From d54c9805b319117e54034a3f4159e868dbdc1ddb Mon Sep 17 00:00:00 2001 From: Kirill Yurkin Date: Mon, 10 Jun 2024 12:48:14 +0300 Subject: [PATCH] first commit --- content/editor/entities.lua | 15 + content/levels/playertest.txt | 24 + content/shaders/debug_draw.ps | 10 + content/shaders/debug_draw.vs | 15 + content/shaders/lit_generic.ps | 15 + content/shaders/lit_generic.vs | 46 + content/shaders/lit_generic_texcoordgen.ps | 15 + content/shaders/ui_base.ps | 8 + content/shaders/ui_base.vs | 16 + content/shaders/ui_tex.ps | 11 + content/test/ship.blend | Bin 0 -> 993328 bytes content/test/ship.blend1 | Bin 0 -> 1003508 bytes content/test/ship.mtl | 13 + content/test/ship.obj | 478 + content/test/ship_back2.blend | Bin 0 -> 1003508 bytes content/test/ship_backup.blend | Bin 0 -> 956600 bytes content/test/ship_weapon.mtl | 14 + content/test/ship_weapon.obj | 222 + content/test/test.mtl | 13 + content/test/test.obj | 53 + content/test/tetrad — копия.png | Bin 0 -> 1627654 bytes content/test/tetrad.png | Bin 0 -> 1787716 bytes content/test/uvgrid.png | Bin 0 -> 3152067 bytes content/test/water_test.mtl | 12 + content/test/water_test.obj | 1097 + content/textures/env/env_water.png | Bin 0 -> 94151 bytes content/textures/font/font_arial_18.png | Bin 0 -> 8623 bytes content/textures/font/font_system.png | Bin 0 -> 22638 bytes content/textures/system/notex.png | Bin 0 -> 2665 bytes content/textures/ui/ui_btn_newgame.png | Bin 0 -> 7558 bytes content/textures/ui/ui_btn_quit.png | Bin 0 -> 6812 bytes content/textures/ui/ui_button_template.pdn | Bin 0 -> 22843 bytes content/textures/ui/ui_cursor.png | Bin 0 -> 217 bytes content/textures/ui/ui_main_bg.pdn | 1475 + content/textures/ui/ui_main_bg.png | Bin 0 -> 1081228 bytes content/textures/ui/ui_msg_nopoints.png | Bin 0 -> 2453 bytes content/textures/ui/ui_new_unit.png | Bin 0 -> 5200 bytes gen_vs2022.bat | 1 + premake5.lua | 158 + src/engine/filesystem/file.cpp | 57 + src/engine/filesystem/file.h | 28 + src/engine/filesystem/filecommon.h | 21 + src/engine/filesystem/filemanager.cpp | 316 + src/engine/filesystem/filemanager.h | 51 + src/engine/filesystem/stream.cpp | 61 + src/engine/filesystem/stream.h | 21 + src/engine/input/input_system.cpp | 182 + src/engine/input/input_system.h | 198 + src/engine/physics/physics_object.cpp | 158 + src/engine/physics/physics_object.h | 54 + src/engine/physics/physics_render.cpp | 1 + src/engine/physics/physics_render.h | 5 + src/engine/physics/physics_world.cpp | 171 + src/engine/physics/physics_world.h | 46 + src/engine/render/color.h | 52 + src/engine/render/debugrender.cpp | 341 + src/engine/render/debugrender.h | 68 + src/engine/render/font.cpp | 76 + src/engine/render/font.h | 10 + src/engine/render/gl_shared.cpp | 44 + src/engine/render/gl_shared.h | 17 + src/engine/render/indexbuffer.cpp | 56 + src/engine/render/indexbuffer.h | 26 + src/engine/render/modelmanager.cpp | 373 + src/engine/render/modelmanager.h | 75 + src/engine/render/render.cpp | 396 + src/engine/render/render.h | 45 + src/engine/render/render_shared.h | 84 + src/engine/render/renderdevice.cpp | 202 + src/engine/render/renderdevice.h | 78 + src/engine/render/rendertarget.cpp | 63 + src/engine/render/rendertarget.h | 33 + src/engine/render/shader.cpp | 92 + src/engine/render/shader.h | 98 + src/engine/render/shadersystem.cpp | 190 + src/engine/render/shadersystem.h | 61 + src/engine/render/texture2d.cpp | 308 + src/engine/render/texture2d.h | 67 + src/engine/render/texturesmanager.cpp | 194 + src/engine/render/texturesmanager.h | 39 + src/engine/render/ui.cpp | 460 + src/engine/render/ui.h | 29 + src/engine/render/vertexbuffer.cpp | 62 + src/engine/render/vertexbuffer.h | 28 + src/engine/sound/sound_system.cpp | 3 + src/engine/sound/sound_system.h | 53 + src/engine/sound/sound_system_null.cpp | 0 src/engine/sound/soundsys_dsound.cpp | 378 + src/engine/utils/logger.cpp | 105 + src/engine/utils/logger.h | 16 + src/engine/utils/maths.cpp | 61 + src/engine/utils/maths.h | 339 + src/engine/utils/objectmanager.cpp | 183 + src/engine/utils/objectmanager.h | 348 + src/engine/utils/refcount.h | 45 + src/engine/utils/rtti.cpp | 23 + src/engine/utils/rtti.h | 43 + src/engine/utils/string.cpp | 14 + src/engine/utils/string.h | 10 + src/engine/utils/timer.cpp | 65 + src/engine/utils/timer.h | 26 + src/engine/utils/win32_keys.h | 359 + src/game/camera.cpp | 96 + src/game/camera.h | 24 + src/game/game_app.cpp | 378 + src/game/game_app.h | 32 + src/game/game_object.cpp | 72 + src/game/game_object.h | 75 + src/game/game_object_factory.cpp | 34 + src/game/game_object_factory.h | 32 + src/game/level.cpp | 392 + src/game/level.h | 50 + src/game/lua_impl.c | 2 + src/game/main_menu.cpp | 44 + src/game/main_menu.h | 31 + src/game/phys_test.cpp | 0 src/game/phys_test.h | 6 + src/game/player.cpp | 75 + src/game/player.h | 30 + src/game/script_system.cpp | 190 + src/game/script_system.h | 54 + src/main/main.cpp | 45 + thirdparty/GL/glcorearb.h | 5997 + thirdparty/GL/glext.h | 12914 +++ thirdparty/GL/glxext.h | 954 + thirdparty/GL/wglext.h | 845 + thirdparty/KHR/khrplatform.h | 311 + thirdparty/glad/include/KHR/khrplatform.h | 311 + thirdparty/glad/include/glad/glad.h | 3437 + thirdparty/glad/src/glad.c | 1722 + thirdparty/glm/glm/CMakeLists.txt | 69 + thirdparty/glm/glm/common.hpp | 539 + thirdparty/glm/glm/copying.txt | 54 + thirdparty/glm/glm/detail/_features.hpp | 394 + thirdparty/glm/glm/detail/_fixes.hpp | 27 + thirdparty/glm/glm/detail/_noise.hpp | 81 + thirdparty/glm/glm/detail/_swizzle.hpp | 804 + thirdparty/glm/glm/detail/_swizzle_func.hpp | 682 + thirdparty/glm/glm/detail/_vectorize.hpp | 162 + thirdparty/glm/glm/detail/compute_common.hpp | 50 + .../glm/glm/detail/compute_vector_decl.hpp | 190 + .../glm/detail/compute_vector_relational.hpp | 30 + thirdparty/glm/glm/detail/func_common.inl | 792 + .../glm/glm/detail/func_common_simd.inl | 231 + .../glm/glm/detail/func_exponential.inl | 152 + .../glm/glm/detail/func_exponential_simd.inl | 37 + thirdparty/glm/glm/detail/func_geometric.inl | 243 + .../glm/glm/detail/func_geometric_simd.inl | 163 + thirdparty/glm/glm/detail/func_integer.inl | 392 + .../glm/glm/detail/func_integer_simd.inl | 65 + thirdparty/glm/glm/detail/func_matrix.inl | 443 + .../glm/glm/detail/func_matrix_simd.inl | 252 + thirdparty/glm/glm/detail/func_packing.inl | 189 + .../glm/glm/detail/func_packing_simd.inl | 6 + .../glm/glm/detail/func_trigonometric.inl | 197 + .../glm/detail/func_trigonometric_simd.inl | 0 .../glm/glm/detail/func_vector_relational.inl | 87 + .../detail/func_vector_relational_simd.inl | 6 + thirdparty/glm/glm/detail/glm.cpp | 263 + thirdparty/glm/glm/detail/qualifier.hpp | 229 + thirdparty/glm/glm/detail/setup.hpp | 1188 + thirdparty/glm/glm/detail/type_float.hpp | 68 + thirdparty/glm/glm/detail/type_half.hpp | 16 + thirdparty/glm/glm/detail/type_half.inl | 241 + thirdparty/glm/glm/detail/type_mat2x2.hpp | 177 + thirdparty/glm/glm/detail/type_mat2x2.inl | 536 + thirdparty/glm/glm/detail/type_mat2x3.hpp | 159 + thirdparty/glm/glm/detail/type_mat2x3.inl | 510 + thirdparty/glm/glm/detail/type_mat2x4.hpp | 161 + thirdparty/glm/glm/detail/type_mat2x4.inl | 520 + thirdparty/glm/glm/detail/type_mat3x2.hpp | 167 + thirdparty/glm/glm/detail/type_mat3x2.inl | 532 + thirdparty/glm/glm/detail/type_mat3x3.hpp | 184 + thirdparty/glm/glm/detail/type_mat3x3.inl | 601 + thirdparty/glm/glm/detail/type_mat3x4.hpp | 166 + thirdparty/glm/glm/detail/type_mat3x4.inl | 578 + thirdparty/glm/glm/detail/type_mat4x2.hpp | 171 + thirdparty/glm/glm/detail/type_mat4x2.inl | 574 + thirdparty/glm/glm/detail/type_mat4x3.hpp | 171 + thirdparty/glm/glm/detail/type_mat4x3.inl | 598 + thirdparty/glm/glm/detail/type_mat4x4.hpp | 189 + thirdparty/glm/glm/detail/type_mat4x4.inl | 706 + .../glm/glm/detail/type_mat4x4_simd.inl | 6 + thirdparty/glm/glm/detail/type_quat.hpp | 193 + thirdparty/glm/glm/detail/type_quat.inl | 424 + thirdparty/glm/glm/detail/type_quat_simd.inl | 208 + thirdparty/glm/glm/detail/type_vec1.hpp | 308 + thirdparty/glm/glm/detail/type_vec1.inl | 553 + thirdparty/glm/glm/detail/type_vec2.hpp | 402 + thirdparty/glm/glm/detail/type_vec2.inl | 915 + thirdparty/glm/glm/detail/type_vec3.hpp | 436 + thirdparty/glm/glm/detail/type_vec3.inl | 1070 + thirdparty/glm/glm/detail/type_vec4.hpp | 508 + thirdparty/glm/glm/detail/type_vec4.inl | 1142 + thirdparty/glm/glm/detail/type_vec4_simd.inl | 788 + thirdparty/glm/glm/exponential.hpp | 110 + thirdparty/glm/glm/ext.hpp | 267 + thirdparty/glm/glm/ext/_matrix_vectorize.hpp | 128 + thirdparty/glm/glm/ext/matrix_clip_space.hpp | 522 + thirdparty/glm/glm/ext/matrix_clip_space.inl | 595 + thirdparty/glm/glm/ext/matrix_common.hpp | 39 + thirdparty/glm/glm/ext/matrix_common.inl | 34 + thirdparty/glm/glm/ext/matrix_double2x2.hpp | 23 + .../glm/ext/matrix_double2x2_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_double2x3.hpp | 18 + .../glm/ext/matrix_double2x3_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double2x4.hpp | 18 + .../glm/ext/matrix_double2x4_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double3x2.hpp | 18 + .../glm/ext/matrix_double3x2_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double3x3.hpp | 23 + .../glm/ext/matrix_double3x3_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_double3x4.hpp | 18 + .../glm/ext/matrix_double3x4_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double4x2.hpp | 18 + .../glm/ext/matrix_double4x2_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double4x3.hpp | 18 + .../glm/ext/matrix_double4x3_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_double4x4.hpp | 23 + .../glm/ext/matrix_double4x4_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_float2x2.hpp | 23 + .../glm/glm/ext/matrix_float2x2_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_float2x3.hpp | 18 + .../glm/glm/ext/matrix_float2x3_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float2x4.hpp | 18 + .../glm/glm/ext/matrix_float2x4_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float3x2.hpp | 18 + .../glm/glm/ext/matrix_float3x2_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float3x3.hpp | 23 + .../glm/glm/ext/matrix_float3x3_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_float3x4.hpp | 18 + .../glm/glm/ext/matrix_float3x4_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float4x2.hpp | 18 + .../glm/glm/ext/matrix_float4x2_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float4x3.hpp | 18 + .../glm/glm/ext/matrix_float4x3_precision.hpp | 31 + thirdparty/glm/glm/ext/matrix_float4x4.hpp | 23 + .../glm/glm/ext/matrix_float4x4_precision.hpp | 49 + thirdparty/glm/glm/ext/matrix_int2x2.hpp | 38 + .../glm/glm/ext/matrix_int2x2_sized.hpp | 70 + thirdparty/glm/glm/ext/matrix_int2x3.hpp | 33 + .../glm/glm/ext/matrix_int2x3_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int2x4.hpp | 33 + .../glm/glm/ext/matrix_int2x4_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int3x2.hpp | 33 + .../glm/glm/ext/matrix_int3x2_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int3x3.hpp | 38 + .../glm/glm/ext/matrix_int3x3_sized.hpp | 70 + thirdparty/glm/glm/ext/matrix_int3x4.hpp | 33 + .../glm/glm/ext/matrix_int3x4_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int4x2.hpp | 33 + .../glm/glm/ext/matrix_int4x2_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int4x3.hpp | 33 + .../glm/glm/ext/matrix_int4x3_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_int4x4.hpp | 38 + .../glm/glm/ext/matrix_int4x4_sized.hpp | 70 + thirdparty/glm/glm/ext/matrix_integer.hpp | 91 + thirdparty/glm/glm/ext/matrix_integer.inl | 38 + thirdparty/glm/glm/ext/matrix_projection.hpp | 149 + thirdparty/glm/glm/ext/matrix_projection.inl | 106 + thirdparty/glm/glm/ext/matrix_relational.hpp | 132 + thirdparty/glm/glm/ext/matrix_relational.inl | 88 + thirdparty/glm/glm/ext/matrix_transform.hpp | 171 + thirdparty/glm/glm/ext/matrix_transform.inl | 207 + thirdparty/glm/glm/ext/matrix_uint2x2.hpp | 38 + .../glm/glm/ext/matrix_uint2x2_sized.hpp | 70 + thirdparty/glm/glm/ext/matrix_uint2x3.hpp | 33 + .../glm/glm/ext/matrix_uint2x3_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint2x4.hpp | 33 + .../glm/glm/ext/matrix_uint2x4_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint3x2.hpp | 33 + .../glm/glm/ext/matrix_uint3x2_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint3x3.hpp | 38 + .../glm/glm/ext/matrix_uint3x3_sized.hpp | 70 + thirdparty/glm/glm/ext/matrix_uint3x4.hpp | 33 + .../glm/glm/ext/matrix_uint3x4_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint4x2.hpp | 33 + .../glm/glm/ext/matrix_uint4x2_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint4x3.hpp | 33 + .../glm/glm/ext/matrix_uint4x3_sized.hpp | 49 + thirdparty/glm/glm/ext/matrix_uint4x4.hpp | 38 + .../glm/glm/ext/matrix_uint4x4_sized.hpp | 70 + thirdparty/glm/glm/ext/quaternion_common.hpp | 135 + thirdparty/glm/glm/ext/quaternion_common.inl | 144 + .../glm/glm/ext/quaternion_common_simd.inl | 18 + thirdparty/glm/glm/ext/quaternion_double.hpp | 39 + .../glm/ext/quaternion_double_precision.hpp | 42 + .../glm/glm/ext/quaternion_exponential.hpp | 63 + .../glm/glm/ext/quaternion_exponential.inl | 89 + thirdparty/glm/glm/ext/quaternion_float.hpp | 39 + .../glm/ext/quaternion_float_precision.hpp | 36 + .../glm/glm/ext/quaternion_geometric.hpp | 70 + .../glm/glm/ext/quaternion_geometric.inl | 36 + .../glm/glm/ext/quaternion_relational.hpp | 62 + .../glm/glm/ext/quaternion_relational.inl | 35 + .../glm/glm/ext/quaternion_transform.hpp | 47 + .../glm/glm/ext/quaternion_transform.inl | 24 + .../glm/glm/ext/quaternion_trigonometric.hpp | 65 + .../glm/glm/ext/quaternion_trigonometric.inl | 37 + thirdparty/glm/glm/ext/scalar_common.hpp | 181 + thirdparty/glm/glm/ext/scalar_common.inl | 170 + thirdparty/glm/glm/ext/scalar_constants.hpp | 40 + thirdparty/glm/glm/ext/scalar_constants.inl | 24 + thirdparty/glm/glm/ext/scalar_int_sized.hpp | 70 + thirdparty/glm/glm/ext/scalar_integer.hpp | 92 + thirdparty/glm/glm/ext/scalar_integer.inl | 243 + thirdparty/glm/glm/ext/scalar_packing.hpp | 32 + thirdparty/glm/glm/ext/scalar_packing.inl | 0 thirdparty/glm/glm/ext/scalar_reciprocal.hpp | 135 + thirdparty/glm/glm/ext/scalar_reciprocal.inl | 107 + thirdparty/glm/glm/ext/scalar_relational.hpp | 68 + thirdparty/glm/glm/ext/scalar_relational.inl | 40 + thirdparty/glm/glm/ext/scalar_uint_sized.hpp | 70 + thirdparty/glm/glm/ext/scalar_ulp.hpp | 77 + thirdparty/glm/glm/ext/scalar_ulp.inl | 291 + thirdparty/glm/glm/ext/vector_bool1.hpp | 30 + .../glm/glm/ext/vector_bool1_precision.hpp | 34 + thirdparty/glm/glm/ext/vector_bool2.hpp | 18 + .../glm/glm/ext/vector_bool2_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_bool3.hpp | 18 + .../glm/glm/ext/vector_bool3_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_bool4.hpp | 18 + .../glm/glm/ext/vector_bool4_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_common.hpp | 228 + thirdparty/glm/glm/ext/vector_common.inl | 147 + thirdparty/glm/glm/ext/vector_double1.hpp | 31 + .../glm/glm/ext/vector_double1_precision.hpp | 36 + thirdparty/glm/glm/ext/vector_double2.hpp | 18 + .../glm/glm/ext/vector_double2_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_double3.hpp | 18 + .../glm/glm/ext/vector_double3_precision.hpp | 34 + thirdparty/glm/glm/ext/vector_double4.hpp | 18 + .../glm/glm/ext/vector_double4_precision.hpp | 35 + thirdparty/glm/glm/ext/vector_float1.hpp | 31 + .../glm/glm/ext/vector_float1_precision.hpp | 36 + thirdparty/glm/glm/ext/vector_float2.hpp | 18 + .../glm/glm/ext/vector_float2_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_float3.hpp | 18 + .../glm/glm/ext/vector_float3_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_float4.hpp | 18 + .../glm/glm/ext/vector_float4_precision.hpp | 31 + thirdparty/glm/glm/ext/vector_int1.hpp | 32 + thirdparty/glm/glm/ext/vector_int1_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_int2.hpp | 18 + thirdparty/glm/glm/ext/vector_int2_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_int3.hpp | 18 + thirdparty/glm/glm/ext/vector_int3_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_int4.hpp | 18 + thirdparty/glm/glm/ext/vector_int4_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_integer.hpp | 149 + thirdparty/glm/glm/ext/vector_integer.inl | 85 + thirdparty/glm/glm/ext/vector_packing.hpp | 32 + thirdparty/glm/glm/ext/vector_packing.inl | 0 thirdparty/glm/glm/ext/vector_reciprocal.hpp | 135 + thirdparty/glm/glm/ext/vector_reciprocal.inl | 105 + thirdparty/glm/glm/ext/vector_relational.hpp | 107 + thirdparty/glm/glm/ext/vector_relational.inl | 75 + thirdparty/glm/glm/ext/vector_uint1.hpp | 32 + thirdparty/glm/glm/ext/vector_uint1_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_uint2.hpp | 18 + thirdparty/glm/glm/ext/vector_uint2_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_uint3.hpp | 18 + thirdparty/glm/glm/ext/vector_uint3_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_uint4.hpp | 18 + thirdparty/glm/glm/ext/vector_uint4_sized.hpp | 49 + thirdparty/glm/glm/ext/vector_ulp.hpp | 112 + thirdparty/glm/glm/ext/vector_ulp.inl | 74 + thirdparty/glm/glm/fwd.hpp | 1233 + thirdparty/glm/glm/geometric.hpp | 116 + thirdparty/glm/glm/glm.cppm | 2675 + thirdparty/glm/glm/glm.hpp | 137 + thirdparty/glm/glm/gtc/bitfield.hpp | 266 + thirdparty/glm/glm/gtc/bitfield.inl | 635 + thirdparty/glm/glm/gtc/color_space.hpp | 56 + thirdparty/glm/glm/gtc/color_space.inl | 84 + thirdparty/glm/glm/gtc/constants.hpp | 170 + thirdparty/glm/glm/gtc/constants.inl | 173 + thirdparty/glm/glm/gtc/epsilon.hpp | 60 + thirdparty/glm/glm/gtc/epsilon.inl | 80 + thirdparty/glm/glm/gtc/integer.hpp | 43 + thirdparty/glm/glm/gtc/integer.inl | 33 + thirdparty/glm/glm/gtc/matrix_access.hpp | 60 + thirdparty/glm/glm/gtc/matrix_access.inl | 62 + thirdparty/glm/glm/gtc/matrix_integer.hpp | 433 + thirdparty/glm/glm/gtc/matrix_inverse.hpp | 50 + thirdparty/glm/glm/gtc/matrix_inverse.inl | 118 + thirdparty/glm/glm/gtc/matrix_transform.hpp | 36 + thirdparty/glm/glm/gtc/matrix_transform.inl | 3 + thirdparty/glm/glm/gtc/noise.hpp | 61 + thirdparty/glm/glm/gtc/noise.inl | 807 + thirdparty/glm/glm/gtc/packing.hpp | 728 + thirdparty/glm/glm/gtc/packing.inl | 951 + thirdparty/glm/glm/gtc/quaternion.hpp | 173 + thirdparty/glm/glm/gtc/quaternion.inl | 208 + thirdparty/glm/glm/gtc/quaternion_simd.inl | 0 thirdparty/glm/glm/gtc/random.hpp | 82 + thirdparty/glm/glm/gtc/random.inl | 303 + thirdparty/glm/glm/gtc/reciprocal.hpp | 24 + thirdparty/glm/glm/gtc/round.hpp | 160 + thirdparty/glm/glm/gtc/round.inl | 155 + thirdparty/glm/glm/gtc/type_aligned.hpp | 1315 + thirdparty/glm/glm/gtc/type_precision.hpp | 2094 + thirdparty/glm/glm/gtc/type_precision.inl | 6 + thirdparty/glm/glm/gtc/type_ptr.hpp | 230 + thirdparty/glm/glm/gtc/type_ptr.inl | 386 + thirdparty/glm/glm/gtc/ulp.hpp | 155 + thirdparty/glm/glm/gtc/ulp.inl | 173 + thirdparty/glm/glm/gtc/vec1.hpp | 30 + thirdparty/glm/glm/gtx/associated_min_max.hpp | 205 + thirdparty/glm/glm/gtx/associated_min_max.inl | 354 + thirdparty/glm/glm/gtx/bit.hpp | 96 + thirdparty/glm/glm/gtx/bit.inl | 92 + thirdparty/glm/glm/gtx/closest_point.hpp | 47 + thirdparty/glm/glm/gtx/closest_point.inl | 45 + thirdparty/glm/glm/gtx/color_encoding.hpp | 52 + thirdparty/glm/glm/gtx/color_encoding.inl | 45 + thirdparty/glm/glm/gtx/color_space.hpp | 70 + thirdparty/glm/glm/gtx/color_space.inl | 144 + thirdparty/glm/glm/gtx/color_space_YCoCg.hpp | 58 + thirdparty/glm/glm/gtx/color_space_YCoCg.inl | 107 + thirdparty/glm/glm/gtx/common.hpp | 74 + thirdparty/glm/glm/gtx/common.inl | 125 + thirdparty/glm/glm/gtx/compatibility.hpp | 131 + thirdparty/glm/glm/gtx/compatibility.inl | 62 + thirdparty/glm/glm/gtx/component_wise.hpp | 77 + thirdparty/glm/glm/gtx/component_wise.inl | 147 + thirdparty/glm/glm/gtx/dual_quaternion.hpp | 272 + thirdparty/glm/glm/gtx/dual_quaternion.inl | 352 + thirdparty/glm/glm/gtx/easing.hpp | 217 + thirdparty/glm/glm/gtx/easing.inl | 436 + thirdparty/glm/glm/gtx/euler_angles.hpp | 333 + thirdparty/glm/glm/gtx/euler_angles.inl | 899 + thirdparty/glm/glm/gtx/extend.hpp | 40 + thirdparty/glm/glm/gtx/extend.inl | 48 + thirdparty/glm/glm/gtx/extended_min_max.hpp | 135 + thirdparty/glm/glm/gtx/extended_min_max.inl | 138 + thirdparty/glm/glm/gtx/exterior_product.hpp | 43 + thirdparty/glm/glm/gtx/exterior_product.inl | 26 + thirdparty/glm/glm/gtx/fast_exponential.hpp | 93 + thirdparty/glm/glm/gtx/fast_exponential.inl | 136 + thirdparty/glm/glm/gtx/fast_square_root.hpp | 96 + thirdparty/glm/glm/gtx/fast_square_root.inl | 75 + thirdparty/glm/glm/gtx/fast_trigonometry.hpp | 77 + thirdparty/glm/glm/gtx/fast_trigonometry.inl | 142 + thirdparty/glm/glm/gtx/float_notmalize.inl | 13 + thirdparty/glm/glm/gtx/functions.hpp | 54 + thirdparty/glm/glm/gtx/functions.inl | 30 + thirdparty/glm/glm/gtx/gradient_paint.hpp | 51 + thirdparty/glm/glm/gtx/gradient_paint.inl | 36 + .../glm/glm/gtx/handed_coordinate_space.hpp | 48 + .../glm/glm/gtx/handed_coordinate_space.inl | 26 + thirdparty/glm/glm/gtx/hash.hpp | 146 + thirdparty/glm/glm/gtx/hash.inl | 175 + thirdparty/glm/glm/gtx/integer.hpp | 74 + thirdparty/glm/glm/gtx/integer.inl | 185 + thirdparty/glm/glm/gtx/intersect.hpp | 90 + thirdparty/glm/glm/gtx/intersect.inl | 200 + thirdparty/glm/glm/gtx/io.hpp | 210 + thirdparty/glm/glm/gtx/io.inl | 452 + thirdparty/glm/glm/gtx/log_base.hpp | 46 + thirdparty/glm/glm/gtx/log_base.inl | 16 + .../glm/glm/gtx/matrix_cross_product.hpp | 45 + .../glm/glm/gtx/matrix_cross_product.inl | 37 + thirdparty/glm/glm/gtx/matrix_decompose.hpp | 50 + thirdparty/glm/glm/gtx/matrix_decompose.inl | 234 + .../glm/glm/gtx/matrix_factorisation.hpp | 67 + .../glm/glm/gtx/matrix_factorisation.inl | 84 + .../glm/glm/gtx/matrix_interpolation.hpp | 58 + .../glm/glm/gtx/matrix_interpolation.inl | 146 + .../glm/glm/gtx/matrix_major_storage.hpp | 117 + .../glm/glm/gtx/matrix_major_storage.inl | 166 + thirdparty/glm/glm/gtx/matrix_operation.hpp | 101 + thirdparty/glm/glm/gtx/matrix_operation.inl | 176 + thirdparty/glm/glm/gtx/matrix_query.hpp | 75 + thirdparty/glm/glm/gtx/matrix_query.inl | 119 + .../glm/glm/gtx/matrix_transform_2d.hpp | 79 + .../glm/glm/gtx/matrix_transform_2d.inl | 68 + thirdparty/glm/glm/gtx/mixed_product.hpp | 39 + thirdparty/glm/glm/gtx/mixed_product.inl | 15 + thirdparty/glm/glm/gtx/norm.hpp | 86 + thirdparty/glm/glm/gtx/norm.inl | 95 + thirdparty/glm/glm/gtx/normal.hpp | 39 + thirdparty/glm/glm/gtx/normal.inl | 15 + thirdparty/glm/glm/gtx/normalize_dot.hpp | 47 + thirdparty/glm/glm/gtx/normalize_dot.inl | 16 + thirdparty/glm/glm/gtx/number_precision.hpp | 44 + thirdparty/glm/glm/gtx/optimum_pow.hpp | 50 + thirdparty/glm/glm/gtx/optimum_pow.inl | 22 + thirdparty/glm/glm/gtx/orthonormalize.hpp | 47 + thirdparty/glm/glm/gtx/orthonormalize.inl | 29 + thirdparty/glm/glm/gtx/pca.hpp | 112 + thirdparty/glm/glm/gtx/pca.inl | 343 + thirdparty/glm/glm/gtx/perpendicular.hpp | 39 + thirdparty/glm/glm/gtx/perpendicular.inl | 10 + thirdparty/glm/glm/gtx/polar_coordinates.hpp | 46 + thirdparty/glm/glm/gtx/polar_coordinates.inl | 36 + thirdparty/glm/glm/gtx/projection.hpp | 41 + thirdparty/glm/glm/gtx/projection.inl | 10 + thirdparty/glm/glm/gtx/quaternion.hpp | 172 + thirdparty/glm/glm/gtx/quaternion.inl | 159 + thirdparty/glm/glm/gtx/range.hpp | 96 + thirdparty/glm/glm/gtx/raw_data.hpp | 49 + thirdparty/glm/glm/gtx/raw_data.inl | 2 + .../glm/glm/gtx/rotate_normalized_axis.hpp | 66 + .../glm/glm/gtx/rotate_normalized_axis.inl | 58 + thirdparty/glm/glm/gtx/rotate_vector.hpp | 121 + thirdparty/glm/glm/gtx/rotate_vector.inl | 187 + .../glm/glm/gtx/scalar_multiplication.hpp | 80 + thirdparty/glm/glm/gtx/scalar_relational.hpp | 34 + thirdparty/glm/glm/gtx/scalar_relational.inl | 88 + thirdparty/glm/glm/gtx/spline.hpp | 63 + thirdparty/glm/glm/gtx/spline.inl | 60 + thirdparty/glm/glm/gtx/std_based_type.hpp | 66 + thirdparty/glm/glm/gtx/std_based_type.inl | 6 + thirdparty/glm/glm/gtx/string_cast.hpp | 45 + thirdparty/glm/glm/gtx/string_cast.inl | 497 + thirdparty/glm/glm/gtx/texture.hpp | 44 + thirdparty/glm/glm/gtx/texture.inl | 17 + thirdparty/glm/glm/gtx/transform.hpp | 58 + thirdparty/glm/glm/gtx/transform.inl | 23 + thirdparty/glm/glm/gtx/transform2.hpp | 87 + thirdparty/glm/glm/gtx/transform2.inl | 125 + thirdparty/glm/glm/gtx/type_aligned.hpp | 980 + thirdparty/glm/glm/gtx/type_aligned.inl | 6 + thirdparty/glm/glm/gtx/type_trait.hpp | 83 + thirdparty/glm/glm/gtx/type_trait.inl | 61 + thirdparty/glm/glm/gtx/vec_swizzle.hpp | 2784 + thirdparty/glm/glm/gtx/vector_angle.hpp | 55 + thirdparty/glm/glm/gtx/vector_angle.inl | 45 + thirdparty/glm/glm/gtx/vector_query.hpp | 64 + thirdparty/glm/glm/gtx/vector_query.inl | 154 + thirdparty/glm/glm/gtx/wrap.hpp | 35 + thirdparty/glm/glm/gtx/wrap.inl | 6 + thirdparty/glm/glm/integer.hpp | 212 + thirdparty/glm/glm/mat2x2.hpp | 9 + thirdparty/glm/glm/mat2x3.hpp | 9 + thirdparty/glm/glm/mat2x4.hpp | 9 + thirdparty/glm/glm/mat3x2.hpp | 9 + thirdparty/glm/glm/mat3x3.hpp | 8 + thirdparty/glm/glm/mat3x4.hpp | 8 + thirdparty/glm/glm/mat4x2.hpp | 9 + thirdparty/glm/glm/mat4x3.hpp | 8 + thirdparty/glm/glm/mat4x4.hpp | 9 + thirdparty/glm/glm/matrix.hpp | 161 + thirdparty/glm/glm/packing.hpp | 173 + thirdparty/glm/glm/simd/common.h | 240 + thirdparty/glm/glm/simd/exponential.h | 20 + thirdparty/glm/glm/simd/geometric.h | 130 + thirdparty/glm/glm/simd/integer.h | 115 + thirdparty/glm/glm/simd/matrix.h | 1028 + thirdparty/glm/glm/simd/neon.h | 155 + thirdparty/glm/glm/simd/packing.h | 8 + thirdparty/glm/glm/simd/platform.h | 469 + thirdparty/glm/glm/simd/trigonometric.h | 9 + thirdparty/glm/glm/simd/vector_relational.h | 8 + thirdparty/glm/glm/trigonometric.hpp | 210 + thirdparty/glm/glm/vec2.hpp | 14 + thirdparty/glm/glm/vec3.hpp | 14 + thirdparty/glm/glm/vec4.hpp | 15 + thirdparty/glm/glm/vector_relational.hpp | 121 + thirdparty/miniaudio/.gitignore | 14 + thirdparty/miniaudio/.gitmodules | 0 thirdparty/miniaudio/CHANGES.md | 1110 + thirdparty/miniaudio/CONTRIBUTING.md | 88 + thirdparty/miniaudio/LICENSE | 47 + thirdparty/miniaudio/README.md | 222 + thirdparty/miniaudio/examples/build/README.md | 23 + .../miniaudio/examples/custom_backend.c | 740 + .../miniaudio/examples/custom_decoder.c | 270 + .../examples/custom_decoder_engine.c | 230 + .../miniaudio/examples/data_source_chaining.c | 159 + thirdparty/miniaudio/examples/duplex_effect.c | 148 + .../miniaudio/examples/engine_advanced.c | 251 + .../miniaudio/examples/engine_effects.c | 104 + .../miniaudio/examples/engine_hello_world.c | 35 + thirdparty/miniaudio/examples/engine_sdl.c | 134 + .../miniaudio/examples/engine_steamaudio.c | 432 + thirdparty/miniaudio/examples/hilo_interop.c | 148 + thirdparty/miniaudio/examples/node_graph.c | 250 + .../miniaudio/examples/resource_manager.c | 151 + .../examples/resource_manager_advanced.c | 329 + .../miniaudio/examples/simple_capture.c | 74 + thirdparty/miniaudio/examples/simple_duplex.c | 71 + .../miniaudio/examples/simple_enumeration.c | 54 + .../miniaudio/examples/simple_loopback.c | 82 + .../miniaudio/examples/simple_looping.c | 76 + thirdparty/miniaudio/examples/simple_mixing.c | 199 + .../miniaudio/examples/simple_playback.c | 75 + .../miniaudio/examples/simple_playback_sine.c | 91 + .../miniaudio/extras/miniaudio_libopus.h | 496 + .../miniaudio/extras/miniaudio_libvorbis.h | 516 + .../extras/miniaudio_split/README.md | 7 + .../extras/miniaudio_split/miniaudio.c | 81195 ++++++++++++++ .../extras/miniaudio_split/miniaudio.h | 7766 ++ .../ma_channel_combiner_node.c | 77 + .../ma_channel_combiner_node.h | 30 + .../ma_channel_combiner_node_example.c | 2 + .../ma_channel_separator_node.c | 81 + .../ma_channel_separator_node.h | 29 + .../ma_channel_separator_node_example.c | 149 + .../ma_delay_node/ma_delay_node_example.c | 116 + .../nodes/ma_ltrim_node/ma_ltrim_node.c | 102 + .../nodes/ma_ltrim_node/ma_ltrim_node.h | 35 + .../ma_ltrim_node/ma_ltrim_node_example.c | 115 + .../nodes/ma_reverb_node/ma_reverb_node.c | 78 + .../nodes/ma_reverb_node/ma_reverb_node.h | 42 + .../ma_reverb_node/ma_reverb_node_example.c | 118 + .../extras/nodes/ma_reverb_node/verblib.h | 743 + .../nodes/ma_vocoder_node/ma_vocoder_node.c | 80 + .../nodes/ma_vocoder_node/ma_vocoder_node.h | 45 + .../ma_vocoder_node/ma_vocoder_node_example.c | 148 + .../extras/nodes/ma_vocoder_node/voclib.h | 672 + thirdparty/miniaudio/extras/osaudio/README.md | 49 + thirdparty/miniaudio/extras/osaudio/osaudio.h | 604 + .../extras/osaudio/osaudio_miniaudio.c | 948 + .../extras/osaudio/tests/osaudio_deviceio.c | 196 + thirdparty/miniaudio/extras/stb_vorbis.c | 5584 + thirdparty/miniaudio/miniaudio.h | 92621 ++++++++++++++++ thirdparty/miniaudio/research/README.txt | 3 + .../resources/branding/icon-128x128.png | Bin 0 -> 1189 bytes .../resources/branding/icon-256x256.png | Bin 0 -> 2284 bytes .../resources/branding/icon-64x64.png | Bin 0 -> 677 bytes .../resources/branding/miniaudio_logo1.png | Bin 0 -> 183 bytes .../resources/branding/miniaudio_logo_400.png | Bin 0 -> 3183 bytes thirdparty/miniaudio/tests/_build/README.md | 23 + .../benchmarks/pcm_f32_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_f32_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_f32_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_f32_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_f32_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s16_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s16_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s16_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s16_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s16_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s24_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s24_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s24_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s24_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s24_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s32_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s32_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s32_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s32_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s32_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_u8_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_u8_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_u8_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_u8_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_u8_to_u8__mono_8000.raw | 1 + .../tests/_build/res/sine_s16_mono_48000.wav | Bin 0 -> 96044 bytes .../tests/test_automated/ma_test_automated.c | 34 + .../ma_test_automated_data_converter.c | 379 + .../tests/test_common/ma_test_common.c | 39 + .../miniaudio/tests/test_cpp/ma_test_cpp.cpp | 1 + .../tests/test_deviceio/ma_test_deviceio.c | 632 + .../test_emscripten/ma_test_emscripten.c | 138 + .../test_emscripten/ma_test_emscripten.html | 144 + .../tests/test_filtering/ma_test_filtering.c | 99 + .../test_filtering/ma_test_filtering_bpf.c | 170 + .../ma_test_filtering_dithering.c | 74 + .../ma_test_filtering_hishelf.c | 170 + .../test_filtering/ma_test_filtering_hpf.c | 242 + .../ma_test_filtering_loshelf.c | 170 + .../test_filtering/ma_test_filtering_lpf.c | 242 + .../test_filtering/ma_test_filtering_notch.c | 170 + .../test_filtering/ma_test_filtering_peak.c | 170 + .../test_generation/ma_test_generation.c | 41 + .../ma_test_generation_noise.c | 145 + .../ma_test_generation_waveform.c | 240 + .../tools/audioconverter/audioconverter.c | 305 + .../website/.webplate/config.webplate | 5 + .../website/.webplate/miniaudio-footer.html | 16 + .../website/.webplate/miniaudio-header.html | 48 + thirdparty/miniaudio/website/CNAME | 1 + thirdparty/miniaudio/website/css/main.css | 195 + .../website/img/Discord-Logo-White.svg | 1 + thirdparty/miniaudio/website/img/favicon.png | Bin 0 -> 1189 bytes .../miniaudio/website/img/github_white.png | Bin 0 -> 1571 bytes .../miniaudio/website/img/logo1_large.png | Bin 0 -> 1198 bytes .../website/img/logo1_large_white.png | Bin 0 -> 1193 bytes .../miniaudio/website/img/miniaudio_wide.png | Bin 0 -> 8642 bytes .../website/img/platforms/android/android.svg | 1 + .../img/platforms/apple/apple-black.svg | 11 + .../website/img/platforms/bsd/freebsd.svg | 8 + .../website/img/platforms/linux/linux.svg | 405 + .../website/img/platforms/web/html5.svg | 14 + .../img/platforms/windows/windows-10.svg | 53 + .../miniaudio/website/img/twitter_white.png | Bin 0 -> 2292 bytes thirdparty/miniaudio/website/index.html | 251 + thirdparty/minilua/.gitignore | 4 + thirdparty/minilua/LICENSE.txt | 22 + thirdparty/minilua/README.md | 61 + thirdparty/minilua/gen.sh | 172 + thirdparty/minilua/minilua.h | 29240 +++++ thirdparty/ode-0.16.5/CHANGELOG.txt | 1311 + thirdparty/ode-0.16.5/CMakeLists.txt | 982 + thirdparty/ode-0.16.5/COPYING | 32 + thirdparty/ode-0.16.5/CSR.txt | 422 + .../GIMPACT/GIMPACT-LICENSE-BSD.TXT | 29 + .../GIMPACT/GIMPACT-LICENSE-LGPL.TXT | 502 + thirdparty/ode-0.16.5/GIMPACT/Makefile.am | 4 + thirdparty/ode-0.16.5/GIMPACT/Makefile.in | 641 + .../GIMPACT/include/GIMPACT/Makefile.am | 6 + .../GIMPACT/include/GIMPACT/Makefile.in | 533 + .../GIMPACT/include/GIMPACT/gim_boxpruning.h | 323 + .../GIMPACT/include/GIMPACT/gim_contact.h | 115 + .../GIMPACT/include/GIMPACT/gim_geometry.h | 1885 + .../GIMPACT/include/GIMPACT/gim_math.h | 164 + .../GIMPACT/include/GIMPACT/gim_memory.h | 1056 + .../GIMPACT/include/GIMPACT/gim_radixsort.h | 258 + .../GIMPACT/gim_tri_capsule_collision.h | 111 + .../include/GIMPACT/gim_tri_collision.h | 253 + .../GIMPACT/gim_tri_sphere_collision.h | 51 + .../GIMPACT/include/GIMPACT/gim_trimesh.h | 544 + .../GIMPACT/include/GIMPACT/gimpact.h | 45 + .../ode-0.16.5/GIMPACT/include/Makefile.am | 1 + .../ode-0.16.5/GIMPACT/include/Makefile.in | 640 + thirdparty/ode-0.16.5/GIMPACT/src/Makefile.am | 19 + thirdparty/ode-0.16.5/GIMPACT/src/Makefile.in | 638 + .../ode-0.16.5/GIMPACT/src/gim_boxpruning.cpp | 519 + .../ode-0.16.5/GIMPACT/src/gim_contact.cpp | 132 + .../ode-0.16.5/GIMPACT/src/gim_math.cpp | 60 + .../ode-0.16.5/GIMPACT/src/gim_memory.cpp | 878 + .../GIMPACT/src/gim_tri_tri_overlap.cpp | 251 + .../ode-0.16.5/GIMPACT/src/gim_trimesh.cpp | 391 + .../src/gim_trimesh_capsule_collision.cpp | 285 + .../GIMPACT/src/gim_trimesh_ray_collision.cpp | 149 + .../src/gim_trimesh_sphere_collision.cpp | 196 + .../src/gim_trimesh_trimesh_collision.cpp | 348 + thirdparty/ode-0.16.5/GIMPACT/src/gimpact.cpp | 40 + thirdparty/ode-0.16.5/INSTALL.txt | 173 + thirdparty/ode-0.16.5/LICENSE-BSD.TXT | 34 + thirdparty/ode-0.16.5/LICENSE.TXT | 502 + thirdparty/ode-0.16.5/Makefile.am | 48 + thirdparty/ode-0.16.5/Makefile.in | 941 + thirdparty/ode-0.16.5/OPCODE/COPYING | 30 + thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.cpp | 405 + thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.h | 514 + thirdparty/ode-0.16.5/OPCODE/Ice/IceAxes.h | 54 + .../ode-0.16.5/OPCODE/Ice/IceBoundingSphere.h | 142 + .../ode-0.16.5/OPCODE/Ice/IceContainer.cpp | 357 + .../ode-0.16.5/OPCODE/Ice/IceContainer.h | 243 + thirdparty/ode-0.16.5/OPCODE/Ice/IceFPU.h | 282 + .../ode-0.16.5/OPCODE/Ice/IceHPoint.cpp | 70 + thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.h | 160 + .../OPCODE/Ice/IceIndexedTriangle.cpp | 548 + .../OPCODE/Ice/IceIndexedTriangle.h | 76 + thirdparty/ode-0.16.5/OPCODE/Ice/IceLSS.h | 75 + .../ode-0.16.5/OPCODE/Ice/IceMatrix3x3.cpp | 48 + .../ode-0.16.5/OPCODE/Ice/IceMatrix3x3.h | 499 + .../ode-0.16.5/OPCODE/Ice/IceMatrix4x4.cpp | 135 + .../ode-0.16.5/OPCODE/Ice/IceMatrix4x4.h | 457 + .../ode-0.16.5/OPCODE/Ice/IceMemoryMacros.h | 86 + thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.cpp | 324 + thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.h | 177 + thirdparty/ode-0.16.5/OPCODE/Ice/IcePairs.h | 45 + thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.cpp | 45 + thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.h | 113 + thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.cpp | 191 + thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.h | 530 + .../ode-0.16.5/OPCODE/Ice/IcePreprocessor.h | 132 + .../ode-0.16.5/OPCODE/Ice/IceRandom.cpp | 35 + thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.h | 42 + thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.cpp | 84 + thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.h | 98 + .../OPCODE/Ice/IceRevisitedRadix.cpp | 520 + .../ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.h | 65 + .../ode-0.16.5/OPCODE/Ice/IceSegment.cpp | 57 + thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.h | 55 + thirdparty/ode-0.16.5/OPCODE/Ice/IceTriList.h | 61 + .../ode-0.16.5/OPCODE/Ice/IceTriangle.cpp | 286 + .../ode-0.16.5/OPCODE/Ice/IceTriangle.h | 68 + thirdparty/ode-0.16.5/OPCODE/Ice/IceTypes.h | 161 + thirdparty/ode-0.16.5/OPCODE/Ice/IceUtils.cpp | 39 + thirdparty/ode-0.16.5/OPCODE/Ice/IceUtils.h | 259 + thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.am | 20 + thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.in | 660 + thirdparty/ode-0.16.5/OPCODE/Makefile.am | 45 + thirdparty/ode-0.16.5/OPCODE/Makefile.in | 807 + .../ode-0.16.5/OPCODE/OPC_AABBCollider.cpp | 696 + .../ode-0.16.5/OPCODE/OPC_AABBCollider.h | 97 + thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.cpp | 568 + thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.h | 137 + .../ode-0.16.5/OPCODE/OPC_BaseModel.cpp | 119 + thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.h | 199 + .../ode-0.16.5/OPCODE/OPC_BoxBoxOverlap.h | 122 + thirdparty/ode-0.16.5/OPCODE/OPC_Collider.cpp | 54 + thirdparty/ode-0.16.5/OPCODE/OPC_Collider.h | 176 + thirdparty/ode-0.16.5/OPCODE/OPC_Common.cpp | 48 + thirdparty/ode-0.16.5/OPCODE/OPC_Common.h | 101 + .../ode-0.16.5/OPCODE/OPC_HybridModel.cpp | 467 + .../ode-0.16.5/OPCODE/OPC_HybridModel.h | 106 + thirdparty/ode-0.16.5/OPCODE/OPC_IceHook.h | 80 + .../ode-0.16.5/OPCODE/OPC_LSSAABBOverlap.h | 525 + .../ode-0.16.5/OPCODE/OPC_LSSCollider.cpp | 725 + .../ode-0.16.5/OPCODE/OPC_LSSCollider.h | 99 + .../ode-0.16.5/OPCODE/OPC_LSSTriOverlap.h | 679 + .../ode-0.16.5/OPCODE/OPC_MeshInterface.cpp | 393 + .../ode-0.16.5/OPCODE/OPC_MeshInterface.h | 280 + thirdparty/ode-0.16.5/OPCODE/OPC_Model.cpp | 222 + thirdparty/ode-0.16.5/OPCODE/OPC_Model.h | 65 + .../ode-0.16.5/OPCODE/OPC_OBBCollider.cpp | 767 + .../ode-0.16.5/OPCODE/OPC_OBBCollider.h | 142 + .../ode-0.16.5/OPCODE/OPC_OptimizedTree.cpp | 795 + .../ode-0.16.5/OPCODE/OPC_OptimizedTree.h | 206 + thirdparty/ode-0.16.5/OPCODE/OPC_Picking.cpp | 183 + thirdparty/ode-0.16.5/OPCODE/OPC_Picking.h | 45 + .../ode-0.16.5/OPCODE/OPC_PlanesAABBOverlap.h | 50 + .../ode-0.16.5/OPCODE/OPC_PlanesCollider.cpp | 653 + .../ode-0.16.5/OPCODE/OPC_PlanesCollider.h | 122 + .../ode-0.16.5/OPCODE/OPC_PlanesTriOverlap.h | 40 + .../ode-0.16.5/OPCODE/OPC_RayAABBOverlap.h | 63 + .../ode-0.16.5/OPCODE/OPC_RayCollider.cpp | 764 + .../ode-0.16.5/OPCODE/OPC_RayCollider.h | 224 + .../ode-0.16.5/OPCODE/OPC_RayTriOverlap.h | 89 + thirdparty/ode-0.16.5/OPCODE/OPC_Settings.h | 49 + .../ode-0.16.5/OPCODE/OPC_SphereAABBOverlap.h | 128 + .../ode-0.16.5/OPCODE/OPC_SphereCollider.cpp | 739 + .../ode-0.16.5/OPCODE/OPC_SphereCollider.h | 96 + .../ode-0.16.5/OPCODE/OPC_SphereTriOverlap.h | 187 + .../ode-0.16.5/OPCODE/OPC_TreeBuilders.cpp | 306 + .../ode-0.16.5/OPCODE/OPC_TreeBuilders.h | 178 + .../ode-0.16.5/OPCODE/OPC_TreeCollider.cpp | 947 + .../ode-0.16.5/OPCODE/OPC_TreeCollider.h | 246 + .../ode-0.16.5/OPCODE/OPC_TriBoxOverlap.h | 339 + .../ode-0.16.5/OPCODE/OPC_TriTriOverlap.h | 299 + .../ode-0.16.5/OPCODE/OPC_VolumeCollider.cpp | 103 + .../ode-0.16.5/OPCODE/OPC_VolumeCollider.h | 138 + thirdparty/ode-0.16.5/OPCODE/Opcode.cpp | 82 + thirdparty/ode-0.16.5/OPCODE/Opcode.h | 126 + thirdparty/ode-0.16.5/OPCODE/README-ODE.txt | 13 + thirdparty/ode-0.16.5/OPCODE/ReadMe.txt | 171 + thirdparty/ode-0.16.5/OPCODE/Stdafx.h | 24 + .../ode-0.16.5/OPCODE/TemporalCoherence.txt | 32 + thirdparty/ode-0.16.5/README.md | 34 + thirdparty/ode-0.16.5/aclocal.m4 | 1158 + .../ode-0.16.5/bindings/python/INSTALL.txt | 64 + .../ode-0.16.5/bindings/python/TODO.txt | 15 + .../bindings/python/demos/tutorial1.py | 49 + .../bindings/python/demos/tutorial2.py | 135 + .../bindings/python/demos/tutorial3.py | 284 + thirdparty/ode-0.16.5/bindings/python/ode.pxd | 503 + thirdparty/ode-0.16.5/bindings/python/ode.pyx | 4506 + .../ode-0.16.5/bindings/python/setup.py | 48 + thirdparty/ode-0.16.5/bootstrap | 28 + thirdparty/ode-0.16.5/build/config-default.h | 113 + thirdparty/ode-0.16.5/build/premake4.exe | Bin 0 -> 261632 bytes thirdparty/ode-0.16.5/build/premake4.lua | 590 + .../ode-0.16.5/cmake/cmake_uninstall.cmake.in | 17 + thirdparty/ode-0.16.5/compile | 347 + thirdparty/ode-0.16.5/config.guess | 1441 + thirdparty/ode-0.16.5/config.h.cmake.in | 109 + thirdparty/ode-0.16.5/config.sub | 1813 + thirdparty/ode-0.16.5/configure | 21594 ++++ thirdparty/ode-0.16.5/configure.ac | 606 + thirdparty/ode-0.16.5/depcomp | 791 + thirdparty/ode-0.16.5/drawstuff/Makefile.am | 4 + thirdparty/ode-0.16.5/drawstuff/Makefile.in | 641 + .../ode-0.16.5/drawstuff/dstest/Makefile.am | 14 + .../ode-0.16.5/drawstuff/dstest/Makefile.in | 614 + .../ode-0.16.5/drawstuff/dstest/dstest.cpp | 125 + .../ode-0.16.5/drawstuff/src/Makefile.am | 26 + .../ode-0.16.5/drawstuff/src/Makefile.in | 655 + .../ode-0.16.5/drawstuff/src/drawstuff.cpp | 1671 + .../ode-0.16.5/drawstuff/src/internal.h | 50 + thirdparty/ode-0.16.5/drawstuff/src/osx.cpp | 347 + .../ode-0.16.5/drawstuff/src/resource.h | 28 + .../ode-0.16.5/drawstuff/src/resources.rc | 153 + .../ode-0.16.5/drawstuff/src/windows.cpp | 536 + thirdparty/ode-0.16.5/drawstuff/src/x11.cpp | 459 + .../drawstuff/textures/checkered.ppm | Bin 0 -> 9760 bytes .../ode-0.16.5/drawstuff/textures/ground.ppm | 6 + .../ode-0.16.5/drawstuff/textures/sky.ppm | 5 + .../ode-0.16.5/drawstuff/textures/wood.ppm | 5 + thirdparty/ode-0.16.5/include/Makefile.am | 1 + thirdparty/ode-0.16.5/include/Makefile.in | 640 + .../ode-0.16.5/include/drawstuff/Makefile.am | 2 + .../ode-0.16.5/include/drawstuff/Makefile.in | 528 + .../ode-0.16.5/include/drawstuff/drawstuff.h | 325 + .../ode-0.16.5/include/drawstuff/version.h | 29 + thirdparty/ode-0.16.5/include/ode/Makefile.am | 34 + thirdparty/ode-0.16.5/include/ode/Makefile.in | 642 + thirdparty/ode-0.16.5/include/ode/README | 18 + thirdparty/ode-0.16.5/include/ode/collision.h | 1526 + .../ode-0.16.5/include/ode/collision_space.h | 182 + .../include/ode/collision_trimesh.h | 316 + thirdparty/ode-0.16.5/include/ode/common.h | 568 + .../ode-0.16.5/include/ode/compatibility.h | 40 + thirdparty/ode-0.16.5/include/ode/contact.h | 110 + .../ode-0.16.5/include/ode/cooperative.h | 229 + thirdparty/ode-0.16.5/include/ode/error.h | 63 + .../ode-0.16.5/include/ode/export-dif.h | 40 + thirdparty/ode-0.16.5/include/ode/mass.h | 144 + thirdparty/ode-0.16.5/include/ode/matrix.h | 200 + .../ode-0.16.5/include/ode/matrix_coop.h | 291 + thirdparty/ode-0.16.5/include/ode/memory.h | 59 + thirdparty/ode-0.16.5/include/ode/misc.h | 86 + thirdparty/ode-0.16.5/include/ode/objects.h | 3398 + thirdparty/ode-0.16.5/include/ode/ode.h | 56 + thirdparty/ode-0.16.5/include/ode/odeconfig.h | 218 + thirdparty/ode-0.16.5/include/ode/odecpp.h | 1355 + .../ode-0.16.5/include/ode/odecpp_collision.h | 467 + thirdparty/ode-0.16.5/include/ode/odeinit.h | 236 + thirdparty/ode-0.16.5/include/ode/odemath.h | 545 + .../ode-0.16.5/include/ode/odemath_legacy.h | 162 + thirdparty/ode-0.16.5/include/ode/precision.h | 16 + .../ode-0.16.5/include/ode/precision.h.in | 16 + thirdparty/ode-0.16.5/include/ode/rotation.h | 70 + thirdparty/ode-0.16.5/include/ode/threading.h | 412 + .../ode-0.16.5/include/ode/threading_impl.h | 292 + thirdparty/ode-0.16.5/include/ode/timer.h | 76 + thirdparty/ode-0.16.5/include/ode/version.h | 6 + .../ode-0.16.5/include/ode/version.h.in | 6 + thirdparty/ode-0.16.5/install-sh | 501 + thirdparty/ode-0.16.5/libccd/BSD-LICENSE | 44 + thirdparty/ode-0.16.5/libccd/Makefile.am | 6 + thirdparty/ode-0.16.5/libccd/Makefile.in | 804 + thirdparty/ode-0.16.5/libccd/README | 72 + thirdparty/ode-0.16.5/libccd/aclocal.m4 | 10200 ++ thirdparty/ode-0.16.5/libccd/bootstrap | 12 + thirdparty/ode-0.16.5/libccd/configure | 18785 ++++ thirdparty/ode-0.16.5/libccd/configure.ac | 50 + thirdparty/ode-0.16.5/libccd/src/Makefile.am | 22 + thirdparty/ode-0.16.5/libccd/src/Makefile.in | 744 + thirdparty/ode-0.16.5/libccd/src/alloc.c | 38 + thirdparty/ode-0.16.5/libccd/src/ccd.c | 955 + thirdparty/ode-0.16.5/libccd/src/ccd/alloc.h | 52 + thirdparty/ode-0.16.5/libccd/src/ccd/ccd.h | 148 + .../ode-0.16.5/libccd/src/ccd/compiler.h | 61 + thirdparty/ode-0.16.5/libccd/src/ccd/dbg.h | 65 + thirdparty/ode-0.16.5/libccd/src/ccd/list.h | 155 + .../ode-0.16.5/libccd/src/ccd/polytope.h | 322 + .../ode-0.16.5/libccd/src/ccd/precision.h | 14 + .../ode-0.16.5/libccd/src/ccd/precision.h.in | 14 + thirdparty/ode-0.16.5/libccd/src/ccd/quat.h | 231 + .../ode-0.16.5/libccd/src/ccd/simplex.h | 104 + .../ode-0.16.5/libccd/src/ccd/support.h | 57 + thirdparty/ode-0.16.5/libccd/src/ccd/vec3.h | 340 + thirdparty/ode-0.16.5/libccd/src/config.h.in | 97 + .../libccd/src/custom/ccdcustom/quat.h | 69 + .../libccd/src/custom/ccdcustom/vec3.h | 114 + thirdparty/ode-0.16.5/libccd/src/mpr.c | 572 + thirdparty/ode-0.16.5/libccd/src/polytope.c | 287 + thirdparty/ode-0.16.5/libccd/src/support.c | 39 + .../libccd/src/testsuites/Makefile.am | 28 + .../libccd/src/testsuites/Makefile.in | 753 + .../ode-0.16.5/libccd/src/testsuites/bench.c | 257 + .../ode-0.16.5/libccd/src/testsuites/bench2.c | 263 + .../ode-0.16.5/libccd/src/testsuites/boxbox.c | 467 + .../ode-0.16.5/libccd/src/testsuites/boxbox.h | 32 + .../ode-0.16.5/libccd/src/testsuites/boxcyl.c | 162 + .../ode-0.16.5/libccd/src/testsuites/boxcyl.h | 16 + .../ode-0.16.5/libccd/src/testsuites/common.c | 174 + .../ode-0.16.5/libccd/src/testsuites/common.h | 14 + .../libccd/src/testsuites/cu/COPYING | 674 + .../libccd/src/testsuites/cu/COPYING.LESSER | 165 + .../libccd/src/testsuites/cu/Makefile.am | 6 + .../libccd/src/testsuites/cu/Makefile.in | 587 + .../ode-0.16.5/libccd/src/testsuites/cu/cu.c | 387 + .../ode-0.16.5/libccd/src/testsuites/cu/cu.h | 164 + .../ode-0.16.5/libccd/src/testsuites/cylcyl.c | 180 + .../ode-0.16.5/libccd/src/testsuites/cylcyl.h | 29 + .../ode-0.16.5/libccd/src/testsuites/main.c | 32 + .../libccd/src/testsuites/mpr_boxbox.c | 500 + .../libccd/src/testsuites/mpr_boxbox.h | 26 + .../libccd/src/testsuites/mpr_boxcyl.c | 165 + .../libccd/src/testsuites/mpr_boxcyl.h | 16 + .../libccd/src/testsuites/mpr_cylcyl.c | 179 + .../libccd/src/testsuites/mpr_cylcyl.h | 23 + .../libccd/src/testsuites/polytope.c | 398 + .../libccd/src/testsuites/polytope.h | 24 + .../libccd/src/testsuites/spheresphere.c | 99 + .../libccd/src/testsuites/spheresphere.h | 24 + .../libccd/src/testsuites/support.c | 85 + .../libccd/src/testsuites/support.h | 102 + .../ode-0.16.5/libccd/src/testsuites/vec3.c | 273 + .../ode-0.16.5/libccd/src/testsuites/vec3.h | 20 + thirdparty/ode-0.16.5/libccd/src/vec3.c | 215 + thirdparty/ode-0.16.5/ltmain.sh | 11156 ++ thirdparty/ode-0.16.5/m4/libtool.m4 | 8388 ++ thirdparty/ode-0.16.5/m4/ltoptions.m4 | 437 + thirdparty/ode-0.16.5/m4/ltsugar.m4 | 124 + thirdparty/ode-0.16.5/m4/ltversion.m4 | 23 + thirdparty/ode-0.16.5/m4/lt~obsolete.m4 | 99 + thirdparty/ode-0.16.5/m4/pkg.m4 | 157 + thirdparty/ode-0.16.5/missing | 215 + thirdparty/ode-0.16.5/ode-config.cmake.in | 13 + thirdparty/ode-0.16.5/ode-config.in | 53 + thirdparty/ode-0.16.5/ode.pc.in | 12 + thirdparty/ode-0.16.5/ode/Makefile.am | 6 + thirdparty/ode-0.16.5/ode/Makefile.in | 643 + thirdparty/ode-0.16.5/ode/README | 158 + thirdparty/ode-0.16.5/ode/TODO | 698 + thirdparty/ode-0.16.5/ode/demo/Makefile.am | 75 + thirdparty/ode-0.16.5/ode/demo/Makefile.in | 1133 + thirdparty/ode-0.16.5/ode/demo/basket_geom.h | 599 + thirdparty/ode-0.16.5/ode/demo/bunny_geom.h | 1366 + .../ode-0.16.5/ode/demo/convex_bunny_geom.h | 468 + thirdparty/ode-0.16.5/ode/demo/convex_prism.h | 28 + thirdparty/ode-0.16.5/ode/demo/demo_I.cpp | 253 + .../ode-0.16.5/ode/demo/demo_basket.cpp | 276 + .../ode-0.16.5/ode/demo/demo_boxstack.cpp | 619 + thirdparty/ode-0.16.5/ode/demo/demo_buggy.cpp | 308 + thirdparty/ode-0.16.5/ode/demo/demo_cards.cpp | 237 + thirdparty/ode-0.16.5/ode/demo/demo_chain1.c | 171 + .../ode-0.16.5/ode/demo/demo_chain2.cpp | 165 + .../ode-0.16.5/ode/demo/demo_collision.cpp | 1463 + .../ode-0.16.5/ode/demo/demo_convex.cpp | 307 + thirdparty/ode-0.16.5/ode/demo/demo_crash.cpp | 652 + thirdparty/ode-0.16.5/ode/demo/demo_cyl.cpp | 321 + .../ode-0.16.5/ode/demo/demo_cylvssphere.cpp | 240 + thirdparty/ode-0.16.5/ode/demo/demo_dball.cpp | 194 + .../ode-0.16.5/ode/demo/demo_dhinge.cpp | 217 + .../ode-0.16.5/ode/demo/demo_feedback.cpp | 312 + .../ode-0.16.5/ode/demo/demo_friction.cpp | 205 + thirdparty/ode-0.16.5/ode/demo/demo_gyro2.cpp | 210 + .../ode-0.16.5/ode/demo/demo_gyroscopic.cpp | 258 + .../ode-0.16.5/ode/demo/demo_heightfield.cpp | 714 + thirdparty/ode-0.16.5/ode/demo/demo_hinge.cpp | 165 + .../ode-0.16.5/ode/demo/demo_jointPR.cpp | 434 + .../ode-0.16.5/ode/demo/demo_jointPU.cpp | 736 + .../ode-0.16.5/ode/demo/demo_joints.cpp | 1090 + .../ode-0.16.5/ode/demo/demo_kinematic.cpp | 239 + .../ode-0.16.5/ode/demo/demo_motion.cpp | 527 + thirdparty/ode-0.16.5/ode/demo/demo_motor.cpp | 209 + .../ode/demo/demo_moving_convex.cpp | 415 + .../ode/demo/demo_moving_trimesh.cpp | 677 + thirdparty/ode-0.16.5/ode/demo/demo_ode.cpp | 1380 + .../ode-0.16.5/ode/demo/demo_piston.cpp | 813 + .../ode-0.16.5/ode/demo/demo_plane2d.cpp | 304 + .../ode-0.16.5/ode/demo/demo_rfriction.cpp | 258 + .../ode-0.16.5/ode/demo/demo_slider.cpp | 171 + thirdparty/ode-0.16.5/ode/demo/demo_space.cpp | 232 + .../ode-0.16.5/ode/demo/demo_space_stress.cpp | 449 + thirdparty/ode-0.16.5/ode/demo/demo_step.cpp | 192 + .../ode-0.16.5/ode/demo/demo_tracks.cpp | 498 + .../ode-0.16.5/ode/demo/demo_transmission.cpp | 414 + .../ode-0.16.5/ode/demo/demo_trimesh.cpp | 605 + .../ode-0.16.5/ode/demo/halton235_geom.h | 2271 + .../ode-0.16.5/ode/demo/icosahedron_geom.h | 216 + thirdparty/ode-0.16.5/ode/demo/texturepath.h | 26 + thirdparty/ode-0.16.5/ode/demo/world_geom3.h | 9 + thirdparty/ode-0.16.5/ode/doc/Doxyfile.in | 2331 + thirdparty/ode-0.16.5/ode/doc/Makefile.am | 11 + thirdparty/ode-0.16.5/ode/doc/Makefile.in | 472 + thirdparty/ode-0.16.5/ode/doc/main.dox | 22 + thirdparty/ode-0.16.5/ode/doc/pix/amotor.jpg | Bin 0 -> 13977 bytes .../ode/doc/pix/ball-and-socket-bad.jpg | Bin 0 -> 9129 bytes .../ode/doc/pix/ball-and-socket.jpg | Bin 0 -> 10284 bytes thirdparty/ode-0.16.5/ode/doc/pix/body.jpg | Bin 0 -> 15133 bytes thirdparty/ode-0.16.5/ode/doc/pix/contact.jpg | Bin 0 -> 12368 bytes thirdparty/ode-0.16.5/ode/doc/pix/hinge.jpg | Bin 0 -> 10548 bytes thirdparty/ode-0.16.5/ode/doc/pix/hinge2.jpg | Bin 0 -> 14083 bytes thirdparty/ode-0.16.5/ode/doc/pix/joints.jpg | Bin 0 -> 8637 bytes .../ode-0.16.5/ode/doc/pix/rollingcontact.jpg | Bin 0 -> 12402 bytes .../ode-0.16.5/ode/doc/pix/sf-graph1.jpg | Bin 0 -> 28191 bytes .../ode-0.16.5/ode/doc/pix/sf-graph2.jpg | Bin 0 -> 24694 bytes thirdparty/ode-0.16.5/ode/doc/pix/slider.jpg | Bin 0 -> 10276 bytes .../ode-0.16.5/ode/doc/pix/universal.jpg | Bin 0 -> 13309 bytes thirdparty/ode-0.16.5/ode/src/Makefile.am | 201 + thirdparty/ode-0.16.5/ode/src/Makefile.in | 1100 + thirdparty/ode-0.16.5/ode/src/array.cpp | 81 + thirdparty/ode-0.16.5/ode/src/array.h | 135 + thirdparty/ode-0.16.5/ode/src/box.cpp | 878 + thirdparty/ode-0.16.5/ode/src/capsule.cpp | 416 + .../ode/src/collision_convex_trimesh.cpp | 120 + .../ode/src/collision_cylinder_box.cpp | 1038 + .../ode/src/collision_cylinder_plane.cpp | 266 + .../ode/src/collision_cylinder_sphere.cpp | 277 + .../ode/src/collision_cylinder_trimesh.cpp | 1172 + .../ode-0.16.5/ode/src/collision_kernel.cpp | 1247 + .../ode-0.16.5/ode/src/collision_kernel.h | 293 + .../ode-0.16.5/ode/src/collision_libccd.cpp | 1080 + .../ode-0.16.5/ode/src/collision_libccd.h | 44 + .../ode/src/collision_quadtreespace.cpp | 616 + .../ode-0.16.5/ode/src/collision_sapspace.cpp | 856 + .../ode-0.16.5/ode/src/collision_space.cpp | 864 + .../ode/src/collision_space_internal.h | 80 + thirdparty/ode-0.16.5/ode/src/collision_std.h | 238 + .../ode/src/collision_transform.cpp | 234 + .../ode-0.16.5/ode/src/collision_transform.h | 39 + .../ode/src/collision_trimesh_box.cpp | 1380 + .../ode/src/collision_trimesh_ccylinder.cpp | 1183 + .../ode/src/collision_trimesh_colliders.h | 47 + .../ode/src/collision_trimesh_disabled.cpp | 302 + .../ode/src/collision_trimesh_gimpact.cpp | 424 + .../ode/src/collision_trimesh_gimpact.h | 278 + .../ode/src/collision_trimesh_internal.cpp | 804 + .../ode/src/collision_trimesh_internal.h | 399 + .../ode/src/collision_trimesh_internal_impl.h | 463 + .../ode/src/collision_trimesh_opcode.cpp | 767 + .../ode/src/collision_trimesh_opcode.h | 333 + .../ode/src/collision_trimesh_plane.cpp | 226 + .../ode/src/collision_trimesh_ray.cpp | 207 + .../ode/src/collision_trimesh_sphere.cpp | 596 + .../ode/src/collision_trimesh_trimesh.cpp | 1367 + .../ode/src/collision_trimesh_trimesh_old.cpp | 2071 + .../ode-0.16.5/ode/src/collision_util.cpp | 613 + .../ode-0.16.5/ode/src/collision_util.h | 358 + thirdparty/ode-0.16.5/ode/src/common.h | 351 + thirdparty/ode-0.16.5/ode/src/config.h | 113 + thirdparty/ode-0.16.5/ode/src/config.h.in | 329 + thirdparty/ode-0.16.5/ode/src/convex.cpp | 1621 + .../ode-0.16.5/ode/src/coop_matrix_types.h | 158 + thirdparty/ode-0.16.5/ode/src/cylinder.cpp | 108 + .../ode-0.16.5/ode/src/default_threading.cpp | 77 + .../ode-0.16.5/ode/src/default_threading.h | 55 + thirdparty/ode-0.16.5/ode/src/error.cpp | 179 + thirdparty/ode-0.16.5/ode/src/error.h | 101 + thirdparty/ode-0.16.5/ode/src/export-dif.cpp | 620 + thirdparty/ode-0.16.5/ode/src/fastdot.cpp | 46 + thirdparty/ode-0.16.5/ode/src/fastdot_impl.h | 51 + .../ode-0.16.5/ode/src/fastldltfactor.cpp | 462 + .../ode-0.16.5/ode/src/fastldltfactor_impl.h | 1530 + .../ode-0.16.5/ode/src/fastldltsolve.cpp | 222 + .../ode-0.16.5/ode/src/fastldltsolve_impl.h | 49 + thirdparty/ode-0.16.5/ode/src/fastlsolve.cpp | 230 + .../ode-0.16.5/ode/src/fastlsolve_impl.h | 1610 + thirdparty/ode-0.16.5/ode/src/fastltsolve.cpp | 229 + .../ode-0.16.5/ode/src/fastltsolve_impl.h | 1440 + .../ode-0.16.5/ode/src/fastvecscale.cpp | 204 + .../ode-0.16.5/ode/src/fastvecscale_impl.h | 171 + .../ode/src/gimpact_contact_export_helper.cpp | 95 + .../ode/src/gimpact_contact_export_helper.h | 177 + .../ode/src/gimpact_gim_contact_accessor.h | 62 + .../ode/src/gimpact_plane_contact_accessor.h | 62 + thirdparty/ode-0.16.5/ode/src/heightfield.cpp | 1876 + thirdparty/ode-0.16.5/ode/src/heightfield.h | 245 + .../ode-0.16.5/ode/src/joints/Makefile.am | 37 + .../ode-0.16.5/ode/src/joints/Makefile.in | 668 + .../ode-0.16.5/ode/src/joints/amotor.cpp | 810 + thirdparty/ode-0.16.5/ode/src/joints/amotor.h | 105 + thirdparty/ode-0.16.5/ode/src/joints/ball.cpp | 186 + thirdparty/ode-0.16.5/ode/src/joints/ball.h | 54 + .../ode-0.16.5/ode/src/joints/contact.cpp | 361 + .../ode-0.16.5/ode/src/joints/contact.h | 48 + .../ode-0.16.5/ode/src/joints/dball.cpp | 314 + thirdparty/ode-0.16.5/ode/src/joints/dball.h | 58 + .../ode-0.16.5/ode/src/joints/dhinge.cpp | 220 + thirdparty/ode-0.16.5/ode/src/joints/dhinge.h | 46 + .../ode-0.16.5/ode/src/joints/fixed.cpp | 216 + thirdparty/ode-0.16.5/ode/src/joints/fixed.h | 54 + .../ode-0.16.5/ode/src/joints/hinge.cpp | 394 + thirdparty/ode-0.16.5/ode/src/joints/hinge.h | 57 + .../ode-0.16.5/ode/src/joints/hinge2.cpp | 546 + thirdparty/ode-0.16.5/ode/src/joints/hinge2.h | 71 + .../ode-0.16.5/ode/src/joints/joint.cpp | 931 + thirdparty/ode-0.16.5/ode/src/joints/joint.h | 326 + .../ode/src/joints/joint_internal.h | 70 + thirdparty/ode-0.16.5/ode/src/joints/joints.h | 48 + .../ode-0.16.5/ode/src/joints/lmotor.cpp | 214 + thirdparty/ode-0.16.5/ode/src/joints/lmotor.h | 51 + thirdparty/ode-0.16.5/ode/src/joints/null.cpp | 74 + thirdparty/ode-0.16.5/ode/src/joints/null.h | 46 + .../ode-0.16.5/ode/src/joints/piston.cpp | 729 + thirdparty/ode-0.16.5/ode/src/joints/piston.h | 112 + .../ode-0.16.5/ode/src/joints/plane2d.cpp | 195 + .../ode-0.16.5/ode/src/joints/plane2d.h | 54 + thirdparty/ode-0.16.5/ode/src/joints/pr.cpp | 613 + thirdparty/ode-0.16.5/ode/src/joints/pr.h | 100 + thirdparty/ode-0.16.5/ode/src/joints/pu.cpp | 774 + thirdparty/ode-0.16.5/ode/src/joints/pu.h | 88 + .../ode-0.16.5/ode/src/joints/slider.cpp | 423 + thirdparty/ode-0.16.5/ode/src/joints/slider.h | 59 + .../ode/src/joints/transmission.cpp | 698 + .../ode-0.16.5/ode/src/joints/transmission.h | 51 + .../ode-0.16.5/ode/src/joints/universal.cpp | 809 + .../ode-0.16.5/ode/src/joints/universal.h | 65 + thirdparty/ode-0.16.5/ode/src/lcp.cpp | 1304 + thirdparty/ode-0.16.5/ode/src/lcp.h | 81 + thirdparty/ode-0.16.5/ode/src/mass.cpp | 554 + thirdparty/ode-0.16.5/ode/src/mat.cpp | 231 + thirdparty/ode-0.16.5/ode/src/mat.h | 71 + thirdparty/ode-0.16.5/ode/src/matrix.cpp | 593 + thirdparty/ode-0.16.5/ode/src/matrix.h | 160 + thirdparty/ode-0.16.5/ode/src/memory.cpp | 95 + thirdparty/ode-0.16.5/ode/src/misc.cpp | 217 + thirdparty/ode-0.16.5/ode/src/nextafterf.c | 115 + thirdparty/ode-0.16.5/ode/src/objects.cpp | 138 + thirdparty/ode-0.16.5/ode/src/objects.h | 206 + thirdparty/ode-0.16.5/ode/src/obstack.cpp | 157 + thirdparty/ode-0.16.5/ode/src/obstack.h | 73 + thirdparty/ode-0.16.5/ode/src/ode.cpp | 2325 + thirdparty/ode-0.16.5/ode/src/odeinit.cpp | 575 + thirdparty/ode-0.16.5/ode/src/odemath.cpp | 312 + thirdparty/ode-0.16.5/ode/src/odemath.h | 97 + thirdparty/ode-0.16.5/ode/src/odeou.cpp | 107 + thirdparty/ode-0.16.5/ode/src/odeou.h | 107 + thirdparty/ode-0.16.5/ode/src/odetls.cpp | 153 + thirdparty/ode-0.16.5/ode/src/odetls.h | 126 + thirdparty/ode-0.16.5/ode/src/plane.cpp | 146 + thirdparty/ode-0.16.5/ode/src/quickstep.cpp | 3344 + thirdparty/ode-0.16.5/ode/src/quickstep.h | 39 + thirdparty/ode-0.16.5/ode/src/ray.cpp | 735 + .../ode-0.16.5/ode/src/resource_control.cpp | 259 + .../ode-0.16.5/ode/src/resource_control.h | 151 + thirdparty/ode-0.16.5/ode/src/rotation.cpp | 317 + .../ode-0.16.5/ode/src/simple_cooperative.cpp | 84 + .../ode-0.16.5/ode/src/simple_cooperative.h | 73 + thirdparty/ode-0.16.5/ode/src/sphere.cpp | 251 + thirdparty/ode-0.16.5/ode/src/step.cpp | 1672 + thirdparty/ode-0.16.5/ode/src/step.h | 40 + .../ode-0.16.5/ode/src/threaded_solver_ldlt.h | 809 + .../ode/src/threading_atomics_provs.h | 194 + .../ode-0.16.5/ode/src/threading_base.cpp | 135 + .../ode-0.16.5/ode/src/threading_base.h | 291 + .../ode-0.16.5/ode/src/threading_fake_sync.h | 128 + .../ode-0.16.5/ode/src/threading_impl.cpp | 282 + .../ode-0.16.5/ode/src/threading_impl.h | 40 + .../ode-0.16.5/ode/src/threading_impl_posix.h | 638 + .../ode/src/threading_impl_templates.h | 1270 + .../ode-0.16.5/ode/src/threading_impl_win.h | 273 + .../ode/src/threading_pool_posix.cpp | 823 + .../ode-0.16.5/ode/src/threading_pool_win.cpp | 670 + .../ode-0.16.5/ode/src/threadingutils.h | 157 + thirdparty/ode-0.16.5/ode/src/timer.cpp | 471 + thirdparty/ode-0.16.5/ode/src/typedefs.h | 74 + thirdparty/ode-0.16.5/ode/src/util.cpp | 1231 + thirdparty/ode-0.16.5/ode/src/util.h | 440 + thirdparty/ode-0.16.5/ou/CHANGELOG.TXT | 55 + thirdparty/ode-0.16.5/ou/INSTALL.TXT | 14 + thirdparty/ode-0.16.5/ou/LICENSE-BSD.TXT | 34 + thirdparty/ode-0.16.5/ou/LICENSE-LESSER.TXT | 166 + thirdparty/ode-0.16.5/ou/LICENSE-ZLIB.TXT | 22 + thirdparty/ode-0.16.5/ou/LICENSE.TXT | 675 + thirdparty/ode-0.16.5/ou/Makefile.am | 10 + thirdparty/ode-0.16.5/ou/Makefile.in | 804 + thirdparty/ode-0.16.5/ou/README.TXT | 42 + thirdparty/ode-0.16.5/ou/aclocal.m4 | 10200 ++ thirdparty/ode-0.16.5/ou/bootstrap | 11 + thirdparty/ode-0.16.5/ou/build/make/makefile | 219 + .../ode-0.16.5/ou/build/make/makefile.os | 41 + thirdparty/ode-0.16.5/ou/build/vs2005/ou.sln | 39 + .../ode-0.16.5/ou/build/vs2005/ou.vcproj | 494 + thirdparty/ode-0.16.5/ou/build/vs6/ou.dsp | 172 + thirdparty/ode-0.16.5/ou/build/vs6/ou.dsw | 44 + thirdparty/ode-0.16.5/ou/configure | 20569 ++++ thirdparty/ode-0.16.5/ou/configure.ac | 145 + .../ode-0.16.5/ou/include/ou/Makefile.am | 17 + .../ode-0.16.5/ou/include/ou/Makefile.in | 457 + thirdparty/ode-0.16.5/ou/include/ou/assert.h | 188 + thirdparty/ode-0.16.5/ou/include/ou/atomic.h | 1843 + .../ode-0.16.5/ou/include/ou/atomicflags.h | 367 + .../ode-0.16.5/ou/include/ou/customization.h | 149 + .../ode-0.16.5/ou/include/ou/enumarrays.h | 256 + .../ode-0.16.5/ou/include/ou/features.h | 51 + thirdparty/ode-0.16.5/ou/include/ou/flags.h | 35 + .../ode-0.16.5/ou/include/ou/flagsdefines.h | 37 + .../ode-0.16.5/ou/include/ou/inttypes.h | 136 + thirdparty/ode-0.16.5/ou/include/ou/macros.h | 79 + thirdparty/ode-0.16.5/ou/include/ou/malloc.h | 48 + .../ode-0.16.5/ou/include/ou/namespace.h | 43 + .../ode-0.16.5/ou/include/ou/platform.h | 398 + .../ode-0.16.5/ou/include/ou/simpleflags.h | 334 + .../ode-0.16.5/ou/include/ou/templates.h | 90 + .../ou/include/ou/threadlocalstorage.h | 297 + .../ode-0.16.5/ou/include/ou/typewrapper.h | 111 + thirdparty/ode-0.16.5/ou/src/ou/Makefile.am | 13 + thirdparty/ode-0.16.5/ou/src/ou/Makefile.in | 598 + thirdparty/ode-0.16.5/ou/src/ou/atomic.cpp | 445 + .../ode-0.16.5/ou/src/ou/customization.cpp | 64 + thirdparty/ode-0.16.5/ou/src/ou/malloc.cpp | 106 + .../ou/src/ou/threadlocalstorage.cpp | 1336 + thirdparty/ode-0.16.5/ou/test/Makefile.am | 10 + thirdparty/ode-0.16.5/ou/test/Makefile.in | 589 + thirdparty/ode-0.16.5/ou/test/outest.cpp | 9027 ++ thirdparty/ode-0.16.5/test-driver | 148 + thirdparty/ode-0.16.5/tests/Makefile.am | 30 + thirdparty/ode-0.16.5/tests/Makefile.in | 1116 + .../ode-0.16.5/tests/UnitTest++/COPYING | 20 + .../ode-0.16.5/tests/UnitTest++/Makefile.am | 6 + .../ode-0.16.5/tests/UnitTest++/Makefile.in | 643 + thirdparty/ode-0.16.5/tests/UnitTest++/README | 62 + .../tests/UnitTest++/docs/UnitTest++.html | 260 + .../tests/UnitTest++/src/AssertException.cpp | 32 + .../tests/UnitTest++/src/AssertException.h | 28 + .../tests/UnitTest++/src/CheckMacros.h | 102 + .../tests/UnitTest++/src/Checks.cpp | 48 + .../ode-0.16.5/tests/UnitTest++/src/Checks.h | 146 + .../ode-0.16.5/tests/UnitTest++/src/Config.h | 25 + .../UnitTest++/src/DeferredTestReporter.cpp | 28 + .../UnitTest++/src/DeferredTestReporter.h | 28 + .../UnitTest++/src/DeferredTestResult.cpp | 26 + .../tests/UnitTest++/src/DeferredTestResult.h | 29 + .../tests/UnitTest++/src/Makefile.am | 33 + .../tests/UnitTest++/src/Makefile.in | 784 + .../tests/UnitTest++/src/MemoryOutStream.cpp | 143 + .../tests/UnitTest++/src/MemoryOutStream.h | 67 + .../tests/UnitTest++/src/Posix/Makefile.am | 5 + .../tests/UnitTest++/src/Posix/Makefile.in | 627 + .../UnitTest++/src/Posix/SignalTranslator.cpp | 46 + .../UnitTest++/src/Posix/SignalTranslator.h | 42 + .../UnitTest++/src/Posix/TimeHelpers.cpp | 33 + .../tests/UnitTest++/src/Posix/TimeHelpers.h | 28 + .../tests/UnitTest++/src/ReportAssert.cpp | 10 + .../tests/UnitTest++/src/ReportAssert.h | 10 + .../ode-0.16.5/tests/UnitTest++/src/Test.cpp | 62 + .../ode-0.16.5/tests/UnitTest++/src/Test.h | 34 + .../tests/UnitTest++/src/TestDetails.cpp | 22 + .../tests/UnitTest++/src/TestDetails.h | 24 + .../tests/UnitTest++/src/TestList.cpp | 39 + .../tests/UnitTest++/src/TestList.h | 32 + .../tests/UnitTest++/src/TestMacros.h | 99 + .../tests/UnitTest++/src/TestReporter.cpp | 10 + .../tests/UnitTest++/src/TestReporter.h | 20 + .../UnitTest++/src/TestReporterStdout.cpp | 36 + .../tests/UnitTest++/src/TestReporterStdout.h | 19 + .../tests/UnitTest++/src/TestResults.cpp | 60 + .../tests/UnitTest++/src/TestResults.h | 36 + .../tests/UnitTest++/src/TestRunner.cpp | 60 + .../tests/UnitTest++/src/TestRunner.h | 17 + .../tests/UnitTest++/src/TestSuite.h | 14 + .../tests/UnitTest++/src/TimeConstraint.cpp | 28 + .../tests/UnitTest++/src/TimeConstraint.h | 34 + .../tests/UnitTest++/src/TimeHelpers.h | 7 + .../tests/UnitTest++/src/UnitTest++.h | 17 + .../tests/UnitTest++/src/Win32/Makefile.am | 4 + .../tests/UnitTest++/src/Win32/Makefile.in | 624 + .../UnitTest++/src/Win32/TimeHelpers.cpp | 46 + .../tests/UnitTest++/src/Win32/TimeHelpers.h | 48 + .../tests/UnitTest++/src/XmlTestReporter.cpp | 126 + .../tests/UnitTest++/src/XmlTestReporter.h | 34 + thirdparty/ode-0.16.5/tests/collision.cpp | 224 + thirdparty/ode-0.16.5/tests/friction.cpp | 176 + thirdparty/ode-0.16.5/tests/joint.cpp | 3054 + .../ode-0.16.5/tests/joints/Makefile.am | 21 + .../ode-0.16.5/tests/joints/Makefile.in | 638 + thirdparty/ode-0.16.5/tests/joints/amotor.cpp | 324 + thirdparty/ode-0.16.5/tests/joints/ball.cpp | 160 + thirdparty/ode-0.16.5/tests/joints/dball.cpp | 81 + thirdparty/ode-0.16.5/tests/joints/fixed.cpp | 149 + thirdparty/ode-0.16.5/tests/joints/hinge.cpp | 928 + thirdparty/ode-0.16.5/tests/joints/hinge2.cpp | 167 + thirdparty/ode-0.16.5/tests/joints/piston.cpp | 1456 + thirdparty/ode-0.16.5/tests/joints/pr.cpp | 1160 + thirdparty/ode-0.16.5/tests/joints/pu.cpp | 920 + thirdparty/ode-0.16.5/tests/joints/slider.cpp | 1332 + .../ode-0.16.5/tests/joints/universal.cpp | 2119 + thirdparty/ode-0.16.5/tests/main.cpp | 13 + thirdparty/ode-0.16.5/tests/odemath.cpp | 247 + thirdparty/ode-0.16.5/tools/README.txt | 94 + thirdparty/ode-0.16.5/tools/msw-release.bat | 138 + .../ode-0.16.5/tools/ode_convex_export.py | 60 + thirdparty/ode-0.16.5/tools/src-release.sh | 117 + thirdparty/stb/.gitignore | 3 + thirdparty/stb/.travis.yml | 8 + thirdparty/stb/LICENSE | 37 + thirdparty/stb/README.md | 184 + thirdparty/stb/deprecated/rrsprintf.h | 1055 + thirdparty/stb/deprecated/stb.h | 13111 +++ thirdparty/stb/deprecated/stb_image.c | 4678 + thirdparty/stb/deprecated/stb_image_resize.h | 2634 + thirdparty/stb/deprecated/stretch_test.c | 29 + thirdparty/stb/deprecated/stretchy_buffer.h | 263 + thirdparty/stb/deprecated/stretchy_buffer.txt | 28 + thirdparty/stb/docs/other_libs.md | 1 + thirdparty/stb/docs/stb_howto.txt | 185 + .../stb/docs/stb_voxel_render_interview.md | 173 + thirdparty/stb/docs/why_public_domain.md | 117 + thirdparty/stb/stb_c_lexer.h | 940 + thirdparty/stb/stb_connected_components.h | 1049 + thirdparty/stb/stb_divide.h | 433 + thirdparty/stb/stb_ds.h | 1895 + thirdparty/stb/stb_dxt.h | 719 + thirdparty/stb/stb_easy_font.h | 305 + thirdparty/stb/stb_herringbone_wang_tile.h | 1221 + thirdparty/stb/stb_hexwave.h | 680 + thirdparty/stb/stb_image.h | 7985 ++ thirdparty/stb/stb_image_resize2.h | 10365 ++ .../stb/stb_image_resize_test/dotimings.c | 224 + .../stb_image_resize_test/old_image_resize.h | 2738 + thirdparty/stb/stb_image_resize_test/oldir.c | 56 + .../stb/stb_image_resize_test/stbirtest.c | 992 + .../stb/stb_image_resize_test/vf_train.c | 999 + thirdparty/stb/stb_image_write.h | 1724 + thirdparty/stb/stb_include.h | 295 + thirdparty/stb/stb_leakcheck.h | 194 + thirdparty/stb/stb_perlin.h | 428 + thirdparty/stb/stb_rect_pack.h | 623 + thirdparty/stb/stb_sprintf.h | 1906 + thirdparty/stb/stb_textedit.h | 1429 + thirdparty/stb/stb_tilemap_editor.h | 4187 + thirdparty/stb/stb_truetype.h | 5077 + thirdparty/stb/stb_vorbis.c | 5584 + thirdparty/stb/stb_voxel_render.h | 3807 + thirdparty/stb/tools/README.footer.md | 135 + thirdparty/stb/tools/README.header.md | 22 + thirdparty/stb/tools/README.list | 21 + thirdparty/stb/tools/build_matrix.c | 137 + thirdparty/stb/tools/easy_font_maker.c | 211 + thirdparty/stb/tools/make_readme.c | 65 + thirdparty/stb/tools/make_readme.dsp | 97 + thirdparty/stb/tools/mr.bat | 1 + thirdparty/stb/tools/trailing_whitespace.c | 32 + thirdparty/stb/tools/unicode.c | 749 + thirdparty/stb/tools/unicode/unicode.dsp | 88 + tools/premake5.exe | Bin 0 -> 1568256 bytes 1398 files changed, 739400 insertions(+) create mode 100644 content/editor/entities.lua create mode 100644 content/levels/playertest.txt create mode 100644 content/shaders/debug_draw.ps create mode 100644 content/shaders/debug_draw.vs create mode 100644 content/shaders/lit_generic.ps create mode 100644 content/shaders/lit_generic.vs create mode 100644 content/shaders/lit_generic_texcoordgen.ps create mode 100644 content/shaders/ui_base.ps create mode 100644 content/shaders/ui_base.vs create mode 100644 content/shaders/ui_tex.ps create mode 100644 content/test/ship.blend create mode 100644 content/test/ship.blend1 create mode 100644 content/test/ship.mtl create mode 100644 content/test/ship.obj create mode 100644 content/test/ship_back2.blend create mode 100644 content/test/ship_backup.blend create mode 100644 content/test/ship_weapon.mtl create mode 100644 content/test/ship_weapon.obj create mode 100644 content/test/test.mtl create mode 100644 content/test/test.obj create mode 100644 content/test/tetrad — копия.png create mode 100644 content/test/tetrad.png create mode 100644 content/test/uvgrid.png create mode 100644 content/test/water_test.mtl create mode 100644 content/test/water_test.obj create mode 100644 content/textures/env/env_water.png create mode 100644 content/textures/font/font_arial_18.png create mode 100644 content/textures/font/font_system.png create mode 100644 content/textures/system/notex.png create mode 100644 content/textures/ui/ui_btn_newgame.png create mode 100644 content/textures/ui/ui_btn_quit.png create mode 100644 content/textures/ui/ui_button_template.pdn create mode 100644 content/textures/ui/ui_cursor.png create mode 100644 content/textures/ui/ui_main_bg.pdn create mode 100644 content/textures/ui/ui_main_bg.png create mode 100644 content/textures/ui/ui_msg_nopoints.png create mode 100644 content/textures/ui/ui_new_unit.png create mode 100644 gen_vs2022.bat create mode 100644 premake5.lua create mode 100644 src/engine/filesystem/file.cpp create mode 100644 src/engine/filesystem/file.h create mode 100644 src/engine/filesystem/filecommon.h create mode 100644 src/engine/filesystem/filemanager.cpp create mode 100644 src/engine/filesystem/filemanager.h create mode 100644 src/engine/filesystem/stream.cpp create mode 100644 src/engine/filesystem/stream.h create mode 100644 src/engine/input/input_system.cpp create mode 100644 src/engine/input/input_system.h create mode 100644 src/engine/physics/physics_object.cpp create mode 100644 src/engine/physics/physics_object.h create mode 100644 src/engine/physics/physics_render.cpp create mode 100644 src/engine/physics/physics_render.h create mode 100644 src/engine/physics/physics_world.cpp create mode 100644 src/engine/physics/physics_world.h create mode 100644 src/engine/render/color.h create mode 100644 src/engine/render/debugrender.cpp create mode 100644 src/engine/render/debugrender.h create mode 100644 src/engine/render/font.cpp create mode 100644 src/engine/render/font.h create mode 100644 src/engine/render/gl_shared.cpp create mode 100644 src/engine/render/gl_shared.h create mode 100644 src/engine/render/indexbuffer.cpp create mode 100644 src/engine/render/indexbuffer.h create mode 100644 src/engine/render/modelmanager.cpp create mode 100644 src/engine/render/modelmanager.h create mode 100644 src/engine/render/render.cpp create mode 100644 src/engine/render/render.h create mode 100644 src/engine/render/render_shared.h create mode 100644 src/engine/render/renderdevice.cpp create mode 100644 src/engine/render/renderdevice.h create mode 100644 src/engine/render/rendertarget.cpp create mode 100644 src/engine/render/rendertarget.h create mode 100644 src/engine/render/shader.cpp create mode 100644 src/engine/render/shader.h create mode 100644 src/engine/render/shadersystem.cpp create mode 100644 src/engine/render/shadersystem.h create mode 100644 src/engine/render/texture2d.cpp create mode 100644 src/engine/render/texture2d.h create mode 100644 src/engine/render/texturesmanager.cpp create mode 100644 src/engine/render/texturesmanager.h create mode 100644 src/engine/render/ui.cpp create mode 100644 src/engine/render/ui.h create mode 100644 src/engine/render/vertexbuffer.cpp create mode 100644 src/engine/render/vertexbuffer.h create mode 100644 src/engine/sound/sound_system.cpp create mode 100644 src/engine/sound/sound_system.h create mode 100644 src/engine/sound/sound_system_null.cpp create mode 100644 src/engine/sound/soundsys_dsound.cpp create mode 100644 src/engine/utils/logger.cpp create mode 100644 src/engine/utils/logger.h create mode 100644 src/engine/utils/maths.cpp create mode 100644 src/engine/utils/maths.h create mode 100644 src/engine/utils/objectmanager.cpp create mode 100644 src/engine/utils/objectmanager.h create mode 100644 src/engine/utils/refcount.h create mode 100644 src/engine/utils/rtti.cpp create mode 100644 src/engine/utils/rtti.h create mode 100644 src/engine/utils/string.cpp create mode 100644 src/engine/utils/string.h create mode 100644 src/engine/utils/timer.cpp create mode 100644 src/engine/utils/timer.h create mode 100644 src/engine/utils/win32_keys.h create mode 100644 src/game/camera.cpp create mode 100644 src/game/camera.h create mode 100644 src/game/game_app.cpp create mode 100644 src/game/game_app.h create mode 100644 src/game/game_object.cpp create mode 100644 src/game/game_object.h create mode 100644 src/game/game_object_factory.cpp create mode 100644 src/game/game_object_factory.h create mode 100644 src/game/level.cpp create mode 100644 src/game/level.h create mode 100644 src/game/lua_impl.c create mode 100644 src/game/main_menu.cpp create mode 100644 src/game/main_menu.h create mode 100644 src/game/phys_test.cpp create mode 100644 src/game/phys_test.h create mode 100644 src/game/player.cpp create mode 100644 src/game/player.h create mode 100644 src/game/script_system.cpp create mode 100644 src/game/script_system.h create mode 100644 src/main/main.cpp create mode 100644 thirdparty/GL/glcorearb.h create mode 100644 thirdparty/GL/glext.h create mode 100644 thirdparty/GL/glxext.h create mode 100644 thirdparty/GL/wglext.h create mode 100644 thirdparty/KHR/khrplatform.h create mode 100644 thirdparty/glad/include/KHR/khrplatform.h create mode 100644 thirdparty/glad/include/glad/glad.h create mode 100644 thirdparty/glad/src/glad.c create mode 100644 thirdparty/glm/glm/CMakeLists.txt create mode 100644 thirdparty/glm/glm/common.hpp create mode 100644 thirdparty/glm/glm/copying.txt create mode 100644 thirdparty/glm/glm/detail/_features.hpp create mode 100644 thirdparty/glm/glm/detail/_fixes.hpp create mode 100644 thirdparty/glm/glm/detail/_noise.hpp create mode 100644 thirdparty/glm/glm/detail/_swizzle.hpp create mode 100644 thirdparty/glm/glm/detail/_swizzle_func.hpp create mode 100644 thirdparty/glm/glm/detail/_vectorize.hpp create mode 100644 thirdparty/glm/glm/detail/compute_common.hpp create mode 100644 thirdparty/glm/glm/detail/compute_vector_decl.hpp create mode 100644 thirdparty/glm/glm/detail/compute_vector_relational.hpp create mode 100644 thirdparty/glm/glm/detail/func_common.inl create mode 100644 thirdparty/glm/glm/detail/func_common_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_exponential.inl create mode 100644 thirdparty/glm/glm/detail/func_exponential_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_geometric.inl create mode 100644 thirdparty/glm/glm/detail/func_geometric_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_integer.inl create mode 100644 thirdparty/glm/glm/detail/func_integer_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_matrix.inl create mode 100644 thirdparty/glm/glm/detail/func_matrix_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_packing.inl create mode 100644 thirdparty/glm/glm/detail/func_packing_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_trigonometric.inl create mode 100644 thirdparty/glm/glm/detail/func_trigonometric_simd.inl create mode 100644 thirdparty/glm/glm/detail/func_vector_relational.inl create mode 100644 thirdparty/glm/glm/detail/func_vector_relational_simd.inl create mode 100644 thirdparty/glm/glm/detail/glm.cpp create mode 100644 thirdparty/glm/glm/detail/qualifier.hpp create mode 100644 thirdparty/glm/glm/detail/setup.hpp create mode 100644 thirdparty/glm/glm/detail/type_float.hpp create mode 100644 thirdparty/glm/glm/detail/type_half.hpp create mode 100644 thirdparty/glm/glm/detail/type_half.inl create mode 100644 thirdparty/glm/glm/detail/type_mat2x2.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat2x2.inl create mode 100644 thirdparty/glm/glm/detail/type_mat2x3.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat2x3.inl create mode 100644 thirdparty/glm/glm/detail/type_mat2x4.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat2x4.inl create mode 100644 thirdparty/glm/glm/detail/type_mat3x2.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat3x2.inl create mode 100644 thirdparty/glm/glm/detail/type_mat3x3.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat3x3.inl create mode 100644 thirdparty/glm/glm/detail/type_mat3x4.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat3x4.inl create mode 100644 thirdparty/glm/glm/detail/type_mat4x2.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat4x2.inl create mode 100644 thirdparty/glm/glm/detail/type_mat4x3.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat4x3.inl create mode 100644 thirdparty/glm/glm/detail/type_mat4x4.hpp create mode 100644 thirdparty/glm/glm/detail/type_mat4x4.inl create mode 100644 thirdparty/glm/glm/detail/type_mat4x4_simd.inl create mode 100644 thirdparty/glm/glm/detail/type_quat.hpp create mode 100644 thirdparty/glm/glm/detail/type_quat.inl create mode 100644 thirdparty/glm/glm/detail/type_quat_simd.inl create mode 100644 thirdparty/glm/glm/detail/type_vec1.hpp create mode 100644 thirdparty/glm/glm/detail/type_vec1.inl create mode 100644 thirdparty/glm/glm/detail/type_vec2.hpp create mode 100644 thirdparty/glm/glm/detail/type_vec2.inl create mode 100644 thirdparty/glm/glm/detail/type_vec3.hpp create mode 100644 thirdparty/glm/glm/detail/type_vec3.inl create mode 100644 thirdparty/glm/glm/detail/type_vec4.hpp create mode 100644 thirdparty/glm/glm/detail/type_vec4.inl create mode 100644 thirdparty/glm/glm/detail/type_vec4_simd.inl create mode 100644 thirdparty/glm/glm/exponential.hpp create mode 100644 thirdparty/glm/glm/ext.hpp create mode 100644 thirdparty/glm/glm/ext/_matrix_vectorize.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_clip_space.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_clip_space.inl create mode 100644 thirdparty/glm/glm/ext/matrix_common.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_common.inl create mode 100644 thirdparty/glm/glm/ext/matrix_double2x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double2x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double2x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double2x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double2x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double2x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double3x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_double4x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float2x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float3x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_float4x4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int2x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int3x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_int4x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_integer.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_integer.inl create mode 100644 thirdparty/glm/glm/ext/matrix_projection.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_projection.inl create mode 100644 thirdparty/glm/glm/ext/matrix_relational.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_relational.inl create mode 100644 thirdparty/glm/glm/ext/matrix_transform.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_transform.inl create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint2x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint3x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x2.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x3.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x4.hpp create mode 100644 thirdparty/glm/glm/ext/matrix_uint4x4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_common.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_common.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_common_simd.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_double.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_double_precision.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_exponential.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_exponential.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_float.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_float_precision.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_geometric.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_geometric.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_relational.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_relational.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_transform.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_transform.inl create mode 100644 thirdparty/glm/glm/ext/quaternion_trigonometric.hpp create mode 100644 thirdparty/glm/glm/ext/quaternion_trigonometric.inl create mode 100644 thirdparty/glm/glm/ext/scalar_common.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_common.inl create mode 100644 thirdparty/glm/glm/ext/scalar_constants.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_constants.inl create mode 100644 thirdparty/glm/glm/ext/scalar_int_sized.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_integer.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_integer.inl create mode 100644 thirdparty/glm/glm/ext/scalar_packing.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_packing.inl create mode 100644 thirdparty/glm/glm/ext/scalar_reciprocal.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_reciprocal.inl create mode 100644 thirdparty/glm/glm/ext/scalar_relational.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_relational.inl create mode 100644 thirdparty/glm/glm/ext/scalar_uint_sized.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_ulp.hpp create mode 100644 thirdparty/glm/glm/ext/scalar_ulp.inl create mode 100644 thirdparty/glm/glm/ext/vector_bool1.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool1_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool2.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool3.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool4.hpp create mode 100644 thirdparty/glm/glm/ext/vector_bool4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_common.hpp create mode 100644 thirdparty/glm/glm/ext/vector_common.inl create mode 100644 thirdparty/glm/glm/ext/vector_double1.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double1_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double2.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double3.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double4.hpp create mode 100644 thirdparty/glm/glm/ext/vector_double4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float1.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float1_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float2.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float2_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float3.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float3_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float4.hpp create mode 100644 thirdparty/glm/glm/ext/vector_float4_precision.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int1.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int1_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int2.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int3.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int4.hpp create mode 100644 thirdparty/glm/glm/ext/vector_int4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_integer.hpp create mode 100644 thirdparty/glm/glm/ext/vector_integer.inl create mode 100644 thirdparty/glm/glm/ext/vector_packing.hpp create mode 100644 thirdparty/glm/glm/ext/vector_packing.inl create mode 100644 thirdparty/glm/glm/ext/vector_reciprocal.hpp create mode 100644 thirdparty/glm/glm/ext/vector_reciprocal.inl create mode 100644 thirdparty/glm/glm/ext/vector_relational.hpp create mode 100644 thirdparty/glm/glm/ext/vector_relational.inl create mode 100644 thirdparty/glm/glm/ext/vector_uint1.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint1_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint2.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint2_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint3.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint3_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint4.hpp create mode 100644 thirdparty/glm/glm/ext/vector_uint4_sized.hpp create mode 100644 thirdparty/glm/glm/ext/vector_ulp.hpp create mode 100644 thirdparty/glm/glm/ext/vector_ulp.inl create mode 100644 thirdparty/glm/glm/fwd.hpp create mode 100644 thirdparty/glm/glm/geometric.hpp create mode 100644 thirdparty/glm/glm/glm.cppm create mode 100644 thirdparty/glm/glm/glm.hpp create mode 100644 thirdparty/glm/glm/gtc/bitfield.hpp create mode 100644 thirdparty/glm/glm/gtc/bitfield.inl create mode 100644 thirdparty/glm/glm/gtc/color_space.hpp create mode 100644 thirdparty/glm/glm/gtc/color_space.inl create mode 100644 thirdparty/glm/glm/gtc/constants.hpp create mode 100644 thirdparty/glm/glm/gtc/constants.inl create mode 100644 thirdparty/glm/glm/gtc/epsilon.hpp create mode 100644 thirdparty/glm/glm/gtc/epsilon.inl create mode 100644 thirdparty/glm/glm/gtc/integer.hpp create mode 100644 thirdparty/glm/glm/gtc/integer.inl create mode 100644 thirdparty/glm/glm/gtc/matrix_access.hpp create mode 100644 thirdparty/glm/glm/gtc/matrix_access.inl create mode 100644 thirdparty/glm/glm/gtc/matrix_integer.hpp create mode 100644 thirdparty/glm/glm/gtc/matrix_inverse.hpp create mode 100644 thirdparty/glm/glm/gtc/matrix_inverse.inl create mode 100644 thirdparty/glm/glm/gtc/matrix_transform.hpp create mode 100644 thirdparty/glm/glm/gtc/matrix_transform.inl create mode 100644 thirdparty/glm/glm/gtc/noise.hpp create mode 100644 thirdparty/glm/glm/gtc/noise.inl create mode 100644 thirdparty/glm/glm/gtc/packing.hpp create mode 100644 thirdparty/glm/glm/gtc/packing.inl create mode 100644 thirdparty/glm/glm/gtc/quaternion.hpp create mode 100644 thirdparty/glm/glm/gtc/quaternion.inl create mode 100644 thirdparty/glm/glm/gtc/quaternion_simd.inl create mode 100644 thirdparty/glm/glm/gtc/random.hpp create mode 100644 thirdparty/glm/glm/gtc/random.inl create mode 100644 thirdparty/glm/glm/gtc/reciprocal.hpp create mode 100644 thirdparty/glm/glm/gtc/round.hpp create mode 100644 thirdparty/glm/glm/gtc/round.inl create mode 100644 thirdparty/glm/glm/gtc/type_aligned.hpp create mode 100644 thirdparty/glm/glm/gtc/type_precision.hpp create mode 100644 thirdparty/glm/glm/gtc/type_precision.inl create mode 100644 thirdparty/glm/glm/gtc/type_ptr.hpp create mode 100644 thirdparty/glm/glm/gtc/type_ptr.inl create mode 100644 thirdparty/glm/glm/gtc/ulp.hpp create mode 100644 thirdparty/glm/glm/gtc/ulp.inl create mode 100644 thirdparty/glm/glm/gtc/vec1.hpp create mode 100644 thirdparty/glm/glm/gtx/associated_min_max.hpp create mode 100644 thirdparty/glm/glm/gtx/associated_min_max.inl create mode 100644 thirdparty/glm/glm/gtx/bit.hpp create mode 100644 thirdparty/glm/glm/gtx/bit.inl create mode 100644 thirdparty/glm/glm/gtx/closest_point.hpp create mode 100644 thirdparty/glm/glm/gtx/closest_point.inl create mode 100644 thirdparty/glm/glm/gtx/color_encoding.hpp create mode 100644 thirdparty/glm/glm/gtx/color_encoding.inl create mode 100644 thirdparty/glm/glm/gtx/color_space.hpp create mode 100644 thirdparty/glm/glm/gtx/color_space.inl create mode 100644 thirdparty/glm/glm/gtx/color_space_YCoCg.hpp create mode 100644 thirdparty/glm/glm/gtx/color_space_YCoCg.inl create mode 100644 thirdparty/glm/glm/gtx/common.hpp create mode 100644 thirdparty/glm/glm/gtx/common.inl create mode 100644 thirdparty/glm/glm/gtx/compatibility.hpp create mode 100644 thirdparty/glm/glm/gtx/compatibility.inl create mode 100644 thirdparty/glm/glm/gtx/component_wise.hpp create mode 100644 thirdparty/glm/glm/gtx/component_wise.inl create mode 100644 thirdparty/glm/glm/gtx/dual_quaternion.hpp create mode 100644 thirdparty/glm/glm/gtx/dual_quaternion.inl create mode 100644 thirdparty/glm/glm/gtx/easing.hpp create mode 100644 thirdparty/glm/glm/gtx/easing.inl create mode 100644 thirdparty/glm/glm/gtx/euler_angles.hpp create mode 100644 thirdparty/glm/glm/gtx/euler_angles.inl create mode 100644 thirdparty/glm/glm/gtx/extend.hpp create mode 100644 thirdparty/glm/glm/gtx/extend.inl create mode 100644 thirdparty/glm/glm/gtx/extended_min_max.hpp create mode 100644 thirdparty/glm/glm/gtx/extended_min_max.inl create mode 100644 thirdparty/glm/glm/gtx/exterior_product.hpp create mode 100644 thirdparty/glm/glm/gtx/exterior_product.inl create mode 100644 thirdparty/glm/glm/gtx/fast_exponential.hpp create mode 100644 thirdparty/glm/glm/gtx/fast_exponential.inl create mode 100644 thirdparty/glm/glm/gtx/fast_square_root.hpp create mode 100644 thirdparty/glm/glm/gtx/fast_square_root.inl create mode 100644 thirdparty/glm/glm/gtx/fast_trigonometry.hpp create mode 100644 thirdparty/glm/glm/gtx/fast_trigonometry.inl create mode 100644 thirdparty/glm/glm/gtx/float_notmalize.inl create mode 100644 thirdparty/glm/glm/gtx/functions.hpp create mode 100644 thirdparty/glm/glm/gtx/functions.inl create mode 100644 thirdparty/glm/glm/gtx/gradient_paint.hpp create mode 100644 thirdparty/glm/glm/gtx/gradient_paint.inl create mode 100644 thirdparty/glm/glm/gtx/handed_coordinate_space.hpp create mode 100644 thirdparty/glm/glm/gtx/handed_coordinate_space.inl create mode 100644 thirdparty/glm/glm/gtx/hash.hpp create mode 100644 thirdparty/glm/glm/gtx/hash.inl create mode 100644 thirdparty/glm/glm/gtx/integer.hpp create mode 100644 thirdparty/glm/glm/gtx/integer.inl create mode 100644 thirdparty/glm/glm/gtx/intersect.hpp create mode 100644 thirdparty/glm/glm/gtx/intersect.inl create mode 100644 thirdparty/glm/glm/gtx/io.hpp create mode 100644 thirdparty/glm/glm/gtx/io.inl create mode 100644 thirdparty/glm/glm/gtx/log_base.hpp create mode 100644 thirdparty/glm/glm/gtx/log_base.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_cross_product.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_cross_product.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_decompose.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_decompose.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_factorisation.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_factorisation.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_interpolation.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_interpolation.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_major_storage.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_major_storage.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_operation.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_operation.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_query.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_query.inl create mode 100644 thirdparty/glm/glm/gtx/matrix_transform_2d.hpp create mode 100644 thirdparty/glm/glm/gtx/matrix_transform_2d.inl create mode 100644 thirdparty/glm/glm/gtx/mixed_product.hpp create mode 100644 thirdparty/glm/glm/gtx/mixed_product.inl create mode 100644 thirdparty/glm/glm/gtx/norm.hpp create mode 100644 thirdparty/glm/glm/gtx/norm.inl create mode 100644 thirdparty/glm/glm/gtx/normal.hpp create mode 100644 thirdparty/glm/glm/gtx/normal.inl create mode 100644 thirdparty/glm/glm/gtx/normalize_dot.hpp create mode 100644 thirdparty/glm/glm/gtx/normalize_dot.inl create mode 100644 thirdparty/glm/glm/gtx/number_precision.hpp create mode 100644 thirdparty/glm/glm/gtx/optimum_pow.hpp create mode 100644 thirdparty/glm/glm/gtx/optimum_pow.inl create mode 100644 thirdparty/glm/glm/gtx/orthonormalize.hpp create mode 100644 thirdparty/glm/glm/gtx/orthonormalize.inl create mode 100644 thirdparty/glm/glm/gtx/pca.hpp create mode 100644 thirdparty/glm/glm/gtx/pca.inl create mode 100644 thirdparty/glm/glm/gtx/perpendicular.hpp create mode 100644 thirdparty/glm/glm/gtx/perpendicular.inl create mode 100644 thirdparty/glm/glm/gtx/polar_coordinates.hpp create mode 100644 thirdparty/glm/glm/gtx/polar_coordinates.inl create mode 100644 thirdparty/glm/glm/gtx/projection.hpp create mode 100644 thirdparty/glm/glm/gtx/projection.inl create mode 100644 thirdparty/glm/glm/gtx/quaternion.hpp create mode 100644 thirdparty/glm/glm/gtx/quaternion.inl create mode 100644 thirdparty/glm/glm/gtx/range.hpp create mode 100644 thirdparty/glm/glm/gtx/raw_data.hpp create mode 100644 thirdparty/glm/glm/gtx/raw_data.inl create mode 100644 thirdparty/glm/glm/gtx/rotate_normalized_axis.hpp create mode 100644 thirdparty/glm/glm/gtx/rotate_normalized_axis.inl create mode 100644 thirdparty/glm/glm/gtx/rotate_vector.hpp create mode 100644 thirdparty/glm/glm/gtx/rotate_vector.inl create mode 100644 thirdparty/glm/glm/gtx/scalar_multiplication.hpp create mode 100644 thirdparty/glm/glm/gtx/scalar_relational.hpp create mode 100644 thirdparty/glm/glm/gtx/scalar_relational.inl create mode 100644 thirdparty/glm/glm/gtx/spline.hpp create mode 100644 thirdparty/glm/glm/gtx/spline.inl create mode 100644 thirdparty/glm/glm/gtx/std_based_type.hpp create mode 100644 thirdparty/glm/glm/gtx/std_based_type.inl create mode 100644 thirdparty/glm/glm/gtx/string_cast.hpp create mode 100644 thirdparty/glm/glm/gtx/string_cast.inl create mode 100644 thirdparty/glm/glm/gtx/texture.hpp create mode 100644 thirdparty/glm/glm/gtx/texture.inl create mode 100644 thirdparty/glm/glm/gtx/transform.hpp create mode 100644 thirdparty/glm/glm/gtx/transform.inl create mode 100644 thirdparty/glm/glm/gtx/transform2.hpp create mode 100644 thirdparty/glm/glm/gtx/transform2.inl create mode 100644 thirdparty/glm/glm/gtx/type_aligned.hpp create mode 100644 thirdparty/glm/glm/gtx/type_aligned.inl create mode 100644 thirdparty/glm/glm/gtx/type_trait.hpp create mode 100644 thirdparty/glm/glm/gtx/type_trait.inl create mode 100644 thirdparty/glm/glm/gtx/vec_swizzle.hpp create mode 100644 thirdparty/glm/glm/gtx/vector_angle.hpp create mode 100644 thirdparty/glm/glm/gtx/vector_angle.inl create mode 100644 thirdparty/glm/glm/gtx/vector_query.hpp create mode 100644 thirdparty/glm/glm/gtx/vector_query.inl create mode 100644 thirdparty/glm/glm/gtx/wrap.hpp create mode 100644 thirdparty/glm/glm/gtx/wrap.inl create mode 100644 thirdparty/glm/glm/integer.hpp create mode 100644 thirdparty/glm/glm/mat2x2.hpp create mode 100644 thirdparty/glm/glm/mat2x3.hpp create mode 100644 thirdparty/glm/glm/mat2x4.hpp create mode 100644 thirdparty/glm/glm/mat3x2.hpp create mode 100644 thirdparty/glm/glm/mat3x3.hpp create mode 100644 thirdparty/glm/glm/mat3x4.hpp create mode 100644 thirdparty/glm/glm/mat4x2.hpp create mode 100644 thirdparty/glm/glm/mat4x3.hpp create mode 100644 thirdparty/glm/glm/mat4x4.hpp create mode 100644 thirdparty/glm/glm/matrix.hpp create mode 100644 thirdparty/glm/glm/packing.hpp create mode 100644 thirdparty/glm/glm/simd/common.h create mode 100644 thirdparty/glm/glm/simd/exponential.h create mode 100644 thirdparty/glm/glm/simd/geometric.h create mode 100644 thirdparty/glm/glm/simd/integer.h create mode 100644 thirdparty/glm/glm/simd/matrix.h create mode 100644 thirdparty/glm/glm/simd/neon.h create mode 100644 thirdparty/glm/glm/simd/packing.h create mode 100644 thirdparty/glm/glm/simd/platform.h create mode 100644 thirdparty/glm/glm/simd/trigonometric.h create mode 100644 thirdparty/glm/glm/simd/vector_relational.h create mode 100644 thirdparty/glm/glm/trigonometric.hpp create mode 100644 thirdparty/glm/glm/vec2.hpp create mode 100644 thirdparty/glm/glm/vec3.hpp create mode 100644 thirdparty/glm/glm/vec4.hpp create mode 100644 thirdparty/glm/glm/vector_relational.hpp create mode 100644 thirdparty/miniaudio/.gitignore create mode 100644 thirdparty/miniaudio/.gitmodules create mode 100644 thirdparty/miniaudio/CHANGES.md create mode 100644 thirdparty/miniaudio/CONTRIBUTING.md create mode 100644 thirdparty/miniaudio/LICENSE create mode 100644 thirdparty/miniaudio/README.md create mode 100644 thirdparty/miniaudio/examples/build/README.md create mode 100644 thirdparty/miniaudio/examples/custom_backend.c create mode 100644 thirdparty/miniaudio/examples/custom_decoder.c create mode 100644 thirdparty/miniaudio/examples/custom_decoder_engine.c create mode 100644 thirdparty/miniaudio/examples/data_source_chaining.c create mode 100644 thirdparty/miniaudio/examples/duplex_effect.c create mode 100644 thirdparty/miniaudio/examples/engine_advanced.c create mode 100644 thirdparty/miniaudio/examples/engine_effects.c create mode 100644 thirdparty/miniaudio/examples/engine_hello_world.c create mode 100644 thirdparty/miniaudio/examples/engine_sdl.c create mode 100644 thirdparty/miniaudio/examples/engine_steamaudio.c create mode 100644 thirdparty/miniaudio/examples/hilo_interop.c create mode 100644 thirdparty/miniaudio/examples/node_graph.c create mode 100644 thirdparty/miniaudio/examples/resource_manager.c create mode 100644 thirdparty/miniaudio/examples/resource_manager_advanced.c create mode 100644 thirdparty/miniaudio/examples/simple_capture.c create mode 100644 thirdparty/miniaudio/examples/simple_duplex.c create mode 100644 thirdparty/miniaudio/examples/simple_enumeration.c create mode 100644 thirdparty/miniaudio/examples/simple_loopback.c create mode 100644 thirdparty/miniaudio/examples/simple_looping.c create mode 100644 thirdparty/miniaudio/examples/simple_mixing.c create mode 100644 thirdparty/miniaudio/examples/simple_playback.c create mode 100644 thirdparty/miniaudio/examples/simple_playback_sine.c create mode 100644 thirdparty/miniaudio/extras/miniaudio_libopus.h create mode 100644 thirdparty/miniaudio/extras/miniaudio_libvorbis.h create mode 100644 thirdparty/miniaudio/extras/miniaudio_split/README.md create mode 100644 thirdparty/miniaudio/extras/miniaudio_split/miniaudio.c create mode 100644 thirdparty/miniaudio/extras/miniaudio_split/miniaudio.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_delay_node/ma_delay_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_reverb_node/verblib.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.h create mode 100644 thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node_example.c create mode 100644 thirdparty/miniaudio/extras/nodes/ma_vocoder_node/voclib.h create mode 100644 thirdparty/miniaudio/extras/osaudio/README.md create mode 100644 thirdparty/miniaudio/extras/osaudio/osaudio.h create mode 100644 thirdparty/miniaudio/extras/osaudio/osaudio_miniaudio.c create mode 100644 thirdparty/miniaudio/extras/osaudio/tests/osaudio_deviceio.c create mode 100644 thirdparty/miniaudio/extras/stb_vorbis.c create mode 100644 thirdparty/miniaudio/miniaudio.h create mode 100644 thirdparty/miniaudio/research/README.txt create mode 100644 thirdparty/miniaudio/resources/branding/icon-128x128.png create mode 100644 thirdparty/miniaudio/resources/branding/icon-256x256.png create mode 100644 thirdparty/miniaudio/resources/branding/icon-64x64.png create mode 100644 thirdparty/miniaudio/resources/branding/miniaudio_logo1.png create mode 100644 thirdparty/miniaudio/resources/branding/miniaudio_logo_400.png create mode 100644 thirdparty/miniaudio/tests/_build/README.md create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_f32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s16__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s24__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_u8__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_f32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_s16__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_s24__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_s32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_u8__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_f32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s16__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s24__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_u8__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_f32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s16__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s24__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_u8__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_f32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s16__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s24__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s32__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_u8__mono_8000.raw create mode 100644 thirdparty/miniaudio/tests/_build/res/sine_s16_mono_48000.wav create mode 100644 thirdparty/miniaudio/tests/test_automated/ma_test_automated.c create mode 100644 thirdparty/miniaudio/tests/test_automated/ma_test_automated_data_converter.c create mode 100644 thirdparty/miniaudio/tests/test_common/ma_test_common.c create mode 100644 thirdparty/miniaudio/tests/test_cpp/ma_test_cpp.cpp create mode 100644 thirdparty/miniaudio/tests/test_deviceio/ma_test_deviceio.c create mode 100644 thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.c create mode 100644 thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.html create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_bpf.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_dithering.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hishelf.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hpf.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_loshelf.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_lpf.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_notch.c create mode 100644 thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_peak.c create mode 100644 thirdparty/miniaudio/tests/test_generation/ma_test_generation.c create mode 100644 thirdparty/miniaudio/tests/test_generation/ma_test_generation_noise.c create mode 100644 thirdparty/miniaudio/tests/test_generation/ma_test_generation_waveform.c create mode 100644 thirdparty/miniaudio/tools/audioconverter/audioconverter.c create mode 100644 thirdparty/miniaudio/website/.webplate/config.webplate create mode 100644 thirdparty/miniaudio/website/.webplate/miniaudio-footer.html create mode 100644 thirdparty/miniaudio/website/.webplate/miniaudio-header.html create mode 100644 thirdparty/miniaudio/website/CNAME create mode 100644 thirdparty/miniaudio/website/css/main.css create mode 100644 thirdparty/miniaudio/website/img/Discord-Logo-White.svg create mode 100644 thirdparty/miniaudio/website/img/favicon.png create mode 100644 thirdparty/miniaudio/website/img/github_white.png create mode 100644 thirdparty/miniaudio/website/img/logo1_large.png create mode 100644 thirdparty/miniaudio/website/img/logo1_large_white.png create mode 100644 thirdparty/miniaudio/website/img/miniaudio_wide.png create mode 100644 thirdparty/miniaudio/website/img/platforms/android/android.svg create mode 100644 thirdparty/miniaudio/website/img/platforms/apple/apple-black.svg create mode 100644 thirdparty/miniaudio/website/img/platforms/bsd/freebsd.svg create mode 100644 thirdparty/miniaudio/website/img/platforms/linux/linux.svg create mode 100644 thirdparty/miniaudio/website/img/platforms/web/html5.svg create mode 100644 thirdparty/miniaudio/website/img/platforms/windows/windows-10.svg create mode 100644 thirdparty/miniaudio/website/img/twitter_white.png create mode 100644 thirdparty/miniaudio/website/index.html create mode 100644 thirdparty/minilua/.gitignore create mode 100644 thirdparty/minilua/LICENSE.txt create mode 100644 thirdparty/minilua/README.md create mode 100644 thirdparty/minilua/gen.sh create mode 100644 thirdparty/minilua/minilua.h create mode 100644 thirdparty/ode-0.16.5/CHANGELOG.txt create mode 100644 thirdparty/ode-0.16.5/CMakeLists.txt create mode 100644 thirdparty/ode-0.16.5/COPYING create mode 100644 thirdparty/ode-0.16.5/CSR.txt create mode 100644 thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-BSD.TXT create mode 100644 thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-LGPL.TXT create mode 100644 thirdparty/ode-0.16.5/GIMPACT/Makefile.am create mode 100644 thirdparty/ode-0.16.5/GIMPACT/Makefile.in create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.am create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.in create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_boxpruning.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_contact.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_geometry.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_math.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_memory.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_radixsort.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_capsule_collision.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_collision.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_sphere_collision.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_trimesh.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gimpact.h create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/Makefile.am create mode 100644 thirdparty/ode-0.16.5/GIMPACT/include/Makefile.in create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/Makefile.am create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/Makefile.in create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_boxpruning.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_contact.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_math.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_memory.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_tri_tri_overlap.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_capsule_collision.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_ray_collision.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_sphere_collision.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_trimesh_collision.cpp create mode 100644 thirdparty/ode-0.16.5/GIMPACT/src/gimpact.cpp create mode 100644 thirdparty/ode-0.16.5/INSTALL.txt create mode 100644 thirdparty/ode-0.16.5/LICENSE-BSD.TXT create mode 100644 thirdparty/ode-0.16.5/LICENSE.TXT create mode 100644 thirdparty/ode-0.16.5/Makefile.am create mode 100644 thirdparty/ode-0.16.5/Makefile.in create mode 100644 thirdparty/ode-0.16.5/OPCODE/COPYING create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceAxes.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceBoundingSphere.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceContainer.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceContainer.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceFPU.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceLSS.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceMemoryMacros.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePairs.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IcePreprocessor.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceTriList.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceTypes.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceUtils.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/IceUtils.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.am create mode 100644 thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.in create mode 100644 thirdparty/ode-0.16.5/OPCODE/Makefile.am create mode 100644 thirdparty/ode-0.16.5/OPCODE/Makefile.in create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_BoxBoxOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Collider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Collider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Common.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Common.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_HybridModel.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_HybridModel.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_IceHook.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_LSSAABBOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_LSSCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_LSSCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_LSSTriOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_MeshInterface.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_MeshInterface.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Model.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Model.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Picking.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Picking.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_PlanesAABBOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_PlanesCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_PlanesCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_PlanesTriOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_RayAABBOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_RayTriOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_Settings.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_SphereAABBOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_SphereTriOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TreeBuilders.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TreeBuilders.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TreeCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TreeCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TriBoxOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_TriTriOverlap.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_VolumeCollider.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/OPC_VolumeCollider.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/Opcode.cpp create mode 100644 thirdparty/ode-0.16.5/OPCODE/Opcode.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/README-ODE.txt create mode 100644 thirdparty/ode-0.16.5/OPCODE/ReadMe.txt create mode 100644 thirdparty/ode-0.16.5/OPCODE/Stdafx.h create mode 100644 thirdparty/ode-0.16.5/OPCODE/TemporalCoherence.txt create mode 100644 thirdparty/ode-0.16.5/README.md create mode 100644 thirdparty/ode-0.16.5/aclocal.m4 create mode 100644 thirdparty/ode-0.16.5/bindings/python/INSTALL.txt create mode 100644 thirdparty/ode-0.16.5/bindings/python/TODO.txt create mode 100644 thirdparty/ode-0.16.5/bindings/python/demos/tutorial1.py create mode 100644 thirdparty/ode-0.16.5/bindings/python/demos/tutorial2.py create mode 100644 thirdparty/ode-0.16.5/bindings/python/demos/tutorial3.py create mode 100644 thirdparty/ode-0.16.5/bindings/python/ode.pxd create mode 100644 thirdparty/ode-0.16.5/bindings/python/ode.pyx create mode 100644 thirdparty/ode-0.16.5/bindings/python/setup.py create mode 100644 thirdparty/ode-0.16.5/bootstrap create mode 100644 thirdparty/ode-0.16.5/build/config-default.h create mode 100644 thirdparty/ode-0.16.5/build/premake4.exe create mode 100644 thirdparty/ode-0.16.5/build/premake4.lua create mode 100644 thirdparty/ode-0.16.5/cmake/cmake_uninstall.cmake.in create mode 100644 thirdparty/ode-0.16.5/compile create mode 100644 thirdparty/ode-0.16.5/config.guess create mode 100644 thirdparty/ode-0.16.5/config.h.cmake.in create mode 100644 thirdparty/ode-0.16.5/config.sub create mode 100644 thirdparty/ode-0.16.5/configure create mode 100644 thirdparty/ode-0.16.5/configure.ac create mode 100644 thirdparty/ode-0.16.5/depcomp create mode 100644 thirdparty/ode-0.16.5/drawstuff/Makefile.am create mode 100644 thirdparty/ode-0.16.5/drawstuff/Makefile.in create mode 100644 thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.am create mode 100644 thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.in create mode 100644 thirdparty/ode-0.16.5/drawstuff/dstest/dstest.cpp create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/Makefile.am create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/Makefile.in create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/drawstuff.cpp create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/internal.h create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/osx.cpp create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/resource.h create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/resources.rc create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/windows.cpp create mode 100644 thirdparty/ode-0.16.5/drawstuff/src/x11.cpp create mode 100644 thirdparty/ode-0.16.5/drawstuff/textures/checkered.ppm create mode 100644 thirdparty/ode-0.16.5/drawstuff/textures/ground.ppm create mode 100644 thirdparty/ode-0.16.5/drawstuff/textures/sky.ppm create mode 100644 thirdparty/ode-0.16.5/drawstuff/textures/wood.ppm create mode 100644 thirdparty/ode-0.16.5/include/Makefile.am create mode 100644 thirdparty/ode-0.16.5/include/Makefile.in create mode 100644 thirdparty/ode-0.16.5/include/drawstuff/Makefile.am create mode 100644 thirdparty/ode-0.16.5/include/drawstuff/Makefile.in create mode 100644 thirdparty/ode-0.16.5/include/drawstuff/drawstuff.h create mode 100644 thirdparty/ode-0.16.5/include/drawstuff/version.h create mode 100644 thirdparty/ode-0.16.5/include/ode/Makefile.am create mode 100644 thirdparty/ode-0.16.5/include/ode/Makefile.in create mode 100644 thirdparty/ode-0.16.5/include/ode/README create mode 100644 thirdparty/ode-0.16.5/include/ode/collision.h create mode 100644 thirdparty/ode-0.16.5/include/ode/collision_space.h create mode 100644 thirdparty/ode-0.16.5/include/ode/collision_trimesh.h create mode 100644 thirdparty/ode-0.16.5/include/ode/common.h create mode 100644 thirdparty/ode-0.16.5/include/ode/compatibility.h create mode 100644 thirdparty/ode-0.16.5/include/ode/contact.h create mode 100644 thirdparty/ode-0.16.5/include/ode/cooperative.h create mode 100644 thirdparty/ode-0.16.5/include/ode/error.h create mode 100644 thirdparty/ode-0.16.5/include/ode/export-dif.h create mode 100644 thirdparty/ode-0.16.5/include/ode/mass.h create mode 100644 thirdparty/ode-0.16.5/include/ode/matrix.h create mode 100644 thirdparty/ode-0.16.5/include/ode/matrix_coop.h create mode 100644 thirdparty/ode-0.16.5/include/ode/memory.h create mode 100644 thirdparty/ode-0.16.5/include/ode/misc.h create mode 100644 thirdparty/ode-0.16.5/include/ode/objects.h create mode 100644 thirdparty/ode-0.16.5/include/ode/ode.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odeconfig.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odecpp.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odecpp_collision.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odeinit.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odemath.h create mode 100644 thirdparty/ode-0.16.5/include/ode/odemath_legacy.h create mode 100644 thirdparty/ode-0.16.5/include/ode/precision.h create mode 100644 thirdparty/ode-0.16.5/include/ode/precision.h.in create mode 100644 thirdparty/ode-0.16.5/include/ode/rotation.h create mode 100644 thirdparty/ode-0.16.5/include/ode/threading.h create mode 100644 thirdparty/ode-0.16.5/include/ode/threading_impl.h create mode 100644 thirdparty/ode-0.16.5/include/ode/timer.h create mode 100644 thirdparty/ode-0.16.5/include/ode/version.h create mode 100644 thirdparty/ode-0.16.5/include/ode/version.h.in create mode 100644 thirdparty/ode-0.16.5/install-sh create mode 100644 thirdparty/ode-0.16.5/libccd/BSD-LICENSE create mode 100644 thirdparty/ode-0.16.5/libccd/Makefile.am create mode 100644 thirdparty/ode-0.16.5/libccd/Makefile.in create mode 100644 thirdparty/ode-0.16.5/libccd/README create mode 100644 thirdparty/ode-0.16.5/libccd/aclocal.m4 create mode 100644 thirdparty/ode-0.16.5/libccd/bootstrap create mode 100644 thirdparty/ode-0.16.5/libccd/configure create mode 100644 thirdparty/ode-0.16.5/libccd/configure.ac create mode 100644 thirdparty/ode-0.16.5/libccd/src/Makefile.am create mode 100644 thirdparty/ode-0.16.5/libccd/src/Makefile.in create mode 100644 thirdparty/ode-0.16.5/libccd/src/alloc.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/alloc.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/ccd.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/compiler.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/dbg.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/list.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/polytope.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/precision.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/precision.h.in create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/quat.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/simplex.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/support.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/ccd/vec3.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/config.h.in create mode 100644 thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/quat.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/vec3.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/mpr.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/polytope.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/support.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.am create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.in create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/bench.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/bench2.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/common.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/common.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING.LESSER create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.am create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.in create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/main.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/support.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/support.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.c create mode 100644 thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.h create mode 100644 thirdparty/ode-0.16.5/libccd/src/vec3.c create mode 100644 thirdparty/ode-0.16.5/ltmain.sh create mode 100644 thirdparty/ode-0.16.5/m4/libtool.m4 create mode 100644 thirdparty/ode-0.16.5/m4/ltoptions.m4 create mode 100644 thirdparty/ode-0.16.5/m4/ltsugar.m4 create mode 100644 thirdparty/ode-0.16.5/m4/ltversion.m4 create mode 100644 thirdparty/ode-0.16.5/m4/lt~obsolete.m4 create mode 100644 thirdparty/ode-0.16.5/m4/pkg.m4 create mode 100644 thirdparty/ode-0.16.5/missing create mode 100644 thirdparty/ode-0.16.5/ode-config.cmake.in create mode 100644 thirdparty/ode-0.16.5/ode-config.in create mode 100644 thirdparty/ode-0.16.5/ode.pc.in create mode 100644 thirdparty/ode-0.16.5/ode/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ode/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ode/README create mode 100644 thirdparty/ode-0.16.5/ode/TODO create mode 100644 thirdparty/ode-0.16.5/ode/demo/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ode/demo/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ode/demo/basket_geom.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/bunny_geom.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/convex_bunny_geom.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/convex_prism.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_I.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_basket.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_boxstack.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_buggy.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_cards.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_chain1.c create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_chain2.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_collision.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_convex.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_crash.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_cyl.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_cylvssphere.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_dball.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_dhinge.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_feedback.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_friction.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_gyro2.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_gyroscopic.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_heightfield.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_hinge.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_jointPR.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_jointPU.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_joints.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_kinematic.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_motion.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_motor.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_moving_convex.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_moving_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_ode.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_piston.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_plane2d.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_rfriction.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_slider.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_space.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_space_stress.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_step.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_tracks.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_transmission.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/demo_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/ode/demo/halton235_geom.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/icosahedron_geom.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/texturepath.h create mode 100644 thirdparty/ode-0.16.5/ode/demo/world_geom3.h create mode 100644 thirdparty/ode-0.16.5/ode/doc/Doxyfile.in create mode 100644 thirdparty/ode-0.16.5/ode/doc/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ode/doc/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ode/doc/main.dox create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/amotor.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/ball-and-socket-bad.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/ball-and-socket.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/body.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/contact.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/hinge.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/hinge2.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/joints.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/rollingcontact.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/sf-graph1.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/sf-graph2.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/slider.jpg create mode 100644 thirdparty/ode-0.16.5/ode/doc/pix/universal.jpg create mode 100644 thirdparty/ode-0.16.5/ode/src/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ode/src/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ode/src/array.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/array.h create mode 100644 thirdparty/ode-0.16.5/ode/src/box.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/capsule.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_convex_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_cylinder_box.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_cylinder_plane.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_cylinder_sphere.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_cylinder_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_kernel.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_kernel.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_libccd.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_libccd.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_quadtreespace.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_sapspace.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_space.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_space_internal.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_std.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_transform.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_transform.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_box.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_ccylinder.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_colliders.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_disabled.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_gimpact.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_gimpact.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_internal.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_internal.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_internal_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_opcode.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_opcode.h create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_plane.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_ray.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_sphere.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_trimesh.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_trimesh_trimesh_old.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_util.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/collision_util.h create mode 100644 thirdparty/ode-0.16.5/ode/src/common.h create mode 100644 thirdparty/ode-0.16.5/ode/src/config.h create mode 100644 thirdparty/ode-0.16.5/ode/src/config.h.in create mode 100644 thirdparty/ode-0.16.5/ode/src/convex.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/coop_matrix_types.h create mode 100644 thirdparty/ode-0.16.5/ode/src/cylinder.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/default_threading.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/default_threading.h create mode 100644 thirdparty/ode-0.16.5/ode/src/error.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/error.h create mode 100644 thirdparty/ode-0.16.5/ode/src/export-dif.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastdot.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastdot_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/fastldltfactor.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastldltfactor_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/fastldltsolve.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastldltsolve_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/fastlsolve.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastlsolve_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/fastltsolve.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastltsolve_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/fastvecscale.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/fastvecscale_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/gimpact_contact_export_helper.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/gimpact_contact_export_helper.h create mode 100644 thirdparty/ode-0.16.5/ode/src/gimpact_gim_contact_accessor.h create mode 100644 thirdparty/ode-0.16.5/ode/src/gimpact_plane_contact_accessor.h create mode 100644 thirdparty/ode-0.16.5/ode/src/heightfield.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/heightfield.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/amotor.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/amotor.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/ball.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/ball.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/contact.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/contact.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/dball.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/dball.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/dhinge.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/dhinge.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/fixed.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/fixed.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/hinge.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/hinge.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/hinge2.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/hinge2.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/joint.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/joint.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/joint_internal.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/joints.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/lmotor.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/lmotor.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/null.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/null.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/piston.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/piston.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/plane2d.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/plane2d.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/pr.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/pr.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/pu.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/pu.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/slider.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/slider.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/transmission.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/transmission.h create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/universal.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/joints/universal.h create mode 100644 thirdparty/ode-0.16.5/ode/src/lcp.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/lcp.h create mode 100644 thirdparty/ode-0.16.5/ode/src/mass.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/mat.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/mat.h create mode 100644 thirdparty/ode-0.16.5/ode/src/matrix.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/matrix.h create mode 100644 thirdparty/ode-0.16.5/ode/src/memory.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/misc.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/nextafterf.c create mode 100644 thirdparty/ode-0.16.5/ode/src/objects.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/objects.h create mode 100644 thirdparty/ode-0.16.5/ode/src/obstack.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/obstack.h create mode 100644 thirdparty/ode-0.16.5/ode/src/ode.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/odeinit.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/odemath.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/odemath.h create mode 100644 thirdparty/ode-0.16.5/ode/src/odeou.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/odeou.h create mode 100644 thirdparty/ode-0.16.5/ode/src/odetls.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/odetls.h create mode 100644 thirdparty/ode-0.16.5/ode/src/plane.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/quickstep.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/quickstep.h create mode 100644 thirdparty/ode-0.16.5/ode/src/ray.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/resource_control.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/resource_control.h create mode 100644 thirdparty/ode-0.16.5/ode/src/rotation.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/simple_cooperative.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/simple_cooperative.h create mode 100644 thirdparty/ode-0.16.5/ode/src/sphere.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/step.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/step.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threaded_solver_ldlt.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_atomics_provs.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_base.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_base.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_fake_sync.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_impl.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_impl.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_impl_posix.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_impl_templates.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_impl_win.h create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_pool_posix.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/threading_pool_win.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/threadingutils.h create mode 100644 thirdparty/ode-0.16.5/ode/src/timer.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/typedefs.h create mode 100644 thirdparty/ode-0.16.5/ode/src/util.cpp create mode 100644 thirdparty/ode-0.16.5/ode/src/util.h create mode 100644 thirdparty/ode-0.16.5/ou/CHANGELOG.TXT create mode 100644 thirdparty/ode-0.16.5/ou/INSTALL.TXT create mode 100644 thirdparty/ode-0.16.5/ou/LICENSE-BSD.TXT create mode 100644 thirdparty/ode-0.16.5/ou/LICENSE-LESSER.TXT create mode 100644 thirdparty/ode-0.16.5/ou/LICENSE-ZLIB.TXT create mode 100644 thirdparty/ode-0.16.5/ou/LICENSE.TXT create mode 100644 thirdparty/ode-0.16.5/ou/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ou/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ou/README.TXT create mode 100644 thirdparty/ode-0.16.5/ou/aclocal.m4 create mode 100644 thirdparty/ode-0.16.5/ou/bootstrap create mode 100644 thirdparty/ode-0.16.5/ou/build/make/makefile create mode 100644 thirdparty/ode-0.16.5/ou/build/make/makefile.os create mode 100644 thirdparty/ode-0.16.5/ou/build/vs2005/ou.sln create mode 100644 thirdparty/ode-0.16.5/ou/build/vs2005/ou.vcproj create mode 100644 thirdparty/ode-0.16.5/ou/build/vs6/ou.dsp create mode 100644 thirdparty/ode-0.16.5/ou/build/vs6/ou.dsw create mode 100644 thirdparty/ode-0.16.5/ou/configure create mode 100644 thirdparty/ode-0.16.5/ou/configure.ac create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/assert.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/atomic.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/atomicflags.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/customization.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/enumarrays.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/features.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/flags.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/flagsdefines.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/inttypes.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/macros.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/malloc.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/namespace.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/platform.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/simpleflags.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/templates.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/threadlocalstorage.h create mode 100644 thirdparty/ode-0.16.5/ou/include/ou/typewrapper.h create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/atomic.cpp create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/customization.cpp create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/malloc.cpp create mode 100644 thirdparty/ode-0.16.5/ou/src/ou/threadlocalstorage.cpp create mode 100644 thirdparty/ode-0.16.5/ou/test/Makefile.am create mode 100644 thirdparty/ode-0.16.5/ou/test/Makefile.in create mode 100644 thirdparty/ode-0.16.5/ou/test/outest.cpp create mode 100644 thirdparty/ode-0.16.5/test-driver create mode 100644 thirdparty/ode-0.16.5/tests/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/COPYING create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/README create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/docs/UnitTest++.html create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/AssertException.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/AssertException.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/CheckMacros.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Checks.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Checks.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Config.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/DeferredTestReporter.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/DeferredTestReporter.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/DeferredTestResult.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/DeferredTestResult.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/MemoryOutStream.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/MemoryOutStream.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/SignalTranslator.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/SignalTranslator.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/TimeHelpers.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Posix/TimeHelpers.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/ReportAssert.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/ReportAssert.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Test.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Test.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestDetails.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestDetails.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestList.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestList.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestMacros.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestReporter.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestReporter.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestReporterStdout.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestReporterStdout.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestResults.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestResults.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestRunner.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestRunner.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TestSuite.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TimeConstraint.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TimeConstraint.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/TimeHelpers.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/UnitTest++.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Win32/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Win32/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Win32/TimeHelpers.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/Win32/TimeHelpers.h create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/XmlTestReporter.cpp create mode 100644 thirdparty/ode-0.16.5/tests/UnitTest++/src/XmlTestReporter.h create mode 100644 thirdparty/ode-0.16.5/tests/collision.cpp create mode 100644 thirdparty/ode-0.16.5/tests/friction.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joint.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/Makefile.am create mode 100644 thirdparty/ode-0.16.5/tests/joints/Makefile.in create mode 100644 thirdparty/ode-0.16.5/tests/joints/amotor.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/ball.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/dball.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/fixed.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/hinge.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/hinge2.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/piston.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/pr.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/pu.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/slider.cpp create mode 100644 thirdparty/ode-0.16.5/tests/joints/universal.cpp create mode 100644 thirdparty/ode-0.16.5/tests/main.cpp create mode 100644 thirdparty/ode-0.16.5/tests/odemath.cpp create mode 100644 thirdparty/ode-0.16.5/tools/README.txt create mode 100644 thirdparty/ode-0.16.5/tools/msw-release.bat create mode 100644 thirdparty/ode-0.16.5/tools/ode_convex_export.py create mode 100644 thirdparty/ode-0.16.5/tools/src-release.sh create mode 100644 thirdparty/stb/.gitignore create mode 100644 thirdparty/stb/.travis.yml create mode 100644 thirdparty/stb/LICENSE create mode 100644 thirdparty/stb/README.md create mode 100644 thirdparty/stb/deprecated/rrsprintf.h create mode 100644 thirdparty/stb/deprecated/stb.h create mode 100644 thirdparty/stb/deprecated/stb_image.c create mode 100644 thirdparty/stb/deprecated/stb_image_resize.h create mode 100644 thirdparty/stb/deprecated/stretch_test.c create mode 100644 thirdparty/stb/deprecated/stretchy_buffer.h create mode 100644 thirdparty/stb/deprecated/stretchy_buffer.txt create mode 100644 thirdparty/stb/docs/other_libs.md create mode 100644 thirdparty/stb/docs/stb_howto.txt create mode 100644 thirdparty/stb/docs/stb_voxel_render_interview.md create mode 100644 thirdparty/stb/docs/why_public_domain.md create mode 100644 thirdparty/stb/stb_c_lexer.h create mode 100644 thirdparty/stb/stb_connected_components.h create mode 100644 thirdparty/stb/stb_divide.h create mode 100644 thirdparty/stb/stb_ds.h create mode 100644 thirdparty/stb/stb_dxt.h create mode 100644 thirdparty/stb/stb_easy_font.h create mode 100644 thirdparty/stb/stb_herringbone_wang_tile.h create mode 100644 thirdparty/stb/stb_hexwave.h create mode 100644 thirdparty/stb/stb_image.h create mode 100644 thirdparty/stb/stb_image_resize2.h create mode 100644 thirdparty/stb/stb_image_resize_test/dotimings.c create mode 100644 thirdparty/stb/stb_image_resize_test/old_image_resize.h create mode 100644 thirdparty/stb/stb_image_resize_test/oldir.c create mode 100644 thirdparty/stb/stb_image_resize_test/stbirtest.c create mode 100644 thirdparty/stb/stb_image_resize_test/vf_train.c create mode 100644 thirdparty/stb/stb_image_write.h create mode 100644 thirdparty/stb/stb_include.h create mode 100644 thirdparty/stb/stb_leakcheck.h create mode 100644 thirdparty/stb/stb_perlin.h create mode 100644 thirdparty/stb/stb_rect_pack.h create mode 100644 thirdparty/stb/stb_sprintf.h create mode 100644 thirdparty/stb/stb_textedit.h create mode 100644 thirdparty/stb/stb_tilemap_editor.h create mode 100644 thirdparty/stb/stb_truetype.h create mode 100644 thirdparty/stb/stb_vorbis.c create mode 100644 thirdparty/stb/stb_voxel_render.h create mode 100644 thirdparty/stb/tools/README.footer.md create mode 100644 thirdparty/stb/tools/README.header.md create mode 100644 thirdparty/stb/tools/README.list create mode 100644 thirdparty/stb/tools/build_matrix.c create mode 100644 thirdparty/stb/tools/easy_font_maker.c create mode 100644 thirdparty/stb/tools/make_readme.c create mode 100644 thirdparty/stb/tools/make_readme.dsp create mode 100644 thirdparty/stb/tools/mr.bat create mode 100644 thirdparty/stb/tools/trailing_whitespace.c create mode 100644 thirdparty/stb/tools/unicode.c create mode 100644 thirdparty/stb/tools/unicode/unicode.dsp create mode 100644 tools/premake5.exe diff --git a/content/editor/entities.lua b/content/editor/entities.lua new file mode 100644 index 0000000..1ddd646 --- /dev/null +++ b/content/editor/entities.lua @@ -0,0 +1,15 @@ + +game_object = { + { PROPERTY_STRING, "name", "Name of the object." } , + { PROPERTY_STRING_NOT_EDITABLE, "classname", "Clasname." }, + { PROPERTY_VEC3, "position", "World position." }, + { PROPERTY_VEC3, "rotation", "World rotation." }, + { PROPERTY_VEC3, "scale", "World scale." }, +} + + +editor_entities = { + + { game_object, "game_object" }, + +} \ No newline at end of file diff --git a/content/levels/playertest.txt b/content/levels/playertest.txt new file mode 100644 index 0000000..80bb566 --- /dev/null +++ b/content/levels/playertest.txt @@ -0,0 +1,24 @@ +BeginEntity test_entity +ClassName Player +Position 0.0 0.0 0.0 +Rotation 0.0 0.0 0.0 +EndEntity + +# Water +BeginEntity water_test +ClassName GameObject +Tag Water +Position 0.0 -0.1 0.0 +Rotation 0.0 0.0 0.0 +Visual content/test/water_test.obj +EndEntity + +############# +# Enemies ... + +BeginEntity test_enemy +ClassName GameObject +Position -2.70 0.00 3.28 +Rotation 0.0 0.0 0.0 +Visual content/test/ship.obj +EndEntity diff --git a/content/shaders/debug_draw.ps b/content/shaders/debug_draw.ps new file mode 100644 index 0000000..39d1428 --- /dev/null +++ b/content/shaders/debug_draw.ps @@ -0,0 +1,10 @@ +#version 330 core + +in vec3 v_color; + +out vec4 fragColor; + +void main() +{ + fragColor = vec4(v_color, 1.0); +} \ No newline at end of file diff --git a/content/shaders/debug_draw.vs b/content/shaders/debug_draw.vs new file mode 100644 index 0000000..f19b0cd --- /dev/null +++ b/content/shaders/debug_draw.vs @@ -0,0 +1,15 @@ +#version 330 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec3 color; + +out vec3 v_color; + +uniform mat4 u_modelViewProjection; + +void main() +{ + gl_Position = u_modelViewProjection * vec4(position, 1.0f); + + v_color = color; +} \ No newline at end of file diff --git a/content/shaders/lit_generic.ps b/content/shaders/lit_generic.ps new file mode 100644 index 0000000..44dda2c --- /dev/null +++ b/content/shaders/lit_generic.ps @@ -0,0 +1,15 @@ +#version 120 + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_texcoord; +varying vec3 v_finalColor; + +uniform sampler2D u_albedoTexture; +uniform vec4 u_customColor; + +void main() { + //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = u_customColor * vec4(v_finalColor, 1.0) * texture2D(u_albedoTexture, v_texcoord); + //gl_FragColor = vec4( v_normal, 1.0 ); +} \ No newline at end of file diff --git a/content/shaders/lit_generic.vs b/content/shaders/lit_generic.vs new file mode 100644 index 0000000..6b1ca53 --- /dev/null +++ b/content/shaders/lit_generic.vs @@ -0,0 +1,46 @@ +#version 120 + +attribute vec3 a_position; +attribute vec3 a_normal; +attribute vec2 a_texcoord; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_texcoord; +varying vec3 v_finalColor; + +uniform mat4 u_modelMatrix; +uniform mat4 u_viewMatrix; +uniform mat4 u_projectionMatrix; +uniform mat4 u_modelViewProjection; + +vec3 CalcOmniLight() +{ + vec3 lightPos = vec3(0.1, 2.1, 0.1); + float d = distance(lightPos, v_position); + vec3 L = normalize(lightPos-v_position); + vec3 N = normalize(v_normal); + vec3 col = vec3( max(0, dot(N, L) / d) ); + col = col * 0.8 + 0.2; + return col; +} + +vec3 CalcDirLight() +{ + vec3 lightPos = vec3(5.0, 10.0, 1.0); + //lightPos = -lightPos; + + vec3 L = normalize(lightPos); + vec3 N = normalize(v_normal); + vec3 col = vec3( max(0, dot(N, L)) ); + col = col * 0.8 + 0.2; + return col; +} + +void main() { + v_position = vec3( u_modelMatrix * vec4(a_position, 1.0) ); + v_normal = vec3( mat3(u_modelMatrix) * a_normal ); + v_texcoord = a_texcoord; + v_finalColor = CalcDirLight(); + gl_Position = u_modelViewProjection * vec4(a_position, 1); +} \ No newline at end of file diff --git a/content/shaders/lit_generic_texcoordgen.ps b/content/shaders/lit_generic_texcoordgen.ps new file mode 100644 index 0000000..44dda2c --- /dev/null +++ b/content/shaders/lit_generic_texcoordgen.ps @@ -0,0 +1,15 @@ +#version 120 + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_texcoord; +varying vec3 v_finalColor; + +uniform sampler2D u_albedoTexture; +uniform vec4 u_customColor; + +void main() { + //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragColor = u_customColor * vec4(v_finalColor, 1.0) * texture2D(u_albedoTexture, v_texcoord); + //gl_FragColor = vec4( v_normal, 1.0 ); +} \ No newline at end of file diff --git a/content/shaders/ui_base.ps b/content/shaders/ui_base.ps new file mode 100644 index 0000000..11eda6b --- /dev/null +++ b/content/shaders/ui_base.ps @@ -0,0 +1,8 @@ +#version 120 + +varying vec2 v_texcoord; +varying vec4 v_color; + +void main() { + gl_FragColor = v_color; +} \ No newline at end of file diff --git a/content/shaders/ui_base.vs b/content/shaders/ui_base.vs new file mode 100644 index 0000000..75f907b --- /dev/null +++ b/content/shaders/ui_base.vs @@ -0,0 +1,16 @@ +#version 120 + +attribute vec2 a_position; +attribute vec2 a_texcoord; +attribute vec4 a_color; + +varying vec2 v_texcoord; +varying vec4 v_color; + +uniform mat4 u_projectionMatrix; + +void main() { + v_texcoord = a_texcoord; + v_color = a_color; + gl_Position = u_projectionMatrix * vec4(a_position.xy,0,1); +} \ No newline at end of file diff --git a/content/shaders/ui_tex.ps b/content/shaders/ui_tex.ps new file mode 100644 index 0000000..f216b62 --- /dev/null +++ b/content/shaders/ui_tex.ps @@ -0,0 +1,11 @@ +#version 120 + +varying vec2 v_texcoord; +varying vec4 v_color; + +uniform sampler2D u_texture; + +void main() { + vec4 tex = texture2D( u_texture, v_texcoord ); + gl_FragColor = v_color * tex; +} \ No newline at end of file diff --git a/content/test/ship.blend b/content/test/ship.blend new file mode 100644 index 0000000000000000000000000000000000000000..ed05f69a914337367fe36c09e31deb0898a0df71 GIT binary patch literal 993328 zcmeEP3t$z+)!rnKtcdbb2q+SiYJ6b`Ac068B)|nh5Gc~t8bSyp6$lL=kG2xEk8N!$ zwfz+zRf?@fZEJm_(u7y*wd(JqzZxx8e6^|8)~Z!%Tl1eYbH2Ne?B3jjAmL^QZf0la zoHKLIH?N(YotZRa@_D6`=bU-Tn4FwBbQv)WW7BhGS8gSrU5flZCVy^4<%&wa*+DmN z^4xhQNsO4oPH1Amg6S|GGQXo1iIp#?$T(?}y3un2VsEf88Dv_NQq&;p?aLJNcz2rUp=AhbYe zfzSe>1wspi76>g6S|GGQXo1iIp#?$Se*JDf?zrPxs0_56 zFzkeu;lqc2LgB}bI_jt|&=}Y3CT37-cE>UO;K4)o9e4ckEyvUEGoKmJa>^;Ew48L( zNi8Rycw)=2VZ)lKeVEvt%0PG0wP(-tA)|9gw~QS-wk1D5zooFSsHJ#PQOh|K&uKYp zMsR!Z64@l|50bE8q^6~1Oq(`s|Mcn8Th2T0yq0s%J-21Xj2X%=EiG*+EGTHn z&CUIU+JG~XQ2kIPvcL%^obdVS)2F>uSXj`KGb*Pg|D61mqN2i zToM{9c`cBdnwm!Ay~z_MOlTf4Vnho)2dLlp@#9-&QM-_nlk=nG)jrgWERdR>o__AJ z1CLd>A4~WTIA#Ei2S0PysGLzh8aniZ&xTe?UJKB3PoEw=dQ3cK0ONj43*&$6vB$Ke zr}r#OUadpToEFH)$hh#B0RvhFP#HMpSltdBal{eJh$h2H7hWf)1qkaU-0u&dGN63A z?%lihwbVBBPEL(Ot(+F1{y(DXKH=WKf4`TgJ-C3d@9U%quanaPgmZtw`}0R1ee^9E z879yBhIKwUp^m3jYP`rWu6tR4+JNq*aHwr)fzSe>1wspi76>g6S|GGQXo1iIp#?$< zgcb-b5LzI#K$o^aIA7JJ;R!PrS|GH*VQzu2jXlf(46`3vAhbYefzSe>1wspi76>g6 zS|GGQXo1iIp#?$1wspi76>iSB`nYx>vB6oMwc)~n2FE=p#?$Wo>FX7@0R*-w{Uxs~{Cp&y@yl$AC5>=8z8%26WA99dA^L=je<_RZt9 zFVTk4*HC9_9ru4LD*4{{MU`jgR_2XeG%j~sj3&GYEf89uTUy|PnTGMJo`zvYbh|Mp zO}8_N8smbQ7gVp9S-v7s`nKrOjI0<5HGds;*1~?eT@3I^P30o5Zu*L9KQDi91N&aL z;P?Hi_MRI_d-9kKsU8O;ge6yi2ZCE)cKt;&-duZ zadpfy5kK7XX)bJ$9lr9-OHA5Cy6W}9N#_{GhEiwxrN#4#tLO&Wy}3c{__SQz-b~Rx z1Y`V&@cin^wKK}sSJvW2JAKGAKQmLM*EB-=)=Ao5AijB`4o5DMcJPbR4$8Pz>zHlS zW&A5GB_)m8F|^#&Pf_HzC|@txw~Xjr#wu>7x81hP)h;$&uEX$~C+T+5ym5lcU;Pbk z->YtP$>mG^`Uf**xk`uapG8d2w@mCGIiXO=H$OU8`ChrqXVOi`Uqu~?)>%_RF6?)M z#Mg^Gz48p>kIEY010DF*2_NLy{QF&epaWk-_#h8_8#CN|paWl~@Iju<=gueSz}GD0 z1MlLc4T=R}SmU^R%Au771h1!wTe^%{O)tG6z-5oCNulUxvvvqqE z(Y{k|ylb>cj0Ydg4*5S7eTEqPPdldh6)_;KdZCF<^GUPl%2Lhsbg}Xm{@NWDnL*xL z^z6cJF#q}0?8#(-;+QXeH%Fx!X=(I4(W95~cF!T|XBcd+AjfiHV=oO$fa8%h+Xfi$ zee=f}gUt1xRbzGd;OVFznewE1mS`UIxbL%yS8mnjJqgjZ;=A}+#RnXL8*ntG>;PZI zU-#P*Ek|&y`<*-NO~e!akrXh6qw(8G3dfkQ$6(B22S*0&VvTPXVo#|V8doVL(qesgim4=DS`h{fXdMJ~CXlwoha zjaeS&J@aPiiyqpc+Vo8EJ!0A&v@XzPKXO`vWxu{-WxwgOb{odyXq27(36^~-lFD5$ z*D&&^NSG(;=dmwRJA!f~Byqh9<`%E0US7Vsx@Ltz|2x%DH`QSmmFG(M>!gmsZ;#+2 zAx?KNv5i{DIc}*-u2&1_3h90!z@Aoto>wldTvEPf*=nztgu|R3;}5+q3P=yn-^dp* z2L92kj^bZZvwUUEs_NC%E0!7~b8|J)_FCHD_6ljo#D$=L5D%aq=+zef$d?y?%*VCA{ioT`nl-D9rJY;%+0@SZ<}khQ zCG;H1+R7zmD{E_3R@Sbru3S}Cxnk9t+RC!(#eA>rX4tCM`N)3a+phPoG#BJ@ zTs}B|pf~B7^H+bwJ+3(v&YzEs7nd!qty!~@BHQVrl{$a$!S1yU@&`KP5A^UkfAgnJ zzF>4|*{peG4ir0>Kgey%NB&?h?@Ihwrf8)T#5z)-9>2U0$}ddc*P>qeJ*YZd*R=2YVrZD7Uby(|)Rrsys0JS^p#W zh>v^**$?!{AL!v9YWu-n-u%I?PW#o?tS(<&X&hMjdr`M5&EmrjLG}ZkH-F$e)b>NW z9WQ^K**=@obo&P%b_lW`=;GzCGu!9vsnUOl-&X!WA1{B0qJ75muQ{}%&p$Kiv1b`S z{C2u?K5w*Fc(y$Mz~3h0U-({mkrut)%lwD-o$)Ah@HvjJ7D!h1HIZB z{~}+|h5MI!(gpfs{OGlVPu*cNB|GS+ANfW8kum7i74pyH zUpVVecK$883BBUA51rY*R+-w4@R2W+3xqNL0A0NN`PjI_AKE?)i) zW&0XEuA}YioXQo8D{Hxd?d&OrUNCA0{3@AWfNx3r3cmv)4d;AUTkR{-4QgMlQ#-eP zgK}CM?^czV_Wzm z{XqOr?p*w#S6lcaUtatpgFBA@nq>zEe||^WQI{>a$9%y*TYRtF$PqHX`1985>G>T$ zJNSIv7XC;#5dV`p7k}v07XHW=@Q;nJM1PD=qDQ&sSnlKht~s$H|HYGi3` z`C3qXT7HVFJ+}UB=ij0%0GD{cwhUlg0qhDJ&X@QkNjC4KIi<3ql|WV zd>Cog`GSx9q23{k{DBVnLp*%P{K_GjABDZV`GZ}Z_UnxKz3e||`@x4Dg6s!6Z~nmN zu-~UTKZ@~{nc3m~wW@q^=fAsWyWm?~*r+{z^aDM*5et!qQJ+eUSTu;I{$D6-E zx0lR#gh=few6@@IfB9a?_d5)(>>xTOfRp2R_3XsVe z4$3p~fqa1<^x&@-e#iws^5NwNJ^0rNKjfku2jw61;ExDDw5B_Z7hg|Rnr4M@W?;ot~2f3){(8HU5(1SlJ{E!R&pz;fP@NX7=$OS+2@TL!X z@K*^xyA)LZKo9;V8Gk@7_=CzH=)sTi3*>?y zdU)*zdhjD2a)Fy)`BUH5$~Xz%G+F1l$4lTx{Y8EDwg;nW5XIly@K;PrsfX=Bz52|Q zd1>4^wEhZ$-bYuI>hEdAw@T&v+G3w?U7=JQpX-zQc2?xzd!^_kKY7bav?zRYd8<1d zCmoKOp?W;|cDC*t#7p1K!f*AxQg8Zpb|!sq$v=NvuuRzRJhC0z&+9Zd?&6CjgpH^n;nLg=iZb{fUf_CLNkf$KX>3TkJ1a80)IR(Btyt%|E>Z5Rrg$2h` zsjlTneFKge67DC$&ho?~4e=F@munokTo8^qDHGE87mVX<~u0zKg#a$Looh_na_{-qhaCOu0e*g4U&TypAPw2gmi1 zzqdyHemTMRmJEGZIqS}1)ovN9DzoNR@Cu%eWq+Z1ix7KrN3Rs9@EkjJ4y3U?d4z@Z zJ}36%wbT6DB>7;vO^)yaqT_g;BjOPt_GLR+!scK-zt6$6R07Q5cyw;g%GdhQeHHF& z)2r}&cf2*o6JP6&H-D%57tSa5Kj?4J|CrMMpkKoCGM-z5o+CNiqe$0Dqyo2}yf<8K zs_y0e`XBQl-Os>}ZqVWAssrnPAP4Q1x!zCSS*4s`!$AMj+a1=Cfqz*~c51Jjg;x_V z^*``^pWAX%hmbYmJ-4wumK)UnXl|xrm;BFdzzsNBrK43n0KVD|71ZCb>jWI@zM^r2 z-wH?GyDD)U-=c=pum5r5*qe~Fo%7nhdw5=P`=aMjG;Y8#I!T9t@8PMPH`hng7$up| zv-JaxzzsM?*6MZ@_0r}zoUi&AmmkjAkCS)c2;6|9wMP0~sh3*B!byhli`}+34xhG% z!@P7OPa=oJ(ziX@fV*#s=G(*>3Skh zrkV*Hfg5ndxC!`bdyOeE7T%udv295a6}mK5)Dt`*o%xy z^O|a&G@T?qpLPrVf^p#|8 z+AM876MXtH)zK?bl@6O&*blcvG3*WsfttdKV_{3MrG8#+&{!GE>yPs^> zcGLMP|5Mgh7hW}eO2J8IJf!Tkb>8Q;-#Waq@U)36=C{%no1#{-Y`D;&3US!3<( z=Qom!#-oOBExh~9*9(3;_92#0@V&oYvpsreT2WKvjRumTWU`Q9cP=X?8O1SyNq+J5 z2Y#vS>r^w*dt7dueC@xkD}3O|sfE3+{-|N;1D|bJx#8yxPTNm;;Q9$pnO}P6?&1_r zE5q&8g$3gZ^B&&U@Xq5uYDoL*TMb+vy^ibcLYnTu{6mS4}CNpSf`Tb%P6d2DaZQ8fDS#1KmqX2|kSfba(x87MEA#EXo;MUO8sW;+(~KXP-T~ zB4^C_ip7g7$LB1{t*Bf)u5!uPvE$1|m5t5GSx3K-Us}2JFb2GJ)?;UwQ2lVUd8+<6 z!irJC9QWy+W0Li!6r-rHFV){C=*W_NMU)dqzI0x6eJ@h^vg_)g1V_<3_Os|l5jB9l>0{g6^!}!oyH%)hNc-`(GJ3J~8c<+huN4^YuoTT}WY#<8wr?S1jX?lVSWJemvL3 zqRR;jdwygup}@aj9Ou)R6z!QGj+$E%Hh6v{IUR5WZom=R0$(~^Z>M2gPPm~yBCK)z z78&UK^y57kfaCYXXxNtlVZ?J9`&u?mYiZfIQIDA;lcwv0Q|UNYdPVpB9;i)zeGHRE z>GxT}GN?Y<613oC{mVGcraFNBHj5PXX`WiC4{TaRbg>(FpMS8jg}GRl{Ml(~sV({r zY{_=!aTUf<7*|>H-5SPQSig<&6~uQzs2ztWK3|2g^g8#5n8#J9kzUHf zJB<1W+<;?L>bGybJOnBS;mBm*y!c%7tw znK$p0eiHs-34hUN9E3#T_^AJ;$U*&&Eb@~#&poc%vC$ollMY9HlBNj~Unkj;HpJkc zzb#lA>=(37QgiEcxkk9~#qA{Ufg^AOj%Kl}uM>Q=s-AU{2ZCeKn^Xp9-Vwg(?>d*!6zYktmD>m+6VAz9Z+W-7(wtdry|71DYRYR@`JuPwuH z_p(5^PSWV|>m-eR2f9v@+h?_|vs2eeV!vIS-`)QGcAe*?cW%A&U6YVt66<7NJ9D7k zU$E&|@%w8(I$hR*PSt*pSnkf`byE_$=XrJn-rLc6C3WMK?hQz6HVuc{ZQD ze=75V4t&|d2YEK1d*58pfp5Q*3&;bXcfVcGfiEh2kOw})cwSuschG@vv+zM4_|~So z`9KH0D&d1X@Ok&!1s(YEgb(t-r}x{naR43oOyPq(@X?ANHL`<#paUQ1AP?c(beoU& zCP6*K{*U!iFF_Cdz2%kDWq#0ue;w+z_$UX>QZ67D{GbPaMED>Vy@noM`+*+(P12u2F8G0imml=tM?B<$ zA9^63?Z@j5(Qo7ZxGDWO=zt%}FUqsGK4AYYUTc-m{;!K<|5x!NU(oaP(F^?EXCpVk zKI_B#mOu{X8?Ddz$qV;?4YuT=jRB6p4LG7)0AHQU=5YVlLkm}6>cA1W0Y@+ZUmd3X zU+V_%L7ZW5SnuN-?*D3A~ap zUiiJ36SgjOjs6F?0Y`I|#u51HOo!i#bt&*1nE{T#4LC-megj_z%OSZ8S_i>x8UMoX z#biEAa08A23;2fLi@_pY)93oAz891JHjA?9eP;E`dHB7UwhG>PyKENkdodYDVO$lJ z_t_Y4VICdt1Mqy0d3Glj+zw5ngn3_m+Nh1byL^sjPyD};GRMjOM&euN%D8HZ&p61L zNWeAPamYdak52QES9c118^o^beTN_Py;yhq4j;GyM>8VhDk&!#zash$zhRfY=D!z~ z7&}I)dDWd1;=N7SJ*-NhzIOh_zBmE#ysoy$agG+PK6-u;ux3jd*AW`55{Qu!AjN0u8@ zA2m0NOK!*gV->YY=y%^En^CY$*VRvJ^nQP@%Lml*u1Uvz+sEp|n%eVf7FW)zt*o@; z4&>?*4>Q|etv`_Q6~R-s-~pNsc6ovHiTR+l{vbH{fU%%Xmxh)%M|e zEbL#$-7beW?)KUaVeB7?_8s9*K;ZIqdvDS`=YEncG>U7Xdwyja>@1h-!I?fdYHm;_ z|9+Cd4LC-me!J_X-+q!BN9TT${y5qDNe0C0@fKm>+)uKF#?J9@#JD*KN6if!lYiU{ z+<+rA2fp?^7V0DRJ<>Qj_mc!p*gq0;B?u#)(}=(C(f4~WhU|jsBR_LtKS{s(XiHFH zlJ*bxk7^!E`r9nh)TeoBr9ZG~7170Rc*k9xojuHBx#arnCmH+xWQ-o)T%X{$7INp# znOa%1ymEEz`Yav*uEPD!@agRB4!e4+i|wn|C!69&dP`Wwk=CTdzCTH%>^U>b0CeDs2p{BW9vVGlxcNW_zD(hRJn(tHKLH*1n)}JRcu#)4 z-=BaEeA|Q%@{k^Xf5HxS0Uh`(;e$Mz&;9)g=)ku?_#h8_hEbf#a|}oibl}StKF9-K zZn~Qfbl}@BLK(3Kj^`qDg2O&a)o?&`9Tl< z=A)&aiX8AGA6|aYgMXXwLvD6Jy+Hm!4}MGdAs76}hc|uDgMWeWL+*YlA3^p5J@~VQ zA9BGTWIxb@fB#Y1evk`(=;6&j=)oTqe#nhV{+q>)UVhMnf3xsIF8G7;4|?!d2|wg+ zmh^+lFX+LaC;X5L{-F8;dhnaV54qrn9$x)H5B?_Uk0G~8@*i0Lg&zF%=+BW4;kN?p z4|?#g6aH8}gg>bKf*$-BPe5*-@CV@!dho+OkPCj;0r6b_c>c@u%zs6uxW_L@7xo7p zUOcgmnAdUrlX?<pyzl%ly{?&VPvKRijy~Vt7`b~mj>q~#HIebCk3Bg` zpK)dc#jAOfI+`q_d6P7fN9U;wHKM8f^DomZlk34O`f)w@gHO7en)YZmR(%A z{s2@wn0g_RDD z<35bvFrIQ6hu{10YswK9X_Idw$~-XDa^JXlEe@?X2}r zA9+P)sg}|S>#X+uhGfxx&hQ=k$?^LlDnC7XsWasCn>#;SKqI!_DP%jgpVxJ__kO?) zI7Z*sI09d7A6i#s+_%fN6^CC9eD7z|=_{7DW&T3c6USk*02jR#Vq8^;= zgQI)I+6~Uh5Zr*HDfJupY9xHtSqYBLbyfj5#nxE`#QWfwM;P)i7)O*9ZqM4rG5Ob7 z0XN_X`=DOxa{&4M?e|rET()w-9{QC&_|rAJ=~sgDPG~4;04J^|?D!r#gmru(>#TCq zcct(zK92F~qvqD>C-YzKA9`rTw%XXVAr{hqSzbyj%G7F%U?kk?sZe1-8=v{a9` zFb>O>`L+Er--Y=~jJu%k)zr6ext>HmuWL^sY`3JT@m7jo-(>z&ubY7%c~|<=zxa%g zkVqUqHr^6BXcw%%`N{K~gJM*<$6s;M;i#L_G=V;!>qUO-ctGfm;m6|H%0ng0EKIa}LUZ;8?d*;|Sk(+?+U$LFb^jaZLVm zP%Oa>I7UC#I0E0~D8$Ks4hnFK;rLhGt^(iWz%lvHL5T`(z|nkL;|P2;j{N-d3&(RS zhyPE7ozFkF1$07u{wBMCc;DxrdV2n;CwYEj1(wDA<74#W@rB>{8Je3Z+41v_?Ef3HTmrm4a~OF(Sr^emJ^EHtzX|WW*7;0Y~5ld=WlWeh`iruX26# z`|fK?8b1Xee$L$2!E;cAuYejJR^0C#l+;<{io6yHItOJUonw+qXP0cJ^GcX!8l7pf zp3X5jk4ySVt7Q=i_kcbZYm{CCqiC?`+*VdGPD4hrR=vCr_Wg*E>R+c z82;Gks@wWBob=W2O#DiZEd>*XPrHBZu)IB4w{9&Nz9G8%!Su+kmbb^1-o9?@?m?5H zCEGLfxec5T)-`n9#{yp{7+0A8@VKmMbJ^uNBq2m`g~90xA6Ar@2}2#ytmxDH1vdrSB+r@7$qmg-oFZ4u1>8I|s$9o%CUPUXZ`f z#!vTeb%lQIlUGQ)!~6Ig6mab;!j<+hN69`@A#AAYC!ctt0Qa~}Bc8$~OP0i>i7ao} zokT#0pI_AXy)w^?d1q6?n2*N$eXI}0{4?gGo#Ho9qs!w^o_8KJ$S{788vLGr^SX0Y zdko+Dv&>`c^P4Ajrs98%=P}5^I6nHOpS=Gm(Ngf9e+11rvXih51pZ|`*{ONwq8vS6 z3E#%hu;26lAaw{?Bi_6-%VW7g^Uj)^sn{iN`wtv}8*nt=kn$nrM46$EZO^Xj;mGyi z3sO#W9`#>0jz>}%NlphGfg5m)yryvkzFPZXR3A6(wk^S7J?i3DSkY+ zkDh&TZl*ad{sqr-u}GGCApzv~T&=m8ik*9&OW#Y5zrYc=0Y^*PY2bUPwGEmd;Wm|j zK{&cwQujR9q1LH0(+7^g4LBkV;H$&ass5eqsE_}O)yKD~&b&uIUYmjX_zxL4VZ8>z zh({XQ*JB*s_eAfT!0V0QOQ}!cUvPcoHXt63n%kXHGKLt(DpCyNrhik}7W2ndmxg1? zWy<$A@V!jUN9ymh;FEZl^Wp@qZ9C-j({mYAJb7CYwW@iE537$4*N zWvtJ@cRyH({L-?&P1BaP(sxjyna0;u1~dX9jJa|8|$%7sQBlF$=UC_s(qu~X;Z2HTUr z6hnGAN8m`h$G^Uga|C|n2=h4t9M63k_Yl}8j(&+4IUO z+}Ea8;rZ@(Ymg`2F)yv?f2Y^|9YJY0Q{(ZHGmQQT{So>n$y%)6!gsy+UIX7}G<&|+ zWj=l%%-?fW&`7SlIu>J{h(5_i8`pE0da|W`0OLGX+ zKf(8%zu~Ve68lbLI@ycu=XKrgI}O1NI7VO4I09d7AJ_hDbgQYDp44`EZHF+v@J0WG z@Yx`6`MQ7Nv8L{yQmKDBj=l-tpMRNlvU5F{=DZ=-Tt{t3l3igt=5W7?jTZ$s;Al$y zcGpYq{%qS~IC7heXAggz3}c`eDt&EBaC>Ci@!fxZaOdz8uzmd^hLYLij?f7B27NC&^{jVy(ytif5Pk;0*a71XJQpCW<1ybDZ(si0^l|ij z7jHbt`NTNHuRdySXrq0G7Y}#Q@9TZUh7UK=jL*}>uKQfBvxp7(xIxObCHb-? z;>x|hD(JwsK=>dJT;Ba-K?lBU;e$Nz8OAGVe2sj94t)E2$-au7{BF!}^MMY0QQ?C; zq{sWma=k`+pab7#;e$Mz@0BzsALzhWC47))^WCauSCJm*z?Ua{kO#hv89ei8=M!|` zGldWGz-Jg2sY|2>I`CzGqRS!VfiE}xB9~o22fk)0uaF16{;8avy+1DKzy~_WL%4q` zm7UKj^`42|wgkwUK|&gMWeWLoWD(><4=AXA3{% zf**Q#^ACFP@6V8WFLLtQz#sJBj|xBJf*O^-Zw>yb-xTbz-_bOS0%o;KHxntKNtOs z+A@BxKQrZO)dz5c9_jhET0a80=0e#IRs6`4`uQ*Vn9sZnG40=azUtqrexiNI!B~~? zRP#I{2hStc<9_m1mS}!iXIjT=8Q3MZUKsDu)q3H&ZxAoNZ-U?IeN%7R7cBAh5Z@(Z zvHiTxxgLV&3RrLFJ@cuzlzlVb8=~w4-z%rR;pogKH}LqazW?LOZ;L-#w=RNa(1xNE+8b|S~?)u>x#sR*Tkoe#1 zg&N1I(-Xw;ryavFdcKWgl(L8X-b}XOGV68bYt2e8+@C#X*6hi1=1rSCw`}&jvK8f* zR4*;(bz)_U%4Sbmt7`M3ryuUTBZq`Ie zTc5G(;kE$%P!CbQ5)qv#5Iy^CwR(n^`<>@| zuxxqx>dM;c@?}WD-*5VxjbDZ6x1LBDKlYu@ryFoayRI{b?snA+=m&kEUvsV24|WR;{UBQeKhhwr*7p{aXH`^&3h0uzu`29Svm@c9~E4>j!MFq-NQg<%z0i)W2@y_lD@lO8DUyTZNZ^mYLR(zW6~O=oeY3^@HB#+_v;vRk?a~ z^@^nlHZ+#>^ISeZ{I|yM_o5&BPUoCIbl&&7&b-WDKj;Jfs%oVE36ADWw|>t0x2$?a zWqECTdIuUeIrRI3=*Ri>!w>IC{PgkO%Y5EFe~S7CeW0JULhA>;&FR7VapSVGrhN)V z9Qy5OLqEniQJ49YzkbjM`k69Lf!=0muznS5R;{jCUbeEfW@Un|&*OsqXwWOwYd7@cRboCGEaV3|%AJcP$s&~{k zu>WU|qydg_L%bnR(|kzWRY4`g!@x*Mjt0T)Av@d0Bh4%)ZY@KT1^TsS>AN z&(jZj^W+!K9RFG0{sDMFKkI&NC*W$n5~N>kaDR^BenW*MLH_~l}GexFa*-2bufbo?z+qAv6FsQ>sy^nrd=-xd4>M{`(!err}# zCp_qMWHQsC-|RN#C-{4?L|x|F+kN$eKG4s)Qt%VK&Eo^~TUoV!Rdq!>dIp`-W|?1N z{Kop|*N4BClkr)5<_R<3@YfIeK)?F$X#JqKIXFPSRn^Pa=mA=yCRTLj!*>5S#795p z{1WB~6G(UDg)bSb0RiFRQaZLsxgX`}x;IuXT$Ci5wO z{h$x@YnJ(U=xrVqpkMi##nm`anOtUx40b zuK@jOtCve_`wQ%Qp1Gdnn{7o}c-UzkYb{gY)L9{_cO?AI_UQ+|JX3KF|;Ex1cv{2K^4F z^Yo&WdFTWEpbzx6rVLX1fyCxL=}bg=ld@`A&FT)H7)aeh3k_EeimM-1sh$IQ{&PR2 zfb}SQsZUKL&Gx(#I4BCVDb#&lVDw?#-+9jq%p18%wJX_o8LN1G0o#Mu1z{Z!)&-fe zE(q&|upS8OldwJz>w}OztP8r0?D|J~QKa`t$y3|QvYo9X-Rpv^zIvY&_-0hE---Oz zZygVVM>v#Bi+Y;JTpwb8Q^+ zNI}%^WZ5Uh{4i71FZj_riuL}~t17eRR;*dJvLg?<1gPF8C59|LDp1cu`=qQZ*6ks; z*F642de|q0-?{MbX~_rE@v}d|yiW?pXVZ^+JbbTvvV`6Hq^O&*KU_ZD`=n5~llDpB zM7-5Qr5Cw4HvaXEzvi*nb35t{J1aQN=*i*!Tp%Q9W1$!iL&6iU^@{%dOS5AAw^+mcJts0Y9J6iw4j`fGuxpq6s z{ULAzj^<8{Bkg7CX+$s6!|yM*FN`B_1C9}?-@x}!YZkQLnd>;( zv$k=xziW@-xGTAE1a80)xB=hbeOZcBeZ=?BJg0k>FryvCJTJnCXR@~TWyuj)LF<7z zIv$R>=`jhauVQ-VJ@YpQ2gmG7=c#d(b&`L(TJ@a?dYr`>A{Y67$J$lRt)G8qQ{l9v zzs;fulvU99`M}06t}ETcIPYNP33G)mo6Sj2OQo@m)7j%HJ&wY-3f}``yoK+U@EsH8 z8!*0t%mFl>rbH~2Z3Cm4qHsG}>rCuXFVdt7DySkB26-#SN!qoe)DRhwm8weAKz zuCfOGNR1z%C)#nyLH&Z7yQ#igUI9iwJ`6BVPz8(KtHDBXs4M_~g`cpcFWA+R;j=&8#Mn2Lwy6dGH!#3L5 z+2M~5d=9#hnqepZqNAVb?))8Iwx1P3XGw` z@%4k#A6r|l)cx@`?R)dpR=EC`?dzYpUFG#b;JcIBdDbt;p&bU!z}*}#?Y-1v&CfWq z3-Wbr$Ns7^q~!B+5rISX*Ty|)Jl$4WZruAAyQn=raNH>R7~_35|L`)w`*pC`k@=XBe%t_1na6dVvnIYqfdIkm=0dn4_RrnmbQZy5ay-a$Y< z`+oPF@p7%o>38U39@$rw%BkJn_|U6cJZ8qY*{__s+Z*@uyDF!DJ~-`7@ zlktySKzrktj`N;yt6MZ1PGxE+B&xo{eAOSv>~HDz2EJ2fBG=9LX#64%C5B^jpL11v z2ibX!?<{f8&$qs*pUW6WZf}s^!1gBleAj*y{MC8!yjgR&#i^}a-0l@o=J9sE|4d`X zEs{z2{NCm$>JQrMG9S|4Rf7-Z6y*}-6!9p(2p=jxXnf-w*ZRHB9wyK1oNsPl*g5Z+ z&Hm+7Q+E9Cx^g|wRMo@3UoeCzC2)-}F*?Y>{{?mMr5d6qp_Q;_lAiw(!@EK@Q*I_p1=y!>uu z;eXR#Rq^ZleSLfW6{{O|K2hDsD z+t>EkUO2*hwSn@`aNY6;x8M7>M;r3)T3kqZD9q0p-I({wGYam!=Ke=E{`j=}&H3N{ z^%o|8qv6OKdKV6Rf92g-KYe@0OZ|KA+HmfNJD0Ug-SuRTH;caeBXiPEqVE-d*=T*f z^`xC^8lQWy?veH7S2eIb%z0Ne+%xye?XNtvM%m-{uN=8O?Zb_Qx2|1(4##g9eDcJd z=WK20v*h2{I7uWY_T>ATO6_N~<2Fp!;hi53Kros2_Iqr@}K0Gt+oIdw1qjFeR zxccnt8iwB2)Ub2;sSR{p@blTv6};4UfGSf?iA>u%Tvzz*-p@?DyQcQnBYvLV_`nSp zG(PQz!s|)Fj>^wRn`yDzXJB`lBM*HJT zkL&G18e?Jpute&ulg97bmgUWVRC{ZHi>(_ zby`|l&o)=9&15>d}aCdk8a~79ZGT}jKUKdk;<#vr`+ZS#Uyt>? zKGwWut$IGeKD5zg`g{Dyhk9Ke{A_towE4RD>u?|k<9KVS`}=;*i?L%Uz2jkrb*Vkv zVI3Lxm-S?)o*#>tm);k_SN$pDigl?xPuZW&w&q{(`y!@ixk2xXG&fUmkD_N0ocGM< zWq#{ldfc5UzUJuo->OA=U266u?YAY#EU=&3+M;#FH*K}Kl6oy^E~!x+X&CqzzsM?D>aV5_tVZ-qUkY){3|1d zwsG{C$9plka0G5K9MMioy-d`uhl(yS9PRTZV)d~p$#4|hfFtk#zB*hd{b60{ieEf1 zaqel=g&%)Cw_(fdYZ4l3^rw2mZi(X=Oy4=Mzwzp0|5Q6Q#*Uryp83+j!O{Fck5lSb z`uC5K8SeX50{tWF$*$7Je%!9EJe5WqGJblSXjQ)<<0#G9`Y#Q~lrwjpr^Z{(bxVsS z4tA3udHs6W*L?MEG5@{(y6e1wspi76>iSUJJBmJ~4ChaMv2v*qX=wMU-q^A&2?L`_?+o@##U~(W6HfVo4^} zw?{YV_3T(rYp(bC-o)_iPgZvT-`z9Q zhqVKazzsM?)@mGquU68tKiPrch<(Z6x5BY~-wg%rPv-nah)+Og>~K%vDEpHg)*w>m zfg^AOj@BBDBktP@ zz%jB?`ctWwI`?+F>kXqF_S>t=@1Bn}R6E?>{kymgMmy|>qq`mU`R?x^?ce32-$A_v z9Dy5fv{p!eD)mxlqU-J7l}8!lUr>E?<7n^S#bY8hE(+MctNCJGAK`2LyY64$2;6`p zatwSCK2(0t{#KkH)JMPlt(&Y(e z{#L?={jGj8d0>%Za{Tez^D1b6EAxjHjYY@ZK5^Sm|D^WunOyx=L-rLD8?L(HA~lxD zxN_q5lYX_KaN@{W>YKl{Bc?wxvHah^UiIByH1aqqo%l%QH1%nesG z+6pR+=X~~_0iXXz@wWX-cD?w!AMgAX?QiwSj(2vh z-&ir}+%s=1e$Z!ss{^&i9Y@^M*!0I)1<>cX$4+d_`=B|d&v&<6vHj`+LyDl!%7x#3 zWLe$yF@4x(-NRul?$kE;-sb1?^QYc*S^oaBE>q=(_qU=wtr~u?(%#?d#dhp(HMU}0 z!A*}J-%$DD@r8Yc6&LnD|6WyIIXAz#_1Ov2_rA7${T-K0%)I#ZU-P{K-rq{T&-Lza zh3|MV&wzHC`ylPSHc9UNt@u2{aDOX}e7L_=xW5(cQx)6e3N3=nU&Pugxi4Y$Ox5l8 zx0-t%O~O$lYM!pod%IrtNw94g|7z}>nKg?mm(fw>@##7e%X8=*b$J;x)%iTlJ+;40 z{H8P=j>^7WkrKUsSG;tvCxjo(zTQV{_;6$E7=E6}qT9&F4PsYQ^3|GSfVu@f(1EWh zUFRF}z`rrW%?CR0)e9fwfzL2rQLku_9_YZgPWT`Xd>b>|e4qngMED>Ne7WfZ=y@?k z%|B<+P0)caQ}`gy=6fa0$p<>{0bj_o`9{0=KnFh1K_0>zGe*bk!e>;X+}tSmSyGNb zkNkRZVY@Ov=)u3hQy#(Z#RvSL2Y}*thUjcNoG0e4N9q_ARXY2JzCqh45Rwe{A1*+JnhN$j*D_ zQwL{XCT~6tb{IGUH{ghJVG6!FAI)U?3KJXleFN%hk5)zvjC$`+N^CZcQA zP4@oxv*>&kE(3Q{BLSaf&^Foac9}2Fam_QLoT6N!oI)_luL{>)TVfcSb|J2%<=T># zLS0TzrM%@xBZhLCAz_>Ybe1O`a>Q5V)O{{TPD;--{t-oshgyQOsf=!@Th15y=hoL@S`-0!7x zIT+&+#1EfE@mi_&y3D9P9}j%c5Bfkq#6xeh`GZ(~IF9+a1}?8zT)m{avUU|167;RB z?RvN^KtI$&Dc%W5&b&(Ra|~k6=Y9%c9I-dS=hKeZ7HNJuelJ=#$0xICXKm5-sd0H4 zosVO?%=LPoVl#i4!jJ92<2sDvFs{RMEXI2n&tZIs@iE4CNCD%zG1Qjudn2A#oSAZ` z$``+DvW|3*>mq#$)wm9R^b9#~>U6(x7@eQ@MLnJ~r^O%FK@Qqk(@$RArV^d+P8io2 z<46{b>tep14`TUxT}u1LbwT@4JI8R&aUF0s|Ke_UfXm_LbGcOGbHNQbLJ#1p!;ix4~}`H6zccO3AVRBCi9x>kcPLNS*gZ%@U5$UqWi;Dm05Eu z)+}4up3dE&$wH$Kg<`!81?c+NsdJjXrrT|<7j*Wjf%HBn_B@*I@o$slgX#7;!V8Fw z<9QB=+YRhPd$NSh!Fs+{$5Q_|D?fG~IhRSZuQQ%bUYqZXCwYTB@wM)F^LLY7@>H_& zN6C*|92-xd4uD3*U-OtckD2eKkuK)^Z^g#fnqP;}pP~Oke+F6T=P*CgB=aTc*D!wp zdiHUiJojh(J~B7`aeJP^dmi^y(tn8`IaP{QCH|mM7Q^wM`W$EAtMse;VTsnW6L2)R4Dh%FzIR;0>)jK_F=)LzFw^)mlr;h1FW-7UclI7Z)>bJhLoWfv>7XwUdr-s{}!^A4Z&?p{6G3Zp&(w-}C}k@i~f zZA(s9jd$bN$GDsZ63KkNc2sZ!j^+rBBkG64Ha=wzv^OZ1D5sGVrJPFn)zVcT-Dsy|hi{>q_esQalE1AIMme?le6LhF{f>T5 zq27D6=X~v;a_Vkxy21KPTZ%<*fs94KS&+| zUU%NKuN98&dHl?jHw^xf^OdN}HJ9zgaMbJbSsZ6YjpG8==QGXWXp<18c{F{$!`TW5kXEwDye;UuFy4Q1aGLBnFQ}7kg zHH#nm=-pgDTy9iOzarNS3IwgsFa7&71*7kNvSHgz=PNl=)>ao@HGN9KNoPFNz%sVZ z``q?hhgTM!Ht`Kw7j3(*ym8e|UMDz(_#?z~57AL<-B^;fiTLY@hra9s8yk#mxWUWBm%xoL$}c_|4}xQXU$Q9=^5kgtzw<3_SB8&O^cX{(8;! zQSYY|%`bSPf%2g8#JQNX{iAQmOwsKzkgh zJ~|i4|M%+;{IXTKP4u1xbD!OPeR_A-KRkchh^@`%b^3NIpxOjSSj9+K_h;Sx z{AsMuw@&To`usVSD;8JQ@&f(ww&6SF>evI2QZMW97o_U-$l2o8Z#+k@8!grARISPG z^{U<^xqp$@Al|6BVeZeehywY*y6|<9uhz`&b@reGUqtvI5B%Qs`Je+|rtm=?_zdHO z9(;{_f)0GmVlT)8-^L6#ALzihP52-We7ruN9nu3G_$=XrJe$v*9_YXad?62fhOv;w z2KM&=paUQ1AP?c(beoUgjb=-}us(W!iY`x}M}EEbWj&c6^x%&QKjb3cUVDKb^x)qt z{E!QN%X*as@kh?GJkJqy2$g@WT#Xe$ay-@sJCC=z(~) zAFtywrJcij@2G@9hjfwupz?tAD?Cp$lv)nGKEGEFZXf;TB+QFseZKh76ZE`hm~m1w!0u#OAs^5Hw4gFCX0i|xkt^E$tETwa=F2m?pp1{_f? zfUnk-*XO^r+t!=H##i@n*ttIc@ARzb=deCMAfCs8t=8wiOY8IBC3${jn$vKu&rkEg zQF9|VdVM}Z$>ak^;07F_Bk{cs*RT8GsJT5UC1H?p+)sL;z1~an3EZZ)<$Q{MTP%kp-m6QCXZ^D`Q*00JH_=a` z-^6nQ`csTI(C?xjMt|y*i+TAny4kxouNRx4qW#(!Gh080!M6rWzj=&bKj%!v{~Gla za!^mBgM8%e7+OGe@>JzqX0<#BCYcv$k=x-}@hGeL6dR z;0WA+BhmrBhS7JYVO&o2Z>7{fgd-|^I@vRqe$*`(yyl1<-f!|fb_gS$@9k^ZIIX2+ z%;g>e<~h4B`~Q<#szyadKqpl6@U zZ#nt=Je{uU(XZ*r&A$(dK9jwK*BQXK`b)ogq|Z2rg|y*N{~-tUKibzvUeU>&H?G?8 zzj|B+{{Y8TLGuyaZd@g}0Y|fsln*H9U!Xn$H{cjKO5^CRmqpYcH}AC7y|BdCQMHG|e3RFmvfHX!6MgOc`v(aG#Czu> z=>5@NWW*7;0Y^*PY2d51zi^Ua{5$Ojs&i$9i&+HhN_PVy()p8Q(hhuJf zM^bBV?hZXY(_SDJw z*@LF#?|HQ-|Eb$b)o{I5P5$?yHf zg#646lkz|LW=a0_!wd5pPCO_7{rckkcT)26KX=Wn{5f??^0(f-GJn)<8}jQu_-6h) zw|qT+)C-s7_dmWYe{Iv$e6nf&+;0rX@AGW`{9p7QnE%3kgY$p!!Jz!dE;up&odv`4 zC;cEh|BnMs&VTFnGxOivIx>IYdwKcu{xl|k(HBn3|Lc~)`A_{gBmY+?|7pU5KfHIs z+`MHIDti7M-C74rMab4&ED+ZJu>NNs17R5m%RpEL!ZMKDWkBzxrN4a&uR}Y8WuQBj0iFzuw|-H_d;an|;+Tsq zP2>#>Xe}Ox`PWN+b>|e4qngRQMnde7WhX)x6h~c>x{x@`MlaY(BR=K?gol_#h8_hS8`lp&#hL*Ch1|^1wGL)y)Sw@S%P}9{3F7 zYPBL9>46S>po2VwN2S_)yiXSDJ@z?@NIpT2@`n0``_1gZBTP5jx`w65Edhpi^KjeZR z<;bf)=)u2E_#qek(8J3QdhkbtA9C~Bupj8bpDFy1YYKl*`k)7YGsYjVgYY9C-u#0e z{M&>da+?C$UDzM=;J1Vya={;jALzlqK=>gS?J{ui<{$Ln&lY~j1wZWQQ7%wUw0(T` zsjKU^hvS_4)J^uWC(~e`x`24~9jf;n`6;PGjVUD0uS~N{=RS3OCZyMCZrtq#`_xH& z29D6&RWJFRb;mw+>YVkB8b{|ob-*cTpE|@N4e=F@m(zFNDRlZQl?&Q8DJNx0D*u9U zbna8qh!OyNb@)q! z<7>MRr_bpgwVT7vbGm;e1UQ%5drmjvV{+7m#_@V0cAnFHH{p0U#ru_MW&@5~5B$FS z*W4f2@dp zZbQF>`3^icKJGuhpqT4E_bTTbv@>k8Kw_ z{EKgjbMHIDA@7I-l!vzu9Dy5fgjT>;hkI+>cBE;)h{HRI4Wp#wG_vI=`mwu1d#(Pm zz1jK4>;8vHQfM?qK8+)|MDwq098nKgzSC)L=bhxdW*l=!HPr7vkO31Zu^1cl5VN42 zLc4@^Dl$#pE6R8o^GX;mV|;ZgrcrwjP z+`1caYMflRh{FHb=^Q8T5`v&{GU8)$)P-)RzC%M7=Qw#cm1<7VuS_$WvmT7|#nH~L zN+wDFaQnjJWYq6u9VbU`mvQm|;<%9Jz8oiL2ac1G9>&S{5f}fSmVB_?i;lFzJWl3# zc09Im?mNa|tDjysqvL6u=N$+CJ2$pZ8TT{hKo7lZ98LS!4)VnRZnB%d)^6Xu{g#sN zN}tl3@1}Q->lJ;w(kFelIOS1z>;y;t$UdjL!&a6%Yz}mXts~uGGsPXY?jP@#f2TWa zZE=UqYusV$N_W`2%pJD=ndg@OwmWR?bBE36-C^r-ci0>_#w~BBJKkF34x1Od!`4D~ z*qrYUTj#pNX0bbLjdh33)7@b!%N;fcy2I9y?y#BS4qNw+bL)4fJ8W%nhs|r;Ve3kF z*u2agw#K@{=IQRRmE{hb1KnZkNO#ywafhunXS?%%u{&%nbcfCP?yz;PJ8Txa!`4`L z*gV}GwzAw|bD%qH9qA66Deka!|5$ha?{tT)E$*;+jXP{z=?~!J>~apci7tJ z4x7)r!`9>Ou({J6w$`}A=Ed%?wa^_l=ext!x$dx8><(LFJ>fdtp7Lrb)m}@OpM#v? zQ#@e`QoP$G)V@mlOj}#JwYLS%{(#!o37+ssPxy3q80T;5^r^j;b)K>CNp5=c zL{E5_J8TVghs_i9a|q-`v)tjx3GT2t)E$l<=MGy#+~LSzci7Byhob}CVe43T*o-*y z>(f8G`eU;1U^hMM1?PvKw|{Nbe|!3C)7ziB`fEymj5B@0YnWRfG#P5~o z>ix|+9#L3KI2CzFC&c4xbio`IN1|E#TCYU z3Fb#IUt-GoYs{ly{+%Cx5u-Porur+sCiwHgXiqXM?UiE_LgXwnAfcFq@1(&~%cJ(=*c}9Ky)dnSp-m|a(de36? zcs@=2+H6;YaMaxX`AMd`Tpu@6AC3O@9lB1W58-Y+*!4DgsK}H=L zF0Z`q0OL`NPw{;q#;sUqj`i{wr(zwtGfVs)_lNZ3@hO+sQK?s|a_iRzn3i5=5cA)Y z@yFXf<4I>C0oUjcO_76kH1e*Wym``I%D6_ouV%p*=L94oxADGOjZYU|O|ocQ1HaX{ zhS$A~qBRHn3m%`cXqFo^KGock0cu&2wLt)mzzsN>?~PLPfWTK0aUeL>eMRF4 zzZH&p-9e%_zC~TA-?+w&qk9e^nJ@*8zzsM?-qkn)UzF&>+drC~Gm;5C8x7zH+<>F? zH)*e>UfLXo^Hm??^20g%aqEi@#9Y>UHiOG5ODf18%_4lyMXA)%F=v zVl2FAk8P`ybhz&OyKkYJc+ZdL!L33F91%vmEk|7`9D5OF@*NK0c%Sin+AZ`8#*xQ3 z7$>%kqx-o(nOa!XN8kn=VPoKn@S*bk#_q@qaP%MNeEG11%lyArtN485L>=R}g~~hl z3h0{IkAL)Tt{*PfbW%o<>jnktjk-qKzlMI=UGl;$+xe>W@6QyBzWd3BZ8x2-DFr8;@sL`Uymj8^w%yh@o(O5-0;R#J9$5xDa0Qko_mOn z;_JqetWCsUPdprcnBbPR&f8eoxge-&1MXLwno{5^%=KxS!3<(wmx^i`Fg>R z$J+Wt?@TLdifDa=-57S~vSN}!Ht8Pj>kqU(D>Tu2PP{s1t-8Vs3&s`ZJ-n~soyULF zkoMQN8ookh39_!p%g+DT%O!>1cxHRUkI#BZ)fw;1SiJTMa@gm3yO7puV*a5->aCN; z?>Z%Rlgp>mWfed9#EgPek^{&6BHowcp7y~D-yPf0kexS{kYe;OOmnjm7E$%dImhcr zBP}hh=b*l7=PWxvsq*H8beeemb%P6deRjW5G|Hm>J9ORK0y&GzD{>a)j4iJmGiGtl z;=HrZ9$k?$W_-os#g*f87Ufn{E*@98WbD}SWuwZ*=H#rS-^eemT$)riE2Mf6``MbQ z`r~OA3<+~w9rH}YPq&wP?>bvXCtdAJF^UTN8c#m)#6-i`S42`B`O1$4# z^Y{dfDPsBM@dfkBkMAo7UxJBw-0rIH-DDjE=AFTf`DlFShWTgAH{-iE(6i6u&>iIe zfc*ZnA;75AXH`?^*B6+d(d&)iNB*Lpf2}|J%@ecq_zvTE$iXZCrldNa(1D(E?E8>A}fa$<5rc260JkSAO@QdkYCh~`PphGJ!58#Mhfa_9 zpg)i4!S}KZydfX)LEpyo;0yXJ{UJW+U*q}=mVw)->!^$h4dpn=+`qn z_(E@4`bT`wqx}J2&?6ms`Vk-Wcn-dh8*o1GoYGG=0@^v)2;LdV_73#WAM~l?fi<@D z&FC?rX34z_4<1kbI*q5iiN8sV0o)6CFGF^NU-BH;bLlpM_m_`fL1T!G7f2WRP{$47 zUWUvEV@vn>G=6L4m=fQjRKA|{gL#IFHOt|*DV5I%T3Tz)u3;n8w}8nooNVuoidX_H-Z73B{KOu32RReR(=Xdbpn7nY_)$HWk|I}9lXSY|UWRIz`|s&E z4m@^SSpMNk`W^Q#;TVYIA-!M87&p4l4X;yegGGzaTC{NS*{3aBvUJheOV@N>(!H{? zzq@D6(u+EK^W=75@PSZI6!-8qw{4>`@SmcQfJ@wr(m0G zch=8$oTI{WDW3~Jms54GMwG*>y&CZxGT05v=_h45jrMAsIDSS8l~YHwy=j-aS7TZ^ zW$FZ}8s=_EFQ>!L%W@k0-!CO_O)%PlJSe9~H_EC1Yncxu)gAstB0uMxw(#7U3#g2C zF6-~Uq`b7hr)Txj0J=oeko%f)&fg}|iToiy$e;J9%pdaYPRN(PemZreuXb{L#*gy% z=>N+6%@TQ}{L#3IF62+`2HIYf>2mK+mM=*m`iz#(8tn|Vqo70nT&`!JbMJaKp_jOP zCh?Q`TiUt&lFl_^5&`6f+ot62Xwi;RzUYEWxtmZAaFX1a;y;#JK9L{fuj>)1ALQG; zBTxSN%a`@9>#be;#QUY^;7-^!y<|$e;TonLp&)y}m~NF6p_fyn1PG`Qmb4SB<>U#selm{kmulp%@A8W;F8BB5DVvm7Z%B?`sQ>%R8u_dCepJu+lw;EK zhx{Oa-kaQhGLG&SYUHm+yo2splcQ&sDO&&YxO{fj$e$Q`=f0*Ko}NGC2l>PJ8Toeq z>zPFVe(qU|PhYZV?)-%`1iiSkyUMHl)U*vN_??oGzdgBqA&m20Q{43YAwS3;_#xl! zA=SrcRPD;kyZe{s#Bo@`?+ZNsB$;SGss6c~zqgR8znq>@YdrZD8+eIeb>2mQ|C0!QEm z9FczDEB%)U91qxxXL7IieL}ywhb8TOO&8t=qaT1D&t)w9mERuRC^~(056kO>)~`d9 z6j!YW4+t#vD~+1MA{<{FJvh#$`pETswCrK&o?MpgXyo#HoDtdUJ?d?uJuEN#E7@*S zy_hTtlY6q1hdnImI|}+u=5UJltm3CVEa`ch_))u2ZLfDKgULOFgg>z-kl16W!9AtI zWbhwq#awSyG+vUeS{x&Me*HtydkET#-aRLN?;`1?pYl!p8Tv2uXW)i@4)beWJkN%H z4fAJ+PcHTtqW+A&tv+Vb!(ol4_QTsP*`K*r@fvQpKb8HM_v7^b%%8L^y37adp!ZY?zsjNoGTr!=?!lyAKFs_}`J^lV8t!8zy;EqT)*5c&o7JC5 zYAF}}8N$MH`26T;2Weaa+<>G1J6^-h_=cM3`1>So9Sz{<5UrT+j9l5Dk(~IO$ox3o zDoTD@e+JAXl5!0Il6EJZon~kiPvx^>Sd_wA*mDk8~gt#Y#h={1DBjf5)8(R z`A*R%>@i8Sqw=2!4Cj&lW?mY9v}ZKusb54NU*Q_=3FN2H5XWEam^co6E1sW;)1Q4u z?dlZq7<9MRumjN!183mw|3tRKC=XI1;Y?SSE1wqo8 zqC6clX-bUyb2}#P)$)|~3^)Qe;23O`I09cuE7o}5_xJ-bp9|-;+*c0$>YDHCd3=8} zN5ooTtog?Cm``{o>#wjY;xqeZJO41Ae&Tspe>DpB5?`vj-(-$2jTcZZQBK__xV_Cl3e4` zL1#F`nj|X{#^DO$?r}!h_OEX#8sueNt1;zre?1zR(Bo*jp0Uil(ByHaoOMNNdxLU`atdB3zwp0RZng0Z+8eUd^$Rw;8ct!Y z9N-S8ms2%DXC$v3xlmP8s#k9K}}=u>~(de4{oUKtd0gV(&{)H@G2 zbf1o+-V@v-(s0l1mG;3uy?xu@S$}tZNu;54(}(_L@Wg3{4E@75kGv|H6r<-hcbHQ#ZYI+ebG2;`)D?JNv7r%)Q%@ zYsKU7yyI+YZy#Fqt1|}It(z96f9e^BmoBz2CcY z+10yseCxm)1_h77)81G$=db;Jh9-@_x+HjnGDvTu|AGyr<}^F(nxB9Dq;+e4b<#z@ zz9TF*Oa6QD;8S<~=$`fa?cV<5i!N$E?X$1WIqiVSLVr4sG=HV^;eWh)aO(lzYajg5 z0Ym3pc+k)nzxnr(?hhWn=EQe>^`B1u`5{lA{4byA9HbOBy!QgXHyoXNFYMvUx#^mF zFHFv}_rhdxh$8L1u%Nu6bKUA(pE;_9Xw=+~_55Gd+1I^n>Cw)mYmO{m*6WPPy%%JA zMs1kdj)nJ8d&LDl^xt^H4JGt%7~O@Z~YU7gFtH^|P6 z88b&%Y3X)cJ|VMtU0d)Ma(&`@c4x1V{(!&laF-<4kLld0Ebwqse^k{^QLgmz2YSQ@z036Ai}HYUAYGtGe9%9+mg^<+0X@>8(<46U2bdmwhZ*mz z^dmm#FJpS}1$~zO5Fhj&(}OSQkq_-g# z@`L!GNBY1Q^vDOEQ~B8_JQz3NyBhz@~d$*wZi zpPwMspPwN3Wxcmn*Pow}B3Du)HFEtqe51()j=&8#B9FjV`e}W(+oYv1lO16u7>Mnr{bN5FateSHO8n zkGn|rKj@cWufuMI{ioZ(lxIvfl|d zHP-(G%m?k38>I1jD%t-yf2;W^H`y2EldkalsLB(9R(wYf*Rb2@EVSujB_92<+b!Zp zd^d_8=}*5THA0hL$su$!xKu95{swNq(R+}~2jeUA`klGb<#9wqI9|s%vfChzH;X)^ z?OB|}arB>Q2i$<8|A531_`cO;HftO~brJ2^P2yg^k~J{?PL<-NMAyl88b9hIa08BL zr-84`5S@kgt#~}>kpBmp>6!Xn{O2+hgzw@rp6m9vuduwDl1XZ#P!^)pykgLN`kH-mi?upa`ma}E_ku}>+j{}3D9IeSi| z^)u<;Hv6sZ;kqZd?)|(z;zYS`2>9l8@trsL;QL^&J&oV_3nX7&cd_9gll~e)_uyq# z;rf|p*9%&)egHl8%Di@n z@4NTf9OwH2;eR~z-!B4bA9cEJ2}_~8SN}f6o{Z-y-|Ii^e659k>1CSOQ5`@%Nc+w^ ziCl8sg_^dV{T?_1H{cj>{RX}=pR~?myn-Y7C64O4iwv9`th>l~o`U0Sfg$~}aisc4 zKU#ND8^x&KsxDkM?ogZR^`Qs9&R#VZ@c0npMeMVQaU|ARKgs)KVZSnrAJqg;6Xo$g#ECQeqkjS)v}@k+Y5evR7|=BW>vW&}J&zaJO&u@hUvHhY-$!zt zZgSKi^D;XA0!QEm9Q|25UgUBja~t+4LvhYy{~IH9ZkZ^@e4dP0-${Cxx=$<9aXMr@ zNZaotiCl7=q0;Z{_rMXj0mon_*YiZZr2P&)zbVc$`Tr*LA0*n7>EcJ%Lg^TPsokMV zKL3KCT`%*SfFp1N zj_xrMN8l@S9@oEge?$5SajeMtC{S@G)Ked`Bq!_RZenkr(TyW;1CAc|x4<_Yw;0!v zZdXa($m2MPjEPZsp*{jP;E2ouU+Mpw`A)}-2k^|H{YWaWZYICluD;0a=X#Dv`vcL1 zb`{TMDE@|JqC^8c+7C;(qCQS<`ArM`YPYL2PP~_Dv35ynEpQZi3C9g0Ul<3!AQZ?j zRL!ku{`iuy-K*rjc?cb8R}7=X6?3I@x4B|J9G3elPWO_|OUkWt`&#F%F0T;=SnDa> ztIIJ}<9G8E9<=(yvPVesRUcLH)1wG5wU0hz%-{+9HqnsmW&p68IS+k;hjbzE`Q z%J}f@{$w#`po_*2^xhHg8S&mRd?@~4z8dcr@xBrJ4P(9<@iEVe_l|U~-JTP}_l~K1 zaJiTBesAphN3tFJUg?5wUKitW@WFUII3kVT*$X7!5tygG@NWe#F;5NGc<)I2^XH$Z z&U)`S*5;{!8*p@gbF`da;Bq4ITQ+~>aHMY)2uF4e9BIGK{5ZZSY^t>PRY@F2|2~hv z4LEu)NF0H0-uk^`$J;B~>3zpeXZ@)|y9(TZqkFh)SAlQ+oOeWuy`4|)h{+m(GyzB8 z1{}S^B#yvWilXmC{yBU<75VR*_yFCj-;L}i-Z{{9vviz&*u{4vc+SCa)Oi3$&I8PL z_8&*T?DtjpZiL1cFQmM$O1?i%&gbNLpM%8h=q_*sZotv!aTD;B`8amIbK%V)Zqh$Z z`YT`De2WOUgERY|tk18qA2`B~=lmE>h~sWTx$(T*9nKTuZxO$29Pu4jJ{*(Zag9bC zfg5mya)B@WZo})>{WJ{kPLYK93ys=cRx8h}VAS zjlT`g4Zpr+)~vSYSCw{s;lXgv)wK`)?cg!zeRXKCeBJ%@y)w~1Bxv5;R+=T)RlXy5 z4G8-83L4^niQs#i;N?pC$^rj-(cGo(oA){28b6ol*Y7j4^x$VcH}uJePahI#7<%#V zZz#2#+gkeV|M@MYq5b~y$7lWR59f63_2PqrA`OGz`+pA){^B=R-S^p(ubV5yy>UMJ$3!=$F98ViF<$lkt8l_0hqNH-9h;JLS^u(lvi}O8c}U zz8~u2t7m`sp07_Wm)>{s^PxT@mE%l(yeHJhcOTlPbo|QIL%sj>!6Ct8XwRu%E&b~+ zf6;!^tnU+#_Ak72{XN00<2#=8o*xuELY~Cq;9FPE6+DDa#>VyO?|wVdIdYsgOvMYd z^|_VOp0CUXp@Asd4C@>g|PM5(n=g=5xK(%Os3Ss9auJPl?PZY}v=nK#|J#=;zUG#nEjW-<7 zK0)MbpQFVnO9bQm`0|k-Te~uhHMah&=;bp0I5B^708YE;?pedHM0>6|-iYu=MDqvu4b=O#DV&K`DuP5&dN8r@9l? zAHDr#zkE*h2|pQMt}Pnr8st6S;&hZIi~9S>$&T}Ehv2WI3uz^{L;T>UR8&O19ua-0 z3=?Uk{*-9>N8#ZGm%aI%C1(nWqM>pRmg|&m6zxf}-Kx%K$>Ouim-VmfEw?V}>|WDf zJ#w8-!*xe>(jdCa=jxufTH4cX>~8Fq{$Ppx&elCgx}ZyQv-adqi;FPo&sv2C>A*ST zPhKqLBVOz#&qGH%&hB|BR=S#{Fu}~_<|n!(9@6jpdVm*@I`&n>oN3)_@KXx>A@FzLpjjpBR=RorUzf2 z@yn8r_@JM{^xzBnEcu8J`eANw!58%cdeGC4_@GC74Zfg<9&~!d2R)vHFX)jEJSY9r z{44qe%)k0)C;JEF8~CF<=;aCLThdpvxz0!b2%nG6Zg8k9cith%`j7P1aw$J_J{$Pp zeU>{tjh{Uqy_P=Ts^`EFxB*9$3*al$H!RLKd~tJ}R^jJ!we9)nu~y!SJKzZ1fFsfZ ze5GHUkA7dbGEX4vUhdpX1<#*n2{Z`q(1@BJl=O889 zMLh){)YDXc_I&hgMZU#PfFp1Njz|{pm416ZI`p!mxrF0vdOyYWe6*gA?r!Jv(IHPi zAAPcRK05q6{Q2nI-=P1&dp+21uy^54jPv@gg9KV}*2bKN}g}3B= z;rZxr-GgMm?^yW&`Ts-wp5_N% z=6c88$iY5c8P92cr{j}i6QTI~uBV-w#?vpmK2jY=dsh29z0v=D7jOfPXs3a%1ml8v zj`Nw#0A|~^rSqK1x)4Y8d&`XP@S=UXGM=a4I9q(9N55N2;NC<2}e2~9pff@KKf1qoK2?HfYpH2fYpH2fYpH2fYpH2fYpH2fYpH2 zfYpH2fYpH2z!=Z~y;EO42J&gMXf#pK`=h^k%F2AeycTZdI0x8)pz7GZ;d>`!Zo5HX1 z=6uNs>t#+~ko0HEp(ls)cfsNM8SFO?*I0K!`_3OBR=?0MT^Ga?`pa5BBdIASU88^f z3~-BZ{I%S#9QcN6sNAOE)&P$8{h8oJ>!8>*aHMrm`EkrzKa<3946L7F+<;^7OFlm! ztzL$p?u>s%|Gx9UEy8g!x7UpC&WM_s=Fz|Je89K?M|W?DBk+|vsc#+B&8$f5SB`z> zM{*q$^^YfupIrwf_jdwrz|q^2ab$cYB;nqXV%-z2*nf)%x`Ts((^uIK96^=ghU?ir zpIDbj;}SVGNgMMKAM87yg=2DLGy2yh0yp4@@h|X|x}QB?tY5o7#-UaC?+g9^Ee@JO zIGs6#d)564d-hiYr2JHKpUJX;V*ls~_-e?{)M|Ec*8m40~p=i2vszua&CrjOix z&lT_fO6i>T96za_9{>!-nv3%j##+9SUweLl6P+DE=LkSA-2N&4@Co}v8;a<*%lQG$vpe?r z0scWn&ky*}IjswqclUR%8S$e_)SaecO!M9Oyl?fBE2TX>!0zla+t)l_;(j=}KeW!B z#*U4gAAodVANlh*UAi2{(e|I`c%W-zKlp)OKR*ERK=)dg%&#do-nYJ0Fhe}h1xyEi zvAiT5;(=~G(}5r8^z#D{4|H8j2YxYK^85hA1Kn(<1HYIqd42%mfzD+*@B^KGegNWu z4&@2_KzE3CegNWu4)MSb{zJ6$15i$}|M_KHZV^A=bnAGM{YvKtAU^0lt^7kzuQ4C+ z1wG<}ehSlrFZ6|U==6vW`r#F_{DCj%kq(_6@j<_l>A@Fz&q_bygWhL)@CALA{tzGZ z=P^C_f*$$M(~tO|Z)1A!MSariG4zl4pnr|qEAU;A)BC(1T8o_@Kvg@C7~cf#;;}7Xy_qCkq%*He`3cVtU_N-C<+^G7?D+wC z^}4;|0Y~5l96c@-zc??zdD{bc<~U6_ zKOmm(*`d!5aK9$MFKW9b?cB#$L)+_)a14Ynr0XBY$@j}y>HEOKCEn5xow4+imAyUd z*7hxJB%PQSg4+nMEL!9b`9b~=ANhtK`RnWITw7kcs(g6^S!`y$kY37Pc>V+TvrjXA zc%J1>$a=8Dgwq`BgmA7yP14ZC^)6Xf_L7?(Cqfs|~Q@093 z&wqekb~Kl8r1Kw8zenr&55d6P?(m%ZzH;l5W$RY2?JtJJh0flBP#o?gFX0hA&pS?C zi~!Q-WZ{84{rrb5oDPb2t%wKz4uAdw_c!Q&@Lm@E65dzfy+M{8PkNfl-?QNSheN8I z{}B9#JpTc%+amiN_w^Gcp2V<@3+)#8pxyG`OyPImD)Fr{UHtue#Y_6-!_2>wPrAbI z*DGfVTJha8+|=)$4-wz4(=YpbAmT@Sv%X)K)D(-ZT#LxP!i|QiUv$S&qk~J`C0npslXt_`y zfg5l{JB@lN^DDl4t~|aOdAH|3Kof7h3mkzPa74<0ukZNRa|@@3_kci*nc&J-+iB6AX$a$E@nIT%~$`&wtoktev4> z_V@bGU)K5=NsXi>zurgaXmEiea08C+^L+jTmy^j$H4gyj2m!t z|G@n&*Gs7mu@36Kjt8KcTI-;;$N3M`KYm2~vi6&w?L5J~GNpQ=qj=&m5;C+CyXYT* z8*ub^+=O~5vv}-$=fayK935wx^jE&P`4$o34%S1~`43;!p0gKDxGr%wF#zRr1H^up zcL)@236aQJ2W8KHkm>`DzzsM;xxg3xx5~}J5#v>=k7?`K_TEX?v$+oxJU4*paBjea zpFU6ZBXMnya|5P6-g7_gUtI6G0i~r2=FYtApYQA6@}C_6=o|4On|s&s>p)xespnSo?L?jw>xWUas2CsNvul9$;xdHFl=O1T% zrR#tP&i?dg=MTHz*!<7`d+Mffvkq8r&pDsneCL+0&%g7{Q^mUYc>OvlvFJGp&_|>B zNdP&DJ!$6w1$+GgAveVzWvg~wruR-`oxFT6j@Y&?P%~^btZEaR8 zvEL`R|L%S=zu7UXJ*m}C#Ltl~a&fP6c3a*k(|%*_oMgZB=p&DW=UE(f!QLm+Z|2OI z?R3V)jW-++y8Dc4bw0AU04wKpW{&`=fon;B=bGi6y~`V5AxLArfKT6I8e8so*L@;M zmAh%LCc2X8ru|KK3&XSj0Pi=nD5OBCM2ZpeEumacctz~zJ9~oIU`X84Z;C^wKYm2~ z9Or}LM{%c+C%lj@xCn(D}h`r|^*6G>zQzsI|QT-RMy#S=vwHmM*7*iTJ zSZr6h$C%2o&8*dcsR25B3_rc^7k+xBD6UC-o)r4m*p(e?r8Mu1g=a2UxOnNph370> zXo?9#8n7A|0~#3TjGG*rG-4lCF;mu}zWq(=pv#x< z72MAy-!G80WnR>_(`CBjLz&V3mZeuF%o?KKfAR0h<;BiFkn+ab-?$Hr?N;bKsfz{Q zB7dc{;_J7HH&~UDw9{o+GG?|fEF;R}di@G%=IxWS7uDE*yb{{wQwy4Nq4G!tt_f1KqH#~I67-`_f~Q|yF4zh_NTfDvP zpn6UF8B-YDcZi>Oz03O@S7^^;%3r&PBiaL+ucme(m`6=Phxj4SM2ogUv=@SIgLoXw zqqc+SL~P;Gz5Fp&+I- z5wuK|+os3S_zvYhc!s}2{%Nw`HV85T+$W9=)9>^7H@bekrzBthTW5Zp=FP;ItwOk@ zVM8L$6NQxO4;Aon&LS$~vIvJ^&HuBD&>o!vbc{nYM$xQMDe13hey3r7oU^cN2^2iS^r+OsPFTVQSa0!ib zxLp!=Dc4jAtIS#8R1RA}x5Rhy#y_64@#y_e`u#Jvwd-CgpmNd{dq15~T&O4j{vc7B=iK(IMa?R{FQWVv~1dV z+sPF6)ZPz?-)S8bN4;hrc4}CDyN)?6bW2>^sJ?cIh^U{R73w8^{7uyuf<8<)EEQr=4&v$d?l9*AoUB_- z1Ek-ypGiab(Y!D8TNd{c3pFlyiSIKxKFZYs? zn`HS6g6i$@xuSfc)}d^Yi}DG6%_^S*wtT7urp7(WlkS>iC~wej-tviZm8X2t`KB1p zRJi?gTaJ_Cg-P=K(WzR}{9nuR=}o%lig3KJe0hcH0hPI3!?19!k1`pt_s+Mj0O z-DdkUK$OE(F;?$QQs52!=E2()_1ouui2Ads3S0D3?)9_6dg;#PertC*{`Xs@>)v|2 ze115G-zn>{Ka2glOFFlOLPODIdkW7d0!=6luNu1`!ves>9St6t^4Fk8aGmzqYLHIeZIbOc|Mg(7(U__0^OQ@1ALWV4C6y_k%Nfcc-p76h&##v4?`!?Za)=ap z&(&8BFQ9TLc;iAj1fOP=!vR|kq4FHATK3d?lN99;`psJoQJ$i5$mI;>5c2_l&E?SD zPqv4%we}Dx@}I4*9R4|#L%gUIwQQ}0bultbt@Z#hJHLODb^ zLwUe_63Rop-)B)V6YpU%-&YU3sLy+ud9xwVfLaFBdpwU_!tZS1g?_0#NG!rR@^Efh z%rmwB#mT$TJh-#&OY7TT`ryX!UU>PvxZ{X*ZzY7EyxX}Y#21)~`RzwSJ}4_L&p8$? zE49AmbIbNyKEf*BrIru%-lQh{ri)+)cl&#BPva^KaXhZy z?xPD6@53lgJhx2gqsw$DZDQ8g0iVAU_mVf_wzRao{;#Ep&;M}n%{>n!H!QKQ z$ZQ)u+gD0e<@!tIA?;@p6Mkf)Lce+X${8X)zKNi|lFDxMt;DPR&BO+N2XsBZGdi7Z zU4GX?ayN(tURZPdeJ*3Iyau_0T{Fe$A;T}ozSH}Da_o21QAZvb|6fq*n3eOq1RlJ>rQe?a;Kf5_`% zc}NH3%`75J(BK^o@(2Eq=dwJc1M-e7C=b&KkO%&dH}GqzPox9#W)+k-k?9~0{2|X{ zc}NH39am5u-d8~$_(R_CuVns^4#+#cpgg>pgc^ELmv1;UK`6p zIw0?ag7VPzKpyx*UhqqS*`4(QdEgIuF3UqY zB6$TH80;McdEgIu1J85)W4V#Mf(^``TK>Qv@;sJ@bVTwBHZXf>^1vVRhJV5JkL5=4 z3N|qGzLzfO6Z|31XL(3RB(GotGg*p@PG>fj{I8Jj3;m1^1vVRJeG%aK;F!P?aRA0 zdEgIu!$0Nv$8sZi1>2W{HF@9OIZTrW{*V{^gzF#6jpP+OdA}wP{2|X{c}PbjuVDLfq$Usi zA#Zpq*FTmU$t&2t;FAfoXW$QcKFdQoB6$Vd7gSrw1AoYCV|hqNB(Grmf@%wS;17Ah z6I}mTF67NZLK%;|4yrBWfj{JRu{@+Bl81!m6~ysSZ6Oc*Ay(FT3whuVc^=C{IwE;UXr1yn^ZBo7I#Qy!`< zn^ZBo7I#Q(kz; zfS`dq@Q1uEmWOmi@{rIv<(16bRmD>4|yKTLpmaP1=|;VoDF&44|&6nas6Ytk-UQK3xAXw2Xonmq7_ykHC0Kb9NGE7-ow)8v6amSRFMM5(By$XMM5)Z~Fb2VoYVyDz@`8Wk`p0r3c?H{-#hN_uhrBM9hjc{p3brpx zGr$pe4L^I0C!5y>mqzMQAY1AoYCV|hqNB(Grm@?lLL_(NXsL#}@; z7xGRh*uH#3lL!8g*TwRXj!0g?_T_v{9{59^%kq$pNM6DAOxk!@-{*c$j@{o>5UcvTdnI;eXA9yvs{x0{*X7Ynd=|RjpY?4|zVzLpox4#Tu9{DK3%+{*c$k@{o>LUaJftI*SFC|qrO5+-$a7g9(h4@bO zYhSL=4@bOYhV6SlL!8g=dwJcBbHaJefcX* z9{5Av0Po9zbj0$CwJ)F0WnXP|?q4)c*)=JRt2@H{hL4tKzNE=Y3zO-~-peki zlV4Is+TKOj<0=`G@S7rnpkK0sMHSI5@RRl<#fSd##)p3L#z+41#HVkCaW*T(H_MwN zK0iSIlX>w(vBP59-G^ z#C=-aGrHH5mz0b5!qiUDx?b?6cb>b5pMJUjb4*xYC|w=mA=-yOihIziH2S%(?UEA!4_~h|S6_M~0w_$qFVPE(0mlFCS9jZK~pWbs+ z`>7PTuaw*;WIs*gn(*8w?zi!d3Hd}?Bqi`LU0DR;cP@Kv{lw65_ij3w+#`kSYj!D8 zV%;ImjnIW{5OWea{J5azb z>F&L#cU@l>@hx~Zim`X41h={z*_Ov~ zvh5OzlO{i=b*Nv^f8k@I!`u=DWbw&omsG^KZ<<(o-P++e9bsQYJhQH;;sJh$gAV)A zN6rX!_4aT7u$_LSL)?=N)9n&)k*+sUoXYkW} zAodnU*vy%?NBnkamHk0n#wixg|B@7HT4acn?fyu%Ti&D64Yo+vf0+FbN!Jae>pjTN zAK>Skqzj(@KFRq&(o1NcvmDd|`WDE%fQ9Lz4L50INSCb#S*l41ufHBZzj^8b;f?at zEWAC7cM7gqLG|Me{pQ6R?MAckZnJo2ftwOuKi<%9UcAviGz)Ln;+=wPR#5$TL%(_P z4tN~UEWC$j)c37ymL|qVc>Q=ozj^V-c&}M_2Nv%-ifa9ML%(_P#<;#&cn?^-n~FE| zn-_1N?YCy(?OVK?iZ}F|7w>@W^k(7RW$|t*-q3F?yfHuSx61c21?I;uDff1+Bsm4I z%vUuB7i5@07QTOV@0a-;t@FsEHD4K++F+{zs{yNl6b;aKyA}R!SI&;6&1uj)$@che zmlD|GjLYon#ABWxq?qBQ`w^a)CyD~aO}_bYm7@0jR1pO~#6~7YVtyPvlWwKted2^@ zy;?XQ#QiqLqrj5Vf_piiMV!d~O}e$_$6Y%=E>)K6Z+-6<=(}C$m&S<_3lbFJ)Sh>K z9OXhU2gEH;Ign*Hdw$%rb_uC6=T%=hpzn5}-@JB-yI)beWZvqYi<}0Q6Q+Dt%vx#9 zshynB!nVr)OWEFy*75sj%~wv0Z8WO^s{yM4p#d6!G-{VPnYMu(Q(;^Mg=c@aOX~^K zzpVmoD2wDpKddLD@m9unyNUE|2fHNvwk>Iwgy|RQz<0X3pT>aIeqns=Po)%Gk+e(D zZ|mbxO3SVhHSjRqT6RhBb-B-t_nn!E{ca-G_D0bF={N1CMB!U}w+sC?YL}o~=;eUK zjf7p&X6+Ia#LSSiGJv|5B*J&Q&~IM5#NWNRU9zs%5&N7Rn@mW4ADTIXVyY3+P(av* zyO+!MuG#y6m?>2;tam*jaRc7qgD}-kbu+G=W*Qm`Y2G?XJuk=;+{V&vsjUXA2F98O zcEo*7TBY<>rmNc}#y%%(mq>d1x4H((ea1hB!FE4{pPhx+?K+2 zNzb}9%R75<2!NTSS|Q1%MTvcgaz6sMVVmWzv-2fIb=f?-1m(G!TQt>{&T7DFVC-pt zx)nKJg1pD$7KGH=f6>uxg_tmUU#tX)u&n(TDb~BysOCM%(tlwr+M_&Uz9hjV-+al) z?{hM9W*_aDcM|VwQqug|d`Tk786GiT(wDSL&~NMGQ9_yYBQNSd=rUd1E;05w;c>IS z!OoXR9aj5G`c3Gd^m0Iw1JHev z26hSbo7XP!@09I8{p(BSukO6OIp1;7p1vb^g>9DGYwePvI<2t(^EqW2ZsHNzsA}nMKx&c zlA@ZkbXEga1KU>vie17TNjz?eQ!-K9X4xe--f%$JXT-7NyO%yVTi6uT535&oyJTNA zs(CMLpa&zHL_ChZFIkr33LtX)#AJjiDlf9kGD3A+UPZPYFaxLoMvfF#HD z+r%H~mSs2lJFWq1mzW@ChUoaiZxX;RfqwJaCH{tn?2@y))?Iv&nVMW7Xy%N_hZ-Rb z1%z#?yUN-nMYU+{lA@ZkbXEga1KU>v)UC+xxb~2PQf1r{r(}}2&9Y0xBkD84ej}#9 zU0NTUZI=+$0U%9x!?C@11euXs8gFITB?(UX?2?iGjw?(;n0EOcm+sfsyeE>J;Su|B zbtml-^xOJ)lu(Z6p$HSYOt+R@;@>j}y?p*7` zDcgiOT>Tz?s+)1`l<`OIZ}aRDHD4&(sylLdu$-(0tOmw{1}0M`d7ogJ-?Wx-d+ifU z-*J(A`fe=a;`Tjy8qJqPA$*gT4|CJ>=d!Sq@1)%&~NMG(d6I({h;aUc8R|3-DA6?x;E@nseu@C75_Btz%GG)v+R<* z>)uf=^m0J5<9cD@k95oOl09G2W$ltQ7&F4^D+jbsF!Y<(F7dB!$S$D|xq4QSu==lL z)1uJ7SGga7+pyhtm&<&P*8OxxYraMRf6KyZz-nOZXh8HUl@iTSOsKNHgzShJ%E1m)PwN+E-p{a!rQfY zr{J0uR6pL(Z(h97ZZr$;;Un$-E?MZB;q~JU{pQ6R{X?_x4lLd#teGM8;|=}h#oOm` zK(p{3uy|*pYd)wSZ|FBK-WcyS3vb`zZNi!vQa|3%Z(h7Hu5T9JT^8?5bj=6#;|=}h z#oJ^1tyy?`7H<>Q%#ix=hJN$n?X#WUEWFz+-kIo{59-Go`mKfcyv2^=ix>9pRr0+| zk^L8Y`n${|=MTwzRYUU@BY1`Lj2RVle= z$$mPT58{3s<5A3m5z6uR280P+rdw-%JouX2=fvM&*Aq$|R{M*xue&A<=EtF58jngW zC~kyPd*1nRlncEa5I2+wg|AT_-l45hgR|`tT2C0Ra8#ow@5AxD2#Q=Wwh!f3u}gT|B^gmJ z)AE=UQ=f0Dh>oAQO{^zu=KhQR=}Ef;{kA?HC6srMvP78BWxBQO5})mo;96^!NF7%D zi?Xk~CJpQo=(kb31m!|62P8Qz`zHP}W%%|o+b$V?zx|%cL@_Hw#~*$(KI{_cH?Ljd z{-&s1vY@kfRe6mw<2W-h`9d=7lK17K&l@Xjvx3uPd)Ms!K+Hq|Gg)Vz+WVo7Wc#T; z*YAm&q?4|G4?oq-xOSRpXe^|8b_w#Q=NtaU(ru}&2CN3gmIfv|dr*dVxP4BlppdXJ zDDALIbegOdetX*`&D`h2^^$f8`fYtYnjAc!A2eOvE;05wVY@`vwe_cI0R6J=nsH&5 zc&y(nyCm;ECny(sIUvb#y)f~YDXVRl1lBGwLCg%%@rU0efL#Lp=Cw=wXA9dUy`7gj zN6sj8R8^S%WYeN#>2T$K1a8B2-#cad*d=itLCS%dtTWawN$MlEx72XlLxHr-Y^$@S>Cdarm!!#8c<;1!3HCP0x6etw{TKZONxKC7 zwmu%E!9%>KCGap^-7cYhPL5oyd|KbR-myV1H1W3=WgH_A4m)qvH2)qq3Y3a>9=ZN%f2SUuIh<8pLc zAtsE*WlE3;tG54QjeWmJmi`NwQ@VHD^(7r5&GIMLlj#0Dc8ID&x)WNc*nI%!X%1xV;^|kU(7D?=OpbC^xOJ)bfoMOpU2JKjD;!t)Q5Spem!Mqe<$wf^gk92Q?UD$)+BZ7>@SBvdOQ7Gpc8Pm`QM+W}>dwCY?q#hD z$}4($FE&$DGbGb4IkF~cT@k`QCOFaBC3R^{iEQl>MH%+iYQSn>2i8DrmmrbxxCJ4# zzT(?!8i)IkQjjyyYb9$EEq_3!6pxflD=4%$J;yv`f%$>*G;M%Sg{{_6O{ZxX{U zfqwJaCH}g?c1f?;j;puT>+D@_CMQn_nmHrxQAS8Z0byD7j<H0^q{|M>2hfCKx zjGw=kpC2mS1Y^ZRdI`@}WjUw^^`E0#T~n^D2a48f-Ku3py*KGl51`*X^?>k3d1@Bk z!$;VC2u-xILR5^^dow=phJN$njdr71cn2156RgY-6=U_@BmmyfZ(h97KNQE?Op6*a zVDVPfQR804SiLvNfH(A;7w>?_0nNhOw|JXiWrnC2tM?`W@P>Z#;*IfMv+(Y+c$;8l zhNu{;_a*`GhJN$njd6Xm@b)a;CRmvvD#q%)NdUZ|-@JJHY`--N?>39K307u^im`fc z5&&=LH!t1++v&~1+qHO`U}c7=7_0Xt0q}-?YvCQ8qr0lX`SC?P-D~>I^yCP^>vkjR z3?rnWfbcz+J6*ni8m;?*jMjWLguErT8n7A|YZ?%5m3PSb@p0qQd%S2qoPmn``b>uI z2e3Qi{J7teK0odqBKv6?&xG?q+;3w%3LTItaZh+lU6^jI`SIYZa-S3E(z?|&ppHS+ zdy|Inqj4PcOXE?A1;veU%6*P5%7tDIh+Cd=Agf;X{P^(UcAkiIneS4uSMN<~*d@?! zExUx`6QF1xnl(S3Y(HpzymjuOVe<9-BxeQuzzsE!!>kAnE$8()HfO{sW{N z>@Qt+KYsppe!j1CHP(?A?IGl#9*p$-cwp;6r1RQ0YFSb5O-j@Q=r>P22>U-SPtC%6 zz~XI!l^LR9tlpahz#IC_i#OVhX5sByyiKq&LsX2_dy@coL%(_PM*mP8Z!;}wNSDQ1 zRY#3`6=U_@Bm>^iZ(h889tSiFZ_nawf|VJfVyxbq1i%~m&5Jk2d(FbT&Ejo>l^LR9 ztlpahz#IC_i#Nvg&BEKYc$;8lhNu{;_a*`GhJN$n?XmsVEWC%`XWvVhXk~?{7_0YY zeBcfJ=Ed7*JH1(W2NrJ=tjrJcd6~(5wsmTe@OjtG#7EP2(AwA3(p2@u&z2ujKp|jg9Cs-CFbGzMUVJs>}6P9fPX( zCNbeh^W)GjjYk`qA4j>+%K=G7nMlK*o>RCiyV>*O1J*8)s>=0O#a_KPiD8#OzqRZV zicf$B+&-A|<4*MbkSZ-M18QjUKHdk<(Too{KmOjZfa>`{*^#{H{3!h*+5Yab-3oS< zuD7RjU6=inr0Y+RZZM9YxA5~fkFUDlH`+tUK|L7h`ElRY10CKfezmNq_a+JI0rZ=v z9)$fLm#1do-DUAM!O9F#F;?$Q0^kk(=EWQBMziqtEZ!zqnIS61>b*$-yrJK`c%y$P zj<=Z>HKfhrt*WEOy^67VZ;}CT=r=Fk9*+Z>g|}<*Ho?jaQ88BUO#b~&0-5@;12x%xFe4pn3Nxpv?t@FsEHD3)OZ;7o2tOmxK2E=rQogZ%$^r?aYe&B@; zdnD$^{d?2r$AdRyKTYEqnjb*Fjq#`m3a^oxANTD1IN;6Us$)>~-XtabXnq{}r4eEy z^W!KNdO0A;C=-p$k9S$SgjAXHQn6R>O>)>J&~GifgyIvRXdle^agt|VaTze5Aw-?q zruGBvnV5~A=j2>Jz9*O|F3I-(Tee&NtI~B}mu~P1`~M(a?`7%wzvJgG@$=tES7DpD zMSBQ2s0SlGKknIjkkET|k6QNCdy@+F0Q${S55oSB%Tu%PZnJosU}c7=7_0Xt0q}-? z^Wu$mqgi;n7H<=*%n%i0_1+`^-q3GeywN`t$Jb*$-yrJK`cw@ZREW8IS-X>U?Au7h|y-5JPq2IiCV_e@X zynTze307u^im`fc5&&=LH!t1++i%UnyUXHjf|VJfVyxbq1i%~m&5O6oc6zh$_AK5e zSeYRz#_GLE0KB2!T6klA+&{hO{P-!|t5=)ps3RnPJ*5%N)cg&4!}n?43-bNb&N+`v z8c^}q?;U5f=Bq*MExFZz)xen2z+{ozN@<$-TkHJs(?y&WD^`>qedLjo&GxmL(y0Aa zFR_+d^(yz9Hr`gE`IC5VP0~x|NduYuQiJc!uT&&~IZrihNR9a4)eVPQWF0rQYLp!_RKJawu{BxSbzQLQvI5 zdQJP8M1&vBkGrg28jr?xAi^j2{^}?fdO0A;dCGw-yKy;qWx}i>`u!LG+N@nds?2$% zl>uOyWC6Pb`ps*XxN{q^OIqjlmOGOP$?romXHXn#gftWow)4R+cV@dJs~p7PJEvU| zr$4>DOE2pUl?F>}HDEO`nl&JXDV5Shk;7VcNt=iRyX2|Y-wgL^f?Hiddb^Zumk=L0 zZfX%%Dp%bOiTrwW-4AZt+b$W2?{>X=(!bkvpHJE)=(qLp=t$Wm!9Ut_pHPl!yIK9U zNeR0I`fb!MLAlV&0ZESQ1?r{g*0M`nYnPZHW`@*P4yFh`&~Gifgvt#8iuM8D?IzzH znF(v*QXu2S&*QS;IJ?BbN|}iRXN9+F-YB3FzE?u5U2&thB*TAJwp+n7()FH`uKQE= zKP6rNC(;eJ^7AM7`H!Wmuua^OUc&DbsJ!)A|?@c1u=g@DS zdO&!iJT(jNz~XI!l^LR9tlpahz#IC_i#OVhX5l?x@ixKA3{f#w?@a>W4gKcD8~sCZ zyv?+zA-=_1RY#3`6=U_@Bm>^iZ(h7T9tSiF?=FkC307u^im`fc5&&=LH!t28?==f= z&*E)@l^LR9tlpahz#IC_i#Nvg&BD9Q;%$PJ8KPpW-kSu#8~V+Qx6Afhv+#B;-X>U? zAu7h|y-5JPq2IiCdu*pS3-96gSo_;VD=S3BSiLvn18?ZJ7T(c!yR#aZA3thAxxBW# z#+fmrz*$Y1^W*Y8m6^waA>n+3|G0erG+O78M{B+cVs6Q-2CN3gqz0&4k>Bl3nl8T- zROd*bl3m12)_1!zXU>e~s1ZMQ(GSNVB(K_cyJ>SO3N^_}{KOoOn5&obe(HRWl8;3C zw!{2*m>zW4G(W!Yr!Fk@b?wuB+fBbenZ7a5{X!@6tN#=YM7NbvutoOMG@c3PgSg+u zcoaIIw8VNPOz1LQSpwqkHTCayeV!i=CNHdd?uz6;*8igH>#nI6!Y@4ciS@Yc*gsU^P%n1JtcFx}MNUjQf)k(Uz&9)$J0pfvQ|2EAbo4c8T|B(k?;2 zt&c}X$}aJE-0ZekyQE4P=NiVuU+D|7PU)G?d`m%Zd*i6c&1%)yc(>DkAGUT$QB_*Iq^RaBoz;NV!1mR^ zcp#vSK{3bE%66iOtUE=Q7h+WdUr10s4>hU02 zc4lk-2EAcfbpvac6xE}(ONwgF(pe2y4Qyu(h#^R&bbt`r+5+9l|>_3zhk{?N@3?{ucAt|Z2GxAYGB5Q`Vde$91o~~% zEE)EIiP(`px;_{36&cH6zv1{IYClxyy1rMoelhO z<1&B{)7{?;N`y>G_~5CO(C(3phY_r}6dr7n?UsALbo~dU>wSX#e<|JI3hBC+^Ycsj z`8w$;Y!kPnmvG+;mVc<=U&5O6sc6zh$_AK5ys8#&+;|=}R!aLgMWcQ-;`o7XOJTZ-Eyt6CR{y%!5#nRdyEg;}N&hGo@TYweO$?c@lw-|gm5l8s_DU^TEK zXg~~8DkbrOYLma)RkNDCo{(b2<0h3Ry63T!>DTFWKPd8jw~N^M+^HfOexN{yJrdvT z2H#KrZr5Lvv`f%$>*G;MORSIV@o25@c0K#uF5u1K);As{y9D}e)Gk4}(8~cOI!`&M zWtX_tF3EvEqSRLo!t)7PzqRZVe7B4C0pIP)^$U=Tbi{642I4UNj@C`kI&$@zIrH}D zJK#fIdIA}hQqR8QPa;3sJ$kC%lbJU3`cb3*5)$5xnKvZ2E>5WveLmkP-CIRqt_cy#K7kW7$Zh6XqEW7a>p?+53@H9J5M7qp(>G;EMQo}BR zerwq!6rTV^`+&J}$=Q^1;|&LdbslY#nSwZk%3syF^2rnzGRT#FmA5{>NinGW1?S3k z%RNWB{)eUOoz4Em(hU|#*FB4$e}JE#E?wf6cA-6l9MpqG=gPl-+<8M6?X&C9{d4af zJpY-Chdeu1o~HhcFtx0x_hxj$kLJpuU#bUsS$g!5J;ZvLUxjn!H{S4JQT}8Z1okLf zMX|3nS030l&;%|sM8#XZHwg$onk$EX^R$69_CXucEW8IS-X>U?Au7h|y-5JPq2IiC zqpv8Ax0x0-#J6~>>ZozAVyxbqWWXEx&5O6sV}fSk-DUAM!O9F#F;?$Q0^kk(=EWOh zzh>d>S-efKGDB31)q9fwctgK=@x~axS$MZuyiKq&LsX2_dy@coL%(_P_Slwd7T&JK z+XO2!M8#OWHwl0@^qUuNpKbPL;XQnaeJ5q2l@+35tlpdPfj9J93vbMoySK`BH1*Gw zpV4`FxwrmZuHvMtub|>kaYt(cNAL>Yc?GA*e2&&R<cMk0FZpAtVY7kx54rF3VU2Nm{d z6o)RF14_$d_^{xgse%qaaT~tBM~5xv%0tE?9V5Kol_&dY8qb7ltGM6Bcog}hw2Z`l zSHV~0c~HR?yWdraXw^R``?_lq5q>mR4*k-2w2`@Tm&=7-4oEV}gshtsLg`Dovg~Hh zl?T=?kt)meSH~ZIlNfdh^qbc%@i!Ebg>T%YZdwA9!zT>~-ZD*k#b^208HezWY7gFmy1%YTPBNBI^W z`T@syuNRaHy&RCNxL$zHbZglq1J*7fRpz|vD+l!b8uXjjE^)6fYL|SVvv*aww{>36 z>g73eqeV+LEy}}aV=N`Cf!na$2lHe;N9(-8Xw6pxxLZ=I0jq(rrvWicsg$l4`JTXr zNjz?el|gn%oA}Wfn&u>)di_n&?Nmx|;&_{BQA64+-l{rk+yh_THOYWC^qUuNkH-hi!rQfYn_y*z=s3e~ z5&&=LH!t282Q~}u;i-0AiiuWM2=LWiGd}Q!e)Hmu@qV-L4lLd#SeYR@&hVQAz#IC_ zi?_>mU9<2Wuy~tbWrhG>-8Bh-H}snqZ;$QwX5sByyiKq&Lv)QO^&xG?t+;3w%D!>h|k(xITZkFo|kw@!R z(*VYwx@*!9e&ISp)-R1m<2gs7r>l~CPhOM@y&Mo1lnFVfAMyqN$gMSR-ev6)Qe4hU z#~*%^9CiuxTgxt?_yj202h5vG&as?tb{VdiXv(g@~%M|VvE!k+p<=r`FH%J_>n-W+{1 z@yE6UOXB;9sP96*hBg3gOVM&r>zfJB_Jt;hnISs<@S6mLAFXYHe)IH&;a(fuhZM)# zOp6-QX7N_lQR5!?>aIx!yrJK`c)L7SXcpeC#oGicGepN3ev<%rL%(_P#+a~Kcn`PQ zIdl`PtPtR4KO|UXUbe!Qg34k~Bn-}kZZM) z7$FS>gtn;nlzc}uTIZTaYrYyn-V$34SPhIZ4eTrOi@kc~tX(|E7OSHAo>)#aHxpCf zF0BvFUVBb^^}bDzP6WB>k}Ulf#-en~HJ6e|$wwl6luPBAjNIVgse&FqutJ9|=g>Pu z8styzfu;Ma?T3Z6kKhbf?1}Xg*-z7WCR}sH{Wiv<$S0*Gobn1uE36OGl_dc3DkWp> zInNuo_uIXCfkL)R`c3)P*`qzZHWMZc`OCNb<1=$FQ$5(|nO;nbdYuU?c3y&Mp?k+4fVYnPBJ zb6z_B@SEhYOQ7Gpc8Pmi+KcuU*}_rrf$@S#RfB5?RlcY+4k`dCL6=+=h0E z_hUO>QdF1CJ3|EJxtd!v)t1g`z-nOZX@I(wc)oyO7Ru{I`(+tbAld-baCM`q5P z$?xCe`0*XusyKvb->!PK`~5Y)<`w5ChVn=zeqj_!cgB24fXKmE=l0AYSVv=U*qWO}2KXqZLuWO(7+iv>($)rQwFLa`vAtGt3{}uBkk0>y`6pK-m00x!(`Kf zP~k36?nmG@Y_q&a?R-g5T{h1y$tvG!c+pf_I;#Pzfw89n>Q>_U63Tx(Zi%%~eSJy# zSW8l)ZHjr3ylV3$2dh!dd+ML`Ul?mw@pt>qm&A$F?2_;SI_zTWOJd&HcVfQe;iO%H zep?@pQd&l0zQkj@#NS}&OR{mn6O?`3#q-#OT>|}P*(JH>OSoL<<$xr{^_$W~m+8u~ zn>}ALe6ZceMCvZjUmbt=O(b^?NHD~Fp2CN3QuLh`Fk#>p0AGF8gmN+F7#ckGiTtuJ!Emv9}T-`2djm4(j z={5*LXpvP)dHOM+{yU6NKflVK?Px|@teKG-GDZ=-ez%7tDI zNLF0GDP44#ZY{ecuy%=5U9P`6{_vZ`uuGudympD(Qq(RvbKUZlWoO2Wqs_$R3(2%g zPRU1~H&$3yy$7vbl2>7RJZqQe8nJ$>0jq%>Qvw~lH655YzcaWyLBrEa5xfYb}jQNs8`Y4xac}y3VFLbJihM%}ie8+_|t!B>b6Khs( zIZ5uvMYdeRudOf1v))9^mpqWPOVDrY<55DH>^6B3CUlvuECKO*a(zFp;LCD-iF=i` zOQa5~{YBZ=U6TfO3G~~jU4n95Ul056)W42SpEq(|^huzHYxZL}bb_x1zeLPBO8Hw+>e6~xxwI#dm9YANg$uckN z1cjLw>=Nj=QM&}?LN5m-Ij-MGi|NX;8{4P)x_95&B_@cOA@!95T3-VF=Cw=QRfX-6 z@*1agk@!>eLNjO3dqzk@0b#xI2CZFEREyRwDXKY3XEk6ou$?s^hAEZO0YYf4`I0sf z2m5h7_4=D)PM}hPTRqJf_aIjGdbs!JMIBEjEYJBLR0imMlzPpad3!h)OkvZ*7aeildiEWE618V&_bARJ`lDS`{@m}& zcFX&obc4I3>)*-#+okK?CSC74{QTSe{9Dr1SVvyc3!Uc#Ij9HwL^0GG=*V=(DYZqV zz`bYd0aTI0jmrv^llW(nV%`w?%~KD;^Sih_H4E=Hi?<0@W{8ThdT$Z{Z|FBK-e@

tMuzgICU?Au7h|y-5JPq2IiCW4zZayaz1aCRmvvD#q%)NdUZ|-@JHZT;D9beT%mV zR%VEbv3hS30B`6wFWx@eZ~vdY?*Oo(y82%bWvpPsB~i2XjTOa$%D%T0^{FTb2pD5z zg@r|`uvnw6eeB(Uy}S0BSZGR&KD(wUpC-{HCfOu@(R7n9@%_(l?)|;#`(|G8PnJ7y z_Le#4-aF@aZksc2rdD{j8N9<_#pZZ1_Qt~k#GCp}ig#Y`(`$uy&fpyeD>lcAu{RzT zAl}q(DZGb|6<@mC53(n#`!Z!bx7#{r%8Xfc&Yy6566APqJNeiT^`$CKIQ|CfL3N$| zOWYr-q_+Q`e&n8dU6tTrWQ_)l2G%$Yh!;WmtBrrWeC)RoQ4&kxgk25)xcy|be>``; zny0azar{Ag-sXB#z;zC^g>mJVKsMG${;-sP+%o=grLshSy=zc!JS+_S;2)=cu^v@e zfVYTKCh7YZX%|5|Kxs+rK-q5mbGwaZUjkJoJiOR@<6&{`OQ_#c`x5X2P>v5l|M-oI z2Ns_n^48|sfH&742TynY@tqR*#~l`bv5x)a?@{BHb+-!b`&5{_Q>SlNVg6PXTDR!) zH|g_>RTwC`nB;iKde{%D?H{*HKPc+Fbi`}>-gsD&{eb#S(hr>ZPuo+i@b22m_|L*< z#pQT0_Qu2f#GCp}iZ{oNTH&2Hc!$A?&GBOFjfVw@H}#toZ_W?p;~j2`H>cC!?NvvK zxEEt@JS;}Mso$h{+j<>PE4*!kcNnbL952S+cvygVQ@=^^=6bJIc()n6!(heccro_I z!ve&c`b~;A*Y&l+J7@3?gB6?O#n>AU3lMMWH!0pZy??6}-i-$DFj%oUUW~o*umJI< zev{&D>wS8y@U{%zVX$Izycm1qVFBVz{g%R;{&8!9x-V0%f4pJb+-YUL-HNx{Ll<*c zD5s(f=YCrLJL)=nsvo(hUROoP8)2gXqk%O|1L6kh$YMVGFY=FLj`EU&8T>h`?|ZG} z8H5UcKc0Ic+COgHpyp|;XW$>;yv_9}bb_N={_*@x>V3PaRP(oc4S3g}-gsCF_`yF; z{bD^@iGQ4S5wru;Oq=ld-L(%DZN$Of0LS~syS6lbBBiS&f4$gy<6&9uOQ_#c`x5X2 zP>v6N|F|~QMp6>kPeH&5!D6tt{6Xa<-s;s^$@cl)(_BCqU%3r0z z+;u9nuGHzvRcK$T!u-Yhysgh)s6rNOZ=CVHUxVWz>tR1g?;mFk(P7^7gJ{J>=F(1r zVVIfyfcj0+51jc=+f%LZ?lgFZ!HUfZ;>>hdfOu2CN%7{mQ7gP{gLfFL*c{>;48sD% zoBBt{KT93O^SC;@6&6Aci!L~1}ip)_y)tU0P&`NOW{rbxP7>~FH?qp ze8!w9Z4Kkv=g*yq%F-XP{&9T2yo!f&kJLV2U1v}ABlpzns-hj3qKpQN270~*WM1jW z?j_e*TmQI!PVVlzPxReHy_;8>iTNo=4oZ;gss3^6@o4{e{#-RrV?6`^0OxJ4N2$wd z`NwVJA9rjm*&n~n`~8Ui5`Z84#(Iy=Do)v4c7`x4j<0Oj})^p8tX8e4xuT^sP{dsAQWCL-c}Bl2KN+^C?AHzRnE zpZj7T$DeKgTZQ?5snGhT3UmL^=l`b9|3#nwvp)Zs3L}blvN#^H9`=K3`^Rn558@z* z$WPx7sNW?0z?uKFJ=F^DHiLHru5nrE@uq&0;>~fRR(R(O-f`eYJxBQoOkis1@Fp!8;Dzi2U?;Q@=^^w)J|iR(N-P#rV%6fQ`vYk2m$3 z6z{xV*VhW~yumvL+oiL{XQI#3%0jMiPFP82`AheegRb@ef7R9h%-sXCg*NL{QTPy;Y;0TAxC+qq}KPsHP zV%Osf&mr8RrSM!%`^W9cS>Nw9Re9#GUjwxLVCd&UivDrx7wgfA{Nvg#f_8wqXcJNR z<2>?}?YegGmp=Qf0R2@{o7tBrRVDfx#GmP~Fzmqj{K&#K%u8#$u^qk%P11DICSz64jlfsngyL5@1`ev>a_ zUxL~Os4Cn-g|< z`x2JYUD%P${m#EH;k+GOkD@K^H(0PP!VwNj?Mp1ZFUgNF`x2$Y?*2l*Q52TpzJ&U% zbYDWd2-*Q^cKdf2f7PfSHJeF^oObYEiaQue-N;ev{OPUyTc z&wmj*g=dJXcsOI0{adpyDOaavUsA5-jGWPc(ZH9i0a>zkWCu#6rTiuGdc~r@!+uch-xFGuZH~&fpz` zZB%x8ys6)$c<1&0tyXw98oZ-`jmb-oH}#toZ%gmfYlXLE@Q%SYDmy*i)Nd)g`FldU zp-3^(b_LqgwuP+xW+o zLKFQ>w;pxgx2yGw^{D%N2+G0Hk$LZ~c{<*ly>jKkCY@jG1GEF$MbHkwPn&Rk^~Lg8 zPs!gCcGVj{kjKT#JwKKuRS zZ~2RKQmET{OO0E(H&mE^TZQ&(I{k_Yt-K0zFX{6y>hmjA7*V{F#eTqg*bl1hAJ3b9 z5C=g-e)@hu{U+%L&hrr3o@#}6r@=b{*SM_ocvHVg@#eTuE4*!kcO1A8`RVbdev{(O z`Jq;Lw;8-6aE;4Kk2m$36z{xV2hJxBQoL=wf2$SVU0ay@5)r`0WTnTO`b~;=UhmUug?HZI z9fNIDc6z+2-%@zfKW+^u+dn?Hy`{aiF8zCUd7-iXaeN!MiidMQ&3ZxIf9k1zUKNb~o~}hU2KqM{Dx?c5`Q`dAjHi((^Xg zqx6FT<*K}ISNm_RVdlM7Vbcue(Ds92kk9kJ-75LVX%|5|K+Uv?O8n!UW?vEpF*YZN zKht3W^kewPso$jg5_|n}_a)<|%x{_3y2qH-mYEC6`3y+>d##QNUtT3S&KzTZ+w4oq zRib&XRk@loaz+D217EHN+YWW&L6i;dQ7w)sZdvzTL&@eYjnxd(^2ko>q%PDL5cm})OH`;v0CX!a%L zYRY@geqVt{*nd$eF^97;Cd7&SL6GHwq7^q`mhdtWkZ#?;n^v8}UOPW3k> zX)1PKGCpYu$@m;wwdR|BNiubXxy-&KtP_)CG+;EaI%=RA`;zE6R$VRUWbd_izb`5A zT`bH`L2?RTz4s*@*};Ps$hX+GT&%ZC>yd7MRR%>69)B6&u zm)V!Zq2lJ#_Jg6z!5H@?)NiHx652)34p3h;_9cyGUqa{-ghBk74vTVMLj5M)m)N(L zy)T*F+A_baAC~CW$L>px4qsj+InKVso};d_m&b}LDKtv9`s;Vp0?Zi(<4mJ(y1i5J zSMP82ugh!4j7Nply9QH|(SXswnxKLHxXA2w@~4k}pgn42TuaW!KgZ>ct0*jd1kcCs zOIAMfL>BeOPIlpE#)`i$3Dz5R9pJ%b#+?h6ku<%HFp?zde5;t(aP=9V$osq9@!Ino zms7mnrv&yTN&F|`FPY`vmvG(=u1DDxbv?u*90s4`+UUKrR_wcMWfQ{PCA9RuM3rOy zcCP^!b1(i;GTfI?zj6DLuU#-xuWy>M$Blh2JnbTA2dLY%{V@KjZG5+P{BvBE*_TAY z7?YRI4sc(F`c1kov2G}TUov-Yds_t?6aUcS(ZB>xyb4jr?yZ^XI(zDSt9t5nRRMof zgwcS}z#5|gOe=hjOI@_PZgG{cwnXFhCDs1jd+bD~nuGbtM_=;q-krMj7Otou?nw~J z;oFzs_a(vKxd+pNL0S8f+zkJ|g!6WAJqn#x<9%HDMe6q@xz94@ckd3=-Tg_o9>wq8 zso%JLNz(6C(Jq2^fY!SGyZn7g*8uZg=eS7oEZMBgz8Za7I12rJ?N=J5}TzIM9y*COE?(O3@ z93TAi3t4-2WEpm^H}CkQ;+u6N;I5k@nS6T^;c(_Fa6)eN{e6k}h#q-41A-12hYq9t zCAcE@$UPkW9o>AZe_z6RJGdUj;=$D`Fu)NGgZ`3_3TLm__4vYb2)Ae{JZI~DiIO*e zyVroSb){!-JS;_j3H4j)zJzuWv;)+f#17PAGu~g4H~W$>h_N|d?7i`@0H5QceoO63 zzz;w<83u zl70ZZX-~Dn+ctQI!HUiCV(g8F1&BBGn-p)38@0l_&EOpdD>lcAu{RzTAl}q(QoK1o z)C%vM!8;6AY>pRWZ#*nOys6)$cw2fMP%FF}4c=j}VspG0d*fjN;!XV~#hdHBTH$RO zyu)C{=6Eso#=`={oBBqp8EroaSJG$qn`!Z#`e{oLB z%+`k7+_^K$e0De1KRzjZd6nci_s(O|Hv1B>{-#pXsnT_a)SC(tU~j zx4oU`=*!rb%$?TWI;U>Xp5ewM%Zc5W9Ft5wDVJlL`7vs|`|o-m2rkjv$AWRD(Kp@R znN%@J`D*vvF0YSv9SoDU(C`@z7!4Q=Km+~pr{sH1%HNlucz4`I3hB7#cKM!@ZboOl zc2XtD-!=L@C)Q~HzJ&93a6Rf@JP?m?7<_Iw^qv#*+-}kEN=Jh8Gt*&7?n^AK-?)8A z()XOuE`oM|n%%w?#$UCp^m|U)%)TTHVr&j^4~Ag@?n|iOr27)<4`uI5+UK{!@_Otu}H-14aXDpax`KSv`MG7&GBv zehQKU8`fJr_4kC4`C5ind+Ae~2fSQxof$DqlzbDL@eMuO^*qn5BfcKnGzoqsiup0o%@u8Z( zC)_;tden)&k>~xtCwwJ>2li*>)VP&9ScUmRRcMdY>ESB0hN&=@(dUQi^9QIfqIf3@ zdV&9#^{^jQ`}c&6rXR#X5Rsq0A5gzZ`hoMjfVQVv;cXebBXEt&N{=`7n-p)38@0l_ zYcunEggA7=`RVbdev{(O`Jq;L=MCOrSYvb2<4yf0#XGOp0ky)r)8HM8Zumrcys6)$ zcyqm1E4*!kcNo^#ob-58ze(}ty1rI;w;8-+(G8zSk2m$36mMJa-)e<-&fpz}H8v+b z-qdeWyz_dWUMsvC4c@WnhEJr&oBAzqM*p4(MAGJlN*j0Ve^of z`mytULapDV`x5Kja`z?UTTdN1zvUEvGt#8|LyJf9o1S`O|22PD~hPV$5O z`x4IE!SyH>4{H^b$~93q4DL&U&+X>SzNDzd(vjf&%yd{1>rv-DCtAO8`;w&3?b0rS zc7U4Q{vF0&wXF2-3A;8m&ohNljLRYJ!7$9veF^oObYEh>R`$N6ZN`+Db6OWHDE9}2 zvHOza!k1S`jx+9Cd#UlRr@p7Kr(Rbjz#Cbk0i%I6P6INpte(Fobk|QlOfWwM$zcG0 zPyIb%{(W&9$bCKuSZMmODwZ5acnKwpWr%_>992SCDd=F`x4qk z&<;>@5<5_f&G_FF=FPq&3}S2!aSw)J0q#qv-%|S$@B>he55eCPN)5sLI8LRm4TRgk zoVa&U@qAxKd?EL=%FbT(ol^g5Q_19`=K3|DLeZ^n*AEBJzXw&vclT{eb#S(hr>9k7|3W72dYNI}BEA4)G0! zVFBVz{U*hm<3_FUZZmj?!HUfZ;>>hdfOu2CN%7|VP%FH12JbLfu{p#y7={IiH}#to zZ(FYeYK3>B!8;6AY)%koro#fnoBBNhFgIlX_Y72bJ+cNnbL9O4@c!ve&c`b~l#KjRuSc)-(-ZTItB*+k-r!d?(#Mw_6wKgHh#0&B5J&2gLb>BO4PAgTC?N z?+MZF82`AheeeNCU1PYaNTOl&eJZ9guQ0 zXXK0qj0V1B4ahR(%Xn_rdsXrK5^$+?ll$v8ucX8=KLyEQfn3bK#5vDD$@V23+4bJr zZfIRyLvvkSvoj}BJ}TTsGD$qo3Hy!LN69`#uKAIdGoWeHrWNDJM_R%Weqt#qwC|68 zZr2{@-)nh@z3qHnSDtZ#MqoD8(<&w3vpjU{g&F7fFFQzdHE!iLQ(=A!722EVbbl3E{ZyFSP@i94pI=vn5yd-M(2MgN zr>=+npxV#v=1f0`gCHV5eLtXnlk@}NO?#>p-i-$D2wdZ`(&J72CdHfMMy>F+4Bm0z zM&zf*oBB`PUgaPFsB zmb(AcQ=f0`sn=C8`lbM*0i%I6Ndq#kbY%CEEAG=rc}JssCwOkRQ3$?V_@F=d=NFF8 zmtpq`3(w2ly)%XNuN_?|=BFSzoRI6O{_$LAw0}Iej+&>7{vbVXb3IC3V)4LuCI0c8 z@sB&Umh4a1?p@vcO8|b(`*yW{u^z4Dxn0^t&<;>*5<5_f&3ONKquH1E0qi!F&JOS$ z5bC$ozJ&g9jt@cq_>GGfJHH_aHqW(z;2AeP(2YjM`bOru{&CdtMg$M?^S#x$W%W{_ z-A9GF<88Mk6Wf6#6b{|pS~YZze)Om z^E`yMr&{6N)!+EfB7lv_N{=`7n-p)38@0kaZ}5)6HYz(k-qdeWyg5JA3hz#XcNDNO zdFk<{ev{(ObwI7~whi7f*hXci$D8_1iZ|DLwZgm2;2i~QOkR4tso$h{b6sC6ymJQc z7;K}m)8kG3CdHfkw_4%dXz-2#HYP7U-qdeWyluTtuNB^w!8-=qsOe3#u{_&QyC3Vm5_{Z~4s_X2je&n8d zUEM=#&KeCE4HymhG$7xgT^)Zv9{W64&^PYPSGXd_A0xhdUR~V${NMQdaqGTl|G4#p znx~8YAU$t$JxU!^%Rg=z|2WMnL6~km3ja9uiv?mO{&Ctx&<@aO+Jx(?M>}!2VJZK3 zS3lz?QaVfWH=P~8KTiFY+LzEj&ha7WAFt&1`8!n@QM{AI@sRbfA5_~vo;Up<4uXjM^!EH!0pZy-%+d-g$#}47O3(>G7t1OW{rb zxHUoDmnqvnK77`~`B6Khq<=hrPqcqLf2*3Oi~b-zZ*x6LC05Hnp1)pw_sI{5NkwZQ-Fg)Maq1WA z(MtT|w2Pn}pq;b{uRk35-gGJdc&FKyD3vApo6Zj4AE$mx?Mvt%=lD=f|9HO=*VZ@k zyx%{L_6HBxU+yL~ZrO`fXx*a1{C9Nv1{LP6Q=xs0K7W-yf29h8Wi!q3koB-1RNFsp zn|?r*CkWH`1L`+PKY)Ln_Eamp+YH_ba3)K7ys6)$cyrvS72Y|6H^EF0rpKH5O^P?? zhg#v?Xz)&eGg;E(P5mat+tTZRTH$ROya{H4Fg@PXZ&JLu-m4YfT^kzzSpuLnOM1Mi z-=uhRU0*A_^9Jwaa68Y(M`0c=0{biMQ>ovic<1&0tyXw<8oa|`#pXm|jlA$dys6)$ zcw2g(UMsw9gLfFL*qrouQ@^F~rhnW%T-}!`+dn>Xe#SJI{hPe zxw_7t>PPOW*OeM=BVaUOG_WRXV0HD6$6>NY`p50Nqy6LdrD~oo`h)bm&GjgKq1Ez_ z+r~d0XO|KA>DHt0k5j)`k5=Lzr(Fc?0F9(gc>UqX_ohqv$J@-lM5!#%-*k3>`wrA^ zseK9hJpkqSP)+~%MkTJTZ{&Hue|%~L5A4sf)wq?rP=)!6RcN2D)90$tI!A@MF9y$8 zh3ovc3iJO`VMOsx7RN)@!+ub0|9H;ygE$Bx^3(SN>NiP0aK1mT?WtCHHyXSnaE;4K zk2m$36mO0jwZhvnc*lVok)IxK>NhFgoF8h1ch?5Se-;63Ojdflso$h{=k+?ER(R(P z-Z9ulWv9oR`b~;A*L$_XyVKwu1#C=Sdc3LMq-3^(cLz)$)($jDI}FETgj1tw-S>r+%?Oti(S~y9n9=T1lJm`ood$ zO_%bIH=2EkQdy$E>Ffahaq73!zJ&g9jt|xJk8e`q+WJPG_xs1&B6whb`M;=f%lfkl z?Z2ro_nA)rQHA+WRA~J{pZ}dc|63JC6z^nlJY+rW2i5kETc#hxK@gFjz8_G(N&12F z{c&wiwZgk=ed9li05&EoJ>JxBQoK2C)C%vs!8-=qsOlX49&hS5Dc)S~)e7%6gLf3LF?s3nrhb#+&2@dP@Xi^$ zW3Y|NPLDVBn-uSy-oMof??!`n6tFRQ>G7t1lj3dbeR{3%whZ1e*hXci$D8^sg*W}< z_C6K*$H&cXZ7J^s>#_cEw7rUl;~&reTHSx@sea_1dRhnKVVX$nb*$-F``$6=bm@xnrXNt{3BvUKfcj0+ z51j9hYkR5{-kk>T1UQo=J>JxBQoK2C)CzCg;7u?Sgz537ev{(O`Jq;Lw;8+>;7peE zcvHVg@y_XWK&|l38N3N*f-pVa)NfL}x!$W4-i-$D1UQo=J>JxBQoOmYuNB^w!JA+v z2-D+D{U*iR()+hs;oY^axi66bXw8xyZ|XNG-Z{NbuNB^TgLiVc%lUqS)^91i=^wXt zDc3(fv317uw)Tc$C%4XTnU3nx9Zdw%EMko`k-|EZ_?k$dWObq}pMYcyaq zU^L*_uh6x>*^Ys>*|`dzr=Go5Zy-7 zB%WgP-*|nLqpQsxc{u}u4jG3Ik(N+=kzQgcD$L&=;~#%P&C^AHke;`>9_4kSEsUd1 zRcbg?7U23tKPsHPV%Osf&mr8RrSM$-I`jQ;KQi5=px-D8%K$(4$EjZ|5ET~SE#j2i zKd$W}Xa^`Qi5)20b?qSNA8)hNKGgnbf9Ji|DAycu8i=w1V(OQH9XRh-*ZM8BF9AOQ z<@gZvkKed>v5Y<8!?l6%Ip$RQ{c+T>drU?7x3!=94Heq2snB{$h51)>IGb`8`c2XgocF71d#V-Qw!ym_e1oUb<4yf0 z#hc?st?+I$cn3l4CY>H{>NhFgoF8h1ch2D54Zgus>G7t1lj6;FK&|j@GNhFgdA(1s72cf&?-*>OveV;D{g%R;{&BmZLjU;iSqsbi`FI^~w}&nq zwpYc&xp$sBOWl9!sea_1dRDHt0k5j)`k5=Lzr(Fc?0F9?jc>M0pn$Mdq z^QX9v#v)wO<0?Mvt%=lHO?`Nwy2jm@Vb``c>VvQATB?i3Z~ zPgkM6P^TBD(3-Ep+&q1LjtYH+dJ-HDSr7X`wf*C^*`Ir~UMiZtA5gzZ`hoL)b!|_z z!aHa1E(NmZe0sd8-=uhR+^7}ajRtQIo~5Gc@uq&0;?4P?R(M+m?@}Op&Zozl`b~;A z*8#P{yGyULJQro6u^LE^H}#to@4Q~`)e7&t!8;b+@QL(zQ@=^^=DNOCcy}7S!?4EY zq{o~3O^P@7Z?(ePHh9OP8$OX9Z|XNG-nQPS*9z}8gLfF#*qrouQ@^F~rhnWTP@#W( z*sQ~k(2^}4!;)|@pOFd8r#@M*yDj}Mfr z)$aG>zGfB^LEpIc?J2)sPxX)IZjJVj=VqvRn*Jcp+gy**Jyk9Lc+U98i-uS_l5Rcf ze1BZ)7wgeV{NuEXpdFyive9IEo);IDzZAaPJuCB2axsjD`RpXX5RfYC+73NOR>B%b0e@lhdar*qR`us5} zEEXDwa6Du^><88Mk6Wf61T^1WGJQXwev|YA$3L#^saAM*tz+Kb*d4ev{(O zaidmv=MCO|NV`p?$D8_1iZ|znTH)Pk@a`62|H<@tQ@=^^w)HxoR(RV6Z$G5nrqbh0 z{U*hm>%Cgx-DdFa7GeL%^mtRhN%7{ozE*hW4Bmc7yG^CXoBBB!Mj_8 z{U_7oP5mat+t&N^TH$ROy#0`Nn@W#2^;-&W`p50{EA)@o;QQkTT{6uyC zsi*ppd+K$iM%xG&4Hylq$r@OFe?PufBrI#Bf84qy#y@_rnx~8YAU$t$Jxcdfwfy6j z@sCFuWprk`^{Dgx@oM?UwOs`50PUnrRN^1+?4$fd)(htM^+?xZ|XNG-dqRN3hy?9w-?Y7@$`68ze(}V>GfW% z@Xi^$OJM9hogQ!MH!0p+*VhW~MuWE(&=T?VcvHVg@#g-mR(M+m?-Ce$Pp8M5`b~JxBDZIyws;gTi)!Mfo=zMQ+oWx^p{X{ly%&0Lfvs&BR zQBRpi?C-}D8QUODnnu2IXeybu28m@LUFM?T7ZeuB~i%X{o| z8Io6ye3W}e%dtGMO}RcrUODnn&en1)Z}FmfX{REu9Qi0WNz1XkAHMx|6Ui$_KFT#} zIhF@r@4@~~KFWQ5yzYNm9%IB8|NeIN>Z|%Y-%+I;`6!pyax4$NpHF^&V)3`dlp`PI z7HK(_S64VI+j-wk*^6!4`HnT^$Va(0Eywa8ciE-uXGJ%@c9M^BL$n;r`_KQKk^SQ* zvjqD(`PQ{N4w8>@mX>3AsPEk$kI!6ieji_b=>hRNMhy(d3 zSFhz*-e3N7QugCto{)X{2M0OVNjdUSuIo76|Fk^x`J0cYW}kX;uuqPBlzT?Yu{_BA z^!;ySmo7QbCr3WY*;63hv zYt(WqZ~3yOZ2qN}oH53WJNYR0`8Rd{)AD!Sxvh-X9U0<5eUguIc`e8CP#5g;yHD-y zYbW_Aw@AyeyjAbMo4Mom24_t3wv&96YtwQpPwYB#^;K6jt$e=O*Kf&3xglDP<^Arr z)3a~9cDSQcZ++yWoTcSh-jCin%CQynd9NJ#D7WfZ-T$=w_g@&6ed6&wvvq|pe0m`t z+@4Hzwheu z>4kihv$Y({!GNK_kdJbWT8`x@xmPo=HLo1`DEIj> zy8mf;$jN%5>Dp^H@yU^oa(OMs@;?08aarjLnU`LC(PwAmque4b$MQrbd;N9SWq$hp z`#w4HQLatPvAjzzURP}Mu_hVkeeEP4<%VcEmUq*QH#+MO$b0odKFV2Ij^)YxoVmZV zvne$HX+FxWnxy-m&X@j^!Q3umz3=+SN4ZWd$9l2;xa0QQaeY2H@=@+oEywcIGbkO| zfB)-WJ~{GHE~n*K9_64D9PCHrqg=g~V|k989$UO}EU^~f2xo5N- z%cGoXuiH-YQO?$KERX!KZ*M!vN4ZH_j^(*>{_&H1lxx&-EUzes%H*Iv$w#@*C+PmC z_7i;N=S};lpCVuSl&Btjdu3aK^(|OIZMm2yaylHIs3}XhsZqU$B%rJTXnSVe_H;t zKmIY}tg!-eVllzS$vE z&GChNlpCVuSRU3bujX@^;Cw_r%2`^Dy_(sWMFIF>mwiKPStWO@6ktgckZ8k@4Nf>x~b7u@%gk8-w_V|mz1 z-*xAmp?wwkC^t#Vu{_y_X9WAq&wu(&pI*pExkfF=@~}68jRx1rh4zKk`v7ujN=C?$Q43FAK64Ua+=Lj(n6`q~%zijJwWw2HW=HPCm-DX*rfB zxMxKtvgUo>Cr3WY4bgHe@4Rz+XMggeadN-Mzpf)6o$`=6G_ zJ;m#;-838APm_;wom!6N-E;T$8Sh$z;|uvHcdC|SdC=iS7hWj){yLw%kdJaXEywbp zf819L>VGMw z%aMiYG*uYV_{$C%J7VsvS*=sr zXUv^bw`Z~Rszhd1P|6VEv;2%RR;9V1LIss35G77*UyI0~c~-!$XH8K4OBP8v@=>w-nxLtIywXKe+jLpNoTLN!Fu2RL-S_0|LplwAGo#I z-$;yuB<~sKb!2z`;&NxLvh8ht9|^}$S4xekZW=Lfo;O}sclM$^vW46MPTAnB|Mfk~ zzc%^i%)LWeoxBTP-!Xg80b8xOB zn_f#Vd~0FTz61AuLAA@@9xYRBk4ih`MCSF9YZDqQw(z9p9gK33hqQFzCxz|g8-KqHM>Ry5Y$+5QT?Q6a3^!tMD`@Mx7vQh0+a@G24O&S@p_iB}G%5gP-dF3-n zQs0MhoPXZsabsTWE3*N-Kl%+>x32fzU5-E1t)p~Inpnl-_GT1TobU9!n|{6Rz0S>d)3RWv_N|cwI21BBZJR>?RDRyhV_@8mlJS@BAFKWR`+joW@_`q&XFu8Sgk_NW*|dXJ?ElE9 z<=d|Oic`-Hh5p$;-En;8?zzer=D&uHEaZPKGT?)Jy_5y{KZ}ecJ5Vmlx<|=(ytC(y`A+7sy)d_lojCKITdI6ZKYaAuVeS`*BCSBBbBixlTOt#32mseNDi`EEhmjCrZm->m>& zvvFJvCnfax)$y=${{1)Fh_d=77imP9Dz%fl&ep2VF)Lhz-jOG_4mYRF4rmQ!G#Udpc4 zdQXzQ>Iwyg+1g@k!FrfxSZvU0A9e&4M6 zKz~U;FB~^vo4^E#3IvlfSpae@5^7;>RC8 z;gR%4N1&d%I+TwC{i@nW!?3}F2RExykFC|FaBq-{%h8|FuU*N_Z8FCZq@(q z@?h(>G=g@6e&Ov4Y#;BbGF@qyJ4V?lLzWk;o9!cCckP>U+LT$X3s_QRp>>jKpLK@6 zKWz{wmhCI@%XwTwW4m7tdGlK4%$?sdyXE-y)8@4X%BeId-7xZC``8}p&zhvKPhThG zr_ET<)_Pj&@zds>99dQ+nQDv(m%#QF+aoN{Rog_>KDOK2zFDnvs)Vw)J&nV>DaP48 zwuky_JWA_N>k;jn)zUtrec{yB<7dyP`t2!#v*b=N+&hwbas{hsX(w6E$4EY&}X?U9QB zzV=k5Keij~WB;8uch+fh=FXncGVA!6cyC=b4$S3IH+YuCU#4?W>jx6zh!c z+SjSu!}i$=l>XT6xc1>jS~U)=fuhPO>QA?a?c=<|cE`0Za8oa;ibQ!>pG7gUeQXcg zM_%eNu6=uxvBEHJ?KnzC?Ng;g@ZR{pr5^N;q{paXwewL_>sFAu9_}>}o<0)mbF53{ z_pce8SH7dy<@vD=tiPK#``ReSAM4jchpg*IH4W_Z*)UbcC}*9I6+iq=SFZfqP=xM& z7j-oZSLp_kkyU!*Au&G4(?{Yw?4{Ymw~546`y~=M))tRy(&LDOWQ{6|P~H=2mO|so zjVkkqgASQLV`|iy3i4p|a(t@>_vYI4y`xD|{U=BVplH+a#Kwm$stzHaX|Bkt7$@z&kR zclFj)>FwNGcCg<6pJ`m5w|?B)2lYpTD>zW5SG@VBla~DW#G^9p`!3E*?fqWnuvyD8 zYxSyo=bH7_(f4)g>bCjY^#{TB!bfy&n9t;-(E#Q}terTnLl&C0mt5oc3UD479M>1g zIoM07_4~ZJ3uZXanF?*P_dLjhaQ6kRc+&OwlUwIk^q@!Q zt^IxXEmxhW>W5w$WP!r=?I#@ubK@9a19Pw)22UbfrcmaQ7sXX&)F`el!J z?4~8p9I|Zae}?qWJ~j7*rggvj?*sSE_RFr9**tUM*S24J-?RO)t^JS9?75ybbnDIf zXIsYYu>8^$*(G0W+CTg2H%>nA#PL5Jy2D3ScI1*V%YXHo8-_NmyH(%L)%KojL9MnfKSfeA&mZH!l6j29IR6 z%Qg%xygGmAYYoq3-hb!CrsvSqlldio>3BVJ+{s5R->q@tk|!>@ zH&fr-u>2nb&sp-su&Xj#{O|okFYoo_&{MvBXXe)H`(*w)^e0O%+T@W;pWY{A&Oh;~ zCI8sWQI$Kk)p_E{}e?^!*!l8v5H?ugz@#%tcLSTz}Efmo_>jbK2Me znIBHsd+5qLnlfj;z18x+-*Vy5=N@j#%pcHuS=)I_hYo3*l-WOb`+>&~?cMZ?myXO_ z{<~u`TR(pB&>yZCp83{{qn6*;`>LVme|~u8-vhtDY?nLk9lGhyhh}!zCfoGCJDhmn zz6WiV={*0a<)55>#n4|CHqBgkO~dkDqt;rw?5qKqlmB$*vQHNO_P}qSws~gq5t}bN z?X*vp^!v%?nSHOn`M}YG&KkOC!1|d5GX`ZoyXikmvUhBj`NgBhX5MPwamkFan`KU$ zyUX%jJ7zB5{)>Z}M*m>9-w=^OsA02IXJYYvA(9*ItzQ-_MRmJR|Yt zh_BqLX~|Ykty+F)uTQZK-}9j{KXLdO*KJrXZE*6@9%-`^zh%INnT;l%?!-6w_qv(; zpFDdx>XP_!iIZo!@HqJoJh1bib>4h&`5y28vFX7F9zZ;Uc-wLHnF}7Bow@a`w-8@0 z@#TomS-w@~^&6%zzFguC&sx(jS~h3Y)XWRBzrK9w6PGT#yU+2<*KTp*O%l%_E<8=b zvm87ZY;nwz^$U+LKlb&;<>zd1%z+ZmAb#X)eV1<9Z=VC-@4snQ$CqsT_>v}x4@JHw zz63lnH!Vm0uP*3~ct+w&e(A;!%)Gb`d6szM2R2R4IP)6#4{+lL-h0ew~$fJ_+~|)?A(TH%6Z3Ixh@(7| zi}R=-{HPByD0`&P2qBaS9)!(8$OoYuoJSid1HB$1loLW6xU z@S!be2lH^v&>7AFBbE&;MhT4;Izs43p)o?^gpLv#D>PncqR`Po6NJ_g!qHc#mkxVN zxPj1GLhI|i4Rs#+?7Bi53n35vkKuX}NB`)rL-1@agg!n%2x**0d>bL0Lq9;cwa`Ev zf~P@fOQCIra1J&N+uu_N*9@D0twHCoH{e4|fIDml=b>BJA#@5E;L|JwydVc#LkO&a z8!$y##2WlK2Q2mxf?lCx=ytTwQ9{r$bPGKnEd(7;5Q3gD?=6+kJN`q@HJWB?U#z~S z4!H>4dsrlSv=xcQ9=lJOJAY2={EGKN)`Nk$e5{&FVXJ*&YZp7v^;bHVTeiRc&MB&X z*rwG@{YOtYqGet;=aZhwJvPO+mU!kQcP%k%ZE1tn4{l)Ko+u9VbF>XOp>LzVqaUL` zBOiSj{S)Vq*H36OA>^SyLbsa=Z6Sm{k9fUKBk!w1$cGI2^EN^l2hgXHMjYiJ4LS5} z$l)CF(9dxm<)ELV&m#}#kO%(lgpjv`5XKF}chqUf;C!P{gV5K7AcOeMLP!r1$_W7{ z#1W$ZW4z$_zl+2N3t=okoa6645HGKj5`=#h6xQ98Y+ZxFn*vcoZC+bV@|Ws z2qBCkkmX##F{WAaIPRc4@PQv~K)EO%?L=7^$1tY#5(0+674hCWjc_d;M~MCaOc9D{ zIC0<&Ip7bR5dvRegfhX8G(yN=Tt^%s?1XZ#6JRqy=L3K6!Db*2UdSLHA#4V5)PwWD z8}@=Wzzz`N9O5Vg<$?!sl!f#TLfZ>%BLo{j9@+pt*b^{E8fD--_z>S#r(sXvg?%BU ztw9cU*`V`K#&$xmFUW#_Cn4AuLZl(Po6s&oy9ym3gmS(v1liq%U}pyk0rz1-LxlDg zf{h>zd|`*k-&Y7S;D!C{BZTr%E@TcD+FuC#u;XSS*cfE*yeI>*C?EEQ zveBO5LZ}OM9x8+~!OwP}+{1*Lg_K&~ZZ0DeyR7=o}&BJt1_K5b(N32)ID+_X?dV zG*jq4p&3FbuT!W+=x(70g&r3=QRpF|lZ8;mV?sE$Q0P%1l+`W--cy7g5klG1gpL<_ zK%*uxbLYZe5c1X*T1Th*N@xiI^Yw(*)#+XmZXmS2(B?uL3T+`YKxiYO zO@yu%0)Ky@ewsFw@ERe=z(!y{y9#YDbiL3HLiIwh6_f$~8-#EUV;bxRY1Fl~(07Dj zXN!fl6+->66{K$zx=Cn}&~-wn19?qC7zg$f;y3`i0q^cYdkF0)bc+ypV2iM=y@Zgr zi_mRCw+amw+FPe#lluzYF0_x({z9-R*cfban9y*ckva~!5kiLx!B(aV9U*kE&>=#w z*D*q)g&r0MxLYc=1eM9Ixp(#S=3Uvq_ zD+D_mD>PdOn64#swa|t_twNAPo!=6ANC<6#J*y98toO?tFNKunvd}p~D909hQs^R~ zr*s-2(#XF==(|D}3w=+g5h9KJ%Y>d5x>V>HokoZ>@-YUllFr6;@;+;+b?{zioSk|3 zoGIDc{=7J|&de#*@;v8XE)w;N_PJbzmi?~yRq31rq*TKHkp{c?YNsW z?~FJ&`%YU&=6^TXv&g?}$BnXEP1-ZN^69$lXP50*lq28h|6P>1e#>361EzLl-v6jVJ*^6Fn$i6>qtL&)f_AKU~zTJrI z;GH)wo+sZ2M-0erx}bNFpY_acxixdZ+UsYR-qDfy$FQzUTk|`a#*enj9{uMxGV2ar zC(HB9XM1>__1%8f_Svo9_)UiOQI34m2d>QQarQ?U%CkKy@2bnb{rc}R^=JJy^YQOL z&5)0Bq@;&gCTQg(d$Y)M_{Km}3fBvYLfA2|eWcEJ!QqiZBgAn|;&ROh)M_o9_$w%GD|Ixf%vdDYv-OkK2 z|2ZLp^7>!7UenKBz9)k^Xa4T%+4WbB%p(87RqfgHt{LOVffwbo-1okDsM8+sBA@Nv z>goL)z5VCo?=(R_@1Hxv(bIbQr!w|dQ`~;2`z!OU8~-me`5pH>)9lCWzf9l!k6p4$ zzi27;Q>M51Wpnn-AGrOV;{nG9_J8(grrCelk60ha51wb5^}KpibM~;Yxnet*=6J&L zSs%v}_M2~ZtexHe&H-8SbG%?Z9A{WR`B)G6ndbPzd|nUb*}plyuw5Jvn5G=_*$%ds z*ILI@r8VB2gfIlU#yqq^#Am?nVlYfIm7XbX`W|!7=O@zIi7HQ;kd;1aNJ|P;Da2; zGxAfO{TAWU=g%sRXB^*n{@C9)X3>Z1X6~BZ{kG9ge+NJNFY95s7_%^baGYcNSwF^1 zoagw5vd~VBw;YdAKgJt`b$8yjgySK{NA?%?SN22NFXT|}O`We4L)43WgdFcUPH|k~ zIK}JYb#i>d_=<6!<+D8Up{=Nw;~LK6I$r-rhUMY0C&EwqLiIvl71~;88=>umb`siIXg8rjLW70&7202Ds1Ww*!-WnK z$_Zglhx8}|bd%7nLU#z=Ep(sI147tqV=s(-CH98T2`v+PUg-NmFA2RO^t#YnLO&9E zPv`@op9}p`=r=;Y7y6^npN0M|^e>?=gnEl2*AZG*Xak{sLYoQ=5E>}7mC!aq+Y8}E zGP?-vAtW!IabBc0M8cs$c(Kd~p+kfY7aA=zPH3VKd^i(F>vwJrQ13XJEWk;2;$!0I ziF4=AnwpqD4I}WX)iHm{m?o0kd4PGz(UEO%LjJkExvnlhOr;tmRD5y^z;h^V)$gc& zpJD%JubAIyC=q8&l|htzPm8!J!Bb>6(Idx?YipU>I=|t_xl>!)=eL$I#Px|QenH%c z6#166BYVM$>G++5YAqT&zO2RutF|{&c4}>^!u++v9DOqk@>e@8&^gCvL)V^$>kA&M zt{&1Y{z3lwoO(wp?4nJ|&7Cr{wOt`dJ`*YplsisL zTCDmt`YG&${f~A+{nCDzo-lXT!r5gE($$h@IcwE58#|#gY8cipa_t1;Imd5~=d7CJ zy!{t7j@y6K-o%xsjl=@{qwf+=6Z_1nF=T@@btW=)r?9F~UGdP}?S3}->3!JL;g{y9G z`)PkULp?3i^FH?m2(j*`J<)z>Pwcm}GgdNA*5-4jx3@8!Hmp?Mtxu2p*YPb`vl#`MJ2%3kSQsXW_( zyD!dqvr6{VX6(sTttU3=I=?+pPkwvC@0!n+Um>4;HtosM>xZB{d5U(T<*fqTQYw{a zN!Y&>`t>&Z7b>JiVg0qPJq?jMxDR^DH$J(QbKS~yD(#B<8`%Om2kfp{V!Z#giNvMI z(19@n|B@b~#z~n{iS>VKy|!meJLAe5HEz_5Ij!T`Pn%W2<}0S~^s{D6LB(v$3(d`1 zJK5fiN{2|Vu>T&U%g=k1#iBitJU*0+AmRYUX$yF!_*G!v1 z=-LZ)gwQ$kxjHA^ODoI%oqxZc-y!NfSj94QQ96iHxSvC%gb#KD=QitGo%}MVM723( z9__2zewjXXEpng!-^1*Uv|naJ73Q>GhGEL)re^1QWYYf8ewj&~emhzlj_7WV`n}xy zNRD4dUmx$|S^7Fzg#EE9-};%q9-e0}E!Q>kMc?u3M)_q}F6${3_V1GZxI7B~OygZkvbmAoh*{;ZTo#azfINQ z(f)AE{26nm%$PT;b!x-#aU&0=c;c}C+Q04_@Eg&qeDLRjEAN zQ*HmTW%@H!>kAiM>hI6g6aB}*dmgyI;Ot|x{m`DQ7j=KtcE(DMXl-wqHEYHcrqhO% z%DeS7tNG!gNdNJ{Eh!ftxwQ^;@7KANtnsn>^ZoWjJ#l`(^YiZEc^*OA5ADgGqwOg$ zPK}+raC%z>>y1j~-L_ZPp1OJ)dvbN_##Sv?cHoRVl1)7Y>`C8yb?u4vLwmALP~~u( zVhx9m9?NK1VWskJrP`jVw0^aWJ-Mp&#OlB2w&8|+>bEEAiT0%CJNk7zVhu;mojvL^Kv@82< zJ@4u9ij{fnV%(IL^bb{5D$jNl$EkB7??;Kdt-WPViWfiFX5MpE>xr$p$3O2;PaLN< zk=a7-amsIFa5M~~J<)z>Pu81yoYHot_~m!u?z~slt$<49*^c5ms!I1aZ8J`}s`bPc zUFeQeSVvJ$v?t7ag9ksEedLgHvV#XNXr?{Uev0<=iXNZzIK?VF_AqSDj0JP$Iof$? zUOrQ)JljzmpQ>a}ZN{El)p}x+F7VqE_2jiDm)*U8U}^iIJ=rhm@kx(Ulo>a#b;`n7 zE%O^>bZQvYdU9*Z=W8pKXFIBGPmRW&T-ADFtIqY?6ZI6Zr||u9Z9heOdO_Qhwlh{y zw1@FC%6gEfQhBzclz%v_KhHAuMAiDjMd$eKiF%@cIQSeq=Q~^5Ptl&v)bpLTGgeZZ z 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

wx7g3u*f|_wdbx1e{jO(1a-0*fr1^Y#EmEWujIL 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 literal 0 HcmV?d00001 diff --git a/content/test/ship.blend1 b/content/test/ship.blend1 new file mode 100644 index 0000000000000000000000000000000000000000..c3ac8ec68d407b35db2605afc39fc876cb85af0e GIT binary patch literal 1003508 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 z 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

nPMz(8p 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 literal 0 HcmV?d00001 diff --git a/content/test/ship.mtl b/content/test/ship.mtl new file mode 100644 index 0000000..f07aba6 --- /dev/null +++ b/content/test/ship.mtl @@ -0,0 +1,13 @@ +# Blender 4.0.2 MTL File: 'ship.blend' +# www.blender.org + +newmtl Material.001 +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +#map_Kd content/test/uvgrid.png +map_Kd content/test/tetrad.png diff --git a/content/test/ship.obj b/content/test/ship.obj new file mode 100644 index 0000000..aea73e5 --- /dev/null +++ b/content/test/ship.obj @@ -0,0 +1,478 @@ +# Blender 4.0.2 +# www.blender.org +mtllib ship.mtl +o ship +v -0.206022 0.561873 -0.011206 +v -0.119716 0.560522 0.008077 +v -0.004751 0.563370 -0.023105 +v 0.055282 0.559558 -0.007712 +v 0.145744 0.561427 0.010017 +v 0.213930 0.562807 -0.008303 +v -0.161451 0.562805 0.002926 +v 0.003766 0.534670 -0.026896 +v -0.203839 0.511860 -0.018421 +v -0.162010 0.485302 -0.011563 +v 0.213836 0.478134 -0.020207 +v -0.045692 0.468001 -0.022078 +v 0.082312 0.521381 -0.023491 +v 0.044539 0.402209 -0.003451 +v -0.205619 0.398058 0.004964 +v -0.169031 0.381641 0.021240 +v -0.068870 0.339942 0.010303 +v 0.009781 0.363531 -0.014819 +v 0.096103 0.397493 0.019756 +v 0.217964 0.399421 0.006125 +v -0.204553 0.325254 0.003283 +v -0.156494 0.281751 0.003940 +v 0.020743 0.317356 -0.021851 +v 0.129477 0.339985 0.026030 +v 0.220711 0.306984 0.005506 +v -0.076777 0.258270 -0.021832 +v -0.029258 0.288322 -0.010229 +v 0.066269 0.241267 -0.016551 +v 0.146799 0.185187 -0.011010 +v -0.154116 0.185233 -0.016433 +v -0.031760 0.211335 -0.023012 +v 0.004891 0.201931 -0.031796 +v 0.212805 0.190185 -0.016808 +v -0.204504 0.199190 -0.017579 +v -0.095264 0.184441 -0.020341 +v 0.046662 0.144795 -0.003573 +v -0.204608 0.144531 -0.017393 +v -0.156809 0.146968 0.008014 +v -0.079562 0.145971 -0.007068 +v -0.036451 0.146321 0.007217 +v 0.004779 0.135828 -0.015514 +v 0.172277 0.145146 0.007740 +v 0.214538 0.144846 0.002540 +v -0.167417 0.048338 0.229981 +v -0.169698 0.120416 0.169698 +v -0.176530 0.046931 -0.176530 +v 0.176530 0.046931 0.176530 +v 0.169698 0.120416 0.169698 +v 0.167417 0.048338 -0.229981 +v 0.149213 0.119405 -0.270229 +v 0.146055 0.120416 0.270657 +v -0.149213 0.119405 0.270229 +v -0.146055 0.120416 -0.270657 +v -0.149420 0.054673 -0.264796 +v 0.149420 0.054673 0.264796 +v -0.002886 0.116702 0.423249 +v 0.000000 0.120416 -0.416631 +v 0.000000 0.046931 -0.416631 +v -0.169698 0.120416 -0.169698 +v 0.169698 0.120416 -0.169698 +v 0.137782 0.120416 -0.244473 +v 0.134751 0.120416 0.249709 +v -0.134751 0.120416 -0.249709 +v -0.137782 0.120416 0.244473 +v 0.000000 0.116627 0.384384 +v 0.000000 0.116627 -0.384384 +v 0.148794 0.101470 -0.232115 +v -0.148794 0.101470 0.232115 +v 0.154047 0.101470 0.222255 +v -0.154047 0.101470 -0.222255 +v -0.066114 -0.165458 -0.222948 +v -0.068212 -0.165409 0.217791 +v 0.068212 -0.165409 -0.217791 +v 0.066114 -0.165458 0.222948 +v -0.003313 -0.164842 0.347649 +v 0.003313 -0.164842 -0.347649 +v -0.008288 0.574476 -0.002763 +v -0.008288 0.091646 0.002763 +v 0.008288 0.091646 -0.002763 +v 0.008288 0.574476 0.002763 +v -0.299244 0.509855 -0.002763 +v 0.299244 0.509855 0.002763 +v 0.299244 0.526432 -0.002763 +v -0.299244 0.526432 0.002763 +v -0.299244 0.185914 -0.002763 +v 0.299244 0.185914 0.002763 +v 0.299244 0.202490 -0.002763 +v -0.299244 0.202490 0.002763 +vn 0.2960 0.1490 -0.9435 +vn 0.1042 0.2427 -0.9645 +vn 0.2031 0.0498 -0.9779 +vn 0.1526 0.4199 -0.8946 +vn 0.0654 0.1817 -0.9812 +vn 0.0049 0.0973 -0.9952 +vn 0.0234 -0.0659 -0.9976 +vn 0.2961 -0.1736 -0.9392 +vn 0.4145 -0.2013 -0.8875 +vn 0.0536 0.0203 -0.9984 +vn 0.1127 -0.0811 -0.9903 +vn 0.2245 0.1521 -0.9625 +vn -0.0913 0.1252 -0.9879 +vn -0.1088 -0.0643 -0.9920 +vn -0.1416 -0.1919 -0.9711 +vn 0.0753 0.0706 -0.9947 +vn 0.2214 -0.2971 -0.9288 +vn -0.1656 0.1289 -0.9777 +vn 0.1741 0.0710 -0.9822 +vn 0.0013 0.1961 -0.9806 +vn 0.0693 0.1131 -0.9912 +vn -0.2896 0.0434 -0.9562 +vn -0.0926 0.1833 -0.9787 +vn 0.0158 -0.1219 -0.9924 +vn -0.0801 -0.1775 -0.9809 +vn -0.0108 -0.0845 -0.9964 +vn -0.0855 -0.0924 -0.9920 +vn 0.1983 -0.2123 -0.9569 +vn 0.1618 -0.5128 -0.8431 +vn 0.0552 -0.1308 -0.9899 +vn 0.0491 -0.3777 -0.9246 +vn -0.1157 -0.2374 -0.9645 +vn -0.0945 -0.1010 -0.9904 +vn -0.0217 -0.4108 -0.9115 +vn -0.1150 -0.3936 -0.9121 +vn 0.5507 -0.3000 -0.7789 +vn 0.9705 0.1358 -0.1991 +vn -0.0000 1.0000 -0.0000 +vn -0.9705 0.1358 0.1991 +vn -0.5507 -0.3000 0.7789 +vn -0.8038 -0.3896 -0.4495 +vn -0.7124 0.0428 0.7004 +vn 0.8641 -0.3331 0.3774 +vn 0.7157 -0.0256 0.6979 +vn 0.7124 0.0428 -0.7004 +vn -0.8641 -0.3331 -0.3774 +vn -0.0031 0.9954 -0.0960 +vn 0.0031 0.9954 0.0960 +vn 0.8860 -0.2683 -0.3782 +vn -0.8860 -0.2683 0.3782 +vn 0.8038 -0.3896 0.4495 +vn 0.0708 0.9051 -0.4193 +vn -0.0546 0.1148 -0.9919 +vn -0.1125 0.7364 -0.6671 +vn 0.0035 0.3344 -0.9424 +vn 0.0104 -0.1386 -0.9903 +vn 0.0321 0.0517 -0.9981 +vn 0.2213 -0.2523 -0.9420 +vn -0.0870 -0.3704 -0.9248 +vn -0.9957 0.0926 -0.0000 +vn 0.9957 0.0926 -0.0000 +vn 0.0183 0.9998 -0.0099 +vn -0.1072 0.9874 0.1160 +vn 0.9732 0.0295 0.2279 +vn -0.9732 0.0295 -0.2279 +vn 0.8866 -0.1307 -0.4437 +vn -0.0183 0.9998 0.0099 +vn -0.7069 -0.0000 -0.7073 +vn -0.6844 0.7291 0.0079 +vn 0.8713 -0.3871 0.3016 +vn 0.7004 -0.0000 -0.7138 +vn 0.6683 -0.1939 0.7182 +vn 0.0190 0.9998 -0.0005 +vn 0.0450 0.9988 -0.0192 +vn -0.0450 0.9988 0.0192 +vn 0.6844 0.7291 -0.0079 +vn 0.6820 0.2827 0.6745 +vn -0.6820 0.2827 -0.6745 +vn -0.0004 -1.0000 -0.0001 +vn 0.8876 -0.4605 0.0042 +vn -0.0005 -1.0000 0.0046 +vn 0.0005 -1.0000 -0.0046 +vn -0.6993 -0.2313 -0.6764 +vn -0.8876 -0.4605 -0.0042 +vn -0.8713 -0.3871 -0.3016 +vn -0.8472 -0.3191 0.4248 +vn -0.3162 0.0109 -0.9486 +vn 0.3162 0.0109 0.9486 +vn -0.0088 -0.3162 -0.9486 +vn -0.0088 0.3162 0.9486 +vn -0.9997 -0.0129 0.0225 +vn 0.9997 -0.0129 -0.0225 +vn 0.1159 0.9865 0.1159 +vn 0.0085 0.9993 -0.0354 +vn 0.9524 0.1148 0.2824 +vn -0.9524 0.1148 -0.2824 +vn 0.7382 -0.3193 -0.5943 +vn -0.0291 0.9996 -0.0041 +vn -0.7132 -0.0259 -0.7005 +vn -0.7710 0.6369 -0.0000 +vn -0.0085 0.9993 0.0354 +vn 0.7710 0.6369 -0.0000 +vn 0.0004 -1.0000 0.0001 +vn 0.9064 -0.4219 -0.0218 +vn -0.9064 -0.4219 0.0218 +vn -0.7124 -0.1810 0.6780 +vn 0.3162 -0.0109 -0.9486 +vn -0.3162 -0.0109 0.9486 +vn 0.0088 0.3162 -0.9486 +vn 0.0088 -0.3162 0.9486 +vt 0.249493 0.888699 +vt 0.300679 0.888352 +vt 0.252663 0.828817 +vt 0.564031 0.887391 +vt 0.604969 0.823879 +vt 0.501539 0.840125 +vt 0.305402 0.774472 +vt 0.250800 0.691603 +vt 0.555279 0.692751 +vt 0.502804 0.650588 +vt 0.298686 0.672311 +vt 0.250764 0.604810 +vt 0.468563 0.568169 +vt 0.443762 0.782837 +vt 0.758306 0.691625 +vt 0.655221 0.628090 +vt 0.614851 0.694249 +vt 0.762260 0.582675 +vt 0.587125 0.509568 +vt 0.309337 0.543049 +vt 0.251234 0.453628 +vt 0.408695 0.616702 +vt 0.402239 0.524216 +vt 0.642557 0.450784 +vt 0.379100 0.438470 +vt 0.502499 0.456641 +vt 0.452134 0.468589 +vt 0.553067 0.388907 +vt 0.308797 0.392191 +vt 0.302135 0.439353 +vt 0.401258 0.389574 +vt 0.502475 0.377401 +vt 0.752623 0.441086 +vt 0.703496 0.388274 +vt 0.754208 0.388223 +vt 0.768893 0.270715 +vt 0.754316 0.015814 +vt 0.813716 0.356142 +vt 0.706982 0.358905 +vt 0.293085 0.698783 +vt 0.338306 0.791985 +vt 0.325015 0.816889 +vt 0.221363 0.270715 +vt 0.176540 0.356142 +vt 0.283275 0.358905 +vt 0.235941 0.015814 +vt 0.810847 0.270715 +vt 0.916090 0.015814 +vt 0.762219 0.015814 +vt 0.700395 0.291473 +vt 0.655175 0.198272 +vt 0.668466 0.173368 +vt 0.496740 0.354358 +vt 0.591654 0.336168 +vt 0.591654 0.358905 +vt 0.690585 0.358905 +vt 0.757323 0.336168 +vt 0.791985 0.358905 +vt 0.599616 0.358905 +vt 0.496740 0.354447 +vt 0.599616 0.270715 +vt 0.401827 0.336168 +vt 0.401827 0.358905 +vt 0.299672 0.358905 +vt 0.232934 0.336168 +vt 0.198272 0.358905 +vt 0.669690 0.232934 +vt 0.329763 0.221671 +vt 0.496740 0.033828 +vt 0.323790 0.757323 +vt 0.663718 0.768586 +vt 0.496740 0.956429 +vt 0.203684 0.358905 +vt 0.221671 0.336168 +vt 0.786573 0.358905 +vt 0.768586 0.336168 +vt 0.179409 0.270715 +vt 0.074167 0.015814 +vt 0.228038 0.015814 +vt 0.353089 0.887169 +vt 0.491410 0.889914 +vt 0.671747 0.882793 +vt 0.753450 0.889732 +vt 0.751248 0.782506 +vt 0.521490 0.588413 +vt 0.251190 0.387845 +vt 0.452996 0.389993 +vt 0.706982 0.270715 +vt 0.283275 0.270715 +vt 0.673508 0.811023 +vt 0.496740 0.995128 +vt 0.173368 0.358905 +vt 0.816889 0.358905 +vt 0.995128 0.270715 +vt 0.496740 0.270715 +vt 0.393864 0.358905 +vt 0.496740 0.358905 +vt 0.599616 0.356142 +vt -0.004872 0.270715 +vt 0.319973 0.179234 +vt 0.496740 -0.004872 +vt 0.700395 0.698783 +vt 0.659826 0.786573 +vt 0.293085 0.291473 +vt 0.333655 0.203684 +vt 0.424218 0.235941 +vt 0.569262 0.754316 +vt 0.572861 0.228038 +vt 0.500066 0.074167 +vt 0.420620 0.762219 +vt 0.493415 0.916090 +vt 0.486794 0.324378 +vt 0.491813 0.903823 +vt 0.498444 0.324378 +vt 0.506687 0.324378 +vt 0.498444 0.903823 +vt 0.491813 0.324378 +vt 0.855863 0.826272 +vt 0.137617 0.846165 +vt 0.855863 0.846165 +vt 0.137617 0.826272 +vt 0.855863 0.437509 +vt 0.137617 0.457403 +vt 0.855863 0.457402 +vt 0.137617 0.437509 +vt 0.393864 0.270715 +vt 0.486794 0.903823 +vt 0.506687 0.903823 +s 1 +usemtl Material.001 +f 1/1/1 7/2/2 9/3/3 +f 4/4/4 13/5/5 8/6/6 +f 10/7/7 15/8/8 9/3/3 +f 14/9/9 18/10/10 13/5/5 +f 15/8/8 16/11/11 21/12/12 +f 18/10/10 27/13/13 12/14/14 +f 20/15/15 24/16/16 19/17/17 +f 20/15/15 25/18/18 24/16/16 +f 24/16/16 28/19/19 19/17/17 +f 21/12/12 22/20/20 34/21/21 +f 17/22/22 26/23/23 22/20/20 +f 17/22/22 27/13/13 26/23/23 +f 25/18/18 29/24/24 24/16/16 +f 22/20/20 26/23/23 35/25/25 +f 27/13/13 28/19/19 32/26/26 +f 31/27/27 35/25/25 26/23/23 +f 28/19/19 29/24/24 36/28/28 +f 35/25/25 38/29/29 30/30/30 +f 35/25/25 39/31/31 38/29/29 +f 32/26/26 36/28/28 41/32/32 +f 33/33/33 42/34/34 29/24/24 +f 33/33/33 43/35/35 42/34/34 +s 0 +f 49/36/36 73/37/36 50/38/36 +f 49/36/37 50/38/37 60/39/37 +f 59/40/38 63/41/38 53/42/38 +f 44/43/39 52/44/39 45/45/39 +f 44/43/40 72/46/40 52/44/40 +f 54/47/41 76/48/41 71/49/41 +f 48/50/38 62/51/38 51/52/38 +f 66/53/42 67/54/42 61/55/42 +f 59/56/43 70/57/43 63/58/43 +f 51/59/44 56/60/44 55/61/44 +f 65/53/45 68/62/45 64/63/45 +f 48/64/46 69/65/46 62/66/46 +f 69/67/47 68/68/47 65/69/47 +f 70/70/48 67/71/48 66/72/48 +f 64/73/49 68/74/49 45/64/49 +f 61/75/50 67/76/50 60/56/50 +f 55/77/51 75/78/51 74/79/51 +s 1 +f 2/80/52 8/6/6 7/2/2 +f 2/80/52 3/81/53 8/6/6 +f 3/81/53 4/4/4 8/6/6 +f 4/4/4 5/82/54 13/5/5 +f 5/82/54 6/83/55 13/5/5 +f 9/3/3 7/2/2 10/7/7 +f 16/11/11 12/14/14 17/22/22 +f 8/6/6 13/5/5 12/14/14 +f 13/5/5 6/83/55 11/84/56 +f 13/5/5 11/84/56 14/9/9 +f 10/7/7 16/11/11 15/8/8 +f 23/85/57 19/17/17 28/19/19 +f 13/5/5 18/10/10 12/14/14 +f 14/9/9 11/84/56 19/17/17 +f 11/84/56 20/15/15 19/17/17 +f 17/22/22 12/14/14 27/13/13 +f 21/12/12 16/11/11 22/20/20 +f 16/11/11 17/22/22 22/20/20 +f 18/10/10 23/85/57 27/13/13 +f 18/10/10 14/9/9 23/85/57 +f 37/86/58 30/30/30 38/29/29 +f 23/85/57 28/19/19 27/13/13 +f 24/16/16 29/24/24 28/19/19 +f 22/20/20 30/30/30 34/21/21 +f 22/20/20 35/25/25 30/30/30 +f 26/23/23 27/13/13 31/27/27 +f 27/13/13 32/26/26 31/27/27 +f 29/24/24 25/18/18 33/33/33 +f 32/26/26 28/19/19 36/28/28 +f 39/31/31 31/27/27 40/87/59 +f 40/87/59 32/26/26 41/32/32 +s 0 +f 46/88/60 45/45/60 59/39/60 +s 1 +f 29/24/24 42/34/34 36/28/28 +s 0 +f 47/89/61 60/39/61 48/45/61 +f 66/72/62 53/42/62 63/41/62 +f 66/72/63 50/90/63 57/91/63 +f 55/77/64 48/45/64 51/92/64 +f 54/47/65 59/39/65 53/93/65 +f 58/94/66 73/37/66 76/48/66 +f 65/69/67 51/52/67 62/51/67 +s 1 +f 7/2/2 12/14/14 10/7/7 +s 0 +f 58/95/68 53/96/68 57/97/68 +f 69/65/69 60/56/69 67/76/69 +f 47/89/70 55/77/70 74/79/70 +f 50/98/71 58/95/71 57/97/71 +f 55/77/72 56/99/72 75/78/72 +f 65/69/73 52/100/73 56/101/73 +f 60/102/74 50/90/74 61/103/74 +f 45/104/75 52/100/75 64/105/75 +f 69/67/38 70/70/38 68/68/38 +f 70/57/76 45/64/76 68/74/76 +f 63/63/77 70/62/77 66/53/77 +f 62/55/78 69/54/78 65/53/78 +f 72/106/79 73/107/79 74/108/79 +f 73/37/80 47/89/80 74/79/80 +f 74/108/81 75/109/81 72/106/81 +f 71/110/82 76/111/82 73/107/82 +f 54/47/83 58/94/83 76/48/83 +f 72/46/84 46/88/84 71/49/84 +f 46/88/85 54/47/85 71/49/85 +f 75/78/86 52/44/86 72/46/86 +f 78/112/87 80/113/87 79/114/87 +f 79/115/88 77/116/88 78/117/88 +f 82/118/89 84/119/89 83/120/89 +f 83/120/90 81/121/90 82/118/90 +f 86/122/89 88/123/89 87/124/89 +f 87/124/90 85/125/90 86/122/90 +s 1 +f 16/11/11 10/7/7 12/14/14 +f 23/85/57 14/9/9 19/17/17 +f 37/86/58 34/21/21 30/30/30 +f 39/31/31 35/25/25 31/27/27 +f 40/87/59 31/27/27 32/26/26 +s 0 +f 46/88/91 44/43/91 45/45/91 +f 47/89/92 49/36/92 60/39/92 +f 66/72/93 57/91/93 53/42/93 +f 66/72/94 61/103/94 50/90/94 +f 55/77/95 47/89/95 48/45/95 +f 54/47/96 46/88/96 59/39/96 +f 58/94/97 50/38/97 73/37/97 +f 65/69/98 56/101/98 51/52/98 +s 1 +f 7/2/2 8/6/6 12/14/14 +s 0 +f 58/95/99 54/126/99 53/96/99 +f 69/65/100 48/64/100 60/56/100 +f 65/69/101 64/105/101 52/100/101 +f 69/67/38 67/71/38 70/70/38 +f 70/57/102 59/56/102 45/64/102 +f 72/106/103 71/110/103 73/107/103 +f 73/37/104 49/36/104 47/89/104 +f 72/46/105 44/43/105 46/88/105 +f 75/78/106 56/99/106 52/44/106 +f 78/112/107 77/127/107 80/113/107 +f 79/115/108 80/128/108 77/116/108 +f 82/118/109 81/121/109 84/119/109 +f 83/120/110 84/119/110 81/121/110 +f 86/122/109 85/125/109 88/123/109 +f 87/124/110 88/123/110 85/125/110 diff --git a/content/test/ship_back2.blend b/content/test/ship_back2.blend new file mode 100644 index 0000000000000000000000000000000000000000..bc0d1fd14dd999780c9e10948ea916c4bc000f83 GIT binary patch literal 1003508 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 W&)A1OKgs-Ts!sjdwJhNO{QVDoK-4<` literal 0 HcmV?d00001 diff --git a/content/test/ship_weapon.mtl b/content/test/ship_weapon.mtl new file mode 100644 index 0000000..b97640c --- /dev/null +++ b/content/test/ship_weapon.mtl @@ -0,0 +1,14 @@ +# Blender 3.6.7 MTL File: 'None' +# www.blender.org + +newmtl Material.001 +Ns 360.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd content/test/uvgrid.png + diff --git a/content/test/ship_weapon.obj b/content/test/ship_weapon.obj new file mode 100644 index 0000000..93fc934 --- /dev/null +++ b/content/test/ship_weapon.obj @@ -0,0 +1,222 @@ +# Blender 3.6.7 +# www.blender.org +mtllib ship_weapon.mtl +o Circle +v -0.001998 0.003339 -0.279058 +v -0.060548 0.003339 -0.273291 +v -0.116848 0.003339 -0.256213 +v -0.168735 0.003339 -0.228479 +v -0.214213 0.003339 -0.191155 +v -0.251537 0.003339 -0.145676 +v -0.279271 0.003339 -0.093790 +v -0.296349 0.003339 -0.037490 +v -0.302116 0.003339 0.021060 +v -0.296349 0.003339 0.079610 +v -0.279271 0.003339 0.135910 +v -0.251537 0.003339 0.187796 +v -0.214213 0.003339 0.233275 +v -0.168735 0.003339 0.270599 +v -0.116848 0.003339 0.298332 +v -0.060548 0.003339 0.315411 +v -0.001998 0.003339 0.321178 +v 0.056552 0.003339 0.315411 +v 0.112852 0.003339 0.298332 +v 0.164738 0.003339 0.270599 +v 0.210217 0.003339 0.233275 +v 0.247541 0.003339 0.187796 +v 0.275274 0.003339 0.135910 +v 0.292353 0.003339 0.079610 +v 0.298119 0.003339 0.021060 +v 0.292353 0.003339 -0.037490 +v 0.275274 0.003339 -0.093790 +v 0.247541 0.003339 -0.145676 +v 0.210217 0.003339 -0.191155 +v 0.164738 0.003339 -0.228479 +v 0.112852 0.003339 -0.256213 +v 0.056552 0.003339 -0.273291 +v -0.005752 0.003339 -0.035452 +v -0.058510 0.003339 0.024814 +v 0.001756 0.003339 0.077572 +v 0.046705 0.003339 0.017645 +v 0.001756 0.088887 -0.035452 +v -0.050701 0.088887 0.017645 +v -0.005752 0.088887 0.077572 +v 0.054514 0.088887 0.024814 +v -0.032392 0.125403 0.093038 +v -0.032392 0.240710 0.093038 +v -0.032392 0.125403 -0.346358 +v -0.032392 0.240710 -0.346358 +v 0.051930 0.125403 0.093038 +v 0.051930 0.240710 0.093038 +v 0.051930 0.125403 -0.346358 +v 0.051930 0.240710 -0.346358 +v -0.023528 0.055092 0.045175 +v -0.023528 0.170399 0.045175 +v -0.023528 0.055092 -0.002624 +v -0.023528 0.170399 -0.002624 +v 0.023972 0.055092 0.045175 +v 0.023972 0.170399 0.045175 +v 0.023972 0.055092 -0.002624 +v 0.023972 0.170399 -0.002624 +vn 0.7940 -0.1224 0.5955 +vn -0.0000 1.0000 -0.0000 +vn -0.6576 -0.0577 0.7512 +vn 0.7100 -0.0623 -0.7014 +vn -0.7523 0.0135 -0.6586 +vn -0.7940 0.1224 0.5955 +vn 0.7523 -0.0135 -0.6586 +vn -0.7100 0.0623 -0.7014 +vn 0.6576 0.0577 0.7512 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vt 0.480149 0.438877 +vt 0.469410 0.567032 +vt 0.390375 0.438877 +vt 0.492575 0.559691 +vt 0.410487 0.915985 +vt 0.326147 0.890401 +vt 0.248419 0.848854 +vt 0.413540 0.469410 +vt 0.124377 0.724813 +vt 0.082831 0.647084 +vt 0.057246 0.562744 +vt 0.048608 0.475033 +vt 0.057246 0.387323 +vt 0.082831 0.302983 +vt 0.124377 0.225254 +vt 0.503821 0.390375 +vt 0.248419 0.101213 +vt 0.326147 0.059666 +vt 0.410487 0.034082 +vt 0.498198 0.025443 +vt 0.670249 0.059666 +vt 0.747977 0.101213 +vt 0.571158 0.480149 +vt 0.872019 0.225254 +vt 0.913566 0.302983 +vt 0.939150 0.387323 +vt 0.947789 0.475033 +vt 0.913566 0.647084 +vt 0.872019 0.724813 +vt 0.747977 0.848854 +vt 0.670249 0.890401 +vt 0.585909 0.915985 +vt 0.498198 0.924624 +vt 0.503821 0.438877 +vt 0.492575 0.567032 +vt 0.413540 0.438877 +vt 0.559691 0.438877 +vt 0.559691 0.567032 +vt 0.469410 0.438877 +vt 0.480149 0.567032 +vt 0.180290 0.792942 +vt 0.180290 0.157125 +vt 0.816107 0.157125 +vt 0.585909 0.034082 +vt 0.939150 0.562744 +vt 0.816107 0.792942 +vt 0.582856 0.469410 +vt 0.425239 0.480149 +vt 0.492575 0.390375 +vt 0.390375 0.567032 +vt 0.582856 0.567032 +vt 0.367207 0.794470 +vt 1.025443 0.621735 +vt 0.367207 0.621735 +vt 0.452666 0.794470 +vt 0.578985 0.621735 +vt 0.452666 0.621735 +vt 1.025443 0.794470 +vt 0.578985 0.794470 +vt 0.578985 1.025443 +vt 0.452666 0.367207 +vt 0.452666 1.025443 +vt 0.578985 0.367207 +vt 0.438908 0.689140 +vt 0.510512 0.516405 +vt 0.438908 0.516405 +vt 0.465945 0.689140 +vt 0.537102 0.516405 +vt 0.465945 0.516405 +vt 0.510512 0.689140 +vt 0.537102 0.689140 +vt 0.537102 0.510512 +vt 0.465945 0.438908 +vt 0.465945 0.510512 +vt 0.537102 0.438908 +vt 0.503821 0.559691 +s 0 +usemtl Material.001 +f 36/1/1 40/2/1 35/3/1 +f 33/4/2 2/5/2 3/6/2 +f 33/4/2 3/6/2 4/7/2 +f 34/8/2 6/9/2 7/10/2 +f 34/8/2 8/11/2 9/12/2 +f 34/8/2 10/13/2 11/14/2 +f 34/8/2 11/14/2 12/15/2 +f 35/16/2 14/17/2 15/18/2 +f 35/16/2 16/19/2 17/20/2 +f 35/16/2 19/21/2 20/22/2 +f 36/23/2 22/24/2 23/25/2 +f 36/23/2 24/26/2 25/27/2 +f 36/23/2 27/28/2 28/29/2 +f 33/4/2 30/30/2 31/31/2 +f 33/4/2 32/32/2 1/33/2 +f 35/34/3 39/35/3 34/36/3 +f 33/37/4 37/38/4 36/1/4 +f 34/39/5 38/40/5 33/37/5 +f 33/4/2 1/33/2 2/5/2 +f 33/4/2 4/7/2 5/41/2 +f 13/42/2 35/16/2 34/8/2 +f 34/8/2 7/10/2 8/11/2 +f 34/8/2 9/12/2 10/13/2 +f 34/8/2 12/15/2 13/42/2 +f 22/24/2 35/16/2 21/43/2 +f 35/16/2 15/18/2 16/19/2 +f 35/16/2 17/20/2 18/44/2 +f 35/16/2 18/44/2 19/21/2 +f 35/16/2 20/22/2 21/43/2 +f 26/45/2 36/23/2 25/27/2 +f 36/23/2 23/25/2 24/26/2 +f 29/46/2 33/4/2 36/23/2 +f 36/23/2 26/45/2 27/28/2 +f 36/23/2 28/29/2 29/46/2 +f 33/4/2 31/31/2 32/32/2 +f 40/47/2 38/48/2 39/49/2 +f 34/39/6 39/50/6 38/40/6 +f 36/1/7 37/38/7 40/2/7 +f 5/41/2 34/8/2 33/4/2 +f 33/37/8 38/40/8 37/38/8 +f 35/34/9 40/51/9 39/35/9 +f 42/52/10 43/53/10 41/54/10 +f 44/55/11 47/56/11 43/57/11 +f 48/58/12 45/54/12 47/53/12 +f 46/59/13 41/57/13 45/56/13 +f 47/60/14 41/61/14 43/62/14 +f 44/62/2 46/63/2 48/60/2 +f 50/64/10 51/65/10 49/66/10 +f 52/67/11 55/68/11 51/69/11 +f 56/70/12 53/66/12 55/65/12 +f 54/71/13 49/69/13 53/68/13 +f 55/72/14 49/73/14 51/74/14 +f 52/74/2 54/75/2 56/72/2 +f 13/42/2 14/17/2 35/16/2 +f 22/24/2 36/23/2 35/16/2 +f 29/46/2 30/30/2 33/4/2 +f 40/47/2 37/76/2 38/48/2 +f 5/41/2 6/9/2 34/8/2 +f 42/52/10 44/58/10 43/53/10 +f 44/55/11 48/59/11 47/56/11 +f 48/58/12 46/52/12 45/54/12 +f 46/59/13 42/55/13 41/57/13 +f 47/60/14 45/63/14 41/61/14 +f 44/62/2 42/61/2 46/63/2 +f 50/64/10 52/70/10 51/65/10 +f 52/67/11 56/71/11 55/68/11 +f 56/70/12 54/64/12 53/66/12 +f 54/71/13 50/67/13 49/69/13 +f 55/72/14 53/75/14 49/73/14 +f 52/74/2 50/73/2 54/75/2 diff --git a/content/test/test.mtl b/content/test/test.mtl new file mode 100644 index 0000000..9a75c1f --- /dev/null +++ b/content/test/test.mtl @@ -0,0 +1,13 @@ +# Blender 4.0.2 MTL File: 'None' +# www.blender.org + +newmtl Material.001 +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Kd content/textures/test/diamond-inlay-tile1_albedo.png +map_Kn content/textures/test/diamond-inlay-tile1_normal-ogl.png diff --git a/content/test/test.obj b/content/test/test.obj new file mode 100644 index 0000000..5eb365a --- /dev/null +++ b/content/test/test.obj @@ -0,0 +1,53 @@ +# Blender 4.0.2 +# www.blender.org +mtllib test.mtl +o Plane +v -5.000000 0.000000 5.000000 +v 5.000000 0.000000 5.000000 +v -5.000000 0.000000 -5.000000 +v 5.000000 0.000000 -5.000000 +v 2.368457 0.000000 -3.631543 +v 2.368457 0.000000 -2.368457 +v 3.631543 0.000000 -2.368457 +v 3.631543 0.000000 -3.631543 +v 2.368457 4.000000 -3.631543 +v 2.368457 4.000000 -2.368457 +v 3.631543 4.000000 -2.368457 +v 3.631543 4.000000 -3.631543 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 -0.0000 1.0000 +vt 1.642477 0.813162 +vt 1.245911 2.069025 +vt 1.245911 0.813162 +vt 1.245911 1.242810 +vt -1.067532 2.069025 +vt -1.067532 -1.070632 +vt 2.072125 -1.070632 +vt 1.642477 1.242810 +vt 2.072125 2.069025 +vt 1.642477 1.639376 +vt 1.245911 1.639376 +vt 1.642477 2.069025 +s 0 +usemtl Material.001 +f 8/1/1 11/2/1 7/3/1 +f 6/4/2 3/5/2 1/6/2 +f 6/4/2 2/7/2 7/8/2 +f 7/8/2 4/9/2 8/10/2 +f 5/11/2 4/9/2 3/5/2 +f 11/8/2 9/11/2 10/4/2 +f 6/3/3 9/12/3 5/1/3 +f 5/3/4 12/12/4 8/1/4 +f 7/1/5 10/2/5 6/3/5 +f 8/1/1 12/12/1 11/2/1 +f 6/4/2 5/11/2 3/5/2 +f 6/4/2 1/6/2 2/7/2 +f 7/8/2 2/7/2 4/9/2 +f 5/11/2 8/10/2 4/9/2 +f 11/8/2 12/10/2 9/11/2 +f 6/3/3 10/2/3 9/12/3 +f 5/3/4 9/2/4 12/12/4 +f 7/1/5 11/12/5 10/2/5 diff --git a/content/test/tetrad — копия.png b/content/test/tetrad — копия.png new file mode 100644 index 0000000000000000000000000000000000000000..47c02f7b9d7becde6fe082407aaed3f1d6454b21 GIT binary patch literal 1627654 zcmV(%K;plNP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D|D{PpK~#8NT>VS4 zW!t*mca8gP&dl8V+(J5*O$4gAzy~6TAc6`6RHUOLBU?a$4@TKF3Wfq8BU!e=NY*)L zua!CHxL^4FThFy+7;CPPee}_KYw!Ji-qu?0WB%X#%m4cMeD3y}`}MZnZkNmD?RL9f zuba*0e7&5GAIs%zHeWnu%ia6e)oML|++LT@$Mrg!E&hiu?SK6EQCGX&{PkEaUia~u z&1SFHYqeT!*6YXfdAVE;o45I5^_tC}vzari&F=BMozG{3-e$AgeEI(NwwS+G^VfVf z)2$w!&)IxFe_l?T`3r?wdLQSX&g%Ul`fqj=JvwH^RZa1@ALWR z{r2N}y*!@l{r2~p&F^ zBXZZjai_P59q&`i`RqYz%jIIZT9AX2UK-baHv7EY&X?!ovLK(;Zn@k)mh1WIwR)Xb ztLJ>VI^#!DHYErp*ioQW1_(F{Xyk~H($SZ{F+E?WkIx_9zq@R?UB9i?yV;DTWnuR_`dy;usJ-8J`@JC?kimAF{a}P4#Sw{- z?PBqyqd5s{_|RY?%t!3q9<$s1iRIhvzL+n*91d$d&*);IhmVgBUFqCaljNP8y=M1Y z?vo^Evp*UkHl4xgY`JpMRZe>0*SHJz|9E^{E~o2twOnl0tIcXnbE}4XE$0h=^Nc8M z>UMg${BdV=v(;|GZK!EgpBqYpnEz9>+RZ ztXId&84Cn+dp^&P$2rx!_KWq~Wt`q?`tM!BuIpDseLN=ZPwaTt*gFxeHv0^17(8|+`*N*sPtUtBb^AEKEk&pm z1{V9xdb1GTMfUgiuk&RwnBO1w)A_Vptlsu}`ClSLngpR}IYID7b?h?WJ^L%;W;q?>=-*%fd z9;(6b#uy%fXd;9-ONs++7WNAM8e2GFDB>G`023qd+m+N`HkbtNUcT2(W ze0$Q;W+l?QV)1%BGxFK|dcKn75a(+vLZD~87652as5NM6aJud?evD5xPoa(tjo;hj zbO&PY^V@xSzTB_(8g92MY_UXE&0qY-eloKWh}bz@=wY?sEJd)%9;iml$BUM5#f^85 z*HAkKne#}U7OqWLs(si(Yv?#Yaz|-B~9JV8^!kqo-Cmtuw)OUznrf&WZMwq zMI9r#-MR4fd3S<0V#9f3(c>LZ5<2cz3FYhC`~CJPah37}qQ$R2e!S+dgB-ck&xoj# zos%RFx?V})!Jhq>cMIk(31w=7-`D-Ry5IixU;piY|1bace7gU=fA}B#{r~tc@B7E+ z<-A^OrIP#QJW1Q-lXZC^n{9mm{@noveKkb}&Gf99cf?Q9o5u2TpuNULBGxdVsgn*| zm@L4~_4@pAq>|~X=_Fpf3m_Cz2!*3>n9r((- zt7eP&gFQomz8j%Q1Ls}dXQ}G)6Rr75SJa0FR&m3(vkbK85+kni;@bRZxti}*5bSyC z3eD4qj-3HGz!ClU=t6xuSudl_*S7!?Az^{9;3vylLYjje`GKJvw!x_ zfRDGs8*4L`-h?ncVOI!pHMa~i!R&%GY`S=@gXU5sDdS|xK3r%3Y!uLq6mm&kQQ)&M zOb66FC_qwd2+wfs1C+e-=<5SO1}tCq`C_w;$-{2T4=|3r!1L?%R$AgU;_Q=qAcCCO ze`Yd7Y1ED>hQ{O2R}tgm;~5-8WlB>6JZCqj9{O2sW^;9q%ZyT;@Pt!0-r2?;BwsBh z3G^eFQ$WEXVT`XtnU{#E2Qj_iRp=?Hum!jySJca$aTs;^T+BXCpZY5i)QT0AR*k$~ z36T%(x`$|QS3FZCOY<(HkZp#7hLvI?=ud6glZYdRIXkMU#JyPh`gE4YoZqasYOB8A zEjN%5Yu@cw2tyizsD%aSc>^ROmU6ihoAZTXh{o^(_f+~2xc-H3pin^rg)g_zBz+Mn zPJM@tovr|A8B57@=Oc3kB%PzSi1Jcr?z~Uxleb7uoMI7z&hd2YJfUQCG0*z+PQNZq zUV1xUR2ERc*Y`Iu0P#*1^!faTaTZ#K0bc@_InpiL>1_gaa1zSm5pA(E|E**#(D2b=rd9_2XJ0aA&EhJ8#*1G9R8-8g4gDQo_k zB#af`ro7iQgW$Pg!oeL0p11 z?)YOa`AJ&P&nvX#^H4g;C=pc}I{`{~nWNBotlyDE)R>|71jS%xg#yYA$VBXdUt%8# z&bx;1`?j%SGiek4nw5{a21JgMl@%48WC*c{n}jrSRBcA@ zIj`I0O5`arn7_E5HJ0rjR4RrMHYhLU2nka8?fO^=O+_~%^e1@F5Bo|u*I&;kfp_Sr ze2JIruz)k6^T%#qVLx;_+w9T{u_GwSV!TAIzv@Hk=da{}S9ZGJZAP>dNF?9{NWd9z zQ6VGFfJgp<$%vhp5Q*_}YY8r!&(t%lEWXS$M&t$tlCF@n-RJ?*1JV~qK%HI~;;E#v zjQnLF7YTS<+>#je7Xen`fibAq&5@}9RfhHz^CPM;NJ&#=k@4_ZCP$Xb?fEL86|df? zYD6DH6)r~_qAg*P?G2FVyw@$>>lWpN9Z*AtTTvoFho=!*KAC~LOFW0~MZ~yk& z+qXaai$B|c+dURfD&BARco%~9Z+jgu@bl$-Yx3}=rX9rB%z06folllf&m#Y3J)&{I zKm%t8%~uQ_`7{Az4ADoScRn(Sm%?t>3lm}n#Rr|^th9sbrt=IP3$GV|L52lwag1k8 zd2tDCZrC1uq1+H*ze%&KAelz?PpSwyJehzV&DB)EC%h-|kGZ7P!f>2lCk2PmKb`t2 zkBC8qV)By8g~e>eROeyf)$5`D zP81`HAkRpr<8j1#{bosJ#PXlZ2mBc?1A9e5nRa%$L20xV*wV!SFmq)GrR{c2t#J(a zZ71#{WY{j95|BB3*~dB-33q_+ zfF?;G+j9|R-Hk{=A_Ar+$)xUKRnxnHXac81A#|D;seh%x?q6%+fLF^a;uUIr9q2=mydek?1L#4^+#PY%xWPt88F@1h!fsGr)@MyFcv8JC z`7rcJEsG;SS+S$OD|j#?mq~fo%Y_~)pN8affMMB)C%WFn=A1v+%A5&soGczqvQ3SU z!D7J~FzW4exv7@)wP*AkDtL4zxR@Ft!!@_N3hIG));+lhR_o7jfw`hTI)_{}yqBA? zF&c=`nb$B;Mj~tOciZ#vs1kZT!6wpq>N$@h)q0HDVL3%-CpjEz!A`my)mXu(b?I#MzhzxN=Vh^A#6W7coKf?+c zXdHvLyTm-EdSzPb*qphXS29f*R-~E;#Zu8$_0^rtOEX49O%O$~bNE8?v)OI_QpOZ?9NXT-e zMxE$0$mQ%1tdBWbnWlMufPgFvY;a%Iyw$Mn<;+y}AD^eyeix9sKSk$O*`o8M+R&Q| z=zQcO#eeIK4WgGaQY(A={qA`BBs4yQe#DTV@%^DPIN&v_h!TroN=yKN)5?&5=({4y zPz~#qjC|BR;Ts|0AY~Nx3NE8i)pk^F<(+{ZQu=`S&4#=lYC;2FZi*lY({-5;ujjEC z;}prJ1dldEi#E;qb(po!zkdDt_WqU;jt(Iw?6VehDw%-(xEWAq2wAV96tV0$1_>Jo z;>hCpX?Z5kEo~Z()xqyMX4u0C7ZNFqHH|W)nzYNOFGeClxHf&I0;0=ec%NLhWF_zW zZ0~-#i9e(aqzeuStsH@MoIX$I@E=x@$uKnMCM6u5^1h;}hFKIbvH?RUb;)5RMj@Ac z!-WLGn(0bIWRi|6Ig|q*NkW5cYXGECm&P20PWB_7-^Mgk<2Ew$2_ur96oVUdqENt+ zml73Ng8n#WL`|#76qrh2#scZKE&F620|Za!vF3!HjpCS|9`bKPK6#PG=lesfKY};& z`P<fe3ae0hIAY~I!o$7_I0DT96LgSpc$r4!WQZSNpO>p#T*b0$tp5;Wvw55jNoDSvOc zftwQ!8uT{&UVRAR~S)3<&IF!VdT4*r}>9h78v+WY80+^M<~ zqJ>lEh}G3&u7RQHUsRXH6i)Gyt!6@rqN+d7=Z;vkJ^| z;q4a4P5O+-3$StWXY8SV?0`5rnuyH92d>qG0lpS%-Y+maAvsI+IMT+DYGWf`FDE`T zg-!3}yIw*Ah$9kiv(!aXM54ms@U}bb7R|#DWXsJ>zsV-T&2q&Aq#n=|#f90VMA>(g zX{3Zl^qOJjMJPZLE=2)U56nHHty72-XX)&40O2;vHR$uaKCW1fDbfXbn-N{EL^Iq$ zms~v{GS^&YAgY3#^Ac@Ax!|J1oTQNr2i_HJr}<;Dq^eXoJd?-9Z*$VC-G1w9S>f&N z8`VfL8MZh{l2{X#uU9t9;_l}&k%8?1TJ9?9C1GX8D02#DEuoUtMzuKp7)54?AP0#; zMYLV-_H#8GETT-~a95)MCGyQAt`xsoZg`mkG7P;mVP8f>6+PbJ11W(8zo7Xg;mcZD z4>Ix{#*qqED-PHMJ=v_cH0mq7-!A6>`u%#?Z75o*q8D5Y1R#25v*hNOQTZFIlk^AW zw<^G0k%f2R8d57-Jzf`?31H>^n_cMyCAb=sPsHNF7z}lSEh*B+xd*-=k4m`wot((SKAE$#9CI{{o8uGr+?-z7#g|QLZ1qF(t8>j zWZ%3WfRNRGYrrx&EjFe<)L>_zp6z2yyLu+`GV1*)w_&Yv#UEoqy7vo95R)-WFA^2F_*?KXEF03AXvdVba7Z#*xcUJ&u@<`^{qY z3dRM_B)!)h5M)uIPHo7qfS_l|BEgIC(q)i0!L=~L;^aGMPacWQ-HvDEX2p(@3yamA zT*YBY22#(_hxx*j5KHrcJe6j~njT~z0G|N`d{j7B(qHu@HtKxppP_{3Gqg-p&#q$K zEcjt>F!XuQT};_NtIYr%8a)fEc$2q6V5oxM8`fkMc!0 z;Cf}E-uz*?@HI>n7b9`YG&?wJb(^nFukCHVeL`c)CL^2df4slH#g1THhRSa!YMOVa z4aSC^Vact-3+Y3Q#dc=Eu2}o{ssNORmyEILyq~Fo-mw$1;>CBif_`HNhWZqAplxo; zz?_->id(}UO2I@d1T+;=F^4)9GP2V1dOnec2Eu~#j&Wh={H_R~_&Fo;a#aM;O#8H* zon)Ta2dT~oR*f#%&6~!JRy~MGa`*};Tc-w{dYhbgz2YZ~5b*{`-F5neYDg?gW4&x- z-vph(7snBO6#blyrIcyoY=jb?%*gx^1(9)4 zF%+DLtFh*;Lnn2WhdyTO47G_sj+f{G{VJK_n(h+f$cxu&6BtGOUL(SdQuRLO5&f6n zk-^yOG9nP1C!=@Km=P^ngRsl>4&|;z2fmOzNs~aF&iaRc8mg! z8LnUyfdP`0ZUJjC&LHSoH=k$pHk_5kLTvI4wP!=PX*tX%UHa&tG*VC%2(mCVbigYH z0p1Cr2-m!9pv^EPi8&vR0IA2nx!o{nN0R;hdd8=#$Plx-&;K_@H1yBr&J?7t7mDJg z82tG@2BnDStCEblFGnIMIkbyxx!WHof@#NX}1t}4Uix(N-1MXnD$%c&;TPkCpXZ3f*0)o z4#6t(BrwM^*>lalhZ|&N1rAmOaV~cIwr7=;(bz2M68LW1ZL83Da`pXwp<6LP6@EN^ zhRI$fGYWDQcDHPBRlzK$LuPHs)CQhz_efG6X3XBPxTW*~^ZFC&h=|Iln3*9Sn`wHg zkveKKw11iSm_YIs!%#9vayf}4Y*><6S4UwT2OC1=t(3xR$qECR6D9{t7r_hS1Cfcs zsXkIwXc$>(JPfos455V#snX@H6KxP#Zl@9QC&T@>di17K*asM#R|-dz+@&~vPPwQuC81kfwuO3F8!PY7m+8}^7a}B?j|9_{Oqfi)%y>okc09{oRRjxF`b;2j z(x2-%a!nIGRd;6*cc`EhW65klN!C(c@a>o!$>m;xpCHqU$B2npQKsuyY+{9)E>!ZF zF{vVv?{LqlGT#ekBbAc|vN-I(SdnimEXUxe{dl&XI(rcSXGrZczg zBiX+wWM!N^5|B!8%&Mp>_sNpaJHfLt-XXj&{kQio4lb8la}@bIGD6JRyb&*FO&K{w zGfM}pH!W7X?T#QsCs_q{BTh7wo5p+}eW8l9lMIN74ddx>kV^jDx6lSHm*1(+d7xQn zZq`AX8gUpj$$AAGIp*4q?MF*o3ZRjpTwZrY$odvJD0fVs2|tPT@z`$Lm=q)reMFgx8}m*;ZKg>p2dg+?Pw1j@Lz+{J@Wr=za)9vU6h zY~gDZ*dy1e&jH}J`RxzTvofTgvk5qUlqXa==Y z)D4suJ#HvsYC)xCH>Vnd0O=^A3Qen4L`xi9dD!iOXI1Y3Kz-_kco8;fu0VnK5m8I( z-RL>`U(RI?6h4LJ0h_Q!0YdYHd5Y2H-`Gai&`B@4PBh_->SmcmO7-VnZ~Qq=mRz;hG}b_=>*?N=+pQUpB9TZp-Dx*95({wQ z%HlO=<|lGeA~PEK&KA-sI~x_}*k9P{ydJ$sC;_yKx>V`CsJ_5<_4+8gc~NZ9(Dia6 zVdhf`9@5pPuYu?)VkC~uZ7HFf{la8Z6%$_tzVb_HdtB&MY#PFy-ceO|v8@Unwsuww(izt}Mw|2O zTeK@yFy+hHRqca((nOo!88?3S`#nueq?Qzi@shl8a=zw25;jGiPbZ<-ALkTpcl#}! zO#8h?uH>b4vMHbjCqf^u*{4#WRgPj1CsEob_k511xqz}EL5^#g0sEIKBEnyG8rJAu zLqeoH(wx%g@Gt-OFI`~4X+5hiQ&h1dsTxv0mz!9h!l%Z>MTzOIY1fxxChMdWwNQI= zRODQwdm$-qEbqzmWf{#{AY?z+yKl8m8WA6vCrZxGNC(xTV$VIQn|IPziUB>>;{5g^AOJZ<$LD>sZfn zKpE6AYPnd+m3cEIwml5V!DtSTc2}AAMk)DK&|<*{LZ*<4-m+C;V~TO8mm4N;HPA5T z0s>xu{_2nrYN+mXI_Z}2XnZ=KM2Tc#nBhlG=7RJ$_BIGxvJ(GDyqE699K`(0Pvo4C z$1Pb(YhTqzI}L6^3Da=>`SbMS`;UAmPiS6-REO{~o9V(qSo%enz~^I~3mxt8OPkuL zSjcL-*^;@I!x-SUOa;r{cLNTRdHdxTD}3LsXR=PLFA*k~4z5xjOXwVl4MiqR2x+wm z!}*+;vj}$4hN<=dlyT8EB`zc(51wh8&%htPA}%S8!;ffLn=uox))Qi<_qM&tPZ%qP z6`d4&yWj3#cMXA%1KENJd{D$IB^UGjiyxOPiQ$%<>v6OlyO0Z71ts>A^~N4&S1WMH z-iIlUb&{cIFD3GHFkXe#$&@C8WbT*S1I;?D6AA(!^Nu#h5&nc22*h8(dYBMm(CKoo z$w)eCVO=HMDj!K2Y&63vU&8Mb$Xjf-vIRxM_f1b-uT@?th2UG|=d>81J!0Wpx%cqs z%wjM?fTLUemJt%@t@UwA8m35zm?(Y-W`VI$66jv+jif*@moFlD70L$6&jtI-&hf>l z7zlMR* zp>!(`SiBL17C-Fc0zO1q{tUB_Rbr z&gKhHUZ>-d>=h-$Do_Uy(`YKAigAuT+<*M|&`*gaM}g`9-4XG}n!g@sUyR~)1>Rw4 zl&zD#8k)*k#;Y(fmZVeSV8IJ%jWx9&VfTzUnIjJx>NajQRhgH;vpSa~^QI%4h%p42 zWGEw2d7tVcCk>i}M?e#TDW=fXQzi2E;g8uTu}EL1%e8vW;FoUgU5+_py2#95-Mp(i zjn^Bot$PIM5e?DS?zI&U*-7-GjB00!u49h?K!&%$ak-UejOh<+7iDK7I4kP*PL`il4^9o{`}o#(qlICRln-Lt*O1%ckR$r6n?YD)G})Md zoH4YnaBLKEW~)T%jN>7mUVj{J7-}le21a$##}fI-u#UYZilz!J>t<>9$LaH!$O4{T zC2>M_)o`0F|G}2HCy_JB6|cWHaE z)&=qm#i8|7-Hd)Alm};j!C%Hb z6WgFj%;QV2$uaagy${n_~ljk&P~q zv7+L{nSKF%>|#ivxaNK14v}^p;crMdeKI|L*4M_jU4FkkcH0A*_qzjjwwrwslBtY4 zyNIU3%==g%n!whGnPLTa@j#8E$p_I)>;eHgk?k{EG6LOBw|`JgHU7Fy-YHynElU({ zvx--y=zKVG%r9!m!J+c&cveOtZoW-eYQvouKf?)?vwj`WjUEh|WP1je5XKjq6!g*| zgYlXL3-DVx_7(Zy$xk?ST?bC5;(@Q5|>|L=4|W&6aX}4f13%b<{?;``w15) zGF+HH`-Dz^qk_Iq$E}|&fQL79*McE3VP4+`XT2*M&0 zLMkG&=dpe{;v8PY8ssnuglFm_jAdFXPw#TJq!`%haWqW;2YL*hR0*$XAfaSaV4=(= zGAH{ngdX%ML>QWhCA8I+Z6H5Q7;He~DXHj_OdO0!c5vZFR)y0xlvRdlj7gFsR9ug% zg^0RA0NH>zn)u8}7>u47$&m9vV1_6pjLCR^qlnF<2O@0FPk}$y;z&@WYj69sST-Pp zP9jnS7)cai#n1Zf_6&L=FzqTg`TJ{jB?N4C?;V5Ratcz`Bfm1Dw4%?o@L_%_K%`gu%rBx;1s6@!)&x-xEM9(*vwyh%tAm z7$_4di>7`i!W@vHfeH~Qh%$hZOKiQf^Pfij6b)X@7HL5^&l&t#hVI6>T`;^bUfzO4 zj;BxW>&^1;wriGo;B&pzNlWhoWVZ;%53*9d2neBf22uWMTSw!BZ2M|bhau3c-2{EA zhk9N@Xbp}U6<%|bT8)MVBBH18I0Zv;aO@yU9u!S7$0}(;ze$_{TyD&mk^0-L<)sR? z;N)sUlxZOk&W8V}nur__hia!aK5=2YKeRJ&u^feIx2}yU=5;<_1qD%)@QR|tvUReB zz!<}PsXRbPKGiPTWTgp`ngs={MV%+Jgkglr*X69bYZkWY>#VmGR63dvi+EkIkspb8 zy=BhSE+3qZpJQ)KQ#&kNOBPBIdLB))5UnOlia)}_FWOR`7-j0e^UnVnl zhIjO!us&NmYzX>tGT%Wqn>p!F%gTH0$pIH#H~@+Ep)lE}EJI%%$U8EQpzFQmk8y7a zbpVzW#7>Y~^cyoIzR9+`Ni6Pki+&nGEvhI)Gp|%XUa<Nch+6#C2FU(f4nc6lyn=eK$7EHA)eLJD`We(jESLDGhFk>Czrw1P$AKF&j+q`E6@z$2gifw z;&BlF>TZ)^xpgX{}=3g|u(WAlrP)ncPKp%4r$WAG-G6E5mweObZSX`2`3|NyTujE6Fojfhv)0d z`}_QPZ^Nb8%X1}GHn8$99yzq`%Oc?T63sWgFzq(~x`NNYmq zacU=OiP7I!WLwF;`<@3ik{LTE>sSjoa5_>)bjUv7&^j;5b>IZqGK=mWdj<#e2ab!&P>ud!lZc+{rRUN1JuY@N!^8)`MUn8`6laVFlv&1I^kuF)w@TTPHdS!-&V6jE0xP`3Sp;}%-(}#h9gY)X$0})&w?RB zlrif1l!U?-SkBL|9{WD#e=D&9ZY2cB*TBE~a!@7TPZz+B@SXemyKiVeojx((Pyc8v zDrPF`-D!d#*p0O@enCv)fe|pSARQT<@S7yVcr(4C$~;_h*thGQCC(nNmTw%iiT0N3 zHWxTsjop15K}SZcyH)F5C8GRu1WW`o>v14UG06RH%Ra9_%>8wF(951(v3WHINSN}IToVRrii$zVYsD+{5p<)fSe<0OxUVav(bwLsNbMf!T6eYaTd z=5wQNcdN(PE-`XYlYDIM1`!gS!aHBXeOnwOzm31?k-bey+4N8*-)ccA+A zx{Dj9GvZV<62yJ8c2sZdi(bkd%Wh7UWU(g2sGtn!55ZH|Cn$2G4ncULUsBKed@hmw zO#I%tpZIjS8)ywgUN5K3Vut83DKq6qy_Kz8I9NkGBQDi&hXz!^R{_?{xP*+6(ohd1 zII|@KF)aW_p?yU3eE#*zJ2fahnJ1`6WO-$giNzv6LWxDUI(L9;2Su#vpLEZEua_%- zLlW9>N{?YFDtfyIuUY-BzRzSu41m+D5TfD%F(4CV-W1{?H- zw(>U(0>h*_o<)u;2L|(1XKuH)&CgbI7+|wHtboc(D=QS_54IeIC1IrM#o~Io!77Mz zKAU8p$Y8OANu1M!9M1}IjI0g3-Gze9$GPV<0g70VB4y*yRGHKzJWmIJJ_I+`;k$FX zxfs4>>~r_LHjV3J!X7%3_eUkxOcVJNGNV8LyYI*U?#Gw6Z;$uY;eYg}$IbP%oZXHm z6hcvAg6l?{N!%hHMOW$+f`T6gfOR7y59@L2R6|PnNcbv8&@BDtn3Hl5JSZ2PMo4i$ zr}1CuQo+wLsh+V7QnMDuv>uOve#K3zSVM`;pG|rU)%+&O@m{etWjb=!hc-G#3CGDF z`Oc`9^q-kFO^^=yIC7R(R-J8x0npDRn3Calnc}V|I)-^tE$%JPk5IHJKPr%awLw>LPE7z3#3$9glK`i*%y#nD(tu zU;gud@>htsU3)+kewbj{aS(dHw_oSP^}9=w7oL}oo=K-31C9g&Tn%|{9EkfwOpXXL zw#(x#MWzab8yG#l&0<-YYmnj+M0w5TM@aB?=h+&9{r*bh{wa+Mxxw*TQdOSdxNq11+D4+HI}kl9d`{A_d?)8^}<8 zR%1T+Hgg?TXz(#yb)mXnOS}&X7n|h{n=-}idAmGkr>m&8;p(gTWih|vk5y%nG(2X_ zwF@*VP;zqZ|;*rT1r z#`Ho^Z)~p!aMTqG zAN`ci#xBx)xZ1Lp=m67X3}X~y{TAgXsRGN(tN8ewRV&Sw-0sw*KgJZ( z1V6=n0--hDD2OB*l6*}*4|6~#Yawv3Rkq7HS#lEq+^L)5he72t)O5N&&N6)z!do|x zL3K;y3{)fMQ8+HCl$vOUQM?!ml^G+TFt+dWDFo=&F;|I9q{;32j?uw{F03 zoikPcS>i7wL+7&{o*cI%AF@Ig9(#xE38a+XW zJ%R0h`}Hg^ty?-=KHt`_-O@laC1Ls?sevbG2c#XI1Z-;$*ebgC}k&3!sQP_9S88XH*+<~bjt$cO z=4NVjObR|KgUJvVSn|Dgy8ZF}J0F=r8#6GaX+8F}gNK-7ai&~I1_ePMZ7l1F0Pr(bJa?V zB+`VgfoR8zCUGU&^hYV`&#@wdJN?I3YD=o18{-uu{c{ykdIX%%*#1UFNFn%=bF@d! zm}xDfi*(~U*0{n#m4U8@2jyLxCNJ2XLA)}nX0n=9m2leHEs4$7=Q9U^p^bIfQZj%Z z)=6B09r%7b;%)}1c%$_xT#!#0jsdjTrkVUbnT>qfY?MF8&X5ec8078ox`jSH2Mr7SD3HGXpBk?pXJxC@u-nrY{8H&Bkn zzqSs8DMU09M{H5-Fu#Bo0vzkzfvayetOTIt?EE)BzW=ws zkkWO(IQ)Y@`CQ$?jA6n}SY+7hBp<{N@sFezGK~HqvppKr!+0)8rv z@)Vr`yOyhtBpW#FZ#1A6ttRsJyTf2DjudJp)I|<{QD6rUBYN@S^N)>LM-w`drBs=ZGEpKxg_G zj^4k7G5BYDfv0K+3&E$Aj-a>x7TUx-`gMdq_yofrh9W6ubw!DYy$Ca z02B<9eF%5SciuC$bx2CAdbc}_t&>feum#Q|VfiW()J>m1nH?koNm-(NtIF zx0GjwrXriof)kW9c#;pstEf5!pH3L`dhd>cC`+y>-M=Cl(_sha^RdaiS5KD2)NTnV zH!UPvfDz>hJ&!AibPQC5YMATwCOk8iF&_|MB|M>d7-6X{Du#4j!ageR=Zgy!j|z~E zWCX=5yh9OD<~3r;AbdQhZvpuwI!-7_JJvBZ8CU7Ycm}8WPX{Y7>m|w0$5sn8{c<{i zdvCklxBWo@@VJlZif##oFx!E^MbMn(0vxNK&*If#eU6Fh3`Lk51-etnaMdJ?cc@7H{GJOM-FMlQ0dt51>$kd6OH zd@M)k>Hz`!%8(_$zA-{u&qvG~i|o>_u${s(gJE)Gih{-ikAr|#o@H(lpt|gUR5WnD zAmsw=4@|@gaVQ7T(|)twE1TBq{btirThaePj2XZLg#%bzubH9LJ@jp$?G|+j$rwY_ zBf|~JqG&cAsY0#JU2eD2<+fe{3=E!Ci%_+vk_@aZ-+*#LAv&yW*IU(#J~J@Z3`OC> zAEzFfJ_*Q6*Q8>{DwbLLN!{7-j z*J~^U`A3HX(QvLNSj$FqM5j$?#-y~tac=Tb6v7Q}+kHizKGSNzmoa2pV^Nz(B}E2P zaEQC9^Kdiq4*Hvj9ZpshowzPaE3g-911UCSEanr%>8QJmqIBeG)KZ-HsprCAKh1Hb z_n*+es-SwCezqawpHpHEM0n@pyY!>z?Y1)!QJCM%7F)t|t&I7a_EIOs>$th4Y>?fB z0ZIYGpHs~+Ii0IRoZjybr^^MWxVqmjXw$OiwI=OFk2-A*NR2JisGPYNqBwf{h}iX- zG~{DgynL*#&ZzvM*5F1l7ONPhQZiz(&a|bzyHP}dn}Q7zzTEi6m_lAX1ag{Sj0R#D z_@af_9X{#hcnGHng+fZOZE@_5NwKTnB^=;}=#rr*ofCI2S2v+mG_?R+;1Zn=+x6}A z33MP(TxXp6AMph8Zg#LpFp74sF;=lo=MJ#xWeDuwy3@B+iee}ig(lB_oSDQL(zL!a#haP9eg872hei8mp&w%ROn zK$irrNeTg?PuK)$`Wa4I&;Ne2@f1}^(p3>QwwM<=1{SB>!Ch(VEH4f;EPXZi`FdVX zA5alIt%K9~WUL{DW?S@WID8e6CVO;tyhc!oU9I{lt`VONS_l(#kkNwk+!x>Y`8n^Et;Cy?_jBW#k>fk{KQQ#F$CI~n3^ESc}~Z;yZX zum5g-TYUNZf42L}-(6STm0Q|jTJ0-doJlXaMC#OYYEKc9E(*31v_5@15J(YzxPu`e zv}(vfthfzi2OFlatQn?}0eAT-!TUd}7-0=>W7i4AIpcgQ4`ZSyn`Bg@k{{9;g3L!U zUHC*BC&mz^c;D&Ai)k6rw=!d-VxTNGS#rOUHp-GrC60uwAiS?{|Be)f)qF!SM8ELbVzan|!&+qfzrDI)gh;U@p|B00KejJ6}UuQjR|zW<{#R z#Jk*-@CEo0pt?VsCdAR(mvQMuX+7`gK)Ytu2hSR{M(?JR&xpcjvo9261$4+49<%n zQ$0f{+yD)j#|(g_XwnX&ECtOh?{Wi3dIN9!Y zWJbt&I2pyR0fjt9Z%m|e5}y6D=X_g(6wJL5n0jbs*heC!CmNb8<2F+s_1G5SzcPQZ z9&2+=En#c89F&6?J z+0{Zm*l&w8BZ0}dm~THsi49@sG^T@LqiBw7owOlNY!xdyzUd7q%;^R(mQg|v=i_mT zPbK108>D$zcY>pW%<&e z3#(50^txV;NLs9f4}=RG_kGV+=oa6R&lnPe|bMpdHQVTj9t)us?0BjO&Tu1{qsJJ)>X5v z_Lqxs%hRbX1Vc5-n?}(Ik^~5f*=`T(^-h$ODJbd*yxI@ z>3mpn!OMSXQi2+(y`hUKWA&iCUa2s&Kd2hlqom4J7tMX@eYf5mq%$7mtIm%#0m`{i zy}>edMM6)Da@+6ac04*#63(~#wjPrjZ{<4TO=i|jB3zj2 zIac!yqF!b>W(W9}Gd{3bT;ts!6vh{F>`-018d|P}5nK~62s>&vWR$)E7#edGQMYlu zhrRT$tgwQYlAy$&cew;6o}}v|Z^`shx9`W?#IQuJ(aHIPsQBQSWQmG$Q}veUotb>< zd_GrZOT(_4^&5uo82` zCuT)~{BWA^hCBxDDTB3-rD6apY$%nJSw1=!F<^0~rM_VJ8`8$D_tBag8iKWrD%-WIs+iGXOfIvYOr#>Kcs{o_U&oOe0wcO8Fr%^zgo{h0P2bj04>cK4bw*(-XJhbb2rT zmOH{6PWHGbo|A6`c3bhztwfm3_P}E^dx#qH>81{UwC(EY-Jcjp?KT%Fmm^==A3{h z>tqU~>cc#-l4hiXaSseaK>t?&B zlarAp;mcqz6_M0`>p7u^?FL7m{SJ|_t46VUyP%W6lX)Ij1kL-eH`Vp6DRCU) zxY8V_NYpK3qRwcq;KNc@6jV?s0=vJW+fhpKqD{rvFy?-@p6B{FUa3YC zDxd!73#W8P?9j8$%i$t}F_ydc?K{x2SKdFK*TwAK{!N20U0M2kxsIQ8_IEo|+++r_ zm@D-*#I9PK-0R_>5i_mv(;$S^VmWiD!9;-YK9r2GDqEMIPRH}*=wy4C<4A6bTL&pY z)GJ#}##RRRaT=(Ca_F%fI6g2mU!hQpJ)hy6(=gY1(A%Jy&7|9&GcmAA$-M_I7GsFN zb;Eg2@MPF}L+!Yv>=4l{>>G$BfI#{z0_aU7@s9?GW&j1*QHr>Dh$YYSmBw^2@y_aX zuG@TJ7-2osA{P{1fJu@I6Yebx0#G$y@ZnEqI5o1_q)>r5ulvIX=d)jxQeG&a< z9hTN%py}znnbCL*0@}DG-bsfm=^n}3&7lduZdh6QrqnfiHHX=$B+V*;JBDFs&LAQ% z6hUFAxycpogy#KzCl-^0>_&3Wwu3gU@_FouJ`5nY9KfP=PJ%*W)4&VZl6;vqXn}ru z)tlEOifIBvWyNTPX7;WJn)c4!gh=Lq_m7VswWrM{2Ak2(TjQo3{{-UXFsWXSjiP}I zeI*q+=}d+8M9XC^yQ|zHL_@7dK%7g&=N0^@1N}0zg~lYrPI?H?6GgfwUnra8L)dNsfHzRF6B!l2jp>IgQ_p3SC%}uf|L++K@Df z6#d`6eLEZujBw&beUCNA+_h*J(v34r=--inT0{Y6chyNF-nF@Q!o;L6h&UB{1&A{A zunSRg8&>i4xTTxZppI^WFt49<4sR+Ck&||a^pO&M$pINWW1Cd08$94d)S6aTG3AU{ zXhae(U-mm|x04JoHgy=G8#y?NJ0zXP}rx{ACpIF0CFiVs=m zF!*7rndNkh-*L>Ks{mH(1s3j4v6ABmo%~TGq!%Gtx6Kg)lx-lw9N)b zCsecg@csC?fZDc8BnUe)PKcI}SUfin2QvZ@_?!(CbLJtKItPMZ$#VfEu26_jm>@Dn zCcvf!*eXwu$du2pH*NxHNryDKf_He0-A)lsSBBm0=pEFMz!sx+syxnT*;c_a6q;ITXd$FgBiD8>SD2mUlC0T{3ONb6fouy8~lq_G5w&U5geAa!5#8_j=0 zQVj-l2*f9vdrP?D-!v;XsyjmHj>%RS@2@f4k%=Xo--<^4P=Nz4Cms8wE5xwy9iNv| zC_kTV!g~Pj>WbQp-@xIlI>G*AKi>7@K>s@Nj;d)aAEzRXI3`aYmki1Ya6D5%OJ9*T zmag5YrKb|ED2EuqvjK*<5K{dAEjhZ%wR+R7kNTW=+#mKV-*JTDJ7(vn)2S3Wcy^vm zx?CH)xX>B0C2FKPkE+_NwPfo2lN^$d&KVw#eCwmj^+!OCV{J$vPj?LvQqZ1WDx&rv zU`^!gGYyV;ebjh2qHZ}lx#`nKDj3NtzBEW8ziF)o)Sg#guZ<|*>C}oJZ!gBI) zq5LbQ2CYQU?z=#nuiAMrQSM!bx{aRS;VShS)j50Ow;ia)leJ1ff_En}l*=_|=Pp^Loyez)( z8@eGG!1&4;jph^WW2|`qCN4PA#sK>wA4kr9$Mno75yMi)^T$}j$Ebj)zjn(ERkefG zy9xj)^j()@^%Kitv~)KVI#&^0o{Q71=_xUCzn*WFpnpLJ6EQYOA4;7-UYXoWZ%*)N zEpVjIVXQ%6e#|Oe4HXYm`bfw+-))7uKYX5<|L?wim2~Dk5*~j+D~`^RMEPseoHFfz z@68i5k>J2*XDTu>EO|06hkYAu~j zacAU6{S*=I1E^`>i~+IWooe)vg4jmX-sFVK@m%^}twqol4tiRA4_@4_fs2-Xb;3XO z&!2}Yib`*EM_v93K;xbk79>Mo&L|zlW)P%3xJHz>xClxZP8hUVz2%R#0MTiWEpL{Q z;9CA3m?zw+F{|5BG2m$gX>0Q*rS64OTVHE!; zYDJyxm^{D$*$gfys9-(%AE9*CPAx+bItV3FCH;o>ZL6rHi$~GQaQMGDKZ5k-D!p;d z19HC{4wut0jF17a3S2h?nhi&QBmKk$3S*W63cF4)#h)1ncfRUiptF<-?at>P?fVL@ zsll58diFck7(t}Q4!k=aGL#NA3y^y6|DI`6!?4;w|HDD>J6)PVdKYAFkJp(>>x9Uj z(35a5a$$2qES6H(hQ9Kn-wJ?fXzxyyJ? zEiTETz+BNQH;m<+NWBMBQpl$Jp4CuIOtMQx??7B45O(bVDS9^awxTl(s0p=8x5BAlLSn`lR$XTGQMw{F z<`UFZ5JDu6`)<2_JE$#-n_{|C+yuXQEG#x*DY3H0L@qb0jhIdch0V79`>5Ni*ohc+ zig>zAUjq;uOPm+bPq*$kfWI}$3CGN&Xt4>{&grtm5?eV)!EB@JkliBLf^^OL|ca*{~$8-MWN)SYv>PF|sUjzFhL z-T%&HSHlPO5nI(JWzp01B_LMC&+o-v(N$n)+;lz`Fj$j5&CIau6o-9N- zqX9CT{U1-js3lNLV|h20182|{JNjY3h-p~~YeY!%i#^wp8M|?1ZD^d}f5boexWc!Nh8MH* zarhG@J2MH>$H@iIV?>NQHV%>gIGr~8{qcO|Uu3^s^;6OMm0Q_1tK}73_ORchg)kwj z!T>psm(KMzMdRrh7$_Jz7An_0`%=>(oc02__}bKJ6Cnk=*=;YGbo<ktX=<;JxoKS!BMtN2fjU@&x4$5dw*+1i@}UlnKZ{&zX~$z2V03l_x({#XsuZ` zed@=7cY(|M1UHSSa|ccudU{Rgi+IcV{p0rizxtcsZ{GHQ`ZoWQ^?iL8{MOs?D8*{m z?()(;${Lv&>#dw&2rmbJWM_J%l`#bNL6qb1h$?3$W`!(mTr!1j@3K7Jr%Q&4Tag>= zkRdXQpTof`*PUTAUYtGsH^FN3r0O+B{;5p|-d(SePP`i(7f714@JGq`%7JUA&bja? zNPhF*k@qL!U9NLIcz2cdGOq>_tFc)&)}~lAInE>vAEyhooW`B}C1L&cn)>&x^Wwzr zZ8uhfd2Wxg6rryDa1bPzB_JWkE+)-<)M4S3>;3Qk`meJ10WX}CFiT%TsVfQnH9t!d z%q8TyggU3ir*Y3at}Az3KKg4Z%CQ&H`O^6`?mHgncE|-|*^s#JDxr4+H01TcQ}4Xw ztV9dYbH(}LuzOrDuFoxU7JWaB;)5ZGW)cUJbLiinsZ`V71WGzuMw)P?gd4Oq6E8=K zrn(N$toA)FgF8`G7Z%^giYEMJ=bUM}-Z54HUo^Qz8OXsYxBw6)Y+&tfT zeYMSVvHEdoplL2=>VI_AqC8@ZH5s?#+x3>`qe`W> zXp6#X9`QHuhbADN^$NV(x+LprvcIg_T^t+NQjklC1uyzHXycY>qhPQ}a|GyBNED!b zCH_-F-@~f2*>%)5<5%0H>7d7Nzo-@5pY)5`1XwQdmE!Yt>6y?MUrpYw+c9{d+^8ci zG4-&Ear_Ygeg*5}?Zw3!NZcF-@wi<$*RC}_#v>^A0Hk5pURoH=F&khxtdTKZIRU2G zz*xknr=;C_sMn%2vs`?fPEf=7(*06`*C>AGTgnwj7OsK+;Prg583mn&MDuLg+3ofc zu5Y`SL-x(BdN6`0JZTfx1j6bH9R9lLMd;|E1O!(+89(B?9&Y7Dcawa)h!9g_1}@7P zz3HYI1byWzBgapdjIi5BLUsUFLmylV{{(#4(3KS1q?&67N-KJLxqP|=oe5=fz2B^T zI(FdQvDV?wf`?vPERs8#KObvBJ@b=IUT*@!j-Cp0#R!o@7h(#1f%4?0E0ky%kKI1h z?c=&Z7C2`VS7a9D9#ve)tct5ygU3>t{hGnAh~9L=MzFgKu02B<*)cMDcGg%<44vq( z$zo>=l=i|C}>o$8`VbbU5qtshoLKV+V} z3Efu5X$=-%z6zY}NRrq5AgOFbqEI>>Nn;7mr zRu2@5J2q6d(q`FY&8;0H6?c>K5e2RVdBnzDScK0RP_FH9UW&F+X!Mv*v9K=ted1g| z6zb+E6~3=0etY0b9&x=~VFIGW2(2Ir;jU`4RF2Rh5E~_^#M$&xym0y z-{eN=uAztG=QK!S^@fIGK9t~tmwq+K_Qs$Sg{9OSjae#pRWIE^&e!CI_Z{=qQBy)U z!RU5jh{MO~ij7f+P7uIr^y%P_>>$;pgXnMifJkeAm@ zi_A7V1xDo~?c=*^lU)~HE+<|#J|$a!)Ft{6tiOyfhtVL$eZ@TqmqnbqYa8#r9=|o- zY`r0v)*!g+Gml9)*l|v8I&tisU`0Cb&ZB2K@M>tEOFXahFPC5ctG|6;?7#f^?^b{Q z_T0ok#?AeSF|v+&!eXZ(6a+WJL6T<&1lQfNu6*~T(K@mj%F^= zwevnAFB=K9(g@A?>%|M5ro$o2ih1q5gDzSRY~w^G@yGYkFnvwO2%gS4=%3%{oDP@; zFEdavbOgzcM_mnWIY#Hy&(!(!LI45bha-$ty)n+aE{`2ywu!}J2b_6@IfT2*n0>sxp8aS) zhgdIO+t+=+nn^#G)6vK%W`vh%UvCw$B9Ad3r|!fZJkV7&K~An5t^c$1U`E9J=j z0`lu~sj|3V@g(WFaMcdl*WyL0hVF%Ocq%MhF8LX%yit#1A}PN?v{U{!c6`;S zu;RRvM_4fL^{RQlNI&S&ohP(Y3xC#n8I) zE((*IV_&X%aB3D7Wlb|_KmYjnyf^jRenhh1ffz@MlMvayQlKeA7ryxONa4s#<_H={ zv3)N+v+{ML1HL^%s&6$G0xA(lS|A-nT*qBXx|mJbefR_7rK_wTk<(3)rRh9pO6y-`?J^eQz)9yeF11Z)C{?LpYOa zknJ`3*{)w2-6i#b8+@gWuDrBObWmi{s2f>h7$zaMMo4J43xMe(M^K|K?^j1#CaGnJ zxC*X^WI&I@LXj%#z#O|VciW@Kl@M%Eg?V|-?nRlk93=qFmh0zYDS{M4pf|W`HouUS zh=d%Ft~uVVMDyqu^VpTZV#JNOyP9tv*HQ~J(H5iF%DzW{!z!i*M+F`B61S67my31J zY>iVjH6H1!TZ?AnCipw<)~%^X56gKEs2i&s^UboSH>7(8*De+{1aP6skvZvt^=yd?iZO@-+Zxl?3^n$HRrGLsi>7KFB5w z#Xk9q3NJZ?9p=s$r_2JE*Rvl9ZY@Jm-6IM^3(Wz+h@e>W>->kI6*tc3Q?`<*s3Av6 zO7qFB2yddX=ur{p+iB4`P`KW04`1HDisJ&dHyk%5d%2CBrVkNK)UT!yCso&cvX!D` zPmCJas~|r1x#e07LsfT$=S^90Kl|o#J0Z53J*z2iu+Y-@qlw0X!dO*su|x%Z9#1Z8 z*Ge%gmZNyGiB`s1Gi=E5)zl_W-}OAq;t4<0fZ0SzUyglWq}?! zu2T}k&^XpZG0Ioj=fIb_=Xm8IxpT}KtA3{klMk%sTDE~SCm{hJk>QezM=Xnv5$cDh z>A|};`u9GRIA}}uJSm+{k|m{_Wb86^#(CdTfNn{Ao2%ZcpUQHvISo-u{N>{Hq31ZPG2hB4b$6z*CgfgV=iR_C)aMe7aoDQfpTsLRc7Z(?CP0 z&A+(ZcnEfqsuMzCI#0>h<3Qlm*~ESh2cNJjXUHHLSmTIam~RPlk`jWAQk(Je z6=_ey{ZR!@Y7^08`gQu~r2+OKnd#;7Y3P1ZGmTDD^0AOq$EFWgjkH^uc0Bbz+2m+e z4_?RfN&Jyg$!BEwSo6|`xlRtNU*}oZ>D;4*i2_rT5GMKujSVp!IJ}(!q@tqt?Q%Eg zbMvk2uv%;v%>}&yvtueA=knLe$yQ$LPOpJo#@d#8$$S$ICB#;IdcN5F**?bc&?xO& zLhX5KzBjM}7lEkduCyjL_X`)N(}xCw&VD7NI`n7q>{sjAt@GoSP|&D*`$CDC6?G;} z?mgkx9GEPI5DDT{Vh~ERW&SoLmd$pb>erI*78*okUNABcL0<-R*E$zhFDhgao{}27 z)Nq6Tf%)U*c7dfyzXX3?<45^{b%Mpukp+7klB@hqu#GT8T zmGH@9X5&5?ycgpz+03HI)pG!ty_P=%c~d*RGkY{gKn4*z8b?NMCLHbdyTh14$ui^) zCWe@Dk1!`D8o+C{oF=D-J$WPNSn}u7)LtjKD_`w#U}ap+y4@WJZA^cnL{7~pU5@TX zD7xl;#v)DluZOsfb>4{{0wETpCpstjC^;y6<(0OzEQls4W0Iqmdv>Y51{9prB3Ekh zRgmb>igS^*xmdjya~&IHOfWQ6<9LLOSXnUaOyITNuE(vS?Stn+hHhr0hr1QliANqi zUrr+$%r58CsI3}@)eOOpU#vNdUkap920G-Es3$-zb*0JWcDFmc9mcd#`4#UjC!@(| zKR-YJ?hk(uwvu5+U%5}m4KZAr=uzOMI^KsaQ@t9$h~r|4aG+Zn#e#4>7{qG= zoAAseKxc-uQ;3KOV9D)>S5ch1FPUCs0;a;HRfgQ0ndx%u=?|IxgaX43Q?%p--g)^b zyizag9qDgPnf^uUhz3b%GO!pMWKgRXEDNo&DCfG~uTp`47nm6F(EwUbFL}{PV)Qrq zslLK~qToZhM}H2>YToF?P)u;nG$8TJ@qwe~a<_dOKg+dfWvg7=BT(DW-gD@Hb&|vf zl5o+oKS&BXp2srnw1ckgXkv>xVX+!If$xL<;KqRBTL}HNTwGs|OBj*Psyubuo|N{< z>=)g8wqNk|?5gjY3(h-V{0}Q1KR!UzObtPUUuwrsZeKw5qTXGTk`*=^C6bDT)sh5j zGY-2fOt33rWCpXIYu4H(*)YY(X|PCG&Xob@N?4ZI3{z=@Om4(gu4;F2K|8Jui;w~> zkDj$yihSvh!Z}wbpTtZmDQ@gD; z<@S|eRcI*`>KpCy9=j%3Ts!ST+P@lO+>4TDQT}+qZr(gq3;r8-Mz*k}PP+o=ug_y} zK1U|8h8-1>@SU=s+ZH-nVN@)RIjZ*Z3cL(&zzzf(sim+*n;!8r5)Sdt*-OE)rH)y* zUrJm4T!;V=MGRxH#EV~+oUVF2YqK!XE_95qSU?6aERqZP!*H91bu^qR2?RCrNIg3j zRtI( zjctp?``g=;G^cKdMYT9A$UD}L{*wYWeJl7FlwaIie$gc>n5goK-ckP$2;sS2LP?FIvERa=iq zvB5;oi@d+8cJzshKXyAE=)4bwxU_$pYPK=I?j>d*+6&8C`Nmb#S1wE@+=^RH>3y}@ z?+?5EfpW=!BBo0uTn8?6fGt9p&JcoraP7jWXB|4}^>YL8|G>a}JB`@ENPlAbzyeJW z6An7D!EoqGw^Mta`?jtIv(TzEj4a<#r+Q2OuBBOo|I9$LQn53u!&N~od2Sda9qUkU z6FZKF-p_)J^t7D0X0=_3=N&I354h>kBX2htS_u~0?xL?M0P2M!5PrQ|?hZ4VY`GD2 zgx7ZTb-(ZW{sarMt>%`u-KzUs?Rr*=Jbmf$bX-%sl$p=sy}ExshvnO?AySDu;JD=7 zkNBoiK}G!{S86YsYhTsBUJ0vG9nf(qQRLnu|K^cNV{8VXfJ~Mq*!^}(OIgVi2SvLTCQV@ zi_TrJ+U}bW+a2^^&`KF8Y`bQxmk@0Ev_RQTW#PTxJ)NvF%lpPU_2Uja267ilDXLwS zy?SV2$C6o-e52CHM8m#Hn!S|BsrBh2(?K4QTmsImJF{n5LSjTzNe5O=em#$m+#J0f z;K_p|O16S^-r3N>`i{fFne(g4%CPg3x|U9L-@5jlqSZXP&2b5`a{aoc zoBXG}V)7v$>qahJiF~=rNURjq>`SS=8e_%A+BGB-h}{g;$sV+tk+5ci#@X1F#GP)r z?ddx11+2~}Dwks>aKrott8JrKK&CFdZTU;X0k484AD-Njq3*_H071DBg|wje|MFk_ zi|OWU+VL%mHU`J4V{xa5GVYccu!(|tT*_G8n$jp)xN3?I?d=(>9JGfamL^IN0D4Ed zMQo41XsW4)Zyh%KJ=+mTTe_MA+fRT6i>6pQ41m=kqM$?hDK?WXI`8-Up}=mAM4eW! z>?-=Tb)spiQVo+E&;bJ@>D--UvaCYpW#~j?EV$f-dPeHR5pujw%RaX$RvJz}NcCYv zUYad1632)J4ty&h{(NwUFmSs9ywYg59rweET#Imi?rN=v(GLmxs8{d$aq!2N209;v zdvyo4^iDgdF<#qLWEf=uUM19lXeVNjineKjBR3zjeV;!*^Ym6pyIq@8tov!qej=1K zK;35icn4HL6C;VOgqrRX7H;+;j*r}9IGL2wJ=SADNmu$%Yn{*wey$67Qg3L+HdFVSHMEDG{j5tmh!6V2L=+2rwKv|7df4w zcWuO7e{2WBRfz2N-0GoXOl(du6H`MnKjp)24D|l~-YBmgFFUfv zD1;=qFjP5?GspK}Tzl6#7-x#BQ|Hq_zK!eg(x(Q_Xdu~@wt6-UQLyN4bmnE~Fd~Ji zxF~Q8@eof4Ok5t7BQNMVvs0gj@G^v#{@JAWC_w#~#TzO9CU+;HVH*>(j=j5zY&Az% z7nTwU6Xg|hn)`@^CNjo-^Dg2D5GxhvM$6r9KluWdCsBCmkp1hEOpxc}BpkZP9>#&; z4BfX`X?Dv{r|CS`ZMSPu(-Mv2tK{Yq$NAl0oc*sTXjIpbgEL0Se3WpfqkiJ3J#3X^ z6W1C5{+Z3#8sy}p4qr{Q{rJ&C7YearuLRyi%*98*LZ%T31ws-PLZW)yp6~PR+K(|V z|I`1&{}I$Uf>a`}28#{Q`D{O>WV8;r8<^uQS1JUk6zI$!ve`hYI^i0y7H2jmCfs_w z;IP%cXEwm!v&C8H+4PJc$1XnJdL#tU@|@Qv;J4bp-DdO8(KSAI+if=hwS%qMJ(SX@ zmzoVJ%;fXf!>73uO_9bFPw_c?k9>^V)7G6#jR_GdO@O{O2#(0>HGo~Ip~X{9x>F7e z5zWRUE1WK;Lj6c(I@LHpFl{lYQTd^@<%)C48UDVfpIe)uc z9yVjL?YRVXsZ)Da6ffC?5V$J0-yeu{jCqXsNTpef7~}v9Ra2@e$hilR$5ggeKX*{& z`|VuaYl!?yA_hjvqr zGxW64EL{N7--Id^l_gx|eoH!KO>Wc=s^|T4K<#`zWx?$Q+W1N$yxh)og;5=joz-M1 zsb^pfUMv{^~$c8m4v9KuNttB{` zuI`D=l8ta8Bw&gYj>>EP89;fS3^!0R#JdK}Vq4a4{!a49KH34*o!{dt0Ji~Jwr2ig z?N1<4-}_ho{{6RazyJO3-oJb$0b`Rl{)87|Qg(D#Zja>ee$w^0I}k1CO6lDnrni)+ zE8dP-Y%<&1?K}c&=_mFUK$|bKOgUJZMhJL7_wl7vZ}-A{JVp}Nk9HG~unhX>=I^E- z#uhkLqUbqE)%#F&S;4UsqEC@j+#Gu!ns6%m4)|N0mLdj-T+EsV&a`}c-EG5-B*dVi6 z&vAD-ePDdOUKXrM59NhQtd=yJ4B;qv8K^EuHSUOTIG$*U-yUmL_b*ARN#Z0ALtk%? z)0tSrruBBg4N(|9t^%L%35)^|n0@xn<$(-D^#GYFISdHKpfgQ|b;bj9YvaXT$JvFI!O$Psenw&#OVJ`GIa z+K^yY+w@&a2YU0Z-f*Pbv}3q26XZN0d$~p@+9PISzr0pbnEg1O=zKd??Rkq{kj92j zpT~BNjZolPm*^Q&XGPVs-)%)qNgy5v3tAEa3mj2eOXq!T$+vj{gsE@ayd|6O=zIBG zJlo(@EsEB$4yon1y2}UYKYcc+6ii7(QwyGpq+uaQz81PsKAyL8fx&LC&*C8oKe}IT zEv(MqTnfTXx*Ah9qNqOW{R9QnJat+rKxir>e+~n5oC09bbo}%Tp}J1sZ?HW zwt|diYBJ6;sl&T7hQPIcfJCSC!@kLKk`lp1fFW{%3@?5LYrfQjYr~e^KvLx_Dfre` zF45Ql-Z9?AzuB#3K0yTf+U*YLY_v$KH*rdwWhj|n)41ub99QP5Li6rekd>u<#prK$ z(VHlqyM5DS)doS?yn|U8*XRaCbIvtH369)32PLTsQmW z?!A_nK^B{SE#OVLP_K^x<%JG~#C)||?cVx1swA}1?DOJn?PEV-MZ%lSthxDZbDXSV zdw4F^fA~1&Td$^*5Br^TONv5L3>I0~Unax3In4M_WrDVxnIo3`NN7OPJ~~+gst)WqOMWz->O#onRAkWf41+5Yi5(Htl+Adu zp@c(X-f0N|<)1}Qp4n{Q?jf+%uRo5uU>_Sr)J!O6NEPlHqe_oTp5k5$+yo!&_Cehx z(oZMZjffI!xuWjZNkAr^9&|lql7X%;I6GaRyF)+Qe%LG?m$sdtsbZ-H8xE&Kc$!=; z>=oSSljG4GD4I&FB0BSd>y>Rd3fvY%oyaf5&{&=~uXqO+_7wnfbzwJS9d_xDkx8E` zL5X0_m$S0IpPZ=A=9^Uo&HuHW+Fse>L64S;zve=YM;tte+!LzT;C9%_W9*B%n~d zgX5O7`}IU9$Q!E9xfz~FTID^ZAHLgK2ZgpQ$|w7ki3Gj^@MRFO0GTLcL0JzI%~mCb z+39i)`gbpNkW`<$eb2P7@i29W1g*q_a$$>fwJP-#n#3lsQ`*>FS!%+UA(m(Jq1oBM-gIKk(Yyh3Cd|LqOjsnO#)OY;+ zOz7hoCJ5=TibZ(Rs;8`;&gUPWpZk4#9p}%BBs4NZMyqE+C|i%%IxIH}V=_}f%uk!B zrjVC#CZrBIm!hT|X}m0SwQQq&J2JE!*;fYILrB-6M+0`xdi3|Iru3pL{f{B4Vw^$4 zxVCXoV-#0}H-O8cx$`d7Mv7rg>rzMew&FIdMykvOGi=*sID&;;#&l`_HOI1R`yDHQa&}d0v;N0} z1&&Qt3?g4zEH9^iN>LAlC~=Rer7MXuDg%vmHNmxeyG5(gZrTh_zgFuHvGjKDkuptm z`MzySaHn`psQTAAkwcfj8EFjQHynQT>WMAnlqW&eJSq?O7PRz*k4rPdM;Tk zzkhrhrojgE>3_D%)2~S^tdH!$GV-iPgV8=@f^ zWgsEip(gL+!Vvk$6k|!G9}sL!JB+nn?8QXA48*1lE_GdrIVu@pWa?ymGk({0JPpqf zYy}_M`PR!-M(J88Q)wq6Zg-p$Q_QMK9=c{Q^H)zg8T~VsA+et|jF;hv+)3><@Jdue zV?@C`#)$o=0aqHCqN_9lt7Mc@x5&NT+GaP~E*Lys$|6DOkuyR1oA(Ar)zs3QQa}g4a;dN zlmF@v*-2w4t9od_cke!q;wrp8B~ZPSp09ANLs7!e^)QN9XC{9^jGm36;Oowp2|^xg!A>%_V-5yl#(G*oOE-hop`EJ_y? zfbxC6Y5TLx_R%|0uXb=fuEiMv3X~EY`B9IkZpG1+%%Du+D#l*47PP%D|L6ake+;10 z55UuImev8D=RniyeDB#+#~z~qA+PWKzU){M#o_gSe9SK654u0!q&<;t{;7sotVPWp zr7~Myo-<#$qU8mR+2(4~#(#sU7S1=6iXzT@jHi>U^Bfg&P@+D$s-^) zzN=h*x}NknU!o5^@-CFzq9xY5RlVD;9DB#ha|UZdnFQSqCl=Fk54{4E^Udi~rOQm8 z0r4wqqP%_@Q=H+=Vmi4@@q#uGDWmwdy+E@u6}(vEcfQ(=10G0*uTzWDtM4BtA^Q90 zXXu{m(mnlGg_ROsO9(~}%jITwYBpfelO&{^6>&i|Z8c@ND));l2nIb{C}&ef(LR*A zSbko{gV`m6ZmI*&mLE4(x#16-jO}grZ_mZ{$LA&Vzmm{5h$m3&5S^ig3$>iGfm9W1 zC!@aFOO|w3TLS!SE7X9kA*0oJu3lThYJ9nq=djT+XEx1LErR%jps+k%+lu3I%rXhN zcDm}tqUOu*pXVQ1B)7S5{hh8YE=?uK@%&h9zI>iIzu*$V>)qR8y=SH2W^ru0e{J@k z*N1F>RgR+n*;6%<%7WV^^?ded6TxH5Z{MEtUr!*(re=@X>E3gUiS)$Y*6U<0)-Knl zD|NSszuUi6asbfH$}hp1X--bGBxt5`@0mLL%YAlV^{7h;@I+YH0I@vJMIZ&sw5-K}# zu;uRYT9MmovsYk$o}mziuw#(V`Hu9CK5o?EVCM8>dBJ462;TH`AY*3+T%N-&wnz2S zi`&kxobEZ#yP<*E%DcbEGBs`Az7T8^n?n%{pJSqSgCC1PDW+h%7?n1LT zo0+w9FEd!H|KZs+Ll86`dJs|@%iox$XriSbw3a;QyB!N(f=UJc6}nr_P;N2!rg-HS zSxVquZ+5%orsrdJ=hIAwY?UvJadZFXBVO#EKvbFR@(x|LeyGf zO)Hnr+m639?uC#OX}?M#f&-etfJ)JJdgzMo#_ti1$9$-(>%5}pMxn@PYHBNjk(+q6 z-X6vy9i7}2K;*@_plX1oDsX+g&_=jzyA{6k3#5D3Rj_rSU}H@io3dV61OO^1^<&I3 zUdf^dUQO@7M5HeIHFpVN`Mk2BIhvmY!egIvxI9FFkh#)5?9!&qem%$)>v-voh3-}G z(ee2vIdm(tGwr$@OTp##?biE=lK?uB-^j}7JTkV{pypdpvS*qhW*syUoX0I$aHkPR zUbq36r!mblJ>u=vqeYtr3d4+yhbVGr)Y)nC%7;{`ZDjRPtU-lt=|GZ(eom*$;qayk+idp0%s>dBLx251 z)a%j8cD%K8?ug>|-FmxjVId4XD*1Ik+}SiuUXUvU_0*cdges#YsWgSb1sFsV zWR)L*&i=YRCULA;g>1PUzs0q>ULFB*L(IF2xJSXfo}t`XNE_lpC89vkHdE;O>MtRqQxB+z1-HkPyy#KGj5n`3l0p+k!*O) zz36MZ+um+hgNbQ!1ZfNvF{0u63A1~b$<{zv8_*@EAkUq87gNloN3&#mFsR|s!HiK& z9t3U32@$zG1^J@6nEv(ut=L>-`-x4Ps9Jvh@_G61KXww;?|19JcbM(SI}h3li4fww z5=dqlC^n%tijE)fZohQL& z&Mb%6>VL50B;#~UyDT;|)ZB69GQKJgWhSa0d%yMXSYs+CKx6sZM{Y80#L%E1Hy%sn zfK2d^`#9-fv%RRy*D~xgy6t0)s>3Cu58vp2Kc!-og z06NQdUXQ!tKF-&V^X>eYe_o$IE)TfYyL{-(AAX$v@OeE3F<0MDcMU$(03a#w{eljc zKTg-r%fop9==1XW6~vpZJ}&nb8kU=1Dn1~Uy~g|F^mxgRAL^Os?EC5Bz~7G(3C!^2SQ{zP!QcKkg1;A# zSB-JGeE@eL#d330nB5kCxXiEfjpRT&wo8P z1j->W70C4k1Fb>&Uyl!hqNdODg^JI>DaG|upX#64k8TE7thPHHBk%lNVqa#Z4*q&U z)%n(rwI`w7R1V4q1PiS`9+?^Q6>63=a{avBKCjo0>ji7-{pWItkzc571(8$wL#YGS z7)=?*E_eZsU-Ru_zL_nRa~rN9;O0%U*9V%(;i_1@idbXZ*Xct%jE65@8V-?aQACoF{N7Y7&DfLP@5cd*lla)k>G3hs8t)b^l5y^Z>$mh2EBgNxCl z9BNj)K`VZQCd3OYtMy)pH%`!h(P}FUf=G6*LJ}*&luI?w2B+h1sdNctj>S^&N;o2R z6)j7EzX=>VyU+L<`&4T++y7(y%HXp7lgn*;PT;S#PROFVh}dZa@)o+Ed50V#d#Hm3zb;0#(Ec7H9x|y(Lwh4X2Ti5B~%ITP7Al^2fMmo&nHM0%pDINDC3rodpumF z;~0es*s_woecwL9J}kUvDWysr$#-jr8*4Hr098P$zr~3^ax1MW`ML3GyMN58jX#bT z>9!@CkauHMG;DV(0xW|FFRkPv^|e~}Q|IM;N?8j*`SDmEAUXq8E|O=*&9kX}l;}(+ zmO_vVR&l2` z`SJz3jD+#DLq>^<(=0@A)>X(7V$)34ZZ!aBfD4F}$vL3iQ3rAYu8R0Xv|N9Rw|RXt zXw+xuc(1PRL0Ig3%s|0FqmGq&h{%utSCh4DXxO}}40sqfMAJ^jfb@JpEislxgro5h z0eEA+v1!Q4LRd~$OP#w&8IL{$unM39GGXk8w>`TH1+>*qXO)ghGUHwav+YiM!$WLL zqoCwuQ@P458fJ$)t0^ZRjkVqF;jPc(X|vs(&aJesHtdROashb^#C<+4#L#_j^L|9N zfYaLI&oa9drV{J0Ew6?EytVn!`b~g5QFJ_CF1wXNzZr40lA;9h zB$oXU6Q_eI2iXOo?LK=5NjrzH%3wnF6|UK&N|6K*X%v^8zMuc_U;jJl^X)HwzxcDa z$8M=}KnhL;7c2kAO_)qw|CnO``)WI3(9`X8c`jru9@P%}a8I{pvb%!gnBEq5 z!jPrl02PAdCo{MTqPosjzzG0-rIh*g+^=!GPOex8`WFZ1Cj<%2?tuIHEZ43T z3~CYwA~Fxj3LguD8U=DfnB+&5kjhc1G`q`T%`q>bD-Z?3aYLGpX{8TuQw6a0L0AgNe^B--5 zRA?=KLB=ac4bVImE8ViJU#JZhe9k{d8!{K=C&^sy2916+`}@>STadfAAwQEdKB4I1 zuQQ)5&^&zo<8oqEC!DS3LP(gH!{A%BCT+I?t_5^gF`xoOYap}h9GyfvlOxqIq$M5c z4TdOV^?c$Q@=<{^XM=zhkhF}4ikY6iA-RujU(NF?aX(uwm@6vOug}%;^G><5&-?B8 zXonNqKc68{46RQmo_Tz}R(Hx(z?@mw>dX#i>(5U_t&t44d{Q&$)I<8EXXNuZT{G$D zcveh80jEB1_wQr53Kx!5GUul%u29d3PM4diTHsJ#^BDDBwRBU$WOye64EC%mwve3H zAD@Mq=VJDG1bckdW3qVKXTRJ22%n9T9wj|=wlphbl;&ter>ne#*SXmEe zRY=I+_YW$|6D2C?S!`~Pj^ZC?H=M3&3V*A(NHrFm$92yP#RWn~po{?Q)~D2~(r7sm zM~O(Tkti^Ft}0mq$o5uH`jzTy`B zth@$IQQw`>zgQoVU>*h70#pXneSL+7~4+J(}I}y z0l!+oYgT@>0m3w3X|niYvonftprs@<^cd!tNhh!6E%f>N?8ag#NsZb>9P~Wwuz4wU zZweX&lkY(JN^aGAXN(&r8_Tn4YCIXD`J?43@;G&CnEY%Zukw&36e%6|~%*Th`mI4&TnA z=jK42GPQzpRMD#$+zA>&c+%PZg~;b(SK&Rf8(*2;#n8iV7$~vTjyISwQ1ih&owd>3g2o#Kds7wiURZyMYZNkoWcez|CQH zQITzDkPJ!DRk#C8TkTi*7z~&oy~TPzTko2OQny!2G*d9m7v#=DF><+NI}x(gYRG5n zkOpk@QCiT!_4K;_4a1#%{mZ}D{pFwBw=IPUF;P3$RW^(;5<}G#i-eI;61=*fASZ2V z(r`mi>LQhcXjwfhK5%WPnji+{I+_SZr${2aZ zL3(^fjDg5gf>E~V`Tg7PjS`QG;y+TWJLA@s}nYxZleY?9nA2b z0rb;F3W43Y8|mk>P-N}r^V_#?r9bK)KyN1#w43N!IWq2sWQ7W^$Ni>ER%{%0+ipZ7 zUk>cGAiJxox-~edW3l@9I1*Af)gXPWRWv&=39-ZOrwPQ4-=ri*wH1-HqPJdu|M(<@ z_pe{>mtU8!Gih(P+ZHSt+4uoGU244Ad^;ZT*ODb3^`^GgQeC#)^*kZwM8l;y=B0B5 zw7OYzY4?OP)vq(+F3YP?dSaG8)lvD6;Vc#BZ7+MYA^E!7ttBUwu~grxXp1$hLAUKY z!~#ue>@tD>^YhcS+p%#x=5N={s(m9LPXTE4ykFkt_k+r_EVYyIlVnRY7xFO}zms+k z`vXyqc^Q2-mI@G?(=8@+*JFRwY>fDJ`SN~%yh&bUfnZ96Y;ZYOaz1ZY=|Odq8IVAI z;rxEsF3)1mYq8x_s&;?s>$>iFPHp^6Ru|qhn;t)GEbl2I*XTAKIcUAxj1rb-0(MPj z%x~8%lAV*MhnVcGW(-rWN$F z+8scFHX(55nINiQf8u?)+Zi&r#gUgwW`KB}=@1^_au`fR{k;5W1$y=VdThsxwBURqTWUdAz9Y4b z{ia5t=B7hd5KIf`{aD5I^d=)M5cNvnvpaC)U6lJ ztO%l?<4{V6CgJhf<2D~_uairTaM5-Yl~FvluVDPjK|3YZyWLimO^+gITsOy4kjHic ztUWN9Fo>OS7R|%8zY$((j`78QiUmIO%VYBf;t7)Wvv%@nldQmUItnxf?GC*0IB-0Ro?RIYv;pbVIDBm3p z{qU!hPc;toU=-ESxoBDF0HU(0Ol*6f<*@>9Rep zvI0$RmlY_|G-n#68uBMUOcmTPG9EQZ#iXafZ<22hJ-!U2(%N)~7)1m@+CNs*Z(}-$ ze7c+@{3HaUgF`DsUI)oqnrV2K#VDA1YuH})Jm1*6a?Jwhr@d= zwwOPzHwOIKhPTBZzW+G9e@U<}DFCh@XqjknKK0W;nHjo4ei4m*+`BD!B(63%$+dPR|;#@ zI;vn+0LWh`?gqA>&&`C~T{4zl`mLB@3^TH2Ke;Y9G}ed!8D9AB{`Ku$w@?BaMsO>b zVu8lL7Ne$E~I1QavC6k({dC z^-?v>&1T2bb$(x4;92{eZetr^8d0;Fs4h+6z8pu`M}z~pk%BtxT!v-5xP-~3x$ z%AY=y7Y{k?_9=o-0$vu~w^SNv zWsUy?yThZ4`IcARn{Uj_C`lza?(hhx@%C0G9*6xKdAddx_`C1F0wdqP{Z6F;EKGM} z1dH{6ESy9IdV5L_QPRO+R>)QaKAz8adcW;<1tU^Lg__o&Mwbj@Dv2#BErYNAtDf<%7cm16 zKQ8xQt^n13w_fgM*L_;cS)}Rh}NH(!=_c<|20hFG@!>{Kv(TV9sLHlaY zk3?g(v5TO7j-;CYqfO|u<$4L!HpM)uwHyYu22u{qADj98;{#R}R^G(NZ4vN(KL2?C zy1%}*&zVeCpn>WzLH^{4*={<#z4qG$z;JzBAtSIW3!QC9z0FyhB74-1q{1sWf%j1I z@qE8r@HNos{8KP3815BTG8m&YqPWbvL*0an65qJ}j5%3H`{-j144 z53krh^F!1{`rPm4O>6i3C9zUdS~Gw_@hdRDfRJVh!$C%yPUcf%Yv8(A(}2Fa-A=;e zdLyuMwL|yczpm%g2ky2qA*nsJZIcG`5(lAz9=R~#vul}Ki}$y8DMv74-iR;dT&{W$ zn|kn<5w&KBusi`bMEe2FAaoe!3R-dG0l=a{lqxc2s<;3(#M?V$saDnnwLSTjETSy z4z_EVj|ttxF(6o~R&sCXnj4aj7&{1HCe9nnNyHt>{Oh|D)vVrVyl{CbugsTdM!EQX1fn^4dr zWzN@+yr>zU>Xy=Ek9SL$`8I&&n9)zh01t_Z0qfOcc4>320wCb^SiB#^3t?z4$Nk~^ zj5`n2BVO%6ZrCvjUANFwG-gQb>gQv)ysN4y4$2R7mw-r%*qPW4ed7P|eBy80?S8db zx8QrDi>60P_O5T1q^fmz8RuzR|MX6n26}sc3$l!7VKIhs$0+jkd^=u6Z#*|W+SOnOMRv{oHT{Z znacjFZ7YFJ5(net)bZs;_B$GYMQQ^xhJmmz$d#3G~ww8o1_;$l#%jeqWO8g?IP0wih*YRn?ySAN(W zkI;WJT(Ks!&Sr=GK~dUs0>+~wiB(wG?Y0b*oX5Sn{9RnkV7%#{zZf@#;3I5NIbG9QJhXk?K8d5ICgsI(9gyrrey)w>pS;vBCJwbYyHpv?(h12B5XbHkM;tc zPCbfArs*Dl_Wqt8FU3(i8lTtqO>^3!AoC(Oz34OnjT|58L3*U*o24KLw;v)`P9GHtBP4)P10-E=hqLN5x0 z;gbf!C!P&jWn6U@dAse~kI&;`*#gQhA0Pet)PDcQt)Hsl9&tbvf&w`V@Do70-cNLu zx9b)coAm{H{qf`L*KcAbnBt7#=z7kwVZM9b!g#u14Y#xB{ZE8{ zIO+O}j0rBpLN5?MEOogWZn0`rcS(8_f4Pd(qHQiVVoj5FSRP)K!Qqjnll$YLc=|iT zU1r<2Z|^Q=rDNw;>ip5NAm*+)-9S@oo&_xJs4cK!JL z!|t%X&Gxd}C}TKkl?Gxc4mV?~rkYd!IJfhBz4^oGez`6-%iXF+III-I*YVgaEGTLT zaTs9*!0U25e;mP}oM8eeO#|luqVj1TOo??y^6aYZdh_w)Qv~0?9j;vYb^pAc!CTT` z5`l@=TrKKg@lsB-JBiWKa>XAtkP5G#oun!Qjncd{@2kl>1m(7U%arTo=Z{}^Et;YC zC1P=vp|VM_(SR@5je>v;Vpie#+CTMdU$Olj&02B&%Rz zm1pZM<(>A(QWT7J(UQ3>dyalFyDH*%6z>Rw==I=Qc}anJ4X7-4i^tpcb^83JZM(CU z(cTX2)zOytsDru()GG<0Mi;{iph-5n)8O;-LljkxDsRFr0ACcY4Nul-v&TwuhxuWL zsynlBE%t@ubDOWIrxybljb*Zx;T(%0G$CO9#&udl8+MHQ```cmd}(VBz6?#~?KOJ9 z4bScVO$3-2G#;=f#=TC*qqrOQ z5ZC@7Y&8RXyYZVV9f&8h<+u}}pfo{*A-7DLy`i6temT6$&?625MkT`Y<*b3wL=bAV z!`qv2U8@IKTuF~eo$ywUcDz3Y4#uE@TfM6NTJe}ERL-h1iVL(gE(d27K7_Bh*pQnS z6Dnv-F%WgSZr$j65Uf&@K?m$?+-fRl?i#JiaV_4+xbJdg{bbA z+fm$;fZB;y;-3K)7#Tq?I6J}u-QAn!B!uDD49_c34llT!g_oY6v)rutGDDVNLs1~F zI^pqpKO8RSldAK__g^LMBJP^mU+D$e1fr$R8DVM}0hv`S!ea^55JYXH_xJZf*mbid z($=+ht2LW+SzD{;ZERaWW-sMp({>^a$I&h1&71{DphAqqsn<9z493D((_rJQS-irs zlLkTze`Ec6TP`<*u~{okucDwfEC*(51$;g~Y+BnS5J2eXll8|W2-|IDe7F0x-oo^15Q1b4TgbaMh8cbewNB<|GaLY2ArV#eJt@(c0h^O=U z<#yw%3V_9{DVs^Tf+`IPj~aOW<{f!DzKFuHHp#b)`B*^Qe{=me|M|cA54L~u_8Jvu$7`q;L_Y`O;aJ6N$EurXBJVms4D9KKtsio5q{5d!q8Tx7&zXV*7 zR(Nr8vL8fFW{gh3l_%;zxka(Mb-oetrO0WoOw$=L(Sj0>?Vcoc$n|nxFAM3~DJ2?GByyu@B5r+?H4Y<}p?-XP=*P9} zLA@cC_SIOfXGmfNw=UC>%zRC%w4CdW(CU4&ZT)BYzyDAEswX%-ZV-y0XOFAs+2dz! zXM#Spdo|TT3G?fx{JQC(ft;n=tOH9uxQWk?f`-NhL<}hwshfcHMZsGrpTirim@`Fo zv3TBI_shQL$2E_U*B1B<{K)WOk?K+EB037~mWAzp_t|D$sif#kC?E?Vm7AN)%QrZ* z4DCK|!C);D)8}WPL4tashlA8Z%nl| zEr}GXKy}xMYlKD$Yh}L@`g!KIGRml8NbbJcH2Weqa6hE9SS&MiZ`r}%qRFKNf%Ug< zze}<~tF}YgoF>+wwYSu2W}v_GK#UCdw3jJ3~&pUc&(O}1_MoAs-W-ICX? zW+pv$3N7|R@46CYIc#e^S3==cv|GJz_gnG$_UPV2vXf3!(PPUVhhHx+wb`v(v6U8A z#6A zrjaoVNH_>idYUa~7$g%{6X4=?{{pO0%IkrQvD)%@+to~Cqr59qmMrRYdu7zH@-yiz9)~5=e3ak?hPUnNcW-PVrrHt7doe>Fc^4mRk0t!k1DFiklmg z7Wj>;9PBsCx6S&?9@R*Fe%<5gNlIA$({EqKPc_brX)L2VkfxS-{^B575jO8)JDh($ zZ&!&ZP95+$-L02j4}12m|NWYF7WMzOe?RnFG}u7Tcwget8TjCLM7CZ!Gm(mY;=<({ z=`ZHH?wWjD83yw`(9rzrc5_&2Ff-_v-4;ii#mwb`gm-7?iwzv`OJ%R;@4vk}phq+e zdsrX(`2v#M{Cp3-EmTcxPpmsr*mC{li;hL2Gn-x6B=JVV88_`i-KM6Zy zc#TNo*dS*@6GHI*wYjO810Rt}YUrSpbftJJrAkjS>hsEX1olKg1)^U44uua7QU#ft;oRSUz%YI`_#JMh$-a2Px;8|>zDnPw>Rh? z9Nujh!{e}X!-+1DtT8Sih0+ut-=bew|P4lNH@eR)md$@pvArGkzaluws zAjod*t1Nc*`XXGc9$$BS1kbZKoo-ZjZPIo$MA0voV#vF=l=&&QI@9J`#ol@~W31s8 zp?>kqytmL+KNQ1;v#po>@)~+yPL9STzP!CN4i>|+2F$9uW3goZg`kzAw`;M2uJf$w zNe4O0eh<26wh`^yVqY*J;ig_@Xl@R1q>s=#gXc6LYV#vy@}AlQb=|HfbiUK-V!2)O z!U)rY5Y5h^KM)F$v_EgLy+;6*1Z4iPfJ~K)mhf|^HYW9pF0|0BW&1B?<2P_xE8A`t zJ3ceJORNwM-gPCf^%x@EVVv;?(3HeIb)CydyC`q!-FH27*<*UTMW-s-WHsi|!9m9?N&+&3 zCcc4yPKXxWiJzbBA;Cdd* z(nIxAd4OB}fN;u0=&i2CLrc@-us{v8lv;v(lspP2Uxe9|OdykK`#bMi5^{Y}vOWI4&2f z4y#$tKDL~e=OGLaogOZ&0}*OJk4aoeckEG^m<6c^7^=K zckj1ncgqAHXa{ia`fq)H%@3y4XZn+V};m8VDLU*lN2vcKkz0QnvK`{0q&dcpA zHY;83&nrKnNbz0}!wG8w(xJ;At`_vP-y)0fi7)Ob$p`X;q%r|LmuP_l|La(+c=UV)@BF8o zEfT8GzW|J=i>>>ojV>*7SFc=qb_)$K_07w8{e+XzP%lG~wU55{&JFjQ9ZRJZR=_xP zBe{r0?DKd&aTy>F4d@40pVzCf-V{tWNtXOjW%p#0N{$|WlX{`mc5aj@?(Ib(Gu(8G z!}TnjlE!-e(59SG$4pm8&9v~of#>8rwNTsV4CewaDo^N$&M{6nJ`FinviYivUrf0b zOKM+X&&88Jx-hSEVd~0q+Sez}sd!hf>+611sZy?Vq6g{*ma?qp6<%_zbsZN+`DSdbE$^HGL*yI;+Mtree^_LhjXMbIZ^emq(M<>!uiI11l?cC2nq~T_wFx^~~fzH<( zp(y~!FuNs|0bvDM-Qp#?sji3^EHoV1Jr&Z%c>;FFkMF_4rgbj2>)BOWM%aiS%%hzj zkJsnt`wxI2E{XDBeGv}s5@4L0SZrOBM*2s6I9eHQPVAN2=g5M@>y~Dm(LnF=9R?k| zD`M(@q3R$&VR0iq&l>K=T!_6YOetG%QuG@(MOSaI|E(nGdVTH{v?FC!x-0>{Bm@Hk z+4<`84wGY$qEQ@Lvcu;V=>h zDz}K2&DCQo+6>F)L=Q~qme710@>%-QSSm{ITu|1d2v)?@Ap5}mfabgP{M*~^&{K~V z#{IJ2`l<Ss`!a`lvGo@>vMNB8ds94tlk-+%qm zOr7XIU#fVy!e_}XbAF*$nmq)vKUnoT48*I zlrJb$q)XE4&8462>gW74rkYVlWsoEz`n@N~7GLBU<(aWY519(X$Uz{YQQm(!Y@0V2 zm1iecv(@Xqe%wxf_i_LK{CJ7ziu++PU96*pR?^8ki{w<<}aki zj^Md?@q+5HQcw0qlSbsxGK(R^Lp7?}?!Yxh{LC?ZeY+IjX4rfaheI%+dCt4RH_CNx z>_<)*Swo$ustsMyEEaLd9)W|Yemjy*%XfE~T86*M zQ20(kh>*?4Lxc&CDUy;{TBV4`<#66WYoSaE@GjnFY$_I*6r>Q!f$(-|iZj{zQwE{v zZgP2t%s~Jy-R=8|>`Et4L4)3oythqQ{dYu1ivl9f- z&HnfUK1m_aTuC?I6(d?s!X9SG9Hdb!2C0X3S&L@-y>L@;k?qpeYa0OUmMojcGYnQOcb`;A@6_OK0m(yU;m5$3-I%&fAaf8+`T(bbqx|6kNjk4 zO#CFZm7_!}1&d|OuLlLIKNZfu8(XR7_a4>+(V~r)G7c#~Kd*r;N6b-1tNOfQhba%& z8p|tzr6)Kgc5IOi9!sPNXFSpNdcB?0k)+Ke!%X-05gaL-pVv9)2TH;)ij-~&WFb|* zG!SCu;Qh3)0N4_0Mv|RBzkK~Nwh>FEvmIlVK1>Y;9wj>dvcSZ&NYxPaTs6WK*~rMHoK(ai(2D-7YCUn;s- z()nKLLW4p*t(8J@G``uKMg0x^wkAXrKlbGGlthX_n6Ko4FcJoaa$d|i4Y=<2~(B6p4gg|sGF=05V$7Fo(m!U{0 z6AjR8FQ?N|F}dD-2p!ASJM1!p4tq)o%OG(EW31FT2+kGS2g3D6P!chkh8WQh zx0|}9ZSHM7>Q;(o$`Qcoh1#RJb-rD*E0Wqk%Ol$Gdrm=;pWm?E1d$Mh8Ob z>Grp?IL{bTU35xU#cc@%>P$GI>wwgxJs9Oy8SaQVbz1R&_hv&k50g!hV{{ z6>rC6UNdlH-b&O+r-6(StiX{duD1Z1H_Te|X}Ilb^SV8b zr^{;BEWLOFh`_n8+iAOeK^(-S0H7f}PjFd0F`P3YzqcJn?B||o&n26M67Ko9c!!on zFI52>FA1`zkrlg@YBpbOw!wYecH2y03aQy}fRc>5MbJQBy92tN`@x?Ppyf8A1vpy? zDEL1VR20V=3neT^1dwgB?S6nrav}j7eNmE?o_ZDtIK^wI;LAI>+3p%23mQwceVGzy zcGW<9AV>AvgkK2(IFphG^mn2K1p0bB)fyM61+hTw+j?oN^;kj)->>Fxhb=fEedvdq zb^{+Bt-^2$D$wwKziIVm;G+6(B0#c!?u*eqokdTi6~=06HLyX*W1WW>TUgf6Agf?$}MD3KF`0jRfOoq7F%%7y}kAHqq z@Qlf+HFMxrk030^&jyEsz;(lz;ow$riM_ILmOF>Vn;H;th%~We>dm?}2^mlhD>i4( z${9b?(UpR)075&59h13T&uUw<2`Jeogzvctk%_so8 zx8I|!)jz}ZT@Nt3YHY#I6a|m@h^mZI@@t|>C^g1-h{3qYqbD|qXy@^h`=`^XP48pl zrmr5d#H*?F;&}M&A)ubF_t`>Ku=qTl_zcs5!xSsAk z=n#jZQPwBkv7a!UoW10vQljDZ>s=cxTQ^}Wk98oho9x5(mE&Eo-yIzLbM1C51nqzr z;6;5kQ_`--gG%l<{%$x?uGkmisB$KIq8Q1m;@s;t?q03;KxVgT>NcTO?qVK&k(}1} z|ERL`GD;LIZ%@zpk6VnMzyF0liVg*B`nG2w~yK^ zgu+lwFDHYQ?GpffM2vM%ZC#1gZ7n@j>2W-tm7F5^mV5`xdc>D#pB0Dsl?p9Mkt7z1 zG5B0cmXtDnXuP51r0(|7!}JoIz3Y)n_d`6dRun&5G-=9l`M4i|)ZtUp;w`Hpkicj| zlXA?V9J4;+$pP-1VQhNF;eg=GrW(7cCEk{8mU5qhqP7JiWGfzERsge@Ry4|nU`Q^$7 z#Bl~KVSj~pp3BSH7F#U%8Q)@rMM->gb-o^Rj;Yu)Wa=Fjn19L|0(=i34%amSSFPnt zg6-}Ol+U|$Rj^&LoHly2AF%$z+PW)sJbQL$dVGvq-nD9qR1|k?6iI+WV~mU>e#c{q z1{9w{4Ph)r8}YeYP{nP~vz?FpRsMQFjqGoLEjUOKc=pjb0|m8<{}^f`C}7a~k1sNb zN;oYL7`kF~e3OO9wms0c$2hJxTd);atip_PN79EVMl!8>arUA^(^V2#^-O{ujd8&M zhMVPpNj5S#OC^LJ0%%0>?6yNekJO~m34IogR$yP|$6B?V>|a2DLT58xhN&vb=>QzgSLEHVhX6XK3p`MEVy87>VY_-?ubw|H*MIZlZMJ^@)A#kCZf86C8nS*q zc$uzfpYU;}(}G3Q2NmAYA0*J>P|8>ehS2=MmZ+7J!mM}~;8`mfNP&)zGN<7hH_^n<=% z_L&{H2YS>}E!q?S-SL zp6=3PS45j4V@Ye=uPK|Xx(5e2;=Op}qyx!DVm9=+)yYNh)7-bD#0SL`K~-B3pbb*` z893s6yRUaW6I!6rE5@l#45P^=`R6m#io%-f5*DG>0ZGSXzpuS|OxQ^}EndKU)q;!1MvLSiXnj4+*JEg8mgyL*s!+UeIru`Q0QN@2!HX%GRO4W$s+k|My! zOFyF3;*Vz1jO@ygK!?_FT43qX9AY(o5jLAi@Fdl9{r~X!ajFPh4(ET}ZfE>K0Lse< z9u;O}(pI;y!+9(4n(2yyWK6u&CA`Y10S&yMpgCpXyN}2jJ+yP{+#}UqZ*TiB&;26P zwBv^1wYmUI&s5N)c)6aNsmuH|FBELYlWoaM`07DbRQ+T~OJ&r0y^{b2)5Yj+Q5u`@ zu8;Wz0^_I^W5OWYqt(_KNgndyknNjUFO@EY3q9@R`8u8u)z6rZKuL<-Hu-uatD-A27$a=WEf$W z;HWife&=$xDw7SYJc zOhF3~bU^0`zYZ-2$JkHZRfP5^Esk>n63^H9YW=oIC<^Fw9JYhBfiG2Eq` z)`BEQXv3%hgON}8PZpruNMNxJpcz4fsGD(KuE$o|RvUhtJ|z6zwouLHIQY?d*_ZgmvjLWZkmQ>b6M{-8)*K3B8r zkP#O4d|(uzJ$w7`|?Q=YBe=r9}5ygxq9RU_Vo`XaYUL1@~^`pw^kg^TRZ$Mtd=i>O?udq+vNdH0r3 zR=3$h0l-|htKHl7!0wDo#oRE98zGJXC`qWv<-sb>as{SabfqpB-yfQa$v8^#2Id~c zO!6#i6wp@U*2sF#?G_r4hZqDcxazgpcNe%nmwT5_*^EAl5lenC+EN^Yk?xjrawNlk z;BAP5;tEq8b4*F`dAZ+CV~5*D448?Z?G&X<1}}@Jb%D4rkbr?h@5Wfla@9MT3S%VPW+&*Hu^WK z=T87{M+Pydj>cZNDhIH0h$WPtKpsEaxBayRG#GkV%Trx3m1>PrjopS=4M-1RW$%$* zN6})8_BmsG-e%W;G;4EWyVq(eZdOcp7 zKT%kyQM8M#6xy6I^u#@=xg4fbbiP_My_~z*KX3NRa|CU5c6`Z>;svGN2_pXtatR5# z5QGGM5Ljn?lFrC~zzcTXjGzuBQSI3ZIyttGHnM5YcnR@z!+4RURFlgSd#T!|ObKHn zn1-B|i__^0R!Hzen>uV+c_0$ZiDJv^cvAHcMx*F3;f$UQT`mu~{>(Y}?CxSht9tTU z6^gPSJ@EW~;PXD?w`JpmYoGu}K)Ap9DwH9+>Y2@L*2mMwpa1!vANF4khc}duXP$@= zbYnP4H|~0g+7Y9+>1oP?S!W3F@pnp9Hriu>ir+%wR^uyUe%Q!>#p z!x0#I%_mFPvSuP3n0t^GdK-xr^hw210yDenaQ<9w)~Cnw2PCvuzQ3uBjm5lsh$O~| zCBnM-q=;j@v}UkDNyAIJoo&ZLihFd@XOuXY%wyC=LmvX>YyQ!_=J)+!Uj$Iqj13QL zCp3&PB5SH1a{{&hMj|Y`;IbkLVF|6@@MoEHP;8eS9XJ06^k2sFYBPj>|5y=)hrpwT ztqZS3PknP7*N%BQRj5*yWb!@ZmXg|xxOO5r@Nt#Xy}dn{Q-_Qn(P~?~_hB$SLGp6z zaZotMmvADO(3()hFw&tia^%Z>I~N@^lWpAeBZ@JoWIzDMzpw{-9Ue#MOm$KW7*|%A)knr;{^BLjr zh=DRo+@+7RepT?#yE<(bMoYzp&lCAxI*X*0jwW{{JUvmydSV#zvkoc3hL*fD%8JlzH#Q(N$Gj`ZuqmE|bLcxx>+ z3*jSSv@KWINdDF%*bhs&>mdI-j~CAgOBUq^)mLaMyU!yrH>#YXqmU87WiQ1+vg*)K zX|JIjf<}Y#Isz_>!9V*&4a+jvgtDCBx0e$SswK6Kqcd(Nk^|END;7su%@uP9;~yA< zmbL@-ARBFIYQ@#}Ndoz#58EHz4cNOxSG*EBS04~l1#A3Vo3D+SNPsi>n2)L!Eu**} z8K@ScvVg?*QNdi-bsP;8?L4^BMoPa>3D* z2DtN+f-r)ZUeVJ^MT~SIh3;fzAGh1*AJ2dF|NPJ8*5CfEUylFyx_(twt{*q}NPjOQiB+nK}l31{BucWKxovAjzQu=Sq@)ALkms)_zOgeEC2NXIK z`zHKVPFH5|U2zfASt~r?R3bs4T?fd+mX`}7WRO##*4^ec0Cb0szd~#NXjz|(i>423 zsF!mJt%Bj2IHh8A&yp$&FnT$O@8fZQ>!F-ops)ufleLB)oIp`)v9p3$6UQgHqSQdO zi&x1R-2sfF)oGK5GmVlqN|J|^XVPN)E9`w|2*I23O9=T*Ljff171ka|O(qVJDEj^M z4IuPSQ``UZ|NeicQ$y;=7#Z>q$lL4lHt&_xG1qbAS<>s$F7_Y-_?RehKW;q`%@L9Z zO+I-Dt{xZ=$gITm9DAnT-Nz<8n&!*4x4TjJt3+gx-q?M^a^)r0obKe#Lz^-aA- znFaSOP`&wN8-1;gfjQtG6|IP5JJ8rpgF2F=ZjR0 z6+ZR3?m1#3qVWZPKAQ;*X|{Z&>dLSaBn((Js{h`zrz}xQDdC@Yh4m4KI;pggIWf;t z#>t^&>|Jgpe#AOtECaD{bdz7%x0)j6O=D9bil^oQ=q!gG(aVVY~;&!_x^EjPgKT-`9(cHWU4|1Cs8~S>`-kSdZ{Aj@= zzcve+i8F^ifN;9rcxS*$3-XvzUkbh4%7?HiF0z1mzB?HQ(NcugII=DQ_4@pJmo{vA zuEN*j+G?w&HW*tIbwg+IYrxQx%QE3fK8&dH((}S>&}hD+X-m=$rAv+Bxx z4FalkK7Ez7^h7z8{Jk5Fn{IeL?N`sMd~F=)k(_hkW1h~{fDaTl-rp}{@A~04;(-M0 z0sc_C?)eqWA3Z!T%On{c=J;>jMSFXGoL~5!-W#Z6&OJO&0i(~5nac#F0xBZ?EHL&7 z_IPxn5p8Q@n|=YKQH-chJs@M=?kroOyTa#O9zdmtA~HikU51s+3Vs%-1iWTs*;{s} zXFj2U%PKh3a*HVmR?-r1=n4+lbSW|c<><+v8#$Dboo*1G&iLG~4L?IgPBoU1j2+CL z&u3@WWAEa438W+&Fy?B``Gx7{tFbV+7bd$2Dx8IdlX_{9-QL5@fLaPz7f7IGD-XN!; z2hwxOc!nOSLClC3N*NS6H^l1ew!BrKH4C423fOMmdxHCE^<1@@RL0OmUAb3fO=jD< zzJpouVI)Gqx}AXxw_ux8bzo0_lmE3h%Aso2oO1V-J!RLyvwPN+O{;m%w^JW8!=l4( z8sVIyrPROLW6~|sTau(ci$eVw#`tnj;^2<&-B4!yuiMzR{&+*qu=N8BqR<@9()CaW zSUSunPckqQ5XsP>Wg&1oexnG&z$ci__*oW|0}8L(i?(|jsj3T}^DStAd8#WGsR~N* zM2lt=>u|lgON+XI&y}%mKdA;i*=-3GKakrLnfxZ6-!C^M^ZEQjfZqii^j_$dap%mL9Ms|9m} z)|@)dm{QXRtbk&(vfY|QP|F#RfNpgNlJ;b76YhPe##ljRoU>GoY@y-#xBGwb|M<_( z`vengE9%m!2tPFg zQh|kp?I#9)J!ifW{%E8XT#Tkdx6zYCWIpdsu?L{0a#-v3eC~M?Iovdv1|ff_T? zC=H7_Rz_PW0=TLUmNxYbIhI+kUr!Re+yprkK68#N(5C`m&aLVga>~8@#Z;G*j&|j| zOJ&riXcL3KILv#GGm{*2gqONHwrbXp`Az{)$iux(SfYWh*GqS^&jk&dGg_{S#7Z#0 zsOf~Rx2#*m?whFdcb|EjQ5ilqi9fJFKE;&7J!2QVshpt z^YoQZo1cZ&pGAM{Q{seMvK6RL=QAg8y(dG&A9g*?VtLpc@o88r;Y4PxLLZAqzz*r9 zc|*^LtdiJkGt)2*{d}S6W_t&SE~vq(*!uPO+TP^}UopArVL>hVrKB>RsAkfGy(ZcP zn{0mc9>u<}ff3sTn~mqJ)3VMCCna?{%xlDv-J}scNm|Fm-ZT~Nj9c`XO=Sq;c%ENh z@;UW6CuWb$nK9Ct=<43kdJDh7FlH&E?2Y zMKG`tIS`J)$$FOz;N^Ty8nZSm!eM-$K*q5!`4!lug!y@oX#jqXvraMB+Q2C;pLl7@J_H2P{v2Ka<6z ztnV!1`TBZLZ0zZDu*V>H5}I@hmvy~dByAwUX*64uPV$G5lqnTkmsM3L%E|z95*ZnW z3{iF|yuJ6RXXi3Ni0LwVNoS7_7* zCFW{c3zYGy0NYatN|@|K0yS651&(>T&T;#7pEn7}ubjfGcMH}P({#6}c<~itnDZyJ zEhcN6Vn|3G+d>T8VU4o#8$A58hp8mZFwr$$o=zm?>GYw!bE||$bo_=6>qK2T1;8RD zI}K-h88@5lHo}uZWuTA0-T(McfB(z(zrOzCuZMqpy|r#P*kgqDq9)3dAzG1MTl0(Uu(S@DH0 zvP2!>W)^gZft+*Cw9wmNPfZfEvWwP*9gL(|@aP%VN{F-XWKs~wd!rn)0U$kzR0Oiu zPd%r{MU1Ruc*)POmvR8=!ajtg?Eae9K+Fu7>{_HWUJf^D`v3SZ{>1=p-Gb&mbA?Ny z`PE{No4?PhSSiMVg;csdnxNuD1dD8=kFU3Eo~Vtqz~i zQ?MDFahYMKPirR% zKrPjc)Io_;e7L0c)6}g+=XXQP0!fSqK|oi6)SY=0I_f1pljT$C;zxXwls$1bvy!-$ zJ!R?`sc6?81|||iFf4adff5+><`UK1Xd*Uzi6BR1z zs5)Q<+{uSH5}HK}RSLX9hp5O0-#@o%0Qz#mvGumPX)eJjq`}g3nuwY#q#YhFNFzN# z&2lC~prq_--E`&(?hjXaZ_sN&kGP;kft(5Ao|C z%y#9JS#{krN03P;G)U;;WNOW+rUeyIDl0G`^vC6jVq=lgtWq6PW-5qf`cQ>fXrR=Q z=kjkOB4Dp^4yr6O**6JTu11MDP>4!{P26_L>z?ZXtmV0NugBxPhwe4qXe@FVab*I1 z1KW5*1UNd_F7*kVxKg6beo!=~%}s#cA}C zltxv0I56H(13OQ)6HTkJeSVHrOB5LEnPKK(5OcY!*_uz~E4U7dq4RozPoK;`2es_c z6+KSW=~Ip}FYEPH2`!=PmjNqI7G7o-Z-nwr)XYo&|Q!9P|T=z0$h}0 zd;(m?A}T+TtqoP`pV*v*H|T19z^?7IxjlKtcX0grcGJ z6bXsn*-@)c#%s+*By2V>Tuvz-9EdjQ4DTg_O&A>T+(N1<=Sn^io8@R$zT8w%)t7$s zNeESc0!v#CzbxBt~Dnwf#_z7^8QFC zYNRIhrHGBWB7%RRqIl7||4@Muy>@iMoECN(E>uuuCw61*l^YVTx@4n=Qqn>nRef~B z3{!2Wo^3s|V257w1pCc}eSO|}iNbu7L3R%f!2^R)Bvc!imbj@b@2f?V&u6}{7}SLt zu}Xp6(s@)?4CL=Qg zP4`WHaSzayzbZ|5a1b7|u6RcVhfpMNd`pF?2oV_8E0AP_yCaactQ(M#P~mb^Z4E+kf(}PrH}b zfBfb2&%WRH0bNprm;^^AgX*tZPQufWk@q2@ThU3I^ZXlTf@5c{|Z|qMLu<;-Vo#}iYaXA-R6D$c3GEHF` zQ?oOIA(lTOm@KWezNu`Y$Wl;_?&CBznqHlaeo`Cx;%aGD7r6_von=_Y?S|33!17a% zq%*M!$Lq)K{`&H&ySE=dWVD*YRu5xY=Rn1$8A8cX>4scBi|ha?V_KBtbM!VK_3ML?Hvx5_j5&@f9=6BR(d3@EtNz(7*ZUyeBxNvs9VD=AK~f zb*B@OpUDi_KtT~L)wLsY(i!0k1znA?pOnN)ALmk#(Z}TS1((YOYco(M`L@02*LVHT zc0WV=&ySxD;SS^Z?zupbFelX@BAw8kF=zd^^OCSrmVSKLzBc7C^C3nJmMi-(W;>fp z&yl3hPU(L^eQ!h(W=o@Az{>8Zy zPlr%73C66F&sRkMscFungaNE9Czi9S*2W<&?AWuMrKJu7bT5rh^)q61_5zy`Ln)ft zfl0GWl!LUym~%lV;xwk9!B{2AIFu>t$NH9jjAFkg_V|phyAc8d38$m!1-=@2Eg^2= zst_`x7cmB5;j}0-s{=>iryj%>FS?D>`7Bn}-f?06G0?QCneVnY8|{ajxI|=50@ZW( zVNZ!I9AukAVH&YPPzyV@eJBo49YUI)hITZOb(O4-F4+wKThN_Y9%xEdH7>px#h6VB z6hS|(m&@&PWgt4#8$lV|(P(zU>|8b|w9qmo88j}t%QgN~wEa%D0Rzi*327dr(i8cN zx)#=cV%`U7IvI+ZAor~+4Vp#O^=InCp#4N=zmEp?EHhHe9#E>o`bf}R>g;jK5yij* zoEm7&T6m7-b&^ih+-@B%1SGv?0e50&9(%%o?g)^VgGQRP1u)o1ZMsbcolZbHjQDwH z^QfW^X&7P}tWJkhHz@D+_v6bfU>qBvNbdVPJRADP5RC$S^XAv(jzqCyNg>Z2b& zX{u((?ZJWX1cZqDjUT(W>`e+$<|k)rsVa>uG_zt7S?Fn~kB{EBe7W#B>R8d8RNCE~ zk?+=gAC(NcgZN(GaZxrTnyLIX_T`tZvQ0!L_dv)@USHXG-%~WH{+Wo=sP>Bi7**=phrrpX3j&|M7wqD{xpW6#owQS3)W!9Y`H zlJ`y>eyX8WKIg;sEEauqBYky4OQh~y>-xA8`sXtQ4nr}FRvgVEc4)I_G*j7DXC&?Ln`5iRZQG_D{2e4y&rZ zHpkN|{&@A%sioGFzYq%nvu>Ru1Dhqe_X|sH@zm!>&ttq_e()M!%FXXp2O1n=nZAn}&t?&p#&ml;cc&%ftA(v-|NA*~M5A$J0PNEaOf4|F-=bn;MW`Kv@Il@QX z7Dt1vR5h7FLllb(FrX=QcM`kAW_)#_x;0lan)~x+Xs6T8xT++ReP&>Iw6F1W2$Xj2 zPZC+D+EbF3A9>ze0ajiKPDUi>#gS4op?}fc?Rld=e~<=(8L^HGD;!bc@pRtx+9u*{^?-hyfPNGr zdxY~*LAonjfWiI_bo_(T&OyoN(u;ypIrBD zpewAG#4iK&$pOw1Sp5rx%0U}-5L65HO_xk;V_98--EXfzGd_wiRcUH8tsO7peue<)96JWh9k1)|Z2+ed>a61(AL+E`Rvl7JgcsBTw)Vawq_V21 zBFs3S$>m1=%&uO~F9kwDr~BuBIt|iV%niS24u0gF!ty^3kqLkM_Ws6J?XJy0&d`f| z2mvkhrLo>3Yw|7|J`Sdt{NdEVb46tlFG1Ooz>Meu(Rp5jP=w6qOkT43;x6=tLGm|* zj~=k8hv`I!*fNS`Lo+)K2+2qa?$g)+IO>GfxJCeft9_s;K)0|~x4wPs#Ds6n7W%cU z6ML4TQ9|w2kJiw! z0_($Ro78dO&0{<^?Bzt35J!5<@>-^m8giV)aFyO#8NZ1((93BiT17~G4fvfMUVVMYnx-U z?AKwAFJ35MjJEl+#m1I|H82j6#&Sl@bI)Z6n`_NsvdbJF91z}^B7!khd@8dKj{4DZ z(YN0GoOGuh?8ql%^l!I614MEZKi2D0mbq@psiuux0m>!OfP9g}C%aA`{?DZrFlI7o zmQwkQ%L;Z4N)P9k9_p5n1U>OSzYGOAxSo2~e?HBtmDLt%D%`c`xwTQduMLD`232-t z6B$4Q(HIxjvJt&tfni^+7lPsgGsT=)j#!uE$^EjIMeJC6dwYA1De_i;rJ%$s!gBC> z3xT?ryJC#rP^I;WZ9W*JE@bP308y|=6*qK`Dr)d!9-bY|1rv=Y8Dub0VMvtKIeD=1 zNwEz#X2zT*5a-{6*i1~2qLX#zXVHlFMaQ5rtEb>2xB);!xO!L?;>I9h^~ICv8P0C$)q?w)gt_ZE!BjETaO{#2yUnAf6ty zXNZ366cPcMbP`CrCJxTT%&ly*aX}3kD2oDsE(7b0@ko7nePPKue!sl?SyWSUH@TAg z&OmnjyWPo<5~0%1oQSZPZ5wk-u*k^schR}j3c=2%9Gu!XozL2pt6?nv@dak+goWez z#8qV$6F+rHAt>?Dl1!$bpV?SoS+bVNBBt|ZE^KM0$YGKi$c&Z=MJDt*Z)TB~Xxtes zkn7Q83mqhtOh$l_kD0D6Y!EgDd=va*Q`3AB0vjmt$vYagPuAx{Yzn5KFpZC?3NRz5 zTx}x4_+rleEF&bw3Q>c`J5R#UD5;zN3%Yz=k(-O}$||FqizMA1*P04Gmcl>ApW5&_`plKWKV~<_! z)xFh1J@(YMR=O%`NnQ@8pBWnvaz8^BX2)@Dn(*{T_p>a7a71b{(FL6b3AXNA<|oLr zcR^C;>7{XeE9|qeo=(x|d9A|%(G@h~x*;aRB+yt)uqbWy!TzxBk(V|?}SH{@{)yZu>& zARAWCQ56G5AbC0x*&py?7Y~f5aaJeVL%*LoD0@oP-6gRnvYydN!3_Iz*TY8RRxf&h zI$p^5MVT&c=c(*jgx1w&%!Y%8UGlKPN{_)DPr>2$ppIq)7O$df)=ECv;w&{O(sZ@0 z<%lE^+uPkk=FTxo;J>lX8cq>_QcvO>xBT%LL^nn8mElK_@y z3@k>0vV|u@02076XusKxz(?th8UmZbNnAi9R8j}G80IUY7P0*7a170M>DNe)fipyL zle8QPZ6i`g7JiHcqTPh%pD~W}^4gQB$8@#$F=E%DpLQ-EJGl5?{V5Mjd>4%YNJdp$oHT!6N8b?C>&)e-nw8vvF?0Nlmo=1vrPN&1m z%j-dccRC+mUQXxNf;^{i#%7KSz~{z5VRv`E8@|c?79Sm76)83o7ueP^z4gZhS@wx>IJB70@jlF#-TLu6{{UxlQ?zf(guajc< zX8YdFm!#i)9P(b^CjTL-`AA&yXC$~g9hygRSZ2VSiH%~Xf2c+dX@u|sVN($PF}m;C zAhjHI(^`r`Vy}=m&#T&qfwm~YKn{Xz9cEdHkuy#UMX7qDoVcvUUEuq3x^%9XFG*5X z?@yk8|N2sS82q5!s&{fVhoqlapw{M*u;3fnT!jdd!^rgLfru*-@&zqNjvgO@fmm`L zGtrwr$Ln5FxGy|+2BB@A_gm}LX7ZIQn=?p$FXW?z!t};L*pKu~zai0M960_so~rUE zeJ6k5{&?P&*BGzf?_alBKh<*~)*nsLmlfT++B3M#2r8B2aOeBuCLxD2Jd%miB7SDm z7pbDlzTKf4r@{{_OgKeKhnxP5$DF1dj2K%YmLIGZG6dynja1tL`Kjr{q>0i*zF6PL zl<34Ttj|>`WHy_!_%HHS_&JYZ7*WDUfKK4mzIxVPX`d4&d8$WaAr;i9d*X3f*J9!0 z2nEzAN^X;g4=SoF^w1nXvIBofd_rh_?el9zBHJc7*1C_*QE!Dbw}y>xIOd^xE>WSLzU}8JHkq90$gUTM~8-;rXk+n z-))l>k{rb&!D?&daa|2dxeh$n;K7tJv_0+CFC0_3*7|a3?Nk16YGzxJJjzYWxinJvQ;2py zB@D=XbdJG9kw-4Z)0Q(PwX~=77bF*TOVA?(x5nvO>az%$kV|=3-?)gNufzP?thm5} zu6+Dnqy&*kvIwqW_#R}p@-bzg-{VrTtjd< z=s!vCv=;qZ{HT-F^8AoO0xpJFulW_*}Q+msWFW~GS-V6fNLpfNEs1lXz?>iqGN7+d$tB=7S7pbEI?;0WFVCgQY3M&=cdHTOejs*mP%dcWuT}1 z>g(|?s2om*$5+hapQ9lMCTZwJKnNhBaXvpqhXy-s+uzY*>pYEWrmg@)2xF*5WwFQ* zbY9O#LHXlxlR~jXC&IFGeU?R2PP9{wWL!N{-{o>i(NiASLt=cdqvS9)sE&DF|C`Hi z|LNZ(Qvdkx{Ilb~_3ggB@8->`o zt^fo}&(@L7=t4{s`JWL%0ewR(cux=5xf#VzG=ucOsUYl^5lhzdQKOle6B*qZ?#bo1 zr%REPR(uIftL1`cukWzUZGNp$a+ygqm{H*2kQwz_^b8^kUff`-0kM30+$JbDP{glQ zXxDmcx}l8AV2CSGL^Ll{4Z%^LfvEg%b9jH}j|&9H(~EKlQ;4rb;@sv!2Tt^$TFpNw zEvK9#jM-S^+II#Z>21pKfja_`+Ku6Jl^@GoW95QxJOQ!(($G8_g^$pO({Lg|7YI_u z+#r8@%%jL`JHb%RIi<6&LLe%mEc&ySl73XSfTxwmQ5iE;!_8yO5PizL=XKdjf>^*P zt1e4+A}m5l;Gc;!dWAAl_@+`EUxVN!yFz3zoKMRBkiV4_YK*jIV7rnIFM2CkBTH2I zMyS1giguM#&;dON03IlzOCH~MOjTRA)5qw}F}kWZ>qp}}qlUK{#IqvR?8%uk36x1s zxQ>fFpe=>nV7>RGEJ%)yiYo4=i>qjFf%p&`Dt>7$p# zK^l6{uR+jBr0p9jhCzphyBts(1y~Rl|LJ8MjfM`5#E#wDS<0*b%u?qf9LI+bD$|Mf zXrSUH?>-(*B2g1$RXx=~VS@>Vsbop!h+H;C)g@}Wip?grRJr)vVza8gr_G*~^13$c z&+Kcy4S_u0aEQV4xvS4a_!L0r5feHGvoJ}=Ty<{VnaajkN>JLQA1`Y%J<)|Ts-wk&MUU4)?c2WwOsfQBu0lBd{ zD|$3329twR7J)cwjK(bvhD7+kKV=*CmHMGWx^U=KK+YCoHqC8Ko)>vdH8?l!ddF(`Mj;l{*W^N`j^2|2NvyD4&Zk`hDRFXGOiS@x@B;e&1z^a<6SqV_c zY@tZqYRtVEB=M5S*qDwf)t_Sk4bc|P=Xe_#SaB7Udf9%(%{)e85 zAA5+@NW)+gXPW)wWuX{7ORe4Yc0u9u{-Mliy0sL!3QK5HZl~76{y0lF^|qb+5yBG* z=o3|AeOVYA^dSfo8lg})_%sUlWpsaj<7KHF{NWC^4Ys%rqg*f7A3xsouiId}>P2kx z#J_PwO+W@4URs+)utcUYdBl_uFAW`6aJgOaRUXJ{%I~|nuBAhfJ4@A(rxPC=2175A zV9NLMXM}Xm_7AeFa&e;sxoxW|v|xM93AV1O*_I2F$ss7JBf|&OGK4X0L9n66Gem?s zyfCnyd#uY_bc=>XP<|q*U^M;2)ChNgO)M?U3nJrxjEHPH)utq8X6tG{zQqUnje9zu z{|>xGHOmKiBhOM{eaaSa={Cq>arV>rb4 z565BxK*7Z#`$@^uOgk3f@QcW&Gxeq@j6!p z`}eEF%%E)ZcE9z)tBDpRUrs6oYoZ~&!$zDXmym%$OQoXG@JtiuXiQS<@){Guh_p{ zX5OpfhU3X6fln4PZ*ea&DRbv_V`O8eEEXJikP&ffA8o@V9bhkI^y1~MJPyVPna3a| zT?SFUHE&Zr6~6F6E$_s%dl%-!bZ^n^VLHa$)qWVl?+a#O*x&>XyT zcDl<1a^Qx{z=qf=Z!&&7Z|EWO6gmH6Gft^`m@Tu=-I7!hP4TlpP92~Gc6QmL$}}{c z^yGSYw@a$lwWHqR`RKhKYS)jF7cS@V?2=b6c4di`1Yj5=nd-W^x$F#D7F+vyU#12* zr&;_HkF(lO<7JI?QTETro6gUsNv0;QhFof5{>PCDF5PF@$$ztwL!3(JR^8p-({U!| z1-TC{qcthdUjB85!_=1_`56X$*D|7*gCZgL`HV=1<|(}gqCaIDYpo9N{zwwji}p9} zmu`CLvaBV^<6II-($c6D_Zd@D{7IuiXi2a!EB{atF?sb=QQ?utXKAStqGnPVB@(TW zwmBbre-PDmVl!Xy;W&aF{*!W+0xYD#=(HKl)#c1|7Ww-4?Nj5bf)XPiCldP%X@TovfA$20CQj0SWrURc+7DwFIw4kXA&L%=}3^up<dm zEn*;JUH$3)=AD#O*0P@}b2}YfZg)1!h>Q4RhtxP$R5)dL$){^>=iA#K*s&9&PsbM$ zvpx1(3|_2?`WXDuzdcf&%wU^v{Y{jLLbiyJK?5Hh_z|q_JSV#KlXyfW8y>opc2WfW zamcKxB%3MGJ0STGiH^ry%8$}@$qFHpu!OVptjpW2J4X>XXe{4tu{x9+ zOim!}`&?7b`EbHgN|znMSXS=%;w0nG#`x*3hI3pCVZ%=O1i<^Ytp?Wg?X8DBurH#9 z1n9p?3Muq>VAAv=@eUx7kfR_ke|X;adVA{1Tjoc;dVJ*K@WvfhX&Z>Hxse=s*QkC9 zGcPoq@{;})vIAjC>ak!IO_kzG))u=CuG#g(fvk}ec4WNaM!=SKZ(zDfySbu*>P)&m zaoWd*{gNetd!i3$@ed#86R`6cLRG6!nBIP~`+T<0kfXc4UyM68mA;LQdWd_P|X-_Q$&nK`mJXLsXpfI!;i;c@O`ZUR;W!#wqk1!dR13ocu@VSQB*fSOwh z4$?ld-*nsItN}w4MU2!dnMiF>-%o~fK1Ar)=S@PgDh)7r=%j}C1wo!b~+L2l*9Mp?4w#T-ku7zQ8Pr8f3gM3Wlo@#S=;e4zDR`aQ1? z^EW!7s|p&LfS-lz<)eRSu_u}PAWAmRcs@VovP!Go83R1MocVk(o;;%Z4abC(b_WYQ zJY5_JMb&m-2BZX*nUXB=etGMG5Tt#tL@|h@W&Lt4tEWUg)L-vT+P0eM&TDOy7@IHF zxObF3KkS@nliQPx6#FoId^x^4OYCs^j`v#aMs@mJANMEb@2~rApYXN=o}81MK~M-^Q?^O`ix{;Bwy~jZ9N*;|77wvRaf%ae^8OM$ z1ibWCmONY*<%0&SOy*dGLpO|(JZsd{uj({+Iu-z9%zW(`DPGk0i1l1*{f>)6QT zA5PbfL$N2N4y1hkj<(m=SDLnw4^mO+dXhEDTLvf!aZKg9UH7!1fyTOWc9>_cMnw zllMl{%!UT_iAP>?7-4lWn2U`;XK`dBK!F-3oW@mtn_3Ki#egA|^z=jQ z3jwwnwUA~5!NgOaQ`sL+r|;jtBc@z!B##mlEb<@UTehcX7+bWXlVQ=n^-zZc0K2sG z-Jsi@#5x`|nltQlh&-2;IFHZi!C`~?7)5~35y)&l?o)CgR+f8hoq-5Q$=xMc_3eN( zg@{HWWdXx)>*oDwrgy_RBra9C{+r@Bsh}FD*r=%e>)b=K+ zt)2nerExd!>IZH{9_{q+Qi?+dsm%J}KOKFk&(d>|Z($h$3uOJH9}u!3eP2Ke1AhMa z@uMX$olI=d0Gy&`8vFKHxb|`G zY3??Y!Vr!Cp{1CyfJ_5^EZ^jZ0+{FoeXYmU(1ZR5oP9cR{EX%Z90`;$4?0l{OJ{nfM#Vw2)%(mkFHDhIqR?TtS#YsE#t|3T z7H;aJNg|$k_53LR6GN(LRx?r8)Vt5jr;L;QNz7b)aM%V4aT?CctA6?x=b*uGb3MIl zL;!brdt)Q^e@P>vJ=0*D8e^Y`81f>hWe8nqy!19B?!LXgf{HV>97i-xzKgm}>VYkn z%ezqBCEojGwYH%S5lXZnUVYS#l(6YkG*CVdvwR|PY{a%!6fwj!u{tM;dgBI#ysn&R z)*0HvWI7*YB1q3tRlkYFM{kCUky)#->@l=`iYVfIJkUnns?*99FOmej4Mr*=!5HNWS_VRcurSaf0Y@E@4Z~2#nbmPl*PA&UI8Xop|MW>jK~z#i zGpFEB8v<3rvK02vlq46`UDv@zj}F@-*d@Z+67Lda@{oF1XjneB(YAaCmBCKfUNX}8 z$*DpksWPQ7aP2NTtl%hYKC<3fUUWJc6OdOaHuH+L>rTI|@;v+eOrLlD$mf>QfXb5%vSR zSai0pJxbv7kKcdCq2ByN`l#|lA_=&xO0j8?-O*Go<}~W>C}8$64BE z*nM>=&BwX)rSSkGSxujI(>3J=KPPIxSi!W>>x~K9w-+Ooal}Fgk&vhX$@)3u^=am8*lwiBc$WT= zvt0dZN^G-MJn%290ND})!CF)HLQsN~ax62`!{k8nnCU9{5?>rnq7x03C=oEj_0a}X z%edN<$4$=7R~3qQgf2mR(zHTEo+9Rguf9>o{W)Lflrj@h$%o*U6n2r7(a9V+w#s?7 zKg*$+bSVZbc-e1zI<@#P*|g$M?Jw_W2#gqjgVB{v&^DKOvKB-I;jzpm^y;(5vH;cU z2!z%+<*XIgD#I29wXoG#Z!LXQH{N9XlKgG#R}08J16WW&IQ-MpIGE&S2wdn& z??&{EFyD?smGG~tLVHi30t1ndOD?520|4O8pR86_Nnms#d@?iJ%Qt`-O>Z%^ZtaSl zJ_a&03EOZ8wbeSfo?G;utL?%RY*gvb+`#9|*g+xv=!gEBR(XEx4$#t)eKJU>k2^MT zlWe-jpv6W0(7s5%+sNknENtAmx`jt+#K+(;Y?z3*ootu?pj6|^Xw%-s8)Iyl>bH@agD=4Uej0P0wjM2 zgZ+GjpLP#DAtAv|d|k2}Rz|SF9uDPyLgVMx%lSyJXd*>w<^}mUwvc<6YlV8&zfOE) z`G&AF7FNrHJDLgB0sT@dUKWnd-(J7~7Rmj`{dpzo!@hd}fUsv#r`hEc-IAN_h_721 z>zdej9ihAJ_A7A2L>3<>2JpchylY zVX`rL$!e++e?}MIEN)<5a3VBg*=|n+_RuGiZ-bLsuOOcSUQR?$QR$8jGLxTnD$U}T z(~F%s?xq($W(HtPep2eC@nUN=qq~o4V8P#5(i2n}$RXnr!s)nsDrGB7K^&%L>@(#`{(V>|+)B?L_n*FN8QsQm6k>Stw?E#S>sqZjn%(&bR$SPT%h-a{&q)wN zEE(!Twoj9+sUs=bt&Kx=AVL2CqH}1jl9An>&-W*g_ zQipl|FaM2vr#DUIv?;6Tl2}kpnbbFgG%nisnacnD?%ukffdX~!_FS0s3}xS6U&-Y- zSGP!~PUZ<+@TC`LEUwRI8EClcqP*;v0~xWc-rwKsHezAIuDR?}w#+52Hh;(kMlQ$2 zO{qqD_?1sZMMQ!Qv{V*kr-7E@O8q|LqI#(Tj8P;(q=d|IKgRE_v2^33j{CV) ze5F+N#VT84E~R+hPov7#sHBkQ^0VqZt4~VQb>N(}HvwaOP&`vm!9N7>7vJAODoCRh2 zlKZSa9{bJL^`hc>o=&|Z<alOiLdV_v6KL~x`VdU&A(B^dE^0G8O11iJz~1}oI5nq$Uws2l>}-3PmmbVPmKoy z{>_h}Au;CUdffi(XRf6;qdqK>SpV^Kj@nN$&O)D-jv7T)sW*x?C}kOIF*L3+Qz^RX zV5wbW8Ge>FLt6ysEy+uOFYjconV+*$%gMZZDq7{GLw=xv>&8yl_z>|A^n-nE7OgG5 zB{w#+-f1D8Vu}}nGzYXCDdkdZ=+n1;CVNSwP~XVoy?rmI_~>lvxF*Y3oy2Cqne`b$ z&R2j909QZ0eTZ)1nP~VgGJ@*Eyab037vtaWVV#Y2tI(1cXCj|-5q;hTqjAbv zqA2OkpkrN2HB-7wYIc*$$aK&|W+NjT_5+ia4WY9M;n%6^m?Q@(2XlyT>$(G@8JQY= z)>Tle72#I(SNc+5!)Zf;+Srg7|+N?NHnfUqC zEThh=3DscZceHgnkF5WD7wadXy*)0Vk#Pv3HN9?nS^wXSYW?sja`^S6Ite;LuIDHRpxM^2c9|8cq8 zKYP>gxAVC<{~te$i+Z$;mLrlR!3jx7^%E@)>3KBOGEPG`6VX!wLbyE8w>Cf%4q0N! zYc5gILum2|UX|}4nS5sJj}~DR>Gee#3Yc+nt!={~HggT#=vgKtvdA{Z|0fonBdIeA zr2Dz{2Jgkd%$7~Kwb0|OLQ9GawLW@n}l0yRg>ZetCUlQfN0wzfUF&&3j>Ph3 z=YxGtX!Uk|5Y(9vlb;Hq$!`=oZ={`U{2cNj_G`jQ!G-f)dZI82Ma*>1Wwl@(RbzI= zUzlRE@0Y8>=6E^_DDn-mB_31?am(a@Zvf+kXy52`yTEVZ1u=#}cO!QDdj+Q+fC!~_ z4(}P)tvFoLtV1m2Zi`|vO52K6@8-sc2aFk9q^&-J~W>uU~jc0>od6dshQR=gFNSH?Sv4`C7ggg?xF`N!1 zIbS$J+5Mj`@BhUgFZ=WPuiwA@^IzXr*OphN=`XKNSH`W(rkx4x2LreQLb2|E#9vWT!e>%e?N&4yV0!|HB zYn_)-OL(036Z|7uge~!XDbN0m2heR}%?#8c5Y2oqpV=VCu~1*13OMU3Cy&(TR9%e4=qo4(ZDtG@x_o z-04efUiuk^dikpj?R`rc@zEjSI)|b+tgyFy1kpRXpS6R$yt5NiccOdjJ`x~=fv#sorJ0$a0XpRH#5k}(&>3F>q@&I&Iwhj^XX zf3jg5M$JM087jt%o3~zyZ=xH+o2&eO4!F4T?$O^t;}nqo99rhT=adh_8C8l&&%DQ` z*JY}ah)dfYcVrtF1`%q#Nq*kMqlmiU42r(M6b_ z?U5%9B_)J&626UPT!RwPGI0(FY=t~qf=(gRv!SEc;fYK@KnT(Ir%|Ik6_?^P(1Iw# zVSfP7to$#(eAoH=Tt*7yQh z$6%_k+O>Noc0S#1cYAuI;s`6+CYIz|wG|KegG28pF_@o**2(F3vN3Y?S<0BC>fB0#Xm%q zamn9VK19ePBLEx*1NCEgP0)Zy8aYgE2VIC`}WbB?*)*9#`L~) zWdo7EyU?@mv3TrSv&XI3ho^$@u$QrEfKJrva}gI(OGWY|8Q{{P&(fREj3Sg(Z7rNa z78-(V!01AgjVfU}8ONxfK1iAn$U$O6SIIG%#E73_`9%5NP%zTi$436vM< zz7;I?*t8q)Pp58vT_(K95M9n9n+3-@ajp$RmcXQmB7Sonr45_zhHpJ5Gif%O+97F~ z+{8j81>`7+tdfd=MQ*A}PI6HMl5E8O!1$_8zTbJS?I1=zyPl!KLZ*bLbQHN*`1_w+KSYyUF(($(_O z)Jbe$*PAL-B4<*PzuMMMOZT?hC1Em%Z4xnb<~jxHPe<`{Bc0h0U9`Ed(KO?K zPF)r!Q{-o;dV@%XtN;xW@^9ZW1$mNpJ%ckb@B^m_?muyB??M$ff-7o%bgxJ``yk{F8?|8|&Z-@Gk+sS0a37r6soWP-O$DDpO z<^y!hZDHG>KEzJ=e;-j7*UQzIbAv&i-HCo?{C3W*Up%CpgO=um!*HJzYJdVNFFy-0 zapZvg)L^WIczbO4{GC$(7@IAH{qf_6ZX98)cn`)u`%ic50P+}!(-&!=4>}g#B8Pr` z>0NlhY3CwjQsEJD1py!gW?)rsh9KKK#_0D0vQZ+F>H5>m*AgItCx7@UKp;1+mYq5D z`vM7P`HAEhGUVc{YC6^6*tLEah9ed>;EUqMOV}O4r*rn+5-6!He$}ghWQdk*6}*2Q z8us;+XB}?_QosNHJNk`?9p7T)0*}#DdYopDZj^aGZ)#dZjDQZk+$u-eQ_NZ=5A$7F zilUi}pISHDk12Q|Z2i50c<;*{>74Er|22&GN}Wnle~~Z}f`c5f{rv>CbSk2Q4-G z`&fEjuw;*)__DG5TxL>i_jrfHUTp3vIrG0cn1;UG;TL?Xlg znx(94HakNcE7YNnCX=YgtP@G*{r%0k1QI%?Ljy*Gnybg)HiwiG^xrZ!Otu*0$t!fz zuEt2^Sa7Vuz(dW`UFAd|MMo$ou`1S&GzMSc0R-Z7J`N#o0e~KSFET_KhJi)0%z={!E8 z>IovNEzn%A0D0Q&HB#g)rl(89*cc|FOT!e<3HA(9k{(jWe3HC9y;x%= z`$)S%!{g`+6Mz1pQU6hZ@hRc-rxigTc6+?NzrE>|GEDp#jy@W~NN z)OKBG=tBSfemWe}Gy8e;rew^ZKjWTxeFW+slfXw+>Zr*mS}wGp@&33C2YJi|ueJ@h z(3XV4R+xj4!Y|PerWa`KSv$2JX^&5z6vQe0o^we&HLS~^ zurnA9@ic}2`}-_c{3nNV=;yw%XM`og@n|-59appMLv+H`a*A;aDkCD@3>7Sjm6K0e zPFTaf;p$>CCqI$oL_q;hhSm2-+ZWyUiWY4R;atX!&GXMI6)hYb5h)RnDbbt=xNllDqb>N6RZkf!Ik)F{qsC163EvzOJVv@`I2EK3Hf7gF!c+|>Wv9@k`H z1ywhy{pr+@AGA_PEsX(t=rpw6?8Cc5S|Zpe!V4lD^%-%6Lqg-B9lBhl^|=_?98Ece zBV%~j%K`prcOZ}~kkX@|zvenpE^J(!nA(zPSXu<~(~~-^6rs zmHwpIm4`-)EV* z^f8MrD?cUR%jFZYN*~#^4FFy70_atdK{qM>miaG$f-G;*s z09$!R@4lIPu#MQv^w6n&mfE><3C<8vOI6R$%Z2>$UOj_5Wbb>4WY`$-R*td@c8VQ4 zNuITi*eWxiw*e--%aoS2*B{*oK8ttcY8$zs{l?Xy^OU5zbxu^yU5j*Y+fg4K#@pmc zH5-`uEP#lGQq{WFCq@=J?eXa-6%Nmc@nNv!06C>Ty_rJNFS+5sv@t{iG77g~2sG%{ zxeF?6FdSWLvy2kUAImefy5AYr3>jv|ELNLHhe(i#*o5Pg!U zQ+%{sIJHs#X`!q7_eV@Ip)T#}v4}|vS@V9^GCVzGCb*jAtcja8XOBNDq~Uo7Hu@I? z^5J<5`FE)>U}vi2QCy&vZVR~(6ujUo(~dPRdY+@b>WN0lG_R}&X&(_aR#!L!6C!$u zyE2s3P=qAL&!1>VSWaU=kVmb8rYPjnDX1R~r|k}OHKe^J&;d}(Ne!3j+miCwm?8?x z1hQa&9tb2O8d1}1E!P`%fnvvigk*RqFp^~A7pAnq$w;g}J?Q)8RZ-n&Q8=YNtzr4~ zm16;ed1ahB51llU$h zXvD;1YB>O6^L@R(LnZQp%@P8d{rCxB8h2TwX~tPrpua}$0_Zd@CdFX>Q6MpSp~?S$t^Z*+gQxXPv6T}GC)5k z1lTYASeEo>ww&XvhFaojqPbkV!y%w(ZC5&&qD>dz)?l+Xy491bFW@EjMdA_&3<}%G zIvvmaDqF}ld;MPSIz2UGq~ja}&!d47+D;$b;P&$N&MVI(6IPek1a5d}wEN9A`43W* zl_ooWtpPFv&2=(DEElxLcZ&UKHudJ4d1zmiyz9Nz1&`b)+*aBuCl+sVRDMi`#~z>^ zVK9f~B~X!zo$@vOrv#h~(FACtZGg z-eE~>r5QP=?IOWA7b7mx)4vaTY!4(_ilFn9_0!~v2R3G0n6C~t?%!#4iW8TgdM2L{ zexcD2%P3CNXGBgAU3k}woB;!Dut0ftXNU$MLVOYgVZ>Nc^0<=yX1C2b`Cm z*W0_nn+TUyej%Pg@(8>kiGA=G%PMOmw$dwkj#N2aK%G?<>NMV=?#j2y^AO`FX^n(J z5xt|CC_pU29R7))^fit$yAT6>2Z<$j85d%p32)Bmk;b!Txa*;}Dds^Ju#u$4WYEK9SNOQ4qD<#a!_pF1{p`$X~8{$wg{5&(d)e5~6v( z53xOhFWLL1-^ptsro5T7Kr%s0yf>6?aaO3CxBsM=$-;Eyd(f4aL_e=&2!>Bqi=rE` z6^TEe5~eq2E%ZCZp_g9fkZ0!yaA#XL6RsjlGx!-kjHc4@EGGhdR~Zk)czT0cYPNEg_mB1d*$3dmz(%2I7`7M{l%%aHl{Oz!uXns z7h|u(LVw5fIlK^{8lY~mJpPar38_1&99>j74!+4Sy|Ew`hSD^bdG?u>f4Z8vNCmfg z^Wjdx)u8qOu*QzkiT-cb@?0tD2(TyCmj>BP>e&twHC`luRbKRcK91^VtSQy}{B$+H zMSaky933BAxdNX-srQ%Zd+;`=kBWQ#TQOp+a(E9ggR&62bnJr+2ON)*sd*>}Xs5#f zW-xMf?j#mJZxrTqe*AcY{_XM(M=0&l$#=5))-%}(L0$1En6Nwc%bgOwwnYc&+xIYj@P(!CrMJw7^#ojA%)wV6F1ZcQkm2#y@cwU_Q z_;lpuJ-SbU@!5JQ!d{nR<$5gexz z%lg@47_irZVL~(X+>Xwgn%g8wosy3YYa}|3eab?pSQ^Yvc}|k?(Nr5pJFQ$fjx_M=bbLMR&({kRgOG%5 zEQg5B$LLPcLqC4{u^xABTbmKcg69s|Ioq8%oi}I{4sLSAQ-|38yUOgbJ)?{R>}J}0 zo&AC=+mlEuddY*7{c$)f$)1^^ZS}Gt7U39lRM#hqo?J_}OK;JW8I?lVqk&&eA#8Tf zeDrqgEvNhK|LH&Y511rKg2_BtpZJ#9$LCF5g8z>yD%Ijk0>kqs@^G!F`#I8xY#{}i z%TF5)89O_jkDVyUusO9LdsjO;CGIDbI`8g9{=J^`W&Fb^zd=egK4z>4-&0S(4 zfYc2{%6^ipp4&Me8-6a7puq-52Y@IkmFY^B^1ZwA@6S)bJ|5N|j|)l3B$CueH`eHf zoa$%pBGQ#_z5P99_w94!R)Y8udj#KlzA?Fwy3Pr%f5VD2Gy)F5X>i{{G!+YC-kAPZ{+ic0Y(Gys z!>(_SBF%QKp1JV1+0hi={`f}@YRHHw5)cpwcF`bCeUP`r`y@~Wt^cF&{(iaH&L{Lh z<(9x7_fr8+<(CJXnQ6v?T7izb^^nrXkl4G8MPEM4&N3XtphcPf(PQ;k%#Zu+v66px z51-Ny$wIL9Y>mXUA+GU#`L*>fk@7jM9ZG&^Q0rf$Wy(0z%s}02z1UY=JHWm^$KmEB zj2n4Ou_s;=h<&^+>jFi>nHo3q6<`6V94WV0QKv_Y5UHU@P5ekXdd&7Aho(#I1V0j9 z;FNwxki;TcT<`YxCwO!#;Ve)v(7G#>!LNGTK&3Z7ODx3Z+Bj}vG;{%VbZVTk+n;Ww zbjr7=uDm1YE|~Wwpv~9zaBa4F^-cXwEMc#}=0qcrI^vf&7XzdD$e5`Mda-{*&82Ku zZ39JH!BsZ%_W()n2)%rK{kT0y_;e6}HCA1-Ay!$>Jr*Y8g)t3kDWsE`)_= zV*3K6(zW^M*|))D%yXU( z8SW%OPZ&!+bkUsA-+q0ZvgRfZ`6xZ;T!$si)4y$>Wkix(cH+xU zXp^nRBP;46fKw{Ku%sUad=dO>{n+k)T<^yERmJl8JRJ{T^H?4dBd+Gy@Z3yiE3LUQ zl`Vqxw$Px?VL|qc-v|<@AmNf}1Bcihe~UykyBSxhf420py&N2Db^cEtQ z{{fLwkad6bps z8J9>Pz&VWM{AZww9J&S}#Ei=NXo9>ZB8eR~KFecn5aoz|DrTBg06z3*Sy)QrKe3Gq zazK$V2*wy;%^Dbyu;V38D~(2RRc>@JJ+!i<`Q*)hb2uSdY+paGtt9RaU=}8ZbORXG z7==NNZDvX-eU@w~x^$~Ij1P~AF&7~dK);bD0V6tzQ90XO2A8u@f48_R##*16d|ItO zPZ!X0(?GN0(S9E+`xuu^kyt@E@e)QW7Z!nN?Em#`_1k~;x83&e{h$5y@Q>#=`NgWs z0$oEcE)`gb2n}^3H-+s%3aeST(7)d})ot3DNe3;RXo#%4d?7!hF&orRZZw&s^dW}S z18=%WIGH$6BAEGNsIU`q*#hfM>}W zehR_%^3zcp3oi@Tw(Sw=H9~oYFxT;-ewc;Zx#Y4$hZ-lZ@!=4m8l-e3$5>^BuF>q# zEV;~-_umP#?PHq~0aF8~M1S_8Sr79tM3v|Ivzy8`J%E9zi-I5Y)tk{vh1mma$kz6U zlWjIaXrR5`9XK_>mIf&NCG3}(N7(H+x9%B7GQ1WV=MK~+`O{J|GQvV1*&)mKA|r${ z9oyj8TJ1O?RLPM4zdpXo@WxMeE5aTu2u%V4V4rIVF%>xknHx6HUCT0`XV=w!f}Bfg z4z(ivDNVHhfFP3Rnq?#(-O9$QSRbD$ChY`Wxe-rC3<67iY*w>o`<|K03#D?>yVa+i zO|dQurgM;FtAqSwR+C`&vc?pP|G69VW7hc)|MRP-Lv;6W%&s_TL40O<@Xc53<+pWL zzSX6-&Pu~A5lnu~H1)TW2H>!pmb&n#Im_~QpCm?~J9%f%L^^P=WZH5aozzpj=nZP} z1A*1<@nlg;JFOq3M?ViIbxqH2S*AL0LiML%m12C z85`iQH?lzxH&*cK!(BdvY0**qMS$Qdp1hppHUTY{JZsyIScUnSB)DDQMOHA98LTD> zl0}IE%UlWh7i6;{jH+EJ0K4Ub2-O%z-1>^DiB!G;PKq>X@!QPdUNMNC+vSIPpp4~y zkye&QH`99mdR~RgxZbQzBsD|Nl{{ccfiz7|@+x@FqjCytm*yW*?XPPOisfRqk|<)a zr^L6JH6SSUV*qUFZ$Ey&-mhS}|0)ww3lX5&m^84ayyKmjlGB|j{d1I{jeLd&20rB+ zE(oxz?eW{K@Ei@5E&zi-av%|D7G1Z_k_qQPzxLcuWl06wvgFTLA{bB^iypB(DIcGF zC-CEb`&nitZpG=xEW)W-*Au`nV;Rhse$3R;J$q%M8<`Cg;3c7@tkEZ#&4;b}$vgSx zF@5Kw=FK6FV%aG82J`8kq~(hT^^&0~%Lwa!K^_KQN+%0A@I)}U;{v3_Su`gDQ$mzv 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 @PRat**k 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�lu-HYkz2u9wRR8TQxhgs_a^_r;>Zk$yrh zIuy(Nzy))4nA4-01Pg4=l7sCmA2GXbx!^3rf2kBDzO(ma&mp6O8xX|l^iA4JPSU`} z7+D_WD8Szx+JhwMd#ndlP1YLsos;+MumUdgDznXvasCy>79N~O9KL=0L_jk7b0_z( zbEXeD*Sy$bGP)GXl7OwACf3bHog}@A6`G;V5XNF^C^BMZw^Y@C#!VlcJ4y&T)W9~% zQMG(xgC|T;sKd_5csg5q|6ovHXk#$?(&xytaA5y{jMZ{;x3Lx)Y&hhb|Gon^&3L^M zsN7fj>yo$d;6zr(Y`875okAR)e5*D?lCEax{6S;eU|X)(ga4MGES5R+JN4>1GZx6^ zrlO1w`#Dn5*7w5ZP*_mY;q~Ib=2wieFleW5+cxOMxIA<5qL`nX)={g0-Qs@za=i)G zhMtHP{S|YgdJM4_Uglhs_-<~)G zEPHfq4+$6@Nn=g7!#vJN&$=Zjl;hfm9GH}!EYXJVg%{gX22fA~CFE?z2pP=8bg?x* z^;4VC5jb;E@{qrvX8)pMBT7_G9O4OMsd$MokrC+ZlIvPENm9!xyqB zP_M|JBvJ*47@D2(`9V!i(4%c9`e5Jq1~D#(#*mhsevYN|aSC^|VI=$K%5ca%74UvPBBR7ja*&!?C=D zf)r8+59RcHs-An31^Q_eJUVK0j5ur2py9AT17@rO=Euk9Z*}ju1M)GJ?@i=&9jj%Q72mlG!j~+JKR5M9E-`+p|$kHpw-x(z_ zWkx+ARCGxd%+y{?m5G(Zd{N(rJOORgB3U#Isq`3Pv%7E8(8u zvh?pR=gKQ!1dMB9fs2*I~v&bfMEe-5MFm`Ms1K1$>=Dy3q`@CWv zhB3|`UvWOX`#8~IKWXpl`&+$A2!ewHD1VJ}`Ie2q!L%+u%tM*Of>gCX-X2LNz&0@| z1HJ;xnH;V$z2EKMkMA%MM9g&T8AHA6dZb+;E-sA}S^(?|(CVBnw`H9g*zwhrKv6lZ8)~L{_OYSq`@%=|pTred8 zj?Y-pjh!CY&}0xFC2(4>%?rdfeUc$FlRK~mpl6(7eiL-^@(OtfuEy)ilv88P4_vpi z$7gaJlG>claNX;A;aqEWZoBX>b@dX12?u_?Eeqs$$QWIj;6DDx^xhX~+bx^ojuny2!shZbVDSDf)HsDA#05@4R@svh zYbG&$L}2atk&lfffHc~?HW9V_0);5ICl`KGP6(JeS>oV&lTx)A@0KZOb6YzRuX~H$ zbhBOW{A3uzS8~v;{=p#U{tgj;<`?F-&`((mhPrJSPw`0|OJtr3lSk|eoB#X&&HoxF z&cs?~Dq3iVuh;S-tI$bFR(4%4Te5Vs4@@D8} z?Ce1pj##-!FruD?agI*T6F`sepZNdhKYxNs^5W-li!JAp0Qy-z(%(NAW#zR_e3!Mr z0bER_k;wV>OtKIiX_YNz!g8UZfZ1#fH3DZ3N&BSRW0QV>Ot%8eilC$uI4?RoU3VE6 zDh)bQP>MT4QwQI~rbCkt)GG}Vb<@lbcBqZR=MX`8;q!g>c0V6lXT=tC!U;kPdP_|{ z%SJs&KP8Z$pAF~xwv8jq7PnJf(FXy|9DpOYCzVZd1MIrIq8D08>1$!G3fne?BGnKd zA0P3U#Bv^j%F4!G>td4Yg7zq1Qb{a}0rYN#)rmGQTUrYzQxh!6k4WrU zWLIxHYw(PKEQaM}1B}&cwj{Q;Z@<)ERdBI5O`>l7+%>{zWr_YZcBb=qIDk>R7 zi1BUc_?k;D+7QPI1bmXM-a1?S0;E8|4NS9INV+s4!Ia zD~!*~!C-pA(UEw%A0Y07o;V*{GP)iM$NSd0^jg5x=K8$9@rg}O6z#3lwXE`Yl5fQo zn;9)2kcA^YjZo0SDZZ9bID6xysZOi_sGtt6p-$!!Y`(8ONJwwTEfTOI$_cpZ4{~@t zPha0GSwtG!Hb^Y@fNq(u$;Gg8J1M9vp5JnXau_!Vqb+_%t-TMaNHJ@NmfK04-kfdA zxR*6VE$+nA_^i-GRe8jMbm_yUFrzVXjR};M*|dRM#gQ<2j|p{B+t;a^cL?$0{kVO* z&HDBxT#Z5|u`<@n01n&MTT_Q5HrOSfk~eSHTWM@Y5u%Nlid$ndrXonb0r3S0UKTXz zIw-|t&i+(8WZmkyq{ru;}~cNAMeeq${kW0eZ#O0RzPz$A2HK;UWS2Mk2CHKwzSBqbytd{Lyo z`#@?)PP}6>M8n-)IeqBR=k)!x*xMpiAH5}<2~+FWT5R!$4YDm%YO0a&K2-;T(fNGa zDoSn>5ciwsVe?ijD9O_=JJ^Cywk2Mg_dO;Q3%((tLS2tjgmjC zU}v@eI6c>=`}6aC|IX1bzw%|8-XB{nx+E!0=VL=O#tx!w>kZ9VTN*4sZ8(Fnr25p< zrGU(ovRDV(kixW9Y|L}EiJDT`QvK^{pk9m$5jH5qw{F=0Cn!qfCPc$^%h*c^ZRq0a zx@Aixv=stG`={u`Ah)Z>2RlwC*HA%)St>B7E+#&e{t8@>S=#A3k27xf8~PET$DmYq zH9*fJ5IjD5Uq0SMLa4*Im}%wZ>%j;SHUf(;p~$}!k2ItbJ^>h;F$;#8F!e`-;Nt$hJzi*nMl)gvO;`8!WRD` zSnh@lW=WyT&!tbFB^L-$_OTkz*&+!`G=e~YXZ$q3WOOX@@y1v&-d%!&%gek|J-d3|Ji^0zp?(?{=R>{AcFE* zleu^#XBLYo8v&|)*RGEO1>%~gqm3@u8q|ob^5wRziwfcsT@aDJ`Xl5}_k_yGX2V1w z!nZ!8bv4B$OwqnTu^6+DC^5~LXlokLM$h%wP6?~zPDD=k(F_?QmES=%nUH@R8Ln6= zmD@8xD$VAr1D_gw@SC$z$BdhLCR#dCS3d9Wv=$!5`DBn5iyG=QZ?4R+Ch-Zbew_6` z&p|=J;rJdNK{Os>Id_Zm8QQnbF|IxnpeA6vDVtT&OqVY(vYCg?^o2YlGcX%|q8Dpm zC}o$Z<=}w|UH=uT3PUqd5l7piKv#P!3YIGl$w)n-V?d7Bhq5v>e|8A<3gDYYrh@_Mua`S z`KVmdTmUFG(TcZjb}!{RTAcmk$LC*ud@}OqlOw5g*Z!7ABhBTxvn(ioV`q1$@;=?p zy2icG{aii2FW&{bQTC$gnE3NkpdZ1prsWU{v#fq*I-R&p#!G$Ac9`(Pe?7pfw{e}R zV6~=u>Tw3cEIc4R)6-iO^YoWL3=PGtvB{FXy~LMCJ+C_car`*$KaR%_q)jd(=EO<- z6G-u>vWM_4_{E@&>%8nt((D~3BjFy!kQ94D#axE3UU{_WqH1+Kw8x5t3X5V07uPbd z>ZUj=_(H1kNha9*E;6&ta_KC$WjM!PhFa9a3>gA^*ck!#33P}y5C%lCdYo@(s!H)s z%-}r6)Qa$GQI3)H80ysacN?zf2JmV?smtsE?bS$voaRS76S5} zxeXL~R9!F>A-kiX={NSt*^raQ1De&Wc-YQL-%Ga_S9OF}hWSxzk?Sx|h-0sE57o1e zU*F$S*}T^T%oPpTCo0r&K(jCrk|QfIh2~L(JUu7DI!{uh*7yTVi3M*WX&22S9<&59 z-n#Ged1u><*&jt0Wl)|6hoo{Yd1Cs>t=DfV8CIj`02ODKRC;_DcBPYU`s0yT5^zj* zFQeGAdE9z635k=o50fPj4UnBEnib=t2^MPv>!@u8AY3dV#n4NTK|4rH=mL~}#)zRd z1PdKPQ1J_)$QbBJ;MJ~dS3U0=odPcE7y`()fCGGjk>Z<}TP$FpQ~B^X-HSpjs@&bg zo^ZLoUweK$vEN{#TWbm+%jpS$lr#@JiZa6`PBifqp}g4_jO)B$M^D+F7-(m-ARG?J zPCJkAFZ0kw`>6q+>s_0E=PiiiGi=qpW+~>{4NJLx+s$D9&tXfmAGsrL?S!E@D% zxVk`4eL#J3Q{`(>tCDtB93|aSLu*aK^dY%I?jp(ae6Wl>5?khsoZ;ly%Y>HgH5apC zbi8qn$j(#3H;a5AjENg1HW7m-vSiBbist>AQM3qF6k;0j%K>xXS%*MF3A8%kodKM3laDI z{e8#|{$kdKqyGKdU;nn)Y7uNqcWj_a(ue;LW{JvP9?71^+8yH4{S^=u*vm6jDw~Z> z!{}K|5qUN!A{+Y?hk<9~Ny#=_6PQc%9?^Mgiha+B|}o7HUEtbk6$*5JzQB& zIZ4xw<46gmni1x z2Ch_?8cCVC(v52}25^hfFKGCkVLyZnyjHNkhl;y({4VMeg zV%pQ`vP2$wCx5X{p8@8n7?-*D+x4QiXkD!^LPr=_X5ju))uM|8L?{TjOj z`!dqt4<<*;2`~ur_03lFLp~&f62nE~OledEGKPtrHZ*G)%<@F}xv4IE(EdHvQ>Q>s z4{|^14ne}1B0^oq(p-_E0J~tgZXreH&4DuFJP2%A(n}C0a3s~`n@l`wZ)^_u))QG6nqVWc zJFVaRMd7@J--zd)PzKUnn{gh{!z4z_)kWd8Np2EhkZ9dKJtTuoV^Fu2co10;MiqS; zJzeA54wj<}ERn|ZP4yBk+3cIbCv1{gE+CHX9O-!-ql;{n3Ip$m5j_V|r`id!i2r>5 z)EE;VAKe%J{dKMec_<4*x)@CEvl_}r=i_J%lD(woAs!jC0KKZ~(UfK{z_#S?ZWPrSB*NqJ*R|2KiO_O$l#B4X$r9A?TL0m{B zr6IKdZ5+OXBkc%+LpjO7fv50LF97xlA2f$rje(vUqZsJVSV^0ejZ-v1rB9qDRe??y z<7p|Gy$Y6NMgEcE&_J8UvO#*|IN`8GV!7OC+#iQrQgInK!LbS$C8Mq1oM`dNrqy*< z%&0+Hut)z3Fi2cOp`%9BfL^q*$Ioi$E*ufe!cM`-MtblQD}oSPb+dT}W64eRBygqBp=uMItGjNc_GXq0^u(JV*NsX#%)PViwL1=;5r*ZE)&bX)Q|Tyo+t5qe;Yu8MDmssB^T(-2J-`QG|@w z%MCE7Hlp*Y-eIb_F|Q|Y8Z3yuK3%?nfe>-?N2xD9p5aLYVU~?CIYKi8k_cO>*ZC+D zQ7y!0zmt6uA|<7+d~i#KbSW9BUvaln86^B;8W=<=7k6A}3*6dW(E zs%M?Bon)9*%mTb2j|%+GSSH1@J$rm06K7%Md^$S=i4&pymGYVzXvJ*Lw5hGWf3tp~aUc_)XBbhprm1?j%SIff?Ct(-y?xv4 z{J3)OE#Tgh)D2nX8HyF48J+!GZES1sw)#C4of_U+O(k;^C7wmhIdT7(xjbU)^wxfD z?k(`^NjRw93WI2QTOVOINOD7mm2@OP6Np^w051vkHMm*Ln#UBC{FjGSGgP{@ke8L) z{d)WS=sKse7>`Rpa607{Z)RzY4i*Fb?NO=2Uu&a%((lAC4WeTl!%wt0l+a!j-9;WYAi} zP4pjQ&Pt~&spOzm#!plgjoIUY{Vai1gh~*P=dZ6{RTrR1OB7nGAvO+lD8}x#+$@n0 zs8I?Qv)WiXIn8?<8BOc@x><9Q{n!$TEa5L21TY5Qhe?!t3?XX}>E-&(TakS@?$ZI3 zocX~<75`}VZA8PpADiB*^U*FjEDAOhQzYCZ6HigOgC+eNe5>aZ5I>Eox)ac!Fle8WSI+Sb^Id~$}= zHQi1d&yq!!(WT_% zIdaHq8orqYBcg(B>U{ZVtcTw}Si{_n7kjfvY~-1-`RPR8!zqO(DnAP?(>*&up}b@V z-WRHToVQqoa>h<)aXK5?Odov;*Mr9qpidn;f+gIs#0C&ACr21aS5yfh)p z;m1k3OHrpcl5A_7g61S9rZcNE1GeGYPMMIIAy_8_`!C@eB_Z26Ry3nzpUWf~;0ZM^v=3`V0}T0v zhT>kGFtA1;f<;AXx>|yU^jwF`xa>NJJi$`nL`eTrfXAgFuY(F4bCR!teP)hT{*#sYG=VCk^3U~VVSN%p~{pTVPCn$5K5_k@Qw zPBEw+aMbSZupJ9&$=37A9m=~XtmV)I|#*&GWS__;?p(K!7(*B_8y<9$&v%9PRs0)P>6CmZ;z*=r+sp3&V zaUvu66`sg+?sUNihSSjap7sfjD>f4o0FsCCNgH4 z;fOnQy$F1q5(4yU#`}2Gc{8!Cpa_HdKF+)7uzYKBrArOr#+JtEeY0RS{rg5k6wW94 z`OL&p)n0*KGhG*awD(PPn%D|Up?TEeLwCiMWYWimoy+%fNqQhcPkpi3Sj8Q(O0|R1 zC>Z+3hd*-O@Dsflh@~uV`xBUr*SsuZ{9Lb^r|Lqb{sxjOoaTa>SkE6KtG-)MF-@Kw zshkNkL#{?$mTAVgkks3}b1dOVvk8n#5_at3h z02w;uG84s?rCcQd_JBd(fBDN_?B^&L_dhx8kM9A2Sw^BSj#T{J@9t85v4&Kyf@YUY zwZSXb;3VlqrT<66Y9JH7emSfDxOW z)oOMU6u%Qzl0<2?%PG+=(7lQBsOvs6(IB%Qp)U$;Ja!lgEnG&=#Ar>>KraqZjYImf zX9V8IAwR2R=G{*=_0Zm2va%$5*zKE1nA_~o!?Hxm)Az-`z^o|Ny?xbP+8aU-%UF3% z#Z@7AEGMLHn+LJ?vV>$9)~%xO&uk?{r}z;4NU1c|?@3NNM;lTn6lm8`eJ+gam#sKO z8$(c#sP526sZisz?7OP9vaq}oEHHEtV7uZ&y+*-f@~EqjgRlvd82P$+omkpD(%#x{x^k-ZnR*1R9+9SQ*)Ldeb{R_+5Ec~8Mr@U`&?=dLOaFP6blFWdLph6#d(w;n{d5{er z3zOrCy4Bwxv?geVodWVAM#T7zn97pu+#b$^8vH&HtNUkldr@r6z=_&x`~CfmTHC%k zR2bmTdCV&9#M?p_J6mIZ{ch^78533{mX?wH9M382Bbl5@(grssD)1()jEWKIvkd2h z5~qoGi@5ArM2A`AjXZsxY7S|Bjvxy&;u|dt~(Sam{$fl z(b44^k!|f|Bj0-S%@=)-i$&Y!fB(Pz-x6rGRM%_~KldPKH>+>3qCE8a{P;NR;EMb0 z`~|oyZvoS(;jxPbGAt|3bMdl`7x0wB2>809B;vK-h9PWYlb1tm3%uCvk4@%n_ApD> ze)T4+hfVmh^Sd-L7d!m5(6B-T6NJR@U%bx?Un?eF7M3Z@t8;)i~rt+moNVbVV~vfh7Nb6~?$7 zLcI#`_1v7UkJIhG|LDF!^NU7JDI01M@s6vU!v3M6cgj6R8WqKFpnG!p-?-2%K+pFeDv@o<(mt z=Hq&1YEGBwK~lN7>s;CjORa8rdvG$HvSJcq5+YFmCfC=>u_pWJN2u`=d_V~k5N(DSTj&#c^Rd$h;S)EJ z(-2EYwcjY%)_$YLY8`X^h?DZ1%=3?b{GzhLRO=mkzbyUfJOh=rkV(FNAsHjRx-#PM18V&F1q&Rfw#fDLnFDsB-Y_zK*SVhc zPFyyOZs#(({%6=iGkMq~AJv^-B0Q)ECH`h&CGd#+nAp&<@d-(W+tqwa42pasDo_GvE7m^?VPGcOh#@ zw|dYlE~W=VU}WjX9xU7TSq}O9YyXm8oSW9bg~#*y+}u~Yud}qfId0#!Pf@)9$YCXH zgXqM$OaDt(zTf8|+UQwW)2)p~m_Em@9|nV_w24&aC+G-SB-z3MDie71cfVr5-;zy8 zTb6BZWgb*z=%s(u@;L`Fpa1uN9uMz-`}hC$`SE;ys-n6!b-Un_k2pP!MvOgr!O09m1JQfBCA|bj8gs{8 zE`Wy#DT1FjK~$elWh8T&J}ZnJb;!oQEaYEg z|Ldpi!`z@KEcxR{aDyBZ`+B%to&UR*+V9`D8v*X%n>-Pxk&aYC=hHWRE0iY<$TS9! z!r8syQUF&`?GZuocZGWS@I#rje#5z4yZ6H%z&4f{1jE}gp+~K30$G>vn!zzgLfo4e-yNo+p040HB+$*?uAbM|<0_J| zHr*JDiRl~m5(qXuBIBuNEzjToeBMvnwW8;us8MIMnoKz*n*!DJdDugLH_YNVCOV45 zMGZb=BGJy;1z^0v~hPtoLldSaNPpOrOv^H1y3V?MV~!c!TVAbr-~hWq-3o z_SDn%WNw&mx94q7$-vpS=IXH>h^W+wuaf^KS_JCT{r$-*_m-aKYkRpay`L9JH{hN{7yD_*L%A=rfZ+hO5X)L-Rsb|IrHfzJEI)6RTC-8VGq~guQ$q*dS@#4N*$G8@1F~Y}mr5~OA z<~@(-GWv90sO2lj@^Q#O|Nr0bFnI5QD&>n0lee;7frMeqUnW^mu9K)Ql}TO6Wd_y0 zbN>WklQk0;?tCx%ee|(}W=A@uaQ_>0HI|T?c?t@~nvv_zEtuk681iNBhj8sm#M0ag{)l_*ND|0NAwde zq1lRx@p?QiETgK^N0@9&k#B=8IL>kI%a7kVI_;CTPXx?W6ekia%IS?LS}rV5HG3zL z8UFVp-j@D-G}L}{(R~g^OmT@+12T+~G1~jAIwO##z-AUgD`(+?_n{~a)?>LjN(gIY zURW!>wTkA-rT|ZZ({%cYcvNdfvx_R(d>$~76JHB=`BaYBN4J>5nvJW}{(XvBU6GQ* z(%X>+dmw*Z>3tMjv#gpVW~#$=%sy^?<*GWpDOn1LSiG+xZuV<`WC z{!*#aC8*HN zB9bf={7p?}4F0g$d{fuc$}6lL407A~ZSLmQ zl|KEH{FkY(u|xXHlbxiXX>z1WlgH_!g~B9jCqKcLt3sz{()f7nYWzo(RvL<$h} zgDFO!89Ad#mx5mjLuH8?2z4bnZ!j+&$)l_ign|#pjtHn(s49&X5O6_;le^~xZ#-nK z;sUj*g&RpSF@rYZO3=CaW3+1ywB1N(>Vb~weJn$Vh(!5<9!S8KV4cKa^mb;{R3u{H z%Rme*rpKwIt-wvwQG!F5ei0@yE+)uC?9w?Gg+B~dlxoVPN1f=;=)Zpb9OLU5c{&M! zs%RO}gpTyx>h{)4@QlLbTXUJVsiB8?m)Ne4SMkE={)pao^2C23oX#k#t}=~9kv#G=qsyXe=@WPoNqT;6?)3I*f(Pk@ z+wR31=)Ndc=0dAF5VjK1=0QV(mB7_acnaE?GU+2E^TU}Wqd!XYA$lx)yuV}W`|BG= zMz-_(^=&NKl@WK#+N>aCDfYl^6MpZ_;mUP#Pd0|D$^?o7qP;5aD&t< zU^$!3qX_S$ffn0&tpBCPC>6UH&vkC15^b-7XxVXAN%5sf;mEmO&nhTLM_xe_QyAny zu{p_O35&j|-f)Ywmye&m9SyVQ1(7<;g;t6D*RO8|$wz>PkB=B&LG*}zvT@?l!P3vb zgLxKUCgM8CImg?7=yrLG0(u>d?Trel8HIC8JKUQEYvRI zgW5|)#p~b5mBjqAA%LK>hVwC~LkmZ>teOsUXoCj- z6qE*PXcL33d3j)cA@3G;DdKZ>v?q{1FWq2iVdT)=jd?4UZ5VZE`Ry`{ z1U>J^_YpNRdoF_SDXbFRq6IfzJ%^3NZCQ4|TNGO0i36unv>}oi6f6+M;*+{KWJazfvl&)TD%nH$Lm5cRNYIb}c7kCT(6XFBkM)GK=#ZDzc5! z`JmsRHuUWK>+7qQf4)8lUw)FU_QzQv$#(T^XUUHKWpCXvI3?q9LH>7DU+0*hFCFW_ zM{m02ax**q`-&?&MWYkO_;Y+VtY1fi{`qJT9&3xXbP}PJJZX?^pbB`gZNqR{*>~cTUvK5Gyr~$M+6svu5#JxAt5{Y}eHy4n{Tik)Qo_i%S)( z&LuGXW}S;Ej{<^CPD3LJqQ~hP{8liw9IisRbATy&)o8o@;oU#%jY)OiM|XOW$NKxE zTtSUt0#0n}|h?tO6GQF#PgIDwmxwGd1(?jhsM8n28h!h z{ZRhWt;R&k6bwWx-J{l%rC)<(fSHNU?tTP47i zB35yK7Tyf;m%n@-d%lj53E?Fav=~gpswyi+8;@a+T}2SCIcf5nX~LwP!!9mY6jPeo z(QQ1e&wu-#93KK~nmI2&?vGeR81vAT@3Lp^UqCz_Kaow_7^OOyShv*rXFU3y;B{pW zwBt*NIU1#FQ`NC*BfzivmT2xbV5GSvl9&n#F`3f!UQsYlJj#CIg~#oD`6iGtB-GKH zv3eSBCLsp|u=7M=6=4K~cZ#R&^Y^!&Ycyh{BRgr$FUsP_l;sC*^(1@w6LR!RT<_rK zT>UBC9*+6s?`T!s!T8`T9Ut?3$ctszi z9zPD7T@M2Ab6O=)agTcq8SQWASvH_6*2NMuYnHk!Vt40ggM%6jZN;-!ujvJkbaTI7 zL>G>O6m2vXDm4>K{M5tej~`@$-~~Ze326z;ox9JKa!YlSl8u%5A!ZP~4e>JUg9E z#)C^En=h{3qLUU%Ax-bRF+ch@5_uFx*~Gx*MAResy5_Jjr5A+xeH1bd+g0}$_26AS zcG+mxgJPinE`q&YAD^FxBf)k(4r?}ubPS91?ihh%jD?pL$c5)B542=mhPJ_#3`#g7 z8B{m7vOHcEB|f84worwS$c4gx>C3i_QA(z?u&{h$?N2Q`Y$G907FZhCut&5|KuLzn zcp`gS&o^1kphAbr(70OaVQJ(i5sl>fy>z(1DL+RYeT>Dsb4ID5Nrwg{B9$-~vmMLv z+Z3#w`g9gwU|3wy&Y3KzsLnpBvdzh>s)EYdGKCnYseCFhX?+i-e5IU$sp4KYzS^Qy`MO4(?P`Gw4X z5o}{o2f?6)pb+o7z4h1UH-+`s1_oL0FqntZtSC|77)0lr3`yQ0+d6R~j+mC8b~n@1 z0BC5VD6qtoxjkah<%qHLctkIm@2)npt+>``MFz zfqPXPY&sgL_5!Kl#m7ah!N1_r<>vV3{bUaqur3$qMvmw`bc_&*9F+{oWkOHIG~#(M zhazw)DBmn)-V%f+z7Xs3>0HgIM$jodvaI5pr~te8{{H&DoUbb9i3$Y;-7LBzv(ahH zqN}XHP$#(CPiYQ3Z&#&E?lJeM9OZx;$Q4W(^sf}kn7XDT1aEq8WvkZm&pURi#_u=j zvbZ@8h(L9B^>nRLOl}>}bDWVx)V-Wxb7qo3e<}aeq@MJH?lfNSvYi2=3*G!zTD2E7U zE`b(rba(&p`Hr5LzzL~~Uriqt$Nk2>%DNtB22t=uv>1{%j;q%1JWWqDv}d7h*(mrH zf#-3kr7n{0StyQ?6mIh_a$oaGdt!njo0#ggHNlRKkDcg^qHXx-90|U;wxmn>OX{Wh zF^0*Jlduhk{fYcNm<8Y9E#jdTl=}n|=GwGx3J{+$E9s!Q>^6wdhF0n_G&t;B4Hw$* zPDG=HE`^8Si!;euhXg|?iOVp9qR^}*!f=Sx1#IzAKLi~5or;&3(4QiT)mZ1VqSK$A zV#KZ;w-6{7(V^b;tE+LK`I|@Mu`%=HBP%$O7<&Js6^r`XKbHmxN6TvK*rI4Ta z(u2|R*(N(Fnz+iLc-d@P)IQ;5={8}FxDYc?p!rMRdiK%D@&V=QYO$+;a-ZDC}%Ytcw(L!!)2H?){x&9xvVnLgVt z{xF+eZ^h}kt3aDfU9mA_`-%Sb5@%+Lc2|vDEA0uN#C&)> zdUaK>J^y?>)ybmw#o#dK{n1Z>Vb-*g|9v0H)XiZ@QW(TgPYWe%>E?DPXD(uo@XIW7 zc|(r;r&cep1d;6-c^7Z(CuIBmrk^n1u#mIfAJ=m$f>y89=f|fYMkbG&`hUw!7f(#) zqjWz<6iHj+ut7de7)_l?UQ4sZENwf{N3(wswtScmji>eI<9~+&4I+1blGw7x_hhX; zLx`R>pP!$ENfDYk3Q}l${ZDHLmMO)Y!ZzErzokAC2lCKK^mRYu67Wq%AZSn*-q41X zvTEIO5w`n%`ldzAog<=14`L}f8FCB@m4xzknr0-*yb7Llvs){4=StT}=fevfcWG}S z9fb?V)q?3rU$(hh^HRs2X{cL)NW_~7$A_R@0ii}Qpw`Eau=@*`>UHP*X8-v~;Ve$% zATWQw&gWK_Q`jbxw)g89 z0F-=K&2F{gG(=$Azt_w40cwngQ<)USIq8uCx~Niumj;q7eH)r-vIRCFYAWQm!0{nb`*FV7aE5b(VkyI2^^EKJ$y2 zupy=`d8p6cHjm4l@}8Ib^L*R3)Q210PN#n&t-BXiPtv+Sl%U+bTjiJ>Nsakh$+68%!l=qixIkI1ESH$A{ z(t|^6f-}P4pC2Er0{;8@?%993ry#g{uus>iz_Oh@aRc9h`_d0J$~GS@itRpE>d+%c zZg!LmnZWzJR;G8HG+n!Red`?u6_JAwiK(ft@@^Z%lB+ejB>;3wuo2sswH&04G?HgH zh$IPdV63{3ntZwit))I0v0q95Da><%W)eo8I7@}GRV72M(qQHe<4QG`>-YKm>-(E7 znisiTgvG=$H%sNMe=g-jPrgEqBTO5<2PiH{l;|Q&vzB=T`)*PSQi|+4D z7a4@9d6t?#_Jdm+V0jn2h3uG1jLxqc4mj!(mRrv0tQD=6@SsQ>ZWo5=UAI&6vcCtzK&m^<*6hjC?tMf-^MMVOdu8QTC^(un!m#5{$_;ob8DlVNLs0MZ~U|Ju+4ATkaD12i1{q=9)JRKx_+fTTp){!NwncfpFVY)saUQEV>T(e@__ zjNkz@_C$k_66jkP$p%W_?MDp(<-xv#lgnJ(tBKREKSHwSw4 zdicVkdgk$%i>s*VVe_<*6_xRqq~Gn%9ON-&5!9d9B*Vj*!@v?uZ+Xf*l-^}Yz z3BwOasZBXb$GVwEKfMp@-q5(+LgDb~P`pII}v!tvDc<7n;yIxPjadu8rsGC}Ll4 z>-+U{w>tK&&dYK0dY*sn-tLF>^X>Y5JIQb;ovR!xHS2S~;;A2u@P0e5UpjoO6vucM zR3EFskxT?d=WJrXIbF}byLlDgE&G z>HF*aeSZpN=jS!U-aOWCGTQmRzPkWYY3Z+8pz0l3mfZ+KBiDU^@<-j+|E z5=?*WQ=2iZyAaJLFe=iB<7m~{`mnmb-A}+#wV-0x=ojYX8}Ea=6eO<#mmIiX>5`V7 ztDEoTPp|9e;}41c@#=8`OfbaB5B9gy>w0d|rHIyzt+($Up>wzIqQGMnZ*O1UzXFKK zC5U4ieM-dWWTwM_9{2%MWxjT@^5LULd^%6In%mEs17@+|gx-3?9s!+0H=!ur0u{$z zz31<60Lj%SvaXZbkWoH=pr_0GF9!CxSLD1YQF_6?d@-)U+p7wsiNf>a%H*UZiHM4? zmnPdZ;=p+;(!jnw=Pcl};o{J$C1eXH8lhIVW$FC^btK+}x8~K^VQ#*3x9wr}+`OR6 zm1K{CRPVdkkvC+bw7K4UfCTk0nHyJ0=ZcoM-Im%8vx1_~6L36>iI0ImNxGcgo&ATg z7QVtRvdsIH3zB#1yQlym<&NG4ra*p&M5IRAL>E1@F6iy;)Fmng-MTo&{#q*wStzqP zA`U$^-?nn0@YTc{a3GI$rLU5^8N{~a|J^%#b|#WUCvjD3=Ja-^C~dX}1@d|=sPWXW zQ*lzGVkax3olb`ws%0{i>-|cvPWoD+jvjh-Dwb89dQ^+swFk#}TKW>4S_Mc9bK#nx%m4JyeH+;q#)hWe#tUaOq+4 z;rvafkd_}rdI172=Wp@AN$2yINWto5E0)$RLdFW!PfBR2NKig*6hseE>cz4Ndi7jy zS3BX550*w-5y1XejJXLIBg5;^7K`c*;9_}K=tb`Rf$BJ>JPbW;k4M6$HRIuUz1iO$ z>xTk!y$`WfQ-xAm7T61(87`A2@3-A%@02nMcR8PyWk2$fn+4k+>_9jHk3^9>{K4iqR7?D7;eY*e$Jt#%I6-^$!o4f)5BH>S&+f)Li zLju{yxo1~?KCWcFE8M&vd(`paQ0NcIR3T7={7f(W1)0FDl6;Gup3%MbT|{i@KlAFb z`UE;twMW7H@cy|L=6hh>j`8k}AG`O@fb82#w#yt**~E+J)We2hN##DiXlb?EFE92( z7|WEXRe|tb&k)#eS!v-0HU(Q*a8H|<+7p5jsLJIgJn>y z0-~nN={j;0s~ua`@K!@!|N%I(P%+WgZzM zd{S7V37P0}eSBX;kj*mtmJ73x(>02_Ulzo8NB4Ty%aVUX_O~8kY)gj%E*iSD%PV^x zSvel&k*u0$Aa1$lcu*gdKbeGhL|pW%3J`Ia@$2{NVm5;FEcexMNTSE)tWYQjJ2+{u zlrCi1cj6)C96JWqgJ?WyyE`ML#}GAPQFKv|_v*cF4dgg=EWLMGb4M5a3MJgDpyzb4 zciujPt85HLUY=E5%;;9ZCOm`Ax0qj45Js7&_FUv5I>~sqXG5!x*v{O(>#tl-QIgx(ZPfPVC{j*8j<_QxK@+pl) z9ASg*yZAQsNV#E&EPQA4zxiMOuQ*&*8IhFyR*NP)SOhq_KHne5&yV-x?sYq1uQuFF z;{m43kM$Z?WVFpKWg|IRl|ahh=ZnFG%Dj;d=n3Zqh!2HtNwPBiZU6pxy4`=CPGw$% zDXP|+dA{EB{qW9%$aAi3;t@abm)-Z&0>eQs%C9dQ$M<8kQ6AoH5KYU`SJIJYxQBC| z6#saN8VBeoJ#5}o3#&mysd-*A{|3IeBbZCXzZo_d$qWXfaiT%nhXZQ6_UAX?kJ2%0l2hF-#V;y z?y(HT0DLu10K4FN@JdI>UrbT#V$%BMpP_m<`W+S?ia6dm@Uzdpa>xUR=?2y;sv!ViT zPb{P~NJ8#QO~a3Viqx~gunon~r|8Y%3${hffTZm^_Y!`3Ht=;^}Bj)|G#^ zCK#Ye{spBQQ#p2FaV8`Pzq$qE`t?bGn+?QIefUb4)t^hg)`3_(ugomXL)(1&`J<;S zbyH-~bGyT}Kr5jGYUjt}_}9OxWbz>83iCE@b*RyrLNK)GiLBhcDI7-ib~=CWCA0Au z9a4j~#LI^M`0>dK?$sjU5On`ueFey!@Zz*k4+SjRBg;Sjy<@kP3y7);8Tv#Ty zVLxdQo!p?Ck!y1e5^YLF6PCi5uV008SZn(sl0Hshujc*<4of6(<<&Mw9|oovPk z-((84)}3Xw@#u|plEgbS28e8q@6OpB-^DKmp_CFT<;uhR2WETRa#_O;2GGh>W-NMh z6cw3KZo&�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

R<$jQ)@Rb@XkbgL*DE6Fl_FAJ$m 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%0DZ4 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 z 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

%N$q2k)hr3^UJ7JSJ|#onE^9&r%k`zp%US=v-K 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 KpI?-`_v} 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%0AK&tx89M-*pY{se-jpfabt>voSI)#Bcf{71+ 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%0DH;WOiZg?HYJF?n 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

    SAN`t!yQe+7u{Qjd|?XYhfg}bS@V7 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

    _=(6e(~rMn2H>MPgA@v;JbJ{o2!P@H7C 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

    x@U$E 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

    Je6rqii-PP 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
    =he)LA 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%0A(%iG6MWz$%k3!%GjrT4S zPc@;dVDz-S*rd-%@~-kEO=RMbCZ#-1+Lx!#{Nr-kw0AhFegYb=b-F*G&DYauG0OZc zfVn=df_&%m`F6XJWPk2=Uw`_8Gs&T@*X3#je>Y{;7j>6K(QcOzg%W&`j>*HygPsgi zzE0=U-dG2Rvl*uwop?NspBpsfiGN(Q^D%ndFI}Fz9QKFVY<4cIYim8p>zFwy;pDRs-yhxM^eO$i^aoN1m=f4Stqf12 z;!_?y-R{!Yp43+AM%&6@hLd?JokeWQbFLxel*xHGiEQqWuaK>3l*p&a^K`m59*iz| zqbR^~lHsrMSzO2Wwgo@FeS6p9H1nOF$RGldbJ(`MXGUW(EP~*CcH}x5_V?S}QZT!p z2K{c9B(;A&GjfsXI$aH4Ulxy3*8#``khih35j4e+E(1i=w{5d{h?DmEwJ6_I-fkr! z(H1#AGbF4Gg+3myudhlzWaoJUJA;B*0U^_W1>A7*mccFfRL6GG4K>e&nU*dJpLYHLwUU+GhnZLL6(Z+6O$s{@(SI0wc_rH z8MJTmEIU(o(0uLo9NAr@e~bs2g*u#0?&HDz@P3%Ja~h>Dl-7S($JeS~bmHK&W6Q8d z8PHlTmeRj3U%s67GPFGIwsS;;!?zr26zH>00^`hzG`G&-}C4o8B^LY&%K zE|+;xbw0mjMcTYLODp`B?q?2h=5=7{pqXxeTRdl8KEb;`>~GhsV&vlU$~Fdnec$QV z-+W#5j+@858&77R$7_({>694-WwJOr@n`+=LN{W|L(#f}Ld)FKro5*ff4*8SGkIdz z=Sgmt8%M-IzLWKQF`A6OZ$AI}*MHzN{`OCQB&_q{__|tagpU3C>$mcTTpq17lYARL zleV+4N%Z+}*sYc;Y9nXRk^0;pZPbYt>GiysQVZ~-yGrO_mf6S0LBc2 zizP3Hj3AOnMkN^?#PH`|{<1&rURKL5Z?6*3<$QKIAKU3<^YIZ+`^cINWgIggMqBIk zS`+(y7H|hF5D@Y8dOE^>^(Vs&S>kYX&I>ktCN@tG$CI4@*Y6)x?+-u!G%pjV5-Z0e zJ?V2U_q_1KVwzDVIzu3?=@AYRef1mhewkpB_hht~0n||*MBs+ju@2hnU(fyP5g-2i zZGX58fBxg&tY(v5ci?4SUfzC}OGZyKz@Pk>``1Tp>7$x!d9BziEi@! z+cyUsH2rkomYW3kdjJ3a{rBDJ#&0Y}kDp)Kd{p_FkpfvT_d^7k&QzvU+4v&$gG8w1 zF^F}_>qTnM!*)|0VEv3{&;NKsV4v7 zmtV9cJy^WF2;9Saw^}SoihExz7iIq7y}X*7rvxH1b)pqIMjRZGp5oGUGJSh}`+Wac zF6Wm!gqtP5(Rp_J99R|K>kBjL!Xb&>KDf`paLx z{QQ-5kRuCofI`F*0o^`vcZ=o6#|I5pAP%;?5~FKGwpcE90G?br?_|e|GErsfltTQ3 z&oMiuQ5GaXq5l{obrN|+x9M#D@%};OxN#m&o!X`CeCGAEFGwbfBC3)?x*Eo206t$u z1Vs{0%d8>y3#Mhh-1AgsGxDYT-R|Y(C4TUH;(~N1@$P)&cASEFNX;1@`A+4~m*5gat1&p|A~&bFByH+z*hgH#R~ zb6g5pu=3-we&rq~+ubf+g%dw>&PxZX%3F2)=kd7bezwOghGbspaIjh}`v39&?LVQu z)CDONo$AtK*$k}zdCmfhJn?KY2E0fV07}T8wUUq9IcqXP7vW}J`p#8bjX>ZfFN1?v zgHQ+dO47OUBrDD`6I^hYyqz0;|NNBeua@)0EGskcHQ0eL>_c7N?rQR4-|PBS=u!YR zdH}9ylm?SJXz8R)N01R>v{zrlpA2?4sRi#i-hCcizBR+e%Zdi>-rr}F76@)-by-Pz zy7~An*pBew8DHEk=AnPwv`t=d!*241c%MM_SdyI&sbKWy=V#_56n{iWC_G)ezFT@a zew+4(i{Y(*JB}tpSm1EIEt}?YJnADV1LZTBIE!*f44%EmbcTCWBrixn5e3BsI39ppQ_1Tw}==;ZMpXB@goKI%VD3Pp_c0Yu`kB{%80l2xAywMU2mH_18 z(PaAZ0j~8YvwQ^^fjK~w`A0W*uTF__~q;Yw}*t(IxKSjf|tC>XEIAMpy9%*Qb@43Sqk z`St79%_cD;7Ry1*7PKg@Y~3DsqNn%kc|6FXpKjE@;TS!fVWyTy9-t^K?zTJeH7}^; z<8E2+kA{;D&0&o;oxKkN3ZK`(%i`sh$s32u?F0`EhO_-a5Pd9UrqJg30I|m@JbLi; z`TCel+YF?X7eHAu2{PdF%5)ZY)6rcPD;(3hm({98^FZ2scv-*fTsU@_i+atw(c|g7 zUH#M_Px^y z37L+X3^?WWkBs;<35;xw`TP9=qj-D>O}&>(luARuXgkaLcWE(8 zq)T=1FVP+4_KZTi+CdbMAAP^(dvNE|WiXx|JN~ubw0Zqc7~@R%46Ngq2&M=>W6zjI z3gjNGC>R$eNIp{%VEWM?Wt|iJ+-(>*N;!l62YT?e|=fsZjbHf zj+NwV&7=OQtk{NHL~1DSS>#)OBiSGkREJ44`iL-&2-Ey7uj|p+AG%fMDdkm)XFu-* zZ}!_ucSeWwIgl<&3z}p_ICukUC8qE1-_?mg_3I8((m@>lA;Ge22ntz8Ig@=*b6!Kw z=cQc@j(@jZc0aF3(Fo8?ef&vbW4?S*%v?@86{(L{$tbEH8VC8QrE= ztXplmh*i`wS&h6?8pIdGFC{K;9a?v9c=EXRAHDwy{=a5T?bW3BIv2wpn`1IdO*30{ zYEeUKetmo6_CN9f{J2=YYCD6WSv6m-l>`r<);n~#C2vPS-UOC;ApIkN^XMf3I3E7B zfAt^HhhZky#i%m-V1Io3@)P-f|MeS)S;UtSG>RQz)ujRoMxq!~u^_L~X51kbQMyCb zCHd5oNRj)UBikLM=xv*2%Z|o{I~x0zx%KJa(O@Z&Xf6c zbJ!iu$Junc%7i-b*j&Gqy`<7zDN}p}&Nl zk;*ASoTSd~b;$+yVi%#b(^-E6rjLYUw-;bJBCjuBzr4S{%RNuW0Ad8_qdntQ0|cj+y*>SK(^tr**<#GWk5Cm0ZO=W4mGo#QQ6gsJofi&g4ms2Np zf!bMeS6>xyBZ#OK>dM$F3X*;V+3$DJxUvE=Q!itD!2s%qI`D}j`ts!qsr7D;?r?Ow zOPGTU-osu;wwt_`Ejd*!ZLc<>n6Py?9*~Cuh`A`z6RLSULA_^gJwxnS9C|0y7*A(^{`1)~n^_^Bo@(Lkl`-r=!Du$9_1A-HzwIl$1Q)Z4hU; zI_yu)glEapSE!`~NM!^2?x16bbAsUDdi!ZH89h%xHve~i_GZ2NdAq?k_2Apv+Z#n8 zT~U00Jd9?U%f%I_M;~=i2pl&_bsGOZWAKm=GRh1ZOz1NYsPnJKZ98A|ZvADuxSSGb zfBp3@-TnB-zx{c$`F!58SZvcoUTWZ{;6!>Rd&$>(?x!XznFK1~3E{|Nig)0smPV;bu+P3MZ z{wPlsyXyn+8V&ki-(K(?)0VV5--ISQR6DXL=Q$bv`ZqJzv5Mvxdumw=+I zGM^{%+7T`@tVLL55I0MjYDLY=hcDB#EF$I{F$FFqZIi9`da_PFE#FTlYo$?uO9Y*` zmFWlfK^c&P*ps1^c#{>VIPS%Zl3CI6chTpTlAFaf;j;S#iOjr;a{3uX8o{aQ9_kUZ z--qpab8S5NECXgR>1$pC;4z|0{7w@1-!gxKKXE#z^L#PSeGzDIs{bT&@=Nq9tUk_~ zz0*tw^q-bo&gb=P%%Ec0nXNPjAi$R%SoX~D7;bP%?$QN*AGM%EKHzuHa5Zhz3Q_WY z3{XjUWp{vvgP6rj#2~5q9Mp1h6n?r`2a4UV&GWn*KZ)q9f&n2^jyh8c)Kk6vh60mnK*uauq)%Da_i zqOw_yodTct?k6h*Y)b-;~g^L?oJG3m=`dDoJ#o93(9~nt?;dKW*l2B zS9E}6@h?itr&IB3mQQM$QOQ^Hea4J|bFF#X@+^z$PJ$7yivV=;fsZnStQ(J}_iIp{ z4e`1lhhc`AxMs!rh=+6v5^ymdfE@LgctFM8d4Jvhx{w#OtM1v{dSfO%lFZ$&u&{rW zuR|*AniA?}0>mZj?`eqObd4S&mNU_vIE_h`>U6B3bid?R?h=}t`vr-A-Cla*CGpHh zz1iq7pSOAIFK@QNPw{*44C4_;s%Ezf4abv2UG(I#vRLY55{QDx)9EPq;#hTw#7&Oj zi_kM_*6TM?+`Rv~oMnAOSXA1U*}c8I%`%-XkjGO|X_Z8oVa9%6tRy%Vk-4H-Jt;{k zjuESl#HHNtG4t-sNzKZ84%mw6z#F+|JWk=`2HEawozl{inbFpZ<&0AKw0@f9qfWZ~YJc z%}wv{=l4JNM$hx@y8iM?&7OG|94*LX2zvmXsmdNWv||sxPz#TA*hbP9><5C@J4!(dkP;n=DC^nE|XkF*rduIEi(* zvM!&hpcMRM9ZE(gc_DymR7FAqR?u2a2;XkRe5^o>tzP8@IYd&}Y`4m2R~wU&@L} zWvmmI^ovd5u!um>g%aIi-RJ@R)q{AbV=->S{9!-O;L2vpRS$>&9ThAOH9VQb?-M#CWt&^$oi7HRXs>4Nx|fJhR$+I``}_CILGKOO^6Pq;z%cIZHlM4vpNG>0 z2JUyCZ?A8B&G+|rvR;%$^rDJcNo|e++ENdFzzzLGpWZ(!RXDJg9Pa5MD3=-e5t@VK!gMd|Y8ZtJRWFC`L3Zy84_>+QaUEnOa4XhPZ5g z_NT8uX}4XxUAoix_;EPy2F>&_T>RzR`=}ZG^mRGmth#)*hWp^rGD=rArfN$Y-I?#u zo$6xBfdG8{)1UrWh74l}gsX!!Xt8S@7w3kPyoP(x>_Om6X3~r%i{+X(kR#^8{=Rl1VS`pWFNMF>6LI`Q%(iA{nfnyDR8B!x8xeE8@8h9Ka~lM8~AgnUI=E!|JD` zZqM*X#sc%4OY1)A<;E!mi zX_peBCNPTVE@RBIi{tS^1Ogs-Te~w$!E&To-yZ<>>{f!>UE(i?!~?+>YY~+*kI!MTn!ljxNty^ z2jnV-XXGMp1wmJNTy{S0Z|6=c%o(-n!5mIf*o*PHIUfb9QzDal;Da+_`MBi|kHhDE zGU!7V^S7^i88U7RhndlMzs1)K`X%|y=3e%j7Bkd0)|NO`3UvFukYDf+Sszi{!7eA<%IyuQAjvI5XOs|G*C^xn@ueffBQ&p;6OnyKf* z_wVmeBpjf1?P{?tpMsW^CT;1?U%!5p2C5Ty)l*IA&Xb$GKk^U)*$+avOy~0(kY7HC zCNk|dyI_}bkSLGZA3Or`d$(a(FjV8t<1x)*#nen834pwWb}~J=obzO@gJ@8kB=Tjo z+wpezcv-FKo*Vq)w0R*s!6+72hW_HOV1E`^ zm7E8Q&S%PER?z{ohP|t>^KrzxwmY3B&+B{$M9%KR(YKGow9V5`dd}bG_O4?!Y9|iT z8xE)VNnygVfo!#>#hRwJ(+R~G2_xm%X$BC6<8ub1Ccd z^K;9Tn2-(%NJS`l?#*9dm&{B8z?N03k1X0j+-6xX`zSH%_uKL0l9z4ny>^mWiG0a3 z@A4c@o1%MpubOEBVL@MI$;jEu`n}Ap!W?((%M3{zQ`i=)`0UM~UJ1XT-(6+1*X{Yb zJN>i&%m3^6AN?{}wSW8HO9F6hAKUA81zp^(hwh}6DT9>}PFw0l)=lQ|=Tl{5lm#IB zS;-x!po$n>xEcg3vjnH`mR1C0Uh>|yy;KfzDB867w^z;Kuru*gN!4VS0rW*VlR{vAL1I~0lP%Ld)`SK%10=V6 zBQ=lcTtWsqAu$9|tBUp6i@B$t!d!<-pjNk$p} zoC(spfO4t`Afe~kPri+8F@-zemQl;BbPC4?`P9G^=D{3p4b6DTwZ>x)M{kQYSCggg zA2-Cs?EC(Fkc-{Q^lb<2V%<#Vd>Y}j z%hm3D`L@|#f_E0LtJjqZW4{*9-?);aJk9+^QSj?m>BFTI-x}5ie$9*+# zZ^WV;S|Ff;k|@^TCwaJn)-$ zPcgLrSkA_i5r55hCjv=i^cX)m_xgl4j<&ow$ckWrp|l|b`K#lu8$5@T@g#4m<~8=- zV?;)IlWlw2ji+Ob?QZAC@VPtgkKLVTX~)C)WZ1u*nju+soX+EZLF52kK%&1=N(yw- zHjYO8emADPSi&sq-Y(~6)OX{{a*mt*=XI8Kj<1W^^nUHQ_Plr4M`L$B>~L%jBWG|+ z`gmyp)kjwNWu&u7!qVJFF3w3B(u`Q9_gIS|9HEoO~|9PWA! zstxX$9D-Z7l1;)&_dd@FS~*4)x-&+b(BQ?h93zxtvdX_ltu?OBo(eumH=q4WHME)a@?M$|vC%{P%C) z0Jh0w>~+v)XuoaNK&awDybi0aA37iBZ@Nk2?2EHBLsN8Jzigyx<8*P zcpvxkbu&sg84td^EKnH{$VDjXco^+^NXCRI@U^e6SWO}dYnS?I%~ddIF`4N&4S`cz zc0I@g0sd5W`MR7fr(;A0V+Yn;iN`uzu~6#^RK#(9>A zX>US0&JO#S-+KVrBR+O_x~RR3CdqJq`tp)hnF|kJRtw6OVMQHjG6zx| zE6T$Usbko-6O^x(^DofM;Q76 z%w&LjefVWM9<20X)ORmRG%G=Nl488B}@rte#^ylxtMD( z->ofAx5a3R@`sN84i7P<{=)A$l@e&1VRsGgLy``G(sh z#_QhYtaUND^m4YEHt^WuWqp3!H@ht&*Ryurj@R=EpZ*m;C!v-emO7_yPxd$(BEz12 z;m+%7!DVq}xJ|eI^z{ob`}K9DkFmJ^16N;O7O%?%JL2g0Lzrfcre@4cXp9cOtQWk~ z+j>brshnG$Hg5L^(aeX##c24N2?v8kGk%%2tI6c+a``%&WwtVgklmuyHTy5GFX;T6 zKYVqPFOl=(^G~nKmqoh}9l6cX00`w3*QL3a)%=e?f2D+PYcX}XR1cmMV&;=E27>(S zMLX&}NO?VP@!>^7WzBCTqd~IVmlp}dY%VS@m7!6pT`i}?^SYj+YC*Ai7Dxs9XvS`2 zKETl{8V0(YrEb)zzjT}MV#>5Gi@J|+m%2jUZ;SSYr)X!#%l+~gtXAvQtogc@kkaId zrXx-LTFl(;n0XRnGal*=58!0J_m{$rVlV6E+hXyD`TQp-`u(w+X4HOp20^Jot;B>;X&t#M5WW z;~U@3OGsN~ESQ#ex+Jy|&90*`Tou&UP+V1;&KcZ&>#N-wvBR)&W7a6+(RAI1T{hHkKe#w_NfsPEW2c19!LK};wQdt!g zuk-~=dBV(w801+fZn#&FwNJBA)JNWxDr@^*&L?LOnQ0^8hmb zd^Y^|{_($?v31#2om`d0=~Jt}EI|lCgH1Y_hnqn1T^}ff-pYgJH*%K@c#JYfMH_V1 z)e^u0-K4hUT86vQ->kuNxi&*dNQ_^L%wE0nf4)p+u%KiF zCa1)BG%i!^U7xlYfSfR;3>)Wl*q7HeZ3cY_fDsZ59V&yeis4W=?k`Ob2WM$tZ+PhL zzkKe9eLmx{@1k9X2>OG4hDbRRER|4ZdIYKCuo^Le{sq^_4>I7@=K_z1J!UVn&|zl3 zlxJi_lv$o2keCYCK;-AU7#oB%8H3T6K)GjLjioSoAx{jZ5Sjc#*RL5SnFxyI5 zb;ggb9o*zhlCtyd9+NI3$I*Pg?1s9y0BL{53kv}l7?~2D{Hm9Qy_{n3ZH6?7GNg7{ zReCy`FVFcV_^mvJpy~l|;w8Qm-;_~Z_nuD2%jG1m5$dAGRP27^(U6N^WN;M1RXi^s zG90W|OZtx~nUUWe#3jLP+)TE+)3LkWGqU{%Ob=|Dh|uK&)p3`ZdvRxLM-jA)6ae(m z7RB_d!e50MIQbU=wRvKjK@=@#?xSLvQ<67aukzkz3fc%mk8$tT3~qGtdb?>xr;7NZ zfyjrC$_JFNloJsL&N`d_M9qH5?r0qfYE6&Zi0jP`*l-$D`F^LGFpBZhxkm zHIB;S(HB3EU{<~d9JR-^Z^yMKKet$eS+a0#Py{!KBr#D&G;BmcOPWlfRUAhx)>8vieOJD5qhWFAo z=?NtAd~P`$NKSeIqcbRXzpU2_%s-wIerzC?%;o+4b1=>-gFn5#d|53d>Pyfqf0^Vc zuN2|2UFE#dYB66!ngO_p-}W2@S@YF645_Lg4+s=wrZCX)GKp5!7&%>Tr|z<9XX|Mz ztC6yw5Bp>t;uOVZ*b(0y!obE_e;i<0+&vi!Js9xDS$-J)D{vua;+mwy%Y_^O^2l-p z86J8Fb$|HdpQzAc(nvOV0q$Dm;amBj9{0s{!vW$hfHh};#Rg{p^pC^_^q)Zdf)QW6 zn$41jJ??L-b>i!#dtEMX=e)y8JeUM2gVxOuqoSn(mOVhcUbbIexXWpI>vV>Zl5*1FRS@t zG7|XHrRQ_8n9z9`J)<7Ihcv@+AagVH_x8GK#yl0Ck70kIkbFaYn{-H3F#V&~|J)z1 zk3N=7htJnp2nJ7~e2S)8X;?<+PKPfqt3P~wlXd$^^Jwr-Z!c<)vk9nVsqdPWT8yQU zx-U^#$cvJ>V$ni+F`e!XSu+1mKmYW%UteD3G2!pyZ~pj44ate+IcNtVbqWFg$c0u@ z5?veh^9&Ff_=)sCeEp)ptQmfNS$%nVA#F()DN`7gEGCmoDt`9<^v6H`&7c0Dop36NPb-}XyYG$`pWq${sOsAlN2eFfr)lX+kaZF zr4y2*elxlVwd3*Yd``aO;T;2)$+imRFC`V6&n~AEB6a#z60)4bVaXPH4;kPx(32%U z`J(wGX*q-^ZD2&BXMZ+o#@%!Haht8?|LTAHU;p{oL5m28(BZY)DU%K zq^}s&3|EWUPj7F$!`GMfX}6Wa=^)ujl}rBLXj5CSi)G8t_xtl^`np=Nk#^Lm=w&`# zP92OG7Xgv7tRA)UQZ)Ks4J?I;5ffjlzlj9{$=l40_wG3EKQbNx+)qXnB|hSL<8xH? zyq&Ri9{yx#I=aaYpQ7le)mAYj!J)KJ!d+3plHCKhTquyy#jp74kVufQ}`!ZRgFs?PL zwGN*|JnG%?T5rqux$iplymp2A9Px!936&JZQFmt)kWSq%l030v*v~VDta%D;fMC<9 zM0pkM8Z^@e(thNvN&XxV%-mcK5O~A2=dIIO4jq3TP+7;Y8su`lK45Wywd51f1wbEM z9(3LtYGK>mHr-Va`}wE0SxNLo%=4+^taKesh`*i=s*UH2)yb85y`c2_Y&qL!<%;Q~ z9m4~I@w1uq!Haf0eO~Uk%J}3}m^`0KFUFVib<;fu<8eF)sN9n&J~Df?tXT!jKQc+F ze5(CP*laVC798;P!8*O`6psc^26jE|*#niJ(3DtbLG)g4xGZBrw_aXs+8wWx>FVvv z+iCaC6Xo&tAm3%|%H)tB`N43$n2A;O+3@=HrL2BcutDaDGS@ssAr0|Gx0T04xVpfF zJ`e*f-ugp4%wlY*w8u9ATd!ET|B$Jf9p&F#4lLK6HV?@{ULnImlk1h2do5Zj1&faLXH_ zGHM*=)3i)vWFadiMBoJd{xY*qUWbe;D4C%v+)3hlJtYs<%l=r*)2o7YWORLT0^8A9 zo;>o}var1UrZqI!YVRaDb&o<~Y08I?aC=%7d`P}qHUx*cCI7wmhwr-s9?oVw$MbgD z-MYQXpS_4)wKOpG*GHsL6vMlmXnMO`f^U$MMfvfQ>k8=>m_qbB3GVRY63B^E&asR+x@oLZV{sr zNxmQuUJkNQ5;zZM?K6>GTGQhR!D0$2lLJec&m>F~~<$U=Jeywtp2u3*4h zmr1TNeI4Xdn&Fs3=uRC$D4AIz0q1>LNBMC5Kyy+r&?MPuzb6mp?KvBr$~RB$&x}|% zgX?h%8}oa~zVcE^MtSJAei2C0c&y+yvSb{;swUYYEx!7b1Bl0Nlzy-XT1j;Yon{V& zgHgNRojy04)hc>O;nxCH9`9r@QY{G0yMpmH8jUi|=X2hG5INUdX7uJ&hm2}y|HljH zz2Z7D#~m%`qxWb(-frhd?{vA$#*=lMm(DcjUOGwQ>Zg5fQAu&QoOV)EB+?~{t+N{7 zg6KD9>ds`Gr$@9#chXk*F7o3drycPRr~3^q&luLUS|sK!=|Wv8MxH}HF3Fo7KQyOQ#ePXS574wl`u;%xE#U4rJ%~(9kjKENR2Dnw z{^7744tokDvcOqqvKpQ^`O!p9GacO4t$Omt>g9Y)nwrsT{nNrlf)E{h+z3?cc8K9o zy7FL6S)Dmtj#o}`@Iaz;_UYWPcX?)cB|Q{M4w)Hr=iBvb{J6+5b$LEtFx*=*URmEr zJ1}}I<$QAAqHS4I0B7{zA(=sn;^ug@U$9kS;?<6jf=CJ_f_?$+W>74o8ZtZ9&6L^uTj&xzd>F^oYxy)w)r+Oj# zo#E>M`gz%h@#T+S|FwVfUw`}Cx5x0-%5{5B;Fc$puR#rY3?)4-PZBuE*>o&T z()7U^%66S%eW);4`05BI=9n#IvTyq=6s_p=XE(JM(HiUS%CZf zZMiHsupsF?kpuKs*rGf&$gFkPx*30ceF6HFm!7$?){}=YdDhpL2FNznz*7Uz_FOH{ zfA~dilCglkbPJA39$SF8dgL4O+ur@E%zQFTf|mp)*&uM}r=;{X54ebUpn%F(a#Kp) zyTkOOo|JO&r3`7%7PYNmk?F*lVa9PDKfOYid4woSk#$Iuq!UsIkW9q-vRH9tkr8Co zjQ{ZRx`qz1bRt)rPRGe%Gy=1Qyy;lq^~k}pO`0JOku8u4(J!)2Y(G;f{_+3vf03sW zB@&BmSwps+v;6D5cgd_2XtG%WtNlKOitDGZWiB;W=4u&JEDIL^M(1;U?<2!e=MK1^ z@}U?BX>z0VlC!4A;7mX>kte_mfx>flJzZ|A^{ezd(^N8!lW*fycu`()(Bg;-PT;u# zO<6x-8n-?}Mjn1;^fqsZ3mEOByoUfTl+jsT%ks@l?{Mf=%dfwD|2$pK0tGNV84M=9 z5ic+8)KGzInTIAw!2jhjOMovc6H!j*j*oRv&j25KTHNkvGL=qbn(tznbrt-nDgdyV z{=@tBn(0|h-T{_|W`UaK#xDg1mA9JO*)4O5`b1OkRK5d;{yahJkFS?Z-xM?YJQ#%D zoiko8P-N;@-qWf;_K~@lgVXi;eY58aNI$kMKC7y%EUR#W@nA&k09LvHilJ(`)J@$} zLB2m&7A+~08_6g52=iGrgf<#a&({kiO_unvFQ3?)OlIq4+q)fjk4R*aWnKd8N*vAj z#B7%2BMK%b$?QF?fI_6kj^alj_aXbBRKo$BCXoqnxXBZ^rYu%}d*(YSYJ&p!N@WW& zLt#`cz$;MjfgzDy5u(mcfw0UA%rrPPLzDcwXpFRaYteVdc{O=5Z4p?|_H~m56XiS- z*Z#Z_+%jXb%eQ8sIs{{OLCWJXCGithC}~JuAl4UHmJ+(;yiytsx*^2-< z)h}t~MR?5zM+O7PmEY&}_&3af{BC0RLz+M;;x2TF1YyN;a zj?sKP9P{?4NCSx9@}=uIrT*Y_pj|Uep9#S$N22IeYINCy{Pjr5t~4y zD#z1pcQ|v%!sm3+h)xZ% zRvVHCO5UrdurIH1@9`U1wjaL)vp(2-$pqQ%mUnOPjF|Y+BJ{j03*CGICCgTpJIJhqEYC_(D9UPs@mRe(w3;lUz*fKGJAk`W77*97 zFg|gC*GiVi!m&7cM@_FES^7}gckFJOldOxr@Dx>0DX_)MsQ!7oo(?-HDm)~xJudk? zmGcV5EnYh*qa@^-i}o=p*b9-1YRWsa_1qY9LG4Epce9XAC4WliMmw_voAWRmLn zbddgWCUQC4B}cia8nnX(@)%Z@a6ZDL`g%K`k_X(ceO-cB?s@D3Frp{T-ES^k&gbLh z8sJ1K6kdjwh0X*ufj9#vNn_|SV>lln$m3zhXNHF*1(k1IP6y}VcmP-Glj)%Ow&FDo zL()}N_Eb-IJM9k}zG69=0ELh_GFXdxcwxM{cYGZs*|%_c-b_%{RR)M;#Z;BYRhes^ z6z)Yx%H&cow;ewFZPa4;`RBj>`hWk=A<);qeSQ7Y%Vfc1nNM%lW^&Pltv8KIGPeJ? zwiD|2%uK=lo%~u(l&MqcNe1Z^xL?k*X_KX~^Dwg~SOA&&PFAuGlEp*Zh9q$gfTUtv z9(OHCq*E{Jbz)@JJK73H(k(e*te@=5gIr0-0swhlxLZcaFFI((P-v7{uPF?&my6bo zvp{)?10$W>Z=H;!EPx}WAKkB~-D{($@g{o zgPBQ(Ue_!7?Z!NFdr8uwjA}LEs>)X&3M}!wbL%n+y`w)AJx}{GrK>j~+5i3j_CFv) zRvnDi@YKDmqBoj+|Jd!it9ZYf%`*q$p_$%hyUQzk1#hulP@ZiC@aFpfg07~^huJ0> zD-x5GW!uTe%4jXG$Gc4?vhB>v4+i9>N2&0ix>GAbyL0`N44{YM7}1- zgc5W)Z=eJXYCRrL|9__bZC949+Y`I;e9jzW%(>S7_TE)xSE-ztMFDjnpV zbC62XT&r_{!nzq{o6F_7o#=Qv73NC@sx+Cql;m+CKIoigf#!REd&onY2an71ob}ep zjD(X~WlcgD6#MWZ=V8rWkXzz12?aFdrrh%dbj_4aJ(+H`T52!HJ1_4Ylf#lxc<|sO zXlefQ+v^O?(8&}FNBW_GCcPg}BXjWCAwg-W$j(jcWQA-}LDfv*0bu3To=a4s1}?uo za+f3rj2Ow7nclDSQLy0oe1h#{2p!6B(l+7&#B~fde#_{?W6~t$q?@Pl!}Ei%LX&&9r)6+RDI;n=CjO1nNa>?ZzW~ z7$GZy9MOz1=&&Vq07xu%TxP;4gP|+A2*CRO{>i8WO$B_r#H_%hf>;GC?vGXrONpH1 znpFW3*Pc*A68vzq>(1Y|TLtq{wVGseMGVR#4IzOu z$^}{d1wBsDtwV#RARGpub7bSaw`Et4<;+hK zIoeaTI@d-%@l9kDuCmWCQWHWqRSz&UwrWHg;!Vq?S}S58+yeYnJKFk#ytg4seO9Rz za(RHJGC!;zN-s6%jSWo^AhJQR8SrgWcWaS1WlkH)Oq(LICWiB+#x-ajH3MRv&Uu2+ z-+q0Yq#|JR>QcO9%@>Uj9nq76$h6GU<#MefFKto1ES5e?s<}*}w?fh>Cl|SuoL|Qi zPI=5u59~U0*Zt|T+PtR9x|2p81D`6Z$~qdZXG?$=U}A7K^2*xvk$XL1 zTz;prjaS9>o{4{98&I3+`p}&Sxd$65TcIni{Tw(5j&0_dWj6F8HW_{*EG>`(4J0ZoXe3}$P$R);!4svzqmXNz%E@cH?;Y9ou z?$3o2ePRJy8G^4}>-PHIAZaMG`(z^v!yY=0e~CRrN5G;za;8Fb;*U_Aba}l!7E@Jd z)<7m5_f_>XrzR4n=n{oaK<>HVZ^VTJ@HDFyfmh#p>(!d=MQFL)`ePH^@_vI<2e-uB z`~BDNpNHdFv|Pe&PXIU9M=R9lhN2jF;5c4WdZmo1d2vTIpe%47cgF+U>n%Ze_*`bZ zkyR}!R?lC~(kObAYTxs&{~HKspyBf($c2|=a9wk?Eciy_F7Fn&zr1W1K$a`U;&Q2q zl`ub6=ljcMO?NR54AwK~z_RGQ-|zBdzw5Ve->*G5XADj{C6_O9{q4kr?9ihVr7Aqe zPtm7PPBCo!OlmlVh=5?&fdt6eabL6O1@yb#mvw)a_}X1gAD%V^cFaEV! z{^x)8x37QyWwx2+#g+Fg=|ojhFz`AaDP@$Ep|(2^J0IsQ(|QOVQ{g5k(SEnxN)5a_ z@oJdI{@}=`KqWwtj}VqCz=NkG0;;eFB^eXwP}|QEc2NQ!-+%vA5w*%46tR9Kgd9Ma zI2D|TjgV)vN!J}v8G!@hN}YO-SPXua=N)lS_{aHjH=(d%ou;10RbC1ElY!Zcn@ zz~@O)f#z949j*ni(;*K+PWTr3OA`SVSJ+hZJfa)p`OM@>o=wpm4{RYatS7O+#ExeI ze>hBem*<&WWF1=MjVX(>0yU3}#SZ=2T1qB)q@suD5l>})chE-e1WtE?1}SMw%T4uC z@$t~@cX=%ZAuwBMzA4a=FwZM0fDt2eOg61Y{*#;G5j;a=L_JvMwbjGJ z4#%t1BQC;Iw93q5=q=kh~m?ZB#veXo70V6(|byW znXjUoP|HvqZ)8U%BT00etUL!w@el1Ok9gyzlXxO{T*3AXlP> zxh0lN>;3^g#X|g1iLTIQi$~Q2t<0@AUdzLr5rkNeyo7`tM;PJn_Xo!6s}lpl1`6XT z@^tMil$SenNniAO%`GL+oHXJO|NAX#$DL$T%aEdx;6I}a%M6b$+jCc5UtdYa#sCa$ za6yV2`ZJaWj@OgJLL@UMC5;&wV7 z5}ae(p~0Z#&^f!f%jK0^$4U8`+Q3fqBb3Es0wx#|X0g}MQuv%t^^^#X&8R7QryAam z&eOG%^ata?Y?-x@v&BrrA)d)3IXW{DxyL(X8$C#7JmG|lf$=+%N~xbmTu88qoTEX7 z&{^{7XtG$WR5$Y2_wS$b=X5e3jV6-$IZjIZd7exb&2|^@KAsxS4M*$MaA7WX7Duhq^JLwra`mv!ht0exb}rkxU-zljXj=5&^dxaIONr zkF%(`H_GJ2m>%hciX+))kLC1I?2vm9EwoJ3KH*oR=+i(-0)6t+&1TJ~I3HpX-S1DC z@o>z|lNHYdq8avyHzB!N#5`5H&ptB{(gntffr)!IUmLYAFE7|xuQx&{h6{T2Scac8 zKt`RY(>OuFE{1pQ^<|%@Z&g=vp?u;Z*;C2A%2P*?jny**+kf&p#S?4mPe%7a?|3~6 z){(;FA>ZZ1XAg`$9uHv?m6^Dt<#ebk=>Sy>JIUS<2~D~4jS`s(qYmoG#k!G$nfmB6 z26!gSI+@%sDCo_X%d=wP0+e2Sa?azq+jo1mI$4O%m)rfw1H>d5UR;;cl!V8_`}3g& zyJxNW;Ca13%wXGD#G21Wc?3g0b@gO4olTGd1Wt{idj{~hMQHuuqzl}9PUg#dy{tX2 zVU{b0d5{s;W1_b++vtlDGEz!_so4DTpBEMBbfRJ= zXVBzyzvgPfVsyCe&#L1`_tAY^dgt5VYX3e|PuxzAuKQp8FaGB=AY?#h{Ed_3(egnG18IS#UsntGqAu5F=8PFpxX28= zHmg0<(_52z3ogeKGI^gxAj|mzoTYyGHQ=}=dGfrZZ0a+flV<^OYLPQ@Pz++0JJ@(H zm{|R%vI$VGhgt}utJRWsip#7={z~i~<+12U(wpEB*b%@-NdfnaQQ*ui@%I74<1*iO zAO7V>i>#>!R_^Q>{rZvlKruVH1>$sCuGfe&x_v((ui%zG*OXI87r0h-l{;a~_!g)E`)FgMII&T?tELiuK+78*XTS=UhsDHstMq=XC93O{m~ zabrA?*?98#@%gfO$?e^RO9(Kg5U?0vp;KGwBA4<8qcP|F{;@rs<&0ULy~wA}r@YRv z%5H=SF)c$>uBx^8f%J4VmFz2Z-R-kUN(^=Mon=&6kf0fqKdeKc5_3dT(ltVJ3nFUuo1#%*83Igu=rV)*z zPYKcv_vdBh8G6q1?D1|aM6!G)%KasH*3R>`AMcW)6!gA3_WHSsJs)NXX&_WV0VC2o z85u=CRe{v%6!NRpEc`;ZW$7s_IYV5yHg*UJ#|RT`1)<(_u{gpDnT=Ig-d`5EPEGGj zRNc*B8Fsm3Vwz9XvJC{!7qD+^lzd2>KWHf;#d6poyjbBL8BCRj=1S3ImSXmNrn@h1 zuY=r2F}$Q0m_K5-x#9%!lVoI71dGGuEF<7O&|9^c8mgv+s+#hjHpvb%;VuuzVlwh~ z9`n*0b=^s{651BLtI|QPR{!C)e5%sZ@RBDOvOUhlz2SbV*amW2E%<`qFh`W(sv&$i-!=xg00Q zQER)_(I#J#9da!<*Dkta?s`Ae)d6+^21*NjPsfQn6?srPo+!NIu)v{!WE6ZNXfhiM zB&D6)$bn=wpq8K@N_(s<
    y00(1Z1Or;gU04&K3`$v!Yw+>$F&zzN(=m_UpA={HuC&Q;IgXvT z2h9aDPT9jga6C66^D|t zl0Fk;&ZB<5!wf|y|F$r~;FEFc_1F>MP~IO+rLcRRQHv8X(0@+POR}O0UFDIvI+A;b z^(d4|h4=YvvfF)NMcI#NHG3XebnawrgUZhpCr5BUs&>1)N<0Sn$Xfzv6Sy4Z^@}oE z3A92Bsi3Nt^@re!$N_I$%Ib4sSgXr=QO`It`t&V8J?b$h9M(4y%R=u-}Kv z1qlM#KsUU6C|PFCJbu*|iHtjY~(8tES5_>6Y+Yz4%Y04TA3qAF+o1p z`;f|(XY)n;DDQ!rH~*&+B62X*?4$pAOg)>(KJLf$@7K-C3x4UmP00g@oAo*}6ue;S z$3uma6^*$Ro^0gvmiK-$eHlp3s4Gf6b`!Y96xtd9j(xgZ^K#TwUs+e5xeMv}WqmyE zyHn!0fXG5HZWP?BG z+)jB&Gpe8s^4C?!&c)qK8s!>Wo{*Ed%~&GcwDm}YB^I|UK0=o!9%ZoNNgtY@3G$>G9gWUOn)G`W=#Os`1>0`b0pJa0Of>XRRP!q$mV440h^VV*$!+{&-ID zT-T{dIvPq8&r%|1fjhA@?Tx@JNwpGo(DUsKzT_<&{@5o$7>kOC@q9jxCJD_I*{2e@ z7%bW!x-O;ohZ3a%tJk^_1I zp=z?KonG-QXh|)|y0^!R0t=jcUCl=U!4rrUQxhG?(lW`jl|vQ+Gsm z-bqr#U&Yj1YR)qUKi=~$4tOyl4E|%w{nK%GhZV+?%e{ZTCQ|t^s}ZiJX$cLHG3>gU z)FwEn+t5ODMZ#2Bx}#G$MKt4>tQAK&a>`S{{fcFl_U+s6A0ON4EU$<@^45CiG1S!) z9YLAAE+7wIVraAw`xoxeS*IJ(!7}nx>;-6YF+`~o2c+!YfK_+hK^*|BkPP1xOjJPc z9PE0{4cnr8o{6BwOIPUEbHGIGE0a5hLokMXg{%0)^&U%egrOw8W-c>j9F-b^IkmY4 z@SVy;s*let5eh1kp+X4RDB+D4%1Gm?|Ctu65HFb%8kowaE~B+)nFo^iXOu-yOxwPA zLKM&zwVu>SQ-~_B9PZ#0nqzju=V}5fV7J|Z6)Q?ksQ_Xu+DAQg2qqf%$!0`f(U2(H zALiXYb=9t79FB331kac0f9F)i8xSdSoa$UiX<2s_-}98lTyRDVThbZgK}|T*EWdS~%De?YpH(lNOA^7X;zaKb zrVHXD1!~G~(T{dAi8h%j)Vs?#cx;i(rFEvl)-s-zd!db zy-X(LN>nT`o_pWzK4-JZ_VZIbVoL~^N^rG^YrFdtfcN`7Ykzrp<0!{thiMIPFRjOr zvs&c3TCPqh-LrZ~a+2`yRBqe%Yg|-qHk~p9Sy*FVDcwHZusLJJUuvEdY=c58r+|d) z>Z-wEzvuY57npZQnS5tBICn?GzvOwHJwf^IzbqffHRo_Jo~RmiC?PRN4ynSgi9YOu zu>;g|E}=}h&Ov4gYeR@oWR#lNb6qx9m&hu4A_e&`=Zhb%5h`*cU*=0HkL5D^?KX=> zrn}Eipj-`>bfb16EXgs5F`ihJ0!PkMnDio$UrrcN&}7XL@(9e={qFsC?fBnlm{~Rk zFxG_e8P4f;7%^^8OJ_1=S<#X8@G4YEXg+51`6|~BRvey9GgBwFPwn-P43Sd%#~KVB zI<7+v7_k7MKgSakaydAb+O8b8|M9~nH(XDrZXfNqWJ(e?D!Qiz`9&3 z=zx7($U$8v0`hbQG@8;rX|rXW#;B0l>nAK`c09=2DNfWCF7S&6LnMMG=>I6w{ltHbCQw8`GfZM z5qZj6S2d#o2bh{Z5+GPd$$oq~AG279^=4&YS1XPp79$|OVIBNKM>ZppYLSUAYT z)k;6gM-H5RGB*?aTJJl{Dp+HLs^gKJHDxhe!I4MtB`whpdXtzeRhfPo6ES>HwQPN6 z{Sjs<6lJ=7t7&5gHIy1I-4$BQJ?{7Z2x4K6gUPTr%Ht4owKvO?j9B3#*M*6X0!We> zwVsKEE+nT=i1E7RH+X1N&#}JUnk8#jG~}Wd3P*+JD{2L5^B%vZjr5bapg0~S?=?)v zK7+S#bji#C<=36NBqp*jBf*>=^$?S*4fSnA$JvSFnI))%$`d4}?e0)k9G#L0`muvA zDwFjwMM6f_i8zoPR`@j&isfY}*8r*E-%n*)0xPP@fMX_pq5aql1>dut?Od0eF=9t} zZIOtj`egC`^yNcK-8SJERR1xgysjuKtcbJ-80>aC`^MEnb8!29K``4Ps_kxToYJ21 zZy~rFQQ{8*xX?(ht&6aPnLaar-#}zmi-qw&HUW-uiP}$&%VZtA#U)O;-lg_#LU68s z5P%ddg)Q22P!G)p>^NS|mA8EfL3Y};b(@x0@GG@yM#&D6fD_TvE^v`R1=|?m1Pm2A z@)Dn{4$mA>E&}A1V#QKpQ+p;SSmV7tOa=`)M32sOs4GWs$3qFKUU5AXaQupx%tTcuUu_!b(5AhCBKf*aSvXDXN+Dl9@f zi41rlu1Mj@82xFi0ng1}dKO!5#7HGD#FFV(vQS@)$Sw=W#(y^{e<<;7w%=C`+DL~OM3(Ybe|9pLW zo94v`nRFmGECe-ACP`!E?8uu>C+Fk7{58}xU9=t4mIeY9=!F!-5xQU%;!Iu4?m6fP zfpQk9gi9FOagp2*)FjlS3BTe7$UvSPJrN?0BG53AA9uz=9%E}?Nq)6jLIN-o@54*1 zG%$B)^k%wf;0Vr7fGsg)B6N~KO4K+8^3wi%`Tf`5ST%^2H9eWBQ7dMN8ao&83I2M{ zO&}TzTqXaQXj3JzuU2cn$oyic&Z8IkOW7|t@+^@+2j`ev{hE!M^w2&VJ4nSyT(3WP zS#LxVu@60@XI!md6Fm#?NlIrS_bB%+SL^v|Spl;gE|@qFyu?Vcnfo0Akk@;++vjO( zPmbE3k0vT+NBViCSlpj@nU);_Iv)1jA@@nnW{U*-Vq;jgD1bC?Lgym)+vbwEUlt5N zCxOCK1Zy@KkLqDWqksF?zka@dcbb@>ljClGJD%pl(aUT(dh{;4!{C}n6%B5W`Cv59 z-7Mo2M9tQVJGE$AGwMwO($!?pdD;AIkR%OjX*qQyw zWNSkCgJ7AQ;;(mmOa`N6CVq$a(?w;lWWdUQT!=BZ;iNZRP1ldhbKDy*C#yWKdob%= zdxJcL`FYx%@8`!7Mv-1 zw<^5e{W1^%FQ+vL*6UpIMBoXGLh<|O^*DH(hyTfc{4aBvrKTt{$t2>S;n~~n4_#e> zg6w8>PqAivmA;Kg*S<4)Lcw^80*-TdhMVvVuOGAc0S*&!69V(dnk;6ndFv^c(uro5 zJYD;GJYDLt%5Wi8GxkiG#jxm!=>%ixLcSOdce{fQ63l!3YaR|L4QE7A>~hUJ)p?dd zosTm60#j~o);`vsUk~cStR^4iIjm!-=pf3Ygwu*9+6d6Wr#~he0*1ssG;^j5QP<0K zj=p0a%a{O76pX{ZabX3U;L>i``t5xy<*a5*gSiL2=Hlw@&X1j_Ocx2-WCk(>KErV{ zpP2x;=k;guygcQw+(7^8ig2VBcxvxBkDRaY@y6jjF)D8p}$Au|0j~oe{3sS)u zkDxW5M4BrnlPoYbs8A`d7>jQ+1nN6>M<~ZvQ3e^g&>dW!i?jQ~5x`POGBV2G>SAIt z3E}fI{S%7F4oy}~OZ$zNkeLNS!bl!w0&f`_`^X~@hm~-4g#Lk$)Xz<&vw7Yk31zGn z3z(uN3392I+Y#to&6`EJJp`cNhybFp0ZC#sNeYEV5it`h+x^+LEMAc4?(*}`f5F6L_#8YFww9~S4b!k!teb<|z4I9(GFb^_DqhxI4K^s;Co76IXZ|%@ z^DY8GYG!vw6IG13ea+jlA4*UO6|JY^j)6lvMj{{4KSIn>4R*-0;(k0dhB{5Uig5;H zKC?n*Mt50R$}EXm!juo0iVnr=^X>7;=(7N9_O@BG4WWINsZ&@XXrA3p1=R%gX;52r z+YF%DfTY8IRDN($!eSzBUd@OYc`m+InWpaE>1B=OEw6zT*@<8^0D3@$zn@C2^Z8We zXh*DS4C zlh`G)%;t*tXmbiWDaAfDHPra}_Qw9THM$|nwzJ61SK~Hh&mzKAUOe1G(y%5w!k zts4|B^6{#0L}abd6Y1U`e~gl}aWSU6)L?CuFhm?d?rc<@e9Q z_;`JsFSnI|D3s?d-KlC6KQb$Vi_0wSz>CpiDYYUTcYJ}h1SRDvk?OQrJBsd3+-$O< ziN4s{4M_ub*!w)5=Me33F&R8=c^0RW*a%GcR4L9EbBG)-QC|fRf0Rg6+K9`^h*cLE zeYso}icM_KkXhT4MQfU|_WiMwSVq(7>&xrcuj<}@n}C!OnAUzTDTuL&rD!k&WQ%q} z6W*~u8cxJONFB?J!H70tMgRd>jWwB#CiC2V+?}P}$NT$M093`~Aw=4j|PKjb=kIUApio1T{;jX5o?SAy+XVwAEEDHJoI_;6)6w*vgesWC|XR z3Zvv5ILwSzNr`8%C`4*b1*Mvv@^bt6X+*?A$Y@YD>rU_&f$_CmX0A6#HW#IjN$SQP z47Xi(dE6Jm?o8I52tf2IQV^K4#x>P#9HWqvL18}QW>MB~NGU>45@j*y*C5(!Dl2Xn z@Q^E7xkXLhg8{CO!S$I*4b^jQd=IA2HQ|z&{tO>?)(`JSHZus~?f3S( zEr~fUQpMNuesR$w#?(~Fxe|?q>&J6{>ITULdH2OqZ}+^$k^2%brkAUwMA*1!=MrYC zrrK9lp>tAI*!YSsMp6N7{EUm}DQo1|?#J=+zV8?fXv^M%q!N=-O>H(CBxIf8aG*Mp zC{saRud}?IKG%>n#fZ-QRN~B2w#0nlgMs4fbU2NA!|gAhhyQjf0ss8Z|91KF`W7Uc z8Og$r3BWoD`*a12L-2VPSt2So#Gs*LHdzRF|M>V66}g!9NqYDDa<)Lyl7k0h0#e=n!3+dRd5~Bn+kU@2SuP^u8SI`e3LoC$k_AoK6$5}z!c|=(O>}(b z4xF|=RX$I8kCCyYJk3r^(iVJBXrq*fghOS){7El~Jb5(blYHcG5=%6p#z!yeuS*%q zP6wAvKkuS>4*OJ-0#e;EpU%(SSxK|Z3ssX!^fsH!I7Fg%e;_Fby`3+4Z%R63M7cr^ z3!q)W2bSlC<6MBv>?zU(gvD$^wM5Dc+8yWYB@3w@*Ss<=-f*48*lTiu2*ZFkBEK=_kx3>Sc;3*G%~olQmF<=v(32+~z`n2+7~GcRnjf`coNf zy+JyaPY6X(%p&@R7RN@PpF4eQw;v#{WR?_l&ny(_a+0RzSbe}(@CYlUY+n;ZB?gsA zE_1Rw{Umplz1AFh_HPFJ}?sXR#JVta{iDxX9)V4dDhS+h%LM1f%VxHt;D#Sx`5Cwe&?j?N-sNeCIW)vC3+!KkFFPzo|{5)S8i z)V*3Fl}~rh%)L*n>4&1!A|j&3Q02KhaUL{R7|*@YI^XSf%z_cIbw!1Sa+q*1navej zG(>ua<_hdxvL^*Dzg#ZAynUtYkdZ|ck+zMV*{ht-?Nx)repJbNe;zt_b?kpDMVkh9Qp3x?fiXsaaZB6j4pHW z_dML2O-?80n%9UBVYQ%}dZtA5m`WS%#G}Sut@9EI7RbQJ1PwMjD#o}^;#{uYIARZ} zJV=-Ll^BpsDK*7QN`uq66Esw~zLMc`EnMYFCYy0kHBn1TBeHzrJ4g4bzJc&k83AOR;-wON$rhl$3UtsQH<^=Mfu%f3a>Te?FpSwOpxa`R8<*R~YG-CHVBmx+2Lzzh07<2cXgT`sF1tlgm8C zWLPCndKD;;;Rw4WE;a+WAWv|RT`HtSS*)FH`$T9$3IzY`=~O4oNT@M{v(}qU65ni| zr7qK1mfFU6f^3YdbAMfT_PKf8@Czo_ioKS1*d9UAj!tB*R+7DJ@`hQZ3JfH05rw`& z9{OtaeB1GncaDJsqe<^MJnWD3uNXjmT~v>7QhJSZ**wG4b?=x;u&RHC)8wIrA4rTl z=+htXD8rIHvmik~lDR+f^D{e%TDqhE+bFZ3lN$ids`Ueq6%rPm*cgtp+E9=zd1g&P zqJ%J&$+h5nqtwvBBzAlr>cO&Y-zj#acl1KD1`1-zqz{|UA4-fdnNP&P7EX;xT7<*d z!3i}1ZSM~|pR;*vcz@U`l$<4h>fRCkw9rQYoB~ORti139P>rirM_{}NK$3Wti(?*h zlq+l;%JD0JS)B+z$Y#fpU9Kp#r=A<5L;)_oO`Kjv%Dj0V!z6*Jo(_PiTMD3s_8B(@6BdXXnsU#7To zb9}6Wzt`%6OfaGlwxAN!Ug{ZBQk#}KOfsl7AU5KaX`PEj#sAlzzEnsY<&}a!b`}+% z>!#?7Fv2f1OF*cgRIx5=?or*Ha+NIjI^hnK&Dt}=#65EvGbtD&B(QmXAte^S##NoOrwvlAJlaARH!qPxfKsm_|u7ZOIP zF3)e|5vp;bBQhj8`11NH`w+JlFE1|g%nIdA?T)8o%wUhC+H}D>)HACg*_SCj+Dr; z{6vvSze(W}*jZQc@VZx+pQPG3sd^K*E57`AN`RrU@c#XKOtu!!#gZz^G6ae_Bz*Mq zhF%%HZp+{r+AzOnD+cmaMik;{g`kq2^Sq0poGE&Ts{u_cNg!Nb&cFTs`*ydJ6s0Af z&PRDx3JAQZ0p^FU`|H2`Wp_AAh}+#>5JnHxvLw|XOqPTwowxfvgXsAENJoyswYdb7 ztG7?~Y-soc;J`S$!?hi3Qf~2hhk;N@cfr3RUca}hf`lhBQ}U6Z*snZ*53!rRFpkxV zR>=s=gZ3HH^O1>OCklK-!weA24*^Yli7Cb+F#+pu{^oBO&uXPC3Gmemhmpf7$j5Db zviC%xy2esbyQfb?Qo5gxr;b30n_5iRUwxrQoI()h%k z1OQtYrIKIr5Pc;^C({71v8_@Z@)G6SMnsMTPNz0Y@r(@?mC0o1V%CVGf#OiqMjxs+ zFE3P|(7}olXVY7px~5Dv_4p1Pu{Y!@PMf_MrD)4qDZ(Kl=kP^Q0(Y#AD*9oCN5MZn*F&3vS11?#9|he z!1I;iNe+dmN92ep_ zz7o?Co!E(VFy!#{fMdk==JRC`z6x2!4SmifLv?#5ED|^5Ci#Dnn-H8Gf^w}jD|)Fo zS@n|V#u8XG$djgO0D4L0!a|h~d9*ZvmLUONB1G=zV;B%KYMI1py-wyZbZWQ(rORTw z?G_td*-zqOuB_$?B)-_FI-F9l!w$eH>Qx%3%GRrwVSfzz&L{Ix9vYWLn~I0JEE+s2 zsB&{&fTDzmx_aCc?Ei1yKYsiDTkVUVlEwMhae`cc0=NUOPshVSrXuz%xbX=Ky}qpH z^O?a?3uc@%9ElZ)&bPYWCY5BxPOIXH9@(k;61v-*S~luy&cwlS$w>p4ez^RPDmv$+ z(CW`~vm$gA^shaXcsZXO1WbSwQ2uy2ynlXt|9qz!cFTlP7x#yii*tcpsAnq7)PcO$#JOM3}WS@a_tSj8$XW7WV9#8e0lPDgmRZ_PEPdX%qp?UE^4slk zwoBy6(u3S+QBIhd1WcVEnNZdvYDyvsc%>kI z#2lES(Fw|+lbMK))GRF~1VX_Dxsi}_0c$qvh!Qc2pby*Jme9Y0(hA?fWO_WG7n=>_ z1I16~vwlWbR1ex;y=>-!``LWF+kX}wzB*l`&*G-69!Z+5n9ZcY+*4*g?-$1DNN$VF zQ>IV+kLN&($J4DlKc2(s_1-^Sp4~-YfAmMQf>T)+kYrR#yx1g}8;|3npNJ%C1w($X zTl>lo1d~LY)AjQ0*tj&dF<}(LS`MgpeWp@q?6>!U|7K1!} z6C+24rozj+1Vz+=AbnFB&t9&N;f2(iZIxfuy57Y0L?zndGd6CGf5wWdm|dBm9h_JG z$Yf`8LdWgM%`A@pa2sbi2PU2T?yGi_cNuBXp5TZ6iO3w#Z&&Olypo_;;h)Jy?*IOG2@`fPJj5R!acKJ zY}XNC*M9SAyf_jcXfFZHtkaL_H$uXl1cV(40M^uAMDU5i3PHI`Qa_2Em)u0CnUc=G z;;FvAzrXvn3C*Xm{@lnD8H}$%TLA(VIoFeMg9=mGH*krxT(k)gwB>%3OJ?pBuY{HC z-n%|-v&GCQNo-ldPG)@y5?`FbRBeQeO=P^iy$QK?LhEAAm+LzIW3gPx2Q;V9CWaVz zO%>~bVGA|v0xJ#bJ(KveQe2-Jv{IM=rNWdbSzMlSh1lu6(r~IrCzeVR;K&Y1K3A(% zG1$p-hLG7H3B##FGq*Gx$^9w)Gpn80L@&4tUlFYQBpU_y&@vjN9jVDzsffTu2(nPm zu{tHP5SLD%klfd+&|fWRH8ph5GhrexLW^re)P`ZzJ_)LxAr@p{6gI%TWB)a6V@Gp* zNuN>5g>6)4H+=?DKIm@H4M9S22(=VD($-Cyt#?b8ctj$icG|QZnuOf zstTk`#X*Cj{&TlILoq2SV}7KHW^@`Tm@O|NsM)wY{xwkxJYpopTHTOW&kB_MoPs1| zHvzJ-NP9F&xm#eGIq4@7={;D5e=<#1z|0@4v3cYM#%LHnX&p-dH8S1phhz2cYw#GWo zxd2PMDqF>v4pT|SOoFR8toS;aW+@T*RwXe}`a*du99>RyoNyShu@I2UY!%`47IUgp z;kYg4S4Nvymtyrt(TkGL&yRu%S^XbVnB-b0d`{iuqpLDnCXvZVQjK-xqG%pV%DR@T zwLr0$uc|P~S~8skjMhycNWir?W*4sfyQUW;zmM^I#KdN_AIdZO|?7 z#Na1m)ln$TJ<9ppgRh59e)lQu%%*u7Bd+zM69Ndd8Rg+Zk{5PJiMkWYa*d?|M1PRF zj+z2JX?LKi5hUbf;jt!I?iG0+!@7vgJ9OZ)#enUCGaNIsJ$lFgXP#BJB`D!1T zJ~fNaPI6&KA}B{}ZVRa=6v&l&&`-ij`LEBKjf%sPjDeXnOVFkbXA}a&q_6~F$PwYQ zmg)umRy}}Vrx=~=S*)|LzgR9~coB7`go(8EW-NB=6fFrPa;UAvNrQ%+nn7rBgOCH0 zVj6TRag1}Z;&GcVW{ld7uL=NisECEHe#~h&BA3Y=+CBk|jB+}ktu|{!2*MhJ_0T^R zXhtIlBkITsg{5|ciBA3mH&pp#w3LzwxC#Gy%$IXXB(Yy=NXpK|Sb>P2lNok&Rko33 zc&w;`04ob(Nsyhhw|hU!9Oi?OdhbjpN(+B1Q%_2=+|k}wh-P`JkbHYwXN##41B+n! zEw3>+5VFEdd(ConB3E~3j?e|Zp2~&(D0e{g1{Wv51KsIkdtmgb*ON(+mJ31sVV`8y zFB{k|o>`!m7O+em;w|r{AV>U~r-m^w;#n@v$_;50@E8un%DlUqXJu{0(C5>cqdc%h zEc)EJYeR>ro#rtKYmby?QuwZb)(xD9y5_r$NT3+FqM}{Sn2`-ACdaMepku_=o ztOKF;*c~K}>q8;&a2(pwk3^kV$af#l#ca-XrhxwSEcSD&JR*(F1sTQxNm$@u%FL$$ z=}bxFi8@uC_3|hl{n(=JntZ@lbc|(DHfLnnRv2N(wY2Np<)yrp?En9nzIlQjJ}gB! zPV8+mBf%&$YH~)NDJwT?X~bAhrT^yM6d{p}QJf_eu_nW=&;<8!pHYBSHt? zrEZ_T`VSyzq0J}uQI!ef9}_AD;()2@pLZbRLaTHx44-e;*>WjeN`xAeoHASEbOcf@ z>7Z_&=bA7K@j)O`MW7RT`W7yvNrNBGS#Eec9rr!?AC{`u4g`!;4I{y#g3s^vfG8w- z&XjLr)u^63lu(3gJ0Bk(;x*Y)Ws43R0X;e~)MBQ`;rV($T_0JhmfO@OvdY`rSM^5h zBab4@1#8xRx#sGgPEaYn=!Yb+#lCnv!IYike%xHzHvHw(7%aim-|g~}w^iinI+^i; z&veb9a`O-u51c|+l6}<`U_nA~1$H)>WsA>x150to-7rYRzS?Nq_{*i@Bsd9e@H0B) zo7%rpa^@-W6f2r}=vbKL)`fl^^**k-t&3rgGg1$MlOa#a#PKOt0p(}0sXKJXJqI}`Wn%7`^}j10ozez1qR%!u={3Kz z@j+s0p6FL3X+U};mV72#=E0Y-U3%71L_%`}T9`u*4&tSq+d9-0KeErVhf=Twi)*i3fp1~FaoA{=Juj-?odmm zTKE`g!X)rW<3gx5Y&2XH`vOou?`w;^;~iCtI+1iwx&0szbFo~#zP%;VZE*K|GR9Vu&3r{Xski@|PP1?`-_=@$v0&+_BxmVRyMew9#Giaxld}WXnH= z7jw};E~ucL+lDi10NtkEh^L6#xj!hcjv;3egoSm7BYy>8?GrUq0FCsJdD6_;EmIAo z(4>j+qfdZGIEZy%LB#0iMRYgZ>~?z+M6sc0T#(^UD3VWkfE||1I7yogpoho#iad}J zo;zQ%FqpZB8lhBZJ#}08@shY*5sY=mN!2F-{C$Pcm>FOa_HJe$%M8oU+x5B1%`LfV zWGa4HYipcLKWv#N%0Yiy^h<2b4`I!oPSoXN<39KK&|oFn*ztNR#=L z`Z7^sZr04I zI2)}qu{K{tNq*&Wg0KB#Wu7|dx4AUtTd6SC?$rU2GaLIh*bv5OnTVCZi zmKnRrykM61kQ+5?AZV6w=X{5g6Qk3?J|==TG%!R*{ATDjC_06thQ-^aU%uLmEWIRL z8RaP%`H6k#1S8nVEjWyOm^CvRf)lf8ZZQ#hI3u$DUK(kP}7?5n)P|NW{@eEDq6 zd=|p{F}W*iBN(+Yae*i`o&JCDq75?aQ$cZx_uQvwpXkWh5u($~ruqtPqw&kj3rZ3S zxLk!l(1$B>BgazD=sPbK6;fs8Ts)?%JdbNSBL?YTcqKNzhcwYJ1Sq^K75TG6C&zBj zEG0!O;FN%zE|(vUM+Ht{ki128n&&fQLVdYdqV1ID&5!sz0b&FsATh$RHGLJa*^(Y= zt|jyjhkyO+U%!9LH6 zfM6Xu7pV=6*_w`Mfm*~DbqTS{Nwv5luW8dp1L}6J#tyRsmctVKq`U1l@1fM8L9uKs z#|?+DvKGx^KelFsb}$VMI9FFn7w58TLEKp+BZCrsz{npC?iAy;omRa7L&0pNCI^kE zb6@q`4AnqIVRDZ)ehsxhEMJn2z5*Zmz5RZ-&nQ_8kTV6pRPI+)u&c7Plx>*MXMXS3 zo3rY1Mr~Zj8Hv%M5MPB3M-XXfyx5^2a;V{WL0sVV^_53E13S2OmMu%ds#`?r!Pe~A zS+wS-7*I{EX>4dp*SXVK-Kkz&D3K@V6@`FPB;%qw6Ly70U zKjf7c5nyRnGbCVw5~+!H5TKb@#L$FRiOoSYuxKPvyt8b^(zAYyg?^T-6Z@~PdA(FA zGZ!0;+j#UwGmq~D{A;!{# z)kascgP_y2X>%k=DV^t?1A6AvG5g6#EI(u^zrrbDArW#u|M>W*Y5Oz?pua>5iv={C zn?WhKsUtR-i;96lUw`__Ymwo&p_pZaN-x)oXa%q%Me(Y={!2ueYM~eHeMPp>%X%v{ z)IENUohBk-1UrpQ2b~@}tM+Yky5D$>ZpGf?vEKU>4Q4*IB+roYXgIM#?N11#XCT04 zw>z*a8La86EFmik^l2>3jGMEvW$6KxYh}Q;(FGyYW#ne1V9!L5;S9B(Sx7&%%&yy< zUjuOj9omrL>z^D2=Tyuz9+aCF*~w%R^?^*e2z_A;{5fA4Ocaqf@hynUkrpJeY&g7s z{qj{g>id_sx6Ev+atgW>N6yG|Ah3-oq~*!Nif6G>rl?2B*LAN<2O)C!pZ*{J4>-78 zN_h!tv;J@_Gam)SXKu)w6F8{@tdG!??hDOJ+cb&@G&L!sfGqsvQ!`(2DpC1Ko7$9a zr?)R(i6*nxxsLgAxiY3aE?2!Ae`VEqgoy-0#DG~I6eYq`zRyGVE4u}S7>Kt}iBTN9 z-D9sPF^mQdhP{T&f&%lRbx!iJJAB{mWt-({(ZAoOnuqM%pPm9I&?lEc*<6anS;}9$ z=%l$QURuv~;3bU>Z9sX;xDI355sj(vlm%#g2@p$PURICmaXQXA0qQnVyFSjbi;E=G zW+FMa0&&|6#9Md*8p3AoaF6}X7fb9xv}#%Oqv6nXx888Mf=g~xrqUA|)UOOfdu_-t z_&M>2Wz>3-Je&(hh$X#5O!Si?QBiFAk!1&2Es#kd1Y{mXa2K(&@=Jx6Fv@9I2xY&^MY ze=f}?bA>@WOkJpT(1>Loc`-I-Q7g_)X7k^_ zeP;=W-A=9l=ncNTZx8Uzu&;K{t$8UHo{N+jJ;dllD2@{-&^aJhB2$NO%NflPE#LJ(K(F(_>IYTP`)$4^ejuTz?3YF0M$$Q|CI$LTD=(BWc6ZZ$IzTTMJAE-{^cbgL6RN$L)k z7e{EY7#TJr*qbmrm!2!)rQmHL%8p{&2mXilkrG>;W*TZsgNDt3{KTE z;u|k^97Kr`?dwi@@JV_`Z^vW0qEdoEA4sodtG&T!yxkwO=zBWSonR#j(5x7lVAbpI zRFY(u$46vJ1$lb2|9Kc45;B_@)hlXv$ z>)g3c@wIj6t0C;zFl*nDvI`b9xL@zNe!*Gu$bYejS4oXjmh19|{qb_|UFc2Zf80*{ zkN$(v-g!4VnTc_Yg$teAp$Nwz5*ln{NeJ|X7(X$BOya^uwVY`_n|5mH6ok20K#cq2 zk$d)rd2DWwi!2`dA1T7MuW*_tDNDCPlTen)zoNEc`A8sAec_rR=m8~Yz-$u>&_Wflr&dYXlcT8*~-{6xRpr_ZwCOrm15*$fepoI1Q{U_XJ; z%f*V~kfZ?zwbvg`8Q}w$QN)866GV5|trpAU5f%v=K&qW4N&9*;gil;J=!hX4c_u6; zyMX&Fjoi@8BQ`HD0xwRsyZu3`%K$ue0G=TXbcjpc*^ZO9`;2UdT*YtL-b>n2GQJg z2TDYzpY&#E(rZ!0Cv+vSaDN9|Gn^7L6C(QIWcGPDvG}}j`hFQcud}h^q$&-BHukJ~ zl!aPkK&swCXl=&QCjny0=ocSb-p+-nRVyO`^V?4-;+_+$)>&g?KB(< zhR(j7yHnnnQE9ztstrMLXz;0L#2Z3uTJzbO;TdOLR*um$$-H!o+O~dX+2x!MPUEUFTel4 ze;>}rfBENs|I z!Zx`u8#z|xpk)i@cKD-HYS4PX-7`rFBQ}yjo1F|DfSQ1;nXkrG%k6fb>&;4p!7Rec zQ<4butWP^|qa*3o&oFAe3JF$XBJvTE#?!F^;6G;A!ZdlYid>GV+p+x)xvDc(t3?tW zLrfa{ z;OOxE^Yh-T#qr}jxm8?{MRA90XUUzzJctahd7d#*=vn%)74anZqmTpLjB5jBlB4UA zpZpPspLvKp+6*O%=gD;L#Dq-1w%&C@zhtQbW@lKytj{Jc(V^|I8H4>{%}E@!FPXSthk3It6kQQ0Mxw7gL@NM`<23FJ-GUlAmZs&RWZBL2V9SP$s?ze#86sP;t1BoHP zj&c2jV*7Rsr*me=Q)5qO#7lM_9=%14-v? z+{TWd5(HU8E#B}!U`==Uvu~@yX7LX9f5)BzJO;EA)z$runoOj$-d+LZogiyFb+&4l4L2!KxHU;sBSi^ zWneeQROZrDzk7}T2vO*_7-5F*(MgA79^mfC%f z7Ao``5?00->g0sYaxoe_uX*27R>N-A8(kic9ox`DBwJT^3Av4eDm(ZhSP{nWnfaI6 z@@(H9PqhXXL=Wtrub1;Z_pCGcSx{Q)UICD^i|Pa-{OU6u{81g*kc!ems5~oZ3A=Iu zIhU#5W=hShw8@pjMXsC(-J>Is2twROz7SxiN$+yKk7skxgmtJuKTnriFnAg0{RVoYwdtar$Oal;#^acD z#{%K4CcxGJvl<9uMp3!N{Ax)8329QZN_|j+E1x;9usL{)p0{{r=3d7HO#-o{VEgn* zO@C6ZbM&-*qhPrmEOs&(A2L#4Yr_~hZC66nf_oh`?l@mFIFoGQq%L1*GQu?oS?Ag zKq-g_{OJTFfcA3PqMo#lcKXUotjJs;I?8o#lnh40Od=?bp8e1N?)zW=@Bc7aO#kxF z|L*lKFZYo|6Y#?~+01IaX2ZV9{xCpu7Nem^=ga^Leig)!(U=l)k%z}7TU(>HajF42 z8{>9bGir4N(P%p!j=%r@U3(@?2)Uw?l~+CP<}fmi}lVe^q~dlrZ?tu|89 z30XEWs%45*M|@=$ddPK~_*nvbe~=*QL`u}aj?XsGa=B{fqNEWVy_RN6{A3ky@2A$F zTITeaaX&0;>^Od-wZgG}<<>xZrLg{X`zf>{KU&R=4cQRbOx-BLiynw*x7($kXO9^Q zN2(?r8JLOGoaZ>@a?)7U&Bj9?A|r+2=;)`)Mkox}ViqbDVwg^-NPAK%%Cb3yE83)L z0S=+4$=EgfWyly;c^Ul?s|NIbkYkeb`oLynoNJ7P)1BF`ox9-EPapGDWbn`gs{pqHW?20^-#++&SHjHspoK=(|dq zFzYA(RJYPV8^ur9(FUp1lfYO{7J^EE=G()dx%G3AYsXm1hHKtJhQ~j=0_AaxLT{~q zoitOxcoU}MSV*+5DSTrY5y01<_S#knm3guVhw0Qa5G$g^$arb%S)a#xpPFAGDo4Qh z-g9*7#G1RE$9*`REEUG5+ydS2j(IC>o53Y3I)a3@`@^t`5F}pe`f|D{RADrR?T5S! zWia^p=bsVJ7Hy4F8&1()RQ-T8KK)AC8tbb7Ztmz(;wv6?9`neJq?KIy8epT4$E@ep}9$Sq5;+ z4QaVQq*hHJ=W;!9F#<$GV?_T=boxYmuB$2@>S?*Mkv4?irpjV7vUWXlOkX;oGDn5h zoRkHYAznDs6|-P86iTGBxFM>ZkCbeB;gSwj4hn*gndLejPw$fKvH3^QTT3{cMC2(& zSPNnhEVSsB@Md$t|D{P!c-G9g7&5e2q9rc415Wkgt3Gw8aYHc8hW(^lF}93Z_~{E? z*eJ*sT~H;{1d-($zVPw!$xMI#`EU5RM8=894Lg7rO3;dUL)hA&t=>1PBJy z&+J4ZPn5-+z{LdNJfa}Z)ziNik_Y<=jahM=VKI^iyeRMvhr9(Mfin~FL7xoUrb9fk zVU*Z+B&w%W9px6xN_91_WMBNEi?k`LwqYtPRLs%v%0Mgs0&{+9JpS2O!{$&n9sAXo zkU62jg=f$uqeX74l~gfWrCf}(7K2R05)F=rT7ptRSXWI!Wfearq>bo^4PiM_H~|3x zdV+0T`cQCU+^>x`KRKvnQJ4ChZ#u4%)}gKpq`e&jQMsQqO>ir8=&N6w*#|q-ERX0I zAw(uwNQTEBl=bt>B8uni0Ox+Um1uRf-)lob7l;8;VGp8o_LY&hyI{+I@f_4wH+&m~ zCx*YKOOa*7Uo2tjnG6Q>XwiIJ+W z_HpN*c1kPh9rTk?630eEa&tzb#936(hR}&-juV++wv*GWMFJUnNM zIkPu9W>}dTAgc5|wASQ=eU(!dB-NQP&iQ)#_U(JurK&RkDq@t6Jd!MsqUL67cJQNt z&w%FArGo;it`O9Sb1-Q;@Q|38M%2ksd^#pkfTJ~qL=cmN1@ax{`6n9aqhyivh6Wur zBGYrU_QMt1@iv^VU$bWcxus~!Sz4u^W`U%WSOuFV~#brIG<-e_bcSE(-1a0HkPL` zs_9Y-JF+-OeSP^NpW$M+JM4CQnE^i{7Vk9*w!=tJ^H?FJpEjA%y2TVCD`n>a|N3M+ z2v-RZ6^4N3$#_2K2RHk*c@fKKuNrW~Ct~y+@#cQMKG`NEmv^zM%4>OGMHct`kV)jD zz4Mt(W#1p1;@$B-l}LKBOS1vydp_shTqA~=OxS05rbO$kAxbLOH6V{A(VB3~oAMYE z_@vaepOshj_@L1wb0;zuNuLscVzPBP+DmQbm zsjNvAgZA6zI3BR+6xlQmm}ecZ@dVIoWm9oB@4u_Nok=bc+?OVTY`J7n?}__y2~t0L z<5QXl0Y6x-^k6tFe0+Rp?5m&BeNhNHIL^zGZHhua>A|nf@bsylj8`@+Mlv>S^lWn$ zQ*m4aLx8444}@ueHYYYR{L45+NI~=O{_YiAP zU{8T`m@mN^i10Z@^DKR;3no*RlkN=0Nu6s@mgY}PWJs&c8grYMm$5#_d8?gs^#&!x zW=cDVuo(sKzGBB#{X|pkt9=a|L~Vx4ij-)_Pfo=ei^a}4MPU|68^TYicr$%ZZ<28U ziXrsbSRi3y)Y7NME$!L-YSWB_+laR65IsZ@r(F1ZWAjj>rWe>4gRDX)`oxYr-J<1ut_r&S^2=ZM zd$>6&2Gc*@KXmxz%a`ZfVxp0gHEf%xMrrw#V&1SQ4I@@CdNYbEoVh@}T+2Re9gJt`I8#@B%og#?tl%eItJK2qwCuz);AkDYoe zP;T~X;nDv|*tny-Dpyecr|#_AipZdL<#V}G5;)@3x6-Q!E$o+aGqJ!<00d?&ARyU^ z#E_J%(;)bIeivnB5-oA7dK7!7LhCFG;xyp0dA);}#j~+oSSpr7kLj)gDXP#`PMw0exm~V#gxxc%*tLlx6R*()MkpvTA?X8oc%3hD z>w<0(?_eXciKSR@IRD$ng@cXdSS}B*>5|5;gU;2gwTa~0z5C{LKCGAACl7=I~nlyyY`YEv& z$$NOKo;=&yw9$RCM)K4%evN{R#t4=*5Rvq3r_ww%)2z{E%MX7{aQkX7J32us z#KgXg4!en6h;>$MeEQ>Y{2Dhh?CX;>TDplLj;A90a1mn3TTuv!&Yj|Oz}1haT68qd4nvozz-2P-G+iNU7N3dYC+bvTCD`@V6|TF_3e#7 zH5NIH61r=tu~^1<#B1S?DdT^d#alaZTqMxRaeu_bY(8ajd2#*y(wz>o`2>K7(86^# zeIlSq%_s7pI;AxdHPB;L6mrR4#i^w*(lWA z`69>E!xjd7CJ$)KQu$|Y377oZycd!LXq4zN-UT*iGB0RYKf2B9KWp>Vh7wj~P&+Lm zw9Rl1uGgYo;tu*Xh+&1bLhy;m{qRD)OhIi0kzB;?OaVGQDlV8sUhD%gI>B#QXCMfY zI1KR^F5oHAEFq7*IxFz{#~Ynu1We+T=QcbkcQwFtW=2Qs5WY1mCC>&$I;`sQ5Y(8 zzCUb5Fmzq&nIU!AuTPsIMI#=AxZ@PQBB_Ss8d+*71+Y@Zk`5imo$%rlbBp(D z%;N{g;~r3Ua8fsA`cEeaYeZsACx*~l4W7{(Qv}<@tEFGHwC`MJk#rzzC6D>+$8$?@ z)Vd0-RmC~zr*^P?)pPr*LrT}Dvlv;(3vvoCwAjjBv`%L&f}KEZHbA%jNHSKM*V)J% zk(Hi>hzwmz`0VK1KrLbjM-Ju&=1igGf$P9MT{%VQpf3h&tVOMi;`YG~Az0xTY|*%Y z>`8;2D?2#Q%gY8Ox@uMmB&kpMJ;DFUul9C)HsjP!4yIns6;@$NW|c3pmu3^q;!IT{ z@N?8M4*?m}aTY0Qh7!$M+9E4oZFbgm(Li_1LsuAAk44~h8M7+4OjKh`bW$N>Txj6E z#o=Lio{U*+BbMw;IqFX{V)NQP_(|vn>tw$2@UC;sUJMP(}AF=OCG^DqJoK z?d2^uEy(MNj`*Yglg<(VVL+b0{A;7!dWb4wY?xvyzVb&u3QY`8YII60wP=pO*?%o_ z6|txcJzQJhLa$b{d7+FvRWo&tUv-Gh2&EPz#49S1Xy8cGxP2z!MEg{N6v0jOs(6;t z6d$OP>-n9bSkb&ackXfZb-Bvym4WnZha`2|hAYCONiFZyV@Ps}jRbrRUwLt1-Zs-70%l{AewDYVV8+IQ@pLwne;o>Pi*sc? zb0gR(k$x${J~HUY88o4P#|_QAecE>lX3_5yWHg(!Ig&w(HdLxEZwLl{Z} zwwo-O)n9(ktcw1f!oPfN%xcfUn*hb4W?<5LL6INUmFpihg&GBAX-rT%7Cx(DCl)L$ zmswtq5=Ridn`69aCuZ^>d7?EMNJ0Dleq3I8O(I>a%GCcHfVbvf==}56FWZJ zW;ikt8|#Ytotx4a??D4&uj~<#B2`VIF)|waXl^8-@ER+4tXDiIRk6@08Zgwl*r#cE zg$(US=P(3DK%Cfe9Lur-OX56ay;v>9Ri59Jr#mDN0TCnDL7T4zrX-t#p|RHRWaf;3 zvaTkp@EzRlcjqdsHOV6p6M2K)3{J$1R&VLH9I%OkT{h9TZM%T1D8GsK7oJ=?L%x8`oMP=u-uSwchD05ke9H zod6Kth?>Fame@#wQ@?+FetdlH51+S(teT4eai`4xi3b5j-6`*-@BVlq6JBfx#vS?_ z3&IK_iKw)ljoetPLH#5;kMpkYOg0d(NKb=K3h=r=&W9tH>JCS^LO#Ji+S06EOXQTg zOdLJ?M7%#c#x3vcgPTNc#OY%G_Vw$_X3fq~NEdow1PAuB&%^JVmTiBk8yD2!Ts<_6 z+eaHG^lRgVn(V~J^4cg&tpR#zjv`bJ?tZMFtui`S|>lS|f(cF;wWcmlT{$OP?Hu)#*f^ z4Ar?z%P4KSh-?W!Vt}q7Hf_X5BT4r-q#M!ra6I=OgJs^MlvoJ4%Z!W=v#<@45>EV z>V)VVZ;IB;!HA-Yw@u?PK)fa%$|!BaZSC8JEQiktqjmQoMw z*6Fn~<=stL$gtB!l>dDCN)#mEAmR%~ZS-g-j)}|^qQXvq6U>qu>>hcJbazCpHlhESTVQpC*;FMZ3r1f73q?JvR->So#(A^iGlhdC5`=& zCz8oRfO6uuZ{N|Z!Qq&<*cuBXc4*MAwuF&-Z4;vk+Op-nQ~@S_61C6^iD&X9kAjG9 z!c8VVvnrqo%1MOcEBcMqhTx^?f^nry295LdE-gq2RG3X}*>o&Uo19eccbNwaDSK)yY1Q?e*`5FU&UYftRMCqV? z;i8EJBc-+HcDt<};;`DsTni{`ADtBV!BMjif+#~sHz-CJN+=?ztqfIRh$>?kLRY1f z_;TaLrjypvezafr@MvbABl9mm`D#QxRO}AHl&_5O6S7HwAJtRli{6xiQMwlIzAg@xQ@x{j01|#~!uc1-$ z`ugfDBRWFnM5Imqnzz&F-@dMzxEfQ0fG;@$T1#OrPp&?)A z&UH%7+Gi%!SLelnYu=%mi-SH(Za+Y?HXKOLc34R0mMlxqc|1-kDkWV^5Q9Fo0f>1> zN~4HD00qBumcIKy4B<%r|dXNLyQ(o7}>5>RrD?2tz#)NM!t z>-~J`LSz2{tSe{ach-Q}l=maG(5+U@+)n51vD@CCXP6BuJV<+L4JCl)bSB^tW)f@( zdAU+wUL{(Y@%8mZO291jfV||467&;DNhTK3z?=WVTtYXrURS3W5UOetD_+g^0IUWs$*_r0w}uclsf*DY%jm>`7&|FV zMe0(ymeJtj`TFw0eEk}jSt#7-PHb+!*=*Wat-WSGDc4u-&}=|s9+LG-*C@0SYUYxI zKg!OCNF(CnlH#?qu$jk;6wOXCkd}6QwNE-e5zqu{P*DbLbz-d1r(gY42{kP#j!ldz-zHY0B%$$*JzWP#e5l$85-d{ z#K23Dc~yoHP9-e^mPY!k)#BMFpXc$o2NZQv<&sE}x&o2n0?$z%3tHm1A8{gZ1oPK5 z%ts(%7V_yFmky$BxmXdVfdse;3-u@=a3`AjbX;Rpg`V+POk*|}vF|6m_|&mKxPb(u z|A@D|8-vnQ+0O8k<+DkV$`>v3sDXcH;W&uHHeOy{4NWqyuWz%Oe?|}8>DDM{pjBVT z^=Jo7wKx`DDz)QuTp5`k8@#!ajO_Gw>A+8Xs+k#zjo8k`1qVT`{YDa3eS3S;gObl@ ztc&n^Pd1M+j1a7r4mvA#h~aWaL!jS(x{~_>Ch8h;vPgwf((|dMZbMZSKThMyNY8Pq`TVeIHFnW*;l;lQ%l^nu^enB+CH6Cj*|X?T=t;UVL}xW zCkX8}PvRj1-I7?9MG{x8 zlWQ}tsdVQWo$~x-=&a#K~xm*Hd0oBLb`zOXjYzL74|2>68+DPPgM+9bye(`fo%w6S6}#_9Lk>NS`Q)AECR- zvuc*fVjcPwUx@dsgWFxM2KcInW36^+C$ia=yljoZCfFJzhNPLFv~+rP6^B>zp1Hnc_YhOc+^9C7~jjE5&JlT&y`V1zMpGc(U>^tRM zW*|~ZqQ;Klf&x{fWqkDQYi1;~(&hn#!u5I98E zq0QW)#KEI6ihJh1Jk-B#_;8{`t=3D0QYA8L%QI#3ioo2VKhdW@ofEJ83Z+b^BLU@h zJ5R=a`($gDZpk&s$g(j%ba`X>3-EM zXu=v}&sEvSj1%imhKqTK>?DZzMA#?1xZ9)W+V72|gxdqwIY0a20v+-wj&m0JBpcq# zfuvGZGF;xQj`|3MX`v?}t;?e4;~^LOj8#QTU=RX`odie~sLs>)6iXHBNpQQ}C2Ni+ za1DEMt|%it5J+=jM-mY#LVbRI3iN(Lzus()x3H4SlzD!z9-O5JWF(PW@Y((#DCc!u zk4qlr1)E;(=fg!1UCc+zMIMZstHxwPn9n>aUSLqyNxEg{xdKIN#*)<>bc9YHc>_^m z-syN0NsKne0OR2%QlPHt*@aiwH~NatX7aHN2{v@19u#%d4S)!7K4+wQkHczkIvyZj za?FCcnBxTtIg%A?Bd$c?RGXzGd0N~^4iT?ZXv1e#rb-q2?e4JKLuD^7Z*QAd?IaGa z#4j1e^Ks2SBe5LTON?lq%_ba5@+7!Ci5NfUQwCssLt4&lVJwHO zYH+y{kwGfDT={;kAF_Ow7ouyW9i=x=?vM5Z-MW*k95r=t{#nodP#CcddWd%-ZLF5c zvpaVi4xV!a%4DabuPHvxmt2eofF8L~{>#^|$}ORTk&EZZ5$EsM8{P5oxJJ;8QaSIc z^GAbPk{AQ2m#62JM}J5XT@xMiO1mD3)JiVKS+CEN*<|7fQ;1`7xhO(qxuf7g%Mf8M zFWUl$0|@d(2IRA#6ulag%_Nlx1X9njTTCaL&GLNQC;vcub+;dPb0XvCjFLT$9FId) zWk_EdB#6}&3^fcQw>znDOY8~OOkD?g7iRz=%MoN~5S{p%GnERb{s?NSylGR>KfrqC zBS4)30ja>@6=NC5Ptu}jnP4*LAG-ta$LE}tkSpjXu3jSx13aJgP`QhabL&l6qo6>% zc$X>k;PjdhSBNGyRvDa|=5;8ni47pcYPn*qNlB;MFz;!NKZ^mwQD!uO9385%!{%VP zS7auu=phvm7Qt#s7|{es7Ffe(T&obd@P)j(Qj(Rl`KM$Ks4W(&dh)`MZ4fhwVhNB< zjUjI2Qu8dBJqwN^OD0b4SuU4ZhCCWm|HViO6@w}e5b*l?`eQyg)kDCE^Q-dAHBbGW z!2lffvamwDc=iUkJzsBm+*Lh#HN|P-B5&)fD-Q)_^jSP!gb(V>iYY}xs`tD-MiU5d zHkn~VC$zvaQMdivo=?~Nl^HC!l9)7|P1N;<? zOJKfTD(zxi9U^&Ag^13V&hgwVOvp78pb?RzdcJ0UvkFw^lNA<3@%!(;vA$X#5OFm@ zHO~rPS@NFQge1;xuTRm;r$3@@Ep7T~J2dM=zQdGA+s5k3as<-4c2uRZsseY}a!#b<2{9%|JvRr)L?novSei@ktRFK?W`Ve zMnc9>UrH=l6|Y8s8FSr8<6(QTsW4(hl!%c=#8eP%JSPuzku5|P(h0=WBuFtc zVTmC9JfHCf9Rt5T&}fvU5Nex2TOeCY+v{ymP6^pgzdt0KAU36zAe&+_ch@DJ1iI&` zB&|x;lv2r!hRChdOe398QEyM2*E?%7H;Gwr;dJQs+bxx?S1aB{Ak2rJ6ZrYW`IKlb zXGQm^QhYW;VH{DmkXOl;075NMXXYSbmy|?e$ql$!t;D?CV?=0j<>A;70^POp+h}r- zKZ1`lXT%tZ{>k%v&c$m?#cDGdnHtPkiHmvI(fM%PlhAsj#2ivc)nI*OvYJ#E_3jS> z1u%5Nnn}2-)c}<0mZ}qL%jtsChzzIXY;=w7AM^Upa@RqhXm~prXWj4_M7xUx&CS>Y z%GriMA3JDwfCbMb>uSSpjO0rx<;wS2d~jpE{QV@M3XCfeo8{qAtP+y>MY z-wep8p5-&%F*|pV$(|MobGO}6i3auwBrS7mV_Q0Pib&<2T*S)Es88W3wB-R2$yH(v zz=ln~R=z*tP`G%?<2E5>@hb zFvT|cm(bY>?qN9o#x?>34KK;02C$!Jm~gA$l9W<)A?}_ClfnZMIA0P>sz!}+YUMQd zw+p)^JIXp0xhY1K)$R8JY7IIt;;B0Zj-ct$yL1dG2FXftAXzleZsIry)2C>`X9GNQ z#qT5v7#a_WM7g^$4`Gn}j2L9hTKfI-;c(o29`>KcNpMLX%0q=fWUteF0TUJEoq!&bpgUDH+uRD&U3v^F2bs}&D zHA^mt!8)9|S*kiA#q6bs82f2#w^e0BeN_4 z$4w?vS8BWKhGtA9m&E0~Add(CfFOSI*EB+)tjM1O?3{UvNNiFz5qD9Q$O)=K zzk|F}j6

    rD#BC=hN}0udfV4m#uO}IrI>$U2heT1AY3*!9omaAeq@5UrK)M<$ZPW zsWGUpFg2TzEG$DK(g$o6Ur3$}Tw(()KUU<-#A=(IPO>fx=LqryU`-vu7da4NR6H?I zo*bXx)^4N3Jag}}!-CbB?nxSQW<(Ze%iKy(B4vlFBWtIDVuDp>OKwUKQzd1QiP{XQ zJZR8UC94~Axv7>xhi=xb5)lEer{mRcSyQB_ibQ!QJe*puR=T4Ng~UZtcB0hUf^$ku zT$`biaD1({CS;$^i(hotz?@@di+1t5J@VAf3C+n*k_J4m76x&5IQaF)GF{@;zn=Am zL4)dCHq0j$`V4q34LP2J1%i(!mYGEjI*xhHX8S_WvB~GXJnIK|wV}|t+{svmu<>OB zwIg<7!c}yM-m++Br}{ndgiNMW(qPnM@uzoJrlu;3T{t=SZD)EBJ2YM)C=&pPRB}me z64dARgY7Ews1sBu4PV4wxsDwuU+Ths;_V5dKBOn%LgfsK8-dmF9nO{5D>772^d}>9 zmm>>d)$|g(iv^NcYI77eQ*@N~X;4cTMz}~AAN2DS!29J?FJR}BllhD_P3F_yaB#Wb zma7HcMKql>V<;5|2htInL=0nOnI9h?hBH(H+zEoUy2-fYNyvI|iW4;^AD^@xRZr)F zn~h#L6WU&1H^{~XmFknU%c0=d$7+?QnYEkij9bSqhr><5?AnZNb7O22_4$UE0YOvnX%L`^96R=h(+P`2vRyHNh3IuDuYE> zFT6)bcfOYp^60Lo(QK^Nzkrm_+XN#^#U(896k}Kk>C-{o*)WcnxB6alJXRx(c^JRbk_(^tkME~#?}{B^y| ze0CP8cS0Nf(RS>3)Sx?G_uXk)1S46EBww`=^*| zS3W`L)q2B(ri-O)t8%+Nd$}16L>W&fi)C-1&K`_sOXEI!Q5aF6P3T}H(>Yg}V5K9P z$Fn_?n*gayOFz?>A5yFhM%VjucQ~PK2sLI_5wL&8nlL?}xEaEdNzfr9;-Wv03besH z>pC3Ip=vTQiZh7Ij-Yx{HI1jvt#JjNJL4df{jpoUybYLjZxFir!^wPk?+tk<62x^R zZ+CmYVskW^QNiZ*E!}pfOy#RXD34uEXY6OSeyK9;4zc1)EU$M4=Vib`276d6a|ILV z@hgqb7Xm#cX4b3jvCYbZGo8Y|Hx_QJsGdX7HCM_6lAajSkd}GJ ztR9JDP&{U>gHjyq8|MybpKOMd_(DV>+YN_C7e}EC0VB(kS1X;PM2Ad^Y=hY1utt%$ zV`=)CFP7Y)3NfO~@zcVCE}Oe39&IyxK6WKy;VOJ8yn6oQF##} zSGkbsY!1b+kgV{U=zLBcLQSgh#SA(-UQYedP}o$d$ZuH`h{rK`!cpVX<<4;`+RSo= zkPVK;WWuVjaB@O_l;K#+oB=HKbFaEYLh8$-z2|nn8|0c%Iz%PvX?{4G@_;JSNPb~B zj))zC&=s%84)>9DxsRcz%e8nhUvA!z) z1-s~r5SEJ#SWXBtW)oPbq=ZT!T^VFzxE5@wz03(%CyF!hl5;5eh1%QW7{~V=cZ$t)r1XF30YAIFZeO$C2daDn)@5 z*VCn-VJ7vQg&3sHYKBx*Kq1n6qL;u?4?F2HbC(x5;1m;16Fex6M$cDeVP2xtQu8ifksxPbHNcG@9;b84krq|-+H6rl*NJi0=j+B z9!jV*)RV0eglS?ti;pX>grU_2*(rX9gb=~vNlzruwi12{Gg|ZMh)TFArG)9QQjIxh zT%Wxsd{1~&B_#k%!W!#=?woF?91!l$;`{Q>)BF27=}|%E*sEa3#ORMT-kH2+7fm%u ziM3YBq2>tH;dE6S2hl7>9M3!k~&LwX~;@0>>9@X=4j;w(rfTmGS1K})hGP#0w)8%|P8A=oLb*9Wu zw~I6ZZ!gwMc})=X-(o&ztT@S#VV^I_zj+afeVwG-sJtGCth(tB_(}rhU0m*onY-ie zcpN9|5W`ZqG2aBeK0&6F>$N-oxEw}Igyo2y(SQ)f9Xvh=CHgsJ z!csb_)#}J2eK-mEaW}bu!RKz@N|S?^moI=F-<4F7Y=&b9M!&|=xiK=qNn7!#JYgNv zHEE_CqO3^}(*3#L9j{fZ1|ZJ^CO`~;3{@iu?>WBS2gyVI0gOdH!e6nOBB~??*UhGR z^v8&-wz~twX=k6yFrCzdPNt7zg$SY<9FaMfi2C4-f zWxYH&(~Wfr$;LydjMc1HFZ_@J9}nkbgvs0vmwS8tDVIrdVN=nObjVAY6Cx3F$b-zX z1d~A5>kTP|cmZI-d!S6Z$dW$d(ttmRCR9e=JVB(#3y7B4mR!S6IIZhBSyKx!!{x-w zDdw~JD!1?ta;+*?UFS};F%lambgG^7`6D?!M5V)_Lt)K`%qpqKVVMMC$%ihF)1N$yzSOn)w1qf|dfNueZdg$%qX>@~ib4v|_0=VrRZwYXcNWN4XlC&eiJz;#o?r zq4OM`Ah6qg%>3{+%w^h1#G6N~O7j%Nx&##-%lDBqUoRO5_=0uf!#RBs0^$ut{yq}| z?#~L{HLE&TKdKL-*=MGL#;6YJV|db7yJ~S~MSBpA0pps+c_KoDM_<_2$8PJ07{epP zBa)Q8*gOj%QkE9=RYV)rV`^AyT%%SFP#O9+w%4yq_0M>!$P93qFI86l$a8x*B1w_M zOyP4iB$4{dY@UEP%3DVcU0#-iYbA=J2t6VR6a(&$>JjcfTUNm2miuw%>6|x#^*2tM zG?!4LTtLA2>?jbj2o!Up;AZ;MqF68 zQE1^UGEZ?an>~jEh%qc&g~v>M9LWvB=;!L0R`}E@Ge}Wg6;z5c1x)Nm=7rn1JUd!0 z*CaLzU*lApN+wZSNaISA8PKfN#KD5C6qL#Y;6tjfI#|8FrFs*CQ(~0XPN`oN|7<(E^ZJLB@1nATc=9F)H5Rh`msNpzH35|Pc?Bqe2gUYzyZ;mlWHF(Dc`_ktVqi%yxnxY0;_VOV z2?PM4tmqizkgtTE6T%C@mRC^^0 zJMwJZPdAgKsEj($BMi>wqM)om6#}8^?=f47Z;8ESxtY7AWU)m$etZ2g5i9f5w-L}z zP(h@Rtm|7Z7j~Y|{HY@}k|dhjx3 zTZ5%YD>4uJFY6`6!wR`-EYhW-sBBnE)mvWX5=rqg(`>f;ro-o8611&ixegx#POb-_Qw%qh2qg6Ol-~t2obKO%hfLo^Qw@TPZ$0a6@vGihc@(fWQkdW&j9*a z7##Q6=Uw}qeQg%A)v!O#%MSaa*Uc9L>EUJdk_m02@B(g`D_jB1j8&CLBTWTF?WYQ& zsmy()DZ<%qx4gFQFUu2Fxjr%Gc}&YysM_XYYQ%L+0|@1pX83K!+xh@ z)PaOdUdrQ>w&a`?21J>q7xA^a?nbOapb`s9$1aAL#tEIWX)p}|!9Ym^&z{J_B^EkT zq>y`EcK0n;`ZGCIfO)YKs+Q6aVXHGXXX%DZHBxE=kdryO*I2GqU`b=!5h zHDNql%!Zoi>1Nugr+%yxYh(?{HbkcjRSth758tEysBha0p`5o!$ zygTVxR_g=BS-d;RolGc{cZpsnB>e6B`>)@(F+}OC-~al$kwyq}vsxVv`}J~3-l{Mr zb-kR)lU0dj+wEr*mZkH8S?>HZ_GZ0ls|H&ejM66KW*BT3j_l6TZDP}EF&RCt0__x( zyj*4k6_d?699%1yr8ql=B+S?~pwQ29|3nb6CUzOr&Z2lll$>S6oli(w@}-3Lk54ZC z^Vcs+wFZUgED2al7f3S)6D=q^VJ{S)FT#^(t*4ZIx#-CSSq;`6GI`cXUh<%rXgF{- ziE)h{r!XhO@v?nNCblb|jfP+Z`q61b zO7ImkdM5DjHyog6y;@^GS^glC?rs@{U#X`q$G{_Sbm3BhrYDIo<2N8U%A2 zen2$uheO@&9fjf7Xiw$GIqTZ%0ykZd-=nN0kHeEn?J#(D?yDeZERv*U2iB}eUn(8w@V9ntXJdJa+!cK5MfiZ>QH)4byGZY;l*qYBrRUl*$RTZAcH&wr6?u0Ck6E~ z12#;9&Wa2(Wd+wJ)RJ?{2&?2mX#c!=GCgPTiezFAKt%LXtJjIk2$`9m$Djl1#X zT>Hu;DXM$jY}o7E5F?-_kxdq-we%T05#jImd*iNFi(1^D%lfg*n!}12q*I-1a#wjE z?{qn-GJF@)cr-`*{&9d5Xjf7m_TkHej1lC6=onR&@}_L)IZMYF%?qvklVW8wdVPCC z3_DD~s0kEjv0?fm9LER{3{WMiigR^E!95<49*(lEqu6mG8v^;2sgX>4L4xJaw4F_0 z($b(jB<>>`3SxDas!y%dp-Q;fVrfSKrJDUXbeQBu!0db+?noqb)~0h`DVpusfdiVj zd>WKJ`$R(o#er*GJ6W-LL=_||K!UpG^Kiay!BC-WF%NFI)q29oOue_7FR6n^F}y_{ zY>z2bD+v!~b0(b`VnHwNqeje%>Pxb=v7h1qj7=@-%qzG{jJc40y?_7qPVNK*M6Xs$ z?rn&uPE5;Or?M^GA`i{S_F~XJY?Fsu7zN;yG!nrE!s5+x?240zxXHfpO@F+wY#47$0O)iNSP-;8Hqrwx73kYjw?>&UCXmP z^)c6BcnU3uQgoMCMTzxnuH=GIZu2v&7$uXEfYXTuVa|=K)>9mH%(x85p&%~(Bm~v- zMH~dBS`4g*T-5_PvN$JNvI{sRik2w+RNaoC6?g!=Mq5oW-SvHxc z0%qCRj|UAooiPah1zmhq9_Mi&j)=iXJsytA0OT?I@RcbXYRx+(nT>O_QNXes+9q>h zIhV)BnOPj2Pb4VOOkgJou77`CyG!2xjOd!)W`(NeQD1bM`cr%>f1<>p2)Fwhd>B-ljZCn$bQ zRThMOJ$JKlu5wWcr9SeWwFelGe^MS(*?^^uzwmAQ0MgrKDZKzgf|)2FUul=gklqE9R}(TaumFj-`+B)v z&X%zI<8nJ}53})Xv)afyyU!holN;3!au4h9al7CtFR)c-^6vX(GRits&GN48L0_4T zSY^DEF5Trp7)XUK_4`KZfC%079@j4~ ztJk$tG7~&t6!!-`-!C`X1Z8`-=X5lkk3oP?c-Vc4n;3I$uJXsHGvn(gH2ca>efnb# zJ_%gpPoR7Db*r?xUyp;Qb1y+cvR%z?iqzD^pRl=F%-PEJ@XPq#ows_7Y6c) zg#cJ)T(hV4tDm@-)a|R_LcEiKf^hhkpMF}+^1$rl{&V;F&i=L^@2A85wBMhO$EGZw zB%f6nd}K0OLK_5Kb2N;dFNg!g$f(GZ&%CVXEa7(Ud`7(D-(hz-`a6vKVWbwB-m)gM zMARPQ#_&)(pG+97uw~E|sZ~1I#IMnPMf!3voiRfk-Khp7CcsST59L33Oo)$PlDXpc z0#ueZQ$o~@zXmsKZu(b1_U{@CiHe!o+h(Of=2Gg4bj!KiJKO!LGCT>shrRedcwGCp z^W%KP7w#|~h0(E_sbdX{G{aZ~$XXg@QxSqL^*zpeo(QMKkM zl1qF22M`-*ksGDE?e}kDrCQz}vKgBY#&ZNGK~X0FIWz&(jPekqTyy}GDw!(+h8MO$ zU(jLKiE)ysp(nyt7L5cMpj=#@!UuI%N$$cF9Bg^*Xp&-0vPc&JH(7sK1whOAK7kU6 zGKnVlk}d>_N3TCyu1Jy!i6CfTGF6{n?uWx^D}3`f9`)?0M!NHP-<@*n!eY5Zuo8yc zxQ8+9INsh}m5V&N;w^o5FO9L^=j?Ac$Le}DT3MsBv~n0nqh7>FZ0>*b^#MP z=MPJ|WH6akwSYW=Og*U+u`n*3CrXVFj|&`WbLQzPfy9mhLIsP1 zU^^b|Pp7QVU(A*-i|4Qh+m5I6bDndU=?QeIUeJtqj46+EdB=a0RhJ!7|CBT}#t~aL z?pU2BY_!|2c%Q!D9;>> zMbu-#+%+IaJkQO+*JF1`E)eBF>G~Ab6!-JV7~YaHSQ?Qcfi_Et?C|!7u4A_|z%p;# zPg;_F8Gk1colksMR6^KUX1}xZ{%So}K&{r9`)9-at{F4Q2aDA6vhCzL-yiFI$izU4!sM(ydyfeve#cGX)paK=QJ2dI>ghSU zsN3Frs@(Sb{d&E&FNWxWeSk2UlZO7w@cbBPT8euz-ot)R`iMnTd^(p4K%+c>fqfx@ ztZ)JcmN!j?|MGwTFM(cp@b>607aP?GIi9K4|IhyG<|QSng6w=@$Lgi&bPl1ho!ueN z6(K2uMK;x`7gU3T&P@K}35bUtD%jNO8I~grkmslz;k$03j^bhVWA1 zSz!l3DWY(MGgi)loTUhg`DOt`|K1~H~trRjno@%`VndIfTAHv)Op1cs+L?-@gZr z*TS2OQAI6{JE@T$t@pF=QJ$kOgxL{7cru~E2&f`u^eC6~By>xSMDFzO?;p8_Fd1!D z3*Z&fU~18j{5j3j`s|g^?l>~Ey||hNN(yxwjsIrh0S-Bq>dwBvtS=_fMQ)hL8~}Z3yxnbShXj~`19Ar4 z5#_M%cmbMY1^r=nePpHeaQM1ef{oYHQC7o_=p#I#Hbz*_YQ#+fVnR#I0kdH<4|2|Q z<|7MIX8IrGK1)pJt{8DiwQC&cjj#^gg;xsV^9hiu!U-TyHcQm{Oo5&7m$}$^p2(J| zj>DnuP=EFoi&f&`?E$Fh-4Vj(rd3Slu{1{fhrRooZGWnRz59^f|x=n1X8EC&E6@^&gZU!~95eYVal1qqZLfFja zz)PF_LxY}cHAm)vVc^~~k1E0g56NJI{wZ%#HwH+NsZjJ&Z`BygHPj?`ulKl%H@PC150EYjvuG}YO0XC z5LT!vy-)<34Bfe^OfG_PqNdRm2F}klHpcJ=woCBKPNofUUBM^UDDW-!1;E_$K%p<2 z)w2dF8-upxpDZwt<@BLjq;jj}av8g<^;!Yn{5(%)3}B@pz(Lfav&<7o6l66RK7qiS_A87{LxKNox$b0B0tiBAvwggz=F#y5#55=p9J4E z;do+RiI9)y`^TrivdNPs^B{RbibN8qfC>v#BTp07F3zS&VK7V)x0J16k^EBRx7z1yVsO zpk78!%GN;3#F^xyxIX}2sz}VKusrS!XS)+5E}+d`mYeA?&&xt+2QP{Pr&A;*mgXTU zQH>}^bo$B@uqycHow#+`q+)zhkHh)luK!&7Mhq>X>~dnF3g}DjgWPOLgJNnPq^)>< zN-Xnvu4j@D$p-udF6F81jggjanwPa^88@pqrnkH?^G0V(=fnsf_sj@Z2i&n$L=W3Bf zCoc8+-TuIgS*>)>RdY$QtRN{gj|`5)kc4-q5K$77%vAT!1pE}dWE)bOmgH(^ke*Qs zhLoosU~;j9yi4AIS+`T<)nkR?hN=3p5NB?hgd)j;DiEb<1*OB-PhTe>}ZCvv5`$F!{IN z-?0po@b>O>+h4B7$NeBpPG_sdN^Yax`=I}R*q`tH)0O{ARdbzu9?l4Zy!PGo+0TOJ z@YoxCNE)XLIpDFo-bujdySi$R;3L642KOE^m)pbX`}_73BUFk=BH~D{Wf3Kg9&p!m zv8&axh(BF#;~AKkC3olR!`I1tWoTtniWaePJf*Maa74l}k3sK4AwHQqorv_ZKV*pz z_{z}Ph2yKu8;#EvYX-3EF1f)Dib+VDN4fpsg@WsrN@Db>0lpwTzI_Rz0uhq%u9;&5gn{j;Q50eQX&ix(40w>SnD%(enDg+8*f#a^jvkIu z+W01y*^M#u^12VD70PiYYOPmG*)VB)IztucrCKmh1-%K)OlG-Hx67lSNcg!wbhElQ zCiv+73?;XRM&sFpHz+}?@XkEKslpLJ!It=iuqoCW?*04w+v{7(kHn2W+g6+`bBDdi z?kwMvDj1V^%RrKo7^$pNw@fHpd6$UbJ6p_0 znL6qrNzX)BH>}HPd7go2bdhnz@;pM|n)yf>Wxx~pmk`Y^@(2sfLZ^t7kT2Jb2K~w7 zJ~&>kpU1;+9GjnV5FxRbsQ{TkX1`G#5>0_^ID^e36Up9qR54EH3rDgd#jm{q*iB+b zx*GsAJy3&$RpW;)H*QM$v`+Hd<7w9&6%FhF4D3JqdA`%&k%u(otz=?P?k+I&Y_Ztx z_e6wl1}~h5YzoE)|9vn$j<}>&ZCC=%#b5+Bn2TvKbxc-V%i5=KK3o3KRXXkSc4G z3k4+L7c`SzKyYs9!APcnh*Utz1NF%>RsHaN`zgW6pL|(MTC>l7vow(rFY{u7am^i$ zCbPp4m-V=_fg1azDno@QtIuo*yvU*qooOkj)iN7Spetr+Ux$>-D04}h`3Av{3}_cB z6CBv5UptM?*XGzXp>VMc zHg9x|Uf$lg7_+2et;(?biaG_``Fi>J>zCd&Pr?m4WDuc00lb{MpNcE2rCOw6ac-?{dO^#U-G=`{=Pfu^6hoAnJ*v-qd?>gqvVk} zk<1GW@-RK9CXe?Bs0|eym8^)4g)H)T2A-s7hX~1k>=Z_r`3xnHaX^UclXD#}C%(F% zT81|m9M0YCk(I+*@OQd~nkTa<9T|g-p_xD6kvt8=+kkTH6amo>HQ93i1LmQLfr*x%|?HennyKV zvWP#(GRu2_@@HS{Jm3DS|Hpqh*^Wkw@$~1#mw)op8Hh{ve*{fVrHiaK5#6{CzjBg9 zMg?-Q#KHRvu*-s_nLlj9>*Pj~=hJ1g*-$h&@}!ti?i=H)T?L|Cdq%k z%~3k6Bv3;BB+N6x zYx_3BaYO?uczJmd!sAE68jO^Q$gKEiNZpylTnIi@fpf8-S!|>o&m+ikRZ!=r(^)^- z@SxBm0E0g)>W^oFp@$0`4+q4E?~X@LS+1N#>4(Ffr=#keSL3O`9m|ahf}3s~H!d?( zpC+tuQpp7fz+zFbi+8ZJ3@*G`y zaOV}Zr#q&`PB(h^y@xMD(LF9VU0IH!77uT z`s4(fjW0Cv7^youAwQ^X$RI!W$NqRWU#+>bd;@EUX7~OS1BfRj5^F>~LA_a`+EAfK9c-dey6VBB?JNbSzVMdA&*hM9OfNpx+G&_S_ zs~LUl_gPf8*{E{@WTA7JIKWDslf3eKVvNE{G^D`bCt}p$>g7#+{_D4Q$!WG+af{fF zbPKPGXniI#kAvjkg#7(00Qu@OSGIsihC3K?5x`OpzL3AtZn1t{1|LWxk5ew<1~c1{ zOS{T^tOjdfxUM-M5Rqr~fPS-uBS9IDC(4KQR38otNh_6Ec(0Zz&S#s^c>caSY|q_r zK3%U~*sjolc!tR=qgM5d<5D7faSBvRViaDOo6CO*$o(khr_82YxNj7k ziVS(65ZHIQO%_W5n8KAMq{r3AsLI5NBPpA~lbvXX&)2Bz7Wn!{8atcpMl3(iQmTReTsBb-9lO;0S-m)bIJes!I1W&L|M* zbaGiDkIK<}^84<9yqEQBc^>S0M<=CYr!REK*ny8Vt(Z4EX08+vffr?SlmPVLA2bGt zuwf-GoNZiwy+0g*Afx0`c2&hd&-rv(&L)HBtu9qP;Y-DX;F6m->aLYGR|=^>iqqLl zy@RZvMwXQ4*KOWEK4-HmK`Bg?%Up$cxN2T!IUOw?7lN7Z_wrj$#XF&ftX3EcY`St* z?k>)}YSxKhI;bRT3Z9BUc+S^WN;!f8JTj_iEmukheoAywNI^oV%o74`yYpc%O?53* zQxZj8Z|6Mw14f47o_X{q6=MstproM{Gjml(;W&S!if2RP$Oo!MxyClo1fw|jb0QvwtaB{A7FDHvRm4CB%R^(y6_tvs|)>?>&C`SAX6A>)mKE`p5slzxU?1;7r9;m-gv&w%>PxB9Ze>5luaIm5e6~`hhDlLsO4nsS-F3MNq@@Xki3F zbecJ`*^K3|&nIDKVmr^rqa9jXuGcgoMb*TII_sbprl1T!^sCkjpVQ`ROJCvSFgA}w#XpPS9g=H*3|lK4~CA5@9ae6H`^ z5wqQX7?y83%VtoY$k4#%6vN!k_3ZDuEjYg>I6cM_hACtp-3lKE73%_w#cLb`Sdf^Wsgq|TiSg^bI90&No z_wS!HF4>=d`Szg(5YXRu@5l2=KOcKZ>dr8zncd0s+hcb)-oP4}8%g^P`aeNe{?2)L z<^6EF0NKXcoi1P`ynQ$u_atFt=>18%Z`(aA_}j2hC z>wovl=eLvuzi#))`{mpFwmTc$5V8c~Wuv|Rjd(m`HWRVX)%5|bBJ5ND>XXA6v&G|e zyFdN*J@*PC<^(+tMzZ+n`8dOH*IbX@<}#=1%;^fPzTtlV*bx!PNP2($?K>EE4D|0g zNU$iuf}ZhZ-;wXT0|}$$_wDh!Dor7Ax_|rFKYL^OkI_l4EM>djkvFK;#MO$Z^Zh{u zI+QY_uLE5ua-h|RfuG50b;9Pt@P{d!9jC5-U6kTT|0`Jm}LY?ahCx6)c@F@cY*SuUlCWmwB`>qL|i?7t%p9!=nba;eO~N^59O=%)WE_qmrsPF(nEa43+&}vgYn|K3vb(mwm@WIA+KvP@DO-T%XBP zaqqo;E>{gzHA4dcpwxgdSX}=sXTEv?i|T=%{oUa-nJo|_&ckGgG%m6A`_n`NKv@AW zTg@iReki$(RN&dTF)ZM)EiE2i+uX z_?|>mYFd*&vjKVKpT%lPZsDRop-8F%{MfQ1fH=N#F=e_SC7t^;E}s(Uh|XUHq+I** z%W$gNiAQi^l>=RGkKzR`@9v_dFe6J^Q1-e(VsmS#MwOH`^LW=i+#1yntrIx5hQ^lo?vP1z9m8$(Rxll8E5Gsc%qp7NQw ztXkL^WRuklIt?7p>l^yH)dEZzj7O_AI(yVoQ@rDS?wSi+MpA*-JhlMYC)mqmI3E$B(0$vVBP>4N*^vQ}m3K_Jo%3TFLG(`gn`tZ3GduB`SpLZnb5Eq=92>nR}NH9E>AuXLV zDy5xvxc1c!QDjZRa74*<%`no?lNf<=;zGgZWi^@%9=G$TcN^XAd28n238Xt$-SoNr zB%iht%%C{62`a>Yg^}*}Y2I}J7iLOWj})Q7Y_95^@cFFt1X7=J$59MgOctB@0wzR8 z>kT{Tr+Va&aA-^97&)R6Fton)8LP=n(wR<%kjfU)EX(!51K{D`ynp^zpOek>pZo{^ z-u$QaEw?^J3+e>QR6~vi^T9Ar`OLfzSf9)L!@dX>Af7tKP73~eE7ful4_N11@tL|- zpn?$8=gZY^-#>V|{&V&3!51Odti~KoQ&i{i43Echy7?HMC>8k`tG7FKSy;=)R5u{OTt{A*m7L?{)H@u&MmctJV+>Y|2b zlaJYA-v7`3PyZJ+%UIxh+;cD0Z~!LH>uJ8gk@92v`Mo>tzr4NrNjafpSuB?C?>p+^ zf`~w|9hDKHnP%3x#`sfIwTi@9dP~8SQJ=@|L);tY^xks0iQDuBQ2NW;%egy0F1cc| z-*46Y;}N}%_j~E-qJU1xBNjfXAx5m*)XlZ&KMEGMs_606r_rnGV(BTuo|pdH;!P0z zb^H77vR$p`quX;m%<}PV-qVq5H?+mmBI|mHfzSSED2nOlcs!=A$~rlR^E$KB^}5d0 zBIt6_kgm7+bdG`Zh)SQi*Lib z*5ZQUX0=cVV$*QA)2xgsm!FP7Q*|loIk_W7{`>@NC z;Mr@|^No1s^V{#=7Pu%W5zv>PzP`V|SACqtg&1toONDzn&+1MO&UwAuXR{giuf>g- z<}z+(OGR_F!1K9ydF{m9E)PL^-mg<8eZMIv;0fETZm`RY?EU`v@!?#~9H5hX>NU>{ zjO+)rGIyGdH--k_ZLpZFc$%or*cM9x{kF=}%IDW7;C`G8#;T&Pi_P}^bF!FkPx}ri zn9SH<-BJt(&_$rM)@gTAP<`)w_2`~x&WO#Su9twv)n*5iK4sdY_?vl zXwZMF<&sE6lfnJ6q^rlZyY&yx>9wCn$86@)#khCaer(n&Z77u4lx>+ak?9npgr0zC-!U61z2BXb-eYoGxb)EOiYBL0m9}hWcFv~*o zVK0x|2q>wmoLPoA9)Enkv+Y5i1DScj7AWYH3AM|weigll#Xy)vr(eZO%m<1Au#46C zbo`sIKfQnZu36wkwq(}*?vsMD9PW>KP#s1`=QB&*ANP6Y?mTbzgXCk$xg90E#7-VP z-p$QvJjaI6vdVSjEWoJw$U`3HlSeAyQRhU2L5)Yl@p82s^dGay__tsF zIvEccq{Ksd_nwH`J9dE=fBDN#qd{*@AS%MV94eFUNBytnnWrfOAQovw=Llej6wwn( zueaG;g4nND>r79MC%l1WCOnQf&iQaU$doUu&2ir;0fytOOqh))UpB8F?;l^kenmh> zhVnw@vu{8D{PVr1B!=bk%zmLv9w8~?ic5LU!Kq`MVnwr0XAv?{gen#BWCo9_QH4{& zi5uF^2|%y&gYlc z7Zy^fC$Nu=Db@eYez!Xv|Nf_+SL7qI=`kz8@&FtWZ!r^W7Z@iNof9KyTw+AXZMS>p zbixJ2HAfg`{Zv-`s|N!ER0x->AX?1w9s?D^`^Wb6W&OII8>c3VE14$c!Uor_9$N_jIU)VUiw2WZJNj0x8Uq!`A%`qNB6E)qD z(DwUxNa5e^-yh?_-~Htut;fs#Z|{rQcs%bPE{y8+lKSl_&d7t*NJJPhG{K0{DPJn% zuwmLnN#t2CY$7zkX5Zy~k+RHi05e>=<7vCyL%=`%{PS`(yI;HcsK5XI?X@1zx#pp) zWwp#rkYIN`zTJrJQbUNpJ}$Xrl{ZfMY9MaDKjdwRg2#Pu+N_s@%TV6C-c7KF&k(7F-Hc(XUmn) zrRoMcRWj@8u-os*mY1@bnz@j$i)cSKa=3nU;$y}iEf_lJMe*M5y)znQA5=tIcB*y%VaqmBgI85HX-?R&XxF<5!xI064ovkmTivyiw(*Eyf- z@Af-RC!#uOmWOCPVRt~Trsj_Fh5nuOH-Gc9@&ni|YwGjy?$A+^&?jU`#}o&jYrI@6 zrPuclIei`cGY(jV1n`HvRUY+zdjI}Dn`K1(1Bkc#MP2aIPp|#|>wo_LO#Xg60C78F z2ifr}b)nXO`?r559K>p8(*<3sBk~GECU_iA^UiQw2`&{kdmJWyI2v__y^tY{(wtg? z5P7B;N;-tH?>=UEOci)ML#<@H-5(F9(|Yq##YC1E9u9erx)_n`fq9olu1RqTPJ*T8 z(l$FhXR}$iE3>D|g-_~ZKAXpq>PeR(^n{(X9e@vS;%_;B4&;RuPPqW$NpMCi@dkzkVUFPi8trPy?5}Rm3(6Ro- zf@d=|GG$GKpk+~sz0yJ67uM-4=ZnK`&)Qy=tNAobXtBcaaI`xeFN5c)bAPh-!~G>o zFL$QP;cwgD@UfV$tF}orVF&VotN4JbRC&Z`NSoVdc4Vz?#<2j1tWom3(L$c@e)dNF z;cC7-AMy@hy4c^ZQjqekRz}{=@-*t5q?Kmi-EqH}=I!(&AQc)A3s?_kUtV7&2i}Mh z*8lPONueb0p7|b1SVodVk+BlHsVgUN5!de6e19p=$x!C4g1l1E0b< z53P*GW3J_r>m{$n)@d+SM`W1bk$90ro4O0I%MwG6LA$Rxoqy8G`TX_E+wFXi7zdrZ z=>N>>8sOVR7i1{g@j4nw*_#P=H7Y(GCa>2bH?<-8i`WT9jtWUQ7i+#aepo_6Dj zQgd+2e5C%ZcbhCHlkV{JWA`$c=PvKyaeRuy-RX46mPrFmJwqZ3|5MN2rP5u7d}M*#4r;3A~^Jx zS)TA^plu*N!PfJvy|GL=7;QG6>%|iDY#rvC`qUwBS-W7tp^`cyw-uu1DtK@pR91g?FBhTVZ zGKMOqQKAr}GS>yAxR4qMLD$>u_S-MNXxBmK0Dcb6rXvAaM|$^ZYyCRsd^nG8gTZ`y z8Fz2J=de5MT{1TLm+$}h^*?+4{rivENPZ0fC6>A*X_Uc`A@hdynS^HXBGwj6%5-yO zs6g0x+)u~dgYhdiF1J3G!GvyxA*Wd--nC~C_O?I9pYKY;5#YaE&+|0zS>(^FLi{J8 z_$n+&gbaoS#zMp(nmQeYtY#|VZqz?r#fm8RW&T=kYwkMehy-vu<(Ws`!G7>k2_n<1 z>Ebgl`R?#p$M=UlE@@4ho6ik?Y0_2t70{ZO>8q)X?fXi*yF(0MwwOVr=VL&juEiV; z+PG4+lL0CHqF=-yzyycKJsA3MAX1iKA4$hd`Msacfo?Ca!c^V^BXR-_x$~y}IqdVg zQItKO3Y-jecKS{rAK~F|p+Ha2zZ2PTOr`ty*nB217S{y$@$rrxv@KUJgYoJn;VC|{ z@r#lOlROTrBeV7f`viax_6=`%{oUoq7;4#)8Ok__!93_Mx9dY zez#RTyZWvq>^wS`ZvT-RV*mK#x61&)ntxrsTt8F5$aJ)TmtnAbRyJhe1OmrO9zvql zC_+eKf`h^E^Yep9kg;B_l6`7`5)Bw6-hyl06Zkdgeq4`W^Lq6%y7gxvs&jrE6#(PG ziYW>OFE48nljyM{#34XrRwvXG7b$y!EuYVN_%f+zBzux5V0zeclj~i|n*wP{k{`dn z_eRt8%S*qj=y-rW>a@jdDqD_6lkMSf%OZ!Q(i@i-x0hqDGklq>`!{v$Nxp6pKp`R6(SjDg%lmmhUE&@^ z>#Jfrd(L_%@-G-85ik{ulfdMEI8vk3TPc-g(M#`nIqvi_)p&V<_60+NoK7>#NY_|HFeC{- z(EC68yT5~LGf*`d7et}K+?A?fpbKMQ!{EtmDhRZ61l&(G`|+zEGJq(TRmd zfCPuJCP~KQLY%(5q_mLf97MJ_ifq9L^w++`)X5sMj&QDrPy=@f@^4?hK$47`*TzytZx~UBuh%b)WlW(x_BkDjsVT)np}eP&-Ei>y^RHjK z|Nh_p2f<8R0`U2EWqHJkvA_QI`)@x!e*N{=*<`SsjV~u*ktm*ED2fB2vP8px_%gNu z0o?MUZ(hx61*~7beFJ2OOpU_iK0iO{mD@z|H+XTVOa}@9j%RQ9*WZ2|FOSh={zJ=f3RZ)&hPA=LGvZ_=zTJqGNApB z-P`i(c7OcG_uu~V@%yj8etVhBzRjfPOuEsC#O88D@zSdE3ib;dQ^>@<345-xxh^cfcAAABWxMnm4Cs<-qe}jfhcy@jCm<`+u51T(i~p z@4q7wUkpygg-5n>%kzF6?O+o}7_?Ck6VqvL+`D%lpC2EK#flK_m)pzgWw+f!WWW9P zeRH|2-(HuCX|Jm`+bb+Em0KW5&;8x@U6_fL*DJ3zE7thb>9vj|uI2gf4WZf3;79x? z3)mrsu}P7~lQUQU@gM$)54?SQ`4|8EpFt_jIhD3dkcikHQke@n!7#-8%7yghr)CUk z@Y!YD&syDer*Kz)&u6>sjx=Pvtm`)zi7&q?xXw3yGfpC4eLdQ!ex&T^wbRARlP{^ji(AgE}nHE5;b@sH2bwLAVi zZT|ev|E`Cl!*zV`OvjV$dE1+^>ic@W{@i?sFNdQVW>AlAss^*3KyG(|1G#-aBS5~{ zGAKJ#4^oE!emv|(gNeFnws_ecRc+_f?eex>b?(>YY|^=B-Nv*8;;0$@o9FN{cQe2* zYYAt)M?T0F$nE;Pji)27D2vk_k+$d=Jx=FmRe=KWc2e+v94@>2@cnpO%;(+PX#qH7 z#nn7uSBWPc#NhElJFNGhfPZ-vLD0`s@Jr4Ni9KX`d@V(VFX1WCGs7>jVFfW`y$pK$ z=ksvB%tX}d<966j=Hvb2IGK%!Sx3^?rXmdMl|Cz`XHlB%X1iF%GMlw)User0_b0gE ztMDVFi0LfZ<@=1^kIywZO6;AE$E*2#x8L;go~f*BL@6|I&mzJZB;D7Osk~K$Eo07; zh<*YVQ*%~Pp)-tw4%jJ78t+SiB*slbsH4Z0qm*WqmFhk2(z zdOVLe6?h(5Q@{&Bt@P3+^ElRgAnk~*%f+HXe_&Aai`jUj3c?ODn%C>DZ0Q1CKf%9| zvaLIs&oYbXbe)fefBpR*zWn+1x9>luqb0M3n+enTg1qir%UbZn>%|t5L_BtsYAv${ zh*Dz4ECxq-t50ax`D!g%A~bgWxMe-w`}IYqKx(HC3_Usn*;wTu5gzJ+Ma1h%k1}E? z&wxP&bWq|adW0TCGrdQ)hh4Yx7>xVpTQoi$E#Svq-Y7IvMfD$@&5sY#Mt1ULpEDlF zU%<)8rvja?WsMG!%j{eF20*beU9NYT{GUNVf<-&wbBBsNhhM)eyZ5{tEmo_pZ+C06 z7Le-uaqW%i#YStSgax0vaG@xnM~sLO7};#L&f!N|AWpedMjd62{eUs^(9h2e^4J(5 zOsDH;Ka;FeKuDG;mb#hkJsgj=B|7Bcg7oyR)#K@y_9yet?aoh-*4x_~8UFPzf1%m% ztHhtJKRyJm>tderfTzslHTXO#Qx@J~U&qN`voJu!6<|Gr@P^|_co`~h&R}K5hiok5 zTB9NQA{h4a?d#XyzW)X%5jzxgx$W}c_H!Wgd|r;XVa8il!&&!mXEU?KS}pOp`Ai)i z?3;I{s?B&S@We36LSlZP0!|B#%dFGeEvxhd%jVU{6As?|-Od#fT1@?X%fi9K=kfYB zSv;<%Tko>F97nzNY?8d(uF}4!$I55w%q+5$vPyph&e|amwLOJKEX36x%$Kv9igz>+ z;$&Uk*?A5Gkm+*83l96usGo&;;ey#z1(!E|GQx}aQKO}RlPoC`uGVYOn8EB%m;N~8 z^!9BM9NslW9oUpW%+D5B7Mz_wu5el$WWRUQ#s?6gfa; z`}?_@MG(USgU4tBY|X8pP#JXGA0TQxPBw|59#0+B`RD%M{XhQA?*IAw>`$|Q`M>#} zzW%FUZ?oYRe$8qzxEa7}Tr63*-{@(FU{~34LE{89s1Rg7jK~#5> zQTJc|{lDn`oB!8;E2HzJj4XC~i{pb6=|gD40Og8h#N;?-$7gCyU!SCWxyp`G@?Cv+mKP_Bzy7}Y*j>AW z>91eD_UK0kyc5JZlzf+vfl`bogzE$X~)-@v#Xa%PL%l_Q1sk9WrO z`drOk{`&p%AHM(oc{=>*mv8_4>$kVb^br`JDG=X(|NS7oESD9C zgDh#HKgn{FLf5=rrzH)1|Nf)Gx18FU%vs)nHQ;)+ShDF0gux(Sqi*MYxBUT#Js<1k zdh+aptKH{qzMgE4yZLCUP5RRm>%r2Y z0ovUkj-3n)UsTn9`R(_Q^Zn~DzpQ3sW!wF7Vl-fxT2zYDU|up+Z=p}@C<~U-s&^RT zG|e&@8KeY@#Z#29byxB?Ax>YH+o?O~pU(I3WchFZ-T$!NZ~ydnzx?{kw+<(-`}HE7 z)1(q*w5-N3>i%6D*bqum7{p#T+wgnO+TiRMcTArsU+N8>Ca z@pwS$bG);n^?Ic!6#>3~UbdIs_ua>z|M{N*jRg6pT&`wczr4IG z=BM3o=(g6+H8?L)BZk0ICI<2D@ikGJE?+j2H71LzIj-#;pFi|l7H z%jefGU%vnT9eh+n{^?JDqL=+{FHlzKc>MZz{|o>^iLj0aI9`B72KxWM|M=J4@%s8A zP$i>YucvD*5V?v@Z|zO~>|9@56Tt$Mea+eiy{ze>L0ZFdJ5Km|Pj(6_%KZu?^|H>^ z^0)6FPsD$G{EI(-S~aJ|eYWRw0Y!4-0Xn z6g9DvQA?ub^(SGoPx0eZX6Tb3#{Nwonk@rS} zyf9LXk~zNr_)b17&8IBNaCmJsu7^_sO6;;IG}D9G3aP-K;gqq3WJrIMp%{VND8<6m_=S=bjjsrzyt;)! zrk;hIPTqgKxAH?@nKP34X~D|KU<#Fz6 z_p`$H^?Y2fmV_feg6!{`y{1`IYt!X+7z~%0&6kL$G8Vg9t&#Em{zGnILV2O-Xtdqz z;A99JRSI1gX5Cu8X1m{wr&%0R>|@eU zBWt{55#9`N0t0xovG9!es)`2+qfFKkn*Z|GzrL*3F%QwKZkO?e#cC}~bu(x(+W-Dz z#+c8C{ZsPJr6a*phHX%Fv-uP|{gj*(@x=<` zXaZ0#FNi2>OuFQF5>Ic?SL)3(@4kEdee>V{H~-!BzuV4#S^SIt^}m|`=WqM|U4+d8 z7`aIT@|`l?mnrzt2uPE+{>`$K7Gvq04xgD!RAYsDX)Mr}z+Qx80-<&)+E8mI>NG== zivoXB`2@*Ofjo{(S*MIJD=8CrYfuX;g*7rTM$Xg)I^IFx1nUIs6NLbxkCE`0J0g() z@6&SAwf)~ezKahLiee_%HP4c`3RN!2J7nY(-Q=CeFidVwA;5(lmH_J9AG>`{)-vfZ zP6Yt+BlVylJ6UBbMqLkfh{=5J9AOEp+7px);CI8ZL=BF%XldC$c@>KFgDwgMO zzyEHZ<@jV91R@UG#}U_8o$}5MW_5GFPF=?n*|f;p{{H9xvwzk9KmG6jOX1;qImudD zL=du|uifq>>*4=pwdg!9{H7vK@3`MDW^?V`l=jC%CRk^RNLNu?lMHjOU6DbT0foc7 z*KPaxsd4+RA`gxLJc}tJ-f%n=PWGqsk9X3Utrm;%KxiAP<(vb$-Ezx*D3OSC7|2bQYt#DJUO@6lFxm{xCCk`nSvRc7{J7jNW({ z-@A3XYKYUlvs$ia!@(N#QueS6EWlI2xWVXHah#;XI|)=k1(#@qy6{&K0LaanavzH*^uJWPIAZ zUZ#`;R;6=v}9}MvtsqpgM?&OZbFY z{p#f0VeO3$$4h39j7RgyXg=sZuDe0!_Uzp<%zwKMI~qAY?q~Wi{@DA-A_FJrhL^R=lKWNbT zh{iLgd3#wm`6woj`wcpEs;B4m;t#GG^*Q+vutrkncHVq^bb5p9bC5+&I-q}7n|X0k z=AY}7U}G?}5dEdry|IkrVMmX-i8PxqPF8WfoS6W1BO{AuaIuLxb6jsQ=pVKl)Q%^) z3vs@lp{vPo@U~vQ=FP*M*>pruIu(1E0V5=Bts((0HJPFNxSWTKElM1}Ea!QQ=8UN; zwdQG#^Km(!T~0@SjRpdVDXJ5)^5$^4SST+*v6=93J;IlW7ekOuV)9^2EAQTlfn{ly zem8~zd57(c$#K13zJ9?ZRZhoaMhLykz_d+(($0GadF)$YnB|K#^CB z`O0w_(o)LR;BtZZ)gp3T&ZloLD~`_~<%O@W>*ZWnzD|by`DC_OtTXL%mukVqE4Dfw zEap1N0N83iV?|tvzG97>bTa>=Q4bpzpcijd`|xKGM{#D zUzg*5@yqKd%g6Ki{%4*x=`zmO)tod&UH;x*4!i4dcQJUZC*7B6|LaohJ1$2ZjZC^% z2iKFHuU{6!`QY(wIiB>czpTeEliure@-m*jOs9FKc+g*GwWGl=dBk`=>hx}pE)0rI zW<$tt`kO(>OsB`TVf4j}Pcwk*kQMpNrxPc=&4zE2{{a-28v9(Nz3-s6|o`Pcbi(z%YixAnN^WPJT+-(J?!!OML3 zGVRTJS4@3bj8@~$-@Ps7{rj|k6YmJ>&u@!g-xja);Vi1=qN0q0s7>yo`G<zCPVQiC|Jv+2uZ^2=)J$X9`)*PjhX$Ng#28+_pqkLNei z7SA7#H-^%GeqD@SXTu4~Uze}b!Ft@EcJE&}b^pE|MP;i|_jNXk5GEQQd|ia`H#(hk zUe=T8SOgfptO#EPG8hh_qsM40@1NHz!QlAyjb@(nDL=T*=D^L6PrxJ_m-y*zoEj=HmcqSuID8`*ATJ{PMDX8PCDz0hBz-nC|fQSY$D^=QQ{j=;qhF5DFI| z6i59z^mlvw{vZBvxVFBial|x%R3zaVfg&PXUP?2D!G^&Hl8aFLoXsal z4FU71bdd1%xS=cAp#Wj}zB`gsB6>T51_v|KIOTkBBbUU{l27!zAe#uZm`^!q;SECI z21!3+aRP~Bou?pG_q5IwA(1##_$BicYFxOJ_erZ}E++@!4_OVp{j3Ait2ll0M{5vP zV1qP~F`c|V6`|tle)Dlx^6od^zJ2ZgU;i)vXGLa8^XaS}1L!@vYQD$icHdlX2JTQwC=mD$SiRtd2~ zdYPp}$?c)s4!h+|{<>2+9FOPcaQNHjelS_C*RQ;%r|i6(wRz(J%T=dtZV-hXYScn* zmnyh3!y}^ze#M*eOKFA6F;MEn-V?8f!=B=irkt8iCmF)JKR-{GM}It@FQvXI*rd?9 z%f8BgY84v#fFSsS=6h-zUYY?&8Wp}^VU|bEEGc+1usVZS(k}L4S$`uYP7;IR0X)B4 zKTr3R!-Kxi4-HGVsT}$l?sYlwqYp!vAxZGMn9^4saFM-!o>n9AD#^>`=M#RJm(lHA z@7Jt-aqnL4oz4EJ(-il&%QqP@3qE$$az{W72pIH^$HU>cU%$M_yso|HnJsZ4&x)K* z7oYd6By*sFLKk_{3Q<64tJN}Z)V*V>cYAg(kM8H;L~6_B>Set;A8LkrSd2B&JU>g} z7wzY+8Cc(!2^nQ>Eash{G{vt9#46%dUtFBf?!&z7CiBr*sjsX{k^KFAbH1xiM-(*d zTt}T-AnPN`koBKk5u`?S^Tu*|XY(3OaHG_lN@FSA%d0~p!@!XdR9(iOH#dt20x65$Z}ti$ zm5#7B8$*qcyiWFUhsmIQt%;|kk_cOOq85ly{Cxq#|jll8}NHB#nF$J2fJlU(Rxys>x4^HxfIa^5$$` z6PJ@YIBN}gWXgnUEerAqMECP~+_~rZIR(R@uRi>Fex8c!YmhrQu0fG~_xa(|U-D3j z%&Y%(#BK!ySEYBEHZR_~v9rjb*EwD;A3Kfv@>KdPCbW5BT<6nyAxDpf=Njm7mDV&u zF35y9k&btqssu!j(_zoB1RS7K8h&0+n#ve|s+6DURyoRtwujy89F|oor{aPT#DHY1 z>vau1WqQy}8<_klv?@>KM7k&)-XI2K2nZQ$P?Ybsi^cSLJMn+j_GCO3Ke94UX1-3~ z!zpcMCHo8yD|1;6aHEECbzCe@r{Cy#5v+30?bG`SfSb)m-L!dnnNMC9)5~GAV5X1D z;CcJ@vP{i-J!hT4EJ$_cK%faZQ}ASvsU+Roaol@Mhh23ERPwsiUT--W`gBBT+_|6U zqr86KIm6y#*n!)|vr+%J-7iMduWML04`wdLUh%EKR!0>Xf*-57vuYVG0H>TudC^o^@7mnY-!}GurnJf zs0d_8dOlaPAvu42$xG_>^*Wy}r<2^s+V8%;EY*I?$w+t598$X#sd5|V*&*Jy+2~K} z+3yQUVlCY}Va^tF>i*N~qH{aKuK?BN1O{raMgW*Yi*W;E5kPiP9J zr|4c`soR-M{_fkC@t|X8;rz)E@nAUv#yF2hYW8S>;Ze$ZIa9HB?^g!c zRniRmugm%UoXM-WaF8g-8O_8#?svbeS2NXx{N3-_XS#w$N_47G4flI2+4qT5UPB$DZ zq&7*qONsNp`{nCAt9bTSvkAvqEoaPu@PGaKrX5=CJ#W8$`yx`!N5ii#exLYjmn0V} zFJBhO$XMY7*TgOwWCVfBjR(*5Vq}MW@FBS@=UJ?cnr4&Xm)8{&(#Y4PDzK+_-1}Qh zxNk<6T#*5^XC(Yq7(YUStndi9Ac?4%r zua)KK4GyxL!FZYxSs{Nqg&YKL@CZsx{_pnT(d*+eTP&&yNG;$~ zuBUTWG8X>=Ug}ohJrn*iW7v*5q#rJl#R0@a*6;5MGA}Q0iE;J%bPZnXrN3|%)Zr?X z4{#KLNEoQiBUx&qJFYKR*y*M+$rQiIXt+Vk>xXKH`Gn0M&vddWarZ~Sq*-SdD) zCjW?!Qq}Qzkuw5rQU_$^1crnaDI}0KbHW~7#p?`Ke!w+8YndJ3^_k){^A?7qJQG9Y z*zJr}LACbZ_INowxWY*I4pW(%n0W$t%&T@(LCQ1q z0D)vhv-IcW6c||+vpY>kkLB{^{_KBl_w*#~%Q_RWq??<(8c~$68718Qun#vP^UsyA z0m|IIqXW#I$)_&CK)^HTlR_#)($w{qwW0|vGhxoR_gz+^T&-TdetGS}wcQlNS1iYw*b^{&U=ZU@1EkJJN5c8AlC!}W82TCK#W(c=!0r#9ry&N->#5S6*igo zU$}p;+w7L}g$SaCp*2YKdOnf^KT13?C98?%+s9*QcPy12I z^-RmA#48JXJ`cw%l)g$5>dFdr2kF@rD@3`xQ7Dfp@i<7;MUn4A{f3f<8j0B;!%v`R z#)dLR)dR)JH$wrb<*wv|--xX9*dC9M4s%_=2VkJI8d2xL34HgK3n)2|m{>G9OA#Vr zj6vay9;L6UTb^7XRU-O=BvT3ZN`Mg#eZnkEaeulBzg!i{T#UPr(<9Y*h9?MQG9KpP z^}GtUOBb?D`7t@+PXU};G!h^B$xhiq=47eY`{l&ilo55esdG7 z?6b`AaeFv^d3j~K%7qMPYBTlAU0t!;e5{vCEYO*#aKE3U2bAD>dxK`&U$ojg8*JB^8Dy#>Em1H{&_k-vI;HVKfVg~!-rCJx|$#N z2hOfd?NolBkV#-b#%6{L7%-q#dJjzDqFH_9nOPQDauU3T|Ea$-9nSI=nBMqwl-@Y0 z!n?DcDde-r;FRu%Db2>c;gplXNgZi)npYd%JK%Q}Vr=k{o2;mu%;f#p+Yr#Ihi(@u zEDdunq`Q!CGcAy_Pmr%rT1|u~(QiB-&wJ0_{hS59y7w-jEGCQfJX2gAog3@I(|na% zZ?oluh!)G)LU_xNe|I?>GaGQ%S&#E+1)yFo=kM>g|FqHkpZ_QS*~_2Sxh-vQt*tNlZ9!V0RHPyH><_ueAu~8`GH8Kk-7ytA!acJrc>=qm_>&h z(yq_f)!e7D%8o`}mb7zo&X@I4BhTyU+v_S-1hI^TbS2rNYH#Ib|3zHw^3xG1DDV`7 zXq%5|K_x#XH4M}p*FI7eCn_akw4-@d%aJQ^1{fOZg2@AvCTaiea`IPI{j z9$U|+fB@lSurROW%^b?yBCqKZ%j>qkI8jJ<~ zOZJ%!8MK$cBl+3MP*BFPpsQzc?ei&rI|tP%Vg%M&2+Y?M-?TIB;@J{mqO_pg9wVY*~aj4;Ury=Go#rf}_)U(2BGIHYhuKtX(?u z$C<_hP%P5{hmogEsLa5u{7$#CX&8#kLL%q`!zV|A#ta? zs)rf!IPv>74@C*K>laa=H_hGO09~|PuL`6*FumJ++NVuq)Xj{Zl%Cru*Z^@#U5{r* z<&#u0sv7zNi2!SG|MA>?ZXqFZ!O``8o$w%G5Q(AM6 zNMaq0vc6}Yae7=c5h1MT_`DE=7-qd&zL^A3=RU{daXy|uKR2{M$Tz4l&kv+D&=+>H z3|6nNb^_2e4DX(ffrFy~jH1KnC za>t<)I1@aSS3V1}%tOc80;wx%6?*G*=7?6Ewi_Dpi}Q?L>*1#u!FY74=Vf)5`Z=ZV zLvXS;%!sgvdE4zbr}J@tKI=%nfg70Icr=Dq?Yz9KiI=qCD)QtDK1)3J#cFwjkTP*O zFLMNJlnA~u8**?SM%XNR*eBsTu7ktj)B(GL%u43diHrm;Kv9|1TK3G@D+1(hgW^g6 z9}mZ0Ufuww$P(Gt`+l?2n%v)}W0I@SfR)@qP|u%P!t3i7q4Ijn(;ad+&tTPz>af{q zR_X%hjJILp0#4rnd}A?@?T+5xtsfC@D&`-ecKJg8k4>O;wc*(JmtMSS^P>S z9uBY*vbd^v0Skz2`YLzMqu_$`<#N1SK&jJs0<_&y2(g-L)^*{sPChfI!xk$faJ`;s zQzH~91dYad%%-}CJQWbL`7DDJmowuju&q|<<*i+@qZ~rY{F%<{weQ{Fv;~$ujiEOMU(qA z5A89b7+ngg&c}~;!f|1)&4W)DHmNE44i>))p}Do}7h*|>D>l`w93YqsAiXNd@=z~+ zb*E)%!kKxdt7Zmc7(rPbFAF=09-+@ULe%x7oS^o2dd#q<_ICPtt)ah1Z{%;Wej82~ zcKV~B6quKu&zU-Sp3Rmef!uk@!yu4(y~!R@9!;0^I)!1U-XnhJQ{&lSvH%)^>@#o% zs|}{luJWL_`to(WoNrG1_x&a~Xf}TIyNBy}bKL#DdA|XMk35o#9m2_dr~3oM^*8Ph z_U|9J!%-t-&;C|e%?mgCpU3Nu{mF$uC=@(dtWLLQUivT?cSrNVbos3J8xAMS;bhSxmeKrj z&l?8&(?x%}JU@H8^W$2t=6npsy~*-;>m6?Q-IXhzuf2!5xig-CuEP2vg=!pCDi~U!3z(_xkK1cz3!I@9}>BJZ?|-Q-3n(3_G{p;{ljY z2amz?ct78I_e<|~>RivAn~r=vDc?Hh{-iIXjF+p)a!okX)yp6Y(48;OEZ0vz{+?X5 zo0k~ottl7c84RcM&S3PhJG_7FAkUBYJ#BTL!}IaJ+j2vdiL$4Ey{NZeuIJur`EA^v zkGhl9>}Av)A3lzRJ{!yjk74)P>#8?XtpJXS0GuzG=ZJQmy=z{6GT81 z7@YRk-~alZ6Vy}h-PGgV*?6)Z4HvyzzjNt~I-~x5Fzk%<@^k+QM-98f=X|YL=p8Tn zETho5=<;+Ihkrns6WJN~9`+vBlZq11gtuVr#dM;yyk9Pp!SJ#_X13w5I~_eUs4UH? zOBbq~LGN~+4!R3~I}^zs;~pNnGj&Dx(QeTwI_yX)YKE`tMQ)2t1`A-em-`h^eMZD{ z10Tg5s2$$^<;$y@H^8C$820bo=XqLVJs_47yq9`MTc>o;Prx$qc*qlrf0wVTVn? zM91Y^>C`=L-!mgr=DeTBgXb7My~oR9LI|hr2L@+?a4~M@aXDyH3(w2Z312dKy1V=M zUD?p5yqcq{h-~L5Hb_SCFz8$}aTkE#m-qAYdIVdZx09X6_2}f=X^$h#1NwR2bnYSC z`)KYvcpv1ZB_}(pEEQ%7hA|L$Nie5P_3%Y?(z)fPCj%thC#$@l^sK(oG5pWgQWyHa z-<5vE_NR;@vJ{d?hV}y#ka-BCKVdF4u64Pydf`;Ek!LPqz!i3qjcbsvJ5|MvrZp|N z)489n=dJ9f?c6KvVBqPvI_z;h=f+F#IYLq1sURnr-FDuB`0Mh<^+Q|SJ7pf`F4@Nmd(lxivhnbWX@YaJQ$N@zV zz|%*?megiua77ffF?zllcspDSkQPK^2(Q_yxmGRu>H@g$jG! zx~ZL~mStASQAGi(K5lu7h8!cW1p7a_;DW>F3`?J2h3M~c)LKU4GPvmT%u0<-^ z!UGzY>6S>%@bhFw^RlXIV?r6-JJ!m8XEjUr2@O24fQ&Zw zrq1-07St{5gF7)_4CWD(-0V{l9LswLl6263+5&mcEH1fRnQd8ZWE$xScxJSwUVSu&H3_we2G;nrYZEa?gzA2ypy zU~ZpRoDX}h6wAx~l@8J9H}awA=XJT5ak!y)!x!kgI+`rT)AMK z9Smg%FUC12oCzJb$8NJ9^oIL=7Kso3&5-AKKAQudsc4m*_xtg%dwp3UNHw9->C>_( zzgj$0Ux&TuLvvUQVC_B~QJ%XJfCB{;oN;dGFV2z&b*j>@r)2XuU~axq6dG6%u&1)M zDHrGSh0|2LARzMrC{v-0hV0jlYctfUB_f8?YSekTSY(v@mIZvU?eaKk0DVRrGwKMz;)j4?+?t8ko6lz%@2|O1_SK5HA)-HV;PIGDr;17k zg$8|y;fjW;`BUi$fx1(XUak*O2e0Q^`Hr~I{Py;ST4d8KSiRpnGbOAO7=RAO;~BZS zHcO?z`sX~yuQ)o@OLjq9c<8~Kgp8V8o63N)G6<3ve5yJ~5{HP@qoTr3RqEvd^gKm< z&qA4El^rI*cU*%O{E9y6o4g0=n9&`oN^7-(3O``}#DyrF?QGlqOP=;t7*kpQc406w zj*@=Hs~*?R?eYZf9pu9av7GUKJMVXS5UAHXZa4SyNxC`g_E}YLLcg<@^{da}a7@_C z+0t=k2Uinb#akZD%QEaC6b$l)yz_pm8$Zn_@^SZiI{3?(<5A6q2LtYh?JgrL7kNa? zNO>omD3Dq(vK{t$5H53n?vFgMk-_?GEI*?c+5D4AlF?)|jRQ-g$zyqXSg28d=kchx zKYPV(?qB&?AQm5xblNK{Gl5&UyPOYznwb2u&TUJPMF@p6BvPT{>hFAzIxDd3Plqk8 z<(KqUlllZY?UR@8NdCcLD(;Pks?_Ov{$&Eio*yHjBR6DtsJOiA3vpfeH_KmNPdiy^ zAq`05r)`aN%8Pe~FU8~$TR}GDKe$S}AFJ`qoxVdt0}*UF$MIw_10{j0$!flo^a+-* zo;T+_wR1Xd1WVZWa^6Wp(}~O?)bqUa{dN$ifki#uuKVZx2*#>UJKfuGrmn*ePykiz zT+a_cs{>n+I40ARbkE0X`8bw8%G_+m&t6i^it5rpmA!oGG>3+BALsTUE0yZ=20$-} zso#5<>%21`4PXGG0^-m2!>x0bk5PH=b<{Z*fl#15Y_vuI+wG6*?Qp%FM!AbjT)25o z`-7KR*81q*AE)hZp4kFVbz_2&*cQrI{Y}uIpYtV;7m5pfXR8o7-8LIgM{p2nZ%Is9 zB`LR3ClUmCUoLZzCR20qm`Qy!9s_2Qc_>lEGCCa&gp?up!9d9^UM&`LJ*%O4bhn!a zd{d8iGi_LTpV34D&N|B(0TBkT&~S!-nOE){K9m7aQs#(s=F9ns=(>5xtD zoHNd|UL>r2xUR5A?k8tJb~OC<_3QPLr{jELhZ!MHk0BnMx?gy7Tq8?|cZ7rQAL`ai z|6l)C|78F=104nHL?U_h=2kitO!)d#p{gK-_5wvk|Ka1=p(OmfPP!9}a=RHHc?vt@ zwR)7KG6+=JkQ8V)l90_3UxpmNlQnRt@F8gK_xq2}k1v_FGFQf-FGG?UzoB|C9e9c) z>W(~XV^0A?xfh-l4A}?=$>Y))?bBm7#SJBe7#1lvj96htl#Yk(`F8(Lzx|M^Af#{W zS?|6Fxa*k#Zk+6`)x~lyMK~fTxJr6rzO<=RR-zL^3_a&-KQ{)lK+0%1(%YaW6=bSW zFKa%MhD+}2X}_jUW@1nbwS=Y%L`H34!i6aV*n1F6w5z{Mpa%y^ozPP-KpwK1 zpxV#9k>u~(!>RXO9}dS$O??0Qx}N5yITMi=f`{O)nsU|vObQPmz|OgrA)+{{iNrvk z487%qah}@DO51oIW!dr@Y?u@J`0Z>ych}2xzEHl3#1ct}N{;F9|185VaVnj5yUln! zpv^oipT!pQFfkg~0|`e=1%ui+C`za)R&r--tn&Qqc0Y3;pSP>j`&0ib2C|A&R$g-= zDY2-u3GrmP2Bsf&c}RRZQy`xFnR&04%QF2+i1{8Fnuk>0#8* zo8YGNF`Yg8qu+k)uJ`_GK3=*sQ};k3(tnB5Tfv-->^nO1N+Jku;$F(=v^Ycb$VI*x!+kYCNhfbKnSdz9A4I|>NYDOFkrq$ z%LL1zj(a5^TC!dtsV?T3K$Y>@Xe|gE{QYCnSEgO*d$-#|hlw8JEFpkkp>?`Fj#9GlSolJ@$YMSNTml)FetXY@#Dr`_0bv9xgMXF;3wp z2(`}a0BM8_j_~EYdVF)5z>%ZD1j2ac4P5f_J#&$`MN*bVTnp_5+W(vtw$lFm=gOFT zHW`$$A5U4`xGqrwg!?%>-8sj0F3D@@&>WIpIBZ|QS45a?dcf&mDhx3 z6sZS(LSF21u9^f7vP=PM*a*7;M7G?oP60OLc6*fnsNib?H(4lerqruPx@*t3{NcO3c z&wA$q>GQ!J`ojrecL28p1<-#nmw0n)V4B5;{JA!MUCSG|wi=Ps?y#$@FKnsIh0q%5 zxI)sT#%o@dYh9#zka?uWBcs&)+9Td`YD)zhbor3SygYsPR3&b#4v-3 zeBwU2fh>HE2U)Bge(nNfck~Dzx;+DWfffO9c`^iH#LlWVn@>WUj`}aF$=CJbvfnJn zgZ<`x@O;#Cj6Qn>Qn7#uS81~*@QjX%!{dIQ1qL26glvRg=O_>p_D)07z;Q`}vxhLh!rDKEpoCBinPN$^C0AZ_t(P zMt7m%a;teYpx=!pEGBb$+wV`ithGfugL%I@?%b4Qx#iIxsWG0{Q;oVx?it3@EJf#0 zheXt)qNnCuWl;QjI-XB^>@UT)(05bGu^sr0pdSHlgYD_CKkgyE{;($^j)s5;nH@ob z)N@G|8L|QJk|xAyF+T6bxXc!_&+U7PF(4s>Oa~YhVQ9!y@Dw@s%%m7WXhKJBQ=Xqa zVB?Vm11`NxhV0~}v?A3Isz1#Z)5B@ET+Kf>@1Ueg(#4su_fWDpzcU_?B>)hNCy?=} zxM2$U;ThuI92D;V^zZ)TVAC5-7k~a&f4~0q_0qp)9i#g#cl$5!5_6MQA|ZuCUI;C4 zSEQ1l_X!{=*WcgY`K*nP*Y63oxo=xA;F9%l>-9$gkWk-+v+`0=OW42OeDp`!x2j4qtpg< zgBm>IFwfmFui|#?;OU1+-q(;YJe&x6xrN@JU2j>>dw-NA;~Xg7Zg*rFfToF5hG0;Z z5VfaEo9N;F@`i}T zN`$T(&;+3vACdU=R__nb-FE0$65nihQl^&4>MS%%?-`N4oz91v^rTp%;uM!{N=aa7 z_gPEfjsOprC^u`~{2=cNxMimMHM1>2$j<(F%Jbwgij0JTJMv|Q(K{LJ-0pXRm7-RI zqaa9$<#Nj87ssJErSMUD0Q$4+Ap`k2L#az<_0s;w5EL6su8&?OXAaSErK^4yBpPl$ zx2YGdfbwm(f#C1E-9f559_nFVF(rPOJ0rW(f!yi^dNrBg_JH`6A_6Uw5+xqpVP2m9 z0Mf6@k<-Nm+XzHxeBP9vfsHiXALxtXmdjPCG6tYE2bJNm;lWUrN_{t;>5`@KdXld$ z1f^P_J=0f$>c;iVNmnien_&H3clEL!PbYq3yxJhfTIaOO>LkMXaJ292sn7XTX2~spbBZ!1-2eFat_O`JJ$1nBMdC*14)UU(F~cDmR1Z(*3x+wF z&6zBLxCZ)k#9ur%@p-h}*V|G%S=jV;xu2T_vw-_^I-chBlU()joI;S(*?Rpl7(!ot z2?Gv0oE`)0x3>ikdPlff)E;HTJ?OO$Mr3XYn;K2Hz_rV|dtzeVVa_tSgIY*^OTjR= zG{%ZA%u>o6DmTW*)8Tfx>^9r`%!AuOfDqCwuyOc7z=rf7nI5HuvYK-D6k$^ z6?Q9DvFZfbijxU7aqJ|Ij3xkzgTa`eE#^yIrzA*jlVD=B&Pr!YH?eH6S0vk!+Y<-U zbPK`kM{M+o={?YK%97!(^OH}$#r$mS5E-g`&e7{nmXuW!=DPJ{0&|+pmeZN2H76F4 z+ozHvp#;9?Dixb53$pGG+jeP;;g-eP-`_udRi1G*a^Qk0`J1{l%k8NFM4e=j^X;T) zn$7bfaOgj`ce?kBlK+&o9U*TtBVLqt^6T5%dcB65mEl=_X*Q=vy>k}q)JRa~-Xw6t z{9Cy0bU{FB>5WXUtEXphngY3jcwlZaA ziY5q0m;;fQ@h;~}a+gtWXJ><0G=UuWvb zZKpBULU^W~_EM7MCYR`(mFUO*#H+<}EsQu{6uc(HUs#vCoJv-mFme_154h7gZ~_jA zDvW~wb;s!_kriN+j959TF+sLOSuxij5=eVmNq9m~)A@{1ZugRW9!QWeajtzRHNH9& zW5u@qWV-AP#>f3xqN}`0#R7gpX}>#~jpwSu{$NU(f|i!0W>KAph&C45@7|ty5#(z9 znj-BCleLgHL(uVl*8OIi()wfj<9grUJ4XfKts{Gm^oy`c?s*u1(Co_#?CF-*=5<9m zApwBN!s)%9F1Q7wV?Lt2AmQNUc6D0m^0|{?w_Ey78hna=0lZ|PVZ0-itdEED%lbd)il{ozzkk~lY;uU68QCbH;5clh!B9jX_dnkoZ@cMj88}FOXOnQFYrpx)umsesVNfALrjin~S zI1#-UCCg$qq2Mp;*SFP|)pDlBn9ph{h`i>0I_#-D#z{M!{pPb~pxo})JfcImDZhn)(KzTWmZASVwl~VG(H^iR zpE@Wqi++hjT@_UB%JM0(U9j^B=r)M&xD6uoD=!lyeIAY(Ii1fl@KDZ|$K;$3C8i+0 zo-pR1OOy>hwa~_E{K-(#k>D3!PFz0=9uz@WP!)Ytipvm&zXU)`2gis5P7)L=5#ha*6U7u+kJSY0fbBNO?R&uu~=j7ck7(5;i8Gx(er(LUSJ7NYj zf)P~VQua!ns@p0t6QS}pgRJL+J3t|N`uRrjY-JEXbt#CuLY^t($Y>QiK)l{{tP*pu z4OPN$C`?h8=@}EEKvm(1TqOeBIP;?oOFKD3_*wZhEAhp$lZmUi(GyYV!X@%(0FbDk z5$;T%@aG5`MQuicF1*kguJwfks0a%L=<8J6sOq_KiV-9-4+3vBx(%JK%^S||f(6}$ zg~?eEk-a1Y`ow`1On7D^D- zu;6mJY?mXL$az}g0UOImaC)CiY9*grraFYdR4f^3u-Qj{mOnq8h|AY{Z9-~v`#j7e)5v?$g8xb zqwM6qQ)KJcL;yi115JdS=$&wv!K{QAVGpN{RE(mCelECS3!SfU(M3j7YZWdnQ5n~Eb^%SThnq4nv^#?3++8-a+%&(!2 z$$Ywcd6~}VE)ki;_SNgEN--aO!lZ6B9gm8CikkPwahF52R{IkQn0XnNaiW_P2q3kFATZUA0+E4jGEH2TP&s09r z+2j8D`Vu0?6Y}SXHD4|*h!)8US!ArwZCR-hlYyh)>P}v#(CuzNH=pkx`|U3E$dK`7 z@t9HGc`SJ*Vv8i&Av_)++7uqlj5RtEQpo4?be~y6OsF9T zc;<3>Tdy-;DeH9SPFe1o5nQJK=DDYgLuInbBXvUN>SalQe$DjF1AVtE57(Ix5kWnl zi)kh{uV!GU)A^vX&9v=@nrO9HCY_`dV&*r>nFxFkFGm(AGKXd; zkqOJ1BjE9<|Mt4lUGg7a;-dTq_(|oPn}K=BnV8~}k*m1ar3griIrn~Bua}}v%&~$s z6qOuSgM3mE$z%>gNq*IxmhH1_b|yUunlcw3PtMCoc4mEvBg;iz6!P-& zf_|)N$F-76^~Luv0V)9jv2~4r1rAo$$mE|md4o7bbKT8JhR!=&x-J>bXWUrzJ`glT zX(23p$ryR-O(f!2u>@kbIEg($aFY_D3(-A^Fy$YZ<2=k?m*4nTMaK4zYwo3zg<_=ZeO4o!d4E zFz4MS_dIz!lI?Ci1cvSR}y_PC-US^N0|>9pYn&nI0cg%<<%q_ z*A5oOj3XoW;)EhF(_Jz}N+1~$K~r&N)QSMYhXilv@q(>b&pEmv4EYn7$ ztyvnVJR6AO_kxdHIR;-qB`eJGrNw+fi)ufbp~U(OF%jP+Z)2r3t!aXMD2wnBm)oz? z882|$@8nj$h716bag!1W9}Y@H4JIFRXbj@Qwp=bMKE%0vl7JI3pg(575p(sZS%P>B zNBp{fznm3+7;!@DNDFalLQr{jva|yq!yB@NM3-VMrFhD^#5aE}EHE>o)26Rgo8M(D z=SQ`E=Qz*6c1I$(Zgf|%F>BEhD}gWy*YRS8Lr3e21b6bFY^y>~bsHR9sqAunr>RlsJo((!CMy&MrPL#eF zRbMhFT_<=r8%b%W=AID<+OP>N(Ee|~{dKe1ER`0S_FR%wT^#q9DuZX4#4kiY_IuWN zV=C+ho19U1FaRkf$mh0XBC7U@oN19c8EU`5ty$Km88OA=_p<*#VB!=^o=*IvtSVY- zHS-%Y&X|Y-4}S;MkZZ={L6#96^^d20kk9#yIFPXt_n-mUR#ay(u}6ZQ?vMd=p^XyB zEjz-*^%A33Xen|~c|L2w5gg$T6=*(RvRpxf9O677WYGSM7|P3N=S|K<~=hQVI;AHLBbLF zIE^`Jc~Db-ou>WVE$Q_n1OOVEWB>`5vJY${WaTbICkvqUvr3J9%?r@ZcwMiuo~MH; zOtY>u4je}|+s2*LCjGHt2P5#y8CuaGs^D^+rZ{5;Fh`MHlxMK4%KMBQQ%zG4AJM$Z zxpdsaxT{pXTA(9_hisLS$bLDVpnvS(D>4Dngbtp&Kk8P*P%41m6g!(_nWN=;mf)FE zPtjAixpk4c5pak=G_vELDp6rN$<^o2F(;<*H{5`#Y{gNMpCh7)BhIurV)K=WQ=*8< z+=Pe>Jf{#5-`T6CA`1WOsl^n=rBk$=BtHg|Vc%@6!KxgB0{OF;&e+>=y|)byzB+-J zutRk^);(8cbJ!H-LpJk#JT&j*7KL4lm)5v=B=9m@6+d&DS{dPnzNGgDm;ENBULelwi z6Xuc4SwNP6RFpj+q};fOMdjvbhd*N!nXHf~SRA9wJ+SeJD+MKVI=8%+u3v~BSm|BP z8p)jm_Dqtux_~Sm&AXCm!xXVNV!vt&3nw*jfv|VzYJU6b-gc8#^}pHaj?> z&UOhx3ZF}wDV5PCnFAJny5uzl8DHlkMnv*L?xO=jl>>^D&SAgJ07TMy1~^>g zCwvu~5sTy64INn_bV}Wf6Aixd0d&ZP&UZZ`?I1wcDz|92Th4orH98+SXgWfHlO1us zugxHBjx@W~BM&Q+&KrxlZN}B$CuuuRHD)Hnd^#iB>*<`~!)NDm%G1AGq0QO#KY7rj zji@2uY9}(ZQH_3MU?pOG`4<7vdluu897ZzYCGQUBYp%-^d&DohP@gkV&Zit_gxnK5 z)YL{fTNj%3tJ4Vs*$V|nZXrx3v&>_U%%!I8^BAum&1Z8_LO$H?w!7VyH?lg71VWB7 z8lp!8+t)m5cmzzXYk46BKujjnBZ!&-qacG`Z5~%{a6{)i_LIgcSx}kyhqfR%O-rA^ z$l>t)`wyK;1J6tVs2V`g+v^7v^@oY?b+;WoHr)B+R2q!!#o6ryk_so0S%#B3Qnkxz zaDU8mizR1l1>5=LM~A}^QnRl~C;^*|-)c6K53lEp$f9&Un`NHA8oMTvm2mRL5-I_3 z;TY8BZjp;~OICZ5PVU&Cg?H0YJfzoKt(Lwz?hHIMN$QRYTE=H!+9npFsIcRN+&|H& z^O=pRgmL6HOCEU-4&W5;KYpAJN7-3{;mnGBF%^W6Exx!iE_g~=Y?qo6HBwo1LU3-O z39~&%BI9@@-Nv=iAMqy2kpFYD`S3;ZLRThl%<_084rcSV9zZz2AR}Sev=1xQ;#5x& zAP5o$MdkI9#dG@^WFf`?xxDEj_w>r|0l2LstplONv6ij%SHYnk7!@H{U~(>1!#}%(A4rY>7CM2_1$!X~&PfrKyK}@&=FCmtBKpMsUf(`4TBvcv z7SUxt;vyl|%km>7+?+GV5>qf`Ep(9)B^49E3(F;xCRs=>3Q&NHqR@D zv=GK?(2#OT2&UYL8yU3EZB_9$UUk3Wdc9hSs}P|xQIa8CDU(?79+x(FHANMPSy{}hH(gEE$AuIiz zj)K z?kt1L`Wb>=S)>?20_5=fZ+}guJRT9^$RW@xO{97Mob#+>B3>*Cl+W@rv`Z$L8JqW+ z2)Sc|jr@~nGSCn@%N#W;^PwSGOH$6OHP3oE%ff{)$%>LvtxYDWB{WgUC)Og2bULpu zFO9Hv6*eQMYb(WvnRR~WCVVrMq^=*SBQj5b1WOk8D=n77Vw70Zvaf#7Q)Q-%4>?{x z1!QZa9nlE?cOmxpKXN%IulB1f5;5i;MKByo6V8(1Gm$2%$WbVp))72`ie$%{W}tb0 zT5!1r?f8is*vffwe-jyYT5D}z@7>hfBczKz|J{7b}t3j-Z4iUF*eFJ{y}14*?|4zR-;|a@EUje>fdaxFhUN zjXSjS+opA$x=>R-G2#SG#f+=CvO~OLhc>V0D}Bi@xszTj!0~z%XZ>2xCsF)ygI)X> z2z`5!o(PddNa?COD)6;xy67eMQV#?UIbB>p{S{;!EV?LV`w*3)QKt znPFt;M%pf$c07e;AzB+=d}@Tg?D%v9FUem8>B{dOQvT^Ad4$w@R-X^{<12w^Stx*C z*{NjRnUc8#Q1U^WPMLycIc^esrKLtTgY>ILd^TdrsKl<=n0+c%>yj`A$*tz+b%-=D zW;}HTMhayWM6N1Pry6X=ropB`N3fFO3V> z8b=lXA}{o~ym5zG_Q^_P5M>FiYa3#0S|)2hXmf5e7?NAHl~`2Yh>{Qc?|kkcYh+3IOL!o6oiTXD48qs z4lqDbFRyEoly68=_)~Ws(td%zBcWUAm1P#UgjXJ zIg+rPf-{dgpqI@QeoZI<7~*$Tq!AHMM>e~~EKbQ>BQ~Upvh;$g_4<{*Fr`PD^(nA~ zIcXW{zbIJ91WdUFVD`Fsq@~l{Z`JmvV9(s9gUg5A+x@WH$#RiTyJK)?!08kL6jZSy zGtgSLW2-?oB-QT&Rp>;2joanf21=oN*A|*Ny}$o}z{gR2W&z3lEN}`(=C1OL2V$Rd z_vF0X!56c#0&m>skpN;V-V?Hr+*IkHPsc?~oVq3@)9KtKN$IqT5#dw_^~ne7p2<|1 zN+8Yld?NJ^6N;bk0-ofL&7(ZQTq5WMi0h63eU;i}${O_T5squi(=!PD_WIg{cB@FhLiOQz3GQ#WLWsYFgb zTo7S0%FXRX9!h!gV!e)W?)`~v)?Zq$;ll%ki6ux82Zfv6TRh2IQ5W_<$Z zwZQH|;Uu6dS7m%%7cODLX$xW~93_zvpd*X{E78t;4&KeC01v1ZO>$&G)~75n%+d=& zeWtz9Tr*lH2~B*4Xj|B17A2Wjh>j?i;usPNiIIW9VkCx6P%zKMWnAM%vt-H}NF00(KIpe^AiZDAI1zS>`{ z*J^$;#Ht=+3?!9`C}2@ZL!!+PgViHi(xxSLmvxh?5truCJ|IT#t|JO<1hIsSpCf3B zZ9+(DFXZhS?dc8;u4P9;iT{f{WMr0?25E;zd^)axoM%Oll#WQ)etsqZLRqQO(HJNL zZ4`Fwpk8&!bMK0hfdeMSbZ5q&^PgkX)sM*47 z6=8mLri08o7ee5ptjPB>Gxi0gQ-V|aHP8e#y zHw;jIy_B~(zrJNYMCjhh#M(~qi2|2IJs`&QM2GrC08?P!a;GkmUB4mpp!l zc%iX%apL=6?I_yB+la;+^6`0*o~mj}Gd$6q9?AwC-0Y3nEP40lzU} zlw+Z3v57Hs))O20MZE2AEq~V5j_eP6j_z6^B5wo9n{FT6@^!VYaxjh{k@*NNLUOVha~Hd z@kmP|7L1F}#d1NuepR+I^Z<)zhiBT6)Z4Vr;L61!BR)k2dH9U!)_|h(fl>t)$jVGx z{ziP=+>+X3jyNJ8EK{_t>B~V4wi9f2nnIn1V1J9nk_SDZUYQ^x?#^O~=~PG}9MLd& zr?wO%brY;L*u+a6+Vr*cNcc?!_R*h%KXbejbJLa_oR4&pAtEeGcCCs)D3XL{V$r*> zgV_+!&odi&K0CMH{Rx*h_5N(S$O=6DythFD4yPrwkk^kuD8mWjD_v%Sd?ugdrf4d) znl0Vw##tnt5@w>rxaJ8Z6jmjsFFnFQKAV3jBYe$hgCeX(G&Z8qZ;-Z_p zfnm7FTx6mhGaaZEFFI_ZBODjnA?KfD#}0$w9nq|>(n-^oBaDh*V12mIss8sua%A%lAUZ=yq|bA+TQ zBD~d%?Yu7~bFSD|5M-^#n)1GGk;U~2*x6~FW`p5GPDtW#yj^Z$-u82|+iVpz1^c*U zARWD+pC{SjFMcWnlCeXQZc)O%P0N^$FVqkzWtKd`3mU+M+~t=NPltFnI+>F=e81*# z#ey2c(FE!9<$`zfH$FSBz$M2d=m1+lq`wr%i?^nT$ZGwSy?)O5Dfwrjd~<>HSTp+z zD+k{=g?{;XxnM?BH6Vpbv6A>PN6Qe>a!mD&f;Bl)uLCPm0 z%(x_3BM~6CZYh_5(5X$jV@P&T!+t~zxg~s*+?b|nL?imxe0P*-dj~tNuM^^T+bs#((ID#$`tNpk`@P;HmYS*>Tzk7QZz7@#Zo)Z5*A_oxmjj75IRH8( zz)G*6OrTy33%1edi4?CAM1x|^WJwytH8oO=OLU}D=fejsv@ZfNpY?LZ zARofi^SR2B)2i7WEXc}AeIb=qun{HMMM;_M0I1KgB~?YtDNx2>GwxW)3^(3$S=2T z1J7zcTQf_|k|#(;4i4sV^wpw@JrTYpc@9Ny_6L1tIcXFlfS{z6<1uB)PubqY{GqF8 zwxW=>6JE60HiXe73!HOEaJ<(?EyYc03VqWolAP&6``Tp2zS1MD6${zW8Ln$uL|b)6!xm70GRAvuT9j z70jXS48d-l?4SnOxs!FIJB1v#4Lh}VSw=A{A2mTa-#OVv!(V>=litY|5^xohK|D`H zo;JxjL9#~Debs_rQDsMWj`&HZb{wBC=Gw1dmFWzv2eJZynTks=18?Qd6oN3osbffk zK2JwEVe-a=kz;OWvykgKYia9)(~wRz7jqP(+Pn6o4#D)lTu0)ft8NvLcdf-X#4%X{iIXM#S~vrUgO0P zGKX#zX*ko4Bq}^WU?`{gaGMJ=;D_1pP@zYIgsC-TAV%W^<)EKznnFVZZMpzXnX16( z`hr8?bqvfoS`ex#QO?a~vV88RRW4BpCuTJsbt94zAOhN^qx8jHGv4;W zF?@}D3WzI-v7_1KziZa*d3*h$-fr$D0_}E(8X|;UP>3f9AmglH66h;gmV-)?pcO#{ zZ$sXdp@T&frjgZ}&U>kehZ0klfeB6=)jlzdc8cJ4l<+9 zOi1R)c}6)mwb&1rLno6KVFY|hWH=sirrj~OUe$KW1<3SpIF9Hs6YaA6h8&P$p>9&M zW3JA$poqnC77!!l+^?ByFzO$USs{SU2%Ojq{;Lhwf>g|1&Ugb7Cp8zSB|o$Qg1AF-s)7P@G^Ksi(uH&FGeyCK zDRN0P{P^*^)|iksGH&~Lk_Nxpx4?00)vEyY*{-7ubMmg29(sKnWQexMbd!r*wEXY^f)Hk44k!k7_p zQZNtA(4a`=bGO^*PMe9|Wwf~mnSqGMbUc=~sFnm?-d27^BnhYmbHg&LV@d0CChZGB z*>F_!2aNejaV4V6t*8i@V3;nR5AjUHzPiK-q=!y;L&m{^SS`gMkPI1|zZTNP^UX$B zc&t`0F@t9wCAIHNqQh?)UKL1xE9cId%0QBX7#9fHHK`TUud&n4W4&JCAgtA_5Tusy zv$RI2jf}5o(;$Unef{#Hec`mx&la>*_Mdw%PHV?8WnWVibh(`Ls4tK~dp-|`UGhFr zQQs8S%57B^>o*Mwn=WtqD(EhiNPc3UY2j{-M2tkx@GNQiJk%7HxhmQpIoz1ta2^E6ds2Zc5Q z^s5~Nkz2vCSV)tk`{!n3-zD}T9zvrYqIR;VD5q*i?wBv&hKQQD!~y-&16y^K6ExV=>|&-%NJsAP~T6d4qFls2K#=~P|?OnR!C!ARt}oKLjn;5G|)=p|@Cu`eVr zJV(SNzRf>$j(PBH3Ng2c9@>Cce_L>3cI5AwDX z`TJ64Nl@aLMzpL2mp5tKv`Jr%H#>0-ZQ5~8ord=wdD50fI4lQki+>Zeeh_OUqX1MOp;l(9 zN1i~+v$ctVqnf1&?`ZfCC)zMw9zbWWZ*OEI3gPGaxb^ddYLC4FO^fBy&i;51awu(HB{%nhyJ;+yH4o|L4q(z+WM6Wo5#t(^LHlr0vf>QUy9~5GJK)$fqt^ z``PNQ8f~&D%#^`AcY=I9WuDR~K%Z5pvSNDVp7c=BLsPC24}H>msLYyFnOetI?Pv0O zH}g-}r%v_a9C9P-20L6x$sh0U0#ODmYT-V0k=_eK1X;uolejaUUS^-CqIK<;)~t?* z&Ur{8VNOcj=oE61&6ngPjkumw$3D+z7G;M4Ear2FHV;w@0*l4@a@~ucx0JyW25-w$ugnqU3k_G~*aGr==a9ZC2`G?<>x&s{AoPWW;V!&b2-KX78QWn{WwV1-#6c=}3M z0}%{fjmB@8@(^drtEiIxAJ4_okpazbw)>!3jushKoK*6vw8b0&quX!DXfzJg(I)D| zAIep@{q^_XM3UV4cv<0y_IQ1rNsd`7UPuG-6wM?k7Hf}MHrpLa@_u$-02l10avRCB zQCg=9uaY&s?3WFVSL+wCg*JIE$6+rnCh*N}D?vL&+`@!|ADeeDncsz&pyJ#Cj=$s; znHg$8p}-^YF4s$Trc<8P8}#Ufh_IvCn2jRsdTG$#wJs1H;TndDMgn24SHEsH8-QNQ zVJS7zkonfUk-gYw&E@P#PMlM4;$z&H8QB+%cVaK_= zN`JzYz!Z-A!?EB(ULeQoBp{#M##JqTwG7%eU)jLv!t1WP{Z>4&ldzFN)>IOFNsnl8 zNDGZ|Uww)R{w^o}cSDa~^+1>DGVaoUOyze=Kqsc%t$kSBP z(2k~jwef)BtUc6~1tOSpj@vR@JrQ5esg$}o2O*6qoNAgW;4j0NjPpo_*x?YB5ie8Gov$|M=*y-H?a;no+rD#js&_;(1BA&I zBC`1ESJ%1<0d2u-20zXFO3=xoG|-U+xsb0;!-aEVqJ^c{fW{qhUfXfRX)<3WhI$q` zu9ShCGP$f;TVs%n4fg3(4?mw>CN`Ey@&wfMj#O>ZViR5K-B01OEY{c7Z=_vNj~EmB z6H>E*#Y6`QaUZ5wKK^L)i{iv(@S2s<9BD<4Mx-JQ5*L}U2M6_1?fJ#&8G6T>OPtIY zn1ml-a=~Rfn~P2Oa)>htFA3+BqBy>U#9CoZUw7L>h9`oSqePTc+K4@XWg6E}>y&bZ zKa@j$8$|p#jz`7}`N@CV~LjdEOw3)3uY~J!fi@ zmF8CQWU^Q+^nw!Skc`txMD;x4Gf^EJ820nl+vac1O~Zv#Su!BVV6a@cCz5Bn>hAr1 zi#%kQBfjq#FLYT8LQ5U(saofx}C>L!mios0y4$XMiWq$Vqop#pVfQgg|vep z%i8G+Bh6wlh-Evp>sN#oJdVr}=d)eg0B+L-TU7&aO7Mh{gHj@Adha~JAi`&wBpC?T z?QZwGQ@te=W?=f&d*TVIwXZv0?dy`B^h1&&Dw>s>kW&>}5w_OaCFt>;aP8xWR1xYY z2MLob$grL$%?aqvT$(B)c$;fkj;O`VD3qBA4O)<0Kzz_Ap(%4le$KrF+A!;q>L^&B zh#c8$wxl5rlo+$bHmk;(aL0ukkxmn8?6|~1#*>4s+O5<=Rutgc6K90~x&bKlcA&Tq z@?6;Xas{Q}%VDa~PTPpCMOBh>ejSOA69SO%xlx{FfDfr0Awn8Wv|S}}UI(c$=Q2+% zQ1o%?+jakil9CZY`YSn4j?CuOL=( zpUg%8MHN?q`w#4%i#6{c{Hn(;V2>Egwb`??8L8`aiF{|e78@?0KHkdv(C_?afi0&{ zDKYsq=3OE2cs_1-8{JWWtO?yQ8x>KpBlpuHw;4s09L{<3#ZuXgR+Kp+tg&_6CHd#x zN?Z3y7*5nl#|E zf)q>2;AJX-cqeevKOS>`YCc;sp18DXq4e4aEVvwui!d#_R+#X+pJ>5X+R?Tc=QDm+ zcjxs^&zVWs&lv`iZ3=5|diizH5US>JGB2i~9`W zh-i&3N1REK%#K*Z)A(96b)f`i0Cc5aHV~-C&65;erEHVV&`Y$$Xs5=;#JVTPK%R$zQK?g%mz(lG+62mX@c%<41VSGaf&^Fn=yuN{nf9NHcn0V7* z4Hr2G5XdfwjTfZ6%s?{Xqc#%Glzml4n2}G}h7eIy&xD9PVmD4O(T-kS!Wfc>3MIFM z?AHY1>ltv*SZ@g?E+0k5Hk#O7JIH;{>Zvx$I@jGxu}O)D3of}{tz7{xDFo0XW-#8x z61ZWOnfyZtuEj3~nSFz{5#3L?_CPw-Bn9&E?`D(x z0K$u}nnI7pU0VqVo5@~nM?=^|s&j~xOERJ2F>CF~WmurVmY>GBs}WPWC)2zaiy#xc zYYZq)8U7?yg9;1)K*J%6CsrNXkgL!Fg3|(-vvX`FMuq7<4ucs3zcOH14-rNb*ija0 zTtZ=Nbf;NN`H8r+zbg>nZ2O2Q%gks9ve9E7vpKT^GUj1l!Gu6=r|w#O zu!ZidtffQ@_G}EYfu>vsWQG`k(zKfttY4DQQH--JgYz>&d1tj=u9nN;NH))6GLd`* zIs&1iW=<}5#6B0RZ76z_k8?gn-3RuAMhgofQt!Gz;uM9?K#;(h#bPwbO58k=v7sbB zJ05fV8lFC*(d8~|roz;nK+8zbb=9S*Nb&A8TN9m_6@$r3@gtmynj}B!JbK4H%|=ut zCqofOGE0S17m93-G$ieO4C+o1TepHl#Z1V=DobLVe&I^4C1i<@J&M5ddQwwk3`1Lv zV3`=uOyfyI6&acbF*y#x*w9mk5iZaw`jsfa(2T7_xP&rRtOkl44=2GVup6Qn43v>j zioYs{a{aR&GeA$QwAX)odugWPs*EuAGGmYh>M^`jO^!{VU;ZLDo6Ct%Q%s$Bo5s^D z9>j!bb~_8VV%oKN#gKFUIgVC2`g zFRud;>82n88atAbI&n51PiA>M?EPI?y`gl1W;@fV_}N`9SIYiio=o$|mcy%tb{T1r5nO;fyvIQ_I(WF6SX4$HxduItwf$3(V2 z@kTNrUEv%SI~`MT3B#OM49T!yBpodzB4)j#RJ;TgGb`wVW|Gh{m9;y^S2fy1oI0;2G&`taHem-MWV9LJyesS&SY`?P1O#H| zN|sTdp^72o3?~eeEJUu7ukbk-DFCLU>3A4)OX(O7**g0<@|U2QiFCP>kucY|LLRAM zvdN+;9xUkG(b@q-`~&ZNefiqUf;kU$MgVb7pvP=fPGm&MF15oweWJ_`BTh)GK&<8L z_w$gr%Qz3^Os5(AVX`sLety<`9arQQ&)Q4%gNLr`IwS!qh%vy-ucil?U< zA(mZb%AkJ+SL15r%N9d{FSU*sBco;MWLQg~O|p<>?j$A#-0tc=k#9De^C}k z;)xG+cDv1w_uq*YLueLLh>qqfOefrv2@lM2!U-HHY9Y_uOn6+!S-~uM?s-q|q=cSh z`7N3AKo}H}x&2h77vT_WGtH2id(%}W=1Je9PXy?z9bqGf?Y8&q$XS!goaGHh6HJr| z^v}5!bb498Xr6mycFp->Uz_Q$hM>@(uVxHBab#m8A0MCE;IV!UZU9M>Z2CU&MQWr+ zSIm{jZdtuSRR&0&=p)c7*+CouT+o?^`D7EtIhyO&pw)(`h>ifYOo?3SaQ&H3V}bYBDt3B;W189nUdtOqJ3@Ft5q_U$i9}-cs*ByHD}_Q@M(;ks|D0C zt32E}8U};MBWmh4BJ0G6h0etVDMwQp&=Ihu1+Dp%(d}r-Cu2Mu_Ln@l80egor(nRt zJ=3gl1R2&<`}-qnDah`U6o=3)Si%?D@CJ9_h9gctl@uVA;5>$eUlb=bm#kQMb85%Q zTx87Cs(61e&X8ZIH@8RQ>}9J2Qs_F%9k2w~67B6S7C{VAlWR4?1t?3uoW-@S(xs;C z5E0h2?kWutRMt<+V^L|mxQm=wJMrD}4(ZKdf8c-_2q>G!BT5*k_KD3WIyL18?l_uo zak&ue-k*M)j73p`Fa?reoh8W$E=sioDSF1{vuAC^t$5O{n3H-#B%SfJVur{jNqhy zgv|4*7+ncpxlb!lV?pmW{UYi0^)*335mL+KiSycWywY|MZFHB#I+85VqY+y~F&usQ z@|C_hIEe-+^=N zHF}!dbnQqCJFik=qLlPlEEg{?Yeus9RIes(Bw^PuxeKKXxQpF!Z!Hl5-pIAU&)T4J0e?gF*G0mVPD?f8a+ORL47@CQo*Di>qL_a(V3~Ghpc2m zCp3(+1d+@~1B^Iq@EKEyi58D8Y2E4i59i7eEI7+2Pyb13oyEf6HI`&1c0)k~}98=3ZaADaogR?`3Mo zlV#PYHV27U7HH8(jb}Xwfz@f8EwmIy8jd&f6p{2A0iDj!2(NLNJOwK$kfTMEr`usi zmjUJFs;AW|7>LlhKqDrO9#c=uw1W&$$4}%h!A@stU1Ud74*IqHLtvM$#Yuq_oDGms z>8PG`#N8K*nG1&_4wW*vtS$zul;U?hrp*AdLnauEObYl!(Rtqv_!e1JkL-A;YydUc zk8*{om^#`yF(n=q2Up&Z(cbnN8P?Bd^((iu4Qu4212*6*@j;~MK>!r#LX6aKL|nB% z8A&8ju9j<{zhJXMF5{=n)L)rO{5N;}>JFa$7I2E4fL@z^b>|$yR7g}Yl_ygCQ<6-C z{Nt)bA*?Viq97>80n20tZh|OAED;Td&{&&VcP9uEsVtZ$%992!=e!mt_sIODQtVyM z!3vtA15|2METbE?!8u)s8Xb#Au0{v4bm+9SnE>0FW%3+Uhai^*jWSdK7WO72K$fCt z&)+7m$U9`Yp5)P!7=At(a3RLvHbn=Y77MG4u@9T(7(b0ks2pe6h*yW}HkuWDoMAslAza((&Oy-l^`R;d_PR&ess8W+2 z%hWcX8|PDmexsm56}lrFJI-ofV_mwh(l($05z~p>?1=4{*DmI{D>Wo<(FpedFaGkE zzi8G^dUTo=*t4eW&+9#`n$nS8oMubdtwjFg#}B9JCAo`1Mv^x;cez|R$S7hMgH-cL z(uIc_!7gt-qElbn?UrpiUAq1J{Ct0Z7jxF@wJzwTK7UHje|$lh)l?W&?PTOB!-pw(VvU!fKuapg{APy&%I? zN`s@EcF3bCDLOUVh$r5*AF@XoSk*q+`YOhW$g9O5@WLGcbA__Wf&?jau(TW=tnybeo zVDZ9EzScYhM`U*G2pzg;FiZPdXy@xlvsC8OR|kuRr5!!mT-8oy754d;W(gBv+(*er zcf@7G8cLi@L*(N)ZLZfZ7{{^}XFRAxq$y14R!1@EI#Q#;Des(vQxsv$Pr@wzQ&CGslksRW&4>#l ztATtRQQT-UT>eqX#1j5coX1rmlf{g4p>HvurBKp7ddF0Ik~t<6+bSE zKi)izK^tS%lxD>@Euh3_;~f)aLO%ujxyhY39_A~xwifK+*pb;hW5zT=+Q}@4khFR{ zJ-oPF=y2L{na4x1W7C442)v1x#)-@31SgXc0_<}yC&(twNu!zn`C$2a*KD&uXKBZd z%iC8>`E*H>Op7Y*5XvfVtn`}9XlvG}y9hj9 z_H)S>y-=x7Us21_oWL0T#QxYeZ%bgTEzH#vQHv<4B|v5MKt1$$JD&E(bJnCuf$@hkhRIB&&ZbB0 zFegpgaRe7mnASR++EGNXF2|`Akvj6LGf{;$Sp>nCgo|2ItSa0(ZJeY(o6MDA#22(! zkCa8Qxqp<~Hwvq)pq6Ex?wil;_G8;_`Vozyn1%b3G9FJoMb?BB->xDlH++UYMGroa z=u>Tw9M^pw6%dqLVFf&)&4>Q$rP=)~C&IfZF+kQiOqu-=4g0Rzj-__TV{+FcYBiK0 z>WU0;tqRVV%n%w`APH!2@Q_ET!W%jXD6=U>32h|Zqq6&BKAC3L`Ln}uGw3G>)zeqb zK^HBOSBWBIg9D^k)+WCCETJdDH6Y)Z)kJD+--paMabOo4ZlQX<3FX7f^H^PSh%H*uSpJTvF-=k0hjL=FnJjBF>Ok&Jc- zO-|OSU$gXHzqduU7)HCHaPp^L{-i;D5fR}qS~9P&03~O65aG3j7(Y2kZklqrs10Fi zJe7$<7C3I?yWROQnndwo1xCw-QHGmAw@+QaIX^aPp^cx#EU8Y%Z zC$lD6{MA13X6&8UGwk-Xs01kBR8}BN4f+)^y4FzyUYoDl$9v0U5mNo?e2$~A$AcrS zIA?4DBQ<>FRBFeU;+&x(54EikI?_>V%0cI&+;MG^o=ehcb?tb|(2X7(MfJnHypJQ> ze|cF?XOrnPcR$#YBN``I#Q0ZRu0?-S9F=K5#8mM^O~P;W$iQT*+|JU4c+q(Z7qWjA z2@xk<@+cw&tkHE<%V)NgmE2q36+0%egEUPK|phnY+Yw}ltyXaF&GC75s&e&X!$ zh`MJrtEHv*Op9C2_tmv_@S+8je|~(BkD^mA1n6?-xHdgjc~AUY*d>0l;oWJ?P~p6n z^;*OBQLY^u50OK)&l@zNU-UTMEX@&N!Dc5mRG~G*)|A>tq?2Q)84(NDh{r8;Q;HTC zZDMSUAceh?T}4p1|MK?6i`XX?P@aT!~)d%$#Km1q>Ab>iHW{hI3?NFc393xcisaMn&ua`vJ`+dDy6RF1e0ZxB@+l$7!K2GdHxKGHI3> zz_|hI>u$Gg0A1kW)Rf8>U4p!9WqZyigtF%#kn){uehNSTf6<<+#cUjO3Hk87_3Nu6 zLeS-WjSF`lRjFmX7)ijeHjqbPFg+Q6`SMLR)X^XlN9ODGtFE=|PXQ(hHjyK;84|N8 zcMDJ^gs47u7wZm2vO|R55(nFNPW9Dh%ft&@;+n<<@}1Ba^tI-pWge^}dDu0PB!fgb zQ_WCO)II~25ZgXdeL6=A&2q|s6F?9aL{~0Qy$|&JR`=5|#LI+4UCN^m(sHk1I4agL7c+sU248p_f8`}glnUx@MJd=3DB3Ixm3*-z$5 z0mkKY89c4W9)wB;U+I={UT*#W+kgFEcWad{c}^ti3of~fbl>iCv!C)IP?caew;(gy ziD>9cOy}eEr?UV-?8&>++}F4l0~y%=Lv{)GjR9Hh-pPFg5@1YDI;#Z&UUT0}39_h( zG^DYs=X*$_Y_{@1aj1g~I?Me3{D@4_wsrS5UTJV7*|2Vvuk5oUBYFIUQ|a3+M9o@yqQ zaH4k-*l%}OU`p@1)8TTf*ExcL*W6R=_A;5~k>_<zPIl`jrRRhhKW?ecA>j%#f= zrjQj2E8z|La9C7Ml6-(I3NMbZErrCs;!*E;C->(q&m;^+gY)J1`sE7&jt9x*@)>90 z29?-IPa>40MW28S5z?;+!4bCsM@d^TAnM3CY)Tp@QP}l%k8t|kXTKxb4)E)o@ib9P zE$LU*lEhx+f$o|?+hK*x^a;EnKa5mIS&!hSPWuDBoUiwf-RX08AmH(EIG>E>`)Whyqe>kKX=rJ6=H? zWU1pGu{f5UD8^4pHgC$y#$^DP5GuPUa`jJ7RLDbr7bw z60=Mo77+Pk`HZ@yfu8YXn(2S+^Vx}i@$<=O)bIIJY!A3_fpvCR29Tv}QL90Rk}7oH z?6c&iFU0s-?8K;Cs!3;yD%^^MoWkwVFkdVOnMN3igjlbvF_t;p8SD+JsFOTkcI$JNMx8t_|2&|V1 z3KDhZ%cTq_^%9HNqOwXhE+C>Qyy)EVl5hse9E2E9O{OZs3$VN8QJ4*n4C^ zx6fr=lCgzat)y1NEM0C=wv#b3H1BKWuEvfdlp@aN zGg}G_ViYt)X@yo@lJ$s4RjJTeM$%%TkjR4CN-4xUO(~O!M)gP=?cjo-TG(-IVtFl{ zgh7;Gg`FUP-D!~gaaIH*2-BM5HDsCB0{GMjGE^qWW=LWTmnLG{BG_16fr}o)YtgBB zR&|{TIU1qFVkZHO>7s_FMBp;Z1jdS!FCaE%6q*PHeNILY**ST! zT#j?EY6&+vA?IXf5$l!!VnhC%raNubxWB6erB#0*3eYZM@~0>eYV6RB?xXz-qmpmt zSNRO_6@N40uioHnG1E-AFD~+iam^t2!XPB?US~L+_#4k>{XDEYszH(Nsvej<9aU)= z^bMrLVD1%M^J)(OAt^WHVscQEI?9#W^Qkc}B0$n+vz1IVwv3N5OER=18!1NQbQH% zeUYMw5i?p)$auMP6tc}yo5kC4gr#uC-R}7Qu}RiZ#@y~F;k7(9&@y)(guRS`Wwa;) zI&#VK_YAQ&+tZ8I9MjiyJ|{26$rW30agMIlP6)$A{4+2msfXyC$!PIH(}+2cNMtQL z1jl3)Gv#s8p_E3+HRK(EvDEGE;3sLxK3$+nBf7&rtEoz-a;+`!#g@exSUNXytOOXF zXKS>(0LtS&0)*@GY#D-up?)tZS*Wk-XbkyW>#d^pT`#8CoQX%D*nvtBnqjRhHxUa- z&Wn9qlHq%~ja$9shOC~_OJ(R>3-;FCHV3(CL|%}nIlJS9ZS!P$a(^bB2E^#D7|$w! zE@T4)8o>_wIuH(+kpF7PGI!FnX(!yrdNY%aA}Vbs=Ecw}IZ?U-M%|NYM|!FGwa7qh z@sw)H5gQ3GgIY$9x}6rw!?8#zo+3f~U(8qQmoMXS9!ee!#><6Bq87u*b3B{4aJ$uq^D=okj7vsr%p|XA|6W_UdO|Y)dobEFL z6gH@(RB1#&RSjx{TG#7Uf>{+&F4lOtLvLDZtwNexA zM6-F+UZI=FlQ9}9tRj#W*iYOoSuUd@udCc=JRb=&@0Lf_I79Nm!3CSBhLhw?Y7ud| zBS0)7Joi^e<9OEN8SJ8MKTs^caixvHN+LQXdi}C1rzs0rSE!02M2<}U)QxNTBj;rd zY+P(}jwqe-N*R2DXp`e%ung#TMVNR!o}3~Km7PEFC0e3WWYh*3k#$0Tl6Q(0)FNg! zn?+KINhFNJlc@_6Z=*ye_RMpVA!- zaSSB6Jh=G&^U?oiGc=t0d(*rT_E^KEv$;|>5@9DbVDuP{GdtHo2B2rBp$+BwqFkc$ zrXzHo-%7S&@61@(M3Ei)>#Iny}~l7M|iLg~Kd zm4g{dNG*y+vYbuF`gHZ_c%dm$wS#xz4qXK0r?Qh2@LCAhmF1(*+Tsy4t$;oFiOp9G zkCi4a6i}simzQcHJ=rP>02b?=&5wHhc|1Dv`c*ivmmYsEc$L0#6DhaXWJz1|Q&u}2 zQ+M17clUUD-t0?F)S*umlWX;o1;m1mqBZtOWiH1{c~C-`&yrrm8D#9Iyj&t{pk>@& zYr(;ReR85JR;$I!>&wgQtFBq)A%mwsE5HI@>8tWMuP3xD5R{m93Cc|ElQ%+fO#RM% z)?KC`w=(d|*(Qnro=lt{qk)OZ&&R`#zo|p}y*$m&Rs|Ux(RfpL9I%YvzLG$jh@ftg zFYZ#FMem$cnb#G@XY!q>I3B5$M(2|Y6DI-Lax|Gs0We|1_Mup`!vm3gZu3r7H6gYX zG$H}K>s?=dDlUR59VA|x&_&?)dgUn9_!D*OpuiG)rU@q;YBee}qd`+P2VLIGfbHQ9 zo8X5X$6c7XF&Hy>iDojM>GXObiaztN_>&bWlIrs4m%x%aghahsE$I>E*wG-fbSCGT zSDdl`yh{NYVoqSJ4K6L?4<&wLDnN)&2wZR=bzS?a(;8mOT`&FWE-G&cI=-haEQd)S zwP+*Cm<*ZNKV2?uX>EjZO9r9LCZiU(%g?(q9J?VG-U2?cEy_9^TzZwcQ%eMzB&wj^ zzMuG;jn^Q6wTplsxfS~qIvZ=qm3TB1A{qMSjA;DOHTkj$sjiz*lYpU?bBUJGMX9+e z?qo-=_{=m*2_yL+i{qkHG8+Mopn0NpK*DUf-~rJ3>3TU{&PR+VZ{0HaY=2;|fhO|n z`78UlYQ;6*Gi&H*3kE@J~V1tsZ`W7F*E*pUd& zr!3Z0pO@?Ta6CZ>(xo7LyLb2dvxKhlna$UdA`2mWhSJ7|7=jYOL56j`!FF6VbBcKN zXfjflu#epRcnU_Uj-?uA^SOZsX(8rv&7eq?a#@$a-;yZuq!i`4mXOl|d1}`+@)JFx zG8$C7Gc4Cfk^}Y|elvIouXCKG{&H{YfSV z@eYm@b0u)OGL+t9v9bQj^csvQ#-|>)WC(fU=d@{?!@713X9nyLJe!FKndIY4Bn4zw znrA|g3aYSKMNFP#6>#|v<7ILT3SssWuoB0z)N|CFTPra#mWh5n3X3wR-kn7CtgYu?PLqM$%^m%AqV&p&;>53Z@`L+0n0^j8Bv>o#A{9wxD9i5&dW@ zfxe0{dl@3DabHKApV6S2AZVWM;LxzPE3vXe$qVTvPY%MDSdvGJBlg$Imu z`rTtqt-)#f0<9z{ga@VMHD^r`dJ)I@x!&~>Mvf;Q!MQ?t7Rdla!aNFy5hJeBHL`^v z5ctQB_cB#Sj`LdRL;J`e5GW@}HS_UOdPV<`L0{a+IK7Y>9o#a3iV6sc?N&M&+Ce1< zg&ujeU8qM&1{u_T==Kxa(6g3u+r3^Ckb8Yv@8%Y0a!Ve?8CGVk`*J#}N#UAGv3*Xh zm}=~t0Zz@?K?P@wIQUx)&}aq!Q94$NiyTQPi?t*8ENIHo=oE8vI3%wQ6cym50`P8lg$(=;VoTmr&C2 zBu$_Wge6~ATlIgUY2iH9PcnFMj2VCV@|8Jo zbV7DKfv!?$Bv8nbK!gH>y{y*_kOe0t0>`=pL+QE9II8EM>AhanXN$mNlX-?pNFzp_ z{tQZ4(4#VFUQH$42yF3CI|-fgT&Dwb=q^Ify=CO6u}L5+>5*0BP;Nrh2)5s-75XCK zJdu#)eP|q=<*+~N@QR3W0b&rQj>m&sneZi@?)S;WmG4;;`#H5mz;IY4{bsvOCS?>W zR*_e6X))=)?j(eg^i$~}w{6t}aYot}-VlevauY6`=b@1rS!H2>Es~e9v}ap;qlM^l z{cPGqx65@=DJi!F*oIFk7+5;%-$Dqt`H%j3xnA#^ZKg4*2h0(A~fYLJ}z_jFIy?{&tXv+emN;0Ar zkFzxkZ(`vKWTh{{bd~M`oMJLhTybiRL%=E9S~><0g^25QQ!lq+hY|@>qK~wdM35U? zt}k|S3zx@X3eHw&JcN?E!|yMV>Xc=0$dY1++`s%QPSEekZ+Va=_$m`D|N2jV<-zmS zd^E{unoP@;m1?4KjX!47k$y4alaTGSgTjtaU+5x?Wnr!)AoMdyB%z_r*wGPK=tv5Z z@*tG_%r=9(KcSg%(;zANWHls7@2GM_n>HsP!LQXt61QkWur@XzN=V)rzRpb&zMm;- zj&sKrjVaev4c|8wd_`i&sh_mwv-~&n!5p+z(U{SCQKjLlNnV{e!kyCs5n_g%-G{0& zp3MM!)k~9=)9kd?bU_wWp8!`tsJ}Q_H#b^fv7;5I`6MOOF*+c8yWQs|bk;Z9^O}s~ zT{Ec|M;U3(u*)SVE?XV~KIpsd=-nwkfj6ZRe@H1tQ>Z1xT-)!?L0vXBV6tJJDhPC6 zZ)GbXOE4;g1p4sb=|66%1h6I}`WxjX@R?9VIlIriavUcyyS&f;nQ>QmCyO~8PWuDo zK#8)hT%?>&^2vnYqr`(+bsH8T6IsI%eV^+oq9!$;%`)1?zBw01+#h!H`IJ{UlVR2j zoTtR*+Q5!yXSvARE@YUDHHp-tkpgKae<)_Qc|~F`@xLoa35C4ubU6T?#r00m)%`JmdbD)+V?JpSlFnMl@d8^){G^Qxdy`3;%qs(b z&V-IYf(0`z!9k$Jh5BuS5KWa@cAE2pjdG*k0SBeJ%<4{cJqB|-i+QFxU?D$Oe5sa` zxqT*HmN{#axUkuJC&S`2@TQC}yqEwgLn%d;u6~{snTdJj~ z0wMe{Mtd#DUee*p7~)|Bq`n*Hz?E#!%p$YeJE`t_?F;e!&jV?U$ve!RWFC$uUb%*TYl{D90zlklqWDQC z&!3SH?kbEwcDqC#U47nG_rIwvjV++rb&Z<`C!x*0g0<3&az$-t@?4u32b2 zw*q#XZ4X)Z$IHw5@#u<71=sQfaB*EvC1w5$lS^rlmdl}h5)hAZycwi3ZOzymK^{7t zBhovf;Gle0$#5A*_=)PW1^}uY$EnRulYRMAMnkB@DdnNgiFs>{0sBG;<U;&2&E9Fxsa)}Ur( z<;b8XjdIc^d7Ppo|5dT1O?r2!pxD|&&e%_-)AURBZ$hp7cpE5dcjJBQ);bhzi?e9MStq+_Q)#^TRqSG z{6twCTWY#pPf?k45NM~ESJg*BIkWCAlD}fyewkdnwQ)yu&Os_RTTm@^XGkc&Rs!;C zQ+)_N<>~y2Y?I9nXb`@tSP@5x_F>?pFsn~`L`+jn~b?qQWM5G4wPQPWV zNezCAYHFlRk65M}L^S#++7P7U72ETqS?NDn56N8!NYPrin(+;%?6!vtj(1YT zW_l`5R zkVptgqJS&LgZ^Q^*&jBY?oD>C5!)L~tL{~#Nd0Ae{!g(UZ@cpUX*{D`1@m0Ac&2z# zOTvdon^m9xzJm zCgr>^SbPaZldQ`66EXOVLH7c+2SYBG?0Ly*PJYW^H{?%9<)67LO1#Z{wHyWCnzlYb+K4MB-`CigJeja>WHw= z>(8b_aN=D_2b8?*_X&26G$O0J+#YeEjr~M2LC~21ny69XvL$1bKf9gjd_Ky3RiTOf z;3;w!(R7%>z0=EC@&hi#J&n5sj#N@X-%6&Ou?gC_>~4TeIoq^#+C6$db(=@(UDJrRSZxo zsoNR!N1#0-BdBhNqcQCroGRbnQg5(4(UO8_;c~smD7w>$BhAZDsAU`xA3v(T;&iD= z64qtM9CLYP%;d8 z??2vYBm7cKQm;?|we-Pa#7HstS}uP+?TXpBb}d+h+SaeSw5g1IG_DaHp^ygAW2gNZ z^|Bf4=Ml*x%U1d#>j*O{*i;$`hf(YP5Ksl*G-WZ=o){>t*T^V^YH~hbV4*SMNF$FH zG~p_{1lYR%`1kwh)Vkm`dIgf2p<_##04bw3z|Iyo=-KDwEiEyr5nl z%qzebs7eU9v(4vp1PCM9i91Y}!O`cT-^ApOWLDV2*B)J4T?7&qVu>~bpp_>UsUnP_#A{RPe z_D1w7Uxq1nJ_R44ZctO&wb|V&Hove z)qdHs7JTcD$JCu)$wW=ztKV{8<&vAsdarrVgxJl~xc~aU`rlEf5S8K5Qm3X64YfRm zfoQAwYyxhntyS%E-t#8^u?7JX4^=wHh7j?n?)4OXaA{Jj$$33iU4b=^l_;!-xgn0? zoDI!PrXSng;d~iSW^XU+{{7+y0gV62=*)%z!4-h5*sAx=^UjGlThQ0##BE5DjR+aq zq;!D`cNqt((Ge<#3gsas3iG=@I$5bR&zTvU+U?Djx-O~%VR$|RQ>lA!Ff?7)8@B3;bpv$T3T4*I?Ae!E(&NLcih z$>{iSI-YOW^~>vGzBFk2m;}7j8-ZZ(x2VI{n`K6W^Q!% zer4S1l78<2lE9gV;~`10ye7A9>Mjj=rlUgee>)Nh*M=CJwvgnjUb-1L9s;d&$6XJ% zyXtu{oAo&F_dx_(yaDdVJNRaOfw-p?a~i zhw+oidb{0;JZlx#P)#?F^JOA#PBV5cVmR6asI@XMt>>1JAL3Sy)a*A$ZkN1ViJ~Z% z<8f(Jb?Rw#VUcd%x5~}SY&u!Yrj&K7J76sx>W+a)hfIrP^mRIB%+gNjF*ojz&H9|s z5C)r!RV11Rqx*@;kh&;NF55ldJ+5EYlq$e6cq)#%f6kU3flBNtW}N~ipPH)tk>u@2 z(~iv%j5}DO$cR1{ex}}hj)&l8Z>vm%na0EUWCF#E17EZ96NN4t$3uSqoy=12bX^>vdojDGBSr+)ugoNhDd10LaKRn=6e8S&H3BzdPs( z5phFgj7JlZRGUJ2lTYIUq4BJGR60Ih9nn%HKfJ~%T)y3HGsA#hha>vBbb6cPnQbiQ z^?tjtvN;3x1jxC-5sd@75F5B8&Ink#&|Gu#LcsZo07n{wF2O976*^s+KNWv-d=z6o?K6w1Cd++pj=i{^g9FK;-yuMBIR>8zHr{O%^)jA4dnj^7_WR-|f zYqXuO8$KzQ&v8iQ?S@q@d+_1q;Co{!{-2@I8Jc&3Dy0q!z=tQz& zuSJgphZ@bqpf(*LL*hc46U0x2hlm(vsXnv1Coz9m_H=&#_?R!|QtWIx9*R+*O93ZB zAZtPZY1lTVI9I7Lu^XF81AH)8&6Xp;BShV)Ms&ki_s`vUG+0gs zlR>u+L)WVOyp`y1ges9rpRtpeUyJ`K=QPVn9M^?3DT4^S444cx|0JBJFn<3J|L=d> z`*@6(qhJ4vKTp5Rx2pK8^~oKd<4K+nIMoyRTnu-b)wo`oyFp}{ukJ8c-jreJ-0g7= zs8;(KdT?6wSWz^jW;j0pz(I13heHunn-x=er=-)s6=oZumG+%%kD}WC>Vzuo<>}XKI`&!YA}NO+lNN(^q`T|2AztgU%uo+yNUX z!C;0Ml!)=xM1LklFK3vUUi3AQlSiU*-c*+=OX|(@D-kJKM64YR<=v}1YBMlw_3?)u&|(hsKE(ZB%P#B)FEe(_wWfo zaNg-~TrQT{MOlZ?dqd!ScewV`(o838{+^mTuv0)&4V9PHUA+KWuE*(uvK`8)&0RJp|;G3{8}7z)`BcWQy(;fT)kzJwk`-0-CLw2W?b>5_ygpCJ8a+B( z7P{?@dXHX;u>NDdn2x98XYYBs9QVf^Y@@{E*Ak`cvA~##bUMv#3W)%N#Bj`uqK-U} zWip|DFHj@yCKnFIlj%ada;8EzaUiH7Z<~_rrPbW`S6M~PRFox{d_7-o&d~*1(Fh4t z3{6zxFg|T@;ZFZ@1s&<=e7@Zo?@n-mSqcRXde7a*_vh_0q@^1qbgc)eRdL|p{Cd0+ z(Rp`zT$Bo2p*J7R?x)*X_0_rDZ%1~*{S_jQ3kx}SZ|CRj)a{d~V3Asv2@L1s-hA@l z&b`MBfrCEFrq0YTp=K0x*<8{F%P+0n1#S5^kHMaR;lO$=VeoQQrwPQ9YZj~MFXl@? zOCr)@BJX_84$gp9Ok~K+9&Ak7&H!yn4z^-r8mZlCRD*wVb=I}r?sG3Lui=vmAc3S~ z#c-67;-Pmu)_BZ#cI!=!GR|o1uy82ouy9b&zwErlK~afp6*gj!E?PKiJtD)e!|6x? z!LE_soAi(Ob7$Bc%tp^a_jo-gh=E7gR^;=_(R?&m3TV*UXuhLM0 zO0U}D9sTR^emy=<$Dd<1k`5l*V9ep*C3l_K(&>#1q5AQ> zZibzKj>UAAtV%*Re$Fv_Vo@Fy@^`w9`eWpEnb~B%TCUNPo1&8}6gHoN8FR%9c9jM^ zr6Ro2TnCHkGfTFI zrYKs11ve8<3<#cj;E4fE?4bvSZAvCBPy+m4d2Acj$O8d3)W8PSk|J66-gD00Uz2S_ zWar#eWeC2(nV+zM1E@JZ6fJkPgWM-v_ z`-3L-yZu3nhD^Pm%$IXy1$Gxx1uKZ2SkwP`S89J9Pk6#p!B{hnwXSAO&DLb7lDXM( zd9P;T@Q82J?%6zM7zCs8QC2zoDNA>_I;PK>$%`LO2hVsC2tqodkU*cQp3_OjWHDt? zw)|MkpT2C!EC->Vhfr3Av+;`I2o}^v0r3a%j*srcKvj;n@GTITUtMips@cOA<Yn?q+rMqdr7e4r|^rzrrQEMjbzuUx%`6>w)_2bKGUWW!hc&E!=PLf z1&^(r@ww??86A#nM2fo~IS3ie8;)&0zh__6G)kR7@QS(7S?#fG~ayb7pI=G=~|(G=a-g*^{M~@4 zQN5UPFmZrdSEiA)?Go>WUg9mu5dms1$VJE4JX=nW912y9)syk;-0@!YTZT+4?;;EF z5hb6yZdVye7(@K+x>@sJynnM0H*fpHmeI9fz~!RF?WXNPlp96`I>e)ml59uUvj}0j zS~qO#p?;rY3uQ@r|bSD&Rqn-1z$$M1$rh~r-U3hm`4+Y)Be;esx6Qv z^D4_ea)I5~>&M4O-ycwwb$mJ3DE+(|n&6r4y9@QyW(p(DQapvYi%3NwiChAcLe{Nx z0w-e=#jp#Od4KzH9Aff$G_ecMrgw&9%WO(NFnv!J^Eszewcup`Vd@Z0vioa}NyP$G#f1rDRgE2^ z(K$$AO5Eod1Q*y55~}Ljo%`MCMqL1u8{dCb$LM^Y3*^Mf@=5|nq3H<)q!9i|7obGB z4)bj|<>_eMEH?D)F1qubIgAWzRXiI{yZtd6dPeWXi8BeuMd4W6XxF~$U^E>6%s8tX z;f{QO!30m`aKrjb;sp$@%8H$8L6D4OqpQK zmOQ?L^M`Y{W&L@}JxIVHY=HOCN_0CdYu z#rEpBgWk2a-R)m zt;Yg#Yo}O!SEC4&#B9-oWMAUBtif zcherW;2A2SHNEgYvlltE860Rr!jwMiEAz%)G#E;#P&cUY`l%CPDYMeJI zGjXL%rDHX-ck+qqMgTqRIP?~S@);I=f_8IJLQYsvY2Ttgj}*2 z9}Q0*S(yn&7>tWQDFmnQP<5!HoXFHJ--n!Cos&8W39&12EZqly#q9#LG9IEuykFU= zEXY4{PQ=O3EI7?)l-O1#T_k8Lx~#Gg1bu5_O3}>Qm9e-MhW+W^)8@%P@aS~hrI%9C z;P*XEC}@TO-fIy+WXTV|C5K6u9`xoLul$+qq%$orEAjF;-<}Qfvw)I+X4j24 z`WUs4rC?q<(LFWGHUpUo2qdS9bVDZj1;U1e#o231xjED2`=~1=_p^iXBO@}=RmonrBNM_@8mi9eAz(wC#_P+gKS>nI=gFjEgJo4}g=e3&EXzt+v38ZU@ z+QTC*{NqhLgY00)QKlhySB=z9%Rm=qi5h~4b_|(4c*@kNl!?!ebkwV&r&i@U$2R8_ z3ns(n*#%$Y`;a5#!<{(iiXZjUh%={FG(Wysrpx{(FiwV!@d}G(HOrbvGY?GIsINF25-hXHeoR_qEI^m$*XSK6>c^_BJ>ghI~asjhuab}R?@v$HFgUH;A z-05Nu5{~=qF2g>%fA`{Ix@Vd699qrvF6jyIor;D$+Y33P8<#3&26QQ$TyFKWsg|l3 z`$z8*aBvEjgZ4&;Q|~(QXEvwK`^)4ssr%_-f2+>3_BfgyNAt^kHO$)aVjYvtBFmX? ztf#%Je1C2~1U#uoER+4uX58^^yTvi) zA--}&sLa8-KYrXm;O+#Y(fo9xh=J!}(XI}+=`dYf=gszfAEq_*(B~`%BV!fu z0C5>U-Fb8#C-;);-Db&@hLHsS+TYjBMmVL$l1~3LXZP_=v9Eof9=lX(q?e($Xjla&W8#{H<@pAVyYe7tq_dNGx)^Q*|9Eb~!!lV{$Qd@0l(FQ@x_ ze7ts&qhZW)fUK);|GHa0Sxlsur)v^T51%ulcRQSq!%Y@++nzqvb>Z_Ap8k5!zrP6( zvkbXh#+MF5Z@u;^%8OMqBMhpSjOpCfvvkN|ZW-;XMz>23&z052G{nCIkDP`+WPGT~ zD4`DrOs9nQj4)+apWc(SeVVK>7Dqobt}N*)y&9}Clf$|W9unT>^&F}YBMRal^QZ~P zlk9yVP|+^Yv~4-TMLgf>F@9yvFz*$g zecJbOafx`g4U^875DQV2Zonm!sGQ|;CH|)?UqU*odx%sx2aJv#3@6l;h0y6-i-HAt zrw@)^^%%Ruh9ckDfxk}GR(lGj+iPY)wfZw(BUME<{XRz>_f!# zuc|uj1{TTy@9BKSSGY^)$k=#A=@!zL2fk;6LBMs5Z&~mSP9|X+xq0e`W7qp98Mxf; z;^2JQNagpZA^p7>1|DI&_?_zbj++?K`ldAggd~0Yr)mbu9u|#XqnHJZ_Odi0^(fU= zRK)ROz1w(Sl%hS^{24wC7Y;l(2X4ZPF=XOPopUQ6O6K%sLcDH$cCN!Xm^o4g>tJ*ONAE=7| z?K43!qloQlL#wCrMQQeA`Van{e;1~Ie*VJdDtB-9Ewp&-6hNXZo?>&~@tC90N7f&m zN7bCd(boHfx{NQQsdu}~#9^89@N!ZCN_$Nr2__=ptCut$Ydv!j$lCR(KR25VcL025 zYHi;q8r{-I-e1lLK-{ujQ?c`XES-^&$niK4nym_@fwax~>DZsEx=}?4D`5whnGaV? zk9`vu$H|q(l}+nYj~89LS_4%g2iVq?4vO<5{?f8?#t~mI4@0;MI@n0)}9^~*_mei9`xB%18A z27o3c_2-KZr=ec9EFrTvaQkptt)Ghh+@^~qt`e%pc{?7Nqn9m#e-et1-3};B*FsK4 zZ2FA0YBijcQ^{PhmIE|@8#Sv9Mb#dHP;uepAfEG3`JT)qYKJj5*2p`YLS?60m9ClnT^uVlo5#e zB9pytc$icAOE^y#0w)CyjBa;0en$-5UOnZ|`ul2?qfCRh;lT8ioMVmYJdBT|O~nYu z*;^r#A;6=|x`U1|TFR78Wudyi;IQX&+(Y)lQyB?odKEd)|L0Ac(4K4~XQqC9yisFz z7cCmjLMi6UvG50r103SZ<4|Y|C;Rca)m+?GMZIj&10+tA`GQ1)c;53cT;F!5kI%zx z7@Bgl--x6ziPx^C7((yRgR93Qq(H&bfk#lvRCU5Z4z*d5S4jhgVdx~wQvtXveGy^- z4S~5JpJBT_ilCW$O=b&0@+K?r0oE{@%aV9%!DN=OotQWm_9#XMJ=5hk&Dk2<&SOAB z<|NDfZN&d769(=oD2h5S=JTI_{v}=2RrS1Djm`kJL~p!VHeQiV2RQ+xnX*qb>6e#p zlMp3Vh-ct4kStJ=0#Bz>cbbNE2+)>w(+-avLbAQuXt&?tnT%^S11km^Ho4z2mFLlw zM6l@?9rb-+F|$yk{8qv6pZv|As-E7*&0;C!rj3({L|xEn7PW6MwwW(D?B$~6ezAX9 z*PGgJcSrWw$QD+uh<;@lJRh7n^nDp~OkaL+jMYUJ zT9zKBuP43W)t-W#&ofU2aNaZTvzl?#=_Q@u#+0!rlX9ZE3_Fz(r%c++h+w^3etdkC zWhZ0UPc6l-CTk54xQJfx(6nB(EMvPpF!||p+)k&<$#_ly=M`n9rEnkgHzbvJdagmEWSqZY5WwXrQ?U~JtDu}M65o{n`^@}o zH6tGr3hc_&$_5@e`6Gcxuy>G<9`D%Q1r5R2tg>lvDSWGI7llyyNjiXivwiBP_FP}C zzkc$Tx6P`Fa>dhAgkBcLlt$9k8Tx;l*Ow5*Ej?z4DPf$Qj_I(w+Uri~7Y^!CL&afM z?=(hD++dlQ&L}hI@ea9m z1;$U3J7RMOX zu;=;PI0P4$0pqlk)Y40pX-`owLapoc!j_nyr4gD2@{n3dzN*jNcD7t(zE@^6M$crM zD5T{cK&_iRn|zvc6Jm-uq5%ZK(^8epXag7pg@EhOgOV;PH$q97F)>@Lo{yKCAg-1L zG@i9dGx483s|%fkB2jlI%7}s0T;P(>x`T$o;~YR1hNq)y@$vTY{QMFep7@wfn)9*$ z<)8lL?fqP}v#)>m_w(oWoJ>qy$Q)+EN<^AxvrDDn9vM88jaF-r=J>yyQGumjYp?b#{ZR<&^(uQWOxQw zOIy+vLgmOs>?@j|)rJ}I^go3Di7G{rpnsl$W&Cg|)1|aGc~9B!fag(;1eHWQPZ=mY z4aC4@Ij9HPdD=XA7xQOTT`VQ*3Ze|zBCl+1Qt0V3dQV9yRaR3hnx#Y{j(R*^w(azP z_ka9HyS?OFVmgEB2etvGjjMws>~fQfWNdddW)eICm)hS(yY9T}F6Y~Hce)uy7ATkAWNt^jJl`k7 zZ6wK29aKA8t|T@t?&Db|F^;Bw+Leh)2MO+2OubCG^7Cca-%hv5^UD|7#7AYBK~+!c zb}la;RSH+KiZI7p)nXMQ$1`_ERjst|AB9~56PcebcYNce#qsNTbUF;%k7HdeFPQ;D z{^OHE=tA2z^|GEXIgMm5JmB?yLK^w@x6eYQsu8~>IrPhZss+iaQTbB1Lx0wMZ0D5g z<+_?L)5kKYlqMLkbrEX{U@99Mt(wfHRn9!U!xOP$-|dd)Lsj3Gn|fSb zo0f&2B=cPcXk;&=GYly%ELsMV{-}&nXD9aQw7ztZ6VYTuCG*oJVB3WM>1p=38rO^T z$_(d>d^Q_*J_8v>y+HmXdw(j9o$a5iS!s^jXV$Yy&lzlk%SxWB@3No66ICjxr1fHP5;np8J`4x0Go6fsKe+8Dx zGLQZCc--}iYTi_}P(ME3ZrwQ(y2CX=W^(JVP2EOo-K2|B<>3Z*s72nEu8MRWB_~Z{ zM$fCHzP#~67l+e%bej%c`k3F}x7;8yRxrOXCUeZhzv)x6QtYKWH}mD34xpVOr2CSC zm?X~YMj{XTL-Vr(6W({7Ijq-_)v`|8=(*ElJa+b_WWvY#IDA(;)8G6VFX({W;9fHe7^}Wj0Dyi4q08X*Elu;IOjq zFgsC&Fj>G>lBq}HA*jPT094&Ri9jbn9j2Yl#p8^;aSG{0OK23T>w59A{VZ4{-SpCo zutjkYXuxgC5mxyexoy}Ww_7%+R+L~91?L$xC$pOrphfV|BNgFkNO}9Y6(S#yxy~LVq!v(x&tee| zFM-}3jzs+% zFUz4bnHEs8jz?l6L&GQ($>%}mButS?_RaL|Cu)Xo60&qO^VmW$8r zNA{hD=82#TMJ51c_XU=lQTgxRzyI>fFXYm$TG|*#h|QV4l|2T^oM*(|L^H0Qp9uA` zaXvvP%CyGp@Bx7!HQX!xN2*5rqoGO`^}17b#*5D1lcJ!|>x?}$UNWW7i#X*;O0Gp% zO#uGXBvO7Td*G}0(>2tWl0yLE!duR{U3Hm36En$J>d-aIMR)RwPkdOxoMG4`C*oHg z#%#qRSP4QLny3Hv|N4I`R0-Ywy=^m;M2*CL?hVkL)r+pbZ1?;0c?-n%$&Zhn_MyqOQ?&28XL~ zdpP-yy=RO|1{})+4P_vI{P|a^v6Zj&m#k3}Kh!;{;7CdQuWz3p`_udG_}9O_@4NnR z+JEe}pbS{H+n;{@@!^&1E_A(reC|I>l-r#y{cZBO??9P$z1ba(yZr$s``GWle{KOC zX$e35%iFtaxN3Xsa6?AqzUz>BgnbW>KQ*Mk-7rnAa+_Aaer&fV!R&rGUk*aWbn3B> z(~Y@k7lq)$VWaLk{`LK^?=Gm`_t&@m$@fR5Pst+2-#-tMD>wby;e44_zLjzH=U+Z{ z@(Q7PcL3?-4E@LMq>cSq!g?kq96MJ)(X=i{IRE_XkH7x-_%6N6+Dn0cSrV85+u^G| zLnz%gSYhGMEjTL5g7DMolXI+>+dX1sOf|H++pFg)(U2i{21})6;LUuwx{tYXZYDjt zKrIC#9g_MybV(O4lS6k$FD1E6>h~S~!e&!#hejxsFyjtoOVwo~R8`3Fus5T*Qt-=| zzZMvP4}buk^lx{d5(8`+41F05_pvs4QBH`aV?Uy4D4}(~zHO6Y-NtX*BkX<}Xi=vz z)4G{dHHVLrS|T4SrQHq^g{qpjt?0`gWgPr`lSCXdf-z2Sc(uSHJO#M5+CL~#`I%M> zOiQ|Cq?uQn3wq{zKU_X3#`U&n*U}zsL?=T5@UoICt4$K=MIcE5yD0f*BWt20r}G{C z8a3P|Y)W+)HJ)V@h|X)KpV(kYzekEC>*T1SZB=Ztu3WP$}$I<0RQHLJ1ZIGH_ zoB2|-d;5^yjM&a43u1u0H4RxdD;^-ZXeR7WWG)yYmr@|jrpelrhAPC6XIV$roX%t&3b40=DbSmSEJZRB2GE%LJXRYRk^u64d>lM64 zYp$bvClF%7e1Tx*iTz@rtW0f4P1mPNGoH@H33j(-yS|Jv2d1hsitTAN`4cnejm#V? zu2uWA?JnEXnFmfUPc^R}TT#$Mk7ZvxSxts&U6du@8Bf<26d3jpT zE*VS7q^Yjwg5TcW+NJ?zV2pxyp(~87OWAPUb@Q}QJ{UsK{s#Miyj|ICT}FfwAO$mT zquKp3yB@^UMj!qh&}rN>k0@ zer&AkWiKSAYVDj!Xy;@xn+Ti2J@2bQ3t9L$BW4kH&d<(5%wV`eaFA3X-uueTxK=gRb_Eb6d+ShGkN@ezbMlO z0K%f@muDp4A=VLCDJm$F;H0le^<>(kl=(DJ_?)v#smZ=OeeSpIa`El?Wpo}E^Q>b5 zs7M4<)s#nrWI`F1%w~_BS~RnyI_Ut(%0NK%BEuY06}Xw@$N{$R+l8>TKmD_R@?YP6 z^vlh1^A|78*UfmI#3{q$L;w8pbcSxDz!ZwAB*VcXH+_;|m*e(;@~r4%xMf$gF1xx# zCAHL_fqwzJAy{JXBN@gm*M6c~rOQ8?Nrqh+VoD!`$CL@q;!hY_Rk8?WCV$FcOnuIt zo^yDc=6rtaW>vjjKl#RRDww%Div}ZU9LtH7PAVB|+qP;!6)pua`y`bOyXX$b^iXU} zmd8jt5kux4z*P3`r{o@G(=GX@au&;)+9*Y7rEwx>N@XU?W+AP?78;n;v6$8Vu=haB zHsc7ltW%&Zlu9a94hfb9$n}(VIvwsgV|02t9I}?OtRYqnl4KXN>ak-%_>UL~Ls>tf z?)&tA`M>(#fccF5UFx*maeo=~p_1w2NvDpwe|-Ou6LT_BGynP5UyD1IcHA#H+*o!J zHwta-x!de?kYof8`Ri@Y%FG0Ee*f{7QmG+Xx*|o-pSt1WW7{<8YT}GOKR-^#)5qs_ zx7*9ZM5;_ci@BYlT?h%d%3qhu`*!O^W(&k4S!CiE!RXJ^d3(C-yIXgg93&@~@&1r~ ziexnigr5X7B9K}3Dev_DDRsH;cE`^hRGLWhmw)v$6$k?Ea}ctNpMU=Kl)mczbjrF7 z_dN_2L%HAIKDzUHcj|t8dnbSOkuZY8!oiMh35SDkR=_&SBmCl-=E*! zKi{P^+x;)!-$+9Wz{$`C$(u~$b9VrmnG=rf_eXEx7>eNXFinewb70nG^dVCjZi)z# z4=346z9Y!X=Ky~|fWLlEaQy`Z7}T zoG#sU`hFOQdpF#Fkp|wT{(W}mkK<`K48A{QhUd%2;h3!KdVQC|!R}|N;H|$`AIFRI z?0w(w&!g_1Z9}>a$2*UpS>&N*jk;u$NnLeaK?McMz$Vivum4$YP(=A(m-m33!M?r8j zsjJyq@}QZsMf1zsmafhhO?sod?)~F43#I`7=|U2<&n9@F-sn1Be1K{9+4(lhYQACE zUoQT2qtVAH3%j;l=7d1+Rojx?mafd^V*crN>nG!WGUefp_tD`tnzyU75Hg)gygh%! z6Oer}N$=lsxj&x?V%jwC$D`}VzvGp01+$kFPKq)nTQF4Wl4SCFJ&kU|Xso)u>s!`a zoF#teYxD970~|U1s@nDa2_WNqpc4iqy}XaM=K)jh^&IgR^p8gD|HyDo9UDH6C)zSz zG{0`Q=--!qX)XHfv$xbvHZq%Y&wu&;t~sOG{4RtPfWW%(r02Mjre64$6F|OP=1-m! zUI$1JoTL1ar1SIO2m*jXbFtYL6cV?hc$CfsFjN4`>i zE`^iW?MhDsw}L8^&1aK#xs;!fIgi5EXeE9D-tw;fX_(f_^aud`Qi!AtGY!ghI)K7P zwyFsz39~ZURib|HN0Wh@x?FZg@#PMb!Tqv@W5)E;$%g)8f9BaR4QKpgYlUNwO&jAH zG7ULt?W|TQ2Z&3YKgw`NLPV??@yqORSutRI{rseT=T!qKFX!p&&m`#p)0x13yGG&j;ty?)t=BkUl zUToVn;lMG9REyUGPXhdh%xt}0jc>#AW+nLCrt>Pha>23#GnHQQjoK2J4;q&ZsRgp6yJHnm z8wf``rj#F|;3V@$S(2-1vOX?aLcy=Ty?`cVxbF*5JiR(<}?9($-gmjlIxbOqFJzp$k>Bn=XjZM?Xy%<-s{m>H@;(q%2JKvcf z#)ph2BBu0`CeYjvS%6X=BD!&OgPyg7_t!nNdvB z(DqOv`hz^IV?nF~F|+v`Hx$*bHy)Ez^G**w0R1?E6%Wo7rAFsS*rFV2;nU>L!d@9g zo@#BGUbx)*A37D2Amazau@iS1i4;dRo%4sK>V7F zlc<$AuAkcR|Ls5gf6G(oe3jiyi7z`6AeSoBc!=R?-G08mf%#I6&C}D5A8+kywc7*! zQnBCmN!@HVvK{a?9Z1;+x6cC5+3fxOoo-5Y`>tDWHcTrJB*{W~a&X))YN+mf~3bB7s#t>9{E=Vh{MdKkYNTRZUOn+}=!f-q zcI*3(-FDp8yY7&^Uct2Sw0(Y39R9t3d}JPHrom;2n>!M^Q{Q2AO*3-1@BNy&dSlt7 z@@0+m6%uG#K`YC=-ag)+pI;d6Jj?B}cUUHRmQSX?XWR-QUR=Vv8Tq5slmpT5%vd0Xd!|0$M2vakU)HsxzadMFz4hy_ z-|<07vPn#zeltoxs2%e$9r>7?w84d9mbDG!l)w~h1K8C~MsaZ^? z<4jq|7XI6zJ7z}s;wtOC-;{%Ll)7Fv%gGo7&&s~iJp-)fq#G^O78l*Aqq=2*;z`UN zGOo(Ti?@}{r~kMr3!M*D##t|%HZRPNFlqRp%;v|0lVVAVeSz+ma<*N@AFps*TOzMl zY0-!d^gQhSo(UzWm^JgoMxK!2@1VNO#PK=z)G1vUP0Rfyasn$70=%S?&^YNt_i}z; zIub&|>7@4Ah`c$kOj~5p3QM2Ibkg8<`k{7E$d9*=rdcKA+;3+gihkBrW?Ng#u+buVBmg^C>n8yG5P%yY4Q$eQHkI&tg=NF2x zoX<}CefoKC*S5`abKGm%2FEbx%a28}17G_O# zon`wuo+Jrpcjl4hHs9ah-TU3k`ml8|k*neb3w0fOlI2XgY`&D8bJES3lczF!HT^oV zi<}arUC%F1fBfSgm(=w#Xp{nC#Z@yNRhigc*D~%zhj9*31FoK*o_iXS@%1UQ!XNP; z6X+8DWa@S~utrZPvZeR^~jK-tOF+fYe{Eo0pg4Zu{kF10X%<7zkmA=zh93p9Eg~x%(kJZnW`jQ5)HLMl}b^Ccz@C8%v#E< zGXRdTGK*%n+w&^Cx|@Fb{P+Y)CUrA8kGsS9b9ek@`#ImV)7kiMeSN(hj#(Hp5iima z%Qb+8yWN%)#NB4qGOZtP?|wpg@kMK8K3s=1zp3kI?tQmIkJi!cpMU<@9jbE~4$8W1ej`<(gyZ+jUNB5kvOq7yXyiyz|ugSaZei^_M|Ji_RIUV(o z{#4moK6j>F#bPtT#*1$syLq#cK)$n_;qZGgnoZ5hNVlgZV)$~^VXiY z4!7g?-OFPA|N6iFKllIH_m{u;@<02x|L61HH=oxNpB|)=0@c zDz?tn!WD(p+;hfBGHIUQVCJ8nAKJ-dQ!1uGrn~JZW*IKK0dSs+1f?(WdNKxBHa|?U`2I3J+Ffq zBx(CjwnmGJ<^YiC>A&92X~KB&$A9@RUtV6d3uoXKzmR)ra(pgp%>+)dFXJ)2a zbflYx`$up#XW(&`vZX)%@vmOL{j``*N9QADrKV`&5LqVlERi6&YQgNYxJ?!KP9boL zP`UEhn@7{Vu^T1$(TnO zM&Y~?J)^Q^n%x7Sh<)c5e{SE8y9#i;1fMpqPi(#vl5CVg3R)i@?{PEukS#%TMpBja zi#I--5py&|&|Pk_&v90)OCCP|?9cz=mtVhA%~@vKO=tHr08BT$i&RZe-tl+}(`qPd zLU;HDED@Aw@WW{qypkCnnt-3uFuDOPdEJXbCf%FQISA6BW^oOGm<@LeiCgUUJ9>+gW!tOkm-P!g@OD1T z*YI-~*?QC(V_qwcZ;DNpMT z`R#JvFgKdzM_AM!l6zmqb$?R<;~!6{aYj+cLP;hFW_qU}eZ^X^5BmRj-~aUO_l#(N z*v9F|a5$@n?c-<(bdz4t|o-pnP}Q(L$Ept~P3+sEC0D38

    vPM>c!{& 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 z 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�}pWhVVx^yk30C?JA1IzVzqTD(P30)BRKS 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

    EsCo9G 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

    exR0RXj z8$?@y@pt_|-Uzvaud1=Cuem1_;`GVWrN6z^E+xfV%qZrbOS9s7{xBwKBnNjMxgizD zpqJZS>{?>M&tDG-yktk|yCdojblnME*`H8-eM)yHMH-<&x98E7bnCS{B~E&M9yO#9 z(91OI;`N=r$MILfHGJOf^F!dj`>(zif7ZtzK1Q3upZ&{E*I$G+?(c6}gEwSXq+y8r zmo@?!$+0ZIE!N+V6`Jv7qWn(7p|Ha;g|jGgD$;JuEMk zF|=3Ul}oa@LOsrcA>$WaW~qhX`9s`%OqKTn%qj+{6m@j$0i-Fp^85v>GT1A43k<7->qQymUs<0|H+rR#f_6cp;u!Id_{?Iw z`x}-PE58!O$M5+er!V>$UtLmPux_2wRffb@W0P&s+Y-=a!h|OP@Tv9Ec z98`*LG@TgJqGYdS#!7#5C~`__=g%7fwfiesnriiy18pEd(#AC5DdV(JL|E04w5Tz% zO!A01H3RdG&O8XHIy;05u~0R;0*i>qdzC4%jMR_{KM*qviaXiF-z-J5ecz@xb@BSy zonN?7*NpuUnq*)x=S952Jli@@glI8FM=+_nsG{F2Zr;dn`v=z`0Wry_=3@@eUXa<*-rHpmRN; zu|Wn%Wt2?9$t3>F@`k~upPzCla|miK!tZ?3X0~bY;~)N+c;j4@|7Kd9vQ>?<kMC1&5W5z?43 zDHdZ_KXk_s3`NVpMZoxS&LlMqr3l1qDB+qc=xs9eCz(uZdhr1(vi|boeXxgKHw8u@ zM@^na11d>Re!;1}RZl*w=I{GWB%F-thavqiB%F*XIZo{PWM6kXMmz8RhN%G;7w(9Q zl$WE7i84GVJ&*tC2R~c-%#S}f;E(>ZKX~Ig+}7O~PBe_tEdE`WJ8P7>hpr#R%K=#&b3V=R(?PXpG7GCUPCk2L zGEUa$s?ueA4l-wy_8#<_iC$aPLah2MjLbFZUk3D3MpsP0f_A&l@yWCJ`J+dNE3em^ zL@B&71nRN8ZHj3?a`xZYA0?iG zA{?dKC4&*;XvC(TDA41uZVc2$?aNiywfgcZF7|pjCvC z+S({l)-)#bx%jz>xQ@nLOBB|mP-LT0W;zlEVpdoX`XOb#jA0p!y@=~b>^RraR7_?D z%9@5zF0*xKgTYnAU>GsDiU>v#1qxV(IFY+fjl0`B96uKeYf&_qrLg)%#OLXiO`OJ3=kn|r+M72m>!^Wd`C~a*r9gUfeZqTi1w!5-0%W(8L zjuG=X8kJ2NyLU6o>p%IAzbWq9PL229d&KVDZ59-G^;#gd7SkeITbqoOPNb?b)0;Wk zwu4sLTnSLpDw`A+Q^x*?vQ-hi3OW5*P{4TL00p#4NhARcEA2w8k41;>1z|ACl%WED zoMDDtV%p6CfAqSE{eg&IqyF;ludLT^(g$#x5a9_JOYF>PQ65CatfE^cd6q*pcT$A_ z0B0`)K9%@*rT7WjG*Nya-uWZfrDSATnpagIyxgFbH&rRppc~t&#yh{8Lhq$JQq``n znz`(4Bc6o;u-~l59ss~WZnWKp??2?j_a6e^=gH^Kxd?|0E&>KAy-+mx07aHLI@6wj zCJAcR>5l@zDJgvPKmHF3fTkR6+&Rl8>Kxsnv0rCbHUK^U^@(_$M8-9?1uy!s9GW-_ zt~2is=83he-7~4bFO~lyz&p-?ewq%j#n>Olh9nQ%i0hu|l1Dl`C=@eEDcee;+BDF# z?5Oa0HV>*>V;(Is32$u;br_A<>whFubK=;VUYaR~f? zp+BT*RnaYt@Ouhn_3$N1TwI=N7$;Io0qN z+rEoq8n{7-=ZB06AyF#4F}2+FnkSWH+o|Hs%NV6nY#7HmWL+4F&EVs2eUs*W(G2yc zKll+K2r|rKa_+7l(D!=TFmu)Hf}G^@0IQge;^$TFz>#FCsX;1-AMA1TZj)~JoU^U~ ze?`~Ag;oqgwslWlKe1*GJ`%~j0|{)KT=eU>kYGPqXK?T6>hVGL6*S^TvKXR{mD2 zMQK{(_*aP{=Z6w9NUYb6YV%#8{3T1LWa)&R;oI@`ugikwGTdH)_7i7j_1+cNOXke=JemEKLXCeM+M z{nN^?q}xpvv~6ek4n92=)t~+Qb@m!{hOffdSb4#4DaZ6CS*QG~%Aso5sXE+J8IT5` zR)0H)yypiooY=NZPTS`kw01d_N`CQWm!CgLl(y4N_mrH&Tx<@XN(KM+M+b3V%5i(Z zhte>p$KzjT4M2aG*Oe?!Wa+oWvz~X*#x) zOYB(H%-JGy_x5Uh^Su_$`wc$%>CfoA?4fOy z`OddQDY)C~GaKfnB_qa&r;WgEc!t{>la_xs?r4M|r0u#={#xB+f3HEed(OUuh7bBZ z+&9cpJZ&fZlc!H}qsddtywmQ{d?y)F^6BRtjBRl}KW~dt_22x>DZiC60;+td%89p0 zv(=>ccO8z7nj9T9IX>=Ca$P#@F3pzk#A5@{dZ#XofbP&~pQSz|YYaM>t~hZpN00V7 zder6k{cV#oX~aS|!f^EXk;NF?i?v2z zmE<}5R30F20OG$#eGEOD{T@lu3)Q2Lm*bNj$0t30KN!UKQW=5FY{&U@sR0JWZtDd@ zC}an-xKj_kREl^1Xv4-_tiMO1TC(gZ?CsU~&Tl`WamR_7j+*xy>^~6a^XX5Ya`v)M zWsQrk@=SrZhEqT`&fR-%GvyMxv=FqIk*cXqtF54TJC&GfJjLI0ZxEQ0#XEXJgGWadaDPV@#`6qum>_chKbY^BL8u zi7Q=ieOG71jEnO&;p^e@dK?6@j3{>YCqMtSxUZH$y;(=ovf+($r$Xe1oDZ&X?o={H z>&Y)qaR+^#?4GjMsB?NUz_4{dLzmEn!v~_Y`0QDa!@~x@^YM2wpC4WIXzkUAIx-U~znR&7RM}EjmF5t4nE$Ay+y(^NI zz2cN8E8go{)UAg2{ga>mjMMgjPIt{UlSUvj9CXd@o|fMP_8d+|)GH3* zG>VOYl7S!aP@Z2);uPj?Hsm?&Ul6_?63Y8j$>9ak$`6Uxr#Kx)Q*n%qdkTLM#s*op zjJV>Et~{Kb_c=T7^C{ri4GzEF;Nyezwz0vO_%2K~? z(t8zfemg#&5|gTiiLzCR zIrlSi3unN}*Q_BsSSOS5>V@H0Nx0MR#@13XEM|zc&5|t zWt6|3ioqX0*yB_}VtXm~{`B)CY};?_u;19>tap(y1goU9ZBUtIp@VuMTNbT4I}GJ9 z2bTlMKAXtHRwJYQDgmf+0G+N_pv^tG(Y#0l$5L?Sw=@Fz=ifv-Uaze7{|EB=wR^)@ z`J1~YGtZ283>+{J>IkR5S>PQeHs4DK^E>59xRM^e&=U`5X64bjx5Qjes%) zX(M2ivY}xZ!Lu*KdU*ClFGkcJ9?I)HNy-36tp+<6GfsZh<=M#@hmZEz=)cS^^noW7 z{G)H|$!8Lmyfg54n1V*uEtB9PTC4n!DF2`@3_x1>f$YEyC2vyx{A=EFT91-aS%YwO zM!ViH*D60`*;;i=&(Jq$R$0*-yipv99E*T*$sp}Gmwto%0tn<;ECnrA{#~<*{eH3| zoqpct8{hmId-ugX+;8mAdG#u@>y&R{GadyabZcwrd1VC5?ONQe3ebFahX<|grPnLv zhm_(gFW?q358KheUmefGYIdS^qU!zO^crJB$Jo$?7m)H$2Yx{3Jctbd2&Mld$LVKC z*6Z+Mz4xcr1g0SL!`PcNECViuVn*xP#+%kB{F^ z49K&SGd5s^Z5fQiE5g?cbWChV!zo|mUe1{37=wD7lECA>$t;{Q+knI0e?Q)p4jTp# zF}vyT3HjsW@{g3t-=jOMy>+c1rKXMV#qAupQij2DXf$(_zXS7$)aeDd^1Srh29&1tc@fXCpGYVTUlMP12DtKT@gI_fbbRv2$2JGE+;0m@3 z4M;q&JC;r$H-yTL4ytiVc>+7)x)^unaN}|Qq=(TIo9o!0uvgQm-#36cy$g>@K@BG@ za@y%rL?hBfUrUi0qe=FBj9RXrZxO|tY}j$(vYhIBfs+>=7zTR}_X+(l$pJXIjI?_> z!*gehi(+A^+V*IcSjc{F$fJk5GP5Z%rA}$a&p-c?C!ar$Uzf%%Pu$TG8c7`Znt1;$ zvi(bRO)KP0lU3^}D}R30qxVAGFL5MgfP28Hmo#y}RN%JZ$arWlMMf&tAc)woF&mM1z~mNy)rI<&H+^hp}g&M0wS!(j!o>LyTexBQ=L&LqQo`KjEfV z7s_@t<_kIaL)Nxc$e}BX67iW_EAS)L?z zjq+E`^`X#%)*fHVGFsqAn3f(>$Az9Om--X*6=T;+se4zKypw+6!=jQog zhRsKG{v`(5sF}=fvZKsOsrzX(9SFqvx)Nv>C$&v+jkLz!{|A4f-VohPsOZTBiJ}F= z@sR7Akb&Q4bQ7|XqhPY^5(U8budwVY@4ZtO4AQc}VKLH}M`N_gCY8Ors4|`EN-C0t z4ric^au~^Nvh_f?Q|dbwIYeDS-=wS;<6(-U(E`1qp;t6KcfzYz0mhw8jOr%FolQ12 zDqICuTm@G&-f3{xZm_j!u(fG$eG{^gYW$c-5mVW$VQia}E1PUoHrS{L$11vv*r;q! zu9TUKCK%fWv()Jf+=v(FU$K9`fmyAxf4{+ z^vWjFn+4;E+$|;{MTrc&j;0hSu(c_U#S4Z+H#Zaun~YL;SKSZ;GAe3Bx@_LLQk2rO z1?N34@c^#FgmSvFi6))Mx?@n^Hh8>W#~+Po)pkS}aN+YW|N0kD0NdEYyt_rG-NX0A zAerK3l2gzso7kJ1RP=J{C!2)K{w?`w+;b=Z#yux)v;vsjWMk}wq`)w?b;^|urq@>p z>T}i1q_I;^cH&Jrmb(B@C{W&jZ5b{VC~!3z%hw5IpG4x?Zpa3?g$$|G?ysRS@dGCQ z5Gb&r7n$5loR&ZjCcP61vGhH zo*xk1+@M!BnA}X_97N$i|6l(_W)EoCI<6n$1tG2N{nufpVFYW=#82Wb zQZB4+88lRDs`OY~KYRQK@9H%7>j`-0*Bm;nnrkTA6rh_iM%N8hSV*CQZbw3D5d)Qcbsax zzwXv{Xw+&Ly7+v>Zv4Oexxo29K0Kgab3l(cdpV%nyWs5QfJ&;W6vRuKeG8)ut;eX?u+-L$cRD@B;#WNFa5_TqJyaAhtK<`u967jaD|~ zOouB2KOoAr^VZB7X6k(1tLzl)-fJ?wB956{u;%?58+BQ?tjm~U&>zI1fa`~Z{xH)m zsIbo}PCb1wjGsRR{`68@@b@3qIeJj%@%xE#0et!L76{`atNi~yl2-mzlICBtn+}ay za>0+=9S$X;>f_%N0E!>~Kc5T5KkrfBZN&ZJ>d`>Ulrh7=mEU6!`5{qpj$@grSlp(5 zUtaS@m0Cq8K>J0X=6*dclL54QJx+c#VDKt?PKuFv=ng673}Wa{mT0!qV&^dKdX!@G zar@|n!2!W% zpJmVMTq<}YdwtDZZ%TQQCFLyoBSL?KVO270&yLJJnW?w$m{p1&nw)D9`ycTi0=hNHxs-dB)XY!Gn!DA?qTQ; zSyYNN>N_#?@w|V9ZJFruHK#B7+}$>KeHl_HK*Khu-?e!BehVm2)ZpSW)uS3MmR}JYb`ft;l3DcVi zdZiTi&^wliSrc1Yy-}rp-((RbF>0Xz)|QP`t>O=__`7Fc@UoLtqVm>^p*v(O-9kUO ziDw5!H&gUVg|c)%>+cvihO9@ACU|a06wQdj8=~+A-PmM0g-XRB8ihnr$VOo^KKAN8 z2QAf)9!A&k=rs^-aWJ~(i?bILfUD6p`sOBe$H36bgwX_Vc#W}D!PwF{Jv&F=(&+Xi zZeTc~L=kshBwEM|cawMdQ)|Kf+7o(nb2BFJhs=tTB%A{%mSg1y!5Ea5*aL`PFdN4cYi-lu z`Apm_Ma0-RXk}b1$aX}f<@4u#N}+^;zTb?0jvsb7e%|Kz3+WB4ssN}&lUxsWOBjW3 zzAKdTKz2;IvYSdZC4KgVV9xo7J$mhcC{@NB^aIADe9i%mT20<>H92ZE=}A6>PWy~c ze)1{a>*YFanrT>3FDWtX#CWAc0A-oSn~4HF{b?7YoLJ<}S)Y%;Ri=4gz-0L;aSuh_ z3&E>49IQT0mM1sGuD@ksS|+o_oUr3& z*NjRa`Vn3s)x>4hnWGxZ0tS9S-w)Y+S9poL?>5lqI%l13{5{6DIAYb^shin$@kxq_ zs(HUg;h+84zslqQ+#E)JRumVb`_(yn@9v^a#le2|3!!M$q7l2Dp+8|>&^T!BB@HJ* zl2y02bsV!1_e!6(d(><=Np(-D`mo9p*S2eX`@KE>BdOr0SK|0iz8LW6>ouZ_+$g6l zvA{n3P=+3B_&%OWf(KnM@i0w%Dhs23`mY4__Hn<<7x%lYYBL zIhEpY{ea$LCee>FDU>6K`1WC)?|)bHPyFz&p7Q*8He^!ue^myvSCmu@!=Nl_#h9sZ zZYY!@N7_8nUH0#Z&-e06J(@2pMx)!tKvDFc?t5Uy2l-p6|&6JUh z{3s47lqjD63)y{GPE`>cYnKoIUY%B>PV;`9&dWaDpwG{L^(9Y^W!Za?HG;~#mMw#l zWiZLLHitPD?vW()s5k2vmO({sAg&x~&W5<(^}5O95B7L)zs?VSlF-Eeqi=o)z=t0Q zo(`3>ev%1 zB@C;?jx8trsF+vo;I12S`lSaO=Nz=oIe0g5OCGh7-#`AnkK}LS@gUc&aPq|<^Lxu# zMy3HgQ9al>;6yPM9zQ=?DD%bjFj0Q-4Vk~Ixp|Kzt#&t+)O!B1 zkNcHaC`Vt2zvrh-heJ5;C2rcdlr=CBi2)ENzIt3&SuVm^Azv0rTsRARjN5`sZ#zOXNMhUhhO|U!IJBiLD+7` z|4&1fAMVz9AUBP(6c<5y_cymqg3BOQeh^BZmVPIBuQCQoG1G>?cB-)#kimbr;Y#{% z$FgyKkC7k6{Tz95=W~_=*=6ax?DGjv`S@Fp`S9o=PmZ5wlt29#VleT;*bt-*LK+j0 z##iJ+8Rbp8en8o%@c4s7(SeNeKl$Zze*W~!*Z{C`d$03G8i5AHioZ&_U!5};xHRrH zXxwYid)1Q%T#CbhjPhGmTz7!u=-3h%k3lXj1`3<)~z9;YeJ_P;$j zL!XKhdh~U%P&Mfy#fD(|cKrH}zn(PyJYR9n$`c@`pOqV-Fg^L6JW3&tK6g?#FD*$3 zu)GkTpY?fg*5{$zO$Wh>zUWgiMO6cY;!oTQChi5AW%6)W9OuK`I;W9G_eDQr1RzmRX^JlE9$COs&%<^Jb2E!o8=+rtnX?OX-Pd?@Ri`#*0dD(Ei z9>x}wN<|J@dwAWXZd%`~C$PaeoOWC&K<}zYy;-NeUFSk3ao7Ov=op1LXYC%Y=Q3$E zxcjcnZmJt>blK(HJ!7}EPh)$JgMFRjQ;9V51I|ys#Pvf?B(-m=$jMRfaCKqr_WDucI5ap z*%I1t93qG*ykh)8x=F^5C^=<9nFuL4Wx^maS#_sEbS39JOhv3a*Bm^5PI)e_Poq#o zw~craYFe3j5TMDKjUP7mX*Hz5(4l{FMmQbg?26;6$U8IfLI(X1ts-{ubJ;IpXRC5c z9FSgHE&iG4z{n|qiIGwIS#D?7yzJ*i1u=?C$*3@)W+q`rB+IxP%&0J8l0pu8^q^Sg z^rv#o>^JzQ%{m``^qBu<%jEy~i+>AqF$xnj?5ajE98+9@TGX};#+N~cJ|L2wjymGN z4{_`y!RuHWwzUU2MQhaV!1uqz7dYX%Jc3zmvQt!7K?6r2OwH$i6-t}Ea?7qFrE}jHNs1CLUVs}&B za+ui?EMaQf2|o|*=aABlwXKl#Jo<@i?}PEL{#H0_)qi z*nNvmwraHU`{a4L{wVGdNH_TW;4ePq*~w}MzWN>zp;DeBa$^<6bA3+&Il3wQxk8$) z>3of+za!oHU$;*=Z=cg^_h_^l*mj-9wou}p1P$$)Rd&rP=fMyL5$kZ? zkfj}eBYjxVkNKYL9M$#dvo7|EVGrs8DVKdd6Fb)4gT1BK0ytI~H?PpD!K6?Hx#Nd^ zv^3V+w8|87QRm}F$@e@x5!c7rspDSsadztHmPTV+qjxFk%~NF^&ktc1WvR|3W07Ju zHd2jB{mTetOT1u+ZB}E&Pb%9c{c5>1olv9q0RF&1lXx|O3xo~(0Z_vMORI#0pgA?Q+;^@a*M)Pkx$AyLt9V-xBiOY)&?7SCkgKEEOSAWjxi+T2M0s1dMWIl>~%W-jta;qwG{D$@e=mEvqyV z%N}{Jk2-LtO9OHpv|Lq>J0o4H}Gh+#BhmDyHEQxwP(W67InfMrlA+{tt@G8Lbvuo7Dv#C33h|NE2AIQcTw8m&Q1 ze&?fi>O@{P`L@KDvVcajL9ZiQp`|{nQddtJ?08VdH2(qO_!OSrK#M&x&!1#+B6+b3 zHOkHG-^X4Ajx-wX7zHG8q#~(7sh$K6{qZ0DJ|7-6`QQHh-}2<1HYc zre5*@cJ9@=c-80Pm2m&(z`=*Y`aAbbn%}R}eSXf_mp#tD>`}2D>bs_Zw(gqrF5Eb2 z24E-(q&Oz5#1#U7-+HgfhmV>(krp$p{A*<$Wtg*iT)3JrEO-dkpv`ik41gh%h7-AK zx=VR}A$cAEC5B~W`mc>m18?wV59Z`WpB>ZSLwu;g_=DrnK@q zWt=s6eA_9-MnEWjB0u`^Qw~~nX$Ttpvp@dF{OHF&i)RJorJORja~2p@g)qPVCF?Oj zB6)XMb0+G6Vn^yizwwhbQs_?p3KaboL(ZviES8?(=iBQaM(B z5Uz5%_FFcMc!2^L}5w}j7RyKnf%Po{)`SH}jLfoX7gd`Ue zH!`0wu~>4?ykyQuJ*deOo8GjBvknaav+=e*asKGl&OwPMs}8oAhQ1~tWUrG|ie8EVsQ!A0eXmMw#}tgn5PJa12;}oBdAV&$5Zpr1 zE)9X}4&&W_>9bdq5D!{)PTPG>+kJlYPRqL4i&@f+)za?=lJBr9jKJN* z7$}b*nj}e-&>u0TjANDKyo@JZ_P1pTRy%+Ed-_lA5YJ0?e`NrEE)#$s9yJ+WCqa%d z0>b@2kof(^J(I>il;pdwhRj~8@KzXs@GUAhX|fTu`>&Ig9zZID-Iy(k6eV2Mu&NCG z*Eq$LnxvqtTzCuy0j6Fex^stvM@{&8g_B?WjDZA_a95mY=tWR0Q}#8=Bw0&^EL0tK ze|HA2F9Ns{3#eu#=(4l+85PUODs86y)y1%~1|{^z$u93ynWtQSjX5*oOT?V|w!^~* zLeb6#9+V;MhYURr6^oY4lwtHECaEm=$PKA@*Gv`~gZRAioOtuhj^nwLn2^wRN{pB= z@&t0uv2~g?hj({toOF9UYBf0ZJOR-9N|t41|GZff2ERPhV#Q8&S3TwqG$q57zO6G~ z=rC79N@OgYnS@7G*@2N#w^m_?D`Fi{Y zi+2m%x0)*eB=q8Ls@pHkTNl?tZ+BTUUoER1zp1DgEiDOWz-nxKv(=>4YSO5g^af(F zoDV#DFS@vX5F-lDPG4mRNHm!Rk2~bj=lg~XaCuuN8m!+vT;F5VjyP=X@+bx7`s62{ zExlG%Nd|-4clD7Q;XGJ|Kc{CXW)C8J4?q3sXLNh#eCJyqv46k8WQ&+V7r=3f@uTam z?jcBf0}aO^8jsP->xYh>`RD9gyX;%LbR-_dwn~{i!>K~x$!XGi%{s$);EJB9UYx$@ z$JMTM2|1!NtRzJ ze;UA%Zb6+Wk~f&yT^GhtDiZAP9F{Hv#sD+?cky1inf~t#YQyQL=OSP@=|d(77_MVuS)u#~kD463*CLdYO=|ZV3|{q!ybyg2Sje`_*Z|ab8`K-SnD?C+ zBhY9yz!l0L8-NuzKdne>m4A)lP#M?p`BW$CLHwYw#z=Vyg zC5}?NnDPQjmY$k_nB1`M{oZ2?iMwyMnsnM7o~D{_V42iYDZaV}vtYJb`QLI_ii%}d zCA2)>Wp_K-`Jc7VaXl|4fK3l58M?zb`D?7GKq$A?HY<$WC{s1b;3zz>trS*+>|)+G z%ACFIv46iow|9=YQ;qXABR|Y2zwiQb=ad^A)^lANj*}RHwP|tKvl;~c?Tki&A1)aH z)ps!RqqRjS*B@rkeQQOnFIfsy>pouptGvEdx}ZFO#=cmX^_>m+bB%B~W|Hoa&he__ zIKpk&tm0nI=wJ5%jr#pM=KTiV)z471#)_>oO0~<>Y@37nyEML@EUKS2INj_r9ysyj*UBV;9;+iw=>i_^C z07*naR4iIsDc0Z-a}P7{ z_Ik#J685gcvu9^9|G#??Vc(zO+%qXoXH3HDxRL}gx=i%JuqrVPdfeq%EZ{hIM03}w zaCH{qICbppI;USgW#sD^R+XY9A?h;cp*&GL-E6kK1&&&u;`%PxyOnPAn%(5I(166N$j?JoVCyCwtJb$z3vK+ER;9au*!t~1d8Ph-a9Y)n?~Qh zK@>+}=jCz@+x7bi>Jgw&#dV7~P7-FVx)y4EM1DwF?*8if=Q-qsN)k~k7X#W zZW-9SCT=f#ZB~`qCgT~U@^YmhX~j(#s;W*kHUiI{Ws!K!Ju~yW>&;{37vEip=KzF$ zM8y_^U*epA4D}A)*~>uEKf>=5=Y2+s#RKk2|< ze8PkGT0D5K#mpb#UATA`ZfpcHE`L5hkvq=3$B;SCoFa#QlvJT}p~oa=7?Ss_&Y`GM zV(~h0Z+CX99KGwr%_C1wI&|ATK9xzey;cKrX0KKL?DvX=U(A=bQh#W$cb(G^suns73Yt zbCT9>*AJmsh#RwPv%1uq0$^{~mkdCtT83q45SI#r*-BuPNIJLJsMk&E5^dGLTjjH2 z3MbuhMOh)m$(FwmYUfux0u4z_;K|Hvt<|9WRgX_)Jv6V1{m)J?c54`o9gM~fozL3B z2mlY>YjJRNfOlbYdVGp^AuMMmnU+?5v$`VA5sN)-G}4N{gt*GQcZJ-NT^C_Mlo{+; z+Yz0QkG`?Gnds9M8Fk1mx(o}kRgRT^t-OU1P>C_y2G>us7D2ZIEmSc5x{UDS0btK?S83nX5-en)x?aR+nc^7rR zWn$AR;dR1H2yoCec>J|*07>$>@XtT{S&R?r`$3HTZ%Z!zPCMzB7m`(B)qWonQZC9k za+0lGH{l9SkDubHcGh`0yGzG0x`WX?Kxagx;#SM#<8OYA?|g8?|NMXcDWCrGm`0R! z--@Y)9{NG%=t`E3w(VqGZ&=_X2JEp7`9Rjcw`ZI}ZI<0Z*TFsoyjCxzV_NijAnw(PibWOKfGn$dS6WJbVS3ovcCyE2F7qDT# zx4Bs4D))gda+#}KCqUpUBd~!iw1D9Ra9~NcGM43*My+mjPmkGT7n@b=Vx14xDIR|3 z;(gB-$?6_0lBYqix>zL7$NN0*^ZEaygCcuX)J)AxpKs|y-UT_}dR zO|y9m$YGc|hN8tIC&!(7z1Fa~Oo42uMHPEb=jcuq?Z!64>4eMigj0V=&Cq#p_W%&% zvz^|6G1El(ogs|n0v$(-BDo%}Q${T%Nj9~#B$P}vC1cKPoF^BCVzJ3%oQ$G?D<73LjWboQ&6+3sOmN^&jyssBJuL@AD_kG8{zm0Ro!8^B@|!DD=2w|t>qNh zhTH_$CL9k5$3f~*AROl?sY^=gl2BE|IY;L$S7;b!Yxt&BCJZO!atoAF0i{%65zfem zNGSBOY5I_(pxWjl^XTz?4HK{*4hUv+Ui!h;VdXJAE+(!kT{j9b2Ud<>Ic*8j6FBmF8$H^#v!79hG zk{Y?!E3*TMC$Qq`Uvtd8G@m{2;Db80Oa%3%JJ|F5*aJvQFRt7M~JHr{o;bJR58#cYC_D_=|RofgN-V;PG$iMM`Ck?Wzx z*ng%~Cd^;OCES8lVzB`LwDqKmSsik8uZ}CB;>=ww_5vc&3^nHR+av|6%BEH#drzl- zHjMA{+WQPWCjXo|ZZ?;+66Q?h{nJyWP}NZ5rlHUY<5A;BPdj|?Up?UNyAAHX+u(bA zg08NVZ;KlDjvL&Qd8SUsd*TwtkW0!UO55IMn+N}!eOelW;RQyeE@#e|DP{xxncKsWa z|7}t-=7g*mEz8U*zM+4q9Koveu3_|XhB5{$Awq#g>br77RzX=a@Aa1FDgjhh}WnoCP3ZxokKrk!`5>SZoiWolkX=Vwo-=;FY?(x`K=TW5mK!(Vm4 z&o0b&ZXfa_0!g@`5?-ESAJm9^GkrT9vtQx5;zGoMMRY@U#o{z2-E%7tJ5?HLyH>BU zziV=IDC4UUJbclTOs_6>!{k9#oaduwT`Ktkzx|<@BM2@B^m|Vkxhs22egXO97^l+Z z4!jWZ1D4BTT+3HYn0Qf=QSaDXI~IqQ@mQ0QVx@R}ie_OyV3soHt+vP-U3ODX6G~IT3Ox zyDD5dKo1rXK}oMz8{DX zpzjQ^tupqmPH~~&_(A;WUaSB*&Pk8_SA%{oVZ2OdK8OzqR4B}6zDHOfe zUK!Jkj#pMUj%2l?WL4Og3SX<&P)8$LC&GxdUY_EfODd^`8P}U55|RgZ6C~ot&z|z+ z*)veGXf)w$g1S|VpEu2hk7NeFWX$8&lMMPB<{jTKMjc7-%%`^E+NL=VuJx9Xm4%!< zjv(473aQsM%+)b~NV#*7qLV>WieHy9olGz-D+b1Vn>>9PaC|E<3Z|i^F!sV2eJahf zuu0PQplVd98dc7{^LSD-#%;J&#eFF}3iDPK{HzBUB`Dt{5ixC}dB{;!T(Bn+9`12B z>#bbLc?bU@MBh~*mmKHNA2CX80rm#3FU|quP za}faCzk4hUz>1`m|7|kz6bh-`Jn#}`-!Iz=yfz0fWz`6PfO{Lf7`)L4yiHu$BM{{= z62+HBfO6IY$Yn!{LQ&gn1U80hZP>lTxj&_NoCr`UP{`E<00Jxc0(0h^|4-m>P#ZQO6GPm#` zwdSMUA67a6`_2%%{Px(;l5t+-4Mf8-ke&0^42>`danAZUQ5Yc0LhhL#djdtH6dQs( z3R7o@cDueA3?wBVezifmtOW4n=}F8J2+)_Yk9|<1|Eh_3OefX81jF*G0f-cTlP3_#f2#6dE0K~frOTi0El9clLKYO# zLp#^@Ya|3F19?r-Mqrb;*I)>ymA_&nN>??ifPmTVwEHw9449en0vr|`HBzO}D>%AW z7s?MDHbt(XaaT^&N`ug8pU`f1={)~(`Jgxq*jnF6?G&i%qm@m4D`E@nIYVkznZD;^ zS9K=Lso$#59S#W6is+1Jc>_$Ng6lnJzH-3|n!=qsf|txbu;~{U^n2&@WfI9s!C_5z z<`hz4QNbv}#RT*ncS_$wP7rae{U82y+X<6mv&m+(O$b(D~v7 zZ3{|zfpHXr=3y}dvXr6W~kV5K}4DJ!IdyL z^SRia9(?U1{^;Kbgycuhd-yVEQ9ID6HI4`tMV$5t;%psiI~JCl`FEUQsuC%t^$a+NDj*huk#yuP2ocU-6(ZvL|ASaD^xl(4hfC2Lr z<(Cabkx^JR40F&5^Mn*MU%<4|o=N2D50i?<68ZB`@Et@=NnQZu8z7T*rlAm~aQ{Ou zOtmG859@v2;^?b&ie(jlIfrEtwPIoE0Z!0Q*oMp!=5k0lOnu(8h-rc;c^2(`n~I^c zOtmY2=eLjOyy|guvkX@wKB`+#xA?pH82}$1e-z^$ktP_THWxr+y|whu8T6KmT=tk8%3s6tj^{luR$exQlSI za^D@&U7W)>c^yiU*R=BY&xRDp(zR{Xc6EBG9lrm1h%Gz$`Z8G&I72i`OnU|XMDG5= z3s80v_o#o`$M;tA2J=|??Sl$MJ(=bS`aY9k_9i!O+H{_t2<89%k7?YXqZoB{H<@Y$1W zo3-0<*Ni|Qfm)}M3vv}(!ZOM@-Vn8*#>yW~*TaD{euKWNC;M+NN*Im4Ty6HKaSVVz z4e0m!;&IyF(3+TVtM_~7Skd^bt)tmG^}9OmDj$YAY1E5!QOmDGPH{X;GNE-nFTbmF}loj`1 zO4W3rp4h>kwog%Io&Mwdf^zB~KkHD^Dtz<4$d3NQ za|ysG%YaW_RQRW#{5{}cRVq}gjjp4ay zJbHc(jppy6nk5#C1wl*3_J)>e9$m;7-7*VUyK|U=y<4N#?Qz=efvU5ASjO7X>2-Zt zvlgaA03Kbz=t6>5_I;e!Q>+_vEK`Y_WK=UBcftXviU_HqnzCjnBq>h_+?B+so-A<7 z&@s2v1d3`7R(=87b0~}Z;tVA}y;UZhi8h{53Qg8BCIqtg!1qFmGKMZ#Ws18x;lPg- z*EE5=%5*YeQq(Bytd}|C#-TXoXT=Dl#=X6v%-BbFpu(JCY!dT^%EHZ#ho^0m(^~p)-&Qy}FD;h%xv!%;Tp~cvq>s zcA7Pg8;Q$4XMMMels}S4`Ty~8hsV+Y{QR>HUw^;B#=oeD?I3O$Ed`Fq_SwcBs6s71Oe zXSLIvaHX7zsh45{U>;>%#q{V+tKFxd7J0T|mLI_NTpY*GEUj%iwJ4Si ze&Ufen@#RE8$4@wcq(bp|LwPr-wAwvd?NeIq9!JeuT<#MmuM^6P)syYbtFr=jZJ#&ECg6?l ze&;SnvL>5W{zz)|eOyTbl~FJ_kWSS9=7acJZjn; zHEp!K%5mzPPMnZdr-kE1hER!>-*txMOC}XtryAvfHk4vT5^{uYsD#edTj{6bdVLTu zk}J+C5gP&5k<+75c#tj&raS;KqWt#JU|=JR9e|lva2JUe@cf#$HzY=5TE6$~Pq^2t z@T5K9aa)8zq2!K?48YURd)F$z10kJ_5vbi-+||F?zvPA zle_XjzbDB=0siN|_yM0j7ELm$n!S*gTtHC>(0cY1t5U(NR52@6TAyc+KV9<&_)sPh z`C(!NBAGfv9{ucL>;+us4W!vt|7A+~|K@L>5r*@O^6wvRzOm*8B9J$UCB2T;%dE?H z(#m!)KFUo5ImHWcU+=zm$Z>6-yYgIug$oU6bv9Rb1~LZ_8G!2~@&eMzU$Dw7tkXnFX*r#X~Pz{}hyTGbcSBaHET&b}>Gk?_ItxDh&RrhLp8O~OcZ;XL>|n|v zlJL^WD6nWTRvG3b`tDVc)2ku89J0S(W}Y9@II?IQS#;Vx$->g*eHlh5dBIZr;qQM8 ze_NpUixm*g8zxgZ-m9WgyP-0@yU&S4G4@-@`F!}n2dGVx9`Nu#KBV`oC$Qn9k*YOo z9PNp2ui&)9Xwb)TLvcRtoYE3nZj0$Gq@YOd040YjmGJ1Rb<~~Y+y`fUCYK>@W{4)c zz)gLE6aCKV^X!#FFv-)uT2QZ6xOZs6p~>N%#gCq};8G}P_f#wnU5%KfifRSFD*&hnuf{HT`={A zvFk0Gj1sSAA?wIdWo4NI#~sE-!!ncmJ@P_W1cXZ!RpKI8yW)9kHs>SLazk2xaD6 z9B(MsSjA30x6!Q8Xr9t(_xSkRAJLV)@dD>?lqmm-MAe71Sl3M9MV*krTmY$9sU3Kb z=4za+W9nZkmJQ75o{MXJ^7@=$w&0U{M>MJ?pWHiwh49LvW|gQD?d06$kT0{p`b}X*2x66BEG)}w(L#k`{ml7 zuaideP$+*ryQbRRKJBi!{>~S2oIK^_-fPylCq3EptbtIr5v~_v8meIUO@+;hp|7D- zr%S44PKk3BBX1}}hY%}WA~bD6IjE=ZCimcWEjfqYt3I^@o6}w&WPguk=s2D)3_wXQ zqZ&HS%Pu8^@~7ogf^hU+Nv`>bCC4bmynj(50>~h?08;Tq5-_|R9IpTXAOJ~3K~%yW z0MQrl;J5Bji{fWF<)o+Lp~yCx(R?+FxX(WDLV~$yS>4|+Q`^&N9A*v5pZ=l7&!zH5 zMxb{MNu$}Ym4K6+?s`AV68c=yz7naaC3o{|$Kw zZL5f_YV^Zooj00?eDdv&1EhJ2$7bEg*Bc+ zOJyJc}kfQdJXJpkYHW5rLtKU_m~?WMR2 zk5&x8`}Z5Xf4>nc|7VY%#>&}i_t?K>5qcv+FXZ%>J-DI9yZ_*IAKugHw0pE=RUthY zS8w-teUiANwYxQX7mf%qzVi{ic8^EDcocU@zDXjspGf&Z0IE2yNXABV?TcBY_=%Xt zhE_i>_fGIt$PPDsEgc3wX;L|57A(5mjcP{#vmQC0`p=fBaCjeiL=g(YWPS&~W?8UGUwY7LlY4cA% zea?gXO$Pl-S}i9NvV8X06B^A5RMmI^)m59rY8llTQM}Ca{PhrjG8GsBCn2Rp0G0rc za6PasjVS?ILr1f0Tp5!)XFiVWpl+!IgMApOteoItVG&z`azi}7G^Xa}b#rJ#|4yl_uwW^Ae zGwBZ$`U9WEg@sy}6HXP>!kp1U=4$40LC--gDuk0cT2UdKg_v^D_J$5M%>rixgOMl| zWXMcSftP@Q$95KZ%xD-)?%Zx$t`}*LMg;LRpSql2ZmC2V1dW3l)#?%VQUhUs|NN9k za@fm0ta0uK%%^kew)CPF;`;8$YD+P{PP<3vMK8lL1FYep zM>FrGsBy$vN79|ru^u*c+AoJRZzWztgopz0$@?GTgf4gAZSVtr6dQp^TJ1g# z;y4~X&b}6Z-aB;~?}%gRyb$5dNpHwWFP+s5vr~OeJ2nCU+DadWBfIkixkQpg5~0^T z1C#Au*Y5AR@eoZKRW{8$3_|JC&yjTHG@ES-llT`L~oRK6HiLQTDrEyTB zS=(Rh+*_IShRTA?uFTXMQINTb?u#Mycgmc!eH4ky-@H|({W3Ym_StF17#!ab z3OUqVTJ1hZO`Gq2`vE5-g-4IqOQQha{NN6a<{_Q-37r>R37wW*RQtEom{$tG`; zs5ie-i8}t0STRz5L6njF-n7WJHhV7Ys+mTpNCsZOxgSt0i|f-qb7<~a93G0DE;0aV zm}&qFsNYJAK!4z4SM=Bj46cTl+JsYgb8OplM%1n$Xswm4>fqdT`vKKaIntjd#im%sesr_ghF@bO33^&0(dFEcr}Dnn;PC-3md zHzXLytA0EbW*KG4@9-AozmEIzx@4vdr2@GI7CE8(5eY~q-;a-_Q|puN{jJzKuAkk= zG9G~I56LTH*QoAaZ-K5OdjO&QcfTbr$g`h3B611rW)-_xSy%oS>j|`UrhGJ?$hcoD zh@rRk=kbKp#0v?0mBXeUdjbtRNq{wPm1(D_ygO$-%0_8z=Z};h;KBU{5AHYk^S}5} z#tZo72Y2}9*Kgx5NhyC;>IXV=!Z=@IH!OzU`n3z3KAL5t?b1fH=mt5WD=RN}3rel2rzv>txXPGgTDJsIkRDh2TaLPJ2w8+0>9-F z&KyeR$c+o)%w|O8Y(_~4854c*(Q#Z#G7oF0HSznyy$T<{e~fb;~PK}oHjaw_2!cvJFBC>Y6rPHktv{Ph6qW*O7gal;;(8&WDvs8=l- zm87`2oPj$_yiL2_tN~rcSTz6FSgyyD{RdYN*s?OzWNwv2L0oMz+edUUspqB2L7K z=moIw0Si;txeS*p zy}Tt!)UyC^hRg}$ISde0m&hodoCSmfDDG%f>JS8doC^&W5j(LaJb+dwYyFFIR(diG z;w;3qalaHXU?Qi^5Cz~wojA*Qzd7;dzCD<)-Spo z)n(V2Bqxo}WyR}@xa{h;>-ZNwLubg)8B#Gfhi{w2UCuFCq352f*LMMK00~`{sQi{L z!__LueSh@4%LAFT`}4p0l&6o^s|o#+6b53tH^v__y=J$4BE3khkzHePe@SGx(@a~N zWR>Y^1ygX-d!x?O*a%2ABiHlE>k7q!MzNs9>Bw|RH4_3@hDu)h62mKRa5>@~Lk^fk z%HKT`dunB8b0f>0<3n!0SLe=cEu;KuT_|7wb)UYR;San?OpX~b`$lfEq7`xbLJ{4Y zWzcoH=MMF%#ivhNG;Y+W?5-=zGa2U>%3bANNovFl99=Qgib1=(b>1C`a50f%ft`|VQ7%M-3S`p8Yt8ArqOvm%%c(-s(0~8Qb zbZ_(k(z|~I6}F=MX?#B<$gC^7IY+71*;>Wo!JRsfo_Bfl`3aAncPU&G5=BO!0(EEbdKklSDMdvneuU#U6CpyqCQd+EqyRQ< z+B|P}0gb+N!J^$Uit%duChpBL&g-F|hSE1LyyM#vZS~yc_<{iX zVjO_zxfu;vG;4I)J(R){Hx+XpeYFl>t;a?nt@ww{8jhP~@;&`BYO;wcQvNqdKbq$#uO}ll zqY%g9_wQUkCsQ;^6kr{(D6Jo9PwC5WHLU1XG>b8HhVjp)p~pE0byw(ev9NBs9B;u^ z)TCe<1spHn=$2UE;i*q4uL_6JFyjWHX)ZuBA#aqK166Wl=&SrWm0gp8H$llM=r_t7 z*6VaTU3|}{)AnPp#@;V;SHgI=UWkR@53cB>KogYZP`aN&F>2D9m2zSM_C_-68oB%7 zgvL8ZG~NOKVt}d&mu!9swZjU-#R$tVc_L%>NVWdmAAZ7L{>M*w@eg7?Ox@S%K5NlR ztw+02C-5b;^Mwki)b@|4?TeN)1qYsvK0`NPVgxV=!2;h4z*R8ITCBvXoU}wyFn4v_R&sBq3$Pa&#RU}Q{J|ub z7s%44N#FAcE=7RcKat0N&rZvLW(({W-C>BeHo)bY@a5_VZ-o+~g%^cNg9Z4riU z!THsIqjycd_-ApRC9??3nJ)!eV`KtKk_%HYH0EBA%?CIx%Um|*&yf>+4$(baX$(;0 z+F0cCC@7Q+nBvC}Vxj90x(-KQJw^puFQ>S&SI0CgwxV4e1msj&oUMfZ>W4Fg=HxF z75s|<`fi2#?PH25NUhqfDxLOOTy|`?PdVH#bMI~%QbUMZD-*8p%J(~6o=A(`l_QpS z?}(|Z=<)YIeiEM_z@fyXKYG7T>$5K1-YNCkJ|CyD(VzY-!6_{6R$}GPNM!kUlYB&_ zs-xG+F~TgsVBp6iroh_IYviCQ(G3kQ!yKHl0(cJF)58c*6ie*BY1bW+H;Yo1L?`K^XU zF-Kg+U4;g(zhiHN16!5oeSsIoia%e?sYS8I%r9HAMKyw9zI!X{b_~5?OqmsN=slN;55fqPW+9DcWz7hr zNgE=JJIm0RFTlQ8CZahH&V2lf?0ajntF4zRYqi^AxaZS_V8xV~?n* z7OZnAM&VQCW*M!TRUFOG2|S-56+ga-7jToL6L@KkPD|4W=2=LzR;Qma&cRC1Ta=rM zE$;z4O58Ra#Qh|xkVbrpZtuZ*FxeU?g>M6UnXInw0k^mpT^4HxOIJd zcFfD?g-qp+9G!E7x_sEQIc(aJI$dlAje{Ef!GvLEv^R18BjXwG{{J6HTKT7*PeCF| z$3UWPHvF&*GeZHt6)C^#O|gt3)?SU)FB07QwEHg(ezWqwNxE_X$oG8uXTHFsTVl%*0|=~W{DpqItsd-SU=_RTW;DNeVcbI#=IEkh*0lV>Nf7ohIzgr`1X zZ%BCRGuldXuft)=;-93Db{_ulArE=T@BaSp^5FeC{j)NUKI!rHdHt`~?yaR?X1YAP~EqJ~V8x`=`Y5 z{Iv!mwNL~j3yKPfL0*-4aZyC=}ltiU*Kx zJ=F}ALTbV=G6K==ANA_5D*q~GVkx!LN3$o9$D_(|-1Gupk=7!R;n+vi!T>1()R zbTy;ImDD#ye&_r;{>ikYLx8lT?oVt8mYZ{S?(1`ozIu$?>EU_~reWcDE?ejdLC2v0 zI7^j#H$?r@y%cXW3ohgZc!M(vsLe`XB!NiLNU;GKww%}&wa5^YUd!R}ONR%y>P+S{ zteYdIi%C4@Hw+epo=a&a7qRSZ?JXk^5j`Gx*OnoLT(Y2HgmP72&>o-wgZ6-6o-FbH z%Py0P8LiXgru^1NH5$zt5YlP4u%$~KPK9MZ{eL=1_Emx6LNdHcnL2KqY^fd9FePen z==p@+knW2P$IS;=O`YzG4y~cgN)N<83l9nn%puaoVu>04C7)-QMzko z*#fKSDiv8O9h?RP!#RN$U`LC3DG+``$%mla2q_ec=)0N#=U8S#w3=~;g(v0`Oe4=+ zDpcSNc|Lcjnu$RTnPWCkIwXk~GIqjFl96VFNx z3d<>$nJi+}ET?e&%P=1bUVw;hi~{P#Ak$@NO5A)?y1iEF^4UuPjM{Qte)@}F3xI+} zkxtu9!K{h*iOhJ393iT#47gqZxe|ew{9QxWVgMRTt?)GP2^Km}P6CwekV>UOF<->L z@Tuq(@(kj0S8_#GQ=-e&}NybGZ7da7Xhwp zoGa-?)EgEopD=Pm3QLE>hLuzS-UjcbZWTp$W?o4sosA-a7sR8WneVCBXgt z8mFf{)M<8!OmgDfbvZCIpUoMZzS(_@LJs$ANDu^=a{G=lEnOEUup-jJa`h!LP$ zou};6q}Hs*j7p&&#vX`!?i1#GDn_!yMvFh)S1@|h%zn%XC^G+RFj$Lh9KOn=l$PV> zK66-`Gl}4}A|sHli<_y679_JV0C(T7W9^z52)cgi_#*hUs3huN_?b3kasFSTkoYDJ z$Ln589mgh!ut-+^rrMGxGw)bsj?ze;MifL%}wrMjDf>r*KP7B6j*ry?}zDGVw;S5y*&lPLR(YOQc|dKNRSy7f$B)(OB=QM2M`b z-;!4TbOTRR%8M4t=`Y3aPhTdOeBb@f1Ma*lLW|pX>U{CM%P-HL$H!tzh_u>Xjh@W8 zq-(Tk@nx1gl)(Cf(}2pphI&IL<@^9m4*EpOAD@E+V1EBBf#Z%)h2IDwH)uY$R5rJEKaIo;5opXt^1Cb1ESc783koY{rY_tqDN zrE55Tb{C6dR(FEdu+bWJYy^PK24F=TH$+(`H&Z|?iv(Dd99xxsr<*}yUAUX^5F+K@ z1PZ!ITJ1jn>!1A%ckUf>`%axZ?;djJ-9s)f3$dru`sEXHMN!r~J?#l2aG71un(Px9 zO4JuWMYlaT4KQy;F}b`>Wn$%b&U~zWHOr-UjhKc27AebK@%Va3NGr!XmuygCVV%m^ zhg!XoK`)9DUFUwEs%~SK=eV9vtJB5xd}>V*!bL_Py%PiLHUH@NtOBkx&5RnG+5~r2 zh}UH}n-J9*kDL2Ey9W3r4YavQ`Bzzjh4M>pVUQ@Fb7W-A;RX^filuzJC=gpjbk$RIoIS8#@}OFMU`PP)kMJi%V|oXt)=b zP+Bl!&TKqkOI4VAITQ-a$4g4gWSw$J?Pi5A4C!^wA)Ik&n+*CcYSqNLRpxRu=gDUd zEEPnJzX(o`;A+NlvA}5Tva@G!b60~>Uf>H%W-^A&C68r@rROMZF9^es|m@^%Z2)uy8QU+C48PsiZ<1x!{%*^pw zhGX)%9Qhpa1D256h2@p_S*-xu;GDx~!bLFVi)UT7izT*;C5{`rD8&MUVMs|Y(|Y<6 z#vZe)5o%5$45z@9FdP$xV_4=GhZ6#Ch|_;fVRA{Klt;-=QSwt3r93nFZ!{i>&bQp0 z#du8V#um$^Law;QY`J7h-$7-Gs&2t}%*?xl(1qfHFdV@$WV@7vQPc5+Wo}M59uba5 zsD?&S-KL-xDX2xp~)<;!$QA;Iihc>015=wEzcsyju7|(l+s;a2!R`S@{ z=ADBIdq$bdLBP|_kbyU)YS@IMC08t1kY_=jz#oIE#Ag#Y0nT_qX{SWBqBFRH0wYv4 zkE-SguP(`rMcg0#BVQru8c4mCtCC}sz!o|jjWqxYV~!imO&+jn0o4MoNV#uN*Lm||DhO@4f;NP ziIZ>YWsYvuu?$^4@@wv~<4rQ9%yg^r#2XQ;jN=Y2LIxL65B=Lci;UzpSpB$3qXJG> zJgj;Kv*5>n`%nB>(vv-S?~p(G{dKnBR%hKq*;Kxp$`;$H=QFujd3X^LQ=-V5FdQ-y zo&GR>MBlgRdsRcDYS>hbY!>~fS>x!P8b`8A(mD5OJ?YYV(&gD_-56iu?)!DD$_DBv zz)iuXmF2NT&(S@9D=e6lufNyev!@+?{%g^nFUE3{^RX(Mv+nwa@aX84opieTzgg+7 zAQQKJ>AgL;ZDXZ(>fH_D*y;`+t+#8uf(T+hx}Te>F_F~jHR=c1^{dqW)xY_Ecr^y# z=-wga9hK+5KH*YkuhpF>1{8B)2`pFf;UvtlHzgb|2*X+S1ewQF zN{U)QRc~+=&bX3)x3xXXj2kaQX*|M-c&v0@Xc!NjP3&8F@--fs(o{)&ioKq^P6#@!;+|{OjNO8fOEK{i;F#?1EnZJRXpN5~Uq+ zt(?J-qPk#P-HspAak74n8a9@x<9Gq5uU#B3V7^qS8f9Mg&&dJf$%MkTxGRP20yFm# zr6fX~@i8H_HUF ztIzd^soh_vwPEglbvVg13i0ov1n2)Ra$T&J+W8yJY}Mh>ud~Ph-QTIx>iD!$U?mX_ zqGjleNLLPY6a>CvBqPsd>I~y#Wwi50Ig^PyMKv@+FTm;alOZ1Y9IjAJMfs4Z_aV$> z!CwXvi|=VibT3|cK7aGK|HP3*K(?wnR#nG5FXN_=q@rZnG{|FCN|+Vtd2MT$dp4#t z0@2tkh|a{&OJfowsKD{Ci`?0#UWI}&$n5@Ke=q6Nj|O_IN*S}F;|_e>fsbaS%gRYl zKzf}=n^z>~@;lGHA;o+??i?)XB?^|t)Cnk98U;&>5qkmp7XgJlSXLS5%jA+)6Moku z2d~5DSj}aCPfSb;&gX(LCU*tP z9I6W8I0RWAr3x58~=e)Y`+1{)0-48z^Uo11r6?o-_ z%r3ixBZqM0&^XWu^Eo`5fTWpo`lu8P&w?I{IK(4@+x>*fw>lM-B9Sn><@oOoN-INm(tCQv(*`3736` ziFe6tJY_r{p^!%*PdE;7y-OT7BpiXNTWoEWDC;IhRbxlXq3^2byDD479Fx4oWw^y< zxW#^bj~xy20GB{$zsni7_N#pL?h(PTkG7MGA2%(H3*UwOf>LRTxx0baye(>1F9}b5;gv;?j7K)g{&%_J4OnF;-5?w< zP*nwTDW!LY5KeGLIl>TLzrLhU-lDc^q3+}O~PJ0gN-=%-vRHB@CqolGDQE(ZD;GM+4DgADr zN1r~Tp%&x$^at-A^1uDrX2zbr>=Zm|)@ikqeu=||%}J_@P`zQ&N@c&p4bWOqrRJzn zXChsEeb>UjaN?b{B`X!Kcb0jtc}>p5^&DK!VPA34G&`O_*S{j;;D7F6f2BsFQlYyt zV^Oc|XF4pOrp|Bm+tR)FomV~X9oM=4-XZsn6R#`1^MCR$KBUtX?ZEBsDV?X;%7CWX z@pBN!adSCJ96w7)v3M%eut9o4n;;pZPRD+ypFu2gyb0tJkJj~v8LrAT(aR!BcO6!s zGlbnrJe3=J!qMMbWvtwKCE(~*ofcvpWX6G8FJl(QMXZ17H~?oJ_xU zGV-AI69lPo9du|LLz=u>tt(X$Mm+(2z9Vje3nAh3kz$(Wq?EJi>S6jE7tU z)5R!D2mb~*a>wgq8P{Nh#eXAOtRw78vti+`Vn(jY8}u$c%qPBJnEhEYcA2tN+j`lJ zH+ztm&-Hv3#MlxkXXy=e?#7RPjff-@M#Mxda)o%9sm+&P)`AXYpT=>gbv zo4&-GPovZTynp{LpFMuY&mTWU**nBOu(2a0m&Y9$0rxW7w=h~sl$SEN`g(da0oU_m z4`3)?$V)(pXy=-)LQ+XWida12;Sxx8)JXcRY~$6ZlVXhZAf9Wn5vbSp`JUu%yekcW zLe>a$UiIk6If{1gl&Wd-_}3?~0dQB?rj>~gJmiyq@ga?RjZeP)AwT-zx)F$vUoX*r z`4)wIgG61in>u4%Z;EM=nYQd1gX@ieFIs!wCeakH>rK}^gp@evKHMncre+9ofADAj z%QCX4omV|tFIwD@%JsZ`f_sr|B2i5hHTuF%z-pHA_U&ViKd3V}^LhG~KI!ZpD zNsJY8>9Jx#G?6YLhIu8^?YHJY<*%WFiDdb$Qf}%TGQ<>7V^Adet{rzA z0`%J}pPyIb`FjB8JWb^5Yv(5~v#Burc9#G!F$abzZI5ukA(&gZko9e)0} zL+3<%pDXWqo@vHA)eJM4i1damQiw|b{!hQl(H(K!|MEZlHBX*Cr?Pi1e%(H(@kKic z!2-{vKtAq5G_3+Gp=4={WLP_w1~l>r=k5gSwoGVV1hICwUx?#2D-lSmjcM3W4e5W; zB9I=K`yy+==6P}a(RApXY?Z(=Eilv=E7Bc=XoWe!R6#4uDVURtJ|DevL_;~C3o zk*muISCU%2|K1&b_;)|$pP!yUE{9gWK`^{zB9l^q=b{-VgVRe)tAJXP z87fMBD;gpF#LQ%I+RI!HDlFIOvOKDeQt5hT$O%vt6ZHq}eARLPlr@Fh1wrepB zL#CsU&`rvarmZvE-a#oPm5PxUu%#xEdr2+9cqWX%ct$vyQB;;p7kP@x5=EV7^w}~k z-v8=dJpU3yFQfctfBGli2QbTJng=!ZZ|dxt0#fM0^I@5n2b?tkv+4UzC1Gl0N!S&%+Q9(U zg9S@o?7IQOSoqC=G4QkDhlQW~w1!v4zQ~3!@-76!@@jg$i;**ku ziC@(I#dvHL1Fu)yOa`*N=fBDy+@Zx21?u~k~_!~kADvfHv zV++ENnp(#ho>Nonj9iafy*8Vs#)a#n)?!K}xW2W?^{q|*=5HV2k1tar5YA`#68&l# z5Csc@V4fO)s#-(c)R;{oWB$v z7u%uMXrRPpRIN_nPf@ix3#&z((fSZgr&QDm4WmI-tx_ooW1v=Gcu6paXkJde09Bi@ zY3jV)+vQeok9Xfq2D@A@8TYL~vir+0pec;a7soOSEFhW;Ip{0CPlN$h&PjIM0P|Y5 z_Ri17?7q933`6M6G|6~XQX}vBPu>vgyzt$IN>E;(rCv_dNwM8<1qtbO2~Vw?#iI$= zVVEMpMs9#_hsBA)*9~?o$=TCme)IboFa2>Y8;-Z0oTPBv?rUP#39)lD_WF?OXiL_x=$Nev&Xq4+m$=tKhpfnqlF) zHgXny8Le5}Ou~Ttx(eM%3elB+-OlDdXMej|-Lc&v;TPH3XurJ=hFw6GmH;u4eNNTd zO8O0qMwW*usX3^4m3H8vRb!5ph*@M9VC4X4kDm=t${*bO!DnBda^$|`9G{sR@zz`0 z9NxKwsc*5Ux=h0%lp`odT({aZ%1ZjMomZR?|)3m6W8!%mfT18P~%IbpI^c4$2%6g5l zJ7LZof(eT-rd%qqP#Y{LgY6Yu=*abm77L;vV7^$Ol*&Zzl$v3ns47aSO3gH&R7O!% zqKTb$IaI7B3xAvt5{QQ#&Ip3!QLiX$85)+V^49eh-etgvJ0|j`RLu<(wZ?opPj-Fz zFto(YH?M(Oqpa4LO{Ods3pT!XgUV(C0#a(!nFV2zfyjpfi$(fa1OagnBz+&E8-E(k zSdHsL!8fk+-Usi{|LlzZXD?XPMfQK1 zJeR33b37J7z{GilA1c&p6>7B#4O3&oXi}z3Je^Ud49XnJ3ue=hrm9kH2;AWyO6F?3 zIO$lO&QZ4;1Wt)*P~zgnC5yPq^&N|AEtBirHnR#WVxPJS8|DUe6&ekNN@)Sp2uoGb z%40SfGbW<}EH3etF_#2X)Jwi~_XfXq@B#nu!w+ecRT^cLfBpY{&gWmg@GY!zDM*ne*w7W#nmnkc z)l91-kige*MVH)z&mYsiXyHvd+`il8dw+5d{^dvXFN$k?-Sp?M@6hg=X(cFY`RGR} zp(l`DSMj&KMx=tqVu4Dfv^rbqvpqORtG^aGer1rUw$!?sN^T@{IfPkJ$k!dWcuwJ_ z1(twmSwvw->=#W)!;nnTZ!+xFL=z!}3@f%;q%h>N_dsGc^wyB7NJ(3~^Y_Lce|426 zC&ST*;3DMl{bRoOC-?Z?pWNf4|NF<>|MbbqYvuKjXFu#a2?UwgP#VGrn0hmXQj_Jr zU`?qSk}6tLU%j6L(b%=qS;Z~1jAq)bWxHdDi$?zN;DEz}+Z-Mo0C~liy(KDpyIB}; zB`*LZhJdO~uh*m7>vHeA_ppzSc>K&}*o`!(5}yTv-TOsl*UOi+N_v zm&A8t^yMxm;EP;Lw=BW(u5DBc_yzKNzjH3P$c$O?^9m+Yq<%x^cZqw-(^YWMm8(@mb)QRh-UO<75@zPEWK*Q2tF((ikk!n=aoN+pP;TAmr+a6sYD$~$bn>lHEafyv8&(2R&*#V~{Y{e9X$Il;lhIrdQZH9}(I`8?Zo1@f`uMkywq zDmdyQ@*ahBDYIi#bKf77%UPXYZziF@SJeGwA41Kj5nk9F9Z3VEGjwOP#f|sG4fR@8 z2F{SZw{JlsN*jlyV#o91_m!7$O4d>RUFqx+1y~cnOB)l(3{ESYu{+I|8BC92B$*?g8`k+K4W)*X=wO% zoPOvcPxvqYt3T#X|HnT|-e2*#)$8))xDO>LFG#%@0LRI#&&81HjXnIAK8JUc^V%Jj z8P97eaf+%aU{x`7ab1)NkaZ8ni(Bc>{o?^B9=7MPD=;5hV!d=zmh7m|9T_+dKm5_h zJQ9sg5}r-M%!ikL>YZ9h55N{Xjy(jFRQYS0YStV6@Bv3av;HByUPf>IVDuzae%tk6 zeuATAtJk5}{{JB>z0*>p%b# zTDoHu!;NH~|1+{(Pd9X0qEX~TFcIm7T0n7;vl&-Pfka}d0`Z2`h`iWtjI}0KZ?)G? zk4llakL#o@7VF`!e1I&Eo}FY~k7;OFdNM)!*|Q`5@b~X==bbK}Jvm-sLIR~Q4Y^M8 zRSn5OAd!fJ{wX!1iPqi4@1OF~U;LPR|M)}x_@Dh@Y6PC-a$i#UW&T1Tn>=;g2$Tf% zI^~8a6&Z%hi`D8@zT$Co8mx3Fw%f&uNUzhTdt=r6{pcqjb9U;a$`2XOMAh0Nvd;_D za$)d`SPeUoZei&rVbRc+1eS?aQ_A2iae(Ed6bLqD?|ljotC8 zTfQW>-|g}Ev)3mm-&kacEN27)cZ_N@SHp|Ec;od(09KSAl8aw6nzR7V9itmfbTR(B zCqNrc2j?_pxl8t@f0b+hI)yK_3J9}bB~?Rbf2k8t0@${$H)*wX&PJ=(M8bq8Mqp0s zMt1L7*UZ!aNEw{2hCHUB!81X+`TNP=-o4f1vsvPv%XaVDl1G%EB}xA#nw!j$l6GTE z#A|AXj^96}YUpU)UHXqt_~T?Nx&$ISVCusboNs zyZGMQK_LlhkFuZJ?GAgJ8r>UhJ`!Xy_r86Y{=ffxz|nEyv)vYB=s!EoLfopg#XPr$ zBiEyXwQNaVtsJxzYfE6EA0N}}^*BC0PBWfa=5sX(qZy5r;hgM$%WSt^@o#T9lyc=U zUjbm6-mqnEwlxZtDE}DM&)Y9CLqLpZMnyC>`a+i1be8B0mY*wPP z+b-b4$XwFH&->hYC!6pqG9oMK^#E3507#YJS;Q+0MDkoC*GqrS8-d^Zt$Xm-9|4Ic z;JY#HuIPkxW9B3VV0kD<8i7VV@c^1uGsOzba~DcJfpyaDb*L^mU&n(X$Ack#ktpnU z5>A5t^P<;~c>%@NaJlZ}xmHX=ArJ`+r$0=uf7MV?Vz4jfSXQRgXBU7T5$QeyJ7PZ1 z{?|0N@P+#?cY&(jz;^;dHvl&%#OBE&mQ#nx`x;c3+YV!SC&d9v17lrNiC#?@jRKC; zfcN*+Lg}g+2ZY3g?qPuMZA-f%& z(P@oxIlwY>T9yIxg}5F*s;Pph(8xh_O>)gZmhl#TF2wh3A4;`?0#ylxs~&^3fkp_P z$ODx~_zLEkR=xjzva@vb=@I_j@ z5yu=k0h7^)U;Eu(1>ok3ukrD}|5^H8eN!QbCIs`4T|x0ak24~h;fnz4nwl0S%4IN{ zDrPeai^r7~{ziwR<39iV&;B>C?Zl`WP1G-s*x$(#n4_UjV9R@<&@$Q_y&MzQ8c=Hx zD>}@VhM^V?%G%QRE2@S{CEr(`T@*DNb9KSCi$(wh8RpXgO=D~MlYwj(u-C*qMK@No zSV}4^dv%93a|k%Zt0!~k-7a_D?eb8B0qw5E(I>0ZUYeodJ5k!zn93DpzM9csFFwVV z5Y=;!!qF{z3-%Cn4Q*4W{dUrMWslC&I&t9oc&84-`^9Xx+3 zbTh|Y(b}?@14qYwj&fl}mUr?P3E2D5%jEt{e2*=oP0MIAa)-pH3fvK@a&lOp^NE;c}Bz*EB`2uzX1k~6)ztRZgzi1saS7HcesqzCwWy}L8ii_f0 zj2N5_*x4;!_&e|RQUj2es|ZWpRP0g{YcWor9o8h8a(R@z@&l=Nl{c`yj1lkdvL(i; zt$(1W2B5c{cme$vPL^A7J&vB8rc-c7%RAj`M3bySmL^b`bN>^bmUP+MyGu<-WoYNI z8{%T0M*h>J2O!0}@UZR70PMf_0ed%OlG97LAjSVq#Q8*}8oRv?Cr^`Jfc>`; zLxc02!Iz4Eye5y5mwdZ=zU*Fst1-piRL1T(o-5-^LDeL7peso|mPBSNQ^_g6 zkDiYBo!>g(NO*{Qq6eg^Ck7zz39Q`HwfzT4vam{+P~#aYTGyguWij*F(*fpnjm~!F zf#iv0?tS-t?2~}g7Xwbt2AsYaFscSPUg8B$P-;)3$Xqt+?YjV!BOxdpCg?Ln+LkQ_5V}+7E{w!JAj9A@N69!pi(@GH` zNJ5n1=?+$;n3s3r>DGscb|7ulU3sY03ZNKL_t(L9X=Zjc(>ER4&i_QVN#yky|KsW z*@(cMpc;+!pc>27BXK>XXV^B?6rj_xnxHH-4QVl*90ADS%hN1_Z>W6po42@o*ya9% zK0m$R#~Bxop=#>1bInn~$j9+~_JxuC`}+@YL|mT`c@&e0@gieWZg<-Rm7*7IRaz;a z@5diK1f>`vOFRcZ9keSZ1r_7HU;jZonWp2fKw-W*b|(T}I%yhFN_7G0EatBlGosvk zU*#aIYS-%2YLaYnLNuMBG*pyEJ^g&6R86zA0ShR_Y5pLXMv!ZC(RB?)DK@U${rW!F zTSod|HskvB7UwS$Lu2hE7dq{nT5^Fa_2l?w(;2FvQdMh(96^AZD!*0snAMtC+ZNF@ z!WlYLsieveaQ4)r(X10(hFF~@aXclQM%2|BTiX^(i<&+$bpubFi;LrcHU&+&C@TAfcG9pg`<^igA}zd-<21VMn>XfR(S_flz8P#P7O zC|E`U6IKE)d>8+fpFX@u;NnfE_|qvhMa3IW(?=LgI2+_LBQa=dowlK)sdW~WqQUt5 z_yv?obl%#cvwxkvjzRl|&i?J|Xw5pBfP~kyI{W+A`Ga@wa=qE);b6e^W|JTW?{ds| z>@gmDT+^FC>1tRlwT@|2QB{RtI!~`hO~7btYK2ClPNPxBw#S^ETw=9yMx(Z%xv{}c zYm3XtE1o@liQ@)1Zot{_lEngk_DP?eZG(UKyWfPk#Oc|HAOGwzr!PinJg+tC3@<$D zlu_$7f@#FrONSuv34%q+;^Wxa=&@r*$=VR&yFTT`g85{YJ~T~7-KdKYqeKvhDxLrZ zot4VeR3&{Zs1dq8ikduU=M zO}Km|4nCmWwea1rkOXurv)}>bOGuWXq3K0eTsBSgIs)e{hk$eEpv#@FC85AWfvaan zVF5%F;ON+6zb9KSC#eSzxB>R^ylSovZqJu?6DTPE@OXqh$axckF>}RF2`&H{mRk6E z!8|E13IjE|D1N?#m}=i_Gi1n6jEc6eHyOTMjdNF;n}o<-eku^!f;>btdf7{LbKm#T zv>fVBuD7AKl(pCEiJ=-iPZY44E4#M!CP7@`bTDSO(@c|ol1j`Odib;C^M->l!{=Vg zk_?F_V2=Xc?R0qfe89u!18%q4>~#_^==AvsXP*pE4HZ+YQE3QNL&NO0uk78(azktC zh9t;6M~_djv_$!(7cekRQ}-I+{)2w{{7?S#@9^P!2YmS60ss7e`5{04*<*$m9_@TM z>-hzq47vMDDzE&=v1wKDoH$p(%0i^jo=_7#JI=Y6gcobXcSE#Xi86Hk9NnIV(!Bk_ z59Yb?(0Pz3sQg>GR4M>pI?sKA$y9g%CC;8a#d-dcNHAJ#-q|k3wqca+{+iJsn#Sb6 zuiQ{m@QHv0s;baXB`%%DoCP2L+97+pZT5ECT-Pn`-af!+HZht_9zOY!^O4K>$mM#= z;NgSEoWC4#=iLLYUvE)SYwY*-uuPr(-X8m11k#?LRAg?_=xPbL{)gztt#k(Yj7G>wR&1-?I}-PX~xI*Z8!KL-FZ50-i- zOEH?Jp(qN9qA*{~h=b%2*#U7WJHB8VGMR~nqbMSZA{139sO#ZRr+6<*>F%#L>(nbn z1sYBL)LR&OV>X+KfqwY-9B(}3?Jv}E5OK8nH?M{>7U|dMi`2Lj6w#CcK zORhJYoR2U0;^6}*mH6=6AEGyPrb9Gc3OP+t=sJF zY%y>z@x^*?Z|YR$MV^6RGR3qM)P@Xgv)w-k=DZTX9rBnLlaN@T3aPtI8s&{N$r!wv zLRs7o@tw0$LZKH;=S0&vu`piqX+-4uln(yu|8|j2c*Sm*CalbE8{hTP@k;y>Mzbn|D zT`z%g?{y3R*G$oE_w1C=P536ISVkM4;tnl4?W&tP(S^sIcedHxO|IqX=@}=}%wtmx zl|TIL4*>Y+M-S*94{=`F+&ajTz~`f)N0g_POM5BE2yj$GqrI8M{Ly7`XH|8PgE>nQ zZ?a74+aCUf2WEr22VK5((BSN}rXfYC! zEcss9xhFSZ?Q0g%MV7}=XJ+ciPcI^}#F}nEunV!q5HVFN<%g2-j~I;9L)A ziXH8vA0@4lx86J8u-E2+m>_&N7mwaO>|&o4`wku+d+aPhQ3EtZ`?LS_h}*p$x5e+$ z8NB!1dmJAh^TFL#%8>_fbhN&6aM@Ll!kB7peF@eP%{^8wfirSFMtTzs((zM!$=y{2 zSfsNEvF8z#!7RnF5cex*2(m80)p_;T$CF9w(ba-Vng5@&hS5vE;qf&GnOmM?ZN4K*y zjqN6iT4n481k;et_H`^nXU3G8K*gx370POz#WbR<)~IY-^tL;6wyzURrv%d}3y}nL zolDVtk-1%+UWZDff~r*r{Dhg<5J@Cg+~DWa%td{0Jme=Iea6o|>8Ht?Qdy;CX6IZr zbuM!kvo1V4|-i%ra|2_FvO!oiM?%Ew;l+_oC%IMW-vUbR4QRRiOU&G zi>`VUCA^09dIG*FMV`N^HVCExnz_M5h76?=i)n~jPq^|LYMog5G<1W(kV9x zh*9fCqWGl{7R{7nPl`mV$reB)_zjEeBKMBsZ{8(PVm1w#O+)Hp&4z1|u))-4G`t|1 zM%2_A(KMpExygJoMQPND>@gd9jXQVVVQ;s?>FF7de|jQfev`}LC2FHW|9FULYTSKq zpUqZ-FN6WuY&EIZNHHZFY6;u*Q|>%~YgWi_5h^RMe)EhO%PH`icO(UY* zJz0o*ErCbp!Hm$J5C=1qMiqrB6}8T6Iu!$JMGtL04N-(nD{7s2qgvn~tUz%_-sulT z@+6yYt{IKg`c!8z*7YW4TgPnce7qC_$j=Gg30l{}q>ST^(}5w$D6~!#Lrd@j1)J;Z z$SFE9BoOYu*;cWxXBM(6>co%TkO#*@4wtY4WC-y1Q~^#o=;_?2s^KicECi6U zXcj$yuac7J^T*5*wj~8;A&*glU=)upe9b+SB60MG{UPRNvhxGftR^sLZ@0tA>41~d zL4lKC=bJmUg%5r#F(M8+-1=vKz+e7F!o-{>lC|A=q2p1Onyp-!P9h!+?Q`0>1~m!f z62HpU!*DR9vj&qQFCYEn<8;vH9`MB%e_8myX>1id04Z`v?y7E-n$@7XsS=zF6XpUN`Vd>W(ED`z>!b!x&hDEtlqk0*zus!Cl zW;Evj7X$%FF2dQ_Q)bS97f+w^`O`iZuS(dL0neUaFjGp{&MRy$do=V00i=(?keCQslLIYu>q<|!!T&fBWfEp7NrvPMun2KfjU>{?5MD)qSkA) z%sSI)iMm>2GA&^mCQ!yQDnP`mg@PIcs0&!s8$?7*nKNb1*mhCXGPCJ~GBcuSK(LT# zdF3R@+6Xh{Uo@HIcAJJ#O;MRmqlv243%eyiu<+Bf515c7t;z{6{WJpNZ15lM|D68F zVO!sz#)9%RqP?LqzL*hCVqTot-2d5^>|EbuyKPY}Rk#>UIsN=oI^7#I)C$4)g1D+M zdO1Sh)Umd=xZd4GsW;Nci@{6W7Z+SQmt58$4rchTLLA$tv1w3WRN1K434@SLqe(ms zsnr!Wj5@Wtg69Ohnnda2?;Z}BL<`<~cMrp8a(wmz&vS{UQ#w1>Ik$&ILm#FQRkcJs zFR^evsw}9oNIhyrEu*Lqx*;`Fgza-Emm*X(U^<;JEtL|SNVN_VabFh;=0U)`QDx2| z8MaygVZwY}Z2MU5n6k(CZc+wZM%5jOL<>LE~PPuz~mwVs+kU#xj*YjFNZU~F@-f>N=g}k>OxdBQE#%_f771)^t z$$1FE9Ixwd&tByB?bHZtSq;wZ3Fo4g95_jWN*aOrMsk6q5y(sHMjJgxL<$*_P(Fbr zU7D4|hQNIDTn{~W1DuhE)oQMA{5hi`P(>G?chWzlVMwLT7Pd@;Ms7fq`-s7_5ff1- z-rLnVJsDH2DTRLavFjDi=lH@QjFOw-`#udhykuQHFCmAmY(#p6c<9zWXQ_SbiaW{229>G!{JNLa}}8{p)4!0FS>0EAbKSLY-R zcib_inHY#kF6^4v9t}%pVtatX{YP1%wAs>9TD!c+{)uQ_EhuR9igET|T9(3t%2z4B z6lny`OY0Yyyom1|bZGA-iLl|xh%k(4_h*A1t<9*D!X z69xY&6y#+IuNZn+&a>x2^q-D6FWYI-Ml&?@>XN&YJFfOtYXx@Hye7&Vr7D)$LU)~Pcq9g8 zv%<*rXbD9N&1zwo$JFJxngLDJvn)X+qWzDI=K1*w?mZ1^Oiwm@-*eT{4!0y|->Fl_7;W1j1HO>!1kW((z(C~$2lY>mDEAIwL z3$tkDl_5jTPzrqwk|)3tonw*fq~nsZ1zPT=?OfZj?IydD1iolmO}q;aC6{T}4K+2O zX3o=U8Cq%t65jbGuOn6dCRpMQ+F{WU0BTJkh>MI$&gC0SG0M31E1IhU$YtyIdPz75g1I_}gYUfXuwUdS{Z@N6r2O=!eeV5MG7Q#T zYEW7hiJ2F;0ji;<&oUeDFJnRqS*DVYsim)%uy!rkGjKNKufzU#Afz4-t92&(P+ zXrdKV<_-WXUBmX0*UbeFn}kiOUSyv?^2TX-=&HH?JV<8MYNq8%Irp3y0ExakD!5Mp zr1Sc!p{3)J>D=^^1vQNhj$2Gf?oFKQOt;%v}AeDc`2;?M>IJDp0Es$6M zg~S>_;LZuwVC|h853sLriJcc7=C+P=E<85-^h#qOF)X{iF8$-H>C>v#7G=UB^%j}( zYle=VyH<{?pc$kahIG&Kuy#`Umu2rpo6|v?(U(Ul99hk1rqj2D;}Q*G)tZ87sD)By zc(D`;$0l{`R`Nc&>-%j;R@q z0yx5?Z)damuFo~2wgeE7e70sJp@4M#rR0@g-_);~ElJMz!lP{{9NcWfO?dol$T4=# z!>|b4a?t}IT2cOiJ;Bmy$+MxoaS|_ySyyAntCWA8o&{l9C zmvJsfXG2;$ZO#WnV88IcXvyPMH+AeU-t2tdSYFc`aJ2zQp{0v|Ru^#rh*B!g-~8JV z)$T5LdKPyMZ!zd6yQDMru`j%|*}@Uc=eC>7MNi@^M0jyAA}D2j-avv0my_Un1hW7~ zxKGO5XRDWZ8NNFv^fa~%6-17LVJ#a zNxrhME|FhI5>_Sz3!nn86cw+W8J}H4rxt7In|f*(Pmf2Sz+4I0eXGUkv!r1vT!5l$ z)C3+v%>d60*?Vh`_RTiYJi$T07FD6S$h@CBpyWLWn+%u8N0NCa5`(y7nTWEZGbWx_ zSL}6ZQ6q3-RB_$LZd5=Twq1pW5i@a>#Ct>~B>8!Pf@P2cQXNACYDGocNREF#%hpck zdKXAA1`}*Th}<%z6qiy@Ci%oB>e!JOXU@)dF3wI_myGpM1 z&Pw}h|8L7gpX;ZXX}0U*UHECNOor7fI)vir<*Kn)zYyx zbxfcZM-ztqQ`+5KG^5F-yJ`vL z4sN?%+JGdD!u~sg+U$B0^Lmq`r=wK)O+!svm{YztvBTyKfIkY*T15r(Lmxl##rLjW zuY9f~Zv^iD^hjKngx)%ojWSF0)K`k3XHeryN~swQCia|~RVEZ=$Ccc`8j&7A!>Fb? z4arlGSNuJ_Np&;vu+}NR=lUzkpBLSzp^435_$(LZ30U@|FGNe}xPV`XuW_A7WtK)j zEfw$M>#k4o-t0c+hv?S9slQHS4k29geUANRA>J<|Z=M7A3W;4$@XHH;2oVC9izN9a zLjX%OTOU1J-MM|&qamiTq#+o+D89}f46z48tWG;M1o@b2OYC&#Zos)4P&+wG6*vLc z%zc*Y!KV)P(8Fr$nAe-^cTJAOq?u)?P_Cu#1#m>;lk@`oQRbd&EzwgD`Dj%MYLXcM zOC%(Z`ojb-&d|~^XDRo8dL(uXFZGOouT=hm7qFuEYMNw}$}gYS?QnT%hz_*IQdi<` zw-}mzD4L;$WceI@Q-=snpJkYTdPPZ}Kf4Ur&V5Exd`EugPbS^|N+!4p`=A5jxI8`G*5jKJ%XwkzI%+z)@* z9KdqjuvFSw@(QvYH+CG#rde?5k3Kmfy3t`@-^D^lj_2=}%9!35Vh6A|dKPnZGJK_RS`K~HX8SgA6fE1cUA!_k=AZ_nBDlje|a%S72y`HgR%@bJ-N%xe%{d;!&> zftb&IbhDh%h2^H}67NJQFx8j>H6|R3*Uv#IC2h!vu0zw5RB3*i@{u7!wOsV5v}Oj8 zv}H9ZZzc=hW1+UZdu!Y|X1|-Q0yasA8DZtd$v$hLmziNVRIES>He^q@5dX zvYG{6fxuNL3HInfT)VQ+xRy}@=FEYyn;QwgX45v_V&61*cs$_XtrmaPAAlRMZ`SaM zu}MnkZKDRIIkr0i(QnbzTf{(FzZ}5H z^8tG~N~n|-c2=}xo~@v@vYk)qYHPcmA~#5|&b=_LN@#|Leg9~M0XQ$A>r>0QYWp`W zxS9PwR#bL7JL$3h`=<%$spAfD+#&YM;^&=-F14NB4uj(X!4hU+T)H~;e)NTpJv*Y? zN)~aqWzzlr_jvf|F@N^wKfLmNP~;3W4TYnAh-DO&|8-)!nMWeUG&G{ulrbzrPpdjx zSH#>Oemj9jJ9$3h$3J<55`T54oVkQ-oZk(X>)lt1$bIY!&*W%LT&-_*R{oYA!?upK z-DENj3EUtHMSlT)fusbdQQK4rF9ITak|gJruGMF2Ivdx1oah(-wGMJ zla)k~*0q)lMzWxF5rPCo6eo_J*@+P#N&Gc8)%2K8o)56}HnulR7iqO3G3*3C!V-tQ zrfF#_u%>CK)iQqcdWGzdvTM-2W^(8D0e|@Y@A28sAMxY&}a`+(7xO4k}JGYa1b!Wq8n7dy7T$ZOM=!QypF~<=MzDd$4 z54lytv|1^#h~tgdRwS%^a~DNpl`8(4EdS`jqr92?eD52#>9$)Ood>D%@7-vj*Rmdl zb9+q0qCnO5y4^ooLngaca>tfee);q6+wZ^j?Q-`ww{<4tWPQtSz=8+`&JP0t1tXCv zJqgk7obr_(#KSccj?1!a&}4_<+Gkhq|Q` zoO@V%D*CpHwv#B>>9eH+uc-KA1vO9^Jn?C4#RZq%(zj`fML71x^q;&)-ghB7=r%LF z!qD+i7b?1;;|Znl0L?%$zl6%kj=OoOLARH2u*dP3(Zwsmg@V4RGjSb;IS%>xyjY?# zt5!-(qMIFp%i>Lp7v)0CtF@9e*I4wLH?6Gh?p=6jS{X$LEhzKg(^HgJ7BNXB;P7^r zz7Y^V8_+*VzNc-raJ(_)vRD)`X|~z2novq^sZ3m&BI#n9CPUX}WDij*0+2F_h|5Kn zzdElK+YDx}VKhxgx0fEx!dzS`pDW!N94Nh~S* z2W{*e_dv9mXJ|!+icU5y=(*m?cx<=VqWe~If%;F2;f;6U5uh*>P|a>r*yf=L;FJD1?Fkg za{tehh73I)1+X*?+xJnc)dKOSJodauNNbtgjJ#rqckkOD(7n;&qo4de{pSM|rN)Q9 zmh|7Bo(_q_xKMU&h+g%H?I%Br9P?N8Kdg|}$m0+p&Q6EHivedTNn~bI58rNcB(n8;cat)bo{KSk z*C!myDZX4~pd}iKgdxaZ;1f5wcyjnhf@35@H;KERSA1Enk|Dy-j#I^#A~XG-8!$Lc z&O<(Hn;N?UE^6;aD~)xRrI}jgED53Kps1EH{vSLV@Ziya-}3@`H#>CqI;jyTxZ{f% zrtGr3@UngdZ=5JzX`^@`GY7R=B@lUupkksguAYG(UU6>^dL3@}c2=mVrSkvyZys=R zI;2srt$>w6W(Z)7^53A559S4 z>2o!T?AH1J=}gv;>HZ?3nBV&KALRc~boqtV)02;8YNihr80H3PE&7Euh-F<71WIy!6YJ> zM9f(*pUw!~3Bm9Ze>!D4o#MG;phQKj5lv^*)C!u>pwTEQL|uicU@02gV*=`EjWR zVtF9wvcBF+5(w?BCd1JswQ6QCT<;PVWg2QdeV{CGE9*Q7}$)H7; z`W!V%DB^HU;d&3A+6;5h)}7vh`CMbm0KY%PU(5*{`Th)Q&Ae{m*f!QRF#+cJoR(s$ zOT#U?$QoBgl5sw^@w{X>X8-0cjtzs8A%o8cboSbehBmuzC*IKQx7$2C_fUg zi0DwDx|YrC?}6(Rc*%{F3T`Xo^!=tcW-<#>q+#EkP@{;7D{JL@xz07Sy_(U^k8nz< z8XDC_l=f;%OYYcN=_~IpYDR-_DZ?&Xd8ye&8s z9vz?Z`1nL9KQs(E7r(meNMh1d^4DEvvMm|HEzy}T{T>K~id==rRX|iF9z0HP_d0hv z9KN&1;jLXBJUQj);|G|_Bf9zPTP_!>NRgn@su~(KLnF%ZRIDoj=EJl7o4ds2>^ytD zUCJ})2{^KDPDk$E?sE5b7lH;4Zaqx@{^-dkE8mfqyz&?SE;jY6`=kJ<1Z!f8yzGdB zmCwrvWi@pvi@c`UV#{jK5)41Y<%06Jd+k*Dmu2GmluEUNk~yx2r8hZxc8cSA1?Vd| zf|_ytO_;brp<53UPP(H{Pchp%@oXmEtEU-O2?|*=G(UUXY`_;GEy`i&vL+7;9#2acU zOt|fO>CT@@PWiDT&{xZBO>2v>eU84_B3~rfe`}wE?&^yA;g5dIBLQ(`iT~UE_3Ns- zdByd}4>9G+S$=r~%}`bpf9+=r*fXj8hX;H7gWvy^RQVr#o`G@NgW<||C7+QTpEUMi zPCvre=t^TLJ1?s+nqV1{IDmPks{ZpK&Lml@cW-yO|ENznoOAe2mIqJ>dH2@s^!J~A ze#DdG^`N55(&=f?XfQatde|rpZq05R)zGla;tiU-is;<9x`(=PEjfXq*d?9k!@};r zwY&hKfbasZHv)O_-GJTwc7_g<%koPF%+p4xhRU)zN*Rqv3_){qBR7yVM)5+lZt;92 ze>_07RwD{Ss-nKVg61Qnk!M?4+Wqs#KNtseZmwS6v~OWkr?aVZW{=qsJI?;C9gdzP zJJFASa?0*t%wA`;UZ0FSFPZGT@&mp<#0L=uz}Q~(9NO2fzW+b`(T@wi2X2&l0qZ2X z2vQ?Zvl>LsYXB7BO68aIX$>>Gwvnsw;QWNcgFOxpb~!xQV?@lq`Zqu3!IM)CZ|$=G z{vk&nKfpN~W}ZNv5cditPye1b81|T;Tt+o?CNhz^X%(t4Kmi2wqn|vWpF;xr`13RF z-tO|#pC^gR+C01dhwt3s&N~@?0VEso@9`l3PmWI*eShgg&T>Y3 z7BD#WI6F>`xAdR>&;Qc`pexE+ik8J}XYQPdU9=v~QrT#dOG0|CM@^Ztf3ux7mkck9 z7c`!W29QlXNnR<*#rpVfK2Bb+1TbM5RsrE;|LqO-g@PXShxiw-80OJ{mE!cbz68;1 zUxbCMbH1+n|B?2tJ#J)ap5KpTk`+{laY<%TNijuMmei@1TC1+Ao|(e*cJ%-a*n_pF zG4Srfem8(W!+wGN5PoYIZoqzF?7%h#i@|u|_D)xO$344M>e8sBmP%Dwq>^GPl+3tL zA%@5#=@;jmh!a6(s(SVT5_uzoaXHU(p6mY~hZEv)gt=p3zT3duu~^1)yuoIRE4tGv zxLPwy_nKI&>LIHEmtL<&zu#wq%dT7?$I%29Gt6p@SRx7U@3=X{+X{Kmk-0?tQP;dk zX2dTin38d~eyQLxmrdEd# zUmnue`T_=r1qCYN6IAjs=*WkrU1c23Y1&m@7uuLpFjI}oAe+sP*x$W`}2Y;`+rdXjgoKm2Yz$^g-ei0H*+tx>VL zoyC)W7!ylsDs!tw%P<+emV9WZ5xyVM-f7V8v^nk^r=>L|j$OTQ&fB$3%uC9^Qel~dv{N@IW1ZU|3cyuJgf>Abgz=xP&+^^+gn;n$x%V9+1%$xlDY zy{>Ir+Se6gf_oY?E+-c<$A9-w+!wNGQExEhw-0K3a7UKiz8s`wI%Nb}-3{K( zhBc^~ZD#U0m{%-XcROhkF_Nf7i!I3lCHqK};wubFx0ES7fFr#{KTJ4$=Hp0vd3-eD z-fupKvwCU(KDgbbAtCB-$-f;9`t+LO-1=kLeAeqR>G#vm=(TtGX ze}Kg*U?l6AyLOpC%Ni?C4|Q=x>2y1^-CC}-)ZD5OpNWU;C2~Gqx|yzPrCoJz6>X8K z-f+h@p<2TmN3@z2-Z-Len;7y71iiTum5|Gn-b#N5;SHEO@^3FE#4ji8bX&RCn#^*& z^8kLBxpcv+G2N>zx>sBLggbokv`Syz{9dm|b=k;gW;OBRn9AaOmpD3&sFcea{};=~ zD}^{yGy*C7T5Zhn_#`y~bANKK5!h|+q;-C!{DbEsKK<--x>wuu+HJbMcFqF;QeqPj zim#;Z?yxH7jl+&i7M{r0J9b8$=7+O9Jer1==-490-r03&UJ+~P@fV*n`0Oxud_A$N zp7Y%wWCj4p5!#B^U7Q!o9si1jc_nWk)aDqyh^X(ZlZ`6nPr?|#R1st6NOBaXj$?_h zAewS~G-7wJFfuN8`}+?E>|Skf`?kP5ymh-vuen3F*<>&t(`)Xqx-70=k=t7FtJH{h zueP%sz@V`E9!p>iqm(KB;nzdr$s#oXx~r33oSmWl0IBk;??#!F96~j)=sW*g*>L2s zRT$vE7M8WuY~YO}rV?wet{DL(HQT^kl-JrR&+DyLZgo-iyR8(MQMcZH=;3Z>Awc(P z3+rktr^3ZlO04eA)IZ4? z0}x&S3-Nko2r68_jj(Fq$j(bW*YWs^&l#W7WvY8_t*)q8-2ZaG-5+%Mi~sH?{N-Q$ zdacTn;{nhCHi5a;YFpcGQmxN0r5?mDBE}N2IC>FbU9s3eqy&&+A{tI!hxFkzCC5kS zchEk1I7r?9TerKkuZuO`ZHnZhI~Lcb3_$m4-k6mB)i3|=6~Mh`Lq=i5Y^-6(g%hf> z3tX?WS8XG`S#fBBFWn2b$L?L(i8cr@Yy-0Zk>u`(-<7-Tufzg4{I#I-MmGR<3&Jt3 z?cl4K>PyhwYw^h+i+b;%KS+1jL4UyEp#bu5Qj}eZM~_Ipd7;?(CK7+Xtj)UzJ}KIa zGB828l7nbYf*J1hEU7XpRAHJD8L0Jcoz{-QM>lRUc<#}cS?Bv-9^z|_I2Be@l_xS7 zQqw>tYjz!@Y^EqV0A`^VJ_eeaAr2B5+C&0n$yl0cNL05?$}HHC-a&KQ!A;y|6JJh6s;Z27_go_WEDABdyVT*t8=!Tv`QZ%H zt}v0;->~a~>rRtefw<-2w^@w8-)plB0z7>~>10U3u5GfAIh5{u!h8AX#y*c8Kjq=? zp7QAN(=-|S;L8o&g9ly4a=mus`miK*To6WrDm}>O%oa=0rkuidZyPyYjp>Mw%$pTHyw;&D*WA`9jB-2D zT-ym}VU!JlNK~0hVH`Kdc;oj;!fepyBJtvwcs{>));kmp7=F0k?l?H|=-&(6qH_Zn1xdoWpV~Uhc^qN2~hz^rm`WlQTY(VHnC(~@5GNe@ncLkOQLzFV`|G) z8ZNk&Nuyb4bdfs?z~Pg}I1Ou!#d)s5eYwjrzBID#Qd4Fvo38KpL%ADU8`^P|6hkl& z%CDrQg_Sc%(U$r;P&^$x*Z%vYs!D;AX{UdLXO~YFw9;5 zcVX^P${&X@Wz)piu`qTf>HLhEYy}Zwc=v~Cl5zc5n<^yQ^k|sJ%K({tM&zQl^j`q_ z_Dt;jc7v+jz~ptV4nH3F7has&`PJ8s%tD_O+|qbH$E(AqKZUQY2|PT9HSNpNNrPKbYWJjkK|88b-v) z9Mcfb_FUyx-wVC$y5dj5X%2C!;WpNkf4=~!*6QRN*y2|)ei*0k3&J>sVH@;EL}PgL z_$jw;3M262e_Gg(3VYB0@rCeIhZ34zm7P^-HxAaHncDruc$I%QQ+ zqACN$h1W7oxaMZ={{`aMdByL`?z!$>rd+*VkIDGyngPh~P(D#Omdc;^*ou^Xx_6?C zfUzxheh>x#;PKafj`#$?I}!g=u7CGxi|(jJ;OCQ6vv86s|2&MS+D@wcHM`DYBHE12 zf-(l$GaE}car`2}khjUS8kn|4rJHvN&r|-l$>4az;CKYD-u_zWa`x=4g)jnsd6_o? zTDPB`G;FM~B$abRQ#ZSAj(KWY$cff#dC$R@Luk6;7;Koszy19i+`4&#(aFUVt9!C% zWA9pvqh}t=My7ziOJ6^gKGP7ACL3t^gt#l}OeTB`W?`Y$$~J8u=7C4Ye{z z0w}vN{y3%#-0WG*WK1`U5@w|tRx+jY{<~PNL3A3ika+Bhs;NQ+KV0J4Wx8U*LxU`w{?hEc68J6rui%!RQ z+I*eqHdqt^H=JxiR~pc3Ixw5^>1U!(BM4k9t($MwfRWs#kyc$vZb}>GZ(d`*FG`V7kT4Ar%H1X9)ttKHu+wu`mS!xO z5gclpSuLhwG;#^iI2OhNfGx1h3YHC)EvGGBg8x$dzkCI@hF+Ex9(o>)vP$lW0UKqH z)kvdGfdu(jr`2_t*_jLG4~Fncf>qcSu5G2zR!UUIn`(CP{gX7yT6K3=`scSXP9mSb z{I`ev3qI!7L5D|LF95)Puf>yZ$7x(W4-*#4C^aqs75Q1JzFcO(+@GZ7v1C}88_6z2#+0ZcgnWXdF05vpsGI9g1o z5}j5dCREDbaCd09JH&@ibN`=fIUF5G;>Oh$*W03;sgz$FFMIY4*I>NJN~@5SXjPR& zzk;{Uw)-(bd4Xw~X_8AnmiS%_0rL=O^(gQ0>^Y*))$KLu_L?kSm(%N0@~A)JVgFbl z^k{VNK{Ua<)WobwotTMD}Rn;BB^3m2}7g1GXc3H3tGtXe1)De*Vp{YNr6`0%SEZXI;Eb<7$h+ z^U+$<4+!O-_zAIwB@jmw7FxV5?uW!jOc8C&T#_p%t_fkZmWR?eU4L>W7Tnh(HLg78wj6xB)oXLSTIRy5cs3<|ld!+j#(c-XZIttvmI%dHVeXwD zNO*Lq{HA0vQpcQW9#hIfKvH!!bTX=frpqBXV! z;S{IgpRzei%cg za6Ik*!HWpzodO4dD~>mj3$DBn?@z;k@{;&8N?DaFmGnEOKseGlM%?pM1bRF&#T*OGAZTX zpReiXlr&o z9e0i;ELq)Yqz2QpO&krvC-5Q;k0v>8hw@h53#hz$jcXhDVS;NLw7S=G24HKhjVrcx zEsT1N$6AskITT5&sw$`Lw{x9-zCTZgd#cVA_I9eYH1{fbC40|b!T7}pZi4C zL*hv$001BWNkl`Eb_49+AvEHCTopM zu3V6DFlAKniRLE0_8zmL#nWT(S6Wu9+#pzrYoAJ2-Cb7Lb$2-SPcZ7FeDO-We!gD3 zE89Yk{TaJ%4R=w7QiXXsGm#nWgI=5e;zxJV-=7XWYFkxqbh|u|S?|i3cz!Ql%f#bJ ze#gy2xGm8U8me5zvKQ%yb{xhyt4c;!4k!cbxOL_;Sg8Dh+<>goGUGvHoy)LP(%Ncm z3nV)=VZh*U%tFpWm@<@cW#{CBF9+PY+oF4|1r>4p?$s8P*9qQfj3<+)GnwUA8``+w z&D5hA!zf$hhUx^AhU7-qg6=-v@d)q5i0UhY&U;vnh; z&~7(q?^?Kyi*pT*pN|M$i*>L(OPFY5;U9h|<{}*FSl_wPN@2-w_jcLp7P#pSq&uXH zz>R}0PoJzeMJRFYM#}vzK1qD@WIUyj`$g{|Ghnow#m_x5l2t-T`bFt>c%Vx7m|EYUW(RK;pi^2=DO+P z*ajEU18)%3N1!SQ!E%CAZlpg`%9LNpvHX2>ItCnC+w1Ohr10RERd(GPyBfo=jzyqc zW(D!3!HU%OS-nQ{yp2uUtfFMA5yhI_sqlH_L+W>j-t zjoWQyfVANR@7_I{S6b{`X{Gdbtt)L>SK2)I;&UQZ9gv=?Ypt*AmKv;+pEDXw7>y=; z^kIj#?CjgU+UEHAh|%D1?fsC)9?(OctwDd3)Bcicj!g%^* zz>R}0)|I_{j3;-qAdK+C3GvtYq@C+@Qsr-6X|pWg@+(nBpn1ilbQeBinX^afJ6gqIK1xCC7JbizSc0E>2wL!yq+CQ*$48fa;6OaP%UgBi+pTY)M-JtbF)`4oQ%? zyFoPBh=Z<_KME5Z);%sIj+XD!HwB#r%_yu3CH@6@o!C!u4NyhtwcGSZBKI+077;Mc zmpF_$8Vs@Q22nUk4S`N?cNiUU=Z7B&Be2^hI?WVT7=Z;|-%H2ir~7U0XyfATc0(9| zhi(&dpOPAoboGq)F1JvKVs|CU)=-l(e1VA z^>%X^%A375ezVSvgD%&l7w}RlN)U-;k8;74_>$VGb+tvS-D31@ru@1%rMd6`iZ?Nu zVixA-Yg%K2K`r$HigN#c-c$MIU;l#BwKJYsx$ep1mXHwaeZedig0 zmQ-}FP~TTZV01dCy9&}~3x+Q;C$T zh)*vz0NtzYwCSmMUsb%VNMp0}%w882jE~y+l|1<3^V9&eH06$?n3`p*VPkk&(zqha z*KLU<8N8Zu{Ct#p0)O$}h{@Btx1^`NC0ugVj_ygQG{s4<6`aaRq*I^!_k7BztgUDy z`KKSf&xb#}#aTt{xc=mbU=j&@K*u2QW37n++$}@!nm0SdGpL;{*nj^zAn2VFU&KcN zh(-`Ch+9k{PUh5>3u-{Ttgz4wl53mHsL-%YCh|c$az6XjYhjrD#SEh<27!WsCrCd2 zZH15jNF)hwzt^VinkiIuaHwUt-)RxZCW|>$VlrkHN4fTzo9a?!Y8Tr+DGNmSn6d-)O`W!u8~uKU^Pj1$iSac$0CUqX=vhF*)WZ{N(iC}@oGg` z_5#GKDMT|y^6|J`mqg|a0)NcUzt^TKOIZI&BIlmGnLxROuabGHPH*op8VBs#CXn^N zk7FPI>&zQFbPCO0jZ8_JEt7pqtOc(+p}dH2x0k|W*p}CQI>RZ|2v#%jYgpTowJGg= z&yF3f7St+BW~WisQE6o~-+tiZ7Q&;Y?^EA`N>CP!L|RguZ`B7T`A5BI?vxK%BW%iE8c%u`>^UNbM zYzuRdP+4TH_r2@y1Mu|8h#LoOe)h?a#f#_Cbt+%t?hi#<^ZhTL^6A6R@y8LtSQ#a` zj%6cO9u_JyMbX_1cPB3 zuU#Z*!%zD2=GPUW!XYcYINeqb==5lK1b;7n{^9Lz+LPc|4T4Ke{FfeqC%(`io8pw9 z0a2m?^cdd{V=B~gUQ>m7jyl}fRDacWebpc`^*4J0?zVw{n&6)%xZ6fv`P1`=IF$GT z$+DcAf~?#1l;y@B__$s1c|ZT7I~;Ucxthj<-wkpQVZmvFQz?U;m1flrtM9IL*GSJl zD1>H#_6g0GCbzz~OY4e7>xzZDmT~W`hke$TiY()sZVR$Z~c>h&6`*7<`vj&F53oB!o9Ksl$gGWIeu}%3b?xaE{@e8 zc{3vjBa%07M43%OVqfmjV+_bt8vI)Crj+SQ!AyA*>&R` zsG7K~7Ijm6uesSwc+IG3+%$+rmDw8%BU?yQFJk%TjEaN~SFA8+Z_a33xeQd9FPE6r zDlOAw9K6c43|B6}R(*>S(ikYVU;hN^mteXSyMKL){p&9E8r0vZ;Mg@rFB6WvmyEnD zG&oxcD0C%NYHS%>Y#Cc5VM5*B;-J&w$G61Je^!PEj|VAC_PZSyqrN5J;Y*PA76XJQ zByY}0-ptY&t0a+q?)5Fsk~5H>b@oQByljt|#&aTnO8jb;4r%Q;8IJ*gkyJ=nFj^+G z%XJd1Ole-e%vMc3p9%9fGve6XDM3aQ@G$52NcNoP4LB99-Y$c9;_NFWhOz9me^>fzxkX0$o(%w!&lw4XkXi*L{J9gKG^&R)MEettsY zolvzcj4SVQ7Dl|%7HDb!B&ND9hH;7HO)+b9M)GD(@hMAkYtizm}dGlnMTw!1slpEF-9I4!WXRwUrfR$U%rRmmnS>z6PnW7Mm(J94Dis$dwxNPPP&#t-v- z60d^SsrPgCW)5#EBw<2{5>=zhmH|!6q=fi>R)$~xzyFYuXS_-Te!a78P@+Wg!iP7n z#TO8SuP}^vIQC9}5@*vlROPu>>s#=SQ2tE@0Klki5yx-vUI^X>t5G9)^9I+pD3wYi z!5fTHg;EKOQU$|pVAShWDitb~3aeEqM~+#M>#6ECByT{Ovp3=kprlZPHG)?C`Mr02 zkB@(}kJYI2=*clwUEJHAjLnAh2c^j$Z~bYQr?@IU1#f^ckqtIGgJm(%XmlJ+c$4=jY5g{iW_&{=THAE<2rmL z4U}20)7p0V{NC^Qzy9VwYB&5HlIaY890{XdzBWO+5JkEV@7@1baOgk)X(6|O}jx=q7*Oun3`LMZE@{W6mNjT z66i|z|L|$R({BdI;XO<$%={UiKf|(Hz>ECl*$cpzz#Kl5EDWtKKMfNMz>qZMW`SAC zkHiPnU6a8{jOW+cm)&!YZNYMdAI3PS-g$A}vsY7Q#JSl8(>B+rLzi-6P>I?oNYYXE zIGkcRE{FXQj_tDF+ojtUA>X~v4jDi9nI$!P?>0F6W<0^khW4)#BlkV`?Tm zc|9U|Ek606W%4BSsg#8!cbYQ6s+te|Da5hZ7UDS98hzt?d&Ex?+T{qmi1FOT>CADj zivhdC`ww6-OPihdf4HA2e>yhZFu5xs&i?+vV}9{J|Bd+ku1ONk+!2= zTU4dugD?W#WIeWTZfJwO{a%ZQ-@Y9gS;=uful!1C1$+V>g?E0Mz-74J5k~##&|~;K zub`7qftj|5LN5njH4S4>@%LNWr$O}Q&o5xEn))%xQZ$1pBcMce5LQ=WP1Wo=@xW&x z@f38LFdfOa2AbMB)oOd z=GH+Q40v)w8#$gXxOrQg`@!u!60&K-e*c((|INAA|MaUPU?-cX_4@={8a~C97Qypu z*zRY4@=>bzMY;cAz%TySUjtwit5Dh+$=x@9lK0Pqq9@EwqudKs>ulBCVHtWHe)a%Y zwu`F8HS?#`T+!MXYnTCL+f4i2K`RTNLt`ArF;1;UwN{gvU#*kuJCtOrwwaPSC{al; zVF=0un%i6r$jv%z8h3SUBQ*j_EZbNcU3IKRHjA%-DE<5n-?UA3EK@TA+5d09*CO!5 z10FpW=fB%)q?2u({xx{lN{S`<9W+;!Wc zj&5qRDP^*@S^&#&*OXtEu0|R&@FJp@`R7;B9{Iud+7vpeDfZRyGqGb2Bv-+s$3v<3 zn)n%Dr~kskf8pU?X|aFzPHF&t_9ud1HCRt3S5~6Wo>Yrf0HNqw z=G321m1Q>N1sL-A9Depd;QwD~=Exq3EU0sTLT%fkwrz=BzR>HhvbVYf8zuntrVxFC zP=x@4=Xr&w%7vuk5+q?l5RP#a7C<=8O=H#UI*!%A50caiIIpo!SAJDdaBVA>9ar-X zEw@TbmbsF7o>=47e!fPWTocN&+ZIaF?uzSBihu7{`KKb3|+Q~sj(vVlmQ?_qz$tsm~^ynx@{ zA8`LczW>FrtI`O}fLYE1D6%YXE%mx!K`Ox9k0kZ7EVpcl>AGs{5Im2l#xpT0Y{`kH zXqv{s0F@wNaVnsf7pDuBW!2dy7eN?P)*`u*8orU8UTQcep~>uYjM>QVy20TQ2RHXX z)=w)`n9J`52FZzV{gQY_;KvwN9V`iWx~iu|syQ%^rc_D`8g`4Bye&1Va#s=yc`83= zF^9d5#q7+d*ULarg44|BtpWRXg=V9cZuChIFxE;v138PK8gXvzitIeG+L`JM`Xl=N zk#H>w#4QR_$Z-wvn`P#@@2X`70;rY^s!fNVeI!_B9`%RpHckA0fI&l^Ps5~~R%;Z( z&m4mULw-)VtYi8$i3?nFeqoxlQp%riMavf?r3#d1IEsyV;p3k+sa9*Wb{p(}Z=VDh zdl8Q%d8*mX6wcVSQjaY;iAY{fn5|^jpxT6XU1(UuO932f^y-WV7(b7wm^J(}m1K*V zg)#GQ^5e!HQQG8V+9u`soY7F!rsHrzg$&IJ5(3|aMis+08QdG=7Iu&qlb9R57R`4L z_@h5QU^14VGQT_K$>S03D+Tcs5sV{@J(uQ=MR!kZTKkvVOo*8bVt)JZDUTixs3OYm z^DrVv4N?`B@*em>3c;rQ0=4OG73+fH4H{LFj42hOi2$(_yQSJa_3>a;-f4B+lr2b! zdXFx}R!*h(s?#fy3ubmYPQ4HXYN?~!9NgUF$>YqmO2UX>0LO(K-87u0UV&j7R7%Bh zl4TkP12k>1Lnzm@`A$9#(Ul+8Ir%E(1!E1k&ktUw2B2wMO#4EC9`}#A+1pK_&y*C= zYxFi+C5~$l_=z+E=S#DIDfg&9%!9Covo-i_aZQr&wd)X<@;9_rE91YI5Xe?;=Sp_G zAe^Tlt3?UUaZv$0Z8pTZV$p0_+yqWuPKYhpcXAw&97pMJrUKP0H?3S=#B5r$n%UYO zOHXk;(nfv<7w2B|aonbD=E!70w5eBZi+R{!QBn+5vGAJY^YX(9K^XD*V9M8W*ys9t zEv{U)IJnv7;AWdAk9XnCoW~D^SMWrl{oeV0mpeBll=9bP3kl2SY# z3}LxEpG(7SU@A`>l>@klbKPwK%M{ZLU|C)t3C;3Niyhg&|ERAm;G!ay&QI;s^dB^8%!JFCx4b5zS8ie3IjcWHb@Ve~##lLQ44+jQW$u!_)}K4+k=jbp2Rn zZraZq3^g7Ji<;dKibvM69%u&TFdXLyahpzqr?S=eY2U*=t8;KugbX*oFOrKt|Cgej z_sPQn%_~l-{0|-v_{G2Zcj>Y3-oC+LD)c4Rjgp>l*J#YplI?(&1K7k2lV#&J#RWWax(A?FD^C2Fo7aUQ7=qmSMXRsyC<=fiI~KmyuB=xD zE<47W;w!Q27Ew4$Gt!Q2G8|6mbQRVmo2shdonIg7+&6Z*c4E6ra5$`ZKaA2f=+GxeBE&9n-YqfPs6&@G4&QIfZf^8isSyBp@_3YfKj=1i{IJi>+dU3$cWF!daMi5!dx?g-h(F1N0H6#& zQTexxGxi7VZwql5qdy(*><77PDEI#^nZ%BvDA@PTzJ76v0`$*T#?@ zsC6p(8kRtPxdfR_jtiA!$H^;?4jX=W6kr%|H1rsD+H^X*bgwkvOr_RcN*Z_KNi?Bm zsf3rj&r3Pf(hzmy<5LOP(P*;cIt)%@I_(D7aP&N4ayrLt?BIuG28Rn=+o5SYlnF$* zAn#EcI&IA`ODhOwf-PBIPjv-M;qawcc=b|^T3o}^0WN|CMyXg?A5pO_%5rzBXc_<5 zPY4*M2~hWn#jN7c0leoEyyp?4lSB4&=BYE8vrs@PT5p9ItjYsu6nP4Gm0&bL^>-!G z;3}oGmf7>$acgOg=kV_z5DY!07`eo7ecxqvnjmpIu-=n!bhf~Kh{GB9lMFSXM9QBy z=|9VJAG#g!yFU~^1Ezh6IPg>NU?H8i=1Lr~-ZZq$(!h%(_v8O}#G>AyEnCw+|4)y& z|K(E}+ntoE?l*t`fSsO$Y1f%MW#%P=@~V<6;ipeidIS2?T^7rLxDAT*bUF!$KrPWo zDr}ROm-#mo5QgVrl$++$B?#x3(k9j1x-bIj!51gFR%4Jxz-5`mCVN?n=voBrv`FhSnpN?x?ZQ#k>_GmcD z8384A+r*GmVNN;$s==m*5v@)>RCxTn_o>jH6`sWcHE1W++ma4kvDSN&o@1Wn2w2Yn#4$M}$0E$}d3bILH79Knw;Jh9Qqb4wdB{cuest|6vy%xP*i*6??q#q4O z42Idc_J4BN!fJPzRjNTeRyV*gqP zXFp%2YvaO?gc0bE`aF3)Nn`TXwGKCQm*0;${QL7E=aVp@qTR6Cwy~bnPESs*nP8Gv zephQ?Qp&FxgLyggNakTgIVEio=UsPP*{LY5yBPA^|MDq!e~^*wHDZc(l5$G%iY&{~S><(k3G?XP)a;1J-x{4Wc}N-nAC} zXI{z^u(#XEc>$1bHcA~>-3x%!FsNo9*RJE(27B+{pxeuKp`-qYp~j-zzH*8FP#kA4 zj7ZM%Nxo^}zSKADKx+4IZJW8>|LA!{9q_}=nRLf?34%Ch1ayhRX$pN+lpO zfLdr3`DIm`pA{vk(0anFXs<1_>SQ3CeMi;NsIn&K2#kUY6_ z7d?UQRbhyOiiCR8*35&iv-sL6yf3L#+aQi=m?hz!xIG7d zAT16{W>lbD7D+P2c(YpOc7N5nS!ThavZT7Kk-2#_sw>%2yDWoq313BRGJ70oJDOuf zVx$p>;|a_yC|6-#NfTv%C~2Jh_gYNjgy1ayKy&-a_N7A3Zo!S)x9GG@_O8l>c;>=c z2B>iOyX?lAU6*+9nl#cf5{yXlu=xw>c9~>Sfa$g?EGBcJFvhY?jLRksr@`@fg5x?Q zuSAG59th=+ODk&jB3(3&ZKXAON|O{$!I#QkqRxUUhFv2GXN-aX)3z`g;=GbDVsr*c zMKo8<7Zu`goQ^OpWNexm>s7X6S8W3%?6!a*pEGU92C+tyDKI!3z)C1xx7VV5%cW9| z8GQaoJkKBz^?NDCwI=$0>woK~?bU(3z?bK2Jdti`{H5p^lrHj}J6(s5I6>gn1ZIQ!VcjLvWcg`e=O(r6#MAR)Gp4N~==A5a>CwO7^=yfwf5mSW1xA z-VeK&+a`=__~8k$D*smV9#2)4ovZKEaNc!UM50+Q4g+f27RIVZ5=K-l@f^xF#HZr3 z<1nJO%=c+j(sL7%&jA$OM1KMrq%>qZxBA)bSsY%_ma0U#;fCX;L?5}2@+Y{dWs3gXyS4%e>N+ug|a@;15gy**~8I)<>eKjA5?fyxf#gIh zksf-umLo_u5nhQhaH>>XT2w^W;Fm+3mc^i7XseZ?5@o`0PSTJ@&)UCL3WH2bH4huw|;0|_Os0AmJ24BMirdD*6IG50UZ3Kb1N?yOWysOGO_ zrV>Hss2hTP*{hx!g6|Lqr$(UE$SZ&P`Bh;Eqz9n$#*5$c%J9igKcwH!T=Uy^y4=3g z<$wHNMBVi6!5$wRba=B6im#-2dybwT@te;i9^UhqeWNZG%`o zj|_TC@jzwIQ_csY*g4O!2sf*hA11}=* zBBrNP_B6uIY*aWK8P5I}kvH1~vDy&HIKI^UZU*2yWmoH25wkWejAjE@mXBjO7CuV{ z$#!ysYRw&s@kzwPuLj(@(dE_+VF*+kw90KQHLl4-lKJKUQ#K6u>EUl?0Mw4UA>549 z1fU{9tfod_zt>0&z%PFVhu>bTFq`CD<(HpzUQIa&BTUyqM>f9ogS^H3i$D9Ay9b%V z7aN209i-nM;)NIQp!&`q4}2=t<~o1fE(;Gpy-%BDva}@XXdKaQTC`uIs1oyJPC2|EC zj)~!z_(2|PFZ&E(w1N%D!$$)zH3FvHSnJeNV%io#ERWOu(bM0v_D`N4P#FuA2P@{lw$@M^b@3;}I)H%N z!`aRj+^k&1FIRBNE%tkRd{}P57vJXLoixn7WjKa+D!7t3!D*UsCaAbmIU8ft5{}E_ zfjE^h&bB+Bm9{FFW&tljn~$$Rj!jq8-B;-=4Tr2wyg0!x*RUF8s{;{a2^F(lZRvGZa7h{OJn z{c9ceuXWO_uzT0Se<5yS@M4@=aQEGY__>mI`HWgZGF#7FcY9rWy&m1(9)tc7pZ@AM z0Nnb1i_W`QChy77n1j76Z}F(_@lfl!8XjghXwZ)?wDda9{U!Xr}hU!>UIP39ICgR=${X%@{7;Xel9Xmheyn{B-iXTg3AuBZ4rbM zfzNQ>XI>Phs_WjgHoik12H?X#{)qOqHU?9?QG|D_JTRp{3hg*s|8cmW#sov+XexQ! zKjNU*;pad7nE&CMf0t5%n|6gdnn$M@yqsL%v8;Cjeg{#o-8y{tVSmVB{|K%bsYTkq z*1=CSHr*>busDYpKuR(*FltFIxtb||H>doc{Pbh4zbkfwBX2$Qcycu6FMpM<16#7E zYp@}4>DUe8*Cpm>3#wZc=4T56f1V=*{Vt;CG?e1I%USv}8P<{aaMFeH=C6R~IvIt7EyfWyEURQoNKgs3hcdg>j!*3^a z-V?7mxZUO8c9*~UH=hbG;BEOLx0e}#q6c8_fj^+?Hc~>@LxwnZBZci&Q*yKK0ARB5 z9u!4lAm|b+K-?XR_O&*%a{oRvu&#~LHQX`;ayqiXq;MIcp*+TH# z7r6NXe{((pgjJtODl1>z%WZL=bmdo4b$8NghQ9mz{xD|U6J6%6ey*=K(9b}tPBE|g94^5KzC{_rS`ZS@#e^)&clgc<(h zJ3Kdvu9RT}jz#NM2UE@>>^d&(Yi;iR-RF#+jc_jKa|?>{PI&_5mDXBaAg4*fi24S= zE7M+2l%`>T3ISAz^OondwNm4-fx!ANskFON+)|vDva0wbbBp98VaK)vBkEEuP7~K@ zb0~*8;_!^ehXblu)Md%fU->K+6aZ zcj-C~M_(TSCZGKDlQhwE|LY9VWqg`Yzg!{NYVz;Dc*Nne0Zyd~8nR8al9Xj(f@#4t z1Zz2C;hzuzOS=lIIoMSKW*CL6v>~VL((;$Ao5z8Ckn(n>RAL(3x7%$|D;vz_F|*1N zvuwiZHHXjql)RB;g?XusCy%#amVDyI_k<>_gmm+v76D;k3Gt+4P_ z@-J5gT;=QJQ|(@uwt=;s->FA0wGH92!HxIY>{!|Q8XcYR^vgbRI1%vhtNeyxY`aV) z6Er~ea#r`R6R~i1NXX#9lchm(TO>=PGpJN5X`h6(T_KROB#vDn2p23=&L$KkeHN*c8t%1709%67M20Z8evj;19%2n1)M|YW{{=EO6z`aNGt9vWL^{b@}8+cj#Vi zrFHn8tS=Ax9{s+@_;j|$ua^_0{Bp*BzM7|ag({=3=&^GmD-Tl**S=mr87kJme;+YT zabAAm7+xekw`EoFq72$eHv+W{Nu9b<*f2`r$i(q8@5T?Klobg?TX)^IvR$p{M*g1j z4EguU&vCT4Jv`$6gUo}ud#B4SIqLf8@j2eWcR2^&16NyH5zg@PxsdkwJ0+R2F&aJ42T6)c53c%$@INNzY9c z5=VLjj$I+5k(T`C{)Eaot>H>G_n2yO-kfxE10f}#k%|aW%D+jxmtryaa`)HUiMze7 z*y#(rX1ekZwB+7Ku$X}#cW%A_x@@2dQz4L+(aq1LD)WB2$XVFVX#q>^*M`+sIvKHE9KY4KaKDwetN@oQQdflL*deHQvMyc z&fS~mcTOppg<0);v5ZpXuWdI;7E#U!)NNCs;cAkENp7mFIODD}39I+&Y&~)8VVW&w z^Ee$>&OCua$RY9F^bWt8+hFETa-M)P0xf9-lvr}HsDYjHhPnRHQmhyNSgp(hfGlR$ zyQ;MVth{D=ET9RI^+hT98`lH1A~fqz7Dn`i<~R>m`JgFpO8 ztix_ktgFY5gh6}qR4A+$N zNkp4?%CLXTkxbSVQ-v!<94YI>?p8w6Hd5dW$1&*cwfN+ZMg9KpC}4czbMjJrk5)B? zv4>SPY1B-{MCE=niT2Cl>!z^ogn^Jwge>~fZe9J0o?sTlfU?vKj9bu=GWYRIKU_` zaI_(z%4_gO$9T_2w63;s8;x{;3ZPmka#!Xz@QogTYa2KlIPH85<18JUJsWX+Brafg zPu#SQjG8Z0-z@S;}O;{U!&+w6v2DE@i~{6YHrZ+|!7gKOe>esEXd+|83J zkG@N_K**20KNZ!=(nHqqq zBAukXQTZY70r(Q=^6-8}d@y=8;_lsB?Dg90_1bj$yWIcPJpcj)$+aQ=YRW}xqT>Flkg6313BR~5?SU$WLWIfJP4JU$pGa{IX_wCD=HC~9{<8VqGCX}hA zyMInDQt0CUeh*_%B&PVq|M_eF;?IAQg>KRVxO?{&ckkZfKm3RL0KdNxxIp3;QECKq zQ49?}M14lxv{%WG0205EUF zIU}Hky^6zYO18{yUZLjUf*_pY*mdCvxGhmqL3jXu6s>_~=rWW8Im!rBK1n!yG|UV@ zL7KKf)2gRN05&WB_~h+FH(kveP)0z70MTj0#Gfz=XB*Lpi`Z$|-yMQb@79XaNi%@D zl^Fm*3r#-hsPhDz#`r;YA3T48S*i-dDivG@$PD~NijSWYZ5T*-07|jcEP-O+y>~C` zg*g1`C~ZRp;SEfKWv&8JDQ*4d?f!2u2Ag;we*3!tcW-uu5h(PD-MZc7mQ;dA4+kXM z4MyLHXM6n3G0(r=JPim4e1|>pe2msPiNA#?xv4U0AoEG8kw=%|a}UEVv;qa>OZH&Q9LN z=)Awf&0a=*w5Ui7yd#gJp-0_+owsDGRW8FhE-$cn!zeiB@R`p?|D?^lR-rZ$7;!-$ zZt8xo#eT0&GM_WAxExD-_lHltbeN;n?Qr{s%lI@&6MZ!|Z%Af-rktZ-O4~4abbmnb zQfBxsXAnI!1Najv<#G=847-`D4$KRv;dKQ6Txw4v@o)^=#IViW;F4n-RGCv@ z!T2=7_b0@ZQZfm%WB^r}z%gJo!6?_5%i6Z-LJ;JySK+=ZSH5{k#<-Hi)!py0vtyxR z$^E@nN+0gLTPHYO9l2rA7bQ{Bf zCz5sN@slA>wB+)BuZ?472Cv;|Gd_L6i3YVce!khWC@Q~_neV5iGxOaB@rlL*v29UN zT!!Hk+)EPs_m3IBJmIJiSFGa( z^iw0?XgPu)OnC6wkXF0Foga1JM;$)@)q=x*{&=3}(Q38uJTF(LzUanfa&eMK?ev*a zS2C8O!fVuAe2ykAs6ff-`a7 z5Bnny`^O@VACA)ZJB@s2p6>qZJA1pg8Qu5AVP*j1mmA!&inZdKbHg?4m=** zKTa{~0j83sYfnNejTht1u8cGSdU)X22A&)bRxnXN`{|uE0|4;g%LjO#N11%x|MAzE z`?BNKiCBjbA3l%h$m2i#>L}%2yYam}M&or?R?SjZ-3ApFSwoP9eVAT@ywQO_$r*yw zjS3eeVJvq4g7I=~11lvlk@qZ|XYkv04bB-qE-M3~B>~q$0V%D${9(0|)~Pbh0n`P1XRJ)AiFYD93D zt>^uNCY@Unvr%@^ZvE!h*|5xT;6Wq5qwICw-u0)8v!S8IdmixsXtf(SS1tCndGROT zcz7>8JWo7m9sR}|3~BeW9qq)QQX@aUIy?tFQ-OZyN<9ygHFjA&9!im5$%stimGA(Z z4ar31DX1F1(`|vo)N?g9;jy0ZQ_3ICgaJ^JdIIA7mSK_^0V$4cGB3^2-CviMMBP1p zHs<){gk6~_*4c|4tHFMEh9Ay29K^VGjj12!jDV6w7^Q}wrU+n9lg;IfCq?7r7#CeR+(_0i)-DB5kGU|&>OZNa$`HnLf`;xyAX%bX(_*0UbwpwoYcn7X7k~HsCx8bkC)@YG^a)!8bdus|{M*1N;3sE19vAn%RI?Zr*FFl0@-pGNMhCmwb^=3x++2JzNB%AcFG~1i7)pu>{UktB z)XHVVC;+g9hZ@KoxNrnN<4BT}W^^{@@h|W0u)HgQOS&gNi1S{QCDJUw#dHcdJ~%0% z`A;t3T`z#90?+_sQ{mSmx^8KJ77d;xOqr!14N&JCl|DOwz?e{zqDEGy;(D%V(Irb~ zKs-fu66591=QvFm`1dus_ccC^8N%rdi#H4W@lTgnyjcLb2_(m=B6XTam?i;g^li4U z?n_*@KGKsI>?9J)8YyMbty}P74L8}Lq&$n4gb2P89#US^qB(2l(DkRP0Y#kSXEBbS z#Ym4BaymE6XW-inYVr_*rxD5}c?&XW3>X7l^1G_oAWmZNZKW2TW{r=FG{wKZ`WlkF zGME63#?0Kw^?0OA8HESAG&=U+P%_0*<4sf!RQ1Su@`LO#)5o#p0Q`&*4T z&@X;5$Mu^M@sXKzt1J$>Db@4u9&x;4OKqj97yt~74G8M!hHi-34s)PYlwVaC5n!cT z&Zs8Ltr~EqS+}7U+~&}A9N5h#K3_a!GJcPf5Rraf)N-3w*0EpoVGMtu^e9EoIiyL5 z2EZ{{%zV)F|4Z;sPcj^*F|w0LH7ou0w<7jGJDXuXpCPY-nO4?X-0gP- z>WxeNaa&u0Wuh^js=b8(fcNITYBVr5MTJZFW|eCvOThG=8z7b56;&3}lis7<)Ld;T z0)QZhsd6M3onos+cnY8#=p20cvpHV=>~XBX03J2}kXnD6CC9x22w60!1lC1`C<(DH zB!}6LO9tt4&HU;H^=OK-=W(wD+Wo(&$GqXhe4tnNr2+oM%|f_##)*mb)N)0&EV1c| zAf3d>?@O@}mVR%ni28s?kmTi!ASoSKej*xuN^{)!JVkY-c}aHD&}e;73FuPNeUW3a z;S`0>)-`#*sVim?Lxd4s~8MduuAv({vPAy#3sd=0P;5#$?s2*7DCS^(hIX=no z@>B7>ELYy#EHIzXaDBbN_4NYZZ-G-%0H6Ks=lJ-=468RQtQNv_^Jr%ywC{cUeF|I_M!kV|@H;&8CWpy7i%rUxTM5|QqA0w$SJ$Clui zWR`fy1JEQL0XZ3S`=qa5>z0VN$51RkOB|y$cAM7-8G&aKKWODNl3~E{Nv4zl&@&%rClS8*;&WVI zFK|Q0Q~v2 zw!fZ|g-MF^1YG#44W1XZ0>9jz#O-|wZ>=1K=55q>B^+&0NmXbo#z@8k&zo)ebtT|W z0#x5wJuxybFo2Dr=|P=^c=Xvn{95_k<0LeCrvczSxs9^T(WV)8t=BtN+iIuWYN6G4 zFfFXzS#FOfcCCw)npfB~K=Xr{HEr)5gr5u&A))$?qjt?3#(t8a)?DGu&h1NhfST`r zv}l`cF3#sj<=%pAjb+6Z?J!FM@S=tvQ5KesvE&=ng}GYIf$3q@+BQs4rm_-)&&|WR zNG}ji0+eJaW$9cmP-A7W zYXJFusTA$m@hLK5L|#@EmUrZVFu6ZrWhiXDL>VRl&d*Kn&ZDfo90p^NgY^6u`PCix z)e4n%I6fQ0!xOXA>hv$jIal{vE>@AdAlcIh{M{OP-g7_#K#H+iz#`t$n7~tr_0CN21pZ`UD#O43i#;ixH(bmaDJ$jiqo@7`R zOH9erthk9kjBk^(mc~B!qE4f&xDvKyQc+{@ogK z+i^|`Y;U*XU8&|)*wYBuy*J9Qd#l)N93)o%fHei#pU}hjd7(Aq>8#gP*W3(nCAeM71=Rm1JMDC-i&8Cl08HTXqQo?b zjn>q38h}krtJmt~7QQh$smx;>V+?bgcf;2hvj_oMs13EADi#`@^UdOalS)9J4Ui&p z0M*tEk?cADFc~A81Xz-Vo|EF05BQ-~<>DA4xTuV6vifw_>FgLGG4Moej5WoHOL9}Q zlfxz5X1fKd3UxO^UanQ&0CNTS0pPOr_gHYHjOlrhcS3pqI`Bi!L&!p7b=N~8AL1LG z#DqMO^A{O@`Ky=s#5df5-+yD7M%W@pS)8NtEmk1_;=3au{UU-w8n3O~_3IG5P3Q0skR0ctM0Q#7> zzQR2o2zjN^+Y}X!*!liLaT+t+mK=*BhXZrDg0I!l_0~r~)ZFX$2lkTDMUmh)l7rgk z>)`h%Q!pl!K)xEzJ=oy46@qNhRTh5KD)86$C2FGlQl3Cw_rTY{uV(!%@XG|C^a3hc zb(V$XnY1Qwnx>5A^{nS(#oJuzGTKVXVK7eLdV|F^l>FgM@(<#bj zjfl>_ysxqP{$a3%VTcgI69CM6k;ah%KLDgoi_)S<8*5&kRIUK}GE~>UzuInCozp(jC)!4fgI8h_y@!%@DFXex_Y(1`HPuy|Cet|WG4|?*GE-U>Pu%G)}Ird zwhZW>_tD?~({GHrTb`RS;fChJmo!jNBLGWWMMG2;t+9=?zLzIWC(yMf8%nn^$s+7s zYgG12lNcB0b6`Xon5Z=EZqzOdM8}Bat*`vTAY6B1|VSHa`f!-_kC+)MC`x9+|FTyER>8 zHVLs<_Mn(ee4NfQ%!oOO(;kLzX`*tW?RD@cA!^!0v?&~nlMX>*8-R_$dh~>u547(B zH62sn`Dj|R5ynstEZS=;cCDNNI^v6@B`Pr!_s5GvY|Cm-0mKs^J3U2qGQ;xi0@r`| zld!aB4ie?}BA+IoXu`M{fguzczWYgZ7x7!M<_qkyZgU-rY z?aVwzYv;_Yy#Y_?*pHKpTy=5WtAgXM$nkeS7knFEzP`btC^55|tQduK^7)q~Oto^{ z+U8tBR=mV@?)v~r)Ad}^2S#*lrDhpxw>U3aIIt}~y+OJ`{Mwimc#PO(HN`Mou8o8d zFB-riVBn5857rUw*S1EJFGK^1C1Dt1{JsJwuZB4e*sKSHR04X8Z>%SaJ%pA8V3Sxd z8xhT!4&T*%i6^m-ywZ|h4rQ4;^K)e4tZ~ercc}5%UuU?bp_kLAOo4y#W{IoUORO}| z_%w#x+*fF5$G}G0Hq6Pr9+Rg82+l*B^+;vi!w`7|q+y8LoWpV4;e5~|ITd)ZKR)Zl zheJ{w{9pg~S85g_Bw)C1i~L@%K+Lol!5$|HJyV)H+X1W`g9Uw zQItqSUwHsP5B}U)1KS3nwnpsON`%gziN81_R%IM9EJ#s(^Dpx-Mdhv&)eSCTY`^Z)LB)PL0QJT z4ziP1DuIjbVLK@SOFd@Y1z64wt;0JmyKuWc3aG485u*ko`RNK(JpF?_6>qQ<7E5hu=GHRe_e69<*P>`LQn1%CCb z&H-~eFsIX$0QR;;Z_9l!T|AdfM8f)KCo_1Iqma7~_d(J7@=&{`H{*74BT)b3_254&U%tMLc?AQOelhbTbgb_eb$_W50^})4=+9j7gR8YQd}qLZjZap82G41<_T|h zBa?7s7Z{1SU}yGr$&hKBh%LT+y$0(Xa9Iy0EVINRcigftyBcE_B9JW5D{Ge^v&OMY zjS)Jp5yxg@k-O`RFs`GdX+*T%VM=`q-%<66-Y&EL|M3;a<=6ivUYlg_BU#4X3Z93g z?YH41k?6y)8mn~1rH$W$jY9-SU|ZpY*Sso0l^LTw?}e$1#E=?m5u4nCgyi6yLuBza=SY?+66$K2z1+=GAMm^XaAM{&LDfM zT;$YiAU%O&HpX-kV0B;PtKaSSAsCGqBqAT+8P5tl-`|FBFxE%g~_0LfjsR!YF%eG1OMF}wh8 z5-2agIM;%s?tEj{*0)B27Qg$}3M^DgqLj7^&%*9S<}Yt?o;>9(7uI^xjtP0+g2d6pVFB z5x7<(MljLbX-hWcM&u+cuR{WS%pz>0Wwktkjoe4ChX5?X7?raAo1*T4Dqr$|HDbvz zcz&&p8vxL`>a)cg+x^d^UIssP#aQuu1y{cRfM*a-hMSb^O;A$8I73w~59TlqNopOd zbp2-$6UsoF9bicc$9lcoxv(rsENRfokezn#v;?H?2^iPfe$J!^AitlOzh6JcDpV7*oOlbz(+_E`ViZNp%^(!$GGN8WTO-qx#*~VzuTEsn2lVK zB~4=>1?mxiqY|@ujQV?k?HaJ>)b_Y*;W!)ce1p4pCBFXM4Q5W)KWy)EG?)iIZd?E*&~!8e64QB>LYa9q6cbMaMJVczfKp?9 zWu2q?0@y^HL%aJ3CEp;X8@rU%h_b>giIKLJvZSnTUep)^DA6b;R{&`91{irg@OPA4 z9o+%DW8_7jkV}&m%cAHLOnik9zOhz}lLh1y?E0xV&+TrDkOgS8zJYqgf#e0!)B`%; zl=&F7EiUz5kz{Pd&8^iq6Lv0;`annH$c_o9s`Kb|lpJ%c`Ts74`vY)&bq#j}#1!+F zxoniN2B&t=5`K|*UU%#M=N7B8QfL4IXAC_D!r_C_^WeHq@Ham_SCnrz-`=3Us|_0O zoVRK=Bu!dim!lhbh>|fH%AKw92AiVlyKk+WQ!w)SDY}sdpFASjA?o8Jpxfb&I1I8R zG{%>iY}`s@KMin620BPW1lFbw<*(ZG*^D`0A8`NA$bAbE2f@I`Qb1%!d0{TUn9XCH zo<%r48+KdewAh}o=!4UG68r#+mQPS2Wc~qQ_Dq0(akIq2=E;QVUF3ypw2F)4-Ry0d z8WO#PEa=YU^~e$zdh%%U`_2`Pse#D2q~nUy8K;qcwm5-~+uvu#f41+=ng_ z`~Yw}0Di#q0Bl)XAXbXND!SBg4IWB3wz0A;xGXu663{#>Hi^MMK&O8Mh=BpA1SI%5 z@4%RYY$BFgEGYx*IYOIA@B_jF0D#(R4r1pan8xUnCLs`;Ud11N|3?78+4%wX6Prkq zLI6W#bIy+X2RK(_ox1RxE1K}XM3~+FX^8|s0Gv{X+E=e>`mt+q`MT#}lp0)qA5bf( zr)zYXUtrlD@NF>%g*4HWs2=z0mLI^u{KPGnt}s-iQnhUq_+@g`+XYzd#Qm6#Vg#vq zt;Vq{=IqOT4<}<|Ov6}hf;t?Pyy(CZ2mcQibV@w`(JRXU O0000w0Yv>+fzcY|~fAQ%h{1Cr8R(jhg1q|zcCA`OGmDcub( zpa0_h{&Ln?_n!5f=bXFmv-jG2M`)@m5I&%L0001?lA@e80D$hN016N1eq5<*xPQR} zD9Op_dYbKIsZ`kN9dwJlu*_ewX2QejdWxkL7mg|>XH~Q+%*0pT4kTd!c-isr3xD7R ztLu`1EOSA)B=k^e6-BxNc8CKb^D|tsIFJ+#3WbmVxbx1HWBy_aZL;;|SL@C6`>amI z`I=#jlVAB{Lm5JELfn}q^z3dg;6e#P7WjfB@FyI6{?DLI-1qKk9@cuj%1@kgvu#gZ zb-iVNM`z}}S+vOU{GxBamnHV;qWASdPneFnnC-W_+pDm5zr^f)Z;1oWf9|7Q{hU0P zA~-@_1FmL&&fndhRb?Ihl5qWYKZLpZF4ISPjLbFdwMzKjY?+&%Wu}_@dyP!B-R!vj zeA5FJk-lj`Y_|>;h#}y58Afb=-a#G868r3} zFU7+gueJ2Rv!w1{@1{&W{<QOCCVY@r)MTYS!lGKF1?#|%zx`Z#hJeo3DFS=|3g z44b_ha`k`m%4es}a>}=<3Asb}OMI(BXy^7~IDqk&=yKGellc{<(aZmScijD(euLJ> zw=duMZh!6hLLb}evlv1hO0F(ysTL3;wwAUyVPFnjm3BE0u~|LJxw{s%yR7%SIu&Yb zI+-*H+w)zQ_}L?M`nBw#+|3B_e>)HB z;QH%484V5(+0rdvfBC+avDkKZv**@HsxU6+bRE+=cYjMi*QL(B%bWYGszg_^pqh3Y zohXXW=6qZkj;&)Qg#|W&U#!_kjAMc3?J9o&K6O4_an_#ySyLX#R{=)<^Toy8rai`b zmsu*JBqgi7>5$@iQ{;^8GV2U+7A`oGnLJL9<s+fqWT&Sn|~&>xJ+4<3+{_(0LWiV{JkX}zdY45 zaSOXU!b+wA7H4?gdR81PE%~x?zO=IUI9t+ZvX^>=ko1%P<)&JP@e5!i2I{WM8i-O( zyLnfOt#yw5c|DCMP3TfmHl!UEaDA}&g{*Fix_VDz%SRIC2#gvtNeVtm?6i?jB+!yP zU-(G#W?L3awH(odR*&8;daUOJ+=>r{ z?ZNzxM-~fUHyd>5@&5E~*6PF*YoP@P$paBdf|%pj5SOnFZBBiuiZcjCX>{EJV%|q+jF@rf2f=vR-qr{}A{U<}1lr&*P7qO&D~x}D{)*?y&j8PLz_rja z=6z`#OHF|fXHR*`Tu1L{8in^-wTSV*Tf_kF_( z@cu6U(ul}9wHKkpLkZ?f%GsgTqD)80Py&&)Ds5s(R4D>u86}pxQz;XL>h}gR0;xH& z&q?*2xcp~Zqy%L9S<(i_nG#WwuLWmS#R^G_2W3$RPLvTm)_&r*PW#FJTyK8r1)z`e z@dZZI>D7SYuem$wyv$tbVzt&c^r!P?nlAKY-PuUTo=iu0Mfg5joc<85L3cOb_4Uzk ztiy zi7%H%@Qk_ENntlX5d>hin(|S3-D7V~QYupNC+>;KAXnbUG~@*KBV64+d<9s(y}qA0 zF{n!O!otV@ax91Ou4x4_zEbDQs&BDl=p+%p=j1qOT%m)9x;1)&!tULr6Pv%jD*UB9 zyN?N8=}uJY!<9BS{4EsSzf9{w)=NnAgo+hngWrDFRO@Ms$?ERk^RYp#!_!Hb<=CxU z&71InP`lJrNxo3A&B8<~;crODHWELOT^yrF1JoN1DUdi4v(-%!y<{?>bTlVJjXyb& zF}t{zr1dRld3=x9zn`H4WbM`LfZw6;8yoXf1|Q>%_z$|vc8iN+(&q)_G;Is=cp0!3 z6N&1Eep$b{Zg0ewd9?CZ!ZjTRTs70%?;N^OWeI+p|h#fGwz=gC;G~;a6|K z@NOmHP=C!OEIo)`9oTj&;Qty&=!R}5R$B}CP;kjw78RF`gh5@fj|_wkmo&WXA(=AU5_Tc$26yoP6#s>1xU zkO8t)Xe#O;()@bKAqbxNpEibF?KZhakNqY?3G?)Q)w>hxFiQrM>3~em>bx^Ytryz8 zgrBJ=^Bj9<(u4j5$ieRkJ}ohAe*c9#rJ*x7PHZ%bAJ(5u`?Hz6p0SqRNx8ch@NW5!WsHutnDmDfo* z`Ohx5Jo^oiHa`G`^W2E4GzC+xrIapUAvI0$2bY$!P@er-LzEn-+zV0s#Q~p)i1zCs z(R?I&-;vMcwBiKIA+_HAM8AerKaZWhzEZ+t_Z0)~3*eOIcP5z`5>1P$P`-)9v*>?> zS{t=4EO=oFz0i;&yVh@IkIsq485$sutc}Dx0$62UsdDE!umVnCxzmE?OuV#dJsE=d zL|LxIGAW=rkJJ!Jz2@u{a1Jn)&Ax7O`F3+U+tW_x^MC}}Fw!$B#v8eEIKU4x^IA>r z`hvLOsWX*MyR?>8!9&ldwpNy|7*F%!exgd=Hs7O3!Rb45py2&cC>g+2Re@t+P2p=( zFa@wKFQ_mj%nO;k0gRiGlAhrGccwZ=v~7}l`#V=xp>+VYgbpKK(EhB3-#?u^fmApM z4a+OUVNsnE$@f2hcj%`EjF6f|D^i4$i?vY%&64Mmg=A z89H!K(PFPml=D(;QjPYAmvC>%+J??u-K@`%OlCwh#AZzHN!QbzA88&uF(9ph&O;zD z%;aFSlY?Bp_ld8=bw77y1%|4euQyv1)!Y5*#E%zj(VPKM1bSew3jond)P+#^4ruSe zQ>eaj2B*o_*$$)}e45^cpastm5G{#AcOLS748}{jgW)7mjBgdp$x7ezX$-(|=JXx}V}U*;*+N{>XG{&&sr%d|3*K*B@0IUP{c`(^H3r zJ>p{+l}UTHSue1=i8U;@!x__Ndv(^zk|QBXYDj`2yk*WSUXin+!OUnnDI;HUza_|% zpm_^w9(%)510w$QXxNMmd>7lEVZ%fvT%WuxsvU43@Gk1d`bwe(BUDtJfZx{kVEacl zVff|ywRHQK%~WKn3b=Rm4JR|nB$hP&w~hSB?kD;t#DHGXk=5a_$M*Dys*v*p7N{Tz!kBt_6zyEUg%RetQrrwA!BEFXKNa8Nn^UP4+fKA+;mM1z>19`;Ltm z!|Bn&i6LIUk>X=X&{~qi`#o}0^nKKoce0L9<;_DX4NO(qBvosx2{~>%N_oUji!vdl ziaYA5xXRk6?DEEfsjlXS1liCrPWOzWv^6bLtQBUAi#WH=q|x?P3z2Z%h{NY zxq@ERn^2^oik7$^_cH?-3rI$CHt*`lF^}inV1{D5zHX*QiGvy)Kezm=39jME_6eX6 z{A1UF{Ww~Kx8CfgPBzb9F8MVA(%$4us+i?aE3nB7I4ah4#K3`xKasViws@$6q)W3W zTFy}cx*vv@#;t>r$4?l^;+^r@GQ=4ss)0%e!X)K9$Gj`G2Qw>pe<#&9;vFY2E1uA! z{9pr(YEypND2Qg}g7J%%4#qCFslzp=mE7BDXqG8ia&rpQcE)J4`Z{{BZj^5X4*2iA z`|cxnAb4{ek@emF;K)qEe$=Ab`#TRPshWoO&1q8i>Ep2)PlF%432J@VGIk?&7DAsB zoK2QWc>ge7EI=QiD=8`JnD-sndu=0tib?q*zS`e2g;Xt}zot#irzUngq+|jP_rGmFR6wo@`RVTlxS5w-V=)~$m z%kp_Hf!PifJFuYGpJee2!Ug34P< zN|yy3Bwd>iy2TyxS(%!+4nPWKQqX`SEdokH zJ`s!0*8w06>uWG^Qs{|%X)EcE>1$^{^MSHMmt$SV3|U}cy4rZ~ak4kEy4U%rxc%{G zprz@%Td;52#gHqJeU2aUC_gPl+7$wtCVQpzSN8dx)GB6@pmz9_L|Ow4!9-Cx=t@=M zRywHF4wh_)6yT5~i;-<%mKmUwUf}(X*E`etT5?Sb?htJ*DMwr;AIkN=3Q~vhU25WN z3gfMCARuETBbbpR_wRvn2pbD{zgW5wUvqT9`}TETneIw~LaxD&A*c6}cH4pbppu*s z8lOvY7xsnS$`mI{LsnFF2SmRMK?)3?Lgo!+8VJ7Y@%2yEj|CaHX}O}mNB;)$wKdU7 z8*C`5P_`cQM1R$0fe)<{+f+;3F@=~*^4mRTl=h#nOC2u#azlJyG0BPKr1g$c!c!@j zBbw)_)Ff_uTdEkgW`M5E)Do>&!)jvHFzJh*e>Oh~uIWa$^mxq~KR-1sz_FOvob+?Q z{uTx}OTQ?8(SO&2b40D^8JI*qtnrOEN4H$J@U)l=MaYMteghnjWe+Kw5WjzGAy_R@ z{aj^N&@tasw#+HG+`TNNwuKQ~wffap?IYQwKjDc+IUQrAp~p`XURAk9Euzl#Xmq&EeAHI`piAV5DN*2CTody<=6L_v2HJz1ddMeCLYQ=B)LHEJ8Zy z`sL;UdivJbt7=r@_>8f+xYnEvrN4f7CC5a1zQ7c>*$(^keK4LCtin>o2c^lBO@DoF zihj~g)+uv_jp@a@LPCh1GBF|dn!*SFL`st4H?{Bml^$Ah{>hy2w_=0Me0vi_+YW5_ z@Zb!GkDIw==bWWyZvI3{p)ENZp-z7REpDsah&! z6hAf#_xW52^n!EFYduT;Q^>VzVeXN7^6F2XYshLW2~ZEj(`-0Tql zX93-w{pq%buU)+gS1Oq0nvI8d8XX{)7CMAa-C$v6=CG52c$n0h3&$ zSh3ujyg9!lUF_NnBY)(zua)Vro_C#46BEid2%3*$lC8ofaPA<;KETmBj`I|p;ard@g@UzI29@%OxXQeb{b^zOk zlCQ$#o%0;ZoTRAcrgt!s%B40pIj+El8z#7c|HArsFKV!(lwCTQV^5yE;SHrRxtY&4 za}qmO%o0;!(&32dzQ5)`w|OcRPUKd@ee$odpZ6*QK%i1PzNiFQ zR5F^R0HW0$#6e17`w5P{^Z6Luk?2mFK~h7jJ(gFYfM@D!zO|c1s-c9hlncfd#E&oU z!+N)79v#Y(eAQ0HqIIaUa^%u;W%dt#94qtvWUsn#H8VUk8WBD-1aA=4y&m^ULf`X6 zbFhv0`jL?SgR$e`EwQUJ&V7Yq1&Ue1k+?hOz>+N78o<}BGZ2p25I-g>e*Br|QKc-< zVxUq0y5#Oa&lj(%Mm+`a+By2q&%y!DA@%+@XfD0s=DJVbq#z6S{31D0Z#Hr!Gur}%?Ko3N(M;>?fO3ht>b+FV-Ey(+PIxfEX&a(M9_w4Jsc zW?d!ct?8yqdM8huir(rEkf97%Xe+y|sEZ*Y@Z~woLsF33-WgZlAuUtmQm z`5BljpZA{20g|5P7)S8m6zdDP8-e!`tantTM%Tx`!h5}42Is8X!-4J9Cp!J16UeuV zCJ#XTjznzWxK~Dl`XhjH4+^&v65j6m^|i(YABg;d#(3wdi$YD&$?|;9z|VeI-TYr= zTVP3{-*1EQ1;tE6^jN)-@i$y*q^0&l!y4LVz~mJ|h^t_^-T3^ei&3;~J%lM! zqAZ#NP%bkJV)9-~dDfdrQ$O4ZCemgg~%nBogI$r09gS;MXM@a6z zD>ETr`0WFE_V%%3)#MnuPdi{+-B27_!W)%=|GTN60m6?NLeFS6VSO#cmkThz*Hm)# zHx!UW&(CHyTMFv!y^G~t%g~f~d~Fth7l%t({x%%*toGND2|F;a0J(qoT%nuFeZnOlrBSQLq>z(XcE^Y9dvjz^g?fO= z-nB2WL<_zBHvzvIf6krM5}cQ%dUBYr^l0LDdC{ZP?$T2iOnLT%647uHT;k%F0^bAI zA|O)0`7icU1}V5GectzvIMOEd*@65A`Yb4Yx%;%09D8t!^I9M-qv3c%l=GS1z29*A z)2Nl%*$-(azBy4qx5p4c)#E!Qq^fp?Guz)mV~=0?uq0XVX({i&68 zY*F`Rfy}3%=|wH?LcYjCH0SOtlHy1v@a1(&l6S@Yc`(rhjxx2*ezUB(Bi0F_`G)|r z&FnIF^M4J`E?cd%k_2pTsHr}0#7Ih-WXQqxP|AB_a%a1LB`$T2jb7dqOLhnIhkkYa z+gRthjK=`pZKBSwyA~Y7xblj6pLI)F*O19dJl~bX9T;|#eyM3$|J<~u? zJjaaZe_t`VnC9QY(}@BC=IflM6cOliZ*GQo+-!21F996G3=$q!r#oGQ*wuwRhDouN zUB%OP#uD`MKz-7yN1;Xa62_lPh132vdBoD=5-_ZPV(W~Cqq4DGZV2O}lDe_$%lt?$ z8fC%ZvNB@zV+ypsWJp*^URmp0qETey$C!L&8G#~}PGdS=zXpYHQmX z6w)9zhTF?*?yVmG?)T>L-36YP8v1XM-q(`91{{Fq&-!8UQWqhxiSXLhdNHvL=AaCc z8m#*MRF;JmbS~(t3Ew-xr~r{8`P1bTUXyOkchMtc>3FzH7R+9RxfGNt&(Zar-qr<+ zcSaS?hAXnuvd>1Me+xY}IcXArPTS#v;(Qm1GQ!f2NN2mAoq_Cd80iK6?&tI~@ z>7Mk@R51@{4hPvaiq++V44K8v=JZO-R*upI%h^7E&SP5DMWbWOPu9e zwA6)iXIhkaqn)JN_wW<$JT_U>Yu}+m1OHXp6k~>j>Q8+i znYaazx8IHf+s$X9pcXNzzGSv!2WB20n3a>@(7{y*4$~FRNys0EaY?I0EuZgcGRTz5 zjRMnn?AX6{k*-&>$s+ndl%l8#pIntA zj@y&wvUP-wnfXw6+Odg;r_Vi>UDO(ds?|` zqN|Qu*k`XtxFAj7)Ecl%+N-$`-SgB$6=L^6nylTqm`fr9&-oeHsSIr5Ub9wv z86gdKVDb%stZk zhczK`7WH_nph4un>gE5ICIH3OX}ql98hIvsi+Y_@s0O5%B@QA0{prk1n#K!O0&21v zw>{wk(0iy&8aslB_WNWCIBjR#wud}$^BzjIN5Di8Vi)-z)6aYo*?6i7_fX=YGqP|2 zlmC2%g!{~aoQ3#3)R^EP6d|6i&lapcJR<^aThy#W71wEt(R bp2K?gzCn+ER%_iiVgO3=>T+LXErR|JjSfU4 literal 0 HcmV?d00001 diff --git a/content/textures/font/font_system.png b/content/textures/font/font_system.png new file mode 100644 index 0000000000000000000000000000000000000000..87157e35684ad2531674dd7de7d7b116e5b33071 GIT binary patch literal 22638 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 IRI~{F9|WWE>i_@% literal 0 HcmV?d00001 diff --git a/content/textures/system/notex.png b/content/textures/system/notex.png new file mode 100644 index 0000000000000000000000000000000000000000..0eab827a7e74a8b85b1609d8a1be04321443c645 GIT binary patch literal 2665 zcmV-v3YPVWP)LkXii(Qp z?d`?kaM0P=$)Q7s0H~>{K}2vmoowH}9e}E;D$SOYljGKwq9}BBc2ZDKFu9|e8W5St zqoeTMcX03^gob)A5jW z<@U{+((&b&G87avQAX{rtgK9{zj5P65fQOiEb^`DFE1|_MNuRsCPs>jiYCUBmnY+1 zq?=ZN;o)JatgO_&*RNl%2>?tQkS`k`rKP2knwl!(+|M`h)8K^sT6t^c_3BkpQ&UMyOw^{hq9_5c zwaCaw7A{=KrAwEntE(d;Bg1D|1(*XQR#gEIvuYL9-U=osg9sZf76h<(w4ng==ga}1 zyStmOX0iulWo2Qv+i7fUq@bXHu&}U6Wim4}5fLqWOIx;Vp{%S7fYYZ>vth%A=~e6L z0bs#`1zhmxuS^6Q8UT0$~v4vE9zkd~Gf ziH?pAcnQ?i)k#cDi~tfB7bi}qQ(|Ldy)FT5de5FMA|fAu{IR^~Q3jBeD_3gk{~MiO zpGInHB`YFA7K8N7WC;M=zI~fLd-kw-^Je1XW_-;apU+S-bu zC}d=0&^J}ZhaU#u+O=!U@|<-*z150UQD|y%|NoblmlGTu%z*<3Fg^1Orr21%1{Ta@ z35W=5)~wNvJ%B}v76l|gc6K&dSy?EGqU8@i{E&={3@WCoxNaQ)XU?4A=RS(8T)Y?& z!Loe0n|;IhIQehC&G3yI{A$-OpF;aJ1M>3n2n!42g%@4`;H$4Dwv+JiaJRnz^XAP1 zAXGiS!^6Ybwrv~d&Yg2>v#zd=r=NbB^AA2aVNmxLMNtR}4u(~$VE=yFKm8P|)ylII zjZI$XJ9bP=7Q3Bx)gKZP0)WZkAmrppwr<_ZN72zUy9d1a=9|*e(jpFrL;Cvq+bs3mGMkbQBg9i8_&^~U!JJ5e7TH|k4tY)kKCBaJ>aLv zKT`*jrnAiji3fQLWK1{Uv?<#8LFZRM{;1ZuI#l^)M?g|PDk`pIR$l&0h zS32Y(L{^m$txHI7AKd=^;&eLY*@Ogn4CG%R_N7bRf8ca<$d7za_kjBPdgjlckJ)S{ zIXRhzh6d)&ojWBVI(+F8gLQTMA|r!lcgn(z=4NV|oB0e#0zx`E=GRP#TQ>_U`C&;Rx66Hz2>H7prt=xd$D(w*q*fK%TZ)Pd>@_6&0+F zjC3P3TG?&e_~_g@{^ErYJ*X0*MU1pZ6F>_@vplL#0D@yFH*wfCQ9v z)a6KSuA6OWXh=%TX0K)6&(5B(sf6esmoH1N4?^VHDqel{Rq5*LlF`vosi>%sy?ggc zNlA$tjq*i@{NB<67QlnL6nof0LE3zj{gY~YShYRzKR}}txzA8W;r=G$F=Pl}2w(_c z2w(`{9sS%r&2^NAoSX@p(+PUYqc5+LBBG+8s0ey{Cx}jGC#dm7F5gtKN8YH@=;$a* zmMqb_J$337B7)s+$80vMASs&S;$r&y`*AoN*laeA9Xm!uL_`2LK@ZCFcbC1SM8;Ig zqrcpU2PrBBk*X>g@9&r2$Hhs!F23k@I^R^WN8YH@_3PK=^Upt@WZ$)Gm-gM-+A3GB zT#=-tBnb@-m6Iw-vREtu-UL<3bFHmSc7jMykjRM>GCVjar^?D?osSrwU%S4ChvlD@ zl@bHe3NomRFLL<^F!m@vKVQbi#v~;r#m&BB#|{Bxzeg(%y zX#RYf%w|YV#@W!oES2(v`lzD?(OkVsTxu$t5)+97!m6sMa>W;^zCdG-Y&ILmk00l~ z_uljB&&6#iXV$(HnV`j;)E zQl&h8Js`gRn(q@5_;X<)kvn&C-D+jX6{w*43JVL_xpOC0tCi>}f*4%MWHK=@Fu*(S zyu-nR2bn#4Hh1paaq~N!cmF*XE?m$y)U>p;nF-(*U2GOp^S%2lz#KyssZvvxW#@yV`m9qD8!~^SO>V z@GthrCBVqY2>JQ>?Ay1`Z3zTQl7jptKT}d3U!(}A$jjq=SQzNyi@J0^e`All**^K? z6MA}j-1d*@NRt2efIujZFH)q&7u|6ZzyPj=||jQb6~BKq!wdQuGHk)+ZQb0mxpEJ3601gp9|KA%G!(A%G!(A%G#k{Yw7< XI{9)+v7d3o00000NkvXXu0mjfB3dg_ literal 0 HcmV?d00001 diff --git a/content/textures/ui/ui_btn_newgame.png b/content/textures/ui/ui_btn_newgame.png new file mode 100644 index 0000000000000000000000000000000000000000..80fdfe0012ac10ecb3b5446998d19812b7a40625 GIT binary patch literal 7558 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 literal 0 HcmV?d00001 diff --git a/content/textures/ui/ui_btn_quit.png b/content/textures/ui/ui_btn_quit.png new file mode 100644 index 0000000000000000000000000000000000000000..464ff1ceab8d9b6754c2f90cd9f9dc3e99aae02b GIT binary patch literal 6812 zcmV;N8e`>&P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D8Zk*kK~#8N?VD@N zuUApUcTa(qQfRrAmKLzBTqJ=4MU9c9#&|&#uSiT3V`2;%BSiRs1~tG3Z^3{D6G&7N z6eL2e2!Vi-0u2^Wu;C8HmeQ79DHVF5mvib`>o+sc^PYkq^$U~zpJ!&xn%R5L-p~Ji z-%~i|p7m|{|CAiSzdmT)bIg+Eamery8A1SCOZKj`XltVPtRvEg8kdfZC{&4^OQKU6 z#I{yQ)wGg(ItwtBjqI|VqL0#dp(@+Cgp4Fqc1QI9lq<2FCTwo(eO7HPxrYx{DhQJPeQ$>|TdN8Ux#=VltvDN)r_d z?pzYb2*>e>Y9-P7RPf+L*7X5>_HRiITkrR8&VLqiX77BL^U1 zj?zJn*>!LX*GfZmM<|MzlQgL!Yfz~1ZLr*onlxb1f@}m35#vZ{_L)KkiHK}(4py|18?#MHi$g_xEQhDRH&1Kz{@B+911meSx5D#g;LXY8h^Qmy)nBr`NM^8?-xyeCw

    %2`6+k1{}BLp7Blrea09f@1k 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 TrJ9_5I0+B9wlnAXSHAuOLvf3d literal 0 HcmV?d00001 diff --git a/content/textures/ui/ui_cursor.png b/content/textures/ui/ui_cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..2f0793b8a3060d343d33690d2d37ed5e77f7b69e GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eP!3-p~sQ3m0DaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoB=)|u0Z<#|NlU;_*ioxkYX+g@(TtETtCGNRLj6w;1OBOz`%DHgc*nz@vj~9lUvr=~E*Fl^3Q?;R z3h2I4B9pi7#UjlOnNRKa@!s>kcYlN3!cPr9`#ac$PcZIV%k_O8&{PIbS3j3^P6 OPaintDotNet.Data, Version=5.13.8830.42291, Culture=neutral, PublicKeyToken=nullPaintDotNet.Document +isDisposedlayerswidthheight savedWithuserMetadataItemsPaintDotNet.LayerListSystem.VersionSystem.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]][]   PaintDotNet.LayerListparentArrayList+_itemsArrayList+_sizeArrayList+_versionPaintDotNet.Document  System.Version_Major_Minor_Build _Revision ~"3System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]keyvalue $exif.tag0[0] +D $exif.tag1[0] / $exif.tag2[0]7 $exif.tag3[0]7        + OPaintDotNet.Core, Version=5.13.8830.42291, Culture=neutral, PublicKeyToken=nullPaintDotNet.BitmapLayer +propertiessurfaceLayer+isDisposed Layer+width Layer+heightLayer+properties-PaintDotNet.BitmapLayer+BitmapLayerPropertiesPaintDotNet.Surface!PaintDotNet.Layer+LayerProperties      ! " # $ % & ' ( ) * + , - . / 0-PaintDotNet.BitmapLayer+BitmapLayerPropertiesblendOp&PaintDotNet.UserBlendOps+NormalBlendOp 1PaintDotNet.Surfacewidthheightstridescan0PaintDotNet.MemoryBlock 2!PaintDotNet.Layer+LayerPropertiesnameuserMetadataItemsvisible isBackgroundopacity blendModeSystem.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]][]PaintDotNet.LayerBlendMode3Layer 2 4PaintDotNet.LayerBlendModevalue__ 6  7!8Layer 5 4X" ;# <$= +Background 4%-PaintDotNet.BitmapLayer+BitmapLayerPropertiesblendOp'PaintDotNet.UserBlendOps+OverlayBlendOp @& A'BLayer 6 4( E) F*GLayer 3 4+ J, K-LLayer 7 4.-PaintDotNet.BitmapLayer+BitmapLayerPropertiesblendOp'PaintDotNet.UserBlendOps+OverlayBlendOp O/ P0QLayer 4 41&PaintDotNet.UserBlendOps+NormalBlendOp2PaintDotNet.MemoryBlocklength64 hasParentdeferred @84System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]6172@8;1<2@8@'PaintDotNet.UserBlendOps+OverlayBlendOpA2@8E1F2@8J1K2@8O@P2@8 E{ +۲^ŕt,#s$9#0̱94z_Į̜Խ=/*VZ͎Fdٺh>=Z=9۟os:{ 9xt=s|ex8 NzӓZi<}[k gg}-WXEx=[kϿ'.u={wz5q&9ӭ7`zz_M89[kMNRgwX:IO>w^“{~G9 2@fYc~xr`^1ϱ@fb|j,?1{bmX}|}[L̏/>w#Cy s>}rSP-~ "sOD쏙b1/1 wdzgu3T1A7ٞ{wKܪ󩅿6v ;0w`, cλ@f~2cs@^8 _"縟q&ph@ V1O>[ WMps5K܏Gr*ǹ\>T,:k`.Α!y.kE ?|1Θ9&{!Cǫb] Toe2"g m b~wf T/V K@!Dy3Ad v3.Xe`7Y@Sxa/1@~1@d{꽓TgбAwf`܁s7T֯]?zv gt`\淓s_~) \Ot_恪u~q&~TН#C2{v||, MW.*).*Y*7u}U׫]8:}`悉~ど[ea/eB r߭/ݙw;.؟b /~O_R/1KgnYb{c~cgw6w(e8Xe*9xwU_>WYY7;0w`܁;_dxL`' z23zջ*fV]@50e:s,@[|?L_fǙGv{U?d*Uq_g T8@+߫ @[y?vz dib}o6r*@su}1 j ߻̟)2"S?d|STP@Ls]%r;/p}90>t~S3;g@ V<Ye8n]'g`܁s87S1c})Ǚf{@+ 0e]y?*`@nvU0>^guMyȑ_^pb.w`/5T|@`n. >d2)?d3er\sTn* : wf}+]|U\j99r*ٿCSYSWu]yODΗX S3?79@d>=m6ks;?uTu/s}9|0?~zUٙ_1Pu/0s^R;B\wr5L,0w2CW@Kޏ< ) Xy{~USgp/usgU0yA*ans@|8>y?K_ f:Šn;gl^0? +L?9ob <P{y`L@:ܯRP-1Θ_'=BWey b1^pܟ+R_>`Ld;3S:5;.p88c*:i`wc2>i:@\,`~93# f&0 @߅߷2)n?V pʬ;0w`܁.p~?#Toe?!s?v^`],.V]j+'HqXec:w9N vܯW`5O@ܿ?7zf̟〘qYv]3< /9@.%H _r;.>voS 8y?S@.*ܿs_4t @eWe:jW̯ f&x`~_|r@5~=99 >g`܁sv} UW* VNU緳@.r_ܿn_eRݿv} ;3\yxd .>O`%>0>\iZ:V]`v0zU\ `dp 9@ ~D]rsU`<X .vܟ+x㬟ۧ>p' /`怎y*dz-3 Ĭ߷T,P:V=v|8ךn5sWw!]P;39@RX9A8$Av2.Ύv Q6gr3xv* f8rw@cf+\]6_NK_.: Vnw7x`{ Sߜ$u#HZU#< _™2R:0sb~| }מ]tٿ2UOq@ v,u#uwX׽"o0w`܁s~w=;3`<\?<j&s$ :\0?T@z 2d3LNܝf/eq?|羯8߇v+R};8aUy_r֏s; }+& s!U798T,s\@qV09?#s+r9`s~\e dg&}U6\y]K{\9@>`fs. f&Q;Wl0Sq'i gTN td|?Ҭ<@pppVN`YLp;s>fH0ӆUO@ٝf9u)zg2tUW-@TORLn T`7? +p6ks;pO>*` 99vy?_s<@U=`vjX`r.:i&`w9W8@:Y@p7ȝ N#Ϲ@K]6Y $y?^9 k!#8 >5w: .\{aqY;~ 9ȕU`~rW+0;BS:?D: &?cjfry}V31|R*WDƧ%'gy@ )wYw ?Κ܁s3:>RO_n+G X1?d4Oe2dާX_tܙ;ߝ, s>7ϜZ|o5 0-v+9/0w}ՙܟ3:p:`^?uY@ 1 3~C6by?䀕TS/@l vq?rM篚יO!LY*|!Zd~j~s8貁t~75a/});jo0w`܁ޏ?}$x?d<Pqߟ ;0w`rap)Gf7ua%.7uri j gos, sg/yUޯb;s;T;/Hb{)_+>s/??bp@2@.:n :Fz9ATX@8 zQ|AT8t>N&Q`ris9?dta\ *ae~9no0w`܁:uT3//H:7HEW<`=&/+V+:R'@r?vpw`Y.0ܬfu^_w:nbUn9:S^.『 .wf~j. Tߊv|Ug9N5[`Ƴ<3םr'pYug%Ȕ1λNKxvVxXs ͚܁srܯz.:œs}T3nWd:ǥͼ_0t.`Ƨb};>NUPu< \S9@%" X< 9\б@1DU]ޏy_f9pw7^0wmo'xk3\rsO9q@,9@>6Y De/>s#Xu|dUO1?d}n9A } jΟ3bg*7p}9a܁s~?{һ,@\K;Ks;^U@]/u[5 @<;:/e`~⁝Y]9\ ?0?Xy&};_g @Hbs_})x7By 3@n0aOwz=e/y$T82ܟu8?>< srRz!n`rL1 `+0?w_<7۬;0w`Ớ_g& TY@S_txWlKY Ts\s :G~sjg To \yYQ?z<*fZg@8 8b|?s真bj0Y7;0w`܁;f* ?~W ܟ֝ zf? v.o fr&K=`ddwWY*2;or$[b)יXU[u 8eLsҙ{} +'H7Xb}439_ Ly@?u{)f3#XY|wer*f7u~̹?If| *p9Uxf\2ks;0w]h"sݥ3n0O>\⃮[~?μy_bjsW3.g _?䀎vN;?< LsU8ԙ,@5ys(XL`||?;}w {y.`qNб@*יe*zUN_gÃ>`fV,&̌o.`e _|w] 35O;"t`7w::Ɨ .fb/@:.൦|f`܁s;߅ 8]x Y~Ss|?HzWj;C.?8`}a|n}\*2f)*W9)<;:>NP9@lr{Fs?sy <@}72&?[ 3A T}NY'M7Hǹ?ޯ`c'=?a様p&ȷMvq?{C}2K<S_&zp>pT@pys {rs`T'Wk&N92j)'ާK` < s@13`Weq_-_/$ecGWy@=\@,@'@s*Q]`F/;܏.8 vfr8o7[gv] :{f|wf`܁s:?. PS]`Y%HrsHfbW \yU&`5Pe; f^sc[L96_u(UN(^5'; P>^ 94OeUX8 @Cޣ,U}\r+`vع?2|\ .:Ƚ/Tv8 2c[uU| +. tsbz)Xy'713;0w`p/to7 qsOe+wUX֬ ^Tyˍ|\ ;>`r9)v:'~q/yAxc*ssS>p9Ppr8| ܿt{7RO De*yN#Ǟ_uN03^;>?:3;LPA5 Ut899\/NO1@ ty?wޙ 93@\Xq?>,`sy5s;<@~af{MN`Ms\= d.`|.SXu~=g,u}r>sߪ|.:i&`r/X@*/~ "s@r9*#sTF0qjs@@sPNwuNpN +Asb{c/y]痙yrUH:RezYO37 v:V,^Ufx 3?~w;0w`܁ts@7a&躿p_M?:~[`5; i>@/' O .p)xu *֧~2t?ګ b.r @u~&w UN[p2>};4n9)WTN`73fV>(+W.409@wlT@ >b~g2*pq8WoY7;0w`܁\h@<@5!Txd]Wu~w )r~{X_
\^4usrDu,S_>sR?dizn98S@`gtcp9`r)LPerd&XD9@$u$tYߧ|U`K/U?:s_f~]PL9@Swg r̿r_Z󫲀\=]81@*8w}9qas;)x {@ROU9@D14`vY@ fv8`₎V\@f} _.އOq? Tr9A:?p\{{>uW +sNs9? Tn'Ϋ9jb*\`] 4O?f0K 0-)&˦.`7й@v~}]9/3@ b]y?v3d,<`'8p5Ǜ?<@ Pq b} $z'Ȯ$T*w*H Ynݦ De o0w`܁s;Ox';;X{1O}S=`.t|s*DWtN{/ld9xQ_>ޯ:w,PTYj`Wޏ8`rT P-| \*x `@ Dt j_Lwv~Ts7P:>+Ty?7~ߙ 9~\u;9@`g_V9M3ks;0w`&H5#E.f2S \x\@Ԭ4)NO_=`< *~+s'w~]" T`zίJ** s,t`npN rӰT<IUYn߷'/~o O tOCyp? <̏ rL 8SOY DA?fLo;0w`܁ R9x Vߓ9@љ*Oc &G ޕ8e d| VuW9/:&יs~Oe_3U/1A*ojrw] n_<02*븀U3f:*W9@S>`AǼU\@\`'󗘠As~sp@v$X s@:< @rpV3s^`G 2s=`v 3S'XuϹA_M͚܁stC~+3Oux?lhm?N*2tya\ u0[>`<@g* X9@ @ۘzn柚_5p[?L_u;'/0s? fsXuyޟyQprsOLKoc~s0A24:  US}? 4K`s`\@\T+ܿ"3=@竖;0w`܁8KL;e8 3d~n:&ȳv~| r?0s+8prftٿ]㙀 yGM3];+@y8&x=ds⁘Sٞ*''o_v=q ;3os9ǽSu~8Npq[p?x,4KN]03y&`Z>/r뜳*f:8_ZqޯUKu~ @d>]`e.Xu};3*Y3͚܁sPOy~s}`Dއ3> :*fV"\loufuU&Pu;??\ !i`ROyA*,ٟ+ Y?g2P;2FwÞ/w=9?U/e貀o<Aq'v+Gr`8ew2 ߽p@7eb~U.;:d6 w}Y'Oy9zg/;^p~ó*w7pk 5Ao0w`܁sY@ dW1@?< e.ₕ8ey; J\eKsL`fp-dyV37wx Y)qޯ:Sx;Y@vO{N pq>>cX @ +6Tt4Vs$@<@>Os ]@|CWeSXu} /d֧`p&puZ3ֹb|;_5 ]*f*7VsL v}/zd?ܟc_rtLpc}r3ks;0wc&;T.1@S,.]WsO{/o0?u?9U&~r/?=U3u9H>*?d{)`7]7u~U%c/>9zr{8z|CToo$ey)`gbVpr<ee?/H̯>UO?q3 c~*o'0fS7u:>0Ssya&`o >w`܁)޷ag.\Q&*pw<ܫfv~Lܿ $qN̏O_N0ϯC/uKLwx?u.ubur.Woa8r+/0@~l *w.7ޏNXVyld?Hqt v2]W99Xu];®_u8)r0r캾Uo r d/pr?sWmwf`܁s8Wy dާX#cƧaO9?ع@Duz =`U,{H܏y〮d Rus3;N&*WyA*Xr@L=`\'>`7бAf~Q1@To}WٿN71?Tx]1??.b| ara70u<0y@v_g`5O4UW.&xAVo=Sv,)*܏K58 o s;&槲) \W7XU;5rgվ񼿎eΧ|\u+2q? L]` TKj.(Xy]z:ivD9q&~L3#@V?L@u\j_r:_7ȳ3U@u2SpS<zܟb ^s:Ec' XߑU';u.Vٿ|@>p+pU@tt= <}o0w`܁wK8*P:UgXqKqֿSo.Qs?~2<#p7\@|γW% Ys~pxwu= v[Us=`fVL&0S/nt3]>b`\d}mb}rX_~~X fK.o=`wzCe:n`><ޯso#o72`B3wD'n< _g`p_2F/9+ i`b7[EW1s5Fy?~'2c*X?TpS7>0sO1A})Y__NnL/72_o@/K4_wa`SY@F ߩ*;0w_]0?tLP1B]37t,3~*P?\9;Swgp#s; _T>Ywss fl>r( x s{n)*x 2> ;0u}y.૆ r7ey~n[]fp7bzoCu{20@~]/Han/XenSMu+p\@rw< fsIL`g`|m: L0c9# 0\̏gx&2SsNw~s8;`N*3츀Spw ??v`5eS&09K`f~O\jN0\u}U/~*%&e_ 9@TIn7A\_)<_#/ D@S5;[yN0TL@ VUN*D%OHvurɳ^`TOQ1AT.P?$nDuNPs.``\ێrر?N_e92w܁s?Kߝ;p#sXsqW_D\2/{?s/@{j#j?ԙus9JG@ +ێ^Eņh1gC̙0`7 O373%UJK1"*CK.R;w7?\ +d],kjuճ`~^VVϏr~~oo̭w`{:hWux7ܻ=2icl1~U9:lO1@_]7Vη7\tܯ/W!ĿKq1[U9~D\9 2gb!s }b^*|N0@ Z/W~̯cU!TWW.Rȸ%l,` Vև 1NcYWsa/7UO1@DǸ c}ߑ񱥲lu-*.yd?|?2&p@|s/벀*x }#xۯŸMk9`obgOA.0!&O+d\3~BY0w`܁s「VƇg*2-S!l1C_e{os?|9ܹ}]&Y y? cna1@jon)֗d(_Ƙ*2b}La- V۫<0"d :zVx`e )a4y?&) )֧9@YoO![ d=_ey>[,W「 Vrl%/c20~윱?duz|uݚޟs;XXq?d* &GD>.p7Sr@~q=)c/4}`-Χx^޳.0UX~u,`v Yl`fy?augu {Y.x95Xzic,!{c]i_VUg">?/"cg,ק Sܯ.0M,,y.22U&ü_sY{c^Nx ^ȕ=.0CY L Ow~os~ xκ.`}?%`}u2ާ`aIܷd|H8UA<2P9;r\!Yܙp3߅Y<]sߛa?< c}u0~^.x9pN0r~]f9?ngߧ Puc@L~=8`rww_V?}Wbie dp&NX ~ d`ó"Wٞ₪L< ߂`܁>w3 >VY@}`XU,QeT]r3 Tn`>;So=\b˲n ?wlP?vܿ] +ls6<@s.`t.`d<~*d15ȲYI痹oD0O +[XZ_fc@yWy@`y~Iy UDzߝXv[{|soLб@F/r;@~72GyAf܁swweTが1WϻI&P?U躿|.n ]os,*b/,\ v_sFer~o* 1c{e~Ig⓱@&?TE281xfUߙ ;_5б@ƌ;_9f.0~8ǧ`=`npAH=c5c`#T?V<~]0Ȝ Vڳ_M?6`$dy?.0>N| 31u}+|Q tK>x߮ Wߨ :?0cs9T^`cߑo*x'7P}HY`2 s@I9ȷ3p X2Bv3[s~C>r;Ys,ݦfy%l_&﫾{5c,\Oi~5s;0wcy4: y>?q? s.`Q?KG;Y].P׹f"d]`c=`ļ{f`dl6 c S>`wC*]:`v}`eOKz507I`Y*vs~mr@; PC2Gs9l_v]`:]ίb`X3uK*粀,?u?7Pq c~l..0:$zY&u ;0w`܁?6_Uu~w 5|ٿ]0wz|c&`<`1< :?rȜs.0@ e3w@ Lܿ}/f0#0U7:Bb@e dnX w.YLr־/dTj832OCy;3 y?)!ów0 f%3f<t c>`q3;֧`pe]'Xe1Șo 7w܁s=X?= un c¸ @e 첁,:s75XEjYex`F@,Ș:_o tycr>`6 q@ VY@R p/ b@5q< v 9A dw}8z22XP@8+qALXwӸ?;w.R? T<+|g9.wy?6돝߯,Y0w`܁s=sy@3f[^aN`ijf x8 |8O|̟;S9u=`DL\.1A6@5 yAT,sX|[..tdq@7 ;8;'}es {{YL ȸvsœipUՐ:Ueܯ2G=`d@Nz f2Hf0@F]eo!S^`>aO0INfVχ~*< \ <wx5s;0w_/2֧d3;@I~s]~+L)L IOqA V 8 >üfa?o Vsf=v}̯sίrwxbl. \q>d s~\ =#q+A~4 ܙ?;~v'$Y*P9_7OyAHUozߑ t+S Pݿw؟" t?w`܁: !e?6 <3 dws]`\ϱx`}|2Wϟޜi/<`e~fd^ |P1lvT?^} Zs2ޗ0?קu,TeWz,=@r똟d,P}ޯd:9@_tl@z9@Cx3< 0O7O1@FrŜ)8\je\@9HS+?ty|Y0w`܁sNv޻I 8O| 8e;2u& scW::,t_< 2r2'0,򁨜'*> V}_e`+H ~: ~b,bV3bޏq@eOyu3::cey?Tv}]y<@d2:0z@*c v]C'0c*9kX?\.<*s]`\K}L>{,@e}ߤrߋ0d$8@X-Xgם9v3] v}?w`܁oe D:*/#c 8bl~c{3X78Y,]e{5ǘs(wrf}&?;zI׹_!_v,5N v`Mg&$/0rA\/t.`] Sr8 +oCXXYv]d QY vU9@T'_s1>L} ~1/q8` QnjY@fL T_:&uၝceu>Yߴܹ^K}]x\g쿔y 8б@6eY.wyYcPuaT9@Iu.bzl `7su~>c?>pV,йs c.YO^ 1?t|D/d?.N`>j& b7X9;om?w`܁~U2=v3Up7XwT;0V<osY1s ct3;1AM _8Ac3: zg]*rt@L@M3.u]`*r t>`ܟc~r2\zIsr-f2׹@cl~X`X`N l |X` +8#` 1XaoWT| c.=Y&P1 I/f,`Κs;po*Ǿ?vƼln0@F¼*.:j6`u<<{s `D?;5 %3˸ 9]u@7}Y?\7R g&| R}`V F֗)H\f:<3k{v P9?\,ż,9@0sc8cߤ Ut_\_7v]`=_Tuq` Nݿ 8{]'Cvo?w`܁L{V`!Kg@Dw_T:x< {V<.Xg&Y@>g ؟c*i《`~:o*w},_*uw}`u`S^~$A-q21? @l. 2A9uΟ_vY.!cz@,WϘW@TyrΘ] *G=܏9/s/Vq??f몬NvN`|w3 e2 0,`܁sN?W .K}yS>qdl(~02~*Dtl{*ع@l?̟ `K f9@eK2]$ovvX7~j.o.{Wdc,xc5 k~L s(WjQ𜱿1Fu,^?p;XR}I T fy ln%c.` L<:sFs;poܹ@T.X^e@>0v~np]< "﫳Y7RA +\[|祉|I[ \ cc~`YwTNfK890sc׫Cfv3䀻sdPu~Y0],Ș ?5 P@5b+c?~HO?5\`b/u YOe> vuwf`g, wc#瘟;u=g,r ]X\@~,;uoռcx;0w`܁a7v P?<`3+@Df=߄).x?Ծ:fg69 kx1zκlF벁nr3C[56 じ d̏eT?f_2 t|o.Ș_uc3J:s_"f"Ĝ lf:b*2v+ zVٿn& qNV/uc*~ D:?w`܁'/{)2˾=$r"S>F.Xog7<Թr/C4rr t`a@g'z S}Kgj2yfߔg.02Axs{ + UqufU.qvu~ dp_o2u_c7y_f_6 2A6ϱ@:l70f,@{a1@$ YEWu12q0w`܁s X o e];Np2'[5Ss\&1s3SXs~53,@=:1@w,10 $`Fw1c<p@L ,zOYu.L@7UPq:珹@? 9q.`}Ǚ \^v>\ [~:x!t]`asU3~]q?7/X \fbsCqq?Q?FWss?lY x_=:֧܁sN,w_zv}$:G 2+/>`bOA2fbp'Nn2@p?t07HE>XgCK\ u_*ս`p?6P@Xc eXu3eTN\+~oTWT1@Q^dc,17ޏvNC= ,ǘ t%NuV~dY[)s[`}`Wާ2?.!N`_>pw/uo:Oo8ps88s7`7]_u^.W= F@5-UnuXXߕϐճY'e$#\;@60#dy v\@I)?pc:nr?te,@n;bu\ ; ]3~ί~]T?O/0s]2j 1>v |Xp@U=`p@~ tKy?~8vxx_ac,輿? 2]XqU}_ t ijo?w`܁{sS@v0f!<. `UPATo~_|Y3s( v7 )&8`*Sn.f":~,0A8 'N`e`\gIu*0ܿU3WdB`#r=M.o,`NP0|lXr;$w@oU~]Ϛs;4'0d] e[u@sH{" $0qr,q@gbsL`f`3t6:*`v}`//2. $#C087HQ݆b/' g:7y۝v޻NpP \Y@uN`~9?Xϗ3'0@ o2s>0s ?V?5 ǰ x_a>d&c.ȲYFt LLo~~;0w`,w_3й@ܿXIrࣆ @ IX1' |53u\j_>v00G7:;gsq_U^4Vf?Ay|ovl@\o/}5 s}33P@0 عb4=T^`V`sog:\9x~bTX&su@ f=`V]1Kver}`T/;v@5?w`܁'ec 9@܏y^d9/fج@\oSN?f>e+_e4h]vdCPe r?*[|o:/xicc|s3z$ #f]g0,ewua7-ƙdd}j/N @Ƽ_F ?d~̟syC&Y?7Ϲ?,@̟b 3p8ps8<\a/l@C.XY_SΏ!L@*S3>~|_d)WYr/T?FSOq@6i3:@|^v[+$ȼ  +^2ߝce @d} ^0z? =FW>c@u! yYp V_*9rkAc, |<@ܹ+S.`|g49?7ۆ10fe'w @dgc; 9'}lF`E}uDc[~;0w`9b X S^`7;?d7`7 r]ejO}Wsb'T>`N*f?oxXC&R 2@0rA^ ;9N`5 I~_ @Tn-}sຘ m@X/s;vL@a׉x?e]`fyU@̿  )ȸ̏)_GDe:=` ?e.]'@&_6pr;0w`^egas؟rc}]Wj>`I\]`kn ߡ`V PCuUvSo]D9?@a>w_e}`_T?5 Nfvs1xߡ^⾛< ~1T7:@R'c*eY80q3WfT78]IW/Hf@t 2/:@<@L tv>`t0^dt< *,6q?Un.`/a;܁sN'Փ1Ac]3 ?7z_3dgOn 2G:< _]| N=l_e{6:J8^ X |@u:.f0U9A}j6] ;]/@/~ss+s,3]p&d.K= ]=]Y_`=a`Yn6r#Sa֏}77&._. rHYd:g$狀 d<7܁s[a9@Y@t 9Tn`}0~ |ٿ$]/w?9_V?e_eyv} \ S=`t31u3n6 .8`:U9>Q@,`NfL%Y@6F쏱@Yg2sI'LL n:l6Z*̯$=`@57[~*M< lXpT^5&"\g*9rzGW~k `܁su1@Mr?@ 8 2.#8v>u usf0zYX}Lg|;?`,P9ȸr]> j_fsϘ9@X~_c˾!c`cs "d`_&.`c,8@XP2;fSrycu:*/כN`u ?QYWH.:,s9c\$wDF0L9ふY@};0w`܁iw`S @{q3p?"d`g5=O)z^e:/X_e?VO+c;Ǭ?;0w`܁? `\>0d/+#p:j {Vd| 0Ad{*]p#S`6K p^K*>f`YO@`}`+:ݪ_}ccZl : q? 5w @c9@P+K8+1q8?,f*8z y@}_D@s/ȼ1!.oLk_2@d~?c}nEpƌ―)9w S ds;d~@չ@v j:rsX?7lxo7OqAd.&Ԟ0| 8u「bϽ# td;'*'d@3V1[O9`AX#D旸2]<DqX/7 Pq@P7t#{x?n}Wd[sl k|SgY@9vSO~+02?vY@u3qtsr, =;^g܁s YFK9U7._e} xSG@5/:繹_տQ|O]8v`50u$/0,9@61?Ps[pfYAXTypc`r?⁌:G*Sy.#d;02Ag&_SS$`aY@!cp@6?:pUU9_E\,u9@` u̯cyU0)0cjk*?'ϝ1)*e :?CNX@}O~c,2> ܟcc.G2q@qt}`UY@u\8 u~ Vtc\fc;@t%T?%.`:'/g 2& I*x r SΏn >50 @~,8;r#vYǂ̟I"t y@,9 :ǹ`_t̏us1c^<%/7Nº.1 f:X{? F&Xy%Tp*70o~l.`],yN緛x_= vUWs1 q<+#֌bIr +YUl: H]*J (=+by[fc.t{`<^v|/Efz:ç;o4>y|qߕᅲsu_?߆]=y>߽z߻qs=Nt>(C}z>SQ~sWIΟ噾{ĜO᳞]w>/|u"]:-zq: ϓ%<_}z>|O;w8wzw^?o+zn7{SOoY!y}|_Wg}KSzw뽞{¹Ʃz 7>yg=?Xճݺƹ=9O:g=8y|ҩXyR귧w}^'򼾯g7N}<+l^>?/wgw^z݋>`~:(t~yW𙞯K}z^u~S+3ߚ;='=s֧>*$&O[ :X9b}~c֩܏:WYzo>rTG<ַ^b~+~*[?b} ~:`sb)s 8`|:&y;Y798y߁*SX`偷5/qt<2>)t*#W;*̏w6 8} N+ Tާ+ӻoc~W/W9=ez3x AOHl+ף8 1u}O|zge3|w~ |*۫Sz8_bo}'>~TW 70+ma|6,]TXy27,c~.*ۣ;URr/~uOs)Lr+S8G!d+S2]PLܰ@z),p77~;oݻ_e~cs ;pށ9Нoo}\l1M_p[&xd Ncsrse| s#7xTl2ާ<0Ԭz9\O3\1*? ?w4܏X?#Gy;-ާMXf\5HUW3s܏r벀]pq8S2C傮K`fn~&X`|{e `{iFfC,~v 0>byI7^~]&7eS8e~?oKL Rs ;pށw3 KY)#&/u~Y@]{]` t_Mg8}K]8eҙf]z}$AP;{b~ပQ7֝2>b站]s|n'_OLP;Ir=v r?%&t_y߄v畍LW Wޫ$W_?|, e+sOߛQ~!Wwy@ջ܏_)sY@ &1t{]WrS+.c } a _4.GVX <w8ywK#G_)G? &q]Ww 2Ae~w5u)X_}R.t}[g/?_~ +`imN%=`=1 뺿[W/mReW_`2h/} s;޹ }or4uX`o^(X_ @ v>k .57rUG?&7L؟stp)2=|mv}>~+ 8AN?y;pTW j6z>ywA` ; zv5 H ޟ@e|{BÒ2=  N TXcSs}K/u R_ |a PYr@$GX_]R0] $.H ջ:@<> N{*;@(>dб?UIuw*_rhs~r?_”cs.n ?*KL?y;p]81?ed>~#obuO7w{~h\ n09'?Y%Gt9@ya~^7k/q_⁕e\ l`N?v,eirv>IX}'>倔s`` ֌$Gv7a;`t`NpN}` H/y@@rLL@u9@b~]89@ҳrX +rs^*K?f<3z9R'.wwy;pށdw<L6*R'8mϴ 8 i8m՝߸M@ei=}距 Ouj:?e t;VD`e5븟rAje\p&.K y@ v/| ;v(X\:Ⱥ$z>`}&2u ?\s&Wq~pL=`O~T;pށw ?:56' L~n v'O3d x!6  N:Np:.X9`ݿ 2qp;zv.ie8BcxK;SOv'y ]K tXˁt`L;7qPWX:/Nz]\ v$@$ pLMO i0yA*t?+f]r.Mwz;. .8R/m.ށw3GunP r@:mAY,й铜 SNm>M mj6M_xlsOnL9@bmܟ#v1>4 |]X .G܏ ߤ;qw:pߔu.\x\ i'x'ߺ 2 ?uIP. wM 1@SouS/K )< < R;{ m(W~nr2q$ǴOs78y;pށo;N6(~#\P'.`:\L%xb|t跇=9 v^}V nv;; ~BRm&n|obMR XQ7r"v`b 0mn7L r T< ߕfx{S/L{>p{: rLpv?}u߮ 9xq=y@.O2lvy@}vn`쿹98y;U&߭!`e]/r̯~t5Wlj%4=Q) HxoO `~7 >Ro L~{}(SP_Lo~B7HVΗ ny?(X{t4 _ίߺS49@qksL=OO?rTG\01?rM@qwbQ=Sח1vܿ;?hK??#(W?]/a?:H:QFopށww@__ٿ]Ϲ]:ׯc%?lu.7=\MCx`̟ny'u>s]2@MI2tw~do< %GNptoV v rބ=@L__:io z7tyqGo`_n yw= uY?e B[]OyoR-@%/H=Ww|<w:< .t$ow| _z@-[t?anpI^`LbzX)|w7뫿i.po~n3q&ޗ qKrv ]ߗ:.P_$wM\ t2_KLW d؟U6v`~ozMq?w/|S~??0/eꝺ>&?9޷x`>0m: ti+s 78y;pށ:{ r@_s@ҹ=Qixd9AOw;7 8;r?V7qPr]Py@:y;:&D3;_b} . Xٟ?Ue2ݩlPtK?~TH6]o 1ie*O;v^1O0A+pu({߷6s]OX @~._#se7u:”LyHٿ=m|_]9_i 0 L P r5+t< s 0e v_wpށwLn mMIPw9@R/e +;&rp^nRvK,u?0pz4How3Pٟ+ L.`rͿ798_]ϷI_'oO}NyN`= {E8*'9n $H_wOX Ru v]wg 0QpI;O@$'0\01? +jP e/lyuL1/~enpwpܱ@9v 2.Nee:'7\< n/e2a]:5 ~&IS\>9@n^=#=`bx=w9;%Nru}#mϚS6xGLNLS`\$x5}~u{$~O{|| <0 :2@Kٿ)x?MY ]uMz)G\m%?mհ!HwLuSXiP{'?k:7 S< ;e(q@e< 9@RX:As_j&〺v D&^^vug| ]\>PRx<gw) P?:7$/?v}nm O/09@| GNtY)d:'> yA.n>1R89'[n}s:?3uN`&~W/u;/)W19o4I'sSpv>`8#~;L`%.߇Ϳ Խ?ex@>1 r?H_q.`zL$XܟNL/&7sοy;pށ _e{]89S u,_8`en 첁)W;<_=`e|Ny 9@:dPy qA0?b|aգv;u ٿL @ %?&~ ?ߒR֏+K]SعA&`O@K\  zoC )pw ;MH『 G:T7S&P3]{AW= ]Lw t?W*{w]W;p;pށ|:m~Rk7mp=jN8?*TG_]z+@ x&K;{dN^ \ 1A\o:vKOe~'obn^) X_R|$_ `u s]v[=.y@4 ]oVs t9KS&7pM6;$ RXܟu{9rtW >0wshW(|`}[opށw(t aW;j\W`\82Ϳnp m%u9pjM_v.1-@8q_ss@s_Sg/RO9 sCX`rmjwܯ]ׁMf9_?~ _j+KLIM莘 ]/m.>294Y@ ;}r~'ޏ<K^`T7\Ű?3}]bLc}E98y}w$׹oί y'SW3iPߝ]c01tK`S 1S>8u?wB9M:c> $,h0e[)n'o~~&/;နew^`wSpz/u8HH/ms;o5X_eܝ>nuu#H̯~W/.sοy;pށ|S߁&.udwvL;U?}]/^coK|T<I.}ps^b\?ltO߄?m9He}>y@{.`Ks\]W|`~0y@}I?[`⁚l`|q' K]`w_e|Y9˫}| L{% zG}d֟~(`S\>enO3]8>M,`T֗| ~e4G``i'8y?#2wr7osοy;pށ;֒,`~d .0N3;0\/mv[(Go7_}viOs~nv$~i?r'x`e|, _*;$1]ݿ?ͿYT_xmM0|+U874ع @:B;ߥ>]9AK@̟;>ϝxnN5H\0iv.Oo}xs ;pށw۲Wߺ رdiqrR&uAY{ KAcu7N2̯?-@ 2*ͿipK;)s}_H=` VXMvy@v.2<`e|x'LNLcBy@x=op,`H6o v\ @q}Ԝc~q;.X`Pxr=$]@1%)xqۤ2 `]`{b|t2?=vjޯwld'g-@v>d| y6O.w.G>];i*,=,`fmn2t:6q?[Wy?f\0m:?pr8MjX~\O?b՞bw=6%.`L{߃؟8x_89 @SVбhrs ;pށwۼ:]O]2?)v>sٿ{ۀn 0eRߗ8`u:m@%q.`=@S`s@kƯ\3c~`t_偋非zO;SG_~;__#( _{tr@g]7'_^M;= L ?VW&/]`e#u|8dOsD_$@R7uQ.L G?u/&Xw :x=wXjxb|i#p2?`K,qA.X7e}tlN X_}TG{n d@.0mvOߓa󏞟f ?~S|u1uWsO`.>r*wi0m89;,0eU`L_O|j_^;C io9f'Gw&. vy9u_ixMO/; )G=_6k:_nz?9 CϿy;pށ|Koi][L.N=`bi0, mL`HD{qWy_OwK_njm9' LBq\SWvX unvߚ]N{<TGj?5׹]eSw`O`h'v9@rO <= s ;pށw_Qu5w.w&Y@e 8 uS&0? @M[iǷ<Re]?݃y'iA:h`>y?L.)츟N0u0m_v]c/4 z Pjϗ(o gSxZ~; uݽ-. 2@ ?m{p=L?Gy@j>woH>)s01?] *to#edzw&QoTG>9:vۀV?XY?7&eR>+KۀB}`r(KGϹAT&`%G vvL?㧍cK<EI{\0qǤf|Q6z2:]% q? v}=1@vovXY~v;t_{vۀ |]o}VGP 0qBqPu[qߔ벀_'GtYnߥwz?bL0?TGپ?4N`:G[AwA k98y;M{*K=`npOs`?ss?;.H_@*k8a0ws@VǯnN8=h]&0u)W?&Hٿ]/ B p'`?fov?}>׭?{Q60q@&s&~O_zN\&Ps%'H]ow/_LOcX^&v׳%&.粀iT>Iu[LwKvS@:b}+FvKYuO v?8qL< L^7w>=w?|g3^pN=`v,Pi*KpHN]nO?4_e~G,2, }d $Gr(K麿 Rq@s>'y> xd~sٿ;9@mּ6:.Xs~)1AM^T0ݓ 8'_tl8߯Mޏ6JK. ܿm@s9@b~b}o zOo.o!>?:Q&u~ C;$/pt# H_&\P~4Ypxy;pށo;&ﷳ \_ |ۯ&W` `87ゝL2s>`՜ PMPK 6&.`Wx@ LO_ @ޯt^K :/01?Rй?*T7'@R7]ח. ]؟{&V.:sL wzn!QY_~L6;HOs~{7p=vw=i>>c\89z2$o߫}`v9 `~>9w98&GٿtK@!eMi%G]_$< L<=}9.T'H}Nٟ>4ofkP3kP=6d%_;o9@(@⃚K`'޷rpQ\;lRPsnvn㫿_t73q `v} ].@L q@v pv{nuL9ixv@M@vo4Hiy@ 1@~~s-@ qpy;pށo[@H]8e\pLa .8`v~  ^>0.7$uS2t=q/&8Ozl]0$/|St r#y@~)ܯnM~n Ax 991A tNq> v;7K>`2\tS:x-Kr_Ruw7)_L\u4m*SN w=w?u,-_uұvl`\>$=$/Y@uw.H_2qIGL`$.8󭣹?W ̰LYo?}io+$'r?9n&p\yLrH;9@t2~9  |w \ >c'&'898t+#y`dzGNK)܏5[2opށw[~R;UG,| cr5맼8?l{ s; ?$uI>S֯~*߫,@;b~.p pr;ȓYk:prt?XTHQ7xt64X)&k2npv~8'L?t_&s .8sN\PY`r`8$N'zs]`8?<pu;9xopށw}z |?\:{Crc rv'nGw(z>sߩfys.`O=@XY ԅ{L Sp\`rNg2=l2 @; 8kn*Mߎu*ܟ,7H]o@u`?‎[#;:)Gn`gQO z]v)g 1@iW`fSp\2)<ow{\p__L{T&e) e+sz2G9ԾrA&?% %L}= 5G|O yܟR/eX} {pYO#1>\wM.`] Kw%Ȏ];H&pu(tY۫OS_B\e2>evyKz;{_Vrq<O prSM}4o)8t.v.=a ^st P3aUwhk@(ߕRpX`wٿ9A&ױ)S7tnP_enq@n1_Q8y@`8]oL_~)vo >̟S7{ `vߔsOnxvW_r;'?x+S/K:.897YK:k?&^b{k _Np 淾W淘ގ'dT:aO3<(Ky N~t\NI{vu[ϺWyqW^sS98yw~]޹ML. M@N_  *O/@%??;pn>`nPw.`>X`e}5H{븭su~s? @$~2gL`%zint x_oL;>TGqM] cK@u2?!ޓ?倝:'~$*K`vY?#XymWn:GS&xs@p:_'`~xa8y7_>A&Nع]pMX`j`b~ui79A#7sug0'= G$f ?uaϹS'X7S3g;Y0?_>?d~Ri01Av9@WG\?oXR0:H\ 0m[G 01?xf(1h/;;Y]&0u]7v:2G{̟ ?ܝfM\/O?Oz78y;pw@z>#׹wX`bLG} w~Rݛ@Q/8X]dI? ``^ &7>"`.w츀]8x'n~l`Nر?qO ;=`jMG𿗆9 ߺWSN_b&N| z mҳ>t0mMX:} Nb~6;]\ h_}Y?>~?Sx;`bpxy;pށo;p{<:0q@~$/sd0m%zwnp@&Vip1?bG8\'>z9@_ >|@D}jN`uhqMXH)K}s?: -\BbqzG?eo{—K 2\ym  $uxVrA5ر?rc`;p9@&ߺ h n{>p?i]/9)79As|z?vu_5w98yczp^=q>;V8 v_rTBvt483qB~/tTΗvj' Tu>s#0e xgPs Le~Mգ/VH`'ew7ܝ`N_?rh>WW74 X9`C;|29G6 x@^} Ix`O`eSXYc}BNu> 8]<L>axkJ{ +ٲUJA,FPQp X҈gq0k~8'2Og\]:Wϟսz: oׇ{|']9op o||߄s9Wu}w:߾>]:w={ݹW\չO\g<߁W&Xy=f}!S,YB?d >OS/l߳o >\=cηb9 ?uU1,>/aQ0@) P?f1S̏Dg1>|FsA~o ٠bk ^8 3skbuxx߇)?'r@g1 ذY@wy@f)@?9 o||NS9ݵ[ώq&p2{x?s\&~=U0) 8 ?;r\3:fU&Y?>*Cx`{)·YA|UNY_N̏ߺ8`2*o@~ؠ"L =ջc@|Vüd 1cߺ6Y.7=;槸 @q~;*&!!MԳ90{1Sr.ؠ2db} ߦ<rK<7d_(Ϫ 8_Tez D_Fw߬眿7p[\~绊>=)}` sA&peUP+Χ?~9~`~cc}`b.E%k2 2cƧ7?S> L9 }T쏟5.X_w+M=`>]Q߆ I@5__+2"rY rz)#S@k g T S5@dOݿW_`}.* [! @?|*8Rs, A|S.?LbS?K>2 M=vsOeSx#K}s|8j~1>}*t12m^9޹M@dz|>(']p8G ?̟?SL $? &ȴ zq^T׷>%Ss@~,ge&v<=7\PqhOu\ T{瘟pY 2> 2*y{?0 s@?lS'b o@ &ȴxc#7m:(_̝`d}&2>@ d#slX< z??x f 2]3p>`d}N n`UOW9op o|_o}\,ٟKN;^ΎU1@1270DNK6or4[ vtnd0 EoOԻ}M^Q?=`S׹?\&+Mk!,mܟ~i 0uUy`]&-O нk_: i0= hxb~fTpo3\|S'Xdί{2cN7>p" *_9op o|_o۳;{tq^v;\ $ds쏷>\8@n~c}?9:jeS̏s:?0_u\; O K9@i 2 ܲ?{}=&.n)z ~/3\6'bs9{I&ySOxv  ;Sn~  䜟=@ xͿ.s'??\_l]na9 o|71@Dܟ*]! ?~)s?6>`02/~U1A^|tor@6 =398 ~*:M t_{TfvN[*.Tt=`}S/X1]-@|mm]y@s9ߵ6]P1@l'8' <_x3.`TXV?6.:8^nW2\&׿'T7/m:g07p |] $SM۰?tNf)r~V,y?Nbm3p⁉^1u ër}-@D9'l F 9}S w<Ϳ;kT*.,0 ܟ|`ߣ\2 9@3Slp8>qAy;b& {?C6TOۣ| *Mm2{dR/yA<?f}چ:j*lߜٟ7yPU~N;VOqv[ * xlv;) ~ q*-dˬo:)X=rNRO)` u?}nOy?Ta)RнS_ ~R8?d|y 2wߑ<d8ATޯ P0]@|i r?侯3ru^z' xP?TaO=OYKy| UY5b~1m6K;t *NTn p_OQY?lI.{i'p7p/7vs\ 6o x`bBHL?v%D?r`; TX7<?S]ٰ\*8຾6 ώ̟1{`^xb;_܂N0;'o v<32 VOW`|M'?Tv9 r?c ~ jOe'ܟlsQ_]q'x=2Ǭ9؟w9op o`ܿU` 81M pNٿwt.~MO=wz; p?~0r, >exo=?v~]@t^t_Mz2kLN`n&9@)˂Mn{fj1>qY@l;Gs_3>s`pP]M@;\ L~Kz?L[7<⁗xoͿf7U* }]x k}I^` *S=`o&SM2&`._r<:[߭M)7T`awpb~9zVor$0? sc~s@d KQ^D# ozjН +OAL Xrvߗorq8Zp4.`^T_Dǜ=!*b}Mw;`U \\N.D?s]Sxr+~$s$x@>.=n%[1Shzq?Z/xߴ NU787p/72~2u;?u~Su @w)@);y/90< y@mp?b|%~ M&ǽ_[S" :< 3?r|op t=_c.+d&wȼL60@M_|8_b|߻*@M=1;~]2? TYUy?v޽IsO[s@r1k.?#s_>2OLr+l:8 rOLvyYrL{M&б>S_od֧ϛ)&\7?8@[O X^7Toot Mo's@̟e.2.rr~~ @?%/'10>g.r;m~F]| <.lraMw|29p opQjO{i9`s_9L_v[SP yc?STeUOeرG, ˼㷸r@`怩< v'ܾ*_\ q؝.L?[O6;.)%T=`g܏r}4ۀai477] Mx߾`]Ux&s ` V[|e·T ùA?(7'dGbIߠCdSmN=` oSTP}/< 7p o $7u}3xS}`d~2 ˸6[n,%.`S)DeױT& o0fS'x]s2eo \)Y?Q/y@nX X@Z'{@tN`::)89@\.yS>PeRN#y@&6opǹ?.\ ~W]ߩ?q,{j l^`^>IM|ݿn=mF:m9op o_R m`N`8 Dǹ@|_p~/?< n%/3 ؟~>b< T6&K`v(pb~;Y)71?vL>zߍ `w)0hCu *:MOm2t|2Ð)ǿ%ޗlp~:p@蘠| =`d p-7p |d~VN׬q@~vSN`|M@~SO^pb^bN/_|f `M<:A} `s ^Np!7p |Ys09]9=;7U[;u%r8 ؟9;ULpb) Ng;]p'x`n0f pQ-@- | :Ƨ~Pt&`LK29< ;d|#<v}x<؟~S'{ +#\\7Iǹ@D? v\7xD\;`*'i= ,`QY@fj0?S)70{? /]R0c眿7p |پf-L?dwGn2219wφ  rxs@s=3 ߸<o%;Oy@~<cSO'Hv r<+Ko o/svOuTP1@غ@q/*M.`u8&r.#}X`V[2}:3@dmO3K$ظ''b~"Sip'7?o.8qw{0i;{m{o|sy@ǹ>e.%rr@_vq@|s>HO1?Nu8rSx+ȣNqA\3O9@<u {S`L]KcQw sd>j {q_ye~r]|_qy_vNxu9`qo$@T/qi Pe xl )O_*&`UϜ?ތ xg9 AM_qΏl6S `늹?|>y{87e;6w98w-@~N.Ty?=@ `Ua?P=)2@v@ TY~SiPq)Wq;-@|vvDu~r37`f~/ =_V_ߛ6 3~.:HiNZ?}n 0y@3>|vYks?SnrUp]NGes{*G9Ȏs~S7T~\, gs3|zRHE&9?~sRlM;.9run؟b]bSO=><}= W ?9}-@p@f_s&7gaU/qX7p ppvNX r?& v]2Eb{]V_#?8_ry_ xr{[2 T]߆ _2| >+gR31@,8x8*瘟<`yu}q^9'?%Hrнg={?v6Ȕs?gVwͺ:cA&PuU&p:j/9笠q9@w/L`Uӂίt=3| *?^yoos|~;TU[i;jN0u?;Q}`k}ìoΟ~o }'s8n) 6ww$9A oLޯsmyo< -sKLP1."Qy ]`:&jM`_00@S܏^\l `L,{\7?&| *2s ovI]` :.l} LL[.p򀨫⁊5_umf\s$ȾQ߉6{}nY_n* 2?S\: :&/YL`U0u9:p\r~; 7p o7$`tna懼ﮁ̟bMgf*`De_r6p_qsY@~W쎟Sr~4YOv_S;n N8;@xL`rO@~yu}6.*~dۀV'7O{Mw2t0%y{7>pr\g; YQ{pW1@u+~nP@m9#T_;2T?{Q?t.`Dާ8MV6 Sm:7HM@\&m@\'{~b ؟S. ^@U@]uuor4[O "rXn~\pt︟qnzMyߔSY@xty@S>d7p ܮcMO1A&*c8 ?.t=`u .p;tWxf89Av:._Q%ާv697]n)HbqS'8mgd|dxߴ ~|R_ypY x|a_{tN[C.4e _b6sMiU'Xoxߴ!bP9A`bEާrp/?5|'sL7>8L_NMe6T;?Ty@>pMUWX@.#8_^s8nx`K@U)x s?UO6ϹAY}uLusdχ?wT_o r~sy| ;%n6M ^|y?v Kzrwo8?1T] 2Ko<'7e0tߔlvt<L@#ߴrM.Pe8?SFw6Ͽ97e5S]`ݿf]!u@,m6R;~p{| ᄎ| =_MY?PWrϹdײ' fcGr_q б?㼟){;yr+֗`r7^`矊_K?U?iS׽Sf Pe'tNd`V@1noD?kޯU.}pfwc)2`n;y'?We~ n)=`~8'wUխea T;\Fwvo9 o QlNpq`sj fT1) ?˙@q>.O{lbrjP_.m5<ݩw 6SObWe@6&8@~a*K)*X;/?  |Wg~+;oYK2Mmb8TϹ@X>QM`W>L`;as lvbvx?N <9op |]7*GsejPu'7L[lO&uM^ T{~ osۼ.ugu>vO Dݧo:؟r_r_fțLrߔd\/KI'7*fޯ~\ /u#X?t\PeL`v@KorLMUPe g  ~`nO_  ) N;@ȝv8r7&;ndƗ:n9 o|_o xON'y_ToLL.`}km{Sޏ92$&u>~ p*1?,*ާ>sr1`fo&x Ĺ]/u-jewS 8 +~:@8线;Q\6й9z| *x@ gߧ!󧺿S=5.9 o|w?f XTߺ&8~Mq@vC7=E0?#wSY@3,bv> L.~^ ز@aS8@Sr.8@d!ac6_cSq&2UnprP>Co} Ln >}p3Sw?/vqu8pNT̯2S]-@ဩ<]e_?0?[prpߗ;Gw*Ɇrx}NٿL=NO;78n}|eoop?vU?;'p}.rSWm* vk;q)N『,}*6tdrI{\>g6=mPu{4y'S>T0uu]2RPw~;~:\Sw˙_߇zU?ULs~  Uy@ dM/y=7=@~TXu y `bMokwS>`L7Mipa첀xs:眿7u}~wz xߝC]EprLss.|aĜv^u|h#2K] f N=`t@[GN.`Trߐbz;?dS_y?=߿bvw)8csn NeiPuϻfvo{]@qvNV :^ |9 tU@d}#?8'" )3߻tߧ. SXuX<9s||\0e `8lP1@hX] i`.粀 L&6m6ΏD^pv9!c8?P{8{r&S_sT9z;!VS@rWeZr8ms!SqRoUW[]Wor @!Ax+?R/yS+/\ \ )< s@/}| _Ror0m @߱m-fe]u'{5^d}| |o|_o`q?<wl$@8yu~[+mjL s?p?/rߎw2*ǿ5{mP1zϖl?Zrr+ǺgTSؽq^+ٟ'tv~^0_lp8Η \8)88b]Nn| %y@u`uPgǐd~;}?0?Sٿ> ݿ| C68_d! V/9B Kb? - z?s_ _2K +벭UlNqEEQn +*A+sv=2ZfcVD#7!?EfvpށsY:§>y羫u*ޭ/y篕y}嬻oY7t)V帻Yw:ʧ>w?yܭ\X\Ϗo_=Գz<:{ɫ<%s=O3wγ]sy8/z^;ӝ:?:]K>,/'{ON}g峞W*k:Χ;os=o\ܻr{=og=ot~yuy]λWg=WY'__y|s=/Lw!|ksWo;>H]8g:s=S9n}~6\}z]=zE9t~?]~:::+w>,wΝ'ݟsw8? #ާOU؟2@|ߔ.t{b_~)rAs<_w+;w:qq?X`~ `}t.c: Ho/W+$X_w,>{^xu֧|LYe9 PzOO[-G7`u,Pٟr@e{b}WO{t8ߧg9.X_e:r讲>?[/1AerUrwpށw7*< 1CoWΗ8 1@o~ReWO$X~V)TW99Gcz7a\+Kf#Gb0o.;[5>k֯r?e~m2  2:)SGr?5뗸2?| r|1Ij.p}O.HY@'ַ2'w?8?;iX+K{v9?e{OpLrI LLP3t# ;=gU8jq>_>8 ugߦ~;,P9 }NίRzW7i&;  z־/iA0&:w`b9Woex} LGOv?~?,I: o q@ .p~jO{i p- u߮SN2.'jޯfo`v .XS=@SޗSozv_e[ws=Lz;aޯUz9y;z248Sp <Twi2@so񼯁#e]/X`)y@R8q@*\,x`\wrHdw p'L~L]`v$env=`V~#&H\wυo:DŽ8ӝv~'/˃SvSSu].2~nv9q?u(`0];01@􂎰&7Ht!7H9'Hb,%@% uNHχ8y;v)l0nv:G 9I_nOCa,㗲N7Sv;]4\]38ۀiຣ ]ov]H/uNiPs/n0@{ &cqMX9KO{<0mR߷S8G79u{X 1o esL_L:LMwt_}zO^i'X3]r@L2uT֧w&AXn,_< @^.tv_ u&ׇs~0T d l:f2 1?w7? r2$_~t .:H;>9:y;)x/AbH<.bz 0?'H⁔ KS֏,s$<\UW+ >SpKn`(0 Xy q>s]viq`O7ݿIx7v.9RMx.~\}w' P;txN\Sd[CsL $[@?6)y t {ݽMu{ݹ_r8倵<t=_ ~wy8yt/V:>57q.O:?:xpMsY@ %Her?)/e__z.< n@L}uWY;;0qHN]u~)x&HuSD sLm9S~ pMX?<8/u VXr.H ~/e$q>~'pMy?M:~;p_7iP3} _b: \ P3B&$ewG??%G?N8`SW97:GoU51Admr%iP-@r&|t7~~&`..p\v{N0T< )G:@7YdrzG=`r(s,y@^ v)_LAso?~9R.0+|:/y/0Izw:7O~]%x=&G~LNwy?k߷c]x1<Tu[1u\`u]x_}>?8ynUx)L,pM.޹nb| t[ M{sK{NruMy@eB`R6p@=j)r D:&r o}u!ߎ + t@H.p +].r~F6]cN`W槞` noev?bi pqr VH  ܱ@|tN_tN?Xǃ @~nOwLO~_ls >w(}]POy;pށuznP]o̟~.ƗxnOI/0d>st/eRΏ:nP_}nP3VtlVHM`&W=iz\N9@4_N%`SLٿvV֧, uOs4 8`Q6\oyAsY8q@&7X3VW;u D__v}zG/ep$proz]q Psy/ݼN<Shw8M]y+sgb_ `9= N/eKN9?y@?(H;tĹ;f]81鼿v>L_Nvh/ e+ ~;N`w )G`oSG< i|) 8q;ȋRoH NR\*L?&G`y;=nݹ?Vwzh09@~7v|rN;or4y?_` A 8]@&Hs6[LG9 8@h/mع@8;pށw&:\;i7Sey?&{i k * @oQ/~n6@T]__!Ʒq@Nޏ>w&[]c;v/ίLr.G_# ܐSy]Aߤ%HluO?;kw?LعSn/}鎜L]`57&9oQ;\й;uSrS.%*r @XcxdNN9y;p߻]2vtr(L|>x>.>S.M<>%L?}v_l{FQ7{ビ&F t>qP_42:2$HAg}?t2K|=K[oGNT2>:6X:@\Nwi/ARx\`< y;pށ޵Nl\ nvR5 8%=C^nt}'{s_Xٟo)K5Wh0߄%ާ=.88 LN4}nPpK,Ezr^tl-@~L&G`>pQ}oQpeG9@`swb7`jz?\.}8N})粀L]_u8'%ۀ? _~ )]uD;*mN|? 8M:A=@;p~Lz8Wo/Mx``bRui0~9w=G]`<:} ~j:\'f0?~q| A'\/?q.u@v=`s9G@I0e6b{tGL [_HwI ?=@A& e5u;'Ȕ}0$2n0q@ѣ[] 7eiw?},8׾N&>i]@UGw?%n@}=9A:&/$HO9`,`LY?‮L{ $/0sav~:'?=*#Gb%SG8`>t1?\4X 쏲BϔLI/1uslr~uR9y,c9)vwnmRXN< 4{SWst'mع?Rst̯u>IAe .粀;&׹?%q>O69wX0w7 L>>p+t]_뺿nP9 ~h;sL6'ws$ȺK/'ո?3KRXy j2.:;[6 q@t Fݿ s=_Ծ t_O ~}w_ N=`&e}wu|{X?Kvb^pV!tgu:~1@7 $e) vhv1?KOb{&` <sށwbuG_b 2u;H9i'u}ww'.`Q $&Ho\;[MxfK<`<_r;~|WX`]p/yA:ob;e^4^`Q=;foY@tI~N&`e粀 7僓>q?&pN_{|$7M@zVjO{;9K9@;*L=`;q #xd~M<`&plP9$G8y;pNLY+L N/An@›GAow]`] j/7l 2 f. 1>orP/q@N;;Χ&j6@$w6u^L{)~'S&P)H^`~ | s/9A a}3qs%عA[u mv܏6j7?r8X_ ?u_bL&e}^9?;qO!6'n`b(G?]=?_;xy;pށ e)Wf.0?r@@vI[?~u $&zW2B|tcpLKL Ly@t=r@$Nܿ傴w{u.rn Pw'&`N]}< ,h P] 547RX_:_vRc'n`o L.<`uS`/yAN]Rw=`x?8M?Yfh `}'~~m_HviM.a*;v>`hF8?&?}<L_?(uP mu[. v/;L]`wr r*ԭW6{.Xߔ:pro_v` LY@RN<rnWC7$7u6/1i/XݿiP]@t> R9~#GM7.urnPs]/Cp x?;pށw&&,%pos p* u{%7pR&av`^L e+\q ݿo]Tc2a0.LKrpLwG@us I<+ ޯ~/7_dR7u cN?.v ұ@b}]:x`}?e:o:>=ukmLNkw@&@5;H{H.GϱoD~Cr$Gp ֳ~׿w8y;pS_Y8`vO ~*{`*{Sko7 ,й>zVuAoo?$ޗSW~8_r?y$d@{fs:'s)>6 9@\8c~ι%z/A>`zz@;ဎv,zw uC]`bfX~7@]Ph px?'y=O e;[zK[ x_M&8~; `CY@Hq9[g4( O=⺿.z]pq<vu=Asn`X?]' 'Hurı@S&m0A/l?:HRxo< << G`e}rnl>s/~0/qɿ-@bMqw|OS}`?]{8$ :?~ X&~w› HH}ߴE,ຯO_&]`v?0wa?8y\B. oݻoO]_P&8%ع@\u;on e&]{b~nPd&7pLR6pr:jOw]8WWs߿bv @4MyPG%V7Sx&LpG#O1a~L9@$coMsi0L, 7+:Kz P?S>kX2A|R7w uN]ߔ _e XX#޷I? ߆OsSx?8?qFomq+l$+#7H\j $ekuL[]78@;zWM 89@jߗ~ln`7>bi NiP/Xn>f_8 K`'.1| {K`} qIp1>bL|ru(fr>Ob|aUXe]Wa9@:0e:a?xH_X_R.޻ LYl^0@}O&0ew8ut*o26.p>'LO?iL~x蘟=wjo| 8uiO8'`=B~9'1e$>Wjc6\t@*SG@ sNmo(o?SfW78@`^ t8s=vw]p&eS/9A\s$[w?Wp|Vrӣ.KN27|NLO55waw)YBpxޏ6켿NX2@Oz| . BNb]2>rkVv.u._e|f2?:*z=?x1g#Gw)X{\+ҰK P3 @( \ 2#Gω!q?\;>O] Tz t0;Wtor(*KoLpe;ӡq M ?QP} ?8y;pށ;#:n ?OvIxN?>p:;]@@eqyi/m] q#L<S.6hP7'}`)؞I ; Btuٿuui 2?w_z]i> x&v+t#G,0wH`倮&_z@:r < 7 m;'7*K?ܟsh֏s̏r#6Hy@qO*sN]VY[?8y;pށKށ zq.m tٿ LpK@Ww.G?bTH9@{|c5H9@L'=ܿ%u`p L]vC8v쏲 L.KK`~$'p^e]wN&.iA`bi\ ٿ t,h\Fܿ֐oc~8skI!.WG܏|N| Luv u1WN2X)\ :u/e qAK=`R@ߤswb ps>z;7Hv_}{L I8m `I>[WwCi'XyurvS BQ6]eoo2){\|O+Q$H H/AOY:@l ) |-_X{߽6N>iO]`j7AL\R߄V~pwut}^\c8`e*%`u~l.y]p uj>Lfs\- Oy9y;p?M,`e|/H}]7X_ L=.W뺾 ?8`~tG;ttS'uLy@(_M@iPO]Yip7W;|ov/ [M6. _5W:H z߄j ;x]\ @e~l9@& Nr_zxdO:.j/Xs;?~]. ^_cq.Ԍ_~܏{H@bpXy;pށKw` {vL{Xh.`*sYq^[xoϚ/| x_ObeupNN痜 ۀ#t rIwXn= /رzs;+*{|S: L-;r3&'~*:=\7?*']`]h| T,v X}.H.o}avs `Ԭ_uO9 m,`)NX9y;p;Pp>p{:$rn PeU>t t p<2@Ww/y t=S24xxaB8y&oB–ܟ{Co}OM;9 ܟf4G}` c~d W3LΏ;3q8/pzLzޯ%8q;?Hvy@&ܯO_L?4Okq@r;01;Se]p_&7=iOW/q^!pz<9nd-@eTWzr8@ x\6r?$8=SUG9@];pæX{2sX;z^ٟ>$q@%G.8ykחVH/q,P#W_b\>v` ;Nr 7< f@bIy?t__ a 6L}Iw ]biP{X_M= p18L5wO>'Oe5GQ68/q/>q>??0u:H `i?8yn" T6X~၎N~ )[O D;ztyzw]}&v;ipvm <;zN*߉i0 []Wsχ>tWߏsSOwx_?l;%>:; rGN#:; @`vL0u~9;p?]'}Q&0mK57NLvkm*@&G/>:6ߎn8yt\ cLLT @ߺTG.`kL@U֗!vg@:p?rߤLv|=Ok_:`jp%4/@R81@w8@:NP_ N@o9y;0i6/s{$wOQ+{@* tw]9.rs{6)@c;i0.?K_b;QXw\.7q#y@| m:G ^Y@\F&r(#Gp;ߟnz@ @;ޗ| f]wW7?o]@v>`u&7e]NtL P>(#*^i&бN| r]w%HbueiP=kyA:70>P&| 8y;0#n?. Ww7ΏI/@N p>>r?T7yy?x.ho 8 o6R'M@NrHglp$y@wUm9؟c2>'G^$9 o2̟V?P0ݿiol~i޹ <#ωݧ? -wx]Io7 ewSx<sށw @ \ u=`$7p=L Y3i/y|u<~}ݿIx_s  j>1?Oݿ_GL_\X !~cF2A&>HY@'=`iбsLM79+6]Pi#б? @b;н@sG.+#25WN9_ > i81]&8uLs x~&&`W3*Tz'x8aA8y9c\2?& L 0+s^`8 m*_,~n8.T6!벀@'m I0y?sY@u?#?2INu\2[end5H]_rjuo 8v|#7s~Lg2?؟vi/np:pv>`TGϝj0mv\wK;n 1.s?\dO?7>`w>OM7aHSX~4r t>pr?8y;q \rX':SݿI}_p/uL.Q&Ӽ{$0o]#8첁8>+s|Px\<?wnӝ*ӞӾ{W{.L`s9.0y벀s7<~~O˰ mt'w >@L`q;>>?6^~OyOLƯz lO9I&:A f0~5׹:nO;wiyA* $;5X_L 6RO5C Nw. x7zM}ݿc8`WGr?~a0}>w?D~ , @6_  Cq?< 벀 )ܟ2tlv_s.b}o6>ù@xw s_b~ܿ2Y~Y;nyݧ>wNIwK`7-K{2?̯sNOM2 X @<;/C|8yw[wrN7_gnx Q&17_R8:ss_ h23,`Xߴ;-@ߖC7.awv;>v[ rI81toQG<01>OFLx?539?\w#wN.H,PN,zVΗ֟rM7&9y;pށ@Gk:عui/~\r?jx `T9?wv'~z2၎>dֿOO};_|b 2q̟2u}\.tL_?e N 01?M@8@\pv^% ǹ@y&74H.9@^mX9A%7 | ;C~O6Ko/*#HB^벀)S_}ֽ のu,`e~> _79y;?܏z]@L_K9@5wS>.X94miN0mݴ :l[CS8_ oO x@*nL&G_⁚t/yw7v;ဝ~L`zc)e$pܿz &'Ȕv?x\ uSu윿Y}cq#H8p@N< ֟~8d]8?kܿ&'pb~zo{>0mzXd0@?!o}?8$wq?gNQP]ߺ̟:'Hz)ݿM;b]O= ;wx~L8v~.~?oCsAb3~N %N]ȄNf'_u{_T2[3zn) t]WBc 9{)t,p&/0uSr R?$Ht\ uRO;´ s'8v ~6&y4`ၩ*_@. G_]6S7@;7Squwz9;pށ;e΄N +ٲUU롪IJQA@-Ti+b17U bĚs͊y 'Cߍݧ:Q۟q>v(|v~z>y@~9ߏ_k??;<}:l\>Ow}:8q~w|5'w)x~<׻x{=9ϸg<>uހgok|6[o|{Cx>ݳw{t;5ߏ8|;9nw~=}u>k<{~;߻|1_ܟ=~d:Ol:K]exs]_쟍r=>zg<*Gݏk~(g?M펺gQ>9k~w`߁SL9 ?߽1KSb;ǟwg<`?_0 |rqN8 L^"C7xb{p?fs2{(sLp32}X~`~9߸e\4pWG=Cև2@d~g_g*&ϐ~`=>u A+ 9`b{\|쏙Եwg~? , @u> o\#So+8K\.؟b1@ 1@G9cg!Sl޸wϙgu/q:7>uO:&A.8>1Ov,Wsw`s߁׻k8i?8k{S=gl ).8?^S*ȟ2l)؟|S12@>3d}̟z~1K,T1܏Y`|Y@3O1D>xdT*?"S2|p%Oe^~0T<б> |8 w~C]: Dw9=`~0eTo<~\+r\7ucWuUPuUu]&8S9c.X +6~ Pmvq ~3[ `Kxc{g/_]q?A@~_' ];?0z3 z@]8`UuU/9@\g: {[Y`TT|~"2i {j,D^cYz;pn\s~| rO9@~@t'v~*sٿ 2A 8bx@.p tۀ)7SM ™?惉a7S&},u|0so~88G+ŦP@NޏN_S *j/9 *_U] ;nOm U&UM 2s>UO} q@Xb,ށܹ$ x} v6+ XuS.|lݿ nP< Dv+s@ 92@cU̯:;vaT 9iQ}`.`w 2S:? ;@x0eawX~(Xu9~0rAV?vtxP\&ٟo'} \ :_r?v*pW~f8y?:/yA 线dRuUPq[P?uߨܟz~x?w`#D~S8d`]L ` 2dp؟*_ *8b*۝(3_rs2s pU0e 3?v V@恎2hDžw ) 3~ Ys];G}r)gr|nﯛS asn;6] +fpP+/s`חf/;/dqZzlW1<8z {DW9}\eu{ 0m_v3 8?f7pT`wLp9s2T_&cT{Uu] ~qr p6D yU倏5?fz/@f}].vrrkVYY@f|e7g.`″-b~i2 Ru9UN`F8`u3E^|csn,*w187q81X8xdBp"wٰ@u'+?׍n^pE&uP2LooSNts.:j/~ճߚr7Xz;p;*Ƨsg&]߷b)uof9|>@r H uWٿ| <رg ` |N?9rcH8૓>`^yS7y!r. XuW_ =_.7#?H@f9_rt_'j9]:@, m9Ϳ<N.pvΏ9OH:/e]*X1fooϢr̟L ?uUV/ATU~'p_ڸTߙ:;u8pu^`v*pgop'X96Xe9痼*O9+@S1\ |?U T9\ .`:irPvTdߚ밿k@sϪ̟b.;_L=`v]Oe q/A N[z|22-Xz;pilV 1AU^%6/*貀l*6T2@Q%|N`ߣݿ⁕w&8w'2L{}j p )r8v3@ T`\w ?]vwϸ[kv~U@T]_P01A~:v;[ s 0ooW~.|nOq?tt.@~9W*.*8~.=`؟'pjPu8@C9YgXzxg %Ƚ{d} /0>tn<~ΟV}_/TOq=ί<9V\|8XG]Xq@|:{:&q;GL?UTOy;K6].I*C*r.= |.0;{5vv<_7 _ sb~*z) M@jf.9r~]Q}P;<0~;.o\{x}#u_bc&NR8aȝ_ X_C62uoށcbqt:ٿ wz|xr:.惊VoG&;qN6;@uX.pVNM/@߳&2k >KMϝ\]sO@U;./w|xs? ܏?<`~*s0\ gU79"S=aLz&y`1L_| 6;.,:gfp6M{A~hvUb*YU@~!粀Iy@v~0\gXzw!cW?ίrWw69fXeTO8`{6;jP9@ AN,Py@Y@cs@W>l@ g:~`]&q@22? xanO㫶U./^} Ŀ_|luz z]{orWgy*bq<e}:orrt|.yU@0?U>g. r̟⁕ Nqw T7A6 @Ts} d:,`2co90>Ry@*zP,#LP}~w`w`:U{.M`/o&w,~ Ln`fIeT[<;T@dH^*se's@SNDއo v+/~eS.@x~"؟{v˜36s[;Gm.s$y OjP 9@fU/u]Wp?_bN7[ θ@0;&d'ug]@ ~-w7_gX `8g6`| p<ݱ:ߎ XyuSP]W N3xsT,`j#Wuto<12@^'&x} |cC>Ƥ}sޯbX|]]gu|3`< puXj3nP:70g6zy@kSϔ$}U?F| #Xq@N'\se9i0m V6\I99A=@<`dcter߭w\rq/A ]țrB+.2{, r> V:WeSO?njn9Gs@{ u=*`^L'S= _urf3@[I t.2~:?kM V\mv8ίƳ?}w` 7Wmr wT}@t.Oq @ >2rpO }UѢ|ҸAfٟ,sߊu2 ;A r}>~~=].Az~|w2u} X3? q?l"\dx_b/N܏y?U9@d/6;Y@Hn'\ x_t[8V>G̬pW}ߊ*wSy u]1sO=C槸vr1s&.uoށw_Vٿvm2?u>`dx`/g\fzU7MU7o':j0A89gUU7ܟob9?\a`|/_VNW1@f\5M@~< {]ATsv?w?ÿI\[7RS}`*y6=m@ggysW?:Nu$7lO2?~Oq/S/~j/0@eb \gXznwv g:Yi>8v?U_b6Y?gr#D1r*,P9 ĄR/eA s:6\e]3*\ dPmuN ;'+u9@c >Co:7 0e 9gK븀L`1jqM/w~U>pr S]/HU89S1?期{?fo2RWwnu~wCK>fZmw]\y;o?:7XzSo?L=`LLgp03?'8?d2~yr~U>:,fPmV_T@ W:e/pϊcΧ>_`
/2 T=`6?\gE/m_ w? _b* =`e"K^2K d㬟b*| Lr3H Գj0yA#?}_2X`b}U;쏝|q:#1< */ ;ށ9#wznwD1AfU&Pq )]W@@S+.SYN's~xT`q:d Xew]r_r̟,l-@F9@\u;Y s?U8{s\qcT?] ~*p\X@{t}t{f.rnyB orl Dv \]g_0n)X̟17l*-@}jP}]ޏx*+e#y|\ nug)} tNMy?f[뺿n 3_y\?TIn ] uޏ˹xvR0n~uoށ9Rd*Lq9AT]s_'8`sU mܟb)X9A`0S*߯.@_wO]s&y{.`f;2+t.`fjvdN?8?T~Tw.k_^kdvX9@M? >p_t W~+b0xw`r@;b r/[)8:O <_z6qvON0?v@S)aVϪpb~:NN>Hod0ȟM>ty?cxz"2n/m,`r$& t;:{R6i 00⁜=@7>17 -@&r?&1Sr(2.Xy?`[1Acצb~<c v3N:?^l\3^, ;ށ$p:HU2S}`Kn7]~QuY@*Nf-@fE.S/'|ۀ. |j#s3"|g0@ t>Qj pXq@f|ߌx S s~q?W}`f̏ j| \L` ~ s?|| " tNeȽt27 ^`d밿N&Pn %/~\ xlP ur$7Hrt\QY@Md·|0Lx`VYTX95؟{K=`;:^rKL;b~)m 5.?T=`}SM@A?}jP>U*2UW1<| 9N?D*6W߾ ;ށ3t>_Zy?\9/Hvs|g 3[T.w =,z` 97u.K\Pߨ g*ߥ̟<_x`bx
_~ gOowV܏`D]Gu~U0@R._S^`ܟ9H~uAReM2>v]u?m@xcwa2?~6DSqΏޯǽvz*뗺Yz;p_%'ArUov3UTc,`: 8WG~:{.NW\'ɢ#x\$g;w:{ _1 :?:qW& `L=`䬟j/>\M]wv^]~o+We_:oM>`U P9ASNbO>u/m,곻rK(̏r_SN{f3?n~T?ߎ=Sx2k|7ݯ~w`;Y>tޏjof Pq?K?+/o\`r8ob-xrUmr9:L/99縟%'0g*'vyl`rTq۫_<f^@)wF]@e.ܿ**qwTF6й~drWe: m.p)秺Nb. wm}*?f.;/0+ jJeqO?.`cH}, _oy>DUwoY@Uou`wy_~6r+k@d~+&8s$7HTa\.P@88 ;t~5?se5=2@Tp/qT{|v*H1K8m*XU@sS~`*X9A:OԽbkc?Xus`7 輿 C v T~`|]/o_r{1T,{@~ 2u?n V Dm~:&Ϳf-@l&& \9>V"{b︲C,ށܾ@{<|nﯳ6ίS.> ^uS6P%N;Z9UO'&'l/]UY <ίtV?pr('H p8Wm"d>9@f{m_\ gL >`U3QYC`pr1ק;.0o-_]UXm2 Dq7q-b|5V;qMn4pOq 3&n̟viO'?fVny@ Ws]gXz?;<) PeRxr.`t] @:2TRtN*Tg=\`U\=LS3@yb."Te\_rS}`<]tYb;n߫_ur\e*pL]්@M Duu].Gy?QO`TUUޯrp ++| 2?%]@U<>g_kvg1X9A:ٿNefw|.63~0@\_&q_[W_YzX?7vHT_1<xL/?:,:3Sr3T_| ?l>Z;v$'u r. =7 g{dDb̟?TK? ı@:~jev@y@U@]p- e}gv:zmtn:. rvg.3fwu9@ _ab߅.2M`{V|  @{P/ aYz;ށmO1_ I/^'oro iu8Ks3?t}_ߨ`qOe]=xt3W9=`N2c.< LK <9+J,pܫ @V\U]` `S,0"K<I`}xx~?.\ b~TUO@nprx`S&v^fus q/@`/Ȍ8UO?g_3vΟwά x]YgXznw`qnvM}:< 90m+ 95W<q*=@U=`T9Hs~:0o:.8h؟d+r&!ÿUy?u>ૂU,PugU_v3㫘`b}"2@D*?c~*qTNUd=so:W9O ;ށ7tLXyϊc}s+' S* tfPorT__zL}:*`8ATXeC6؟3KN`k< SP>0?es?Y/7(Xy?^0g3dz Y?uܿOj/ϓ$uT@d dzjoto@_'?pfb/u7 m#:U/  2Qso6`<09@:[.\YwZznw`l v$y]NYS`;l?~}`v>dU8uO4zUX9Af|GgO]1Rmu. Ƚ81@e\7m_ Ne*~8=`)I8u?{n0e@{.CySd?t? v;'y7m~4GoUY@u*z8eS}`XuUX1$@ S>u9n&P9v;)RC_d_f)ȝ`r@qLo[ O ;ށ;v:?;dG;/zA:_v1s.`;@y@.. б_bv{ Dת\ug{O4y`'\ S_c}Q;'02Agp?j , s?Iܳ,b} ?f߸a}U&rt;=`/~fsxr>p{ ~_=1t׊}!8 N&9A8}_:LGxN;jp;*SoD9 jnereԩ +xbJXܟc~p?κ}@I?f)`^p::7Hy\VnbxS@2ãza'ع?T?gG v*ju't]ܟVP`}rn p_|Y1tq/?.OUrv6y//`d:&`|)xULO9@8e$7S>NwT}w 2x`b3>fQ^9bǽr&:n pv9f / ;ށ-f j#34=w7:UK>.+%HO 2^ߧYy׻uun?%:t9N[zv:+z_]_n=_n_t~XWG徾[Usp|J_Wן\z_Os}}=/뺯Z~y^S߽Gw_grO?/빞_wKnuu~u}һ f%W:owowJgN g:9kS\w\V}w~uz/Gzyr{=2Cpx^G?*:\r֣>>뾾psο7p/7SۯW}5Suy}]wWuy}ֽ<_|u:wk=TzޝYC|um}rOCoֻypx*u_zM[<zuxz7c驌OO)$b}[,WW9>p +αַ;q)#WKUם^}?​z_#8a([GKKVG27[W95{[ym`{;&H P߻c`}Zo)\lo=e]>fu@b)Tq=e~ *S.x߭osο78bzar>}I/Η9WY+#G,c}W+SƧ.a{e~ ?o7or|԰A}=Ygb}ӓ2.)`ja5W9c. H@f 9@:X߄i8 eN1`e~݋.X9{2@UWoK^Lgw1Of*d$8HOY` @}NY 0s Нiο7p]~Sw ,W`= T&4ϚԣOkP]׌O!Hc~ TwI~taWke5)d M78&?:r}t_2lPs5]Xcjw|π*d'9@N}ѻo YK6 {Lm_;Ln.8 1.H?b}m*{+ߛv_$>vOP{?t~9?V^8~,`:jOy/q{Q]u_ݭ:G?jWo/';`M4w `i<;ާWb.8u}`u_lf(op o m*/';Pܟ{s}t`}pe_H`2){z ,Prpev;'$ xS6Kٿ2ko&v ܏.x`sa.`>mn#_/@wz:ߎ dL.߄Vϝ du{1ex?m^72`僚L`(87ש]_KR`9u5GQ7+s]_R1xt߭osο7@@u,}@io&'q\Oߝ옠@:6`ьW{̯/u.p:\5 Xy_|L i 0e:|y;e}v@|!`b]c\y it/ \~JUGs,KN??| ]wpo;HL><~f9/i\n:ot_b)dh_e|2ܹ@&K~6K;̯zAf;0 ozeiҩmvW_0X{j 옟sLXln#/}; @v'O2zL /O_~!螻 A;-@l>49V)= ;麿{|3N ^@7ed&m9&z @yvu>9cS/0u) H~:IeF?:)G`8@M7nй5zy [?o;Q.P۹ :Gb } I>.$8e $ORߗ\Y@e~| ܿ?o|.n`t} Ԭus|gqw)9Aq ޗQW-Sw`~ < )|Hߴ8kع@\ޏz_ H_!G,pH?Q2@&3\ )v}=@~_jpo&{) 0Nx*TIWy`< >qLv}@{Go.֟㲀H[2nϹ8.]`~.z a\^ ;<o|Q`61?9x{Y?&p?(KqA:&xφ''0m߄?0OsA7G67Ro7,-@w?b~,0A{aP3)x_M7~syN'e3LI'(76\X뜿N3t/_u[ߕ9ps9z 8@e.z69Ix up~fs ǽW?n&u8rwg pYW~qsS9S;L?78(W9>h.p K.6D_}a*sN`p@Tq?L]םup:ȤyAhr)v`S?NnP5' x SY?:f'`ի;vH N9s q @\ p |Nav;.` >G>wL޿{A_L9yAq.6e To|%y9G~o$H e;0 o h@\ov.Ru~koU'p;O61~W9H_倚Kn R`0Ap?#mNrO]@N@$y@:R8`}W_ \r=oK.Ow$>z TG]n*$Gb4.wN_4@ a.7Ppr){.,@t[o7c)zio2es7q{\) 6w8 d < 8qO| ?ϱ@57>sο7p}.&`R) &}`_y@5$k/. H} st=qbu/el_e|u+mO~SO\ %;Cq?t[e)~9_;Lۀ.~rx'.`Mr;{f\f=`t sL[yk}7l:fKtSOwe,`~L9}dC3ܟr?}Hu'O/r>]e9n7@_MF3N.*Sܿxvޏ;`e,`O2i':Asx=?\l_ vI-@};@jt P3qA{KsR,&ܿw/9' p֭? ?U+SG,q?rx/ٿD_ &tz.m.x_}9.Xk>^' Dߛ:?.p 8a>c׼߄ NW 8s^`2S/@DY>#S8wK^3~01O@ns 7p _v)}<\v)8a @:47qONr'`g~N`W{&( V&HGzQo^9_O3=Pί{*>xA >ޯ:< 7ipvI9@R>*TX&p=.~v?⁴  u{ne 캿]7@Nj`;q_S.H_eR7q j)~]@u/m,`j0^{9x@xK SGq= ,v{; 7 O]I zz_ߴY@ +X e&;{֮>k2]t~5rO^;o*뫿O2@pY@Ll2L_v2_ /:J_ͿIw vj6K=_?L=!Wy*oLzS'p}WtM ]d=w3.; voRwInN27u'G@Kp1'/9?~zr98߀9e}u=_e}is]x uM׎r@*[_ezw Io=Ow 2q_l du~B~#LG6rO > vvVG%s+ӛ;stK0S/#K@Ozv)#Go<w.9)H.=+#H`0y?p`~t]Kv75Wp7qt]`}n#Pw> ?vy!7tY>9@:s u7K9. y@4;m:vM?\2@Q/w57(Kn 2d'5@NM7{{?*ٿ_rn:9@tpN?*@b}\f`]/u B?.01zO]K0\~iНu:@\ߗ) aOY~L?Q֯ip,u;@lM*| )8t/c o|_zzw~d Y&c~i pdnst s]_$G'>ySo1&G `b7e~es@yA^pSX3~X z;)m2@u~eLP.-@e v[8\piO} &'v`rL!^:'[pN]1AM~;.t Psn>(7u`b($p:?.0?&@>`<y?;9q,pr[^*LO=`ߧߟ o|_om}e H9\w9?&]`~R~d'}ѵvtyl|N.=8d]}xM@:|)O9`c;7eRXS&V_Y\o{~ x ^}s/w. _ +_@4粁v^9!FPY1v'Y@b\=^^ vۡ7rs`Ksߤu};zo\%L\ o:ifopO4zo`)r4LpB\^/@ܟDy4?ߎ p@s.[g`u:t ?KT%)/ts_t;]9|@;.Ht_}&eһIOsX]׹A22 L`^0uw/-@iuٿK37d^ Tַop ou\ m~u$mj'X AM<ݿH.H^u_$ <9tmdžY7Xo>Lcs92?b>e + L B.L{ui09遼`=@|uI>e~1?kйA_n[(G[*#tu_rtߔKu',&.q$t9]XsÐT5G ?=^3븼_r'&xo|D m%G>` @]N~`۽'\nN;r?uo})X߃Ipy4pvo=1`O6R.3>r.7uL:0:zs׬nNp}7p[ (s).`\2'}`9wu}ı@e P[?snN/?}/f+K9j_~L8% LY@btM( v~>!Xs|< 8d/w9 s[L{'WB $6]p z|M>`N]];8w  $v@x BY#W_p}_|[&_K]`K9@~d6_ z?o|_ѸAnk&Nwr M@fl h| A.`W~/`a $Rf)G`ₓO3]'8:7q/[9`+ tϱW̟c9Aϫn0Q&mծovҳrWu~нjO4e;8,`C>`}2Ark_:I} H_}VG `u& <&'c~H?X u}B8%A?~<ߤm:$/Ȥq_8k=S6]7:'~;?[ X`—\`$/9m5x 9Ԡ *7c;>N?Gr\ iq~i# H | Ao uqhtKC7Mu =9;/zzd'_NvޏIe\0 zѽuO;7m}<ݭ+e'\Ps7uO>`詿94 Nٿ%'c~S]CX_pNte5~H}N>zi aI;#d*@b@k60%?ptRr7wT߇s>0?0wn/mK?qh P]wz\L[c0}޾j'.eI=`9u{eupqL:8~\_bVHr.H?]6Qq@/?d Oֳsο~]@&`u,npUu' P9$z.`S m&_쯺0jovGnq]W6m:/c?倴7:ȓMP5Gs^w첀n׿9e'qI_r}$4G;s _?~`ޯ(mN+&\4ߚv[տg t p|_rz@k?eom~W $8<vySG܏:syoS0 x߭g }}G?윿 /uo:@\oW-.2?%4عAT7pRee ,p/>TH~9ޗS)@MJ?+t_N_L[] HNS_buSr?q9?{N`r}L ?>O:]X9zx뺻`:/zR/yA&m&89u?w mv}uӳ:&X5X:>w Lo~QH||;_ _tWp__[c6: L[߿ ob{_ +_r~ v#G ;jm%o9'Hlj&p]@\pVG8t.H}_Ls?%.z)y@*-@:pݾu v!Go*<븠';`)8zu/m[  +m)K ]Xw( >ߔߓX6n?>3~) tSxHGpAN]9A~2>O9B0m:Jй? RO)G Xn1Tu`u;c+K,r? VG.?zv쯻/?i eRX])/m_}Q/ : ;Gx

    pt;0e\O{]'cA? nog.`%t];:G@| \;'ptosw{nor?=P0?*9 Ow@Vp-@x@:i'@_w>&#ot>> HL`exu@L  |S~9F{ܟ:~\=}]0?8o}džQ]+ޫmj1qlϹ5ع?R K>)v>a@o|i/ywy`v>#9@u?s ͍.}W>2xd'7u_Nvd&_h.sLM7S/4D3u} o ֳ_;WuWNpO=]]8SfGMRRι=(u']0y]yX^\ۀQ uO쏘sN2 Rsl`sr$0 qOi m|8Cum $'_bztorm.x.xghe)Xx$`~t@:Ȥ_n'Pw5_v*KQ^/9@7&躿j/m:1BEqt#n;/u:L \f^ ii q@u:H)G$WN]7XwR~.Gi6r~Mn~0+m=@e&`Ku?Q8r>,9|86M@b}w{l:/f̟TƗzǰ@?b`
 $?u~rke|vu_LozRtaoSPL`rO<T.p zG.`%xqN?Q&XY_8 qv_t׮5nНtP] >p H:G}x L?2K{_rO?*s{iRr`}zAv:O{|787u!O}p_ @uWH_}x_=1uΏvi0ukﷲz<`}r?S'ev@b}Srifuvܯ%n2M@t.`t?A$/L|E/AW٠fkW eu`r8W,H~S/9Aw {8_L`&.\ i?r-@#96 dL.Yw\ ;0@Q7u+[ȧF qnН :,|_:Y_  Di9@*_s;e 2 6y'HOy$WAWw`^B8qAb8~58'`z;/)95eRw+#ǃ )56Kkwv< iP3]@V5qN.G9&]6Py_$ \+y@-iu~.޷N~ZmRA.~w~3W C)Υ<~,2?59@^=w#wX^z~XYsh}_N]M v]w'`/v.L\c]<LYך-@=knUq?\&n>p)'[(Nx`=K:HM:i9@VWY e'^`t>s_+<p oo֩?q_vu]cu?v]P_~t].8 ]=bp@rpQ.P)z gLNl>jwlD_VާK] N7&n: 9`}m&8q﫿)w?m\P\ ^i2)Gٿ*E$'0L=@g? ΐo&;Rٟ?޻obu?Ku.rW/H:<~ _H> /u?Sm9wv9|.WsRweKd>}&mu߯o@N&/9N8z^Lر@T<'9|08'Y@Rwl*WNBvv>a:>;c8A?}ipo tu}aN %~\vKW_\_ukp L\ * |=I〵<[n=@jt`}L〉V{<ٿ?hXzܿRW3HY@b@= tt:l|}ߔt,hi| ]Ao=;,`uNs;`:R `s.\ N= D]?u+q>y _L`b8_v WNA߉2 pV& tp 8g\j痜 [sԣ+> 0TGM:$W9kRrG=R?+vk8pHn й& =H#4 ytV9Ioߜv;|p T.r؟d(7$=_O@}G}_9s veR/?;G_w%7pm@xgq$/wN0À7pϧ똟s-@ m.t\ \ q ~_7~ֹQ01{ |.<%1B|]+#&v$~[tq@Rw z9f/m_Rй;~.mj*!G9g94B9@ L[o H KMΏ6.+7 .YW~w Pfp}qL{H9%Hlg/vbnߴ纾LgKn0i95 >zw L^Q6z\sb L,p=׌u$֗_ߛ&p}ym :He| V/]SfhnX{s1Av'p_rP66\`r @$fD^~<;o|5Y@zֽ ) 5?\/؟Af \;qL cdwLn_ut`HN8a]{rܱ@o=&o~8&zu:qg s Ω p=;'py~jܿϺO{ms|i8,)=`}];'He{]V'0eR'x2~\oIW=u&P72ik\W.u?o6uٿn*K8op ooW8[H_V'^Sp2Y{-6׻S.2wA)}*8dϺH, @m@`| NswudVP٠T3_tq@q/#do[:^wΆ y@ez.7uSc~FpM.nPszzdw7j/81A|v >\d ^ֻo o|M{⎺@#H#0e *sn|Nt4l&7c{}R.pu/m_b;L 9@R/WW@t>?b>:0m n )s8= @e~iO9 L?:'%ٿK= ]pavQUQ\ +鲭U뎪l@zNP@lPRlQ@jS+b|s̵+8#~+ wΫ>Fw?>|u~.y}xwx~%7'註ux~%w>}>w.Ṻ_=z_wݺOw9g~?|^?^wxGݟ;_w<suWx+gu&|x}9;|?ߜs oo`7pչCtu>-8W~^9z;]਻ϋw[Mw^w/κ@|wu/Y߭gH>E8G\ݥwu(|S=%|O'tr|g~z~>:OgwU8[wxF[=g߀uI[pŻ;ߦwϺO ݋>񨻫y_ǿsY{)`b~w|2Os9 W c T|)w Dϱ7oSloSlY~q@f~0\O?fobxn1;x "[ߑg\|sb dw.옠c&dݝߟ8uO]3 `N?<3r>}Aq@w{d~M,p=f* S\ A:nj1@f|u?gևGq{~.Ƨ>A/[ώ.)`y):ObNf~_ϱ|Cp@@~W/qCևy=~w/1@dx ?|w b1|I>~F6q3? T P? ߺGr>f~.\p)tYb*Ͻb}o?~'9,a~*Obre;s?fSOe^L@~VO2TY )) _?<p8sl2G懟a>[sC/so]r9wwY@L}UO/ y@/O@wC]q?2SYs. ȇ~*Ǚ?a22||/?f~S9@{~* &;soSSTy,PsO>]eR'?d}Ssor>0q@L<0 ,ykC{Xbz 7` N8_]c?)=`;?} "c)GqAMY1t: rOec~?U?S'3S?f}] ާ:§ |?7p~w~`M@_e槲 rw2SwmXq>{?7?u@/# )8?sp#L9@;}O,1?u"&8q>fu sL'~._"M9@NaXu/{.󇌏NsM&1> t`悘Sw*zM_|W{S7~L=-& 59@Td\~QY@>c|ǝ_ ~{ n_bら&_RPuyo|Oݧ]?lߟM٠|m2i]71s;8WO,y0mM}`w_^wonM0r&:b~ s@N.;g1@]Y@3@뙻M/"Kf~MLq??x~踠r%y?<*:߂03cև s T܏~OT9@ܟ_gf{ t.?O@SٿU>0@x uT&` e,uyr(`%7u @ db~CG9GTP>T}2.7exybxx6GN0gnop~7p~7SOeiSS^]Ρ^.YM.Tݫr6!8@\q%rj_O`}ݝ7. ؟btLpu15Om2@v {?ߔ{2~&Hs [C7X t~.N>S9b]8|SE'Xmq'=f#ܿ2\_&g,b| n0әvS7uႼ2g`~[nYs82S܏`q^2)Nx 3K.XgpÁo.[g Lsxom̙o6{iY` vx{)ob)7@0粀.0wѸM8|fn$y>-3T>Pu}'f9~[=y&q8e& r@s;nC01u=7).0[X_.p8CⲀqo;悜v\`| vUO}8- v(0ovflr} zL?oM*os~q8q?wWSw  ]O<8[]O< )SLw,3i]Yt.i7p,C\/uK?,Pu] =]`:ckgt; &/3@}''s8"8@;ݧr&0mzw@ rSW1@ ?89ADm|wp_Nr.7U{jo,q]=vw_.~'t?{\ N f<9@0;o"N`dz*7e^dxa?7p~Y< gV2npr<x& p#S`v$Hb~^s:r*Nwwp(T9e]63>0ղ 躿b}_ Ϲ@ 0@v2&8@7xt)H>b~{ߩ&-uǙ?8 OL\ _NY7.p8@Xޛ9| {*r? s>`76u}Md8es 0yQ^5> t?%'G?? \POUSlu[* dwsvor> ?u l|B8?\&= =GoV9z;oot[;;]`P*RX=+ǟ,e5;r%|0e;, g5[vr$/HI?:2CÍ_89B66` ~Xc~?RO=0?3dIfP1?|~ :p:ioLP1N{ N.`Nr"7;st:@\X1S{x=7p~7q0 d>x]Xe9:MOeT 9:NOqs)*}_`y@71Aer?9w;y?~5-t]` ?w Lyv`v{H_f+}`ms;8q#K‼n#_s8χ^l< S7ߥ _bi-@Vnpc88?&p=@rK ;S0y@>r2t~7p37>x`> c9gT<05lCwnS2):o8{dM^n¼,.5&/H\7{p@Vj0?c*ȟ`np9A8?TWe0v~'&b8ywdt]_SUδ z~p073on0s?{p2sL_s<=@V@: &{>s>@̟c < ? =`$q2|jϹv] Qۀ8ne#\6}$s`/9AO`Q{NgT6pJ+~MNSpAM_怼0ia~{xtMOe#X}*w9 s?Y@d~dSu~R/wK@I{i{&׸]9xteϔ dއqp*~b~_d7c|zYfTPq?9 ؞_oFc*zz ]_'/3t]epAtޯb* '^u_Ngww]ώT^op~7p~?9:@ ?wXs]CR؞cwsq{gs[Ma.7y];o?9ާ|{-@w88u7LWR[ Den0qKsA;]?bx)7m`r 8Vn_u]| 717mϱAXe< c7&Gu/g9 :y&`t qA?)N`q~\p486{An/S9@Ws`znٟs0k} *2.߃!7&)cw=ߔrޔ,K:< u{ _\?+;{~Y@TߔL[ `;6S~Wn}m~1#D> Ȝ/. >M u1 &ޝsg{on@ǟm *wa~Xzo}w]>1 TΏvy@v\M's3ۻ#dl-&cCz;z@xiwRwS&pj Pw.` "p}*7t` Vߦ~/ka} wv~=fR_#S`=@;9 &S'89@ߔL. Nip2S'عsc} y_{N9zqޏSXN@f.so_l&7~<^pbOpÅoT0\ }/q$:71Ak:M;s])|  oN_l< Ϳ& _NZ'\lCesSOeU1@x6 '0>7ttY?o, g3?ԝOk: D.q sώ!S_rױAT~`ǏM1@6 Tϝ[8`~ .M] L) ^ <0?tPU) +wU:~1 6(sWq>S9簞8b{mym+0n%lnN;*v=voN~a2eW8e>9?f8A&p//w.t9@4<=T 8s+Hrs`w*&8s}`1>q?.ݡ8x` l)< ?|1~OB2|uk{y~s x19@}*~ߴ 9S}|2X[2+Gx ~Qq{v < ߀'9xN`uSй?$H^nb6`K[):z:aL>~Gr XuUэ `b~ zL^x_r4o'bn0y@8?d:d'nS~`+ٿKlY_;>2?}jP1; >p5e3w)GI߉.qߔSnpۀI ͰMM]&輿|,`^M RpAN?R/R~X'&< a< no r0?K`<+] 1 rP?vߺg~x#Si/U@uϹ?KۀCYyd#z@8>''UYXMY*N_t5뛙?|FƧ\S?)ǟ$Tuw~\ 䕐no?Zw $ع9BϷq('qxrpN׌nZ'C~USOq@6&|nY@~;SXrpi>T}fP>`1<9Â :"=@i_"S9@rW/1@kvgf~; :@TmNy?T>yo}\ n0v&6xi7m)/_r0Sߦの0y@KN4*]VA" dƗ_sl6_xiwS P=%ȟSp=#ssil\2y'9?c72n`u}.fSWp@V;pXm{N}`{r`/xT>ws;$9?s^sο tOuU_'!:k|Xq@z7,^ N }0?>y?q3;~gz^|W_T prO?f}d)?9o ?>Up 21@dG0?ߴqO}y|o9&d:_3T<#|d8-@V|q&:y$7xψor Lo`0ecrm5G?\p'> ر>_cxI_aL*vU/9A d y'v~_)w %&?9Bd8'ײ?f~s@d{*7e0nn~ T@t랑#DN)p9/> wa>7p~}]z|z45Y":@&oݻ߃eO1A& = V*:i*H6`;9 T\u7p~ߧ 8e[!)懬sw~mew]?+XrL= !z@\Xa88%?pY@:3"(ׇ[lr0T_i}vKN`L/\ oT@v~p_@ߔKo2ٿx 8p>S@c2SL= < ipTXq[d`&`:wj>`bjp2v]wb~|u{F<}hx.b r8? d_r(x`M^6`j0yARs޽q{_7?S5և,T}7yئdM~08]rOxC/)?sN`&r ?g?f~̝`nr>81Sn`1Sas;SНS9@>d!~.6?s7m6NS\p)89UW9vޯ 5L; ܲusY_Tob._.0<xܟn*Ѱa~s~7Mٿ]:K{;9k * 9;5@ gt01v d/c} qY?NN`lo&bM@yZAH>` | 6?q?d|l%`v߷/%|7970wlzf9?#d 9@f~vڦb9^_.pr';SWwT|1?Pww&p/>09&Gu/2LY@r>9>L082Pl~iO3S`lY1l=M߻Logq;'K[>s;Hrs_n2qOq@Ny.2~so2TY2_ _b~j1?SXo<`b}{#޹X`0oz qd @i +pt=`\lM8y0>&O ȹ@iC&pOr;/9؟mxLOy@v's#T6=p87x4 M7>02YϘv,ľl`r*7? 2uSwS>v< Dm0AN)8@RxrS6n01@|G71udׇ~pϱo>q{jyAR@xpMO]~o.0{@\$zi`p+ax;~iYmX`El)ݿ]_I,/u)~fDSes 8}GbM[S>ȹ)}?o{A_޳81@S{nS&_4.8@a/@'#rO%"s_u6dbYqA7t{.e&'Kn`:x[X~_rem8}ջter:غǫb:8׸S!;?^LPy?|dƗX {7uNw@u.')Χx7p0ېlrn;-kw98ο d2 S:?0g/??UV=`T0y#{-<_zOv_ߔt{L\0@Zb`⁼81?d}mXm*OMb=/1@|S'?t'xS^__/2?~=)|nPm)lPwԳ7 e7m"cȽ_?.`]mcr&l@|V`.޻ù@t@ULY`88?_]`SOu DyA =ܟ>p]w>7p~rL`LNr>}_}L@p 7N/o'₎]qS?3~O]_ d1-`NF ̬/)~x[ws)7nsϊ)\ TO~4Oe&sx'cSu8m1s^`q *L[=i/O ~y?怪랑Q,P?{`:{:t96Sx<~qm[)@N9@lXS|e<簟8@⁉?oT=_paQKX})=sy@rͿ¹?[w?.`nw-g0f/~S_| xo:_-7e-zz Lb $q@i ?f~=m1ߛ > sBWwY r*;Mx:;2 togPeUs~ws pUq#t/uS?kNr$3Myfs= s>f0e, .0g_~xXb r|2t~d}뙙Wm:>Lz.?  8?sۀ*89S)7T0]&0< ?);_;?vlkߖ< g|{*/pTu)̏] 2xzvޯ `?s2imbpr6.`f}) Bq&r)vg!|t~/<2K] #DyAT9L?w{I9:[nwN fر>MvU'8eW2?vO~apxjp*89i`d|z߇"@qÀ:b~|C1Au\*֧z¼AR7'〟+u Duds7^`f-}{VSq†p f7uk30f 2 v]`*n Py]?y?qT9@ߺ1ȌOd?Ml؟~2RPmU/~%f1z~866 +7?crL.`f|&GzNY@k\_7ٿvmr~xFF< 0 re?69@N `d97p~o~*oߧpr~s0@̟rOvLb# T?~xs 0@r?e)8`#fsS. rsyR?LNpkjyn ֽls `{$?q@Q{q CHu`q7x/?Wp@P|  :mO1Nn`s`KX?DGi0dN<N ]m0A ?^P'zusP]}&旲 Xb;V 7 d671@oVy`|Oߔl]>0s;簟8[iz*2x %Sudc_RٟM@)_>Pt])qwG$:/p9:M}q?tl`l^hM/Lu}`q/q@lmP~U7yAoU10n:0n{M2d7:/?nSO/)Ǵw+ */H{s =&;p-&`v~8) q_dG]?\)6UVq@ݿ:v. C0C9c~Cms oYp! ܿ8TL>Sfֽ}+֧zw.spޢ_1vR'L+ާz`#CO))0wع;  .8TobS7Xu~Dm"K`dtM)6M P&dC0sor\_6S]Mdng p8m5!ۛ9N ۀn_tN`n6[/M9>pN,й]O#TOm6Q@ X1@|? |/Up~S7SO!D:1k<5;iP;G₊)d.؞?T@87#;g2r~ޟ3'DO8~:6 Py@\6L  $]ppϯ drOZH;_r7'?Kv8? 3Tls(.ٿo7ep_S}K;ޙ2S[91@NL.p~i?[^`u6? O1@H9@fN`}5* :H_ﰾXg;oL>Y@/u \ ~Fa}VoNw)ȹ?F旺SOuۼߴ8uo Ϳ6_ntp?]o} uor/?1[_*7^ow[|.2>2~dƇYNp2m;*?];*>' f? v ^\o|]L7womv9re\]N.<  rpz^0s@D6ܿznPT֏3 t^`2T[ŻO8_~;?fی? q_fnmrP\ {? e?}#\2 o~%mg/a*YsXmF֧wާs{X` .`v. r w;t`M@+9? ϯk}k;^u>` W=O;s o p7u]St~iobr~nOY]w^}9@r*_o2S?f{8U{6S&&81Gprp?l7%އ)* w#e? ,82|j?X1@Aۀ;_SorpB~Ի~~b~ TS8.8A|/s>f}~;yA8 n&5yLϹ;d;6z r>T/1n\ mewW?\G" rY/919o7S]`ܿ|i9A&/2 @x'%]`xFu+8 z0N/? ˼~ }#׬z'H?n_8T[0A 䌟)63;,0m%'pK@m  l\>|>0- 䕍,ˆ96 N_x 3vIi0d 8e'X)YQLPA{_~/0)yrn;om: \ :&pb}| \ 2rn|;>w˟ o>bȝ;?_uJ|*  .Oq> 偻  K>zr'8'%  s.*0맶z2ed}Aع I[NU]`悉QdXq):N[1̑]S +ٲvUmRJP@QN*u(X `f>ysbG1G}6.f|koswC{o]Ṟʧ>S>.Ϻ[ݟ_:u +\r.zv߯IϟOݺѩ߯z~ys[y}_y7p~˹A>s:76&,[ݹ|s[snϋsywg=wʡsk|]z<7tiν:u7z^盗U| ٝow3.|Cw߻/O^~>QݓrO]>_|zG<}{~Vݹ`y~qoNe~;b>pݩO]e~quNyゕ)}+>-lr)y7pYާL0Mw6zw&$& ~v{`q@+w|tGK~pu *g}*ӿYL>/}[_};{}=쯾+ސN}+=e}Co1wbǀ9Xٟ2މuG>z qIK,xrA~)TwsLP―My{B[lwx 1AUާ̏N8kQƀ!_}N/<01g@})sox߻S?) _b~+#8tٱ?.H,G.G܏.79_)CwI>U<8Jo1@s%Y@ܰ?b[o;倎r?~r#Y9@9`#x)R/?~KzN3 jOKOywU~nq<?5>ԜmrUO]_ ]ouy?bO?&X`Kr@w_L Pf *#\&2{=1IPsXٟ#ӌ_},>S&e(G;Gw-G)Ws *0~ s؟&5b8`u,p&@} e)x:&H@z5NOjX~, @btwk"so`o8&Xe(S֗X e vgas`\NpRg0{B7e~NԾ|s黝oR6rS|6_w]`.r7/)SzzFx4uy@}2G9Ӧe{j'ROs~ݻ2IY ֳ~K@e+t) Hw5G9@~ _R:x*K,)1uWy_R\v'=iWWuSv{iclXv(IN:.X9v ;3':W hOlv+Lui Py:o*$r]t01_'`LUzU v/m9?bN8$ ؟vsL=`~Nx[?R o뺿}6X`s,PY_ٿ}@_s; & mRpiOf(}W_sw[*s\{S{jϷ)d&`;9@?wOt~z^5G, *`'/9A&.NG>0y@jWO}Ry_}_?wwiX=?=8op?-@w?iuiOS747ty.7$&8&H@:@(xp8G~w?bu p$HY@j'Xy.p]7X.8P'?t_H9N T7|jj)I H??lV踟n곲?v uIo€.0qw}dt9A&n%~Fxkz Ny@Oe}M}i0IW〕sP6#i ٿiFP7txzANx=߀L@;e~v<0?Gc}.Gw\`K]wn}d/yAo8uw;at;N^)-<j毲 #2 mV/t@2.e&`?/pMG u~5_r'؟t`IO!؟W\xCާզL 0m _e| ]? +_tL.tt]GeW{dO ޯc-@e~D9wS/HG79+$cswt=?]\k9 ob7o?{G]{K/1 9ia`SWYn~#0. 91Rr>z}׽=|_e[_& ; ؟: M=;c`.`}~jϹ?,`rw>`b)&`bs z쯾C0A\`{27+s{8:)QG?~Ԭ^0΁ずKYi8m=@%&H $Y@: tG9@VxBNu9T|uw'?Q<8Lu pv;>e&9@nSo ll:s$qݑ1?|)x Hw첀}3o2i!)7No:-@$VW^ X?{d2O9~i+{* SN^e\&P_O;wY?}ۀ)N'ȵ9 ?K^V1q` r#.KO]Y@59Br@ H0u5\Gޡ$#?{Ki~\_ @+V&Xs}n0@ L]G-@UXyNe*$d( H\/I{ _;& ܏.r| \9`,_׾ot];.n|.mj~)) t;̏8.x ߀c} nGu'r_zv_e1ݿ/y;'C/;׭ u\ .p=?e~5Gw=H])| +@s$wq_>r@.L`9 | &.4,~RSsQ&Hob>n y@\&0q@#ܧ?>u{ni Rs87.>;0u+#W9>?. u?s~,PsNR$Gpb{NppYuO|o-@z/}5؟T8׃L`I9@  d]@N L~]>w< $Hl e58@!XW=v[d tLY{9y7*\7:/2@wn '~wk7?%. u+ۛt~;/ȴK=6A X z~@Π`Tkt]``w,Pt*=i ?:p},s`w:v}S'x&p}O[ |lu;U._*q?]2?eC޷ޝ>N7:'r? ]@:K@]7؟l9:0?X]:Hώ,`9@*ӛN]_W7VGq>}?v}`Ln& ~Cdxߺ?;q;W`5nP >q r. G]`NBMOY k׹A{M@' 8 @&?b랺oVaƯ}'k7? `>]60?( .vرw^{fq |>swSX9>w&=`r`} z_}^K`ru_}O?ba z]O v?t?vw؟\d?L}?.L] D?Svn \+۫]8*w90='N. Dyq:A_&6' \Gn'܏ `rh7m}h ^nD`:w7~p`zO]P{ *@ xT貁{.u;N`t?";7Ot7_eXCXwj1[VO$ 1?w ~'9Bod'.~Pw߀) tlP;N9 .0 @e~n0{w}_@nr.=@倏5 0) HY$ H;ty!f/XM6'_ߔ|qv5z&ۀ;@O6]:.ٿRoz(2@`r(\v}lph /e\fufr(wz:H9A.HpU \_K f,Psm=3w9y7*K]7 $G9K@O48$ F2>S0?b}&`o|z*g~L)zop'r) ~`e6:K`r*t?s߮ܯ%@R>Pp9p?6;.wvi'X9_W8*s=d \'&z 8/-@蓞 9@*SHyVmL , 8 RX>L`#/Hwٿ_~]Pycй@_X)WLn 9YG_wr;2 +#LmlRxu sR/m*t.`$pgB/ 8~(G,@w0^.׹?s`\W~i]A47U8r@8B:8qOi}er s` <0@w9_ w쏶i/CO;©[< YK^n2_6~ B_RXw5Y<*ttDY`}V0@zz.pi.pH@YSkW/Oi/p{w @AN`g9 iso~.pu.st 8u_bi4 u5GO 캿|H~wt+Lc};[.տQGnxA LNG\P_b~np>8SfP{.z'uw`&6R=;[Nz]^L 0@^d ,;torO`倓,v6]8u}iWP?&ޗ'`L_C= L.>.^~S&x S+s@zAhй'}`L 7.ro |~sh8q?b L=`僩{& +=1t?o=[?SG9@&=8&x_O.G~̟〝 cߎ~4_偓 1-@^v=:t?}Lv >I\:GM|`L, | 7 2. \6]L{[8ီ;e)G 0@:Wܟ? 5X_eHnSwvgud? N[;?dU]`=]oO6X34vid~מU78?s/RO7wpM04Hnr|n7 _et𿻂#8ukH}v] ]79'@^r{9'?#q՞0@:+ 0'ߴN[tnf(v+$ޗ& ?RD9nv4X`}v_$m%Pױ.y@ 5 $2_t^G.I0u 8w线zxL\ߎXOTo|)8 `>dr&` XY &Y@tO}@&7a< T'c,P': J\P\8]iO].X`L`MR.znp}58'~;G5xL1@eGrTw ;)X 8u~orkoN}z9}c}y ܹ@sϹ@?v.`@\SΧ><La0q?e~).,5}@v<ߵ9 ?l`~:@6`I]`^O7hOy`}3wtK_$xvS~]e:z`v `}wKۀ qjSw&[}BO{RXSo, #=% P_rl:B 09AUz *>SGy@ݿ7y`;GenpQp }M< 5H)L@ 2#eNps's_u^.O<@eymv| olIx;qLv;*#7[L;gN 2M?> `bLϝ,`qu.s> ,vMw8PAt_{.q c_:WH r.\;nk؟8djv5망_N$<ر@t;;H?̟n~38.ෆs`t@}]0@$A?e~ܯ@ H.;wv܏z/K4?t? 3⸟~7V2G\>g֧R'%|%j;ǫ_ p7V`~R So.W@= B,v{s_r8e>1?N:w??{e}L dx?z&؟v~|rOu7Hw{C`wkptQx)xpP3w;[șpxqLw2C7.qN,MvgCmQWKN~)8%2 Vy; < ,|{\`Py@*;qrLί2A,stw\ k$}ǛuY@QױQX{n󛺿$u=NNuWSG>߇v{&_~ \#Mvn`~u؟r?e>^,<8Sٿ-@ty@:Su\]@Bۀu{9m m=@{K.$ ?~nov`=X ݿ$@N_캽&@qo@}ov.`e}M\<yw6/٦LΏ{s^pQ8ut>`ci0yj 8~{@@Nv<>H9@$GR89@r=kvuo_WK9@~)8 qi׽WާS9 yK#r.|9 |Y_)O?켿ʝ2z>sL_~2= B[. e[(ӎoVe{gs{K`::@9AdtS\ ܿx`&%'\s<#}) e}` 1R~pߤK]`$z@ B/]|de)貁\L9;&sas/vlPs.\P]}gRW~=`u~Lx`vN` &?rxVobdz+SO;vNCy]s7p7x2?:]@?&H %2H$wW!:.GLyBY@bB?`rt6:.H~R/usN_rNX'9@ ;.ٿe+L_  iO_ f&.?LzϹhFp <#G9?.p jmp> Wc~+#3)s |Hx< 2:~X$X.ﷻ X1@(sNiAx|06:p ` q@&X)HYoopuwG~oR8@TG߻o= t`eջ`To $_'pM, u';%.}4Y@ ~qv_{txA4B._M2XL`xA*\wnp. ?t|m\=su 1Npyѝ#*ωL pM;l?#:?sxq9@r7.?;'puztwϩ| /oNpb~2 #`nun`.#D3 vv\0q?*㛺@v.`_rtD{ .H^`:8΁]u.8qv.>|`^OO>}9@nRO4 m=mvXe| @OR7u_H p|sٿv)@s`:[9_ .Ni/׻nQ&p}ܿ\w},`Le}i&7vIouWw57eR'89kZy7pt)}o'!L_bhP7io ;N`b$8^sz:.x@4, @MlppܯO>q}|{tt qq<'nR.Pe'`upzs.78K\p77e[ݿi$,q>N.W6qߕs{.Ws B}`tL\ )[oe. 1@enO_)t< =. H/7t@IܿGߎ<8_@~8HO؟w7 in^ r}`{dvk<̟s_t}`v=(;9@R7X 2?S;y@#7qD].r S̯cMxv\`ws?tpc0]`5 yv $߇) 9=@r]N@OwrKn*}Sб?R9;;`vu>`?]#޷^;,KI vD~wR'asP&〟no_hwO;op7S7$H6 w_N7;r?ew,f,P9` t?m:2?&Hϱ>bz֮c8uT:Hb: $X9 9@vAۀ'd Lჩ< t *zv9g>A6䀮<tHxU`r{}^_no50 Kw <SwM`o%GL0u]Py߇Mw N }X_U7;9q@ $9y7p:W_ Gy X\Mߑ̟_l2?}.6KoQ<>O>`\ώM_9;}?N&Gy _kvu]` Q$9@@zS7/&1?|p7ޥί2>5vOܹ@ R_|o)<H TG |k3W >0y`brw15:6{u> +q;;=ns벀;$G2PH_[|#y}G=.YLߔ dooH܏2i揜 Sc|L9HGe~]tyҙl:}7Sy?']7.`r|k#/=@em=@|)x \p#?ߏn0yA?; -NH :y>:01WӼ}#e8u ωシ,4G7B/m (t}`~. H_ .rQ`ss=aw1~`Xc~]f'}Wܿ|tL`wlpgy]ީߗ6 `} v`zO ]G2?R.y6`e|]6w;?uίL q |׀i.7q֜w=HO74'&x 8Ar>'H[= ߥݿ8Gߥ/C7 &Wޏ%& `i0u9W>`:5X:p}n0Q>;&}V'x`>pL`Yo L6~KNe Q޷7<]_ﺀ)?軺 2*L9G6z>'{92=d B%/Q=iz%p;&xSI0?wjo麿:Grw;w0@= ~ Hs=`o;5W[w)> S7qh&ej2G8Q;> uwߚ#ӡܿl` |~QWܱ@M |~oN &68u+@m'@rMQ_xq_ BOv`.ޯt>+m@t> t^.G1I'\/1N}+Ki>&prk2s?W3~ {9A:pӼ_:)$Ǒn}'`NXN.ܿC> #8u &M]g7u6Yi/ $?ջ^si0|'[)v=qDw6 | (t{K:GeLit2@L=r o<b'ov> WWY`I^ D5tL̟fj/X9sT5!eSGs,pr `^RO&G X3׻x N#CPoܟsu$57%]Hw5&fRhߗީ!r~nٿ? #2^9 =ܿn ef]zkO} u. \ th0~_w_~;?SoV 8]g H=v}`\LOu>S$9ܟW뫌Ow`f.pF9kǗx`|ӻz2.tO;yjo>L8?/qIP= .W3x vvSB.]pe魻W;{=%0`< H;]6Нs=`O<\?`SPG }VGwnjPr9 XOHoO~R< L0?^Y`r~Կ[N/rR=+w#wtߴN@ѻr}`} 0;Mpp]q?M;e~m\ 5`~?+_tBw8t{5`K=` \(8]qzGR=8op2sߴs&r>uL= عAR&p/uC;w3$i P q:oU&_A?u޻Mtk֏v~C=q>bX{v^`j| :-r;tN lv j]{q oӽit=v ;xcvy?}sp.7N{p̏6qPY'[@d!`r/ݩx߻d9@.| =ԣI;3':Y2;W N.H<$5.`M]R&~SsY@e}B܏r|ϰ?( H?߻_r~w@8Kt.`Mܿ)y]`L9.G oqKyjSW9 ?ާ`—;npٿ/G̏ާ9v]X?j`d. t}`v+ L.ع@mRwMe](琉z7$~R2n]e:r2?O< b`=@un0u;js~_#t>⁔#_t'i P]ip\\e :GN`Qη .}Uְ>ޯK]mCi/mpv?q{uϹ?T?C%s,~1t_KNz'@$H}t=`.粀gN'w:_vG |?x5Gq/0@uk.b2)G>%K>?_ױ=v mt@?-@e;KM9۹Sw@e}ke]&P^Sz;,iMN0@}p}Oy@){S֏ܿ\޽bXM:n/rs4  L9@H,0>`&&שe'`8e.0<2~w9RPz]P>0rIGe\2_ T +ٲmUm()d`"J${z5J6lo8z1\O;.Qk-8ǿ \'s|g}?}Xn,KޥsӹY>׳;Wʧ[:e8~rwky?}s=_w=s{ϯ7|Lpi:ߺ8峞z~s;w:w_]us|zu+~sqwSgܝ]σ>}8徾Gʧ;Gg=}y"'/N}~*wYst~$sztLyӧ;ϖuYw?)g:?Og>\ֻ;ϕOw8pqs=/ϗ.>_,%׳{Yκ[R>yq幾q\ޝߕz~/yn>שownsp;} ys_s_/_}Cw:_O|`as&G?^^'rߝ?/s7P`~snމ)ӳx]rJ`~*#8W\6y [>a7n1η P;/w|;e}x{O'Gg1 K w \\ѩLpwr>b)㫜c=A}\92>w_e`:7*w&o*s0?⁕U,p맲>*S€

    }:G9 b~9踟e}f*f]r4,(7jΏ8߄ j贈Vwp4b{_;3r~SƗXR}W9_傔'dY1{M+z4T_[,`bip}*KYQ| [0WߟTױ?5Ԑ*tstO9`bUGώsY&5y*̏X2>w9@\<0eݩ `V.f9Hy@}svcG.{;~?&&e)CO9w}ߔK_e=}V2;6z=uW/=W&O,[ ?ew Y T{kot\8 ?:/S/Xs@{x>\3):stjrl;}Ͻkob8_SO;s >wFrߤ,P4}_z /W{.d~2zrOs&7_\PT_}~{SXr? >Sq@؟_+S&W~ܟ>'&H >k0?0]^:Lg9 9Hor`{v)wN؟;uoNR0ue*{R!,`vuNn}N:nR8mkmMe{+?whfu`~qMsz28_v6X`t_=,̟Vu}ߥ_}N_i0ml_*ߣ;G'ue';GٿI/I1Be1IWYѳiM~LxK=IW9`w=ޯ:M8`}lo9 p/1xF5e.#}`%_p@*w6kSHO9`t/1K?b.w8@#֗nzm@N;c< r*M@6ҽypi0?>2L) 8@`t>;O;{쏶&9I9~z& u}`vMO~o} @b؟\n'>vt;^x  m/y@j̩WoK)K<.q)tt:Hw 'd7po`1a ?:w#~`y& xw Ua @nRٟo@m@RƏ:]O_qr|j1@nS w'H pXc(\ 8~LN\\r Luos@['A ;c}SKA_}WGb?Q78?\.V9@&2SWv>)pN6һM $]jױ?$OC?9A>09Ѧ$?> +uw5G,vS:;/Q##mu'=`i&0e `\i78 y? zOYzߚt)z0`Y@9&؟N7ҽs  y>bxz~)H~`uzRx( ]7H= @!&؟s ~t{]:1S ?Lޏz]8Kza]'~+ رm!Hb]֟,{[&./y@&] [q7p>/:|}.K>l`R14W+ tgqB]`qmR߷2q>wS~{&G{Ϳ| :TG?^{h`b~r>Q tssۀ)v4 >5H2v\'X_ &.`*wߎR9@)e^@6 ˁ9G]mjaw=/!/ݿfSHt<_]@*-@\@$q>N6 q?Ro zvq%.8a龎\ xwSXCM>%/0=@ @yoޯzA1Xcj0@ݧx< 9.^U缿& 2pNwNLO`v9AR'xx_b@b}~9GR R8]S}$Njt5ߧ q>Kr>w49`e!G57qPNvSo׹S8>8_۱:F:fuLpvy a|]xi`9_-@fsN`?.89L`T_bﭻIחS/HR0q?e m9G`t<ܱ7tLP;rt2~/ dlyLyG.{nl*I?bOz) |x@8ywX߳~G_p&&SO< bM.e/<`Hq@ X_b.\)2<#踟-\2*sN>}iCH9@t/wwz`N MnpL=@N |pwwUS/m>0yA&Is:pjwvSxROko <[l6!o .ng}]/nv​0?`:/u\?HnM_#>N0q.H@kM]:?HrSe(H=\y@c~q`b]wv^ic}t=ա u~ߐ؟z?=S.Prh}v[. Hwtdw?t` 4 &߆qws?=~:ifOߵ 9 _4v'_캿w] ^.pb~&Ko=qV) N1@uW8ဴi81?s_@w+Ԍ_}v`};G r1G3u< (8_`v.]ql y? @qsY //=uHܟUf]QO}} LLuzt $/Hu. @euP&^`t>.7qLi0L{S_S8@s[@z?g!9 ~ B?w`Sϱ $ާ.sz@L`KMK>`N{_#0?{~ps:A(lP} VS' ]ޏ؟\ t;uN41@}ou2M'?=?S[np1?z';u,>H{n PO_@:'My@~Ne{=:.K>`ut_rVu=ve]O 7a}#'b|KO v#_{q ?u,vy?$8]ޯSx'yuܿ@ߎ8?d}rL~&/0?b~.oM[#7$X_87Hbݿ+.`V&sL7;]W9 Ls=gmyD; \\/X9?& ~ |e߄̟v5X?#~+S/p=zA]j: L\%z)S/0eKY@o:7pe\woOn й? +{8:8/#X3}L?NY?|Ӑ? wRe+#؟z?n6H?fu P(x`~>s<>L=6=]2 8av$9s.`םnMvٿ /nl&lv@v|d;1,p ̟{8_#zKO?~_캾 \\~ :H$o:?pb~L,Pw@ֿUGٿs7pG7󑲁:Hu|[Iw/9Su dS}Oau)fM9G|̟v)\A'z@< >vN>0u|'}]pe| dv#1@h0m>6`8` nP?]wg0e ~'ٿiu]'XY_b~NϽw2^ +oL`|9Ap^S$W;A^`ݿI痞 +9@ֿa]nv}]&KweW(So^}`m $/?@[<';/~,࿌,o/ =`b|S'H)@= uX_e}yA\:2\ v_Ho& Pt}= xa?&u;xA9:'$ޕ;rv{sSW`~_i1]c9/pQ09CLuY&/0<#*{K}eaL;uO=NnNw\ 9/p ^`eaj7)vk:eoX y? @=U ~)W: &G:& <`&0qv$`b){]Ou24P?9'Rg$1@*=]0]9A߃>]X1tLs;qv)HW.zv&_oR :>?>_N\8#w0k`~vz@TwF2w]ņ_]9`??\y/@9@}&;V_2IӎoN7i6Nw|=_='V> ) <@sL +s[Dm:G;>Xx>9pi~ vLY?+P9ߎ8.8&';.f&;.^p@~ge^y>O| ;9s `wee$is0`m@R֯xq?e}*t?%֗`;'0qN`nP\X`}@:O0>:!yA?4 T/yTp}~}|9+tP.yk2?@`r jޏzL`H_TYix;> O7u;MiݝOz9@9@ֳ2|Oߴ'〴Xk!8L;q@u>~N mi7;a1T2q;ߟAu98u8K.`uL8`So&H_~$/N| #o |?~v\0ҙ.$Χn=)K][%sw^⃔Lz4|pu~<;uuO?irKy.pNVG mT&5G t:~K~ xM ^Mtz@(G'?(;N|.Kx@jvN/]oqjo5| L.dL}ߩ?Kۀܹ$1@}vnIx'~㸟U?) H?bSȇ~rRPS؟$؟{kX쏲󫮐s[)&_vX|MqN.`\`e6:'e+S AUy?e]X7,n&/HgNs:8c}L]qIn:.GX mo=6&2?|%W~n/)H?<8p`rv>Xyzv_}4R71@b~Hz\;*B M< )׹6`8'p_rt]`vP75G< 5v&eB@}st]zOX_ \ |^<Rnqi x_r~c7?$6ر?hp6;a _P9?TGY@n ί9׹w6>mo P~>QX2a~stsw =eC踟x;'uu.u?vٿX yQxNzWas`b/UL v}`e_w1@W='u n= ӌ_]/u~3. >~. 2.u_9~]/ykίr@:Rޯ>c^Lva~| \(s蘠azw%d:q@]'RWٟs>c=@} G(;ݭ!KϺ8_,;v#pVӃrS_N,rL Ԭ<t@t.)G_v`^ LtR09@:8>penܽ8ov_wSw1?:,`rRXu;nRO>?hؠ*,`RoL. L̯`5xαxϚ-IX{)xvzh'P~s[.7S-@s9neq@Sxz:8iܟ} Hi~N<;TG{7afP~$&I; t/y pxr?zj60w_Q~*#u~_0 t^ޯr?w.׹@\8_jr@6s[ٹ#>X'q>iw=`{u`b;Hc$GLp?b 0Au}0=Q> }&7Roל_uܯ `| +NwlOn0~r@}|`vo(X'pf& 9?b.ݹ;:@\痶]}]w;W3mVw_Gy~ϕܟf}=tf8 uw:@?:A`t>`e&h/?.\wHG.ַ:mv.`uԻ K?ry@h1AK<'iQu{isvO<`t?倎)k\S4H̯~GX`r+뫟)!L]{n)Kz9 ir@Lz( 8ut=MH `LUG`L [wA_QH$oezЙ=cq'&rou>pe}g]`r̟*L}2@z_vL9Rυ9pI7qA}Ռ&?o$Ha\C\ *sN]'}W<8gݿIN{:'&W}@1@d( X ѳcpԞn#1@M|K??]_rwXw  u_&旺sv`-@b85qeu`$NL0u~nWyλ |N;' ;v]`];N:p'88 +ϫώjwgpz(緓pw'Hc~i?;*KoQ6vt<`:G`rO2clu9?}eM< : ,:8V_Vv~ r?}w{/C й?R9An5m~e<:LV&Niw}t p3w.ܯ^ rs +]/@֝~̟v~' pi8t@`'q\o&pRX q~nV\؟R7\W9e)@): Z~1{e&'ȴLvN`lg,`b~&H: v^NvS8#X'aO)u,}w>+2@r@}Pog~ip x[,p.pl/y) HC_U.?Rxm x쟲o3`r+sL> s_[fv9AwDNmv p/ı]}e}n;X7)+{Uׯp 8u~ݝc|O9f '?ϗo(y TH}`qvnՐ L/e8`&@[wu^zw^~.$6p>t]Ni0+l}@:&:>tO< AНFX x6f;| L@@Hy?0{R?Xٟ~h~ /u#mjwvx %HR,z?M@e)`N&}`)v;m# Nb? ?$8?9p( T?=wPmNn\Og:L?zyA)sS|_>w}Qwd!'H}Q>'p?@2~ r]I8_ tLy?uͿ&'1@;s7Y&N&H<0y]?p;_r?倔 LDs.8& e8]&@b}#X߄j qm@e'<x?v[.LnM:i8 ~=zX4Wٟ:AF79q?e~ӳ#:us;,`n9@<&.?e~\pv.`r>mVG`|N0e[w9H~`|X9H$yi#_:0z@ N}_X3~sL2  F ? x;6xs07p-~#Hܯs]y@Y\/=@r+ı@ +;\6D9,`q &>H@ѻ2@UXs}]//X}^`K:@ x>u>`N<8CH@t2u@ӡ `~]6t_e|> sy@K^:/tYIuL(S&8\Y?LvG @j0m:L`v_sY@O.܏މ:qo}VX2<:;G_b]P5\ 8@(w{ey@bݿL>i/VwMdSo=.ծ$7uS6ߝ-@nrHG$>Y?q_Ns \y>?kߗRi8$W)X@V&\ mv>`8xy5Gy]G= tN`'G.`&`b(X yC@w}r~1@&:쟲wu?L.`esٿB;mO.s`;;~**\&G> |*c]< .GΏt.prԣQ6c=/\w=9]Oy ^L='6_,`e~}@:Ԯ/[zw߉f X_x&'p.0?%H:p@\z ye;|o;cSηKP1LpzAMԼߎdܿ8'.W8@_~;::r(wÆ$9A*s(WRJv:?2~22u`r;*xA~f| K,p`V޷X&*Ӽf\/1@MsL~ v:Y@|=SPwTl%OH;G`bл?nz@\;) 2o0Ur?ej9?e _tY:]e)H1@e{:6x N K=`}\y2wltd>* t'ٿRx26h`r^dp L_sܯr?b}L _ n9AeR񿇅Q> ,L]x'$r2{i Lǃ 1 2B%7?^n&|<. H 0v5v+ \wb}#쟲@&s;]&0/m& w$ΐi2@}$|O:w,u l2;<;~ur`\ q@ꝲ>,s ;{?zS/r 1?K?V?6Qy@3{.wO!R:( |N=`j79A,`?lNw]p.W{MNlQ痞ua)w#74H}_Bae\.б&7;8H]u9nLn`5:o\~^xN:m a'p <.sm&79L'mM7N}Rmu_N̏Bg ~L] &fhs` `}zVWq y?U.\ vIx*M@&;osy@e}$1K- u< uSp vĽ'^ } iP= olmND3tipwun`ygv}]=~]&V/']`tLHaPߧ{?5?b]q7po@}I{<_lQ+u~˗5:)'$/&O= S

    u~S?];vٿ]139'p<_P< e~.GA)G?vw1y?rwv>`u/u x`^`I_< )9__5'~Gwuozxu XW^뺿7UG@SA$] {FS'\]o^0> K_}t.ȱw0\V֗6'.L[qp/@| H=_W3Gs] ܻRM9@?l&0)-; LBx? Nܿt *羦L`}x@ B=. eSuwoQXlj y@4u)Kn2>}'$>5G@}~;>`;R.PRw;~B~`w y',z&Xߔ \wWy_l,`t^7]pw5>S]GR@u)G.`cq><xVӡ:<T8=\&mQPYv~ 9A(q?rPX ݹ<ㄔsSq>SP;0onr@rti' xz*v#rSprtN Rٞ @v#X.zN3~ ؀jo, ^`r?ki{.'N7 ek揲)3d<_t<_`~v})q_LN@rT޷:9A2]֯'z--u]`{ ts\ 캿LN@b}/He|<Mw-@L б vw0j u}?K}`sO ]o /);;ߎ:cQ3uy9?*;g2 g0'89]xKzKe_&v?t_ScVXS.H?zv[<\Lq@w}SΧwM[0ݫ8y; @w\ $uK@gb~;: N_WO$Χ6fX8-t`v(HNrre\/YYy?42iW]/ |!YMvsNn~.8qOukX޺6Nn 2]@wLw pN`ݿ.G[Xx]~swt]~v[/w_KOz__b8`$o 0 s(L;N`x*{ML] 89'.0m>gri/9?\?&쏲]oK9< .Wt.L3M?? ?(; +_vvy?'89r׻_'~O] ;7½ $kٿ7Le;^}_*%en VB[Qssj?z_>uߩ5xt;xv6\ߺ_Ĺ5X^⁓Co? x #ޗXݧ=@Sor\7y5Hϝ #7q;\ =@eOs׻_}pN^s[oʯsC9Fv|[zn8t_-|:*~z^m[GOZoxSO߹~rWOspNw_<~]rW{?}ݭzL练C빞u~P~3}O=\_wrN]zvwu}=?8Y#wm]\Ϗ˩w[}~κ ?)w}=T~yxu8Y,Oo=._w*׫߹xO9>np?=GP=z?1;};%q?!1@~~^|[n1=z02?⁕*N̮>w?e~y1G,p*7t\p1=b[VW`O Ow?e(SwYGp>*㫿ί-Gsώ_}Vy`֝~Wη*#܏br>w\^t-W'>w^7c}tK |ѿ<>7b{{;>[8`~tx{Ngqw Tq`);?@+/W߉V_QG\8o*K?e~/eҹNMX7;dj?;‚@~})Wٟr^_ho1A#W3~+dSY`vX q@6e*K) w*X.1? :MTO524W9ߺOTGG9f `>W2'7SCH`OI0z?b~,`.WsߖXy7Vr\/}`e絍 `4r$oi毲?//)sٿ*Lໆ)T_U>e(X򿏀Qfzvٿ܏ro>;&]c. P@u?L0u, eRr)>NW9H}_p\r]OSS߷2;6w21u&}`e+L<2d*t?s191]Hٿ j֏2iP~ \?X L<бuA._( H_1Kwv=`~u=:`Q2=b5M_s)\/u!HL e4 \<1O[ߨ\y`b~zWYWeS,sߔ#`M`|t;yw 7@N.7o~]zs[w(Gc/?w`~NM2 /w3}{[{ݱ?cܝ2@;X ;uMv9u[mL~p]/w' xsߎ`l8&^~u/8:Ɨ(G]`.v=`Wާ]OO_]ر@S'2M@\Y1A`ek.vkX)/m%x-5LpLosO{$`w;nP2q?T?ϋ#r{;+]ޏ~/@w&ChP2?n~c3G/o]fOnl&GmNd PfRPi)옠K9ȉ?>%G+߽ܟ2?r)yAr rt<0M4nsw98*rA~( )tߔTϡ LuiXy>6+sK.\PYc |iXY_}y^Mt<Ԯ'*#G>0?]'X} ~{tq1@I=`>lv{I:lm`e-98i+,`dy]79[:7SP`r;/H~]u'@:|  v`&/b{v X.ee螘Ov=R:Ah_ޯMz.$C벀Q']X7< 1:=9A moI.L@ed a+s?L=`~ _@~3O.9? 2Ry@tP}-]H-w#`6Se`o?tiC= p9.< <T7;{w L 9,~_^$SoN] L +4NSr)󗺿y?nmuNYs}Y/Ik3<,Q?y@oK=~.sq~pb]N؟nxhv.`b~ `N]/ei -@&X_: :}f:s6-@伸\ogv|;Hr~889=s7%7xwrߏ~:u~Sz&~ xf\>s8,b&/&Hb}ݷn ivLz_~X~ s_}N?_et.Xwt_WL{< nOR'dOٟf u0i `I F7$/c%| ;]w#&r\y`9c=;=N}`<)؟+w3_v9@ `-@< _b~|rwvok揲r{~.G֝?rm_g뺾N/rN.^ Pc5H?傔w倝X`}.X9_|ϱ 2n0eod+s߉U&l_}kϱ>L ux,`N;WsUwK3|We]yv@ieBb<)-UַLRO̟st.v^uvu;?`'Η`vp<8:w9R7e$'0:H +ϫ&en7;=ќ_ <'oh02/p V֧{t,Ԝ_gTPyヴ.:/H}$Ho.VGY@rN2h:Am.p8&;s+shO= G?z]0y $7?8qit<йxޫC38a]phi9i7Sz*, 1?S4SWN`l@47q/q 6i/yA<t]z <8_첀KNY@ v]-@o ;_:ޗw N=q>tn[Cؽ( c| TDŽ:ծK K<x 8}/~^S'n*tLfi=@.hqW輿 L{#7$0>wW/Gs  r?SwN.x@N\wy/u`rjPSpq>rP?~tO/:G_~$_Q6_uO>IO&}Y&r gSC9s78P7u$'%~v]ֿL_4~L;7ױi\B`t)7;%?01s a elc]p}_<D_t=`[>w.`s $RPs~_j]׷q?e|fpM@S8 9@y tG. }]&1.H?:?>‴v pUe8u.ƷvSP? xB 8= :貁X9~t]Nn| gS8y@]Xϱ@5X3~h/~i8iprvr }Y|_r/@,`I'sk/%pe}d~ذ7dr/vݩY?Fy)zLy& e_V7]>f\?2 @ptߴ8| ~]׹`oNK}#眿nQ7m0m&{;W\5xN:1ix}00AS/v~;W9 @:shy')8e}ucA<(8u~]u;.H9@ uS>0y7}>OyKK?e/}IO$~p~K1〚L%/𹜏M6SoXmu_~$رv= $}sTX$ 1?d`{npOx`;]`=>8z7~z0s<\v$8q_.x=@e}.2lNrq?~N]_v Ro&{[K[[ߔMrK]<vy@rFqo`*Z2s $y5Xܟpr:_9IpϚT.8R'.w[p^y^r ]?04W;G nUx?&o$mV׽'Xrytc09w!zdM>pb}}vo߉)|3L/KyH A/)o}]`WYnsכ=uܟsCOo}_bip'7KM-@:ީw[=߿5,{b)< Mp\yr?> d۝._b)_O,b{kP0>l9&xS_m%'Ѿ$ zh pj LY4:m*9CsW߉9HLpN&$w9d8_t1 L_I^&7yL.s>ϴG_:UVH_w E` ߴes_|sz '$뗶$i7ӆV֗)VN똟fkoF'X~krs,Ѱ Lpr_sN o ܟO]~o'8NpM@v?* qf'ޏ\,&ȷ$r. xk`nk'u+,`e|TpLY? &ߔSyi/q%0*-@z?`xf)q@.QgL x=0tuLvӲ[ojy@]@R79@&y@B#,P;ߒKW __SiO^{׼4&]pvo9@*$'sw@St.01?w} `߀X]}_n:u2?tkqA< 'vٱ2&-@SzL,. 7u=@-wM\}B9@l &V92?&Gr_ @\ f) zų l`b}M@Cy؟@h&ޗz L,P3xa 9?&X3 ;7r>zt2j%'b~ܿQ0uqt`e <]@b].@[Y_?HN`tom~2l$Gs}w P@ + ky;?@ |m: 2  X ?q<0eQXsS6P3>~t_w]P= (sD$VL|S9@dQ0=HcuSpl Oi0eSpu;Ȅveu2@}ϴ p9 u~ _rkחN Nٿߋ)/yA6 y3| l8kP} !0)G?ױt[)oޗ2HoRu$]r 8 ,qDs-̟r= 92:87P\y@\:upfUzAW992?e:{.xsY@<$ޗ A[K `q]u^ T_x@*ۻͿurw!Fr)LXt`&G?^KO_NQ7XLς,}L>;.̟s @?]/9=M~_'od~w=@w p%GiWj^?M8n/9@4L<. Mvv +x)e$F`r@Re; @VW Gߔ?Pk s} oL.o48t[U}c 9`T2U.X^vy@\t{C:sWș.֜߄Q؟%蘠f4GPw{~_:G]`vܟf:O#`r? ?w$/0p9Q.vsw.`^c~&Go@sqSus.wu9@b~Sݹc};u;9v+#pSHwu;;ߕ/9#9'H~;%߇scN[K傉NNp{u 0e&]nϱ@gN3 K[L_nҳ/eޕ$ޗ6|?8\2?Ԝ7Sol/* ztiOϺwb}0 m]T)8 pNt<Sx u\P3u^`un2p/y?t>u<>`N<:2o7 5 Sz@*Ȱ?Hٿv{Y|`@j1uLss^ع8uf/q?%G$8 9@R}1>} om2@q=ۆO'-vN܏hrioNsi . =߿I[;np8iehP3-?8X hX \Bs,c, >voHs [ Do9@(xnT1?[;89@\o5G8@j}e|\T709/(~rMu;@n9x~yNl߀::WKmoQN\ \ ]x Ɛs+pro5_#GOٟv9qǐ)[>`^pjϹ@4H S0v3L.2`(N {ݯQw%y@h~SΗrB8mN;~wz) S=9'pG/@rͿ]`uL;]KY@z|bvWδ< ) %G!ع'[S778t>jPy_)_%pΏv pV]'X>sP<9J TGuLP^ Hi6etpG/u}uo%/NS~X$ }=;xNVIpLp3m rtO)xנ;T7T&7ܿ;#'o=8_ta@ou[6SGb~#D$G/?\rM_ .2A.`/淞:Gy~[:T"t}w=]}mt ie11OMpy@ֻ#&!enPL{g2AVΗ:o X)|]7QO9;[;mR71@w\6SN? 9@ֳjo' p>\Y`b|]w6`rN-ۗ57uwpf&:R0.8okrA{.]@ :uw_b{n0@$&x0+^s75W)8e}_0[Iٿk/.ぉ̟o۱@;=NAS/fYsB ~csc v6'[ir)֝@|=<#uu 0@}y1v,P9 1?x_&H? y'73u=_v_ +v^ip[{$2VUG L]߄t})HjU` [.`@vp ƙOݿuS毾'$e wiXYg8+Y?zO9qy?rR:]Xy( GOW??%9rn:r5\%H9@N!yo#w};?)H`b}D怜3 <L vvOo} u)x_gS8) 8O]N _% {< }r>WO TfP3~QvN>X`e$p|n{r>)m&8;9iio'e `rPO v_m2g:M@GK v.]Ϛv~9f/nvϹ@tp /߀pb\2Mrg#H^4Gty)L dW0?uvGxq==R'9@:SGrh>eip)KN`r t?}9A\ܿw~Oo ?;-@6Rpvw PiPY_.~Bu]`ei] j&p\ )ywN@~ @M 0e뺿79Ι2.X nc~iJ +7_>srixt@S!WUjo2_< =߉\ 㼿nܽu?mI@ҹk2.y@w>6?N[E@s= HLp$&_S3ܿH??ₚs,};LYzi׮2@bpw(Rus?ovy?b\ LlOSx;L]ȄM!8,7%/;'p(׍pSX7StgfX7W`';RW~ <8ᾜuO;)܏I<66Sr#'~tO{v`K>`8?pQO:>_fwX_oe~Ly@@(xtߎ8eRpo=z线u09?v؟c~m8)x01ip`.cg8mU7)X3Dvg xw%\Gջ{as.zQ vG@9G~_bX`O,nޯN] uT:㭰Hϩ_6 P7+tҽv~).Gyn:x'0u]^yr?@N_ ?&/prPO&/pq,P_,`~)S pݑ.we| \{ ıK[o캿z c]1@0m_( $u;rNh\ op=`'߉W ?(ع\ Xw_R y@5]`#'4X9? F6ίSv=e)'i0첀XY`r;1soNٿ H@~h99.;) O<]vٿf $W]}V\-@K-:HcO ,ޗ6|W6j|H:7e $He\pnpMtG{8'\ey; 9@~0m:\~[|@q>{M:?mMݙSp77qt]P`eK ~'G tpt,mC_ $:8#uK.`weS/?< 2igQO v}ݿ`=.O79/0?үn90ip$e/93z ._eiP): N<)sjw=w'>`[aW51@~v9+s1?⁋}`b~iYS'pw_&:#;7@ }&Ȅ9xx@i3<LB;.X{|{jo_ ?r'OXsvz.$< U@v]7:01;u:H4ر9@;SRO8m|t`L8`\n0y? sLJi phPSA:%c~uO{G,0qM9Mw?~^A\mN〺H.i?T>8''fip}np>s;`?e 8w}o'7q '_ɐNp:d jxWWs78\Ur_O=_wtoLۀm_rt[X:@&`X;n-'HVtokF/ \yUG9|*S; G:' >@4:'f]WI₮첀]zO>wy@N ?-@i Q\ v_KN p ?zNY55ߴ'u{Sw>O4 y@:s8`K ~ xC6?zW7usT9@\& Y)v}NNCcps=@ D3r?b| _j0op s ,zH};ofeRvkֻonvQO94gtٹݿtV7X~.؟1kO86 B\2{]@V&H>^0$q\ر=:He~u?$p(p_:L6S0qA~ d ~6|+as7R&H~$ա\~vM@j:^{T&y':mMwQ9A*׫vY@:!ຯ=@|]CxצcSϕQލ6]r?h/%p~<dn6 ?:@hO3.6 89'o1B7;Ɨ؟N} Qw=i|| ^;cG @u$o}'׭d47*{e.p \uF/s^`ܟ??pTWwz]ߗ_.6躿:]`:6n農( ϱ.ߕjK`pK9)KY@9| d%XWDY_8eS? 6_u@}t鞘(HtGߎNIhsO&]`;rk2?\2.Gޏ|.rs_e\~KY@5 /i)<&z]vuor~^y>sߛF߷]Ϳ~+/}sۀ[9~) 9 rk5xshWS旜Se'^` B wn-Oߩ}] M'9A e{b2R.H:-@Վoj} :'>ߺS726Ĺ@sSG?G_\ L]ior跚o522.H<:ȟw~`ѽ?<;qR簿|y `b&swy˛v^_ <vnrL@|n 9?zߤ?.f3}`O&]`_(| d'W_8XiϝMq?ij^v]@ Ho u{jw=yw2u;rܻz>v:L=;Go: vr*9A\B?xy?Q0?;BNM|j_b}t.` /_uVߟI,P;%ȋ Nܿv>`r~t?ko.>;@;}zY@XY^7'Ώ]tY?z'&yixV ry?rBwr.}ߝ<WGl_$w+hs7{?&Su'=`x&Osq__rO \)7w/;χf * @\X} m2hw?=;?w]nu} [^LxvޏH_@j]md5KR.r6ѭ_jPٟvi/1y@>s[<뼿.7{*:.r \)[w-L>$:@ ?倚x`w$2 ]ޏ?SޯcN=!XT'0u'Tn N< z9 ~0?(zolߴNu=  uS78oo@?]+X9ܟ Nu߯o#W_r2?bLfku2h]fmg\Pjq?N|9H^%?ܝ);<ပx_؟st}/Ht`լ_z&wbx:lt=`H2> &r-'`u'>v Pw; @5l'G`}g X_}vv} ] ^lt 79j^LO5|󭻷xpTG]` ?P~ \;rs:@&SX] SxMίrxqos=BRq*oi~;;^;[>jN(ߣoK{jl`u&AS׷NpcGM.`; r~;=`b|@ udܱ#:G,m'޹@(_w\MvY@;GNL^R)ozO A+GwL.#+t}ߺstrSOy\66OOpĹ@*K,P`r#y' 0uݽsv.S6PY`_<9]`r~0~wl>~ht(W9?>r_)S'Hv$/rN`|6e O3S9`}oe 첀trvWt\y%/y'9I@uN?bx  vM `:Gm#7 Lr?e ;vt<+SǤ }nNMv#_2]~A2|F0u5H.tq Ά v̯tw8qrFps_&u 1@eֽ?e $7ai:q>pR֝=q }@bt,`O;&}|vt_`=7;u)0=Sz;s^r&tiR/m `ܯ:ޗ< |Y/@ܿ qRp _e :HTrcK~I8y5貁=OY $p򀬻+%s78:NX`Ri/k ?:'KGm@b~pI\P;N]!K_v 켿\ ,~ ;gث X +nUmiLM)U)AET +PALj)4iݛ;bψ=ZcocZk~?բm}Ξ}s{yǽ3Yz'ݝO}ixgYg_]^r5wggg=ٹ<}g^wt~{|C<,ޝGSuhx>vsv򭿝_7So{=zD~dywoO}}}{}]O:wϞ]{7sN-sކu1λw~*Y_z>)r9g}sv9VϞz}Ox[9<;WӳzW߯bN0?;P` $>\^*9 pbk}:pS&^o<ニ-g>z!s>+\rz?#x0is?)ϝ3zoezߝ 옟b. ~?i_ `',2p?])<vLe:A|*ߛf)G'kPyMU槙+Gy1@b|hqLVsc~ tO(9,pQ~WGO_;e|&\22e,;_=6Y@‎:XzCVSGߕi&PY 4~uY |>+#G!?ごyL&7>77e~5Wf+Cy@}:^wz 0[U&X_5R;! @b!wcz(G~) 8`)Ӭ};`[U\eW_K({zܯcwx`LoUƧQ q@ TNA]ri\o%X1@~[x2؟vw?_һ2*K9@M3 8|Ne0@&)W0io `oux~WG=7 ua~ί~S&PY VG;S>էcգwh(Ne|Np| $޻ `)M_wP/8o7@2Ad-GtiNo ?N;wT޿er.z9@*,]`b~5_O.2KٿIX3(벀io]`V h.ߔc~n#G}\K9n1?}r@*Q5G[]pK]`O6.d*w&` XY |~#.V`boO{vv){c3x'd/u{s[#\Y&ICSziPqO+}1?)ys`e8o7PY.M8~@e~&ofq+. Ho#< ,`Lޏg7)H'O rrVHNj:;s xnov_SWLi9G;n ~+ ] ;?;g >9?x77 u'Srn8_) LL\;fs' ieK t/i0jOwwQxk貀9.8qp}W2M@>U~]x?-q?:1@9G, =v0ޏ5`pe|sG9`N:@?%Xv:pb50q{f3 ܏6+'ouO3-w\v2noqnrI/98k7yk\&ォi>{L\PY_ba}6&f: '`)Y@N_rT&X |qI|__[_f#:Bk<-@eK^s. pz)躾X`e}MsA]< ;u~}>~rd(< H`~z*a~̯s;,:y@~\ܿ=4>wN29^>\%'N'8eK AS.X_8`N0?G\0@to~K&7~n A8?2N2?ҝ%~k @v`;vY@&6H~sO }OG?.` v?bVާ[S~/߀fRAv\s`N ,`]۰ 븟z MqoM@bie?rw9@wðe;)ߔ 0?$~?(]O,?HwP.jWX`t%popt_wen-$HONfs1=Nv|)XcvG0>:*﫜}ﰿ< ߔ'{^pxA( x`xMY_K_G~`Txvw]`v`l;SGA5Z<9v}S&v}.`S{?]dO_wx?=8Sw78:p;5 Xs7Se{Lٿ.痜 @#e\&s'? ]`u_;r)yt йuߎdwp\ |2%XtбuSX]&ouP>;Gםf&~`L9@1q,R;K P_뼿 HO_^pM=gx_v?Ts'&X,w .7+DZiwqYixv] :&8`3uS.O~>0&>.Gy@_L}`e~fWX,05v:0~ 0<ḟN&`et>e\Tv) xF0?T4קb|;5Go&y@ֻ2, euP`ޯSp;a;d4=]};9hGC`r7V:\3.8sߔK.tc@7Ow}SP:Hw:pi:0ukroL){'t]{0>ۛnX 1@rw)K`r>v~At#d^p<:p߄:z@` v?;l&?L`N]`uԾ2A:&X.8#'p~GN}.v u]q?ӻL,P߄R7y@ uٿ.yevS)x. <&2ݙvp>&t8&#Cw.tP{><? 'p1p < v`r??(}i&Pw;O0[/#?w^f5 P7u^SS/ nRzFO_~o5Yu.` v/?K>e\wHbMur✿[ώ< ]p`o}W߄f 8߿6z>#)rt]S};&=ߴop@ݿ+78.?9R񿯞\Sr@9r\p!y7_$)s9@v`@q9@e?⃎)u@!?H܏K;vn@S]W e׷cBLpq99qtۀ /ݩd-,`!q@b}?@\&u`~&?&S]ݿ7F?>~#:H *#6a~<tܯf4WstCm$X}0TqY@vt;;ne< M>8"9w>6i/1^`vӜrz~=r \y@;ut pN{;Yc9:)2z99H`uQ ?].ޯr>ONs]`~ zOn vwnݣߔ t@RGx@2_s tw܏o+<, Y@rtO xch0y<ႝ_n6KOt^K=n.%;#Ds~^$d q>q6iq<L_4s߿xd/?9Rҡ0M@e}t :;z.7}Z|]Xw2r{$ޗ\;zVWY q@efh=@p$yA(Gߓ ` |;6;'6 1ALN7y@4ΐ_%;6+SH~x@:]`=K;S0N_Nb5 8N06M@ X] |`rdA/Ns 1 ev m꿙| i6R ID-s>z`'v_K;W+sOy_qPX` `Tf j`_οs@b|}Ak7zAq I/@_~0@ K_r~wMsL?7/:)y@n `–S@6SviߝyA&>.  uWrA8c}N&ޏ: 6WױI 2i^~s)sF:۹@:{NfKLvwtje`v9q)K{9N7y?_)󗺿 Tws|LNq;5Gz缿 tpe>w_ ?*[ޯRϱ?ѭ(sYt_7?b/1S{nz;oCs:r?u>8.` }ߎ.p}w>&/0u+#o~n0esS/eoL < _Ks?8|W~v|ipOte*@~z& 0)7V/yv$/pܟt[n]֍,m5뗶d&_:fM3 @e#X}@t. =@X` @ VWʿױ7`VΧa~ 8R/BPԌATG9@6vn`>}BxtI.RwM 8wN !xo{Ѹ?#7ȔvM?7>s6koQW;f)뷳uw}~W?`}p%s;G]oK9?q[T9A~3v86/>V&POKwr ;m'~\ WTM{ iXٿeBYw9ARޏr]oDsH>_T?'pb]؟f2)/@ Ɨ26 2SG,s̀^: L?v~k]W@u~t]X7z?(/}rkO3BΏ[/e @q&'rIvKߔL[017]`e ?rhOS/XRٟޑ 8`~;u|q u)X`w{:;IoW0?Q/uw)ϊX yHp:.WK?HyIر6) fuv&.`W柾; uN57t}N^:.?9"OyQ<O v wq_N;eiqoors;gq@zO[uo +閭Rh0?Npr\ &.e?nr9Vַ~Ӭ_==}?6Mt>⁕)oHUSG !T/ut.juVW9S]_q=`KN@ep=z?ιt]l4/c~ι@*SH_qt v.XٟRWy_~|۰=wbz}X_]\ٟ2#W߆Nݿ>~L΄=I\7@]{O{_hq+Os]`b.m*p־kؠ z@(@=So:ٿ u\e8@nus߹Ru5G_<~s`WbB2~&Wy2@ B9 SP`y?=6u/uP `Ln.)G p{,%Y Y;.)'\ "\1?u y?\6P98t;&-.'/u]e{.X_/u;~^nO3]߁qs?8oC߯69'c{`p߫CKߩeNnfn@xa=?'?` P;-\f X2HP{N]H ]>დyA4u~_?|~2_=<u>&c=0@{&Dpvrv\ LPzA* 6vߴ H}`2o%Xk0q^zp[W^
l 9_>u[$߇NLP`. ݑ󗾵vAep}tnP7;#G~zz8SLXhn4q <L>b~2Cvs( >wuߛ'@TWM|]НS t&imհ?傔v5X^: 2eݿn/#wqAi2nPvKG=`Xy`q}`9A8q{`wjww u L/p{0?\0/eu t]\&PsN.\W9_#d;uS]vS71]`5<   L et]\ ԾcLy@2[K?_5 v< X7uO٠2AsXq?|27]cY6yAbRe+Knqvzo7Y?;9Wߺ`ܟt_t@~;2GrԽ?׹s?8o=uk $1?}SX{N3SO;sN3.`r(@s; N<L,;]"8yAvK9@ֻO-t.8]&nN t>{.:AS)2=p$ ?v[Oqw/@b}. HOS{'Hks/q@]))܀-'']`
K—8_V\ > $d RWH7S\\_rN~ѿ.por@bQߔ>p2uTwpts? ,G9@wR L]`ot.2S9_vN_v_o!8qkW)X RN}h #?-@tP=!Ru;8Qxr~s]_%v)عy@&ɩmj]Ow}(H` Af]q K p `mIx L^71S| ]@^4~e&OY2@K` +r>b0\G`w<>0u} ft@e@ԧ2)&0mN|nO=9H+; S&1@}$0ʆNLs_O.\ ӣ=`To]mRo@tyWy?4Nz^i 2)8>r;Yo]Xd/m:nXie\ސ#8 mVyD RxgOmvL@ݿTwvMNo+Ln < .GlPV/1@u9@G0mvONؠpA$'^9@\@v/ 7}_hux9q9@M9_v]`b) X L <sv\`e|]Os)X;m:{w?_?~Իֽr@euIO]:G,0%܏wM0uS]@倓\r?b}=w_#X߃s icAe~ r?VX-ǤdOe{i P$7a_bK$瞩<=S WױAe}0@^ Ix&| S/0:H;uSGX_Kqz NOǴuuHp |*@e*K,p^prMnP_s:p‚S:?(s_N) 0&_u.V:S(s~l$0qA4 ovSXd:'0ec2z;|bo}w wd&N߇ܟ_c 1?  )^R~ ]pyt/Uޯ2+#HN =n:vtO t=.G Ϳ٠\Nxfrrs?8xc\_;K L`<;^QнL y@L>d9)v~` w9?G c:Gsowp$wl?bӬ]_p@4Gw0::7C.p7O&Nzw9@e~Dq'_ca@&Ώ<~g#s$ݿLz.]-'v UG`9x`2)jPwS| ?G<~22 v_:]9;w/d'< 6?w}`[`ֻf':'.Go Xs+#&[%|Jv) Ԝ{w.TH?ko-@倎 >{ gs'~sn.\ v 0sL LOk,rI0u߼Vt>`)tߴK`rDy2/y?mr0~@ 98ᾸcsLgqnM9;: 7w}iP{) 8q8)ٿoߎ K M)T7ဵ  2&n/;Ǐ ts@wm+t=_r~L9`W>`y+K]0@ ?%}.\ v`}@K@}xdX9_| ` ;r@~-R6P Rm\s7\u_ϛv[VΗSo v}_/첀 BINqOz _#o$9k8 T\9_?e].Hs{:0y@*#1@b6&f.Nw) ?&6u0uiP= Dy_b~7B痺s<)~wIm5رo@* @&7@<@Y;p#y@ ie]OMv2$\0@* Tȣ&mwrWޯsP毲>b|t7?q,p?iX K#u=@~\_v`As e^pNc6_ ٿ\nMzn0]}V.=j/e #7p&9nOstr_"eo8xg>9s]` y pSt0m^3*ܿ48qL>מ> v_&7L]P盜S1?[5?e~\s}wT:]毲@X=Y]8qI>Sotx>Sp v\xe;L{ 1 ztvLp'r@}tۀt>~ ~|;_iP`[{?.Ov ;HT'H$ߚ c}Qݿ?kܿ[!;L>W7QGy?̟$G?M:< Ly@}.#8`츠;X RӃ-@R81@N\ .G;;h~s,PRO);/҉ pb @bzl=2 oev9A*?L\nOwO97q$TGHٿ(Y.g>9WΟ) ќ6WƗ| v+S(:NOs5D^:`N01Ai6:&xk/mv `aPy90{u|.Sߗ65Ho6$@倎RܟzN_G,PS7N 0@L?~ B.؟;1R/?:?&G, 1Snk&(8t>.uOLٿg '[1_=jW$<]s:A:G%p~].p.8?;`~B.!7L\'XyޟrA~UW7~Sm_rWߺぺ _)}Oݿ!\0 8~0c~ݱ@t=`vN49?Pާ zSy?^`^yAG߉iKHO3nk'n`up#Wkr8B&2ݹL]`z\wo[ v8'n`V7'G` @$X3~}׀vyz.8@:/Hr)WY޺_ T8q&'mmV)d'% ,` v? +8\O8u?^ uq'@w#oG@p?x)8;:H..sO9:?C{-@e5wwn{t]`~+wfxe]c${&x__g0wwf\sͿ B[]z6\;) M3=.c]`&v⸟vNsLY$N\"`0MuyM`e~iy%  j^0=xw2⺿Nv{~&貀p~N`Dщ )$GNA7a~5 H_:0ex]~?9':@_Pe $e;XdB^X7t098.v5eM#e&,u r v=`MN̟]m9ȮD]&i&p8 @I6*lT < u<ߤ:L`* L<09]/ukOH01 | _v`q.`{mCR&PܻvPgr\ϴG/mbn}ί_8^ 2=vQG@U&ߔ `bs̯Updwp/ѹ`MX u}u{tٿ#6[oK_s`ǣyARWs,:nR9S9w\*e`]ܟ;B揺M@~*KٿTQgc Kn ?z0N=@??Tv v) H?x%߯v^N:_%7qSpm9@\ip=xUG2*|0 x☟f_=gs:s;Χo0i:.H>n \9/֧:@:?`eoSO~u0/upQ 3}eIɩௗwm]9>r@wK9:6'n7e^c|ܯs~8X]) xgi6t&?_%\$G47uL] 77?H;~]nO0Nx2ip2Wy@9@|uzQ @ya;y@b}KOt plK;(K_|[tO/CW; v:H:lj~'.̯.A; 1$HG6?>6]]б?ob~?app \W=`u> )'H?joM~q[q?bpn$ߨd!v;8RQ78m;N_r;pbwn;uL0@j֏ܿj&pVOOs;Ws~Ϛ `}v`jc(_!ع?Rw ;.'6=:~z1@.縠r>R.0;/fw] &H}*sY?R0}w߉:9Sw6.pt@T?`w?:NPy} `b}x@;Adv;v@jov`rt9}|ױ@o'ߔ$؟:i/x [G0u +ߎ v?8Nrz^`&GVH~^fQoY|[^(:1Hn[9{WΗ;wt.v>qww0u+K}`!{.kuם< 1A*{h { ~xqhP9@he.ĦD] ) 8q(SW&Su_M@1xA._v'=Mv.`w*[Xd<5&H.\_K6[ 1w[^`R/x5z}H; 0=sߜOl}SP ;XUx=@xN [e~;=qoz4 ?k/L[SG z>0ӣ\nxA \u&>uNe +OM"]`v`v}`hw[y{/1N _:M9=T1aCU7oc VHԜ_}q9v;w< L?]9`)\ ~e R:W;=`}_ߵK=`z|-tOۦKϩ ~gcv|Lo~n\:/HL]>rIXy_t \ o. |qw?X+!GL^u$o뺿Spu<0y@ܝ~wnl2NqH캿tGL?bz_O``S7x=`gzgsOxk| F3Zo Z +ٲvUbQzQlBSlPQDDAFDF|s|`soqBGOdg;~w|sg=v:ӝ:/ 7.~z=tq}rq{{>=;W7ky]Wxsy?=9x>wqֻkZ:׉;?=.;w:7yz}}8oo8}+<6ӓx6Ktnϗ/8}:_5w|M<չ碌uN|ߧuxǻu׹ߜ=oһ;^s>=O8[ߝ;<|7oߺ8|p{C\azǃwӝhx w/?w}y7ޫ';'q>dzxSy0 c'3?db}xNl1AT2s> a~c#s,.c~0y޺ode~;?~2 T9b}.w^u>1sY;JYc} DƗ+ַ!Dއ_<(c;1C79c~wGO?Cއob @Y̏>C{| :xxw >6p@d~=N܏A~ "SI;NY@L2=-3S?qe'/~0?9@~w@|3⃘d|S5mQw9r~gvbU&P1&\o}J@=vboP1 \L;CpD@.;sC2?<l9y? t?N~>*s;w9>^ y)ȹ Ŀi-5|)\\>~S|).ٿ<:cx׻2U/eM,ϺW?W΍]9@fbrΏ sG?sac&*y?> P>~;2sOy}n1>|0, 1s бTy@τ!\'ow~Ho'`d9yT.ߺS s*tRWaOu~U0Mc7_K@d]u1؞bSw<]e2 r6܏-@D6ݿ_sj0{m2^z=`]w!KsqMCPzj%`vY@\`"sns":*X |]tf?< o?S> @>M{m7~tU'_U}wǹ;-??uc3*?_̏_''Ot8, f\.}ou@V:z 8>yKl%}b ,y@`l`#e;`TgO`f| `da"n 8>,Vd|Y]Ou0~uϹS5 b*Q/m,~Q?R8U-N0s?\k2 Sח ߺt#| *7y[yA8^fND&7}d2}h\ ;lr;X=_L.]):j/uD=NeqfGy$Ȼ8| :xߔW7R}*`[9!}`ye0;ٟsON@~rYY@fޗ~rK[: n e@-캾5LzA<`} T?TYno~ wZ/~,`?u`qͰ൅Dm%Ǯ @c+xs~F<i p;u~ 'g ,MPq@6S6P1ǿs={6:%rl8`& @ X@q{SOm^&|&pb "8fNgqp' rp f'L ~T0?&ΔS}uO9<`~Dv's.m ;LO03>QΏ <+\TsOSn}@NΏ)SP>?| xo\:`vy?&`r3r~0@ 蘟:֧:5Y@7@~8uu(8d?NG @QS8u'qL9@f/{?Z7f'rsz9 jy^r,_xNz)wTo)M77R/7M mO9܏p?ܿSY +Tv|\ ߕ T.w@1?d*z@D1qPz:tp0wp}>m6Sb9@.]?ucwߔߛb7RW@RxtoqOu\x@&]`N?|/ޫpn; `dwvsMi8@\89@TOq@K`$< .o+r^{d/{$l0i~){ΏLP 8`N y@~c&?'prTx&zܟ{iy{A|;f|prKj# R0q_>b|T7`q/k$etߗ.0Krxp?01])0?= q-@P vfjOq@|>&0:< xxߎ?DO}OOq8v бx_k6nM@d~xwUY@v~-@}k6=q;2?ob~w&6@|s(/ȴ =[߳a l{;< )/2~S3r 6wU!6/M.`,4xN׷b}ېs[&7H treNߟcNY@_~k?U9r/x?7Tw:W_6}mwt__MyC >K!s72 T\T @vL`?,rpMp:6.v7q?[ltvs=` f3?ޯwYeؠv']`U}` >?}J\= sϘ@ ݿ.pSnOù ?12,K prNMJeU 9^`MY7D1*8@SwRXyUP1~M쏽uǛ`~$%Vn0?~0TO @7er :< iTX>.C.|qO_1N7so^ )vN` 2mrX=@;r/ߴ `%l Sߦ̞Qoe :7ot||=)lDe7pr(bu~]Pm*]/n=^9X\ n P16N`Tߊ^l;e~7=7M]:'zo-s0v߻K]`][~&GS}& 8MtwwR`~;^Oݧq  s?d'79@2iU=`V?l_3 x;v|e%Hb~ T.fw_A|_b]_<*<.`qߴ sߴ_~?K]`Ln?qFLs.0n 8@\wN96c8m9؟xsY =`t,S~M,`)'.]:s pkX lT]K%%\ \!  >~_f sc蜿>pt;*g= ~;i\d<| PyAR{|&rX3x3@T=f1@: t';*l} `_u[urݿf~ n?&L 0u}S`(6vLhؠ;S/q1 xt|[SPio'8y|`;'u/#{wߴ %==@m/XuϾd|Mۀ߸!Cq?<'ǗttPY@M9@tN`~|L1ft?fPO1|O~tÿTmc};G}8'02?~w̯+I9s8gKB\*s$8 +0fNx^\ sc :6<6=j_sN)秺n7/r ATm{e踟9ٟC7TW8:'}a~1@<'t\rtnt $9K7y9~T_TYvO KY@@\. rO.0./ ?xPw{ b91=VV?| S ؟wmU' 3tنx=%Ȕt9l:;\%₊%m/u1 vNwN | #L _v, =_U0exdw0/cvow%76@v9) n/>VN`*s*c3 dwl*~De8m&]|ws]`=u'pT;t+ȃE T.dc-CΧv\ @dzN_;n3ۀ. x ||M,S}` tN+]@s/AZBN`~?,.t`x%'0woTL=;eyu~==Ys$pcN0eKslN_Tat,bODvXS~]׺@\%?(8qox}<}x@{} ^S"8tR_c)zk|nyluL>/k \.'Km躿-Cz;U~t`*/w'֧:.{#ߩ|`tuX9?]N@S^S_r'~|>\>Áo*Ǵ2v=g7xs_f2.f~aPタOIeޢ @ V_Ty%]`%ȴM@v)ޏ,XY?pO9‍cb<4G'ߧs=}`7wZ'H^e/sA<y)͛?O9'K8A\`M[S?wqSmNۀSށ-f·O?ߔ+<  o2L^f潿t tlN g`iY*|M0A;x/] 8`=m8eIC7c}_9@dܟ2+Dާ2,/mN>quy@d~) zN?|*@T]`f}*Nn8`*<<X@xp= D9@wY`b~*wc'u[m\)x _qǿkrvY;α^EFw>0?Cַ/vTw;i`N8]~]/e'8 .wGx/e5?$e]wSx2kvUx3? |(d6LPuC_ 2Ⴉ<)8uC0=tp<3seOi#psoy@o|8l X wM@.t?| <ړ2D?Ns7eN r0]F?)Ƈ|9_T}_m]Sݩo}&ݿ, ;?Rs ,pz|~\*Y 6`L KNU@_P5g7u:.8uM@W@\ XAnv[ v}s )&.089A\ur- Du|{踟z?Nߖ<`rcn.'{oI,01?I]`{s9<87)P<9dާ؟z2UбAu5[/u y^@< xnΎm i \NXyk~N`~w}}}*'e}%L@[i mNu7 P ߮`Cn`dz<-@?cm]i u] = @~6z_r(Ȯ񁤞ZzAT'X_N;* zAl6ws:We;o5;:76w) 07e&T}_pr}Ss ?L]v?Z̬T__2Swl:Z?n-@~>?D@v8?prL=` @~.맲nNds^& ra5 4`m vޏ N<^]Im)S` L{Mxg/_,S.6nP]7Xq@{ia/N Iޏ 9Hn\ عBkN/?ws*/Yo7 )ȼOa;ߴ{Kts?]-r]?M@<|e>[\rdv'7w[)1AU}M7T_MN^}Y0)eT g}wSpt>`<恜{8?Tow ߎdk 'sNeU{{|zq@r~8@8غrs_Uy *6v&\bK?%2es;snKx?.S`'0>9t??u7`taQ[Ov$G⁩vU7@'ȓvs1?f +;7H~je`r(r&`Vq?`rǬo2?Tz@ <TO6si L 3>~h;*ry5Gqpb{%~qY Sn) ذ@|s~(֗:9To–#m:Gמi p <Y?~~:H#K. d~ 2ty@b\v;+Lsy?uv 䌠b~mϱT..0?\o=>`)Χ1~i 0~'c1T̏Kjob`swLPm* 3s9,i~rT?^;?u*2d8)8_b/1Af}9?T޷|db2]` | L[*#wy&Mb~.v?w?{Cй?WKbzv|s|:躿X.2}t~:?77] ?%U3~ >f.غ?8 "Ky@v=`K2s?`~/ 0q;Ƚ_f~sb} |cGN) y?ްf%.?iw~dr~L[.!)K{?0 fZ)7gl <X.Ǽo ̏] %s7_ v u< n4 ;ySp'ǹM;*2}`T\s}qO?n< *ob}.s(ș̟?|t(x_R/f=7= k$]P6.`< r)r.踟'7-@c7} 8[//@ @w;? _-sNW6=@ tyfk6SPm~*rUܯ?wN2`d} 8 ?D>wEN |*OI[z:?__r.sL;)7yARjO9YwBPu~cϹ@r@ nzsv`v\/K?79N2߉N{j/mR8N?Tn`Q9A:p{K`2nOe+TI 0m.wm MOYo2n>;s6:m:/b _r:%H=@ ܰ?Ü_/?U>2M@K<o~Z]/xFM> @}jp f.r'03i;GwONݹ~$z~`9 غ@`r`/@Yp`7^LO5@6T;6kUƲ\B/yAk| n9S?lW6Out88@28@?psR0q}*-d|o}{r2{L||zL?7'.~We z%\@/p~L.f~.am:~?\7{6fޗeY@nrTOed/bw _[r0S}ߩp߻g@._ .`v>.r#? $y@|o`~tU=@fM89?vۀ]u'H?d[#\ ;<0K! n/m2l lYb|&-9:7mN@Ȍ1? 1%= j P1C6?bjONp&SNǽޖO{-T[Q_UPeXe q.s+]0@8B1T_sݿ\y?Q[l]prp>0qω㗳^0s?w_nSY +V_t>`f~S.бSs\ *.xsr. `N97p9pixN,p1=v}. 2@d'pNu.ȧes;=`^ V.`z\&pRPy{is }wK}`n| 'xp  . xN9@N`fSt;`'& .|ݿf88@X C7L>pr:Ͻ`nvUMOu0?['NpOߝ3;a6{;'t~L R}y`0K.]_9q;|W Py?7 X1A5<б6wnp_XM?v{)粀*.e?}L`m9Sxб@}) vTF ?V>`~2lxg.<`t3 l*pv{sy$9ߎ5y@Mw*8 rzF÷r.&8:Ll987+xVi߿O%6|w9fOn/y/?f{m&PmMYw;'fT=vA-3jo;9519A`~f7m:G`/pP>\pdzb/ܟtx".u@Uw`#S`_ၿlO1@sy_Oݥ=ex9_rM.jPm~Pl"S\&s~Tuq9`*)7exz~jp~-@~4;!swS&Uϗ;*p@f7V,0;6 3@d^O9GO_K9ذ?drg1UO~qߝL.`~WY?7g8M`jut?oorr&P1G.M'#e lM,I}ޯfSSϔ lwZ'0@)r<.j Pe~4p@!sos9W?d=`V]̽ߔT_ \t +nUV܊% +*x@O "xA@9u׊^#:#+3ߢ#[c__>?7ӝ~=ߋw~xٷ;/u7>Oty_|^zo>:wϛ燿EwWx[{>go:gx;ߏs .x?{:WIz_}3Χ\IϞ3\ăw=?~v>gy:Nsk;>_Y_'u%xw<_'sx|>{^z_Oxnw=:7wMpnz_;>;ߤoOVx߷S:zt;οݫs^wT.z_=?׻:wt9?|^qn=>+Ϟ?uz~x;w<'΃gO<{'s;y4|?v\u5y8y|=מqulK܏ߗ [gq|x ]p?b{&&Ȭyb|xޱ?f}-w + dƇÌo:1L3[ ?u~ 2sLY>c`}o"l2@d;~o1{뛙b}(on=K#;d}xǿ%&z!DYSPo'2yr|=\^wX: :x_\Lٿu??LA8e|`\LO1@~k k8Sa0q?us c)r)6 fw|?KO$֧r.ay_b)ccߗ;cvSߙr@COu&r dx#!s < 3sO ~-[֐d·3cF߷s~d1d8b. <l!S?|. x3Ss/1@Ι? z/㎰}*=`dz9X^0gTн#S,ٞ~7|X`uY@f `s.X r?}7:[)X >]z7]^}x0>v1@u G>5?qM׽;.z9)71B.5!8s@}s=r>S,Nyc_oV`;6`Yb)6 0Ͱx|ob.zO~Ad})ry?wM`)as;98 w~;om8qlt9w?-{Tx* Uwqw.cOKRsTߙ%>uobFƗ8l~"Sۀd`k*)Ys?Mu_b_s@~]ױ@Ly +O2 L?Vut<2 X?wf|:6 dl:Gm*_"T>CM1@[3|qm`}>q[N/eXwU')?$e]ҙM\q@w>|N9ϗ+ ܲ2 =@ibPߪD?f _rl6en)b}7{_V w[w+8@8vHTƜ?Sh{&M@/rAS =0S.躾);@*br(.v:OllL`?:a7;Ml6r8oM/lo L]`^}|p?8xIp@uN?T?'?U97\&:\ d?p^pv]},b 2]or7_^0[q @M=@_+6x  xX_?ǎ!7u)G\ߪr~lN*&Y@rw2crLN.&v?\Pޯ~a] | )Ua D1fySw7s]_0r`O1Bv'@9ȽSY;]7o;{dyO=xb*x@K) < V_}'dd&l2w r*~SPyNi 3qWT{ R_.: wzi 0d~F @dTF] |߀s\sߴǙα?vq[r=j7~f粁ߒĹ?&c.~._(wyq7ZpSP1?wrq;=`Իsq79b,`zO`j 5]_AΧ|~s'?&0fR }rͿ +؟bs9@_L'm@ \ +v8@ t]ߗf7y@d:MOu[wT0.ppߛ38yA?0ߧXy?~g؟b8:缿`{uޯjN :HV=`'aUs*dvb rϝo2i0?n^q*?M[ 8>S | X >9U߉ί)7Cၓ˥xO ~Í?KO:߇z_nS>_LorO_M]`1L)8ux+c |7'Dwع?v} 2F·|0/r(qk_o9&غ>0'/fe~Vvw]6dqח;r?Lv恜wL^KYޯC8y'Hr4@<3'gUùvt;'n**prX1@~"K^mz?=~81K]`gO4=K 2so\ twm*S?UG@=C21_bu6`\\/m2ߔK`n=! L@2[wȜu6T;:@._bOsWugk 8UwT/uSϔ dGro/rF7ror(:;P]'|rSE0)'{&;u'npP0_< )td mbM'!xs@x 3!Ds80=d?t?nqgٿuv]7sߺKUN,pgP?\nOss~r@:*z0q;|OSxw} S1@>U27=@V9@*u2 .`1#t`DmM@}:<5_&r?VlO)0s@U/uC|v3]`6T bOeSx) f\F~KM|/m?!:&p=K'0gT6Pup: fl{.7mܟl3;;KNxs@{dU/7eT\Ϸ=@?s?Roo>Pǹ<Rwbx*:R8{xr}b|;=`N?uq_b~? t?/ To​tN,f:ܟz9? Lی*2[e>xTsq.Nfo<[8@T; \ *0@ͿnyzpǍ9SO9ATTLٞr>Tor Ĺ]7c7+&< <10 켿ez© ~_U~`|j "]`\2u u@‷^*&rLN.`w;ioLGw xqlbx9@~ M@qqd| ș)9dί>-15S~?Yf&`I0?TxCݥݿg="y71~v`n;9]@^sm.Tut< };=uάy=@M,O }.M:7H0sf p3;e"|p6ۀf8Au$_t_d \0WYc7uob) otzWV;,qMTޏrK 08>ex s+8yk] ]8 mOQo:t/2ip5_:y?%G~>_EW{x@[!kM'sN9@H}:Sx+px"9si#0 +yn_V,m!K~)\])V.`&: ߩ[ fCm97k;S/5o7?>f~0c7`r4?w2Nq@Mk6UO@Ty.p9&8eU9@+6;6f߸ _rpzs`t]`w0TS`ny_ L;z+xzo=7`O+輿 ty Nu[c;9޹nN`|g槼)7?=`w< i8us@{N@t]`wQ9T;~7*'p 7'r~\Y@_''H 3?qPYfO(؟S'wj=:3qϜo0UXyAox$/0gy.p `rs8@M2 ħ s&!SPy? {)}'. xCLy@U`ޑ&t}@|@S_ l}n:~W=` :k\ܻ=S3@?.`pO_]1? ߮u {x92V_xh:0ieSw>pb }Smi 3.r/K&yk2Lb .` < *7Q`V@|g+x*l:f8~ D&xYq/2>_}~0?3]iryPma;pr @c8@\eW6s(oݱd;bip~7c|7?\P};/;&')xof~0r@N̏{;o%.bz 95]p:2ٿ>xݫܟ;y\ON0xT8?c:??t?o~n01? N}a֧8 z7-s{ S_Syb~Nu,P{H]`<|p% ܰ?>8` <- 8M/SS wx?oaot_S^`R=)sN \ϫBN;K_v kJ 2k2.9?pq@k<?t_l= ;.m⺿DOm:X %/02 _M.SY2?Xu䮁 &:?K,{}ۻW>L=@f_Dη)q;&89DAcdz"[\y{@竲iP@8.?_aޗrs+o `9A)ާΜoty@SBEo*ș?)ro< `7w{ww ߜߴ O ]md:UXe9w[u9~N;*. vRחvrޏs0>f~;~S{ ?􁨞5߻ +wg~pO9C]f!8myCq~H?Vnݹ ? |:}u k\ iq@5[S ܿi @0_dv\ X`b&3l3 ? D79?SY@p&​.b~y@ߔ'-]@]b}. :U8es+:@0xQ$rX :2cpwr;_z 27 P@NkKߪ뛜 De +ߊ3?~O*pr02@M!{<~R6r- kr-T̟fxb}0t;DS֏~MOu}OqLO?~vUn'H?f؟s1߼ :OMOu1L`t`N V>9`*lS܏} ̝_u&`r+T揹mxu t_:2 s4r* 86븠Ky@=6/?:?/_y6+|xbo7n/qU#K=`x 3?U0~Y0A䀜 |T`NK68_ < _u? rff;'on w* M:~wcgb}= ?vs6fpT?S61lr.0 @Yuo]?L;iOA r,S/y]/9U'Kr׹,r>|SO1@P^s`:ꝳ}xX L{^0?&'p0S ŒG?pfޗm179 g&6]X1맜i/I ]). NSv[Ǵr;9Ao#rL@FoV >rܟsOrGr W̟c)`Cs@os 1 ߏׇr+|_jOu?90 8`)/&Y@I?&ov.q/c7[8 wFf~]5]_t/?:*ǿ)~ o1?l3p@+  |CO?f T|OswM`u'v? V,9`'&|Gon4`|w9v9v o q`ı@6K@tng,9;q@f/m97^d/u'U/]X gⷔs>e; nܿ!K;)r.iϹ?o ALM pNy?OMm@.wF/1 -6q= rb +Ǜ:PeS/ر?_L?@~.7ty@OjpswM>`;SS/ &ߔs 7Pm>V2 2L 3@^6[:Š=@*no7|Ss}q]`|_fO{s<s'98yt?V;<.]DFPM^|B}%qz7Nnzs+}28@m5>`:X?dwn0y_q=`VNģvb~q}_k:7mr0E.ޯ}'ew3aKs9g=@xw.& ;s> ^0T@~?X9xl_\w|;`d|{wO @~ q/@pU+/>!l#to*Zb~ 2&0wkٟs`7_''tX1?<SUoi ߋ^~Fu\W1@q`uN]`)86w3x*7?N]`!~}VjqM@Eݎ|O9@@~<`d.x?I9@QSp@*7P{rS6O[b~)rqv8xߔܟ OMf] 5N%_\pM  p:n_?IN`}؝i q;s(7s`SjpTG8vP;M/u_㼟sD)=ߝ_v`\ g:tu;*lOqA*㾯}y@%'pQqXm:%*ߺOY?φ_v| <f6';7u-@S˥D1 r`)89'6>0vߴȌϝ z  2@ F׸s1? `S;v񞻾TO}{v@K/l;af0y]q@?~z;.`OEP'MM}`;]/9&x|x:8?={:l.~to&ߔ=1DOU=!K_Fx`rW*~m} r؟0n&L*秘 oܟ^wAoޝq(_'&:|?N^q:w_.!.oNUv` lbSpro|=2'0\ )yün멺) T)N]@|ؠT'|noߴfN9iυ\E\/_fm7(旜 Mam'~D?z =@~lN$VNH`#9~ s|Xob|} vz_xXmߔk;WM@g t_d :H{ L9)_ L`UϽcXr8և_d}%/pr0 d&|x879@ֽ!SorQ~`7p=`ߝCחM?C]r `⁎9/p?u 6Du?s0O~w=`v9̼OvLPy[y@?{,8s}|T`w2uOk T?nߺo|6uf} ~pdt=@n< _} 6oϤ]"?Vf,s(̟{\ NMN`ꂽ_uX뛜 fb~9?,)8t<Нt 0<7@dy@j/ T2y@Xuqy s..MٿS:v x 2>}[/ߩoU& 2e^,6'|s̏٠sr6йQ&؟oS{ƍ?2}@ *w]?8'ΧzܟDn/="ߛs @wvxi3<.8}`cKۀ:/Nob+XuS]`z16<ߧM@{vo-Y~Kl&;sy)%'H, &s`r(V.x{O_f~n9?~`:_\ U?su3c;s}`.0w]X@ane@s?o3sfϝn؟c̟̿bM9TlP?TNb =@`Vw߸@q7w ߊ?M}`707຿ OU p3SP?9YP`j/p? ![imL {?Mۀip"L`:.s+8ݩp Q| NLLб?{7r;>}`8p I;:3n/3@d)l6ޏ |~S)ߴ&>Q]ߟ_>$\T@ TƏ%v.N<\`n2 Snl/g]6w{d/c/}@vߝ89\My@=?/粀Ѻ +=`f~ b/@0ߧXcNqzOe<s"8+*x*t}`v}_z`~b;~`7;;fz]q@*׸>-@V,P7Yı6 T.P9Mesn 0qS8euL\ -__]=:jPeI>'D8@v;| csiOqW6sk >w| ;t̾2MWeߥ;r /?:?S@|l_8!81?I?wT <lt`6=2<1V/Swys?^_o@m }_uiSqc.wk[w2 Tlw {18M}`v *fM8M.7_ |p':2'sq S.ޝD.>yߔlܿ;%]_MQ0&G>w $?U1);.`qA=~Om2_5|.l`eGxQ703we\.l9x܇G2SYVĬ{L`YoA +c1O.s;,a9r;9A(3v=c6Sq.Dmr'x6'MEeNpb)8[\~ < 6S;el -y~&<?=]ع@T]`i ?( ]&;ߔSy )wJxt`~X9]/~m8e䩰 p`/ù22eܟN;Ȯع'GS^Q͡ 69:8 e'xq>: +o9)G9u_sߪSΏObi1@f~_Oٿ]u'Hc8eߨ? ~Yu1L2i;n-@_;?M!ۧ͢\ 2SL_xs[ zAt -X >u woqPeUW}Orp_ĿU}0?=Le꤬C {'pc\,Q*iRpL.xsKߩ\  <܇, ARe=@&ȼ8r *b{wzw_Y ^{Ώ) LPk7| V _MoN;:/Hb R[no/>5 "kM7ek] s;0qmN]w)u^ܰ?| #ba\ vz`8qi/yC=xQnɢ DZ/wS^ N;#̙? 8g2Ty? \`Yi ܏M;[~n 0?^>v[oON`<` \ ,\1.w?''/1ϕ@iOq)< *6=/mU7mp* б@moߍ 76{03>g< >1rSuc~ MppM9@~*718`O[t{( >`盺==Ux*0U9@ܞ 6L`TqH'/c};5[+n8qc/ߩ_2]`9nP1@IG@lo^u|)x5ݥ'ox5ޏ-@K[d's.p glS鉶ye[wzMw7-@z lv8n+_Ny V0@iMx =άı@ @fj/?Tp=u-t?ve9r ~6r̽_f~.v=jo_6t7&r0s<0 ,aƏoMca&vU&P@d4^_ \D doz:ޗ~̟bt8< V` NT]sL0@.79oA?|2:H,:?HPToTl  *踠M\PkO`&r7Yv J^ LYu$:+Hr*/mrFeS}S{ 3;>̟<2;e5N` 8{ [`N.`+0;?Sc?sy_t69U;o6`pQU`+| .T{_F.:ld t_Tw&*r/3@]Y(_N;8p@?}vLprܟ*/p{#a @f⁼ xJ=/6q 8  @v~__T]u6^I)}kK&k< v9A@w_U.;Hn#~V|~]e}[tzl >T i*jo1?dn'ၸz S/q?r-@~.`S}_wVU+燺o )y@Q;; Œ/@|̯sq+s`D1A9&m7&_m@?F

    1`r$p+O&;?\&1teSwf*⁜k * 6O:7*ǔT\P6gq=;o%$c7.ໍ#[6 *`d`‪;?fӢ~0K>u9@s}#<{V?wV89A=`Mr$ X6y qo8n#~U{h) \PuɌoMM?{}o^ |6>`:~j *\OZ']>0?!6{r"s\eX_lzޯ^Ut_-mߴ_M)߷,`ܟrO?UvR K>`T6θO.'8][L gwrHTY@v[;TO*'HrpyLw)'s#K֟>\0@ T\P@3r@~*;L~q&q@~OpQL`[n;'pmo::a7y@`>1{^x̽_})懼m&Z8@RavLe +rUUE;TA$bQP-&؂]Q 6&{:j֨9T݇Qk/4j׿ (xכv݂wcT?pM"sUmΙoL=M[So;x{ްa&v|{v᭱;L~;{xx{c~vcoo/(/!dvve-{^o|;>l}YG'oaj4vdzû66/l:_h&ʦr@7ɽ fڿ2;W|NO6v oNˮ#&ج\7䞜_*'p71B*sܟvf~tA| ]=3z?\ ݟߕs߷/> T&Wq+|  <_{/?\!s{>/@zAw|)NG`rs'8%('鹿UK/+\y=莏Yrr|~!S>o:c /y?u`Ng \~iU|Nn0y?{ޏ$>sW9A@|`rt)^y@<[q MO=N⮯9Az>7}_{Yrx?>?z+%GXD]qK}}| ]'ϳ`d DYF_|[;l7t:~S)w {t~td=S6 uۍ_rUy/AwUq7_rdKYM[w{Oq}K=*Ώ/A?Ϗ/\`rXyt7~_HDT?~|_|3~;xzFsǛ?z@K?BW=/8[ |kc|RP^_tS/ܟ1sTGחtǬ&:Sܟߩ;~=H@vLu_Uί"K.~|8<;@91Hnjݿ s;]_ӝ&wqV/L/e^_LY}c/U.N`t}fok)tϙ3 f>@! +1 Om"! +1 Om"! +1 Om"! +1 Om"! +1 Om"! +1 Om"@ +IeqCam4-($ 6rAѴ jUPP4`s D`4Z,3u{^8Dsnz_p.3CX'K{j_ϭ_٬mdz7?ޭ_錴$NMQl<3wdg'nvv:/]X]Vԏ ҹii:==pvNmm[H7 tk%]]l{!̆tZZhz:mNU/֏ϦMP;ݜnQr;}Zz7}73tM$ Se]ܓ靴=oiO)ӞӮ3^NiY:nH;[sv"&rmmlg0j:,v:3v}&P}?kskj3]Ns7VGGlYg\[wۆ{gvi}ZT6m?ܝO3vuô>DO33 `T̯;ku/~vݷm;)i]zƩW3]u@FAwiݙ|H_wv~wyÃ\%WpW]_7wk.R;}-uzq +}_ yS1 zg4?S;3i}8ܵNogNOCޑva?e?5LjXژ3M]7ҪzwtszLo3Ny&q" +yqf!$!Al /%vZ˨Jl UKRK5(բCQ#k]m Bo{|ϙI{y 9߹=~?+@V+(( JcS<tIm-Qʬc?x.PKuJ'e寉YYLP*rw)B<^keer򦻸j7U3O!vGwLZ:J93ssjpط\Qf7NAK~*/{>2 +EWgWU?๿W^Zt1owr{GKa<男~^2>`9d+9[̮a*fWF*V|]"=(1緱rrrgerr7l? ߢ+Zίi|Q9DRg}>>fݗJ1By~l@<q }''5W v.37xw%>Ox=;|wt VVvRn,X<.{%ΝEGO{.~]dS{Ž]N$8\PWZJaU9ϫc@@vA'zk{w+rߺĻ䏼"oY &+<_SeqG1_Y;e{w*Aޣ .qHVzGa 9 jS7 .~w7uU(+$T>3)"~W^WN,pv{\g[ZξJ0w>@N񄲷ұKgV{+*+_ktG9JFY︣;Gkۼ+#YnzqWŒ{Z_|@"w5M̫]޻W#}vxaϏt+oSeR4reރ=xGi2es݋?r_'徻n9w]eʧ"~oPN=y{-Iec>eFϪ_JV=uߙyyG]vYgtWĻTU ߱/O_qMnԕpy+e,7xww߃.~"~nR63@Fww~=>ssxƾ)'nnrN@}|rgvV6>>ƻ9Z.^kؙ1D&:ucZA,wT_t`}W=)Ols]~:er\DrI>)cr{;u +w=}Q[i<shsdw;crğ9wn&I9y39KT[C9By({~<8]]ԥ>^]ylʄsRNjTo<*3Z_`3hy}`]ygp)]htXεq7urZ_RdE+7,xʳU4@yՔ}VrS}]Gs3oe{E,oU淚r{_>GY3G ܤLM@ b(zJG2;O9OxBXg= {خU>H"K{p2Z9[[uY {!7Qns}xw}5?r&VVVP +:e ܷʕʑ.z3YCSktx2Ƚ"*NT.V^l_u"iq?|6x5N =Bwc:]ʅ$eBWg/-v_Vr'?rn^_T(o W+?K@*.W䳷/RQF*=;MTNglek +sپ#8w WPvug;(m.>0^{3VY<pAy(eszSm sL~OM5MV)(٦|GS~YoW9Q'ZR7[yU_?\mh!=g|ʿ|Xeee / Jq20|C9]4ҵv=ajcU}_mlNFϮK963TySyFgwg㹬N\^b7X,qg +ߝxWw 4uyTnE-p9ϔmmu9ӍC|Qe|[^ 7gsMhjer/YO8wsJy.qve" ޶Rג6 {\[ٷާ{r;NL{yqF';;S.W.39'f2y?od@8Sٯ YBX#M5Gs}n(+'*7xn}wY{1gn@[}ڽ蛉#l*+(+s_(wxc{nw޵=xZ=u* Wv=]n\s 3-72R_>?sy`%'ʓ})7c Pu9s/$=jwM;+W +=_IPyƟ%@ڸUPyQNYùϢ 7݉}]"sh+|wa:ˆ0øTV=|ss7jw.7QRGĝ<:rsSP,y" ^N[Gf_z_c>{:]RRVsO{)Ty!S[9;QTs\Npz.qi4+XɍJxNx>;s|);U)W)w*O#~pw] wwP̤m\빹g3;`Eb:U}X;>Ĝ}Kpq=%z9ٿW)8T*=їm= >{ŸӽcwV]<#Z9;3]ZzGg=f-xuI`aN0z|ye_NEǑWv>3s޼=U lRoq\>AgKy޷^8g{v ?Ӓ:n{u _;{̃S+|/^Uҵqo33n--OQ>~iC޵t/ۻ?J3&!27cr2*Q3A1{ֻU[rkSSV +w]5'Jt_1U{7t,r + tm}vQ!BYv2 g)dp_t7)+jȔXgVrt v3;zr9ucqN gf׭𮫍w\gXz]F勄^|1;{6ʩ+s=ܥ((M(')kV;(EWsy/kJ/S8{ʭ9-}?]nޯ[lʽʼw{CUA' +PouÉL}e2;(EuRP.gz.s@m\y'Qw)߇<ijM{ݟM~c݃u.;ꩌRsW{?K>sS=]x&z;.%}ʽoW-|beOߩסbFosw_+"z}wDwĘn)Lj{_7+z38;sqfc)HbV}/^9<bk<)skI{㎏ʼnk)'*$LW.UWH,1gzF⻙EskS ޷Q޳#~c]>K;ܬ`2o뤒Òcz/"wJ3WSN{{|[uޣ+yeQ{3O莻Bw{͔/;w*?3ѥrx=|3s!::wxMMgxSelǝCVYZ++)d0;>=/vT{!p.joM#wx12џ_ߢg='*+*aʆ {8<'Mܑr[iSU%q6YK7 +Pʞ&ޞM9P9^0 ? ~ltwQ+2wJ9 +|v '/=<Y8xϠl$@K5qMOe諬?w܋>}:=mcؿ|Y[L8 .B,j/$Mo5sror?J7J|)qF:/>{_[筥()smщ}DG_\*׊I޵3r:AoT8 bk6yRؑA]w{5M9ϗL#we}yᎺ*UL1@%koWBjfR0~9w2NTnM3^/I)*w5Oy@8@9ݸ2-!gݫ)|cNMDF&Wy_K27afuifeWJKn=N΄L"X~6gms?#<(smeUWeV'+v7N}iYu3<־4;kV>L<׹kÞ4g{WM9鋔sxNbr2?9]3˯{T>r[ϳH>S^]Ͷh>WOgvSz ]fԟ^gp9W4?M_b+5iANzr7f{J:ʵiNH;M dz@goN9]9Ot~mXo\v]k5͙3iCҷEth|?.73^RUv׼柭]tbֲuOϴ +cӍQ:w5gONTuN;߯g{k6M{8}/]^fpjٵk?Ynifzū[ CU:Ο JMפү{ko]Ro?\'_:6W Թskw?^zrYњ utv{Q|;7ħ|6Qw4@]^H?IgFK3'oxzcLzmOVA7tzb>9سvan¯JHg;)zMs7t}W:t` ԝӕ龺< +6Myi:Kߖ:guUrƊiBoO%;`[w6⼡KKz7.;Ts `v:7+ͯw|Voפ {.Lf4\l@/j2M>[cPiv+{`z:.-Jwko2[ @?HY鮴n u#u_ݏY۴w?ݏm?oN$=_u.LM-|K?I#GӼtQEZ_u`ТsLlKv~|tHz{ݕQZI{x tn҉鼴4ݙ=yN7;_;`O'kG4;~ty8vN^]_m@jMOJwc`ߵi:@?M_H+{p + +meqn%[fH@"]7++YFTHeF!rYG'QLe305gN>~Pp?^/x?٣ٗu]G0HsӊtwڛKצҜiZ0{Lӻғ5;t}Z߉״.0Tm閴'Gj0$s޿ts0D޿MLWf qgm\^bq\Mw{'ұ1 :{EtG:7⛿};To0I @,<޿Ս mtk݁Y(j3-颴$9 Pv^{&۔J @M;KJOw`N7ʻnHS5i{w=c6[.HӞF5]%ww[鑚{N7ߤu a},>n{ 7wEu?޸gt麺밽w }_ӗӶ3Q~;^.7?݁ܨ{&ݖޙN@'7o_N?NOg7)E쯽{qf>mO:0Ի_;zZ*%ln2M!ϥҞ=Ttrr>{Bh=u0ݛ>$ߥtSzfbT Eu[{Ts?u"}{󸙬9>֧U=OgwhwЧ7?VϧdmD~ZNs@ޓn;=ڼO钴<9 @vV+|+4ssҫҺ9_;|i: vuwm{hNӢ)}QԴ>mtY"}2v~[ϦM?o4nKw iU@_{k!=\g`=GtQ:f*:tο?ÍM]`wB89;C骴"`j%ρ7Y{]0b +m@:Ё8ەn ]`Ϥ? >3򼴠汋j8g8R{sI,ͩ9tR:=NzhKvNIפݿܭD tiڐM қjx){gmyNl#W~j6Qkڿ;mO;u&>S;^̂:k{^Wyf+}?כ`Κ ~/K̰=@^H[>JCcrA~Dl屼fC/0{okY׌xF ΏM7nݿpSG8W7E>~^g|6ܒۛ v8ܼ:?'󧾶v;L]H߭y㑾O_9jBL  + ܛ!Y %b%ՈHc2{%!*e*"QkXf CVQ>,W(+;*vM0e:}|){(#w][}'j(Mu~wZr 249^oktejoexe{5nU3EOۆJO6eVߙܣW_e2ZYuRnP=e5rߛ!]{{7e^75?'Z4hbt/&ר mp6RTMto%f'))9vT6SP6WZ+U 7OPf;ې5ʹމy1{B]׊KJ{pc^7iH? X鮾w#[ya0y\;$x*+yg!ʣƿQ[9wRUWܩ\l9oj5vE޿QD6ǛwTSf'Yr{[D>A9ܷdeDob?+Qy.{bvrk a~<,J0>o渳Ղ{x?2>uk\;5{#s1G8սcwRz.U\KW+C|'޿h=yvыPyIgfM hC|cW54DH(M]cn$轌޸EyĞn:w`V0kznQj P~W`#y8r׍yߡ[^ \;+W{ΐUSzP?u.j>ֲ&7<#^}Pݏ]ߧ @u=UyAawUԛ]ϻ_;CVr9[kq1f +?רR޿^Ŗ*LP]¡rjݧwM/@u]LP]?7Kǜ5?V%]}S7Zvi vpƽ৔TZNpo!eߓ# ؝Ww ?ڣf47PD6{ǘ}P]k}KwqwUlrwEdb6jw?%rBͧbG {>?*=f +աƵ]kx5{re;bsofevOޯWq{(++P=mX{kM>VUm>==ڲ|SPFNg1G(UgB> +sj&Qw5Ҽ6׽5ݓ=w//ny i@}ZpoD3jh*x3}iQ97%ߛ}o/jmS1,2ϰnɼr tqo +;X3lWݏMNP{ zH#v4vWQ>LԏOܖ @W`GlAUZ*w+'u|#돛# ]DQÀW@uX=F#|5k& 2ijE먜Lp^rz+Oo2zg(7)(̀Pj} "nB<,^JJ]Feςn%D_~3 ']r23sԿ}'~@u 5~vrҦ{&&_^WQ<0s0=(:D-2Lpk&rVH[n)1>)o=]<4|ʟ +_{J{V޿s/\TWNܡK7[A٭:6Q\DCsELʽ-g{x>(_Z]WNRThUPaߚ5O=w9ڂv1Tta޿HM=Cqof(Ж\u+Uȸ2jӕ+PNQiyj^/y;wwVU n׾Da_yoY~w?ZCb>r2Aa_YǕM 엊C~ Yw>럡EA3V9Ws@>OPy9`g-( ܙ!͜wsro|{<CYwwSx(_o *x[}ʜy3/jݕ}9bS +g=e/.p,x۽QWr29X9V'-CʂGޢP);gXZ-k[+g(c% +v\ƞ=oo$.Nox22-%}S9y(Gm zQ(ý5rܖ\>¾riP? ul*܁,g7 +ej=qd||̢Fy2j{Vy 2Yλ>;>sYgg)7*̂P@GLeoe^RR\T_c3/T&%Fhh崡rg%kYszV -esykVmsF P̔͜/Ƌ.{-_]x'g?Wʳ q,-gyIGQ)1u!;RYW8$MΑvv/ 3j}.[te3ŮWȳqo|߇/&{K2-o~|g.y <$z"k>,O}lLO8vS[\K?Joy{RΟ\u/vu~A>!;%_Yt _/ucgq}`IZC-} ;~<ޏk=? P>26ׄE/][F}<;Lyw^R*"OX|QpM;r99ү3`(=(9tMvc/crY +yFc^#zwS_.Yq\*g8jz)F| _z߷%=_qΑJ@4g#& |܃w+zA}Z; '=?Ijwς^Ѭ3%=(y5>R*Фa#Ȟ>S˓7d<爽dw>04lf3ܯŒgjy2z=ˁ};~PG Ax)f&y=u'aAg%GL|Q 9m'/Na<[̿Q4븲Β0 2WmK˫/R$7qΕ^Ȝ / +,wOtGtVy_ط{PF[\._zyps@k^-p.cur^Ȧ/z3?/ 7/F>3ׅvZ%,ڢ9.?iCLB6 ș' N{7{f\bW=/zX=Yr_9Y^/_{=\ؓ|Mopiw^7;=@+\9}P;G4O25l1P\;O~/c}GVUa{Sp EΧK)dܱc[6Ԇ^Tâm'r>WG:K VzDs7Jf>Z{_~ КjO]V.oƗܷ[L!ud<-o +otg3)2 =ؑ5$G,-j;J*\^ *~He7 +z|X[yL3!o{Vqa>K!u׳UI?y; |3bo4Ϭ;P۸/\FԹΒ~Hiᙁ_ɏc[O8wu=„,~vz4f_>IfL^'`Ge|?W'gNqk/8oɹ+}ᥞǻz܁9ws}gg#2s~}M&:a_6R>彪YBF +gIkKcgusq>B69yVw29y3Q"wcg7}\~v?ѾP=ڼl9Q, +C?>lIkIl6]ΐ#G׿ȽY1;;rw%}}n`L_S߷r\Zܓ|V0ki#+{FWw.O׹Y|l>_Yksx>˳T߷[|'PQ#7'3\SXo +(D2^)]Feq~\-e =B:gDwu>6FC;- 7zw"?;_N;y97y)Jao?s?9(c2;jq^3 +A9 y{}5h/%5߫ |}±d~.RC\ycϊey6`}dG+KV'xFҝ0I?sά375yϰ \{=2fs~BsԨ>%[z\SS޳Е<}9ɢ3pF +gzokM=7.gLWYy&#;T^O;\y掗rU!͜7"]>^OLw]d[΂qמ%}ݮ;ujMU|mwQww,< K=]y`^o4jHk.{ܒ_:Uuu-\|"ٞ΂T _m<oSst9= q9}݋\Q%wAk,n3$N\Ǭw4ΏC?ae!yt32WuNkb?gڵ5so۞91sRX'^׃qMoxgY4Lsgp8ugxf-^Q3#|yk +> D=#cU\\  +yu;&⊊+n'%DғY iJfI$@˥IJ2-6,KIM<+,^y9f~ߙywy|wZ;)(;(]ZlLU,]^?W:TUUF5ʞ0Be2ٯeҖ9˚x sη"DhϞEt)fUt\j^xkLuٗ}his O qpZ˘οkfgoQ1frIOB_q:;X>P\{6f>+v>_ fPrOrB|o_Ynuws?#~8k(D_vrfAm~*{CR 57!+}@]8bb5V<z{[Nٙɾ=LjwdjzUN9Wy2{H6g}dܧ7:^*OnCTOf_켠\ݽS}Wf +fϴwM@{P9^yȽe}/Fe{vX{oRe|!+.w9̍.Snyr2ul9erw묒Ќ=e{p>ަSR+{7j.rn+q^M@g'} Θٱrrr xV-o2B[JuvS=[_ڵV1}*}17_a~ylE_*w5鮿e9;7M9Uj>UFy:ߣ;Q~GP:P+\l0Uc'8w?ت5|(vrfƳw_z6 pğw/\7Z[9Z6߉~y꧜ޫZ]v%;آu/߷7?ٙ햳3&zJ uT_fא<qG6cJEL>qaTa9;4f7'$^ҬsG8(&YEenЌ>}ZvAk#gzOj9,3z*Q6Jh p}YLrIm +_]; +Rk,yHIzpne~9tqOMǕ{#GúVWqh]5|b{?by{8=cݿDuGcq;2BYCh4%u~m~[v}_kO?ڦR(Hm};G~Ҍ3'ʣG[}4{VM]tgTzxFJ=q]wj{?ytW9>('+2Eϟ{_̭9ɽ޿zrwvOLֺ )9n|dR&?s/WzC\3YPxcPuk S~L5.ozA}Sٺߌj1gWOhտ?2zgW3{|PO:yjl׊+fW\ψ-yL"2?[KavyxdУ9+^|{9_u)j~ꪑ+->ζ2lxǵ>[(n58('*OgMT;;iGĬl\ި Q+>WD-c2{Smq/bX^y;Pj7׼Ǽ E࿔+Y9»<&=j\[܏^ټ+ǽc~ ϐ/xx RR =-_ee?z)%XP9Ky.޿+*zC}}޿<ʽ7{kwY7S|eɦ (zG7O?@9JVP+Vd6sOloEUm:eֽwFy$~3w6Vx-TJ}(㲣r~^kn)%vިS~C|h]缗:Y@)^=xŞ H.fP&<~\l GOgb4[43Zg_egrTp_np,j}=s1Ge?!>'cx)%YԘD x- +=KQa1pjhhjipi G.APѡٖh m7D[CCP!R}J)y/Gp +I$Iu%}ѷ wj.-DF[3E zNW:rhy$I$vIԡ{??Q3j_x_TvYNh5sCK7j6͎$I ϝ%:L?~.6"}e^OWGK{{zԍS>)є_@7 +A 01?T,,Up]L7oTKe ~c-! +1 Om"+ +v\!ƭlO,D Ad  %JV,ô?pf{,[jykun*Sk[O$B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B!B![/MB!B!ɗ +e,l:mB +%?!B!B! PԬPY\DZ׊\/[ߗMY˲Eˠ-B!B!8\[ڰR/BX +P([_6lhB!B!B|ԛ[jjYo\_cJcX_g +9ˤz,?B!B!cUJ9VfspMT:_ߏަ{@Kp}'n{~|/<`Lr9 A B!B!c`pZ#B Z_Veu_^:2udކ;@fŅ%p/*^bd|nK&z,+'B!B!3?xFùf0*f_1oFu8:~,2{.R|. +Ϧ-L&e<-,B!B!_f~qΌO yo`ī[t!:coT lj6sGW wT*~q, \6ݿB!B!"fmm}m}#pw@fՏsC8jCг870dCUJ%} Q}/.z?y+~3?2B!B!c + \MΦ"[v[[ߴ)>>k |-Xgopq!o߷V)Q%-w=,2qq߯7 Y?8?zUl&/ߢZ!B!דޏ>&Mjr1iWoܱ;;`C#hZohj857<?Йw(dc1ьfՑ+XZ5<_B/-UmN쿪?[_?B!B!f~bbJ|.ct.JUo_/{O|mV.y1Ǣk!l֬Z)x.,^?O_eXm>ܯs~_:C>V U{񜵆:e?;OU,W(y/Zwfs-SO!B! gewJ|.|ϿW|ů>o_>Wgۧg|̾7|'{mub_WgƏ ^ /rb_|\ _ c0 pfuv [ZٰQ^~gq}ހc.]5 k923{~51G];ol >x +yjV7UX|~q/_!?o-eDrߋߛB!B!wr9dJp}\&et׿.>faeWǥ󶼴`s3clk[۶kۗݴݛ~ܽgWߵͽy9;=8GvcۃC{oްw7ɻ[[~lOz#]8##GOp_vkm\vf|:  |<2_9XZG>?tuf q}|l>wtO\o WFk(Wi;? !^= ^7g]szIi?!B!Bo :|6痆Kᘰ\mlLv~zȃ*%>dK 3S̽gG+{/~{}?~'?޵-;7;?WL̆cs>K/z/}-H_G _ V/O_ߞW>88E~1v<_{}|}p/ߚֿ{;_,~{Tz;1('B!BғHZw ٫Dڒ *kKry:r;U^(Uj^[ o` LFƦ{q7>uƧgmo삝=dsp Hf+bK87mkkmc]x=u Ǻr]uϮ޷[}dM87߃9}߃Mf1G872yC;@'v2SnW{GO޳o?C履[+\]kw{:^Xƅ^|ټj-Nc`vuk#o69CpWY?:}^h\ gD~Bx_̿ho7|cΏ~]3\i2e]! !B!BdK"_Vx[#m^XfeC6pdCaθ(>dg#ٿ5[\_[K D Wlm(߽C}~wB÷;ޝ[]#;hWnlbzgt[]C}(];:bW,יO}!sqox.رi4rV*5|- snjcxy^<.s\xZzgvmCޏ!wxίdYVe{QY?Ş.T*-'B!BACRd>{u+>d +NӺ:O?2' 3maWßrIWANjyp8F?QQG7 +@=n ++0h ߥk>/qjpW߶kp7:8;'v;|~lomoⲍO!K[:c>W9G^BZgU+G}! +0EGz +7G6½uxgOj,Cޏޫ{sdkt^Rb8 } mvX2@%B!B wm0WFvq;_=2Vǽ9K1_/8ٿT+͜ \ +4Cz +>Gwٿщ:kcMEW7< H +ʲm^o{vw.efo"{v ֝vowoݽn=D|WU>mϺ'`S+=w8t|q/qثҍx_̋cb_!_]#^GV㎏JEx<-vef‘|e\g~ }37cN|~΄B!B!p[/ =aS3bC__ZOON> h`O"ޏ‰_p`t!z0=ߕlYcߙ9>hǚ-.;WU޵u ;".pE2?`.o^Yw\ne3xpS%b}Ybep,:Y43P{0'eC3=F0F¡BQbC_x}bJ'2 ˧,gB6iy+Ҹ=)2M r﹂Sϱc1 Ǘ{w9dt8fTRp~p%:_u<<?!B!B|iX5d 2\]zҽSszu ?w7+~=_?] s6~JsZ[΋ZwhdFi<=G /+gA}Ŷwvm(aڭ{m3gl`p]U&99oYX +pUu뜣yv~gܕ҅ܗх+Lԩ7@ήd;w#l9ohXj^ߡa IG/w!83"vl\Z>o sgme߼hvwū| ;aSS3w^ p9[)q\pOY,7}WE!@zAVg+J'f2$?n+08?ș{^Qso=kի{B oc߬{a*}>r~(:2r>M2>R c-rHd<)$/sy$|*J&:;#_wBbD,B!o`_F'Dޏo`_j nmyenY?,.EC'봎dv4p_+\b'U 65^;<:a#N073&lٿs.5[ [Q/,/ oキm؅Kbs8>ζ?2~)1އAV-h]exy"<_@# ns e2~0ryߗ=x\CՂ ۸cq7㭷=yZ| +?U^a40mݿ/c/o;MJ[fl`&>sھׇ509@ @dx֏ӳ;75;hsK-,-,xovfm\R3d,HDݝ1cOTO/|ދYt)¬T]6V *<\a\:18]!} ÙeᰊpŽ< A{m~>g /8@::w,b1>VJG=p}"(pwFC:۶ܟ_w;cfxP$}?ѥ +3@63*B!B4VQ KxίxlK&<7zo\_\ }ܿ|bb߯?Mydz;_w?R0/xJ#c1W-jY o`mxxYa1q8GmbMĤMONT67;k[>sct'C-c\<zJ{p.D!E6- "3J#/N2?Hz]ػדs_lۜ`/)E̸yZVjpD5e^P_݆6:ز3C}61:hglrlĦ&$^oW5?4/b9V.*zB難wc.|)wvc/y|W>1s'rtTfW \`^#/w 0v)Or?9 |^|!cXcߤ"K6zFx|w|a&aoUNRY:aϱ5o-d!,N2r.G`IGNiix{L|ѣo5|L > +^^x^x}pWE{o``{!B!bw]m\Wo囶}]x66/qe-mEg;2vɨ璕;џ|K_^:wk\hsƧy 0}iMy+dqs=]s NqqĬYc^B7=!9? -E/p#!X<򐕐̱X+~ 梸]aԿGy&}=րgy{as.}w_8EN0}1ѼH]E8돎PbN|Hߕ̡Frw%̗1{;e?s?o?^xETpzu+8?w}t{^/y/^{b?{S6>>!'B!f%ڹ v3x.]e޶w{pxv΁7֍ȥ[v;x>p k嫶bmUܹoyVB/&ߺsK6=ۤMML2p~s}^|U{p kY9.9y/'ϴ[pѣЛNrc] )H}4 > +.#QT*?f z8EǼ^|[7`l +>Gf2q/8,c>`~{vS 9xP|\~z^}ߧy2:A\`d" })f/w;?CqUPeU{|}{jC_//狱+3d1γ|ў8ǿ =|}sps8yot!=0s'?gO즁{f޴?z!.qœ=sn?|oߋE/r~۽'*ݝ p//GYn/BggB! + QCvkxtFqs_s˸uf'8m KǶ߶e1wGG,]囶{> 5۽c7ߴ;c~w۽7*!w~yѶmsk6=z}c2aKk>ozf&Gmrb霿7zxwsD=Xte"9?w~B/d;NhV]u/ͣNwزp*Mp_w7v{ ?Pp c>(tN)G6l6p}z]xnۻE_?]~yC +GY8|@j-"( +(⒴g4F38[kHhou6pLkfv>/43fmMOpiM93\J in/.6[Ӹ3ΝH/t`yA:OtG輘.`.tq=fu8u9^}ϟKϝ|?"Ko{{9]zG-y^;~t ]t%]؅]u!=.jժUVZjժUV57w~>H;{Gi8^KG7ԇǷҵwҍ[;[w[77{wO.~X'j\y48#y0$x-e.hϋ]ZJs8K8gZsR o]isdqx3i真K^o˵mUp@>^ŵ16ݲ#`h]/\zp]{ޫs`w&g:wMG'ϝKΜNgOoo3k,/XmLP###;LP ,};_g:|]L"ա%Leb_vOo?~GmWWVZjժUVZ.L`g tJCסtp|,F:::Lqyt1x' |U笿My'믿I|Ly'?7S`Y  >?lwVi/ğmgw3f7`灟\~mNlf+{,PٟƎ̖:r o,z:N#хq8+491ٳYy`{Km?_lwwϷ%;xxx. sxˌs`o 79_,%۳m~# I˗]ϴ6ׯߨVZjժUVZ?o>y7'S&Dx3 GvxMi݄v>Hw}t~H} ᣏO>(=K!}-ݏϥ /~N^>=_/^ڿO>oύ=\{p$}vtt߭t [Z?wY_O 6z|lj$oM2Eh) _3r?p2idpdqixh0?;OhhוMV3I&c`r?3cq9$4~m5acCii>>ΩA p~8'a~b&n[1m##=3SеL7.R~aVҶ90 5nһ9[m` r-n?vh /o8{5CNq-r<v<}rYޟ{:Uݧ8?]B4ONϞ=+d|ޙO]u)5\ +=_|/#skߧ\@{7Ou|eY>.vCO>|L.(+r-m*l+}ժUVZjժU?wߥ6{mzنY/_+{u-M>xu$KS}@%'> ߟ ^kbg;;ܐm/:k\eBy{c..HWh/{P+t_k~ x'tw3z~gsSh|yGs?~\_ZjժUVZj +ʼ)59?އs4/6,j%fp^ H5h87<^l=w[7;Nw{ᣏ$=Sh ~ q.}7ߥ/{}M=|=(}t{[OxP~:>O轍vcZM+͵W|Z]^[[kUseZ-h,br܇E!g~B_5s"+~ٌYo +n4 ɳ=og^$cy\ـ/˜\kbTVQzG |AFE-q~b z3εc&5fŹy_q̬Q9;3iv/uRWhq33.L"a8K v$gnx&vi^ pF=;o9g\p9sqǜobl$3S &Gm6_փ<c1G-^-tq+ ܎/f~v܎?YKrЖ{zk#}WժUVZjժU֯-Рyz'+[Xv `1,ctI=6;jv_Op{ܭ2{{߃68|w=sowݹOun]W_?I^qyConކqKa++M[tZ,< P&* + + }9u~7&sk5P>o~=gآ0g2ARǖ%v?kH7TS^ϻkɺcci9Dx߃AKm],|܂s79X:Y?S+qYq-~>@.]_ȟyhNyߵp3"KWj1cb̓z:L> _yKۍr^kxűro//K^g{a Mɜ&[~oXsDhuy޾nejժUVZjժ+)S HA&eZ Led=ꈌ15 2 +[sn;[i{s%loCpMx8ݎG{H'=~ym{>HwMo/o߹nܸn\ & wp,똳؛{ƵLmzd~G˾3[%O:: +\+.y>jz6\0qhǍA7쏩o&,76}}?=,g r&`*&;5/{c>`Igr%s)y֭q?.r-'7Yxl|B,5ipSZB0ΈnE~O~wpu|5i2:5<]3\Nȉ L0r_\)>y/O+Cٿ>ugmg_\ 0s|b_5Wfx_jY`vXF6/,͜`wKywr4զ p  *UVZjժUV_APOo6/}&<\PE܅h>lf܊rtZ^I+Kieq6.Nvv6[C]k֭&#{AG@߇NᣏlG5oeߠ3>O\ۗ6 "Ffae9Z~JK 9S9νd\g>dlFcxuҘ=;=n 4@?9~& /0w}fs +='Xma)1E3rh94OkY M[L{WßYQL8%]]p;gB/\ypsl +Ϭiys.J4ޣ "o:o-2W"yZ4eegEh'G(ާΐIg%ly`7fryCm`118*v9_d>o&_ L̴ K仸w/]q: &u{9ZZ?x66u+f̙'5o[qR头2,'z`}Si*?kẟ:g+]Z&#rrbh-rڳOk%+y̜Y9y]8t8 =;3]ԙ:;,y/Axb~d.[$VZjժUVZjyN[УI.ӚY-JZ~4Wh,džNӜȌGP$z.C0螖=oZ 疽k"^JGoIn ׎;=165~7v6^J`l< Lװ=ًqfG_{7odq0@|cכZ8Nc$_h+ʬzFPK HFGm-<.|M7\k4m`\'s^|V^wak|0twrngıq_Y_7umsX VŌӗ3,,~w92b74sP#t΋n +ׁ?y>ZpMxLc;51Zzu2nvzq?m_a{m'J#&rx5g.r>dz'Y_2oybp'%53[哞b'>#=?=GX(P?8VZjժUVZ~%բ:)Z26yP>E*4=|\A50f?2{ ,@A_(B@z9%93Z-` cc98Jiow+]v-݀X/槾q'Ax]؁ok}%)~+fIEAgU'p]xw +zsCg=} óGF@6- ^72jo_)Uư8"^Nj`.~)v/Syhsnј-e=? '?fIO%㍬"M hKr&ÙޘGY|+.|]k3ݽsO/N x|nư?5}Ek{EG\ #> >^?!?UWVZjժUVZoLD>T.GA71b1Pgyr=汙~æukb`7avH3D6  ܓOxg{+mmjn@w~ +^3wp-wk;,5uRd*ta1Ǐ?rPw/{=yYڳa1?;k]cx##r` sELQ?)hb"3d' ;o{z??C+C\gaT˥oǧ4>ϗ&07p>lvfՙNz!mKtgt%|2iV0ZM>nf%ż42ױ\YF/ʹMw( _@z|;8"kn1w[O++;d^1Y]EGhUeRv( eF1?:;0;u'LH7dљzGpAz>Iϰt GF(j {IOx]Wrfqr&`sNgdecOgϑ.,f=`53 ^uN9/zl}r&ߕrWsdD1?Sr1&ߏߋX;S8csox<Ϯ-ft<ܐm, RK.8Ο[G<:C >h?o{6u~9|zpGA7n?7;Ʊ>bohX/ZP8?{_>no| :3 Q~~ '4l±yoQ?Λؑ!n f0gЕL?siε.)yrXٺ{tl`q2sG8`г.Ǡox?ϚN4t~lyo^9b|Ygz[zu5'%s߮*}~ϝ4skY?wavgg}(`Tz Э|_4ddxe0fm;YFv{|O?LOJ?>y<=_gϰUzoM^<{Z{Zg_5 +LM-Tӏ?TWVZjժUVZA0DXxB9N}u pE^r_/1H;/f'ٺ`981ylmI9k }.s}+1o'Yp!\E4eقoPx|dfl>_*~&/CYἏ\2CM׬ 6oo<1>=1 gs7?p1i9g;?y-2o>a{ /mdPdu_ςYFb^`r5Ϲ_5X`dyfR;Vf:kȯn33Ymٖ٘-mx5}j$M9wBf~y|XvG㡹;=^0F2?0Cs1w%+ǿD> 7zAG  +9~ǫ:~:Ѭ3Шnoy" 3>CTf6` =ܐL3 gkm{dRcjf,./<GM;<0Y^~Kzqw gMsds}̓cP~[՜̹sL`_2?ؚ|>=dT󻷤Օǚwi?m% =},={ +~'1= +fG ?}/p}/_ܯVZjժUVZjj8y=Ca97,ZeO~\87+&q0rV?r2)py{f壕Ns3SlA.}uM~̴]tݰ +.g\2wŽ9_ ޛ D ȉ%M:uz8Wݑۯ%9uw5pN ؞|Cip2@p G~`8k|bzYb> ?Gphæ5pkM6H::1p:R"|+L=2,Ǧx`gŜA?ͦ\h{[.o0ρSob=~֎KrI9?yz#3[v_^Gsߗ:Iݟ'_֦Zfι5՞ݜϹ{kvk\5L fn{Y~1z~߂}[Y\ZjժUVZjժXԓ'  v \'<6ϝhq!a?ˤ#g|y+$k^0g^PM׷,wh" PlVۊ|xPRnr>zLOQ#yc`m\\;r1DuGFǙ|x`׶y~KbaeM3!\{AFvYW]컖sOkm>߭z߆k:>5gw#469 8 >K FH6Otb.0΋!2sxkxKkQqX0ӂC,SaMuF3=!AZB6rɁ-?MG O~`gvY{Ȝboy%#is^1tre>G,sj~DZۇ㑻m\=z$#~?G1_wzm'fv6(}'!1L.J>di5ðYU"?1̃cgLdӍ.0}dfJL<>eifrYo*W-M1&=">h:އ9Jnd`=|bsG^ܵv}Fg-dhcdϠ\*9sGq C/m#ٟfc;VZjժUVZjj`hX>Q<6rA0=>kmsd`ԟu +M7~Hԟ1"CS][f|擄VZ)GGtiԨQ{$^5Ц#Óq 쏹 -QdU,Y]AF= !o:=Z}h}q2F9cq;ujzuwV-|#z`v.I8ދc-mߑ5ΑpbF[-'-7Խ&K0y6:Ey9Cҹ\:2sb|08i3O|g-j\<٧y_?rk{ĩkժUVZjժUV_S_OwH`C#Zئۯd R3x=V\;虵R59#̨0!mgѳ*՗HӓM&)V + !uXn/uj9>&>a90kL|F/g3RHgǬ>MTHFt t}O9y6"(i5"u'c6׍5-'=spu߬|sm~ii8=M+Z=zgt3*=&+geop=lsyښW&ޫHY۞=3Xt~1ױ{6vg,d[VZjժUVZj?~חzEcfl0@z{ ^hAq2{vԹkC̺%T:|Ʒ84lkV͵hJ Gh,qge 3vGrϣ{3wqه\SE-!?Y^̿UNndxTq:J)^HͤO✩9NLi6>jr֛s$8@8':P4?3j s +u%=a:Rq~+%5ԙűaBeh$3XrYf:en=1QPz{X{9zsuB;OnOҚރTז?y}zs3<  c,8#eX_ZjժUVZjժKV/]ooO龊%t|64~ڶv_ˬ0Cd~C`Llj'sܦ&{im‹J^y=gJrMF/F9Ŧ{[| Xgc<$rqhIZۘg1 f,Fg8Wd[륟66٠`P&!5fX1 9zE巸F43-Ŷ;6M 9[G\gPGș%sNh>o^0qzg 1`whjCΐ[R|^|NK t>Kyrmọ{-_\o}I\#ߞcEu7y^ o~lv2?_F3wyc87|ZjժUVZjժZx[nn}bCt +if=W>C:BrB2@A>f,-3B͗?|>h {E\ӺZ1Bg)97##wҌAgk1KP3A1zf3;փ&+J[f͜4wh` ϧ?y) 6/TB5iSVgpidm䴼>cyc_C w1OwqlvsR_I(Yn%CɥM#?O:CG}4 5xG,b\'{8x0*53dkч,=[>3h5^4OΛd׼.:QffƻkTpq~$c3z4<<1 0dUDG\Y_ZjժUVZjժwwڍ;;. # Բ=ffH7^+0 Nfs`|ܟK1}=]|}  frΜ ;ν& Ѵs };^ڢղ~[[!1^+(Jc1Kp2 +vl: +7*K3}Mu?Ƃ983̩{Z0jV&8ժUVZjժUVZoF[]ʹ;v3_ `{7nkpo/qny]N]]Sիzcd}=b~ۻ¾̐3ا%Ľ8n7|K`]g ,>Af;ϊ -zlIXjĦNp #rfm؜6FF<@lQ~f~Z Ϥ֋T>2KPOg,2~柙|mM7XlNG=?oUep>E6K,sWii6=ٵjƓJi\,;1ApQc\W4/bu W^{v\@=9sv=^|)Ӛ<"zp!7.gx~Ow5j]So>̛~ < 9n'^׊ϸ:u_ZjժUVZj6z>\o1M!1ntxxM\иߝ)A:NG8ݸe ܯt1^ۚ<=zܭWHxv՗WaahL ح 0;op`(Kq988bxs[l'f,iWenD5 +_i7OZ5&C7ЦӜ37yTʠ?Kj8P"=-g?v$ٌoW bf3}6:<0sl0^Mn^{5ǡH򿘇wsҺ>Nhdr9ȁ]7tq/fMq=Ӎ6̻fnf2]ܷ=:SrfsȘK׌Zno ?15ZjժUVZjժU뗪kis0WQwwNw(gP 尿to}y{Giwk8_mdwA x2XW\%|OMkpKBe\@+܍.t6y7\tpw)SVZjժUVZ~4ӻۇ/<Ctw(wL~B7xt|t L ~wz˗.}q?2?vu? vBz%T+by= sD,_&в9\1_?YîiecA3Ypg71O9+Z&i5==3GvgMBg2CYs5>:Fd>0T )c: KJϾe9*ύ9<V˞:"9f:nȳ06 ΰ}%zYGM=׼n2~h^>yۘ߄%͠ϛ2gO~r\M}$fн +!3^޷ ףm,B.'\?^RK+qrɳy/:7uՑAywȰjpi2/˜^[.}}&f^-W8&}̸G_u'A--->zTcZjժUVZjkiej7M=hV:80G.Hu;.k 3B^~tu7S>tXc.9#'t +h. ? +؍4&h 0 5d}`zeش*[ fZŽ+-\o4Sƶ +GpSIԦ3@ K o0Ӝ.b|ϺX/=b}dE8c(mzJ`d.QF6yM;,Wxx6){w d1',}k">s%uwiljyfXUl]M}|UWh\ј_\g16w2< 3v\g?xo=n?Fʹwv~K;imc;o^ZH+kiqq1-ϊɧcZjժUVZjC㴱&Q:<ÿA Y~h{`x Hs m | ۰q!uIw L1?+x %Nh/^v1% tX,vuA7\2GV 0;Lm K`C,q_Ev^Q019qt(BSf45>ua8%q^c\QXd W~|/z-=M +4b -%>s>Ie l .l6e=ӳ`9ɑfI_?F0C wҫ[2Q 3;z6yn);#13Yf+zoݷ˹|p 20+^YU>,}o<4OWy?j +{/zQ3j - ^Kw-hV"${W^/^'OoFH ηwv݃km}66^[T/nK}ZjժUVZjժw_RXۣ3}f&fBG^OׯHׯ{,C#;::ӅsZ켔.]xٖ`~ZRƒN .-`4ube3_K_.`;# ph1@p f_$3pm XCo>bj`{κEyk\A綁HVhC?fV4 ]?zJ5O,M;Wd4o?j5|_~ {&cҌ8P>m>b/?67uGm߰қ/앍L XC,E˺sXfpduIV#s,q5y'm4M~̛;܏A-#y9`t4onpIސ;Yq?Oڿj m Xb/4ʿur9/=3f2bZ\XHi}==vuzWnl5_|<}<ӓ?:}30ݻw;.}h8l_?H~b򪱿EpjժUVZjժUZlپ8vo:9Ppw#z\}#n:kZ:`v8fZ;;Ώ/)M#(6zA@.3,Xe.vvAl0`o Up73HzE ;1/ 9_X24_&H8`ω5I,@pB\K7ubR^ߓVKfĵe748济|z@fT ?ϸh_>قϕ2@|k_ڴv4y](\z52M4Ky̙C@6cZIߕ4e:=N t}|:*ǯ?cfĵti /1v[41 +|7]`weB 8 ~<+~>s45=gRZX\A/%4p[;il95^y^}Vsvwt҃>_Ͼ&}woOW_~{g'/O^'_}zɳWL_`O>y>l}L` k7nc4KQnoo -?m].mwOoHg_R(ڿU[T_ZjժUVZjʹ0Z=3i{c ln[ NH`w +q;:ֹ\ӒW}BG:w :.B Y, ? zAjM7a +7,Aj7lA|:s@G,ҥ,nap@hagtS~ϫdSbI?*(Bϳ#B~1%S23 p~yFf/Ƙa즙njb[#qW?~[vH1gqmY#1<ߠe|vg>q=uH\aשyGpXGom -pKG{qtC юf o'c^;HOy{#wc?x~\GW`dٖΑ=ܰ@󽻗XWRsM-> 9f Y><'%wL4v,gj̧h[ZM`im}S>-0}7nKnny?ݹ};ݽs7az~~Yz`w`~m/!}W|i,^{X\ZגZD>pשXKkZ7ٷ3n^_ʲ?N?gNsss:O_>ժUmsܯ+ßlժUVZj^]I+ic,m.- Թs3gY;!<M` o _T3E +` $k_Z4 \ln|V`krD1Lf`g^WlM)r ,@#ˠt9EIOԠ Qxy{qcDBoto99>M\DHyܶF7bȺC+O2@cuo`dl乴\+rPypCW0A5SN̨gDыL~.rr/i }6瓯%g撏DG&Ȭ\27oy+3R7o9{?1s7L6" Dgp"c=̾ ='n,υLς[Q-_OkrnQw҇}<*}`{ߤۿM_|}zwD!64oKrUl_4/X\uNZ_[[?Wlfm:'^'[.-n+~d9-.3js4\C_?O۵j;aOEC?C_6KQVZjժYgϞKONg< ;]'hZ m~)xٴvp3@9ػ-S| +4``WPnrD,KؚMh ^ 6 eo|r +/!\dX<27ϑSvrr#a~x?297Z=N?>|?rb0ߒr)62NL{g-3Șhx⏳2jKCmߗ~'ts ۷sam6N"]p\ibo/nS ̟o\5왜afW=5zKZP:(K}$?$|2yr?}^If@|~O+_95 vx+cx?FoV1lk oeema(3OYMoMn{?QÏ}>$sG[, MMZroy-+ow,.r8(m+`kFs_ Wq1Dh1k\O3V~|FQ4uӭ)ZjE. OFs}=3 ncw}4jժUVZut)`z;wY'~xVy Zް}ٞ's5;`u  40{ :`f +;,71`*MQyh>wrbܴNylc;`XZF0sl *=5<|,=k29ۼΑ,4 +_\}mu\LEE-dg9 0?7@|f&zL&. ֧L&}mhI͔Ko.z(}>3`uVǑ>_pƒ)5zy|д륩UVZjӧϤS{@x, vۂ$ o9ChAg/6,᳧,FJt@ V:+C8fƼ@KW &e==9G3!kLNR oj (2' ^-Z 0sa$_˙Yxl_NCi~ܡE +PAE},p,= 6+8un怮 fN^ւQwx6}OL3 /=zf`}gLwEQcxuy:}|筙M|`fXAR{3ˆz09g ޫRHƩ~dhu(q,McMa666vev3Y~vo|ji&kYs]or4&F`,ޟ=7;+m|Zg[gkLB1Aq@땵uu q]r~&3łWx87}^|k~#5$hsVZx4;GX0^хSpȳ8ڷҟVZjժUiTKNu2@k瀜xos+|Ff%yΘhczVb16N mk:Mkڿe55?:w7ӛb3wLrwZ3NϠ2zv&Ls33昣&& laM q?jYK bXtoyY&lhJ=! 3.eFH=>OYpJ~>4ZjKz#>ygy|G۸(^ZjժUVzһ.Pz@0?~XRH{ m7b2@q@@`\q376hah¦ :_s2=hq@--W* d jj!W]2W ǘK{e)tMc#Q7bۨ_mYZW/u\:&-~-Wtz? + kOueB(V1-ɉĹԬm-=6/)Ee?1$9/86lMsL ٟfiK's|?ƹw=~df&y nɣ2ڸBnsN|b%iɘݤ7s,$Yi9Gxqa憀\)@,&\v~Is,bY;ל&3Gќ DOy"XN8#y3wzE-G7˛sg;|}ܯB¶.l*x j#B߄& $&,5C>|~=9+a^aGZALWd*HwLNu,+kL&׷iLt=w%܈҇\O3t=/˭! N'D35l㉑;1,:r;>Yf=, AB:w_;ԪUVZjz}oһw*{љ 6\ ;ujfN/rֶ mɹv>wv\mcC;xLc,;-;;#| zA6ٟbkKc0Z@6 LeٿcC``w 1 (Ga < ȿ/l5%R9vʶe2mmKƼȺkjB%}3_?.K?/=KǽDy)iѺa K]3 w2crxɶ߆M\C3_C<#犌̴U^6m^ ٜ1&Son7_b ˞ѝzp?{[]J6?2f<;s:Ly5!}Dd0~NΫl{Ld12MΘrubnN5]+Hm`@299瀱kQGm ;rr,VсpN!u{|;rj㌜GMޜd???+> >G<qӿZV,[q%]-^'X t>; ylv=ߵjժUVZ};VSy|PM| b)pӶ4] V,/>ف3w=<J9g< g`|X^`X ڹ`_/u . z|ϽzfiNZqv)x``c`<:%LDAhNKߟ"Ë i|)mMNCF<.2d4WNL?Z,^laV79aBv̬+9clЁ;KUΠb^_u6^yaLObvdjdq_Y.fw0-d+sۘN4_?'j˴Yy+,:A2@?3C(֧_#ks؝ |N.֩[p'6g#sэ/Kzy?=czQ/o}o&zZjժUVZj뷿]-w [@8 :Ѷl+?>}=t`ZlϜ*q uc?>rN5}93}!4..:;/|keULA$hG㣇2=Ʊ+yOҼ zNx H榑6M]@M;n`VI|n(zed::Añئ e [vQwE6pG^nUu538ʨOE2.b~e(ۅ)Xg*{|,69Cq@s{>Jv,']2%&BFY|*~Yצ;x/ou}5-l옻h<ۜAI<3i7|/e>v;vʁ,a ]rvcIftTsf` Ԝf}Ar`pigѱ8# )R_8B̖,ޟ32 #~uszLM^_ZjժUVZjo?o~O8wrc_w;\'CL0]F0>f2kX ]3=L~ %uR+œ.nY.$88q v$Uusd/V +i5x. ʗC#?d#ۘ |cyTK4O@疑<``|( + ?op@؟XunT㜱mpA-2dwXolzb״`S'Na3jlJfu̫X&9r5/YfϚ'vlqV/t{̯SN5~~ΑWMΥIl7yZyju>s&sPzܾ}s|+68Gzq CX_pRez)p̮pȒ&w l'lemkoʄW]O}=g/z[js@? Sa4p!{wf`7 $3`3#,Gf^ ~7a y1jKg@7z;& 8tq%GK'-pe^Zga'; ya3ܣgj!l +gX]p"Ϫt OGŲr]q>ڑYWf癓j&-g(>qrJsj, ֜EztGk<&[E>q΢9]}dw-;c't.SeI,?]q}$kE3dWGX[t7= {nL +_7 , @N:5W/zOoozrsV2xSuO^o*F뭬- Z::f]8gzfU;CABs&' dcXS']E,G} z],Xz?CAmm!l{c*G*f|^CJevصlkyLoy[?hz6+5ۮk |2y F1V2,gfYz@7hk`W_9}}ƠznsyZTe5_c?elG/Fϩ ?횩ڧ^$9rlyڙmy!qO53 PKܣp))4309`fk^gq{]iwuOsZ㞓u]dzx XA#9Ap5!7L$oƏ#b?f͚5k֬Yf͚5k,`-i G쯷y^69_\^C ,К̼@<Ųf0 Gxߺ3>p=h ϣ  Oog]`$p ύSBf"F ?83y]qץ9E+me l}q߽Y'W!f$vw6꜃H~s~Oh09Yꘁ[le3?_k!}tqNq>emuRS4@{NWr[| ?/ĥD'˽ݓ#|%uܷqWnLCk=~OHWχI8XO;Ep`hƮP^?Qyk[ce~r1;i9&>t.g̯kDMh g<0[8hom>})g0,;VHͨV]-Z@o)籘a0ABK&fx jgo;nu˱~~/t5x=r= -l//@/W͹_ܲׯ_uoޜ(4gO{+ߵkJG=/%?m0~7,/s&Иuy,Kz@M$==a{:ϵxL0?_^r= +'XRY t-U /}+hkmQ;WZFo<2<=|NXo+:>QhɎ?sqcr\73El>\59+z=-'# M އD]-y +o{ϘlX6 w9|=`BXA{Bx`KYSS<jh0{>&8)PEH ~{`]^%{ += tM8ǹoN{ ld؎:jj``5GelhJ%X|zcWg_.<֎ + +3{ιvvU}k}k/ZKkh|fU_G1Pdj;dMf  >oW6~6?衧~ǥ2A僪 \ >xg.+G\;u/L.8rϏ5kh<3W, r+<@U+ynYYYYYYYYYYYYY~%m 0r@O:d6Ya> |#f~ <97,P3DNs0?8fX =fȹ<)]& T=f(y̾]Yd{mavs?Qc<?583g3䢟3gȹ*sjt=8z܇ gkEC}G盖3^OR+fE`M:5^0zmzЧ=UHl? N/U; +ۓ+x<|֪oc8ǐ 0tk(Y &NT_wW]k3,Phd'\̫)>r;َGy_w>WF %[gb>z`{Ì{ɖY;EGF䟹N8OkcGX`jv ۳Cp"; ltXxq@rEOKnp 13ּ]7h45+31',h+~%Zu|:" ye?Ay gPs̿'ل~G41juoɍuWy\O90ϳ%^w=LStoF¶s5<'?GsjMoU'#yn6oQ?ea myA0r2;mڣsۀK0@)J)3C.t_tut\gOxX:v཯6k6 gm]ˑ]CτlN1}BdWOzԚ:.dXZwH6)F:Rb`Bsc|>dc{嬭wz1S Ƭ+{ +lW?&2+00@mD'6` vFe C~ȹhu>e\8vF@ .s+` 7=ࣰi'kc%ifts yd炥-gX8[؟pLOb_f4jQؚ;|.Wk/':smq~z +Sn6ˌ͚]ý0Dakt63Pm<9+mlڛ%dw#g,DY[2g #זƥ8_'UI=9]n,t+W.++++++++++++V;|f,|37#@fc~'D!|/6/<䄘0g 9ئ9!]ܯm"'vݖ֏3q :'j^-04g +ZrY5@s*yrԨ}s<M/%j8Tg|q^WuqJvG`XY ە7zH}帖EQfs)z_&$+S42n>Jl/\VVVVVVVVVVVV)09UpX -B^( >V9oI2?>{ L>`hgo1a,Gs{ܨkO?N`ߐ/LVx9W7q>W7d;c,r!CyYMppU]3235~scoSy)ǛcY77y̔5Mu}':ћ/erR V9 D=4 h;д w3Jy#C# ہ/e&ύ1@KchW8M ~wnd/~0>6"یq]H "y,gu# ChzAz-Y~FTssϿrYYYYYYYYYYYYYYQ `d&+ysrL'|9` `hH=g278@,ˬKD?X5֬_>P9 3Wf18fX,@?,<@0d ?gKxO ?FV8p?b_G}`gcӷ*[USnU5uE71ߟ^۪L^cF{p|c =g}X_K|v-oz+5)sN0]xqME +H61O0>7?hƽ:Evι3>YF6_Y~_V O_:`"gۄ _\x?lǺ?[Xe]8㣬\=Gx13;eeeeeeeeeeeeeQw t]eu_xB~~@ 9_,?XgVp䀭6y!gY&4ҕ1УtvYt}/ד@4~K=j嵮ShAK[%heNkyvS-:?O9\;Ca|ȓT "CY{#*0ijn\M犀i狥_Kti݃L}L̦SFZEk˼[f sNͭy[V0LgpE:2w. Kr?$\,o-w ?x({.R=!t&&>a1d~rm%eeeeeeeeeeeee1?g6OY 3A#x,?dV TX sB0#З>}sM Y d_?syv}ڝc,?_P( F(/eKnͪۯn5vݦo7{{===hU0:fr{/Cs,C3"o`)0fb:33:z.o_g=ں^َY?S,37泸eckUM 7|Ez5+dV1A9S·mv{;d2-'0͒Yt[[cecߒ p `~W oY RkF/ +ceQ̋>-de +3WtvXz._ɣp3:,zzg.*E24Iqd0n8c2iXu}`YMC{yUׇ܎>Йd_Xgs3ő}~qfmgkN|mi3 58UW2ZLsK +48z@/#o˲6v0rwawQoUm)}%%2嘪uygL6h9bO_ ^=ŁpFYLb}`'N>_?Z>2}䂿leFaZݯ䅔>0r\`s3ߓ+3?clޏm'Zq^{+؟?a~d9z@yRgy!X7 3Lfj 8B\GJW?w{+j + ,z1%ؠZz%lp4~77=Œ`X `]g(vT6?gntD-k=I=j;\ 7ŨNj^jԄ= +{,R"Oqx1\˚:? 0Q=cuU?n/8lc +4t.]G<)\`I <i]{<eP~v?9+P+V$@p̢SY;4Btb;E[O53s57Ya," gT nߏO?CcAs\gO=˜Ho43V0 Ԝ A` f^ϚRrB?xnpYy3ߧe/=t!3a]qN &+lp b:2fBHg,\X+1sv#ȍOt.8 'C21g`rZ94|]7f Td``q=s?m_o/f`F'1dx]AwY#?כg;&v7psžE3Z,#|hr3oŒwKl@0B Ѽ ͼ  `a%+`;kop0~~~z*ka!/<*`kU+M֘_*Lqʧr:FAg6zP]e_ʥ܋>иs@{w!9`{l1u +s8P\ǹ}^f90@M??0#scf҅ZVlsgF hya +2#yB(0T7r@U0_k,i߾˶m۫VPVxᚬ!xEG.H(jZxp4B4[>ђiE38[~%X8`rϵ|zccg|_Ϧ 0d0W9U]_\^2>5_#C/c9Rh<%&t?y ٹqǮٍ<ϫy{!M$W:l/䁄=i:Igrwcֽ7$,{;!xV0MtG=":jv- %${<9\xݝx nQ^ad`f` {0zg0_|v 0Cw#¬_p@h}_0Ac0 +X!w:6a|>vXz9ͭLqP-~WgoAVlu֟2*mEOh,^0PԌ͡wtm|3LH㹾wGW}̿hk=+YȮ t_O'ഖsnhMx]=h\;H͠r2f([#^䋖17sog~./\2_"92Is&_XѸO9Dgms]}zx'Xc}<3Y|&|W5ԕuU}3Ofϳsե31 +<>PGևY~,s7dLx, :f/++++++++++++O'`z./2-;5ف* ly`a>9sAJ@Fн51@ab?X>fP4~{jI؟],pg?2?dAy`A`CM^ώl>r:@2@4]e9| ͠U{. =##p=fb ]=.Y|/?/@g<6U6ܰ\?G6E>ϵ d}-S"+fri`euV  suǙy6;je*Tqw!l" 8x^'n4g\~WVFg%55Cj\W+P!' + ?tYYYYYYYYYYYYY: "s6l<m8[zo0}ʯ[M`/+$7X_ + yGB\2#d;T7i:ͲۈvXsrEsa5&z1'PGnm 3<p%#^S3"׭9u_5z,uo9Bm*Qt/I1'jL5˸63Kj>?q76W`t6>f5oF>wϼ| ٻRGn/K;=ov/1]9ش~8>b+[7 3]o{07 p$ + xeD~8t`oF`9(cu>`odvp}=_0@_$Rg>`_,f>`۞o%ͻ 2B T/(Ɔ+SaJ93/h-i,7W+|F/s-=vy"%{#dΝlF c̺F3Kfo\X!5۾ߚQgE kTo'qo57dFg[mu& gok?zm3m!jTc5'K g6wF.N4z̞;;m-:/mxs1*{ee|h~e؅1uC#OyٌAtmdd :"1;SgXok-1_+4nf گ^45+$j0Ԗ2Cgd3+$'82@YF1uX:o\*yOLvC0X2_Th<oV2JFoQ9A1(DZ}[N幵Рaޟ1E]Ge<Owq碋6C}E ͯu\gdh$qƪ<+vMe&5qF:r9`iY _>}W +,Q|{we]e>oeap?0?g:?Gȅ q-WmׁQWAnLw }A̵D[w5̰Yga y@g vH &?^Mf|^tq^˚mf5: i\l[fzU7&J?ch%Ldx}!4eeeeeeeeeeeee}~c`_ze2@m3+$~'˷rFN X4XE8 p|Yy l+ż[ WSp anuT5cWZX"z:.f{OEoRn%~Vέras 1;gpXr -r:} _OE}a{1\ŚAw_0Cdۆ4眯*m&QNZh Iݽ-|Gƈje[.qx3L/.y! m9_cEx0Issumy}a 5C Df '4=]g=gd|,38p@5;Dg֌kw\"? +9`ـ~&CCY^`/es@*[Sڿa@~-wg ~K|(ΉSFYԁZ::pùqdX͛% $Q\Gcs,Q1˶29p=WEY "ol+g +; (/3v/ʵ~;Nj@KYب\c|%|Q8KdPg?:'&j|l[&#3Z]^ ?j_|O~}wg??fFlPj[O=Q+9uGz< @~P9`_(K#+ $|Acwv~A^ a`υbNۭswXzSqmLg"u]=8 5tߊ>c3O}>A`5M$5ʭ6n04[>ܒ?\ښ`yf 5G12}񿃜M#ZBcɘe>rc/)5V{'8yz:x1/a)k3@8i-ەz~} y|w2zd!="c_9]%̔.eeeeeeeeeeeee}7 96Y!=(\xνy @h3ߓA0z P02)vj{3'86`"e*lVS0Y ۶y) p?a@ӎysktceP]}+gϱ*[M'';$s[IBë|Ntz۫Zx[2OkfFBFQgLOQ2?gp@zAaÇnx7?= 6`T_Xb 1㹿<Kƻ+ =@0wH(op'C\Ew}y.}]vg.,:EH o]VVVVVVVVVVVV}p>kf_〖9\f}=`;RXl瀺l=߱OOyh[[6-`?C;6ps0򿨱+L z7ahсQ7f,+zf|ٹrnp)?p:rLJVEywv Օ+df 8'C72+:>C{fXo` zW ,'ת4 PYt$^ ZO>ɿ$3$\53 }3\~DLϹ\ZyeO6.1' Q,YǪ2dH[\D*X Y }H=^+Ld#Y+:Ux{:+{LF~g,O_ ;u\cy]5C8 ~[J9 ?1O`]`Xeys}p`ffڪ<]k! ՓW\2W^ |?/P_BszoLx+ᲲW) +/0Vc `7r@ YzEW-D,m5- rߒ9 woLK}<`{xWsYn=Y/B:?ئےu 6pz1WttsMǜۘ2?}Lװ?]8}:0gO[m64v_Otll>f[6EqGEp br:Йr;eB\@Y 91h/qf/f~>?}6 _v(;Mt֝0-[[zᚍw9|›oLh岲W/?c#Svf-p5@/1u{kyyWkIѴzœ7[ +Dc-I^7'䀟?Cx)j^ Z R{{c{eҘ yjqvr dk˹.Բ(_wZF:B?a(ЮIe_>f̼9n~{ t_ +?1AWC\[*W#_CmƎ:;߫O|pl?˜+?c~>41?}ƽ86s_YзQ& 5Ñ)+Zg}W؛G98,M4'֮-hLO)5I`$Fη'Ll'<L>^by~kႛ4+z|DW6,Xhk`~fcvVMc9[2³:jGubΩ<)}y,ZGp-kWc5us ~ܒ|ǖ?E~j#ҟىXgy~[yZ3X듞3}_>K/|و[bG8D7`n]dV,lo$ p4QEhڿsNY CF.XPy0 ff7ez:Q_ha?5#EclpLkJqn~Ql4CD3G|]<]*+= <?k35Zp1h0z`;g +y}4+XˌDYxlP33K ]|O [ͻ݆`pF4CF^=eGa_Љi3JYOGifѣJcw _X:LΕ ́! ${ 3#WVjn-g<Ǹ?Wy5BksZE&}wyq!/++++++++++++YwG2Kp `6>X;g(,!5:ظ7k2'%\T0gtvɞdios^ۜq>a>S 4ú|2c),3j_~dҖau0>Z:_K-`YX `,owrpU'XxNxz[ٶC.֒O?J61>\g»+پY? .y\,cDa|Ѓa]yk +vl Y5C369M?j۹7V-7"7YU 􌳾8 .x3^ +3{zƺe ?x`'2y1>;jlc?Yqӻ\O`}ٚp2Q'F2˗yڜ70-0..䁘,? =O0yOX@fג jFZ9\ukO PͫpgUnI#X4m+qeo\#'V\yK&܍=gx}|l"#\z1{nhjEFGŻ /_de{Q9!v sKJ#Caө0Bpɬ44h{|d307!7|1{`~sz[Gs'fVfڱ'S+N1nUTNǯk0ȇٿ srN1ĽywNwQgK C- z:PVc~v> @>tgg? X[ #[:fhx쏭r5A΄yӢ %ͳ +cgEC~ LA)N9j^C2>oaKC^zGc48V}=zzpXu+uCÿ z>.д}KaK-aoZ>=§L)pZtl\lxt6Zwg?y|%BH `qy[d]6Kxi*s !|Ϙ܏Q5QH-AbCa~,pQ^hfy t[֣8,9\[w]<_VVVVVVVVVVVV?q``520v6`nYf@g' 6m^4pZ?pe.qsoi8l"S,p'MɖGLDm5x"- @(&[Ll3g)vjት}~O)Gv0@9+s1 -6=^YW ?|5R_{snkγ久@ĹڐMO֝=7!##Y}F/08za>>pƠzgg>SϱzcyOtr.̰x֌:O|l=GF=Yyo9,p pmٿ93{7߯3COp!?ـa/8dߗ?tsr#x_њB3aZ\vK;Kj%s>hh'Թ3XǪO rW\AWs8J]dY=?S}Ԯ%`~kc s}~Jت >radfܳ7 + >Sp* 6oY֌ BF!Ǜ1s9:Dd |] Y4YMan{QG'z?5.|η¾Znf t5;ļ3zk=wp8p +9+P yl5z:CC6 +|sf+= afjޯ>U{~9!S#x, saxo4 XgN+[0n~'~Mk/FǰdNfSc>Īߥp .o/-eeeeeeeeeeeeec_7{0_c'n=)4h5Zs@WiN_4Ø m+ߗK3/<5\w誗A.2h2׮竾_ o勜9Gz?d?rA7\į (f03IfNRsR',0gKr6?Y{,3_ဖ >_7Su6;XD? +[=`ߐO +|XMdL8gy9?ߨ7@)o:@õ8+ZIfhӪ|@pΟfw\?*u1qZ@9|8'ϗp~o|+S7 _Sm;+5/+8މ'/s>?rq.H4CP_3 +G^]-`qV-ֹٟo`Qoz5Dg 2Ԣ;dnL^=-wB~ȯ ;\rZ?~h9U{ܞgW BL_@^CG]%;˜ʨC\0@2k8qץ\z/|&o 'zA<׀ ޯ}gYzC 3v 9Ŧ~^5~毰PO#'ed +#36VWrx!l_dÌ)U_'CK? 0@ix1wP|1k(h2@giF\ū<#{~j妺=>x^MN@?Wg +3j~q_+Du+|ەAkܧ\WދIL2д8kטh2@*?* 7_}KXYg6 ܧw3qD8yw +Gbrrw9jwЉ9C0-4uզ3=v+-/׿#ܓ3p@r?Z6 +?.ϑڹor^G]s={٠rv߯g5Ǵ`Y/AW>^p/:oU<i6fP[s7KyzÚ/b^\8 Y_3 ++L@a|- U93AMBўgx=2ǘ̐&EGX+} mޡ媸[4`?YYYY)Ոt. +Wh=]*yZksgwk?oCŷ߰w 0D"Hz=VZjժUk/_~矛X?cm=kSMuoWۿߠM4p?p~QfttnV6f}}Y[Fެy嚖j^ZZlf9/^ie/fq!<>t3?;й \ǚs}khkvolu7ַ 6k+^o66MfyjlqohyVִZ^jYݗ%]?m[Bzl327{PEمf^rY{K~9Z_,1?7%^뽧r}w^׷8?L53T3;3L5ӳ^NM4ӳZ3t399L'I-' -&܎7cftdz X3sFϺgOy9psssԤ1>[ẹ~][9L7SSn={I-'tب˸;.{x6yqNz~ٙ;1S]RVZjժUVZjՃS ﱿ߷ߊ}]>yi344dYVszfņ__ucRl+_~14DҒxg +͙S&S'3cכ,67-#m>8`G |nrѸX$<{N[*+Y_[Z.f4 L{37k~6%/89WMt>f>3feYw{yv' cW8`r䀰XNL7f +/<σZ(kn,:{ڳVsVtc &;ZQmxgBm=&Ŏo{}Bajkjd-;=#\AΡsΊMyo0˙Sƃ滠ij;30Ьڹ 2ߴnv佞s'Gshi +K X} m2OZ|^]vTyLjժUVZjժU뿲ٟKmѿٚ@ 0{1@@o7e =}kMoͳgϚׯ]W.~Y,5taɆZjY~Ŵ\^ss-s[4% 'Tv9ާs$/7Z}\-O|rո_Jײ{^k<luoxù78}p l5N}`D3=1f`}&R8+OW/>*8'nZ0ۜ/t{=u +g3y8Wi;xضX6.?p =`Oo摰Iei8',|mv6sb|O}תUVZjժUVZIa3ؿ4>В M LP`ec^?}{>ʘHs+v>0/_Bc/5g k1oE/՜6 ѥF1| ZX//tzJumsv{nh6;`}V# u0/ۙqfc|'`=`ělA+=^7ɶP>7; +N3}m`[ytu~Վ9y`zZ=221(!mwܾd@3+vY1=\zgfsƞ_=0l p [Ǘrܬ=ز=}bw͋w%}uz5X~GVZjժUVZjWׯo]*(K>KF`_ja}у<025K6$_}$u_?{jpvE2?GŜ: ?gD_y[6˹a~f2̏}+f nn\17 2@ +k.?f| .EJ ̼909kTk}E_90 coZWnz}֩l 9j9[/f1{.W]z=otdyHEǵq o֞sXv/#:or g3:ޙy 3`q,4Ncv!/_fjʲ{U k_nw~LǿkժUVZjժUVj=|`K F 0A0/2C?? w8~4޺._%+L3`C◆>qJ@YC`]vLAc5C[¯ڍ\Hޗ=2o]oMcksiiu3S,\(ߒ +>dz{YL v3;:MoͧxSJ.gy_0$:+"d~,[<: +o:e/[ΰwM fua?F̹\ۣvp֕22wϻy2+Rf 9- =t- %%g᷉?gNY6_.Шg~3p6Ŧ3l/ZjժUVZjժ_]S ohzkth&t@x`49-J+^y=pjbS~1we> +0NjFjy~Ѱe3|:Nҟs܏+d::srt988`}ܿ hR [[i99Cl ,Pk+d0p}\8`v%0rJA&j8wwY1 4%7a5Y9l˜9|¾ŝ1##MG~M^K +mϚ-">1Xhx )bd$|2-RrKr?y;,ٞVZݕNfzpuޟ||=fK>oZjժUVZjժUG=T?ʲs.`i3_'Wi6c?-`7W0=9e_f}h+gM<Ұ_GZrjr\aNT;6o~q3 +XdNY4s<` \r0蒝P_Wk0u*oyl󅥡e*/~9~:ƩUgߜ(Nb3S-T\bfaMp֏_C}ZeŸm9kekKop&sx/צ]|/|j [W #\{fHǵg|'ń//Zg;ow;zGnmlTWVZjժUVZI 0>Ӷ_{y2ϼŲ LࣞWm `h17CA1 >&]ؠ`~}C>'>+4ޏk;|y٩s=?*i'KdGIآ9gչsy9eS]S2V{0q/#X ӎxߎqz]r;QojB-e\wD{>psbm^\|wdpq_఑πxى%ANOpF./*ٱ3%3W|.41ofJ'u^1[L |A7FLjo_sEsx~g7C-=hĠއ.<ك$37C~/9)7tzo>'v.ͪtb;foW763QUVZjժUVZߢxyQX:uϞwO&>]Lp@4 ):, A:N/Lqsfos\o𿘕X Ud t6P2lRLj*jp9 z Hj3FKN)Z7 ̉E憹Gt0Ӭdw 5 ߶zg r{mesطdT|hֵx˞s,7X])Ǭ1|5#5{rȠܐ}-'C)JyRnЯvgy-p;2fg͕z;C6e#Bn,X7 '"6}xs=}w_ߑ .ky-?@ښ YhSc%#x6fy\ٶ&RJdu>|ˢwWzڻ{԰9<~#__ZjժUVZj& Om|iS ͳj~YcE?u.=;|k~~Vw3 {_Å 1r;K~?EKq k2- Nt Ksa|hZΊy OeZWC-1#mf#13~>o7yѭ-͉-h{zf{C)^mx}m0> }XjY#d [#= u閑[]{pNR>kg1 +mݲ$c/lsEUi:sovh!<))>~)q jI-imUO{T3)7g}_z ᢭fYskm,QM>a]ЅuK^9kz&VXmWvKç<'8Xq6sVUϮXAǮOsgvhvޒO'w$is|rkytcvZjժUVZjժM*^='XLew?,_,Y7ܲ,!O?=[45};o`4aa ?XȨ}oj;S6 hjʲp%gXt %?./LlyaƬLu&/_:x..72Ubnm4b0x ݝv}[T[byb\}ehᅰ{\Ř3_jZCc s GZkM^duGliXxh9Z>x#b|жzf6&ƛdYF66 { Oo0ws=<;ru,=3:q;=gs(, ^2b)v1Y%+&<46`ކ?3,xCVliZ|d} p/Z=0CHO%_5ܛ%4{<^߯_hAoϳe_UD=xjȹ 1/e2 Ёejs!/Yœj$I| *W?hqM-so銕xqZ eK>~0]qBs]梩K&s%T/C_+{v:7•.Z.{9y!G3͙'EfmbCHEo)nK[v<3==X C{߉Xs:/95+uoJ=)v8>:$2dž_Ǻ9y~#C ^v^,6X4w<2F0gM?ų%&D{s5Z>3~^=ÌV[f;sK۷i}9ѡXIsvv֜4gGժUVZjժUVoVfv|/?{*}ZA˗ԯK 7|&3L|Fs~gLkNw +~q6~m\Di#acs:=s(b&Z̷ؙ'bú썔c}uY,ohP`}[b1x%a2h^GA1kP]>m]8 1_-6նb۬&3HGs̺dLrZ3wO~fD6!\ қH{~b䑬$o&Vtpu܅|к)7yX ~^ Wc +D&6ug[epj߽ׯ/_h4L|B1<mKgLa!7Y=~Wh:σs=+3Ƒ/.,,r̜̞Ed׶ۿ+"%Cx&O8; A#c8=ŞA͌3ٿ3tdǛIxh8?<&F8 N( ȇ9VOu<-z< {wy +V3c8xq۲`| -zy'].,OdI˷pؽNn?if'n./oD_6ھ8oΏ˫UVZjժUVZ߬`rO4_z\pn{ܶy=~}}`DX2a)2ۯ Aqߴ˥=_[7Z پ%o8 <23>%3CG2(N7add//|ā %oԱ?s¦`U: fމyg鉏V翤ms`p eΟ>:<ᅩhBeZlYm}V\pˉmc{U|Ԝ缎vrmEM&T2Z>[<:Xӗo*\/4oz~!__E !dv3>5==5LfONEKOV;fPIwy"] s~ -^GwN3g$uޛ%şl^57>|g;_qg_\^nHY%r}-X3V-$k7?wv']k +^c}g@YF$q0KZGDJlބ0? <#j>^q`6`uÇ/ _2㓋1;K{z9l5WpVys}ռ}ZjժUVZjժ iu0?t{/5^ 7$}#jms,3†ж>/:.yW~ȋA}‘!qj  @~k2<_Zn&0I]D﹵;3p9 ,:s$t_7e̿Vg1?wz=h21`ZAx@6<'u[p%I.Cl`:0O/Bb@l?|?G'Ņ83D:bx4MWe=qp?i{0̯hY[p|i8E `~!#_x?^α7K&pںn}z:^N0߸NO qL2qv;8 1:o;gdO:ߖb^O>s[w|D8;Y>{N`Dž]u}[W_{ߙ}yV)έon*UVZjժUVZߴ^h/iSqg헱t}{,_2s Ym82 .8c~gbdxc2ОdmHdO=vӒ3 f_fvd<(oPןG72ykJ/x-#fv>\{8~6/Q-c4nw62zW{o9nꝭr>[tq:4z: +9(גy;ex`hsJ.Ȯs ?K2*Z~\@i3)f'4=.ke_oo~/6esGHf̈)zC83SѱN <-m`\~nV_azm9ٺ#O7su`ǽ>9?.}vzڜHwzrԜ7jgၹ~.kzѾ=8_v{|/}׼}S(;/NUVZjժUVZ߸F{ *\|/!9n8Z9FsFݭv }W3E?[|ykS{]z; j~߃:la2Ff롙q虋C=< o&&JHRv??ՖQrE|jvUٹ.2O\P_ݖ^}s@?Bm~nChŒ+\{Rr%v@gɫ@~W?2RrBzc+>#Eo~f*.8cF`{.dM/g.sj}̏W[# o_i1cM53P=xۛuCI6z6=5=7qMmڱw~ヽ`9;?oNgwώԇjw]\\/ݗ͕n.u~~bwn޽`Ysw{WjժUVZjժU3z8|iN7*Nk{;Yh8_䂚l uo.{a|j=J=p2Atm~qnS2v h'K{sE74^X_~۱?e\ڿ=ř#,N4-v}mQ¤̪oSJY}d74]mVCbeЂ6JJgof·3`̀-‡vm Fl kY-o0<rz|8;,f=n;e +,NKx D!o~t0^l9`j;͠tS &pK-́,h^c)'F'x{:ݧf&'}OڻBw.N'噶.]jJe c˖D3VKW#D9+ƾ>/ytE7d~l[_[11O~ n9@kJcNvÀ +#҇df H27dZws46=z6%.)"_dYﹼ4;pN_O )ov`?g#KPKi=t }0@3Cf% O̬9/ZO2ۗf:>s>=`|d+KN/^_r8";3;>ng g=zR=ź(B`'VeOO;6IHV &,_%|Y ~>A/?1ߺ%>R?6%?YϬ2rG +4 ỳsI+_ϊ+hu5uc5AOqy=3>€'y}CWg-O7w+NXƒu^^s_mx!']خK춮=%d^kf%k!1}7|/c>6W0?~f>G `p 盟,EgH`f< +F/g7=mAIO:>ߞt};;hlw&w㈜]ArZw wkw)6zF|'6z+o˛fkr/^>PsHbYGj1 ;/=<>+:: {;|׻,y{gV,͇߿z~ߊ 6>?^jjժUVZjժU7>3~duy\kyTXNӳ>fCi5rKBg c!C{T?xgiy* obIWh2CGyO=IB=" /3u0I݃i., q9W%ssւm÷[Y i͌W?z7:wxXvrF`aomSOuWnOh1j;͟590 ש`r^[Gp/xz!Z#5/lm >bf\Ѻ`xe9[m`y)Lk޿Z1̼9qW.W84bMA7#7=9nԜQh5}=ߏ?x[G}r3S5K:u8J>+i`h9WZwZϳOߙ}%ǩ{s[3w:ǿ +'ؾ/ {|F|,[_VZjժUVZju|YcYWy>m>'7Uxz;癔mlDd:v9мEzXd\bb>GhښE=p 3 2@{\Y}_-C9מW>?{|sߓ%鳶v&q;0cCa>"wZwvf:E?2#b:ӱyu}U,sor~s=_0?4z辂kαi/Ã2͌FA^m>z@XRј+)>lp䏴/{O\@,:G- ^ul;3B/s~d Kg\`3s?j\> -h%1xv^.W8K.@'B'.  2`I']_{%3;n..n{umw~v]h`~ܑ?WI84`u>5oز;tS>Z/w'^Qoeݟc#_UVZjժUVZ5YܴLMyzawci+3뛝oi=z7ؔ4SSpjSs$;Y{d>|䩳9 ǃ~ ݟ: d}T}9㳂E6?2J#<ߙir.\a?990))~_td:W 1@0&볿ڻEz:ڿX/H Kc#K:L~t^pB3Eσ ˑ-dN2@ /_sϤ[;[[lX:v^`fJ81gZVԫ+=vmN gɬ>ί #k[Mf 3/|Ax`9.5)8`j |3=3p)Läf3ץuwȌ>= }_{r"ևO/[3nxSߙ9hXX۝Xޭywz}|fz>~7kn\l}3BX`B_HkiתUVZjժUVZp9\p91@F5+\.5z0v-,`w3hG1c}Vczod$`́́Y7ޛ3-vfሽC#C˗ {˃=|(Տzپ]ZŽ6#wḎ_5 Kc9Jnr䫠~ϐ 7;;sf&fa.|Vs dfTfy_\ ̽Z 9V,:`4g o'-- Ys'ۓ>@ZýpWPZj}f>!A֦ql9\? ~ε)M<_!v=u) k].00UrUVune}s,\I|.3o򿍖Z9}g(|PKJ[pf'h<~sӄz_zz/.-3Jvo!t~oz!>n/Zݛ杘 :?>^a{?~˻',.4t{߿7;k9קr>_6?on&jժUVZjժUֿ@gCg7#75>홖uw,/\+qcwS &s14%{tv63H1d֘5Go1d OG٠C{~}qEc=z쯯{Ȏ&/yį_I72"c-vikx/=mn%cQ/̱D%e{?ͮ,Yf;=ڑޏ싢3 .և~:w6mK0ڲGok]51 xO΋UE쎎{:ok\j6W GjgfNmbٶ4ͬmAƈ9L4XKkc19o/[4C-Ż[oe;]?cQ>kM0\oL/rpE_C{؟;8-\ z@f<d1~xzxek3|cI͛[u.[񽽑6O}{? &>mxo߇wgߟb})HݼVǿo_+ҭwL^mժUVZjժUVo_3-~><3žc9,B?iv^4{sf:P2J3/2V8#l͐v~<3CQh<'/gn֥GF90K={-9l[ ߒ5^p3Y3Ar%𖮭[XW,kֽu/v3xi|Px`xu8ߞjx?XP}$}Aszt>9:_C]D';ys Zݕt[7V[w;{e16\[Y9dž[hIn0Pogԛ 6^-_ ˉ'=e~3DzW YgYIZRV[z+Noq1/ oN<x` hC7#`~I;*ٟ:4~oވ\_[ׇ~N | [o]ڛ>'/?[ͧb~z7W-?|}^o>?? .I^Lp,`2jժUVZjժUC7%v7ngX\/^/׹hܴ/=USqIx{=P/}8?'OK J~mW/_Z}Mh*hW^Ќ ;B7&6O-淲Ь ֵNj-vVġte;+c6^S ݝmgھqYrKCZ \:*V*v,ٽ-h/X,m6KfkKw)t4͢ߣ,? {B8b7OxWizGq=79W' |#o Y'LEF +6<|>Kn<9k.^]݈4^]\zy}u-*X͛ ѻ2}l]5wbmg;1w MAK?k>x>x>/񓴅 l 2E)˽OZ+Sxśr^\̅ fDnkų:sл-ۂymO3 Le^_4yހvO5fc8O }F>F9!/uq@XL%c!°%Yz`$5Kd|G4jh`|o[h +Z#Xހކ0>*M k +GD5 F=Ŀ̖;߆2 9n7/^/lg Kuc}.{[_4aɰY-\YLJb 8o.O<;u_K<&?qzS={KI+J==7d8>c~fm܀˽y?f//>O + ݟ_\f~yģY7}UkrXF'Ff,3ޜo@Ut]f<%v̏Xn>< ٟ 3?h>4h%.ՃڻK\Oiɇ= xBz/=Hvt}Kt=]= .>N0YN[ˌdgg.r6?8zB*&;^[+dv bKܯ=Ih ѳ.wM 9?jnΛ7';VZjժUVZjղN>[reכWMMZ md.͚[ Ҳ>Cw$pQ-:rSY_}c$sxRorb>Qi=P<3y(ˬߧϞYLy_`~#y_R;XΙkEtfI. _颱skN@4z?>dAXW\|SΏV6;ڏ9{>bss@L0imͽp9rhm'η[x6{GAaͶ޻CJsg&-ۃJ0 ײm]ء|7t&t~p%laeV!bKX2LR+,{1‚K19"gr-cnQZ+;|a?Xk K-^܏? ϑ747_ZjժUVZjժkN'K✶g&\p^:<2{4bVrhChmkp>_a[ɸ +LX9rm VWs]䡲Cwh73y`~?n~= d~$3{=k:ڿv~<zڿ`aʬ?~r[GkɂuCVSlj֯,_-t`@0K\ػ{rDciȜ>88 _r>4  `0 f "]_KxOęFGNQ1o5p>=o; Ӓ/ ? >so-:~~Ff=!l#pcu{aXGW4t1>Ȗ11^,qG)5=+РxaD~\߻~_9zJ'އ- 0^G:\^> (x#:u`|1@}|~ד(x VZjժUVZjK +|Ɔν9s2kqnl%b ߣ#c^>8a+f++XZ''Qθ.ZCDw891&}mJz;+3{po?>31F|ccZX::cxsB}>3Z] `utː3"+C PN!sxux4/Y_ޗ Z2\ Ph'7rEwL ϼ0O/;:rMxog|wW?jժUVZjժUֿFM/zѢ;-b 4\Pei5m + F8k !Vgi*&h!+j"P2nIr k*0%3ӓ(3.3?iio{~y)-bO:c5Mo(>g!Ōgˋ/~̍s+I쿖!_p@4Yp`^{;K߯?z)oPxdqz`x-}}S|3CWxb/iza}A L]chu6?ֽpv\~pYGi +[W +F#xu/8)ۛ%>=S &36V=o[XL70_w&f).0jtd6dK\̬6o{cn(U:G{,)MnLdA_5>݃tw׃\]9[M?^ICcHȟ>x`{`nuwpěh jժUVZjժUEާZc/?s=kΊ׶m2gɡeYd 0!>7X0=\1<0[/.~!$xvzJ|mF:NgG @of}/=5Ctއ~}E8ģ#:c& _di`[W +?=v]9q=d5=k;*lWϳڌsEedur:N +q6-Y]DK&x^0(;OOwkߠ0xe]U2Ad%$8~vAkudK?! >Xο\nuE6 es]|Q˺O$:ul\OvVY,Ma&z~AZ=7m9&? 0|<1;s3S6^q_|c?Zmp}0>1 w9o˫]#_YS_cNt $!OIγ?IG/2C`YhދԪUVZjժUV ;c̎2kǟ2X;a}%43k;^泜iZ[DX OxS,&mM#2@~<~$[{$'O{O^>I74ߤ{BoLڿq G.qffp2/_lc_X~VG~{j#磗=h{Z u|׆.k%Kͩ{p?q_$7?:++;_CeEIhM۾Yx` e+8é as͇ϖbo0ҡ? -bfuvC˦3Lv0Y A`&rG<:' MFĚd'7+Ԥt<އ< (t]9LŅ8ލ4{o1оk1>ZAMaxzn.K~.wz"c˺Ld;.wgֱgz/. >K:A2yw{\OG0֗\r$ũߍuZjժUVZjժU +h0$s9f Cg +oz.}bX 1os6- 3X _Ate`g1fINqn榚9Fox{xy[;ߋϊ/߰H 7Q>{m&u-S=7'@3X`h'N[hm~=7(Y"z އ '^?&,_Ŭ?z01{:1ǯ頷:/,k4{Ot] +oC1=] {\-r8~_599uz2yCK7gw&gkR~Y\+n7%}|Ԝ7>?O ;?e}u^?q}𞷭g~ێhv6C'J>ν (yhg 4b_ZjժUVZjժRfrmf~o8?XS$/̙ Y]UNM@d/.lA₞=.y$gX^{f'ƥOh& ׽nOڽ/"˗'=,z_ׯuޑᡘ'8ϛsiԵ8Dz?QŪﻡ׸7+aH2 wof]n<LFf^?k=_Ms&Gٟ<|hN+DBOz>{> 4,>M{5 ;Aezf%c8K р7YD%LyEFo.l.^o`ۤW4qL1Wo ﯞ|xaf=׿1c2}q̎ܳg}Z96l۳,쳞ճ,WͳQ~fc'y`O㏥#7g]!Lq#`Lis};onNק\߿#o]NA &#[^VZjժUVZjekdh;̲-&sD$XߋvPZȫu댌?B3 fqN:$iG4;Zh`z{=y$ ?|*8 97_h_[|Ռnj{uhL[C57ťx:ArJG+z2G OWaFyB/u~}/gOjb)b*f1z~lkJWhv8.^vtrŢ<*zŘI!+oܫdFc-;!0璮23v}W]_orӅӶs{1O:ߞݮ>Ǚ}>q͖] yg +:ɫan:Myγ6[?23yxvy>C˨w+^s3y"dM{Ϝnۘ^m \OJ~kXO<|^%"~w'B~{}fh|̍߉inٿjժUVZjժU_<#n`uſ ےn'R2Vo,}axwͪ¡' (']9!)#j>x3@ Dٽ~E\<ٓ?'3?~_H-~-8$_)ڿ1)qG8}SS$rvfJ䴯wYߗiߺ8*)S+ⷈ#sd' ckWy_Ze'\ xxY&+inͅ!NiدSח_ˋ灸 39o|vjà x1eA1#ʵz3r?;b0@?rV2S7O0:;w>-E[N|N.rƎ =s|<-n|`=W0詘iI >Idfy6 ouegЅes6~u{;ٺRU}zHgŐM\;ٛ7͍g݊Ey̿Oo=ꢹ8k\47뫪UVZjժUVZ2&xVw7W|Wm+f_r`Y|k";dё;}'j𿥅9g0[oN<ob#}4??KS~d~;ܿ!1ol}in望@o9D:O1 g=4_ڢ+o17tp1[~o +g Wtū{rxМ:߁}z>^ۅF#1Zӣ s9gM^EN̯ap6 33wg̓Y#r\2KH{شzkt9b)<870'L20tl/ӑqptWrNC,0Y[䢄n"Np|b*Xd3&,̺+s܂4KE{g3Uz|.xvuZguͭ-}(`[eimr +Ɩx= 9rjФ^77WiFZ@f1/?>ټ= ? 7תUVZjժUVZY\8i;ʬMZ%Zdɦg1adsi+R P(i(!7h;P_dzQlȱc%DK&v%frH>yÑ9ܢ 5_۶_kR7^̯4;4!"$.K6/̎o^Hw(sA1A2?N/ %7EVsE7{4EM8fdHw5m~]5ua6Le4sȒ(dCPzd?+R ;`eo=|zSumv/XN;GO8_>QC>Ns 5y^7bhny+}#}zzYO`xV k+>F(&w#gcz Mۡl`-}J^G3H8bs_H~ N)|\C,#f纏[Xwny#}z=>sP{,ƍrܞ{>d_QVxfn0y >F,r|xqWuKrs-E5t/4l{|{~}QVZjժUVZ~ +(4E r!f?"KoȐ[/-ݞsr~sI#~7v&/Lbi{S灘5ͥX[κbu0oO@+cWN}H3hퟖD17!쏜w4IDL$GZ sez(g2mλVL jc8mRYΡa! OVp9c{.S/Lm WCo'ٛu#Y.S,2X_fw 1E2c7vdy3;V #˶]dY%-f9:>sB'd9é ,.|Fg姹ka~%|#N]Za},ѿ0&ƘϺG)zMk83V̗.f \;8w8<~pLpE;X8h?9=Bc.3޳A_)\_sBee-ua{zzuտjժUVZW e +ٚ+וkJ{}@f"}Ry㪒D}#Q}ޠ=AkU}\+ܤdJ*R5o~H9ԪUV?Z_Ljkiuu=,ٴ7Қ5ʺ{yy--iiy5--.8Jr~>zn~1-م47f}^=XL33VTOMFOͦɩ419frO8'udKC#i``(=x0޻n߹nݺ_Nrjz+^^z-]v]PL7noNn^Ow{~wA᠏?88\#z^-@Kci||2ML8_^'׀׵uO Ź43k15f&Drav&-.̧eO+Uz{tceMW>.5ǛnVP;NڢvGronVhNnoƺJJ[+nwv;ggv:ivl֪[k喏6ז{Sgsm%mqnx o#8oھwYi{k[|77^u&Gjmž>ZnlŴ4kk] Kfvz<.<6[z~/=\kVS>V9nRXntS2>w]Օ/Υu}n;>~3fgf;A '.?3?1>&'&wB|7u<3[33,%}_Zgfχ=nÿ˃txtO.tv4tz$gI:п}תUVZjժUVZ7XWzMWŐĈЌp[Z-"nx wwylQ +7}~3.n896".0,QFG`u=Hw2!w트;bW\Ӷkݸn޼}~oc!;'?#odh0 C}L})x}33Eu-Ha\;]/̴{^,pqaoanFo*-'LZZ \!m,07s@3$ v"γֵo ςX?xcvk/Sku>mo,m8fI{|{{{Zn_},}a~;bv]® |n%rpFq\OyٺY|4p}Ma6g& +˄%FtB\׏ۭy73cg&fHeCsMι!jdᴼOeƷ -7(kM}nF]~MM ޽{O&gb,@X{B orbojj3?zyy?=flt42O6[>XL怳n>k w}Qu=a;q:<>KG'b~3{׵<<'念jժUVZjժU֤(r9' j WVIsFYǚ2G϶/bF7.7-mO?kt| &'tQq`ptzܹWC-ߍewMC߃o@Gg8, [Il4r\ZOo~~wYZϼOvq~:8DZ:ڭe0Wyoxi XkcoOmĺmoqC_& ʜtn iK-߮t}{'{u0EQ:8Wtn>=M~ڿ~G 3O [O8?4Gh'+Xxײeޡ=G3,:'؄ΉhaNڅA?'JG'im.WG6ZņFZ[\'<,c,t??K_< hL-lXb 15[ç{ +/ֳ>&/_l+4]urC+r~7Y/u+|bF xSx ^[v߻{GwĬtx+]f@.{^~:>9VZjժUVZj +Zeq&{H%n4;fđ> Z443|% ~gﯴ:3 :~i|Ə/C/ yMSh29g7 vwkbi,?k}:$?|b}?{}վpW0^mfgNӾmq=XT0]) S~Ff<BO1o O,7a2M[k.:)-}-Y'm51AzwWn-bK'6H i6c{5\ACKU4#8cءX :B3ĻnCՆ6k6ʜBǰ?C{{eNj\l}[yOܯpqX;4˼<3ss4 T7ώ?+lx?x; xR-nGWx7 <#J > ` &PW+wbץu͚v2>83:/1$kȘZ:>Y`!muDG'xZ&'C |/32W z@߉9:8L{xww0?.>]Kx~o\Y(= IɶXev#f~,;fc9D >ʼÎPGkCXvnuX?1oQZB"pmI\~ah +?mm. !9nD+3y]|,co +O8^[o;Wb=;,t| m !+䎴]17ca6빹قG|}ko1#^4 }n (Lm}[?u:lܹ|Ak 72/s8_xفjy n}|l%פ\5_}dɯ%s?Ȥ9ٿjժUVZjժUX lO?s?-W3'o&%߂x44=!GxS8q)i7as1_L_xYș8rx/`4 L_n['sߵ` x۷,3w) `DO7~(LR;|]f4os#:7ב  O[crGZ>.{I'YMTg[z1-+cy9 =mKC~]}ⱙ#蹂G`y5s#,ĽβV@[[Oj\h 4G uX亅e]"0_%s[V#؞bgn+-"y)zC.ηp@lTׇC3`=?]oLzzWkiռ|7 \k^z4 &_νFgwg}/EKo&g:@{s%~`,Ñ?\2/<ι{4w2sŒ~u=vahѪXֺ^VZjժUVZj=[kꪘYhrG՜\:| 1O\jbSh0ਖ#ibT, 6:G/r;?Inp\ǜγs4=ccfsp:ew8`߬_w eZrLGpJFg?|^|Z~-vnbra:wmmqgTʒͫΖcjgEGHOu"VGfaãX/,غq[94\~XfZ9sI{M{9aoZg^ fKG[F # ϱ8!mӆY.qjݾ`|q<~W{ֱw-3\`]G+/^yEK˯^~/ꫯj@yG, +@5?6>aio5-G[7f?}Vf&g3s23ry2~ht7fw; s&"IH{ժUVZjժUV$vLxM#_v=k̢*6.Iq> /cZ?R y);;} P}Gdl>K_{?ZjժUVZjժWɎX܏% kZ)~Ȯo][su<ϙq߁^3;|uu&^8ygxa wtt4sݺKߢ{9ڟQ+IL¬so' 3gU/-ZYj|Z1 >2/9YY_f} ff,cnscu9zڿOYs1`{gnbOfelW?JwxxC1; "3uǺah՞+XOdRww;meg_#Ol3ww66 .{u@lN;xu}}p>(s4?636:OO |Y^m |Z }znGp7knٓD 촳g<]ytKww9ժUVZjժUV`HI+++d~do_~{ݚY[?4}+m7MZwQF8d}8#xT;wCɱ!3A:ywCDok0@zCe-3EK:ű>[P3z^?0_o)~o}i?+?K= /{HWCWtg9+>نA,Xؗ\g֣9!;kS6O?ӳGZ^< +pG?kCNP z'5}ۛט`~=;; ܇w2[0y0p=c`׸ 8`2_m]f20g͌lViU;OֆOt &s7{G}?< <0||UK_^mZJ/Z- ,ݷor ,,ph|~KLkFF345uWxw]9ժUVZjժUV-WhĚėgB^J>Eyp1MTNY76a=lp5nSOG b!phax Mx7{C'M +h}1+йCCCb?qJ1&Mx&!o3f8G-:.F ex eG\]e|Xwv?q#svS5A AB'B3S#pgYQj0@xɉTͧwx\P,sexTϚc]Q繻,:3įI!e,3ƺ}а6x߶َxhmA印ͼYM`ގo1oghaGX> +-Y*ҪjwNK@i6 9FxW8x !d wpAe~`\#S\o>%NlwDc}Codtg)̝3SK?1ժUVZjժUV圏%%fͅ4_a~f SWl +7+ Wۿ)111/r}ǧrF76@q)3IwE2g_G'DgA|x?=ϻ{Fsf%VpLoki{Cw4wK?H?0K+Y_X:Ϙl)0͹bFn<]g>Je1  ,n{l7x} +?$ +#WXo_AWyU֤_fplov(^c߳꾴zhY߉E<0hz K88'Wc|9/1ɃFX8__/O]69d9B9j |歡 ^?8ǃ~|,dZ'-:'ٚȯ >*4{o2qQG`j_‘33L727=`P,z\:M7໽Gt|~nw ^}|E^4g6y)zH vZjժUVZjYEo&vIZ-Ͳ[zx[̿;'2|53+7&s'-]`5|=ٺˬrLDžO-}fGwͲk#Vh@_\mr-ދ'^y=VZjժUVZj+3#gv~[fksX"__g\d_3/}it^ry +ۜm}? 8h_4;s$ó㶞/ȨPzx}W K1D<6JZJ8?#73Kgfg_ vb=Ozߺ8_~=oY~^#%ۭvCC&_Ϊ(̪h +++=ƌΖP:Ϸeu0OZ>y~ɱTcGn< aap=f@oY>=O`G8Q:F'7WA#[kC6Ќ4E<(Wߵ=Η庐K1|{䞇9,˲^XsaF})neؗ% $O.m2ա 0! ztoϹ=0ϰ5\MkhpYqmU,rmV_ZjժUVZjժR51 G7s7> 皙n_ ǒ`~h|%#x^J~.,m_dkk+FVڿf3]{: mA1^A{(g%"V/pF|xg߅\\oݟx씴dZmwiA?fuy9lm&R6ڿ}%ÞEfmX*9rI+9? +~: -3t3%dF\_n`:Wl.4h1ȳ8" Xvg `fzz}˺{Œsickyt +c i#w[i`1+9OcAX\|#>S/Χ%}.+^a~Ͼ9jժUVZjժU֟S--1ߴ q;`~]K/p߶"]gohMm~GGr۽m=iI/?Ir3koXXO:5Yw~=rB#/;Nh,GyqN_69ab3ZŒkwqQz"r=)ZҵtK(.0TZ4\p-3u`[ߌeͬ'nWCF'% ߷ɸתi/wb]q5߱t|umdlZx|th?0-Zޅ/#g{X{Na?4-<놾-e6aeb q,tlY/l dfZe=ap{l^u& ]o-i?.1XfG2OHAf.V`Y3w؞ᗛ,L[}!?s>d{9V3'&!=gJ+=,z]}>.|Uz٣%\??jժUVZjժUϺ+kMڢY3@_?ǚmzBZg_7Ck`wKF,!ŋc3MɌ8sD򵳆\fzOx|ˋiY\Z Fgt-rI$ kOf o7{|)n.svŞ̭̿J,sb =rd=L 1.1@{qtk^:*,93Sk3-.Bfx:6 }q]f:.y׾Gnqg[B].DyC#X4|}] +l'9{[~~idJ]b'6dd'})t-~\8g K饢,L𭷯Hx=f \>Ci0gd?Q-9cJ PߝKdb!&KhO1Ok+UVZjժUVe,:ϣI-H׷ V.zܺ=7L7{~2?z 0$Xiiǻww0|_h25{ ` _979_5{쏼?$&hG`Ek83f:2ג^wmjZɹ%C?r@V Ws& pSZߦ|t}-40@;ZI{U3yͺF3.rdaMܺ-n@l ^E\}kɡ-M#Hct768ݝh:Xz=3  .E? ƧӹKh}!I-"<^_s;rx?o41/77>g#m\Ķvzvvg\%9[ [pU>f/n߾Z<|ZjժUVZjW}SΥ6ax|78 w7{~5љM33m<}Z0g#Cb9.m%L7{ʼ?~7ڿAW 2,8/-,#Yڠٸ~H#uraYپ]oyAO,К?zmUoY|&6VҦmZV.3: $fv֦ߎȫ0#d/z/c¹q'v=g3P9Guk}|4ʹؽt`Y+Z#K4u{mk,خu.lme'k]bGp k3Dh="!z=ݻmpB oyou?m?un2W䭰k뭴 +\Y?ki}_89kbkj.V'Ƿڿ~p=$1xҮ {9gy~]7+N`n|N-HU.pd٪bsC~Z?Զ胆rcs,?qw;Ik n{='NZkha.!i]wwyu?:={cؗG<09 Y[3 LG D_݅J20O>}m0SOw|v/FgW^~%k +*xMq;!FG>A#~5*UVZjժUVZ.OJ,7#85>잞SI 6e_4&̈ ^/}ߘ#h'2?}^YnjHC84 ']߀=,?i,>g͉rF C}k‫+H}ym-~y񿅴46 \I[}YDKH3u`|;:y':|-q=tZnoW^LO M,S+ݾfǫnxtzxfGZ'gbdx##͟LqrG'kу'7b'Vlo/O˧.d=_F+ZLP:Faz_|>J??Qg0Fz+Oph޽{W3qkժUVZjժUV?^WMOO3[60%#X`?i&܅a>#zLvh_0|Ȭ2oK3#;ijX%ٟ^"&$1.snW؟!>?l2v-3麰?rj + +ܯQ8^&Sv΢8A:>_T[ų44{YW+ǏEg~B}/ctyzygm#[A+o}|3^h|u- .☩`wy%ExcN?(OVh$9OࡏUs9?z/RWs/~OgهhfaA/>Hy}f'_J~/Bz啗ozMqիWҵkW [7SWתUVZjժUVZԲO><O0&Q%YpB~KNH0@ȷP|NǗh`<~FD~4FEI0?^Ǭ^Ǵ?u 8#ߜ9̹fX]|1oIo_ K[ܯ//=OڿMiV\Z[YM[_]_Ξs;fή=dxj[/m=ߞIG&-xax}7gRd+:@k謟? ?΃1Pd~ey\fZ(>_GbxZ ?#MVq3~c<>ʅ`6ۙ3#W >Nj5zby\?v4sI9k_3;#0F2I S A~◽jXmo>=/[l|sb~A(O?"ӯ_ǒl>N[[G +3|t +t;Wӭ7*UVZjժUVZ?؂&.9iz?sGΜXN&\d{ 7Z2ˏzb4 |_psy'3sntzEQX=_'yt}~b,_Tq3SianV649>2 PVV7w'bm0C?nW py j^p;2(N=XjM&V_E/6КG5hS<{~ٿ̮4ܜmvx\,I>1xvvj/>'OO`sQyOtϧ68)זm s =d#wA#HVǾz{-='>a1gggz^4?A 0q;<CH~1^?/>g?E/>K}yL=nsXyOG|>gJ|ZZ۾} _?3gr&s?e37No󎴀+^vZjժUVZjժ.0_0nw8jo(oe~ _f? iayK ηZ9x5.ͧeok#CoO|gg~,u_w@z?mۊLΎ؟>}F.2o +`~kLK =7;"wpy漊>xɃ.&ݧϡo^xo'훋,}axa{dxyapV0~͍7wقbysxb9ߑo?I,!xo[l0-x9>P>x ~ck==#YׂLi.f`|ww}=q^q?;bW8}ɧO q'_*}oҧ_Voҧ_Z`k;_H/rzӛo%}jժUVZjժUg6lܾӝnỵӧ0Qv#7?`lQCZ.8Y i +W,mzL:!k&+y2$CKGabh uls? \Lˋ s. z_h?xBzZ[YL+33毯a~,N=mےoW,3EG~HZ?hu3&tf!O}ߣa:'cs<Iz֠*>cX3Ur쏎 /h?x\fo {~wgzCo<_ǟ_b0Gt~xc}f瞳7{<^|ꫯ7x#IFTWVZjժUVZ~U8_(ܯ[{(=_9yѱ 3@uZۭHc Saivasc4*68>21-:6u%I70h8ҰHJSC2O޷(87Wy_^_ϙZ[_]R/>_r>Cy_~2o:Yť5yDw^}>|jժUVZjժU +ӛ$ϣ6/f]3Ü`~j_aF'_1{t܋{2h/^`v~87 qim4X%@~ K 'F.691yN8`ds'pJL䙙'Gp/ͥE5,piiE/%@_6j_k[K@ui776S[ۙG6?֙gQw,=&w2YW?Ȱ8lO=2O2?5qoeg {|sŹ O27C+\X ێf.^]"Sc-9^f~fyf'X2'G$G`:NGHu8[$3Ʋη?;"D/֜ky3LIKx"Cq@w׵W2rHάq: +}s;>*JvGgyw?My߳sqcПYX.S 0ZgժUVZjժUV?ⷭgMFzg>x%_롱>4u9W7u +p89g̹b~##6d {Cz=|3ZM'FMd $pʜ3b@h D8"y)bifj2LW \0ͦyqe˫V*3~ܯbm͵մ<-8`7ڿse]?߅y:Gy_k!,hr/̯Ϛu1/weës~Ԝk<>V4}ȳ=?{lߣӥ~bm☽ cu~f}oqc~{O3#sD\Μ^,ĥ3s^IJ0,k.x*Hz@rv1z4'y\C2tݭl+\gwYWG^o?S7.H~G?cw|>Xq?X]3^s3q=ٗSOWzo{Eoo3jժUVZjժU֟gCۂy=Qֵ5p_?x[ne=Gǰtuc8YG?l39]b~=SH^ǴZ)rB+ a/ذAӓcj1@kfj9ӓiNgo t˺z?=o>myX+\K[흜_.K4,{3uĂ܏ȩ W`𿽴 ^@3__/VG]C32oaVֱ"#fZ2pvis=CCG#1G3|_x$5ocp7WwHw[X[a|thxic~d3['Hl>7">k1fm޷#înݭu[@ex]>_l.gt^Gw = S?_:s?'⃟~ 1/}7~mO3oOzކ]TWV?ZNNjw֪UVZjժ'\ _eoz“bzёy>XY?gr63iגsg2^75&~G th;Nq3dz_d1fXWr)qO\pILpuyY:Uu[# vt}=@{asSpy!m4,رֺtG s?gdg]k Y'񧺍m5o{\qAG|n/`Y6pgH)<^i-G{}\;|bzOO v6̐>S<90a -8>e `)w.tv׏z<.}"= 0 c"kyO(-?u|ȼO@7|?i\[f֪U͵NZk遥0nVc&^۝lתUVZjժ,m6֙GD6TL.}&Ül i}zX70(Gz9H&3on`8aoMlGy{sr"pY!'x_xgbibgn1O/kX}_9xh}3]ιc1KȲpfvff͛ҵZjao][J kij`Q[/shnh7|V^XVZjժU˅ ?C@5p@71AGq;|_aqc#Wzhe/c.sް ]:LÃ?gDZ>9zAQrO +`<"Ҽt0YDZP/MNgԳiyA,piQ57o.@+ii-1+Z@_@X%=`GڂFb9JbXz=cQJGnāqP2MmmCf/w< [h'ƾ vlt~Y;>pɱpt?~k9;N'[ٱ_f~{iÓ\ei')nGt~7ߤ/hK/999ֽM| `mEsWϏc8^uܗχm~_C63x'?MDf3V_ZXm4p!M/ z[Fw> ۲F0aj7u5۵ 0ش5ymV^XVZjժpôӬ0c,p(EnpM~%#<;WOϭ`d>/=ǢsGqE?dङ4I9b}csDD ɵ^0K//~]rq)X~sb~3^..1s3Φ%-Wf㯭7AK@؎ ݷ"ߦu;_3=0w[q%y^z(s̞7T??๵xsfHh=y`{3Ku~`7nZyrr+oWh_}Zv N;Uŗt< 䶷|E^y_E7\0']4?Kn8gan߾nܼY_ZZ_\Hl=GgӪWWCçƣXϽ|/{oml<FZ]]!608_!4 Az7mj&\=a" BsKU]VZjժgRc杸p@rp2wOl/󼼾ڹ8؞{p<}/2d90( Y #}Ϭ<pOh_;8rDx|C=Vorb뱿e1>|嵴`X]e>xW58 ,pM[+6Ͽ߷[|@rB}L6eƯO{1?{axw/zjtr}f9ÜOS|=g_lѳl-~\{pޏ'??܏Et'O>c ;+smN(ynP_BfK0`}:wfmag fwk <۰=]DOt?&;}@owq+=9=Ooӯ~MmZ۾\0-X~ez^?3+Kp%-5r[_]Z>|Z],^ڲGﶥޒF5o}h Kv vKr>Jև?>d+sNkkg}ޱmo\f}W73_4[&3ooo2I3JgNus)֚u drݮVZZi :4}RZ?fXֆ'o~f+ s+d} wqx +x!E[~zM=g[ KlNVZjժU֟A~NY ]@k&ho0Zt/ߗu|a9KÇ{p^tGل`瀈ߍgԔ\YLÒy2swo9͉5 ?6P1[!cmӾuz^~֤ -*.F,pcUKl7Eۿ3JEV^χ#] ߒqs#fh{?}1k8_^'"3J0.] ,Lٽ/ޘ/OM=;/tlO㞘=c{.§3zu>g1^tG:'ths`ßA /?/K?dHw| CO柳>mM>3+wN[wON^N~,/(XZ̍mG|7OotB7xxC(&g'oLJ, *mHv$T%5!}'!ARU?ϴaz6|7SRkX[čfq?ϟ>{8i=EGMyNz}ovVO {2;M9?:_d7foߜ^ b~o %|C޼{+@#K8!~SF{6]ҞF /q5_36.Ծ n>1A9 66, Z^h:`kCUFjuhGHL57u4R}ן\ ѼўE5n}i:~]>G7j{Pp>4z~ZAi>ԜqCӡCpO I.:3%h,}!_1 4=tMCu[uϼHbM1`ֱ"@},Vw6\g?78ZdzԜ/qKJlΝO3=Lt6hLlM:$N>jS<Gw~)#G{$}1C4zxEo`4ۿpz_xfX3Gꐙ빂g K,Q[B yȑ#G9rՍ^ 8}1gS>xg٤oJ~1+/ kj_ZGGoش𿹗6R?=]O`KK_0G-o[#yкr],pn&3L3 +B+=ߴV;w~WaWCJ']~jUkk 1Vh5N?VJ| Xvi}ǼYiukG밯V q v] _'޷k)ckj_zõaYyh]+v:Á8`0(][3Mk j~Y ӏc& !Aj'W\xxӞO#7[`c %Uczy `Lz' `t|o#߂_\8f ^AcޏV+'}}ˋ[?m\[w]̍ ^V3yOZ=twxiV/;d%ߖ18Y6CY*P-9Hid#ǶzU8q; yYL@H^K1т1 j8c˫&ИA|_n3$u$VmOu{}quFO`S-_44h  +X]Ϳ.~hkkNj6] }7ii>w}1Q<\ΥꌯM;ΉLq6cybobpEFwq.^˳O\wm*]{[p K׸ Wc}WZKGLߌsoRxdo*ܿ_9r|_ +ɑ#G9r1kl-T{=='L}Z`颮4~m4xώzwp)> [oziwUfnU+Kߢ$Q0y_ں{W0?z 5m +ށw??EnݵyFhZoG@س~ZBz{:GO}o@Z=.u=-jc;\~wW;h ?H_9׶z;ԽFn8ޑ?:#}{n ;pk*Mݳexް@v D؄#RS<P3,] u:[/̫`T;0?F{PDnMOf;C:p Kq?88#C'׳żӻ67ft1#1/:뉴#4xeOf~+; 9K3'{8ݿ~n;w9rȑ#G9rȑ )Чf:%/]8! y\OO_=}g@z9ǣ7iOoax4 [N>5>~P6C +՚X]#(M8{WK|OjMwE9S ߂V ^X-ѺY+ ZY|4'?y<7[WP/}u륱|>Gi-FGqM?9VJ/;gQ8ɱ{19ԵX9_߳1+_GnOk[o|}O~瞙v8C8uO|u?&OH⮝>cxmʻ֛tI^/75y*\_'B`⃿`]!I@qUͩ~ajSqVxI3?oRh! N6+pJG(MbvUŨHo2_/A4iW#Wū=bYቸ)9>}>/y2yth<鿜ύ|<7fkFfo>!9#Q1Ab=^hC]O}brD?;Z>1@׾[[q?bi>wqMZ-ߝo?kig +a{^͑#G9rȑ#GUo +Ysdu)Nd) pyCF!Qf2D2LRRJTLmVmO;/k}ȁd2uٲs?ןIo[/,쌛_M K#aA낛oqޜ?<6 q3Ys:3;ߟun<r3ec8?Ҽ[\sKʲˬe\&2.66&mmupv;;.wہr;{w]/w`v~e73n'm{P/`lSPؑ;-\{WaRcwmpDb_vs\O\O*T庫TRijjV[Vk5~os:wgW :=tCӳK7ggnN;s~qq =tԹ;;cc^Wo뵻tW~N{]wyq\'!tkJOǽ9=|{Vx9>Ӯ;ܣKx~xKuyG`C7kpzzΆrq罆;4R]kׅ)^!_}ίz\t_^Q;qjuZ]{}{н_~wݻxO#6Bt럏 %ko:>n/~O kz>p?=jc>os|Ï^s]NZÕ+{ _~~LLL;wotXbŊ+VXbŊ}@|/(x}%#_/hoV$7䤻?C~k<:`{z'WLd~p> +EEcUz[2v6en6 g<<;#]A~ :q0CڝB`ggV} xYu^Ǖ|x wy.Ϻ`烾kw{ kY%G3N?ӹ=sti}M·kzPṑyCdi߅?%)i tת\qAuM|zx.gnt럂a9?7>>GР.}-8n7.{bizWGbw<#K8!V'qlЋ]>DqxZOGO>>_@}W?[ +iwob"Xb?'cŊ+VXE>g[N歷㙟o~R %/L{<:x7oNk=y \NXҲ[^^ѺV\]Y2__]rk63b뙌x ˻ (hIWA-x)m_&4;5FK7\QB/`~kᇿv[GfM/H|EyWOpO<V/n~A1C2Gr?\E2O>O KMw=kR dx?n^bŊ_XbŊ+ַOOAvnԳ FG:3ӾVQ߯~}g??ozjo W>k uqq-.A˫`nee +_:޺ˀQ|k8o70n~ۛmmmH_e' +|w!?es=L '܏,0u+ '~f K1BިC#\<)$r@J6]VsJ,:N-3faܝ eoߕK&K#k+#T/'3&_۫+;!|s=}|%٢%N8>`Uϯ{Q&kn>rJ~|d+znkb!8Co/mŵx_rDp?~&|[ǿ룷OqV>m ~]k{m/<icŊ?'XbŊ+VoAG^3`Z<^x.ٛ=  ޷k%dϯ ロHGXgD[op΃a'pm/t[&nRd|}kn}`Z3xp9>n[=dYysxF&hڬ,e_0=6@` +sc mOˆXeq`bj jޱF˵]ˌ8RJy~`qgWփ%?3%olh]?2G.ȕcه>!%zty#g1 @J ,0&Vy:'#\ջ3=z]|'ߠ3_r{9SQC=@7Oks8`Tp-;u7o@.[kv9?w}@Xއ >W{Px}kpG`xJ6#cɯp_OxSOOZ-hzxh<_d}0.+U|=˞Y/4~NNF+VX_PŊ+VXb}ɺx \?tޫ'{o)1!}R|?N_f~8_q?wO759?CI3?p+|Me|lnfVm`eT|[|z2e7!Zpz= \0/WgP}>CdO;Ɍ@ Ĭ<gzIKJ7{Z:SH3px P7b_dgmU69Cr7 OS蟓p!va]qmƟ|wOXx\s.gzv1c9<6d>x/\AIrM9!}Þ(kq9# =1Ir^|Rtҡ\3;-|E0BpZM>~z*'E?2A?>DC2j҅>^q4f 57Knyu\kxuqßYn0f dyM&|e޺8a7簇YŜyx1ZƉ=}k?CowO ОXFpW61@\Y_<(c >dۯ[d}U?x[ Gqf M?xo"kx_#yyy#Ȟ_C߰jf`O)1;xz3se/0@jP\rc&q`9x%XŹy̮e^-9NqO|,n*U0ϡs6E]&> J_`a%zIdox>=sڲK} k(w} v7dopf;̒?D <&G/\ w8v딋~X9ÛǛp޽NwOθ]ygVqs*=u8?;b?uO܏^x!6ɑx;#?)rVY 9TMgM#7_2+'9 K'G7b[.v`y2Ho {72krn[;eL0+.~mmY̏lpAnHX\0x n#BumoeT~h` \ xr``O!y >GϔUWZ?p`%J8X<~WWI/sjmvn= = N悏nslOGx +5od l.?0gL﹦nm\ _Y췥P'a1@#j>@8Kݣ+z9g7Lf]ʟ;Es5s O`ofٿEK5G)/<ވv\W_y̞pw +&~wm| ט{_c|RS/+Vgk7/XbŊ+Vߢ. +sWi+%fPd}\ &D /3Ar)yQ_zs}T= |űum3`p͌1@8!$S_0I$3= }Ѿ7Z lqcb[\Uaש &(z2^}uF2GW<ªC*%SzH7CfL_O_?3_dbŊlobggXbŊ+oQg WϷ\.5E,JHȞS S?s^G` )⏥#gWo.3.Ay(=<܏?\+6 σo9@.z{{ߊq?@o3#CK2Dn_Z/?x}wsY LmFHZK̍ed|Cyr G>Rtr\t'j3 _Ag^.X_o?^K>?c{AOY,ML;=+5# ,_ u[ ^9~7߽=y/q`% +t||,fwl?8P*B߹{7ٿVXb=[_XbŊ+WP]|S>3: {xufBOwI~^ 3 %缿E0@ze~|/0<)8Ȭ$Grb >A>q|836Ks=}H?x^6ƊCb5mMH>pow啟ˣB8 `@?恐֙" NgڝdPyuѺp2x?|>y5 OSZ=O9/<03[zr!nc뀕uj_9@{h< ~a^^=)^nK9W3 %U{[ +:wM}qc' ̯G[Mndu0Ǘ|ϸ{N4a!H/q%c:< ,ϪrG Xo<3AY灜v|JCTׁ2^`Vp2021g9G{j=0V +v }s0?LCvf ^>a?#sT/+mU7WlmlҼx|r>St;E'~}I:+GV/CWq~o/x69\xa==2A2OE9w5 y&-ϗ|/-}@w=|m[$ \`K?x^gh %Zy`ȩ<H9_%+z_+VXS +Ga3XbŊ+V8,+w 6p ̏|+9ns> H/ {oٙb.=|x7%>g)>pn g=?4A>p-i0ljÛW۪Vˮl&dtOׄZN>kRm1Lzd~䇜qؑgzfwn}a"{~ϟS 2@qk~[ |l+cg``~!E>R?4*Ǯ|L|_o ~$i[ߣ1~8s^K'4 _O +O*Uݑġ>UoIz8Y}-bŊ3z;aAr?bŊ+VXz'=y<~wЙ:v=d#pDz{|<sB?r^erLȬ.H<?ߦg+wB Z&Hk;">sSp(:<db~}ȁ;8`{twE~d'UN1+_!O1ȠG3HVudm'nq`28c{x9q~~Bts9\:+yy,& lu ]@yk%WwsKZ8޸l_{6O>ɤ_x=Eg|{Ն?IJ?J61xhs$9)M=_Xb}A>&QV/+VXb?uo{m#x'@@Ao{xcMzͣ7:}1<4 p*1%`357> Y p<HbRF3G@A b%Bڈi~e33+K87v=nZV]\07,.=D8p3 ; W+GR<;qG`]+;\.C쪮R\W+b%cqqUruxAg_#P+dnzkݫUpǸ9ǫ`oQuoKXOq<c\sf=b +>Bz)Gٹ^ϣ +X+q8ٝ wuڮk\W>`cKL&h/;-!mn=n_J H~,00sۼ|<3@e1q8kx1->^5d.PE+VX/ݴw ;J؟ Ŋ+VXb};?uO>vbyKyzC75r9><;:&O!{z'oy =+g_$ 8Yx +|<8mϸ|6A#l9}i ?o5?f`2^gy2CR +\ϲC|:|`av`p /}1D/2Dn"}ƾטp'vv;cGXpK殖a\ G?\jkC#oսgUOgl|  CnXbG!u\ؗt? Ts`zK*z]46F56>*&dzZs$g$amXPr }>c]<3f&A.]$$҂r@虠]*|m!,jĚhO ~F 9^z\Ag}M>M\׎ق y<h:$[9 %W*U]܏/ 9` ZL ͌`fgN=xg5uf0|i6{_H5jp7Ȍ[WS+d~ g£HYJ7b|gQwlѳBΝpgqJn9 x||H*$x(9s/ѷyVCC0-??쿋d/9~%B:bkzf4 ~./1g#p >E0}|~CL$gMukk=kXb}Fo=opW3xPy"+VXb/oݲN|`_P)Lb8{ H)pWU2G|}Vϯ,g}\"r@2Iy8#y]~9 zNfq+X`*h3 =I~H9?̐p sm.`:;$ ?3XL\9`x;4w=@O*R-epp^Qs9cb-$o9œY}<^M152C\;#?5z fXo/{ mim5ՒU +,HVH>`srFX* OX\:9hi"w^u6h._]]\@=~  6/\v&c~JM{3gr|1`3|$$ࣧxL+dd? :7+VXA`~+VXbc/r;o݆ybbtd)&h.l'~!ܗ o#/(f2Hl9)qD.8mgY {\+(;8 +n#>~fʢz;쏛0d @d.-N(r&3M`<\ ?/` bW?shΞ6lndnOB3|9"I_!3vÝ+qV  {q< Ksyi6k>`z9 g}͇`G|//bhO|𸐗Jf0sD`?:-vU#t;'%8 >!=`z\~7VXbŊ+ݻ" 4O=?¾`Z)x=Khs xϲo?c~}b^~2#X9]H@gsXo|օ9"Ѓ%p@j"Ha=dI+;/ ꊟ ~030@k5~-ـzD/a7w\ Яd'J ^@Ӫbh4e+Odv=|x^d}pdv9N<~0crG&Tϋ"Tϖ<>GUp^x 3_,*-p:~ }\z,RıcoN;vZ޿cZUzx𵝟cPoLOϳ@ϼvs`V/v9^՚k3 ߏe?NOywy]\Q9Vy N2Esw.=wCXb}oos?X ( XbŊ+VoE}{ pqHO%G xb(pMAzCp?,[0nc0d?7?؟V@fKdow¼]x*'' *>jJבI z\x[~10ɲ-F}ʛMSYǸ2B[.c9cDZjGߗ̞e |}=A:uNMwae׬mV x6;8 ?8 = Nݐ,Q9G>h/I7yA#|Wq~c?qWG>uD| +h w~ͭbŊsdy=~#w(@?A0şbŊ+VX߂zuߧ&x o {6|ؾ<8gy=+wlGϲ-D}8ၞFl޿)O)3$=XpLp>p ؟:rH2L_``gP9a;ugNLC#?S0{Kܯhf^|U3.9 ~G~c{͉+=ʱygs !QeScbqf ۚw9yhG9a[q\N5ʛvۚi|k`Zt`~dm WX|?㝊^=a~+VXbŊ-){އo&X-oݺnz%7 ~ j۶A^Ȍ4 k*xL}7C_eW@ 䶉,2Ayg fa )_X9(%{\m=!K2D,'2[u`tkˮQ:kLƾ^r!nw]3[5_+j_~醽.X!bx v58`ý6XJmDgJǞb卤 p 0 ,MOM(Kx4;>%z_8T0 xo7 d03!,G-)cܧ % ^dF<aUI'<dL|c/p. yCw>탑+m?p4{=?#g/4>g|M@r4,[\8(ss)򱋸K0KIxBpUodT9P?vg`3m9xZ0v{un9gn"25;G`Vf\;H^؆l!y{F3s>.Gwig_(' FqiXb}Iߡ?}Gރo?/$+VXbK/~@?v~W_6v`@~D 'h,;q?fࠑp 27{&&=Wx47г@ /?9/Yfȳ3dpKOx o+Y2{ >EcwX9B?! }*Zœ5Uy_!^ֺs}-??Pb&HNss/8{`ooж}Op"?C*7qGx_qxQ<3b?[009= +ߋ11Gr[/U5xCIr<5߉_`^0SȐbo\_7|||M?]gi+/``){]ܪ5%lKS#Uڮa~gw|c޿=~Yzcq'8wc6q>}XbŊ9ۇ#^F;o6`ǥr+VXbŊ-_p\dLи #1A_ Hh}  !#8⽁7x>rCfmF'ps07pvVo1G=陁)H<B\Z +-}C<?yGp~ȼ+& vTOq5dn(xmnI`[[[8gmoova_|~@hwo4h 7~@09r|NE(GWEdn[q)N11xXbho;{ X&!:Օ2?bŊ,&?RR6#c?p`Ŏ+VXb /^я /H B?OL 'bWxyl, +}l^@3DxLP!{aN(C4\8 I}^ L!̙ &@=33l.x+(hp"c?"&b3 yMpMfV +mc_8]n{_`(Xp`ʓ{)8q5?9?hޓ`.wHlqʻ8QoqX/fWkl*7՗g|*VXb}Aj.}wr?ygꖫgmXbŊ+ַ~я^0Rbԋ/&\>+HK)`GsA) L = c%<҈MܻݽQ~xvtV078I`1r YǙlAc(g35k3@[/*xɭ~ews6]~0g0}{#Hf39ۇy!Z]z?dǰ}>Gv>`Gp?($Eu"J>(!9 }y䏌CFW4Ve N\j6>˳ ~b#'Ƕ {;lA:u/T_dQu*VXb}̠Ŋѓ{g/rXbŊ+ַ~|{NX E.B 'h^Gh<}/+H&8)b !7m^`}2OL k`wB0%@0 /pa?}cf)``r@Ҿ@}Ygz`+C|_;hAVp|ueId~cn\p@h{+k 0H̑?A﻽ݼdգ 1GGH{Vۨ YV {l@ٷ]ܿ|uf~l|i}/VXb}:Y\?:;8cubsA<<׃B ICΥ= _^3 +-al9)-++؟2S!8J}HKW+ z̯z>܏k T=3 g:vo?Pw/;qŊ뛯@/c/0)+VXbŊ뷮}_M.} +lD&<# y#.~TH&Tn-c\W; X{ ~AAq `hëI&(ؔɬ f2Y[$\,0C x!6/lVhggWc~@1Q|B/:폎YȈ-nom~d'{?b`i/ % y絳.#{9bє䂈V\ⳀkMWWof._ըװr1@8/,Zl9 \v?φU+.g RuX򲙷~.qFA P!i^. XbŊ+VomG~AZp {yor@k xmcoQ?6#vw3?K$~&J>x7 +`fqN/G!Ivp|޿oќ?z83P>g##>p3Bd^[^s*/8j#퀟j>_v{|0wxAyq}t[P`q7:{S\19|~g9pA^BIʁHmfXHC쯤Q2漢x?;bŊ+VX +7p yE~@"-pn4'>,a# .Tr(rAw³IAm Pbz-?8!<6# N 4?yp'(#& טci&,s\7P3/Lp}ӭC݄ϴ6 g#Ffmrv nr`sۛކ/0y~if|Oot{`x/0r6Wוʃ7 l1H3Y|Yxv28P@e+뚙_UUIk} ]@xk^@rbY |`/`ޏ *_X}wboX:{Ǖ}|z\,oowoXbŊ+wލ^a}C8#4)3s|^7us۩! '޿ y_7&HMr83< Լ@q@@-/,p4p兰wX^y`z_Y,{;S3S f?1A7M--dF{!p mֆ p{s]Q9bL{4( =\+mûxELzltve939CݼUW +wrt*c2A )?r<1bܠ?avmy NWŊ.+xF߉י{c .<\@vR*NJ+VXb}G|a+ fF >|V~wI#Iw.''MyD 2c 4q/L.(@b3 L`jv_#G ;$C|vNhM_>anspuu oØY`Xfʙ6bd{&o?c⃖)m31/-1EegvlqΡOy[``|N{ XߞS0mn eEJ +%/䁔W;rb?`5kz&*GuzMΗzZ_03dHvͺv:nke`bŊUeރy_G >`q~U9+VXbŊ?8 PrC~'g%37 6'{ /_[w¾q by! )!'4&81 I  d_gfxyv LSKu;‡(g|`ځ e/%ps.B0,mm&4mmQbmѥ~[ 3n#'.a{٭-+ڶq}AֺݗCq,<C K#›|c~10% u僘bp.ﱊG쏽jzmjM{9@=,|}]j4_>wcŊٟϧ,{z+g@er`~@+VXbŊ <*3|>a 8aK3+̹ ,+d:% xhNs8 gΥn}s}s?Q_{SlEhy$ a愬R ^_@@2A^@ +d>r 9p@n378+_B(Ad.˹[Y]oq"{] ќ´0\/kmǘ{!{4L}\:V2[]'v ̏,|/9ܿMשWk.@:uowGZgbŊ[cVͧQ:^~`@e+VXbŊ |#  8 +2B,+Mp7x]+}nߑnSx`cA7;d>3pb20@愐g@OǒYͱoNlo,_x֘i2@!LQ>s|#o<$ؘ aop 'n5@0#p}390M76sn+އUc}nnp8s?bdn1x޿ \BC@j{e7W! ownfZ .xxr:}L?.?qrUjud]741z2HUokvK__ݿ˿7//{Xbźw^f}nLw2 )Ck_ByglXbŊ+w>}瀞?Lz=z'8T_$_?~FOl<0O@!OwN8!r%3O'<\[ Lc\Q 2l ={ r-c+v&LQK#L<8Xq-fO1Y!G}/kH`>s{ywk?@z-7u}}7ݨLr+VX~J~ӗtv8+?^j}O G+VXbŊ?,'B = ~5Ff{|P"mf p_w?<@9g7>=j&rM7린M~m1?x '?X]7)~hfGxnKlbl>@WV|@??5xL.Y]qˋy+L/`|b|8>CawOl! Z1{1ryYye+<}+}8{Ky+IHp7%rmR/AwYrŕL`5  6L`!Cet͚t}_Xb* ~?ol_~.6{S/wudĊ+VXb/W` }0=550rHYoq^`e'N*CrF~@fp*3D\<= !AMOO(Yc@>r[C(ە`/1C~KV\L-(rk.Mdt|rN ,V՚N _=5jwb7`Ƈ²~:ev8oŊ+V9w.܎ܱߙ+VXbŊm0_4m6U/L2BLߝ d߯޾eO}>+D,p'󿉄X5@0Cp;ӄ}ޱa; BE=d>CJ~Eg$p&"=;L#aG 3-+$-Gp @@@f-/-e ̀ePL0 g s;5sFVx핥%p@ 5wp+u +e8W_Vk/ﺝ}p]s;y,7l0a#}' D69~ޕ +t#5VFͶk{zvW}u!g /`u:[-i';]WSSwŋ >bq*B'y@;=g7Z;U3{fvnrC<>Bз{_$g\HׁUO0BP( +B /!++duU'Psxz>`_ZoH;8@r/:SWwͳr've-B9ߖ14e24L8'&cgtϠyx2B޿J57dXY{'\nk ?0)D~h~mv;y`CN-86m_{Z'k֤]_UNW4}G?BP(^;V#"k1aBP( +Bsh~ȃeY|\&^@倉p  ns7dP!t?سcnٮB>7&N3hϲ +Vvvv~+0u?Dr<\gp%m Fx,)[,gC%hc值\1wWKr_Dw=/19_=w. 1+DzK`GSiO|u?t;uzN; ?*`U~9Xyd$\Njv)r 3.17W2 etjP(rk=?? BP( +}Ϥ^ cWLAl0`0+Y74,?0۩/Osɛy<~ݞ Yǫgڑr:9Q_Du 2'̞: ,f?N({{~ qOGy_ng:-6o!}'q3 H֗2ڞJPRi>غC A!9I@fmpji/pv O`SN} `# 1 } z2|p7 {rm^ íeg Yl6_>BP^K?eBP( +B_y,L{Wrp pUd|Tִ#d=s< 0ypnGs hCViN}y?ϳz8'e~-O^#eǘ; _N;o>s)@xA3z-l=! ,*7pwLNGL^)uo_6)"9_nA8Hy(/]3Ĺ?XCxǩ7h5[^=z<.]xߙ cM?ڌc7>^> xٰ`Og2 j:xT.p g +Bo{o] +BP("GGLr24'2A =s:/$1@7=v彁Kr /f?fY#9y1C0/x3i: q;n#0? tu +\u)F$"ιt95{·ulɊ$-˞,?bxƒEcܹDJ <~sT2Iku*:U@OIiKYyRʵJ**HeETWUKuMIumTUv֭zۼ}y5*wMu5nI]z ֡7Hl|nCCШdžf=n؀jil9\Ss &o3XKuH+C;c(6vJg{57HG{l޲Q!;w];w]ek~ى޽߅>(tɁ.t/]صݹE.]v;=r9zEwڟ~y+W\G;kksgrʕ+W\RA]K{L{qy'''zJziy`Yygg[$-^{bp\$/z /%Kdҥ˖-| +W+eŊUbxl2WUj*굲zMY.Eyjy.E:mJY󵗛k-u/\yks\z[&+ѫV@z*Yf^sQQKqI)`U&(c%ERە`Uյuzy^s +y 4'{H-$Gٮoׁu6AtS2c7ǚZ۵[Z:6+kk):ښi_lٲEvܹ[);vO{>?e]a9uD!ч#_<*Gw{v][dM'Gu9ʕ+W]gg^K\rʕ+WD7@8>`O> HVp,| Y0-"\ ̏mY d<.Hfؙctdy0@ [vY-|׆),9y5*eMWc =ZۼV G2@xmMy & 3,ո`yHQZwt(kij@7&[nmwmvF޹kw_})Ow9v/"_Or}"X,wn7˾۾ ϕ+W\}` vʕ+W\ra8v!4>(\z?yN Ay xsoKL2U`ְ4YGBYjY\nr}c{&^ ==z+l|Wr@_K/.& ,QXZ.)b? &`WT0j0?mq355:O Xg:YdK]usF_ ^xcz.`s g:!m" ,6n[ɦ-;d֝m@?]p{=p3]zQ#(/:^o۵Mrxέrʕ/ڕ+W\rʕ/=Ⱦ99)>f8>řП ~Λ _XLGvf0eL~/: Pgw1gߚ5wn3~o2[bd%K=~n`,sU>{hp")|X!VZPL0p_`eVUJey*\v&H' Y`?rիhZr>xA%8|%8oKs]z٫1,MXrr@m< +1!d)rD `u%^[Z0݁^753F` +oVWZGx Z;MH͍x6o? RA?rCG<"ny{v;aĹwϕ+W\}+W\rʕ/nطnK)ܝOfӻs2ˢIJ_(zLp e>+#d6H@[ 3r' 9 KSqJ+c׃' n1|Ķvt`gg'f4@m;wTz3HP9`Q>'9?#Gr_סr»rnϕ+W\}}rʕ+W\wݯoߙ~+WܽuݑP|X{ >7i9 &p@4s7NVSsLûDw|@;3ݩG|zo>H9&Z dsc>sQ{s X $@92h .@僜'.X^V{ '4y"tO09Zڬua0Kn ٠u>_p.53y܍=84ISzۢ&8Wv=yGP=@XQ?w:WwܿWSg˙o;ʕ+Wsʕ+W\rYGc{]ιsܚ"S7.J4+d}A +L0&@$.f.MȌrx&sr|>1Kcfj^`_>Û?vAy3LQn5y o13u`p)s4qMk2+X030t rnX]RW +w[Qifyh|A3m炕qZj+q?bA`e}3Lа@67Ksk47jGcCmfv-Tpn@ޟ}2@Ç3]9zt߭y]C):?W\r*rʕ+W\z};7 wCnݔY~wNK8H, / / p~t`_$-}_DWzX\~PPwzs֑Xysg<\p?mglճ#k#s9|?YG3 $ c{l6Y9vZyg,o&Y7Y[T@r>?%I){JX"VTr Y/\G :L/ dnp?f [h^n@-:ڮɸM 萎NprM=`}V恨x8|ߋpk.ٳc۷WvyQο[9G_ʕ+W߻rʕ+W\֧svu#oMi~Qfo^7? wWN8&'&8 Dώ/^p~r?JG%MWs¹`?sais&p $']z -C?༝>?˳=.\q-,0C~\vrߐsEZ9`pŸM߯,3lJHXʕ^`U5火@4_5_5?$5\^f57z:LPA k@ozfctnڪ ,܏ = H{ ==]GˑC w .]үsʕ+W_rϕ+W\rgu3W}sXwkD&eu!ti7^9~u9r}'GzOJo ~LpO/hjHr\x<y-. +ݝec ˗-ed/Y{ڋbHi.xk??˞]G+w+؟~<ȼ<dsʕ+Wߌrϕ+W\rʕ3[goM}&sS5#WevܹuSId1ח䈄8ˌ_79Ǵ{{  .xBO >)} +$ȪMKZIpBpP$&:q8/ y{>cNQ@ϝ;tߟelvdڜ_oSg8 ?,` ܰ?W/p^~p0e0y5ߍy=k?73l8Z,kkr-cnx澙#.++¥pXEXͽ`5R][]WϝRSWs`&]n g5KSC476Hzoos^6Y`2@o,4r2s,sʕot9ʕ+W\r-ys8;93/ݖI~Efn\p^=qLbni 9^qn6o" <\0;ƹIu 9q #Œc)D=p2/&7Ll} ds~|=w0?;tv9Q2==?޿Hh,$sʕ+W߬r\rʕ+Wt޽峼ϛ띱۷8-Se{n^ˢ3ltQ>ǝ~>H/*e8WG^&ۃ{F/3#p 1 uKGBߣ;'c=)Z2A'N^ +c82qy3 ?'##[~`wSO߷?,g'd,+.gt帽|oЛ!^cf͜uͷ*lkl7XbF"t++@ ~aFu\pg|!u#yϕ+W\}a]oW\rʕ+Wt3ݜLBqQ_@n\(~r28߯/>8{`|'JNKMzOPK *ۋ_A?gQ7980~T#8//$ײoy _07sk2 ?Ot=S>skl| ,H>D[b2Q=LTgm~ 7Y*t)nDI 8:+\-lx`<N&Wn]A js X A4VWR?3p7nXW' ?!۬ P p+`~{|>`.OxlpNٹ}+en?w۾# 䃚ټ[[NNN2W\r+W\ryݗݝ}$GorL~.w 2sw2uC0 ӣA/i 0Bs=: 's=_>0P_DpN^^4~hf&h,O=8^v=G$@|N$"D0+9 3A]y3~uw~gzܯ?0 xIyyM6hSO!@3pq| /F2A﷝^c5c* ݁7"u;,b6_EJ0JU/P9`eX!5UR[[-׀UKfبslbz<:dw2 >>m;~_Klg_r +zPg~+ +}@>EHs*Վ r׻sʕ+W\#s/oj\sr֔ܚ*?۷&5wxNpIٌ32xy`~}P^4)tn@]87f3>Lp;5 9~0XZwj>B͜yz <MwOwZэCo7{?̞ܿ {=?|?O@c ! ~f<׭Ng\޷թ4ڜWi|y ۸6KDw"p, "" ffpa}&HX*5UR_[iJփ6_'M r`{{n رp@$<@HA&0 ]@nsٷg7?Crȋy;w.Zy`Xϛh<ɢ")?oW *mJez]rUA}ޞWW\rʕ+WߠzT(} ojp[wL߸$W+37/mf{끻7 Wo@br3آGhB!x'G@Hw ؜2Cy`<7~^x9h +{e` !䄜NG%Nf'tk370qb1  r@i[df +tr{q kxc]y8689\92@GmWYޯpv-~ wJV2ͼpX`23o^FeW-55zẚ +.c}MVga= I{[r@6m*[n&[w Gvi*ow!0v嚁>ŚeB)_^S_=0cU5UGrշsʕ+W\#Guv䵋rk߭y\ܼ~QfcHK޾I^WpbQ@68]W`XFٲyl޶ c޹ SoM#K׮m,.2,9ɚl2?|`~D3mfy5[Fv+<'\Ұ?rB?mׯOW\\rʕ+Wԍ7g#3W}7g;>}g|//SW?ep&3Cbǹ\.mw(>.c:*5J"d*{ms :xI z?cnGF׃=i>"q8JeFHR`dczY咱X +pәawNxL]=rH_p$ L%uFgo|.] C}珳2>?;!Iec49hӹepUKOk avv7u}<3vq2$=|^Cg%1x=tZYe27 +~-J,ԍxl5@ pޞ=k4.{l/]uB3_@ |a, d<@fX/0-Z}W]zudݺYQ6lhFill4M[`~7tsx x!y`{kM;e XeͲ eS': k^ٽgoB~OȟIy <0A@E fvy27x63f|z+}g3\3_]Q^t+ua^&X .gĕ+W+W\r7>yܞI0|o;r&eMŁ)cކu.=0s? 8_tK?[ v>~<06G/̍m@,}Fcf &Zr&xG@Y 2&!\2ݿ#3sd١3>#ip4nq0@i1#wGg~;6mkߩxh|n + 058CCMq W~%J&;lX ]@9ޛRM2^[7Ppa+G-,֝uy" 8, x2@0/*(Jp*RX^ YZEZZZZ[ۥ̯EZ67l# li$;ZdsgUmj$۷m;v?o?Cy3ۿ7_W* ,V7]ĶLe6/߫ +_> U=WOwY|=G7|8꿗]rVsʕ+W\~c,w?=)z`3dp>A_<=bp9ut.M9a0P8$}} ٽ'DƘ _/ i'W/=O{q-~^VI/L v!{B"%wc~ _< ~9asL$# SOkogVFisvF("8#X S,03z^cLvn,dsCΤJ^0P: t&I L% |rHfd 9" ;yӘ 6m/hs"=h@oV扠dވ2?G+y ZETUWUںuRSNj-\I{CC4 U[ڴ[Zۥn׬t'wr +ۘ#L'I6u4fvglC~ț/zOνnw/$Gʏ /{Z~6?_«tΗ<-[+͞@/#k3 zWQɹbGp2 p/3UU]혠+W\}voʕ+W\rM=%7.}#3ijenܽ5)sr%3G/7.)ayrсEi`v 9瞼$=<5/FG1ǜD0z!½dUIsOy~:Kc2㹜1d +!Wx_0xYp$9U >KsWp 4 iiZ'-pZ5J[K30amm?^g"k% x`fdFeFNKfh {1et|gur܈)f~^=7d?iX9x>"{\+ƕ_(r){sߍu4}z=KDU u8wʽtzm3{wޞ?h ON\(spRߧf8L)Nro^\Bfǣ``iD 6%:Ѓs$Žz`Ԡ2G8{CIg^k65yP>|64׻\Hafz3-3`~`Y^I<T,2 @)o1?sp{MN9^?Ndxyy6G{M炟:6ǻ`nI)4{ +)~>;9a)Ly`cW}uʗkւms# qO_R3?`vf +áT \-M&7h9#=Dp8]^?Gfwu?>#äkH'c$)04]&u6xq{#|N_p:;|:ˀ)Ćc{DGE^?8}}!f΃)C~<87|-:o9=-c[$|ll># Q9%^{L>m%?%)@fp>{tV̲-8: ̮_gvmfi)-|o3[ #nP[d۶m6k_ݯ.?'?^0BrA3?\LpA6;dWd9uN8z!,=9!`%V9y9n>F/+W\}5+W\rWPNʧ3 +a,Iu?;;%7^n\X$~-3>l }y6Cs?|".Ŝn| ܵ@>sų70>0:z{sg?fq_I5~>DV?Ss;4C a, +70V?Pg}ޑ(_"{3Ya;' af|^S +Nd|6I*ҫ+H&̀hv|"[ŃCvn"K~pN?i\DYܰFNn10ߠqo₲18` _#ힶ?^^{3Lmy {>:kl[}B@ + <\%k֒鞺Z2@z`lGLvim.@ß=gᅶl0@MemeH具ߗ_^~;╟P~/ $dHZWIzhAuJ 2Cay| q+y_` h9"_E9[fM V׀ϭ2:wʕOgϕ+W\r>sܼ֬޸nN;;oLO^'ׯ|L]0:h8 +n8["9hvqޞ2IKЛ sc/gu+Pa}!Ax]>™[_ +ys=|=t旙&al6y"&;iIes&C|,co~a:;:ݰ~+Wa.35,>lHI uyL>̏5GƳ6ocGF wO$\FD+#LL:-x#`z 4 /Pg!rpBC#fIMrf{X,?`ip7;>M~5 \}?`VhxB#BdW\̾\32#h /?l`PY =@/̇7W*nުUGNƗS^yY^ɏ +~O~~ ,)Y.3SzSS~#W?yG}W;ԁ Dž"`QfvmݸgC8W+~9޴.Q5jwue~~='a:)8; r}ZN~iyt7G6{ۅH703]Yiu8Kk?|ϱyc󉣜?f.kov#d΃mǦ!MȜ%L9x ٠9ϜksG \8<~ ztrA2S&sD8;#>~qJ/ۄ @zt;WX!VTXR;t\~U8RX8\/ .`Fikk6&F0vl";eo?W^.7`{p1kxf rVÜY*tOsl?/9|n1*rͬ]0Ύ<mY^dkGLqLڜl/' 0A|Q|wt*)LZt:h9c] c{1jۏ}]rXCWrʕ+W\}Iug|zg~c s2{#wwu3}%8cl1vI3c,8wNjt&!r7yץ+!wcF%<p'&9:s`t#8rg_gSr o8k{a6Cl2Hio| ׏N^N\'f̼ &p-5dI*A?plnl~ps)S'n^?k?f}x{XX2YN Ifcd0?u|#~˙b;Gb/wg93|F/YHo4?r??u^˙arAY [ sxb0% pfm& TH@2&lf 6j_fI[PKa?K?{y `f 0o{|^J2 |4;K7&sNUEyPnl_1o|<|_p7!stG8h >݁1|+LK%#xLo@G "p/?::8 7UgtqSg1 6rxF{SߍÍcٍy/ϗv3#nlBi <w:_Hoܰz=r=8ɶy##E2_f0} ˒%/XgeyHh T^Icx-ށ{ /8+Tϕ+XRd)p4zʥNx`MGgZ|j?/rRO4G+W\rʕ+Sq7^׮0Gunû237%n]yeڇ2yCD06iyrw\_~5/F/\(f1 pg xd!r p2~&<ܔ8lr*Ief^5|>g1g7 /Kdn$(d3ɄuG#dFH +=lr=#J-%_J x3 g. 61 ;4lt+}2?#3M=97g"1N^")ijǭ]~`k A(c9p&iG ^"åH1+CC222*cϢ߰\ aѱ2::!Cf&{-bA37<†?o>N< &$9ͦZV˲edՖ ~>s9LXr@M`h d}}4x!6|!^+3O=>#e*3,7,n@ 2E`W;rWT_Frʕ+W\}\LO,/ڳ.U83#3SW_sޗ `߉פk`xK8X\$/egys<@HٳPAco,&x`r1uBXU)xx9Oh_rE@z~i8ysfwlNg{ObE$Cxcy<8fDi/^=ꬰG㾼osעM=E#3tf|Y&ߡMs~sx; x䭜72|OH{qs2 <=TȽ.H>Ȭc C8N7̐A\ =@~Ln Aehh4XXa 1gm@=zY?i^q{ʥe0c=yF)\ p^VK|49]@r@/#x}CCH߆&fߢy7~w'S<Jq}>NHnX ׯ + s|`|l;#l@j:ʕo}}9ʕ+W\rUò3rG2{kl/ܕٛWew4$ܻX4)ၰݯW= 67pt﮿G3}pq.p灙סtkr=vhrD 8ף~\\. +W.+ 7|F8ZR#wcvf*39ږyrrY'poDpˁnp^0^>2#ry4ygbmA?x6xFS$L3Ȭ]#k~28\Rwߩv幟; ?8<T.Cr9Cn {$>;µ 2,Ss|t򷷕ōܯ Ǒz`T2?dq3ߏ\mp2v|+ο/g./Ͽ#ξ!cۤ=8)S5< yf!`/(\r2bg+VиfA6_'j|{<17?a}*ܰi)R"X(3֘Y`W\r,p\rʕ+Wgqx|*S372G pnZ\|W}n\keOׁѱf_,1, bYp1{>K;ѝ|y̬>evo /26sG|P?+~1ȝvtRp2fy EՑ1kޛi@r7G#Kjg\Sd]a ^Cn8\,95>:t&N&s7QY1ngzy>ha޼0]B@˥?ȕ+W\rʕ+W\}tܾm|/a{j\ۂuȧ`s>}$g)w3di㸏)l0\?-c"ў_{]vO>,S繀2qN;{H423'%n =r9ei8}8d:̩ Ogn̓^^ja`Yp?6ϣۖ_%go̤s_Lv6_`V0?Í&f# ֛T6 ex<ٷ{O&i?/|ár=76''k&h_G7:&cQG<7e}y9u( O[^b^FJ ]`8նkTUHmMx`=@7 qߚ2="+h8e $'ߪɕ+W\rʕ+W\ݿ<7sdy%,MpOdodʇ2{my p7:y܏> xi8G57#|u1L[½~ ßcn|, i3dtc{ dw:˙DRy\<7oL<ZZ +h^8/_9%r8?fkqސqq.3h3Ydq̥L 1m^ +l// &+L%3FWf^̷սv̘Afm()k.m_~n=5σ!xSqUhx㠝B3lf? >^ + Y:L'ZweiV:~cԁ3x}pq.wq8s;%Oo 823tN3pA/G 7^x؅ 9?uYs2:2$cC8dx(dkF?h駟Rh^Y|ZJ֮-ҝ 쉚:Fs6ܯ\jdFgL*f0 Gp ,d(W\rʕ+W\fyo^H]_w={<|]wKfɵO>+˿KSM.y^rH304:x18xIϑJEsk~=[S.ΏE$c~F k9?K/%Wg}<xr8tEq$!sB8}uxṂk=>ҵ?Lh r@p_>!%w~d~pG΁7џd}0=p Y$br$^x'ob}yz>mrQ1Qu)[><4I{7mM:Up`j\+Y)r@sL9ieǣɌdv:7:qdއlg9s`xf +'5s7 ygu? \TV\F PV|`PG>EW'"yy suG\B[~,W\rʕ+W\U(Vo3@o\} 5 >ƝteoYŒ"ׯk2}M%x~қ6_c3}9C9ǢuJ=ѸKὸK/O>9yfp +מKYNJuItR=2p zAgsYZ;Gr(zlePf| 3=h?~~$@Sѣ6љ#Y:U[J'| [MF#<8;f8c`xtFcHFHG\ +ݱ 8f}9CG:u*L0{a22L? a戤9G FHr\1S|?9;l{̻ڣwqyR&7<1M38.dǘ:{?sAN`so7NGgy#mπ9>O݄~C<3T9qs8M9{]9<c)\`!s`k&ҥ\\֮Y-EE1`Uf/ E*Xn2B \rʕ+W\rʕ+WPJ~ ʭkϛ1^srrkzVs+>g|S-1ZZV4N\^~]\. +HO\N_06pWI\*Hw08x:@Α2gY /`DOBB9LnYyǙ,{9Clcei<3GwLH>`:bIgK\qXߏGOYy| s4 lA 7PX9S\D10S31y `i4J{[ 8`\@4`{Gttv(Gk w)Nu=輜o${г3H7qSGhzyl1F]sZsL2C96?܏#4>"83}Ϙ,~3}]})zz7uNÜGfv2;ފ<{D +ܻGnpt89ݩ ~^99J/%}=|yf,QSF|t&|>sٜ'N;k Iu9p, wQIFp-9a 3G@m2pn`K9ef7ǹbI\[tp~ + 0Lu4,&cF sCX%78zf_pvf 47H+#{C1Bvy"p0H.-9@p sw&wn͡Fe7$;0~$N^7AFqu>;y{gs}<9 s;3Yf ]ǑQ<ƟFesG鍏ȩq9Vw +7^{fNg]uzo✷ MRypLp|bl|Jj. +YjYRVZ +)ZJJׂ ,c>iYg2>pϕ+W\rʕ+W\z +S™_{]~nՋ_h;r5pwGeɍK`7~'{]z8˼]<92@_%KI tUE'9#y>5\>xް$ŝ}t 9#L,$XY8h<3kYQV6YY|g3uҞ񵼝? _23qNxߋnb^!<>:ixsYA\Y! ={NS奈OK"KeLpPQ8ow]x `:so1x]<{3##˦=CtÃ9 &7~ .u N@{c.o0s(~Ndsͤrnx0Xx>f27 $#/HFǍ{L; @PoHe e07"aϗd"ɻmghxgs8=)8l!e{4DFҚ}'to zSKyYڂ+sΖt=`x̠ΰ8C63pôqw;p^5ƿRhTp/ɟ2pSL'$D m.\ r_aEoyOթ(GҼBb= + ]7r1YA{uS,fҕ׀ǜ_#͝}`sZs8`ߜK;݈19$32rD|۱|r19$3~qyKd(#LqLrC278\Ëq̯C.  ptuc8qt g`0Hַj_u m'nL/3}MiٿZxRMO ܫ^ӚUe^?W\rʕ+W\rn7{JO[gv=;6ǻ{y >{H&[[ޭ+rw(G#g;p7r8?sǟ~C-5owWRXz~x7֘[±2˃,^,1muܯ@ݿTMG?ᶘ19^Wϣx2`!U {kGȨx&\69}9{k2{>L~ʾz|/8?} OYa֧ζv hX)f6\'1xηj;ds3I\Nȝ n;y|[N{'b;Ե3,{yʁ2>ìǎ;.YͣȽtSqΑ>14%u1)M v伲NeuN?L:ˬ5y?x{~7{/GNUu8#gcv +U<ﶳ<k$݅G},G2_=f`o[81)Sde= x,p^`pZk"d㳿} ʕ+W\rʕ+W\ 8$g7o\n7у~?#!oʝYKeP߾,ŹӺ7d%먃3p5O~_ /7{>^:߫,GW7n^n^AY\e ]8ӮXδc &WҢ4*u<VY(ӽSc:zpt̮?l;(,J0/^~Vi.||fq u7KL6.5Y'?13&8+dyp"yp"pȰUYQ73]Dkyl72成r~w]]){}I|?B戀GFgwd}|y=7@`0Y!aJoHNy9zLt򾇿0 x>}Tn^sGrkC6 ׆rs-vEV*efr7x%pgt57sVu\+k96=&xq<߫M_V{ gl? t1\7M_iGWg'<AazwDV. /H0G'اO*O|.`Z^2I) n@iwʕ+W\rʕ+WƜ\]_>_ u?9O /'܏R] ٸ!Oܕ{[r5~/rZOn\ P`p>grmx7`L 0@o/X4ed{tG+f;ʕ+W\rʕ+W\W/~kx5ϞmVeә=<׽o凍ro߸$[W-ˣ:|YuVR/$?7ɝ ֪-愹/\|Y[5|=8e\2sn| ܿ+%[s%09<רz]TVg2K|qp5"sH-zSe/Fl `S` <@0 ffoNّߨJףY`%]1|vVCn,?.pb, j`m0EƵ <_Q0ۮܮ(c7a~MޯfodY`y#[;Ů_١'u_CܯX;Q8SM|S9O}?3_>cOȡ>z#/dM>򿃲Aa?{\rʕ+W\r +ˁo_jO;_5ߓ{y}Y66}~#wn^}[ o맬W3df:N8 >y^͹^5mlMVd}`c8K5yc DKgo-SG/Ǽ.FGf>87(+pN,c+4drduEDGߕәb.guS7H :f6Y x:#Kq>g䁗s 9#\r_اw7r.`#1;<5n &nwc xr8_^XCYf_YǺ"]2S %IP;n3?N/~?ʡf 坽{Go߾}f&#deǯs?~7k  +Ymu'[&{ فqΓ~y /`ɖ{ ђv(=ީ3>XTսn=ͩ) ):DZ,Sg{S"o_ bŊ+VXbŊjW-Z&[Lh{O~>O~cھwؾws??{{׾Λط_9_=7^ZۯZzWRcrۊtҵzoZj՞U+ xabGٽf+VZTfiJjU +y+gtgmxMlzwMw쎬Zɹ5=k6FjrcR:ĺC[Qz;VxcZ˯Q*Z#Z7[;no$~iJњ#-7\Zuu,/7Xz~7iiu]ZZV)c⡵,Z5o +֮L,M陿?qi>>,}ڜr +#k߶*y~7kVKn6qݜ?s 6lH+t?&3nuUYRw[+ut^u[U4\W[ m0Zpzf9\ܴܟrLW6.m2ڨ߳j蛼g7mc:\xgy:h[jZҨY$c=:1g٠Mm?3܂9i 6]`3z69E?/o؈hkLlw4v鶍ݭYS]:nfޣΎg?Ջ6K8}Ŗ/BO^%8>|i.=xǞG>qg>=v}cgs}W~2>aOǸ_e_eKK__XbŊ+VXbŊQ9X_6Wܦ%;ݳݫ>y<|=8ۯڛoh~+VJ5^nܶR)RY W>Ve,/x+˺[ꍄuy8,w &kU7u_ڱjfܱyxYm\W Ln[+Sk¾*3z~xۃڽۜҁ͜%tO&p?FLPqxXYsԲV9jV=nMxh +kÜ`q]_+;v&g)kyBs.aG۬լYRx쩟t:5YX^.'ɍmH`Ow6.ߧ?. s)a[+VXbŊ+VXb"*wm[lӶlIg~r'm;ߴ~?>^ ݷeZ/ڻb~o^=xd%Yn2xۥCwpԽj0!mgԫ8ޚNtۉ,P J\R?wyI6X|nG ^KFSzFw܊XC8zg]>gG\ +/42\/N?/ vޗ8sN# &6}m8܆ }'Y1U_엜E/VXbŊ+VXbü,.9۹^j۶dhԿ?gyվ o;~|emv w7_q CS70Z:[.U^+ +Vp[wޅo ~U|Uaxj~u?Z;!X1%֌0<-V.Y5she^"_.q0# LoMt8``x5w'GN{50۱F fٵ_ PRjkYPFp=z1lyz8듇W'j=7; iowg^nuy.Zd8dn=8p9ǛFT5[}䟧Հ}ErMá؟f/PQcx˷ <ky7K0AeM|fv`W#p'Ya=; .즳1{iNe: c+u6~}{jyx 8,S7 FafY8 x|δLp9\-\Kɉf +ԾC-;mbycJpMy`{O•.s\/Fh}@yم|Zh8[pjy%k ~vgxdM|R]k6Y`ɹ LMhei޶W=Ʊ25_p#ֺ0?]vNZͦBvq9>9vkk ۼ^Y#@q;0SF/.Ё9`NM<;wӪ0J:Ղuup}3\^g-+k cv<'Lgay\5gp0} ڸʡ%UMq&CLΜɳpx㾟o"-XÖsL&q&NLm4+܂}fxGgS^؍t1+k2 >ې7݃>c'~Cߩ7Sq͵f7oR~ާ<8B%0R5}M¾~mm jpbs6^@炚ۭ+Weqv5+X6WػjŝV>+^m__=?q: NHkmvZqY՚gn褻ζj\rudR.;aƸQ,&aaMK?n=_ 'Ԭoy(t_/QNp[N"tk98}ڍg q4;m~u N9Ζz==.apN!cj?!7okҺ3Ɍmߙ%,3eN>H뗸p^C2o\IHlr>}<.=`_=ܓ'_TW߭ݹ7VXbŊ+VXb wSmyfʖWPhXwoh4˿;0N7{þ37Wwωy իs++G՜+gW^3B ⁘5+]up8x !֕0< +spG!,f- ǺnBγA+.$[k jNRZE<"\k=sfy;p^W]^sV֚)T˱q ,(K;Gk68qxyE[ap:.^ځ2uǓ'Oyۏ2>}s/p^ lbXcs']X8 ^"?P^g[א_8:ox=ޟr7#xG'>b`>^WˇI-!?DY|oΧvא#y8q:^_s\34aS 4?fS{wL_܇ .m9nt|Ng֦;8#t鸞ف8[4+|>CT/x|JXٍWm7L;{Q 1{vbkw;g>_9KޟXdbŊ+VXbŊEgէmggx~%_J?ߧܣf6ʿɿw7^W_8Nͱ}[gw_& s +w&wwV}Rٚ5xϷ4f~+V,XR+xmaA{8_i0`%u]{ gu_6e`taFCXjUabܾe~ţ]g qI=~a^?oݔ{8[| <w`GZ%{󿾯o8q|y; s;~=3D7|^[ة8n6:twp;SdNs:tOsK̹\6|SLߨرF޿\E[yawpyyb}gy|vι%[~^tp [؂M=R ~*}Sܿ +OOŊ+VXbŊ+V?u`{..oo:39dgW(R}?;on/\ gc{ mѕnv荔雴>+<}٣W\|W3u.9|XY `|C ?'?}}<bŊ+VXbŊ#]~W|#_&lllr9:_w/Rw{~;7^o-gk'[ +E+ +xo``׬l}NYUJ^r\O~37X_`Co2;&/?~8f Ã5ʸzX@b.A 7x_[sʼ<Kޝgt>;su gw6gnY,|n,6/0sہ54+:}K3r7 ~y_LSXzAZ8x>Ǥ(fh* )j{/ϊi^maiƧarcO9&!̔mw|p+@-^؆]K9g*D6\ZL=vocLL5Krߋoυ%u\mɛM:@O Ԭm,n幛'w*v'k8.™s%ӇIxĖ;kz%gf^s^rטNP;`v/Z,u +pjsͼ0TVɿL]5lN9ڕ'>|fsӺehXk58*/䏭vBfsu|:k-y>=䶞z!{R)Y7,,81fz=J}D TvHfaN}ϟ\@j0s7X̦YŰz:p`/ءy"^;V;Iq3KXݿK{8%,gQ\58|D]:}? y#xeWs\[wN +L ;xSFAy|6s|2,_O+׳u~̀)crfgp: 4Y'x Nx2Gr`[Rq7C8ɇGnϷƏ[);d}=ǟLs[ o`qѝOqNoj'ڷ'(. г:4+ S6M|?s {>~osw!)50QE1Z$UQN8{;;_}IۻەfĽbŊ+VXbŊ#Z[Ovg +}s)Y۽voΞe,{T7of^:ubo~M{7b?ޛ>|k@s*9x[0_Vk5Q6󌐎m+VklL!7Obsry^s(s#2Rpm+pm%C;x5zk +ssb>̵4b]x뾟>s3aNسӽU)7Pnb% 5K,ޥHg{}Yv*|s>.]grCp.?q7gn992q}#xNTsI +~`f-uN;g0}1ߍ皉Vnk\urZk Ե{2q^߶Gq:{;  w ?p}Kk>'_tbŊ+VXbŊ+Gva|ןkgGpl'm?']g=#>7w}mַn[O޲r5dw(W]˕;lZ7djzK~(]uX=b׮R^sWn1gnrZմ<ޖU +8v^M"-sjOYf/ﭦ}k:9W[n@兔0˾[@ߣ}:;h: lv֫U (qzdٶ䶃W9vv>Unj@zWjfy^p[s[v*YwcbI78)'Wr4uiyF.jV\|Vr | p*Y|=X|Ӈ列5Jj:=|-gWɣڹls4\5 כp};_Ӝ[N?0q퐭{i#wLl)jف yY:sz?\ߐq 9io^[/Ooy\S}7׊˜+Msxa}kkn{J#_<@x=ݫ^> zxfv|3A4skF' G3>Xi>'w 6pS!^A9]kiX{}Ww͝mM0-5sn>QL5p-F!3kAewcN#E~|cąvi?^Yxl.~F+wHb5:*b[W%ynWqoׁM>[ON^ i <0qg (&9hlPذvd#eq%Q +"PY,wUխ34GXOr8r_ NR +=q8G~_/϶O};2ݿ4w6EM+VXbŊ+VX~^ux-jVv|޶L"Tnv=x0'}_;_#SO_p\W_4x[vpE |u\<| 6 uM Qx+;^`w><|9]v|.~-Sw1:޸| 3ɖU+ ˹a"/g,p*u?< Nwr;\r|WI~,Ϯw㐝4^vlr|G{gq;:gRoُ~?ޛV,enO{; m+ 9q[j}h` ++skh/f xfG{b ?*?Wo +$&yex1޽b-Knۛ!No=SthWWN_VȊ[_7Spن1r.=?ep(WT&cqt-/5Ě^!e8xpSL㰖Ƀ-{~/|+jO;9@9-78V&Nc vZ052!5r_O{p7ܽ;`~ՋցuuY * ++\p +RoVYQ\a>_ϨL)߅?1@凌.AC y݆jfsk/*eK8dwܤIYשf6o}=oZY˧[s39{+\yF楻]>IS}}kf7J`Éwǰa&x}SvpΓ䖭K[ &wg?ggbq'_lG+VXbŊ+VX>lJ:>뫙'^ë_#cWRruƱy۽'++ Pbj )Wt<x :[/QX ׯzxͪ{O[i)߶5\ݖؙ s6{=M澸:ѭvh3 ,fفw%)>; BO,g5g+7QY5\j|;V/j +9kUXvov3\ްog|G׬SU~oMx u 9r>,mzzSS š!2א5yIxfUކ%.'6ϥsEr q#,pTsX#w>9MN9n<7 7@zkɋ01&?-Mpz6}F1>^UǃysOxkvwr5mh1A!-m6y&Mߕ8OQ)$Po<_gLV>c[ys|9g=}NOoz%9G~=c_K#Gٿwg?s?`zooD+VXbŊ+VX>2Շ~?y||b{`m^W{f9+xC~fk +6txvj:d j憹 q0Ic֭bp3q>`ؖӡ-{ >`9wӟww5?/VXbŊ+VXb}dJJ3@?s[1Ah\|QaaFW< +̮g}^k٭gm?:}=jy!v#'zDžۍ (Ͻ?w,?)>X-ȹłghGιϗu¿`sȕ=}sa`#ĵk0빜UwzSnqK)eao1 Vj&'pjEȹڼ]{-`;5,Y^18 'ۭ!C3<.ïk"5GܪdᕰN(ֈ3Ws&ڰd͜`x}ŇECP<=o9X6*kD;C)'S:KZ|`}<.P?1lm͔1:&=<vo81oY\9n 1Hysxs|7O^GW&XNzS\Qpu?pAviv-/FdsA㹋K[V[ vxayr Wީvm2 luH1\Oyr{5[.׽5C|9No^9VpJzu5Mλ7r)/^`\T㰿};&'؟fg O?_=Ɗ+VXbŊ+VDifJyz⩕+5+em;O6o<ſ*CV.;ܹj[هZne^*79vց{{vaמX|}_OLP Pl?̱/}X^S y(_=׬^8[Kpz>:ùܫviW؟#b3 =gy?n{7zbŊ+VXbŊ+Gz%>,(/0-?7`vE+gq~| p/n_pj;UU+vwcl֪0M\הf笸U_YS34v0>v[/őzu~9+˙+vROyivҮ>FCٽ`yp̎g[- k sr [! 6/^lyr}Swukq:}x1.u",]>a}?iίCoV|=*Oc:='jEK8w*XYsv86 CC@`=7ejrR%}1,ri0eVqp±ngO`|^ONbA#3qx^n-󆌏yzk -W5 ׂ8km|_\~jvxb7ob7o}LYp|2|9)T=׃yMzμ_gq+;m_|i\Q{+{5 W7,ys@lksgNg9cp[n)e8|jW8 ?|m+8|"; #jWybw՞1^a|wgr kPܯa[Zlm?XY `p<{mׁ{=e'tt& Gl# Ga~m&ap:\yr6p.)w\﷚ ʻ6ndmo7olx79-Χ aݞD.}ء)9;{N9jŨ{ٟ2/^_˞a':{?/VXbŊ+VXb}$tp \evG8%-VmaFWsG~źgkN~~>|ɪ ] 0́9~9sKW `vIׯߥ}q밼kWvxk~ƊGa8LYf9GpDzן@fr؉y~wxx4 .k6QzyĹ#waNe s-B3L(jWyo,c~vXH{ Nӌ:!%7qnĻB-mnx}sy^T&OX?\~ځsa=?|8@ Ss``V>};۰\)}~O{l)C;Ls羷O1,phnˆq&5ϜT\Wop#w峽kS:-NP#@瀛֞A)LrG8#ôׁ&v<< Oq9z7_ {X5V 9p?e;y\* +9e7,Yn;-uyJ-#x)#B|gۚӵ^zk^G#y=0rbOZqIinX0y[tV!{7F',47+'7n4Z8k쭎{X>܂SK!#ϲ]98;p^|aqFC]qKS*WB)VK3pǷ=6 S5 *C0)'pسqܐZ֯g +i`<5_@8ސ'<[ܲf s v ރ\m>,i&{9!zs8YmN.ᅫr7o|v7򾩾&,6k>V6\w.< a`ڗύ}.eܿ{ >/[ooG+VXbŊ+VX>Ճ$p +n^}ʶexr/hY+=EfCbck?/~)9,_H{=wz;gmu;163k5tp޲N<ܵn^6 +ޛxj7ҜY1glxup=%ɔQ[8:e*znG~ii?;ܫS.|@MU940ՁK|7վ>yx]<;sS nY拉`睉{O3̹SyمY߼?SN_| 9X|q3;>UfpPgj/xǧ);yepk\l׆5l90t OWkeȋ\7M6jl*+xxc޺Ou`hS?e:8߅ =Ka~`yo S&o;_?UdbŊ+VXbŊ#]7UFkXR9Gmv{pvն>k/?i[Wa;y~Z=; ^:e!|g<:ӻ͍H%| +mgyQ+巭n-<ί|ק݄3 v~_;ZpVYƅ= ma8\:#q'1Us{lUL/ Of=@9| 'UX KxL9q5,^L]vȻӾ@5s=_hp+'.og 58&8|ϟr}0S;5+7Ps8^|] 0^9#^aN9*[Y 9gx f:q{4cg*C u=n8W6-q86}0N^ +lp㩭`xwy_Ǖxs3,Q\Ro S\~n$Erclr:Qf#k'7)dG ^ފi[ivy?p'v"^kw!gP7޲c;llTdܷ PFy3>-Q+??i>/>ks9S{gfO/忌/VXbŊ+VXb}+! +em+t=r>;kv~ͮ<}nxӖٹ{0ak8Wxt﯅Nc|Nx5I 67haVB~ww3?Y_~^w/__ǿwf_җ"+VXbŊ+VXgVcsOs+jVԞ=baE`Z T\*Zh~מ]UjmVV6y Na&cWmhGڋׄ Tޯ+gqә`9zpZVx}+;p;VQ,'5ֺU[|mY UM,RP}j ]Աs{[V׬oj35>+<7HR8uaLRG{W?n#zrʼ*gܯYǿk}g[ݵS>EpOߤYb=;KwKs0?){my|w#_~ ]to?5K|;Qfxg\>13rƭ G>f~K;b?Ϲ ?_'ٯտf~bŊ+VXbŊ+GUd=u-y}:}y]vtV-\X3|%5YW&h_) +)W\Qo`xft5\ ++_V;pp kkVNiI +8!εqMaS@ ⒣)2kWƗޙ,Ir]gZO!B(6zZw\Iڤ  Q!jx޴8a1+3#c="c#*HY??# +T=f:᨝q|քC(6-˕!ƷFķr(gOb M,-ۈ9s;8i~8*a~ osOo냵:clXϬ9BO<[ce9SS"o>V9y[&7ü@8jp

    m','P^yO#k18qNҚ݅ݾ }&sߕ}ܿ/`SW5oŊ+VXbŊ+VWb^ X~Ix^_9}>{c LpCev5_t 5߯:6'9I&02B~X0\Wsn_B~oUH~]8zu%6_r߳ K^Z{bio{M!c\@9k_x[чrY XM3έx|3e#[w+ T* ošzoйr>纃^!Z9ߛ9wp)C8_b_p: ,:g6ﺜE+O$kfCև /p6<9q27wz_xGXn +Š1afSfqC'/~|S毼@e.=#a>}Y{/&*z{[wn;>':O P=]ɟ__̏XbŊ+VXbŊJUlemz_cSf] w[,ǥ1ʜ]-ef܏>[j֬}`wK;=Y&'>Vq>x-ee<kbq!.`! ֘TZÐrrOaG jǵO_`y)䓳07O +_ΗtkϬQ;9N7o|4un.XӴ"|7l=ܶ כM2Cz~ Za;fsoSx0;{`'i| .2yN T/ܯ[So. 3Ywӓ{ d;Kg[Q&I9}u5\s%R-Gck,Iԇ\yf'vl+Rfp|9G1^`yᱳA^=/nr'םrK]8 ֥9}Aۦif8vd:WT.0<^ ^_wS[2|n߽󹰿?‹ <~?ww+VXbŊ+VXTpdxp̯&[8~O5++Z|j(&>' TƯ~a a̭~Țg'כzOõ3k<VX > /O nk28\'WONnľ0ñgݶ p*G GmO~bݴȹFs\wp3kw&09In 8Kw(#2W쯫=Q<&e/knN"ku5spmnr\{Տ;3 6< \ 69q89U2ajoxOقǜo +{.E*waE.yzg{Y>Ï=ZxYV]m'޻+_Q1N^?CX׫6;e/p>E`q3-<\(&N3 |0o8Vmg-x` +11ٔ +3p[:Mu叕qޣzWr 0L2ߦӱ!w,Ftlt[ynE+\<173pP^f)?HEq⶛\r}N/`c"?qB~tj;x8^pMw%=#s8+8NxC[Wiϻ_؎.~U_59√CM{O[Ol9hyw!ֹ;;ؠ2C*Nwg:i[9{':_җ/VXbŊ+VXbR5ʇ΢ + KRzOs`Zp5e~3WgO[y˞ޱsDs2;vmm|}>s+|>}7-zY `Vg+O>zn[6| Cor9CV1ʸ^մ|?q,Ecы>/#c-l5k>ڣZ}r ƔL@ 3'O0¾] , ۈ)YobXT9=W<ӄ&SZO}0>a!?qǼ/7r2YV!"Ƨy}t=xhv\Bpۭ6rϋus1 5l[zYbSXd3tG\wQ,<9oafߵe P909zs09nָTfG[oTM YK;߉'l^(sA^z-N+ܾrw8ʔkM 77^,NY蹿ZLp77݊N[ ]h \!fS8lzق[P}jO~bǮ #,>YpS&UvmɽmCmj"U ܯ_ozχO4?7bŊ+VXbŊ++W}K:Zypp@}fc WւaxVZ.yxGjF'Kݛ*s:ߩի5k48yVrB>q>yIq;Oq|9 x?SƯzjCYug=yd^Æ3b{0r^Ӝ>e~?>{ȹZM`o)xdLLRr6Bfc1Ǻ{Iu _x4˂iM78/8Dyb`r&Ћ{S69a<߽00䁥,R?=Ksιm<4cpuqR~eO#u+,7r!Cع_%oyLyZڃr/Wa7#"8j6]\/K/p,yr:۞s<_6Zv9l/%lM _p(ݿiށfo?bŊ+VXbŊ++YUܻ? +M?$}`iV tWV;~VqfuW=~g`le=VYyКxfZ7 N [W= +'Wف9~KXb|Vv<kT+vrN>-V\DkZG3m`N؇!O +ٽւ: 7Os׬)\i F>cbL ίasw`0Z}xl{[et̍2?ts[y{L'V04\9!^x{[rء^ӆ w p*{;K?~5P3_"K~ p@&S5+蹾c9 gWϛ M{ iO7- г{û'np +:60py_]n ȭ ^aY۔ W3܋w*?d9 r _jIֲ9~/.ΫMnp[?z\.a~W?՞8 g0+s/'m?{0?;ٽކ}n*=׾f=XbŊ+VXbŊJV?:8;Gέ~WVM;άV)lO5xr*Ǐ'V}#?Վ>Zݱ|9B9]jNf9?=ºĻ>pfiQ9SXC= <LzY`v| +y xO]^ͺ [2;j&v:yq҄{lN޷O'guӹ43Y9Og~`_3xMyS1) +l6lU{Pfv0>l5N 榣͒>47P nz_o[[~[esS6 +xsz}i&v,p-oϙDso_30gE,*D=~m^޷ ;KF̼]詝6Z67*n)7cx:e蝅[oVx\|/p:ۨ/<;vB8F~!p.a0!{Ij6kXɩq4sOl{^?(/Pp^j&SydWy>[Fw;u2Co(gɷ "ysb5l )sx7ϓUq} ͕sY{₞ =w'G&CCӶT}Ǹ}Ok^ezSU^G#|aΡ^Xi/0CNuy10zIO=݃Xe*vRn<}@q~\?1@^.aM+[wܷdhs~q^k,rL?}_ך55Vbrܴi9,E^ʬ^fbr9,xž]}bϜҞ=7=uϟ.WW]W'aUܿƊ+VXbŊ+VXwEx[#F%5{t#{އWy/ŪtMܪ^ +˽OX{yӱZ){uSm'O{EMeY`}<-= +U0K^gGf>/ſ16?|lFZFM=1xo(Wv.B=yw>g>̯'X/.tR uSOh}o=x`R}\!R#p =rKxo䬜7 םqr4c0U.q{7V8tZ uq4}(7N,ќ@6-auwKM1SN}S@W)cw%}:W0%,p4Hm:<Nدx@y9<|o󻈥ڇ!~Lf>^43o׵<< s_z_ 3o273>51BY¸]>YRy놿KOoەʳU/.Νs+Yx\#>Ro3KTF9‡2|Oq 1u\d9wlSsN~,`8T}bp8q78L ʞM$׏-9{X7q uc?:c.Tӱif؟s/|5< ~竍p<'893\pk g >_U/o؟f9=k1YKE4l6lQs~A}\#yka~6#e.:abV3"Th`Q߽con\ߛ_}{3?|b~S/bŊ+VXbŊ++_4phs,4?'p3N'b?;C;V~hՓG8hԜso/UrӑV׃U'Z:ͦu{YL_nf}e 5oq7\L1i:~ŵ˸{Y|s?l>Z8Icl}Ś8t(7,=ao߅~]]} +;%kc9l彷q٭]89p +ܹ9,qKOו$LْkVt{->Ľ¦35ޡr= ۓ{x`r[70A-gZ%'<oqjB7--3'i˦t\r篯]\AcyZzT/Vib`狥]/?vOW]>nK߾un};w˹߷>g/{]E\??/VXbŊ+VXb2eL 2ܱvXO9;9ZcVfM|Vwy fj3;9zlG>X@~pi|/-ŁKa_mc>z[3 2+<י2>G,Z w wl9yal@9r az{K'.3ml{xz~5#~_O3`o9 +~3aƟ;x\?dLuL~jaȯM<>Sׇ48cgu޳O5ԟ;򝰔+,]9xWfޠ}{wSpz 꽸#,Ol::Ka'6%.ܨ5r|˗f榥^ܹf*oX9:+ sKgoy[l1vY=GV}5*˗s>|<}? i5;I(OP;-o؟%%"'&evVB/lp e΀8~}q)LqM˧a<,r {fp8jk}ޞgw'ď +3TN Qֆؗpݱ+itk*7enrrF#Z|͛x~wS+VXbŊ+VXb $`dC9~VVe^9 ~Ò%#ktV[՛=5v +Yt.``==C+'0ICo0n_BN[jӏkpAX!}V;d {qX qƅ;}!]_֏q ?ܼ2h v~1:A|LFp Mu'>"p@-<%M)K7٠ +Xm὾gօA~7mmM:I2NLaf[n<^@>r9xLe27ϫ_epFnklX}%Ne6>LW5=dޝsXVlN/ ka>[>mpp>0M``xup;hTfMoTi_)ڊ`q5Ϸ^%cyzs)|ZfCڰ>,WsշMnn: t 'j_f@ ݭ"8ֻ)z00157p@y7Sw觜u]v{[wr;w qg˳;JS֯xyᄇׁ _ s5V\"y:ݽ󿷾 +{S2?|_yU|C+VXbŊ+VX^,Se!J(N c]K0=`sOO/}::/i'#\ugۨXNuz{轿-[ N{s\=ݖ0S35X|Ypٟ>VOͲ6MO-x9S:wϹV1 6k|şy~9 _U*dk狑=Os޽ {o[o[bo:17oXbŊ+VXbŊR3*`O`?cJKNא:p tG9wzQ_ =ŭnߚk͓V?5~𷤰I_k naὲy|Tj柲7ԋ/5+x\ +s7lpuN`i%! ,^wvbک 8]Wz}/}6,&7 t4.i5#+lr8-Z>e~4yC wlU/0EyG7]8h#P+?dk[k _/Ka5Nl5n>qlM]9xWW08ws7Vlݍ{yMFPy3cX^eiv[*xy|]w)aK89lz+z=8\ٰo3gM=X}P=exr}YUzwЬK ywzc.oyMq7.L@'<:x}/ + +o:x|Z,=w?ssqa[m]ݽ{nܽko{}=/?w/ܿfŊ+VX?۱Ѭ + +wmgyàj'{6 +P O5€@P:G:;W;7x>B}{~i繫yg o/_|o&sXrcIlzb֙u:[ 0.=6qVSFAX(l>Fk{xZ7߶ƭn4?hY܊ +s̢؂nǷ{pz7lk[Ɩť?dlaq0_X螯,)זVk&yY-M9O0AiÛ6hw,Fr xX8ذ~{6wx̱*+%^ZʭsVM{Ś7>[[Qw3q#ws G9WSˊ 56n:4Z^*P+[\ruV뻶޼kmɆ}[jm /umZ=ɰeqײQ}t9O8ޅMW6aU 8]߱ZpwmO? l}=:=NxܰfyfU82Z V -$ٲLmXN8;ȶ'rڞNef.C`]}m/dO]=oٳ~Ǟ7ɧ_?u}~|A>9-}7˗/_|˗/_Yb_'$<01ރp qi-yQaAf~jVw:76 +2 `pzb~cmYg]kߺf[p}oXg;)&:dž A9u]A>r S ׋ +_V3)l[u:=ڳѾK L15G%op,qױQV׃$sQ 1- ++8g ,M`n}k~= 'M7o[2R8܌o{OZ%<`9QzVm>)m>ٸ,&bCt0m:߽gmQZ 9VHcV6&Se-l5fgk&=C]Vv9 ܾo+x +N0fS#.ݛv޵gE^mǙqlkcZ\,P\o9F|Җ'wk6f-l}H`6g׻ՌG u}ؔ-6m0Żpc~#!N9 Vx^/{;_CWZ=m{gɧ'| +g}3cwc?oo{˗/_|˗/_|w*O*5̭SNQpRN:,=paaKbNF! 0*j/7޵֭ciC~sw1 N 0ELl ,Ԣ !]cwF0[\;cm>[s9p? ?KJLfrazg~UVS紉 [ <qXVw^@C. +~ ;7wrSr)m[f<{r >{> =,Zڳ{6yuYvJΗ܆m*Ncߘg0!ĉXW\F9W;1MogsK`w}_39"Ƶ77p9.lp>8x3 P^ܰ\t q9Z 64U/_|˗/_|OU¸gMůYX`mz}VV?٥%gGꈿ`! {0Z_h ?å9wѭ=s}o,+zÁ:oV6GEAcO{m6l4-OpHlMøҽ!9b~N7YآS ;_n8;=c3<[Tt?e|lN7v.w>ˏarc\]ri"e _|ӞWg~k's˗/_|˗/_jB|8=G~[[ l kSd8;#yynoƁcG{׭u[7{|pc_ղVwK 8؟Xf +\ JX^0l99oY&8 {{,ta&K b]/{Xͽja 1A ׇ!^\Z69šlR;rv M3x8QK܏K8WsRncwz>q#8m=XWˊ~6\0S0Ējx1@oR;=k9w˭z`Y[ʻۺ~iE}{²ԓ[9fxyUj9{{{L}j 涄'nqֳU?8m_Kbk ,0 ,eoq6cKyj!nF}7t= L`%wǶ͊f}z)<;OK+[iSk??~s~/_|˗/_|m;{zL!\6`f,Tf.~״;ָ+vof+Ձ^]kz^?oae+sn^qW?%9ɹXsMrփEv߲7\o8[e68~ W}9,MTk򘪯SZrRaz)>8PN 6nڨuh1-KRO!ޞ@ۻƼ8887ogoXw5[t|͢Ma`b_ rk+Wì1e~rvybl7gOK\N u8sޖ8whe:3sϦ0n ZV6xq9׳p=b@V jގSf-Շ<%hw?_|˗/_|럨(س6*R+ eU kް߶ްn.uz8w곍BB|szo߹~zsoS|ĂA*`8dscCFsM9z޽X'^T|aÓ[ +z[pj柜2/|Sս`|.W!m,kŹ<]ϫ8=:1p4;O=U4u`I۷WD#[6y1,7:fVޮ5u3jO`}3y𬹖oLqMV8++ow {^(.osӼu0Ǻ'XO纀jN2akS +p_5_re,݌z)cŽ8&f&8֞0tض +Yzmdw_=lnVdnUާ>Su\qLSɇvyq.>;;sS1\2jԳDZdh`Km3Cy||gsӿ=?y_|˗/_|i^f ~ww˂~eq\؀5p͆5n_^8tz .9k֎lUovnzێqᅣy=i .߈}㙍z_@4O8q{`˛C؂q9_qx4rzbx BXP[]\G9篪`"q?ݻq,<6ol^8a){;@=<0+Xܬ@gTo27un'sv=°%x\?ey@1|m'y5ǥ;,pa'opy#qs zߝ{Ob<@ؙ@ͽ[+C7KMk]Eeϩ?1Kuqܲ*K\I0=fc#ps +Qcŷk(M2Y2 IaKzwwsS^ժzE-|~nj;vYK8-aP-ܴ+˄OfZGwgkG'x|G5~Cj_P8>$tz<>w5L.[.CG0E5 "^k9S4$wlMZ{}]0Wf"xnؚ0[81,3ث<9Mй{g9Nq7,d԰pЀf .͂}_y Q2;؋$'|<}^K]H6=N`,/g"n৛z~`w9GO'U/~3A|(М@9+ll& {Z?'|w|ȭO˗/_|˗/_+cgiN뷅Y Km߱8`زw;y ky}x^ۯ)CcRgztzMq. xy.N`N7C%5+a}8S8.2=:{}Grqz>Kϵyas~?z7=Pے0t.ypR>DN Gc1 4Q}Ǯ94jӓW)7}lasߢ.NfWGj (7T?0+a󪂹a^mw]Y܉Nߔs+Ε- 7ܭiWQT6F?h9m¬NuWpQej8 KuߟU +)5'87Z9V{p+˫ cײ +s\V[p/&1;xxr"O[)wޢOv?S6zu]j)kx#N}┊yf-.3wYn]WX#o%x^|q_AE儜(c^"4q~}o}9z`Z>=w|˗/_|˗q\:;"sJԱ`س~?w߲ѾuZvSVolxz`XȱzX'8qkqそ kPQb ƨ 1{c{__? qnxuvT!l9C1]7OY4vsʩ>Exsa&GǸq1neﲢv scv> +DVڸӲ`Ab)'D j`\q;ޤۤ,]ˬ5KC+]~b~]8Zc?sX\fpƮOyr徹)x7믜#ʅk{ ށS +w!]ci7V=pk!/=W q6W_L9O +2[̜{=۞ NLQ΢f?Ws,)-x NLww~kQ<' [iawS.o&#˹I=77+>Y ps ;9 +θg,{,_a}'wv oSo5M3{⩧o=].߃}?}˗/_|˗/_%Vʸ83.)]/;>ݴ! {ٵ7_7ްw]G_O OWr+bnvu^W P.7:x?l~ 7eEq*,vܯkֱu\o nbi&n8[ـϹ$ f/%\Gxp _wN`4sKpxdu$W*NdyVfnnkwȦ) Sfp^\@.?*+NmZ0r/rsR}q?wv&Z&cY*ù)e(3M{SB wrfrlՔg \vņQdrs\`y&E1߸L` s's}s18 qea]zp] ܉%r??uU̐#+cXm\g oq3;UO}^3K؋>cry;&GXzOᅧ'9`38:+*D)s-x r g>ݜszǛwO:C8a&hgr6n;& 3ߪߓOS~_szw˗/_|˗/_|3qWǁs/Z8p[׮[om׮e\î_K<nn8%7D3uX|\@}NJ ݺsp uF7­G6‰SuhѠo)|0DZ")lXXXFxWR{@>{%uo(Ã8AW9ac\A8_3\ K |Lz]H%U"~@`8}b] +q"[+?KΟz^>#VN.C-kzs峭i)K#ӒAǬ<NLGmp9a[xVdž߉s;롅iݩ]yT:p8 +^aYYXV,n۲f1`d;1> Rq>mz_OQpsù^]7NcZr܇ +/y~rFΜ&6 {m^[7k,渻^-9W}‧h~bc+T9+\a߆}Ӟ$;'}{˗/_|˗/_|s*``)x`aF[6< jZqBW +~Acǭ5}q {W_ol=惿bdaø-^6?s]qB1X>]d#ޠw|ۚZ'olhV`/Npbx]"yS%^&\BC@ tIɱrǷ߱A /F7 O7q)K]2D + P\~}/Sd5"_P"pvVJ?`oa<<\˴U6F}DL8fʑӣ{.V] aq .Cq37OswbU:lYP& 9Kr4N$%^hݒVto-}=LL9 2OqQ_}a|,0P9('游7P^>Vn[>qV7Ρ7,hIlZ -OK؜fU սly5}LyhExqYЦYĽ/pg[i?xv9 jw Z{,aenrԅ93׼c.Q3tǜa),V +~gzdW,M\P~zaqKޓo(orn$soYcVUi>BL_9WLx޿r]nZ[~ᤔ[(:NGqmq-u <5+s[½r/bUXWvLPT0b;߰J;Ұ8l2_\Y^ &Jp 1@w^n6u}b sYdr^ -KŰ6M`xqw>>>չZKXrA﫹Sd \nk+S+#xl0 +n ;;.װy{78fkUpMK_;+؞+CԙsܩZ&%ǹ[75[\.7y⌚gMvS~;We &K輿y;#(so?fu&Nj`Z8qǸ~Qi 25/=uoWouF5xK]1,P=Zbzr~}(/ON*ᵰxжr#s\p&:X?f#+0KT_0 SޔC"PR<<<_b?o.ly(IQ.w*Ws?WMY,ÉSqr6Sn#Prz"kWPѿO nM +~7 ?tvg'|dms5l|Ilܶ86l _x^׏g7p/6kXy;Wg[sV79u>|lpe8j4hڨuMճ, s5٢H{ϖY.GvzǟǾo^|M{w=\CW/Խ__|˗/_|[i: Ee+ӫW_ :Cp{ y *VWw] `gqh=\qp]#w8+pF\P枵߶ۮ781!\Q,Po^[}5ӣ`L WT0R#ܵaس0Y8^#U>1{VMN \s9qB>28X۹~f ('oT*券ٍ}: Z{3eL,qšslxjwPrLi'Tս.疥:P +V8ܫibܔyaV+\-K8l^,G6)^8Yθ7p3\=n'.|-`maVj Å W}ݿ+o gs`9PfY,a3\Ul ~wC'bfijt Qqw~en7S7 ]\..^ϣx NrW+(qKq>×/_|˗/_|*7?+3KutW=[_Y{>_{s<x[,uE. x lvR;{ QA٪v w[<=(v̋¹kˤk8|qޟ|=eWA-pxԆcF6Cι,|ʱS~U~(]2'(Oਲ਼oɰ"knk^·?5-ŭK3;؇SgWOﹽJYx}n./?{n1K1aNJUÛbcT??l@1=;vS狈mS/ݺ] +/x;xvK}SM>yf{ͣPO&/OzV_pECoޚ{[|_[Fx0D0ޖPu q0TT +{%M|j2pFiw'|Y8jYA_LQ>8`#!1W.Mͥ{r;[N4[PnMnX&A`"a_Σ OS`Ÿj\)xqbʶpoش䮹4OY.COn6.l=CKpMәm˲UY0j>Aop6[s\G1H>:%9ǎǭY+e/XpN/鶰Vyȹa۪޾-ʌ\S^]f0Oٻ5. +n+;ỷ.G <xy+\{n?1߯:;g>=ߣ;kO~ӽ{o~`w\ iq_CWw|˗/_|˗Qcܷb +ais~ӝWۥ+fuaAR?0*lw7q>| uYcMkpA8\8Yc{8|./X_|<(km!_\8@=Ǝvќ#ܷܯ8 +"C&'x2S'|;\8yqy}s GqCV59Qjw+svmظm!,+ 0-hGGpf?ȱ ?hn]IٽZzǭsrWNtwܺ5q08t$ֱl l*83+w}S?wu| Q/ڮ7?vzO`Y'KNZa4*=$@hC9[wu= A[TKM^k>;p9.ղIsK^/̪oqaVֳ ׳u.Xg9ƧNj;;-= |3_O@=Yg'':'ߣpGwϵO+W^o^|~򛟍poO/_|˗/_|/!<) CebH XUjn\~5aaAN0q OOMsk7ữ~a{]ևrBqn/cqwΟEx#"N86H껫3 bq@n_dnZ[bun$w NЌ:6XCjO.w &ETƽL1TSZ@~'9f5Bֽrvs16;Ƿ1.g;8&9IƮ쮝_o^Ed ?pm]vZ[g^1@s=]T,q{='j}1;'_\W^_٫/f?z/L{]C˗/_|˗/_5oY{%ĵď`A9,Hb|:9jǶߊ:O9uF(loVÎo.Gcm۰ܷýwǵ/KXټ]xGtN?ڊ)8ijo5K p0M3^bcyFuy=4-nVa L`^-񽘽& T\^`=֟&3+_)\n>bᰇ Ν7W'Z(gc\cuŅi{,[ףK;=(7w]7{w-ˆx ܅*s\9Tssje󿿛Wګ.ĵlIalË'!jΟ{?~`z:'qZ(mm S^x!{:SCw ޥߎ^^ ^k;+ď~_{dX?yko+_ۋ?^:/?p4˗/_|˗/_u[Gvt q ,-Hɬp%K3X~3F;vtZ.hu;- 68cilS +p(#az),/j:vĸ4O}šׇ2@x&Q6zփ_ FpAۀ} `%[q[pI`n|92wZs.7`G6kbʄzw,v>ܷqzr/Ks9&[[مZ:wvz ;Ù[زL\O2TC2`q\leȿQA&V}l[oy~ +T锱_jV=Wrad|a(eA ?XN\/}59{'k=nmZۂ})x .fEl>;8p˗/_|˗/_|Js[6VUswͣn^oYgLimڭlvj;ֻuS XxwOBX([u kc _bf)#+Oڭ8]Վ3w^qnq}80Q̵'+(fr,B87 9zɻ;qY!꫖ .oc[m70r8b NZU[V +'S Rp- 6,;x5p4]H4:T׭ 8,`x^goxyp ;ܻZeg܅[Icީ5J_}~S< }br3`n{oGQSolݴ~ KgT6H4crB~;rMut1Rk]o)ãvbs}:c0=IrQ~ +ϭ)t;XJV+c-XbkpFy3{Mճçi͖xzz9mԁM8l <- >]Zb ŢIZ;y4rp߅imvKY.Cy05᫳uϫ|8(% 4C}۶(Sncxj bԱϽ[qx]ΰO;wq' LcŁx%\vokVO1 s.{綜v,{ G_q#䵜Ǿ~n?yq?x^[y~K/^kߗjϹE77ޗ/_|˗/_|U4=sy[7Ur&s㡛-d}geT?4aVGO}e*#~y~L,G0ƕ 퐧cNͫ׎^8paeI +_^9=+'pR*7dZkdU/<vRp;+`i_:jK37p6?qy:U<;V.pːk+\;6wf"`+vu|/s.}???{*1@qǾ~~s _~%k:_g|˗/_|o2XR>,.8[8sOX,V6Tx[.XovMM`lb.cVq"e cq7Kjj\Pnv B89ww:ph؅Sv[vx5r} N|m^g^O^^_+OՊ]FP=h+9ÓcEp M q|6G»RqEcV8ܿ\ceԳw?ȿqkNcXqX#7/h_`U2;ݶ7tzSSi˗/_|˗/_|+f+#r*,x {hjfNN N8 Cˣ2q7«#;dO}Acφ-`YL,XK[TKחفW=?ep7k3oX%ǫp'Jqj]2 l[={ٰpľ*'WSClIΏb[,oʚԷv8vt$H ԀB͕syy EJ^k"ۗI;⏓y2ϐ \|Zlf0C{س3?z=Z `~ rz rg.g1, z IEimp8e ga)s&OS\;s:{!jG/u7ч%AqEcj¿Y(p y\ 9 d8q%sKI1xg|82L %cOrxs:}UOW˵p\\ oܬ\|soZO[}uwᆲ{(?o-r[y-3?SL2eʔ)SL2e۩=1c>(o]ߗSGDsoFʖl VnԕM|?maٺdoc<}չ{`xV]*=ِ=0}\๹l/R`Kb1|g7>hٟ2R+z xtǁ~[f-vdzY&g FZ"{`ĩUtK17__G-fp9;ϕe|?G3j-S{xMK[]<?γwcz1|\ /_u6 ΫL?~iG6?8n_zY*Б' ~[FI)P=WȚ y{v9vƬd$Arg sN q9 ϒ#q鱄g.\0Ti3eUE 6){QKOyd y{%;+#}{Eܽo}ykOޔ)SL2eʔ)SL< zjSUc6Cۃ8&u] ȗmRÿۑ5YG:g/Hڑ:a\>^SN 0:7|@p^,{~U0swwMpd~'u7ـpGydeze`=p>8~-ьb,o\LBp887̰O +0N%"pjYgøcD-qH\0SY!O%Q>^!v߯38^>Gn85 &CdH x$.g'8f7:{47ÜZpΣ休l}uρq d`o8PX{w%mZvhJle0զr7~M{p ؏̥~W 0ԅr )q 1^;¿V[Z>%*K'emIݑW:wڎ { b0OĽF_G7pYf/)7^q8X?͜9+4X셷Y\׼{KwG| 5W\O?y_]'+pW?Wpd~2eʔ)SL2eʔ)SVMܽZyOJOĮ ep5:"^R*Cذp@Kj5 C H)=7 =m7< Wuc?ނjؙZe񉔊;RPgdko]9kveop`[ YUF_<O܋`Ԣ7-v8-s4skc?gYmʛr| 9 8AΧX.!xܳ?,_џ~iz^yK^IN$uŮ;z!O^;>LO\(,JaNߥ|{z/c/xx<_L%3~t :{ҽ'ncZ/n-ˍߗ^ +~ݿW*?aʔ)SL2eʔ)SL}Y+g"'bm蒑#干^Rp Lxw1V(5˓ndۮAӍ1c;RqS);Լ ϷOl/g4mK[qRx^ +KkK 5Y!/U`n3$R^mus~' |/KpFMsk2rNsz#86K(Xڒ:FI2>`ۑ1;C?0&8w]X)K:!ة9_ϙiv08ވp +qz|?!(Cwf`=yP{w;ɳ`p d_^^ g=+kunJI*"{d[k欳guV`>ߏ9k=~]ϗ̪~Y;M|:E|܏ qv3s_l3R<1ۄ+dx#`-aOHIŃ ?EW9yoru_˅d0/`s h_/i/}x}׷p2;ŋw+V)#[o \Y >O?P277zxL2eʔ)SL2eԷ]v +^H_jSeu +Y`!-20,)JSoK_GƾOo5H<}$ +xՀgy\~^n`y@ +>+UlHR٥!<y  T CP'o/ 6RN{iǽ 3o6[82FO|}@?\w7gP-N3}Ŝ9ǖFq[;ūkyLSe6Y'X?W:`}gW[['#|NX"w2SƼ NUx)ܻAߡ/29qC9'n2#%$`8e<>>)QO{<^^I`AnrMc>|7j +O7Iu~wsv Lp q#x~-Dޞ$AU\i+teJdpxfޒ8|5OsZdE0y|΅ed/u+row 6|?2Oeeo#Sz[pf~3ϔ)SL2eʔ)SL+;i":'ak S1NY/g`Yا B,w zM>W T>\ݍGR]jy_, ΝGg/ꨣgXjv 5pjsw7}%]/2ǃ! {زnss323|m.Ӯf`EvMŭX[]?oy9!x(cp,LxOs#O%;eF1̹&:]曔(_%LҎ”,N'}=>8Vg|^A66SFFL.=+ ':>\;2bC.<V\pt҇kz8.j3 k;ڿu<65<}pfyy(6`_w-me),כ-ȅso7-.Wt81w3Qp Nނ.) `ח2mv֊\CMe~}?L)SL2eʔ)SL2]}dGtL2$nSsM |Nlϥb5bgRWuB;]*|R/oerg{X߲=qѬZEpےFܚipWO39Cp .>]N?=>قr>?sXxmef?tJgÇl%MH|'|ymL<>rPݾx/S#[2Cs~q[?q.\ZMR//ULɺr0cf77%1Ϋcr_e|&\,A0qH][=|Mov[/cwɣ>_l[q|}:Kww{O}Ik/>yMӏ GX/ry>($Sen978>1:MX[Z8Gx]pK^ue*,\Y)SSS2;o >xO; e0-Sg)3Soj.f}@?.M8 3> ޺*>rk7&߫ϔ)SL2eʔ)SL!3.vb)2re,2,>Cցצ!|!U-UM8}l)[kx*Vck(+mop6Z ⸹ Y@l,7~}P~蜽qsE`C:`}hH'pۚ8qM8up+궄N] 4#w= lM$|.vqKJIf)؜4㼛{ < <`lم9L}8~^ gpxl; w$<5xuc3q9KOYKez#{x/5'PL쫹|pZ3­ 5v!<ãrxRp;23wY.%#:+g90.F +p.{WfoܯPl¹~" ;4qWhMx̞`z}odg1'oʔ)SL2eʔ)SL}7%c)Zb> {a=6Ωb?2@ OnJnIvs# 6l)CJUٔ#\jn {`E>\2Cpc|qB03AC4/!7j䊚L`8fUg{*%ms-g3 +.j0߄}BW]S|"N &s|}6be]tkē-[ߖثJՔwe$%&Nۑz;3dz8No|*>{SH^x wr"æ D6ZIR0K9¿Kr~fQ㛟sr2533~̞_dSs,^ և/)ΜP9ߏ}0?_tnf핻 WsL2eʔ)SL2ewW>ք sۜU;`9]><ϕgpՂ։L68_Ӿ\Os7:SxT56Y>clܺ><׷)sy9EZy#x͏ſc\d_s<Ǘ};T[[]q"#58d2\ +6`ѩ^%`ʾW.ܫ n +{'P{La$3Կq5=q5V|"b7$u2[r cgxtCpD(FXԭnC7%qyΚ8`I/l'3"ǽdsp##kdd5Iǎ8\"df]kp/&v-M]$r5pLGV&[mxCqﱿt5rI4s8" (.\E^(,j"¼ν)3XO{}{}g rۿo\a +/j|K2=.2[f2c<a7>@?<\2?Μ9g~?SL2eʔ)SL2e;s:̈́肍 <N"=0ߤEkA+qYQLN"V\0 .Y{,kT`>Sy~r@ly`|!?zuJVk⬿.> 7~>/{1|8mKXu)-5WvwGfq$8Z6P-xƱVB8tpZ?Hn σ_sy9f>KLbI?c7GNG/oD\! t=*)\vhƉmr6Bg-z?61285G Kە3Cܚ4jE X[]Gpt>`;kJ3 +$kK `ӂ7=$Tc}Cf@fh/1_K~߶8?|�eb/,_[:r="}-g-p-l,? a0ܴ>&_u>`r?2Uަ8#\8f/_pB0< ,[=Ibb\ ΟmW^j7:pOc^cΆK-<=-a2B>~blptN!>}rY~ NҨIqoݖӛRۺ#amOڡ=Qρ+:ǐxa>Kw&1y`4Hn$-c?f4Mɂf*{ᷔw} vY|ւٌ V$6{2Af~>`dn q-t}0jp$ٟ:~w~t xLox⻲o0SߖBaN 2?'S3[/ 3d\oEw-2x.^m &>ޜ=ٟ>D}`Xw;kW5[+xμ +k8c?SL2eʔ)SL2e;./ݻ_ +| \-ՑHࣁ"9{` h>s[>ߺ ΞR\RݐtMK`L<@>`攁y_p, ւaǬ8z`dwQy]<7͙߄A\ؖzI ^bnH ̱-##XR l|s8#^ّnW,_X_ݝ`X`Qp|p6sޚBw56oSf Fq\M##S`q g! L_cOgp7e ƹ![5p/|".&{dֆ'{os`92?;/ :…7~op<lrx92.87;-ݸ(+dy8𤋮>|*wmȽX*p?Sg?SL2eʔ)SL2eRI L ǫoSanBYډ'NKpxWIDYjV0\gjcyoa^E,خy5t%|R, +WiX!᜵57Be z.<kU#$#0?,21NfMz|-3B@3IQ /l[$u{3y|8,nz~zpI{5vtq 3E|lyp =Yֶl=y_gvpm[w}fjx0=`ɹbg˕{2a<=_&|K[j{~|!ZTΞ:-m|N ^7x=0X[C'd΃?'xg 3eʔ)SL2eʔ)S I!?^eWJ[kX pݦG8%;})xKtzʅ$ݝ ݐOͼ_S/ 5yFp> ΝS(rܗf&&;p'}܌=Z`\73y.<ɘ "6X8Fj`b:aWM:>p?[d‼~*6HӵWLiB&Ee_9GCN{0Xexva ez`}^b-m#J?gq]{jl)&>ιHQܔ_?gӟBG?cfs&3NEǙp@9{2dn,"9#_Y?0y>3CDpOoQHZKrϏ/g~ \ >{ek]UuYzޟ7+_SL2eʔ)SL2eU:%tv5;:󮺽!viG&v&a6K)q.\* +5Ƞ*\ko - 902c~_-əC_`L̺Pu$1}u)nI-UV@J;OsٓGK/p88dks9M]uzt#COa}{se'.DzKu }ss';k:Qޔ`[=<e6Gsn}@.fga/{b59]:Og}pZ|gR06𴷶2\#]Fxl6zMuƣ~-O>Ҷuxv #=+L8M{G>lեΜ5|[Ҩý=yXp^ڇن+3AXwZ\=޵[+{. ^y(Kd.-~V;rGrʪ.p^1}L2eʔ)SLQ Z +s]ٕI2Ix)U0]%L:xLWQSj${A AޥQt|>Yks=zԭWĎ{i0Ly=o"?{l>~!?6;#s7/ y>'OޓϿ/G\=y\>/O,M]%Y\X Y^'Odm㉬},kSY\y,e} {K|~dۍ_/d Y}eXHk6YYZ[WʥQ~uB_e5Y[ZwڔՍDzL7'jan7y޳tl<BV7_߃뫸v}{sV78'3܌lߓMX%S7.Ե27uM,.c]bX/X{\6@EsWdeXYYLݺ& /G=Ě>xnZ[7ⷮo{{븎\_`e_;_rUؐ{}x3ٸ{O!sos7Zw i4vWv=_knT״[wICSޱ vɮ]M( uRWW-eRUU)55R[_jFkl[KZK{;q]i8"moKKϣpqX;MZ[>Ə}CZA)uz}i?y_>?tr.9\CHnpLzry9uC>{Bz%7|^F/Ia +dp O\a9;8q³='I햳?Ƶ\vɎ;l2L&d2L&W~-,r'_,_ʽ'_ʃG}0ۗdExG[ʋ'ɣ.IY!wl#=/ +={K0pKel,eU2 | |kea εzsp8j3<鮬.[\9?(*~*۔UY+3K2|Ofg"ja.tg{2V➇{8qn[A-X:-`=6^s5^[E ~QN߾fd +n~ <sl,<y98 }2:x:!iuiYgM*e5\[o-y&KBGc25l3mQn +{qQ܍˲ qwL?ڔ< t?4z}[^C--ޗU7ʍ˓`7eq`7Q)ǣq~wק&g٥E0C+r*CxgWrv.><Ɂmn܃Gu0һuL8X:p@ߗofuvxHGW`S߻ uz7pp8{`w_Q7( +޾°oh56!Cc72*2wr?Kɏdrߘd2L&d2Lo+O=wz_ȃ抬-ݖ˲4uA$g-/{"w x+xι߹: y;wd`˦ᗺe3Lo ѱ/~Ijiԣ_ߞ+OYw >sD +I r Lz=ں:5J +L<kvzZZۤ ռ}9 +&xXiko- GGG9_gTwPCw~S27B M876&#C2X\>w8s"3dh`'+W_yż&d2L&d2._l>5߯n}kZ^_ʳg𕭳g,-O+-ʣqW2or;k{͓թ /q[c{G➙ey#gy~9]Y~u7llqu sl&8%x/w][;V[cL@9ޭ7[ZsK<(s`՛s y@G}7o^?0DE%-w0?z !W[g<廲Rx-&s\_ux| }\Y?@dou,F=T:::{|d|-*rAn|G1?7/r,0O&avY^z2Lr``VVbM8=$6xt<kikoH?ҙm]7.09o߿#9{Bf++xG7Poh± +2<{9OџIe3LN9-¸F쯬L&d2L&-#>KݪʳW}!_][𱹩[[Kyܿ)+wds29uejvNejno~9Y-;5Cio0{ +__)x.ˇs `ĩ9=s3[X^WV9=$SS3^?U)Ks`h`m`Qe.8'3^pEWs`=dn~?|ߧ"OݜA7GO܏Y#{ȼ05x3OV7p~@_&9T[[5`ۄF׳U{]o(UW[] W' {w [3:a?#??/?}7y^g , 0|vwї_:&U.S>*P[NPz{o޿L&d2L&-? ǟJJ?{!O>Ǐ8+nYVi]903y<|ܙ&/ɵuL6=un^4g#Ǣ/PٞcX +=3 Α魃ݾzYn]"7 +sE̎ P֨H +TSxg3{{lpf0ޒ7~Ncii>)`鵀﵀u2o~m P}[3ͰG@ F7pWnݾ3Ώ]ϼ=5$קj23pj#[2G|ۺ"[X\YuCnކ'Sn++wṻzouyAfn][߉oWOYz:-.0p-xKG2?+ ij1VnU.By^#k~/)e t9qƇ P~>SƗ>~(>2?n߯!e gua/{uF` -`#yߗoP3>|>k${Oy;iǾoA&}=?7 7!#dt*E0 ooIyL&d2L&dW"/|//ʣ8Y5< yW1=?g%swf^n޸!W.Nʥs#rqbH^lf.j."8{y/*43Դ\<92|oޒ;i|O!H`#H8ˌ`4cx5XZ轀}^/s}k"ʚ,--4ݒ[&ƅqeT׺l=qUsٔ/3zkH8{Fγ.η▽)@edd/{ +~3|sl^߿b&"ZGҗzK&9_/UcA}{~ -Hyix?#\d^ʽBn~/w՞FSx,Sn| Kk_çd !qUה&^CLbB/[IVR~d|GWQQ.圍Xju07e[? Nik?wX}]InYT^AD;?j/~og7&L\ uK%w#9}g LA2Qn/wqF.Jy7r;M3L&d2L&ɔ'۔EY-=)Ͼ7ic쫥<Лovn^<xdjzNnܸ%.^srqr~IsW烿pa^fkwIބN|c~s}sE^>U¶zKUUiUWuu?9NϱbW*֊=>fF\,=j+5Y5USfHx +^`|%,pk)}&\9&?m؏c>\ˆ%-_Ko5o쯼T*ƺfp&-z]uD=-m`Gj|[lo5a::S߻m JFFe=9>)ݧNJ%} 2{Bhظ /!Ν,X^x9+));^5g2L&d2L&ӷP0%+rަ@zijjD3w{ވٱvu%<`2z]o$/xx*:Wc7w-+YX5sں=m@knkGup~_ۨw'l蔎6i8`t:"y_1Ŝvx89CO[N@~9DOT=PGF&d 2oyI 2 W,CWdۉ );WRby&d2L&d2}[0}5y5KUݔW[0@uVs秜/ +x_`\_8IFu<_] /aM&^`7}!,U?٘s-_#8YSSViRDݹ4$y 9az>6~ >#=yJY`z|q3j11cWU0z\]Y_58i ֪Z_+|`~_g_`g|߷CZv0ޕ.r{~umVן떞3̉'?O~'?d紜>slߤ wohq!?@?žz,q72ek2L&d2L&ӷX7ɓǜ+yo[fqpg^rN{ d~ +8!.|_>]UTiql/) o;=yYcUgUZ_p~)n_wk<-2hG֧!R =`i5IKawqx wÇZ7q3@S*-ɾ7~ wO~&d {s #2 +?2.s; &8*cWsodj/{~tuǹs.Q`Eg2L&d2L&ӷY;K˥BJ*q?Te! F^->w.+=0s?\;z$p<<_2yzFm2?Ҿ(CϻJ_eILm:ϫOX_ߐK)~zF=ߧK\WZ^?e=7ef +rMj™i.JcXx}љ2?Oyq=wh}{_-- +K!-}?grNIPQ莜;ܮxBr3۳;8پ?L|~cW s &87|IĂa d2L&d2Lov2@"LK_͞ӻb.my^=#"\[z*<*_ ~j-ot=)Lx߮챯g8`\>hzyȞq7'=%C>ڢ_ݻ5M=Ҝ_n#ckqg-ٷuikkv@{?J 7 7n`"&dop\rt{pmIܧ}#s o_~Gh%Smcٷd\Cb8Vg9Z[s]̅>zʊR) ~>2@%+-{Od)s|66ºff-\dߕRIDq L6zrG^Ú'5AF2af{4Xۣo|_Z//}C+yd<((pR3|G2Xi\?ϓ_/ y篘d2L&d2L?Uy2@+t2<0Lvoq PgmzOjm&UܟKvW՚Loc?)gEUfE cG`/M$:_m2kK9_68e*e{r@z^_e}s) ˬ 0*8z%B&륭t3|Uk}Zvju枛WWffsh]Yki+%әn}CF +n60$%<5vIr`~;zq <:y $f~߮#u -!|/-\ד(8$xp:E<Α,K/`^_a$7z.\W_e NkK\f2L&d2L&嚘ow:ohq:2s~Y}M/m ϗftz_nN_ęr("PզyȜ">g[8VzO̱{hnG~7V_CWr.ԛWd|b/xg1/O_zs3\6n[f2'E/cvfh,JiXׄ>PQr㷵^{-^eq&JjC/`t`d/=i0~6~GpCEI[?oI[klnDzGOyxށ1=藞{Xz+9wĤ{x LWfsL&d2L&d'?NiQp`|~^_/% i//x|WU>J(22x3y쿐eѺrW󫴂/QΝ1P5]b-+d\Z4rMfbjRځyj.{jp7쯎ydxd>Gnb.fvk>Rk0g&9)Q?uqqz~#r?W MgZn~C"2SFz?:koW!hk܏I٩>́\򱟗~=e8? _?>[KJ2?#[Qc_-퇕 K[GtKʾ~$-Ⱦ7$u&`~x^<YC}zk}$CC9!,77 \ C8~?/(c[zO}pA0=:jf[HO^]^("3}e̿M,{ǡB6ϢUZlݺL\0zfL߆7̑Q﮼K_qOP:dTysSUۈsi8fq^}mcKs3?/2 +Xz#knrz~[Zkׯ&m<=:篭yk[m/Wp쏞yϫoo.+g}X|9d}5w`d9&H84,9s#剅?&d2L&d2 Y~%0xmYPoG;Peg)gJe7p|ʕ8?6JpJ:_~/|]-2=>R^iss՟=X^eқKq>/Ι{u\{!וp +mu"IEuAfܱ \1ugu&njֿ^R8[qn3FOs?^> PsWBvw>g#LcosTkzvK;FqΟ~G̯K[[7doPKZqo /02* /wpe|~w? gqй]QǙ!<Ñ26> ;}L&d2L&'mu)s_ff{O~bVXUx_x^Tz??2:ۑ%U^B/p9We,d:70^,ከ*q=3糿`{519!~=$sdxa?UxEbVz\|0fOX2J3]ny߯:e}׾n/\x_Qoq/p&wz#ܞ9?JbX63¬EA.H2W? "KZioڻIK[cikmTO Hoo J~ <7܏^?fq~wзWtr<ՙG%%Bи9޾>9s؟d2L_??wf2L&[4#F~}{cn3`_e>b'fqD< +_8f9v";wdgRh7C|Y^\WW]cvDžnn`-˼ڒ{gzgՆ)aۙw/>#Ŝ )sU>-Z)lokE3 7vf>_1|y>pavM-{^j7c}kԾꈋrV _3|~oȞHAzZdO0ǽm߾7)Sޤr8CY74q?jtXy /K?? qU}}n> ?΁K?a.~]L&d2L&dW{W^VV.(lps)+z y?W<[J(Tj̽`?U_:\) |`(7dۺ϶erR/Wk([>Q`$3|ncf3]330U3?w('Y<ž}~51a*x ׳GPy[Z:jB/?#rNi&;WҼuio;(>}y-gJ>?޸tFO}Y~#8P`K/_N/2חIoϬАz?d2L&d2 8]EEU +JctieJٝ;Wvee69fo-*+Gd'/r<-]>^WnC{r+Vqm$KǞe9m[P``ցo^``{mhɝۋIou d\. E>kk^7(90WTG?h_p_fo_ta\4|¸e y+Ț@swq`z#orpuJ{a8ttuvIa`LnLzz `w5>=܎`un_nhT9ޙC8%G1 H_L&d2L&g { #+ Oyay:#\/w]vx%e{wōΕy}z`K]zW +>_t=d¡n{޾_yػX`y3d'=S(ĻX&Vya ze+fr871?~jY۞9~ۨ?8|Y/JKsM_?~ X EF,e#\DrTXͭ);(k=tHg!{O:}rV _gG? s7&g`.#dGX|/*\t8mQL_GON:2gd2L&d2L?#%ƦUR";2 /^^qkd|3ADߞz`rIW1>]0r= +f®טd%s1Hn%>p^3?bOd `U(.$x0{پ\߮#G_;*fG9uTzNq) WxN8ӏe/J.#=ǏbGُm_۾؟d2L&d2L"ۡJRٽvw#ir|;~I_Jq&*`W.+Y lU~O7)*/+kpkg dcUg}`T<>\̳.nOޔeyuhzMIǖs!6y y"IV_2Pיևة7F' Яggma`ylop/ Rd/4:G1Qf3 wu*O2͜/{J ߶< w]l#{WQ=sBΞ:fW?2?fC?0q;{P>2 wF~ +_ߗ}Sg3L&d2L&d3Si^>WIϮ=r#;*zdJdKʚ !^fޏW뮏eWT1@#TU{Sx_%Ȯ8.Qtq&F³b~E* 5 پQ+&*Y ^ڷ5"MU`{ߘ%{;O`1Bfr?Ct=[s9 1+ }6 +<UU}.ÿr'0pv":t_a]ף 9u3 7 +yrV>/H #_!ĽNIJWr99s7~L&d2L&d3TzĘ"Jfyg.w|=W:oOٟ{o+**BE@p:Υ3ۣ_0:O6sms6e~QoYKT{Rc?_v&]TZy2PQE(ܺuO2wsΛG,L)u`70B 1 ok !&I<启s̐>z˪UUJX/>TK[[v&cﻎ>`]՝rاۯ/+MJElq~dox {R~:~L&d2L&d3U|;fƇz {Q_j~09+x_=7|/aOQVǶxeo`5xBok8OgL$4BzvkXj+eY:/Pqӵ 9_xlE9O3?g! ?fz}i恵'ƆZ@Ƿ3??#c?\g_o߰:#9{W c6&ы-LJ/ˏL@aDGL&d2L&gxr ZEEg_E״9|qE2eP)ya㭊mxx+]fUmʕ M}6gyp0϶-_\0;0^d.`󗖮Tu=SLNzGܙyEѱO~oy_\!\kRE>"1tY,Z̪UWW[9e/߀V|# ﯾJU PO =. Xgy_-e*kG`~pBzz +8?Os}5z3L&d2L&/To/9># 2yiwQK}5I#jtUC]|5?8+5Sc?c30>EoW9}W +jL +~zi@zSֳ&eZ;h3O=(fwFO%9'BAΞ:*'~,{sғW70}2a yu~k2L&d2L&_Ju_sE?0<*xK}Oc+Yt~\zь8y\G5+ِ[_4o\}™%\u.ѕc-B kn`[ZC#IF/+e~!$;cUUs=BMEƻg|Z8e{^gղ Yy)c+?:Ps$TV$Mk0; O_ 2}"I̓2A3L&d2L&/H%BmzxklV6~Zm oyW6k6[w/#k,I}1ۅ֔Ssc`' ~Ĩ9oɹzc스].K:wQg gϩ2F\48c~9Yt=ڛ[^Q>K%eQnr32BjTyuRLCVwN~#_2o2c<<2< +70 =˙JOr W8~L&d2L&d SIYŒЯ>_+(HtքȷwowI ,?lC~l}VK8C3o4#E"~ҵ%w`ZՁ9W?Q>ZYz>=˳\/.Wodd\ף3{C[ېz]%Wld]^Jnhz_gf &b0ggO={C>0@2ĶNa_ඛFK?*7xNg w_zNsGzz 3gHgd2L&d2L@%}e2O_ d\7/N9[W o{Vީ 8z=?5r@L/g7/a~qmN_: |Zer?"W +ʝ7$tdXN$gEKvHi+IN}ssWg%}aMfǕK{ʵo |\0^t*gP9 9$skL?"3Yx.d*?f~404*'笜39?|?c&d2L&d2S%8M`I*N`S >ѭ9x_6޾ ͔s8g14f='z\Y12Gq7UK[e~{I^.ׄydx*:]n=%fuuW吥6/\)fγ}ߴ}_(Bri`u?x:ǹaK80*}(f{ KOΜDN@zΜ?|?c&d2L&d2Ta\|)*g ȷf^[&k#mVx͘=8_yz.z<ےّ=R_19U1si:~nҊϻ~a>˾\PqXygR|zUU";+W8yާ5 1aL$]W_ kU"vXlt\n_~`e7.30L{k5kٿ5.Cܿ#K¸F&Cc;&u˩ʉO*g}&d2L&d2+"/`20)_G߅8/6'><_VS|gF _Kgg7|*b{ڋdoylb>I5(chGWWJ/N)۹*L)#+8zv9 jFJ߭xԐwrC9YZ&'Z\eU W\slzO{M1j3_.Wc48O=k.m>p? W凤OΞy>/aZT,]i/_1F`K뻔d~{ G_?<,߉[~3L&d2L&ɴu_^fg{|x?&w;>zFu/)' =ͤ#sU$s/o,*p\Z70 >qKƧ~lTJJKkG1Koc|Ǿ_ UG5_kWw l+gRMXi:q+ 3]8/RzyCr&C({s@BH]q]ٟq_o~Py4~'r?5L&d2L&ɔmBn1fֺ }/u{vz8Ӗ>`ǹ +p?~쏤+/C+cq`2Y ) Lٞj*Җ=v-k:y ȹD{5|<#qq彇ph{00n5vO%XIixves<>Y~?lY?M[nic/G0xvgXW9ufuO_>fX_Qruj^u9r=~1]_4Xzy*HU\g7,ϚӋ|<˫Y^>=^ӵʵwz,bo)y;p>+Gǜ=qO[Fq{X4V\F:qy^\GN/g;G|_N=soW_ywUܖ/TWk\lWWjgu^O_5Yu3(}z<^?nz|c 7;@+ӣvZnϢ5rR+'5u;Zɲլ[?y/nmWjMxԩi]O|<A=;;ێN͆|7/zΞzM{U?"Zo'o'oԥ;suVXk/:wZ%g^|-.y;~^uz/5z>oC`qi~pz$qwշ)}/{i ZOkۗ>}գfjfu~9}LcO#;?"ӷ۽~t{ӹޓ3zuDMjr]|~mcz\s]\?Ӻ2;DX˜6W5eq\fD__: >0yY7<G3(#־m'D8®./^G#?#:g=O ^~>Bgz?<P\\u{csQg<>&/jؚ32o5֭5n7m딣V7/]_0=oV6m|?Ϸ=c_`݋;Y|_}=:?x7ךfz?`3~K>jQzxc_}{]|7{C[V9=IVb?= tn7_g,V/ /g͓ W2}|s,xkS~ԥR}gs)_7"_ex3#۷^[[o?OkcH]J=Rub_6փ. i-. 47u^|~#zWcy}Yo{奣4#dvu{O_?,k{I|"rϯWzK ռaʼTWZQl=Xk4nJ}w3wf=vn_){ KZ㗼^y\ 3mul}ٿzya~!ڶ_?wwv{uz^<D_cl1>,}Ưs=srԞbfnBYg..Y][ j?9 $V XzA^ݟ@m%^ts(ru{uoz/i'5[ >08b=Ep{ެ'_O}f%O~ު u9߹+f`8ɛ=%L5>%.ϳ=Ss~\ _f[l{꬐=~G/<$7sogjbvo{>_;FoǯG~9mG}]LQ{zSbo8]=ع?yilxޥy?)k[^ל1OW3oz}ݼ k^%9p{&/&o>Y=|'_\/_zV;~_ zuSۺ_ :dWO]ZoY7Zk?wwy9Gր{ie5>!@X~q 5`<6ր{Y~X>*ր{ 5k=@/<6ր{=qp0k==?x\p?k=@/<.ր{= 5?_5W= a k{&?RA$ e +ɍA AY=|XS<":PW?2e6ll ޠC`@K7*I7nTNHѦl0 4*LzSxW\n:Go6tq6o6tq6o6tq6o6n?Jwwޑ@'݂ܭ xW;BnېۿY>n@^G]K=SW_!_!0G%Y0K'90S+0W-0[/C3C3~K7>O9W=9: !ݡ@ܓnRNsNZ[9ZZJH + X @t\w-ˈK@! +1 Om"! +1 Om"  + uq vD@PA 5LQr2Q`<03/BM<QP{aX|>g R_fn@~6V +B?q_RPz)͕nJg@;๾eSee/Der {_g)G*()(*/(3K9yַL\@ 'ƽ}}=7YyYY/Sܤ=!E<_ }`tenuehc1{**3M,eg{*H)%Je2[v\eph1xOǽʇ8[: _gzz/Ǖ<ې*Fe҉g XWbίBL UNRnVVVyV5P݂y@ +cλ|{);*{(+*+[;ZS}R-Фoczo?+W(w{\]݊~({re e'k*['9}wMv~1_`;{{Og+/*JM+h4eB|;ʔ-ݕ|kJ{l}cvK>}ɷ= W]ʫ>[U)SJQ?k| +A=s% *ۿv&y3utQ6Q6m핯>yʝKVȹDDN)R]TzάϗvJe[G\?_\ϸ_{}}]PYޟ**|eDus~;xS3-Cx,ͺ]b0cN{lOR.SyJ|j;{7W9== @G8SiGSJiOoѳ_*Ǹ;xt}>:|x>;/=<9de;ɳ.fF7ĻzOC~1fG}SR*Z.аy +Z3lommc?G{;۳|?P~Z)/۫tP;d/4^|?H?$}nQ\vt{nyS=9===(fqWR:2+cBܣ7H_9^9dzwW)(׺g4o!z ~'7+w),K%{ZF9sVN5&g)Pnq;yNs#;d߳w]Y;<]+ϻύ-r}._;{(㉿^_{-Ά.ޝ_3ߪ/UYi{d/J|kgeݳw}n'X^xr;mtJ;S_;ܚ&zS;*s*o5=a6f.)\.3^-;u*WVy-Y >wY{?/'Wfz+_*Ox>/~tv1s)qF%"|o +_uvm6eL9FEQA@p-k%ިgM+FH/iEh :t g:?_|~g&[i'Vd߽ӹ麴;f-:S~u>34U,V@ڜVXhZ.Osf}4[Ȱ2<ߥ4bǧzf3 b=s\Xf.Lu>f}MǔHNktI[љ:+,ߤqxhuwr˞2;ҙiەVIWS:dNwkzh˻='ݘN3ZR_8Xo(Θ5Z;uo І^/tsxe:7mMKGL?oUxF=6]nJ/)3n7 ggҖ ztc3c~p]:5X;u&]^۔lYKHMΰ!}3@o]*=NT#3@,-iSZnng~^Kư)}Ste>_使бWVc`ϥ;nN{t=tYsqNG{pDk8~>FЭy&=ܛdXVnO&wc\a< |XZZa=y:/_P°ͰG93FнWK4XzZ;t>om"I4i]_mH+ɽfϹ +ss/-нsaE#߅gi˞tAz(1>ЃyxosLÜrfa_Q/VO}3Jӹݴ>ABLGi:dٛoJϗ0xYi^:'~o~KO({ iHsQ=M,О7I{;&@?VҜtpi:2M@{7vJק5e tMoj]i+k<}`p-Jǥ~VfSЮϷM,A{iZ]ٟo]˾,ʼtx'UȊtv& Y=ewZzRbL5->1j M;9ݓP&@giQ?ofZXԙ}.+1}rozci{?ǧm{t~^~}Ӵw;dԜiW7Kү&@ 4 P!ɴl:"miȴ6VufxI:[BٷNǧoޗMZc[_JۤS7oyli45 ? U9J㬱ߴt\z&z?(՜|u"Lc^MI˷22]KCX +Q +{eqsͩ44̰DQ"DeQ4-Ӥig4V$فiS㴤$Z,Y܌M'xJܞ>\>Mz}ww'Jiq?lGڔvA`lO4= c+ $>z;8]|#r]宜o7p/\ٿ@`g~sW_`[T3-J'[]O{%?z5ދmgZJo3ґ0{Yi5FP^:4MzƗ=F^_0ǁ*Ýsi4 =tR}E[鄴8@o Yifk0>K=~>.Mvq[HӬ߃6u8W҃߇oy@6ʜ4 _-}N6vF9#մCiy5;ߚ>Uogeˬ%i _;wk!}{w`gwxy[_{wQze{?_`-ʹ3_l9779=W >=+ӧݿ0NL^{+Ҋtjt]@ޏ}z=7~f:&HÍ >K?®Z਴{k0 wߕLhp bs='4 Wݿ[|׻77`d/`|OLs7}Ϳ]7aWmH?JX'C҅ }[w}D$@5bq`d?%ο6v8WY)iSzd {zn+v}s?m~ggy:sҁ@[Ҳtk[ 3?;җӬ4 _𻧣ҵ}x(gn40^H Ҿ@3һӟ{݃ }+}fc@{cM ͇tFz_M'z=6n{2ON{'?Ϭo~ cK>SQHǧ=^StKC+\ޕ>nk\_0 Ю_=^ +{uqwvRlCG ! )ĺBjC jAe94#* +.F)bF1NY.圭>v.}<>y|8߿a$=FRLL+i_O鞴*-zƧVhV%}6:@'ߥ- ӃiwnM+ЈzoMz1֦p͜CZ>tUУ_g K=zzofnΧQiFppekӫ=lOOIh SQɼ~P'=L?7;~hߟFu~V'oᖻbOz"=YJ0)lO_I:~hAyG~SiZfw~=P:ޅWމmic= zf.]NL {6SAe}[:?O3==<7R<nOHsІgջH{{8+cZ^>~v:~ws ?86fYcO_Ly ^N/%{#e?`~]Yd?0]6|lOk,% M`4_˧e{cЖg;_cHws,`b]^u_`=0ߍtZk,~Kit\_˧%ʴʳM뻿n<+~|yʳ ן;]_ @cU۴}KsotFX#ia/4&ie+| K=ԼHg9]WeO }.ݟvclK?JJ@?*|clIץtV}1h `隴?hS+XJKE[wWӓMǛ@SMm.`iMY'^ɾ~Q,=c/P~5ڔ}ciUZLN+]}boK:#ʹ@{XhS9דzti:u +oi)x(V=}&͵@{W3fvNS>wjIlĶtmlOHyjWx9]uF0}9( +Uqwݝj[nLBX85c+`Z?ZT*eo(#+()ŐEd"ִմvmz8!ֽ{}<9LJӹ鶴7XyZNmFHzARz&7yi -t~ژ793C9ɴ,jFS3GWu:mJO6;M[Ӻ4L^o&?l]iE@5usĻtS:#t@[ݛ4;ݳ=2<ޜZM8﷒лYLӨ5!|u{z~j6SI`+gw%ɷ#ouڔ ;ۖ6%` ]ihpڜ.J돤U+i_N.-O@d?Vކ߿kҢdP:'ݜY08in5 ZJ iW2r mL+Ӱ5'{%iG2Q_+oӱvqwkӼk0=_6# g_5)S%t,2G>ʾNzIN:hs4zO#_yty;eֿ)](=7ݚ@ؓnLN@:+}~̛_:[ޓ;?Ӝk02XHg:^ci: ݖtiښK= ]U9/ tXsK!2ߙ.Khu>u0l*Oiqr:KIzW tT}y{st_yȂ;Q Wґf - iIr:*tGgݖMgk0=S ݿp~:,{tI,|W)-O@e?ޗLM%^>%?zpMLc3{4@3u6{==]zz\M7x:PПi5Gf#iqZXؘ~~Q'L҅ivutFZ^^^.KoKOu, _gT:; foI ´(=+^炗B=¿%s@ߎtM=;zUOMKMiIŜ hizMOz.ݓ~:o0 &Z9] /K[g7''ҹi +/,|3""#ݜN?i_:(pMI +LLX}/y\~zWxW=T1q,=[0)ePg'_.J7/'mKWE7`g|2Hgt}^,O_y{kڐX^gGox:?Z;)it?Cf%f}LNg >52}-ݔYYCӺqj>8vwK_Nץy5?᪽sT3gYw2+gT?kf#؞U/}1]Z3vBeyMz[s7FzV:/9+ZӉtQu!ݛ>5;[炡״_do%Sk=9xjw\^&|,o+z vWҋxo%3Sұ.`zt^O}jJ8.]+%gRoWWקo?^HGi7i49=9m9eo_l0+rמIN |%]uu ]]mh?5.>C6KibZ=[wޕOk? ^Czc,GuFf88%}*~T3kWpc>H-=#W{2핦pm7pܴ[zs`tJ:; <ק,߫uGUwܛ椹2Sd~?J; >+^,N' 5l{פth:xzxuD::}.})}#SsE3ūkfw;T3g{j8 +ms N{EHOHkל32/1|Z2m[;am[{gՌZ;S8\gcfmwt_W{6K}~ox51'l-'5S\f3jNS{{Βos +n38fx6|m.LW>*|\vy\njpJ6MO{}lqNi95S?,q/f{̯G>#mfdk 6UTħ~Л:fck&8v9jgpOzGh:.~~UXkoplpEYP9lg|NIoO[ܳc&:^ g5#6Khѹ⾚MǤ nRguk6:W2][gV zS{nvĉtf^k N;k]ڮnOXu'^4vit@D:-]+xEti Qvw f{~k>ﵿK@/9iԱ'_^w^uȫ6'ܥh|zQf ]=y0[ Auݿ6긋w|&lpQ't;fѹ=cVTG8v"}+k8;E?9u?౟.}6]WtSd.,:tz.p^xA ߓIm7;OkӜ CC<{?޳0#SfutG'3ݒJ?J??uNNN+ Lۧa5;)}&ސeu0j8+=펐F5{y>+pIvJS^ 5kgۯj>J2var/,gmop}izfPqIrtY>1?X{˪OM?lܧs>Ҫʜn|zV:epPZ?@u4{)g57HhWc6mqx\'@L۲eKhɒ%璉v%+Ȥ#[ow6F>W8ɓ}ٹ-ToOBt~=\쨹XxbdJzĜ-AYyȴ}˗?_n6[8h͙ӧux)Drz ͘1ef+F${5#[YybdZre_GGǦrP± k׮=}v={ã Q\xᅉ󣮮W2Lr<9#` B2-Y:^Gy{ +@} B ޸SUf͚ꫯŜIYkҤI'£_Թp,4+ӱc2_stSSSgwY?*ƍgHwy;edi|9*#Yۗw, )xׇ?t#<07U'|2q4~SbdW^5+{=f#2=Vj&jG88L}7g?O {ib=z4aAY#$e7n\9sΟ1gΜU_JLcccDӴW1Fuw2,?xb%W9r$쯭m k_s5[njܝӗf*T___,iҥrMl!sN:bgf鮊!1-)Rf}Ig?iIh7_JbϞ='~V\l0FBM4)kfSuXU.\Ԯp<4K9cO09sLԓW!S\^Ec?=3(hnn>̽lٲgnF;[)' O&ڋ/&y}_Mѽƈj8q"g3|A;uYῈL~rObXI9[ZZN3;B@=۲eM9;wn{ڵ+sјm۶- @= +Zs;4Szׄo=1 7ܰXߟ<N_4j^rUvw /r:Mzq;wLq'O.7Xw)}Ĭ˖-c{47ƺfΜywoԮz ڵkc9CSSSگWngt;|pYbs\5I9c:Xw)x6~Ƽy>? |I둟fMuR3 @} +=.*;1\.Y*޷~;YZ17t?#+yuŋw̕u=fq^ziI 1zחOر#/CT/5˗/l̶aÆ\ze/jV[B-jYU{{ C P_ 5=B5jg, ^`jqB6MW{g. +tU?DDA#w$$T%*| +)*>Dyޤ{ 7s ﹳϽw~k%9gf>g3{ IR A"@:DR +$sDL_&H e@^%`oyr:v(=uGtm-B_oWM6 c&Y6mgfv +R6H#]t')H: @J߬ E ȾfXXXnfӀ)'dw[mAAZ4쫥Ar,a1 +[|04СCUm۶MK0Fj[QRPqkoX-_''1Q[ٻwvQbEE[3eT"th@Z1K{r16^b}[ەnXE޻oЧL/}}dIMP9:ݜ$+/aƓ eUmkժ ;{# +Qz UL.^s dHA_%`>Y1} {|ϧ G;xܗ[+I|U~I\\\e:nܸQ3Cv;gԶ Ă'|ũ謣˧򺠝l}_㪓IǟR0 yϝaǎif=S>҈|vD,KQgSWct|6?h!vС){m +_}LUѳ=… )S~:if?1ߊ1+\3 +ЎoAx?u'HEF>@}|0 0@a!={V˶^ڕV>c`Om?9PT<|ȹ?{&>FhCcubŊ#*UꋳԀ9(|IFS-JʢxQjҌUP]/*/7{*:`^h9w\{c!l)?a?/'={ֺu^؄;w}ͽNm 7=K..[ؓ=/66(ہ1cƨոzΠn:wh/A?W :*!5R}ܽ{޽{#'>|Z{?>9?@"sw7o|ٹ\K +])IHHH<}|ӻa&Dٶj*Uۢ|"YheܩF*UsQ`1bDev07P||_reM$o,XlV)[l6+44JuVq&yʯQ!W=Ƅ#('KҥU*-t,^xwƌq>gQZ*X`>Ydt~ (k|y/שS/a*Ao骶zI;e;v3=:5k֤]ebɠA9r<\'OA E̘1cWaSچKI8fbݖhByI`7ٍњRvCY528{lٳgqϤ[ԫWeiM|~f*yseҥRƍY0 0.ʶ~Aնf͚=4aCf./ʕ+Iye/>>ob;9Ց#GVElA/  +|[sӦMz#MIV̉nUֹ/ ȝ;xMj:uٯa*fj?O)gΜ`^sQ?SCE t߻Jg9y8p@ʞ=k F0 c#DZELX''k.+V-c\2yf-E.]Du[۾1E zL2)mە(Q322R;4K,yG̉n+WNm WVÇ&-`F_Z%ҲeKx'|}]ɑ'NsUvӧKfR4i\󬘘v7a&x \>Vm=zP  ڰD NR2eVZmoApq['l z/v7 Sڃ:5ZRr+`!2gua^d?K8uꔢe˖f/*#GI/30|Yf-F_hh(\tM[Jn>)d~QA0 j. U@6ؿs̀ 3Dd,XŸ0/:d<4w]q][hpEfΜk׶<-kԧOP9ꗈxF0&o?Z/_bpjεüPNKje˖iՏBųgt߹޿^S:ӏ~1 0K3vJNM)| +q6>O0aB66ü}E$[>;NiDZc)}&L!u(##0=Jrƍ1be{ǒ(ƀ4\cH^UFX{lA;v#˻K-]BXs)k֬}o>7a&x \2 "mB99qD Ng٩}Fd/]V>؏)N'53.ظ6 d CCQ{[+I?Q0qDW^,Y6WnHqnݺz+@ }.qKLL ؕ.\8}_IrTI3~xu3fMca@ڃiO2ƍӜ@u+FV\jukR }ҁ{⛠tҹly@?~2hNLO[xu?l8 + sݓ +%{ *e)pn"XMxfq2g~٪UH1Gm\y>DٲeSbŊ]c1 0@zli'111X+Y&G0ӧm +y3'r${N9s|'nm"v;No7h|uC۴i90wwSX LC=ڜJ6gΜ!2/^\;\xq7|l?5Z:B޿:Κ5KjҤ uҥKB?|Ƌ)Zqj)1c3 0 /"*U +) RH=  +UdʔIsSFTEӧO]l"]VZth"q}իUBBBRYf_[} +l1υ [JsΕƌ;^,טx#'o|/_P^=G `GU;={gg1Hd-#F 2e^l{NiG .Ɯhɝ;w*?\oݺn~ByN@Fƅ6mZx{a*5SPV͛7gϞŭ9a&8 8kOվg=))IPbqHp#66V7o>3Zgwn޼)M:Uz뭷t' }Z:wl@)S\KUdgcСCBto}C->ڵnUT_9~z +_Nq_6c/j +*mJk^==VZפڬbŊWN^0Gldd 9ϧY:Kf0 P?}ꫯj'E ;u1Ν;usѣGE=*W|:<,Tf6hi_1lW?s… E*PL{k]YXX-+~r29oj*<9E䧴OԵ_o,B={`GtBE+-붗-b} +bW"""F3eԭ]CҦMs\v$ν{HݺuSZ4eaR8g#u +hΝS\K4"ٲe3E!se˖ y.&& {.?|$S2ʕSԱXboR-Te97n(<~Omv'c,\\ti |~AT{lY90}a|6wp}lڴIa͛7 O5C qEaN?: +~)ūW'A*騠su:faz8g(K~ t1 +[`6my$flHJ;3|?kЪm #GtkM֬YbP[LoPB%Ao}Æ =u}ƍsha~rkCM>k9.=^uͶ,\j>VL}+dҤI)|;|p֬YSqS~_H )]`̞WR 1wܹs'P駟\ZѣG,fFf}o$_dK̜95?rlۢA,Y,oFW,6;7BsΖrF㖰vgٲeۨ?0 0?ܹ9zk{5h@ҥ4h ׺UGJ>|~֯^ïFo5;EsBJ1-q]Ig+VxY{~?Ժ8#0jԨfMm*RO:;ϧi۶&+m/ Sw<){%a˖-)"w(_r vuĵNx's}C3fy:U]͎F8)ju4|uֹ[' >?U`=v ңGZ͞ӠجZMρ?xӮNu%}Qc~L:U~SMy]k 0>={ sx3l s?-KEOgPBO~N+>1nݧOWN޵3&:ƨṸqnz?7id\;z 0O^ghncJ/(=SH$KժUX s옗!C j=ü:Fr7\uT[--$66Vϸnؖ:I3gڳ0ߠP؟.C qաwYwI>}cEmaLxNQK13ʁ ʁ@|< d .S(i#yک7o_\]s5~l _+IgaF40nq&J}:I]suXcl{_އܷ0G ߼ Vxrȡz6yoP!gLČE >fkO6Z9ïM6;R Rچ> ZW(Tmp?߸q> 3eQdj6iDK"`̫KfkP4hPzcs!wZr ؊|vjuL<%,,솑6 +NkhcAޖı00 8 +8g3gCs5kt3\c1Y!N}10=Hg 66'Kqz=ݹQ*:^oܹz9|v(ׯ]50.\` aĉ j@F_NOMY?B =p/!u{.Ssg1P!kԨkʧcP%3eh>>=Hn_aaLЫW/Cq?/ss1 myx}n¼:r=MgVNʘ1c4}3݊5ÞtFZt}`g2R"Ĩqz+Z__W_iڏg*;%fj \RY./&x`P`aA3ΎueN8#a7`b& xpkؽWf + E^V 4H-vZA&{24QurʥRO4}/3$#f`E)hذ <~.GEB;=V]ꋺ|A c 0 0pOÇ%祊-n fU~{/K,JnMuG3~#+glرc}YfCu-[~ŜV`\{[T%Kj<ҥ3I{`]=b=h{75QٲeoSZEC˲a)pyi;TbŊ?R3`ʋ U@ālIP'OjRJ=vM_UGG oӦJ">L9=׽}pBQm+,3@^t'bTL Rȏ [A/^,;t=TnEoa&xqnlذAԩ_ԪU+^)|mI"Ÿz5 ^̆Ab޼y-XPҺu=:}Ek|`>_[N(qV SN<8!TJ'Xwcpڵkj˷ 'p޽ǫ"0 60}o׮]R\\ʱuvc'pT]' x_ 7o>†~/Xf=m۶o j9%!z^#gL-M&U{Ѧ]vz%4vwN~P* ^ z]D ܼygxca[*wYiy+X%  _RqiESڴco--XR'+޽[g Ѧ"C rN/L25Q?*2P>yO/>0bz9oMPb;80 }ȑ#K{u}ܹSھ}uViҦMxis Wi=TG6lԿgϞRll/_>b}n KޡelfNşgto;fMǏWUV=t׌tQ!!!VW/(]3̀whWʼzu/=eSXC/sZj8sُj9,?N+V쉜; 0jKLBʔ)sW%Nmt9j5ݡϝ-իZ6n0\ţ=4߿z5j7F'wh~\ wĮn==U!tɮ~-W2(ILLVj & 0 +4%JxԦMM6JPܯbS_~ϿMdb$E-/HoeݺuR=0sDR+Wjƍڏ|\bbMjYfra6cɵ=z4ϖP?Ip}6矪6ͧ-X4iV\yF_!'[lE3~u]3^z h;˚>(U}kpEg+WHcƌQyR}57a\#J`.:bǎs:H֬Y)Sy%a12vrYnˆ O>D*[s8VcM}hŪ65h C(u ЯVjr^z%R;`t/^$_jyGʕ+pҗDmt"͚5Kv;Iiٲy'f`aF8眶rE"wͥ~gkʹs%]9$MV-NՆU&XNIƊ+5 )Ϟ=|>]Qh3^R{D=ul.mswyGF8qTWQFۚK XP;0]5/_.ݼXDŽ-Z*W|} aÈ1:l1G9?O9?*{X~Mmj큹<=Aӧ̙3r)E9y෨?~\U;)G뎲pBKgjǚF}3i[%K|h.7X=V;5-[f:\vh<3O? Iǔk/T15\y>3ȉ0-)S]5;1Mll߻ܺu,ӵkWAa9fi4*8œb7~Rv{O^ֹmW^}Ct 2Dx NhŕE5SLMteEݾvן1'm2͛fdq?}ftpW >8p`Qj;5׻woU; Bt5"6TXbh4x`9[<D~W|qb0 0pO|1wqԡCs9r<ùTLLt[up_sWHժUS%KE*$ZߩMT/b5 )+TMO'g pwKCٖsDeoƶ= +v۹IrZG(=S;sM?!q_z5Ν[3\caÆ|<At-_<1aQo:[{"##h/- TzU +{z%_|rLrJ 7ky$jr߬4o\SgxպҔ`NCwgn__SD=dg,{ .@שSICڵ3=ȓ'Of̘1MVQ|:> !k kSqn}jlٞ8"Wڰau=.%‘ 0! z0j(Ssʕ+_8ՇS?nݺ^'eB~;ժUSb㳭;Pnk[/p566\q + tEu|a$,BBXE ƈ +(|30ePqEQ@YN$ _w~U/9uH׽nW)4nXk4[jj4ir47tealEEEHKKE1h86խ[Wmڴo/ޫc(dtݺu[L6==@ժU/h+<<\yDP)Wt}$p84}+|*5/dFl>?xܻe˖cccGͽKؔ/&zvhѢNJ{I֯_U̕aÛo;(^ᣯUDsر٣8dggk2}@.۩{ClیRXX냈soDp1SOZiHܬ_g^^Vgס06m+e"!c,ZHYR^V)ɶ(K,ω>{PU1=Y)]H{ܮCO[fN,C6ԺSL4l]NIwŲkx=57B5OGk3m&ڦP+V(z%Zn7PeuǗV<\(W:-[hdfclϟe;EMdg 4 +N.H?Y@<')T% v8_ZjP{|jmvXgٷv#GhƯrť9 M!J{uoWs$ڞPݻw+z*L0)2\\\ԌE]g?6:f dL2dҩSjGD% 6kD_Zc&u'A}F =φ?> e֭%%%h,͐ۿg+`W+di=X5deeiڠ`5N^nz쩧l%fQ>MNL @S?'dƍz9OزP-E+ѣG/**Q~]Ç^ow#F\OzӬS41&P[K--m۶ ɢl{NYφ{5uGoM{N(JĐkĤVwz+Vav3gΜ9D9cRLL cy>lԩmmgxM/gWM0M!?ZDp%Zf>`-ސaGi Jusqb4Yl(SgPo߮ @nOgZl򅣙8qVS+Q[$ÎPxG|~+W_}*333u2xnժU\O )_);ټyRL_ +Z |fϞZB%22SqdfΜTTƍfỺV?j \!$:fn5jd iV4p@qZ߲e˚ vԶʰ!T>}Ք.4@PSWV-{ਸ਼,>P7@g'?D F<DŽn9\F'(ݜ/;V)8q^@káFm>_d̘1*VU.+2q.k^ZYbN(V@T믿~#h=.ߡWu2Ҙ6mڕcqs0ydgPcKa׮]/۰azTORDof͚ >k֬[cƃ~et]>H;wԽ{SKb/1z|Nʼn kB!jdA©rf%mױ;B]?DDD<ʠ?<dGz١DVRׯ~ʘ=dkg|]va푱߮\|7hjh'?3E3fxk꙲3)ׇ'7uQMd&|j!` Ko=>&}H.]~Se{\܃My2ȐoqeYQj}3{_ Jj+SkϡjΈwh` M%@9|^G,5h@ƦυRPPWwʸ!7k#>|؈};X%n_ 4ߞ +3g]@Coqe,>0pO M/j@{mh*8S9Nn,5eHOO7TשSouѱ6œ9st1iG~m/5k3PdvZn ȕ5燓o-33k; 43"s~&b5o>ڴic`pF:t!j36ٳGO@ o@ϻZw)CիW·.XnC1j۶.[ȸ +ukh?*u;wo2bДq[nݺg?А w2uTC7uF׵g:vn]`e= 2Ⲳ'i4y6@nG1ر￾=u6(ķrΉsHx3|sp5 py֕棍5=S=2pBC_ʕ,XkBٷo?I8m簰 zOmI&OrMv?#V73rHZ' }Qau|>޳ X"=hZX\Om46Μ9<6g͚ue8@3t IgҤI|-==} zc$7U-E=vS}E%IKKe?Ov_Kml{HٳK1{` ]԰/1eCs}LLn?rΊǽ’ _rݑR\9a\fM9,X%Es ԅ}ϭ}_Zh + 5mHHHd(D\M[p ӟL[v}3M5ʵײ)>LobAC')‚PƏoh@^8ƆG΋:x7믿R^/+Vng*YYY/k^zG=@zhzmXkӦm/ p~Ct̵jƵA04/ _퇋{۹A~[/h۶m®͚FAC헶A<䓆`h̸<";6W^vzH뛱NN>wї6̑#GXCQfΜym7v:P#7O>Q lro- СC$B! :ZkGq"߷Q7l2]_dff~$ʶ +s>}NJ+IvwԩK}MMM~8[*M41/{CNM, .(Æ 3w@dhZǟ y6q`FK F-~`{1>B]6–-[x>=~0s}4CX@%1jX tiAmDwߺQFQwIttt@ߢN8qYk׮^Zt7|2}tnsZ4u˹s羖/Xr%lJm=zMnOc 9€/vEu}Qnݻl?}dAA7nW#dW +ԏi|2?i;יw7<ҿ +ϩoU; tZ#5o~|%7)NA1cO ۀW{v?~\O;gMo׋5Zc[ŊE=[;3e׿@_8%SOwهo?C3={\?/_t|~B`~'VѤ.3W}-Z4kTgQߋdMZڛNΝ;_< ;Bz=c*ժUQϔ)S,Yrv:_}#\vn֤I㱱f'_d( Dǽ$'Nݺu! 2}wNQPтWeSgd-Z0VB01Otȸŋ/ ZZ)ygP~zu]g٠A3]vgB:A憎4Sn:M6ѣ7NH/qYү/VZD묵 V);Zff&.]0`#GzsΝ;wڒ{!C(111q_uUƔZ' Oo3Wɴ?֯_?孷޲;ܬYf-sA5SxprƍPƛ9s(=5֭[ͳDh;vC3bb1~SFNP +<ڵTb$¼^yÕc*T33^S͇͛^xe„ ʣ>$%%x 7`KHyOtͶs5v\H:> zo^'==}>VI'֓3{n… I&ykO|N"]J@tP;\Fq->WX+{}}k8/x~~rϋ^=L=x'_Jٲe=qqO 4 8.h=x /!!8tP<>w\%++s>asZwɓ'{?ݧj,k5/?guf< 2I*UTLyvF7\),,삨~ۋ[w! Vwsa_WχC(gš{H2Â@+3K]ϵvj4_4j蔃(>ޟ_JaxH L[D.Vkh=kWV|:M=8^U +4T}ԩSHQլD5PX4Rcn6-- mj)!|f +i$Y?n\\ܓ6HXsviѢE_޻n5w8֠x? +!<1119\ϒ^HIloGr~K"vd  +zs;Z#}Btضg Iy@em;Tvk^yq6!^oox4gB +{Bݝ2k挔r]UZ>JbطԿ>יp.^ن@4h^_ye%sjߨr^܃t{)qz뭷&\uxjkVq?^<*1My?Ɓ@o߾`?1?\ٳG, {ݻwk g[ܫ@ D]vxܫC D>}???Gw_?'[wZrr/v__O_Ͽ;[_+ӟ~?g>?g??ssϿV7|ܵyw}^_swvmw=o}w]ڣk>?3\ѵ:׼Z{}uoq~߹o~rۺWw嶷t??mOu>[}_-cowo;-_׻qnY\wݱ}Vc|;u6{o5orӭc'n{{/yNvy{޽}m{M;=}_ߞPlؗ~u>k1k߻7ۻͼmjZwv}3yLmV[Ƨ+_C~ßOG_O7߇dEr9X?hIV9ou@ґ|7o[RPKvV6Yws}ή^{[7߷yZz7]G욾ᄏ[.9]}'C>>>L~=k>I.\nܘ ;l&XT[^'eL6Y^/Ǭu/q޿vw[%y}2k׿GOOz?y?Ygro{;~o[o[>;>?wo'3OOz/~~?>/O?gzZ9{|~˭r9wL}5oڠg{{{{__?3?3~gm>ntK"__MO޷J>\n[Я^9Kwf>Oϕl|zXT>$nHg:.,!khoiqqq[ 'E_#| &ϽXx |M|>|pڨ__cyaaZ;r;ZVL浈۱}m-~na޺ VxxGĚOמ x>N+=veݷazb,jr}Ď窟oW,XBϝg\8nw\~O&)xKEYaϚe9Y]y066]O L wl׎mdvQwxv뷜 iC\E\:A}ݱvcϏͮԗҥbA^yܺ>lc{ۭq(7_r')cw_b/y| F=C,; `L'\Η̉7v~-- w <\qz0{wO? @Øn96Vl[Ov̝+w;]=랳vͿ7~o ~-??<,~>x$q=wx_:!e)1dc}#l6~W|eyR^sۧƔ}vrv_&1(& 2qMW4~iL$<l[,Ϫ߫Kd5?*Ntrmu2CLt{1V-;|Vo"}a,Y;oL_s ->k#ﵯ~}X =[[{w}K6M{Tͨ+«GIިӴ]g'cy/a帥WqkFO]681lܾ n5_>p/6_ߋjv_r3ɗ틭eT΋g('0ތF1|>#׳x{npoz.da[qJs1olVw}µ[b-\s<~[ru{?/Lpb˧kzqv8}#9^yG!V83Ύ<="_z>xlw䖸q9}]3_%.`1l{R1lllmsm9[tq룈)Gk-Qf_qgHtvKnR;ܺGv>ǭk-b|za~{1tXݛM.Uwk'\aĆm'Or +][_1'PH6_4Ogl8qvNjs_|plRGl.9u3n,I\#Q1{{[qqz8|%1}S˫z}{y[?+'߼~Cǡ\5Gו OcGurI5_\k^{εj>-RܤwpmژM%? 䴉ZޛxY2I\ƺS{ſ6~g=Lo)|r~Fm(y۾\ދX]w}}9gc.v`d}|y3Ϝ|pB[xSϭ~N [Xm؃-7ăInw}WJ^6QNWu8^}]s $5m+!<W;}[Uac_[O!תcޏ9SWܾ//?QΪ3}BCYgYyfOn#)GS^N>M1ro;VL51bӛ;Kї?|w ;\_F^t/a݃MbmʍE/\a_x8x_yxA9aoyzq=n0r[gK9̷~ ;7_X.˿˟ -]9Sx7wmYl'E> )0K~c]k~pm#.#qݶVwa@qcx/֏3{aƵ6Ƶ|OE{_ba07n_5ww1dS aURywg썟kl.ߍ8\eun[ثxI-jU3YXb'{qP]"&ks}"/q[smlv.Il=uT(6BqW ,[ 8mOs~z[?Q7y՟?X mSr ,P ɁOl䏽yrQ-#pv\Ϝ8s>KIIOOdK$˷o$u2.b^kKw1WcVm\QO;mkԮƍ7g0971mmTxc{:BYg|̶e>G [>*h?X}cr ˻ö"PQ^~}_\zOu7r7$h/gLէ7mf>o>x?^|P<5gD~y*[",\U|@?ż_<#aX~nu[kyBm;Gsp_v(|.kjӅ6ǭw_ }3T޲z r+yۊ+ġK6yQWOxZnW6o~eSuݟLV~&'n=}#M{m_묉ȏZD[3{vعAFx[]8AH'$A.Fcqqoc_ٺx前8Cx6XB@Xm/oeا}>.~z|>6x<}ygS $nޒund8ձѹ.=km}qtbaXnDnV7Os_췿]: ҽ_o*t źo}>_-O_׽z=xwzmVmr))47|N:+7n~Hg闦 ӓdſ|6K2ӹJ.]l4s+.p9'򋣶<کw9ESa z&~1}*w>ur<Ƙ؊m#ex~rkZj{yck&&SWa6ɖg'ϏM|Um@_|Ըb勝*>YnOY3P7 Gx|ݾgr{~s %;CI%On[uz%<mqNGV0]wZ5n^995vUOA\pӻ| ,_ć;//.`s/й /(ps_L}cY\v,.s|\ o=޹K5ڜK$Xy PքlŽ{z\ƐyI֘j :9At[,^\9^k/,aaȾT,k)'_be]u\npxo?9}׼g1 mDɫZLqҫv>[j1񊭁lIڐd|^Ww#nC+{߷irZ~_Ξr86W c/M;Vk׍xsmvg ج~iy4mϧ_džY/jÊoϥ}bnmgg_yy;q@9r1#<̺xqw1oSDU g1}ؼlK:SluN]^ؚ®'ay2mӏ0ZٹOgXn۷}^567lL'WG^x`X9wqm`>wzk80x J߳7SHme#$C'W+jOx}>V_‡oeA0jc77j*%;Y;!<&\'̦i sKe#e~Qyګ|/ Ԧ4 *s6l^1ϰBUgTb?嗙'f8X!RW}fice~Z=/.':s Ԧb_˸پK۰[!{mm0r:brq ּ/ eC V=G'94 p_9p!^K93n^~S+/Z^a_0ݽsw8xX޷w}3c/ Z 0\ySakqd^|αO8,vgn0`ޯ/nׯu_Ko<_q4}?/ivX:9Skx89\noݚŘĚԎ~ڶ߰6bp2GAaVaWݹre6>uDՈ^ĵřw++ֳr9hܩ}7xzW^WXqv|÷I5dJq+Sʌ֗jmX1F,g:n C'6?|as]6-iSݸ'H*u/F(֝(랙;'o]=-WMY$v~4fsax?/^r/.mVyp`͹ƍ9_}Ə]? Ycc;^_g-s#:?_!<,lor^}ɯ&_9ot޻K_[4{W;XYd&Z{g6W{se媈X[|GÜxeqp~aΤ8tϼ9|rp/,gqU PL:NQM|QsNv[6vdq[:kH_#_^.1Q~ʃ7fX1󭍳f5_;>q"Y#P..;M^.xG\SMחmr#_y2Xi2n&NQ.!IiVoKL++󮿷<[sګll|}Wo#r5oӏ֦0vpbpo9Du 1w{;\̰Ρ[yJPqI!juqk-~¦o k/}|㚓ˍY[[>h1{競u#egXbwqEy#c=~mi RwֶG}V<N\:|Ukq ÿw__sGuu , 67o 6q[Z^Wz6j䦚k|Q_jе6Zr8(w_Wm_5b,RGL8_Τߎ\]˖~=յGyDnKqv߉\A}ͳ,S;Xlskpe׾/GJFX7̏jmxmw9z料o8wJ&5> O~`,>k>|Ɲ1uUr'ӣޡX|?_bI[{4/>ϛ./7VuC,15=* p>ygK/^;+ͱt8q\{46Xs8ݤzS8Agegc9A^sxFδFv>oY\ ǑX`jX:kgln/pyobDkͷk^CâCV.a_?${[ ++'&.D6s9oIODK6w~5MSP~g3u?oc8qK]rc>-N?;y/n.Zda|nst߽.`?߽[e;V;Omc[Wjmkۻr{{93՝ ל!=Y-L}aGsߖ[=[Gx}&x\s>a r|𾖰w<[?޾]pO#ǽr{me.xyٟp}l0s'r^sӶqP1h[3ĺ{iF\)[Cr}GnmPMr% عF)ϒ5ٶN>T#Ӝ.l x s0oӟǫ69`KnQ'1X D5(;+[ZbqN=ooWUΞ1prĭ͍1IL-Yۊ% /'1dX1o\ռ1Jaq?8 r=moR;\NYcQEL Pl߸Wȷ}3-W_[sŘ&}cuyk\p9otxMm+0 pijA1vl޲y4JxrT O(/Lw4#%| kn.k[]A'4m9U _~;<ߜO'9@6uKsjؑkV}oʋo qYt^\'XB~}"j5!e>L(<wwv"椯c'7NUK\W>dݳ|rYQmܺo3|gӃ~׹DG#~;};Lrt;at6q <1ox-ao>ak0n\u>u͑乯tw|Se%{1o}@S\`jU.Ҳu9Ƃ+͝4JY,Wyr;5inNc(>d,?dp=+瓴K:a +ʦ}rGٚkzqcd[[}aF9M=s孆-uܮ[wZvm\YC^ym~G:cg0/g2n$9X mwwwlgΖG_xOkbʵ>V+[䊅9vJ?60뻍Y]SeHs8ٝ6]^J/֫qݾx<밅tƍ&/;7cX ݺΧS W D2_QŭkqdUC^K>=Vw4-r{:l0_ŗ>eל 岘^vn K}o.Py`ҥb9~5kM$Frl0@?} ; z-gc;hoIbUBѮ>7vmf'{п ;X~6?kml,ĶOn{Q{8|q9<0'1N>[\6t>f1e[`s-_o4me\Q΋_fi x>asq҅{K:Cw;ო7/.Lラ<$5ׇ[p;q0:]pX`}!}1ml>޵kuhTǯXK] 7dD5տaaZ,(^O>_{ڰdXJD,Sgߞofsx]1 sC{V1DQ]ѺyzVV%coMV ; +1@H1%͏wc1>ʻ0M9u²W % 6k.51D{@{˝Ú֗Զw{r>oalyxj}?<ѸrUb[W/˻Sw~j_񥋻Ou}н7;gvڵ9.+ͬmyCv}dnXưAks:_rB?F=>{a3s&sl>rt˗r^5ðI֫pŝt-'˲#8Y^cYX_/ʞ'\W&Qp@qhcC_È޻ vwn/\9xb@:=e~'k{a &[l;^/t95'(dƌ^VG56-WA]*2V'm}pcaTሯt{q+\ݺwcm϶[g=ߘ'7ugճ?rOb!,Ry5H۽îwzOR1||} 9BZX\D K<;pzѼ}_?>ع?8<%.53&xUi8Eͧ^o9X0bfU3Xy;1]1,~/ۋq/gq($9rYv,'{93Ͷqq;eM8kX0Kʲg늙$e+d\޶~w0 H߸ l/d-)'>%0,y0W<[V8n3[X]?mM%W0&bDa'>OI1pdsuO{l[qic!%' {0 -|X}}_.66c+N'_Zr>/X_4Lm;ͣkgw?ezMгzeJ}9ӻgw{.RSۭm_~g {wuS,s/hM0ʪocZ缿JOSݻ(MX#r1`zYb,\w=yzkAkz?٣5Wv޵OURGX(g7oG_qB|0;qp{KtN ɼ0q܈Yr"!.OS׎Zc jlͰq8'%NA=7۹ڜq=<6ՙcMpr7tttIvެaA}w1M/.!'f͊,O\<|f%uƈci\>CحGû9Ӵ|͓0|Hm77@>@\G>9YmRv~rqv>Ŭn} ܽkk'dVی?h Ո89M>Ghw>}yӶ~9CIJ6w,鋨ʛzϏ9n.ܵa¹®ζ9-\`yPa\6.kb!{'ZUڗ[\WMQj#[_gv?dAry cn-͟-ϱ>mJvqIc3[Pomxל"9< WABY `~8rb=9Ov5+W<^<ź̍M6'ǘȵ7]cvy o'OVgD#;&1έS1Q96q l|-;3_||ekkg-?|\svW~b;<.iN9׾q߫Twx. 7ݣ_yZ/^`=9#;ϝ+市A+zjg3zaʋ#s_ͽp{ŏ;XS-x=r +͇G.G1<0FO:s*%N5:k#<eQZ9s?]KμzXC%ck7w76ҭC|4s:1lJ\ 9/h7xk;_)β؟9s8osgm]J7=ҭW:b_-9Lk8NΏ|Ƴ9&Ѧ7*&xF_Nyܹ[jO}击,m1Sz8AUuikl\=eDܔg|ygg3UN;it.XyBݓbn|^\56'Xcuy;2-y ߻njW%55ptTxrWqU@H0eO}!ީ5?4Mߖk]@k6oZ1{e~77t~ 95[Q|z9FG/\ ,bDη_я㤎X܊$Qѥ;%$fbݓl!q[\KIvj;QtͥVo {zء/@}eYQoc\Acr\Olgݼy)n`Sߍ8r|ƥoҿߠ♛kF6e)7R?=վfro|2 1/igc5p.[T?lGV7ǼG[滦'տo.=<;N_y{^[>^-x1w;}x*0;kǞگZi5w6|Ac_1{q5ì Kl{U[ĢVP6n.RNog,;7r]|GlG*cR[g7Rlc}SrڴWgd}s3oKs^Cm 3> )zaΝkyb}8OrSfչH'֯\NM^<绩~y<Ͱ;:,'G9ܯIbXTbzٟ_[S+o'ݿk/ger6",lv?Ϟ|4L1/㍻,=o"[n#xЯ{7ܝmtzGT^\?aBZ2Oɣyqz>?_xxwW c+gN8Y|s>{/YX_yX|_5U.^B,__B2CЖ#ڹVbuۮǛ[@[P;y7r}Myk慉 \L˛c>bm;k?󐳡Pv.69uq6㺋 +Rm3um6a(?$&Gac/mۗ+|GrIw;=1tΘ/_ZxҿfJ46AqV]\9oͳS,bB [cݛvqU1dcsEk}]?,Hmy`>wX]xܶ<\p}; x5; a}k{ȋ#\Twoh9)<ԍiG+&2+o+n.ʼݭS_XL.Dr:hym-o_Lq{snO: z&';~1Ÿƨr&zYGzKobmŭ+[$F涴m1t[>msj][7W\7-s2ʚ +9zՑ)osN:febǕ7U<X_6C4VOl%D@O)KKqmƟ6˞WnF{b^~m1XY#|m[O89*Wuc/NuIMf +cMbC˻-o499$#xr϶2^][3Ʉ~u6 nUr[M37%yNTC R}{mo[^_swX`\ 7OxFs W-CWԊ;uby/^++>lr{?^[eXޘ[2xq=NYw#f><@ [s."#OP|r9?sQ{hg\ 1scZ>#>!\LSq=T[U OkVmKr~Klmgnqi}.*4) _f;e6yr(I|U+֙QSH_ Ⱦ3#qH^h_/9;mo5T.zDo6>ڇ%-R{&ǚ0k?y^wf}r؟yżNcw'Go9 ah6O|ϝ8Y}U3ts٫NCs#q 8{imO^snZ8=[Ym/⇞_eL\޼~a>xrcӭ^>|SUnXo#c}ɺ\/_nZb<:v5B9w^<=yٷ&rzdmY}I:Yxx^5Rpoޭ)й Z֗KXzI/[`s27g_pgC/qBld1QM-;}76X斉Mk3m_j9(?;+V{'NjX&18]u-6xq68dz4>S{R}c./Okk琺w.>cc\59f?h^7»B#Iߎaǹy(+~UJuk{\ +_ƼK;q@kjx97f.7^_ce^G {qݮ\[b[$6aɝhu 3(&^\GXT+|M09mޝ2T,~}\~Ռ>z}Hdvs1ܐCo_N](O{/Tg,F/,2vxNj76IQ{zIriUfG>NvYa1JN/&ۜg%wZayqk/ǜޝ_;Ox_AmZ Lxrȗ\i}lxcoLu $V%&ϘqAgxu 1=ou]3i08-'\p?9)tw8Wvqc|O93庾{(`0l{,T R^5f\Sr26vَ 8j6ULo9ofVߥ{/{]e[~3k?B[ ~X>&GVlƙ +6 {WfÓG~}sy$/!I=k0frӛ ?ƫ仆3T/>y;o[ Cl{;r<}:}ù5Ηu>|: .'^ڒ(0Zٗ 'mq[KyN^yhܶWX>=iaٔΧR|$_W#ٸ:1u{#6/9j~vY^j$v.8[Oo jwlnCx` mWNsޏ7&I/1yr9ɼ8zw<|d0jB$S=LQzesB#[+=lz`86޾Xakd<:!b#\f[QDN7/N+q('Y>'?ax 47U+XXbywJ,gi JiOwr(|?fx7nhg~SO}YC:ir2TJ֟|ݹ@*xxW_|$S]6ܧڸ *%$_;}SnoTvcNN#K^ޖg-W[>goU#/Y[n+=gljreoa,&Xr +% 6߱6gu8aS}dqrtY_Y\=]dn𿵹{k˛gq]qR$bh+_HN/98Nyχ7>'FNr!]u7#3p{ʟ6 |}vi:^?×ɑ2. x28p1O7^/k[yc$W@Pޭ'V 3o?|ss8=||'C4Gtr\^5 {w}w}}<y2=~Of\+ʹo17OZcG'oy#,iz/^`)tIJ)OYWmH^4fS$O_eML12}9S+[7uy2*_?sugӮCǹ <ָ{}Ĝ᝛L\xf$.+C@&n{9͹Q>1srqIUC<?Ovݾ#P8H~T/ Pk {kjs_yy6S9\qe@؋sx_rF?|zƭv{92u--ف`{]?Õ`N (s5}{xr~og?ˇ߶9%3|F奘''Z<< w혱-;yȼ01kGfcS}'Kxy- >,Z?޼,msݷ8]v=ĻWw)1vxt~Ks"g~pyw)0lp'0`$.ěo^{koZڙ?z2`\S߫6B^9w+/z1^Wݯi͂<4f:CCm2D /\Ũ; Pn[ H6>08غϛ+(vYfݛXrmts6;ߣ9ø{O7.f:H/_qEr{8^=jƺ_3[Lٲzc<6hck1_xϽpwCN]ϵ>vW|8]q; Uڡs~qҎM/~շ Q7$˟6~~77OIwB)+/~ou|w{~`YX K K{h&G哭<8k.-erK?5_V1OY-[(=o·6n$vkNmܚڕ*G.6|ڀw^7A;5ٻI>|'uyq_!Ge+\O^ax>ׯ´e/\.0[;{h1[Kql۵mky&|dw^SYm\rky)K^l1/xa-3.A~6;V^x+&nx?mwͦΟ1GB>XC8O +9sCk_nQ|˺e):_V]c;ݷ~'j+^>u} :owpW:i +kkS󉙵BǤyq|s1=lOq,yq]oO+׶5M'Yιl/tʛ_ޟkSY&PtIqCXZ-ٓ =$Tk#9d^5^?Eйҩ{G _Gv{7b}VH9ݷܯ5r׭_n>yxb4@q7cup=~ Z_К711l sſmm= k_$GA6BLjΑпwq\s:<9P,=ym'7`k[bNXӘ3~CMr)~}7:x3qգKf/,7#?];Ƥ,qyܿ+o+uʗ}󉅄Zkmܙ,ŘlI;Zv;Vg[}YÍdΟzGʘ{O)e 7Yk_~qWGʁoN#_s./q_&[ |<8xq;_\b˅ON~Ia$mo107#Owu^?'~[`Xm.gf}sy;{}n1Sqd>-4S9QP,V(WX{X,2/_>|o}򈏈d].fUI(|;"yVY.H~5o//|O??~>w0;ܯV͗y9û8~?&x/8_뷔|*ﰥwݓk{6ܛdAZ_C]̱X^ͱ/l죾sy~ƤtG\scdzq}q;)`~E\&y|ac<-g3]UY.吊uYR]}mX17K}O6fLZmɨP{OYUq1\JFuc{P9+N{$=;2&"O,b2_#l.~eye۽Vn׊_RZO mkӻr(0HM`-z|ww= sO )yrݝuk0|9̧T\݉c}:[.oVOm0/uSV?_SSj}Key[ak u_bײ8GT@hoˁHǩ֮xavOg0=yrqz&Dz~Vp_z> c /O}һUo䰔rՍ}* ?9,.J,.'"Fz]_[bu}c"ḴMq7oUo5~bTr$X gXaǬͧ7Yr +[$3d[+Wm>^yr&ׯY?#b]rMĻH\aO~x;g~g>؏g?Omc'Ju]ͷ>UoݾXb}v}^q_demc|+NG[_1C=^x(%~Ul{,caAGs!,$_1*ELVvw vW~-'vN:Nqhm'4?mnL˫Վ1V Zns oifkM._9-a/LژGs\ń3߹ZOzm7}Y2L|܍-mb/L>y7\~Xgnzmbd2lf1B ʩӏ2&#Fݻ\9~WUq1aɓWb@|t|d>~QvH&U"|39.)|kdʟr1bP +we>9ߋ\XmNοo<-E}V볓ҫ;I |Yl|lj6-οujMܹ.re~خyW>=ˇ5k_[,Z###8ݛ6_tEc\=d#~ا$XT>s\l9 <_O \wlsW׾sYNdm\?;lu˽_g_O???[m?}~36x7xqaa߼ ?Wn{]~qu/<8)-Y'%}x=m1Ӌ>ZcXʙ1ZP9gm:WL>.~kƥE+g3eXZ3֎vΈ=SiN\^덳%gǣ܍ڡvokmKCյ$ .gm~$g]vaz۾:^{][5U6TvT|zǹX֫ެ֯[}kTnm10kvWΉ.U%7ÌeNr*xpr\/WNC{-1|\x=o] G99Y%{Pi_mۆml}q13wfs7'vP86qegjڶ9)/.jҁ8G9X9N[gsO .޵=.R;njdv֥^!(.Wh1 ߟGX{anq!}x\\Zޗ:Do xn-[/W]60r1YF\nkb+N43,rd_Mܮke%[DRn̩ \;؞Kl^_1cmמPs};Q[#.%_~Wg}2nEZ}kLy9/vv_l,Ϝ`9mkX>kݘ__1=8TV˹-lkc=}rg)'{9㌳$g˵\oszOv3\J7(q۸W咫u׾VG:i|s-ox6dص鶓;rMŰoߎۻn۫Z|O .[{֗N{=Rcg Omvqc\xwQ'+V?>`vZs~3/^a|IcrGc]v~Mۦut^\ڸZg;>)_xKjW  ~B:@1]Huzg8v9.9>^y=*6+.?,.}Ŏwy%bI}yPrZ-xi=dx!>/nXY_ܲu}da8>ⳍF{}^|r u Yq1 !Zeqƚɣ]OYl۫bއ6]=ag~w8}8}rq~r l+/0|x-kO[h^q8}MuB<#l/|W="&?6/Nc%6ֱҕ/_';`1He2!ƼPʇt} tM}w,r]_b ʎd]vxKvhX]|;Wp.8hksz{/ח/spה#ŁI? l2Oᕓ7uWC޹Fdq1c/57umckltmx{Psof)؞\So=?;sf壉]yot̍o68T}O&\bZޜZڌ9Ķ7ƻ|y֍5s{Oq6Ƣ5_ם DH7'ޠ?.7^O.V|[/|꒦ ><0W/%Sl;\dw͚,Xu%/z?0pcu1p8Š˫z31dd +6U~ORd.u~cRN0uz7s!߽O%<ث6ؙxrrly|o}(.Yn1(H^}}爃h?1nQ.~y֎^(_;!WZeGWX^,о /tSV--6io;5Qň /p/7|O Z+o9^ရ|qzbz;m;<0SA;^L$g{b;ɿܘq*_^X\G+g~o/O(..x#Ml0N?Ô!7G0\0;7nov sew男>'=NC-lޯѼhm?ǪbY1M}w~l 崶l92s8Gp\iWz! [cx=G'w> soVe_6>.Ɩ|Y;`9vɗͿ;gn9.Te[COܻ0sw~r +|eJf]K̰{.\L8yޚtso]+}vktZ{-,V_͸Ma۷>Vb8RX`zd};t!wqԫry\GQ}15k'1حh'})`mƪ&orHUysZ8/bCjt/_^g.0ٵ/>뷱#ן#/^X_]`9/. 6GzT0{b{NIm\[W?mEg'mkq?3q׳f֋SjMZyB|t\Q5kpQ~i+攟QLPmO,.q17?2=mn(y`rb?.N~X GX Q\E8̎|nJ>GqpVq-|. gIEE`h{+z 4ǽxmdF{N<; ;l0Ay'RNpy' mk4Hna|[.aaw;#DчT$Ǿeެ؜W;[[qY|xY.YRϯ\2S\*+u9S fMݷsu 'YC*@F˓}{Om{WٞWeuwJj8 ]}k^~wk ȳrlOU+xb_rc֚K{YAyӹ;o;,7H67vC՝+|k񲎋^ j*ΗnEs]GKWx\bvsJmo=$"|ʋ ⁍k0'k*fNG旝~ǬWM1 yrʭ{`~/۽7~%_I7Oo**NmҳcsFZV_˯X)]CNy:'= rѾn-i.ߵ_I|s7/?uyG䟿x~5Ի٘ͦ>Cޚf^oy|W'h_o4濹/Gޯ~@b;>{}6akU0|00Z/ 9?w8ay9Gs4Hy-\ba+#zWarp+c_ߥk;g=/9~;>;0s1rj sS4$=`|Q>7~1+ߜ-b=3, [~uۺyqx71:tbW&/#f'𕿥(WPcm2we"6G*y[GAg왭-#?gkolN1lGY^R'֧&>l}Oaw;/Z/9+vZlv+Co}L93k;?#ma9r0N` Dmٞ#۸{3_O>NuNj·һ_7w{= X1xr1X_pkmPȣV.0F`T9׷Ouyf>gr:00{Xso5F<R>kƭ6.>ݺ֥6l ֧靏r?K6{?gݚ3Sov쾋^.qW z[s Y(Y^t6<Ɠ6Za.V5a]6x9,ueyԩˏrc6^5&ޗ+-K%}N72~F?[G.ǧ\ q-2*tca,L[on}ٵrj޵[Wox5/O[?\N Oܾ.Q1kIO7~1/xh>EclfؾWF̢6xuŌvhߋ)qU[z_/;Xd?9\ڥok7^K;'w^K+7<!={遍ܢ\\o`cM#b\![v]~_LYL|ו95'd|IPǥm_4.=obˑџw6^uKlKJ:yxϸg eΡMh\k^sn, NǙaLƃv +J 7Hݗ{1.1GmOW7yE\~`r/,0{~ym#OXn:ml9EsX8|j~a>tm'7qxKx`Ӹck9vۍK?XnhrMҥՙCx8][/|kŇ/Oa6:1{Jk6Ҷh"}NdPEbr9VNM(F.uv|m%&#YZc $rkv@9x%G4Gi>[A +tܧ~ZYըNr5GWz5WZ7y Wv| ro<˶r۸L.`c\+ݤO[ϭ-_9,g\v<-GwdsJŭ<ѷkc}O] r?ǐv=_20>z髒^ +Io%sWW{c_i#'SkmhoxK:.Ϧʋ #97u1l܆q}_H&ek7Yb_4wť},ܼ17YoжV8E _/^\&G#m}N_2rb*1wV9~`][o]>lj  uC. >w|ϻ6;{zF9pn %7kֻc\U΁ͧwǐHγX8D2YEX [\ Y)+z?`,B}Cď9xcvy q-X=O,rlȵ0Y!L{z,˗.m+_]wWaƙ_.a된?{k7_=G5ϪCҟ0}|:z}S֟lz}ʩ21օ01UP-]s+gW2ebeZ~RO{utdr0h3k0fmo:o}w1mD[ up8Jϱ!Ư7UXXNw Ѧ]gml\Xfչf{ǖ~|_m搼Гū,NDm%{WN_5sU֖M/dsr_p9#摞$;2\u|xaq!{?0;_?M ߋxߏwz}/ +PZ{|Ys#Xr.\8[]_\d}~qln<ʂ%8vbo[rWg{ߎ}ĵGU~.oﱜAmiez_|֚[3w|:Ɛ|ݻ:ï>o]_/.ɮm_ocus!`5/zu:F.ab֝·2NRdK }UDܣs_\C)N0dX]=F cOm.6Q;/W1^KLc9/N(!?+s^l'7q@Il>Q/6FRwؗb5ˉ7{q=9abHgל}zj&ܺztP{[|χ [d>j[ +gp+p:m峎_uw}.ɫ֢4mky(bg;&ߕ^_>q|yX\"gD/oB\{N/^b'3_Ư_xsIjs/e|Y;^G'b[ضl;(B,z61=ZOrndvfvwB-^LVql]9Z/G|DB[Sc̚ݿPC g-]]yomtTcrui3h7O7n+vs,V O S5l.7Qk[ź1Fb/K,S_̍u{a<k{%Z[lVWq%cA.V=u3=c~q}qBVh9KV9Yflr+߂~n?axw!'mRҎ#ccIl<_%>uo=dV=9+u|s[鏺Zl=/ǭ w>_O 7/Wc07ԏ)'m4gEȃɆW^]y/ԏbbfo8926oFkFy'‰1O}-^u4?1rʡ} pO/E;qQJ:-Wwn˥77> U,EQݚ-Zʍꈯ-h9mY?CXαO{yO/.r1;_I/NʿZ%ú'ǹӷvѕ#mޗ|u:|ܼ ίqwwbs;/,χxo +W_;rzfM1sW']U9^7wl/h/O91ܿŷī +3wnλ}4;WWeb>zn6T٘\ϛڸ(u|sR껾h웭AQ7hLu8+S>e`.C\SNlWOO,2:818v8୫[ɧ?<}Za|{#(Wi1<0y}6cbӹrѾo`?o+on3ƣcf/Ućy铋u6zg6Rm9OClkwocڰR{|CR;%K{룛SOq*E&7f\ZY8Čwq'/^}W$wvd9v{?l,lSU=wn|,w?qN?XoSۭK_\v=pl,ɝ+a͉ϰ{n}'5&PPu5,֗p Y|Φӏ'~o}#/M}9gn^ܑdƁ蹍=l [ߖzbPG?$V+m!װuLx]G'wַntWR;>oQc=3Z#_dCUe?iS쿗Mο ^-kn,HϹk\Wy9~EeceKKnŶ}ۜՙvЎЯ.,W[.oOΌ '?տD_>vcXouy1WleeէvXP~];5: |Nwx^5A xppB1||0L1m'>uίxoזk8(đ+_|6(qN}8bYLL0;a}/Y9*p~gpi{-5>/y!n"o؃XhsXVB.bcz&h9 opzxXڼ㕏Yk}/g~.e_uw>'>j缋˄oMd}떾\_g~aW{Ga9sٶo^HukX+vև8% FHqfV ;9m>,mӔkɱ6uvc7œI3}(Eͪ'oFQLR^kwv=.~񗯦妫xߺ_j_q8yG䦌OY?(>\+ūѿ6[_,c}/ߪ9\g|<弼x+kWmiٖ[[Vb +;G*8r1ȵ{aųIʨG+ߗ e[ڦ;} ?:OKnMN;}ڪ|~Wp?uL.x߻νVq_~+/OH8]Vo{ ٧{={7|g/f)~%[:[ث0AiXcx+0 К|ΓWhFc_>{.aWpEi&x_rX{WvNCǘ{E >'Qn7(oF|/ytaۼ+qcxSLGr.}wmZ 2Jnxͯ\\+׆ON9ȫnvs[*{U{}ICDvqxZqq>W7N`ᆝwIk}{.nRoV581Dm.X[诂-usddgϫu_? /}?9.a⮻{ Hv'#o/\__n5?/W3?r[-Pe/sON\\'v}\={cZ[{kH@Ϧ>'Ty-ьG7<1&c[[tɖ!GOLks|wji7b+c,\U{Nߦk?!YU{q4iJs?sY)A/\ ľ/Q-k,Kw܋o`c{O6*]aqv8@yaqxm=n߽W$_xܾj 縇wuݵ0;039ݡ6մ\;(ַjnݱ؞ؽ7yӝWpq %Oa{ʕ Wٚ^SurS/?ۏbY-+h}q 6y6ߕyizٳ1#܂ng+uNcVOuYб8 Sw\[l# 8!Q v7Vqq{+86Tyc&X&s,0k/qilJ\}}k^,QD|;ez^XlߜݛG0[ky͍so1)彚˿h\Lݼ~˹'ť} 1`cB=' 9 urǛ1tٟ'<~/f{/ֲhy.og/0wE~;yq_ɭwb }JUW>G8][K;0_5xk-1l~Ѹ!i7'?ᅉ߼ J'7fϼ庼׫vۗw_7~(&GZ9jR\vd6ies}_?)jm`LqtDIS}y^)}lkdsmb}ּ9@K/bF/r.96~yƪ]8&[tci?jkոŀTkxؠ688ˆ_N3}AMI,b/ŗm/ű8~ex2:y+c.!Ծa &aqazak1}|ppoWpo駟9ݡ|啗 xnOt8D"n1$Ǡ־cط/ʀεbaQO\SUs:۬kP۶9v-|5͵,y! u8I6.UQ@lڿrc_gl,ҋ5=f/{j9gbwۚ˫Y:d<3yԱK\c]ksWNo?vceygj]lru6hrTvHwÑFΓ}'3ʟ{bٓUkFudƝqO[[FKͩy7['% wW۽kݜRr3yܖ|͞u}ɖ|bUЏQ>꠫Gw߰qsm99)qn~pÛa~!Z,^O_M9\P\ߚ$'5}+pCcRFo)sofso~c䰽i=C9Xx1Y9_nn_kOTD,O\{guaᬍ-\y .?!W'VNTnw9 +^wq^1&$^\sa[4ٮƤ1d.9lF^`_9o;L0<> ?<\rwu/ d棾, +O^X2ƙ\Ĺ޾dABpbeoIK`w0@`;u//Ml@~k룞Zִ5/s[mu/mKw3at +WO̾2VOjjɱ4nƝcK},k9Qqi]<'Vna#GkGGg{1Zϸ`i͓;/Ml:b^vo.]ñz!~9Bޣi|6_]<~IܺA:Ko͒(_bs,ɫJcq丏!wDs5!J->X6|b3xΒﵞo?əgul8~ T5y  CxVgS[ㅡf}ש&jycT~6[o;[_Dr?яzL9-rXpWj❆fi\4U!ť?as*KVݽR9J|,N/99vmg FJF~ҭ)ɸϊ d;Vj2&2϶[ҽ7W3]y9뿺}cMmvq/k helU_L>qܾ啴&صu/XP;WޭuV"ܑHa徼˽t[,u6;n{1.ٳu/|7;v縨=w/λ6qk/,ҹz)Wv}ϳ}<Źt k{\0O/\N̯Wo< ;h /|Sh>]mZoQ^~3>qű\^;ښ[[JnC+,x2;V{+8go˵~_HߨFNfui~؋`mN&O[9YoOYȜ~cL/oOWw߼- Sw/wxξ!?pn}ٴYŭk 7 W+q'Tŏo 3nyv{q NψlO+dol娄[2z|].n$'n߼KJ(R/Vow,ٶc!,&k^!_V(=sd3_]_%BeC'/vl>,g\RQw_cj>O(8n:bQfK$ꋻ8Vz{aO?¬g0 5w|s;z9W̍x<Xh,5nxGgW<}5X~g߹$?k'k?ܲn<\n}t8OqرRɘo0hy,nc셱PrxJ%h^|W6Xtq6Gɮ7a[\tq6^c]Y 6#U5=t֦֮[mҷonPG Z m,1+ ݽ|5^B[g]uybub8_,6Yj/<~H6%3/h?^d=8&fk='߯Wq]ku}{>mw$O(n%l!9i];2gyx+ɮPշa_x/j9UcSeåÿ}_{cׇ`['_XG~3-^[;G*K=F ~l=bk9/XձClb}>7 ]֘cLV'u^s9_޼P,1Շ˳>yV5C}u\-0nZLGl';(F]ュ𻰿 ~qv ,q80+Myq(kKueW}\1/.ZXbq|/h?3xɪ_#V<{[Y:nLզ{۴؋kwڲ D:5D6;6 SnCq^ON<(1ۼƻ8G[7Oغq1[c1^A}"|]2R6~5uOںFk߱~ݰư)qDcI `N0,g =J>_xڨn%OiLu.aznΏdtsuQNSQ U*L:'>:yblavܸlnka~鏛q]2S?z]`zZ}y=[o,nj8S39gQ_/^o i>xOrY\w~zmV_|œOԏ,}[#ņ&[K]K[mpy@+WyQZ‘2䍣=W,\jY'xk~J6f@@[0 }sg)6W.zXqB\ת d}$1(c7s{z\5ȫQ.G[M#[e]](_quPXw7ʁW<}WSɡJ]P6nueE43~_\H{s1Nc^7-# q|ګ1/zm K9>_63}~.VAĸk }X\kcj9?q/koExϟA}6oX6s{ט>)ٽ>[:m_+rTX:+W`v7o?^i;h8e(+wbj͕,Qt:nP-4eq /R'[[r:Nő[Nӫ{T{Z?659)vٔayKmYOux}Cogy[tu ,o//Ḻ1qFu3f9,OqP.ԱUljxR_[xyq8y[[ԿK:IeҼ9lX's=X\xy}M>WxN1%w\ Y}{zSb+w_:8䈽ZnkbEy>*?"ZP7nT7;j߾;.Fqͭ ;u{_U:X뻾6}+b_ݣkW8u2q0]cɬ}T;SHE;JcCou8`yr#hhpp~~&q\|w_]ݐy,/;giN߯9͍Ť7q(0ey\_oq'{3r1H;b/5}ևC+QYޢ5}nOvm.N@!vL LWTrb ۴OխN۷cxyf6i=wҭCoc Gg,8-.n@}{q7G1jqzF>y1/aģZ_\0uۗ{<+9 z?#otϰFm}椔#jROy<:׹hb).Lb؍,ٞ 08Yd.ӏ̙[J1ckXtӭcՋ[G1Do}x]Nd[ W&sԎtC ~*r +²/:c]`}r]w~F_],I.2vI\/}{qEi獵c.ɀc _^8Xݽ +7rSɳt!>OUl+vj3n93ɷ-wuMrWTɼ{sx|1MmqlR,/Orźz`o3:X6xsM1%p׽.bb [A2xxR 6:>Y}[=7oOnyﵱbzdK+k<Ƽe+Ow_̸;l8x~|vX`Z#0=.V{^-|i2th[cs\-F__lWɽsxerڔ~~=W\9wV~.qU/'q%LV_^T9mW|//a[k8^D~^~u /6/|=W܋<[~G_<#;0!t1Vqima;^D{:aY>;pfwgXx{q'-5>Kb^TNθ{x4~(nuJYgeƢUSGxK|?c\K,_Wg{qs﵁[/}~.5iaz +_ߍ.&o_sX-|ߦ55.YXN^ Ugg:V|^\sO=_㱜[FlIF&m\@1]Y[ԺMU+}p3׷ex2'Oݘj'}c?w-9軺9LO\,Db(/o\rGuZ+yz_6VpC/W =\*+mqh;x ']ݮ}+>N_[_~Ӟo>_]x}3-_9wՓ H3Y1fBގ>|uUYM\`^XJ2\y8dsoW<k 9OP߭9\ދ: (xuB寮d:|X/{]@=/>b%ƽkb+_{pv61/o=6,]SC5>v{Y^|t: w/k˼bYtd  َ oLokX]m h}a=+F]d\=omN؝]/~r1u/\pUOQ Tws74_ϣOۘJy [Xݶt-&]; +e5Ͳ23mfffffcf~Z{9߽۶%\5 2Fo????~?_O׾wޱw}ַwη7v7mwm5kkշ?kc︻]ί]W____׼o/oOooOoooѯ?ǿ~_۟s۟kumkcmcn6ծg̟_:1O?z׽{ٷoͿӻֵο_׿O?gn{}G:gg{g߯^__o_{ǾՆ/oymWoV}WW[yY_۽כɷ˽{7Vn.~m~{~nݿ۝svs7nn=o'׾5nO_2d9/~}wkSczmV 7:xX|ǵ1͋CKާN?}uܹ'o[5Y{=׽~qw_;펽ku?{cɸNݖ~y߹]kGlP&ndmON޳76 =:5].=w}=9zWm>_}=n;vV.7>m;jC}snkwNϣ}shn5:ESōw핓ۍ;9Եc׸_{zIoo&S:gP^4oyj}6{~{w.4>ƓcKҳnwx??~~o~om~o-~o~o~o _ݾ߱7oxw}vۯuۯ5ۯտkW|W~}oK/~/~/E߾}ſ~%|m/?]{z٩7o&He}1tNnptwǸծ:qy+oMYݶW=_ʾ9w(|6f;^s)jkv6ζt>m\q}>~GӱɎl_?l̯u̝׽CV/$s_?y-u=v^>?]}]έ/M@칮Y-wlr:ݜm}Mo.(/ɞ^z4}HԎٟug}ɝ%=pN[pQj^ߍ>p6økЖ跳nqؾx?~}츳/Y]v O /| ߳/==[79zb5_0~tDA:C,cx=?mwul _1 xXzw//O ~?wm4z.O1/ ۲o7wLsrlwlxe/mplvO7/l1u%aDaq ߫{1 qö<1'{ϡ=o>wxavb} nwl{>1u:UW}m0o;\~-ﰿ;/x ޽N8Nߜ/j<8~w#s1g;:++?˕m211>ƙ1tⅯӹ􃰴v>InfXm _qeg٘m3١LO<*Θ\Z35Ya_c,W{}v7^}>;g#Jdk['n_|lb[F]#7Nm&v$Φ>,,_ewjey@_`ߗp+ڿ7;cn\.>wO?{|m 3Gnulaѽ~GC|i&1q;n^{Ļ?,9l{]~ L.=om=nvs7NN{-g/ϰ']gK/l ,$("nR JE|WdKؠ񤍛O==oh4م'!/>`xa/瓜r}Wqy/6OP[7&4étŧ_:~;kqbr ]ڗ %^r}qS/+ۗ:@};5ѹl}c7Ϗ1wvrVw9ڧu{.sio -0ݜ(d,&#1.돭$ILou+o? [^Lmϴhfsy}m>/~iөw|:wX1FVjaڮ~/Osўo0?f vݜ={q\k+b#[p)Tvdc8?}酛>O^a}Džv|ө}q=n`:ױo;/<0/mwzm~s\)G`y~q`כؒ_Lnebrd'prʛ׽^/\:Y~&6B>`|zՋQnD 5V/iSG\pGskƸٙ_~zq:Ÿpb}?%7;n八Ϭau6/쵳oN}w8ȵyw{w|q̰>\Μxh{oa2=OOCn,',$+?/~VX| 8mL)}z߽[_0}c^+q\XWp:>w9 qx,l +eCcܻ{{}m/ gl̗GpTeoO&hi^Sz?}1t[ajli߈m\1_R<&\7Ԗ4vzKwazɃnr6O4^9DgrG1U>>/\Wl^ol5&mEbɾqIcX |miޅ-Woo ߸.wݳw}_hbp=ŗ?G{x1&l6t6 /#<g9Wbݎn_2}rosO_ϖ<,910P% 0L0`xc|@Y^^<@vmq6H8PcSQVwlbDds>?бcI9oSv녡됇/6׼ܸK,wWV\x~g_3ߵ^8Ccp-jߩS_{^15cn|~(<ҎWi}ZW^0h^$wet}_i}+ ۺ)9R2qO9b8wœ›;\b}UQ9ma}[MY 2=x6q!n^?a,5O\r ývd0W0˧?g۝=wivN{->V8&(1˃ g̱zNÔK+'׾W`w<>9Yaa7+TOv.7,pSj{\uVo\V/=_0>~;?1X[͛_;_x]?q(oW:_[>ƥ7m8y8uba9x|a擷?\k[lr/q%㵆XJeᄌ}|7_Mf[ћ3Z߿8',&q6Wz}Zi>9CͱNGXX{[;Z.;ŸGޑy#k{*@L`Q&BS[-+ַyFa}& +3Mjmo *绮1%e⁛lXOdn^M^ϕ1wp79o>O<+T]Z:_<3>ь(59axmvOS浱k \np8`u;ǚ]˅6Hnw {2qK&c]?Űrw+ua;+oкkDܲjnL΅'oVl+/,0_x}z7«ܾvNk,,o9 :Vwwe,.8`bTw\z$hL [s3ƹ55llO -X~Gro;c Xp7ޚr| Ϙ 4Z]#ddwKY>^߷?q sٯ-w#'ofql׏߳[pk `q|?Wv?ֶG-㴸o}*?6l1{6Wv]jYg.qXԦ~Yӝ jƿﻵ9g ?WP/Xx#^[ z}YRށu[˭yy׽kQX~v[!WZkarmj̭,_:cۿ;+.(믘֑zA暾juMqsjaxtnO$"peQrH<-_,-V8? [iwwqK.n>sh-{۞t&ww{ThLc-9Tpp8aDb1[Òͮ(竘m8rk;'?Icxspf[_Cr=sd [f}\LϜ]w8ZF>c[Ϸ8[\ؾ_-ܺzwg3T.kkdg }k{?>JݾVh=s3syz[=_ wM|qO{msC3kdUk'a5:~k%g]DкbBgqíy}f\|NK$o|ܘ$}n ]P~k7nWy3xs+vZz[O`8~_Z)|M]a3g%=?-w/|-iWr?wundhhm.HVH؀>mUk&>bg{1fzQ,^ᾗOa8~/ d*&ڷ8N_~lXXcurꭴW9 U*mݿwv/哿b}yhnk;7v1|lA 4.!bqkm13}vz5ۊq7FNO غ+榞6^N :;rt0Z | ؚ{_lSWI ˗~8[O{Ay;/Joe诙}2*OT䕼z>ktxwZSZG _XI{~kHnLT\]N}sc[o١Qf\R~MF6h3)w>-yq{ЧSѹ;bڄa}[kO'Rw8yCNˣvwmzU9~?t6b;;[yۛm]maf3,`M{rh܅j|jv'uxu~o00@XLgfP[WKu?jNԒ\w5J^_k O˹YwmEO8kqRol{8pz]38_=OyX9B^sOufM<166zy1}"fw,W ~uXջ]~Q_[/}\ztuƵm}w͑:Fk|-To? 37̓'n넄^{6ƕux|.S&mbwxA\X\u]{9ww}+3\T'gm>x&*]dۑPoZ2|:ƐvOyht_t|γ?Zkgmj={|y<@9~buhv`Ǧ8 5l|Uu=1a]NisnJ\`Dua|r@Ma&v:u7`1[lSr}쮟?a |찵1eƜh.2P;_>[8^LPU p/,[Ư6䎭^\W;4A[F3<gS'->8​Q0lGWfhS΅Wd=_>ʊ8ŵd׽dX,tҿ}wh-_w +t`w!MqygܳnX?`Psm8 ;.YQLʸxW>g/y鵕+[9ݼΝ'bqubb)TdϼpƗ Q~tm=>xXƳE_\81倩^/'I'͋m/z}b);XnVvk(dynkw<1>qVڄQ\}(PlXC0k 2z aQaA#YlZ[<j볆-N,Z;V~-biWm];ulv OuH+u݋A|:o띉υ&f,|.gvpn=Qxmx 7X<>19ok=c5 ]c:jώ9#_8`[|}_`ay YIyk7^Cʔn>SыÞZ>\kz'Fu,j}j{(>ae~>bl;_o[&[|K[]k +>>zx@bʲŴceE}7}r(ʏFG­7q*NPO[;t`~K6OdX9mܪ[~&;L0pju}} 3|OynwL9{kEie0_H{Upuv^&u24Xw YN<9MٶrMiqu [ /ާyaEVqR?a^Xf'ZO\˱>IhZ\W +zC߲E/7j- ( (w`U)\vǭjsS?6g̛ݿj~=g^#mdqYİʪh0B&櫨ӻ|s>_@nrs푍3:^މ Yxv9k#[NU>x:L\.V̫53U5¿NqGGRYsҜ_y׶G89/|zwxkϨQ[YqU:?N]5ﳘ^틏g}?9n`=⨵exל6* 9b.rRkln99y//}Z#<]GΓ=7RL؍1d|Au;e}޵mɹbɻtrE [Zqqgﰺ,6&Tzčb|/ާCoOƂy[$d1m_֚X#iHX~-tg4ek Ϭg|E_|t~5 lr 7ek.N^;Hm_q^ZDZ);!󉗲'pMnYoXk}Wx_w`?s>oFw\=brAkZ]#s*`x|!ʷp[kڟwz=by~YιvYՑS ]yq ̞< g  yar]_?v= pYpkx\}bqڔ={ۮlZu1\YK[^bhn+ ;{mO<9^oxz/ִy -blbPYOswp,M<(Z腝=X9ر<ƪ:T+wCij{ֽΦz׎lwhϣ?)Otm|^N|}#?kyhc,.[:)[5@\-ߵ1>QcND;^oro-fiwekot}X{5ya{ĆwudY[N>Y|8ΙkkW3_5];<`Bsm}G>?,gamu k;ۭN{u.A_-[}ysc2najˈ&a!p2rk([TX瓣(γ(wurݘp-=)k8X"~}gyH>.xY= E^=W=k׺+ڀbg-W=AOAOuU[1GaElbkۖߢ_k,N"?J:ϼnb:N[⎕'a1w9=8k˶ܧU6?-+\NË>k/<GSoW/Իrw{_`k=T<}(?y4Y\䜊4?aίw-G*ߥkZ'wkǗW?_Cb2"#{g{.U8Z#nmǵHk}~Z so?ܯ9;/`j0ToɭյqB>}<{p}빅Iw]޵tˣӏ~R>*o]ԕ)  m@f}~\Yh^O0-1H1ӱٱ[؜:E\$n\ZkbSKZ>'=&VY=nW~}3Yn`wuۺ{qwk]hu-prޭWޭqQb[D^sﯼu)H&מJ%Cc^{UP3&+Y}֨ !~`~H>|0Wp5o\soXk1߽xG}#GTYO@:xӾR?ÿ/[fXqs۰Y\ak9fwr;!˜NuXr{9qm=o͋WyCk6PϪyü[vƣ.ȋXӈ}yUa`M_V|ϽrU^\vWbZ,ttxk ÷2绾oz=qC-2;q[c|Czk2b.G} b}X}{+;s5|)׌&pmY wap~~_8}ӿw5bϣԶl5?6'bFb&&bolaw~hC,oXM8bD^25v5e<ȋ7'N4_ϸ窽Vg*ׯR~0n%ܰۘ;N`_0_߫Xbc%ro0ӼFkO3.E+^gzrZVlkJ[;lIa:|dۻ͜'1m}~Zc8<0 윰q0yd>KwmŶ1 ~.BL"< +ۭ\yza(~g=6qλN}oZ71[bk_|;ԝswy{70Xf_mo_k[.ѝx+wJ?bJqMһUs.H}?A +<p ϟ~sz{\,>l\,6Đ\#DO1|ߩc^|G߰Z.:ڿڽ +oݘpޟz7f;Wڇ͋U"3 g99YlWUY]vc?yvl| +׵Hr_ë)3^y~L1۽o'Nz6`8d [zrķ}udmuѹfCڰna/N|l[#xk⵶^qĩ/bݮv}+O9\7\7ܜbȭr_篟׍+sꙸf(kwgپ\KX7P>ȶv3^?=]ޒ9ٲ?k?z։Ц6 {b['qXpkRݿ<|Gp׸U~Zw׃^)zἸqZ1xk_s rA:'…Oh8E6_6J\:JZ_h?ΓX,+^|ڗ(︻_iuko핰t}!lko>*.MMy"qpri8ͳ1'ܪR|ƺ~k9|y5}G\~|r7{ޓ'C'^Nm4,/nt}sx҇|;|yčϴMw-~[asڔ!(hutq:u{^x}&Hkmr;~3g#b?m6S˻_~v۠!v+o3q ?Q{&X[W9&OCrSe5Gcl-Ik.+QʒdgH-]>e^~!?? 10 *\1-.ʣXSEhe(OgyueͻyxZ+~sg?}/.f];?ޚ}v%W/Q.X}776,x׵;00l;. 57qz([?M.+wɻZb״_85?ZmzQ}RoNٚ "ʚj5~z_c=þݻ$g[/Z=ⴷY'.q͉MW>pÇrwݻoxEk~b/Us%}9r¥G裊bמxb-/WF| 17u\'[۶WquMk}vOm~wm. j..GRI5@߮onߵ}984]݊u^xC篭Xێ6^nbE߷iV&9g--t[y_+{ݵ8[~;787=yێ+Fe]=G<˛za"rxw.6|1dHזG"ߊ1˙NΆnGq}8apx{;a|.X`yxƺsH+sԑ˛s>ɢ-R]%;_c̹7MN*}o\)*O[Z6>{a{uзV4_Syy-O}fvd׎S<;9>^~y/^8`qiΖ7 sݒύC˩Cߋ6~qL].u&R|jן#8R8b+xyI9oXT·Tsq\u7?uzևN=9M9wn>}FyU=^$;_\jZg;*g/A|>rR;u/WS:頸MC(,څNT3>>ˍ?;v޺ɅaE;'c˝\[+w-<.yc?_mOwI{֪U_ʉ&@VW7lZ}Mdmrvr)rXSnM~;ԶnlkLgkY0%4_|ǔ7#@; Pwޣ6cZe냄Ɉl_t7niK/=ާ6tMnkwn1ڭ %?1нVXl)gw\ӵAhmʕܺm93v<-/μ/>'v<_[ncD'=yswz@w<]{4,6y~lsLJC|[r}~O~OSO/7bg74Wk-x͹j_mͮ]Ls:Ng.6t㾾ҏwmã ·|uf=cbkyǟƟKXܭqsEY{AyQvo]FNȮ-O"g05wJ[ƟN'nf6ytrƯ=pWw'6&zG],~wWmywTZĶV uPʋ̗4?ʋ,7\upq׹nxa$,f}C|'7?~<9K~>`#.6imIJ1̵Sv Kl+u!;7}rxcz_\.G_R^'c=S?2;~'U7\v}/6[}͞=m,+[ㅭﮓ~> wi~)K#|k^>L͓η-I _ -ژʎ6[[ţԾ^"F۵@eMmyy9Y\ؘsi9hgVClj/(:߻m*e䜮LGp-O\8䚷+w;XbbotبG[{ʜxq=YzY#u=ftYLJi~۹>:9P.5+|Ǚ׺țczɦ- Jo bx֔o_v~OW@S>E6qCm)sNm/6x6Kl,IlWg:`cL{uU*̈]dGq^\ÚP{ 68Pt#&`]+y;/>/tqڮۻHfnέkXX7Plbs}w{5AϓF_g0OcD=]']SמoW.}"/cz9ُ騰>9kOX=<^B_yx]#~NHxrSne)_K6ǒ?en:[#Vn{NïZ =N'57@dH'&R)[~0VZ_{ꝈY̧ya r-IOTpe燔{AqrZF|ps{bo l1}ˉ3i-m]d{&YQ6[IſYNWzY>5y NVSMS.fAsk5J)(m,c]zab=/t_Wqdy{b HC.K>vk ecq)i7!߻xT ]륟|=`ml|dtL搷~Nvrؚƕ/ެ1O8ߚb:rI[ζbpǽ[^ro};j +;^_O?O?ۏq?O ?ۏ?k~}\<57 smq;/AE cׁt̕I;>ݖ'8x#tsX==HY } [-괛/b'yC%k=d~ׄXk\[<^YL.o^.r. 븍5Fؘ\|F}go{}(CobھaFc=Y߭=7hbYS2'|fxZ~_ޘXɝ{}-(Ҙ2V0y0Fh\#U)|x[c-摬Ͻgvq2m]ø2 +?⵹FXc+WFmSsn:ͮv8B}Ž;k){/8wmṽ\>svqn].ng宑:yKLng6>9b[ɴ[o3rŒN>'1Ff\|1Nm~jɍdƩA:?~yKŪóV`xs^8n`<|o~f{/^^¾WckU>I:[o|_1Z: k+beYևyZ?ƈ#3.vuK\K_NCTu?VϽ_95 kiӑwM xaɖ!odJNٸG崈ԧ?ac4lmy}k.'bԽ{N?yv~3$;G|p X~1厾^rbDrg?s6m؟߬K 7NJǑ;{?Nwpf<> ~6H.7>־p+Ɩxj6 % w[c#?W]gs}X/("#Gݸc?|L9ƫ5?kŰ%n9kk/psr[Cz݋B m \zhھs.>hMWUD̥vo=M9.%=#}-k櫶 ;5?%2㏵wr_5m R|11Md9#ݘ] <RnQG\Guwk'mB=kz$D;ȼOs̳]yb+koYwtǶf-]ݾf/J/$Oy^ԖMchUUkDnn\MKP?]rH^|&l*z$:ʼnw;ݖ|>mhq][+$XS xnܽc־חrl{zqʝ#̦9X^1l!c\7+'6vYsU|ݔƙc+Y֋Izu;+3oƂS}?1Ac?J7n8;y8oAxa Fgkx9_kz4oWo#ͻE.9\$106^ PmzkcXb?UWq#S5s7|K_UIwj>jպV?m\8T;gEF9GN}r^4Ə7_ksdϓ]9W^%5eWrp>ˋehW]^v(wgl]ۥ>˶ nNNM/T.#oS6Gz/Sƥm}6Aw8Ƭ n6O4)8ܽe7kX6cb]s r ?Z.,1/3'$NV;u0Ok)9$r2Zg; l-O ow뻸c΅[Fغ.̎W 9[|oǹo^+{6\O9\ (G&gnd?%-nxqWbsE.]R_nAYLj#LFX|h;koMM|q/;bb Kf5zb?ͼo_{ex s^x>^ܮ~E05LɧZ}5FXġɅX>zFy$\.bkMƎk%.];ͪSom?r\\0ͯ+7/_Xfk]fWO._:sή<B>^G}>_q"꜓Uq?//UnkkX,Mx`J/.@:ܾYC mkو1̛>}rWMj҉7fn\6q`Ssŵo:~^HUP7yr|?_gup3`~?yuI=[NLrӥWa]#v]AúL\Fka)dWz߳z,g0(:_޵Sr^L 929OWzv}fiS\5[٭7-+^\ܹ?W#9usgll|eS[.RӛG+ohAqύï]ݽxbrE㔥)t-8'޳1ż_b/y+ۭcҭue.q|s8su7Fk+8~k'xo\w9L{[W8QY_!p ɝ5v7'je;JNpjk];ނxZd/t˗ȏh}˓A=TzQ\kY8d9r\OBnn^,nvYX6GKo/%i?|߶ڼ}%S-b&=bS_cZw}h' QY<䰴Km>ʾ{tprkWob(}gGΚiKyڶq *-z}q( hg/.ta|ZΜ~eo811Wn==(yĞ=u# <\Я:k/|xuP^lxZjZ/q8}6V|sL;qux0'*w0cܜx1ӓ򜛫ʭ.8p^kbi|H n׍䎙+x"~ y =,MOx}25g]+w߲~bPo._{s~ opɍ3%ڳ8k,oX0+c4ަf.Oi_]_0\+>}'vc˷M.Gߘ[N3=!#nl)ȷvNo%ty?qS}rү׆l1/Cb%l1-/~a]oO_;6emp?EO66zM|YNOW5;^Hkܖn5i0NSC= o4m)9?>k4fm%ch_.OOΨ{s~7-xj x趿+?)_z8Y кɖd٦g=3/8_[9a|‧ӏw8`yķ/'︀qؿ+/<~K&(MX؞uK_3&wDy̮{szٮ#hdok,Z<鳈Q}ؘqImd}ϵrϑ|Z1@?wgjD>[} 8vk_LT%S}9 cz}١5}0^C++'|q- lǞ|^9 S66`6r?, +G}^;0tחHZ\]'>堵_{=?,>2wCKͼrΛm}Si^Pǧ Vϥ,Nؾ\wܛ-g~oC;7^h9>EOv[F/lۜ`r[ւZgvjSzu_|O77sy^aGaFKIF=׺C8p5[\6qx^{awao|Խc['WMK-]M h/?k3:3be}I|K~7Jn!?N<#ٸr/WV&?1ky +bM }=*>)~*7@Ob~Zy7X1Y|6}xci99_۽yS\aŽӝwqφ_2wΜs_[kmѵ7.8Y|\]ܿZN]}%۲sqϗ|Znb|ʬV}k}QgwW߫k{\qQd_7WNWŤ&G8`[9uCv];~\ k_n7:׫Yt%?3__湙',2a7;ஜ9yi_[k5.>C}+}/T{T1dN-o(^\čo5?ޏb/5dο3uuʗldhljm`_ծ͡X}'ruܯ={qٜƀlW-1j9渽j1)(94V&l5%k9zaC]YqW.|)e@3 '22$J^9qﬓP<9|o[K[r?Y'ʙ[./Tob1߮EwN4z9_bxrqq{;7|еY~`;:!"]_]\<1|0t5ˋ!dvɰ'Y??{0n}͓hIT~秷gsx}X]>:_7x]!溜?ׄ^>ɕZ~W3b ޯkܶl-oImͳk,#k{nn>pkw\>9L&;F}c|2Eg؟r~wb!XWch,fe9LVcjWj-dquy(b~/~W`渜=P,m)w5W?Gv@XM1N~^j?o.&@;{r xr3}-3>ʯٕSƁOYo=;8~jﱹ8Id|s~q(ǯNGp#?#_} [悹)N4ݳd?2aN\r=6?9!>յϝO}ho|ɿk'nhQ__:rz/ ʇ\GIz3lvc{1ows.ִ<կZ~q@V_/nr壉;/]SgQ].܏l{Pmk}?jl;?^Ψ}}n8&|oyǖ-%9y7D]ݼNo|Kv-XF[PKec"G +\+iyl]sP*맭vc>o:& 0N/ݘ w3?ˣ_[>w;;&_,9s?;u;{>K2ۼx{y嵫+{/ၭW>8k.׾]S8ǜޭ :;F ]Xq,'nzĭz6[œcq'![e2O~_׊a0Z7S/8Fwbֳ8r뒙r9M}z/=O!}<7v|"un,̷Ek/`ɚ+LWķ#"A[_}w{mW\ؐXXMK55MY&}w;'WTCm'qyj ;߷Ϻvi;kkgzwbڱr(՞kUصǧ nm캭o^ݝ_^@_:BZK3 q~Ǻ uxk6X>2}+݊i𝓍'3rF|_ݹTz'mIeA}={'Ow\ 맕rz֩ ߳_k[vZDzvAZ#]rk}lrd37:C䷈M)cҿ|?+.2&V{#;ݘh3W/dM1RZ$o=C}ۼOX_<)kd? W/5|ո[>uң%GԳ/r6\h|w>-mj]36rZ6EGō#W̗͢z(_FMgs^PiMYF<|Ūf3ԾN\sŬٗǭhcݖ= 'ͺN񪭧P7RO`ؑz_;>ڔOn[޹v[='8r}}O4 z'k+X\6^`܁v{{>% O6)#1Gzb)f nLhȭ\uBSڳaw_kgڇFk?SvOū,g9~{Dzk{<?7Ύ+6rpW΀_kc5V]F1[K~4Ƙ}㬝szou6qIPq9qn_c@>dg ƠߥOe57ʼnkKk\kKkZ|ycSd{yDErU'O0L;Ln69Z^b"J?g;Ӌ^{nG_[z#k~|km4jÉ_n wltY|~{ڋƲʼn`[A\Rnz\chy;/άTLrx|^Ǧ}ζϳ]ƿ_cSZ+ o> w~Yyq cglW>6~.'%l#r7=/uǩ2~wO^~zv TǺE[ů|;ߋ3c눕8|٩FXkpc^~w}\ח_Rꅧ:^7R_8r-__ XHpݎw;l9 y;; 0.}snow['0ێ x۵).`s5ʭyu.9 cJYj WOrp?}d9C k>3) NN7o\pgoݯϑjޑ؞YvmyKWkW/H񿗏TI< Kxe^]!}j[뀬#>w&ear=6?7ƍ{ӯ}ޟysţ.t/3qn91on[6ɈMܶd\\nLFO_\GcS!ڭK(a~OΒ^1ִtrٝœXG6`ơ 5#y~Eޝ6}XAs)X..ZerLz~'-N֚5g948jv\pmxxE|')},lMl;]-m /WlN95=q9'1r2/kٮY?|".zPv丯qPMֵcF;|8_%m㭩zm;}5>jvX|uqӮ_)>#xMƊ3,~`2c3kr 8PKY#X^M.asOIJIߦr7K,C\'vvL+v/9ϗ-X4_ccCɡ-7w$m~XƥǬ^OO}l0{qOOgH`gf5> kffrV\Ɖu7xǴur6\^msIo*{?+sh%o-^j9'l eӞMz<<99 ;\~[W[ǻa.y͙lv=z^M~Լ(ל/ɇ^ +9]X[{QމazS,ޜq'ɡ됇ͿL7؟ȭ^LSm<񪇶z0OD5w;&w?#XoW]ňk[9mm+[{~4%wg5/_\q]{_%H/6cƸқߞϳyʾ/ǔ~gcwݸN6m93ӵp-XW/aY5;W6ͼpk$Vry3e~EX_nbkG0.DsK^8|iŏ;VN7}/[u\O6nNkY+x~kY+Pmq^b|qr^5 W^-k^{nc-֧H]kec%-Кp?"bͫԹUD2"[oocXgk7,V-ifLb9m']]_zElܮgߧxkO%' ;#^gs9R(_>lMMܘdFv8}y~_\~ּ;`]_;  \81)?C|`|ëKroͰ6lsbv|r,߮>\O~Ơql ˩NZ8uȫ-`=kLϳ5ċ;7&c)ސ xqPK6?ե5˭7XzYϚs,ݘozv#[^\~'߷+`@mc}!~/bML`v`w<7cIʎ3w}Ȟ1Xw{峾rǻV\g'^n~ihcJN1r5S{o+, r^ģb+oQ^%ۊkZumYqGi8~5^_ؿx]} ╕+G0}1rm_Ǿr/X&|CIuuSҝqS=l]ܲ|0Bq9xo׎I/F(l6v0γ\v_吸_y\+C^okmؤ2U7vn{񳛏Ʒ}GY_(=Qji/M+"GQ]$Ko@מּW?c_^^;7zĹ/Hot9kx8ۮ-;6B+TmZS9{/r6Xɟy=CI4ϭ,lϺ?a歽rN~s%w>Sl]{ZޝX攉nވF_ 8c OciҶw~X^S\XD{'#[EHr0\#sz.׎? +d룊q 9luͱ|qj;W>[Cs;́ο5COq뛞sCs o~z;^lw}NhqQ'?K&/kSMQ Ο?"Vo[#M\EpuUk˃}}X\}q$yJpSk\(@ξ:;&3/wkl=jMU\~ /:XQ܆b q%ߛ/KrW9[GV Olc.3.U{^ՙ{6vzq5گ~CLZV9{5Kt~Vs|Z_浽\!WlCИY~x53KV%mK-Ewo}kq%q*}d|WT;_n/e\pǻ9闓Gdz>zW䮭]\^]N%Vm>r8}Xn`gu{}ֵ,xnyѷ~6qʼnk8[.[?7׸Xt]|lB^y|ws lq:푧Msr}bW;o.U88#;b_Yy)Ak{kkl؎aZ|l4zlGms?W.$[mGZ<\Y#v^foխsw|<:=<,xغR>vhX1#pbsۼٚ}@r$ +`}?$t x̭[zj^ԧ=ez[8qݰaxR}MQ .6rr'NYݖo_\Bcz=W֋e􍇷9BA  |_ma`=Cz6Ex9Lu6ύSj)9f=<Ev:UszZI>+'a'\zc[:ށOz>幫| +7 +{C6B>/o6,5{^Kވ&q".mBv{lTmQ,Fc>MmKjQշڒa.H?LC^9q0dۘ7y^_ 4qeDHAA(Pc³MNʌ?̎_3?]ݍު]a{_jno$6թk]t>76RmE1uԾ%+wZ>j󒵿˽m_m眈XVP U赽rF[ r"%o71%۵5qqm|Z֚Zb:$5>c NWFƕ2yv9|K //T6xϩ>M gk'k{7g׶߹5lj ۡcWnlS5Z/w QJ3>)Jd?q?V/stIr?}-Wv}i_6_qDbUk9ad\OvҶ\22Pbe8j2W rZ+]r Xe2IQZ{&XԪ[uƌkE]>o} ZPcb͑|縹6w_}b*+\e֍I-W 5cWoQdy_^mgl>@/`u1W0P}v:cw <䃴n_y4sǹb[S 3?rrڅukR.ܘf_'Wk&/Z~5J 5g{;Ư8`:Kw| ǽo-˿o|pxw1x˱YHgv:jKh~Ξ6d a.6y _z[^`۫=l[/[:cx6?6s.9[TmcqjӐ! ^>1 ;Jo1_:Be'1ɣ>mW:^ܖܳqk6V6,Y!wmY4}ml{|oyL46Sj5˕jkdq2Nq90ljys=޼h76?Gׯ,'w:/56|Nj^oǮUN/&K}KӍ3;:|{[ oO~1PxQZA|w]rmLS/@ﻞblfxsqslS҆\z_>kNyy9$L;诺cڧZdrޛtZ%[G-k5/~n91ι%QnsieBܳ=YR;ڍw.'ssGrWL\1-w~=%{ԇ{᫝Y][ؗg:j?ԏrŴ!r|;8Re.Bjo2YXvc&O6?3V~q$YI߶ yro^iY{re+2`~w1>W.>kzfS-,`qwT/Z:o5z=ónO],eƈ.sߩ-ެ_(Sgkm}#Iz{2lNٛLst~z~_Ki회ȶvpj̯|~|@2]+Enx9ȇ_2_4k77`ԕӍi9{r@y2y{?P>hO*y}//q~.[y=Va }_i܅s8-Mm:~_a[z=ePoEӇgt1/4-ƌ6${/[k\pu6gǍ8CoX޶}}v5D]îSq}[ԏ6dۋ5[[\V|xBo_?bK[?عZ[g=YXubBxw +jY1MJck.bgZ[xq׸:_3G`~]cZ;wruB 6oASfjs9=u/,w!vZ3&>%;ն㽮_֮_m뗕չ_w]Wkw_r9s-WS\Wο^HiNw?ӷc/q_׿c +6+jBdq%%e9{(ƓϹZyvl76y쓯6 ov^j V۬MݸҞjMz]go,Hh,Y_~6fT>h{c^luA{6a]yyxta~ƞٜte. u_uL/}YuB=b']u{-={Z}t"Q:#ۧXw\}>ǽY<Ɓ/c^>={>ߠu:k-߽ޱ*k +o\X6O\m|¶n֗]kc2Ƹ792=VǷo}O]Z?+}ʖQq;p&1_F__i8NkSn,+[3B}S\Z}Tۮ ziəF޴~t0N}T皋ֻKcZeiWؖڻY%5ƏY]nQumX|jn뷌;|2e/j݌wMbK7b{56>#cBmN1ɼk+F0^wS7Q|;˫qt؈Ik47p?Ķ[K]ju +j;--Sۑ6i;KOgv)Xh@^\ϱXcVmlpYhSurq[˹34x2oݎ뵝8n<&x۽?x;ߝw|MC<W=/d,e&LJҍu923VX\/]MilNZeGGz;':ګx^׋PF;NvS9sԁFrgd8nWe&۽k>Dj]9IΞ/::u roQG7G9f:?/Xg/څ4o^[K_&.;#ynж}2/ wl߸jah,_LդY_ZݴpZc!oUlO'+voڂ#Wo}Ge/sX j+6UƵeَy_%lC>n1^]1(inwblK}?WϹk2~dJu{[dGQWwKZm|z@y`/v種SRZ{ꙵB~ͿXuBc7LJڿbH=ݾ;/Ww6z,5Hotɲ7ajm8X_q햯LX cl^2Մ ]lYvuXڱ]v`ZbXrv;x;wnyw L&Wb=kxC4ax՚T={spUmNWѶ9[+"SI_qEc'l;/gw|]O;u=#nƱh#e+ԷU?iW#OS_ﱏUg-2P=Cc6lyKye/@Xr֧c{ 63nX9![ֽQ6^Z55pRO8`9PLN'MٞU_{:UlzİKok7g^[pqO?mEj|.{Oh nvw9o?_1:Oo/ٜ?gϿ(xkxNy)ݥst LeN{qǽq~gml빿4W}k(~X جXjX\qڱ9`'m#'&+f9Oֿd=kJKm+9 uiT۩m}m c͛v^ڏڑrJYN֮{/>_v9w-c1]:ի2Lx-Z^ir}mrZUl9dCczԃ}4Yg褛o}*ܺrV5=ײveF{UnLksu_n,-ywnG\\W0]=~W߷lw }Vq{߻qu{iݳlxsnX{cl)'N;4/չӲ+߱v{g[byoX_`ߩ/?^hnXM▏sU=OXno\<2p]2eI zW5w|u}_sj5g}:*woVZkW^A _>=PW_ߛߙ-Z(rZ5vin;׹MǞ1o|S725{>tZjۿx45 }\$ظڽtw]LLOwUw׏idѸ-3ۋU7W].֯9׷l۪LۖowQޘ<gz :rzwWOOU/?;[;~wlt}*;h>v +n>_zsLmCט=UlIk;oSy~_ {Aǐs6jӢy۹Bۗ@~{yd+-FjZ& GZ{v3֖nV ڠWJ,T{62#d<{ڹM +~%VϳCV`:`̓S>vu/KhecF[UWOƪgjYXqv߹[.>Pftۍɱ[~ Z-Ap!6yJ-\7n>]ui59g 1W|V= |` OWK_S]}F6|_9pkWk۵9.?oS S^{T:?۲և8{|ڃ{/cTOx}{'jϴ]?]y=m}YEIȧ {Qk+V֞k}Y96W,,-ScSt^Bmw/j خ:؂^ǎƭcʥO_<бcme-O}fesu\1ecaM6u=U[ַݸmۋcw;!ژ^LchuM9^l;݋YVo h?y@9 {[ϨtHLOXi{o~cz1_<tNx}o?Ok+Q鬗e[Ur9"i?uێY ߹y^[7w$ݍSW1?ޜ"ƢlljeBIIv }f,I[o^zW]]C1F;/*$_NxڟY?/:gǭXGD[\}t.dz,_MeaɑVڛO/M2,y]˻>i}[O*Sz5dbaK76zvڔ:WV^ҿ\zS)Ubꌶ35@[~|#ۿ/{:Q>ılqss0w3;{Erm8c~ŮY\nqu~x#m@_]s/zmAl[zWt_ދ]8;7U~͏flYҾW׷sz֯PϷqZV@݋sߵjWs٩ϵn練}V/n;6wmL6~o˽{Fw{@kZ{'||ۇkSbY|l[_ܭ,CjO]ІlH~a\}sOz݃~29xWTs/eԱ1$^kq1a4f!qYZ7rߍOo[\2O~|Xn|i\U׸nRH\k]^}lauE*䂫|鑬[c79+Ud;Uu=[^1v;^;o<زk`9%!sp;w_.v|N߸]|mc}jt}8­ ,f8w'<&y tz^n|zji6q6ߢv6%&^}E};oĹ vV;vr +jg͵7^^ޘNQVY{3yr|е/^;w6q7n9kxvyݫheO>eΫzguO~r썣3}_hZcWﻼOc]XVb>ۋ/T[w[WϲkޫӶ+fU{x'#kp?O-UsޜSjm d+^TMYyLw RϱTosM_R>r^1w∍ݽ_co5@svbcY{=5&Im1/}k6+FRk^YJ}ߐiee#gmЗ6nپѱ> 6ǹWLo ;q^LP\__W]鯾0q,0W!>?OGK}d}Be/,1j?52y~,C=1s!L{1|RstYƲ+[c>n:Yߧƫ?Xm_??׶6cy/q1tԋVe!֓lϹ8-v|mq_ͯO]_S^*6GkңG8 EvM=X3tE̮{.76%O:vZ{kkW}~?o״ O"!rcn.5x|f@]j^`+W-u~;wkc܏U9O䝝Dmb6mVl3z/zOLP R{[k2OlB~YS&eif[-CX\rH96crׯE[;ryv>7ƞ8mnhG]nlr=ޖ^ec-כK>ֲ^^ mݾa㎍}:k`}Eyk%m?/޸|ӲZMVo[7ݺ}t\6[8G5N7fqv&Suqڞ ڪg^f5gkk\DE&/u+lܰkv]-kA|I}iuoމ~gaZ޲ k#ѷyzo+l,F;b5ڮ/)2R;sȥ6O+twף~>Lxc'mq0^ɿV?'?֧;_&ua!wXS-Y]Z~ՇeieU1WgX=SpP/'iW%~S4ʪdMyzT˩6U H~͗?|N__߶qt^sU7\{O\n^Yf_n]۾ [bzmml~/t.eǰ*n 7~2>ی6_1_񐻯K e/&o5r99eoktzK tdIݖ?g˽XK^c6F8X4TSoݽ0mt2m9Wm|)f1vqj}im+mYf~FY;]?,4[SbZϗm<8ޯ{ĭ|~خ^׶24m=V[95q˦5} 6ٕ|\A5[篸np/Mvbcmx~vNvۭ;3kPw+g:2,wTk8Lc~nVh? +#z1~Rϲڒѫr;%PW>\,jb,scz> ;oϱ\|n[Ont;級;[&R_TGRh5ѝkG^}W5־42c=Ez]2^v.Ϭ_Z#&j _~z;?g4,ޡҸhW[};zc c`"cWyZ/΋K6c~Qɘ]sd\3^K=ۏ56757/uuZ9see.#^8V3v~W,:F(~͋b1:>oj }۶㮼y5wqu9kԿ~}59^]ztu՟kVGkf3ޯO8=_ z±{m{2{36X=`1§Lx,{}O-dK'mو朩/l~kڜzO3̷Yf֯uc­q_26nT,tuw~+nw"d{'N~!;Y]Vfޟv<- YrKɺ'Wx!筫UPXte_2T5ireP銙:]BcaVdne9O֙˻->oH%2'ԃTW~_wcǭ8W=^ Z7^suOȸKn1l*{5UWNùgܖNPM6?,pO`Uzw}^lv|1ww9ḃw[iy]m&c.xR]odkj_5Z?8'c7M-PkWXr:W?mw _M_<~n )TQ%S6v6kSVy^Qk7gGԽCʮd[1u61mzwj׷]ߠW~a?-s6 2Z(Ʃvl=ӟ{]}~tCҙpKXƭ]~ե&J^Qn9#./^ۣy斎no룴9ro,jƩlcs:Ɲƪmua",̓&73bm Wې +3|jG?^q}Gԝ^&(_5lK7տf^}mTZz_(V4?-"i[bNx]ṅSNvyi-C7.^ 7Q9bz~c:%6^ +ٳ]Gs?WXuT/n&'enkX8خM,c<2u(O,\nbT`䲽i}NQ\2eM>y{O6oErmYe̬QXXc9՛VҺ&[ظ4nrnx-y^M޻zƭ/iie]l=]Fr4]furgi6yn^w"n[mbU|}Ol]-w=W%WXv7~~}*{2RsĢҫcTQ~UZ[X$}#)櫨[M,^\5l|R됝u_e/.J&Eu&dJ1=ΒduBs\ѷ^qvݳߟs2ưzK[hOc]N|lYW[&lB`Q&=H[?|?1 Wc9'6sTۚ36]_Wb5vcm\|u ȁl</=}Zί6yoX9ލl{WOݚ[:4v6qc|\8뱽{tx[[WsܹS]Tݴs%qGc}(O\KҜ_5W i&\˶Zɝm!NZ~iCS˳q3ͮQ͞suzԧ~I+q2c{_pL:Շx86gG9|d=M?;Ghk.%>^-iV֭WXd=i6z_r͏%MQWGa^jٳ~}lߩ'{i^n7p9\Kܸ1x.$GUv.ƻI={}YOvnϜ'_1nkNȜKֹ|6xX /̦(~(?7_}gSl|}M]i>V5+Fjkawm1|5Gc +זro7b4tK/*hNkQo,Ij.W5l]o~By֞Vy ꪌEY}d,2ծoCg֖6KoQwמswUv*NQcZus2Q5~$!\\ӫ?z筬{."oyRVQ;7\}b$1E}~?:Ʈ-CsVrSk%?]z1oׇ_YN½^ܽʶ5/WwxԸS be\Yn$[hw;qBԲE?szX>?~Omb{6kk_\}>s[gf}Wo}y uum~c >޳cӹwZ1>1ێ^|m%\|inbvNzf,A-i]5[br'1'>~`Zlzcβ;[qs-nZ%B^\}λ'tkm̢>+yYyq=w2o7u/O|?լ6ieO: +ΥgV%W~[ܩN ~]on,o~6̖Nw5X.k [wS޵eR2Lr??tO \iTu}_%7&>gj ݧ%px]A:ٗFQm%n}ߩMN;v{>2΋]YZ5⫃6lĔڵ28b[jm_/N>OݚFvo[ޥGsɞuZɕg9Υ}5ޤ{.wbsmOz#Fm d8m2/vIy*ۢo1 a,H }-cǙWy:`djuen " dZWwZY|8}+8`z{-`ܯ -=`k}fuč^1=lY|^+kxi 7Z_c>s-l''TSYݭL7z~vlxi3ewbX\szSɺbο5N)_j6ui\vO;7Ne?wa?}6(Uu~9ZyGjn7V&|vg4.?DkOlpe|ϣXg\b[uyOfl{zHd[Vi֫ZfG[ڵD'B}W>ڸS x>46b16Q)i/G-}%m>~^,P%d~ebΫ浬~[{ݹRٜS۷g޲cO<թ}k66'eiۮͥӖz٦ecЪ'acd24._|]ҲMVpp]UXoY>eM˶ɯWqlbFlLA!Sk W-x-_*iUTWyu_};}xBǩ] y}Vu%޹|:N/!nu6N|u[]bK[ҮDl+(>xk7H[qmr_-౹bmi[?A\zl7xǘ6^w-ݝml0?{<`n._TGYݵ ObD>B^ڭ~ j^Z;߯oƿ~eud:ߖUag+"7ĒVSm_ӫe~:޿VV;v緤Sms&gj]A=߽`cn}W,֕yjyۋy:z/_~1sV_zu\}+V[|5iun5["/ǰ8ueqGhSU֯\v=Cuv)vHҼ7ʦjt_qN? +lr]?gAGG(CR(ޮw;އnŲ12#M?__+_____ڗ^~////>~~_}|?W_}_|~~[o}箽ǻ/~}^{o//_|{3wwϾ/^=y}w,?__ooͿ޿_Og?~?|==~N./?^Gw~޿蹟蜽ϻ|}wO׽Vy^s;*{s{߼{QywOeӵY{^?[^yyVU_޿e-V.ցu5^6rmg6uն=mLmqs5+{n[r|ֽ_rN}}}=^_v}OwQk7u]wu>畷I߱ۆ+oƲhۿUmWW7V?'?^9^]\6ƐƓ{;wk~cYqk*_6l'X][}apǕIwm}::OW{O쏶klom`o}.Ƕ;vjck~SC/)Txw{x67Z`w{??wwgg/?3?~ꧾO~ۗ[~˗[뗟ɟ=7Ư=/??z7߼ /|lN;wL`ͯzWngQ[]gk=xwԶlWW\=ԧq}9|sվk'WgG7}w|ugkᶕ˳I;}ߜ;'|wەtw;]=}sw}3~[=v\}׫^2αeZyk#X|xQ}eWks/=+ǻk%&ǵ^v_5-Flѿ^}֦ߵVowaukQ?y;o{|-{O^*O{ALDq//;&{zק\_y;Gv qƻo |/[9أ տzF?hs[˝/q߸wyBLC\/k;wϕ/|-o ;{k111W_S_oek1ƒ{lo[~vl:)Q{q.ߵ_6(Gy߉Gy//spcŸ3˟˟sGq{4G޽vzrd9eiM{m0.C> Ɲe!)2a߱YI}ܮhk~;w-q&^c<ұ7[}¯v\ؾ~9I6|6~{.{47n=l;;~Wwexq^౿c~ ~?ʉ~)v.Z6]wuNw~ޗcmGW6^ܵ\vs;#cvw8Q%oxcE ټ=ݡ%sݴڎ.E,'qה,O~1<-\Ҳ>Ϸ5,;&z]owk-m߭~k>ƭ7ٚ/oyߏV]^Qp> y=>]s6+w_{]8tr?mNuv~?Nem]'#w[ǵ}mo{]n?_=f]f!kd5k^1Я^b{wk׍ىId8֚s]ÓxW,0ڿ ޜ+߱O3d\aw+j{^~ez9R%ͧb5'_.?ssC5䉮%o5\=Qu8q~^ڿ{~bˠZ勎QI] bym}~ǂ+Κ+g}ԒU_bw{8};wOڝ |[Sdwg'2>}Nmۚ]::W׼}5g3jtmf;ՎiCjc'ɨԉfDiy*oWgq]޷,jd:rCI2 pw-nך\/wSLsRs&YY+rզ6W{_]_:WxoνV&˓T.)ƧY_aP&,>!-c2^j^پF?Q&W\GNP|:c&\4}nX8vv>ۼu,je/v/G|i _綿\yq ‏^ח ٯ7>cd߯>a}3x2"egO:+>vOߣ#ٶk=oXךӮ,^ݛcM>mesR5$T>YzQ}?]G C_』g~T?5;hzxX\'+~gǒ\_ҶdݱOoyv]hk,߾9m߬O1x}HzuOHny,Q>rq=?r>w^wu}Ƥeڋ\6u߱s+q~uxuP~{w{ntOGHK-C۾>cw>g[ٽ jơ:7,Gt;6m=>=#N;-l~*r@, csrscpUWݿdc?ٵǵl>d[]p]>&1&Ϩg۲"7&vj^}.rߘ:7eև\kchYvi 偖{6un]z|,.ru~:o*gc#㯯1s4q}9YmSNK*Wg#ڳW>qѰߚthSXʹbŭ[ӹ{ݻnbNV9Ηm1~2iqsI&M2MN1?W/p?H|xݿ_ #꛺Oo6.<4掳/ןߵ=o5qvkqJ0}~bۍ~VsdйKywG߽>eL>?Jv/e}ncy_zl<&<O͞G۱ۘuD/m:W?Q̗1r1?9_{Ŧ[_9?GwW@e52e9rr zdk~ņeA|g:cg+)@[ˢ~dYy,gڑ C_맯:rYꖗױYs~w\ڼA5eg9xZ+P3$5=9l}o8.w x`y8〧lOw7o&*W{ūfٺ۹>ӘSk}g{Ü/ ܜ{l; iV&?3.PG6sN=<^I%LiYvKbqc|Z9]'qMavˀV3Fŗݯ[AѭL>Y,q`%eVˍ캴qXmk6ej)go^:xj]'dus[^erWN{ߵ6[|W2v5r?ۙA|uosvWckk6/tzq?3?GXaUZVK'h}9$_pm-w8R:rv^ej,/on߳=jt;X`qD&RKzhjq][`e9]T7Gw^Z{ΚWXP]:'usej^Uʖ׷xAu sud;OYN_5&(;4Aԗ8{r}v_AJj!@+o9,{ںG38oVK2>,J}+Uyceu~N/ߠ4}X_cxOc|/o}8W'זwNʹkӖŗZ]GTwڂ3)Ǩ_'Fhsl5Ywjc5UgV9Rm{n#+ځܧ7;lۥLXne}Ȁwm_w|u^=j67޽ȩVk>w-C|=qC5}M?\q f͟HsM-i7Rwds?;w9x1&`u%Qo{]<7+v$=xc8nzX׌5gwoy},7f7nF5#ss^ձ /ǜݬ*|΃8P1Cqޓt_ruOuUkcDu[j^Qs8lL)2>m5OX\WjF=6.ɕw]b?zxj^2=4qe}{oׯu%b$WV{ɯN3RM ilot qvq\F[jk] wW,j~9>ݖU|yO3e]+&؜UMקϾqySk>0]s]Ob{Ok]sV&1ڵ8Z%9f~Sss*'ck Lד&0>)r'cL30`s0ccf\_PO\{-g˖_rl?rvq}1e2][i!Ƴ: ʰٹaʶ|G^eZ[ܽ*ޗMg :^0Ɨk+<;ƴnkjĖ.0i˽zA^}޽7bڗu;w~ǽn|qqϏ-U䙍ꓭ_u/_@ُU&[N~sg䫡eZ} c|OPQ.7ܝ=2fȑ[}Ձ_{iM倕k}!o/u|_6b9}Ճʛe/뜖GjqW]ޑ}Y;\~cs'Ƀԙl;n̸9e1~wPWN|Ow=x}8Emۼ6صW+S~B]9^>X}-ms]^,s:u~X_rK2՞4ul'Ug\iz1Mž=>vڱLmpSs,Go4w\r։OVǷ늉\kW>_, +.ύ}y}3ŧd>xHq7qc}7e[O?i6? Vߏߴ;fpëdw~X}qi #|>iOxϯOC]vi[ٕG~%c"k>Mjάk9֮vC/<|v(7i sSKm]ykm ٻlBm߫mFYjx~O3&S]Ǒ]_$fp%;/7Ǎpkcs˹޽z: udj_;VUoݸsgm_ڿclr?xyF]^M_j3['s\F$ӱ~=*w i7wdדɈ[,rOɫ:/\jcSymRz>׹Nw˘uޓe7&~[o?j?12ǟyǗu^ /AB| SkkY\#*hqVK>ZƲ₋=;[.ާr|90~Ww^1W]#ݨڵ*jdVxeǪ;' ^J[r:ψch\r.qxZ^7_f{^O'*ѹb1CY`0Wb7\_)W.9ٖMa*ٚn>G.}}p=_}c)Nrfs|qLrտsaʜEqzFg.ދUǾc~81tnc~GQ u}Yu!^[+5&s4ٖwTVz2 *˘_牫w NCs_=]<}V}mc}1 ._Zlr"}ëhhW2S~<̲x~t_9X[]QqChSlME_~\A6[]^z-rarϫ_˾k7˞cqq_h* +[K=eʾqqzX>YkZc7qn O9\qo-Xc} ^!_p|i;NW qW5׍0VS:c;cmmTNo;\պSkW?QZKcF&O:}2]s15ż~}̏ ]Վ.\'W#9_סj,؜9\K4Wҳsܘqn+Uefg깊}\ffN/or%Vy]/?C߲>&QLyDVXn8>qmȝ{_؉t}Ky/\\8O`̻5Ovet5mq12-37_q ㊕K{Ĵp݊H;pa%7k\e~)iYc-&wKC͕5ルA'{5޹Z3i<5/}a1_ײdQ:^7n?J޴}tX/q^*c}ZcGViݪXVc<~p1Wh">n쳮)/'ԗc>uwHwG}ZdyKcm[^1E>wuǎ/!|br/{ S=^W.#UaBckˈ],R >HM]|Q|]/^&]ַ~jem/R{z}o|*:VGQܝR?W]>=dٛi3Ful:}gIb,Ƽn*^cص[ZLjz~<]<@bw}A;t[y˹k͝׹Ƶo{auںpGwƂN]K\5LrUu)49eևu\8Mc0o~G9׮Ƥy7tˌꇿ/c"67ۖ;'6߹1gq@s㸼! G6{Sg]~K-K|]K'lQfh R?u8KZchqmkwOzLA׫F9*XZi?拑uuܡ.LO{ܑΧCY,uIfW2`Ƅo.75W>zhb;/;Y^/_1k]6*lwҽth,I5>/_jr~XˍJi~+/5y{bwm#]:i_u=+[w.,^ܘdm]fk]k/+~ɈWϢaUl"0>cr^[ɮ6.?Y[2ܯ|מv65BC&!ͨ;kAmÍ;\.kp黊ߨoH&mȜg_ZvtyX&Z&ӫG|qpW݌IJ-Km H1F*GgJ󐯤&XK]pbq/o_~|c/u mT[޵'NYQGUlA kyG8,' iL4g4ZD\?wgWKv tSb?ԚjeSmrG>o|玡|sP/^dT򯩱lϬFާkc4W*Pc֦{{ B7׷WrmHٝbıTcO\|qxyWK+ǟLgdYruUڄj97q 7\c9>k_||G*5=?ow'.ɢXbsߏt"zL%y3V9ڼ|+bk{SZ~b?wJ喝7YW=juW3%3\Cr-g3VH^xǕON+gx=Vo8xu1xs6^m ;_1Z@ycW"koղ|cί_eS pب{K:߲y\5&~ΥyC9ۊ OZD=;?1h%۴6?1.Mc1fQMq#R#k^yKmXxqaki +er3A[_Cuc7)}޾ so[֗a/m>vyެ^ؘ,?ܸmz7WƧV9\Ǎ+_h s9>{_iO$󻣼~q@xk _'~'rcb[6и^M%'T縶>9mWLp_9SjdwdN>kh,~y|Zl^ǧ5c,_13~ix7zO>a<:7_ex}v5ogczAyxo{_ל>q{y w%8e6wu~{^j?ٳ|"AVAէk +2׮Ŝ2[C֬8[_^a욋/Kv>j3QWo7V}R_%o!,=~>WrY{z1>Su.glc{sdnɪ|N͟^~lGm,jx]PZ{1#]]Z!B;zYٜu+Fh 1c5hN_]䏦5߻{6la37E<#z5jsdWUӷ,.|~UsVͽ55jw]Uc7eɖ]{׸>r*Wۤ,CU}>1Ǵ9; jOSW? 7/{Vڷ95ȽVԶ;gK0s߼fۦl4@=Y2)GjZTS7Om)YM2{=\u{Z}jE9tn~_˗oreE~+{$2?ݿLSVwj^|pfm5跼lB_K/e,um@vМFq3K%u϶m+]wի賅K|G8Vr-'<X +=ZΕjǕᛓØ`yZ XM_3q}6"$ԧݜmҰ]e[fv_{pγ6Vq}Xd0=n|gW[9rM cbs'pz-ۺ ruZS璷,T+w^m?[Q9]اxeoM_';6 }iv,k8V8>c^ z!Z~qPfdy]\}_)`}ڧ/S'+Krdh~oֽJ^^>~_ wqOVW ƢkʡX89S5u;Oӵh/̓ybB&{#w +:ٯu]ܦgA1>vV{~v\爵ڨ(43>2 1Y*NWnU*ud+ >w4;˯SC&[Whz5cW!Wecw'vZv5wH'_'cfMzz6~}~U#ڭ6׸ns:ٵ)];r~j,y۵%zHBknҹQf\:}}JzCr\~*;-/ `:Q]ݚsr)- f.*ے81vMCgVu3͏ص;+Nq*&?7~1?|c4AZ0~Dc\뗽n+ml+fxs'Uh6sldڄjLgZlo~~d/'׋Ϫ32;a[W{VŮiY ]wyr.$mG8sj5,y̓_2;W߲՛z]ͦ}9RߚJm'$ݤjX6mv͡OV(31koq&i}]5He/xɯ6+݋qẂݜw$q+㦳׸erݪ7nk-_z1ΪŕMGa_+ZK]u-2ka5u^bmjjz޻ٺ}C"<^zmo`= <xqJw 1獙s΍V:L8cv ];~m22yɷα3fpW6쵧?8__Kú2Z-a^kOiL[|6?j.քԤQ ._m,Ǯ#/.KϿ#a.AuR>l/8?ot? ѓMVvWegk8.Hg,G~Ys9D{z.~r.>nc_-MkQ{۟g]-gwܚFaa\lǘӧUR.kiqqJ8 vnlo~,dW7vR \ؓNe}$i><:?{(5-ΖU{`U[Vxl8cv~sX=wusOr~4ߨ"{QΙMJwntN.wVoڞe,L||_yXVdg{dvLcGϘ^յWsqj& tO쩸?sJ-[U/3?( Dm2-ܥƋޑo1J̮El Xg9=>%c6V'ۇL.z9Z]s6؜Xqmݫ@M:slQ152Ԯ xůVg #xcv6YZMr9ꨜ_W3:ka׮7FZrjk2u!jo|i;i;~: @ݸ^PG]#u2۫}lZ9QƋ^>,_ݫ6`QY6.t5/{\ݗKPͧ|Ogi6{2pd5>x9㽺6`XX|_Wn=ݹsżOE}_.Xl=$z5wEץ0ٟoa+qeӺK&ٍy.{K\|yay5Ui\d4NppߍG/# oXFҚ6VXZظ'Uj/ll'޾%F\ݖs}ܸpFo5c'D"g7Fќ՝C^횟7v}yƦʵW7Y\<_Gy_q[?XmKTss1cDTua1eulNV\V?}˵7olm{^nbw ,68=yn:keΉ-ܘP1z+Ԝs{m;r?_c˟pNީ滗"Gjzqe\/a ]fgש|n{6c.4v}He޿XJ׾j_)Wꕃ聾fOo^+Vjׯ=Q]΍_׵ʩٟwkDt/Mk6Y˨Gqٻ!||s\eWաv-UwƊnͅ۽޼x'&b6\/%cRh>wsR8k>xG~bGsﮯz_VϾ[z, +n]yx{V=ۧ<ЕgחGܟ·猛y?5=5}Out6&WA_uU]m]=y0-3,olom\Wmw.ڌ?hX44ٵC`}Be_kؕP5?GoS;-:AY~u՘Dy/zӨsj7F[>/eoV?[nTwwf+24\tE'nN6&2w-w^fmb~ iq9oW6:+yS屚epr{ϲWrE{عk?qlN&ΐL:1F^e&ja|M`j\Eɀ:x_?ꃮ__y ) wKCɇϧs_ߩt>Kvb݋-^~Nθ黷׋!YM畝l>we~u]tl\I [?xWk|c>^q+3/hkٮ{y{Ϡm䙛:_͸K[޲Zxy\[}rͽcW+9ag4r9w㞯r{<^w=ev8\=u18Tʖ3),dtw~Gl4$6Gc9j5ܹC-6~5iwձ#<ͩ%1GrLN~ 3ֶ߬Y3𕓳~\]=U{~\)Nw+N}qzrcƺ+ʃys&%k~Η\>kNu~CjL76tԗVP>2o; C`Տõ͙VR$C캝j}2͝'Q׹@W~v-s|ye\~2?ic<]5KfU{O-uidvecmԯ]CVV{]lBc:ۙrnnuuLVjNݫ7;9ؠ\k\'1oYӾKzOeO1i5޸yŘy ͙k?n;>z>w[[Ovo[mV~ižŝR<~[Es8H+ :4a{Tsg~3.xc՜m3YӠZko]6'fy2Ys%l?T{j.)5KLVޞ{<pϖ;s'Kw;^^틓Mݽݛ±wcӜC~>_Q,s%ݓ:Of{w_hw/ASSgkǾ^],3'[ܺP/+7b7V5Cٓ,1M?6q5y] \W ZS}~5cGcY^o T.6zLis|ec0j{1aށ/[}h.=~cu'lMdk jLϏ۪[ƥ6npqy +znۑYN\]S#]sKvP_VP^7s~*=CjjOꟾ9P'zyo,=}X|=G{cx~L7mr]-Xgʃx۷}+Wc }fC]{/Я}mܚ%K}qW?oyWܢܾ ۼ-볍ڵ CkjksU24L[Ge8?nx+'s~ rɍ]~XeF G] P/;0vrf ƻ޵{Ȏ][:f;u˫{\1nKh?Exi,Gyorʄ^1O\OxWlce9G7e>_k /XG0˿ٽXkT+Wl';Zuj͍S{~qa>Ιw޹=6w|\KAծĊ=-Y9ֲʸฟ{՜,G[g ' ܋}G8ia͓,_!GカAV\±\Fi̧%_{n/v~C:X߱8^8\<Ƿ(~{?`6}h;5k֫hLUڴbTZOE~"Nھ|SceܰyVkw?ڃ1iKԟm,䯘]-08:mF 1?ǜXWaNP]qui6ZxFוj'.]׾@qNWl]I7kCKOlm86neʯ_sVjƖ2??'kv>DFq֍z5=m/Xug~r^zc;8oX\]!>kG7n^O멎˟ԛ.f|xu1]ܸVܵ-aƼ9^zhԇq_<ٛE.axN,Ľb +Uݧ#<%]沔{5w& U;]nrU\j} +c93q@]W/W+\*/c.:)7fuX;gu-e׉oOEcCݲcشAh}||sJ=^ٜu>RjNZ%f<\Wwϝt{?o1jAJ>y,H%[~'ߢyceR?i; +֗_J}=Wk_T{vU<_Im7`Vbc_еey/N*Vc.[~c?b=ǹlAz1ɲ56W1ƜUof=' zoǩ7VֵBc6嗯m!TmcCq1yٵm*WèѶ58yWծ4W.@9`6p5<9W_zٱ>knylr\ؾu[om}}nCV̚~' |%ucb'9eMoVy~^SkxQu˛j}/uw\&ʥVq5r n??;YMNd}jNе_XxX{m|ZQϘ[yʫ-7\hڻO}ϭ}W+Iַ|WKdc(ح&I&([>b߇ t2=_ܕ)c yF|k3}5׽xƴ׶M۷#ł7wLmz'׎^;NoIm?j/wu};˰ٵ8gȓw淦e1_q <Ϟ=_#Լfj{yŀ)ts-9t=Ԉ \ ʱ)?cOq?cEp?^s{Ows׷s|?w}q|CxqNquWN}֪JS{ҽ!&+RS$C[2*6&T~GUD?SR}^mq@1YƻpzV~Z^FOҘsmw&ۻ'y7vuv6=reXw8O4~q0xWmN+:.Tq瀶W oqSFy~Z޸{;?zs\[V_b|[νQ-2޻k? 4bνK˖$NP_َ2Q㛲W(:F)i`?V_}h>jV/h}q{-R%wz'\bzk~#NKRj=~s@q#.aZ]{5;t}뮮RFbau.{?jdӮ9nGr`E 5j3&G{շuW +>bK՗ڏo^1x>s Wcf\ߙ\UnƝw=swe/莙;VlhU'rWcy1[ l<׹L{ZhҋQvWlY{/힯W|EbCٸǍ52z]S oe/mK?蘼1%6+3\Ce\u2t,-]F#Flw^VVkVjlCuӻ~oiڗ5V{s\mXrS\ƽO{G}6te~?k޿N??WKfn/+GzGi^R\{K^~q3c>,cijSq2@gqc"էZV6}{]Y%vLܵʞ߹B{0箿/W/WZc!rsW&1כu<ٵlU}d7q!Wk=|*/D,ع~.k?jN| 5ۄ|ܲt>]vֱ,O)fNv.b-N mƋo-+K/Xݮnܜ㯆1ןٸi8EXXRؙ]8guuڜoԮ?D_&оٹ$orݫkW##t1_ |Rߤfbs1/piUQ3t/Mg7NG?O!cc ~](@۵oݵmm{x+]=|>Kw->Ou>>=GvZW9Itk՚ْuͿ;{F}ptNZfckoʑ؞v\FWkja}w{ۺ5yUqkj6(.ײ,^7CٿkM-4Emdjdx5iV^k+۟9guȚۺ^6&C{Oݸn!募sܱ;文.S|ct͡O8wQOyyVC.5,Ք.T[zu1|׾rЍ}ᄋ~r~=_+L+C q5b˟1ާsƙf#/;4['ՅJ[vIy`OTIv@ڠmqm:Peqޟbת 9zFZOR]ױsmt?.q6Z]z[7j\W/>J;ֹh~ګ&֯8aYaְ W:@Gz^=Ș/wlr)V\g'ʿQnWgo"3gf5i{1SSx`b+E^<4ͳQ*2[4T[9[smt9*ڧؽV[{نWϷhmk1׷CC_h߮U}thlH+׸_:68x_Ս-y/fp_vlM} u7\Me'[o~Rz5/':y[ü?.}th5]m?v-S\SnoƬc9S2<}C&3ֵ̫f06nQ)?ڸ\ey׺m)횻ZX6ge[}}s(9Bg9[e-uGV>YyҼٚdnSc2'7*+;?凫Hyjj |쾱sǯ0 H9xҍƈ?cJ#s^{6ƁϤ K+\q;=쾧4]CL2+}{Tټ>ӱ۹jS7?frv,u﹌h9qtmˆӎ'Lڿ~}$8G\qnƭ>{j}k?r1A4}4qi{vNr,iCL9S__Q=ċiOmfٌ2&~Yvvx50rZm(~:m_tE(=5w~4<;1Ek?޳{Ϭ:iZc̢u~(W[>CQY}2GNlQm9rX݋.ϵ*Cd ocZѷ^-,G_LQ|u;_j_uto;/`߰7vL3jgW+Lp;ik:g';n}ͱ^3:8A }.KE/W7=j_Ǖ_]je0r{ȟkgjhS|l7qWNںemk;y٢s\:jzT[{cz-nܵoJy~z^m6`|͟wme/gԽ+_sc+vո}Qײz7NrH_1G/oϸ<11Ս-^=76qXs#̿6/=Y&.jN-QOZ۰bCr|ϖ;/h}mg[7nO,_:9{>Sc< X6sNW8;PMMK y~;[I__Mi؃e3jWtu[ӻXr~ݏ\\wܵfEϾl%2vkkZ~} ;O;_bwkZ* +oK+=Zv4vX=}/ [˜6jLVQy/F4.t|mٻyJƀqE͊9' Yӻsf5̻gMR.[Wg'am^._duo]o<4)}~cxRDvkS2eΣַ|^֛G.kwO6vur F׊x߽1ic[W]qsjO^6Z]x];h7#ُP{<ݘ:j.c8L`5Qb{GOe3k6ص،mkrXi\ZN9K3{Cm}s폌.[rクn䋯p̮6gX =TFVVNqϿZG);6>ŀ՝nlp\ݜ +΍cw_[}w_[ lcze^wm/x,]?m{赟tܣ88om&`.O;ʖk{~hgHUo}eV7d/Wo߾z]>>vs)cژy \7٤jv#{w =Bz~u>͎'Wb.>,{MTyx\=g~/??udUW\0FR6Jݛs9Ǯ\{~﫣4jjS :z'c_l P~=noi cs#U-ZON}sPT^V./W7hw*|[ fqw߲19}^T/ oxs[o/>!>Ĩ+٭m^.~7x}"׃V$7fԸCіkvh]D=m&[& l[֓vge|T{:X9{Եviu~2:jܵwni?9[ONڽݍ|3sjjl-rN')}Bns5K{{~Kn^ƂJ}Yw>*Wr6ӗk1>Y旝Wճvbdw:79r]y+;]s7N-^1ci667R'3ן,1#O=/$~X,rs+ƃixڲ;[k'-x\T_R8&Kl.~,^ ;-AlN\՘Αͫ?3?3_#\{!MXjjV \j,R.:re_|gCRe\` /Uߋ>WLhv=G6S[֣{'-lڀz5Fz[Q.7nEԳ40W}ҽ-ߑh?]7"-|^_V:}pu5~mcw)#.]ٶ_ܦ:b]Xy߭uwL,?W󼜮z{WZՕlƊ41Ǐi32?]~k]k?simէU?!Ww&3XT#Ğw9PZ1_Ze9Z SvN,KDfv>qK}{yL/CɥORc:tz/>iB^wח]Gf] :0=lgr^n|1:y=g|=e8Gz|/^Gr|]ͫ^Zpٞ] cz=Qni|zݿvػ'PǥϺAmˋ-cS )m~7ߞ+FS{a9xk M\ʏN󗏜<=ͥw-[ccez5v\fcxj٘pw]w_-[o?\.~8s^̀s6Zjv٘*wvǯ{x+_e:Ƌ5Y/\1ݡܱX}ʪ~=ӺVYc(ӟKG.Vko;gze4oi4smFj ) ~5 Q :wZ^xKz6wz_:涱|}J1_sՂ.5NxΓ^lp5w=jv-ϮV82\kO/{ Ur*K^AzV>z^Mf}d6'Mercôƞ+8*WN|z9^X~Z>G=%o3H-v}pz.go 5].3uc\R..{xq/o¸{9I\+l(P}b~َ3Npyȍcz7?nwzk`y`{;<;Ys{J-_v|ӽM'f':ϰߴ^6wznps6;Z[=areˊZ猷7mj-\BP+\]1ݣڞ*]_,Nm>Lso/ su=| +}.duݫs;_9oyU֍p5ZacK{hg̐ߵܵXG;Gqonkvډuj_mcReU٣k"Ʃ۸49mͭ1p:VnO_osD ^륯*ٍirlMN.%Xn3&3?,^jtRZ8f;?0rO*7uB]PZ:>9]Χ411 r/>WvrKھ<ݞyg{i ;{+lK_*ז^Gv=d5;Qgq;Glo&{cu)c>Ϯ֗eH~#߿: W{qOO򙌗}:_^uAYۮzעPtx[f~O{ Բ|ea;0NJ}ԀmȱE#p? {,{vc Y$d~D8_a6O/lvKƞc6dcBuH]ck1k/5jֿ_͛Je}nYW:fh5_E}\w?鵣Vè~On6@_yWas v^rl6^gryΫ7k\]<պ߼[vۏi][k5Fݭ{/_[k4_v\ݹ/ŭU+uqiud+Ny^>m?Oc6ҷx XpW;%Yjv09N|cV}Nv9;p妮kve*gڏS:ό8^󦭝f^ Vv-j)ͬΥ_O9X=l/7ugm=^yYjsZ?{3}ʢXR;gwc3߫xvowmmY,ycl\_5k 5w2qsnuܮk~5wwne_z?|gTki_p؜|o^tK>}ݼ!e|h.ؠNeS[ns.2c%λ[H:ݫv|s&y \we`gDqƬ!leZo]ϗ397'2Ouu1ٺ1zn;_\ھvu?MXk,sZך{1%5i;\ԋ߹2LKn$~sS+̓g>ٸβh`&crړj@.tu9c'î*QNT욹c\և}mWyGl~5>T}yʹi&yk;ƅe mc[jyt#j=vr2(1uSFε뺶Z1XIuqߋUZϪkvGӵ{^ʼnG^\h>9`:ڼkcrճumZW#.36~uTŜcxn[1e0Mڗ&S嬭bj_XYL-{߿g)mjho/ Y-rnavZʡ6㸥]nv״j t="woI^u:osd7^,7u!Zŷe9Uy]Oz?6,L;\Ʒ|YSҶln,´)kKr(mn}첽Sd.ݷ>kcնo߽/j/l6vb)-SZc21YXll;]V=W'Tzϵ<VC^sb_ի{Nc'X{P;\>dvp1dz:+|Iːʀr Ӟվ\.1lKc'֯w8֫]$Vsk֖mdCYֱ@kZ#"8}uu>kݐ}rA彛ss Ӛ6I~|>.N1z~@+.6(nw/6&i*?+q}tVi\|9j볲+G|[?n.?~Br1=F_[<;H=rW|zJ@_{oijƑU}U^Gꟊ>cy;NiC2|㼘1úf9ṠsX{\f^\[6UOo̗ܵ~Qi%L*we<׎<@;*guNM`;KNVFT,ĜWnYrm^G峿Xc]Xz>sV⛾XOy՗Pn?h2;Gsegmyk~_ןsS? +pu[d>kD.T[i2݌mYu glKgenvk68~jv믮e@ͨ}l޹:_'M`[_mm\ս|>>ǿ}6(Yay8oc}bo7_#_ Wߧo+ϻzRn9~4뫷vMsT5_TL_z˟(.U˩-x/I,PC$[6ע %yr5##Ur̵IT?]/ќ_Pjy7']~!K߮l>z?Y,dr㒧浹,5i\a,36Hmjuvd=qsϧZ}b\:z]g:[ mo/i~E~ǫ-}}.R| r^i/Yw'ο}o_QhCY|WnI#{uONwţn~Sw}s=x~A+G[p[[~:#疥z;_C޽~il_˟WəyiNh36Tmq>ż:Wgmbzg-l nY]Y6櫮sZ^P^mא~|e ygW{]~K2V{j琙run2=;o9vcÖQ=)69_vwo;.k Ȑ4zVo湖Υj.lh~jɺ]?{~i~;׶pϲ#٤ǤO,l8?55w ^e]5QD}>Wo rr_9]qOparAqM5x;y">Y|]lh_@b9_mYԲtEr[|fǬ(_i:\idQj}|:W&Y2Ոϳvl}]C[O6s:c#.4>2hY;c71E~\wb^8򶫮ŶڏL_b[Y{_hO'\kYm:]lܹzңyۯ.] qW'ǎsRy;kn;m ˟6x򤻇Ss'OZV'R<;pҞY-ꄲzmx2!Z2Qc\/&t=}[W׷u]?(j+{鬒mrϖKt2FYP_u}ۼS~>lt1<΋kY.jbz{/״}oϡ>U1Viuώw2¾|srZ~]vocO_?\7ϫ>˪î`No#gKzƪ YW3ry ^^j^9׼z]۲fPwYY'7!k/?|_9?ke0uL=MzZ/7V6g&5_$Gnn]oϹ׏f9.kkdF;Ϙlɮp%=K{K1O3]:>5NМũζlc׹cw7WεbwW3ٱz9j[w\lTcƾ7.w[lߑW^}zSL:|Qz\r/g?crݹZv}\uN'sкflڝ6d;猪dyML[B;iyRoIϘS)SXYoCbuG_`jvmn|唫u u-;g{}G[eVы=g1ehzw4(4By_ߵȚ~y<]{+{pm^Oxim~mu9k/@X_"O?.[ݟaje~j||֘3'1:ZG8ib_Tύ2}w6o;(h+5dj:}>s}MV h؎#w_ggl\SݑŢo^LHHWtuhuMI}M6c.UbDoQ-n¼:͛}Dd/);/>iy-!$Bqu}:\+cY^^~{rv3TʷZ;Y2;m=ثㅯ_ P ;yG7yſ9!թS\8WWXݝ[lcWGͼ4W/FZYms.{qkۮ~ׇ6~~JŜ|[76W.['c"8M ll5C|O~`s;YβZk_/_-rӋ66_wՍO?x}AC}}~?S\lC$襃 4;.jjuޮ|yr+%UuwmHٟ,mf xjjqp.]eծqtCtq8)}yn;mjb}'Mќa1ts1ΣR}oq'> m}/1juVWW1t\ʲ4sw7_j2mc|_YjVgkvNHsS|s!V[vm9:)lw (7gƾcw76d{-m [?ۍ_X{xϭ)el!v7Jۿd1 ^#&c\",mrL/wև,_s]_ouK1o w\>EȖ6\N&7,5Tw/;lq޷/1<_|-ۺ('=^}-S?l\[٘ض~{mU1}/.5]0KnV?N on+|{]c5}_ꦼ-.0Z3u>{ͯV׋\OwW7N*{iXc;v8hKV+Sc,ec=e~_ }ۓsJ˹T[mf?q1l;oA,0{мj7\+8&_;s37fŬ[/8_ĥ=9>'-6&Q#|m9j_Ǯ]c5֫y719sĵ#[{#UZcuݝ9V?xUGb6+5ĭ[Gģo7=+-wTW o<ॵ3^nGOy׾B-6Yk~uO~sn_s~<ίk36fpk5ݹG-^e޳9˚z~ٷ=/ WfQl9>z+MˏV?jު{1#|jU4zmaۏ}oT ZW۫ Kɪ=򦞭luy(3Amiex[Fv7f Uv3V)?=q<'Lx>Hy766AUo\vjڶzW1~^sdXAE̟|qy1R<՝W?k>똫vT|΋Q!#I:S{뀜kvWS[V|ז#sRن᫩;/f.C_c/3e?Ve?_j{Zl>STQ!;{k#p/~ p>w n9ֵzZ~_:ޯ0]Hܻqj *g_%6W/ߜjXXIm܋3oWx ;6h5Kql7yڶws2e7Zl媨oߴkvy:; $͋syw1ɚոaYauݱ<|c7i=M.y Q2YmpX%7OQ<#cqP9›ܾI}l{۬u#l-Sk?) }uۻ1{ًqSy6c{b|ܞseqz].pWͭ6j\Hiί?1Wt[e-3҆oNnŁuږ -h}wkq==[o7/TU`˳ժxƯScs$zڨ}f1m|yhU7_z699ѦW}VW,pԟ̣;EΏ&]sD6W7wWN 0-_mn/^%>U}vo- +ZGecc196Osd+iCM͒Ly߽ԆrrWo;SՎ5:W9#-`k% lmE 0Qc;|wks_=XόKXO2>^C͉}}|m_=&_Yx]wek48e*>%o +޸u?y7.zfgpY>YkIs(X=6ś%\Ԋ\7lqx#-ƩWӲ5yc ..C{nީ{OҶQ΄mi9hۥfo۩Zv>ը92enˆ<W}YZWڳƿk n],kr^{FՎuS|Z`]'صo켱SOݸ:ݗvڹlo_ͮ(nhޏg{SFkmLO+ec[oSf3P]36'\k0qKCCڳ5VVkgq-;ZA5i]v+U7g6Ww5/dN|Wv٣/n/ѳ3ocګ]E\sǹ ֶۖڟr{w|Zus引>Z~5sOPh{1ܕm7?[{_)1w \yi_f5 ?/`~X>Up7{/_66x_w,TUOO_4O/O9dD[^+xh\}Fb/Mr[v|}s֯]֦WU_e/4VwJv5#פm8V:!͇z BSݪ.]Iﶘ}ּw^_g $\W^2~Gew28rr>W [V9WVH]cQ}ӵmc 9i꩹ƿв=e +kmcu[^_&޵_qs7aAZ5W6ub 6 'Psk[]LVgK)W|ƛ{Xh5-/ꊹu +\߲<__ָ\f<_7:X=w^lmWXciQKˣ{uw]g3Ű;|={{uN+RQ3i1,cu|0co_;6[,L?8X|2 < _گW&kezp>qɘ6ټ;.!><{@[D qfhn~x~b/Ŧ=_,ZoMʜ&k}Do0ߓ<<__}vaߩU'Zy&c`k}$jvmlk;T^u5&WS]noW.HJj2:_L^欭2οGb_Nݘ9Zj w#Zn+Alsߵ5Wuۓ9 -۵o˧;Gcs:_eä^ 8cg<> Hx5UNk٩XerkmvI[|Pγ)2߷^|ksȃ7rB=Øhר~|6_e9e5.{Nc ^nl:Żnڍi6_3z&tnFmߑL.N0ohW'cm^W_kd5QizTx5u}ۿَhɩV7:S,vYr:#{-_,0ۜ}r죭{m;.:Vv^ڦ쾿zO,F-@S𥕋dz9ezw}w\{ھt|_7tb|_N8';]_qێ5mۜOSt3xar.\ye1Vc39WOfX~ݮ=_]ZbkjS8Bh2͍MogsƩCNsg4_kvPshPu@.XNm~ۻHGd^mͶ5Lً5̫ϯ ӑ^r.<=l1ʽ|^gOr^ʨsw2q毗h.в<5cLl~~8uvJƾc+38yi5]kܹjHWhZ|/MI> +ciI(q/_5/>76/PNPkdw]1 +b.^vۣ~7Ł\\~}̲l+Qžcf5Zqۤ|Ulux>Ke}Ԯ R|cǹתB_y.zlv+w}@[bC{0?lIz+طܖOo &϶]x[U_T-Om4ƨ?#WmN2jp[WvnOxt]j^k暻+Z.ιVOnsވ/6'K \h)(#}/Vstܞr~pc/ƹ:jL%^ql+9k'Mڿ}c~k}&ܼhRzۦWkz)ɥ] x9?kl묌\)w[~[Z6!.{50; +\M{"<|VQwM<],%۹O}9 -pc&T4x5Nw߾{2SHUQC{}[\C? ڿ́/mq,/9o}!mo:ӫj5>Ww:O7l\cuxۏ]NO+e5=k jPz߱˂r|L;{󁯏(ИrqgX&qn:*_uw>E}>Ɛi7+W,&ko=.-2FY{nN{2y-;'FZe~b|hs^U@vnꏬkwֲhm;fQuPvn̫K\e7l#kG-y,;vط3>~s}i[dnqϺ]l8m,\:{q};oLV(Vw2V1צMJۭF;5n?vrl7V66:+cj*'甑-_[ݟ_zɞG}j6[_ X*?4kk,h[{d[V;? [M^wOD Pm/Uv^e)Nze+:u/-KS -[_]*[>Bkkk1vJ:V\0n.c|\ca8ˆj"̛_G kǫ;jkZ6w3/ľ~sZ_X𘷡"yDrU'nHJy =~CsTp-?DzZi_E'jlltvrZ{ 85>ݾ^A^O{h.?:tZiGuZ X9p0:qXゖڬZ4ϭ=i\xj{M׻ui +w+ZW:fqB_2ɖw[L8>(4g31}{?ޓ(Wuw>5]C#뮫܅+`Uy-g0__8b~z뙤M=UW,knO+y1a=P[[bV/j(d4/vlI W΁]81;eY[/mZۋign{GEW׹ej[b7]k.~+<};(ޕPmtueȵ+W#ȣϯy1_}' u ->6`٪Ussyt^,g:LS"Sƭ9e˾ؒlo;V{Mfuf=p5Sg%Iص-g y^cNx==]CHwtQv[lϲ5TT+]lL|eN,)%9yj^N^'3t69o{r5>yw{|dK |{W^9]47cr\>o`pF_sC];\[Tmώum|}wkw/KsnrAO7su8rN<9wL^;廚㡟OA{F}uncok6wfyq=5+ݏuc:8Bcrcٲښo4O؁{풍:.E]řf8{~q!iu+>{ >`zoJѺu8&Z[Pd*eՅvOP$Z ?}e> ~ž{m*O۲|WXԿۼ~;3s/v\mUW=F\<۟y/ÕE_lSoCd+6|Q4}|Q`UwlGrt[qӴb[nq`olތ\| Yw\\@.Ѯ\Wٯ4#ˬb;6׫QvY[~Kd}[}|YsV۷kw8f ]kr׍_sl3ru/Ǎ?6[NPgRvI3~BO>ͧ@ƝfYHO36'^_@й\9./V!/[x;{Ӡ,sTeZcǮMTZdY.Wp!VtN1 4_xcauB>',OlΡ5mxC5"lnm{ǘW޵*vYV?>m> |򿮡e\"?zB/TQl\mOfl^Av)9 W2;%$:iQ7z^{ww `lX&s[G6W7F<ƥzov~?s1y͋gݟw>է9흼 +wu_So(($LVP(K}gsUjkg0zto}[__|}}_~ˏ؏}?G_~˿˿˿WvW?oo/?[[_~w~o_/˟~??//gu?oc~z|;vn{}w1s~ߡtm[鹺޽/׿˿w˿7k_ϿKnظ;;}vcƵwű{޳Vasuwn~ʯkڮ6w-5˻W1n?]|~_qsw֖sc>knkn_c)9k{{x;77k7'ݳvj~Ƀ޽,tL}p[=gYc|fkwRlL4NzV:lGXk9O7wV7G'on<ϓQ??u//u/??ȏ|~|ݾ{~~ՓwƓw> jOCucy5j~p2S{gzOq{Wg},1⻮ʐ;ɣt}=y1TinP˿˯m︶~똾1w3gtfW.tlcNJ逞s;]cIkӮֆC}ܹߴ}m[}:Ezxuzkl۳q//n}Nj㰱1ǜ2Kyվ__}~v߾~?1oMs|n={虻_}꘮c$_W>vho5jڸھwmd?ws;cΫ~o\xptkZ}Ok75JK`cݿ^ο9lG}i_y͍16kw:a6Fre3$㻞+[ґ鐞A[Tfm>Sf=kkkONvrx(Ki;m޷-vn;?_;_q9~臾n~X̯J&6ܒgNwm3ڎʝ>צۼIIƲ2Slr\R{Zܼq,Wk]W}7o_mӛ}O yw}0L,}:“cɣԏӗVOf^ܳb6&Զz.'4tBE66Ұ6e>ht<|ɞ9ua_O<}xN>d-6~ ],y'v/~qaA8OWl6,EL%,vXFo{ 3ypp>Fy8 nw:wS?S_;gk>ak' ټr87+d_;]˃YZ[.ٯ|]Y>wn86/|xq۷س~tXzravmg/}8|z6V<>{W~OF&q܇?js1=8tqM+.EcwN~a}o8 w8_ݿ׹wɻℝOtv/eK;7Y.{l&1H&1:|敾t>[N\Ccy[?4&8h`<ֵn 4n?K/~nw܍}8.PLA/ 8EqWdF_Y>_%9X_vH5{vq8smxD8{R_ɱOQ\*A̽wKl~z/mUk[s1|}M#۸nrx\ƺ6vrMN=1:.|@1we2K&X>7ɚG7on,;;>|︀gmޏps~rw8Zs1[.01}uvl8rd#(:."}]^ΓVjN7j^${}OdGCR;.+˞ExqqŞ~{q;>[w1vf6>2;~~'c}ԹLhνk:x-vey/ֶ72#7N)6Gmҵyj}"1AAqrM#'.&no .)ɏF70bB-$ws[,{ a}=o={7?+.oF[ 8_|lgN_.-^_z=_ 6Dg[dg$᛫muuqӸdrɴŨ\>2Ov^9 a#j ,NhmR|N&y|}&.(o2!Nme_oipq2S$Cҝ)w{me92xծƕ?H'{_i7זs lMa/y/Sab/\m1?Qiʛɦsqbp..q6gww\,vs,Ըz lَ۾c?sd_8_6bBrVč6ccYELTm7%1hkg+k3GjOlK";?'f]xo<1=?{?_`T}yxֶ[{L%ƕ\n?"7*9F~K,oA|g ]W1[;@ŋ͔}_ߒE޾XWx4OO+֦m; jlnܣ=+}.5zmPNs~!ߵCX_>mLx~m:^.s6Aux݋obImvc k6[?|=Ctq3ܞ8ʅƲ8Qk1-/p|yxƼ6K5jbvn=;{~W-/B@ޞƵ3ig/?n/sSG'[ˍQ.+Tω[l&է]kmBfeW^;5.vI9=\]wjX>vKyy89wܿ}WW ˼(4^Y|{;xq« ]_,^/ G}{/_M۩~7}˵:w$\Nri]U\Y̏J^%­ae G1@87n+.)zӗ0QLS\ֳ7y݋$χ'lhc`Nrzc\-q}{svj˭yqfPĕ uG +WYs{y^6vX 15ǴXC<¸E|ыxs[↛sƣqb'S? L齎w:x3lx_xkMNV/s?윳7/mx~œ<7^r[K;vKk jO+˷1e0>ad)okNSya][+C\)zWپ̮J&W+10=m21r_޹;w_ٴxre!x|/^m3yjk"g>0#&n7fqWww__l+ix6VP[̺`b{s[.aG;ܺh<~qٖ<6ZnU+e>Nb'ghw=qq~8 <ߨo񦊷٪gm{dx+zqf*[,oLaˤݞkkX,@g5&i坅?uguß{MX_4^kmsF{:omZc=ͻN8>ëi{Y˄b1{qQ{ƒM,[,*]#Y.gz(.7Mg(^s_EYܼxLIv1^ce]]_bum$&/},>)3gYNڐ)5k\y͓]\w}ANOޱqa%1j'jw1Oyzs0/{rw=$9k- t ޱu;6ޜy\~U ŵ95s:NYi.kqtq}~i #;;7"pNh vK׫MDI۶Ms#ouy9S6\8b<>;nRVbɗ8퇽Rnk] 1@Kuba\Xxnbk~/p9W┛/ul-k\N0QD9 7Of_S-Ӂa|s Yi=}xX|/l4vw[o̸/~Dr?u׈[QNXb“]?)vm~F_,zs) n6_{Wv< z3ޣ-oNv!b󊳭4Fsl:Z\._ݳdcebn,?p1Ǎ?Dk;څwy18t'ZW7,/ځb+qt ts>S`20 +9%Olk[jSxkj?×7< ڮqk |VXL,F|7ů]]ÖZc"Lx@|pߎ4pmݺCՈL-ڀb=k[L7\+ؠ~}˃ɇm={<~ŏk[(۳gx<KYLUR_5ӌ%N/{nk:b"6W'ɽ;(; m+++cC7h7NX|}ub_{v6uoܿ1Ydx8غo1[lSݸL;Ov^kV9_ͯ<͛[n}_?NٟYzAadO}k.Z4Xk?@G{wx>ゆu~667=bkW:94`]j#y˵v.h/j;MٽWXq {SݑC|#''S P O>\wq/9%xr}:{|U'k,SA?S8H~Mu.Z|z>:agMӘc?SƛݵCWV$lsO}Ϫ i{{Spk׈/'Ooz۟-aaIq=sz=76WTy͌+ιϗ .2 /9梆!MQ{ӳ Ét{?ch]O$<:a'N5qwɲwڀaN%?x,giKٍ 탗6cbmX\1+Ħmjm̶l +ھe2z0jZ<]Zx2 Wqz3w-k/n|Kmk}OFX'[i%jʆ>c+-5Y[NwOs'S s\ȱ_>/M_Z/8Kv]\n]lDa in^q?swn|`YC(g_/[\}hL/*cs~ly==W=ܭ{< 9ڀqo~}lŎw.j#I]9Z<%~OOvoui?~קKX{1|\ur[Bk.vW琉R2S? %9./ PhsK<[1?A;_Y}wdz:rcq9rgk{71ǻ~rj}?@}!BicX8K/ʡ[c86VNc_]d9|{bp#:/w7} +2<° OXnssۜ }5>'+[{,Cx*/.HAw' qxgEamW /^ z׼vNv$zTk˕+nKf4qθ>H]_]ڃ[/#Y~cL>k1:&?6Co~vy[gN[]3nYϺI޻r68FϽ1^C} >|}Vnym)mcO{?v- ;yvw\nտwNF#?x=,p#Q^sk|v=Cɨvž0qм'Yb9}X?!yZ7:ymպ?1j*xA>kmu|^y#Nx3,ZN{ww^^NNjW|¥S7Xtds;7TnO =_=b.Xu5.&y3\p&q]\:7O߿K^_ڹJpb;M6OOU>qY0<=c{7ؼwͩ}w[qy̗\ [5~-*/:>yGlO}^9ɊO|7uyz]9|ͯs1t#׻nk] z_u?alxN>.)\cqO9>' AcfO%@r0 rfa(.'غ  ,'|/eCe9#mLhuLrrcsCm>,OgCsճe=9~Ǿq8k.x Wߜ{uhdDc҉ WgÈ'/F O[n_ǿ ~эڠuqumo8c\AQ'j,g>}}"o4UI34V֟kWd8gP˟Nc5_c&No_+n壵V1o qJHʵ_neyr+ 43 ob.ݘGk{g}OۨJˌuo\KLK,U;ݳ_̯^7_>" q<(hU qk:ŵYN'9x[Ok^}ϩ-f\ilcc6Y;9S=om;Gar7~{[9,\}\'NU︀o:U'>[[Ӯ8N_rjsdsy)'O'10 _9랯>Y/\u Y P"^o:vC^1syX?2amoKwީ< WjW]?=xN/⌫C!x}ޮ;iw֟+Xj}L71x{w{oڣn-~_?^hbkv ׸],mk?o0-6qb}J{/b䋏5{&9Lǧh9q=_|Og&7eۇ寔t>g|xTOolTuõ_ %:θɧɶ]RRm1C~Ir]w{/q߶Xj_b|'tchMq~۪yqEk+G|ŕn϶Mv<~:ٗ "'8ח䜒fjkզ11*(O#揶E+%cT#ܹƻ!Gr˾;4{|9`vq]26ZJ|ZkOTws_alb1||;1ȕW;o_Xf}!>?zb |y0X},T^b +k3ſG]sc bd⁻§vW-μ]/TkPl^W,]T7uZ /\d=oyІ\]ܼMN +WF~]kTlM(!bվ8rɥ[LLO\aXu\C>S*givx\ưQS.*qSܺw[ۡO^\_O2w)lO=mO#/|pل$xf6v+6+N/1ƨ\W)a&˥7}gt;ml|ZZz*T}3\\|Zk':]A0'_:1p=sd5fڗ(?+|-?9w ܺ~87>w]ylXؠx}c` -^lZ&[-Q{vI/|x V+{U|jSG_8b!4Lg^`lk(^ggCb"bb2W+,PNpk=󠮏[õZo6> IwL@-&O:S96ů0ѨwY}۷M{&}v8X~}W}4OkT[/Tz9^bTű҇[?M>rCL\W/΂*F^]ί@\ٽ+,$)$L'%.g+{dNjr5ՃĴٷޕ{9rO gwr@}v:5n~@ɻQ+s>.!X'Xk.垷էźz[=k&Yyzq-,U1Ů۟>5{Vg9N/{dxxi}-';_k,tZ Ϯ~k.x=Y:vq)qձb)9ktzK݈3Ng'ߺosVbuXv{sbmhz8xyb.gnkl0 }}k:,/7's./R,pEhݖ/[=w&JnM8YE}c.wBF8;7_i'Q7VƓW?j->m߽;1e}g.,吝t-U9)Yj:߽NsG9My~Hc11=õG6YXA㶱xSHvYBUq.zaW \{{WN?[5ö'k6b5v[=J7WoONI:a1WuXŵnm9?OǨs/T 1:0@B峹9,}̚+g{ݱׯm"^Eck5c H67Wvc>yCmWg|>M?>ŤUA7 {mγ.R-^.} w//:klgf>cݨuv^\ +X߭]ڰx~{v;vټBlų\m}Eb \f"fbs`"\sW8Ǘ9ڊ77Iww6nƎ,yG$l}ƙ6uj Q՜-@9X=u]އܿdCLvtE2)cssymM-^+<[0qz~yk2i1VFւ/WpkV¼ k\Ac3:>#u(> G)S'ZzwmɷN+saw) @{ٗ)|1嵅y^wik,Grxa}k\lC573k7{SSr NOf8! 0rcͯsk{)SCql^nZ )os^?T'S#ϽUo@θ1-g9| sM;uU\L[Qı_7_oa9Żz+2{;'aއ[T吪;vo[6wٿ6Xhr[my(Fa!ZrFx2;ޚk/)n㮗s~R@0tw)cʲx9X>xG s%h\)|+̳9N1;lOq]u _?SZNqU/ys}8Yn5ڊ9کOY(V?֣a2~Lv5㭗uݻ7P_${?1?\榲X9l]OZkx4<=+֖Z<Gj][11W=;kY0ᅗb}޼8=\oZdVmr(_͍p$Ϛ5POVhx ?y|׺oʹs\՜Wq춍Wi$S ]'[us q=A|W T9/*<~G*\(Yv~-Gz z@5֒k:8tyAy՚C{9ïW&׬ |OHMc2)̫>~ _8zof v9dWp2dOͳ {Iէ]Zl󕬇tɾ/lLƥT1lb?'v0nͲ7W;lg@컶|{69)zZ=cF6xkjc%ɓ@/?'yVŇ̳hlo=\\WN~yHqǜ1{ ..晍,|G|} |sqAszNf|X +*ɦ^l15ы*}-vm dnc_@=ϊxE2wynrx#<]̪\LO.քxVz{Ӈ&Om'KH^cq|,A&l(1yF={K6[I4KM5N;gͱ4V/ٍѥ_.mWƺҦtݟ+N%_|Gcl`M߅%Zw+kωf7$_jIj{[7t/#r"8Z\-rs6n ϭo!⃮޻`c28}Q[ Xrmw;}q\?j9q3? chr _(:Sr^?t]gN 5W0M{7Yn]B_ oЭw(aqMuF³{/ژYZuTu=&iֺ>{|'9ҋ쳻^V:MR?T.OOy<'ks+6sKq»xHYEK$Yb~ 憊l}wHô__qx\tuzgsqU}vsw cJ?qcœ]9ă <몯|x~ǵW5xr]U[ky(cgmZ5u?Y-õsT[/0olH4FmHm|lSU'wx~q˹ +KkryW03޾8bv=vk/7p)(W]ֿNSK-XCMnKJ_3\Hş̯hk wxQY[N h9tvD8<#b+Mk_n<œ#ݺyS|W.@_  _?p  |,'>Ӆ6Fk)1sqov܆bɻVx:7MZjls,Lj[\YMvmryr\w}͠!%~/Zl, Χߛ`0)挶Ϣӳu7r>_}]߿QsOgw5tw9E{ՂkZ'n;YwDȍWՓcN^uk ̟gjY4G)⥝ڹ8c<13[.ɋK{6YOQr'^P>cd$/6=!?%4_>eGw1nA Wdg}L>Aq +1ݯ5˧?}]lO..jcӫmѹb0=ɲN6\6&6\H'esMnŦg.n!j9/(X8 \/]rh.e2Cu⋿3pXxTs9m朲}wLͪwﳚroΜۋ71P}9́d .]($)QY|&](WK\!X[r;xr<#7{5\^;g \sw +DzfGzWb~`a#S(&h=q8a'>dm]j|}sYako6--,!0/{| 霷 w?Ěҽjke/f'O^5)gGXk6'UO QyΫVr\\O,f}cHص0}j_&)NV GҵD QJF7{9jմ};ʡ+)<\Kw]{Dws҃C(aowy列]/{tcc']ǵyz'.>My/\ +{菛w"Ϳ5eYjVW$ Mc}Kًwm->:7_~˩]^Qȱ2eca=a,7~kݛV-צyDƋjIwLi.)W(]ܹ;G,C><:C[ZSۇlϱ/Q.~*V#C")ܿ|;\Gy`M|ʟz +|61JcfY%wpl}<~6 7w}n;uXM 1 9`|asU6k[~/sܗOsb]'y-o}ȍlouT}W5_V9LbΟ%/_9K=W .XĪRXư}._V?x3֔򌳍Wc =cچwL27׵qʧ':<ז{5^8vXM)}׽kgֈ.viFl1pjy6|Gׯ2nVP.km9{[~owS녈[v}{a~54׵@ڷ9?m*3!yQb/=>Irn{j?N_mwL\r 輿{69qFoG_E)]z(qkg{֖H?m,m=bFqh1ך:bq~'A狗K:nR)9wZ\\ Gt{$3cZC֣3.%70=ڱ8?a}g/'y~w'yr6'x1jU/q|WQ5._\ǚbrN6LdNu]%cv1u|}̓|a9"%abw/on]!EoL/3MʥHn*5]v՚Y\x+[1n5 +$ֿY۟] O X]Y#&bm}9\ |W~:\yoqGra˹[ RΜ>xw \n5}dKr^7]j,:a#>kvvĀډ˳Z3l-.\X=,@7=r7$gV{vǓ|฀W Cy";vݹl+(v(-v-`ٓbm򡪁`T<˸[>/xr3a5^9﵏qyTㆯ0GzG!,bpy^S2ԺOT/fճ},Κy D|<5F6vy~nb=xAC]yJ27 <&vеm#|:{܄<;}3oqʮQސaɐka.mvy} {u9>d1n }bwknr]嶆Ggnآ9kpf8+Zqڭ ־'x޹}G|sƵ')mOr\+*rKj_11ǿyo1qm`/؟(ZOΖW_B_P%}X+kjۮfrkyv^8do|5QF_c%R>䋟&oN p\͍{?\5*V'ޑLMC]OV<1&-.ꞵrޜ}rb B7~\q]N]C\Ebb{6''Nu66=aw&[_]`-y}am~s-goX7yr=.g͑06F}>O.k־Ƶ5sV==.+,CZJbGҲS墇5Ϗ_o}KM7 'HO>[d̬/y[LWoG|.&~juv;0>OV붅Qe[}r h_L۴Z}Ƃkī0V>bo]ӌq3gq0^s=nBZ/ cnqld۠~o.)"N<6/ Hed#Kvvݫ~&T~>R{}/i^n]yw/Nra̭ۉ%.k6s'35U"d] CZF2(q<T'8|ޜ79 ۾Z~BXʁ؋,Ƹ>n޿6ө888 []ck` u:&}v>/9윕k'ƹ9 o,Sc)b:1ñX6+8u,4V{ާk'^\\A|Z獝g}&=\wqyvu}7/{{y;<ק)h85:.M7dW<%c?7gU'~޵ѹ6Cys?l>?\n\xZ^->";_kZ +:_^Z8^b>&m2uOeg/?n{ħ.Iaw==_ϐ|:1Z|/QH)=_}i>;B1?nq<{m([8 ?v]zw08!bO[n]ZN5. r$T6}T?Q ]? [tyRwW_~֯mؼ_6Oƭ]޺,A_?E5뚅6-Nɻxɻsd,\]XbXPsҸ[`lcqg}yT,$ٗ,\Pn[ߘKsSnv<7/k޹|pZsp!i'f1_hKޗeݶ+/O޽n'6ȷ>ue+mnw^5oVCq=_q5YjװỎyӒab}Yt9˯oJGy/LD<~I4 g/ͼgh\؀X[߻sg~D7x8K6~]':/]]n^oۨGzkܱ?7sNq㫿}m5ޛӐ2WlɜOYA}wо0;?}Ojˣ.+=b=\+7`n^quX:_w]X oQL9msաLuoןُ=Ckb{k<{31B4nkdlzGo鸶m!F]vm<|n'Z|v\b]S[05v=|9\oZro%g/Q{zܮŘڮ_u4u1&^5^Ou]O5'd\ð~鮳yȋ.3uY{E}PF/sX v1|M WWSƵ-VNpcBC ayq_[t 1ٜMx{y{;_QGmG掕c}.l,X拋$_ڬxV@yqONӶ@& _['Y-H9̋+ݖ ?'L0ٞ?d-O|o›ĕ6{W|Źv <r"!:;鋊X{Kx\㵎5Z0;"{2ƴ8yq.GʱJX?5܁3#[3nSߵok6O8N'y +G}''Zh'o\A<<8>;xukmr/ds9\dmn]׀3nZҴ&W^`/\o9+0kn@Z]d X_iķxM=򴱲q,AZ/>cǬ!Bc\W۴`PcEk.ɹ?m'N6nlۗ- zs&{WSͺ/J5^~4kmm 7߬YWN򖶞6gU_%W]+&ɆH>5䬛S?Z>~剘nmޣmW^M&~j]ywa7]:p6OT^G?\;OXYm&6a׷~NA&w.vr,GP*}aS}'/(}>G67<]6Hr-v<Em~/UZ}rB7/͑| ;o yr"|,לly߲͓qtj^,]kmn<ɳ-.黿]_ʯHW-Zp}&u񽸔[wܹfrET>u7n{E%Tlϵ_Ξ1yXZ^}FV")s k.'Piwaf^#}_8b`{ާw xmm/ּDӅoQNn6Zm>jNK<+HCŗ]и|nm~1zriI'z:_:,a+Ov_M Wks9Z:/jmgtTt6k@<6Wmw-d+_憘ӑ~W/Sr6g?}ur2Xݵ⽩{{X|ݳfמ9by¿'de%Pd^pj7'7Ĝ|kP٘OITH蛪{½aN,,"(˷/]DE}c_3}WqHqDŽ60nZ&Vɡ8V𾟽~rl?0^Ty'5[sRv׿MXXӿc!qI)Ʈ?~*66ķ^yU.鍗HCOGقE(OYOqb/۵A.#|SVHLgskD]]\36gb'r,nzo|ڼp0FJr2&~nqʁ+(y}};_+%"vrM/. _zmhoC1\2{qH0εyu'.T׍&߫ښA3{~BG_2mO=~XFUS\y~r{g.v6wɳ/sPVwA飍%;Gٖ+N1ekil=2{6=:Dq|lpUs;~.OC'(^yb^/JpSfY]sC'_`=ᮝLOXWݵae?r7틷i4~n,f9wkB-;f}dm+3^y %e}{gmK|ƮXrŭc{[<t\=nW`r)G3ךP=k}x׮+o/+X\; T֊1q֖+f7@{'gfs'c5cU=c!2e핓ys쎍?}ܾlʻ_'_pח*]Q#!;V.}}k9rie^k^;|-z Ɏr}&'ś6OKlﵦ_ִܼ_sୋ)/.q,^%keϖ;[ԋqs~t5•%+wܤ'e(nMƤ6g9)7j)ߺxoq1zeɿ"&r?f*_5+6r;jxϴ85?Ϸ9/pU\ObsW7/#^/ +/'l=:塘t6vk%F.n!J,#o}-n_SgtLk/y~OKU=P}mBu k#Z&{s9;1GmO2v=O{Lm͙բe/V>v\,|1Naõ,ċwMY85fô}B[uW^u +7Gyׇ /qme~8y]qQ?,WB[ѱm.81͗{ nqDŽyTNF7jm }–-/\o'rP̷ݶ>`;OӶ-Tf+v+WjD\;p@.$V  X9Rˉs]bRrZoQ|*,".vy'O5oߝw׿#݌E]ujDS9.g勊k[~zFsC?/]yk \`{7޷C>>qk2w{׊7"nD6ъ_M@uLcq/ټ1^/+U]ywop\[c Q0~r{OOjg,fnVrs7Xvsq\3<]z|3ܭkک|1img>svj[X5NΣbO#̽ٵSlI+^X^r ѭdΩa囧\ 5?{#[0kzN%zbIſʟ˵0y[ê $6Va_ڡ9`ݰVOkrolTyk&mI9rOOKhpP33yg.q䃈'6Rm$kSpg3w0̭&o@9~׹XNa9͏g=Vo,OV}ҳԧTXiKȜ\T֫6U>X1͛,6gwshͧz7wqc_{vAyI./61kONYC(֓ _.vMg#%V'1Wd^Ϯs:KS(w,ܯaOP41]S>B:k#:ͧė>1>Ps/u^\o<_1er|ŏ ׽6r6{35h܍r(_\?焿Z~̞s'kG|Z]uGwk)g#gO9<cص@7nkƤ(gfX@[^3x/\/uzml kk#1J|էټHo>$$%y۶6)o9Er7w@~757xqtX:?k˕}aO2xH\l4oٍr}\6\ڗ4gόf*gr`Ϧ&r<䳺V>淈_ܯ/eS]ś嵩s3^ec]1j^r_v~d21&PCQm&[ìQ^s/~_w"-.j{qtsL_s|ݼ{&qm{T>\@Mm6) KYYl/^ousW}ӡ=}XNOkMcx^ɥsN?Y qtMN߇Kŗ vǔG:;s#[l,J&W$ccL{qkX:vw~fOd;r8|׾V:+,0q`US)/N(reTc!\?D[| klf,U\{ŵ#3mh'6A}fu}W')o0(XًՓ/b]/4gU N.y!~﵎[n:'آ#}έCOkĭVWŭsw|m:֜۵Sjf8jpK.|uΫsӥՑs9ټ8Dvs\k%Q=j ؾ;%:Q >mw\]ugqaTlן0Y[;9?WͦmKeäWnL$6oʗiio:'k{x|y7$3a=bTvO>W܈l`H$us̍O\}4*y?6mkKEkrf߸Mn*DS4Գr̛5cPg\gc1gMw(Z/˾r?a:`O}Pqm=|iۈO~Rc)~ͷ;OS]-md]GS,].׏վcmGsSԼA]Ю"oO`Xo 49Yc ?"?_߳/!m|.V +6Иi8WW3v/'SL/\~x%݈ˬr3\e1v8ø76>%ԏr4KVVn)a 1X[.ʑoOX<ұ|[P/? qyqslɸNmg럞=yV?Cqedy` b q͆pjh˳2Z!S/?\8Z[K̯HY9}1(XL1_%bo\le=lr:G|#6gZlan3\Vs[3ص[isgՙs.55\õ-W,3N]|Ǻ,ה)ڋ [6V!~Br`{>~X\kwqoEKs(zdcw]xbzҸb˿P8W9ٌ$Ow֌ɦ7.~:<}S.50]z,ֆ5\1\;IwkOj.k/_h ͗caSvrU]K9u˱wvtqS),ׯ6(?Gg}oPj۽0M&;l;y ګeoedRbqr[__u|O>V˻:vww|w|&\/"m}W&8fLjQt\f7ЯO͒ 2Ǫqc:|E]()<)c&ڮ 3_ +cY.."b6v{-hЭzvr@/r'7.Fd'FNa*&Ӧ GJ^g_⬥{x*bA1=<;^)N|q93,ٜA'^7(9 ?ܜFyq&|2{]||q^[MM$]O*.ywꝗl]@k45nf~]BRZķv2CEy%mXٚq2\1y[`s[/X<a}:V76Ox1@?6k-q4ζ6R1ںڮسm>˛\^_x΄r6yn3Fgl7ٮ۸DYF%DܾO|)kމ~:vkҖ:~F70~rZ[m5lw=Km}_gb;Ǎxq0ϱunnp9q*(_?$P-tzz鎹s˃Oׯ~z,OGA̞V~VgD!R=xn`ct/<>Y.uz3s'cNdgC:/׍geT6eT1|뗈 jw5zm̤=^qe{v`kϺW^dq/w¬5V~ptԮ.ar]R./{ﮣxH^oM]]P@Pkѧ>1lfN.xiy5o89tI{"[W.`|}~n7Wyj͓.K88CLNIΈS+)&cxMqkƿvus4߄Wg{dEagCrřڼ8ǽrxA2ׯ-X޵@+' o[Mr9wF7oul+r[n9Wc:BR6Ո&+|Rxbŋ/]2_\է⎯.'{vys:enn/h3Fx$Ƕ!hZ\jHȤh07d!c# i.|[l l!w{nc/7o,N<^}:Ozg|}/u=ݸv$/|>9XiUfYruҩ^.K5W-!\k}T c"gN|XLMPk[v]S9N敊ӗ=#Z~pܱװaCDž8.v<;rYUz{W{8ws\́6xhh}{m *}~8>Ƨ3ו~w~oY#a f3òyvܫ<{frc'ei5_9h>9|WN wlϮM\7񿻯ͩ:ܷ{ +kqsȭ!M^EjwkS-|)(e/Ŀִk}|>l{wm=[5[Zw|/|FgH6?>#]RX>i߽zFy٘z/xONUvmLZ|mlNk]e/'{1jng--9}snk5׺7PqV>j7no,`nwzJ^lu,ƓW{,y} -:t?.KZ\ q~W@:qIxaoz$cuU{_K8h8b_w?;~1x2xp).`5}sߝ.YLg>D39l >0O~PamGQo/YJyJM{Vsc'lV@bs/Yy_U, CU<8wCCmS˪ ?kSxlء|1.ܞ۟9u'l.qcRlm1U1;kw8bx4w_޺Qv[;CIkܩS&3u];Gg9֞yրߞ6uѶK]]r9l@붙O|cpl{9n0Ok6/Թif@s߲Cװr ˪ٟ2wlwwmާOnMj'7qO´9 +'xxǸӼ>=_וP?y͵촛SG1#[1;|ſjV_xb, 0{H;w\djܼ\x;?ڸh}7cs]&lcB}O$.)簭CnNWxOcrh09n%cU3Su8v9<'{9bɔ1-Sz9ɚ[3ww[GP~;6?Av87|FoʗȞwluk^TNW<sBG]b]Cʋ߾gmϰ5׺9Hq m[]7ס~b0r{[z=cۯ_q͓I[7|זMH;_;_Npl9Rl29MX1*g>Kyo>ܱ ,D.[R1_g[,'VlMΞE^]?ôq{OU1q;y!;a1octr7)~ximkQ-y9uT_dBl 'S9ZhM/FB*^OUB\V^}aZ_iq96~s62_\UqW7rX Pe?wp͏$g~^kdx1?(*7 @ˆN1w&gIkWe??orq` yr'ѺL./k+'Ͼ⻙+fl4oUi4#.sKVfsJ' jeXxesW[س{LZZdmIcNO\ 7ӥìA *_|V.僇61y c3o#kN16'ܛ{'Ok#Zzspl.97'`@q# mӣۆK&w^f%qS-.9pQ lhmxcT>Oi}+A9[0~M+.YЗ_t<k͞1xՔ)>-M^⵷n-5>[÷NruZ|u؇`q7.ٙ +kn\0GsՍ 'yzb$򋃨O^cd}@Ѻ\*kiS57F4VLMnv߉*#}+SDŽkjJKc^k,r(__W;[NV۟<cʞo\Bޞzv'$ixy }[Y{[HI;A|ر`ϮKo{N򋬱ǖl=u;aݜ|tNɞ0d\@T9[|<{rngwUׯgl.։ۜ%9'e^rkq@9a0Ϟ/k׭X`=r(qVgek_Ivr9zS܎;}n-3(WAW?}\1?##عo;X-l Mq*uv9rdz|ijai>ر͝7x +v˯fLx}5Wۣ;WVc?妨!l3/Ap黆n<;;NuNZsgxX{+9Ϻ<|r|{&zP&,G+}-_PrBpפqQ>kvk$Sot']\0$O\zsOոW!kLM;+䥩k^5y۞be//ݻ||-\[-V6(W%noB҇mraAyʅeqlp0%ߵgv}>?:+Qnlg}/w˯P~-pkYG|3$g-_\f]lg{Vƚ}:V_W'cIU(a9|Ka1ȥ0b'Wko\he\aB6h6K9bTsۛÜ_10Zk1@]8=^UuC7C7;Q6Q\㔛ۦjP?Yi}g9/~:GCX7LV+?Damcd-拗>{Gu}zna'("Ělbzb۵H({|ɐ?R=p)7?^qa)7e84amK}gMgmc$γέu2zkK>I&Zh/䪶Fy·U{C9{sVK7cѽ_|<-W&٢>h7.bY Xbog_ѭWO'l8kn ㏏v7`}?ٵ渘Ssǖ)Β-O:F^Ņ]Nk/ob7P2v9rcVr9՟;\Ű '%?\0j]Gr">h?bw~۞ [ !ر5{I[^.o7ƬnL׭#gfkc0bprk'7gk\^9Y,{ʞZNn]~`>Wхzk+X\Pn^E~bwsu:^l=?KY >$\ƺ[oМQ.Z}'BsRl:;fcxXnFWX(;?{\g{e.M28pbq8zwL=Ųաv zvg^F7N*P۾>gѺ*^w ra=Y/AllϦb2Ehy}[dyv9Q0r/֗_>ҍ(ܻƻ$۔A'{Eg-E_1np^Cj_qˡZm0꾔cه׿>r\o\h[n.}W_fkSk_nKMZnTϷ^ rNfs=~EzߕW]+kĥ[>tLqjm5>)З^<=[ba\{sccTO?:6o2{SKoܜΑo'D cm)}K)ghnjg[hN)9ͳ:۽5{g\@qb1drټmצj%uq'9?Q$&09R#e״G8 Ycmg}־hiohj}kX`u6d{'7oʝ Ӕ%~\-bC8PSzDy.*v1?8H>U5_}‹k)ާ_$.X |>ǒb9~wh>5K4 s_1va\e㓋9֌hnLt}K۶2I{y5 []SMQ~:r^~{g~lO]C:gT?Ĝ􉽁'Oq1-ֽR{_='-lg9*=^򇊣X//npR'0lF犱=N4lec|^E;Yr '3HL5-ʯo;5 Q-jˣ{&)WjWy[Wr1rvY^2JwMnW a{OXZF ޼c\nqr{P5|ai^;";39\j\Ts||9vkvW+_T:ϓ;Ķu/ʵi 0mt^>_?Hri}}KnIG]a1xcƊŷv͝'?~k^mJFn.~_?}!Wo^V39cdasQw}w9;c3 ;o9x1]zCU-c|?}.EṾ҇-F1$͆4#+ YkDNڇg&6nd=ܣ'coԳ:ةGNuXXdcM,oRn2p/^Ѷ&7}= 9}n.wi.#n;WPPu81A3||a+^x݋o r9V9wVv}Nm.GRgs@s糼@uXʚ66yr_=89X<x|񧶾$yot뵦xxJ2߼^kY3ݲ^1?Ӝ`y٣龓7.WR%YnqƤ1蚊Y[Nڴ>7ƭ Ua|bq_ޑ WB:([D{nMg`LS~5ccXÞo[z|^fnke_s#e1eڙ}\?}y/g_Z=-5S}MkSVkLW'ibdiYڞX]%Ast ck{*Ӹٱusz=fM\ti-NP^Lq,>Þ g-,{TsK Y˫n(o0Nh=W^2[nxsF_F@.sV'Ti yHաT$WZɓZ_Z @$[2uX]̖S/Hm`$;]ɚQQycaZO+y+gg?>{O[qoDgݼEX^CpmEC}_t/.ά?<;3ɵ\&K[֎yzyYŸr;lg8dx⮹dx2|T{+Z[3n>l:-].}kCbˑ {9(' ɖ.jlj%;6ؚHrzA摾 W^;;[NˮG\ٴ˘ĮckG\]k]_(N~z]wtL5w;bdgޚKh3OQޜ90pܳ  |bub֌ٽ(Tl>y:|[qEJ&.Skft;_}2RL@;o֏wf^~^>U*)dfgO?uut1 gmizljOPM;bV[/c6J.k.w{#n8?t[ў9N7q,Vp&ڽg|P已9Μkͺ9V/}m֘uғ}-1䡸Շ#Sc+^_}& NkoֻwzAsnΖLb7jJ{M侻5{٭m6/=C>_~|wXL6wvcl-rpr{=`8a߽K_NV;lUCvp[myqS>pP织8 |Vl]ۋhO}IٽʔOw}z`v=l*m+q_{_]*yޭݓ[̪ehQqe^](rHwk%ynCnv|G^PV.?dktLx6C8@kc_eLQOLB[nVKnNzV?sy{7\w؏9|pqA_٤=\2fJ\gߚX/՟<us?,m:﷿} ;[F]&Y+9 S3ϥ,'[|x@꽵Sؚk/Z`~q4ײs~}/U/#aeyMs]˛|Ϯ1T|^1:2Nn]~Smn USxCuz㗋n}eƬPPY.Ѽg3ڹ`go~>,Zvӵ8,^ѿ-?TW)> '^obX׸Lwn~?b>/.~yc]\#a.~u?Bbqlۺus:;qW>b₋|f7i[nzXUP!ӳ0Vjwq6^XW7y rԛ=+닓p5rW{nw|kӈ[:vkͼ7'g+N>\3~ ZY*_{cnwϰm=ra!uݘ5?b 晱U~0cr>UQ-/>%qW *WA?Y1EbBfzhr95cOOs/5 'P O:#eWVre{5  :}]kyb@d{-:~I1)5ufzv\TW269ovXO0^^wzô+ ~C_~֣ۚ>q7 >戸Vҟ Ŋ72>wżϹ>_}]ֶ߳k~q݌s'ZS!%ރ1tZ o8wݫ=tϭ3Ƶ/ǒl6|F}F=Z33>> Ôl~Eoom5`yq@8@'h-M^}?f\d#:8bۻ4[_{fZNk(_duy#%}XAA?t㎎׼Ѝ1v Mk^ by[NktZ%+=s=ﵘ9Gyce]ˍ Jvjn{܄r`^qoǐOWsk%.mNW}l5c.6nWR`4&c:JƬ]W_4>ܼܺ3H˭y~LYqtT>ԍcXQNqWlUD|5O'I=^捅\ܨ[~}^س^xsu87듩oyk]3;]xD1~}>s@qjw>=|p]oc!h>csy} ))gLy_rEYp;buӼtuYr ?1֣g4r7ҷ[ܯq؝q@ƙ^kac'Z߯b'GxkٞM\]?9_[Շh=0e\0ְbIwr@^ϳ90Ŷlf}&L,kQvJW,V/=b5 =Wo]{fn4GG).\Wv}kA_j{[7r0~w[GX'm؎T]cctYd\ep?u wڻkxYì/q|oW1MyշÁ-Z/֓c{r-)ύ[{Fk|}/i9w_O/e><0sA֓.5:4ĉq7>9H(/mkyB^=T[ӗ{5O/87M70ε+_H@ŲQCٹX},amE0wHM+9;}Rq[9 lvG:To7q10_fkG2E k91Gq}skRHc,oΩ΃8ցvN`I?sLN]~Ws4Ǖa}sy$M(oLJ$"9T.[|w^b kK}o!@ +ȍgp{^ 5*oG= |;_&×|/^t Wkh)wM<摳׸[Z,}t?o`GI^:r :gvYة9R{/obޯ/7Z]簇pͮɮt={ج–áa\.QE?u<}gÓwzU.fj#-os_8ba'uk{ȣkC,P>r꒍Ŭ=jZ{<ᰈr{514gE)oqkaOlky{ 9_%BW {yj_e8aY6_Ul/:Jsמz\ }eR].7Hdqqق(g>kkr۾kϝK^x`ܶ0{Q&k~oۧ*ݧo-gI?q&.m|zO󚻮.q[r':+:v򳵷G~ae]m@ax4G?(KئJÎ>ǘq+;8c=gnΖ*)].j:{00ü%+ve?t%gwbߺN&w1]|{>,0ӯmȭ/{8Ó a7>pٔZ4=k pMgy(OT؛2~մҶv>b֓ܞx_=~/d|(,~SVȍU^kRĮhw'[\G~^OyvGk8Z sv;vnb΃ES|/ _M-^qW,߽&PK5"/ l:9ʘet:ѹ)FE۠_Lgngy'r+[0߲ʐ^o}U% dB!ϒ$~qY:Non)'kJ{?dmKq2xH WQOi vlv/'q!k-kIezՊ79TmekD_=CsM6uX&ק}Ŵ;6ney .n%UF\,nm5+~ `5Z rE w$_I>ˑe.:ذ<8hc]AWYdo|qq`발*5صㅁs0/;dˍUme=(*˟֎_onc;GR׵Om[㭯v=]n/:T9]vYr}-Yy~~x=e=\m}G{-v9LYO_sW[3$aӿ _GYؖMQN=~VOtmf1e37'k ̏iz׎5\]>|@;4S +;\oӥƕ٣~X+[ ,WI\psm-1sp״qӭ `tP7]k֟-oz0ŬOZ[eƵ1K{eٸfy룽o\/Z/8ؓc??{ޏ/Ng͏#[Ǿz)Srqt,_ĩs]>xW5c.}TǺsYhEֶk"E^`ϸΡ_#cʎŌ7rf_&;Ӎ>b +c ꜟ}Aƞřo-xb幹cU7׵Գn G,Cuw&|0ͷ1Jck^܀־x{zx߾7oN~pGeOpjmn1p.-'Fn0}WHz[RKS2<71hޚ=꺭EX\W/=ތ{ٷEXvĿ콐~Ud_Y_XCvZZؠAk糄?X+py<.`{P76h-'[}o\`na??'>9zǵg?nWsybny{e%\/yˉZeD6הn,ac +ڜE嘻6 +D}[kmg|`>^q ɹ~{kxY\!=W懆O|g1{.'3&iN {#vHy[n1=}3yb3)}!\Эpz߄Պ糾q@C}ױߜpWϷ|J?37&5}81w wmx7Ϩ7c-\^?#9~;fco}xŵFcqݴ1ݵO|<='W?B*XߋWO#غƗ2^X6؁ұS Coѽ/^q:.q͵co\:aM߶&²/~aӸw|O{61 >y'vWm|ˈ a8{B3k~U^d{:wꗥ6,:;iG{X;G?]_['=_ d%[w]&?VO֧ȥ7>‹玲،dnƜKo!;n,F׾qY>?N_X;yd a"͕iMv /d6c֐:q^, 8F1}^b>~:1X KJo9ۧv-&7:+6cb}q?YA[1ִjsf|{^]̯\mxX;| 2'u97=峽p@p']{Oo>ñ.R4[P} XoջȖNX$Yl|6 }NG)ɋqJl{~ژ׽_65'0[o-nr +H uK1ލSssͫQ.YS]'!ǡ\c,1x3+;&?&'|N\aʶ|d_$7g~/o5C<[t_ +4F6PlM[L֪yd}w)M[O,n9p-skb8=>OB ikv:E,3 ,آlbb3n I{4q ƴzڮ3ڽw':g#. K}}^\VXٯͬp_}Ƚ7;z#:8qcݖ/+d{MC߾{=--_;+}`-9ο}_! +\3׳?]|}>n/[-}S.z9mMl.{N{Pd~`?i{ƽ j,ȡi'd鳋;m|#YIi}t_/w?jɆIme+rJ; +ܹٺZ|*\0;8.ZTCfm7^^5ٔ]rӺ4}}C﵇˕Y\z۬aY>c!'ƚmݚ\ki-1w|1arPGw'?d^zKlpax7wZO?5kcZ{*cA<qͮsm!x_Y5~u1dp쌸r7G 57×tæ͍WM7+Ɩ,8\_|?(w w {6З2&ėn}վ=ꦊ;fWÔk<cZӮy-6'M~ |q +}"vtp&{>/}5x!}e3ƭ]5\vK}knTc6?tQ:8_$;a\ןK5^ˋMs/NGx~wq7[ xc#,PzCHE#^\a3揬L{i~yqw6oޚOtׄ{/'ll(!?uqu1p@n[_nIv$I$]fFZr= _Qv-Pyeb2+h8l74r^׾q&{n{ZHȘƄoğ-Ry&뇊Ǵ;Ux}+ֽ~chTڛieb}b]|\|Jw\/y/^mn~#d算\x_۬1Q-'m_9nNO{~c} kWZ׶L|nq{9ܷv@<ҽOj?bB?9(Z9qUi-+Vwp]M_dm\J,gK+Nh#K6Ɣa}tvOY>5=wCs,8ݸ嚄aa6oֈ{Öv9+a#Ss*$ǶxƘGuvvv{=ms~{pdV}$?Ga>ssz.%G[/])ﯜÁa7c:\pw}?G_|'?_}l?OvͿ]NVǧ8,ފܼsU_iZU}Y6XO0 zݼ0ߪg˷y7UNiXR=CښDž'g>-g},ċmw|iָ5Z\+Jw}MΆ8yum'G{ _>bs.9|ԷO׾R}Vrr(3P +WDQ!q' 0 &s9眓GJkR^Ҝ¨QU˿o+owwox}W??u}/^??}33w:OFgus;\~9^vnwmͳ|{f~}}jggo'z]Kc26z߹x,;}?םu;k_wxZOkp~~7u5c?co???o???{{޾;ۿ߾۾[[޾;_}~ȏdȶ5;玻s/λmv~Os۾o~~1\^ϼγ}w}o[zun~vݿK:Ʈk}}gk99g m?}F^ɤr5xֹ3km15mvMmoo~w~mn߯꯾OO0{ٟ׶}E;=ם}?ϛ#[Cw};csgT9'14d\ {3lݞǼ3ߞgoo>ZlmǶ׭}sX~xjkmokmd~yXv]omǞ~nޑž\$*_Fٹx;cFrrm{ͮϸj]'c߶={<$+ w-y:p}}m{ooo}m;v9?vm=l =hKK>?s?s>>?>s>>޾ }}~ۗ}ٗ}W|~׾msqccCs>(3el9ڽd;R;aUs=okw>tdmc3sbmՇ]yWsgskDz9n;neƎ|$zVnȭek{۾k}}ݳq!'l]'>O~oMz_oo}μvΝ>>3un&ns^@ENvv݃=z}d~^wصoLv跶~6n\vϖ>_zcϾ&ևckSlc+Lnw}kw7nؚݾc>s6QߗVjstkQ󞼬\&w{f{~}6C?C]e~>{f&g8;Fum >6ľ~[)+cwvͷW;Gf=Yx{^oo~,\{sBޓQ3Ahom?~^{>{fFLq'\ݭ5R 8֞kc=68ֶAbC1gB-[U7?Ut>T^CֵOmo%.k,/uL/I/߭Ӽ?aO"Q1X/3]pv2i|hk 6]10|k2l2^q޶ɽaor̛kt瞮?9s|z-oM~3nm7dL^Ѻ<]u >=_wp t+} ٭{j\̫O\>:e^3inaa:޳edi_ߞmǎxƣRmbX=ܝi=܄]}uuycVeo<+t/ڵkyî} |`7w | dQܫ)ұ[vuם _̙⣓W4 mv `9|ɰ}ڊɽql=髏/LcW}W}WW~W}}+ŧtƞⓐkm|'KvWڤ'j d5QS,D+{] Ʈ̸Xמ敹g3֮8ѿWwL6g'ě_pmQѻ[6٧>v-7lh|oǫwyc| q~0 &v +#sP;. T'XacJ#H7Ӟ=y<[}.p;drY j__%ظ.ό}T_*0ڦ7*tegR +Y28Tܵ=nE9c8ҍZ=w9|c^{:]9/'w36y[8k ~5t=+W0苛յv=xΝWw_|Xx\7>ˉ}N&f G\d/57YG/M.sEX"N\|ٰ;^{ٸܽ){϶}6\pk:߲wgvsTݧgǍMǗ,?+r/?V?V6UUElʼroL6byFK>=öOxcw6`|^`q?8I3U;nqcl[S8Ճ{orNjy1p6c]Brqm>=h >sO0c~G~G-Wk~cs_qk\5y_k΍ǹܨ`A;U8OƐjWUr +}mlm3^5;6g8+ۃ\b'.l{l@615'U#L>}Z?_3vں ̦UlJ,|;MDmy7o.'nw#+82[l/rHwcjg4_w-z_Nʙ۳uzQTzseF\% kw|cav}WUF^<.F5sc.47#y{+wsڞxO>;}û]NNcz^v,;^wx1Ϋbp#onf{?ֹ/wZC6ttuӷfcӧͮ\̶',؜(~b/xyc.[8}'-Ysx/ZlQvVO=w~q;i2)DʻFmڐAs%G{_̚ecal ©f`o eU~v[h^ᐾgŝk'dqŴ{\=/ض뺾?齲5=bn8,X\ySb>/nY~YqN84}=y<*o +WLc.oG'aM +ϬWr}H%|Ӿ/ޫ~x׎ko<4c:ndgnWir{n2xmѵ^+WoN37 gG0@x,>&x!wc"׊&,>Ɯ|m{]GaP2:*ZOPW=N؜9+Gt^ɞd6]ێq2?Qز}槹]ڼY-oS]~ˬc_eʳ#opm |F(8Ť}ĮI]v+wy^mps"Y=Y}T w>;'ےPy9۾1_w}L 2kg=vdF.o3nk21ytg_\N5[X]JZWb֦stOjn.F爟2;qWNZm_6X/֟a]|aqX]}[u/v]NͭrZW\p}V>G+wSۭ/rnqX-nk=GW]J~мpnze"Bޑ/AP6iIɺU~5SYE|S c{ŋ) W0syXş5v G"*S\kO+NkR5sU[ ~!nr9(\d0{eGKj+j~~wܹv]ӾuZ7iK֟|DwXKԎi)j7ywK;o=ʣe]tK1փ;(l[[RxU0Ě{0G<ݫ[[=gOԱqbڧ<]|Googq}npb}V7E9V#+oN$vCXnvydhuXuk4wg7]K린y52՘6y8t~-t ٱA|\\"mʏ=dG+\͍:{j,^#6W_jkv>Im.W,W5ĵc蠋-gX|Ѹ7KO|u=&y_9*ԫwXו8V처ڶckvB^oN0konk/[;'7[r.m{1=?vnuv{0a;6 ޷c{ם߆8|B OqvN5w 0P\}]7_.?\cb,ǁo+&lo~8i}\$Uܵyku*gnޕ?j`ˮ{/Ӟ46f $YlBl=bzlwpV,\Űϫ}5|쩝ߚ=^> vܳ+Mar]_?ucVSAvSma`kW\IZ]c=-+OWLy;|y3 OMuô;yY_9z]صcoLܸHe_ / dKզg7ya;UY}1k;+:jng:9}׭E)**_Vys8E.*nikO~̾-4btXy5w=;7uYۯ6<[j?/?|1rИ|\^{xG͍_A(w ;|a㉩{*v.Q9ƽw~8onNn 1 +n{j ;l<򓪃.'/gNO Ș7 J,5,]u6F{26~Ʒc^{՞iLwxp3\`kZ,7o. j-2,(n k\v961"zPb\=a!Ŵ/^ds8*uo>Cx[k꾽bSw.?#G-NpsN<4~tU撷͟8`rkS>LmOE|'z7mqc ڇy^: gS?_X\;ؾ۳eo`{5oMO. W5W܋umw[#7{om4Wqq֪.Ȟko/4j7pO8J9!P~:<-rg7_i<yڧ8`!a֚2{ח7 ^t?pmʪb{\i_[8şnG.I)w|84߶-+~Z?|_ȭCYLжkl/Qr`2刷fDa]b_#@qWw-d~N~NN~:ŔnlNv?cK+=8VC\.~gdhqk7YPi*1q.:W*w>l^g0'of__ uJ.gwll@s7w,n 0vn,ZsF*5Z6W<M>ֶjpkSFV=N7S~l!qPSdWZ٬zT8s>^kZ,kSWk stt@kJ>`17k_/O׷Xiu4\u}ozjku^<ƧoYbCJnxK{SlKnyolGj^{ +vkq++N{6>{}ǖ'`|ܟ٭|0Ýs~r[ ?~~op&K}gXͻ㯴Q}ˁyC㋻רYE0}opkʝu W6Mqr`cꗨq^fӰ41E1)ۼؐ?Z;x,6Ն2Ab׿uO\OA67ءs}?T暿/_έ|ψ{O 0S8߭RXuIm5qhoKM}}:,~,/oCy"|oqß]J)v~8;&V qxЊP[n,%Z󩼮'>]=}WNJPw0bOuF\~8hnz':r+F\9辎YL(Þ5w\>{u&Veo_cU76NMƵÐ]ci]͝sp.ϲy[x6|~;̯u.3T?]B=u8w>1q5;[ܢ㑣>: a{;tI 2#?kݬ=tw>(+O˳cqs wۭ~cTS/Xy‡绶+O]-Ǫyj׽Z[}{C\urm?9n ح|XS=m6ZfaWok)礵Z`Þg<w\9m)5ش;gG,՞| TେgnOq'lZwN!=^]\rv쫓6Hs\od^9V2dqښ{6gY̺>& [޾ɧQxn|y~^u #նm¾GV ob-6Vο õپy:] . V.V=l> \n:=+n;ыqxrd2˷nhuYNGybu.5VvaYW.=qXc׻Tu^>Eqvwl.\D@ r/5-|šKI~d8aۊ8߾0*w6Upwuͷk\wduF_L/9Zۧخ{M[V +Y{o:gkb|ݵmq:y2}i}/yn&L4ﴲz8q-;no]|}67f."[Vj)][sȟFU^YB癏bklI!ϋ65X~luD^wA}h}u^qܿbw]\r}qCkGH>4W`Zս+Wۍul\gUo,).WܴτX'Wq@ąhO8/[ ͣOdo/]Nvk3yW/I +ϥ(#tIL`tE{auv0ʿ́=֙'o?<7.g>1YZ[q֮b5-8es/t[sX[`Ϳ]\kH7o5զx5^Z9ejĸ#C-h_lzSk[GT|׋cac,[ϦCɥ> wjH[Oߚq`VanzH6D^<0"3oܴw:W65x#^]uvbsk \obA改B$ܚOsuZl17x[xc'y \9u/?y*G` b^ֿbC~ӼoYq>yyWGQg~9Q\3Tu]t'O'nb@SgNw{w}gZ8Y+enTe3U3CǩqrjU57‭jWx@1bxsɅ9[[v`k04|Mnjc9h)9b2.|ڭ2YyS٘]`ņS18gVvO086^zsPV>h-@ܘԚ>{5^Z>|+m~ywyud}y֭s/"[=ev^ٰ|v6U\g"O&[ܪT_1Ar oG/._VL{c=.{X;.&'n2q=ƳUwצ]Y@4Wb\X?m٭o]O]uو4싋ٿulCkcZαĩ[[v}N ^z#̭_‭Y;?K1'{V;YV@XgXdwL\nNXly͟*KX9.k {Q7n8zDgy+5x赡n9G&GA;tk64vrksEqrWKm}K‘[#?n|CnmOZN*@<=:Ջ oLtӳ:]= ?gc\E߇W9nbH +c5}]_6n ]r +k#,XzwR_5RTΩk&Omk>uie +ӆSgmzl嵊RNj˭>|ya'5WcM^4z}x8ߜ@yqwsV]Ŧ3rW >7}v,n/m;kl\sۭyb[/ȍ*Enu41JNa}+[׮y5)v!)uy}ȭcsPVf+Nb1νKW' GnNY@sׄ6CqHGHqtx\t+.d۞KǴ[؎7_ձmmn=6;j5H[cEֹF7|,zZڳ/_xKrn]k|Ƒ=>[ro/[bUjGY[xiQ:kgmY+ްλʱֳ7m|uGg֑lrR],3$go?gĭm-S\RwtܸDk#5W^N!&+s.?__k}_q<哴RkBZlͩܺ7?׍㲖[gk/~u_Y\n;{gw;/^{.xbnϦ<;6&S\l3b$]Ga[NXon}y{-k?ֆkc6x*;|@*#k]^-:)U|s%Vɫwܺ,ˏ6#OHĄ6ѵR}`<ٖS<'N}չu{oZnwO]|Z_dF1/^9O~(X\z_?#0{rj~xq]ӂ^lΗb}St{{_cW>,'n7zK5Ꙙ7-5WNl c֗82$v;y+MU&jJLvo[vs WS W{x Z⮡:o26qwpk#{KxL#EQFerZ.]vݱ1ٍt&ms|v_|ߋ nob0r?_b$=|dQj{^xcbl0ϸ}ĎxlH<+KlM?|rnkkgGmX <\U hkǺ${wZ#ʳjo=r6̔]7T>7?m,k_ |9!XivsbAw~xu\F}ʧ#b`喚8侧[tҊ?做7\ 2Y}8lrOh ^:?܏r}]m\r5Ys[`WhV?~T^76Swsn6kr{V8(9ٚ~JǓG?L>AkN0O9{>L9*bRylr`}\3klG{r%RD}^?7OS˒qrX{^ ~Z1xD|ױuȗn[۶X`]gk76[v +;?[+rJ^?yyیiKkV۵/ƿl;Ňkպ}܅ +6n^͟wճk7~tGv>wuظZ9{_p<ƭqzV>lAT'ا9*ŲsO57G[$]5^_}>m]^Sq\<^K+Y랴?̭y\rsl#˿6@u*)ӭ];H~9Nv^h_m.Ņh]''Yqc:&~k~q<߾g;:?M +/pM`wqr?:=廵/pF^kok[,X^Xs9#Q}ȷ#n;%X_^T-y?qKbf,W{7OR[ښT˾,΃\QkϷLj|rZGRo-S,򀷩( +ܵ8\6N{&[?ھjn¼l<_k^G7}Z(ֆ"gnݽoD;fx$g<]Ҹg)N./L*y j㌶柚X"r#vq~.\ml!5y?^sy ȶOc7CMyM-)T|XQco_ѵ|S_+w`}%lbww^qMi'\Z#-' T֕i>)Q?>u{7',Qrt|onO$3ToV?4pe39B._a`vss?LvI9mdWN^ejk1^r{ؘOE^_b hlNdW[ Ї^zr~3>j ˃]\`e5t~~foU[˶kԦ,~Svl߳wxʵ#3{{͙y\-Pl5Oqʓʑr*\9y5qq갫 ^'gg/q>⅗wq|'=~9T;ЭTʛݘj/F͵_Ȏ3_i<3YY9A,KstEp`WQ|׫5Xaޓlm۞\wݼhFouCϹb_ɍOڐQc!>Z5V,)mnc> ty W̯+ x{*^~?M̽g&NMG߀Cݜr,Q; +4Wl͎}1|x]bPEV;- +s`[pq ]ͰoQ5̼=.g}y5u~yH_\XeT}uslv >y-="|P3a> prǮ9웓:do n|3ʍ/rr 8=QjZƄ?R3ͿјǶsǷ^skZjV>0{q\!Z~\(n./덗5>.s1b zYTׁ-6yuVU5yQ};v1dr=1!=swUs΁ƹ\Wkv&o˲'u~[37V˿_z >%n||>9>![cl9s>W9+Ry^۰^T\k#jX剗v8h ˊ CT_^#Þ>k8)xOg~~\*럹{q\ܮq˧O߆T`}ˑx7rsnO`\@nR3zl"y,^n}%8>rydヌ#{}/ {VNY7^5[ qhnp7wڬG\/'|wvUn<9~iew=~{W|Tӯ5ت|,ik7k-Pۦ}?A񕡍UZ]Ճ֘6=`{F/Aqֺ.U۫=p+'Tg+}n?\ga9l\mg;[5y|RlOT\}BnTF|s֧39?]\Ed8xB5.<Wp۰y"{[pnw$ʅb/Q^4.ެOhUV&AjCWkSE5xY?rs77֣g4hbᵦXx_~a`}km}L8XZ}~-pm9'qŮ3k\8'b]r{V߉wy'#N^?72S:ukjö^h+w>nugj-e6wm$8Q }ض-c;/ex0>1p>H`}m?3'y]魿yk޵,nXNG["ٚOXUXc ܫ{1[ݸBo3kmrvo/Y\z-+&[bo'{ c>{3 ?{ª:OsmӹTx`qbn=ך\w~/ّ7r`'Kɳ +kwj_ym>1,6@Aw;X^^bk"Nbl;msP `En+⁾3S6kˍe+Mʿvq'|ʥwI֚峴jsso} ޜrX+577ݺ( ֚b๗}հ=kOP<ʉ8TIc^~`^RQarRzz{}^rɧ=7pּ:]^Y~{O־\no]=pGn\bc'4vz͋~{:013u-X;2I\ %%N6n/^_,jnYk7(\?zwnѴޮzo݅[|>~8\Yw}eEy|RyV>'/NojmonwbRCR.G{%EVmO5Gu:oi߾>؟Zoϼ[?z]^F9qs5ps zb1]>eZ˷Xo_ykF߶;a-f\qul<˜ VӜ\ec(.5X9oV-xw3œV|>~v1W޸bn3w3sB 4O8̍[7`|/&X=ml@/g(^XUNS}+G{`oh}5^e4g;)zs.:+o}^u>?[`iO'>S՞}5޾l^<_?b@:v,*sVLV//vM|QS][b{O|C=tcl]6 u|'=zwl9q^mȭ|SNq#`ub~6o_~v}[Η-޴k«iMM9~: {>698 +|׷G{|Wm68]Rws"3#onmS0&B_U^{kjU5X>ͧW)[t;g*oņqmv]^?q۫5I#+`[{Ͽz]hSreY}On.C\נQ)^ևˀh 'ƞ).nv@k _ګ=):s2H 栒=\}vłQVH8]m|b/ٞŋpt|@~Lp啚Sd\/ϣ\ˏXGm&o.Ze:}kSw,׫cZ^8YVPǎ9U>=[z `j3,v.[tIF5x.~VQ[Ʀ6紜 qr/~ugڮ-̯9]LkV<|sq'yykPgVsrv1ή_R֯#ˏ_/x ~_lCk{|:U[*v=7Y_dڷq.r1>1rIb+zW^=SuZ[7PC>:erJ/~qks6\<7ZW}7ֺ1^.?cr\;bWZø>csorhfwki]Y{bm\Y)jF>A1u[_yl.H-Z[E|—iR0/^=>]p{zbl]sl-O>o]3s>XKT>ck-&Eͅ5phkhO<ԭ5u{{,C]0p<198!rmZcڣ_Wm}lcwkD9k{x0*c\KW7ַ=.֮|c*v&ebx*pl<Ȧ[#V^|NϠ5l/NgΖO.n}o4.x=~qqbu47vohi-VZcUcZ 3'P<9N+fkGW:T_nhߺj<)=X?ƾb'wU\Y=q07][g&#x}&˃k5/pW}nk:Nmuv{b}vp^Ks ,fx#T\\u&nN;PYU# wU.][7@.w*vS^:7S~>G,'aO.%&Da?p߷W?{kU6*P{ky_:էi@a/g>2.^)8D8Ʉ]wChc]sk>Bsvm4뢹j@~VU'ߗC>'7?ӵ}wyW}.gε{5 +ȧ!j~:[oehm3߸эLKr9zflbrk9okn"'4k|C;?qAvڞlڻ)'6X൏k;?iUIzGP|)ש~BnXcq' [s{2kPje3Y0޼S?Rl"o~>'P.@4fR7KR󥼞Uܹ`}:02K5ƌW,^Z6h(i61{qr/PrL[;>57fh>S_r}.6J}\,AXY2m F}5wP?12~;|&>_9$Ưncҍ]]qi #xJw|.L kqz|I+˵O}>+^GzJ|}"c<]k^}Foj{cbէLvOmSS?u.Vh.}N6n a/-.Ɵco7X|D8P#vx>S!A4/-|n\I^خa&Wkm?w9ϷW-nzƣ:Q_)rTwmTu×lC\}ϐT%Qk-軛czfkl[?^ҾT +6Cv9 +Zm\jn~P5~r7hߑ3o-K6fT#:^?Uc032,%{l/.W~Yuh7>61\UcWmAr Oxn6x6ɷ?S_K9UM,އc×o ,W9TnLD7\Ʈ`OyzCu}SVw.P~Oz}ʱ[HӺ-;b$ +3*&,W}{O;̱rg7k\<~LMKsG/4ǣ)Ogx{psskn1vh_]{)7<+ʑƷhE:X |h{N/ZdswHկcyڧkjn^n>nmkgk?[9WLǔ3T绘1z9vnvkkɧXĕM!{[cǠ\Uf-. Ա&;j:)zў~|k^<uon|%ҭ5Fg78z;| ]ojÉTӨ$.V^XBmLǼt3Tk(fn{oqT 㲓[ӛ01> 2f_›!O8ߕ ˹+IN{~Acl UnnSPm3j=éVN}960g<'k#XgʭV?\܇kon85g5^ׄOnLbj#ųqZ/l,zXOs7P/麻f;]_5nhRc,[sSN/~cF*OO6=յ.*_mq*r krٖSo׺y_l/gq&/=b~ݯk%[Ț]ߍ947jWw5u1_d9+ /z/Wq<׾ׇΉ +Lܾ>dqq+/hUi> +]1&]BZxH{6֨O{iL\6zs=v9zZ+-왙3b'p,G\$k==֟/v{^_H1mlg>hy [x4ON[|~sqXoF#nXjZ}0oKkbݻ ;xq}vdⵟ׹'''սߵ˟bWձxXژKy1'7ݚumr&͡slcsS_r++vCO⨱ssݺZW'~6߰6i\/~Yy[s]rڼΗ~;6<[ovra'׺*?^igl5٧#w,xf1A)'u;?*զGq{k=|_|L_gwb9`λ;W<'^ȍo "o.ew#6ڽT[Zևk[W5Q[#Gzd?QܸjA_wcZǣ5t1ۍ>wo^ >{n<}& ~NyT駜>\9sxz(sZofmWgP{1{;?kR ǫ5ZMm8t0>/}][`Bl?$ȟj>(0jm>a<7)fܵN6|k{] ,v;2sN7W>;Yx}^y5{1[_I_?aw[cy6zO$n?57{aȽ"]_= 8hjIu?=z(pP}o?utqj?'0@p񠶵<{ k\^NbQ ?m||ͣbvc4WlkyNcU,X|^[eZOyKutklV׶,|1G|o֡\Sua|[w;cr}_cs7lL}9+{6×s[bw/#]/g6k|-Wxﱟ_b)r YE)1AޗVi?Uk]q'6_1bQ +lXKv$U+<' +Cm-yD!=5lzxqc־ŝu&[čZlj.~k_hW勺s{'k_fnnY?낏5®~mSnL族U?\?6|\ u]ۊ~G}_ݞ %b7̮OԾ 씛W{`kgrg[O=q7ËOܣH{Xkصx-~O+_t]1@p=Uo~ek_d\RuGJ?6[ Wk\r4uc/#_#s8V^$́)WNDnz9Y4͵zlj 6gKtSn]HϽ5TUy/Ƹc;9O֋$aam/aT_Ks4|sbٸa.uУY7yGεuߩ,W  Wlۮ]9|yݸnԗwPr8ѽi=[yZSpY!n1+Zx3ׁmZ=.n.~Cgݪo]wxS=z.o8ߵ\ּyK8;?fk1'TkA6&1ݩiWz*t rwmgv-탲s?טkq栚Ykψn=<W68iݔlm[[t9&{i$k{G}ʊO\ƺc6jsy/=C|ߓ'6q"uqbp]k\Pa zkGy؝Gnf7gbsL}5? />Xܺgf: ]y1) A˕(.{sX}ucxt܊ټ7\Oܯʁ;1ћg|m/i8>q%ڰN#+t3. p@zT帛Po\Ikҳ 3.6&XnucUsO1~VDW#k H^ +䔈ɹNׅ5ɇ[}{i{|n\*7{oRIkMwOq8(fY\ld߭بZN_sC+ueA3ܱeM]G@;79cN1`K|vHkny&(s}sBp>q눑?y ܺ\8~ <3l|>uUX@ٺk0@+14w2}z?|kwE֮)ڿ6u[OQ, *ˋ=h\{(v S/|@򶼡_?7&6lId+]ޗ'*lj +-or.Sq=QZ1ýmZIQl^sں-GՅO~wc姘֣SnPump٥倉\} +(綽˭lZܦ#aq9&η??}?!7:wh:NƧ0cߵ>eW{~;ܚp2V{6QSpkֆ!oo?mޚ}㦽?sk,̚4 xg6==;T6SQஇބ1YnC9ڭ>6s<Wc}q>+?AOOt{5+mw<@6;3W"[C‚+@5:g:}-:t-ŏ%!OpWWkG[]W |u-V?6r9+Z'{ _Nu˓j6Q1k[ Gh,nWegGXj+?ı[WG΋X +dc{q&%ng{O8 +zl@XzB\>S׎.ޚZaggPy{g'9Gr[wW|Z|!<= |i#oo܈[[k65>Ƕu<ٮaܧ9^zFjN 5Ո}.O~=q~ ?}m,&8,[wbN|{A5נ{8#˭,sŎwn<)sp׸ўY]uoߚ. 4>8Ǎ拇]W=č~2쳾ˍX\?ruAcO~/8=^s}F^롓ۚKWL]mu5oqrjC7nus=C{l<o}}A{^?(ިA[K^[kk5gpy]}bt}V۷ؠ/\{[dvd>;7vHsqa7/ 1SfxVlsQL2سԣ lbw GZ㙚מA1EjkVo/{:ƍ_Yv-~9xSQrqRqSxKd9V{õ7h!y +7,ph7^`|b:T6(jn?HǶq5ǹ>[}/'hM>V}$s>Gf.>:@|*5\].Ss8]s=Fͧ+sZkKf͸b,ۧknGۆ-x65Z'P@|A<sy +}WMs?=g[r{ٚkxֈhߞ#sYXѵ7:!Izmy^x.";DҾ Wǖ\wZx4 P|P(J.oHߕӘNZeln}_QIߗC |+/Ϥ\[oy|x{ O߳q >ޖܶٗ#>ۯkz,^#k |nݿ9osċiէawnUo8/qA;Z]3 ޮ֒%l~Z' w(u>ٵkLnp^WO{.Fz[`,+ݦc2QnU&]n!=R:ܳU;p%v[|4[w0l1ߵKkHw9"l*7W4nܸ~y!s~Wy?Z۳rj.w=/x{;~ӱ) ˼fcyu:udpkP7$Ɵп nעwˡlD]QۆHqoqbo־u?uIZOϳNܭu +$7狿4߰LWlnrjr|~>1]Nc7//&H5ښ{7G]o:=}|[xD·֤&lnM532WW՜jr`JEc7ܾaçpcllOB6\<.W,r߻}6ǶƊ;nm8kڈ65|8.Ul +s1_6}i:y1iTvԞ<سm+wkɩמ{ ^g+u nϫƆQ멍ڭ}Ul\-ѾKZ/7@︪7^_̧m-%r}KTqQ/N9ܣ!/Z<ɷc%_avsZS5:f|mS}c۷zlu]pz3<#F}MϿKTl+L71nu=Yh,r.=kBK28DE)̩5:oč-.&.W k|kjO6։|#s҃c'qp9y{_ζwG>)xӿONWטHe.[ܬ -d\9#vcv ؼkuoWg~mwϢX^yhݧZky Y}y1˿1ʫ{r.&YߚiUf?l3 +Zׯ=n = +UWcp֐k00h[qu彳AZWV~g1tMs/Bkј9b߸5ԜN/ר6nx?IKnf9_5[{p)_ﱜ +)[zH8 e7giS%cO*tvR &vǞZ1o|/֒lkv/+G ;h< g"N=T*) gݿt7fyz?'-3 +[MUxĚWz(ǽܠy0Zּ{pqm\oL1br=o&Sh6n<1;z-I=C98ʎVP_N9zWW(~NwzYOVzgw/'GۣZc=Z{ϛʱ\5mԝ;//qVd>5'Kz4W?'h%0^q 5eMb ^-߯tEצsS^w}y]B ݟQwoʪv|qz4z/ q /904/>Erc-WUaR1,Z/^:x1?}^z:-r~cDkhexp= 7Z0εZc _O_m^k,KD,BXD˛kgTvԓtc )b~ [+lͩ3ؾyH*=-P0u/ c횼|q=^޳^ya Ouye/(Or8AtŇs"+(_)[+gO 1;ŷƙ;u6=>1ݗ[|K{ȗl.TGFq@m{n_b{{cf[om-}ʰ 5}잉#(.(^x!u1:t>R8ߺ{mƞsbo]^иn] p㭾ղEEO83=$h.޺cSs[?ɮu7u_|W.=&kKa6G~Yb-K=g<Ѽ{?͚x=)>^xo^syA=ĸ^l/#s16Nqy!5>r[z\=}u7ښyq~O{z 1;lS?Zs|QOie@35~_]ua6Ι/cIDI}iҧ'|xaVj7ç+DXip~k<<'[?]_kĪ!*~C'Hq}2/`!k_}^?pǪy`,87vQ_1/GHdqI/~Qu85uoEZ/ +ĭKx\  S?l|=Z?~هD<0^kZ_9֑/|PʇOAw{XgI}sf/ǹ,&X]lmr4Ρm1XYϋq-WZj{I\YcL \ҚcWٞWoqkm)=qswwMڽC$׵p]tݷƈq־a^qnּ͍1a2-kx7קSh{r@\qmlo;j^PDC-W4% qy ֫4hmfwYcvC []<{\u\a8^oż+g\䝿b3Qϩ.cYr8[ غs}xz:8ǩ1]hn'Wќ9ԫXXu׼)/\lzuե?|bMa=äv[:vko ';+K})!㗯[K㮘UH|rx_`9sX_-_r<>%nՄs\_5UR<ټkqd'gM~@s#0^|U-]ͧ1 +׳O\->Oϰ5Nj@lܹsp|D1O1An27uuʶ^A){YVҐ^~F|ӆ?"d~f[4c[׹zua/Z,QQOGqn>j==mE4#*GGbq\z{5G=өzS2le42.61`/b~⢴[{5Kw],7ĝu䄊oϿb5ݧxpCcjXA^oN,7Q58RSWUODz׶ +UvLk|gDLd5  {Xe=$q +0^tU1tlǭzܵU9ZzW.T .>kW}ޗ6>[a]\=8'V3/+i>w|xX޶2yPS]h1?#w93{y[^\nutG5#}k[J&Wirw_$nb^jYb Ųj'¨>!Vm,$V<'yk.64{Wjm[ 0ί}?W ̯ZYk=\@}Zw\knqr9ViOt^qOٺFåX^F;\󭖠cnŗۇ1*{qhZ b4듷<}7+a|){E:F +6zׅxkeo^F>鋛oT'g\)~qQ٬U^饭Wz`'5 ~*N,:ԚRl"nŗ(,N&flW\q}F:>ʼnӗ]mzrP~XηA<锶H]Zw\1zf"i!y65j߽iL$Oq8P/~gozzS737E=pcg󮘂X㜵q1@rfcs֚Wu5Ϙ{}|[?VcSf([,8_vX3W[/>۸u?^35|D-gQyQy4v{~7gx)oP7#Z:qCj_a9FA l-*V^{qĈԾ+ W/.W:~G[:׉WTE޳^w=~pu*[ဇݺw۟8־0D}ޖ`jӠsm[O Zo=9Qc;OVcVkor֗r1&[/N>aſ\޽}إ_b3c|Ku:r 홾Å#VMX\k18˭a7_lM=d1jiW[{=1. ӹg|q]X0r>5g|/Ή]gUw|oPO{2sCo~[\P;uPoG_]gy(mG#cc0Q226k|k\~?Iw-wTn+n96_seuyVw"??9~}a}o&϶s=g`S^?QtIoHl|l~jL*?@p-yWo.O8OW}yzק`k_GDFu5 ^lNRp1θݵZ7.z9zCGub7U=GO˧Չ_gFm/㶞K#\z{vƗq9s|{{u,/Ҽ8Ѝ݇|ǣrU_os9y +4z[XzX^Z<µ[\ſŋŪ#ޝnFr +9\Q bjjNˋ}1^}<ŐvL8oZv-f'Y!hCA2v? +ϋ@ͧbg/tbzl{5__[p5Լݯr#jغ؈Kzǹ~qDkjG!-/d<f28homQ[Wߛfou}yu yz j.ܪ1%4Ly [OY0OGxXxmLx #[n+zYk1EO^oŗ}}'ŭ./mqϫ&Wa\8oNtN75n\ψ>/7Y-}XR=} QMã_Z0C}=kS֣['ds6V>P<}=y=Ul3&Z?by|}@{_ox|cz݋/>v-h~#^uj~/Xqu9QXsSڧ(_=l:j1<؊3{O곹8_^sͶb՛?.3gӫόcm[5WwFFQu!N{qkܽ8=ֺ4% ޖ>ף} hz[&\m[Lk^!/녓6[{[]tY:ocQݯks{_r|_8xfk7<)aΰ52_|^KcP18A:9Yڇ:6[ͬ|t[z?Zb+1zˉ-Hs`{~71;T@ޞ=߶j]%y0=Y~żvznz&V/!m>|#+˧iÎUZCg;ml\?4;qXo ޸['{*׋&n?󅱉Y/Cka\Ѽ] Uôb][7^6ojN[ W p{4w_[ڒ{}:qh_8`|66)^Wr=퓽xrcxz^n-F'ų̽{=GF9|\UHjLmd8rR_Wݺ[Pq{^k\<㽷QUqk<c_=VibcXbZhiX|k*.U<_{ j0N`Vw7qU 7z=n\ /QS|-5[OΤN~1y[r[Mzu.($sGo0= h#o a!|b#9ZϜ^>TܣOMsfXcŋ8^;I}=7Fﰞe֫SW'׏N?}}׺JCh**WUoprR(OHufSxFm=1wA/0o>5Ņ^4&Ay]=/>uk@$&W\a5ꨋmӽs_XZg,zXA=a:ObyTO9o[NXj|]o8bj*ͺF[O|=C^s|>t>/u뺷kɛodϹXJ9օƢE\A1>U,XnaգQMGq>Ѷ"Egqc^BܖWlEjq/^wW{ZnRsA'WFrH:~*E HkOkWRy=OSjb{9ng[ zXO޸q Z nq&fƥjkznFG9IWqXn>I¿W7%PzNݗ1xkxzv{_W`Å Z_FxfE|ߕ׃q׸R>b:75Wt>\}[q 7#\yxqӷ?8=KyYI%W,7z}[ck;nƾ #{sW]޻τQ3m C5ߚgYh yZˎz{VGk>i.>lo70o@o8^m>Ld>ao4o^gy8V?DsCdw[b..(x>$=&8y߳cCu?,~nL|{f5YkGu0]ØAr8c[zw/W?q+QS8X[-D]X>6qY=1[ߑa/ڜH;bq/:Ύ_ =Zmgw)|yߞpsu{|'J}rn8=8-.VzY'b+WapWΑ͡_z\!^m4ڸ|Tc>Nh9z-Ϫ +K^YOĉmrČ{ׯOz{ (VXFuQkG}rv\[/U}Bݯ|uOի/3Y̽ᆵu|#C\͡_}[ǹqx5/@{z8:/|/NE{l4Vj;"-33wi)_!忽t2|5Gxn$|dp.'v}6k̊{?=6 C"v=RP/5͍؞zĥIP}I=گ~b@y-~p~qP7fθ؝^9W=,mw|qw Mל7"C.| +_WmXZˉԹ:JR5 wU$c1t9a%wvX.R<67X;|{jy0xro n\ ViݺO0t~b} +WާwY}4i_o.&h\v[|i~wm+w ,fz7}{>O,^Ҝښ `~!aJ[C^~#f{A9B_8:ʴhzݵݚkX͓{Gi~/besEĉY\Bj_ifyq̩W!6w$|Rތ{Y {+:F;G}W-^nXD s35S7J@,,þ} m[#9X~4[],/5}OgZcdu}-EL('~ұyXx9F^0G\{…'4z_|ViF/.g_áoS[K[#ޓ`{_xޖ_`\۱ի׍rNWǝs@[|{ܶ_‎mq@˻59Ѻ|]έjʺ˛~n ny1{duZvSwq?O 7O1Rn^h\ȯnCW7t od{pk/Jݤ+n [Nְֳ$mzv._ɘu:ծūyƗ-~6[7~pLz϶wUQՁQ}|xWX֦{>br__pc{ۨ}f0R8l̕owK&딭gXOq>7owY'۵W8&Us/&fxȣگpV:кCb4}yy$f=@רx}Ԋ+ŋSP zr_`_0{/l00A 9pmZwor]ITb&ϵͣZ.kWI9-_ &6^k[QOz[km;~v]vomq956\ok/\鶻'޽Q`j=7ЦVx8biV[w_HəVˣ^E.YuԁVw:ea׸={zl{sڹ&~$WͿK׋S}>z+W3Nk/)jŝ 3^z>v; V~_c]^cn_<. p5%܍}ܗjyzrV=qj-1ik^-3)܄b~O>-j %a!mu/x~ʹ^qo'.*iZ[_q|W~}_]nXNxPj˿GVV>/}AUXMTz}rTyYu_-7O/o سޑ&FL ̾Sm%=};?=6v:>{#CݹrN{/泱R_ۚXעu:|wRXu=Ьa7o~I¯ŴՈN =w,^^vۭW\-rO :g|^vm77scU/US*_h.Sܦ^(q6^0*n]|V<]gƘǤ~7^@E=n]M9~q`4G]\KhfW.:`ǜ#FGssZְbW}4ȍ#OVY7_SXބۏ(Ub}j_=z a4z34u&xJ,/9Gt~ 8B[3vZno :j1;GOk_eyZ]> i{mᅽ y-0Cokg'WɎӍ̓wvO|f-]oYG,ާpamYg=͉ͫX/O#]_5grVhL6> 70Ypǐэzıw >rw46kfq9;'crDt;A| q[V~j+.c(d}x6㞨ےY^`/Fp'GƯتy~ )fn@.C8ףܜkZ01b.Q]3E.ޜ˗.g6{x<7nxEb\W+Vҽ^<]X䶛VӜz%g%3@[9Gq6O "}z#mcqR iϐˍ m˵i~k7V|asuoa[ˇjyӼys\ٸrVi ܧW$f(7\ͱrZqr9SkK; X= !N<g8-jοc(?7P`5w~߰z$3f^b@);a9A#KŖ*,^>Jqr),n_IP}Xw5mxz׸s/hSgmj(3򩶞Qlt~1u1AWmeAmؾJ]Z8_z'=n]0//0;4sڟ?|4ǽsǓZSƗ^{Ǝ{ It.uT幺F{ɽŌVQ~Wku5[>kXa qθ.F`c4"q.L旇ˍR^V<+/5qGWHemTTY\4·h.\__"FT>v9ywrѵ=m{q?[*:&(NYO͘\%1)61F7ƗrǖZi}oܛvX[:XqE0@J[ .Ʒ}sK;o/GOPkF#GL|1g=ÿCWOkq|vq?qhm;X[ח`W_3⌍љ|q 7>đ8u\+[(zoV^B˿Ø(Ųz_.cc7ztS[7vI 8փX.vM}j㬛vM̻z_EQ^qqXmosle.P߿ScS~rJ[ F^1wp? ܩ\7nPM 5NZSUؘo?W,aSu~v|f1XZ`u۟ou]o8;bz=w+jMn%:zcD j'SamyXk܄ Z>rpWL0xHNz9kɁs׊OwvuO~97-)g>~ηߺ;/# wy.=lsbey'/mXث׎Vǥ_NL5!qq@b}[}X {y/Pn5u.Yl4/ߺ51)F}.d2|IӡQaBk׬GO᫖ޖU~#yۻ& }5r@vL3ژU pnyw6~rdy-s]Y@yGin,E=O}ӑc犑uH؈ZDsG{ō^i[S!ۗ~>~EmTLϖuŐܷx- 5@}cTGgv47[kXlYlrw!LdqY^}}jtkkbNbgw;ߩӎ^ k}?ʫn˴'6;/]6rKcn}q]Ӱv.N"~8,R]oa"W<6#n$5rU{l߂xY?Ks uvËg[ ]|89{q|Mj ԓp{;3ޞ9b̎퍳آm}R__9vO^~}˟麈ik8勽؋96^ɩE4qk]qzpCV,صYzXK;Ǽω/:/Sco\7iFk*z1w޴*~\zjW]E {\i<<rG1퇞{ Y*tfTtVz/~a ߿G#"~Em^Du{\.5-oyr^멣W+6n\-KSz/ZO>k_]кNsc]=ͷ +i[#p8:gnq<嫩\,ؾ&'~׫o}|{|OGQžq-~ +W+' j+oQj"''˧9ZǺ:;wo\u^ġԝՏ~Ԝg=D!NY,t={{ ׮dmX?s4s_};\ ?6sQj6nޖCR ^߷X}iCo)Z7cWeu7bOo;Ϙm5Yzi9Ť%Ga+/is.x5ř9lJ5qzp;/W,0\d5<`\蚻g俪ŗu{wܫ_O5+om uט66pPPM6V/#8' ¿/>"c|^h;g֪^G^c9nkhqEه- ꇷ->=C*s+y`jW[fz3m%jc9jb/XV~0n^->3fx'm=5>O:-T\&=Wm\]6'~ݧxSc59|%/r)}b|z[;.cl/z |@"56)xeY>E[Įauz.(W=@yiQ^2運!GV_|*[Q}[O0VnMcoF;X} <`;^νb=Ӵ5W[)\qs|_Zd[|X~Ӻy+/L@jP4vX>8 z=VG6O=|دnw;q)#iUi׫DZ.5պuO\6X~_޷c[.zn_ݾ/,}{,Q W .UK`}J[n='C{Un +ՎŮꫫH<ИQos \?_|hy/O鵉ƵPo{OؽxAo11q{hi=%Ĕ_~'S+xOUwvR\:c)=ЇXMyM}o)Ƒk= -.Di/ߪ'g`ǝcoceٜYP/Z'qsuWYMr gV]2[ݧ[<$"9run܋hߏ=GzjĨߵ=g=ZՒ-m,w^3.sws/M溮^tiC/^:4b]XJ8Ovy[{W͉x^y_<(XF@}ZSzOyc._>J&6vԹly{/Z\ؼyE~^牷f&j +ӭF<rk5Ly.en!>k5y-n> \[ϯp ~k'GfK O|:ycϼ6>CkU'-ϚDkr 4n-7whcop1j ׳ͼ80_1q ibq0q3uݦFTe5sޣzٟvu!wo?֏ח}֋n_44S$Ѽ_=ZRTRVST[Xr)=Wm/NjRc\AyU~߽Zƞgb9۫5J~zύZUcgxz%^ʭ=.V*\|i1c?skP[#M_yŊ,7kqcRrm̉iezsFcVyrߗW"c_U1G]r6^v7/N 3W[\ON5/]_[_!}qbͽ_Ņ^^Qs\}Z\-a^^lZƺuyb/Mj_8!'TV';5mMElW\>y|;V[o>{XYna19Kbr_<k\\eEsz3u=q_]훳6Z.>o[T/{yqլߐTŪO>YWLv){axޫ}K,/bMIRt +YEQRi^ 6UUP`!=؋6 6`uD㛧<+>k~͙3s9_ُٟǟ/gs}7{}>?'+kkO///>ouv?o/_7~7>տW_oͿ\~7~>ݿw>r5߽>?;|Ϳ7}_\n[=[_r{z?uw̻;^{>o]Ͻ}={?{Lϟ?sMvqu<~w{.wo3==Vnٶsrq9rs:;z]wŝw55_?ߺ^{v랉ǬϺ]:w?_}վ}~//}._6rϫkW~kww}>Cmsg;]ޯ-מvǸec;u筿߯z/gǿ{ǻ;YuUݵ>ҵ[w~]O}vۦk{Kеƻ[غklƄًg3{|-=c[W{g;[糪-\X[]KyLkS-ζڤ}6k{j#K/d;N&s|'ܻ>{ulX1>;\[mcn|[~~+|.>}}nv?3?Crǹ3ugp6>]_yw+[Xy%>ks-9?}qo[ڨtٷh~*ۮ+ߪsolOhvqǮrmS}~|#U>}vYydFmw۾}zZy=:6SɾN:>`[uWܺ>Ccw^o:omXǽ6wۮw_7{qqY}cXxu_w=wmэK}cg16{V[:4 +=3e{kvxU@m+/yc}r\n<%喟\w?ԟS?????G~>o|~uO}O~w;~_Kַ>G9Qvg(clH[-1-}>]jg8;i|=~mk{gxņsm:_xڨ>`1KMےkx q@8>Ac^gsYr6([w}ۿYnv/rU +kLk|އm~ٝ;~waC=y\Lo|v>;qϵk Mw=~9]W^Gױj;ޝѻ|]]ALovzoh#\evm[~o^{Պ%7bGX/Na-a r[ܯmq{+k!GrO[y+ߴL^=t廟rSXk?l]][={lN0s?j31;\(q;ge1[tyVo}73c뺽w +Y}>ڙm>VK/ۚsqyƆΰk6,v̾U{=b>ZZĭkn}0ð[. uw}Ÿ_o{m\JmSZ}s ȇkՖ1sn?ǩrZ ڷݮ[{$bzH1`~Tc]bqWk3||A1|y1=x|.'V?s0V5?]^2waLU3wMUw{ ?i\z>b|0=%d}_waNwyǿֿۿcX߶''嬳%1.w?G类fs_c1a}bk^XlWkO| d^x\_xaݒrV nbo6/|mza{rvlh㖏R_o7>s'W]0 _,.lq1-|WcU| s!w4_xj,L`}ֶWw6# L/T<7hĽ|k#ѻwXۍ qq/slQQ;mw <97}{N}カs<9o^XTK>\m\}w7o,y,Wmi(^؞ ;G4\S,Ngͼ%s~3jȿpڛ3x剋ڝqrκ7}?%IaorBꃨA#OصS}g^sɋr?վ0g9)h:>-k{gT|SS./wv@_LiqLSΪ;{v\Hx>xd;V}qscL:nnƲˡN/4j +QhΜƭӷk|W5j{|+7*Ofna6wX-nww<8ۭ??{C;mX7V=\?\6#>_6Z~K]X}vs_6מKm{L^zvbgC;nŮ_BcZڗWE®Amba7hjahwQo~U ΥZX/O_[utՇtح M]=([ϖqNjW1/c^<,\V#홄5˭1 A1,oζ_۾ֿ8p;ſ4w=rwi,^Q\FRF}qg0Ɩ+\f>^,&1yL~aqͭ/މWi뫴Cy4f:֘e{0+w&W_ni؍OF-7_`}O[rVǬYky cýcZ]xzWQa4* >cS2fɍS-=z0^juQ{lhZwi_~[[F pmh9V,Ulo0CJ;/V7g|~4vg3&ds'|y'/?|c>͟'O߳ ,7cAx/>~a~w ޺xZ؟y|aBcY6RLC{K&h^k.q9wُԿ/M>Z`XTY9|cp51 g¡5_oV6~9G[ILH5'ºPwתqZr{v̭{T_<1dg8#kWon.Pۜ_oxg#s6P*kJo['Ykg_|b=Z 댄 [n`p[M CLg@ֳ|I|͖y{}t/`H*!X\2iC5۳yэ}7!?$o%6 ?14R+v Vѵj˨{ܱ Ǽm[f"e 9Obhx/]=IrOXu bբ'S3[951-czBbWcuXbIisNpK24͟XBU-|h^;޾WnlC+_}:1b>柽0ec\]-9Dgܬ*{+_/X S.-:3Z/;Ռe>ksXb~ ͼۈy]'ȭ{<'Wk3yqGK쵩?wm?@j#ⱨ-,RT|}Q칼l\wޕYpg9xޕx[7g>Uxb%_Ly8kO sbר/o].jTpY{,?d9{ͩS_[')n}Jnb[Txg0vp&S>VNj4AbS,ޕʾ\חrU]kwrѦulm(VO#u?ǿܻEW6ju.sx!Zds~7اsؾlrl-bb{=̆]ibq`6դR;&)Y) FXcwucm8u~r!ϸ}fS{6m,_N[\()mjSK8Xm;3&z?0]w|Ac+ӽs7,p񱰊bp)Q-Pc5w['SB>8g_{blq#>cgI#gw}D9˧X^]G|ޗr^Yƿzz\1=㧭'.sXC{;vW[{LdzV0wx+<֗:f?ݼ"q k-nqc]ڨZ[v9(\ Ո'?ۿvqex5⁛[}m-'y~>Go^p,7ˬdkſ{I!aCqQ/U sK[6F=9ɶzTakڶ7&6ϳ\8Veڥ|odI}DOޭ:_q>Vc'9b029%v-7}wIHzqQg[3[lښ?w%oV^|:]=VyGkjA37Gm +' 'XMaBO`l4a7a:b;w\36|m}2/NJ61besÊ/& >!^m_0?r52/~Bmh5A/iZ>9cN_K~iTl$X ;>\Zs~ׇwl}֘[|4vq|犨vo3~emj;w꯸2smuP=x uV}Z \)[t;^~ydc$L%*V[^^ʍ\]. ^ ]nB'_@c[oM8++X`6JͫZxIc5[?d-Ʒ5K|Mu|ٓeq?mؖ[/h=*oWM|18ʿ56a<;V77V뻶6c]ZyqӼc6'Yk,7~uOiyw5r)VS|ڍsRq>ܧ8v o[+LNN>0G p=j .&uy7V,jyi#¿Z߬a* kG[9D.r2d n{}a|Yks7v]prb[oLFW[0e~=rjo3d'^5/;5* Wn$M1v'g+(yE5xOakbZaYk/)//[^𩙴/_$ cv |"I\?qg8Gmsy~jQԃ8crmwm6{\|sm붹knblH9 YDnC/?Tlla-hu~Ktk,PlvmP߼e==#_|49CI;>vwgWf^6cNѸo ׆P_mbx Ռˮ85za ݽ/|sjƷ_.\]ɣ ^jc&_>/\c s[p_.ݻ98#cqdHƭ4KYs[&%?P}WŢrCk[ 53٩vist'>1N2{ﵗvl\vkj?WWjiYA~8?X,ٚ?;Zw):80e*ۭ||߭_>ju_gw~6f6ڊ;^g7iۛcS8ΏcMiC>usx΍_>qoةvelG.ݎrV8WSBEpcKr6_Gq=ώnm-w;_pp[n}P-kborf$Iޟ>Jxq@];>xas6W9Vos]>VWon_(FWY"VR5Worq>w{wZ}kxv;ߏN2w\ 9*TX`oZs?|qW>⊶K_܆/|1]Kj =%>[_8`jۭ#^yXj^յugk]^8^K9rZ.'m5^fx'bz῵1+|8jτ;vy5S^Kޕ^6[av b5y.?9.KrkyA%]m{o}ԵP_5Kvyۭƺݟ=<p-/WFݧm6N_uG(')E~~jĬ%x|7/ k88So_X_9vqntPQ#[^J.c#2iӿܜ_{]5Nӽ0{w7Ϯߢm<X=p;mʗK|^*$~!:T?Yy͙/hl\ þ76o<ė;,z|jnU:/Y6c)oVqMd1Bq)Zq#WؠXc?/.v Lr?ϝ/Mv$Mzk*޻cj6GTljLl}œwv-~&WN^:jX P[;fWz윸5f)WrjO1b~j{wk ٬W푞SlٮWLlHrF'VG1|r/98`ݿ^ޓ||މUnA.:xa(.l 6kEԍK'b*MX4g[}=#ۘsguʖ|>t]KggmֱC~yal(3S+F+l~[?#-AqWͥ"޸ΚBNV~bKou},J}n(qӔ΋ gq1,Zs.c1%qJ6L|RX8+߷E5ʯkÔ?]ɁohtШ5X q:fe7N\ߺu[&i 7/f~m;;fmLAڗAsb|lm+,Ae>æ?m|dn>ѹV%Od1xy=v߷v}9|S8,<˚Oa-3s5v^bM\}9q2@lʭ8R_qj;Ʃq=KN9r]wueyjV_{R Qz((/=xb1:1|ϿjǷYt׭Om͈kh PÜw>ggqmk.~vI|@k9E{X9rzI9Fˑbk&\qiGC _$՜\.﫝nL{\yF)*mw~>šjC'_UWZa)ړswX-]?ˋ{|}fE|mG\\YKRn~iwGHO֟lZ!߇`lS[2'Xj:$?p>[xƽB._o\ 糆QO>D=Gq@rQ{_˵6cqbx/v,.$N-vBn⓷Ë./ ӟvզ+ZM}*CE soi3ϏvY۽c#o=8v/qXVLcmG+{i:grw٠oC.:H9^$ľWKq`ȶ-5c,u99aNn ,u5HV[]ZO.oAѫ`X\#։Sԁ Wt5sE1r\qV/"+r됩vgvs,:\ěsL1 ]55~ؙZtY?>'ñUqs?/f]Gr~xn6a5 59mj(:~&~^.ekZ~0usy1C?eyF{r޷y[FT#8yO+O^ƬP wR0sݵr2Tn\]tڤ7vߘT;uv@=>slKknM 2wU7s0~Zjn)~Sx[Μ7S/zF"ŽM {Gh]Cy ,>Lw s6gZ'+?um>ِk+g8oZض [k?_ux#(6ry@ OnR_ǼV d繺?㶶΅;aҍ#̳j勹[w 'ڱ?i}bas k3*)_qҴ +H~$p:00@ӽ5}oUwy%ي7Ӿmjpl =37'woiB<ꞌ1K&疟$$Om磖xA.9elsո:;Σ]dwlO}(Vq~ՍjlM|Uθa[9=5Hw'S o_mO J;\<,Op>^5UTdOn9܉[|XX`9SXT|{Zh>IY[> W6> ˏ>8tmmZicײk~l`5=酊{ooiTo+ڢ]#sSC5[CǍS]ȇ4w?&ՊEO[b[ wRXXaօ0bj3=_9;fWlZ#׫gX1GY }c|8²/Zc(,0]p\@#3q~_u}T,yiq~>ؚvr^؟i}֯4Q;bm]"oy_\^-xmϭJ.zln>Gߧx,9ՀI/wmpt4hp3m]lk;6.hߦJc={ ̞eo Tx#B c ˗̈́fmC-_tzNb=C]r2۹3{/b{Zv̵8ܚ lͲv6^~ X{x~c#_ws^Ν:>1wظz߫qnۿvظ#Zo9츽juJ/nC8]^*혠/.~=58.Œ>M$'?ml͑G=W_GG{L綦f hX@#}8~|@@k~P \a[W\,v당Y5_!6uϦ{ 4MotCw=X[=_]C_O;6.n3'TCΓm3Vyjon{[~գ/w&9fɥa{,NjEyqVsj\rgԭ͋m~U/Y~aܫ{w~qJ,qS^Z׭~h[xYn묤aLk|[LwN\}qri..=O9y[KDyT厊-g 1Bҷۺ˰þ"xVgx:qj5-oW~|G`'zR&|G՞ř-4V֖˺%ޏZa=a)b}~+ܱݑ_R9'_mkȏRc\R|"-[->f <(ڍ_z%5_\>~'݉Goe קX ą}CΫ5GՖo.ڛИn݃WI_!vnT|~ +^5Ǿmȃj{rm cqh~V?O_7y}ՏB{}mk][Cv=Gmv3{΃'m$ xu`LE}O@sebb-;j_׼o9arUęW_V?_xѻ΋ELq4VZL/L喳f^k9֯7{}qϷ9Dը"(wOi>&%w_M[t)AqءsOQ{_j#[^n0qSYع<|έ6(##sl +C[ 5䒼Mo76~-Vǥ~6;*W1 Cjm+7!϶ +}˙qLε}X]5Q9_6v|D Ն3]sooo7&3c4c_o94F.XŻ^~qO|ov9[r w|u{|ꈵ\c7T<ܭs5/mW*{lܞ|vT1A(XIMWTbi?ׇפMZC>m_n=OlWlBژ\o}xhz?3]Ī¶ʉI8ۛ-ז6;H1?1#^ܱ5NX{F\iѧ yT`n[EXbƧLy<[8⮾7ji751x?$"Uk>p1s|WkNyrb5o}Lc1͑gG0ģʶ[(m{ʪ7o[[C]ƃ]_\YHjt 5%f)Ƕ)7ml[bpv5jόgEm:*nNh#5<+ߐaueį֕w/g|V'0/~A~ey&8^aa+wk ABcW_:>iG9 ]bu :+#prr.5]+ͥ'~rv恚hڒkw\_#Sv'ն-ʘw-Z| ce}vis=q;-_,EN|b4k"fI ԍҡ/^lל-w rv'K31&?b;>Pb1x_-l [dJ}SF+Ub~vMN>M k;W۩6_L__<]Ȥ 9P3XxՅ0^ײ's]7ͳqymَ/ϾqF:~ykrϭwW [S8.+gsCn6Xv ^XMrb>EmSG\} (`0pG0ߨZa}tTv`<~ƅ(?Mm +ŃƚaƍO[dk6j{NN71(՘diqC9}bzb@fC]}q,n| ƍyyN}u<Gث2A^.j~OMEkq_jwk \b`uk, -lWg,eܤ&AzEr`ٗAk͹vW yw**wKpC k\ͼG`IE>lmwO}Ym}η,Vhs6'rH__w>zcwl~5GguU+a:;}.K>jP;w[?^ߑsc\8A9Y1wv~t^M5Ֆ:؝xb7[c;qځ;/I}|o~罻{o!'Թ^˥yZ{ CWo\$6yW /'qqY&Zm[&9|x֌RL:=rK!K9V`S^;4N-0~: Ƕء{koŝ4ś|p4 5ĻK8WTC}j}bo +|H6obs>I|@s}m_wp@uq[wk sT涹]sstbui.-'~ֶю9L`5;jV| +1˝D]*<81^KRتK6M>y6n)Ju3%g2j:;gfs+F`f唆5ٳ_HJ8ͭURkv٨ ]̷{6>97!ySxk3"5ϣ1as2n+Xb'Wp{>/_~/srv.ϭx9~*?-P>T~6ua <1񆍛z[0Aq@];Kk#>CTqZ;6rzqWܳ] b}sg[yUǐ구~9O!ƞGIi^^wkzhNvcq4r|~l|~5.~y>nx]x -vzi]^p;5 <|h,9ԇGƁ/s#׭ꁭV} I?[,&wϽuO?vR=ٯSniP\%k{r>aqk_}fB oۧίj,)tn[#8|uM.g7K!s9g_sk!j?k~õγ{S\ r(6ݾ[T-j=b?"}?/^@q;yz[۱<}~pgkj 7/f͕u3#nT?\Ou?.dZ#okD VGr~ [ #V ӚwS/%UP5-WGLs/ǯjA\ww}q;ݾ?`]/~g}֭qsψ{|7W>8)~M= yGnuG]S|aUjt}=' 8ݳakXBj|_űl #~=fSG1ߚe5;c-TOyjٕƯ|9}8gn^?H5̫sW+]fWwuI2#ܾ;N׸c-nwqu]l~'~{X؏}tSvvqZ.7{}|ZOƠiC|xaXUZ˩X|\m}[xqzŎmUW|*7{}gѸy>]r?-we=65Wsr^S#k٭{/'O%V~Dm+uy{6EvOagچnC/9j?zg8]S`b-Cx~e"c}〭&Wg| O߹ Dcfki[G׫c^rw{!w^gs hwdݯ#䉿5ܹ71׺[L/q\~o}/_]㦿g~}yi!o7[~}a֫eߐsxqȁ^uVr'Xl'!9ټvk- s}-[Chٯ_4[vj9w=Scv\Y3۞=ܞk~.W `n&;.nwxqyraފϞ]Qo K]wb $?nq1c*9jE[FyC19Ӟ/F\\NyGzVY>}2q;_?(~h w]+sޅuݝ/bs֠y|}˗}Է[hޞX6k+-6 ϫؚj (S|_|:(Q+wlk<}閍#\~_dz^^7n`)i8ۮ:yK?h۾_3` oqnn,m=^.ü4r7Vdyzmjrmb\=忇 ~o8a5X rskp4r+oiny<\^Ց/(ŅrpUlC=FbkW^Ou??|>{]ޟ9&7;zjy@,ww_{(O9?rw]cڱ~1aZ~q࿰օ8w?/]v5ޯ~to~z8Au nEXY66 jbK٤]ۺ@s ;bn]YB1v/:ݽ0K0lvR>sKhwĚMA'! |juac['hSUi1͙Ou7Sqқ^ 3,ꕻnK-Qϓ _'_6WP0_8Ϝs}-6X[,\>Ջ<掊jw}LX#;kw>bb-.e~ֵ? ǫ斗''1|voxڹ^967ΑO}F󲕫 Vss~4OsWКّmm>m]}F<f/ ΝF-k8_mn-tΓ^BYy})թYjp+\r aB獻ZޣstG̵ȗXXCqTډxrT[wqZë]=5Fn^,7I֎\ پڥWug^&VgmxicM1ܻ́6./v,-w)f(ڏj恽&27/;!8<ޟk[.u:5<"F\gܿ.g}RWs7j8ۯ#Db6gK!>-4G?eCm8LB+l ~HK/yczak6WH~?7??ZΎUX+6w0?u^%l'9Lr٩O\ѱVm)^N۩+o1fvul0 xϯzF[lg/a Y6F|XΧ˹+;5\z) +&}IM埋kՖZv;[=km;V#/g9dOCl_oܜ;Yu\{ on* n*<#k,zmە7عY]8rmhm].?մ{k7>J]^X{X`KOq8oi[>d5ŭ791͟,1,澲{Yl] rU֮]lk|85GYejBL{if63-Z}uW޶󹊃WrcʘQ*O 3O-qbn\ZXbSv)?]J^XKϻnȡ{1ȝ[p(pyy[-}5>L6τî ܭ0 nNyOu;l'3uwm-'Og*E_O/*OLquj].*%K6lcK}nO?/>R;.FswP]jJAWPOZc|잺ppD kZg8VuROWnN.8w]ۍ^+MԺ-ov=ҕgֳ߷ah9 +nGOS6Y<z-;5m͇gnN/Y _ym(̓k74oUf^Eڞǩm-֟ 0+ /M_tks߉|y~/^ޅ(&ʩֶ=tt77ҮY>{)o9}ؖ#;H'b]_]gqvXpY~<,U9m[WAdm!ͼVcn2H9>W68m/eN\ nK;Խfw(-q{@yj|϶Ś i;c/m!~{1Rqx~5Ynmso\T3rk{iKAj'\1>}cw*>~@1>tߍ)_\nܪk14Z GO~-vallm#~c .<^qFjNiOjIK6ظNyXzF4_1k}|XMl=Ak?^ƎՀ[Wx2mF~zjCW<1lt>ëW xټmy_{ϳE:QNf# }1;+j!z>gyj.og_ 3{L˵R5jb2YOP_!q23s )k@{@\?DU)[QgDm~O~c9j}6 +}M5Ǽt}^47s-Aէ#hnutU|AiPJS-z묧g+gqN35}6_^b䛾N׺ž]# +&q|^W-wb.աesui''vs o{>~7>v}8|.5Lnukc;nc_>xbGwy}Pmk=ӻ|a]Ô;⡄k Z-ky򫵑ᲾZmZW~x5]M~k-+_Dj.5FwfW[sOٯŴz}q}ÖxזGZ+ p߸Y5"߹fcue + :6gwMy޵u^}8\#Οs].* kތj25i^8Kd勶蓊Y:R~u67ܼ@Ϋweqw) ]nAdZ61p@|yjbaya[ޡ~60\ K+X>2;_ף~)_TTΨ0g7,.//'ޘ8҉ZG_{9W鮽j{}ϞUc^[om0B/u77/0xbm_76ˡS[ji9@jhVgO ^5 _u;-y.66UΚjX ˼ܳgMm0ZOca˝kkn:u?{M췾߶`ؕ9br;6^: }9}ӻUkW>rV"Pwg8k*m/僫v.ydvB)Im-b0rP>́67jۍ盞z| OkldĶj_!ƒc~5jT)eq]1.,KDaĂ;(V[꙳i֎t:ֽrzzڻgk|陫.fpp5>'&T=k΋V MurUmon޻ ǭS(/Hݚuf{gw};W_{kMgzcEX^:\kTs|BfOV׵MjVlK^~Wpk}a3pr wu>0/8!w[-l}}9狯qd)_:'H~hѵ}GMs4]_w|6Ҵ)6վTtۈ䧘/76ݼ5 ޔkSC3?06T#f\@lZZMK>%k.Nv.;e^Y2Cxrƿ&kw+;Fjn_vNۮ^wbE\/y =X 3jUlygtm`)ׇзwzBy/`XayE8}7Վē'^OJLbp{ΏVq.YStnP Zq}n?a֡FNk=FOs[_ZUͳVot</ҸXꇝ͹DŒOqlŸa=!m,V-ɦ=6^y<{qRWzpmgZ.c8m^~|߬>x>0_6Kn59&-iW8<~KWq~+g"vԶ$N;,ge/J[zp>pD1Z˗_^ıq+nv밉+dGSW,򪷔ok9X1oӲdg6 ܹ'׶>*Ն{P_um:cE=Y'EݶZ9 x}LUE Pm{W|R6#[ԼfGOfy*ݿQ\c^b˓Pc%zLm]n|!W.fkvNv.O psO$k1D[XWk*&6X6ۺ9q,粰-:zcUڤmy.+?<.߷6[R5wh.'n]GՕCus*f_ͅ)ΓR>8J_Yi.byb4}k,7 do㫁.?ߜ0 ޳ tu'_c)q{q8vﺪxxa{1sAƩu󳶋طyuښ,ֽoޤMΈ:[+ʶ)gekp|x_9}Y`vsk-9r@=쌹*mt:p9ձܹ5Vtnupw1(wWo}u Soy]nr?sboٹm.wXyqzwmr+˘~$sxG]>i}69HǕ9ˍ'еLflV~k`4BJXj;^ >E^?P͟oֵ V慍/}iW{{n~w9ir[rن{8ޙ]mBrnoY4/x[ w^؎?W5,~z\Ϸc^W^u-aZ}sڀpp۳_oM`-&oZr}a-.*_ϸP7~cywb/`}l+?ci+{O{tބ1@'<>;WڴaPNڭ?fלRNl@FC]<؟rj )6mQq>_`, ۊk&X9k/!~'r[Cu)7?s'jgO7q,^ S|м~^sY9p|]ruqzk !byFCWܺi'ZF=}kW_Jk9|w룷8sԏ7i[[8l,|Z^ @,4wp15kPMu/VQ8b(4kf]Ѹ9XY/4v釫//b91h1iAჵ/59UCWִ+z -'?#s߼iiNvݸŰo{/l9s g[\Pl-.z/LTI5V,NqQfk3Zغ?Qwm8_[nk{kޱ{|!mΣ/yh;r=u=wvFNB|xq^B62n1`yc՞H˳\5u.0wR{ +%'돯&[,oX!W(6}onyj2/,agۉ5 o/zi{/P'>.5{˯8_-Q|̺l싧>|eۮj{^+m1欖bϷ\厷*#\ 7ۤ "QW񶩖E͏.]mj v_o_&g<0Gߝع-W3.g/|K zj;uC=w$?yqk%U'+=8g-ᘫGY5Tnv^j?l /ҽ ]Hm],oיX=p[OMGz)cbj]kq̱vԭ[=Giwh{hkmsb/\$ە8RSnq1p5\8/nu_\Nb|w5[+_KRߖn6Vn'e+;Ɉ'ʾmw,w[-ZS;b~ swg{w5^/]O$!޾y._~+0cc}ϭ1wT&եn|Gb~ܵ޹WdkOk{fo}=/LNv;nM6zU9|0b|Mbn9~m("OIsb۟K G[XP˿&Gs痞Xl}yr0үߋ9wNۊ>XNX6Q-3 XnZ\ڞ4-o/uN8Oqrvz]OM82䖯ucbl_s$}i Xyu͟7DẦ4C"WNlQ81jߍ UOLL]={6= OTWuKU9/KsX U-@sx٩0]c^g!LG9x\ހ\5kl? #DZ/E%ے%[}Ec8~U#2"w_T9]9:^[kwCOYXaWoEu0c?=1:̫kcb@jO[]pcu{wvذ[O:ž'吏/W}_cbCcS VUL6Fg{:ߌ#/\МLo?1@קF&oHݗy|o'ԖY+p_=xS_?]oN|g{ۗ5eGozŽ׽q+tߺ5C։wui]dˍ_%'8p5P'{(Xmkގ:ڼS|a~`عhMX6{}ny-?+#_b#搝cqD5Cׁ޾O\u{]\pbwA/n|1Oqs浘L[uy0q (VИ _7yW)p;DzL9nle~^֎9߶8-R,k]jg8قj3w>7{/|lp|պcfU_b} nݞrcikyYO\;9fV]zkfnYgk o=-Y\c9ib[ӷZ9l0 +{wת=y߿[\oyj򆪕7QZǵ65]'v.6ƬSozTZk$&(F~Sڬ׷+ۛ~Bwi^k).*ˬݻvl YMs#_ż~}qHqrWolUv}^'ւzCyeMݤX'HLz/ڶG }l=!yfs>S-oKoO=WQ \>sզm Q|Zzhk,j3kG~[nW_OSYܬٜk{-M<>sGwDlo߷h9ڿ)RjW,"Oܶzez}~0[وl[z-;,\xg&}4kjSy/wǫ>ٟrœ?Q_즜Yvs_Y u5gu>wq$10dۋ7zw=ײձpn'^~kz_H8mƚ[:յ,O_z&&,qڋcG~8e',p`M4_|[bvwk]6BTڳ@jX?>{ĺ;-׫u_Ӿ[q5yӱکhP/̵BOo1aj[y^?¸Egj{?C9B=sz.C˜[k(b/΍qǘq/_`RǍx~rlwx=-S[;s'=k-Z7n>_;+#e\%W :7Zx~n ʅsX߸g]~^uպ/оb'},'C,J;՟K=y`7N{{9/3r_Vh|g|&|Ck3V`Oϸhm^wid/-~t 9H̙OuܤUCVP-vm6=?ύzW6\<|^#'V^lUc~s?/sY˴7qή5lI1Mu[/(>y[^sy.Adt{_Zy/ea⊋3֧o~;*'>ancA]bߞq:3__ v`UE>Z3NJ}XQGƱ5:.?jŻ{ab6~w87Q|a}<<^Wkև.qac!r׬\lωfy[c_]GI]o_f&\_o|X횀Q|ŵ}̮b5q[מbXg/7l-k3[7xR)zƮ-|HM}v ?O9͗kZ.oǗw^ar{ٴ|qM+3O&%Nf|s\ak|Ǯkxe8geFJ8Jzyr wN;գ;'u8}5hCzOWk.:Xڦld\ kCYx\uTQG8zi[zK[ǵiy7zpo`j_k[7II_qN)|~_qڟ34i?3Nvc}/6|T>^[v?xYԾ^En[f]M6Z}ʹSY,c~}~ּwʎg}cXj=Ûo{a@֛~wr7ba;WNE>}/{=݋ŒȜAm' reOX_"@wvN a5ϩ n|V[~z ~\~'i~ꮷRk^c$Ne_McH^~T>Rϰ∋ݬ[.܏Xr_-Ԯ#v77\,n1:ߡm6]Ωxu9b~(^^_⣫;Ɲa~ 1ۧ~x8[lI\n5lYoN8ռ.Ur[h{NJquk熻[WW횷pV{~u1YLZya[`1~ku?37Է\N%ry֍/xך>1Xo]]։YcƜ**nӖQ_Nx4v}ŨþՒ7nVdO3|Z11=13X_&k(jƷ[ð7S!q W[=הݵI‮&w1wqO~' ӵ_-]ѳ-1m{o=z5ls7^1^y[XKNU'!{;/3qciaww[*=ժWSlWvݿWǐw5|9m|!>'^=b.f/$-+C@yv@FzscxxzFcb`bCikj }\<ʼn4F,CΏܾ?['bϱc/Gl9Z8S;[6ioTw|y+Vmޒ>bm)s"]֊E׊הAa8haR.6^S{5M-O~[ǶyoWv9;f5XQg3UTM^M@8#m{Dk#p~;n,E=pV3YػPΟ5tQjo5v+S}j?ǔw/iN/o}e~sXnouju VO4'߽7&Z37ϋnv_L9Ncwb":I1 R`zSus>f<_{eT3 K\yuoCt.;臁^_^uֺ8_:Fv^^Tpt}+p-~Qd|96f䗖G|<>|q_Hc/?H vhea?Wm1_sJj2 ꫧv~_an->d^r]xޮk +k +l_|8ZMOUՎ68>E>=nk#8qO1rW}1/yLs2y=;o(VĻNsBŰ:/}1 *c +[Nʻ#(Wr"\K0g˅B{ȷ^5Fcw5mk_mr6u}_JkrTs02Յj+Ż2x͓'/WkݵEb_j[_f9`3SX2[=о8bVc<׋׷6}g߿óo<5Ɲ#s0Im#3m0wPkMVf3Սo]y!O,Q~_ubUh\X/"]s~brW/Ygk՞>n8sn3ڻGn><*lrb^_"F߸Ax=zE9w9Y{o}zeuv|I׍Z =kGǚkWowT, ,֬rK~BӍl?\v9|=xm'=|Z?ZXk,g"뱖UO̧8ȫk(6M-|Z9Ƴ6Cǽ}kF?_ia{U]LUbA+Q#]^#W[\iI)h깝O|%nL"[|VLxq7VUhNW۵-wnQs%LJ.(ޮ~xioMŏ_\mH|HQykk򁽦c[ mk~^ݳu]7o3NL{ܝ冊; 6_5l9QPc'X%0\|WTԭI,ɹ_}hkz<~w[ɧkZ" (_#'zQ^;V\_%B8kŔO}֍˛/0}+r=-W=<@dܽS\eMxL-j('L>Up54a5o978(smb4}4ͭgڸU+}f䤗g׮<랛AXxbf{{ }<߶/'=3WZ"U]sgss])F-O8jحrHW%v%m5k}&K泫ojqݶow}3v[fLﭖM~_ O9~~jVܵO*S!Θp'9ۊamn~+b +qXu8ݞH<sߔoP'\flkMו,?> cfn >ak^"om\ج7G{arٿvvg&>,[T5ܼC\R8eȟYܺ1ݍYk{aE%[Ғ lF)sQU~cԹywa]>Nx;qZ{q7Gb+-W>8+Nh9F>iڜ+}ؾCb w;G>u&w]WU1MPq9;VgIl_ ++RܻcT-[8X޿ g4\aqhz^k.O. lt_M#wa5a7Ƌ|ni#nlOc}<}k^M{$6 +޵.U|W? {q={7ژxZ;TZϝ}ZZﰺ+~'Q'g0DmX![y%`+<,a a 8a='ǻ`}am1b>>ssirخKi_Vݭ^o)ᴟWb6\u`^T9Ff5kq-4}z O[q1zz&/z>{~)b78EәdJ?{Ċ-cOdd+5Lvٔ~s5bi-[Nk8 RW}[ctð65{W,jߵhl?A1~b֞>l`q~<\lW]`=+WuS3%[qaaebӦ(>/}.-pǞ'[:s>E. g~d#7[7.xך=-|]_ G@ܬGP (6ڱB҉-0Sj XW>,gL+GG>[ϪLc-O:Y(^Va{ъOe|N+;Ӥsھ0=L矷ߑu&1c}uX-Y\#qXVVV94iq tn,vO zߍsL}7+zr47+Fm븾O1pu-8X_%GJNu{Z_P\,N(K e|m6^9Ѷ~w֮} zwu=|a0W-Wf)q̩}qZw{Oך/ئsεMvԴ+UkǨUu*?l [,FGYr՝8roM:_X5׷JIuc5FSݓ^9˶,. }/|tϴ]w]/Эqoݱߖwœ_$Cߞh ;u~o~LYs~91rZ`ϖb /exX5ߊq,Wm5tsd˛;b5՛32':^= ۏȓw5{3 +3mp7N S?c6ZrϻsD\,pHܳG]CM{ܽ;lIRO7]K,ŌcMmf;vɱ_w1]w+5b|M{4G^z'X(%_ycncwoM_Ԧx1_5!'.|,qҁ߸s7CSqKw]>;>5j̚Ƕ:bƻk_~fΓs>V,_+ Z8ܲwȵUK[xs5qb9bNG|V4C8䍇u_Ypuņsu[5yۿ|OX1X~> mٳ{ۘj<87n8]Yngl~Fk5|~k1jkC&n]ZM>s7js=rա|{5bqşs߫_b9o^bg[c|Gh͛1suRuay8Iۍ׭MuaZ&o55-.͜xju@/<پKfm_0WO%s)I?}k^zokصN:f5^W[W׿\gʿc ˝Wvʵ@Η_~S"gW^>qkS<#M9aXf-9lr;Vbҫg@S`ξ9r1o˩_X6#}ϳals$qڢs+מjYrxKu,{f^9ۃeoҫ76L8su̅uwm)ϱVYN}%)SGsm\{;\ +sͣ[m*{ SW-{OݵQ j}zC*7ʣ77|G?Zf y]ݹ8_>qau>o5=0Wor|qbkG=+>qLi9s15}/&wNC϶~ -ϋ3ݘ5k ֿz˓3r_x^cwq\[#T^95oƙ5u٥dM#=|!k/WM=\);R :Q];k)sX ruF[ O#'sl,ɦ7}H#ܬRoݻv4u|/ K\29+&zczV{GžrSeՂl]ݵwG'#-o} Vv$"켍[m,(?:p#{xZ%҇-5 >=>Wܾ.>(װv]/P@ko^yL5q>Krݛ#W?S_2F~׿3s 善i-U.p,H=cOI]R.no;Gw۞j߼[\\y_M&M*d`qߟ܅WO{ϑp9 r;T¸\||ku,}xlϗƓx}7r?%ks㪻cXᮿS}y_ybD *?ȿ'|C.Z|凷ؙnՎn}eջf;>!]+o(_ iSu\o{'6~b'ԌW۾SWq.w We]KRΗ!յ_mr=n׸eΦoo\A-_G1<㞚׋˟|*7|wnYzq{Abp[g^N?nzglvkMcy|<~ˎ~nwjL7kӾW7psr7g^T )^LlkJABԲno9^^HܶӜS!9]}nLq,u^‰:i ;^j.^8Ƕv+'Թ-^g_5ƴs=ǥu +!-nqz}^,bgy|VO^|-"j4H ňŕZ7pY{^_66E^{%p}immՋd猙6>+Gʮñkp/Gp1@}ba>C3w|[Vޮj@^5q~/

    L)3ZwPN-go~ 7ǒ+&i^*ؚۮ5Zg}g-Φ3v-iÔK>= )/7Zjk^>%8l.gk}G! ҷڇTy?l9o67n.&ZD~{@79br\GDYoXثܼbƣ6sSI62=b8Kvc7hq.w⥟9UQ'޼PΈkG559X:+ 9ra95LĝլXs籼+Ƈnƀ/Obo1ky[񲹃kY>gOQ{j^LUO DŽs9j!>_?mg1Xk + WVu\  ZOC]S8<;c~7s|5V>Q;?V(}ݶ}jUxǖOعd79ӻ/sB"-L=s(66fYLT0zS)^+ ^0-1LxX-r-"/t,u9.w[zq'>+orW}/RLB{_=N11cYVcN#k|-Kz{ٵw<[$nw?iʃ=Yh&ب_WDou֋7nd}=r@_xG_!5͓=o͆GPzhO2gk,5wr|^q +wm5Co,oNcoZ/S?Qb=8_뾕ԛz|.ɿl^٘/~3]Xfy$ο y: 6/8抝Cx|Vv.ĊuIOwm;^X)箾Y:א ӿXzLzf~bXjOYCN^77=li($j^-5CVo} cZHxb ֨\+[b q\ힱd~Vj[;[/'+*`Zc/L/[yt{`so)y;rg/_ U=FT^\ktO~|_Ο={vݗ3-7WNOS/? ,wI? O/N +K<( pd=u^]`Wܖ?OJԹ}s}u,qJF~J֯sġE1ESoo)5gC`[;^/Ors T,7c랑4yW{/ g(&(޿p8'ƒ!װ +4!w;a1Vڍuz{ܹڃ#KՖ765!oRvʓ-3W # ;߻'u]|h`k[B׳2+o pTv~|ֳ+VWl5գ? >H˻̖(WW>>-߫Ymq1-_jqCmX=k7NYsqlnW'qz 3.oc/ptb[]ϲn~p,j׋#S&Zk'kܾ^}6՚g:f[;ŅH 1ew9 /~j>~uϨ g7{ +.(nK~L;'6'Ŏk{5m-^urӋmo?|-Z^o_ǫ_gmZY7d˷nMԮ,6~56V?.^%X޶ujU1T0iNȚsO9sO.=:vecR=aWHD #`<^k<,pÜ#(5E;~b4aհo;=d?,ID9ݗ[9F8 ijOr;Pw9x/XbwoW-h[rVl0H9[w=0~g{:l>B +\ /ޘzY~!*RF=*O"fYo\T}u|{r۹qC-W:guhU7ķVִuXċX|7 5N1?WsF'r&{nCy`ӗ.E?mlv{go^A*N,,ƨ 6+N_WE>~5ji^jW-gn%ڕ8}ΗC1akTlH.zx}_6q71 kT6p eywl}V;ε-qJoonl.f{pc9 +|F7vډm,+ {.Ocvg1|@~v5 +nزi.c/۷3*ؾLmKkȟgi\OeJqM_/9Ukq8i:݇?O-oXW_j={hMοX޳g?wAVJurp0¶bb8ghX#o֙ؖ+[9粵&mgiz1_5/{; +.OlGb>[|g{' G|^mhO} nByԴq]+ƗK.֝{751\?,g[ x/_^)lk5vlVǷ_K[e-p3rq%&Wl>j ׮_BӾՄMOX|A|@J^ܻbޯo_kGz / k{?>`=slM;_> u/W]~9#~|br8ڿ/c]xh{ѹݽ%:{5\3\H[A ++^njpb_-1r-Uq {-dM[~7|gvD9ͮ#q޹}s-k.Z[PX=Gn\n:y7[^^x5/Xg2R] pןYbss-c~1\k^oak.z1^vαsq~g(~7\q;GwnM`1Fcq.Kcl?]cxqQY47`1U1ߍ5{ߩsj:f6)__t79vb A9lEFhz>(cRc-Ri9@9#ψ5kqr4} X<ع-^̳_%_䍽ƃ4Zso8wu >r8b|켽Ξ?R\/ +{=]W4 D@ +(oメɵ[zJ*5WͪUǯ~w_///K_տW_|/DZ>5˿qO_~~ooݯ?ݵwm{w;v~_]ww}__c''_o_Oc?///////?W_}~loc軻^s}=oۿۯv״{~ypm?~׻ϝwkmwޝ߽o 5c76w?1n~{ֻܻ]]cm󮹱y1}_;6okN\{;'+\{۵}n{\c{玵yNm޾wmskuwgzֶK<9yn>=p1_~W~CΟ\/??ǿOv~g~c_|ُٟ?_}m?s?8q:﾿tȝwz>WnKo5/ǵ~???˽髻BuT>Y~C7~w{5}o5wm7˝wM&߷w?y7n'~=ם{IucK27~?w8g=덙Lw y[>ʠzs9zsNN;V[8W'w]}pu{k޽V=O4.c]{[Էڳ${te&;9 ]߱HG'wc<wq7;~tg۝=Jv֞;ٌٖpM]s>tt]{z,=Mݖvӳ7nKkw}߾cccΡyN>===_ܻ5od6v?Q']o퓝dYAQ{l]<=otca/x5Xloϟ/wN>uc*As>SO^i~]_~RGӏϯ?]k?]O??u{)m>oۇ5=ܹ"x6vCm;ugh>5˻ɣ۟K߹v~Eww_mtxS۲wz}&3rO./n:D:% |WϽS>x8ٶI2$-@,v1}wwi~Wݷg9ɾmi1bqq6xbsb~]/#'v'=bGbM㽰°x8c_g}?='F'Ƨ]Wv^:GYl1[CO.wzs['[ L6؟mݸJ8rd 7̗Ӷw>g?ԉjtHzv''O#>ݹvߩ{1o|[B{痜xI8قamwNaJzŒ3{;;/>wz|=v~=CVOdo;_5isկm<7%;'qs~ҽ;پn{9wA|pv;?{g>~=?1tI:y6xZC;Kci3QoŴ-޵OڞݱkkwL[k )jK[E?&Ihzgeز:9>Fqmw8FulVcevq('o= n})_!|F>&_Onjw}vO2ؾI>)#xϩS!d51=?#?vV:*>M*v[:2x}sx\l1qrN>oGcXuni.]{󞬿 ]K/qdZ:>4$_&$&m\_wfui>(_}]7nnmtgeie]?^d+4pm=kb.sJ/ pq(+S,":2b45 @E:V_gq|>+IK[bZ[=\a0avm'N?sϥ/;/ymN\ĝwǒ7?9I>A{;:x axmŤNFyfKɭvH2cbW|d#9X@O1r*1ߧ989[ XB;߇ujq_/o-'wM^a~~yeUqO-@=i>~skV֩coi9>–m/6>Yնlٶ=gt}1~9ͷdؠ`v{>UX:L˛iLwN-P ΍dFPܧ}~qF?0^`K[~J1|cO^̨fu|ν͖o<b_~W7//ñvcӵO|fg:._kgovӍ3rvoνknRM +sqazo}e;G{gx9\+O[. )L_7٬2ܻOPUd]ʫh,Lw}vm)V/ުWuV&)Yƻͧt)&\.aw/O[*c\Jp~/ָͷxytqYn1= (V 74%n,{}uw}}V{q+ Kjc{|X0oB^1m[柟(.v{ _S|^. Ws_n=b/$b~q=>K\hrT^ [Ǝ8qq[Y[N6?\ *}uͧӲ݇ڧkcoͅگs׆7 ~V1xr~sbӝx.̆??]{uݝeזxu6X=mٸ?Fq anrmfRr82@WϯbI_{ڞ78]jv_Nnɯo_ \) m䍋w޵{dɩ|̫[Wm2wwɷ3V4 \n~T^;˶.E6}^qh߇G}{d||sW(>%o\61?w/i[N a71(7lbϲʝ_{=ь7.7S\ܖl1y>M7<[*J>2uyVLw/-Hl8t˺<䅶1ƶwMv\'OqFqck?I?s0 /ޟu^9+sw۟aus05}7뒗>dr7RnGzd}rI3ZltsK-%P~y r{bGĵë}_ybX+msӘJBX{e׉O%̉޹}{_ƺ\?5!]\5~r<_l|Ǹru矘T\8+73<?0~yw?߹˹R8ag^| 9췏kn<,INK"m=uq, \o}y[٘u ߤX]t3tbCwEd㱄^(y϶Ksn7X;]nxvxyْ/ZrA=[NΜ{=7!r8O6FX[~#_fcn\[ſ t.wmi=jL #(_ nU_5?̃_-cnAfwos_[!/dKwE`kOso[_ay=b30n}>5s>8yxztPx:4&Wor5ٚ{kXp>~ۗ'0_qroNezyK6us͹ʞF3X[WݹqM_<+'9+zy+'vn|NOm9$9޼N4Gb6?/A<{7ŷOplW|^:>RնRoy?inbpH}  1s6LN~U-Os/`6U}Gf_Ɇ_]:v?ےmv͵Ɩ\Q0yN1PkON2w@{u@g`-J3dLws ~.L b\FyϖՆ"s¥577z6w[^kT'^B[ƨun+ 9N=۵ϧ8r*vׇ5ˏߜj(8~[+:rI+ ++~{w'<Ⱥgs*^k%/}f~m\_%u?vg[J}X/l7v +?AkGRw;&~p1{|;/ /JLN~Z8k˽xqִj|mW.z峷>|`qts9m5<7^\y}cC.ص$[sZBqk2k畯/r>nܕwm-v'MrmmWl(_[,e9⍯\GR]{"6}Ur&/&v {6ϕGdi oIqf'x[Qrmd]T~d k:7"q[n_|¾kx5Gl[=6g<]P7ڳX<8+,uLB{}W 3ܚgRo-]==6^wfvbB sQx +W9bz \B*VJ>TlwQ>ew/vぜ_ۿw}w}uNz}xc#Ypvɍ/"@];9w)NzA/A>=z{ۘdͅ~+^Ý[wgxyb~[O0N܂ƯsYԽ\jKiwSUYU 9ntoZ[ی[ǯM17s'ދhg⽪>&d sv1-o=m~=cڮ:ɂܳ->\eF_5IW𭋁̋vX9רExci7~ew[1X|@B'}=˵Kը7b֠oʋ3oqէ9?u{ENkƺkיgDxO޽sw90\GKn 6n\K+oF |V))aL?x2,9\ΒezW9!qA|tgw4'Yo/aw-/>^u\9yN5!^-rOzicE~8asµuy "OH@lo R7D;QK<1cŰ]P[ys k7m7C\ȵqӶsgq=^0%kZg=ٗ5!Zڵw/x0\VΑO9*M;L R~mק7&ȯ8\68Or5 +C C/Lst67=3ۯX}yu\jj!(k*ӿ>1~>'/x1)Ş1kR9snbplOks=Y~b|\ "]P~hxA^nhQ*҉N%NΝ,_ ZݣZ66.]y1/1@u=絮Hqy~Ȇhj3+zb%7]n[Ψ:XŒŔ7"Ql0_3sػcK0~W~Luib5}'Y0StB^ L[ݔ>QO۽Jש4l|~+w'Z@mnXE<o-9~g,v9d4~nX_o/nUPl]~.Oz/kM}6Els?bj Luӧw╛_Pï'e48,nk6+{ L?{ .hnE+[<+#7}_W.構Xs7][_P~x{}:5_x2i׬B|偙 %>]31li,B}p>ǣ}N6i H޸c@b\ۮNpL*Ż<eL8 _u6j_|}g^dء5&[nj vb!dkhSO/WnX^ s؞_ks%o+/KxV;wi\[Uxc{1ާ>ژe}fuT+ަίQ?uG9j~8#u2h8ɺRiոVqӍrr,ާ^}!{:w,X㯺µkrÛ^^f{oθ z]+_5QnTZw\x 0OϒY th}gs_5|S0ey[=媫.ލWt +9S[1 Pz/uk']F~`9r=^z\6䖼'0ٺb=[|8hr?,v_(n΢OW)/S1x SB皬'h>I̩^H[7:x颸%Khû\۳o<7|Tܬpm.ZYokZ6"_+y5jx/Nv||+;ޟ{%,~ W~x-b-+7:`}y/.+o{3S&̏ZdEZ_bŚƝ㰸n& hM0yo3Oos6c}zr1/[%yZ Nŋ[rWuZǔ|E3!̖=g˞uy"Inmll^Z+s,w).~wmvcqߴȍC'aӭkp[9Pګ֍{t@{um,%7/=ol|˹Ӛ\Ir'FyQ:'Ӓ:u W{g|ysON^6lb̍]❽RrjT/o:z߱wQ&wxu_ o'a~}%^\LD+k\jFȮR{T2;wd׫(JlJ~5>@Cq=_*WgC:D[hsHﻵ3mm#sµϗomR..yvӋp|wOzņބ|#QOϽ83[^`؝g,]9S{~xɢr:&& + orťi{c5M?K~'{cm|m c!Vڃ?Zm6k[O|y2WiFecr>bjq:|鹍h׾'_8l6_)DB߻G}.wŭFٻÇMV>wxD:7}ܑe /}.mN;=L1oqodu|bW;.(F?+{`$ebukԹ}g}8szltgr{7n<>}۷}o֏'VU}xqN\厅V6IΑS>~aퟯ?eedC3uR߸ٜ\enc[8[rǾuTc7Ws<3m.kn^-ڀw\u~ra| +e>yE?["m׸F@>F1%Eeڭa/HtcvL.%+b)Wov8SqdIqr/cFm_ujk wiOțo˿Nyr+^擛W.ˏ[9YٳC;]|6>+Y ccț촮7_ey='{|q;9>,8kl83,1vcwZʨ|E?e[.6s54k_u;q=?[X{ڎS,70yrRd4ש6\?yAgX>mۮ3:bC,[ϫOזgqYKvr䝬m4C~XsGnj/Jbe_kGiwzncI;^gEh͉p@kXԼ&y6z6'&yQ?%{;n~cl.k/v/.Vͱ]ޠWn+u +jmLa1k 7?J{%c_uWSr863@y޵mU.G6oönk [U>X̱}ဎ_ sy~kw꙾/W.l4?+J~uXdןLǐ`}"k`^O[7>CM_`:a9'NSq֛5/Os'k^J<*Ţ͹Vv/_ HC\+u,s7$G_sS,G6~OniO[>ئ>}ywn6s]F@9|ϻϷn\o[ںա]=j+Y2K!Čnv6ߘ\@ƪ|捝15b}/9fܜMGNE~5~kȃs/puxD8wtceMPgM8dD;'CA5 79zbą^f{9w'\CU,w2'ysøxSY !}yU]80j|~ m¯r ߼&tXe{ΫH}Nt VkWr{_ߋ'pyA1C1:;4]3t_ʺ 4;ջ(,&޳p6sm[A]- LgKHFΗϋ,Y?G1򍒗BmN%jϺԙ+x5\SRN`īUX+}}sel3wMx[_T /\KuQ̫9~͑Kerқ[oAZGjyǓ}luK?Dmf7[~l^h9I3;Y֛Zw_+JCZU kԘ/[@& _^Cb^}_dkQYɜ|ЖS"_*?8 0|z5qc^^q'Ζ= 0O~CawؾܾWk@Sg5/W+H/+qV[' Qu[}9Q//{˗#/Wط%Oǯ9_;IjsUTcJnͮ\^wuCީmŊ]̓yّ[zq1m}@.œ e>ލ-mmEk_Ok2dU;.6mEޝsslna4g6gw]+fݏ~1lzj- \c\n{vqrKɠU{5/˧ή5ZKc9qֆ~nIz{xpq[j^d}9uO[\kp,Dob=E Ngz +n β5{S:WO[KY̭xqϴ@s Al V>ʹzÓ;V?Nnr`c\4s"nkLw3S6/I~8z֐w@\,w~ڹKֆܜR54=b}+ؘZ8)do:@m;}w]_QXUl-Иq|ei|nαҾmkާ5`!pry嘔dRr;GޟyLZ0Y;Jȡ)^jtzX~eךpɌtdZ:%!P-8}SGVGZx aNBd3amrk͝)p߇4o6V~gun f`XV<˙Ka=/رE,T2ݣsz^l]ԷwNy[[xsz:$ڰ.g`<uz8qqͽXji\;żs孫^)[]\_<[yG묿ޡ]*@Yqڰvӆ>Onj+NuGyY^W>޶"L^?[f<>/>)Ӱ?kiX?mŗޒoy]|jh3m\1d}}ڢ2r4_3}^U9vũw{an[rxn.=G /=?\xZrQog2mXgeeHW|vݖnV:0=xיSVd6kn|*X3va~@c=y,kˋ?wֽZ>k-FZx{}9qtӵ[-צ׶դHޘ+i\?lXvwbw>5k?̙fWH䬚3u::ך:a}n*N&]='79f < +s ̡<7j|?-m.89-o1G{/,|]޻w+M,6g{Moz6_lŏ\kn/x]c@LKB{;ݱqplҎ/XBd֠\~dԭ3!Fkv\8YuUvkA/'|k2l7(ic\mY_<}!/G~[?k"4?k'(-.`rX6M׬mkl:].vyᵍl7SyݭcomH׹b *{CkWg}2b1B%yaʻL'Ur_\\Np[aʣg[ha~L],6OO~uwɿ@-x {;/blځr;W#_SX[ S]SC5+*<|9]~~j\.N/1}mqJƈ3F]xxdoӭ3bN[qc5߸į\gU2͉N(֞U'9W{fU^,rA>W~m̬QiegXL c|rdGlWWbiO/[7T>؞>?91} trOZuAʍgw,Pō'e e'wkT%>"O6G7!xu9.l^sClV۵:kXZjִGsk]]}Ϳt=C]Ύb{&뭼栜\/^ N#f;.Z5&H_>_ƱsKday3ZTN16 ,o|NǗ:u|;s6Z\^l:ǂXarWc_0k%#=GVbP[~;vyBn}~֮sm6q7pR;ɛh]jޜ?\&q9a3w-qr7vV-PٓsGںm-ra|mqP~GTC9hs{7Dk9z{q|krgO\~b {\ =qojD +cˇ{Kos"G\׷l>+XP=|b;g;ACˉ'/;1.gm7` ?I~E:Oܚ\9Zqr| {ٻg9[/2k0wۚ:!ҭ}_h=-v,E>[}R?ÝtVoUNcLA W_z>G_Ο8y!ΣOo?儸rFNƇQ9]emd濆QcM\W>羃|^iFvZNKŝc"TW̭SN85GW/Ʊ<~ar[Ni>u;+oϯUߴY8q\p&kM4Šn|GIo;sg6f!gץk/?h6%3}󵬣(޸Xn=0D∍Y3hy}%Y7֯{ϵywmrsVԇ₋?9adr_?ym`МrK}ylẃl+Y;[o6X=gm[`QNcsR[n59^9rfͣ}k31Үb>ܿ[NRav١#[';gJi'~6N$#-ߺ9[׵qlמU7bGoj~X'9qokw +'|a2Wsuv](i0@})}+k+YInՇKw/.5[;tk!Mxܽ7G[L*؀:J=o^\i^XR,^8`UMzp֟k̉7W׾zY7a,*uo ~bbgr1@ӦU;v596:V^˵o̭:ojW+$jq=_Vb\];𿰻屇]{_v~1j;ɽx)XFC{?/mo,P1}hQ)xm9|G_ΑWx낸_ +].?sv#1{4X_ı6Pl:yͽɽm{_}b~5ck+ΐNf]a cZ#O7_:Pɤs+,p?7?QNX1@y˽s,1W-Tj:)a +3)B3&.O}/h]j#Gxk*V<.zp̕*}~=5f N'o'צ֓ +/` ay_~Vφ[/ұ˧[^zO^Zֺ5*{6}zZע5^6wƹܱ:7;9;y"WMͱغ.rvQ%Q;-v=pW9_ᆱon̓l?1Hy ٰrayr7w,W9p[/ -ֶ[i_b%6ȭiX[rBOkmN~-vo3daYΤ9Moi;;ƚ`bz +؜FAh3WMex2-/?"6V/&nxXZ8aDcabTr׷^ I ^, Sܡ>+Sk޸h.g.X,Wq|78L[LqŨȍ?%7J-0Gɚ_E[Υ{q\|w=ks˘/pqAOvh7eܯ7Jk߻f]v͉^쳼o N|b'+T|Ouw];h$'igX/N_B.`v|8]ܿC[8r7Sȧ'Z8.ĮK|Gy7rܾҸCoMo:E*'wkk~آ:#Cݮ̣w\D~a׉5Z&5X>Hk4x &EXg7cbbf/ހ߅Cd3vkzxkr7/djm yu2I +ۦrW)ms{ݶ~~K+\B+~:|6{/v󕗏Խaέ`͖3,$we1bFL*aF'׬_X\ 0?+y ? ^l>i,KKɻ悲Ag_'N>U떏ol _S'Qk_%kwS5iɱ8iמ3 WݻW-۾ru_<ֹn{rcW{s|- ' 5Svvw$ާ&/0K/ڝb }v1w.ewOk`fՀJAޘq+$l~+.0vnnCcWg9^ͽjmrղkZl03ް|Ō6ܼbW}>)*|UNep>e^3rf7jqytr-nq}%ts7U1ZvHoeJhaׯz$y^G(O~1:ݍȽN2q=k33NS[߳&Y^X`kvXb\̻l;>ZM}ktƂ2w 3^!.H/وromrr_|ECⅮ3y+}jbՊ,qSݣWgPy1Uu?#qo='ʸOilkOMvr GI9ܸtm`|mOkBl^ry˹^l(7?[lv46f@=G{KA4wmf[|N>}6nXvwMЋ7l\ZخWsa%{e-:F9aeųvz* 0#ofv ҃pmlquWkz‹m.Z]=кvRm#]|O`MAuj}$wc1by.,l~aM{6x'\,[;a/}n_e?vkl7Vں5?{';+ ؼ\5ծ.GQ*k-GRO5!579VZgwwm6o_&6jY ӕ>!W_^ZNY3o [~8_We _;v\8\s{ɜȕ4WӼ.:1a[[t׎R+Cc$7_lλ? 9'Xeˎծa%NkLTyѶ''emc@s_f8~HwngܳͩwwsqsY Չm.F'@Y%6&_ns;"?-Oa/ȋc3xpj#[Π"DA+?A[B}9q ڗ/^~_c[Y{<ߥN[INm{/o1[D[yֹ o^7s׍X\:oL޵ '̏/ g\p@k2,b].cmGG]{vƇ^>\e- 'Ww#o@qa;)g'\lYWoORw,_=ͷl;I-ʗmlx27u.NmןSBE|ۚsqx⚫O_q y]{cO>Pn;Õ͹{DJsx9೚^rˌg2G~>4V.w;lzmZc@waw뻗o++m,O\`e')ZƼxWu٪ &.ֺo]p*϶al,|_,&(KVʍ?Z#7qqϔ7~~2RlϾ_ޠ2;^\v7LwS,vsPƯ<\UX^_Vq+q6W&9k;k'P\<~18Jޓ+,+-ToqN7ݽ[3v^%?5=rmNM,ns;8B8kkၟ nM2[gkx,/s=j8_wWK8nz.ΦUm;x>or3Sܮq,So6{e:_юR2Xs\>~7v9ƅͿ +[W?ؑϻ]1'؟uo}\q'Wظ7~恹qm64_!pn>/P pqXDm)['Pޮߔ#X=dcJ߰4*[O[\S&kF[|]#aˡG0|{OʣY\պX~qP|'Zm )a}lj $}~3\߬}%EVc c~*_(>c>Xk;_las}=ֿʎsaVQQEvFv}W}4ZNxq{mPYL+j{rҳxǛbʵu+w}n"Ɓ/ 7o,GgܚmլϤkmQesIꃮ|5sL{ JZϸFXv<}'eϻ9>&en-@m݋6,l8a3vWW}uwpa Z!r]l]èmoU\Z+[i7άKaL9/دzcŝ_|9} +R`?ky⓷wMtq؍kkGs8`XovXqd}s\EmX.^ۮzd%kݥ-n8hy]sbl-}} ynXG<|? (g\"6(F=y Ζ|w^;llH"!#fa=4qxsU7R>8_~q//񔭘m%femҵ>yaȍs_q$6aqn5 ?'pIfuo=oK|/ |m-x1Poޯ9=yb7=/wO\ostkq>LZvo~w2QMY;`oޚc\gqSl f9ۗ4 +v+sk\lX,n3ǰ5;xWdw<_287#>ؤu&3ÉJaUouljvG|VOWCnRI\6=}{jqMy %/ʱv6W]۪Xĵ|~}lи5m\ԭS)/M}g[:xkbXz5/:7'GS}W?VZиUcHcA3W&vY.߫ +/kknKs7NUqz:6ݧH'w6X-p mrf9a֔Z.WO/|X[D+ +C2.~ֺ<<sq(w0=lR[њ%{i_XOM򉬻Xۭ 5ŲbNr^,yf3瑘z1'׿˓ӯj\:6:/|jqn91ks+.'Nٲhs9~gε k<Ƨcm9[js7Oyq3G(:̝ K <ͭW^%_^qέCT^5.槼tSm`aG=ɟh.]dPXaT ~q==gWQU6T&_]c|ƣt\:׷W3ڜsQ{e'ZB[qcdԤxw0eطY&zǹܻKٽɈFыWWf|VX`q9#+PٰIedXaIsך+G;|S'h.궜KW>p׉.'{\nxgacykZ50[w9= 7oO.|eݽOE >/ϼ_1yme,`򭛲{fQ˿As;Ldɾ +vX``ma'+N=Эk\)=oq>~ V&jv;n>sR;keݹSמoV9u81>_[y"j\:nğ /dkt+_?tdϮx7gWq~qk xYk,wrxbګo4_zoΑfsFf\f!%K!FWvt/gxsv6vi]!ʵu<r{g#+k.GmOT{~Mg+oC昳c<~7к:YX5ok|l#Z;2K6>~UlWY/Z9ysXzvT3y]cFy`Z.le9 吝}esY~M;}]Q8|^sgk/%fز<V}J5-rVǤ8nȳ6_{>&_r=pxci_Wq~'㔫WºN6sܭ괉T/w!'Zgw=q9 +?{Jo}]s<̋o=WqExsKǚs@aY{1ś5VKqtaaw/;Z%Of]νkրy{x>9^\H󛛗bԍ{kh4͝(Gpص\'ۚˣܼymKz}}q92ˇY1.Pͷs>'̓'ͽZڭ=46׸`Į o<5].|G]МWBkv]D媕rSb)Ƣv|<׿bvk#?4s3[)3q\p"y|F\`qA/Z d;ߺFژ8#$X6gܴ.uH/voyCkioX#unwrw)OFv@gX}^1ez5=r'F+V+H9'_7ǚ.f"ݙ?imk[Z2CrK6Ww{l]*M.>d#~V[NY6/r̵z/s]70<6pJm=p20b(`ku@69lFrrz֮ (N,}b yR4[zV>F&<ɸ)c>Ե뇰pj#}|x&s<v OGxmg46ƃ뷫Io~Y$[?uߴ8aqw͒]M9[MǍt|0ƙ \}6sɹ'w}/1ټ=G4YUSLBS9}>*u{cx͵Rw,6,>xg=߷<[nm)l=-ry7mպf90Ɩ?#j37^s\lMŏԧCZ@r}Rzb=ܛrJs{ӝ'':r#ko6b{쮿6&Tombb/_|X|N?8Wzc_ +Gm怊KUKu*wW3-V0,q'.c+9o^c6'_š ܻ.]8XcvO4w{q%=k ^r0m'ڵGGϭOk'';jֺ?؈Zsq/nk7{qؠk/Lrދ}mq!ッM\_~s3?lxns>=;?;a<'^Kά%S k۷aqu桺^ت=[_N^}Mwm֏iglU ZPΟ5˫'o5\;uO!kC%OY|I{ϰᵗ(]WINj] 䰼rzuy_'ܤ>Wqϒ}o.U3G9sc|c7_XC"Av;?q]ks.K[sq!!7װ?1ĮO_xq_$3P}ql\rU=km z/SS$v u>>~-?mkb<>byxBxd[4' \FîW7x_}wl/!qxW7O_LzəH;|b1ldqkԾwmrW\=t,:>K־+מbٺ0Ɏߖ xߝ]U!Ͷz[c:~֋\?Ύ + 뙳)~/xZxbbZ6bՂQT۵YkZڹv=W?HN壹V8`ίtg^>b~2g>/.FR|1eʹyɲx1)rCsyXZY-^؍0ɑ6ve -NW ?{Whk(W~ObFA+b\;m񭻯oXy1|8+1?oQ<971OSs77Y٦<7ne,kq jmb;V{}_xbbʛ8ur_!0DHJ'1Tq?񀸍&ic/3֔]gm +I5ǖ'gNܰOuq6'z.y>ok 9Gm]km|_1ߩ>kssY?!9 ޵o5G{r)OTWL3g;홭o]{"/|]raxg\dvc6H>{"%zj-pضΑN g}FO7lQh5߸w.4鮿#߯ɖ#kmQ#gNm: m͸]qaq%q"9aUrj0csV)Gr5$?4c20$+rV0Q|Y%;!q- wKA{CfX﮶W7Ɨ ݣ3e[nU=`7Tq׭+g ubSbWֿȆF|8$q7+[;`[10멕o||-w/~16k_/j%|o=#;,N'sT:qڀ͛l\7cLq‹s 6vXǺ 62ՇS5x~084?׸6k+QIVͺ9:)r9LKL[$qO&7>q,nkkuɫ^Yy Xm4zӱŝ&PڋsZo-и9uk\gB^~+#k|n<1RM0:b~TZ[p$^⫛í-'S,+$l涓6MF\LVG"]ڮV+de5TɅVJLuM3n|+'5O{j-߭#'oq9{^k[+cOuY<ٵcmM0L߾~V;a aww}_huAg)OiMaai=k6..'HY_ ɲY.amݚz:C/x>ޖW_}}|N'ןhˆw}۵3; /./XoMvUJ.~r[n9'=ַpsΠ9/rg[߳m웗5K׫oVIq=oqTGyob򰖷&O{u>Q'&/R$<+Yfww)~SjX{T߻c}N1EfԜMw~ 1r&dcW>q]ڽE8_: 6o6"V,.]ck&X?.OY{ÚGĐɏ1|8hʓc/?G[H8p,9jab9!9k\=}k8ܭ]MRX\jZ.[}^sGsutw\bkG۷r+׏-F;n>+U/zl{^kt,ݑ~O~n}mXy奚W/GK\Jp1:qټ^1]~rrz3x/w-d댚KoZbl˟[M|=jŶ6?}v+߷1?稜hca8Lͽr:WQyw_ry2Ly2Qxq7ﶜh{m/d9[:[am>nv]sS Cr%3@ߺgc]˹~` 1_(QdYmgwc֭z+E=l|,ں5aQ}s{\Ͻo~RvrpHr9M+gm89by-4߻1Ϥy/Xsxwuoip6uwqN *j9F_$3m>XxvxF1vbo?&"%%nn<5띹Vvla[Oyokk)f}lx5fY xl6Z(oksݼp?ή74Nr(= s^fy {=OVMXBܥ>fKxߩ[zbuqxtGuԸSl 4FXL} ډ<ӿ %Zwꅭš[.Òi5Co犯-(:sl]^%eu[vYsk|,R\C[:[_ 9 ^XZ| NNvg6j75SvG.|/'okLXK'=}rn7nIKZ|$_;_/jok wڟj#rSor/OyMh3]-rIg[ZΘ88a/k+im{6[[b5};˩gO-s^iu7^\d֤pΉk["P}-L~MN~>ښBZ{>_:/q'-և (u^Tb WZZ{mG[6\9zuk=g@祰$oι!0cq3qSnjcEcs k(~T4w˖-tcsroY=G-~G=_~_~w sFnmyyb2A͝_{Ob=yEm˽f,@fD_x߭A^~W)ryڌԎ7_Y>KswX`9QՔn-~so|"7o/Gg_y -V߫qp0]}U95ˏ ai3Nps1؍nlמ_>䶃pFK[rAWk|q |{{af{q9^\ǝO{~h1E/^N~\<}.9?[:W؝k8vN{Fܹxa١jŘknƚ L(j!}1Glp0|}/[3K3qVrm^3#;]@>roym%HvP>b~_?8Ͻ˿v68w&7Ly꾋GyϥZ?ΧVcf?C Ym(Noω{h}S,N|)Ėt-Oomw};1}^/{P-&¾VX3RiM8P[> +ϙs؂j#7 Gy۶[󔶒pW{)uϛ.qbY/X[7>Mi]k_ sk\֖=3?Y:P`_2o{398ʩxk5S7Ǫ9m7ޱܮ3n%[{hדgvߗ#5gi]_Wǻ/7y~ Ә-\+>c/kl)rV />kW^vN1NcIc J~}JݍXuq/3;֒;}%oFl4_mmp5 +rkKs +oQ*r\i׳o-7y+D6`١b'/y. XeilG +/nzh_z[Yy}O} 'ݯV{hgj+[`q*w{?zOc,:aj/?k_SS6yĴdqڪa3{TIt\ތ8\oͣsk86su~XHn~孪IoaZ};X| u>pzEbFyo.gyL_pscm-'qm/{_^[Rz7ȍ縩nJ7۪±}.@!Q_YG;_/WR^}Ge9ʼnc*\S݇b:b2xՖ/awwrskj['Ofnqͭ۱@koh= aks Z'tP1;V6v#-9d4rejmA7wièml55~ +; lmCL֏Í,ncpw!J\5X˗f9nly=9;w/66׿zm&?6l<5͗N)r8WZw+<^~͚Yc\/suj S+Wvb~hf~ֿl4#LoR 7%\Z-^wt-μ߉-oifrSi:9CmޥV#bKhmLɳ +{i-amEO~;MP5Sa}bm/\;.?ckW-躯gm[b+Vnݐ[ ~!yR/7 +q.oaω+ps+z.1Iq,Kㅃ?łA[ɸ>ѻ߯;GlK1Xcy_8C/^ޅKjQ>ˡOYl>kM?_07_y?ϏMR]h+%~%T>`8<ޜmN\kLOkW^\1sa%ť [f}~wmyyxOm8Oq*ʩZdמ]8=:Wc/P{ r>P:l&P޶zǯsu's$C -W53=ݝk5b H{'W[cߤ|CGw퓇&fg+'hS.θʯ]|]څL8_]<.X-W[,e뙼tƵQŵƪCjrս3{o{jVOj'͌;[ O n>ئs|ivV˻Ջ'>%陵 ë5krϺ 緜?+ګ _=Ա/O,Z1Ҹƚ7lDϾlVl> ^^qbpKl6jfb;Vء:⵭5XaI<>me%+^`^b_)Mآb~q'X\L{1_9Qi=[vy1^{`'ğS"W^>4aqiqr8ꫫi|kW1.'0rrr5}g>j}.˯Wؖ9{Xf>uI~#|}@mu~kjy}9Zai]xs+V"xy̗kjy7RܢX.־>kPq1ާ_}sVzs*1|9owo~wӶ{򇪡b]eNQ<ֆ]Wt!׍?uy1z[^:lhC8/li.]ˇCsq,?o36R7QTg:Y8wx=a-{qIm|$cdlsjtoAԬ_kֽMqoG\UͮvM(fn!\ZRHzbc¨;=3|?y!o5/q1D˗\[/. v^oVӼ哈#.W{s]ꂗkvmߛ?=uwWk:!/s^ꢬNXk;9G<__R~}0?ݗG8bRbf;^G昐(㝧i`s5u#/mA9wk)䃨 +Kve +Õ6o 9~S,܀]:vZ*+u4EF]#5[k3w5xƿXbC!KlKVcu>b6ln ,Yz>M;hX^|m[ckStC\lxDڶ޶Xկno/g„̵uBoə[pvrY xĭݜ'N2jz}»UӴ>zq"IӐSc^0I1LyUdsԆ9ɇb9>Wy~Jk|]0G\0|CcJ=7shN}^ AxGb-[bs_9çN؅@Ae^87'"w߇w޾j77۫oQ|Mq_q1ņo6گ֞ʶi}Л_P`흷ļ7,x! _16nB-}dwyaGx7Z~E' ͫm]P;b;u[119O׼'6ةܮLorbj,x]7jfN npsL{\|5576č1őz&D,(=<=/ގ\WwGҽ7=޶+W~x ?(us =]VY\Wv4jݫ*N9_uB:s1)yt{NǓ1piq;;-/0}9/wP:'~iǗ|sp6ϼ*F>Oj>;ugW?W0}Znl|9; + }9Io3E旑׺-fJ\c-ߴ. ^XB[U6ZVXsxoϔUIm]WGYG7/H\q;6]WC'-3v1@-=?-n 廪j|X@&;U.8Hފ{뛊ó~hvw8]no;AJ9޴;4 _yK O&wkG~}#o]@"^mr4^(7TWk(F=,=pu;ϭ*O`<ռd>WsWsk4a钴RۇypMcZOc\~R?ϟowNj'^=r1[kOf~q7q:v `OT-_On؜>46I7Q`|.br ߺ{}{a: 1Nw{kGGܹXGD:i#GM6_O6}]XeYΫSwnݸ/l;^K]7O`8`֒ Է[;nv/1%\6?b;a1rw~mP,>ur`':rM0WlM[n [Nrp63m_nc?ˁ,|{S7Z6=Lh{-Fm 59>㴙_q[ yG{m9r)aL}̓oo^pO8CZm r[ [GVW)%?vy?ScއSxN\ ך(Ʒ{h^\s_7 :DsW,g| -cvc[{؅auHsr oM:n.WO^kؤ6V|U[rvsv=j^{U/z}BnOJcn91տ-ļ+k6]XxqjlZq/_zx!O6OIl,jQoxٕQk9 Kun]EB>e:bu/\!6yzġ7_5p n>Bbڱ}1.pfUwu==xش-W־O<^c253 /6켱czΩc|ju>^ts r&mSy>/ͽr_`s1F^ 4nfm6F]Κ^)6uu2 )-9?&CE>u$Z|>}08uvk [>xk~w(8gS808kqn0&FEZiʬ|}%Gy!W-_v5cî/{ .qԝh[ YYȶol=rl5,3טq#L:,[%וګ1ǎYy<1{cO6]T\AKXc渰X _|_p^qxa':Q# +HyNokw6D5ʡ{mǕ5MnoZn~#wkRaZm}W\/ 0|ﰿo?0;]_~ǰ}5+K}ؔس\.O׼g> +sȖ;6Q[++35+iikFm:lg*;Q0}ѱZ#j 7r>/?eFojp^)r ^Wj#Ӯ|T9> +SWս%ygerC<.>_~!a]#j圎<[Fm|8a6}X +'O56ꢵwc5};7ϧMPzî/wq{cj8G^\(k6ke>}n6׵>HP8ZVբߧ׼X[5ClNM\d'mm ?/h]'DފZan1\hXL[Y8~Qpk=tՐɖFslz^rW_\;Η-sns&Y襉Ts$Ϊh-7zᾷuW6kc"uw/r}V/G26]˵|i볍g-vGYIk//Ps'/~ڜ{bv>1yWqXwG휫1SP?I[~"ڬ\|K/Xm8nۘymKsz9[-m?]\kN^vT_:cq\>rWKevZ_9K^my=SGr01qqu a ;/L,Voyo40 0P] R׸\W^xr?>\Bv Q\>y|1CEcA{csdgXvs~y^Ov}4=*a[w+ok1>?+iK<I2gYss{%u9WG|m_~<5mlk5]_-qmq*y{r3Ӎ- k,fm-f9^qoZN~_]SN?.{޾hy{6'nc&E~N0dY*g8GK\N8am[+&9\Xa}go.ӬqA5_5"u֢VGl|?1ߵï6 KXڽ;~ׇYv+V6n>y +=5kNmN[ꔛCak?ʥ[.ؘ+6|>3>k;֧Mn_cjmgec,% v^^-q e\'{o2_X>rm>/範4.`>K;ZPn~BOllMoزZ0ՠivfcn+,&9 ɑO뷮b__ZW,.yP9(:-$F"29ݾrͭNusυunVwv5Gϱ,~ݷXW>);}]"s׫[{zybUg?!;Fƽ`\OWkWO:#|ы? b +ܝ=z}nNb09c=^~}iyеe6y[0 mX[!q̹G>)?˟wp]mߔf4N;5ar_)'zElcˑquv3t/4w!".H~OBͼyjlsF.=b~SJ8g}>]1׺=;7ڹ2lôŗV4[s/_x'w|rswaL}}b{/ror+r6{-񿸖CrNՏg)Ѽ|$Ogsm˛l\}h}V?zî_9[/Q}Y"0<sᵭ]cѸ|~ik¡⼴F {t>b?ƻo/ˏ0Xary7+;{O5=rZD98U9*W[xZ61fu/Kj|kpޕ#(Ujm?qIV^bow{_s\Cwͪ I^m|]#w[U?D{A~׾7wZu,Ɖ9y6x +ŋI5f/09]k;6jru(޵[k,V"/~=W1 ňghg/VM)ym?[{7nscs{/5u_>Sw[3SVXL*mlu:cy5˯Ł{|9;~< tmb|O fsFZk}7\}0wLyӒ/uoÜZ}̧\y[i+go]9|L{X "c>gjj7y[+9Ȯ {\ru/L)V@R.\\jҏ-ǒָV+$U?kSU,R%~ }t}Wwܲy^Xr77g[a皸ŕX's9 Цm-͞ z+-Fxqu~Z|چ>nm/SZ?Jmum s9Vx_. =GH~>qWamBabHgegGz&P +%/$|/W+Ϡ9~9k<ڡW徺3x~q2yva}L~X3P})hO5Nh]·r𾸘cB~/_cN?<[rG#'N׶q'-;aqY+0*Ѭ5t~~g?k:aa?Nn=brfś;L8Gӵ5|W^; w)ƴv :[;;nr'knr+! `w,9ur+,'Άd37Lju=4x}mz߹,׹yOltc']mW [rmsStli:-|הVlѭng|p4my#{uSrKWSl=ܱn? 'idݲ|>cW_?wq>m!z{? +o{aBkw-ylpAkʣup_\0Wquy|"(|/ 볟{]S Z>5 hY~>Z]1C[>5/L]˅Yuϵ_F(lFlP,P]}g[~Xbb{;1\iMφ)z˃֭[-^擏+Pz5-;M$o_xKs{9h|.\?N]4ϥu[Sjg~gw~'I|k7yxƑnIlIW+zk٬:$zGw?cFgV jwm{wPQ}llbYml;޵]ye٫[ +Dq-g?Hئk|3\pߛoGV#,gl~8sHZS՞}\AӶY{ٵk6$?zi m+Ýk_5%^ؾrJ98}ȵ/k/Qv9b5f¯cœoc3?'CYOXl.\gx7om-y+F|5n?jaWq+ ճv^2iJ>޼rMpiyik=ݼ 痋\OMr>{1Qc ]X__]c@صyymO[Sܲ 4`yլ.{v~/g/|F{ rՅg8ac]&gڱ~FSmk[|h]bDbͩ~km+Y?QK.jjwo]Wm]C9aoa}_i[] <ߺ`k87qsm妭'/gaw_^!F/|";E|q_+t}b߳~ּ<O.۽Ou5Ud5⟯|_gkxr-7rj[Nk񷾧_\-'41SǺccX-bm\m71}xa]ş yՕomVYܦù>n|L,MyqNl_Qڵɵ+<3Lk\ϖwnn×jw.h8: >/EĶWad?ӗuMޜ>o]؄͛ktG| oCn96盶0iŪr8S^|<{]ÓnMh/]\Č)vqqnmncju/#xqZ_.#cq(?@.Sը%h}+/s,^Nzb-vw7.,n'[Lp˭]k_6:g5Cc1inn-'L\0rϮ+ΘTTb"6?~/V¼٣)^c΂lQksJ\r"7HV.y\}v/_Wau6C1)66YY\c.9N]^Z/?PZDT\ s5G]ɹk!%.8\zٜw=co>_[p~p-fo6|/n1S6jWziXd)&VcӸNqC%1omܲCGvCU\խ R꺜_&Er[cyYwއ*vzWn:x {drC/͌mߠ ה;y]Ʃy_|9Ǥc1^1 PFiz6aIdqEg5_^) /Kkk7ڸbW#nKbqLΘX_v9caslsd} x  u;o]mV|gvQެ{#ƕz,u?'G]ah-T2'mrw縼O{y/ͥO<{OqḬs߅mn,>|so2o`k`X^⣨G kQ#rϤGyZ{k孛 WN._6 P.Xd@-&(1Kq;k(yguc7t߽m,_3Yn*؟~~nsbow^TΆ~Bcm8 k;CO.\&kI_L۸X\õRlP Mn+6䚷ͅoյ5W?i/ճ*\][_r_qn9ݵu}0m1.:gNNŵJ4Vo9xM8`sz$&s#6/4顪^nQ&vxc|kC9R_9Y{}|-D|Bb5^_\X|ru?LE{bcz[üuw_LR +׏}Qs뿜k ggrI& &99C洴wZsPjԨ__~z__//{>{<|}s?=>}m׎}ڝs{w}o>~~_y=]wz}v͵~kx~_q7.w\ٵ~s==Ssǝs׺ٽVǿvt{_c/>?g_~~v?~>?'???Ïwg3|??OOOwſ?_ԟS7wq6\{~~?lם?|;~{;}=}׺~ݽwww;~;׮ɝw׿q=o}\hw)kιu׏׿׏9778>_ﳿywܘտW?_K/9_]cs9wV$k_{SkV_v2H=;ҵkQ6sG5_:suuwS_qmnݸܸݜ󛛝w=OzlC׵k޹yc=jomY|7Χ?jw} InW^Lʑ$< %O.wܺuz5Ϯ\Ϝk}~w;ܟs;N9wtrõi'?]>=Gݽ޵u̷~18x{r~r}|'o~>~'~O?c?co󮯓w\?[w46w}V{}ޘwϛy}qض6ͽW'kn眎s1{~{1_ߴo]ձ1ۯ_թHlw]wǛli;՟/=[ }{S>hɗ6*{xwN1Yg鞵]X=lݷwo=ͫS;?3=zkRy~xK>g(sO7ߕgZ'wuLvɇӕyZ[_]~{G{~ں~;3mӻ:r2#!~_թ;]JZ`kGNms}{GmVgPLv^8Q^??߫w_:כ]}wm?ǝSv5V{m}׮:{/ݫY>ofz>q߹&)_{yݱyz]K/,zXAZ=}0ܮuܹ鋑z܍r$̬õz]?ߔcu +XmJn[]!T=TCt{{홧#~:nX麷Ǐ~W'VOϼ#0t˰>LgUM@ھe{s/sl!{i|ҏVbCbk~|~ K;#86{>}wޥuNt7de}Vѷ#>}duA}}1uqpk}V>w_>nۚwr8[C%9y;׹7FUg+fjcX:n;ʋѭ|+o[]oX__0ppA-103~>yq~su >q;g>r{:}&`6y b7k.}LܼܵGoˑ{aWk%W_7uA>V.x 1Cj⸵sw^Ot,Y"7>[^٩hikg{v=ms)9N=_c}}s{l:g>#Rٱ[fW_[!~]yͫ^;/N">&g+h|(ӡ_zmx>*ʕMkens}u}}}70?ttpܰ'iw^-$>,J>(3e՞oVOK>Clk9:ļĕGY{}8qO֦qֲOogͱ֞& ]}/Vc|2+..Er;<~i/)|跭˰֛<ݚW;cawl8n|傇]n߈]vtmΖ(?g9b9)/~qΆko:ךh9vkomlOqLƒOXoό[rq;w3ىI Ӳ;‰$ڜ>kpϲ]`9IR9Ɏߓ,TjO Kn5na OZէϲ# [n|AqwZXӷè9vc׸̸}rZ=0x;OG-peq:ģċvݣ+r}Iźkw\]q˞8/u˛Ms^hVH~YGlfxAX8\ol9Z{;-PLj_sŴ'61[\,&Oϖ [69s lĀŅ;6,N,.ˆFYcω}$no,\ʧuUbsWnorTLVy$6qy8_U{G8ykueOG ?WuuMzK7aӗn\x|;6{q>c!kta";O%q"z/pGE,_yqgQߒ_^,5^rD7NH0Qt0pbQ|Ą=}rx.N=\p>,й!N+e-9okNݳəi|>[}_)"Δ}@\>6qk6ĭkgpN${˚?&7_> oI~o^#-YuW{^5.[{̓^=8Z8,w|_?GkaWwkn>ᰡ{Doy͍&az}Z^S +ۜb5Pq~W\<؇{/R-(,>嚶XorW[_c{ lj !O*̧~k ߰;('8^_bN>t\ƺo:ٽ__}//p1pL;.8bZN&uM)ַkn9/lc|0_FϮgJ4oSb{%ykq*ʣǻJHYvy]s=A^\ƯnI /X)$;$"Ko=l{0ϐzBζ랍v{eFH[<;ԣXWL_A-ʫ +0D~m[c9o'w^%Ĥ4 2/zN\ƿWXѸk+n:|u=uN,OW=FvuMY9G t{m39| Ho/I~Pݏ\|f_G;Wga$?'Bkqˍ%[?FTy͕s:5G]ƺ5}obm~mmlצlpkW\#a=fUkN1_ϝB|o5N~u}Q mg(gn}ݺ`\z6dGw<_r: InQ^1tߚbˣ 3<>sQnK_˸o-뻻v愹:ݛ'ߋ˔( }n~ĕMyvc+^XlA秲ٸqydWLJmY_nN{ɮ۪jq\msO\\vl~guok'_Pr-a4'銕s˓bk{y=ϽOgnoN~aaaھڈyY\ƽ+vܫop?{Z |r3_13un86\˹v6C Of\嗬n"Գ+v݊O%Wވa/yo\֏oYژq5m __vo螼x{z{iۧrcw1_[J"w_k߽ĩB[6cm c/>_k9Wsm Z%Etؘ7 CKoJ,!\J[E⋫O#nQqgmQEO~r:_o޼u K7nԵ]ӮO#]\{ +2w1Iqx​kOcu!P\<ntNsL۫[ʍ1-^0{k?LvP|8YnMdIZ'o>޲q`+[3t4̮|NjO_srڗkFֽ+GZxfm7Ѻ. <0Xqyl5w7f{ȋsW m\֮:r|Witx49}}#_q \^|}8l/ŕHz=[7OV`_IE׷3Myi{>%n[Ǡ>q;r]-o`v>/YSij=_p1}txqAO)_wf+kN̟Nt| gu|~/VLGLig6~TH?1Ht]PGT?XN?纺Z)ZeՆMՆ:u3a-Isg g m+\O{iOQᆵ'sqQ_go ;r9'nHtuuҡcg94k/Xku[5=X\Zikk|oe,LUVEvh6yj쌸Douy qx䤙x,q?~w-._9^$/^g}/Әw/]ر|TKnsהG+պ*GU|*Taa)}u'|9)u.&I2Ř0^c}ovٜǮo^ X^߫s1rTkC LJ C7+s(/6oR%H/9u"咚\q{Fy>kݽEuE基p ;8l}Ur.bn.tk[uK_*9k.G>d`Gmk#4M'9{'-=cs=n>GkϾb\˟k97P~mu൅מǷQkruz7f^3\p.F\7nox߽AFk-n0:9|܄[+ Pshȗ]߁8O$'bK^ӁynG3[.#>K٨[៮!Ɗ|X؝omƌs6nqq(sy^|Kʦ+V9'U_&6h Is%5Oޜь︲u֞Xaگ^S=fe;Vu״-BӁm?go>];lO楫i_+Gnxػʵ#gk.'x*yѳ5ۓW1hr1GP^5ن דkq_ mve}mW(}5CxF7v\cǿ{x6rJqfqRzH]7\. u)U{~1a9ڪk+rcTbPgN{DrW8Nc\tM{}kbaʙ3B17wyYnօ|03ŋKNSp!uԗ#znZxB%y4'zu~8_nSyb-*gDpbh}L/\+{y>Õy*O'v~/[bͧC۫8yߩO}^獅o=oC_Ѽks3ou{qagkVM&;ʗSL2YȫR2{|bˏWe}ͅtݿ76Cos#>-wԽ˛Hs$t7ׯ=nDܝ|3.Y1ú[av`ȓ&oG{^-Nqr{ |1q1n[{be{ڢ/;G.8_s|r +]Z.yVɫ[aR= ǔ7ʫ3(&'_rg]G[t9=ni]LihR1h}%_,^ո'~Z{6[Xz7wr_?X3^W[L?RZL_ƣLr,VrvkgCxT#@5o`]l!y('<>qzUWյ\ /N .I9q5mM&P6t[O^7n^qUXqRTuv؝'s:5hlҎ`.sևvЦ6Ʋk /3CȚ^۸&8r97*}=Z?My&.n|坾8&r[7ݯe^}; /fzk*חÛP'GMj>3/[m_97:1;9޸qw=!(i9X4l쟵Amӑ}3:gZwQi :6|qox *ֶ(vhɾ I_1q1 +xClplYÈƓ/ڮzZC\"%S7ƌ]I]@N6+5~[Y_i6FDH+#Odu\$kGiƕc:FxNV.+saW܉uwe#5[C:q&On*O>t|(+#c>:ר?}*T^frf{MWd|Drz[ݺ1*Nw]s 3u13k[{l]W} s^q/>Wc\q9s-/|^O:oc79"/H{^Kڄbdkq[fkb8,f~g\KkSA/[K{bermut -a9f} }䧈/]~`?//.׷o.]se5ާ3[]G}Z.2@dT9Bp'پ+}V+׽rla8^trW ?q%~s6^WNCzw{K{or#n̓.%9n~3.y%GJZnapP|A/ܠxKw-h[98b˅wZ66fVVsyՓ_VQipCrh:cbrH&m֌:ڋن9YKI~YvNj|L׃9j&jXKX;ƸGaqdkځ[z9%,v857o~'W0Nd{Xc!e ~9wf4[~85 .{kAt~4NdXF'6}ݘbfTd+{}󸈛G5GC7UiR^*[#n r0~_#k}u>_l+;oW+5ͭJԘ yooLkm (g5g,PYg6}K9Mתr.׮#yORw|UIN'3C,[mqi΋7ӇYKr lM[qÿmW,m|aoǑ{<[Ứq mr5o^]iYQSm>+\x2';%Wy)ϴ^k6%/w>nƲ-g8˸R& _9T+cƳ!V6W?^#q}k#8]yIs/yk۵i~-oOx}Xw[+n5b>>ͥV7Pl$][H_3O/h/'2S͸ |w[<1N;Hy߳1=[NR%C0wybx[ؼJcٳcNNkklo-_D^Qg5NeG!˙pwnw?8#⾲݋cߤ$P}b)g~ Wqk;bzW+.XOn }_}hG;qh]FsCn\x lm?*<XIxɶSͅi_5v_^2+?lv\,~tT9gӧ$^/VV묱.NM>GqoaXsrq_ouIwIqܼw 0ȿu'ߘz _Rs]|g?ŏi;6M8*s]L[ToҞ)V}7ljZ'u_ke+I'8|c97{]VZr?GߟzK:tr>ON7ۦ=hwu!c̗j9K9s׹_3kך<}g-{AbƖC~r>05GGN2Sθ`sd) zH{ce&{6_yh08U^v[h6@'1EI.N׊%Y/8dƋӯޒ ++֟,[ 0WhtrriӋmW+<{a|Ӌ;fC_4khsbU綼򆌿X1]Bn|?u4W7X(.ΚVj<+jSʿue|gsTo 3vW.\߽8s[w+̮ՆW/Eڶr,L.z>oא(XY{X{k[rI=+dfs.G+w$/,} SN}}{|=6fmcȺ֭%o-pbgnM9[Z.)b}CmOX:S^a^;y줓_ :9}GO]ZSCrs m8rgr}\[_ZruL +,?in$f1o1+Kh'a<#Ȯ^for8/,/v}+]|V<͗OuyӣԯS7VIlacԳu\6VQ?u:Z:c`H.u<7k6‚M-_\7kzwz9,PAYõR~=_SrvZ:˕-F\O.@beprst\xfgAXA>mLh2A,>46J.D{xvȘ[c]ɣs~]LϹ_7'g3 +W|_j󵡸@hgĘ)-5/C< +=ͻne{m]ntdkQ<VO]=9,}!{*7mNj'v-"OszoV7KZ=|\vc/c탍id76. ?Uظl\?r7CPʘt{X~kɪ>s3YٱӦ,kkm_1U[*'%}oh倾 [o#' e^}];Eɍϯ]GrsK^>#3;bȫϭ/OR[ϸ9jmkh;&.gml q?NڥrQҟOT|9ᗿ^7wy+z͍Zx3L[_UAH7&ٸծ^>&mcXň.}{+~:ozQ\ i-(a0p(zw.g3rr@=`0D6F_m=Α.&n}c.!܆3_WU/F؞x^v1lw#=0ީ^,Lbz60SJr}+ܒ˭mN~Eho[_} L +Jݵ{k_)7}3c8K60sP%m}?wTcWM]JsPCpk?랷5g$Ǩ+(?F&#1{SblTGk,OzbN|Iq<(g7-Ln9O6^hۡ]3[7O?isy͗Vdp1uUtŞ_ƲC@28>n>dqaOH'wOᄌvc>;x|;V﩯ȭ:Y]{9U^G^W SzuFc۟n7&'Y[7u>⍕@>p_#}X`x_5 .'.ʃh_em9w{G=9t6F{Lcd?"gWLY.'cI5VMA3a\Y4rcIvacsr:y'a!>ӼlS#}Ύb/Uعbڞ%3OwI[\[}6޵2o,Cۯ\m +nw^y:osi~ +ߓ|V~׼W9+8Փqc+K5 +^_yyN&;{ci^t^>UZ{j̑cHsST\s.|\tvlLsդ'o[O_cG`ϴX6{m)-jט5'/Fޖek.&5erȖI/W/͎\qƴK%=|t-!۫rWsӇcݑ '|v\r5\1:Qs壊/i%mn"c.7W^3ȡnQ9^yNj(c{n,/].||}LƘb~;v%Y;BNcb%e&6_wo\tcx䤵ViOu,6' AuZoj,u}mX[Y|+j,gcZSa9ǸH3{5wu2=?k_njr&\]盏pgK܁p&ϛgƈ*_XEL8r6ښY9Ĕz3g+qsGd|\<19en=;=6N\eZĿ>Nk<j}.t4C79j/4aonybM96'7ƻt_|b⎁Vk.TdӉ+?[U +m[y_m8] !k?e1x{f}0pc}É@W~>n +e{Sȳ҆5s}_1JMB#ω-!fl7&.->Vhqjsr]G7tɽt6i'36v>kþ^|o'=_b_wl2O_\ݭY eo6C>^gyR~ N4z=&^͛8lr#W}Ùqry;̯'N{*}'U}Zfr96Ә¤9֥X~h5AKoC^??YwG~G>Eyz}M*hosZ:9&\Z׋3_^s(O֏}~6vfz^~~~úfVj-F1qJ[l?;Wf'jo6 GsYH=˘Ayƭ<7bd`cQk9Z/梴/dOO|ؚ&ZNu(#Zg'.^Wq[W=ܚ/o>‍ۉWSrNmu?:Wbo%Vgn Կۜd<\?˱{ ;\.q7kHƒl̒zn|qek.lc<6vb`cMr9Լb`}򹌭0~%=LIHn4y[sv {˟NSޔ_95DŽ~6}S0/gcتu1qXJyU8yXw֦X=$}lk!ۧ<ɨ0 qͥɌ}w,+ߋG n>zx 46Z Yk]Pot|H w|7:֥P/@?~s߯^u%߬_x‹{e1A+kt>/bsM{}~gRlؘl|}VUWO\׮[̓صsz˽t eI;>k, O~u97SCՖpn9_ccWP|\;]>ΪnLO }@*}g+X+uU/'wbZj\av轞m]xgo\֪>h0oVʭFh݄=r(" {]~[ o.5ޭ[& 5$W5I} ՛VqkZC0 Be7.b y^E Pja]v+`cX]80ra ΊwݜY_40_X558t+.sa{.pw/}?/O}~wY?5~׍7T ֎Ӟ$s#-m/abF͎rN,VkNmW*;A[VY_fz:IO?rup 㛭U޸/O˺HOQׯ^!4Ū3i{d*bAէj]yacU5>~XYTk{yaOԇʨ0ڛ<6ׄK^߹abb9͜fs]26SIv{f0*}Uֈelqbg5gCb~%żߵ'zGE^X[otUqƛp56ӖsoLlUngl{W8w[ƫmˉxil]dښ(.̬$ϒ^=G-d^ΕE}T963BR5va|w`͝mm+b_]˟moAsSe46prXyXXzqؖpξgɖ ;~Ǿv溶Xsl>ଡxnQTG~&}uqz#)*W֯6a7Wڤ/W%y}Zz/[/c̖[>oήn9(9k/cTu8. y$/l>ܕ̴[ʂcv+o2P-?lNZ4&ɂ 39|Ivh_ݳ%kT7ӵ7yQ,v90G}u)x6n(ܿ?_==:.ls9,{6r }5Pq^׸[;SX]kPhQ% k|~8?Ԋ˓_ޟڔ-57"#yg0E9j}Fks.ҫĊ6puɡ;?CseЖ#xaq{rQ<~+F8y +_^mY.m{8l^)m<:v9lH59'Ϲq޶U/qeav9fai/m>#E}q){.2qߚ" gIǕZ;zxW%W噷O3s6gb[7E˔Iy,ԉ2ϥ\ï8i3 +4^Hy&̺g1Ǐ|}9>߼b͌c?\Cn[NKCYnv9h{.מMn޶b<['ǭ]/͸0^yesۖ'`͞#uΦ?̯pxgwV1#tw9Kq%i~n-Fk=Zb#[F:xo-?5hNλ^(VY٤ >$F}ǽ6_ QޖZiV3\۸L PUu%\[K_`Sy֍H6g^ceIg!y];'[3}|NF܍jϹ[KdRr8?j?]I۱z,.[W.X~fg-wJ7Y^*iQrq;0ݲvAZ֮1^变vnJw3;_t(yba憐ޮ|ʭr uz}N/7xkw66>Z.{ˍ51Ь$0aLܼI5\,cO|i6N^A_+=:o)疸r?ڽT%J/ [ r>+}^88$kZ8OI-d\% +rl7wh\~߭Gm x6k%<\p/9<ܘ%qZoӘ 1G8kִUұqɥ8N'9^ͭ5 eM7ɴn9H'Iv\LwcdZWڍkS~=fA{h6lnt@׏vqz,GZ٬wi<Ћf bWrh õÞ=򁦷ݼ'E_\@?rԝE|?/ƒ./O#Svt}_\3Ƈ1(W(483}}?Jy׻}v~~ <[X+k'mpҤIg2f1i}~g=Dϝsi~9|=鮭 shf1r؍]^xbڤG7?mr6e!rĴQ'`^8^/C4jSMF{lzZVrv琱ٞkXA?sϦ5Pnڈɗ!![$ň5u4f__@ZvƈķӅE9}/ڵ1}Cc|1]|an''=19zcubMcs44?M+/= X_ce6ЍG2xlm&3՟WrqKVOrX{\ubWu0Xk9=ʯZƻG0߇`@shoϮh2qsnύ)=>6x~׿:?Cp;`o]X['ZO|y2bmm|kV5..[^c8ň=jx|U79c9v]:"ߪOus1Q'(nh,p7T^26_7ϻό۰FDm7x1JL6z.N!uwm:-ֶ5\wr^:XX}9␽kr7`1HqK:Nr,Nhr+h7ɹ_ޟ Y1!6\LTW~i,[iFP1ew~ֶ~#7oۮtj}8+wnovX5SO6J6uN],}:);L=%>Ucn/G@u:M:{R(7e"{FsdSoү\ko?ȭ:tuѝg=5_#[p~~ wywxɠ* +WxUlyԾnnc^S{ęXΫ6zg_8>c܋~u~9'YN4W6{xlrjj{)|Iy;儙ӜON|({K7ƒJS12vGdIyI}5I6yrą3onNcKkӱqu,]OzusĥFv0ց YyãZwG^㦙_髼JV[:{5;lkmf_dg۰򱹦~c'#7.Tks1[[=H=[qCƟOvʼ}f.旺8gsLnO zEz%~Tߕxχj^dϕZn'/huiP}ˑm]޶.C[gfپq6WLpu  [2ڿ :~ ~m Z|LL\דWܜ#xcMų1Ivu5'+ā?mY~l<=GY:o>^tsf8x%4WuL6DŽ؉8KۍA/(fUm?~p\+b>w,k6v[Cк8 8e֗^Oɋtr4_1w/8^qى[ 7=!7G [juX@bh6x_6I`1t5'#9i_!qNyWׯ -5B=f'G8x ^;8.`vytuug#;cŜ?1N>,a9>Xy\1-9-pcxS[/!{l^[1fbyCd}mj}f‰x^I'סM(Gպ=vn>(q *y[ʭ#du嵙o5N᝛S%]rmOvo\kosZ3}=}}x6/C\{/m{6sQ L7_rQ>%OU.m2c:71)oY4v7Q Vv}\X1tu٢k=P~1>*ύ:I}۸h?{}L-hǴnsɆrZ[0VGl?c\C5uF>at0@s({͟|׻kk+ۓ}b#ͷgr7d\r?ariRYngxd@9ܣ3<\~mnmSsmGjsNhX xm,z[^ؘWW@qōO6nlx^m~׹=q^>ZsyD^7HYƛ*S2n`~:0卽upG0Y&zڸvLbI>_2c'޴5NGs=϶y[[UD7c9olΥ874/s-(Xg#3X%Giˉk_P;>~ZgšllU}8hVv_c(ĘamƵ߅sXA6⵫XjW|;q|UdkoUbYq x©+^>k&Cuۋˉ%'cݷgWuQih/n_2.pϓ+,ݜ-o}֎4@}]'~tk9FZ>r?,~!A1[G~9\7L1{ridGzMm.lc9rZ?#O$G9ĹL˙3M9kN97sW g1F~W;goO x+<~o eo1 )g=U*$T rG/'p9U6/ SR)Se-R1U],'+;M>?KDl*!zVcơ-/(>PRyA|#o;'G^>W 3WSwIu{v]q.th[l.溹ݏobVs)gQ1'Co99`Ntڍc!;]ͯUR>ucvnEH.SDõqq66q]ߋ.V~~>s 7&ViWkl|96&Қ+/A\cnkC@[^ye\ `F:Qg/zׯh}'ʥcԎYe/W8n,Kni}qaq>?<`wjل٤NF5yZ3Tsp|~8tyXhwe4o+Zd2Nz9xlؼ01}[C|Nxx 91Xke1ssg׵'j֧[gڣ~oFq}mg ZG(i9+!i霛0fӯSn=7_k:T=C9_U]aa}ʝ|ɘ;7y:NЕem3oܩ&P_z$w;P?cvޜ[3ټiq]n=b3rƬʩ+l8kmMie ݫ[<6ƳbQ77+_}"zgxf[dCULI=Eign_oNt8Pu-.y]{|KUkێ߼~ZPvB^`b|rw =1H$Y1c~UֲpXyS62N\|t5dC˾67?.\Xڱ&TGp߮Y>SY|8wclh;7`8yln}|͑VyfwĖ7Y5<]ǭ_,JqK;5'޵ [2;}J+w'0,"{@IsL.損\SyΓkf޾o?q+sK#/uj7),=ܘ<65<;9vħlfcFW ?~6p/(gRnz5/pu;l ͗J)& $lDGHTh(פ&1e6m弊Q^66ƫb^b| _9߇)_Ii~wN|c12RsX^ˉ6I=bqɕK˗M˶П\~q߮o-!6*>&&>q`6`81һ7vl3Uic\E9J!c"w(]t?稓5ҟ8QxaW+[577lc\gC]>vy)܀ɵy<]źe[Úe M\dusumAsgCڽU6˜-tαj{y;0w||~w6;{N7>h烾bdΟ0jYkXFj;.aW}y?x~Fp</wc^!D߮ys -y/OkG0zbHw߰mbZrԱʁUp|@plʻkk6T>8GYe qs,$Lv='XVD\nnl?O}Z:W'P9bgiwgu?9sEnnr-jFs#gS/L&A6KUvxmghiN[O z}7wplWr.oc~754AY.>g3dbG|)_[+do}D#|]3n`r 'ׯDy/O}_nls'JE̤kɏˮ<Ԝw#,*okü5k帺Fb^)~T]+LvmsZ5k-^z!1V6Oƽ;ŕ wxqo>oq7\N/>f6@<ڼk_;m~`8WȖbYRjs8Ü;󾋣%|\.:rĤO]㩪!?qLFh_Eb/'`5ͽy'?׆l\9Q^O~m}6iLSċe.}4;=~Q.*_oc<1NnL'j;߸d/h.*_h~,ob9+#͙r׺{>[2<:mm~[ h>rq{^,yk {rߚ%w+o,VM#caq=~Qz2dqt(Hj]&n>ݽL7.v[Ӹ bs%\f6߂~|kj lnF^~ iߨ#? 쳎-O]]^דiNcl9Gե۝¥?C #4X 殹fgNn?qߌO{'S]n+_wcŌSͧW?*wrٻ#SI10㉣GZ^ڬ%z p=4Ń!^?s][և5;Y}[N SxOm-}kTy-n*F.*Ny*}:=1_s5NqK˃_cgHKV30>+ob2Wkxx<UkXnkJjbɊ\{3臒 pl7>To%'T}nT,Xl(sCV1fjũ\U63f\qNm0ci6`6cޜ^>!j嬺b(rbAX4Z1a +ƣҘ67feyqZgS x掿S=]_yo.IoČa7tIZQg h q\[vq8m8uUq+w |*rI0XL<9boz|M9]F>YHڍK? mNO.5,|qdq oLAW)>GShr\`󡽵װPnYw n.挲F}S?5bzY_Nv۸~YG^|WӍtlYt_kX=%]߉(ºV-7X$ksu8ns)W+IֿrKwzߵS}鮥^U<8ݳqx]'gm_sd}KLƁl\`sWn-ޚH&o|o2ù/?(}J`Q[[^|scŅkf/;& EL'{ 7nKq~8+/r!r]:خFK[>>Wy:{\7Uuso ^lUoկjGmL3,'hsOog[8aɨϭ ^1˽X% `vUNWͣ'tvȫA2wylm$9{v-ͬ'a`>" `smYcɎ7~z.v [F\6;goӯru/= brތyqzf_uǽ._.dc0x/=\ٔKNCMk W\.IgkW79޺ 󗿧lmMo3nl7<ڽD\C/ΤbWN@@'Y`yRtl@6#`̖v9?(#8,l>7n-SgY`qټ8rң O.]~eJзj\4@qp51G^?&s~kv|q{מ_xqeà ZL^9^%y m}{cqE{8XmÛZ8u8wˉ\Z۱ce07+.BmE`ܼipCkQy㹘*")me56Ž Q<8c7O؟k/3+&k\}y-7_YָsEgr+~covxyN}w^v phH3玓8ju˛*v^/>n,> My[P?J\5]ioV ㉜iL 鮯g b@Ww5&W?Nk@.P![3/_]/)3ds1cIMV^y+}^כ[-ϧ=οjl1(o1J}Z϶]Coזq-/w`7?k2L@]C=_< 9H]30<@\*my{mssb3Gmeq ,[_DLF]h23$L98$גEck{Uݜ 8Cao=[a??1{~?i^hnecycvjp)s_bƾw[}ZXN|j|,n>cecr$Q^_ML^xɹ@Z]yg͏Wpl5r|qϑW2LU> ↉DŽ՚>]b!66(/qaH39~ścY^6]|4ˉv=lrs% l"훍06P_+A=97qEEs2 ?5_ /m㲲UKp1xb4Uv'7Cݘ!?1n(==]lǴwZ 7wm6x.ŤWo]_׹ɍ~qt*. R߇lje}_Hn%m%GDKk,k+ +8Usˉ'ok߸{kmݯG6/X769y} r\^k^XbQGE>il?ov",xֶ1fX_7#4TS_=V R-ׯ(٠ <#ޮys/&UzRʅ4>_X qn~ DRc[9+{z;( ZO}~~wɒ%Y|[k}WSA?}Ҿ$ <'H~7mWZwWWΙ[_w;u _"I=AQ?ߴ|F>h\ÐԃM+y{/|z:潶կGK7 O`c=Yexrk%)ɇִ>7˻~F}^9QOo^E{= +&{-X+Yэo[SD,3rJ^YWh_çN7έ;8m&o ̈́1GV +z9/-zx'r5B%og8zg~Pʁdzs(n 1@s6&~>9}_^5?ow4O29$F`{Hn|g}^W%*yWnocٌ_ڗkW@1@sa׹kr|䱱9_#oO9 g셉{65,66t֣rsi8<6BF~_7kO;okYxsZ>N('&(vu6ceϷY+`pN#n/d~{&}=kxƁlGZb ˕4W<^Ook +aiM0a6('L.G6DU>\+V<œcP>]DZa[>,.w8ݿV]֖PxC:k?1~o-7u}>~ows{etts9ؖ~Z/|m&g\b6nN=6mP6gV`{irm}?֩6E/|]>Svo6NW̭mw_s%Z\ 6zyX<_lo_>ܞ~gޏ;ouy_r{'6epl?iZS#{F>M2giko@s\ɍxq%_n]?͝geq]qsfs?w]x1YߛW ?}3/'<i⇮ր6C[ngw qqJi}=Wӑ0VfMֵ*1judr8Nk̶E}mԹ_|u+~x8)øֆXF՚;y쯽/øZ;1@t=Czx4'zWr gy_⎻fiE7 ]/=_6J1_^r{wssyܓIɅw!/gMy.kclVGM7O-\^\CX`6>- Pm3G!1pc>[>:t +Te  ω% -ZGdD7پJ|-$aɫm=R.aC+X۪6*ogAnIFyIC}(>$6_=>ǵ|_Sײٍ<7s +yv*vj9́QctsG T<]ѫo%q\mZ".j*9~'&q_5;mFڗ{1;kH΃|=bg{Ϟ3!qnlQx dwLw ϯ[w5; +/;q{}p1*1u𳭏mybuh/ Ε^k~bz|@~ +mO\^mcR&mK%'9ְŊ4.Yq7@{B^s',{alcWQO=Z;9e dkm_ݸKCg/ލa1W^h#'btn= Kasr7ao3x#qI}:{,>P0p%A1c1G[U8* Sol>lo-ZXC1Z,1-qMj҉esudchϷFL7nҋSyt{͛/,xYbܟ6=MS<mkut⿿{{b6X24okjmlumcsk3[en\` 4?w}%kOu5Eשju9/՘՛96àqϫ]Ǹ !]7[w^o o6j¾_:"]%~\w]3*x7W7/zsS~Z7G7+_rxb7wCmXMrW;G<XrRnyW7NCp?ǿ1)/n6^g$'x5i1sΪ8vM 1 =9%Ϸ\}K{ήΆ3u9qϺ!<1\( +Sw:v~W!eb (զ_yjQ^5|t9svvqkH-uY-,ؾ 25-oM/gր\ۆt?= ̕G{,w2v^\1s9QwkPzӬFq+֝0_qqp{yf˫߾8!7V{/ljxnk疟yi4jشw%l}ٟQͯ6gޜVe&?<%r\_A7^|"HV3o|l-g: m7bzۭ%^$Opg^w~[O;LOmxuw]ZD Q=,N/.6niæ^ =]ϸiqw*2fnǪþ4/~Ǻ,jv-wwo|vnccߏ^&py}mŷoCkI՝ҲIT(Vny%Yf[+a[C1i?|w;rV<0԰۲/jQɫW"/xy~!g)[}X+QNX}#_Gy}jfCYrn$W.6?U [^x}8r^Vn-/g9mƇk?:fl93ڜW,R:ˍ5T'm3{6F7}Q lx!̭s;7FZ;u_bb׽x3kހ;yyD$yJbpԙu3esc?w 4jQah!&=}YCкeXr睟}';[pyƔjh+a>w5~1~KzHiƲbʼnh9c|.}XQ,_9,W-pۦ]|ȹY?˞yx|/W76nX7?:6<ɕӆYyis-Wou;/ _|@1n-d5Q؟tqg8W,N׎Eڶ㤵fv\@ C_!m}GSlngsX + U߽SC'0vq&>7g5㻱!?N0l3--q}U+R/|PG>U~P}dkj=aZCZ}cmW:Ouދmy/l1>x<\:Wgx2;ܼcO6w^zߺr+{gstGILdr3򟍙ݷ{{bm١Ag>k1v:WC1v.f >m:ۗ`Li.4iOV|/yjk{cQnQߕs$7/wn_M_tq܄f#reo5Mz|WD&I"ܜp-oIocg-U6e볷pP{` qsrɻbGjXew~8)^z8?O~Wj3_K\7懘cJMpaٕwSQ.7]1K!vY\`v> +AT=P_q&1m[^=%_zC/ڼڣf6]\arj*vn2`N\+Rcպg]8Dmr"{4_+\|ӱ\\suRn?zja{r)}c%٘nk[;6{_9Ͼ/ZxZwq ljd57Ĝk7OP+L^<_˟/xy]mmgоn`SbQcfY9i9~o5|RMb b#c__q[uZ8p2‹M?η68ZXr&Ub/ֵ}WFnbbi~xյ/H1.~~n" 9vy*ۘݰ|z՛m;z + VuUM!{,LȸIGڏi\r#n`9=b[ˤUm)QBn7W;h1zD;8HR{mGnh^ڇ9v0f ~XVcWi]?6_.m.-G嵆ylR+ϴy,<ݳsW.r j۫coh3=z6q^|aɽqkWFLEܳkRDo1;6&1ڀa981]6}^k:v'ߕsu}^U59*{|e}Y60{y/łi)7V_j.0umNi˱FUݷ8جeI}wYlx!L;}l;acXgy;[Obx{̤CYmh|5v+'u1!?(n }r䬩|ucʞ'w-u1dA볗9}9ak4'=kc,R}m'&TPc)^jZS w[Ytk ݽaƭ6Kdhܳu5j ްI{5I%f{qt߭'U>ylje;}9p3rl 0klz4}(7/_Nw /,v7:V8K[eƳ=߱"kTKV== 4Dzmwx׫(ϹZz+CAe-K\<9zzŀ1'TM|Ŗb_<8wGrͧKƎy+&g}?y#/HPq/} {; Ƌ7(VZjسS{tTaaɅG8Z]?O7_Va]ƣ|G5z}V=OՍzJ;PL -%r2y/B k)JOVNiW'Ť&}ߎ/p=ϯɕSw!NR3L.Lly ܞ}Z1:y=:aW6_!֖-V]Squ_ni\|q7Ś8r^\٦jSN0n9/y`>^~\՗Lic)ךnn8{2K.₻r{߼?:&=`\q 1@4j?w﫚5L8n1go +s9[0Z 8 zb~(kQn9g݇ھ|8m=ǪUkh~q>w[#xx_оKokcX94g5[Z黊aq\D~vM{X;={{ᛃ_PG׳~yqڊ||ur#o5ٗGQaծn_,6^43jWy[z,j1(rjk"5՛cKkdu=; huϋMCu7O*9/?*ǐkc֚[Q|W>&"'ʹ_CTmǷ؞y5Dְ?^uVm3զ5169]lQXj@r9bn%He_(ɺmY򾳘zRqQ,"|4e_qo>Af\j-9qb.vl]͋Zu-_bj[~͌;_ yn5bugP5v_@oa}=tqƨG{b]H?O'E}Ҝ߸{X]LV+,N'.= +֒[oyquQsrn,kcu\/YKGu,VXh's1&s܆bAm:[q;,sS-Ʋoιe\ZkJu774?RCԗm~,.G8F q2`mcx\\5z6h/2VX\A+}u;ZUkTqQ9=m^֙^塕6A=ٜ&/۱kb^ ]Ƴu@}6ck>&=.'xC,gssk>ںG>XNTZ{l_o`וT{^rkXlJ>1#F\/;sZ^ƼJ/F;u_ƪ}yr{GZR{j_<cꋎuqGrqN^|xg9;60x~jl^4.oc7I5F&z'K{ر [×F+N)'gݣx@z6q:][}\ +Tָy&qb=4o;s;sbtu;g:=+׺|'wmߵQ{: cO4[Gj@C]+&16^or)y;s_޹x@"F|+JLRo]\ qke7}FS;g~|Ӱ7F +/,IxY..w_|wqbqiϒ`>`'3/oӫO^ڕ)ַoeA59(>gqꍗ\gqjixÃ'>)~pE}r}Z ~X\G?{|q=hNtƱko%G%LnyܸO?c_u4_+=Xƈ ]/kVKyb{{O>'髭Um'5|m7. +kӍk57j"|,Μk̪k/妉c=2Ym׽{8K2o3/ \G^N5^qש޿- Xދx:s(|8||pzr\-~Q.fLbo湸mcRx-Z_C|Zj{6Fm]-\^UNgBvrsqz{z(q;~00'Zk1*ymru!w-ko|VxWysAۯ~h^/R鵓V-~H+d~r e[V{3+۩o2NלZlnsǰ[EΧo>5ݧ1߳~z nj {}k~hn<8qR"`MZP{{ +=8j㏶-F5Z50ߵ8Z}` cu&V'rOk]ëEX''Qnbw,k˷Xv>?O ZݓTws=^/B|ϨCm8ŕbq/5;[mѫ|+Ņ5? z8|[;m.1~+5Zmw}{vP8M|)uIqLeO-=dWW/ }>WZ[_^ 7g,wo?w?mAþtEcE4n/rREgբb~w;Ow_ϽwۚʋoOvIk.oC׃6Wְ3bAji3w=W_GR-#r<7ӾnbM6~SXo/CZ#Q k\$G^f~˙ɞ{ '_'&FVWǍtW |^MIP\Fu"΋mjj0_vkŨ(ƌ8qaRFqJ8/|')gӚ͒M\ 5ت5ǚ+4qw=?^F^s, ++ױ@v^_<\0c>D:M옍UK*]LPNaLW@10@ ]K`s}ԡn95_(֋c#ǵ9ޜnƕ9Lu~&\{*F/:`kDa;W-w&_ey1\{vܺw6.Zs4v>_}Q ϗVl qH9wo-ﵞ]$ݯL1ˋX=-'(W\}~Yb͇qW#[ݪyqj"bU5V̖V:/.W|~{MGn4ܖ+JS_lY6//"7ӹ}K^W\Rn0}`^<֜Ġ{_X}y~KJ^V:lkj]Mi57i]WsA=VNvx\ wnM[#8aObOw8<@mw\]3mSc1UabjH7?n[y:ϳA2ii+mgg3r3Gzٖͯ[_P\x͹aGtOb7(Q?|u\lGRP8G\ק6ZQ|=ּk\Nxc8kuRQʱTe^1;1.a|ggϹ!jlͻF"sc朱n>/{sRmumv=},E__8}SCg5aW+/ߜɦ5nqGo?7K4ڹ8y;8=PXΗm5/loqI1Ƴ ǾX܏$[Z[Zuvb5qk'u_d_٣!N@<0pޯ[l4^}Ѷ1u.^kNGE.˗Vwxʧ j_y\?uY>3mUr̹VK?swN9-zg{qܖS.{uQyq j^z15Lcmmo/=|C?gŵΕ޳]C,PT.L9:ד||ՇIn +y +ƍwO9r/kL*V_1@9%ֻ6VQ{sTklolrrr@sy5^<ۘq^N"\>j_jE_.5){uŰ\V4Tx9s=O>[O??w=x<륗Ztm=[)l] mg=*zXr]j_ ڈ)\n\t1v{4F[~#6pYM Ow{dn.'ιXr;\yvG:G{wCބem\׽OQ!NXRPL7_o o 3^8u8kyt^#^k-zHoA~5.Y|[I@-=oYo^~q׍ՌY{3~{+__R36 ZMlO=֥<67obYa5D'M0EͫrqЎuL`% {l/<Ƶکbr_<⮍śY#3S͑S4+D&/d#qo q׵ 3}b5-~u_{q^_oŵo=S;>^g@< {mN98݅+NwT^ܯ^]P6ӵhwwLr#a3E;/*t[ku׽*/Pr6"\=O&7{U Cux}Zl׫%6cׄpLǬ Ю;7^l}F۽6S­k_,us_l>:ΛZ\MYw\Y?ׯ㰗]Į5KգW4vőg^FZ)߸H>gh^#B\bxr--Gѳ\%s]S>jI_]\{jɅ oV!R>w^=BuWrW]n_Poo%c!m{d/l=:˾GZ_/6w_ Nm,1q F1jN{;s]u95z3XCmtqŔY[X~Xu\-o}s/-|{_!6Zc?(Ƅ_E}vF[cOAט\8Q};:Znۿ*_57G/YRN%cMߚ}6..:ZiXN|i&ܡq#~ƒ CwU/X֢č{5nv龓 +/)7g#nrՙXCG0N}|oX lQZkluj1kX{_LXL\ q__XP>*;nY{0)ֹ=㱇v{1*by˾Vj|^ח՚MY^kބ^ώ5n.w]80}~q"vmRcW^q"^|YW)6\Ky>q3؝FXk=tbq[^3k+fXP}QgKp ?n t)e;9#iA^xxt^\>#im,޻9\n8Vz/[` +9/7ߜ5sクxpbyΖkG.^xXhIk(vO-oX\n7XO8a~5Ga&`Sc|B>Y- )~tP+X=ccok9@1:5̛nV0W>5Eڇ8Oh kYkӐt!׵dckǶqycqb˱޺ȫ7AzxCMT?&y߷= ߰@k%r1k/y﷟rb~4Gg?iTzmgu\sԊ]xv cD9 ([ j_om SY}}9]ϪrÑ_LwkjѬMOTS'j-[W̰ys'ŭgs^'m1 +uk5|Z%J?q^yao^U/뗚u*q0ϩ}9?qow>UsХeyՈYO ,&ȶzޮ&ub:=,WGsƚd>׺lMQyԚaOcǶVo7@ߌYL{{np^siq_/^=r= Kt]jQ?+xvջzqԟeٙ]W/wiQ!WОjtmɅyŐss jq\-_.ŮX=˾֊zrʱy}*n?X ZۧUbn/Mŭ`7?. n kߺq:9.͵'nݜ1<+.}Ǩx[ݮ~q?/on/wD^k;gI_w~A׵=[0lt#>DvH-ȅ,c{`yǍ~}߭&xm^rVwk>+7W:Yv=@9Vb|ժNyў/r{\껻/=ڼsmOz*sIo[#ʞh_`m|WmeX-7cεqǻyycAs!{; w‚رc/-o}!wgs,Fm|c_#3g~驼uqV.Mruc싱i#\е b#kWZC'zݣtWmr7G +[,.I|!lpkߐ"_֍gu_\_z-l{Կ߰Ǔ.vCZϦ_*>mO\>-~qaWsf&ºb;f׃4l~[qh9~y[cW8siiީbxE T{0λ׍W)†֩jWl^xveG㠭 sƗ:pX9Xk$̠ukֻ_uz׳W[c`=:aS?!N}N{Z'N5Kz'9/%nuwqJZ/~g:> y,V8k]# )`z#Ey߽zՐlkubnϞ>ؼP=SQ_xa$o}Tȯ[uu'DG^r5_p ڷ}VjW-xƈ\(CҸ[IU3k[bDlKhks^5W˻~l@{`7y圆%{=ش>ujtV_8Jb~ ,=\w9ë]L9V>NIWַ9a{Qwo_ū kwjqʁV TG6w<_} *YvE4N/~ɿ`Ztߍwmҟm_>=kɘs:fk^,*~jbwqW k1ì7sa}SǮ]2Oܼ0rZꯊ7W&),NK|gKl7bO_9)tį[JM! /kFA~rz 7/_ve,\mH|x_<>mxs\mU-]m9Cv$[rϖ;0z 7Zb}ZYȰ[~縁oyQ WZYU.Y_ oݰNk6"=y>fsv.,ޕX-:;_;# ?=`QCuGl3{az܋u}:`nfWLQM|/_ܜA-OH}Ͷw¾8_>y}1ėm8j{?iL`|Nwy R;6=Yzs0n3,|/wڨ81f +WtARFH\!FACӸ{;qx7ϼH֏<iT&wfdĊ"_ӿ˿|>?~_~{Xwg}>ƽvL~ky]my}]__|?O>_??GGӟɟ|}?ϯ[ww?Χm~w׾m;ֽ]u??O}~ӯʯ|~>/o___ؿ?__kpt[]cڨvkW^ͯv~wo8w{>o1{cy??g^?~~87y̞MW{oon\vٽo6v6u^|{^Y4f{wC???;~රݽk߶>ڼd;m3tٱl]sy}{ڳwkڹvz?ܗc3{qϫw2tcon;]{<~wΙ]Zngc?c~G}O{{???綟ٟ{㫟Oԗs3?3~m_f{X@عzׯ|{`}g|@wل>yO|/;]]]Opc_=Ss=lA7O4vq}3^Imw/,>mýf&G~}}[kM{>٧;~vWOͮwk\im1ۗ>g8sݽXo;mnjlksGmw׾ww Ν]Sv9<޼X՟{F=bϹs`}5'/l|@|5C/>]k{kkΎܽշyLcGm^wy檟?ß~~v7/v}nseǸQ9ϳ߳ww-7O}s~w}חA-?i޺X~Sc~ng7'z.˾i3چdO99ke׳}x^c=1Kunj,ygKǮӞOw}8#?䞣~]~lΕ9gG<;~1co?Lu] =|W~轿nzŸƺ1gN?o>>_[M-Hؼ.gڡ~]G8S{?g$-F.qXX൳xu[}~s}8~o|>=ۮj9m?[g'yr;q축y{V}&X3Ԧ^7MEW#v}>[rml+шXQxd]XlsO9™7 +4^1R,wQPc6FOgu}v:N1BMy|{.?-x6|Gcr{bqrOsmdܐXx9lKsJϱEϯv.6yG{{>r?{e~R^$Ngo|aw]vdxc/ ̶5O 41jcY];Xghn/]1'-^5u'sW4>oRWZI|W~1_9y,?y- 09ld1<o7~am9;mmgsύjwk]7w͍axvs}w!~aǸӚ;{&,w=۷;];p/}K>޽_{]ۜ jjn +̎f;yba@c/Xw@?[/i_lI[m{hw̰#NkNcE|5/9.'ٽsjWc|Ÿ{b/=Gq>[*)+? Cl_} ?3)f3j+('wWc9\|_161os7!_4w[1w\tۻ.97}qqkcܼ[{ /lQoon|;t_}ʾkxYsrrL3j67e,ob69:/'7WfMYo+ s3q7gkc;~cL̯y.k\>1X_sn~6_ef`~T}@~B~{qq{nŒ^n)[3>-^-Vب^߅!/Ãڌܱo<,hc9~ƽrS_''mک娈i3|>?ykp|9\a}X+wQ~ \A?Y.(vqq ~xdy~~[ڮ սh k5.|}N,(LiPYQ97m[/ʼnru/ǝouu?(k0G]+arofLU.W?Լ#>^yohAί(%-~!!a kl<س(>4_t9r̍Ճ4w{I݌8MX>_Z櫎k|(>xmI&x%Secrhrn> +../y^{RT;|~Mmi/MXDVq^ m_Woﳞ~}+Ns~wgG[,x+:G,纼A=IzY m|lXI}skܷ:ٯΧ?XlK]\r1+l&on;ZWa-M;ǵIh4>x%aq3ţ5 +O[yEFN{N==[ۼ'75>,6#&V!]:9j͡5uN핟˞fh9kk{ҋuծBBrCAt>؏X봨aw]kbb61(5|+wZK{mfw9Bk)(-sl|miw|,ϷN[f7mtn[>O`>;i+ѽ7nSݗ;/~7T?Y@J?1<~r.SP/mO-g/&o`YB|:#_݁X7fyjkͅn͂i8_?ڗwZ?Wp}|-sy#h_Q mlz~zs\ͯ67+w[϶̧]̧zM';Wq>WwN\5ۖ$u9Njr񲅋\,,Wt= G~n 7d=@%AA.O"*u9 Ȝ4MI[mx바0NMGW@&_En/lqBh-٧B?sj<;[Y2Z.Z{vc5V'XΗ S'—|c/ꫯ>+vB`u5 [yYgb1UTVomh1ױg.OobuXn%{}ꞅP)cّXx绅]!],qY0aqO{6/ۡ ^pjO#W\p5.+#?~+_ /v,,ϺnoPً.j>+ڧ86ƢP[3nmw9²ǖ3R?٘ZFVv܈jÖ~;. ןouպ,έWĚ/UX6uu]ŀXAZ YG}hq\ ~#z֩\>6z+Qy&|VH@qgab[.榮W\/gq؋ý||]-ꈗ:ܷ{o5w.v]v1Wx57\_Yƽv8#a/{7\tZmT߰f9%/ݍՂx \ bϱ]9ZVi_ˏ6F_cO p}C8 +mbwvm)8O|1Ž~r'Π1GXulmD; rϻ}6O5/^Źyj5-'+sԾr [[b׳z/@q?5sIw:/.U"}JQIb +-EhBغzqǹ60O/6dy347 S\ ]ޛXo<^{#~q6$3gպ͝1.uVr@F76zőb;yZ}qkљs=1?q@??[̯߫Uy:\H{Vܝf@w|ǃ>spuzw_Мo?uAl-Vrwqo.wxZo})k^\W}_臯#߰x9}ȯ@≍|fX";Vg_}MMu;W=@qNwu^d:ߵZi{l\,5VQ{wŏhKo!b|,]:‡sw7nԿw=lMۚա|kK}d^Zkϻvezp嫸~pc{k =]F`ؠEy{c0kQ{tW\{=U_U`{Yl׾bvZ^kWߑX.]uv \_3'0?j_ 0p55ny\Vc$:mM^^M=\9p_?Ӷ_%ΣL蟿4r[k׭2⏭Op){saժQ,^8m>oya6^m_.Ogv$`a5a(iƯrpCX!YOBi/v8޾# \k"!|uj}'oa.sqgԷQ>4ZWIX\\ms֫hçfc|זz՘~b~+6}./κAc :`u>_M,lyP{[v}wZ0œŲ<W4NY|kz獛<{Ǔ|1ُ-ytj!]ZbrlQ_>Y<:ͷ(q0lb-޼q9ǚ8jn>mo>7=k7-b~{x-qwy}e}lt8at΅m 7oqme}p ރ\[kҚ nm&r9^ۄ_6׭e[km`*k'gbo8xb ?yDmx#ٌwr'-[ޟ|/8-.H7oks.~յ6rm]Za׿'Wܯz0^_:VW_~߱Dq0@5'txU^7=]}uy{;jol=9}\W}Ji^c;!iw}·Y}ǥ\W}vmA5D_}"oݕOVuĜ;Xܐ#*_zQY;sb[+}{ߕ۵|\-(1PggUb~xjv7GN/H,6_&%9px onS鶵;ǎcFwɹgrd=F;Ը/>^8Ǖ"Ǔږr{Oa=Uڥu6w5Oq6O[u ѷfQv- :Lۺu.Xj ~jy/OmVm۶.b/X빪 Qa~XfcͭrtqnQo<ܽIlnu~lXa1֔ ߻akVYKL__Ϋyun1lwlTסfWrEAшjܹݹoܖGwn}: b2r|pMj׵A:7X%\E-jwwݖ->M1|w]'0@kfNJm\a_q@ ͨTl]p(kԷm~iu팳_AOL_cmZ}<)v^،el?õsjKZlK1yYgbޥ嫻W#>gN,^<+/֧!6k5pwp|\b)ܾjӘꋉ)w=yp.6{o^!6ukן[ms6%(n_[ ?M綜魿\n5!ݺb%bDBuŸap͡ါgM?c1^R$~ڭk,K?NU|tyzjok;qv\mZԭ#a!=c}k5ПXݍxCƽˉܱa*0&EuYsY6_ay.b/~<=s>:OfS}`yj]#16}ͷj­3Vl}rc+b[|\pCs½Kܫu7Oݯ9֠W뜻Dok8<ŋ`ʝVXZsroivm{Ԯk1;Wߕ|sWՐ{׾Z.y-R]؜C}~\b3.XN\n"H龳^p 1ctfYn.?4r=]n_鍉mkciqw;u5VfyD,컵mʹZ~ӫܖFJDa91[cGUkz#b[o%Rc:9{kyͿ i|em]r7&D~kݬ}|鸗mԾ7Voyx˵ƘoĨ ~n.mν:D1pxmŜ֛*NRCrߓF@M7zBrAU#ʋ-_yyTOd\Ri5'EE?(_m ̆.=y}o%'%u1}SZ,PoJk :| 'K7ӊn="_mtV/ii[]}`s6̞qΖzg.._*]]V Uz;כ_^uXsc[- Qpq:17}gGt k-^z1ׇ^5'/opqmmߋ 4F4Nr\k1Ar/կU M}Լ}~wn0{/p5t/\Bl9[;iwiyEuƣc~eZq9/M 'A0\jw1m}~^5]KXΑM>ƍ4Nb\⾎aqycE&,˱[O>ݩ6ok8EWܴf:LP; ޱ]ۣziF}7|Bk5|ϲg0>^>+jƊOWWq{81@Gs}t(_9lko[Sq-.wqEb^d\NNv^8Lc(L5/ZGis#h>L-Bs_"w)uc}q>uz՗i^ךigp[ż+[;˿ll@/GkbR~؞|[pszu;\7|\0h_sܚkO-{rq]s>ZŒ.0/oA\AT.]`/H,O =q[A*<lL_wԣ&H~o11d$WGu@ɥAz_z$/~>bd`_n>p+s 9F}nm0xi3V^6w?s}[WξĶ:K4v[㯘Cζخ77,Zq{Umޡyҹxke.6Q_:sqmpq֔ѯZ S[%Gn>Zn51욝n[sczkmMnחfu{i~}P:ĻMXHXfݺuriիiMWNW-:}0ֳ6J_K_h㟎yxyx3X9̖!+ZWzbmc]mLFjiXeZ8~]Ϳa6x,>ؚ¡>,RT?d$>PVߖU_5/㼪y|"r:QoS-ִ_ֱ,g;_Vkr6W#íxL?ߧ?s{&㼴Y_ja}TyyK}&y1͊kcjVVG=v=?ִpעVP՝'0Mgw-o\yXivɼO?t_wOsmm_ØZm<|}߻Fnx|-b=?qGy1r/3"u, P.`g1N>{u&=<@Su8wwGky#6ƼsY|o'u%q+7];_s/[ZLu​K{/_$ \B5ONJyk[l>Ge_xغ [W5[u\ϱl}0|lyy};k&:l.U5˲<@|>Mc){Vyccjȱo_wA^ܿ|տxqnwW~Y(n'0 o1Mk,Uz {-#b(W#X߮/a_q1<հ3(jv5U'S;,5wML~aqՉ>r$aK\Ov?&0|D{hus<7mpb(?@oyeݭc?Kw7oR:`V.>X8׷^}b/۱qFcC gZs 4ign W`M>r_6u/}Pwۭzi/h`W!|TJ꺳pQc[x3θ[M95;4.w_ϭ)Oq~P=|b7b;7-ov>w85/]Ku@]g_'$:䋆Y奡V։ax=+֊_47 +2d /uxarrt;:mٗҽ46&Yk|\9F.\fk5Q7.9.LU =ۡք^-u}Ƈ +] Z#O Oνx] =[Τo#iB_mٽGܺ9M}ߍϻkI97>aQ̇zv7GV_hMC8w8W/3 ˺s6.)֩qn>cYߠ_;$J/'㼓Gpa>?ƣvjMZY8UsdGݗq=gzʼnwDž(}v|oo]]t;o=ZqwxoĤc_m T(kߚ#ڟ忊ϩ][yX5 X uKZəZŎ+r _:{un) +OZiCk]kp_3\|ݗ_e|swĜWgnѸzYaDbwo\شǜ~9r{v^݉X:r_5'"q#7d]o}y]5ՀL7f_c5vQ/X\FW>V;BZU.lZu}]cc.sř_{/O۱om!vY`gVu[V1׮sk+˭K>k=y|t{xskX*ZO6{׽.qkԇ䖩ѷ?&'9[,Iµ\ 6;]c6ں#׼U;[ce+W}Fa!ڜeeQ_+ϟj|㬍C|yo]˩ymwFrKhC +sr>imtr +\Q~K(gk4'~"b*f+;e_W6V1XwE.lckƴ>eL>2qs:7u7 aQ}~t뾗ÍQ^:kvٺTsY;׳z;ucc_{g߷lu"uCSm6/Jjus=3u:bJCܴ_5ɭ`/GܣVYw/pڞbū++ԏK׫~5)[#5k_:\cIu[;]ugnq;mo|>aGo;?{R8C_vᮩ@gf}fwﻸ=x[\yƣ|W}y68^-üp`v3dxakw:V1O;{իʮզhz_6oUCZ~/vfZr)]P:r!bc]?U9\!봄{{bIw9!!l[cG[_F h3ٜ~{v{R7ĺ kkir5~[wsSw_-v|a<Ӕۓ^6W[q}ƪʃ69&%yf}j#;g- V0}: ZpROZX$s!o7CXnrI֟Ol-3v]N[#(?<n\kVny΅檬/w#W:Wo~xmrPwk,dfřҕx\.K>Vox]帿88iN O+~PyϟZ3f\OAOmn/D5V3j3w]eJ rX].g[0>>F#cs9R^6~9`w]YnZCqͩYG>wώ˚ 7+o_.7'GD0?Xhc r/]s4$9_3:,Χ{a/LQ{{9bƪ>ׇk}Xu3۴'9 twM8Jﳴ;|~ΟΏ߅ TZu<@_q}k/ZU-o|9=b8t[މ_]<焏p;r wӀ/Nqyx?q?sU6(ypz,y\Sjч8/fHzA/}Zރ< +kp]+1΄ysrx=ZwLQ|IN5KZ 5?g=@í\Z=sAyq>ҙ|'Q Pk>c_;[NbO>[18vӖu^ƕ@1qdX=˸MG077@quR=}WcO]_y0'Zz~9<' 0C~ך \3~XsW$vq=gj' Gmrh>Hs +`[H,KLϷJ<k4.1ֽϭ?_TC)7_<y ORmun˵[i;}_}XQl .vz}H3ݭ?a>eoT}#,OUbݓzoU1rm]J|;yO9VO}6Zqcl.{g YrJ|^q dIx_8']r1wy5n;oŃ?+AW)-.?YѸ4Ƽi aw\46mԖ.OOL&QoU=ovkHQ߼Cv̋9gsǼwb#=k떅V&5^c<.]K{6`󽸳vj>^>X||Y;2]pua)C}~Kſ_}/ff>ΩkN])h5Vh련οya8wprါE|sc%c챹| bj~76؉rvjE&gwضVZwbk &кƹb۱}u[n1{2G`C,m(-[ b~ak_2V?m;+hlzOs}^WO[ml?/$ULu_[ڴ|\} |o,M {i54Цs0FS8ql)dю罞v8^`TV0@)Z]TZívfk_'b,PX7o#o/Co |qЌlb݊]jkm>y}b"rJ{k[֘k= F5J%1Oz&XSpobgĢ+ٲ09bɦ?tqvl71X-qafE}Ș۴׺zڢһ.y:rk˛gOa昺J}>~T%rC7*'-9LN~Uu6qwVk+G0[([^E,>o.r돔{iq?o D \zS\2ڍ}6aѼ[P_`c2W˟R_w+'ƶ&km8Ю7[ɽ_^`lb{kXߺo <5x7j-G9o{i\5f?簜[^U}>c.?Ɲc676ϯ_]cZ˹]8O-/_LZEj+O_ޞu 0}/.ϒpǿyvq ϱ3glƹ}\mm?OiU[/*?[q3gG_~8t8zc3Gee9/K\Z_O^_7ծx=79ڤJ0|:k6nv_C[W[|i +Ͽߘ5r~ՠ|b[|ϯ=_5ߪZw8f]o>dXsRسqfR­)nNV@No1J1qm*SuyfabnV^ C<6 n+SCsrNb~6^ |yOk+9#eb\q;kzqr}}>ܷ"w\$٘F>I뛞'W,wQ%^h74ulX.k]mi_,>X<zios[j%yj˴F}XlY /]sԚ7QAsj /H'rUŕUyG4bbVO_/>3ek Vŵsmt\xa}Aa nxq$日A/'ecՔ-6r-oT3OmpZsn~&gWK\Q199ȭjuBo_GqvS_NUVVOg!F_R)J;bBS+GClU{^Gض<*rXw_f|嶮k51еj,{b׏;>IK~=y,4\E<Cȴ/n~YF:Y^9S0woc[[bbӚP[5Hbwj }vgp/ٮϪ=Of6QX z}WsA}y=j5>ðߌ2ųjW/ aSj7(W1 T^np!3.x5̹ZP٨ǭGw翴>jWkZviP~͝yX%l6ʧ Kz;~S1Fͺ/:b]xDKbagsj[y.'Pͽ[^y-gښ[6Y C7wmm%I]. cx} +_r{P+&c@}_>wGKk]}Y>^x> (WUlyS4~6[>0/mGe'ן؜|09qw%? 룋K6zfȭޜauifs$yNء㸙y_oٍW=\W:b/.ro6Q>^Ϗ9;;>ェN;|[5ϏJ+'skC|8_Nv~oјS_rٿϛ ~/-DluZ],`.Ƈ]ηcWNPG^bkci޴x`Oƹj7}Gns2rӶ Xn45ū3.\՛wN7ǰQ.[۾76OofXߨclX@GJd,?qŕ@5v?(u-z]v|>x6'uZ!?P8ѧy克U{}12ٗǸskx4'V/̽W~feE[zG Zޟcq홱G}1z~^3 ^Jsͫ& 3-f!J~!|Mފ/:q}_M{F֋w߳:~{~hbw]Z>;]C}1{9b4jy Ӈo׿-ne˼ ]b uo&ݓ2˫t-Uc]OC=0y;8!&སpLq@W}No6*+[_h}x~1ǵLǭR?n6~l1';n_o[}Zb[ﯸպGYz3KjSV[u/qݬ.9LISvx<&/[?k' gō{N1@q?-[l>"u +o}՜%-iژvZ;ł#xbk0yuw$jms!s!>][׾6'9ijo{/oOv|D59lԶtNyp8^ڭj. xϯ ;>^ڏ|VuhwX/yňyg&'W,Ě'X:mj.VO0ssh\WuV7?Zz7yghMxލs:l}s+`l쵘K]}sZ噙_7ӯ +}K3{QWU^}TLZ qV?^5syn~iYu^m؛nl|i_;4.(\ZzO-fbp,yi{zvw~ٹWgq1+޷㺶=o|y)߫v?C)s_/,re;[Z1m[:q?5/.՜6\ƒq8S߹L6.Qw}Nq_M}~E]j`kc珔kNSr]>m7w~g917Vf>|ǰ|;W?ZO-~KuZ>gTwL+xǻ&2_koSV>zmi=<=חCض^~<8}&֠iǖʵg]Z-;zb~b}J}/b@Z׬s\鿚\OP ^9Jujҡŗq}6-k yz4Mj>[J?:`ߪSlT6Va""+G~͹R_FՋ'峊ﲝ]|qdjBjj՘ٺ[rJέ/[~ڋˢM9/wx-ZvZ7}s ϛ v}.}.vwؚ=ϞiϿq-K]_U# j] xyggM6yƉ嗘_y/険~5ȿ0T\s~]!R~In:Sg״j>)f]ٸ.ے-.m@Ym9oXyeѷf~H5H9uߵ7[Wߗn0nk8yig-Wk9#op⭋iln[ޖ9l[ -Qk]kqw [BXBϷyXS]|1Վ:5[l|΋$vml-/ga{ۿbջSǵF`c@XZ9NLo?w*y:jɌ՘4΋k?/&9ַt>~#Ϫ,16wM=;msmcZ%~q~f'Wsp,j,7CKw{>^Z3Cdn5ww;壿k>NΧLSbU94,Swq;icj}3ssu^_務_wuufa_7(?͇.}Tk7Ml`d럭b?[oSgc`Q-CBz5}B얳yW6w.5[۫&Rj+-\x@~z(\N^!Թkc0۾/Γy/[#^a>b:VlN+LG֬]gʇ.NL}_/T] ϶e>,6߭!iZ_} os9}qm#n(޶j. ݚ|I{WO/0LF^5Ξmm#,yZ[O| !K&Q4_ TlCJk9=/jwΧ|bfb[+|^|d)xƷn{_ߐ_չX~y{q{.{m/g}nΥ_՟O] z[V,v>}Y Jfy/[,^j33H'1w.'!>+o1fݺ/Fx@[~Vw53|qkW\c]WmB=k48XHqߋx/r7j 'h6r~̙C7nbqwo ںr[#o?5w5\cQs_<ⵈ!tb遷>Cm={K_>|ݝW_y[bf[M PLpߵˋ ZOT9|ْ/5$s_cPر˓Ѿ=N[N5K+3z]Dކ挹ϸ;6xj7=\Oz}Y\xrL1I0y -oon#>s>j)|T['+ڽ&~ژsǝ#.v8isiGw19-oؚUr]6&LȚ׋:g~$U+ 6V_]՟ [c~XAމs+qo9w~9^/w_ysVc>֭ oW^T>' lPTzqzݟzaqAq w9vjk[8[mmaL¡{&œRXD<8-zf٩jǨA6˜G\.ގ>, {{M.p\mW d5ƏNSFp'nNu4mũ'&WGq}2ڿuf%&?"\Xؐ\b\.L7>bדԖnUg}ykkkǻWsyxچ[!#7Vۻ\?s6[<;}G_@[Nsbk'SP{^sor7Nwnli߭R_9y_#m%V~+}_:-xz2Fv9~مp(㏰ǭx˝f*Ŗ6{OrGN\y G7^sq5j_:_4w.*vt7^)[s~o\&OܡyWl)aٶdC#aka}~W<ˎńO yuA4&HV㹵Cm prX0ډקč 3l sjKv|y^uύ͓j>aw.i?j~=>q5gڧv98wۅE)ʗ.]3ya]tiuXmj}6>u 6ײ+PI_,m|sX,Sil&_{,vQ:X~SxMc8huq,XGnR^\뭆L `RLG717[zhM{y32. x _n|8piptcJ,1&EGrtY?ll[5~_|{g[[ b!boZ7sߟsq\vusXjsk,\xq,bSj]1kvL?1=/q̋/l?iz|xN-Va:x.%N59֟cw/GV'?q_Na9g [;rזH:خmv^_Ma@(k2Xxyc觥 :ge;=u6W޷XȽ:ż߲[@Oܪ&'RYы{޵7w|өTK,i1厯~yj^l6ۇkwX}^{}ehOڝcؾmc)e]F=e0qCҘ#n @hrV+vmΆX?9"Nq|yūtߛvmnV oͿPXP^1b5 Gό)e^>sء; fKs~էo_oZN?=_}iw\6C!q6o?_>_j}1=l bYY3Q߯{햗/ߚeA +r4u.ϳx+\#s,p9,Z nk6l\1|V_jAT&{Ů6KO\D=R׏(sS]ݧ=̷75YLxkj#w3RouFf9:bO~ʇY^,V|מa|r_{ozr݌KM,wlS93 #+>۵c/ַ썙0blnw|ȟەnׯۑ>Z_}G{:)ZY-ۜ{w3~xlN4_n>sW˼٘Kr~ٵbW9ƶ6GD8Lq}1g\Mv>WZWJ-pC_LEp ˁ*"~ps#wy}MM|Z0,qֱWm?넄lN\N\Nm&&sTZj%exzWZaw[m9(״@}s>xe6}+j +*/kzw~Ig^HP>#W֒϶K}ڣE90aj}6kA]\N<^_~ż̵gqré7}yO~\\|r_0|TNG}>jW7Z6s7ۺ! hh|n *i<"ޜۭ"a#P[>̷1kn1ۜZwv \>={VȖ<7/_B}r}ƬA_?96LUG^mE'r#~fǶiwq{f^qjjϥ.o֙Pr8_{=_ݢ-'\C,zg棖O9ArH]y*|"k^m[|޵dCFeOgy"Sf4.K3(h^`}}2 _<qEڱ>뚾!xs!\c7k1? n|n^xYʯ7_a>_ Y^!jciųGӘЯW/s};O΢B;FΟкj0V[!.ck{Պ6Wm(ǔxî zŒm,\l`mj6U?nyS[Jsw6&?Om:WT:9Y_@괷<7mb )֐)ΰqv6r\M\߫nk&Cs~_ִ}`[C \٦!mY}[.Nܫ;m1Zv>kRt[Z?NluzaO|C/W{ȍ^ukw˼ _\y+^6$_q5}W,.X=kuB\~<@5V[PcBͯ٘-^vke>B,ZOwpbKrDio1:-qmiV~>99= čT-vr mɇOw1wk;ᄚ^H3)c8Wds_r4]u}iXߎRj/W-'V Ϻ狙w}[-~ٳlZ഼U{n;!ooL*b}\r +G۠PV\p\$L/LCSmsrZ|o8vO\ת5-䗫Mmf#w.9/.IW3ze?g}4M1+wE\1 \+aǖ8Í7+~T9~~ZGW}] /o.\&xmzu[۰+Vmg^-n&'O9OW6fi_ax{+^.:[GE |;Oȡm습z&ޱaȱT9q_)wY_[jtZڒM^X|wZk͗K]-\W&yu>šf^4WkL-nZۼ1.GX}u|r[W=R9kq\9'C1>V[Wamc$ }sXM[⎭NڷO⋍Y/{ϩ6gc>mܕk;{zq]w9]G}[6^L!om[k9M}gPށ<|kG>x-%߮m=mm~rZ!^`K_j>ꃳCWW|eUXcI[rƣ귎Xz~JaN8rK䘬>X?.,_8Qp9${.kukhx<1Sc[ݞs+천rw;+l~#k:]5=k,f6ض 5~nr^մwĮĹo1.o-WHQǨf`vu//>a~>(cu%FύQVlgW9:gazrۚN9.s h$Ӌ׿5˃cr|/ڴGUP`gWXh+!름C.^ߎڣ{'k7ܴaXt}]C}G k<8<,kMY3Plb9Kw]ۋ_fɳ^\Lֱ-kZS'8)?X.0 sj}ދӫvrn}{{{I^S+8j_hzW'[lzmr*^_ܓǍOW؂8bꀗ(gmug)֜^8}<kIw9aү6;g+ݜ&Upq4cj,Ğc=r^uqf/IHy~`o\8G0 {͡yO%ݬ=ܬCt!OUG_O<_6bsG<%([/6bc5-v?ɱ,>i*ccIcVcNv.N *ʼnNמX}u=1PŮ>];Ezvyf?ظ69u Ӌۗ ra~{0XԘtu']){a5۩ha  +/nͧ{g4 Mj06O~*_{c!3{'-'g{@][n+w,A}OXNSrOe׺ϞƇq>Ɣ-x~9ǬO|kvy/\\߸g\˅P9{O+VW |rz.ً]j_\||>y);R0Ο%Z ap_Qwc~g9'K--Vs;_?8xm3oOO6fu_uKcMnjn|XE=b=qͬ}3/Oab^N^zmmvM]cDMd7^uv97~>z?ElWӹՇg>߹6P؛6^E>Kg&0;1o빅m ܬ9M|ks zj͡/ k=zZ(Yrl'cXN`8٫jզëϯK4ufLɯU_=-Z? [SOPV[@,[Uo?gcLO,|/ދ1V⼅՗Kߛ|ێǧ>8[ƯaǸ;߽ʵ oOf\ ~;f]wƂl>}vTʱ[7ǗNT\Q\#ƘJZf9fKii|qL{+y9yʙ&w^W[q8ҋϋ Op{Nq.G{>ܦjzu[}us7Ww=_#Vs6AL[6Dli9;O]Ef<41719Ʋ_cO\nkvj-sqa(g.w9}ֹ ~e\qŸVX4=rkقkw2Gx33-vsm:qw<7UWn,oR>HNS#uėw;ǝx5N~\X_ˇw{݇^P,Z.=CqL8~clmgq@Z=\\u){ ˵(1?^7١fak_ 6XF6$L^5ҳ[b| S7,^~'>v6Ð9leR]6fk(mndW}cνs,G2?UŃZlk-Z{v vܻN99;Vȉɭ+Zl{ __~dY\쾓wc-]G,P O51w^/ku/8X1T6 ۋwK}'-kY !a 6z`'-n\[170( da>!0Sӹ\]r}Ŋ_8؇Kڹٖ屜mFRLY<31u;^S/^5?]oƘ< ̯h>%-Ca/%c>RcC|q~>}>_9U^cIg{r4fZc^?mM0jJ^ߣ8sukc[lwS-7C6QZ<6U{^w0;_}f̾^ъl}A\ٗ7l[ݰ_/oS7vte:zkhWw^ l|ٔþUҳf]ScCW2%G^9d6庽bĞ}Q쵹=B 7'CKlQg/x H_qg t1Z_fk +nW׭Z;D3L9ỉ5.kX_?2$&(B>9 yiՒ͖rwB{)&|qrÃ^z|nnƻ nFq9Z׬Pxڍ??`5ƿ;.յ{ƭx«iͭm]֞k݊ŭ;^,_>z6g.M]<۩OuۆƬzt~qoګ{y:0J/by| +q ܫ97Uu \C>N7,k1@yb?˝t< hnNuNk6ga̧\U\Jw7?W[&yQ\9Rny"?g< F[1wo+nLeϊݍ=wrbAYǾ<}(_suS8-Viuj:!:q6WuJCo[wuѫM4\m<'}bq|ortjjsͻr^~c˼~e}6[Scy'Zl=<̽&Pc~]r뇩s[ikϩGm\xW9" s=q^gqqs~j6K>P _vZLn/lo1nm8]^r˒,5ݾ\C.2l\3[حZ߻hrr;w]~}76+f"y{59=ot*|'E~B~ǝGLMo>՘c9-뼘fw.Qm?ko#[쭹u>8dvy}n}Oas08s%)zmT:P"W{0/ +Uٶ[np9Γ'//FgIĚ1ӑhH]]},zt +f7|rg{loe_zw=l ?}O [\,lv2שQ<ưg}_]DR\A(΢\k1m3esa-]O~ZXOxM96q0ڭ{mӵ/g'5qNXQ$;̅iʻ/ڵܻOJXޢcSt6n=sձr6N=O'ӝf_<εĂ͞:'sp99,:gڌo9>,q:9HW0}m3pCqSUXF6nT}8\+1lߋ,t9A}cv9Zٗ9Ky?xU.NVƺ7O[qgߧXYO0N^.wWgl˞U(?k郔/\ΰ؞x]ZZ6RqZܱaZKww_~7u:y&Fmwr/u/2qG^|@ w%{+]tz"X 0sƗJ;͑yũ lԮ_;Lޞڱ:W|aZ?7F {M5V$B]x⑯X-1^z霞۹Doar1m_Sܻijk1Nj5zA9޺:(Uk_+̓ M٧}^ &?PO ^}1qr*;N.oL:brnT.OxXm~o3VQuq@e&nN3ﭷa We)[|~v1 +pվ^ɒ39 7n_1ʟup-+. oUǀبsm$C}.>%uSicm_zy<%%4^7bSs>1A7_DŽeܵ,y?>ꔻyRorV5^՝krzyiSv t|>[W+OZ;ȇ/1r\ v W.x?YmAr^lc^ڡbr74]/iÝ'?uc¤z?bnbkO)>qݽnLfTn4q2~z]Gnƻ4/h}$jVqStg]ʝon:$ͭy[߶wmW. ţT$}>>[9]35}?  /¸y [ˊU_H6_o~}R>ݳԯ[\:|;zӻ>IFq)Y@yx*kn=_~̿M[~a_|qb#yVbm8u.WPAUV=/{3=9,,߾ 0Lrb.OM;70k*xm,Pp9!uAf\^S]{=XZb9|kz3/I.kb)>csbUk g5َdok3b7/_qж1Z<+i#Xe,򽫸q:b#m͉;?Ce+'֬], rSWiͣݚ󻱊Z57,Z:FgПg{紼'xWϷ8k> ;yx6vEx';Ri ,\v- ˮM޵S,7ʹ&y\]엗¾#.ڻx́cH T7x1)Vo_884䍪j#94_z=}:{/η19F,u[;|@'X< >9cֻoWX?'Nx`('ܿ>9;JďĤ4us2c8\+/m" 5>q܄;tֶ;5#U1MM6G3?>mώ1w5u޵qQv6vϪYIN<.ou{ݵZ޹ǺVn,[k3ǃa c7Slܢ}>N~/~w܉V7EnP^/1qas;D}]s /ۘY8cu{q)c;/|&^{cu5&9.[OO 810/dm~+SF/_UAXz9ӡ-UXU9:!kTyi}kt)y\-8r:.N7=syVd]mﷹ`|rZh\hϹUW 2^m8r%}AQ'PJ}iqyՈc|I 7V3ŘթGYsƱ3~+ժߚayqX_kjQB\wJmM ɱYΎؖq˃^~''N؁1Vv6~7a1K6Nz?Vۼ@ǯrvv^$/'/o[lEcn-b~ +W/YgkT3s}0b}X i_yk٦;~=;v<;-vm)n{kͫʭXzq]\VE9x`^9.864>y;'41#Ӑu  )'A ;)6S?UE;-6|dW ^ndq6rFp/kkZWuseo.?1w1ݭ_".i~볛Ʊe;~}s!3[kJjWNgK_<}^wbr-,w{\% ޽e7+?kË ?_g~mmly˻Nir1]5ڣ>r;+.q͓&n'os 4+xcxM7wu]Oj֧^x81oޯ$9&z?ۭ -Xr,.y΍gD>s._lxn*  Z.jUS{|G,tk_ߍ3G4[+5m6.&:}c\Kd+s/(hUzxLq:rsSk/Sl|br/vw7?|k̭>.6l^8td6,JlIdrp|-z;(#mEwM2reAXs+:xKR^87^Y`ז{c<g]Ǜ̉u̽XYyOecut1Abs~@qf5ʘi10mdƚm퐭'6\|kSY'b:kX;^'N ,oKc勱}v83?_ XV}[ ݺ y~^1ies3/ږϽW% X;lܪw_̓Μ|L]_:j1[oؿ`7Sh8曯v?C+rъͯή N[6myk?Q >͋u7^x<˱3?_5ӷoq +8W}1?k9U~qxoFת3ѯ]kb>^Q*S\meHrN_q[+'~!z!}6=mӔV}|pǟl8=g]h >`¤{YS_Wc#nR_#rY;^%6guM6yZ;0֒cI1<&)}Ͻ{녥l,nm{RPˇ9})g?*59cEZ>00md;MjOKG0dix`ջ{d]_ts9$"&1ӎpx64u\0[?Dާܵ>b[ˣ0>P?8k=XN3_ax먾ݾvR9!~^?}F냐k,*Θ<:X⪉E-G εUce/rݶ&5'C֡ftOe [[[57lo -/ug[uYnj}29&_ɷx $=?0b{Qӗ?Ǘ|:HM3l-~Mwzq6xS P̫|7^9kom[䌅+^ǿN\b{w} (:'|'k/b^Sɽ5mŕs;sl_bq <h;i WVvڕk\]}=Ɲd#Lϸh:ylkc}%oq>1 9O7p['Nɿw#9@^tNh6A_z$>gy}>8Ab0 Em;ʃA4s]z|nY9 6x0&x9{>?|zu 7N]7W.nb){69[ow7ےx\%:3n[/{~}']/M\:w\wr {ţ1]O82Kk-yRˏ[v~9/YqrHCaBx(l׍垽5+X>]l}m]߶}+_^؟}}(٧lP&C22G_*Ō*56Ff5napYϰa[Υ|TO~9/LdѰ}-v/Ok|q93|s|ֿ_N^\ۗ 8-̡-O^ Fdgqa׎w_yrklayb6I؎KŞ^Xy\< < [,G]2k.qyNzzaU`9-ӫ|ܮ۪)OvyƔǽ,5^x9[XxCL>Lt8q,gF#LxދO8rkk3&ۜr5׫mwcLn%#Y>y&4|KS16ӛZ_[ áXRkuqAza2ge+6[C^3#ggˑs+KPU_"fˈ:[ŅKe)GBǯF\|}x|8ʒ妬R|Vs?]n[#$}ymL𧼀y9ֳUaټ^y];*o}sc3%&~}8C˳+1uߝbGvܺֈ-xߎi^CN8ƒn<6s/^WQ_y,oƖk^E(߅K~k3wswܮb1ws0n ~_#dorG5@ӳu|q- C'j.ysbдµ|6&xǜmapC|kljMFp0N>[V_vJm6u$Kgib +l{k{kQ6q%keSLw9zƳdU?W}ebuP[0{[M +z/v׭?zZo]痳X}/ufdmmyq36~N3hNUa<9]'^s\p>5@zkeq2 yxUW8P>ng6 ^ؔ6ks9> Uxy/p-:64غW6Z_KO>m\x&+nԧkg7N^<@2IaJy74VF2k|8+l;D֖.+d99@h˴zbbrI;6\}X3oKvc uZG[_iiɹ~{gW{XHkgڂړTnx)[^2\Rw~[xB=y<&G/vs]\R{Pk~+9p}8{CcơǍ᯽OwuǻszW+ ˻6SY0l{lj%ջ8敾dʋP;RWf`r!l.?c_b|a~P~|G:O rM0l͍漾X[~]_>'Ƽͯ:WnP1n8|.]x^%a.3qߵ/o!K,Qq7I,q2ɺ|ڜN7qq]ly]M1*e Z[.b7}q4sl`qaՏիz-og\ܖ׎־n}1vS/ȶ0A9r}c6ֺge+.om,h8,u|ű[ X +bWeyoD{.ݗКl?>o.vn>m|8@rTgg$_yꛗoyz-k5~xبXrPI7άwxNCs5VաzƵ5@Zt:d;o)Uҗoޜ |FwkǗɚ\umƚ(VݯfyXgፇ_cXcx~ӎXqk囲 \OL*wq@®b<_csoHcKïk޷|l?ecvqs[x1bɣG_UN{377~اZ*ajT|˿v.޺qe>(^]Uy:WwNWGOYg~og}r` U=Ocgl3}I0׳Gdz'|87waX>kɋΑWVy[?ؘQ~uCu/1ivcyv\kknʭ٭!?]B`؞8 a~4-dͮ[^>gנvh$,v+f0b?-3>00;ULvv~|'V7u61b+q W |d˚+m;l v_{7~d^wԁVϵ,ڳl[5cqik{sĦh]-5踔c3<\s+צRbr6e|iץg/V/gmx_츖;%X_rs&{ڼ]ĭAֺ]}iLKns] ~S]l.ͩ~"d16+oǧ\[4 Wp.$Y dǥ>Ƽh/|f9"^8)wkq %-? \;4 :εN8umOX7׊Ry@׎ՙ?v==%Pyq:KǫE<)0c㖖߼9hنI[#T,j׎?ԗgW;i{t^y[:\d}ikח0k/f(!681{ru?;ecrc ^J}[v~קV[΋彮^z]Ki_-n/N1IgwmgagnK3.faqR{ܹlLSܹ#z'u𬮒618tuW媾dF~qx󗚯. ˻ӯ'fo7vHgs20}RׇUscd[q۸zާs}YykM1̨fvYv;.gLo]E2!_x`XGѩO|:]Nv ]^2W Мc9oS_;%"v&MYlK,gY>>k,b:^|^m]ao\>zSm5[7b-b?b +o,^5|νylﵟxq^Y?J";B5Nd|zqֿ֖]~զ<}wv->?r }j]G|>m )c1^|Κi?kf|O}Geb5o}v|ogx'TjϷrOS67u_ѿ~yrj-o1.?R}JFua{ i'38rϸ5 m-bE9f:P뉘BzbMvLA5/];⌝^|Fy_6kn.n*f/R5scl^ܿO OOm|9Vbqby͑HuGJvxk<3/J$ki#凲ZN@6^ڡN>ZukGuck;GN\m޸-"ܱ/Nln,yf#.Z>M޴|"ū,*ѵ}ŎկӍ4nr].>o}jo[8,F>[2u\nK?]OW΃ycutS淙FB<.Xxaͥ{9v׺ggv׼>x}vt=y}gL/oNݕrɏbln8vbo~Hgxa^?O,-u]7}ʟdLo~^?g^?ֈ5?ox-\Ŝqgm abqvao8pl8y 3.xsI%NL kmN7i^ULO]1 q?c(Ж[T^:c>+Ǭn>?rf8xHMk1 }K_^+1CY\UrԿ#+:7nmgmx9bP;}wl|qs[SwuQ8q<5+%csZk~_^.olQPnn+ b1`\ViK_&sҟ(2w=1⹗cX9&|&nrX#sou׹'cݼ>?^Kuq-رԩc;vq$eU%*ؽ;v\=1>5Û;h {⛧ԍ>aHٞ)kp; ^ɱoz`. eήs<ƕi޹:/__Ӯ9,銛?KS߳s-"vb }\[DC΄_&eߛϘR׿Ȼ׵kmOl{֧~E%SٵcwjslDl cg{5&OSA%rsxņ H|HG?J9~|6,Wds}lm೩[#fﷸDK- gݾAONNgiN׆܍[ FacE_YˎAv9l;/c so kXtw[kTk3ac~77h-in_B\wu|Dr~-fJNSzħ8~we'߂6N=x>;uM_YZ<]+sok٩f d̎6rckxc47^Xނv7~b=]+~KeBqojxȷS޻*[q'krvG&/-I,&9"_I]A؞2RO~Xz KǞO~\ec&' Ƿ8׏NxOƟ3㏛.4/\uQ˸WXS/n*oןo r|~fX?kﰸ6:9į lSxps vma]Wݷ_O/?OW~Xon￞sW O 2=c?y9?Gw߸d'N+hݸԆoz >[#[.~q=obAc ́+4:{?Ǹ}K5M!=P{{>U7i fеq=^,ۭ̎R찘Yr {◝ f{Ƌ2Zߋ[Uṿ qQ=}WԖg/W+67z>gwnMŀS]lKڹ{X]xuQC48=y/9)tjl}+8{Ek^;s4gxsuK%&#V>2qܘ+׀ۘ1~(pxwm阛?_^u2^+QvgqԶPؼAw]5ŝ軘rM>M*Gcu <76Iْ ctS4fꉇ,vh<~ZO/\]>L_^\<5lMO?smxaÍ/[ص "&w+7#mw?%o,|~mn̝}MI1K/>1dߓ(ץ5}Q{ٕ})}J7zMa}y0[[JKrw._ۥz}3a=n^yJٍb7}-9]O?@קl;ujmZ6o-b 8 \_6Tkﻨ36D~/UP?}&Xm~+y7k _ⰻ/|_~|я~?sA,.X^н}ׇ \ Yd^?G1[m͍75U>,,+^qOW``@k.fA5+";_9ɺr ֗^y OH0qXǧ_dg5C_uk2)7\ug-}(I;#la濸6wa:Wm%|Zkx]ov~~Q?ٌh XL~6.DJUxryԛ?Mgې"J}d 4o[ĖZsăk-,8y+&WItUus73Cܗ%/ߤ?_o{>1؟Q^[cV pq8 J1] ٿG>,7@9P4q]'Z+6Aߴ9uİO>MHGk0[7X<@ej/Mlϰ}ևh0mCe!<3 +tc=`aێ˝'bݎO9S?]>㢵w@O< Fi͗ˮz\jq}e{=ʏ"em׾_c*|)+_β7y2 5r+xtrƉ׾{֚)W}#+O_q}ΝiݓǢ'gRvx ]ﵭuk.'ֵk-6c]ӭcԺbly"TLhٱ&gN-/ıL}gS\n_ܿ;_+?`b?Tl;LWN7|C<]=-m~y׸|2;9"ftԬ9F֯~_Oۯʯ||kkgm~>oƧwo{ͽu;α~>{p[/ҧ___;1mKmq޵ѧ??''ӟ~~v??mc~//|6u53{v;ǎ{^m9W&qoj~W?m{Oh\m=-Oxo>}6߮//k&=Sg߷wm}m|POCwmW_oOϟ:|{6ԯ~??>n\;}ص]ǽq;Gc^;ojWܼ:}~vk^{>sfcy¹yy}s|۹߼~u7<՜uоc1nGZ'7o6C={o=_?M#ngnW-|XY;\Y{'t]vcd/:]Ghmnv8_מmWW?4Gdgs{oz|~ӼZ?kklWys}^{|ndZWhͻEF}+=^\}gk>}o\P{}ontQn9-|S?S~OO~ٟ~o77/#zی)?>/|??~?|W?}{?smz<;w~Ǿ=׆k)gϷN}]}]in׳{}nӿwlf|{:}vôv^;G}{l^:_Ϩ9eq~Shf{bG7GYc1&P_tz?w\_7+oG7vn vyW>zo]ryj:~{nO5\8f=۵g-s۷9q~+Wk.>_鵾kXI v"F>cw{l"kƉt|w=}~yXF ڼv}~e 4nק;=k;o~i 9? NR<׾u?soc qJ~>W1gjata*g_>[ϸucO%/ۏ՘;N~؞~ס_gou-k ;vܮwmJt\q+bg0|>vx^wa~}ލmPSyvw\# ڥI;ۧqR>ٖ=O1\󞶳Y̸Z~xS6M/oWLkɝ֜qs\ytG٘gf[܋;gͧw/~?0o۟~G~OO|Z1TL{[|F=qX;eŜC_O龋W9夛m0&X޹ces˽Y}'f{ l?7wn[D{-<_g/WW0Ŭ\X_]NIUgqQU&nv8*v {}y~'ѱnw=w?\_^"Lr;_lvmf+x=!&W?ͱw1#{)1?žz 3ZHe^|P0Ot9O[e_\[ع}__[ϯ>b]fl.&q{_lQco^y16};ύl_~;]GXI_<>ˇs`gFx_~f^bԽoձpynOUڼr~/ɜu` }ݫ6d|gOj O(ox*cw|6^-c5&3;r3f1\~d1kXle1ݞ1HO8ۇ_c50_9W] 8kk9][1!(˛>y8*\k Ѽ+(6]wYc~0O PnP {%r/x[#C>jis0\8LļSj~:cȞ:Oqa_|m.~syh^>>s(>sy$ak=F1A7GL+w8Ib9K[';9qk{q75^Ƙvs1F] rXb\vw{3Vp78St,mvgfyqT\=>vԖzsjqmxatb~cmmo 卯NA0?tmCqRΫy\يY_,7.~|;F}^9isKx.9B]79:CxY|H :|;9ė? aalx/-)xy ; W K\ b^؅pq9|S{a~KvyUt. 䉿tXc?}0|s3u_+_Ɣ˅^ؖj;<$/EL{sڷO_?xׇoksѵK1EXNqV}C!jwSΥW6o\9tr>^ϦO2i߾ 7Kwe1U }q<^ڇr!1uło%#ހGޓ. 꺊?߫ؤgmMߕo_bA/^1\İ ^|>ǐ+ajLe)_{{H)h ؗ}35뛓w;''oǫ1Z>lq>}lbq|kyr3Kĉ,^S[c~lX%x6Z.xz0A5hoiq $|.w$+hnf/0=zgJcX&6/釾W',ޥoqqCڸ8kC3l\S{;T35y-ZW_c=;/^n^ƫB}~xrL`>VycJqow|-$N䜶\}nߋm~'/c/PrDW/Ejz˷^n{ĭɯN~91穞OoWk5\PL)^,lm5k;/8qiM_*P}8o-WNMs4vfmd~>rFjpAt5^/c[oz):bg?B^Kow|paN^֡R71Gփy7~ôjp +89{ 7S[3a{= p{Qt,ǻ:kgsݫxWP,M/}*YlyUju~l%zr53c-WۼG3_yWvf _ + gkVw:5]šV=pmUs\mȣ={v6|פ^VWl%z]o>Տz/gо5j|-n89|3}'vS{.*?ĭԸ~+G.2o?'C,ZZ8j{ W>p7c|rEMFV\k^+QlPq굊^+s5`ȃyZ_x\y&ōk|:.\#!K<s}HKOl[/~iǗثuV>WW?_덃m<]B~PD&斖kuN^Kg'csq@~'Pߨ88csXLΖMnulRÍDɇ6Χo(oj-8r^wy̻&sWil<{aΙ{/oslKly}ڋ4SovE1CZagkKLoQ\Puϡ_ wp /OU/?XwתQ.NLvsd3ߕb}tc67[Nk}T17ؚ5WW1\z@y^[mʱ_59mVu4kťa^aaq#/W[݂fX8=aWa^c5LPL޵ysfKvn;,%7_=8VOviϸ6PW۫J]@"LjC޼kT{#jKS̨&w)Q_o,7q?5"ŭ5$_FLgʏ,8k 7UaE+S\tW/fux/,B~_m] t=׎[7ݸ-.>oNyXxm1kv4;v8um<ۛ7bFjDCώmu7Frrͷ57ɣOs͌|ono[~_]DgJy{:Կ_D,qs^6 ܜV"06׷Cv\6)_3UxMS%`5-x,-,U<~ӾXaa׾y u˭my:9c-u]~{YQ\Op8wMSy'XreVK8G;t\4~/V.wVW󧘖j>s,9zӷ^}ž7\~ƢSw ޝm!6I}nfy8W7)ƺ};Fxt}Xrn_1~cnujݼsemȫrc8λ7Jjڬݜi, C,EvOb +aFK7z`y-jy~ۣ62(f{oVݳx6ke;ZC.\N,ͺ[IU){n1@}tqIk"fyc95bMսO욫g /(arq\;\^k^{o͚,9}w]kΆmn5:jk+a>}_]e Ol +yYk>xݰ0PZOAޒko{abhnr>M><뷄e{&f}Q,kY5lz}ӗ\K5_iO򫌫ͣ_zpʿZƾ>7n,aWN0ܾQ V:Zg/Mgg]羝c_Xak_Jsf.QpK3i]7lY𥳐50]U5'_ժE=;,w*vp|߾\;- +oK'x1jo rv_y#mU9U-1H֎{ZѪv]G:j\oc`;} }xyj7?d^U?d󓎁vǹ|9O+ +q+Wm{ #j7__#Zr/=`؟Zp{x݃/gqo2|-ҽx[xဇ]Lɤq(fm8ww{MOkbÄW6B''O#b9S8g]Bb׋^-&56\Mp4jln<|ПZC刺9]nܵs6\??~cqUoVW}xX8_[)v..?Nr'wbb2a&ޓX#.Zy(~G|]ymn_P#!f. Gd[Wd름0'p9{|L y|~ΩO=<14n˩w9ɿ܇ܸ;טZ._\jjnܨ߫S\\{v/ثqm`bab 5?UK)qܡ^|Az 7 T}׿Ta]+x? 3!vܘk:,'lkvb8&j ^E֢j 5V|m=xt-HjUlnm| 1)ҏO\-YePr^9ͻƻguaBιx6&zbp`+oՕoF|j /Υx֍;ĝǨa_Ʒޚ"[Fg5jNӳ@9LW?V_5DaGX@X=4Ϙݞmmkr_ V7P|M:# YRgʱbo>G~/ +\|u~acoo);-T;6Fio.3vqDZ rnK/9]@ y]$ҵs`0m\p[v_kT +_`_W@9兗g|ڳ?1.V}P)ߡr/;S&oyt79|F{~/&#z4γq,{}n}oFu5s֌iX k3ͳrOrZ'=߶k5O5][+Iݪ\|Xu4/Lp\m\3b}?c5=.w逳5ijAŃķZ^;A<]UuPX«ƱO7Ey-Z'?jwK>|^Ŗ7[s-bo.z +݌^~>Ay+>6.nijQW/>'Nm}3b +[ښ/_|{,uPvjmP9~ތS歛Oeϡ߾yͷ6?y ,u˯q_{Frns3?D9-ˊթ"?Uz,"[?6ǰ<.fod>е8wjQEK䰭v[5=,i&]n]mY؜nn3u]֗ˣ5hv?۷ө&,؝|^8bײ8u5Xm˱9{v`NG3߳X8}XX㾿?0'o CoȾkÑz6#wwwFjgߜc${ubq%o^y,_К;5ۜ9!(_o՟^=cj9/>s{|#1lkvc}:Maqv165]pܹܽB[' };wt̻.Zƾo6mNٽx7ϸ`:jhK_>C +3gO9v4"yx_nM;ѫc?lMC8-k_n6]j?Waai<9ɹxM?zSlXAܶ۩_u wq䶊,zg)*aZ\PCcMصA5䍼4/M/ >r+*G`aǵb[>pڸ|ռ.6⩮gNf|V,NL*?IT \_ ~:&X8CgzxW:q0>}jȹKa*|a|x`:`WxՇjS~ͥ_lFk74wuLk#:k壕3Weղ?k-:5^[u1_3Α]^ENjϹL\UыmXԘ|Wyg9kp=|Mć7g5ezv1gv[?1e٭?Soφ׾ +_uՋ/ox*$V~x1:/.R~h~F#'u Q_+wqlu}usv}1n~j~H5&J6 X +Tc{w͏0{{?Vm-De#俚OS]sZ?yȋ -祿j a!yqx(O]3g(>v%#O-`_ĶvpIϿk%4*3*.m۪8rt#q?n>pbmqEO',և߮a[/MgMy1zgriG+Fx<7+ߵfhcښ ^X6_k֧A'W_Wr?Sϡq7z̓{t\r9קz*߹ +{9ʱQ|p磘M>*8ahO7R˶!6,'a.t}|Nm _9ͭ$!4?HRssH\7#|y qn1N߉w9KoVoc崘?r.&u.^3<]'ߵoG-(돨\oa~}~ma +nT3 loߵkyˋ&|v5ڧpCmk՚1ZeReֺh|84!5)kCj̩9Wkg4idz>14B9q}sp:ך:D/]5!]n}|9/iT&њܒm|.˼z+o|9#i\tjlrSli^<1q;Ϯ79nyrl_}~sN(->N0L>x#x={ Ü; +mcżY-|su^ ;l}챺}!^PL{7/m}b۳ޗ#-Y`?kTnu-m'=֓ c +^.] &P;Np-yb=Ryma [됴]k;x9dZm;_w[7nlΪ](Vb&t9[[́tirkwkF.uchmw5ˮՋl-Fqyq :} Kc?o0Z}VOdZMqssT~tw pi^Z-{h r6i|e~i~+*gjM]Ԫ؜krKϬFg}l\[;ۭs0ڍvjspfF"_D&1w=p~::kO5&˗1.5'%s~fqagnNUN}{=]gZa{/bxr7nMbaqVZm Wo|9ё?1sW_lZk0_;v떋ZekV3gemf°{s_q]sfX?N]scKX9V=o^V^g"QOT1Tq梻qy鞇5o-d=جVZ o]c;nrWbw|ks}r 6XZD^\~7z!n6Ǿ/М6ef,/xJg̩U/?a5pR:{rvْ;-X8ipoo} sr]]\]kWk% i'nUMyDksgo(q-syW{POfWR3N[ p8a]k{qwC^5LzunLӛz?Zº%Yo?Fau/9tk%5SnN//O>`6rFXnJWk%}4@pl&T7('mzz/b>щ.Wy+ h ^e/6^Z.8^1~>Uk".R?h}],}w6_ZߜZ^3b`(?y}5rV6/_v5~'^]=3\߸]>|UKm"myb/zwܦe/]h^kЫk>{A8Hllm~ a}Xu} s5b-o.ϬsZo {q)M:OxTi[Ç?ZK_͓8$_kfh3Tcvi;o/g3)OecqsG:m =ySu}os2){ԙo>Pظ7NDr mGaEG׋3Y WLM^-ai_ixa<#V//@toꃫ5,/U[Pp_=!8XK]\oe+=`jݖm1֏:XatrboCs/ns>_^um=m[oią.Hj͑;_^wmR9 cs^kwͩn43R˗0~  j3FVӮ{Xbbk֚O_yy@nc~^E׾/ӨgsUϹ) .l\<](GYgt1>Xb>sb噳!|GuZŭ ߕkLCɃg>r*@߇]먙GZ,8auMǸ2~H9?rvm>p]t`%:V[^53}gOz;l괌nTUl~VJM/^9 #/h>^,|ޛ z;kY'[-g}W^}s;Z+com-s<}'Sv}Yߋ "޿5±+?g.˚ͮoU#<$?|]6Ϝfc<<>wnnmcnM8S*^"aKڬTCr[Vw{q橻Z'_3z}ᅛwq^tΊco*v-x]1G7_?lq^ߍb)q¼05R;~yrlb,;oWg9[lu{ٯDο~u]&We߶F<޻e1U +;jBY/O0?nl5˽n{X;Hgfk:!jiY?ȳ{"WQ@YچW;;o/ybMp}d5.VX@\=7ڛ#"yp1_-͵8n &ti];M`rwaM};ߝ+Kz8ᑭ|7u|sH[WKn7k-ֿuk]muoo5:&jwݚ0jO1qݷ&>1WrG.pgG^`Փ_ xA)nՊئa'זVޮ ?<^{9?Zynۨ=7>EwrA_ڰ-.~GpM1_Boz V/Եm)Fm⮁뀸V3[:ycuqܞ~5W8^K5rNRV?ϻk34^^.k3wbWn1ð ݼ1RknW>G:}h51\}ѧk1:oVTo՗\? vߴjgtk0FUSMּ8ƻƼcs|͋^ERǸ{{gvy~]ؗZy/-ٍ>+~v~4)gXXss9>w=[1H~OniNu/'{|&w-[\?&CDk\?bwbb#m:XK?t٭S^Y{wZ,B _Rk/~g*{~lgGZ]*1p+{ʿ}kG wmQ{.#c _Xc +wOxYyiw_@}#}!ý:eR1X۷bNdya>!Zp/x/Y.W_!o7.Cso?&aqpuƠ53m & [Hh>y}6fc8orԤnʸƘyjé2wd_3s_ڌu7Zk*{5j巏WXPUcwoOj`ŸV3S~'vx:!Gkư/(.GN(^=^vy6}iΉжmN#+D~nj;7_|oMЋ'6N/ G*jq-'˻d9uַ#"wcj'\[ܽqM[JcY8LQx>_Ƒ*qwd \N`ms54 _V_ݨmmF1RO[Y\kp^h^>~[&ǩR"^iqu<3ߨ.j^RKݱw>rln8a/ zWs>og[{+lgد1~ZÚ%x'1@sw>Wڼ\ڛ."79,MDN{К#ᗮ*M]ikw_j ෿Ϙ߽ LڦR΋di-ڴ-?x%ѽ  '}8K#sGމ-V#uwk-cO}mZM[sS[=[O|t5 k +ov 7[l/$oG_ٺ^ugMk}|ڮ1n߲NxW-pb1m +kNgVlܺm|hkrwGq}6 j)Ei|r se_| K<]Qk[+xyz9>;63.sϫ1w'~8T_<7ɫ1h9r]Wܖ$o?_3 {[ry_vu˖d+h8Xg{m󍵭<@+Luqo`ѵ݋y/Qe]-7FrWcoAH^G3=(l]wc6yC_{bxi)5xF:_8'p~[\l1AiGC6ǫWKE9ڵ恬wW]l:,rZ}Q+\?ǝW-Ս~$_>~~|15"=咙#6P>͵GZ?umUٝ𢗖WϝSGj\^2{w}}njӞ]>x_juE ԼjߙS7@mz% =Vge-%/+FէPf0~E_xUro.Ǫ'um15%׌O:c;?rwC/ ^gb"uyn; P?\^︯3ƒ^XBUjs/|B;h-DFX?pyŵW3%#>#t W.\p>)#wiG6ʋ'ymꂭBݓr+׻z_?{h,{k7Xc9.2:[KRCק[P_֧ܰyf׺*ͣm[nr{o2?r ɯY?7FbFjW}a.bUW:FL%\Oxy/(0pkCZx4<6^jvWk[529ucbքO}rP^<UǷ9PaqZ-},友k6F^h6Ǡ| h2z[uŽW3w5,OX.N1}xi_ێ4_߿aqad\\%SVH:d.ogC端_߳^pwWڏxq9՟jQ8E;uR: 3awX즱a>F>1z`kAx]׺ K*ǣ~lԧޜ{[W7w\N˅&Nv-[k\8Gy8M#?^yX'^fǺ =#L62+yEL%4z&[28W^}^߲Hgڤ 5; 1H>o0^\bW@kvWGin_?[_:%.H^5cbzqm{ja +jg?Xͭ}fNksgη3GR ܶ5,{j~sĦkC}C9ZO5BzZ%Z;n)N<𸗎W~Z]㌅Sl*s{~c1쫹9X>|;gqqToRz~q~yy5;ai1.sjʋh?/ׯw;}nuۿjLJǡZƨBqbQ6ig޵vٮk^s7a2k>rkͯؾW3w9|iW <6ybor~M\oubCޣ9,o}#LfFilRQk\_׳CO1o;F~臾o}K+\m3]oe$sSoa٧knqӰNkiucj-/4/hFd5ά^xk_3.8Olϸ7?0vI7⍞S,AG_u$7g4/#mt懯-g}ꥼ4yV+/gךwܯE#9#_(\q܇ʋk@}\}Sq"&za}ɵ7w)^bkňk-sWe|-v&F37N0^N͝XDXnu17o6.VaͲދ_/&->U_}r}0~@+׼5x;6j>1/X5;Pd[s5ޖ}<|ƻiN9yUӪ>yụ#m՚}b\,q^\_8_Ksg6Gعh›;eݱ(ź&Hxak9XrUe+ϷXը1-֋o5j/99i~3:]c܎c]5ln}p]#K%|y1VyAhw^pk;^g8#rbjޏv˶?P$8L6ukvj|+mKϤΥ+sv:@y<Ņ[XZܫrV|]﮵g]| +a{\M9k 1ӝ=w,6֗CZ"Ovwcp֯9L0Y @L͗Wum׋˗0v k +k]K?wxkS By,]9CWSý}5|^/'KkVCxޟzXgpk|[(gr6E[K[57ʋ/]FYO p{ZYbP-v]q6_oǯo/_I Kz׸y}U]yolלX.vewUwCxʻk췶o}upa9v`>} ,Fœ__W-25 +v̓n[$KL>Ƃb~b݀cZqtŌ ϭ=#Gw{]c.Oҭ?w/rW=KNoޚ W}ܲ]5a五5ī~Z~`cɸaaCvذ=m+Χ1>d.gvǑz_߫V?Um}|iz'QhZسW/gl~J{$~wǿ:~yíW3eڃ챹Cϻetl ]5qǹkXW|T?Zr1^xj9+zQ}Y/U#.op@ VWڨbwza2a]ED_?{.C[й\ -u]O\`k8՗+_3iLVO}Gڨ.5 SI7qVW}96=x/b;Xgd{f>{O^&j;u?߮/[dmO=%0`qi~fAj+w&Nzr;6./<9]]{kzy8~~:|sY[osϨmO5ߊ=ORlE,eu`/Ffs[qrZJm|11@ZϫK)V6fly1uoz ȷPKg_Hڙ&Xة]}9Ǽ>ﴙlQ>,so=͕-i1lwH_I{\4{/?z[7./4꼭Լ[k>ꙈMfrw\yi..g~8-~fu{׏ݘ@rQW,pQ ᏸ6\޹N뷔Zz>\Wn|afcaYnS|\pgz/?6[Otfy3{Sw56j^lgk#W^s?²f߈?;5Zspj⬯!/`k/q` ͇UEP@m#WryK-OOΥ1n}F}tN >X.)o5!P<#&&*z6~A\z Qy{,'WE!9H&5xiki,_& nb~"phnDqןmG\fuK=hfSk^=k{{_j7_^z7~sq>51b^΅|n=H[V>_؉׽O[_ޛޗoۺ,'"'s|ޯey t6O[~+q9~qXmmuc{fjLwؐXg2%\u˺[@dCׇ{/Tq&)^Tp1,uaGPGaI/{zrی(O\bby9=ۭ/msZ3-+FNuݷr^-kx߳v _/qk(׳6t1յr[/d?m-J:óq\d18kb[X̯˜`5\ghu^@59^-=\NXbs?rƠ[UwtX}k_s̟Y S~}=?y|un /yQ.F(|1TWEfwEV(wF{s.r690foKz/1ƼX'K[uzrt̽x^Cb{Xn8v>_c'_iɗ:-ml~#~~Zj݈9=/ܳqy;r/ŷ^΢cqPo?7ΰ6Q1CKodrj_63->{>KvQrqQ k`[KW%0Vb9*sXlDy' ^c.x앟ϸzWX݇X}>W^Hlm6g:O۶Kӥ>>>}Œ#ժD\f;g3nzr-ziں{3Xrټ&]p9̹=绎lܳz{g/W.|?b];Vcvָ\J~}y|riï7\>l}}sGxycp1<kׅUNXSippv߷Hug*dӜ3Ņy귘Zqb3rK)Jqaǫ5w-0Z1kKkt=`ycqAsC8/ wc6ϺYq/[k]Ǡ6xs!KkɥYr53&+ GP-w^{`3xy/6SVn}T`kNX]ݬN ?Usd_,j'T5dmyٲi/v}` ;pC-nqOwċh%+5v__\f^W춵ak%7Z1/~2%~vO(ѵ.[Tmvv\,Vt m|[-'-or#'7,N vu>okKŽuo._r_+Vj}*~.Qzˎ.gqح/Y{geϏߑօ9}ۊۛZM=O/޿ݼxg-^^yt,r}#}ic!f9V뵍/n,οu´ٛڦ`޵I_1qpoGn^mq\Wu>5 ,G*еg]#n{mZK`[8jTgdMwTgDa}|`.t</x֏{aȫYg4u).ԗqVk5/ڶNUrxWW[?5͋^+.{ٞ/G;˕_L;Os,Vث=qZ~hɱ{s-9^8w\`˕ԇ W xXmi YX|0!f.&d\cUqbo֦Ydںk4O23y rOu-1C<9~{f뿋cSAmo뙮ٺrd峫bNzRrkٷVkVa[$[lml^K{mz]c\}3?Yesϻ9 lM#]_,I-K+Sc8O.cx}wnq%x_\F}4RiIz⪈SW_y`Ř[@ͅb媯T Bs\h"k,?׭-Aw@.܁{]ѷ% ݛ;_<_u݈wF=ZDZv_'jP/Mtk'ƥ/>WNH~!_ag7̸Xmjp?q9wW0-c}\n/yaݜ +mc}ޘV^}s\is9mۮ--Os5۪+|0爯˸O9&$̇oךvX؟Ws7fbgw΁Σr򷟋.k8ZU_I>t늋A;Sqib +7+j tS#N6Tk؄>L8鞅9:16}*.Z+C:1sF&dkZ+PKd .aq?$`헾~Pb|j661`N%8.-VޱA%_g)[h¾;kkSo7]s}]?qm;/ 0kS4Us19YLrx,y9}U9Z>U`9f=5ٮZD>Xa Qh>5B,7"G6\X#]s39,N5ݵ~[\ok.G~m}8\}&v7ǩOyChvC??OvSS~Jjz&PwTl~Rk<]RYqpƺƻ ĎdŒ=pyb循5^XvLJxmVشug3$.T^(tQYNͫt~%ObLqc{z>p箷u+Sc'^&h_m5ǼqO^;b{~mV9]M+skpNx-krY^梱$}Ȯ-/r|5}|U 1n'j)2ϼr})]O~}>/o퐶~6{qRm]os|TWg36/ߡ>wWk_\oy鵺7v>}r,ȓ2ռ.*\TlgK>9h_]Lpk693xo~We5֭hUA\&[F|RW Oȋ(yW90?RZbݹ Zd9jbaF [RԚ~i~XMe_<_%=S0y|K׶o~/Stϳ~aW}4׏y`9uvmRCvr= l]gbkY1eCofrn{uLt_ɶ:˛o0maKW[A}(1؝??8Os'ڼϕ9 x[[]#;_mcj :^/=+>Itlc11"姫Y;OӵxW 尺^5V+O~_v=kG%k_5V߹/q~cv}zo6NQߙw2|f='/_Zk;C,wWg?a?V Y˟חf/]|uY7ƴ/Z:Mwew/δuOkl\{ߥz)>Fol]4D|cgmq7.c|3 f,͵6X飘G3N+֐ 7q|k!{Z%OSߊmbq/Ϙ^oϽy{Pc7Ly{b3Y:1c=^}M_^x;n[{v1 Ksw|3xs\ZXuj-gzkr5Q'w8On=aGcZ>ZZ߫_5We1\*w=_؟uWQk];4xk!Џ3~ZK{,O@Mj^լj;&P|Oaz}4P/}u P[}xq? ٺr._dq⍋,$7+\"D|p7y./9Z[88Jf9bszq˯o56,YmTl\k:ݳȿ\yu5fԞ_['{@rNǕY':GKstp޷~ykr?O=?lNuH{'oYng{.rXrrZ?E_j=Gqe~69Mj|o^$RWô[r)]2[+H_/ˉ^9S_}oZÌm}Kڊ~ӈۜӶf桚ۊbt&bbu7l[fL`|X߮ 񷵴+Cy{Uw߷>Io|owϼk˸ "t.6Ԯ`-TswzՈ/U'][;?lwq[۫K:Yr 71g϶V7WݜVZ_vulyz/i[8!mk9l^ϋX<εA g榜úWc4䤉vDW''-Z.P>o-ՙS[g$KuMmch_6YF^7c.H,V˟T7\xbf)mڮqJk|ļrֈxiW}A_m>0qx y海b;탋#,6뵅Ǫ۶y m ;c\^X_پOj +./YmS{nii;u.g%nq^XXJLҾg,zfj>?;vxm'vd EhiSu67 ~n'FVċP$.cU?Ed#;^a󊭴۶;w}֙鹯>_csv?[{g՜e?fu]SKQ8fcfgѼ/vFu,ΐ{`O}M|o^5˩\XS";$G]y5O'}2}lli£u^/.=xuGjzj +;wZ'⒭bj_{/˜kTk>.in~9.?|Շyr?բY5:Ly+>3y1rObꪛg\h.Viòsx7_S Ƭ&Z_}K(n_m~ar.{Ukko}>kpU`Ϛ׺'a޻|=s͗ݗsw^j~ajQzދiM˥kV`ocʯ Ϗ֯WMO!ƧVgclkwu yozL7](g>ظxE>j^yy9d7^l{i?Vq̹&mcs݋n>n׻?ySb/s04AZ [nLZʁ0昌_۵&,Y͉f(}o^m޺%nZSpyΏY​sy_զ>/.NjXrN}8x}>b՗l~w}'cũޜGuW?":W^C^5|mɗtsu/oqǏ yxq\^5˭QN>@2,pkt1/y]>_s6VpDZNIQc]0*qAc7婬6SoqAqSSn s ֮V'E|)^i+7{su9ymH՜mKqyƟjy[7yϤk3Z%qU1#y'0[>O];u7{v6mms9,ŗ6X?n}p5mjNV!ƿe_yխq?%( ^\<}7N}h;j| k{e<@K_Q|omrNomJ #+{a|Z_lpT~qO9.*=l<7SҞs9kr"tX[si[@zai|xPN_cl[r<=ǔu[:_2쯶STTsp%宄~|!7 ÓK8ZaKY1|cl7Y<|5'_;YE\1Ε&PZYrZs0.^qvVMl/|a?z,1JjԎB^☞ƗbOo}ģb'5wc_?O޶_9p}NfÀﹸ0Cm>jDYeNle@ߋZjyrRL?\Ƌrxnl]0kkb{MU gݰxH䩸eX_Clt®;p^{s3usHOƧ +ɕ$_΅>F@dV("?(/|kbVWz1;A?uo8f>ѹ~[,8F]Y/}}jhyy뱺n};xA]|l\_"qpA_Wi|D~v1*_QjXza;k.0\It8y5wKbZcyg/ V7~p_צXC>c)"ys9ֶ^*/oMٚyysqވR:zc$H0j/Gj)15}lN'vu[oZGuUGspZT1C7p1j5b~fw ;sls\Uwvo֏fuN>77~[nʛmcZ_2ZQPnclX>`ڋSWԋ#Wub_~m7뫉\[4޴Qr׏ȵr<kCw,ĭ9p}rrv*7[߾6~q~|][d@DO^\__]Pos} #ܽ~T{c+[] #z?tvon~ޡUu^ 1۴&/ ss{ߙSIwEmeZLyjAoQ}=޽տXMjFܧWُ{olqɺ|u/~n*(6*n{y_\pՄAc?VqŋY˱3W"8 6G~bqfq{\>Y[[4=.fyo/MbA:}c6͝/5d|qyש:q^*zGZ)!H9}~'u9ivmqνֽ -? +7[O2OܿӾwuf;,i]}qSr\xy孷ϰb?7[uxxB@4oXOu7Coz0um^aX]wQ^\.1Rc3q-1;Э;~1o"߮[ #d>?CZ?^qyk,5ZĒW/wy\smQncXOpͣLm#OϹE,K|qQ<;/ :_VktrƑ_5MPa9{~_>+|iW˛VZ'=9fL),w[>|n;k.Wh}ʡ3X\pt!jzoM{R[>H[y[6X7PSsY8=s1>~]Oyn4}V%~wW\}5/zk6׭?}v*KUO}e=L/.\:Nb5~"o0 0.x-+\>Xo1\MD1@s=1ڴ_}fy!3ƞ1l3F[^͎˛74&hֶq#-/im8%.L$owU_Չ<x]}VޘCʝqn<-o^6; P]C@OUzt|~x3<>kHP56ycn?-^\h56p8ΚD9/ck꘯c@[~d=c7vw7-rsu-7m]9~qqrǽ{ ^g(OjfܥL;#;y6ဿ~/kֿEXt֝-L~S1osqI}{C4n@q^Mj;6;$4l c]p۶8pb[[YaSk9ЛQQݏ:Vk׿^jm„z^ߏ\GT^΃{W^Gp-7{b~]"۷5.lɫ+<>Z"5?{G׭t=u[ KTƭyx͉鿳~|q#7O18bq⍯ϗ[,Q(M M?8X[c^M#CXk_^1Y^nwĩ+v^>1Ř:w^Yeƺ6~~{nxTqG]J^~|}~\HuJB?o&F\^Bc /_Γ~y#gn׃Qvϯ{ϘxX{Q̭^49[/1 +n|c<Xx&F-ZS/|r׽zou.[y}=h콼/v$|[~^<sx\qa.F)ö('7zݮ漏xq3q1xq_DW8qګ7k;35/ɷӯoyo^RMw>VϠJ5K#τvfћko}_ݓ9u^nym~\xg9w9~Z掫[O)_5(1W_P/K=ur(蔋Ǒ/^`54|6DŽ0?q1[ߵ +֟UoW׫Iww:7nw/ +ׯOڕϮuz3\z.#6ZW>|Oq3ы9Opvʟz–{cn1=v~yQw.OZL7~9jz&R .[Yzr^ OM;$sz1'/_6}:7w6]<;'uV_ ίlŗ]X,-i79mb[jq}> q8puv<}MC] + \ls0^^[Dz8I"jy5oŌٯvD^]v}yAKk~VbA4_|P kwhw _=VM0<δ^KMs\;ܼJ?Ύ#?zx!=qɋ҃m(GcX8A g}z qN18rO[l\olog{DŤԾG9fK!x㢹򅍧}\O;ȵĹwĊAGRkخυʣžovY^1XyT7VUt]IJx!m/vyV덟r?7`p|Qc=((~:/6^_/bdW6Q*޷BFy?Zځx֣P+Wێ?ꄋ;^b~ -ș3V6(z/>Dc\ oi2_|>v"׋sm }%z˽(?/.x ln{zC#8/ΙĕN֘snuO󿮦R}n⛞_o͟v>ޔV.aM9,~fǢ9y7֒јv>}Kuge1׭Zd[o-G\㵞b~¯b..xݷT'p.~ L~cRE^H>fqӅɁ}u{ɮ8rv߮͡Xn99UEz.qXo]}w vm-q<1@A1 yw͐Ս؟?گz:8ox9 C,ڱu[=s3kj9[Ч&-JRXy|j`j6־:F +ZwP6ȋOEW}~M9ߞұƵk +:b0{ݵ*Ӽȡxu1-abS)KwmyE1՟T_cum,^ 6/ïȜ+wұ_>dyy? ^.uk7V+<Ǩ[hH'X$&=U8`fgkb/zubNk=/V 7t?}o̼l\{Xz4^|#i)l^e+rwUզrv]#d]BmR~j"71N-/ŷ]Fck3u^9z>{D}{ovklmƶ=Ngl n,˕K{n 0,}Ĺ"b +җv<\koLeuֈkH5Žy,OKG}uWh8q51k"jvkKjO' Nw\󹱷{!K5]orW, n6yޗ~B_^u> ++g5~=Y fsh>o)5(INO;/gg)j4~籯76yyfͫ9w}p]oes3ϊSgl4[Ny(>Fi@7w. S~qH͉ qaacmz~]y67Wj8ӯ/x:PSPk+tS-cxj'sD;+t;Krf ma^\_z_ߓ3(gsq]t7Bz[#P>{K_>>_ P~9rZڲIo)}0S^ }ugq.]KEc@cG,e7Xb@@뉰{PPY_bŮ5ol}nG}1LO޾=r~k_zI' a6f6yq3}'ּY#˪jY5/kt-\u[8uwx=W#FzXkIG|㵭<>hlirU_5~s2n9[hAw\coj jA{-'[>1 -cL}>_ՊÄZ{>X< [^m{/&輿kp6:wM{ݺb{DWoaysۮ99N-7ߔ{W}xL=`xxF~WNK+kE{ v+/pQ/ sZy7XĘV]u!9֖dNrjr+c''dWc%pƲ +w&Ͻ߿% wѝ-c=y]D]ڽV=/kqmo@-_k8]/SX:w'Զ>q4;Eb3aGb){{}0T_&d=(VWסnU1u71~5];n.+Ұu㢪ѕ嚮>~&8 u~o}y}$6<{tgZw=G=۰oWOqc:#%^ {a{_k>h5Ʊ;wc;˗׶JHózchxU\\FM7ws[n|bƍ~5|}VϽoImLmXݾk.gҋ-:W̋~G޲)kxlqxq⟾)y|SC\,:{V\?]oV/]]~qŵ˻n@jXH.PGqdxگs{mՎOrj%4^=}s砮~\\=f-jhw'_{Y]˫n1Cq#?eؖ~no]M>_mv FXzoyϾbڦrNbOԋE\6ɮQ=l95%/%\B|0V+*\й.^H1=8SV,(1uJzQ,:D;n'_Xo[_jKb%b r6͂Oj}g?\)<ɭ/*8'Y{NV*nSNy coh;L>b׾^^7OaWG5[]c +^g1uŚn!ω:JcB=Pz~aQkq\c⨋{~loWg.wﵝ}r׈5p½O<ֵ)^=b'ar^%G`.D7^6n˥ /(o 7S|̸fS${Y(Oob3XK7vWO`s1_1.5L׻kqʏwL#e9?v=u ^ :Sw ou}x9;99?ys5ś;nغ8dCkms]z۩۹w V{kd^j8aJ3yi <W/,n}Oi r:>oj^XΟyk1ޏ/

    k\9w/p\3c͍jcq(.ַXsbc'sh q ?J1RLz}_g|>y\1q}śƺZ*FtmWbbzartayow. \?Jy#m|ո8P^X~dAG:[Zr=_'}$Š u99˽}k+'Pyc ܚ;X~i6ַo6)/WLlŇ_eyjosϗ[/_z;W]\V_ _iyS8`\_}2qd] oj{}ߜyls kBjqJ(v$=Akk5vuU'K]4uឍq{צV3_> 5E_C~|yݸǵxw⒛{^Z<Qz?v|q?5{ɳZw\yz=_9zîңwNy6c;7y߯}g=:֯䠽j'Ŋӛm]3j_Si^eWndf=yub6CU_b][yoQBS^kS}tχonļy2jQV z\͏¥ͩc[yqi^ZΣ^x[oh>Ztx<.&׭v(,ܘv6o]_y1hb c+q?<8˕ąCHិ}& 9IsƭՙG5KZYB@~WN_.Z{q_x }bm z.ZXn}y(߯-\sЋnwu%/W8,6Vbevl[yA7yP$[ W#&*﻽;twz/{NՎ +S ;w k005yyݷ`q 8Qxc\Ńk 1u xέ:qW[/'gMZ\??b/!'ڽVZ{8q|rX\]od;k;Xן;WkLŮX׷qy13az/eWc)Z;_1Żj Cاd⭎/ 9y}LjZ Wkί!Ts!zIXGzakQ:,jgUnP?[kMXk7:^q_ \>Ga8szi6&_m>=ӱo=c{V_Ʈ 71BX靱A",^_y[k}i̗XWo 5׿{VZ0ŴpT ~mq>5'ӸN=|@ߗgn1rk._k|둷|޻8wZ]:\Yj< ZL[ޥz$646Z%bi'!8yTћdT=p:=Fb}:ӒciuoZ\`1/m[{]ؾZWp\( ,\qܲoXoՠp69[3$0<0^`|w]绵~ֿ8߹a7優A弱Ze]'.'6'qlkEukꔝwVcW`5tQ|ՕK\W=q5]wmz61V{󎷘BoͥVXBZxףENxz:Ǯwx_Op`{`S:q;(_jV_@qb^K{-~-aYgurunrP36G߷n%V(UٱZxz8z[9~:u~Ӹt./X\,諶}׊eYڹ.uG\wO u$.|=^isv 67껷ϼُ3IVբ_껹xWx+,T t¢%z~F=>aƃ%Aoܣ.6ޖ?b.oTM1Z6o^owP+S:,η)cWx j| k/2_ rʰ֬L__oL<1>Yr9im9K.j~ ޺1uҗ*_@o8kuݤOf1^cJD #]^[Z׷]ch<֬` WSNǶ bh[O;S_ 9׉<V,n#vb}yꩽB8V45T c(ְx:j'qK9ujA0鯮P/^5U9_r;ϑsmz};ߟX!Mœe̓__>,N h?^BNXU?z뺨pl)u\~G|پu{_c + + 5V]"4J'6 +3;#z7'p'UwZUW31ǜ}///;;_>}?/?w;OOnm_˗??gg˟sumnwuc~_w_՗~ymw춿˿z~;6xxygmޝS׾a}ѹ;>|wݳ5Wt<;Nmwy߷9f_wsjڵg-w|[~6ϵnwum竟m{\?69:;>ٳ+3n=ɞ}nSFݻ&3vɼ}/[ywےƎu_CyטOmo[9ۿW˯__~g}_կ_Ͽwm, >{1^dɬ׿ƿ˿~/n+Yg~g__w۝sӯνܗwɟ?_?_~?G;ȏȗz~^} 7k_]V{oT]߳\^^vl\y[mx{֯گ}qm vupVwm 羛}Ӹ1}wuts~s>͛οwq[r{m˞ưrX[6Iּ=oɭqO?\9z py'O͇>dPɬ~wMk_%c;ڡwp~uQ}wp%]4_mۚ;}>j|Og}kwg/j{p ;Cheoigfgv6~ENgn-P;^c2ٳdooi{Gaٻ.ھM'Q?OR[3>iͫuI{߸-}>g~I6}mi/tƪ:ogKvOs9usQ^wǛK77{dV{ݦ_o'd4VC^w{u͞[m_ab靟O6m{}p͵G۵KtH}l<ߦ߸Pyמ<뽦6vOtN5?XONׄ7}^OI;(}3mWթp@mŔűk׉j:{/mm; z^Sijzte[yٕ SĒzv7a)uu@ظнI~qwz{vvs?*0} OԗwXc?c_1;?w~}vq}R{~bnkh/;{~̮6 o_|0$b1=p^_NabEčTi:MܥGf^gl~Co{n ?v-es {Nm{Xr-#6o{7nOݸ9?yp}w̓_ǴrI6zC_ƶ>mڼ}]W?7onqyn6;|m|QcY|i856qQMmwεwql[m +'Ӟ})5~^kg-^m3p~Aǻ^p+qˎl\oSN;/YPlxb a7UF},#-g[sܘŞ]swbu +H.͹FL;̇wꝵUy^b]xsbb\;?oS\;'V~cߕЖμ+# >b}Ρ5:~Krbz;M4 #"Iܲ-jbrv]7f1XZ.YL{1ڕ[cLW8?t8N/%/151΍ [>by83↋\mr|^,׋oڗQ^/j3OkS'~S>rן˕[),nf e6fn^+V-&o<}cHg Ə46|_yq]ԕb/ۘϿWoZ_m?(=ŸK%?|~m/x8|{n}v;Lniӻ b-n36?7Fڢڣxu7-vxX\1xg#dms0Vd7> ] }eAYpkZށ[q캍~ڊۘfcʨŵlKG>X.Xgܱ8_K'sÌ·cfkT8[-Km|7&=ۚWwεs.]|DVXr-crE2qÓV:e8e8qlw{/_|x 'qP5[3Xq>|Ӿlg횞U\o۽Iwf>)/x1ws_ԩ>͏Y|PO㞯&X7go<5/bcD+^et.XᎅɹG3VW-wP](u?'Xybvҋ)nQ?h,6q{-8קN&r߅#3f[j3.'>Ȏ4xq:!D+-w~ӻs#f-*cvr q]c6/f/e/KS̯Lٜ|\$17W'|C=֧;se#Lu_x{ݳPl;l/ss8==۵Uy#޼Kb獫S@Ǒ>x{񋲳ugl7Z]c6񷵋_lT%s8+Gk(Ӕarzgm͕Z߯_,[{.댏/wҘbbKg5b]ƎSec 7o+w4njY+-zį-ּ)n<O''!"ܡ{gsP7՘y+kts:!-]=(G;9KO7ꍑ$SUya/-9 7{@Zɜ-s^%m^cƵY5\Y +k oDvW2po9e}|ڢoZ~?klqQW7n\9yŰ)kiO7q4/_O{gYő'o=nmf}s. 敋Q.,l9lvmՌɝ[on?qD|bqǺz+Ȧ>ܷfbhLc=$NQK(>ՠ[Ogc&iLnjbPnbΗ1qw.N?O)٬U?.y7G"xq`bn/~2?{-$n}q/g?] jM]'- -wI$1cs?$EW9ؚ)׏le{T5ǎsWO_qGP|>3įx2qUG͙is_Cys͕Ֆڧ<=1m|k,'UQ׻v'{Xk~\+Orcw<R-(g:|_eNmoU',7U:X9 vEo,~k;?,mǫ|B}ۓ=izvUq k/7r^?LTܳ9xb>)u~𝬁Na}-?Ku>m}a{cEs:n >~6:EwA#j?5~.UJFVw䃽rі 4N8}ɷΑ/`srN[Ti{x] ͝ta 'i,NRN9XGR_jsDݳ:vYN>bA׍ymd[8Img#Z 0;PsQxW6UV5ESxyyXk 9~bcG~t96'EcV^۸+uDJS.*& u¤{ [R21k=f,!J6v<sX56ޕ1kqOtk1疣}ert:1N"Gpe[rŋ2g:D>m0:;x+‚r0'l|rK<ƛ9;}B=WͿ|"uN>֋ g|{Y9>a5;ihYwDkU/' {jZ'ܣ\8T˾׾sms Y6X60m5OH>dmͭ{^xTܧ+Yg>(_[1*]|9γlm=ثw-J,g8kQ]٠,KO2wTܯͭUZGU{X6u o;#LGe&+KU nyr[M<2N[-a.F!v-}L_~cT<{h#]Ĺ|q洯6敼l!K&L͞\m}m}aʈs&]-ko_mVG7Ɗӹ^u̇yƚ\_w_W^\()/An|qlϰ@V>xx &`\@kwulfojXrm䍭uL[,+[cc훯yDko3ϳ5)kmqgmP1kHl ́7bg3sCsO}w}kRv+_鱭!ocחض^Cl\fy~rŁ3PqBj1'O ~ʯ#>r'4|4sqƈz&9Iy[3?as7lr#2W.O!y綘6C̓&նtmUpv[`s n:}9+[z1)vqY[ˑVk?'[rZOS}ѱrLnb9>+?"ƾObHb[cQ +3jlU|)yakX=VmӝvOPLnI7wnryyr־z7l90/ڜ峈ѩ[͙Y ^y 1gm=u9emM12V?lֲLhMC#O7|R9m (9ϵ~m?kb6kZߵE7ܵ#0NM_ܰC+]5v=W-r)c;8K^ŵĤ|ޔ=^,ސX\8lKW.yxra#}qG.'Z[)f.^]ڥˣYnrX>yjrM\{LLP;"[Bl'[y;Nyܾb7vmwV +˻p8[>`aa8`[`Vpv/zm?m4NW7ny寙 oGhܶ1Gqlle;4'X ڱ)Wu\|Wx.,_1͒^-+Hf,g;}ָ_Z~N{wMyX}tuhg+Ķ-_"*œSuV>ʪ#*gsz[U'k=[۵Yz~;x/kt$ R}Y /ּ5#hl7gXϞ9[7g嗽kɧg&)27BޜO52 ͮ|O* 7U~ޚgt.KroGZm}*xXtul.pnokbk\k6εvm|{ /Vu_\Nܭ5]8{䕃P?h{Xs<]ckmj[3p5|YDL8yhƴN/V[h?pb$IY}ˉ؎6}Ɨg!y/cN>+N]wI.%N[5?Ł~ c?9;E511Ɋ{s{흭>˵3l Wma;,Мxw|!(w}h|펁W|u{q}s9;A`H֐6⏮j~S̱ #hޮ}[ds}Itȝi\H+ƣG?.oy'spcZnkl3&c"lY}css4F>ΟO\:3*gse"4/-Ox.Ňy|ԇoUPw82xsq?}}ߵw E|BD+٤^|ͫN5W7Gg^>֮!~\?9ѷ&Cg';S)ȽQ96蕽~զ9~}͹m9 [Mr%^,ۜEy@,G󅵉:!y"n|^A듬gN&]#>_(WljM/Vfϒnm9B`h觽8W/v^rVЛ[뛗 ʘ_Ms>}%BX@Kkm-od[o(G=.Xoc}QyrUƌ%)ڱ} Ym]m'^?.Pޟ A"Fy {_7/8v +skķ4iќQMWN&[ƻC6ν9>.kߘOy3vý_X潺s W,ey9VN)v(I1 -)(Y)6ؖ]iiu|:uM8m5}LοlNp~kzqU30 0~=_dezvcqcJbM.pMװ[{:C⥎l)lb9"mͯM8պ^^m+'w>>!Wߤ?|A5'$g:69;׶}n{Ͻ򶺏^N[_`+|a=^1_gcm9?=`76Oجm,FSVn4W6[+fÕNOfnzW^6WFdzkmkúYG8@>A9[fl_#~y=_ƺG+_-Dڸ~>uG}1@)_?oNCmN02$'\{M#>1^{"{*/齗smy%rdQTvmNy/%nsʖWl}_&{}AɃsj[w}X6gN+!ɼc#k+Uyk8XK÷W_uCZ_kgcS;i61b招s=#J4?el^=}(g mqOu͋k_}xɛ{i[.cW ʨ>~4GF;e:3Wk]xS}S[{}Zc%qn11'~ܓc_!V'gp]:x3xz޺@_ AbҚ)q{wFr}Y_1 m1lsoWvU~({_r+zֿI|E*e|%[K/4,uk/jr 6'&e ? z^[,tK+5[3TR~PrB\39wOaۉ{_~z{gm8u"Oup6[sʡ?wd5c7mWNP0@+Z+Y2HzCYoBew^xbNWo>Na>Kgjl<(XV;"ۺRmm|l$KmX ֳ߮nfV9Xsdf#qͅOuj[_7i{{V;!+=qo;낄\n5 ]m??~臾|npk1V(l<=R}&9qvzDuʓ6w;r7+vfa{ѴW[/w盻db7m{Nyerl,S_ץS^{>9%&ק'wYO^ЧؘŇ4gc[N/1>$ص[&__]լE<^Mo_lm@Կw;>Wԧ3SY[O8<kH59 x +bmMxܸ^'^_sqخ:9qX{9aĉ np;6nvmt֗uѕ{i^.fkCxOTlPaa}sy \oeZ>#3i<7֑ g@m +} +i.&+6WGJb^鵿Z9ir϶}%ꑾWOۏe1u93K/A$ToryhgLb~[o*Ěb@uKG4\/ۻw:)v\r<^9!G4λqΠ|ljc "w`8]3e=eko?r䍫.W^-#z>>ꈍ7;)_7n朓~{iϪeR]" cߋ8>ϳ;_4A9rnO\Ց鋏o o@kGkXCr9[g1m]d9xr}oo?sQV>}Sمs%+ԫok58\L/o7(gwW/5qb]XcY'wژ?)|JwoM{ݰ'ūvl[1oyy_~-?];fSzٙ_m/ _ϡ"6EmCm*O{%Kϵ>ekl1>9Ke\\svUOߣ:OwuWOyqp8.8m;_ۿ{pp}~k|uc5iij_Ϻ'^vW¼ǭ]}XpPםfXwyCP%mŏr3Ð8Kjl;.'WE0>zSn5.}c?ɋƒzXYY92&U$m9ٴ?^q>8c+o:_Slg,&~lj;G]sS?{Ε/*wcToozS-?'gb阍/(/1nKk{rG#1-!z_q5e۵oc~k 0'8<+_(uK+Z;qxL rv>6_7vܤ}YtK|oO'v|d#߻~G>_׆]ލs(ؘ]J~gdq ֎-v,Bok?^2c5)+ԵrS u1M_Y\ָʧ(&`_ 91gg=XZH5՟7ԫ_m<~ce:ג.1 JfgsܚoU{Nꋕr:&gxpAk#ܱqC9L/Rrw |guFb=sGfm)wW~u~km~Cr^2V?6iwMVgmܣc/.7mv*{wm +ms7oV:'|7k˙=_q QEY{uQׇelrWl;ۡ_^Ƹ^C>Z\W(ڸXcmxm*cAƖ̑Y] }mz t?n(FnI1|^Z|͵m~jwO`U[n~}[zrK5r[ǥr~Z{s]gDb|ْ鸶Mc=gfk囲CxqWnڤq<3} e ?[qa晬n\*ƔMcRLpksfcqecdq=P>0I_{{/hcfw%o?|pqpFWVw;vLn+;;_5ʓ1oZ־Xbw^uO~]{J0yn<jW{ƚC˿j<;H^6qp6Ws<}lo-_kwkw|>oɎi$wHgǼpe_m`lLޒ?t]{ -?[xbC=T^kw+{6VJ5c(?^5s}|6&W\VLWS6?xcE뫮>&']^o5.kl?濮W>~挿/b&m_d[rUmf7VcLcB󔫖?1Ak[C< _* pqtyq;nj^p~{Xa~ | + <ۿ{e}tvc8RNr1S-&qYlX}?cOi% 9:-Qۈc4=K} Xn}_r}.(Rs%C_;2"~Zn'zgk uWb9MowmRIP',c1Y_3h,Mg|TN}1sc@w1;3^^mVH5G>:UyVvbr66h\XvVr~o wM$g9UT?hjkCTp6tpz8|7Z'0dcC{%wm,|vhSw m\@?K+f){ҿ::,mXjuFv:vlhMnj}{fǞ=^L۱n P'@u_UVc/ޣcHEeir^7Aeݷ_[^#kQ{!œ$^tN>CB$n=\3\fy[ p[7x5!.YS7^ɱbxb57lMIv㱹^,mkaJ7Zg: {Z4[>zK{ژU)k}gҗJW;U;)VҸ߽xb]r:7~b bs?^O@ܬwɞk<]|;1ž{O9{8'0_;!ϱu4')Z<|W-w[rQ$ /S|cp.Gn3]76Zy;};ov=q'(}FeurabHI4g)s WgOf'8I[~xm/UCnRc{?YLA Oy):_ϸ`YNe9>q?r/m-] ܣj/?P^X]` ')a[sZ*.~}WEwGir*wD10lɵ`>K/fW{u͓ƂKIKC*V,1^N KV//LNgcE@-d w}ťE˛y]˦jwU 'cѦ}GOS+ O?Ŗol1nΕ^yװdH=}Kce{0>FKvO{Ս]p2#?|ᕏ==|c'_R,<ﶰpj~:8kl-] wqo'1\q*1e9Qu1+y X~@y M p n,A [u_Y {׵_X9,'H^ayJvKO8llAHo.C|vcAUvMSNU>˗s[lpd>T>5'mv Po{ncM}[I3R^k[䒋iY[vm]Ÿl~kvmI$yb|o\64_Rx]7e4=佘[!g OG0^~wI5U,!}'mnyH ڤ6_;T,)˘yMx]k3\Ig>uקk8QY@^m"Ybr3vO&ϧTwmeשאOծ^[̧qR.w7(_ұb#[b㮏[D_}9xX~}@rϮtû1~[i~EZy)]q9[8'&ڙ=Km#\^ڳ8@ʒ}mOb[/_cL +;b9w榹ɃPinQ[^BO8oxZxrJ`n㭹dl/?R0%#--vgNgp3Y\8fddzQdb (~yl~^bϿ<`sϪXo7#ޜv]]1;1@ɛ#YsC٤<K1M(3 -'SllƢ9yz[˸^/>n|/>y]bS/\0?:Ήq7&)͵0mkl z׏YyS؉Q> -۫~41X}Gі0^ʓr>hi{[\;RwXFLSw^9rY]xqDۺeW Bkt~t}ߋ8v?=^ؽ1MmcǏ[h'8s߳4Q]M8h;kIְxƥӥTJW_[^s\V}0cta~rk9Gh1|az5{P9fLTu%sȌLߜQ!_USCj9_ƶyP+Xy%[k8PmۘYiߘ8cz9@^N+OF[%-WR_d$-|˿T_96,Fw>u:{{CWK sSCN''W}o{13o[?Ost'xqŧ&÷xk-&=Yø=,*lK98&ˌڧmk)-"okSu!Y]~=OnEݰ{w3p؟u ΗϺw}ҧ6ƘHcW[}_c}j7iϢͨS)̳t՗?e[ךh[s_xeEfbwqWM#ecjB7kN6jz9Q[W&đ+nA/CV:/p웯qKMT_욇Er˗_Iqys+6FyCy1|;gsS47@1n7]kr\[5AD8OœE6͸khk5*kGw4wF x1s(nj+w۵6w]`>M|okdnL|s'xHgVKkvMy a֬?ݥNS#~λ&ʆ搧l'7?ķ>@ִbh#iGkٛ Mv/s%i7Yn}f1(1}DeN ?IG'^\~GH^i[&Zrk}/ooK&k1ʷo}m|ySǹW=V&sh]LyN߻e\[_#ym$~o/_M}WvM6Ojc}{񋎙P\O]ԽLĺi#Iϴ~eԕqlʇjTǬG[\9)_#Z֧ /6\m*{6;__.p֚j3vX_ wkT/[,ΐ~4֜bNr^G8r侫[_mY+{wl[XvS}a8\|h?ibVk'm|eZ,7] yf/ksȮϱ_S6O-bZuCa@񒝗G^«β['^Ѹf2WJ&pJ[\6'fMwQhnxֽ gs-֨}I|9.{썍;:>Mrn k,~cc^}@[7G%5WF &Rx\O|@y)yʛp95/UW@Z9ؚYXx]zmň=-YI_sXހxcF-hzg]͘m\bPq`}/Veޫe6JKUq,o͟xgkOllpb|ya$u 5>^7]'ΓEr2yU笮ȶ*F6uXʮыS藬S_mr6P vԵ 6iny#$Vy<[obs9: -*=tA?sek9gomΖoUU7֒^qnߪCfqxW7^Χi̇dCx_o؎~TWr%.'/PAMonΣVdD>)c++q֗ 72k濝s:gцf<ͭj86Vʃ|Rպ6뽍mNss1P?5OVbR3vx`zq8RaWikv>m8a<ݭkQۿtƶq>mZ_S}Q-wp˱3Kϒ3ni^txMGߧ|'FyvĔ^ms~҅q[o#P <fļ?TLc4jg@ld/fjmu7:&0Wվ˷/ͮ _2'|}/ߖ?]5?_b}zY/ZYq]_ /O PVXL gWg+^1\Y}"GIb~؝9¯pxqhn^.}S.Pc9X٫gV\ֹދC6WM;Xw}ZCґqY-yZcA1yx|99Lȥ3:6|+: y΅䦵mV~97qﵷ_lj1=߽Gy&Ij6_s_9YomKO/}(,u8=WϋwUevu&B~1 K{fl˧򹭃 I |ac\+yr.-m>뛉;gO{;/G[`}|=}O>}k}P9ca5#k˨Oʊyy\<ׯ  q{xzK<1O'c̃9M=|^C-&ivg7#+[<69ܶ~bc ɾSuw$<_|)[8Z24^^-8zrvcQe\L39Sk6Qk9qQLF}|%X4͝4*1.?l* 0v8ٲlm8]pw}^m{qϿwWaRG͑gر/)Db߾vCY5*Ӽ_}_1p+f1] L`:shs0y;G[ߑ}Q<4<ssEW2ܵy')Lߑ+Myеq_:+aɎ|emX`\*1-q0C^Bҫb~7M1?$>M9ѵ?.(c~3w 9m.%wseErʖO{nkV;+XYoh^ΈĎޥ9=].ox^H/l3;qmn0baWk\|+.6s~zv9c*ObBo1ٱ|]:9}$XJ&|q",' KK*~Iya덄$/7a|zbrn4Ă699<o4+7_X_\ lZqA/M y/1 +cϋ'z/)m׳jqZƐ3bqm?lp10/4/b?Ò,Q{Nvn]MXm;?6lcl5t _?񷼷l[㵎q0\yxq  u8ǬZZsLt޶Q>v`:X::nŴka˓~r7 $Ϝ's~b{;ғrKi藊 b>0y%ox ^λrf2Uzzq~k 7/+cŞwYSƃpv@f'cn0L︀Ulݞ|紞rw_vi~i?3 wPZ1-)4,ڜٜkc<8 6}7lWly>X>CW]qa-g?wx6{_;c.Ertƙ'|忩oWY_ĺWfF›?seyQJ~[|a9_- J~g:1ݾ1@b]lqn'"'vk~Pm{9&;gmk}S^C sJ/:;XZ0ژn]%]f1¶ A u;-箸`rrQfm|C\n.ULo\0;xv5ߣ8A +,foZTIpڍ)³=‚̋/]}[e51 SDYc@~N"-&,aEkHkiCF^9Kluc;︾6q>d8E ۼ~7hN8뺵/nދ1=G&e>:>[勥nQ?78(rXj5ͅ7cS񾰢p>/Qޛ+7?O%7bO9P,wA~B#N5S|~/v&6+uLs{8CqzL~|FndmUڮkϮ͙2ƱL~%3^;}u~bb鋍Kn82l1@nǜZuOw8\!> l,UZڥƬ܌l{_ .i9/rF{}ߵ^ȝޱp.٘LX +YUB-~Ca/n>sc#rdIC6g.N ?Pѵ啓3C.O5>m\q^N_6~w޵isxׅ Sd_\mP F1^*w}cXŬ0C}kC^/^v{^/kؖ|=1<}_W?3uS2lU bYmruD_:_,QyϸISGcv/M'dy*k,w8i}v盧w9^ڶ/p@™O{sl:#;΍fvΏVjs7_ /賹Y,ur\}כ߿,%9]XMtd|0@}_Tc|WnkF|]ޒk|I1ĭ^&>qx\SʷMb​ Oӑ\|dX[?f#1xɋ;֦[s#ݱ^mDycև~cP[Lz{/G|_UT\[n>\e[~ANU[-(X^~1:9~O~6]D;)ٿc٬2/{\rˑ4^>]g-$9Yo=wyMNnK|Qs@\&Swt??5|{8eFK/Y!<۷[or;ԯPmzl}Fe1x氧ڌE.7ik'YkkY֎qCfw/ -c;_wV=a/9$݄ETa>9{ jK.Ws|Ŧ;^<@c#wq. -_x/ϠՋy+Tl.Ooy|-!7o<0%rsKb勃^ß.ܸ^ܜ'U MqX_k,Znmle-?O~Y+"{6xĀloߙ|/oix؏y')A N}V~g#cen 'yWޠAN+GJߺ>C߸Ӌ#9[ݿyOn G.wa9IG6l~[4)koT|MdǔA+skoIɖR^lKŖ|?}}W;7n /y1𿍩3_|l֟ +sSLOWǽ[j ·8w;25^_|xRT[&K`uon@weH>e'6ǛS`;ch {3^o…Z[_?-'xo3m,5ٚ,-NnV?ayw@\%͕l<ݯ߉ݘ3N[p^RmDo͑M.$k93_bZ)Ns`3ޱ]w=cVzh/KߴXxs]W;0i#cy'FI&;am!y_Qv<ofWΟQoܜy{ { ?9 i+x_^9ya +˵sa1lyjwng@ H"D>sy3=kuTʼ_򳺮Mz NfK7֭ͭ\x5{k ýbR_./±./0=+#|7wӧnwNr1uVo&r 'yow^\rt_va{n_{fhu4} +5l-;{1_qnlwj[^_\rW{@={rd%^Q$F5]78 6-JOSdP}?5w#{Ⴏ5Czw{5}(旮 Gܚw<>Q˽:b3ځcm:,6>."zNUCk;oNd8Ϻ OXk@q=k3Ϝ(c${;wQ:fRb~E]] w۝wTa ^aV*򟵏:a}dkv,%8ww[_;v:(y<{]qy/ ~r{2Zֳv:5쮍iK绻Uvۮ]ھ"^XG9yr cs<4&;v}i;ծ٬IͼclB׸s*SWn藈_[YcTgۢ_LRlAɟH\e9c\;箹cuTO^|tzZ@~gCs9~ȫ(RoLW_$Y"\^_cX. e%#"lbm,61tvػqP䍾ayw{ƜDr3ӹ^K^CKlކ+Fc%/?טEE6oEa]]ǯtޝsoIuӶu%˓&W:鍛8b&:}yvA,16(/b}Mé,gfs+z-ƪuL'ê__2)뚿-u 7f']r\eyc|Jy~=9V_|Jۼm#q덗jWMK ֞_~+X65J\W,4IfԾ1%w k62+~4Tf*OvM1}H|ѵւ5|ygޯ/<<;0{7t'ǧV1wΑ V;=w`8N3kk ro0;7O{oS;ԣɂũ!}ծ|&qۻ w?kdcj2®ܮͮ~bxj۵AXmQx<0^7@y}ᆷ'}f:)UmXsz wLwY]U+i[~b'w=[XxvrS/6xリ7ak~T߻ɫbFFg9EbʑN^3*bOr\΍j?{_qh?3mq$8{XNZZo{tZ!{^}c[|^o[ǚ&]FyOc_61nBs͗ڼdrgxnΞmѽi2`/.^׶yg +k dzޟM[K>=_}FkO}urx7ņ?b DE?H6k)>5޴5'z>sڔqw7d~N1WcI +}Eel}hwS{0Cm~l; /W}mHd||U71Maa-~b\'_,/Ps[^৚y0Ž{ƽR9(#so pm<ٗʶ3۱n^wMyyXTW%)|g_asD9-N{%;=\MNSder= #jPL9 {6~5H_&GgWe=$WC6ZSGn6vv>Kwk59Ƒ^SɜbMub7Zr&6rEĈWXVya˥*&,.ljq/?pu_|8l16#{v>Ik휍W~v\_.׾U&WWڜlh{4 +3EcX<ⵯP>[s:aezdcV5iZ9D1q*r.qحbξSv|dŽsy#FY_){dȍ[XzJ+#/}Uu';;1ͼ]4krk?[[Se>,:{MscoRq/ erȑ#)k+% _XߦoZㅏzd@뢘[r\܂̓429z{im0Wݻܳ?n*|r~ 7;ƍ+yH7s 6r1O2`iWxm΍w]ȑ^b%^9|Tn:ߕ5cmaB:6<:#>[J=w7?U-Α k#,'u_vbmo8ܱ-v 8Trn? hkoXRSܳrP]8k0ۿ#|Tiwo'QqGŞ] \UdU|3Nf9\Wk1[y{y?,as>?U/LO_g8}nr8w+\h<0ja(w=]ǹ,opX䭉_6,\0Σ/ءS}5Os}ol$7.oX9@ŽCG凉jA,GCl0)ˇ\b>r,͎fR]N__~ײ5}[PU#/\v->mk7Wޠ*],Bا5? +w(FkD~]W5}UOWw{=OvrEʼl9Ki`/ٸ[.gUm+G$.>py/xXl.';ٺ%Nn|o}1?YN={>sQ0o4vkNuͭ4Ox[rx娯^_R9a}QOvw;xCfٲTt9r`'X]%/^^"XMlydr†ZBⷅʵvYm][kY|>-~kCٝמ 1)}?'Fډ +{O/~mԷ]m7>.7Nm搾r⌯ܓ1tyW՟Z/kUyO9⹋g.l'0|;~X+Ϙ׵Ocr by/b~K|19U576VYnk[-+~6ua|8ھϨ϶n>;~/wn~&mmvqxvf{k,v|w?[Pqi=}DƱ_^ϝkk}g}޵d%Qq|bɏZ)֜ߵ^`4;.idssy 7ܹYy8OuR1F?cu7^xs{rr7V?]|PG}8v}FIG_0vU,>]srtc4lKkm^_/ޜ9/L.?cꁳ⟮\~z#> +UjWgPF5Ǽk䟅{\ҋX8MŁ37Ӷ9ۘN_a?7.<=\璽o/m_y/>sy +btJ#6Ҭ&!\0fRf`Mg{X>67.: SMV6fCMgOF/n_5?‹gc1Bd1rMvDZ^{&R9J}bc]ge_|)WC 1eys ke{pٺkd1G}=s7ݏ%Yg{q|n(c@gO'KS)^%~'fH#giuɷc!g׺'=j獹0gaVs-1۵\lY{yu7u0[/5/gbqru/] 3/K>Xp>ګ$G:u.C8׾]d11 h|X+'/v6;׻ 5g-vX{Wʳ5hl79W/((6\aAcb?=Z;癿(ExxLo-ůwvԸM m9њWarmM].go9ߘ7Z>Jװz5q>qgپx۟!=wqMۺ>$qڦsˆxNšwϼ~fLl,\M$tzUrӧݱxUOqGbLit$_~Y]]ܱÛu8_9/ ZbwhmMB_v`LqY]ƟTFv۵8[HZZ?9a]׹5}7jxfm|5эA9P1'ս[5׆{Jar٬OG/`}]9޻ /~Sx+~.ZXqdðØ ݻ^=g\ٵH k.XZssDW߳Zԭ%50'9|یm=7r_k_sadbL8ؘ9כ],P2Vu<寊e.Ӌ ,ZCj= +jY%q2N)?~6\ʫ[.|y(V<@9b8Y1P1k7b{}r1ϪIR|g\1S[u_rAdnvwlk9#on[;l֗kb^ez9jc̴E3֜͡v5&uM'I{az&E,''ՠȥuߜk w}jbįӠs5a}ϗچSӧ}u׹uo_R|C;?`ڮbd7-Vvttـ­MܴzGs\9;~^?uX:w9XL^a~Hr Nbn3jv;X9o^Wv8߈asY\rcO봽0#U_/ƺxrg[߄]y]?Y9@*>\ܙFlvy8}p4R\ [{ڤ bb}[ OIB;\^A5߼:C.字˷71mA}ak5gS[ʯ+laT˟-#[yk?b3b~Zb~]mS!(Y~C';V뮹(_/wsk;,o|5wnaUִx8Zi]AK7nZpkqixwk P3zQ5khwgo}Wo'ﻸ׿w61v?\[^yϰۧv>Wߺ~Yk{/ظXL5CwⶋƠӐhܧ'[_`!^9K_oKcA͉xֶ iqkQ[xuMKkk"ߧ[͗sjsը4/o`N Zb,vO2^I|!Ls_7I_Y',}\Wg]4yYÆ]^ybr%qkq g\Z~lm;k6Gjnr6`.McIx8\;/MαiW_SWgSā^9d_i\aquuWzWK6߼J]7ϵRk9;B9_xOY춸iq]5+kD7.|M<Ǯ/ndŮݺ_ѹ+gϧs~ώOx+.H;8<5&.z}x^)7?\cQLP~`~Gv1$?oA/vږ/|9;gYr#a$?z6^ibY}3_ҦTGp}VkĤWuVsR-p_5]q8kAf#c(/XܚW|tku2_k\5=0|Ÿr ;|bGs5?6O&Vo7-3V\+q_ffqƊ&w(ay>8:q{m^5v\C}k~EǕ[RK@V۶\$r劸Y_u ^8xc[, umcM|eY]yڠ/ݻX}1yݫݴ@奚z6{m3>šSxq6,91[>~/mɳc=ܶz.1[Oz믤6m+957R;VS[-z@ך!&+}pt +X6[HStj];Gh=Ǵ {n߷i a _5&.:/rE}9#_jO{>rG.9r;Iz/}/;uaڼ[z-4?u2x=6yvn΋ ܾk~X#;nExglM"5D1Z-Iחjhl[;|\~mP\xjSqG>e׹]W_0;W%}t/x[Θqo99?5==[sz=؟~l]s?c.ka}W}{w[t6y.#o7~g;UL{8{)UvX?bϕOJZרik9x }w o>e9bu?lj/[ Iϲ=! cNxdS$VhRm̓sL1lq[3aB Y.I\IYD8_yOԜ5\yD|V1 j1͉n4]Sk?,kzOՔ*WE g/h;{7e֗'mƖŎSBrش+ƣ\A}*ǩ?&,x٥P)NHc,߯xw֖G[`v-޷Lti_bbpZc&nFTሮ5SqlxŮ^59ݦ'QwXX2 9Sf=7iݤWU驱]b8{ƫr^u$6.:-W_n[]W]Q>^xbNna|zKqCcչu_5g,_W{^6(i1?<Ndbyy.Gaֈ-^*,w<@sZ1a$&#˾~nul,ݲKy9Ūb]b&^w0>tey%n7?<~}74' qE~봩}r/uXMhOTT."lsҖ noVDvyd*rgڢj.5ܸ΁x3,H Eo:z۽g1ފY3 +#Rk,'P8K[+~W7ݩuhYkmNMөg1%j/{f۬Mغǟ>^}ɯ + +_9|ޫﰞbkP[ݜZZVXDkd}6 ݞʮ1VXY_v\[=Pc(g w~~X#5_kKɞ3 l"±8q_kHw XN9mlfy2l[HL,~Pv%ꅵ;luX~Җ_냬pu"\m;}H/,#{bH\s8W(9^ZՒ|r .(|b⍮a|ҷN{ou<]pu~&ҵDU52c-Y_^Tm1ɫv 1-5b{ٟ`kvv-6a\(''V^hǻIō_u-6b/P 792,_/:q܈.jlƸ9`onW:_ ̏zzm<5_V;l-ecPqVX+?{痍ą1ʯxc!z#]kj[|q{W u>l-%aǠrQO?5˼~~cǿ.‘W͋Yo?V|pA} ΂yﵷUhyˋ-e.@*X7M^*,=5¶&a5-_8<՛'Y^7ڻ`׶w8k֙ k ZkI-Q[TP^Am=rX:avrX`q~/k)gxyB.G>X΢Ftaͅwuɽtz>^_muµ}q$xz} ϴ;r'{4l8r&>983Z@{ȫ4GxXi^>'ֵX`=9۫)OP`\l]sn<AcbaVbmŃAbV;O]%1@Ƹ9%]X!k0v2}>vQKC\s59j)VKtW3:zӷtQxnA qAy~r'{c n/JSq=ڤs}__vu +qw,jD6l?i}_qVg66{xg6(8q:{ܯ8j\/]/&ymIrB#)7m-Kv$`[۲ք?̶n^n=jp4wd3"f[SO^+ƻ?|Nrb~/6k[,O\Q5}oL &qw*sr1q{`\[ʝr_{T5;}-W+CkGKs5ȟp{5_ 1WXPa-]ŷcӼ]KXeq=qq_Kl^\'(~`N}f[H"?1zub,Ŭy/7Gر/('컺SqsI-U\ưܻ:w=wֲ5:lvpljqϺj86ܚgC~[6<~wk]Ӿ\plD>Vuyu eoZ?1>~s{^붻jwĸ䮩^ m}}xʺwbjc3ظ랉./m&ٺ΋h_˛[ϝW׾k,G5~\:5ū1ӖͶծ{sm~]C^Z'ä慉ۈCq+^&v>*e_.OHJ-:3j ZQ[nVnbqgoxp˽G9{Ҡô9X'w팽6q@11=*V^S}>^֌X\x^܂=yhsQ\_ߺ/\Ly 1zaɟ'b֊|ێkkYe 8mi)u9 oxiGVc$.`.S).|qʭ#ŹJL9g}_ZxT@w{ׇo<!=1.f||?NZ?1W]+T,xr^u>w1_~dGk4'kRs8(_`bx8*]~uucԓdq?型I6/j~/GiN6LGkp;:L[c9‫CZ'gL]{k6?n/ghRiԍZT6l\͚ŢbG+fQ\q>&[Bau]~ƪj,OK#ZKpxa]vwܘ< J^G}ηSjm1yPO&峨9+ri8u/nmckM<0y+֞W~7jȌ«'|{=h:_^|"B/Y~!^5{}g{7ґjgȧw֙:}Y\uVOZ rNJӜݹ^[ˍ9V;l|XRq役Nف0@.־0Ún׭ܺ v;Z|9.c6%|M\bk?ν"\'@]QO>G|>Acڢ\Sk 7w,6^cjb^\A~/'.BLMuLŵM<gn쥑p:>Cdy]{>kk3))>[CPoʿ +1{c3qw=?LA]۵s+q]FbCƅU'bbNj1oξ㭾rٛrk X6p7SY 5{m:quj}}~w}id/fuYKy粆ֆS҇_,Em5-/m7uc7rK +uN@9sVbb|,.g1FFZ,al3Wv>ya84=un׋&?}uuv}4ϲ3ުȖ5\smӚ_C]mkvB-ꂋ kaț{f6nn,g(A]qp6g1yo[x. Z<8Hy0UnQ5ҙ;׏Ug.tQ]p^xlNԓ@zku{l]ZP}b\ d}!on(^qs}m!Ƿ|=ͷ~鑭>_k}9O#'u}ʽo͝6qecx_v\{xbɮ1ŮN>TIr~ktys cXL;?>βZ`]7q7 _sqWW|i?WSg\ֽ;NT oǟ`#/i;3_ֲgw}w a3n4_]?ݶFs}VwOjwbY+'tﯿg_v瓟]lq*t.-g}4S/%ӷ<~zkzn79Kx\;mn<"_D>jVj͊5[8Kn9 n5zPVɕ r{ӻsu6\}ool}.s9bv0/̿غXZA-puvZpwY5ԐdW%-z;3)9Rnͻa +}f8Askm儊YԞ]mbbAۗ˾|B^Z+|Qjȩv]~8Na_]bYh=ϻ~k&7hX~$:x[#]m:ِ)'kV8[Mj=Jbx3Z8i\'F~QҸ#"lacۏ7b׫78ŵ&լŹfͰ|}0[ަbT,>ۺw[\Yw޻I׺g1ELA-~xJ{^kKj.괼B>R9ԭ뛯o5+A#bNxulm[]|ŭvvq"y~ޫW ͋\ߠiڴ8T|Uj|wM,yyEfvz_"OY+0ڼ<|triz.ƯvhqPV}9<[sƟSW˽>>~Nozͽ97:,/˛?}UG<ѽy$~WsT6xxsbaTrxĤ67NiM-UݫG^0ΤŒu|,b[c׾y(@k傼\ִ[oub8bQm{_}W{|kX[_6ޕ[I\K_q[k1:obiݑG[煡]_[SJ-֯̆l>6c3\,D>cSC[T"޺ۋjC?7#z/mklMWXӠsonɭ]J!Tj!'ׯO4R +_i/xsysL/Žv8W.S4{O+癷[{~[Zگ}z`aaE[gkn*7zVߵON_|6h]m5~w8&Y=3I|k5 ֙\k̝|/ Yj = +݇mɑ P{ޛQߛOtM622##~~w?l{?o_z?=w}ks}Z̟3_?ԗ?e?~w/Ϸ|sc\vwmwk{a?]箫s'ğ_Oyw_m[6^[η>}wvcǺwwG}{}>Ou~cկꇍW?uqg߽X?yvc_7woomRt,91ǩu^k+sΣ׽w6ms~ޛٍl˵}Ƴ#{}?e7Ϟ{l?Os@6scu}ocqknk.~u/vc]_7^ߎ{=ge/Wȗ8ߢg} +_!}^1ҽ>mkl]}~Ll7';sݹkOs']oq~Q[9sc[w~^?|??gwcݖٱ;}G=j'ם~umu>>uy_C6nVwگڼucF}=;pGm-}x6vsC~[_3n3m߾<ڥ~6.l٦>gMC7G׼-@ߣgߖf\?ט0]XƄ=soAl_o6KW|;~O}ۿuqM}~|~׵ŵMh?Z{r{!q= ~r\}s/X_}kָ6pENshvϚ##;mU6%pbols1c{_?q>-+ֈx;Lj!4c4/6_Y,6m2ﰀ0"qVU! x`]ӝxHȝ]S`m-X= l=v>w^]Ox_6W07_>#{Ǻc޵]G8aw]}3'u*ηsu_6gw.x]ܫx7@iIi_bk0ζh.Ʒ^xuOk1Ws 8 m~Ϋ6lQ>=_O:>}W +bGXYXe{ϲ+g;~|ٖ~wll:&w}ָ |l`g\=v^W J}~VsDO Bobctpbӳ_ƭ6|öAvw=7f՗mU6N4 77 [̷sv?,/w6 [Ls0Ǝw}f󋡋Y˽p]ݶ1}~-OxF]Y]y}#ea;15Ta1bmxqbq)zٚ0Ō]1 fo\秖in򥜃z]1/ߔ_|X7wY޳qዳ:NP490~*;Vz-_̮1o޽3~\ʼnȝپ< +~7ֺ//x}ppEgg>_xc _= 8ʋ/~7G/^xprɧk,o8b(ڜ1;tݜ9{}ZE}.ܖx ^i͛g2}6g\s3xajc~y6>O+0r=OD1*uRv Ȋyvy:=8>>Υql63ֻ'?OL>&V[]٧ms;$.U+_74^˛j\w}P 5y~bx + lygSآ~|xd?Y߅3G"斟P1n^>1vcloo~xّlI~~1:u_`p~A;N!͡b9\? X~ bӎoM^ܖrmr ny2q7yɇ({1Rs6*P /bGR] ~c +bW||㗯m?rzj߭Y иvqimOj/}-~,޷؟jg;Gyb굓m8튶Ⱦj_r-W'jG݇sqr3^X|ܻlh~Zh>7}և2$_n9=/kg*k^\0Y۫&Ll˩piǹq=|Yq✪Mm[!;8krai[X_$ xoayŹ|xݏ؜5[>g5747_x9p1o+v>=|bpm}}~<ⰿ÷B=g6!hCnXx`~m~r[!1>'n_a?1̣ϮWٟn}jbYߋ_m?ZScs7WֻtƔޛ:V#aF{rŪבߤKb;v8[T\}77X,'cg&$<űŴ\S}NrmM/P?ں,swx3ܺ][z%UGuǛLXm|8~gP\&vbk7/~5ۡT3mXϋ V fk*k'^/nmX\-Ŏױ\)qbb'Ac(6:O`a0ߪkފyq9{3wnj1@e?i~;^WX}ؾ$W~j uyǗܿ0xAͽb,J;q~osDoXϋ!wﻻ7׏~7j)~-Ƕ}o=99Oܘrs[Olq{klcOmsjl:|-^xs>h0hN2$_k8|'ѳPΛ}b FW6`RK.-q|jFU[{}}'6?~Ƒ47߆ƋR#--il9/p{}1xaafuWyw^[5?ه[vZ ^|][ ԯoʿڏUVӲ6w vR]p{v{VTqbyS+X'P\>]Nz_[ow>'Pqy|sxDwk3m]uoݰyy+UX}.L]z~r;qa}<̯-m&_"Lq$ (vzv$6/.>}{5&ں#6>ť:e9ǵ}ZK/ּ>W\|)ުmknE׍z.\b=}姧ֳV~XU| +1Oذ/|G{/|v-N/noa^a];mmuO !yfۮb_6&-v)vS 7o?lLx\(WMqtn}nx]%941rV!sb=[+"֡k0(%n|g{^kҮ+9=M'ykW 8Xkq?}NƌZō-cvcm5kkō_{)Ȏ+YRO pO|jF8 o<͜Gs~|㷹^|D#wb2[F?emώSe[,3^,W~`+C`ဟj^| !i4G Ymг8.'ȸk +<ŵ&~\0p?紽[nR~C09߭w~ݳj$\6jnLb=R.϶8lNv,gѵY`Q OclXīg]wyAbo~ųC;lMZu.Ƹb_cqA'y_9 WzuG¶o;Bi]C}wsM4IwMt/L1F7ZӸG}!cicE茻,/8tcR}_0Nu! Y⽿ߪ_Jg_󉱻V|ylW.?kꃪ+k1|Sպ[r|yXjp7|uaˉq +{Dz>^_ܗ; []×o`-XlM{qverH_|G׋7*6ʿs *N\ⵕg%*ϖZi-glS_Wm}R|?"m]luwo'lnקxG|Wγ] yG/Ok[ +☭~Qؽ{!>9ۗrt.5^.X}8GTx|ulu}Z!0؏Z_*֤ym[ޞߛ_lNqiI{5&ٿ'n~觽Ty;Oo~| 4g6Oovѷ,/ 'O8RmXkjP^U{6J>64_\lRkmv\Lŵ7I,OB֘!O P ^U5x9z uZa7ޱ4qbu=s 2\Ockx?q8t^|c5MVr. 3[?|knP+џt4,IBݔ&nWN7cZaB>"^⅋QmܷJn ~_+> CKv?zrgH~7_ZIb]s1|ec_ȅ7˗/iUqC!$v*V8^ZOh\JPgj.ϸLZ85rsWZsڮ4ۗu nm!?P͡)bz[l1@׷íwo[7+vd]kLx.&1]ZΟq~+횿 +;rubD"_//ιٹ+|ucmV.<NjzǸz j.$-,}Ż2i䦗tuOw lW^byQ{Ks [ʯ5wӛ^́嗯fN\1Z CkNH{,o[vWyNZb4*sq=[alms*k-)-&y_k sx‍~af3sS@l9|套Ӗdg؄뺈{yxvxAz_?H|ְۧ4FN_֞&ro 7asZjqklU3bڕc0n_~ v{Kcs7~o{=櫦\NMV9_/*RmWn;N?zra|ܺm۪>we)68czx譕l_)'9^x.'&v[puy_26,f'v F~1ܘu%/NoXmXKU5/<{ų=ķ[,텧;1I9_{mQ9ZScj{-˭5֨vm⋞ËStP^>rEUҶ獃/&Z_߽ꨖ*jqaw͌SߜHx9k{ePsIm;WIe]?j}.[î>zuRFw\]s~}lo[ݾygS+[1'2֖ϯ#7A?KYSΓz爷7ۨ`J}?yǫ&q}En OW55fOχ/c"VnE8RmO ]oP;# pz^yH`s7X l~bk)7/T6O|-ع9:|W.k{[O%Nk^pn-6q`c\翛/><b"5:6nW?3 JQ>D1aX'/8<,U+ϯϪM73/K#[Q|)(> x? sgt~ֽ^cb4Ţvot6Yt`1j0ŀvXm}췘m#cϲ۪?W7 U^r GĵQDqMkŶk[\9˩őH ڴcuAk(z;TpڝbEq@{|ۭ Ro ݦVɽo}__VqY =߮h<ݵծd:}N [ͩˣO)PM]Nl_ҏPCqֺP{{/>5Lb~޹1z̭1+|~k5ŋG4XN^ء۷~gc{m\Lvy]I>jjO1MWnh9?=YoZޚW~Ÿt9 ym}\S{?DZ8[63kO5+o Wubgb_}6Otzp1A1h]ssλ}/f.<xƵ}1v1_ѱ--_mr/U.|sdaw̕|O%6zׇxőr|n)NVoO# zˏhNpbܗk"wY.L0~`|urGf:o5\$H 7//u4ử|wv6vF~H,a`i=8E1 'q=Fs-]6kGV nmx`>Aq)_V^;!:.fgluͬ^0]m3U#n~1LXr<(_>z1Wm=pvR0 ڢlQk-o +z&q>j ߗur7Zbɥ[nB[g^M9lk46#GUO|:e9 {M{ɟ뵘c[(>2[(q7 ZM?oCoy?a/.Xh{_/{qOJuo|'f Feo]KxVsFtbȎZ~ӎ#oNc)ƷێoU_-v/Wڠ]-kdjY4ϧ/q^-(d9Qy6/ċKqV뎡Sk Kgr\^Ug6ϹuƟ0|5c]gyBr=c}w-/ܯg֓z޷{_{uN~_[x+ks\f-X//dT2͹?棩!bn|߈ Z ougkyޜߨkjqcX.Y[i?<>yRˇ+iS+ː,VF'Vpދ5&r,69<[V5 m|aSbv~r^_9r}4_Z\욟r ;tTt4ius~~s غuﺗyFbv:gi[l⃇}W Gp?j%;kىω!.PM=T9}6;bЫ/uˏ_߼rr]oW|5{]Y~Y9s ~<.5# ͑K-XGx1g>q䍼tQg/4fŴsޚV{ڀp gŽ/}I.\(o%y_~G˯{}oKzP[k"Kqxqh}qߞ=:;?\`p\䎯8|9Drm9nk|Wb}_.論!pYĨ>u۬g>hcRSo7aQnZ^7km5փ+k~,Wgm+6ں7~j'R5j;5I(P|Mܰz1]wݺi]hqHϷqiFꜻ>q71O!w*[9͓l+qo]Bp m~q9w`qԯ5o1ٞWKf낤e"_~}?kw^oyڝ2fu/I˴/QR^]1uC7jX/7]5YnnΰŶ~l͟r.L,'h8z k{a!.o~uz +qmK_-xXV}?=kOZ\?.LA>=kRg_I16ryIelygًSqqZOwv?QwK7GpVэRǯ4_m OoRMuc|U5kӽMo^NnmO[ /zy/p)JkJ3;.wy/{Q\]lIf9 w @ͷ`S)9=˯5)pe1ٹj6䐾5|Ƿ~|lMS5Gs+uqKm}ri +}䇈S6W==>p>txg +\(Og\bsPb9;'ʱ__ϼz}s['$+oovL &t7iËk_Sǚ sP{-䭻/|کv=ܲFq8+7'.O]  y~[[xZ,,0kT3֢߱9cy-.N`y7FewZW5}Z&Hxcs36fi^ֳ. +#(Ͽ~=SrZ?!hs:w]"ۯ,~!z2Tox1.g.{y{b}Noͫ]Gcq5š{y}jo<1_^]{?{8Xg.+`9biָ3WPywmok0㴜[MbHXWSImbXjm2VÝ])N\ܠl/c,'B]aܬoR);mJW?IQ9b&z g.a-q^ΝkOzaaF94yxͫrbljY㻯5.?|b,Gw8jl0_z| klðl#+2' 0.U\ ,?:ݯ_9lh>ܦTc'!\+N'vۜ79r͉Y"Ku?zY?r^Wl°^\W?Fk' kؚmy}ܻدu7s}l}|ؓgA.j5ſwiqK1esgyb-/Rx}ťT'k56c5Kp@~ "ZXc,APsҽwz^TgcNtߗePmg̥4^|1&_;+`/l]z1@:rlj fg[;uP1ʵ?7~tǻ u~?idҷpݳ_r>́\aNS~üKs\; ӟ]zx>]~~ԒZ ym͉y&xeYrQ&_9{ZWcӊZ2lR,^k&Y?ژغ`9JW|a1jz^7.V|p0k튋 ~ΚU6#VB7Z9$F.Rj8qYk&u~[WӺ+ں)p/P5WE՘+l}uhoja_]|l̎Ɲg1Y^uk(OњE1,4G(+pܛ.g?ۇ߬9j__DNm5/pqj\[\000BZj5C19tOmq|X7?ѲU{y}Wv_˯?8֘_K/Cͨ~~&uӔ<|aAX)Ʊr1 eTb|:_rqռZ-_呉q*}=wg+rmëS5g1妮6+ M苏1]P0l5V:ʻ8h9.ܘk>mL&Fx[')(95YX#s4k|rc'|"Kϧ3L'}ɥ/hL [GnFV]棻ٍ{: @L0scj7c h=<Ί[_掱qic1N}_·>xYTQ ln޻jwa ֻ.N5!sޗ}Y/|kvk o?Osc5-v^Ns/՝xkZ_5`,vZ9#(&P<= -ߴefЛ_'2z^C1W^v5 +WFL5 ^\]A3qe{ŭߊc_~p 7_~/{zwԽy||{W2׫~V1Jm+\M9uIy?/׷ GGc #ȯsjBT{5vWq㶾q7f$r9o׷̆%ꏭͿ߮SNX.sok6n{7xfѵ&ԔZ ŷzV[WH_//Xw^n9|1gmXCqq/_e_xѧ}XM>sگ6|cS}KM껧vaهrՐ?mdi5^mC>sHshsV6Ygs˫'WsU:ښ^?QSokJÉYkk?xWO(xbHF[WP vL'Un̮}`om>je벝W]<v{V!sӉw뀨e}\EwD}ʃ۶|\G,&uQyoֳǴXskW{ǯ}&uZq[->'֞5YG~l?FݵjʈY7/S?bmꎩ?}bw. wmr}u8xO'/\*FՇW/was-,g/Y&[Wzes ]Zwar2i5⣯'^b#Vk+qov]ua1^6BBy//}io,Y<>{k'VǥZb~ϲk7ac)A +v|8GiMq5#Ox?T^>ZᇭxŝC K5ִ w_wrAͯxdw] pxo{|{8r4ǰ:k[w'|ש3.1\.zy{M^1`~ة/fgڡvi~4W^[(Q+)ocW1&~?]6}k)XMbgv^f~[\^XU|&G<{u>fTzaa!6.wgTokv65;v[`XC񪇩VA[dM_1h]LJ-N_o|4k~ͅ,Woyt/\dcvu[Na4~ְX_qӱWwO۷`N,C.^}>qn=V,L~3w{N9(r¡M_'ɘҹ4Օ[w%2] jZoW=rm?%Q-.1@Tq}u՚i,ȗj\o[.W^[x»Z۸Vk[ϼxZ u!gOOМ]sUGaq-'@o{MnTkTLr҇xݸF8]>/߽8;,1xyvr3"g~u0g՛p^u -u>Zݟ}}罴7~i/bَ8rNwmym}YO {/N'_uվN񋜆ǯz_/Nu[c&6N[o9C ,߷kgZrM_5q<4.is5K\1{g|Ŵ^N5DjV7X`6?ݐ/U~[\ږ]M=\z]o.'ҵl&ݵÖ9⴮Q!w,ۙ >zI0wPCI?)XyR0[#~ڜO|ܿbϫk\~Ix%7ڛo] 3޵>-)wpfGwv{eLιsv\=}M ~mmr/eĔ]Na!TnPp8ׇv ?|Q'_WcuNg]){z\kq)sq}5nc|҉v/?i8&KT땼ÉʳoV8lk7bw]zbA;im8`ŧv[+k.VkiCQ:X\Z^&WU}߶Xֶk[ߐ?ՠXgfugp?~"/-_-P]byqoc w sN1mf_9fZ dڲw{(6y +(-|>'6]w5/=1 ̟g`V>ߚ/gYS͟xW#f@ {W\IG]&a؞5ⶵ~aa~7F[+ol޸lƢ(/T{gWbo8yꀭG4u[+k9(5t}jO:ͫjC9oqc;W3cbfq}59JƂ^8|}ͧ1?:soU+GPk}?}*6sp])lZ]cQ p{.Pls{kMT3-NkecytݶUi'-Ď#:XkiUsA>ֿsa͝쾏Ggآgչob₫+/X8ӣZ͵. ՞ꞨK"p}R7_k8WG1uxפFpyb[^k5a4ٗun=\\xz.6PuUϮ_Ƨӵ}vנ^FT?//kc_t7sLjh-c3-79LkwܽGsc0|ػK#g3f}uq6?.:]OKKkBqAv}I󆾻RVc>E @^ך|)N'jtYS5|b]b(mE뚹aDQ-pOg=Y2R+ֶS+Ne  $վ<[cl]ux`5ydQQ]X5#LeqMk?AE\SZ/6N桭oo5֍NV_.I 0qw[y֛MLUeϓ%?s&֗棸nX7禎fcuٳo}&X-[DVk77Vߗʉ4:-ͿmM\U ܁nmtǺT|r 4dz(7x2]+/[ >.dk30G>C9yh[S]6?\u؎pRᜳU\D$yNQ~;Y-gBW5qbOj]{1pqp:io釅w643nC9!ɯ&1<|30;gGjZ>;!!|5~eZ뭫 Y>[[ WΧd/ڮ;G}yM/mZtڡ{mV=hAw:_E>hXm_\b׎SҸU+MMuv)OsekoCƜsOs9w˫վ_۵A|-'WNlFW7w XL򢬱^n٫N/?k#{:`VQ/ާWv) #aw Vk3PU7-:}Ks[8@V+~Be{5w\= fps?w[/qՏ>[z,gQ}YK ׆gvMr~Vf57Ʋg lƣv/``B9qr;t3`a.gup.{smu'w^֗1~?>zr͕O,0qSpJ-²>e/{2rx}~z>:O1,z,6^8bbg~er"s&t^[ڸZsRW6_.zqro֧-gC~k >V#+TsaE<@>8bń-l]X~QnWw~ͽnfG^c{SAqsF{oc|-Gʍo~ Wku=;r`{?C\vb4?鞩͚}צ5>۲ֶ8q޼`=cv㼵\15|=oݸ3Y_+z}gk"6'fkN6Wvq{|}5DWzzSju5Ο {˖5ʏ*N-n(>7sϊg,E>(~u&b1,ʡZl5鳳GE=).f -KztŽn^ yŵa|Y4]dMI1jY"kYW/b3n@3[ + 7aezoó6q3rwfku}o t0ڿv͗*yWtԠ '_]ܖ^V.6Mѳzt,1.z/Ϲ[X|XVlr%Wl?aU.c5v:k=Ɯ|3[_wyK@ ؟s^_ mb^?sFڭ&o5xn8Cpa͏g a Y+\Lk/fys[Sk?[k|}p _Z/kd;m[q/^aqă +XIΈ5ۏ^um8}{ֲZ+OF4N+Ok +w"nWm/ߵy:k;o-~&m"bֻAo's%֋ݩ~|ʾ[:,N%ݪ>ٷm]<}j,;VӜsy=[+"=)su]Jf쎿c<]657nGpݥNCΫ՜[d::oWsZ|b)ڽK8PS@b^|xznjmmצ1֓|6Oޟ[]LH|ՖK/YFθi yb9ծa?_xD/[ M|W=pqŃ9L\qݬgT?܁f;gkҦs^k IճPcJ:۷;[ op [8\^eym_|ZC䚩!d c\ +RqmXz]il (~({osLv@6ܺ;Nw>b1#뱽ϯ<['z!Qy].z9tkkcYOs.WԦuW[EZ ޵oU/X' ߽8j?qm;|c/~|ym֯[kW ]Lu=߭Gi O{~k;`5>uՃmm"ތcHLLA_{hҚw w8,,cVTkOu[V>לO}D~/;?<oA{_4cK~*&mw)r+6UqS]8d_67uKs 5ϸeu1sͼ6VWJ+VgSo}o-<9Ǚ+nܶx >]9"n~͟ɾnK|׎</>9Mn56Z-t T7z^-ޘ2CMxGߩ+ܜs׼ļ։G'Mq6mjW>auqwbàŇ9;&ŹVo5;}L6Y[GyGs폴 uŨaXque9]C}rmai-S7Z@O8ߪkNA'hgܯF?/5#+&w31WM}9@/˕1(^:jeˍjp]O[ ߚ_1u_ vYh[+uډ!7h%vPNom"~'}-/~T?a/~ik֎UzmyGPe(N +ۓ"X?]g7Y8B+wu* w{ow=>k? ?vNpxͯ,hM-zvapOcFrkv~pfrWs~dk.]cmyꁿƧ>xG}k3iYF_?a?\jl*YzsٓCg[vQgcYA1>5 5Bf _sw/[$Oٺaq}aW\[k?9߾ 凚[>S%5-'I- q yq!??ny߇?k|:g߼j^Ks붮G_Ћ,6 ڮM{q%X͏:̧ks T]<|윚Xca+ƌi5n[?K⸋[/lg /5'ζ;moݎsX%&T~A~wxnnWv@ kGb77|ӹG#l=ïٴIT.N^hA®j/S l=o6oc\(_Xr'!kWm${h WͰ7l{jb8v=[|rpV{}^]㽘M@܋]o_'?ɗj{کhZ9Pʶᙻ ۵f6v ן\mxz-*̳ޱώVuppŮR~Gzz/T,޽v>ڷ|~5r6{-N~j^2)q@7q{}Vc ˷gu*볍[7q35Sǧ9[|\lNoSw1-Wj?~@\k`[*w:9 Ay궾ZaXwWƦl'ηXy!emp+rxw^p9>u/ws\{w񑌑_jno־D=r ĭ>D{qO~'Ł_׹|m {s˃^/sb++]\NjK7ES&~u-PCn9haHI[]pkoEĈҾk;_o;:˯{o/xV+Ծńukkty[wq73?[P/'cKshk֗9߾Tcvs&7_}Q1<#q@9ÛOY{<3cy}(7~}֏ͻA:|-ĥl`;/-z,?sl]|sM_<}kROac?gm.vB57ɍõ(U{s~׿[7|_<~>O\.juXcbWx׵_o mI.?Jn]978m|6KCzl® {kzb,U9R7h\-~r݋~\W n?EwNw-Ńai[K6X)^jY <5TqyqږT>(ͳfqh})}a +% +L!k$ +2NO'BUh5GĐpsجNma}_>xe1= 1n}a؃ڐjϜ]{q&98oUWjuv͛Րɦ6 uEt\~_ yk0_1DA0쐣Wj[Z$|6[[~o`YNy,oF/]^e;\û18 s mpH{Ik[|_OM VkyUbY-gk|oav~ܯ}}m/XU jn~Xn1Z.m=mhy}m?uU[Z΀bwmV}Ƶ:yLƛHNhvw[c}d7*ڂr ~M^Q}k^=c-xdcY+P0l^vHְa//ĝ6r}חɃR+<{QL8 UC_~moǹ /hSIySp?]i3?|"{q?UIq}K=;FbؖL=-|骶|rE^Ԉ$Z^k~XlsxZ3aR7(gH9j=_k't]qmZ|\ZD~8'ف͋>T\{^\}k%|o@k/d}bᖮC9U3_ ,0^c5ei-mL&N۸ !|I+pץ~hnI{RekzB~no|{]j驳,`}yNqHg&}f\-1}''ܰq _ lY66xѽZnqc??4~OF}^Z'G:!yv=59*ӻf1Ogz~:6}Ś S||L@~I/;՚WM>d?i,=7^R۵S'\jҪuyu˹9GŊֆ/?Y 1oИ^朰<;^qS8\x|t^9Org߱'G_O~jKPH|\G.|"pww>!^oWmT}vƽ낼޽??d?Z1?狝5&dy)j+m]W<T=or.Kt/\ ]CQ PuROguLsq䏙:h1Հͪ+|n-{]|?^_O4ܘUTaykѩGͻ֬G?Ʋq֦j,{|m_uC]d]'uus:ruOWo[u/MJx6xkT+3ַ4YX~u7bXUʦK}1Smqj{!V|n<)]^[XOynu-aq.pn]|1@umjb6Y??cLU[/{m]iu97pNQUU9[ufX'zm9jΗSaf!߮_ַ)||pCs}OO,._xʻ neK_|Q'ɺ N/[]W5.x|g!H~kkk/qan|]qsܔb7jA Ɇ<{t=nB!#>ۯ߹#iXZJ|U6of)Vmp(QCnb }5lb`#6(/k.sr~5b3뷌z/B~ڽ7S6ZݭϳК|oxaYmvtIg5ŬnxҊYv>Rwܷ#jΞώ]}k_P6qsr=uqp?}NjqXٓw\ ܯ[_KFx֏}Y_gU 캂w(bA6^ +W{og\ks?9{jzZ;,vl^uבYl|ڰơܽhcq'kVHð{ǥRn8زƚ坫kõ|jc+W`Qq-ӕ_TlZ}򮏵lP-ô獕[\j1+__=~m"W-qVˉ[8PK5}\ʛ"ﳟOo<'סv +w(1*k@/x +Ҧ*]+掷1}ⓟCxw㛫u7vCk<qs2>緆qE6<\dgo.iy|rb hKlN~әg +;|y)_u_Mr૶_Zaoaְtgĕ:]k̃W+裪x>u-cw=/ +WuBz3#׺ rpw?mkؚejbSt]kܫhc׎?m>yq~yE~s n;5v1VS糧 [~abާ5#n:[4馆#J`ZݜCizvN1^aާܖr6sP|ǫ__.6>ֺ|7k/YNU-F\KR_u}O,b`LS~p߻vw₎;ٵgagG,u4]sXǧ_B]xCR^̦o];ۗ4jor:ε/_ںzszU>CZ?kQ}xzsPPڗƁ49.jTooZc9mqDQT<3ܘHLpy%֧ٵrƌ|]R㥋TOXB-Wl]sa//}zb]CΟZ+gZEZ 7-G]0ėUmSNZlf/sԕul4R;_Hssbb[ܹ)bYjc1;rQkU'P~`<?ڵAQ!SМKs~jslc1v^X|-}kVoak_`X׫E^ً5A\ py֘?~q/^-^eijzb-sC[]l6[\+{uoċ)3zq}bDT[O>c2c]k<|jcdS͗ۓk@H=u[PZr]'i1Ak|򬫶pk򖿹\>sxĎ&~m*kɋwMM,q@ZƲcwyy8@Mu-7mF+0/kEqsX1/>o?d3Yal@v}vjpr 5|/<:b=% ,A[[nX9v}HqNKloj\Tkή1']zl潮mM ECP}}s /1B);l,abQbWo"7,_`yg[!EԽgUX|ugX$Oasx:qNN[!yEis֧.7h..,l@1m-+կkśbUN nTlmZ?U=@ 'q͏9R?L,к0:C|O[C*ΈWj;OkWF]ܱ]BLɵ?pWoZa.Vձ|{-/Ư؛t˵kgw[7k q+r΍M<⌊m]oK~KLKCaտ1Nr}XV z-Oظ.WSXx瓝i6ԦK˕vYbc}l| n-sڔg25񉘎[ Ctw[_M1_SP1X\>d73 eqqlkܑ\r9lhĺ~C<5*1iX55<98|GvF}1]k9Tl_u/dlL8tgIF[j>)]ƋGU\-&,1j4/,cqB8i| y.Mtpl/^ol^Y~bqQln?@D}o]xjwcŁfy./Nk%g6;-0n훭X{z?/ //vԽw!!N^#X~l7>NDt@v]_i% ~im֧`swM8r{y47˗uOl.`sD9yFkَ ^i]~5m3ciyfՂzPsHlezml^6 WxƵRʳy,N*GNŹ7޹?ά%\_i֜-G4,k˙,+чv-c:abZ ѽs s]35@\k@nsd]<[;Z ;䢹B5S݁ 5i؍{|bڭ6v5 VW%}VQ+NsI$ @hkI x O=bdfd/fCG_w 6']΃j,_gq果>^ͨ>PΔͱN'^  +QNw19f>fxb[֓:ZwGj*?\^LB]p>r7/d1Ob{H.PH=ˇWc.#g17Xk{.hLȹ2Xr`̙wu4>BWؚ /]v$ CA[߹N6kQcQwx֗llWH,7m3^Ta-]c59:?\Ŧ9e%Y=ج8v/Nu_krj7kV+ 'znăӏhʧd+~p9Hϵ/]j3r.'LνG[~*yt5Ir;X<Ӛo=˵yy\Ȟ9;,D<ϽIk߭k5*CvѶeӓYKJ,Zjwn6zm-5!&}~sƾ>ªs;}e6nZfHP =Oח׏cwsy=>j|c'}sC3"_žl+TأCNa{;w=_[iP~s+WZܭM\վ^.fz1#q{#~qkLݢq76{aW}*oWX Y I}RnnӹZDvSޯy'{Gϴ^;1 +Zq5f%p[^akj<*n|RG=qP__xXtuosu==X}ki:|bׇ.>n}^/sq0~FZNvV<|%ނ_`5rt_qrD62q^TS1['^31n5s1wCf\ӷnoȿ5ٯhxX՗9>şb Ʈ bq]/ms[l^=N;]q(ڹjVjZzb`Ic{7ga`a]M/ܯZ=F5Ϋ26POXqXBS(wk}_kCd|>m>]Nj\xrnܷzы+%W5zU.vkko~?۵+3QDք]SB|B9vq\#>z23T^vuy{`@i]:e*(ҵ6FdC:wc溜39d_Wv[cƃrV]ro_V_q}_Ƿ'50/S:SkZ7߶y`ꒋMnsybj,F(] l|qUK/ίvwh>ȝMx>Bmao,&s('-.3q¿|ڣ#~.g\⌋a}a[P}I/Azj@>6,/Fw.`a+DzwZssD\H>lYs8T.<֎7W5mf5K>us5<~q˻[ea/ P}a~T|5ib{< +*~KǍON\! R|8w|\]ԅ)Ħ) ڞ/Wkyװ}2eQKvL1Kx{چBvsD,C^kW=w$5 3 k߄Lڟ~ˎ\#e_31ƀ#ײBFoV֤5֧~eyyb0Ʃv׾Xoݍg+Ńqci;| lai{q1/37OVggZACkq;W/X\u$w#j,*/wo hY`>{Z~6ފoG7ﲦeL&Vܮq-?΋q3刧3]]>ZjLCqUxVyd|jͧ^J=Ջhcax> $?0 > KH#.P./Ѿ{0jETi7r9חzs-`琸dt"j_s_k +d5؟X`9/I6&h^.l5zq"6[Lxѿ3 }ߙSU=g62;j94\kruEr:?b۬m/9y'㬿2+f_MVq|~ rg^|4f1@cݻ;^q껛dFq?5 +b[;'\!\\&k1V/I'[].],l"(V\X1۳V%a;{j-F?~Ds|{w)^w1k#^ؚ8GSս~;Γg:(~c +;lml55]˟w[nu|v搜mqb9Zs}H)mclͱ½ꁫr뛿5sǶiGb|+SY87yͣ[_ٓ|tW֘W룱0[GڗAݣ~}sObO__LBy_}{OrRVy㟍{<8վgō2[L~-[·%;lI"G|yt#ЊOW;H',CD /^o=vac!K bP<4@dT K[|ũ=8[qvuo;ڗ;rEc]r)51䴊{f_N!vݯ9=i.n'l Jyr|Aݵֻ}NN=7Zr޷Wy{>\7}3|u#\uaqH}r:[#صX?"5<#juքg+/hYM}qWVdC`[.WyXi j-?:cxCaꝻ|A?8' 1Su25ݵX+է< fqpz77;_mN̝Ԯlys:䈪)ƶ>O5,IӘ?Mk`lbb(rIǶ'ٝ_ fڋWW̠5z}k=˩5<fڷ=ɯN~z}'R}IYwblarq[7m4;5x%Y¯Zq}Rg{78kҎ%!V ߫.ֱ'z[kҚ!VlO:aB<(/qͨ/;qݓuyk,~ ߾{mmbՓyaUuq^5w&>fss( 50N4^1ȦZ}۳ᚫ̮Zr{Oo6X\~cjL÷13/Wgquqb Qri||~Y̠x|X+ZXE~lEl2϶ǖ=;޹mZsĵt?`E'wz?rK5..S[Zm+{[FXI=rKX޼S<ҾEY3,W$۱vn~q#|yav9Wg=KF\Kγc7rԤsNlJQ탽-s-^QGlLo_z]Rk*{cۼ_`x[~xD=0/Ɲin\ͦ4Z'U$[K/~a3QhecPށ=mVONϥskrwBrPl)|;S^tGyxs/+k{Φx~8Lmz{ةXű'd9!V[o&noX6;=|ŗi[c_|ʯms|m9>xr]7aCjXP{ƪ8Xƕj}aZv;}x~cmjwq07G;b~c{=eē]ccQm[ +޵^^,筞vqQX51g[r]ɕݽzׅ|Wy؊tbUUvo{CָE͋.TUo^g;~νA#lo~mg5J-uSc1ݟ#wX8rtkOig>qm?-/\<\-}a.OG$z7q1Lq cTJV("6l4y惸`co"Bj3E<_>sq>yiţSofۚS˹v'yǿ{f$om{[L߼صnqRwub_oqxpwݏ־{xk0o(f|o!~I+}"ݺUekbVS oONίz6MAgy[Β}d(]3[P>A}ڍO5L1?s:1G{)+ޙ/4v2zʼnz]Fu4{?#3ň1gu3݋n׮0a13xqsoW? d4˥gDx){Ƀ.&ztAڊouzy5W3}/쏰:Wvt'lR9y; P WoO{D6W`^"G%[|f8 r4^ظ.ds_^k|J=5Ͽ?Cd7mވq֋wpXLܼW p} yV?^ߘb> .ֹ֕| u +6 _uŪɭw3YnajSXl>ԄkA{͜5Pߺ չ{l_(1'sy9bۣ_͉n Y@/]u18-OGyH6߭yuJ0ȼ޵e a[{qpys_#泼]cWՙ㗍j^7`RZ؝#뛫d_]c̱z +,{n~} KI9cZUjYzJw-zyBp55S؜ +'ٺilvm[䧼\_lXL>ڎD>xރ]j\3 d{=O]ˍ4=蜐קuyUB=7w]baQb%bx\槜`B4_5>Ŕ[X0?3c mvk=`'c+ㇳCwx[׋bQ =[_9$p贍o N'&2T1[ZZw4\{u wk"^Uo,M><>g{e #;7*/o|BͲ}<^}fcƄ[GQ3dq*͝#ކOvW0}Ev:ӟojʇj=:`֗6ؽŅS{_Q>J}}nZg6G[|u&;zג/ݾr澫/R©wsa-afNDں簵{yj ^\/RMMr[7dkso酙M3o4ן֋ukln^Vq c%jkT>>_O-p{o\@{v~,z醋Aj"͝k8}z_Mt~w{ki:ga֫v>lV5l~^5yP]:lKͻحb@l{fNG_}_:`ytx;'W׻尪o| 5}kAN{e\;ݻbw-W>q Cafۣbsm qKewn)+(jכ}01k,θcw<,x%)G1mg 3Rk8})hvҶr_ql/ +~a ʁK9ݾb1<9av/:oQ9ثw)E^< q*ZZr!_ti eB9b)Pvkk#G~R+o~9/"_!o}uOl~ؘeZs44'18LL_^5x!?o^\}2#oMչZTSp]g_?t}˻.AN? &0u^mW_퀚յgž;4Zm9~w8a}6WVhk&xξ#o !G4?z2Үn_pTUOM{xnZ]1h:Gu魙z{ͫ=Wo+nYWj}h3/^g,ivkؽ1h\;o⼫sܺ괊#}Zi\Xr}m~'q;9ڒWkbkò=ϕ= ]ףFKw`=K8ڜiʻguѮ-m/gaSc{mXiq[{7Tk>GVmFmCm~8eq^q>; /~_|<}cO=\;օj,W}?n'J ^ܽiS5QL$eaY'fsfo{MY7;OO9'ml\`]S;nj/q-w.'>Y^>Xzιq>yݹ[T5~y/*/>{Tcl1f<慚]Vŋ/N"V#da=Zn^\Ŵk^<f6VNvvlXg0&k|ɸ-ϞD˯hO Wۦ-6Zmϩ`[{ڥ3Z.>g^\0fk go<0|r| L';,L}W

    g14Tz=ł/jkUV6P,}}wq8)Q>x/ͽ{19!9(]kzPsބƇ}*gÎ+nkr^Xo_}Yݯ8V.νQ9GT[}1՘k,gPcݟտӜ$L%4WC|;{jBmA=c6Sߐ_'m_R5sͿpu>y{ԻFQ8>H-rlˋm`-^?9ymy樫sZ=Vġ G(FZ^5Wbc(Oj'밾,nvMIyPj_l}9nŊȵgzgcin[k7۸]_b%su5vYc~ 6WQgxj0Ebjչo '[g#ζ6z _Xuj?{5Fɲi-'V:lGu} ۮE"rWQ|>x[lH y.wMjÆ0_/w{,&׺W.&O Cܼjn|#vs;>f6TgcN-r;&9Ͳ6;Q$8i2Z,ͲƘoj w 1bq{ߺ5r nmS݇zʙlLM6;?0jIu:s27(翵oz}cl- }mبQ׋ll^n2a:}qL8Z|sI6%[{o1ˮIqDMx:qi^|c`.qqkU A~EQgձT=l#_xjVzgFcdumM޿vjq:h_}~> HqJBx+T}X_yPƟOul/s&8ܱњi+d-}crYv= WS~!|b-GL-XM}O.\>fNp1 C̷6Fboѹ)N= >_Q%U3M_NjLզn }u˿nMINӥ]rԺ?Aa>ۗ~qw~_]\[WWLy\&V<ݹn,rk`]y r :Op"= /L|~yV_lZdq_S|sB1~-}[}ۋ\7޲}w[٩|9/[; b0xx򾚧nNܹ5^w{}sޯ~WoZڟwaOiv^%rtmt!gy(qu1s1Wo:pժ:^}= =B|ydד=o]g 2NP$ǥck,<?\7F +޽Wo{<kp8aΜ^XhXS.Ͻ009Y1Ov[Q1}1K~Wl:jּaki}2Xo36q1F'|1ﹼD/`7wց[y1껲]ߣ/;c{cSV g Zu5ޝW=TOlPU r|s( .\ꔶZ#?YMƃ}Xw X?Wd]\\YW͝u}rCYc+]=T'K]p{|w?/?yq.3rᚣuxX oY[u6S׽pAmyX'!߄c8Z>Z1Ĺ5Wן/(~ s^%4;RtǾd'w}W~Zs^+/u\\ArB}ż=䳘Z5(gUc7 Ug).׵lm#~KlUͰ:[fb4Rr[?P;o]wc1IV{{=ub#o:ijגm>=3֞izU5[kznuoa%͕jqWtmjrbbrI{,>^J. Xa9iC10vRc|&DjʝԷvO[ys,ϑ4\gl)6o}7>[{o[;gzyݾZoixս +4ÝKmE %Xq)S|.s{{x^yկ?"8gtm[C֓u9`bXDޢ9_ZlLQ؇9L-in,g=cɗ] x^?~˳gn31眼j []XKכ Z̯b `1lĽW~C?>~[͎,_^9#7WKh}T྿g^IZVe^i|ton:~[-ұ(}6 GܽB^{ȉ>y |G9⋻.I\5[yU50sZ~r8z|Z<ĪKފ/,OH+f-G0O8Dqa{n֡9[5lr{m,j]?{<];x9{u]ֈz|utnﳖ%MN@k8UC΄5F9rؽBڛ,=!{faDb}~D@;ZrVb?Myl1b[Y޺?uynS;wy.ڟkm|ZSO(7MlH _kqs l~OP`1;֣<^{Kw1SPmIk_Lw-&hy48[LMi؍6M굷|a/.,&Zoo[i-h5ՖIه~%^%khxX6f4uvmq>yѭCߙvCl}sפ~+k!cÕ\c%_T>aZ'D\~ڭ}{|kˬNoNǫwzi6xGwo7/S eeɾqbK0S+)k[y3yk{ ^lp9jlU喿c nƔ%g{R޷so{U8؇AtigA\?<[c|_[u&OSLY}$I^k홢ufViP+OO|{Ǽν{P&e/5kWuZoK/u g=@ޞ}ɵu=/hN.gwraڲ?1{Sd XGh,[OWd;|^z++ Z^Gc[~n wkA?֯=ݯ^nβܧֿ~el&o[e<&$&-gvgS_*[n5Nyb̽v{)o{[ol`~rGlߤa 7?ƠAQX]~Ej k@ҷ8Cc{/_=q|rPk J +݇,ˑr$PkZkmþ떽ʺWuuuUVfd{???G{;['ٗ?_O?}/d?ds߽_O{ukk/~SO}~c}O?GO__g̟gܟ___W__Wcnw:޷s[Z;}Ygr]OŽ6/~kusyzν{g~ۮo_wn^7_˯__ߵڰߺ}w{vmU޵ky=7zOw{ ۮOoCso]=VϹvkjc6v'ڢϹvڬ8c׶wl~556λ}v۞o}h_;>zuݵvս獅jE}~\l{=o͗w{}qh>Ͼz٪۲ew6>o;Gcikcsg[j;ߍ;}_~vWk[>6m3g׳׎OS;H}ko4C>{#s>ܽnmsy7_߿??o_1z}}v߹s=s=|޳Rcȝ~~߼߿ڣ{ǹj{wL7خs]۶[7nZ݋sp]ƎT?][1ٳ=ھ>.]c6j:6}s\=ۺ;6>m>[p۟O-79;q#ߧ1.4dO>_KxA㶳kwwv}e#Y[ݼ;++?~}s}~v??u̷k3o\?qlZ߾k׻kgˆ6ֳYKoݫ6̞s^MMpu^{ﵵ/5h糅}{vl{m]viZڶWq3޵cqb|}4g^1w{^qcyoL9+sv}kklX=kc{׶Ǜ˾m_dž_j fvG˟3bRGs=k|k߻ƚsm[̝>u5x-ʍזFs~lynLs |6I۲6)ک|~OٕjiOg7ڡ~_<׫>{zwE}B@WϚG7cΖ{-:Gq8SR紾q>Se_8Wńg;_J> ;6 0|~x8Fgsݾlv(ߥ͏k ^l~|]:._4-\D6| É{(^{̖gkP7¦¡ @c|{aonKyԏkks1[; ܝj?ma=C}>[^@8(^St-a25w|GCk+0?>i3=!5s3O7veOYQlw~_Yr1m/?/flAv1?⎛Ō|v۟bdR;o>>cqswOG{-~j{}OwׯCv8~?+k~+_9IR}bN1bW̊~O3;N,j6X;.ٌ ?,ijWбza-_Qϻ~9՗{wOݿ};\16Z?,[<Cbs +eͥ 7cO\g#ۋn읽O{2gtCcܜ8`|[fܧec󓛻kSe^"q]>q>}nrzr1O +W_O>dϩڡ+殩Q^Xw`X1Ɲ+XȎ85$i?!g,=k 慊SDzUEݫ9l_6͡jwc}.wW|'ya!a0;}֞V4g>ayua#n>Cb~L;.n6ss wcO_\ܣm~iǼS{oٮys9Yb"gk Z_HNy1h:Kōwlg8[0Dj!}l>YqGohlSX̥g*&&W,]K}#8Y?K1JJS[;׆1=(ޚJW;e 6=ܞ#k:6<.)&c_ѿm|@y#r)?9=0AW*x/V|&\g1 bٚgʽ;q'7W._XH-śϕlhqi-Y\׽x`y}'זտ\';n-&y Glfa|byTa(9kgN3s_~x7uwb#~*g,"sJr;%oq`P׼9!U Zr{عđ dy}lc}սgGTs-yq ߾pi~8_\;E0U<ٜKnU9SxmUYUW)-zs (yf45D1FB]G8`]_ƹɼ)] TsuqIS-sڮ>>oō}=߬}a yy?͏-ְއbL{3l}mΒ֩Z5;a + kCl׭S={`ޯZUʩ7$'ι}v7;9"xs}/ U') _8j7Owa~z|{]wx}7X1Qn>5TP'':;-?pLJvKZa|],w\:qv~9Jj\w+,Gy4[gؚ c5_pzyXnw}7'wM=Wkt@ +V r>LS.Lky[_jCi5'fTc?ŵkmpCم{5ֶ6SoWkXBmM6|#~&;ſ&ey'|8[q|5?T|.;oũwrG/^C?;~pBǗ㸼`돚Ko4nYn ulKqCl{M/ݸZ{9_b@Ljlcnbk/d]lbu ⚩QUׅ{4^&[ءE\~6=#i-Vc-X_y\lcӎ7g3'HWmmF(T-IijlC{򶊹jy\AQ~I6:\s1灶Se?էR&Iܬ.nVm8Bo-'Yl{VVYncmBsF9&,ܼ|a֟ ζ-s}V5>^9y"QYMVVΩ>@Jfտ ~;yv`oo^Ge!u]_;l#?Xp;vsw.w !{ ߌ;/a^w?wMiäuZrP<7g+]c<]c=?_3I6+:t&Վƭ׽6[]X:kBsƜ'nG/ƒbraQ785>甿"?S7r[Np+ Լ68<6Jh޽m6е-ǯT˜g/r klm}/ɞlZyhg6\6^7Ő\ngM3s:_{>C;T㸵o0omo'/_c65+wmڻI +ڐ:Z-{xOgί5s}in CJik]kʅSWŶ,ixb/c'=PϭTV]H~dњߓ?O"N!lqw}i9 ~1ߚ]x')RLY۩=xu?ob;FLH5n#V&X y9ycA\~Ju\j>O^z,a7\_3&.{œVKa]?J1{a[> yt;K5_vϯ7_ xgֿ 躑|sb:}?&~i>yy{9a!}v +/v3ݸ ;3_^,noW 6ĻM{h}ʳ͹ƸXC-_s/>{cw|Vx7_\ݿn}x1WIo?߹ZJR>`klnf׺ +?o[As+-[l']i|pl$9 ^3Iŵ<@q%ϯyl_/]_xiv37s_Z>Ujv>yk7 wζw߉t^k6Bu߮ϫY~hQٶg-%-g[[0נ0Q=q[*]DzJu` >}sbֽU+'RT?kI3c=Wsr)P{ +)6S^ܻ8aa~b[l0`mR=q|Nrᖇ Nsr/9ɘyV^O`sXY]նj %Z抌iVX,c\g=†!C_PcjMKg]g1![q1e׽xK+F_qXȅY#\<XoKPWTn5ݼ=^XNM|~]p'<{N~y͡/_/dy~rguʱq98iMKa,R,Lok&C(b>ҚVq*YCXW\VA\8}1Hિݚ:3;dD媷vc!l[۩18LǛuvEHMqH5_;dz,oin|az}j>O \?buUԺ߈Y+nZc׶b;F8@~Nh 9dGVyAXw]XXZ7u65\nN6ئr\f/a]® \p6ZDC͟"a1C( zHͽ;k1Z(]݅ىYMr-K)_*VK~T2,cɗ]>5zg>Z͐?AL|3ꚛW]XrivT9͜˞3@ߑǨ>7y<59!(V~f4A~bUqui#[빥ٜ9wo Օ7ogv6;۱kg3⡫ ޵ؘo O:|7G~{՟6j,RkG[v\ks^?޽u ֧X޿b>+nŮf:%7}G5{}s7O\r}o;o+PzήŲq|>ߵC9(s[Qc#.Z;iƖ֋ط#:V +_ܮ>_86|w͍3o(\zrd:>kEދz.⃗FF}pXR__n=wh|vmeqpfOsVy~W pƦՁUcյeȃ+N}ŠL,nr'o9@]cI^J$1]-/(=뺖wƮiNɦ7Ҟ{/}NV•,'i\f}?7uXc]|n[ZUp ~Z_eו+j1skûnjwkYquۭh@?7s㹆˜)kOǟMʽw7>:b<`_Am3ks.~Zr9ڸ&T(hNMo[Κ"ak-'N_lc5\{95>m ]ؚi\O*ݚ/݃\fS끇[^O\SW15ucOQ޹M,v*wgllvydujm=睫]ZL7kSR/mokXXQK9始;axaγ;,>SjvK4[PX<\~ՓƠ~q"%f:7\c+P,U^\Kba/Ѻ#[Aos5Kҝ7vy}}/>mJ^Hm-& S-@mٿ]=T Qs'\WO]Kq8Mu:P&Z:Չfm"u#A7.Pjnu'n}۪{xRAs_#crޜv}3koɇ χ;.j[yX]6k&rؔ6h+~5!͕n-k}unvu[fu aaQw}N)?ޕV`|".>- S+Gp;W} Ea >YWA|cWڳ6Oz=W+N\#ac }ʷOb>oq ƥx~JY@/lhyCuBs*ͧˋ\YǪ]men.&]:7ΰ֗6.g!{.K 'dݾ57m}U`=4%/Rq7yr5<~yY\6Z\O=~9'y֛p\dՓt|Du[7JۛGF\:v;L~bt=v}t됉?QoR+ =[uxv^|N'Jy8;dT }?^ﺊ9WOols|KG{oᅋ)Y ?j?huy1^l1~8֫'!g_lG}A}N?xZ-||T^et/In#~iÏ_i y򋶆LƼpeK95R<~1nb?Mc6<6,[b@kǓ6euVR1?߭rq6ot|DĮ9G>m7f+;Xryh9SXRltwg|ow U[ iXN͋۸]MRqje/_s}ƥZwBW,Ow;/Go~ /G[w;َ/]凿Maٵ:M~7Nc}i.?dL n!3G置Z^p6-ņb>P DŽ#-A59lO/} ?[^JH m׫>U5@ֻW՟:cgg;^ܸxuX샺.r.˷^Bm^:5֙ctC\=qAh];Xޔ5ߌٶm5-?/ŵ7Oj.>HJ=TIe뻄w-Հx~WZ9/&FҸ6\ט],p^bx}XYlgkᬞ>+nP-ٸ9UYykM̵XGxU,b@\U$g?Orm''Y:xc1Ull9vIY|{-}90˭ϱ<91dld[{fJ~yh6tՏ9b? ǍGǯ\xt5JWV!`>}WVu-b~Y@۬M=:R}i>{ΟzvQl$Iۨ>z[E?l?kv:5{ZKZ^I6kǮs^[J[xqs'k ɇ(~/g6L_+^ XX[hLG{27bz˅347~7u|Cև/cV0i; +pi=g޹f};>l~nq2b;?{_qm%ֲyj7q}}&xiiu0}8cAMc,6|\ W,/]Cuwb]óck~mm/ ʵsk3l^]qyެjZ/~[[tՁ9./~n.&g^~T5^viqs .s~5i.oIx5'>|]qcl=Ε>a9[Uo yj1 uAL]+k4!yd,nJu,3/-W5ckCapqj6h | fǵk竑۸yqr _/SpkDE׭)3w-?q6ׯSo\-]+NY,XV'wOa-FF:kݍi~d:y-i0 cbpj@ <7]?qjyMzب`M4g{7\;X٤0@9%*tV=&tmxˏ$yۋX+ZѺ#ǿ;;Z|;{ozYto͞k3wẀſ\sbls[grmw|8C{l%znbsqGN] 3'R^}V?Z1~nj6cի.]hc4{{ל}O&5;1n8~omVs>Xض؃uj8ͮ7!fl}t=Qk^>J>uI_s}Wc55hXށxrgUᾛ_1rc";~:=#+.~'Ƶyڮy3?m_ؼv.Lv5ė^ڢ1ybV7[$gXȮ&u)];u5o9p <}Ə$}PMk쵨 #Nk 3.nmsr7smosyptuO;^kW;_ 9y+z1Pvp-Jqu.}ۇwwnG@wr.P\_ .^/;Gw?,lhX]a8rUϫj&jm8˷Ծŝ+} 0f\Tm}a)ȝ f\;oV\ϘX΍vQuW_҇ynua>}>q %Z=b3[lu-Wվ V&wAkk4^7&;dFQ٪Dsimmk8ak} k)ơ_ql?P7,|{Zڳ9㨛V!?hy|I_lDh_>jEXbW052{W,'K@35 'MlM8&neqWGn>iM⍻8ybd}3C;n3́0s?rz?h7Ǽн`r n?t/m5^mӟl7}H5հ]̘ps=٭GxX,H[]#wďj[3B PY\~Xֺ0?/8^ p9aNW|v|jpj{jϗxw]-ek>,鹫k\{]2ƤXu8Xj}?z 9O^`[ Iyo#w5/8!܄׷n`xr1q-_joW'LPs}.l8L]e~Z*[MN?;87_ ܺ|jW'+΢/Fئm>59\]XEK`sȎ`m$GkI,`7/ԩӆktd޻^<+[G_寵o]%Zl>Z4㩣 +N=74b}yrͅa볭xbΩ~cpF`|iw>ߔ=\-r_N~m|yڐZx.^TcӘi5KEX\w|\\^xƋ∋ .߶q/.DPzJmO'QJCqힿ+kR/w}½y/r~X!^'"Y-\s/bꃋ6ws*˹)+qe5koШ16n^ ˟(P'Wױ+/S[s+9ι٫0Jm9/jļ@S'}w4Z#MȆo+DS^2vЪ [֢߳u;b_ůۯʯ:Ri(}}=sO ];^󴦟_^cwwmu#'Op iVSBϘgLm57.&6]qj̝}qLӟx֛ 3|j0k(%W +/QbhY?"Nk݊p]&||kúM䶗|t YBikх+V\NvVi LqVI듿'"jگ>)phӵo &(,_|*?";[{ve fGO߱\3wvkϳ54h'8mrq;'[`1srciAK/hLze{{o~ _ʗ׏_6>xi^٭ܾݖ񺜺ɹښW~>8(.vϤ[zcyW26xcq C-Dj16W=#-9toi̪HlyGMRsb}n6?)h_ѶNp8e9q7볬j6~O~?WLbk޽^,Xm.wဿG?☷5cwb}adoO`5c4o?ѕK']Y}pFSjsEg/gC7{c33FLokܹuCMyZ,>Q|/sKZXl)d1m>cTN&,kaq1~auޱwaw~ 6x;:A9o10"wݚk[1Y粜?'iP;yrmrwQkx\ZCcoo:꒽0)ָS l&7mG3Scs^y{k-c}iTXk ,r9ˋ=͈wu캳lm^P&|FjܹgjkWcM({-]s8|sc 5bqwmZ'y_ZոZ`pOʵ_r W%ڵ7bafa0?[gEoit^4{'{aV#a~9Yr$՚-g[~sMqrq|r2~wΏ.l/=z_XK7k%fli[ݲ;bd[@.qOG;5gYwϚ5=Z_;P'#fซ;$NVVZ7){162;:֬7Q}*5"sd?1bbO>?k~e9]ڢt>}~N;.8;{ʼnewC<~sݧ9ly:1c,2~ufŸ_u/ȖPSww,>8Ruq?qLoǸ VC9y,qU#ۚj]愷M>Wz5//gn|KnqL~W{Vo7cg} wYPv%h`y츶>jW]жՆ `_:/5,&"lu'S\:ӭK\9/7±mrW/˵m!_Hlk;4\ h xDK _=po^33Z^];~b'7*%lWL=FlNX_\[*@ۯ9fo|'[츖CP' <ŭ9KK#uf 86S[cbWoy搬eMb}}j;9z|~.݌_qNj簚Kms&O .vX-}ȸ㞵6tLm۟m^g~?c3L9<'ZΦ[gk'#@.o:< ^^GSN's0u#ї ^˓>[}mtqWutbtf,؝#ܰn/< __g;zw?NaڰMhA'_xq~_\њk/~ؔߜ}뜻Oo3.jO[5?]}4g[b齯~\;ʓ<%#۵]ţ/MmyUMc}.,(ܻygq@ƖK'Ԝ}Yݜ|~pVubL)1Xw摝WŢ;ؽعv~vuu9gכn_u_@!'73&.~ZLgctjBNދs}[^a&[<j 6{WuT?yem /_ZWƜa./n@yT,^%oq4u/}xrv$h~;K:tp X[ɃOt>kƴOZ m}F+_k ՟wY}18sekb}/Uw0#P i]徽x/Ņj;<X<@zxS}>8_ce5 KԡCO+▅u̾?7so/_؟>;GbܵSjs-<:k(5+/rͰƫi+ӠM{|ݓmgHN=sO+ߵngջ7,&LcsrƢ\ק/n^Y *[tku37l^<뛊u0gg=Z74Zﯸ.GZ7/0 x__.ww;'MrV܋/\qԬC_[,)ua^բo /M-h90mcx|ans!o.5VK[{'jL/.jbVk05\۾6>uwaͳawF{9g !a>։Z1UOXk6/nu3ǹŦb훃3/Zn;yT}~~mqZ?ZR˃h F>w͋m}ȵ['AGlHlC4{Ƴ1jgf^wilLwZKPy[NGO8_p wLiͦ-Jq}fVgm\>|O,4p3oi1Kbb {?Nn~F?uzrIt}Kre[_0TsTg~L-G:1 q-oc)/w^gwͱ+Ickyz涵7ޟ?su[ǁ^0ȏ_\y^Ga:eWk=bz܌}v#'cv9p}N?_>('`ɵE.(<cJco]ce:^\B8חsUj8 Z#+\} >l +}gz5u/=cE#y56_Z-zͱڊ=%uHZbvc8N]Skh~R^q5*ڟ`O/c/GRۤ NպqayOUNOv\W;>K,'r[5嶇ă&K3ܼf [@bOsUu-7xem`k j9.?~]={q˶?ڟRu~i&s>9z%"^كrj+شP{|rJ?rzgKyr_\p 5![9-9Iq +c_L~wUǃ˥ PGy<4ysxa9}.[/JΫGcϵqkOܷ8ڃS_X:5ˣ{#8ro[]׈ay9/[.}y5i)t {vj_~f\}Ao!\/wysk9~QO_E7Ofx;q~÷u5=6I$߷Wg}<?oy"ы{l_yxҸxb{Mƺq\Lg89kwv7"hk cV~RVpx#ǽO{e泛R΍01]D^=hW湵Mߘ,/Rv}i7057X?;lzήfo,K7ǫI TVtB-G|kotss]#8+ȍf_3OHl@[ 4n?ë3mLNV{O˗}^acf,#iVC`q0zك[Ӥ~c݉/XSݿ}߷O;7_:ZAMH|r٩6ڰjȍi>;7m݇cbS}V5fQ{wknX~X?mN8g)>(k1s>ip[.HP4aZŵ/>e2:wik-^{_AG\: tX5ꟘU5nbYvְ`xl,UMtq4,->~/Omb]F_jm}VaF$rEZ1*~m+[ZT'\Q\N݉z`1&˭1޸yN51ڔvaswwls惻@m۟jǓ'V?iA.7W}/`bJ/C9^(~k[qjQή gNU?bOg3j9/x /Ǫr[;E땗~ZO+R5Oq XrC˿_/]<rYl@}r Kh]ƚ>\xv˚\CMZLcԃot^qD3@q?kȖ6xUZrh{/E_g[ܿ|DY滸~I'WxJଡa]5|&Nj5+ +r#V)oug1H: ݿU~?/0ܠcNn=LZO+^|7c|c-DhTWY|VO\@(wlO.Ony O6P>ylyX֕>rDH*8{_x* w 櫡x\.'nBq5 m5piG8eR㸟%_÷ٷо_-_+n,.O/`W;Q%))~c6{o{c2/}y$&j[rQGf~ YHuU99kvu:k`sC=jopm{`rjHV?>W s3VVw[:\- +{VOO^n As(' OOk}}Eg|8r +~WXqZ+q?ٽ۱9GڶƠl\fwaۅ)u/t9:꼪oi P Y͎sq:q3^=kN|w?ܿs~P݋9lk!*wR\_{^=wSs֍b~ S|qu yvH=k{5L\=[La8juǶnmt9B7˾mWkG]ckߵmk.xdkc_Qo.X`Nkיo<+Sgt#P\lv+sQS_wq㮯}6cqfmꏶKm_v%|9꣋%c?}1*D_[E,RlQ^j[c~¿ƿ [sW~>3o9Q}yH Ϋs9zy_~Q~w}ڜv7{yqܔ(?X;N5kV{U_]rˌgzsڌI׶kkY>hfwd7̽Iq(?]oXDǬhk k+g'`cd˷6'i_o|5rvWtzߵۏ~ཿ[^k AнXj䉱1﫞^p/;Q[I}Bwq<͹޵\߉?k4 %y}[׭NzMS;7X s擝ǜ7Sk^Ol+9Oo97wvng >t +]^ov%*99/~čl vmi󗜿;1ԃfߝ^}PO3osT}x=jg͛n.5Bﳳehni.ZjE,5b}g_W#I,os?˟0%_tޫϷT0:SAև56߻aW,,H@hrꯋS48gś2N$C|/."uUq;u[q^끈12F0\uȬ۵Z̍k{[ϝ/o'-vZY/VǼYCAW{/Z\j9%N3~[P$voS녕CO.>&hLVW.GmyDrRCmQ{ys7gG||+WMsiaqщmgVcqk u]Cv'rlmuG.8506/X,I|+}~AZDZEͻy=VϿx~^۵kqA4knkk~>uHǩx:|2s/[e؋|776fcUEo:b[w_ZfcU\hny׍E"[dW#[3:;v/h6pJ8cVO*o/ob1?_i=oS~Qasq޾pq~=4®qhmj_kA~_qƟ:;w}mVU\$['??L}/ֺ57gvvQ"[QkNS6hC^ڽ|scCq.?WߵTbJ"9Ԟ/t9?\}^IfWl3V9p6^o-;gO\ҸG>wl|Yi^w5XOn`}YV\b=E`o& ]ik^P\/OM] ց*&)>]M,E ky7:[okab↛sB9kF9]ӷWCkMAOϪ$_u<j^¾_ܿX/Nxzw7vU)Ӹ]j|#&^\gr4ON5+_u\QK>ї_Ve{zk#>fN9CQoriťW-FbrPw.O7/f];v9k;sjnl SF(fVpȋmX6-uCK0Yj.欿X1و+4u>Gŋؘ[L\zW_, X#=)g٭Scs։Ηck ?Yqi"=_{;W^rۭq|M_eo~p xqj]Vs^kd{꘰¶j$uܽI/=ə(ߨqV_lks529k3 bWqeX#_i./˥Iߑg=plk~'(p_k0ǭwguPq9[碹Ե)/v\sOm©·_Z~<{8$X P. ׽K,xQ|b5/B 걲s5ӌ;a$a^J_z(?Hb؄%]uCn.:$j|&d납!Vϰxeu>r[5A’؟l +'WD-S|ZZʺ."kN6s[cdu:W8뎄o nXTu&W/wAu}CE|^cXX!AIh5aԫ{__d1"?{.i)W~yUQ~&fߺ0?޾^MUUȶMxmr-\X|o m^KK}J4H]/oǯq1}zp|٘I&Nض?8Մ?Խg(MbTj4ܿ_wL٫'M}X|uwkkmmύWG'^P}~}+ \vQNx|P<{aW.ʚm^?[͉x߭nܚ^령z2\N+x 燷/?Ww̝︀ϵn[wk V-8?˷_X,>I>G:߶4q ֱwO{右a,ju/3~18/ޝvpkz׬㹺rx>FZyjØ4guR#eʍiqڊ[/6/o.'joʱwu.}Ծk_ˡ7pvҘٳ4>N_Um7܀>Zl&W]-o~b}ռgyϵ¿>kȫ?ۜ^|[ߩg{!"#%VWZ/ʁ-WB^w~Ro}j}]}{8L:Ʈ`k^[#$Mc5Z}|\gDo9V>cw0ݜtmrVVdۺޅX>X)梦RŮ e.U xbE0s§GB-\%%}zb __bŪbj|R_t}UTX›ku@Գ^bǨv "Ft~5-u={ݫ?뻦k8aW#kU3zq0z/-eC>|crۖX/7c#sF'o}{_6~4F?ksqT|/1 k46ccz?{Xu[c콺%a'g|ж9GjKQϿqs|i-o&9_)c mp ϻ0u'-\0,bK#&Q::b!(En^F +}{'| څ1rQVO{?̲_͗xbp-ne@yqn;|6 7,0AK6ɹ|3ͻUƈ9ŝ/z<{gퟭ3g;S]odr|±;^55['uߌw~Ϸy1[ YwJ?򱾏n[sS s<+.{mXnmխ |74μSy]"r0sxփTh<(Lo hыio/P'*O,X; +ǭ[yD}ToxtGbbw1kL-8yH:Gsmzs*Šq _LP뫜ke9Gzp"15//SLpk.x7.OlmhǓZ-ܳk y~wiUTDܴ';ݼDM,cuj'զ \-WC{Hi?ܶyjgcg7i\Z}{5H=Ƃ̞|)z}yx^8 s>+%6 _b5A[|bּZ j~|sm5p 3@W}xƗƟ[Wx.Gl}?߽i}k53۸l9 ՚~@1Wfy|͍١ws7,|>CZ_l`~ubޫkqbš&58eQgk4hV78z|7~;WX6Plu]uFii~wώyiZ?77ҝ/hiߪ/j.b/S)gPΡ~|jEw~y}+i^4gyXqa ")mE1Pr|t +~~:n9Y(Mqe2_}wwu 7_T}M㏻TqK¥z??Lm5Y{xak 6v046)X%+znK=\CuZ`y~}babѻFuK8xˋg 'km0눉3)vճjlmk{uZ\iԎ :ֵsUƕ1Yn8~5gvþ- t ~~ꭗ[H΍΁[k?^,olmu48FŖN܍0:A7Tղg_o/~b|{^i/a+['(ᮽ8IT~ۋ GkuQb/y-p IQ \punof1?a/a*ƅq\^~ܷ޿7-n+oVb9)n{[py}bTڪqݭwPէgc[7cs35卽t=,푘t9~ֹzvi W⃵{q?4=:=۩Ӥ;+9_x +{.}ynZ@_VYlM Z~o\\qdG|;V?M,T_O-8r͉muƋu:;^VG1[#u1~_,86[4jO4Ucl}O|l: jqED3.uhc-om#sr׺poپ6)gaqZg%5S-ƪIAͅroo?/~ /9fVu5Ir*Ԗ{Vk7?CjnlyՒk_5?e4-|1YnLz?&iL]:l\Lio<)'^X@Wc>{ᆾ'+:ǩT7,_I"_ɚb󳾟VK-wgSZ'f] + C,jKc[#rMgaI3V>b]Z` ͫj #|oٸtʯ}Vz/lPkZt\#91_5]22oG|F}gZ8 Y\=صAĦo*^GkTKwR>R7ㄍեQW5l 6bk}:9Y|dul]ۻ.~Jio?ag\nFI|͘zߕ_0< w=ly5\r+/O,{O=U> 7tk5gWmRxs}>Z?#l53`׍o;-/Mw-n>nwCwy.70X C+8|.0\//} Tp]凕Ct޴ޅln 6==VKݼޫzO~ƶm73^^^~&VֺŨvsnsܽ56#oy7^69oi߶aqEk9kWͅvփ47:޼aVbmQ8qd]a}_=.m]b{kkA47۷^s^ٹ|I}nWM7^z0CW"UӶ{F~^< >ay[Z矦SXMvצ^<pr;<晛+ʋ5O%/$:ŽbO|/UC(~]9h5?pNGkNf0k]|c)pxƟռt,~1>{y}g(/L~W}ϖ+.wtynr%?ճ/6Od/i+o.67S-tٯ?zy|/P,]n4>'q?|V׿}Ao|kWV^Ϳ__G-^ÿm}skj{MRcҧ`?ϝ~7q]Cyapcb}ͻ2;/>yH[3[-^X='a'ńrKTFO^,;+wx5?a/_my}1σ^?3+7oVly&✕CjlxRؐ؈5q_9m*w}K};qrvd}\n8[],>?QkiM\t5F\y5=?!7967*gJ)5"KS˳ZߧƉ岭 xaײ5\Wܵ=QXi|֊r{ȲĶa}qkAbA/ݷy`@r<0鿘+Gw'N+<>uMͶ`6{{+.bsj;֚b|Bx TPo=.ck 7d9Z|h95:>뭱gLb1;֯2Pi|qgr~?ݰnhߺ[S^:?LOakT8k_X}f:Ϲv|L=?T߸6gu>u܉߻s8{邫O 0FXg\ k,_/mnűyb009nM 9n=޹-[${V5s^޺'&|+^-ݚ !^^mR.sy)[Tlќ]>-onPnP{lkuOwZkPUI7sU?5P8bW]dzuw}|dg<D q[}҄[\{Fgaז?rlÁx'ռZTsK5ƂvzB?_,\ikum.H~)y(mm9ݿul%@yz!u3WG9\G/y_Mg_sX@s ++ͯR_?\W Oʀƃ@Jk@T*U F0XUۈoɗ͗+Wu?i}6՛k~~~0_raIj}kE˓WG-֡ yi'ŗq6̕4w/)=Gym-ooa :9`Lkmn3ɮ=L1 @CZ %Uf~\xSQ#EVZ֢6Vw7NIs],hMe/Ml8GĬH|`ybNր0Zg/.qi1rcҜ31ns3>nL,U~j ÍVwI\C[W9}jo=8$ck_SWɊIM8|,=[{{|IZv>wwvj9_ejxў%a+ [ K-^hi-g5.8PǫEFؼ_XN/KKh]ATu.=}6)ZŮuq !7{_CMōy岹i}nwmnQ}\o[̵zk3_Mhoz`qkmS,^p}Vu5/^d}+΋./~Fl.Goiܫ?Cn Vm(oCngc5&|=Ίa_Art-֦p1 _++m\g9"bHok.YKNXXϬ##_z!Ɨ/&oOIq(u1~CxDQ[VSD\ΒW+&axEXn5-o wWߘ&nݴb 1mCg<7xy]M]wqnkU$n[j⏭&Mkyٖ>S\GXQ~j#:ZOA|{R[/ '8FZ:uksڋ]_bq5^SZ_f,[u_:V\Czxxk8f,Vc,~&T .Z%5Q9\S;sZCv~K49i-{Km5'-]{/qp}o|qƎq5Ųq/e~/yk~6Z~6v Śh/^~7[uu_5r7]זi]c wWlor-g5vߚۯ/|f]lskM>G6K5H^ﰳ{f|Pq8k W9K]uhߓ>ŷĹ(~-~au<X$6yZ^b)Wnox?5\w׮oP)&螉kf7+BW9;ձ77?{|Www9[ xvs*{t?ԟ-ְX.r]6wo<9}!l' S?r#߿1"&&n[;.> +r]qcpf|߲XTsqδ6_\0Η_ 'Wfy3˛O#GbLd ^@umpsq;,ε1u~|mb__תβ1}Q5._Wxx:oqJsj-;s]sc)7Whm5:m>TiyKP*H~kY'N\J룩)3/ZLZޜW=}mbnyoɡzj}^F ϫ'"-9;[b^sE:G߫+/J[Opyy:/?J\!Nsp[@?޵ٺ{6ɭqajmVi@U+\?OZOؠ}X~s7_._96cԫ +_ـΗ"_:õi\fCұS)icfcgc6J T]8}ך3Tkhn߾^a9AgBwOw^÷{{xXH5,5kr{ 7>['] [4vZΧvjai^#\0OMV`|5~x]\8l=Jڕ?.Uk_5 F]r|?/٢k]<:|iᬻo W_qZ4yiq}a竹M{4̏+lz0D987.W|O- 5^זq37^ ޖu8w)-i<yNi3?ío}ql xq|:cd`8mƭbMbޛ'!ƴZw1[Pg &"b +Q++HO~2wU5"[r⧊y{W+v-uǴY4cDz/b] k/m\PFm <m{ic!%t1L<1ؘ +˓/"D^dSUw-gZF n,e'q@mmg/;5]k~cY;a1 u, k7[?֋j|ĩz7_mM5[ƸF +ֿ?_^j +Nsak]-'yQսcֈݸ4ʮ}wiw_y-k'G//fՃf(&5ڭײ0w-^لYsLL9I_-ߗ7hùmbͩƘ5]G~i?yzfr8m~jaKݻ9wַcMeUrcx éPk]K&x:48\0,a:dX`3t緦Ktv[rw[z|6װǷy_Bkny6+]U~dkjkon /m~4Ho..mm0kplqkskܹpg?CՀ7>MQ\wpߕ;wܩMN³{Ÿuaǹή͵mv~k-M?oA7W?Aqώݺi^[:󫾸6H,^_\֭krҏ%'"m屫S'޷8az|'}rOނqƍJFil=)륄-L,R ~L}Qɗev2ޜq[+f5.9qQ&դ:?`?hyC"G5.v:xjA4]m}W`*sgb/oM s W\K|Yό?_baG3&kƭ/b"s[>+dzXPז朗?zd#bn_jr5[IYkI,q\8oN9O̭7~H'_͏TK OXlq69uvxɭmg!|_6 JW>ۗg}4_jCVj~[Ы&yqDOɶ!chP)Ouk`[At?j}uvysEė,VXxs/(fX߅gSz>nwiM_\oP2<0w3qiE=l=La{ߣqU)cA?0F{B}osc6X[UJ{<suqڿj[dŋ1Ws3浶ig;u^L4lj6%|զWW{=j:;c}wt-{\;؝s׋/نqt姩ڿʿ0a Y[3[H_?\FN`klPfq_Õ/4a›>f޺[;`uLHM?ϼN935/z|kC9SÊ֚&)o剩6FY3Yk2ceccgTr n~s׭ߓW?I׻<= _&gm蓩[]f{ulS3P]pľ[^iϽ;^\~{+^hmb^9WTC퓼O8+cn[.S`5l]s + 1.F>شv@vAiжM0\~Ž ,Rڣxy0-WG8^mioβ~X!5_}7>ibǑݍ:WxyUq" /^?\7ڮيľ:_5omڷVb9kN:ݭøz~abĭxˣW\W^m^xx?},_d~Sf3H7ۻrw]/_X`*rDŽtXPzY_E\~Ê囧za{j͝-7؋1㾣f PcagXθ:khtfw4iwR D_JuL뺾@~ƣk+ұf֭f~VGyk$jz.6+m&wvr0[\ߞo}8TlwOo9 qn|[WwsbJk,znybK›}zmJ~#wi.1oĭmmo]xjj_t\7Sl qk;~|˹|ͻ~}:?US=Ikt.pGk5m5dƑe5M\K7+FϧN7Ejr|E\Hث~~\b>{pb> #ދ1zhdmS MPߋSCpJխ-V.OJ]|S#y#X$b O}J7AoȷM_^@|X_SQ>j1W-ӭqp9FaEks&_߆״&qܨ8#:wr*ad;^kewꈻvݣ:ulc[Xjzʹ\}`xKjܘ[WG{ut_ͫV Ʒ֦[9o}K:[N=}\U0M|?|?jAWcj|ik37NÓ?񫊋/mYZy_uZg@hxJץoݽݘl\]c[ʯy?}/9h4dTo\4=h~}/0)ysc|[ a~\a\l^u];욻Ν9R5_iͯ7yjkSrì (!h=#54q>:9;^l Q-M7ܬcOǿ+7|qp}lN\A.s->b}E1~v-0˗s˗1OiSYt^!91pYk~_?͛wvd Qo_]}]Eܹozxa}?su֞i&eܽܽ7[K;':&OGܯu+{Y}FBk]3:8>[ى楹G}j[=<?څ/TuSk!Xفp0WjJo\6uDIw:Gp?R-uŤHb!}( $VnνC=͎{pA_,1_@_YϵcnMO6 +]a >aܘP06\v((#fpcR5ej!`2mkk˴58nKŠ^VxI[dm +yPy׹:mȭ%#GIcmfg~_, N}7\\PJ8ښHM2_Ok5ŎwmΛ5kĽ0$5=rԉR%C>Tuv1T|5qus}][c@>ccE~:ͮCKFm>*(л?ѽ>;L0ޏ~!1XP[=o=xwm oMlOs1lku횷5뺞e[? XHkc5{ղȑ5tez:#۪?m`^ΚSSX\r?n |˞w}klc s,=QŁq}|Z_u=?qY_a0֢.؜z|v}|p~ԧ[}O<P/k nĭ l'6{εޚGA[5^\':xb}b˹$_H=_>?x}_AM1NzGSqpkuUy obO/RN^w9=6ocs7\>辊CR-~,?v1S}W1wՇ[~e׉||ĎZ?Ҋw鷈E}aa +Ŏ|[sW}$)_(z~L*1;O7voT}.MpUs3Rl9g+1F-GfBcnd=ė+:$xʜ>Ցʃj}[N_ܫ֘Zki1XH?->(܋74.řVsZ:k|SW%| r#ƊƠ[o럨}2mͨa##pjmmnyn5W}g[/`sbޭ5?˗ X\<͉=0t[Ô3-^߾ N}ɴm]m|xJiպICim A9{\#8wxh0c{>{>j;wa?ٔ=՗OgMGn1?qOk3)p5mb}bj)jͣu]$[k֚_[?K˕k\4a7G1p3G?f.I?5Ժ}|ykd]/Ř6iO yW5} O{^F>5X6غlOFfcAǯ빱@VQom>{s - c8gQm5䠕UKǩ=ԇ1Zyt)N ign(ܼ>q[qS\TLq_s2Ua|˓fKlOqTCgnEqCy3Z5Su?FmGkLuK]n<@0uk܇~Ϟ[#o:-Sj3ӘW"g#gKtHp92HsxQޟײp1/͜zqja/{̍xb8ֲkTňf~˚aEWkdXyH6XL+0),S^b.k-K |僾8kgwZϤxlk\lf*,oqܵ5vju +j‰?#wfK?֦>,޿u1CM}"nyx0% x7jk+Ɗ}_3FXfZ?Ja_ZUxGwp{m,8=_gf['œEJ Paq ڀjN`߽S0imd/kZM$Zjy]5ͯc G4\3};ֵ9 +3{Or=56\[S4& ȎCYNeC-owiu-k-=(tu5&š.\ +'‹?QoM{~V!We?WӿڶS8Ǯf|Fk-5P?\#re_os&6qرg|J ^!y: Moפ6]<~^Վ{Ԏ{kjՎXqۦVM͙qmom)kcOZ]Kn}]c׹EV)&1V]˽xi﫿ZOG1cVO\].r^?9{D)ݳu}u9r 7 7~p k,OIZU9NG5vX722X Zmuz|Ckٿ'g^;gl2.ƼcqX2_fg^,|Z^z`&->ckuS/,K-|rr O=Zad?ź +C1^I|ByaaOYA;@򁚿屴 jq8|0ÛήuՏ\jkWH!>by]󴥯kӬV$zamӨkws:[Cz|\$xwv~f|0[ߘY޵P6=G}\ۘzcڒO\ߋ1r709(6VVX#Lo`9֫ȯrN/7uiyW(*/9٩^jt}7kǫ83_[7rjƬ֫o! y}{W$D/-;5ņ3QƸZ(Un|y\-mp3;$}8b~btrv.Y>bƌcso pi1=} Z{wZy\zbb{/ޟy prb4: +₭T;{]|D/\ֺ/^~ YMur_Z^Z`qzcƎxXvn1Eyr Ŕ_q0L;4WIB >iFZ\n @^xĶ^S-խo[㬁},epb];[|oo܁ﹱHvrbs,ƨ_>3-ܘ6v܆gbbƱi(/LXJ\KrV[K/&Xx |{}|ʃy_澺]k,6f6Uͺ&<߱?wk_׽np'gPR}z_ #o08_9 5g3woɐ+ +9y?Յ󽰶 -h̓>'6,_7f7kr*'0뿹S|h,?EL1.:j3N~='NNr߯U<~{Z֑⃯ܺG/b4s൏MT͛yO7/>TN{p-:õoc3>8Xv%`,7&8~}/LP۠>WΧ9h=|&mr_0\~`nPSuպnj8Gi. neCsp?q ת~>r=_xu۫!uOQ F_kebg}}摘hWgwՉ J5'(>[]q8>p[{/_Mn Guiޭall$֐Vݽ~qw*.566rb-qcmڱztjojnW?'ѹ6y~k&}E6[0;[[/z^yqAG/ o޼XʚQ[L~JEߗNo풾NkL"ښfĻƉCa<:arÊ[עnGq̱[ϜK/6]69l:^X8r0-(SO.j,|63ww߷<'5>7FfW+ vj<|bK]a͝z?mGw͖;iƁڥ] qb7Xڀ$W_gE ~Sw(C>ӿS*5&Lۛ+im>eyfrZv:_yr\.׸~+Uڀ.,>ڈ|i{9(Z-,qfز;ߢ+lμ4|qՐ}D. ,a4ښUr_<ˋ +ϲnռ_\甓,r)ȇ۽&5E#[r|?(|:hX8XO?;tw)[LOqkNwnNj4?gr_?ԷM6&qj{׈^,{.({hYNȇ1GDr< jM` Y'myVGΫ͞sy8jWY΋ko;{(7{5'!/PX\iku(ʱh|ׯr}'/ʇ^c;*w/4t^?sD91T2`uYh pmKو Ӗq[|)jI_|'rynoN6؊#ѱ~eW]X;"mu*Gms֙8zk싙[fοPZ!'aIZطNkG\%bq -l/^q\;}=NXsˇ)yoկ{5PZi;O7ܞb&qpc⎹vT$?9qkkfH׈{MVk>g҇l.,nkܩlhis[s1Z-[;Ԟ^+Wm֗蜛kg蓚,n+?ܳwۺ z|sηrQq|n5#_Y лτ9FtjL_:^9@YQyUx}u_=_\͸6e bvQ}ow 96j>u1.}S){(GpWkouح8s?Yb8[efLVXScZ7',n%68u֬qetq fC>3_Xu i-fXߋT̞f\(Pmr|v.q'{uxi>_ZV?O/x_/״-OלR⌫k-Djl [/zOX:ECzy+X.e+yvB,,=}H2Wm۪by˖/?P|xZ |@95N^5'o-W|1{>ƶb@1ϿVYq3ֆuGH놼Fr}WW9;}O^k8__偊רMZqx\i>-gV_Va}"yj [GCATMxX޽n?볞 ざ;i?߯=4'd ]?g3Zc#GN~A}Ip?SgHy{oss7KAmNOjo+o٩-;|g,7M|J/߽|{Ϻ 5ofVb}+ϵo~[<NŽr fӇӚOolmv~ԋژy±+\b5'׸PpҢً7^vכíHV4rW'*GXkkx ̆m=3 y[BH>D1K c{#^Km^~v}oņ!zZGu{ᛝ,/gj䔊Ն˓4%7;{-i{1M-  +VUJQ(9*Jgc]UO9zh>~~oo~O{?>o>_>o'y?OOg//?_ݾ}oo>o׹c?_?o~>vt>}v5vmrv:qڷ;wo?}vsoƧ__++kk_w{ݹwww;1u={yz]kw|tڰھ}Q}whgqܹ߱ߍѻ==߽˶g꘼zu[q|itݹ3Ԯ>~ٖ|1yc?Eǒ\vvp7׶C}SmɡvrSwkndfϧ\힯1;߱Z^5>Oow޵cmɞ}Oug2CS>}y]si|}vpsd|s?sͿ7~~ӿӏȏ|?~~OO}2=]||Rw?n6nҽAI~nsu>'M|['O=z.uvmyr?^~>m??g~g>>?s_??O1}qry|rR]_\*ˮO&$i}wύ\slIӁٗkgﮡso󩇓y~OOsuׯ-瞣gyzSuޱ'#ɘ{ۍc(|O5gc,X +ɍ>k[j\tI'7^w/ۻ^=s6VvMm?9Nٳ7M?]4wl8Dc9lm[X__KɷWK'ڗ_}z}_|qژjekwUW':_6t;>ݖDo{[l{߾n߯?Wq|W[{umY\9nsqrDBݐON\ۖnZߖL}PFk}smԱ426 T&*+Si읜PFcQC53evFG9gqMͣUZ[wͮ{m$f+N-^ob aaIb]WL.(%;G[(I\E]C;Mv|(/wLRi,=sh۵rl꿸L]:vjsU7ޡ/_߱-&ݦ${> ֓\mk3Ҙ{[2w:9xCcMF٣=Ҹiiv1ےn I\,=qdaItg?\Ookif;hGgKv5Gkͯ~˹-5[Y;kk71d!Fz=0m__'wos^ncϻχn 5==xh6rMtJٖݾdֹɋіk%_n))3,8`ryr0V(!&vm+jmwn'#Y #}'O)ys~]|3E/$6F}c6~R_ml!}qۮߌViڱb;Ϛx8i(^Q {cl~gXN}*j|x _~ɦt<}C} 7F Im;=~:<\0}_L`gG>{#_([}޷6:҈i[ F߶xllI7WTcX[hٱ F|Ǡx1c8]NLryDz"MGN9]8{C嫤Oi +xh>ߘxG6H6Q8^y(^qbn 6M_㺈47_Wמh iN7Ś3G9V'4zƦ,'GL}r ؖ(I;G?([gT׈-W1B6Oo​_juW:d~wP]_{ڼ0)_C P/[J p>;Kn$O_Ρb26T^\^Ea|]' ˯]<>m|N;XqC1ƑE/>!KET8 v8}>3|+6ll0B,^qV>Ӗ3em~w\=}fܻ/qcתҗ]ޖ^8~ kc k~+x׼1rE|Hw2-vG[/ΚM,7?Y{ϼ~_Gӯ~'d(M>\qdez#镧6ss@ӏ[7V>wW;h?b8~Qq_،hqXyrm|7כ7}&nܮ/7gű!v> woq#Uŷͷk&CWC[u.%cq/F GN9p쇸1%vmp:6^_8mj3<].̋|8c5ҿ.Y-FMTq fG9_}}u +C'jCOlA|"^ 락|BKm&F )L8w~qW\umcX["xibku(̓>i&3&7;#&rkS8c wr.ojPpX[zQ|x_.los}oa}˟ߺF}{w6o'WslGOnͱV|bcAcD+F(0LC]L6>ܺ͵o?/A9xxo@h*w ւ~T3> O\]?_ ̶1,7~W.[.%mh ^k\u|ꞻOWxyLͻƵo6߼Ŝ;Y\md hO;_us_ɁE<+n 8y*o~J68irvyqU7ֹӧO/_ļX9M~ùrT^J?#/g0ޱruzqt}ugr3w/~ٯcj;5̵^[:B~X-&xp+1BcX,ѹ\8Mcn⅍uR?9,js;a[Ϝ=3n>|0}7gKallb/͜^-sxǯҵ3fJʖ٪ u%p>:[[}DcW_)7P>,&z}7o9j~oK`"܇6jeCϛ{}Q]/[y咦^Fr9N}m.<0]n]b'1QaGqjl~qֵn_=?#()}[B#qy\rn뗝͕k;6j5t sb}^\5_ysi{j=_}[50,Xk\,xbR_v?19y 7-ǡ;C廉U-츐+Kˉ 3nm|'9z\3^>/9q忦W',u ϓ6i9逭9X.~^]Xx;9&<2%؜bM09ߴn<g[ܾOǍ-|h>6~<ΖWo ݡ \MLP_sK-wuw^~Oeklzbb!_{ O|ݼ7_mpkVcZ&d=j{׹\l6y㋱=>n +ϷoɚByM>O Z]z\㕳[=lqMjukgܸ O<_| cl/>k(V8\7V6WN]^dr<fx}9Œjs?]?3 ;Sm1Z<>;,/O9.2飼9l)b=-^(nsN2'O_إXxbd=lcqrԇ3OW.,v];OE8h;ɭ}F}D)u[o\fkq0ـ OBy-X86Oxfol-k>♷i_]rJ~x'N_*fʵSd,\4e{/9hٺ;'#rW Sgט-d8PnX8coMV5bO<qؗ[\c_K4͕Yc/{>q:>m\rdlߞn7҃Jrʑk8V^uqr3h+یg;Z2rJen^xծͷo8~c5]"~mucS].NQ1}p0u9.ؿW3zIwXQ<;5!䀧C~uuJ;zq]ۆY _U/Πr~g]~x `.J2#޼8Exy8׬V.'s_zv{[/!.lbbZb}w룅f',C-kŲ=7yLki4sn>uo)?.2>n;m [Lhy\+1b|q7&9׌u6WرnM@>^|Z7Jrqhk>CPpv[G׻׶]zKW;2n/h]צL YTP=t}?-޲|jNz6v{r^eu9N[^񍵉>^_0l|Cꚮ\_ /marb/zP{-cD={r&͉vcqgcZ7F;e[nqIe6VX 'rzC?CntxmoŷAxkCs 'Yo>{kVg'}/|xp6ʋәNUN2=? 6⚨[zW?Ǹ<\9-snKG^2dxڦXu6]U yV|?Tnob i׈^G7v\[=ga"msusSG?oޯ&y-7m_|So-F+_}麗-FHz}'OvKמx_$lPnRu}_Ԗ<,1!e?c:[X`D1;9*L2rIr>۪k(͜ƭTEٻ@1Q0棸_xyxm gpZ٠7[5f-75#Ž3rkڈ6>0}|#JLxrQ^Qs2l^M8֘n7ݐnm +7aX^/W:syaᏭIx=Wk+;DsκιHKX7׷FX}QZYbZ4fUPw~_-6a_oyww>}ظ7\o~.ǯ,ܲ䢼ga-lnsd?6_7-{jm^y쪕 eGic?#gږ7Llv+嗈Q-yl(X31 q]1\5O0qKq,z|Q&mX_=/.q> +ons=UN9qT7?8lw-sx}N@Z}⃋򠪅ya#gY,.8f-96;ŬsE7v彔j=|k>,ꁓImo֘q9 KڏbK>bB_xzM΁k'WƎ[cõ= }Qv9ou\_CkX_r{=νdXqln=tʼnK#"!I2ff Xܣp}lZ99 +ٿY<Zhshݘ4}աrٍi;OX._=K\m;UK(7'7U#]~"vz !yx;^{b]S*S{Y>77`٘W{.9]cs.ɘeך8͕'ߌYomWpeGX<-:Սbl`㺦\Ï?z$[{$Pl~e7+'psɖ &'(ߜW­\ 1=^Ms3n.g?W[Q3&b}?u6bxbO:/\>y;׏_莯G3޲}X6.:ר7z9|K/ֳ8}Bzeb&6A\P}>f֮7.k\-} ϖ(=j>;iwgmDhNYZPu G[>MXP~a%c͏R٫6|$E_ă֬~wߕW]*w:'2+mr +9^5V.-NjKYele|?䰺dPvps&ސ1w@.$gv/y{ \.縹$Ovuד#xSmOo1 kLz'gº>ߋ[eo燿mp׭۷EOvS%;'Ε wm<{9& :Xծ?՞q"-W\aj ߌ;YDE]kخ~^wXw}o~ o_\ւYnmwcګu_ r6>oןb!b+ PW[8,LQkocu">j ON9Ƌ{MS۠>Kɿ֟2Ǡ>0V"NeU~U7ցnr"q}Q5;#uo{xo}QL8u H.x~Kk뼰!~{,a-wR-p"ǜ +)uqyr_[,nǝ7zm\ e뫔>_LIYv71 rv$cSv[climPe>]zo|#ҫFq]'~1LqB[ \Nss*s~I&n\ U-'ؚsaQªI:g=YqzbɽN,EN{(Brד3zcq9Ax+Xo8aua|}a~&` +C q9SZaX;ňm.^:ƽ;ka@ Woz[|W}ikqzmZcuS'3= iWƓ/ Ӓ!ffs-笿͑r/(Q|¶ƍ~r:׶,ޫ~NgOm+?"Yf|&zN7tc(9r1fkoT?E+(v)Α]r>x>a\Y3>\}1uaCFnBs9ʹN߼mƧM1-3I\NYbl߸Ocu/\<䟜AqQX7ܱw;\,{uk^G.ƫOf&owsҹo{;v1?L`y]dy˥X Θ}~s-E&p9 ֐ԎsܦS rw +&#t2ưӶŮW{xV~m%WL;l7)K)}<3coqN闸x[߭kqה3 +t]p8yq2[%_}}k.f,,ZޯzW5#%Gr5W/h|LQSyQ S+7<4\}ت;f* +W=\ۘ i'O6.Ny{R|Ɇ #z=.߮+naʵ^Ǯe\%ǡ. >Kؠ6XJh88^~899U r7Xm3{cϒȿg&?fnW\"(ˮ"n1ޭmo]e~uCn|&yM+ܾzʣl]y)hn][4i,Y||nN{eb5 b_Q72fq)s`=g}Q@qdkum=_X|@0,O|a9֨GMk-]ݼ87IyxO>iYUDl1BrtZч-ۯ͏b~hw.K66)n|øFpޱ|Tɹ^IuM}oNOV^Xlɸ4rX]m7^^b~7[ܜ,KR?_WX+ClK-is_9G8mւm]b/Tɿw&^Y i1Zyhs`[bq!RU~^M<9}ͣޭ>9??w;xv\OyὭ#/Aڠ[cfz4഻X@.`sos ScpOYe+5LG撘kz8Ks٭riĐ}˧Xk7U +mԆ\^@Z3ȟ<pDusscҫ_uX{N΄orkׄqr$wsˎWoT.c/hv[87_cq)yfE?_vM탾jwk~wdlls'$sG-&(VdՋGV$G]|X>XI/L8.Ky]3\#?y7,gok͎|]?}88iK/i|H\5gԸ>6{cZm~ͶWvyuv2s|\Xo~rR'sC/ [,QDEZk$;q᭙yx]^9c- FhcX*ݵX뻵4ͨGk0l7VY>#lD<xHp3jNqhkb~ZToem 3؟1#8;G _X~}n'we|o:xk8k0~];ݯc3rݵ*s]C[;(Sb !ג:%ucm6O]5nat l;p-\|] z=qkerv;kjUăYܘX/,'-?}yˑ^B_ϔ~ũ_a;\^Awq{sudonUZ:a76Gc x[8zkn"oY}uV_yڸ'Kl}]~ՒeLSy2\<5}wƙzu^m0@z~Nge}GXSWW ɻ[/}׿_s=_2_TY<6<CR-F`ykN]rhvMb/!&'@k+멗l4[^LAT}+XPӡlxX ʏZQLnF}4>dm(ߧm5MѼ7qUaw\Ǽ$y6m+7]; +?e׻ }Πy,=76F$ƿW?7w\myrWɀbZ+F؎:{oGnzO뺽2{pڱMځS6gMSRĔN|b tg۾mycuX^9 W?aq6o0N'۾]`%6M|NuT3 6"|v8Ɠ\e!Ck;\_}B 9͛>7[#9yR~Ύ;$Oɺ8`ƚtcϾp6Ϥ\}S|j-gy"_\T=7[x*77?"U^ _{X,n!fqR&?n'%VF|tߚ?ZPv|FG\xo6a{q{u/w85DZ-`{ϛ,rV/-&Nn>^';&~V>.X3!Sbӯ5}'ڹdiӆ!.gg3n&^ֿ0nmz]٦osoߎrs˛s7No^ݽԱgb#+K,ey5,vӚ w]_d9ä<6 ly4 q)nuSyb|$5>$)vO}A23llXocšg߭բ|iLr_4~l<,WˬS ']7G/_Q[`r#L?6w[+VZkX.`1B1v0 qx/qDc;ͯ>7S?JPh9G?_usY1l-o |QO/.\8'_C{wk:wYK2VSv}l~ka?:ܾOވ?cF跻O8吆ioQ$VΙzWpr0u~3j@m;N|qqdkȑF4&ya_֞MokP;8֚kl7hckza8m@oT*<`kvNbxprXo^՗晊}w_XQک!9UprN_o~a|wL]o>wluMbOg3~jNhYc1&OV,}yӵc?ӾjbpƝ{Y1Zmt~qbM,znwfkBig;Xc+Lnqn1%CXͱէGY/z;}ߵ>g-.E]"UXaH\xdqw +gMo-f_y!7{lb)o8u'KlI\+?ܵ-]ޮw$n.Wced6~̥1Uj~8RwɣW=ć+]k{[X?Y?r.߬aIKɥ81uvO~@5Z[{!uƷQןk 6d^zpo_o.)Z +,(\:fOv[7咉0N|gF2U/4%uX36c}懼r:~1W͘WwplZ3k)ݸI涼&TE-_/_x|Md9ċmrFκk-9isRuMqcXع|E/'&GB~qti\ck dwg+],O9޹nQ-b~rw=a63ʱ3ݸq*7nr/oc#QvWkZ87Gm_ڞ3fX/0n`]1*rxڪSv׺.x鎳?YvާS|us\~]#NZ6_ab/PL\\+9ZmʅB_xALX_}O=չRsBk'g>~Usw]`y~+a t9O!<!@s\߰vM఼ko_]sl&Y,lx:>ښr7Xr%h3mNW_Wrs9~q69 1F5w}ikc$?\hr䅤gzK=޽kVՇrߕZuMwNvr.gY4:/+6+~ܱ>y#P>r՛c dnWXޣR5H.W7 'k8bkm}WLѹ_O%ڬuőӧX~IXZXq4?u ޺m:.[_|9#/OƦ-D+F-gcq}Hqt.gr3v,(H'gHayey }_:d[m5;_1spqkx 9o[YX~~(-vX-^CR?#sbyy+\~~w3[8K򠹑c-@szKiX_\0pD9b{g+'_(zX#dwʿoN1lbe9b)}Z 1墸Z9Ե6/W{)3EV'pSN.95$_Nc ߨ*T=|_klX\H@ɽ[s9a0Ƿ\u Z紜~׾i [whemq0̎m\76#]Vpm;oc9񎻿5:Oc}x/}Y/*ѱXbŇFi7b8Y뷻ޝ~rﶵ45}w<`=G8۾bn2dӃaj# /%Xp8KkNgq?5'6jT97YKzWȋw=eܻۙW5wr9\ +eyZ-|S?a%$u%9޼1GI6/}l tox}ܪ]#l;ڂvO!qI/BmoT,kvYz<-.%"1F̯wMWYе^yX'k}7wwv]58z?צsJY&&m|ﴍ;vll^F;^{~xמ=7].wZ넘Ocnp1dXQ{[q +_bb|ړ:90.8` Fٞ.w=/.wK,'q@m$m!y&Y?z~lvtZV|br0aq/{.Wz9Z}+gul{Տ[ gjcp@hemƯP.+6 //ήuNk|+v}2< +wwQI*)Qr?[,|FsA~b֮w^un8 W|,ݼ>#wY/yxUvnS[L;6i:w?v]TgpV寙T>1&#̾ckoǴ>kt_.{s;:mqfw5\3ڀ 7;$Kټpjx2'fg5wc>md'ɕK8F-~h\f9"wH9.f\7J% +3ֹ:XNT (@50<#nTwh#49(;7~9q߮wj-/U ^ *[lAd[כ-!<|q%IFb +Rƹӷ7A FjL򽚯IܗLq^lKc\9*X]6սsצ1}е_]ZT8m AnA^>[wl T\%p'ϫϮ~$vn46ޜzc,FmcB>9,sgNKx>וg 1r{mV9;4_d/qYm]lLR`nؕ,DZg Ҏ콚̎I_&X}7|][!l:~ +dzjr7ywS +E3 >pk>'ϯq_[;ay_1>#218^R6 ˋc@7{-׳W4i^vykl $SSi6x}/Fi-9l;ٕwU;_qJ7ǿg)QG9bw }9-><87>5jÄ5lʭ dGm _qony֋Xbci ˘w(sW[<~ 1_>_}&߾yqw᧗G +k}.G؟koy-6WWO.#x]֙2wyngxz18_>`l^Klb}'֏`8u@1Z|]/?nqo[Nq>QYK<Xa9oNtwsR[| -u?wr2SPoq4ֱ+~ ӗVwln5g/KfOtm_q@=M,PGK6Xt='++?/V{ۭ3?0\*S~ڎbteƚW,~#<>pmE6.h^q>sv8|p3)+۠^m&c6'>,k$jZ6;ƕu$|=sӸ8|㳺[nK?׻izׯHndAZ[Pٰ10_*vhzCZ8ul\.ok}}?v[#i?g31&?vs\n=1ʳXuZiCϛ&4zcq,aԆ>c]zʵ]·y,c頍cYvc⾳}s}u~m3ܾp xw-ڢ{."&k}X`u86v v(vփM.X}|S1|y|͏%.XJo.,m,{p ..RL^,qk65Uwv<>]ߓ+^U~YX5,JGV/1c87fb/yk|OXvuul˻W[b}52wzŷ]Uξ>YN~9^-_'ؼ;H\ :6_W͍o fyAb⾎)\n֌JOOx-b*/l0J>be/+purٺm0@9V׿N5ʭk`LµkG' 1|Ik0mNhvr|wꢻ+ع[a&޷sK|mqjum%%_XqWb;_?mQO,V'5~6HN򜒍֔†h< l{!k9_yg۵%.ڪ7&vY8~l xXݱۇWsf4oiYƜaŗ揚!UԮ qߍcf6sLaqmBc,=qWNghW9ݔAo־;nlN\75B~ݣ9XŖIhcSU85^mzKAl-W3xc>Yxtcv`͔t맊nns޾汮뚿nZW嶕ڃmWW]H]7v1'S`D}cy^kH搾lu\? e^wߍqǍ36~}[ǭr6 ,|QhVɖj8w8|r18_}&m ym97wd̷M8aM}ϴ:o|[+p^YgmHo5^e809t,.Gx>]ǕU1 `%Gd;l]5[-к ['/}[:/˱|8%~'2DJemwY^(;vm)1T[k+pyq{^پ^h-@;M΍9wo_87k3i+W6?ݪT(,2_H|cN {MCҖO5a/|;o~N?ݽ3gr77k@Θuusk|?#v$k]X}띻r{u8U1KrPSz;p +EqrefBhݸFecjW8 ~T|>?s ,trzxmI%a/o^6}ؘ7gܽյ6ͽϵuP9G悺WК +G2ޱuzqhn;雿?}w|ǧoo۾>}|jozonTՖaq}Nsy\-vGyor28+~dz5Uݣxz|mm.Lk{zʨdu+-6\SBXq`xc7ww;8<0x89Tq^c/:<^~v]{S"Vښ|dr6f5/DOzslb˛ s|X[$]%wSLҶ_\s{)=ڣ5oå}.[>b{|/Z<;kŕݳ!_?G©dֲ7sO 9䯼y% k_X]N)9u1Ud|g-]bW轳9os_V7L}rrbTrnUrݯ,O"u z/+wcbr*4g775W= $|o;z}囸垯[vZ[:tAASυl z6ڇul-+=٬x9@[k ȷϟ1vѵF`\"k''P}ٮ讅{G:_aI͟5rl;g1GyI>Ztn׳`z/EORDmg n޵򒫑Pn⮯`m؍iY*1FԳܾo|>n&+f{Iʅ[NHi}xVҍ w0,^|bւXvL{cpX݋{]XK/.%wf̓NVOy&+.n^oo~˷|glp0}*gqBWy0u6'?7;7>NZCs8廊 77RZRǧC[L;?kT f`\ غ1wco\gRC4,h}-s0"qw ^և\A,fJܚ]C|^y8S^zA._eܵ[-[>U5’}Ń[[|o{M.ѵl+k [9/`sB/k+8Ƴ'6tq?e<yپ'[;hMF e0@m߸5&eɜW {EO׏a={[kkS +1{a 7C/[Eq+5Skb~LwjZ+&;2DI_GzSƁr?ԧڛ <9Xt>;~䒊?r Yak׷y k8S^kٶn w+tǖ1|qV^okmyUf,{VkX?Eᵯ9 y tIS"Y8`\?yqzQt\4[U7fy0st\x`x_27?*ggNZJua[Ӣ!G[nϖoUʟxyN&|nNG=eƄҷre+ztw}h\ɚ鑻̓?/_~'OOl-UKvXωwoԧ{Qx(k}oOZl_mM?N [Uנ][5v{zJ|j[3/8 ~,Wgm|zZ6Lk1Yl8}ظ+`.P7WHv/?/'\vGXصw}5QL`^;N3Z_VMw>v˟m 8^s_ :/vST_p7~]6ּr8[{m-u9]K4ٹ8=⡙ǽ +[xvrޜhmo7ֲIOTv ko#ڀuRGa~ǗöVlb|j+< GNrRט3U;Ĝ>onW{|]cBK6#eG槊7h_rw9W2wkTR[f7doGZq/ks7Ws}v`Ǥ.N&&k1ͳ^\^EFxy.rl}&1*<5W1 ZgsgX {|]\w9Hw ;ׯ9cڔw8^> v;gOw%CZ_-&ⵙ?o?l~~z| ֪^Vu$s_o=t>}Z/߮yuyZôn~ydٔr^ \7bً &_ĩ.ZlR/|Sle,>fx mr?[#]=uι6`sՇaX;&*`כmZ4_YNdPvRCw'Op1碘3; O'=Fk| +C19m2$;l׋69b l-dqZh]O)hle}cO6m{yvܬ./ɲ{6S8[Q?qaTcؘ-i/m3[#9Xn>^[|)~:3̻(W=1dט + +cPηdָuэp݆ͩw5ͧ{HƳ-elp qW>'yps-&#.:z/b~u̮;o`G/?g;i3sA_7pqcJޯ45zxLxwQf̓;.\8\q^k,>b'[lGe9[EM6l ƤS &uH1a-b"^ۘt9}VVek3vٖmٸW)n1A""hsI/xOb]_Y3՜[w;x{qDv][߼>o'(dj1y6sa'~{rۉO,I,[D\SP^G(RQ^]1VjwjT+ԵɍayO/\Y3a 1.ܡom0ysD.}.3mɇg[('ly1?ew|r>HF,.YrVu}~|}x38cb7YYu6{,W,E<ㅽ\p 6/1cœA1%(o {A}/\k:e8y":>֓7kTʏKz+ ZU}5Pmnr;^%>Dw]}!ۗ~mN/ޣom;n}Peq(ON^a}qNbR{?qh*rO5ͳbh9#ܵoLNjyx˭/s^ΫĻ̋^1c?ó cFcQ~srsl,k};O~3F\[h/s}#)x,S5CHNbWȃzzZb^5'7ν\I}x?1r $O-mo?/ Blpqڸwtܺ~<7G/oy u$#.ƸǗbx}W: \m n]1tso}a47a 2q ++<ۏbrr盳mEn6A~J8_rl C4N,Ecx~s&U\^ַRָucb0n9'kdil1Y%\TY:WfϋFj]?W^MJX΍p;>=?5Ju{6U;+ ;sڮk}4]ټK5yW.qK2d*3{C6k+"q=NVwXvk7lm[pC[3{ۜ"1=ZyuM߷56],f~z)'?]QeeKg'X;/nO*h5Cwys=ڜEeyƺcY.d⠛KsK _2@^NsWz2q f!' ʏ\oG}o^XWP܎do9Պo^_/_Wsμ'uBuOfDc(}QtƟzܜΥj^bbʩ8U7jrŰ_c/X۬\rM66on6-{ՏR~<1k {,XEד[b(Wo 1Gظn5ѵI=]>$f#+$j-pkg:L$槾x%ů͗>w_wׇ\L#Uw-˷^ zE\Z.aw)_$mh˙gל;sbx:;C'^fsߺVa{ qy#W6^o5ry:吿꨾][/}wbz\a~ɜW58gqgt5\k ߺ`wwߩ9vw\855b~[?q[9ɶfUr[0;*ck$Xmx[w\WmB{{lg[q/ߪe]7]1/c&k?f-g` 988n^/d]zrwà5w +׶-/vX7/k;N5#/GqcҍƓ ;3wcukw!yuNfd uywn߾r]k-]7E{dk7á M{w]Zo@^l]Gǔ|E&hͿƳٔWtxi ǃ,o9\֧<0)}/nI8ъ.q[7.|1|lseyHrߊkx|q mLHyM y S#?za:^wcT6β;Fiqp/G=ܱ1QG]Q^>Y⾍?IL/FQn➯_[ú>:~Tra楇nRzis%~Kh%ì[k||.+f{ɱ|ϽwWw;|Wq~)(*) 7XCe߮a~ء!Y.vw]/x]B툭'F9⬋9S ޷\I67PŽ׆bX}݈߉,G2{xmnsŬ#Ƈms-ɹY^Ǐ}MIDϼ0 r ﰿwl\ 1Ww9:k39o-un-Tk9J=mys6_*crll7OY=sc\{瓤qczu9YOqؖ;PvFZqi}.>Z_&\"&>/NުU}`Vy][gYP:G冋Z#O^aK?9Z㌕5GvIu,] |q$WK!۞~s:k8tvp?=dye\iJ%g Obڕ~!b˝9v3o-`>Z7yK1r9UqݵZjֻ;wxzDOa{w/b `=k(#n9_5ZoutZ)ijviʻ7!&#_yn 6;1<}V1?u~溽N*oXv\g8]MG;̯{[/~iU\5_݅n1`bwWq5m_kGeܽNֻm nsCc_c~|bi #`ccbbr_M=sfbۮ4rbȇ6x<{9ø̘o]{@~i4kK5&[ƴVD<4薇sP p C՝S by|ʮb]}sk7ŕhs5/46j'Nt[vLsLٲQ#6X>;s?Z|_dVsM iv֐ps[_:^kk;ulmuI>8xI묥d.pt[#'cǹk~•7݋s~\46PWgS`nZnM.7P\SԘX 1ylg,V#$X|$&q^Wggkv֎܅#}̕ 0Do~,.גݦ=?,of5{}{!w\{cesesYo"(Iy0rl ;V`"WOw1N*k;e./WcFgX|4jj${T؞YU:Vӝν:_k]Rh>kO'"ꊙ')*nd[k̻_q 4Cr'6>Q?Zn_N[-[;_lNJ_w|Kb ý1|j]G<_-oE9x9wrJVOc,Ϝղ~t:n CvhΓ7Z_?\M1qs:w.\&&\yWdOo/wva0uV6,? 90uǚ?eckggoX<+/+s^ך)3TϣpWWΑ//POƝ>l.Ӝx;-/?G} mvc2 Ysb<*M 8~qc t;nn.k] \}Vrn ۋSҋWipK}nԯz/Jݵ4w?=$joޫ>[ok/4t'̗Чֆ|?sosW;A>sx{w]>[|2d1֠08XRw|991)骭Bsrz呅Ie^kk-pcW>/1?c.͋)71v<{96l4+/JN~5]2Y{Gby(52x,Rmil{v?_|?F}.4ߩhng;|׿_˿^{fko8hK_bx6:b_O6?5q|vv97<᲼a@󃼼ۗ8b)5]獥7.&cwٱS/{~aMAk]kgRUj|tO9,H^|<@u+[6}]ϔG/mܐpņ:}XWdt[^Oc;)#}wMٹW];OkfmX.~栘O?QGkt ͥy mŕ_y?Lw1w}ܯ6~W; s[bmykOxζm!&Y37<񷾉c8ȭ^{Mk s]?»3R@x+ܽU1gWOg1sضvavr4TǣѳFі߹:xrtnӺozpXKO=@y{Հ88u|ޛeqYs8;Wx"XP1Ɵvf/2]^`kG婽/b}~^|Ojy6FVf ~n9n/7/kW?n_kwwzo۴G0@5c`Λ=g|;~bX{XW}՜v *S]6竇g{ʽ|OZ~atj.ֺAckE[}.0os11&.u;O?V{hWq/mX+{f߯{1/p}b[..gz_-V(^?&/~OW{|~^RJήR_=0o]7k Ԟ h*XUV\՚hؼŷ|K,xb'~(qbK^/'x[p?AoϹ8{m>yanxX||CHͱ:fpoG<3b~[=ppM4籸>#~~ڇzy~F13Ŀ7wNnN8*w jU-Fs{maBn|*I Eoqm=FV6|X[q`oeyuvaCP_ 7ӽ-Gi9P8rDxZ3XXkyf^Gi~=/n^/'7؊Ww}{ӚrsMkSG6jʦ껎1zgwva /k,|}ҸV/sŹ[+*_9&y_oyy5IV\]0g;nو[/{>bus_".ZmTO0}3{6^^'8#GQ|RӞC.Rr0wO~q樎ѳ}^xkf~:-61(4_ZfPZ&_yj }Xu5u7j^aߋ{?a/lyuA3[\j67v]lgsT 9]0~hoهrןv]'=V롚!jnKhv_<2lZ!}m}0m&r䢉}X|о^ntcY+8N/y#-/Pa'(''~(CJLxi9u{f {^6\S,nq=ͽS=vR ŵ?7'UQ+ewl=ll4&nNXս㓟?7taba`syyS#{^~TkV㶾fmA|1ux_knAg,+ZGo6l^pW|`lKsù`y;8h9=Znh9؜59w{>u{L~v)6ot'ddjNkLW5#K)ʥw1OEG[&񽘩g6:\{x7zK6NQT>bcP I`qr]1\MiΥV>[5Lb[Lw^TR 99\0;svԆov~}g뵘`krsl5^i)僇ѳl3;wj769/6^K&[/`>֯0_5LmkrôwZ͓≍Y󒭳" :ss',kmo;N9?ܙK?j~]K[W8_ܵ/ձ(F^lؼƪam"njNNbKV>z!̵)G|=V)OS|e`LJZe]ek l7}z~my!i#/Վ۝`w 78ݐ0l"uk['lN`sU#Y_S{Aɲ.ZS%GOMO>W؊́}ybib۸֯؜Vq5a2ޏy​ cKf2N m>b_<]yGLUpyw<{֟ Lf}_wltg Z|.s +,iTsZim8.w--6â^BK<:j]]\{9;u,4|uw!o.9=sjӷ[ΝR imsuabr\Ԗ|鞽|J Vk&sc .GcWx1N~}"}ۺ[ۡ_pkƍJY̎\?9 /6޸aۈ/~jsJ<էzã4uޟ8}#\? C&U]gMS>cj֏!_I,Pp}Oq}.\;k)-~ҜgojZ֏T?zb]Ƣ0V]s׭W2>,d}]Z׏W3 uTy/:LVgO}H?1`hb0b_j\Cilp{a!g_|}ک2߭q.('LLZ|5.yXvcw1xGXa תu]Ekfs:b˧kjMkePS'Y,;{;ln z>埆iw]rzΛckw{5skm.i:{l⁋-p5;vW|cT6-60;r_aɕyEb~aab6]O:~aaca^no=jk>zm G([{_auٶaBu'Vht/]ź8Znݮ߬Գ'WD.6+GvI:_؟x|;~=f&s滊}csDUkLJԟjO7K_}jwL9[Hl\myn0C:u44V_c-sqkKͩY~c]Dz]KBk2~oul0=_"_&],#^zKy4/lst1.{6Ɵ_9퓋6](I~XX \M?|$<00@u{+WdF=.mc7ݜo5|F}YmWwk to8>v\rI^^69al7ϗ?>mp'vm\b=7W}-KR?Isx^yr,7y +b_ױx{ {7_\و^;h|91Cqfzar?dśnuL^8ƞ-V,vi~81ƣ1.rLwfΧ;&n"Vb,޹ >rj 4˜W6ONl[k/k+MXܵ:fWEcL~$'jXllq57sыt={ߘ[֥VWb_@.޹aqd19r•WcgV#{7gubj1KNx[Ncz<þ,|6{=M3|滾1NjUe[g+Ve^;SSsMW.C9|޼e~5:Vǧ8֜#?>}cKs..Ƴ +AXD>N'1y9.@ok~UL w_\GNqh#?Ąkqs"ˍ-?ZcSkZ>b~>bꮼjn{Gx2rɭ?vl6R8G:b|buٺ7e{aڏg"r /m^8rE䜯Nvw6f}!'H;4|:8sڧٰ`q GW˺?ew.d-1-;qюWΖܗݗ~+Y-'^9YNskW\kܶj3oTFLZ8j4'ɓHp9/svLf:d}/V~bj +fwfGNFZXcIiқePX#L1>6ŷp>S;wbW2~4`7g]nƇ\/v~~'VYg-'FgN@/Fjb}w_b!rgzrZ:+xz;_:6kvX`6|(9yvݜ9)0?s%~E5zםs&WeըuޖgTe9Jn 6:~v }gMh ڮc=#[A?leAzLgO|D~jO +5d\լ5*foi#-7Mux|_jj+ϴ`8fvrD˾8C?>ϖ'_Yܯ>j"Nyw͛5ٺs|;H5#/̺kcnjYK붯}|rIfn#op&x_=+nt1ф Fbirz-|ax_:~޽;nvNVϡZb [w}mN/r{65serVV96Gn(ŝ#Ay~Od|b l=V3,+%<͍狅&$wO~9+uߌ#[:j4?&aNa>YkV߮lrV5,pEn6qzmzsګ\/SeٸZv3J o;qƧ:kb|v_ksLqQcE]ƌuqnޯ}uuv_qpΩmmO7_Rl\|w+zWRbw"\>5ĭ]湗iں[{^˟]ݽW[7Զyv +KRm6~Xiٚa-KR}Gq0QL2JqsԆ7NX\V xsa_5p~]xrkW/Οm{<Ә*N/ ׫湴2̛^Į'GϷb_Xj|{+WMꤔ#_װ͝W0Otl䃘g>޸o2ux67y\'M*\{kwW1R4vq-Vv ^i}s>0^='e6(PA-m߯FÍwyk4,yHщ;cx_bmAma瓻hX]a ~7_~mu̷^&x\ZRo9Q>uuJ0MɎej~^i8ռ\@ȹ +K+?_r wmi*^7=p욋oȺOwcDk}53W{Y3@2\ڜ[> Ǔ/K(Wb哷4ٱ7zkN˘DQCa]eFJs$7f ̷w"6Qq@kkqyvi7ah,c1/}'?U/luö_VM/_bʟo[1/O\ZPsV?yōsMAh^.Ӛy|s=#nյcc|p#}wǹ*q/׮3]me}~izP5X7ok#o[޹׆lr`"Gq!G./JK_1ٱvYD^an>j>֟ڃbz/|mvbLUđm̿ {[l.ih:_qk_iW5˱5fו[!ƻ\ X[jH{/ak|ʣ/l~߇gp/c׮ƨ^ڟwή#b7[w+hk~S`q8^Ǖ7\,GYt&nȞ֐i>+u(<Ι>[~z\r겭 RH۰!{f1N.^XLî]Zg_mfq^sbb Y%'6br2I*:nߗ|ȋҽ\o\[OߋS,ﴝc;_#xLmkGN榺ӐGs^~}'Q-|ncFS,[O!rq)jޫU/,7xI5JROxzrKq&穎] g}YQO]Lg.51_-0.I+Ν/NUO)1( s|í*ޗNZ\)õ++؟kb&{i7o3lͭӠ:scԚ]} {m'o7g5C{L=m0Ʋ_՛S񊷾?/N8tMmWlř"b7v~jmnZ]6Sxjc q^z]̫X5O__6=~:M4_1N9*Y7Kmq~2:y[#NS,-vsps['7'b5̋ _}ZrV>kayp>|Nr-wpS11R|}}kÊ+Ǜj62?3tk.O [?'kni5N\/vHX-"R,/t}ncy )[j0#m&|ck7E7N9N=jw/iLOy֓7gk/{勴s~Mkr8_*z[kžV+/ʷ(^+>˽[}W|o56G,ܽ=ju!>n~.ŗC>uU^])4~9>/gd[.F7ZPc{a1^:^}aǽ|0@q# (sc̸s6Gަ~_5Gœqi;Wz;Gn!&uE{W)3oZco1䭉Yoslp^?y~=ۭw˺.'y{u[ͷȳ <ڹ}k^\(ן з7e?CgΗK=wqˡO[Z<ݯBSu˙a;y5v ;/>yS.Ơ-ך>k9VJ*X8m_>__OMbS⧷Y ai|u W3>jg+|Uww]дmn˶T<1n_Z/Dz5P;SJ_ynb~/$o ËQΖ 'F}/nWLlk~bz8/\}WbFrJ†8ro~X^xp[sbsՐ>mސo<9kW}[6'i=#}ו3lvpƳ:~U߯7ܪY_clnK Ԛ:b_uQ!3oJӹz0i>qn.i$=wll 3t/M/ܰׯkQD[P?KJrsh=b" cɻؼ,1?u.WJ\Cxm- _˵ۜ'W`k|q6WpWm^u _qum`c /s+' j׈se3{o~9Qכ=G'n>/k9ۋm~r>Xj򡲟㗅Dž--W8M=M"8oEH}j->H7#A|榙/&)F%9_R9'bc)m& Gsλb[d49=1byJ˵RWm:cO/M֤gm.}p3svmQS<ճu|7]|PиO}szžsM&i +ük5~Y[W?Hn`vcpoyT[^׼k<4z疓k?Wz{-1W6/[ly}oT>O:k+X^}PGn>y?q[kOUW;gw-mrW5bƽ+(O~q8}Aͩ-k.|~lǼs6zͳ+cWX@Q<( '^йs#@xuH:-#o;xywۇ%>^W]}r^y_Ďz5D˗ao\7Էͯg븫}_vb~mN~N>[+ Kzsw1&xĵ[%n^qwbj:vWq_L9c[sLذ} \wӫߜݱ<奭MCx]h-.glE>z!zń7ֻ9]Lz^>6m6Fnr\^!9oPs/o}rV^zpl*C1xǤ5mh10͉zFr'v'%C-|☶ٻ9.o9G w9Y؀}5K_ǛSTS{s?>POiA7hs~ȿH_Oa5/{ybwk*5vZc=p(ȼpR9a_q֚Ӵs +FBD $t%K-Bܳt(ٙ4~kזWЕ+WK.-.\X+o+]xg:[9sf_s-gϞyN-o~P_ԡ7n,rÇ˃Vz)ɓ(?~BO>y;ٳgkgy75.䷔-ݻݻw[n`?7o.wY:Խ}Z5X'擵y9cH!]/Reu2A[mߘ{~պ yu$).}W$UΦl?w:N7s|=\T\//Jq̅r"ˤo+<"^I +xb-;e{g=Di[9PY[k=5矗.GY߿:th駟Vѣk!-)rSNg/@)kzZ~o~嗕Ϭ8p`={,[dYw9PFO e(sm!޽~zy͚1ځի˗; @9S3mA<=?\޽{_}vh[ >|ߑlIٶ}S_SF}[G҇O>}g~y 1Hg=D+3Ic+^Iʜcx}}ٱY!܋̫yeT=!]y ?|Y*&d^NĜ#[V.qmmIgԧv6g٠9S9k9Ff-o~ ΕiOR݁Y|ĉ 8ϵ#xQ_NeپGR `csϹ}2kSRM;j3ڍ0HUjߚz~ve&yvcsC<Ǥm\[W׿ԇuא=?l\MR s]'Per+ʹv+}^F7v{^_w΃'i11ў>v.D͸?O9Ç> ::&2U^bު\/e>C1_>y׽=uOeFW_D==Ur1 (~PCd_q.}ob_e mɷ؜8zı9f=zV#b)`M! yՖySmsfO1CK\ϳ@y<ʷDu5ȱQW,6߹n[ Zܹ^yT&T/Pjښ[sɉtL}vS^IՓbA۶m7+XR"ZgBՕu*N=㶰pY.+fM%=jL|UXmv^V\AN|C^\O~̥v`?!ןM$VP8xC=)V!xħy/&V rsqBqC ReyoĩG;4oߟm:UIA@zUreI*a}qIKy1{ڧf<ѩ+_oq6sQLL ޹/W^+GZo罅2 hG{zPL1Ą_*sb=iij6($Qܯ1ڢPmuiV_SqlړK +y{cnY8G9-봾c3ckG'V_ZImb&~7+UWgۨTLrښqsҗgG-~cnb[ncՉ}=ʔ>g w=T8),2mKby'#_J?u_Ÿg\?iSk=øwsC/c*4m +Cߤ?bJ )}?}\y+FeBk';מ+QW܉3w X`1 +yV̐o{g8bg[qDoOob&FvgD1@厩r9o/~Tyl֡ߌڊtM짺F*.1^fٺHZ߳wE:]Y"/hsM]\ʳ{\*Sʘ7~5i̝w}iYOs[ű!XQ;i/=x=]Y`m#' Rc8sAʳbR/`#zoQ&+YJ*V(~h,ݻSBQlm,OWz`8bCYƌ;IX&X +bY<@PSHX8C񻉃#F'?}bcf1VQZou2v;d5CgS.Fa,u +^`m/'O}=G?~Q1bzc\ }3qbR׶_##[=;9b;WcsM6k:e{)&7SX[x qۊcs<gw‍\j{6}S8k;b)WZeHOOfl1߲in?4*&YzbkնjB>hcθ-lY{G8~b]Ko+uMcMĊ}S抍5.CqxG743=ߍӖ|^S鯨g޽'}PdX R|"m*ό;ISwm{Kֿ_'5yvzNJ9<+!51.mbb[xoX;63˵^jG7M,VC[x1c}+'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Ɖ=K5oyWYDTj_oB(RHj1v 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%0De=4u9r6eP=%80leEaPY`Mmhs6J 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

    5pbRU}BC`Ywp;Mk@ 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

    1|sl?l6j>g}< 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Μ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

    >8{w_~h9d4-T<34Ky@y&sbp^j^q$1 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

    9Zp)i2} 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

    =Xv#cH)> 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

    @8L`D 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%0ACnPDhWZi5X?>jIXD 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 eV-m8wJAyxkZiA;ltQ3dm>SGO!FXXuw6rG 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

    + miniaudio +
    +

    + +

    An audio playback and capture library in a single source file.

    + +

    + discord + mastodon + reddit +

    + +

    + Features - + Examples - + Building - + Documentation - + Supported Platforms - + License +

    + +miniaudio is written in C with no dependencies except the standard library and should compile clean on all major +compilers without the need to install any additional development packages. All major desktop and mobile platforms +are supported. + + +Features +======== +- Simple build system with no external dependencies. +- Simple and flexible API. +- Low-level API for direct access to raw audio data. +- High-level API for sound management, mixing, effects and optional 3D spatialization. +- Flexible node graph system for advanced mixing and effect processing. +- Resource management for loading sound files. +- Decoding, with built-in support for WAV, FLAC and MP3, in addition to being able to plug in custom decoders. +- Encoding (WAV only). +- Data conversion. +- Resampling, including custom resamplers. +- Channel mapping. +- Basic generation of waveforms and noise. +- Basic effects and filters. + +Refer to the [Programming Manual](https://miniaud.io/docs/manual/) for a more complete description of +available features in miniaudio. + + +Examples +======== + +This example shows one way to play a sound using the high level API. + +```c +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" + +#include + +int main() +{ + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return -1; + } + + ma_engine_play_sound(&engine, "sound.wav", NULL); + + printf("Press Enter to quit..."); + getchar(); + + ma_engine_uninit(&engine); + + return 0; +} +``` + +This example shows how to decode and play a sound using the low level API. + +```c +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" + +#include + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData; + if (pDecoder == NULL) { + return; + } + + ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, NULL); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder decoder; + ma_device_config deviceConfig; + ma_device device; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + result = ma_decoder_init_file(argv[1], NULL, &decoder); + if (result != MA_SUCCESS) { + return -2; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = decoder.outputFormat; + deviceConfig.playback.channels = decoder.outputChannels; + deviceConfig.sampleRate = decoder.outputSampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &decoder; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + ma_decoder_uninit(&decoder); + return -3; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + return -4; + } + + printf("Press Enter to quit..."); + getchar(); + + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + + return 0; +} +``` + +More examples can be found in the [examples](examples) folder or online here: https://miniaud.io/docs/examples/ + + +Building +======== +Do the following in one source file: +```c +#define MINIAUDIO_IMPLEMENTATION +#include "miniaudio.h" +``` +Then just compile. There's no need to install any dependencies. On Windows and macOS there's no need to link +to anything. On Linux just link to `-lpthread`, `-lm` and `-ldl`. On BSD just link to `-lpthread` and `-lm`. +On iOS you need to compile as Objective-C. + +If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, etc. you +need to link with `-latomic`. + +If you prefer separate .h and .c files, you can find a split version of miniaudio in the extras/miniaudio_split +folder. From here you can use miniaudio as a traditional .c and .h library - just add miniaudio.c to your source +tree like any other source file and include miniaudio.h like a normal header. If you prefer compiling as a +single translation unit (AKA unity builds), you can just #include the .c file in your main source file: +```c +#include "miniaudio.c" +``` +Note that the split version is auto-generated using a tool and is based on the main file in the root directory. +If you want to contribute, please make the change in the main file. + +ABI compatibility is not guaranteed between versions so take care if compiling as a DLL/SO. The suggested way +to integrate miniaudio is by adding it directly to your source tree. + + +Documentation +============= +Online documentation can be found here: https://miniaud.io/docs/ + +Documentation can also be found at the top of [miniaudio.h](https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h) +which is always the most up-to-date and authoritive source of information on how to use miniaudio. All other +documentation is generated from this in-code documentation. + + +Supported Platforms +=================== +- Windows +- macOS, iOS +- Linux +- FreeBSD / OpenBSD / NetBSD +- Android +- Raspberry Pi +- Emscripten / HTML5 + +miniaudio should compile clean on other platforms, but it will not include any support for playback or capture +by default. To support that, you would need to implement a custom backend. You can do this without needing to +modify the miniaudio source code. See the [custom_backend](examples/custom_backend.c) example. + +Backends +-------- +- WASAPI +- DirectSound +- WinMM +- Core Audio (Apple) +- ALSA +- PulseAudio +- JACK +- sndio (OpenBSD) +- audio(4) (NetBSD and OpenBSD) +- OSS (FreeBSD) +- AAudio (Android 8.0+) +- OpenSL|ES (Android only) +- Web Audio (Emscripten) +- Null (Silence) +- Custom + + +License +======= +Your choice of either public domain or [MIT No Attribution](https://github.com/aws/mit-0). diff --git a/thirdparty/miniaudio/examples/build/README.md b/thirdparty/miniaudio/examples/build/README.md new file mode 100644 index 0000000..e0b0b0c --- /dev/null +++ b/thirdparty/miniaudio/examples/build/README.md @@ -0,0 +1,23 @@ +Examples +-------- + gcc ../simple_playback.c -o bin/simple_playback -ldl -lm -lpthread + gcc ../simple_playback.c -o bin/simple_playback -ldl -lm -lpthread -Wall -Wextra -Wpedantic -std=c89 + +Emscripten +---------- +On Windows, you need to move into the build and run emsdk_env.bat from a command prompt using an absolute +path like "C:\emsdk\emsdk_env.bat". Note that PowerShell doesn't work for me for some reason. Examples: + + emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html + emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html -s WASM=0 -Wall -Wextra + +To compile with support for Audio Worklets: + + emcc ../simple_playback_sine.c -o bin/simple_playback_sine.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +If you output WASM it may not work when running the web page locally. To test you can run with something +like this: + + emrun ./bin/simple_playback_sine.html + +If you want to see stdout on the command line when running from emrun, add `--emrun` to your emcc command. \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/custom_backend.c b/thirdparty/miniaudio/examples/custom_backend.c new file mode 100644 index 0000000..e30cd46 --- /dev/null +++ b/thirdparty/miniaudio/examples/custom_backend.c @@ -0,0 +1,740 @@ +/* +This example show how a custom backend can be implemented. + +This implements a full-featured SDL2 backend. It's intentionally built using the same paradigms as the built-in backends in order to make +it suitable as a solid basis for a custom implementation. The SDL2 backend can be disabled with MA_NO_SDL, exactly like the build-in +backends. It supports both runtime and compile-time linking and respects the MA_NO_RUNTIME_LINKING option. It also works on Emscripten +which requires the `-s USE_SDL=2` option. + +There may be times where you want to support more than one custom backend. This example has been designed to make it easy to plug-in extra +custom backends without needing to modify any of the base miniaudio initialization code. A custom context structure is declared called +`ma_context_ex`. The first member of this structure is a `ma_context` object which allows it to be cast between the two. The same is done +for devices, which is called `ma_device_ex`. In these structures there is a section for each custom backend, which in this example is just +SDL. These are only enabled at compile time if `MA_SUPPORT_SDL` is defined, which it always is in this example (you may want to have some +logic which more intelligently enables or disables SDL support). + +To use a custom backend, at a minimum you must set the `custom.onContextInit()` callback in the context config. You do not need to set the +other callbacks, but if you don't, you must set them in the implementation of the `onContextInit()` callback which is done via an output +parameter. This is the approach taken by this example because it's the simplest way to support multiple custom backends. The idea is that +the `onContextInit()` callback is set to a generic "loader", which then calls out to a backend-specific implementation which then sets the +remaining callbacks if it is successfully initialized. + +Custom backends are identified with the `ma_backend_custom` backend type. For the purpose of demonstration, this example only uses the +`ma_backend_custom` backend type because otherwise the built-in backends would always get chosen first and none of the code for the custom +backends would actually get hit. By default, the `ma_backend_custom` backend is the lowest priority backend, except for `ma_backend_null`. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#ifdef __EMSCRIPTEN__ +#include + +void main_loop__em() +{ +} +#endif + +/* Support SDL on everything. */ +#define MA_SUPPORT_SDL + +/* +Only enable SDL if it's hasn't been explicitly disabled (MA_NO_SDL) or enabled (MA_ENABLE_SDL with +MA_ENABLE_ONLY_SPECIFIC_BACKENDS) and it's supported at compile time (MA_SUPPORT_SDL). +*/ +#if defined(MA_SUPPORT_SDL) && !defined(MA_NO_SDL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SDL)) + #define MA_HAS_SDL +#endif + + +typedef struct +{ + ma_context context; /* Make this the first member so we can cast between ma_context and ma_context_ex. */ +#if defined(MA_SUPPORT_SDL) + struct + { + ma_handle hSDL; /* A handle to the SDL2 shared object. We dynamically load function pointers at runtime so we can avoid linking. */ + ma_proc SDL_InitSubSystem; + ma_proc SDL_QuitSubSystem; + ma_proc SDL_GetNumAudioDevices; + ma_proc SDL_GetAudioDeviceName; + ma_proc SDL_CloseAudioDevice; + ma_proc SDL_OpenAudioDevice; + ma_proc SDL_PauseAudioDevice; + } sdl; +#endif +} ma_context_ex; + +typedef struct +{ + ma_device device; /* Make this the first member so we can cast between ma_device and ma_device_ex. */ +#if defined(MA_SUPPORT_SDL) + struct + { + int deviceIDPlayback; + int deviceIDCapture; + } sdl; +#endif +} ma_device_ex; + + + +#if defined(MA_HAS_SDL) + /* SDL headers are necessary if using compile-time linking. */ + #ifdef MA_NO_RUNTIME_LINKING + #ifdef __has_include + #ifdef MA_EMSCRIPTEN + #if !__has_include() + #undef MA_HAS_SDL + #endif + #else + #if !__has_include() + #undef MA_HAS_SDL + #endif + #endif + #endif + #endif +#endif + + +#if defined(MA_HAS_SDL) +#define MA_SDL_INIT_AUDIO 0x00000010 +#define MA_AUDIO_U8 0x0008 +#define MA_AUDIO_S16 0x8010 +#define MA_AUDIO_S32 0x8020 +#define MA_AUDIO_F32 0x8120 +#define MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001 +#define MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002 +#define MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 +#define MA_SDL_AUDIO_ALLOW_ANY_CHANGE (MA_SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | MA_SDL_AUDIO_ALLOW_FORMAT_CHANGE | MA_SDL_AUDIO_ALLOW_CHANNELS_CHANGE) + +/* If we are linking at compile time we'll just #include SDL.h. Otherwise we can just redeclare some stuff to avoid the need for development packages to be installed. */ +#ifdef MA_NO_RUNTIME_LINKING + #define SDL_MAIN_HANDLED + #ifdef MA_EMSCRIPTEN + #include + #else + #include + #endif + + typedef SDL_AudioCallback MA_SDL_AudioCallback; + typedef SDL_AudioSpec MA_SDL_AudioSpec; + typedef SDL_AudioFormat MA_SDL_AudioFormat; + typedef SDL_AudioDeviceID MA_SDL_AudioDeviceID; +#else + typedef void (* MA_SDL_AudioCallback)(void* userdata, ma_uint8* stream, int len); + typedef ma_uint16 MA_SDL_AudioFormat; + typedef ma_uint32 MA_SDL_AudioDeviceID; + + typedef struct MA_SDL_AudioSpec + { + int freq; + MA_SDL_AudioFormat format; + ma_uint8 channels; + ma_uint8 silence; + ma_uint16 samples; + ma_uint16 padding; + ma_uint32 size; + MA_SDL_AudioCallback callback; + void* userdata; + } MA_SDL_AudioSpec; +#endif + +typedef int (* MA_PFN_SDL_InitSubSystem)(ma_uint32 flags); +typedef void (* MA_PFN_SDL_QuitSubSystem)(ma_uint32 flags); +typedef int (* MA_PFN_SDL_GetNumAudioDevices)(int iscapture); +typedef const char* (* MA_PFN_SDL_GetAudioDeviceName)(int index, int iscapture); +typedef void (* MA_PFN_SDL_CloseAudioDevice)(MA_SDL_AudioDeviceID dev); +typedef MA_SDL_AudioDeviceID (* MA_PFN_SDL_OpenAudioDevice)(const char* device, int iscapture, const MA_SDL_AudioSpec* desired, MA_SDL_AudioSpec* obtained, int allowed_changes); +typedef void (* MA_PFN_SDL_PauseAudioDevice)(MA_SDL_AudioDeviceID dev, int pause_on); + +MA_SDL_AudioFormat ma_format_to_sdl(ma_format format) +{ + switch (format) + { + case ma_format_unknown: return 0; + case ma_format_u8: return MA_AUDIO_U8; + case ma_format_s16: return MA_AUDIO_S16; + case ma_format_s24: return MA_AUDIO_S32; /* Closest match. */ + case ma_format_s32: return MA_AUDIO_S32; + case ma_format_f32: return MA_AUDIO_F32; + default: return 0; + } +} + +ma_format ma_format_from_sdl(MA_SDL_AudioFormat format) +{ + switch (format) + { + case MA_AUDIO_U8: return ma_format_u8; + case MA_AUDIO_S16: return ma_format_s16; + case MA_AUDIO_S32: return ma_format_s32; + case MA_AUDIO_F32: return ma_format_f32; + default: return ma_format_unknown; + } +} + +static ma_result ma_context_enumerate_devices__sdl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_context_ex* pContextEx = (ma_context_ex*)pContext; + ma_bool32 isTerminated = MA_FALSE; + ma_bool32 cbResult; + int iDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback */ + if (!isTerminated) { + int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextEx->sdl.SDL_GetNumAudioDevices)(0); + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + + deviceInfo.id.custom.i = iDevice; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(iDevice, 0), (size_t)-1); + + if (iDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + + /* Capture */ + if (!isTerminated) { + int deviceCount = ((MA_PFN_SDL_GetNumAudioDevices)pContextEx->sdl.SDL_GetNumAudioDevices)(1); + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + + deviceInfo.id.custom.i = iDevice; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(iDevice, 1), (size_t)-1); + + if (iDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__sdl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_context_ex* pContextEx = (ma_context_ex*)pContext; + +#if !defined(__EMSCRIPTEN__) + MA_SDL_AudioSpec desiredSpec; + MA_SDL_AudioSpec obtainedSpec; + MA_SDL_AudioDeviceID tempDeviceID; + const char* pDeviceName; +#endif + + MA_ASSERT(pContext != NULL); + + if (pDeviceID == NULL) { + if (deviceType == ma_device_type_playback) { + pDeviceInfo->id.custom.i = 0; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + pDeviceInfo->id.custom.i = 0; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } else { + pDeviceInfo->id.custom.i = pDeviceID->custom.i; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1), (size_t)-1); + } + + if (pDeviceInfo->id.custom.i == 0) { + pDeviceInfo->isDefault = MA_TRUE; + } + + /* + To get an accurate idea on the backend's native format we need to open the device. Not ideal, but it's the only way. An + alternative to this is to report all channel counts, sample rates and formats, but that doesn't offer a good representation + of the device's _actual_ ideal format. + + Note: With Emscripten, it looks like non-zero values need to be specified for desiredSpec. Whatever is specified in + desiredSpec will be used by SDL since it uses it just does it's own format conversion internally. Therefore, from what + I can tell, there's no real way to know the device's actual format which means I'm just going to fall back to the full + range of channels and sample rates on Emscripten builds. + */ +#if defined(__EMSCRIPTEN__) + /* Good practice to prioritize the best format first so that the application can use the first data format as their chosen one if desired. */ + pDeviceInfo->nativeDataFormatCount = 3; + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; + pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channel counts supported. */ + pDeviceInfo->nativeDataFormats[0].sampleRate = 0; /* All sample rates supported. */ + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormats[1].format = ma_format_s32; + pDeviceInfo->nativeDataFormats[1].channels = 0; /* All channel counts supported. */ + pDeviceInfo->nativeDataFormats[1].sampleRate = 0; /* All sample rates supported. */ + pDeviceInfo->nativeDataFormats[1].flags = 0; + pDeviceInfo->nativeDataFormats[2].format = ma_format_u8; + pDeviceInfo->nativeDataFormats[2].channels = 0; /* All channel counts supported. */ + pDeviceInfo->nativeDataFormats[2].sampleRate = 0; /* All sample rates supported. */ + pDeviceInfo->nativeDataFormats[2].flags = 0; +#else + MA_ZERO_MEMORY(&desiredSpec, sizeof(desiredSpec)); + + pDeviceName = NULL; + if (pDeviceID != NULL) { + pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDeviceID->custom.i, (deviceType == ma_device_type_playback) ? 0 : 1); + } + + tempDeviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextEx->sdl.SDL_OpenAudioDevice)(pDeviceName, (deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (tempDeviceID == 0) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to open SDL device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + ((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(tempDeviceID); + + /* Only reporting a single native data format. It'll be whatever SDL decides is the best. */ + pDeviceInfo->nativeDataFormatCount = 1; + pDeviceInfo->nativeDataFormats[0].format = ma_format_from_sdl(obtainedSpec.format); + pDeviceInfo->nativeDataFormats[0].channels = obtainedSpec.channels; + pDeviceInfo->nativeDataFormats[0].sampleRate = obtainedSpec.freq; + pDeviceInfo->nativeDataFormats[0].flags = 0; + + /* If miniaudio does not support the format, just use f32 as the native format (SDL will do the necessary conversions for us). */ + if (pDeviceInfo->nativeDataFormats[0].format == ma_format_unknown) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; + } +#endif /* __EMSCRIPTEN__ */ + + return MA_SUCCESS; +} + + +void ma_audio_callback_capture__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData; + + MA_ASSERT(pDeviceEx != NULL); + + ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, NULL, pBuffer, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.capture.internalFormat, pDeviceEx->device.capture.internalChannels)); +} + +void ma_audio_callback_playback__sdl(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pUserData; + + MA_ASSERT(pDeviceEx != NULL); + + ma_device_handle_backend_data_callback((ma_device*)pDeviceEx, pBuffer, NULL, (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(pDeviceEx->device.playback.internalFormat, pDeviceEx->device.playback.internalChannels)); +} + +static ma_result ma_device_init_internal__sdl(ma_device_ex* pDeviceEx, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor) +{ + ma_context_ex* pContextEx = (ma_context_ex*)pDeviceEx->device.pContext; + MA_SDL_AudioSpec desiredSpec; + MA_SDL_AudioSpec obtainedSpec; + const char* pDeviceName; + int deviceID; + + MA_ASSERT(pDeviceEx != NULL); + MA_ASSERT(pDescriptor != NULL); + + /* + SDL is a little bit awkward with specifying the buffer size, You need to specify the size of the buffer in frames, but since we may + have requested a period size in milliseconds we'll need to convert, which depends on the sample rate. But there's a possibility that + the sample rate just set to 0, which indicates that the native sample rate should be used. There's no practical way to calculate this + that I can think of right now so I'm just using MA_DEFAULT_SAMPLE_RATE. + */ + if (pDescriptor->sampleRate == 0) { + pDescriptor->sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + /* + When determining the period size, you need to take defaults into account. This is how the size of the period should be determined. + + 1) If periodSizeInFrames is not 0, use periodSizeInFrames; else + 2) If periodSizeInMilliseconds is not 0, use periodSizeInMilliseconds; else + 3) If both periodSizeInFrames and periodSizeInMilliseconds is 0, use the backend's default. If the backend does not allow a default + buffer size, use a default value of MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY or + MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE depending on the value of pConfig->performanceProfile. + + Note that options 2 and 3 require knowledge of the sample rate in order to convert it to a frame count. You should try to keep the + calculation of the period size as accurate as possible, but sometimes it's just not practical so just use whatever you can. + + A helper function called ma_calculate_buffer_size_in_frames_from_descriptor() is available to do all of this for you which is what + we'll be using here. + */ + pDescriptor->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile); + + /* SDL wants the buffer size to be a power of 2 for some reason. */ + if (pDescriptor->periodSizeInFrames > 32768) { + pDescriptor->periodSizeInFrames = 32768; + } else { + pDescriptor->periodSizeInFrames = ma_next_power_of_2(pDescriptor->periodSizeInFrames); + } + + + /* We now have enough information to set up the device. */ + MA_ZERO_OBJECT(&desiredSpec); + desiredSpec.freq = (int)pDescriptor->sampleRate; + desiredSpec.format = ma_format_to_sdl(pDescriptor->format); + desiredSpec.channels = (ma_uint8)pDescriptor->channels; + desiredSpec.samples = (ma_uint16)pDescriptor->periodSizeInFrames; + desiredSpec.callback = (pConfig->deviceType == ma_device_type_capture) ? ma_audio_callback_capture__sdl : ma_audio_callback_playback__sdl; + desiredSpec.userdata = pDeviceEx; + + /* We'll fall back to f32 if we don't have an appropriate mapping between SDL and miniaudio. */ + if (desiredSpec.format == 0) { + desiredSpec.format = MA_AUDIO_F32; + } + + pDeviceName = NULL; + if (pDescriptor->pDeviceID != NULL) { + pDeviceName = ((MA_PFN_SDL_GetAudioDeviceName)pContextEx->sdl.SDL_GetAudioDeviceName)(pDescriptor->pDeviceID->custom.i, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1); + } + + deviceID = ((MA_PFN_SDL_OpenAudioDevice)pContextEx->sdl.SDL_OpenAudioDevice)(pDeviceName, (pConfig->deviceType == ma_device_type_playback) ? 0 : 1, &desiredSpec, &obtainedSpec, MA_SDL_AUDIO_ALLOW_ANY_CHANGE); + if (deviceID == 0) { + ma_log_postf(ma_device_get_log((ma_device*)pDeviceEx), MA_LOG_LEVEL_ERROR, "Failed to open SDL2 device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + if (pConfig->deviceType == ma_device_type_playback) { + pDeviceEx->sdl.deviceIDPlayback = deviceID; + } else { + pDeviceEx->sdl.deviceIDCapture = deviceID; + } + + /* The descriptor needs to be updated with our actual settings. */ + pDescriptor->format = ma_format_from_sdl(obtainedSpec.format); + pDescriptor->channels = obtainedSpec.channels; + pDescriptor->sampleRate = (ma_uint32)obtainedSpec.freq; + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); + pDescriptor->periodSizeInFrames = obtainedSpec.samples; + pDescriptor->periodCount = 1; /* SDL doesn't use the notion of period counts, so just set to 1. */ + + return MA_SUCCESS; +} + +static ma_result ma_device_init__sdl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; + ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* SDL does not support loopback mode, so must return MA_DEVICE_TYPE_NOT_SUPPORTED if it's requested. */ + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_internal__sdl(pDeviceEx, pConfig, pDescriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_internal__sdl(pDeviceEx, pConfig, pDescriptorPlayback); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + ((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture); + } + + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__sdl(ma_device* pDevice) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; + ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_CloseAudioDevice)pContextEx->sdl.SDL_CloseAudioDevice)(pDeviceEx->sdl.deviceIDCapture); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__sdl(ma_device* pDevice) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; + ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 0); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDPlayback, 0); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__sdl(ma_device* pDevice) +{ + ma_device_ex* pDeviceEx = (ma_device_ex*)pDevice; + ma_context_ex* pContextEx = (ma_context_ex*)pDevice->pContext; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDCapture, 1); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_SDL_PauseAudioDevice)pContextEx->sdl.SDL_PauseAudioDevice)(pDeviceEx->sdl.deviceIDPlayback, 1); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__sdl(ma_context* pContext) +{ + ma_context_ex* pContextEx = (ma_context_ex*)pContext; + + MA_ASSERT(pContext != NULL); + + ((MA_PFN_SDL_QuitSubSystem)pContextEx->sdl.SDL_QuitSubSystem)(MA_SDL_INIT_AUDIO); + + /* Close the handle to the SDL shared object last. */ + ma_dlclose(ma_context_get_log(pContext), pContextEx->sdl.hSDL); + pContextEx->sdl.hSDL = NULL; + + return MA_SUCCESS; +} + +static ma_result ma_context_init__sdl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_context_ex* pContextEx = (ma_context_ex*)pContext; + int resultSDL; + +#ifndef MA_NO_RUNTIME_LINKING + /* We'll use a list of possible shared object names for easier extensibility. */ + size_t iName; + const char* pSDLNames[] = { +#if defined(_WIN32) + "SDL2.dll" +#elif defined(__APPLE__) + "SDL2.framework/SDL2" +#else + "libSDL2-2.0.so.0" +#endif + }; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + /* Check if we have SDL2 installed somewhere. If not it's not usable and we need to abort. */ + for (iName = 0; iName < ma_countof(pSDLNames); iName += 1) { + pContextEx->sdl.hSDL = ma_dlopen(ma_context_get_log(pContext), pSDLNames[iName]); + if (pContextEx->sdl.hSDL != NULL) { + break; + } + } + + if (pContextEx->sdl.hSDL == NULL) { + return MA_NO_BACKEND; /* SDL2 could not be loaded. */ + } + + /* Now that we have the handle to the shared object we can go ahead and load some function pointers. */ + pContextEx->sdl.SDL_InitSubSystem = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_InitSubSystem"); + pContextEx->sdl.SDL_QuitSubSystem = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_QuitSubSystem"); + pContextEx->sdl.SDL_GetNumAudioDevices = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_GetNumAudioDevices"); + pContextEx->sdl.SDL_GetAudioDeviceName = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_GetAudioDeviceName"); + pContextEx->sdl.SDL_CloseAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_CloseAudioDevice"); + pContextEx->sdl.SDL_OpenAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_OpenAudioDevice"); + pContextEx->sdl.SDL_PauseAudioDevice = ma_dlsym(ma_context_get_log(pContext), pContextEx->sdl.hSDL, "SDL_PauseAudioDevice"); +#else + pContextEx->sdl.SDL_InitSubSystem = (ma_proc)SDL_InitSubSystem; + pContextEx->sdl.SDL_QuitSubSystem = (ma_proc)SDL_QuitSubSystem; + pContextEx->sdl.SDL_GetNumAudioDevices = (ma_proc)SDL_GetNumAudioDevices; + pContextEx->sdl.SDL_GetAudioDeviceName = (ma_proc)SDL_GetAudioDeviceName; + pContextEx->sdl.SDL_CloseAudioDevice = (ma_proc)SDL_CloseAudioDevice; + pContextEx->sdl.SDL_OpenAudioDevice = (ma_proc)SDL_OpenAudioDevice; + pContextEx->sdl.SDL_PauseAudioDevice = (ma_proc)SDL_PauseAudioDevice; +#endif /* MA_NO_RUNTIME_LINKING */ + + resultSDL = ((MA_PFN_SDL_InitSubSystem)pContextEx->sdl.SDL_InitSubSystem)(MA_SDL_INIT_AUDIO); + if (resultSDL != 0) { + ma_dlclose(ma_context_get_log(pContext), pContextEx->sdl.hSDL); + return MA_ERROR; + } + + /* + The last step is to make sure the callbacks are set properly in `pCallbacks`. Internally, miniaudio will copy these callbacks into the + context object and then use them for then on for calling into our custom backend. + */ + pCallbacks->onContextInit = ma_context_init__sdl; + pCallbacks->onContextUninit = ma_context_uninit__sdl; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sdl; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sdl; + pCallbacks->onDeviceInit = ma_device_init__sdl; + pCallbacks->onDeviceUninit = ma_device_uninit__sdl; + pCallbacks->onDeviceStart = ma_device_start__sdl; + pCallbacks->onDeviceStop = ma_device_stop__sdl; + + return MA_SUCCESS; +} +#endif /* MA_HAS_SDL */ + + +/* +This is our custom backend "loader". All this does is attempts to initialize our custom backends in the order they are listed. The first +one to successfully initialize is the one that's chosen. In this example we're just listing them statically, but you can use whatever logic +you want to handle backend selection. + +This is used as the onContextInit() callback in the context config. +*/ +static ma_result ma_context_init__custom_loader(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result = MA_NO_BACKEND; + + /* Silence some unused parameter warnings just in case no custom backends are enabled. */ + (void)pContext; + (void)pCallbacks; + + /* SDL. */ +#if !defined(MA_NO_SDL) + if (result != MA_SUCCESS) { + result = ma_context_init__sdl(pContext, pConfig, pCallbacks); + } +#endif + + /* ... plug in any other custom backends here ... */ + + /* If we have a success result we have initialized a backend. Otherwise we need to tell miniaudio about the error so it can skip over our custom backends. */ + return result; +} + + +/* +Main program starts here. +*/ +#define DEVICE_FORMAT ma_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 48000 + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); + + if (pDevice->type == ma_device_type_playback) { + ma_waveform* pSineWave; + + pSineWave = (ma_waveform*)pDevice->pUserData; + MA_ASSERT(pSineWave != NULL); + + ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL); + } + + if (pDevice->type == ma_device_type_duplex) { + ma_copy_pcm_frames(pOutput, pInput, frameCount, pDevice->playback.format, pDevice->playback.channels); + } +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_context_config contextConfig; + ma_context_ex context; + ma_device_config deviceConfig; + ma_device_ex device; + ma_waveform_config sineWaveConfig; + ma_waveform sineWave; + + /* + We're just using ma_backend_custom in this example for demonstration purposes, but a more realistic use case would probably want to include + other backends as well for robustness. + */ + ma_backend backends[] = { + ma_backend_custom + }; + + /* + To implement a custom backend you need to implement the callbacks in the "custom" member of the context config. The only mandatory + callback required at this point is the onContextInit() callback. If you do not set the other callbacks, you must set them in + onContextInit() by setting them on the `pCallbacks` parameter. + + The way we're doing it in this example enables us to easily plug in multiple custom backends. What we do is set the onContextInit() + callback to a generic "loader" function (ma_context_init__custom_loader() in this example), which then calls out to backend-specific + context initialization routines, one of which will be for SDL. That way, if for example we wanted to add support for another backend, + we don't need to touch this part of the code. Instead we add logic to ma_context_init__custom_loader() to choose the most appropriate + custom backend. That will then fill out the other callbacks appropriately. + */ + contextConfig = ma_context_config_init(); + contextConfig.custom.onContextInit = ma_context_init__custom_loader; + + result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &contextConfig, (ma_context*)&context); + if (result != MA_SUCCESS) { + return -1; + } + + /* In playback mode we're just going to play a sine wave. */ + sineWaveConfig = ma_waveform_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, ma_waveform_type_sine, 0.2, 220); + ma_waveform_init(&sineWaveConfig, &sineWave); + + /* The device is created exactly as per normal. */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &sineWave; + + result = ma_device_init((ma_context*)&context, &deviceConfig, (ma_device*)&device); + if (result != MA_SUCCESS) { + ma_context_uninit((ma_context*)&context); + return -1; + } + + + printf("Device Name: %s\n", ((ma_device*)&device)->playback.name); + + if (ma_device_start((ma_device*)&device) != MA_SUCCESS) { + ma_device_uninit((ma_device*)&device); + ma_context_uninit((ma_context*)&context); + return -5; + } + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(main_loop__em, 0, 1); +#else + printf("Press Enter to quit...\n"); + getchar(); +#endif + + ma_device_uninit((ma_device*)&device); + ma_context_uninit((ma_context*)&context); + + (void)argc; + (void)argv; + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/custom_decoder.c b/thirdparty/miniaudio/examples/custom_decoder.c new file mode 100644 index 0000000..b838498 --- /dev/null +++ b/thirdparty/miniaudio/examples/custom_decoder.c @@ -0,0 +1,270 @@ +/* +Demonstrates how to implement a custom decoder. + +This example implements two custom decoders: + + * Vorbis via libvorbis + * Opus via libopus + +A custom decoder must implement a data source. In this example, the libvorbis data source is called +`ma_libvorbis` and the Opus data source is called `ma_libopus`. These two objects are compatible +with the `ma_data_source` APIs and can be taken straight from this example and used in real code. + +The custom decoding data sources (`ma_libvorbis` and `ma_libopus` in this example) are connected to +the decoder via the decoder config (`ma_decoder_config`). You need to implement a vtable for each +of your custom decoders. See `ma_decoding_backend_vtable` for the functions you need to implement. +The `onInitFile`, `onInitFileW` and `onInitMemory` functions are optional. +*/ +#define MA_NO_VORBIS /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */ +#define MA_NO_OPUS /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" +#include "../extras/miniaudio_libvorbis.h" +#include "../extras/miniaudio_libopus.h" + +#include + +static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + ma_libvorbis_uninit(pVorbis, pAllocationCallbacks); + ma_free(pVorbis, pAllocationCallbacks); +} + +static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis = +{ + ma_decoding_backend_init__libvorbis, + ma_decoding_backend_init_file__libvorbis, + NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__libvorbis +}; + + + +static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libopus* pOpus; + + (void)pUserData; + + pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks); + if (pOpus == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus); + if (result != MA_SUCCESS) { + ma_free(pOpus, pAllocationCallbacks); + return result; + } + + *ppBackend = pOpus; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libopus* pOpus; + + (void)pUserData; + + pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks); + if (pOpus == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus); + if (result != MA_SUCCESS) { + ma_free(pOpus, pAllocationCallbacks); + return result; + } + + *ppBackend = pOpus; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_libopus* pOpus = (ma_libopus*)pBackend; + + (void)pUserData; + + ma_libopus_uninit(pOpus, pAllocationCallbacks); + ma_free(pOpus, pAllocationCallbacks); +} + +static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_libopus* pOpus = (ma_libopus*)pBackend; + + (void)pUserData; + + return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus = +{ + ma_decoding_backend_init__libopus, + ma_decoding_backend_init_file__libopus, + NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__libopus +}; + + + + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_data_source* pDataSource = (ma_data_source*)pDevice->pUserData; + if (pDataSource == NULL) { + return; + } + + ma_data_source_read_pcm_frames(pDataSource, pOutput, frameCount, NULL); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_decoder decoder; + ma_device_config deviceConfig; + ma_device device; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + + /* + Add your custom backend vtables here. The order in the array defines the order of priority. The + vtables will be passed in via the decoder config. + */ + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + + /* Initialize the decoder. */ + decoderConfig = ma_decoder_config_init_default(); + decoderConfig.pCustomBackendUserData = NULL; /* In this example our backend objects are contained within a ma_decoder_ex object to avoid a malloc. Our vtables need to know about this. */ + decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; + decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + + result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder); + if (result != MA_SUCCESS) { + printf("Failed to initialize decoder."); + return -1; + } + + ma_data_source_set_looping(&decoder, MA_TRUE); + + + /* Initialize the device. */ + result = ma_data_source_get_data_format(&decoder, &format, &channels, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + printf("Failed to retrieve decoder data format."); + ma_decoder_uninit(&decoder); + return -1; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = format; + deviceConfig.playback.channels = channels; + deviceConfig.sampleRate = sampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &decoder; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + ma_decoder_uninit(&decoder); + return -1; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + return -1; + } + + printf("Press Enter to quit..."); + getchar(); + + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/custom_decoder_engine.c b/thirdparty/miniaudio/examples/custom_decoder_engine.c new file mode 100644 index 0000000..aa2ef5d --- /dev/null +++ b/thirdparty/miniaudio/examples/custom_decoder_engine.c @@ -0,0 +1,230 @@ +/* +Demonstrates how to implement a custom decoder and use it with the high level API. + +This is the same as the custom_decoder example, only it's used with the high level engine API +rather than the low level decoding API. You can use this to add support for Opus to your games, for +example (via libopus). +*/ +#define MA_NO_VORBIS /* Disable the built-in Vorbis decoder to ensure the libvorbis decoder is picked. */ +#define MA_NO_OPUS /* Disable the (not yet implemented) built-in Opus decoder to ensure the libopus decoder is picked. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" +#include "../extras/miniaudio_libvorbis.h" +#include "../extras/miniaudio_libopus.h" + +#include + +static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libvorbis* pVorbis; + + (void)pUserData; + + pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + ma_libvorbis_uninit(pVorbis, pAllocationCallbacks); + ma_free(pVorbis, pAllocationCallbacks); +} + +static ma_result ma_decoding_backend_get_channel_map__libvorbis(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend; + + (void)pUserData; + + return ma_libvorbis_get_data_format(pVorbis, NULL, NULL, NULL, pChannelMap, channelMapCap); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis = +{ + ma_decoding_backend_init__libvorbis, + ma_decoding_backend_init_file__libvorbis, + NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__libvorbis +}; + + + +static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libopus* pOpus; + + (void)pUserData; + + pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks); + if (pOpus == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus); + if (result != MA_SUCCESS) { + ma_free(pOpus, pAllocationCallbacks); + return result; + } + + *ppBackend = pOpus; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_libopus* pOpus; + + (void)pUserData; + + pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks); + if (pOpus == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus); + if (result != MA_SUCCESS) { + ma_free(pOpus, pAllocationCallbacks); + return result; + } + + *ppBackend = pOpus; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_libopus* pOpus = (ma_libopus*)pBackend; + + (void)pUserData; + + ma_libopus_uninit(pOpus, pAllocationCallbacks); + ma_free(pOpus, pAllocationCallbacks); +} + +static ma_result ma_decoding_backend_get_channel_map__libopus(void* pUserData, ma_data_source* pBackend, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_libopus* pOpus = (ma_libopus*)pBackend; + + (void)pUserData; + + return ma_libopus_get_data_format(pOpus, NULL, NULL, NULL, pChannelMap, channelMapCap); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus = +{ + ma_decoding_backend_init__libopus, + ma_decoding_backend_init_file__libopus, + NULL, /* onInitFileW() */ + NULL, /* onInitMemory() */ + ma_decoding_backend_uninit__libopus +}; + + + +int main(int argc, char** argv) +{ + ma_result result; + ma_resource_manager_config resourceManagerConfig; + ma_resource_manager resourceManager; + ma_engine_config engineConfig; + ma_engine engine; + + /* + Add your custom backend vtables here. The order in the array defines the order of priority. The + vtables will be passed in to the resource manager config. + */ + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + + /* Using custom decoding backends requires a resource manager. */ + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; + resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + resourceManagerConfig.pCustomDecodingBackendUserData = NULL; /* <-- This will be passed in to the pUserData parameter of each function in the decoding backend vtables. */ + + result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + if (result != MA_SUCCESS) { + printf("Failed to initialize resource manager."); + return -1; + } + + + /* Once we have a resource manager we can create the engine. */ + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &resourceManager; + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize engine."); + return -1; + } + + + /* Now we can play our sound. */ + result = ma_engine_play_sound(&engine, argv[1], NULL); + if (result != MA_SUCCESS) { + printf("Failed to play sound."); + return -1; + } + + + printf("Press Enter to quit..."); + getchar(); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/data_source_chaining.c b/thirdparty/miniaudio/examples/data_source_chaining.c new file mode 100644 index 0000000..6d01990 --- /dev/null +++ b/thirdparty/miniaudio/examples/data_source_chaining.c @@ -0,0 +1,159 @@ +/* +Demonstrates one way to chain together a number of data sources so they play back seamlessly +without gaps. + +This example uses the chaining system built into the `ma_data_source` API. It will take every sound +passed onto the command line in order, and then loop back and start again. When looping a chain of +data sources, you need only link the last data source back to the first one. + +To play a chain of data sources, you first need to set up your chain. To set the data source that +should be played after another, you have two options: + + * Set a pointer to a specific data source + * Set a callback that will fire when the next data source needs to be retrieved + +The first option is good for simple scenarios. The second option is useful if you need to perform +some action when the end of a sound is reached. This example will be using both. + +When reading data from a chain, you always read from the head data source. Internally miniaudio +will track a pointer to the data source in the chain that is currently playing. If you don't +consistently read from the head data source this state will become inconsistent and things won't +work correctly. When using a chain, this pointer needs to be reset if you need to play the +chain again from the start: + + ```c + ma_data_source_set_current(&headDataSource, &headDataSource); + ma_data_source_seek_to_pcm_frame(&headDataSource, 0); + ``` + +The code above is setting the "current" data source in the chain to the head data source, thereby +starting the chain from the start again. It is also seeking the head data source back to the start +so that playback starts from the start as expected. You do not need to seek non-head items back to +the start as miniaudio will do that for you internally. +*/ +#define MA_EXPERIMENTAL__DATA_LOOPING_AND_CHAINING +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +/* +For simplicity, this example requires the device to use floating point samples. +*/ +#define SAMPLE_FORMAT ma_format_f32 +#define CHANNEL_COUNT 2 +#define SAMPLE_RATE 48000 + +ma_uint32 g_decoderCount; +ma_decoder* g_pDecoders; + +static ma_data_source* next_callback_tail(ma_data_source* pDataSource) +{ + MA_ASSERT(g_decoderCount > 0); /* <-- We check for this in main() so should never happen. */ + + /* + This will be fired when the last item in the chain has reached the end. In this example we want + to loop back to the start, so we need only return a pointer back to the head. + */ + return &g_pDecoders[0]; +} + +static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + /* + We can just read from the first decoder and miniaudio will resolve the chain for us. Note that + if you want to loop the chain, like we're doing in this example, you need to set the `loop` + parameter to false, or else only the current data source will be looped. + */ + ma_data_source_read_pcm_frames(&g_pDecoders[0], pOutput, frameCount, NULL); + + /* Unused in this example. */ + (void)pDevice; + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result = MA_SUCCESS; + ma_uint32 iDecoder; + ma_decoder_config decoderConfig; + ma_device_config deviceConfig; + ma_device device; + + if (argc < 2) { + printf("No input files.\n"); + return -1; + } + + g_decoderCount = argc-1; + g_pDecoders = (ma_decoder*)malloc(sizeof(*g_pDecoders) * g_decoderCount); + + /* In this example, all decoders need to have the same output format. */ + decoderConfig = ma_decoder_config_init(SAMPLE_FORMAT, CHANNEL_COUNT, SAMPLE_RATE); + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + result = ma_decoder_init_file(argv[1+iDecoder], &decoderConfig, &g_pDecoders[iDecoder]); + if (result != MA_SUCCESS) { + ma_uint32 iDecoder2; + for (iDecoder2 = 0; iDecoder2 < iDecoder; ++iDecoder2) { + ma_decoder_uninit(&g_pDecoders[iDecoder2]); + } + free(g_pDecoders); + + printf("Failed to load %s.\n", argv[1+iDecoder]); + return -1; + } + } + + /* + We're going to set up our decoders to run one after the other, but then have the last one loop back + to the first one. For demonstration purposes we're going to use the callback method for the last + data source. + */ + for (iDecoder = 0; iDecoder < g_decoderCount-1; iDecoder += 1) { + ma_data_source_set_next(&g_pDecoders[iDecoder], &g_pDecoders[iDecoder+1]); + } + + /* + For the last data source we'll loop back to the start, but for demonstration purposes we'll use a + callback to determine the next data source in the chain. + */ + ma_data_source_set_next_callback(&g_pDecoders[g_decoderCount-1], next_callback_tail); + + + /* + The data source chain has been established so now we can get the device up and running so we + can listen to it. + */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = SAMPLE_FORMAT; + deviceConfig.playback.channels = CHANNEL_COUNT; + deviceConfig.sampleRate = SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = NULL; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + result = -1; + goto done_decoders; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + result = -1; + goto done; + } + + printf("Press Enter to quit..."); + getchar(); + +done: + ma_device_uninit(&device); + +done_decoders: + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + ma_decoder_uninit(&g_pDecoders[iDecoder]); + } + free(g_pDecoders); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/duplex_effect.c b/thirdparty/miniaudio/examples/duplex_effect.c new file mode 100644 index 0000000..2c2a545 --- /dev/null +++ b/thirdparty/miniaudio/examples/duplex_effect.c @@ -0,0 +1,148 @@ +/* +Demonstrates how to apply an effect to a duplex stream using the node graph system. + +This example applies a vocoder effect to the input stream before outputting it. A custom node +called `ma_vocoder_node` is used to achieve the effect which can be found in the extras folder in +the miniaudio repository. The vocoder node uses https://github.com/blastbay/voclib to achieve the +effect. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" +#include "../extras/nodes/ma_vocoder_node/ma_vocoder_node.c" + +#include + +#define DEVICE_FORMAT ma_format_f32; /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */ + +static ma_waveform g_sourceData; /* The underlying data source of the source node. */ +static ma_audio_buffer_ref g_exciteData; /* The underlying data source of the excite node. */ +static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */ +static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */ +static ma_vocoder_node g_vocoderNode; /* The vocoder node. */ +static ma_node_graph g_nodeGraph; + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* + The node graph system is a pulling style of API. At the lowest level of the chain will be a + node acting as a data source for the purpose of delivering the initial audio data. In our case, + the data source is our `pInput` buffer. We need to update the underlying data source so that it + read data from `pInput`. + */ + ma_audio_buffer_ref_set_data(&g_exciteData, pInput, frameCount); + + /* With the source buffer configured we can now read directly from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_vocoder_node_config vocoderNodeConfig; + ma_data_source_node_config sourceNodeConfig; + ma_data_source_node_config exciteNodeConfig; + ma_waveform_config waveformConfig; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Now we can setup our node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.capture.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Vocoder. Attached straight to the endpoint. */ + vocoderNodeConfig = ma_vocoder_node_config_init(device.capture.channels, device.sampleRate); + + result = ma_vocoder_node_init(&g_nodeGraph, &vocoderNodeConfig, NULL, &g_vocoderNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize vocoder node."); + goto done1; + } + + ma_node_attach_output_bus(&g_vocoderNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + /* Amplify the volume of the vocoder output because in my testing it is a bit quiet. */ + ma_node_set_output_bus_volume(&g_vocoderNode, 0, 4); + + + /* Source/carrier. Attached to input bus 0 of the vocoder node. */ + waveformConfig = ma_waveform_config_init(device.capture.format, device.capture.channels, device.sampleRate, ma_waveform_type_sawtooth, 1.0, 50); + + result = ma_waveform_init(&waveformConfig, &g_sourceData); + if (result != MA_SUCCESS) { + printf("Failed to initialize waveform for excite node."); + goto done3; + } + + sourceNodeConfig = ma_data_source_node_config_init(&g_sourceData); + + result = ma_data_source_node_init(&g_nodeGraph, &sourceNodeConfig, NULL, &g_sourceNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize excite node."); + goto done3; + } + + ma_node_attach_output_bus(&g_sourceNode, 0, &g_vocoderNode, 0); + + + /* Excite/modulator. Attached to input bus 1 of the vocoder node. */ + result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_exciteData); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio buffer for source."); + goto done2; + } + + exciteNodeConfig = ma_data_source_node_config_init(&g_exciteData); + + result = ma_data_source_node_init(&g_nodeGraph, &exciteNodeConfig, NULL, &g_exciteNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize source node."); + goto done2; + } + + ma_node_attach_output_bus(&g_exciteNode, 0, &g_vocoderNode, 1); + + + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + +/*done4:*/ ma_data_source_node_uninit(&g_exciteNode, NULL); +done3: ma_data_source_node_uninit(&g_sourceNode, NULL); +done2: ma_vocoder_node_uninit(&g_vocoderNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/examples/engine_advanced.c b/thirdparty/miniaudio/examples/engine_advanced.c new file mode 100644 index 0000000..3911c22 --- /dev/null +++ b/thirdparty/miniaudio/examples/engine_advanced.c @@ -0,0 +1,251 @@ +/* +This example demonstrates some of the advanced features of the high level engine API. + +The following features are demonstrated: + + * Initialization of the engine from a pre-initialized device. + * Self-managed resource managers. + * Multiple engines with a shared resource manager. + * Creation and management of `ma_sound` objects. + +This example will play the sound that's passed in on the command line. + +Using a shared resource manager, as we do in this example, is useful for when you want to user +multiple engines so that you can output to multiple playback devices simultaneoulys. An example +might be a local co-op multiplayer game where each player has their own headphones. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#define MAX_DEVICES 2 +#define MAX_SOUNDS 32 + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pInput; + + /* + Since we're managing the underlying device ourselves, we need to read from the engine directly. + To do this we need access to the `ma_engine` object which we passed in to the user data. One + advantage of this is that you could do your own audio processing in addition to the engine's + standard processing. + */ + ma_engine_read_pcm_frames((ma_engine*)pDevice->pUserData, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_context context; + ma_resource_manager_config resourceManagerConfig; + ma_resource_manager resourceManager; + ma_engine engines[MAX_DEVICES]; + ma_device devices[MAX_DEVICES]; + ma_uint32 engineCount = 0; + ma_uint32 iEngine; + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + ma_uint32 iAvailableDevice; + ma_uint32 iChosenDevice; + ma_sound sounds[MAX_SOUNDS]; + ma_uint32 soundCount; + ma_uint32 iSound; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + + /* + We are going to be initializing multiple engines. In order to save on memory usage we can use a self managed + resource manager so we can share a single resource manager across multiple engines. + */ + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.decodedFormat = ma_format_f32; /* ma_format_f32 should almost always be used as that's what the engine (and most everything else) uses for mixing. */ + resourceManagerConfig.decodedChannels = 0; /* Setting the channel count to 0 will cause sounds to use their native channel count. */ + resourceManagerConfig.decodedSampleRate = 48000; /* Using a consistent sample rate is useful for avoiding expensive resampling in the audio thread. This will result in resampling being performed by the loading thread(s). */ + + result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + if (result != MA_SUCCESS) { + printf("Failed to initialize resource manager."); + return -1; + } + + + /* We're going to want a context so we can enumerate our playback devices. */ + result = ma_context_init(NULL, 0, NULL, &context); + if (result != MA_SUCCESS) { + printf("Failed to initialize context."); + return -1; + } + + /* + Now that we have a context we will want to enumerate over each device so we can display them to the user and give + them a chance to select the output devices they want to use. + */ + result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); + if (result != MA_SUCCESS) { + printf("Failed to enumerate playback devices."); + ma_context_uninit(&context); + return -1; + } + + + /* We have our devices, so now we want to get the user to select the devices they want to output to. */ + engineCount = 0; + + for (iChosenDevice = 0; iChosenDevice < MAX_DEVICES; iChosenDevice += 1) { + int c = 0; + for (;;) { + printf("Select playback device %d ([%d - %d], Q to quit):\n", iChosenDevice+1, 0, ma_min((int)playbackDeviceCount, 9)); + + for (iAvailableDevice = 0; iAvailableDevice < playbackDeviceCount; iAvailableDevice += 1) { + printf(" %d: %s\n", iAvailableDevice, pPlaybackDeviceInfos[iAvailableDevice].name); + } + + for (;;) { + c = getchar(); + if (c != '\n') { + break; + } + } + + if (c == 'q' || c == 'Q') { + return 0; /* User aborted. */ + } + + if (c >= '0' && c <= '9') { + c -= '0'; + + if (c < (int)playbackDeviceCount) { + ma_device_config deviceConfig; + ma_engine_config engineConfig; + + /* + Create the device first before the engine. We'll specify the device in the engine's config. This is optional. When a device is + not pre-initialized the engine will create one for you internally. The device does not need to be started here - the engine will + do that for us in `ma_engine_start()`. The device's format is derived from the resource manager, but can be whatever you want. + It's useful to keep the format consistent with the resource manager to avoid data conversions costs in the audio callback. In + this example we're using the resource manager's sample format and sample rate, but leaving the channel count set to the device's + native channels. You can use whatever format/channels/rate you like. + */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = &pPlaybackDeviceInfos[c].id; + deviceConfig.playback.format = resourceManager.config.decodedFormat; + deviceConfig.playback.channels = 0; + deviceConfig.sampleRate = resourceManager.config.decodedSampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &engines[engineCount]; + + result = ma_device_init(&context, &deviceConfig, &devices[engineCount]); + if (result != MA_SUCCESS) { + printf("Failed to initialize device for %s.\n", pPlaybackDeviceInfos[c].name); + return -1; + } + + /* Now that we have the device we can initialize the engine. The device is passed into the engine's config. */ + engineConfig = ma_engine_config_init(); + engineConfig.pDevice = &devices[engineCount]; + engineConfig.pResourceManager = &resourceManager; + engineConfig.noAutoStart = MA_TRUE; /* Don't start the engine by default - we'll do that manually below. */ + + result = ma_engine_init(&engineConfig, &engines[engineCount]); + if (result != MA_SUCCESS) { + printf("Failed to initialize engine for %s.\n", pPlaybackDeviceInfos[c].name); + ma_device_uninit(&devices[engineCount]); + return -1; + } + + engineCount += 1; + break; + } else { + printf("Invalid device number.\n"); + } + } else { + printf("Invalid device number.\n"); + } + } + + printf("Device %d: %s\n", iChosenDevice+1, pPlaybackDeviceInfos[c].name); + } + + + /* We should now have our engine's initialized. We can now start them. */ + for (iEngine = 0; iEngine < engineCount; iEngine += 1) { + result = ma_engine_start(&engines[iEngine]); + if (result != MA_SUCCESS) { + printf("WARNING: Failed to start engine %d.\n", iEngine); + } + } + + + /* + At this point our engine's are running and outputting nothing but silence. To get them playing something we'll need + some sounds. In this example we're just using one sound per engine, but you can create as many as you like. Since + we're using a shared resource manager, the sound data will only be loaded once. This is how you would implement + multiple listeners. + */ + soundCount = 0; + for (iEngine = 0; iEngine < engineCount; iEngine += 1) { + /* Just one sound per engine in this example. We're going to be loading this asynchronously. */ + result = ma_sound_init_from_file(&engines[iEngine], argv[1], MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM, NULL, NULL, &sounds[iEngine]); + if (result != MA_SUCCESS) { + printf("WARNING: Failed to load sound \"%s\"", argv[1]); + break; + } + + /* + The sound can be started as soon as ma_sound_init_from_file() returns, even for sounds that are initialized + with MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. The sound will start playing while it's being loaded. Note that if the + asynchronous loading process cannot keep up with the rate at which you try reading you'll end up glitching. + If this is an issue, you need to not load sounds asynchronously. + */ + result = ma_sound_start(&sounds[iEngine]); + if (result != MA_SUCCESS) { + printf("WARNING: Failed to start sound."); + } + + soundCount += 1; + } + + + printf("Press Enter to quit..."); + getchar(); + + for (;;) { + int c = getchar(); + if (c == '\n') { + break; + } + } + + /* Teardown. */ + + /* The application owns the `ma_sound` object which means you're responsible for uninitializing them. */ + for (iSound = 0; iSound < soundCount; iSound += 1) { + ma_sound_uninit(&sounds[iSound]); + } + + /* We can now uninitialize each engine. */ + for (iEngine = 0; iEngine < engineCount; iEngine += 1) { + ma_engine_uninit(&engines[iEngine]); + + /* + The engine has been uninitialized so now lets uninitialize the device. Do this first to ensure we don't + uninitialize the resource manager from under the device while the data callback is running. + */ + ma_device_uninit(&devices[iEngine]); + } + + /* The context can only be uninitialized after the devices. */ + ma_context_uninit(&context); + + /* + Do the resource manager last. This way we can guarantee the data callbacks of each device aren't trying to access + and data managed by the resource manager. + */ + ma_resource_manager_uninit(&resourceManager); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/engine_effects.c b/thirdparty/miniaudio/examples/engine_effects.c new file mode 100644 index 0000000..acb259c --- /dev/null +++ b/thirdparty/miniaudio/examples/engine_effects.c @@ -0,0 +1,104 @@ +/* +Demonstrates how to apply an effect to sounds using the high level engine API. + +This example will load a file from the command line and apply an echo/delay effect to it. It will +show you how to manage `ma_sound` objects and how to insert an effect into the graph. + +The `ma_engine` object is a node graph and is compatible with the `ma_node_graph` API. The +`ma_sound` object is a node within the node and is compatible with the `ma_node` API. This means +that applying an effect is as simple as inserting an effect node into the graph and plugging in the +sound's output into the effect's input. See the Node Graph example for how to use the node graph. + +This example is playing only a single sound at a time which means only a single `ma_sound` object +it being used. If you want to play multiple sounds at the same time, even if they're for the same +sound file, you need multiple `ma_sound` objects. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#define DELAY_IN_SECONDS 0.2f +#define DECAY 0.25f /* Volume falloff for each echo. */ + +static ma_engine g_engine; +static ma_sound g_sound; /* This example will play only a single sound at once, so we only need one `ma_sound` object. */ +static ma_delay_node g_delayNode; /* The echo effect is achieved using a delay node. */ + +int main(int argc, char** argv) +{ + ma_result result; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + /* The engine needs to be initialized first. */ + result = ma_engine_init(NULL, &g_engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio engine."); + return -1; + } + + + /* + We'll build our graph starting from the end so initialize the delay node now. The output of + this node will be connected straight to the output. You could also attach it to a sound group + or any other node that accepts an input. + + Creating a node requires a pointer to the node graph that owns it. The engine itself is a node + graph. In the code below we can get a pointer to the node graph with `ma_engine_get_node_graph()` + or we could simple cast the engine to a ma_node_graph* like so: + + (ma_node_graph*)&g_engine + + The endpoint of the graph can be retrieved with `ma_engine_get_endpoint()`. + */ + { + ma_delay_node_config delayNodeConfig; + ma_uint32 channels; + ma_uint32 sampleRate; + + channels = ma_engine_get_channels(&g_engine); + sampleRate = ma_engine_get_sample_rate(&g_engine); + + delayNodeConfig = ma_delay_node_config_init(channels, sampleRate, (ma_uint32)(sampleRate * DELAY_IN_SECONDS), DECAY); + + result = ma_delay_node_init(ma_engine_get_node_graph(&g_engine), &delayNodeConfig, NULL, &g_delayNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize delay node."); + return -1; + } + + /* Connect the output of the delay node to the input of the endpoint. */ + ma_node_attach_output_bus(&g_delayNode, 0, ma_engine_get_endpoint(&g_engine), 0); + } + + + /* Now we can load the sound and connect it to the delay node. */ + { + result = ma_sound_init_from_file(&g_engine, argv[1], 0, NULL, NULL, &g_sound); + if (result != MA_SUCCESS) { + printf("Failed to initialize sound \"%s\".", argv[1]); + return -1; + } + + /* Connect the output of the sound to the input of the effect. */ + ma_node_attach_output_bus(&g_sound, 0, &g_delayNode, 0); + + /* + Start the sound after it's applied to the sound. Otherwise there could be a scenario where + the very first part of it is read before the attachment to the effect is made. + */ + ma_sound_start(&g_sound); + } + + + printf("Press Enter to quit..."); + getchar(); + + ma_sound_uninit(&g_sound); + ma_delay_node_uninit(&g_delayNode, NULL); + ma_engine_uninit(&g_engine); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/engine_hello_world.c b/thirdparty/miniaudio/examples/engine_hello_world.c new file mode 100644 index 0000000..f6f548f --- /dev/null +++ b/thirdparty/miniaudio/examples/engine_hello_world.c @@ -0,0 +1,35 @@ +/* +This example demonstrates how to initialize an audio engine and play a sound. + +This will play the sound specified on the command line. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +int main(int argc, char** argv) +{ + ma_result result; + ma_engine engine; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio engine."); + return -1; + } + + ma_engine_play_sound(&engine, argv[1], NULL); + + printf("Press Enter to quit..."); + getchar(); + + ma_engine_uninit(&engine); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/engine_sdl.c b/thirdparty/miniaudio/examples/engine_sdl.c new file mode 100644 index 0000000..8b67161 --- /dev/null +++ b/thirdparty/miniaudio/examples/engine_sdl.c @@ -0,0 +1,134 @@ +/* +Shows how to use the high level engine API with SDL. + +By default, miniaudio's engine API will initialize a device internally for audio output. You can +instead use the engine independently of a device. To show this off, this example will use SDL for +audio output instead of miniaudio. + +This example will load the sound specified on the command line and rotate it around the listener's +head. +*/ +#define MA_NO_DEVICE_IO /* <-- Disables the `ma_device` API. We don't need that in this example since SDL will be doing that part for us. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#define SDL_MAIN_HANDLED +#include /* Change this to your include location. Might be . */ + +#define CHANNELS 2 /* Must be stereo for this example. */ +#define SAMPLE_RATE 48000 + +static ma_engine g_engine; +static ma_sound g_sound; /* This example will play only a single sound at once, so we only need one `ma_sound` object. */ + +void data_callback(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes) +{ + /* Reading is just a matter of reading straight from the engine. */ + ma_uint32 bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine)); + ma_engine_read_pcm_frames(&g_engine, pBuffer, bufferSizeInFrames, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_engine_config engineConfig; + SDL_AudioSpec desiredSpec; + SDL_AudioSpec obtainedSpec; + SDL_AudioDeviceID deviceID; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + /* + We'll initialize the engine first for the purpose of the example, but since the engine and SDL + are independent of each other you can initialize them in any order. You need only make sure the + channel count and sample rates are consistent between the two. + + When initializing the engine it's important to make sure we don't initialize a device + internally because we want SDL to be dealing with that for us instead. + */ + engineConfig = ma_engine_config_init(); + engineConfig.noDevice = MA_TRUE; /* <-- Make sure this is set so that no device is created (we'll deal with that ourselves). */ + engineConfig.channels = CHANNELS; + engineConfig.sampleRate = SAMPLE_RATE; + + result = ma_engine_init(&engineConfig, &g_engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio engine."); + return -1; + } + + /* Now load our sound. */ + result = ma_sound_init_from_file(&g_engine, argv[1], 0, NULL, NULL, &g_sound); + if (result != MA_SUCCESS) { + printf("Failed to initialize sound."); + return -1; + } + + /* Loop the sound so we can continuously hear it. */ + ma_sound_set_looping(&g_sound, MA_TRUE); + + /* + The sound will not be started by default, so start it now. We won't hear anything until the SDL + audio device has been opened and started. + */ + ma_sound_start(&g_sound); + + + /* + Now that we have the engine and sound we can initialize SDL. This could have also been done + first before the engine and sound. + */ + if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { + printf("Failed to initialize SDL sub-system."); + return -1; + } + + MA_ZERO_OBJECT(&desiredSpec); + desiredSpec.freq = ma_engine_get_sample_rate(&g_engine); + desiredSpec.format = AUDIO_F32; + desiredSpec.channels = ma_engine_get_channels(&g_engine); + desiredSpec.samples = 512; + desiredSpec.callback = data_callback; + desiredSpec.userdata = NULL; + + deviceID = SDL_OpenAudioDevice(NULL, 0, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_ANY_CHANGE); + if (deviceID == 0) { + printf("Failed to open SDL audio device."); + return -1; + } + + /* Start playback. */ + SDL_PauseAudioDevice(deviceID, 0); + +#if 1 + { + /* We'll move the sound around the listener which we'll leave at the origin. */ + float stepAngle = 0.002f; + float angle = 0; + float distance = 2; + + for (;;) { + double x = ma_cosd(angle) - ma_sind(angle); + double y = ma_sind(angle) + ma_cosd(angle); + + ma_sound_set_position(&g_sound, (float)x * distance, 0, (float)y * distance); + + angle += stepAngle; + ma_sleep(1); + } + } +#else + printf("Press Enter to quit..."); + getchar(); +#endif + + ma_sound_uninit(&g_sound); + ma_engine_uninit(&g_engine); + SDL_CloseAudioDevice(deviceID); + SDL_QuitSubSystem(SDL_INIT_AUDIO); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/engine_steamaudio.c b/thirdparty/miniaudio/examples/engine_steamaudio.c new file mode 100644 index 0000000..d64cc2f --- /dev/null +++ b/thirdparty/miniaudio/examples/engine_steamaudio.c @@ -0,0 +1,432 @@ +/* +Demonstrates integration of Steam Audio with miniaudio's engine API. + +In this example a HRTF effect from Steam Audio will be applied. To do this a custom node will be +implemented which uses Steam Audio's IPLBinauralEffect and IPLHRTF objects. + +By implementing this as a node, it can be plugged into any position within the graph. The output +channel count of this node is always stereo. + +Steam Audio requires fixed sized processing, the size of which must be specified at initialization +time of the IPLBinauralEffect and IPLHRTF objects. This creates a problem because the node graph +will at times need to break down processing into smaller chunks for it's internal processing. The +node graph internally will read into a temporary buffer which is then mixed into the final output +buffer. This temporary buffer is allocated on the stack and is a fixed size. However, variability +comes into play because the channel count of the node is variable. It's not safe to just blindly +process the effect with the frame count specified in miniaudio's node processing callback. Doing so +results in glitching. To work around this, this example is just setting the update size to a known +value that works (256). If it's set to something too big it'll exceed miniaudio's processing size +used by the node graph. Alternatively you could use some kind of intermediary cache which +accumulates input data until enough is available and then do the processing. Ideally, Steam Audio +would support variable sized updates which would avoid this whole mess entirely. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include /* Steam Audio */ +#include /* Required for uint32_t which is used by STEAMAUDIO_VERSION. That dependency needs to be removed from Steam Audio - use IPLuint32 or "unsigned int" instead! */ + +#define FORMAT ma_format_f32 /* Must be floating point. */ +#define CHANNELS 2 /* Must be stereo for this example. */ +#define SAMPLE_RATE 48000 + + +static ma_result ma_result_from_IPLerror(IPLerror error) +{ + switch (error) + { + case IPL_STATUS_SUCCESS: return MA_SUCCESS; + case IPL_STATUS_OUTOFMEMORY: return MA_OUT_OF_MEMORY; + case IPL_STATUS_INITIALIZATION: + case IPL_STATUS_FAILURE: + default: return MA_ERROR; + } +} + + +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channelsIn; + IPLAudioSettings iplAudioSettings; + IPLContext iplContext; + IPLHRTF iplHRTF; /* There is one HRTF object to many binaural effect objects. */ +} ma_steamaudio_binaural_node_config; + +MA_API ma_steamaudio_binaural_node_config ma_steamaudio_binaural_node_config_init(ma_uint32 channelsIn, IPLAudioSettings iplAudioSettings, IPLContext iplContext, IPLHRTF iplHRTF); + + +typedef struct +{ + ma_node_base baseNode; + IPLAudioSettings iplAudioSettings; + IPLContext iplContext; + IPLHRTF iplHRTF; + IPLBinauralEffect iplEffect; + ma_vec3f direction; + float* ppBuffersIn[2]; /* Each buffer is an offset of _pHeap. */ + float* ppBuffersOut[2]; /* Each buffer is an offset of _pHeap. */ + void* _pHeap; +} ma_steamaudio_binaural_node; + +MA_API ma_result ma_steamaudio_binaural_node_init(ma_node_graph* pNodeGraph, const ma_steamaudio_binaural_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_steamaudio_binaural_node* pBinauralNode); +MA_API void ma_steamaudio_binaural_node_uninit(ma_steamaudio_binaural_node* pBinauralNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_steamaudio_binaural_node_set_direction(ma_steamaudio_binaural_node* pBinauralNode, float x, float y, float z); + + +MA_API ma_steamaudio_binaural_node_config ma_steamaudio_binaural_node_config_init(ma_uint32 channelsIn, IPLAudioSettings iplAudioSettings, IPLContext iplContext, IPLHRTF iplHRTF) +{ + ma_steamaudio_binaural_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channelsIn = channelsIn; + config.iplAudioSettings = iplAudioSettings; + config.iplContext = iplContext; + config.iplHRTF = iplHRTF; + + return config; +} + + +static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_steamaudio_binaural_node* pBinauralNode = (ma_steamaudio_binaural_node*)pNode; + IPLBinauralEffectParams binauralParams; + IPLAudioBuffer inputBufferDesc; + IPLAudioBuffer outputBufferDesc; + ma_uint32 totalFramesToProcess = *pFrameCountOut; + ma_uint32 totalFramesProcessed = 0; + + binauralParams.direction.x = pBinauralNode->direction.x; + binauralParams.direction.y = pBinauralNode->direction.y; + binauralParams.direction.z = pBinauralNode->direction.z; + binauralParams.interpolation = IPL_HRTFINTERPOLATION_NEAREST; + binauralParams.spatialBlend = 1.0f; + binauralParams.hrtf = pBinauralNode->iplHRTF; + + inputBufferDesc.numChannels = (IPLint32)ma_node_get_input_channels(pNode, 0); + + /* We'll run this in a loop just in case our deinterleaved buffers are too small. */ + outputBufferDesc.numSamples = pBinauralNode->iplAudioSettings.frameSize; + outputBufferDesc.numChannels = 2; + outputBufferDesc.data = pBinauralNode->ppBuffersOut; + + while (totalFramesProcessed < totalFramesToProcess) { + ma_uint32 framesToProcessThisIteration = totalFramesToProcess - totalFramesProcessed; + if (framesToProcessThisIteration > (ma_uint32)pBinauralNode->iplAudioSettings.frameSize) { + framesToProcessThisIteration = (ma_uint32)pBinauralNode->iplAudioSettings.frameSize; + } + + if (inputBufferDesc.numChannels == 1) { + /* Fast path. No need for deinterleaving since it's a mono stream. */ + pBinauralNode->ppBuffersIn[0] = (float*)ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, 1); + } else { + /* Slow path. Need to deinterleave the input data. */ + ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), pBinauralNode->ppBuffersIn); + } + + inputBufferDesc.data = pBinauralNode->ppBuffersIn; + inputBufferDesc.numSamples = (IPLint32)framesToProcessThisIteration; + + /* Apply the effect. */ + iplBinauralEffectApply(pBinauralNode->iplEffect, &binauralParams, &inputBufferDesc, &outputBufferDesc); + + /* Interleave straight into the output buffer. */ + ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, pBinauralNode->ppBuffersOut, ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2)); + + /* Advance. */ + totalFramesProcessed += framesToProcessThisIteration; + } + + (void)pFrameCountIn; /* Unused. */ +} + +static ma_node_vtable g_ma_steamaudio_binaural_node_vtable = +{ + ma_steamaudio_binaural_node_process_pcm_frames, + NULL, + 1, /* 1 input channel. */ + 1, /* 1 output channel. */ + 0 +}; + +MA_API ma_result ma_steamaudio_binaural_node_init(ma_node_graph* pNodeGraph, const ma_steamaudio_binaural_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_steamaudio_binaural_node* pBinauralNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + IPLBinauralEffectSettings iplBinauralEffectSettings; + size_t heapSizeInBytes; + + if (pBinauralNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBinauralNode); + + if (pConfig == NULL || pConfig->iplAudioSettings.frameSize == 0 || pConfig->iplContext == NULL || pConfig->iplHRTF == NULL) { + return MA_INVALID_ARGS; + } + + /* Steam Audio only supports mono and stereo input. */ + if (pConfig->channelsIn < 1 || pConfig->channelsIn > 2) { + return MA_INVALID_ARGS; + } + + channelsIn = pConfig->channelsIn; + channelsOut = 2; /* Always stereo output. */ + + baseConfig = ma_node_config_init(); + baseConfig.vtable = &g_ma_steamaudio_binaural_node_vtable; + baseConfig.pInputChannels = &channelsIn; + baseConfig.pOutputChannels = &channelsOut; + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pBinauralNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + pBinauralNode->iplAudioSettings = pConfig->iplAudioSettings; + pBinauralNode->iplContext = pConfig->iplContext; + pBinauralNode->iplHRTF = pConfig->iplHRTF; + + MA_ZERO_OBJECT(&iplBinauralEffectSettings); + iplBinauralEffectSettings.hrtf = pBinauralNode->iplHRTF; + + result = ma_result_from_IPLerror(iplBinauralEffectCreate(pBinauralNode->iplContext, &pBinauralNode->iplAudioSettings, &iplBinauralEffectSettings, &pBinauralNode->iplEffect)); + if (result != MA_SUCCESS) { + ma_node_uninit(&pBinauralNode->baseNode, pAllocationCallbacks); + return result; + } + + heapSizeInBytes = 0; + + /* + Unfortunately Steam Audio uses deinterleaved buffers for everything so we'll need to use some + intermediary buffers. We'll allocate one big buffer on the heap and then use offsets. We'll + use the frame size from the IPLAudioSettings structure as a basis for the size of the buffer. + */ + heapSizeInBytes += sizeof(float) * channelsOut * pBinauralNode->iplAudioSettings.frameSize; /* Output buffer. */ + heapSizeInBytes += sizeof(float) * channelsIn * pBinauralNode->iplAudioSettings.frameSize; /* Input buffer. */ + + pBinauralNode->_pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pBinauralNode->_pHeap == NULL) { + iplBinauralEffectRelease(&pBinauralNode->iplEffect); + ma_node_uninit(&pBinauralNode->baseNode, pAllocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pBinauralNode->ppBuffersOut[0] = (float*)pBinauralNode->_pHeap; + pBinauralNode->ppBuffersOut[1] = (float*)ma_offset_ptr(pBinauralNode->_pHeap, sizeof(float) * pBinauralNode->iplAudioSettings.frameSize); + + { + ma_uint32 iChannelIn; + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + pBinauralNode->ppBuffersIn[iChannelIn] = (float*)ma_offset_ptr(pBinauralNode->_pHeap, sizeof(float) * pBinauralNode->iplAudioSettings.frameSize * (channelsOut + iChannelIn)); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_steamaudio_binaural_node_uninit(ma_steamaudio_binaural_node* pBinauralNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBinauralNode == NULL) { + return; + } + + /* The base node is always uninitialized first. */ + ma_node_uninit(&pBinauralNode->baseNode, pAllocationCallbacks); + + /* + The Steam Audio objects are deleted after the base node. This ensures the base node is removed from the graph + first to ensure these objects aren't getting used by the audio thread. + */ + iplBinauralEffectRelease(&pBinauralNode->iplEffect); + ma_free(pBinauralNode->_pHeap, pAllocationCallbacks); +} + +MA_API ma_result ma_steamaudio_binaural_node_set_direction(ma_steamaudio_binaural_node* pBinauralNode, float x, float y, float z) +{ + if (pBinauralNode == NULL) { + return MA_INVALID_ARGS; + } + + pBinauralNode->direction.x = x; + pBinauralNode->direction.y = y; + pBinauralNode->direction.z = z; + + return MA_SUCCESS; +} + + + + +static ma_engine g_engine; +static ma_sound g_sound; /* This example will play only a single sound at once, so we only need one `ma_sound` object. */ +static ma_steamaudio_binaural_node g_binauralNode; /* The echo effect is achieved using a delay node. */ + +int main(int argc, char** argv) +{ + ma_result result; + ma_engine_config engineConfig; + IPLAudioSettings iplAudioSettings; + IPLContextSettings iplContextSettings; + IPLContext iplContext; + IPLHRTFSettings iplHRTFSettings; + IPLHRTF iplHRTF; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + /* The engine needs to be initialized first. */ + engineConfig = ma_engine_config_init(); + engineConfig.channels = CHANNELS; + engineConfig.sampleRate = SAMPLE_RATE; + engineConfig.periodSizeInFrames = 256; + + result = ma_engine_init(&engineConfig, &g_engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio engine."); + return -1; + } + + /* + Now that we have the engine we can initialize the Steam Audio objects. + */ + MA_ZERO_OBJECT(&iplAudioSettings); + iplAudioSettings.samplingRate = ma_engine_get_sample_rate(&g_engine); + + /* + If there's any Steam Audio developers reading this, why is the frame size needed? This needs to + be documented. If this is for some kind of buffer management with FFT or something, then this + need not be exposed to the public API. There should be no need for the public API to require a + fixed sized update. + */ + iplAudioSettings.frameSize = engineConfig.periodSizeInFrames; + + + /* IPLContext */ + MA_ZERO_OBJECT(&iplContextSettings); + iplContextSettings.version = STEAMAUDIO_VERSION; + + result = ma_result_from_IPLerror(iplContextCreate(&iplContextSettings, &iplContext)); + if (result != MA_SUCCESS) { + ma_engine_uninit(&g_engine); + return result; + } + + + /* IPLHRTF */ + MA_ZERO_OBJECT(&iplHRTFSettings); + iplHRTFSettings.type = IPL_HRTFTYPE_DEFAULT; + + result = ma_result_from_IPLerror(iplHRTFCreate(iplContext, &iplAudioSettings, &iplHRTFSettings, &iplHRTF)); + if (result != MA_SUCCESS) { + iplContextRelease(&iplContext); + ma_engine_uninit(&g_engine); + return result; + } + + + /* + The binaural node will need to know the input channel count of the sound so we'll need to load + the sound first. We'll initialize this such that it'll be initially detached from the graph. + It will be attached to the graph after the binaural node is initialized. + */ + { + ma_sound_config soundConfig; + + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = argv[1]; + soundConfig.flags = MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We'll attach this to the graph later. */ + + result = ma_sound_init_ex(&g_engine, &soundConfig, &g_sound); + if (result != MA_SUCCESS) { + return result; + } + + /* We'll let the Steam Audio binaural effect do the directional attenuation for us. */ + ma_sound_set_directional_attenuation_factor(&g_sound, 0); + + /* Loop the sound so we can get a continuous sound. */ + ma_sound_set_looping(&g_sound, MA_TRUE); + } + + + /* + We'll build our graph starting from the end so initialize the binaural node now. The output of + this node will be connected straight to the output. You could also attach it to a sound group + or any other node that accepts an input. + + Creating a node requires a pointer to the node graph that owns it. The engine itself is a node + graph. In the code below we can get a pointer to the node graph with `ma_engine_get_node_graph()` + or we could simple cast the engine to a ma_node_graph* like so: + + (ma_node_graph*)&g_engine + + The endpoint of the graph can be retrieved with `ma_engine_get_endpoint()`. + */ + { + ma_steamaudio_binaural_node_config binauralNodeConfig; + + /* + For this example we're just using the engine's channel count, but a more optimal solution + might be to set this to mono if the source data is also mono. + */ + binauralNodeConfig = ma_steamaudio_binaural_node_config_init(CHANNELS, iplAudioSettings, iplContext, iplHRTF); + + result = ma_steamaudio_binaural_node_init(ma_engine_get_node_graph(&g_engine), &binauralNodeConfig, NULL, &g_binauralNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize binaural node."); + return -1; + } + + /* Connect the output of the delay node to the input of the endpoint. */ + ma_node_attach_output_bus(&g_binauralNode, 0, ma_engine_get_endpoint(&g_engine), 0); + } + + + /* We can now wire up the sound to the binaural node and start it. */ + ma_node_attach_output_bus(&g_sound, 0, &g_binauralNode, 0); + ma_sound_start(&g_sound); + +#if 1 + { + /* + We'll move the sound around the listener which we'll leave at the origin. We'll then get + the direction to the listener and update the binaural node appropriately. + */ + float stepAngle = 0.002f; + float angle = 0; + float distance = 2; + + for (;;) { + double x = ma_cosd(angle) - ma_sind(angle); + double y = ma_sind(angle) + ma_cosd(angle); + ma_vec3f direction; + + ma_sound_set_position(&g_sound, (float)x * distance, 0, (float)y * distance); + direction = ma_sound_get_direction_to_listener(&g_sound); + + /* Update the direction of the sound. */ + ma_steamaudio_binaural_node_set_direction(&g_binauralNode, direction.x, direction.y, direction.z); + angle += stepAngle; + + ma_sleep(1); + } + } +#else + printf("Press Enter to quit..."); + getchar(); +#endif + + ma_sound_uninit(&g_sound); + ma_steamaudio_binaural_node_uninit(&g_binauralNode, NULL); + ma_engine_uninit(&g_engine); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/examples/hilo_interop.c b/thirdparty/miniaudio/examples/hilo_interop.c new file mode 100644 index 0000000..994e3ab --- /dev/null +++ b/thirdparty/miniaudio/examples/hilo_interop.c @@ -0,0 +1,148 @@ +/* +Demonstrates interop between the high-level and the low-level API. + +In this example we are using `ma_device` (the low-level API) to capture data from the microphone +which we then play back through the engine as a sound. We use a ring buffer to act as the data +source for the sound. + +This is just a very basic example to show the general idea on how this might be achieved. In +this example a ring buffer is being used as the intermediary data source, but you can use anything +that works best for your situation. So long as the data is captured from the microphone, and then +delivered to the sound (via a data source), you should be good to go. + +A more robust example would probably not want to use a ring buffer directly as the data source. +Instead you would probably want to do a custom data source that handles underruns and overruns of +the ring buffer and deals with desyncs between capture and playback. In the future this example +may be updated to make use of a more advanced data source that handles all of this. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +static ma_pcm_rb rb; +static ma_device device; +static ma_engine engine; +static ma_sound sound; /* The sound will be the playback of the capture side. */ + +void capture_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_result result; + ma_uint32 framesWritten; + + /* We need to write to the ring buffer. Need to do this in a loop. */ + framesWritten = 0; + while (framesWritten < frameCount) { + void* pMappedBuffer; + ma_uint32 framesToWrite = frameCount - framesWritten; + + result = ma_pcm_rb_acquire_write(&rb, &framesToWrite, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (framesToWrite == 0) { + break; + } + + /* Copy the data from the capture buffer to the ring buffer. */ + ma_copy_pcm_frames(pMappedBuffer, ma_offset_pcm_frames_const_ptr_f32(pFramesIn, framesWritten, pDevice->capture.channels), framesToWrite, pDevice->capture.format, pDevice->capture.channels); + + result = ma_pcm_rb_commit_write(&rb, framesToWrite); + if (result != MA_SUCCESS) { + break; + } + + framesWritten += framesToWrite; + } +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + + /* + The first thing we'll do is set up the capture side. There are two parts to this. The first is + the device itself, and the other is the ring buffer. It doesn't matter what order we initialize + these in, so long as the ring buffer is created before the device is started so that the + callback can be guaranteed to have a valid destination. We'll initialize the device first, and + then use the format, channels and sample rate to initialize the ring buffer. + + It's important that the sample format of the device is set to f32 because that's what the engine + uses internally. + */ + + /* Initialize the capture device. */ + deviceConfig = ma_device_config_init(ma_device_type_capture); + deviceConfig.capture.format = ma_format_f32; + deviceConfig.dataCallback = capture_data_callback; + + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("Failed to initialize capture device."); + return -1; + } + + /* Initialize the ring buffer. */ + result = ma_pcm_rb_init(device.capture.format, device.capture.channels, device.capture.internalPeriodSizeInFrames * 5, NULL, NULL, &rb); + if (result != MA_SUCCESS) { + printf("Failed to initialize the ring buffer."); + return -1; + } + + /* + Ring buffers don't require a sample rate for their normal operation, but we can associate it + with a sample rate. We'll want to do this so the engine can resample if necessary. + */ + ma_pcm_rb_set_sample_rate(&rb, device.sampleRate); + + + + /* + At this point the capture side is set up and we can now set up the playback side. Here we are + using `ma_engine` and linking the captured data to a sound so it can be manipulated just like + any other sound in the world. + + Note that we have not yet started the capture device. Since the captured data is tied to a + sound, we'll link the starting and stopping of the capture device to the starting and stopping + of the sound. + */ + + /* We'll get the engine up and running before we start the capture device. */ + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + printf("Failed to initialize the engine."); + return -1; + } + + /* + We can now create our sound. This is created from a data source, which in this example is a + ring buffer. The capture side will be writing data into the ring buffer, whereas the sound + will be reading from it. + */ + result = ma_sound_init_from_data_source(&engine, &rb, 0, NULL, &sound); + if (result != MA_SUCCESS) { + printf("Failed to initialize the sound."); + return -1; + } + + /* Make sure the sound is set to looping or else it'll stop if the ring buffer runs out of data. */ + ma_sound_set_looping(&sound, MA_TRUE); + + /* Link the starting of the device and sound together. */ + ma_device_start(&device); + ma_sound_start(&sound); + + + printf("Press Enter to quit...\n"); + getchar(); + + ma_sound_uninit(&sound); + ma_engine_uninit(&engine); + ma_device_uninit(&device); + ma_pcm_rb_uninit(&rb); + + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/examples/node_graph.c b/thirdparty/miniaudio/examples/node_graph.c new file mode 100644 index 0000000..aecccfe --- /dev/null +++ b/thirdparty/miniaudio/examples/node_graph.c @@ -0,0 +1,250 @@ +/* +This example shows how to use the node graph system. + +The node graph system can be used for doing complex mixing and effect processing. The idea is that +you have a number of nodes that are connected to each other to form a graph. At the end of the +graph is an endpoint which all nodes eventually connect to. + +A node is used to do some kind of processing on zero or more input streams and produce one or more +output streams. Each node can have a number of inputs and outputs. Each of these is called a bus in +miniaudio. Some nodes, particularly data source nodes, have no inputs and instead generate their +outputs dynamically. All nodes will have at least one output or else it'll be disconnected from the +graph and will never get processed. Each output bus of a node will be connected to an input bus of +another node, but they don't all need to connect to the same input node. For example, a splitter +node has 1 input bus and 2 output buses and is used to duplicate a signal. You could then branch +off and have one output bus connected to one input node and the other connected to a different +input node, and then have two different effects process for each of the duplicated branches. + +Any number of output buses can be connected to an input bus in which case the output buses will be +mixed before processing by the input node. This is how you would achieve the mixing part of the +node graph. + +This example will be using the following node graph set up: + + ``` + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + +---------------+ +-----------------+ + | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ + +---------------+ | | =----+ +-----------------+ | +----------+ + +----= Splitter | +----= ENDPOINT | + +---------------+ | | =----+ +-----------------+ | +----------+ + | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ + +---------------+ +-----------------+ + ``` + +This does not represent a realistic real-world scenario, but it demonstrates how to make use of +mixing, multiple outputs and multiple effects. + +The data source nodes are connected to the input of the splitter. They'll be mixed before being +processed by the splitter. The splitter has two output buses. In the graph above, one bus will be +routed to a low pass filter, whereas the other bus will be routed to an echo effect. Then, the +outputs of these two effects will be connected to the input bus of the endpoint. Because both of +the outputs are connected to the same input bus, they'll be mixed at that point. + +The two data sources at the start of the graph have no inputs. They'll instead generate their +output by reading from a data source. The data source in this case will be one `ma_decoder` for +each input file specified on the command line. + +You can also control the volume of an output bus. In this example, we set the volumes of the low +pass and echo effects so that one of them becomes more obvious than the other. + +When you want to read from the graph, you simply call `ma_node_graph_read_pcm_frames()`. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +/* Data Format */ +#define FORMAT ma_format_f32 /* Must always be f32. */ +#define CHANNELS 2 +#define SAMPLE_RATE 48000 + +/* Effect Properties */ +#define LPF_BIAS 0.9f /* Higher values means more bias towards the low pass filter (the low pass filter will be more audible). Lower values means more bias towards the echo. Must be between 0 and 1. */ +#define LPF_CUTOFF_FACTOR 80 /* High values = more filter. */ +#define LPF_ORDER 8 +#define DELAY_IN_SECONDS 0.2f +#define DECAY 0.5f /* Volume falloff for each echo. */ + +typedef struct +{ + ma_data_source_node node; /* If you make this the first member, you can pass a pointer to this struct into any `ma_node_*` API and it will "Just Work". */ + ma_decoder decoder; +} sound_node; + +static ma_node_graph g_nodeGraph; +static ma_lpf_node g_lpfNode; +static ma_delay_node g_delayNode; +static ma_splitter_node g_splitterNode; +static sound_node* g_pSoundNodes; +static int g_soundNodeCount; + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->playback.channels == CHANNELS); + + /* + Hearing the output of the node graph is as easy as reading straight into the output buffer. You just need to + make sure you use a consistent data format or else you'll need to do your own conversion. + */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); + + (void)pInput; /* Unused. */ +} + +int main(int argc, char** argv) +{ + int iarg; + ma_result result; + + /* We'll set up our nodes starting from the end and working our way back to the start. We'll need to set up the graph first. */ + { + ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(CHANNELS); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("ERROR: Failed to initialize node graph."); + return -1; + } + } + + + /* Low Pass Filter. */ + { + ma_lpf_node_config lpfNodeConfig = ma_lpf_node_config_init(CHANNELS, SAMPLE_RATE, SAMPLE_RATE / LPF_CUTOFF_FACTOR, LPF_ORDER); + + result = ma_lpf_node_init(&g_nodeGraph, &lpfNodeConfig, NULL, &g_lpfNode); + if (result != MA_SUCCESS) { + printf("ERROR: Failed to initialize low pass filter node."); + return -1; + } + + /* Connect the output bus of the low pass filter node to the input bus of the endpoint. */ + ma_node_attach_output_bus(&g_lpfNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + /* Set the volume of the low pass filter to make it more of less impactful. */ + ma_node_set_output_bus_volume(&g_lpfNode, 0, LPF_BIAS); + } + + + /* Echo / Delay. */ + { + ma_delay_node_config delayNodeConfig = ma_delay_node_config_init(CHANNELS, SAMPLE_RATE, (ma_uint32)(SAMPLE_RATE * DELAY_IN_SECONDS), DECAY); + + result = ma_delay_node_init(&g_nodeGraph, &delayNodeConfig, NULL, &g_delayNode); + if (result != MA_SUCCESS) { + printf("ERROR: Failed to initialize delay node."); + return -1; + } + + /* Connect the output bus of the delay node to the input bus of the endpoint. */ + ma_node_attach_output_bus(&g_delayNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + /* Set the volume of the delay filter to make it more of less impactful. */ + ma_node_set_output_bus_volume(&g_delayNode, 0, 1 - LPF_BIAS); + } + + + /* Splitter. */ + { + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(CHANNELS); + + result = ma_splitter_node_init(&g_nodeGraph, &splitterNodeConfig, NULL, &g_splitterNode); + if (result != MA_SUCCESS) { + printf("ERROR: Failed to initialize splitter node."); + return -1; + } + + /* Connect output bus 0 to the input bus of the low pass filter node, and output bus 1 to the input bus of the delay node. */ + ma_node_attach_output_bus(&g_splitterNode, 0, &g_lpfNode, 0); + ma_node_attach_output_bus(&g_splitterNode, 1, &g_delayNode, 0); + } + + + /* Data sources. Ignore any that cannot be loaded. */ + g_pSoundNodes = (sound_node*)ma_malloc(sizeof(*g_pSoundNodes) * argc-1, NULL); + if (g_pSoundNodes == NULL) { + printf("Failed to allocate memory for sounds."); + return -1; + } + + g_soundNodeCount = 0; + for (iarg = 1; iarg < argc; iarg += 1) { + ma_decoder_config decoderConfig = ma_decoder_config_init(FORMAT, CHANNELS, SAMPLE_RATE); + + result = ma_decoder_init_file(argv[iarg], &decoderConfig, &g_pSoundNodes[g_soundNodeCount].decoder); + if (result == MA_SUCCESS) { + ma_data_source_node_config dataSourceNodeConfig = ma_data_source_node_config_init(&g_pSoundNodes[g_soundNodeCount].decoder); + + result = ma_data_source_node_init(&g_nodeGraph, &dataSourceNodeConfig, NULL, &g_pSoundNodes[g_soundNodeCount].node); + if (result == MA_SUCCESS) { + /* The data source node has been created successfully. Attach it to the splitter. */ + ma_node_attach_output_bus(&g_pSoundNodes[g_soundNodeCount].node, 0, &g_splitterNode, 0); + g_soundNodeCount += 1; + } else { + printf("WARNING: Failed to init data source node for sound \"%s\". Ignoring.", argv[iarg]); + ma_decoder_uninit(&g_pSoundNodes[g_soundNodeCount].decoder); + } + } else { + printf("WARNING: Failed to load sound \"%s\". Ignoring.", argv[iarg]); + } + } + + /* Everything has been initialized successfully so now we can set up a playback device so we can listen to the result. */ + { + ma_device_config deviceConfig; + ma_device device; + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = FORMAT; + deviceConfig.playback.channels = CHANNELS; + deviceConfig.sampleRate = SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = NULL; + + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("ERROR: Failed to initialize device."); + goto cleanup_graph; + } + + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + goto cleanup_graph; + } + + printf("Press Enter to quit...\n"); + getchar(); + + /* We're done. Clean up the device. */ + ma_device_uninit(&device); + } + + +cleanup_graph: + { + /* It's good practice to tear down the graph from the lowest level nodes first. */ + int iSound; + + /* Sounds. */ + for (iSound = 0; iSound < g_soundNodeCount; iSound += 1) { + ma_data_source_node_uninit(&g_pSoundNodes[iSound].node, NULL); + ma_decoder_uninit(&g_pSoundNodes[iSound].decoder); + } + + /* Splitter. */ + ma_splitter_node_uninit(&g_splitterNode, NULL); + + /* Echo / Delay */ + ma_delay_node_uninit(&g_delayNode, NULL); + + /* Low Pass Filter */ + ma_lpf_node_uninit(&g_lpfNode, NULL); + + /* Node Graph */ + ma_node_graph_uninit(&g_nodeGraph, NULL); + } + + return 0; +} diff --git a/thirdparty/miniaudio/examples/resource_manager.c b/thirdparty/miniaudio/examples/resource_manager.c new file mode 100644 index 0000000..d9a9697 --- /dev/null +++ b/thirdparty/miniaudio/examples/resource_manager.c @@ -0,0 +1,151 @@ +/* +Demonstrates how you can use the resource manager to manage loaded sounds. + +This example loads the first sound specified on the command line via the resource manager and then plays it using the +low level API. + +You can control whether or not you want to load the sound asynchronously and whether or not you want to store the data +in-memory or stream it. When storing the sound in-memory you can also control whether or not it is decoded. To do this, +specify a combination of the following options in `ma_resource_manager_data_source_init()`: + + * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC - Load asynchronously. + * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE - Store the sound in-memory in uncompressed/decoded format. + * MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM - Stream the sound from disk rather than storing entirely in memory. Useful for music. + +The object returned by the resource manager is just a standard data source which means it can be plugged into any of +`ma_data_source_*()` APIs just like any other data source and it should just work. + +Internally, there's a background thread that's used to process jobs and enable asynchronicity. By default there is only +a single job thread, but this can be configured in the resource manager config. You can also implement your own threads +for processing jobs. That is more advanced, and beyond the scope of this example. + +When you initialize a resource manager you can specify the sample format, channels and sample rate to use when reading +data from the data source. This means the resource manager will ensure all sounds will have a standard format. When not +set, each sound will have their own formats and you'll need to do the necessary data conversion yourself. +*/ +#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#ifdef __EMSCRIPTEN__ +#include + +void main_loop__em(void* pUserData) +{ + ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; + MA_ASSERT(pResourceManager != NULL); + + /* + The Emscripten build does not support threading which means we need to process jobs manually. If + there are no jobs needing to be processed this will return immediately with MA_NO_DATA_AVAILABLE. + */ + ma_resource_manager_process_next_job(pResourceManager); +} +#endif + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_data_source_read_pcm_frames((ma_data_source*)pDevice->pUserData, pOutput, frameCount, NULL); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_resource_manager_config resourceManagerConfig; + ma_resource_manager resourceManager; + ma_resource_manager_data_source dataSource; + + if (argc < 2) { + printf("No input file."); + return -1; + } + + + /* We'll initialize the device first. */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &dataSource; /* <-- We'll be reading from this in the data callback. */ + + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("Failed to initialize device."); + return -1; + } + + + /* + We have the device so now we want to initialize the resource manager. We'll use the resource manager to load a + sound based on the command line. + */ + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.decodedFormat = device.playback.format; + resourceManagerConfig.decodedChannels = device.playback.channels; + resourceManagerConfig.decodedSampleRate = device.sampleRate; + + /* + We're not supporting threading with Emscripten so go ahead and disable threading. It's important + that we set the appropriate flag and also the job thread count to 0. + */ +#ifdef __EMSCRIPTEN__ + resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + resourceManagerConfig.jobThreadCount = 0; +#endif + + result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to initialize the resource manager."); + return -1; + } + + /* Now that we have a resource manager we can load a sound. */ + result = ma_resource_manager_data_source_init( + &resourceManager, + argv[1], + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM, + NULL, /* Async notification. */ + &dataSource); + if (result != MA_SUCCESS) { + printf("Failed to load sound \"%s\".", argv[1]); + return -1; + } + + /* In this example we'll enable looping. */ + ma_data_source_set_looping(&dataSource, MA_TRUE); + + + /* Now that we have a sound we can start the device. */ + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to start device."); + return -1; + } + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop_arg(main_loop__em, &resourceManager, 0, 1); +#else + printf("Press Enter to quit...\n"); + getchar(); +#endif + + /* Teardown. */ + + /* Uninitialize the device first to ensure the data callback is stopped and doesn't try to access any data. */ + ma_device_uninit(&device); + + /* + Before uninitializing the resource manager we need to uninitialize every data source. The data source is owned by + the caller which means you're responsible for uninitializing it. + */ + ma_resource_manager_data_source_uninit(&dataSource); + + /* Uninitialize the resource manager after each data source. */ + ma_resource_manager_uninit(&resourceManager); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/resource_manager_advanced.c b/thirdparty/miniaudio/examples/resource_manager_advanced.c new file mode 100644 index 0000000..19236c3 --- /dev/null +++ b/thirdparty/miniaudio/examples/resource_manager_advanced.c @@ -0,0 +1,329 @@ +/* +Demonstrates how you can use the resource manager to manage loaded sounds. + +The resource manager can be used to create a data source whose resources are managed internally by miniaudio. The data +sources can then be read just like any other data source such as decoders and audio buffers. + +In this example we use the resource manager independently of the `ma_engine` API so that we can demonstrate how it can +be used by itself without getting it confused with `ma_engine`. + +The main feature of the resource manager is the ability to decode and stream audio data asynchronously. Asynchronicity +is achieved with a job system. The resource manager will issue jobs which are processed by a configurable number of job +threads. You can also implement your own custom job threads which this example also demonstrates. + +In this example we show how you can create a data source, mix them with other data sources, configure the number of job +threads to manage internally and how to implement your own custom job thread. +*/ +#define MA_NO_ENGINE /* We're intentionally not using the ma_engine API here. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +static ma_resource_manager_data_source g_dataSources[16]; +static ma_uint32 g_dataSourceCount; + + +/* +TODO: Consider putting these public functions in miniaudio.h. Will depend on ma_mix_pcm_frames_f32() +being merged into miniaudio.h (it's currently in miniaudio_engine.h). +*/ +static ma_result ma_data_source_read_pcm_frames_f32_ex(ma_data_source* pDataSource, float* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, ma_format dataSourceFormat, ma_uint32 dataSourceChannels) +{ + /* + This function is intended to be used when the format and channel count of the data source is + known beforehand. The idea is to avoid overhead due to redundant calls to ma_data_source_get_data_format(). + */ + MA_ASSERT(pDataSource != NULL); + + if (dataSourceFormat == ma_format_f32) { + /* Fast path. No conversion necessary. */ + return ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, pFramesRead); + } else { + /* Slow path. Conversion necessary. */ + ma_result result; + ma_uint64 totalFramesRead; + ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint64 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); + + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + ma_uint64 framesJustRead; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, framesToRead, &framesJustRead); + + ma_convert_pcm_frames_format(ma_offset_pcm_frames_ptr_f32(pFramesOut, totalFramesRead, dataSourceChannels), ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); + totalFramesRead += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + } + + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_source_read_pcm_frames_f32(ma_data_source* pDataSource, float* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result; + ma_format format; + ma_uint32 channels; + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the data format of the data source. */ + } + + return ma_data_source_read_pcm_frames_f32_ex(pDataSource, pFramesOut, frameCount, pFramesRead, format, channels); +} + +MA_API ma_result ma_data_source_read_pcm_frames_and_mix_f32(ma_data_source* pDataSource, float* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead, float volume) +{ + ma_result result; + ma_format format; + ma_uint32 channels; + ma_uint64 totalFramesRead; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the data format of the data source. */ + } + + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)]; + ma_uint64 tempCapInFrames = ma_countof(temp) / channels; + ma_uint64 framesJustRead; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + result = ma_data_source_read_pcm_frames_f32_ex(pDataSource, temp, framesToRead, &framesJustRead, format, channels); + + ma_mix_pcm_frames_f32(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), temp, framesJustRead, channels, volume); + totalFramesRead += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return MA_SUCCESS; +} + + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + /* + In this example we're just going to play our data sources layered on top of each other. This + assumes the device's format is f32 and that the buffer is not pre-silenced. + */ + ma_uint32 iDataSource; + + MA_ASSERT(pDevice->playback.format == ma_format_f32); + + (void)pInput; /* Unused. */ + + /* + If the device was configured with noPreSilencedOutputBuffer then you would need to silence the + buffer here, or make sure the first data source to be mixed is copied rather than mixed. + */ + /*ma_silence_pcm_frames(pOutput, frameCount, ma_format_f32, pDevice->playback.channels);*/ + + /* For each sound, mix as much data as we can. */ + for (iDataSource = 0; iDataSource < g_dataSourceCount; iDataSource += 1) { + ma_data_source_read_pcm_frames_and_mix_f32(&g_dataSources[iDataSource], (float*)pOutput, frameCount, NULL, /* volume = */1); + } +} + +static ma_thread_result MA_THREADCALL custom_job_thread(void* pUserData) +{ + ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; + MA_ASSERT(pResourceManager != NULL); + + for (;;) { + ma_result result; + ma_resource_manager_job job; + + /* + Retrieve a job from the queue first. This defines what it is you're about to do. By default this will be + blocking. You can initialize the resource manager with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING to not block in + which case MA_NO_DATA_AVAILABLE will be returned if no jobs are available. + + When the quit job is returned (MA_RESOURCE_MANAGER_JOB_QUIT), the return value will always be MA_CANCELLED. If you don't want + to check the return value (you should), you can instead check if the job code is MA_RESOURCE_MANAGER_JOB_QUIT and use that + instead. + */ + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + if (result == MA_CANCELLED) { + printf("CUSTOM JOB THREAD TERMINATING VIA MA_CANCELLED... "); + } else { + printf("CUSTOM JOB THREAD ERROR: %s. TERMINATING... ", ma_result_description(result)); + } + + break; + } + + /* + Terminate if we got a quit message. You don't need to terminate like this, but's a bit more robust. You can + just use a global variable or something similar if it's easier for your particular situation. The quit job + remains in the queue and will continue to be returned by future calls to ma_resource_manager_next_job(). The + reason for this is to give every job thread visibility to the quit job so they have a chance to exit. + + We won't actually be hitting this code because the call above will return MA_CANCELLED when the MA_RESOURCE_MANAGER_JOB_QUIT + event is received which means the `result != MA_SUCCESS` logic above will catch it. If you do not check the + return value of ma_resource_manager_next_job() you will want to check for MA_RESOURCE_MANAGER_JOB_QUIT like the code below. + */ + if (job.toc.breakup.code == MA_RESOURCE_MANAGER_JOB_QUIT) { + printf("CUSTOM JOB THREAD TERMINATING VIA MA_RESOURCE_MANAGER_JOB_QUIT... "); + break; + } + + /* Call ma_resource_manager_process_job() to actually do the work to process the job. */ + printf("PROCESSING IN CUSTOM JOB THREAD: %d\n", job.toc.breakup.code); + ma_resource_manager_process_job(pResourceManager, &job); + } + + printf("TERMINATED\n"); + return (ma_thread_result)0; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_resource_manager_config resourceManagerConfig; + ma_resource_manager resourceManager; + ma_thread jobThread; + int iFile; + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = ma_format_f32; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = NULL; + + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("Failed to initialize device."); + return -1; + } + + + /* We can start the device before loading any sounds. We'll just end up outputting silence. */ + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to start device."); + return -1; + } + + + /* + We have the device so now we want to initialize the resource manager. We'll use the resource manager to load some + sounds based on the command line. + */ + resourceManagerConfig = ma_resource_manager_config_init(); + + /* + We'll set a standard decoding format to save us to processing time at mixing time. If you're wanting to use + spatialization with your decoded sounds, you may want to consider leaving this as 0 to ensure the file's native + channel count is used so you can do proper spatialization. + */ + resourceManagerConfig.decodedFormat = device.playback.format; + resourceManagerConfig.decodedChannels = device.playback.channels; + resourceManagerConfig.decodedSampleRate = device.sampleRate; + + /* The number of job threads to be managed internally. Set this to 0 if you want to self-manage your job threads */ + resourceManagerConfig.jobThreadCount = 4; + + result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to initialize the resource manager."); + return -1; + } + + /* + Now that we have a resource manager we can set up our custom job thread. This is optional. Normally when doing + self-managed job threads you would set the internal job thread count to zero. We're doing both internal and + self-managed job threads in this example just for demonstration purposes. + */ + ma_thread_create(&jobThread, ma_thread_priority_default, 0, custom_job_thread, &resourceManager, NULL); + + /* Create each data source from the resource manager. Note that the caller is the owner. */ + for (iFile = 0; iFile < ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) { + result = ma_resource_manager_data_source_init( + &resourceManager, + argv[iFile+1], + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC /*| MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM*/, + NULL, /* Async notification. */ + &g_dataSources[iFile]); + + if (result != MA_SUCCESS) { + break; + } + + /* Use looping in this example. */ + ma_data_source_set_looping(&g_dataSources[iFile], MA_TRUE); + + g_dataSourceCount += 1; + } + + printf("Press Enter to quit..."); + getchar(); + + + /* Teardown. */ + + /* + Uninitialize the device first to ensure the data callback is stopped and doesn't try to access + any data. + */ + ma_device_uninit(&device); + + /* + Our data sources need to be explicitly uninitialized. ma_resource_manager_uninit() will not do + it for us. This needs to be done before posting the quit event and uninitializing the resource + manager or else we'll get stuck in a deadlock because ma_resource_manager_data_source_uninit() + will be waiting for the job thread(s) to finish work, which will never happen because they were + just terminated. + */ + for (iFile = 0; (size_t)iFile < g_dataSourceCount; iFile += 1) { + ma_resource_manager_data_source_uninit(&g_dataSources[iFile]); + } + + /* + Before uninitializing the resource manager we need to make sure a quit event has been posted to + ensure we can get out of our custom thread. The call to ma_resource_manager_uninit() will also + do this, but we need to call it explicitly so that our self-managed thread can exit naturally. + You only need to post a quit job if you're using that as the exit indicator. You can instead + use whatever variable you want to terminate your job thread, but since this example is using a + quit job we need to post one. Note that you don't need to do this if you're not managing your + own threads - ma_resource_manager_uninit() alone will suffice in that case. + */ + ma_resource_manager_post_job_quit(&resourceManager); + ma_thread_wait(&jobThread); /* Wait for the custom job thread to finish so it doesn't try to access any data. */ + + /* Uninitialize the resource manager after each data source. */ + ma_resource_manager_uninit(&resourceManager); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_capture.c b/thirdparty/miniaudio/examples/simple_capture.c new file mode 100644 index 0000000..e3b1824 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_capture.c @@ -0,0 +1,74 @@ +/* +Demonstrates how to capture data from a microphone using the low-level API. + +This example simply captures data from your default microphone until you press Enter. The output is saved to the file +specified on the command line. + +Capturing works in a very similar way to playback. The only difference is the direction of data movement. Instead of +the application sending data to the device, the device will send data to the application. This example just writes the +data received by the microphone straight to a WAV file. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include +#include + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData; + MA_ASSERT(pEncoder != NULL); + + ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL); + + (void)pOutput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_encoder_config encoderConfig; + ma_encoder encoder; + ma_device_config deviceConfig; + ma_device device; + + if (argc < 2) { + printf("No output file.\n"); + return -1; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_f32, 2, 44100); + + if (ma_encoder_init_file(argv[1], &encoderConfig, &encoder) != MA_SUCCESS) { + printf("Failed to initialize output file.\n"); + return -1; + } + + deviceConfig = ma_device_config_init(ma_device_type_capture); + deviceConfig.capture.format = encoder.config.format; + deviceConfig.capture.channels = encoder.config.channels; + deviceConfig.sampleRate = encoder.config.sampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &encoder; + + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("Failed to initialize capture device.\n"); + return -2; + } + + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to start device.\n"); + return -3; + } + + printf("Press Enter to stop recording...\n"); + getchar(); + + ma_device_uninit(&device); + ma_encoder_uninit(&encoder); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_duplex.c b/thirdparty/miniaudio/examples/simple_duplex.c new file mode 100644 index 0000000..c604db5 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_duplex.c @@ -0,0 +1,71 @@ +/* +Demonstrates duplex mode which is where data is captured from a microphone and then output to a speaker device. + +This example captures audio from the default microphone and then outputs it straight to the default playback device +without any kind of modification. If you wanted to, you could also apply filters and effects to the input stream +before outputting to the playback device. + +Note that the microphone and playback device must run in lockstep. Any kind of timing deviation will result in audible +glitching which the backend may not be able to recover from. For this reason, miniaudio forces you to use the same +sample rate for both capture and playback. If internally the native sample rates differ, miniaudio will perform the +sample rate conversion for you automatically. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +#ifdef __EMSCRIPTEN__ +void main_loop__em() +{ +} +#endif + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* In this example the format and channel count are the same for both input and output which means we can just memcpy(). */ + MA_COPY_MEMORY(pOutput, pInput, frameCount * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = ma_format_s16; + deviceConfig.capture.channels = 2; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = ma_format_s16; + deviceConfig.playback.channels = 2; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + +#ifdef __EMSCRIPTEN__ + getchar(); +#endif + + ma_device_start(&device); + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(main_loop__em, 0, 1); +#else + printf("Press Enter to quit...\n"); + getchar(); +#endif + + ma_device_uninit(&device); + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_enumeration.c b/thirdparty/miniaudio/examples/simple_enumeration.c new file mode 100644 index 0000000..3eeaec3 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_enumeration.c @@ -0,0 +1,54 @@ +/* +Demonstrates how to enumerate over devices. + +Device enumaration requires a `ma_context` object which is initialized with `ma_context_init()`. Conceptually, the +context sits above a device. You can have many devices to one context. + +If you use device enumeration, you should explicitly specify the same context you used for enumeration in the call to +`ma_device_init()` when you initialize your devices. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +int main(int argc, char** argv) +{ + ma_result result; + ma_context context; + ma_device_info* pPlaybackDeviceInfos; + ma_uint32 playbackDeviceCount; + ma_device_info* pCaptureDeviceInfos; + ma_uint32 captureDeviceCount; + ma_uint32 iDevice; + + if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { + printf("Failed to initialize context.\n"); + return -2; + } + + result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, &pCaptureDeviceInfos, &captureDeviceCount); + if (result != MA_SUCCESS) { + printf("Failed to retrieve device information.\n"); + return -3; + } + + printf("Playback Devices\n"); + for (iDevice = 0; iDevice < playbackDeviceCount; ++iDevice) { + printf(" %u: %s\n", iDevice, pPlaybackDeviceInfos[iDevice].name); + } + + printf("\n"); + + printf("Capture Devices\n"); + for (iDevice = 0; iDevice < captureDeviceCount; ++iDevice) { + printf(" %u: %s\n", iDevice, pCaptureDeviceInfos[iDevice].name); + } + + + ma_context_uninit(&context); + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_loopback.c b/thirdparty/miniaudio/examples/simple_loopback.c new file mode 100644 index 0000000..c8318d6 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_loopback.c @@ -0,0 +1,82 @@ +/* +Demonstrates how to implement loopback recording. + +This example simply captures data from your default playback device until you press Enter. The output is saved to the +file specified on the command line. + +Loopback mode is when you record audio that is played from a given speaker. It is only supported on WASAPI, but can be +used indirectly with PulseAudio by choosing the appropriate loopback device after enumeration. + +To use loopback mode you just need to set the device type to ma_device_type_loopback and set the capture device config +properties. The output buffer in the callback will be null whereas the input buffer will be valid. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include +#include + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData; + MA_ASSERT(pEncoder != NULL); + + ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL); + + (void)pOutput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_encoder_config encoderConfig; + ma_encoder encoder; + ma_device_config deviceConfig; + ma_device device; + + /* Loopback mode is currently only supported on WASAPI. */ + ma_backend backends[] = { + ma_backend_wasapi + }; + + if (argc < 2) { + printf("No output file.\n"); + return -1; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_f32, 2, 44100); + + if (ma_encoder_init_file(argv[1], &encoderConfig, &encoder) != MA_SUCCESS) { + printf("Failed to initialize output file.\n"); + return -1; + } + + deviceConfig = ma_device_config_init(ma_device_type_loopback); + deviceConfig.capture.pDeviceID = NULL; /* Use default device for this example. Set this to the ID of a _playback_ device if you want to capture from a specific device. */ + deviceConfig.capture.format = encoder.config.format; + deviceConfig.capture.channels = encoder.config.channels; + deviceConfig.sampleRate = encoder.config.sampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &encoder; + + result = ma_device_init_ex(backends, sizeof(backends)/sizeof(backends[0]), NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + printf("Failed to initialize loopback device.\n"); + return -2; + } + + result = ma_device_start(&device); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to start device.\n"); + return -3; + } + + printf("Press Enter to stop recording...\n"); + getchar(); + + ma_device_uninit(&device); + ma_encoder_uninit(&encoder); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_looping.c b/thirdparty/miniaudio/examples/simple_looping.c new file mode 100644 index 0000000..dcc5a2b --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_looping.c @@ -0,0 +1,76 @@ +/* +Shows one way to handle looping of a sound. + +This example uses a decoder as the data source. Decoders can be used with the `ma_data_source` API which, conveniently, +supports looping via the `ma_data_source_read_pcm_frames()` API. To use it, all you need to do is pass a pointer to the +decoder straight into `ma_data_source_read_pcm_frames()` and it will just work. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData; + if (pDecoder == NULL) { + return; + } + + /* Reading PCM frames will loop based on what we specified when called ma_data_source_set_looping(). */ + ma_data_source_read_pcm_frames(pDecoder, pOutput, frameCount, NULL); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder decoder; + ma_device_config deviceConfig; + ma_device device; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + result = ma_decoder_init_file(argv[1], NULL, &decoder); + if (result != MA_SUCCESS) { + return -2; + } + + /* + A decoder is a data source which means we just use ma_data_source_set_looping() to set the + looping state. We will read data using ma_data_source_read_pcm_frames() in the data callback. + */ + ma_data_source_set_looping(&decoder, MA_TRUE); + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = decoder.outputFormat; + deviceConfig.playback.channels = decoder.outputChannels; + deviceConfig.sampleRate = decoder.outputSampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &decoder; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + ma_decoder_uninit(&decoder); + return -3; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + return -4; + } + + printf("Press Enter to quit..."); + getchar(); + + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_mixing.c b/thirdparty/miniaudio/examples/simple_mixing.c new file mode 100644 index 0000000..e4c81b0 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_mixing.c @@ -0,0 +1,199 @@ +/* +Demonstrates one way to load multiple files and play them all back at the same time. + +When mixing multiple sounds together, you should not create multiple devices. Instead you should create only a single +device and then mix your sounds together which you can do by simply summing their samples together. The simplest way to +do this is to use floating point samples and use miniaudio's built-in clipper to handling clipping for you. (Clipping +is when sample are clampled to their minimum and maximum range, which for floating point is -1..1.) + +``` +Usage: simple_mixing [input file 0] [input file 1] ... [input file n] +Example: simple_mixing file1.wav file2.flac +``` +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +/* +For simplicity, this example requires the device to use floating point samples. +*/ +#define SAMPLE_FORMAT ma_format_f32 +#define CHANNEL_COUNT 2 +#define SAMPLE_RATE 48000 + +ma_uint32 g_decoderCount; +ma_decoder* g_pDecoders; +ma_bool32* g_pDecodersAtEnd; + +ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */ + +ma_bool32 are_all_decoders_at_end() +{ + ma_uint32 iDecoder; + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + if (g_pDecodersAtEnd[iDecoder] == MA_FALSE) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + +ma_uint32 read_and_mix_pcm_frames_f32(ma_decoder* pDecoder, float* pOutputF32, ma_uint32 frameCount) +{ + /* + The way mixing works is that we just read into a temporary buffer, then take the contents of that buffer and mix it with the + contents of the output buffer by simply adding the samples together. You could also clip the samples to -1..+1, but I'm not + doing that in this example. + */ + ma_result result; + float temp[4096]; + ma_uint32 tempCapInFrames = ma_countof(temp) / CHANNEL_COUNT; + ma_uint32 totalFramesRead = 0; + + while (totalFramesRead < frameCount) { + ma_uint64 iSample; + ma_uint64 framesReadThisIteration; + ma_uint32 totalFramesRemaining = frameCount - totalFramesRead; + ma_uint32 framesToReadThisIteration = tempCapInFrames; + if (framesToReadThisIteration > totalFramesRemaining) { + framesToReadThisIteration = totalFramesRemaining; + } + + result = ma_decoder_read_pcm_frames(pDecoder, temp, framesToReadThisIteration, &framesReadThisIteration); + if (result != MA_SUCCESS || framesReadThisIteration == 0) { + break; + } + + /* Mix the frames together. */ + for (iSample = 0; iSample < framesReadThisIteration*CHANNEL_COUNT; ++iSample) { + pOutputF32[totalFramesRead*CHANNEL_COUNT + iSample] += temp[iSample]; + } + + totalFramesRead += (ma_uint32)framesReadThisIteration; + + if (framesReadThisIteration < (ma_uint32)framesToReadThisIteration) { + break; /* Reached EOF. */ + } + } + + return totalFramesRead; +} + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + float* pOutputF32 = (float*)pOutput; + ma_uint32 iDecoder; + + MA_ASSERT(pDevice->playback.format == SAMPLE_FORMAT); /* <-- Important for this example. */ + + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + if (!g_pDecodersAtEnd[iDecoder]) { + ma_uint32 framesRead = read_and_mix_pcm_frames_f32(&g_pDecoders[iDecoder], pOutputF32, frameCount); + if (framesRead < frameCount) { + g_pDecodersAtEnd[iDecoder] = MA_TRUE; + } + } + } + + /* + If at the end all of our decoders are at the end we need to stop. We cannot stop the device in the callback. Instead we need to + signal an event to indicate that it's stopped. The main thread will be waiting on the event, after which it will stop the device. + */ + if (are_all_decoders_at_end()) { + ma_event_signal(&g_stopEvent); + } + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_device_config deviceConfig; + ma_device device; + ma_uint32 iDecoder; + + if (argc < 2) { + printf("No input files.\n"); + return -1; + } + + g_decoderCount = argc-1; + g_pDecoders = (ma_decoder*)malloc(sizeof(*g_pDecoders) * g_decoderCount); + g_pDecodersAtEnd = (ma_bool32*) malloc(sizeof(*g_pDecodersAtEnd) * g_decoderCount); + + /* In this example, all decoders need to have the same output format. */ + decoderConfig = ma_decoder_config_init(SAMPLE_FORMAT, CHANNEL_COUNT, SAMPLE_RATE); + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + result = ma_decoder_init_file(argv[1+iDecoder], &decoderConfig, &g_pDecoders[iDecoder]); + if (result != MA_SUCCESS) { + ma_uint32 iDecoder2; + for (iDecoder2 = 0; iDecoder2 < iDecoder; ++iDecoder2) { + ma_decoder_uninit(&g_pDecoders[iDecoder2]); + } + free(g_pDecoders); + free(g_pDecodersAtEnd); + + printf("Failed to load %s.\n", argv[1+iDecoder]); + return -3; + } + g_pDecodersAtEnd[iDecoder] = MA_FALSE; + } + + /* Create only a single device. The decoders will be mixed together in the callback. In this example the data format needs to be the same as the decoders. */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = SAMPLE_FORMAT; + deviceConfig.playback.channels = CHANNEL_COUNT; + deviceConfig.sampleRate = SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = NULL; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + ma_decoder_uninit(&g_pDecoders[iDecoder]); + } + free(g_pDecoders); + free(g_pDecodersAtEnd); + + printf("Failed to open playback device.\n"); + return -3; + } + + /* + We can't stop in the audio thread so we instead need to use an event. We wait on this thread in the main thread, and signal it in the audio thread. This + needs to be done before starting the device. We need a context to initialize the event, which we can get from the device. Alternatively you can initialize + a context separately, but we don't need to do that for this example. + */ + ma_event_init(&g_stopEvent); + + /* Now we start playback and wait for the audio thread to tell us to stop. */ + if (ma_device_start(&device) != MA_SUCCESS) { + ma_device_uninit(&device); + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + ma_decoder_uninit(&g_pDecoders[iDecoder]); + } + free(g_pDecoders); + free(g_pDecodersAtEnd); + + printf("Failed to start playback device.\n"); + return -4; + } + + printf("Waiting for playback to complete...\n"); + ma_event_wait(&g_stopEvent); + + /* Getting here means the audio thread has signaled that the device should be stopped. */ + ma_device_uninit(&device); + + for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) { + ma_decoder_uninit(&g_pDecoders[iDecoder]); + } + free(g_pDecoders); + free(g_pDecodersAtEnd); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_playback.c b/thirdparty/miniaudio/examples/simple_playback.c new file mode 100644 index 0000000..491b4c2 --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_playback.c @@ -0,0 +1,75 @@ +/* +Demonstrates how to load a sound file and play it back using the low-level API. + +The low-level API uses a callback to deliver audio between the application and miniaudio for playback or recording. When +in playback mode, as in this example, the application sends raw audio data to miniaudio which is then played back through +the default playback device as defined by the operating system. + +This example uses the `ma_decoder` API to load a sound and play it back. The decoder is entirely decoupled from the +device and can be used independently of it. This example only plays back a single sound file, but it's possible to play +back multiple files by simple loading multiple decoders and mixing them (do not create multiple devices to do this). See +the simple_mixing example for how best to do this. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData; + if (pDecoder == NULL) { + return; + } + + ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, NULL); + + (void)pInput; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder decoder; + ma_device_config deviceConfig; + ma_device device; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + result = ma_decoder_init_file(argv[1], NULL, &decoder); + if (result != MA_SUCCESS) { + printf("Could not load file: %s\n", argv[1]); + return -2; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = decoder.outputFormat; + deviceConfig.playback.channels = decoder.outputChannels; + deviceConfig.sampleRate = decoder.outputSampleRate; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &decoder; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + ma_decoder_uninit(&decoder); + return -3; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + return -4; + } + + printf("Press Enter to quit..."); + getchar(); + + ma_device_uninit(&device); + ma_decoder_uninit(&decoder); + + return 0; +} diff --git a/thirdparty/miniaudio/examples/simple_playback_sine.c b/thirdparty/miniaudio/examples/simple_playback_sine.c new file mode 100644 index 0000000..ab1f25f --- /dev/null +++ b/thirdparty/miniaudio/examples/simple_playback_sine.c @@ -0,0 +1,91 @@ +/* +Demonstrates playback of a sine wave. + +Since all this example is doing is playing back a sine wave, we can disable decoding (and encoding) which will slightly +reduce the size of the executable. This is done with the `MA_NO_DECODING` and `MA_NO_ENCODING` options. + +The generation of sine wave is achieved via the `ma_waveform` API. A waveform is a data source which means it can be +seamlessly plugged into the `ma_data_source_*()` family of APIs as well. + +A waveform is initialized using the standard config/init pattern used throughout all of miniaudio. Frames are read via +the `ma_waveform_read_pcm_frames()` API. + +This example works with Emscripten. +*/ +#define MA_NO_DECODING +#define MA_NO_ENCODING +#define MINIAUDIO_IMPLEMENTATION +#include "../miniaudio.h" + +#include + +#ifdef __EMSCRIPTEN__ +#include + +void main_loop__em() +{ +} +#endif + +#define DEVICE_FORMAT ma_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 48000 + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + ma_waveform* pSineWave; + + MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); + + pSineWave = (ma_waveform*)pDevice->pUserData; + MA_ASSERT(pSineWave != NULL); + + ma_waveform_read_pcm_frames(pSineWave, pOutput, frameCount, NULL); + + (void)pInput; /* Unused. */ +} + +int main(int argc, char** argv) +{ + ma_waveform sineWave; + ma_device_config deviceConfig; + ma_device device; + ma_waveform_config sineWaveConfig; + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + deviceConfig.pUserData = &sineWave; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + return -4; + } + + printf("Device Name: %s\n", device.playback.name); + + sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220); + ma_waveform_init(&sineWaveConfig, &sineWave); + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start playback device.\n"); + ma_device_uninit(&device); + return -5; + } + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(main_loop__em, 0, 1); +#else + printf("Press Enter to quit...\n"); + getchar(); +#endif + + ma_device_uninit(&device); + ma_waveform_uninit(&sineWave); /* Uninitialize the waveform after the device so we don't pull it from under the device while it's being reference in the data callback. */ + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/extras/miniaudio_libopus.h b/thirdparty/miniaudio/extras/miniaudio_libopus.h new file mode 100644 index 0000000..d2c0ee4 --- /dev/null +++ b/thirdparty/miniaudio/extras/miniaudio_libopus.h @@ -0,0 +1,496 @@ +/* +This implements a data source that decodes Opus streams via libopus + libopusfile + +This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom +decoding backend. See the custom_decoder example. + +You need to include this file after miniaudio.h. +*/ +#ifndef miniaudio_libopus_h +#define miniaudio_libopus_h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MA_NO_LIBOPUS) +#include +#endif + +typedef struct +{ + ma_data_source_base ds; /* The libopus decoder can be used independently as a data source. */ + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Will be either f32 or s16. */ +#if !defined(MA_NO_LIBOPUS) + OggOpusFile* of; +#endif +} ma_libopus; + +MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus); +MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus); +MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex); +MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor); +MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength); + +#ifdef __cplusplus +} +#endif +#endif + +#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) + +static ma_result ma_libopus_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_libopus_read_pcm_frames((ma_libopus*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_libopus_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_libopus_seek_to_pcm_frame((ma_libopus*)pDataSource, frameIndex); +} + +static ma_result ma_libopus_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_libopus_get_data_format((ma_libopus*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_libopus_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_libopus_get_cursor_in_pcm_frames((ma_libopus*)pDataSource, pCursor); +} + +static ma_result ma_libopus_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_libopus_get_length_in_pcm_frames((ma_libopus*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_libopus_ds_vtable = +{ + ma_libopus_ds_read, + ma_libopus_ds_seek, + ma_libopus_ds_get_data_format, + ma_libopus_ds_get_cursor, + ma_libopus_ds_get_length +}; + + +#if !defined(MA_NO_LIBOPUS) +static int ma_libopus_of_callback__read(void* pUserData, unsigned char* pBufferOut, int bytesToRead) +{ + ma_libopus* pOpus = (ma_libopus*)pUserData; + ma_result result; + size_t bytesRead; + + result = pOpus->onRead(pOpus->pReadSeekTellUserData, (void*)pBufferOut, bytesToRead, &bytesRead); + + if (result != MA_SUCCESS) { + return -1; + } + + return (int)bytesRead; +} + +static int ma_libopus_of_callback__seek(void* pUserData, ogg_int64_t offset, int whence) +{ + ma_libopus* pOpus = (ma_libopus*)pUserData; + ma_result result; + ma_seek_origin origin; + + if (whence == SEEK_SET) { + origin = ma_seek_origin_start; + } else if (whence == SEEK_END) { + origin = ma_seek_origin_end; + } else { + origin = ma_seek_origin_current; + } + + result = pOpus->onSeek(pOpus->pReadSeekTellUserData, offset, origin); + if (result != MA_SUCCESS) { + return -1; + } + + return 0; +} + +static opus_int64 ma_libopus_of_callback__tell(void* pUserData) +{ + ma_libopus* pOpus = (ma_libopus*)pUserData; + ma_result result; + ma_int64 cursor; + + if (pOpus->onTell == NULL) { + return -1; + } + + result = pOpus->onTell(pOpus->pReadSeekTellUserData, &cursor); + if (result != MA_SUCCESS) { + return -1; + } + + return cursor; +} +#endif + +static ma_result ma_libopus_init_internal(const ma_decoding_backend_config* pConfig, ma_libopus* pOpus) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pOpus == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pOpus); + pOpus->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { + pOpus->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_libopus_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pOpus->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus) +{ + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */ + + result = ma_libopus_init_internal(pConfig, pOpus); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pOpus->onRead = onRead; + pOpus->onSeek = onSeek; + pOpus->onTell = onTell; + pOpus->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_LIBOPUS) + { + int libopusResult; + OpusFileCallbacks libopusCallbacks; + + /* We can now initialize the Opus decoder. This must be done after we've set up the callbacks. */ + libopusCallbacks.read = ma_libopus_of_callback__read; + libopusCallbacks.seek = ma_libopus_of_callback__seek; + libopusCallbacks.close = NULL; + libopusCallbacks.tell = ma_libopus_of_callback__tell; + + pOpus->of = op_open_callbacks(pOpus, &libopusCallbacks, NULL, 0, &libopusResult); + if (pOpus->of == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. */ + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus) +{ + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */ + + result = ma_libopus_init_internal(pConfig, pOpus); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_LIBOPUS) + { + int libopusResult; + + pOpus->of = op_open_file(pFilePath, &libopusResult); + if (pOpus->of == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. */ + (void)pFilePath; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pOpus == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_LIBOPUS) + { + op_free(pOpus->of); + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pOpus->ds); +} + +MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pOpus == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBOPUS) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead; + ma_format format; + ma_uint32 channels; + + ma_libopus_get_data_format(pOpus, &format, &channels, NULL, NULL, 0); + + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + long libopusResult; + int framesToRead; + ma_uint64 framesRemaining; + + framesRemaining = (frameCount - totalFramesRead); + framesToRead = 1024; + if (framesToRead > framesRemaining) { + framesToRead = (int)framesRemaining; + } + + if (format == ma_format_f32) { + libopusResult = op_read_float(pOpus->of, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * channels, NULL); + } else { + libopusResult = op_read (pOpus->of, (opus_int16*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * channels, NULL); + } + + if (libopusResult < 0) { + result = MA_ERROR; /* Error while decoding. */ + break; + } else { + totalFramesRead += libopusResult; + + if (libopusResult == 0) { + result = MA_AT_END; + break; + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex) +{ + if (pOpus == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBOPUS) + { + int libopusResult = op_pcm_seek(pOpus->of, (ogg_int64_t)frameIndex); + if (libopusResult != 0) { + if (libopusResult == OP_ENOSEEK) { + return MA_INVALID_OPERATION; /* Not seekable. */ + } else if (libopusResult == OP_EINVAL) { + return MA_INVALID_ARGS; + } else { + return MA_ERROR; + } + } + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pOpus == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pOpus->format; + } + + #if !defined(MA_NO_LIBOPUS) + { + ma_uint32 channels = op_channel_count(pOpus->of, -1); + + if (pChannels != NULL) { + *pChannels = channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = 48000; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, channels); + } + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pOpus == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBOPUS) + { + ogg_int64_t offset = op_pcm_tell(pOpus->of); + if (offset < 0) { + return MA_INVALID_FILE; + } + + *pCursor = (ma_uint64)offset; + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pOpus == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBOPUS) + { + ogg_int64_t length = op_pcm_total(pOpus->of, -1); + if (length < 0) { + return MA_ERROR; + } + + *pLength = (ma_uint64)length; + + return MA_SUCCESS; + } + #else + { + /* libopus is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +#endif diff --git a/thirdparty/miniaudio/extras/miniaudio_libvorbis.h b/thirdparty/miniaudio/extras/miniaudio_libvorbis.h new file mode 100644 index 0000000..9a62358 --- /dev/null +++ b/thirdparty/miniaudio/extras/miniaudio_libvorbis.h @@ -0,0 +1,516 @@ +/* +This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile + +This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom +decoding backend. See the custom_decoder example. + +You need to include this file after miniaudio.h. +*/ +#ifndef miniaudio_libvorbis_h +#define miniaudio_libvorbis_h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(MA_NO_LIBVORBIS) +#ifndef OV_EXCLUDE_STATIC_CALLBACKS +#define OV_EXCLUDE_STATIC_CALLBACKS +#endif +#include +#endif + +typedef struct +{ + ma_data_source_base ds; /* The libvorbis decoder can be used independently as a data source. */ + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Will be either f32 or s16. */ +#if !defined(MA_NO_LIBVORBIS) + OggVorbis_File vf; +#endif +} ma_libvorbis; + +MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis); +MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis); +MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex); +MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor); +MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength); + +#ifdef __cplusplus +} +#endif +#endif + +#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) + +static ma_result ma_libvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_libvorbis_read_pcm_frames((ma_libvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_libvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_libvorbis_seek_to_pcm_frame((ma_libvorbis*)pDataSource, frameIndex); +} + +static ma_result ma_libvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_libvorbis_get_data_format((ma_libvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_libvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_libvorbis_get_cursor_in_pcm_frames((ma_libvorbis*)pDataSource, pCursor); +} + +static ma_result ma_libvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_libvorbis_get_length_in_pcm_frames((ma_libvorbis*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_libvorbis_ds_vtable = +{ + ma_libvorbis_ds_read, + ma_libvorbis_ds_seek, + ma_libvorbis_ds_get_data_format, + ma_libvorbis_ds_get_cursor, + ma_libvorbis_ds_get_length +}; + + +#if !defined(MA_NO_LIBVORBIS) +static size_t ma_libvorbis_vf_callback__read(void* pBufferOut, size_t size, size_t count, void* pUserData) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData; + ma_result result; + size_t bytesToRead; + size_t bytesRead; + + /* For consistency with fread(). If `size` of `count` is 0, return 0 immediately without changing anything. */ + if (size == 0 || count == 0) { + return 0; + } + + bytesToRead = size * count; + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + if (result != MA_SUCCESS) { + /* Not entirely sure what to return here. What if an error occurs, but some data was read and bytesRead is > 0? */ + return 0; + } + + return bytesRead / size; +} + +static int ma_libvorbis_vf_callback__seek(void* pUserData, ogg_int64_t offset, int whence) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData; + ma_result result; + ma_seek_origin origin; + + if (whence == SEEK_SET) { + origin = ma_seek_origin_start; + } else if (whence == SEEK_END) { + origin = ma_seek_origin_end; + } else { + origin = ma_seek_origin_current; + } + + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, offset, origin); + if (result != MA_SUCCESS) { + return -1; + } + + return 0; +} + +static long ma_libvorbis_vf_callback__tell(void* pUserData) +{ + ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData; + ma_result result; + ma_int64 cursor; + + result = pVorbis->onTell(pVorbis->pReadSeekTellUserData, &cursor); + if (result != MA_SUCCESS) { + return -1; + } + + return (long)cursor; +} +#endif + +static ma_result ma_libvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_libvorbis* pVorbis) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pVorbis); + pVorbis->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { + pVorbis->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_libvorbis_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis) +{ + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */ + + result = ma_libvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pVorbis->onRead = onRead; + pVorbis->onSeek = onSeek; + pVorbis->onTell = onTell; + pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_LIBVORBIS) + { + int libvorbisResult; + ov_callbacks libvorbisCallbacks; + + /* We can now initialize the vorbis decoder. This must be done after we've set up the callbacks. */ + libvorbisCallbacks.read_func = ma_libvorbis_vf_callback__read; + libvorbisCallbacks.seek_func = ma_libvorbis_vf_callback__seek; + libvorbisCallbacks.close_func = NULL; + libvorbisCallbacks.tell_func = ma_libvorbis_vf_callback__tell; + + libvorbisResult = ov_open_callbacks(pVorbis, &pVorbis->vf, NULL, 0, libvorbisCallbacks); + if (libvorbisResult < 0) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. */ + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis) +{ + ma_result result; + + (void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */ + + result = ma_libvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_LIBVORBIS) + { + int libvorbisResult; + + libvorbisResult = ov_fopen(pFilePath, &pVorbis->vf); + if (libvorbisResult < 0) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. */ + (void)pFilePath; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pVorbis == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_LIBVORBIS) + { + ov_clear(&pVorbis->vf); + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pVorbis->ds); +} + +MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBVORBIS) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead; + ma_format format; + ma_uint32 channels; + + ma_libvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); + + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + long libvorbisResult; + int framesToRead; + ma_uint64 framesRemaining; + + framesRemaining = (frameCount - totalFramesRead); + framesToRead = 1024; + if (framesToRead > framesRemaining) { + framesToRead = (int)framesRemaining; + } + + if (format == ma_format_f32) { + float** ppFramesF32; + + libvorbisResult = ov_read_float(&pVorbis->vf, &ppFramesF32, framesToRead, NULL); + if (libvorbisResult < 0) { + result = MA_ERROR; /* Error while decoding. */ + break; + } else { + /* Frames need to be interleaved. */ + ma_interleave_pcm_frames(format, channels, libvorbisResult, (const void**)ppFramesF32, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels)); + totalFramesRead += libvorbisResult; + + if (libvorbisResult == 0) { + result = MA_AT_END; + break; + } + } + } else { + libvorbisResult = ov_read(&pVorbis->vf, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * ma_get_bytes_per_frame(format, channels), 0, 2, 1, NULL); + if (libvorbisResult < 0) { + result = MA_ERROR; /* Error while decoding. */ + break; + } else { + /* Conveniently, there's no need to interleaving when using ov_read(). I'm not sure why ov_read_float() is different in that regard... */ + totalFramesRead += libvorbisResult / ma_get_bytes_per_frame(format, channels); + + if (libvorbisResult == 0) { + result = MA_AT_END; + break; + } + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex) +{ + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBVORBIS) + { + int libvorbisResult = ov_pcm_seek(&pVorbis->vf, (ogg_int64_t)frameIndex); + if (libvorbisResult != 0) { + if (libvorbisResult == OV_ENOSEEK) { + return MA_INVALID_OPERATION; /* Not seekable. */ + } else if (libvorbisResult == OV_EINVAL) { + return MA_INVALID_ARGS; + } else { + return MA_ERROR; + } + } + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pVorbis == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pVorbis->format; + } + + #if !defined(MA_NO_LIBVORBIS) + { + vorbis_info* pInfo = ov_info(&pVorbis->vf, 0); + if (pInfo == NULL) { + return MA_INVALID_OPERATION; + } + + if (pChannels != NULL) { + *pChannels = pInfo->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pInfo->rate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pInfo->channels); + } + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBVORBIS) + { + ogg_int64_t offset = ov_pcm_tell(&pVorbis->vf); + if (offset < 0) { + return MA_INVALID_FILE; + } + + *pCursor = (ma_uint64)offset; + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_LIBVORBIS) + { + /* I don't know how to reliably retrieve the length in frames using libvorbis, so returning 0 for now. */ + *pLength = 0; + + return MA_SUCCESS; + } + #else + { + /* libvorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +#endif diff --git a/thirdparty/miniaudio/extras/miniaudio_split/README.md b/thirdparty/miniaudio/extras/miniaudio_split/README.md new file mode 100644 index 0000000..8aa145e --- /dev/null +++ b/thirdparty/miniaudio/extras/miniaudio_split/README.md @@ -0,0 +1,7 @@ +These files split the main library into separate .h and .c files. This is intended for those who prefer separate files +or whose build environment better suits this configuration. The files here are generated from a tool based on the +content in the main miniaudio.h file. Do not edit these files directly. If you want to contribute, please make the +contribution in the main file. + +This is not always up to date with the most recent commit in the dev branch, but will usually be up to date with the +master branch. \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.c b/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.c new file mode 100644 index 0000000..651843b --- /dev/null +++ b/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.c @@ -0,0 +1,81195 @@ +/* +Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. +miniaudio - v0.11.21 - 2023-11-15 + +David Reid - mackron@gmail.com + +Website: https://miniaud.io +Documentation: https://miniaud.io/docs +GitHub: https://github.com/mackron/miniaudio +*/ +#include "miniaudio.h" + +#ifndef miniaudio_c +#define miniaudio_c + +#include +#include /* For INT_MAX */ +#include /* sin(), etc. */ +#include /* For malloc(), free(), wcstombs(). */ +#include /* For memset() */ + +#include +#include +#if !defined(_MSC_VER) && !defined(__DMC__) + #include /* For strcasecmp(). */ + #include /* For wcslen(), wcsrtombs() */ +#endif +#ifdef _MSC_VER + #include /* For _controlfp_s constants */ +#endif + +#if defined(MA_WIN32) + #include + + /* + There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols + such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're + unavailable. + */ + #ifndef STGM_READ + #define STGM_READ 0x00000000L + #endif + #ifndef CLSCTX_ALL + #define CLSCTX_ALL 23 + #endif + + /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ + typedef struct ma_IUnknown ma_IUnknown; +#endif + +#if !defined(MA_WIN32) +#include +#include /* select() (used for ma_sleep()). */ +#include +#endif + +#ifdef MA_NX +#include /* For nanosleep() */ +#endif + +#include /* For fstat(), etc. */ + +#ifdef MA_EMSCRIPTEN +#include +#endif + + +/* Architecture Detection */ +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#ifdef _WIN32 +#ifdef _WIN64 +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif +#endif + +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#ifdef __GNUC__ +#ifdef __LP64__ +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif +#endif + +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#include +#if INTPTR_MAX == INT64_MAX +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define MA_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define MA_ARM64 +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define MA_X64 +#elif defined(__i386) || defined(_M_IX86) +#define MA_X86 +#elif defined(MA_ARM32) || defined(MA_ARM64) +#define MA_ARM +#endif + +/* Intrinsics Support */ +#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) + #if defined(_MSC_VER) && !defined(__clang__) + /* MSVC. */ + #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ + #define MA_SUPPORT_SSE2 + #endif + /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ + #define MA_SUPPORT_AVX2 + #endif + #else + /* Assume GNUC-style. */ + #if defined(__SSE2__) && !defined(MA_NO_SSE2) + #define MA_SUPPORT_SSE2 + #endif + /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if defined(__AVX2__) && !defined(MA_NO_AVX2) + #define MA_SUPPORT_AVX2 + #endif + #endif + + /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() + #define MA_SUPPORT_SSE2 + #endif + /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() + #define MA_SUPPORT_AVX2 + #endif + #endif + + #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) + #include + #elif defined(MA_SUPPORT_SSE2) + #include + #endif +#endif + +#if defined(MA_ARM) + #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_SUPPORT_NEON + #include + #endif +#endif + +/* Begin globally disabled warnings. */ +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ + #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ +#endif + +#if defined(MA_X64) || defined(MA_X86) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static MA_INLINE void ma_cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define MA_NO_CPUID + #endif + + #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) + static MA_INLINE unsigned __int64 ma_xgetbv(int reg) + { + return _xgetbv(reg); + } + #else + #define MA_NO_XGETBV + #endif + #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) + static MA_INLINE void ma_cpuid(int info[4], int fid) + { + /* + 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(MA_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 MA_INLINE ma_uint64 ma_xgetbv(int reg) + { + unsigned int hi; + unsigned int lo; + + __asm__ __volatile__ ( + "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) + ); + + return ((ma_uint64)hi << 32) | (ma_uint64)lo; + } + #else + #define MA_NO_CPUID + #define MA_NO_XGETBV + #endif +#else + #define MA_NO_CPUID + #define MA_NO_XGETBV +#endif + +static MA_INLINE ma_bool32 ma_has_sse2(void) +{ +#if defined(MA_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; /* 64-bit targets always support SSE2. */ + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ + #else + #if defined(MA_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +#if 0 +static MA_INLINE ma_bool32 ma_has_avx() +{ +#if defined(MA_SUPPORT_AVX) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) + #if defined(_AVX_) || defined(__AVX__) + return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ + #else + /* AVX requires both CPU and OS support. */ + #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) + return MA_FALSE; + #else + int info[4]; + ma_cpuid(info, 1); + if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { + ma_uint64 xrc = ma_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + #endif + #endif + #else + return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} +#endif + +static MA_INLINE ma_bool32 ma_has_avx2(void) +{ +#if defined(MA_SUPPORT_AVX2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) + #if defined(_AVX2_) || defined(__AVX2__) + return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ + #else + /* AVX2 requires both CPU and OS support. */ + #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) + return MA_FALSE; + #else + int info1[4]; + int info7[4]; + ma_cpuid(info1, 1); + ma_cpuid(info7, 7); + if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { + ma_uint64 xrc = ma_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + #endif + #endif + #else + return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +static MA_INLINE ma_bool32 ma_has_neon(void) +{ +#if defined(MA_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ + #else + /* TODO: Runtime check. */ + return MA_FALSE; + #endif + #else + return MA_FALSE; /* NEON is only supported on ARM architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +#if defined(__has_builtin) + #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) +#else + #define MA_COMPILER_HAS_BUILTIN(x) 0 +#endif + +#ifndef MA_ASSUME + #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) + #define MA_ASSUME(x) __builtin_assume(x) + #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) + #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) + #elif defined(_MSC_VER) + #define MA_ASSUME(x) __assume(x) + #else + #define MA_ASSUME(x) (void)(x) + #endif +#endif + +#ifndef MA_RESTRICT + #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) + #define MA_RESTRICT __restrict + #else + #define MA_RESTRICT + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define MA_HAS_BYTESWAP16_INTRINSIC + #define MA_HAS_BYTESWAP32_INTRINSIC + #define MA_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) + #define MA_HAS_BYTESWAP16_INTRINSIC + #endif + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) + #define MA_HAS_BYTESWAP32_INTRINSIC + #endif + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) + #define MA_HAS_BYTESWAP64_INTRINSIC + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_HAS_BYTESWAP32_INTRINSIC + #define MA_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_HAS_BYTESWAP16_INTRINSIC + #endif +#endif + + +static MA_INLINE ma_bool32 ma_is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static MA_INLINE ma_bool32 ma_is_big_endian(void) +{ + return !ma_is_little_endian(); +} + + +static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) +{ +#ifdef MA_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + + +#if !defined(MA_EMSCRIPTEN) +#ifdef MA_WIN32 +static void ma_sleep__win32(ma_uint32 milliseconds) +{ + Sleep((DWORD)milliseconds); +} +#endif +#ifdef MA_POSIX +static void ma_sleep__posix(ma_uint32 milliseconds) +{ +#ifdef MA_EMSCRIPTEN + (void)milliseconds; + MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ +#else + #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = milliseconds % 1000 * 1000000; + nanosleep(&ts, NULL); + #else + struct timeval tv; + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = milliseconds % 1000 * 1000; + select(0, NULL, NULL, NULL, &tv); + #endif +#endif +} +#endif + +static MA_INLINE void ma_sleep(ma_uint32 milliseconds) +{ +#ifdef MA_WIN32 + ma_sleep__win32(milliseconds); +#endif +#ifdef MA_POSIX + ma_sleep__posix(milliseconds); +#endif +} +#endif + +static MA_INLINE void ma_yield(void) +{ +#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) + /* x86/x64 */ + #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) + #if _MSC_VER >= 1400 + _mm_pause(); + #else + #if defined(__DMC__) + /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ + __asm nop; + #else + __asm pause; + #endif + #endif + #else + __asm__ __volatile__ ("pause"); + #endif +#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) + /* ARM */ + #if defined(_MSC_VER) + /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ + __yield(); + #else + __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ + #endif +#else + /* Unknown or unsupported architecture. No-op. */ +#endif +} + + +#define MA_MM_DENORMALS_ZERO_MASK 0x0040 +#define MA_MM_FLUSH_ZERO_MASK 0x8000 + +static MA_INLINE unsigned int ma_disable_denormals(void) +{ + unsigned int prevState; + + #if defined(_MSC_VER) + { + /* + Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't + know which version of Visual Studio first added support for _controlfp_s(), but I do know + that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older + versions of Visual Studio, let me know and I'll make the necessary adjustment. + */ + #if _MSC_VER <= 1200 + { + prevState = _statusfp(); + _controlfp(prevState | _DN_FLUSH, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&prevState, 0, 0); + _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + prevState = _mm_getcsr(); + _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + prevState = 0; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + prevState = 0; + } + #endif + + return prevState; +} + +static MA_INLINE void ma_restore_denormals(unsigned int prevState) +{ + #if defined(_MSC_VER) + { + /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ + #if _MSC_VER <= 1200 + { + _controlfp(prevState, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&unused, prevState, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + _mm_setcsr(prevState); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + (void)prevState; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + (void)prevState; + } + #endif +} + + +#ifdef MA_ANDROID +#include + +int ma_android_sdk_version() +{ + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + return atoi(sdkVersion); + } + + return 0; +} +#endif + + +#ifndef MA_COINIT_VALUE +#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ +#endif + + +#ifndef MA_FLT_MAX + #ifdef FLT_MAX + #define MA_FLT_MAX FLT_MAX + #else + #define MA_FLT_MAX 3.402823466e+38F + #endif +#endif + + +#ifndef MA_PI +#define MA_PI 3.14159265358979323846264f +#endif +#ifndef MA_PI_D +#define MA_PI_D 3.14159265358979323846264 +#endif +#ifndef MA_TAU +#define MA_TAU 6.28318530717958647693f +#endif +#ifndef MA_TAU_D +#define MA_TAU_D 6.28318530717958647693 +#endif + + +/* The default format when ma_format_unknown (0) is requested when initializing a device. */ +#ifndef MA_DEFAULT_FORMAT +#define MA_DEFAULT_FORMAT ma_format_f32 +#endif + +/* The default channel count to use when 0 is used when initializing a device. */ +#ifndef MA_DEFAULT_CHANNELS +#define MA_DEFAULT_CHANNELS 2 +#endif + +/* The default sample rate to use when 0 is used when initializing a device. */ +#ifndef MA_DEFAULT_SAMPLE_RATE +#define MA_DEFAULT_SAMPLE_RATE 48000 +#endif + +/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ +#ifndef MA_DEFAULT_PERIODS +#define MA_DEFAULT_PERIODS 3 +#endif + +/* The default period size in milliseconds for low latency mode. */ +#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY +#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 +#endif + +/* The default buffer size in milliseconds for conservative mode. */ +#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE +#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 +#endif + +/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ +#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER + #if MA_MAX_FILTER_ORDER >= 4 + #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 + #else + #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER + #endif +#endif + + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-variable" +#endif + +/* Standard sample rates, in order of priority. */ +static ma_uint32 g_maStandardSampleRatePriorities[] = { + (ma_uint32)ma_standard_sample_rate_48000, + (ma_uint32)ma_standard_sample_rate_44100, + + (ma_uint32)ma_standard_sample_rate_32000, + (ma_uint32)ma_standard_sample_rate_24000, + (ma_uint32)ma_standard_sample_rate_22050, + + (ma_uint32)ma_standard_sample_rate_88200, + (ma_uint32)ma_standard_sample_rate_96000, + (ma_uint32)ma_standard_sample_rate_176400, + (ma_uint32)ma_standard_sample_rate_192000, + + (ma_uint32)ma_standard_sample_rate_16000, + (ma_uint32)ma_standard_sample_rate_11025, + (ma_uint32)ma_standard_sample_rate_8000, + + (ma_uint32)ma_standard_sample_rate_352800, + (ma_uint32)ma_standard_sample_rate_384000 +}; + +static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) +{ + ma_uint32 iSampleRate; + + for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { + if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { + return MA_TRUE; + } + } + + /* Getting here means the sample rate is not supported. */ + return MA_FALSE; +} + + +static ma_format g_maFormatPriorities[] = { + ma_format_s16, /* Most common */ + ma_format_f32, + + /*ma_format_s24_32,*/ /* Clean alignment */ + ma_format_s32, + + ma_format_s24, /* Unclean alignment */ + + ma_format_u8 /* Low quality */ +}; +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif + + +MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = MA_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = MA_VERSION_REVISION; + } +} + +MA_API const char* ma_version_string(void) +{ + return MA_VERSION_STRING; +} + + +/****************************************************************************** + +Standard Library Stuff + +******************************************************************************/ +#ifndef MA_ASSERT +#define MA_ASSERT(condition) assert(condition) +#endif + +#ifndef MA_MALLOC +#define MA_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_REALLOC +#define MA_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_FREE +#define MA_FREE(p) free((p)) +#endif + +static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) +{ + if (p == NULL) { + MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ + return; + } + + if (sz > 0) { + memset(p, 0, sz); + } +} + + +#ifndef MA_ZERO_MEMORY +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) +#endif +#ifndef MA_COPY_MEMORY +#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_MOVE_MEMORY +#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif + +#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) + +#define ma_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) +#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) +#define ma_abs(x) (((x) > 0) ? (x) : -(x)) +#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) +#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) +#define ma_align_64(x) ma_align(x, 8) + +#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) + +static MA_INLINE double ma_sind(double x) +{ + /* TODO: Implement custom sin(x). */ + return sin(x); +} + +static MA_INLINE double ma_expd(double x) +{ + /* TODO: Implement custom exp(x). */ + return exp(x); +} + +static MA_INLINE double ma_logd(double x) +{ + /* TODO: Implement custom log(x). */ + return log(x); +} + +static MA_INLINE double ma_powd(double x, double y) +{ + /* TODO: Implement custom pow(x, y). */ + return pow(x, y); +} + +static MA_INLINE double ma_sqrtd(double x) +{ + /* TODO: Implement custom sqrt(x). */ + return sqrt(x); +} + + +static MA_INLINE float ma_rsqrtf(float x) +{ + #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) + { + /* + For SSE we can use RSQRTSS. + + This Stack Overflow post suggests that compilers don't necessarily generate optimal code + when using intrinsics: + + https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper + + I'm going to do something similar here, but a bit simpler. + */ + #if defined(__GNUC__) || defined(__clang__) + { + float result; + __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); + return result; + } + #else + { + return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); + } + #endif + } + #else + { + return 1 / (float)ma_sqrtd(x); + } + #endif +} + + +static MA_INLINE float ma_sinf(float x) +{ + return (float)ma_sind((float)x); +} + +static MA_INLINE double ma_cosd(double x) +{ + return ma_sind((MA_PI_D*0.5) - x); +} + +static MA_INLINE float ma_cosf(float x) +{ + return (float)ma_cosd((float)x); +} + +static MA_INLINE double ma_log10d(double x) +{ + return ma_logd(x) * 0.43429448190325182765; +} + +static MA_INLINE float ma_powf(float x, float y) +{ + return (float)ma_powd((double)x, (double)y); +} + +static MA_INLINE float ma_log10f(float x) +{ + return (float)ma_log10d((double)x); +} + + +static MA_INLINE double ma_degrees_to_radians(double degrees) +{ + return degrees * 0.01745329252; +} + +static MA_INLINE double ma_radians_to_degrees(double radians) +{ + return radians * 57.295779512896; +} + +static MA_INLINE float ma_degrees_to_radians_f(float degrees) +{ + return degrees * 0.01745329252f; +} + +static MA_INLINE float ma_radians_to_degrees_f(float radians) +{ + return radians * 57.295779512896f; +} + + +/* +Return Values: + 0: Success + 22: EINVAL + 34: ERANGE + +Not using symbolic constants for errors because I want to avoid #including errno.h + +These are marked as no-inline because of some bad code generation by Clang. None of these functions +are used in any performance-critical code within miniaudio. +*/ +MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstSizeInBytes) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + + +MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + size_t maxcount; + size_t i; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + maxcount = count; + if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ + maxcount = dstSizeInBytes - 1; + } + + for (i = 0; i < maxcount && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (src[i] == '\0' || i == count || count == ((size_t)-1)) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + char* dstorig; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; /* Unterminated. */ + } + + + while (dstSizeInBytes > 0 && src[0] != '\0') { + *dst++ = *src++; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + char* dstorig; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + return 22; + } + + dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; /* Unterminated. */ + } + + + if (count == ((size_t)-1)) { /* _TRUNCATE */ + count = dstSizeInBytes - 1; + } + + while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { + *dst++ = *src++; + dstSizeInBytes -= 1; + count -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +{ + int sign; + unsigned int valueU; + char* dstEnd; + + if (dst == NULL || dstSizeInBytes == 0) { + return 22; + } + if (radix < 2 || radix > 36) { + dst[0] = '\0'; + return 22; + } + + sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ + + if (value < 0) { + valueU = -value; + } else { + valueU = value; + } + + dstEnd = dst; + do + { + int remainder = valueU % radix; + if (remainder > 9) { + *dstEnd = (char)((remainder - 10) + 'a'); + } else { + *dstEnd = (char)(remainder + '0'); + } + + dstEnd += 1; + dstSizeInBytes -= 1; + valueU /= radix; + } while (dstSizeInBytes > 0 && valueU > 0); + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; /* Ran out of room in the output buffer. */ + } + + if (sign < 0) { + *dstEnd++ = '-'; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; /* Ran out of room in the output buffer. */ + } + + *dstEnd = '\0'; + + + /* At this point the string will be reversed. */ + dstEnd -= 1; + while (dst < dstEnd) { + char temp = *dst; + *dst = *dstEnd; + *dstEnd = temp; + + dst += 1; + dstEnd -= 1; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) +{ + if (str1 == str2) return 0; + + /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ + if (str1 == NULL) return -1; + if (str2 == NULL) return 1; + + for (;;) { + if (str1[0] == '\0') { + break; + } + if (str1[0] != str2[0]) { + break; + } + + str1 += 1; + str2 += 1; + } + + return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; +} + +MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +{ + int result; + + result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); + if (result != 0) { + return result; + } + + result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); + if (result != 0) { + return result; + } + + return result; +} + +MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t sz; + char* dst; + + if (src == NULL) { + return NULL; + } + + sz = strlen(src)+1; + dst = (char*)ma_malloc(sz, pAllocationCallbacks); + if (dst == NULL) { + return NULL; + } + + ma_strcpy_s(dst, sz, src); + + return dst; +} + +MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t sz = wcslen(src)+1; + wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); + if (dst == NULL) { + return NULL; + } + + ma_wcscpy_s(dst, sz, src); + + return dst; +} + + + +#include +static ma_result ma_result_from_errno(int e) +{ + if (e == 0) { + return MA_SUCCESS; + } +#ifdef EPERM + else if (e == EPERM) { return MA_INVALID_OPERATION; } +#endif +#ifdef ENOENT + else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ESRCH + else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EINTR + else if (e == EINTR) { return MA_INTERRUPT; } +#endif +#ifdef EIO + else if (e == EIO) { return MA_IO_ERROR; } +#endif +#ifdef ENXIO + else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef E2BIG + else if (e == E2BIG) { return MA_INVALID_ARGS; } +#endif +#ifdef ENOEXEC + else if (e == ENOEXEC) { return MA_INVALID_FILE; } +#endif +#ifdef EBADF + else if (e == EBADF) { return MA_INVALID_FILE; } +#endif +#ifdef ECHILD + else if (e == ECHILD) { return MA_ERROR; } +#endif +#ifdef EAGAIN + else if (e == EAGAIN) { return MA_UNAVAILABLE; } +#endif +#ifdef ENOMEM + else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } +#endif +#ifdef EACCES + else if (e == EACCES) { return MA_ACCESS_DENIED; } +#endif +#ifdef EFAULT + else if (e == EFAULT) { return MA_BAD_ADDRESS; } +#endif +#ifdef ENOTBLK + else if (e == ENOTBLK) { return MA_ERROR; } +#endif +#ifdef EBUSY + else if (e == EBUSY) { return MA_BUSY; } +#endif +#ifdef EEXIST + else if (e == EEXIST) { return MA_ALREADY_EXISTS; } +#endif +#ifdef EXDEV + else if (e == EXDEV) { return MA_ERROR; } +#endif +#ifdef ENODEV + else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ENOTDIR + else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } +#endif +#ifdef EISDIR + else if (e == EISDIR) { return MA_IS_DIRECTORY; } +#endif +#ifdef EINVAL + else if (e == EINVAL) { return MA_INVALID_ARGS; } +#endif +#ifdef ENFILE + else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef EMFILE + else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef ENOTTY + else if (e == ENOTTY) { return MA_INVALID_OPERATION; } +#endif +#ifdef ETXTBSY + else if (e == ETXTBSY) { return MA_BUSY; } +#endif +#ifdef EFBIG + else if (e == EFBIG) { return MA_TOO_BIG; } +#endif +#ifdef ENOSPC + else if (e == ENOSPC) { return MA_NO_SPACE; } +#endif +#ifdef ESPIPE + else if (e == ESPIPE) { return MA_BAD_SEEK; } +#endif +#ifdef EROFS + else if (e == EROFS) { return MA_ACCESS_DENIED; } +#endif +#ifdef EMLINK + else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef EPIPE + else if (e == EPIPE) { return MA_BAD_PIPE; } +#endif +#ifdef EDOM + else if (e == EDOM) { return MA_OUT_OF_RANGE; } +#endif +#ifdef ERANGE + else if (e == ERANGE) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EDEADLK + else if (e == EDEADLK) { return MA_DEADLOCK; } +#endif +#ifdef ENAMETOOLONG + else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } +#endif +#ifdef ENOLCK + else if (e == ENOLCK) { return MA_ERROR; } +#endif +#ifdef ENOSYS + else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } +#endif +#ifdef ENOTEMPTY + else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } +#endif +#ifdef ELOOP + else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef ENOMSG + else if (e == ENOMSG) { return MA_NO_MESSAGE; } +#endif +#ifdef EIDRM + else if (e == EIDRM) { return MA_ERROR; } +#endif +#ifdef ECHRNG + else if (e == ECHRNG) { return MA_ERROR; } +#endif +#ifdef EL2NSYNC + else if (e == EL2NSYNC) { return MA_ERROR; } +#endif +#ifdef EL3HLT + else if (e == EL3HLT) { return MA_ERROR; } +#endif +#ifdef EL3RST + else if (e == EL3RST) { return MA_ERROR; } +#endif +#ifdef ELNRNG + else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EUNATCH + else if (e == EUNATCH) { return MA_ERROR; } +#endif +#ifdef ENOCSI + else if (e == ENOCSI) { return MA_ERROR; } +#endif +#ifdef EL2HLT + else if (e == EL2HLT) { return MA_ERROR; } +#endif +#ifdef EBADE + else if (e == EBADE) { return MA_ERROR; } +#endif +#ifdef EBADR + else if (e == EBADR) { return MA_ERROR; } +#endif +#ifdef EXFULL + else if (e == EXFULL) { return MA_ERROR; } +#endif +#ifdef ENOANO + else if (e == ENOANO) { return MA_ERROR; } +#endif +#ifdef EBADRQC + else if (e == EBADRQC) { return MA_ERROR; } +#endif +#ifdef EBADSLT + else if (e == EBADSLT) { return MA_ERROR; } +#endif +#ifdef EBFONT + else if (e == EBFONT) { return MA_INVALID_FILE; } +#endif +#ifdef ENOSTR + else if (e == ENOSTR) { return MA_ERROR; } +#endif +#ifdef ENODATA + else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ETIME + else if (e == ETIME) { return MA_TIMEOUT; } +#endif +#ifdef ENOSR + else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ENONET + else if (e == ENONET) { return MA_NO_NETWORK; } +#endif +#ifdef ENOPKG + else if (e == ENOPKG) { return MA_ERROR; } +#endif +#ifdef EREMOTE + else if (e == EREMOTE) { return MA_ERROR; } +#endif +#ifdef ENOLINK + else if (e == ENOLINK) { return MA_ERROR; } +#endif +#ifdef EADV + else if (e == EADV) { return MA_ERROR; } +#endif +#ifdef ESRMNT + else if (e == ESRMNT) { return MA_ERROR; } +#endif +#ifdef ECOMM + else if (e == ECOMM) { return MA_ERROR; } +#endif +#ifdef EPROTO + else if (e == EPROTO) { return MA_ERROR; } +#endif +#ifdef EMULTIHOP + else if (e == EMULTIHOP) { return MA_ERROR; } +#endif +#ifdef EDOTDOT + else if (e == EDOTDOT) { return MA_ERROR; } +#endif +#ifdef EBADMSG + else if (e == EBADMSG) { return MA_BAD_MESSAGE; } +#endif +#ifdef EOVERFLOW + else if (e == EOVERFLOW) { return MA_TOO_BIG; } +#endif +#ifdef ENOTUNIQ + else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } +#endif +#ifdef EBADFD + else if (e == EBADFD) { return MA_ERROR; } +#endif +#ifdef EREMCHG + else if (e == EREMCHG) { return MA_ERROR; } +#endif +#ifdef ELIBACC + else if (e == ELIBACC) { return MA_ACCESS_DENIED; } +#endif +#ifdef ELIBBAD + else if (e == ELIBBAD) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBSCN + else if (e == ELIBSCN) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBMAX + else if (e == ELIBMAX) { return MA_ERROR; } +#endif +#ifdef ELIBEXEC + else if (e == ELIBEXEC) { return MA_ERROR; } +#endif +#ifdef EILSEQ + else if (e == EILSEQ) { return MA_INVALID_DATA; } +#endif +#ifdef ERESTART + else if (e == ERESTART) { return MA_ERROR; } +#endif +#ifdef ESTRPIPE + else if (e == ESTRPIPE) { return MA_ERROR; } +#endif +#ifdef EUSERS + else if (e == EUSERS) { return MA_ERROR; } +#endif +#ifdef ENOTSOCK + else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } +#endif +#ifdef EDESTADDRREQ + else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } +#endif +#ifdef EMSGSIZE + else if (e == EMSGSIZE) { return MA_TOO_BIG; } +#endif +#ifdef EPROTOTYPE + else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } +#endif +#ifdef ENOPROTOOPT + else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } +#endif +#ifdef EPROTONOSUPPORT + else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } +#endif +#ifdef ESOCKTNOSUPPORT + else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } +#endif +#ifdef EOPNOTSUPP + else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } +#endif +#ifdef EPFNOSUPPORT + else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EAFNOSUPPORT + else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EADDRINUSE + else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } +#endif +#ifdef EADDRNOTAVAIL + else if (e == EADDRNOTAVAIL) { return MA_ERROR; } +#endif +#ifdef ENETDOWN + else if (e == ENETDOWN) { return MA_NO_NETWORK; } +#endif +#ifdef ENETUNREACH + else if (e == ENETUNREACH) { return MA_NO_NETWORK; } +#endif +#ifdef ENETRESET + else if (e == ENETRESET) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNABORTED + else if (e == ECONNABORTED) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNRESET + else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } +#endif +#ifdef ENOBUFS + else if (e == ENOBUFS) { return MA_NO_SPACE; } +#endif +#ifdef EISCONN + else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } +#endif +#ifdef ENOTCONN + else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } +#endif +#ifdef ESHUTDOWN + else if (e == ESHUTDOWN) { return MA_ERROR; } +#endif +#ifdef ETOOMANYREFS + else if (e == ETOOMANYREFS) { return MA_ERROR; } +#endif +#ifdef ETIMEDOUT + else if (e == ETIMEDOUT) { return MA_TIMEOUT; } +#endif +#ifdef ECONNREFUSED + else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } +#endif +#ifdef EHOSTDOWN + else if (e == EHOSTDOWN) { return MA_NO_HOST; } +#endif +#ifdef EHOSTUNREACH + else if (e == EHOSTUNREACH) { return MA_NO_HOST; } +#endif +#ifdef EALREADY + else if (e == EALREADY) { return MA_IN_PROGRESS; } +#endif +#ifdef EINPROGRESS + else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } +#endif +#ifdef ESTALE + else if (e == ESTALE) { return MA_INVALID_FILE; } +#endif +#ifdef EUCLEAN + else if (e == EUCLEAN) { return MA_ERROR; } +#endif +#ifdef ENOTNAM + else if (e == ENOTNAM) { return MA_ERROR; } +#endif +#ifdef ENAVAIL + else if (e == ENAVAIL) { return MA_ERROR; } +#endif +#ifdef EISNAM + else if (e == EISNAM) { return MA_ERROR; } +#endif +#ifdef EREMOTEIO + else if (e == EREMOTEIO) { return MA_IO_ERROR; } +#endif +#ifdef EDQUOT + else if (e == EDQUOT) { return MA_NO_SPACE; } +#endif +#ifdef ENOMEDIUM + else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EMEDIUMTYPE + else if (e == EMEDIUMTYPE) { return MA_ERROR; } +#endif +#ifdef ECANCELED + else if (e == ECANCELED) { return MA_CANCELLED; } +#endif +#ifdef ENOKEY + else if (e == ENOKEY) { return MA_ERROR; } +#endif +#ifdef EKEYEXPIRED + else if (e == EKEYEXPIRED) { return MA_ERROR; } +#endif +#ifdef EKEYREVOKED + else if (e == EKEYREVOKED) { return MA_ERROR; } +#endif +#ifdef EKEYREJECTED + else if (e == EKEYREJECTED) { return MA_ERROR; } +#endif +#ifdef EOWNERDEAD + else if (e == EOWNERDEAD) { return MA_ERROR; } +#endif +#ifdef ENOTRECOVERABLE + else if (e == ENOTRECOVERABLE) { return MA_ERROR; } +#endif +#ifdef ERFKILL + else if (e == ERFKILL) { return MA_ERROR; } +#endif +#ifdef EHWPOISON + else if (e == EHWPOISON) { return MA_ERROR; } +#endif + else { + return MA_ERROR; + } +} + +MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + ma_result result = ma_result_from_errno(errno); + if (result == MA_SUCCESS) { + result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return MA_SUCCESS; +} + + + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define MA_HAS_WFOPEN + #endif +#endif + +MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return ma_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + MA_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return ma_result_from_errno(errno); + } + + pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return MA_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + MA_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + ma_free(pFilePathMB, pAllocationCallbacks); + } + + if (*ppFile == NULL) { + return MA_ERROR; + } +#endif + + return MA_SUCCESS; +} + + + +static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX + MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + ma_uint64 bytesToCopyNow = sizeInBytes; + if (bytesToCopyNow > MA_SIZE_MAX) { + bytesToCopyNow = MA_SIZE_MAX; + } + + MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ + + sizeInBytes -= bytesToCopyNow; + dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); + src = (const void*)((const ma_uint8*)src + bytesToCopyNow); + } +#endif +} + +static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX + MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + ma_uint64 bytesToZeroNow = sizeInBytes; + if (bytesToZeroNow > MA_SIZE_MAX) { + bytesToZeroNow = MA_SIZE_MAX; + } + + MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ + + sizeInBytes -= bytesToZeroNow; + dst = (void*)((ma_uint8*)dst + bytesToZeroNow); + } +#endif +} + + +/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ +static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + + return x; +} + +static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) +{ + return ma_next_power_of_2(x) >> 1; +} + +static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) +{ + unsigned int prev = ma_prev_power_of_2(x); + unsigned int next = ma_next_power_of_2(x); + if ((next - x) > (x - prev)) { + return prev; + } else { + return next; + } +} + +static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) +{ + unsigned int count = 0; + while (x != 0) { + if (x & 1) { + count += 1; + } + + x = x >> 1; + } + + return count; +} + + + +/************************************************************************************************************************************************************** + +Allocation Callbacks + +**************************************************************************************************************************************************************/ +static void* ma__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_MALLOC(sz); +} + +static void* ma__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_REALLOC(p, sz); +} + +static void ma__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_FREE(p); +} + +static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) +{ + ma_allocation_callbacks callbacks; + callbacks.pUserData = NULL; + callbacks.onMalloc = ma__malloc_default; + callbacks.onRealloc = ma__realloc_default; + callbacks.onFree = ma__free_default; + + return callbacks; +} + +static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) +{ + if (pDst == NULL) { + return MA_INVALID_ARGS; + } + + if (pSrc == NULL) { + *pDst = ma_allocation_callbacks_init_default(); + } else { + if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { + *pDst = ma_allocation_callbacks_init_default(); + } else { + if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { + return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ + } else { + *pDst = *pSrc; + } + } + } + + return MA_SUCCESS; +} + + + + +/************************************************************************************************************************************************************** + +Logging + +**************************************************************************************************************************************************************/ +MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) +{ + switch (logLevel) + { + case MA_LOG_LEVEL_DEBUG: return "DEBUG"; + case MA_LOG_LEVEL_INFO: return "INFO"; + case MA_LOG_LEVEL_WARNING: return "WARNING"; + case MA_LOG_LEVEL_ERROR: return "ERROR"; + default: return "ERROR"; + } +} + +#if defined(MA_DEBUG_OUTPUT) +#if defined(MA_ANDROID) + #include +#endif + +/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ +#ifndef MA_ANDROID_LOG_TAG +#define MA_ANDROID_LOG_TAG "miniaudio" +#endif + +void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) +{ + (void)pUserData; + + /* Special handling for some platforms. */ + #if defined(MA_ANDROID) + { + /* Android. */ + __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); + } + #else + { + /* Everything else. */ + printf("%s: %s", ma_log_level_to_string(level), pMessage); + } + #endif +} +#endif + +MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) +{ + ma_log_callback callback; + + MA_ZERO_OBJECT(&callback); + callback.onLog = onLog; + callback.pUserData = pUserData; + + return callback; +} + + +MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) +{ + if (pLog == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLog); + ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); + + /* We need a mutex for thread safety. */ + #ifndef MA_NO_THREADING + { + ma_result result = ma_mutex_init(&pLog->lock); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + + /* If we're using debug output, enable it. */ + #if defined(MA_DEBUG_OUTPUT) + { + ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ + } + #endif + + return MA_SUCCESS; +} + +MA_API void ma_log_uninit(ma_log* pLog) +{ + if (pLog == NULL) { + return; + } + +#ifndef MA_NO_THREADING + ma_mutex_uninit(&pLog->lock); +#endif +} + +static void ma_log_lock(ma_log* pLog) +{ +#ifndef MA_NO_THREADING + ma_mutex_lock(&pLog->lock); +#else + (void)pLog; +#endif +} + +static void ma_log_unlock(ma_log* pLog) +{ +#ifndef MA_NO_THREADING + ma_mutex_unlock(&pLog->lock); +#else + (void)pLog; +#endif +} + +MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) +{ + ma_result result = MA_SUCCESS; + + if (pLog == NULL || callback.onLog == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + if (pLog->callbackCount == ma_countof(pLog->callbacks)) { + result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ + } else { + pLog->callbacks[pLog->callbackCount] = callback; + pLog->callbackCount += 1; + } + } + ma_log_unlock(pLog); + + return result; +} + +MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) +{ + if (pLog == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + ma_uint32 iLog; + for (iLog = 0; iLog < pLog->callbackCount; ) { + if (pLog->callbacks[iLog].onLog == callback.onLog) { + /* Found. Move everything down a slot. */ + ma_uint32 jLog; + for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { + pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; + } + + pLog->callbackCount -= 1; + } else { + /* Not found. */ + iLog += 1; + } + } + } + ma_log_unlock(pLog); + + return MA_SUCCESS; +} + +MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) +{ + if (pLog == NULL || pMessage == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + ma_uint32 iLog; + for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { + if (pLog->callbacks[iLog].onLog) { + pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); + } + } + } + ma_log_unlock(pLog); + + return MA_SUCCESS; +} + + +/* +We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a +logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). +*/ +#if defined(_MSC_VER) && _MSC_VER < 1900 +static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) +{ +#if _MSC_VER > 1200 + return _vscprintf(format, args); +#else + int result; + char* pTempBuffer = NULL; + size_t tempBufferCap = 1024; + + if (format == NULL) { + errno = EINVAL; + return -1; + } + + for (;;) { + char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); + if (pNewTempBuffer == NULL) { + ma_free(pTempBuffer, pAllocationCallbacks); + errno = ENOMEM; + return -1; /* Out of memory. */ + } + + pTempBuffer = pNewTempBuffer; + + result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); + ma_free(pTempBuffer, NULL); + + if (result != -1) { + break; /* Got it. */ + } + + /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ + tempBufferCap *= 2; + } + + return result; +#endif +} +#endif + +MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) +{ + if (pLog == NULL || pFormat == NULL) { + return MA_INVALID_ARGS; + } + + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) + { + ma_result result; + int length; + char pFormattedMessageStack[1024]; + char* pFormattedMessageHeap = NULL; + + /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ + length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); + if (length < 0) { + return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ + } + + if ((size_t)length < sizeof(pFormattedMessageStack)) { + /* The string was written to the stack. */ + result = ma_log_post(pLog, level, pFormattedMessageStack); + } else { + /* The stack buffer was too small, try the heap. */ + pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); + if (pFormattedMessageHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + + length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); + if (length < 0) { + ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); + return MA_INVALID_OPERATION; + } + + result = ma_log_post(pLog, level, pFormattedMessageHeap); + ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); + } + + return result; + } + #else + { + /* + Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll + need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing + a fixed sized stack allocated buffer. + */ + #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ + { + ma_result result; + int formattedLen; + char* pFormattedMessage = NULL; + va_list args2; + + #if _MSC_VER >= 1800 + { + va_copy(args2, args); + } + #else + { + args2 = args; + } + #endif + + formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); + va_end(args2); + + if (formattedLen <= 0) { + return MA_INVALID_OPERATION; + } + + pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); + if (pFormattedMessage == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ + #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ + { + vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); + } + #else + { + vsprintf(pFormattedMessage, pFormat, args); + } + #endif + + result = ma_log_post(pLog, level, pFormattedMessage); + ma_free(pFormattedMessage, &pLog->allocationCallbacks); + + return result; + } + #else + { + /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ + (void)level; + (void)args; + + return MA_INVALID_OPERATION; + } + #endif + } + #endif +} + +MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) +{ + ma_result result; + va_list args; + + if (pLog == NULL || pFormat == NULL) { + return MA_INVALID_ARGS; + } + + va_start(args, pFormat); + { + result = ma_log_postv(pLog, level, pFormat, args); + } + va_end(args); + + return result; +} + + + +static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) +{ + return (ma_uint8)(ma_clamp(x, -128, 127) + 128); +} + +static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) +{ + return (ma_int16)ma_clamp(x, -32768, 32767); +} + +static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) +{ + return (ma_int64)ma_clamp(x, -8388608, 8388607); +} + +static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) +{ + /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ + ma_int64 clipMin; + ma_int64 clipMax; + clipMin = -((ma_int64)2147483647 + 1); + clipMax = (ma_int64)2147483647; + + return (ma_int32)ma_clamp(x, clipMin, clipMax); +} + +static MA_INLINE float ma_clip_f32(float x) +{ + if (x < -1) return -1; + if (x > +1) return +1; + return x; +} + + +static MA_INLINE float ma_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; + /*return x + (y - x)*a;*/ +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) +{ + return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); +} +#endif +#if defined(MA_SUPPORT_AVX2) +static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) +{ + return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) +{ + return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); +} +#endif + + +static MA_INLINE double ma_mix_f64(double x, double y, double a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) +{ + return x + (y - x)*a; +} + +static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) +{ + return lo + x*(hi-lo); +} + + +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + ma_uint32 t = a; + a = b; + b = t % a; + } + } + + return a; +} + + +static ma_uint32 ma_ffs_32(ma_uint32 x) +{ + ma_uint32 i; + + /* Just a naive implementation just to get things working for now. Will optimize this later. */ + for (i = 0; i < 32; i += 1) { + if ((x & (1 << i)) != 0) { + return i; + } + } + + return i; +} + +static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) +{ + return (ma_int16)(x * (1 << 8)); +} + + + +/* +Random Number Generation + +miniaudio uses the LCG random number generation algorithm. This is good enough for audio. + +Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across +multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for +miniaudio's purposes. +*/ +#ifndef MA_DEFAULT_LCG_SEED +#define MA_DEFAULT_LCG_SEED 4321 +#endif + +#define MA_LCG_M 2147483647 +#define MA_LCG_A 48271 +#define MA_LCG_C 0 + +static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ + +static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) +{ + MA_ASSERT(pLCG != NULL); + pLCG->state = seed; +} + +static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) +{ + pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; + return pLCG->state; +} + +static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) +{ + return (ma_uint32)ma_lcg_rand_s32(pLCG); +} + +static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) +{ + return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); +} + +static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) +{ + return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; +} + +static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) +{ + return (float)ma_lcg_rand_f64(pLCG); +} + +static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) +{ + return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); +} + +static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) +{ + if (lo == hi) { + return lo; + } + + return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); +} + + + +static MA_INLINE void ma_seed(ma_int32 seed) +{ + ma_lcg_seed(&g_maLCG, seed); +} + +static MA_INLINE ma_int32 ma_rand_s32(void) +{ + return ma_lcg_rand_s32(&g_maLCG); +} + +static MA_INLINE ma_uint32 ma_rand_u32(void) +{ + return ma_lcg_rand_u32(&g_maLCG); +} + +static MA_INLINE double ma_rand_f64(void) +{ + return ma_lcg_rand_f64(&g_maLCG); +} + +static MA_INLINE float ma_rand_f32(void) +{ + return ma_lcg_rand_f32(&g_maLCG); +} + +static MA_INLINE float ma_rand_range_f32(float lo, float hi) +{ + return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); +} + +static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) +{ + return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); +} + + +static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) +{ + return ma_rand_range_f32(ditherMin, ditherMax); +} + +static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) +{ + float a = ma_rand_range_f32(ditherMin, 0); + float b = ma_rand_range_f32(0, ditherMax); + return a + b; +} + +static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) +{ + if (ditherMode == ma_dither_mode_rectangle) { + return ma_dither_f32_rectangle(ditherMin, ditherMax); + } + if (ditherMode == ma_dither_mode_triangle) { + return ma_dither_f32_triangle(ditherMin, ditherMax); + } + + return 0; +} + +static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) +{ + if (ditherMode == ma_dither_mode_rectangle) { + ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); + return a; + } + if (ditherMode == ma_dither_mode_triangle) { + ma_int32 a = ma_rand_range_s32(ditherMin, 0); + ma_int32 b = ma_rand_range_s32(0, ditherMax); + return a + b; + } + + return 0; +} + + +/************************************************************************************************************************************************************** + +Atomics + +**************************************************************************************************************************************************************/ +/* ma_atomic.h begin */ +#ifndef ma_atomic_h +#if defined(__cplusplus) +extern "C" { +#endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif +#endif +typedef int ma_atomic_memory_order; +#define MA_ATOMIC_HAS_8 +#define MA_ATOMIC_HAS_16 +#define MA_ATOMIC_HAS_32 +#define MA_ATOMIC_HAS_64 +#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) + #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ + switch (order) \ + { \ + case ma_atomic_memory_order_relaxed: \ + { \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ + { \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_release: \ + { \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + } \ + return result; + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ + switch (order) \ + { \ + case ma_atomic_memory_order_relaxed: \ + { \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ + { \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_release: \ + { \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + } \ + return result; + #define ma_atomic_memory_order_relaxed 0 + #define ma_atomic_memory_order_consume 1 + #define ma_atomic_memory_order_acquire 2 + #define ma_atomic_memory_order_release 3 + #define ma_atomic_memory_order_acq_rel 4 + #define ma_atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(MA_X86) + #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY + #endif + #if _MSC_VER < 1600 + #undef MA_ATOMIC_HAS_8 + #undef MA_ATOMIC_HAS_16 + #endif + #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #include + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result = 0; + __asm { + mov ecx, dst + mov al, expected + mov dl, desired + lock cmpxchg [ecx], dl + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result = 0; + __asm { + mov ecx, dst + mov ax, expected + mov dx, desired + lock cmpxchg [ecx], dx + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result = 0; + __asm { + mov ecx, dst + mov eax, expected + mov edx, desired + lock cmpxchg [ecx], edx + mov result, eax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; + __asm { + mov esi, dst + mov eax, dword ptr expected + mov edx, dword ptr expected + 4 + mov ebx, dword ptr desired + mov ecx, dword ptr desired + 4 + lock cmpxchg8b qword ptr [esi] + mov resultEAX, eax + mov resultEDX, edx + } + return ((ma_uint64)resultEDX << 32) | resultEAX; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) + #endif + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + __asm { + mov ecx, dst + mov al, src + lock xchg [ecx], al + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + __asm { + mov ecx, dst + mov ax, src + lock xchg [ecx], ax + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, src + lock xchg [ecx], eax + mov result, eax + } + return result; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); + #else + (void)order; + return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); + #else + (void)order; + return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); + #else + (void)order; + return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); + #else + (void)order; + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + #endif + } + #else + #endif + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + __asm { + mov ecx, dst + mov al, src + lock xadd [ecx], al + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + __asm { + mov ecx, dst + mov ax, src + lock xadd [ecx], ax + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, src + lock xadd [ecx], eax + mov result, eax + } + return result; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); + #else + (void)order; + return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); + #else + (void)order; + return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); + #else + (void)order; + return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); + #else + (void)order; + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + #endif + } + #else + #endif + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) + { + (void)order; + __asm { + lock add [esp], 0 + } + } + #else + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #else + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) + { + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + } + #endif + #endif + #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + #else + (void)order; + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + #else + (void)order; + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); + #else + (void)order; + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + #else + (void)order; + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #else + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) + #endif +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) + #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) + #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) + #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) + #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) + #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) +#else + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #if defined(__GNUC__) + #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #else + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #else + #error Unsupported architecture. Please submit a feature request. + #endif + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + volatile ma_uint64 result; + #if defined(MA_X86) + ma_uint32 resultEAX; + ma_uint32 resultEDX; + __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); + result = ((ma_uint64)resultEDX << 32) | resultEAX; + #elif defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 result; + (void)order; + #if defined(MA_X86) + do { + result = *dst; + } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_X86) + ma_uint64 oldValue; + ma_uint64 newValue; + (void)order; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + return oldValue; + #elif defined(MA_X64) + ma_uint64 result; + (void)order; + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + return result; + #endif + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); + } + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) +#endif +#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint8 expectedValue; + ma_uint8 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_8(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint16 expectedValue; + ma_uint16 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_16(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint32 expectedValue; + ma_uint32 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_32(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint64 expectedValue; + ma_uint64 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_64(expected, result, failureOrder); + return 0; + } + } + #endif + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) +#endif +#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) + { + (void)ptr; + #if defined(MA_64BIT) + return 1; + #else + #if defined(MA_X86) || defined(MA_X64) + return 1; + #else + return 0; + #endif + #endif + } +#endif +#if defined(MA_64BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) + { + return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); + } + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + { + return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); + } + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + } + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + } + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); + } +#elif defined(MA_32BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) + { + return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); + } + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + { + return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); + } + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + } + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + } + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); + } +#else + #error Unsupported architecture. +#endif +#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) +#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) +#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) +#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) +typedef union +{ + ma_uint32 i; + float f; +} ma_atomic_if32; +typedef union +{ + ma_uint64 i; + double f; +} ma_atomic_if64; +#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 x; + x.f = src; + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); +} +static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 x; + x.f = src; + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); +} +static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); + return r.f; +} +static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); + return r.f; +} +static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if32 d; + d.f = desired; + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if64 d; + d.f = desired; + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if32 d; + d.f = desired; + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if64 d; + d.f = desired; + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +{ + ma_atomic_if32 r; + ma_atomic_if32 e, d; + e.f = expected; + d.f = desired; + r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); + return r.f; +} +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +{ + ma_atomic_if64 r; + ma_atomic_if64 e, d; + e.f = expected; + d.f = desired; + r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); + return r.f; +} +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) +{ + for (;;) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { + break; + } + while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + } + } +} +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) +{ + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); +} +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#if defined(__cplusplus) +} +#endif +#endif +/* ma_atomic.h end */ + +#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ + static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ + { \ + return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ + } \ + static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ + { \ + ma_atomic_store_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ + { \ + return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ + { \ + return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ + { \ + return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + } \ + +#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ + { \ + return ma_atomic_load_ptr((void**)&x->value); \ + } \ + static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + ma_atomic_store_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ + { \ + return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ + { \ + return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + +MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) +MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) +MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) +MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif + + +MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) +{ + /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ + ma_uint64 outputFrameCount; + ma_uint64 preliminaryInputFrameCountFromFrac; + ma_uint64 preliminaryInputFrameCount; + + if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { + return 0; + } + + if (sampleRateOut == sampleRateIn) { + return frameCountIn; + } + + outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; + + preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; + preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; + + if (preliminaryInputFrameCount <= frameCountIn) { + outputFrameCount += 1; + } + + return outputFrameCount; +} + +#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE +#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 +#endif + + + +#if defined(MA_WIN32) +static ma_result ma_result_from_GetLastError(DWORD error) +{ + switch (error) + { + case ERROR_SUCCESS: return MA_SUCCESS; + case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; + case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; + case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; + case ERROR_DISK_FULL: return MA_NO_SPACE; + case ERROR_HANDLE_EOF: return MA_AT_END; + case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; + case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; + case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; + case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; + case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; + default: break; + } + + return MA_ERROR; +} +#endif /* MA_WIN32 */ + + +/******************************************************************************* + +Threading + +*******************************************************************************/ +static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) +{ + if (pSpinlock == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { + break; + } + + while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + if (yield) { + ma_yield(); + } + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) +{ + return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); +} + +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) +{ + return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); +} + +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) +{ + if (pSpinlock == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); + return MA_SUCCESS; +} + + +#ifndef MA_NO_THREADING +#if defined(MA_POSIX) + #define MA_THREADCALL + typedef void* ma_thread_result; +#elif defined(MA_WIN32) + #define MA_THREADCALL WINAPI + typedef unsigned long ma_thread_result; +#endif + +typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); + +#ifdef MA_POSIX +static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + int result; + pthread_attr_t* pAttr = NULL; + +#if !defined(__EMSCRIPTEN__) + /* Try setting the thread priority. It's not critical if anything fails here. */ + pthread_attr_t attr; + if (pthread_attr_init(&attr) == 0) { + int scheduler = -1; + + /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ + pAttr = &attr; + + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif + } + } + #endif + + if (stackSize > 0) { + pthread_attr_setstacksize(&attr, stackSize); + } + + if (scheduler != -1) { + int priorityMin = sched_get_priority_min(scheduler); + int priorityMax = sched_get_priority_max(scheduler); + int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ + + struct sched_param sched; + if (pthread_attr_getschedparam(&attr, &sched) == 0) { + if (priority == ma_thread_priority_idle) { + sched.sched_priority = priorityMin; + } else if (priority == ma_thread_priority_realtime) { + sched.sched_priority = priorityMax; + } else { + sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ + if (sched.sched_priority < priorityMin) { + sched.sched_priority = priorityMin; + } + if (sched.sched_priority > priorityMax) { + sched.sched_priority = priorityMax; + } + } + + /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ + pthread_attr_setschedparam(&attr, &sched); + } + } + } +#else + /* It's the emscripten build. We'll have a few unused parameters. */ + (void)priority; + (void)stackSize; +#endif + + result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); + + /* The thread attributes object is no longer required. */ + if (pAttr != NULL) { + pthread_attr_destroy(pAttr); + } + + if (result != 0) { + return ma_result_from_errno(result); + } + + return MA_SUCCESS; +} + +static void ma_thread_wait__posix(ma_thread* pThread) +{ + pthread_join((pthread_t)*pThread, NULL); +} + + +static ma_result ma_mutex_init__posix(ma_mutex* pMutex) +{ + int result; + + if (pMutex == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMutex); + + result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + if (result != 0) { + return ma_result_from_errno(result); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__posix(ma_mutex* pMutex) +{ + pthread_mutex_destroy((pthread_mutex_t*)pMutex); +} + +static void ma_mutex_lock__posix(ma_mutex* pMutex) +{ + pthread_mutex_lock((pthread_mutex_t*)pMutex); +} + +static void ma_mutex_unlock__posix(ma_mutex* pMutex) +{ + pthread_mutex_unlock((pthread_mutex_t*)pMutex); +} + + +static ma_result ma_event_init__posix(ma_event* pEvent) +{ + int result; + + result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); + if (result != 0) { + return ma_result_from_errno(result); + } + + result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); + if (result != 0) { + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); + return ma_result_from_errno(result); + } + + pEvent->value = 0; + return MA_SUCCESS; +} + +static void ma_event_uninit__posix(ma_event* pEvent) +{ + pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); +} + +static ma_result ma_event_wait__posix(ma_event* pEvent) +{ + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); + { + while (pEvent->value == 0) { + pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); + } + pEvent->value = 0; /* Auto-reset. */ + } + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); + + return MA_SUCCESS; +} + +static ma_result ma_event_signal__posix(ma_event* pEvent) +{ + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); + { + pEvent->value = 1; + pthread_cond_signal((pthread_cond_t*)&pEvent->cond); + } + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) +{ + int result; + + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pSemaphore->value = initialValue; + + result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); + if (result != 0) { + return ma_result_from_errno(result); /* Failed to create mutex. */ + } + + result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); + if (result != 0) { + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); + return ma_result_from_errno(result); /* Failed to create condition variable. */ + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return; + } + + pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); +} + +static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); + { + /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ + while (pSemaphore->value == 0) { + pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); + } + + pSemaphore->value -= 1; + } + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); + + return MA_SUCCESS; +} + +static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); + { + pSemaphore->value += 1; + pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); + } + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); + + return MA_SUCCESS; +} +#elif defined(MA_WIN32) +static int ma_thread_priority_to_win32(ma_thread_priority priority) +{ + switch (priority) { + case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; + case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; + case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; + case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; + case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; + case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; + case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; + default: return THREAD_PRIORITY_NORMAL; + } +} + +static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ + + *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); + if (*pThread == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); + + return MA_SUCCESS; +} + +static void ma_thread_wait__win32(ma_thread* pThread) +{ + WaitForSingleObject((HANDLE)*pThread, INFINITE); + CloseHandle((HANDLE)*pThread); +} + + +static ma_result ma_mutex_init__win32(ma_mutex* pMutex) +{ + *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (*pMutex == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__win32(ma_mutex* pMutex) +{ + CloseHandle((HANDLE)*pMutex); +} + +static void ma_mutex_lock__win32(ma_mutex* pMutex) +{ + WaitForSingleObject((HANDLE)*pMutex, INFINITE); +} + +static void ma_mutex_unlock__win32(ma_mutex* pMutex) +{ + SetEvent((HANDLE)*pMutex); +} + + +static ma_result ma_event_init__win32(ma_event* pEvent) +{ + *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (*pEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_event_uninit__win32(ma_event* pEvent) +{ + CloseHandle((HANDLE)*pEvent); +} + +static ma_result ma_event_wait__win32(ma_event* pEvent) +{ + DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_event_signal__win32(ma_event* pEvent) +{ + BOOL result = SetEvent((HANDLE)*pEvent); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) +{ + *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); + if (*pSemaphore == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) +{ + CloseHandle((HANDLE)*pSemaphore); +} + +static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) +{ + DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) +{ + BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} +#endif + +typedef struct +{ + ma_thread_entry_proc entryProc; + void* pData; + ma_allocation_callbacks allocationCallbacks; +} ma_thread_proxy_data; + +static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) +{ + ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; + ma_thread_entry_proc entryProc; + void* pEntryProcData; + ma_thread_result result; + + #if defined(MA_ON_THREAD_ENTRY) + MA_ON_THREAD_ENTRY + #endif + + entryProc = pProxyData->entryProc; + pEntryProcData = pProxyData->pData; + + /* Free the proxy data before getting into the real thread entry proc. */ + ma_free(pProxyData, &pProxyData->allocationCallbacks); + + result = entryProc(pEntryProcData); + + #if defined(MA_ON_THREAD_EXIT) + MA_ON_THREAD_EXIT + #endif + + return result; +} + +static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_thread_proxy_data* pProxyData; + + if (pThread == NULL || entryProc == NULL) { + return MA_INVALID_ARGS; + } + + pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ + if (pProxyData == NULL) { + return MA_OUT_OF_MEMORY; + } + +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + + pProxyData->entryProc = entryProc; + pProxyData->pData = pData; + ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); + +#if defined(MA_POSIX) + result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#elif defined(MA_WIN32) + result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#endif + + if (result != MA_SUCCESS) { + ma_free(pProxyData, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; +} + +static void ma_thread_wait(ma_thread* pThread) +{ + if (pThread == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_thread_wait__posix(pThread); +#elif defined(MA_WIN32) + ma_thread_wait__win32(pThread); +#endif +} + + +MA_API ma_result ma_mutex_init(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_mutex_init__posix(pMutex); +#elif defined(MA_WIN32) + return ma_mutex_init__win32(pMutex); +#endif +} + +MA_API void ma_mutex_uninit(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_mutex_uninit__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_uninit__win32(pMutex); +#endif +} + +MA_API void ma_mutex_lock(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_mutex_lock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_lock__win32(pMutex); +#endif +} + +MA_API void ma_mutex_unlock(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_mutex_unlock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_unlock__win32(pMutex); +#endif +} + + +MA_API ma_result ma_event_init(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_init__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_init__win32(pEvent); +#endif +} + +#if 0 +static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_event* pEvent; + + if (ppEvent == NULL) { + return MA_INVALID_ARGS; + } + + *ppEvent = NULL; + + pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); + if (pEvent == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_event_init(pEvent); + if (result != MA_SUCCESS) { + ma_free(pEvent, pAllocationCallbacks); + return result; + } + + *ppEvent = pEvent; + return result; +} +#endif + +MA_API void ma_event_uninit(ma_event* pEvent) +{ + if (pEvent == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_event_uninit__posix(pEvent); +#elif defined(MA_WIN32) + ma_event_uninit__win32(pEvent); +#endif +} + +#if 0 +static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pEvent == NULL) { + return; + } + + ma_event_uninit(pEvent); + ma_free(pEvent, pAllocationCallbacks); +} +#endif + +MA_API ma_result ma_event_wait(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_wait__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_wait__win32(pEvent); +#endif +} + +MA_API ma_result ma_event_signal(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_signal__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_signal__win32(pEvent); +#endif +} + + +MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_init__posix(initialValue, pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_init__win32(initialValue, pSemaphore); +#endif +} + +MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_semaphore_uninit__posix(pSemaphore); +#elif defined(MA_WIN32) + ma_semaphore_uninit__win32(pSemaphore); +#endif +} + +MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_wait__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_wait__win32(pSemaphore); +#endif +} + +MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_release__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_release__win32(pSemaphore); +#endif +} +#else +/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ +#ifndef MA_NO_DEVICE_IO +#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; +#endif +#endif /* MA_NO_THREADING */ + + + +#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF + +MA_API ma_result ma_fence_init(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFence); + pFence->counter = 0; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + + return MA_SUCCESS; +} + +MA_API void ma_fence_uninit(ma_fence* pFence) +{ + if (pFence == NULL) { + return; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pFence->e); + } + #endif + + MA_ZERO_OBJECT(pFence); +} + +MA_API ma_result ma_fence_acquire(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter + 1; + + /* Make sure we're not about to exceed our maximum value. */ + if (newCounter > MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; + } + + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + return MA_SUCCESS; + } else { + if (oldCounter == MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_release(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter - 1; + + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ + } + + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + #ifndef MA_NO_THREADING + { + if (newCounter == 0) { + ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ + } + } + #endif + + return MA_SUCCESS; + } else { + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_wait(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 counter; + + counter = ma_atomic_load_32(&pFence->counter); + if (counter == 0) { + /* + Counter has hit zero. By the time we get here some other thread may have acquired the + fence again, but that is where the caller needs to take care with how they se the fence. + */ + return MA_SUCCESS; + } + + /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_wait(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + } + + /* Should never get here. */ + /*return MA_INVALID_OPERATION;*/ +} + + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) +{ + ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; + + if (pNotification == NULL) { + return MA_INVALID_ARGS; + } + + if (pNotificationCallbacks->onSignal == NULL) { + return MA_NOT_IMPLEMENTED; + } + + pNotificationCallbacks->onSignal(pNotification); + return MA_INVALID_ARGS; +} + + +static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) +{ + ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; +} + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; + pNotificationPoll->signalled = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_FALSE; + } + + return pNotificationPoll->signalled; +} + + +static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) +{ + ma_async_notification_event_signal((ma_async_notification_event*)pNotification); +} + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pNotificationEvent->e); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pNotificationEvent->e); + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_wait(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_signal(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) +{ + ma_slot_allocator_config config; + + MA_ZERO_OBJECT(&config); + config.capacity = capacity; + + return config; +} + + +static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) +{ + ma_uint32 cap = slotCapacity / 32; + if ((slotCapacity % 32) != 0) { + cap += 1; + } + + return cap; +} + +static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) +{ + return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); +} + + +typedef struct +{ + size_t sizeInBytes; + size_t groupsOffset; + size_t slotsOffset; +} ma_slot_allocator_heap_layout; + +static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Groups. */ + pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); + + /* Slots. */ + pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_slot_allocator_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_slot_allocator_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) +{ + ma_result result; + ma_slot_allocator_heap_layout heapLayout; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pAllocator); + + if (pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pAllocator->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); + pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); + pAllocator->capacity = pConfig->capacity; + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pAllocator->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocator == NULL) { + return; + } + + if (pAllocator->_ownsHeap) { + ma_free(pAllocator->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) +{ + ma_uint32 iAttempt; + const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ + + if (pAllocator == NULL || pSlot == NULL) { + return MA_INVALID_ARGS; + } + + for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { + /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ + ma_uint32 iGroup; + for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { + /* CAS */ + for (;;) { + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + ma_uint32 bitOffset; + + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + + /* Fast check to see if anything is available. */ + if (oldBitfield == 0xFFFFFFFF) { + break; /* No available bits in this bitfield. */ + } + + bitOffset = ma_ffs_32(~oldBitfield); + MA_ASSERT(bitOffset < 32); + + newBitfield = oldBitfield | (1 << bitOffset); + + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_uint32 slotIndex; + + /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ + ma_atomic_fetch_add_32(&pAllocator->count, 1); + + /* The slot index is required for constructing the output value. */ + slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ + if (slotIndex >= pAllocator->capacity) { + return MA_OUT_OF_MEMORY; + } + + /* Increment the reference count before constructing the output value. */ + pAllocator->pSlots[slotIndex] += 1; + + /* Construct the output value. */ + *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); + + return MA_SUCCESS; + } + } + } + + /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ + if (pAllocator->count < pAllocator->capacity) { + ma_yield(); + } else { + return MA_OUT_OF_MEMORY; + } + } + + /* We couldn't find a slot within the maximum number of attempts. */ + return MA_OUT_OF_MEMORY; +} + +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) +{ + ma_uint32 iGroup; + ma_uint32 iBit; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ + iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ + + if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ + + while (ma_atomic_load_32(&pAllocator->count) > 0) { + /* CAS */ + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + newBitfield = oldBitfield & ~(1 << iBit); + + /* Debugging for checking for double-frees. */ + #if defined(MA_DEBUG_OUTPUT) + { + if ((oldBitfield & (1 << iBit)) == 0) { + MA_ASSERT(MA_FALSE); /* Double free detected.*/ + } + } + #endif + + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_atomic_fetch_sub_32(&pAllocator->count, 1); + return MA_SUCCESS; + } + } + + /* Getting here means there are no allocations available for freeing. */ + return MA_INVALID_OPERATION; +} + + +#define MA_JOB_ID_NONE ~((ma_uint64)0) +#define MA_JOB_SLOT_NONE (ma_uint16)(~0) + +static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) +{ + return (ma_uint32)(toc >> 32); +} + +static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) +{ + return (ma_uint16)(toc & 0x0000FFFF); +} + +static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) +{ + return (ma_uint16)((toc & 0xFFFF0000) >> 16); +} + +static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) +{ + return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); +} + +static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) +{ + /* Clear the reference count first. */ + toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); + toc = toc | ((ma_uint64)refcount << 32); + + return toc; +} + + +MA_API ma_job ma_job_init(ma_uint16 code) +{ + ma_job job; + + MA_ZERO_OBJECT(&job); + job.toc.breakup.code = code; + job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ + job.next = MA_JOB_ID_NONE; + + return job; +} + + +static ma_result ma_job_process__noop(ma_job* pJob); +static ma_result ma_job_process__quit(ma_job* pJob); +static ma_result ma_job_process__custom(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); + +#if !defined(MA_NO_DEVICE_IO) +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); +#endif + +static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = +{ + /* Miscellaneous. */ + ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ + ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ + + /* Resource Manager. */ + ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ + ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ + ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ + ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ + ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ + ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ + + /* Device. */ +#if !defined(MA_NO_DEVICE_IO) + ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ +#endif +}; + +MA_API ma_result ma_job_process(ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { + return MA_INVALID_OPERATION; + } + + return g_jobVTable[pJob->toc.breakup.code](pJob); +} + +static ma_result ma_job_process__noop(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op. */ + (void)pJob; + + return MA_SUCCESS; +} + +static ma_result ma_job_process__quit(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} + +static ma_result ma_job_process__custom(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op if there's no callback. */ + if (pJob->data.custom.proc == NULL) { + return MA_SUCCESS; + } + + return pJob->data.custom.proc(pJob); +} + + + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) +{ + ma_job_queue_config config; + + config.flags = flags; + config.capacity = capacity; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t allocatorOffset; + size_t jobsOffset; +} ma_job_queue_heap_layout; + +static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Allocator. */ + { + ma_slot_allocator_config allocatorConfig; + size_t allocatorHeapSizeInBytes; + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; + } + + /* Jobs. */ + pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_job_queue_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_job_queue_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) +{ + ma_result result; + ma_job_queue_heap_layout heapLayout; + ma_slot_allocator_config allocatorConfig; + + if (pQueue == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pQueue); + + result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pQueue->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pQueue->flags = pConfig->flags; + pQueue->capacity = pConfig->capacity; + pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); + if (result != MA_SUCCESS) { + return result; + } + + /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_init(0, &pQueue->sem); + } + #else + { + /* Threading is disabled and we've requested non-blocking mode. */ + return MA_INVALID_OPERATION; + } + #endif + } + + /* + Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is + just a dummy item for giving us the first item in the list which is stored in the "next" member. + */ + ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ + pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; + pQueue->tail = pQueue->head; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pQueue->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pQueue == NULL) { + return; + } + + /* All we need to do is uninitialize the semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_uninit(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); + + if (pQueue->_ownsHeap) { + ma_free(pQueue->_pHeap, pAllocationCallbacks); + } +} + +static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) +{ + /* The new counter is taken from the expected value. */ + return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; +} + +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) +{ + /* + Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors + */ + ma_result result; + ma_uint64 slot; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* We need a new slot. */ + result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); + if (result != MA_SUCCESS) { + return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ + } + + /* At this point we should have a slot to place the job. */ + MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); + + /* We need to put the job into memory before we do anything. */ + pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; + pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ + pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ + pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ + for (;;) { + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { + if (ma_job_extract_slot(next) == 0xFFFF) { + if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { + break; + } + } else { + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } + } + } + ma_job_queue_cas(&pQueue->tail, tail, slot); + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + + /* Signal the semaphore as the last step if we're using synchronous mode. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_release(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) +{ + ma_uint64 head; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're running in synchronous mode we'll need to wait on a semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_wait(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* + BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below + is stored. One thread can fall through to the freeing of this item while another is still using "head" for the + retrieval of the "next" variable. + + The slot allocator might need to make use of some reference counting to ensure it's only truely freed when + there are no more references to the item. This must be fixed before removing these locks. + */ + + /* Now we need to remove the root item from the list. */ + for (;;) { + head = ma_atomic_load_64(&pQueue->head); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { + if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { + if (ma_job_extract_slot(next) == 0xFFFF) { + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + return MA_NO_DATA_AVAILABLE; + } + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } else { + *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; + if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { + break; + } + } + } + } + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + ma_slot_allocator_free(&pQueue->allocator, head); + + /* + If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We + could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as + possible. + */ + if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { + ma_job_queue_post(pQueue, pJob); + return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ + } + + return MA_SUCCESS; +} + + + +/******************************************************************************* + +Dynamic Linking + +*******************************************************************************/ +#ifdef MA_POSIX + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + +MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif + #else + handle = (ma_handle)dlopen(filename, RTLD_NOW); + #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; +#endif +} + +MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) +{ +#ifndef MA_NO_RUNTIME_LINKING + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif + + (void)pLog; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; +#endif +} + +MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic pop +#endif +#endif + + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; +#endif +} + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DEVICE I/O +========== + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ + +/* Disable run-time linking on certain backends and platforms. */ +#ifndef MA_NO_RUNTIME_LINKING + #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) + #define MA_NO_RUNTIME_LINKING + #endif +#endif + +#ifndef MA_NO_DEVICE_IO + +#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) + #include /* For mach_absolute_time() */ +#endif + +#ifdef MA_POSIX + #include + #include + + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + + + +MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) +{ + if (pDeviceInfo == NULL) { + return; + } + + if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; + } +} + + +typedef struct +{ + ma_backend backend; + const char* pName; +} ma_backend_info; + +static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ +{ + {ma_backend_wasapi, "WASAPI"}, + {ma_backend_dsound, "DirectSound"}, + {ma_backend_winmm, "WinMM"}, + {ma_backend_coreaudio, "Core Audio"}, + {ma_backend_sndio, "sndio"}, + {ma_backend_audio4, "audio(4)"}, + {ma_backend_oss, "OSS"}, + {ma_backend_pulseaudio, "PulseAudio"}, + {ma_backend_alsa, "ALSA"}, + {ma_backend_jack, "JACK"}, + {ma_backend_aaudio, "AAudio"}, + {ma_backend_opensl, "OpenSL|ES"}, + {ma_backend_webaudio, "Web Audio"}, + {ma_backend_custom, "Custom"}, + {ma_backend_null, "Null"} +}; + +MA_API const char* ma_get_backend_name(ma_backend backend) +{ + if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { + return "Unknown"; + } + + return gBackendInfo[backend].pName; +} + +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) +{ + size_t iBackend; + + if (pBackendName == NULL) { + return MA_INVALID_ARGS; + } + + for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { + if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { + if (pBackend != NULL) { + *pBackend = gBackendInfo[iBackend].backend; + } + + return MA_SUCCESS; + } + } + + /* Getting here means the backend name is unknown. */ + return MA_INVALID_ARGS; +} + +MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) +{ + /* + This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers + about some enums not being handled by the switch statement. + */ + switch (backend) + { + case ma_backend_wasapi: + #if defined(MA_HAS_WASAPI) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_dsound: + #if defined(MA_HAS_DSOUND) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_winmm: + #if defined(MA_HAS_WINMM) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_coreaudio: + #if defined(MA_HAS_COREAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_sndio: + #if defined(MA_HAS_SNDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_audio4: + #if defined(MA_HAS_AUDIO4) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_oss: + #if defined(MA_HAS_OSS) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_pulseaudio: + #if defined(MA_HAS_PULSEAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_alsa: + #if defined(MA_HAS_ALSA) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_jack: + #if defined(MA_HAS_JACK) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_aaudio: + #if defined(MA_HAS_AAUDIO) + #if defined(MA_ANDROID) + { + return ma_android_sdk_version() >= 26; + } + #else + return MA_FALSE; + #endif + #else + return MA_FALSE; + #endif + case ma_backend_opensl: + #if defined(MA_HAS_OPENSL) + #if defined(MA_ANDROID) + { + return ma_android_sdk_version() >= 9; + } + #else + return MA_TRUE; + #endif + #else + return MA_FALSE; + #endif + case ma_backend_webaudio: + #if defined(MA_HAS_WEBAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_custom: + #if defined(MA_HAS_CUSTOM) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_null: + #if defined(MA_HAS_NULL) + return MA_TRUE; + #else + return MA_FALSE; + #endif + + default: return MA_FALSE; + } +} + +MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) +{ + size_t backendCount; + size_t iBackend; + ma_result result = MA_SUCCESS; + + if (pBackendCount == NULL) { + return MA_INVALID_ARGS; + } + + backendCount = 0; + + for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { + ma_backend backend = (ma_backend)iBackend; + + if (ma_is_backend_enabled(backend)) { + /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ + if (backendCount == backendCap) { + result = MA_NO_SPACE; + break; + } else { + pBackends[backendCount] = backend; + backendCount += 1; + } + } + } + + if (pBackendCount != NULL) { + *pBackendCount = backendCount; + } + + return result; +} + +MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) +{ + switch (backend) + { + case ma_backend_wasapi: return MA_TRUE; + case ma_backend_dsound: return MA_FALSE; + case ma_backend_winmm: return MA_FALSE; + case ma_backend_coreaudio: return MA_FALSE; + case ma_backend_sndio: return MA_FALSE; + case ma_backend_audio4: return MA_FALSE; + case ma_backend_oss: return MA_FALSE; + case ma_backend_pulseaudio: return MA_FALSE; + case ma_backend_alsa: return MA_FALSE; + case ma_backend_jack: return MA_FALSE; + case ma_backend_aaudio: return MA_FALSE; + case ma_backend_opensl: return MA_FALSE; + case ma_backend_webaudio: return MA_FALSE; + case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ + case ma_backend_null: return MA_FALSE; + default: return MA_FALSE; + } +} + + + +#if defined(MA_WIN32) +/* WASAPI error codes. */ +#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) +#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) +#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) +#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) +#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) +#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) +#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) +#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) +#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) +#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) +#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) +#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) +#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) +#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) +#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) +#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) +#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) +#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) +#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) +#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) +#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) +#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) +#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) +#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) +#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) +#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) +#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) +#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) +#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) +#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) +#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) +#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) +#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) +#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) +#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) +#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) +#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) +#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) +#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) +#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) + +#define MA_DS_OK ((HRESULT)0) +#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) +#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) +#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) +#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ +#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) +#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ +#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) +#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ +#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) +#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ +#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) +#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) +#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ +#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) +#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) +#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) +#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ +#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ +#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) +#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) +#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) +#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) +#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) +#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) + +static ma_result ma_result_from_HRESULT(HRESULT hr) +{ + switch (hr) + { + case NOERROR: return MA_SUCCESS; + /*case S_OK: return MA_SUCCESS;*/ + + case E_POINTER: return MA_INVALID_ARGS; + case E_UNEXPECTED: return MA_ERROR; + case E_NOTIMPL: return MA_NOT_IMPLEMENTED; + case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; + case E_INVALIDARG: return MA_INVALID_ARGS; + case E_NOINTERFACE: return MA_API_NOT_FOUND; + case E_HANDLE: return MA_INVALID_ARGS; + case E_ABORT: return MA_ERROR; + case E_FAIL: return MA_ERROR; + case E_ACCESSDENIED: return MA_ACCESS_DENIED; + + /* WASAPI */ + case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; + case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; + case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; + case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; + case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; + case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; + case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; + case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; + case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; + case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; + case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; + case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; + case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; + case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; + case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; + case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; + case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; + case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; + + /* DirectSound */ + /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ + case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; + case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; + case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; + /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ + case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; + /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ + case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; + /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ + case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; + /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ + case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; + case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; + case MA_DSERR_NOAGGREGATION: return MA_ERROR; + case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; + case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; + case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; + /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ + /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ + case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; + case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; + case MA_DSERR_SENDLOOP: return MA_DEADLOCK; + case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; + case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; + case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; + + default: return MA_ERROR; + } +} + +/* PROPVARIANT */ +#define MA_VT_LPWSTR 31 +#define MA_VT_BLOB 65 + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +typedef struct +{ + WORD vt; + WORD wReserved1; + WORD wReserved2; + WORD wReserved3; + union + { + struct + { + ULONG cbSize; + BYTE* pBlobData; + } blob; + WCHAR* pwszVal; + char pad[16]; /* Just to ensure the size of the struct matches the official version. */ + }; +} MA_PROPVARIANT; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); +typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MA_PFN_CoUninitialize)(void); +typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); +typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); +typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); +typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); + +typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); +typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); + +#if defined(MA_WIN32_DESKTOP) +/* 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 * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); +typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); +#endif /* MA_WIN32_DESKTOP */ + + +MA_API size_t ma_strlen_WCHAR(const WCHAR* str) +{ + size_t len = 0; + while (str[len] != '\0') { + len += 1; + } + + return len; +} + +MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) +{ + while (*s1 != '\0' && *s1 == *s2) { + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} +#endif /* MA_WIN32 */ + + +#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" +#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" + + + + +/******************************************************************************* + +Timing + +*******************************************************************************/ +#if defined(MA_WIN32) && !defined(MA_POSIX) + static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ + static void ma_timer_init(ma_timer* pTimer) + { + LARGE_INTEGER counter; + + if (g_ma_TimerFrequency.QuadPart == 0) { + QueryPerformanceFrequency(&g_ma_TimerFrequency); + } + + QueryPerformanceCounter(&counter); + pTimer->counter = counter.QuadPart; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + LARGE_INTEGER counter; + if (!QueryPerformanceCounter(&counter)) { + return 0; + } + + return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; + } +#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) + static ma_uint64 g_ma_TimerFrequency = 0; + static void ma_timer_init(ma_timer* pTimer) + { + mach_timebase_info_data_t baseTime; + mach_timebase_info(&baseTime); + g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; + + pTimer->counter = mach_absolute_time(); + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter = mach_absolute_time(); + ma_uint64 oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; + } +#elif defined(MA_EMSCRIPTEN) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) + { + pTimer->counterD = emscripten_get_now(); + } + + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ + } +#else + #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L + #if defined(CLOCK_MONOTONIC) + #define MA_CLOCK_ID CLOCK_MONOTONIC + #else + #define MA_CLOCK_ID CLOCK_REALTIME + #endif + + static void ma_timer_init(ma_timer* pTimer) + { + struct timespec newTime; + clock_gettime(MA_CLOCK_ID, &newTime); + + pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter; + ma_uint64 oldTimeCounter; + + struct timespec newTime; + clock_gettime(MA_CLOCK_ID, &newTime); + + newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / 1000000000.0; + } + #else + static void ma_timer_init(ma_timer* pTimer) + { + struct timeval newTime; + gettimeofday(&newTime, NULL); + + pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter; + ma_uint64 oldTimeCounter; + + struct timeval newTime; + gettimeofday(&newTime, NULL); + + newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; + oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / 1000000.0; + } + #endif +#endif + + + +#if 0 +static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) +{ + ma_uint32 closestRate = 0; + ma_uint32 closestDiff = 0xFFFFFFFF; + size_t iStandardRate; + + for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; + ma_uint32 diff; + + if (sampleRateIn > standardRate) { + diff = sampleRateIn - standardRate; + } else { + diff = standardRate - sampleRateIn; + } + + if (diff == 0) { + return standardRate; /* The input sample rate is a standard rate. */ + } + + if (closestDiff > diff) { + closestDiff = diff; + closestRate = standardRate; + } + } + + return closestRate; +} +#endif + + +static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + return ma_disable_denormals(); + } else { + return 0; + } +} + +static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + ma_restore_denormals(prevState); + } else { + /* Do nothing. */ + (void)prevState; + } +} + +static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) +{ + ma_device_notification notification; + + MA_ZERO_OBJECT(¬ification); + notification.pDevice = pDevice; + notification.type = type; + + return notification; +} + +static void ma_device__on_notification(ma_device_notification notification) +{ + MA_ASSERT(notification.pDevice != NULL); + + if (notification.pDevice->onNotification != NULL) { + notification.pDevice->onNotification(¬ification); + } + + /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ + if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { + notification.pDevice->onStop(notification.pDevice); + } +} + +static void ma_device__on_notification_started(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); +} + +static void ma_device__on_notification_stopped(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); +} + +/* Not all platforms support reroute notifications. */ +#if !defined(MA_EMSCRIPTEN) +static void ma_device__on_notification_rerouted(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); +} +#endif + +#if defined(MA_EMSCRIPTEN) +EMSCRIPTEN_KEEPALIVE +void ma_device__on_notification_unlocked(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); +} +#endif + + +static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->onData != NULL); + + if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); + } + + pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); +} + +static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + + /* Don't read more data from the client if we're in the process of stopping. */ + if (ma_device_get_state(pDevice) == ma_device_state_stopping) { + return; + } + + if (pDevice->noFixedSizedCallback) { + /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ + ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); + } else { + /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ + ma_uint32 totalFramesProcessed = 0; + + while (totalFramesProcessed < frameCount) { + ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; + ma_uint32 framesToProcessThisIteration = 0; + + if (pFramesIn != NULL) { + /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ + if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { + /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), + ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), + framesToProcessThisIteration, + pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; + } + + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + /* No room left in the intermediary buffer. Fire the data callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* We'll do the duplex data callback later after we've processed the playback data. */ + } else { + ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + /* The intermediary buffer has just been drained. */ + pDevice->capture.intermediaryBufferLen = 0; + } + } + } + + if (pFramesOut != NULL) { + /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ + if (pDevice->playback.intermediaryBufferLen > 0) { + /* There's some content in the intermediary buffer. Read from that without firing the callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ + } else { + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; + } + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), + ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), + framesToProcessThisIteration, + pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; + } + + if (pDevice->playback.intermediaryBufferLen == 0) { + /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ + if (pDevice->type == ma_device_type_duplex) { + /* In duplex mode, the data callback will be fired later. Nothing to do here. */ + } else { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); + + /* The intermediary buffer has just been filled. */ + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; + } + } + } + + /* If we're in duplex mode we might need to do a refill of the data. */ + if (pDevice->type == ma_device_type_duplex) { + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ + pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ + } + } + + /* Make sure this is only incremented once in the duplex case. */ + totalFramesProcessed += framesToProcessThisIteration; + } + } +} + +static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + float masterVolumeFactor; + + ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ + + if (pDevice->onData) { + unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); + { + /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ + if (pFramesIn != NULL && masterVolumeFactor < 1) { + ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint32 totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; + if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { + framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; + } + + ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); + + ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); + + totalFramesProcessed += framesToProcessThisIteration; + } + } else { + ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); + } + + /* Volume control and clipping for playback devices. */ + if (pFramesOut != NULL) { + if (masterVolumeFactor < 1) { + if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ + ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); + } + } + + if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { + ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ + } + } + } + ma_device_restore_denormals(pDevice, prevDenormalState); + } +} + + + +/* A helper function for reading sample data from the client. */ +static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCount > 0); + MA_ASSERT(pFramesOut != NULL); + + if (pDevice->playback.converter.isPassthrough) { + ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); + } else { + ma_result result; + ma_uint64 totalFramesReadOut; + void* pRunningFramesOut; + + totalFramesReadOut = 0; + pRunningFramesOut = pFramesOut; + + /* + We run slightly different logic depending on whether or not we're using a heap-allocated + buffer for caching input data. This will be the case if the data converter does not have + the ability to retrieve the required input frame count for a given output frame count. + */ + if (pDevice->playback.pInputCache != NULL) { + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDevice->playback.inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { + framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; + pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ + if (pDevice->playback.inputCacheRemaining == 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; + } + } + } else { + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; + + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } + + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } + + if (framesToReadThisIterationIn > 0) { + ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationIn = framesToReadThisIterationIn; + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + } + } +} + +/* A helper for sending sample data to the client. */ +static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCountInDeviceFormat > 0); + MA_ASSERT(pFramesInDeviceFormat != NULL); + + if (pDevice->capture.converter.isPassthrough) { + ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); + } else { + ma_result result; + ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint64 totalDeviceFramesProcessed = 0; + ma_uint64 totalClientFramesProcessed = 0; + const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; + + /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ + for (;;) { + ma_uint64 deviceFramesProcessedThisIteration; + ma_uint64 clientFramesProcessedThisIteration; + + deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); + clientFramesProcessedThisIteration = framesInClientFormatCap; + + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); + if (result != MA_SUCCESS) { + break; + } + + if (clientFramesProcessedThisIteration > 0) { + ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ + } + + pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; + totalClientFramesProcessed += clientFramesProcessedThisIteration; + + /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ + (void)totalClientFramesProcessed; + + if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { + break; /* We're done. */ + } + } + } +} + +static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) +{ + ma_result result; + ma_uint32 totalDeviceFramesProcessed = 0; + const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCountInDeviceFormat > 0); + MA_ASSERT(pFramesInDeviceFormat != NULL); + MA_ASSERT(pRB != NULL); + + /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ + for (;;) { + ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); + ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint64 framesProcessedInDeviceFormat; + ma_uint64 framesProcessedInClientFormat; + void* pFramesInClientFormat; + + result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); + break; + } + + if (framesToProcessInClientFormat == 0) { + if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { + break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ + } + } + + /* Convert. */ + framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; + framesProcessedInClientFormat = framesToProcessInClientFormat; + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); + if (result != MA_SUCCESS) { + break; + } + + result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); + break; + } + + pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ + + /* We're done when we're unable to process any client nor device frames. */ + if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { + break; /* Done. */ + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) +{ + ma_result result; + ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 totalFramesReadOut = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCount > 0); + MA_ASSERT(pFramesInInternalFormat != NULL); + MA_ASSERT(pRB != NULL); + MA_ASSERT(pDevice->playback.pInputCache != NULL); + + /* + Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for + the whole frameCount frames we just use silence instead for the input data. + */ + MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); + + while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { + /* + We should have a buffer allocated on the heap. Any playback frames still sitting in there + need to be sent to the internal device before we process any more data from the client. + */ + if (pDevice->playback.inputCacheRemaining > 0) { + ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; + ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); + ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); + + pDevice->playback.inputCacheConsumed += framesConvertedIn; + pDevice->playback.inputCacheRemaining -= framesConvertedIn; + + totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ + pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + } + + /* If there's no more data in the cache we'll need to fill it with some. */ + if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { + ma_uint32 inputFrameCount; + void* pInputFrames; + + inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; + result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); + if (result == MA_SUCCESS) { + if (inputFrameCount > 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); + } else { + if (ma_pcm_rb_pointer_distance(pRB) == 0) { + break; /* Underrun. */ + } + } + } else { + /* No capture data available. Feed in silence. */ + inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); + } + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = inputFrameCount; + + result = ma_pcm_rb_commit_read(pRB, inputFrameCount); + if (result != MA_SUCCESS) { + return result; /* Should never happen. */ + } + } + } + + return MA_SUCCESS; +} + +/* A helper for changing the state of the device. */ +static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) +{ + ma_atomic_device_state_set(&pDevice->state, newState); +} + + +#if defined(MA_WIN32) + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ +#endif + + + +MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ +{ + ma_uint32 i; + for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { + if (g_maFormatPriorities[i] == format) { + return i; + } + } + + /* Getting here means the format could not be found or is equal to ma_format_unknown. */ + return (ma_uint32)-1; +} + +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); + +static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) +{ + if (pDeviceDescriptor == NULL) { + return MA_FALSE; + } + + if (pDeviceDescriptor->format == ma_format_unknown) { + return MA_FALSE; + } + + if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { + return MA_FALSE; + } + + if (pDeviceDescriptor->sampleRate == 0) { + return MA_FALSE; + } + + return MA_TRUE; +} + + +static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) +{ + ma_result result = MA_SUCCESS; + ma_bool32 exitLoop = MA_FALSE; + ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 capturedDeviceDataCapInFrames = 0; + ma_uint32 playbackDeviceDataCapInFrames = 0; + + MA_ASSERT(pDevice != NULL); + + /* Just some quick validation on the device type and the available callbacks. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + if (pDevice->pContext->callbacks.onDeviceRead == NULL) { + return MA_NOT_IMPLEMENTED; + } + + capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { + return MA_NOT_IMPLEMENTED; + } + + playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + /* NOTE: The device was started outside of this function, in the worker thread. */ + + while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { + switch (pDevice->type) { + case ma_device_type_duplex: + { + /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ + ma_uint32 totalCapturedDeviceFramesProcessed = 0; + ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); + + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { + ma_uint32 capturedDeviceFramesRemaining; + ma_uint32 capturedDeviceFramesProcessed; + ma_uint32 capturedDeviceFramesToProcess; + ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; + if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { + capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; + } + + result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; + capturedDeviceFramesProcessed = 0; + + /* At this point we have our captured data in device format and we now need to convert it to client format. */ + for (;;) { + ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); + ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; + ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + + /* Convert capture data from device format to client format. */ + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); + if (result != MA_SUCCESS) { + break; + } + + /* + If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small + which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. + */ + if (capturedClientFramesToProcessThisIteration == 0) { + break; + } + + ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ + + capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ + capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ + + /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ + for (;;) { + ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; + ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); + if (result != MA_SUCCESS) { + break; + } + + result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ + if (capturedClientFramesToProcessThisIteration == 0) { + break; + } + } + + /* In case an error happened from ma_device_write__null()... */ + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + } + + /* Make sure we don't get stuck in the inner loop. */ + if (capturedDeviceFramesProcessed == 0) { + break; + } + + totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; + } + } break; + + case ma_device_type_capture: + case ma_device_type_loopback: + { + ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + ma_uint32 framesReadThisPeriod = 0; + while (framesReadThisPeriod < periodSizeInFrames) { + ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; + ma_uint32 framesProcessed; + ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; + if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { + framesToReadThisIteration = capturedDeviceDataCapInFrames; + } + + result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + /* Make sure we don't get stuck in the inner loop. */ + if (framesProcessed == 0) { + break; + } + + ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); + + framesReadThisPeriod += framesProcessed; + } + } break; + + case ma_device_type_playback: + { + /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ + ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + ma_uint32 framesWrittenThisPeriod = 0; + while (framesWrittenThisPeriod < periodSizeInFrames) { + ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; + ma_uint32 framesProcessed; + ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; + if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { + framesToWriteThisIteration = playbackDeviceDataCapInFrames; + } + + ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); + + result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + /* Make sure we don't get stuck in the inner loop. */ + if (framesProcessed == 0) { + break; + } + + framesWrittenThisPeriod += framesProcessed; + } + } break; + + /* Should never get here. */ + default: break; + } + } + + return result; +} + + + +/******************************************************************************* + +Null Backend + +*******************************************************************************/ +#ifdef MA_HAS_NULL + +#define MA_DEVICE_OP_NONE__NULL 0 +#define MA_DEVICE_OP_START__NULL 1 +#define MA_DEVICE_OP_SUSPEND__NULL 2 +#define MA_DEVICE_OP_KILL__NULL 3 + +static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) +{ + ma_device* pDevice = (ma_device*)pData; + MA_ASSERT(pDevice != NULL); + + for (;;) { /* Keep the thread alive until the device is uninitialized. */ + ma_uint32 operation; + + /* Wait for an operation to be requested. */ + ma_event_wait(&pDevice->null_device.operationEvent); + + /* At this point an event should have been triggered. */ + operation = pDevice->null_device.operation; + + /* Starting the device needs to put the thread into a loop. */ + if (operation == MA_DEVICE_OP_START__NULL) { + /* Reset the timer just in case. */ + ma_timer_init(&pDevice->null_device.timer); + + /* Getting here means a suspend or kill operation has been requested. */ + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; + } + + /* Suspending the device means we need to stop the timer and just continue the loop. */ + if (operation == MA_DEVICE_OP_SUSPEND__NULL) { + /* We need to add the current run time to the prior run time, then reset the timer. */ + pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); + ma_timer_init(&pDevice->null_device.timer); + + /* We're done. */ + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; + } + + /* Killing the device means we need to get out of this loop so that this thread can terminate. */ + if (operation == MA_DEVICE_OP_KILL__NULL) { + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + break; + } + + /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ + if (operation == MA_DEVICE_OP_NONE__NULL) { + MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ + pDevice->null_device.operationResult = MA_INVALID_OPERATION; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; /* Continue the loop. Don't terminate. */ + } + } + + return (ma_thread_result)0; +} + +static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) +{ + ma_result result; + + /* + TODO: Need to review this and consider just using mutual exclusion. I think the original motivation + for this was to just post the event to a queue and return immediately, but that has since changed + and now this function is synchronous. I think this can be simplified to just use a mutex. + */ + + /* + The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later + to support queing of operations. + */ + result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); + if (result != MA_SUCCESS) { + return result; /* Failed to wait for the event. */ + } + + /* + When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to + signal an event to the worker thread to let it know that it can start work. + */ + pDevice->null_device.operation = operation; + + /* Once the operation code has been set, the worker thread can start work. */ + if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { + return MA_ERROR; + } + + /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ + if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { + return MA_ERROR; + } + + return pDevice->null_device.operationResult; +} + +static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) +{ + ma_uint32 internalSampleRate; + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + internalSampleRate = pDevice->capture.internalSampleRate; + } else { + internalSampleRate = pDevice->playback.internalSampleRate; + } + + return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); +} + +static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + (void)cbResult; /* Silence a static analysis warning. */ + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + + /* Support everything on the null backend. */ + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; + pDeviceInfo->nativeDataFormats[0].sampleRate = 0; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + (void)pContext; + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* Keep it clean and wait for the device thread to finish before returning. */ + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); + + /* Wait for the thread to finish before continuing. */ + ma_thread_wait(&pDevice->null_device.deviceThread); + + /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ + ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); + ma_event_uninit(&pDevice->null_device.operationCompletionEvent); + ma_event_uninit(&pDevice->null_device.operationEvent); + + return MA_SUCCESS; +} + +static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->null_device); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* The null backend supports everything exactly as we specify it. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; + pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + } + + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; + pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); + } + + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + } + + /* + In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the + first period is "written" to it, and then stopped in ma_device_stop__null(). + */ + result = ma_event_init(&pDevice->null_device.operationEvent); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_event_init(&pDevice->null_device.operationCompletionEvent); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ + if (result != MA_SUCCESS) { + return result; + } + + result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); + + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); + return MA_SUCCESS; +} + +static ma_result ma_device_stop__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); + + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); + return MA_SUCCESS; +} + +static ma_bool32 ma_device_is_started__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + return ma_atomic_bool32_get(&pDevice->null_device.isStarted); +} + +static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalPCMFramesProcessed; + ma_bool32 wasStartedOnEntry; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + wasStartedOnEntry = ma_device_is_started__null(pDevice); + + /* Keep going until everything has been read. */ + totalPCMFramesProcessed = 0; + while (totalPCMFramesProcessed < frameCount) { + ma_uint64 targetFrame; + + /* If there are any frames remaining in the current period, consume those first. */ + if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { + ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); + ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; + if (framesToProcess > framesRemaining) { + framesToProcess = framesRemaining; + } + + /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ + (void)pPCMFrames; + + pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; + totalPCMFramesProcessed += framesToProcess; + } + + /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ + if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { + pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; + + if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { + result = ma_device_start__null(pDevice); + if (result != MA_SUCCESS) { + break; + } + } + } + + /* If we've consumed the whole buffer we can return now. */ + MA_ASSERT(totalPCMFramesProcessed <= frameCount); + if (totalPCMFramesProcessed == frameCount) { + break; + } + + /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ + targetFrame = pDevice->null_device.lastProcessedFramePlayback; + for (;;) { + ma_uint64 currentFrame; + + /* Stop waiting if the device has been stopped. */ + if (!ma_device_is_started__null(pDevice)) { + break; + } + + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + if (currentFrame >= targetFrame) { + break; + } + + /* Getting here means we haven't yet reached the target sample, so continue waiting. */ + ma_sleep(10); + } + + pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; + pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalPCMFramesProcessed; + } + + return result; +} + +static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalPCMFramesProcessed; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + /* Keep going until everything has been read. */ + totalPCMFramesProcessed = 0; + while (totalPCMFramesProcessed < frameCount) { + ma_uint64 targetFrame; + + /* If there are any frames remaining in the current period, consume those first. */ + if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); + ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; + if (framesToProcess > framesRemaining) { + framesToProcess = framesRemaining; + } + + /* We need to ensure the output buffer is zeroed. */ + MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); + + pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; + totalPCMFramesProcessed += framesToProcess; + } + + /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ + if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { + pDevice->null_device.currentPeriodFramesRemainingCapture = 0; + } + + /* If we've consumed the whole buffer we can return now. */ + MA_ASSERT(totalPCMFramesProcessed <= frameCount); + if (totalPCMFramesProcessed == frameCount) { + break; + } + + /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ + targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; + for (;;) { + ma_uint64 currentFrame; + + /* Stop waiting if the device has been stopped. */ + if (!ma_device_is_started__null(pDevice)) { + break; + } + + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + if (currentFrame >= targetFrame) { + break; + } + + /* Getting here means we haven't yet reached the target sample, so continue waiting. */ + ma_sleep(10); + } + + pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; + pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalPCMFramesProcessed; + } + + return result; +} + +static ma_result ma_context_uninit__null(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_null); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + (void)pContext; + + pCallbacks->onContextInit = ma_context_init__null; + pCallbacks->onContextUninit = ma_context_uninit__null; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; + pCallbacks->onDeviceInit = ma_device_init__null; + pCallbacks->onDeviceUninit = ma_device_uninit__null; + pCallbacks->onDeviceStart = ma_device_start__null; + pCallbacks->onDeviceStop = ma_device_stop__null; + pCallbacks->onDeviceRead = ma_device_read__null; + pCallbacks->onDeviceWrite = ma_device_write__null; + pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ + + /* The null backend always works. */ + return MA_SUCCESS; +} +#endif + + + +/******************************************************************************* + +WIN32 COMMON + +*******************************************************************************/ +#if defined(MA_WIN32) +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) + #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() + #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) + #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) +#else + #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) + #define ma_CoUninitialize(pContext) CoUninitialize() + #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) + #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) +#endif + +#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) +typedef size_t DWORD_PTR; +#endif + +#if !defined(WAVE_FORMAT_1M08) +#define WAVE_FORMAT_1M08 0x00000001 +#define WAVE_FORMAT_1S08 0x00000002 +#define WAVE_FORMAT_1M16 0x00000004 +#define WAVE_FORMAT_1S16 0x00000008 +#define WAVE_FORMAT_2M08 0x00000010 +#define WAVE_FORMAT_2S08 0x00000020 +#define WAVE_FORMAT_2M16 0x00000040 +#define WAVE_FORMAT_2S16 0x00000080 +#define WAVE_FORMAT_4M08 0x00000100 +#define WAVE_FORMAT_4S08 0x00000200 +#define WAVE_FORMAT_4M16 0x00000400 +#define WAVE_FORMAT_4S16 0x00000800 +#endif + +#if !defined(WAVE_FORMAT_44M08) +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + +#ifndef SPEAKER_FRONT_LEFT +#define SPEAKER_FRONT_LEFT 0x1 +#define SPEAKER_FRONT_RIGHT 0x2 +#define SPEAKER_FRONT_CENTER 0x4 +#define SPEAKER_LOW_FREQUENCY 0x8 +#define SPEAKER_BACK_LEFT 0x10 +#define SPEAKER_BACK_RIGHT 0x20 +#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +#define SPEAKER_BACK_CENTER 0x100 +#define SPEAKER_SIDE_LEFT 0x200 +#define SPEAKER_SIDE_RIGHT 0x400 +#define SPEAKER_TOP_CENTER 0x800 +#define SPEAKER_TOP_FRONT_LEFT 0x1000 +#define SPEAKER_TOP_FRONT_CENTER 0x2000 +#define SPEAKER_TOP_FRONT_RIGHT 0x4000 +#define SPEAKER_TOP_BACK_LEFT 0x8000 +#define SPEAKER_TOP_BACK_CENTER 0x10000 +#define SPEAKER_TOP_BACK_RIGHT 0x20000 +#endif + +/* +Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this +because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The +standard version uses tight packing, but for compiler compatibility we're not doing that with ours. +*/ +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} MA_WAVEFORMATEX; + +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; + union + { + WORD wValidBitsPerSample; + WORD wSamplesPerBlock; + WORD wReserved; + } Samples; + DWORD dwChannelMask; + GUID SubFormat; +} MA_WAVEFORMATEXTENSIBLE; + + + +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif + +/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ +static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) +{ + switch (id) + { + case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; + case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; + case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; + case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ +static DWORD ma_channel_id_to_win32(DWORD id) +{ + switch (id) + { + case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; + case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; + case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; + case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; + case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; + case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts a channel mapping to a Win32-style channel mask. */ +static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) +{ + DWORD dwChannelMask = 0; + ma_uint32 iChannel; + + for (iChannel = 0; iChannel < channels; ++iChannel) { + dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); + } + + return dwChannelMask; +} + +/* Converts a Win32-style channel mask to a miniaudio channel map. */ +static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) +{ + /* If the channel mask is set to 0, just assume a default Win32 channel map. */ + if (dwChannelMask == 0) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); + } else { + if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else { + /* Just iterate over each bit. */ + ma_uint32 iChannel = 0; + ma_uint32 iBit; + + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { + DWORD bitValue = (dwChannelMask & (1UL << iBit)); + if (bitValue != 0) { + /* The bit is set. */ + pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); + iChannel += 1; + } + } + } + } +} + +#ifdef __cplusplus +static ma_bool32 ma_is_guid_equal(const void* a, const void* b) +{ + return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); +} +#else +#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) +#endif + +static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) +{ + static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + return ma_is_guid_equal(guid, &nullguid); +} + +static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) +{ + MA_ASSERT(pWF != NULL); + + if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; + if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return ma_format_s32; + } + if (pWFEX->Samples.wValidBitsPerSample == 24) { + if (pWFEX->wBitsPerSample == 32) { + return ma_format_s32; + } + if (pWFEX->wBitsPerSample == 24) { + return ma_format_s24; + } + } + if (pWFEX->Samples.wValidBitsPerSample == 16) { + return ma_format_s16; + } + if (pWFEX->Samples.wValidBitsPerSample == 8) { + return ma_format_u8; + } + } + if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return ma_format_f32; + } + /* + if (pWFEX->Samples.wValidBitsPerSample == 64) { + return ma_format_f64; + } + */ + } + } else { + if (pWF->wFormatTag == WAVE_FORMAT_PCM) { + if (pWF->wBitsPerSample == 32) { + return ma_format_s32; + } + if (pWF->wBitsPerSample == 24) { + return ma_format_s24; + } + if (pWF->wBitsPerSample == 16) { + return ma_format_s16; + } + if (pWF->wBitsPerSample == 8) { + return ma_format_u8; + } + } + if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + if (pWF->wBitsPerSample == 32) { + return ma_format_f32; + } + if (pWF->wBitsPerSample == 64) { + /*return ma_format_f64;*/ + } + } + } + + return ma_format_unknown; +} +#endif + + +/******************************************************************************* + +WASAPI Backend + +*******************************************************************************/ +#ifdef MA_HAS_WASAPI +#if 0 +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ +#endif +#include +#include +#if defined(_MSC_VER) + #pragma warning(pop) +#endif +#endif /* 0 */ + +static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); + +/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ +#define MA_WIN32_WINNT_VISTA 0x0600 +#define MA_VER_MINORVERSION 0x01 +#define MA_VER_MAJORVERSION 0x02 +#define MA_VER_SERVICEPACKMAJOR 0x20 +#define MA_VER_GREATER_EQUAL 0x03 + +typedef struct { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR szCSDVersion[128]; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; +} ma_OSVERSIONINFOEXW; + +typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); +typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); + + +#ifndef PROPERTYKEY_DEFINED +#define PROPERTYKEY_DEFINED +#ifndef __WATCOMC__ +typedef struct +{ + GUID fmtid; + DWORD pid; +} PROPERTYKEY; +#endif +#endif + +/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ +static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) +{ + MA_ZERO_OBJECT(pProp); +} + + +static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; +static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; + +static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ +#endif + +static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ +static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ +static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ +static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ +static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ +static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ +static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ +static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ +#endif + +static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ +static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +#define MA_MM_DEVICE_STATE_ACTIVE 1 +#define MA_MM_DEVICE_STATE_DISABLED 2 +#define MA_MM_DEVICE_STATE_NOTPRESENT 4 +#define MA_MM_DEVICE_STATE_UNPLUGGED 8 + +typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; +typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; +typedef struct ma_IMMDevice ma_IMMDevice; +#else +typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; +typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; +#endif +typedef struct ma_IPropertyStore ma_IPropertyStore; +typedef struct ma_IAudioClient ma_IAudioClient; +typedef struct ma_IAudioClient2 ma_IAudioClient2; +typedef struct ma_IAudioClient3 ma_IAudioClient3; +typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; +typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; + +typedef ma_int64 MA_REFERENCE_TIME; + +#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 +#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 +#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 +#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 +#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 +#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 +#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 +#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 +#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 +#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 + +/* Buffer flags. */ +#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 +#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 +#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 + +typedef enum +{ + ma_eRender = 0, + ma_eCapture = 1, + ma_eAll = 2 +} ma_EDataFlow; + +typedef enum +{ + ma_eConsole = 0, + ma_eMultimedia = 1, + ma_eCommunications = 2 +} ma_ERole; + +typedef enum +{ + MA_AUDCLNT_SHAREMODE_SHARED, + MA_AUDCLNT_SHAREMODE_EXCLUSIVE +} MA_AUDCLNT_SHAREMODE; + +typedef enum +{ + MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ +} MA_AUDIO_STREAM_CATEGORY; + +typedef struct +{ + ma_uint32 cbSize; + BOOL bIsOffload; + MA_AUDIO_STREAM_CATEGORY eCategory; +} ma_AudioClientProperties; + +/* IUnknown */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); +} ma_IUnknownVtbl; +struct ma_IUnknown +{ + ma_IUnknownVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* IMMNotificationClient */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); + + /* IMMNotificationClient */ + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); + } ma_IMMNotificationClientVtbl; + + /* IMMDeviceEnumerator */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); + + /* IMMDeviceEnumerator */ + HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); + HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); + HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); + HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); + HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); + } ma_IMMDeviceEnumeratorVtbl; + struct ma_IMMDeviceEnumerator + { + ma_IMMDeviceEnumeratorVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } + + + /* IMMDeviceCollection */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); + + /* IMMDeviceCollection */ + HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); + HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); + } ma_IMMDeviceCollectionVtbl; + struct ma_IMMDeviceCollection + { + ma_IMMDeviceCollectionVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } + static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } + + + /* IMMDevice */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); + + /* IMMDevice */ + HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); + HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); + HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); + HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); + } ma_IMMDeviceVtbl; + struct ma_IMMDevice + { + ma_IMMDeviceVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } + static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } + static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } + static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } +#else + /* IActivateAudioInterfaceAsyncOperation */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); + + /* IActivateAudioInterfaceAsyncOperation */ + HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); + } ma_IActivateAudioInterfaceAsyncOperationVtbl; + struct ma_IActivateAudioInterfaceAsyncOperation + { + ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } +#endif + +/* IPropertyStore */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); + + /* IPropertyStore */ + HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); + HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); + HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); + HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); + HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); +} ma_IPropertyStoreVtbl; +struct ma_IPropertyStore +{ + ma_IPropertyStoreVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } +static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } +static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } + + +/* IAudioClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); +} ma_IAudioClientVtbl; +struct ma_IAudioClient +{ + ma_IAudioClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } + +/* IAudioClient2 */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); + + /* IAudioClient2 */ + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); +} ma_IAudioClient2Vtbl; +struct ma_IAudioClient2 +{ + ma_IAudioClient2Vtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } + + +/* IAudioClient3 */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); + + /* IAudioClient2 */ + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + + /* IAudioClient3 */ + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); +} ma_IAudioClient3Vtbl; +struct ma_IAudioClient3 +{ + ma_IAudioClient3Vtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } + + +/* IAudioRenderClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); + + /* IAudioRenderClient */ + HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); +} ma_IAudioRenderClientVtbl; +struct ma_IAudioRenderClient +{ + ma_IAudioRenderClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } +static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } + + +/* IAudioCaptureClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); + + /* IAudioRenderClient */ + HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); + HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); +} ma_IAudioCaptureClientVtbl; +struct ma_IAudioCaptureClient +{ + ma_IAudioCaptureClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } + +#if defined(MA_WIN32_UWP) +/* mmdevapi Functions */ +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); +#endif + +/* Avrt Functions */ +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); +typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; + +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); + + /* IActivateAudioInterfaceCompletionHandler */ + HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); +} ma_completion_handler_uwp_vtbl; +struct ma_completion_handler_uwp +{ + ma_completion_handler_uwp_vtbl* lpVtbl; + MA_ATOMIC(4, ma_uint32) counter; + HANDLE hEvent; +}; + +static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_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 (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + /* Getting here means the IID is IUnknown or IMMNotificationClient. */ + *ppObject = (void*)pThis; + ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) +{ + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; +} + +static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) +{ + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + if (newRefCount == 0) { + return 0; /* We don't free anything here because we never allocate the object on the heap. */ + } + + return (ULONG)newRefCount; +} + +static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) +{ + (void)pActivateOperation; + SetEvent(pThis->hEvent); + return S_OK; +} + + +static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { + ma_completion_handler_uwp_QueryInterface, + ma_completion_handler_uwp_AddRef, + ma_completion_handler_uwp_Release, + ma_completion_handler_uwp_ActivateCompleted +}; + +static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) +{ + MA_ASSERT(pHandler != NULL); + MA_ZERO_OBJECT(pHandler); + + pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; + pHandler->counter = 1; + pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pHandler->hEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) +{ + if (pHandler->hEvent != NULL) { + CloseHandle(pHandler->hEvent); + } +} + +static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) +{ + WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); +} +#endif /* !MA_WIN32_DESKTOP */ + +/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_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 (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + /* Getting here means the IID is IUnknown or IMMNotificationClient. */ + *ppObject = (void*)pThis; + ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) +{ + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; +} + +static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) +{ + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + if (newRefCount == 0) { + return 0; /* We don't free anything here because we never allocate the object on the heap. */ + } + + return (ULONG)newRefCount; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) +{ + ma_bool32 isThisDevice = MA_FALSE; + ma_bool32 isCapture = MA_FALSE; + ma_bool32 isPlayback = MA_FALSE; + +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ +#endif + + /* + There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect + that the device is disabled or has been unplugged. + */ + if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { + isCapture = MA_TRUE; + if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { + isPlayback = MA_TRUE; + if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + + /* + If the device ID matches our device we need to mark our device as detached and stop it. When a + device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device + was started at the time of being removed. + */ + if (isThisDevice) { + if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { + /* + Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll + use this to determine whether or not we need to automatically start the device when it's + plugged back in again. + */ + if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { + if (isPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; + } + if (isCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; + } + + ma_device_stop(pThis->pDevice); + } + } + + if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { + /* The device was activated. If we were detached, we need to start it again. */ + ma_bool8 tryRestartingDevice = MA_FALSE; + + if (isPlayback) { + if (pThis->pDevice->wasapi.isDetachedPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); + tryRestartingDevice = MA_TRUE; + } + } + + if (isCapture) { + if (pThis->pDevice->wasapi.isDetachedCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + tryRestartingDevice = MA_TRUE; + } + } + + if (tryRestartingDevice) { + if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { + ma_device_start(pThis->pDevice); + } + } + } + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + /* We don't need to worry about this event for our purposes. */ + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + /* We don't need to worry about this event for our purposes. */ + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ +#endif + + (void)role; + + /* We only care about devices with the same data flow as the current device. */ + if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || + (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || + (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); + return S_OK; + } + + /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ + if (pThis->pDevice->type == ma_device_type_loopback) { + dataFlow = ma_eCapture; + } + + /* Don't do automatic stream routing if we're not allowed. */ + if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || + (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); + 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 miniaudio, we can try re-enabling this once + it's fixed. + */ + if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || + (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); + return S_OK; + } + + + + /* + Second attempt at device rerouting. We're going to retrieve the device's state at the time of + the route change. We're then going to stop the device, reinitialize the device, and then start + it again if the state before stopping was ma_device_state_started. + */ + { + ma_uint32 previousState = ma_device_get_state(pThis->pDevice); + ma_bool8 restartDevice = MA_FALSE; + + if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); + return S_OK; + } + + if (previousState == ma_device_state_started) { + ma_device_stop(pThis->pDevice); + restartDevice = MA_TRUE; + } + + if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ + ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); + { + if (dataFlow == ma_eRender) { + ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); + + if (pThis->pDevice->wasapi.isDetachedPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { + restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ + } + } + } + else { + ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + + if (pThis->pDevice->wasapi.isDetachedCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { + restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ + } + } + } + } + ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); + + if (restartDevice) { + ma_device_start(pThis->pDevice); + } + } + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + (void)pThis; + (void)pDeviceID; + (void)key; + return S_OK; +} + +static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { + ma_IMMNotificationClient_QueryInterface, + ma_IMMNotificationClient_AddRef, + ma_IMMNotificationClient_Release, + ma_IMMNotificationClient_OnDeviceStateChanged, + ma_IMMNotificationClient_OnDeviceAdded, + ma_IMMNotificationClient_OnDeviceRemoved, + ma_IMMNotificationClient_OnDefaultDeviceChanged, + ma_IMMNotificationClient_OnPropertyValueChanged +}; +#endif /* MA_WIN32_DESKTOP */ + +static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) +{ + switch (usage) + { + case ma_wasapi_usage_default: return NULL; + case ma_wasapi_usage_games: return "Games"; + case ma_wasapi_usage_pro_audio: return "Pro Audio"; + default: break; + } + + return NULL; +} + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +typedef ma_IMMDevice ma_WASAPIDeviceInterface; +#else +typedef ma_IUnknown ma_WASAPIDeviceInterface; +#endif + + +#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 +#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 +#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 + +static ma_context_command__wasapi ma_context_init_command__wasapi(int code) +{ + ma_context_command__wasapi cmd; + + MA_ZERO_OBJECT(&cmd); + cmd.code = code; + + return cmd; +} + +static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) +{ + /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ + ma_result result; + ma_bool32 isUsingLocalEvent = MA_FALSE; + ma_event localEvent; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCmd != NULL); + + if (pCmd->pEvent == NULL) { + isUsingLocalEvent = MA_TRUE; + + result = ma_event_init(&localEvent); + if (result != MA_SUCCESS) { + return result; /* Failed to create the event for this command. */ + } + } + + /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ + ma_mutex_lock(&pContext->wasapi.commandLock); + { + ma_uint32 index; + + /* Spin until we've got some space available. */ + while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { + ma_yield(); + } + + /* Space is now available. Can safely add to the list. */ + index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); + pContext->wasapi.commands[index] = *pCmd; + pContext->wasapi.commands[index].pEvent = &localEvent; + pContext->wasapi.commandCount += 1; + + /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ + ma_semaphore_release(&pContext->wasapi.commandSem); + } + ma_mutex_unlock(&pContext->wasapi.commandLock); + + if (isUsingLocalEvent) { + ma_event_wait(&localEvent); + ma_event_uninit(&localEvent); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) +{ + ma_result result = MA_SUCCESS; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCmd != NULL); + + result = ma_semaphore_wait(&pContext->wasapi.commandSem); + if (result == MA_SUCCESS) { + ma_mutex_lock(&pContext->wasapi.commandLock); + { + *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; + pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); + pContext->wasapi.commandCount -= 1; + } + ma_mutex_unlock(&pContext->wasapi.commandLock); + } + + return result; +} + +static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) +{ + ma_result result; + ma_context* pContext = (ma_context*)pUserData; + MA_ASSERT(pContext != NULL); + + for (;;) { + ma_context_command__wasapi cmd; + result = ma_context_next_command__wasapi(pContext, &cmd); + if (result != MA_SUCCESS) { + break; + } + + switch (cmd.code) + { + case MA_CONTEXT_COMMAND_QUIT__WASAPI: + { + /* Do nothing. Handled after the switch. */ + } break; + + case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: + { + if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); + } else { + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); + } + } break; + + case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: + { + if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { + if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); + cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; + } + } + + if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { + if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); + cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; + } + } + } break; + + default: + { + /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ + MA_ASSERT(MA_FALSE); + } break; + } + + if (cmd.pEvent != NULL) { + ma_event_signal(cmd.pEvent); + } + + if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { + break; /* Received a quit message. Get out of here. */ + } + } + + return (ma_thread_result)0; +} + +static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) +{ + ma_result result; + ma_result cmdResult; + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); + cmd.data.createAudioClient.deviceType = deviceType; + cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; + cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; + cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ + + result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ + if (result != MA_SUCCESS) { + return result; + } + + return *cmd.data.createAudioClient.pResult; +} + +#if 0 /* Not used at the moment, but leaving here for future use. */ +static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); + cmd.data.releaseAudioClient.pDevice = pDevice; + cmd.data.releaseAudioClient.deviceType = deviceType; + + result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} +#endif + + +static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) +{ + MA_ASSERT(pWF != NULL); + MA_ASSERT(pInfo != NULL); + + if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { + return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ + } + + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; + pInfo->nativeDataFormatCount += 1; +} + +static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) +{ + HRESULT hr; + MA_WAVEFORMATEX* pWF = NULL; + + MA_ASSERT(pAudioClient != NULL); + MA_ASSERT(pInfo != NULL); + + /* Shared Mode. We use GetMixFormat() here. */ + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); + if (SUCCEEDED(hr)) { + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); + return ma_result_from_HRESULT(hr); + } + + /* + Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on + UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on + out, MA_SUCCESS is guaranteed to be returned. + */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + ma_IPropertyStore *pProperties; + + /* + The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is + correct which will simplify our searching. + */ + hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT var; + ma_PropVariantInit(&var); + + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); + if (SUCCEEDED(hr)) { + pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; + + /* + In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format + first. If this fails, fall back to a search. + */ + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); + if (SUCCEEDED(hr)) { + /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); + } else { + /* + The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel + count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. + */ + ma_uint32 channels = pWF->nChannels; + ma_channel defaultChannelMap[MA_MAX_CHANNELS]; + MA_WAVEFORMATEXTENSIBLE wf; + ma_bool32 found; + ma_uint32 iFormat; + + /* Make sure we don't overflow the channel map. */ + if (channels > MA_MAX_CHANNELS) { + channels = MA_MAX_CHANNELS; + } + + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); + + MA_ZERO_OBJECT(&wf); + wf.cbSize = sizeof(wf); + wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.nChannels = (WORD)channels; + wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); + + found = MA_FALSE; + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { + ma_format format = g_maFormatPriorities[iFormat]; + ma_uint32 iSampleRate; + + wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; + if (format == ma_format_f32) { + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } else { + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } + + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { + wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; + + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); + found = MA_TRUE; + break; + } + } + + if (found) { + break; + } + } + + ma_PropVariantClear(pContext, &var); + + if (!found) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); + } + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); + } + + ma_IPropertyStore_Release(pProperties); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); + } + } + #else + { + (void)pMMDevice; /* Unused. */ + } + #endif + + return MA_SUCCESS; +} + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) +{ + if (deviceType == ma_device_type_playback) { + return ma_eRender; + } else if (deviceType == ma_device_type_capture) { + return ma_eCapture; + } else { + MA_ASSERT(MA_FALSE); + return ma_eRender; /* Should never hit this. */ + } +} + +static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) +{ + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDeviceEnumerator != NULL); + + *ppDeviceEnumerator = NULL; /* Safety. */ + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + *ppDeviceEnumerator = pDeviceEnumerator; + + return MA_SUCCESS; +} + +static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) +{ + HRESULT hr; + ma_IMMDevice* pMMDefaultDevice = NULL; + WCHAR* pDefaultDeviceID = NULL; + ma_EDataFlow dataFlow; + ma_ERole role; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceEnumerator != NULL); + + (void)pContext; + + /* Grab the EDataFlow type from the device type. */ + dataFlow = ma_device_type_to_EDataFlow(deviceType); + + /* The role is always eConsole, but we may make this configurable later. */ + role = ma_eConsole; + + hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); + if (FAILED(hr)) { + return NULL; + } + + hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); + + ma_IMMDevice_Release(pMMDefaultDevice); + pMMDefaultDevice = NULL; + + if (FAILED(hr)) { + return NULL; + } + + return pDefaultDeviceID; +} + +static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ +{ + ma_result result; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + WCHAR* pDefaultDeviceID = NULL; + + MA_ASSERT(pContext != NULL); + + result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); + if (result != MA_SUCCESS) { + return NULL; + } + + pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + return pDefaultDeviceID; +} + +static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) +{ + ma_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppMMDevice != NULL); + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); + return ma_result_from_HRESULT(hr); + } + + if (pDeviceID == NULL) { + hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); + } else { + hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); + } + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); + return ma_result_from_HRESULT(hr); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) +{ + WCHAR* pDeviceIDString; + HRESULT hr; + + MA_ASSERT(pDeviceID != NULL); + + hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); + if (SUCCEEDED(hr)) { + size_t idlen = ma_strlen_WCHAR(pDeviceIDString); + if (idlen+1 > ma_countof(pDeviceID->wasapi)) { + ma_CoTaskMemFree(pContext, pDeviceIDString); + MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ + return MA_ERROR; + } + + MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); + pDeviceID->wasapi[idlen] = '\0'; + + ma_CoTaskMemFree(pContext, pDeviceIDString); + + return MA_SUCCESS; + } + + return MA_ERROR; +} + +static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pMMDevice != NULL); + MA_ASSERT(pInfo != NULL); + + /* ID. */ + result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); + if (result == MA_SUCCESS) { + if (pDefaultDeviceID != NULL) { + if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { + pInfo->isDefault = MA_TRUE; + } + } + } + + /* Description / Friendly Name */ + { + ma_IPropertyStore *pProperties; + hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT var; + + ma_PropVariantInit(&var); + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); + ma_PropVariantClear(pContext, &var); + } + + ma_IPropertyStore_Release(pProperties); + } + } + + /* Format */ + if (!onlySimpleInfo) { + ma_IAudioClient* pAudioClient; + hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + if (SUCCEEDED(hr)) { + result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); + + ma_IAudioClient_Release(pAudioClient); + return result; + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); + return ma_result_from_HRESULT(hr); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result = MA_SUCCESS; + UINT deviceCount; + HRESULT hr; + ma_uint32 iDevice; + WCHAR* pDefaultDeviceID = NULL; + ma_IMMDeviceCollection* pDeviceCollection = NULL; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ + pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); + + /* We need to enumerate the devices which returns a device collection. */ + hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); + if (SUCCEEDED(hr)) { + hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); + result = ma_result_from_HRESULT(hr); + goto done; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + ma_IMMDevice* pMMDevice; + + MA_ZERO_OBJECT(&deviceInfo); + + hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); + if (SUCCEEDED(hr)) { + result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ + + ma_IMMDevice_Release(pMMDevice); + if (result == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + break; + } + } + } + } + } + +done: + if (pDefaultDeviceID != NULL) { + ma_CoTaskMemFree(pContext, pDefaultDeviceID); + pDefaultDeviceID = NULL; + } + + if (pDeviceCollection != NULL) { + ma_IMMDeviceCollection_Release(pDeviceCollection); + pDeviceCollection = NULL; + } + + return result; +} + +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppAudioClient != NULL); + MA_ASSERT(ppMMDevice != NULL); + + result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); + if (result != MA_SUCCESS) { + return result; + } + + hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + return MA_SUCCESS; +} +#else +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +{ + ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; + ma_completion_handler_uwp completionHandler; + IID iid; + WCHAR* iidStr; + HRESULT hr; + ma_result result; + HRESULT activateResult; + ma_IUnknown* pActivatedInterface; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppAudioClient != NULL); + + if (pDeviceID != NULL) { + iidStr = (WCHAR*)pDeviceID->wasapi; + } else { + if (deviceType == ma_device_type_capture) { + iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; + } else { + iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; + } + + #if defined(__cplusplus) + hr = StringFromIID(iid, &iidStr); + #else + hr = StringFromIID(&iid, &iidStr); + #endif + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); + return ma_result_from_HRESULT(hr); + } + } + + result = ma_completion_handler_uwp_init(&completionHandler); + if (result != MA_SUCCESS) { + ma_CoTaskMemFree(pContext, iidStr); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); + return result; + } + + hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); + if (FAILED(hr)) { + ma_completion_handler_uwp_uninit(&completionHandler); + ma_CoTaskMemFree(pContext, iidStr); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); + return ma_result_from_HRESULT(hr); + } + + if (pDeviceID == NULL) { + ma_CoTaskMemFree(pContext, iidStr); + } + + /* Wait for the async operation for finish. */ + ma_completion_handler_uwp_wait(&completionHandler); + ma_completion_handler_uwp_uninit(&completionHandler); + + hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); + + if (FAILED(hr) || FAILED(activateResult)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); + return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); + } + + /* Here is where we grab the IAudioClient interface. */ + hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); + return ma_result_from_HRESULT(hr); + } + + if (ppActivatedInterface) { + *ppActivatedInterface = pActivatedInterface; + } else { + ma_IUnknown_Release(pActivatedInterface); + } + + return MA_SUCCESS; +} +#endif + + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ +typedef enum +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, + MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK +} MA_AUDIOCLIENT_ACTIVATION_TYPE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ +typedef enum +{ + MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, + MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE +} MA_PROCESS_LOOPBACK_MODE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ +typedef struct +{ + DWORD TargetProcessId; + MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; +} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ +typedef struct +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; + union + { + MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; + }; +} MA_AUDIOCLIENT_ACTIVATION_PARAMS; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" + +static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) +{ + ma_result result; + ma_bool32 usingProcessLoopback = MA_FALSE; + MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; + MA_PROPVARIANT activationParams; + MA_PROPVARIANT* pActivationParams = NULL; + ma_device_id virtualDeviceID; + + /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ + if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { + usingProcessLoopback = MA_TRUE; + } + + if (usingProcessLoopback) { + MA_ZERO_OBJECT(&audioclientActivationParams); + audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; + audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; + + ma_PropVariantInit(&activationParams); + activationParams.vt = MA_VT_BLOB; + activationParams.blob.cbSize = sizeof(audioclientActivationParams); + activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; + pActivationParams = &activationParams; + + /* When requesting a specific device ID we need to use a special device ID. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + pDeviceID = &virtualDeviceID; + } else { + pActivationParams = NULL; /* No activation parameters required. */ + } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#else + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#endif + + /* + If loopback mode was requested with a process ID and initialization failed, it could be because it's + trying to run on an older version of Windows where it's not supported. We need to let the caller + know about this with a log message. + */ + if (result != MA_SUCCESS) { + if (usingProcessLoopback) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); + } + } + + return result; +} + + +static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + /* Different enumeration for desktop and UWP. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* Desktop */ + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); + ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); +#else + /* + UWP + + The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate + over devices without using MMDevice, I'm restricting devices to defaults. + + Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ + */ + if (callback) { + ma_bool32 cbResult = MA_TRUE; + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + ma_result result; + ma_IMMDevice* pMMDevice = NULL; + WCHAR* pDefaultDeviceID = NULL; + + result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); + if (result != MA_SUCCESS) { + return result; + } + + /* We need the default device ID so we can set the isDefault flag in the device info. */ + pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); + + result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ + + if (pDefaultDeviceID != NULL) { + ma_CoTaskMemFree(pContext, pDefaultDeviceID); + pDefaultDeviceID = NULL; + } + + ma_IMMDevice_Release(pMMDevice); + + return result; +#else + ma_IAudioClient* pAudioClient; + ma_result result; + + /* UWP currently only uses default devices. */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); + + pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ + + ma_IAudioClient_Release(pAudioClient); + return result; +#endif +} + +static ma_result ma_device_uninit__wasapi(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pDevice->wasapi.pDeviceEnumerator) { + ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + } +#endif + + if (pDevice->wasapi.pRenderClient) { + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + } + if (pDevice->wasapi.pCaptureClient) { + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + } + + if (pDevice->wasapi.pAudioClientPlayback) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + } + if (pDevice->wasapi.pAudioClientCapture) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + } + + if (pDevice->wasapi.hEventPlayback) { + CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); + } + if (pDevice->wasapi.hEventCapture) { + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + } + + return MA_SUCCESS; +} + + +typedef struct +{ + /* Input. */ + ma_format formatIn; + ma_uint32 channelsIn; + ma_uint32 sampleRateIn; + ma_channel channelMapIn[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesIn; + ma_uint32 periodSizeInMillisecondsIn; + ma_uint32 periodsIn; + ma_share_mode shareMode; + ma_performance_profile performanceProfile; + ma_bool32 noAutoConvertSRC; + ma_bool32 noDefaultQualitySRC; + ma_bool32 noHardwareOffloading; + ma_uint32 loopbackProcessID; + ma_bool32 loopbackProcessExclude; + + /* Output. */ + ma_IAudioClient* pAudioClient; + ma_IAudioRenderClient* pRenderClient; + ma_IAudioCaptureClient* pCaptureClient; + ma_format formatOut; + ma_uint32 channelsOut; + ma_uint32 sampleRateOut; + ma_channel channelMapOut[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesOut; + ma_uint32 periodsOut; + ma_bool32 usingAudioClient3; + char deviceName[256]; + ma_device_id id; +} ma_device_init_internal_data__wasapi; + +static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) +{ + HRESULT hr; + ma_result result = MA_SUCCESS; + const char* errorMsg = ""; + MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; + DWORD streamFlags = 0; + MA_REFERENCE_TIME periodDurationInMicroseconds; + ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; + MA_WAVEFORMATEXTENSIBLE wf; + ma_WASAPIDeviceInterface* pDeviceInterface = NULL; + ma_IAudioClient2* pAudioClient2; + ma_uint32 nativeSampleRate; + ma_bool32 usingProcessLoopback = MA_FALSE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pData != NULL); + + /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; + + pData->pAudioClient = NULL; + pData->pRenderClient = NULL; + pData->pCaptureClient = NULL; + + streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ + streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; + } + if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { + streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + } + if (deviceType == ma_device_type_loopback) { + streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; + } + + result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); + if (result != MA_SUCCESS) { + goto done; + } + + MA_ZERO_OBJECT(&wf); + + /* Try enabling hardware offloading. */ + if (!pData->noHardwareOffloading) { + hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); + if (SUCCEEDED(hr)) { + BOOL isHardwareOffloadingSupported = 0; + hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); + if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { + ma_AudioClientProperties clientProperties; + MA_ZERO_OBJECT(&clientProperties); + clientProperties.cbSize = sizeof(clientProperties); + clientProperties.bIsOffload = 1; + clientProperties.eCategory = MA_AudioCategory_Other; + ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); + } + + pAudioClient2->lpVtbl->Release(pAudioClient2); + } + } + + /* 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 = MA_FORMAT_NOT_SUPPORTED; + if (pData->shareMode == ma_share_mode_exclusive) { + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* In exclusive mode on desktop we always use the backend's native format. */ + ma_IPropertyStore* pStore = NULL; + hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT prop; + ma_PropVariantInit(&prop); + hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); + if (SUCCEEDED(hr)) { + MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); + if (SUCCEEDED(hr)) { + MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } + + ma_PropVariantClear(pContext, &prop); + } + + ma_IPropertyStore_Release(pStore); + } + #else + /* + 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) { + shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; + result = MA_SUCCESS; + } else { + result = MA_SHARE_MODE_NOT_SUPPORTED; + } + } else { + /* In shared mode we are always using the format reported by the operating system. */ + MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); + if (hr != S_OK) { + /* When using process-specific loopback, GetMixFormat() seems to always fail. */ + if (usingProcessLoopback) { + wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + wf.nChannels = 2; + wf.nSamplesPerSec = 44100; + wf.wBitsPerSample = 32; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = sizeof(MA_WAVEFORMATEX); + + result = MA_SUCCESS; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } + } else { + /* + I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself + is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE + want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be + safe and only copy the WAVEFORMATEX part. + */ + if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } else { + /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ + size_t cbSize = pNativeFormat->cbSize; + if (cbSize == 0) { + cbSize = sizeof(MA_WAVEFORMATEX); + } + + /* Make sure we don't copy more than the capacity of `wf`. */ + if (cbSize > sizeof(wf)) { + cbSize = sizeof(wf); + } + + MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); + } + + result = MA_SUCCESS; + } + + ma_CoTaskMemFree(pContext, pNativeFormat); + + shareMode = MA_AUDCLNT_SHAREMODE_SHARED; + } + + /* Return an error if we still haven't found a format. */ + if (result != MA_SUCCESS) { + errorMsg = "[WASAPI] Failed to find best device mix format."; + goto done; + } + + /* + Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use + WASAPI to perform the sample rate conversion. + */ + nativeSampleRate = wf.nSamplesPerSec; + if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { + wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + } + + pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); + if (pData->formatOut == ma_format_unknown) { + /* + The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED + in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for + completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. + */ + if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { + result = MA_SHARE_MODE_NOT_SUPPORTED; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } + + errorMsg = "[WASAPI] Native format not supported."; + goto done; + } + + pData->channelsOut = wf.nChannels; + pData->sampleRateOut = wf.nSamplesPerSec; + + /* + Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns + a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this + case we'll just use the default channel map. + */ + if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + } + + /* Period size. */ + pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; + pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; + if (pData->periodSizeInFramesOut == 0) { + if (pData->periodSizeInMillisecondsIn == 0) { + if (pData->performanceProfile == ma_performance_profile_low_latency) { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); + } + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); + } + } + + periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; + + + /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ + if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { + MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; + + /* + If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing + it and trying it again. + */ + hr = E_FAIL; + for (;;) { + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); + if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { + if (bufferDuration > 500*10000) { + break; + } else { + if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ + break; + } + + bufferDuration = bufferDuration * 2; + continue; + } + } else { + break; + } + } + + if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { + ma_uint32 bufferSizeInFrames; + hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); + if (SUCCEEDED(hr)) { + bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.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?! */ + ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); + + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); + #else + hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); + #endif + + if (SUCCEEDED(hr)) { + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); + } + } + } + + if (FAILED(hr)) { + /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; + } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); + } + goto done; + } + } + + if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { + /* + Low latency shared mode via IAudioClient3. + + NOTE + ==== + Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the + use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using + any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to + that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. + */ + #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE + { + if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { + ma_IAudioClient3* pAudioClient3 = NULL; + hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); + if (SUCCEEDED(hr)) { + ma_uint32 defaultPeriodInFrames; + ma_uint32 fundamentalPeriodInFrames; + ma_uint32 minPeriodInFrames; + ma_uint32 maxPeriodInFrames; + hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + if (SUCCEEDED(hr)) { + ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; + ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; + + /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ + actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; + actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; + + /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ + actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); + + /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ + if (actualPeriodInFrames >= desiredPeriodInFrames) { + /* + MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, + IAudioClient3_InitializeSharedAudioStream() will fail. + */ + hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + wasInitializedUsingIAudioClient3 = MA_TRUE; + pData->periodSizeInFramesOut = actualPeriodInFrames; + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); + } + + ma_IAudioClient3_Release(pAudioClient3); + pAudioClient3 = NULL; + } + } + } + #else + { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); + } + #endif + + /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ + if (!wasInitializedUsingIAudioClient3) { + MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); + if (FAILED(hr)) { + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; + } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); + } + + goto done; + } + } + } + + if (!wasInitializedUsingIAudioClient3) { + ma_uint32 bufferSizeInFrames = 0; + hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); + goto done; + } + + /* + When using process loopback mode, retrieval of the buffer size seems to result in totally + incorrect values. In this case we'll just assume it's the same size as what we requested + when we initialized the client. + */ + if (usingProcessLoopback) { + bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); + } + + pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; + } + + pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; + + + if (deviceType == ma_device_type_playback) { + result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); + } else { + result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); + } + + /*if (FAILED(hr)) {*/ + if (result != MA_SUCCESS) { + errorMsg = "[WASAPI] Failed to get audio client service."; + goto done; + } + + + /* Grab the name of the device. */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + ma_IPropertyStore *pProperties; + hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT varName; + ma_PropVariantInit(&varName); + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); + ma_PropVariantClear(pContext, &varName); + } + + ma_IPropertyStore_Release(pProperties); + } + } + #endif + + /* + For the WASAPI backend we need to know the actual IDs of the device in order to do automatic + stream routing so that IDs can be compared and we can determine which device has been detached + and whether or not it matches with our ma_device. + */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + /* Desktop */ + ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); + } + #else + { + /* UWP */ + /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ + } + #endif + +done: + /* Clean up. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pDeviceInterface != NULL) { + ma_IMMDevice_Release(pDeviceInterface); + } +#else + if (pDeviceInterface != NULL) { + ma_IUnknown_Release(pDeviceInterface); + } +#endif + + if (result != MA_SUCCESS) { + if (pData->pRenderClient) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); + pData->pRenderClient = NULL; + } + if (pData->pCaptureClient) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); + pData->pCaptureClient = NULL; + } + if (pData->pAudioClient) { + ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); + pData->pAudioClient = NULL; + } + + if (errorMsg != NULL && errorMsg[0] != '\0') { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); + } + + return result; + } else { + return MA_SUCCESS; + } +} + +static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_device_init_internal_data__wasapi data; + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* We only re-initialize the playback or capture device. Never a full-duplex device. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + + /* + Before reinitializing the device we need to free the previous audio clients. + + There's a known memory leak here. We will be calling this from the routing change callback that + is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion + this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably + need some system where we post an event, but delay the execution of it until the callback has + returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for + a command thread which might be useful for this. + */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + if (pDevice->wasapi.pCaptureClient) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + + if (pDevice->wasapi.pAudioClientCapture) { + /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ + pDevice->wasapi.pAudioClientCapture = NULL; + } + } + + if (deviceType == ma_device_type_playback) { + if (pDevice->wasapi.pRenderClient) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + + if (pDevice->wasapi.pAudioClientPlayback) { + /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ + pDevice->wasapi.pAudioClientPlayback = NULL; + } + } + + + if (deviceType == ma_device_type_playback) { + data.formatIn = pDevice->playback.format; + data.channelsIn = pDevice->playback.channels; + MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); + data.shareMode = pDevice->playback.shareMode; + } else { + data.formatIn = pDevice->capture.format; + data.channelsIn = pDevice->capture.channels; + MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); + data.shareMode = pDevice->capture.shareMode; + } + + data.sampleRateIn = pDevice->sampleRate; + data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; + data.periodsIn = pDevice->wasapi.originalPeriods; + data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; + data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; + data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; + result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); + if (result != MA_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 (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + pDevice->wasapi.pAudioClientCapture = data.pAudioClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->capture.internalFormat = data.formatOut; + pDevice->capture.internalChannels = data.channelsOut; + pDevice->capture.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->capture.internalPeriods = data.periodsOut; + ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); + + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + + pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + + /* We must always have a valid ID. */ + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + } + + if (deviceType == ma_device_type_playback) { + pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + + pDevice->playback.internalFormat = data.formatOut; + pDevice->playback.internalChannels = data.channelsOut; + pDevice->playback.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->playback.internalPeriods = data.periodsOut; + ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); + + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + + pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + + /* We must always have a valid ID because rerouting will look at it. */ + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result = MA_SUCCESS; + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; +#endif + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->wasapi); + pDevice->wasapi.usage = pConfig->wasapi.usage; + pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + /* Exclusive mode is not allowed with loopback. */ + if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { + return MA_INVALID_DEVICE_CONFIG; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + ma_device_init_internal_data__wasapi data; + data.formatIn = pDescriptorCapture->format; + data.channelsIn = pDescriptorCapture->channels; + data.sampleRateIn = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; + data.periodsIn = pDescriptorCapture->periodCount; + data.shareMode = pDescriptorCapture->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); + if (result != MA_SUCCESS) { + return result; + } + + pDevice->wasapi.pAudioClientCapture = data.pAudioClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + + /* + The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, + however, because we want to block until we actually have something for the first call to ma_device_read(). + */ + pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + if (pDevice->wasapi.hEventCapture == NULL) { + result = ma_result_from_GetLastError(GetLastError()); + + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); + return result; + } + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + + pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + + /* We must always have a valid ID. */ + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + + /* The descriptor needs to be updated with actual values. */ + pDescriptorCapture->format = data.formatOut; + pDescriptorCapture->channels = data.channelsOut; + pDescriptorCapture->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorCapture->periodCount = data.periodsOut; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__wasapi data; + data.formatIn = pDescriptorPlayback->format; + data.channelsIn = pDescriptorPlayback->channels; + data.sampleRateIn = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; + data.periodsIn = pDescriptorPlayback->periodCount; + data.shareMode = pDescriptorPlayback->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + pDevice->wasapi.hEventCapture = NULL; + } + return result; + } + + pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + + /* + The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled + only after the whole available space has been filled, never before. + + The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able + to get passed WaitForMultipleObjects(). + */ + pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + if (pDevice->wasapi.hEventPlayback == NULL) { + result = ma_result_from_GetLastError(GetLastError()); + + if (pConfig->deviceType == ma_device_type_duplex) { + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + pDevice->wasapi.hEventCapture = NULL; + } + + if (pDevice->wasapi.pRenderClient != NULL) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + if (pDevice->wasapi.pAudioClientPlayback != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + pDevice->wasapi.pAudioClientPlayback = NULL; + } + + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); + return result; + } + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + + pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + + /* We must always have a valid ID because rerouting will look at it. */ + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + + /* The descriptor needs to be updated with actual values. */ + pDescriptorPlayback->format = data.formatOut; + pDescriptorPlayback->channels = data.channelsOut; + pDescriptorPlayback->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorPlayback->periodCount = data.periodsOut; + } + + /* + We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When + we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just + stop the device outright and let the application handle it. + */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { + if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { + pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; + } + if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { + pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; + } + } + + ma_mutex_init(&pDevice->wasapi.rerouteLock); + + hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_device_uninit__wasapi(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; + pDevice->wasapi.notificationClient.counter = 1; + pDevice->wasapi.notificationClient.pDevice = pDevice; + + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + if (SUCCEEDED(hr)) { + pDevice->wasapi.pDeviceEnumerator = (ma_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. */ + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + } +#endif + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + + return MA_SUCCESS; +} + +static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) +{ + ma_uint32 paddingFramesCount; + HRESULT hr; + ma_share_mode shareMode; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFrameCount != NULL); + + *pFrameCount = 0; + + if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { + return MA_INVALID_OPERATION; + } + + /* + I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing + higher level function calls from doing anything because it thinks nothing is available. I have + taken a look at the documentation and it looks like this is unnecessary in exclusive mode. + + From Microsoft's documentation: + + For an exclusive-mode rendering or capture stream that was initialized with the + AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding + value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during + each processing pass. + + Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the + entire buffer. This depends on the caller making sure they wait on the event handler. + */ + shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; + if (shareMode == ma_share_mode_shared) { + /* Shared mode. */ + hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; + } else { + *pFrameCount = paddingFramesCount; + } + } else { + /* Exclusive mode. */ + if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; + } else { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); + + result = ma_device_reinit__wasapi(pDevice, deviceType); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); + return result; + } + + ma_device__post_init_setup(pDevice, deviceType); + ma_device__on_notification_rerouted(pDevice); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); + + return MA_SUCCESS; +} + +static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) +{ + HRESULT hr; + + if (pDevice->pContext->wasapi.hAvrt) { + const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pTaskName) { + DWORD idx = 0; + pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); + } + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); + return ma_result_from_HRESULT(hr); + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); + return ma_result_from_HRESULT(hr); + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to start the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_start__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + +static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->wasapi.hAvrtHandle) { + ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); + pDevice->wasapi.hAvrtHandle = NULL; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); + return ma_result_from_HRESULT(hr); + } + + /* The audio client needs to be reset otherwise restarting will fail. */ + hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); + return ma_result_from_HRESULT(hr); + } + + /* If we have a mapped buffer we need to release it. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to + the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. + */ + if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { + /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ + DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; + + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); + } + else { + ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; + ma_uint32 framesAvailablePlayback; + for (;;) { + result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); + if (result != MA_SUCCESS) { + break; + } + + if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { + break; + } + + /* + Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames + has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. + */ + if (framesAvailablePlayback == prevFramesAvaialablePlayback) { + break; + } + prevFramesAvaialablePlayback = framesAvailablePlayback; + + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); + ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ + } + } + } + + hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); + return ma_result_from_HRESULT(hr); + } + + /* The audio client needs to be reset otherwise restarting will fail. */ + hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); + return ma_result_from_HRESULT(hr); + } + + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to stop the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_stop__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + + +#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS +#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 +#endif + +static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; + + /* + When reading, we need to get a buffer and process all of it before releasing it. Because the + frame count (frameCount) can be different to the size of the buffer, we'll need to cache the + pointer to the buffer. + */ + + /* Keep running until we've processed the requested number of frames. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; + + /* If we have a mapped data buffer, consume that first. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { + framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; + } + + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + framesToProcessNow, + pDevice->capture.internalFormat, pDevice->capture.internalChannels + ); + + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; + + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferCaptureLen == 0) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + } + } else { + /* We don't have any cached data pointer, so grab another one. */ + HRESULT hr; + DWORD flags = 0; + + /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == S_OK) { + /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + + /* + There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every + call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially + work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution + would be to figure out why the flag is always getting reported. + */ + #if defined(MA_DEBUG_OUTPUT) + { + if (flags != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); + } + } + } + #endif + + /* Overrun detection. */ + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + /* Glitched. Probably due to an overrun. */ + + /* + If we got an overrun it probably means we're straddling the end of the buffer. In normal capture + mode this is the fault of the client application because they're responsible for ensuring data is + processed fast enough. In duplex mode, however, the processing of audio is tied to the playback + device, so this can possibly be the result of a timing de-sync. + + In capture mode we're not going to do any kind of recovery because the real fix is for the client + application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers + to prevent a never-ending sequence of glitches due to straddling the end of the buffer. + */ + if (pDevice->type == ma_device_type_duplex) { + /* + Experiment: + + If we empty out the *entire* buffer we may end up putting ourselves into an underrun position + which isn't really any better than the overrun we're probably in right now. Instead we'll just + empty out about half. + */ + ma_uint32 i; + ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); + ma_uint32 iterationCount = periodCount / 2; + if ((periodCount % 2) > 0) { + iterationCount += 1; + } + + for (i = 0; i < iterationCount; i += 1) { + hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); + break; + } + + flags = 0; + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { + /* + The buffer has been completely emptied or an error occurred. In this case we'll need + to reset the state of the mapped buffer which will trigger the next iteration to get + a fresh buffer from WASAPI. + */ + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); + } + } + + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); + } + + break; + } + } + + /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + } + } + } + + continue; + } else { + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* + No data is available. We need to wait for more. There's two situations to consider + here. The first is normal capture mode. If this times out it probably means the + microphone isn't delivering data for whatever reason. In this case we'll just + abort the read and return whatever we were able to get. The other situations is + loopback mode, in which case a timeout probably just means the nothing is playing + through the speakers. + */ + + /* Experiment: Use a shorter timeout for loopback mode. */ + DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; + if (pDevice->type == ma_device_type_loopback) { + timeoutInMilliseconds = 10; + } + + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { + if (pDevice->type == ma_device_type_loopback) { + continue; /* Keep waiting in loopback mode. */ + } else { + result = MA_ERROR; + break; /* Wait failed. */ + } + } + + /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ + } else { + /* An error occurred and we need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); + break; + } + } + } + } + + /* + If we were unable to process the entire requested frame count, but we still have a mapped buffer, + there's a good chance either an error occurred or the device was stopped mid-read. In this case + we'll need to make sure the buffer is released. + */ + if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + return result; +} + +static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; + + /* Keep writing to the device until it's stopped or we've consumed all of our input. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; + + /* + We're going to do this in a similar way to capture. We'll first check if the cached data pointer + is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with + a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE + it means we need to wait for some data to become available. + */ + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + /* We still have some space available in the mapped data buffer. Write to it. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { + framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); + } + + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + framesToProcessNow, + pDevice->playback.internalFormat, pDevice->playback.internalChannels + ); + + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; + + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + + /* + In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never + seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine + whether or not we need to wait for more data. + */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } + } + } else { + /* We don't have a mapped data buffer so we'll need to get one. */ + HRESULT hr; + ma_uint32 bufferSizeInFrames; + + /* Special rules for exclusive mode. */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; + } else { + bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; + } + + hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); + if (hr == S_OK) { + /* We have data available. */ + pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } else { + if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* Not enough data available. We need to wait for more. */ + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } else { + /* Some error occurred. We'll need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); + break; + } + } + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalFramesProcessed; + } + + return result; +} + +static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + SetEvent((HANDLE)pDevice->wasapi.hEventCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__wasapi(ma_context* pContext) +{ + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_wasapi); + + ma_context_post_command__wasapi(pContext, &cmd); + ma_thread_wait(&pContext->wasapi.commandThread); + + if (pContext->wasapi.hAvrt) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result = MA_SUCCESS; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + +#ifdef MA_WIN32_DESKTOP + /* + WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven + exclusive mode does not work until SP1. + + Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. + */ + { + ma_OSVERSIONINFOEXW osvi; + ma_handle kernel32DLL; + ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; + ma_PFNVerSetConditionMask _VerSetConditionMask; + + kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); + if (kernel32DLL == NULL) { + return MA_NO_BACKEND; + } + + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); + if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + return MA_NO_BACKEND; + } + + MA_ZERO_OBJECT(&osvi); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); + osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); + osvi.wServicePackMajor = 1; + if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { + result = MA_SUCCESS; + } else { + result = MA_NO_BACKEND; + } + + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + } +#endif + + if (result != MA_SUCCESS) { + return result; + } + + MA_ZERO_OBJECT(&pContext->wasapi); + + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); + if (pContext->wasapi.hMMDevapi) { + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); + return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ + } + } else { + return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } + + + /* + Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread + than the one that retrieved it with GetService(). This can result in a deadlock in two + situations: + + 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and + 2) When uninitializing and reinitializing the internal IAudioClient object in response to + automatic stream routing. + + We could define ma_device_uninit() such that it must be called on the same thread as + ma_device_init(). We could also just not release the IAudioClient when performing automatic + stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so + we're going to have to work around this with a worker thread. This is not ideal, but I can't + think of a better way to do this. + + More information about this can be found here: + + https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient + + Note this section: + + When releasing an IAudioRenderClient interface instance, the client must call the interface's + Release method from the same thread as the call to IAudioClient::GetService that created the + object. + */ + { + result = ma_mutex_init(&pContext->wasapi.commandLock); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_semaphore_init(0, &pContext->wasapi.commandSem); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pContext->wasapi.commandLock); + return result; + } + + result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return result; + } + } + + + pCallbacks->onContextInit = ma_context_init__wasapi; + pCallbacks->onContextUninit = ma_context_uninit__wasapi; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; + pCallbacks->onDeviceInit = ma_device_init__wasapi; + pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; + pCallbacks->onDeviceStart = ma_device_start__wasapi; + pCallbacks->onDeviceStop = ma_device_stop__wasapi; + pCallbacks->onDeviceRead = ma_device_read__wasapi; + pCallbacks->onDeviceWrite = ma_device_write__wasapi; + pCallbacks->onDeviceDataLoop = NULL; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; + + return MA_SUCCESS; +} +#endif + +/****************************************************************************** + +DirectSound Backend + +******************************************************************************/ +#ifdef MA_HAS_DSOUND +/*#include */ + +/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ + +/* miniaudio only uses priority or exclusive modes. */ +#define MA_DSSCL_NORMAL 1 +#define MA_DSSCL_PRIORITY 2 +#define MA_DSSCL_EXCLUSIVE 3 +#define MA_DSSCL_WRITEPRIMARY 4 + +#define MA_DSCAPS_PRIMARYMONO 0x00000001 +#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 +#define MA_DSCAPS_PRIMARY8BIT 0x00000004 +#define MA_DSCAPS_PRIMARY16BIT 0x00000008 +#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 +#define MA_DSCAPS_EMULDRIVER 0x00000020 +#define MA_DSCAPS_CERTIFIED 0x00000040 +#define MA_DSCAPS_SECONDARYMONO 0x00000100 +#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 +#define MA_DSCAPS_SECONDARY8BIT 0x00000400 +#define MA_DSCAPS_SECONDARY16BIT 0x00000800 + +#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 +#define MA_DSBCAPS_STATIC 0x00000002 +#define MA_DSBCAPS_LOCHARDWARE 0x00000004 +#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 +#define MA_DSBCAPS_CTRL3D 0x00000010 +#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 +#define MA_DSBCAPS_CTRLPAN 0x00000040 +#define MA_DSBCAPS_CTRLVOLUME 0x00000080 +#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define MA_DSBCAPS_CTRLFX 0x00000200 +#define MA_DSBCAPS_STICKYFOCUS 0x00004000 +#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 +#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 +#define MA_DSBCAPS_LOCDEFER 0x00040000 +#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 + +#define MA_DSBPLAY_LOOPING 0x00000001 +#define MA_DSBPLAY_LOCHARDWARE 0x00000002 +#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 +#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 +#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 +#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 + +#define MA_DSCBSTART_LOOPING 0x00000001 + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + MA_WAVEFORMATEX* lpwfxFormat; + GUID guid3DAlgorithm; +} MA_DSBUFFERDESC; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + MA_WAVEFORMATEX* lpwfxFormat; + DWORD dwFXCount; + void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ +} MA_DSCBUFFERDESC; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} MA_DSCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} MA_DSBCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFormats; + DWORD dwChannels; +} MA_DSCCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; +} MA_DSCBCAPS; + +typedef struct +{ + DWORD dwOffset; + HANDLE hEventNotify; +} MA_DSBPOSITIONNOTIFY; + +typedef struct ma_IDirectSound ma_IDirectSound; +typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; +typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; +typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; +typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; + + +/* +COM objects. The way these work is that you have a vtable (a list of function pointers, kind of +like how C++ works internally), and then you have a structure with a single member, which is a +pointer to the vtable. The vtable is where the methods of the object are defined. Methods need +to be in a specific order, and parent classes need to have their methods declared first. +*/ + +/* IDirectSound */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); + + /* IDirectSound */ + HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); + HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); + HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); + HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); + HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); +} ma_IDirectSoundVtbl; +struct ma_IDirectSound +{ + ma_IDirectSoundVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } +static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } +static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } +static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } +static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } +static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } +static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } +static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +/* IDirectSoundBuffer */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); + + /* IDirectSoundBuffer */ + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); + HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); + HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); + HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); + HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); + HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); + HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); + HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); + HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); +} ma_IDirectSoundBufferVtbl; +struct ma_IDirectSoundBuffer +{ + ma_IDirectSoundBufferVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } + + +/* IDirectSoundCapture */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); + + /* IDirectSoundCapture */ + HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); +} ma_IDirectSoundCaptureVtbl; +struct ma_IDirectSoundCapture +{ + ma_IDirectSoundCaptureVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +/* IDirectSoundCaptureBuffer */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); + + /* IDirectSoundCaptureBuffer */ + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); +} ma_IDirectSoundCaptureBufferVtbl; +struct ma_IDirectSoundCaptureBuffer +{ + ma_IDirectSoundCaptureBufferVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } + + +/* IDirectSoundNotify */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); + + /* IDirectSoundNotify */ + HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); +} ma_IDirectSoundNotifyVtbl; +struct ma_IDirectSoundNotify +{ + ma_IDirectSoundNotifyVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } + + +typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); + +static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) +{ + /* Normalize the range in case we were given something stupid. */ + if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { + sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; + } + if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { + sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; + } + if (sampleRateMin > sampleRateMax) { + sampleRateMin = sampleRateMax; + } + + if (sampleRateMin == sampleRateMax) { + return sampleRateMax; + } else { + size_t iStandardRate; + for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; + if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { + return standardRate; + } + } + } + + /* Should never get here. */ + MA_ASSERT(MA_FALSE); + return 0; +} + +/* +Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, +the channel count and channel map will be left unmodified. +*/ +static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) +{ + WORD channels; + DWORD channelMap; + + channels = 0; + if (pChannelsOut != NULL) { + channels = *pChannelsOut; + } + + channelMap = 0; + if (pChannelMapOut != NULL) { + channelMap = *pChannelMapOut; + } + + /* + The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper + 16 bits is for the geometry. + */ + switch ((BYTE)(speakerConfig)) { + case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; + case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; + case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; + case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + default: break; + } + + if (pChannelsOut != NULL) { + *pChannelsOut = channels; + } + + if (pChannelMapOut != NULL) { + *pChannelMapOut = channelMap; + } +} + + +static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) +{ + ma_IDirectSound* pDirectSound; + HWND hWnd; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDirectSound != NULL); + + *ppDirectSound = NULL; + pDirectSound = NULL; + + if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* The cooperative level must be set before doing anything else. */ + hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + if (hWnd == 0) { + hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + } + + hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + *ppDirectSound = pDirectSound; + return MA_SUCCESS; +} + +static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) +{ + ma_IDirectSoundCapture* pDirectSoundCapture; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDirectSoundCapture != NULL); + + /* DirectSound does not support exclusive mode for capture. */ + if (shareMode == ma_share_mode_exclusive) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + *ppDirectSoundCapture = NULL; + pDirectSoundCapture = NULL; + + hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + *ppDirectSoundCapture = pDirectSoundCapture; + return MA_SUCCESS; +} + +static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) +{ + HRESULT hr; + MA_DSCCAPS caps; + WORD bitsPerSample; + DWORD sampleRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDirectSoundCapture != NULL); + + if (pChannels) { + *pChannels = 0; + } + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } + + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + if (pChannels) { + *pChannels = (WORD)caps.dwChannels; + } + + /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ + bitsPerSample = 16; + sampleRate = 48000; + + if (caps.dwChannels == 1) { + if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ + } + } + } else if (caps.dwChannels == 2) { + if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_context* pContext; + ma_device_type deviceType; + ma_enum_devices_callback_proc callback; + void* pUserData; + ma_bool32 terminated; +} ma_context_enumerate_devices_callback_data__dsound; + +static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) +{ + ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; + ma_device_info deviceInfo; + + (void)lpcstrModule; + + MA_ZERO_OBJECT(&deviceInfo); + + /* ID. */ + if (lpGuid != NULL) { + MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); + } else { + MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); + deviceInfo.isDefault = MA_TRUE; + } + + /* Name / Description */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); + + + /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ + MA_ASSERT(pData != NULL); + pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); + if (pData->terminated) { + return FALSE; /* Stop enumeration. */ + } else { + return TRUE; /* Continue enumeration. */ + } +} + +static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__dsound data; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + data.pContext = pContext; + data.callback = callback; + data.pUserData = pUserData; + data.terminated = MA_FALSE; + + /* Playback. */ + if (!data.terminated) { + data.deviceType = ma_device_type_playback; + ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + } + + /* Capture. */ + if (!data.terminated) { + data.deviceType = ma_device_type_capture; + ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + } + + return MA_SUCCESS; +} + + +typedef struct +{ + const ma_device_id* pDeviceID; + ma_device_info* pDeviceInfo; + ma_bool32 found; +} ma_context_get_device_info_callback_data__dsound; + +static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) +{ + ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; + MA_ASSERT(pData != NULL); + + if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { + /* Default device. */ + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->pDeviceInfo->isDefault = MA_TRUE; + pData->found = MA_TRUE; + return FALSE; /* Stop enumeration. */ + } else { + /* Not the default device. */ + if (lpGuid != NULL && pData->pDeviceID != NULL) { + if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->found = MA_TRUE; + return FALSE; /* Stop enumeration. */ + } + } + } + + (void)lpcstrModule; + return TRUE; +} + +static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + HRESULT hr; + + if (pDeviceID != NULL) { + ma_context_get_device_info_callback_data__dsound data; + + /* ID. */ + MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); + + /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ + data.pDeviceID = pDeviceID; + data.pDeviceInfo = pDeviceInfo; + data.found = MA_FALSE; + if (deviceType == ma_device_type_playback) { + ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + } else { + ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + } + + if (!data.found) { + return MA_NO_DEVICE; + } + } else { + /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ + + /* ID */ + MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; + } + + /* Retrieving detailed information is slightly different depending on the device type. */ + if (deviceType == ma_device_type_playback) { + /* Playback. */ + ma_IDirectSound* pDirectSound; + MA_DSCAPS caps; + WORD channels; + + result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); + if (result != MA_SUCCESS) { + return result; + } + + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + + /* Channels. Only a single channel count is reported for DirectSound. */ + if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { + /* It supports at least stereo, but could support more. */ + DWORD speakerConfig; + + channels = 2; + + /* Look at the speaker configuration to get a better idea on the channel count. */ + hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); + if (SUCCEEDED(hr)) { + ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); + } + } else { + /* It does not support stereo, which means we are stuck with mono. */ + channels = 1; + } + + + /* + In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel + count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio + in order to keep the size of this within reason. + */ + if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { + /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ + size_t iStandardSampleRate; + for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { + ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; + if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + } + } + } else { + /* Only a single sample rate is supported. */ + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + } + + ma_IDirectSound_Release(pDirectSound); + } else { + /* + Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture + devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just + reporting the best format. + */ + ma_IDirectSoundCapture* pDirectSoundCapture; + WORD channels; + WORD bitsPerSample; + DWORD sampleRate; + + result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); + if (result != MA_SUCCESS) { + ma_IDirectSoundCapture_Release(pDirectSoundCapture); + return result; + } + + ma_IDirectSoundCapture_Release(pDirectSoundCapture); + + /* The format is always an integer format and is based on the bits per sample. */ + if (bitsPerSample == 8) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + + pDeviceInfo->nativeDataFormats[0].channels = channels; + pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + } + + return MA_SUCCESS; +} + + + +static ma_result ma_device_uninit__dsound(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->dsound.pCaptureBuffer != NULL) { + ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + } + if (pDevice->dsound.pCapture != NULL) { + ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); + } + + if (pDevice->dsound.pPlaybackBuffer != NULL) { + ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + } + if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { + ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); + } + if (pDevice->dsound.pPlayback != NULL) { + ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) +{ + GUID subformat; + + if (format == ma_format_unknown) { + format = MA_DEFAULT_FORMAT; + } + + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + switch (format) + { + case ma_format_u8: + case ma_format_s16: + case ma_format_s24: + /*case ma_format_s24_32:*/ + case ma_format_s32: + { + subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } break; + + case ma_format_f32: + { + subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } break; + + default: + return MA_FORMAT_NOT_SUPPORTED; + } + + MA_ZERO_OBJECT(pWF); + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pWF->nChannels = (WORD)channels; + pWF->nSamplesPerSec = (DWORD)sampleRate; + pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; + pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); + pWF->SubFormat = subformat; + + return MA_SUCCESS; +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for + reliable glitch-free processing so going to use 30ms instead. + */ + ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); + ma_uint32 periodSizeInFrames; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + if (periodSizeInFrames < minPeriodSizeInFrames) { + periodSizeInFrames = minPeriodSizeInFrames; + } + + return periodSizeInFrames; +} + +static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->dsound); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* + Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize + the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using + full-duplex mode. + */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEFORMATEXTENSIBLE wf; + MA_DSCBUFFERDESC descDS; + ma_uint32 periodSizeInFrames; + ma_uint32 periodCount; + char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ + MA_WAVEFORMATEXTENSIBLE* pActualFormat; + + result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + + /* The size of the buffer must be a clean multiple of the period count. */ + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); + periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; + + MA_ZERO_OBJECT(&descDS); + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = 0; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; + hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + /* Get the _actual_ properties of the buffer. */ + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); + return ma_result_from_HRESULT(hr); + } + + /* We can now start setting the output data formats. */ + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorCapture->channels = pActualFormat->nChannels; + pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; + + /* Get the native channel map based on the channel mask. */ + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + } else { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + } + + /* + After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the + user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. + */ + if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { + descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; + ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + + hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + } + + /* DirectSound should give us a buffer exactly the size we asked for. */ + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = periodCount; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEFORMATEXTENSIBLE wf; + MA_DSBUFFERDESC descDSPrimary; + MA_DSCAPS caps; + char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ + MA_WAVEFORMATEXTENSIBLE* pActualFormat; + ma_uint32 periodSizeInFrames; + ma_uint32 periodCount; + MA_DSBUFFERDESC descDS; + WORD nativeChannelCount; + DWORD nativeChannelMask = 0; + + result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + MA_ZERO_OBJECT(&descDSPrimary); + descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); + descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; + hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + + + /* We may want to make some adjustments to the format if we are using defaults. */ + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { + DWORD speakerConfig; + + /* It supports at least stereo, but could support more. */ + nativeChannelCount = 2; + + /* Look at the speaker configuration to get a better idea on the channel count. */ + if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { + ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); + } + } else { + /* It does not support stereo, which means we are stuck with mono. */ + nativeChannelCount = 1; + nativeChannelMask = 0x00000001; + } + + if (pDescriptorPlayback->channels == 0) { + wf.nChannels = nativeChannelCount; + wf.dwChannelMask = nativeChannelMask; + } + + if (pDescriptorPlayback->sampleRate == 0) { + /* We base the sample rate on the values returned by GetCaps(). */ + if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { + wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); + } else { + wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; + } + } + + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + + /* + From MSDN: + + The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest + supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer + and compare the result with the format that was requested with the SetFormat method. + */ + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + if (FAILED(hr)) { + /* + If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have + observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a + sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will + use 44100 for the sample rate. + */ + wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.wBitsPerSample = 16; + wf.nChannels = nativeChannelCount; + wf.nSamplesPerSec = 44100; + wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + } + + /* Get the _actual_ properties of the buffer. */ + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + + /* We now have enough information to start setting some output properties. */ + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorPlayback->channels = pActualFormat->nChannels; + pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; + + /* Get the internal channel map based on the channel mask. */ + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + } else { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + } + + /* The size of the buffer must be a clean multiple of the period count. */ + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; + + /* + Meaning of dwFlags (from MSDN): + + DSBCAPS_CTRLPOSITIONNOTIFY + The buffer has position notification capability. + + DSBCAPS_GLOBALFOCUS + With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to + another application, even if the new application uses DirectSound. + + DSBCAPS_GETCURRENTPOSITION2 + In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated + sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the + application can get a more accurate play cursor. + */ + MA_ZERO_OBJECT(&descDS); + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; + hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); + return ma_result_from_HRESULT(hr); + } + + /* DirectSound should give us a buffer exactly the size we asked for. */ + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = periodCount; + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_data_loop__dsound(ma_device* pDevice) +{ + ma_result result = MA_SUCCESS; + ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + HRESULT hr; + DWORD lockOffsetInBytesCapture; + DWORD lockSizeInBytesCapture; + DWORD mappedSizeInBytesCapture; + DWORD mappedDeviceFramesProcessedCapture; + void* pMappedDeviceBufferCapture; + DWORD lockOffsetInBytesPlayback; + DWORD lockSizeInBytesPlayback; + DWORD mappedSizeInBytesPlayback; + void* pMappedDeviceBufferPlayback; + DWORD prevReadCursorInBytesCapture = 0; + DWORD prevPlayCursorInBytesPlayback = 0; + ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; + DWORD virtualWriteCursorInBytesPlayback = 0; + ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; + ma_bool32 isPlaybackDeviceStarted = MA_FALSE; + ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ + ma_uint32 waitTimeInMilliseconds = 1; + + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); + return ma_result_from_HRESULT(hr); + } + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + switch (pDevice->type) + { + case ma_device_type_duplex: + { + DWORD physicalCaptureCursorInBytes; + DWORD physicalReadCursorInBytes; + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + /* If nothing is available we just sleep for a bit and return from this iteration. */ + if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + /* + The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure + we don't return until every frame has been copied over. + */ + if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { + /* The capture position has not looped. This is the simple case. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); + } else { + /* + The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, + do it again from the start. + */ + if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { + /* Lock up to the end of the buffer. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; + } else { + /* Lock starting from the start of the buffer. */ + lockOffsetInBytesCapture = 0; + lockSizeInBytesCapture = physicalReadCursorInBytes; + } + } + + if (lockSizeInBytesCapture == 0) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + return ma_result_from_HRESULT(hr); + } + + + /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ + mappedDeviceFramesProcessedCapture = 0; + + for (;;) { /* Keep writing to the playback device. */ + ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint32 outputFramesInClientFormatCount; + ma_uint32 outputFramesInClientFormatConsumed = 0; + ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); + ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; + void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); + + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); + if (result != MA_SUCCESS) { + break; + } + + outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; + mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; + + ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); + + /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ + for (;;) { + ma_uint32 framesWrittenThisIteration; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + DWORD availableBytesPlayback; + DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ + + /* We need the physical play and write cursors. */ + if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } + + /* If there's no room available for writing we need to wait for more. */ + if (availableBytesPlayback == 0) { + /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ + if (!isPlaybackDeviceStarted) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } else { + ma_sleep(waitTimeInMilliseconds); + continue; + } + } + + + /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ + lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. Go up to the end of the buffer. */ + lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + } else { + /* Different loop iterations. Go up to the physical play cursor. */ + lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } + + hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + /* + Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent + endless glitching due to it constantly running out of data. + */ + if (isPlaybackDeviceStarted) { + DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; + if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { + silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; + if (silentPaddingInBytes > lockSizeInBytesPlayback) { + silentPaddingInBytes = lockSizeInBytesPlayback; + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); + } + } + + /* At this point we have a buffer for output. */ + if (silentPaddingInBytes > 0) { + MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); + framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; + } else { + ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); + ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; + void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); + void* pConvertedFramesOut = pMappedDeviceBufferPlayback; + + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); + if (result != MA_SUCCESS) { + break; + } + + outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; + framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; + } + + + hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; + if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { + virtualWriteCursorInBytesPlayback = 0; + virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; + } + + /* + We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds + a bit of a buffer to prevent the playback buffer from getting starved. + */ + framesWrittenToPlaybackDevice += framesWrittenThisIteration; + if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } + + if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { + break; /* We're finished with the output data.*/ + } + } + + if (clientCapturedFramesToProcess == 0) { + break; /* We just consumed every input sample. */ + } + } + + + /* At this point we're done with the mapped portion of the capture buffer. */ + hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); + } + prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); + } break; + + + + case ma_device_type_capture: + { + DWORD physicalCaptureCursorInBytes; + DWORD physicalReadCursorInBytes; + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + if (FAILED(hr)) { + return MA_ERROR; + } + + /* If the previous capture position is the same as the current position we need to wait a bit longer. */ + if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { + ma_sleep(waitTimeInMilliseconds); + continue; + } + + /* Getting here means we have capture data available. */ + if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { + /* The capture position has not looped. This is the simple case. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); + } else { + /* + The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, + do it again from the start. + */ + if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { + /* Lock up to the end of the buffer. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; + } else { + /* Lock starting from the start of the buffer. */ + lockOffsetInBytesCapture = 0; + lockSizeInBytesCapture = physicalReadCursorInBytes; + } + } + + if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + } + + if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); + } + + ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); + + hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); + } + prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; + + if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { + prevReadCursorInBytesCapture = 0; + } + } break; + + + + case ma_device_type_playback: + { + DWORD availableBytesPlayback; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + if (FAILED(hr)) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } + + /* If there's no room available for writing we need to wait for more. */ + if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { + /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ + if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } else { + ma_sleep(waitTimeInMilliseconds); + continue; + } + } + + /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ + lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. Go up to the end of the buffer. */ + lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + } else { + /* Different loop iterations. Go up to the physical play cursor. */ + lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } + + hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + /* At this point we have a buffer for output. */ + ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); + + hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; + if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { + virtualWriteCursorInBytesPlayback = 0; + virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; + } + + /* + We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds + a bit of a buffer to prevent the playback buffer from getting starved. + */ + framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; + if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } + } break; + + + default: return MA_INVALID_ARGS; /* Invalid device type. */ + } + + if (result != MA_SUCCESS) { + return result; + } + } + + /* Getting here means the device is being stopped. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ + if (isPlaybackDeviceStarted) { + for (;;) { + DWORD availableBytesPlayback = 0; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + if (FAILED(hr)) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + break; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + break; + } + } + + if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { + break; + } + + ma_sleep(waitTimeInMilliseconds); + } + } + + hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); + } + + ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__dsound(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_dsound); + + ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); + if (pContext->dsound.hDSoundDLL == NULL) { + return MA_API_NOT_FOUND; + } + + pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + + /* + We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too + well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient + place to just disable the DirectSound backend for Windows 95. + */ + if (pContext->dsound.DirectSoundCreate == NULL || + pContext->dsound.DirectSoundEnumerateA == NULL || + pContext->dsound.DirectSoundCaptureCreate == NULL || + pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { + return MA_API_NOT_FOUND; + } + + pCallbacks->onContextInit = ma_context_init__dsound; + pCallbacks->onContextUninit = ma_context_uninit__dsound; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; + pCallbacks->onDeviceInit = ma_device_init__dsound; + pCallbacks->onDeviceUninit = ma_device_uninit__dsound; + pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ + pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ + pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ + pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ + pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; + + return MA_SUCCESS; +} +#endif + + + +/****************************************************************************** + +WinMM Backend + +******************************************************************************/ +#ifdef MA_HAS_WINMM + +/* +Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN +is defined. We need to define the types and functions we need manually. +*/ +#define MA_MMSYSERR_NOERROR 0 +#define MA_MMSYSERR_ERROR 1 +#define MA_MMSYSERR_BADDEVICEID 2 +#define MA_MMSYSERR_INVALHANDLE 5 +#define MA_MMSYSERR_NOMEM 7 +#define MA_MMSYSERR_INVALFLAG 10 +#define MA_MMSYSERR_INVALPARAM 11 +#define MA_MMSYSERR_HANDLEBUSY 12 + +#define MA_CALLBACK_EVENT 0x00050000 +#define MA_WAVE_ALLOWSYNC 0x0002 + +#define MA_WHDR_DONE 0x00000001 +#define MA_WHDR_PREPARED 0x00000002 +#define MA_WHDR_BEGINLOOP 0x00000004 +#define MA_WHDR_ENDLOOP 0x00000008 +#define MA_WHDR_INQUEUE 0x00000010 + +#define MA_MAXPNAMELEN 32 + +typedef void* MA_HWAVEIN; +typedef void* MA_HWAVEOUT; +typedef UINT MA_MMRESULT; +typedef UINT MA_MMVERSION; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; +} MA_WAVEINCAPSA; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; +} MA_WAVEOUTCAPSA; + +typedef struct tagWAVEHDR +{ + char* lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD_PTR dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct tagWAVEHDR* lpNext; + DWORD_PTR reserved; +} MA_WAVEHDR; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MA_WAVEOUTCAPS2A; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MA_WAVEINCAPS2A; + +typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); +typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); + +static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) +{ + switch (resultMM) + { + case MA_MMSYSERR_NOERROR: return MA_SUCCESS; + case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; + case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; + case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; + case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; + case MA_MMSYSERR_ERROR: return MA_ERROR; + default: return MA_ERROR; + } +} + +static char* ma_find_last_character(char* str, char ch) +{ + char* last; + + if (str == NULL) { + return NULL; + } + + last = NULL; + while (*str != '\0') { + if (*str == ch) { + last = str; + } + + str += 1; + } + + return last; +} + +static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) +{ + return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); +} + + +/* +Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so +we can do things generically and typesafely. Names are being kept the same for consistency. +*/ +typedef struct +{ + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + GUID NameGuid; +} MA_WAVECAPSA; + +static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) +{ + WORD bitsPerSample = 0; + DWORD sampleRate = 0; + + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } + + if (channels == 1) { + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + } + } else { + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MA_SUCCESS; +} + +static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) +{ + ma_result result; + + MA_ASSERT(pWF != NULL); + + MA_ZERO_OBJECT(pWF); + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_PCM; + pWF->nChannels = (WORD)channels; + if (pWF->nChannels > 2) { + pWF->nChannels = 2; + } + + result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); + if (result != MA_SUCCESS) { + return result; + } + + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) +{ + WORD bitsPerSample; + DWORD sampleRate; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* + Name / Description + + Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking + situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try + looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. + */ + + /* Set the default to begin with. */ + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); + + /* + Now try the registry. There's a few things to consider here: + - The name GUID can be null, in which we case we just need to stick to the original 31 characters. + - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. + - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The + problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), + but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to + usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component + name, and then concatenate the name from the registry. + */ + if (!ma_is_guid_null(&pCaps->NameGuid)) { + WCHAR guidStrW[256]; + if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { + char guidStr[256]; + char keyStr[1024]; + HKEY hKey; + + WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); + + ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); + ma_strcat_s(keyStr, sizeof(keyStr), guidStr); + + if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + BYTE nameFromReg[512]; + DWORD nameFromRegSize = sizeof(nameFromReg); + LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); + ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); + + if (resultWin32 == ERROR_SUCCESS) { + /* We have the value from the registry, so now we need to construct the name string. */ + char name[1024]; + if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { + char* nameBeg = ma_find_last_character(name, '('); + if (nameBeg != NULL) { + size_t leadingLen = (nameBeg - name); + ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); + + /* The closing ")", if it can fit. */ + if (leadingLen + nameFromRegSize < sizeof(name)-1) { + ma_strcat_s(name, sizeof(name), ")"); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); + } + } + } + } + } + } + + + result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); + if (result != MA_SUCCESS) { + return result; + } + + if (bitsPerSample == 8) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; + pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) +{ + MA_WAVECAPSA caps; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + +static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) +{ + MA_WAVECAPSA caps; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + + +static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + UINT playbackDeviceCount; + UINT captureDeviceCount; + UINT iPlaybackDevice; + UINT iCaptureDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); + for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { + MA_MMRESULT result; + MA_WAVEOUTCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + ma_device_info deviceInfo; + + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.winmm = iPlaybackDevice; + + /* The first enumerated device is the default device. */ + if (iPlaybackDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + return MA_SUCCESS; /* Enumeration was stopped. */ + } + } + } + } + + /* Capture. */ + captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); + for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { + MA_MMRESULT result; + MA_WAVEINCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + ma_device_info deviceInfo; + + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.winmm = iCaptureDevice; + + /* The first enumerated device is the default device. */ + if (iCaptureDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + return MA_SUCCESS; /* Enumeration was stopped. */ + } + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + UINT winMMDeviceID; + + MA_ASSERT(pContext != NULL); + + winMMDeviceID = 0; + if (pDeviceID != NULL) { + winMMDeviceID = (UINT)pDeviceID->winmm; + } + + pDeviceInfo->id.winmm = winMMDeviceID; + + /* The first ID is the default device. */ + if (winMMDeviceID == 0) { + pDeviceInfo->isDefault = MA_TRUE; + } + + if (deviceType == ma_device_type_playback) { + MA_MMRESULT result; + MA_WAVEOUTCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); + } + } else { + MA_MMRESULT result; + MA_WAVEINCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); + } + } + + return MA_NO_DEVICE; +} + + +static ma_result ma_device_uninit__winmm(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + CloseHandle((HANDLE)pDevice->winmm.hEventCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); + } + + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + + MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ + + return MA_SUCCESS; +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* WinMM has a minimum period size of 40ms. */ + ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); + ma_uint32 periodSizeInFrames; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + if (periodSizeInFrames < minPeriodSizeInFrames) { + periodSizeInFrames = minPeriodSizeInFrames; + } + + return periodSizeInFrames; +} + +static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + const char* errorMsg = ""; + ma_result errorCode = MA_ERROR; + ma_result result = MA_SUCCESS; + ma_uint32 heapSize; + UINT winMMDeviceIDPlayback = 0; + UINT winMMDeviceIDCapture = 0; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->winmm); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exlusive mode with WinMM. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + if (pDescriptorPlayback->pDeviceID != NULL) { + winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; + } + if (pDescriptorCapture->pDeviceID != NULL) { + winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; + } + + /* The capture device needs to be initialized first. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEINCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; + + /* We use an event to know when a new fragment needs to be enqueued. */ + pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDevice->winmm.hEventCapture == NULL) { + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); + goto on_error; + } + + /* The format should be based on the device's actual format. */ + if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; + goto on_error; + } + + result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); + if (result != MA_SUCCESS) { + errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; + goto on_error; + } + + resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); + pDescriptorCapture->channels = wf.nChannels; + pDescriptorCapture->sampleRate = wf.nSamplesPerSec; + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEOUTCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; + + /* We use an event to know when a new fragment needs to be enqueued. */ + pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDevice->winmm.hEventPlayback == NULL) { + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); + goto on_error; + } + + /* The format should be based on the device's actual format. */ + if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; + goto on_error; + } + + result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); + if (result != MA_SUCCESS) { + errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; + goto on_error; + } + + resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); + pDescriptorPlayback->channels = wf.nChannels; + pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + } + + /* + The heap allocated data is allocated like so: + + [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] + */ + heapSize = 0; + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + } + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); + } + + pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); + if (pDevice->winmm._pHeapData == NULL) { + errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; + goto on_error; + } + + MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPeriod; + + if (pConfig->deviceType == ma_device_type_capture) { + pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + } else { + pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); + } + + /* Prepare headers. */ + for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { + ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); + + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + + /* + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + it's unlocked and available for writing. A value of 1 means it's locked. + */ + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPeriod; + + if (pConfig->deviceType == ma_device_type_playback) { + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); + } else { + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + } + + /* Prepare headers. */ + for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { + ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); + + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + + /* + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + it's unlocked and available for writing. A value of 1 means it's locked. + */ + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; + } + } + + return MA_SUCCESS; + +on_error: + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.pWAVEHDRCapture != NULL) { + ma_uint32 iPeriod; + for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { + ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + } + } + + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.pWAVEHDRCapture != NULL) { + ma_uint32 iPeriod; + for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { + ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + } + } + + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + } + + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + + if (errorMsg != NULL && errorMsg[0] != '\0') { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); + } + + return errorCode; +} + +static ma_result ma_device_start__winmm(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + MA_MMRESULT resultMM; + MA_WAVEHDR* pWAVEHDR; + ma_uint32 iPeriod; + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + + /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ + for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); + return ma_result_from_MMRESULT(resultMM); + } + + /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ + pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ + } + + /* Capture devices need to be explicitly started, unlike playback devices. */ + resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); + return ma_result_from_MMRESULT(resultMM); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__winmm(ma_device* pDevice) +{ + MA_MMRESULT resultMM; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.hDeviceCapture == NULL) { + return MA_INVALID_ARGS; + } + + resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_uint32 iPeriod; + MA_WAVEHDR* pWAVEHDR; + + if (pDevice->winmm.hDevicePlayback == NULL) { + return MA_INVALID_ARGS; + } + + /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { + if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + break; /* An error occurred so just abandon ship and stop the device without draining. */ + } + + pWAVEHDR[iPeriod].dwUser = 0; + } + } + + resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + MA_MMRESULT resultMM; + ma_uint32 totalFramesWritten; + MA_WAVEHDR* pWAVEHDR; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pPCMFrames != NULL); + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + + /* Keep processing as much data as possible. */ + totalFramesWritten = 0; + while (totalFramesWritten < frameCount) { + /* If the current header has some space available we need to write part of it. */ + if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ + /* + This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to + write it out and move on to the next iteration. + */ + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; + + ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); + const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); + void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); + MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); + + pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; + totalFramesWritten += framesToCopy; + + /* If we've consumed the buffer entirely we need to write it out to the device. */ + if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); + + /* The device will be started here. */ + resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + result = ma_result_from_MMRESULT(resultMM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); + break; + } + + /* Make sure we move to the next header. */ + pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; + pDevice->winmm.headerFramesConsumedPlayback = 0; + } + + /* If at this point we have consumed the entire input buffer we can return. */ + MA_ASSERT(totalFramesWritten <= frameCount); + if (totalFramesWritten == frameCount) { + break; + } + + /* Getting here means there's more to process. */ + continue; + } + + /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; + } + + /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ + if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ + pDevice->winmm.headerFramesConsumedPlayback = 0; + } + + /* If the device has been stopped we need to break. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + break; + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalFramesWritten; + } + + return result; +} + +static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + MA_MMRESULT resultMM; + ma_uint32 totalFramesRead; + MA_WAVEHDR* pWAVEHDR; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pPCMFrames != NULL); + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + + /* Keep processing as much data as possible. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + /* If the current header has some space available we need to write part of it. */ + if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ + /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; + + ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); + const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); + void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); + MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); + + pDevice->winmm.headerFramesConsumedCapture += framesToCopy; + totalFramesRead += framesToCopy; + + /* If we've consumed the buffer entirely we need to add it back to the device. */ + if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + + /* The device will be started here. */ + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + result = ma_result_from_MMRESULT(resultMM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); + break; + } + + /* Make sure we move to the next header. */ + pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; + pDevice->winmm.headerFramesConsumedCapture = 0; + } + + /* If at this point we have filled the entire input buffer we can return. */ + MA_ASSERT(totalFramesRead <= frameCount); + if (totalFramesRead == frameCount) { + break; + } + + /* Getting here means there's more to process. */ + continue; + } + + /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; + } + + /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ + if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ + pDevice->winmm.headerFramesConsumedCapture = 0; + } + + /* If the device has been stopped we need to break. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + break; + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +static ma_result ma_context_uninit__winmm(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_winmm); + + ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); + return MA_SUCCESS; +} + +static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); + if (pContext->winmm.hWinMM == NULL) { + return MA_NO_BACKEND; + } + + pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); + + pCallbacks->onContextInit = ma_context_init__winmm; + pCallbacks->onContextUninit = ma_context_uninit__winmm; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; + pCallbacks->onDeviceInit = ma_device_init__winmm; + pCallbacks->onDeviceUninit = ma_device_uninit__winmm; + pCallbacks->onDeviceStart = ma_device_start__winmm; + pCallbacks->onDeviceStop = ma_device_stop__winmm; + pCallbacks->onDeviceRead = ma_device_read__winmm; + pCallbacks->onDeviceWrite = ma_device_write__winmm; + pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ + + return MA_SUCCESS; +} +#endif + + + + +/****************************************************************************** + +ALSA Backend + +******************************************************************************/ +#ifdef MA_HAS_ALSA + +#include /* poll(), struct pollfd */ +#include /* eventfd() */ + +#ifdef MA_NO_RUNTIME_LINKING + +/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ +#if !defined(__cplusplus) + #if defined(__STRICT_ANSI__) + #if !defined(inline) + #define inline __inline__ __attribute__((always_inline)) + #define MA_INLINE_DEFINED + #endif + #endif +#endif +#include +#if defined(MA_INLINE_DEFINED) + #undef inline + #undef MA_INLINE_DEFINED +#endif + +typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; +typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; +typedef snd_pcm_stream_t ma_snd_pcm_stream_t; +typedef snd_pcm_format_t ma_snd_pcm_format_t; +typedef snd_pcm_access_t ma_snd_pcm_access_t; +typedef snd_pcm_t ma_snd_pcm_t; +typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; +typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; +typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; +typedef snd_pcm_info_t ma_snd_pcm_info_t; +typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; +typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; +typedef snd_pcm_state_t ma_snd_pcm_state_t; + +/* snd_pcm_stream_t */ +#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK +#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE + +/* snd_pcm_format_t */ +#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN +#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 +#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE +#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE +#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE +#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE +#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE +#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE +#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE +#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE +#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE +#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE +#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW +#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW +#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE +#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE + +/* ma_snd_pcm_access_t */ +#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED +#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED +#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX +#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED +#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED + +/* Channel positions. */ +#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN +#define MA_SND_CHMAP_NA SND_CHMAP_NA +#define MA_SND_CHMAP_MONO SND_CHMAP_MONO +#define MA_SND_CHMAP_FL SND_CHMAP_FL +#define MA_SND_CHMAP_FR SND_CHMAP_FR +#define MA_SND_CHMAP_RL SND_CHMAP_RL +#define MA_SND_CHMAP_RR SND_CHMAP_RR +#define MA_SND_CHMAP_FC SND_CHMAP_FC +#define MA_SND_CHMAP_LFE SND_CHMAP_LFE +#define MA_SND_CHMAP_SL SND_CHMAP_SL +#define MA_SND_CHMAP_SR SND_CHMAP_SR +#define MA_SND_CHMAP_RC SND_CHMAP_RC +#define MA_SND_CHMAP_FLC SND_CHMAP_FLC +#define MA_SND_CHMAP_FRC SND_CHMAP_FRC +#define MA_SND_CHMAP_RLC SND_CHMAP_RLC +#define MA_SND_CHMAP_RRC SND_CHMAP_RRC +#define MA_SND_CHMAP_FLW SND_CHMAP_FLW +#define MA_SND_CHMAP_FRW SND_CHMAP_FRW +#define MA_SND_CHMAP_FLH SND_CHMAP_FLH +#define MA_SND_CHMAP_FCH SND_CHMAP_FCH +#define MA_SND_CHMAP_FRH SND_CHMAP_FRH +#define MA_SND_CHMAP_TC SND_CHMAP_TC +#define MA_SND_CHMAP_TFL SND_CHMAP_TFL +#define MA_SND_CHMAP_TFR SND_CHMAP_TFR +#define MA_SND_CHMAP_TFC SND_CHMAP_TFC +#define MA_SND_CHMAP_TRL SND_CHMAP_TRL +#define MA_SND_CHMAP_TRR SND_CHMAP_TRR +#define MA_SND_CHMAP_TRC SND_CHMAP_TRC +#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC +#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC +#define MA_SND_CHMAP_TSL SND_CHMAP_TSL +#define MA_SND_CHMAP_TSR SND_CHMAP_TSR +#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE +#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE +#define MA_SND_CHMAP_BC SND_CHMAP_BC +#define MA_SND_CHMAP_BLC SND_CHMAP_BLC +#define MA_SND_CHMAP_BRC SND_CHMAP_BRC + +/* Open mode flags. */ +#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE +#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS +#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT +#else +#include /* For EPIPE, etc. */ +typedef unsigned long ma_snd_pcm_uframes_t; +typedef long ma_snd_pcm_sframes_t; +typedef int ma_snd_pcm_stream_t; +typedef int ma_snd_pcm_format_t; +typedef int ma_snd_pcm_access_t; +typedef int ma_snd_pcm_state_t; +typedef struct ma_snd_pcm_t ma_snd_pcm_t; +typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; +typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; +typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; +typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; +typedef struct +{ + void* addr; + unsigned int first; + unsigned int step; +} ma_snd_pcm_channel_area_t; +typedef struct +{ + unsigned int channels; + unsigned int pos[1]; +} ma_snd_pcm_chmap_t; + +/* snd_pcm_state_t */ +#define MA_SND_PCM_STATE_OPEN 0 +#define MA_SND_PCM_STATE_SETUP 1 +#define MA_SND_PCM_STATE_PREPARED 2 +#define MA_SND_PCM_STATE_RUNNING 3 +#define MA_SND_PCM_STATE_XRUN 4 +#define MA_SND_PCM_STATE_DRAINING 5 +#define MA_SND_PCM_STATE_PAUSED 6 +#define MA_SND_PCM_STATE_SUSPENDED 7 +#define MA_SND_PCM_STATE_DISCONNECTED 8 + +/* snd_pcm_stream_t */ +#define MA_SND_PCM_STREAM_PLAYBACK 0 +#define MA_SND_PCM_STREAM_CAPTURE 1 + +/* snd_pcm_format_t */ +#define MA_SND_PCM_FORMAT_UNKNOWN -1 +#define MA_SND_PCM_FORMAT_U8 1 +#define MA_SND_PCM_FORMAT_S16_LE 2 +#define MA_SND_PCM_FORMAT_S16_BE 3 +#define MA_SND_PCM_FORMAT_S24_LE 6 +#define MA_SND_PCM_FORMAT_S24_BE 7 +#define MA_SND_PCM_FORMAT_S32_LE 10 +#define MA_SND_PCM_FORMAT_S32_BE 11 +#define MA_SND_PCM_FORMAT_FLOAT_LE 14 +#define MA_SND_PCM_FORMAT_FLOAT_BE 15 +#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 +#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 +#define MA_SND_PCM_FORMAT_MU_LAW 20 +#define MA_SND_PCM_FORMAT_A_LAW 21 +#define MA_SND_PCM_FORMAT_S24_3LE 32 +#define MA_SND_PCM_FORMAT_S24_3BE 33 + +/* snd_pcm_access_t */ +#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 +#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 +#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 +#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 +#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 + +/* Channel positions. */ +#define MA_SND_CHMAP_UNKNOWN 0 +#define MA_SND_CHMAP_NA 1 +#define MA_SND_CHMAP_MONO 2 +#define MA_SND_CHMAP_FL 3 +#define MA_SND_CHMAP_FR 4 +#define MA_SND_CHMAP_RL 5 +#define MA_SND_CHMAP_RR 6 +#define MA_SND_CHMAP_FC 7 +#define MA_SND_CHMAP_LFE 8 +#define MA_SND_CHMAP_SL 9 +#define MA_SND_CHMAP_SR 10 +#define MA_SND_CHMAP_RC 11 +#define MA_SND_CHMAP_FLC 12 +#define MA_SND_CHMAP_FRC 13 +#define MA_SND_CHMAP_RLC 14 +#define MA_SND_CHMAP_RRC 15 +#define MA_SND_CHMAP_FLW 16 +#define MA_SND_CHMAP_FRW 17 +#define MA_SND_CHMAP_FLH 18 +#define MA_SND_CHMAP_FCH 19 +#define MA_SND_CHMAP_FRH 20 +#define MA_SND_CHMAP_TC 21 +#define MA_SND_CHMAP_TFL 22 +#define MA_SND_CHMAP_TFR 23 +#define MA_SND_CHMAP_TFC 24 +#define MA_SND_CHMAP_TRL 25 +#define MA_SND_CHMAP_TRR 26 +#define MA_SND_CHMAP_TRC 27 +#define MA_SND_CHMAP_TFLC 28 +#define MA_SND_CHMAP_TFRC 29 +#define MA_SND_CHMAP_TSL 30 +#define MA_SND_CHMAP_TSR 31 +#define MA_SND_CHMAP_LLFE 32 +#define MA_SND_CHMAP_RLFE 33 +#define MA_SND_CHMAP_BC 34 +#define MA_SND_CHMAP_BLC 35 +#define MA_SND_CHMAP_BRC 36 + +/* Open mode flags. */ +#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 +#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 +#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 +#endif + +typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); +typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); +typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); +typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); +typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); +typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); +typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); +typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); +typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); +typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); +typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); +typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); +typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); +typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); +typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); +typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); +typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); +typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); +typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); +typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); +typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); +typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); +typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); +typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); +typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); +typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); +typedef int (* ma_snd_card_get_index_proc) (const char *name); +typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); +typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); +typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); +typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); +typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); +typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); +typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); +typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); +typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); +typedef int (* ma_snd_config_update_free_global_proc) (void); + +/* This array specifies each of the common devices that can be used for both playback and capture. */ +static const char* g_maCommonDeviceNamesALSA[] = { + "default", + "null", + "pulse", + "jack" +}; + +/* This array allows us to blacklist specific playback devices. */ +static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { + "" +}; + +/* This array allows us to blacklist specific capture devices. */ +static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { + "" +}; + + +static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) +{ + ma_snd_pcm_format_t ALSAFormats[] = { + MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ + MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ + MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ + MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ + MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ + MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ + }; + + if (ma_is_big_endian()) { + ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; + ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; + ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; + ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; + ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; + ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; + } + + return ALSAFormats[format]; +} + +static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) +{ + if (ma_is_little_endian()) { + switch (formatALSA) { + case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; + case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; + case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; + case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; + default: break; + } + } else { + switch (formatALSA) { + case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; + case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; + case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; + case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; + default: break; + } + } + + /* Endian agnostic. */ + switch (formatALSA) { + case MA_SND_PCM_FORMAT_U8: return ma_format_u8; + default: return ma_format_unknown; + } +} + +static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) +{ + switch (alsaChannelPos) + { + case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; + case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; + case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; + case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; + case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; + case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; + case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; + case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; + case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; + case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; + case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; + case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case MA_SND_CHMAP_RLC: return 0; + case MA_SND_CHMAP_RRC: return 0; + case MA_SND_CHMAP_FLW: return 0; + case MA_SND_CHMAP_FRW: return 0; + case MA_SND_CHMAP_FLH: return 0; + case MA_SND_CHMAP_FCH: return 0; + case MA_SND_CHMAP_FRH: return 0; + case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; + case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; + case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; + case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; + case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; + case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; + case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; + default: break; + } + + return 0; +} + +static ma_bool32 ma_is_common_device_name__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + +static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + +static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) +{ + if (deviceType == ma_device_type_playback) { + return ma_is_playback_device_blacklisted__alsa(name); + } else { + return ma_is_capture_device_blacklisted__alsa(name); + } +} + + +static const char* ma_find_char(const char* str, char c, int* index) +{ + int i = 0; + for (;;) { + if (str[i] == '\0') { + if (index) *index = -1; + return NULL; + } + + if (str[i] == c) { + if (index) *index = i; + return str + i; + } + + i += 1; + } + + /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ + if (index) *index = -1; + return NULL; +} + +static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) +{ + /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ + + int commaPos; + const char* dev; + int i; + + if (hwid == NULL) { + return MA_FALSE; + } + + if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { + return MA_FALSE; + } + + hwid += 3; + + dev = ma_find_char(hwid, ',', &commaPos); + if (dev == NULL) { + return MA_FALSE; + } else { + dev += 1; /* Skip past the ",". */ + } + + /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ + for (i = 0; i < commaPos; ++i) { + if (hwid[i] < '0' || hwid[i] > '9') { + return MA_FALSE; + } + } + + /* Check if everything after the "," is numeric. If not, return false. */ + i = 0; + while (dev[i] != '\0') { + if (dev[i] < '0' || dev[i] > '9') { + return MA_FALSE; + } + i += 1; + } + + return MA_TRUE; +} + +static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ +{ + /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ + + int colonPos; + int commaPos; + char card[256]; + const char* dev; + int cardIndex; + + if (dst == NULL) { + return -1; + } + if (dstSize < 7) { + return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ + } + + *dst = '\0'; /* Safety. */ + if (src == NULL) { + return -1; + } + + /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ + if (ma_is_device_name_in_hw_format__alsa(src)) { + return ma_strcpy_s(dst, dstSize, src); + } + + src = ma_find_char(src, ':', &colonPos); + if (src == NULL) { + return -1; /* Couldn't find a colon */ + } + + dev = ma_find_char(src, ',', &commaPos); + if (dev == NULL) { + dev = "0"; + ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ + } else { + dev = dev + 5; /* +5 = ",DEV=" */ + ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ + } + + cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); + if (cardIndex < 0) { + return -2; /* Failed to retrieve the card index. */ + } + + + /* Construction. */ + dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; + if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { + return -3; + } + if (ma_strcat_s(dst, dstSize, ",") != 0) { + return -3; + } + if (ma_strcat_s(dst, dstSize, dev) != 0) { + return -3; + } + + return 0; +} + +static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) +{ + ma_uint32 i; + + MA_ASSERT(pHWID != NULL); + + for (i = 0; i < count; ++i) { + if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) +{ + ma_snd_pcm_t* pPCM; + ma_snd_pcm_stream_t stream; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppPCM != NULL); + + *ppPCM = NULL; + pPCM = NULL; + + stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; + + if (pDeviceID == NULL) { + ma_bool32 isDeviceOpen; + size_t i; + + /* + We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes + me feel better to try as hard as we can get to get _something_ working. + */ + const char* defaultDeviceNames[] = { + "default", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + if (shareMode == ma_share_mode_exclusive) { + defaultDeviceNames[1] = "hw"; + defaultDeviceNames[2] = "hw:0"; + defaultDeviceNames[3] = "hw:0,0"; + } else { + if (deviceType == ma_device_type_playback) { + defaultDeviceNames[1] = "dmix"; + defaultDeviceNames[2] = "dmix:0"; + defaultDeviceNames[3] = "dmix:0,0"; + } else { + defaultDeviceNames[1] = "dsnoop"; + defaultDeviceNames[2] = "dsnoop:0"; + defaultDeviceNames[3] = "dsnoop:0,0"; + } + defaultDeviceNames[4] = "hw"; + defaultDeviceNames[5] = "hw:0"; + defaultDeviceNames[6] = "hw:0,0"; + } + + isDeviceOpen = MA_FALSE; + for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { + if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { + if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { + isDeviceOpen = MA_TRUE; + break; + } + } + } + + if (!isDeviceOpen) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } else { + /* + We're trying to open a specific device. There's a few things to consider here: + + miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When + an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it + finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). + */ + + /* May end up needing to make small adjustments to the ID, so make a copy. */ + ma_device_id deviceID = *pDeviceID; + int resultALSA = -ENODEV; + + if (deviceID.alsa[0] != ':') { + /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); + } else { + char hwid[256]; + + /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ + if (deviceID.alsa[1] == '\0') { + deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ + } + + if (shareMode == ma_share_mode_shared) { + if (deviceType == ma_device_type_playback) { + ma_strcpy_s(hwid, sizeof(hwid), "dmix"); + } else { + ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); + } + + if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + } + } + + /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ + if (resultALSA != 0) { + ma_strcpy_s(hwid, sizeof(hwid), "hw"); + if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + } + } + } + + if (resultALSA < 0) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + *ppPCM = pPCM; + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + int resultALSA; + ma_bool32 cbResult = MA_TRUE; + char** ppDeviceHints; + ma_device_id* pUniqueIDs = NULL; + ma_uint32 uniqueIDCount = 0; + char** ppNextDeviceHint; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); + + resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); + if (resultALSA < 0) { + ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + return ma_result_from_errno(-resultALSA); + } + + ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + ma_device_type deviceType = ma_device_type_playback; + ma_bool32 stopEnumeration = MA_FALSE; + char hwid[sizeof(pUniqueIDs->alsa)]; + ma_device_info deviceInfo; + + if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { + deviceType = ma_device_type_playback; + } + if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { + deviceType = ma_device_type_capture; + } + + if (NAME != NULL) { + if (pContext->alsa.useVerboseDeviceEnumeration) { + /* Verbose mode. Use the name exactly as-is. */ + ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } else { + /* Simplified mode. Use ":%d,%d" format. */ + if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { + /* + At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the + plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device + initialization time and is used as an indicator to try and use the most appropriate plugin depending on the + device type and sharing mode. + */ + char* dst = hwid; + char* src = hwid+2; + while ((*dst++ = *src++)); + } else { + /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ + ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } + + if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { + goto next_device; /* The device has already been enumerated. Move on to the next one. */ + } else { + /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ + size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); + ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); + if (pNewUniqueIDs == NULL) { + goto next_device; /* Failed to allocate memory. */ + } + + pUniqueIDs = pNewUniqueIDs; + MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); + uniqueIDCount += 1; + } + } + } else { + MA_ZERO_MEMORY(hwid, sizeof(hwid)); + } + + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); + + /* + There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and + just use the name of "default" as the indicator. + */ + if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + + /* + DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose + device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish + between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the + description. + + The value in DESC seems to be split into two lines, with the first line being the name of the device and the + second line being a description of the device. I don't like having the description be across two lines because + it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line + being put into parentheses. In simplified mode I'm just stripping the second line entirely. + */ + if (DESC != NULL) { + int lfPos; + const char* line2 = ma_find_char(DESC, '\n', &lfPos); + if (line2 != NULL) { + line2 += 1; /* Skip past the new-line character. */ + + if (pContext->alsa.useVerboseDeviceEnumeration) { + /* Verbose mode. Put the second line in brackets. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); + } else { + /* Simplified mode. Strip the second line entirely. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + } + } else { + /* There's no second line. Just copy the whole description. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); + } + } + + if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { + cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + } + + /* + Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback + again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which + means both Input and Output. + */ + if (cbResult) { + if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { + if (deviceType == ma_device_type_playback) { + if (!ma_is_capture_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } else { + if (!ma_is_playback_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + } + } + } + + if (cbResult == MA_FALSE) { + stopEnumeration = MA_TRUE; + } + + next_device: + free(NAME); + free(DESC); + free(IOID); + ppNextDeviceHint += 1; + + /* We need to stop enumeration if the callback returned false. */ + if (stopEnumeration) { + break; + } + } + + ma_free(pUniqueIDs, &pContext->allocationCallbacks); + ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + + ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_device_type deviceType; + const ma_device_id* pDeviceID; + ma_share_mode shareMode; + ma_device_info* pDeviceInfo; + ma_bool32 foundDevice; +} ma_context_get_device_info_enum_callback_data__alsa; + +static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) +{ + ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; + MA_ASSERT(pData != NULL); + + (void)pContext; + + if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MA_TRUE; + } else { + if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MA_TRUE; + } + } + + /* Keep enumerating until we have found the device. */ + return !pData->foundDevice; +} + +static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pPCM != NULL); + MA_ASSERT(pHWParams != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; + } +} + +static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + ma_uint32 iSampleRate; + unsigned int minSampleRate; + unsigned int maxSampleRate; + int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ + + /* There could be a range. */ + ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); + ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); + + /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ + minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); + maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); + + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; + + if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); + } + } + + /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ + if (!ma_is_standard_sample_rate(minSampleRate)) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); + } + + if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); + } +} + +static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_context_get_device_info_enum_callback_data__alsa data; + ma_result result; + int resultALSA; + ma_snd_pcm_t* pPCM; + ma_snd_pcm_hw_params_t* pHWParams; + ma_uint32 iFormat; + ma_uint32 iChannel; + + MA_ASSERT(pContext != NULL); + + /* We just enumerate to find basic information about the device. */ + data.deviceType = deviceType; + data.pDeviceID = pDeviceID; + data.pDeviceInfo = pDeviceInfo; + data.foundDevice = MA_FALSE; + result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); + if (result != MA_SUCCESS) { + return result; + } + + if (!data.foundDevice) { + return MA_NO_DEVICE; + } + + if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { + pDeviceInfo->isDefault = MA_TRUE; + } + + /* For detailed info we need to open the device. */ + result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); + if (result != MA_SUCCESS) { + return result; + } + + /* We need to initialize a HW parameters object in order to know what formats are supported. */ + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); + if (pHWParams == NULL) { + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); + } + + /* + Some ALSA devices can support many permutations of formats, channels and rates. We only support + a fixed number of permutations which means we need to employ some strategies to ensure the best + combinations are returned. An example is the "pulse" device which can do it's own data conversion + in software and as a result can support any combination of format, channels and rate. + + We want to ensure the the first data formats are the best. We have a list of favored sample + formats and sample rates, so these will be the basis of our iteration. + */ + + /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { + ma_format format = g_maFormatPriorities[iFormat]; + + /* + For each format we need to make sure we reset the configuration space so we don't return + channel counts and rates that aren't compatible with a format. + */ + ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + + /* Test the format first. If this fails it means the format is not supported and we can skip it. */ + if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { + /* The format is supported. */ + unsigned int minChannels; + unsigned int maxChannels; + + /* + The configuration space needs to be restricted to this format so we can get an accurate + picture of which sample rates and channel counts are support with this format. + */ + ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + + /* Now we need to check for supported channels. */ + ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); + ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); + + if (minChannels > MA_MAX_CHANNELS) { + continue; /* Too many channels. */ + } + if (maxChannels < MA_MIN_CHANNELS) { + continue; /* Not enough channels. */ + } + + /* + Make sure the channel count is clamped. This is mainly intended for the max channels + because some devices can report an unbound maximum. + */ + minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ + ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ + } else { + /* The device only supports a specific set of channels. We need to iterate over all of them. */ + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + /* Test the channel before applying it to the configuration space. */ + unsigned int channels = iChannel; + + /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ + ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + + if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { + /* The channel count is supported. */ + + /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ + ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); + + /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ + ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); + } else { + /* The channel count is not supported. Skip. */ + } + } + } + } else { + /* The format is not supported. Skip. */ + } + } + + ma_free(pHWParams, &pContext->allocationCallbacks); + + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__alsa(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + close(pDevice->alsa.wakeupfdCapture); + ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); + } + + if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + close(pDevice->alsa.wakeupfdPlayback); + ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + ma_result result; + int resultALSA; + ma_snd_pcm_t* pPCM; + ma_bool32 isUsingMMap; + ma_snd_pcm_format_t formatALSA; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + int openMode; + ma_snd_pcm_hw_params_t* pHWParams; + ma_snd_pcm_sw_params_t* pSWParams; + ma_snd_pcm_uframes_t bufferBoundary; + int pollDescriptorCount; + struct pollfd* pPollDescriptors; + int wakeupfd; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ + MA_ASSERT(pDevice != NULL); + + formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); + + openMode = 0; + if (pConfig->alsa.noAutoResample) { + openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; + } + if (pConfig->alsa.noAutoChannels) { + openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; + } + if (pConfig->alsa.noAutoFormat) { + openMode |= MA_SND_PCM_NO_AUTO_FORMAT; + } + + result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); + if (result != MA_SUCCESS) { + return result; + } + + + /* Hardware parameters. */ + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + if (pHWParams == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); + } + + /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ + isUsingMMap = MA_FALSE; +#if 0 /* NOTE: MMAP mode temporarily disabled. */ + if (deviceType != ma_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 (!pConfig->alsa.noMMap) { + if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { + pDevice->alsa.isUsingMMap = MA_TRUE; + } + } + } +#endif + + if (!isUsingMMap) { + resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + /* + Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't + find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. + */ + + /* Format. */ + { + /* + At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is + supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. + */ + if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { + /* We're either requesting the native format or the specified format is not supported. */ + size_t iFormat; + + formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { + if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { + formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); + break; + } + } + + if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); + return MA_FORMAT_NOT_SUPPORTED; + } + } + + resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalFormat = ma_format_from_alsa(formatALSA); + if (internalFormat == ma_format_unknown) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; + } + } + + /* Channels. */ + { + unsigned int channels = pDescriptor->channels; + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + + resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalChannels = (ma_uint32)channels; + } + + /* Sample Rate */ + { + unsigned int sampleRate; + + /* + It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes + problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable + resampling. + + To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a + sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling + doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly + faster rate. + + miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine + for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very + good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. + + I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce + this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. + */ + ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); + + sampleRate = pDescriptor->sampleRate; + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalSampleRate = (ma_uint32)sampleRate; + } + + /* Periods. */ + { + ma_uint32 periods = pDescriptor->periodCount; + + resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalPeriods = periods; + } + + /* Buffer Size */ + { + ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; + + resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; + } + + /* Apply hardware parameters. */ + resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); + return ma_result_from_errno(-resultALSA); + } + + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + pHWParams = NULL; + + + /* Software parameters. */ + pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + if (pSWParams == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); + if (resultALSA < 0) { + bufferBoundary = internalPeriodSizeInFrames * internalPeriods; + } + + if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ + /* + Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to + the size of a period. But for full-duplex we need to set it such that it is at least two periods. + */ + resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); + if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); + return ma_result_from_errno(-resultALSA); + } + + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + pSWParams = NULL; + + + /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ + { + ma_snd_pcm_chmap_t* pChmap = NULL; + if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { + pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + } + + if (pChmap != NULL) { + ma_uint32 iChannel; + + /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ + if (pChmap->channels >= internalChannels) { + /* Drop excess channels. */ + for (iChannel = 0; iChannel < internalChannels; ++iChannel) { + internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); + } + } else { + ma_uint32 i; + + /* + Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate + channels. If validation fails, fall back to defaults. + */ + ma_bool32 isValid = MA_TRUE; + + /* Fill with defaults. */ + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + + /* Overwrite first pChmap->channels channels. */ + for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { + internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); + } + + /* Validate. */ + for (i = 0; i < internalChannels && isValid; ++i) { + ma_uint32 j; + for (j = i+1; j < internalChannels; ++j) { + if (internalChannelMap[i] == internalChannelMap[j]) { + isValid = MA_FALSE; + break; + } + } + } + + /* If our channel map is invalid, fall back to defaults. */ + if (!isValid) { + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + } + } + + free(pChmap); + pChmap = NULL; + } else { + /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + } + } + + + /* + We need to retrieve the poll descriptors so we can use poll() to wait for data to become + available for reading or writing. There's no well defined maximum for this so we're just going + to allocate this on the heap. + */ + pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); + if (pollDescriptorCount <= 0) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); + return MA_ERROR; + } + + pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ + if (pPollDescriptors == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); + return MA_OUT_OF_MEMORY; + } + + /* + We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver + never returns from writei() and readi(). This has been observed with the "pulse" device. + */ + wakeupfd = eventfd(0, 0); + if (wakeupfd < 0) { + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); + return ma_result_from_errno(errno); + } + + /* We'll place the wakeup fd at the start of the buffer. */ + pPollDescriptors[0].fd = wakeupfd; + pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ + pPollDescriptors[0].revents = 0; + + /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ + pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ + if (pollDescriptorCount <= 0) { + close(wakeupfd); + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); + return MA_ERROR; + } + + if (deviceType == ma_device_type_capture) { + pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; + pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; + pDevice->alsa.wakeupfdCapture = wakeupfd; + } else { + pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; + pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; + pDevice->alsa.wakeupfdPlayback = wakeupfd; + } + + + /* We're done. Prepare the device. */ + resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); + if (resultALSA < 0) { + close(wakeupfd); + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); + return ma_result_from_errno(-resultALSA); + } + + + if (deviceType == ma_device_type_capture) { + pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; + pDevice->alsa.isUsingMMapCapture = isUsingMMap; + } else { + pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; + pDevice->alsa.isUsingMMapPlayback = isUsingMMap; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->alsa); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__alsa(ma_device* pDevice) +{ + int resultALSA; + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); + return ma_result_from_errno(-resultALSA); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__alsa(ma_device* pDevice) +{ + /* + The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is + a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. + */ + int resultPoll; + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); + ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); + } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); + ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); + } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + } + + } + + return MA_SUCCESS; +} + +static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) +{ + for (;;) { + unsigned short revents; + int resultALSA; + int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); + if (resultPoll < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); + return ma_result_from_errno(errno); + } + + /* + Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor + has had it's POLLIN flag set. If so, we need to actually read the data and then exit + function. The wakeup descriptor will be the first item in the descriptors buffer. + */ + if ((pPollDescriptors[0].revents & POLLIN) != 0) { + ma_uint64 t; + int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ + if (resultRead < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); + return ma_result_from_errno(errno); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); + return MA_DEVICE_NOT_STARTED; + } + + /* + Getting here means that some data should be able to be read. We need to use ALSA to + translate the revents flags for us. + */ + resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); + return ma_result_from_errno(-resultALSA); + } + + if ((revents & POLLERR) != 0) { + ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); + if (state == MA_SND_PCM_STATE_XRUN) { + /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); + } + } + + if ((revents & requiredEvent) == requiredEvent) { + break; /* We're done. Data available for reading or writing. */ + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_wait_read__alsa(ma_device* pDevice) +{ + return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ +} + +static ma_result ma_device_wait_write__alsa(ma_device* pDevice) +{ + return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ +} + +static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_snd_pcm_sframes_t resultALSA = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + ma_result result; + + /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ + result = ma_device_wait_read__alsa(pDevice); + if (result != MA_SUCCESS) { + return result; + } + + /* Getting here means we should have data available. */ + resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); + if (resultALSA >= 0) { + break; /* Success. */ + } else { + if (resultALSA == -EAGAIN) { + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ + continue; /* Try again. */ + } else if (resultALSA == -EPIPE) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); + + /* Overrun. Recover and try again. If this fails we need to return an error. */ + resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); + return ma_result_from_errno((int)-resultALSA); + } + + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + continue; /* Try reading again. */ + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = resultALSA; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_snd_pcm_sframes_t resultALSA = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFrames != NULL); + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + ma_result result; + + /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ + result = ma_device_wait_write__alsa(pDevice); + if (result != MA_SUCCESS) { + return result; + } + + resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); + if (resultALSA >= 0) { + break; /* Success. */ + } else { + if (resultALSA == -EAGAIN) { + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ + continue; /* Try again. */ + } else if (resultALSA == -EPIPE) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); + + /* Underrun. Recover and try again. If this fails we need to return an error. */ + resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + /* + In my testing I have had a situation where writei() does not automatically restart the device even though I've set it + up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of + frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure + if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't + quite right here. + */ + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + continue; /* Try writing again. */ + } + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = resultALSA; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) +{ + ma_uint64 t = 1; + int resultWrite = 0; + + MA_ASSERT(pDevice != NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); + + /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ + if (pDevice->alsa.pPollDescriptorsCapture != NULL) { + resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); + } + if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { + resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); + } + + if (resultWrite < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); + return ma_result_from_errno(errno); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__alsa(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_alsa); + + /* Clean up memory for memory leak checkers. */ + ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); +#endif + + ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; +#ifndef MA_NO_RUNTIME_LINKING + const char* libasoundNames[] = { + "libasound.so.2", + "libasound.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libasoundNames); ++i) { + pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); + if (pContext->alsa.asoundSO != NULL) { + break; + } + } + + if (pContext->alsa.asoundSO == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); + return MA_NO_BACKEND; + } + + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); +#else + /* The system below is just for type safety. */ + ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; + ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; + ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; + ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; + ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; + ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; + ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; + ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; + ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; + ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; + ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; + ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; + ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; + ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; + ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; + ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; + ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; + ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; + ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; + ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; + ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; + ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; + ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; + ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; + ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; + ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; + ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; + ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; + ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; + ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; + ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; + ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; + ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; + ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; + ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; + ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; + ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; + ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; + ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; + ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; + ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; + ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; + ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; + ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; + ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; + ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; + ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; + ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; + ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; + ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; + ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; + ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; + ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; + ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; + ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; + ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; + ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; + ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; + ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; + ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; + ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; + ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; + ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; + + pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; + pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; + pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; + pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; + pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; + pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; + pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; + pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; + pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; + pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; + pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; + pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; + pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; + pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; + pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; + pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; + pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; + pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; + pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; + pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; + pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; + pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; + pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; + pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; + pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; +#endif + + pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; + + result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); + return result; + } + + pCallbacks->onContextInit = ma_context_init__alsa; + pCallbacks->onContextUninit = ma_context_uninit__alsa; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; + pCallbacks->onDeviceInit = ma_device_init__alsa; + pCallbacks->onDeviceUninit = ma_device_uninit__alsa; + pCallbacks->onDeviceStart = ma_device_start__alsa; + pCallbacks->onDeviceStop = ma_device_stop__alsa; + pCallbacks->onDeviceRead = ma_device_read__alsa; + pCallbacks->onDeviceWrite = ma_device_write__alsa; + pCallbacks->onDeviceDataLoop = NULL; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; + + return MA_SUCCESS; +} +#endif /* ALSA */ + + + +/****************************************************************************** + +PulseAudio Backend + +******************************************************************************/ +#ifdef MA_HAS_PULSEAUDIO +/* +The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on +in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. + +PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it +allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it +appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or +write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the +simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient +when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. + +Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to +get fun, and I don't mean that in a good way... + +The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands +don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is +enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost +all of PulseAudio's problems stem from. + +When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own +vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called +pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop +because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use +it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. + +To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer +to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded +main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely +specialized such as if you want to integrate it into your application's existing main loop infrastructure. + +(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. +It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) + +Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to +miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's +one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which +is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if +you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` +has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can +set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. +All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. +This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before +attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. + +The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an +internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the +host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. + +Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. +The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call +`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get +information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object +is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to +run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the +context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. +All of that just to retrieve basic information about a device! + +Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the +context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design +choices in PulseAudio. + +PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here +because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for +writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can +set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices +straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, +PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) +because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback +would be where a program will want to write or read data to or from the stream, but when it's called before the application has even +requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at +that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the +stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data +callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio +doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been +started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data +callback is not fired. + +This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will +continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device +is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in +PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call +`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always +writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if +you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to +*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining +important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained +before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write +data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! + +This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* +write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just +resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This +disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the +callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) + +Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, +only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as +"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think +it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you +guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is +absolutely beyond me. Would it really be that hard to just make it run synchronously? + +Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that +they were initialized in. + +That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're +embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to +run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche +requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is +constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a +parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These +changes alone will change PulseAudio from one of the worst audio APIs to one of the best. +*/ + + +/* +It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header +to check for type safety. We cannot do this when linking at run time because the header might not be available. +*/ +#ifdef MA_NO_RUNTIME_LINKING + +/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ +#if !defined(__cplusplus) + #if defined(__STRICT_ANSI__) + #if !defined(inline) + #define inline __inline__ __attribute__((always_inline)) + #define MA_INLINE_DEFINED + #endif + #endif +#endif +#include +#if defined(MA_INLINE_DEFINED) + #undef inline + #undef MA_INLINE_DEFINED +#endif + +#define MA_PA_OK PA_OK +#define MA_PA_ERR_ACCESS PA_ERR_ACCESS +#define MA_PA_ERR_INVALID PA_ERR_INVALID +#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY +#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED + +#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX +#define MA_PA_RATE_MAX PA_RATE_MAX + +typedef pa_context_flags_t ma_pa_context_flags_t; +#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS +#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL + +typedef pa_stream_flags_t ma_pa_stream_flags_t; +#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS +#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED +#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING +#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC +#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE +#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS +#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS +#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT +#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE +#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS +#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE +#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE +#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT +#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED +#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY +#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS +#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND +#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED +#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND +#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME +#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH + +typedef pa_sink_flags_t ma_pa_sink_flags_t; +#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS +#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define MA_PA_SINK_LATENCY PA_SINK_LATENCY +#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE +#define MA_PA_SINK_NETWORK PA_SINK_NETWORK +#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL +#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME +#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME +#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY +#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS + +typedef pa_source_flags_t ma_pa_source_flags_t; +#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS +#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY +#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE +#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK +#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL +#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME +#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY +#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME + +typedef pa_context_state_t ma_pa_context_state_t; +#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED +#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING +#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING +#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME +#define MA_PA_CONTEXT_READY PA_CONTEXT_READY +#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED +#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED + +typedef pa_stream_state_t ma_pa_stream_state_t; +#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED +#define MA_PA_STREAM_CREATING PA_STREAM_CREATING +#define MA_PA_STREAM_READY PA_STREAM_READY +#define MA_PA_STREAM_FAILED PA_STREAM_FAILED +#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED + +typedef pa_operation_state_t ma_pa_operation_state_t; +#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING +#define MA_PA_OPERATION_DONE PA_OPERATION_DONE +#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED + +typedef pa_sink_state_t ma_pa_sink_state_t; +#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE +#define MA_PA_SINK_RUNNING PA_SINK_RUNNING +#define MA_PA_SINK_IDLE PA_SINK_IDLE +#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED + +typedef pa_source_state_t ma_pa_source_state_t; +#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE +#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING +#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE +#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED + +typedef pa_seek_mode_t ma_pa_seek_mode_t; +#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE +#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE +#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ +#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END + +typedef pa_channel_position_t ma_pa_channel_position_t; +#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID +#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER +#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT +#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT +#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER +#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT +#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT +#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 +#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 +#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 +#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 +#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 +#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 +#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 +#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 +#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 +#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 +#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 +#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 +#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 +#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 +#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 +#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 +#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 +#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 +#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 +#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 +#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 +#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 +#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 +#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 +#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 +#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 +#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 +#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 +#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 +#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 +#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 +#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 +#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT +#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT +#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER +#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT +#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT +#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER +#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER + +typedef pa_channel_map_def_t ma_pa_channel_map_def_t; +#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF +#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA +#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX +#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX +#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS +#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT + +typedef pa_sample_format_t ma_pa_sample_format_t; +#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID +#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 +#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW +#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW +#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE +#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE +#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE +#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE +#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE +#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE +#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE +#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE +#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE +#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE + +typedef pa_mainloop ma_pa_mainloop; +typedef pa_threaded_mainloop ma_pa_threaded_mainloop; +typedef pa_mainloop_api ma_pa_mainloop_api; +typedef pa_context ma_pa_context; +typedef pa_operation ma_pa_operation; +typedef pa_stream ma_pa_stream; +typedef pa_spawn_api ma_pa_spawn_api; +typedef pa_buffer_attr ma_pa_buffer_attr; +typedef pa_channel_map ma_pa_channel_map; +typedef pa_cvolume ma_pa_cvolume; +typedef pa_sample_spec ma_pa_sample_spec; +typedef pa_sink_info ma_pa_sink_info; +typedef pa_source_info ma_pa_source_info; + +typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; +typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; +typedef pa_source_info_cb_t ma_pa_source_info_cb_t; +typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; +typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; +typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; +typedef pa_free_cb_t ma_pa_free_cb_t; +#else +#define MA_PA_OK 0 +#define MA_PA_ERR_ACCESS 1 +#define MA_PA_ERR_INVALID 2 +#define MA_PA_ERR_NOENTITY 5 +#define MA_PA_ERR_NOTSUPPORTED 19 + +#define MA_PA_CHANNELS_MAX 32 +#define MA_PA_RATE_MAX 384000 + +typedef int ma_pa_context_flags_t; +#define MA_PA_CONTEXT_NOFLAGS 0x00000000 +#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 +#define MA_PA_CONTEXT_NOFAIL 0x00000002 + +typedef int ma_pa_stream_flags_t; +#define MA_PA_STREAM_NOFLAGS 0x00000000 +#define MA_PA_STREAM_START_CORKED 0x00000001 +#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 +#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 +#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 +#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 +#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 +#define MA_PA_STREAM_FIX_FORMAT 0x00000040 +#define MA_PA_STREAM_FIX_RATE 0x00000080 +#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 +#define MA_PA_STREAM_DONT_MOVE 0x00000200 +#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 +#define MA_PA_STREAM_PEAK_DETECT 0x00000800 +#define MA_PA_STREAM_START_MUTED 0x00001000 +#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 +#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 +#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 +#define MA_PA_STREAM_START_UNMUTED 0x00010000 +#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 +#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 +#define MA_PA_STREAM_PASSTHROUGH 0x00080000 + +typedef int ma_pa_sink_flags_t; +#define MA_PA_SINK_NOFLAGS 0x00000000 +#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 +#define MA_PA_SINK_LATENCY 0x00000002 +#define MA_PA_SINK_HARDWARE 0x00000004 +#define MA_PA_SINK_NETWORK 0x00000008 +#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 +#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 +#define MA_PA_SINK_FLAT_VOLUME 0x00000040 +#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 +#define MA_PA_SINK_SET_FORMATS 0x00000100 + +typedef int ma_pa_source_flags_t; +#define MA_PA_SOURCE_NOFLAGS 0x00000000 +#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 +#define MA_PA_SOURCE_LATENCY 0x00000002 +#define MA_PA_SOURCE_HARDWARE 0x00000004 +#define MA_PA_SOURCE_NETWORK 0x00000008 +#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 +#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 +#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 +#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 + +typedef int ma_pa_context_state_t; +#define MA_PA_CONTEXT_UNCONNECTED 0 +#define MA_PA_CONTEXT_CONNECTING 1 +#define MA_PA_CONTEXT_AUTHORIZING 2 +#define MA_PA_CONTEXT_SETTING_NAME 3 +#define MA_PA_CONTEXT_READY 4 +#define MA_PA_CONTEXT_FAILED 5 +#define MA_PA_CONTEXT_TERMINATED 6 + +typedef int ma_pa_stream_state_t; +#define MA_PA_STREAM_UNCONNECTED 0 +#define MA_PA_STREAM_CREATING 1 +#define MA_PA_STREAM_READY 2 +#define MA_PA_STREAM_FAILED 3 +#define MA_PA_STREAM_TERMINATED 4 + +typedef int ma_pa_operation_state_t; +#define MA_PA_OPERATION_RUNNING 0 +#define MA_PA_OPERATION_DONE 1 +#define MA_PA_OPERATION_CANCELLED 2 + +typedef int ma_pa_sink_state_t; +#define MA_PA_SINK_INVALID_STATE -1 +#define MA_PA_SINK_RUNNING 0 +#define MA_PA_SINK_IDLE 1 +#define MA_PA_SINK_SUSPENDED 2 + +typedef int ma_pa_source_state_t; +#define MA_PA_SOURCE_INVALID_STATE -1 +#define MA_PA_SOURCE_RUNNING 0 +#define MA_PA_SOURCE_IDLE 1 +#define MA_PA_SOURCE_SUSPENDED 2 + +typedef int ma_pa_seek_mode_t; +#define MA_PA_SEEK_RELATIVE 0 +#define MA_PA_SEEK_ABSOLUTE 1 +#define MA_PA_SEEK_RELATIVE_ON_READ 2 +#define MA_PA_SEEK_RELATIVE_END 3 + +typedef int ma_pa_channel_position_t; +#define MA_PA_CHANNEL_POSITION_INVALID -1 +#define MA_PA_CHANNEL_POSITION_MONO 0 +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 +#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 +#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 +#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 +#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 +#define MA_PA_CHANNEL_POSITION_LFE 7 +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 +#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 +#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 +#define MA_PA_CHANNEL_POSITION_AUX0 12 +#define MA_PA_CHANNEL_POSITION_AUX1 13 +#define MA_PA_CHANNEL_POSITION_AUX2 14 +#define MA_PA_CHANNEL_POSITION_AUX3 15 +#define MA_PA_CHANNEL_POSITION_AUX4 16 +#define MA_PA_CHANNEL_POSITION_AUX5 17 +#define MA_PA_CHANNEL_POSITION_AUX6 18 +#define MA_PA_CHANNEL_POSITION_AUX7 19 +#define MA_PA_CHANNEL_POSITION_AUX8 20 +#define MA_PA_CHANNEL_POSITION_AUX9 21 +#define MA_PA_CHANNEL_POSITION_AUX10 22 +#define MA_PA_CHANNEL_POSITION_AUX11 23 +#define MA_PA_CHANNEL_POSITION_AUX12 24 +#define MA_PA_CHANNEL_POSITION_AUX13 25 +#define MA_PA_CHANNEL_POSITION_AUX14 26 +#define MA_PA_CHANNEL_POSITION_AUX15 27 +#define MA_PA_CHANNEL_POSITION_AUX16 28 +#define MA_PA_CHANNEL_POSITION_AUX17 29 +#define MA_PA_CHANNEL_POSITION_AUX18 30 +#define MA_PA_CHANNEL_POSITION_AUX19 31 +#define MA_PA_CHANNEL_POSITION_AUX20 32 +#define MA_PA_CHANNEL_POSITION_AUX21 33 +#define MA_PA_CHANNEL_POSITION_AUX22 34 +#define MA_PA_CHANNEL_POSITION_AUX23 35 +#define MA_PA_CHANNEL_POSITION_AUX24 36 +#define MA_PA_CHANNEL_POSITION_AUX25 37 +#define MA_PA_CHANNEL_POSITION_AUX26 38 +#define MA_PA_CHANNEL_POSITION_AUX27 39 +#define MA_PA_CHANNEL_POSITION_AUX28 40 +#define MA_PA_CHANNEL_POSITION_AUX29 41 +#define MA_PA_CHANNEL_POSITION_AUX30 42 +#define MA_PA_CHANNEL_POSITION_AUX31 43 +#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 +#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE + +typedef int ma_pa_channel_map_def_t; +#define MA_PA_CHANNEL_MAP_AIFF 0 +#define MA_PA_CHANNEL_MAP_ALSA 1 +#define MA_PA_CHANNEL_MAP_AUX 2 +#define MA_PA_CHANNEL_MAP_WAVEEX 3 +#define MA_PA_CHANNEL_MAP_OSS 4 +#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF + +typedef int ma_pa_sample_format_t; +#define MA_PA_SAMPLE_INVALID -1 +#define MA_PA_SAMPLE_U8 0 +#define MA_PA_SAMPLE_ALAW 1 +#define MA_PA_SAMPLE_ULAW 2 +#define MA_PA_SAMPLE_S16LE 3 +#define MA_PA_SAMPLE_S16BE 4 +#define MA_PA_SAMPLE_FLOAT32LE 5 +#define MA_PA_SAMPLE_FLOAT32BE 6 +#define MA_PA_SAMPLE_S32LE 7 +#define MA_PA_SAMPLE_S32BE 8 +#define MA_PA_SAMPLE_S24LE 9 +#define MA_PA_SAMPLE_S24BE 10 +#define MA_PA_SAMPLE_S24_32LE 11 +#define MA_PA_SAMPLE_S24_32BE 12 + +typedef struct ma_pa_mainloop ma_pa_mainloop; +typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; +typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; +typedef struct ma_pa_context ma_pa_context; +typedef struct ma_pa_operation ma_pa_operation; +typedef struct ma_pa_stream ma_pa_stream; +typedef struct ma_pa_spawn_api ma_pa_spawn_api; + +typedef struct +{ + ma_uint32 maxlength; + ma_uint32 tlength; + ma_uint32 prebuf; + ma_uint32 minreq; + ma_uint32 fragsize; +} ma_pa_buffer_attr; + +typedef struct +{ + ma_uint8 channels; + ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; +} ma_pa_channel_map; + +typedef struct +{ + ma_uint8 channels; + ma_uint32 values[MA_PA_CHANNELS_MAX]; +} ma_pa_cvolume; + +typedef struct +{ + ma_pa_sample_format_t format; + ma_uint32 rate; + ma_uint8 channels; +} ma_pa_sample_spec; + +typedef struct +{ + const char* name; + ma_uint32 index; + const char* description; + ma_pa_sample_spec sample_spec; + ma_pa_channel_map channel_map; + ma_uint32 owner_module; + ma_pa_cvolume volume; + int mute; + ma_uint32 monitor_source; + const char* monitor_source_name; + ma_uint64 latency; + const char* driver; + ma_pa_sink_flags_t flags; + void* proplist; + ma_uint64 configured_latency; + ma_uint32 base_volume; + ma_pa_sink_state_t state; + ma_uint32 n_volume_steps; + ma_uint32 card; + ma_uint32 n_ports; + void** ports; + void* active_port; + ma_uint8 n_formats; + void** formats; +} ma_pa_sink_info; + +typedef struct +{ + const char *name; + ma_uint32 index; + const char *description; + ma_pa_sample_spec sample_spec; + ma_pa_channel_map channel_map; + ma_uint32 owner_module; + ma_pa_cvolume volume; + int mute; + ma_uint32 monitor_of_sink; + const char *monitor_of_sink_name; + ma_uint64 latency; + const char *driver; + ma_pa_source_flags_t flags; + void* proplist; + ma_uint64 configured_latency; + ma_uint32 base_volume; + ma_pa_source_state_t state; + ma_uint32 n_volume_steps; + ma_uint32 card; + ma_uint32 n_ports; + void** ports; + void* active_port; + ma_uint8 n_formats; + void** formats; +} ma_pa_source_info; + +typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); +typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); +typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); +typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); +typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); +typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); +typedef void (* ma_pa_free_cb_t) (void* p); +#endif + + +typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); +typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); +typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); +typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); +typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); +typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); +typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); +typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); +typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); +typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); +typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); +typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); +typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); +typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); +typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); +typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); +typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); +typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); +typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); +typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); +typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); +typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); +typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); +typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); +typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); +typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); +typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); +typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); +typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); +typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); +typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); +typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); +typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); +typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); +typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); + +typedef struct +{ + ma_uint32 count; + ma_uint32 capacity; + ma_device_info* pInfo; +} ma_pulse_device_enum_data; + +static ma_result ma_result_from_pulse(int result) +{ + if (result < 0) { + return MA_ERROR; + } + + switch (result) { + case MA_PA_OK: return MA_SUCCESS; + case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; + case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; + case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; + default: return MA_ERROR; + } +} + +#if 0 +static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) +{ + if (ma_is_little_endian()) { + switch (format) { + case ma_format_s16: return MA_PA_SAMPLE_S16LE; + case ma_format_s24: return MA_PA_SAMPLE_S24LE; + case ma_format_s32: return MA_PA_SAMPLE_S32LE; + case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; + default: break; + } + } else { + switch (format) { + case ma_format_s16: return MA_PA_SAMPLE_S16BE; + case ma_format_s24: return MA_PA_SAMPLE_S24BE; + case ma_format_s32: return MA_PA_SAMPLE_S32BE; + case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; + default: break; + } + } + + /* Endian agnostic. */ + switch (format) { + case ma_format_u8: return MA_PA_SAMPLE_U8; + default: return MA_PA_SAMPLE_INVALID; + } +} +#endif + +static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) +{ + if (ma_is_little_endian()) { + switch (format) { + case MA_PA_SAMPLE_S16LE: return ma_format_s16; + case MA_PA_SAMPLE_S24LE: return ma_format_s24; + case MA_PA_SAMPLE_S32LE: return ma_format_s32; + case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; + default: break; + } + } else { + switch (format) { + case MA_PA_SAMPLE_S16BE: return ma_format_s16; + case MA_PA_SAMPLE_S24BE: return ma_format_s24; + case MA_PA_SAMPLE_S32BE: return ma_format_s32; + case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; + default: break; + } + } + + /* Endian agnostic. */ + switch (format) { + case MA_PA_SAMPLE_U8: return ma_format_u8; + default: return ma_format_unknown; + } +} + +static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) +{ + switch (position) + { + case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; + case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; + case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; + case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; + case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; + case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; + case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; + case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; + case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; + case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; + case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; + case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; + case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; + case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; + case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; + case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; + case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; + case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; + case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; + case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; + case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; + case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; + case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; + case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; + case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; + case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; + case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; + case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; + case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; + case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; + case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; + case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; + case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; + case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; + case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; + case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; + case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; + case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + default: return MA_CHANNEL_NONE; + } +} + +#if 0 +static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) +{ + switch (position) + { + case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; + case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; + case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; + case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; + case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; + case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; + case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; + case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; + case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; + case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; + case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; + case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; + case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; + case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; + case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; + case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; + case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; + case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; + case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; + default: return (ma_pa_channel_position_t)position; + } +} +#endif + +static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +{ + int resultPA; + ma_pa_operation_state_t state; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pOP != NULL); + + for (;;) { + state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); + if (state != MA_PA_OPERATION_RUNNING) { + break; /* Done. */ + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +{ + ma_result result; + + if (pOP == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + return result; +} + +static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) +{ + int resultPA; + ma_pa_context_state_t state; + + for (;;) { + state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); + if (state == MA_PA_CONTEXT_READY) { + break; /* Done. */ + } + + if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); + return MA_ERROR; + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + /* Should never get here. */ + return MA_SUCCESS; +} + +static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) +{ + int resultPA; + ma_pa_stream_state_t state; + + for (;;) { + state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); + if (state == MA_PA_STREAM_READY) { + break; /* Done. */ + } + + if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); + return MA_ERROR; + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) +{ + ma_result result; + ma_ptr pMainLoop; + ma_ptr pPulseContext; + + MA_ASSERT(ppMainLoop != NULL); + MA_ASSERT(ppPulseContext != NULL); + + /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ + pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); + return MA_FAILED_TO_INIT_BACKEND; + } + + pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); + if (pPulseContext == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return MA_FAILED_TO_INIT_BACKEND; + } + + /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ + result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ + result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + *ppMainLoop = pMainLoop; + *ppPulseContext = pPulseContext; + + return MA_SUCCESS; +} + + +static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_pa_sink_info* pInfoOut; + + if (endOfList > 0) { + return; + } + + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + + pInfoOut = (ma_pa_sink_info*)pUserData; + MA_ASSERT(pInfoOut != NULL); + + *pInfoOut = *pInfo; + + (void)pPulseContext; /* Unused. */ +} + +static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_pa_source_info* pInfoOut; + + if (endOfList > 0) { + return; + } + + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + + pInfoOut = (ma_pa_source_info*)pUserData; + MA_ASSERT(pInfoOut != NULL); + + *pInfoOut = *pInfo; + + (void)pPulseContext; /* Unused. */ +} + +#if 0 +static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_device* pDevice; + + if (endOfList > 0) { + return; + } + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); + + (void)pPulseContext; /* Unused. */ +} + +static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_device* pDevice; + + if (endOfList > 0) { + return; + } + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); + + (void)pPulseContext; /* Unused. */ +} +#endif + +static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) +{ + ma_pa_operation* pOP; + + pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); + if (pOP == NULL) { + return MA_ERROR; + } + + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); +} + +static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) +{ + ma_pa_operation* pOP; + + pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); + if (pOP == NULL) { + return MA_ERROR; + } + + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); +} + +static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) +{ + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pIndex != NULL); + + if (pIndex != NULL) { + *pIndex = (ma_uint32)-1; + } + + if (deviceType == ma_device_type_playback) { + ma_pa_sink_info sinkInfo; + result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pIndex != NULL) { + *pIndex = sinkInfo.index; + } + } + + if (deviceType == ma_device_type_capture) { + ma_pa_source_info sourceInfo; + result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pIndex != NULL) { + *pIndex = sourceInfo.index; + } + } + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_context* pContext; + ma_enum_devices_callback_proc callback; + void* pUserData; + ma_bool32 isTerminated; + ma_uint32 defaultDeviceIndexPlayback; + ma_uint32 defaultDeviceIndexCapture; +} ma_context_enumerate_devices_callback_data__pulse; + +static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_device_info deviceInfo; + + MA_ASSERT(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* The name from PulseAudio is the ID for miniaudio. */ + if (pSinkInfo->name != NULL) { + ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); + } + + /* The description from PulseAudio is the name for miniaudio. */ + if (pSinkInfo->description != NULL) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); + } + + if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { + deviceInfo.isDefault = MA_TRUE; + } + + pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); + + (void)pPulseContext; /* Unused. */ +} + +static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_device_info deviceInfo; + + MA_ASSERT(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* The name from PulseAudio is the ID for miniaudio. */ + if (pSourceInfo->name != NULL) { + ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); + } + + /* The description from PulseAudio is the name for miniaudio. */ + if (pSourceInfo->description != NULL) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); + } + + if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { + deviceInfo.isDefault = MA_TRUE; + } + + pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); + + (void)pPulseContext; /* Unused. */ +} + +static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result = MA_SUCCESS; + ma_context_enumerate_devices_callback_data__pulse callbackData; + ma_pa_operation* pOP = NULL; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + callbackData.pContext = pContext; + callbackData.callback = callback; + callbackData.pUserData = pUserData; + callbackData.isTerminated = MA_FALSE; + callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; + callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; + + /* We need to get the index of the default devices. */ + ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); + ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); + + /* Playback. */ + if (!callbackData.isTerminated) { + pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MA_ERROR; + goto done; + } + + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MA_SUCCESS) { + goto done; + } + } + + + /* Capture. */ + if (!callbackData.isTerminated) { + pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MA_ERROR; + goto done; + } + + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MA_SUCCESS) { + goto done; + } + } + +done: + return result; +} + + +typedef struct +{ + ma_device_info* pDeviceInfo; + ma_uint32 defaultDeviceIndex; + ma_bool32 foundDevice; +} ma_context_get_device_info_callback_data__pulse; + +static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + + if (endOfList > 0) { + return; + } + + MA_ASSERT(pData != NULL); + pData->foundDevice = MA_TRUE; + + if (pInfo->name != NULL) { + ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + /* + We're just reporting a single data format here. I think technically PulseAudio might support + all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to + report the "native" device format. + */ + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; + pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->nativeDataFormats[0].flags = 0; + pData->pDeviceInfo->nativeDataFormatCount = 1; + + if (pData->defaultDeviceIndex == pInfo->index) { + pData->pDeviceInfo->isDefault = MA_TRUE; + } + + (void)pPulseContext; /* Unused. */ +} + +static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + + if (endOfList > 0) { + return; + } + + MA_ASSERT(pData != NULL); + pData->foundDevice = MA_TRUE; + + if (pInfo->name != NULL) { + ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + /* + We're just reporting a single data format here. I think technically PulseAudio might support + all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to + report the "native" device format. + */ + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; + pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->nativeDataFormats[0].flags = 0; + pData->pDeviceInfo->nativeDataFormatCount = 1; + + if (pData->defaultDeviceIndex == pInfo->index) { + pData->pDeviceInfo->isDefault = MA_TRUE; + } + + (void)pPulseContext; /* Unused. */ +} + +static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result = MA_SUCCESS; + ma_context_get_device_info_callback_data__pulse callbackData; + ma_pa_operation* pOP = NULL; + const char* pDeviceName = NULL; + + MA_ASSERT(pContext != NULL); + + callbackData.pDeviceInfo = pDeviceInfo; + callbackData.foundDevice = MA_FALSE; + + if (pDeviceID != NULL) { + pDeviceName = pDeviceID->pulse; + } else { + pDeviceName = NULL; + } + + result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); + + if (deviceType == ma_device_type_playback) { + pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); + } else { + pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); + } + + if (pOP != NULL) { + ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); + } else { + result = MA_ERROR; + goto done; + } + + if (!callbackData.foundDevice) { + result = MA_NO_DEVICE; + goto done; + } + +done: + return result; +} + +static ma_result ma_device_uninit__pulse(ma_device* pDevice) +{ + ma_context* pContext; + + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } + + if (pDevice->type == ma_device_type_duplex) { + ma_duplex_rb_uninit(&pDevice->duplexRB); + } + + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); + + return MA_SUCCESS; +} + +static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) +{ + ma_pa_buffer_attr attr; + attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); + attr.tlength = attr.maxlength / periods; + attr.prebuf = (ma_uint32)-1; + attr.minreq = (ma_uint32)-1; + attr.fragsize = attr.maxlength / periods; + + return attr; +} + +static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) +{ + static int g_StreamCounter = 0; + char actualStreamName[256]; + + if (pStreamName != NULL) { + ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); + } else { + ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); + ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ + } + g_StreamCounter += 1; + + return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); +} + + +static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 bpf; + ma_uint32 deviceState; + ma_uint64 frameCount; + ma_uint64 framesProcessed; + + MA_ASSERT(pDevice != NULL); + + /* + Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio + can fire this callback before the stream has even started. Ridiculous. + */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + return; + } + + bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + MA_ASSERT(bpf > 0); + + frameCount = byteCount / bpf; + framesProcessed = 0; + + while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { + const void* pMappedPCMFrames; + size_t bytesMapped; + ma_uint64 framesMapped; + + int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); + if (pulseResult < 0) { + break; /* Failed to map. Abort. */ + } + + framesMapped = bytesMapped / bpf; + if (framesMapped > 0) { + if (pMappedPCMFrames != NULL) { + ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); + } else { + /* It's a hole. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); + } + + pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); + if (pulseResult < 0) { + break; /* Failed to drop the buffer. */ + } + + framesProcessed += framesMapped; + + } else { + /* Nothing was mapped. Just abort. */ + break; + } + } +} + +static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) +{ + ma_result result = MA_SUCCESS; + ma_uint64 framesProcessed = 0; + size_t bytesMapped; + ma_uint32 bpf; + ma_uint32 deviceState; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pStream != NULL); + + bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + MA_ASSERT(bpf > 0); + + deviceState = ma_device_get_state(pDevice); + + bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); + if (bytesMapped != (size_t)-1) { + if (bytesMapped > 0) { + ma_uint64 framesMapped; + void* pMappedPCMFrames; + int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); + if (pulseResult < 0) { + result = ma_result_from_pulse(pulseResult); + goto done; + } + + framesMapped = bytesMapped / bpf; + + if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ + ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); + } else { + /* Device is not started. Write silence. */ + ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); + } + + pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); + if (pulseResult < 0) { + result = ma_result_from_pulse(pulseResult); + goto done; /* Failed to write data to stream. */ + } + + framesProcessed += framesMapped; + } else { + result = MA_SUCCESS; /* No data available for writing. */ + goto done; + } + } else { + result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ + goto done; + } + +done: + if (pFramesProcessed != NULL) { + *pFramesProcessed = framesProcessed; + } + + return result; +} + +static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 bpf; + ma_uint64 frameCount; + ma_uint64 framesProcessed; + ma_uint32 deviceState; + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* + Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio + can fire this callback before the stream has even started. Ridiculous. + */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + return; + } + + bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + MA_ASSERT(bpf > 0); + + frameCount = byteCount / bpf; + framesProcessed = 0; + + while (framesProcessed < frameCount) { + ma_uint64 framesProcessedThisIteration; + + /* Don't keep trying to process frames if the device isn't started. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + break; + } + + result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); + if (result != MA_SUCCESS) { + break; + } + + framesProcessed += framesProcessedThisIteration; + } +} + +static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + int suspended; + + (void)pStream; + + suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); + + if (suspended < 0) { + return; + } + + if (suspended == 1) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); + ma_device__on_notification_stopped(pDevice); + } else { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); + ma_device__on_notification_started(pDevice); + } +} + +static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + + (void)pStream; + (void)pUserData; + + ma_device__on_notification_rerouted(pDevice); +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports from users where buffers of < ~20ms result glitches when running through + PipeWire. To work around this we're going to have to use a different default buffer size. + */ + const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; + const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; + + MA_ASSERT(nativeSampleRate != 0); + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } +} + +static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + /* + Notes for PulseAudio: + + - When both the period size in frames and milliseconds are 0, we default to miniaudio's + default buffer sizes rather than leaving it up to PulseAudio because I don't trust + PulseAudio to give us any kind of reasonable latency by default. + + - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this + flag, capture mode will just not work properly until you open another PulseAudio app. + */ + + ma_result result = MA_SUCCESS; + int error = 0; + const char* devPlayback = NULL; + const char* devCapture = NULL; + ma_format format = ma_format_unknown; + ma_uint32 channels = 0; + ma_uint32 sampleRate = 0; + ma_pa_sink_info sinkInfo; + ma_pa_source_info sourceInfo; + ma_pa_sample_spec ss; + ma_pa_channel_map cmap; + ma_pa_buffer_attr attr; + const ma_pa_sample_spec* pActualSS = NULL; + const ma_pa_buffer_attr* pActualAttr = NULL; + ma_uint32 iChannel; + ma_pa_stream_flags_t streamFlags; + + MA_ASSERT(pDevice != NULL); + MA_ZERO_OBJECT(&pDevice->pulse); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with the PulseAudio backend. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (pDescriptorPlayback->pDeviceID != NULL) { + devPlayback = pDescriptorPlayback->pDeviceID->pulse; + } + + format = pDescriptorPlayback->format; + channels = pDescriptorPlayback->channels; + sampleRate = pDescriptorPlayback->sampleRate; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (pDescriptorCapture->pDeviceID != NULL) { + devCapture = pDescriptorCapture->pDeviceID->pulse; + } + + format = pDescriptorCapture->format; + channels = pDescriptorCapture->channels; + sampleRate = pDescriptorCapture->sampleRate; + } + + + + result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); + return result; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); + goto on_error0; + } + + ss = sourceInfo.sample_spec; + cmap = sourceInfo.channel_map; + + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ + if (pDescriptorCapture->sampleRate != 0) { + ss.rate = pDescriptorCapture->sampleRate; + } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; + + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_is_little_endian()) { + ss.format = MA_PA_SAMPLE_FLOAT32LE; + } else { + ss.format = MA_PA_SAMPLE_FLOAT32BE; + } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); + } + if (ss.rate == 0) { + ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); + } + if (ss.channels == 0) { + ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); + } + + /* We now have enough information to calculate our actual period size in frames. */ + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); + + attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + + pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); + if (pDevice->pulse.pStreamCapture == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); + result = MA_ERROR; + goto on_error0; + } + + + /* The callback needs to be set before connecting the stream. */ + ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); + + /* State callback for checking when the device has been corked. */ + ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); + + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); + + + /* Connect after we've got all of our internal state set up. */ + if (devCapture != NULL) { + streamFlags |= MA_PA_STREAM_DONT_MOVE; + } + + error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); + if (error != MA_PA_OK) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); + result = ma_result_from_pulse(error); + goto on_error1; + } + + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (result != MA_SUCCESS) { + goto on_error2; + } + + + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualSS != NULL) { + ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); + } + + pDescriptorCapture->format = ma_format_from_pulse(ss.format); + pDescriptorCapture->channels = ss.channels; + pDescriptorCapture->sampleRate = ss.rate; + + if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); + result = MA_ERROR; + goto on_error4; + } + + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorCapture->channels > 2) { + for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorCapture->channels == 1) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorCapture->channels == 2) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + } + + + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + + if (attr.fragsize > 0) { + pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); + } else { + pDescriptorCapture->periodCount = 1; + } + + pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); + goto on_error2; + } + + ss = sinkInfo.sample_spec; + cmap = sinkInfo.channel_map; + + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + + /* Use the requested sample rate if one was specified. */ + if (pDescriptorPlayback->sampleRate != 0) { + ss.rate = pDescriptorPlayback->sampleRate; + } + + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_is_little_endian()) { + ss.format = MA_PA_SAMPLE_FLOAT32LE; + } else { + ss.format = MA_PA_SAMPLE_FLOAT32BE; + } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); + } + if (ss.rate == 0) { + ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); + } + if (ss.channels == 0) { + ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); + } + + /* We now have enough information to calculate the actual buffer size in frames. */ + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); + + attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + + pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); + if (pDevice->pulse.pStreamPlayback == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); + result = MA_ERROR; + goto on_error2; + } + + + /* + Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a + device state of ma_device_state_uninitialized. + */ + ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); + + /* State callback for checking when the device has been corked. */ + ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); + + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); + + + /* Connect after we've got all of our internal state set up. */ + if (devPlayback != NULL) { + streamFlags |= MA_PA_STREAM_DONT_MOVE; + } + + error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); + if (error != MA_PA_OK) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); + result = ma_result_from_pulse(error); + goto on_error3; + } + + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (result != MA_SUCCESS) { + goto on_error3; + } + + + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualSS != NULL) { + ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); + } + + pDescriptorPlayback->format = ma_format_from_pulse(ss.format); + pDescriptorPlayback->channels = ss.channels; + pDescriptorPlayback->sampleRate = ss.rate; + + if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); + result = MA_ERROR; + goto on_error4; + } + + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorPlayback->channels > 2) { + for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorPlayback->channels == 1) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorPlayback->channels == 2) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + } + + + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + + if (attr.tlength > 0) { + pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); + } else { + pDescriptorPlayback->periodCount = 1; + } + + pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + } + + + /* + We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main + part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for + us later on because that will only do it if it's a fully asynchronous backend - i.e. the + onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. + */ + if (pConfig->deviceType == ma_device_type_duplex) { + ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; + ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; + ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; + + result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); + goto on_error4; + } + } + + return MA_SUCCESS; + + +on_error4: + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } +on_error3: + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } +on_error2: + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } +on_error1: + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } +on_error0: + return result; +} + + +static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) +{ + ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; + MA_ASSERT(pIsSuccessful != NULL); + + *pIsSuccessful = (ma_bool32)success; + + (void)pStream; /* Unused. */ +} + +static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) +{ + ma_context* pContext = pDevice->pContext; + ma_bool32 wasSuccessful; + ma_pa_stream* pStream; + ma_pa_operation* pOP; + ma_result result; + + /* This should not be called with a duplex device type. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + wasSuccessful = MA_FALSE; + + pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); + MA_ASSERT(pStream != NULL); + + pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); + if (pOP == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); + return MA_ERROR; + } + + result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); + return result; + } + + if (!wasSuccessful) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); + return MA_ERROR; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__pulse(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + We need to fill some data before uncorking. Not doing this will result in the write callback + never getting fired. We're not going to abort if writing fails because I still want the device + to get uncorked. + */ + ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ + + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__pulse(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + Ideally we would drain the device here, but there's been cases where PulseAudio seems to be + broken on some systems to the point where no audio processing seems to happen. When this + happens, draining never completes and we get stuck here. For now I'm disabling draining of + the device so we don't just freeze the application. + */ + #if 0 + ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); + ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + #endif + + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop__pulse(ma_device* pDevice) +{ + int resultPA; + + MA_ASSERT(pDevice != NULL); + + /* NOTE: Don't start the device here. It'll be done at a higher level. */ + + /* + All data is handled through callbacks. All we need to do is iterate over the main loop and let + the callbacks deal with it. + */ + while (ma_device_get_state(pDevice) == ma_device_state_started) { + resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + if (resultPA < 0) { + break; + } + } + + /* NOTE: Don't stop the device here. It'll be done at a higher level. */ + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__pulse(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_pulseaudio); + + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); + + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; +#ifndef MA_NO_RUNTIME_LINKING + const char* libpulseNames[] = { + "libpulse.so", + "libpulse.so.0" + }; + size_t i; + + for (i = 0; i < ma_countof(libpulseNames); ++i) { + pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); + if (pContext->pulse.pulseSO != NULL) { + break; + } + } + + if (pContext->pulse.pulseSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); + pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); +#else + /* This strange assignment system is just for type safety. */ + ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; + ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; + ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; + ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; + ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; + ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; + ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; + ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; + ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; + ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; + ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; + ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; + ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; + ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; + ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; + ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; + ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; + ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; + ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; + ma_pa_context_new_proc _pa_context_new = pa_context_new; + ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; + ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; + ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; + ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; + ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; + ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; + ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; + ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; + ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; + ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; + ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; + ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; + ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; + ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; + ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; + ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; + ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; + ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; + ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; + ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; + ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; + ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; + ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; + ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; + ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; + ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; + ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; + ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; + ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; + ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; + ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; + ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; + ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; + ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; + ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; + ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; + ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; + ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; + ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; + ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; + ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; + + pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; + pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; + pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; + pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; + pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; + pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; + pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; + pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; + pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; + pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; + pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; + pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; + pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; + pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; + pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; + pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; + pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; + pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; + pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; + pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; + pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; + pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; + pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; + pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; + pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; + pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; + pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; + pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; + pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; + pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; + pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; + pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; + pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; + pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; + pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; + pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; + pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; + pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; + pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; + pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; +#endif + + /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ + pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); + if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { + return MA_OUT_OF_MEMORY; + } + + pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); + if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); + if (result != MA_SUCCESS) { + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + #ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); + #endif + return result; + } + + /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ + pCallbacks->onContextInit = ma_context_init__pulse; + pCallbacks->onContextUninit = ma_context_uninit__pulse; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; + pCallbacks->onDeviceInit = ma_device_init__pulse; + pCallbacks->onDeviceUninit = ma_device_uninit__pulse; + pCallbacks->onDeviceStart = ma_device_start__pulse; + pCallbacks->onDeviceStop = ma_device_stop__pulse; + pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ + pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; + + return MA_SUCCESS; +} +#endif + + +/****************************************************************************** + +JACK Backend + +******************************************************************************/ +#ifdef MA_HAS_JACK + +/* It is assumed jack.h is available when compile-time linking is being used. */ +#ifdef MA_NO_RUNTIME_LINKING +#include + +typedef jack_nframes_t ma_jack_nframes_t; +typedef jack_options_t ma_jack_options_t; +typedef jack_status_t ma_jack_status_t; +typedef jack_client_t ma_jack_client_t; +typedef jack_port_t ma_jack_port_t; +typedef JackProcessCallback ma_JackProcessCallback; +typedef JackBufferSizeCallback ma_JackBufferSizeCallback; +typedef JackShutdownCallback ma_JackShutdownCallback; +#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE +#define ma_JackNoStartServer JackNoStartServer +#define ma_JackPortIsInput JackPortIsInput +#define ma_JackPortIsOutput JackPortIsOutput +#define ma_JackPortIsPhysical JackPortIsPhysical +#else +typedef ma_uint32 ma_jack_nframes_t; +typedef int ma_jack_options_t; +typedef int ma_jack_status_t; +typedef struct ma_jack_client_t ma_jack_client_t; +typedef struct ma_jack_port_t ma_jack_port_t; +typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); +typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); +typedef void (* ma_JackShutdownCallback) (void* arg); +#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" +#define ma_JackNoStartServer 1 +#define ma_JackPortIsInput 1 +#define ma_JackPortIsOutput 2 +#define ma_JackPortIsPhysical 4 +#endif + +typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); +typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_client_name_size_proc) (void); +typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); +typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); +typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); +typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); +typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); +typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); +typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); +typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); +typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); +typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); +typedef void (* ma_jack_free_proc) (void* ptr); + +static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) +{ + size_t maxClientNameSize; + char clientName[256]; + ma_jack_status_t status; + ma_jack_client_t* pClient; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppClient != NULL); + + if (ppClient) { + *ppClient = NULL; + } + + maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ + ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); + + pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); + if (pClient == NULL) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + if (ppClient) { + *ppClient = pClient; + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + (void)cbResult; /* For silencing a static analysis warning. */ + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_jack_client_t* pClient; + ma_result result; + const char** ppPorts; + + MA_ASSERT(pContext != NULL); + + if (pDeviceID != NULL && pDeviceID->jack != 0) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + /* Jack only uses default devices. */ + pDeviceInfo->isDefault = MA_TRUE; + + /* Jack only supports f32 and has a specific channel count and sample rate. */ + pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; + + /* The channel count and sample rate can only be determined by opening the device. */ + result = ma_context_open_client__jack(pContext, &pClient); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; + } + + pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); + pDeviceInfo->nativeDataFormats[0].channels = 0; + + ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); + if (ppPorts == NULL) { + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { + pDeviceInfo->nativeDataFormats[0].channels += 1; + } + + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); + + (void)pContext; + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__jack(ma_device* pDevice) +{ + ma_context* pContext; + + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->jack.pClient != NULL) { + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +static void ma_device__jack_shutdown_callback(void* pUserData) +{ + /* JACK died. Stop the device. */ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_stop(pDevice); +} + +static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + if (pNewBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + + pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; + pDevice->playback.internalPeriodSizeInFrames = frameCount; + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + if (pNewBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + + pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; + pDevice->playback.internalPeriodSizeInFrames = frameCount; + } + + return 0; +} + +static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) +{ + ma_device* pDevice; + ma_context* pContext; + ma_uint32 iChannel; + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + /* Channels need to be interleaved. */ + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { + const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); + if (pSrc != NULL) { + float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; + ma_jack_nframes_t iFrame; + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += pDevice->capture.internalChannels; + pSrc += 1; + } + } + } + + ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); + + /* Channels need to be deinterleaved. */ + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { + float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); + if (pDst != NULL) { + const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; + ma_jack_nframes_t iFrame; + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += 1; + pSrc += pDevice->playback.internalChannels; + } + } + } + } + + return 0; +} + +static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + ma_uint32 periodSizeInFrames; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDevice != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* Only supporting default devices with JACK. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); + return MA_NO_DEVICE; + } + + /* No exclusive mode with the JACK backend. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Open the client. */ + result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; + } + + /* Callbacks. */ + if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); + + + /* The buffer size in frames can change. */ + periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; + const char** ppPorts; + + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = 0; + pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + if (ppPorts == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* Need to count the number of ports first so we can allocate some memory. */ + while (ppPorts[pDescriptorCapture->channels] != NULL) { + pDescriptorCapture->channels += 1; + } + + pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsCapture == NULL) { + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { + char name[64]; + ma_strcpy_s(name, sizeof(name), "capture"); + ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ + + pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); + if (pDevice->jack.ppPortsCapture[iPort] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + ma_device_uninit__jack(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } + + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ + + pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.pIntermediaryBufferCapture == NULL) { + ma_device_uninit__jack(pDevice); + return MA_OUT_OF_MEMORY; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; + const char** ppPorts; + + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = 0; + pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + if (ppPorts == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* Need to count the number of ports first so we can allocate some memory. */ + while (ppPorts[pDescriptorPlayback->channels] != NULL) { + pDescriptorPlayback->channels += 1; + } + + pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsPlayback == NULL) { + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { + char name[64]; + ma_strcpy_s(name, sizeof(name), "playback"); + ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ + + pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); + if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + ma_device_uninit__jack(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } + + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ + + pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { + ma_device_uninit__jack(pDevice); + return MA_OUT_OF_MEMORY; + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_start__jack(ma_device* pDevice) +{ + ma_context* pContext = pDevice->pContext; + int resultJACK; + size_t i; + + resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); + if (resultJACK != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); + return MA_FAILED_TO_START_BACKEND_DEVICE; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + if (ppServerPorts == NULL) { + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; + } + + for (i = 0; ppServerPorts[i] != NULL; ++i) { + const char* pServerPort = ppServerPorts[i]; + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); + + resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); + if (resultJACK != 0) { + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; + } + } + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + if (ppServerPorts == NULL) { + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; + } + + for (i = 0; ppServerPorts[i] != NULL; ++i) { + const char* pServerPort = ppServerPorts[i]; + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); + + resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); + if (resultJACK != 0) { + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; + } + } + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__jack(ma_device* pDevice) +{ + ma_context* pContext = pDevice->pContext; + + if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); + return MA_ERROR; + } + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__jack(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_jack); + + ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); + pContext->jack.pClientName = NULL; + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#ifndef MA_NO_RUNTIME_LINKING + const char* libjackNames[] = { +#if defined(MA_WIN32) + "libjack.dll", + "libjack64.dll" +#endif +#if defined(MA_UNIX) + "libjack.so", + "libjack.so.0" +#endif + }; + size_t i; + + for (i = 0; i < ma_countof(libjackNames); ++i) { + pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); + if (pContext->jack.jackSO != NULL) { + break; + } + } + + if (pContext->jack.jackSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); +#else + /* + This strange assignment system is here just to ensure type safety of miniaudio's function pointer + types. If anything differs slightly the compiler should throw a warning. + */ + ma_jack_client_open_proc _jack_client_open = jack_client_open; + ma_jack_client_close_proc _jack_client_close = jack_client_close; + ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; + ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; + ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; + ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; + ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; + ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; + ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; + ma_jack_activate_proc _jack_activate = jack_activate; + ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; + ma_jack_connect_proc _jack_connect = jack_connect; + ma_jack_port_register_proc _jack_port_register = jack_port_register; + ma_jack_port_name_proc _jack_port_name = jack_port_name; + ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; + ma_jack_free_proc _jack_free = jack_free; + + pContext->jack.jack_client_open = (ma_proc)_jack_client_open; + pContext->jack.jack_client_close = (ma_proc)_jack_client_close; + pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; + pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; + pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; + pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; + pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; + pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; + pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; + pContext->jack.jack_activate = (ma_proc)_jack_activate; + pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; + pContext->jack.jack_connect = (ma_proc)_jack_connect; + pContext->jack.jack_port_register = (ma_proc)_jack_port_register; + pContext->jack.jack_port_name = (ma_proc)_jack_port_name; + pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; + pContext->jack.jack_free = (ma_proc)_jack_free; +#endif + + if (pConfig->jack.pClientName != NULL) { + pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); + } + pContext->jack.tryStartServer = pConfig->jack.tryStartServer; + + /* + Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting + a temporary client. + */ + { + ma_jack_client_t* pDummyClient; + ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); + if (result != MA_SUCCESS) { + ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); + #ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); + #endif + return MA_NO_BACKEND; + } + + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); + } + + + pCallbacks->onContextInit = ma_context_init__jack; + pCallbacks->onContextUninit = ma_context_uninit__jack; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; + pCallbacks->onDeviceInit = ma_device_init__jack; + pCallbacks->onDeviceUninit = ma_device_uninit__jack; + pCallbacks->onDeviceStart = ma_device_start__jack; + pCallbacks->onDeviceStop = ma_device_stop__jack; + pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* JACK */ + + + +/****************************************************************************** + +Core Audio Backend + +References +========== +- Technical Note TN2091: Device input using the HAL Output Audio Unit + https://developer.apple.com/library/archive/technotes/tn2091/_index.html + +******************************************************************************/ +#ifdef MA_HAS_COREAUDIO +#include + +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 + #define MA_APPLE_MOBILE + #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + #define MA_APPLE_TV + #endif + #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + #define MA_APPLE_WATCH + #endif + #if __has_feature(objc_arc) + #define MA_BRIDGE_TRANSFER __bridge_transfer + #define MA_BRIDGE_RETAINED __bridge_retained + #else + #define MA_BRIDGE_TRANSFER + #define MA_BRIDGE_RETAINED + #endif +#else + #define MA_APPLE_DESKTOP +#endif + +#if defined(MA_APPLE_DESKTOP) +#include +#else +#include +#endif + +#include + +/* CoreFoundation */ +typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); +typedef void (* ma_CFRelease_proc)(CFTypeRef cf); + +/* CoreAudio */ +#if defined(MA_APPLE_DESKTOP) +typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); +typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); +typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +#endif + +/* AudioToolbox */ +typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); +typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); +typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); +typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); +typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); +typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); +typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); +typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); + + +#define MA_COREAUDIO_OUTPUT_BUS 0 +#define MA_COREAUDIO_INPUT_BUS 1 + +#if defined(MA_APPLE_DESKTOP) +static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); +#endif + +/* +Core Audio + +So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation +apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose +needing to figure out how this darn thing works, I'm going to outline a few things here. + +Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be +able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen +that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent +and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the +distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. + +Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When +retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific +data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the +devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be +the central APIs for retrieving information about the system and specific devices. + +To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a +structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" +which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is +typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and +kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to +kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. + +Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size +of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property +address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the +size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of +AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. +*/ + +#if defined(MA_APPLE_MOBILE) +static void ma_device__on_notification_interruption_began(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); +} + +static void ma_device__on_notification_interruption_ended(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); +} +#endif + +static ma_result ma_result_from_OSStatus(OSStatus status) +{ + switch (status) + { + case noErr: return MA_SUCCESS; + #if defined(MA_APPLE_DESKTOP) + case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; + case kAudioHardwareUnspecifiedError: return MA_ERROR; + case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; + case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; + case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; + case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; + case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; + case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; + case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; + case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; + case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; + #endif + default: return MA_ERROR; + } +} + +#if 0 +static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) +{ + switch (bit) + { + case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; + case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; + case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; + case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; + case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; + case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return MA_CHANNEL_NONE; + } +} +#endif + +static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) +{ + MA_ASSERT(pDescription != NULL); + MA_ASSERT(pFormatOut != NULL); + + *pFormatOut = ma_format_unknown; /* Safety. */ + + /* There's a few things miniaudio doesn't support. */ + if (pDescription->mFormatID != kAudioFormatLinearPCM) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* We don't support any non-packed formats that are aligned high. */ + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Only supporting native-endian. */ + if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ + /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { + return MA_FORMAT_NOT_SUPPORTED; + }*/ + + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { + if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = ma_format_f32; + return MA_SUCCESS; + } + } else { + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { + if (pDescription->mBitsPerChannel == 16) { + *pFormatOut = ma_format_s16; + return MA_SUCCESS; + } else if (pDescription->mBitsPerChannel == 24) { + if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { + *pFormatOut = ma_format_s24; + return MA_SUCCESS; + } else { + if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { + /* TODO: Implement ma_format_s24_32. */ + /**pFormatOut = ma_format_s24_32;*/ + /*return MA_SUCCESS;*/ + return MA_FORMAT_NOT_SUPPORTED; + } + } + } else if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = ma_format_s32; + return MA_SUCCESS; + } + } else { + if (pDescription->mBitsPerChannel == 8) { + *pFormatOut = ma_format_u8; + return MA_SUCCESS; + } + } + } + + /* Getting here means the format is not supported. */ + return MA_FORMAT_NOT_SUPPORTED; +} + +#if defined(MA_APPLE_DESKTOP) +static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) +{ + switch (label) + { + case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; + case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; + case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; + case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; + case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; + case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; + case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; + case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; + case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; + case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; + case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; + case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; + case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; + case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; + case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; + case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; + case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; + case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; + case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; + case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; + case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; + case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; + case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; + case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; + case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; + case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; + case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; + case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; + + #if 0 /* Introduced in a later version of macOS. */ + case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; + case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; + case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; + case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; + case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; + case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; + case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; + case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; + case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; + case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; + case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; + case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; + case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; + case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; + case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; + case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; + case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; + case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; + #endif + + default: return MA_CHANNEL_NONE; + } +} + +static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) +{ + MA_ASSERT(pChannelLayout != NULL); + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + UInt32 iChannel; + for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { + pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); + } + } else +#if 0 + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + /* This is the same kind of system that's used by Windows audio APIs. */ + UInt32 iChannel = 0; + UInt32 iBit; + AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; + for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { + AudioChannelBitmap bit = bitmap & (1 << iBit); + if (bit != 0) { + pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); + } + } + } else +#endif + { + /* + Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should + be updated to determine the mapping based on the tag. + */ + UInt32 channelCount; + + /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ + if (channelMapCap > 0xFFFFFFFF) { + channelMapCap = 0xFFFFFFFF; + } + + channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); + + switch (pChannelLayout->mChannelLayoutTag) + { + case kAudioChannelLayoutTag_Mono: + case kAudioChannelLayoutTag_Stereo: + case kAudioChannelLayoutTag_StereoHeadphones: + case kAudioChannelLayoutTag_MatrixStereo: + case kAudioChannelLayoutTag_MidSide: + case kAudioChannelLayoutTag_XY: + case kAudioChannelLayoutTag_Binaural: + case kAudioChannelLayoutTag_Ambisonic_B_Format: + { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } break; + + case kAudioChannelLayoutTag_Octagonal: + { + pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; + pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Hexagonal: + { + pChannelMap[5] = MA_CHANNEL_BACK_CENTER; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Pentagonal: + { + pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Quadraphonic: + { + pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; + pChannelMap[2] = MA_CHANNEL_BACK_LEFT; + pChannelMap[1] = MA_CHANNEL_RIGHT; + pChannelMap[0] = MA_CHANNEL_LEFT; + } break; + + /* TODO: Add support for more tags here. */ + + default: + { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } break; + } + } + + return MA_SUCCESS; +} + +#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ + (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain +#else +/* kAudioObjectPropertyElementMaster is deprecated. */ +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster +#endif + +static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddressDevices; + UInt32 deviceObjectsDataSize; + OSStatus status; + AudioObjectID* pDeviceObjectIDs; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceCount != NULL); + MA_ASSERT(ppDeviceObjectIDs != NULL); + + /* Safety. */ + *pDeviceCount = 0; + *ppDeviceObjectIDs = NULL; + + propAddressDevices.mSelector = kAudioHardwarePropertyDevices; + propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); + if (pDeviceObjectIDs == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); + if (status != noErr) { + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); + *ppDeviceObjectIDs = pDeviceObjectIDs; + + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + propAddress.mSelector = kAudioDevicePropertyDeviceUID; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(*pUID); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + CFStringRef uid; + ma_result result; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); + if (result != MA_SUCCESS) { + return result; + } + + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MA_ERROR; + } + + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + AudioObjectPropertyAddress propAddress; + CFStringRef deviceName = NULL; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(deviceName); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MA_ERROR; + } + + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); + return MA_SUCCESS; +} + +static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioBufferList* pBufferList; + ma_bool32 isSupported; + + MA_ASSERT(pContext != NULL); + + /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ + propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; + propAddress.mScope = scope; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return MA_FALSE; + } + + pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pBufferList == NULL) { + return MA_FALSE; /* Out of memory. */ + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); + if (status != noErr) { + ma_free(pBufferList, &pContext->allocationCallbacks); + return MA_FALSE; + } + + isSupported = MA_FALSE; + if (pBufferList->mNumberBuffers > 0) { + isSupported = MA_TRUE; + } + + ma_free(pBufferList, &pContext->allocationCallbacks); + return isSupported; +} + +static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) +{ + return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); +} + +static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) +{ + return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); +} + + +static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioStreamRangedDescription* pDescriptions; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDescriptionCount != NULL); + MA_ASSERT(ppDescriptions != NULL); + + /* + TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My + MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. + */ + propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pDescriptions == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); + if (status != noErr) { + ma_free(pDescriptions, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pDescriptionCount = dataSize / sizeof(*pDescriptions); + *ppDescriptions = pDescriptions; + return MA_SUCCESS; +} + + +static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioChannelLayout* pChannelLayout; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppChannelLayout != NULL); + + *ppChannelLayout = NULL; /* Safety. */ + + propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pChannelLayout == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); + if (status != noErr) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *ppChannelLayout = pChannelLayout; + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) +{ + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pChannelCount != NULL); + + *pChannelCount = 0; /* Safety. */ + + result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MA_SUCCESS) { + return result; + } + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + *pChannelCount = pChannelLayout->mNumberChannelDescriptions; + } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); + } else { + *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return MA_SUCCESS; +} + +#if 0 +static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) +{ + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MA_SUCCESS) { + return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ + } + + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; +} +#endif + +static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioValueRange* pSampleRateRanges; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pSampleRateRangesCount != NULL); + MA_ASSERT(ppSampleRateRanges != NULL); + + /* Safety. */ + *pSampleRateRangesCount = 0; + *ppSampleRateRanges = NULL; + + propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pSampleRateRanges == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); + if (status != noErr) { + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); + *ppSampleRateRanges = pSampleRateRanges; + return MA_SUCCESS; +} + +#if 0 +static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) +{ + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pSampleRateOut != NULL); + + *pSampleRateOut = 0; /* Safety. */ + + result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MA_SUCCESS) { + return result; + } + + if (sampleRateRangeCount == 0) { + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_ERROR; /* Should never hit this case should we? */ + } + + if (sampleRateIn == 0) { + /* Search in order of miniaudio's preferred priority. */ + UInt32 iMALSampleRate; + for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { + ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; + UInt32 iCASampleRate; + for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { + AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; + if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { + *pSampleRateOut = malSampleRate; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } + + /* + If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this + case we just fall back to the first one reported by Core Audio. + */ + MA_ASSERT(sampleRateRangeCount > 0); + + *pSampleRateOut = pSampleRateRanges[0].mMinimum; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } else { + /* Find the closest match to this sample rate. */ + UInt32 currentAbsoluteDifference = INT32_MAX; + UInt32 iCurrentClosestRange = (UInt32)-1; + UInt32 iRange; + for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { + if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { + *pSampleRateOut = sampleRateIn; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } else { + UInt32 absoluteDifference; + if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { + absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; + } else { + absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; + } + + if (currentAbsoluteDifference > absoluteDifference) { + currentAbsoluteDifference = absoluteDifference; + iCurrentClosestRange = iRange; + } + } + } + + MA_ASSERT(iCurrentClosestRange != (UInt32)-1); + + *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + + /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ + /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ + /*return MA_ERROR;*/ +} +#endif + +static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) +{ + AudioObjectPropertyAddress propAddress; + AudioValueRange bufferSizeRange; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pBufferSizeInFramesOut != NULL); + + *pBufferSizeInFramesOut = 0; /* Safety. */ + + propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(bufferSizeRange); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + /* This is just a clamp. */ + if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { + *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; + } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { + *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; + } else { + *pBufferSizeInFramesOut = bufferSizeInFramesIn; + } + + return MA_SUCCESS; +} + +static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) +{ + ma_result result; + ma_uint32 chosenBufferSizeInFrames; + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); + if (result != MA_SUCCESS) { + return result; + } + + /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ + propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); + + /* Get the actual size of the buffer. */ + dataSize = sizeof(*pPeriodSizeInOut); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + *pPeriodSizeInOut = chosenBufferSizeInFrames; + return MA_SUCCESS; +} + +static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) +{ + AudioObjectPropertyAddress propAddressDefaultDevice; + UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); + AudioObjectID defaultDeviceObjectID; + OSStatus status; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceObjectID != NULL); + + /* Safety. */ + *pDeviceObjectID = 0; + + propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + if (deviceType == ma_device_type_playback) { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + } else { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; + } + + defaultDeviceObjectIDSize = sizeof(AudioObjectID); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); + if (status == noErr) { + *pDeviceObjectID = defaultDeviceObjectID; + return MA_SUCCESS; + } + + /* If we get here it means we couldn't find the device. */ + return MA_NO_DEVICE; +} + +static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceObjectID != NULL); + + /* Safety. */ + *pDeviceObjectID = 0; + + if (pDeviceID == NULL) { + /* Default device. */ + return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); + } else { + /* Explicit device. */ + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + ma_result result; + UInt32 iDevice; + + result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MA_SUCCESS) { + return result; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + + char uid[256]; + if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { + continue; + } + + if (deviceType == ma_device_type_playback) { + if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } else { + if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } + } + + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + } + + /* If we get here it means we couldn't find the device. */ + return MA_NO_DEVICE; +} + + +static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) +{ + UInt32 deviceFormatDescriptionCount; + AudioStreamRangedDescription* pDeviceFormatDescriptions; + ma_result result; + ma_uint32 desiredSampleRate; + ma_uint32 desiredChannelCount; + ma_format desiredFormat; + AudioStreamBasicDescription bestDeviceFormatSoFar; + ma_bool32 hasSupportedFormat; + UInt32 iFormat; + + result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); + if (result != MA_SUCCESS) { + return result; + } + + desiredSampleRate = sampleRate; + if (desiredSampleRate == 0) { + desiredSampleRate = pOrigFormat->mSampleRate; + } + + desiredChannelCount = channels; + if (desiredChannelCount == 0) { + desiredChannelCount = pOrigFormat->mChannelsPerFrame; + } + + desiredFormat = format; + if (desiredFormat == ma_format_unknown) { + result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); + if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { + desiredFormat = g_maFormatPriorities[0]; + } + } + + /* + If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next + loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. + */ + MA_ZERO_OBJECT(&bestDeviceFormatSoFar); + + hasSupportedFormat = MA_FALSE; + for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + ma_format formatFromDescription; + ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); + if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { + hasSupportedFormat = MA_TRUE; + bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; + break; + } + } + + if (!hasSupportedFormat) { + ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); + return MA_FORMAT_NOT_SUPPORTED; + } + + + for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; + ma_format thisSampleFormat; + ma_result formatResult; + ma_format bestSampleFormatSoFar; + + /* If the format is not supported by miniaudio we need to skip this one entirely. */ + formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); + if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { + continue; /* The format is not supported by miniaudio. Skip. */ + } + + ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); + + /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ + if (thisDeviceFormat.mSampleRate != desiredSampleRate) { + /* + The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format + so far has an equal sample rate we can just ignore this one. + */ + if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { + continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ + } else { + /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ + if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { + /* This format has a different sample rate _and_ a different channel count. */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; /* No change to the best format. */ + } else { + /* + Both this format and the best so far have different sample rates and different channel counts. Whichever has the + best format is the new best. + */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format. */ + } + } + } else { + /* This format has a different sample rate but the desired channel count. */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } else { + /* This format has the desired channel count, but the best so far does not. We have a new best. */ + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } + } + } + } else { + /* + The sample rates match which makes this format a very high priority contender. If the best format so far has a different + sample rate it needs to be replaced with this one. + */ + if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ + if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { + /* + In this case this format has the same channel count as what the client is requesting. If the best format so far has + a different count, this one becomes the new best. + */ + if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ + if (thisSampleFormat == desiredFormat) { + bestDeviceFormatSoFar = thisDeviceFormat; + break; /* Found the exact match. */ + } else { + /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } + } + } else { + /* + In this case the channel count is different to what the client has requested. If the best so far has the same channel + count as the requested count then it remains the best. + */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; + } else { + /* + This is the case where both have the same sample rate (good) but different channel counts. Right now both have about + the same priority, but we need to compare the format now. + */ + if (thisSampleFormat == bestSampleFormatSoFar) { + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } + } + } + } + } + } + + *pFormat = bestDeviceFormatSoFar; + + ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); + return MA_SUCCESS; +} + +static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) +{ + AudioUnitScope deviceScope; + AudioUnitElement deviceBus; + UInt32 channelLayoutSize; + OSStatus status; + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + + if (deviceType == ma_device_type_playback) { + deviceScope = kAudioUnitScope_Input; + deviceBus = MA_COREAUDIO_OUTPUT_BUS; + } else { + deviceScope = kAudioUnitScope_Output; + deviceBus = MA_COREAUDIO_INPUT_BUS; + } + + status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); + if (pChannelLayout == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); + if (status != noErr) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return MA_SUCCESS; +} +#endif /* MA_APPLE_DESKTOP */ + + +#if !defined(MA_APPLE_DESKTOP) +static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) +{ + MA_ZERO_OBJECT(pInfo); + ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); + ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); +} +#endif + +static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ +#if defined(MA_APPLE_DESKTOP) + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + AudioObjectID defaultDeviceObjectIDPlayback; + AudioObjectID defaultDeviceObjectIDCapture; + ma_result result; + UInt32 iDevice; + + ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ + ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ + + result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MA_SUCCESS) { + return result; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + ma_device_info info; + + MA_ZERO_OBJECT(&info); + if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { + continue; + } + if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { + continue; + } + + if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (deviceObjectID == defaultDeviceObjectIDPlayback) { + info.isDefault = MA_TRUE; + } + + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { + break; + } + } + if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (deviceObjectID == defaultDeviceObjectIDCapture) { + info.isDefault = MA_TRUE; + } + + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { + break; + } + } + } + + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); +#else + ma_device_info info; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; + + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { + return MA_SUCCESS; + } + } + + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { + return MA_SUCCESS; + } + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + + MA_ASSERT(pContext != NULL); + +#if defined(MA_APPLE_DESKTOP) + /* Desktop */ + { + AudioObjectID deviceObjectID; + AudioObjectID defaultDeviceObjectID; + UInt32 streamDescriptionCount; + AudioStreamRangedDescription* pStreamDescriptions; + UInt32 iStreamDescription; + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + + ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ + + result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); + if (result != MA_SUCCESS) { + return result; + } + + if (deviceObjectID == defaultDeviceObjectID) { + pDeviceInfo->isDefault = MA_TRUE; + } + + /* + There could be a large number of permutations here. Fortunately there is only a single channel count + being reported which reduces this quite a bit. For sample rates we're only reporting those that are + one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into + our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen + if some driver performs software data conversion and therefore reports every possible format and + sample rate. + */ + pDeviceInfo->nativeDataFormatCount = 0; + + /* Formats. */ + { + ma_format uniqueFormats[ma_format_count]; + ma_uint32 uniqueFormatCount = 0; + ma_uint32 channels; + + /* Channels. */ + result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); + if (result != MA_SUCCESS) { + return result; + } + + /* Formats. */ + result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); + if (result != MA_SUCCESS) { + return result; + } + + for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { + ma_format format; + ma_bool32 hasFormatBeenHandled = MA_FALSE; + ma_uint32 iOutputFormat; + ma_uint32 iSampleRate; + + result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); + if (result != MA_SUCCESS) { + continue; + } + + MA_ASSERT(format != ma_format_unknown); + + /* Make sure the format isn't already in the output list. */ + for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { + if (uniqueFormats[iOutputFormat] == format) { + hasFormatBeenHandled = MA_TRUE; + break; + } + } + + /* If we've already handled this format just skip it. */ + if (hasFormatBeenHandled) { + continue; + } + + uniqueFormats[uniqueFormatCount] = format; + uniqueFormatCount += 1; + + /* Sample Rates */ + result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MA_SUCCESS) { + return result; + } + + /* + Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are + between this range. + */ + for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { + ma_uint32 iStandardSampleRate; + for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; + if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { + /* We have a new data format. Add it to the list. */ + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + + if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { + break; /* No more room for any more formats. */ + } + } + } + } + + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + + if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { + break; /* No more room for any more formats. */ + } + } + + ma_free(pStreamDescriptions, &pContext->allocationCallbacks); + } + } +#else + /* Mobile */ + { + AudioComponentDescription desc; + AudioComponent component; + AudioUnit audioUnit; + OSStatus status; + AudioUnitScope formatScope; + AudioUnitElement formatElement; + AudioStreamBasicDescription bestFormat; + UInt32 propSize; + + /* We want to ensure we use a consistent device name to device enumeration. */ + if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { + ma_bool32 found = MA_FALSE; + if (deviceType == ma_device_type_playback) { + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } else { + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } + + if (!found) { + return MA_DOES_NOT_EXIST; + } + } else { + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } + + + /* + Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is + reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to + retrieve from the AVAudioSession shared instance. + */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (component == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; + + propSize = sizeof(bestFormat); + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + return ma_result_from_OSStatus(status); + } + + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + audioUnit = NULL; + + /* Only a single format is being reported for iOS. */ + pDeviceInfo->nativeDataFormatCount = 1; + + result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); + if (result != MA_SUCCESS) { + return result; + } + + pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; + + /* + It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do + this we just get the shared instance and inspect. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; + } + } +#endif + + (void)pDeviceInfo; /* Unused. */ + return MA_SUCCESS; +} + +static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) +{ + AudioBufferList* pBufferList; + UInt32 audioBufferSizeInBytes; + size_t allocationSize; + + MA_ASSERT(sizeInFrames > 0); + MA_ASSERT(format != ma_format_unknown); + MA_ASSERT(channels > 0); + + allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ + if (layout == ma_stream_layout_interleaved) { + /* Interleaved case. This is the simple case because we just have one buffer. */ + allocationSize += sizeof(AudioBuffer) * 1; + } else { + /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ + allocationSize += sizeof(AudioBuffer) * channels; + } + + allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); + + pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); + if (pBufferList == NULL) { + return NULL; + } + + audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); + + if (layout == ma_stream_layout_interleaved) { + pBufferList->mNumberBuffers = 1; + pBufferList->mBuffers[0].mNumberChannels = channels; + pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; + pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); + } else { + ma_uint32 iBuffer; + pBufferList->mNumberBuffers = channels; + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + pBufferList->mBuffers[iBuffer].mNumberChannels = 1; + pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; + pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); + } + } + + return pBufferList; +} + +static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(format != ma_format_unknown); + MA_ASSERT(channels > 0); + + /* Only resize the buffer if necessary. */ + if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { + AudioBufferList* pNewAudioBufferList; + + pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); + if (pNewAudioBufferList == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* At this point we'll have a new AudioBufferList and we can free the old one. */ + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; + } + + /* Getting here means the capacity of the audio is fine. */ + return MA_SUCCESS; +} + + +static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_stream_layout layout; + + MA_ASSERT(pDevice != NULL); + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ + + /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ + layout = ma_stream_layout_interleaved; + if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { + layout = ma_stream_layout_deinterleaved; + } + + if (layout == ma_stream_layout_interleaved) { + /* For now we can assume everything is interleaved. */ + UInt32 iBuffer; + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { + ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + if (frameCountForThisBuffer > 0) { + ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); + } + + /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } else { + /* + This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just + output silence here. + */ + MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } + } + } else { + /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ + MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ + + /* + For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something + very strange has happened and we're not going to support it. + */ + if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { + ma_uint8 tempBuffer[4096]; + UInt32 iBuffer; + + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { + ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); + ma_uint32 framesRemaining = frameCountPerBuffer; + + while (framesRemaining > 0) { + void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; + ma_uint32 iChannel; + ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + if (framesToRead > framesRemaining) { + framesToRead = framesRemaining; + } + + ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); + + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { + ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); + } + + ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); + + framesRemaining -= framesToRead; + } + } + } + } + + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + (void)frameCount; + + return noErr; +} + +static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) +{ + ma_device* pDevice = (ma_device*)pUserData; + AudioBufferList* pRenderedBufferList; + ma_result result; + ma_stream_layout layout; + ma_uint32 iBuffer; + OSStatus status; + + MA_ASSERT(pDevice != NULL); + + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + MA_ASSERT(pRenderedBufferList); + + /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ + layout = ma_stream_layout_interleaved; + if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { + layout = ma_stream_layout_deinterleaved; + } + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ + + /* + There has been a situation reported where frame count passed into this function is greater than the capacity of + our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, + so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the + number of frames requested by this callback. + */ + result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); + return noErr; + } + + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + MA_ASSERT(pRenderedBufferList); + + /* + When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes + that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer + being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a + problem when a future call to this callback specifies a larger number of frames. + + To work around this we need to explicitly set the size of each buffer to their respective size in bytes. + */ + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { + pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; + } + + status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); + if (status != noErr) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); + return status; + } + + if (layout == ma_stream_layout_interleaved) { + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { + if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { + ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } else { + /* + This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. + */ + ma_uint8 silentBuffer[4096]; + ma_uint32 framesRemaining; + + MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); + + framesRemaining = frameCount; + while (framesRemaining > 0) { + ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + if (framesToSend > framesRemaining) { + framesToSend = framesRemaining; + } + + ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); + + framesRemaining -= framesToSend; + } + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } + } + } else { + /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ + MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ + + /* + For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something + very strange has happened and we're not going to support it. + */ + if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { + ma_uint8 tempBuffer[4096]; + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { + ma_uint32 framesRemaining = frameCount; + while (framesRemaining > 0) { + void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; + ma_uint32 iChannel; + ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + if (framesToSend > framesRemaining) { + framesToSend = framesRemaining; + } + + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { + ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); + } + + ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); + ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); + + framesRemaining -= framesToSend; + } + } + } + } + + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + (void)frameCount; + (void)pUnusedBufferList; + + return noErr; +} + +static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ + if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + return; + } + + /* + There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like + AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) + can try waiting on the same lock. I'm going to try working around this by not calling any Core + Audio APIs in the callback when the device has been stopped or uninitialized. + */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { + ma_device__on_notification_stopped(pDevice); + } else { + UInt32 isRunning; + UInt32 isRunningSize = sizeof(isRunning); + OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); + if (status != noErr) { + goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ + } + + 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 (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { + /* + 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 stopped event and thinking that the device has stopped when it + hasn't!). + */ + if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + goto done; + } + + /* + 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 stopped callback needs to be posted. + */ + goto done; + } + + /* Getting here means we need to stop the device. */ + ma_device__on_notification_stopped(pDevice); + } + } + + (void)propertyID; /* Unused. */ + +done: + /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ + ma_event_signal(&pDevice->coreaudio.stopEvent); +} + +#if defined(MA_APPLE_DESKTOP) +static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ +static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; +static ma_mutex g_DeviceTrackingMutex_CoreAudio; +static ma_device** g_ppTrackedDevices_CoreAudio = NULL; +static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; +static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; + +static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) +{ + ma_device_type deviceType; + + /* Not sure if I really need to check this, but it makes me feel better. */ + if (addressCount == 0) { + return noErr; + } + + if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { + deviceType = ma_device_type_playback; + } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { + deviceType = ma_device_type_capture; + } else { + return noErr; /* Should never hit this. */ + } + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + ma_result reinitResult; + ma_device* pDevice; + + pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; + if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback) { + pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; + } else { + pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; + } + + if (reinitResult == MA_SUCCESS) { + ma_device__post_init_setup(pDevice, deviceType); + + /* Restart the device if required. If this fails we need to stop the device entirely. */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + OSStatus status; + if (deviceType == ma_device_type_playback) { + status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } else if (deviceType == ma_device_type_capture) { + status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } + } + + ma_device__on_notification_rerouted(pDevice); + } + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + /* Unused parameters. */ + (void)objectID; + (void)pUserData; + + return noErr; +} + +static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + /* Don't do anything if we've already initializd device tracking. */ + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + } + g_DeviceTrackingInitCounter_CoreAudio += 1; + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + if (g_DeviceTrackingInitCounter_CoreAudio > 0) + g_DeviceTrackingInitCounter_CoreAudio -= 1; + + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + /* At this point there should be no tracked devices. If not there's an error somewhere. */ + if (g_ppTrackedDevices_CoreAudio != NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + return MA_INVALID_OPERATION; + } + + ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); + } + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__track__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + /* Allocate memory if required. */ + if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { + ma_uint32 newCap; + ma_device** ppNewDevices; + + newCap = g_TrackedDeviceCap_CoreAudio * 2; + if (newCap == 0) { + newCap = 1; + } + + ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); + if (ppNewDevices == NULL) { + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + return MA_OUT_OF_MEMORY; + } + + g_ppTrackedDevices_CoreAudio = ppNewDevices; + g_TrackedDeviceCap_CoreAudio = newCap; + } + + g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; + g_TrackedDeviceCount_CoreAudio += 1; + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { + /* We've found the device. We now need to remove it from the list. */ + ma_uint32 jDevice; + for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { + g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; + } + + g_TrackedDeviceCount_CoreAudio -= 1; + + /* If there's nothing else in the list we need to free memory. */ + if (g_TrackedDeviceCount_CoreAudio == 0) { + ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); + g_ppTrackedDevices_CoreAudio = NULL; + g_TrackedDeviceCap_CoreAudio = 0; + } + + break; + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} +#endif + +#if defined(MA_APPLE_MOBILE) +@interface ma_ios_notification_handler:NSObject { + ma_device* m_pDevice; +} +@end + +@implementation ma_ios_notification_handler +-(id)init:(ma_device*)pDevice +{ + self = [super init]; + m_pDevice = pDevice; + + /* For route changes. */ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; + + /* For interruptions. */ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + + return self; +} + +-(void)dealloc +{ + [self remove_handler]; + + #if defined(__has_feature) + #if !__has_feature(objc_arc) + [super dealloc]; + #endif + #endif +} + +-(void)remove_handler +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; +} + +-(void)handle_interruption:(NSNotification*)pNotification +{ + NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) + { + case AVAudioSessionInterruptionTypeBegan: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); + + /* + Core Audio will have stopped the internal device automatically, but we need explicitly + stop it at a higher level to ensure miniaudio-specific state is updated for consistency. + */ + ma_device_stop(m_pDevice); + + /* + Fire the notification after the device has been stopped to ensure it's in the correct + state when the notification handler is invoked. + */ + ma_device__on_notification_interruption_began(m_pDevice); + } break; + + case AVAudioSessionInterruptionTypeEnded: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); + ma_device__on_notification_interruption_ended(m_pDevice); + } break; + } +} + +-(void)handle_route_change:(NSNotification*)pNotification +{ + AVAudioSession* pSession = [AVAudioSession sharedInstance]; + + NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; + switch (reason) + { + case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); + } break; + + case AVAudioSessionRouteChangeReasonNewDeviceAvailable: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); + } break; + + case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); + } break; + + case AVAudioSessionRouteChangeReasonWakeFromSleep: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); + } break; + + case AVAudioSessionRouteChangeReasonOverride: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); + } break; + + case AVAudioSessionRouteChangeReasonCategoryChange: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); + } break; + + case AVAudioSessionRouteChangeReasonUnknown: + default: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); + } break; + } + + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); + + /* Let the application know about the route change. */ + ma_device__on_notification_rerouted(m_pDevice); +} +@end +#endif + +static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); + +#if defined(MA_APPLE_DESKTOP) + /* + Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll + just gracefully ignore it. + */ + ma_device__untrack__coreaudio(pDevice); +#endif +#if defined(MA_APPLE_MOBILE) + if (pDevice->coreaudio.pNotificationHandler != NULL) { + ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; + [pNotificationHandler remove_handler]; + } +#endif + + if (pDevice->coreaudio.audioUnitCapture != NULL) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + if (pDevice->coreaudio.audioUnitPlayback != NULL) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +typedef struct +{ + ma_bool32 allowNominalSampleRateChange; + + /* Input. */ + ma_format formatIn; + ma_uint32 channelsIn; + ma_uint32 sampleRateIn; + ma_channel channelMapIn[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesIn; + ma_uint32 periodSizeInMillisecondsIn; + ma_uint32 periodsIn; + ma_share_mode shareMode; + ma_performance_profile performanceProfile; + ma_bool32 registerStopEvent; + + /* Output. */ +#if defined(MA_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + AudioComponent component; + AudioUnit audioUnit; + AudioBufferList* pAudioBufferList; /* Only used for input devices. */ + ma_format formatOut; + ma_uint32 channelsOut; + ma_uint32 sampleRateOut; + ma_channel channelMapOut[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesOut; + ma_uint32 periodsOut; + char deviceName[256]; +} ma_device_init_internal_data__coreaudio; + +static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ +{ + ma_result result; + OSStatus status; + UInt32 enableIOFlag; + AudioStreamBasicDescription bestFormat; + UInt32 actualPeriodSizeInFrames; + AURenderCallbackStruct callbackInfo; +#if defined(MA_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + + /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(pContext != NULL); + MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); + +#if defined(MA_APPLE_DESKTOP) + pData->deviceObjectID = 0; +#endif + pData->component = NULL; + pData->audioUnit = NULL; + pData->pAudioBufferList = NULL; + +#if defined(MA_APPLE_DESKTOP) + result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MA_SUCCESS) { + return result; + } + + 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. */ + pData->periodsOut = pData->periodsIn; + if (pData->periodsOut == 0) { + pData->periodsOut = MA_DEFAULT_PERIODS; + } + if (pData->periodsOut > 16) { + pData->periodsOut = 16; + } + + + /* Audio unit. */ + status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + + /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ + enableIOFlag = 1; + if (deviceType == ma_device_type_capture) { + enableIOFlag = 0; + } + + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + enableIOFlag = (enableIOFlag == 0) ? 1 : 0; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_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(MA_APPLE_DESKTOP) + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(result); + } +#else + /* + For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change + the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. + */ + if (pDeviceID != NULL) { + if (deviceType == ma_device_type_capture) { + ma_bool32 found = MA_FALSE; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; + found = MA_TRUE; + break; + } + } + + if (found == MA_FALSE) { + return MA_DOES_NOT_EXIST; + } + } + } +#endif + + /* + Format. This is the hardest part of initialization because there's a few variables to take into account. + 1) The format must be supported by the device. + 2) The format must be supported miniaudio. + 3) There's a priority that miniaudio prefers. + + Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The + most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same + for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. + + On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. + */ + { + AudioStreamBasicDescription origFormat; + UInt32 origFormatSize = sizeof(origFormat); + AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; + + if (deviceType == ma_device_type_playback) { + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); + } else { + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); + } + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + #if defined(MA_APPLE_DESKTOP) + result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); + if (result != MA_SUCCESS) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + /* + Technical Note TN2091: Device input using the HAL Output Audio Unit + https://developer.apple.com/library/archive/technotes/tn2091/_index.html + + This documentation says the following: + + The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY + variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate + conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with + another AudioConverter. + + The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We + therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it + safe and apply the same rule to output as well. + + I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() + returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but + this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. + + Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with + this, however, is that it actually changes the sample rate at the operating system level and not just the application. This + could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a + configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample + rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run + the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is + changed by miniaudio. + */ + if (pData->allowNominalSampleRateChange) { + AudioValueRange sampleRateRange; + AudioObjectPropertyAddress propAddress; + + sampleRateRange.mMinimum = bestFormat.mSampleRate; + sampleRateRange.mMaximum = bestFormat.mSampleRate; + + propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); + if (status != noErr) { + bestFormat.mSampleRate = origFormat.mSampleRate; + } + } else { + bestFormat.mSampleRate = origFormat.mSampleRate; + } + + status = ((ma_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 + bestFormat = origFormat; + + /* + Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try + setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since + it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I + can tell, it looks like the sample rate is shared between playback and capture for everything. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; + bestFormat.mSampleRate = pAudioSession.sampleRate; + + /* + I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with + AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. + */ + if (deviceType == ma_device_type_playback) { + bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; + } + if (deviceType == ma_device_type_capture) { + bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; + } + } + + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + #endif + + result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); + if (result != MA_SUCCESS) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + if (pData->formatOut == ma_format_unknown) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MA_FORMAT_NOT_SUPPORTED; + } + + pData->channelsOut = bestFormat.mChannelsPerFrame; + pData->sampleRateOut = bestFormat.mSampleRate; + } + + /* Clamp the channel count for safety. */ + if (pData->channelsOut > MA_MAX_CHANNELS) { + pData->channelsOut = MA_MAX_CHANNELS; + } + + /* + Internal channel map. This is weird in my testing. If I use the AudioObject to get the + channel map, the channel descriptions are set to "Unknown" for some reason. To work around + this it looks like retrieving it from the AudioUnit will work. However, and this is where + it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore + I'm going to fall back to a default assumption in these cases. + */ +#if defined(MA_APPLE_DESKTOP) + result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); + if (result != MA_SUCCESS) { + #if 0 + /* Try falling back to the channel map from the AudioObject. */ + result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); + if (result != MA_SUCCESS) { + return result; + } + #else + /* Fall back to default assumptions. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + #endif + } +#else + /* TODO: Figure out how to get the channel map using AVAudioSession. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); +#endif + + + /* Buffer size. Not allowing this to be configurable on iOS. */ + if (pData->periodSizeInFramesIn == 0) { + if (pData->periodSizeInMillisecondsIn == 0) { + if (pData->performanceProfile == ma_performance_profile_low_latency) { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); + } else { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); + } + } else { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); + } + } else { + actualPeriodSizeInFrames = pData->periodSizeInFramesIn; + } + +#if defined(MA_APPLE_DESKTOP) + result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); + if (result != MA_SUCCESS) { + return result; + } +#else + /* + On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point + number. I don't trust any potential truncation errors due to converting from float to integer + so I'm going to explicitly set the actual period size to the next power of 2. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; + actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); + } +#endif + + + /* + During testing I discovered that the buffer size can be too big. You'll get an error like this: + + kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 + + Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that + of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. + */ + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; + + /* We need a buffer list if this is an input device. We render into this in the input callback. */ + if (deviceType == ma_device_type_capture) { + ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + AudioBufferList* pBufferList; + + pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); + if (pBufferList == NULL) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MA_OUT_OF_MEMORY; + } + + pData->pAudioBufferList = pBufferList; + } + + /* Callbacks. */ + callbackInfo.inputProcRefCon = pDevice_DoNotReference; + if (deviceType == ma_device_type_playback) { + callbackInfo.inputProc = ma_on_output__coreaudio; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } else { + callbackInfo.inputProc = ma_on_input__coreaudio; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } + + /* We need to listen for stop events. */ + if (pData->registerStopEvent) { + status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } + + /* Initialize the audio unit. */ + status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); + if (status != noErr) { + ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); + pData->pAudioBufferList = NULL; + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + /* Grab the name. */ +#if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); +#else + if (deviceType == ma_device_type_playback) { + ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); + } else { + ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); + } +#endif + + return result; +} + +#if defined(MA_APPLE_DESKTOP) +static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) +{ + ma_device_init_internal_data__coreaudio data; + ma_result result; + + /* This should only be called for playback or capture, not duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ + + if (deviceType == ma_device_type_capture) { + data.formatIn = pDevice->capture.format; + data.channelsIn = pDevice->capture.channels; + data.sampleRateIn = pDevice->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); + data.shareMode = pDevice->capture.shareMode; + data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.registerStopEvent = MA_TRUE; + + if (disposePreviousAudioUnit) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + } else if (deviceType == ma_device_type_playback) { + data.formatIn = pDevice->playback.format; + data.channelsIn = pDevice->playback.channels; + data.sampleRateIn = pDevice->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); + data.shareMode = pDevice->playback.shareMode; + data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.registerStopEvent = (pDevice->type != ma_device_type_duplex); + + if (disposePreviousAudioUnit) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + } + data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; + data.periodsIn = pDevice->coreaudio.originalPeriods; + + /* Need at least 3 periods for duplex. */ + if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { + data.periodsIn = 3; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + return result; + } + + if (deviceType == ma_device_type_capture) { + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + #endif + pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; + + pDevice->capture.internalFormat = data.formatOut; + pDevice->capture.internalChannels = data.channelsOut; + pDevice->capture.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->capture.internalPeriods = data.periodsOut; + } else if (deviceType == ma_device_type_playback) { + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + #endif + pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; + + pDevice->playback.internalFormat = data.formatOut; + pDevice->playback.internalChannels = data.channelsOut; + pDevice->playback.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->playback.internalPeriods = data.periodsOut; + } + + return MA_SUCCESS; +} +#endif /* MA_APPLE_DESKTOP */ + +static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with the Core Audio backend for now. */ + if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Capture needs to be initialized first. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__coreaudio data; + data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.formatIn = pDescriptorCapture->format; + data.channelsIn = pDescriptorCapture->channels; + data.sampleRateIn = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; + data.periodsIn = pDescriptorCapture->periodCount; + data.shareMode = pDescriptorCapture->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.registerStopEvent = MA_TRUE; + + /* Need at least 3 periods for duplex. */ + if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { + data.periodsIn = 3; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + return result; + } + + pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + #endif + pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; + pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; + pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + + pDescriptorCapture->format = data.formatOut; + pDescriptorCapture->channels = data.channelsOut; + pDescriptorCapture->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorCapture->periodCount = data.periodsOut; + + #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + + /* + 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. + */ + if (pConfig->capture.pDeviceID == NULL) { + ma_device__track__coreaudio(pDevice); + } + #endif + } + + /* Playback. */ + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__coreaudio data; + data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.formatIn = pDescriptorPlayback->format; + data.channelsIn = pDescriptorPlayback->channels; + data.sampleRateIn = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + data.shareMode = pDescriptorPlayback->shareMode; + data.performanceProfile = pConfig->performanceProfile; + + /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ + if (pConfig->deviceType == ma_device_type_duplex) { + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodsIn = pDescriptorCapture->periodCount; + data.registerStopEvent = MA_FALSE; + } else { + data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; + data.periodsIn = pDescriptorPlayback->periodCount; + data.registerStopEvent = MA_TRUE; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + } + return result; + } + + pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + #endif + pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; + pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; + pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + + pDescriptorPlayback->format = data.formatOut; + pDescriptorPlayback->channels = data.channelsOut; + pDescriptorPlayback->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorPlayback->periodCount = data.periodsOut; + + #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + + /* + 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. + */ + if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { + ma_device__track__coreaudio(pDevice); + } + #endif + } + + + + /* + When stopping the device, a callback is called on another thread. We need to wait for this callback + before returning from ma_device_stop(). This event is used for this. + */ + ma_event_init(&pDevice->coreaudio.stopEvent); + + /* + We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done + differently on non-Desktop Apple platforms. + */ +#if defined(MA_APPLE_MOBILE) + pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; +#endif + + return MA_SUCCESS; +} + + +static ma_result ma_device_start__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + return ma_result_from_OSStatus(status); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + /* We need to wait for the callback to finish before returning. */ + ma_event_wait(&pDevice->coreaudio.stopEvent); + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_coreaudio); + +#if defined(MA_APPLE_MOBILE) + if (!pContext->coreaudio.noAudioSessionDeactivate) { + if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } +#endif + +#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); +#endif + +#if !defined(MA_APPLE_MOBILE) + ma_context__uninit_device_tracking__coreaudio(pContext); +#endif + + (void)pContext; + return MA_SUCCESS; +} + +#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) +static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) +{ + /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ + MA_ASSERT(category != ma_ios_session_category_default); + MA_ASSERT(category != ma_ios_session_category_none); + + switch (category) { + case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; + case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; + case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; + case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; + case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; + case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; + default: return AVAudioSessionCategoryAmbient; + } +} +#endif + +static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#if !defined(MA_APPLE_MOBILE) + ma_result result; +#endif + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pContext != NULL); + +#if defined(MA_APPLE_MOBILE) + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; + + MA_ASSERT(pAudioSession != NULL); + + if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { + /* + I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails + we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. + */ + #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) + options |= AVAudioSessionCategoryOptionDefaultToSpeaker; + #endif + + if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { + /* Using PlayAndRecord */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { + /* Using Playback */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { + /* Using Record */ + } else { + /* Leave as default? */ + } + } else { + if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { + #if defined(__IPHONE_12_0) + if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { + return MA_INVALID_OPERATION; /* Failed to set session category. */ + } + #else + /* Ignore the session category on version 11 and older, but post a warning. */ + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); + #endif + } + } + + if (!pConfig->coreaudio.noAudioSessionActivate) { + if (![pAudioSession setActive:true error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } + } +#endif + +#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); + if (pContext->coreaudio.hCoreFoundation == NULL) { + return MA_API_NOT_FOUND; + } + + pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); + + + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); + if (pContext->coreaudio.hCoreAudio == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); + + /* + 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. + The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to + AudioToolbox. + */ + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); + if (pContext->coreaudio.hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + + if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); + if (pContext->coreaudio.hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + } + + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); +#else + pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; + pContext->coreaudio.CFRelease = (ma_proc)CFRelease; + + #if defined(MA_APPLE_DESKTOP) + pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; + pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; + pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; + pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; + pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; + #endif + + pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; + pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; + pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; + pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; + pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; + pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; + pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; + pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; + pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; + pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; + pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; +#endif + + /* Audio component. */ + { + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + #if defined(MA_APPLE_DESKTOP) + desc.componentSubType = kAudioUnitSubType_HALOutput; + #else + desc.componentSubType = kAudioUnitSubType_RemoteIO; + #endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (pContext->coreaudio.component == NULL) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + #endif + return MA_FAILED_TO_INIT_BACKEND; + } + } + +#if !defined(MA_APPLE_MOBILE) + result = ma_context__init_device_tracking__coreaudio(pContext); + if (result != MA_SUCCESS) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + #endif + return result; + } +#endif + + pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; + + pCallbacks->onContextInit = ma_context_init__coreaudio; + pCallbacks->onContextUninit = ma_context_uninit__coreaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; + pCallbacks->onDeviceInit = ma_device_init__coreaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; + pCallbacks->onDeviceStart = ma_device_start__coreaudio; + pCallbacks->onDeviceStop = ma_device_stop__coreaudio; + pCallbacks->onDeviceRead = NULL; + pCallbacks->onDeviceWrite = NULL; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* Core Audio */ + + + +/****************************************************************************** + +sndio Backend + +******************************************************************************/ +#ifdef MA_HAS_SNDIO +#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 miniaudio'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 0 +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#endif +#endif + +#define MA_SIO_DEVANY "default" +#define MA_SIO_PLAY 1 +#define MA_SIO_REC 2 +#define MA_SIO_NENC 8 +#define MA_SIO_NCHAN 8 +#define MA_SIO_NRATE 16 +#define MA_SIO_NCONF 4 + +struct ma_sio_hdl; /* <-- Opaque */ + +struct ma_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 ma_sio_enc +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; +}; + +struct ma_sio_conf +{ + unsigned int enc; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; +}; + +struct ma_sio_cap +{ + struct ma_sio_enc enc[MA_SIO_NENC]; + unsigned int rchan[MA_SIO_NCHAN]; + unsigned int pchan[MA_SIO_NCHAN]; + unsigned int rate[MA_SIO_NRATE]; + int __pad[7]; + unsigned int nconf; + struct ma_sio_conf confs[MA_SIO_NCONF]; +}; + +typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); +typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); +typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); +typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); +typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); + +static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ +{ + ma_uint32 i; + for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { + if (g_maStandardSampleRatePriorities[i] == sampleRate) { + return i; + } + } + + return (ma_uint32)-1; +} + +static ma_format ma_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 ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { + return ma_format_unknown; + } + + if (bits == 8 && bps == 1 && sig == 0) { + return ma_format_u8; + } + if (bits == 16 && bps == 2 && sig == 1) { + return ma_format_s16; + } + if (bits == 24 && bps == 3 && sig == 1) { + return ma_format_s24; + } + if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { + /*return ma_format_s24_32;*/ + } + if (bits == 32 && bps == 4 && sig == 1) { + return ma_format_s32; + } + + return ma_format_unknown; +} + +static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) +{ + ma_format bestFormat; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + + bestFormat = ma_format_unknown; + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == ma_format_unknown) { + continue; /* Format not supported. */ + } + + if (bestFormat == ma_format_unknown) { + bestFormat = format; + } else { + if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ + bestFormat = format; + } + } + } + } + + return bestFormat; +} + +static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) +{ + ma_uint32 maxChannels; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + MA_ASSERT(requiredFormat != ma_format_unknown); + + /* Just pick whatever configuration has the most channels. */ + maxChannels = 0; + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + /* The encoding should be of requiredFormat. */ + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int iChannel; + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_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 (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + + if (deviceType == ma_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (maxChannels < channels) { + maxChannels = channels; + } + } + } + } + + return maxChannels; +} + +static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) +{ + ma_uint32 firstSampleRate; + ma_uint32 bestSampleRate; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + MA_ASSERT(requiredFormat != ma_format_unknown); + MA_ASSERT(requiredChannels > 0); + MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); + + firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ + bestSampleRate = 0; + + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + /* The encoding should be of requiredFormat. */ + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int iChannel; + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_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 (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + unsigned int iRate; + + if (deviceType == ma_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_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 (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { + ma_uint32 rate = (ma_uint32)caps->rate[iRate]; + ma_uint32 ratePriority; + + if (firstSampleRate == 0) { + firstSampleRate = rate; + } + + /* Disregard this rate if it's not a standard one. */ + ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); + if (ratePriority == (ma_uint32)-1) { + continue; + } + + if (ma_get_standard_sample_rate_priority_index__sndio(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; +} + + +static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 isTerminating = MA_FALSE; + struct ma_sio_hdl* handle; + + MA_ASSERT(pContext != NULL); + MA_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. */ + + /* Playback. */ + if (!isTerminating) { + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); + if (handle != NULL) { + /* Supports playback. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); + ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); + + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + /* Capture. */ + if (!isTerminating) { + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); + if (handle != NULL) { + /* Supports capture. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); + ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); + + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + char devid[256]; + struct ma_sio_hdl* handle; + struct ma_sio_cap caps; + unsigned int iConfig; + + MA_ASSERT(pContext != NULL); + + /* We need to open the device before we can get information about it. */ + if (pDeviceID == NULL) { + ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); + } else { + ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); + } + + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); + if (handle == NULL) { + return MA_NO_DEVICE; + } + + if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { + return MA_ERROR; + } + + pDeviceInfo->nativeDataFormatCount = 0; + + for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { + /* + The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give + preference to some formats over others. + */ + unsigned int iEncoding; + unsigned int iChannel; + unsigned int iRate; + + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps.enc[iEncoding].bits; + bps = caps.enc[iEncoding].bps; + sig = caps.enc[iEncoding].sig; + le = caps.enc[iEncoding].le; + msb = caps.enc[iEncoding].msb; + format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == ma_format_unknown) { + continue; /* Format not supported. */ + } + + + /* Channels. */ + for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + + if (deviceType == ma_device_type_playback) { + chan = caps.confs[iConfig].pchan; + } else { + chan = caps.confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_device_type_playback) { + channels = caps.pchan[iChannel]; + } else { + channels = caps.rchan[iChannel]; + } + + + /* Sample Rates. */ + for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { + if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); + } + } + } + } + } + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + const char* pDeviceName; + ma_ptr handle; + int openFlags = 0; + struct ma_sio_cap caps; + struct ma_sio_par par; + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + MA_ASSERT(pDevice != NULL); + + if (deviceType == ma_device_type_capture) { + openFlags = MA_SIO_REC; + } else { + openFlags = MA_SIO_PLAY; + } + + pDeviceID = pDescriptor->pDeviceID; + format = pDescriptor->format; + channels = pDescriptor->channels; + sampleRate = pDescriptor->sampleRate; + + pDeviceName = MA_SIO_DEVANY; + if (pDeviceID != NULL) { + pDeviceName = pDeviceID->sndio; + } + + handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); + if (handle == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* We need to retrieve the device caps to determine the most appropriate format to use. */ + if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); + return MA_ERROR; + } + + /* + 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 ma_find_best_channels_from_sio_cap__sndio(). + */ + if (deviceType == ma_device_type_capture) { + if (format == ma_format_unknown) { + format = ma_find_best_format_from_sio_cap__sndio(&caps); + } + + if (channels == 0) { + if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { + channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); + } else { + channels = MA_DEFAULT_CHANNELS; + } + } + } else { + if (format == ma_format_unknown) { + format = ma_find_best_format_from_sio_cap__sndio(&caps); + } + + if (channels == 0) { + if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { + channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); + } else { + channels = MA_DEFAULT_CHANNELS; + } + } + } + + if (sampleRate == 0) { + sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); + } + + + ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); + par.msb = 0; + par.le = ma_is_little_endian(); + + switch (format) { + case ma_format_u8: + { + par.bits = 8; + par.bps = 1; + par.sig = 0; + } break; + + case ma_format_s24: + { + par.bits = 24; + par.bps = 3; + par.sig = 1; + } break; + + case ma_format_s32: + { + par.bits = 32; + par.bps = 4; + par.sig = 1; + } break; + + case ma_format_s16: + case ma_format_f32: + case ma_format_unknown: + default: + { + par.bits = 16; + par.bps = 2; + par.sig = 1; + } break; + } + + if (deviceType == ma_device_type_capture) { + par.rchan = channels; + } else { + par.pchan = channels; + } + + par.rate = sampleRate; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); + + par.round = internalPeriodSizeInFrames; + par.appbufsz = par.round * pDescriptor->periodCount; + + if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); + return MA_ERROR; + } + + if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); + return MA_ERROR; + } + + internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); + internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; + internalSampleRate = par.rate; + internalPeriods = par.appbufsz / par.round; + internalPeriodSizeInFrames = par.round; + + if (deviceType == ma_device_type_capture) { + pDevice->sndio.handleCapture = handle; + } else { + pDevice->sndio.handlePlayback = handle; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->sndio); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* + From the documentation: + + The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then + stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the + buffer is drained. In no case are samples in the play buffer discarded. + + Therefore, sio_stop() performs all of the necessary draining for us. + */ + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int result; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (result == 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); + return MA_IO_ERROR; + } + + if (pFramesWritten != NULL) { + *pFramesWritten = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int result; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (result == 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); + return MA_IO_ERROR; + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__sndio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_sndio); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#ifndef MA_NO_RUNTIME_LINKING + const char* libsndioNames[] = { + "libsndio.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libsndioNames); ++i) { + pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); + if (pContext->sndio.sndioSO != NULL) { + break; + } + } + + if (pContext->sndio.sndioSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), 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 + + pCallbacks->onContextInit = ma_context_init__sndio; + pCallbacks->onContextUninit = ma_context_uninit__sndio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; + pCallbacks->onDeviceInit = ma_device_init__sndio; + pCallbacks->onDeviceUninit = ma_device_uninit__sndio; + pCallbacks->onDeviceStart = ma_device_start__sndio; + pCallbacks->onDeviceStop = ma_device_stop__sndio; + pCallbacks->onDeviceRead = ma_device_read__sndio; + pCallbacks->onDeviceWrite = ma_device_write__sndio; + pCallbacks->onDeviceDataLoop = NULL; + + (void)pConfig; + return MA_SUCCESS; +} +#endif /* sndio */ + + + +/****************************************************************************** + +audio(4) Backend + +******************************************************************************/ +#ifdef MA_HAS_AUDIO4 +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + #include + #if defined(OpenBSD) && OpenBSD >= 201709 + #define MA_AUDIO4_USE_NEW_API + #endif +#endif + +static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) +{ + size_t baseLen; + + MA_ASSERT(id != NULL); + MA_ASSERT(idSize > 0); + MA_ASSERT(deviceIndex >= 0); + + baseLen = strlen(base); + MA_ASSERT(idSize > baseLen); + + ma_strcpy_s(id, idSize, base); + ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); +} + +static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) +{ + size_t idLen; + size_t baseLen; + const char* deviceIndexStr; + + MA_ASSERT(id != NULL); + MA_ASSERT(base != NULL); + MA_ASSERT(pIndexOut != NULL); + + idLen = strlen(id); + baseLen = strlen(base); + if (idLen <= baseLen) { + return MA_ERROR; /* Doesn't look like the id starts with the base. */ + } + + if (strncmp(id, base, baseLen) != 0) { + return MA_ERROR; /* ID does not begin with base. */ + } + + deviceIndexStr = id + baseLen; + if (deviceIndexStr[0] == '\0') { + return MA_ERROR; /* No index specified in the ID. */ + } + + if (pIndexOut) { + *pIndexOut = atoi(deviceIndexStr); + } + + return MA_SUCCESS; +} + + +#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ +static ma_format ma_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 ma_format_u8; + } else { + if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { + if (precision == 16) { + return ma_format_s16; + } else if (precision == 24) { + return ma_format_s24; + } else if (precision == 32) { + return ma_format_s32; + } + } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { + if (precision == 16) { + return ma_format_s16; + } else if (precision == 24) { + return ma_format_s24; + } else if (precision == 32) { + return ma_format_s32; + } + } + } + + return ma_format_unknown; /* Encoding not supported. */ +} + +static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) +{ + MA_ASSERT(pEncoding != NULL); + MA_ASSERT(pPrecision != NULL); + + switch (format) + { + case ma_format_u8: + { + *pEncoding = AUDIO_ENCODING_ULINEAR; + *pPrecision = 8; + } break; + + case ma_format_s24: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 24; + } break; + + case ma_format_s32: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 32; + } break; + + case ma_format_s16: + case ma_format_f32: + case ma_format_unknown: + default: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 16; + } break; + } +} + +static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) +{ + return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); +} + +static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) +{ + audio_encoding_t encoding; + ma_uint32 iFormat; + int counter = 0; + + /* First check to see if the preferred format is supported. */ + if (preferredFormat != ma_format_unknown) { + counter = 0; + for (;;) { + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { + return preferredFormat; /* Found the preferred format. */ + } + + /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ + counter += 1; + } + } + + /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { + ma_format format = g_maFormatPriorities[iFormat]; + + counter = 0; + for (;;) { + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { + return format; /* Found a workable format. */ + } + + /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ + counter += 1; + } + } + + /* Getting here means not appropriate format was found. */ + return ma_format_unknown; +} +#else +static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) +{ + if (par->bits == 8 && par->bps == 1 && par->sig == 0) { + return ma_format_u8; + } + if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_s16; + } + if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_s24; + } + if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_f32; + } + + /* Format not supported. */ + return ma_format_unknown; +} +#endif + +static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) +{ + audio_device_t fdDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(fd >= 0); + MA_ASSERT(pDeviceInfo != NULL); + + (void)pContext; + (void)deviceType; + + if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { + return MA_ERROR; /* Failed to retrieve device info. */ + } + + /* Name. */ + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); + + #if !defined(MA_AUDIO4_USE_NEW_API) + { + audio_info_t fdInfo; + int counter = 0; + ma_uint32 channels; + ma_uint32 sampleRate; + + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + return MA_ERROR; + } + + if (deviceType == ma_device_type_playback) { + channels = fdInfo.play.channels; + sampleRate = fdInfo.play.sample_rate; + } else { + channels = fdInfo.record.channels; + sampleRate = fdInfo.record.sample_rate; + } + + /* Supported formats. We get this by looking at the encodings. */ + pDeviceInfo->nativeDataFormatCount = 0; + for (;;) { + audio_encoding_t encoding; + ma_format format; + + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); + if (format != ma_format_unknown) { + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); + } + + counter += 1; + } + } + #else + { + struct audio_swpar fdPar; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + return MA_ERROR; + } + + format = ma_format_from_swpar__audio4(&fdPar); + if (format == ma_format_unknown) { + return MA_FORMAT_NOT_SUPPORTED; + } + + if (deviceType == ma_device_type_playback) { + channels = fdPar.pchan; + } else { + channels = fdPar.rchan; + } + + sampleRate = fdPar.rate; + + pDeviceInfo->nativeDataFormatCount = 0; + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); + } + #endif + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + const int maxDevices = 64; + char devpath[256]; + int iDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* + 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. + */ + for (iDevice = 0; iDevice < maxDevices; ++iDevice) { + struct stat st; + int fd; + ma_bool32 isTerminating = MA_FALSE; + + ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); + ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); + + if (stat(devpath, &st) < 0) { + break; + } + + /* The device exists, but we need to check if it's usable as playback and/or capture. */ + + /* Playback. */ + if (!isTerminating) { + fd = open(devpath, O_RDONLY, 0); + if (fd >= 0) { + /* Supports playback. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + close(fd); + } + } + + /* Capture. */ + if (!isTerminating) { + fd = open(devpath, O_WRONLY, 0); + if (fd >= 0) { + /* Supports capture. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + close(fd); + } + } + + if (isTerminating) { + break; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + int fd = -1; + int deviceIndex = -1; + char ctlid[256]; + ma_result result; + + MA_ASSERT(pContext != NULL); + + /* + 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. + */ + if (pDeviceID == NULL) { + /* Default device. */ + ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); + } else { + /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ + result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); + if (result != MA_SUCCESS) { + return result; + } + + ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); + } + + fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (fd == -1) { + return MA_NO_DEVICE; + } + + if (deviceIndex == -1) { + ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); + } else { + ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); + } + + result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); + + close(fd); + return result; +} + +static ma_result ma_device_uninit__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + close(pDevice->audio4.fdCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + close(pDevice->audio4.fdPlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + const char* pDefaultDeviceNames[] = { + "/dev/audio", + "/dev/audio0" + }; + const char* pDefaultDeviceCtlNames[] = { + "/dev/audioctl", + "/dev/audioctl0" + }; + int fd; + int fdFlags = 0; + size_t iDefaultDevice = (size_t)-1; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is open the file. */ + if (deviceType == ma_device_type_capture) { + fdFlags = O_RDONLY; + } else { + fdFlags = O_WRONLY; + } + /*fdFlags |= O_NONBLOCK;*/ + + /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ + if (pDescriptor->pDeviceID == NULL) { + /* Default device. */ + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { + fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); + if (fd != -1) { + break; + } + } + } else { + /* Specific device. */ + fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); + + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { + if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { + break; + } + } + + if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { + iDefaultDevice = (size_t)-1; + } + } + + if (fd == -1) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); + return ma_result_from_errno(errno); + } + + #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ + { + audio_info_t fdInfo; + int fdInfoResult = -1; + + /* + The documentation is a little bit unclear to me as to how it handles formats. It says the + following: + + Regardless of formats supported by underlying driver, the audio driver accepts the + following formats. + + By then the next sentence says this: + + `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. + + It sounds like a direct contradiction to me. I'm going to play this safe any only use the + best sample format returned by AUDIO_GETENC. If the requested format is supported we'll + use that, but otherwise we'll just use our standard format priorities to pick an + appropriate one. + */ + AUDIO_INITINFO(&fdInfo); + + /* + Get the default format from the audioctl file if we're asking for a default device. If we + retrieve it from /dev/audio it'll default to mono 8000Hz. + */ + if (iDefaultDevice != (size_t)-1) { + /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ + int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); + if (fdctl != -1) { + fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); + close(fdctl); + } + } + + if (fdInfoResult == -1) { + /* We still don't have the default device info so just retrieve it from the main audio device. */ + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + } + + /* We get the driver to do as much of the data conversion as possible. */ + if (deviceType == ma_device_type_capture) { + fdInfo.mode = AUMODE_RECORD; + ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); + + if (pDescriptor->channels != 0) { + fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ + } + + if (pDescriptor->sampleRate != 0) { + fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ + } + } else { + fdInfo.mode = AUMODE_PLAY; + ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); + + if (pDescriptor->channels != 0) { + fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ + } + + if (pDescriptor->sampleRate != 0) { + fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ + } + } + + if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); + } + + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + + if (deviceType == ma_device_type_capture) { + internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); + internalChannels = fdInfo.record.channels; + internalSampleRate = fdInfo.record.sample_rate; + } else { + internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); + internalChannels = fdInfo.play.channels; + internalSampleRate = fdInfo.play.sample_rate; + } + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Buffer. */ + { + ma_uint32 internalPeriodSizeInBytes; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + + internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (internalPeriodSizeInBytes < 16) { + internalPeriodSizeInBytes = 16; + } + + internalPeriods = pDescriptor->periodCount; + if (internalPeriods < 2) { + internalPeriods = 2; + } + + /* What miniaudio calls a period, audio4 calls a block. */ + AUDIO_INITINFO(&fdInfo); + fdInfo.hiwat = internalPeriods; + fdInfo.lowat = internalPeriods-1; + fdInfo.blocksize = internalPeriodSizeInBytes; + if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); + } + + internalPeriods = fdInfo.hiwat; + internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); + } + } + #else + { + struct audio_swpar fdPar; + + /* 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. */ + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); + return ma_result_from_errno(errno); + } + + internalFormat = ma_format_from_swpar__audio4(&fdPar); + internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; + internalSampleRate = fdPar.rate; + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Buffer. */ + { + ma_uint32 internalPeriodSizeInBytes; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + + /* What miniaudio calls a period, audio4 calls a block. */ + internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (internalPeriodSizeInBytes < 16) { + internalPeriodSizeInBytes = 16; + } + + fdPar.nblks = pDescriptor->periodCount; + fdPar.round = internalPeriodSizeInBytes; + + if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); + return ma_result_from_errno(errno); + } + + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); + return ma_result_from_errno(errno); + } + } + + internalFormat = ma_format_from_swpar__audio4(&fdPar); + internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; + internalSampleRate = fdPar.rate; + internalPeriods = fdPar.nblks; + internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); + } + #endif + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + if (deviceType == ma_device_type_capture) { + pDevice->audio4.fdCapture = fd; + } else { + pDevice->audio4.fdPlayback = fd; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->audio4); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDevice->audio4.fdCapture = -1; + pDevice->audio4.fdPlayback = -1; + + /* + 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 + /* NetBSD 8.0+ */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } +#else + /* All other flavors. */ +#endif + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + close(pDevice->audio4.fdCapture); + } + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->audio4.fdCapture == -1) { + return MA_INVALID_ARGS; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->audio4.fdPlayback == -1) { + return MA_INVALID_ARGS; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) +{ + if (fd == -1) { + return MA_INVALID_ARGS; + } + +#if !defined(MA_AUDIO4_USE_NEW_API) + if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); + return ma_result_from_errno(errno); + } +#else + if (ioctl(fd, AUDIO_STOP, 0) < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); + return ma_result_from_errno(errno); + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result; + + result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result; + + /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ + #if !defined(MA_AUDIO4_USE_NEW_API) + ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); + #endif + + /* Here is where the device is stopped immediately. */ + result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int result; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (result < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); + return ma_result_from_errno(errno); + } + + if (pFramesWritten != NULL) { + *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int result; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (result < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); + return ma_result_from_errno(errno); + } + + if (pFramesRead != NULL) { + *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__audio4(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_audio4); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pCallbacks->onContextInit = ma_context_init__audio4; + pCallbacks->onContextUninit = ma_context_uninit__audio4; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; + pCallbacks->onDeviceInit = ma_device_init__audio4; + pCallbacks->onDeviceUninit = ma_device_uninit__audio4; + pCallbacks->onDeviceStart = ma_device_start__audio4; + pCallbacks->onDeviceStop = ma_device_stop__audio4; + pCallbacks->onDeviceRead = ma_device_read__audio4; + pCallbacks->onDeviceWrite = ma_device_write__audio4; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* audio4 */ + + +/****************************************************************************** + +OSS Backend + +******************************************************************************/ +#ifdef MA_HAS_OSS +#include +#include +#include +#include + +#ifndef SNDCTL_DSP_HALT +#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET +#endif + +#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" + +static int ma_open_temp_device__oss() +{ + /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ + int fd = open("/dev/mixer", O_RDONLY, 0); + if (fd >= 0) { + return fd; + } + + return -1; +} + +static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) +{ + const char* deviceName; + int flags; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pfd != NULL); + (void)pContext; + + *pfd = -1; + + /* This function should only be called for playback or capture, not duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + deviceName = MA_OSS_DEFAULT_DEVICE_NAME; + if (pDeviceID != NULL) { + deviceName = pDeviceID->oss; + } + + flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; + if (shareMode == ma_share_mode_exclusive) { + flags |= O_EXCL; + } + + *pfd = open(deviceName, flags, 0); + if (*pfd == -1) { + return ma_result_from_errno(errno); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + int fd; + oss_sysinfo si; + int result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + fd = ma_open_temp_device__oss(); + if (fd == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; + } + + result = ioctl(fd, SNDCTL_SYSINFO, &si); + if (result != -1) { + int iAudioDevice; + for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ + ma_device_info deviceInfo; + ma_bool32 isTerminating = MA_FALSE; + + MA_ZERO_OBJECT(&deviceInfo); + + /* ID */ + ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); + + /* + The human readable device name should be in the "ai.handle" variable, but it can + sometimes be empty in which case we just fall back to "ai.name" which is less user + friendly, but usually has a value. + */ + if (ai.handle[0] != '\0') { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); + } else { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); + } + + /* The device can be both playback and capture. */ + if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + if (isTerminating) { + break; + } + } + } + } + } else { + close(fd); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; + } + + close(fd); + return MA_SUCCESS; +} + +static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) +{ + unsigned int minChannels; + unsigned int maxChannels; + unsigned int iRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pAudioInfo != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* If we support all channels we just report 0. */ + minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + + /* + OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, + which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which + case we'll need to use min_rate and max_rate and report only standard rates. + */ + if (pAudioInfo->nrates > 0) { + for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { + unsigned int rate = pAudioInfo->rates[iRate]; + + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ + } else { + unsigned int iChannel; + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); + } + } + } + } else { + for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; + + if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ + } else { + unsigned int iChannel; + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); + } + } + } + } + } +} + +static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_bool32 foundDevice; + int fdTemp; + oss_sysinfo si; + int result; + + MA_ASSERT(pContext != NULL); + + /* Handle the default device a little differently. */ + if (pDeviceID == NULL) { + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + return MA_SUCCESS; + } + + + /* If we get here it means we are _not_ using the default device. */ + foundDevice = MA_FALSE; + + fdTemp = ma_open_temp_device__oss(); + if (fdTemp == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; + } + + result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); + if (result != -1) { + int iAudioDevice; + for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { + /* It has the same name, so now just confirm the type. */ + if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || + (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { + unsigned int formatMask; + + /* ID */ + ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); + + /* + The human readable device name should be in the "ai.handle" variable, but it can + sometimes be empty in which case we just fall back to "ai.name" which is less user + friendly, but usually has a value. + */ + if (ai.handle[0] != '\0') { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); + } + + + pDeviceInfo->nativeDataFormatCount = 0; + + if (deviceType == ma_device_type_playback) { + formatMask = ai.oformats; + } else { + formatMask = ai.iformats; + } + + if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); + } + if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); + } + if ((formatMask & AFMT_U8) != 0) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); + } + + foundDevice = MA_TRUE; + break; + } + } + } + } + } else { + close(fdTemp); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; + } + + + close(fdTemp); + + if (!foundDevice) { + return MA_NO_DEVICE; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + close(pDevice->oss.fdCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + close(pDevice->oss.fdPlayback); + } + + return MA_SUCCESS; +} + +static int ma_format_to_oss(ma_format format) +{ + int ossFormat = AFMT_U8; + switch (format) { + case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case ma_format_u8: + default: ossFormat = AFMT_U8; break; + } + + return ossFormat; +} + +static ma_format ma_format_from_oss(int ossFormat) +{ + if (ossFormat == AFMT_U8) { + return ma_format_u8; + } else { + if (ma_is_little_endian()) { + switch (ossFormat) { + case AFMT_S16_LE: return ma_format_s16; + case AFMT_S32_LE: return ma_format_s32; + default: return ma_format_unknown; + } + } else { + switch (ossFormat) { + case AFMT_S16_BE: return ma_format_s16; + case AFMT_S32_BE: return ma_format_s32; + default: return ma_format_unknown; + } + } + } + + return ma_format_unknown; +} + +static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + ma_result result; + int ossResult; + int fd; + const ma_device_id* pDeviceID = NULL; + ma_share_mode shareMode; + int ossFormat; + int ossChannels; + int ossSampleRate; + int ossFragment; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + + pDeviceID = pDescriptor->pDeviceID; + shareMode = pDescriptor->shareMode; + ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ + ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; + ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + + /* + The OSS documantation is very clear about the order we should be initializing the device's properties: + 1) Format + 2) Channels + 3) Sample rate. + */ + + /* Format. */ + ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); + return ma_result_from_errno(errno); + } + + /* Channels. */ + ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); + return ma_result_from_errno(errno); + } + + /* Sample Rate. */ + ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); + return ma_result_from_errno(errno); + } + + /* + Buffer. + + The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if + it should be done before or after format/channels/rate. + + OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual + value. + */ + { + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInBytes; + ma_uint32 ossFragmentSizePower; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); + + periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); + if (periodSizeInBytes < 16) { + periodSizeInBytes = 16; + } + + ossFragmentSizePower = 4; + periodSizeInBytes >>= 4; + while (periodSizeInBytes >>= 1) { + ossFragmentSizePower += 1; + } + + ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); + ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); + return ma_result_from_errno(errno); + } + } + + /* Internal settings. */ + if (deviceType == ma_device_type_capture) { + pDevice->oss.fdCapture = fd; + } else { + pDevice->oss.fdPlayback = fd; + } + + pDescriptor->format = ma_format_from_oss(ossFormat); + pDescriptor->channels = ossChannels; + pDescriptor->sampleRate = ossSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); + pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); + pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); + + if (pDescriptor->format == ma_format_unknown) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + + MA_ZERO_OBJECT(&pDevice->oss); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + } + + return MA_SUCCESS; +} + +/* +Note on Starting and Stopping +============================= +In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when +trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will +fail. Instead what we need to do is just not write or read to and from the device when the +device is not running. + +As a result, both the start and stop functions for OSS are just empty stubs. The starting and +stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check +the device state, and if the device is stopped they will simply not do any kind of processing. + +The downside to this technique is that I've noticed a fairly lengthy delay in stopping the +device, up to a second. This is on a virtual machine, and as such might just be due to the +virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for +the moment that's just how it's going to have to be. + +When starting the device, OSS will automatically start it when write() or read() is called. +*/ +static ma_result ma_device_start__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* The device is automatically started with reading and writing. */ + (void)pDevice; + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* See note above on why this is empty. */ + (void)pDevice; + + return MA_SUCCESS; +} + +static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int resultOSS; + ma_uint32 deviceState; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + /* Don't do any processing if the device is stopped. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { + return MA_SUCCESS; + } + + resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (resultOSS < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); + return ma_result_from_errno(errno); + } + + if (pFramesWritten != NULL) { + *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int resultOSS; + ma_uint32 deviceState; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + /* Don't do any processing if the device is stopped. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { + return MA_SUCCESS; + } + + resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (resultOSS < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); + return ma_result_from_errno(errno); + } + + if (pFramesRead != NULL) { + *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__oss(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_oss); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + int fd; + int ossVersion; + int result; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + /* Try opening a temporary device first so we can get version information. This is closed at the end. */ + fd = ma_open_temp_device__oss(); + if (fd == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ + return MA_NO_BACKEND; + } + + /* Grab the OSS version. */ + ossVersion = 0; + result = ioctl(fd, OSS_GETVERSION, &ossVersion); + if (result == -1) { + close(fd); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); + return MA_NO_BACKEND; + } + + /* The file handle to temp device is no longer needed. Close ASAP. */ + close(fd); + + pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); + pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); + + pCallbacks->onContextInit = ma_context_init__oss; + pCallbacks->onContextUninit = ma_context_uninit__oss; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; + pCallbacks->onDeviceInit = ma_device_init__oss; + pCallbacks->onDeviceUninit = ma_device_uninit__oss; + pCallbacks->onDeviceStart = ma_device_start__oss; + pCallbacks->onDeviceStop = ma_device_stop__oss; + pCallbacks->onDeviceRead = ma_device_read__oss; + pCallbacks->onDeviceWrite = ma_device_write__oss; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* OSS */ + + + + + +/****************************************************************************** + +AAudio Backend + +******************************************************************************/ +#ifdef MA_HAS_AAUDIO + +/*#include */ + +typedef int32_t ma_aaudio_result_t; +typedef int32_t ma_aaudio_direction_t; +typedef int32_t ma_aaudio_sharing_mode_t; +typedef int32_t ma_aaudio_format_t; +typedef int32_t ma_aaudio_stream_state_t; +typedef int32_t ma_aaudio_performance_mode_t; +typedef int32_t ma_aaudio_usage_t; +typedef int32_t ma_aaudio_content_type_t; +typedef int32_t ma_aaudio_input_preset_t; +typedef int32_t ma_aaudio_allowed_capture_policy_t; +typedef int32_t ma_aaudio_data_callback_result_t; +typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; +typedef struct ma_AAudioStream_t* ma_AAudioStream; + +#define MA_AAUDIO_UNSPECIFIED 0 + +/* Result codes. miniaudio only cares about the success code. */ +#define MA_AAUDIO_OK 0 + +/* Directions. */ +#define MA_AAUDIO_DIRECTION_OUTPUT 0 +#define MA_AAUDIO_DIRECTION_INPUT 1 + +/* Sharing modes. */ +#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 +#define MA_AAUDIO_SHARING_MODE_SHARED 1 + +/* Formats. */ +#define MA_AAUDIO_FORMAT_PCM_I16 1 +#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 + +/* Stream states. */ +#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 +#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 +#define MA_AAUDIO_STREAM_STATE_OPEN 2 +#define MA_AAUDIO_STREAM_STATE_STARTING 3 +#define MA_AAUDIO_STREAM_STATE_STARTED 4 +#define MA_AAUDIO_STREAM_STATE_PAUSING 5 +#define MA_AAUDIO_STREAM_STATE_PAUSED 6 +#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 +#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 +#define MA_AAUDIO_STREAM_STATE_STOPPING 9 +#define MA_AAUDIO_STREAM_STATE_STOPPED 10 +#define MA_AAUDIO_STREAM_STATE_CLOSING 11 +#define MA_AAUDIO_STREAM_STATE_CLOSED 12 +#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 + +/* Performance modes. */ +#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 +#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 +#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 + +/* Usage types. */ +#define MA_AAUDIO_USAGE_MEDIA 1 +#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 +#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 +#define MA_AAUDIO_USAGE_ALARM 4 +#define MA_AAUDIO_USAGE_NOTIFICATION 5 +#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 +#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 +#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 +#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 +#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 +#define MA_AAUDIO_USAGE_GAME 14 +#define MA_AAUDIO_USAGE_ASSISTANT 16 +#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 +#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 +#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 +#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 + +/* Content types. */ +#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 +#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 +#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 +#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 + +/* Input presets. */ +#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 +#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 +#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 +#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 +#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 +#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 + +/* Allowed Capture Policies */ +#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 + +/* Callback results. */ +#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 +#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 + + +typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); +typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); + +typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); +typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); +typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); +typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); +typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); +typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); +typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); +typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); +typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); +typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); +typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); +typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); + +static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) +{ + switch (resultAA) + { + case MA_AAUDIO_OK: return MA_SUCCESS; + default: break; + } + + return MA_ERROR; +} + +static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) +{ + switch (usage) { + case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; + case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; + case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; + case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; + case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; + case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; + case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; + case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; + case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; + case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; + case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; + case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; + case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; + case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; + case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; + case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; + default: break; + } + + return MA_AAUDIO_USAGE_MEDIA; +} + +static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) +{ + switch (contentType) { + case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; + case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; + case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; + case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; + default: break; + } + + return MA_AAUDIO_CONTENT_TYPE_SPEECH; +} + +static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) +{ + switch (inputPreset) { + case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; + case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; + case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; + case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; + case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; + default: break; + } + + return MA_AAUDIO_INPUT_PRESET_GENERIC; +} + +static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) +{ + switch (allowedCapturePolicy) { + case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; + case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; + case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; + default: break; + } + + return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; +} + +static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) +{ + ma_result result; + ma_job job; + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + (void)error; + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + + /* + When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, + we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this + cleanly and safely. + */ + job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; + + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + } + else { + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } + + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; + } +} + +static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); + + (void)pStream; + return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); + + (void)pStream; + return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) +{ + ma_AAudioStreamBuilder* pBuilder; + ma_aaudio_result_t resultAA; + + /* Safety. */ + *ppBuilder = NULL; + + resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + if (pDeviceID != NULL) { + ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); + } + + ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); + ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); + + + /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ + if (pDescriptor != NULL) { + MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ + + if (pDescriptor->sampleRate != 0) { + ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); + } + + if (deviceType == ma_device_type_capture) { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } + } else { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } + } + + + /* + There have been reports where setting the frames per data callback results in an error + later on from Android. To address this, I'm experimenting with simply not setting it on + anything from Android 11 and earlier. Suggestions welcome on how we might be able to make + this more targetted. + */ + if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { + /* + AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you + retrieve the actual sample rate until after you've opened the stream. But you need to configure + the buffer capacity before you open the stream... :/ + + To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. + */ + ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; + + ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); + ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); + } + + if (deviceType == ma_device_type_capture) { + if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { + ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); + } + + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); + } else { + if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { + ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); + } + + if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { + ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); + } + + if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { + ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); + } + + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); + } + + /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ + ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); + + /* We need to set an error callback to detect device changes. */ + if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ + ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); + } + } + + *ppBuilder = pBuilder; + + return MA_SUCCESS; +} + +static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) +{ + ma_result result; + + result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); + ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); + + return result; +} + +static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) +{ + ma_result result; + ma_AAudioStreamBuilder* pBuilder; + + *ppStream = NULL; + + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); +} + +static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +{ + ma_result result; + ma_AAudioStreamBuilder* pBuilder; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDescriptor != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ + + *ppStream = NULL; + + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); +} + +static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) +{ + return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); +} + +static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) +{ + /* The only way to know this is to try creating a stream. */ + ma_AAudioStream* pStream; + ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + ma_close_stream__aaudio(pContext, pStream); + return MA_TRUE; +} + +static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) +{ + ma_aaudio_stream_state_t actualNewState; + ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + if (newState != actualNewState) { + return MA_ERROR; /* Failed to transition into the expected state. */ + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + + if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + + if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } + + return MA_SUCCESS; +} + +static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pStream != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; +} + +static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + /* AAudio supports s16 and f32. */ + ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); + ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); +} + +static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream; + ma_result result; + + MA_ASSERT(pContext != NULL); + + /* ID */ + if (pDeviceID != NULL) { + pDeviceInfo->id.aaudio = pDeviceID->aaudio; + } else { + pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; + } + + /* Name */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + + pDeviceInfo->nativeDataFormatCount = 0; + + /* We'll need to open the device to get accurate sample rate and channel count information. */ + result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); + if (result != MA_SUCCESS) { + return result; + } + + ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); + + ma_close_stream__aaudio(pContext, pStream); + pStream = NULL; + + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +{ + ma_result result; + int32_t bufferCapacityInFrames; + int32_t framesPerDataCallback; + ma_AAudioStream* pStream; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDescriptor != NULL); + + *ppStream = NULL; /* Safety. */ + + /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ + result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); + if (result != MA_SUCCESS) { + return result; /* Failed to open the AAudio stream. */ + } + + /* Now extract the internal configuration. */ + pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; + pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); + pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); + + /* For the channel map we need to be sure we don't overflow any buffers. */ + if (pDescriptor->channels <= MA_MAX_CHANNELS) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ + } else { + ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ + } + + bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); + framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); + + if (framesPerDataCallback > 0) { + pDescriptor->periodSizeInFrames = framesPerDataCallback; + pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; + } else { + pDescriptor->periodSizeInFrames = bufferCapacityInFrames; + pDescriptor->periodCount = 1; + } + + *ppStream = pStream; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDevice->aaudio.usage = pConfig->aaudio.usage; + pDevice->aaudio.contentType = pConfig->aaudio.contentType; + pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; + pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; + pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) +{ + ma_aaudio_result_t resultAA; + ma_aaudio_stream_state_t currentState; + + MA_ASSERT(pDevice != NULL); + + resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + /* Do we actually need to wait for the device to transition into it's started state? */ + + /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { + ma_result result; + + if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { + return MA_ERROR; /* Expecting the stream to be a starting or started state. */ + } + + result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) +{ + ma_aaudio_result_t resultAA; + ma_aaudio_stream_state_t currentState; + + MA_ASSERT(pDevice != NULL); + + /* + From the AAudio documentation: + + The stream will stop after all of the data currently buffered has been played. + + This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. + */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { + return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ + } + + resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { + ma_result result; + + if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { + return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ + } + + result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + if (pDevice->type == ma_device_type_duplex) { + ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + } + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + +static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is close the streams. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + { + ma_device_config deviceConfig; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + + deviceConfig = ma_device_config_init(deviceType); + deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.playback.shareMode = pDevice->playback.shareMode; + deviceConfig.playback.format = pDevice->playback.format; + deviceConfig.playback.channels = pDevice->playback.channels; + deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.capture.shareMode = pDevice->capture.shareMode; + deviceConfig.capture.format = pDevice->capture.format; + deviceConfig.capture.channels = pDevice->capture.channels; + deviceConfig.sampleRate = pDevice->sampleRate; + deviceConfig.aaudio.usage = pDevice->aaudio.usage; + deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; + deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; + deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; + deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; + deviceConfig.periods = 1; + + /* Try to get an accurate period size. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + } else { + deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; + descriptorCapture.shareMode = deviceConfig.capture.shareMode; + descriptorCapture.format = deviceConfig.capture.format; + descriptorCapture.channels = deviceConfig.capture.channels; + descriptorCapture.sampleRate = deviceConfig.sampleRate; + descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorCapture.periodCount = deviceConfig.periods; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; + descriptorPlayback.shareMode = deviceConfig.playback.shareMode; + descriptorPlayback.format = deviceConfig.playback.format; + descriptorPlayback.channels = deviceConfig.playback.channels; + descriptorPlayback.sampleRate = deviceConfig.sampleRate; + descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorPlayback.periodCount = deviceConfig.periods; + } + + result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit__aaudio(pDevice); + return result; + } + + /* We'll only ever do this in response to a reroute. */ + ma_device__on_notification_rerouted(pDevice); + + /* If the device is started, start the streams. Maybe make this configurable? */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { + ma_device_start__aaudio(pDevice); + } else { + ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ + } + } + + return MA_SUCCESS; + } +} + +static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream = NULL; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(type != ma_device_type_duplex); + MA_ASSERT(pDeviceInfo != NULL); + + if (type == ma_device_type_playback) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; + pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + if (type == ma_device_type_capture) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; + pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + + /* Safety. Should never happen. */ + if (pStream == NULL) { + return MA_INVALID_OPERATION; + } + + pDeviceInfo->nativeDataFormatCount = 0; + ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__aaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_aaudio); + + ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); + + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + + return MA_SUCCESS; +} + +static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + size_t i; + const char* libNames[] = { + "libaaudio.so" + }; + + for (i = 0; i < ma_countof(libNames); ++i) { + pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); + if (pContext->aaudio.hAAudio != NULL) { + break; + } + } + + if (pContext->aaudio.hAAudio == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + + + pCallbacks->onContextInit = ma_context_init__aaudio; + pCallbacks->onContextUninit = ma_context_uninit__aaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; + pCallbacks->onDeviceInit = ma_device_init__aaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; + pCallbacks->onDeviceStart = ma_device_start__aaudio; + pCallbacks->onDeviceStop = ma_device_stop__aaudio; + pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; + + + /* We need a job thread so we can deal with rerouting. */ + { + ma_result result; + ma_device_job_thread_config jobThreadConfig; + + jobThreadConfig = ma_device_job_thread_config_init(); + + result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + return result; + } + } + + + (void)pConfig; + return MA_SUCCESS; +} + +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + ma_device* pDevice; + + MA_ASSERT(pJob != NULL); + + pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; + MA_ASSERT(pDevice != NULL); + + /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ + return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); +} +#else +/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} +#endif /* AAudio */ + + +/****************************************************************************** + +OpenSL|ES Backend + +******************************************************************************/ +#ifdef MA_HAS_OPENSL +#include +#ifdef MA_ANDROID +#include +#endif + +typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); + +/* OpenSL|ES has one-per-application objects :( */ +static SLObjectItf g_maEngineObjectSL = NULL; +static SLEngineItf g_maEngineSL = NULL; +static ma_uint32 g_maOpenSLInitCounter = 0; +static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ + +#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) +#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) +#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) +#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) + +#ifdef MA_ANDROID +#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) +#else +#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) +#endif + +static ma_result ma_result_from_OpenSL(SLuint32 result) +{ + switch (result) + { + case SL_RESULT_SUCCESS: return MA_SUCCESS; + case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; + case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; + case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; + case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; + case SL_RESULT_RESOURCE_LOST: return MA_ERROR; + case SL_RESULT_IO_ERROR: return MA_IO_ERROR; + case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; + case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; + case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; + case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; + case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; + case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; + case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; + case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; + case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; + case SL_RESULT_CONTROL_LOST: return MA_ERROR; + default: return MA_ERROR; + } +} + +/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ +static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) +{ + switch (id) + { + case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; + case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; + case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; + case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ +static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) +{ + switch (id) + { + case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; + case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; + case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; + case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; + case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; + case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts a channel mapping to an OpenSL-style channel mask. */ +static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) +{ + SLuint32 channelMask = 0; + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); + } + + return channelMask; +} + +/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ +static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) +{ + if (channels == 1 && channelMask == 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else if (channels == 2 && channelMask == 0) { + pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; + pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else { + /* Just iterate over each bit. */ + ma_uint32 iChannel = 0; + ma_uint32 iBit; + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { + SLuint32 bitValue = (channelMask & (1UL << iBit)); + if (bitValue != 0) { + /* The bit is set. */ + pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); + iChannel += 1; + } + } + } + } +} + +static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) +{ + if (samplesPerSec <= SL_SAMPLINGRATE_8) { + return SL_SAMPLINGRATE_8; + } + if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { + return SL_SAMPLINGRATE_11_025; + } + if (samplesPerSec <= SL_SAMPLINGRATE_12) { + return SL_SAMPLINGRATE_12; + } + if (samplesPerSec <= SL_SAMPLINGRATE_16) { + return SL_SAMPLINGRATE_16; + } + if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { + return SL_SAMPLINGRATE_22_05; + } + if (samplesPerSec <= SL_SAMPLINGRATE_24) { + return SL_SAMPLINGRATE_24; + } + if (samplesPerSec <= SL_SAMPLINGRATE_32) { + return SL_SAMPLINGRATE_32; + } + if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { + return SL_SAMPLINGRATE_44_1; + } + if (samplesPerSec <= SL_SAMPLINGRATE_48) { + return SL_SAMPLINGRATE_48; + } + + /* Android doesn't support more than 48000. */ +#ifndef MA_ANDROID + if (samplesPerSec <= SL_SAMPLINGRATE_64) { + return SL_SAMPLINGRATE_64; + } + if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { + return SL_SAMPLINGRATE_88_2; + } + if (samplesPerSec <= SL_SAMPLINGRATE_96) { + return SL_SAMPLINGRATE_96; + } + if (samplesPerSec <= SL_SAMPLINGRATE_192) { + return SL_SAMPLINGRATE_192; + } +#endif + + return SL_SAMPLINGRATE_16; +} + + +static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) +{ + switch (streamType) { + case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; + case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; + case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; + case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; + case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; + case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; + default: break; + } + + return SL_ANDROID_STREAM_VOICE; +} + +static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) +{ + switch (recordingPreset) { + case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; + case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; + case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; + case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; + default: break; + } + + return SL_ANDROID_RECORDING_PRESET_NONE; +} + + +static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + /* + TODO: Test Me. + + This is currently untested, so for now we are just returning default devices. + */ +#if 0 && !defined(MA_ANDROID) + ma_bool32 isTerminated = MA_FALSE; + + SLuint32 pDeviceIDs[128]; + SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); + + SLAudioIODeviceCapabilitiesItf deviceCaps; + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + /* The interface may not be supported so just report a default device. */ + goto return_default_device; + } + + /* Playback */ + if (!isTerminated) { + resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; + + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); + + ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + } + + /* Capture */ + if (!isTerminated) { + resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; + + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); + + ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + } + + return MA_SUCCESS; +#else + goto return_default_device; +#endif + +return_default_device:; + cbResult = MA_TRUE; + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + return MA_SUCCESS; +} + +static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; +} + +static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) +{ + ma_uint32 minChannels = 1; + ma_uint32 maxChannels = 2; + ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; + ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; + ma_uint32 iChannel; + ma_uint32 iSampleRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* + Each sample format can support mono and stereo, and we'll support a small subset of standard + rates (up to 48000). A better solution would be to somehow find a native sample rate. + */ + for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; + if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { + ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); + } + } + } +} + +static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + /* + TODO: Test Me. + + This is currently untested, so for now we are just returning default devices. + */ +#if 0 && !defined(MA_ANDROID) + SLAudioIODeviceCapabilitiesItf deviceCaps; + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + /* The interface may not be supported so just report a default device. */ + goto return_default_device; + } + + if (deviceType == ma_device_type_playback) { + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); + } else { + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); + } + + goto return_detailed_info; +#else + goto return_default_device; +#endif + +return_default_device: + if (pDeviceID != NULL) { + if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || + (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + } + + /* ID and Name / Description */ + if (deviceType == ma_device_type_playback) { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; + + goto return_detailed_info; + + +return_detailed_info: + + /* + For now we're just outputting a set of values that are supported by the API but not necessarily supported + by the device natively. Later on we should work on this so that it more closely reflects the device's + actual native format. + */ + pDeviceInfo->nativeDataFormatCount = 0; +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); +#endif + ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); + ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); + + return MA_SUCCESS; +} + + +#ifdef MA_ANDROID +/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ +static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + size_t periodSizeInBytes; + ma_uint8* pBuffer; + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + (void)pBufferQueue; + + /* + For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like + OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, + but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( + */ + + /* Don't do anything if the device is not started. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return; + } + + /* Don't do anything if the device is being drained. */ + if (pDevice->opensl.isDrainingCapture) { + return; + } + + periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); + + ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + + pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; +} + +static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + size_t periodSizeInBytes; + ma_uint8* pBuffer; + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + (void)pBufferQueue; + + /* Don't do anything if the device is not started. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return; + } + + /* Don't do anything if the device is being drained. */ + if (pDevice->opensl.isDrainingPlayback) { + return; + } + + periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); + + ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + + pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; +} +#endif + +static ma_result ma_device_uninit__opensl(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->opensl.pAudioRecorderObj) { + MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); + } + + ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->opensl.pAudioPlayerObj) { + MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); + } + if (pDevice->opensl.pOutputMixObj) { + MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); + } + + ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 +typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; +#else +typedef SLDataFormat_PCM ma_SLDataFormat_PCM; +#endif + +static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) +{ + /* We need to convert our format/channels/rate so that they aren't set to default. */ + if (format == ma_format_unknown) { + format = MA_DEFAULT_FORMAT; + } + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + if (format == ma_format_f32) { + pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; + pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + } else { + pDataFormat->formatType = SL_DATAFORMAT_PCM; + } +#else + pDataFormat->formatType = SL_DATAFORMAT_PCM; +#endif + + pDataFormat->numChannels = channels; + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ + pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; + pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); + pDataFormat->endianness = (ma_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. + - Only u8 and s16 formats are supported. + - Maximum sample rate of 48000. + */ +#ifdef MA_ANDROID + if (pDataFormat->numChannels > 2) { + pDataFormat->numChannels = 2; + } +#if __ANDROID_API__ >= 21 + if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + /* It's floating point. */ + MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + if (pDataFormat->bitsPerSample > 32) { + pDataFormat->bitsPerSample = 32; + } + } else { + if (pDataFormat->bitsPerSample > 16) { + pDataFormat->bitsPerSample = 16; + } + } +#else + if (pDataFormat->bitsPerSample > 16) { + pDataFormat->bitsPerSample = 16; + } +#endif + if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; + } +#endif + + pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ + + return MA_SUCCESS; +} + +static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_bool32 isFloatingPoint = MA_FALSE; +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + isFloatingPoint = MA_TRUE; + } +#endif + if (isFloatingPoint) { + if (pDataFormat->bitsPerSample == 32) { + *pFormat = ma_format_f32; + } + } else { + if (pDataFormat->bitsPerSample == 8) { + *pFormat = ma_format_u8; + } else if (pDataFormat->bitsPerSample == 16) { + *pFormat = ma_format_s16; + } else if (pDataFormat->bitsPerSample == 24) { + *pFormat = ma_format_s24; + } else if (pDataFormat->bitsPerSample == 32) { + *pFormat = ma_format_s32; + } + } + + *pChannels = pDataFormat->numChannels; + *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; + ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); + + return MA_SUCCESS; +} + +static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ +#ifdef MA_ANDROID + SLDataLocator_AndroidSimpleBufferQueue queue; + SLresult resultSL; + size_t bufferSizeInBytes; + SLInterfaceID itfIDs[2]; + const SLboolean itfIDsRequired[] = { + SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ + SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ + }; +#endif + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* + For now, only supporting Android implementations of OpenSL|ES since that's the only one I've + been able to test with and I currently depend on Android-specific extensions (simple buffer + queues). + */ +#ifdef MA_ANDROID + itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; + + /* No exclusive mode with OpenSL|ES. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Now we can start initializing the device properly. */ + MA_ASSERT(pDevice != NULL); + MA_ZERO_OBJECT(&pDevice->opensl); + + queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_SLDataFormat_PCM pcm; + SLDataLocator_IODevice locatorDevice; + SLDataSource source; + SLDataSink sink; + SLAndroidConfigurationItf pRecorderConfig; + + ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); + + locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; + locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; + locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ + locatorDevice.device = NULL; + + source.pLocator = &locatorDevice; + source.pFormat = NULL; + + queue.numBuffers = pDescriptorCapture->periodCount; + + sink.pLocator = &queue; + sink.pFormat = (SLDataFormat_PCM*)&pcm; + + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { + /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ + pcm.formatType = SL_DATAFORMAT_PCM; + pcm.numChannels = 1; + ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ + pcm.bitsPerSample = 16; + pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ + pcm.channelMask = 0; + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + } + + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); + return ma_result_from_OpenSL(resultSL); + } + + + /* Set the recording preset before realizing the player. */ + if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); + if (resultSL == SL_RESULT_SUCCESS) { + SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); + resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); + if (resultSL != SL_RESULT_SUCCESS) { + /* Failed to set the configuration. Just keep going. */ + } + } + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); + } + + /* The internal format is determined by the "pcm" object. */ + ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); + + /* Buffer. */ + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + pDevice->opensl.currentBufferIndexCapture = 0; + + bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; + pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pDevice->opensl.pBufferCapture == NULL) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; + } + MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_SLDataFormat_PCM pcm; + SLDataSource source; + SLDataLocator_OutputMix outmixLocator; + SLDataSink sink; + SLAndroidConfigurationItf pPlayerConfig; + + ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); + + resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); + return ma_result_from_OpenSL(resultSL); + } + + /* Set the output device. */ + if (pDescriptorPlayback->pDeviceID != NULL) { + SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; + MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); + } + + queue.numBuffers = pDescriptorPlayback->periodCount; + + source.pLocator = &queue; + source.pFormat = (SLDataFormat_PCM*)&pcm; + + outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; + + sink.pLocator = &outmixLocator; + sink.pFormat = NULL; + + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { + /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ + pcm.formatType = SL_DATAFORMAT_PCM; + pcm.numChannels = 2; + ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; + pcm.bitsPerSample = 16; + pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ + pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + } + + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); + return ma_result_from_OpenSL(resultSL); + } + + + /* Set the stream type before realizing the player. */ + if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); + if (resultSL == SL_RESULT_SUCCESS) { + SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); + resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); + if (resultSL != SL_RESULT_SUCCESS) { + /* Failed to set the configuration. Just keep going. */ + } + } + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); + } + + /* The internal format is determined by the "pcm" object. */ + ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); + + /* Buffer. */ + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + pDevice->opensl.currentBufferIndexPlayback = 0; + + bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; + pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pDevice->opensl.pBufferPlayback == NULL) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; + } + MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); + } + + return MA_SUCCESS; +#else + return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ +#endif +} + +static ma_result ma_device_start__opensl(ma_device* pDevice) +{ + SLresult resultSL; + size_t periodSizeInBytes; + ma_uint32 iPeriod; + + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); + return ma_result_from_OpenSL(resultSL); + } + + periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); + return ma_result_from_OpenSL(resultSL); + } + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); + return ma_result_from_OpenSL(resultSL); + } + + /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ + if (pDevice->type == ma_device_type_duplex) { + MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + } else { + ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); + } + + periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); + return ma_result_from_OpenSL(resultSL); + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) +{ + SLAndroidSimpleBufferQueueItf pBufferQueue; + + MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); + + if (pDevice->type == ma_device_type_capture) { + pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; + pDevice->opensl.isDrainingCapture = MA_TRUE; + } else { + pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; + pDevice->opensl.isDrainingPlayback = MA_TRUE; + } + + for (;;) { + SLAndroidSimpleBufferQueueState state; + + MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); + if (state.count == 0) { + break; + } + + ma_sleep(10); + } + + if (pDevice->type == ma_device_type_capture) { + pDevice->opensl.isDrainingCapture = MA_FALSE; + } else { + pDevice->opensl.isDrainingPlayback = MA_FALSE; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__opensl(ma_device* pDevice) +{ + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_device_drain__opensl(pDevice, ma_device_type_capture); + + resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); + return ma_result_from_OpenSL(resultSL); + } + + MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_device_drain__opensl(pDevice, ma_device_type_playback); + + resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); + return ma_result_from_OpenSL(resultSL); + } + + MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); + } + + /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__opensl(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_opensl); + (void)pContext; + + /* Uninit global data. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ + + g_maOpenSLInitCounter -= 1; + if (g_maOpenSLInitCounter == 0) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + } + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + return MA_SUCCESS; +} + +static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) +{ + /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ + ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); + if (p == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); + return MA_NO_BACKEND; + } + + *pHandle = *p; + return MA_SUCCESS; +} + +static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) +{ + g_maOpenSLInitCounter += 1; + if (g_maOpenSLInitCounter == 1) { + SLresult resultSL; + + resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + + (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; + +#if !defined(MA_NO_RUNTIME_LINKING) + size_t i; + const char* libOpenSLESNames[] = { + "libOpenSLES.so" + }; +#endif + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + +#if !defined(MA_NO_RUNTIME_LINKING) + /* + Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One + report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime + and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any + references to the symbols and will hopefully skip the checks. + */ + for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { + pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); + if (pContext->opensl.libOpenSLES != NULL) { + break; + } + } + + if (pContext->opensl.libOpenSLES == NULL) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); + return MA_NO_BACKEND; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); + if (pContext->opensl.slCreateEngine == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); + return MA_NO_BACKEND; + } +#else + pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; + pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; + pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; + pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; + pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; + pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; + pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; +#endif + + + /* Initialize global data first if applicable. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + result = ma_context_init_engine_nolock__opensl(pContext); + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); + return result; + } + + pCallbacks->onContextInit = ma_context_init__opensl; + pCallbacks->onContextUninit = ma_context_uninit__opensl; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; + pCallbacks->onDeviceInit = ma_device_init__opensl; + pCallbacks->onDeviceUninit = ma_device_uninit__opensl; + pCallbacks->onDeviceStart = ma_device_start__opensl; + pCallbacks->onDeviceStop = ma_device_stop__opensl; + pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* OpenSL|ES */ + + +/****************************************************************************** + +Web Audio Backend + +******************************************************************************/ +#ifdef MA_HAS_WEBAUDIO +#include + +#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) + #include + #define MA_SUPPORT_AUDIO_WORKLETS +#endif + +/* +TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. +*/ +#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) + #define MA_USE_AUDIO_WORKLETS +#endif + +/* The thread stack size must be a multiple of 16. */ +#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE +#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 +#endif + +#if defined(MA_USE_AUDIO_WORKLETS) +#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" +#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" +#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" +#endif + +static ma_bool32 ma_is_capture_supported__webaudio() +{ + return EM_ASM_INT({ + return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); + }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ +} + +#ifdef __cplusplus +extern "C" { +#endif +void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_malloc(sz, pAllocationCallbacks); +} + +void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_free(p, pAllocationCallbacks); +} + +void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) +{ + ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); +} + +void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) +{ + ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); +} +#ifdef __cplusplus +} +#endif + +static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Only supporting default devices for now. */ + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + if (ma_is_capture_supported__webaudio()) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { + return MA_NO_DEVICE; + } + + MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); + + /* Only supporting default devices for now. */ + (void)pDeviceID; + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + /* Only supporting default devices. */ + pDeviceInfo->isDefault = MA_TRUE; + + /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ + pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ + try { + var temp = new (window.AudioContext || window.webkitAudioContext)(); + var sampleRate = temp.sampleRate; + temp.close(); + return sampleRate; + } catch(e) { + return 0; + } + }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + + if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { + return MA_NO_DEVICE; + } + + pDeviceInfo->nativeDataFormatCount = 1; + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + #if defined(MA_USE_AUDIO_WORKLETS) + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + }, pDevice->webaudio.deviceIndex); + + emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); + } + #else + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + device.pDevice = undefined; + }, pDevice->webaudio.deviceIndex); + } + #endif + + /* Clean up the device on the JS side. */ + EM_ASM({ + miniaudio.untrack_device_by_index($0); + }, pDevice->webaudio.deviceIndex); + + ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + + return MA_SUCCESS; +} + +#if !defined(MA_USE_AUDIO_WORKLETS) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports of the default buffer size being too small on some browsers. If we're using + the default buffer size, we'll make sure the period size is bigger than our standard defaults. + */ + ma_uint32 periodSizeInFrames; + + if (nativeSampleRate == 0) { + nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); + } + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + periodSizeInFrames = pDescriptor->periodSizeInFrames; + } + + /* The size of the buffer must be a power of 2 and between 256 and 16384. */ + if (periodSizeInFrames < 256) { + periodSizeInFrames = 256; + } else if (periodSizeInFrames > 16384) { + periodSizeInFrames = 16384; + } else { + periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); + } + + return periodSizeInFrames; +} +#endif + + +#if defined(MA_USE_AUDIO_WORKLETS) +typedef struct +{ + ma_device* pDevice; + const ma_device_config* pConfig; + ma_device_descriptor* pDescriptorPlayback; + ma_device_descriptor* pDescriptorCapture; +} ma_audio_worklet_thread_initialized_data; + +static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 frameCount; + + (void)paramCount; + (void)pParams; + + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return EM_TRUE; + } + + /* + The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels + like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer + to variables instead of a hard coded number. In any case, will follow along for the time being. + + Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio + for further processing. + */ + frameCount = 128; + + if (inputCount > 0) { + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; + } + } + + ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + } + + if (outputCount > 0) { + /* If it's a capture-only device, we'll need to output silence. */ + if (pDevice->type == ma_device_type_capture) { + MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + } else { + ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + + /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; + } + } + } + } + + return EM_TRUE; +} + + +static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; + int channels = 0; + size_t intermediaryBufferSizeInFrames; + int sampleRate; + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.initResult = MA_ERROR; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + /* The next step is to initialize the audio worklet node. */ + MA_ZERO_OBJECT(&audioWorkletOptions); + + /* + The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel + count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an + output channel count on the capture side. This is slightly confusing for capture mode because intuitively you + wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have + proper control over the channel count. In the capture case, we'll have to output silence to it's output node. + */ + if (pParameters->pConfig->deviceType == ma_device_type_capture) { + channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); + audioWorkletOptions.numberOfInputs = 1; + } else { + channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); + + if (pParameters->pConfig->deviceType == ma_device_type_duplex) { + audioWorkletOptions.numberOfInputs = 1; + } else { + audioWorkletOptions.numberOfInputs = 0; + } + } + + audioWorkletOptions.numberOfOutputs = 1; + audioWorkletOptions.outputChannelCounts = &channels; + + + /* + Now that we know the channel count to use we can allocate the intermediary buffer. The + intermediary buffer is used for interleaving and deinterleaving. + */ + intermediaryBufferSizeInFrames = 128; + + pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); + if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { + pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + + pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + + /* With the audio worklet initialized we can now attach it to the graph. */ + if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { + ma_result attachmentResult = (ma_result)EM_ASM_INT({ + var getUserMediaResult = 0; + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + audioContext.streamNode = audioContext.createMediaStreamSource(stream); + audioContext.streamNode.connect(audioWorklet); + audioWorklet.connect(audioContext.destination); + getUserMediaResult = 0; /* 0 = MA_SUCCESS */ + }) + .catch(function(error) { + console.log("navigator.mediaDevices.getUserMedia Failed: " + error); + getUserMediaResult = -1; /* -1 = MA_ERROR */ + }); + + return getUserMediaResult; + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); + emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } + + /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ + if (pParameters->pConfig->deviceType == ma_device_type_playback) { + ma_result attachmentResult = (ma_result)EM_ASM_INT({ + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + audioWorklet.connect(audioContext.destination); + return 0; /* 0 = MA_SUCCESS */ + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } + + /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ + sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); + + if (pParameters->pDescriptorCapture != NULL) { + pParameters->pDescriptorCapture->format = ma_format_f32; + pParameters->pDescriptorCapture->channels = (ma_uint32)channels; + pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); + pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorCapture->periodCount = 1; + } + + if (pParameters->pDescriptorPlayback != NULL) { + pParameters->pDescriptorPlayback->format = ma_format_f32; + pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; + pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); + pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorPlayback->periodCount = 1; + } + + /* At this point we're done and we can return. */ + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = MA_SUCCESS; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); +} + +static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + WebAudioWorkletProcessorCreateOptions workletProcessorOptions; + + MA_ASSERT(pParameters != NULL); + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.initResult = MA_ERROR; + return; + } + + MA_ZERO_OBJECT(&workletProcessorOptions); + workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ + + emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); +} +#endif + +static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with Web Audio. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* + With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so + it might be worthwhile to look into that as well. + */ + #if defined(MA_USE_AUDIO_WORKLETS) + { + EmscriptenWebAudioCreateAttributes audioContextAttributes; + ma_audio_worklet_thread_initialized_data* pInitParameters; + void* pStackBuffer; + + if (pConfig->performanceProfile == ma_performance_profile_conservative) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; + } else { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + } + + /* + In my testing, Firefox does not seem to capture audio data properly if the sample rate is set + to anything other than 48K. This does not seem to be the case for other browsers. For this reason, + if the device type is anything other than playback, we'll leave the sample rate as-is and let the + browser pick the appropriate rate for us. + */ + if (pConfig->deviceType == ma_device_type_playback) { + audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; + } else { + audioContextAttributes.sampleRate = 0; + } + + /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ + pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + + + /* + With the context created we can now create the worklet. We can only have a single worklet per audio + context which means we'll need to craft this appropriately to handle duplex devices correctly. + */ + + /* + We now need to create a worker thread. This is a bit weird because we need to allocate our + own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to + allocate this on the heap to keep it simple. + */ + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + if (pStackBuffer == NULL) { + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ + pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + if (pInitParameters == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptorPlayback = pDescriptorPlayback; + pInitParameters->pDescriptorCapture = pDescriptorCapture; + + /* + We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of + the Emscripten WebAudio stuff is asynchronous. + */ + pDevice->webaudio.initResult = MA_BUSY; + { + emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + } + while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + + /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ + if (pDevice->webaudio.initResult != MA_SUCCESS) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return pDevice->webaudio.initResult; + } + + /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ + pDevice->webaudio.deviceIndex = EM_ASM_INT({ + return miniaudio.track_device({ + webaudio: emscriptenGetAudioObject($0), + state: 1 /* 1 = ma_device_state_stopped */ + }); + }, pDevice->webaudio.audioContext); + + return MA_SUCCESS; + } + #else + { + /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ + ma_uint32 deviceIndex; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + + /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ + if (pConfig->deviceType == ma_device_type_capture) { + channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + } else { + channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + } + + /* + When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's + native rate. For this reason we're leaving the sample rate untouched for capture devices. + */ + if (pConfig->deviceType == ma_device_type_playback) { + sampleRate = pDescriptorPlayback->sampleRate; + } else { + sampleRate = 0; /* Let the browser decide when capturing. */ + } + + /* The period size needs to be a power of 2. */ + if (pConfig->deviceType == ma_device_type_capture) { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); + } else { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); + } + + /* We need an intermediary buffer for doing interleaving and deinterleaving. */ + pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pDevice->webaudio.pIntermediaryBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceIndex = EM_ASM_INT({ + var deviceType = $0; + var channels = $1; + var sampleRate = $2; + var bufferSize = $3; + var pIntermediaryBuffer = $4; + var pDevice = $5; + + if (typeof(window.miniaudio) === 'undefined') { + return -1; /* Context not initialized. */ + } + + var device = {}; + + /* First thing we need is an AudioContext. */ + var audioContextOptions = {}; + if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { + audioContextOptions.sampleRate = sampleRate; + } + + device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); + device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ + device.state = window.miniaudio.device_state.stopped; + + /* + We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we + need to specify an output and configure the channel count there. + */ + var channelCountIn = 0; + var channelCountOut = channels; + if (deviceType != window.miniaudio.device_type.playback) { + channelCountIn = channels; + } + + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); + + /* The node processing callback. */ + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + } + + /* Do the capture side first. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + /* The data must be interleaved before being processed miniaudio. */ + for (var iChannel = 0; iChannel < channels; iChannel += 1) { + var inputBuffer = e.inputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; + } + } + + _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + } + + if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { + _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + } + } + } else { + /* It's a capture-only device. Make sure the output is silenced. */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } + }; + + /* Now we need to connect our node to the graph. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + console.log("Failed to get user media: " + error); + }); + } + + if (deviceType == miniaudio.device_type.playback) { + device.scriptNode.connect(device.webaudio.destination); + } + + device.pDevice = pDevice; + + return miniaudio.track_device(device); + }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); + + if (deviceIndex < 0) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + pDevice->webaudio.deviceIndex = deviceIndex; + + /* Grab the sample rate from the audio context directly. */ + sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + + if (pDescriptorCapture != NULL) { + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = channels; + pDescriptorCapture->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; + } + + if (pDescriptorPlayback != NULL) { + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = channels; + pDescriptorPlayback->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; + } + + return MA_SUCCESS; + } + #endif +} + +static ma_result ma_device_start__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = miniaudio.device_state.started; + }, pDevice->webaudio.deviceIndex); + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* + From the WebAudio API documentation for AudioContext.suspend(): + + Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the + destination, and then allows the system to release its claim on audio hardware. + + I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to + do any kind of explicit draining. + */ + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = miniaudio.device_state.stopped; + }, pDevice->webaudio.deviceIndex); + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__webaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_webaudio); + + (void)pContext; /* Unused. */ + + /* Remove the global miniaudio object from window if there are no more references to it. */ + EM_ASM({ + if (typeof(window.miniaudio) !== 'undefined') { + window.miniaudio.referenceCount -= 1; + if (window.miniaudio.referenceCount === 0) { + delete window.miniaudio; + } + } + }); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + int resultFromJS; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; /* Unused. */ + + /* Here is where our global JavaScript object is initialized. */ + resultFromJS = EM_ASM_INT({ + if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { + return 0; /* Web Audio not supported. */ + } + + if (typeof(window.miniaudio) === 'undefined') { + window.miniaudio = { + referenceCount: 0 + }; + + /* Device types. */ + window.miniaudio.device_type = {}; + window.miniaudio.device_type.playback = $0; + window.miniaudio.device_type.capture = $1; + window.miniaudio.device_type.duplex = $2; + + /* Device states. */ + window.miniaudio.device_state = {}; + window.miniaudio.device_state.stopped = $3; + window.miniaudio.device_state.started = $4; + + /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + miniaudio.devices = []; + + miniaudio.track_device = function(device) { + /* Try inserting into a free slot first. */ + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == null) { + miniaudio.devices[iDevice] = device; + return iDevice; + } + } + + /* Getting here means there is no empty slots in the array so we just push to the end. */ + miniaudio.devices.push(device); + return miniaudio.devices.length - 1; + }; + + miniaudio.untrack_device_by_index = function(deviceIndex) { + /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ + miniaudio.devices[deviceIndex] = null; + + /* Trim the array if possible. */ + while (miniaudio.devices.length > 0) { + if (miniaudio.devices[miniaudio.devices.length-1] == null) { + miniaudio.devices.pop(); + } else { + break; + } + } + }; + + miniaudio.untrack_device = function(device) { + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == device) { + return miniaudio.untrack_device_by_index(iDevice); + } + } + }; + + miniaudio.get_device_by_index = function(deviceIndex) { + return miniaudio.devices[deviceIndex]; + }; + + miniaudio.unlock_event_types = (function(){ + return ['touchend', 'click']; + })(); + + miniaudio.unlock = function() { + for(var i = 0; i < miniaudio.devices.length; ++i) { + var device = miniaudio.devices[i]; + if (device != null && + device.webaudio != null && + device.state === window.miniaudio.device_state.started) { + + device.webaudio.resume().then(() => { + Module._ma_device__on_notification_unlocked(device.pDevice); + }, + (error) => {console.error("Failed to resume audiocontext", error); + }); + } + } + miniaudio.unlock_event_types.map(function(event_type) { + document.removeEventListener(event_type, miniaudio.unlock, true); + }); + }; + + miniaudio.unlock_event_types.map(function(event_type) { + document.addEventListener(event_type, miniaudio.unlock, true); + }); + } + + window.miniaudio.referenceCount += 1; + + return 1; + }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); + + if (resultFromJS != 1) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pCallbacks->onContextInit = ma_context_init__webaudio; + pCallbacks->onContextUninit = ma_context_uninit__webaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; + pCallbacks->onDeviceInit = ma_device_init__webaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; + pCallbacks->onDeviceStart = ma_device_start__webaudio; + pCallbacks->onDeviceStop = ma_device_stop__webaudio; + pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* Web Audio */ + + + +static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) +{ + /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ + if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { + ma_uint32 iChannel; + + if (channels == 0 || channels > MA_MAX_CHANNELS) { + return MA_FALSE; /* Channel count out of range. */ + } + + /* A channel cannot be present in the channel map more than once. */ + for (iChannel = 0; iChannel < channels; ++iChannel) { + ma_uint32 jChannel; + for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { + if (pChannelMap[iChannel] == pChannelMap[jChannel]) { + return MA_FALSE; + } + } + } + } + + return MA_TRUE; +} + + +static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { + if (pContext->callbacks.onDeviceDataLoop == NULL) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } +} + + +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + if (pDevice->capture.format == ma_format_unknown) { + pDevice->capture.format = pDevice->capture.internalFormat; + } + if (pDevice->capture.channels == 0) { + pDevice->capture.channels = pDevice->capture.internalChannels; + } + if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { + MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); + if (pDevice->capture.internalChannels == pDevice->capture.channels) { + ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); + } else { + if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); + } + } + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (pDevice->playback.format == ma_format_unknown) { + pDevice->playback.format = pDevice->playback.internalFormat; + } + if (pDevice->playback.channels == 0) { + pDevice->playback.channels = pDevice->playback.internalChannels; + } + if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { + MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); + if (pDevice->playback.internalChannels == pDevice->playback.channels) { + ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); + } else { + if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); + } + } + } + } + + if (pDevice->sampleRate == 0) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + pDevice->sampleRate = pDevice->capture.internalSampleRate; + } else { + pDevice->sampleRate = pDevice->playback.internalSampleRate; + } + } + + /* Data converters. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + /* Converting from internal device format to client format. */ + ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + converterConfig.pChannelMapOut = pDevice->capture.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); + if (result != MA_SUCCESS) { + return result; + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + /* Converting from client format to device format. */ + ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + converterConfig.pChannelMapIn = pDevice->playback.channelMap; + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; + converterConfig.channelMixMode = pDevice->playback.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); + if (result != MA_SUCCESS) { + return result; + } + } + + + /* + If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's + a couple of situations where we'll need a heap allocated cache. + + The first is a duplex device for backends that use a callback for data delivery. The reason + this is needed is that the input stage needs to have a buffer to place the input data while it + waits for the playback stage, after which the miniaudio data callback will get fired. This is + not needed for backends that use a blocking API because miniaudio manages temporary buffers on + the stack to achieve this. + + The other situation is when the data converter does not have the ability to query the number + of input frames that are required in order to process a given number of output frames. When + performing data conversion, it's useful if miniaudio know exactly how many frames it needs + from the client in order to generate a given number of output frames. This way, only exactly + the number of frames are needed to be read from the client which means no cache is necessary. + On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read + in fixed sized chunks and then cache any residual unused input frames, those of which will be + processed at a later stage. + */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_uint64 unused; + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; + + if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ + { + /* We need a heap allocated cache. We want to size this based on the period size. */ + void* pNewInputCache; + ma_uint64 newInputCacheCap; + ma_uint64 newInputCacheSizeInBytes; + + newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); + + newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + if (newInputCacheSizeInBytes > MA_SIZE_MAX) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ + } + + pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pNewInputCache == NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; + } + + pDevice->playback.pInputCache = pNewInputCache; + pDevice->playback.inputCacheCap = newInputCacheCap; + } else { + /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* Capture. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->capture.internalFormat = pDescriptorCapture->format; + pDevice->capture.internalChannels = pDescriptorCapture->channels; + pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; + + if (pDevice->capture.internalPeriodSizeInFrames == 0) { + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); + } + } + + /* Playback. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->playback.internalFormat = pDescriptorPlayback->format; + pDevice->playback.internalChannels = pDescriptorPlayback->channels; + pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; + + if (pDevice->playback.internalPeriodSizeInFrames == 0) { + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + } + } + + /* + The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. + For loopback devices, we need to retrieve the name of the playback device. + */ + { + ma_device_info deviceInfo; + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorCapture->pDeviceID == NULL) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); + } + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorPlayback->pDeviceID == NULL) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); + } + } + } + } + + /* Update data conversion. */ + return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ +} + + +static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) +{ + ma_device* pDevice = (ma_device*)pData; +#ifdef MA_WIN32 + HRESULT CoInitializeResult; +#endif + + MA_ASSERT(pDevice != NULL); + +#ifdef MA_WIN32 + CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); +#endif + + /* + When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from + ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately + after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker + thread to signal an event to know when the worker thread is ready for action. + */ + ma_device__set_state(pDevice, ma_device_state_stopped); + ma_event_signal(&pDevice->stopEvent); + + for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ + ma_result startResult; + ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ + + /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ + ma_event_wait(&pDevice->wakeupEvent); + + /* Default result code. */ + pDevice->workResult = MA_SUCCESS; + + /* If the reason for the wake up is that we are terminating, just break from the loop. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + + /* + Getting to this point means the device is wanting to get started. The function that has requested that the device + be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event + in both the success and error case. It's important that the state of the device is set _before_ signaling the event. + */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); + + /* If the device has a start callback, start it now. */ + if (pDevice->pContext->callbacks.onDeviceStart != NULL) { + startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); + } else { + startResult = MA_SUCCESS; + } + + /* + If starting was not successful we'll need to loop back to the start and wait for something + to happen (pDevice->wakeupEvent). + */ + if (startResult != MA_SUCCESS) { + pDevice->workResult = startResult; + ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ + continue; + } + + /* Make sure the state is set appropriately. */ + ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ + ma_event_signal(&pDevice->startEvent); + + ma_device__on_notification_started(pDevice); + + if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { + pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); + } else { + /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ + ma_device_audio_thread__default_read_write(pDevice); + } + + /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ + if (pDevice->pContext->callbacks.onDeviceStop != NULL) { + stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); + } else { + stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ + } + + /* + After the device has stopped, make sure an event is posted. Don't post a stopped event if + stopping failed. This can happen on some backends when the underlying stream has been + stopped due to the device being physically unplugged or disabled via an OS setting. + */ + if (stopResult == MA_SUCCESS) { + ma_device__on_notification_stopped(pDevice); + } + + /* If we stopped because the device has been uninitialized, abort now. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + + /* 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. */ + ma_device__set_state(pDevice, ma_device_state_stopped); + ma_event_signal(&pDevice->stopEvent); + } + +#ifdef MA_WIN32 + if (CoInitializeResult == S_OK) { + ma_CoUninitialize(pDevice->pContext); + } +#endif + + return (ma_thread_result)0; +} + + +/* Helper for determining whether or not the given device is initialized. */ +static ma_bool32 ma_device__is_initialized(ma_device* pDevice) +{ + if (pDevice == NULL) { + return MA_FALSE; + } + + return ma_device_get_state(pDevice) != ma_device_state_uninitialized; +} + + +#ifdef MA_WIN32 +static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) +{ + /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pContext->win32.CoInitializeResult == S_OK) { + ma_CoUninitialize(pContext); + } + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); + #endif + + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); +#else + (void)pContext; +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) +{ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #if defined(MA_WIN32_DESKTOP) + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + #endif + + /* Ole32.dll */ + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); +#else + (void)pContext; /* Unused. */ +#endif + + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + return MA_SUCCESS; +} +#else +static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) +{ + (void)pContext; + + return MA_SUCCESS; +} + +static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) +{ + (void)pContext; + + return MA_SUCCESS; +} +#endif + +static ma_result ma_context_init_backend_apis(ma_context* pContext) +{ + ma_result result; +#ifdef MA_WIN32 + result = ma_context_init_backend_apis__win32(pContext); +#else + result = ma_context_init_backend_apis__nix(pContext); +#endif + + return result; +} + +static ma_result ma_context_uninit_backend_apis(ma_context* pContext) +{ + ma_result result; +#ifdef MA_WIN32 + result = ma_context_uninit_backend_apis__win32(pContext); +#else + result = ma_context_uninit_backend_apis__nix(pContext); +#endif + + return result; +} + + +/* The default capacity doesn't need to be too big. */ +#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY +#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 +#endif + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) +{ + ma_device_job_thread_config config; + + MA_ZERO_OBJECT(&config); + config.noThread = MA_FALSE; + config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; + config.jobQueueFlags = 0; + + return config; +} + + +static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) +{ + ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; + MA_ASSERT(pJobThread != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_device_job_thread_next(pJobThread, &job); + if (result != MA_SUCCESS) { + break; + } + + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJobThread); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + + /* Initialize the job queue before the thread to ensure it's in a valid state. */ + jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); + + result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize job queue. */ + } + + + /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ + if (pConfig->noThread == MA_FALSE) { + result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); + return result; /* Failed to create the job thread. */ + } + + pJobThread->_hasThread = MA_TRUE; + } else { + pJobThread->_hasThread = MA_FALSE; + } + + + return MA_SUCCESS; +} + +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pJobThread == NULL) { + return; + } + + /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ + { + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + ma_device_job_thread_post(pJobThread, &job); + } + + /* Wait for the thread to terminate naturally. */ + if (pJobThread->_hasThread) { + ma_thread_wait(&pJobThread->thread); + } + + /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); +} + +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) +{ + if (pJobThread == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pJobThread->jobQueue, pJob); +} + +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJob); + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pJobThread->jobQueue, pJob); +} + + + +MA_API ma_context_config ma_context_config_init(void) +{ + ma_context_config config; + MA_ZERO_OBJECT(&config); + + return config; +} + +MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) +{ + ma_result result; + ma_context_config defaultConfig; + ma_backend defaultBackends[ma_backend_null+1]; + ma_uint32 iBackend; + ma_backend* pBackendsToIterate; + ma_uint32 backendsToIterateCount; + + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pContext); + + /* Always make sure the config is set first to ensure properties are available as soon as possible. */ + if (pConfig == NULL) { + defaultConfig = ma_context_config_init(); + pConfig = &defaultConfig; + } + + /* Allocation callbacks need to come first because they'll be passed around to other areas. */ + result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + /* Get a lot set up first so we can start logging ASAP. */ + if (pConfig->pLog != NULL) { + pContext->pLog = pConfig->pLog; + } else { + result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); + if (result == MA_SUCCESS) { + pContext->pLog = &pContext->log; + } else { + pContext->pLog = NULL; /* Logging is not available. */ + } + } + + pContext->threadPriority = pConfig->threadPriority; + pContext->threadStackSize = pConfig->threadStackSize; + pContext->pUserData = pConfig->pUserData; + + /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ + result = ma_context_init_backend_apis(pContext); + if (result != MA_SUCCESS) { + return result; + } + + for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { + defaultBackends[iBackend] = (ma_backend)iBackend; + } + + pBackendsToIterate = (ma_backend*)backends; + backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (ma_backend*)defaultBackends; + backendsToIterateCount = ma_countof(defaultBackends); + } + + MA_ASSERT(pBackendsToIterate != NULL); + + for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { + ma_backend backend = pBackendsToIterate[iBackend]; + + /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ + MA_ZERO_OBJECT(&pContext->callbacks); + + /* These backends are using the new callback system. */ + switch (backend) { + #ifdef MA_HAS_WASAPI + case ma_backend_wasapi: + { + pContext->callbacks.onContextInit = ma_context_init__wasapi; + } break; + #endif + #ifdef MA_HAS_DSOUND + case ma_backend_dsound: + { + pContext->callbacks.onContextInit = ma_context_init__dsound; + } break; + #endif + #ifdef MA_HAS_WINMM + case ma_backend_winmm: + { + pContext->callbacks.onContextInit = ma_context_init__winmm; + } break; + #endif + #ifdef MA_HAS_COREAUDIO + case ma_backend_coreaudio: + { + pContext->callbacks.onContextInit = ma_context_init__coreaudio; + } break; + #endif + #ifdef MA_HAS_SNDIO + case ma_backend_sndio: + { + pContext->callbacks.onContextInit = ma_context_init__sndio; + } break; + #endif + #ifdef MA_HAS_AUDIO4 + case ma_backend_audio4: + { + pContext->callbacks.onContextInit = ma_context_init__audio4; + } break; + #endif + #ifdef MA_HAS_OSS + case ma_backend_oss: + { + pContext->callbacks.onContextInit = ma_context_init__oss; + } break; + #endif + #ifdef MA_HAS_PULSEAUDIO + case ma_backend_pulseaudio: + { + pContext->callbacks.onContextInit = ma_context_init__pulse; + } break; + #endif + #ifdef MA_HAS_ALSA + case ma_backend_alsa: + { + pContext->callbacks.onContextInit = ma_context_init__alsa; + } break; + #endif + #ifdef MA_HAS_JACK + case ma_backend_jack: + { + pContext->callbacks.onContextInit = ma_context_init__jack; + } break; + #endif + #ifdef MA_HAS_AAUDIO + case ma_backend_aaudio: + { + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__aaudio; + } + } break; + #endif + #ifdef MA_HAS_OPENSL + case ma_backend_opensl: + { + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__opensl; + } + } break; + #endif + #ifdef MA_HAS_WEBAUDIO + case ma_backend_webaudio: + { + pContext->callbacks.onContextInit = ma_context_init__webaudio; + } break; + #endif + #ifdef MA_HAS_CUSTOM + case ma_backend_custom: + { + /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ + pContext->callbacks = pConfig->custom; + } break; + #endif + #ifdef MA_HAS_NULL + case ma_backend_null: + { + pContext->callbacks.onContextInit = ma_context_init__null; + } break; + #endif + + default: break; + } + + if (pContext->callbacks.onContextInit != NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); + result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); + } else { + /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ + if (backend != ma_backend_custom) { + result = MA_BACKEND_NOT_ENABLED; + } else { + #if !defined(MA_HAS_CUSTOM) + result = MA_BACKEND_NOT_ENABLED; + #else + result = MA_NO_BACKEND; + #endif + } + } + + /* If this iteration was successful, return. */ + if (result == MA_SUCCESS) { + result = ma_mutex_init(&pContext->deviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); + } + + result = ma_mutex_init(&pContext->deviceInfoLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); + } + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); + + pContext->backend = backend; + return result; + } else { + if (result == MA_BACKEND_NOT_ENABLED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); + } + } + } + + /* If we get here it means an error occurred. */ + MA_ZERO_OBJECT(pContext); /* Safety. */ + return MA_NO_BACKEND; +} + +MA_API ma_result ma_context_uninit(ma_context* pContext) +{ + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextUninit != NULL) { + pContext->callbacks.onContextUninit(pContext); + } + + ma_mutex_uninit(&pContext->deviceEnumLock); + ma_mutex_uninit(&pContext->deviceInfoLock); + ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); + ma_context_uninit_backend_apis(pContext); + + if (pContext->pLog == &pContext->log) { + ma_log_uninit(&pContext->log); + } + + return MA_SUCCESS; +} + +MA_API size_t ma_context_sizeof(void) +{ + return sizeof(ma_context); +} + + +MA_API ma_log* ma_context_get_log(ma_context* pContext) +{ + if (pContext == NULL) { + return NULL; + } + + return pContext->pLog; +} + + +MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result; + + if (pContext == NULL || callback == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextEnumerateDevices == NULL) { + return MA_INVALID_OPERATION; + } + + ma_mutex_lock(&pContext->deviceEnumLock); + { + result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); + } + ma_mutex_unlock(&pContext->deviceEnumLock); + + return result; +} + + +static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) +{ + /* + We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device + it's just appended to the end. If it's a playback device it's inserted just before the first capture device. + */ + + /* + First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a + simple fixed size increment for buffer expansion. + */ + const ma_uint32 bufferExpansionCount = 2; + const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; + + if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { + ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; + ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); + if (pNewInfos == NULL) { + return MA_FALSE; /* Out of memory. */ + } + + pContext->pDeviceInfos = pNewInfos; + pContext->deviceInfoCapacity = newCapacity; + } + + if (deviceType == ma_device_type_playback) { + /* Playback. Insert just before the first capture device. */ + + /* The first thing to do is move all of the capture devices down a slot. */ + ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; + size_t iCaptureDevice; + for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { + pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; + } + + /* Now just insert where the first capture device was before moving it down a slot. */ + pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; + pContext->playbackDeviceInfoCount += 1; + } else { + /* Capture. Insert at the end. */ + pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; + pContext->captureDeviceInfoCount += 1; + } + + (void)pUserData; + return MA_TRUE; +} + +MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) +{ + ma_result result; + + /* Safety. */ + if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; + if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; + if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; + if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; + + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextEnumerateDevices == NULL) { + return MA_INVALID_OPERATION; + } + + /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ + ma_mutex_lock(&pContext->deviceEnumLock); + { + /* Reset everything first. */ + pContext->playbackDeviceInfoCount = 0; + pContext->captureDeviceInfoCount = 0; + + /* Now enumerate over available devices. */ + result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); + if (result == MA_SUCCESS) { + /* Playback devices. */ + if (ppPlaybackDeviceInfos != NULL) { + *ppPlaybackDeviceInfos = pContext->pDeviceInfos; + } + if (pPlaybackDeviceCount != NULL) { + *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; + } + + /* Capture devices. */ + if (ppCaptureDeviceInfos != NULL) { + *ppCaptureDeviceInfos = pContext->pDeviceInfos; + /* Capture devices come after playback devices. */ + if (pContext->playbackDeviceInfoCount > 0) { + /* Conditional, because NULL+0 is undefined behavior. */ + *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; + } + } + if (pCaptureDeviceCount != NULL) { + *pCaptureDeviceCount = pContext->captureDeviceInfoCount; + } + } + } + ma_mutex_unlock(&pContext->deviceEnumLock); + + return result; +} + +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + ma_device_info deviceInfo; + + /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ + if (pContext == NULL || pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* Help the backend out by copying over the device ID if we have one. */ + if (pDeviceID != NULL) { + MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); + } + + if (pContext->callbacks.onContextGetDeviceInfo == NULL) { + return MA_INVALID_OPERATION; + } + + ma_mutex_lock(&pContext->deviceInfoLock); + { + result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); + } + ma_mutex_unlock(&pContext->deviceInfoLock); + + *pDeviceInfo = deviceInfo; + return result; +} + +MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) +{ + if (pContext == NULL) { + return MA_FALSE; + } + + return ma_is_loopback_supported(pContext->backend); +} + + +MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) +{ + ma_device_config config; + MA_ZERO_OBJECT(&config); + config.deviceType = deviceType; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ + + return config; +} + +MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) +{ + ma_result result; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + + /* The context can be null, in which case we self-manage it. */ + if (pContext == NULL) { + return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); + } + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDevice); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Check that we have our callbacks defined. */ + if (pContext->callbacks.onDeviceInit == NULL) { + return MA_INVALID_OPERATION; + } + + /* Basic config validation. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (pConfig->capture.channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { + return MA_INVALID_ARGS; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + if (pConfig->playback.channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { + return MA_INVALID_ARGS; + } + } + + pDevice->pContext = pContext; + + /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ + pDevice->pUserData = pConfig->pUserData; + pDevice->onData = pConfig->dataCallback; + pDevice->onNotification = pConfig->notificationCallback; + pDevice->onStop = pConfig->stopCallback; + + if (pConfig->playback.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); + pDevice->playback.pID = &pDevice->playback.id; + } else { + pDevice->playback.pID = NULL; + } + + if (pConfig->capture.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); + pDevice->capture.pID = &pDevice->capture.id; + } else { + pDevice->capture.pID = NULL; + } + + pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; + pDevice->noClip = pConfig->noClip; + pDevice->noDisableDenormals = pConfig->noDisableDenormals; + pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; + ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); + + pDevice->type = pConfig->deviceType; + pDevice->sampleRate = pConfig->sampleRate; + pDevice->resampling.algorithm = pConfig->resampling.algorithm; + pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; + pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; + pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; + + pDevice->capture.shareMode = pConfig->capture.shareMode; + pDevice->capture.format = pConfig->capture.format; + pDevice->capture.channels = pConfig->capture.channels; + ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); + pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; + pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; + + pDevice->playback.shareMode = pConfig->playback.shareMode; + pDevice->playback.format = pConfig->playback.format; + pDevice->playback.channels = pConfig->playback.channels; + ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); + pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; + pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; + + result = ma_mutex_init(&pDevice->startStopLock); + if (result != MA_SUCCESS) { + return result; + } + + /* + When the device is started, the worker thread is the one that does the actual startup of the backend device. We + use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. + + Each of these semaphores is released internally by the worker thread when the work is completed. The start + semaphore is also used to wake up the worker thread. + */ + result = ma_event_init(&pDevice->wakeupEvent); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + result = ma_event_init(&pDevice->startEvent); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + result = ma_event_init(&pDevice->stopEvent); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + + MA_ZERO_OBJECT(&descriptorPlayback); + descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; + descriptorPlayback.shareMode = pConfig->playback.shareMode; + descriptorPlayback.format = pConfig->playback.format; + descriptorPlayback.channels = pConfig->playback.channels; + descriptorPlayback.sampleRate = pConfig->sampleRate; + ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); + descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; + descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; + descriptorPlayback.periodCount = pConfig->periods; + + if (descriptorPlayback.periodCount == 0) { + descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; + } + + + MA_ZERO_OBJECT(&descriptorCapture); + descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; + descriptorCapture.shareMode = pConfig->capture.shareMode; + descriptorCapture.format = pConfig->capture.format; + descriptorCapture.channels = pConfig->capture.channels; + descriptorCapture.sampleRate = pConfig->sampleRate; + ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); + descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; + descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; + descriptorCapture.periodCount = pConfig->periods; + + if (descriptorCapture.periodCount == 0) { + descriptorCapture.periodCount = MA_DEFAULT_PERIODS; + } + + + result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + +#if 0 + /* + On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between + the requested format and the internal format. + */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + if (!ma_device_descriptor_is_valid(&descriptorCapture)) { + ma_device_uninit(pDevice); + return MA_INVALID_ARGS; + } + + pDevice->capture.internalFormat = descriptorCapture.format; + pDevice->capture.internalChannels = descriptorCapture.channels; + pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; + ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); + pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; + pDevice->capture.internalPeriods = descriptorCapture.periodCount; + + if (pDevice->capture.internalPeriodSizeInFrames == 0) { + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { + ma_device_uninit(pDevice); + return MA_INVALID_ARGS; + } + + pDevice->playback.internalFormat = descriptorPlayback.format; + pDevice->playback.internalChannels = descriptorPlayback.channels; + pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; + ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); + pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; + pDevice->playback.internalPeriods = descriptorPlayback.periodCount; + + if (pDevice->playback.internalPeriodSizeInFrames == 0) { + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); + } + } + + + /* + The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. + For loopback devices, we need to retrieve the name of the playback device. + */ + { + ma_device_info deviceInfo; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (descriptorCapture.pDeviceID == NULL) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); + } + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (descriptorPlayback.pDeviceID == NULL) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); + } + } + } + } + + + ma_device__post_init_setup(pDevice, pConfig->deviceType); +#endif + + result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + + + /* + If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to + be done after post_init_setup() because we'll need access to the sample rate. + */ + if (pConfig->noFixedSizedCallback == MA_FALSE) { + /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ + ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; + if (intermediaryBufferCap == 0) { + intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + ma_uint32 intermediaryBufferSizeInBytes; + + pDevice->capture.intermediaryBufferLen = 0; + pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->capture.intermediaryBufferCap == 0) { + pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; + } + + intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->capture.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); + pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint64 intermediaryBufferSizeInBytes; + + pDevice->playback.intermediaryBufferLen = 0; + if (pConfig->deviceType == ma_device_type_duplex) { + pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ + } else { + pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->playback.intermediaryBufferCap == 0) { + pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; + } + } + + intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->playback.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); + pDevice->playback.intermediaryBufferLen = 0; + } + } else { + /* Not using a fixed sized data callback so no need for an intermediary buffer. */ + } + + + /* Some backends don't require the worker thread. */ + if (!ma_context_is_backend_asynchronous(pContext)) { + /* The worker thread. */ + result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + + /* Wait for the worker thread to put the device into it's stopped state for real. */ + ma_event_wait(&pDevice->stopEvent); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + } else { + /* + If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done + after ma_device__post_init_setup(). + */ + if (ma_context_is_backend_asynchronous(pContext)) { + if (pConfig->deviceType == ma_device_type_duplex) { + result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + } + } + + ma_device__set_state(pDevice, ma_device_state_stopped); + } + + /* Log device information. */ + { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } + } + } + + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + return MA_SUCCESS; +} + +MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) +{ + ma_result result; + ma_context* pContext; + ma_backend defaultBackends[ma_backend_null+1]; + ma_uint32 iBackend; + ma_backend* pBackendsToIterate; + ma_uint32 backendsToIterateCount; + ma_allocation_callbacks allocationCallbacks; + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pContextConfig != NULL) { + result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + } else { + allocationCallbacks = ma_allocation_callbacks_init_default(); + } + + pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); + if (pContext == NULL) { + return MA_OUT_OF_MEMORY; + } + + for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { + defaultBackends[iBackend] = (ma_backend)iBackend; + } + + pBackendsToIterate = (ma_backend*)backends; + backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (ma_backend*)defaultBackends; + backendsToIterateCount = ma_countof(defaultBackends); + } + + result = MA_NO_BACKEND; + + for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + /* + This is a hack for iOS. If the context config is null, there's a good chance the + `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this + case, set the session category based on the device type. + */ + #if defined(MA_APPLE_MOBILE) + ma_context_config contextConfig; + + if (pContextConfig == NULL) { + contextConfig = ma_context_config_init(); + switch (pConfig->deviceType) { + case ma_device_type_duplex: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; + } break; + case ma_device_type_capture: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; + } break; + case ma_device_type_playback: + default: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; + } break; + } + + pContextConfig = &contextConfig; + } + #endif + + result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); + if (result == MA_SUCCESS) { + result = ma_device_init(pContext, pConfig, pDevice); + if (result == MA_SUCCESS) { + break; /* Success. */ + } else { + ma_context_uninit(pContext); /* Failure. */ + } + } + } + + if (result != MA_SUCCESS) { + ma_free(pContext, &allocationCallbacks); + return result; + } + + pDevice->isOwnerOfContext = MA_TRUE; + return result; +} + +MA_API void ma_device_uninit(ma_device* pDevice) +{ + if (!ma_device__is_initialized(pDevice)) { + return; + } + + /* + It's possible for the miniaudio side of the device and the backend to not be in sync due to + system-level situations such as the computer being put into sleep mode and the backend not + notifying miniaudio of the fact the device has stopped. It's possible for this to result in a + deadlock due to miniaudio thinking the device is in a running state, when in fact it's not + running at all. For this reason I am no longer explicitly stopping the device. I don't think + this should affect anyone in practice since uninitializing the backend will naturally stop the + device anyway. + */ + #if 0 + { + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); + } + } + #endif + + /* Putting the device into an uninitialized state will make the worker thread return. */ + ma_device__set_state(pDevice, ma_device_state_uninitialized); + + /* Wake up the worker thread and wait for it to properly terminate. */ + if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { + ma_event_signal(&pDevice->wakeupEvent); + ma_thread_wait(&pDevice->thread); + } + + if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { + pDevice->pContext->callbacks.onDeviceUninit(pDevice); + } + + + ma_event_uninit(&pDevice->stopEvent); + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + if (pDevice->type == ma_device_type_duplex) { + ma_duplex_rb_uninit(&pDevice->duplexRB); + } + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->playback.pInputCache != NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->capture.pIntermediaryBuffer != NULL) { + ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->playback.pIntermediaryBuffer != NULL) { + ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->isOwnerOfContext) { + ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; + + ma_context_uninit(pDevice->pContext); + ma_free(pDevice->pContext, &allocationCallbacks); + } + + MA_ZERO_OBJECT(pDevice); +} + +MA_API ma_context* ma_device_get_context(ma_device* pDevice) +{ + if (pDevice == NULL) { + return NULL; + } + + return pDevice->pContext; +} + +MA_API ma_log* ma_device_get_log(ma_device* pDevice) +{ + return ma_context_get_log(ma_device_get_context(pDevice)); +} + +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + if (pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDeviceInfo); + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ + if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { + return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); + } + + /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ + if (type == ma_device_type_playback) { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); + } else { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); + } +} + +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) +{ + ma_result result; + ma_device_info deviceInfo; + + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = 0; + } + + if (pName != NULL && nameCap > 0) { + pName[0] = '\0'; + } + + result = ma_device_get_info(pDevice, type, &deviceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pName != NULL) { + ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); + + /* + For safety, make sure the length is based on the truncated output string rather than the + source. Otherwise the caller might assume the output buffer contains more content than it + actually does. + */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(pName); + } + } else { + /* Name not specified. Just report the length of the source string. */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_start(ma_device* pDevice) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ + } + + if (ma_device_get_state(pDevice) == ma_device_state_started) { + return MA_SUCCESS; /* Already started. */ + } + + ma_mutex_lock(&pDevice->startStopLock); + { + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + + ma_device__set_state(pDevice, ma_device_state_starting); + + /* Asynchronous backends need to be handled differently. */ + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + if (pDevice->pContext->callbacks.onDeviceStart != NULL) { + result = pDevice->pContext->callbacks.onDeviceStart(pDevice); + } else { + result = MA_INVALID_OPERATION; + } + + if (result == MA_SUCCESS) { + ma_device__set_state(pDevice, ma_device_state_started); + ma_device__on_notification_started(pDevice); + } + } else { + /* + Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the + thread and then wait for the start event. + */ + ma_event_signal(&pDevice->wakeupEvent); + + /* + Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device + into the started state. Don't call ma_device__set_state() here. + */ + ma_event_wait(&pDevice->startEvent); + result = pDevice->workResult; + } + + /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ + if (result != MA_SUCCESS) { + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } + ma_mutex_unlock(&pDevice->startStopLock); + + return result; +} + +MA_API ma_result ma_device_stop(ma_device* pDevice) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ + } + + if (ma_device_get_state(pDevice) == ma_device_state_stopped) { + return MA_SUCCESS; /* Already stopped. */ + } + + ma_mutex_lock(&pDevice->startStopLock); + { + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); + + ma_device__set_state(pDevice, ma_device_state_stopping); + + /* Asynchronous backends need to be handled differently. */ + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + /* Asynchronous backends must have a stop operation. */ + if (pDevice->pContext->callbacks.onDeviceStop != NULL) { + result = pDevice->pContext->callbacks.onDeviceStop(pDevice); + } else { + result = MA_INVALID_OPERATION; + } + + ma_device__set_state(pDevice, ma_device_state_stopped); + } else { + /* + Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If + the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make + sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super + important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. + */ + MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); + + if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { + pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); + } + + /* + We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be + the one who puts the device into the stopped state. Don't call ma_device__set_state() here. + */ + ma_event_wait(&pDevice->stopEvent); + result = MA_SUCCESS; + } + + /* + This is a safety measure to ensure the internal buffer has been cleared so any leftover + does not get played the next time the device starts. Ideally this should be drained by + the backend first. + */ + pDevice->playback.intermediaryBufferLen = 0; + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; + } + ma_mutex_unlock(&pDevice->startStopLock); + + return result; +} + +MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) +{ + return ma_device_get_state(pDevice) == ma_device_state_started; +} + +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) +{ + if (pDevice == NULL) { + return ma_device_state_uninitialized; + } + + return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ +} + +MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) +{ + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (volume < 0.0f) { + return MA_INVALID_ARGS; + } + + ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) +{ + if (pVolume == NULL) { + return MA_INVALID_ARGS; + } + + if (pDevice == NULL) { + *pVolume = 0; + return MA_INVALID_ARGS; + } + + *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) +{ + if (gainDB > 0) { + return MA_INVALID_ARGS; + } + + return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); +} + +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) +{ + float factor; + ma_result result; + + if (pGainDB == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_device_get_master_volume(pDevice, &factor); + if (result != MA_SUCCESS) { + *pGainDB = 0; + return result; + } + + *pGainDB = ma_volume_linear_to_db(factor); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (pOutput == NULL && pInput == NULL) { + return MA_INVALID_ARGS; + } + + if (pDevice->type == ma_device_type_duplex) { + if (pInput != NULL) { + ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); + } + + if (pOutput != NULL) { + ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); + } + } else { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { + if (pInput == NULL) { + return MA_INVALID_ARGS; + } + + ma_device__send_frames_to_client(pDevice, frameCount, pInput); + } + + if (pDevice->type == ma_device_type_playback) { + if (pOutput == NULL) { + return MA_INVALID_ARGS; + } + + ma_device__read_frames_from_client(pDevice, frameCount, pOutput); + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + if (pDescriptor == NULL) { + return 0; + } + + /* + We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the + time when the size of the buffer needs to be determined. In this case we need to just take a best + guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll + just fall back to MA_DEFAULT_SAMPLE_RATE. + */ + if (nativeSampleRate == 0) { + nativeSampleRate = pDescriptor->sampleRate; + } + if (nativeSampleRate == 0) { + nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + MA_ASSERT(nativeSampleRate != 0); + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } +} +#endif /* MA_NO_DEVICE_IO */ + + +MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) +{ + /* Prevent a division by zero. */ + if (sampleRate == 0) { + return 0; + } + + return bufferSizeInFrames*1000 / sampleRate; +} + +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) +{ + /* Prevent a division by zero. */ + if (sampleRate == 0) { + return 0; + } + + return bufferSizeInMilliseconds*sampleRate / 1000; +} + +MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + if (dst == src) { + return; /* No-op. */ + } + + ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); +} + +MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + if (format == ma_format_u8) { + ma_uint64 sampleCount = frameCount * channels; + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ((ma_uint8*)p)[iSample] = 128; + } + } else { + ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); + } +} + +MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) +{ + return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); +} + +MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) +{ + return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); +} + + +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(pSrc[iSample]); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(pSrc[iSample]); + } +} + +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + ma_uint64 sampleCount; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; + case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; + case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; + } +} + + +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + ma_uint8* pSamplesOut8; + ma_uint8* pSamplesIn8; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + pSamplesOut8 = (ma_uint8*)pSamplesOut; + pSamplesIn8 = (ma_uint8*)pSamplesIn; + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ma_int32 sampleS32; + + sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); + sampleS32 = (ma_int32)(sampleS32 * factor); + + pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); + pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); + pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + if (factor == 1) { + if (pSamplesOut == pSamplesIn) { + /* In place. No-op. */ + } else { + /* Just a copy. */ + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample]; + } + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample] * factor; + } + } +} + +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +{ + switch (format) + { + case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; + case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; + case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; + default: return; /* Do nothing. */ + } +} + +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); +} + + +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) +{ + ma_uint64 iFrame; + + if (channels == 2) { + /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; + } + } +} + + + +static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) +{ + return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); +} + +static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) +{ + return (ma_int32)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) +{ + return x * volume; +} + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) +{ + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + if (volume == 1) { + ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ + } else if (volume == 0) { + ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ + } else { + ma_uint64 sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; + case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; + case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; + } + } +} + + + +MA_API float ma_volume_linear_to_db(float factor) +{ + return 20*ma_log10f(factor); +} + +MA_API float ma_volume_db_to_linear(float gain) +{ + return ma_powf(10, gain/20.0f); +} + + +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) +{ + ma_uint64 iSample; + ma_uint64 sampleCount; + + if (pDst == NULL || pSrc == NULL || channels == 0) { + return MA_INVALID_ARGS; + } + + if (volume == 0) { + return MA_SUCCESS; /* No changes if the volume is 0. */ + } + + sampleCount = frameCount * channels; + + if (volume == 1) { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += pSrc[iSample]; + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); + } + } + + return MA_SUCCESS; +} + + + +/************************************************************************************************************************************************************** + +Format Conversion + +**************************************************************************************************************************************************************/ + +static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) +{ + return (ma_int16)(x * 32767.0f); +} + +static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) +{ + return (ma_int16)((ma_int16)x - 128); +} + +static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) +{ + return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ +} + +static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) +{ + s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); + s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); + s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); +} + + +/* u8 */ +MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); +} + + +static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_u8[i]; + x = (ma_int16)(x - 128); + x = (ma_int16)(x << 8); + dst_s16[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_u8[i]; + x = (ma_int16)(x - 128); + + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = 0; + dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_u8[i]; + x = x - 128; + x = x << 24; + dst_s32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_u8[i]; + x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +#ifdef MA_USE_REFERENCE_CONVERSION_APIS +static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8** src_u8 = (const ma_uint8**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } +} +#else +static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8** src_u8 = (const ma_uint8**)src; + + if (channels == 1) { + ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); + } else if (channels == 2) { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; + dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; + } + } else { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } + } +} +#endif + +MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8** dst_u8 = (ma_uint8**)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +/* s16 */ +static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_s16[i]; + x = (ma_int16)(x >> 8); + x = (ma_int16)(x + 128); + dst_u8[i] = (ma_uint8)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_s16[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); + if ((x + dither) <= 0x7FFF) { + x = (ma_int16)(x + dither); + } else { + x = 0x7FFF; + } + + x = (ma_int16)(x >> 8); + x = (ma_int16)(x + 128); + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); +} + + +static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); + dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = src_s16[i] << 16; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_s16[i]; + +#if 0 + /* The accurate way. */ + x = x + 32768.0f; /* -32768..32767 to 0..65535 */ + x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ +#else + /* The fast way. */ + x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ +#endif + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_int16** src_s16 = (const ma_int16**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int16** dst_s16 = (ma_int16**)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +/* s24 */ +static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); + ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); + dst_s16[i] = (ma_int16)(dst_lo | dst_hi); + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } +} + +static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * 3); +} + + +static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); + +#if 0 + /* The accurate way. */ + x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ + x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ +#else + /* The fast way. */ + x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ +#endif + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst8 = (ma_uint8*)dst; + const ma_uint8** src8 = (const ma_uint8**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; + dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; + dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8** dst8 = (ma_uint8**)dst; + const ma_uint8* src8 = (const ma_uint8*)src; + + ma_uint32 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; + dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; + dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + + +/* s32 */ +static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } +} + +static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_uint32 x = (ma_uint32)src_s32[i]; + dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); + dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); + dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); + } + + (void)ditherMode; /* No dithering for s32 -> s24. */ +} + +static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); +} + + +static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + double x = src_s32[i]; + +#if 0 + x = x + 2147483648.0; + x = x * 0.0000000004656612873077392578125; + x = x - 1; +#else + x = x / 2147483648.0; +#endif + + dst_f32[i] = (float)x; + } + + (void)ditherMode; /* No dithering for s32 -> f32. */ +} + +static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_int32** src_s32 = (const ma_int32**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int32** dst_s32 = (ma_int32**)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +/* f32 */ +static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + + ma_uint8* dst_u8 = (ma_uint8*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -128; + ditherMax = 1.0f / 127; + } + + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 127.5f; /* 0..2 to 0..255 */ + + dst_u8[i] = (ma_uint8)x; + } +} + +static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + +#ifdef MA_USE_REFERENCE_CONVERSION_APIS +static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + + ma_int16* dst_s16 = (ma_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 32767.5f; /* 0..2 to 0..65535 */ + x = x - 32768.0f; /* 0...65535 to -32768..32767 */ +#else + /* The fast way. */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ +#endif + + dst_s16[i] = (ma_int16)x; + } +} +#else +static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i4; + ma_uint64 count4; + + ma_int16* dst_s16 = (ma_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + /* Unrolled. */ + i = 0; + count4 = count >> 2; + for (i4 = 0; i4 < count4; i4 += 1) { + float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + + float x0 = src_f32[i+0]; + float x1 = src_f32[i+1]; + float x2 = src_f32[i+2]; + float x3 = src_f32[i+3]; + + x0 = x0 + d0; + x1 = x1 + d1; + x2 = x2 + d2; + x3 = x3 + d3; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst_s16[i+0] = (ma_int16)x0; + dst_s16[i+1] = (ma_int16)x1; + dst_s16[i+2] = (ma_int16)x2; + dst_s16[i+3] = (ma_int16)x3; + + i += 4; + } + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i8; + ma_uint64 count8; + ma_int16* dst_s16; + const float* src_f32; + float ditherMin; + float ditherMax; + + /* Both the input and output buffers need to be aligned to 16 bytes. */ + if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + dst_s16 = (ma_int16*)dst; + src_f32 = (const float*)src; + + ditherMin = 0; + ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + i = 0; + + /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ + count8 = count >> 3; + for (i8 = 0; i8 < count8; i8 += 1) { + __m128 d0; + __m128 d1; + __m128 x0; + __m128 x1; + + if (ditherMode == ma_dither_mode_none) { + d0 = _mm_set1_ps(0); + d1 = _mm_set1_ps(0); + } else if (ditherMode == ma_dither_mode_rectangle) { + d0 = _mm_set_ps( + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax) + ); + } else { + d0 = _mm_set_ps( + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax) + ); + } + + x0 = *((__m128*)(src_f32 + i) + 0); + x1 = *((__m128*)(src_f32 + i) + 1); + + x0 = _mm_add_ps(x0, d0); + x1 = _mm_add_ps(x1, d1); + + x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); + x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); + + _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); + + i += 8; + } + + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} +#endif /* SSE2 */ + +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i8; + ma_uint64 count8; + ma_int16* dst_s16; + const float* src_f32; + float ditherMin; + float ditherMax; + + if (!ma_has_neon()) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + /* Both the input and output buffers need to be aligned to 16 bytes. */ + if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + dst_s16 = (ma_int16*)dst; + src_f32 = (const float*)src; + + ditherMin = 0; + ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + i = 0; + + /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ + count8 = count >> 3; + for (i8 = 0; i8 < count8; i8 += 1) { + float32x4_t d0; + float32x4_t d1; + float32x4_t x0; + float32x4_t x1; + int32x4_t i0; + int32x4_t i1; + + if (ditherMode == ma_dither_mode_none) { + d0 = vmovq_n_f32(0); + d1 = vmovq_n_f32(0); + } else if (ditherMode == ma_dither_mode_rectangle) { + float d0v[4]; + float d1v[4]; + + d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } else { + float d0v[4]; + float d1v[4]; + + d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } + + x0 = *((float32x4_t*)(src_f32 + i) + 0); + x1 = *((float32x4_t*)(src_f32 + i) + 1); + + x0 = vaddq_f32(x0, d0); + x1 = vaddq_f32(x1, d1); + + x0 = vmulq_n_f32(x0, 32767.0f); + x1 = vmulq_n_f32(x1, 32767.0f); + + i0 = vcvtq_s32_f32(x0); + i1 = vcvtq_s32_f32(x1); + *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); + + i += 8; + } + + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} +#endif /* Neon */ +#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ + +MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const float* src_f32 = (const float*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 r; + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 8388607.5f; /* 0..2 to 0..16777215 */ + x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ +#else + /* The fast way. */ + x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ +#endif + + r = (ma_int32)x; + dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); + dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); + dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); + } + + (void)ditherMode; /* No dithering for f32 -> s24. */ +} + +static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const float* src_f32 = (const float*)src; + + ma_uint32 i; + for (i = 0; i < count; i += 1) { + double x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ + x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ +#else + /* The fast way. */ + x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ +#endif + + dst_s32[i] = (ma_int32)x; + } + + (void)ditherMode; /* No dithering for f32 -> s32. */ +} + +static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * sizeof(float)); +} + + +static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + float* dst_f32 = (float*)dst; + const float** src_f32 = (const float**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; + } + } +} + +static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + float** dst_f32 = (float**)dst; + const float* src_f32 = (const float*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; + } + } +} + +static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) +{ + if (formatOut == formatIn) { + ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); + return; + } + + switch (formatIn) + { + case ma_format_u8: + { + switch (formatOut) + { + case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s16: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s24: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s32: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_f32: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + default: break; + } +} + +MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) +{ + ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); +} + +MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) +{ + if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { + return; /* Invalid args. */ + } + + /* For efficiency we do this per format. */ + switch (format) { + case ma_format_s16: + { + const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; + pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; + } + } + } break; + + case ma_format_f32: + { + const float* pSrcF32 = (const float*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; + pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; + } + } + } break; + + default: + { + ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); + const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); + memcpy(pDst, pSrc, sampleSizeInBytes); + } + } + } break; + } +} + +MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) +{ + switch (format) + { + case ma_format_s16: + { + ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; + pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; + } + } + } break; + + case ma_format_f32: + { + float* pDstF32 = (float*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; + pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; + } + } + } break; + + default: + { + ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); + const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); + memcpy(pDst, pSrc, sampleSizeInBytes); + } + } + } break; + } +} + + +/************************************************************************************************************************************************************** + +Biquad Filter + +**************************************************************************************************************************************************************/ +#ifndef MA_BIQUAD_FIXED_POINT_SHIFT +#define MA_BIQUAD_FIXED_POINT_SHIFT 14 +#endif + +static ma_int32 ma_biquad_float_to_fp(double x) +{ + return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); +} + +MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) +{ + ma_biquad_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.b0 = b0; + config.b1 = b1; + config.b2 = b2; + config.a0 = a0; + config.a1 = a1; + config.a2 = a2; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; + size_t r2Offset; +} ma_biquad_heap_layout; + +static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R0 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* R1 */ + pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + + if (pBQ == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBQ); + + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pBQ->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); + + return ma_biquad_reinit(pConfig, pBQ); +} + +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBQ->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBQ == NULL) { + return; + } + + if (pBQ->_ownsHeap) { + ma_free(pBQ->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) +{ + if (pBQ == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->a0 == 0) { + return MA_INVALID_ARGS; /* Division by zero. */ + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + + pBQ->format = pConfig->format; + pBQ->channels = pConfig->channels; + + /* Normalize. */ + if (pConfig->format == ma_format_f32) { + pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); + pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); + pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); + pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); + pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); + } else { + pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); + pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); + pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); + pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); + pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) +{ + if (pBQ == NULL) { + return MA_INVALID_ARGS; + } + + if (pBQ->format == ma_format_f32) { + pBQ->pR1->f32 = 0; + pBQ->pR2->f32 = 0; + } else { + pBQ->pR1->s32 = 0; + pBQ->pR2->s32 = 0; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pBQ->channels; + const float b0 = pBQ->b0.f32; + const float b1 = pBQ->b1.f32; + const float b2 = pBQ->b2.f32; + const float a1 = pBQ->a1.f32; + const float a2 = pBQ->a2.f32; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pBQ->pR1[c].f32; + float r2 = pBQ->pR2[c].f32; + float x = pX[c]; + float y; + + y = b0*x + r1; + r1 = b1*x - a1*y + r2; + r2 = b2*x - a2*y; + + pY[c] = y; + pBQ->pR1[c].f32 = r1; + pBQ->pR2[c].f32 = r2; + } +} + +static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) +{ + ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); +} + +static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pBQ->channels; + const ma_int32 b0 = pBQ->b0.s32; + const ma_int32 b1 = pBQ->b1.s32; + const ma_int32 b2 = pBQ->b2.s32; + const ma_int32 a1 = pBQ->a1.s32; + const ma_int32 a2 = pBQ->a2.s32; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pBQ->pR1[c].s32; + ma_int32 r2 = pBQ->pR2[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + r1 = (b1*x - a1*y + r2); + r2 = (b2*x - a2*y); + + pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); + pBQ->pR1[c].s32 = r1; + pBQ->pR2[c].s32 = r2; + } +} + +static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) +{ + ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); +} + +MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pBQ->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); + pY += pBQ->channels; + pX += pBQ->channels; + } + } else if (pBQ->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); + pY += pBQ->channels; + pX += pBQ->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) +{ + if (pBQ == NULL) { + return 0; + } + + return 2; +} + + +/************************************************************************************************************************************************************** + +Low-Pass Filter + +**************************************************************************************************************************************************************/ +MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) +{ + ma_lpf1_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = 0.5; + + return config; +} + +MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_lpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; +} ma_lpf1_heap_layout; + +static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + + return ma_lpf1_reinit(pConfig, pLPF); +} + +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) +{ + double a; + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + pLPF->format = pConfig->format; + pLPF->channels = pConfig->channels; + + a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); + if (pConfig->format == ma_format_f32) { + pLPF->a.f32 = (float)a; + } else { + pLPF->a.s32 = ma_biquad_float_to_fp(a); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + if (pLPF->format == ma_format_f32) { + pLPF->a.f32 = 0; + } else { + pLPF->a.s32 = 0; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pLPF->channels; + const float a = pLPF->a.f32; + const float b = 1 - a; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pLPF->pR1[c].f32; + float x = pX[c]; + float y; + + y = b*x + a*r1; + + pY[c] = y; + pLPF->pR1[c].f32 = y; + } +} + +static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pLPF->channels; + const ma_int32 a = pLPF->a.s32; + const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pLPF->pR1[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + + pY[c] = (ma_int16)y; + pLPF->pR1[c].s32 = (ma_int32)y; + } +} + +MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pLPF->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); + pY += pLPF->channels; + pX += pLPF->channels; + } + } else if (pLPF->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); + pY += pLPF->channels; + pX += pLPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return 1; +} + + +static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = (1 - c) / 2; + bqConfig.b1 = 1 - c; + bqConfig.b2 = (1 - c) / 2; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_lpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_lpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_lpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pLPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + ma_biquad_clear_cache(&pLPF->bq); + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pLPF->bq); +} + + +MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_lpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t lpf1Offset; + size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_lpf_heap_layout; + +static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) +{ + MA_ASSERT(pLPF1Count != NULL); + MA_ASSERT(pLPF2Count != NULL); + + *pLPF1Count = order % 2; + *pLPF2Count = order / 2; +} + +static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* LPF 1 */ + pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { + size_t lpf1HeapSizeInBytes; + ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; + } + + /* LPF 2*/ + pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; + for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { + size_t lpf2HeapSizeInBytes; + ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); + pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { + ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + if (isNew) { + size_t lpf1HeapSizeInBytes; + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); + } + } else { + result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + + for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { + ma_lpf2_config lpf2Config; + double q; + double a; + + /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ + if (lpf1Count == 1) { + a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ + } else { + a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ + } + q = 1 / (2*ma_cosd(a)); + + lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t lpf2HeapSizeInBytes; + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); + } + } else { + result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + ma_uint32 jlpf2; + + for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + pLPF->lpf1Count = lpf1Count; + pLPF->lpf2Count = lpf2Count; + pLPF->format = pConfig->format; + pLPF->channels = pConfig->channels; + pLPF->sampleRate = pConfig->sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) +{ + return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pLPF->format == ma_format_f32); + + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); + } +} + +static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pLPF->format == ma_format_s16); + + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); + } +} + +MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pLPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); + pFramesOutF32 += pLPF->channels; + pFramesInF32 += pLPF->channels; + } + } else if (pLPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); + pFramesOutS16 += pLPF->channels; + pFramesInS16 += pLPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return pLPF->lpf2Count*2 + pLPF->lpf1Count; +} + + +/************************************************************************************************************************************************************** + +High-Pass Filtering + +**************************************************************************************************************************************************************/ +MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) +{ + ma_hpf1_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + + return config; +} + +MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_hpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; +} ma_hpf1_heap_layout; + +static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + + return ma_hpf1_reinit(pConfig, pLPF); +} + +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) +{ + double a; + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + pHPF->format = pConfig->format; + pHPF->channels = pConfig->channels; + + a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); + if (pConfig->format == ma_format_f32) { + pHPF->a.f32 = (float)a; + } else { + pHPF->a.s32 = ma_biquad_float_to_fp(a); + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pHPF->channels; + const float a = 1 - pHPF->a.f32; + const float b = 1 - a; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pHPF->pR1[c].f32; + float x = pX[c]; + float y; + + y = b*x - a*r1; + + pY[c] = y; + pHPF->pR1[c].f32 = y; + } +} + +static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pHPF->channels; + const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); + const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pHPF->pR1[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + + pY[c] = (ma_int16)y; + pHPF->pR1[c].s32 = (ma_int32)y; + } +} + +MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pHPF->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); + pY += pHPF->channels; + pX += pHPF->channels; + } + } else if (pHPF->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); + pY += pHPF->channels; + pX += pHPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return 1; +} + + +static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = (1 + c) / 2; + bqConfig.b1 = -(1 + c); + bqConfig.b2 = (1 + c) / 2; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pHPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pHPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pHPF->bq); +} + + +MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_hpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t hpf1Offset; + size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_hpf_heap_layout; + +static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) +{ + MA_ASSERT(pHPF1Count != NULL); + MA_ASSERT(pHPF2Count != NULL); + + *pHPF1Count = order % 2; + *pHPF2Count = order / 2; +} + +static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* HPF 1 */ + pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { + size_t hpf1HeapSizeInBytes; + ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; + } + + /* HPF 2*/ + pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; + for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { + size_t hpf2HeapSizeInBytes; + ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pHPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); + pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { + ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + if (isNew) { + size_t hpf1HeapSizeInBytes; + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); + } + } else { + result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + + for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { + ma_hpf2_config hpf2Config; + double q; + double a; + + /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ + if (hpf1Count == 1) { + a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ + } else { + a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ + } + q = 1 / (2*ma_cosd(a)); + + hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t hpf2HeapSizeInBytes; + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); + } + } else { + result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + ma_uint32 jhpf2; + + for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + pHPF->hpf1Count = hpf1Count; + pHPF->hpf2Count = hpf2Count; + pHPF->format = pConfig->format; + pHPF->channels = pConfig->channels; + pHPF->sampleRate = pConfig->sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_hpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + if (pHPF == NULL) { + return; + } + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) +{ + return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pHPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pHPF->channels; + pFramesInF32 += pHPF->channels; + } + } else if (pHPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pHPF->channels; + pFramesInS16 += pHPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return pHPF->hpf2Count*2 + pHPF->hpf1Count; +} + + +/************************************************************************************************************************************************************** + +Band-Pass Filtering + +**************************************************************************************************************************************************************/ +MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_bpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = q * a; + bqConfig.b1 = 0; + bqConfig.b2 = -q * a; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_bpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_bpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBPF == NULL) { + return; + } + + ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pBPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_bpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pBPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) +{ + if (pBPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pBPF->bq); +} + + +MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_bpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t bpf2Offset; +} ma_bpf_heap_layout; + +static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 bpf2Count; + ma_uint32 ibpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + /* We must have an even number of order. */ + if ((pConfig->order & 0x1) != 0) { + return MA_INVALID_ARGS; + } + + bpf2Count = pConfig->channels / 2; + + pHeapLayout->sizeInBytes = 0; + + /* BPF 2 */ + pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { + size_t bpf2HeapSizeInBytes; + ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 bpf2Count; + ma_uint32 ibpf2; + ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pBPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + /* We must have an even number of order. */ + if ((pConfig->order & 0x1) != 0) { + return MA_INVALID_ARGS; + } + + bpf2Count = pConfig->order / 2; + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pBPF->bpf2Count != bpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pBPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); + } + + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { + ma_bpf2_config bpf2Config; + double q; + + /* TODO: Calculate Q to make this a proper Butterworth filter. */ + q = 0.707107; + + bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t bpf2HeapSizeInBytes; + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); + } + } else { + result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); + } + + if (result != MA_SUCCESS) { + return result; + } + } + + pBPF->bpf2Count = bpf2Count; + pBPF->format = pConfig->format; + pBPF->channels = pConfig->channels; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_bpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) +{ + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBPF); + + return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ibpf2; + + if (pBPF == NULL) { + return; + } + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); + } + + if (pBPF->_ownsHeap) { + ma_free(pBPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) +{ + return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ibpf2; + + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pBPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pBPF->channels; + pFramesInF32 += pBPF->channels; + } + } else if (pBPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pBPF->channels; + pFramesInS16 += pBPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) +{ + if (pBPF == NULL) { + return 0; + } + + return pBPF->bpf2Count*2; +} + + +/************************************************************************************************************************************************************** + +Notching Filter + +**************************************************************************************************************************************************************/ +MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) +{ + ma_notch2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.q = q; + config.frequency = frequency; + + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = 1; + bqConfig.b1 = -2 * c; + bqConfig.b2 = 1; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_notch2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_notch2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_notch2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + + +/************************************************************************************************************************************************************** + +Peaking EQ Filter + +**************************************************************************************************************************************************************/ +MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_peak2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.q = q; + config.frequency = frequency; + + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + double A; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + A = ma_powd(10, (pConfig->gainDB / 40)); + + bqConfig.b0 = 1 + (a * A); + bqConfig.b1 = -2 * c; + bqConfig.b2 = 1 - (a * A); + bqConfig.a0 = 1 + (a / A); + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - (a / A); + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_peak2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_peak2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_peak2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + +/************************************************************************************************************************************************************** + +Low Shelf Filter + +**************************************************************************************************************************************************************/ +MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) +{ + ma_loshelf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.shelfSlope = shelfSlope; + config.frequency = frequency; + + return config; +} + + +static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double w; + double s; + double c; + double A; + double S; + double a; + double sqrtA; + + MA_ASSERT(pConfig != NULL); + + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + A = ma_powd(10, (pConfig->gainDB / 40)); + S = pConfig->shelfSlope; + a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); + sqrtA = 2*ma_sqrtd(A)*a; + + bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); + bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); + bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); + bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; + bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); + bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + +/************************************************************************************************************************************************************** + +High Shelf Filter + +**************************************************************************************************************************************************************/ +MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) +{ + ma_hishelf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.shelfSlope = shelfSlope; + config.frequency = frequency; + + return config; +} + + +static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double w; + double s; + double c; + double A; + double S; + double a; + double sqrtA; + + MA_ASSERT(pConfig != NULL); + + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + A = ma_powd(10, (pConfig->gainDB / 40)); + S = pConfig->shelfSlope; + a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); + sqrtA = 2*ma_sqrtd(A)*a; + + bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); + bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); + bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); + bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; + bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); + bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + + +/* +Delay +*/ +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.sampleRate = sampleRate; + config.delayInFrames = delayInFrames; + config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ + config.wet = 1; + config.dry = 1; + config.decay = decay; + + return config; +} + + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) +{ + if (pDelay == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelay); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->decay < 0 || pConfig->decay > 1) { + return MA_INVALID_ARGS; + } + + pDelay->config = *pConfig; + pDelay->bufferSizeInFrames = pConfig->delayInFrames; + pDelay->cursor = 0; + + pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); + if (pDelay->pBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); + + return MA_SUCCESS; +} + +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelay == NULL) { + return; + } + + ma_free(pDelay->pBuffer, pAllocationCallbacks); +} + +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_uint32 iFrame; + ma_uint32 iChannel; + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { + ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; + + if (pDelay->config.delayStart) { + /* Delayed start. */ + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + } else { + /* Immediate start */ + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + } + } + + pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; + + pFramesOutF32 += pDelay->config.channels; + pFramesInF32 += pDelay->config.channels; + } + + return MA_SUCCESS; +} + +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.wet = value; +} + +MA_API float ma_delay_get_wet(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.wet; +} + +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.dry = value; +} + +MA_API float ma_delay_get_dry(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.dry; +} + +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.decay = value; +} + +MA_API float ma_delay_get_decay(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.decay; +} + + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) +{ + ma_gainer_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.smoothTimeInFrames = smoothTimeInFrames; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t oldGainsOffset; + size_t newGainsOffset; +} ma_gainer_heap_layout; + +static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Old gains. */ + pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* New gains. */ + pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* Alignment. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGainer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pGainer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); + pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); + pGainer->masterVolume = 1; + + pGainer->config = *pConfig; + pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pGainer->pOldGains[iChannel] = 1; + pGainer->pNewGains[iChannel] = 1; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pGainer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pGainer == NULL) { + return; + } + + if (pGainer->_ownsHeap) { + ma_free(pGainer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) +{ + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); +} + +static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + ma_uint64 interpolatedFrameCount; + + MA_ASSERT(pGainer != NULL); + + /* + We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When + linear interpolation is not needed we can do a simple volume adjustment which will be more + efficient than a lerp with an alpha value of 1. + + To do this, all we need to do is determine how many frames need to have a lerp applied. Then we + just process that number of frames with linear interpolation. After that we run on an optimized + path which just applies the new gains without a lerp. + */ + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + interpolatedFrameCount = 0; + } else { + interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; + if (interpolatedFrameCount > frameCount) { + interpolatedFrameCount = frameCount; + } + } + + /* + Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers + so that the fast path can work naturally without consideration of the interpolated path. + */ + if (interpolatedFrameCount > 0) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + /* + All we're really doing here is moving the old gains towards the new gains. We don't want to + be modifying the gains inside the ma_gainer object because that will break things. Instead + we can make a copy here on the stack. For extreme channel counts we can fall back to a slower + implementation which just uses a standard lerp. + */ + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + + if (pGainer->config.channels <= 32) { + float pRunningGain[32]; + float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ + + /* Initialize the running gain. */ + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; + pRunningGainDelta[iChannel] = t * d; + pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); + } + + iFrame = 0; + + /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ + if (pGainer->config.channels == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + /* + Two different scalar implementations here. Clang (and I assume GCC) will vectorize + both of these, but the bottom version results in a nicer vectorization with less + instructions emitted. The problem, however, is that the bottom version runs slower + when compiled with MSVC. The top version will be partially vectorized by MSVC. + */ + #if defined(_MSC_VER) && !defined(__clang__) + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ + pRunningGainDelta[2] = pRunningGainDelta[0]; + pRunningGainDelta[3] = pRunningGainDelta[1]; + pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; + pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; + pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; + pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; + pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; + + /* Move the running gain forward towards the new gain. */ + pRunningGain[0] += pRunningGainDelta[0]; + pRunningGain[1] += pRunningGainDelta[1]; + pRunningGain[2] += pRunningGainDelta[2]; + pRunningGain[3] += pRunningGainDelta[3]; + } + + iFrame = unrolledLoopCount << 1; + #else + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; + } + + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + #endif + } + } else if (pGainer->config.channels == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* + For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames + at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays + so we can do clean 4x SIMD operations. + */ + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); + __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); + + __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); + __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); + __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } else if (pGainer->config.channels == 8) { + /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); + __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); + __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + } + } else + #endif + { + /* This is crafted so that it auto-vectorizes when compiled with Clang. */ + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } else { + /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ + for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; + } + + a += d; + } + } + } + + /* Make sure the timer is updated. */ + pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); + + /* Adjust our arguments so the next part can work normally. */ + frameCount -= interpolatedFrameCount; + pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); + pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); + } + + /* All we need to do here is apply the new gains using an optimized path. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + if (pGainer->config.channels <= 32) { + float gains[32]; + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + + ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); + } else { + /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + } + } + } + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); + } + +#if 0 + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + /* Fast path. No gain calculation required. */ + ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); + ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; + } + } else { + /* Slow path. Need to interpolate the gain for each channel individually. */ + + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + ma_uint32 channelCount = pGainer->config.channels; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channelCount; iChannel += 1) { + pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; + } + + pFramesOutF32 += channelCount; + pFramesInF32 += channelCount; + + a += d; + if (a > 1) { + a = 1; + } + } + } + + pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); + + #if 0 /* Reference implementation. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; + } + } + + /* Move interpolation time forward, but don't go beyond our smoothing time. */ + pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); + } + #endif + } +#endif + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + /* + ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which + helps with auto-vectorization. + */ + return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); +} + +static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) +{ + pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); + pGainer->pNewGains[iChannel] = newGain; +} + +static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) +{ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ + } else { + pGainer->t = 0; + } +} + +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) +{ + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) +{ + ma_uint32 iChannel; + + if (pGainer == NULL || pNewGains == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + pGainer->masterVolume = volume; + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) +{ + if (pGainer == NULL || pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = pGainer->masterVolume; + + return MA_SUCCESS; +} + + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) +{ + ma_panner_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ + config.pan = 0; + + return config; +} + + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) +{ + if (pPanner == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPanner); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pPanner->format = pConfig->format; + pPanner->channels = pConfig->channels; + pPanner->mode = pConfig->mode; + pPanner->pan = pConfig->pan; + + return MA_SUCCESS; +} + +static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factor = 1.0f - pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; + } + } + } else { + float factor = 1.0f + pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } + } +} + +static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + + +static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factorL0 = 1.0f - pan; + float factorL1 = 0.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); + float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } else { + float factorR0 = 0.0f - pan; + float factorR1 = 1.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); + float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } +} + +static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + if (pPanner->channels == 2) { + /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ + if (pPanner->mode == ma_pan_mode_balance) { + ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } else { + ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } + } else { + if (pPanner->channels == 1) { + /* Panning has no effect on mono streams. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } else { + /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) +{ + if (pPanner == NULL) { + return; + } + + pPanner->mode = mode; +} + +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return ma_pan_mode_balance; + } + + return pPanner->mode; +} + +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) +{ + if (pPanner == NULL) { + return; + } + + pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); +} + +MA_API float ma_panner_get_pan(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return 0; + } + + return pPanner->pan; +} + + + + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_fader_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFader); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only f32 is supported for now. */ + if (pConfig->format != ma_format_f32) { + return MA_INVALID_ARGS; + } + + pFader->config = *pConfig; + pFader->volumeBeg = 1; + pFader->volumeEnd = 1; + pFader->lengthInFrames = 0; + pFader->cursorInFrames = 0; + + return MA_SUCCESS; +} + +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ + if (pFader->cursorInFrames < 0) { + ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; + if (absCursorInFrames > frameCount) { + absCursorInFrames = frameCount; + } + + ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); + + pFader->cursorInFrames += absCursorInFrames; + frameCount -= absCursorInFrames; + pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + } + + if (pFader->cursorInFrames >= 0) { + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; + } + + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); + } else { + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); + } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } else { + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; + + /* For now we only support f32. Support for other formats might be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; + } + } + } else { + return MA_NOT_IMPLEMENTED; + } + } + } + } + + pFader->cursorInFrames += frameCount; + + return MA_SUCCESS; +} + +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +{ + if (pFader == NULL) { + return; + } + + if (pFormat != NULL) { + *pFormat = pFader->config.format; + } + + if (pChannels != NULL) { + *pChannels = pFader->config.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pFader->config.sampleRate; + } +} + +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) +{ + ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); +} + +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) +{ + if (pFader == NULL) { + return; + } + + /* If the volume is negative, use current volume. */ + if (volumeBeg < 0) { + volumeBeg = ma_fader_get_current_volume(pFader); + } + + /* + The length needs to be clamped to 32-bits due to how we convert it to a float for linear + interpolation reasons. I might change this requirement later, but for now it's not important. + */ + if (lengthInFrames > UINT_MAX) { + lengthInFrames = UINT_MAX; + } + + /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ + if (startOffsetInFrames > INT_MAX) { + startOffsetInFrames = INT_MAX; + } + + pFader->volumeBeg = volumeBeg; + pFader->volumeEnd = volumeEnd; + pFader->lengthInFrames = lengthInFrames; + pFader->cursorInFrames = -startOffsetInFrames; +} + +MA_API float ma_fader_get_current_volume(const ma_fader* pFader) +{ + if (pFader == NULL) { + return 0.0f; + } + + /* Any frames prior to the start of the fade period will be at unfaded volume. */ + if (pFader->cursorInFrames < 0) { + return 1.0f; + } + + /* The current volume depends on the position of the cursor. */ + if (pFader->cursorInFrames == 0) { + return pFader->volumeBeg; + } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ + return pFader->volumeEnd; + } else { + /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ + return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ + } +} + + + + + +MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) +{ + ma_vec3f v; + + v.x = x; + v.y = y; + v.z = z; + + return v; +} + +MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.x - b.x, + a.y - b.y, + a.z - b.z + ); +} + +MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) +{ + return ma_vec3f_init_3f( + -a.x, + -a.y, + -a.z + ); +} + +MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +MA_API float ma_vec3f_len2(ma_vec3f v) +{ + return ma_vec3f_dot(v, v); +} + +MA_API float ma_vec3f_len(ma_vec3f v) +{ + return (float)ma_sqrtd(ma_vec3f_len2(v)); +} + + + +MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_len(ma_vec3f_sub(a, b)); +} + +MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) +{ + float invLen; + float len2 = ma_vec3f_len2(v); + if (len2 == 0) { + return ma_vec3f_init_3f(0, 0, 0); + } + + invLen = ma_rsqrtf(len2); + v.x *= invLen; + v.y *= invLen; + v.z *= invLen; + + return v; +} + +MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x + ); +} + + +MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) +{ + v->v = value; + v->lock = 0; /* Important this is initialized to 0. */ +} + +MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) +{ + ma_spinlock_lock(&v->lock); + { + v->v = value; + } + ma_spinlock_unlock(&v->lock); +} + +MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) +{ + ma_vec3f r; + + ma_spinlock_lock(&v->lock); + { + r = v->v; + } + ma_spinlock_unlock(&v->lock); + + return r; +} + + + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); +static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); + + +#ifndef MA_DEFAULT_SPEED_OF_SOUND +#define MA_DEFAULT_SPEED_OF_SOUND 343.3f +#endif + +/* +These vectors represent the direction that speakers are facing from the center point. They're used +for panning in the spatializer. Must be normalized. +*/ +static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ + {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ + {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ + {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ + {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ + {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ + {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ + { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ + {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ + {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ + { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ + {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ + { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ + {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ + {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ + { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ + {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ + { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ +}; + +static ma_vec3f ma_get_channel_direction(ma_channel channel) +{ + if (channel >= MA_CHANNEL_POSITION_COUNT) { + return ma_vec3f_init_3f(0, 0, -1); + } else { + return g_maChannelDirections[channel]; + } +} + + + +static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); +} + +static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); +} + +static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); +} + + +/* +Dopper Effect calculation taken from the OpenAL spec, with two main differences: + + 1) The source to listener vector will have already been calcualted at an earlier step so we can + just use that directly. We need only the position of the source relative to the origin. + + 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight + into the resampler directly. +*/ +static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) +{ + float len; + float vls; + float vss; + + len = ma_vec3f_len(relativePosition); + + /* + There's a case where the position of the source will be right on top of the listener in which + case the length will be 0 and we'll end up with a division by zero. We can just return a ratio + of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. + */ + if (len == 0) { + return 1.0; + } + + vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; + vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; + + vls = ma_min(vls, speedOfSound / dopplerFactor); + vss = ma_min(vss, speedOfSound / dopplerFactor); + + return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); +} + + +static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) +{ + /* + Special case for stereo. Want to default the left and right speakers to side left and side + right so that they're facing directly down the X axis rather than slightly forward. Not + doing this will result in sounds being quieter when behind the listener. This might + actually be good for some scenerios, but I don't think it's an appropriate default because + it can be a bit unexpected. + */ + if (channelCount == 2) { + pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; + pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } +} + + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) +{ + ma_spatializer_listener_config config; + + MA_ZERO_OBJECT(&config); + config.channelsOut = channelsOut; + config.pChannelMapOut = NULL; + config.handedness = ma_handedness_right; + config.worldUp = ma_vec3f_init_3f(0, 1, 0); + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterGain = 0; + config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapOutOffset; +} ma_spatializer_listener_heap_layout; + +static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. We always need this, even for passthroughs. */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pListener == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pListener); + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pListener->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pListener->config = *pConfig; + ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); + pListener->isEnabled = MA_TRUE; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pListener->config.handedness == ma_handedness_left) { + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); + ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); + } + + + /* We must always have a valid channel map. */ + pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + + /* Use a slightly different default channel map for stereo. */ + if (pConfig->pChannelMapOut == NULL) { + ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); + } else { + ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pListener->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pListener == NULL) { + return; + } + + if (pListener->_ownsHeap) { + ma_free(pListener->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return NULL; + } + + return pListener->config.pChannelMapOut; +} + +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pListener == NULL) { + return; + } + + pListener->config.coneInnerAngleInRadians = innerAngleInRadians; + pListener->config.coneOuterAngleInRadians = outerAngleInRadians; + pListener->config.coneOuterGain = outerGain; +} + +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pListener == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; + } + + if (pOuterGain != NULL) { + *pOuterGain = pListener->config.coneOuterGain; + } +} + +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) +{ + if (pListener == NULL) { + return; + } + + pListener->config.speedOfSound = speedOfSound; +} + +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return 0; + } + + return pListener->config.speedOfSound; +} + +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return pListener->config.worldUp; +} + +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) +{ + if (pListener == NULL) { + return; + } + + pListener->isEnabled = isEnabled; +} + +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return MA_FALSE; + } + + return pListener->isEnabled; +} + + + + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) +{ + ma_spatializer_config config; + + MA_ZERO_OBJECT(&config); + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = NULL; + config.attenuationModel = ma_attenuation_model_inverse; + config.positioning = ma_positioning_absolute; + config.handedness = ma_handedness_right; + config.minGain = 0; + config.maxGain = 1; + config.minDistance = 1; + config.maxDistance = MA_FLT_MAX; + config.rolloff = 1; + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ + config.coneOuterGain = 0.0f; + config.dopplerFactor = 1; + config.directionalAttenuationFactor = 1; + config.minSpatializationChannelGain = 0.2f; + config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ + + return config; +} + + +static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); +} + +static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t newChannelGainsOffset; + size_t gainerOffset; +} ma_spatializer_heap_layout; + +static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_validate_config(pConfig); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. */ + pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); + } + + /* New channel gains for output. */ + pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); + + /* Gainer. */ + { + size_t gainerHeapSizeInBytes; + ma_gainer_config gainerConfig; + + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; /* Safety. */ + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + ma_gainer_config gainerConfig; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSpatializer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pSpatializer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pSpatializer->channelsIn = pConfig->channelsIn; + pSpatializer->channelsOut = pConfig->channelsOut; + pSpatializer->attenuationModel = pConfig->attenuationModel; + pSpatializer->positioning = pConfig->positioning; + pSpatializer->handedness = pConfig->handedness; + pSpatializer->minGain = pConfig->minGain; + pSpatializer->maxGain = pConfig->maxGain; + pSpatializer->minDistance = pConfig->minDistance; + pSpatializer->maxDistance = pConfig->maxDistance; + pSpatializer->rolloff = pConfig->rolloff; + pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; + pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; + pSpatializer->coneOuterGain = pConfig->coneOuterGain; + pSpatializer->dopplerFactor = pConfig->dopplerFactor; + pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; + pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; + pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; + ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); + pSpatializer->dopplerPitch = 1; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pSpatializer->handedness == ma_handedness_left) { + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); + ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); + } + + /* Channel map. This will be on the heap. */ + if (pConfig->pChannelMapIn != NULL) { + pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); + } + + /* New channel gains for output channels. */ + pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); + + /* Gainer. */ + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the gainer. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + /* We'll need a heap allocation to retrieve the size. */ + result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pSpatializer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pSpatializer == NULL) { + return; + } + + ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); + + if (pSpatializer->_ownsHeap) { + ma_free(pSpatializer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) +{ + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (coneInnerAngleInRadians < 6.283185f) { + float angularGain = 1; + float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); + float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); + float d; + + d = ma_vec3f_dot(dirA, dirB); + + if (d > cutoffInner) { + /* It's inside the inner angle. */ + angularGain = 1; + } else { + /* It's outside the inner angle. */ + if (d > cutoffOuter) { + /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ + angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); + } else { + /* It's outside the outer angle. */ + angularGain = coneOuterGain; + } + } + + /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ + return angularGain; + } else { + /* Inner angle is 360 degrees so no need to do any attenuation. */ + return 1; + } +} + +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; + ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're not spatializing we need to run an optimized path. */ + if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (ma_spatializer_listener_is_enabled(pListener)) { + /* No attenuation is required, but we'll need to do some channel conversion. */ + if (pSpatializer->channelsIn == pSpatializer->channelsOut) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); + } else { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ + } + } else { + /* The listener is disabled. Output silence. */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* + We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is + the correct thinking so might need to review this later. + */ + pSpatializer->dopplerPitch = 1; + } else { + /* + Let's first determine which listener the sound is closest to. Need to keep in mind that we + might not have a world or any listeners, in which case we just spatializer based on the + listener being positioned at the origin (0, 0, 0). + */ + ma_vec3f relativePosNormalized; + ma_vec3f relativePos; /* The position relative to the listener. */ + ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ + ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ + float speedOfSound; + float distance = 0; + float gain = 1; + ma_uint32 iChannel; + const ma_uint32 channelsOut = pSpatializer->channelsOut; + const ma_uint32 channelsIn = pSpatializer->channelsIn; + float minDistance = ma_spatializer_get_min_distance(pSpatializer); + float maxDistance = ma_spatializer_get_max_distance(pSpatializer); + float rolloff = ma_spatializer_get_rolloff(pSpatializer); + float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); + + /* + We'll need the listener velocity for doppler pitch calculations. The speed of sound is + defined by the listener, so we'll grab that here too. + */ + if (pListener != NULL) { + listenerVel = ma_spatializer_listener_get_velocity(pListener); + speedOfSound = pListener->config.speedOfSound; + } else { + listenerVel = ma_vec3f_init_3f(0, 0, 0); + speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + relativePos = ma_spatializer_get_position(pSpatializer); + relativeDir = ma_spatializer_get_direction(pSpatializer); + } else { + /* + We've found a listener and we're using absolute positioning. We need to transform the + sound's position and direction so that it's relative to listener. Later on we'll use + this for determining the factors to apply to each channel to apply the panning effect. + */ + ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); + } + + distance = ma_vec3f_len(relativePos); + + /* We've gathered the data, so now we can apply some spatialization. */ + switch (ma_spatializer_get_attenuation_model(pSpatializer)) { + case ma_attenuation_model_inverse: + { + gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_linear: + { + gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_exponential: + { + gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_none: + default: + { + gain = 1; + } break; + } + + /* Normalize the position. */ + if (distance > 0.001f) { + float distanceInv = 1/distance; + relativePosNormalized = relativePos; + relativePosNormalized.x *= distanceInv; + relativePosNormalized.y *= distanceInv; + relativePosNormalized.z *= distanceInv; + } else { + distance = 0; + relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); + } + + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (distance > 0) { + /* Source anglular gain. */ + float spatializerConeInnerAngle; + float spatializerConeOuterAngle; + float spatializerConeOuterGain; + ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); + + gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); + + /* + We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that + are positioned behind the listener. On default settings, this will have no effect. + */ + if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { + ma_vec3f listenerDirection; + float listenerInnerAngle; + float listenerOuterAngle; + float listenerOuterGain; + + if (pListener->config.handedness == ma_handedness_right) { + listenerDirection = ma_vec3f_init_3f(0, 0, -1); + } else { + listenerDirection = ma_vec3f_init_3f(0, 0, +1); + } + + listenerInnerAngle = pListener->config.coneInnerAngleInRadians; + listenerOuterAngle = pListener->config.coneOuterAngleInRadians; + listenerOuterGain = pListener->config.coneOuterGain; + + gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); + } + } else { + /* The sound is right on top of the listener. Don't do any angular attenuation. */ + } + + + /* Clamp the gain. */ + gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + + /* + Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for + when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the + gain to the final output. + */ + /*printf("distance=%f; gain=%f\n", distance, gain);*/ + + /* We must have a valid channel map here to ensure we spatialize properly. */ + MA_ASSERT(pChannelMapOut != NULL); + + /* + We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being + to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that + the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and + seeing how it goes. There might be better ways to do this. + + To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a + direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will + be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized + position of the sound. + */ + + /* + Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's + relation to the direction of the channel. + */ + if (distance > 0) { + ma_vec3f unitPos = relativePos; + float distanceInv = 1/distance; + unitPos.x *= distanceInv; + unitPos.y *= distanceInv; + unitPos.z *= distanceInv; + + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + ma_channel channelOut; + float d; + float dMin; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); + if (ma_is_spatial_channel_position(channelOut)) { + d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); + } else { + d = 1; /* It's not a spatial channel so there's no real notion of direction. */ + } + + /* + In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. + The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to + 0, panning will be most extreme and any sounds that are positioned on the opposite side of the + speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it + doesn't even remotely represent the real world at all because sounds that come from your right side + are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at + all, which is also not ideal. By setting it to something greater than 0, the spatialization effect + becomes much less dramatic and a lot more bearable. + + Summary: 0 = more extreme panning; 1 = no panning. + */ + dMin = pSpatializer->minSpatializationChannelGain; + + /* + At this point, "d" will be positive if the sound is on the same side as the channel and negative if + it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to + calculate a panning value. The first is to simply convert it to 0..1, however this has a problem + which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right + in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like + the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front + of the listener. I would intuitively expect that to be played at full volume, or close to it. + + The second idea I think of is to only apply a reduction in gain when the sound is on the opposite + side of the speaker. That is, reduce the gain only when the dot product is negative. The problem + with this is that there will not be any attenuation as the sound sweeps around the 180 degrees + where the dot product is positive. The idea with this option is that you leave the gain at 1 when + the sound is being played on the same side as the speaker and then you just reduce the volume when + the sound is on the other side. + + The summarize, I think the first option should give a better sense of spatialization, but the second + option is better for preserving the sound's power. + + UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a + bit better, but you can also hear the reduction in volume when it's right in front. + */ + #if 1 + { + /* + Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power + by being played at 0.5 gain. + */ + d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ + d = ma_max(d, dMin); + pSpatializer->pNewChannelGainsOut[iChannel] *= d; + } + #else + { + /* + Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more + consistent, but comes at the expense of a worse sense of space and positioning. + */ + if (d < 0) { + d += 1; /* Move into the positive range. */ + d = ma_max(d, dMin); + channelGainsOut[iChannel] *= d; + } + } + #endif + } + } else { + /* Assume the sound is right on top of us. Don't do any panning. */ + } + + /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ + ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); + ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); + + /* + Before leaving we'll want to update our doppler pitch so that the caller can apply some + pitch shifting if they desire. Note that we need to negate the relative position here + because the doppler calculation needs to be source-to-listener, but ours is listener-to- + source. + */ + if (dopplerFactor > 0) { + pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); + } else { + pSpatializer->dopplerPitch = 1; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); +} + +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); +} + +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsIn; +} + +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsOut; +} + +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); +} + +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_attenuation_model_none; + } + + return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); +} + +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); +} + +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_positioning_absolute; + } + + return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); +} + +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); +} + +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->rolloff); +} + +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); +} + +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->minGain); +} + +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); +} + +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->maxGain); +} + +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); +} + +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->minDistance); +} + +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); +} + +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->maxDistance); +} + +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); +} + +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pSpatializer == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + } + + if (pOuterGain != NULL) { + *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); + } +} + +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); +} + +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return ma_atomic_load_f32(&pSpatializer->dopplerFactor); +} + +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); +} + +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); +} + +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) +{ + if (pRelativePos != NULL) { + pRelativePos->x = 0; + pRelativePos->y = 0; + pRelativePos->z = 0; + } + + if (pRelativeDir != NULL) { + pRelativeDir->x = 0; + pRelativeDir->y = 0; + pRelativeDir->z = -1; + } + + if (pSpatializer == NULL) { + return; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + if (pRelativePos != NULL) { + *pRelativePos = ma_spatializer_get_position(pSpatializer); + } + if (pRelativeDir != NULL) { + *pRelativeDir = ma_spatializer_get_direction(pSpatializer); + } + } else { + ma_vec3f spatializerPosition; + ma_vec3f spatializerDirection; + ma_vec3f listenerPosition; + ma_vec3f listenerDirection; + ma_vec3f v; + ma_vec3f axisX; + ma_vec3f axisY; + ma_vec3f axisZ; + float m[4][4]; + + spatializerPosition = ma_spatializer_get_position(pSpatializer); + spatializerDirection = ma_spatializer_get_direction(pSpatializer); + listenerPosition = ma_spatializer_listener_get_position(pListener); + listenerDirection = ma_spatializer_listener_get_direction(pListener); + + /* + We need to calcualte the right vector from our forward and up vectors. This is done with + a cross product. + */ + axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ + axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ + + /* + The calculation of axisX above can result in a zero-length vector if the listener is + looking straight up on the Y axis. We'll need to fall back to a +X in this case so that + the calculations below don't fall apart. This is where a quaternion based listener and + sound orientation would come in handy. + */ + if (ma_vec3f_len2(axisX) == 0) { + axisX = ma_vec3f_init_3f(1, 0, 0); + } + + axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ + + /* + We need to swap the X axis if we're left handed because otherwise the cross product above + will have resulted in it pointing in the wrong direction (right handed was assumed in the + cross products above). + */ + if (pListener->config.handedness == ma_handedness_left) { + axisX = ma_vec3f_neg(axisX); + } + + /* Lookat. */ + m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); + m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); + m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); + m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; + + /* + Multiply the lookat matrix by the spatializer position to transform it to listener + space. This allows calculations to work based on the sound being relative to the + origin which makes things simpler. + */ + if (pRelativePos != NULL) { + v = spatializerPosition; + pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; + pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; + pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; + } + + /* + The direction of the sound needs to also be transformed so that it's relative to the + rotation of the listener. + */ + if (pRelativeDir != NULL) { + v = spatializerDirection; + pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; + pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; + pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; + } + } +} + + + + +/************************************************************************************************************************************************************** + +Resampling + +**************************************************************************************************************************************************************/ +MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_linear_resampler_config config; + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + config.lpfNyquistFactor = 1; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t x0Offset; + size_t x1Offset; + size_t lpfOffset; +} ma_linear_resampler_heap_layout; + + +static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) +{ + /* + So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will + be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. + */ + ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ + ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; + + pResampler->inTimeFrac = + (oldRateTimeWhole * newSampleRateOut) + + ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); + + /* Make sure the fractional part is less than the output sample rate. */ + pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; + pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; +} + +static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) +{ + ma_result result; + ma_uint32 gcf; + ma_uint32 lpfSampleRate; + double lpfCutoffFrequency; + ma_lpf_config lpfConfig; + ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MA_INVALID_ARGS; + } + + oldSampleRateOut = pResampler->config.sampleRateOut; + + pResampler->config.sampleRateIn = sampleRateIn; + pResampler->config.sampleRateOut = sampleRateOut; + + /* Simplify the sample rate. */ + gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); + pResampler->config.sampleRateIn /= gcf; + pResampler->config.sampleRateOut /= gcf; + + /* Always initialize the low-pass filter, even when the order is 0. */ + if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); + lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); + + lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); + + /* + If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames + getting cleared. Instead we re-initialize the filter which will maintain any cached frames. + */ + if (isResamplerAlreadyInitialized) { + result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); + } else { + result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); + } + + if (result != MA_SUCCESS) { + return result; + } + + + pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; + pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; + + /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ + ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* x0 */ + pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* x1 */ + pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* LPF */ + pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); + { + ma_result result; + size_t lpfHeapSizeInBytes; + ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ + + result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_linear_resampler_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) +{ + ma_result result; + ma_linear_resampler_heap_layout heapLayout; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResampler); + + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->config = *pConfig; + + pResampler->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + if (pConfig->format == ma_format_f32) { + pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } else { + pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } + + /* Setting the rate will set up the filter and time advances for us. */ + result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ + pResampler->inTimeFrac = 0; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pResampler == NULL) { + return; + } + + ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); + } +} + +static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) +{ + ma_int32 b; + ma_int32 c; + ma_int32 r; + + MA_ASSERT(a <= (1<> shift); +} + +static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) +{ + ma_uint32 c; + ma_uint32 a; + const ma_uint32 channels = pResampler->config.channels; + const ma_uint32 shift = 12; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameOut != NULL); + + a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); + pFrameOut[c] = s; + } +} + + +static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) +{ + ma_uint32 c; + float a; + const ma_uint32 channels = pResampler->config.channels; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameOut != NULL); + + a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); + pFrameOut[c] = s; + } +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const ma_int16* pFramesInS16; + /* */ ma_int16* pFramesOutS16; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInS16 = (const ma_int16*)pFramesIn; + pFramesOutS16 = ( ma_int16*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInS16 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; + } + pFramesInS16 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = 0; + } + } + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ + if (pFramesOutS16 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); + + pFramesOutS16 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const ma_int16* pFramesInS16; + /* */ ma_int16* pFramesOutS16; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInS16 = (const ma_int16*)pFramesIn; + pFramesOutS16 = ( ma_int16*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInS16 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; + } + pFramesInS16 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = 0; + } + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and we can generate the next output frame. */ + if (pFramesOutS16 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { + return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + + +static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const float* pFramesInF32; + /* */ float* pFramesOutF32; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInF32 = (const float*)pFramesIn; + pFramesOutF32 = ( float*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInF32 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; + } + pFramesInF32 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = 0; + } + } + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ + if (pFramesOutF32 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); + + pFramesOutF32 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const float* pFramesInF32; + /* */ float* pFramesOutF32; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInF32 = (const float*)pFramesIn; + pFramesOutF32 = ( float*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInF32 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; + } + pFramesInF32 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = 0; + } + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and we can generate the next output frame. */ + if (pFramesOutF32 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { + return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + + +MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* */ if (pResampler->config.format == ma_format_s16) { + return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else if (pResampler->config.format == ma_format_f32) { + return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; + } +} + + +MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); +} + +MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) +{ + ma_uint32 n; + ma_uint32 d; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (ratioInOut <= 0) { + return MA_INVALID_ARGS; + } + + d = 1000000; + n = (ma_uint32)(ratioInOut * d); + + if (n == 0) { + return MA_INVALID_ARGS; /* Ratio too small. */ + } + + MA_ASSERT(n != 0); + + return ma_linear_resampler_set_rate(pResampler, n, d); +} + +MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + return 1 + ma_lpf_get_latency(&pResampler->lpf); +} + +MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; +} + +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (outputFrameCount == 0) { + return MA_SUCCESS; + } + + /* Any whole input frames are consumed before the first output frame is generated. */ + inputFrameCount = pResampler->inTimeInt; + outputFrameCount -= 1; + + /* The rest of the output frames can be calculated in constant time. */ + inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; + inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; + + *pInputFrameCount = inputFrameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + ma_uint64 outputFrameCount; + ma_uint64 preliminaryInputFrameCountFromFrac; + ma_uint64 preliminaryInputFrameCount; + + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* + The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to + determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't + be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation + of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. + */ + outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; + + /* + We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is + used in the logic below to determine whether or not we need to add an extra output frame. + */ + preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; + preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; + + /* + If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than + the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data + to actually process. Otherwise we need to add the extra output frame. + */ + if (preliminaryInputFrameCount <= inputFrameCount) { + outputFrameCount += 1; + } + + *pOutputFrameCount = outputFrameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) +{ + ma_uint32 iChannel; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* Timers need to be cleared back to zero. */ + pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ + pResampler->inTimeFrac = 0; + + /* Cached samples need to be cleared. */ + if (pResampler->config.format == ma_format_f32) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = 0; + pResampler->x1.f32[iChannel] = 0; + } + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = 0; + pResampler->x1.s16[iChannel] = 0; + } + } + + /* The low pass filter needs to have it's cache reset. */ + ma_lpf_clear_cache(&pResampler->lpf); + + return MA_SUCCESS; +} + + + +/* Linear resampler backend vtable. */ +static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) +{ + ma_linear_resampler_config linearConfig; + + linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); + linearConfig.lpfOrder = pConfig->linear.lpfOrder; + + return linearConfig; +} + +static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); +} + +static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) +{ + ma_resampler* pResampler = (ma_resampler*)pUserData; + ma_result result; + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); + if (result != MA_SUCCESS) { + return result; + } + + *ppBackend = &pResampler->state.linear; + + return MA_SUCCESS; +} + +static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + (void)pUserData; + + ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); +} + +static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + (void)pUserData; + + return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); +} + +static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + (void)pUserData; + + return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); +} + +static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); +} + +static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); +} + +static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); +} + +static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); +} + +static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); +} + +static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = +{ + ma_resampling_backend_get_heap_size__linear, + ma_resampling_backend_init__linear, + ma_resampling_backend_uninit__linear, + ma_resampling_backend_process__linear, + ma_resampling_backend_set_rate__linear, + ma_resampling_backend_get_input_latency__linear, + ma_resampling_backend_get_output_latency__linear, + ma_resampling_backend_get_required_input_frame_count__linear, + ma_resampling_backend_get_expected_output_frame_count__linear, + ma_resampling_backend_reset__linear +}; + + + +MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) +{ + ma_resampler_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + config.algorithm = algorithm; + + /* Linear. */ + config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + + return config; +} + +static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) +{ + MA_ASSERT(pConfig != NULL); + MA_ASSERT(ppVTable != NULL); + MA_ASSERT(ppUserData != NULL); + + /* Safety. */ + *ppVTable = NULL; + *ppUserData = NULL; + + switch (pConfig->algorithm) + { + case ma_resample_algorithm_linear: + { + *ppVTable = &g_ma_linear_resampler_vtable; + *ppUserData = pResampler; + } break; + + case ma_resample_algorithm_custom: + { + *ppVTable = pConfig->pBackendVTable; + *ppUserData = pConfig->pBackendUserData; + } break; + + default: return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_resampling_backend_vtable* pVTable; + void* pVTableUserData; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); + if (result != MA_SUCCESS) { + return result; + } + + if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) +{ + ma_result result; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResampler); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pResampler->_pHeap = pHeap; + pResampler->format = pConfig->format; + pResampler->channels = pConfig->channels; + pResampler->sampleRateIn = pConfig->sampleRateIn; + pResampler->sampleRateOut = pConfig->sampleRateOut; + + result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); + if (result != MA_SUCCESS) { + return result; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { + return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ + } + + result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pResampler == NULL) { + return; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { + return; + } + + pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pFrameCountOut == NULL && pFrameCountIn == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); +} + +MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_result result; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->sampleRateIn = sampleRateIn; + pResampler->sampleRateOut = sampleRateOut; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) +{ + ma_uint32 n; + ma_uint32 d; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (ratio <= 0) { + return MA_INVALID_ARGS; + } + + d = 1000; + n = (ma_uint32)(ratio * d); + + if (n == 0) { + return MA_INVALID_ARGS; /* Ratio too small. */ + } + + MA_ASSERT(n != 0); + + return ma_resampler_set_rate(pResampler, n, d); +} + +MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { + return 0; + } + + return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); +} + +MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { + return 0; + } + + return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); +} + +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); +} + +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); +} + +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); +} + +/************************************************************************************************************************************************************** + +Channel Conversion + +**************************************************************************************************************************************************************/ +#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT +#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 +#endif + +#define MA_PLANE_LEFT 0 +#define MA_PLANE_RIGHT 1 +#define MA_PLANE_FRONT 2 +#define MA_PLANE_BACK 3 +#define MA_PLANE_BOTTOM 4 +#define MA_PLANE_TOP 5 + +static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ + { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ + { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ + { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ + { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ + { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ + { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ + { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ + { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ + { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ + { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ + { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ + { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ + { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ + { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ + { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ + { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ +}; + +static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) +{ + /* + Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to + the following output configuration: + + - front/left + - side/left + - back/left + + The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount + of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. + + Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left + speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted + from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would + receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between + the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works + across 3 spatial dimensions. + + The first thing to do is figure out how each speaker's volume is spread over each of plane: + - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane + - side/left: 1 plane (left only) = 1/1 = entire volume from left plane + - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane + - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane + + The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other + channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be + taken by the other to produce the final contribution. + */ + + /* Contribution = Sum(Volume to Give * Volume to Take) */ + float contribution = + g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + + g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + + g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + + g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + + g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + + g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; + + return contribution; +} + +MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) +{ + ma_channel_converter_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = pChannelMapIn; + config.pChannelMapOut = pChannelMapOut; + config.mixingMode = mixingMode; + + return config; +} + +static ma_int32 ma_channel_converter_float_to_fixed(float x) +{ + return (ma_int32)(x * (1< 0); + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { + spatialChannelCount++; + } + } + + return spatialChannelCount; +} + +static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) +{ + int i; + + if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { + return MA_FALSE; + } + + if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { + return MA_FALSE; + } + + for (i = 0; i < 6; ++i) { /* Each side of a cube. */ + if (g_maChannelPlaneRatios[channelPosition][i] != 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) +{ + if (channelsOut == channelsIn) { + return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); + } else { + return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ + } +} + +static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) +{ + if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { + return ma_channel_conversion_path_passthrough; + } + + if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_out; + } + + if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_in; + } + + if (mode == ma_channel_mix_mode_custom_weights) { + return ma_channel_conversion_path_weights; + } + + /* + We can use a simple shuffle if both channel maps have the same channel count and all channel + positions are present in both. + */ + if (channelsIn == channelsOut) { + ma_uint32 iChannelIn; + ma_bool32 areAllChannelPositionsPresent = MA_TRUE; + for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { + ma_bool32 isInputChannelPositionInOutput = MA_FALSE; + if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { + isInputChannelPositionInOutput = MA_TRUE; + break; + } + + if (!isInputChannelPositionInOutput) { + areAllChannelPositionsPresent = MA_FALSE; + break; + } + } + + if (areAllChannelPositionsPresent) { + return ma_channel_conversion_path_shuffle; + } + } + + /* Getting here means we'll need to use weights. */ + return ma_channel_conversion_path_weights; +} + + +static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) +{ + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { + return MA_INVALID_ARGS; + } + + /* + When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the + input channel has more than one occurance of a channel position, the second one will be ignored. + */ + for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { + ma_channel channelOut; + + /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ + pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { + ma_channel channelIn; + + channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); + if (channelOut == channelIn) { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + break; + } + + /* + Getting here means the channels don't exactly match, but we are going to support some + relaxed matching for practicality. If, for example, there are two stereo channel maps, + but one uses front left/right and the other uses side left/right, it makes logical + sense to just map these. The way we'll do it is we'll check if there is a logical + corresponding mapping, and if so, apply it, but we will *not* break from the loop, + thereby giving the loop a chance to find an exact match later which will take priority. + */ + switch (channelOut) + { + /* Left channels. */ + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + /* Right channels. */ + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + default: break; + } + } + } + + return MA_SUCCESS; +} + + +static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; + pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; + pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; + } else { + pFramesOut[iChannelOut*3 + 0] = 0; + } pFramesOut[iChannelOut*3 + 1] = 0; + } pFramesOut[iChannelOut*3 + 2] = 0; + + pFramesOut += channelsOut*3; + pFramesIn += channelsIn*3; + } +} + +static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) +{ + if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { + return MA_INVALID_ARGS; + } + + switch (format) + { + case ma_format_u8: + { + ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s16: + { + ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s24: + { + ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s32: + { + ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_f32: + { + ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + default: return MA_INVALID_ARGS; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannelIn; + ma_uint32 accumulationCount; + + if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { + return MA_INVALID_ARGS; + } + + /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ + + /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ + accumulationCount = 0; + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { + accumulationCount += 1; + } + } + + if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + if (channelIn != MA_CHANNEL_NONE) { + accumulation += pFramesIn[iChannelIn]; + } + } + + pFramesOut[0] = accumulation / accumulationCount; + pFramesOut += 1; + pFramesIn += channelsIn; + } + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ + switch (monoExpansionMode) + { + case ma_mono_expansion_mode_average: + { + float weight; + ma_uint32 validChannelCount = 0; + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + validChannelCount += 1; + } + } + + weight = 1.0f / validChannelCount; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iChannelOut] = pFramesIn[0] * weight; + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + } break; + + case ma_mono_expansion_mode_stereo_only: + { + if (channelsOut >= 2) { + ma_uint32 iChannelLeft = (ma_uint32)-1; + ma_uint32 iChannelRight = (ma_uint32)-1; + + /* + We first need to find our stereo channels. We prefer front-left and front-right, but + if they're not available, we'll also try side-left and side-right. If neither are + available we'll fall through to the default case below. + */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_SIDE_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_SIDE_RIGHT) { + iChannelRight = iChannelOut; + } + } + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_FRONT_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_FRONT_RIGHT) { + iChannelRight = iChannelOut; + } + } + + + if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { + /* We found our stereo channels so we can duplicate the signal across those channels. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { + pFramesOut[iChannelOut] = pFramesIn[0]; + } else { + pFramesOut[iChannelOut] = 0.0f; + } + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + + break; /* Get out of the switch. */ + } else { + /* Fallthrough. Does not have left and right channels. */ + goto default_handler; + } + } else { + /* Fallthrough. Does not have stereo channels. */ + goto default_handler; + } + }; /* Fallthrough. See comments above. */ + + case ma_mono_expansion_mode_duplicate: + default: + { + default_handler: + { + if (channelsOut <= MA_MAX_CHANNELS) { + ma_bool32 hasEmptyChannel = MA_FALSE; + ma_channel channelPositions[MA_MAX_CHANNELS]; + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { + hasEmptyChannel = MA_TRUE; + } + } + + if (hasEmptyChannel == MA_FALSE) { + /* + Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully + help the compiler with auto-vectorization.m + */ + if (channelsOut == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { + pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + + _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { + pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 8) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + __m128 in = _mm_set1_ps(pFramesIn[iFrame]); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); + } + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { + pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else { + iFrame = 0; + + #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ + generic_on_fastpath: + #endif + { + for (; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Need to handle MA_CHANNEL_NONE. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Too many channels to store on the stack. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } + } break; + } + + return MA_SUCCESS; +} + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) +{ + ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); + + /* Optimized Path: Passthrough */ + if (conversionPath == ma_channel_conversion_path_passthrough) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); + return; + } + + /* Special Path: Mono Output. */ + if (conversionPath == ma_channel_conversion_path_mono_out) { + ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); + return; + } + + /* Special Path: Mono Input. */ + if (conversionPath == ma_channel_conversion_path_mono_in) { + ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); + return; + } + + /* Getting here means we aren't running on an optimized conversion path. */ + if (channelsOut <= MA_MAX_CHANNELS) { + ma_result result; + + if (mode == ma_channel_mix_mode_simple) { + ma_channel shuffleTable[MA_MAX_CHANNELS]; + + result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); + if (result != MA_SUCCESS) { + return; + } + + result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); + if (result != MA_SUCCESS) { + return; + } + } else { + ma_uint32 iFrame; + ma_uint32 iChannelOut; + ma_uint32 iChannelIn; + float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ + + /* + If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to + fall back to a slower path because otherwise we'll run out of stack space. + */ + if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { + /* Pre-compute weights. */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + } + + iFrame = 0; + + /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ + if (channelsOut == 8) { + /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ + if (channelsIn == 2) { + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; + accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; + accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; + accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; + accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; + accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; + accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; + accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; + + accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; + accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; + accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; + accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; + accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; + accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; + accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; + accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } else { + /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; + accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; + } + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } + } else if (channelsOut == 6) { + /* + When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll + expand our weights and do two frames at a time. + */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + } + + pFramesOut[iFrame*6 + 0] = accumulation[0]; + pFramesOut[iFrame*6 + 1] = accumulation[1]; + pFramesOut[iFrame*6 + 2] = accumulation[2]; + pFramesOut[iFrame*6 + 3] = accumulation[3]; + pFramesOut[iFrame*6 + 4] = accumulation[4]; + pFramesOut[iFrame*6 + 5] = accumulation[5]; + } + } + + /* Leftover frames. */ + for (; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; + } + + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; + } + } + } else { + /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; + } + } + } + } + } else { + /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); + } +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t channelMapOutOffset; + size_t shuffleTableOffset; + size_t weightsOffset; +} ma_channel_converter_heap_layout; + +static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) +{ + return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); +} + +static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) +{ + ma_channel_conversion_path conversionPath; + + MA_ASSERT(pHeapLayout != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; + } + + /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapOut != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; + } + + /* Alignment for the next section. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ + conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + /* Shuffle table */ + pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_shuffle) { + pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; + } + + /* Weights */ + pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_weights) { + pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; + pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pConverter); + + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); + + pConverter->format = pConfig->format; + pConverter->channelsIn = pConfig->channelsIn; + pConverter->channelsOut = pConfig->channelsOut; + pConverter->mixingMode = pConfig->mixingMode; + + if (pConfig->pChannelMapIn != NULL) { + pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); + } else { + pConverter->pChannelMapIn = NULL; /* Use default channel map. */ + } + + if (pConfig->pChannelMapOut != NULL) { + pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } else { + pConverter->pChannelMapOut = NULL; /* Use default channel map. */ + } + + pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { + pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); + ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); + } + + if (pConverter->conversionPath == ma_channel_conversion_path_weights) { + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); + } + } else { + pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); + } + } + + /* Silence our weights by default. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = 0; + } + } + } + + /* + We now need to fill out our weights table. This is determined by the mixing mode. + */ + + /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (channelPosIn == channelPosOut) { + float weight = 1; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + + switch (pConverter->mixingMode) + { + case ma_channel_mix_mode_custom_weights: + { + if (pConfig->ppWeights == NULL) { + return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ + } + + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } break; + + case ma_channel_mix_mode_simple: + { + /* + In simple mode, only set weights for channels that have exactly matching types, leave the rest at + zero. The 1:1 mappings have already been covered before this switch statement. + */ + } break; + + case ma_channel_mix_mode_rectangular: + default: + { + /* Unmapped input channels. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + if (ma_is_spatial_channel_position(channelPosIn)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (ma_is_spatial_channel_position(channelPosOut)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); + } + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + } + } + } + + /* Unmapped output channels. */ + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (ma_is_spatial_channel_position(channelPosOut)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + if (ma_is_spatial_channel_position(channelPosIn)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); + } + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + } + } + } + + /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ + if (pConfig->calculateLFEFromSpatialChannels) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { + ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); + ma_uint32 iChannelOutLFE; + + if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { + const float weightForLFE = 1.0f / spatialChannelCount; + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + if (ma_is_spatial_channel_position(channelPosIn)) { + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); + } + } + } + } + } + } + } + } break; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pConverter == NULL) { + return; + } + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); + } +} + +static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + + ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); + + return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); +} + +static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsIn == 1); + + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; + } + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + if (pConverter->channelsOut == 2) { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; + pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; + } + } else { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; + } + } + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; + ma_uint64 iSampleIn = iFrame; + pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; + pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; + pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; + } + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; + } + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pConverter->channelsOut == 2) { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; + pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; + } + } else { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; + } + } + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsOut == 1); + + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); + } + + pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); + } + + ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + float t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutF32[iFrame] = t / pConverter->channelsIn; + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 iFrame; + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + + /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ + + /* Clear. */ + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + + /* Accumulate. */ + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); + ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); + ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); + pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); + } + } + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; + s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; + + pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); + } + } + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); + ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); + ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); + ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); + } + } + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; + s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; + + pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); + } + } + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; + } + } + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesIn == NULL) { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + return MA_SUCCESS; + } + + switch (pConverter->conversionPath) + { + case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_weights: + default: + { + return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); + } + } +} + +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); + + return MA_SUCCESS; +} + + +/************************************************************************************************************************************************************** + +Data Conversion + +**************************************************************************************************************************************************************/ +MA_API ma_data_converter_config ma_data_converter_config_init_default(void) +{ + ma_data_converter_config config; + MA_ZERO_OBJECT(&config); + + config.ditherMode = ma_dither_mode_none; + config.resampling.algorithm = ma_resample_algorithm_linear; + config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ + + /* Linear resampling defaults. */ + config.resampling.linear.lpfOrder = 1; + + return config; +} + +MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_data_converter_config config = ma_data_converter_config_init_default(); + config.formatIn = formatIn; + config.formatOut = formatOut; + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelConverterOffset; + size_t resamplerOffset; +} ma_data_converter_heap_layout; + +static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; +} + +static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + /* + We want to avoid as much data conversion as possible. The channel converter and linear + resampler both support s16 and f32 natively. We need to decide on the format to use for this + stage. We call this the mid format because it's used in the middle stage of the conversion + pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it + will do the same thing for the input format. If it's neither we just use f32. If we are using a + custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced + to use that if resampling is required. + */ + if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { + return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ + } else { + /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { + return pConfig->formatOut; + } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { + return pConfig->formatIn; + } else { + return ma_format_f32; + } + } +} + +static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_channel_converter_config channelConverterConfig; + + MA_ASSERT(pConfig != NULL); + + channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); + channelConverterConfig.ppWeights = pConfig->ppChannelWeights; + channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; + + return channelConverterConfig; +} + +static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_resampler_config resamplerConfig; + ma_uint32 resamplerChannels; + + MA_ASSERT(pConfig != NULL); + + /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ + if (pConfig->channelsIn < pConfig->channelsOut) { + resamplerChannels = pConfig->channelsIn; + } else { + resamplerChannels = pConfig->channelsOut; + } + + resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); + resamplerConfig.linear = pConfig->resampling.linear; + resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; + resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; + + return resamplerConfig; +} + +static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel converter. */ + pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; + { + size_t heapSizeInBytes; + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); + + result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Resampler. */ + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + if (ma_data_converter_config_is_resampler_required(pConfig)) { + size_t heapSizeInBytes; + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); + + result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; + ma_format midFormat; + ma_bool32 isResamplingRequired; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pConverter); + + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pConverter->formatIn = pConfig->formatIn; + pConverter->formatOut = pConfig->formatOut; + pConverter->channelsIn = pConfig->channelsIn; + pConverter->channelsOut = pConfig->channelsOut; + pConverter->sampleRateIn = pConfig->sampleRateIn; + pConverter->sampleRateOut = pConfig->sampleRateOut; + pConverter->ditherMode = pConfig->ditherMode; + + /* + Determine if resampling is required. We need to do this so we can determine an appropriate + mid format to use. If resampling is required, the mid format must be ma_format_f32 since + that is the only one that is guaranteed to supported by custom resampling backends. + */ + isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); + midFormat = ma_data_converter_config_get_mid_format(pConfig); + + + /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ + { + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); + + result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); + if (result != MA_SUCCESS) { + return result; + } + + /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ + if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { + pConverter->hasChannelConverter = MA_TRUE; + } + } + + + /* Resampler. */ + if (isResamplingRequired) { + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); + + result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->hasResampler = MA_TRUE; + } + + + /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ + if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { + /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ + if (pConverter->formatIn == pConverter->formatOut) { + /* The formats are the same so we can just pass through. */ + pConverter->hasPreFormatConversion = MA_FALSE; + pConverter->hasPostFormatConversion = MA_FALSE; + } else { + /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ + pConverter->hasPreFormatConversion = MA_FALSE; + pConverter->hasPostFormatConversion = MA_TRUE; + } + } else { + /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ + if (pConverter->formatIn != midFormat) { + pConverter->hasPreFormatConversion = MA_TRUE; + } + if (pConverter->formatOut != midFormat) { + pConverter->hasPostFormatConversion = MA_TRUE; + } + } + + /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ + if (pConverter->hasPreFormatConversion == MA_FALSE && + pConverter->hasPostFormatConversion == MA_FALSE && + pConverter->hasChannelConverter == MA_FALSE && + pConverter->hasResampler == MA_FALSE) { + pConverter->isPassthrough = MA_TRUE; + } + + + /* We now need to determine our execution path. */ + if (pConverter->isPassthrough) { + pConverter->executionPath = ma_data_converter_execution_path_passthrough; + } else { + if (pConverter->channelsIn < pConverter->channelsOut) { + /* Do resampling first, if necessary. */ + MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); + + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Do channel conversion first, if necessary. */ + if (pConverter->hasChannelConverter) { + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_channels_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Channel routing not required. */ + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_only; + } else { + pConverter->executionPath = ma_data_converter_execution_path_format_only; + } + } + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pConverter == NULL) { + return; + } + + if (pConverter->hasResampler) { + ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); + } + + ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); + } +} + +static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pFramesOut != NULL) { + if (pFramesIn != NULL) { + ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pFramesOut != NULL) { + if (pFramesIn != NULL) { + ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); + } else { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + + +static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result = MA_SUCCESS; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + const void* pFramesInThisIteration; + /* */ void* pFramesOutThisIteration; + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + + if (pFramesIn != NULL) { + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } else { + pFramesInThisIteration = NULL; + } + + if (pFramesOut != NULL) { + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + pFramesOutThisIteration = NULL; + } + + /* Do a pre format conversion if necessary. */ + if (pConverter->hasPreFormatConversion) { + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountInThisIteration > tempBufferOutCap) { + frameCountInThisIteration = tempBufferOutCap; + } + } + + if (pFramesInThisIteration != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + } else { + MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); + } + + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + + if (pConverter->hasPostFormatConversion) { + /* Both input and output conversion required. Output to the temp buffer. */ + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); + } else { + /* Only pre-format required. Output straight to the output buffer. */ + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); + } + + if (result != MA_SUCCESS) { + break; + } + } else { + /* No pre-format required. Just read straight from the input buffer. */ + MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); + + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + break; + } + } + + /* If we are doing a post format conversion we need to do that now. */ + if (pConverter->hasPostFormatConversion) { + if (pFramesOutThisIteration != NULL) { + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); + } + } + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return result; +} + +static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pConverter != NULL); + + if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { + /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ + return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + /* Format conversion required. */ + return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + +static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { + /* No format conversion required. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } else { + /* Format conversion required. */ + ma_uint64 framesProcessed = 0; + + while (framesProcessed < frameCount) { + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + const void* pFramesInThisIteration; + /* */ void* pFramesOutThisIteration; + ma_uint64 frameCountThisIteration; + + if (pFramesIn != NULL) { + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } else { + pFramesInThisIteration = NULL; + } + + if (pFramesOut != NULL) { + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + pFramesOutThisIteration = NULL; + } + + /* Do a pre format conversion if necessary. */ + if (pConverter->hasPreFormatConversion) { + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); + + frameCountThisIteration = (frameCount - framesProcessed); + if (frameCountThisIteration > tempBufferInCap) { + frameCountThisIteration = tempBufferInCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountThisIteration > tempBufferOutCap) { + frameCountThisIteration = tempBufferOutCap; + } + } + + if (pFramesInThisIteration != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); + } else { + MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); + } + + if (pConverter->hasPostFormatConversion) { + /* Both input and output conversion required. Output to the temp buffer. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); + } else { + /* Only pre-format required. Output straight to the output buffer. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); + } + + if (result != MA_SUCCESS) { + break; + } + } else { + /* No pre-format required. Just read straight from the input buffer. */ + MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); + + frameCountThisIteration = (frameCount - framesProcessed); + if (frameCountThisIteration > tempBufferOutCap) { + frameCountThisIteration = tempBufferOutCap; + } + + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); + if (result != MA_SUCCESS) { + break; + } + } + + /* If we are doing a post format conversion we need to do that now. */ + if (pConverter->hasPostFormatConversion) { + if (pFramesOutThisIteration != NULL) { + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); + } + } + + framesProcessed += frameCountThisIteration; + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ + ma_uint64 tempBufferInCap; + ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ + ma_uint64 tempBufferMidCap; + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ + ma_uint64 tempBufferOutCap; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); + MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + + while (framesProcessedOut < frameCountOut) { + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + const void* pRunningFramesIn = NULL; + void* pRunningFramesOut = NULL; + const void* pResampleBufferIn; + void* pChannelsBufferOut; + + if (pFramesIn != NULL) { + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } + if (pFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + + /* Run input data through the resampler and output it to the temporary buffer. */ + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + + if (pConverter->hasPreFormatConversion) { + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + } + + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferMidCap) { + frameCountOutThisIteration = tempBufferMidCap; + } + + /* We can't read more frames than can fit in the output buffer. */ + if (pConverter->hasPostFormatConversion) { + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + } + + /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ + + /* + We need to try to predict how many input frames will be required for the resampler. If the + resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further + off we are from this, the more wasted format conversions we'll end up doing. + */ + #if 1 + { + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + + if (frameCountInThisIteration > requiredInputFrameCount) { + frameCountInThisIteration = requiredInputFrameCount; + } + } + #endif + + if (pConverter->hasPreFormatConversion) { + if (pFramesIn != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + pResampleBufferIn = pTempBufferIn; + } else { + pResampleBufferIn = NULL; + } + } else { + pResampleBufferIn = pRunningFramesIn; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* + The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do + this part if we have an output buffer. + */ + if (pFramesOut != NULL) { + if (pConverter->hasPostFormatConversion) { + pChannelsBufferOut = pTempBufferOut; + } else { + pChannelsBufferOut = pRunningFramesOut; + } + + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + /* Finally we do post format conversion. */ + if (pConverter->hasPostFormatConversion) { + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); + } + } + + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ + ma_uint64 tempBufferInCap; + ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ + ma_uint64 tempBufferMidCap; + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ + ma_uint64 tempBufferOutCap; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); + MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); + tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + + while (framesProcessedOut < frameCountOut) { + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + const void* pRunningFramesIn = NULL; + void* pRunningFramesOut = NULL; + const void* pChannelsBufferIn; + void* pResampleBufferOut; + + if (pFramesIn != NULL) { + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } + if (pFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + + /* + Before doing any processing we need to determine how many frames we should try processing + this iteration, for both input and output. The resampler requires us to perform format and + channel conversion before passing any data into it. If we get our input count wrong, we'll + end up peforming redundant pre-processing. This isn't the end of the world, but it does + result in some inefficiencies proportionate to how far our estimates are off. + + If the resampler has a means to calculate exactly how much we'll need, we'll use that. + Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output + frame count first. + */ + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferMidCap) { + frameCountOutThisIteration = tempBufferMidCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + } + + /* Now that we have the output frame count we can determine the input frame count. */ + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + if (pConverter->hasPreFormatConversion) { + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + } + + if (frameCountInThisIteration > tempBufferMidCap) { + frameCountInThisIteration = tempBufferMidCap; + } + + #if 1 + { + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + + if (frameCountInThisIteration > requiredInputFrameCount) { + frameCountInThisIteration = requiredInputFrameCount; + } + } + #endif + + + /* Pre format conversion. */ + if (pConverter->hasPreFormatConversion) { + if (pRunningFramesIn != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + pChannelsBufferIn = pTempBufferIn; + } else { + pChannelsBufferIn = NULL; + } + } else { + pChannelsBufferIn = pRunningFramesIn; + } + + + /* Channel conversion. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* Resampling. */ + if (pConverter->hasPostFormatConversion) { + pResampleBufferOut = pTempBufferOut; + } else { + pResampleBufferOut = pRunningFramesOut; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* Post format conversion. */ + if (pConverter->hasPostFormatConversion) { + if (pRunningFramesOut != NULL) { + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); + } + } + + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + switch (pConverter->executionPath) + { + case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + default: return MA_INVALID_OPERATION; /* Should never hit this. */ + } +} + +MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler == MA_FALSE) { + return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ + } + + return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); +} + +MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler == MA_FALSE) { + return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ + } + + return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); +} + +MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return 0; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_input_latency(&pConverter->resampler); + } + + return 0; /* No latency without a resampler. */ +} + +MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return 0; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_output_latency(&pConverter->resampler); + } + + return 0; /* No latency without a resampler. */ +} + +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); + } else { + *pInputFrameCount = outputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); + } else { + *pOutputFrameCount = inputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + /* There's nothing to do if we're not resampling. */ + if (pConverter->hasResampler == MA_FALSE) { + return MA_SUCCESS; + } + + return ma_resampler_reset(&pConverter->resampler); +} + + + +/************************************************************************************************************************************************************** + +Channel Maps + +**************************************************************************************************************************************************************/ +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); + +MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (pChannelMap == NULL) { + return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); + } else { + if (channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + return pChannelMap[channelIndex]; + } +} + +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) +{ + if (pChannelMap == NULL) { + return; + } + + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); +} + + +static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (channelCount == 0 || channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: /* No defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP + /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_CENTER; + #else + /* Quad. */ + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + #endif + } + } break; + + case 5: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_SIDE_LEFT; + case 5: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 7: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_CENTER; + case 5: return MA_CHANNEL_SIDE_LEFT; + case 6: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_SIDE_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_FRONT_RIGHT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + } + } break; + } + + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_CENTER; + case 5: return MA_CHANNEL_SIDE_LEFT; + case 6: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: /* No defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 6: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + + +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (channelCount == 0 || channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + switch (standardChannelMap) + { + case ma_standard_channel_map_alsa: + { + return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_rfc3551: + { + return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_flac: + { + return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_vorbis: + { + return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_sound4: + { + return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_sndio: + { + return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_microsoft: /* Also default. */ + /*case ma_standard_channel_map_default;*/ + default: + { + return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); + } break; + } +} + +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) +{ + ma_uint32 iChannel; + + if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { + return; + } + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + if (channelMapCap == 0) { + break; /* Ran out of room. */ + } + + pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); + pChannelMap += 1; + channelMapCap -= 1; + } +} + +MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) +{ + if (pOut != NULL && pIn != NULL && channels > 0) { + MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); + } +} + +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) +{ + if (pOut == NULL || channels == 0) { + return; + } + + if (pIn != NULL) { + ma_channel_map_copy(pOut, pIn, channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); + } +} + +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) +{ + /* A channel count of 0 is invalid. */ + if (channels == 0) { + return MA_FALSE; + } + + /* It does not make sense to have a mono channel when there is more than 1 channel. */ + if (channels > 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { + return MA_FALSE; + } + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) +{ + ma_uint32 iChannel; + + if (pChannelMapA == pChannelMapB) { + return MA_TRUE; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) +{ + ma_uint32 iChannel; + + /* A null channel map is equivalent to the default channel map. */ + if (pChannelMap == NULL) { + return MA_FALSE; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) +{ + return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); +} + +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) +{ + ma_uint32 iChannel; + + if (pChannelIndex != NULL) { + *pChannelIndex = (ma_uint32)-1; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { + if (pChannelIndex != NULL) { + *pChannelIndex = iChannel; + } + + return MA_TRUE; + } + } + + /* Getting here means the channel position was not found. */ + return MA_FALSE; +} + +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) +{ + size_t len; + ma_uint32 iChannel; + + len = 0; + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); + size_t channelStrLen = strlen(pChannelStr); + + /* Append the string if necessary. */ + if (pBufferOut != NULL && bufferCap > len + channelStrLen) { + MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); + } + len += channelStrLen; + + /* Append a space if it's not the last item. */ + if (iChannel+1 < channels) { + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = ' '; + } + len += 1; + } + } + + /* Null terminate. Don't increment the length here. */ + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = '\0'; + } + + return len; +} + +MA_API const char* ma_channel_position_to_string(ma_channel channel) +{ + switch (channel) + { + case MA_CHANNEL_NONE : return "CHANNEL_NONE"; + case MA_CHANNEL_MONO : return "CHANNEL_MONO"; + case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; + case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; + case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; + case MA_CHANNEL_LFE : return "CHANNEL_LFE"; + case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; + case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; + case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; + case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; + case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; + case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; + case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; + case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; + case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; + case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; + case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; + case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; + case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; + case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; + case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; + case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; + case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; + case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; + case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; + case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; + case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; + case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; + case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; + case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; + case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; + case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; + case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; + case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; + case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; + case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; + case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; + case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; + case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; + case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; + case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; + case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; + case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; + case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; + case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; + case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; + case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; + case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; + case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; + case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; + default: break; + } + + return "UNKNOWN"; +} + + + +/************************************************************************************************************************************************************** + +Conversion Helpers + +**************************************************************************************************************************************************************/ +MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) +{ + ma_data_converter_config config; + + config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); + config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + + return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); +} + +MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) +{ + ma_result result; + ma_data_converter converter; + + if (frameCountIn == 0 || pConfig == NULL) { + return 0; + } + + result = ma_data_converter_init(pConfig, NULL, &converter); + if (result != MA_SUCCESS) { + return 0; /* Failed to initialize the data converter. */ + } + + if (pOut == NULL) { + result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); + if (result != MA_SUCCESS) { + if (result == MA_NOT_IMPLEMENTED) { + /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ + frameCountOut = 0; + + while (frameCountIn > 0) { + ma_uint64 framesProcessedIn = frameCountIn; + ma_uint64 framesProcessedOut = 0xFFFFFFFF; + + result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); + if (result != MA_SUCCESS) { + break; + } + + frameCountIn -= framesProcessedIn; + } + } + } + } else { + result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); + if (result != MA_SUCCESS) { + frameCountOut = 0; + } + } + + ma_data_converter_uninit(&converter, NULL); + return frameCountOut; +} + + +/************************************************************************************************************************************************************** + +Ring Buffer + +**************************************************************************************************************************************************************/ +static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) +{ + return encodedOffset & 0x7FFFFFFF; +} + +static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) +{ + return encodedOffset & 0x80000000; +} + +static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); +} + +static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); +} + +static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) +{ + return offsetLoopFlag | offsetInBytes; +} + +static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) +{ + MA_ASSERT(pOffsetInBytes != NULL); + MA_ASSERT(pOffsetLoopFlag != NULL); + + *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); + *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); +} + + +MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) +{ + ma_result result; + const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + if (subbufferSizeInBytes == 0 || subbufferCount == 0) { + return MA_INVALID_ARGS; + } + + if (subbufferSizeInBytes > maxSubBufferSize) { + return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ + } + + + MA_ZERO_OBJECT(pRB); + + result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; + pRB->subbufferCount = (ma_uint32)subbufferCount; + + if (pOptionalPreallocatedBuffer != NULL) { + pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; + pRB->pBuffer = pOptionalPreallocatedBuffer; + } else { + size_t bufferSizeInBytes; + + /* + Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this + we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. + */ + pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; + + bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; + pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); + if (pRB->pBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); + pRB->ownsBuffer = MA_TRUE; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) +{ + return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); +} + +MA_API void ma_rb_uninit(ma_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + if (pRB->ownsBuffer) { + ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); + } +} + +MA_API void ma_rb_reset(ma_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); +} + +MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) +{ + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + size_t bytesAvailable; + size_t bytesRequested; + + if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { + return MA_INVALID_ARGS; + } + + /* The returned buffer should never move ahead of the write pointer. */ + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + /* + The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we + can only read up to the write pointer. If not, we can only read up to the end of the buffer. + */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + bytesAvailable = writeOffsetInBytes - readOffsetInBytes; + } else { + bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; + } + + bytesRequested = *pSizeInBytes; + if (bytesRequested > bytesAvailable) { + bytesRequested = bytesAvailable; + } + + *pSizeInBytes = bytesRequested; + (*ppBufferOut) = ma_rb__get_read_ptr(pRB); + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 newReadOffsetInBytes; + ma_uint32 newReadOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); + if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ + } + + /* Move the read pointer back to the start if necessary. */ + newReadOffsetLoopFlag = readOffsetLoopFlag; + if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { + newReadOffsetInBytes = 0; + newReadOffsetLoopFlag ^= 0x80000000; + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); + + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } +} + +MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + size_t bytesAvailable; + size_t bytesRequested; + + if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { + return MA_INVALID_ARGS; + } + + /* The returned buffer should never overtake the read buffer. */ + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + /* + In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only + write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should + never overtake the read pointer. + */ + if (writeOffsetLoopFlag == readOffsetLoopFlag) { + bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; + } else { + bytesAvailable = readOffsetInBytes - writeOffsetInBytes; + } + + bytesRequested = *pSizeInBytes; + if (bytesRequested > bytesAvailable) { + bytesRequested = bytesAvailable; + } + + *pSizeInBytes = bytesRequested; + *ppBufferOut = ma_rb__get_write_ptr(pRB); + + /* Clear the buffer if desired. */ + if (pRB->clearOnWriteAcquire) { + MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) +{ + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newWriteOffsetInBytes; + ma_uint32 newWriteOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); + if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ + } + + /* Move the read pointer back to the start if necessary. */ + newWriteOffsetLoopFlag = writeOffsetLoopFlag; + if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { + newWriteOffsetInBytes = 0; + newWriteOffsetLoopFlag ^= 0x80000000; + } + + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); + + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } +} + +MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newReadOffsetInBytes; + ma_uint32 newReadOffsetLoopFlag; + + if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + newReadOffsetLoopFlag = readOffsetLoopFlag; + + /* We cannot go past the write buffer. */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { + newReadOffsetInBytes = writeOffsetInBytes; + } else { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); + } + } else { + /* May end up looping. */ + if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; + newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ + } else { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); + } + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newWriteOffsetInBytes; + ma_uint32 newWriteOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + newWriteOffsetLoopFlag = writeOffsetLoopFlag; + + /* We cannot go past the write buffer. */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + /* May end up looping. */ + if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; + newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ + } else { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); + } + } else { + if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { + newWriteOffsetInBytes = readOffsetInBytes; + } else { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); + } + } + + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + return MA_SUCCESS; +} + +MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + + if (pRB == NULL) { + return 0; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + return writeOffsetInBytes - readOffsetInBytes; + } else { + return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); + } +} + +MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) +{ + ma_int32 dist; + + if (pRB == NULL) { + return 0; + } + + dist = ma_rb_pointer_distance(pRB); + if (dist < 0) { + return 0; + } + + return dist; +} + +MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); +} + +MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->subbufferSizeInBytes; +} + +MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + if (pRB->subbufferStrideInBytes == 0) { + return (size_t)pRB->subbufferSizeInBytes; + } + + return (size_t)pRB->subbufferStrideInBytes; +} + +MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) +{ + if (pRB == NULL) { + return 0; + } + + return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); +} + +MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) +{ + if (pRB == NULL) { + return NULL; + } + + return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); +} + + + +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + +static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + + return ma_get_bytes_per_frame(pRB->format, pRB->channels); +} + +MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) +{ + ma_uint32 bpf; + ma_result result; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pRB); + + bpf = ma_get_bytes_per_frame(format, channels); + if (bpf == 0) { + return MA_INVALID_ARGS; + } + + result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); + if (result != MA_SUCCESS) { + return result; + } + + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) +{ + return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); +} + +MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_data_source_uninit(&pRB->ds); + ma_rb_uninit(&pRB->rb); +} + +MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_rb_reset(&pRB->rb); +} + +MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) +{ + size_t sizeInBytes; + ma_result result; + + if (pRB == NULL || pSizeInFrames == NULL) { + return MA_INVALID_ARGS; + } + + sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); + + result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); + if (result != MA_SUCCESS) { + return result; + } + + *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) +{ + size_t sizeInBytes; + ma_result result; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); + + result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); + if (result != MA_SUCCESS) { + return result; + } + + *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) +{ + if (pRB == NULL) { + return NULL; + } + + return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); +} + +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + + + +MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) +{ + ma_result result; + ma_uint32 sizeInFrames; + + sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); + if (sizeInFrames == 0) { + return MA_INVALID_ARGS; + } + + result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); + if (result != MA_SUCCESS) { + return result; + } + + /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ + ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); + + return MA_SUCCESS; +} + +MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) +{ + ma_pcm_rb_uninit((ma_pcm_rb*)pRB); + return MA_SUCCESS; +} + + + +/************************************************************************************************************************************************************** + +Miscellaneous Helpers + +**************************************************************************************************************************************************************/ +MA_API const char* ma_result_description(ma_result result) +{ + switch (result) + { + case MA_SUCCESS: return "No error"; + case MA_ERROR: return "Unknown error"; + case MA_INVALID_ARGS: return "Invalid argument"; + case MA_INVALID_OPERATION: return "Invalid operation"; + case MA_OUT_OF_MEMORY: return "Out of memory"; + case MA_OUT_OF_RANGE: return "Out of range"; + case MA_ACCESS_DENIED: return "Permission denied"; + case MA_DOES_NOT_EXIST: return "Resource does not exist"; + case MA_ALREADY_EXISTS: return "Resource already exists"; + case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; + case MA_INVALID_FILE: return "Invalid file"; + case MA_TOO_BIG: return "Too large"; + case MA_PATH_TOO_LONG: return "Path too long"; + case MA_NAME_TOO_LONG: return "Name too long"; + case MA_NOT_DIRECTORY: return "Not a directory"; + case MA_IS_DIRECTORY: return "Is a directory"; + case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; + case MA_AT_END: return "At end"; + case MA_NO_SPACE: return "No space available"; + case MA_BUSY: return "Device or resource busy"; + case MA_IO_ERROR: return "Input/output error"; + case MA_INTERRUPT: return "Interrupted"; + case MA_UNAVAILABLE: return "Resource unavailable"; + case MA_ALREADY_IN_USE: return "Resource already in use"; + case MA_BAD_ADDRESS: return "Bad address"; + case MA_BAD_SEEK: return "Illegal seek"; + case MA_BAD_PIPE: return "Broken pipe"; + case MA_DEADLOCK: return "Deadlock"; + case MA_TOO_MANY_LINKS: return "Too many links"; + case MA_NOT_IMPLEMENTED: return "Not implemented"; + case MA_NO_MESSAGE: return "No message of desired type"; + case MA_BAD_MESSAGE: return "Invalid message"; + case MA_NO_DATA_AVAILABLE: return "No data available"; + case MA_INVALID_DATA: return "Invalid data"; + case MA_TIMEOUT: return "Timeout"; + case MA_NO_NETWORK: return "Network unavailable"; + case MA_NOT_UNIQUE: return "Not unique"; + case MA_NOT_SOCKET: return "Socket operation on non-socket"; + case MA_NO_ADDRESS: return "Destination address required"; + case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; + case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; + case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; + case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; + case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; + case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; + case MA_CONNECTION_RESET: return "Connection reset"; + case MA_ALREADY_CONNECTED: return "Already connected"; + case MA_NOT_CONNECTED: return "Not connected"; + case MA_CONNECTION_REFUSED: return "Connection refused"; + case MA_NO_HOST: return "No host"; + case MA_IN_PROGRESS: return "Operation in progress"; + case MA_CANCELLED: return "Operation cancelled"; + case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; + + case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; + case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; + case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; + case MA_NO_BACKEND: return "No backend"; + case MA_NO_DEVICE: return "No device"; + case MA_API_NOT_FOUND: return "API not found"; + case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; + + case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; + case MA_DEVICE_NOT_STARTED: return "Device not started"; + + case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; + case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; + case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; + case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; + + default: return "Unknown error"; + } +} + +MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } else { + return NULL; /* Do not fall back to the default implementation. */ + } + } else { + return ma__malloc_default(sz, NULL); + } +} + +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + void* p = ma_malloc(sz, pAllocationCallbacks); + if (p != NULL) { + MA_ZERO_MEMORY(p, sz); + } + + return p; +} + +MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); + } else { + return NULL; /* Do not fall back to the default implementation. */ + } + } else { + return ma__realloc_default(p, sz, NULL); + } +} + +MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL) { + return; + } + + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } else { + return; /* Do no fall back to the default implementation. */ + } + } else { + ma__free_default(p, NULL); + } +} + +MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t extraBytes; + void* pUnaligned; + void* pAligned; + + if (alignment == 0) { + return 0; + } + + extraBytes = alignment-1 + sizeof(void*); + + pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); + if (pUnaligned == NULL) { + return NULL; + } + + pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); + ((void**)pAligned)[-1] = pUnaligned; + + return pAligned; +} + +MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_free(((void**)p)[-1], pAllocationCallbacks); +} + +MA_API const char* ma_get_format_name(ma_format format) +{ + switch (format) + { + case ma_format_unknown: return "Unknown"; + case ma_format_u8: return "8-bit Unsigned Integer"; + case ma_format_s16: return "16-bit Signed Integer"; + case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; + case ma_format_s32: return "32-bit Signed Integer"; + case ma_format_f32: return "32-bit IEEE Floating Point"; + default: return "Invalid"; + } +} + +MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) +{ + ma_uint32 i; + for (i = 0; i < channels; ++i) { + pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); + } +} + + +MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) +{ + ma_uint32 sizes[] = { + 0, /* unknown */ + 1, /* u8 */ + 2, /* s16 */ + 3, /* s24 */ + 4, /* s32 */ + 4, /* f32 */ + }; + return sizes[format]; +} + + + +#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) + +MA_API ma_data_source_config ma_data_source_config_init(void) +{ + ma_data_source_config config; + + MA_ZERO_OBJECT(&config); + + return config; +} + + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSourceBase); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->vtable = pConfig->vtable; + pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; + pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ + pDataSourceBase->pNext = NULL; + pDataSourceBase->onGetNext = NULL; + + return MA_SUCCESS; +} + +MA_API void ma_data_source_uninit(ma_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return; + } + + /* + This is placeholder in case we need this later. Data sources need to call this in their + uninitialization routine to ensure things work later on if something is added here. + */ +} + +static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) +{ + ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; + + MA_ASSERT(pDataSource != NULL); + MA_ASSERT(ppCurrentDataSource != NULL); + + if (pCurrentDataSource->pCurrent == NULL) { + /* + The current data source is NULL. If we're using this in the context of a chain we need to return NULL + here so that we don't end up looping. Otherwise we just return the data source itself. + */ + if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { + pCurrentDataSource = NULL; + } else { + pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ + } + } else { + pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; + } + + *ppCurrentDataSource = pCurrentDataSource; + + return MA_SUCCESS; +} + +static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 framesRead = 0; + ma_bool32 loop = ma_data_source_is_looping(pDataSource); + + if (pDataSourceBase == NULL) { + return MA_AT_END; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { + /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + /* Need to clamp to within the range. */ + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); + if (result != MA_SUCCESS) { + /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + ma_uint64 rangeBeg; + ma_uint64 rangeEnd; + + /* We have the cursor. We need to make sure we don't read beyond our range. */ + rangeBeg = pDataSourceBase->rangeBegInFrames; + rangeEnd = pDataSourceBase->rangeEndInFrames; + + absoluteCursor = rangeBeg + relativeCursor; + + /* If looping, make sure we're within range. */ + if (loop) { + if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { + rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); + } + } + + if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { + frameCount = (rangeEnd - absoluteCursor); + } + + /* + If the cursor is sitting on the end of the range the frame count will be set to 0 which can + result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return + MA_AT_END so the higher level function can know about it. + */ + if (frameCount > 0) { + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_data_source_base* pCurrentDataSource; + void* pRunningFramesOut = pFramesOut; + ma_uint64 totalFramesProcessed = 0; + ma_format format; + ma_uint32 channels; + ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ + ma_bool32 loop; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + loop = ma_data_source_is_looping(pDataSource); + + /* + We need to know the data format so we can advance the output buffer as we read frames. If this + fails, chaining will not work and we'll just read as much as we can from the current source. + */ + if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { + result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); + if (result != MA_SUCCESS) { + return result; + } + + return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); + } + + /* + Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and + only the current data source will be read from. + */ + + /* Keep reading until we've read as many frames as possible. */ + while (totalFramesProcessed < frameCount) { + ma_uint64 framesProcessed; + ma_uint64 framesRemaining = frameCount - totalFramesProcessed; + + /* We need to resolve the data source that we'll actually be reading from. */ + result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); + if (result != MA_SUCCESS) { + break; + } + + if (pCurrentDataSource == NULL) { + break; + } + + result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); + totalFramesProcessed += framesProcessed; + + /* + If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is + not necessarily considered an error. + */ + if (result != MA_SUCCESS && result != MA_AT_END) { + break; + } + + /* + We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned + MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. + */ + if (result == MA_AT_END) { + /* + The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't + accidentally return MA_AT_END when data has been read in prior loop iterations. at the + end of this function, the result will be checked for MA_SUCCESS, and if the total + number of frames processed is 0, will be explicitly set to MA_AT_END. + */ + result = MA_SUCCESS; + + /* + We reached the end. If we're looping, we just loop back to the start of the current + data source. If we're not looping we need to check if we have another in the chain, and + if so, switch to it. + */ + if (loop) { + if (framesProcessed == 0) { + emptyLoopCounter += 1; + if (emptyLoopCounter > 1) { + break; /* Infinite loop detected. Get out. */ + } + } else { + emptyLoopCounter = 0; + } + + result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); + if (result != MA_SUCCESS) { + break; /* Failed to loop. Abort. */ + } + + /* Don't return MA_AT_END for looping sounds. */ + result = MA_SUCCESS; + } else { + if (pCurrentDataSource->pNext != NULL) { + pDataSourceBase->pCurrent = pCurrentDataSource->pNext; + } else if (pCurrentDataSource->onGetNext != NULL) { + pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); + if (pDataSourceBase->pCurrent == NULL) { + break; /* Our callback did not return a next data source. We're done. */ + } + } else { + /* Reached the end of the chain. We're done. */ + break; + } + + /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ + result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); + if (result != MA_SUCCESS) { + break; + } + } + } + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) +{ + return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); +} + +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSourceBase == NULL) { + return MA_SUCCESS; + } + + if (pDataSourceBase->vtable->onSeek == NULL) { + return MA_NOT_IMPLEMENTED; + } + + if (frameIndex > pDataSourceBase->rangeEndInFrames) { + return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ + } + + return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); +} + +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + + /* Initialize to defaults for safety just in case the data source does not implement this callback. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + if (pDataSourceBase->vtable->onGetDataFormat == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + return result; + } + + if (pFormat != NULL) { + *pFormat = format; + } + if (pChannels != NULL) { + *pChannels = channels; + } + if (pSampleRate != NULL) { + *pSampleRate = sampleRate; + } + + /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 cursor; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pDataSourceBase == NULL) { + return MA_SUCCESS; + } + + if (pDataSourceBase->vtable->onGetCursor == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); + if (result != MA_SUCCESS) { + return result; + } + + /* The cursor needs to be made relative to the start of the range. */ + if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ + *pCursor = 0; + } else { + *pCursor = cursor - pDataSourceBase->rangeBegInFrames; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + /* + If we have a range defined we'll use that to determine the length. This is one of rare times + where we'll actually trust the caller. If they've set the range, I think it's mostly safe to + assume they've set it based on some higher level knowledge of the structure of the sound bank. + */ + if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { + *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; + return MA_SUCCESS; + } + + /* + Getting here means a range is not defined so we'll need to get the data source itself to tell + us the length. + */ + if (pDataSourceBase->vtable->onGetLength == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); +} + +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) +{ + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) +{ + ma_result result; + ma_uint64 lengthInPCMFrames; + ma_uint32 sampleRate; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + + /* If there's no callback for this just treat it as a successful no-op. */ + if (pDataSourceBase->vtable->onSetLooping == NULL) { + return MA_SUCCESS; + } + + return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); +} + +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_FALSE; + } + + return ma_atomic_load_32(&pDataSourceBase->isLooping); +} + +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; + ma_bool32 doSeekAdjustment = MA_FALSE; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if (rangeEndInFrames < rangeBegInFrames) { + return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ + } + + /* + We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now + so we can calculate it's absolute position before we change the range. + */ + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); + if (result == MA_SUCCESS) { + doSeekAdjustment = MA_TRUE; + absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; + } else { + /* + We couldn't get the position of the cursor. It probably means the data source has no notion + of a cursor. We'll just leave it at position 0. Don't treat this as an error. + */ + doSeekAdjustment = MA_FALSE; + relativeCursor = 0; + absoluteCursor = 0; + } + + pDataSourceBase->rangeBegInFrames = rangeBegInFrames; + pDataSourceBase->rangeEndInFrames = rangeEndInFrames; + + /* + The commented out logic below was intended to maintain loop points in response to a change in the + range. However, this is not useful because it results in the sound breaking when you move the range + outside of the old loop points. I'm simplifying this by simply resetting the loop points. The + caller is expected to update their loop points if they change the range. + + In practice this should be mostly a non-issue because the majority of the time the range will be + set once right after initialization. + */ + pDataSourceBase->loopBegInFrames = 0; + pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); + + + /* + Seek to within range. Note that our seek positions here are relative to the new range. We don't want + do do this if we failed to retrieve the cursor earlier on because it probably means the data source + has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but + I'm just not even going to attempt it. + */ + if (doSeekAdjustment) { + if (absoluteCursor < rangeBegInFrames) { + ma_data_source_seek_to_pcm_frame(pDataSource, 0); + } else if (absoluteCursor > rangeEndInFrames) { + ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return; + } + + if (pRangeBegInFrames != NULL) { + *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; + } + + if (pRangeEndInFrames != NULL) { + *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; + } +} + +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if (loopEndInFrames < loopBegInFrames) { + return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ + } + + if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { + return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ + } + + pDataSourceBase->loopBegInFrames = loopBegInFrames; + pDataSourceBase->loopEndInFrames = loopEndInFrames; + + /* The end cannot exceed the range. */ + if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { + pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); + } + + return MA_SUCCESS; +} + +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return; + } + + if (pLoopBegInFrames != NULL) { + *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; + } + + if (pLoopEndInFrames != NULL) { + *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; + } +} + +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->pCurrent = pCurrentDataSource; + + return MA_SUCCESS; +} + +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->pCurrent; +} + +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->pNext = pNextDataSource; + + return MA_SUCCESS; +} + +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->pNext; +} + +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->onGetNext = onGetNext; + + return MA_SUCCESS; +} + +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->onGetNext; +} + + +static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (framesRead < frameCount || framesRead == 0) { + return MA_AT_END; + } + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pFormat = pAudioBufferRef->format; + *pChannels = pAudioBufferRef->channels; + *pSampleRate = pAudioBufferRef->sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pCursor = pAudioBufferRef->cursor; + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pLength = pAudioBufferRef->sizeInFrames; + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = +{ + ma_audio_buffer_ref__data_source_on_read, + ma_audio_buffer_ref__data_source_on_seek, + ma_audio_buffer_ref__data_source_on_get_data_format, + ma_audio_buffer_ref__data_source_on_get_cursor, + ma_audio_buffer_ref__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pAudioBufferRef); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); + if (result != MA_SUCCESS) { + return result; + } + + pAudioBufferRef->format = format; + pAudioBufferRef->channels = channels; + pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ + pAudioBufferRef->cursor = 0; + pAudioBufferRef->sizeInFrames = sizeInFrames; + pAudioBufferRef->pData = pData; + + return MA_SUCCESS; +} + +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) +{ + if (pAudioBufferRef == NULL) { + return; + } + + ma_data_source_uninit(&pAudioBufferRef->ds); +} + +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) +{ + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + pAudioBufferRef->cursor = 0; + pAudioBufferRef->sizeInFrames = sizeInFrames; + pAudioBufferRef->pData = pData; + + return MA_SUCCESS; +} + +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) +{ + ma_uint64 totalFramesRead = 0; + + if (pAudioBufferRef == NULL) { + return 0; + } + + if (frameCount == 0) { + return 0; + } + + while (totalFramesRead < frameCount) { + ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + ma_uint64 framesRemaining = frameCount - totalFramesRead; + ma_uint64 framesToRead; + + framesToRead = framesRemaining; + if (framesToRead > framesAvailable) { + framesToRead = framesAvailable; + } + + if (pFramesOut != NULL) { + ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); + } + + totalFramesRead += framesToRead; + + pAudioBufferRef->cursor += framesToRead; + if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { + if (loop) { + pAudioBufferRef->cursor = 0; + } else { + break; /* We've reached the end and we're not looping. Done. */ + } + } + + MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); + } + + return totalFramesRead; +} + +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) +{ + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + if (frameIndex > pAudioBufferRef->sizeInFrames) { + return MA_INVALID_ARGS; + } + + pAudioBufferRef->cursor = (size_t)frameIndex; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) +{ + ma_uint64 framesAvailable; + ma_uint64 frameCount = 0; + + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; /* Safety. */ + } + + if (pFrameCount != NULL) { + frameCount = *pFrameCount; + *pFrameCount = 0; /* Safety. */ + } + + if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + if (frameCount > framesAvailable) { + frameCount = framesAvailable; + } + + *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); + *pFrameCount = frameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) +{ + ma_uint64 framesAvailable; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + if (frameCount > framesAvailable) { + return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ + } + + pAudioBufferRef->cursor += frameCount; + + if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { + return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ + } else { + return MA_SUCCESS; + } +} + +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) +{ + if (pAudioBufferRef == NULL) { + return MA_FALSE; + } + + return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; +} + +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pAudioBufferRef->cursor; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = pAudioBufferRef->sizeInFrames; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + } + + return MA_SUCCESS; +} + + + + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_audio_buffer_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ + config.sizeInFrames = sizeInFrames; + config.pData = pData; + ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); + + return config; +} + +static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) +{ + ma_result result; + + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->sizeInFrames == 0) { + return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ + } + + result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); + if (result != MA_SUCCESS) { + return result; + } + + /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ + pAudioBuffer->ref.sampleRate = pConfig->sampleRate; + + ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); + + if (doCopy) { + ma_uint64 allocationSizeInBytes; + void* pData; + + allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); + if (allocationSizeInBytes > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ + if (pData == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (pConfig->pData != NULL) { + ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } else { + ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } + + ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); + pAudioBuffer->ownsData = MA_TRUE; + } else { + ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); + pAudioBuffer->ownsData = MA_FALSE; + } + + return MA_SUCCESS; +} + +static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) +{ + if (pAudioBuffer == NULL) { + return; + } + + if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { + ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ + } + + if (doFree) { + ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); + } + + ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); +} + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) +{ + return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); +} + +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) +{ + return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); +} + +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) +{ + ma_result result; + ma_audio_buffer* pAudioBuffer; + ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ + ma_uint64 allocationSizeInBytes; + + if (ppAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + *ppAudioBuffer = NULL; /* Safety. */ + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + innerConfig = *pConfig; + ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); + + allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); + if (allocationSizeInBytes > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ + if (pAudioBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (pConfig->pData != NULL) { + ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } else { + ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } + + innerConfig.pData = &pAudioBuffer->_pExtraData[0]; + + result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); + if (result != MA_SUCCESS) { + ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); + return result; + } + + *ppAudioBuffer = pAudioBuffer; + + return MA_SUCCESS; +} + +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) +{ + ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); +} + +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) +{ + ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); +} + +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) +{ + if (pAudioBuffer == NULL) { + return 0; + } + + return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); +} + +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); +} + +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) +{ + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; /* Safety. */ + } + + if (pAudioBuffer == NULL) { + if (pFrameCount != NULL) { + *pFrameCount = 0; + } + + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); +} + +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); +} + +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) +{ + if (pAudioBuffer == NULL) { + return MA_FALSE; + } + + return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); +} + +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); +} + +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); +} + +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); +} + + + + + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pData); + + pData->format = format; + pData->channels = channels; + pData->pTail = &pData->head; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_paged_audio_buffer_page* pPage; + + if (pData == NULL) { + return; + } + + /* All pages need to be freed. */ + pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); + while (pPage != NULL) { + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); + + ma_free(pPage, pAllocationCallbacks); + pPage = pNext; + } +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return &pData->head; +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return pData->pTail; +} + +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) +{ + ma_paged_audio_buffer_page* pPage; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + /* Calculate the length from the linked list. */ + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + *pLength += pPage->sizeInFrames; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) +{ + ma_paged_audio_buffer_page* pPage; + ma_uint64 allocationSize; + + if (ppPage == NULL) { + return MA_INVALID_ARGS; + } + + *ppPage = NULL; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); + if (allocationSize > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ + if (pPage == NULL) { + return MA_OUT_OF_MEMORY; + } + + pPage->pNext = NULL; + pPage->sizeInFrames = pageSizeInFrames; + + if (pInitialData != NULL) { + ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); + } + + *ppPage = pPage; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* It's assumed the page is not attached to the list. */ + ma_free(pPage, pAllocationCallbacks); + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ + + /* First thing to do is update the tail. */ + for (;;) { + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pNewTail = pPage; + + if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ + ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); + break; /* Done. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ +} + + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) +{ + ma_paged_audio_buffer_config config; + + MA_ZERO_OBJECT(&config); + config.pData = pData; + + return config; +} + + +static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; + + *pFormat = pPagedAudioBuffer->pData->format; + *pChannels = pPagedAudioBuffer->pData->channels; + *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); + + return MA_SUCCESS; +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = +{ + ma_paged_audio_buffer__data_source_on_read, + ma_paged_audio_buffer__data_source_on_seek, + ma_paged_audio_buffer__data_source_on_get_data_format, + ma_paged_audio_buffer__data_source_on_get_cursor, + ma_paged_audio_buffer__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPagedAudioBuffer); + + /* A config is required for the format and channel count. */ + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pData == NULL) { + return MA_INVALID_ARGS; /* No underlying data specified. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); + if (result != MA_SUCCESS) { + return result; + } + + pPagedAudioBuffer->pData = pConfig->pData; + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); + pPagedAudioBuffer->relativeCursor = 0; + pPagedAudioBuffer->absoluteCursor = 0; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) +{ + if (pPagedAudioBuffer == NULL) { + return; + } + + /* Nothing to do. The data needs to be deleted separately. */ +} + +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead = 0; + ma_format format; + ma_uint32 channels; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + format = pPagedAudioBuffer->pData->format; + channels = pPagedAudioBuffer->pData->channels; + + while (totalFramesRead < frameCount) { + /* Read from the current page. The buffer should never be in a state where this is NULL. */ + ma_uint64 framesRemainingInCurrentPage; + ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; + ma_uint64 framesToReadThisIteration; + + MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); + + framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; + + framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); + totalFramesRead += framesToReadThisIteration; + + pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; + pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; + + /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ + MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); + + if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { + /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + if (pNext == NULL) { + result = MA_AT_END; + break; /* We've reached the end. */ + } else { + pPagedAudioBuffer->pCurrent = pNext; + pPagedAudioBuffer->relativeCursor = 0; + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) +{ + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (frameIndex == pPagedAudioBuffer->absoluteCursor) { + return MA_SUCCESS; /* Nothing to do. */ + } + + if (frameIndex < pPagedAudioBuffer->absoluteCursor) { + /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); + pPagedAudioBuffer->absoluteCursor = 0; + pPagedAudioBuffer->relativeCursor = 0; + + /* Fall through to the forward seeking section below. */ + } + + if (frameIndex > pPagedAudioBuffer->absoluteCursor) { + /* Moving forward. */ + ma_paged_audio_buffer_page* pPage; + ma_uint64 runningCursor = 0; + + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + ma_uint64 pageRangeBeg = runningCursor; + ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; + + if (frameIndex >= pageRangeBeg) { + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + /* We found the page. */ + pPagedAudioBuffer->pCurrent = pPage; + pPagedAudioBuffer->absoluteCursor = frameIndex; + pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; + return MA_SUCCESS; + } + } + + runningCursor = pageRangeEnd; + } + + /* Getting here means we tried seeking too far forward. Don't change any state. */ + return MA_BAD_SEEK; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pPagedAudioBuffer->absoluteCursor; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); +} + + + +/************************************************************************************************************************************************************** + +VFS + +**************************************************************************************************************************************************************/ +MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pVFS == NULL || pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onOpen == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); +} + +MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pVFS == NULL || pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onOpenW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); +} + +MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onClose == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onClose(pVFS, file); +} + +MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + ma_result result; + size_t bytesRead = 0; + + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + if (pVFS == NULL || file == NULL || pDst == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onRead == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); + + if (pBytesRead != NULL) { + *pBytesRead = bytesRead; + } + + if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pBytesWritten != NULL) { + *pBytesWritten = 0; + } + + if (pVFS == NULL || file == NULL || pSrc == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onWrite == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +} + +MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onSeek == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onSeek(pVFS, file, offset, origin); +} + +MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onTell == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onTell(pVFS, file, pCursor); +} + +MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pInfo); + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onInfo == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onInfo(pVFS, file, pInfo); +} + + +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) + #define MA_USE_WIN32_FILEIO +#endif + +#if defined(MA_USE_WIN32_FILEIO) +/* +We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do +not have the Ex version. We therefore need to do some dynamic branching depending on what's available. + +We load these when we load our first file from the default VFS. It's left open for the life of the +program and is left to the OS to uninitialize when the program terminates. +*/ +typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); + +static ma_handle hKernel32DLL = NULL; +static ma_SetFilePointer_proc ma_SetFilePointer = NULL; +static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; + +static void ma_win32_fileio_init(void) +{ + if (hKernel32DLL == NULL) { + hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); + if (hKernel32DLL != NULL) { + ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); + ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); + } + } +} + +static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) +{ + *pDesiredAccess = 0; + if ((openMode & MA_OPEN_MODE_READ) != 0) { + *pDesiredAccess |= GENERIC_READ; + } + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + *pDesiredAccess |= GENERIC_WRITE; + } + + *pShareMode = 0; + if ((openMode & MA_OPEN_MODE_READ) != 0) { + *pShareMode |= FILE_SHARE_READ; + } + + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ + } else { + *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ + } +} + +static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + + (void)pVFS; + + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + + hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); + } + + *pFile = hFile; + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + + (void)pVFS; + + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + + hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); + } + + *pFile = hFile; + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) +{ + (void)pVFS; + + if (CloseHandle((HANDLE)file) == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + ma_result result = MA_SUCCESS; + size_t totalBytesRead; + + (void)pVFS; + + totalBytesRead = 0; + while (totalBytesRead < sizeInBytes) { + size_t bytesRemaining; + DWORD bytesToRead; + DWORD bytesRead; + BOOL readResult; + + bytesRemaining = sizeInBytes - totalBytesRead; + if (bytesRemaining >= 0xFFFFFFFF) { + bytesToRead = 0xFFFFFFFF; + } else { + bytesToRead = (DWORD)bytesRemaining; + } + + readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); + if (readResult == 1 && bytesRead == 0) { + result = MA_AT_END; + break; /* EOF */ + } + + totalBytesRead += bytesRead; + + if (bytesRead < bytesToRead) { + break; /* EOF */ + } + + if (readResult == 0) { + result = ma_result_from_GetLastError(GetLastError()); + break; + } + } + + if (pBytesRead != NULL) { + *pBytesRead = totalBytesRead; + } + + return result; +} + +static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + ma_result result = MA_SUCCESS; + size_t totalBytesWritten; + + (void)pVFS; + + totalBytesWritten = 0; + while (totalBytesWritten < sizeInBytes) { + size_t bytesRemaining; + DWORD bytesToWrite; + DWORD bytesWritten; + BOOL writeResult; + + bytesRemaining = sizeInBytes - totalBytesWritten; + if (bytesRemaining >= 0xFFFFFFFF) { + bytesToWrite = 0xFFFFFFFF; + } else { + bytesToWrite = (DWORD)bytesRemaining; + } + + writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); + totalBytesWritten += bytesWritten; + + if (writeResult == 0) { + result = ma_result_from_GetLastError(GetLastError()); + break; + } + } + + if (pBytesWritten != NULL) { + *pBytesWritten = totalBytesWritten; + } + + return result; +} + + +static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + LARGE_INTEGER liDistanceToMove; + DWORD dwMoveMethod; + BOOL result; + + (void)pVFS; + + liDistanceToMove.QuadPart = offset; + + /* */ if (origin == ma_seek_origin_current) { + dwMoveMethod = FILE_CURRENT; + } else if (origin == ma_seek_origin_end) { + dwMoveMethod = FILE_END; + } else { + dwMoveMethod = FILE_BEGIN; + } + + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); + } else if (ma_SetFilePointer != NULL) { + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); + } else { + return MA_NOT_IMPLEMENTED; + } + + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + LARGE_INTEGER liZero; + LARGE_INTEGER liTell; + BOOL result; + + (void)pVFS; + + liZero.QuadPart = 0; + + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); + } else if (ma_SetFilePointer != NULL) { + LONG tell; + + result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; + } else { + return MA_NOT_IMPLEMENTED; + } + + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + if (pCursor != NULL) { + *pCursor = liTell.QuadPart; + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + BY_HANDLE_FILE_INFORMATION fi; + BOOL result; + + (void)pVFS; + + result = GetFileInformationByHandle((HANDLE)file, &fi); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); + + return MA_SUCCESS; +} +#else +static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_result result; + FILE* pFileStd; + const char* pOpenModeStr; + + MA_ASSERT(pFilePath != NULL); + MA_ASSERT(openMode != 0); + MA_ASSERT(pFile != NULL); + + (void)pVFS; + + if ((openMode & MA_OPEN_MODE_READ) != 0) { + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + pOpenModeStr = "r+"; + } else { + pOpenModeStr = "rb"; + } + } else { + pOpenModeStr = "wb"; + } + + result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); + if (result != MA_SUCCESS) { + return result; + } + + *pFile = pFileStd; + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_result result; + FILE* pFileStd; + const wchar_t* pOpenModeStr; + + MA_ASSERT(pFilePath != NULL); + MA_ASSERT(openMode != 0); + MA_ASSERT(pFile != NULL); + + (void)pVFS; + + if ((openMode & MA_OPEN_MODE_READ) != 0) { + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + pOpenModeStr = L"r+"; + } else { + pOpenModeStr = L"rb"; + } + } else { + pOpenModeStr = L"wb"; + } + + result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); + if (result != MA_SUCCESS) { + return result; + } + + *pFile = pFileStd; + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) +{ + MA_ASSERT(file != NULL); + + (void)pVFS; + + fclose((FILE*)file); + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + size_t result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pDst != NULL); + + (void)pVFS; + + result = fread(pDst, 1, sizeInBytes, (FILE*)file); + + if (pBytesRead != NULL) { + *pBytesRead = result; + } + + if (result != sizeInBytes) { + if (result == 0 && feof((FILE*)file)) { + return MA_AT_END; + } else { + return ma_result_from_errno(ferror((FILE*)file)); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + size_t result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pSrc != NULL); + + (void)pVFS; + + result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); + + if (pBytesWritten != NULL) { + *pBytesWritten = result; + } + + if (result != sizeInBytes) { + return ma_result_from_errno(ferror((FILE*)file)); + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + int result; + int whence; + + MA_ASSERT(file != NULL); + + (void)pVFS; + + if (origin == ma_seek_origin_start) { + whence = SEEK_SET; + } else if (origin == ma_seek_origin_end) { + whence = SEEK_END; + } else { + whence = SEEK_CUR; + } + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _fseeki64((FILE*)file, offset, whence); + #else + /* No _fseeki64() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = fseek((FILE*)file, (int)offset, whence); + #endif +#else + result = fseek((FILE*)file, (long int)offset, whence); +#endif + if (result != 0) { + return MA_ERROR; + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + ma_int64 result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pCursor != NULL); + + (void)pVFS; + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64((FILE*)file); + #else + result = ftell((FILE*)file); + #endif +#else + result = ftell((FILE*)file); +#endif + + *pCursor = result; + + return MA_SUCCESS; +} + +#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) +int fileno(FILE *stream); +#endif + +static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + int fd; + struct stat info; + + MA_ASSERT(file != NULL); + MA_ASSERT(pInfo != NULL); + + (void)pVFS; + +#if defined(_MSC_VER) + fd = _fileno((FILE*)file); +#else + fd = fileno((FILE*)file); +#endif + + if (fstat(fd, &info) != 0) { + return ma_result_from_errno(errno); + } + + pInfo->sizeInBytes = info.st_size; + + return MA_SUCCESS; +} +#endif + + +static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); +#else + return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); +#endif +} + +static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); +#else + return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); +#endif +} + +static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) +{ + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_close__win32(pVFS, file); +#else + return ma_default_vfs_close__stdio(pVFS, file); +#endif +} + +static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + if (file == NULL || pDst == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); +#else + return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); +#endif +} + +static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + if (pBytesWritten != NULL) { + *pBytesWritten = 0; + } + + if (file == NULL || pSrc == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +#else + return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +#endif +} + +static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_seek__win32(pVFS, file, offset, origin); +#else + return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); +#endif +} + +static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_tell__win32(pVFS, file, pCursor); +#else + return ma_default_vfs_tell__stdio(pVFS, file, pCursor); +#endif +} + +static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + if (pInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pInfo); + + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_info__win32(pVFS, file, pInfo); +#else + return ma_default_vfs_info__stdio(pVFS, file, pInfo); +#endif +} + + +MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pVFS == NULL) { + return MA_INVALID_ARGS; + } + + pVFS->cb.onOpen = ma_default_vfs_open; + pVFS->cb.onOpenW = ma_default_vfs_open_w; + pVFS->cb.onClose = ma_default_vfs_close; + pVFS->cb.onRead = ma_default_vfs_read; + pVFS->cb.onWrite = ma_default_vfs_write; + pVFS->cb.onSeek = ma_default_vfs_seek; + pVFS->cb.onTell = ma_default_vfs_tell; + pVFS->cb.onInfo = ma_default_vfs_info; + ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pVFS != NULL) { + return ma_vfs_open(pVFS, pFilePath, openMode, pFile); + } else { + return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); + } +} + +MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pVFS != NULL) { + return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); + } else { + return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); + } +} + +MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) +{ + if (pVFS != NULL) { + return ma_vfs_close(pVFS, file); + } else { + return ma_default_vfs_close(pVFS, file); + } +} + +MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + if (pVFS != NULL) { + return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); + } else { + return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); + } +} + +MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + if (pVFS != NULL) { + return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); + } else { + return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); + } +} + +MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + if (pVFS != NULL) { + return ma_vfs_seek(pVFS, file, offset, origin); + } else { + return ma_default_vfs_seek(pVFS, file, offset, origin); + } +} + +MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + if (pVFS != NULL) { + return ma_vfs_tell(pVFS, file, pCursor); + } else { + return ma_default_vfs_tell(pVFS, file, pCursor); + } +} + +MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + if (pVFS != NULL) { + return ma_vfs_info(pVFS, file, pInfo); + } else { + return ma_default_vfs_info(pVFS, file, pInfo); + } +} + + + +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_vfs_file file; + ma_file_info info; + void* pData; + size_t bytesRead; + + if (ppData != NULL) { + *ppData = NULL; + } + if (pSize != NULL) { + *pSize = 0; + } + + if (ppData == NULL) { + return MA_INVALID_ARGS; + } + + if (pFilePath != NULL) { + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + } else { + result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); + } + if (result != MA_SUCCESS) { + return result; + } + + result = ma_vfs_or_default_info(pVFS, file, &info); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + if (info.sizeInBytes > MA_SIZE_MAX) { + ma_vfs_or_default_close(pVFS, file); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + if (pData == NULL) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ + ma_vfs_or_default_close(pVFS, file); + + if (result != MA_SUCCESS) { + ma_free(pData, pAllocationCallbacks); + return result; + } + + if (pSize != NULL) { + *pSize = bytesRead; + } + + MA_ASSERT(ppData != NULL); + *ppData = pData; + + return MA_SUCCESS; +} + +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); +} + +MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); +} + + + +/************************************************************************************************************************************************************** + +Decoding and Encoding Headers. These are auto-generated from a tool. + +**************************************************************************************************************************************************************/ +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) +/* dr_wav_h begin */ +#ifndef ma_dr_wav_h +#define ma_dr_wav_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_WAV_STRINGIFY(x) #x +#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) +#define MA_DR_WAV_VERSION_MAJOR 0 +#define MA_DR_WAV_VERSION_MINOR 13 +#define MA_DR_WAV_VERSION_REVISION 13 +#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) +#include +#define MA_DR_WAVE_FORMAT_PCM 0x1 +#define MA_DR_WAVE_FORMAT_ADPCM 0x2 +#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define MA_DR_WAVE_FORMAT_ALAW 0x6 +#define MA_DR_WAVE_FORMAT_MULAW 0x7 +#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define MA_DR_WAV_SEQUENTIAL 0x00000001 +#define MA_DR_WAV_WITH_METADATA 0x00000002 +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_wav_version_string(void); +typedef enum +{ + ma_dr_wav_seek_origin_start, + ma_dr_wav_seek_origin_current +} ma_dr_wav_seek_origin; +typedef enum +{ + ma_dr_wav_container_riff, + ma_dr_wav_container_rifx, + ma_dr_wav_container_w64, + ma_dr_wav_container_rf64, + ma_dr_wav_container_aiff +} ma_dr_wav_container; +typedef struct +{ + union + { + ma_uint8 fourcc[4]; + ma_uint8 guid[16]; + } id; + ma_uint64 sizeInBytes; + unsigned int paddingSize; +} ma_dr_wav_chunk_header; +typedef struct +{ + ma_uint16 formatTag; + ma_uint16 channels; + ma_uint32 sampleRate; + ma_uint32 avgBytesPerSec; + ma_uint16 blockAlign; + ma_uint16 bitsPerSample; + ma_uint16 extendedSize; + ma_uint16 validBitsPerSample; + ma_uint32 channelMask; + ma_uint8 subFormat[16]; +} ma_dr_wav_fmt; +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); +typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); +typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); +typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); +typedef struct +{ + const ma_uint8* data; + size_t dataSize; + size_t currentReadPos; +} ma_dr_wav__memory_stream; +typedef struct +{ + void** ppData; + size_t* pDataSize; + size_t dataSize; + size_t dataCapacity; + size_t currentWritePos; +} ma_dr_wav__memory_stream_write; +typedef struct +{ + ma_dr_wav_container container; + ma_uint32 format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 bitsPerSample; +} ma_dr_wav_data_format; +typedef enum +{ + ma_dr_wav_metadata_type_none = 0, + ma_dr_wav_metadata_type_unknown = 1 << 0, + ma_dr_wav_metadata_type_smpl = 1 << 1, + ma_dr_wav_metadata_type_inst = 1 << 2, + ma_dr_wav_metadata_type_cue = 1 << 3, + ma_dr_wav_metadata_type_acid = 1 << 4, + ma_dr_wav_metadata_type_bext = 1 << 5, + ma_dr_wav_metadata_type_list_label = 1 << 6, + ma_dr_wav_metadata_type_list_note = 1 << 7, + ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, + ma_dr_wav_metadata_type_list_info_software = 1 << 9, + ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, + ma_dr_wav_metadata_type_list_info_title = 1 << 11, + ma_dr_wav_metadata_type_list_info_artist = 1 << 12, + ma_dr_wav_metadata_type_list_info_comment = 1 << 13, + ma_dr_wav_metadata_type_list_info_date = 1 << 14, + ma_dr_wav_metadata_type_list_info_genre = 1 << 15, + ma_dr_wav_metadata_type_list_info_album = 1 << 16, + ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, + ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software + | ma_dr_wav_metadata_type_list_info_copyright + | ma_dr_wav_metadata_type_list_info_title + | ma_dr_wav_metadata_type_list_info_artist + | ma_dr_wav_metadata_type_list_info_comment + | ma_dr_wav_metadata_type_list_info_date + | ma_dr_wav_metadata_type_list_info_genre + | ma_dr_wav_metadata_type_list_info_album + | ma_dr_wav_metadata_type_list_info_tracknumber, + ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label + | ma_dr_wav_metadata_type_list_note + | ma_dr_wav_metadata_type_list_labelled_cue_region, + ma_dr_wav_metadata_type_all = -2, + ma_dr_wav_metadata_type_all_including_unknown = -1 +} ma_dr_wav_metadata_type; +typedef enum +{ + ma_dr_wav_smpl_loop_type_forward = 0, + ma_dr_wav_smpl_loop_type_pingpong = 1, + ma_dr_wav_smpl_loop_type_backward = 2 +} ma_dr_wav_smpl_loop_type; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 type; + ma_uint32 firstSampleByteOffset; + ma_uint32 lastSampleByteOffset; + ma_uint32 sampleFraction; + ma_uint32 playCount; +} ma_dr_wav_smpl_loop; +typedef struct +{ + ma_uint32 manufacturerId; + ma_uint32 productId; + ma_uint32 samplePeriodNanoseconds; + ma_uint32 midiUnityNote; + ma_uint32 midiPitchFraction; + ma_uint32 smpteFormat; + ma_uint32 smpteOffset; + ma_uint32 sampleLoopCount; + ma_uint32 samplerSpecificDataSizeInBytes; + ma_dr_wav_smpl_loop* pLoops; + ma_uint8* pSamplerSpecificData; +} ma_dr_wav_smpl; +typedef struct +{ + ma_int8 midiUnityNote; + ma_int8 fineTuneCents; + ma_int8 gainDecibels; + ma_int8 lowNote; + ma_int8 highNote; + ma_int8 lowVelocity; + ma_int8 highVelocity; +} ma_dr_wav_inst; +typedef struct +{ + ma_uint32 id; + ma_uint32 playOrderPosition; + ma_uint8 dataChunkId[4]; + ma_uint32 chunkStart; + ma_uint32 blockStart; + ma_uint32 sampleByteOffset; +} ma_dr_wav_cue_point; +typedef struct +{ + ma_uint32 cuePointCount; + ma_dr_wav_cue_point *pCuePoints; +} ma_dr_wav_cue; +typedef enum +{ + ma_dr_wav_acid_flag_one_shot = 1, + ma_dr_wav_acid_flag_root_note_set = 2, + ma_dr_wav_acid_flag_stretch = 4, + ma_dr_wav_acid_flag_disk_based = 8, + ma_dr_wav_acid_flag_acidizer = 16 +} ma_dr_wav_acid_flag; +typedef struct +{ + ma_uint32 flags; + ma_uint16 midiUnityNote; + ma_uint16 reserved1; + float reserved2; + ma_uint32 numBeats; + ma_uint16 meterDenominator; + ma_uint16 meterNumerator; + float tempo; +} ma_dr_wav_acid; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_label_or_note; +typedef struct +{ + char* pDescription; + char* pOriginatorName; + char* pOriginatorReference; + char pOriginationDate[10]; + char pOriginationTime[8]; + ma_uint64 timeReference; + ma_uint16 version; + char* pCodingHistory; + ma_uint32 codingHistorySize; + ma_uint8* pUMID; + ma_uint16 loudnessValue; + ma_uint16 loudnessRange; + ma_uint16 maxTruePeakLevel; + ma_uint16 maxMomentaryLoudness; + ma_uint16 maxShortTermLoudness; +} ma_dr_wav_bext; +typedef struct +{ + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_info_text; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 sampleLength; + ma_uint8 purposeId[4]; + ma_uint16 country; + ma_uint16 language; + ma_uint16 dialect; + ma_uint16 codePage; + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_labelled_cue_region; +typedef enum +{ + ma_dr_wav_metadata_location_invalid, + ma_dr_wav_metadata_location_top_level, + ma_dr_wav_metadata_location_inside_info_list, + ma_dr_wav_metadata_location_inside_adtl_list +} ma_dr_wav_metadata_location; +typedef struct +{ + ma_uint8 id[4]; + ma_dr_wav_metadata_location chunkLocation; + ma_uint32 dataSizeInBytes; + ma_uint8* pData; +} ma_dr_wav_unknown_metadata; +typedef struct +{ + ma_dr_wav_metadata_type type; + union + { + ma_dr_wav_cue cue; + ma_dr_wav_smpl smpl; + ma_dr_wav_acid acid; + ma_dr_wav_inst inst; + ma_dr_wav_bext bext; + ma_dr_wav_list_label_or_note labelOrNote; + ma_dr_wav_list_labelled_cue_region labelledCueRegion; + ma_dr_wav_list_info_text infoText; + ma_dr_wav_unknown_metadata unknown; + } data; +} ma_dr_wav_metadata; +typedef struct +{ + ma_dr_wav_read_proc onRead; + ma_dr_wav_write_proc onWrite; + ma_dr_wav_seek_proc onSeek; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav_container container; + ma_dr_wav_fmt fmt; + ma_uint32 sampleRate; + ma_uint16 channels; + ma_uint16 bitsPerSample; + ma_uint16 translatedFormatTag; + ma_uint64 totalPCMFrameCount; + ma_uint64 dataChunkDataSize; + ma_uint64 dataChunkDataPos; + ma_uint64 bytesRemaining; + ma_uint64 readCursorInPCMFrames; + ma_uint64 dataChunkDataSizeTargetWrite; + ma_bool32 isSequentialWrite; + ma_dr_wav_metadata* pMetadata; + ma_uint32 metadataCount; + ma_dr_wav__memory_stream memoryStream; + ma_dr_wav__memory_stream_write memoryStreamWrite; + struct + { + ma_uint32 bytesRemainingInBlock; + ma_uint16 predictor[2]; + ma_int32 delta[2]; + ma_int32 cachedFrames[4]; + ma_uint32 cachedFrameCount; + ma_int32 prevFrames[2][2]; + } msadpcm; + struct + { + ma_uint32 bytesRemainingInBlock; + ma_int32 predictor[2]; + ma_int32 stepIndex[2]; + ma_int32 cachedFrames[16]; + ma_uint32 cachedFrameCount; + } ima; + struct + { + ma_bool8 isLE; + ma_bool8 isUnsigned; + } aiff; +} ma_dr_wav; +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +#endif +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); +#ifdef __cplusplus +} +#endif +#endif +/* dr_wav_h end */ +#endif /* MA_NO_WAV */ + +#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) +/* dr_flac_h begin */ +#ifndef ma_dr_flac_h +#define ma_dr_flac_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_FLAC_STRINGIFY(x) #x +#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) +#define MA_DR_FLAC_VERSION_MAJOR 0 +#define MA_DR_FLAC_VERSION_MINOR 12 +#define MA_DR_FLAC_VERSION_REVISION 42 +#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) +#include +#if defined(_MSC_VER) && _MSC_VER >= 1700 + #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) +#elif (defined(__GNUC__) && __GNUC__ >= 4) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) +#elif defined(__has_feature) + #if __has_feature(attribute_deprecated) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) + #else + #define MA_DR_FLAC_DEPRECATED + #endif +#else + #define MA_DR_FLAC_DEPRECATED +#endif +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_flac_version_string(void); +#ifndef MA_DR_FLAC_BUFFER_SIZE +#define MA_DR_FLAC_BUFFER_SIZE 4096 +#endif +#ifdef MA_64BIT +typedef ma_uint64 ma_dr_flac_cache_t; +#else +typedef ma_uint32 ma_dr_flac_cache_t; +#endif +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 +#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 +#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 +#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 +#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 +#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 +#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 +#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 +#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 +typedef enum +{ + ma_dr_flac_container_native, + ma_dr_flac_container_ogg, + ma_dr_flac_container_unknown +} ma_dr_flac_container; +typedef enum +{ + ma_dr_flac_seek_origin_start, + ma_dr_flac_seek_origin_current +} ma_dr_flac_seek_origin; +typedef struct +{ + ma_uint64 firstPCMFrame; + ma_uint64 flacFrameOffset; + ma_uint16 pcmFrameCount; +} ma_dr_flac_seekpoint; +typedef struct +{ + ma_uint16 minBlockSizeInPCMFrames; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint32 minFrameSizeInPCMFrames; + ma_uint32 maxFrameSizeInPCMFrames; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint8 md5[16]; +} ma_dr_flac_streaminfo; +typedef struct +{ + ma_uint32 type; + const void* pRawData; + ma_uint32 rawDataSize; + union + { + ma_dr_flac_streaminfo streaminfo; + struct + { + int unused; + } padding; + struct + { + ma_uint32 id; + const void* pData; + ma_uint32 dataSize; + } application; + struct + { + ma_uint32 seekpointCount; + const ma_dr_flac_seekpoint* pSeekpoints; + } seektable; + struct + { + ma_uint32 vendorLength; + const char* vendor; + ma_uint32 commentCount; + const void* pComments; + } vorbis_comment; + struct + { + char catalog[128]; + ma_uint64 leadInSampleCount; + ma_bool32 isCD; + ma_uint8 trackCount; + const void* pTrackData; + } cuesheet; + struct + { + ma_uint32 type; + ma_uint32 mimeLength; + const char* mime; + ma_uint32 descriptionLength; + const char* description; + ma_uint32 width; + ma_uint32 height; + ma_uint32 colorDepth; + ma_uint32 indexColorCount; + ma_uint32 pictureDataSize; + const ma_uint8* pPictureData; + } picture; + } data; +} ma_dr_flac_metadata; +typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); +typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); +typedef struct +{ + const ma_uint8* data; + size_t dataSize; + size_t currentReadPos; +} ma_dr_flac__memory_stream; +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + void* pUserData; + size_t unalignedByteCount; + ma_dr_flac_cache_t unalignedCache; + ma_uint32 nextL2Line; + ma_uint32 consumedBits; + ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; + ma_dr_flac_cache_t cache; + ma_uint16 crc16; + ma_dr_flac_cache_t crc16Cache; + ma_uint32 crc16CacheIgnoredBytes; +} ma_dr_flac_bs; +typedef struct +{ + ma_uint8 subframeType; + ma_uint8 wastedBitsPerSample; + ma_uint8 lpcOrder; + ma_int32* pSamplesS32; +} ma_dr_flac_subframe; +typedef struct +{ + ma_uint64 pcmFrameNumber; + ma_uint32 flacFrameNumber; + ma_uint32 sampleRate; + ma_uint16 blockSizeInPCMFrames; + ma_uint8 channelAssignment; + ma_uint8 bitsPerSample; + ma_uint8 crc8; +} ma_dr_flac_frame_header; +typedef struct +{ + ma_dr_flac_frame_header header; + ma_uint32 pcmFramesRemaining; + ma_dr_flac_subframe subframes[8]; +} ma_dr_flac_frame; +typedef struct +{ + ma_dr_flac_meta_proc onMeta; + void* pUserDataMD; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 totalPCMFrameCount; + ma_dr_flac_container container; + ma_uint32 seekpointCount; + ma_dr_flac_frame currentFLACFrame; + ma_uint64 currentPCMFrame; + ma_uint64 firstFLACFramePosInBytes; + ma_dr_flac__memory_stream memoryStream; + ma_int32* pDecodedSamples; + ma_dr_flac_seekpoint* pSeekpoints; + void* _oggbs; + ma_bool32 _noSeekTableSeek : 1; + ma_bool32 _noBinarySearchSeek : 1; + ma_bool32 _noBruteForceSeek : 1; + ma_dr_flac_bs bs; + ma_uint8 pExtraData[1]; +} ma_dr_flac; +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +typedef struct +{ + ma_uint32 countRemaining; + const char* pRunningData; +} ma_dr_flac_vorbis_comment_iterator; +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); +typedef struct +{ + ma_uint32 countRemaining; + const char* pRunningData; +} ma_dr_flac_cuesheet_track_iterator; +typedef struct +{ + ma_uint64 offset; + ma_uint8 index; + ma_uint8 reserved[3]; +} ma_dr_flac_cuesheet_track_index; +typedef struct +{ + ma_uint64 offset; + ma_uint8 trackNumber; + char ISRC[12]; + ma_bool8 isAudio; + ma_bool8 preEmphasis; + ma_uint8 indexCount; + const ma_dr_flac_cuesheet_track_index* pIndexPoints; +} ma_dr_flac_cuesheet_track; +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); +#ifdef __cplusplus +} +#endif +#endif +/* dr_flac_h end */ +#endif /* MA_NO_FLAC */ + +#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) +/* dr_mp3_h begin */ +#ifndef ma_dr_mp3_h +#define ma_dr_mp3_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_MP3_STRINGIFY(x) #x +#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) +#define MA_DR_MP3_VERSION_MAJOR 0 +#define MA_DR_MP3_VERSION_MINOR 6 +#define MA_DR_MP3_VERSION_REVISION 38 +#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) +#include +#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_mp3_version_string(void); +typedef struct +{ + int frame_bytes, channels, hz, layer, bitrate_kbps; +} ma_dr_mp3dec_frame_info; +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + ma_uint8 header[4], reserv_buf[511]; +} ma_dr_mp3dec; +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); +typedef enum +{ + ma_dr_mp3_seek_origin_start, + ma_dr_mp3_seek_origin_current +} ma_dr_mp3_seek_origin; +typedef struct +{ + ma_uint64 seekPosInBytes; + ma_uint64 pcmFrameIndex; + ma_uint16 mp3FramesToDiscard; + ma_uint16 pcmFramesToDiscard; +} ma_dr_mp3_seek_point; +typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); +typedef struct +{ + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_dr_mp3_config; +typedef struct +{ + ma_dr_mp3dec decoder; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_dr_mp3_read_proc onRead; + ma_dr_mp3_seek_proc onSeek; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 mp3FrameChannels; + ma_uint32 mp3FrameSampleRate; + ma_uint32 pcmFramesConsumedInMP3Frame; + ma_uint32 pcmFramesRemainingInMP3Frame; + ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; + ma_uint64 currentPCMFrame; + ma_uint64 streamCursor; + ma_dr_mp3_seek_point* pSeekPoints; + ma_uint32 seekPointCount; + size_t dataSize; + size_t dataCapacity; + size_t dataConsumed; + ma_uint8* pData; + ma_bool32 atEnd : 1; + struct + { + const ma_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; +} ma_dr_mp3; +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +#ifdef __cplusplus +} +#endif +#endif +/* dr_mp3_h end */ +#endif /* MA_NO_MP3 */ + + +/************************************************************************************************************************************************************** + +Decoding + +**************************************************************************************************************************************************************/ +#ifndef MA_NO_DECODING + +static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + MA_ASSERT(pDecoder != NULL); + + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) +{ + MA_ASSERT(pDecoder != NULL); + + return pDecoder->onSeek(pDecoder, byteOffset, origin); +} + +static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + + if (pDecoder->onTell == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pDecoder->onTell(pDecoder, pCursor); +} + + +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) +{ + ma_decoding_backend_config config; + + MA_ZERO_OBJECT(&config); + config.preferredFormat = preferredFormat; + config.seekPointCount = seekPointCount; + + return config; +} + + +MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) +{ + ma_decoder_config config; + MA_ZERO_OBJECT(&config); + config.format = outputFormat; + config.channels = outputChannels; + config.sampleRate = outputSampleRate; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ + config.encodingFormat = ma_encoding_format_unknown; + + /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ + + return config; +} + +MA_API ma_decoder_config ma_decoder_config_init_default() +{ + return ma_decoder_config_init(ma_format_unknown, 0, 0); +} + +MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) +{ + ma_decoder_config config; + if (pConfig != NULL) { + config = *pConfig; + } else { + MA_ZERO_OBJECT(&config); + } + + return config; +} + +static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) +{ + ma_result result; + ma_data_converter_config converterConfig; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pConfig != NULL); + + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal data format. */ + } + + + /* Make sure we're not asking for too many channels. */ + if (pConfig->channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ + if (internalChannels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + + /* Output format. */ + if (pConfig->format == ma_format_unknown) { + pDecoder->outputFormat = internalFormat; + } else { + pDecoder->outputFormat = pConfig->format; + } + + if (pConfig->channels == 0) { + pDecoder->outputChannels = internalChannels; + } else { + pDecoder->outputChannels = pConfig->channels; + } + + if (pConfig->sampleRate == 0) { + pDecoder->outputSampleRate = internalSampleRate; + } else { + pDecoder->outputSampleRate = pConfig->sampleRate; + } + + converterConfig = ma_data_converter_config_init( + internalFormat, pDecoder->outputFormat, + internalChannels, pDecoder->outputChannels, + internalSampleRate, pDecoder->outputSampleRate + ); + converterConfig.pChannelMapIn = internalChannelMap; + converterConfig.pChannelMapOut = pConfig->pChannelMap; + converterConfig.channelMixMode = pConfig->channelMixMode; + converterConfig.ditherMode = pConfig->ditherMode; + converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ + converterConfig.resampling = pConfig->resampling; + + result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); + if (result != MA_SUCCESS) { + return result; + } + + /* + Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll + need this if the data converter does not support calculation of the required input frame count. To + determine support for this we'll just run a test. + */ + { + ma_uint64 unused; + + result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); + if (result != MA_SUCCESS) { + /* + We were unable to calculate the required input frame count which means we'll need to use + a heap-allocated cache. + */ + ma_uint64 inputCacheCapSizeInBytes; + + pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); + + /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ + inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ + if (pDecoder->pInputCache == NULL) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + } + } + + return MA_SUCCESS; +} + + + +static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_seek_bytes(pDecoder, offset, origin); +} + +static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_tell_bytes(pDecoder, pCursor); +} + + +static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInit == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFile == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFileW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitMemory == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + + + +static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } else { + /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ + result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; /* Failed to seek back to the start. */ + } + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + + +/* WAV */ +#ifdef ma_dr_wav_h +#define MA_HAS_WAV + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32, s16 or s32. */ +#if !defined(MA_NO_WAV) + ma_dr_wav dr; +#endif +} ma_wav; + +MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); +MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); + + +static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); +} + +static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); +} + +static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_wav_ds_vtable = +{ + ma_wav_ds_read, + ma_wav_ds_seek, + ma_wav_ds_get_data_format, + ma_wav_ds_get_cursor, + ma_wav_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_WAV) +static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_wav* pWav = (ma_wav*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pWav != NULL); + + result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_wav* pWav = (ma_wav*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pWav != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_wav_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWav); + pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { + pWav->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_wav_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pWav->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_wav_post_init(ma_wav* pWav) +{ + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case MA_DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case MA_DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->onTell = onTell; + pWav->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_WAV) + { + ma_dr_wav_uninit(&pWav->dr); + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pWav->ds); +} + +MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_s32: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); + } break; + + /* Fallback to a raw read. */ + case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ + default: + { + totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); + } break; + } + + /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) +{ + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); + if (wavResult != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pWav == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pWav->format; + } + + #if !defined(MA_NO_WAV) + { + if (pChannels != NULL) { + *pChannels = pWav->dr.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pWav->dr.sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_wav* pWav = (ma_wav*)pBackend; + + (void)pUserData; + + ma_wav_uninit(pWav, pAllocationCallbacks); + ma_free(pWav, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = +{ + ma_decoding_backend_init__wav, + ma_decoding_backend_init_file__wav, + ma_decoding_backend_init_file_w__wav, + ma_decoding_backend_init_memory__wav, + ma_decoding_backend_uninit__wav +}; + +static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_wav_h */ + +/* FLAC */ +#ifdef ma_dr_flac_h +#define MA_HAS_FLAC + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32, s16 or s32. */ +#if !defined(MA_NO_FLAC) + ma_dr_flac* dr; +#endif +} ma_flac; + +MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); +MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); +MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); + + +static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); +} + +static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); +} + +static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_flac_ds_vtable = +{ + ma_flac_ds_read, + ma_flac_ds_seek, + ma_flac_ds_get_data_format, + ma_flac_ds_get_cursor, + ma_flac_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_FLAC) +static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_flac* pFlac = (ma_flac*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pFlac != NULL); + + result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_flac* pFlac = (ma_flac*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pFlac != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_flac_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFlac); + pFlac->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { + pFlac->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_flac_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pFlac->onRead = onRead; + pFlac->onSeek = onSeek; + pFlac->onTell = onTell; + pFlac->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFlac == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_FLAC) + { + ma_dr_flac_close(pFlac->dr); + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pFlac->ds); +} + +MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_s32: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); + } break; + + case ma_format_u8: + case ma_format_s24: + case ma_format_unknown: + default: + { + return MA_INVALID_OPERATION; + }; + } + + /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) +{ + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + ma_bool32 flacResult; + + flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); + if (flacResult != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pFlac == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pFlac->format; + } + + #if !defined(MA_NO_FLAC) + { + if (pChannels != NULL) { + *pChannels = pFlac->dr->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pFlac->dr->sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + *pCursor = pFlac->dr->currentPCMFrame; + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + *pLength = pFlac->dr->totalPCMFrameCount; + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_flac* pFlac = (ma_flac*)pBackend; + + (void)pUserData; + + ma_flac_uninit(pFlac, pAllocationCallbacks); + ma_free(pFlac, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = +{ + ma_decoding_backend_init__flac, + ma_decoding_backend_init_file__flac, + ma_decoding_backend_init_file_w__flac, + ma_decoding_backend_init_memory__flac, + ma_decoding_backend_uninit__flac +}; + +static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_flac_h */ + +/* MP3 */ +#ifdef ma_dr_mp3_h +#define MA_HAS_MP3 + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32 or s16. */ +#if !defined(MA_NO_MP3) + ma_dr_mp3 dr; + ma_uint32 seekPointCount; + ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ +#endif +} ma_mp3; + +MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); +MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); + + +static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); +} + +static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); +} + +static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_mp3_ds_vtable = +{ + ma_mp3_ds_read, + ma_mp3_ds_seek, + ma_mp3_ds_get_data_format, + ma_mp3_ds_get_cursor, + ma_mp3_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_MP3) +static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_mp3* pMP3 = (ma_mp3*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pMP3 != NULL); + + result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +{ + ma_mp3* pMP3 = (ma_mp3*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pMP3 != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_mp3_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMP3); + pMP3->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { + pMP3->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 mp3Result; + ma_uint32 seekPointCount = 0; + ma_dr_mp3_seek_point* pSeekPoints = NULL; + + MA_ASSERT(pMP3 != NULL); + MA_ASSERT(pConfig != NULL); + + seekPointCount = pConfig->seekPointCount; + if (seekPointCount > 0) { + pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + if (pSeekPoints == NULL) { + return MA_OUT_OF_MEMORY; + } + } + + mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); + return MA_ERROR; + } + + mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); + return MA_ERROR; + } + + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + + return MA_SUCCESS; +} + +static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + + result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->onTell = onTell; + pMP3->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) { + return; + } + + #if !defined(MA_NO_MP3) + { + ma_dr_mp3_uninit(&pMP3->dr); + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ + ma_free(pMP3->pSeekPoints, pAllocationCallbacks); + + ma_data_source_uninit(&pMP3->ds); +} + +MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_u8: + case ma_format_s24: + case ma_format_s32: + case ma_format_unknown: + default: + { + return MA_INVALID_OPERATION; + }; + } + + /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) +{ + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); + if (mp3Result != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pMP3 == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pMP3->format; + } + + #if !defined(MA_NO_MP3) + { + if (pChannels != NULL) { + *pChannels = pMP3->dr.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pMP3->dr.sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); + } + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + *pCursor = pMP3->dr.currentPCMFrame; + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_mp3* pMP3 = (ma_mp3*)pBackend; + + (void)pUserData; + + ma_mp3_uninit(pMP3, pAllocationCallbacks); + ma_free(pMP3, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = +{ + ma_decoding_backend_init__mp3, + ma_decoding_backend_init_file__mp3, + ma_decoding_backend_init_file_w__mp3, + ma_decoding_backend_init_memory__mp3, + ma_decoding_backend_uninit__mp3 +}; + +static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_mp3_h */ + +/* Vorbis */ +#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define MA_HAS_VORBIS + +/* The size in bytes of each chunk of data to read from the Vorbis stream. */ +#define MA_VORBIS_DATA_CHUNK_SIZE 4096 + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ + ma_format format; /* Only f32 is allowed with stb_vorbis. */ + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; +#if !defined(MA_NO_VORBIS) + stb_vorbis* stb; + ma_bool32 usingPushMode; + struct + { + ma_uint8* pData; + size_t dataSize; + size_t dataCapacity; + size_t audioStartOffsetInBytes; + ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ + ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ + float** ppPacketData; + } push; +#endif +} ma_stbvorbis; + +MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); +MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); +MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); + + +static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); +} + +static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); +} + +static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = +{ + ma_stbvorbis_ds_read, + ma_stbvorbis_ds_seek, + ma_stbvorbis_ds_get_data_format, + ma_stbvorbis_ds_get_cursor, + ma_stbvorbis_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + (void)pConfig; + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pVorbis); + pVorbis->format = ma_format_f32; /* Only supporting f32. */ + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +#if !defined(MA_NO_VORBIS) +static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) +{ + stb_vorbis_info info; + + MA_ASSERT(pVorbis != NULL); + + info = stb_vorbis_get_info(pVorbis->stb); + + pVorbis->channels = info.channels; + pVorbis->sampleRate = info.sample_rate; + + return MA_SUCCESS; +} + +static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) +{ + ma_result result; + stb_vorbis* stb; + size_t dataSize = 0; + size_t dataCapacity = 0; + ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ + + for (;;) { + int vorbisError; + int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ + size_t bytesRead; + ma_uint8* pNewData; + + /* Allocate memory for the new chunk. */ + dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pData = pNewData; + + /* Read in the next chunk. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); + dataSize += bytesRead; + + if (result != MA_SUCCESS) { + ma_free(pData, &pVorbis->allocationCallbacks); + return result; + } + + /* We have a maximum of 31 bits with stb_vorbis. */ + if (dataSize > INT_MAX) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_TOO_BIG; + } + + stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); + if (stb != NULL) { + /* + Successfully opened the Vorbis decoder. We might have some leftover unprocessed + data so we'll need to move that down to the front. + */ + dataSize -= (size_t)consumedDataSize; /* Consume the data. */ + MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); + + /* + We need to track the start point so we can seek back to the start of the audio + data when seeking. + */ + pVorbis->push.audioStartOffsetInBytes = consumedDataSize; + + break; + } else { + /* Failed to open the decoder. */ + if (vorbisError == VORBIS_need_more_data) { + continue; + } else { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ + } + } + } + + MA_ASSERT(stb != NULL); + pVorbis->stb = stb; + pVorbis->push.pData = pData; + pVorbis->push.dataSize = dataSize; + pVorbis->push.dataCapacity = dataCapacity; + + return MA_SUCCESS; +} +#endif + +MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pVorbis->onRead = onRead; + pVorbis->onSeek = onSeek; + pVorbis->onTell = onTell; + pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; + ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); + + #if !defined(MA_NO_VORBIS) + { + /* + stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the + pushing API. In order for us to be able to successfully initialize the decoder we need to + supply it with enough data. We need to keep loading data until we have enough. + */ + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + pVorbis->usingPushMode = MA_TRUE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_VORBIS) + { + (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ + + /* We can use stb_vorbis' pull mode for file based streams. */ + pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); + if (pVorbis->stb == NULL) { + return MA_INVALID_FILE; + } + + pVorbis->usingPushMode = MA_FALSE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_VORBIS) + { + (void)pAllocationCallbacks; + + /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ + if (dataSize > INT_MAX) { + return MA_TOO_BIG; + } + + pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); + if (pVorbis->stb == NULL) { + return MA_INVALID_FILE; + } + + pVorbis->usingPushMode = MA_FALSE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pVorbis == NULL) { + return; + } + + #if !defined(MA_NO_VORBIS) + { + stb_vorbis_close(pVorbis->stb); + + /* We'll have to clear some memory if we're using push mode. */ + if (pVorbis->usingPushMode) { + ma_free(pVorbis->push.pData, pAllocationCallbacks); + } + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pVorbis->ds); +} + +MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + ma_uint32 channels; + + ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); + + if (format == ma_format_f32) { + /* We read differently depending on whether or not we're using push mode. */ + if (pVorbis->usingPushMode) { + /* Push mode. This is the complex case. */ + float* pFramesOutF32 = (float*)pFramesOut; + + while (totalFramesRead < frameCount) { + /* The first thing to do is read from any already-cached frames. */ + ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ + + /* The output pointer can be null in which case we just treate it as a seek. */ + if (pFramesOut != NULL) { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { + pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; + } + + pFramesOutF32 += pVorbis->channels; + } + } + + /* Update pointers and counters. */ + pVorbis->push.framesConsumed += framesToReadFromCache; + pVorbis->push.framesRemaining -= framesToReadFromCache; + totalFramesRead += framesToReadFromCache; + + /* Don't bother reading any more frames right now if we've just finished loading. */ + if (totalFramesRead == frameCount) { + break; + } + + MA_ASSERT(pVorbis->push.framesRemaining == 0); + + /* Getting here means we've run out of cached frames. We'll need to load some more. */ + for (;;) { + int samplesRead = 0; + int consumedDataSize; + + /* We need to case dataSize to an int, so make sure we can do it safely. */ + if (pVorbis->push.dataSize > INT_MAX) { + break; /* Too big. */ + } + + consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); + if (consumedDataSize != 0) { + /* Successfully decoded a Vorbis frame. Consume the data. */ + pVorbis->push.dataSize -= (size_t)consumedDataSize; + MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); + + pVorbis->push.framesConsumed = 0; + pVorbis->push.framesRemaining = samplesRead; + + break; + } else { + /* Not enough data. Read more. */ + size_t bytesRead; + + /* Expand the data buffer if necessary. */ + if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { + size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; + ma_uint8* pNewData; + + pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + result = MA_OUT_OF_MEMORY; + break; + } + + pVorbis->push.pData = pNewData; + pVorbis->push.dataCapacity = newCap; + } + + /* We should have enough room to load some data. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); + pVorbis->push.dataSize += bytesRead; + + if (result != MA_SUCCESS) { + break; /* Failed to read any data. Get out. */ + } + } + } + + /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ + if (result != MA_SUCCESS) { + break; + } + } + } else { + /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ + while (totalFramesRead < frameCount) { + ma_uint64 framesRemaining = (frameCount - totalFramesRead); + int framesRead; + + if (framesRemaining > INT_MAX) { + framesRemaining = INT_MAX; + } + + framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ + totalFramesRead += framesRead; + + if (framesRead < (int)framesRemaining) { + break; /* Nothing left to read. Get out. */ + } + } + } + } else { + result = MA_INVALID_ARGS; + } + + pVorbis->cursor += totalFramesRead; + + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) +{ + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + /* Different seeking methods depending on whether or not we're using push mode. */ + if (pVorbis->usingPushMode) { + /* Push mode. This is the complex case. */ + ma_result result; + float buffer[4096]; + + /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ + if (frameIndex < pVorbis->cursor) { + if (frameIndex > 0x7FFFFFFF) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } + + /* + This is wildly inefficient due to me having trouble getting sample exact seeking working + robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work + perfectly is to reinitialize the decoder. Note that we only enter this path when seeking + backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. + */ + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); + + MA_ZERO_OBJECT(&pVorbis->push); + + /* Seek to the start of the file. */ + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + /* At this point we should be sitting on the first frame. */ + pVorbis->cursor = 0; + } + + /* We're just brute-forcing this for now. */ + while (pVorbis->cursor < frameIndex) { + ma_uint64 framesRead; + ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; + if (framesToRead > (frameIndex - pVorbis->cursor)) { + framesToRead = (frameIndex - pVorbis->cursor); + } + + result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + } + } else { + /* Pull mode. This is the simple case. */ + int vorbisResult; + + if (frameIndex > UINT_MAX) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } + + vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ + if (vorbisResult == 0) { + return MA_ERROR; /* See failed. */ + } + + pVorbis->cursor = frameIndex; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pVorbis == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pVorbis->format; + } + + #if !defined(MA_NO_VORBIS) + { + if (pChannels != NULL) { + *pChannels = pVorbis->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pVorbis->sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + *pCursor = pVorbis->cursor; + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + if (pVorbis->usingPushMode) { + *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ + } else { + *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; + + (void)pUserData; + + ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); + ma_free(pVorbis, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = +{ + ma_decoding_backend_init__stbvorbis, + ma_decoding_backend_init_file__stbvorbis, + NULL, /* onInitFileW() */ + ma_decoding_backend_init_memory__stbvorbis, + ma_decoding_backend_uninit__stbvorbis +}; + +static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ + + + +static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + MA_ASSERT(pDecoder != NULL); + + if (pConfig != NULL) { + return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); + } else { + pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); + return MA_SUCCESS; + } +} + +static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); +} + +static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); +} + +static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_decoder_data_source_vtable = +{ + ma_decoder__data_source_on_read, + ma_decoder__data_source_on_seek, + ma_decoder__data_source_on_get_data_format, + ma_decoder__data_source_on_get_cursor, + ma_decoder__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + MA_ASSERT(pConfig != NULL); + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDecoder); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->onRead = onRead; + pDecoder->onSeek = onSeek; + pDecoder->onTell = onTell; + pDecoder->pUserData = pUserData; + + result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); + if (result != MA_SUCCESS) { + ma_data_source_uninit(&pDecoder->ds); + return result; + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__init_data_converter(pDecoder, pConfig); + + /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + return result; + } + + return result; +} + + +static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + /* Silence some warnings in the case that we don't have any decoder backends enabled. */ + (void)onRead; + (void)onSeek; + (void)pUserData; + + + /* If we've specified a specific encoding type, try that first. */ + if (pConfig->encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (pConfig->encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (pConfig->encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (pConfig->encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (pConfig->encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); + } + #endif + + /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (pConfig->encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder__postinit(pConfig, pDecoder); +} + +MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_decoder_config config; + ma_result result; + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); +} + + +static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + size_t bytesRemaining; + + MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); + + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesRemaining == 0) { + return MA_AT_END; + } + + if (bytesToRead > 0) { + MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); + pDecoder->data.memory.currentReadPos += bytesToRead; + } + + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) +{ + if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { + return MA_BAD_SEEK; + } + + if (origin == ma_seek_origin_current) { + if (byteOffset > 0) { + if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { + byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ + } + + pDecoder->data.memory.currentReadPos += (size_t)byteOffset; + } else { + if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ + } + + pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; + } + } else { + if (origin == ma_seek_origin_end) { + if (byteOffset < 0) { + byteOffset = -byteOffset; + } + + if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { + pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ + } else { + pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; + } + } else { + if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { + pDecoder->data.memory.currentReadPos = (size_t)byteOffset; + } else { + pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pCursor != NULL); + + *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + pDecoder->data.memory.pData = (const ma_uint8*)pData; + pDecoder->data.memory.dataSize = dataSize; + pDecoder->data.memory.currentReadPos = 0; + + (void)pConfig; + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* Use trial and error for stock decoders. */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ + result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + + +#if defined(MA_HAS_WAV) || \ + defined(MA_HAS_MP3) || \ + defined(MA_HAS_FLAC) || \ + defined(MA_HAS_VORBIS) || \ + defined(MA_HAS_OPUS) +#define MA_HAS_PATH_API +#endif + +#if defined(MA_HAS_PATH_API) +static const char* ma_path_file_name(const char* path) +{ + const char* fileName; + + if (path == NULL) { + return NULL; + } + + fileName = path; + + /* We just loop through the path until we find the last slash. */ + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + /* At this point the file name is sitting on a slash, so just move forward. */ + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + +static const wchar_t* ma_path_file_name_w(const wchar_t* path) +{ + const wchar_t* fileName; + + if (path == NULL) { + return NULL; + } + + fileName = path; + + /* We just loop through the path until we find the last slash. */ + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + /* At this point the file name is sitting on a slash, so just move forward. */ + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + + +static const char* ma_path_extension(const char* path) +{ + const char* extension; + const char* lastOccurance; + + if (path == NULL) { + path = ""; + } + + extension = ma_path_file_name(path); + lastOccurance = NULL; + + /* Just find the last '.' and return. */ + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + +static const wchar_t* ma_path_extension_w(const wchar_t* path) +{ + const wchar_t* extension; + const wchar_t* lastOccurance; + + if (path == NULL) { + path = L""; + } + + extension = ma_path_file_name_w(path); + lastOccurance = NULL; + + /* Just find the last '.' and return. */ + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + + +static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) +{ + const char* ext1; + const char* ext2; + + if (path == NULL || extension == NULL) { + return MA_FALSE; + } + + ext1 = extension; + ext2 = ma_path_extension(path); + +#if defined(_MSC_VER) || defined(__DMC__) + return _stricmp(ext1, ext2) == 0; +#else + return strcasecmp(ext1, ext2) == 0; +#endif +} + +static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) +{ + const wchar_t* ext1; + const wchar_t* ext2; + + if (path == NULL || extension == NULL) { + return MA_FALSE; + } + + ext1 = extension; + ext2 = ma_path_extension_w(path); + +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) + return _wcsicmp(ext1, ext2) == 0; +#else + /* + I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This + isn't the most efficient way to do it, but it should work OK. + */ + { + char ext1MB[4096]; + char ext2MB[4096]; + const wchar_t* pext1 = ext1; + const wchar_t* pext2 = ext2; + mbstate_t mbs1; + mbstate_t mbs2; + + MA_ZERO_OBJECT(&mbs1); + MA_ZERO_OBJECT(&mbs2); + + if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { + return MA_FALSE; + } + if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { + return MA_FALSE; + } + + return strcasecmp(ext1MB, ext2MB) == 0; + } +#endif +} +#endif /* MA_HAS_PATH_API */ + + + +static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pBufferOut != NULL); + + return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) +{ + MA_ASSERT(pDecoder != NULL); + + return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); +} + +static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + + return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); +} + +static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->data.vfs.pVFS = pVFS; + pDecoder->data.vfs.file = file; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(&config, pDecoder); + } + #endif + + /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ + if (result != MA_SUCCESS) { + result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); + } else { + result = ma_decoder__postinit(&config, pDecoder); + } + + if (result != MA_SUCCESS) { + if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ + ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); + } + + return result; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->data.vfs.pVFS = pVFS; + pDecoder->data.vfs.file = file; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(&config, pDecoder); + } + #endif + + /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ + if (result != MA_SUCCESS) { + result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); + } else { + result = ma_decoder__postinit(&config, pDecoder); + } + + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); + return result; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + } + + if (pDecoder->onRead == ma_decoder__on_read_vfs) { + ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); + pDecoder->data.vfs.file = NULL; + } + + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + ma_data_source_uninit(&pDecoder->ds); + + if (pDecoder->pInputCache != NULL) { + ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesReadOut; + void* pRunningFramesOut; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend == NULL) { + return MA_INVALID_OPERATION; + } + + /* Fast path. */ + if (pDecoder->converter.isPassthrough) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); + } else { + /* + Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we + need to run through each sample because we need to ensure it's internal cache is updated. + */ + if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); + } else { + /* Slow path. Need to run everything through the data converter. */ + ma_format internalFormat; + ma_uint32 internalChannels; + + totalFramesReadOut = 0; + pRunningFramesOut = pFramesOut; + + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal format and channel count. */ + } + + /* + We run a different path depending on whether or not we are using a heap-allocated + intermediary buffer or not. If the data converter does not support the calculation of + the required number of input frames, we'll use the heap-allocated path. Otherwise we'll + use the stack-allocated path. + */ + if (pDecoder->pInputCache != NULL) { + /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDecoder->inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { + framesToReadThisIterationIn = pDecoder->inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDecoder->inputCacheConsumed += framesToReadThisIterationIn; + pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ + if (pDecoder->inputCacheRemaining == 0) { + pDecoder->inputCacheConsumed = 0; + + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); + if (result != MA_SUCCESS) { + break; + } + } + } + } else { + /* We have a way of determining the required number of input frames so just use the stack. */ + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; + + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } + + ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } + + if (requiredInputFrameCount > 0) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); + } else { + framesReadThisIterationIn = 0; + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + } + } + } + + pDecoder->readPointerInPCMFrames += totalFramesReadOut; + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesReadOut; + } + + if (result == MA_SUCCESS && totalFramesReadOut == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + ma_result result; + ma_uint64 internalFrameIndex; + ma_uint32 internalSampleRate; + ma_uint64 currentFrameIndex; + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ + } + + if (internalSampleRate == pDecoder->outputSampleRate) { + internalFrameIndex = frameIndex; + } else { + internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); + } + + /* Only seek if we're requesting a different frame to what we're currently sitting on. */ + ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); + if (currentFrameIndex != internalFrameIndex) { + result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); + if (result == MA_SUCCESS) { + pDecoder->readPointerInPCMFrames = frameIndex; + } + + /* Reset the data converter so that any cached data in the resampler is cleared. */ + ma_data_converter_reset(&pDecoder->converter); + } + + return result; + } + + /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ + return MA_INVALID_ARGS; +} + +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pFormat != NULL) { + *pFormat = pDecoder->outputFormat; + } + + if (pChannels != NULL) { + *pChannels = pDecoder->outputChannels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pDecoder->outputSampleRate; + } + + if (pChannelMap != NULL) { + ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pDecoder->readPointerInPCMFrames; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + ma_result result; + ma_uint64 internalLengthInPCMFrames; + ma_uint32 internalSampleRate; + + result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal length. */ + } + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ + } + + if (internalSampleRate == pDecoder->outputSampleRate) { + *pLength = internalLengthInPCMFrames; + } else { + *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); + } + + return MA_SUCCESS; + } else { + return MA_NO_BACKEND; + } +} + +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) +{ + ma_result result; + ma_uint64 totalFrameCount; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; + } + + if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_result result; + ma_uint64 totalFrameCount; + ma_uint64 bpf; + ma_uint64 dataCapInFrames; + void* pPCMFramesOut; + + MA_ASSERT(pDecoder != NULL); + + totalFrameCount = 0; + bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + + /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ + dataCapInFrames = 0; + pPCMFramesOut = NULL; + for (;;) { + ma_uint64 frameCountToTryReading; + ma_uint64 framesJustRead; + + /* Make room if there's not enough. */ + if (totalFrameCount == dataCapInFrames) { + void* pNewPCMFramesOut; + ma_uint64 newDataCapInFrames = dataCapInFrames*2; + if (newDataCapInFrames == 0) { + newDataCapInFrames = 4096; + } + + if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + return MA_TOO_BIG; + } + + pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); + if (pNewPCMFramesOut == NULL) { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + dataCapInFrames = newDataCapInFrames; + pPCMFramesOut = pNewPCMFramesOut; + } + + frameCountToTryReading = dataCapInFrames - totalFrameCount; + MA_ASSERT(frameCountToTryReading > 0); + + result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); + totalFrameCount += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + + if (framesJustRead < frameCountToTryReading) { + break; + } + } + + + if (pConfigOut != NULL) { + pConfigOut->format = pDecoder->outputFormat; + pConfigOut->channels = pDecoder->outputChannels; + pConfigOut->sampleRate = pDecoder->outputSampleRate; + } + + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = pPCMFramesOut; + } else { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + } + + if (pFrameCountOut != NULL) { + *pFrameCountOut = totalFrameCount; + } + + ma_decoder_uninit(pDecoder); + return MA_SUCCESS; +} + +MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_result result; + ma_decoder_config config; + ma_decoder decoder; + + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = NULL; + } + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); + + return result; +} + +MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); +} + +MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_decoder_config config; + ma_decoder decoder; + ma_result result; + + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = NULL; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); +} +#endif /* MA_NO_DECODING */ + + +#ifndef MA_NO_ENCODING + +#if defined(MA_HAS_WAV) +static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) +{ + ma_encoder* pEncoder = (ma_encoder*)pUserData; + size_t bytesWritten = 0; + + MA_ASSERT(pEncoder != NULL); + + pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); + return bytesWritten; +} + +static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_encoder* pEncoder = (ma_encoder*)pUserData; + ma_result result; + + MA_ASSERT(pEncoder != NULL); + + result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + if (result != MA_SUCCESS) { + return MA_FALSE; + } else { + return MA_TRUE; + } +} + +static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) +{ + ma_dr_wav_data_format wavFormat; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav* pWav; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + wavFormat.container = ma_dr_wav_container_riff; + wavFormat.channels = pEncoder->config.channels; + wavFormat.sampleRate = pEncoder->config.sampleRate; + wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; + if (pEncoder->config.format == ma_format_f32) { + wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else { + wavFormat.format = MA_DR_WAVE_FORMAT_PCM; + } + + allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; + allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; + allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; + allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; + + if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { + return MA_ERROR; + } + + pEncoder->pInternalEncoder = pWav; + + return MA_SUCCESS; +} + +static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) +{ + ma_dr_wav* pWav; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + MA_ASSERT(pWav != NULL); + + ma_dr_wav_uninit(pWav); + ma_free(pWav, &pEncoder->config.allocationCallbacks); +} + +static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) +{ + ma_dr_wav* pWav; + ma_uint64 framesWritten; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + MA_ASSERT(pWav != NULL); + + framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); + + if (pFramesWritten != NULL) { + *pFramesWritten = framesWritten; + } + + return MA_SUCCESS; +} +#endif + +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_encoder_config config; + + MA_ZERO_OBJECT(&config); + config.encodingFormat = encodingFormat; + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + +MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + + if (pEncoder == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEncoder); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { + return MA_INVALID_ARGS; + } + + pEncoder->config = *pConfig; + + result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) +{ + ma_result result = MA_SUCCESS; + + /* This assumes ma_encoder_preinit() has been called prior. */ + MA_ASSERT(pEncoder != NULL); + + if (onWrite == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; + } + + pEncoder->onWrite = onWrite; + pEncoder->onSeek = onSeek; + pEncoder->pUserData = pUserData; + + switch (pEncoder->config.encodingFormat) + { + case ma_encoding_format_wav: + { + #if defined(MA_HAS_WAV) + pEncoder->onInit = ma_encoder__on_init_wav; + pEncoder->onUninit = ma_encoder__on_uninit_wav; + pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; + #else + result = MA_NO_BACKEND; + #endif + } break; + + default: + { + result = MA_INVALID_ARGS; + } break; + } + + /* Getting here means we should have our backend callbacks set up. */ + if (result == MA_SUCCESS) { + result = pEncoder->onInit(pEncoder); + } + + return result; +} + +static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) +{ + return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); +} + +static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) +{ + return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); +} + +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); +} + +MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); +} + +MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); +} + + +MA_API void ma_encoder_uninit(ma_encoder* pEncoder) +{ + if (pEncoder == NULL) { + return; + } + + if (pEncoder->onUninit) { + pEncoder->onUninit(pEncoder); + } + + /* If we have a file handle, close it. */ + if (pEncoder->onWrite == ma_encoder__on_write_vfs) { + ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); + pEncoder->data.vfs.file = NULL; + } +} + + +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) +{ + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + if (pEncoder == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); +} +#endif /* MA_NO_ENCODING */ + + + +/************************************************************************************************************************************************************** + +Generation + +**************************************************************************************************************************************************************/ +#ifndef MA_NO_GENERATION +MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) +{ + ma_waveform_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.type = type; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); +} + +static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_waveform* pWaveform = (ma_waveform*)pDataSource; + + *pFormat = pWaveform->config.format; + *pChannels = pWaveform->config.channels; + *pSampleRate = pWaveform->config.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); + + return MA_SUCCESS; +} + +static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_waveform* pWaveform = (ma_waveform*)pDataSource; + + *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); + + return MA_SUCCESS; +} + +static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) +{ + return (1.0 / (sampleRate / frequency)); +} + +static void ma_waveform__update_advance(ma_waveform* pWaveform) +{ + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); +} + +static ma_data_source_vtable g_ma_waveform_data_source_vtable = +{ + ma_waveform__data_source_on_read, + ma_waveform__data_source_on_seek, + ma_waveform__data_source_on_get_data_format, + ma_waveform__data_source_on_get_cursor, + NULL, /* onGetLength. There's no notion of a length in waveforms. */ + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); + if (result != MA_SUCCESS) { + return result; + } + + pWaveform->config = *pConfig; + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); + pWaveform->time = 0; + + return MA_SUCCESS; +} + +MA_API void ma_waveform_uninit(ma_waveform* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_data_source_uninit(&pWaveform->ds); +} + +MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform__update_advance(pWaveform); + + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.type = type; + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform__update_advance(pWaveform); + + return MA_SUCCESS; +} + +static float ma_waveform_sine_f32(double time, double amplitude) +{ + return (float)(ma_sind(MA_TAU_D * time) * amplitude); +} + +static ma_int16 ma_waveform_sine_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); +} + +static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + if (f < dutyCycle) { + r = amplitude; + } else { + r = -amplitude; + } + + return (float)r; +} + +static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); +} + +static float ma_waveform_triangle_f32(double time, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + r = 2 * ma_abs(2 * (f - 0.5)) - 1; + + return (float)(r * amplitude); +} + +static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); +} + +static float ma_waveform_sawtooth_f32(double time, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + r = 2 * (f - 0.5); + + return (float)(r * amplitude); +} + +static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); +} + +static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + switch (pWaveform->config.type) + { + case ma_waveform_type_sine: + { + ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); + } break; + + case ma_waveform_type_square: + { + ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); + } break; + + case ma_waveform_type_triangle: + { + ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); + } break; + + case ma_waveform_type_sawtooth: + { + ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); + } break; + + default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ + } + } else { + pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ + + return MA_SUCCESS; +} + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) +{ + ma_pulsewave_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.dutyCycle = dutyCycle; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) +{ + ma_result result; + ma_waveform_config config; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + config = ma_waveform_config_init( + pConfig->format, + pConfig->channels, + pConfig->sampleRate, + ma_waveform_type_square, + pConfig->amplitude, + pConfig->frequency + ); + + result = ma_waveform_init(&config, &pWaveform->waveform); + ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); + + return result; +} + +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_waveform_uninit(&pWaveform->waveform); +} + +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); + } else { + pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform_set_frequency(&pWaveform->waveform, frequency); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.dutyCycle = dutyCycle; + + return MA_SUCCESS; +} + + + +MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) +{ + ma_noise_config config; + MA_ZERO_OBJECT(&config); + + config.format = format; + config.channels = channels; + config.type = type; + config.seed = seed; + config.amplitude = amplitude; + + if (config.seed == 0) { + config.seed = MA_DEFAULT_LCG_SEED; + } + + return config; +} + + +static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + /* No-op. Just pretend to be successful. */ + (void)pDataSource; + (void)frameIndex; + return MA_SUCCESS; +} + +static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_noise* pNoise = (ma_noise*)pDataSource; + + *pFormat = pNoise->config.format; + *pChannels = pNoise->config.channels; + *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_noise_data_source_vtable = +{ + ma_noise__data_source_on_read, + ma_noise__data_source_on_seek, /* No-op for noise. */ + ma_noise__data_source_on_get_data_format, + NULL, /* onGetCursor. No notion of a cursor for noise. */ + NULL, /* onGetLength. No notion of a length for noise. */ + NULL, /* onSetLooping */ + 0 +}; + + +#ifndef MA_PINK_NOISE_BIN_SIZE +#define MA_PINK_NOISE_BIN_SIZE 16 +#endif + +typedef struct +{ + size_t sizeInBytes; + struct + { + size_t binOffset; + size_t accumulationOffset; + size_t counterOffset; + } pink; + struct + { + size_t accumulationOffset; + } brownian; +} ma_noise_heap_layout; + +static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Pink. */ + if (pConfig->type == ma_noise_type_pink) { + /* bin */ + pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; + pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; + + /* accumulation */ + pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + + /* counter */ + pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; + } + + /* Brownian. */ + if (pConfig->type == ma_noise_type_brownian) { + /* accumulation */ + pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_noise_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) +{ + ma_result result; + ma_noise_heap_layout heapLayout; + ma_data_source_config dataSourceConfig; + ma_uint32 iChannel; + + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNoise); + + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pNoise->_pHeap = pHeap; + MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); + if (result != MA_SUCCESS) { + return result; + } + + pNoise->config = *pConfig; + ma_lcg_seed(&pNoise->lcg, pConfig->seed); + + if (pNoise->config.type == ma_noise_type_pink) { + pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); + pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); + pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); + pNoise->state.pink.accumulation[iChannel] = 0; + pNoise->state.pink.counter[iChannel] = 1; + } + } + + if (pNoise->config.type == ma_noise_type_brownian) { + pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pNoise->state.brownian.accumulation[iChannel] = 0; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pNoise->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pNoise == NULL) { + return; + } + + ma_data_source_uninit(&pNoise->ds); + + if (pNoise->_ownsHeap) { + ma_free(pNoise->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->config.amplitude = amplitude; + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->lcg.state = seed; + return MA_SUCCESS; +} + + +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + /* + This function should never have been implemented in the first place. Changing the type dynamically is not + supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function + will be removed in version 0.12. + */ + MA_ASSERT(MA_FALSE); + (void)type; + + return MA_INVALID_OPERATION; +} + +static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) +{ + return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_white(pNoise); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + + +static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) +{ + unsigned int n; + + /* Special case for odd numbers since they should happen about half the time. */ + if (x & 0x1) { + return 0; + } + + if (x == 0) { + return sizeof(x) << 3; + } + + n = 1; + if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } + if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } + if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } + if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } + n -= x & 0x00000001; + + return n; +} + +/* +Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h + +This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ +*/ +static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) +{ + double result; + double binPrev; + double binNext; + unsigned int ibin; + + ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); + + binPrev = pNoise->state.pink.bin[iChannel][ibin]; + binNext = ma_lcg_rand_f64(&pNoise->lcg); + pNoise->state.pink.bin[iChannel][ibin] = binNext; + + pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); + pNoise->state.pink.counter[iChannel] += 1; + + result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); + result /= 10; + + return (float)(result * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_pink(pNoise, iChannel); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + + +static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) +{ + double result; + + result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); + result /= 1.005; /* Don't escape the -1..1 range on average. */ + + pNoise->state.brownian.accumulation[iChannel] = result; + result /= 20; + + return (float)(result * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_brownian(pNoise, iChannel); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ + if (pFramesOut == NULL) { + framesRead = frameCount; + } else { + switch (pNoise->config.type) { + case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; + default: return MA_INVALID_OPERATION; /* Unknown noise type. */ + } + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + return MA_SUCCESS; +} +#endif /* MA_NO_GENERATION */ + + + +#ifndef MA_NO_RESOURCE_MANAGER +#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS +#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 +#endif + +#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 +#endif + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) +{ + ma_resource_manager_pipeline_notifications notifications; + + MA_ZERO_OBJECT(¬ifications); + + return notifications; +} + +static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } + if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } +} + +static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } +} + +static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } +} + + + +#ifndef MA_DEFAULT_HASH_SEED +#define MA_DEFAULT_HASH_SEED 42 +#endif + +/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif + +static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) +{ + return (x << r) | (x >> (32 - r)); +} + +static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) +{ + ma_uint32 block; + + /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); + + if (ma_is_little_endian()) { + return block; + } else { + return ma_swap_endian_uint32(block); + } +} + +static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) +{ + const ma_uint8* data = (const ma_uint8*)key; + const ma_uint32* blocks; + const ma_uint8* tail; + const int nblocks = len / 4; + ma_uint32 h1 = seed; + ma_uint32 c1 = 0xcc9e2d51; + ma_uint32 c2 = 0x1b873593; + ma_uint32 k1; + int i; + + blocks = (const ma_uint32 *)(data + nblocks*4); + + for(i = -nblocks; i; i++) { + k1 = ma_hash_getblock(blocks,i); + + k1 *= c1; + k1 = ma_rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ma_rotl32(h1, 13); + h1 = h1*5 + 0xe6546b64; + } + + + tail = (const ma_uint8*)(data + nblocks*4); + + k1 = 0; + switch(len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; + }; + + + h1 ^= len; + h1 = ma_hash_fmix32(h1); + + return h1; +} + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push +#endif +/* End MurmurHash3 */ + +static ma_uint32 ma_hash_string_32(const char* str) +{ + return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); +} + +static ma_uint32 ma_hash_string_w_32(const wchar_t* str) +{ + return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); +} + + + + +/* +Basic BST Functions +*/ +static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppDataBufferNode != NULL); + + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + break; /* Found. */ + } else if (hashedName32 < pCurrentNode->hashedName32) { + pCurrentNode = pCurrentNode->pChildLo; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + + *ppDataBufferNode = pCurrentNode; + + if (pCurrentNode == NULL) { + return MA_DOES_NOT_EXIST; + } else { + return MA_SUCCESS; + } +} + +static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppInsertPoint != NULL); + + *ppInsertPoint = NULL; + + if (pResourceManager->pRootDataBufferNode == NULL) { + return MA_SUCCESS; /* No items. */ + } + + /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + result = MA_ALREADY_EXISTS; + break; + } else { + if (hashedName32 < pCurrentNode->hashedName32) { + if (pCurrentNode->pChildLo == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildLo; + } + } else { + if (pCurrentNode->pChildHi == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + } + } + + *ppInsertPoint = pCurrentNode; + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + /* The key must have been set before calling this function. */ + MA_ASSERT(pDataBufferNode->hashedName32 != 0); + + if (pInsertPoint == NULL) { + /* It's the first node. */ + pResourceManager->pRootDataBufferNode = pDataBufferNode; + } else { + /* It's not the first node. It needs to be inserted. */ + if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { + MA_ASSERT(pInsertPoint->pChildLo == NULL); + pInsertPoint->pChildLo = pDataBufferNode; + } else { + MA_ASSERT(pInsertPoint->pChildHi == NULL); + pInsertPoint->pChildHi = pDataBufferNode; + } + } + + pDataBufferNode->pParent = pInsertPoint; + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pInsertPoint; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); +} +#endif + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildLo != NULL) { + pCurrentNode = pCurrentNode->pChildLo; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildHi != NULL) { + pCurrentNode = pCurrentNode->pChildHi; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildHi != NULL); + + return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildLo != NULL); + + return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); +} + +static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->pChildLo == NULL) { + if (pDataBufferNode->pChildHi == NULL) { + /* Simple case - deleting a buffer with no children. */ + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ + pResourceManager->pRootDataBufferNode = NULL; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = NULL; + } else { + pDataBufferNode->pParent->pChildHi = NULL; + } + } + } else { + /* Node has one child - pChildHi != NULL. */ + pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; + } + } + } + } else { + if (pDataBufferNode->pChildHi == NULL) { + /* Node has one child - pChildLo != NULL. */ + pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; + } + } + } else { + /* Complex case - deleting a node with two children. */ + ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; + + /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ + pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); + MA_ASSERT(pReplacementDataBufferNode != NULL); + + /* + Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement + node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The + replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the + replacement node and reinserting it into the same position as the deleted node. + */ + MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ + MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ + + if (pReplacementDataBufferNode->pChildHi == NULL) { + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = NULL; + } else { + pReplacementDataBufferNode->pParent->pChildHi = NULL; + } + } else { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; + } else { + pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; + } + } + + + /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ + if (pDataBufferNode->pParent != NULL) { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; + } else { + pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; + } + } + + /* Now need to update the replacement node's pointers. */ + pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; + pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; + pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; + + /* Now the children of the replacement node need to have their parent pointers updated. */ + if (pReplacementDataBufferNode->pChildLo != NULL) { + pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; + } + if (pReplacementDataBufferNode->pChildHi != NULL) { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; + } + + /* Now the root node needs to be updated. */ + if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { + pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; + } + } + } + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + return result; /* Could not find the data buffer. */ + } + + return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); +} +#endif + +static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); +} + +static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) +{ + ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); +} + +static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { + ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.encoded.pData = NULL; + pDataBufferNode->data.backend.encoded.sizeInBytes = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { + ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.decoded.pData = NULL; + pDataBufferNode->data.backend.decoded.totalFrameCount = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { + ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); + } else { + /* Should never hit this if the node was successfully initialized. */ + MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); + } + } + + /* The data buffer itself needs to be freed. */ + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); +} + +static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ +} + + +static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; +} + + +typedef struct +{ + union + { + ma_async_notification_event e; + ma_async_notification_poll p; + } backend; /* Must be the first member. */ + ma_resource_manager* pResourceManager; +} ma_resource_manager_inline_notification; + +static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pNotification != NULL); + + pNotification->pResourceManager = pResourceManager; + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + return ma_async_notification_event_init(&pNotification->backend.e); + } else { + return ma_async_notification_poll_init(&pNotification->backend.p); + } +} + +static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_uninit(&pNotification->backend.e); + } else { + /* No need to uninitialize a polling notification. */ + } +} + +static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_wait(&pNotification->backend.e); + } else { + while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { + ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + break; + } + } + } +} + +static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) +{ + ma_resource_manager_inline_notification_wait(pNotification); + ma_resource_manager_inline_notification_uninit(pNotification); +} + + +static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_lock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +#ifndef MA_NO_THREADING +static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) +{ + ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; + MA_ASSERT(pResourceManager != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + break; + } + + /* Terminate if we got a quit message. */ + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} +#endif + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void) +{ + ma_resource_manager_config config; + + MA_ZERO_OBJECT(&config); + config.decodedFormat = ma_format_unknown; + config.decodedChannels = 0; + config.decodedSampleRate = 0; + config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ + config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; + + /* Flags. */ + config.flags = 0; + #ifdef MA_NO_THREADING + { + /* Threading is disabled at compile time so disable threading at runtime as well by default. */ + config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + config.jobThreadCount = 0; + } + #endif + + return config; +} + + +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResourceManager); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { + return MA_INVALID_ARGS; /* Requesting too many job threads. */ + } + } + #endif + + pResourceManager->config = *pConfig; + ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); + + /* Get the log set up early so we can start using it as soon as possible. */ + if (pResourceManager->config.pLog == NULL) { + result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); + if (result == MA_SUCCESS) { + pResourceManager->config.pLog = &pResourceManager->log; + } else { + pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ + } + } + + if (pResourceManager->config.pVFS == NULL) { + result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the default file system. */ + } + + pResourceManager->config.pVFS = &pResourceManager->defaultVFS; + } + + /* If threading has been disabled at compile time, enfore it at run time as well. */ + #ifdef MA_NO_THREADING + { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; + + /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; + } + } + + /* Job queue. */ + jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; + jobQueueConfig.flags = 0; + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ + } + + jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; + } + + result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); + if (result != MA_SUCCESS) { + return result; + } + + + /* Custom decoding backends. */ + if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { + size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; + + pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + + pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; + pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; + } + + + + /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + /* Data buffer lock. */ + result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Create the job threads last to ensure the threads has access to valid data. */ + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + } + } + #else + { + /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ + MA_ASSERT(MA_FALSE); + } + #endif + } + + return MA_SUCCESS; +} + + +static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager); + + /* If everything was done properly, there shouldn't be any active data buffers. */ + while (pResourceManager->pRootDataBufferNode != NULL) { + ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + + /* The data buffer has been removed from the BST, so now we need to free it's data. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } +} + +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return; + } + + /* + Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the + queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. + */ + ma_resource_manager_post_job_quit(pResourceManager); + + /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); + } + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ + ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); + + /* The job queue is no longer needed. */ + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + + /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); + + if (pResourceManager->config.pLog == &pResourceManager->log) { + ma_log_uninit(&pResourceManager->log); + } +} + +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return NULL; + } + + return pResourceManager->config.pLog; +} + + + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) +{ + ma_resource_manager_data_source_config config; + + MA_ZERO_OBJECT(&config); + config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; + config.isLooping = MA_FALSE; + + return config; +} + + +static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) +{ + ma_decoder_config config; + + config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); + config.allocationCallbacks = pResourceManager->config.allocationCallbacks; + config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; + config.customBackendCount = pResourceManager->config.customDecodingBackendCount; + config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; + + return config; +} + +static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + MA_ASSERT(pDecoder != NULL); + + config = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + return result; + } + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); + if (result != MA_SUCCESS) { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + return result; + } + } + + return MA_SUCCESS; +} + +static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); +} + +static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + return NULL; /* Connector not yet initialized. */ + } + + switch (pDataBuffer->pNode->data.type) + { + case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; + case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; + case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); + return NULL; + }; + }; +} + +static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) +{ + ma_result result; + + MA_ASSERT(pDataBuffer != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); + + /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result != MA_SUCCESS && result != MA_BUSY) { + return result; /* The data buffer is in an erroneous state. */ + } + + /* + We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the + "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use + an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_config config; + config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); + result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_config config; + config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); + result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_config config; + config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); + result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + /* + Initialization of the connector is when we can fire the init notification. This will give the application access to + the format/channels/rate of the data source. + */ + if (result == MA_SUCCESS) { + /* + The resource manager supports the ability to set the range and loop settings via a config at + initialization time. This results in an case where the ranges could be set explicitly via + ma_data_source_set_*() before we get to this point here. If this happens, we'll end up + hitting a case where we just override those settings which results in what feels like a bug. + + To address this we only change the relevant properties if they're not equal to defaults. If + they're equal to defaults there's no need to change them anyway. If they're *not* set to the + default values, we can assume the user has set the range and loop settings via the config. If + they're doing their own calls to ma_data_source_set_*() in addition to setting them via the + config, that's entirely on the caller and any synchronization issue becomes their problem. + */ + if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { + ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { + ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + if (pConfig->isLooping != MA_FALSE) { + ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); + } + + ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); + + if (pInitNotification != NULL) { + ma_async_notification_signal(pInitNotification); + } + + if (pInitFence != NULL) { + ma_fence_release(pInitFence); + } + } + + /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ + return result; +} + +static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBuffer != NULL); + + (void)pResourceManager; + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_uninit(&pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + return MA_SUCCESS; +} + +static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) +{ + ma_result result; + size_t dataSizeInBytes; + void* pData; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + if (pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + } + + return result; + } + + pDataBufferNode->data.backend.encoded.pData = pData; + pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) +{ + ma_result result = MA_SUCCESS; + ma_decoder* pDecoder; + ma_uint64 totalFrameCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(ppDecoder != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + *ppDecoder = NULL; /* For safety. */ + + pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); + if (pDecoder == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); + if (result != MA_SUCCESS) { + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* + At this point we have the decoder and we now need to initialize the data supply. This will + be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap + allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter + is used when the length of a sound is unknown until a full decode has been performed. + */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; + } + } else { + totalFrameCount = 0; + } + + if (totalFrameCount > 0) { + /* It's a known length. The data supply is a regular decoded buffer. */ + ma_uint64 dataSizeInBytes; + void* pData; + + dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + if (dataSizeInBytes > MA_SIZE_MAX) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pData == NULL) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + /* The buffer needs to be initialized to silence in case the caller reads from it. */ + ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); + + /* Data has been allocated and the data supply can now be initialized. */ + pDataBufferNode->data.backend.decoded.pData = pData; + pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; + pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; + pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; + pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ + } else { + /* + It's an unknown length. The data supply is a paged decoded buffer. Setting this up is + actually easier than the non-paged decoded buffer because we just need to initialize + a ma_paged_audio_buffer object. + */ + result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ + } + + *ppDecoder = pDecoder; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 framesToTryReading; + ma_uint64 framesRead; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDecoder != NULL); + + /* We need to know the size of a page in frames to know how many frames to decode. */ + pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); + framesToTryReading = pageSizeInFrames; + + /* + Here is where we do the decoding of the next page. We'll run a slightly different path depending + on whether or not we're using a flat or paged buffer because the allocation of the page differs + between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged + buffer, we need to allocate a new page and attach it to the linked list. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) + { + case ma_resource_manager_data_supply_type_decoded: + { + /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ + void* pDst; + ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; + if (framesToTryReading > framesRemaining) { + framesToTryReading = framesRemaining; + } + + if (framesToTryReading > 0) { + pDst = ma_offset_ptr( + pDataBufferNode->data.backend.decoded.pData, + pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) + ); + MA_ASSERT(pDst != NULL); + + result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); + if (framesRead > 0) { + pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; + } + } else { + framesRead = 0; + } + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + /* The destination buffer is a freshly allocated page. */ + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); + if (framesRead > 0) { + pPage->sizeInFrames = framesRead; + + result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); + if (result == MA_SUCCESS) { + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; + } else { + /* Failed to append the page. Just abort and set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } else { + /* No frames were read. Free the page and just set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } break; + + case ma_resource_manager_data_supply_type_encoded: + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unexpected data supply type. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); + return MA_ERROR; + }; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_data_buffer_node* pInsertPoint; + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; + } + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); + if (result == MA_ALREADY_EXISTS) { + /* The node already exists. We just need to increment the reference count. */ + pDataBufferNode = pInsertPoint; + + result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); + if (result != MA_SUCCESS) { + return result; /* Should never happen. Failed to increment the reference count. */ + } + + result = MA_ALREADY_EXISTS; + goto done; + } else { + /* + The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This + needs to be done inside the critical section to ensure an uninitialization of the node + does not occur before initialization on another thread. + */ + pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); + if (pDataBufferNode == NULL) { + return MA_OUT_OF_MEMORY; + } + + MA_ZERO_OBJECT(pDataBufferNode); + pDataBufferNode->hashedName32 = hashedName32; + pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ + + if (pExistingData == NULL) { + pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ + pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ + pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; + } else { + pDataBufferNode->data = *pExistingData; + pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ + pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; + } + + result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); + if (result != MA_SUCCESS) { + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return result; /* Should never happen. Failed to insert the data buffer into the BST. */ + } + + /* + Here is where we'll post the job, but only if we're loading asynchronously. If we're + loading synchronously we'll defer loading to a later stage, outside of the critical + section. + */ + if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + /* Loading asynchronously. Post the job. */ + ma_job job; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + } + + /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ + if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } + if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } + + /* We now have everything we need to post the job to the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; + job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataBufferNode.flags = flags; + job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; + job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; + job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; + job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + + if (result != MA_SUCCESS) { + /* Failed to post job. Probably ran out of memory. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + + /* + Fences were acquired before posting the job, but since the job was not able to + be posted, we need to make sure we release them so nothing gets stuck waiting. + */ + if (pInitFence != NULL) { ma_fence_release(pInitFence); } + if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(pInitNotification); + } else { + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + } + + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + + return result; + } + } + } + +done: + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_bool32 nodeAlreadyExists = MA_FALSE; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; /* Safety. */ + } + + if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { + return MA_INVALID_ARGS; + } + + /* If we're specifying existing data, it must be valid. */ + if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { + return MA_INVALID_ARGS; + } + + /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + if (hashedName32 == 0) { + if (pFilePath != NULL) { + hashedName32 = ma_hash_string_32(pFilePath); + } else { + hashedName32 = ma_hash_string_w_32(pFilePathW); + } + } + + /* + Here is where we either increment the node's reference count or allocate a new one and add it + to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is + posted inside the critical section just in case the caller immediately uninitializes the node + as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the + node is not uninitialized before initialization. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + + if (result == MA_ALREADY_EXISTS) { + nodeAlreadyExists = MA_TRUE; + result = MA_SUCCESS; + } else { + if (result != MA_SUCCESS) { + return result; + } + } + + /* + If we're loading synchronously, we'll need to load everything now. When loading asynchronously, + a job will have been posted inside the BST critical section so that an uninitialization can be + allocated an appropriate execution order thereby preventing it from being uninitialized before + the node is initialized by the decoding thread(s). + */ + if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ + if (pFilePath == NULL && pFilePathW == NULL) { + /* + If this path is hit, it means a buffer is being copied (i.e. initialized from only the + hashed name), but that node has been freed in the meantime, probably from some other + thread. This is an invalid operation. + */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); + result = MA_INVALID_OPERATION; + goto done; + } + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { + /* Loading synchronously. Load the sound in it's entirety here. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { + /* No decoding. This is the simple case - just store the file contents in memory. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); + if (result != MA_SUCCESS) { + goto done; + } + } else { + /* Decoding. We do this the same way as we do when loading asynchronously. */ + ma_decoder* pDecoder; + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); + if (result != MA_SUCCESS) { + goto done; + } + + /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ + for (;;) { + /* Decode next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); + if (result != MA_SUCCESS) { + break; /* Will return MA_AT_END when the last page has been decoded. */ + } + } + + /* Reaching the end needs to be considered successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* + At this point the data buffer is either fully decoded or some error occurred. Either + way, the decoder is no longer necessary. + */ + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ + ma_atomic_exchange_i32(&pDataBufferNode->result, result); + } else { + /* Loading asynchronously. We may need to wait for initialization. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + } + } + } else { + /* The data is not managed by the resource manager so there's nothing else to do. */ + MA_ASSERT(pExistingData != NULL); + } + } + +done: + /* If we failed to initialize the data buffer we need to free it. */ + if (result != MA_SUCCESS) { + if (nodeAlreadyExists == MA_FALSE) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + } + } + + /* + The init notification needs to be uninitialized. This will be used if the node does not already + exist, and we've specified ASYNC | WAIT_INIT. + */ + if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) +{ + ma_result result = MA_SUCCESS; + ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ + ma_uint32 hashedName32 = 0; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + if (pDataBufferNode == NULL) { + if (pName == NULL && pNameW == NULL) { + return MA_INVALID_ARGS; + } + + if (pName != NULL) { + hashedName32 = ma_hash_string_32(pName); + } else { + hashedName32 = ma_hash_string_w_32(pNameW); + } + } + + /* + The first thing to do is decrement the reference counter of the node. Then, if the reference + count is zero, we need to free the node. If the node is still in the process of loading, we'll + need to post a job to the job queue to free the node. Otherwise we'll just do it here. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + /* Might need to find the node. Must be done inside the critical section. */ + if (pDataBufferNode == NULL) { + result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* Couldn't find the node. */ + } + } + + result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); + if (result != MA_SUCCESS) { + goto stage2; /* Should never happen. */ + } + + if (refCount == 0) { + result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ + } + } + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + +stage2: + if (result != MA_SUCCESS) { + return result; + } + + /* + Here is where we need to free the node. We don't want to do this inside the critical section + above because we want to keep that as small as possible for multi-threaded efficiency. + */ + if (refCount == 0) { + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ + ma_job job; + + /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ + ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; + + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + return result; + } + + /* If we don't support threading, process the job queue here. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + result = ma_resource_manager_process_next_job(pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + result = MA_SUCCESS; + break; + } + } + } else { + /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ + } + } else { + /* The sound isn't loading so we can just free the node here. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } + } + + return result; +} + + + +static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; + MA_ASSERT(pDataBuffer != NULL); + + ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + + /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ + ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = +{ + ma_resource_manager_data_buffer_cb__read_pcm_frames, + ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, + ma_resource_manager_data_buffer_cb__get_data_format, + ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, + ma_resource_manager_data_buffer_cb__set_looping, + 0 +}; + +static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode; + ma_data_source_config dataSourceConfig; + ma_bool32 async; + ma_uint32 flags; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataBuffer == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataBuffer); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ + flags = pConfig->flags; + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; + + /* + Fences need to be acquired before doing anything. These must be acquired and released outside of + the node to ensure there's no holes where ma_fence_wait() could prematurely return before the + data buffer has completed initialization. + + When loading asynchronously, the node acquisition routine below will acquire the fences on this + thread and then release them on the async thread when the operation is complete. + + These fences are always released at the "done" tag at the end of this function. They'll be + acquired a second if loading asynchronously. This double acquisition system is just done to + simplify code maintanence. + */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + { + /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ + result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + pDataBuffer->pResourceManager = pResourceManager; + pDataBuffer->pNode = pDataBufferNode; + pDataBuffer->flags = flags; + pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ + + /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ + if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { + /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); + ma_atomic_exchange_i32(&pDataBuffer->result, result); + + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } else { + /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ + ma_job job; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); + } + + /* + The status of the data buffer needs to be set to MA_BUSY before posting the job so that the + worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other + than MA_BUSY, it'll assume an error and fall through to an early exit. + */ + ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + + /* Acquire fences a second time. These will be released by the async thread. */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; + job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; + job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; + job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; + + /* If we need to wait for initialization to complete we can just process the job in place. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + + if (result != MA_SUCCESS) { + /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); + ma_atomic_exchange_i32(&pDataBuffer->result, result); + + /* Release the fences after the result has been set on the data buffer. */ + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + } else { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* NOTE: Do not release the init fence here. It will have been done by the job. */ + + /* Make sure we return an error if initialization failed on the async thread. */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + } + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + goto done; + } + } +done: + if (result == MA_SUCCESS) { + if (pConfig->initialSeekPointInPCMFrames > 0) { + ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); + } + } + + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + if (pExistingDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataBuffer->flags; + + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); +} + +static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + + /* The connector should be uninitialized first. */ + ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); + + /* With the connector uninitialized we can unacquire the node. */ + ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); + + /* The base data source needs to be uninitialized as well. */ + ma_data_source_uninit(&pDataBuffer->ds); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { + /* The data buffer can be deleted synchronously. */ + return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + } else { + /* + The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will + be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event + to get processed before returning. + */ + ma_resource_manager_inline_notification notification; + ma_job job; + + /* + We need to mark the node as unavailable so we don't try reading from it anymore, but also to + let the loading thread know that it needs to abort it's loading procedure. + */ + ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + + result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); + if (result != MA_SUCCESS) { + return result; /* Failed to create the notification. This should rarely, if ever, happen. */ + } + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; + job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; + + result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_inline_notification_uninit(¬ification); + return result; + } + + ma_resource_manager_inline_notification_wait_and_uninit(¬ification); + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 framesRead = 0; + ma_bool32 isDecodedBufferBusy = MA_FALSE; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* + We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after + it's been uninitialized or is in the process of uninitializing. + */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If the node is not initialized we need to abort with a busy code. */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + return MA_BUSY; /* Still loading. */ + } + + /* + If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's + a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If + this happens, we need to keep the seek scheduled and return MA_BUSY. + */ + if (pDataBuffer->seekToCursorOnNextRead) { + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); + if (result != MA_SUCCESS) { + if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ + return MA_BUSY; + } + + return result; + } + } + + /* + For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot + exceed this amount. We'll read as much as we can, and then return MA_BUSY. + */ + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { + ma_uint64 availableFrames; + + isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); + + if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { + /* Don't try reading more than the available frame count. */ + if (frameCount > availableFrames) { + frameCount = availableFrames; + + /* + If there's no frames available we want to set the status to MA_AT_END. The logic below + will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this + is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count + is 0 because that'll result in a situation where it's possible MA_AT_END won't get + returned. + */ + if (frameCount == 0) { + result = MA_AT_END; + } + } else { + isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ + } + } + } + + /* Don't attempt to read anything if we've got no frames available. */ + if (frameCount > 0) { + result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); + } + + /* + If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound + as at the end and terminate decoding. + */ + if (result == MA_AT_END) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + result = MA_BUSY; + } + } + + if (isDecodedBufferBusy) { + result = MA_BUSY; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) +{ + ma_result result; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If we haven't yet got a connector we need to abort. */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + pDataBuffer->seekTargetInPCMFrames = frameIndex; + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; + return MA_BUSY; /* Still loading. */ + } + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); + if (result != MA_SUCCESS) { + return result; + } + + pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + *pFormat = pDataBuffer->pNode->data.backend.decoded.format; + *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; + *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; /* Still loading. */ + }; + + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; + }; + + default: + { + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pLength == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + return MA_BUSY; /* Still loading. */ + } + + return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); +} + +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) +{ + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ +} + +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataBuffer, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_data_source_is_looping(pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + return MA_BUSY; + } else { + return MA_INVALID_OPERATION; /* No connector. */ + } + } + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + ma_uint64 cursor; + ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); + + if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { + *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; + } else { + *pAvailableFrames = 0; + } + + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); +} + +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); +} + + +static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); +} + +static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_decoded; + data.backend.decoded.pData = pData; + data.backend.decoded.totalFrameCount = frameCount; + data.backend.decoded.format = format; + data.backend.decoded.channels = channels; + data.backend.decoded.sampleRate = sampleRate; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); +} + +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); +} + + +static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_encoded; + data.backend.encoded.pData = pData; + data.backend.encoded.sizeInBytes = sizeInBytes; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); +} + +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); +} + + +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) +{ + return ma_resource_manager_unregister_data(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) +{ + return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); +} + +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); +} + + +static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); +} + +static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); +} + +static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); +} + + +static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; + MA_ASSERT(pDataStream != NULL); + + ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = +{ + ma_resource_manager_data_stream_cb__read_pcm_frames, + ma_resource_manager_data_stream_cb__seek_to_pcm_frame, + ma_resource_manager_data_stream_cb__get_data_format, + ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, + ma_resource_manager_data_stream_cb__set_looping, + 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ +}; + +static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) +{ + /* Loop if possible. */ + if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { + absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; + } + + ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); +} + +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + ma_job job; + ma_bool32 waitBeforeReturning = MA_FALSE; + ma_resource_manager_inline_notification waitNotification; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataStream == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataStream); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return result; + } + + pDataStream->pResourceManager = pResourceManager; + pDataStream->flags = pConfig->flags; + pDataStream->result = MA_BUSY; + + ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_looping(pDataStream, pConfig->isLooping); + + if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_INVALID_ARGS; + } + + /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pConfig->pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_OUT_OF_MEMORY; + } + + /* + We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we + can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. + */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + waitBeforeReturning = MA_TRUE; + ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); + } + + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); + + /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.loadDataStream.pDataStream = pDataStream; + job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; + job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_uninit(&waitNotification); + } + + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Wait if needed. */ + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* + If there was an error during initialization make sure we return that result here. We don't want to do this + if we're not waiting because it will most likely be in a busy state. + */ + if (pDataStream->result != MA_SUCCESS) { + return pDataStream->result; + } + + /* NOTE: Do not release pInitFence here. That will be done by the job. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_inline_notification freeEvent; + ma_job job; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ + ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + + /* + We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need + to wait for it to complete before returning which means we need an event. + */ + ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.freeDataStream.pDataStream = pDataStream; + job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; + job.data.resourceManager.freeDataStream.pDoneFence = NULL; + ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + + /* We need to wait for the job to finish processing before we return. */ + ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); + + return MA_SUCCESS; +} + + +static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + + return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); +} + +static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + MA_ASSERT(pageIndex == 0 || pageIndex == 1); + + return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); +} + +static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 totalFramesReadForThisPage = 0; + void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The decoder needs to inherit the stream's looping and range state. */ + { + ma_uint64 rangeBeg; + ma_uint64 rangeEnd; + ma_uint64 loopPointBeg; + ma_uint64 loopPointEnd; + + ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); + + ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); + ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); + + ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); + ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); + } + + /* Just read straight from the decoder. It will deal with ranges and looping for us. */ + result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); + if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + } + + ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); +} + +static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) +{ + ma_uint32 iPage; + + MA_ASSERT(pDataStream != NULL); + + for (iPage = 0; iPage < 2; iPage += 1) { + ma_resource_manager_data_stream_fill_page(pDataStream, iPage); + } +} + + +static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) +{ + ma_uint64 framesAvailable; + ma_uint64 frameCount = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFrameCount != NULL) { + frameCount = *pFrameCount; + *pFrameCount = 0; + } + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; + } + + if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + /* If the page we're on is invalid it means we've caught up to the job thread. */ + if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + framesAvailable = 0; + } else { + /* + The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is + that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. + */ + ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); + + framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; + } + + /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ + if (framesAvailable == 0) { + if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { + return MA_AT_END; + } else { + return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ + } + } + + MA_ASSERT(framesAvailable > 0); + + if (frameCount > framesAvailable) { + frameCount = framesAvailable; + } + + *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); + *pFrameCount = frameCount; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) +{ + ma_uint32 newRelativeCursor; + ma_uint32 pageSizeInFrames; + ma_job job; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* The frame count should always fit inside a 32-bit integer. */ + if (frameCount > 0xFFFFFFFF) { + return MA_INVALID_ARGS; + } + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + + /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ + newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; + + /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ + if (newRelativeCursor >= pageSizeInFrames) { + newRelativeCursor -= pageSizeInFrames; + + /* Here is where we post the job start decoding. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.pageDataStream.pDataStream = pDataStream; + job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; + + /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ + ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + + /* Before posting the job we need to make sure we set some state. */ + pDataStream->relativeCursor = newRelativeCursor; + pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + } else { + /* We haven't moved into a new page so we can just move the cursor forward. */ + pDataStream->relativeCursor = newRelativeCursor; + return MA_SUCCESS; + } +} + + +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesProcessed; + ma_format format; + ma_uint32 channels; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); + + /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ + totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + void* pMappedFrames; + ma_uint64 mappedFrameCount; + + mappedFrameCount = frameCount - totalFramesProcessed; + result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); + } + + totalFramesProcessed += mappedFrameCount; + + result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); + if (result != MA_SUCCESS) { + break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) +{ + ma_job job; + ma_result streamResult; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ + if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { + if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + return MA_SUCCESS; + } + } + + + /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ + ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); + + /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); + + /* + We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public + API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of + the first page. + */ + pDataStream->relativeCursor = 0; + pDataStream->currentPageIndex = 0; + ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + + /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + + /* + The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages + are invalid and any content contained within them will be discarded and replaced with newly decoded data. + */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.seekDataStream.pDataStream = pDataStream; + job.data.resourceManager.seekDataStream.frameIndex = frameIndex; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + + if (pChannels != NULL) { + *pChannels = 0; + } + + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* + We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function + such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. + */ + return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) +{ + ma_result result; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* + If the stream is in an erroneous state we need to return an invalid operation. We can allow + this to be called when the data stream is in a busy state because the caller may have asked + for an initial seek position and it's convenient to return that as the cursor position. + */ + result = ma_resource_manager_data_stream_result(pDataStream); + if (result != MA_SUCCESS && result != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) +{ + ma_result streamResult; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS) { + return streamResult; + } + + /* + We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we + calculated when we initialized it on the job thread. + */ + *pLength = pDataStream->totalLengthInPCMFrames; + if (*pLength == 0) { + return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)ma_atomic_load_i32(&pDataStream->result); +} + +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataStream, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_FALSE; + } + + return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ +} + +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) +{ + ma_uint32 pageIndex0; + ma_uint32 pageIndex1; + ma_uint32 relativeCursor; + ma_uint64 availableFrames; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + pageIndex0 = pDataStream->currentPageIndex; + pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; + relativeCursor = pDataStream->relativeCursor; + + availableFrames = 0; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + } + } + + *pAvailableFrames = availableFrames; + return MA_SUCCESS; +} + + +static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSource); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + pDataSource->flags = pConfig->flags; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + + result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* The data source itself is just a data stream or a data buffer. */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + ma_resource_manager_data_source_config config; + + if (pExistingDataSource == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataSource->flags; + + result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* Copying can only be done from data buffers. Streams cannot be copied. */ + if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return MA_INVALID_OPERATION; + } + + return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); +} + +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + /* All we need to is uninitialize the underlying data buffer or data stream. */ + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); + } else { + return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); + } +} + +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); + } else { + return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); + } +} + +MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } else { + return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); + } else { + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); + } else { + return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); + } +} + +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); + } else { + return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); + } +} + +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_FALSE; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); + } else { + return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); + } +} + + +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pResourceManager->jobQueue, pJob); +} + +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) +{ + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + return ma_resource_manager_post_job(pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pResourceManager->jobQueue, pJob); +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ + + /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ + goto done; + } + + /* + We're ready to start loading. Essentially what we're doing here is initializing the data supply + of the node. Once this is complete, data buffers can have their connectors initialized which + will allow then to have audio data read from them. + + Note that when the data supply type has been moved away from "unknown", that is when other threads + will determine that the node is available for data delivery and the data buffer connectors can be + initialized. Therefore, it's important that it is set after the data supply has been initialized. + */ + if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { + /* + Decoding. This is the complex case because we're not going to be doing the entire decoding + process here. Instead it's going to be split of multiple jobs and loaded in pages. The + reason for this is to evenly distribute decoding time across multiple sounds, rather than + having one huge sound hog all the available processing resources. + + The first thing we do is initialize a decoder. This is allocated on the heap and is passed + around to the paging jobs. When the last paging job has completed it's processing, it'll + free the decoder for us. + + This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job + which is where the actual decoding work will be done. However, once this job is complete, + the node will be in a state where data buffer connectors can be initialized. + */ + ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ + ma_job pageDataBufferNodeJob; + + /* Allocate the decoder by initializing a decoded data supply. */ + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); + + /* + Don't ever propagate an MA_BUSY result code or else the resource manager will think the + node is just busy decoding rather than in an error state. This should never happen, but + including this logic for safety just in case. + */ + if (result == MA_BUSY) { + result = MA_ERROR; + } + + if (result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); + #endif + } + + goto done; + } + + /* + At this point the node's data supply is initialized and other threads can start initializing + their data buffer connectors. However, no data will actually be available until we start to + actually decode it. To do this, we need to post a paging job which is where the decoding + work is done. + + Note that if an error occurred at an earlier point, this section will have been skipped. + */ + pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); + pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; + + /* The job has been set up so it can now be posted. */ + result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); + + /* + When we get here, we want to make sure the result code is set to MA_BUSY. The reason for + this is that the result will be copied over to the node's internal result variable. In + this case, since the decoding is still in-progress, we need to make sure the result code + is set to MA_BUSY. + */ + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } else { + result = MA_BUSY; + } + } else { + /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); + } + + +done: + /* File paths are no longer needed. */ + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* + We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads + are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY + because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then + immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any + other error code would cause the buffer to look like it's in a state that it's not. + */ + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* At this point initialization is complete and we can signal the notification if any. */ + if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); + } + + /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); + } + } + + /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + + /* A busy result should be considered successful from the point of view of the job system. */ + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); + } + + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* Don't do any more decoding if the data buffer has started the uninitialization process. */ + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); + if (result != MA_BUSY) { + goto done; + } + + /* We're ready to decode the next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + + /* + If we have a success code by this point, we want to post another job. We're going to set the + result back to MA_BUSY to make it clear that there's still more to load. + */ + if (result == MA_SUCCESS) { + ma_job newJob; + newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ + newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ + + result = ma_resource_manager_post_job(pResourceManager, &newJob); + + /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ + if (result == MA_SUCCESS) { + result = MA_BUSY; + } + } + +done: + /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ + if (result != MA_BUSY) { + ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* If we reached the end we need to treat it as successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* Make sure we set the result of node in case some error occurred. */ + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); + } + } + + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return result; +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; + ma_bool32 isConnectorInitialized = MA_FALSE; + + /* + All we're doing here is checking if the node has finished loading. If not, we just re-post the job + and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. + */ + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* + First thing we need to do is check whether or not the data buffer is getting deleted. If so we + just abort, but making sure we increment the execution pointer. + */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result != MA_BUSY) { + goto done; /* <-- This will ensure the exucution pointer is incremented. */ + } else { + result = MA_SUCCESS; /* <-- Make sure this is reset. */ + } + + /* Try initializing the connector if we haven't already. */ + isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); + if (isConnectorInitialized == MA_FALSE) { + dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); + + if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { + /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ + ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ + dataSourceConfig = ma_resource_manager_data_source_config_init(); + dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; + dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; + dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; + dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; + dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; + + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); + goto done; + } + } else { + /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ + } + } else { + /* The connector is already initialized. Nothing to do here. */ + } + + /* + If the data node is still loading, we need to repost the job and *not* increment the execution + pointer (i.e. we need to not fall through to the "done" label). + + There is a hole between here and the where the data connector is initialized where the data + buffer node may have finished initializing. We need to check for this by checking the result of + the data buffer node and whether or not we had an unknown data supply type at the time of + trying to initialize the data connector. + */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { + return ma_resource_manager_post_job(pResourceManager, pJob); + } + +done: + /* Only move away from a busy code so that we don't trash any existing error codes. */ + ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); + } + + /* + If at this point the data buffer has not had it's connector initialized, it means the + notification event was never signalled which means we need to signal it here. + */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); + } + } + + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); + } + + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_decoder_config decoderConfig; + ma_uint32 pageBufferSizeInBytes; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { + result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ + goto done; + } + + /* We need to initialize the decoder first so we can determine the size of the pages. */ + decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); + } + if (result != MA_SUCCESS) { + goto done; + } + + /* Retrieve the total length of the file before marking the decoder as loaded. */ + if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); + if (result != MA_SUCCESS) { + goto done; /* Failed to retrieve the length. */ + } + } else { + pDataStream->totalLengthInPCMFrames = 0; + } + + /* + Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file + and we don't want to have another thread trying to access the decoder while it's scanning. + */ + pDataStream->isDecoderInitialized = MA_TRUE; + + /* We have the decoder so we can now initialize our page buffer. */ + pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); + + pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pDataStream->pPageData == NULL) { + ma_decoder_uninit(&pDataStream->decoder); + result = MA_OUT_OF_MEMORY; + goto done; + } + + /* Seek to our initial seek point before filling the initial pages. */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); + + /* We have our decoder and our page buffer, so now we need to fill our pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* And now we're done. We want to make sure the result is MA_SUCCESS. */ + result = MA_SUCCESS; + +done: + ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ + ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); + } + if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); + } + + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); + + if (pDataStream->isDecoderInitialized) { + ma_decoder_uninit(&pDataStream->decoder); + } + + if (pDataStream->pPageData != NULL) { + ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); + pDataStream->pPageData = NULL; /* Just in case... */ + } + + ma_data_source_uninit(&pDataStream->ds); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); + } + if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); + } + + /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams, the status should be MA_SUCCESS. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + result = MA_INVALID_OPERATION; + goto done; + } + + ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); + +done: + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams the status should be MA_SUCCESS for this to do anything. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { + result = MA_INVALID_OPERATION; + goto done; + } + + /* + With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except + instead of initializing the decoder, we seek to a frame. + */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); + + /* After seeking we'll need to reload the pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* We need to let the public API know that we're done seeking. */ + ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + +done: + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_process(pJob); +} + +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job job; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + /* This will return MA_CANCELLED if the next job is a quit job. */ + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + return result; + } + + return ma_job_process(&job); +} +#else +/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +#endif /* MA_NO_RESOURCE_MANAGER */ + + +#ifndef MA_NO_NODE_GRAPH +/* 10ms @ 48K = 480. Must never exceed 65535. */ +#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS +#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 +#endif + + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); + +MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + #ifndef MA_NO_GENERATION + { + ma_waveform_config waveformConfig; + ma_waveform waveform; + + waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); + ma_waveform_init(&waveformConfig, &waveform); + ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); + } + #else + { + (void)pFramesOut; + (void)frameCount; + (void)format; + (void)channels; + (void)sampleRate; + #if defined(MA_DEBUG_OUTPUT) + { + #if _MSC_VER + #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") + #endif + } + #endif + } + #endif +} + + + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) +{ + ma_node_graph_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + + return config; +} + + +static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) +{ + MA_ASSERT(pNodeGraph != NULL); + ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); +} + +#if 0 +static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) +{ + MA_ASSERT(pNodeGraph != NULL); + return ma_atomic_load_32(&pNodeGraph->isReading); +} +#endif + + +static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; + ma_uint64 framesRead; + + ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); + + *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ + + (void)ppFramesIn; + (void)pFrameCountIn; +} + +static ma_node_vtable g_node_graph_node_vtable = +{ + ma_node_graph_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 /* Flags. */ +}; + +static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + MA_ASSERT(pNode != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); + MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); + + /* Input channel count needs to be the same as the output channel count. */ + MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); + + /* We don't need to do anything here because it's a passthrough. */ + (void)pNode; + (void)ppFramesIn; + (void)pFrameCountIn; + (void)ppFramesOut; + (void)pFrameCountOut; + +#if 0 + /* The data has already been mixed. We just need to move it to the output buffer. */ + if (ppFramesIn != NULL) { + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); + } +#endif +} + +static ma_node_vtable g_node_graph_endpoint_vtable = +{ + ma_node_graph_endpoint_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + 1, /* 1 output bus. */ + MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) +{ + ma_result result; + ma_node_config baseConfig; + ma_node_config endpointConfig; + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeGraph); + pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; + if (pNodeGraph->nodeCacheCapInFrames == 0) { + pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + } + + + /* Base node so we can use the node graph as a node into another graph. */ + baseConfig = ma_node_config_init(); + baseConfig.vtable = &g_node_graph_node_vtable; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); + if (result != MA_SUCCESS) { + return result; + } + + + /* Endpoint. */ + endpointConfig = ma_node_config_init(); + endpointConfig.vtable = &g_node_graph_endpoint_vtable; + endpointConfig.pInputChannels = &pConfig->channels; + endpointConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); + if (result != MA_SUCCESS) { + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pNodeGraph == NULL) { + return; + } + + ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); +} + +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return NULL; + } + + return &pNodeGraph->endpoint; +} + +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead; + ma_uint32 channels; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); + + + /* We'll be nice and try to do a full read of all frameCount frames. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + ma_uint32 framesJustRead; + ma_uint64 framesToRead = frameCount - totalFramesRead; + + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); + { + result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); + } + ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + + totalFramesRead += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + + /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ + if (totalFramesRead < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); +} + +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ +} + +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) +{ + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ +} + + +#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ + +static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pOutputBus != NULL); + MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); + MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pOutputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pOutputBus->pNode = pNode; + pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; + pOutputBus->channels = (ma_uint8)channels; + pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ + pOutputBus->volume = 1; + + return MA_SUCCESS; +} + +static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_lock(&pOutputBus->lock); +} + +static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_unlock(&pOutputBus->lock); +} + + +static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) +{ + return pOutputBus->channels; +} + + +static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) +{ + if (hasRead) { + ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } else { + ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } +} + +static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) +{ + return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; +} + + +static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) +{ + ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); +} + +static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) +{ + return ma_atomic_load_32(&pOutputBus->isAttached); +} + + +static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) +{ + MA_ASSERT(pOutputBus != NULL); + + if (volume < 0.0f) { + volume = 0.0f; + } + + ma_atomic_exchange_f32(&pOutputBus->volume, volume); + + return MA_SUCCESS; +} + +static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) +{ + return ma_atomic_load_f32((float*)&pOutputBus->volume); +} + + +static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pInputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pInputBus->channels = (ma_uint8)channels; + + return MA_SUCCESS; +} + +static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + + ma_spinlock_lock(&pInputBus->lock); +} + +static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + + ma_spinlock_unlock(&pInputBus->lock); +} + + +static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) +{ + ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); +} + +static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) +{ + ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); +} + +static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) +{ + return ma_atomic_load_32(&pInputBus->nextCounter); +} + + +static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) +{ + return pInputBus->channels; +} + + +static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + /* + Mark the output bus as detached first. This will prevent future iterations on the audio thread + from iterating this output bus. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); + + /* + We cannot use the output bus lock here since it'll be getting used at a higher level, but we do + still need to use the input bus lock since we'll be updating pointers on two different output + buses. The same rules apply here as the attaching case. Although we're using a lock here, we're + *not* using a lock when iterating over the list in the audio thread. We therefore need to craft + this in a way such that the iteration on the audio thread doesn't break. + + The the first thing to do is swap out the "next" pointer of the previous output bus with the + new "next" output bus. This is the operation that matters for iteration on the audio thread. + After that, the previous pointer on the new "next" pointer needs to be updated, after which + point the linked list will be in a good state. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); + + if (pOldPrev != NULL) { + ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + } + if (pOldNext != NULL) { + ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + } + } + ma_node_input_bus_unlock(pInputBus); + + /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ + ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + pOutputBus->pInputNode = NULL; + pOutputBus->inputNodeInputBusIndex = 0; + + + /* + For thread-safety reasons, we don't want to be returning from this straight away. We need to + wait for the audio thread to finish with the output bus. There's two things we need to wait + for. The first is the part that selects the next output bus in the list, and the other is the + part that reads from the output bus. Basically all we're doing is waiting for the input bus + to stop referencing the output bus. + + We're doing this part last because we want the section above to run while the audio thread + is finishing up with the output bus, just for efficiency reasons. We marked the output bus as + detached right at the top of this function which is going to prevent the audio thread from + iterating the output bus again. + */ + + /* Part 1: Wait for the current iteration to complete. */ + while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { + ma_yield(); + } + + /* Part 2: Wait for any reads to complete. */ + while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { + ma_yield(); + } + + /* + At this point we're done detaching and we can be guaranteed that the audio thread is not going + to attempt to reference this output bus again (until attached again). + */ +} + +#if 0 /* Not used at the moment, but leaving here in case I need it later. */ +static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + ma_node_output_bus_unlock(pOutputBus); +} +#endif + +static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); + + /* Detach from any existing attachment first if necessary. */ + if (pOldInputNode != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + + /* + At this point we can be sure the output bus is not attached to anything. The linked list in the + old input bus has been updated so that pOutputBus will not get iterated again. + */ + pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ + pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; + + /* + Now we need to attach the output bus to the linked list. This involves updating two pointers on + two different output buses so I'm going to go ahead and keep this simple and just use a lock. + There are ways to do this without a lock, but it's just too hard to maintain for it's value. + + Although we're locking here, it's important to remember that we're *not* locking when iterating + and reading audio data since that'll be running on the audio thread. As a result we need to be + careful how we craft this so that we don't break iteration. What we're going to do is always + attach the new item so that it becomes the first item in the list. That way, as we're iterating + we won't break any links in the list and iteration will continue safely. The detaching case will + also be crafted in a way as to not break list iteration. It's important to remember to use + atomic exchanges here since no locking is happening on the audio thread during iteration. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pNewPrev = &pInputBus->head; + ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); + + /* Update the local output bus. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + + /* Update the other output buses to point back to the local output bus. */ + ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + + /* Do the previous pointer last. This is only used for detachment. */ + if (pNewNext != NULL) { + ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + } + } + ma_node_input_bus_unlock(pInputBus); + + /* + Mark the node as attached last. This is used to controlling whether or the output bus will be + iterated on the audio thread. Mainly required for detachment purposes. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); + } + ma_node_output_bus_unlock(pOutputBus); +} + +static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + ma_node_output_bus* pNext; + + MA_ASSERT(pInputBus != NULL); + + if (pOutputBus == NULL) { + return NULL; + } + + ma_node_input_bus_next_begin(pInputBus); + { + pNext = pOutputBus; + for (;;) { + pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); + if (pNext == NULL) { + break; /* Reached the end. */ + } + + if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { + continue; /* The node is not attached. Keep checking. */ + } + + /* The next node has been selected. */ + break; + } + + /* We need to increment the reference count of the selected node. */ + if (pNext != NULL) { + ma_atomic_fetch_add_32(&pNext->refCount, 1); + } + + /* The previous node is no longer being referenced. */ + ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); + } + ma_node_input_bus_next_end(pInputBus); + + return pNext; +} + +static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) +{ + return ma_node_input_bus_next(pInputBus, &pInputBus->head); +} + + + +static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_result result = MA_SUCCESS; + ma_node_output_bus* pOutputBus; + ma_node_output_bus* pFirst; + ma_uint32 inputChannels; + ma_bool32 doesOutputBufferHaveContent = MA_FALSE; + + (void)pInputNode; /* Not currently used. */ + + /* + This will be called from the audio thread which means we can't be doing any locking. Basically, + this function will not perfom any locking, whereas attaching and detaching will, but crafted in + such a way that we don't need to perform any locking here. The important thing to remember is + to always iterate in a forward direction. + + In order to process any data we need to first read from all input buses. That's where this + function comes in. This iterates over each of the attachments and accumulates/mixes them. We + also convert the channels to the nodes output channel count before mixing. We want to do this + channel conversion so that the caller of this function can invoke the processing callback + without having to do it themselves. + + When we iterate over each of the attachments on the input bus, we need to read as much data as + we can from each of them so that we don't end up with holes between each of the attachments. To + do this, we need to read from each attachment in a loop and read as many frames as we can, up + to `frameCount`. + */ + MA_ASSERT(pInputNode != NULL); + MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ + + *pFramesRead = 0; /* Safety. */ + + inputChannels = ma_node_input_bus_get_channels(pInputBus); + + /* + We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They + are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() + once per iteration, however we have an optimization to checks whether or not it's the first item in + the list. We therefore need to store a pointer to the first item rather than repeatedly calling + ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it + after calling ma_node_input_bus_next(), which we won't be. + */ + pFirst = ma_node_input_bus_first(pInputBus); + if (pFirst == NULL) { + return MA_SUCCESS; /* No attachments. Read nothing. */ + } + + for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { + ma_uint32 framesProcessed = 0; + ma_bool32 isSilentOutput = MA_FALSE; + + MA_ASSERT(pOutputBus->pNode != NULL); + MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); + + isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; + + if (pFramesOut != NULL) { + /* Read. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; + + while (framesProcessed < frameCount) { + float* pRunningFramesOut; + ma_uint32 framesToRead; + ma_uint32 framesJustRead; + + framesToRead = frameCount - framesProcessed; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); + + if (doesOutputBufferHaveContent == MA_FALSE) { + /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); + } else { + /* Slow path. Not the first attachment. Mixing required. */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); + if (result == MA_SUCCESS || result == MA_AT_END) { + if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ + ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); + } + } + } + + framesProcessed += framesJustRead; + + /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ + if (result != MA_SUCCESS) { + break; + } + + /* If we didn't read anything, abort so we don't get stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ + if (pOutputBus == pFirst && framesProcessed < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); + } + + if (isSilentOutput == MA_FALSE) { + doesOutputBufferHaveContent = MA_TRUE; + } + } else { + /* Seek. */ + ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); + } + } + + /* If we didn't output anything, output silence. */ + if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); + } + + /* In this path we always "process" the entire amount. */ + *pFramesRead = frameCount; + + return result; +} + + +MA_API ma_node_config ma_node_config_init(void) +{ + ma_node_config config; + + MA_ZERO_OBJECT(&config); + config.initialState = ma_node_state_started; /* Nodes are started by default. */ + config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + + return config; +} + + + +static ma_result ma_node_detach_full(ma_node* pNode); + +static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Input data is stored at the front of the buffer. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + return pBasePtr; +} + +static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Cached output data starts after the input data. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); + } + + return pBasePtr; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t inputBusOffset; + size_t outputBusOffset; + size_t cachedDataOffset; + ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ + ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ +} ma_node_heap_layout; + +static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) +{ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pInputBusCount != NULL); + MA_ASSERT(pOutputBusCount != NULL); + + /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ + if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + inputBusCount = pConfig->inputBusCount; + } else { + inputBusCount = pConfig->vtable->inputBusCount; + + if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + outputBusCount = pConfig->outputBusCount; + } else { + outputBusCount = pConfig->vtable->outputBusCount; + + if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + /* Bus counts must be within limits. */ + if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; + } + + + /* We must have channel counts for each bus. */ + if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { + return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ + } + + + /* Some special rules for passthrough nodes. */ + if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { + return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ + } + + if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { + return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ + } + } + + + *pInputBusCount = inputBusCount; + *pOutputBusCount = outputBusCount; + + return MA_SUCCESS; +} + +static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input buses. */ + if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); + } else { + pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ + } + + /* Output buses. */ + if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); + } else { + pHeapLayout->outputBusOffset = MA_SIZE_MAX; + } + + /* + Cached audio data. + + We need to allocate memory for a caching both input and output data. We have an optimization + where no caching is necessary for specific conditions: + + - The node has 0 inputs and 1 output. + + When a node meets the above conditions, no cache is allocated. + + The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by + allocating too much, but at the same time we want it be large enough so that enough frames can + be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For + now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile + time. It might also be worth investigating whether or not this can be configured at run time. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No cache needed. */ + pHeapLayout->cachedDataOffset = MA_SIZE_MAX; + } else { + /* Slow path. Cache needed. */ + size_t cachedDataSizeInBytes = 0; + ma_uint32 iBus; + + for (iBus = 0; iBus < inputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); + } + + for (iBus = 0; iBus < outputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); + } + + pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); + } + + + /* + Not technically part of the heap, but we can output the input and output bus counts so we can + avoid a redundant call to ma_node_translate_bus_counts(). + */ + pHeapLayout->inputBusCount = inputBusCount; + pHeapLayout->outputBusCount = outputBusCount; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result; + ma_node_heap_layout heapLayout; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeBase); + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pNodeBase->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pNodeBase->pNodeGraph = pNodeGraph; + pNodeBase->vtable = pConfig->vtable; + pNodeBase->state = pConfig->initialState; + pNodeBase->stateTimes[ma_node_state_started] = 0; + pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ + pNodeBase->inputBusCount = heapLayout.inputBusCount; + pNodeBase->outputBusCount = heapLayout.outputBusCount; + + if (heapLayout.inputBusOffset != MA_SIZE_MAX) { + pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + } else { + pNodeBase->pInputBuses = pNodeBase->_inputBuses; + } + + if (heapLayout.outputBusOffset != MA_SIZE_MAX) { + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); + } else { + pNodeBase->pOutputBuses = pNodeBase->_outputBuses; + } + + if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { + pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); + pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; + } else { + pNodeBase->pCachedData = NULL; + } + + + + /* We need to run an initialization step for each input and output bus. */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + + /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ + if (pNodeBase->pCachedData != NULL) { + ma_uint32 iBus; + + #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ + /* For safety we'll go ahead and default the buffer to silence. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); + } + #else + /* For debugging. Default to a sine wave. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return; + } + + /* + The first thing we need to do is fully detach the node. This will detach all inputs and + outputs. We need to do this first because it will sever the connection with the node graph and + allow us to complete uninitialization without needing to worry about thread-safety with the + audio thread. The detachment process will wait for any local processing of the node to finish. + */ + ma_node_detach_full(pNode); + + /* + At this point the node should be completely unreferenced by the node graph and we can finish up + the uninitialization process without needing to worry about thread-safety. + */ + if (pNodeBase->_ownsHeap) { + ma_free(pNodeBase->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) +{ + if (pNode == NULL) { + return NULL; + } + + return ((const ma_node_base*)pNode)->pNodeGraph; +} + +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->inputBusCount; +} + +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->outputBusCount; +} + + +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); +} + +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); +} + + +static ma_result ma_node_detach_full(ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + /* + Make sure the node is completely detached first. This will not return until the output bus is + guaranteed to no longer be referenced by the audio thread. + */ + ma_node_detach_all_output_buses(pNode); + + /* + At this point all output buses will have been detached from the graph and we can be guaranteed + that none of it's input nodes will be getting processed by the graph. We can detach these + without needing to worry about the audio thread touching them. + */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { + ma_node_input_bus* pInputBus; + ma_node_output_bus* pOutputBus; + + pInputBus = &pNodeBase->pInputBuses[iInputBus]; + + /* + This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those + functions are specifically for the audio thread. We'll instead just manually iterate using standard + linked list logic. We don't need to worry about the audio thread referencing these because the step + above severed the connection to the graph. + */ + for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { + ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pInputNodeBase; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ + ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); + { + pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; + if (pInputNodeBase != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); + } + } + ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); + + return result; +} + +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) +{ + ma_uint32 iOutputBus; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { + ma_node_detach_output_bus(pNode, iOutputBus); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; + + if (pNodeBase == NULL || pOtherNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (pNodeBase == pOtherNodeBase) { + return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { + return MA_INVALID_OPERATION; /* Invalid bus index. */ + } + + /* The output channel count of the output node must be the same as the input channel count of the input node. */ + if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { + return MA_INVALID_OPERATION; /* Channel count is incompatible. */ + } + + /* This will deal with detaching if the output bus is already attached to something. */ + ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid bus index. */ + } + + return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); +} + +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); +} + +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_i32(&pNodeBase->state, state); + + return MA_SUCCESS; +} + +MA_API ma_node_state ma_node_get_state(const ma_node* pNode) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return ma_node_state_stopped; + } + + return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); +} + +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + + return MA_SUCCESS; +} + +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) +{ + if (pNode == NULL) { + return 0; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return 0; + } + + return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); +} + +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return ma_node_state_stopped; + } + + return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); +} + +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) +{ + ma_node_state state; + + if (pNode == NULL) { + return ma_node_state_stopped; + } + + state = ma_node_get_state(pNode); + + /* An explicitly stopped node is always stopped. */ + if (state == ma_node_state_stopped) { + return ma_node_state_stopped; + } + + /* + Getting here means the node is marked as started, but it may still not be truly started due to + it's start time not having been reached yet. Also, the stop time may have also been reached in + which case it'll be considered stopped. + */ + if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { + return ma_node_state_stopped; /* Start time has not yet been reached. */ + } + + if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { + return ma_node_state_stopped; /* Stop time has been reached. */ + } + + /* Getting here means the node is marked as started and is within it's start/stop times. */ + return ma_node_state_started; +} + +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); +} + +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + + return MA_SUCCESS; +} + + + +static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + MA_ASSERT(pNode != NULL); + + if (pNodeBase->vtable->onProcess) { + pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); + } +} + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result = MA_SUCCESS; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_uint32 totalFramesRead = 0; + float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; + float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; + ma_uint64 globalTimeBeg; + ma_uint64 globalTimeEnd; + ma_uint64 startTime; + ma_uint64 stopTime; + ma_uint32 timeOffsetBeg; + ma_uint32 timeOffsetEnd; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + + /* + pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and + expected that the number of frames read may be different to that requested. Therefore, the caller + must look at this value to correctly determine how many frames were read. + */ + MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ + if (pFramesRead == NULL) { + return MA_INVALID_ARGS; + } + + *pFramesRead = 0; /* Safety. */ + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* Don't do anything if we're in a stopped state. */ + if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { + return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ + } + + + globalTimeBeg = globalTime; + globalTimeEnd = globalTime + frameCount; + startTime = ma_node_get_state_time(pNode, ma_node_state_started); + stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); + + /* + At this point we know that we are inside our start/stop times. However, we may need to adjust + our frame count and output pointer to accommodate since we could be straddling the time period + that this function is getting called for. + + It's possible (and likely) that the start time does not line up with the output buffer. We + therefore need to offset it by a number of frames to accommodate. The same thing applies for + the stop time. + */ + timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; + timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; + + /* Trim based on the start offset. We need to silence the start of the buffer. */ + if (timeOffsetBeg > 0) { + ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); + frameCount -= timeOffsetBeg; + } + + /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ + if (timeOffsetEnd > 0) { + frameCount -= timeOffsetEnd; + } + + + /* We run on different paths depending on the bus counts. */ + inputBusCount = ma_node_get_input_bus_count(pNode); + outputBusCount = ma_node_get_output_bus_count(pNode); + + /* + Run a simplified path when there are no inputs and one output. In this case there's nothing to + actually read and we can go straight to output. This is a very common scenario because the vast + majority of data source nodes will use this setup so this optimization I think is worthwhile. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No need to read from input and no need for any caching. */ + frameCountIn = 0; + frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ + + ppFramesOut[0] = pFramesOut; + + /* + If it's a passthrough we won't be expecting the callback to output anything, so we'll + need to pre-silence the output buffer. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + } + + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + totalFramesRead = frameCountOut; + } else { + /* Slow path. Need to read input data. */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + /* + Fast path. We're running a passthrough. We need to read directly into the output buffer, but + still fire the callback so that event handling and trigger nodes can do their thing. Since + it's a passthrough there's no need for any kind of caching logic. + */ + MA_ASSERT(outputBusCount == inputBusCount); + MA_ASSERT(outputBusCount == 1); + MA_ASSERT(outputBusIndex == 0); + + /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ + ppFramesOut[0] = pFramesOut; + ppFramesIn[0] = ppFramesOut[0]; + + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); + if (result == MA_SUCCESS) { + /* Even though it's a passthrough, we still need to fire the callback. */ + frameCountIn = totalFramesRead; + frameCountOut = totalFramesRead; + + if (totalFramesRead > 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } + + /* + A passthrough should never have modified the input and output frame counts. If you're + triggering these assers you need to fix your processing callback. + */ + MA_ASSERT(frameCountIn == totalFramesRead); + MA_ASSERT(frameCountOut == totalFramesRead); + } + } else { + /* Slow path. Need to do caching. */ + ma_uint32 framesToProcessIn; + ma_uint32 framesToProcessOut; + ma_bool32 consumeNullInput = MA_FALSE; + + /* + We use frameCount as a basis for the number of frames to read since that's what's being + requested, however we still need to clamp it to whatever can fit in the cache. + + This will also be used as the basis for determining how many input frames to read. This is + not ideal because it can result in too many input frames being read which introduces latency. + To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount + which is used as hint to miniaudio as to how many input frames it needs to read at a time. This + callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. + + This function will be called multiple times for each period of time, once for each output node. + We cannot read from each input node each time this function is called. Instead we need to check + whether or not this is first output bus to be read from for this time period, and if so, read + from our input data. + + To determine whether or not we're ready to read data, we check a flag. There will be one flag + for each output. When the flag is set, it means data has been read previously and that we're + ready to advance time forward for our input nodes by reading fresh data. + */ + framesToProcessOut = frameCount; + if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; + } + + framesToProcessIn = frameCount; + if (pNodeBase->vtable->onGetRequiredInputFrameCount) { + pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ + } + if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; + } + + + MA_ASSERT(framesToProcessIn <= 0xFFFF); + MA_ASSERT(framesToProcessOut <= 0xFFFF); + + if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { + /* Getting here means we need to do another round of processing. */ + pNodeBase->cachedFrameCountOut = 0; + + for (;;) { + frameCountOut = 0; + + /* + We need to prepare our output frame pointers for processing. In the same iteration we need + to mark every output bus as unread so that future calls to this function for different buses + for the current time period don't pull in data when they should instead be reading from cache. + */ + for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ + ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); + } + + /* We only need to read from input buses if there isn't already some data in the cache. */ + if (pNodeBase->cachedFrameCountIn == 0) { + ma_uint32 maxFramesReadIn = 0; + + /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ma_uint32 framesRead; + + /* The first thing to do is get the offset within our bulk allocation to store this input data. */ + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); + + /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); + if (result != MA_SUCCESS) { + /* It doesn't really matter if we fail because we'll just fill with silence. */ + framesRead = 0; /* Just for safety, but I don't think it's really needed. */ + } + + /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ + /* Any leftover frames need to silenced for safety. */ + if (framesRead < framesToProcessIn) { + ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); + } + + maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); + } + + /* This was a fresh load of input data so reset our consumption counter. */ + pNodeBase->consumedFrameCountIn = 0; + + /* + We don't want to keep processing if there's nothing to process, so set the number of cached + input frames to the maximum number we read from each attachment (the lesser will be padded + with silence). If we didn't read anything, this will be set to 0 and the entire buffer will + have been assigned to silence. This being equal to 0 is an important property for us because + it allows us to detect when NULL can be passed into the processing callback for the input + buffer for the purpose of continuous processing. + */ + pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; + } else { + /* We don't need to read anything, but we do need to prepare our input frame pointers. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); + } + } + + /* + At this point we have our input data so now we need to do some processing. Sneaky little + optimization here - we can set the pointer to the output buffer for this output bus so + that the final copy into the output buffer is done directly by onProcess(). + */ + if (pFramesOut != NULL) { + ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); + } + + + /* Give the processing function the entire capacity of the output buffer. */ + frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); + + /* + We need to treat nodes with continuous processing a little differently. For these ones, + we always want to fire the callback with the requested number of frames, regardless of + pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass + in NULL for the input buffer to the callback. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { + /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ + frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ + + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { + consumeNullInput = MA_TRUE; + } else { + consumeNullInput = MA_FALSE; + } + + /* + Since we're using continuous processing we're always passing in a full frame count + regardless of how much input data was read. If this is greater than what we read as + input, we'll end up with an underflow. We instead need to make sure our cached frame + count is set to the number of frames we'll be passing to the data callback. Not + doing this will result in an underflow when we "consume" the cached data later on. + + Note that this check needs to be done after the "consumeNullInput" check above because + we use the property of cachedFrameCountIn being 0 to determine whether or not we + should be passing in a null pointer to the processing callback for when the node is + configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. + */ + if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { + pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; + } + } else { + frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ + consumeNullInput = MA_FALSE; + } + + /* + Process data slightly differently depending on whether or not we're consuming NULL + input (checked just above). + */ + if (consumeNullInput) { + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + } else { + /* + We want to skip processing if there's no input data, but we can only do that safely if + we know that there is no chance of any output frames being produced. If continuous + processing is being used, this won't be a problem because the input frame count will + always be non-0. However, if continuous processing is *not* enabled and input and output + data is processed at different rates, we still need to process that last input frame + because there could be a few excess output frames needing to be produced from cached + data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for + determining whether or not we need to process the node even when there are no input + frames available right now. + */ + if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } else { + frameCountOut = 0; /* No data was processed. */ + } + } + + /* + Thanks to our sneaky optimization above we don't need to do any data copying directly into + the output buffer - the onProcess() callback just did that for us. We do, however, need to + apply the number of input and output frames that were processed. Note that due to continuous + processing above, we need to do explicit checks here. If we just consumed a NULL input + buffer it means that no actual input data was processed from the internal buffers and we + don't want to be modifying any counters. + */ + if (consumeNullInput == MA_FALSE) { + pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; + pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; + } + + /* The cached output frame count is always equal to what we just read. */ + pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; + + /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ + if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { + break; + } + } + } else { + /* + We're not needing to read anything from the input buffer so just read directly from our + already-processed data. + */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); + } + } + + /* The number of frames read is always equal to the number of cached output frames. */ + totalFramesRead = pNodeBase->cachedFrameCountOut; + + /* Now that we've read the data, make sure our read flag is set. */ + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); + } + } + + /* Apply volume, if necessary. */ + ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); + + /* Advance our local time forward. */ + ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + + *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ + return result; +} + + + + +/* Data source node. */ +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) +{ + ma_data_source_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.pDataSource = pDataSource; + + return config; +} + + +static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; + ma_format format; + ma_uint32 channels; + ma_uint32 frameCount; + ma_uint64 framesRead = 0; + + MA_ASSERT(pDataSourceNode != NULL); + MA_ASSERT(pDataSourceNode->pDataSource != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); + MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); + + /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + frameCount = *pFrameCountOut; + + /* miniaudio should never be calling this with a frame count of zero. */ + MA_ASSERT(frameCount > 0); + + if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ + /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ + MA_ASSERT(format == ma_format_f32); + (void)format; /* Just to silence some static analysis tools. */ + + ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); + } + + *pFrameCountOut = (ma_uint32)framesRead; +} + +static ma_node_vtable g_ma_data_source_node_vtable = +{ + ma_data_source_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 +}; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) +{ + ma_result result; + ma_format format; /* For validating the format, which must be ma_format_f32. */ + ma_uint32 channels; /* For specifying the channel count of the output bus. */ + ma_node_config baseConfig; + + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSourceNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ + if (result != MA_SUCCESS) { + return result; + } + + MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ + if (format != ma_format_f32) { + return MA_INVALID_ARGS; /* Invalid format. */ + } + + /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ + + /* + The channel count is defined by the data source. It is invalid for the caller to manually set + the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the + channel count pointer to NULL which is how it must remain. If you trigger any of these asserts + it means you're explicitly setting the channel count. Instead, configure the output channel + count of your data source to be the necessary channel count. + */ + if (baseConfig.pOutputChannels != NULL) { + return MA_INVALID_ARGS; + } + + baseConfig.pOutputChannels = &channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); + if (result != MA_SUCCESS) { + return result; + } + + pDataSourceNode->pDataSource = pConfig->pDataSource; + + return MA_SUCCESS; +} + +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); +} + +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) +{ + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) +{ + if (pDataSourceNode == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pDataSourceNode->pDataSource); +} + + + +/* Splitter Node. */ +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) +{ + ma_splitter_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.outputBusCount = 2; + + return config; +} + + +static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iOutputBus; + ma_uint32 channels; + + MA_ASSERT(pNodeBase != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); + + /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ + (void)pFrameCountIn; + + /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ + channels = ma_node_get_input_channels(pNodeBase, 0); + + /* Splitting is just copying the first input bus and copying it over to each output bus. */ + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); + } +} + +static ma_node_vtable g_ma_splitter_node_vtable = +{ + ma_splitter_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ + 0 +}; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 pInputChannels[1]; + ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 iOutputBus; + + if (pSplitterNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSplitterNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; /* Too many output buses. */ + } + + /* Splitters require the same number of channels between inputs and outputs. */ + pInputChannels[0] = pConfig->channels; + for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { + pOutputChannels[iOutputBus] = pConfig->channels; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_splitter_node_vtable; + baseConfig.pInputChannels = pInputChannels; + baseConfig.pOutputChannels = pOutputChannels; + baseConfig.outputBusCount = pConfig->outputBusCount; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base node. */ + } + + return MA_SUCCESS; +} + +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(pSplitterNode, pAllocationCallbacks); +} + + +/* +Biquad Node +*/ +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) +{ + ma_biquad_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); + + return config; +} + +static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_biquad_node_vtable = +{ + ma_biquad_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->biquad.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_biquad_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->biquad.channels; + baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + + return ma_biquad_reinit(pConfig, &pLPFNode->biquad); +} + +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); +} + + + +/* +Low Pass Filter Node +*/ +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_lpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_lpf_node_vtable = +{ + ma_lpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->lpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_lpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->lpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_lpf_reinit(pConfig, &pLPFNode->lpf); +} + +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); +} + + + +/* +High Pass Filter Node +*/ +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_hpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hpf_node_vtable = +{ + ma_hpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hpf_reinit(pConfig, &pHPFNode->hpf); +} + +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); +} + + + + +/* +Band Pass Filter Node +*/ +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_bpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_bpf_node_vtable = +{ + ma_bpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->bpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_bpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->bpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_bpf_reinit(pConfig, &pBPFNode->bpf); +} + +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); +} + + + +/* +Notching Filter Node +*/ +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) +{ + ma_notch_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); + + return config; +} + +static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_notch_node* pBPFNode = (ma_notch_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_notch_node_vtable = +{ + ma_notch_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->notch.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_notch_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->notch.channels; + baseNodeConfig.pOutputChannels = &pConfig->notch.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_notch2_reinit(pConfig, &pNotchNode->notch); +} + +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); +} + + + +/* +Peaking Filter Node +*/ +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_peak_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_peak_node* pBPFNode = (ma_peak_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_peak_node_vtable = +{ + ma_peak_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->peak.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); + if (result != MA_SUCCESS) { + ma_node_uninit(pNode, pAllocationCallbacks); + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_peak_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->peak.channels; + baseNodeConfig.pOutputChannels = &pConfig->peak.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_peak2_reinit(pConfig, &pPeakNode->peak); +} + +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); +} + + + +/* +Low Shelf Filter Node +*/ +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_loshelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_loshelf_node_vtable = +{ + ma_loshelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->loshelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); +} + +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); +} + + + +/* +High Shelf Filter Node +*/ +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_hishelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hishelf_node_vtable = +{ + ma_hishelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hishelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); +} + +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); +} + + + + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); + + return config; +} + + +static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_delay_node* pDelayNode = (ma_delay_node*)pNode; + + (void)pFrameCountIn; + + ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_delay_node_vtable = +{ + ma_delay_node_process_pcm_frames, + NULL, + 1, /* 1 input channels. */ + 1, /* 1 output channel. */ + MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ +}; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pDelayNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelayNode); + + result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); + if (result != MA_SUCCESS) { + return result; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_delay_node_vtable; + baseConfig.pInputChannels = &pConfig->delay.channels; + baseConfig.pOutputChannels = &pConfig->delay.channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); + if (result != MA_SUCCESS) { + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); + return result; + } + + return result; +} + +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelayNode == NULL) { + return; + } + + /* The base node is always uninitialized first. */ + ma_node_uninit(pDelayNode, pAllocationCallbacks); + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); +} + +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_wet(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_wet(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_dry(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_dry(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_decay(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_decay(&pDelayNode->delay); +} +#endif /* MA_NO_NODE_GRAPH */ + + +/* SECTION: miniaudio_engine.c */ +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +/************************************************************************************************************************************************************** + +Engine + +**************************************************************************************************************************************************************/ +#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) + + +static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) +{ + MA_ASSERT(pSound != NULL); + ma_atomic_exchange_32(&pSound->atEnd, atEnd); + + /* Fire any callbacks or events. */ + if (atEnd) { + if (pSound->endCallback != NULL) { + pSound->endCallback(pSound->pEndCallbackUserData, pSound); + } + } +} + +static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) +{ + MA_ASSERT(pSound != NULL); + return ma_atomic_load_32(&pSound->atEnd); +} + + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) +{ + ma_engine_node_config config; + + MA_ZERO_OBJECT(&config); + config.pEngine = pEngine; + config.type = type; + config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; + config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; + config.monoExpansionMode = pEngine->monoExpansionMode; + + return config; +} + + +static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) +{ + ma_bool32 isUpdateRequired = MA_FALSE; + float newPitch; + + MA_ASSERT(pEngineNode != NULL); + + newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); + + if (pEngineNode->oldPitch != newPitch) { + pEngineNode->oldPitch = newPitch; + isUpdateRequired = MA_TRUE; + } + + if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { + pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; + isUpdateRequired = MA_TRUE; + } + + if (isUpdateRequired) { + float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); + ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); + } +} + +static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ + return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); +} + +static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); +} + +static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) +{ + ma_uint64 inputFrameCount = 0; + + if (ma_engine_node_is_pitching_enabled(pEngineNode)) { + ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); + if (result != MA_SUCCESS) { + inputFrameCount = 0; + } + } else { + inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ + } + + return inputFrameCount; +} + +static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) +{ + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_float_set(&pEngineNode->volume, volume); + + /* If we're not smoothing we should bypass the volume gainer entirely. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + } else { + /* We're using volume smoothing, so apply the master volume to the gainer. */ + ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); + } + + return MA_SUCCESS; +} + +static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) +{ + if (pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = 0.0f; + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); + + return MA_SUCCESS; +} + + +static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + ma_uint32 totalFramesProcessedIn; + ma_uint32 totalFramesProcessedOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_bool32 isPitchingEnabled; + ma_bool32 isFadingEnabled; + ma_bool32 isSpatializationEnabled; + ma_bool32 isPanningEnabled; + ma_bool32 isVolumeSmoothingEnabled; + + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + + channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); + channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); + + totalFramesProcessedIn = 0; + totalFramesProcessedOut = 0; + + /* Update the fader if applicable. */ + { + ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); + if (fadeLengthInFrames != ~(ma_uint64)0) { + float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); + float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); + ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); + if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { + fadeStartOffsetInFrames = 0; + } else { + fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); + } + + ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); + + /* Reset the fade length so we don't erroneously apply it again. */ + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); + } + } + + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); + isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; + isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); + isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; + + /* Keep going while we've still got data available for processing. */ + while (totalFramesProcessedOut < frameCountOut) { + /* + We need to process in a specific order. We always do resampling first because it's likely + we're going to be increasing the channel count after spatialization. Also, I want to do + fading based on the output sample rate. + + We'll first read into a buffer from the resampler. Then we'll do all processing that + operates on the on the input channel count. We'll then get the spatializer to output to + the output buffer and then do all effects from that point directly in the output buffer + in-place. + + Note that we're always running the resampler if pitching is enabled, even when the pitch + is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch + when we move away from 1, back to 1, and then away from 1 again. We'll want to implement + any pitch=1 optimizations in the resampler itself. + + There's a small optimization here that we'll utilize since it might be a fairly common + case. When the input and output channel counts are the same, we'll read straight into the + output buffer from the resampler and do everything in-place. + */ + const float* pRunningFramesIn; + float* pRunningFramesOut; + float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; + ma_uint32 framesAvailableIn; + ma_uint32 framesAvailableOut; + ma_uint32 framesJustProcessedIn; + ma_uint32 framesJustProcessedOut; + ma_bool32 isWorkingBufferValid = MA_FALSE; + + framesAvailableIn = frameCountIn - totalFramesProcessedIn; + framesAvailableOut = frameCountOut - totalFramesProcessedOut; + + pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); + + if (channelsIn == channelsOut) { + /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ + pWorkingBuffer = pRunningFramesOut; + } else { + /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ + pWorkingBuffer = temp; + if (framesAvailableOut > tempCapInFrames) { + framesAvailableOut = tempCapInFrames; + } + } + + /* First is resampler. */ + if (isPitchingEnabled) { + ma_uint64 resampleFrameCountIn = framesAvailableIn; + ma_uint64 resampleFrameCountOut = framesAvailableOut; + + ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); + isWorkingBufferValid = MA_TRUE; + + framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; + framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; + } else { + framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); + framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ + } + + /* Fading. */ + if (isFadingEnabled) { + if (isWorkingBufferValid) { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ + } else { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + + /* + If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case + we'll want to apply our volume now. + */ + if (isVolumeSmoothingEnabled) { + if (isWorkingBufferValid) { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); + } else { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + + /* + If at this point we still haven't actually done anything with the working buffer we need + to just read straight from the input buffer. + */ + if (isWorkingBufferValid == MA_FALSE) { + pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ + } + + /* Spatialization. */ + if (isSpatializationEnabled) { + ma_uint32 iListener; + + /* + When determining the listener to use, we first check to see if the sound is pinned to a + specific listener. If so, we use that. Otherwise we just use the closest listener. + */ + if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { + iListener = pEngineNode->pinnedListenerIndex; + } else { + ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); + iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); + } + + ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); + } else { + /* No spatialization, but we still need to do channel conversion and master volume. */ + float volume; + ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ + + if (channelsIn == channelsOut) { + /* No channel conversion required. Just copy straight to the output buffer. */ + if (isVolumeSmoothingEnabled) { + /* Volume has already been applied. Just copy straight to the output buffer. */ + ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); + } else { + /* Volume has not been applied yet. Copy and apply volume in the same pass. */ + ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + } + } else { + /* Channel conversion required. TODO: Add support for channel maps here. */ + ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); + + /* If we're using smoothing, the volume will have already been applied. */ + if (!isVolumeSmoothingEnabled) { + ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + } + } + } + + /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ + + /* Panning. */ + if (isPanningEnabled) { + ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ + } + + /* We're done for this chunk. */ + totalFramesProcessedIn += framesJustProcessedIn; + totalFramesProcessedOut += framesJustProcessedOut; + + /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ + if (framesJustProcessedOut == 0) { + break; + } + } + + /* At this point we're done processing. */ + *pFrameCountIn = totalFramesProcessedIn; + *pFrameCountOut = totalFramesProcessedOut; +} + +static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ + ma_result result = MA_SUCCESS; + ma_sound* pSound = (ma_sound*)pNode; + ma_uint32 frameCount = *pFrameCountOut; + ma_uint32 totalFramesRead = 0; + ma_format dataSourceFormat; + ma_uint32 dataSourceChannels; + ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 tempCapInFrames; + ma_uint64 seekTarget; + + /* This is a data source node which means no input buses. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + /* If we're marked at the end we need to stop the sound and do nothing. */ + if (ma_sound_at_end(pSound)) { + ma_sound_stop(pSound); + *pFrameCountOut = 0; + return; + } + + /* If we're seeking, do so now before reading. */ + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); + + /* Any time-dependant effects need to have their times updated. */ + ma_node_set_time(pSound, seekTarget); + + ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + } + + /* + We want to update the pitch once. For sounds, this can be either at the start or at the end. If + we don't force this to only ever be updating once, we could end up in a situation where + retrieving the required input frame count ends up being different to what we actually retrieve. + What could happen is that the required input frame count is calculated, the pitch is update, + and then this processing function is called resulting in a different number of input frames + being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else + you'll hit the aforementioned bug. + */ + ma_engine_node_update_pitch_if_required(&pSound->engineNode); + + /* + For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ + from the main engine. + */ + result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); + if (result == MA_SUCCESS) { + tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); + + /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ + while (totalFramesRead < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesRead; + ma_uint32 framesToRead; + ma_uint64 framesJustRead; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + const float* pRunningFramesIn; + float* pRunningFramesOut; + + /* + The first thing we need to do is read into the temporary buffer. We can calculate exactly + how many input frames we'll need after resampling. + */ + framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); + + /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ + if (result == MA_AT_END) { + ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); + + frameCountIn = (ma_uint32)framesJustRead; + frameCountOut = framesRemaining; + + /* Convert if necessary. */ + if (dataSourceFormat == ma_format_f32) { + /* Fast path. No data conversion necessary. */ + pRunningFramesIn = (float*)temp; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } else { + /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ + float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ + ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); + + /* Now that we have our samples in f32 format we can process like normal. */ + pRunningFramesIn = tempf32; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } + + /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ + MA_ASSERT(frameCountIn == framesJustRead); + totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ + + if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { + break; /* Might have reached the end. */ + } + } + } + + *pFrameCountOut = totalFramesRead; +} + +static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* + Make sure the pitch is updated before trying to read anything. It's important that this is done + only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that + ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), + and if another thread modifies the pitch just after that call it can result in a glitch due to + the input rate changing. + */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + /* For groups, the input data has already been read and we just need to apply the effect. */ + ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); +} + +static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + MA_ASSERT(pInputFrameCount != NULL); + + /* Our pitch will affect this calculation. We need to update it. */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); + if (inputFrameCount > 0xFFFFFFFF) { + inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ + } + + *pInputFrameCount = (ma_uint32)inputFrameCount; + + return MA_SUCCESS; +} + + +static ma_node_vtable g_ma_engine_node_vtable__sound = +{ + ma_engine_node_process_pcm_frames__sound, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ + 1, /* Sounds have one output bus. */ + 0 /* Default flags. */ +}; + +static ma_node_vtable g_ma_engine_node_vtable__group = +{ + ma_engine_node_process_pcm_frames__group, + ma_engine_node_get_required_input_frame_count__group, + 1, /* Groups have one input bus. */ + 1, /* Groups have one output bus. */ + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ +}; + + + +static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) +{ + ma_node_config baseNodeConfig; + + if (pConfig->type == ma_engine_node_type_sound) { + /* Sound. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; + baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ + } else { + /* Group. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; + baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ + } + + return baseNodeConfig; +} + +static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) +{ + return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); +} + +typedef struct +{ + size_t sizeInBytes; + size_t baseNodeOffset; + size_t resamplerOffset; + size_t spatializerOffset; + size_t gainerOffset; +} ma_engine_node_heap_layout; + +static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) +{ + ma_result result; + size_t tempHeapSize; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_spatializer_config spatializerConfig; + ma_gainer_config gainerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ + + MA_ASSERT(pHeapLayout); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pEngine == NULL) { + return MA_INVALID_ARGS; /* An engine must be specified. */ + } + + pHeapLayout->sizeInBytes = 0; + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the base node. */ + } + + pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Resmapler. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ + resamplerConfig.lpfOrder = 0; + + result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the resampler. */ + } + + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Spatializer. */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + + result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the spatializer. */ + } + + pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Gainer. Will not be used if we are not using smoothing. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + } + + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_fader_config faderConfig; + ma_spatializer_config spatializerConfig; + ma_panner_config pannerConfig; + ma_gainer_config gainerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngineNode); + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { + return MA_INVALID_ARGS; /* Invalid listener. */ + } + + pEngineNode->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pEngineNode->pEngine = pConfig->pEngine; + pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; + ma_atomic_float_set(&pEngineNode->volume, 1); + pEngineNode->pitch = 1; + pEngineNode->oldPitch = 1; + pEngineNode->oldDopplerPitch = 1; + pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; + pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; + pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + /* + If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler + is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. + */ + if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { + pEngineNode->isPitchDisabled = MA_FALSE; + } + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); + if (result != MA_SUCCESS) { + goto error0; + } + + + /* + We can now initialize the effects we need in order to implement the engine node. There's a + defined order of operations here, mainly centered around when we convert our channels from the + data source's native channel count to the engine's channel count. As a rule, we want to do as + much computation as possible before spatialization because there's a chance that will increase + the channel count, thereby increasing the amount of work needing to be done to process. + */ + + /* We'll always do resampling first. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); + resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ + + result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); + if (result != MA_SUCCESS) { + goto error1; + } + + + /* After resampling will come the fader. */ + faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); + + result = ma_fader_init(&faderConfig, &pEngineNode->fader); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to + ensure channels counts link up correctly in the node graph. + */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; + + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + + result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't + be able to pan mono sounds. + */ + pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); + + result = ma_panner_init(&pannerConfig, &pEngineNode->panner); + if (result != MA_SUCCESS) { + goto error3; + } + + + /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); + if (result != MA_SUCCESS) { + goto error3; + } + } + + + return MA_SUCCESS; + + /* No need for allocation callbacks here because we use a preallocated heap. */ +error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); +error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); +error1: ma_node_uninit(&pEngineNode->baseNode, NULL); +error0: return result; +} + +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pEngineNode->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* + The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we + destroy anything that might be in the middle of being used by the processing function. + */ + ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); + + /* Now that the node has been uninitialized we can safely uninitialize the rest. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { + ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); + } + + ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); + ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); + + /* Free the heap last. */ + if (pEngineNode->_ownsHeap) { + ma_free(pEngineNode->_pHeap, pAllocationCallbacks); + } +} + + +MA_API ma_sound_config ma_sound_config_init(void) +{ + return ma_sound_config_init_2(NULL); +} + +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) +{ + ma_sound_config config; + + MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + + config.rangeEndInPCMFrames = ~((ma_uint64)0); + config.loopPointEndInPCMFrames = ~((ma_uint64)0); + + return config; +} + +MA_API ma_sound_group_config ma_sound_group_config_init(void) +{ + return ma_sound_group_config_init_2(NULL); +} + +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) +{ + ma_sound_group_config config; + + MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + + return config; +} + + +MA_API ma_engine_config ma_engine_config_init(void) +{ + ma_engine_config config; + + MA_ZERO_OBJECT(&config); + config.listenerCount = 1; /* Always want at least one listener. */ + config.monoExpansionMode = ma_mono_expansion_mode_default; + + return config; +} + + +#if !defined(MA_NO_DEVICE_IO) +static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_engine* pEngine = (ma_engine*)pDevice->pUserData; + + (void)pFramesIn; + + /* + Experiment: Try processing a resource manager job if we're on the Emscripten build. + + This serves two purposes: + + 1) It ensures jobs are actually processed at some point since we cannot guarantee that the + caller is doing the right thing and calling ma_resource_manager_process_next_job(); and + + 2) It's an attempt at working around an issue where processing jobs on the Emscripten main + loop doesn't work as well as it should. When trying to load sounds without the `DECODE` + flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time + before the callback is processed. I think it's got something to do with the single- + threaded nature of Web, but I'm not entirely sure. + */ + #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) + { + if (pEngine->pResourceManager != NULL) { + if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + ma_resource_manager_process_next_job(pEngine->pResourceManager); + } + } + } + #endif + + ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); +} +#endif + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) +{ + ma_result result; + ma_node_graph_config nodeGraphConfig; + ma_engine_config engineConfig; + ma_spatializer_listener_config listenerConfig; + ma_uint32 iListener; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngine); + + /* The config is allowed to be NULL in which case we use defaults for everything. */ + if (pConfig != NULL) { + engineConfig = *pConfig; + } else { + engineConfig = ma_engine_config_init(); + } + + pEngine->monoExpansionMode = engineConfig.monoExpansionMode; + pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; + pEngine->onProcess = engineConfig.onProcess; + pEngine->pProcessUserData = engineConfig.pProcessUserData; + ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + pEngine->pResourceManager = engineConfig.pResourceManager; + } + #endif + + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pDevice = engineConfig.pDevice; + + /* If we don't have a device, we need one. */ + if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { + ma_device_config deviceConfig; + + pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); + if (pEngine->pDevice == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; + deviceConfig.playback.format = ma_format_f32; + deviceConfig.playback.channels = engineConfig.channels; + deviceConfig.sampleRate = engineConfig.sampleRate; + deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; + deviceConfig.pUserData = pEngine; + deviceConfig.notificationCallback = engineConfig.notificationCallback; + deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; + deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; + deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ + deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ + + if (engineConfig.pContext == NULL) { + ma_context_config contextConfig = ma_context_config_init(); + contextConfig.allocationCallbacks = pEngine->allocationCallbacks; + contextConfig.pLog = engineConfig.pLog; + + /* If the engine config does not specify a log, use the resource manager's if we have one. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { + contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); + } + } + #endif + + result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); + } else { + result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); + } + + if (result != MA_SUCCESS) { + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + pEngine->pDevice = NULL; + return result; + } + + pEngine->ownsDevice = MA_TRUE; + } + + /* Update the channel count and sample rate of the engine config so we can reference it below. */ + if (pEngine->pDevice != NULL) { + engineConfig.channels = pEngine->pDevice->playback.channels; + engineConfig.sampleRate = pEngine->pDevice->sampleRate; + } + } + #endif + + if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { + return MA_INVALID_ARGS; + } + + pEngine->sampleRate = engineConfig.sampleRate; + + /* The engine always uses either the log that was passed into the config, or the context's log is available. */ + if (engineConfig.pLog != NULL) { + pEngine->pLog = engineConfig.pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pLog = ma_device_get_log(pEngine->pDevice); + } + #else + { + pEngine->pLog = NULL; + } + #endif + } + + + /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ + nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); + nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; + + result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); + if (result != MA_SUCCESS) { + goto on_error_1; + } + + + /* We need at least one listener. */ + if (engineConfig.listenerCount == 0) { + engineConfig.listenerCount = 1; + } + + if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { + result = MA_INVALID_ARGS; /* Too many listeners. */ + goto on_error_1; + } + + for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { + listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); + + /* + If we're using a device, use the device's channel map for the listener. Otherwise just use + miniaudio's default channel map. + */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + /* + Temporarily disabled. There is a subtle bug here where front-left and front-right + will be used by the device's channel map, but this is not what we want to use for + spatialization. Instead we want to use side-left and side-right. I need to figure + out a better solution for this. For now, disabling the use of device channel maps. + */ + /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ + } + } + #endif + + result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ + if (result != MA_SUCCESS) { + goto on_error_2; + } + + pEngine->listenerCount += 1; + } + + + /* Gain smoothing for spatialized sounds. */ + pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; + if (pEngine->gainSmoothTimeInFrames == 0) { + ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; + if (gainSmoothTimeInMilliseconds == 0) { + gainSmoothTimeInMilliseconds = 8; + } + + pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ + } + + + /* We need a resource manager. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (pEngine->pResourceManager == NULL) { + ma_resource_manager_config resourceManagerConfig; + + pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); + if (pEngine->pResourceManager == NULL) { + result = MA_OUT_OF_MEMORY; + goto on_error_2; + } + + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ + resourceManagerConfig.decodedFormat = ma_format_f32; + resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ + resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); + ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); + resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; + + /* The Emscripten build cannot use threads. */ + #if defined(MA_EMSCRIPTEN) + { + resourceManagerConfig.jobThreadCount = 0; + resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); + if (result != MA_SUCCESS) { + goto on_error_3; + } + + pEngine->ownsResourceManager = MA_TRUE; + } + } + #endif + + /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ + pEngine->inlinedSoundLock = 0; + pEngine->pInlinedSoundHead = NULL; + + /* Start the engine if required. This should always be the last step. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { + result = ma_engine_start(pEngine); + if (result != MA_SUCCESS) { + goto on_error_4; /* Failed to start the engine. */ + } + } + } + #endif + + return MA_SUCCESS; + +#if !defined(MA_NO_DEVICE_IO) +on_error_4: +#endif +#if !defined(MA_NO_RESOURCE_MANAGER) +on_error_3: + if (pEngine->ownsResourceManager) { + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif /* MA_NO_RESOURCE_MANAGER */ +on_error_2: + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); +on_error_1: + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } + } + #endif + + return result; +} + +MA_API void ma_engine_uninit(ma_engine* pEngine) +{ + ma_uint32 iListener; + + if (pEngine == NULL) { + return; + } + + /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } else { + if (pEngine->pDevice != NULL) { + ma_device_stop(pEngine->pDevice); + } + } + } + #endif + + /* + All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case + I want to do some kind of garbage collection later on. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + for (;;) { + ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; + if (pSoundToDelete == NULL) { + break; /* Done. */ + } + + pEngine->pInlinedSoundHead = pSoundToDelete->pNext; + + ma_sound_uninit(&pSoundToDelete->sound); + ma_free(pSoundToDelete, &pEngine->allocationCallbacks); + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); + + /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pEngine->ownsResourceManager) { + ma_resource_manager_uninit(pEngine->pResourceManager); + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif +} + +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result; + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (pEngine->onProcess) { + pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ + } + + return MA_SUCCESS; +} + +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + return &pEngine->nodeGraph; +} + +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + return pEngine->pResourceManager; + } + #else + { + return NULL; + } + #endif +} +#endif + +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_DEVICE_IO) + { + return pEngine->pDevice; + } + #else + { + return NULL; + } + #endif +} + +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + if (pEngine->pLog != NULL) { + return pEngine->pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + return ma_device_get_log(ma_engine_get_device(pEngine)); + } + #else + { + return NULL; + } + #endif + } +} + +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) +{ + return ma_node_graph_get_endpoint(&pEngine->nodeGraph); +} + +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) +{ + return ma_node_graph_get_time(&pEngine->nodeGraph); +} + +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); +} + +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) +{ + return ma_node_graph_get_channels(&pEngine->nodeGraph); +} + +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->sampleRate; +} + + +MA_API ma_result ma_engine_start(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_start(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_stop(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_stop(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) +{ + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); +} + +MA_API float ma_engine_get_volume(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); +} + +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +{ + return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); +} + +MA_API float ma_engine_get_gain_db(ma_engine* pEngine) +{ + return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); +} + + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->listenerCount; +} + +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) +{ + ma_uint32 iListener; + ma_uint32 iListenerClosest; + float closestLen2 = MA_FLT_MAX; + + if (pEngine == NULL || pEngine->listenerCount == 1) { + return 0; + } + + iListenerClosest = 0; + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + if (ma_engine_listener_is_enabled(pEngine, iListener)) { + float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); + if (closestLen2 > len2) { + closestLen2 = len2; + iListenerClosest = iListener; + } + } + } + + MA_ASSERT(iListenerClosest < 255); + return iListenerClosest; +} + +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); +} + +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return MA_FALSE; + } + + return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); +} + + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_sound_inlined* pSound = NULL; + ma_sound_inlined* pNextSound = NULL; + + if (pEngine == NULL || pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + /* Attach to the endpoint node if nothing is specicied. */ + if (pNode == NULL) { + pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); + nodeInputBusIndex = 0; + } + + /* + We want to check if we can recycle an already-allocated inlined sound. Since this is just a + helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep + the implementation simple. Maybe this can be optimized later if there's enough demand, but + if this function is being used it probably means the caller doesn't really care too much. + + What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise + we just keep iterating. If we reach the end without finding a sound to recycle we just + allocate a new one. This doesn't scale well for a massive number of sounds being played + simultaneously as we don't ever actually free the sound objects. Some kind of garbage + collection routine might be valuable for this which I'll think about. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + ma_uint32 soundFlags = 0; + + for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { + if (ma_sound_at_end(&pNextSound->sound)) { + /* + The sound is at the end which means it's available for recycling. All we need to do + is uninitialize it and reinitialize it. All we're doing is recycling memory. + */ + pSound = pNextSound; + ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + break; + } + } + + if (pSound != NULL) { + /* + We actually want to detach the sound from the list here. The reason is because we want the sound + to be in a consistent state at the non-recycled case to simplify the logic below. + */ + if (pEngine->pInlinedSoundHead == pSound) { + pEngine->pInlinedSoundHead = pSound->pNext; + } + + if (pSound->pPrev != NULL) { + pSound->pPrev->pNext = pSound->pNext; + } + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound->pPrev; + } + + /* Now the previous sound needs to be uninitialized. */ + ma_sound_uninit(&pNextSound->sound); + } else { + /* No sound available for recycling. Allocate one now. */ + pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); + } + + if (pSound != NULL) { /* Safety check for the allocation above. */ + /* + At this point we should have memory allocated for the inlined sound. We just need + to initialize it like a normal sound now. + */ + soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ + soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ + soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ + soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ + + result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); + if (result == MA_SUCCESS) { + /* Now attach the sound to the graph. */ + result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); + if (result == MA_SUCCESS) { + /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ + pSound->pNext = pEngine->pInlinedSoundHead; + pSound->pPrev = NULL; + + pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound; + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + result = MA_OUT_OF_MEMORY; + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + if (result != MA_SUCCESS) { + return result; + } + + /* Finally we can start playing the sound. */ + result = ma_sound_start(&pSound->sound); + if (result != MA_SUCCESS) { + /* Failed to start the sound. We need to mark it for recycling and return an error. */ + ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + return result; + } + + ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + return result; +} + +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) +{ + return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); +} +#endif + + +static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSound); + pSound->seekTarget = MA_SEEK_TARGET_NONE; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + ma_engine_node_config engineNodeConfig; + ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ + + /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ + MA_ASSERT(pEngine != NULL); + MA_ASSERT(pSound != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pSound->pDataSource = pConfig->pDataSource; + + if (pConfig->pDataSource != NULL) { + type = ma_engine_node_type_sound; + } else { + type = ma_engine_node_type_group; + } + + /* + Sounds are engine nodes. Before we can initialize this we need to determine the channel count. + If we can't do this we need to abort. It's up to the caller to ensure they're using a data + source that provides this information upfront. + */ + engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + + if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { + engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; + } + + /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ + if (pConfig->pDataSource != NULL) { + result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the channel count. */ + } + + if (engineNodeConfig.channelsIn == 0) { + return MA_INVALID_OPERATION; /* Invalid channel count. */ + } + + if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { + engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; + } + } + + + /* Getting here means we should have a valid channel count and we can initialize the engine node. */ + result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); + if (result != MA_SUCCESS) { + return result; + } + + /* If no attachment is specified, attach the sound straight to the endpoint. */ + if (pConfig->pInitialAttachment == NULL) { + /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ + if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { + result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); + } + } else { + /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ + result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); + } + + if (result != MA_SUCCESS) { + ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); + return result; + } + + + /* Apply initial range and looping state to the data source if applicable. */ + if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + ma_sound_set_looping(pSound, pConfig->isLooping); + + return MA_SUCCESS; +} + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result = MA_SUCCESS; + ma_uint32 flags; + ma_sound_config config; + ma_resource_manager_pipeline_notifications notifications; + + /* + The engine requires knowledge of the channel count of the underlying data source before it can + initialize the sound. Therefore, we need to make the resource manager wait until initialization + of the underlying data source to be initialized so we can get access to the channel count. To + do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. + + Because we're initializing the data source before the sound, there's a chance the notification + will get triggered before this function returns. This is OK, so long as the caller is aware of + it and can avoid accessing the sound from within the notification. + */ + flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; + + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* Removed in 0.12. Set pDoneFence on the notifications. */ + notifications = pConfig->initNotifications; + if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { + notifications.done.pFence = pConfig->pDoneFence; + } + + /* + We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does + not return prematurely before the sound has finished initializing. + */ + if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } + { + ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); + resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; + resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; + resourceManagerDataSourceConfig.flags = flags; + resourceManagerDataSourceConfig.pNotifications = ¬ifications; + resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; + resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; + + result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + goto done; + } + + pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ + + /* We need to use a slightly customized version of the config so we'll need to make a copy. */ + config = *pConfig; + config.pFilePath = NULL; + config.pFilePathW = NULL; + config.pDataSource = pSound->pResourceManagerDataSource; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + goto done; + } + } +done: + if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } + return result; +} + +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); + config.pFilePath = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_result result; + ma_sound_config config; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pExistingSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ + if (pExistingSound->pResourceManagerDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* + We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) + this will fail. + */ + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + return result; + } + + config = ma_sound_config_init_2(pEngine); + config.pDataSource = pSound->pResourceManagerDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + return result; + } + + /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ + pSound->ownsDataSource = MA_TRUE; + + return MA_SUCCESS; +} +#endif + +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_sound_config config = ma_sound_config_init_2(pEngine); + config.pDataSource = pDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pSound->endCallback = pConfig->endCallback; + pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; + + /* We need to load the sound differently depending on whether or not we're loading from a file. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { + return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); + } else +#endif + { + /* + Getting here means we're not loading from a file. We may be loading from an already-initialized + data source, or none at all. If we aren't specifying any data source, we'll be initializing the + the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this + for us, so no special treatment required here. + */ + return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); + } +} + +MA_API void ma_sound_uninit(ma_sound* pSound) +{ + if (pSound == NULL) { + return; + } + + /* + Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done + so which makes thread safety beyond this point trivial. + */ + ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); + + /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pSound->ownsDataSource) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); + pSound->pDataSource = NULL; + } +#else + MA_ASSERT(pSound->ownsDataSource == MA_FALSE); +#endif +} + +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->engineNode.pEngine; +} + +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->pDataSource; +} + +MA_API ma_result ma_sound_start(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* If the sound is already playing, do nothing. */ + if (ma_sound_is_playing(pSound)) { + return MA_SUCCESS; + } + + /* If the sound is at the end it means we want to start from the start again. */ + if (ma_sound_at_end(pSound)) { + ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); + if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { + return result; /* Failed to seek back to the start. */ + } + + /* Make sure we clear the end indicator. */ + ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); + } + + /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ + ma_node_set_state(pSound, ma_node_state_started); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ + ma_node_set_state(pSound, ma_node_state_stopped); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint64 sampleRate; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) +{ + if (pSound == NULL) { + return; + } + + ma_engine_node_set_volume(&pSound->engineNode, volume); +} + +MA_API float ma_sound_get_volume(const ma_sound* pSound) +{ + float volume = 0; + + if (pSound == NULL) { + return 0; + } + + ma_engine_node_get_volume(&pSound->engineNode, &volume); + + return volume; +} + +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_pan(&pSound->engineNode.panner, pan); +} + +MA_API float ma_sound_get_pan(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_panner_get_pan(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_mode(&pSound->engineNode.panner, panMode); +} + +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_pan_mode_balance; + } + + return ma_panner_get_mode(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) +{ + if (pSound == NULL) { + return; + } + + if (pitch <= 0) { + return; + } + + ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); +} + +MA_API float ma_sound_get_pitch(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ +} + +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) +{ + if (pSound == NULL) { + return; + } + + ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); +} + +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); +} + +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) +{ + if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { + return; + } + + ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); +} + +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_LISTENER_INDEX_CLOSEST; + } + + return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); +} + +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) +{ + ma_uint32 listenerIndex; + + if (pSound == NULL) { + return 0; + } + + listenerIndex = ma_sound_get_pinned_listener_index(pSound); + if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { + ma_vec3f position = ma_sound_get_position(pSound); + return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); + } + + return listenerIndex; +} + +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) +{ + ma_vec3f relativePos; + ma_engine* pEngine; + + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + pEngine = ma_sound_get_engine(pSound); + if (pEngine == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); + + return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); +} + +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_position(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_direction(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_attenuation_model_none; + } + + return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); +} + +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_positioning_absolute; + } + + return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); +} + +MA_API float ma_sound_get_rolloff(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); +} + +MA_API float ma_sound_get_min_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); +} + +MA_API float ma_sound_get_max_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); +} + +MA_API float ma_sound_get_min_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); +} + +MA_API float ma_sound_get_max_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + if (pSound == NULL) { + return; + } + + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); +} + +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); +} + +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 1; + } + + return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); +} + + +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); +} + +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); +} + +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + /* + We don't want to update the fader at this point because we need to use the engine's current time + to derive the fader's start offset. The timer is being updated on the audio thread so in order to + do this as accurately as possible we'll need to defer this to the audio thread. + */ + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); +} + +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + return ma_fader_get_current_volume(&pSound->engineNode.fader); +} + +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); +} + +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + if (fadeLengthInFrames > 0) { + if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { + fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); + } + + ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; +} + +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_node_get_time(pSound); +} + +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) +{ + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); +} + +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) +{ + if (pSound == NULL) { + return; + } + + /* Looping is only a valid concept if the sound is backed by a data source. */ + if (pSound->pDataSource == NULL) { + return; + } + + /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ + ma_data_source_set_looping(pSound->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of looping for sounds that are not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pSound->pDataSource); +} + +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of an end of a sound if it's not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return ma_sound_get_at_end(pSound); +} + +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Seeking is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ + ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ + if (pSound->pDataSource == NULL) { + ma_uint32 channels; + + if (pFormat != NULL) { + *pFormat = ma_format_f32; + } + + channels = ma_node_get_input_channels(&pSound->engineNode, 0); + if (pChannels != NULL) { + *pChannels = channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); + } + + return MA_SUCCESS; + } else { + return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) +{ + ma_uint64 seekTarget; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a cursor is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + *pCursor = seekTarget; + return MA_SUCCESS; + } else { + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + } +} + +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); +} + +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) +{ + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor != NULL) { + *pCursor = 0; + } + + result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); +} + +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of an end is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + pSound->endCallback = callback; + pSound->pEndCallbackUserData = pUserData; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) +{ + ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); + config.flags = flags; + config.pInitialAttachment = pParentGroup; + return ma_sound_group_init_ex(pEngine, &config, pGroup); +} + +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) +{ + ma_sound_config soundConfig; + + if (pGroup == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGroup); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* A sound group is just a sound without a data source. */ + soundConfig = *pConfig; + soundConfig.pFilePath = NULL; + soundConfig.pFilePathW = NULL; + soundConfig.pDataSource = NULL; + + /* + Groups need to have spatialization disabled by default because I think it'll be pretty rare + that programs will want to spatialize groups (but not unheard of). Certainly it feels like + disabling this by default feels like the right option. Spatialization can be enabled with a + call to ma_sound_group_set_spatialization_enabled(). + */ + soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; + + return ma_sound_init_ex(pEngine, &soundConfig, pGroup); +} + +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) +{ + ma_sound_uninit(pGroup); +} + +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) +{ + return ma_sound_get_engine(pGroup); +} + +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) +{ + return ma_sound_start(pGroup); +} + +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) +{ + return ma_sound_stop(pGroup); +} + +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) +{ + ma_sound_set_volume(pGroup, volume); +} + +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) +{ + return ma_sound_get_volume(pGroup); +} + +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) +{ + ma_sound_set_pan(pGroup, pan); +} + +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan(pGroup); +} + +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) +{ + ma_sound_set_pan_mode(pGroup, panMode); +} + +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan_mode(pGroup); +} + +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) +{ + ma_sound_set_pitch(pGroup, pitch); +} + +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) +{ + return ma_sound_get_pitch(pGroup); +} + +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) +{ + ma_sound_set_spatialization_enabled(pGroup, enabled); +} + +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) +{ + return ma_sound_is_spatialization_enabled(pGroup); +} + +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) +{ + ma_sound_set_pinned_listener_index(pGroup, listenerIndex); +} + +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_pinned_listener_index(pGroup); +} + +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_listener_index(pGroup); +} + +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction_to_listener(pGroup); +} + +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_position(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) +{ + return ma_sound_get_position(pGroup); +} + +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_direction(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction(pGroup); +} + +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_velocity(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) +{ + return ma_sound_get_velocity(pGroup); +} + +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) +{ + ma_sound_set_attenuation_model(pGroup, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) +{ + return ma_sound_get_attenuation_model(pGroup); +} + +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) +{ + ma_sound_set_positioning(pGroup, positioning); +} + +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) +{ + return ma_sound_get_positioning(pGroup); +} + +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) +{ + ma_sound_set_rolloff(pGroup, rolloff); +} + +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) +{ + return ma_sound_get_rolloff(pGroup); +} + +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) +{ + ma_sound_set_min_gain(pGroup, minGain); +} + +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_gain(pGroup); +} + +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) +{ + ma_sound_set_max_gain(pGroup, maxGain); +} + +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_gain(pGroup); +} + +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) +{ + ma_sound_set_min_distance(pGroup, minDistance); +} + +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_distance(pGroup); +} + +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) +{ + ma_sound_set_max_distance(pGroup, maxDistance); +} + +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_distance(pGroup); +} + +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) +{ + ma_sound_set_doppler_factor(pGroup, dopplerFactor); +} + +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_doppler_factor(pGroup); +} + +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) +{ + ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); +} + +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_directional_attenuation_factor(pGroup); +} + +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); +} + +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); +} + +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) +{ + return ma_sound_get_current_fade_volume(pGroup); +} + +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) +{ + return ma_sound_is_playing(pGroup); +} + +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) +{ + return ma_sound_get_time_in_pcm_frames(pGroup); +} +#endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.c */ + + + +/************************************************************************************************************************************************************** +*************************************************************************************************************************************************************** + +Auto Generated +============== +All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the +code below please report the bug to the respective repository for the relevant project (probably dr_libs). + +*************************************************************************************************************************************************************** +**************************************************************************************************************************************************************/ +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) +#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_wav_c begin */ +#ifndef ma_dr_wav_c +#define ma_dr_wav_c +#ifdef __MRC__ +#pragma options opt off +#endif +#include +#include +#include +#ifndef MA_DR_WAV_NO_STDIO +#include +#ifndef MA_DR_WAV_NO_WCHAR +#include +#endif +#endif +#ifndef MA_DR_WAV_ASSERT +#include +#define MA_DR_WAV_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_WAV_MALLOC +#define MA_DR_WAV_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_WAV_REALLOC +#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_WAV_FREE +#define MA_DR_WAV_FREE(p) free((p)) +#endif +#ifndef MA_DR_WAV_COPY_MEMORY +#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_WAV_ZERO_MEMORY +#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef MA_DR_WAV_ZERO_OBJECT +#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) +#endif +#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) +#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 +#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) +#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #endif +#endif +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_WAV_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_WAV_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_WAV_VERSION_REVISION; + } +} +MA_API const char* ma_dr_wav_version_string(void) +{ + return MA_DR_WAV_VERSION_STRING; +} +#ifndef MA_DR_WAV_MAX_SAMPLE_RATE +#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 +#endif +#ifndef MA_DR_WAV_MAX_CHANNELS +#define MA_DR_WAV_MAX_CHANNELS 256 +#endif +#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE +#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 +#endif +static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; +static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static MA_INLINE int ma_dr_wav__is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} +static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) +{ + int i; + for (i = 0; i < 16; ++i) { + guid[i] = data[i]; + } +} +static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} +static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} +static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); +#endif +} +static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) +{ + return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); + } +} +static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) +{ + ma_uint8 t; + t = p[0]; + p[0] = p[2]; + p[2] = t; +} +static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ma_uint8* pSample = pSamples + (iSample*3); + ma_dr_wav__bswap_s24(pSample); + } +} +static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) +{ + return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); + } +} +static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) +{ + return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); + } +} +static MA_INLINE float ma_dr_wav__bswap_f32(float n) +{ + union { + ma_uint32 i; + float f; + } x; + x.f = n; + x.i = ma_dr_wav__bswap32(x.i); + return x.f; +} +static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); + } +} +static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) +{ + switch (bytesPerSample) + { + case 1: + { + } break; + case 2: + { + ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); + } break; + case 3: + { + ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); + } break; + case 4: + { + ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); + } break; + case 8: + { + ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); + } break; + default: + { + MA_DR_WAV_ASSERT(MA_FALSE); + } break; + } +} +MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) +{ + if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { + return MA_TRUE; + } else { + return MA_FALSE; + } +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) +{ + return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u16_be(data); + } else { + return ma_dr_wav_bytes_to_u16_le(data); + } +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) +{ + return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) +{ + return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u32_be(data); + } else { + return ma_dr_wav_bytes_to_u32_le(data); + } +} +MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) +{ + ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; + ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); + ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); + ma_uint64 significand = (hi << 32) | lo; + int sign = exponent >> 15; + exponent &= 0x7FFF; + if (exponent == 0 && significand == 0) { + return 0; + } else if (exponent == 0x7FFF) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } + exponent -= 16383; + if (exponent > 63) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } else if (exponent < 1) { + return 0; + } + significand >>= (63 - exponent); + if (sign) { + return -(ma_int64)significand; + } else { + return (ma_int64)significand; + } +} +MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_WAV_MALLOC(sz); +} +MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_WAV_REALLOC(p, sz); +} +MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_WAV_FREE(p); +} +MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_WAV_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return *pAllocationCallbacks; + } else { + ma_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; + allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; + allocationCallbacks.onFree = ma_dr_wav__free_default; + return allocationCallbacks; + } +} +static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) +{ + return + formatTag == MA_DR_WAVE_FORMAT_ADPCM || + formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; +} +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 2); +} +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 8); +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); +MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) +{ + if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { + ma_uint8 sizeInBytes[4]; + if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { + return MA_AT_END; + } + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return MA_INVALID_FILE; + } + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 8; + } else if (container == ma_dr_wav_container_w64) { + ma_uint8 sizeInBytes[8]; + if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { + return MA_AT_END; + } + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return MA_INVALID_FILE; + } + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 24; + } else { + return MA_INVALID_FILE; + } + return MA_SUCCESS; +} +MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +{ + ma_uint64 bytesRemainingToSeek = offset; + while (bytesRemainingToSeek > 0) { + if (bytesRemainingToSeek > 0x7FFFFFFF) { + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + bytesRemainingToSeek -= 0x7FFFFFFF; + } else { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + bytesRemainingToSeek = 0; + } + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +{ + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); + } + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + for (;;) { + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); + } + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + } +} +MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +{ + size_t bytesRead; + MA_DR_WAV_ASSERT(onRead != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); + bytesRead = onRead(pUserData, pBufferOut, bytesToRead); + *pCursor += bytesRead; + return bytesRead; +} +#if 0 +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) +{ + MA_DR_WAV_ASSERT(onSeek != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); + if (!onSeek(pUserData, offset, origin)) { + return MA_FALSE; + } + if (origin == ma_dr_wav_seek_origin_start) { + *pCursor = offset; + } else { + *pCursor += offset; + } + return MA_TRUE; +} +#endif +#define MA_DR_WAV_SMPL_BYTES 36 +#define MA_DR_WAV_SMPL_LOOP_BYTES 24 +#define MA_DR_WAV_INST_BYTES 7 +#define MA_DR_WAV_ACID_BYTES 24 +#define MA_DR_WAV_CUE_BYTES 4 +#define MA_DR_WAV_BEXT_BYTES 602 +#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 +#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 +#define MA_DR_WAV_BEXT_UMID_BYTES 64 +#define MA_DR_WAV_CUE_POINT_BYTES 24 +#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 +#define MA_DR_WAV_METADATA_ALIGNMENT 8 +typedef enum +{ + ma_dr_wav__metadata_parser_stage_count, + ma_dr_wav__metadata_parser_stage_read +} ma_dr_wav__metadata_parser_stage; +typedef struct +{ + ma_dr_wav_read_proc onRead; + ma_dr_wav_seek_proc onSeek; + void *pReadSeekUserData; + ma_dr_wav__metadata_parser_stage stage; + ma_dr_wav_metadata *pMetadata; + ma_uint32 metadataCount; + ma_uint8 *pData; + ma_uint8 *pDataCursor; + ma_uint64 metadataCursor; + ma_uint64 extraCapacity; +} ma_dr_wav__metadata_parser; +MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) +{ + ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > MA_SIZE_MAX) { + return 0; + } + return (size_t)cap; +} +MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) +{ + ma_uint8* pResult; + if (align) { + ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; + if (modulo != 0) { + pParser->pDataCursor += align - modulo; + } + } + pResult = pParser->pDataCursor; + MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); + pParser->pDataCursor += size; + return pResult; +} +MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) +{ + size_t extra = bytes + (align ? (align - 1) : 0); + pParser->extraCapacity += extra; +} +MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { + pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); + pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pDataCursor = pParser->pData; + if (pParser->pData == NULL) { + return MA_OUT_OF_MEMORY; + } + pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); + pParser->metadataCursor = 0; + } + return MA_SUCCESS; +} +MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +{ + if (pCursor != NULL) { + return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + } else { + return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); + } +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + MA_DR_WAV_ASSERT(pChunkHeader != NULL); + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { + ma_uint32 iSampleLoop; + pMetadata->type = ma_dr_wav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); + for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { + ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + if (bytesJustRead == sizeof(smplLoopData)) { + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); + } else { + break; + } + } + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + } + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(cueHeaderSectionData)) { + pMetadata->type = ma_dr_wav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); + MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + if (pMetadata->data.cue.cuePointCount > 0) { + ma_uint32 iCuePoint; + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + if (bytesJustRead == sizeof(cuePointData)) { + pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); + } else { + break; + } + } + } + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 instData[MA_DR_WAV_INST_BYTES]; + ma_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(instData)) { + pMetadata->type = ma_dr_wav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; + pMetadata->data.inst.lowNote = (ma_int8)instData[3]; + pMetadata->data.inst.highNote = (ma_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; + pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; + ma_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(acidData)) { + pMetadata->type = ma_dr_wav_metadata_type_acid; + pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); + } + return bytesRead; +} +MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) +{ + size_t result = 0; + while (*str++) { + result += 1; + } + return result; +} +MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) +{ + size_t result = 0; + while (*str++ && result < maxToRead) { + result += 1; + } + return result; +} +MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) +{ + size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); + if (len) { + char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); + MA_DR_WAV_ASSERT(result != NULL); + MA_DR_WAV_COPY_MEMORY(result, str, len); + result[len] = '\0'; + return result; + } else { + return NULL; + } +} +typedef struct +{ + const void* pBuffer; + size_t sizeInBytes; + size_t cursor; +} ma_dr_wav_buffer_reader; +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) +{ + MA_DR_WAV_ASSERT(pBuffer != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ZERO_OBJECT(pReader); + pReader->pBuffer = pBuffer; + pReader->sizeInBytes = sizeInBytes; + pReader->cursor = 0; + return MA_SUCCESS; +} +MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) +{ + MA_DR_WAV_ASSERT(pReader != NULL); + return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) +{ + MA_DR_WAV_ASSERT(pReader != NULL); + if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { + return MA_BAD_SEEK; + } + pReader->cursor += bytesToSeek; + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +{ + ma_result result = MA_SUCCESS; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pReader != NULL); + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + bytesRemaining = (pReader->sizeInBytes - pReader->cursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (pDst == NULL) { + result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); + } else { + MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); + pReader->cursor += bytesToRead; + } + MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == MA_SUCCESS) { + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + } + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) +{ + ma_result result; + size_t bytesRead; + ma_uint8 data[2]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); + *pDst = 0; + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = ma_dr_wav_bytes_to_u16(data); + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) +{ + ma_result result; + size_t bytesRead; + ma_uint8 data[4]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); + *pDst = 0; + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = ma_dr_wav_bytes_to_u32(data); + return MA_SUCCESS; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +{ + ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; + size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(bextData)) { + ma_dr_wav_buffer_reader reader; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + size_t extraBytes; + pMetadata->type = ma_dr_wav_metadata_type_bext; + if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { + pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); + if (extraBytes > 0) { + pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); + MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); + } else { + pMetadata->data.bext.pCodingHistory = NULL; + pMetadata->data.bext.codingHistorySize = 0; + } + } + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +{ + ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(cueIDBuffer)) { + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = type; + pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelOrNote.stringLength = 0; + pMetadata->data.labelOrNote.pString = NULL; + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +{ + ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); + pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; + pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; + pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; + pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; + pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelledCueRegion.stringLength = 0; + pMetadata->data.labelledCueRegion.pString = NULL; + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +{ + ma_uint64 bytesRead = 0; + ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + } else { + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = type; + if (stringSizeWithNullTerminator > 0) { + pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; + pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + if (bytesRead == chunkSize) { + pParser->metadataCursor += 1; + } else { + } + } else { + pMetadata->data.infoText.stringLength = 0; + pMetadata->data.infoText.pString = NULL; + pParser->metadataCursor += 1; + } + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) +{ + ma_uint64 bytesRead = 0; + if (location == ma_dr_wav_metadata_location_invalid) { + return 0; + } + if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { + return 0; + } + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + } else { + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = ma_dr_wav_metadata_type_unknown; + pMetadata->data.unknown.chunkLocation = location; + pMetadata->data.unknown.id[0] = pChunkId[0]; + pMetadata->data.unknown.id[1] = pChunkId[1]; + pMetadata->data.unknown.id[2] = pChunkId[2]; + pMetadata->data.unknown.id[3] = pChunkId[3]; + pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; + pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + return bytesRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) +{ + return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) +{ + const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; + ma_uint64 bytesRead = 0; + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + ma_uint8 buffer[4]; + size_t bytesJustRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { + return bytesRead; + } + bytesRead += 28; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); + ma_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; + if (calculatedLoopCount == loopCount) { + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + } + } else { + } + } + } else { + bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + size_t cueCount; + pParser->metadataCount += 1; + cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); + } else { + bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; + size_t bytesJustRead; + buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { + ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; + while (bytesRead < pChunkHeader->sizeInBytes) { + ma_uint8 subchunkId[4]; + ma_uint8 subchunkSizeBuffer[4]; + ma_uint64 subchunkDataSize; + ma_uint64 subchunkBytesRead = 0; + ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + if (bytesJustRead != sizeof(subchunkId)) { + break; + } + if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { + listType = ma_dr_wav_metadata_location_inside_adtl_list; + continue; + } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { + listType = ma_dr_wav_metadata_location_inside_info_list; + continue; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + if (bytesJustRead != sizeof(subchunkSizeBuffer)) { + break; + } + subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { + ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + } else { + subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { + ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + } else { + subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } + bytesRead += subchunkBytesRead; + MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + if (subchunkBytesRead < subchunkDataSize) { + ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { + break; + } + bytesRead += bytesToSeek; + } + if ((subchunkDataSize % 2) == 1) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { + break; + } + bytesRead += 1; + } + } + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); + } + return bytesRead; +} +MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) +{ + ma_uint32 bytesPerFrame; + if ((pWav->bitsPerSample & 0x7) == 0) { + bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; + } else { + bytesPerFrame = pWav->fmt.blockAlign; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + if (bytesPerFrame != pWav->fmt.channels) { + return 0; + } + } + return bytesPerFrame; +} +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) +{ + if (pFMT == NULL) { + return 0; + } + if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return pFMT->formatTag; + } else { + return ma_dr_wav_bytes_to_u16(pFMT->subFormat); + } +} +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onRead == NULL || onSeek == NULL) { + return MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pReadSeekUserData; + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) +{ + ma_result result; + ma_uint64 cursor; + ma_bool32 sequential; + ma_uint8 riff[4]; + ma_dr_wav_fmt fmt; + unsigned short translatedFormatTag; + ma_uint64 dataChunkSize = 0; + ma_uint64 sampleCountFromFactChunk = 0; + ma_uint64 metadataStartPos; + ma_dr_wav__metadata_parser metadataParser; + ma_bool8 isProcessingMetadata = MA_FALSE; + ma_bool8 foundChunk_fmt = MA_FALSE; + ma_bool8 foundChunk_data = MA_FALSE; + ma_bool8 isAIFCFormType = MA_FALSE; + ma_uint64 aiffFrameCount = 0; + cursor = 0; + sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; + MA_DR_WAV_ZERO_OBJECT(&fmt); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { + pWav->container = ma_dr_wav_container_riff; + } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { + pWav->container = ma_dr_wav_container_rifx; + } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { + int i; + ma_uint8 riff2[12]; + pWav->container = ma_dr_wav_container_w64; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return MA_FALSE; + } + for (i = 0; i < 12; ++i) { + if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { + return MA_FALSE; + } + } + } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { + pWav->container = ma_dr_wav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { + pWav->container = ma_dr_wav_container_aiff; + } else { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 wave[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + ma_uint8 chunkSizeBytes[8]; + ma_uint8 wave[16]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 aiff[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { + isAIFCFormType = MA_FALSE; + } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { + isAIFCFormType = MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 sizeBytes[8]; + ma_uint64 bytesRemainingInChunk; + ma_dr_wav_chunk_header header; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { + return MA_FALSE; + } + bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; + if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + cursor += 8; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); + if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return MA_FALSE; + } + cursor += bytesRemainingInChunk; + } + metadataStartPos = cursor; + isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); + if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { + isProcessingMetadata = MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); + if (isProcessingMetadata) { + metadataParser.onRead = pWav->onRead; + metadataParser.onSeek = pWav->onSeek; + metadataParser.pReadSeekUserData = pWav->pUserData; + metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; + } + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 chunkSize; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + chunkSize = header.sizeInBytes; + if (!sequential && onChunk != NULL) { + ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + if (callbackBytesRead > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { + ma_uint8 fmtData[16]; + foundChunk_fmt = MA_TRUE; + if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { + return MA_FALSE; + } + cursor += sizeof(fmtData); + fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); + fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); + fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); + fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); + fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); + fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); + fmt.extendedSize = 0; + fmt.validBitsPerSample = 0; + fmt.channelMask = 0; + MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); + if (header.sizeInBytes > 16) { + ma_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return MA_FALSE; + } + cursor += sizeof(fmt_cbSize); + bytesReadSoFar = 18; + fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); + if (fmt.extendedSize > 0) { + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmt.extendedSize != 22) { + return MA_FALSE; + } + } + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + ma_uint8 fmtext[22]; + if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { + return MA_FALSE; + } + fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); + fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); + ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); + } else { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + } + cursor += fmt.extendedSize; + bytesReadSoFar += fmt.extendedSize; + } + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + cursor += (header.sizeInBytes - bytesReadSoFar); + } + if (header.paddingSize > 0) { + if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += header.paddingSize; + } + continue; + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { + foundChunk_data = MA_TRUE; + pWav->dataChunkDataPos = cursor; + if (pWav->container != ma_dr_wav_container_rf64) { + dataChunkSize = chunkSize; + } + if (sequential || !isProcessingMetadata) { + break; + } else { + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + ma_uint8 sampleCount[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return MA_FALSE; + } + chunkSize -= 4; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); + } else { + sampleCountFromFactChunk = 0; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return MA_FALSE; + } + chunkSize -= 8; + } else if (pWav->container == ma_dr_wav_container_rf64) { + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { + ma_uint8 commData[24]; + ma_uint32 commDataBytesToRead; + ma_uint16 channels; + ma_uint32 frameCount; + ma_uint16 sampleSizeInBits; + ma_int64 sampleRate; + ma_uint16 compressionFormat; + foundChunk_fmt = MA_TRUE; + if (isAIFCFormType) { + commDataBytesToRead = 24; + if (header.sizeInBytes < commDataBytesToRead) { + return MA_FALSE; + } + } else { + commDataBytesToRead = 18; + if (header.sizeInBytes != commDataBytesToRead) { + return MA_FALSE; + } + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { + return MA_FALSE; + } + channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); + frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); + sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); + sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); + if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { + return MA_FALSE; + } + if (isAIFCFormType) { + const ma_uint8* type = commData + 18; + if (ma_dr_wav_fourcc_equal(type, "NONE")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + if (sampleSizeInBits == 8) { + pWav->aiff.isUnsigned = MA_TRUE; + } + } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + pWav->aiff.isLE = MA_TRUE; + } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { + compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_ALAW; + } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_MULAW; + } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { + compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; + sampleSizeInBits = 4; + return MA_FALSE; + } else { + return MA_FALSE; + } + } else { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } + aiffFrameCount = frameCount; + fmt.formatTag = compressionFormat; + fmt.channels = channels; + fmt.sampleRate = (ma_uint32)sampleRate; + fmt.bitsPerSample = sampleSizeInBits; + fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); + fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; + if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + fmt.blockAlign = 34 * fmt.channels; + } + if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { + if (fmt.bitsPerSample > 8) { + fmt.bitsPerSample = 8; + fmt.blockAlign = fmt.channels; + } + } + fmt.bitsPerSample += (fmt.bitsPerSample & 7); + if (isAIFCFormType) { + if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += (chunkSize - commDataBytesToRead); + } + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { + ma_uint8 offsetAndBlockSizeData[8]; + ma_uint32 offset; + foundChunk_data = MA_TRUE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { + return MA_FALSE; + } + offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += offset; + pWav->dataChunkDataPos = cursor; + dataChunkSize = chunkSize; + if (sequential || !isProcessingMetadata) { + break; + } else { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (isProcessingMetadata) { + ma_uint64 metadataBytesRead; + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + break; + } + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + } + if (!foundChunk_fmt || !foundChunk_data) { + return MA_FALSE; + } + if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || + (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return MA_FALSE; + } + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); + } + if (!sequential) { + if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return MA_FALSE; + } + cursor = pWav->dataChunkDataPos; + } + if (isProcessingMetadata && metadataParser.metadataCount > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 metadataBytesRead; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + } + if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { + dataChunkSize = 0; + for (;;) { + ma_uint8 temp[4096]; + size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); + dataChunkSize += bytesRead; + if (bytesRead < sizeof(temp)) { + break; + } + } + } + if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataChunkSize; + if (sampleCountFromFactChunk != 0) { + pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else if (aiffFrameCount != 0) { + pWav->totalPCMFrameCount = aiffFrameCount; + } else { + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + pWav->totalPCMFrameCount += blockCount; + } + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->channels > 2) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } +#endif + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); +} +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); +} +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) +{ + ma_dr_wav_metadata *result = pWav->pMetadata; + pWav->pMetadata = NULL; + pWav->metadataCount = 0; + return result; +} +MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + return pWav->onWrite(pWav->pUserData, pData, dataSize); +} +MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + return pWav->onWrite(pWav->pUserData, &byte, 1); +} +MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap16(value); + } + return ma_dr_wav__write(pWav, &value, 2); +} +MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap32(value); + } + return ma_dr_wav__write(pWav, &value, 4); +} +MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap64(value); + } + return ma_dr_wav__write(pWav, &value, 8); +} +MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) +{ + union { + ma_uint32 u32; + float f32; + } u; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + u.f32 = value; + if (!ma_dr_wav__is_little_endian()) { + u.u32 = ma_dr_wav__bswap32(u.u32); + } + return ma_dr_wav__write(pWav, &u.u32, 4); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) +{ + if (pWav == NULL) { + return dataSize; + } + return ma_dr_wav__write(pWav, pData, dataSize); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) +{ + if (pWav == NULL) { + return 1; + } + return ma_dr_wav__write_byte(pWav, byte); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +{ + if (pWav == NULL) { + return 2; + } + return ma_dr_wav__write_u16ne_to_le(pWav, value); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +{ + if (pWav == NULL) { + return 4; + } + return ma_dr_wav__write_u32ne_to_le(pWav, value); +} +#if 0 +MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +{ + if (pWav == NULL) { + return 8; + } + return ma_dr_wav__write_u64ne_to_le(pWav, value); +} +#endif +MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) +{ + if (pWav == NULL) { + return 4; + } + return ma_dr_wav__write_f32ne_to_le(pWav, value); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) +{ + size_t len; + if (pWav == NULL) { + return bufFixedSize; + } + len = ma_dr_wav__strlen_clamped(str, bufFixedSize); + ma_dr_wav__write_or_count(pWav, str, len); + if (len < bufFixedSize) { + size_t i; + for (i = 0; i < bufFixedSize - len; ++i) { + ma_dr_wav__write_byte(pWav, 0); + } + } + return bufFixedSize; +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) +{ + size_t bytesWritten = 0; + ma_bool32 hasListAdtl = MA_FALSE; + ma_bool32 hasListInfo = MA_FALSE; + ma_uint32 iMetadata; + if (pMetadatas == NULL || metadataCount == 0) { + return 0; + } + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 chunkSize = 0; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { + hasListInfo = MA_TRUE; + } + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { + hasListAdtl = MA_TRUE; + } + switch (pMetadata->type) { + case ma_dr_wav_metadata_type_smpl: + { + ma_uint32 iLoop; + chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + } + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + } + } break; + case ma_dr_wav_metadata_type_inst: + { + chunkSize = MA_DR_WAV_INST_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + } break; + case ma_dr_wav_metadata_type_cue: + { + ma_uint32 iCuePoint; + chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + } + } break; + case ma_dr_wav_metadata_type_acid: + { + chunkSize = MA_DR_WAV_ACID_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + } break; + case ma_dr_wav_metadata_type_bext: + { + char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); + bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + if (pMetadata->data.bext.codingHistorySize > 0) { + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { + chunkSize = pMetadata->data.unknown.dataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + } + } break; + default: break; + } + if ((chunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + if (hasListInfo) { + ma_uint32 chunkSize = 4; + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { + chunkSize += 8; + chunkSize += pMetadata->data.infoText.stringLength + 1; + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + chunkSize += 8; + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { + const char* pID = NULL; + switch (pMetadata->type) { + case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + default: break; + } + MA_DR_WAV_ASSERT(pID != NULL); + if (pMetadata->data.infoText.stringLength) { + subchunkSize = pMetadata->data.infoText.stringLength + 1; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + if (pMetadata->data.unknown.dataSizeInBytes) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } + if ((subchunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + } + if (hasListAdtl) { + ma_uint32 chunkSize = 4; + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + switch (pMetadata->type) + { + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: + { + chunkSize += 8; + chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pMetadata->data.labelOrNote.stringLength > 0) { + chunkSize += pMetadata->data.labelOrNote.stringLength + 1; + } + } break; + case ma_dr_wav_metadata_type_list_labelled_cue_region: + { + chunkSize += 8; + chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + chunkSize += 8; + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + } break; + default: break; + } + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + switch (pMetadata->type) + { + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: + { + if (pMetadata->data.labelOrNote.stringLength > 0) { + const char *pID = NULL; + if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { + pID = "labl"; + } + else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { + pID = "note"; + } + MA_DR_WAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } break; + case ma_dr_wav_metadata_type_list_labelled_cue_region: + { + subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } break; + default: break; + } + if ((subchunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + } + MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); + return bytesWritten; +} +MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + return (ma_uint32)chunkSize; +} +MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) +{ + if (dataChunkSize <= 0xFFFFFFFFUL) { + return (ma_uint32)dataChunkSize; + } else { + return 0xFFFFFFFFUL; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) +{ + ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); + return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) +{ + return 24 + dataChunkSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) +{ + ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + return chunkSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) +{ + return dataChunkSize; +} +MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onWrite == NULL) { + return MA_FALSE; + } + if (!isSequential && onSeek == NULL) { + return MA_FALSE; + } + if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return MA_FALSE; + } + if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onWrite = onWrite; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + pWav->fmt.formatTag = (ma_uint16)pFormat->format; + pWav->fmt.channels = (ma_uint16)pFormat->channels; + pWav->fmt.sampleRate = pFormat->sampleRate; + pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->fmt.extendedSize = 0; + pWav->isSequentialWrite = isSequential; + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) +{ + size_t runningPos = 0; + ma_uint64 initialDataChunkSize = 0; + ma_uint64 chunkSizeFMT; + if (pWav->isSequentialWrite) { + initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; + if (pFormat->container == ma_dr_wav_container_riff) { + if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { + return MA_FALSE; + } + } + } + pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "RIFF", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "RF64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else { + return MA_FALSE; + } + if (pFormat->container == ma_dr_wav_container_rf64) { + ma_uint32 initialds64ChunkSize = 28; + ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "ds64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); + } + if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { + chunkSizeFMT = 16; + runningPos += ma_dr_wav__write(pWav, "fmt ", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); + } else if (pFormat->container == ma_dr_wav_container_w64) { + chunkSizeFMT = 40; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); + } + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { + runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + } + pWav->dataChunkDataPos = runningPos; + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + } + pWav->container = pFormat->container; + pWav->channels = (ma_uint16)pFormat->channels; + pWav->sampleRate = pFormat->sampleRate; + pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (ma_uint16)pFormat->format; + pWav->dataChunkDataPos = runningPos; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); +} +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); +} +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->pMetadata = pMetadata; + pWav->metadataCount = metadataCount; + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); +} +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + ma_uint64 riffChunkSizeBytes; + ma_uint64 fileSizeBytes = 0; + if (pFormat->container == ma_dr_wav_container_riff) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); + } else if (pFormat->container == ma_dr_wav_container_w64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); + fileSizeBytes = riffChunkSizeBytes; + } else if (pFormat->container == ma_dr_wav_container_rf64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); + } + return fileSizeBytes; +} +#ifndef MA_DR_WAV_NO_STDIO +MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} +MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +{ + return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} +#endif +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); +} +#endif +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} +#endif +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#endif +#endif +MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + pWav->memoryStream.currentReadPos += bytesToRead; + } + return bytesToRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { + return MA_FALSE; + } + } else { + if (pWav->memoryStream.currentReadPos < (size_t)-offset) { + return MA_FALSE; + } + } + pWav->memoryStream.currentReadPos += offset; + } else { + if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { + pWav->memoryStream.currentReadPos = offset; + } else { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; + if (bytesRemaining < bytesToWrite) { + void* pNewData; + size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; + if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { + newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; + } + pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + *pWav->memoryStreamWrite.ppData = pNewData; + pWav->memoryStreamWrite.dataCapacity = newDataCapacity; + } + MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + pWav->memoryStreamWrite.currentWritePos += bytesToWrite; + if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { + pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; + } + *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; + return bytesToWrite; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { + offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); + } + } else { + if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { + offset = -(int)pWav->memoryStreamWrite.currentWritePos; + } + } + pWav->memoryStreamWrite.currentWritePos += offset; + } else { + if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + pWav->memoryStreamWrite.currentWritePos = offset; + } else { + pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return MA_FALSE; + } + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); +} +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return MA_FALSE; + } + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (ppData == NULL || pDataSize == NULL) { + return MA_FALSE; + } + *ppData = NULL; + *pDataSize = 0; + if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStreamWrite.ppData = ppData; + pWav->memoryStreamWrite.pDataSize = pDataSize; + pWav->memoryStreamWrite.dataSize = 0; + pWav->memoryStreamWrite.dataCapacity = 0; + pWav->memoryStreamWrite.currentWritePos = 0; + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) +{ + ma_result result = MA_SUCCESS; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + if (pWav->onWrite != NULL) { + ma_uint32 paddingSize = 0; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { + paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); + } else { + paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); + } + if (paddingSize > 0) { + ma_uint64 paddingData = 0; + ma_dr_wav__write(pWav, &paddingData, paddingSize); + } + if (pWav->onSeek && !pWav->isSequentialWrite) { + if (pWav->container == ma_dr_wav_container_riff) { + if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); + ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == ma_dr_wav_container_w64) { + if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + int ds64BodyPos = 12 + 8; + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + } + } + } + if (pWav->isSequentialWrite) { + if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { + result = MA_INVALID_FILE; + } + } + } else { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + } +#ifndef MA_DR_WAV_NO_STDIO + if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { + fclose((FILE*)pWav->pUserData); + } +#endif + return result; +} +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) +{ + size_t bytesRead; + ma_uint32 bytesPerFrame; + if (pWav == NULL || bytesToRead == 0) { + return 0; + } + if (bytesToRead > pWav->bytesRemaining) { + bytesToRead = (size_t)pWav->bytesRemaining; + } + if (bytesToRead == 0) { + return 0; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + if (pBufferOut != NULL) { + bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); + } else { + bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > 0x7FFFFFFF) { + bytesToSeek = 0x7FFFFFFF; + } + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { + break; + } + bytesRead += bytesToSeek; + } + while (bytesRead < bytesToRead) { + ma_uint8 buffer[4096]; + size_t bytesSeeked; + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > sizeof(buffer)) { + bytesToSeek = sizeof(buffer); + } + bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); + bytesRead += bytesSeeked; + if (bytesSeeked < bytesToSeek) { + break; + } + } + } + pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; + pWav->bytesRemaining -= bytesRead; + return bytesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint32 bytesPerFrame; + ma_uint64 bytesToRead; + ma_uint64 framesRemainingInFile; + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; + if (framesToRead > framesRemainingInFile) { + framesToRead = framesRemainingInFile; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesToRead = framesToRead * bytesPerFrame; + if (bytesToRead > MA_SIZE_MAX) { + bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; + } + if (bytesToRead == 0) { + return 0; + } + return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL) { + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 framesRead = 0; + if (ma_dr_wav_is_container_be(pWav->container)) { + if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } + goto post_process; + } + } + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } + post_process: + { + if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { + if (pBufferOut != NULL) { + ma_uint64 iSample; + for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { + ((ma_uint8*)pBufferOut)[iSample] += 128; + } + } + } + } + return framesRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) +{ + if (pWav->onWrite != NULL) { + return MA_FALSE; + } + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->ima); + } else { + MA_DR_WAV_ASSERT(MA_FALSE); + } + } + pWav->readCursorInPCMFrames = 0; + pWav->bytesRemaining = pWav->dataChunkDataSize; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) +{ + if (pWav == NULL || pWav->onSeek == NULL) { + return MA_FALSE; + } + if (pWav->onWrite != NULL) { + return MA_FALSE; + } + if (pWav->totalPCMFrameCount == 0) { + return MA_TRUE; + } + if (targetFrameIndex > pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (targetFrameIndex < pWav->readCursorInPCMFrames) { + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; + } + } + if (targetFrameIndex > pWav->readCursorInPCMFrames) { + ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + ma_int16 devnull[2048]; + while (offsetInFrames > 0) { + ma_uint64 framesRead = 0; + ma_uint64 framesToRead = offsetInFrames; + if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { + framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + } else { + MA_DR_WAV_ASSERT(MA_FALSE); + } + if (framesRead != framesToRead) { + return MA_FALSE; + } + offsetInFrames -= framesRead; + } + } + } else { + ma_uint64 totalSizeInBytes; + ma_uint64 currentBytePos; + ma_uint64 targetBytePos; + ma_uint64 offset; + ma_uint32 bytesPerFrame; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return MA_FALSE; + } + totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; + currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + targetBytePos = targetFrameIndex * bytesPerFrame; + if (currentBytePos < targetBytePos) { + offset = (targetBytePos - currentBytePos); + } else { + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; + } + offset = targetBytePos; + } + while (offset > 0) { + int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); + if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; + pWav->bytesRemaining -= offset32; + offset -= offset32; + } + } + return MA_TRUE; +} +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + *pCursor = 0; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + *pCursor = pWav->readCursorInPCMFrames; + return MA_SUCCESS; +} +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + *pLength = 0; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + *pLength = pWav->totalPCMFrameCount; + return MA_SUCCESS; +} +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) +{ + size_t bytesWritten; + if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { + return 0; + } + bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); + pWav->dataChunkDataSize += bytesWritten; + return bytesWritten; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + const ma_uint8* pRunningData; + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > MA_SIZE_MAX) { + return 0; + } + bytesWritten = 0; + pRunningData = (const ma_uint8*)pData; + while (bytesToWrite > 0) { + size_t bytesJustWritten; + ma_uint64 bytesToWriteThisIteration; + bytesToWriteThisIteration = bytesToWrite; + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + if (bytesJustWritten == 0) { + break; + } + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + ma_uint32 bytesPerSample; + const ma_uint8* pRunningData; + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > MA_SIZE_MAX) { + return 0; + } + bytesWritten = 0; + pRunningData = (const ma_uint8*)pData; + bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + if (bytesPerSample == 0) { + return 0; + } + while (bytesToWrite > 0) { + ma_uint8 temp[4096]; + ma_uint32 sampleCount; + size_t bytesJustWritten; + ma_uint64 bytesToWriteThisIteration; + bytesToWriteThisIteration = bytesToWrite; + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + sampleCount = sizeof(temp)/bytesPerSample; + if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; + } + MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + if (bytesJustWritten == 0) { + break; + } + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + if (ma_dr_wav__is_little_endian()) { + return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); + } else { + return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + MA_DR_WAV_ASSERT(framesToRead > 0); + if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + ma_uint8 header[7]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrameCount = 2; + } else { + ma_uint8 header[14]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.predictor[1] = header[1]; + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); + pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.cachedFrameCount = 2; + } + } + while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + ma_uint32 iSample = 0; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->msadpcm.cachedFrameCount -= 1; + } + if (framesToRead == 0) { + break; + } + if (pWav->msadpcm.cachedFrameCount == 0) { + if (pWav->msadpcm.bytesRemainingInBlock == 0) { + continue; + } else { + static ma_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + ma_uint8 nibbles; + ma_int32 nibble0; + ma_int32 nibble1; + if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock -= 1; + nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } + nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } + if (pWav->channels == 1) { + ma_int32 newSample0; + ma_int32 newSample1; + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[0]; + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample1; + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 2; + } else { + ma_int32 newSample0; + ma_int32 newSample1; + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[1]; + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; + if (pWav->msadpcm.delta[1] < 16) { + pWav->msadpcm.delta[1] = 16; + } + pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.prevFrames[1][1] = newSample1; + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 1; + } + } + } + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + ma_uint32 iChannel; + static ma_int32 indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + static ma_int32 stepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + MA_DR_WAV_ASSERT(framesToRead > 0); + if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + ma_uint8 header[4]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + if (header[2] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; + } + pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.cachedFrameCount = 1; + } else { + ma_uint8 header[8]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; + } + pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.cachedFrameCount = 1; + } + } + while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + ma_uint32 iSample; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->ima.cachedFrameCount -= 1; + } + if (framesToRead == 0) { + break; + } + if (pWav->ima.cachedFrameCount == 0) { + if (pWav->ima.bytesRemainingInBlock == 0) { + continue; + } else { + pWav->ima.cachedFrameCount = 8; + for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { + ma_uint32 iByte; + ma_uint8 nibbles[4]; + if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { + pWav->ima.cachedFrameCount = 0; + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock -= 4; + for (iByte = 0; iByte < 4; ++iByte) { + ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + ma_int32 predictor = pWav->ima.predictor[iChannel]; + ma_int32 diff = step >> 3; + if (nibble0 & 1) diff += step >> 2; + if (nibble0 & 2) diff += step >> 1; + if (nibble0 & 4) diff += step; + if (nibble0 & 8) diff = -diff; + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + step = stepTable[pWav->ima.stepIndex[iChannel]]; + predictor = pWav->ima.predictor[iChannel]; + diff = step >> 3; + if (nibble1 & 1) diff += step >> 2; + if (nibble1 & 2) diff += step >> 1; + if (nibble1 & 4) diff += step; + if (nibble1 & 8) diff = -diff; + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + } + } + } + } + } + return totalFramesRead; +} +#ifndef MA_DR_WAV_NO_CONVERSION_API +static unsigned short g_ma_dr_wavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; +static unsigned short g_ma_dr_wavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; +static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) +{ + return (short)g_ma_dr_wavAlawTable[sampleIn]; +} +static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) +{ + return (short)g_ma_dr_wavMulawTable[sampleIn]; +} +MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + size_t i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 2) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const ma_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < totalSampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (ma_int16)((ma_int64)sample >> 48); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x << 8; + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + double x = pIn[i]; + double c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5); + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); + } +} +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); + } +} +MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 2) { + ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 4) { + ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < sampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + unsigned int i; + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((const float*)pIn)[i]; + } + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_int16 samples16[2048]; + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + x = x * 0.00784313725490196078f; + x = x - 1; + *pOut++ = x; + } +#endif +} +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] * 0.000030517578125f; + } +} +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + double x; + ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); + ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); + ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); + x = (double)((ma_int32)(a | b | c) >> 8); + *pOut++ = (float)(x * 0.00000011920928955078125); + } +} +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} +MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 2) { + ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const ma_int32*)pIn)[i]; + } + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < totalSampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (ma_int32)((ma_int64)sample >> 32); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + ma_int16 samples16[2048]; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((int)pIn[i] - 128) << 24; + } +} +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] << 16; + } +} +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = sample32; + } +} +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + } +} +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + } +} +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; + } +} +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i= 0; i < sampleCount; ++i) { + *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; + } +} +MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + ma_int16* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + float* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + ma_int32* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif +#endif +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_wav__free_default(p, NULL); + } +} +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) +{ + return (ma_int16)ma_dr_wav_bytes_to_u16(data); +} +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) +{ + return ma_dr_wav_bytes_to_u32_le(data); +} +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) +{ + union { + ma_uint32 u32; + float f32; + } value; + value.u32 = ma_dr_wav_bytes_to_u32(data); + return value.f32; +} +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) +{ + return (ma_int32)ma_dr_wav_bytes_to_u32(data); +} +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) +{ + return + ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | + ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); +} +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) +{ + return (ma_int64)ma_dr_wav_bytes_to_u64(data); +} +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) +{ + int i; + for (i = 0; i < 16; i += 1) { + if (a[i] != b[i]) { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) +{ + return + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3]; +} +#ifdef __MRC__ +#pragma options opt reset +#endif +#endif +/* dr_wav_c end */ +#endif /* MA_DR_WAV_IMPLEMENTATION */ +#endif /* MA_NO_WAV */ + +#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) +#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_flac_c begin */ +#ifndef ma_dr_flac_c +#define ma_dr_flac_c +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif +#ifdef __linux__ + #ifndef _BSD_SOURCE + #define _BSD_SOURCE + #endif + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif + #ifndef __USE_BSD + #define __USE_BSD + #endif + #include +#endif +#include +#include +#if !defined(MA_DR_FLAC_NO_SIMD) + #if defined(MA_X64) || defined(MA_X86) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) + #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #endif + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #endif + #if defined(MA_DR_FLAC_SUPPORT_SSE41) + #include + #elif defined(MA_DR_FLAC_SUPPORT_SSE2) + #include + #endif + #endif + #if defined(MA_ARM) + #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_DR_FLAC_SUPPORT_NEON + #include + #endif + #endif +#endif +#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static void ma_dr_flac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define MA_DR_FLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void ma_dr_flac__cpuid(int info[4], int fid) + { + #if defined(MA_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 + } + #else + #define MA_DR_FLAC_NO_CPUID + #endif + #endif +#else + #define MA_DR_FLAC_NO_CPUID +#endif +static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return MA_TRUE; + #else + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_dr_flac__cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) + #if defined(__SSE4_1__) || defined(__AVX__) + return MA_TRUE; + #else + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_dr_flac__cpuid(info, 1); + return (info[2] & (1 << 19)) != 0; + #endif + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC + #endif + #endif +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #endif +#elif defined(__WATCOMC__) && defined(__386__) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline ma_uint16 _watcom_bswap16(ma_uint16); + extern __inline ma_uint32 _watcom_bswap32(ma_uint32); + extern __inline ma_uint64 _watcom_bswap64(ma_uint64); +#pragma aux _watcom_bswap16 = \ + "xchg al, ah" \ + parm [ax] \ + value [ax] \ + modify nomemory; +#pragma aux _watcom_bswap32 = \ + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#pragma aux _watcom_bswap64 = \ + "bswap eax" \ + "bswap edx" \ + "xchg eax,edx" \ + parm [eax edx] \ + value [eax edx] \ + modify nomemory; +#endif +#ifndef MA_DR_FLAC_ASSERT +#include +#define MA_DR_FLAC_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_FLAC_MALLOC +#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_FLAC_REALLOC +#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_FLAC_FREE +#define MA_DR_FLAC_FREE(p) free((p)) +#endif +#ifndef MA_DR_FLAC_COPY_MEMORY +#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_FLAC_ZERO_MEMORY +#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef MA_DR_FLAC_ZERO_OBJECT +#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif +#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 +#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 +#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 +#define MA_DR_FLAC_SUBFRAME_FIXED 8 +#define MA_DR_FLAC_SUBFRAME_LPC 32 +#define MA_DR_FLAC_SUBFRAME_RESERVED 255 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 +#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_FLAC_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_FLAC_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_FLAC_VERSION_REVISION; + } +} +MA_API const char* ma_dr_flac_version_string(void) +{ + return MA_DR_FLAC_VERSION_STRING; +} +#if defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #else + #define MA_DR_FLAC_NO_THREAD_SANITIZE + #endif +#else + #define MA_DR_FLAC_NO_THREAD_SANITIZE +#endif +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; +#endif +#ifndef MA_DR_FLAC_NO_CPUID +static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; +static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +{ + static ma_bool32 isCPUCapsInitialized = MA_FALSE; + if (!isCPUCapsInitialized) { +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) + int info[4] = {0}; + ma_dr_flac__cpuid(info, 0x80000001); + ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; +#endif + ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); + ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); + isCPUCapsInitialized = MA_TRUE; + } +} +#else +static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; +static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return MA_TRUE; + #else + return MA_FALSE; + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +{ + ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + ma_dr_flac__gIsLZCNTSupported = MA_TRUE; +#endif +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} +static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} +static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint16(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) +{ + const ma_uint8* pNum = (ma_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} +static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint64(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) +{ + if (!ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) +{ + const ma_uint8* pNum = (ma_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} +static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) +{ + ma_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + return result; +} +static ma_uint8 ma_dr_flac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; +static ma_uint16 ma_dr_flac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; +static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) +{ + return ma_dr_flac__crc8_table[crc ^ data]; +} +static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + ma_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + ma_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 32); + wholeBytes = count >> 3; + leftoverBits = count - (wholeBytes*8); + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + } + return crc; +#endif +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) +{ + return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) +{ +#ifdef MA_64BIT + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#endif + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + return crc; +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef MA_64BIT + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + } + return crc; +} +#if 0 +static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + ma_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + ma_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + return crc; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 64); + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + default: + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 64); + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + default: + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) +{ +#ifdef MA_64BIT + return ma_dr_flac_crc16__64bit(crc, data, count); +#else + return ma_dr_flac_crc16__32bit(crc, data, count); +#endif +} +#endif +#ifdef MA_64BIT +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 +#else +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 +#endif +#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) +#ifndef MA_DR_FLAC_NO_CRC +static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} +static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) +{ + if (bs->crc16CacheIgnoredBytes == 0) { + bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); + } else { + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; + } +} +static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) +{ + MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + ma_dr_flac__update_crc16(bs); + } else { + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + return bs->crc16; +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) +{ + size_t bytesRead; + size_t alignedL1LineCount; + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } + if (bs->unalignedByteCount > 0) { + return MA_FALSE; + } + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); + bs->nextL2Line = 0; + if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } + alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + if (alignedL1LineCount > 0) { + size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t i; + for (i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + bs->nextL2Line = (ma_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } else { + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + return MA_FALSE; + } +} +static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) +{ + size_t bytesRead; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); +#endif + if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { + bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return MA_TRUE; + } + bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + return MA_FALSE; + } + MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); + bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); + bs->unalignedByteCount = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return MA_TRUE; +} +static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) +{ + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + bs->unalignedByteCount = 0; + bs->unalignedCache = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResultOut != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { +#ifdef MA_64BIT + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; +#else + if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + *pResultOut = (ma_uint32)bs->cache; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } +#endif + return MA_TRUE; + } else { + ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + ma_uint32 bitCountLo = bitCount - bitCountHi; + ma_uint32 resultHi; + MA_DR_FLAC_ASSERT(bitCountHi > 0); + MA_DR_FLAC_ASSERT(bitCountHi < 32); + resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + if (bitCount < 32) { + ma_uint32 signbit; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + } + *pResult = (ma_int32)result; + return MA_TRUE; +} +#ifdef MA_64BIT +static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) +{ + ma_uint32 resultHi; + ma_uint32 resultLo; + MA_DR_FLAC_ASSERT(bitCount <= 64); + MA_DR_FLAC_ASSERT(bitCount > 32); + if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { + return MA_FALSE; + } + *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); + return MA_TRUE; +} +#endif +#if 0 +static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) +{ + ma_uint64 result; + ma_uint64 signbit; + MA_DR_FLAC_ASSERT(bitCount <= 64); + if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { + return MA_FALSE; + } + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + *pResultOut = (ma_int64)result; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_uint16)result; + return MA_TRUE; +} +#if 0 +static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) +{ + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_int16)result; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_uint8)result; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) +{ + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_int8)result; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (ma_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return MA_TRUE; + } else { + bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; +#ifdef MA_64BIT + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint64 bin; + if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; + } + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint32 bin; + if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; + } + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + while (bitsToSeek >= 8) { + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { + return MA_FALSE; + } + bitsToSeek -= 8; + } + if (bitsToSeek > 0) { + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { + return MA_FALSE; + } + bitsToSeek = 0; + } + MA_DR_FLAC_ASSERT(bitsToSeek == 0); + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; + } + for (;;) { + ma_uint8 hi; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__reset_crc16(bs); +#endif + if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { + return MA_FALSE; + } + if (hi == 0xFF) { + ma_uint8 lo; + if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { + return MA_FALSE; + } + if (lo == 0x3E) { + return MA_TRUE; + } else { + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; + } + } + } + } +} +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#endif +#if defined(__WATCOMC__) && defined(__386__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +#endif +#ifdef __MRC__ +#include +#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC +#endif +static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) +{ + ma_uint32 n; + static ma_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (x == 0) { + return sizeof(x)*8; + } + n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef MA_64BIT + if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + return n - 1; +} +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) +{ +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return MA_TRUE; +#elif defined(__MRC__) + return MA_TRUE; +#else + #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC + return ma_dr_flac__gIsLZCNTSupported; + #else + return MA_FALSE; + #endif +#endif +} +static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) +{ +#if defined(_MSC_VER) + #ifdef MA_64BIT + return (ma_uint32)__lzcnt64(x); + #else + return (ma_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #if defined(MA_X64) + { + ma_uint64 r; + __asm__ __volatile__ ( + "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + return (ma_uint32)r; + } + #elif defined(MA_X86) + { + ma_uint32 r; + __asm__ __volatile__ ( + "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + return r; + } + #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) + { + unsigned int r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) + #else + "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) + #endif + ); + return r; + } + #else + if (x == 0) { + return sizeof(x)*8; + } + #ifdef MA_64BIT + return (ma_uint32)__builtin_clzll((ma_uint64)x); + #else + return (ma_uint32)__builtin_clzl((ma_uint32)x); + #endif + #endif + #else + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#include +static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) +{ + ma_uint32 n; + if (x == 0) { + return sizeof(x)*8; + } +#ifdef MA_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else +#pragma aux ma_dr_flac__clz_watcom = \ + "bsr eax, eax" \ + "xor eax, 31" \ + parm [eax] nomemory \ + value [eax] \ + modify exact [eax] nomemory; +#endif +#endif +static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) +{ +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT + if (ma_dr_flac__is_lzcnt_supported()) { + return ma_dr_flac__clz_lzcnt(x); + } else +#endif + { +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC + return ma_dr_flac__clz_msvc(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return ma_dr_flac__clz_watcom_lzcnt(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); +#else + return ma_dr_flac__clz_software(x); +#endif + } +} +static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) +{ + ma_uint32 zeroCounter = 0; + ma_uint32 setBitOffsetPlus1; + while (bs->cache == 0) { + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + if (bs->cache == 1) { + *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + return MA_TRUE; + } + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + setBitOffsetPlus1 += 1; + if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(offsetFromStart > 0); + if (offsetFromStart > 0x7FFFFFFF) { + ma_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + } + ma_dr_flac__reset_cache(bs); + return MA_TRUE; +} +static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) +{ + ma_uint8 crc; + ma_uint64 result; + ma_uint8 utf8[7] = {0}; + int byteCount; + int i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pNumberOut != NULL); + MA_DR_FLAC_ASSERT(pCRCOut != NULL); + crc = *pCRCOut; + if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return MA_AT_END; + } + crc = ma_dr_flac_crc8(crc, utf8[0], 8); + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return MA_SUCCESS; + } + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return MA_CRC_MISMATCH; + } + MA_DR_FLAC_ASSERT(byteCount > 1); + result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (i = 1; i < byteCount; ++i) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return MA_AT_END; + } + crc = ma_dr_flac_crc8(crc, utf8[i], 8); + result = (result << 6) | (utf8[i] & 0x3F); + } + *pNumberOut = result; + *pCRCOut = crc; + return MA_SUCCESS; +} +static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) +{ +#if 1 + ma_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + return result; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) +{ + return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; +} +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_int32 prediction = 0; + MA_DR_FLAC_ASSERT(order <= 32); + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + return (ma_int32)(prediction >> shift); +} +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_int64 prediction; + MA_DR_FLAC_ASSERT(order <= 32); +#ifndef MA_64BIT + if (order == 8) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + } + else + { + int j; + prediction = 0; + for (j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; + } + } +#endif +#ifdef MA_64BIT + prediction = 0; + switch (order) + { + case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; + } +#endif + return (ma_int32)(prediction >> shift); +} +#if 0 +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + for (i = 0; i < count; ++i) { + ma_uint32 zeroCounter = 0; + for (;;) { + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; + } + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + ma_uint32 decodedRice; + if (riceParam > 0) { + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; + } + } else { + decodedRice = 0; + } + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + return MA_TRUE; +} +#endif +#if 0 +static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_uint32 zeroCounter = 0; + ma_uint32 decodedRice; + for (;;) { + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; + } + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + if (riceParam > 0) { + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; + } + } else { + decodedRice = 0; + } + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return MA_TRUE; +} +#endif +#if 0 +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_dr_flac_cache_t riceParamMask; + ma_uint32 zeroCounter; + ma_uint32 setBitOffsetPlus1; + ma_uint32 riceParamPart; + ma_uint32 riceLength; + MA_DR_FLAC_ASSERT(riceParam > 0); + riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); + zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + ma_uint32 bitCountLo; + ma_dr_flac_cache_t resultHi; + bs->consumedBits += riceLength; + bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); + bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); +#endif + bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + } + riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + pZeroCounterOut[0] = zeroCounter; + pRiceParamPartOut[0] = riceParamPart; + return MA_TRUE; +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + pZeroCounterOut[0] = lzcount; + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + ma_uint32 riceParamPartHi; + ma_uint32 riceParamPartLo; + ma_uint32 riceParamPartLoBitCount; + riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; + bs_cache <<= riceParamPartLoBitCount; + } + } else { + ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + for (;;) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + lzcount = ma_dr_flac__clz(bs_cache); + zeroCounter += lzcount; + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + pZeroCounterOut[0] = zeroCounter; + goto extract_rice_param_part; + } + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + return MA_TRUE; +} +static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) +{ + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + bs_cache <<= riceParamPartLoBitCount; + } + } else { + for (;;) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + goto extract_rice_param_part; + } + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0; + ma_uint32 riceParamPart0; + ma_uint32 riceParamMask; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + (void)bitsPerSample; + (void)order; + (void)shift; + (void)coefficients; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + i = 0; + while (i < count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + pSamplesOut[i] = riceParamPart0; + i += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0 = 0; + ma_uint32 zeroCountPart1 = 0; + ma_uint32 zeroCountPart2 = 0; + ma_uint32 zeroCountPart3 = 0; + ma_uint32 riceParamPart0 = 0; + ma_uint32 riceParamPart1 = 0; + ma_uint32 riceParamPart2 = 0; + ma_uint32 riceParamPart3 = 0; + ma_uint32 riceParamMask; + const ma_int32* pSamplesOutEnd; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder == 0) { + return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + pSamplesOutEnd = pSamplesOut + (count & ~3); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + while (pSamplesOut < pSamplesOutEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut += 4; + } + } else { + while (pSamplesOut < pSamplesOutEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut += 4; + } + } + i = (count & ~3); + while (i < count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } else { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } + i += 1; + pSamplesOut += 1; + } + return MA_TRUE; +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +{ + __m128i r; + r = _mm_packs_epi32(a, b); + r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + return r; +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_SSE41) +static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) +{ + return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); +} +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) +{ + __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); + return _mm_add_epi32(x64, x32); +} +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) +{ + return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); +} +static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) +{ + __m128i lo = _mm_srli_epi64(x, count); + __m128i hi = _mm_srai_epi32(x, count); + hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); + return _mm_or_si128(lo, hi); +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i riceParamMask128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); +#if 1 + { + int runningOrder = order; + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i prediction128; + __m128i zeroCountPart128; + __m128i riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; + } + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; + } + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i prediction128; + __m128i riceParamMask128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + MA_DR_FLAC_ASSERT(order <= 12); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + prediction128 = _mm_setzero_si128(); + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); +#if 1 + { + int runningOrder = order; + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i zeroCountPart128; + __m128i riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; + } + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_xor_si128(prediction128, prediction128); + switch (order) + { + case 12: + case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); + case 10: + case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); + case 8: + case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); + case 6: + case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); + case 4: + case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); + case 2: + case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); + } + prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); + prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; + } + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) +{ + vst1q_s32(p+0, x.val[0]); + vst1q_s32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) +{ + vst1q_u32(p+0, x.val[0]); + vst1q_u32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) +{ + vst1q_f32(p+0, x.val[0]); + vst1q_f32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) +{ + vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); +} +static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) +{ + vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); +} +static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) +{ + ma_int32 x[4]; + x[3] = x3; + x[2] = x2; + x[1] = x1; + x[0] = x0; + return vld1q_s32(x); +} +static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) +{ + return vextq_s32(b, a, 1); +} +static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +{ + return vextq_u32(b, a, 1); +} +static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) +{ + int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); + return vpadd_s32(r, r); +} +static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) +{ + return vadd_s64(vget_high_s64(x), vget_low_s64(x)); +} +static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) +{ + return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); +} +static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) +{ + return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); +} +static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) +{ + return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int32x2_t shift64; + uint32x4_t one128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s32(-shift); + one128 = vdupq_n_u32(1); + { + int runningOrder = order; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; + } + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; + } + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; + } + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + } + while (pDecodedSamples < pDecodedSamplesEnd) { + int32x4_t prediction128; + int32x2_t prediction64; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; + } + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_8, samples128_8); + prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; + } + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int64x1_t shift64; + uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s64(-shift); + one128 = vdupq_n_u32(1); + { + int runningOrder = order; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; + } + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; + } + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; + } + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + } + while (pDecodedSamples < pDecodedSamplesEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; + } + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + for (i = 0; i < 4; i += 1) { + int64x1_t prediction64; + prediction128 = veorq_s64(prediction128, prediction128); + switch (order) + { + case 12: + case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); + case 10: + case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); + case 8: + case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); + case 6: + case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); + case 4: + case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); + case 2: + case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); + } + prediction64 = ma_dr_flac__vhaddq_s64(prediction128); + prediction64 = vshl_s64(prediction64, shift64); + prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; + } + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + if (ma_dr_flac__gIsSSE41Supported) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported) { + return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#endif + { + #if 0 + return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #else + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #endif + } +} +static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + for (i = 0; i < count; ++i) { + if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { + return MA_FALSE; + } + } + return MA_TRUE; +} +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + for (i = 0; i < count; ++i) { + if (unencodedBitsPerSample > 0) { + if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return MA_FALSE; + } + } else { + pSamplesOut[i] = 0; + } + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; + } + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; + } + pDecodedSamples += lpcOrder; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; + } + if (partitionOrder > 8) { + return MA_FALSE; + } + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { + return MA_FALSE; + } + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; + partitionsRemaining = (1 << partitionOrder); + for (;;) { + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + if (riceParam != 0xFF) { + if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + } else { + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; + } + if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + } + pDecodedSamples += samplesInPartition; + if (partitionsRemaining == 1) { + break; + } + partitionsRemaining -= 1; + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) +{ + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; + } + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; + } + if (partitionOrder > 8) { + return MA_FALSE; + } + if ((blockSize / (1 << partitionOrder)) <= order) { + return MA_FALSE; + } + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); + for (;;) + { + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + if (riceParam != 0xFF) { + if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return MA_FALSE; + } + } else { + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; + } + if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return MA_FALSE; + } + } + if (partitionsRemaining == 1) { + break; + } + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + for (i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + for (i = 0; i < blockSize; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + static ma_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + for (i = 0; i < lpcOrder; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +{ + ma_uint8 i; + ma_uint8 lpcPrecision; + ma_int8 lpcShift; + ma_int32 coefficients[32]; + for (i = 0; i < lpcOrder; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; + } + if (lpcPrecision == 15) { + return MA_FALSE; + } + lpcPrecision += 1; + if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { + return MA_FALSE; + } + if (lpcShift < 0) { + return MA_FALSE; + } + MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + for (i = 0; i < lpcOrder; ++i) { + if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { + return MA_FALSE; + } + } + if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) +{ + const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(header != NULL); + for (;;) { + ma_uint8 crc8 = 0xCE; + ma_uint8 reserved = 0; + ma_uint8 blockingStrategy = 0; + ma_uint8 blockSize = 0; + ma_uint8 sampleRate = 0; + ma_uint8 channelAssignment = 0; + ma_uint8 bitsPerSample = 0; + ma_bool32 isVariableBlockSize; + if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); + if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { + return MA_FALSE; + } + if (blockSize == 0) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { + return MA_FALSE; + } + if (channelAssignment > 10) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); + if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { + return MA_FALSE; + } + if (bitsPerSample == 3 || bitsPerSample == 7) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + ma_uint64 pcmFrameNumber; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = 0; + header->pcmFrameNumber = pcmFrameNumber; + } else { + ma_uint64 flacFrameNumber = 0; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = (ma_uint32)flacFrameNumber; + header->pcmFrameNumber = 0; + } + MA_DR_FLAC_ASSERT(blockSize > 0); + if (blockSize == 1) { + header->blockSizeInPCMFrames = 192; + } else if (blockSize <= 5) { + MA_DR_FLAC_ASSERT(blockSize >= 2); + header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); + header->blockSizeInPCMFrames += 1; + } else if (blockSize == 7) { + if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return MA_FALSE; + } + header->blockSizeInPCMFrames += 1; + } else { + MA_DR_FLAC_ASSERT(blockSize >= 8); + header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); + } + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; + } + header->channelAssignment = channelAssignment; + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + if (header->bitsPerSample != streaminfoBitsPerSample) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { + return MA_FALSE; + } +#ifndef MA_DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; + } +#endif + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) +{ + ma_uint8 header; + int type; + if (!ma_dr_flac__read_uint8(bs, 8, &header)) { + return MA_FALSE; + } + if ((header & 0x80) != 0) { + return MA_FALSE; + } + type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (ma_uint8)(type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + } + } + if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { + return MA_FALSE; + } + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return MA_FALSE; + } + pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) +{ + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); + pSubframe = frame->subframes + subframeIndex; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; + } + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + if (subframeBitsPerSample > 32) { + return MA_FALSE; + } + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return MA_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pSamplesS32 = pDecodedSamplesOut; + switch (pSubframe->subframeType) + { + case MA_DR_FLAC_SUBFRAME_CONSTANT: + { + ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_VERBATIM: + { + ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_FIXED: + { + ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_LPC: + { + ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + default: return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) +{ + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); + pSubframe = frame->subframes + subframeIndex; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; + } + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return MA_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pSamplesS32 = NULL; + switch (pSubframe->subframeType) + { + case MA_DR_FLAC_SUBFRAME_CONSTANT: + { + if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_LPC: + { + ma_uint8 lpcPrecision; + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; + } + if (lpcPrecision == 15) { + return MA_FALSE; + } + lpcPrecision += 1; + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; + } + } break; + default: return MA_FALSE; + } + return MA_TRUE; +} +static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) +{ + ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + MA_DR_FLAC_ASSERT(channelAssignment <= 10); + return lookup[channelAssignment]; +} +static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) +{ + int channelCount; + int i; + ma_uint8 paddingSizeInBits; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; +#endif + MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { + return MA_ERROR; + } + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return MA_ERROR; + } + for (i = 0; i < channelCount; ++i) { + if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return MA_ERROR; + } + } + paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + if (paddingSizeInBits > 0) { + ma_uint8 padding = 0; + if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return MA_AT_END; + } + } +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#endif + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; + } +#ifndef MA_DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return MA_CRC_MISMATCH; + } +#endif + pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + return MA_SUCCESS; +} +static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) +{ + int channelCount; + int i; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; +#endif + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + for (i = 0; i < channelCount; ++i) { + if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return MA_ERROR; + } + } + if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return MA_ERROR; + } +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#endif + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; + } +#ifndef MA_DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return MA_CRC_MISMATCH; + } +#endif + return MA_SUCCESS; +} +static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + for (;;) { + ma_result result; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + result = ma_dr_flac__decode_flac_frame(pFlac); + if (result != MA_SUCCESS) { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + return MA_TRUE; + } +} +static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) +{ + ma_uint64 firstPCMFrame; + ma_uint64 lastPCMFrame; + MA_DR_FLAC_ASSERT(pFlac != NULL); + firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; + if (firstPCMFrame == 0) { + firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + } + lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + if (lastPCMFrame > 0) { + lastPCMFrame -= 1; + } + if (pFirstPCMFrame) { + *pFirstPCMFrame = firstPCMFrame; + } + if (pLastPCMFrame) { + *pLastPCMFrame = lastPCMFrame; + } +} +static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) +{ + ma_bool32 result; + MA_DR_FLAC_ASSERT(pFlac != NULL); + result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + pFlac->currentPCMFrame = 0; + return result; +} +static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + return ma_dr_flac__seek_flac_frame(pFlac); +} +static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) +{ + ma_uint64 pcmFramesRead = 0; + while (pcmFramesToSeek > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { + pcmFramesRead += pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; + pcmFramesToSeek = 0; + } else { + pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; + pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + } + } + } + pFlac->currentPCMFrame += pcmFramesRead; + return pcmFramesRead; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(pFlac != NULL); + if (pcmFrameIndex >= pFlac->currentPCMFrame) { + runningPCMFrameCount = pFlac->currentPCMFrame; + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } else { + isMidFrame = MA_TRUE; + } + } else { + runningPCMFrameCount = 0; + if (!ma_dr_flac__seek_to_first_frame(pFlac)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } + for (;;) { + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + if (!isMidFrame) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + if (!isMidFrame) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = MA_FALSE; + } + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return MA_TRUE; + } + } + next_iteration: + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } +} +#if !defined(MA_DR_FLAC_NO_CRC) +#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f +static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + MA_DR_FLAC_ASSERT(targetByte >= rangeLo); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; + for (;;) { + ma_uint64 lastTargetByte = targetByte; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { + if (targetByte == 0) { + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; + } + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); +#if 1 + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#else + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#endif + } + if(targetByte == lastTargetByte) { + return MA_FALSE; + } + } + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + *pLastSuccessfulSeekOffset = targetByte; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) +{ +#if 0 + if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { + if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { + return MA_FALSE; + } + } +#endif + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) +{ + ma_uint64 targetByte; + ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + ma_uint64 pcmRangeHi = 0; + ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; + ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + for (;;) { + if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + ma_uint64 newPCMRangeLo; + ma_uint64 newPCMRangeHi; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + if (pcmRangeLo == newPCMRangeLo) { + if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + break; + } + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; + } else { + break; + } + } + pcmRangeLo = newPCMRangeLo; + pcmRangeHi = newPCMRangeHi; + if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return MA_TRUE; + } else { + break; + } + } else { + const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + if (pcmRangeLo > pcmFrameIndex) { + byteRangeHi = lastSuccessfulSeekOffset; + if (byteRangeLo > byteRangeHi) { + byteRangeLo = byteRangeHi; + } + targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); + if (targetByte < byteRangeLo) { + targetByte = byteRangeLo; + } + } else { + if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; + } else { + break; + } + } else { + byteRangeLo = lastSuccessfulSeekOffset; + if (byteRangeHi < byteRangeLo) { + byteRangeHi = byteRangeLo; + } + targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { + closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; + } + } + } + } + } else { + break; + } + } + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { + return MA_FALSE; + } + if (pcmFrameIndex < seekForwardThreshold) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + } + byteRangeLo = pFlac->firstFLACFramePosInBytes; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); +} +#endif +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_uint32 iClosestSeekpoint = 0; + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + ma_uint32 iSeekpoint; + MA_DR_FLAC_ASSERT(pFlac != NULL); + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return MA_FALSE; + } + for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { + break; + } + iClosestSeekpoint = iSeekpoint; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { + return MA_FALSE; + } +#if !defined(MA_DR_FLAC_NO_CRC) + if (pFlac->totalPCMFrameCount > 0) { + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; + if (iClosestSeekpoint < pFlac->seekpointCount-1) { + ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { + byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; + } + } + if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return MA_TRUE; + } + } + } + } +#endif + if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { + runningPCMFrameCount = pFlac->currentPCMFrame; + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } else { + isMidFrame = MA_TRUE; + } + } else { + runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } + for (;;) { + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + if (!isMidFrame) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + if (!isMidFrame) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = MA_FALSE; + } + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return MA_TRUE; + } + } + next_iteration: + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } +} +#ifndef MA_DR_FLAC_NO_OGG +typedef struct +{ + ma_uint8 capturePattern[4]; + ma_uint8 structureVersion; + ma_uint8 headerType; + ma_uint64 granulePosition; + ma_uint32 serialNumber; + ma_uint32 sequenceNumber; + ma_uint32 checksum; + ma_uint8 segmentCount; + ma_uint8 segmentTable[255]; +} ma_dr_flac_ogg_page_header; +#endif +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + ma_dr_flac_meta_proc onMeta; + ma_dr_flac_container container; + void* pUserData; + void* pUserDataMD; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 runningFilePos; + ma_bool32 hasStreamInfoBlock; + ma_bool32 hasMetadataBlocks; + ma_dr_flac_bs bs; + ma_dr_flac_frame_header firstFrameHeader; +#ifndef MA_DR_FLAC_NO_OGG + ma_uint32 oggSerial; + ma_uint64 oggFirstBytePos; + ma_dr_flac_ogg_page_header oggBosHeader; +#endif +} ma_dr_flac_init_info; +static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) +{ + blockHeader = ma_dr_flac__be2host_32(blockHeader); + *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); + *blockSize = (blockHeader & 0x00FFFFFFUL); +} +static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) +{ + ma_uint32 blockHeader; + *blockSize = 0; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return MA_FALSE; + } + ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) +{ + ma_uint32 blockSizes; + ma_uint64 frameSizes = 0; + ma_uint64 importantProps; + ma_uint8 md5[16]; + if (onRead(pUserData, &blockSizes, 4) != 4) { + return MA_FALSE; + } + if (onRead(pUserData, &frameSizes, 6) != 6) { + return MA_FALSE; + } + if (onRead(pUserData, &importantProps, 8) != 8) { + return MA_FALSE; + } + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return MA_FALSE; + } + blockSizes = ma_dr_flac__be2host_32(blockSizes); + frameSizes = ma_dr_flac__be2host_64(frameSizes); + importantProps = ma_dr_flac__be2host_64(importantProps); + pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + return MA_TRUE; +} +static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_FLAC_MALLOC(sz); +} +static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_FLAC_REALLOC(p, sz); +} +static void ma_dr_flac__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_FLAC_FREE(p); +} +static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint64 runningFilePos = 42; + ma_uint64 seektablePos = 0; + ma_uint32 seektableSize = 0; + for (;;) { + ma_dr_flac_metadata metadata; + ma_uint8 isLastBlock = 0; + ma_uint8 blockType = 0; + ma_uint32 blockSize; + if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { + return MA_FALSE; + } + runningFilePos += 4; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + switch (blockType) + { + case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (blockSize < 4) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); + metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + if (onMeta) { + ma_uint32 seekpointCount; + ma_uint32 iSeekpoint; + void* pRawData; + seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { + ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (blockSize < 8) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + ma_uint32 i; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.vorbis_comment.pComments = pRunningData; + for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { + ma_uint32 commentLength; + if (pRunningDataEnd - pRunningData < 4) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += commentLength; + } + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (blockSize < 396) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + size_t bufferSize; + ma_uint8 iTrack; + ma_uint8 iIndex; + void* pTrackData; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; + metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = NULL; + { + const char* pRunningDataSaved = pRunningData; + bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + ma_uint8 indexCount; + ma_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += 35; + indexCount = pRunningData[0]; + pRunningData += 1; + bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += indexPointSize; + } + pRunningData = pRunningDataSaved; + } + { + char* pRunningTrackData; + pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningTrackData = (char*)pTrackData; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + ma_uint8 indexCount; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); + pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); + } + } + metadata.data.cuesheet.pTrackData = pTrackData; + } + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (blockSize < 32) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; + if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (onMeta) { + metadata.data.padding.unused = 0; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } else { + onMeta(pUserDataMD, &metadata); + } + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: + { + if (onMeta) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } + } + } break; + default: + { + if (onMeta) { + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + } + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } + } + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +{ + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + (void)onSeek; + pInit->container = ma_dr_flac_container_native; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; + } + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + return MA_FALSE; + } else { + pInit->hasStreamInfoBlock = MA_FALSE; + pInit->hasMetadataBlocks = MA_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return MA_FALSE; + } + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return MA_FALSE; + } + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSizeInPCMFrames = 65535; + return MA_TRUE; + } + } else { + ma_dr_flac_streaminfo streaminfo; + if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return MA_FALSE; + } + pInit->hasStreamInfoBlock = MA_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + if (onMeta) { + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + return MA_TRUE; + } +} +#ifndef MA_DR_FLAC_NO_OGG +#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 +#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 +typedef enum +{ + ma_dr_flac_ogg_recover_on_crc_mismatch, + ma_dr_flac_ogg_fail_on_crc_mismatch +} ma_dr_flac_ogg_crc_mismatch_recovery; +#ifndef MA_DR_FLAC_NO_CRC +static ma_uint32 ma_dr_flac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; +#endif +static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) +{ +#ifndef MA_DR_FLAC_NO_CRC + return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} +#if 0 +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) +{ + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); + return crc32; +} +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) +{ + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif +static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) +{ + ma_uint32 i; + for (i = 0; i < dataSize; ++i) { + crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); + } + return crc32; +} +static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) +{ + ma_uint32 pageBodySize = 0; + int i; + for (i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + return pageBodySize; +} +static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +{ + ma_uint8 data[23]; + ma_uint32 i; + MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); + if (onRead(pUserData, data, 23) != 23) { + return MA_AT_END; + } + *pBytesRead += 23; + pHeader->capturePattern[0] = 'O'; + pHeader->capturePattern[1] = 'g'; + pHeader->capturePattern[2] = 'g'; + pHeader->capturePattern[3] = 'S'; + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + for (i = 0; i < 23; ++i) { + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); + } + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return MA_AT_END; + } + *pBytesRead += pHeader->segmentCount; + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + return MA_SUCCESS; +} +static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +{ + ma_uint8 id[4]; + *pBytesRead = 0; + if (onRead(pUserData, id, 4) != 4) { + return MA_AT_END; + } + *pBytesRead += 4; + for (;;) { + if (ma_dr_flac_ogg__is_capture_pattern(id)) { + ma_result result; + *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return MA_AT_END; + } + *pBytesRead += 1; + } + } +} +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + void* pUserData; + ma_uint64 currentBytePos; + ma_uint64 firstBytePos; + ma_uint32 serialNumber; + ma_dr_flac_ogg_page_header bosPageHeader; + ma_dr_flac_ogg_page_header currentPageHeader; + ma_uint32 bytesRemainingInPage; + ma_uint32 pageDataSize; + ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; +} ma_dr_flac_oggbs; +static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + return bytesActuallyRead; +} +static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) +{ + if (origin == ma_dr_flac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + oggbs->currentBytePos = offset; + return MA_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + oggbs->currentBytePos = offset; + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + oggbs->currentBytePos += offset; + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) +{ + ma_dr_flac_ogg_page_header header; + for (;;) { + ma_uint32 crc32 = 0; + ma_uint32 bytesRead; + ma_uint32 pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint32 actualCRC32; +#endif + if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + oggbs->currentBytePos += bytesRead; + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { + continue; + } + if (header.serialNumber != oggbs->serialNumber) { + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + continue; + } + if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return MA_FALSE; + } + oggbs->pageDataSize = pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { + continue; + } else { + ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); + return MA_FALSE; + } + } +#else + (void)recoveryMethod; +#endif + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return MA_TRUE; + } +} +#if 0 +static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) +{ + ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + ma_uint8 iSeg = 0; + ma_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) +{ + for (;;) { + ma_bool32 atEndOfPage = MA_FALSE; + ma_uint8 bytesRemainingInSeg; + ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = MA_TRUE; + } + break; + } + bytesToEndOfPacketOrPage += segmentSize; + } + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + if (atEndOfPage) { + if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { + return MA_FALSE; + } + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return MA_TRUE; + } + } else { + return MA_TRUE; + } + } +} +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) +{ + return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); +} +#endif +static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; + size_t bytesRead = 0; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + break; + } + } + return bytesRead; +} +static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + int bytesSeeked = 0; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (origin == ma_dr_flac_seek_origin_start) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; + } + return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); + } + MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + ma_uint64 originalBytePos; + ma_uint64 runningGranulePosition; + ma_uint64 runningFrameBytePos; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(oggbs != NULL); + originalBytePos = oggbs->currentBytePos; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return MA_FALSE; + } + oggbs->bytesRemainingInPage = 0; + runningGranulePosition = 0; + for (;;) { + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); + return MA_FALSE; + } + runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { + break; + } + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + ma_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { + runningGranulePosition = oggbs->currentPageHeader.granulePosition; + } + continue; + } + } + } + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + return MA_FALSE; + } + runningPCMFrameCount = runningGranulePosition; + for (;;) { + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_uint64 pcmFrameCountInThisFrame; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + pFlac->currentPCMFrame = pcmFrameIndex; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + return MA_TRUE; + } else { + return MA_FALSE; + } + } + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); + if (pcmFramesToDecode == 0) { + return MA_TRUE; + } + pFlac->currentPCMFrame = runningPCMFrameCount; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + } else { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFrame; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + } + } +} +static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +{ + ma_dr_flac_ogg_page_header header; + ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + ma_uint32 bytesRead = 0; + (void)relaxed; + pInit->container = ma_dr_flac_container_ogg; + pInit->oggFirstBytePos = 0; + if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + pInit->runningFilePos += bytesRead; + for (;;) { + int pageBodySize; + if ((header.headerType & 0x02) == 0) { + return MA_FALSE; + } + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { + ma_uint32 bytesRemainingInPage = pageBodySize; + ma_uint8 packetType; + if (onRead(pUserData, &packetType, 1) != 1) { + return MA_FALSE; + } + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + ma_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return MA_FALSE; + } + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + ma_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return MA_FALSE; + } + if (mappingVersion[0] != 1) { + return MA_FALSE; + } + if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + if (onRead(pUserData, sig, 4) != 4) { + return MA_FALSE; + } + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + ma_dr_flac_streaminfo streaminfo; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; + } + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return MA_FALSE; + } + if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + pInit->hasStreamInfoBlock = MA_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + if (onMeta) { + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } else { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + pInit->runningFilePos += pageBodySize; + if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + pInit->runningFilePos += bytesRead; + } + pInit->hasMetadataBlocks = MA_TRUE; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) +{ + ma_bool32 relaxed; + ma_uint8 id[4]; + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return MA_FALSE; + } + MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + ma_dr_flac__reset_cache(&pInit->bs); + relaxed = container != ma_dr_flac_container_unknown; + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return MA_FALSE; + } + pInit->runningFilePos += 4; + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + ma_uint8 header[6]; + ma_uint8 flags; + ma_uint32 headerSize; + if (onRead(pUserData, header, 6) != 6) { + return MA_FALSE; + } + pInit->runningFilePos += 6; + flags = header[1]; + MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef MA_DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + if (relaxed) { + if (container == ma_dr_flac_container_native) { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef MA_DR_FLAC_NO_OGG + if (container == ma_dr_flac_container_ogg) { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + return MA_FALSE; +} +static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pInit != NULL); + MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (ma_uint8)pInit->channels; + pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; + pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; + pFlac->container = pInit->container; +} +static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac_init_info init; + ma_uint32 allocationSize; + ma_uint32 wholeSIMDVectorCountPerChannel; + ma_uint32 decodedSamplesAllocationSize; +#ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac_oggbs* pOggbs = NULL; +#endif + ma_uint64 firstFramePos; + ma_uint64 seektablePos; + ma_uint32 seekpointCount; + ma_allocation_callbacks allocationCallbacks; + ma_dr_flac* pFlac; + ma_dr_flac__init_cpu_caps(); + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + if (pAllocationCallbacks != NULL) { + allocationCallbacks = *pAllocationCallbacks; + if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { + return NULL; + } + } else { + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; + allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; + allocationCallbacks.onFree = ma_dr_flac__free_default; + } + allocationSize = sizeof(ma_dr_flac); + if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; + } + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + allocationSize += decodedSamplesAllocationSize; + allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + allocationSize += sizeof(ma_dr_flac_oggbs); + pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; + } + MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; + } +#endif + firstFramePos = 42; + seektablePos = 0; + seekpointCount = 0; + if (init.hasMetadataBlocks) { + ma_dr_flac_read_proc onReadOverride = onRead; + ma_dr_flac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + onReadOverride = ma_dr_flac__on_read_ogg; + onSeekOverride = ma_dr_flac__on_seek_ogg; + pUserDataOverride = (void*)pOggbs; + } +#endif + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); + } + pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + if (pFlac == NULL) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + ma_dr_flac__init_from_info(pFlac, &init); + pFlac->allocationCallbacks = allocationCallbacks; + pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); + MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; + pFlac->bs.onRead = ma_dr_flac__on_read_ogg; + pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; + } +#endif + pFlac->firstFLACFramePosInBytes = firstFramePos; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + if (seektablePos != 0) { + pFlac->seekpointCount = seekpointCount; + pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); + MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { + ma_uint32 iSeekpoint; + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; + } + } + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + } + } + if (!init.hasStreamInfoBlock) { + pFlac->currentFLACFrame.header = init.firstFrameHeader; + for (;;) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + break; + } else { + if (result == MA_CRC_MISMATCH) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + continue; + } else { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } + } + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_STDIO +#include +#ifndef MA_DR_FLAC_NO_WCHAR +#include +#endif +static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} +static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + MA_DR_FLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + return pFlac; +} +#endif +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + return pFlac; +} +#endif +#endif +static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + size_t bytesRemaining; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + return bytesToRead; +} +static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (offset > (ma_int64)memoryStream->dataSize) { + return MA_FALSE; + } + if (origin == ma_dr_flac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + return MA_FALSE; + } + } else { + if ((ma_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + pFlac->memoryStream = memoryStream; +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + return pFlac; +} +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + pFlac->memoryStream = memoryStream; +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + return pFlac; +} +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); +} +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) +{ + if (pFlac == NULL) { + return; + } +#ifndef MA_DR_FLAC_NO_STDIO + if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { + fclose((FILE*)pFlac->bs.pUserData); + } +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); + if (oggbs->onRead == ma_dr_flac__on_read_stdio) { + fclose((FILE*)oggbs->pUserData); + } + } +#endif +#endif + ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; + int32x4_t wbpsShift1_4; + uint32x4_t one4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + one4 = vdupq_n_u32(1); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + } + } else { + int32x4_t shift4; + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0; + pOutputSamples[i*8+1] = (ma_int32)tempR0; + pOutputSamples[i*8+2] = (ma_int32)tempL1; + pOutputSamples[i*8+3] = (ma_int32)tempR1; + pOutputSamples[i*8+4] = (ma_int32)tempL2; + pOutputSamples[i*8+5] = (ma_int32)tempR2; + pOutputSamples[i*8+6] = (ma_int32)tempL3; + pOutputSamples[i*8+7] = (ma_int32)tempR3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift4_0 = vdupq_n_s32(shift0); + int32x4_t shift4_1 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + } + } + return framesRead; +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = ((ma_int32)(mid0 + side0) >> 1); + temp1L = ((ma_int32)(mid1 + side1) >> 1); + temp2L = ((ma_int32)(mid2 + side2) >> 1); + temp3L = ((ma_int32)(mid3 + side3) >> 1); + temp0R = ((ma_int32)(mid0 - side0) >> 1); + temp1R = ((ma_int32)(mid1 - side1) >> 1); + temp2R = ((ma_int32)(mid2 - side2) >> 1); + temp3R = ((ma_int32)(mid3 - side3) >> 1); + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; + int32x4_t wbpsShift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + } + } else { + int32x4_t shift4; + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + tempL0 >>= 16; + tempL1 >>= 16; + tempL2 >>= 16; + tempL3 >>= 16; + tempR0 >>= 16; + tempR1 >>= 16; + tempR2 >>= 16; + tempR3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)tempL0; + pOutputSamples[i*8+1] = (ma_int16)tempR0; + pOutputSamples[i*8+2] = (ma_int16)tempL1; + pOutputSamples[i*8+3] = (ma_int16)tempR1; + pOutputSamples[i*8+4] = (ma_int16)tempL2; + pOutputSamples[i*8+5] = (ma_int16)tempR2; + pOutputSamples[i*8+6] = (ma_int16)tempL3; + pOutputSamples[i*8+7] = (ma_int16)tempR3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + } + } + return framesRead; +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = _mm_set1_ps(1.0f / 8388608.0f); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + float32x4_t leftf; + float32x4_t rightf; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = _mm_set1_ps(1.0f / 8388608.0f); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + float32x4_t leftf; + float32x4_t rightf; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + float factor = 1 / 2147483648.0; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; + float factor; + __m128 factor128; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = 1.0f / 8388608.0f; + factor128 = _mm_set1_ps(factor); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; + float factor; + float32x4_t factor4; + int32x4_t shift4; + int32x4_t wbps0_4; + int32x4_t wbps1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = 1.0f / 8388608.0f; + factor4 = vdupq_n_f32(factor); + wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float factor = 1.0f / 8388608.0f; + __m128 factor128 = _mm_set1_ps(factor); + for (i = 0; i < frameCount4; ++i) { + __m128i lefti; + __m128i righti; + __m128 leftf; + __m128 rightf; + lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float factor = 1.0f / 8388608.0f; + float32x4_t factor4 = vdupq_n_f32(factor); + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; + } + } + return framesRead; +} +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + if (pFlac == NULL) { + return MA_FALSE; + } + if (pFlac->currentPCMFrame == pcmFrameIndex) { + return MA_TRUE; + } + if (pFlac->firstFLACFramePosInBytes == 0) { + return MA_FALSE; + } + if (pcmFrameIndex == 0) { + pFlac->currentPCMFrame = 0; + return ma_dr_flac__seek_to_first_frame(pFlac); + } else { + ma_bool32 wasSuccessful = MA_FALSE; + ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; + if (pcmFrameIndex > pFlac->totalPCMFrameCount) { + pcmFrameIndex = pFlac->totalPCMFrameCount; + } + if (pcmFrameIndex > pFlac->currentPCMFrame) { + ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { + pFlac->currentFLACFrame.pcmFramesRemaining -= offset; + pFlac->currentPCMFrame = pcmFrameIndex; + return MA_TRUE; + } + } else { + ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + if (currentFLACFramePCMFramesConsumed > offsetAbs) { + pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; + pFlac->currentPCMFrame = pcmFrameIndex; + return MA_TRUE; + } + } +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + } + else +#endif + { + if (!pFlac->_noSeekTableSeek) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + } +#if !defined(MA_DR_FLAC_NO_CRC) + if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + } +#endif + if (!wasSuccessful && !pFlac->_noBruteForceSeek) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + } + } + if (wasSuccessful) { + pFlac->currentPCMFrame = pcmFrameIndex; + } else { + if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { + ma_dr_flac_seek_to_pcm_frame(pFlac, 0); + } + } + return wasSuccessful; + } +} +#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ +{ \ + type* pSampleData = NULL; \ + ma_uint64 totalPCMFrameCount; \ + \ + MA_DR_FLAC_ASSERT(pFlac != NULL); \ + \ + totalPCMFrameCount = pFlac->totalPCMFrameCount; \ + \ + if (totalPCMFrameCount == 0) { \ + type buffer[4096]; \ + ma_uint64 pcmFramesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ + \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ + type* pNewSampleData; \ + size_t newSampleDataBufferSize; \ + \ + newSampleDataBufferSize = sampleDataBufferSize * 2; \ + pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pNewSampleData == NULL) { \ + ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + goto on_error; \ + } \ + \ + sampleDataBufferSize = newSampleDataBufferSize; \ + pSampleData = pNewSampleData; \ + } \ + \ + MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + totalPCMFrameCount += pcmFramesRead; \ + } \ + \ + \ + MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + } else { \ + ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ + goto on_error; \ + } \ + \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ + \ + ma_dr_flac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + ma_dr_flac_close(pFlac); \ + return NULL; \ +} +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +#endif +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_flac__free_default(p, NULL); + } +} +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) +{ + if (pIter == NULL) { + return; + } + pIter->countRemaining = commentCount; + pIter->pRunningData = (const char*)pComments; +} +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) +{ + ma_int32 length; + const char* pComment; + if (pCommentLengthOut) { + *pCommentLengthOut = 0; + } + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); + pIter->pRunningData += 4; + pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + if (pCommentLengthOut) { + *pCommentLengthOut = length; + } + return pComment; +} +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) +{ + if (pIter == NULL) { + return; + } + pIter->countRemaining = trackCount; + pIter->pRunningData = (const char*)pTrackData; +} +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) +{ + ma_dr_flac_cuesheet_track cuesheetTrack; + const char* pRunningData; + ma_uint64 offsetHi; + ma_uint64 offsetLo; + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return MA_FALSE; + } + pRunningData = pIter->pRunningData; + offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + cuesheetTrack.offset = offsetLo | (offsetHi << 32); + cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; + MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; + cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; + cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; + cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + pIter->pRunningData = pRunningData; + pIter->countRemaining -= 1; + if (pCuesheetTrack) { + *pCuesheetTrack = cuesheetTrack; + } + return MA_TRUE; +} +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#endif +/* dr_flac_c end */ +#endif /* MA_DR_FLAC_IMPLEMENTATION */ +#endif /* MA_NO_FLAC */ + +#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) +#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_mp3_c begin */ +#ifndef ma_dr_mp3_c +#define ma_dr_mp3_c +#include +#include +#include +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_MP3_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_MP3_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_MP3_VERSION_REVISION; + } +} +MA_API const char* ma_dr_mp3_version_string(void) +{ + return MA_DR_MP3_VERSION_STRING; +} +#if defined(__TINYC__) +#define MA_DR_MP3_NO_SIMD +#endif +#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) +#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 +#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES +#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 +#endif +#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE +#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 +#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 +#define MA_DR_MP3_STOP_BLOCK_TYPE 3 +#define MA_DR_MP3_MODE_MONO 3 +#define MA_DR_MP3_MODE_JOINT_STEREO 1 +#define MA_DR_MP3_HDR_SIZE 4 +#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) +#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 +#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) +#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) +#if !defined(MA_DR_MP3_NO_SIMD) +#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#define MA_DR_MP3_ONLY_SIMD +#endif +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#if defined(_MSC_VER) +#include +#endif +#include +#define MA_DR_MP3_HAVE_SSE 1 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE _mm_storeu_ps +#define MA_DR_MP3_VLD _mm_loadu_ps +#define MA_DR_MP3_VSET _mm_set1_ps +#define MA_DR_MP3_VADD _mm_add_ps +#define MA_DR_MP3_VSUB _mm_sub_ps +#define MA_DR_MP3_VMUL _mm_mul_ps +#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 ma_dr_mp3_f4; +#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) +#define ma_dr_mp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int ma_dr_mp3_have_simd(void) +{ +#ifdef MA_DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif + if (g_have_simd) + goto end; + ma_dr_mp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + ma_dr_mp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; + return g_have_simd - 1; + } +end: + return g_have_simd - 1; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#include +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE vst1q_f32 +#define MA_DR_MP3_VLD vld1q_f32 +#define MA_DR_MP3_VSET vmovq_n_f32 +#define MA_DR_MP3_VADD vaddq_f32 +#define MA_DR_MP3_VSUB vsubq_f32 +#define MA_DR_MP3_VMUL vmulq_f32 +#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t ma_dr_mp3_f4; +static int ma_dr_mp3_have_simd(void) +{ + return 1; +} +#else +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 0 +#ifdef MA_DR_MP3_ONLY_SIMD +#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif +#else +#define MA_DR_MP3_HAVE_SIMD 0 +#endif +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) +#define MA_DR_MP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) +{ + ma_int32 x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#else +#define MA_DR_MP3_HAVE_ARMV6 0 +#endif +#ifndef MA_DR_MP3_ASSERT +#include +#define MA_DR_MP3_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_MP3_COPY_MEMORY +#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_MP3_MOVE_MEMORY +#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif +#ifndef MA_DR_MP3_ZERO_MEMORY +#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_MP3_MALLOC +#define MA_DR_MP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_MP3_REALLOC +#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_MP3_FREE +#define MA_DR_MP3_FREE(p) free((p)) +#endif +typedef struct +{ + const ma_uint8 *buf; + int pos, limit; +} ma_dr_mp3_bs; +typedef struct +{ + float scf[3*64]; + ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} ma_dr_mp3_L12_scale_info; +typedef struct +{ + ma_uint8 tab_offset, code_tab_width, band_count; +} ma_dr_mp3_L12_subband_alloc; +typedef struct +{ + const ma_uint8 *sfbtab; + ma_uint16 part_23_length, big_values, scalefac_compress; + ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + ma_uint8 table_select[3], region_count[3], subblock_gain[3]; + ma_uint8 preflag, scalefac_scale, count1_table, scfsi; +} ma_dr_mp3_L3_gr_info; +typedef struct +{ + ma_dr_mp3_bs bs; + ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + ma_dr_mp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + ma_uint8 ist_pos[2][39]; +} ma_dr_mp3dec_scratch; +static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} +static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) +{ + ma_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const ma_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} +static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && + (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && + (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); +} +static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) +{ + return ma_dr_mp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); +} +static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) +{ + static const ma_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; +} +static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); +} +static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) +{ + return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); +} +static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) +{ + int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); + if (MA_DR_MP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; + } + return frame_bytes ? frame_bytes : free_format_size; +} +static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) +{ + return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} +#ifndef MA_DR_MP3_ONLY_MP3 +static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) +{ + const ma_dr_mp3_L12_subband_alloc *alloc; + int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); + if (!kbps) + { + kbps = 192; + } + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + sci->total_bands = (ma_uint8)nbands; + sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); + return alloc; +} +static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = ma_dr_mp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} +static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) +{ + static const ma_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); + int i, k = 0, ba_bits = 0; + const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; + for (i = 0; i < sci->total_bands; i++) + { + ma_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); + } + ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} +static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; + unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} +static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif +static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +{ + static const ma_uint8 g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const ma_uint8 g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const ma_uint8 g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); + scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + do + { + if (MA_DR_MP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (ma_dr_mp3_bs_get_bits(bs, 1)) + { + gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = ma_dr_mp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = ma_dr_mp3_bs_get_bits(bs, 15); + gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); + gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (ma_uint8)(tables >> 10); + gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); + gr->table_select[2] = (ma_uint8)((tables) & 31); + gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + return main_data_begin; +} +static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + MA_DR_MP3_ZERO_MEMORY(scf, cnt); + MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); + scf[k] = (ma_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} +static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = MA_DR_MP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} +static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) +{ + static const ma_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + ma_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); + } else + { + static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + } + } else if (gr->preflag) + { + static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); + } + } + gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} +static const float g_ma_dr_mp3_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 ma_dr_mp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + if (x < 129) + { + return g_ma_dr_mp3_pow43[16 + x]; + } + if (x < 1024) + { + mult = 16; + x <<= 3; + } + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} +static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const ma_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, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,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,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; +#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const ma_uint8 *sfb = gr_info->sfbtab; + const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const ma_int16 *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + while (leaf < 0) + { + MA_DR_MP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + } + MA_DR_MP3_FLUSH_BITS(leaf >> 8); + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += MA_DR_MP3_PEEK_BITS(linbits); + MA_DR_MP3_FLUSH_BITS(linbits); + MA_DR_MP3_CHECK_BITS; + *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + } + MA_DR_MP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + while (leaf < 0) + { + MA_DR_MP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + } + MA_DR_MP3_FLUSH_BITS(leaf >> 8); + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + } + MA_DR_MP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + for (np = 1 - big_val_cnt;; dst += 4) + { + const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + MA_DR_MP3_FLUSH_BITS(leaf & 7); + if (MA_DR_MP3_BSPOS > layer3gr_limit) + { + break; + } +#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(0); + MA_DR_MP3_DEQ_COUNT1(1); + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(2); + MA_DR_MP3_DEQ_COUNT1(3); + MA_DR_MP3_CHECK_BITS; + } + bs->pos = layer3gr_limit; +} +static void ma_dr_mp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) + { + for (; i < n - 3; i += 4) + { + ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); + ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); + MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); + MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); + } +#ifdef __GNUC__ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} +static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} +static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + max_band[0] = max_band[1] = max_band[2] = -1; + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} +static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) + { + ma_dr_mp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} +static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} +static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); +} +static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) + { + ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); + ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); + ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); + ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); + vd = MA_DR_MP3_VREV(vd); + MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); + vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); + } +#endif +#ifndef MA_DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} +static void ma_dr_mp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} +static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + ma_dr_mp3_L3_dct3_9(co); + ma_dr_mp3_L3_dct3_9(si); + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + i = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) + { + ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); + ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); + ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); + ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); + ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); + ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); + ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); + ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); + MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); + MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); + vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} +static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} +static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} +static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} +static void ma_dr_mp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} +static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} +static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} +static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); + MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); + MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} +static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) +{ + int ch; + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) + { + ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) + { + ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); + } + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); + ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + ma_dr_mp3_L3_change_sign(s->grbuf[ch]); + } +} +static void ma_dr_mp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) + { + ma_dr_mp3_f4 t[4][8], *x; + float *y = grbuf + k; + for (x = t[0], i = 0; i < 8; i++, x++) + { + ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); + ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); + ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); + ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); + ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); + ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); + ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); + ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = MA_DR_MP3_VADD(t0, t1); + x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = MA_DR_MP3_VADD(t3, t2); + x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); + x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); + x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); + x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); + x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); + x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); + x[0] = MA_DR_MP3_VADD(x0, x1); + x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); + x5 = MA_DR_MP3_VADD(x5, x6); + x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); + x7 = MA_DR_MP3_VADD(x7, xt); + x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); + x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); + x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); + x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); + x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); + x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); + x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); + } + if (k > n - 3) + { +#if MA_DR_MP3_HAVE_SSE +#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE2(0, t[0][i]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); + } + MA_DR_MP3_VSAVE2(0, t[0][7]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE2(2, t[1][7]); + MA_DR_MP3_VSAVE2(3, t[3][7]); + } else + { +#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE4(0, t[0][i]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); + } + MA_DR_MP3_VSAVE4(0, t[0][7]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE4(2, t[1][7]); + MA_DR_MP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef MA_DR_MP3_ONLY_SIMD + {} +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} +#ifndef MA_DR_MP3_FLOAT_OUTPUT +typedef ma_int16 ma_dr_mp3d_sample_t; +static ma_int16 ma_dr_mp3d_scale_pcm(float sample) +{ + ma_int16 s; +#if MA_DR_MP3_HAVE_ARMV6 + ma_int32 s32 = (ma_int32)(sample + .5f); + s32 -= (s32 < 0); + s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); +#else + if (sample >= 32766.5) return (ma_int16) 32767; + if (sample <= -32767.5) return (ma_int16)-32768; + s = (ma_int16)(sample + .5f); + s -= (s < 0); +#endif + return s; +} +#else +typedef float ma_dr_mp3d_sample_t; +static float ma_dr_mp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif +static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = ma_dr_mp3d_scale_pcm(a); + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); +} +static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + ma_dr_mp3d_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, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); + ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } +#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } +#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } + ma_dr_mp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) + { +#ifndef MA_DR_MP3_FLOAT_OUTPUT +#if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const ma_dr_mp3_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] = (ma_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif +#else + #if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif + a = MA_DR_MP3_VMUL(a, g_scale); + b = MA_DR_MP3_VMUL(b, g_scale); +#if MA_DR_MP3_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 + } + } else +#endif +#ifdef MA_DR_MP3_ONLY_SIMD + {} +#else + for (i = 14; i >= 0; i--) + { +#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) + dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); + } +#endif +} +static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); + } + MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + for (i = 0; i < nbands; i += 2) + { + ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} +static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); + if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} +static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) + { + if (ma_dr_mp3_hdr_valid(mp3)) + { + int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); + for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) + { + if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - ma_dr_mp3_hdr_padding(mp3); + int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); + if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) +{ + dec->header[0] = 0; +} +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const ma_uint8 *hdr; + ma_dr_mp3_bs bs_frame[1]; + ma_dr_mp3dec_scratch scratch; + if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) + { + frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); + i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + hdr = mp3 + i; + MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); + ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); + if (MA_DR_MP3_HDR_IS_CRC(hdr)) + { + ma_dr_mp3_bs_get_bits(bs_frame, 16); + } + if (info->layer == 3) + { + int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + ma_dr_mp3dec_init(dec); + return 0; + } + success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success && pcm != NULL) + { + for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) + { + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + } + } + ma_dr_mp3_L3_save_reservoir(dec, &scratch); + } else + { +#ifdef MA_DR_MP3_ONLY_MP3 + return 0; +#else + ma_dr_mp3_L12_scale_info sci[1]; + if (pcm == NULL) { + return ma_dr_mp3_hdr_frame_samples(hdr); + } + ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); + } + if (bs_frame->pos > bs_frame->limit) + { + ma_dr_mp3dec_init(dec); + return 0; + } + } +#endif + } + return success*ma_dr_mp3_hdr_frame_samples(dec->header); +} +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) +{ + size_t i = 0; +#if MA_DR_MP3_HAVE_SIMD + size_t aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); + ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); + ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); +#if MA_DR_MP3_HAVE_SSE + ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); + ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); + out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_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] = (ma_int16) 32767; + else if (sample <= -32767.5) + out[i] = (ma_int16)-32768; + else + { + short s = (ma_int16)(sample + .5f); + s -= (s < 0); + out[i] = s; + } + } +} +#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES +#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 +#endif +#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 +#ifndef MA_DR_MP3_DATA_CHUNK_SIZE +#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) +#endif +#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) +#ifndef MA_DR_MP3_PI_D +#define MA_DR_MP3_PI_D 3.14159265358979323846264 +#endif +#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 +static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; +} +static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + ma_uint32 t = a; + a = b; + b = t % a; + } + } + return a; +} +static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_MP3_MALLOC(sz); +} +static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_MP3_REALLOC(p, sz); +} +static void ma_dr_mp3__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_MP3_FREE(p); +} +static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_MP3_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return *pAllocationCallbacks; + } else { + ma_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; + allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; + allocationCallbacks.onFree = ma_dr_mp3__free_default; + return allocationCallbacks; + } +} +static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + return bytesRead; +} +static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) +{ + MA_DR_MP3_ASSERT(offset >= 0); + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { + return MA_FALSE; + } + if (origin == ma_dr_mp3_seek_origin_start) { + pMP3->streamCursor = (ma_uint64)offset; + } else { + pMP3->streamCursor += offset; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) +{ + if (offset <= 0x7FFFFFFF) { + return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); + } + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + while (offset > 0) { + if (offset <= 0x7FFFFFFF) { + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; + } + offset = 0; + } else { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + } + } + return MA_TRUE; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + ma_uint32 pcmFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + if (pMP3->atEnd) { + return 0; + } + for (;;) { + ma_dr_mp3dec_frame_info info; + if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { + size_t bytesRead; + if (pMP3->pData != NULL) { + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } + pMP3->dataConsumed = 0; + if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { + ma_uint8* pNewData; + size_t newDataCap; + newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + if (pMP3->dataSize == 0) { + pMP3->atEnd = MA_TRUE; + return 0; + } + } + pMP3->dataSize += bytesRead; + } + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = MA_TRUE; + return 0; + } + MA_DR_MP3_ASSERT(pMP3->pData != NULL); + MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); + if (pMP3->pData == NULL) { + return 0; + } + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); + if (info.frame_bytes > 0) { + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + } + if (pcmFramesRead > 0) { + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes == 0) { + size_t bytesRead; + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + if (pMP3->dataCapacity == pMP3->dataSize) { + ma_uint8* pNewData; + size_t newDataCap; + newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = MA_TRUE; + return 0; + } + pMP3->dataSize += bytesRead; + } + }; + return pcmFramesRead; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + ma_uint32 pcmFramesRead = 0; + ma_dr_mp3dec_frame_info info; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); + if (pMP3->atEnd) { + return 0; + } + for (;;) { + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + if (pcmFramesRead > 0) { + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes > 0) { + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + } else { + break; + } + } + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + return pcmFramesRead; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + } else { + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + } +} +static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); +} +#if 0 +static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) +{ + ma_uint32 pcmFrameCount; + MA_DR_MP3_ASSERT(pMP3 != NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFrameCount == 0) { + return 0; + } + pMP3->currentPCMFrame += pcmFrameCount; + pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; + pMP3->pcmFramesRemainingInMP3Frame = 0; + return pcmFrameCount; +} +#endif +static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(onRead != NULL); + ma_dr_mp3dec_init(&pMP3->decoder); + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + return MA_FALSE; + } + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL || onRead == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); +} +static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + size_t bytesRemaining; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + return bytesToRead; +} +static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) +{ + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (origin == ma_dr_mp3_seek_origin_current) { + if (byteOffset > 0) { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); + } + } else { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pMP3->memory.currentReadPos; + } + } + pMP3->memory.currentReadPos += byteOffset; + } else { + if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { + pMP3->memory.currentReadPos = byteOffset; + } else { + pMP3->memory.currentReadPos = pMP3->memory.dataSize; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); + if (pData == NULL || dataSize == 0) { + return MA_FALSE; + } + pMP3->memory.pData = (const ma_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); +} +#ifndef MA_DR_MP3_NO_STDIO +#include +#include +static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} +static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + FILE* pFile; + if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + FILE* pFile; + if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +#endif +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) +{ + if (pMP3 == NULL) { + return; + } +#ifndef MA_DR_MP3_NO_STDIO + if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) { + fclose(pFile); + pMP3->pUserData = NULL; + } + } +#endif + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); +} +#if defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) +{ + ma_uint64 i; + ma_uint64 i4; + ma_uint64 sampleCount4; + i = 0; + sampleCount4 = sampleCount >> 2; + for (i4 = 0; i4 < sampleCount4; i4 += 1) { + float x0 = src[i+0]; + float x1 = src[i+1]; + float x2 = src[i+2]; + float x3 = src[i+3]; + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + dst[i+0] = (ma_int16)x0; + dst[i+1] = (ma_int16)x1; + dst[i+2] = (ma_int16)x2; + dst[i+3] = (ma_int16)x3; + i += 4; + } + for (; i < sampleCount; i += 1) { + float x = src[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + x = x * 32767.0f; + dst[i] = (ma_int16)x; + } +} +#endif +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) +{ + ma_uint64 i; + for (i = 0; i < sampleCount; i += 1) { + float x = (float)src[i]; + x = x * 0.000030517578125f; + dst[i] = x; + } +} +#endif +static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + while (framesToRead > 0) { + ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + if (pBufferOut != NULL) { + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #else + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); + #endif + } + pMP3->currentPCMFrame += framesToConsume; + pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; + pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; + totalFramesRead += framesToConsume; + framesToRead -= framesToConsume; + if (framesToRead == 0) { + break; + } + MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + break; + } + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + { + ma_int16 pTempS16[8192]; + ma_uint64 totalPCMFramesRead = 0; + while (totalPCMFramesRead < framesToRead) { + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + if (framesJustRead == 0) { + break; + } + ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + return totalPCMFramesRead; + } +#endif +} +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + { + float pTempF32[4096]; + ma_uint64 totalPCMFramesRead = 0; + while (totalPCMFramesRead < framesToRead) { + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) { + break; + } + ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + return totalPCMFramesRead; + } +#endif +} +static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->currentPCMFrame = 0; + pMP3->dataSize = 0; + pMP3->atEnd = MA_FALSE; + ma_dr_mp3dec_init(&pMP3->decoder); +} +static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); + if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + ma_dr_mp3_reset(pMP3); + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) +{ + ma_uint64 framesRead; +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); +#else + framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); +#endif + if (framesRead != frameOffset) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (frameIndex == pMP3->currentPCMFrame) { + return MA_TRUE; + } + if (frameIndex < pMP3->currentPCMFrame) { + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + } + MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); +} +static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) +{ + ma_uint32 iSeekPoint; + MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); + *pSeekPointIndex = 0; + if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { + return MA_FALSE; + } + for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { + if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { + break; + } + *pSeekPointIndex = iSeekPoint; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + ma_dr_mp3_seek_point seekPoint; + ma_uint32 priorSeekPointIndex; + ma_uint16 iMP3Frame; + ma_uint64 leftoverFrames; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); + MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); + if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; + } else { + seekPoint.seekPosInBytes = 0; + seekPoint.pcmFrameIndex = 0; + seekPoint.mp3FramesToDiscard = 0; + seekPoint.pcmFramesToDiscard = 0; + } + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + ma_dr_mp3_reset(pMP3); + for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { + ma_uint32 pcmFramesRead; + ma_dr_mp3d_sample_t* pPCMFrames; + pPCMFrames = NULL; + if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { + pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; + } + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); + if (pcmFramesRead == 0) { + return MA_FALSE; + } + } + pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; + leftoverFrames = frameIndex - pMP3->currentPCMFrame; + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); +} +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) { + return MA_FALSE; + } + if (frameIndex == 0) { + return ma_dr_mp3_seek_to_start_of_stream(pMP3); + } + if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { + return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + } else { + return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + } +} +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) +{ + ma_uint64 currentPCMFrame; + ma_uint64 totalPCMFrameCount; + ma_uint64 totalMP3FrameCount; + if (pMP3 == NULL) { + return MA_FALSE; + } + if (pMP3->onSeek == NULL) { + return MA_FALSE; + } + currentPCMFrame = pMP3->currentPCMFrame; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + totalPCMFrameCount = 0; + totalMP3FrameCount = 0; + for (;;) { + ma_uint32 pcmFramesInCurrentMP3Frame; + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3Frame == 0) { + break; + } + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; + totalMP3FrameCount += 1; + } + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; + } + if (pMP3FrameCount != NULL) { + *pMP3FrameCount = totalMP3FrameCount; + } + if (pPCMFrameCount != NULL) { + *pPCMFrameCount = totalPCMFrameCount; + } + return MA_TRUE; +} +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) +{ + ma_uint64 totalPCMFrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + return totalPCMFrameCount; +} +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) +{ + ma_uint64 totalMP3FrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + return 0; + } + return totalMP3FrameCount; +} +static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +{ + float srcRatio; + float pcmFrameCountOutF; + ma_uint32 pcmFrameCountOut; + srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; + MA_DR_MP3_ASSERT(srcRatio > 0); + pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); + pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; + *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; + *pRunningPCMFrameCount += pcmFrameCountOut; +} +typedef struct +{ + ma_uint64 bytePos; + ma_uint64 pcmFrameIndex; +} ma_dr_mp3__seeking_mp3_frame_info; +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) +{ + ma_uint32 seekPointCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalMP3FrameCount; + ma_uint64 totalPCMFrameCount; + if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { + return MA_FALSE; + } + seekPointCount = *pSeekPointCount; + if (seekPointCount == 0) { + return MA_FALSE; + } + currentPCMFrame = pMP3->currentPCMFrame; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return MA_FALSE; + } + if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { + seekPointCount = 1; + pSeekPoints[0].seekPosInBytes = 0; + pSeekPoints[0].pcmFrameIndex = 0; + pSeekPoints[0].mp3FramesToDiscard = 0; + pSeekPoints[0].pcmFramesToDiscard = 0; + } else { + ma_uint64 pcmFramesBetweenSeekPoints; + ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; + ma_uint64 runningPCMFrameCount = 0; + float runningPCMFrameCountFractionalPart = 0; + ma_uint64 nextTargetPCMFrame; + ma_uint32 iMP3Frame; + ma_uint32 iSeekPoint; + if (seekPointCount > totalMP3FrameCount-1) { + seekPointCount = (ma_uint32)totalMP3FrameCount-1; + } + pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + return MA_FALSE; + } + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + nextTargetPCMFrame = 0; + for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { + nextTargetPCMFrame += pcmFramesBetweenSeekPoints; + for (;;) { + if (nextTargetPCMFrame < runningPCMFrameCount) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } else { + size_t i; + ma_uint32 pcmFramesInCurrentMP3FrameIn; + for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { + mp3FrameInfo[i] = mp3FrameInfo[i+1]; + } + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + } + } + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; + } + } + *pSeekPointCount = seekPointCount; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) +{ + if (pMP3 == NULL) { + return MA_FALSE; + } + if (seekPointCount == 0 || pSeekPoints == NULL) { + pMP3->seekPointCount = 0; + pMP3->pSeekPoints = NULL; + } else { + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + } + return MA_TRUE; +} +static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +{ + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + float* pFrames = NULL; + float temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); + for (;;) { + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + if (framesCapacity < totalFramesRead + framesJustRead) { + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesBufferSize; + ma_uint64 newFramesCap; + float* pNewFrames; + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + break; + } + pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + if (framesJustRead != framesToReadRightNow) { + break; + } + } + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + ma_dr_mp3_uninit(pMP3); + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + return pFrames; +} +static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +{ + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + ma_int16* pFrames = NULL; + ma_int16 temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); + for (;;) { + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + if (framesCapacity < totalFramesRead + framesJustRead) { + ma_uint64 newFramesBufferSize; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesCap; + ma_int16* pNewFrames; + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + break; + } + pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); + totalFramesRead += framesJustRead; + if (framesJustRead != framesToReadRightNow) { + break; + } + } + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + ma_dr_mp3_uninit(pMP3); + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + return pFrames; +} +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); + } else { + return ma_dr_mp3__malloc_default(sz, NULL); + } +} +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_mp3__free_default(p, NULL); + } +} +#endif +/* dr_mp3_c end */ +#endif /* MA_DR_MP3_IMPLEMENTATION */ +#endif /* MA_NO_MP3 */ + + +/* End globally disabled warnings. */ +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif /* miniaudio_c */ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.h b/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.h new file mode 100644 index 0000000..a8c9b6b --- /dev/null +++ b/thirdparty/miniaudio/extras/miniaudio_split/miniaudio.h @@ -0,0 +1,7766 @@ +/* +Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. +miniaudio - v0.11.21 - 2023-11-15 + +David Reid - mackron@gmail.com + +Website: https://miniaud.io +Documentation: https://miniaud.io/docs +GitHub: https://github.com/mackron/miniaudio +*/ +#ifndef miniaudio_h +#define miniaudio_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define MA_STRINGIFY(x) #x +#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) + +#define MA_VERSION_MAJOR 0 +#define MA_VERSION_MINOR 11 +#define MA_VERSION_REVISION 21 +#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ + #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif + + + +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + #define MA_SIZEOF_PTR 8 +#else + #define MA_SIZEOF_PTR 4 +#endif + +#include /* For size_t. */ + +/* Sized types. */ +#if defined(MA_USE_STDINT) + #include + typedef int8_t ma_int8; + typedef uint8_t ma_uint8; + typedef int16_t ma_int16; + typedef uint16_t ma_uint16; + typedef int32_t ma_int32; + typedef uint32_t ma_uint32; + typedef int64_t ma_int64; + typedef uint64_t ma_uint64; +#else + typedef signed char ma_int8; + typedef unsigned char ma_uint8; + typedef signed short ma_int16; + typedef unsigned short ma_uint16; + typedef signed int ma_int32; + typedef unsigned int ma_uint32; + #if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 ma_int64; + typedef unsigned __int64 ma_uint64; + #else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long ma_int64; + typedef unsigned long long ma_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif + #endif +#endif /* MA_USE_STDINT */ + +#if MA_SIZEOF_PTR == 8 + typedef ma_uint64 ma_uintptr; +#else + typedef ma_uint32 ma_uintptr; +#endif + +typedef ma_uint8 ma_bool8; +typedef ma_uint32 ma_bool32; +#define MA_TRUE 1 +#define MA_FALSE 0 + +/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ +typedef float ma_float; +typedef double ma_double; + +typedef void* ma_handle; +typedef void* ma_ptr; + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else +typedef void* ma_proc; +#endif + +#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) +typedef ma_uint16 wchar_t; +#endif + +/* Define NULL for some compilers. */ +#ifndef NULL +#define NULL 0 +#endif + +#if defined(SIZE_MAX) + #define MA_SIZE_MAX SIZE_MAX +#else + #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ +#endif + + +/* Platform/backend detection. */ +#if defined(_WIN32) || defined(__COSMOPOLITAN__) + #define MA_WIN32 + #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + #define MA_WIN32_UWP + #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) + #define MA_WIN32_GDK + #else + #define MA_WIN32_DESKTOP + #endif +#endif +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ + #define MA_POSIX + + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #endif + + #if defined(__unix__) + #define MA_UNIX + #endif + #if defined(__linux__) + #define MA_LINUX + #endif + #if defined(__APPLE__) + #define MA_APPLE + #endif + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) + #define MA_ANDROID + #endif + #if defined(__EMSCRIPTEN__) + #define MA_EMSCRIPTEN + #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif + #if defined(__NX__) + #define MA_NX + #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif +#endif + +#if defined(__has_c_attribute) + #if __has_c_attribute(fallthrough) + #define MA_FALLTHROUGH [[fallthrough]] + #endif +#endif +#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) + #if __has_attribute(fallthrough) + #define MA_FALLTHROUGH __attribute__((fallthrough)) + #endif +#endif +#if !defined(MA_FALLTHROUGH) + #define MA_FALLTHROUGH ((void)0) +#endif + +#ifdef _MSC_VER + #define MA_INLINE __forceinline + + /* noinline was introduced in Visual Studio 2005. */ + #if _MSC_VER >= 1400 + #define MA_NO_INLINE __declspec(noinline) + #else + #define MA_NO_INLINE + #endif +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define MA_GNUC_INLINE_HINT __inline__ + #else + #define MA_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) + #define MA_NO_INLINE __attribute__((noinline)) + #else + #define MA_INLINE MA_GNUC_INLINE_HINT + #define MA_NO_INLINE __attribute__((noinline)) + #endif +#elif defined(__WATCOMC__) + #define MA_INLINE __inline + #define MA_NO_INLINE +#else + #define MA_INLINE + #define MA_NO_INLINE +#endif + +/* MA_DLL is not officially supported. You're on your own if you want to use this. */ +#if defined(MA_DLL) + #if defined(_WIN32) + #define MA_DLL_IMPORT __declspec(dllimport) + #define MA_DLL_EXPORT __declspec(dllexport) + #define MA_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define MA_DLL_IMPORT __attribute__((visibility("default"))) + #define MA_DLL_EXPORT __attribute__((visibility("default"))) + #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define MA_DLL_IMPORT + #define MA_DLL_EXPORT + #define MA_DLL_PRIVATE static + #endif + #endif +#endif + +#if !defined(MA_API) + #if defined(MA_DLL) + #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) + #define MA_API MA_DLL_EXPORT + #else + #define MA_API MA_DLL_IMPORT + #endif + #else + #define MA_API extern + #endif +#endif + +#if !defined(MA_STATIC) + #if defined(MA_DLL) + #define MA_PRIVATE MA_DLL_PRIVATE + #else + #define MA_PRIVATE static + #endif +#endif + + +/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ +#define MA_SIMD_ALIGNMENT 32 + +/* +Special wchar_t type to ensure any structures in the public sections that reference it have a +consistent size across all platforms. + +On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use +wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +platforms. +*/ +#if !defined(MA_POSIX) && defined(MA_WIN32) +typedef wchar_t ma_wchar_win32; +#else +typedef ma_uint16 ma_wchar_win32; +#endif + + + +/* +Logging Levels +============== +Log levels are only used to give logging callbacks some context as to the severity of a log message +so they can do filtering. All log levels will be posted to registered logging callbacks. If you +don't want to output a certain log level you can discriminate against the log level in the callback. + +MA_LOG_LEVEL_DEBUG + Used for debugging. Useful for debug and test builds, but should be disabled in release builds. + +MA_LOG_LEVEL_INFO + Informational logging. Useful for debugging. This will never be called from within the data + callback. + +MA_LOG_LEVEL_WARNING + Warnings. You should enable this in you development builds and action them when encounted. These + logs usually indicate a potential problem or misconfiguration, but still allow you to keep + running. This will never be called from within the data callback. + +MA_LOG_LEVEL_ERROR + Error logging. This will be fired when an operation fails and is subsequently aborted. This can + be fired from within the data callback, in which case the device will be stopped. You should + always have this log level enabled. +*/ +typedef enum +{ + MA_LOG_LEVEL_DEBUG = 4, + MA_LOG_LEVEL_INFO = 3, + MA_LOG_LEVEL_WARNING = 2, + MA_LOG_LEVEL_ERROR = 1 +} ma_log_level; + +/* +Variables needing to be accessed atomically should be declared with this macro for two reasons: + + 1) It allows people who read the code to identify a variable as such; and + 2) It forces alignment on platforms where it's required or optimal. + +Note that for x86/64, alignment is not strictly necessary, but does have some performance +implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU +architecture does not require it, it will simply leave it unaligned. This is the case with old +versions of Visual Studio, which I've confirmed with at least VC6. +*/ +#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + #include + #define MA_ATOMIC(alignment, type) _Alignas(alignment) type +#else + #if defined(__GNUC__) + /* GCC-style compilers. */ + #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) + #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ + /* MSVC. */ + #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type + #else + /* Other compilers. */ + #define MA_ATOMIC(alignment, type) type + #endif +#endif + +typedef struct ma_context ma_context; +typedef struct ma_device ma_device; + +typedef ma_uint8 ma_channel; +typedef enum +{ + MA_CHANNEL_NONE = 0, + MA_CHANNEL_MONO = 1, + MA_CHANNEL_FRONT_LEFT = 2, + MA_CHANNEL_FRONT_RIGHT = 3, + MA_CHANNEL_FRONT_CENTER = 4, + MA_CHANNEL_LFE = 5, + MA_CHANNEL_BACK_LEFT = 6, + MA_CHANNEL_BACK_RIGHT = 7, + MA_CHANNEL_FRONT_LEFT_CENTER = 8, + MA_CHANNEL_FRONT_RIGHT_CENTER = 9, + MA_CHANNEL_BACK_CENTER = 10, + MA_CHANNEL_SIDE_LEFT = 11, + MA_CHANNEL_SIDE_RIGHT = 12, + MA_CHANNEL_TOP_CENTER = 13, + MA_CHANNEL_TOP_FRONT_LEFT = 14, + MA_CHANNEL_TOP_FRONT_CENTER = 15, + MA_CHANNEL_TOP_FRONT_RIGHT = 16, + MA_CHANNEL_TOP_BACK_LEFT = 17, + MA_CHANNEL_TOP_BACK_CENTER = 18, + MA_CHANNEL_TOP_BACK_RIGHT = 19, + MA_CHANNEL_AUX_0 = 20, + MA_CHANNEL_AUX_1 = 21, + MA_CHANNEL_AUX_2 = 22, + MA_CHANNEL_AUX_3 = 23, + MA_CHANNEL_AUX_4 = 24, + MA_CHANNEL_AUX_5 = 25, + MA_CHANNEL_AUX_6 = 26, + MA_CHANNEL_AUX_7 = 27, + MA_CHANNEL_AUX_8 = 28, + MA_CHANNEL_AUX_9 = 29, + MA_CHANNEL_AUX_10 = 30, + MA_CHANNEL_AUX_11 = 31, + MA_CHANNEL_AUX_12 = 32, + MA_CHANNEL_AUX_13 = 33, + MA_CHANNEL_AUX_14 = 34, + MA_CHANNEL_AUX_15 = 35, + MA_CHANNEL_AUX_16 = 36, + MA_CHANNEL_AUX_17 = 37, + MA_CHANNEL_AUX_18 = 38, + MA_CHANNEL_AUX_19 = 39, + MA_CHANNEL_AUX_20 = 40, + MA_CHANNEL_AUX_21 = 41, + MA_CHANNEL_AUX_22 = 42, + MA_CHANNEL_AUX_23 = 43, + MA_CHANNEL_AUX_24 = 44, + MA_CHANNEL_AUX_25 = 45, + MA_CHANNEL_AUX_26 = 46, + MA_CHANNEL_AUX_27 = 47, + MA_CHANNEL_AUX_28 = 48, + MA_CHANNEL_AUX_29 = 49, + MA_CHANNEL_AUX_30 = 50, + MA_CHANNEL_AUX_31 = 51, + MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, + MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, + MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) +} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ + +typedef enum +{ + MA_SUCCESS = 0, + MA_ERROR = -1, /* A generic error. */ + MA_INVALID_ARGS = -2, + MA_INVALID_OPERATION = -3, + MA_OUT_OF_MEMORY = -4, + MA_OUT_OF_RANGE = -5, + MA_ACCESS_DENIED = -6, + MA_DOES_NOT_EXIST = -7, + MA_ALREADY_EXISTS = -8, + MA_TOO_MANY_OPEN_FILES = -9, + MA_INVALID_FILE = -10, + MA_TOO_BIG = -11, + MA_PATH_TOO_LONG = -12, + MA_NAME_TOO_LONG = -13, + MA_NOT_DIRECTORY = -14, + MA_IS_DIRECTORY = -15, + MA_DIRECTORY_NOT_EMPTY = -16, + MA_AT_END = -17, + MA_NO_SPACE = -18, + MA_BUSY = -19, + MA_IO_ERROR = -20, + MA_INTERRUPT = -21, + MA_UNAVAILABLE = -22, + MA_ALREADY_IN_USE = -23, + MA_BAD_ADDRESS = -24, + MA_BAD_SEEK = -25, + MA_BAD_PIPE = -26, + MA_DEADLOCK = -27, + MA_TOO_MANY_LINKS = -28, + MA_NOT_IMPLEMENTED = -29, + MA_NO_MESSAGE = -30, + MA_BAD_MESSAGE = -31, + MA_NO_DATA_AVAILABLE = -32, + MA_INVALID_DATA = -33, + MA_TIMEOUT = -34, + MA_NO_NETWORK = -35, + MA_NOT_UNIQUE = -36, + MA_NOT_SOCKET = -37, + MA_NO_ADDRESS = -38, + MA_BAD_PROTOCOL = -39, + MA_PROTOCOL_UNAVAILABLE = -40, + MA_PROTOCOL_NOT_SUPPORTED = -41, + MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, + MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, + MA_SOCKET_NOT_SUPPORTED = -44, + MA_CONNECTION_RESET = -45, + MA_ALREADY_CONNECTED = -46, + MA_NOT_CONNECTED = -47, + MA_CONNECTION_REFUSED = -48, + MA_NO_HOST = -49, + MA_IN_PROGRESS = -50, + MA_CANCELLED = -51, + MA_MEMORY_ALREADY_MAPPED = -52, + + /* General non-standard errors. */ + MA_CRC_MISMATCH = -100, + + /* General miniaudio-specific errors. */ + MA_FORMAT_NOT_SUPPORTED = -200, + MA_DEVICE_TYPE_NOT_SUPPORTED = -201, + MA_SHARE_MODE_NOT_SUPPORTED = -202, + MA_NO_BACKEND = -203, + MA_NO_DEVICE = -204, + MA_API_NOT_FOUND = -205, + MA_INVALID_DEVICE_CONFIG = -206, + MA_LOOP = -207, + MA_BACKEND_NOT_ENABLED = -208, + + /* State errors. */ + MA_DEVICE_NOT_INITIALIZED = -300, + MA_DEVICE_ALREADY_INITIALIZED = -301, + MA_DEVICE_NOT_STARTED = -302, + MA_DEVICE_NOT_STOPPED = -303, + + /* Operation errors. */ + MA_FAILED_TO_INIT_BACKEND = -400, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, + MA_FAILED_TO_START_BACKEND_DEVICE = -402, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 +} ma_result; + + +#define MA_MIN_CHANNELS 1 +#ifndef MA_MAX_CHANNELS +#define MA_MAX_CHANNELS 254 +#endif + +#ifndef MA_MAX_FILTER_ORDER +#define MA_MAX_FILTER_ORDER 8 +#endif + +typedef enum +{ + ma_stream_format_pcm = 0 +} ma_stream_format; + +typedef enum +{ + ma_stream_layout_interleaved = 0, + ma_stream_layout_deinterleaved +} ma_stream_layout; + +typedef enum +{ + ma_dither_mode_none = 0, + ma_dither_mode_rectangle, + ma_dither_mode_triangle +} ma_dither_mode; + +typedef enum +{ + /* + I like to keep these explicitly defined because they're used as a key into a lookup table. When items are + added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). + */ + ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ + ma_format_u8 = 1, + ma_format_s16 = 2, /* Seems to be the most widely supported format. */ + ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ + ma_format_s32 = 4, + ma_format_f32 = 5, + ma_format_count +} ma_format; + +typedef enum +{ + /* Standard rates need to be in priority order. */ + ma_standard_sample_rate_48000 = 48000, /* Most common */ + ma_standard_sample_rate_44100 = 44100, + + ma_standard_sample_rate_32000 = 32000, /* Lows */ + ma_standard_sample_rate_24000 = 24000, + ma_standard_sample_rate_22050 = 22050, + + ma_standard_sample_rate_88200 = 88200, /* Highs */ + ma_standard_sample_rate_96000 = 96000, + ma_standard_sample_rate_176400 = 176400, + ma_standard_sample_rate_192000 = 192000, + + ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ + ma_standard_sample_rate_11025 = 11025, + ma_standard_sample_rate_8000 = 8000, + + ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ + ma_standard_sample_rate_384000 = 384000, + + ma_standard_sample_rate_min = ma_standard_sample_rate_8000, + ma_standard_sample_rate_max = ma_standard_sample_rate_384000, + ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ +} ma_standard_sample_rate; + + +typedef enum +{ + ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ + ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ + ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ + ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular +} ma_channel_mix_mode; + +typedef enum +{ + ma_standard_channel_map_microsoft, + ma_standard_channel_map_alsa, + ma_standard_channel_map_rfc3551, /* Based off AIFF. */ + ma_standard_channel_map_flac, + ma_standard_channel_map_vorbis, + ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ + ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ + ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ + ma_standard_channel_map_default = ma_standard_channel_map_microsoft +} ma_standard_channel_map; + +typedef enum +{ + ma_performance_profile_low_latency = 0, + ma_performance_profile_conservative +} ma_performance_profile; + + +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} ma_allocation_callbacks; + +typedef struct +{ + ma_int32 state; +} ma_lcg; + + +/* +Atomics. + +These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too +easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By +using a struct we can enforce the use of atomics at compile time. + +These types are declared in the header section because we need to reference them in structs below, but functions for +using them are only exposed in the implementation section. I do not want these to be part of the public API. + +There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are +some macros to help with the declarations. They will be named like so: + + ma_atomic_uint32 - atomic ma_uint32 + ma_atomic_int32 - atomic ma_int32 + ma_atomic_uint64 - atomic ma_uint64 + ma_atomic_float - atomic float + ma_atomic_bool32 - atomic ma_bool32 + +The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific +type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: + + MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) + +Which will declare a type struct that's named like so: + + ma_atomic_ptr_node + +Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with +the name of the struct. For example: + + ma_atomic_uint32_set() - Atomic store of ma_uint32 + ma_atomic_uint32_get() - Atomic load of ma_uint32 + etc. + +For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in +return you get type safety and enforcement of atomic operations. +*/ +#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ + typedef struct \ + { \ + MA_ATOMIC(typeSize, ma_##type) value; \ + } ma_atomic_##type; \ + +#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ + typedef struct \ + { \ + MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ + } ma_atomic_ptr_##type; \ + +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) +MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) +MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) + + +/* Spinlocks are 32-bit for compatibility reasons. */ +typedef ma_uint32 ma_spinlock; + +#ifndef MA_NO_THREADING + /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ + typedef enum + { + ma_thread_priority_idle = -5, + ma_thread_priority_lowest = -4, + ma_thread_priority_low = -3, + ma_thread_priority_normal = -2, + ma_thread_priority_high = -1, + ma_thread_priority_highest = 0, + ma_thread_priority_realtime = 1, + ma_thread_priority_default = 0 + } ma_thread_priority; + + #if defined(MA_POSIX) + typedef ma_pthread_t ma_thread; + #elif defined(MA_WIN32) + typedef ma_handle ma_thread; + #endif + + #if defined(MA_POSIX) + typedef ma_pthread_mutex_t ma_mutex; + #elif defined(MA_WIN32) + typedef ma_handle ma_mutex; + #endif + + #if defined(MA_POSIX) + typedef struct + { + ma_uint32 value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_event; + #elif defined(MA_WIN32) + typedef ma_handle ma_event; + #endif + + #if defined(MA_POSIX) + typedef struct + { + int value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_semaphore; + #elif defined(MA_WIN32) + typedef ma_handle ma_semaphore; + #endif +#else + /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ + #ifndef MA_NO_DEVICE_IO + #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; + #endif +#endif /* MA_NO_THREADING */ + + +/* +Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. +*/ +MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); + +/* +Retrieves the version of miniaudio as a string which can be useful for logging purposes. +*/ +MA_API const char* ma_version_string(void); + + +/************************************************************************************************************************************************************** + +Logging + +**************************************************************************************************************************************************************/ +#include /* For va_list. */ + +#if defined(__has_attribute) + #if __has_attribute(format) + #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) + #endif +#endif +#ifndef MA_ATTRIBUTE_FORMAT +#define MA_ATTRIBUTE_FORMAT(fmt, va) +#endif + +#ifndef MA_MAX_LOG_CALLBACKS +#define MA_MAX_LOG_CALLBACKS 4 +#endif + + +/* +The callback for handling log messages. + + +Parameters +---------- +pUserData (in) + The user data pointer that was passed into ma_log_register_callback(). + +logLevel (in) + The log level. This can be one of the following: + + +----------------------+ + | Log Level | + +----------------------+ + | MA_LOG_LEVEL_DEBUG | + | MA_LOG_LEVEL_INFO | + | MA_LOG_LEVEL_WARNING | + | MA_LOG_LEVEL_ERROR | + +----------------------+ + +pMessage (in) + The log message. +*/ +typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); + +typedef struct +{ + ma_log_callback_proc onLog; + void* pUserData; +} ma_log_callback; + +MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); + + +typedef struct +{ + ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; + ma_uint32 callbackCount; + ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ +#ifndef MA_NO_THREADING + ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ +#endif +} ma_log; + +MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); +MA_API void ma_log_uninit(ma_log* pLog); +MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); +MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); +MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); +MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); +MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); + + +/************************************************************************************************************************************************************** + +Biquad Filtering + +**************************************************************************************************************************************************************/ +typedef union +{ + float f32; + ma_int32 s32; +} ma_biquad_coefficient; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + double b0; + double b1; + double b2; + double a0; + double a1; + double a2; +} ma_biquad_config; + +MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient b0; + ma_biquad_coefficient b1; + ma_biquad_coefficient b2; + ma_biquad_coefficient a1; + ma_biquad_coefficient a2; + ma_biquad_coefficient* pR1; + ma_biquad_coefficient* pR2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_biquad; + +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); +MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); + + +/************************************************************************************************************************************************************** + +Low-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_lpf1_config, ma_lpf2_config; + +MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); +MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient a; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_lpf1; + +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); + +typedef struct +{ + ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ +} ma_lpf2; + +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_lpf_config; + +MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_lpf1* pLPF1; + ma_lpf2* pLPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_lpf; + +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); +MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); + + +/************************************************************************************************************************************************************** + +High-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_hpf1_config, ma_hpf2_config; + +MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); +MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient a; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_hpf1; + +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); +MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); + +typedef struct +{ + ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ +} ma_hpf2; + +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_hpf_config; + +MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_hpf1* pHPF1; + ma_hpf2* pHPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_hpf; + +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); +MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); + + +/************************************************************************************************************************************************************** + +Band-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_bpf2_config; + +MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ +} ma_bpf2; + +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_bpf_config; + +MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 bpf2Count; + ma_bpf2* pBPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_bpf; + +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); +MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); + + +/************************************************************************************************************************************************************** + +Notching Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double q; + double frequency; +} ma_notch2_config, ma_notch_config; + +MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_notch2; + +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); +MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); + + +/************************************************************************************************************************************************************** + +Peaking EQ Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double q; + double frequency; +} ma_peak2_config, ma_peak_config; + +MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_peak2; + +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); +MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); + + +/************************************************************************************************************************************************************** + +Low Shelf Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double shelfSlope; + double frequency; +} ma_loshelf2_config, ma_loshelf_config; + +MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_loshelf2; + +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); + + +/************************************************************************************************************************************************************** + +High Shelf Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double shelfSlope; + double frequency; +} ma_hishelf2_config, ma_hishelf_config; + +MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_hishelf2; + +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); + + + +/* +Delay +*/ +typedef struct +{ + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 delayInFrames; + ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ + float wet; /* 0..1. Default = 1. */ + float dry; /* 0..1. Default = 1. */ + float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ +} ma_delay_config; + +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_delay_config config; + ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ + ma_uint32 bufferSizeInFrames; + float* pBuffer; +} ma_delay; + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); +MA_API float ma_delay_get_wet(const ma_delay* pDelay); +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); +MA_API float ma_delay_get_dry(const ma_delay* pDelay); +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); +MA_API float ma_delay_get_decay(const ma_delay* pDelay); + + +/* Gainer for smooth volume changes. */ +typedef struct +{ + ma_uint32 channels; + ma_uint32 smoothTimeInFrames; +} ma_gainer_config; + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); + + +typedef struct +{ + ma_gainer_config config; + ma_uint32 t; + float masterVolume; + float* pOldGains; + float* pNewGains; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_gainer; + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); + + + +/* Stereo panner. */ +typedef enum +{ + ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ + ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ +} ma_pan_mode; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; +} ma_panner_config; + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ +} ma_panner; + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); +MA_API float ma_panner_get_pan(const ma_panner* pPanner); + + + +/* Fader. */ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_fader_config; + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); + +typedef struct +{ + ma_fader_config config; + float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ + float volumeEnd; + ma_uint64 lengthInFrames; /* The total length of the fade. */ + ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ +} ma_fader; + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); +MA_API float ma_fader_get_current_volume(const ma_fader* pFader); + + + +/* Spatializer. */ +typedef struct +{ + float x; + float y; + float z; +} ma_vec3f; + +typedef struct +{ + ma_vec3f v; + ma_spinlock lock; +} ma_atomic_vec3f; + +typedef enum +{ + ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ + ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ + ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ + ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ +} ma_attenuation_model; + +typedef enum +{ + ma_positioning_absolute, + ma_positioning_relative +} ma_positioning; + +typedef enum +{ + ma_handedness_right, + ma_handedness_left +} ma_handedness; + + +typedef struct +{ + ma_uint32 channelsOut; + ma_channel* pChannelMapOut; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float speedOfSound; + ma_vec3f worldUp; +} ma_spatializer_listener_config; + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); + + +typedef struct +{ + ma_spatializer_listener_config config; + ma_atomic_vec3f position; /* The absolute position of the listener. */ + ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ + ma_atomic_vec3f velocity; + ma_bool32 isEnabled; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_spatializer_listener; + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ +} ma_spatializer_config; + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ + ma_atomic_vec3f position; + ma_atomic_vec3f direction; + ma_atomic_vec3f velocity; /* For doppler effect. */ + float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + float minSpatializationChannelGain; + ma_gainer gainer; /* For smooth gain transitions. */ + float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_spatializer; + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DATA CONVERSION +=============== + +This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ + +/************************************************************************************************************************************************************** + +Resampling + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ + double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ +} ma_linear_resampler_config; + +MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + +typedef struct +{ + ma_linear_resampler_config config; + ma_uint32 inAdvanceInt; + ma_uint32 inAdvanceFrac; + ma_uint32 inTimeInt; + ma_uint32 inTimeFrac; + union + { + float* f32; + ma_int16* s16; + } x0; /* The previous input frame. */ + union + { + float* f32; + ma_int16* s16; + } x1; /* The next input frame. */ + ma_lpf lpf; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_linear_resampler; + +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); +MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); +MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); +MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); +MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); + + +typedef struct ma_resampler_config ma_resampler_config; + +typedef void ma_resampling_backend; +typedef struct +{ + ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); + ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); + void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); + ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); + ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ + ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); +} ma_resampling_backend_vtable; + +typedef enum +{ + ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ + ma_resample_algorithm_custom, +} ma_resample_algorithm; + +struct ma_resampler_config +{ + ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + struct + { + ma_uint32 lpfOrder; + } linear; +}; + +MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); + +typedef struct +{ + ma_resampling_backend* pBackend; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + union + { + ma_linear_resampler linear; + } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_resampler; + +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); + +/* +Initializes a new resampler object from a config. +*/ +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); + +/* +Uninitializes a resampler. +*/ +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Converts the given input data. + +Both the input and output frames must be in the format specified in the config when the resampler was initialized. + +On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that +were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use +ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. + +On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole +input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames +you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. + +If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of +output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input +frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be +processed. In this case, any internal filter state will be updated as if zeroes were passed in. + +It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. + +It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. +*/ +MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); + + +/* +Sets the input and output sample rate. +*/ +MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + +/* +Sets the input and output sample rate as a ratio. + +The ration is in/out. +*/ +MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); + +/* +Retrieves the latency introduced by the resampler in input frames. +*/ +MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); + +/* +Retrieves the latency introduced by the resampler in output frames. +*/ +MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); + +/* +Calculates the number of whole input frames that would need to be read from the client in order to output the specified +number of output frames. + +The returned value does not include cached input frames. It only returns the number of extra frames that would need to be +read from the input buffer in order to output the specified number of output frames. +*/ +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); + +/* +Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of +input frames. +*/ +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); + +/* +Resets the resampler's timer and clears it's internal cache. +*/ +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); + + +/************************************************************************************************************************************************************** + +Channel Conversion + +**************************************************************************************************************************************************************/ +typedef enum +{ + ma_channel_conversion_path_unknown, + ma_channel_conversion_path_passthrough, + ma_channel_conversion_path_mono_out, /* Converting to mono. */ + ma_channel_conversion_path_mono_in, /* Converting from mono. */ + ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ + ma_channel_conversion_path_weights /* Blended based on weights. */ +} ma_channel_conversion_path; + +typedef enum +{ + ma_mono_expansion_mode_duplicate = 0, /* The default. */ + ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ + ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ + ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate +} ma_mono_expansion_mode; + +typedef struct +{ + ma_format format; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + const ma_channel* pChannelMapIn; + const ma_channel* pChannelMapOut; + ma_channel_mix_mode mixingMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ +} ma_channel_converter_config; + +MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); + +typedef struct +{ + ma_format format; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel_mix_mode mixingMode; + ma_channel_conversion_path conversionPath; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; + ma_uint8* pShuffleTable; /* Indexed by output channel index. */ + union + { + float** f32; + ma_int32** s16; + } weights; /* [in][out] */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_channel_converter; + +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); + + +/************************************************************************************************************************************************************** + +Data Conversion + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format formatIn; + ma_format formatOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; + ma_dither_mode ditherMode; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ + ma_bool32 allowDynamicSampleRate; + ma_resampler_config resampling; +} ma_data_converter_config; + +MA_API ma_data_converter_config ma_data_converter_config_init_default(void); +MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + + +typedef enum +{ + ma_data_converter_execution_path_passthrough, /* No conversion. */ + ma_data_converter_execution_path_format_only, /* Only format conversion. */ + ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ + ma_data_converter_execution_path_resample_only, /* Only resampling. */ + ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ + ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ +} ma_data_converter_execution_path; + +typedef struct +{ + ma_format formatIn; + ma_format formatOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_dither_mode ditherMode; + ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ + ma_channel_converter channelConverter; + ma_resampler resampler; + ma_bool8 hasPreFormatConversion; + ma_bool8 hasPostFormatConversion; + ma_bool8 hasChannelConverter; + ma_bool8 hasResampler; + ma_bool8 isPassthrough; + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; +} ma_data_converter; + +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); +MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); +MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); +MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); +MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); + + +/************************************************************************************************************************************************************ + +Format Conversion + +************************************************************************************************************************************************************/ +MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); +MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); + +/* +Deinterleaves an interleaved buffer. +*/ +MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); + +/* +Interleaves a group of deinterleaved buffers. +*/ +MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); + + +/************************************************************************************************************************************************************ + +Channel Maps + +************************************************************************************************************************************************************/ +/* +This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. +*/ +#define MA_CHANNEL_INDEX_NULL 255 + +/* +Retrieves the channel position of the specified channel in the given channel map. + +The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. +*/ +MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); + +/* +Initializes a blank channel map. + +When a blank channel map is specified anywhere it indicates that the native channel map should be used. +*/ +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for retrieving a standard channel map. + +The output channel map buffer must have a capacity of at least `channelMapCap`. +*/ +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); + +/* +Copies a channel map. + +Both input and output channel map buffers must have a capacity of at at least `channels`. +*/ +MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); + +/* +Copies a channel map if one is specified, otherwise copies the default channel map. + +The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. +*/ +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); + + +/* +Determines whether or not a channel map is valid. + +A blank channel map is valid (all channels set to MA_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 + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for comparing two channel maps for equality. + +This assumes the channel count is the same between the two. + +Both channels map buffers must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); + +/* +Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for determining whether or not a channel is present in the given channel map. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); + +/* +Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The +index of the channel is output to `pChannelIndex`. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); + +/* +Generates a string representing the given channel map. + +This is for printing and debugging purposes, not serialization/deserialization. + +Returns the length of the string, not including the null terminator. +*/ +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); + +/* +Retrieves a human readable version of a channel position. +*/ +MA_API const char* ma_channel_position_to_string(ma_channel channel); + + +/************************************************************************************************************************************************************ + +Conversion Helpers + +************************************************************************************************************************************************************/ + +/* +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. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is +ignored. + +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 ma_data_converter APIs instead. +*/ +MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); +MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); + + +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + +/************************************************************************************************************************************************************ + +Ring Buffer + +************************************************************************************************************************************************************/ +typedef struct +{ + void* pBuffer; + ma_uint32 subbufferSizeInBytes; + ma_uint32 subbufferCount; + ma_uint32 subbufferStrideInBytes; + MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ + ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ + ma_allocation_callbacks allocationCallbacks; +} ma_rb; + +MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); +MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); +MA_API void ma_rb_uninit(ma_rb* pRB); +MA_API void ma_rb_reset(ma_rb* pRB); +MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); +MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); +MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); +MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); +MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ +MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); +MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); +MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); + + +typedef struct +{ + ma_data_source_base ds; + ma_rb rb; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ +} ma_pcm_rb; + +MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); +MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); +MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); +MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); +MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); +MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); +MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ +MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); +MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); + + +/* +The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The +capture device writes to it, and then a playback device reads from it. + +At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly +handle desyncs. Note that the API is work in progress and may change at any time in any version. + +The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size +in frames. The internal sample rate of the capture device is also needed in order to calculate the size. +*/ +typedef struct +{ + ma_pcm_rb rb; +} ma_duplex_rb; + +MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); +MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); + + +/************************************************************************************************************************************************************ + +Miscellaneous Helpers + +************************************************************************************************************************************************************/ +/* +Retrieves a human readable description of the given result code. +*/ +MA_API const char* ma_result_description(ma_result result); + +/* +malloc() +*/ +MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +calloc() +*/ +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +realloc() +*/ +MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +free() +*/ +MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Performs an aligned malloc, with the assumption that the alignment is a power of 2. +*/ +MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Free's an aligned malloc'd buffer. +*/ +MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Retrieves a friendly name for a format. +*/ +MA_API const char* ma_get_format_name(ma_format format); + +/* +Blends two frames in floating point format. +*/ +MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_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 API is pure. +*/ +MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); +static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } + +/* +Converts a log level to a string. +*/ +MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); + + + + +/************************************************************************************************************************************************************ + +Synchronization + +************************************************************************************************************************************************************/ +/* +Locks a spinlock. +*/ +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); + +/* +Locks a spinlock, but does not yield() when looping. +*/ +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); + +/* +Unlocks a spinlock. +*/ +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); + + +#ifndef MA_NO_THREADING + +/* +Creates a mutex. + +A mutex must be created from a valid context. A mutex is initially unlocked. +*/ +MA_API ma_result ma_mutex_init(ma_mutex* pMutex); + +/* +Deletes a mutex. +*/ +MA_API void ma_mutex_uninit(ma_mutex* pMutex); + +/* +Locks a mutex with an infinite timeout. +*/ +MA_API void ma_mutex_lock(ma_mutex* pMutex); + +/* +Unlocks a mutex. +*/ +MA_API void ma_mutex_unlock(ma_mutex* pMutex); + + +/* +Initializes an auto-reset event. +*/ +MA_API ma_result ma_event_init(ma_event* pEvent); + +/* +Uninitializes an auto-reset event. +*/ +MA_API void ma_event_uninit(ma_event* pEvent); + +/* +Waits for the specified auto-reset event to become signalled. +*/ +MA_API ma_result ma_event_wait(ma_event* pEvent); + +/* +Signals the specified auto-reset event. +*/ +MA_API ma_result ma_event_signal(ma_event* pEvent); +#endif /* MA_NO_THREADING */ + + +/* +Fence +===== +This locks while the counter is larger than 0. Counter can be incremented and decremented by any +thread, but care needs to be taken when waiting. It is possible for one thread to acquire the +fence just as another thread returns from ma_fence_wait(). + +The idea behind a fence is to allow you to wait for a group of operations to complete. When an +operation starts, the counter is incremented which locks the fence. When the operation completes, +the fence will be released which decrements the counter. ma_fence_wait() will block until the +counter hits zero. + +If threading is disabled, ma_fence_wait() will spin on the counter. +*/ +typedef struct +{ +#ifndef MA_NO_THREADING + ma_event e; +#endif + ma_uint32 counter; +} ma_fence; + +MA_API ma_result ma_fence_init(ma_fence* pFence); +MA_API void ma_fence_uninit(ma_fence* pFence); +MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ +MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ +MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ + + + +/* +Notification callback for asynchronous operations. +*/ +typedef void ma_async_notification; + +typedef struct +{ + void (* onSignal)(ma_async_notification* pNotification); +} ma_async_notification_callbacks; + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); + + +/* +Simple polling notification. + +This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() +*/ +typedef struct +{ + ma_async_notification_callbacks cb; + ma_bool32 signalled; +} ma_async_notification_poll; + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); + + +/* +Event Notification + +This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. +*/ +typedef struct +{ + ma_async_notification_callbacks cb; +#ifndef MA_NO_THREADING + ma_event e; +#endif +} ma_async_notification_event; + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); + + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ + +/* +Slot Allocator +-------------- +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used +as the insertion point for an object. + +Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. + +The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: + + +-----------------+-----------------+ + | 32 Bits | 32 Bits | + +-----------------+-----------------+ + | Reference Count | Slot Index | + +-----------------+-----------------+ +*/ +typedef struct +{ + ma_uint32 capacity; /* The number of slots to make available. */ +} ma_slot_allocator_config; + +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); + + +typedef struct +{ + MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ +} ma_slot_allocator_group; + +typedef struct +{ + ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ + ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ + ma_uint32 count; /* Allocation count. */ + ma_uint32 capacity; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_slot_allocator; + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); + + +typedef struct ma_job ma_job; + +/* +Callback for processing a job. Each job type will have their own processing callback which will be +called by ma_job_process(). +*/ +typedef ma_result (* ma_job_proc)(ma_job* pJob); + +/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ +typedef enum +{ + /* Miscellaneous. */ + MA_JOB_TYPE_QUIT = 0, + MA_JOB_TYPE_CUSTOM, + + /* Resource Manager. */ + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, + + /* Device. */ + MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, + + /* Count. Must always be last. */ + MA_JOB_TYPE_COUNT +} ma_job_type; + +struct ma_job +{ + union + { + struct + { + ma_uint16 code; /* Job type. */ + ma_uint16 slot; /* Index into a ma_slot_allocator. */ + ma_uint32 refcount; + } breakup; + ma_uint64 allocation; + } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ + MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ + ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ + + union + { + /* Miscellaneous. */ + struct + { + ma_job_proc proc; + ma_uintptr data0; + ma_uintptr data1; + } custom; + + /* Resource Manager */ + union + { + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + char* pFilePath; + wchar_t* pFilePathW; + ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ + ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ + ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ + } loadDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + /*ma_decoder**/ void* pDecoder; + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ + } pageDataBufferNode; + + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_uint32 isLooping; + } loadDataBuffer; + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBuffer; + + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ + wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ + ma_uint64 initialSeekPoint; + ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ + ma_fence* pInitFence; + } loadDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint32 pageIndex; /* The index of the page to decode into. */ + } pageDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint64 frameIndex; + } seekDataStream; + } resourceManager; + + /* Device. */ + union + { + union + { + struct + { + /*ma_device**/ void* pDevice; + /*ma_device_type*/ ma_uint32 deviceType; + } reroute; + } aaudio; + } device; + } data; +}; + +MA_API ma_job ma_job_init(ma_uint16 code); +MA_API ma_result ma_job_process(ma_job* pJob); + + +/* +When set, ma_job_queue_next() will not wait and no semaphore will be signaled in +ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. + +This flag should always be used for platforms that do not support multithreading. +*/ +typedef enum +{ + MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 +} ma_job_queue_flags; + +typedef struct +{ + ma_uint32 flags; + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ +} ma_job_queue_config; + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); + + +typedef struct +{ + ma_uint32 flags; /* Flags passed in at initialization time. */ + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ + MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ + MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ +#ifndef MA_NO_THREADING + ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ +#endif + ma_slot_allocator allocator; + ma_job* pJobs; +#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock lock; +#endif + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_job_queue; + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DEVICE I/O +========== + +This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ +#ifndef MA_NO_DEVICE_IO +/* Some backends are only supported on certain platforms. */ +#if defined(MA_WIN32) + #define MA_SUPPORT_WASAPI + + #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ + #define MA_SUPPORT_DSOUND + #define MA_SUPPORT_WINMM + + /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ + #if !defined(__COSMOPOLITAN__) + #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + #endif + #endif +#endif +#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) + #if defined(MA_LINUX) + #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ + #define MA_SUPPORT_ALSA + #endif + #endif + #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) + #define MA_SUPPORT_PULSEAUDIO + #define MA_SUPPORT_JACK + #endif + #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ + #define MA_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 MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ + #endif + #if defined(__FreeBSD__) || defined(__DragonFly__) + #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ + #endif +#endif +#if defined(MA_ANDROID) + #define MA_SUPPORT_AAUDIO + #define MA_SUPPORT_OPENSL +#endif +#if defined(MA_APPLE) + #define MA_SUPPORT_COREAUDIO +#endif +#if defined(MA_EMSCRIPTEN) + #define MA_SUPPORT_WEBAUDIO +#endif + +/* All platforms should support custom backends. */ +#define MA_SUPPORT_CUSTOM + +/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ +#if !defined(MA_EMSCRIPTEN) +#define MA_SUPPORT_NULL +#endif + + +#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) + #define MA_HAS_WASAPI +#endif +#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) + #define MA_HAS_DSOUND +#endif +#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) + #define MA_HAS_WINMM +#endif +#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) + #define MA_HAS_ALSA +#endif +#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) + #define MA_HAS_PULSEAUDIO +#endif +#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) + #define MA_HAS_JACK +#endif +#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) + #define MA_HAS_COREAUDIO +#endif +#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) + #define MA_HAS_SNDIO +#endif +#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) + #define MA_HAS_AUDIO4 +#endif +#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) + #define MA_HAS_OSS +#endif +#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) + #define MA_HAS_AAUDIO +#endif +#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) + #define MA_HAS_OPENSL +#endif +#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) + #define MA_HAS_WEBAUDIO +#endif +#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) + #define MA_HAS_CUSTOM +#endif +#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) + #define MA_HAS_NULL +#endif + +typedef enum +{ + ma_device_state_uninitialized = 0, + ma_device_state_stopped = 1, /* The device's default state after initialization. */ + ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ + ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ + ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ +} ma_device_state; + +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) + + +#ifdef MA_SUPPORT_WASAPI +/* We need a IMMNotificationClient object for WASAPI. */ +typedef struct +{ + void* lpVtbl; + ma_uint32 counter; + ma_device* pDevice; +} ma_IMMNotificationClient; +#endif + +/* Backend enums must be in priority order. */ +typedef enum +{ + ma_backend_wasapi, + ma_backend_dsound, + ma_backend_winmm, + ma_backend_coreaudio, + ma_backend_sndio, + ma_backend_audio4, + ma_backend_oss, + ma_backend_pulseaudio, + ma_backend_alsa, + ma_backend_jack, + ma_backend_aaudio, + ma_backend_opensl, + ma_backend_webaudio, + ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ + ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ +} ma_backend; + +#define MA_BACKEND_COUNT (ma_backend_null+1) + + +/* +Device job thread. This is used by backends that require asynchronous processing of certain +operations. It is not used by all backends. + +The device job thread is made up of a thread and a job queue. You can post a job to the thread with +ma_device_job_thread_post(). The thread will do the processing of the job. +*/ +typedef struct +{ + ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ + ma_uint32 jobQueueCapacity; + ma_uint32 jobQueueFlags; +} ma_device_job_thread_config; + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); + +typedef struct +{ + ma_thread thread; + ma_job_queue jobQueue; + ma_bool32 _hasThread; +} ma_device_job_thread; + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); + + + +/* Device notification types. */ +typedef enum +{ + ma_device_notification_type_started, + ma_device_notification_type_stopped, + ma_device_notification_type_rerouted, + ma_device_notification_type_interruption_began, + ma_device_notification_type_interruption_ended, + ma_device_notification_type_unlocked +} ma_device_notification_type; + +typedef struct +{ + ma_device* pDevice; + ma_device_notification_type type; + union + { + struct + { + int _unused; + } started; + struct + { + int _unused; + } stopped; + struct + { + int _unused; + } rerouted; + struct + { + int _unused; + } interruption; + } data; +} ma_device_notification; + +/* +The notification callback for when the application should be notified of a change to the device. + +This callback is used for notifying the application of changes such as when the device has started, +stopped, rerouted or an interruption has occurred. Note that not all backends will post all +notification types. For example, some backends will perform automatic stream routing without any +kind of notification to the host program which means miniaudio will never know about it and will +never be able to fire the rerouted notification. You should keep this in mind when designing your +program. + +The stopped notification will *not* get fired when a device is rerouted. + + +Parameters +---------- +pNotification (in) + A pointer to a structure containing information about the event. Use the `pDevice` member of + this object to retrieve the relevant device. The `type` member can be used to discriminate + against each of the notification types. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. + +Not all notifications will be triggered by all backends, however the started and stopped events +should be reliable for all backends. Some backends do not have a good way to detect device +stoppages due to unplugging the device which may result in the stopped callback not getting +fired. This has been observed with at least one BSD variant. + +The rerouted notification is fired *after* the reroute has occurred. The stopped notification will +*not* get fired when a device is rerouted. The following backends are known to do automatic stream +rerouting, but do not have a way to be notified of the change: + + * DirectSound + +The interruption notifications are used on mobile platforms for detecting when audio is interrupted +due to things like an incoming phone call. Currently this is only implemented on iOS. None of the +Android backends will report this notification. +*/ +typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); + + +/* +The callback for processing audio data from the device. + +The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data +available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the +callback will be fired with a consistent frame count. + + +Parameters +---------- +pDevice (in) + A pointer to the relevant device. + +pOutput (out) + A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or + full-duplex device and null for a capture and loopback device. + +pInput (in) + A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a + playback device. + +frameCount (in) + The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The + `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must + not assume this will always be the same value each time the callback is fired. + + +Remarks +------- +You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the +callback. The following APIs cannot be called from inside the callback: + + ma_device_init() + ma_device_init_ex() + ma_device_uninit() + ma_device_start() + ma_device_stop() + +The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. +*/ +typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + + + +/* +DEPRECATED. Use ma_device_notification_proc instead. + +The callback for when the device has been stopped. + +This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces +such as being unplugged or an internal error occurring. + + +Parameters +---------- +pDevice (in) + A pointer to the device that has just stopped. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. +*/ +typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ + +typedef enum +{ + ma_device_type_playback = 1, + ma_device_type_capture = 2, + ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ + ma_device_type_loopback = 4 +} ma_device_type; + +typedef enum +{ + ma_share_mode_shared = 0, + ma_share_mode_exclusive +} ma_share_mode; + +/* iOS/tvOS/watchOS session categories. */ +typedef enum +{ + ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ + ma_ios_session_category_none, /* Leave the session category unchanged. */ + ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ + ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ + ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ + ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ + ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ + ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ +} ma_ios_session_category; + +/* iOS/tvOS/watchOS session category options */ +typedef enum +{ + ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ + ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ + ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ + ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ + ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ + ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ + ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ +} ma_ios_session_category_option; + +/* OpenSL stream types. */ +typedef enum +{ + ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ + ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ + ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ + ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ + ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ + ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ + ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ +} ma_opensl_stream_type; + +/* OpenSL recording presets. */ +typedef enum +{ + ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ + ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ + ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ + ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ + ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ + ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ +} ma_opensl_recording_preset; + +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + +/* AAudio usage types. */ +typedef enum +{ + ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ + ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ + ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ + ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ + ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ + ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ + ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ + ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ + ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ + ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ + ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ + ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ + ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ + ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ + ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ + ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ +} ma_aaudio_usage; + +/* AAudio content types. */ +typedef enum +{ + ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ + ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ + ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ + ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ +} ma_aaudio_content_type; + +/* AAudio input presets. */ +typedef enum +{ + ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ + ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ + ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ + ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ + ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ + ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ +} ma_aaudio_input_preset; + +typedef enum +{ + ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ + ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ + ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ + ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ +} ma_aaudio_allowed_capture_policy; + +typedef union +{ + ma_int64 counter; + double counterD; +} ma_timer; + +typedef union +{ + ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ + ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ + /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ + char alsa[256]; /* ALSA uses a name string for identification. */ + char pulse[256]; /* PulseAudio uses a name string for identification. */ + int jack; /* JACK always uses default devices. */ + char coreaudio[256]; /* Core Audio uses a string for identification. */ + char sndio[256]; /* "snd/0", etc. */ + char audio4[256]; /* "/dev/audio", etc. */ + char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ + ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ + ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ + char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ + union + { + int i; + char s[256]; + void* p; + } custom; /* The custom backend could be anything. Give them a few options. */ + int nullbackend; /* The null backend uses an integer for device IDs. */ +} ma_device_id; + + +typedef struct ma_context_config ma_context_config; +typedef struct ma_device_config ma_device_config; +typedef struct ma_backend_callbacks ma_backend_callbacks; + +#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ + +#ifndef MA_MAX_DEVICE_NAME_LENGTH +#define MA_MAX_DEVICE_NAME_LENGTH 255 +#endif + +typedef struct +{ + /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ + ma_device_id id; + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ + ma_bool32 isDefault; + + ma_uint32 nativeDataFormatCount; + struct + { + ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ + ma_uint32 channels; /* If set to 0, all channels are supported. */ + ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ + ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ + } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ +} ma_device_info; + +struct ma_device_config +{ + ma_device_type deviceType; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInMilliseconds; + ma_uint32 periods; + ma_performance_profile performanceProfile; + ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ + ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ + ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ + ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ + ma_device_data_proc dataCallback; + ma_device_notification_proc notificationCallback; + ma_stop_proc stopCallback; + void* pUserData; + ma_resampler_config resampling; + struct + { + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + ma_share_mode shareMode; + } playback; + struct + { + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + ma_share_mode shareMode; + } capture; + + struct + { + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ + ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ + } wasapi; + struct + { + ma_bool32 noMMap; /* Disables MMap mode. */ + ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ + ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ + ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ + } alsa; + struct + { + const char* pStreamNamePlayback; + const char* pStreamNameCapture; + } pulse; + struct + { + ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ + } coreaudio; + struct + { + ma_opensl_stream_type streamType; + ma_opensl_recording_preset recordingPreset; + ma_bool32 enableCompatibilityWorkarounds; + } opensl; + struct + { + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_bool32 noAutoStartAfterReroute; + ma_bool32 enableCompatibilityWorkarounds; + } aaudio; +}; + + +/* +The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +deviceType (in) + The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. + +pInfo (in) + A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, + only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which + is too inefficient. + +pUserData (in) + The user data pointer passed into `ma_context_enumerate_devices()`. +*/ +typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); + + +/* +Describes some basic details about a playback or capture device. +*/ +typedef struct +{ + const ma_device_id* pDeviceID; + ma_share_mode shareMode; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInMilliseconds; + ma_uint32 periodCount; +} ma_device_descriptor; + +/* +These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context +to many devices. A device is created from a context. + +The general flow goes like this: + + 1) A context is created with `onContextInit()` + 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. + 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. + 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was + selected from device enumeration via `onContextEnumerateDevices()`. + 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` + 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call + to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by + miniaudio internally. + +Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the +callbacks defined in this structure. + +Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which +physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the +given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration +needs to stop and the `onContextEnumerateDevices()` function returns with a success code. + +Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, +and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the +case when the device ID is NULL, in which case information about the default device needs to be retrieved. + +Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. +This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a +device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, +the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to +the requested format. The conversion between the format requested by the application and the device's native format will be handled +internally by miniaudio. + +On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's +supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for +sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to +`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should +inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period +size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the +sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` +object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). + +Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses +asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. + +The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit +easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and +`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the +backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. +This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. + +If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback +which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. + +The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been +encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. + +The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this +callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated +which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, +look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to +wake up the audio thread. + +If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the +`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. +*/ +struct ma_backend_callbacks +{ + ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); + ma_result (* onContextUninit)(ma_context* pContext); + ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); + ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); + ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); + ma_result (* onDeviceUninit)(ma_device* pDevice); + ma_result (* onDeviceStart)(ma_device* pDevice); + ma_result (* onDeviceStop)(ma_device* pDevice); + ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); + ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); + ma_result (* onDeviceDataLoop)(ma_device* pDevice); + ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); + ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); +}; + +struct ma_context_config +{ + ma_log* pLog; + ma_thread_priority threadPriority; + size_t threadStackSize; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + struct + { + ma_bool32 useVerboseDeviceEnumeration; + } alsa; + struct + { + const char* pApplicationName; + const char* pServerName; + ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ + } pulse; + struct + { + ma_ios_session_category sessionCategory; + ma_uint32 sessionCategoryOptions; + ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ + ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ + } coreaudio; + struct + { + const char* pClientName; + ma_bool32 tryStartServer; + } jack; + ma_backend_callbacks custom; +}; + +/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ +typedef struct +{ + int code; + ma_event* pEvent; /* This will be signalled when the event is complete. */ + union + { + struct + { + int _unused; + } quit; + struct + { + ma_device_type deviceType; + void* pAudioClient; + void** ppAudioClientService; + ma_result* pResult; /* The result from creating the audio client service. */ + } createAudioClient; + struct + { + ma_device* pDevice; + ma_device_type deviceType; + } releaseAudioClient; + } data; +} ma_context_command__wasapi; + +struct ma_context +{ + ma_backend_callbacks callbacks; + ma_backend backend; /* DirectSound, ALSA, etc. */ + ma_log* pLog; + ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ + ma_thread_priority threadPriority; + size_t threadStackSize; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ + ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ + ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ + ma_uint32 playbackDeviceInfoCount; + ma_uint32 captureDeviceInfoCount; + ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ + + union + { +#ifdef MA_SUPPORT_WASAPI + struct + { + ma_thread commandThread; + ma_mutex commandLock; + ma_semaphore commandSem; + ma_uint32 commandIndex; + ma_uint32 commandCount; + ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + ma_proc AvSetMmThreadCharacteristicsA; + ma_proc AvRevertMmThreadcharacteristics; + ma_handle hMMDevapi; + ma_proc ActivateAudioInterfaceAsync; + } wasapi; +#endif +#ifdef MA_SUPPORT_DSOUND + struct + { + ma_handle hDSoundDLL; + ma_proc DirectSoundCreate; + ma_proc DirectSoundEnumerateA; + ma_proc DirectSoundCaptureCreate; + ma_proc DirectSoundCaptureEnumerateA; + } dsound; +#endif +#ifdef MA_SUPPORT_WINMM + struct + { + ma_handle hWinMM; + ma_proc waveOutGetNumDevs; + ma_proc waveOutGetDevCapsA; + ma_proc waveOutOpen; + ma_proc waveOutClose; + ma_proc waveOutPrepareHeader; + ma_proc waveOutUnprepareHeader; + ma_proc waveOutWrite; + ma_proc waveOutReset; + ma_proc waveInGetNumDevs; + ma_proc waveInGetDevCapsA; + ma_proc waveInOpen; + ma_proc waveInClose; + ma_proc waveInPrepareHeader; + ma_proc waveInUnprepareHeader; + ma_proc waveInAddBuffer; + ma_proc waveInStart; + ma_proc waveInReset; + } winmm; +#endif +#ifdef MA_SUPPORT_ALSA + struct + { + ma_handle asoundSO; + ma_proc snd_pcm_open; + ma_proc snd_pcm_close; + ma_proc snd_pcm_hw_params_sizeof; + ma_proc snd_pcm_hw_params_any; + ma_proc snd_pcm_hw_params_set_format; + ma_proc snd_pcm_hw_params_set_format_first; + ma_proc snd_pcm_hw_params_get_format_mask; + ma_proc snd_pcm_hw_params_set_channels; + ma_proc snd_pcm_hw_params_set_channels_near; + ma_proc snd_pcm_hw_params_set_channels_minmax; + ma_proc snd_pcm_hw_params_set_rate_resample; + ma_proc snd_pcm_hw_params_set_rate; + ma_proc snd_pcm_hw_params_set_rate_near; + ma_proc snd_pcm_hw_params_set_buffer_size_near; + ma_proc snd_pcm_hw_params_set_periods_near; + ma_proc snd_pcm_hw_params_set_access; + ma_proc snd_pcm_hw_params_get_format; + ma_proc snd_pcm_hw_params_get_channels; + ma_proc snd_pcm_hw_params_get_channels_min; + ma_proc snd_pcm_hw_params_get_channels_max; + ma_proc snd_pcm_hw_params_get_rate; + ma_proc snd_pcm_hw_params_get_rate_min; + ma_proc snd_pcm_hw_params_get_rate_max; + ma_proc snd_pcm_hw_params_get_buffer_size; + ma_proc snd_pcm_hw_params_get_periods; + ma_proc snd_pcm_hw_params_get_access; + ma_proc snd_pcm_hw_params_test_format; + ma_proc snd_pcm_hw_params_test_channels; + ma_proc snd_pcm_hw_params_test_rate; + ma_proc snd_pcm_hw_params; + ma_proc snd_pcm_sw_params_sizeof; + ma_proc snd_pcm_sw_params_current; + ma_proc snd_pcm_sw_params_get_boundary; + ma_proc snd_pcm_sw_params_set_avail_min; + ma_proc snd_pcm_sw_params_set_start_threshold; + ma_proc snd_pcm_sw_params_set_stop_threshold; + ma_proc snd_pcm_sw_params; + ma_proc snd_pcm_format_mask_sizeof; + ma_proc snd_pcm_format_mask_test; + ma_proc snd_pcm_get_chmap; + ma_proc snd_pcm_state; + ma_proc snd_pcm_prepare; + ma_proc snd_pcm_start; + ma_proc snd_pcm_drop; + ma_proc snd_pcm_drain; + ma_proc snd_pcm_reset; + ma_proc snd_device_name_hint; + ma_proc snd_device_name_get_hint; + ma_proc snd_card_get_index; + ma_proc snd_device_name_free_hint; + ma_proc snd_pcm_mmap_begin; + ma_proc snd_pcm_mmap_commit; + ma_proc snd_pcm_recover; + ma_proc snd_pcm_readi; + ma_proc snd_pcm_writei; + ma_proc snd_pcm_avail; + ma_proc snd_pcm_avail_update; + ma_proc snd_pcm_wait; + ma_proc snd_pcm_nonblock; + ma_proc snd_pcm_info; + ma_proc snd_pcm_info_sizeof; + ma_proc snd_pcm_info_get_name; + ma_proc snd_pcm_poll_descriptors; + ma_proc snd_pcm_poll_descriptors_count; + ma_proc snd_pcm_poll_descriptors_revents; + ma_proc snd_config_update_free_global; + + ma_mutex internalDeviceEnumLock; + ma_bool32 useVerboseDeviceEnumeration; + } alsa; +#endif +#ifdef MA_SUPPORT_PULSEAUDIO + struct + { + ma_handle pulseSO; + ma_proc pa_mainloop_new; + ma_proc pa_mainloop_free; + ma_proc pa_mainloop_quit; + ma_proc pa_mainloop_get_api; + ma_proc pa_mainloop_iterate; + ma_proc pa_mainloop_wakeup; + ma_proc pa_threaded_mainloop_new; + ma_proc pa_threaded_mainloop_free; + ma_proc pa_threaded_mainloop_start; + ma_proc pa_threaded_mainloop_stop; + ma_proc pa_threaded_mainloop_lock; + ma_proc pa_threaded_mainloop_unlock; + ma_proc pa_threaded_mainloop_wait; + ma_proc pa_threaded_mainloop_signal; + ma_proc pa_threaded_mainloop_accept; + ma_proc pa_threaded_mainloop_get_retval; + ma_proc pa_threaded_mainloop_get_api; + ma_proc pa_threaded_mainloop_in_thread; + ma_proc pa_threaded_mainloop_set_name; + ma_proc pa_context_new; + ma_proc pa_context_unref; + ma_proc pa_context_connect; + ma_proc pa_context_disconnect; + ma_proc pa_context_set_state_callback; + ma_proc pa_context_get_state; + ma_proc pa_context_get_sink_info_list; + ma_proc pa_context_get_source_info_list; + ma_proc pa_context_get_sink_info_by_name; + ma_proc pa_context_get_source_info_by_name; + ma_proc pa_operation_unref; + ma_proc pa_operation_get_state; + ma_proc pa_channel_map_init_extend; + ma_proc pa_channel_map_valid; + ma_proc pa_channel_map_compatible; + ma_proc pa_stream_new; + ma_proc pa_stream_unref; + ma_proc pa_stream_connect_playback; + ma_proc pa_stream_connect_record; + ma_proc pa_stream_disconnect; + ma_proc pa_stream_get_state; + ma_proc pa_stream_get_sample_spec; + ma_proc pa_stream_get_channel_map; + ma_proc pa_stream_get_buffer_attr; + ma_proc pa_stream_set_buffer_attr; + ma_proc pa_stream_get_device_name; + ma_proc pa_stream_set_write_callback; + ma_proc pa_stream_set_read_callback; + ma_proc pa_stream_set_suspended_callback; + ma_proc pa_stream_set_moved_callback; + ma_proc pa_stream_is_suspended; + ma_proc pa_stream_flush; + ma_proc pa_stream_drain; + ma_proc pa_stream_is_corked; + ma_proc pa_stream_cork; + ma_proc pa_stream_trigger; + ma_proc pa_stream_begin_write; + ma_proc pa_stream_write; + ma_proc pa_stream_peek; + ma_proc pa_stream_drop; + ma_proc pa_stream_writable_size; + ma_proc pa_stream_readable_size; + + /*pa_mainloop**/ ma_ptr pMainLoop; + /*pa_context**/ ma_ptr pPulseContext; + char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + } pulse; +#endif +#ifdef MA_SUPPORT_JACK + struct + { + ma_handle jackSO; + ma_proc jack_client_open; + ma_proc jack_client_close; + ma_proc jack_client_name_size; + ma_proc jack_set_process_callback; + ma_proc jack_set_buffer_size_callback; + ma_proc jack_on_shutdown; + ma_proc jack_get_sample_rate; + ma_proc jack_get_buffer_size; + ma_proc jack_get_ports; + ma_proc jack_activate; + ma_proc jack_deactivate; + ma_proc jack_connect; + ma_proc jack_port_register; + ma_proc jack_port_name; + ma_proc jack_port_get_buffer; + ma_proc jack_free; + + char* pClientName; + ma_bool32 tryStartServer; + } jack; +#endif +#ifdef MA_SUPPORT_COREAUDIO + struct + { + ma_handle hCoreFoundation; + ma_proc CFStringGetCString; + ma_proc CFRelease; + + ma_handle hCoreAudio; + ma_proc AudioObjectGetPropertyData; + ma_proc AudioObjectGetPropertyDataSize; + ma_proc AudioObjectSetPropertyData; + ma_proc AudioObjectAddPropertyListener; + ma_proc AudioObjectRemovePropertyListener; + + ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ + ma_proc AudioComponentFindNext; + ma_proc AudioComponentInstanceDispose; + ma_proc AudioComponentInstanceNew; + ma_proc AudioOutputUnitStart; + ma_proc AudioOutputUnitStop; + ma_proc AudioUnitAddPropertyListener; + ma_proc AudioUnitGetPropertyInfo; + ma_proc AudioUnitGetProperty; + ma_proc AudioUnitSetProperty; + ma_proc AudioUnitInitialize; + ma_proc AudioUnitRender; + + /*AudioComponent*/ ma_ptr component; + ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ + } coreaudio; +#endif +#ifdef MA_SUPPORT_SNDIO + struct + { + ma_handle sndioSO; + ma_proc sio_open; + ma_proc sio_close; + ma_proc sio_setpar; + ma_proc sio_getpar; + ma_proc sio_getcap; + ma_proc sio_start; + ma_proc sio_stop; + ma_proc sio_read; + ma_proc sio_write; + ma_proc sio_onmove; + ma_proc sio_nfds; + ma_proc sio_pollfd; + ma_proc sio_revents; + ma_proc sio_eof; + ma_proc sio_setvol; + ma_proc sio_onvol; + ma_proc sio_initpar; + } sndio; +#endif +#ifdef MA_SUPPORT_AUDIO4 + struct + { + int _unused; + } audio4; +#endif +#ifdef MA_SUPPORT_OSS + struct + { + int versionMajor; + int versionMinor; + } oss; +#endif +#ifdef MA_SUPPORT_AAUDIO + struct + { + ma_handle hAAudio; /* libaaudio.so */ + ma_proc AAudio_createStreamBuilder; + ma_proc AAudioStreamBuilder_delete; + ma_proc AAudioStreamBuilder_setDeviceId; + ma_proc AAudioStreamBuilder_setDirection; + ma_proc AAudioStreamBuilder_setSharingMode; + ma_proc AAudioStreamBuilder_setFormat; + ma_proc AAudioStreamBuilder_setChannelCount; + ma_proc AAudioStreamBuilder_setSampleRate; + ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; + ma_proc AAudioStreamBuilder_setFramesPerDataCallback; + ma_proc AAudioStreamBuilder_setDataCallback; + ma_proc AAudioStreamBuilder_setErrorCallback; + ma_proc AAudioStreamBuilder_setPerformanceMode; + ma_proc AAudioStreamBuilder_setUsage; + ma_proc AAudioStreamBuilder_setContentType; + ma_proc AAudioStreamBuilder_setInputPreset; + ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; + ma_proc AAudioStreamBuilder_openStream; + ma_proc AAudioStream_close; + ma_proc AAudioStream_getState; + ma_proc AAudioStream_waitForStateChange; + ma_proc AAudioStream_getFormat; + ma_proc AAudioStream_getChannelCount; + ma_proc AAudioStream_getSampleRate; + ma_proc AAudioStream_getBufferCapacityInFrames; + ma_proc AAudioStream_getFramesPerDataCallback; + ma_proc AAudioStream_getFramesPerBurst; + ma_proc AAudioStream_requestStart; + ma_proc AAudioStream_requestStop; + ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ + } aaudio; +#endif +#ifdef MA_SUPPORT_OPENSL + struct + { + ma_handle libOpenSLES; + ma_handle SL_IID_ENGINE; + ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; + ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + ma_handle SL_IID_RECORD; + ma_handle SL_IID_PLAY; + ma_handle SL_IID_OUTPUTMIX; + ma_handle SL_IID_ANDROIDCONFIGURATION; + ma_proc slCreateEngine; + } opensl; +#endif +#ifdef MA_SUPPORT_WEBAUDIO + struct + { + int _unused; + } webaudio; +#endif +#ifdef MA_SUPPORT_NULL + struct + { + int _unused; + } null_backend; +#endif + }; + + union + { +#if defined(MA_WIN32) + struct + { + /*HMODULE*/ ma_handle hOle32DLL; + ma_proc CoInitialize; + ma_proc CoInitializeEx; + ma_proc CoUninitialize; + ma_proc CoCreateInstance; + ma_proc CoTaskMemFree; + ma_proc PropVariantClear; + ma_proc StringFromGUID2; + + /*HMODULE*/ ma_handle hUser32DLL; + ma_proc GetForegroundWindow; + ma_proc GetDesktopWindow; + + /*HMODULE*/ ma_handle hAdvapi32DLL; + ma_proc RegOpenKeyExA; + ma_proc RegCloseKey; + ma_proc RegQueryValueExA; + + /*HRESULT*/ long CoInitializeResult; + } win32; +#endif +#ifdef MA_POSIX + struct + { + int _unused; + } posix; +#endif + int _unused; + }; +}; + +struct ma_device +{ + ma_context* pContext; + ma_device_type type; + ma_uint32 sampleRate; + ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ + ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ + ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ + ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ + void* pUserData; /* Application defined data. */ + ma_mutex startStopLock; + ma_event wakeupEvent; + ma_event startEvent; + ma_event stopEvent; + ma_thread thread; + ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ + ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ + ma_bool8 noPreSilencedOutputBuffer; + ma_bool8 noClip; + ma_bool8 noDisableDenormals; + ma_bool8 noFixedSizedCallback; + ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ + ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ + struct + { + ma_resample_algorithm algorithm; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + struct + { + ma_uint32 lpfOrder; + } linear; + } resampling; + struct + { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ + ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ + ma_format format; + ma_uint32 channels; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; + ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ + void* pInputCache; /* In external format. Can be null. */ + ma_uint64 inputCacheCap; + ma_uint64 inputCacheConsumed; + ma_uint64 inputCacheRemaining; + } playback; + struct + { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ + ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ + ma_format format; + ma_uint32 channels; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; + ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ + } capture; + + union + { +#ifdef MA_SUPPORT_WASAPI + struct + { + /*IAudioClient**/ ma_ptr pAudioClientPlayback; + /*IAudioClient**/ ma_ptr pAudioClientCapture; + /*IAudioRenderClient**/ ma_ptr pRenderClient; + /*IAudioCaptureClient**/ ma_ptr pCaptureClient; + /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + ma_IMMNotificationClient notificationClient; + /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ + /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ + ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + ma_uint32 actualBufferSizeInFramesCapture; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_uint32 periodSizeInFramesPlayback; + ma_uint32 periodSizeInFramesCapture; + void* pMappedBufferCapture; + ma_uint32 mappedBufferCaptureCap; + ma_uint32 mappedBufferCaptureLen; + void* pMappedBufferPlayback; + ma_uint32 mappedBufferPlaybackCap; + ma_uint32 mappedBufferPlaybackLen; + ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_uint32 loopbackProcessID; + ma_bool8 loopbackProcessExclude; + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noHardwareOffloading; + ma_bool8 allowCaptureAutoStreamRouting; + ma_bool8 allowPlaybackAutoStreamRouting; + ma_bool8 isDetachedPlayback; + ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void* hAvrtHandle; + ma_mutex rerouteLock; + } wasapi; +#endif +#ifdef MA_SUPPORT_DSOUND + struct + { + /*LPDIRECTSOUND*/ ma_ptr pPlayback; + /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; + /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; + /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; + /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; + } dsound; +#endif +#ifdef MA_SUPPORT_WINMM + struct + { + /*HWAVEOUT*/ ma_handle hDevicePlayback; + /*HWAVEIN*/ ma_handle hDeviceCapture; + /*HANDLE*/ ma_handle hEventPlayback; + /*HANDLE*/ ma_handle hEventCapture; + ma_uint32 fragmentSizeInFrames; + ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ + ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ + ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ + ma_uint32 headerFramesConsumedCapture; /* ^^^ */ + /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ + /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ + ma_uint8* pIntermediaryBufferPlayback; + ma_uint8* pIntermediaryBufferCapture; + ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ + } winmm; +#endif +#ifdef MA_SUPPORT_ALSA + struct + { + /*snd_pcm_t**/ ma_ptr pPCMPlayback; + /*snd_pcm_t**/ ma_ptr pPCMCapture; + /*struct pollfd**/ void* pPollDescriptorsPlayback; + /*struct pollfd**/ void* pPollDescriptorsCapture; + int pollDescriptorCountPlayback; + int pollDescriptorCountCapture; + int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ + int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ + ma_bool8 isUsingMMapPlayback; + ma_bool8 isUsingMMapCapture; + } alsa; +#endif +#ifdef MA_SUPPORT_PULSEAUDIO + struct + { + /*pa_mainloop**/ ma_ptr pMainLoop; + /*pa_context**/ ma_ptr pPulseContext; + /*pa_stream**/ ma_ptr pStreamPlayback; + /*pa_stream**/ ma_ptr pStreamCapture; + } pulse; +#endif +#ifdef MA_SUPPORT_JACK + struct + { + /*jack_client_t**/ ma_ptr pClient; + /*jack_port_t**/ ma_ptr* ppPortsPlayback; + /*jack_port_t**/ ma_ptr* ppPortsCapture; + float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ + float* pIntermediaryBufferCapture; + } jack; +#endif +#ifdef MA_SUPPORT_COREAUDIO + struct + { + ma_uint32 deviceObjectIDPlayback; + ma_uint32 deviceObjectIDCapture; + /*AudioUnit*/ ma_ptr audioUnitPlayback; + /*AudioUnit*/ ma_ptr audioUnitCapture; + /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ + ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ + ma_event stopEvent; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_bool32 isDefaultPlaybackDevice; + ma_bool32 isDefaultCaptureDevice; + ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ + } coreaudio; +#endif +#ifdef MA_SUPPORT_SNDIO + struct + { + ma_ptr handlePlayback; + ma_ptr handleCapture; + ma_bool32 isStartedPlayback; + ma_bool32 isStartedCapture; + } sndio; +#endif +#ifdef MA_SUPPORT_AUDIO4 + struct + { + int fdPlayback; + int fdCapture; + } audio4; +#endif +#ifdef MA_SUPPORT_OSS + struct + { + int fdPlayback; + int fdCapture; + } oss; +#endif +#ifdef MA_SUPPORT_AAUDIO + struct + { + /*AAudioStream**/ ma_ptr pStreamPlayback; + /*AAudioStream**/ ma_ptr pStreamCapture; + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_bool32 noAutoStartAfterReroute; + } aaudio; +#endif +#ifdef MA_SUPPORT_OPENSL + struct + { + /*SLObjectItf*/ ma_ptr pOutputMixObj; + /*SLOutputMixItf*/ ma_ptr pOutputMix; + /*SLObjectItf*/ ma_ptr pAudioPlayerObj; + /*SLPlayItf*/ ma_ptr pAudioPlayer; + /*SLObjectItf*/ ma_ptr pAudioRecorderObj; + /*SLRecordItf*/ ma_ptr pAudioRecorder; + /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; + /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; + ma_bool32 isDrainingCapture; + ma_bool32 isDrainingPlayback; + ma_uint32 currentBufferIndexPlayback; + ma_uint32 currentBufferIndexCapture; + ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ + ma_uint8* pBufferCapture; + } opensl; +#endif +#ifdef MA_SUPPORT_WEBAUDIO + struct + { + /* AudioWorklets path. */ + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; + float* pIntermediaryBuffer; + void* pStackBuffer; + ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ + int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ + } webaudio; +#endif +#ifdef MA_SUPPORT_NULL + struct + { + ma_thread deviceThread; + ma_event operationEvent; + ma_event operationCompletionEvent; + ma_semaphore operationSemaphore; + ma_uint32 operation; + ma_result operationResult; + ma_timer timer; + double priorRunTime; + ma_uint32 currentPeriodFramesRemainingPlayback; + ma_uint32 currentPeriodFramesRemainingCapture; + ma_uint64 lastProcessedFramePlayback; + ma_uint64 lastProcessedFrameCapture; + ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ + } null_device; +#endif + }; +}; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ +#endif + +/* +Initializes a `ma_context_config` object. + + +Return Value +------------ +A `ma_context_config` initialized to defaults. + + +Remarks +------- +You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio +is updated and new members are added to `ma_context_config`. It also sets logical defaults. + +You can override members of the returned object by changing it's members directly. + + +See Also +-------- +ma_context_init() +*/ +MA_API ma_context_config ma_context_config_init(void); + +/* +Initializes a context. + +The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual +device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. + + +Parameters +---------- +backends (in, optional) + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + +backendCount (in, optional) + The number of items in `backend`. Ignored if `backend` is NULL. + +pConfig (in, optional) + The context configuration. + +pContext (in) + A pointer to the context object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Do not call this function across multiple threads as some backends read and write to global state. + + +Remarks +------- +When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: + + |-------------|-----------------------|--------------------------------------------------------| + | Name | Enum Name | Supported Operating Systems | + |-------------|-----------------------|--------------------------------------------------------| + | WASAPI | ma_backend_wasapi | Windows Vista+ | + | DirectSound | ma_backend_dsound | Windows XP+ | + | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | + | Core Audio | ma_backend_coreaudio | macOS, iOS | + | ALSA | ma_backend_alsa | Linux | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | + | sndio | ma_backend_sndio | OpenBSD | + | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | + | OSS | ma_backend_oss | FreeBSD | + | AAudio | ma_backend_aaudio | Android 8+ | + | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | + | Web Audio | ma_backend_webaudio | Web (via Emscripten) | + | Null | ma_backend_null | Cross Platform (not used on Web) | + |-------------|-----------------------|--------------------------------------------------------| + +The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings +can then be set directly on the structure. Below are the members of the `ma_context_config` object. + + pLog + A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not + require logging. See the `ma_log` API for details on how to use the logging system. + + threadPriority + The desired priority to use for the audio thread. Allowable values include the following: + + |--------------------------------------| + | Thread Priority | + |--------------------------------------| + | ma_thread_priority_idle | + | ma_thread_priority_lowest | + | ma_thread_priority_low | + | ma_thread_priority_normal | + | ma_thread_priority_high | + | ma_thread_priority_highest (default) | + | ma_thread_priority_realtime | + | ma_thread_priority_default | + |--------------------------------------| + + threadStackSize + The desired size of the stack for the audio thread. Defaults to the operating system's default. + + pUserData + A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. + + allocationCallbacks + Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation + callbacks will be used for anything tied to the context, including devices. + + alsa.useVerboseDeviceEnumeration + ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique + card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes + it so the ALSA backend includes all devices. Defaults to false. + + pulse.pApplicationName + PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. + + pulse.pServerName + PulseAudio only. The name of the server to connect to with `pa_context_connect()`. + + pulse.tryAutoSpawn + PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that + miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be + intrusive for the end user. + + coreaudio.sessionCategory + iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + + |-----------------------------------------|-------------------------------------| + | miniaudio Token | Core Audio Token | + |-----------------------------------------|-------------------------------------| + | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | + | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | + | ma_ios_session_category_record | AVAudioSessionCategoryRecord | + | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | + | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | + | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | + |-----------------------------------------|-------------------------------------| + + coreaudio.sessionCategoryOptions + iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | miniaudio Token | Core Audio Token | + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | + | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | + | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | + | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | + | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | + | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | + | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | + |---------------------------------------------------------------------------|------------------------------------------------------------------| + + coreaudio.noAudioSessionActivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. + + coreaudio.noAudioSessionDeactivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. + + jack.pClientName + The name of the client to pass to `jack_client_open()`. + + jack.tryStartServer + Whether or not to try auto-starting the JACK server. Defaults to false. + + +It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the +relevant backends every time it's initialized. + +The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The +reason for this is that a pointer to the context is stored in the `ma_device` structure. + + +Example 1 - Default Initialization +---------------------------------- +The example below shows how to initialize the context using the default configuration. + +```c +ma_context context; +ma_result result = ma_context_init(NULL, 0, NULL, &context); +if (result != MA_SUCCESS) { + // Error. +} +``` + + +Example 2 - Custom Configuration +-------------------------------- +The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program +wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also +want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. + +For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. + +```c +ma_backend backends[] = { + ma_backend_alsa, + ma_backend_pulseaudio, + ma_backend_wasapi, + ma_backend_dsound +}; + +ma_log log; +ma_log_init(&log); +ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); + +ma_context_config config = ma_context_config_init(); +config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. + +ma_context context; +ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); +if (result != MA_SUCCESS) { + // Error. + if (result == MA_NO_BACKEND) { + // Couldn't find an appropriate backend. + } +} + +// You could also attach a log callback post-initialization: +ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); +``` + + +See Also +-------- +ma_context_config_init() +ma_context_uninit() +*/ +MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); + +/* +Uninitializes a context. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Do not call this function across multiple threads as some backends read and write to global state. + + +Remarks +------- +Results are undefined if you call this while any device created by this context is still active. + + +See Also +-------- +ma_context_init() +*/ +MA_API ma_result ma_context_uninit(ma_context* pContext); + +/* +Retrieves the size of the ma_context object. + +This is mainly for the purpose of bindings to know how much memory to allocate. +*/ +MA_API size_t ma_context_sizeof(void); + +/* +Retrieves a pointer to the log object associated with this context. + + +Remarks +------- +Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log +message. + +You can attach your own logging callback to the log with `ma_log_register_callback()` + + +Return Value +------------ +A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, +NULL will be returned. +*/ +MA_API ma_log* ma_context_get_log(ma_context* pContext); + +/* +Enumerates over every device (both playback and capture). + +This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur +an internal heap allocation, or it simply suits your code better. + +Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require +opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, +but don't call it from within the enumeration callback. + +Returning false from the callback will stop enumeration. Returning true will continue enumeration. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +callback (in) + The callback to fire for each enumerated device. + +pUserData (in) + A pointer to application-defined data passed to the callback. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. This is guarded using a simple mutex lock. + + +Remarks +------- +Do _not_ assume the first enumerated device of a given type is the default device. + +Some backends and platforms may only support default playback and capture devices. + +In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, +do not try to call `ma_context_get_device_info()` from within the callback. + +Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. + + +Example 1 - Simple Enumeration +------------------------------ +ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) +{ + printf("Device Name: %s\n", pInfo->name); + return MA_TRUE; +} + +ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); +if (result != MA_SUCCESS) { + // Error. +} + + +See Also +-------- +ma_context_get_devices() +*/ +MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); + +/* +Retrieves basic information about every active playback and/or capture device. + +This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` +parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +ppPlaybackDeviceInfos (out) + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. + +pPlaybackDeviceCount (out) + A pointer to an unsigned integer that will receive the number of playback devices. + +ppCaptureDeviceInfos (out) + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. + +pCaptureDeviceCount (out) + A pointer to an unsigned integer that will receive the number of capture devices. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple +threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. + + +Remarks +------- +It is _not_ safe to assume the first device in the list is the default device. + +You can pass in NULL for the playback or capture lists in which case they'll be ignored. + +The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. + + +See Also +-------- +ma_context_get_devices() +*/ +MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); + +/* +Retrieves information about a device of the given type, with the specified ID and share mode. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the query. + +deviceType (in) + The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. + +pDeviceID (in) + The ID of the device being queried. + +pDeviceInfo (out) + A pointer to the `ma_device_info` structure that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. This is guarded using a simple mutex lock. + + +Remarks +------- +Do _not_ call this from within the `ma_context_enumerate_devices()` callback. + +It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in +shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify +which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if +the requested share mode is unsupported. + +This leaves pDeviceInfo unmodified in the result of an error. +*/ +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); + +/* +Determines if the given context supports loopback mode. + + +Parameters +---------- +pContext (in) + A pointer to the context getting queried. + + +Return Value +------------ +MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. +*/ +MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); + + + +/* +Initializes a device config with default settings. + + +Parameters +---------- +deviceType (in) + The type of the device this config is being initialized for. This must set to one of the following: + + |-------------------------| + | Device Type | + |-------------------------| + | ma_device_type_playback | + | ma_device_type_capture | + | ma_device_type_duplex | + | ma_device_type_loopback | + |-------------------------| + + +Return Value +------------ +A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. + + +Thread Safety +------------- +Safe. + + +Callback Safety +--------------- +Safe, but don't try initializing a device in a callback. + + +Remarks +------- +The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a +typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change +before initializing the device. + +See `ma_device_init()` for details on specific configuration options. + + +Example 1 - Simple Configuration +-------------------------------- +The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and +then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added +to the `ma_device_config` structure. + +```c +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pUserData = pMyUserData; +``` + + +See Also +-------- +ma_device_init() +ma_device_init_ex() +*/ +MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); + + +/* +Initializes a device. + +A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it +from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be +playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the +device is done via a callback which is fired by miniaudio at periodic time intervals. + +The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames +or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and +increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but +miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple +media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the +backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. + +When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the +format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you +can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. + + +Parameters +---------- +pContext (in, optional) + A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. + +pConfig (in) + A pointer to the device configuration. Cannot be null. See remarks for details. + +pDevice (out) + A pointer to the device object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to +calling this at the same time as `ma_device_uninit()`. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: + + ```c + ma_context_init(NULL, 0, NULL, &context); + ``` + +Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use +device.pContext for the initialization of other devices. + +The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can +then be set directly on the structure. Below are the members of the `ma_device_config` object. + + deviceType + Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. + + sampleRate + The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. + + periodSizeInFrames + The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will + be used depending on the selected performance profile. This value affects latency. See below for details. + + periodSizeInMilliseconds + The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be + used depending on the selected performance profile. The value affects latency. See below for details. + + periods + The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by + this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. + + performanceProfile + A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or + `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. + + noPreSilencedOutputBuffer + When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of + the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data + callback will write to every sample in the output buffer, or if you are doing your own clearing. + + noClip + When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or + not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only + applies when the playback sample format is f32. + + noDisableDenormals + By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. + + noFixedSizedCallback + Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a + consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with + whatever the backend requests, which could be anything. + + dataCallback + The callback to fire whenever data is ready to be delivered to or from the device. + + notificationCallback + The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. + + pUserData + The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. + + resampling.algorithm + The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The + default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. + + resampling.pBackendVTable + A pointer to an optional vtable that can be used for plugging in a custom resampler. + + resampling.pBackendUserData + A pointer that will passed to callbacks in pBackendVTable. + + resampling.linear.lpfOrder + The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher + the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is + `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. + + playback.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's + default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + + playback.format + The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.playback.format`. + + playback.channels + The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.playback.channels`. + + playback.pChannelMap + The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. + + playback.shareMode + The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. + + capture.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's + default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + + capture.format + The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.capture.format`. + + capture.channels + The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.capture.channels`. + + capture.pChannelMap + The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. + + capture.shareMode + The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. + + wasapi.noAutoConvertSRC + WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. + + wasapi.noDefaultQualitySRC + WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. + You should usually leave this set to false, which is the default. + + wasapi.noAutoStreamRouting + WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. + + wasapi.noHardwareOffloading + WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. + + alsa.noMMap + ALSA only. When set to true, disables MMap mode. Defaults to false. + + alsa.noAutoFormat + ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. + + alsa.noAutoChannels + ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. + + alsa.noAutoResample + ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. + + pulse.pStreamNamePlayback + PulseAudio only. Sets the stream name for playback. + + pulse.pStreamNameCapture + PulseAudio only. Sets the stream name for capture. + + coreaudio.allowNominalSampleRateChange + Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This + is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate + that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will + find the closest match between the sample rate requested in the device config and the sample rates natively supported by the + hardware. When set to false, the sample rate currently set by the operating system will always be used. + + opensl.streamType + OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the + stream type will be left unset. Think of this as the type of audio you're playing. + + opensl.recordingPreset + OpenSL only. Explicitly sets the type of recording your program will be doing. When left + unset, the recording preset will be left unchanged. + + aaudio.usage + AAudio only. Explicitly sets the nature of the audio the program will be consuming. When + left unset, the usage will be left unchanged. + + aaudio.contentType + AAudio only. Sets the content type. When left unset, the content type will be left unchanged. + + aaudio.inputPreset + AAudio only. Explicitly sets the type of recording your program will be doing. When left + unset, the input preset will be left unchanged. + + aaudio.noAutoStartAfterReroute + AAudio only. Controls whether or not the device should be automatically restarted after a + stream reroute. When set to false (default) the device will be restarted automatically; + otherwise the device will be stopped. + + +Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. + +After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. + +If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or +`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or +`ma_performance_profile_conservative`. + +If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device +in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the +config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, +for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. +Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. + +When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config +and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run +on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, +`playback/capture.channels` and `sampleRate` members of the device object. + +When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message +asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. + +ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. +If these fail it will try falling back to the "hw" device. + + +Example 1 - Simple Initialization +--------------------------------- +This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default +playback device this is usually all you need. + +```c +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pMyUserData = pMyUserData; + +ma_device device; +ma_result result = ma_device_init(NULL, &config, &device); +if (result != MA_SUCCESS) { + // Error +} +``` + + +Example 2 - Advanced Initialization +----------------------------------- +This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size +and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device +enumeration. + +```c +ma_context context; +ma_result result = ma_context_init(NULL, 0, NULL, &context); +if (result != MA_SUCCESS) { + // Error +} + +ma_device_info* pPlaybackDeviceInfos; +ma_uint32 playbackDeviceCount; +result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); +if (result != MA_SUCCESS) { + // Error +} + +// ... choose a device from pPlaybackDeviceInfos ... + +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pUserData = pMyUserData; +config.periodSizeInMilliseconds = 10; +config.periods = 3; + +ma_device device; +result = ma_device_init(&context, &config, &device); +if (result != MA_SUCCESS) { + // Error +} +``` + + +See Also +-------- +ma_device_config_init() +ma_device_uninit() +ma_device_start() +ma_context_init() +ma_context_get_devices() +ma_context_enumerate_devices() +*/ +MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); + +/* +Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. + +This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function +allows you to configure the internally created context. + + +Parameters +---------- +backends (in, optional) + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + +backendCount (in, optional) + The number of items in `backend`. Ignored if `backend` is NULL. + +pContextConfig (in, optional) + The context configuration. + +pConfig (in) + A pointer to the device configuration. Cannot be null. See remarks for details. + +pDevice (out) + A pointer to the device object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to +calling this at the same time as `ma_device_uninit()`. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage +your own context. + +See the documentation for `ma_context_init()` for information on the different context configuration options. + + +See Also +-------- +ma_device_init() +ma_device_uninit() +ma_device_config_init() +ma_context_init() +*/ +MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); + +/* +Uninitializes a device. + +This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. + + +Parameters +---------- +pDevice (in) + A pointer to the device to stop. + + +Return Value +------------ +Nothing + + +Thread Safety +------------- +Unsafe. As soon as this API is called the device should be considered undefined. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. + + +See Also +-------- +ma_device_init() +ma_device_stop() +*/ +MA_API void ma_device_uninit(ma_device* pDevice); + + +/* +Retrieves a pointer to the context that owns the given device. +*/ +MA_API ma_context* ma_device_get_context(ma_device* pDevice); + +/* +Helper function for retrieving the log object associated with the context that owns this device. +*/ +MA_API ma_log* ma_device_get_log(ma_device* pDevice); + + +/* +Retrieves information about the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pDeviceInfo (out) + A pointer to the `ma_device_info` that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. +*/ +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); + + +/* +Retrieves the name of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pName (out) + A pointer to the buffer that will receive the name. + +nameCap (in) + The capacity of the output buffer, including space for the null terminator. + +pLengthNotIncludingNullTerminator (out, optional) + A pointer to the variable that will receive the length of the name, not including the null + terminator. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. + + +Remarks +------- +If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to +`pName` if you want to first get the length of the name for the purpose of memory allocation of the +output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for +most cases and will avoid the need for the inefficiency of calling this function twice. + +This is implemented in terms of `ma_device_get_info()`. +*/ +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); + + +/* +Starts the device. For playback devices this begins playback. For capture devices it begins recording. + +Use `ma_device_stop()` to stop the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device to start. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. It's safe to call this from any thread with the exception of the callback thread. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid +audio data in the buffer, which needs to be done before the device begins playback. + +This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. + +Do not call this in any callback. + + +See Also +-------- +ma_device_stop() +*/ +MA_API ma_result ma_device_start(ma_device* pDevice); + +/* +Stops the device. For playback devices this stops playback. For capture devices it stops recording. + +Use `ma_device_start()` to start the device again. + + +Parameters +---------- +pDevice (in) + A pointer to the device to stop. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. It's safe to call this from any thread with the exception of the callback thread. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. + + +Remarks +------- +This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some +backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size +that was specified at initialization time). + +Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and +the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the +speakers or received from the microphone which can in turn result in de-syncs. + +Do not call this in any callback. + + +See Also +-------- +ma_device_start() +*/ +MA_API ma_result ma_device_stop(ma_device* pDevice); + +/* +Determines whether or not the device is started. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose start state is being retrieved. + + +Return Value +------------ +True if the device is started, false otherwise. + + +Thread Safety +------------- +Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return +value will be out of sync. + + +Callback Safety +--------------- +Safe. This is implemented as a simple accessor. + + +See Also +-------- +ma_device_start() +ma_device_stop() +*/ +MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); + + +/* +Retrieves the state of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose state is being retrieved. + + +Return Value +------------ +The current state of the device. The return value will be one of the following: + + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_started | The device started and requesting and/or delivering audio data. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_starting | The device is in the process of starting. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopping | The device is in the process of stopping. | + +-------------------------------+------------------------------------------------------------------------------+ + + +Thread Safety +------------- +Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, +there's a possibility the return value could be out of sync. See remarks. + + +Callback Safety +--------------- +Safe. This is implemented as a simple accessor. + + +Remarks +------- +The general flow of a devices state goes like this: + + ``` + ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped + ma_device_start() -> ma_device_state_starting -> ma_device_state_started + ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped + ``` + +When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the +value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own +synchronization. +*/ +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); + + +/* +Performs post backend initialization routines for setting up internal data conversion. + +This should be called whenever the backend is initialized. The only time this should be called from +outside of miniaudio is if you're implementing a custom backend, and you would only do it if you +are reinitializing the backend due to rerouting or reinitializing for some reason. + + +Parameters +---------- +pDevice [in] + A pointer to the device. + +deviceType [in] + The type of the device that was just reinitialized. + +pPlaybackDescriptor [in] + The descriptor of the playback device containing the internal data format and buffer sizes. + +pPlaybackDescriptor [in] + The descriptor of the capture device containing the internal data format and buffer sizes. + + +Return Value +------------ +MA_SUCCESS if successful; any other error otherwise. + + +Thread Safety +------------- +Unsafe. This will be reinitializing internal data converters which may be in use by another thread. + + +Callback Safety +--------------- +Unsafe. This will be reinitializing internal data converters which may be in use by the callback. + + +Remarks +------- +For a duplex device, you can call this for only one side of the system. This is why the deviceType +is specified as a parameter rather than deriving it from the device. + +You do not need to call this manually unless you are doing a custom backend, in which case you need +only do it if you're manually performing rerouting or reinitialization. +*/ +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); + + +/* +Sets the master volume factor for the device. + +The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and +values less than 0 decreases the volume. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose volume is being set. + +volume (in) + The new volume factor. Must be >= 0. + + +Return Value +------------ +MA_SUCCESS if the volume was set successfully. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if volume is negative. + + +Thread Safety +------------- +Safe. This just sets a local member of the device object. + + +Callback Safety +--------------- +Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. + + +Remarks +------- +This applies the volume factor across all channels. + +This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. + + +See Also +-------- +ma_device_get_master_volume() +ma_device_set_master_volume_db() +ma_device_get_master_volume_db() +*/ +MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); + +/* +Retrieves the master volume factor for the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose volume factor is being retrieved. + +pVolume (in) + A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if pVolume is NULL. + + +Thread Safety +------------- +Safe. This just a simple member retrieval. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If an error occurs, `*pVolume` will be set to 0. + + +See Also +-------- +ma_device_set_master_volume() +ma_device_set_master_volume_gain_db() +ma_device_get_master_volume_gain_db() +*/ +MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); + +/* +Sets the master volume for the device as gain in decibels. + +A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose gain is being set. + +gainDB (in) + The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. + + +Return Value +------------ +MA_SUCCESS if the volume was set successfully. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if the gain is > 0. + + +Thread Safety +------------- +Safe. This just sets a local member of the device object. + + +Callback Safety +--------------- +Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. + + +Remarks +------- +This applies the gain across all channels. + +This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. + + +See Also +-------- +ma_device_get_master_volume_gain_db() +ma_device_set_master_volume() +ma_device_get_master_volume() +*/ +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); + +/* +Retrieves the master gain in decibels. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose gain is being retrieved. + +pGainDB (in) + A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if pGainDB is NULL. + + +Thread Safety +------------- +Safe. This just a simple member retrieval. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If an error occurs, `*pGainDB` will be set to 0. + + +See Also +-------- +ma_device_set_master_volume_db() +ma_device_set_master_volume() +ma_device_get_master_volume() +*/ +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); + + +/* +Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. + + +Parameters +---------- +pDevice (in) + A pointer to device whose processing the data callback. + +pOutput (out) + A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device + this can be NULL, in which case pInput must not be NULL. + +pInput (in) + A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be + NULL, in which case `pOutput` must not be NULL. + +frameCount (in) + The number of frames being processed. + + +Return Value +------------ +MA_SUCCESS if successful; any other result code otherwise. + + +Thread Safety +------------- +This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a +playback and capture device in duplex setups. + + +Callback Safety +--------------- +Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. + + +Remarks +------- +If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in +which case `pInput` will be processed first, followed by `pOutput`. + +If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that +callback. +*/ +MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + +/* +Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. + +This function is used by backends for helping determine an appropriately sized buffer to use with +the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the +`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a +best guess at the device's native sample rate is also required which is where `nativeSampleRate` +comes in. In addition, the performance profile is also needed for cases where both the period size +in frames and milliseconds are both zero. + + +Parameters +---------- +pDescriptor (in) + A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members + will be used for the calculation of the buffer size. + +nativeSampleRate (in) + The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of + `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which + case a sample rate is required to convert to a size in frames. + +performanceProfile (in) + When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are + zero, miniaudio will fall back to a buffer size based on the performance profile. The profile + to use for this calculation is determine by this parameter. + + +Return Value +------------ +The calculated buffer size in frames. + + +Thread Safety +------------- +This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function +should only ever be called from within the backend's device initialization routine and therefore +shouldn't have any multithreading concerns. + + +Callback Safety +--------------- +This is safe to call within the data callback, but there is no reason to ever do this. + + +Remarks +------- +If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that +is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); + + + +/* +Retrieves a friendly name for a backend. +*/ +MA_API const char* ma_get_backend_name(ma_backend backend); + +/* +Retrieves the backend enum from the given name. +*/ +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); + +/* +Determines whether or not the given backend is available by the compilation environment. +*/ +MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); + +/* +Retrieves compile-time enabled backends. + + +Parameters +---------- +pBackends (out, optional) + A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting + the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. + +backendCap (in) + The capacity of the `pBackends` buffer. + +pBackendCount (out) + A pointer to the variable that will receive the enabled backend count. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if `pBackendCount` is NULL. +MA_NO_SPACE if the capacity of `pBackends` is not large enough. + +If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. + + +Thread Safety +------------- +Safe. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call +this function with `pBackends` set to NULL. + +This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` +when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at +compile time with `MA_NO_NULL`. + +The returned backends are determined based on compile time settings, not the platform it's currently running on. For +example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have +PulseAudio installed. + + +Example 1 +--------- +The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is +given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. +Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. + +``` +ma_backend enabledBackends[MA_BACKEND_COUNT]; +size_t enabledBackendCount; + +result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); +if (result != MA_SUCCESS) { + // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. +} +``` + + +See Also +-------- +ma_is_backend_enabled() +*/ +MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); + +/* +Determines whether or not loopback mode is support by a backend. +*/ +MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); + +#endif /* MA_NO_DEVICE_IO */ + + + +/************************************************************************************************************************************************************ + +Utilities + +************************************************************************************************************************************************************/ + +/* +Calculates a buffer size in milliseconds from the specified number of frames and sample rate. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); + +/* +Calculates a buffer size in frames from the specified number of milliseconds and sample rate. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); + +/* +Copies PCM frames from one buffer to another. +*/ +MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + +/* +Copies silent frames into the given buffer. + +Remarks +------- +For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it +makes more sense for the purpose of mixing to initialize it to the center point. +*/ +MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + + +/* +Offsets a pointer by the specified number of PCM frames. +*/ +MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); +MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); +static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } +static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } + + +/* +Clips samples. +*/ +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + +/* +Helper for applying a volume factor to samples. + +Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. +*/ +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); + +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); + +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); + +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); + + +/* +Helper for converting a linear factor to gain in decibels. +*/ +MA_API float ma_volume_linear_to_db(float factor); + +/* +Helper for converting gain in decibels to a linear factor. +*/ +MA_API float ma_volume_db_to_linear(float gain); + + +/* +Mixes the specified number of frames in floating point format with a volume factor. + +This will run on an optimized path when the volume is equal to 1. +*/ +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); + + + + +/************************************************************************************************************************************************************ + +VFS +=== + +The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely +appropriate for a given situation. + +************************************************************************************************************************************************************/ +typedef void ma_vfs; +typedef ma_handle ma_vfs_file; + +typedef enum +{ + MA_OPEN_MODE_READ = 0x00000001, + MA_OPEN_MODE_WRITE = 0x00000002 +} ma_open_mode_flags; + +typedef enum +{ + ma_seek_origin_start, + ma_seek_origin_current, + ma_seek_origin_end /* Not used by decoders. */ +} ma_seek_origin; + +typedef struct +{ + ma_uint64 sizeInBytes; +} ma_file_info; + +typedef struct +{ + ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); + ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); + ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); + ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); + ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); + ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); + ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); + ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); +} ma_vfs_callbacks; + +MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); +MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); +MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); +MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); +MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); +MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); +MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); +MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_vfs_callbacks cb; + ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ +} ma_default_vfs; + +MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); + + + +typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); +typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); +typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); + + + +#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) +typedef enum +{ + ma_encoding_format_unknown = 0, + ma_encoding_format_wav, + ma_encoding_format_flac, + ma_encoding_format_mp3, + ma_encoding_format_vorbis +} ma_encoding_format; +#endif + +/************************************************************************************************************************************************************ + +Decoding +======== + +Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless +you do your own synchronization. + +************************************************************************************************************************************************************/ +#ifndef MA_NO_DECODING +typedef struct ma_decoder ma_decoder; + + +typedef struct +{ + ma_format preferredFormat; + ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ +} ma_decoding_backend_config; + +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); + + +typedef struct +{ + ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); + ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); +} ma_decoding_backend_vtable; + + +typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ +typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); +typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); + +typedef struct +{ + ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ + ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ + ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_dither_mode ditherMode; + ma_resampler_config resampling; + ma_allocation_callbacks allocationCallbacks; + ma_encoding_format encodingFormat; + ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ + ma_decoding_backend_vtable** ppCustomBackendVTables; + ma_uint32 customBackendCount; + void* pCustomBackendUserData; +} ma_decoder_config; + +struct ma_decoder +{ + ma_data_source_base ds; + ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ + const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ + void* pBackendUserData; + ma_decoder_read_proc onRead; + ma_decoder_seek_proc onSeek; + ma_decoder_tell_proc onTell; + void* pUserData; + ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ + ma_format outputFormat; + ma_uint32 outputChannels; + ma_uint32 outputSampleRate; + ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ + void* pInputCache; /* In input format. Can be null if it's not needed. */ + ma_uint64 inputCacheCap; /* The capacity of the input cache. */ + ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ + ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ + ma_allocation_callbacks allocationCallbacks; + union + { + struct + { + ma_vfs* pVFS; + ma_vfs_file file; + } vfs; + struct + { + const ma_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ + } data; +}; + +MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); +MA_API ma_decoder_config ma_decoder_config_init_default(void); + +MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); + +/* +Uninitializes a decoder. +*/ +MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); + +/* +Reads PCM frames from the given decoder. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + +/* +Seeks to a PCM frame based on it's absolute index. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); + +/* +Retrieves the decoder's output data format. +*/ +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + +/* +Retrieves the current position of the read cursor in PCM frames. +*/ +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); + +/* +Retrieves the length of the decoder in PCM frames. + +Do not call this on streams of an undefined length, such as internet radio. + +If the length is unknown or an error occurs, 0 will be returned. + +This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio +uses internally. + +For MP3's, this will decode the entire file. Do not call this in time critical scenarios. + +This function is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); + +/* +Retrieves the number of frames that can be read before reaching the end. + +This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in +particular ensuring you do not call it on streams of an undefined length, such as internet radio. + +If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be +returned. +*/ +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); + +/* +Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, +pConfig should be set to what you want. On output it will be set to what you got. +*/ +MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); +MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); +MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); + +#endif /* MA_NO_DECODING */ + + +/************************************************************************************************************************************************************ + +Encoding +======== + +Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. + +************************************************************************************************************************************************************/ +#ifndef MA_NO_ENCODING +typedef struct ma_encoder ma_encoder; + +typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); +typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); +typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); +typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); +typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); + +typedef struct +{ + ma_encoding_format encodingFormat; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_allocation_callbacks allocationCallbacks; +} ma_encoder_config; + +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); + +struct ma_encoder +{ + ma_encoder_config config; + ma_encoder_write_proc onWrite; + ma_encoder_seek_proc onSeek; + ma_encoder_init_proc onInit; + ma_encoder_uninit_proc onUninit; + ma_encoder_write_pcm_frames_proc onWritePCMFrames; + void* pUserData; + void* pInternalEncoder; + union + { + struct + { + ma_vfs* pVFS; + ma_vfs_file file; + } vfs; + } data; +}; + +MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API void ma_encoder_uninit(ma_encoder* pEncoder); +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); + +#endif /* MA_NO_ENCODING */ + + +/************************************************************************************************************************************************************ + +Generation + +************************************************************************************************************************************************************/ +#ifndef MA_NO_GENERATION +typedef enum +{ + ma_waveform_type_sine, + ma_waveform_type_square, + ma_waveform_type_triangle, + ma_waveform_type_sawtooth +} ma_waveform_type; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_waveform_type type; + double amplitude; + double frequency; +} ma_waveform_config; + +MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); + +typedef struct +{ + ma_data_source_base ds; + ma_waveform_config config; + double advance; + double time; +} ma_waveform; + +MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); +MA_API void ma_waveform_uninit(ma_waveform* pWaveform); +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); +MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); +MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double dutyCycle; + double amplitude; + double frequency; +} ma_pulsewave_config; + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); + +typedef struct +{ + ma_waveform waveform; + ma_pulsewave_config config; +} ma_pulsewave; + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); + +typedef enum +{ + ma_noise_type_white, + ma_noise_type_pink, + ma_noise_type_brownian +} ma_noise_type; + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_noise_type type; + ma_int32 seed; + double amplitude; + ma_bool32 duplicateChannels; +} ma_noise_config; + +MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); + +typedef struct +{ + ma_data_source_base ds; + ma_noise_config config; + ma_lcg lcg; + union + { + struct + { + double** bin; + double* accumulation; + ma_uint32* counter; + } pink; + struct + { + double* accumulation; + } brownian; + } state; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_noise; + +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); + +#endif /* MA_NO_GENERATION */ + + + +/************************************************************************************************************************************************************ + +Resource Manager + +************************************************************************************************************************************************************/ +/* The resource manager cannot be enabled if there is no decoder. */ +#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) +#define MA_NO_RESOURCE_MANAGER +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +typedef struct ma_resource_manager ma_resource_manager; +typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; +typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; +typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; +typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; + +typedef enum +{ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ +} ma_resource_manager_data_source_flags; + + +/* +Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. +*/ +typedef struct +{ + ma_async_notification* pNotification; + ma_fence* pFence; +} ma_resource_manager_pipeline_stage_notification; + +typedef struct +{ + ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ + ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ +} ma_resource_manager_pipeline_notifications; + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); + + + +/* BEGIN BACKWARDS COMPATIBILITY */ +/* TODO: Remove this block in version 0.12. */ +#if 1 +#define ma_resource_manager_job ma_job +#define ma_resource_manager_job_init ma_job_init +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING +#define ma_resource_manager_job_queue_config ma_job_queue_config +#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init +#define ma_resource_manager_job_queue ma_job_queue +#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size +#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated +#define ma_resource_manager_job_queue_init ma_job_queue_init +#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit +#define ma_resource_manager_job_queue_post ma_job_queue_post +#define ma_resource_manager_job_queue_next ma_job_queue_next +#endif +/* END BACKWARDS COMPATIBILITY */ + + + + +/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ +#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT +#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 +#endif + +typedef enum +{ + /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ + MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, + + /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ + MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 +} ma_resource_manager_flags; + +typedef struct +{ + const char* pFilePath; + const wchar_t* pFilePathW; + const ma_resource_manager_pipeline_notifications* pNotifications; + ma_uint64 initialSeekPointInPCMFrames; + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_uint32 flags; +} ma_resource_manager_data_source_config; + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); + + +typedef enum +{ + ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ + ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ + ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ + ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ +} ma_resource_manager_data_supply_type; + +typedef struct +{ + MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ + union + { + struct + { + const void* pData; + size_t sizeInBytes; + } encoded; + struct + { + const void* pData; + ma_uint64 totalFrameCount; + ma_uint64 decodedFrameCount; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + } decoded; + struct + { + ma_paged_audio_buffer_data data; + ma_uint64 decodedFrameCount; + ma_uint32 sampleRate; + } decodedPaged; + } backend; +} ma_resource_manager_data_supply; + +struct ma_resource_manager_data_buffer_node +{ + ma_uint32 hashedName32; /* The hashed name. This is the key. */ + ma_uint32 refCount; + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ + ma_resource_manager_data_supply data; + ma_resource_manager_data_buffer_node* pParent; + ma_resource_manager_data_buffer_node* pChildLo; + ma_resource_manager_data_buffer_node* pChildHi; +}; + +struct ma_resource_manager_data_buffer +{ + ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ + ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ + ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ + ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ + MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ + ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ + union + { + ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ + ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ + ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ + } connector; /* Connects this object to the node's data supply. */ +}; + +struct ma_resource_manager_data_stream +{ + ma_data_source_base ds; /* Base data source. A data stream is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ + ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ + ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ + ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ + ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ + ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ + ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + + /* Written by the public API, read by the job thread. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ + + /* Written by the job thread, read by the public API. */ + void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ + MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ + + /* Written and read by both the public API and the job thread. These must be atomic. */ + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ + MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ + MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ + MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ +}; + +struct ma_resource_manager_data_source +{ + union + { + ma_resource_manager_data_buffer buffer; + ma_resource_manager_data_stream stream; + } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ + + ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ +}; + +typedef struct +{ + ma_allocation_callbacks allocationCallbacks; + ma_log* pLog; + ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ + ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ + ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ + ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + size_t jobThreadStackSize; + ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ + ma_uint32 flags; + ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ + ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; + ma_uint32 customDecodingBackendCount; + void* pCustomDecodingBackendUserData; +} ma_resource_manager_config; + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void); + +struct ma_resource_manager +{ + ma_resource_manager_config config; + ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ +#ifndef MA_NO_THREADING + ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ + ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ +#endif + ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ + ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ + ma_log log; /* Only used if no log was specified in the config. */ +}; + +/* Init. */ +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); + +/* Registration. */ +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); + +/* Data Buffers. */ +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); + +/* Data Streams. */ +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); + +/* Data Sources. */ +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); + +/* Job management. */ +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ +#endif /* MA_NO_RESOURCE_MANAGER */ + + + +/************************************************************************************************************************************************************ + +Node Graph + +************************************************************************************************************************************************************/ +#ifndef MA_NO_NODE_GRAPH +/* Must never exceed 254. */ +#ifndef MA_MAX_NODE_BUS_COUNT +#define MA_MAX_NODE_BUS_COUNT 254 +#endif + +/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ +#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT +#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 +#endif + +/* Use this when the bus count is determined by the node instance rather than the vtable. */ +#define MA_NODE_BUS_COUNT_UNKNOWN 255 + +typedef struct ma_node_graph ma_node_graph; +typedef void ma_node; + + +/* Node flags. */ +typedef enum +{ + MA_NODE_FLAG_PASSTHROUGH = 0x00000001, + MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, + MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, + MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 +} ma_node_flags; + + +/* The playback state of a node. Either started or stopped. */ +typedef enum +{ + ma_node_state_started = 0, + ma_node_state_stopped = 1 +} ma_node_state; + + +typedef struct +{ + /* + Extended processing callback. This callback is used for effects that process input and output + at different rates (i.e. they perform resampling). This is similar to the simple version, only + they take two separate frame counts: one for input, and one for output. + + On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas + `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. + + On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set + `pFrameCountIn` to the number of input frames that were consumed. + */ + void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); + + /* + A callback for retrieving the number of a input frames that are required to output the + specified number of output frames. You would only want to implement this when the node performs + resampling. This is optional, even for nodes that perform resampling, but it does offer a + small reduction in latency as it allows miniaudio to calculate the exact number of input frames + to read at a time instead of having to estimate. + */ + ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); + + /* + The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` + parameters of the callbacks above. + */ + ma_uint8 inputBusCount; + + /* + The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` + parameters of the callbacks above. + */ + ma_uint8 outputBusCount; + + /* + Flags describing characteristics of the node. This is currently just a placeholder for some + ideas for later on. + */ + ma_uint32 flags; +} ma_node_vtable; + +typedef struct +{ + const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ + ma_node_state initialState; /* Defaults to ma_node_state_started. */ + ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ + const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ +} ma_node_config; + +MA_API ma_node_config ma_node_config_init(void); + + +/* +A node has multiple output buses. An output bus is attached to an input bus as an item in a linked +list. Think of the input bus as a linked list, with the output bus being an item in that list. +*/ +typedef struct ma_node_output_bus ma_node_output_bus; +struct ma_node_output_bus +{ + /* Immutable. */ + ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ + ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ + + /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ + ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ + MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ + MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ + MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + MA_ATOMIC(4, float) volume; /* Linear. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ +}; + +/* +A node has multiple input buses. The output buses of a node are connecting to the input busses of +another. An input bus is essentially just a linked list of output buses. +*/ +typedef struct ma_node_input_bus ma_node_input_bus; +struct ma_node_input_bus +{ + /* Mutable via multiple threads. */ + ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ + MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + + /* Set once at startup. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ +}; + + +typedef struct ma_node_base ma_node_base; +struct ma_node_base +{ + /* These variables are set once at startup. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + const ma_node_vtable* vtable; + float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ + + /* These variables are read and written only from the audio thread. */ + ma_uint16 cachedFrameCountOut; + ma_uint16 cachedFrameCountIn; + ma_uint16 consumedFrameCountIn; + + /* These variables are read and written between different threads. */ + MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_node_input_bus* pInputBuses; + ma_node_output_bus* pOutputBuses; + + /* Memory management. */ + ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ + ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ +}; + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state(const ma_node* pNode); +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); + + +typedef struct +{ + ma_uint32 channels; + ma_uint16 nodeCacheCapInFrames; +} ma_node_graph_config; + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); + + +struct ma_node_graph +{ + /* Immutable. */ + ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ + ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ + ma_uint16 nodeCacheCapInFrames; + + /* Read and written by multiple threads. */ + MA_ATOMIC(4, ma_bool32) isReading; +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); + + + +/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_data_source* pDataSource; +} ma_data_source_node_config; + +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); + + +typedef struct +{ + ma_node_base base; + ma_data_source* pDataSource; +} ma_data_source_node; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); + + +/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; + ma_uint32 outputBusCount; +} ma_splitter_node_config; + +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); + + +typedef struct +{ + ma_node_base base; +} ma_splitter_node; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Biquad Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_biquad_config biquad; +} ma_biquad_node_config; + +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); + + +typedef struct +{ + ma_node_base baseNode; + ma_biquad biquad; +} ma_biquad_node; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_lpf_config lpf; +} ma_lpf_node_config; + +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_lpf lpf; +} ma_lpf_node; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hpf_config hpf; +} ma_hpf_node_config; + +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_hpf hpf; +} ma_hpf_node; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Band Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_bpf_config bpf; +} ma_bpf_node_config; + +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_bpf bpf; +} ma_bpf_node; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Notching Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_notch_config notch; +} ma_notch_node_config; + +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_notch2 notch; +} ma_notch_node; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Peaking Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_peak_config peak; +} ma_peak_node_config; + +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_peak2 peak; +} ma_peak_node; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_loshelf_config loshelf; +} ma_loshelf_node_config; + +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_loshelf2 loshelf; +} ma_loshelf_node; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hishelf_config hishelf; +} ma_hishelf_node_config; + +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_hishelf2 hishelf; +} ma_hishelf_node; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_node_config nodeConfig; + ma_delay_config delay; +} ma_delay_node_config; + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_node_base baseNode; + ma_delay delay; +} ma_delay_node; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); +#endif /* MA_NO_NODE_GRAPH */ + + +/* SECTION: miniaudio_engine.h */ +/************************************************************************************************************************************************************ + +Engine + +************************************************************************************************************************************************************/ +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +typedef struct ma_engine ma_engine; +typedef struct ma_sound ma_sound; + + +/* Sound flags. */ +typedef enum +{ + /* Resource manager flags. */ + MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ + MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ + MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ + MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ +} ma_sound_flags; + +#ifndef MA_ENGINE_MAX_LISTENERS +#define MA_ENGINE_MAX_LISTENERS 4 +#endif + +#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) + +typedef enum +{ + ma_engine_node_type_sound, + ma_engine_node_type_group +} ma_engine_node_type; + +typedef struct +{ + ma_engine* pEngine; + ma_engine_node_type type; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ + ma_mono_expansion_mode monoExpansionMode; + ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ + ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ + ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ +} ma_engine_node_config; + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); + + +/* Base node object for both ma_sound and ma_sound_group. */ +typedef struct +{ + ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ + ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ + ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_uint32 volumeSmoothTimeInPCMFrames; + ma_mono_expansion_mode monoExpansionMode; + ma_fader fader; + ma_linear_resampler resampler; /* For pitch shift. */ + ma_spatializer spatializer; + ma_panner panner; + ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ + ma_atomic_float volume; /* Defaults to 1. */ + MA_ATOMIC(4, float) pitch; + float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ + float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ + MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ + MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ + MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + + /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ + struct + { + ma_atomic_float volumeBeg; + ma_atomic_float volumeEnd; + ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ + ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ + } fadeSettings; + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; +} ma_engine_node; + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF + +/* Callback for when a sound reaches the end. */ +typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); + +typedef struct +{ + const char* pFilePath; /* Set this to load from the resource manager. */ + const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ + ma_data_source* pDataSource; /* Set this to load from an existing data source. */ + ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ + ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ + ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ + ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ + ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ + void* pEndCallbackUserData; +#ifndef MA_NO_RESOURCE_MANAGER + ma_resource_manager_pipeline_notifications initNotifications; +#endif + ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ +} ma_sound_config; + +MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ + +struct ma_sound +{ + ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ + ma_data_source* pDataSource; + MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ + MA_ATOMIC(4, ma_bool32) atEnd; + ma_sound_end_proc endCallback; + void* pEndCallbackUserData; + ma_bool8 ownsDataSource; + + /* + We're declaring a resource manager data source object here to save us a malloc when loading a + sound via the resource manager, which I *think* will be the most common scenario. + */ +#ifndef MA_NO_RESOURCE_MANAGER + ma_resource_manager_data_source* pResourceManagerDataSource; +#endif +}; + +/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ +typedef struct ma_sound_inlined ma_sound_inlined; +struct ma_sound_inlined +{ + ma_sound sound; + ma_sound_inlined* pNext; + ma_sound_inlined* pPrev; +}; + +/* A sound group is just a sound. */ +typedef ma_sound_config ma_sound_group_config; +typedef ma_sound ma_sound_group; + +MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ + +typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); + +typedef struct +{ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_context* pContext; + ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ + ma_device_notification_proc notificationCallback; +#endif + ma_log* pLog; /* When set to NULL, will use the context's log. */ + ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ + ma_allocation_callbacks allocationCallbacks; + ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ + void* pProcessUserData; /* User data that's passed into onProcess. */ +} ma_engine_config; + +MA_API ma_engine_config ma_engine_config_init(void); + + +struct ma_engine +{ + ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ +#endif + ma_log* pLog; + ma_uint32 sampleRate; + ma_uint32 listenerCount; + ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; + ma_allocation_callbacks allocationCallbacks; + ma_bool8 ownsResourceManager; + ma_bool8 ownsDevice; + ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ + ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; + ma_mono_expansion_mode monoExpansionMode; + ma_engine_process_proc onProcess; + void* pProcessUserData; +}; + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); +MA_API void ma_engine_uninit(ma_engine* pEngine); +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); +#endif +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); + +MA_API ma_result ma_engine_start(ma_engine* pEngine); +MA_API ma_result ma_engine_stop(ma_engine* pEngine); +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); +MA_API float ma_engine_get_volume(ma_engine* pEngine); +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); +MA_API float ma_engine_get_gain_db(ma_engine* pEngine); + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +#endif +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); +MA_API void ma_sound_uninit(ma_sound* pSound); +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); +MA_API ma_result ma_sound_start(ma_sound* pSound); +MA_API ma_result ma_sound_stop(ma_sound* pSound); +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); +MA_API float ma_sound_get_volume(const ma_sound* pSound); +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); +MA_API float ma_sound_get_pan(const ma_sound* pSound); +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); +MA_API float ma_sound_get_pitch(const ma_sound* pSound); +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); +MA_API float ma_sound_get_rolloff(const ma_sound* pSound); +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); +MA_API float ma_sound_get_min_gain(const ma_sound* pSound); +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); +MA_API float ma_sound_get_max_gain(const ma_sound* pSound); +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); +MA_API float ma_sound_get_min_distance(const ma_sound* pSound); +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); +MA_API float ma_sound_get_max_distance(const ma_sound* pSound); +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); +#endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.h */ + +#ifdef __cplusplus +} +#endif +#endif /* miniaudio_h */ + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.c b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.c new file mode 100644 index 0000000..6e8af99 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.c @@ -0,0 +1,77 @@ + +#include "ma_channel_combiner_node.h" + +MA_API ma_channel_combiner_node_config ma_channel_combiner_node_config_init(ma_uint32 channels) +{ + ma_channel_combiner_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_channel_combiner_node_init(). */ + config.channels = channels; + + return config; +} + + +static void ma_channel_combiner_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_channel_combiner_node* pCombinerNode = (ma_channel_combiner_node*)pNode; + + (void)pFrameCountIn; + + ma_interleave_pcm_frames(ma_format_f32, ma_node_get_output_channels(pCombinerNode, 0), *pFrameCountOut, (const void**)ppFramesIn, (void*)ppFramesOut[0]); +} + +static ma_node_vtable g_ma_channel_combiner_node_vtable = +{ + ma_channel_combiner_node_process_pcm_frames, + NULL, + MA_NODE_BUS_COUNT_UNKNOWN, /* Input bus count is determined by the channel count and is unknown until the node instance is initialized. */ + 1, /* 1 output bus. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_channel_combiner_node_init(ma_node_graph* pNodeGraph, const ma_channel_combiner_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_combiner_node* pCombinerNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 inputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 outputChannels[1]; + ma_uint32 iChannel; + + if (pCombinerNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pCombinerNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* All input channels are mono. */ + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + inputChannels[iChannel] = 1; + } + + outputChannels[0] = pConfig->channels; + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_channel_combiner_node_vtable; + baseConfig.inputBusCount = pConfig->channels; /* The vtable has an unknown channel count, so must specify it here. */ + baseConfig.pInputChannels = inputChannels; + baseConfig.pOutputChannels = outputChannels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pCombinerNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_channel_combiner_node_uninit(ma_channel_combiner_node* pCombinerNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* The base node is always uninitialized first. */ + ma_node_uninit(pCombinerNode, pAllocationCallbacks); +} \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.h b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.h new file mode 100644 index 0000000..d38b824 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node.h @@ -0,0 +1,30 @@ +/* Include ma_channel_combiner_node.h after miniaudio.h */ +#ifndef ma_channel_combiner_node_h +#define ma_channel_combiner_node_h + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; +} ma_channel_combiner_node_config; + +MA_API ma_channel_combiner_node_config ma_channel_combiner_node_config_init(ma_uint32 channels); + + +typedef struct +{ + ma_node_base baseNode; +} ma_channel_combiner_node; + +MA_API ma_result ma_channel_combiner_node_init(ma_node_graph* pNodeGraph, const ma_channel_combiner_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_combiner_node* pSeparatorNode); +MA_API void ma_channel_combiner_node_uninit(ma_channel_combiner_node* pSeparatorNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +#ifdef __cplusplus +} +#endif +#endif /* ma_reverb_node_h */ diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node_example.c new file mode 100644 index 0000000..33ea01e --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_combiner_node/ma_channel_combiner_node_example.c @@ -0,0 +1,2 @@ +/* The channel separtor example also demonstrates how to use the combiner. */ +#include "../ma_channel_separator_node/ma_channel_separator_node_example.c" diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.c b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.c new file mode 100644 index 0000000..f1d9edc --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.c @@ -0,0 +1,81 @@ + +#include "ma_channel_separator_node.h" + +MA_API ma_channel_separator_node_config ma_channel_separator_node_config_init(ma_uint32 channels) +{ + ma_channel_separator_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_channel_separator_node_init(). */ + config.channels = channels; + + return config; +} + + +static void ma_channel_separator_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_channel_separator_node* pSplitterNode = (ma_channel_separator_node*)pNode; + + (void)pFrameCountIn; + + ma_deinterleave_pcm_frames(ma_format_f32, ma_node_get_input_channels(pSplitterNode, 0), *pFrameCountOut, (const void*)ppFramesIn[0], (void**)ppFramesOut); +} + +static ma_node_vtable g_ma_channel_separator_node_vtable = +{ + ma_channel_separator_node_process_pcm_frames, + NULL, + 1, /* 1 input bus. */ + MA_NODE_BUS_COUNT_UNKNOWN, /* Output bus count is determined by the channel count and is unknown until the node instance is initialized. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_channel_separator_node_init(ma_node_graph* pNodeGraph, const ma_channel_separator_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_separator_node* pSeparatorNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 inputChannels[1]; + ma_uint32 outputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 iChannel; + + if (pSeparatorNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSeparatorNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; /* Channel count cannot exceed the maximum number of buses. */ + } + + inputChannels[0] = pConfig->channels; + + /* All output channels are mono. */ + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + outputChannels[iChannel] = 1; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_channel_separator_node_vtable; + baseConfig.outputBusCount = pConfig->channels; /* The vtable has an unknown channel count, so must specify it here. */ + baseConfig.pInputChannels = inputChannels; + baseConfig.pOutputChannels = outputChannels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSeparatorNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_channel_separator_node_uninit(ma_channel_separator_node* pSeparatorNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* The base node is always uninitialized first. */ + ma_node_uninit(pSeparatorNode, pAllocationCallbacks); +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.h b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.h new file mode 100644 index 0000000..9c230a9 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node.h @@ -0,0 +1,29 @@ +/* Include ma_channel_separator_node.h after miniaudio.h */ +#ifndef ma_channel_separator_node_h +#define ma_channel_separator_node_h + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; +} ma_channel_separator_node_config; + +MA_API ma_channel_separator_node_config ma_channel_separator_node_config_init(ma_uint32 channels); + + +typedef struct +{ + ma_node_base baseNode; +} ma_channel_separator_node; + +MA_API ma_result ma_channel_separator_node_init(ma_node_graph* pNodeGraph, const ma_channel_separator_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_separator_node* pSeparatorNode); +MA_API void ma_channel_separator_node_uninit(ma_channel_separator_node* pSeparatorNode, const ma_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* ma_reverb_node_h */ diff --git a/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node_example.c new file mode 100644 index 0000000..de41648 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_channel_separator_node/ma_channel_separator_node_example.c @@ -0,0 +1,149 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" +#include "ma_channel_separator_node.c" +#include "../ma_channel_combiner_node/ma_channel_combiner_node.c" + +#include + +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 0 /* The input file will determine the channel count. */ +#define DEVICE_SAMPLE_RATE 48000 + +/* +In this example we're just separating out the channels with a `ma_channel_separator_node`, and then +combining them back together with a `ma_channel_combiner_node` before playing them back. +*/ +static ma_decoder g_decoder; /* The decoder that we'll read data from. */ +static ma_data_source_node g_dataSupplyNode; /* The node that will sit at the root level. Will be reading data from g_dataSupply. */ +static ma_channel_separator_node g_separatorNode; /* The separator node. */ +static ma_channel_combiner_node g_combinerNode; /* The combiner node. */ +static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */ + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pInput; + (void)pDevice; + + /* All we need to do is read from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_channel_separator_node_config separatorNodeConfig; + ma_channel_combiner_node_config combinerNodeConfig; + ma_data_source_node_config dataSupplyNodeConfig; + ma_uint32 iChannel; + + if (argc < 1) { + printf("No input file.\n"); + return -1; + } + + + /* Decoder. */ + decoderConfig = ma_decoder_config_init(DEVICE_FORMAT, 0, DEVICE_SAMPLE_RATE); + + result = ma_decoder_init_file(argv[1], &decoderConfig, &g_decoder); + if (result != MA_SUCCESS) { + printf("Failed to load decoder.\n"); + return -1; + } + + + /* Device. */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = g_decoder.outputFormat; + deviceConfig.playback.channels = g_decoder.outputChannels; + deviceConfig.sampleRate = g_decoder.outputSampleRate; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.playback.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Combiner. Attached straight to the endpoint. Input will be the separator node. */ + combinerNodeConfig = ma_channel_combiner_node_config_init(device.playback.channels); + + result = ma_channel_combiner_node_init(&g_nodeGraph, &combinerNodeConfig, NULL, &g_combinerNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize channel combiner node."); + goto done1; + } + + ma_node_attach_output_bus(&g_combinerNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + + /* + Separator. Attached to the combiner. We need to attach each of the outputs of the + separator to each of the inputs of the combiner. + */ + separatorNodeConfig = ma_channel_separator_node_config_init(device.playback.channels); + + result = ma_channel_separator_node_init(&g_nodeGraph, &separatorNodeConfig, NULL, &g_separatorNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize channel separator node."); + goto done2; + } + + /* The separator and combiner must have the same number of output and input buses respectively. */ + MA_ASSERT(ma_node_get_output_bus_count(&g_separatorNode) == ma_node_get_input_bus_count(&g_combinerNode)); + + /* Each of the separator's outputs need to be attached to the corresponding input of the combiner. */ + for (iChannel = 0; iChannel < ma_node_get_output_bus_count(&g_separatorNode); iChannel += 1) { + ma_node_attach_output_bus(&g_separatorNode, iChannel, &g_combinerNode, iChannel); + } + + + + /* Data supply. Attached to input bus 0 of the reverb node. */ + dataSupplyNodeConfig = ma_data_source_node_config_init(&g_decoder); + + result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize source node."); + goto done3; + } + + ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_separatorNode, 0); + + + + /* Now we just start the device and wait for the user to terminate the program. */ + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + + +/*done4:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL); +done3: ma_channel_separator_node_uninit(&g_separatorNode, NULL); +done2: ma_channel_combiner_node_uninit(&g_combinerNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + (void)argc; + (void)argv; + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/nodes/ma_delay_node/ma_delay_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_delay_node/ma_delay_node_example.c new file mode 100644 index 0000000..3fcb4aa --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_delay_node/ma_delay_node_example.c @@ -0,0 +1,116 @@ + +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" + +#include + +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 48000 + +static ma_audio_buffer_ref g_dataSupply; /* The underlying data source of the source node. */ +static ma_data_source_node g_dataSupplyNode; /* The node that will sit at the root level. Will be reading data from g_dataSupply. */ +static ma_delay_node g_delayNode; /* The delay node. */ +static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */ + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format && pDevice->capture.format == ma_format_f32); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* + The node graph system is a pulling style of API. At the lowest level of the chain will be a + node acting as a data source for the purpose of delivering the initial audio data. In our case, + the data source is our `pInput` buffer. We need to update the underlying data source so that it + read data from `pInput`. + */ + ma_audio_buffer_ref_set_data(&g_dataSupply, pInput, frameCount); + + /* With the source buffer configured we can now read directly from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_delay_node_config delayNodeConfig; + ma_data_source_node_config dataSupplyNodeConfig; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.capture.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Delay. Attached straight to the endpoint. */ + delayNodeConfig = ma_delay_node_config_init(device.capture.channels, device.sampleRate, (100 * device.sampleRate) / 1000, 0.5f); + + result = ma_delay_node_init(&g_nodeGraph, &delayNodeConfig, NULL, &g_delayNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize delay node."); + goto done1; + } + + ma_node_attach_output_bus(&g_delayNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + + /* Data supply. Attached to input bus 0 of the delay node. */ + result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_dataSupply); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio buffer for source."); + goto done2; + } + + dataSupplyNodeConfig = ma_data_source_node_config_init(&g_dataSupply); + + result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize source node."); + goto done2; + } + + ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_delayNode, 0); + + + + + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + +/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL); +done2: ma_delay_node_uninit(&g_delayNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + (void)argc; + (void)argv; + + return 0; +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.c b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.c new file mode 100644 index 0000000..64aae37 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.c @@ -0,0 +1,102 @@ + +#include "ma_ltrim_node.h" + +MA_API ma_ltrim_node_config ma_ltrim_node_config_init(ma_uint32 channels, float threshold) +{ + ma_ltrim_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_ltrim_node_init(). */ + config.channels = channels; + config.threshold = threshold; + + return config; +} + + +static void ma_ltrim_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_ltrim_node* pTrimNode = (ma_ltrim_node*)pNode; + ma_uint32 framesProcessedIn = 0; + ma_uint32 framesProcessedOut = 0; + ma_uint32 channelCount = ma_node_get_input_channels(pNode, 0); + + /* + If we haven't yet found the start, skip over every input sample until we find a frame outside + of the threshold. + */ + if (pTrimNode->foundStart == MA_FALSE) { + while (framesProcessedIn < *pFrameCountIn) { + ma_uint32 iChannel = 0; + for (iChannel = 0; iChannel < channelCount; iChannel += 1) { + float sample = ppFramesIn[0][framesProcessedIn*channelCount + iChannel]; + if (sample < -pTrimNode->threshold || sample > pTrimNode->threshold) { + pTrimNode->foundStart = MA_TRUE; + break; + } + } + + if (pTrimNode->foundStart) { + break; /* The start has been found. Get out of this loop and finish off processing. */ + } else { + framesProcessedIn += 1; + } + } + } + + /* If there's anything left, just copy it over. */ + framesProcessedOut = ma_min(*pFrameCountOut, *pFrameCountIn - framesProcessedIn); + ma_copy_pcm_frames(ppFramesOut[0], &ppFramesIn[0][framesProcessedIn], framesProcessedOut, ma_format_f32, channelCount); + + framesProcessedIn += framesProcessedOut; + + /* We always "process" every input frame, but we may only done a partial output. */ + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; +} + +static ma_node_vtable g_ma_ltrim_node_vtable = +{ + ma_ltrim_node_process_pcm_frames, + NULL, + 1, /* 1 input channel. */ + 1, /* 1 output channel. */ + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES +}; + +MA_API ma_result ma_ltrim_node_init(ma_node_graph* pNodeGraph, const ma_ltrim_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_ltrim_node* pTrimNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pTrimNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pTrimNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pTrimNode->threshold = pConfig->threshold; + pTrimNode->foundStart = MA_FALSE; + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_ltrim_node_vtable; + baseConfig.pInputChannels = &pConfig->channels; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pTrimNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* The base node is always uninitialized first. */ + ma_node_uninit(pTrimNode, pAllocationCallbacks); +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.h b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.h new file mode 100644 index 0000000..2dddd3a --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node.h @@ -0,0 +1,35 @@ +/* Include ma_ltrim_node.h after miniaudio.h */ +#ifndef ma_ltrim_node_h +#define ma_ltrim_node_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* +The trim node has one input and one output. +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; + float threshold; +} ma_ltrim_node_config; + +MA_API ma_ltrim_node_config ma_ltrim_node_config_init(ma_uint32 channels, float threshold); + + +typedef struct +{ + ma_node_base baseNode; + float threshold; + ma_bool32 foundStart; +} ma_ltrim_node; + +MA_API ma_result ma_ltrim_node_init(ma_node_graph* pNodeGraph, const ma_ltrim_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_ltrim_node* pTrimNode); +MA_API void ma_ltrim_node_uninit(ma_ltrim_node* pTrimNode, const ma_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* ma_ltrim_node_h */ diff --git a/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node_example.c new file mode 100644 index 0000000..0bae166 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_ltrim_node/ma_ltrim_node_example.c @@ -0,0 +1,115 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" +#include "ma_ltrim_node.c" + +#include + +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 0 /* The input file will determine the channel count. */ +#define DEVICE_SAMPLE_RATE 0 /* The input file will determine the sample rate. */ + +static ma_decoder g_decoder; /* The decoder that we'll read data from. */ +static ma_data_source_node g_dataSupplyNode; /* The node that will sit at the root level. Will be reading data from g_dataSupply. */ +static ma_ltrim_node g_trimNode; /* The trim node. */ +static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */ + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pInput; + (void)pDevice; + + /* All we need to do is read from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_ltrim_node_config trimNodeConfig; + ma_data_source_node_config dataSupplyNodeConfig; + + if (argc < 1) { + printf("No input file.\n"); + return -1; + } + + + /* Decoder. */ + decoderConfig = ma_decoder_config_init(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE); + + result = ma_decoder_init_file(argv[1], &decoderConfig, &g_decoder); + if (result != MA_SUCCESS) { + printf("Failed to load decoder.\n"); + return -1; + } + + + /* Device. */ + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = g_decoder.outputFormat; + deviceConfig.playback.channels = g_decoder.outputChannels; + deviceConfig.sampleRate = g_decoder.outputSampleRate; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.playback.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Trimmer. Attached straight to the endpoint. Input will be the data source node. */ + trimNodeConfig = ma_ltrim_node_config_init(device.playback.channels, 0); + + result = ma_ltrim_node_init(&g_nodeGraph, &trimNodeConfig, NULL, &g_trimNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize ltrim node."); + goto done1; + } + + ma_node_attach_output_bus(&g_trimNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + + /* Data supply. */ + dataSupplyNodeConfig = ma_data_source_node_config_init(&g_decoder); + + result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize data source node."); + goto done2; + } + + ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_trimNode, 0); + + + + /* Now we just start the device and wait for the user to terminate the program. */ + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + + +/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL); +done2: ma_ltrim_node_uninit(&g_trimNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.c b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.c new file mode 100644 index 0000000..5c214d6 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.c @@ -0,0 +1,78 @@ + +#define VERBLIB_IMPLEMENTATION +#include "ma_reverb_node.h" + +MA_API ma_reverb_node_config ma_reverb_node_config_init(ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_reverb_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_reverb_node_init(). */ + config.channels = channels; + config.sampleRate = sampleRate; + config.roomSize = verblib_initialroom; + config.damping = verblib_initialdamp; + config.width = verblib_initialwidth; + config.wetVolume = verblib_initialwet; + config.dryVolume = verblib_initialdry; + config.mode = verblib_initialmode; + + return config; +} + + +static void ma_reverb_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_reverb_node* pReverbNode = (ma_reverb_node*)pNode; + + (void)pFrameCountIn; + + verblib_process(&pReverbNode->reverb, ppFramesIn[0], ppFramesOut[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_reverb_node_vtable = +{ + ma_reverb_node_process_pcm_frames, + NULL, + 1, /* 1 input channel. */ + 1, /* 1 output channel. */ + MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Reverb requires continuous processing to ensure the tail get's processed. */ +}; + +MA_API ma_result ma_reverb_node_init(ma_node_graph* pNodeGraph, const ma_reverb_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_reverb_node* pReverbNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pReverbNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pReverbNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (verblib_initialize(&pReverbNode->reverb, (unsigned long)pConfig->sampleRate, (unsigned int)pConfig->channels) == 0) { + return MA_INVALID_ARGS; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_reverb_node_vtable; + baseConfig.pInputChannels = &pConfig->channels; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pReverbNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* The base node is always uninitialized first. */ + ma_node_uninit(pReverbNode, pAllocationCallbacks); +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.h b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.h new file mode 100644 index 0000000..80370fb --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node.h @@ -0,0 +1,42 @@ +/* Include ma_reverb_node.h after miniaudio.h */ +#ifndef ma_reverb_node_h +#define ma_reverb_node_h + +#include "verblib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +The reverb node has one input and one output. +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; /* The number of channels of the source, which will be the same as the output. Must be 1 or 2. */ + ma_uint32 sampleRate; + float roomSize; + float damping; + float width; + float wetVolume; + float dryVolume; + float mode; +} ma_reverb_node_config; + +MA_API ma_reverb_node_config ma_reverb_node_config_init(ma_uint32 channels, ma_uint32 sampleRate); + + +typedef struct +{ + ma_node_base baseNode; + verblib reverb; +} ma_reverb_node; + +MA_API ma_result ma_reverb_node_init(ma_node_graph* pNodeGraph, const ma_reverb_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_reverb_node* pReverbNode); +MA_API void ma_reverb_node_uninit(ma_reverb_node* pReverbNode, const ma_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* ma_reverb_node_h */ diff --git a/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node_example.c new file mode 100644 index 0000000..55ec444 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/ma_reverb_node_example.c @@ -0,0 +1,118 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" +#include "ma_reverb_node.c" + +#include + +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */ +#define DEVICE_SAMPLE_RATE 48000 /* Cannot be less than 22050 for this example. */ + +static ma_audio_buffer_ref g_dataSupply; /* The underlying data source of the source node. */ +static ma_data_source_node g_dataSupplyNode; /* The node that will sit at the root level. Will be reading data from g_dataSupply. */ +static ma_reverb_node g_reverbNode; /* The reverb node. */ +static ma_node_graph g_nodeGraph; /* The main node graph that we'll be feeding data through. */ + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format && pDevice->capture.format == ma_format_f32); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* + The node graph system is a pulling style of API. At the lowest level of the chain will be a + node acting as a data source for the purpose of delivering the initial audio data. In our case, + the data source is our `pInput` buffer. We need to update the underlying data source so that it + read data from `pInput`. + */ + ma_audio_buffer_ref_set_data(&g_dataSupply, pInput, frameCount); + + /* With the source buffer configured we can now read directly from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_reverb_node_config reverbNodeConfig; + ma_data_source_node_config dataSupplyNodeConfig; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.capture.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Reverb. Attached straight to the endpoint. */ + reverbNodeConfig = ma_reverb_node_config_init(device.capture.channels, device.sampleRate); + + result = ma_reverb_node_init(&g_nodeGraph, &reverbNodeConfig, NULL, &g_reverbNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize reverb node."); + goto done1; + } + + ma_node_attach_output_bus(&g_reverbNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + + /* Data supply. Attached to input bus 0 of the reverb node. */ + result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_dataSupply); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio buffer for source."); + goto done2; + } + + dataSupplyNodeConfig = ma_data_source_node_config_init(&g_dataSupply); + + result = ma_data_source_node_init(&g_nodeGraph, &dataSupplyNodeConfig, NULL, &g_dataSupplyNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize source node."); + goto done2; + } + + ma_node_attach_output_bus(&g_dataSupplyNode, 0, &g_reverbNode, 0); + + + + /* Now we just start the device and wait for the user to terminate the program. */ + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + + +/*done3:*/ ma_data_source_node_uninit(&g_dataSupplyNode, NULL); +done2: ma_reverb_node_uninit(&g_reverbNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + (void)argc; + (void)argv; + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/nodes/ma_reverb_node/verblib.h b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/verblib.h new file mode 100644 index 0000000..1260309 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_reverb_node/verblib.h @@ -0,0 +1,743 @@ +/* Reverb Library +* Verblib version 0.5 - 2022-10-25 +* +* Philip Bennefall - philip@blastbay.com +* +* See the end of this file for licensing terms. +* This reverb is based on Freeverb, a public domain reverb written by Jezar at Dreampoint. +* +* IMPORTANT: The reverb currently only works with 1 or 2 channels, at sample rates of 22050 HZ and above. +* These restrictions may be lifted in a future version. +* +* USAGE +* +* This is a single-file library. To use it, do something like the following in one .c file. +* #define VERBLIB_IMPLEMENTATION +* #include "verblib.h" +* +* You can then #include this file in other parts of the program as you would with any other header file. +*/ + +#ifndef VERBLIB_H +#define VERBLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* COMPILE-TIME OPTIONS */ + + /* The maximum sample rate that should be supported, specified as a multiple of 44100. */ +#ifndef verblib_max_sample_rate_multiplier +#define verblib_max_sample_rate_multiplier 4 +#endif + + /* The silence threshold which is used when calculating decay time. */ +#ifndef verblib_silence_threshold +#define verblib_silence_threshold 80.0 /* In dB (absolute). */ +#endif + + /* PUBLIC API */ + + typedef struct verblib verblib; + + /* Initialize a verblib structure. + * + * Call this function to initialize the verblib structure. + * Returns nonzero (true) on success or 0 (false) on failure. + * The function will only fail if one or more of the parameters are invalid. + */ + int verblib_initialize ( verblib* verb, unsigned long sample_rate, unsigned int channels ); + + /* Run the reverb. + * + * Call this function continuously to generate your output. + * output_buffer may be the same pointer as input_buffer if in place processing is desired. + * frames specifies the number of sample frames that should be processed. + */ + void verblib_process ( verblib* verb, const float* input_buffer, float* output_buffer, unsigned long frames ); + + /* Set the size of the room, between 0.0 and 1.0. */ + void verblib_set_room_size ( verblib* verb, float value ); + + /* Get the size of the room. */ + float verblib_get_room_size ( const verblib* verb ); + + /* Set the amount of damping, between 0.0 and 1.0. */ + void verblib_set_damping ( verblib* verb, float value ); + + /* Get the amount of damping. */ + float verblib_get_damping ( const verblib* verb ); + + /* Set the stereo width of the reverb, between 0.0 and 1.0. */ + void verblib_set_width ( verblib* verb, float value ); + + /* Get the stereo width of the reverb. */ + float verblib_get_width ( const verblib* verb ); + + /* Set the volume of the wet signal, between 0.0 and 1.0. */ + void verblib_set_wet ( verblib* verb, float value ); + + /* Get the volume of the wet signal. */ + float verblib_get_wet ( const verblib* verb ); + + /* Set the volume of the dry signal, between 0.0 and 1.0. */ + void verblib_set_dry ( verblib* verb, float value ); + + /* Get the volume of the dry signal. */ + float verblib_get_dry ( const verblib* verb ); + + /* Set the stereo width of the input signal sent to the reverb, 0.0 or greater. + * Values less than 1.0 narrow the signal, 1.0 sends the input signal unmodified, values greater than 1.0 widen the signal. + */ + void verblib_set_input_width ( verblib* verb, float value ); + + /* Get the stereo width of the input signal sent to the reverb. */ + float verblib_get_input_width ( const verblib* verb ); + + /* Set the mode of the reverb, where values below 0.5 mean normal and values above mean frozen. */ + void verblib_set_mode ( verblib* verb, float value ); + + /* Get the mode of the reverb. */ + float verblib_get_mode ( const verblib* verb ); + + /* Get the decay time in sample frames based on the current room size setting. */ + /* If freeze mode is active, the decay time is infinite and this function returns 0. */ + unsigned long verblib_get_decay_time_in_frames ( const verblib* verb ); + + /* INTERNAL STRUCTURES */ + + /* Allpass filter */ + typedef struct verblib_allpass verblib_allpass; + struct verblib_allpass + { + float* buffer; + float feedback; + int bufsize; + int bufidx; + }; + + /* Comb filter */ + typedef struct verblib_comb verblib_comb; + struct verblib_comb + { + float* buffer; + float feedback; + float filterstore; + float damp1; + float damp2; + int bufsize; + int bufidx; + }; + + /* Reverb model tuning values */ +#define verblib_numcombs 8 +#define verblib_numallpasses 4 +#define verblib_muted 0.0f +#define verblib_fixedgain 0.015f +#define verblib_scalewet 3.0f +#define verblib_scaledry 2.0f +#define verblib_scaledamp 0.8f +#define verblib_scaleroom 0.28f +#define verblib_offsetroom 0.7f +#define verblib_initialroom 0.5f +#define verblib_initialdamp 0.25f +#define verblib_initialwet 1.0f/verblib_scalewet +#define verblib_initialdry 0.0f +#define verblib_initialwidth 1.0f +#define verblib_initialinputwidth 0.0f +#define verblib_initialmode 0.0f +#define verblib_freezemode 0.5f +#define verblib_stereospread 23 + + /* + * These values assume 44.1KHz sample rate, but will be verblib_scaled appropriately. + * The values were obtained by listening tests. + */ +#define verblib_combtuningL1 1116 +#define verblib_combtuningR1 (1116+verblib_stereospread) +#define verblib_combtuningL2 1188 +#define verblib_combtuningR2 (1188+verblib_stereospread) +#define verblib_combtuningL3 1277 +#define verblib_combtuningR3 (1277+verblib_stereospread) +#define verblib_combtuningL4 1356 +#define verblib_combtuningR4 (1356+verblib_stereospread) +#define verblib_combtuningL5 1422 +#define verblib_combtuningR5 (1422+verblib_stereospread) +#define verblib_combtuningL6 1491 +#define verblib_combtuningR6 (1491+verblib_stereospread) +#define verblib_combtuningL7 1557 +#define verblib_combtuningR7 (1557+verblib_stereospread) +#define verblib_combtuningL8 1617 +#define verblib_combtuningR8 (1617+verblib_stereospread) +#define verblib_allpasstuningL1 556 +#define verblib_allpasstuningR1 (556+verblib_stereospread) +#define verblib_allpasstuningL2 441 +#define verblib_allpasstuningR2 (441+verblib_stereospread) +#define verblib_allpasstuningL3 341 +#define verblib_allpasstuningR3 (341+verblib_stereospread) +#define verblib_allpasstuningL4 225 +#define verblib_allpasstuningR4 (225+verblib_stereospread) + + /* The main reverb structure. This is the structure that you will create an instance of when using the reverb. */ + struct verblib + { + unsigned int channels; + float gain; + float roomsize, roomsize1; + float damp, damp1; + float wet, wet1, wet2; + float dry; + float width; + float input_width; + float mode; + + /* + * The following are all declared inline + * to remove the need for dynamic allocation. + */ + + /* Comb filters */ + verblib_comb combL[verblib_numcombs]; + verblib_comb combR[verblib_numcombs]; + + /* Allpass filters */ + verblib_allpass allpassL[verblib_numallpasses]; + verblib_allpass allpassR[verblib_numallpasses]; + + /* Buffers for the combs */ + float bufcombL1[verblib_combtuningL1* verblib_max_sample_rate_multiplier]; + float bufcombR1[verblib_combtuningR1* verblib_max_sample_rate_multiplier]; + float bufcombL2[verblib_combtuningL2* verblib_max_sample_rate_multiplier]; + float bufcombR2[verblib_combtuningR2* verblib_max_sample_rate_multiplier]; + float bufcombL3[verblib_combtuningL3* verblib_max_sample_rate_multiplier]; + float bufcombR3[verblib_combtuningR3* verblib_max_sample_rate_multiplier]; + float bufcombL4[verblib_combtuningL4* verblib_max_sample_rate_multiplier]; + float bufcombR4[verblib_combtuningR4* verblib_max_sample_rate_multiplier]; + float bufcombL5[verblib_combtuningL5* verblib_max_sample_rate_multiplier]; + float bufcombR5[verblib_combtuningR5* verblib_max_sample_rate_multiplier]; + float bufcombL6[verblib_combtuningL6* verblib_max_sample_rate_multiplier]; + float bufcombR6[verblib_combtuningR6* verblib_max_sample_rate_multiplier]; + float bufcombL7[verblib_combtuningL7* verblib_max_sample_rate_multiplier]; + float bufcombR7[verblib_combtuningR7* verblib_max_sample_rate_multiplier]; + float bufcombL8[verblib_combtuningL8* verblib_max_sample_rate_multiplier]; + float bufcombR8[verblib_combtuningR8* verblib_max_sample_rate_multiplier]; + + /* Buffers for the allpasses */ + float bufallpassL1[verblib_allpasstuningL1* verblib_max_sample_rate_multiplier]; + float bufallpassR1[verblib_allpasstuningR1* verblib_max_sample_rate_multiplier]; + float bufallpassL2[verblib_allpasstuningL2* verblib_max_sample_rate_multiplier]; + float bufallpassR2[verblib_allpasstuningR2* verblib_max_sample_rate_multiplier]; + float bufallpassL3[verblib_allpasstuningL3* verblib_max_sample_rate_multiplier]; + float bufallpassR3[verblib_allpasstuningR3* verblib_max_sample_rate_multiplier]; + float bufallpassL4[verblib_allpasstuningL4* verblib_max_sample_rate_multiplier]; + float bufallpassR4[verblib_allpasstuningR4* verblib_max_sample_rate_multiplier]; + }; + +#ifdef __cplusplus +} +#endif + +#endif /* VERBLIB_H */ + +/* IMPLEMENTATION */ + +#ifdef VERBLIB_IMPLEMENTATION + +#include +#include + +#ifdef _MSC_VER +#define VERBLIB_INLINE __forceinline +#else +#ifdef __GNUC__ +#define VERBLIB_INLINE inline __attribute__((always_inline)) +#else +#define VERBLIB_INLINE inline +#endif +#endif + +#define verblib_max(x, y) (((x) > (y)) ? (x) : (y)) + +#define undenormalise(sample) sample+=1.0f; sample-=1.0f; + +/* Allpass filter */ +static void verblib_allpass_initialize ( verblib_allpass* allpass, float* buf, int size ) +{ + allpass->buffer = buf; + allpass->bufsize = size; + allpass->bufidx = 0; +} + +static VERBLIB_INLINE float verblib_allpass_process ( verblib_allpass* allpass, float input ) +{ + float output; + float bufout; + + bufout = allpass->buffer[allpass->bufidx]; + undenormalise ( bufout ); + + output = -input + bufout; + allpass->buffer[allpass->bufidx] = input + ( bufout * allpass->feedback ); + + if ( ++allpass->bufidx >= allpass->bufsize ) + { + allpass->bufidx = 0; + } + + return output; +} + +static void verblib_allpass_mute ( verblib_allpass* allpass ) +{ + int i; + for ( i = 0; i < allpass->bufsize; i++ ) + { + allpass->buffer[i] = 0.0f; + } +} + +/* Comb filter */ +static void verblib_comb_initialize ( verblib_comb* comb, float* buf, int size ) +{ + comb->buffer = buf; + comb->bufsize = size; + comb->filterstore = 0.0f; + comb->bufidx = 0; +} + +static void verblib_comb_mute ( verblib_comb* comb ) +{ + int i; + for ( i = 0; i < comb->bufsize; i++ ) + { + comb->buffer[i] = 0.0f; + } +} + +static void verblib_comb_set_damp ( verblib_comb* comb, float val ) +{ + comb->damp1 = val; + comb->damp2 = 1.0f - val; +} + +static VERBLIB_INLINE float verblib_comb_process ( verblib_comb* comb, float input ) +{ + float output; + + output = comb->buffer[comb->bufidx]; + undenormalise ( output ); + + comb->filterstore = ( output * comb->damp2 ) + ( comb->filterstore * comb->damp1 ); + undenormalise ( comb->filterstore ); + + comb->buffer[comb->bufidx] = input + ( comb->filterstore * comb->feedback ); + + if ( ++comb->bufidx >= comb->bufsize ) + { + comb->bufidx = 0; + } + + return output; +} + +static void verblib_update ( verblib* verb ) +{ + /* Recalculate internal values after parameter change. */ + + int i; + + verb->wet1 = verb->wet * ( verb->width / 2.0f + 0.5f ); + verb->wet2 = verb->wet * ( ( 1.0f - verb->width ) / 2.0f ); + + if ( verb->mode >= verblib_freezemode ) + { + verb->roomsize1 = 1.0f; + verb->damp1 = 0.0f; + verb->gain = verblib_muted; + } + else + { + verb->roomsize1 = verb->roomsize; + verb->damp1 = verb->damp; + verb->gain = verblib_fixedgain; + } + + for ( i = 0; i < verblib_numcombs; i++ ) + { + verb->combL[i].feedback = verb->roomsize1; + verb->combR[i].feedback = verb->roomsize1; + verblib_comb_set_damp ( &verb->combL[i], verb->damp1 ); + verblib_comb_set_damp ( &verb->combR[i], verb->damp1 ); + } + +} + +static void verblib_mute ( verblib* verb ) +{ + int i; + if ( verblib_get_mode ( verb ) >= verblib_freezemode ) + { + return; + } + + for ( i = 0; i < verblib_numcombs; i++ ) + { + verblib_comb_mute ( &verb->combL[i] ); + verblib_comb_mute ( &verb->combR[i] ); + } + for ( i = 0; i < verblib_numallpasses; i++ ) + { + verblib_allpass_mute ( &verb->allpassL[i] ); + verblib_allpass_mute ( &verb->allpassR[i] ); + } +} + +static int verblib_get_verblib_scaled_buffer_size ( unsigned long sample_rate, unsigned long value ) +{ + long double result = ( long double ) sample_rate; + result /= 44100.0; + result = ( ( long double ) value ) * result; + if ( result < 1.0 ) + { + result = 1.0; + } + return ( int ) result; +} + +int verblib_initialize ( verblib* verb, unsigned long sample_rate, unsigned int channels ) +{ + int i; + + if ( channels != 1 && channels != 2 ) + { + return 0; /* Currently supports only 1 or 2 channels. */ + } + if ( sample_rate < 22050 ) + { + return 0; /* The minimum supported sample rate is 22050 HZ. */ + } + else if ( sample_rate > 44100 * verblib_max_sample_rate_multiplier ) + { + return 0; /* The sample rate is too high. */ + } + + verb->channels = channels; + + /* Tie the components to their buffers. */ + verblib_comb_initialize ( &verb->combL[0], verb->bufcombL1, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL1 ) ); + verblib_comb_initialize ( &verb->combR[0], verb->bufcombR1, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR1 ) ); + verblib_comb_initialize ( &verb->combL[1], verb->bufcombL2, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL2 ) ); + verblib_comb_initialize ( &verb->combR[1], verb->bufcombR2, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR2 ) ); + verblib_comb_initialize ( &verb->combL[2], verb->bufcombL3, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL3 ) ); + verblib_comb_initialize ( &verb->combR[2], verb->bufcombR3, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR3 ) ); + verblib_comb_initialize ( &verb->combL[3], verb->bufcombL4, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL4 ) ); + verblib_comb_initialize ( &verb->combR[3], verb->bufcombR4, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR4 ) ); + verblib_comb_initialize ( &verb->combL[4], verb->bufcombL5, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL5 ) ); + verblib_comb_initialize ( &verb->combR[4], verb->bufcombR5, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR5 ) ); + verblib_comb_initialize ( &verb->combL[5], verb->bufcombL6, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL6 ) ); + verblib_comb_initialize ( &verb->combR[5], verb->bufcombR6, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR6 ) ); + verblib_comb_initialize ( &verb->combL[6], verb->bufcombL7, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL7 ) ); + verblib_comb_initialize ( &verb->combR[6], verb->bufcombR7, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR7 ) ); + verblib_comb_initialize ( &verb->combL[7], verb->bufcombL8, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningL8 ) ); + verblib_comb_initialize ( &verb->combR[7], verb->bufcombR8, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_combtuningR8 ) ); + + verblib_allpass_initialize ( &verb->allpassL[0], verb->bufallpassL1, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningL1 ) ); + verblib_allpass_initialize ( &verb->allpassR[0], verb->bufallpassR1, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningR1 ) ); + verblib_allpass_initialize ( &verb->allpassL[1], verb->bufallpassL2, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningL2 ) ); + verblib_allpass_initialize ( &verb->allpassR[1], verb->bufallpassR2, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningR2 ) ); + verblib_allpass_initialize ( &verb->allpassL[2], verb->bufallpassL3, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningL3 ) ); + verblib_allpass_initialize ( &verb->allpassR[2], verb->bufallpassR3, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningR3 ) ); + verblib_allpass_initialize ( &verb->allpassL[3], verb->bufallpassL4, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningL4 ) ); + verblib_allpass_initialize ( &verb->allpassR[3], verb->bufallpassR4, verblib_get_verblib_scaled_buffer_size ( sample_rate, verblib_allpasstuningR4 ) ); + + /* Set default values. */ + for ( i = 0; i < verblib_numallpasses; i++ ) + { + verb->allpassL[i].feedback = 0.5f; + verb->allpassR[i].feedback = 0.5f; + } + + verblib_set_wet ( verb, verblib_initialwet ); + verblib_set_room_size ( verb, verblib_initialroom ); + verblib_set_dry ( verb, verblib_initialdry ); + verblib_set_damping ( verb, verblib_initialdamp ); + verblib_set_width ( verb, verblib_initialwidth ); + verblib_set_input_width ( verb, verblib_initialinputwidth ); + verblib_set_mode ( verb, verblib_initialmode ); + + /* The buffers will be full of rubbish - so we MUST mute them. */ + verblib_mute ( verb ); + + return 1; +} + +void verblib_process ( verblib* verb, const float* input_buffer, float* output_buffer, unsigned long frames ) +{ + int i; + float outL, outR, input; + + if ( verb->channels == 1 ) + { + while ( frames-- > 0 ) + { + outL = 0.0f; + input = ( input_buffer[0] * 2.0f ) * verb->gain; + + /* Accumulate comb filters in parallel. */ + for ( i = 0; i < verblib_numcombs; i++ ) + { + outL += verblib_comb_process ( &verb->combL[i], input ); + } + + /* Feed through allpasses in series. */ + for ( i = 0; i < verblib_numallpasses; i++ ) + { + outL = verblib_allpass_process ( &verb->allpassL[i], outL ); + } + + /* Calculate output REPLACING anything already there. */ + output_buffer[0] = outL * verb->wet1 + input_buffer[0] * verb->dry; + + /* Increment sample pointers. */ + ++input_buffer; + ++output_buffer; + } + } + else if ( verb->channels == 2 ) + { + if ( verb->input_width > 0.0f ) /* Stereo input is widened or narrowed. */ + { + + /* + * The stereo mid/side code is derived from: + * https://www.musicdsp.org/en/latest/Effects/256-stereo-width-control-obtained-via-transfromation-matrix.html + * The description of the code on the above page says: + * + * This work is hereby placed in the public domain for all purposes, including + * use in commercial applications. + */ + + const float tmp = 1 / verblib_max ( 1 + verb->input_width, 2 ); + const float coef_mid = 1 * tmp; + const float coef_side = verb->input_width * tmp; + while ( frames-- > 0 ) + { + const float mid = ( input_buffer[0] + input_buffer[1] ) * coef_mid; + const float side = ( input_buffer[1] - input_buffer[0] ) * coef_side; + const float input_left = ( mid - side ) * ( verb->gain * 2.0f ); + const float input_right = ( mid + side ) * ( verb->gain * 2.0f ); + + outL = outR = 0.0f; + + /* Accumulate comb filters in parallel. */ + for ( i = 0; i < verblib_numcombs; i++ ) + { + outL += verblib_comb_process ( &verb->combL[i], input_left ); + outR += verblib_comb_process ( &verb->combR[i], input_right ); + } + + /* Feed through allpasses in series. */ + for ( i = 0; i < verblib_numallpasses; i++ ) + { + outL = verblib_allpass_process ( &verb->allpassL[i], outL ); + outR = verblib_allpass_process ( &verb->allpassR[i], outR ); + } + + /* Calculate output REPLACING anything already there. */ + output_buffer[0] = outL * verb->wet1 + outR * verb->wet2 + input_buffer[0] * verb->dry; + output_buffer[1] = outR * verb->wet1 + outL * verb->wet2 + input_buffer[1] * verb->dry; + + /* Increment sample pointers. */ + input_buffer += 2; + output_buffer += 2; + } + } + else /* Stereo input is summed to mono. */ + { + while ( frames-- > 0 ) + { + outL = outR = 0.0f; + input = ( input_buffer[0] + input_buffer[1] ) * verb->gain; + + /* Accumulate comb filters in parallel. */ + for ( i = 0; i < verblib_numcombs; i++ ) + { + outL += verblib_comb_process ( &verb->combL[i], input ); + outR += verblib_comb_process ( &verb->combR[i], input ); + } + + /* Feed through allpasses in series. */ + for ( i = 0; i < verblib_numallpasses; i++ ) + { + outL = verblib_allpass_process ( &verb->allpassL[i], outL ); + outR = verblib_allpass_process ( &verb->allpassR[i], outR ); + } + + /* Calculate output REPLACING anything already there. */ + output_buffer[0] = outL * verb->wet1 + outR * verb->wet2 + input_buffer[0] * verb->dry; + output_buffer[1] = outR * verb->wet1 + outL * verb->wet2 + input_buffer[1] * verb->dry; + + /* Increment sample pointers. */ + input_buffer += 2; + output_buffer += 2; + } + } + } +} + +void verblib_set_room_size ( verblib* verb, float value ) +{ + verb->roomsize = ( value * verblib_scaleroom ) + verblib_offsetroom; + verblib_update ( verb ); +} + +float verblib_get_room_size ( const verblib* verb ) +{ + return ( verb->roomsize - verblib_offsetroom ) / verblib_scaleroom; +} + +void verblib_set_damping ( verblib* verb, float value ) +{ + verb->damp = value * verblib_scaledamp; + verblib_update ( verb ); +} + +float verblib_get_damping ( const verblib* verb ) +{ + return verb->damp / verblib_scaledamp; +} + +void verblib_set_wet ( verblib* verb, float value ) +{ + verb->wet = value * verblib_scalewet; + verblib_update ( verb ); +} + +float verblib_get_wet ( const verblib* verb ) +{ + return verb->wet / verblib_scalewet; +} + +void verblib_set_dry ( verblib* verb, float value ) +{ + verb->dry = value * verblib_scaledry; +} + +float verblib_get_dry ( const verblib* verb ) +{ + return verb->dry / verblib_scaledry; +} + +void verblib_set_width ( verblib* verb, float value ) +{ + verb->width = value; + verblib_update ( verb ); +} + +float verblib_get_width ( const verblib* verb ) +{ + return verb->width; +} + +void verblib_set_input_width ( verblib* verb, float value ) +{ + verb->input_width = value; +} + +float verblib_get_input_width ( const verblib* verb ) +{ + return verb->input_width; +} + +void verblib_set_mode ( verblib* verb, float value ) +{ + verb->mode = value; + verblib_update ( verb ); +} + +float verblib_get_mode ( const verblib* verb ) +{ + if ( verb->mode >= verblib_freezemode ) + { + return 1.0f; + } + return 0.0f; +} + +unsigned long verblib_get_decay_time_in_frames ( const verblib* verb ) +{ + double decay; + + if ( verb->mode >= verblib_freezemode ) + { + return 0; /* Freeze mode creates an infinite decay. */ + } + + decay = verblib_silence_threshold / fabs ( -20.0 * log ( 1.0 / verb->roomsize1 ) ); + decay *= ( double ) ( verb->combR[7].bufsize * 2 ); + return ( unsigned long ) decay; +} + +#endif /* VERBLIB_IMPLEMENTATION */ + +/* REVISION HISTORY +* +* Version 0.5 - 2022-10-25 +* Added two functions called verblib_set_input_width and verblib_get_input_width. +* +* Version 0.4 - 2021-01-23 +* Added a function called verblib_get_decay_time_in_frames. +* +* Version 0.3 - 2021-01-18 +* Added support for sample rates of 22050 and above. +* +* Version 0.2 - 2021-01-17 +* Added support for processing mono audio. +* +* Version 0.1 - 2021-01-17 +* Initial release. +*/ + +/* LICENSE + +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT No Attribution License +Copyright (c) 2022 Philip Bennefall + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.c b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.c new file mode 100644 index 0000000..32462bb --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.c @@ -0,0 +1,80 @@ + +#define VOCLIB_IMPLEMENTATION +#include "ma_vocoder_node.h" + +MA_API ma_vocoder_node_config ma_vocoder_node_config_init(ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_vocoder_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); /* Input and output channels will be set in ma_vocoder_node_init(). */ + config.channels = channels; + config.sampleRate = sampleRate; + config.bands = 16; + config.filtersPerBand = 6; + + return config; +} + + +static void ma_vocoder_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_vocoder_node* pVocoderNode = (ma_vocoder_node*)pNode; + + (void)pFrameCountIn; + + voclib_process(&pVocoderNode->voclib, ppFramesIn[0], ppFramesIn[1], ppFramesOut[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_vocoder_node_vtable = +{ + ma_vocoder_node_process_pcm_frames, + NULL, + 2, /* 2 input channels. */ + 1, /* 1 output channel. */ + 0 +}; + +MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocoder_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vocoder_node* pVocoderNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 inputChannels[2]; + ma_uint32 outputChannels[1]; + + if (pVocoderNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pVocoderNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (voclib_initialize(&pVocoderNode->voclib, (unsigned char)pConfig->bands, (unsigned char)pConfig->filtersPerBand, (unsigned int)pConfig->sampleRate, (unsigned char)pConfig->channels) == 0) { + return MA_INVALID_ARGS; + } + + inputChannels [0] = pConfig->channels; /* Source/carrier. */ + inputChannels [1] = 1; /* Excite/modulator. Must always be single channel. */ + outputChannels[0] = pConfig->channels; /* Output channels is always the same as the source/carrier. */ + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_vocoder_node_vtable; + baseConfig.pInputChannels = inputChannels; + baseConfig.pOutputChannels = outputChannels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pVocoderNode->baseNode); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_vocoder_node_uninit(ma_vocoder_node* pVocoderNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* The base node must always be initialized first. */ + ma_node_uninit(pVocoderNode, pAllocationCallbacks); +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.h b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.h new file mode 100644 index 0000000..12d232f --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node.h @@ -0,0 +1,45 @@ +/* Include ma_vocoder_node.h after miniaudio.h */ +#ifndef ma_vocoder_node_h +#define ma_vocoder_node_h + +#include "voclib.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +The vocoder node has two inputs and one output. Inputs: + + Input Bus 0: The source/carrier stream. + Input Bus 1: The excite/modulator stream. + +The source (input bus 0) and output must have the same channel count, and is restricted to 1 or 2. +The excite (input bus 1) is restricted to 1 channel. +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; /* The number of channels of the source, which will be the same as the output. Must be 1 or 2. The excite bus must always have one channel. */ + ma_uint32 sampleRate; + ma_uint32 bands; /* Defaults to 16. */ + ma_uint32 filtersPerBand; /* Defaults to 6. */ +} ma_vocoder_node_config; + +MA_API ma_vocoder_node_config ma_vocoder_node_config_init(ma_uint32 channels, ma_uint32 sampleRate); + + +typedef struct +{ + ma_node_base baseNode; + voclib_instance voclib; +} ma_vocoder_node; + +MA_API ma_result ma_vocoder_node_init(ma_node_graph* pNodeGraph, const ma_vocoder_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_vocoder_node* pVocoderNode); +MA_API void ma_vocoder_node_uninit(ma_vocoder_node* pVocoderNode, const ma_allocation_callbacks* pAllocationCallbacks); + +#ifdef __cplusplus +} +#endif +#endif /* ma_vocoder_node_h */ diff --git a/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node_example.c b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node_example.c new file mode 100644 index 0000000..5770098 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/ma_vocoder_node_example.c @@ -0,0 +1,148 @@ +/* +Demonstrates how to apply an effect to a duplex stream using the node graph system. + +This example applies a vocoder effect to the input stream before outputting it. A custom node +called `ma_vocoder_node` is used to achieve the effect which can be found in the extras folder in +the miniaudio repository. The vocoder node uses https://github.com/blastbay/voclib to achieve the +effect. +*/ +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" +#include "ma_vocoder_node.c" + +#include + +#define DEVICE_FORMAT ma_format_f32 /* Must always be f32 for this example because the node graph system only works with this. */ +#define DEVICE_CHANNELS 1 /* For this example, always set to 1. */ + +static ma_waveform g_sourceData; /* The underlying data source of the excite node. */ +static ma_audio_buffer_ref g_exciteData; /* The underlying data source of the source node. */ +static ma_data_source_node g_sourceNode; /* A data source node containing the source data we'll be sending through to the vocoder. This will be routed into the first bus of the vocoder node. */ +static ma_data_source_node g_exciteNode; /* A data source node containing the excite data we'll be sending through to the vocoder. This will be routed into the second bus of the vocoder node. */ +static ma_vocoder_node g_vocoderNode; /* The vocoder node. */ +static ma_node_graph g_nodeGraph; + +void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* + The node graph system is a pulling style of API. At the lowest level of the chain will be a + node acting as a data source for the purpose of delivering the initial audio data. In our case, + the data source is our `pInput` buffer. We need to update the underlying data source so that it + read data from `pInput`. + */ + ma_audio_buffer_ref_set_data(&g_exciteData, pInput, frameCount); + + /* With the source buffer configured we can now read directly from the node graph. */ + ma_node_graph_read_pcm_frames(&g_nodeGraph, pOutput, frameCount, NULL); +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_device_config deviceConfig; + ma_device device; + ma_node_graph_config nodeGraphConfig; + ma_vocoder_node_config vocoderNodeConfig; + ma_data_source_node_config sourceNodeConfig; + ma_data_source_node_config exciteNodeConfig; + ma_waveform_config waveformConfig; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = DEVICE_CHANNELS; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.dataCallback = data_callback; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return result; + } + + + /* Now we can setup our node graph. */ + nodeGraphConfig = ma_node_graph_config_init(device.capture.channels); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &g_nodeGraph); + if (result != MA_SUCCESS) { + printf("Failed to initialize node graph."); + goto done0; + } + + + /* Vocoder. Attached straight to the endpoint. */ + vocoderNodeConfig = ma_vocoder_node_config_init(device.capture.channels, device.sampleRate); + + result = ma_vocoder_node_init(&g_nodeGraph, &vocoderNodeConfig, NULL, &g_vocoderNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize vocoder node."); + goto done1; + } + + ma_node_attach_output_bus(&g_vocoderNode, 0, ma_node_graph_get_endpoint(&g_nodeGraph), 0); + + /* Amplify the volume of the vocoder output because in my testing it is a bit quiet. */ + ma_node_set_output_bus_volume(&g_vocoderNode, 0, 4); + + + /* Source/carrier. Attached to input bus 0 of the vocoder node. */ + waveformConfig = ma_waveform_config_init(device.capture.format, device.capture.channels, device.sampleRate, ma_waveform_type_sawtooth, 1.0, 50); + + result = ma_waveform_init(&waveformConfig, &g_sourceData); + if (result != MA_SUCCESS) { + printf("Failed to initialize waveform for excite node."); + goto done3; + } + + sourceNodeConfig = ma_data_source_node_config_init(&g_sourceData); + + result = ma_data_source_node_init(&g_nodeGraph, &sourceNodeConfig, NULL, &g_sourceNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize excite node."); + goto done3; + } + + ma_node_attach_output_bus(&g_sourceNode, 0, &g_vocoderNode, 0); + + + /* Excite/modulator. Attached to input bus 1 of the vocoder node. */ + result = ma_audio_buffer_ref_init(device.capture.format, device.capture.channels, NULL, 0, &g_exciteData); + if (result != MA_SUCCESS) { + printf("Failed to initialize audio buffer for source."); + goto done2; + } + + exciteNodeConfig = ma_data_source_node_config_init(&g_exciteData); + + result = ma_data_source_node_init(&g_nodeGraph, &exciteNodeConfig, NULL, &g_exciteNode); + if (result != MA_SUCCESS) { + printf("Failed to initialize source node."); + goto done2; + } + + ma_node_attach_output_bus(&g_exciteNode, 0, &g_vocoderNode, 1); + + + ma_device_start(&device); + + printf("Press Enter to quit...\n"); + getchar(); + + /* It's important that we stop the device first or else we'll uninitialize the graph from under the device. */ + ma_device_stop(&device); + +/*done4:*/ ma_data_source_node_uninit(&g_exciteNode, NULL); +done3: ma_data_source_node_uninit(&g_sourceNode, NULL); +done2: ma_vocoder_node_uninit(&g_vocoderNode, NULL); +done1: ma_node_graph_uninit(&g_nodeGraph, NULL); +done0: ma_device_uninit(&device); + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/voclib.h b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/voclib.h new file mode 100644 index 0000000..a0a1436 --- /dev/null +++ b/thirdparty/miniaudio/extras/nodes/ma_vocoder_node/voclib.h @@ -0,0 +1,672 @@ +/* Vocoder Library +* Voclib version 1.1 - 2019-02-16 +* +* Philip Bennefall - philip@blastbay.com +* +* See the end of this file for licensing terms. +* The filter implementation was derived from public domain code found on musicdsp.org (see the section called "Filters" for more details). +* +* USAGE +* +* This is a single-file library. To use it, do something like the following in one .c file. +* #define VOCLIB_IMPLEMENTATION +* #include "voclib.h" +* +* You can then #include this file in other parts of the program as you would with any other header file. +*/ + +#ifndef VOCLIB_H +#define VOCLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* COMPILE-TIME OPTIONS */ + + /* The maximum number of bands that the vocoder can be initialized with (lower this number to save memory). */ +#define VOCLIB_MAX_BANDS 96 + + /* The maximum number of filters per vocoder band (lower this number to save memory). */ +#define VOCLIB_MAX_FILTERS_PER_BAND 8 + + /* PUBLIC API */ + + typedef struct voclib_instance voclib_instance; + + /* Initialize a voclib_instance structure. + * + * Call this function to initialize the voclib_instance structure. + * bands is the number of bands that the vocoder should use; recommended values are between 12 and 64. + * bands must be between 4 and VOCLIB_MAX_BANDS (inclusive). + * filters_per_band determines the steapness with which the filterbank divides the signal; a value of 6 is recommended. + * filters_per_band must be between 1 and VOCLIB_MAX_FILTERS_PER_BAND (inclusive). + * sample_rate is the number of samples per second in hertz, and should be between 8000 and 192000 (inclusive). + * carrier_channels is the number of channels that the carrier has, and should be between 1 and 2 (inclusive). + * Note: The modulator must always have only one channel. + * Returns nonzero (true) on success or 0 (false) on failure. + * The function will only fail if one or more of the parameters are invalid. + */ + int voclib_initialize ( voclib_instance* instance, unsigned char bands, unsigned char filters_per_band, unsigned int sample_rate, unsigned char carrier_channels ); + + /* Run the vocoder. + * + * Call this function continuously to generate your output. + * carrier_buffer and modulator_buffer should contain the carrier and modulator signals respectively. + * The modulator must always have one channel. + * If the carrier has two channels, the samples in carrier_buffer must be interleaved. + * output_buffer will be filled with the result, and must be able to hold as many channels as the carrier. + * If the carrier has two channels, the output buffer will be filled with interleaved samples. + * output_buffer may be the same pointer as either carrier_buffer or modulator_buffer as long as it can hold the same number of channels as the carrier. + * The processing is performed in place. + * frames specifies the number of sample frames that should be processed. + * Returns nonzero (true) on success or 0 (false) on failure. + * The function will only fail if one or more of the parameters are invalid. + */ + int voclib_process ( voclib_instance* instance, const float* carrier_buffer, const float* modulator_buffer, float* output_buffer, unsigned int frames ); + + /* Reset the vocoder sample history. + * + * In order to run smoothly, the vocoder needs to store a few recent samples internally. + * This function resets that internal history. This should only be done if you are processing a new stream. + * Resetting the history in the middle of a stream will cause clicks. + */ + void voclib_reset_history ( voclib_instance* instance ); + + /* Set the reaction time of the vocoder in seconds. + * + * The reaction time is the time it takes for the vocoder to respond to a volume change in the modulator. + * A value of 0.03 (AKA 30 milliseconds) is recommended for intelligible speech. + * Values lower than about 0.02 will make the output sound raspy and unpleasant. + * Values above 0.2 or so will make the speech hard to understand, but can be used for special effects. + * The value must be between 0.002 and 2.0 (inclusive). + * Returns nonzero (true) on success or 0 (false) on failure. + * The function will only fail if the parameter is invalid. + */ + int voclib_set_reaction_time ( voclib_instance* instance, float reaction_time ); + + /* Get the current reaction time of the vocoder in seconds. */ + float voclib_get_reaction_time ( const voclib_instance* instance ); + + /* Set the formant shift of the vocoder in octaves. + * + * Formant shifting changes the size of the speaker's head. + * A value of 1.0 leaves the head size unmodified. + * Values lower than 1.0 make the head larger, and values above 1.0 make it smaller. + * The value must be between 0.25 and 4.0 (inclusive). + * Returns nonzero (true) on success or 0 (false) on failure. + * The function will only fail if the parameter is invalid. + */ + int voclib_set_formant_shift ( voclib_instance* instance, float formant_shift ); + + /* Get the current formant shift of the vocoder in octaves. */ + float voclib_get_formant_shift ( const voclib_instance* instance ); + + /* INTERNAL STRUCTURES */ + + /* this holds the data required to update samples thru a filter. */ + typedef struct + { + float a0, a1, a2, a3, a4; + float x1, x2, y1, y2; + } voclib_biquad; + + /* Stores the state required for our envelope follower. */ + typedef struct + { + float coef; + float history[4]; + } voclib_envelope; + + /* Holds a set of filters required for one vocoder band. */ + typedef struct + { + voclib_biquad filters[VOCLIB_MAX_FILTERS_PER_BAND]; + } voclib_band; + + /* The main instance structure. This is the structure that you will create an instance of when using the vocoder. */ + struct voclib_instance + { + voclib_band analysis_bands[VOCLIB_MAX_BANDS]; /* The filterbank used for analysis (these are applied to the modulator). */ + voclib_envelope analysis_envelopes[VOCLIB_MAX_BANDS]; /* The envelopes used to smooth the analysis bands. */ + voclib_band synthesis_bands[VOCLIB_MAX_BANDS * 2]; /* The filterbank used for synthesis (these are applied to the carrier). The second half of the array is only used for stereo carriers. */ + float reaction_time; /* In seconds. Higher values make the vocoder respond more slowly to changes in the modulator. */ + float formant_shift; /* In octaves. 1.0 is unchanged. */ + unsigned int sample_rate; /* In hertz. */ + unsigned char bands; + unsigned char filters_per_band; + unsigned char carrier_channels; + }; + +#ifdef __cplusplus +} +#endif +#endif /* VOCLIB_H */ + +/* IMPLEMENTATION */ + +#ifdef VOCLIB_IMPLEMENTATION + +#include +#include + +#ifdef _MSC_VER +#define VOCLIB_INLINE __forceinline +#else +#ifdef __GNUC__ +#define VOCLIB_INLINE inline __attribute__((always_inline)) +#else +#define VOCLIB_INLINE inline +#endif +#endif + +/* Filters +* +* The filter code below was derived from http://www.musicdsp.org/files/biquad.c. The comment at the top of biquad.c file reads: +* +* Simple implementation of Biquad filters -- Tom St Denis + * + * Based on the work + +Cookbook formulae for audio EQ biquad filter coefficients +--------------------------------------------------------- +by Robert Bristow-Johnson, pbjrbj@viconet.com a.k.a. robert@audioheads.com + + * Available on the web at + +http://www.smartelectronix.com/musicdsp/text/filters005.txt + + * Enjoy. + * + * This work is hereby placed in the public domain for all purposes, whether + * commercial, free [as in speech] or educational, etc. Use the code and please + * give me credit if you wish. + * + * Tom St Denis -- http://tomstdenis.home.dhs.org +*/ + +#ifndef VOCLIB_M_LN2 +#define VOCLIB_M_LN2 0.69314718055994530942 +#endif + +#ifndef VOCLIB_M_PI +#define VOCLIB_M_PI 3.14159265358979323846 +#endif + +/* Computes a BiQuad filter on a sample. */ +static VOCLIB_INLINE float voclib_BiQuad ( float sample, voclib_biquad* b ) +{ + float result; + + /* compute the result. */ + result = b->a0 * sample + b->a1 * b->x1 + b->a2 * b->x2 - + b->a3 * b->y1 - b->a4 * b->y2; + + /* shift x1 to x2, sample to x1. */ + b->x2 = b->x1; + b->x1 = sample; + + /* shift y1 to y2, result to y1. */ + b->y2 = b->y1; + b->y1 = result; + + return result; +} + +/* filter types. */ +enum +{ + VOCLIB_LPF, /* low pass filter */ + VOCLIB_HPF, /* High pass filter */ + VOCLIB_BPF, /* band pass filter */ + VOCLIB_NOTCH, /* Notch Filter */ + VOCLIB_PEQ, /* Peaking band EQ filter */ + VOCLIB_LSH, /* Low shelf filter */ + VOCLIB_HSH /* High shelf filter */ +}; + +/* sets up a BiQuad Filter. */ +static void voclib_BiQuad_new ( voclib_biquad* b, int type, float dbGain, /* gain of filter */ + float freq, /* center frequency */ + float srate, /* sampling rate */ + float bandwidth ) /* bandwidth in octaves */ +{ + float A, omega, sn, cs, alpha, beta; + float a0, a1, a2, b0, b1, b2; + + /* setup variables. */ + A = ( float ) pow ( 10, dbGain / 40.0f ); + omega = ( float ) ( 2.0 * VOCLIB_M_PI * freq / srate ); + sn = ( float ) sin ( omega ); + cs = ( float ) cos ( omega ); + alpha = sn * ( float ) sinh ( VOCLIB_M_LN2 / 2 * bandwidth * omega / sn ); + beta = ( float ) sqrt ( A + A ); + + switch ( type ) + { + case VOCLIB_LPF: + b0 = ( 1 - cs ) / 2; + b1 = 1 - cs; + b2 = ( 1 - cs ) / 2; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + break; + case VOCLIB_HPF: + b0 = ( 1 + cs ) / 2; + b1 = - ( 1 + cs ); + b2 = ( 1 + cs ) / 2; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + break; + case VOCLIB_BPF: + b0 = alpha; + b1 = 0; + b2 = -alpha; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + break; + case VOCLIB_NOTCH: + b0 = 1; + b1 = -2 * cs; + b2 = 1; + a0 = 1 + alpha; + a1 = -2 * cs; + a2 = 1 - alpha; + break; + case VOCLIB_PEQ: + b0 = 1 + ( alpha * A ); + b1 = -2 * cs; + b2 = 1 - ( alpha * A ); + a0 = 1 + ( alpha / A ); + a1 = -2 * cs; + a2 = 1 - ( alpha / A ); + break; + case VOCLIB_LSH: + b0 = A * ( ( A + 1 ) - ( A - 1 ) * cs + beta * sn ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * cs ); + b2 = A * ( ( A + 1 ) - ( A - 1 ) * cs - beta * sn ); + a0 = ( A + 1 ) + ( A - 1 ) * cs + beta * sn; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * cs ); + a2 = ( A + 1 ) + ( A - 1 ) * cs - beta * sn; + break; + case VOCLIB_HSH: + b0 = A * ( ( A + 1 ) + ( A - 1 ) * cs + beta * sn ); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * cs ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * cs - beta * sn ); + a0 = ( A + 1 ) - ( A - 1 ) * cs + beta * sn; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * cs ); + a2 = ( A + 1 ) - ( A - 1 ) * cs - beta * sn; + break; + default: + assert ( 0 ); /* Misuse. */ + return; + } + + /* precompute the coefficients. */ + b->a0 = b0 / a0; + b->a1 = b1 / a0; + b->a2 = b2 / a0; + b->a3 = a1 / a0; + b->a4 = a2 / a0; +} + +/* Reset the filter history. */ +static void voclib_BiQuad_reset ( voclib_biquad* b ) +{ + b->x1 = b->x2 = 0.0f; + b->y1 = b->y2 = 0.0f; +} + +/* Envelope follower. */ + +static void voclib_envelope_configure ( voclib_envelope* envelope, double time_in_seconds, double sample_rate ) +{ + envelope->coef = ( float ) ( pow ( 0.01, 1.0 / ( time_in_seconds * sample_rate ) ) ); +} + +/* Reset the envelope history. */ +static void voclib_envelope_reset ( voclib_envelope* envelope ) +{ + envelope->history[0] = 0.0f; + envelope->history[1] = 0.0f; + envelope->history[2] = 0.0f; + envelope->history[3] = 0.0f; +} + +static VOCLIB_INLINE float voclib_envelope_tick ( voclib_envelope* envelope, float sample ) +{ + const float coef = envelope->coef; + envelope->history[0] = ( float ) ( ( 1.0f - coef ) * fabs ( sample ) ) + ( coef * envelope->history[0] ); + envelope->history[1] = ( ( 1.0f - coef ) * envelope->history[0] ) + ( coef * envelope->history[1] ); + envelope->history[2] = ( ( 1.0f - coef ) * envelope->history[1] ) + ( coef * envelope->history[2] ); + envelope->history[3] = ( ( 1.0f - coef ) * envelope->history[2] ) + ( coef * envelope->history[3] ); + return envelope->history[3]; +} + +/* Initialize the vocoder filterbank. */ +static void voclib_initialize_filterbank ( voclib_instance* instance, int carrier_only ) +{ + unsigned char i; + double step; + double lastfreq = 0.0; + double minfreq = 80.0; + double maxfreq = instance->sample_rate; + if ( maxfreq > 12000.0 ) + { + maxfreq = 12000.0; + } + step = pow ( ( maxfreq / minfreq ), ( 1.0 / instance->bands ) ); + + for ( i = 0; i < instance->bands; ++i ) + { + unsigned char i2; + double bandwidth, nextfreq; + double priorfreq = lastfreq; + if ( lastfreq > 0.0 ) + { + lastfreq *= step; + } + else + { + lastfreq = minfreq; + } + nextfreq = lastfreq * step; + bandwidth = ( nextfreq - priorfreq ) / lastfreq; + + if ( !carrier_only ) + { + voclib_BiQuad_new ( &instance->analysis_bands[i].filters[0], VOCLIB_BPF, 0.0f, ( float ) lastfreq, ( float ) instance->sample_rate, ( float ) bandwidth ); + for ( i2 = 1; i2 < instance->filters_per_band; ++i2 ) + { + instance->analysis_bands[i].filters[i2].a0 = instance->analysis_bands[i].filters[0].a0; + instance->analysis_bands[i].filters[i2].a1 = instance->analysis_bands[i].filters[0].a1; + instance->analysis_bands[i].filters[i2].a2 = instance->analysis_bands[i].filters[0].a2; + instance->analysis_bands[i].filters[i2].a3 = instance->analysis_bands[i].filters[0].a3; + instance->analysis_bands[i].filters[i2].a4 = instance->analysis_bands[i].filters[0].a4; + } + } + + if ( instance->formant_shift != 1.0f ) + { + voclib_BiQuad_new ( &instance->synthesis_bands[i].filters[0], VOCLIB_BPF, 0.0f, ( float ) ( lastfreq * instance->formant_shift ), ( float ) instance->sample_rate, ( float ) bandwidth ); + } + else + { + instance->synthesis_bands[i].filters[0].a0 = instance->analysis_bands[i].filters[0].a0; + instance->synthesis_bands[i].filters[0].a1 = instance->analysis_bands[i].filters[0].a1; + instance->synthesis_bands[i].filters[0].a2 = instance->analysis_bands[i].filters[0].a2; + instance->synthesis_bands[i].filters[0].a3 = instance->analysis_bands[i].filters[0].a3; + instance->synthesis_bands[i].filters[0].a4 = instance->analysis_bands[i].filters[0].a4; + } + + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[0].a0 = instance->synthesis_bands[i].filters[0].a0; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[0].a1 = instance->synthesis_bands[i].filters[0].a1; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[0].a2 = instance->synthesis_bands[i].filters[0].a2; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[0].a3 = instance->synthesis_bands[i].filters[0].a3; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[0].a4 = instance->synthesis_bands[i].filters[0].a4; + + for ( i2 = 1; i2 < instance->filters_per_band; ++i2 ) + { + instance->synthesis_bands[i].filters[i2].a0 = instance->synthesis_bands[i].filters[0].a0; + instance->synthesis_bands[i].filters[i2].a1 = instance->synthesis_bands[i].filters[0].a1; + instance->synthesis_bands[i].filters[i2].a2 = instance->synthesis_bands[i].filters[0].a2; + instance->synthesis_bands[i].filters[i2].a3 = instance->synthesis_bands[i].filters[0].a3; + instance->synthesis_bands[i].filters[i2].a4 = instance->synthesis_bands[i].filters[0].a4; + + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2].a0 = instance->synthesis_bands[i].filters[0].a0; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2].a1 = instance->synthesis_bands[i].filters[0].a1; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2].a2 = instance->synthesis_bands[i].filters[0].a2; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2].a3 = instance->synthesis_bands[i].filters[0].a3; + instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2].a4 = instance->synthesis_bands[i].filters[0].a4; + } + } + +} + +/* Initialize the vocoder envelopes. */ +static void voclib_initialize_envelopes ( voclib_instance* instance ) +{ + unsigned char i; + + voclib_envelope_configure ( &instance->analysis_envelopes[0], instance->reaction_time, ( double ) instance->sample_rate ); + for ( i = 1; i < instance->bands; ++i ) + { + instance->analysis_envelopes[i].coef = instance->analysis_envelopes[0].coef; + } +} + +int voclib_initialize ( voclib_instance* instance, unsigned char bands, unsigned char filters_per_band, unsigned int sample_rate, unsigned char carrier_channels ) +{ + if ( !instance ) + { + return 0; + } + if ( bands < 4 || bands > VOCLIB_MAX_BANDS ) + { + return 0; + } + if ( filters_per_band < 1 || filters_per_band > VOCLIB_MAX_FILTERS_PER_BAND ) + { + return 0; + } + if ( sample_rate < 8000 || sample_rate > 192000 ) + { + return 0; + } + if ( carrier_channels < 1 || carrier_channels > 2 ) + { + return 0; + } + + instance->reaction_time = 0.03f; + instance->formant_shift = 1.0f; + instance->sample_rate = sample_rate; + instance->bands = bands; + instance->filters_per_band = filters_per_band; + instance->carrier_channels = carrier_channels; + + voclib_reset_history ( instance ); + voclib_initialize_filterbank ( instance, 0 ); + voclib_initialize_envelopes ( instance ); + + return 1; +} + +void voclib_reset_history ( voclib_instance* instance ) +{ + unsigned char i; + + for ( i = 0; i < instance->bands; ++i ) + { + unsigned char i2; + + for ( i2 = 0; i2 < instance->filters_per_band; ++i2 ) + { + voclib_BiQuad_reset ( &instance->analysis_bands[i].filters[i2] ); + voclib_BiQuad_reset ( &instance->synthesis_bands[i].filters[i2] ); + voclib_BiQuad_reset ( &instance->synthesis_bands[i + VOCLIB_MAX_BANDS].filters[i2] ); + } + voclib_envelope_reset ( &instance->analysis_envelopes[i] ); + } +} + +int voclib_process ( voclib_instance* instance, const float* carrier_buffer, const float* modulator_buffer, float* output_buffer, unsigned int frames ) +{ + unsigned int i; + const unsigned char bands = instance->bands; + const unsigned char filters_per_band = instance->filters_per_band; + + if ( !carrier_buffer ) + { + return 0; + } + if ( !modulator_buffer ) + { + return 0; + } + if ( !output_buffer ) + { + return 0; + } + if ( frames == 0 ) + { + return 0; + } + + if ( instance->carrier_channels == 2 ) + { + + /* The carrier has two channels and the modulator has 1. */ + for ( i = 0; i < frames * 2; i += 2, ++modulator_buffer ) + { + unsigned char i2; + float out_left = 0.0f; + float out_right = 0.0f; + + /* Run the bands in parallel and accumulate the output. */ + for ( i2 = 0; i2 < bands; ++i2 ) + { + unsigned char i3; + float analysis_band = voclib_BiQuad ( *modulator_buffer, &instance->analysis_bands[i2].filters[0] ); + float synthesis_band_left = voclib_BiQuad ( carrier_buffer[i], &instance->synthesis_bands[i2].filters[0] ); + float synthesis_band_right = voclib_BiQuad ( carrier_buffer[i + 1], &instance->synthesis_bands[i2 + VOCLIB_MAX_BANDS].filters[0] ); + + for ( i3 = 1; i3 < filters_per_band; ++i3 ) + { + analysis_band = voclib_BiQuad ( analysis_band, &instance->analysis_bands[i2].filters[i3] ); + synthesis_band_left = voclib_BiQuad ( synthesis_band_left, &instance->synthesis_bands[i2].filters[i3] ); + synthesis_band_right = voclib_BiQuad ( synthesis_band_right, &instance->synthesis_bands[i2 + VOCLIB_MAX_BANDS].filters[i3] ); + } + analysis_band = voclib_envelope_tick ( &instance->analysis_envelopes[i2], analysis_band ); + out_left += synthesis_band_left * analysis_band; + out_right += synthesis_band_right * analysis_band; + } + output_buffer[i] = out_left; + output_buffer[i + 1] = out_right; + } + + } + else + { + + /* Both the carrier and the modulator have a single channel. */ + for ( i = 0; i < frames; ++i ) + { + unsigned char i2; + float out = 0.0f; + + /* Run the bands in parallel and accumulate the output. */ + for ( i2 = 0; i2 < bands; ++i2 ) + { + unsigned char i3; + float analysis_band = voclib_BiQuad ( modulator_buffer[i], &instance->analysis_bands[i2].filters[0] ); + float synthesis_band = voclib_BiQuad ( carrier_buffer[i], &instance->synthesis_bands[i2].filters[0] ); + + for ( i3 = 1; i3 < filters_per_band; ++i3 ) + { + analysis_band = voclib_BiQuad ( analysis_band, &instance->analysis_bands[i2].filters[i3] ); + synthesis_band = voclib_BiQuad ( synthesis_band, &instance->synthesis_bands[i2].filters[i3] ); + } + analysis_band = voclib_envelope_tick ( &instance->analysis_envelopes[i2], analysis_band ); + out += synthesis_band * analysis_band; + } + output_buffer[i] = out; + } + } + + return 1; +} + +int voclib_set_reaction_time ( voclib_instance* instance, float reaction_time ) +{ + if ( reaction_time < 0.002f || reaction_time > 2.0f ) + { + return 0; + } + + instance->reaction_time = reaction_time; + voclib_initialize_envelopes ( instance ); + return 1; +} + +float voclib_get_reaction_time ( const voclib_instance* instance ) +{ + return instance->reaction_time; +} + +int voclib_set_formant_shift ( voclib_instance* instance, float formant_shift ) +{ + if ( formant_shift < 0.25f || formant_shift > 4.0f ) + { + return 0; + } + + instance->formant_shift = formant_shift; + voclib_initialize_filterbank ( instance, 1 ); + return 1; +} + +float voclib_get_formant_shift ( const voclib_instance* instance ) +{ + return instance->formant_shift; +} + +#endif /* VOCLIB_IMPLEMENTATION */ + +/* REVISION HISTORY +* +* Version 1.1 - 2019-02-16 +* Breaking change: Introduced a new argument to voclib_initialize called carrier_channels. This allows the vocoder to output stereo natively. +* Better assignment of band frequencies when using lower sample rates. +* The shell now automatically normalizes the output file to match the peak amplitude in the carrier. +* Fixed a memory corruption bug in the shell which would occur in response to an error condition. +* +* Version 1.0 - 2019-01-27 +* Initial release. +*/ + +/* LICENSE + +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT No Attribution License +Copyright (c) 2019 Philip Bennefall + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/miniaudio/extras/osaudio/README.md b/thirdparty/miniaudio/extras/osaudio/README.md new file mode 100644 index 0000000..7132881 --- /dev/null +++ b/thirdparty/miniaudio/extras/osaudio/README.md @@ -0,0 +1,49 @@ +This is just a little experiment to explore some ideas for the kind of API that I would build if I +was building my own operation system. The name "osaudio" means Operating System Audio. Or maybe you +can think of it as Open Source Audio. It's whatever you want it to be. + +The idea behind this project came about after considering the absurd complexity of audio APIs on +various platforms after years of working on miniaudio. This project aims to disprove the idea that +complete and flexible audio solutions and simple APIs are mutually exclusive and that it's possible +to have both. I challenge anybody to prove me wrong. + +In addition to the above, I also wanted to explore some ideas for a different API design to +miniaudio. miniaudio uses a callback model for data transfer, whereas osaudio uses a blocking +read/write model. + +This project is essentially just a header file with a reference implementation that uses miniaudio +under the hood. You can compile this very easily - just compile osaudio_miniaudio.c, and use +osaudio.h just like any other header. There are no dependencies for the header, and the miniaudio +implementation obviously requires miniaudio. Adjust the include path in osaudio_miniaudio.c if need +be. + +See osaudio.h for full documentation. Below is an example to get you started: + +```c +#include "osaudio.h" + +... + +osaudio_t audio; +osaudio_config_t config; + +osaudio_config_init(&config, OSAUDIO_OUTPUT); +config.format = OSAUDIO_FORMAT_F32; +config.channels = 2; +config.rate = 48000; + +osaudio_open(&audio, &config); + +osaudio_write(audio, myAudioData, frameCount); // <-- This will block until all of the data has been sent to the device. + +osaudio_close(audio); +``` + +Compare the code above with the likes of other APIs like Core Audio and PipeWire. I challenge +anybody to argue their APIs are cleaner and easier to use than this when it comes to simple audio +playback. + +If you have any feedback on this I'd be interested to hear it. In particular, I'd really like to +hear from people who believe the likes of Core Audio (Apple), PipeWire, PulseAudio or any other +audio API actually have good APIs (they don't!) and what makes their's better and/or worse than +this project. diff --git a/thirdparty/miniaudio/extras/osaudio/osaudio.h b/thirdparty/miniaudio/extras/osaudio/osaudio.h new file mode 100644 index 0000000..46907e9 --- /dev/null +++ b/thirdparty/miniaudio/extras/osaudio/osaudio.h @@ -0,0 +1,604 @@ +/* +This is a simple API for low-level audio playback and capture. A reference implementation using +miniaudio is provided in osaudio.c which can be found alongside this file. Consider all code +public domain. + +The idea behind this project came about after considering the absurd complexity of audio APIs on +various platforms after years of working on miniaudio. This project aims to disprove the idea that +complete and flexible audio solutions and simple APIs are mutually exclusive and that it's possible +to have both. The idea of reliability through simplicity is the first and foremost goal of this +project. The difference between this project and miniaudio is that this project is designed around +the idea of what I would build if I was building an audio API for an operating system, such as at +the level of WASAPI or ALSA. A cross-platform and cross-backend library like miniaudio is +necessarily different in design, but there are indeed things that I would have done differently if +given my time again, some of those ideas of which I'm expressing in this project. + +--- + +The concept of low-level audio is simple - you have a device, such as a speaker system or a +micrphone system, and then you write or read audio data to/from it. So in the case of playback, you +need only write your raw audio data to the device which then emits it from the speakers when it's +ready. Likewise, for capture you simply read audio data from the device which is filled with data +by the microphone. + +A complete low-level audio solution requires the following: + + 1) The ability to enumerate devices that are connected to the system. + 2) The ability to open and close a connection to a device. + 3) The ability to start and stop the device. + 4) The ability to write and read audio data to/from the device. + 5) The ability to query the device for it's data configuration. + 6) The ability to notify the application when certain events occur, such as the device being + stopped, or rerouted. + +The API presented here aims to meet all of the above requirements. It uses a single-threaded +blocking read/write model for data delivery instead of a callback model. This makes it a bit more +flexible since it gives the application full control over the audio thread. It might also make it +more feasible to use this API on single-threaded systems. + +Device enumeration is achieved with a single function: osaudio_enumerate(). This function returns +an array of osaudio_info_t structures which contain information about each device. The array is +allocated must be freed with free(). Contained within the osaudio_info_t struct is, most +importantly, the device ID, which is used to open a connection to the device, and the name of the +device which can be used to display to the user. For advanced users, it also includes information +about the device's native data configuration. + +Opening and closing a connection to a device is achieved with osaudio_open() and osaudio_close(). +An important concept is that of the ability to configure the device. This is achieved with the +osaudio_config_t structure which is passed to osaudio_open(). In addition to the ID of the device, +this structure includes information about the desired format, channel count and sample rate. You +can also configure the latency of the device, or the buffer size, which is specified in frames. A +flags member is used for specifying additional options, such as whether or not to disable automatic +rerouting. Finally a callback can be specified for notifications. When osaudio_open() returns, the +config structure will be filled with the device's actual configuration. You can inspect the channel +map from this structure to know how to arrange the channels in your audio data. + +This API uses a blocking write/read model for pushing and pulling data to/from the device. This +is done with the osaudio_write() and osaudio_read() functions. These functions will block until +the requested number of frames have been processed or the device is drained or flushed with +osaudio_drain() or osaudio_flush() respectively. It is from these functions that the device is +started. As soon as you start writing data with osaudio_write() or reading data with +osaudio_read(), the device will start. When the device is drained of flushed with osaudio_drain() +or osaudio_flush(), the device will be stopped. osaudio_drain() will block until the device has +been drained, whereas osaudio_flush() will stop playback immediately and return. You can also pause +and resume the device with osaudio_pause() and osaudio_resume(). Since reading and writing is +blocking, it can be useful to know how many frames can be written/read without blocking. This is +achieved with osaudio_get_avail(). + +Querying the device's configuration is achieved with osaudio_get_info(). This function will return +a pointer to a osaudio_info_t structure which contains information about the device, most +importantly it's name and data configuration. The name is important for displaying on a UI, and +the data configuration is important for knowing how to format your audio data. The osaudio_info_t +structure will contain an array of osaudio_config_t structures. This will contain one entry, which +will contain the exact information that was returned in the config structure that was passed to +osaudio_open(). + +A common requirement is to open a device that represents the operating system's default device. +This is done easily by simply passing in NULL for the device ID. Below is an example for opening a +default device: + + int result; + osaudio_t audio; + osaudio_config_t config; + + osaudio_config_init(&config, OSAUDIO_OUTPUT); + config.format = OSAUDIO_FORMAT_F32; + config.channels = 2; + config.rate = 48000; + + result = osaudio_open(&audio, &config); + if (result != OSAUDIO_SUCCESS) { + printf("Failed to open device."); + return -1; + } + + ... + + osaudio_close(audio); + +In the above example, the default device is opened for playback (OSAUDIO_OUTPUT). The format is +set to 32-bit floating point (OSAUDIO_FORMAT_F32), the channel count is set to stereo (2), and the +sample rate is set to 48kHz. The device is then closed when we're done with it. + +If instead we wanted to open a specific device, we can do that by passing in the device ID. Below +is an example for how to do this: + + int result; + osaudio_t audio; + osaudio_config_t config; + unsigned int infoCount; + osaudio_info_t* info; + + result = osaudio_enumerate(&infoCount, &info); + if (result != OSAUDIO_SUCCESS) { + printf("Failed to enumerate devices.\n"); + return -1; + } + + // ... Iterate over the `info` array and find the device you want to open. Use the `direction` member to discriminate between input and output ... + + osaudio_config_init(&config, OSAUDIO_OUTPUT); + config.id = &info[indexOfYourChosenDevice].id; + config.format = OSAUDIO_FORMAT_F32; + config.channels = 2; + config.rate = 48000; + + osaudio_open(&audio, &config); + + ... + + osaudio_close(audio); + free(info); // The pointer returned by osaudio_enumerate() must be freed with free(). + +The id structure is just a 256 byte array that uniquely identifies the device. Implementations may +have different representations for device IDs, and A 256 byte array should accomodates all +device ID representations. Implementations are required to zero-fill unused bytes. The osaudio_id_t +structure can be copied which makes it suitable for serialization and deserialization in situations +where you may want to save the device ID to permanent storage so it can be stored in a config file. + +Implementations need to do their own data conversion between the device's native data configuration +and the requested configuration. In this case, when the format, channels and rate are specified in +the config, they should be unchanged when osaudio_open() returns. If this is not possible, +osaudio_open() will return OSAUDIO_FORMAT_NOT_SUPPORTED. However, there are cases where it's useful +for a program to use the device's native configuration instead of some fixed configuration. This is +achieved by setting the format, channels and rate to 0. Below is an example: + + int result; + osaudio_t audio; + osaudio_config_t config; + + osaudio_config_init(&config, OSAUDIO_OUTPUT); + + result = osaudio_open(&audio, &config); + if (result != OSAUDIO_SUCCESS) { + printf("Failed to open device."); + return -1; + } + + // ... `config` will have been updated by osaudio_open() to contain the *actual* format/channels/rate ... + + osaudio_close(audio); + +In addition to the code above, you can explicitly call `osaudio_get_info()` to retrieve the format +configuration. If you need to know the native configuration before opening the device, you can use +enumeration. The format, channels and rate will be contined in the first item in the configs array. + +The examples above all use playback, but the same applies for capture. The only difference is that +the direction is set to OSAUDIO_INPUT instead of OSAUDIO_OUTPUT. + +To output audio from the speakers you need to call osaudio_write(). Likewise, to capture audio from +a microphone you need to call osaudio_read(). These functions will block until the requested number +of frames have been written or read. The device will start automatically. Below is an example for +writing some data to a device: + + int result = osaudio_write(audio, myAudioData, myAudioDataFrameCount); + if (result == OSAUDIO_SUCCESS) { + printf("Successfully wrote %d frames of audio data.\n", myAudioDataFrameCount); + } else { + printf("Failed to write audio data.\n"); + } + +osaudio_write() and osaudio_read() will return OSAUDIO_SUCCESS if the requested number of frames +were written or read. You cannot call osaudio_close() while a write or read operation is in +progress. + +If you want to write or read audio data without blocking, you can use osaudio_get_avail() to +determine how many frames are available for writing or reading. Below is an example: + + unsigned int framesAvailable = osaudio_get_avail(audio); + if (result > 0) { + printf("There are %d frames available for writing.\n", framesAvailable); + } else { + printf("There are no frames available for writing.\n"); + } + +If you want to abort a blocking write or read, you can use osaudio_flush(). This will result in any +pending write or read operation being aborted. + +There are several ways of pausing a device. The first is to just drain or flush the device and +simply don't do any more read/write operations. A drain and flush will put the device into a +stopped state until the next call to either read or write, depending on the device's direction. +If, however, this does not suit your requirements, you can use osaudio_pause() and +osaudio_resume(). Take note, however, that these functions will result in osaudio_drain() never +returning because it'll result in the device being in a stopped state which in turn results in the +buffer never being read and therefore never drained. + +Everything is thread safe with a few minor exceptions which has no practical issues for the client: + + * You cannot call any function while osaudio_open() is still in progress. + * You cannot call osaudio_close() while any other function is still in progress. + * You can only call osaudio_write() and osaudio_read() from one thread at a time. + +None of these issues should be a problem for the client in practice. You won't have a valid +osaudio_t object until osaudio_open() has returned. For osaudio_close(), it makes no sense to +destroy the object while it's still in use, and doing so would mean the client is using very poor +form. For osaudio_write() and osaudio_read(), you wouldn't ever want to call this simultaneously +across multiple threads anyway because otherwise you'd end up with garbage audio. + +The rules above only apply when working with a single osaudio_t object. You can have multiple +osaudio_t objects open at the same time, and you can call any function on different osaudio_t +objects simultaneously from different threads. + +--- + +# Feedback + +I'm looking for feedback on the following: + + * Are the supported formats enough? If not, what other formats are needed, and what is the + justification for including it? Just because it's the native format on one particular + piece of hardware is not enough. Big-endian and little-endian will never be supported. All + formats are native-endian. + * Are the available channel positions enough? What other positions are needed? + * Just some general criticism would be appreciated. + +*/ +#ifndef osaudio_h +#define osaudio_h + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Support far pointers on relevant platforms (DOS, in particular). The version of this file +distributed with an operating system wouldn't need this because they would just have an +OS-specific version of this file, but as a reference it's useful to use far pointers here. +*/ +#if defined(__MSDOS__) || defined(_MSDOS) || defined(__DOS__) + #define OSAUDIO_FAR far +#else + #define OSAUDIO_FAR +#endif + +typedef struct _osaudio_t* osaudio_t; +typedef struct osaudio_config_t osaudio_config_t; +typedef struct osaudio_id_t osaudio_id_t; +typedef struct osaudio_info_t osaudio_info_t; +typedef struct osaudio_notification_t osaudio_notification_t; + +/* Results codes. */ +typedef int osaudio_result_t; +#define OSAUDIO_SUCCESS 0 +#define OSAUDIO_ERROR -1 +#define OSAUDIO_INVALID_ARGS -2 +#define OSAUDIO_INVALID_OPERATION -3 +#define OSAUDIO_OUT_OF_MEMORY -4 +#define OSAUDIO_FORMAT_NOT_SUPPORTED -101 /* The requested format is not supported. */ +#define OSAUDIO_XRUN -102 /* An underrun or overrun occurred. Can be returned by osaudio_read() or osaudio_write(). */ +#define OSAUDIO_DEVICE_STOPPED -103 /* The device is stopped. Can be returned by osaudio_drain(). It is invalid to call osaudio_drain() on a device that is not running because otherwise it'll get stuck. */ + +/* Directions. Cannot be combined. Use separate osaudio_t objects for birectional setups. */ +typedef int osaudio_direction_t; +#define OSAUDIO_INPUT 1 +#define OSAUDIO_OUTPUT 2 + +/* All formats are native endian and interleaved. */ +typedef int osaudio_format_t; +#define OSAUDIO_FORMAT_UNKNOWN 0 +#define OSAUDIO_FORMAT_F32 1 +#define OSAUDIO_FORMAT_U8 2 +#define OSAUDIO_FORMAT_S16 3 +#define OSAUDIO_FORMAT_S24 4 /* Tightly packed. */ +#define OSAUDIO_FORMAT_S32 5 + +/* Channel positions. */ +typedef unsigned char osaudio_channel_t; +#define OSAUDIO_CHANNEL_NONE 0 +#define OSAUDIO_CHANNEL_MONO 1 +#define OSAUDIO_CHANNEL_FL 2 +#define OSAUDIO_CHANNEL_FR 3 +#define OSAUDIO_CHANNEL_FC 4 +#define OSAUDIO_CHANNEL_LFE 5 +#define OSAUDIO_CHANNEL_BL 6 +#define OSAUDIO_CHANNEL_BR 7 +#define OSAUDIO_CHANNEL_FLC 8 +#define OSAUDIO_CHANNEL_FRC 9 +#define OSAUDIO_CHANNEL_BC 10 +#define OSAUDIO_CHANNEL_SL 11 +#define OSAUDIO_CHANNEL_SR 12 +#define OSAUDIO_CHANNEL_TC 13 +#define OSAUDIO_CHANNEL_TFL 14 +#define OSAUDIO_CHANNEL_TFC 15 +#define OSAUDIO_CHANNEL_TFR 16 +#define OSAUDIO_CHANNEL_TBL 17 +#define OSAUDIO_CHANNEL_TBC 18 +#define OSAUDIO_CHANNEL_TBR 19 +#define OSAUDIO_CHANNEL_AUX0 20 +#define OSAUDIO_CHANNEL_AUX1 21 +#define OSAUDIO_CHANNEL_AUX2 22 +#define OSAUDIO_CHANNEL_AUX3 23 +#define OSAUDIO_CHANNEL_AUX4 24 +#define OSAUDIO_CHANNEL_AUX5 25 +#define OSAUDIO_CHANNEL_AUX6 26 +#define OSAUDIO_CHANNEL_AUX7 27 +#define OSAUDIO_CHANNEL_AUX8 28 +#define OSAUDIO_CHANNEL_AUX9 29 +#define OSAUDIO_CHANNEL_AUX10 30 +#define OSAUDIO_CHANNEL_AUX11 31 +#define OSAUDIO_CHANNEL_AUX12 32 +#define OSAUDIO_CHANNEL_AUX13 33 +#define OSAUDIO_CHANNEL_AUX14 34 +#define OSAUDIO_CHANNEL_AUX15 35 +#define OSAUDIO_CHANNEL_AUX16 36 +#define OSAUDIO_CHANNEL_AUX17 37 +#define OSAUDIO_CHANNEL_AUX18 38 +#define OSAUDIO_CHANNEL_AUX19 39 +#define OSAUDIO_CHANNEL_AUX20 40 +#define OSAUDIO_CHANNEL_AUX21 41 +#define OSAUDIO_CHANNEL_AUX22 42 +#define OSAUDIO_CHANNEL_AUX23 43 +#define OSAUDIO_CHANNEL_AUX24 44 +#define OSAUDIO_CHANNEL_AUX25 45 +#define OSAUDIO_CHANNEL_AUX26 46 +#define OSAUDIO_CHANNEL_AUX27 47 +#define OSAUDIO_CHANNEL_AUX28 48 +#define OSAUDIO_CHANNEL_AUX29 49 +#define OSAUDIO_CHANNEL_AUX30 50 +#define OSAUDIO_CHANNEL_AUX31 51 + +/* The maximum number of channels supported. */ +#define OSAUDIO_MAX_CHANNELS 64 + +/* Notification types. */ +typedef int osaudio_notification_type_t; +#define OSAUDIO_NOTIFICATION_STARTED 0 /* The device was started in response to a call to osaudio_write() or osaudio_read(). */ +#define OSAUDIO_NOTIFICATION_STOPPED 1 /* The device was stopped in response to a call to osaudio_drain() or osaudio_flush(). */ +#define OSAUDIO_NOTIFICATION_REROUTED 2 /* The device was rerouted. Not all implementations need to support rerouting. */ +#define OSAUDIO_NOTIFICATION_INTERRUPTION_BEGIN 3 /* The device was interrupted due to something like a phone call. */ +#define OSAUDIO_NOTIFICATION_INTERRUPTION_END 4 /* The interruption has been ended. */ + +/* Flags. */ +#define OSAUDIO_FLAG_NO_REROUTING 1 /* When set, will tell the implementation to disable automatic rerouting if possible. This is a hint and may be ignored by the implementation. */ +#define OSAUDIO_FLAG_REPORT_XRUN 2 /* When set, will tell the implementation to report underruns and overruns via osaudio_write() and osaudio_read() by aborting and returning OSAUDIO_XRUN. */ + +struct osaudio_notification_t +{ + osaudio_notification_type_t type; /* OSAUDIO_NOTIFICATION_* */ + union + { + struct + { + int _unused; + } started; + struct + { + int _unused; + } stopped; + struct + { + int _unused; + } rerouted; + struct + { + int _unused; + } interruption; + } data; +}; + +struct osaudio_id_t +{ + char data[256]; +}; + +struct osaudio_config_t +{ + osaudio_id_t* device_id; /* Set to NULL to use default device. When non-null, automatic routing will be disabled. */ + osaudio_direction_t direction; /* OSAUDIO_INPUT or OSAUDIO_OUTPUT. Cannot be combined. Use separate osaudio_t objects for bidirectional setups. */ + osaudio_format_t format; /* OSAUDIO_FORMAT_* */ + unsigned int channels; /* Number of channels. */ + unsigned int rate; /* Sample rate in seconds. */ + osaudio_channel_t channel_map[OSAUDIO_MAX_CHANNELS]; /* Leave all items set to 0 for defaults. */ + unsigned int buffer_size; /* In frames. Set to 0 to use the system default. */ + unsigned int flags; /* A combination of OSAUDIO_FLAG_* */ + void (* notification)(void* user_data, const osaudio_notification_t* notification); /* Called when some kind of event occurs, such as a device being closed. Never called from the audio thread. */ + void* user_data; /* Passed to notification(). */ +}; + +struct osaudio_info_t +{ + osaudio_id_t id; + char name[256]; + osaudio_direction_t direction; /* OSAUDIO_INPUT or OSAUDIO_OUTPUT. */ + unsigned int config_count; + osaudio_config_t* configs; +}; + + +/* +Enumerates the available devices. + +On output, `count` will contain the number of items in the `info` array. The array must be freed +with free() when it's no longer needed. + +Use the `direction` member to discriminate between input and output devices. Below is an example: + + unsigned int count; + osaudio_info_t* info; + osaudio_enumerate(&count, &info); + + for (int i = 0; i < count; ++i) { + if (info[i].direction == OSAUDIO_OUTPUT) { + printf("Output device: %s\n", info[i].name); + } else { + printf("Input device: %s\n", info[i].name); + } + } + +You can use the `id` member to open a specific device with osaudio_open(). You do not need to do +device enumeration if you only want to open the default device. +*/ +osaudio_result_t osaudio_enumerate(unsigned int* count, osaudio_info_t** info); + +/* +Initializes a default config. + +The config object will be cleared to zero, with the direction set to `direction`. This will result +in a configuration that uses the device's native format, channels and rate. + +osaudio_config_t is a transparent struct. Just set the relevant fields to the desired values after +calling this function. Example: + + osaudio_config_t config; + osaudio_config_init(&config, OSAUDIO_OUTPUT); + config.format = OSAUDIO_FORMAT_F32; + config.channels = 2; + config.rate = 48000; +*/ +void osaudio_config_init(osaudio_config_t* config, osaudio_direction_t direction); + +/* +Opens a connection to a device. + +On input, config must be filled with the desired configuration. On output, it will be filled with +the actual configuration. + +Initialize the config with osaudio_config_init() and then fill in the desired configuration. Below +is an example: + + osaudio_config_t config; + osaudio_config_init(&config, OSAUDIO_OUTPUT); + config.format = OSAUDIO_FORMAT_F32; + config.channels = 2; + config.rate = 48000; + +When the format, channels or rate are left at their default values, or set to 0 (or +OSAUDIO_FORMAT_UNKNOWN for format), the native format, channels or rate will use the device's +native configuration: + + osaudio_config_t config; + osaudio_config_init(&config, OSAUDIO_OUTPUT); + config.format = OSAUDIO_FORMAT_UNKNOWN; + config.channels = 0; + config.rate = 0; + +The code above is equivalent to this: + + osaudio_config_t config; + osaudio_config_init(&config, OSAUDIO_OUTPUT); + +On output the config will be filled with the actual configuration. The implementation will perform +any necessary data conversion between the requested data configuration and the device's native +configuration. If it cannot, the function will return a OSAUDIO_FORMAT_NOT_SUPPORTED error. In this +case the caller can decide to reinitialize the device to use it's native configuration and do it's +own data conversion, or abort if it cannot do so. Use the channel map to determine the ordering of +your channels. Automatic channel map conversion is not performed - that must be done manually by +the caller when transfering data to/from the device. + +Close the device with osaudio_close(). + +Returns 0 on success, any other error code on failure. +*/ +osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config); + +/* +Closes a connection to a device. + +As soon as this function is called, the device should be considered invalid and unsuable. Do not +attempt to use the audio object once this function has been called. + +It's invalid to call this while any other function is still running. You can use osaudio_flush() to +quickly abort any pending writes or reads. You can also use osaudio_drain() to wait for all pending +writes or reads to complete. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_close(osaudio_t audio); + +/* +Writes audio data to the device. + +This will block until all data has been written or the device is closed. + +You can only write from a single thread at any given time. If you want to write from multiple +threads, you need to use your own synchronization mechanism. + +This will automatically start the device if frame_count is > 0 and it's not in a paused state. + +Use osaudio_get_avail() to determine how much data can be written without blocking. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_write(osaudio_t audio, const void OSAUDIO_FAR* data, unsigned int frame_count); + +/* +Reads audio data from the device. + +This will block until the requested number of frames has been read or the device is closed. + +You can only read from a single thread at any given time. If you want to read from multiple +threads, you need to use your own synchronization mechanism. + +This will automatically start the device if frame_count is > 0 and it's not in a paused state. + +Use osaudio_get_avail() to determine how much data can be read without blocking. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_read(osaudio_t audio, void OSAUDIO_FAR* data, unsigned int frame_count); + +/* +Drains the device. + +This will block until all pending reads or writes have completed. + +If after calling this function another call to osaudio_write() or osaudio_read() is made, the +device will be resumed like normal. + +It is invalid to call this while the device is paused. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_drain(osaudio_t audio); + +/* +Flushes the device. + +This will immediately flush any pending reads or writes. It will not block. Any in-progress reads +or writes will return immediately. + +If after calling this function another thread starts reading or writing, the device will be resumed +like normal. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_flush(osaudio_t audio); + +/* +Pauses or resumes the device. + +Pausing a device will trigger a OSAUDIO_NOTIFICATION_STOPPED notification. Resuming a device will +trigger a OSAUDIO_NOTIFICATION_STARTED notification. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_pause(osaudio_t audio); + +/* +Resumes the device. + +Returns 0 on success, < 0 on failure. +*/ +osaudio_result_t osaudio_resume(osaudio_t audio); + +/* +Returns the number of frames that can be read or written without blocking. +*/ +unsigned int osaudio_get_avail(osaudio_t audio); + +/* +Gets information about the device. + +There will be one item in the configs array which will contain the device's current configuration, +the contents of which will match that of the config that was returned by osaudio_open(). + +Returns NULL on failure. Do not free the returned pointer. It's up to the implementation to manage +the meory of this object. +*/ +const osaudio_info_t* osaudio_get_info(osaudio_t audio); + + +#ifdef __cplusplus +} +#endif +#endif /* osaudio_h */ diff --git a/thirdparty/miniaudio/extras/osaudio/osaudio_miniaudio.c b/thirdparty/miniaudio/extras/osaudio/osaudio_miniaudio.c new file mode 100644 index 0000000..a99a523 --- /dev/null +++ b/thirdparty/miniaudio/extras/osaudio/osaudio_miniaudio.c @@ -0,0 +1,948 @@ +/* +Consider this a reference implementation of osaudio. It uses miniaudio under the hood. You can add +this file directly to your source tree, but you may need to update the miniaudio path. + +This will use a mutex in osaudio_read() and osaudio_write(). It's a low-contention lock that's only +used for the purpose of osaudio_drain(), but it's still a lock nonetheless. I'm not worrying about +this too much right now because this is just an example implementation, but I might improve on this +at a later date. +*/ +#ifndef osaudio_miniaudio_c +#define osaudio_miniaudio_c + +#include "osaudio.h" + +/* +If you would rather define your own implementation of miniaudio, define OSAUDIO_NO_MINIAUDIO_IMPLEMENTATION. If you do this, +you need to make sure you include the implmeentation before osaudio.c. This would only really be useful if you are wanting +to do a unity build which uses other parts of miniaudio that this file is currently excluding. +*/ +#ifndef OSAUDIO_NO_MINIAUDIO_IMPLEMENTATION +#define MA_API static +#define MA_NO_DECODING +#define MA_NO_ENCODING +#define MA_NO_RESOURCE_MANAGER +#define MA_NO_NODE_GRAPH +#define MA_NO_ENGINE +#define MA_NO_GENERATION +#define MINIAUDIO_IMPLEMENTATION +#include "../../miniaudio.h" +#endif + +struct _osaudio_t +{ + ma_device device; + osaudio_info_t info; + osaudio_config_t config; /* info.configs will point to this. */ + ma_pcm_rb buffer; + ma_semaphore bufferSemaphore; /* The semaphore for controlling access to the buffer. The audio thread will release the semaphore. The read and write functions will wait on it. */ + ma_atomic_bool32 isActive; /* Starts off as false. Set to true when config.buffer_size data has been written in the case of playback, or as soon as osaudio_read() is called in the case of capture. */ + ma_atomic_bool32 isPaused; + ma_atomic_bool32 isFlushed; /* When set, activation of the device will flush any data that's currently in the buffer. Defaults to false, and will be set to true in osaudio_drain() and osaudio_flush(). */ + ma_atomic_bool32 xrunDetected; /* Used for detecting when an xrun has occurred and returning from osaudio_read/write() when OSAUDIO_FLAG_REPORT_XRUN is enabled. */ + ma_spinlock activateLock; /* Used for starting and stopping the device. Needed because two variables control this - isActive and isPaused. */ + ma_mutex drainLock; /* Used for osaudio_drain(). For mutal exclusion between drain() and read()/write(). Technically results in a lock in read()/write(), but not overthinking that since this is just a reference for now. */ +}; + + +static ma_bool32 osaudio_g_is_backend_known = MA_FALSE; +static ma_backend osaudio_g_backend = ma_backend_wasapi; +static ma_context osaudio_g_context; +static ma_mutex osaudio_g_context_lock; /* Only used for device enumeration. Created and destroyed with our context. */ +static ma_uint32 osaudio_g_refcount = 0; +static ma_spinlock osaudio_g_lock = 0; + + +static osaudio_result_t osaudio_result_from_miniaudio(ma_result result) +{ + switch (result) + { + case MA_SUCCESS: return OSAUDIO_SUCCESS; + case MA_INVALID_ARGS: return OSAUDIO_INVALID_ARGS; + case MA_INVALID_OPERATION: return OSAUDIO_INVALID_OPERATION; + case MA_OUT_OF_MEMORY: return OSAUDIO_OUT_OF_MEMORY; + default: return OSAUDIO_ERROR; + } +} + +static ma_format osaudio_format_to_miniaudio(osaudio_format_t format) +{ + switch (format) + { + case OSAUDIO_FORMAT_F32: return ma_format_f32; + case OSAUDIO_FORMAT_U8: return ma_format_u8; + case OSAUDIO_FORMAT_S16: return ma_format_s16; + case OSAUDIO_FORMAT_S24: return ma_format_s24; + case OSAUDIO_FORMAT_S32: return ma_format_s32; + default: return ma_format_unknown; + } +} + +static osaudio_format_t osaudio_format_from_miniaudio(ma_format format) +{ + switch (format) + { + case ma_format_f32: return OSAUDIO_FORMAT_F32; + case ma_format_u8: return OSAUDIO_FORMAT_U8; + case ma_format_s16: return OSAUDIO_FORMAT_S16; + case ma_format_s24: return OSAUDIO_FORMAT_S24; + case ma_format_s32: return OSAUDIO_FORMAT_S32; + default: return OSAUDIO_FORMAT_UNKNOWN; + } +} + + +static osaudio_channel_t osaudio_channel_from_miniaudio(ma_channel channel) +{ + /* Channel positions between here and miniaudio will remain in sync. */ + return (osaudio_channel_t)channel; +} + +static ma_channel osaudio_channel_to_miniaudio(osaudio_channel_t channel) +{ + /* Channel positions between here and miniaudio will remain in sync. */ + return (ma_channel)channel; +} + + +static void osaudio_dummy_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + (void)pDevice; + (void)pOutput; + (void)pInput; + (void)frameCount; +} + +static osaudio_result_t osaudio_determine_miniaudio_backend(ma_backend* pBackend, ma_device* pDummyDevice) +{ + ma_device dummyDevice; + ma_device_config dummyDeviceConfig; + ma_result result; + + /* + To do this we initialize a dummy device. We allow the caller to make use of this device as an optimization. This is + only used by osaudio_enumerate_devices() because that can make use of the context from the dummy device rather than + having to create it's own. pDummyDevice can be null. + */ + if (pDummyDevice == NULL) { + pDummyDevice = &dummyDevice; + } + + dummyDeviceConfig = ma_device_config_init(ma_device_type_playback); + dummyDeviceConfig.dataCallback = osaudio_dummy_data_callback; + + result = ma_device_init(NULL, &dummyDeviceConfig, pDummyDevice); + if (result != MA_SUCCESS || pDummyDevice->pContext->backend == ma_backend_null) { + /* Failed to open a default playback device. Try capture. */ + if (result == MA_SUCCESS) { + /* This means we successfully initialize a device, but it's backend is null. It could be that there's no playback devices attached. Try capture. */ + ma_device_uninit(pDummyDevice); + } + + dummyDeviceConfig = ma_device_config_init(ma_device_type_capture); + result = ma_device_init(NULL, &dummyDeviceConfig, pDummyDevice); + } + + if (result != MA_SUCCESS) { + return osaudio_result_from_miniaudio(result); + } + + *pBackend = pDummyDevice->pContext->backend; + + /* We're done. */ + if (pDummyDevice == &dummyDevice) { + ma_device_uninit(&dummyDevice); + } + + return OSAUDIO_SUCCESS; +} + +static osaudio_result_t osaudio_ref_context_nolock() +{ + /* Initialize the global context if necessary. */ + if (osaudio_g_refcount == 0) { + osaudio_result_t result; + + /* If we haven't got a known context, we'll need to determine it here. */ + if (osaudio_g_is_backend_known == MA_FALSE) { + result = osaudio_determine_miniaudio_backend(&osaudio_g_backend, NULL); + if (result != OSAUDIO_SUCCESS) { + return result; + } + } + + result = osaudio_result_from_miniaudio(ma_context_init(&osaudio_g_backend, 1, NULL, &osaudio_g_context)); + if (result != OSAUDIO_SUCCESS) { + return result; + } + + /* Need a mutex for device enumeration. */ + ma_mutex_init(&osaudio_g_context_lock); + } + + osaudio_g_refcount += 1; + + return OSAUDIO_SUCCESS; +} + +static osaudio_result_t osaudio_unref_context_nolock() +{ + if (osaudio_g_refcount == 0) { + return OSAUDIO_INVALID_OPERATION; + } + + osaudio_g_refcount -= 1; + + /* Uninitialize the context if we don't have any more references. */ + if (osaudio_g_refcount == 0) { + ma_context_uninit(&osaudio_g_context); + ma_mutex_uninit(&osaudio_g_context_lock); + } + + return OSAUDIO_SUCCESS; +} + +static ma_context* osaudio_ref_context() +{ + osaudio_result_t result; + + ma_spinlock_lock(&osaudio_g_lock); + { + result = osaudio_ref_context_nolock(); + } + ma_spinlock_unlock(&osaudio_g_lock); + + if (result != OSAUDIO_SUCCESS) { + return NULL; + } + + return &osaudio_g_context; +} + +static osaudio_result_t osaudio_unref_context() +{ + osaudio_result_t result; + + ma_spinlock_lock(&osaudio_g_lock); + { + result = osaudio_unref_context_nolock(); + } + ma_spinlock_unlock(&osaudio_g_lock); + + return result; +} + + +static void osaudio_info_from_miniaudio(osaudio_info_t* info, const ma_device_info* infoMA) +{ + unsigned int iNativeConfig; + + /* It just so happens, by absolutely total coincidence, that the size of the ID and name are the same between here and miniaudio. What are the odds?! */ + memcpy(info->id.data, &infoMA->id, sizeof(info->id.data)); + memcpy(info->name, infoMA->name, sizeof(info->name)); + + info->config_count = (unsigned int)infoMA->nativeDataFormatCount; + for (iNativeConfig = 0; iNativeConfig < info->config_count; iNativeConfig += 1) { + unsigned int iChannel; + + info->configs[iNativeConfig].device_id = &info->id; + info->configs[iNativeConfig].direction = info->direction; + info->configs[iNativeConfig].format = osaudio_format_from_miniaudio(infoMA->nativeDataFormats[iNativeConfig].format); + info->configs[iNativeConfig].channels = (unsigned int)infoMA->nativeDataFormats[iNativeConfig].channels; + info->configs[iNativeConfig].rate = (unsigned int)infoMA->nativeDataFormats[iNativeConfig].sampleRate; + + /* Apparently miniaudio does not report channel positions. I don't know why I'm not doing that. */ + for (iChannel = 0; iChannel < info->configs[iNativeConfig].channels; iChannel += 1) { + info->configs[iNativeConfig].channel_map[iChannel] = OSAUDIO_CHANNEL_NONE; + } + } +} + +static osaudio_result_t osaudio_enumerate_nolock(unsigned int* count, osaudio_info_t** info, ma_context* pContext) +{ + osaudio_result_t result; + ma_device_info* pPlaybackInfos; + ma_uint32 playbackCount; + ma_device_info* pCaptureInfos; + ma_uint32 captureCount; + ma_uint32 iInfo; + size_t allocSize; + osaudio_info_t* pRunningInfo; + osaudio_config_t* pRunningConfig; + + /* We now need to retrieve the device information from miniaudio. */ + result = osaudio_result_from_miniaudio(ma_context_get_devices(pContext, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount)); + if (result != OSAUDIO_SUCCESS) { + osaudio_unref_context(); + return result; + } + + /* + Because the caller needs to free the returned pointer it's important that we keep it all in one allocation. Because there can be + a variable number of native configs we'll have to compute the size of the allocation first, and then do a second pass to fill + out the data. + */ + allocSize = ((size_t)playbackCount + (size_t)captureCount) * sizeof(osaudio_info_t); + + /* Now we need to iterate over each playback and capture device and add up the number of native configs. */ + for (iInfo = 0; iInfo < playbackCount; iInfo += 1) { + ma_context_get_device_info(pContext, ma_device_type_playback, &pPlaybackInfos[iInfo].id, &pPlaybackInfos[iInfo]); + allocSize += pPlaybackInfos[iInfo].nativeDataFormatCount * sizeof(osaudio_config_t); + } + for (iInfo = 0; iInfo < captureCount; iInfo += 1) { + ma_context_get_device_info(pContext, ma_device_type_capture, &pCaptureInfos[iInfo].id, &pCaptureInfos[iInfo]); + allocSize += pCaptureInfos[iInfo].nativeDataFormatCount * sizeof(osaudio_config_t); + } + + /* Now that we know the size of the allocation we can allocate it. */ + *info = (osaudio_info_t*)calloc(1, allocSize); + if (*info == NULL) { + osaudio_unref_context(); + return OSAUDIO_OUT_OF_MEMORY; + } + + pRunningInfo = *info; + pRunningConfig = (osaudio_config_t*)(((unsigned char*)*info) + (((size_t)playbackCount + (size_t)captureCount) * sizeof(osaudio_info_t))); + + for (iInfo = 0; iInfo < playbackCount; iInfo += 1) { + pRunningInfo->direction = OSAUDIO_OUTPUT; + pRunningInfo->configs = pRunningConfig; + osaudio_info_from_miniaudio(pRunningInfo, &pPlaybackInfos[iInfo]); + + pRunningConfig += pRunningInfo->config_count; + pRunningInfo += 1; + } + + for (iInfo = 0; iInfo < captureCount; iInfo += 1) { + pRunningInfo->direction = OSAUDIO_INPUT; + pRunningInfo->configs = pRunningConfig; + osaudio_info_from_miniaudio(pRunningInfo, &pPlaybackInfos[iInfo]); + + pRunningConfig += pRunningInfo->config_count; + pRunningInfo += 1; + } + + *count = (unsigned int)(playbackCount + captureCount); + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_enumerate(unsigned int* count, osaudio_info_t** info) +{ + osaudio_result_t result; + ma_context* pContext = NULL; + + if (count != NULL) { + *count = 0; + } + if (info != NULL) { + *info = NULL; + } + + if (count == NULL || info == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + pContext = osaudio_ref_context(); + if (pContext == NULL) { + return OSAUDIO_ERROR; + } + + ma_mutex_lock(&osaudio_g_context_lock); + { + result = osaudio_enumerate_nolock(count, info, pContext); + } + ma_mutex_unlock(&osaudio_g_context_lock); + + /* We're done. We can now return. */ + osaudio_unref_context(); + return result; +} + + +void osaudio_config_init(osaudio_config_t* config, osaudio_direction_t direction) +{ + if (config == NULL) { + return; + } + + memset(config, 0, sizeof(*config)); + config->direction = direction; +} + + +static void osaudio_data_callback_playback(osaudio_t audio, void* pOutput, ma_uint32 frameCount) +{ + /* + If there's content in the buffer, read from it and release the semaphore. There needs to be a whole frameCount chunk + in the buffer so we can keep everything in nice clean chunks. When we read from the buffer, we release a semaphore + which will allow the main thread to write more data to the buffer. + */ + ma_uint32 framesToRead; + ma_uint32 framesProcessed; + void* pBuffer; + + framesToRead = ma_pcm_rb_available_read(&audio->buffer); + if (framesToRead > frameCount) { + framesToRead = frameCount; + } + + framesProcessed = framesToRead; + + /* For robustness we should run this in a loop in case the buffer wraps around. */ + while (frameCount > 0) { + framesToRead = frameCount; + + ma_pcm_rb_acquire_read(&audio->buffer, &framesToRead, &pBuffer); + if (framesToRead == 0) { + break; + } + + memcpy(pOutput, pBuffer, framesToRead * ma_get_bytes_per_frame(audio->device.playback.format, audio->device.playback.channels)); + ma_pcm_rb_commit_read(&audio->buffer, framesToRead); + + frameCount -= framesToRead; + pOutput = ((unsigned char*)pOutput) + (framesToRead * ma_get_bytes_per_frame(audio->device.playback.format, audio->device.playback.channels)); + } + + /* Make sure we release the semaphore if we ended up reading anything. */ + if (framesProcessed > 0) { + ma_semaphore_release(&audio->bufferSemaphore); + } + + if (frameCount > 0) { + /* Underrun. Pad with silence. */ + ma_silence_pcm_frames(pOutput, frameCount, audio->device.playback.format, audio->device.playback.channels); + ma_atomic_bool32_set(&audio->xrunDetected, MA_TRUE); + } +} + +static void osaudio_data_callback_capture(osaudio_t audio, const void* pInput, ma_uint32 frameCount) +{ + /* If there's space in the buffer, write to it and release the semaphore. The semaphore is only released on full-chunk boundaries. */ + ma_uint32 framesToWrite; + ma_uint32 framesProcessed; + void* pBuffer; + + framesToWrite = ma_pcm_rb_available_write(&audio->buffer); + if (framesToWrite > frameCount) { + framesToWrite = frameCount; + } + + framesProcessed = framesToWrite; + + while (frameCount > 0) { + framesToWrite = frameCount; + + ma_pcm_rb_acquire_write(&audio->buffer, &framesToWrite, &pBuffer); + if (framesToWrite == 0) { + break; + } + + memcpy(pBuffer, pInput, framesToWrite * ma_get_bytes_per_frame(audio->device.capture.format, audio->device.capture.channels)); + ma_pcm_rb_commit_write(&audio->buffer, framesToWrite); + + frameCount -= framesToWrite; + pInput = ((unsigned char*)pInput) + (framesToWrite * ma_get_bytes_per_frame(audio->device.capture.format, audio->device.capture.channels)); + } + + /* Make sure we release the semaphore if we ended up reading anything. */ + if (framesProcessed > 0) { + ma_semaphore_release(&audio->bufferSemaphore); + } + + if (frameCount > 0) { + /* Overrun. Not enough room to move our input data into the buffer. */ + ma_atomic_bool32_set(&audio->xrunDetected, MA_TRUE); + } +} + +static void osaudio_nofication_callback(const ma_device_notification* pNotification) +{ + osaudio_t audio = (osaudio_t)pNotification->pDevice->pUserData; + + if (audio->config.notification != NULL) { + osaudio_notification_t notification; + + switch (pNotification->type) + { + case ma_device_notification_type_started: + { + notification.type = OSAUDIO_NOTIFICATION_STARTED; + } break; + case ma_device_notification_type_stopped: + { + notification.type = OSAUDIO_NOTIFICATION_STOPPED; + } break; + case ma_device_notification_type_rerouted: + { + notification.type = OSAUDIO_NOTIFICATION_REROUTED; + } break; + case ma_device_notification_type_interruption_began: + { + notification.type = OSAUDIO_NOTIFICATION_INTERRUPTION_BEGIN; + } break; + case ma_device_notification_type_interruption_ended: + { + notification.type = OSAUDIO_NOTIFICATION_INTERRUPTION_END; + } break; + } + + audio->config.notification(audio->config.user_data, ¬ification); + } +} + +static void osaudio_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + osaudio_t audio = (osaudio_t)pDevice->pUserData; + + if (audio->info.direction == OSAUDIO_OUTPUT) { + osaudio_data_callback_playback(audio, pOutput, frameCount); + } else { + osaudio_data_callback_capture(audio, pInput, frameCount); + } +} + +osaudio_result_t osaudio_open(osaudio_t* audio, osaudio_config_t* config) +{ + osaudio_result_t result; + ma_context* pContext = NULL; + ma_device_config deviceConfig; + ma_device_info deviceInfo; + int periodCount = 2; + unsigned int iChannel; + + if (audio != NULL) { + *audio = NULL; /* Safety. */ + } + + if (audio == NULL || config == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + pContext = osaudio_ref_context(); /* Will be unreferenced in osaudio_close(). */ + if (pContext == NULL) { + return OSAUDIO_ERROR; + } + + *audio = (osaudio_t)calloc(1, sizeof(**audio)); + if (*audio == NULL) { + osaudio_unref_context(); + return OSAUDIO_OUT_OF_MEMORY; + } + + if (config->direction == OSAUDIO_OUTPUT) { + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = osaudio_format_to_miniaudio(config->format); + deviceConfig.playback.channels = (ma_uint32)config->channels; + + if (config->channel_map[0] != OSAUDIO_CHANNEL_NONE) { + for (iChannel = 0; iChannel < config->channels; iChannel += 1) { + deviceConfig.playback.pChannelMap[iChannel] = osaudio_channel_to_miniaudio(config->channel_map[iChannel]); + } + } + } else { + deviceConfig = ma_device_config_init(ma_device_type_capture); + deviceConfig.capture.format = osaudio_format_to_miniaudio(config->format); + deviceConfig.capture.channels = (ma_uint32)config->channels; + + if (config->channel_map[0] != OSAUDIO_CHANNEL_NONE) { + for (iChannel = 0; iChannel < config->channels; iChannel += 1) { + deviceConfig.capture.pChannelMap[iChannel] = osaudio_channel_to_miniaudio(config->channel_map[iChannel]); + } + } + } + + deviceConfig.sampleRate = (ma_uint32)config->rate; + + /* If the buffer size is 0, we'll default to 10ms. */ + deviceConfig.periodSizeInFrames = (ma_uint32)config->buffer_size; + if (deviceConfig.periodSizeInFrames == 0) { + deviceConfig.periodSizeInMilliseconds = 10; + } + + deviceConfig.dataCallback = osaudio_data_callback; + deviceConfig.pUserData = *audio; + + if ((config->flags & OSAUDIO_FLAG_NO_REROUTING) != 0) { + deviceConfig.wasapi.noAutoStreamRouting = MA_TRUE; + } + + if (config->notification != NULL) { + deviceConfig.notificationCallback = osaudio_nofication_callback; + } + + result = osaudio_result_from_miniaudio(ma_device_init(pContext, &deviceConfig, &((*audio)->device))); + if (result != OSAUDIO_SUCCESS) { + free(*audio); + osaudio_unref_context(); + return result; + } + + /* The input config needs to be updated with actual values. */ + if (config->direction == OSAUDIO_OUTPUT) { + config->format = osaudio_format_from_miniaudio((*audio)->device.playback.format); + config->channels = (unsigned int)(*audio)->device.playback.channels; + + for (iChannel = 0; iChannel < config->channels; iChannel += 1) { + config->channel_map[iChannel] = osaudio_channel_from_miniaudio((*audio)->device.playback.channelMap[iChannel]); + } + } else { + config->format = osaudio_format_from_miniaudio((*audio)->device.capture.format); + config->channels = (unsigned int)(*audio)->device.capture.channels; + + for (iChannel = 0; iChannel < config->channels; iChannel += 1) { + config->channel_map[iChannel] = osaudio_channel_from_miniaudio((*audio)->device.capture.channelMap[iChannel]); + } + } + + config->rate = (unsigned int)(*audio)->device.sampleRate; + + if (deviceConfig.periodSizeInFrames == 0) { + if (config->direction == OSAUDIO_OUTPUT) { + config->buffer_size = (int)(*audio)->device.playback.internalPeriodSizeInFrames; + } else { + config->buffer_size = (int)(*audio)->device.capture.internalPeriodSizeInFrames; + } + } + + + /* The device object needs to have a it's local info built. We can get the ID and name from miniaudio. */ + result = osaudio_result_from_miniaudio(ma_device_get_info(&(*audio)->device, (*audio)->device.type, &deviceInfo)); + if (result == MA_SUCCESS) { + memcpy((*audio)->info.id.data, &deviceInfo.id, sizeof((*audio)->info.id.data)); + memcpy((*audio)->info.name, deviceInfo.name, sizeof((*audio)->info.name)); + } + + (*audio)->info.direction = config->direction; + (*audio)->info.config_count = 1; + (*audio)->info.configs = &(*audio)->config; + (*audio)->config = *config; + (*audio)->config.device_id = &(*audio)->info.id; + + + /* We need a ring buffer. */ + result = osaudio_result_from_miniaudio(ma_pcm_rb_init(osaudio_format_to_miniaudio(config->format), (ma_uint32)config->channels, (ma_uint32)config->buffer_size * periodCount, NULL, NULL, &(*audio)->buffer)); + if (result != OSAUDIO_SUCCESS) { + ma_device_uninit(&(*audio)->device); + free(*audio); + osaudio_unref_context(); + return result; + } + + /* Now we need a semaphore to control access to the ring buffer to to block read/write when necessary. */ + result = osaudio_result_from_miniaudio(ma_semaphore_init((config->direction == OSAUDIO_OUTPUT) ? periodCount : 0, &(*audio)->bufferSemaphore)); + if (result != OSAUDIO_SUCCESS) { + ma_pcm_rb_uninit(&(*audio)->buffer); + ma_device_uninit(&(*audio)->device); + free(*audio); + osaudio_unref_context(); + return result; + } + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_close(osaudio_t audio) +{ + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + ma_device_uninit(&audio->device); + osaudio_unref_context(); + + return OSAUDIO_SUCCESS; +} + +static void osaudio_activate(osaudio_t audio) +{ + ma_spinlock_lock(&audio->activateLock); + { + if (ma_atomic_bool32_get(&audio->isActive) == MA_FALSE) { + ma_atomic_bool32_set(&audio->isActive, MA_TRUE); + + /* If we need to flush, do so now before starting the device. */ + if (ma_atomic_bool32_get(&audio->isFlushed) == MA_TRUE) { + ma_pcm_rb_reset(&audio->buffer); + ma_atomic_bool32_set(&audio->isFlushed, MA_FALSE); + } + + /* If we're not paused, start the device. */ + if (ma_atomic_bool32_get(&audio->isPaused) == MA_FALSE) { + ma_device_start(&audio->device); + } + } + } + ma_spinlock_unlock(&audio->activateLock); +} + +osaudio_result_t osaudio_write(osaudio_t audio, const void* data, unsigned int frame_count) +{ + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + ma_mutex_lock(&audio->drainLock); + { + /* Don't return until everything has been written. */ + while (frame_count > 0) { + ma_uint32 framesToWrite = frame_count; + ma_uint32 framesAvailableInBuffer; + + /* There should be enough data available in the buffer now, but check anyway. */ + framesAvailableInBuffer = ma_pcm_rb_available_write(&audio->buffer); + if (framesAvailableInBuffer > 0) { + void* pBuffer; + + if (framesToWrite > framesAvailableInBuffer) { + framesToWrite = framesAvailableInBuffer; + } + + ma_pcm_rb_acquire_write(&audio->buffer, &framesToWrite, &pBuffer); + { + ma_copy_pcm_frames(pBuffer, data, framesToWrite, audio->device.playback.format, audio->device.playback.channels); + } + ma_pcm_rb_commit_write(&audio->buffer, framesToWrite); + + frame_count -= (unsigned int)framesToWrite; + data = (const void*)((const unsigned char*)data + (framesToWrite * ma_get_bytes_per_frame(audio->device.playback.format, audio->device.playback.channels))); + + if (framesToWrite > 0) { + osaudio_activate(audio); + } + } else { + /* If we get here it means there's not enough data available in the buffer. We need to wait for more. */ + ma_semaphore_wait(&audio->bufferSemaphore); + + /* If we're not active it probably means we've flushed. This write needs to be aborted. */ + if (ma_atomic_bool32_get(&audio->isActive) == MA_FALSE) { + break; + } + } + } + } + ma_mutex_unlock(&audio->drainLock); + + if ((audio->config.flags & OSAUDIO_FLAG_REPORT_XRUN) != 0) { + if (ma_atomic_bool32_get(&audio->xrunDetected)) { + ma_atomic_bool32_set(&audio->xrunDetected, MA_FALSE); + return OSAUDIO_XRUN; + } + } + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_read(osaudio_t audio, void* data, unsigned int frame_count) +{ + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + ma_mutex_lock(&audio->drainLock); + { + while (frame_count > 0) { + ma_uint32 framesToRead = frame_count; + ma_uint32 framesAvailableInBuffer; + + /* There should be enough data available in the buffer now, but check anyway. */ + framesAvailableInBuffer = ma_pcm_rb_available_read(&audio->buffer); + if (framesAvailableInBuffer > 0) { + void* pBuffer; + + if (framesToRead > framesAvailableInBuffer) { + framesToRead = framesAvailableInBuffer; + } + + ma_pcm_rb_acquire_read(&audio->buffer, &framesToRead, &pBuffer); + { + ma_copy_pcm_frames(data, pBuffer, framesToRead, audio->device.capture.format, audio->device.capture.channels); + } + ma_pcm_rb_commit_read(&audio->buffer, framesToRead); + + frame_count -= (unsigned int)framesToRead; + data = (void*)((unsigned char*)data + (framesToRead * ma_get_bytes_per_frame(audio->device.capture.format, audio->device.capture.channels))); + } else { + /* Activate the device from the get go or else we'll never end up capturing anything. */ + osaudio_activate(audio); + + /* If we get here it means there's not enough data available in the buffer. We need to wait for more. */ + ma_semaphore_wait(&audio->bufferSemaphore); + + /* If we're not active it probably means we've flushed. This read needs to be aborted. */ + if (ma_atomic_bool32_get(&audio->isActive) == MA_FALSE) { + break; + } + } + } + } + ma_mutex_unlock(&audio->drainLock); + + if ((audio->config.flags & OSAUDIO_FLAG_REPORT_XRUN) != 0) { + if (ma_atomic_bool32_get(&audio->xrunDetected)) { + ma_atomic_bool32_set(&audio->xrunDetected, MA_FALSE); + return OSAUDIO_XRUN; + } + } + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_drain(osaudio_t audio) +{ + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + /* This cannot be called while the device is in a paused state. */ + if (ma_atomic_bool32_get(&audio->isPaused)) { + return OSAUDIO_DEVICE_STOPPED; + } + + /* For capture we want to stop the device immediately or else we won't ever drain the buffer because miniaudio will be constantly filling it. */ + if (audio->info.direction == OSAUDIO_INPUT) { + ma_device_stop(&audio->device); + } + + /* + Mark the device as inactive *before* releasing the semaphore. When read/write completes waiting + on the semaphore, they'll check this flag and abort. + */ + ma_atomic_bool32_set(&audio->isActive, MA_FALSE); + + /* + Again in capture mode, we need to release the semaphore before waiting for the drain lock because + there's a chance read() will be waiting on the semaphore and will need to be woken up in order for + it to be given to chance to return. + */ + if (audio->info.direction == OSAUDIO_INPUT) { + ma_semaphore_release(&audio->bufferSemaphore); + } + + /* Now we need to wait for any pending reads or writes to complete. */ + ma_mutex_lock(&audio->drainLock); + { + /* No processing should be happening on the buffer at this point. Wait for miniaudio to consume the buffer. */ + while (ma_pcm_rb_available_read(&audio->buffer) > 0) { + ma_sleep(1); + } + + /* + At this point the buffer should be empty, and we shouldn't be in any read or write calls. If + it's a playback device, we'll want to stop the device. There's no need to release the semaphore. + */ + if (audio->info.direction == OSAUDIO_OUTPUT) { + ma_device_stop(&audio->device); + } + } + ma_mutex_unlock(&audio->drainLock); + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_flush(osaudio_t audio) +{ + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + /* + First stop the device. This ensures the miniaudio background thread doesn't try modifying the + buffer from under us while we're trying to flush it. + */ + ma_device_stop(&audio->device); + + /* + Mark the device as inactive *before* releasing the semaphore. When read/write completes waiting + on the semaphore, they'll check this flag and abort. + */ + ma_atomic_bool32_set(&audio->isActive, MA_FALSE); + + /* + Release the semaphore after marking the device as inactive. This needs to be released in order + to wakeup osaudio_read() and osaudio_write(). + */ + ma_semaphore_release(&audio->bufferSemaphore); + + /* + The buffer should only be modified by osaudio_read() or osaudio_write(), or the miniaudio + background thread. Therefore, we don't actually clear the buffer here. Instead we'll clear it + in osaudio_activate(), depending on whether or not the below flag is set. + */ + ma_atomic_bool32_set(&audio->isFlushed, MA_TRUE); + + return OSAUDIO_SUCCESS; +} + +osaudio_result_t osaudio_pause(osaudio_t audio) +{ + osaudio_result_t result = OSAUDIO_SUCCESS; + + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + ma_spinlock_lock(&audio->activateLock); + { + if (ma_atomic_bool32_get(&audio->isPaused) == MA_FALSE) { + ma_atomic_bool32_set(&audio->isPaused, MA_TRUE); + + /* No need to stop the device if it's not active. */ + if (ma_atomic_bool32_get(&audio->isActive)) { + result = osaudio_result_from_miniaudio(ma_device_stop(&audio->device)); + } + } + } + ma_spinlock_unlock(&audio->activateLock); + + return result; +} + +osaudio_result_t osaudio_resume(osaudio_t audio) +{ + osaudio_result_t result = OSAUDIO_SUCCESS; + + if (audio == NULL) { + return OSAUDIO_INVALID_ARGS; + } + + ma_spinlock_lock(&audio->activateLock); + { + if (ma_atomic_bool32_get(&audio->isPaused)) { + ma_atomic_bool32_set(&audio->isPaused, MA_FALSE); + + /* Don't start the device unless it's active. */ + if (ma_atomic_bool32_get(&audio->isActive)) { + result = osaudio_result_from_miniaudio(ma_device_start(&audio->device)); + } + } + } + ma_spinlock_unlock(&audio->activateLock); + + return result; +} + +unsigned int osaudio_get_avail(osaudio_t audio) +{ + if (audio == NULL) { + return 0; + } + + if (audio->info.direction == OSAUDIO_OUTPUT) { + return ma_pcm_rb_available_write(&audio->buffer); + } else { + return ma_pcm_rb_available_read(&audio->buffer); + } +} + +const osaudio_info_t* osaudio_get_info(osaudio_t audio) +{ + if (audio == NULL) { + return NULL; + } + + return &audio->info; +} + +#endif /* osaudio_miniaudio_c */ diff --git a/thirdparty/miniaudio/extras/osaudio/tests/osaudio_deviceio.c b/thirdparty/miniaudio/extras/osaudio/tests/osaudio_deviceio.c new file mode 100644 index 0000000..a08aaf0 --- /dev/null +++ b/thirdparty/miniaudio/extras/osaudio/tests/osaudio_deviceio.c @@ -0,0 +1,196 @@ +#include "../osaudio.h" + +/* This example uses miniaudio for decoding audio files. */ +#define MINIAUDIO_IMPLEMENTATION +#include "../../../miniaudio.h" + +#include +#include +#include + +#define MODE_PLAYBACK 0 +#define MODE_CAPTURE 1 +#define MODE_DUPLEX 2 + +void enumerate_devices() +{ + int result; + unsigned int iDevice; + unsigned int count; + osaudio_info_t* pDeviceInfos; + + result = osaudio_enumerate(&count, &pDeviceInfos); + if (result != OSAUDIO_SUCCESS) { + printf("Failed to enumerate audio devices.\n"); + return; + } + + for (iDevice = 0; iDevice < count; iDevice += 1) { + printf("(%s) %s\n", (pDeviceInfos[iDevice].direction == OSAUDIO_OUTPUT) ? "Playback" : "Capture", pDeviceInfos[iDevice].name); + } + + free(pDeviceInfos); +} + +osaudio_t open_device(int direction) +{ + int result; + osaudio_t audio; + osaudio_config_t config; + + osaudio_config_init(&config, direction); + config.format = OSAUDIO_FORMAT_F32; + config.channels = 2; + config.rate = 48000; + config.flags = OSAUDIO_FLAG_REPORT_XRUN; + + result = osaudio_open(&audio, &config); + if (result != OSAUDIO_SUCCESS) { + printf("Failed to open audio device.\n"); + return NULL; + } + + return audio; +} + +void do_playback(int argc, char** argv) +{ + int result; + osaudio_t audio; + const osaudio_config_t* config; + const char* pFilePath = NULL; + ma_result resultMA; + ma_decoder_config decoderConfig; + ma_decoder decoder; + + audio = open_device(OSAUDIO_OUTPUT); + if (audio == NULL) { + printf("Failed to open audio device.\n"); + return; + } + + config = &osaudio_get_info(audio)->configs[0]; + + /* We want to always use f32. */ + if (config->format == OSAUDIO_FORMAT_F32) { + if (argc > 1) { + pFilePath = argv[1]; + + decoderConfig = ma_decoder_config_init(ma_format_f32, (ma_uint32)config->channels, (ma_uint32)config->rate); + + resultMA = ma_decoder_init_file(pFilePath, &decoderConfig, &decoder); + if (resultMA == MA_SUCCESS) { + /* Now just keep looping over each sample until we get to the end. */ + for (;;) { + float frames[1024]; + ma_uint64 frameCount; + + resultMA = ma_decoder_read_pcm_frames(&decoder, frames, ma_countof(frames) / config->channels, &frameCount); + if (resultMA != MA_SUCCESS) { + break; + } + + result = osaudio_write(audio, frames, (unsigned int)frameCount); /* Safe cast. */ + if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) { + printf("Error writing to audio device."); + break; + } + + if (result == OSAUDIO_XRUN) { + printf("WARNING: An xrun occurred while writing to the playback device.\n"); + } + } + } else { + printf("Failed to open file: %s\n", pFilePath); + } + } else { + printf("No input file.\n"); + } + } else { + printf("Unsupported device format.\n"); + } + + /* Getting here means we're done and we can tear down. */ + osaudio_close(audio); +} + +void do_duplex() +{ + int result; + osaudio_t capture; + osaudio_t playback; + + capture = open_device(OSAUDIO_INPUT); + if (capture == NULL) { + printf("Failed to open capture device.\n"); + return; + } + + playback = open_device(OSAUDIO_OUTPUT); + if (playback == NULL) { + osaudio_close(capture); + printf("Failed to open playback device.\n"); + return; + } + + for (;;) { + float frames[1024]; + unsigned int frameCount; + + frameCount = ma_countof(frames) / osaudio_get_info(capture)->configs[0].channels; + + /* Capture. */ + result = osaudio_read(capture, frames, frameCount); + if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) { + printf("Error reading from capture device.\n"); + break; + } + + if (result == OSAUDIO_XRUN) { + printf("WARNING: An xrun occurred while reading from the capture device.\n"); + } + + + /* Playback. */ + result = osaudio_write(playback, frames, frameCount); + if (result != OSAUDIO_SUCCESS && result != OSAUDIO_XRUN) { + printf("Error writing to playback device.\n"); + break; + } + + if (result == OSAUDIO_XRUN) { + printf("WARNING: An xrun occurred while writing to the playback device.\n"); + } + } + + osaudio_close(capture); + osaudio_close(playback); +} + +int main(int argc, char** argv) +{ + int mode = MODE_PLAYBACK; + int iarg; + + enumerate_devices(); + + for (iarg = 0; iarg < argc; iarg += 1) { + if (strcmp(argv[iarg], "capture") == 0) { + mode = MODE_CAPTURE; + } else if (strcmp(argv[iarg], "duplex") == 0) { + mode = MODE_DUPLEX; + } + } + + switch (mode) + { + case MODE_PLAYBACK: do_playback(argc, argv); break; + case MODE_CAPTURE: break; + case MODE_DUPLEX: do_duplex(); break; + } + + (void)argc; + (void)argv; + + return 0; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/extras/stb_vorbis.c b/thirdparty/miniaudio/extras/stb_vorbis.c new file mode 100644 index 0000000..3e5c250 --- /dev/null +++ b/thirdparty/miniaudio/extras/stb_vorbis.c @@ -0,0 +1,5584 @@ +// Ogg Vorbis audio decoder - v1.22 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster github:alxprd +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier +// +// Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes +// 1.19 - 2020-02-05 - warnings +// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +typedef struct +{ + char *vendor; + + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline + #ifndef alloca + #define alloca __builtin_alloca + #endif +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + char *vendor; + int comment_list_length; + char **comment_list; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; + + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) (void)0 +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+7)&~7; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, (int)exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static int get32_packet(vorb *f) +{ + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32) get8_packet(f) << 24; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + + assert(f->valid_bits >= n); + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + float l00,l11; + + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; + + k00 = z[ -4] - z[-12]; + k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + STBV_NOTUSED(left_end); + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + f->first_decode = TRUE; + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + //file vendor + len = get32_packet(f); + f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for(i=0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + //user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for(i=0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for(j=0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0,hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + + setup_free(p, p->vendor); + for (i=0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) +{ + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + vorbis_deinit(&p); + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } + #undef STB_BUFFER_SIZE +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/thirdparty/miniaudio/miniaudio.h b/thirdparty/miniaudio/miniaudio.h new file mode 100644 index 0000000..47332e1 --- /dev/null +++ b/thirdparty/miniaudio/miniaudio.h @@ -0,0 +1,92621 @@ +/* +Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. +miniaudio - v0.11.21 - 2023-11-15 + +David Reid - mackron@gmail.com + +Website: https://miniaud.io +Documentation: https://miniaud.io/docs +GitHub: https://github.com/mackron/miniaudio +*/ + +/* +1. Introduction +=============== +miniaudio is a single file library for audio playback and capture. To use it, do the following in +one .c file: + + ```c + #define MINIAUDIO_IMPLEMENTATION + #include "miniaudio.h" + ``` + +You can do `#include "miniaudio.h"` in other parts of the program just like any other header. + +miniaudio includes both low level and high level APIs. The low level API is good for those who want +to do all of their mixing themselves and only require a light weight interface to the underlying +audio device. The high level API is good for those who have complex mixing and effect requirements. + +In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles +to opaque objects which means you need to allocate memory for objects yourself. In the examples +presented in this documentation you will often see objects declared on the stack. You need to be +careful when translating these examples to your own code so that you don't accidentally declare +your objects on the stack and then cause them to become invalid once the function returns. In +addition, you must ensure the memory address of your objects remain the same throughout their +lifetime. You therefore cannot be making copies of your objects. + +A config/init pattern is used throughout the entire library. The idea is that you set up a config +object and pass that into the initialization routine. The advantage to this system is that the +config object can be initialized with logical defaults and new properties added to it without +breaking the API. The config object can be allocated on the stack and does not need to be +maintained after initialization of the corresponding object. + + +1.1. Low Level API +------------------ +The low level API gives you access to the raw audio data of an audio device. It supports playback, +capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which +physical device(s) you want to connect to. + +The low level API uses the concept of a "device" as the abstraction for physical devices. The idea +is that you choose a physical device to emit or capture audio from, and then move data to/from the +device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a +callback which you specify when initializing the device. + +When initializing the device you first need to configure it. The device configuration allows you to +specify things like the format of the data delivered via the callback, the size of the internal +buffer and the ID of the device you want to emit or capture audio from. + +Once you have the device configuration set up you can initialize the device. When initializing a +device you need to allocate memory for the device object beforehand. This gives the application +complete control over how the memory is allocated. In the example below we initialize a playback +device on the stack, but you could allocate it on the heap if that suits your situation better. + + ```c + void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) + { + // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both + // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than + // frameCount frames. + } + + int main() + { + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. + config.playback.channels = 2; // Set to 0 to use the device's native channel count. + config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. + config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. + config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). + + ma_device device; + if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { + return -1; // Failed to initialize the device. + } + + ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. + + // Do something here. Probably your program's main loop. + + ma_device_uninit(&device); + return 0; + } + ``` + +In the example above, `data_callback()` is where audio data is written and read from the device. +The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data +to the output buffer (`pOutput` in the example). In capture mode you read data from the input +buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you +how many frames can be written to the output buffer and read from the input buffer. A "frame" is +one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 +samples: one for the left, one for the right. The channel count is defined by the device config. +The size in bytes of an individual sample is defined by the sample format which is also specified +in the device config. Multi-channel audio data is always interleaved, which means the samples for +each frame are stored next to each other in memory. For example, in a stereo stream the first pair +of samples will be the left and right samples for the first frame, the second pair of samples will +be the left and right samples for the second frame, etc. + +The configuration of the device is defined by the `ma_device_config` structure. The config object +is always initialized with `ma_device_config_init()`. It's important to always initialize the +config with this function as it initializes it with logical defaults and ensures your program +doesn't break when new members are added to the `ma_device_config` structure. The example above +uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes +a single parameter, which is whether or not the device is a playback, capture, duplex or loopback +device (loopback devices are not supported on all backends). The `config.playback.format` member +sets the sample format which can be one of the following (all formats are native-endian): + + +---------------+----------------------------------------+---------------------------+ + | Symbol | Description | Range | + +---------------+----------------------------------------+---------------------------+ + | ma_format_f32 | 32-bit floating point | [-1, 1] | + | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | + | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | + | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | + | ma_format_u8 | 8-bit unsigned integer | [0, 255] | + +---------------+----------------------------------------+---------------------------+ + +The `config.playback.channels` member sets the number of channels to use with the device. The +channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate +(which must be the same for both playback and capture in full-duplex configurations). This is +usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between +8000 and 384000, however. + +Note that leaving the format, channel count and/or sample rate at their default values will result +in the internal device's native configuration being used which is useful if you want to avoid the +overhead of miniaudio's automatic data conversion. + +In addition to the sample format, channel count and sample rate, the data callback and user data +pointer are also set via the config. The user data pointer is not passed into the callback as a +parameter, but is instead set to the `pUserData` member of `ma_device` which you can access +directly since all miniaudio structures are transparent. + +Initializing the device is done with `ma_device_init()`. This will return a result code telling you +what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is +complete the device will be in a stopped state. To start it, use `ma_device_start()`. +Uninitializing the device will stop it, which is what the example above does, but you can also stop +the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. +Note that it's important to never stop or start the device from inside the callback. This will +result in a deadlock. Instead you set a variable or signal an event indicating that the device +needs to stop and handle it in a different thread. The following APIs must never be called inside +the callback: + + ```c + ma_device_init() + ma_device_init_ex() + ma_device_uninit() + ma_device_start() + ma_device_stop() + ``` + +You must never try uninitializing and reinitializing a device inside the callback. You must also +never try to stop and start it from inside the callback. There are a few other things you shouldn't +do in the callback depending on your requirements, however this isn't so much a thread-safety +thing, but rather a real-time processing thing which is beyond the scope of this introduction. + +The example above demonstrates the initialization of a playback device, but it works exactly the +same for capture. All you need to do is change the device type from `ma_device_type_playback` to +`ma_device_type_capture` when setting up the config, like so: + + ```c + ma_device_config config = ma_device_config_init(ma_device_type_capture); + config.capture.format = MY_FORMAT; + config.capture.channels = MY_CHANNEL_COUNT; + ``` + +In the data callback you just read from the input buffer (`pInput` in the example above) and leave +the output buffer alone (it will be set to NULL when the device type is set to +`ma_device_type_capture`). + +These are the available device types and how you should handle the buffers in the callback: + + +-------------------------+--------------------------------------------------------+ + | Device Type | Callback Behavior | + +-------------------------+--------------------------------------------------------+ + | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | + | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | + | ma_device_type_duplex | Read from input buffer, write to output buffer. | + | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | + +-------------------------+--------------------------------------------------------+ + +You will notice in the example above that the sample format and channel count is specified +separately for playback and capture. This is to support different data formats between the playback +and capture devices in a full-duplex system. An example may be that you want to capture audio data +as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you +use different formats between playback and capture in a full-duplex configuration you will need to +convert the data yourself. There are functions available to help you do this which will be +explained later. + +The example above did not specify a physical device to connect to which means it will use the +operating system's default device. If you have multiple physical devices connected and you want to +use a specific one you will need to specify the device ID in the configuration, like so: + + ```c + config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. + config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. + ``` + +To retrieve the device ID you will need to perform device enumeration, however this requires the +use of a new concept called the "context". Conceptually speaking the context sits above the device. +There is one context to many devices. The purpose of the context is to represent the backend at a +more global level and to perform operations outside the scope of an individual device. Mainly it is +used for performing run-time linking against backend libraries, initializing backends and +enumerating devices. The example below shows how to enumerate devices. + + ```c + ma_context context; + if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { + // Error. + } + + ma_device_info* pPlaybackInfos; + ma_uint32 playbackCount; + ma_device_info* pCaptureInfos; + ma_uint32 captureCount; + if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { + // Error. + } + + // Loop over each device info and do something with it. Here we just print the name with their index. You may want + // to give the user the opportunity to choose which device they'd prefer. + for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { + printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); + } + + ma_device_config config = ma_device_config_init(ma_device_type_playback); + config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; + config.playback.format = MY_FORMAT; + config.playback.channels = MY_CHANNEL_COUNT; + config.sampleRate = MY_SAMPLE_RATE; + config.dataCallback = data_callback; + config.pUserData = pMyCustomData; + + ma_device device; + if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { + // Error + } + + ... + + ma_device_uninit(&device); + ma_context_uninit(&context); + ``` + +The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. +The first parameter is a pointer to a list of `ma_backend` values which are used to override the +default backend priorities. When this is NULL, as in this example, miniaudio's default priorities +are used. The second parameter is the number of backends listed in the array pointed to by the +first parameter. The third parameter is a pointer to a `ma_context_config` object which can be +NULL, in which case defaults are used. The context configuration is used for setting the logging +callback, custom memory allocation callbacks, user-defined data and some backend-specific +configurations. + +Once the context has been initialized you can enumerate devices. In the example above we use the +simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by +using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer +to a pointer that will, upon output, be set to a pointer to a buffer containing a list of +`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive +the number of items in the returned buffer. Do not free the returned buffers as their memory is +managed internally by miniaudio. + +The `ma_device_info` structure contains an `id` member which is the ID you pass to the device +config. It also contains the name of the device which is useful for presenting a list of devices +to the user via the UI. + +When creating your own context you will want to pass it to `ma_device_init()` when initializing the +device. Passing in NULL, like we do in the first example, will result in miniaudio creating the +context for you, which you don't want to do since you've already created a context. Note that +internally the context is only tracked by it's pointer which means you must not change the location +of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for +the context. + + +1.2. High Level API +------------------- +The high level API consists of three main parts: + + * Resource management for loading and streaming sounds. + * A node graph for advanced mixing and effect processing. + * A high level "engine" that wraps around the resource manager and node graph. + +The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds +fully into memory and also streaming. It will also deal with reference counting for you which +avoids the same sound being loaded multiple times. + +The node graph is used for mixing and effect processing. The idea is that you connect a number of +nodes into the graph by connecting each node's outputs to another node's inputs. Each node can +implement it's own effect. By chaining nodes together, advanced mixing and effect processing can +be achieved. + +The engine encapsulates both the resource manager and the node graph to create a simple, easy to +use high level API. The resource manager and node graph APIs are covered in more later sections of +this manual. + +The code below shows how you can initialize an engine using it's default configuration. + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This creates an engine instance which will initialize a device internally which you can access with +`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed +with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which +means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a +cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. + +Note that all objects in miniaudio, including the `ma_engine` object in the example above, are +transparent structures. There are no handles to opaque structures in miniaudio which means you need +to be mindful of how you declare them. In the example above we are declaring it on the stack, but +this will result in the struct being invalidated once the function encapsulating it returns. If +allocating the engine on the heap is more appropriate, you can easily do so with a standard call +to `malloc()` or whatever heap allocation routine you like: + + ```c + ma_engine* pEngine = malloc(sizeof(*pEngine)); + ``` + +The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure +an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of +`ma_engine_init()`: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; + } + ``` + +This creates an engine instance using a custom config. In this particular example it's showing how +you can specify a custom resource manager rather than having the engine initialize one internally. +This is particularly useful if you want to have multiple engine's share the same resource manager. + +The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. + +By default the engine will be started, but nothing will be playing because no sounds have been +initialized. The easiest but least flexible way of playing a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", NULL); + ``` + +This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the +internal sound up for recycling. The last parameter is used to specify which sound group the sound +should be associated with which will be explained later. This particular way of playing a sound is +simple, but lacks flexibility and features. A more flexible way of playing a sound is to first +initialize a sound: + + ```c + ma_result result; + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); + if (result != MA_SUCCESS) { + return result; + } + + ma_sound_start(&sound); + ``` + +This returns a `ma_sound` object which represents a single instance of the specified sound file. If +you want to play the same file multiple times simultaneously, you need to create one sound for each +instance. + +Sounds should be uninitialized with `ma_sound_uninit()`. + +Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with +`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use +`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting +and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound +the be started and/or stopped at a specific time. This can be done with the following functions: + + ```c + ma_sound_set_start_time_in_pcm_frames() + ma_sound_set_start_time_in_milliseconds() + ma_sound_set_stop_time_in_pcm_frames() + ma_sound_set_stop_time_in_milliseconds() + ``` + +The start/stop time needs to be specified based on the absolute timer which is controlled by the +engine. The current global time time in PCM frames can be retrieved with +`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with +`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling +a start time still requires an explicit call to `ma_sound_start()` before anything will play: + + ```c + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_start(&sound); + ``` + +The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be +loaded and a few options on which features should be enabled for that sound. By default, the sound +is synchronously loaded fully into memory straight from the file system without any kind of +decoding. If you want to decode the sound before storing it in memory, you need to specify the +`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier +stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing +time which might be too expensive on the audio thread. + +If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This +will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing +until the sound has had some audio decoded. + +The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise +sounds into groups which have their own effect processing and volume control. An example is a game +which might have separate groups for sfx, voice and music. Each of these groups have their own +independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize +a sound group. + +Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` +API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex +effect chains. + +A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume +control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. + +Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know +a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, +you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. + +By default, sounds and sound groups have spatialization enabled. If you don't ever want to +spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The +spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and +environmental occlusion are not currently supported, but planned for the future. The supported +features include: + + * Sound and listener positioning and orientation with cones + * Attenuation models: none, inverse, linear and exponential + * Doppler effect + +Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. + +To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound +is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with +`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. + + + +2. Building +=========== +miniaudio should work cleanly out of the box without the need to download or install any +dependencies. See below for platform-specific details. + +Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. + +If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, +etc. you need to link with `-latomic`. + + +2.1. Windows +------------ +The Windows build should compile cleanly on all popular compilers without the need to configure any +include paths nor link to any libraries. + +The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external +symbol for `ActivateAudioInterfaceAsync()`. + + +2.2. macOS and iOS +------------------ +The macOS build should compile cleanly without the need to download any dependencies nor link to +any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to +link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling +through the command line requires linking to `-lpthread` and `-lm`. + +Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's +notarization process. To fix this there are two options. The first is to use the +`MA_NO_RUNTIME_LINKING` option, like so: + + ```c + #ifdef __APPLE__ + #define MA_NO_RUNTIME_LINKING + #endif + #define MINIAUDIO_IMPLEMENTATION + #include "miniaudio.h" + ``` + +This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. +If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when +using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can +add the following to your entitlements.xcent file: + + ``` + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.allow-unsigned-executable-memory + + ``` + +See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. + + +2.3. Linux +---------- +The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any +development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. + + +2.4. BSD +-------- +The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses +sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit +ARM. + + +2.5. Android +------------ +AAudio is the highest priority backend on Android. This should work out of the box without needing +any kind of compiler configuration. Support for AAudio starts with Android 8 which means older +versions will fall back to OpenSL|ES which requires API level 16+. + +There have been reports that the OpenSL|ES backend fails to initialize on some Android based +devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform +you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. + + +2.6. Emscripten +--------------- +The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. +You cannot use `-std=c*` compiler flags, nor `-ansi`. + +You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +with the following options: + + -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +An example for compiling with AudioWorklet support might look like this: + + emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +To run locally, you'll need to use emrun: + + emrun bin/program.html + + + +2.7. Build Options +------------------ +`#define` these options before including miniaudio.h. + + +----------------------------------+--------------------------------------------------------------------+ + | Option | Description | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_WASAPI | Disables the WASAPI backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_DSOUND | Disables the DirectSound backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_WINMM | Disables the WinMM backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_ALSA | Disables the ALSA backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_JACK | Disables the JACK backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_COREAUDIO | Disables the Core Audio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_SNDIO | Disables the sndio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_AUDIO4 | Disables the audio(4) backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_OSS | Disables the OSS backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_AAUDIO | Disables the AAudio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_OPENSL | Disables the OpenSL|ES backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_WEBAUDIO | Disables the Web Audio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_NULL | Disables the null backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | + | | enable specific backends. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the WASAPI backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the DirectSound backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the WinMM backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the ALSA backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the PulseAudio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the JACK backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the Core Audio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the sndio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the audio(4) backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the OSS backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the AAudio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the OpenSL|ES backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the Web Audio backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | + | | enable the null backend. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_DECODING | Disables decoding APIs. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_ENCODING | Disables encoding APIs. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_FLAC | Disables the built-in FLAC decoder. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_MP3 | Disables the built-in MP3 decoder. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | + | | and `ma_device` APIs. This is useful if you only want to use | + | | miniaudio's data conversion and/or decoding APIs. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | + | | also disable the following functions: | + | | | + | | ``` | + | | ma_sound_init_from_file() | + | | ma_sound_init_from_file_w() | + | | ma_sound_init_copy() | + | | ma_engine_play_sound_ex() | + | | ma_engine_play_sound() | + | | ``` | + | | | + | | The only way to initialize a `ma_sound` object is to initialize it | + | | from a data source. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | + | | because it depends on the node graph. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_ENGINE | Disables the engine API. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | + | | `ma_event` APIs. This option is useful if you only need to use | + | | miniaudio for data conversion, decoding and/or encoding. Some | + | | families of APIs require threading which means the following | + | | options must also be set: | + | | | + | | ``` | + | | MA_NO_DEVICE_IO | + | | ``` | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_SSE2 | Disables SSE2 optimizations. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_AVX2 | Disables AVX2 optimizations. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_NEON | Disables NEON optimizations. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | + | | notarization process. When enabling this, you may need to avoid | + | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | + | | up with compilation errors due to conflicts with `timespec` and | + | | `timeval` data types. | + | | | + | | You may need to enable this if your target platform does not allow | + | | runtime linking via `dlopen()`. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | + +----------------------------------+--------------------------------------------------------------------+ + | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | + | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | + +----------------------------------+--------------------------------------------------------------------+ + | MA_API | Controls how public APIs should be decorated. Default is `extern`. | + +----------------------------------+--------------------------------------------------------------------+ + + +3. Definitions +============== +This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity +in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio +uses each term. + +3.1. Sample +----------- +A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit +floating point number. + +3.2. Frame / PCM Frame +---------------------- +A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 +samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" +and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. +If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always +clarify what it's referring to with something like "FLAC frame". + +3.3. Channel +------------ +A stream of monaural audio that is emitted from an individual speaker in a speaker system, or +received from an individual microphone in a microphone system. A stereo stream has two channels (a +left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio +systems refer to a channel as a complex audio stream that's mixed with other channels to produce +the final mix - this is completely different to miniaudio's use of the term "channel" and should +not be confused. + +3.4. Sample Rate +---------------- +The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number +of PCM frames that are processed per second. + +3.5. Formats +------------ +Throughout miniaudio you will see references to different sample formats: + + +---------------+----------------------------------------+---------------------------+ + | Symbol | Description | Range | + +---------------+----------------------------------------+---------------------------+ + | ma_format_f32 | 32-bit floating point | [-1, 1] | + | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | + | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | + | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | + | ma_format_u8 | 8-bit unsigned integer | [0, 255] | + +---------------+----------------------------------------+---------------------------+ + +All formats are native-endian. + + + +4. Data Sources +=============== +The data source abstraction in miniaudio is used for retrieving audio data from some source. A few +examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data +sources in order to make sense of some of the higher level concepts in miniaudio. + +The `ma_data_source` API is a generic interface for reading from a data source. Any object that +implements the data source interface can be plugged into any `ma_data_source` function. + +To read data from a data source: + + ```c + ma_result result; + ma_uint64 framesRead; + + result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the data source. + } + ``` + +If you don't need the number of frames that were successfully read you can pass in `NULL` to the +`pFramesRead` parameter. If this returns a value less than the number of frames requested it means +the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames +read is 0. + +When calling any data source function, with the exception of `ma_data_source_init()` and +`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, +you could plug in a decoder like so: + + ```c + ma_result result; + ma_uint64 framesRead; + ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. + + result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; // Failed to read data from the decoder. + } + ``` + +If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you +can use `ma_data_source_seek_pcm_frames()`. + +To seek to a specific PCM frame: + + ```c + result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); + if (result != MA_SUCCESS) { + return result; // Failed to seek to PCM frame. + } + ``` + +You can retrieve the total length of a data source in PCM frames, but note that some data sources +may not have the notion of a length, such as noise and waveforms, and others may just not have a +way of determining the length such as some decoders. To retrieve the length: + + ```c + ma_uint64 length; + + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the length. + } + ``` + +Care should be taken when retrieving the length of a data source where the underlying decoder is +pulling data from a data stream with an undefined length, such as internet radio or some kind of +broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. + +The current position of the cursor in PCM frames can also be retrieved: + + ```c + ma_uint64 cursor; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve the cursor. + } + ``` + +You will often need to know the data format that will be returned after reading. This can be +retrieved like so: + + ```c + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + + result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); + if (result != MA_SUCCESS) { + return result; // Failed to retrieve data format. + } + ``` + +If you do not need a specific data format property, just pass in NULL to the respective parameter. + +There may be cases where you want to implement something like a sound bank where you only want to +read data within a certain range of the underlying data. To do this you can use a range: + + ```c + result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the range. + } + ``` + +This is useful if you have a sound bank where many sounds are stored in the same file and you want +the data source to only play one of those sub-sounds. Note that once the range is set, everything +that takes a position, such as cursors and loop points, should always be relatvie to the start of +the range. When the range is set, any previously defined loop point will be reset. + +Custom loop points can also be used with data sources. By default, data sources will loop after +they reach the end of the data source, but if you need to loop at a specific location, you can do +the following: + + ```c + result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); + if (result != MA_SUCCESS) { + return result; // Failed to set the loop point. + } + ``` + +The loop point is relative to the current range. + +It's sometimes useful to chain data sources together so that a seamless transition can be achieved. +To do this, you can use chaining: + + ```c + ma_decoder decoder1; + ma_decoder decoder2; + + // ... initialize decoders with ma_decoder_init_*() ... + + result = ma_data_source_set_next(&decoder1, &decoder2); + if (result != MA_SUCCESS) { + return result; // Failed to set the next data source. + } + + result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead); + if (result != MA_SUCCESS) { + return result; // Failed to read from the decoder. + } + ``` + +In the example above we're using decoders. When reading from a chain, you always want to read from +the top level data source in the chain. In the example above, `decoder1` is the top level data +source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any +gaps. + +Note that when looping is enabled, only the current data source will be looped. You can loop the +entire chain by linking in a loop like so: + + ```c + ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 + ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). + ``` + +Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically +changing links while the audio thread is in the middle of reading. + +Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple +instances of the same sound simultaneously. This can be extremely inefficient depending on the type +of data source and can result in glitching due to subtle changes to the state of internal filters. +Instead, initialize multiple data sources for each instance. + + +4.1. Custom Data Sources +------------------------ +You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. +Your custom object must have `ma_data_source_base` as it's first member: + + ```c + struct my_data_source + { + ma_data_source_base base; + ... + }; + ``` + +In your initialization routine, you need to call `ma_data_source_init()` in order to set up the +base object (`ma_data_source_base`): + + ```c + static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) + { + // Read data here. Output in the same format returned by my_data_source_get_data_format(). + } + + static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) + { + // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. + } + + static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) + { + // Return the format of the data here. + } + + static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) + { + // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. + } + + static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) + { + // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. + } + + static ma_data_source_vtable g_my_data_source_vtable = + { + my_data_source_read, + my_data_source_seek, + my_data_source_get_data_format, + my_data_source_get_cursor, + my_data_source_get_length + }; + + ma_result my_data_source_init(my_data_source* pMyDataSource) + { + ma_result result; + ma_data_source_config baseConfig; + + baseConfig = ma_data_source_config_init(); + baseConfig.vtable = &g_my_data_source_vtable; + + result = ma_data_source_init(&baseConfig, &pMyDataSource->base); + if (result != MA_SUCCESS) { + return result; + } + + // ... do the initialization of your custom data source here ... + + return MA_SUCCESS; + } + + void my_data_source_uninit(my_data_source* pMyDataSource) + { + // ... do the uninitialization of your custom data source here ... + + // You must uninitialize the base data source. + ma_data_source_uninit(&pMyDataSource->base); + } + ``` + +Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside +of the custom data source. It's up to the custom data source itself to call these within their own +init/uninit functions. + + + +5. Engine +========= +The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The +`ma_engine` object encapsulates a resource manager and a node graph, both of which will be +explained in more detail later. + +Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing +group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and +`ma_sound_group` objects are nodes within the engine's node graph. + +When the engine is initialized, it will normally create a device internally. If you would rather +manage the device yourself, you can do so and just pass a pointer to it via the engine config when +you initialize the engine. You can also just use the engine without a device, which again can be +configured via the engine config. + +The most basic way to initialize the engine is with a default config, like so: + + ```c + ma_result result; + ma_engine engine; + + result = ma_engine_init(NULL, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +This will result in the engine initializing a playback device using the operating system's default +device. This will be sufficient for many use cases, but if you need more flexibility you'll want to +configure the engine with an engine config: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pDevice = &myDevice; + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +In the example above we're passing in a pre-initialized device. Since the caller is the one in +control of the device's data callback, it's their responsibility to manually call +`ma_engine_read_pcm_frames()` from inside their data callback: + + ```c + void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) + { + ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); + } + ``` + +You can also use the engine independent of a device entirely: + + ```c + ma_result result; + ma_engine engine; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.noDevice = MA_TRUE; + engineConfig.channels = 2; // Must be set when not using a device. + engineConfig.sampleRate = 48000; // Must be set when not using a device. + + result = ma_engine_init(&engineConfig, &engine); + if (result != MA_SUCCESS) { + return result; // Failed to initialize the engine. + } + ``` + +Note that when you're not using a device, you must set the channel count and sample rate in the +config or else miniaudio won't know what to use (miniaudio will use the device to determine this +normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio +data from the engine. This kind of setup is useful if you want to do something like offline +processing or want to use a different audio system for playback such as SDL. + +When a sound is loaded it goes through a resource manager. By default the engine will initialize a +resource manager internally, but you can also specify a pre-initialized resource manager: + + ```c + ma_result result; + ma_engine engine1; + ma_engine engine2; + ma_engine_config engineConfig; + + engineConfig = ma_engine_config_init(); + engineConfig.pResourceManager = &myResourceManager; + + ma_engine_init(&engineConfig, &engine1); + ma_engine_init(&engineConfig, &engine2); + ``` + +In this example we are initializing two engines, both of which are sharing the same resource +manager. This is especially useful for saving memory when loading the same file across multiple +engines. If you were not to use a shared resource manager, each engine instance would use their own +which would result in any sounds that are used between both engine's being loaded twice. By using +a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you +need to output to multiple playback devices, such as in a local multiplayer game where each player +is using their own set of headphones. + +By default an engine will be in a started state. To make it so the engine is not automatically +started you can configure it as such: + + ```c + engineConfig.noAutoStart = MA_TRUE; + + // The engine will need to be started manually. + ma_engine_start(&engine); + + // Later on the engine can be stopped with ma_engine_stop(). + ma_engine_stop(&engine); + ``` + +The concept of starting or stopping an engine is only relevant when using the engine with a +device. Attempting to start or stop an engine that is not associated with a device will result in +`MA_INVALID_OPERATION`. + +The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a +linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you +prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. + +When a sound is spatialized, it is done so relative to a listener. An engine can be configured to +have multiple listeners which can be configured via the config: + + ```c + engineConfig.listenerCount = 2; + ``` + +The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a +sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound +to a specific listener which will be explained later. Listener's have a position, direction, cone, +and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up +to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The +position, direction and velocity are all specified in absolute terms: + + ```c + ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); + ``` + +The direction of the listener represents it's forward vector. The listener's up vector can also be +specified and defaults to +1 on the Y axis. + + ```c + ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); + ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); + ``` + +The engine supports directional attenuation. The listener can have a cone the controls how sound is +attenuated based on the listener's direction. When a sound is between the inner and outer cones, it +will be attenuated between 1 and the cone's outer gain: + + ```c + ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +When a sound is inside the inner code, no directional attenuation is applied. When the sound is +outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When +the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 +and the outer gain. + +The engine's coordinate system follows the OpenGL coordinate system where positive X points right, +positive Y points up and negative Z points forward. + +The simplest and least flexible way to play a sound is like so: + + ```c + ma_engine_play_sound(&engine, "my_sound.wav", pGroup); + ``` + +This is a "fire and forget" style of function. The engine will manage the `ma_sound` object +internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility +you'll want to initialize a sound object: + + ```c + ma_sound sound; + + result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); + if (result != MA_SUCCESS) { + return result; // Failed to load sound. + } + ``` + +Sounds need to be uninitialized with `ma_sound_uninit()`. + +The example above loads a sound from a file. If the resource manager has been disabled you will not +be able to use this function and instead you'll need to initialize a sound directly from a data +source: + + ```c + ma_sound sound; + + result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +Each `ma_sound` object represents a single instance of the sound. If you want to play the same +sound multiple times at the same time, you need to initialize a separate `ma_sound` object. + +For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's +standard config/init pattern: + + ```c + ma_sound sound; + ma_sound_config soundConfig; + + soundConfig = ma_sound_config_init(); + soundConfig.pFilePath = NULL; // Set this to load from a file path. + soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. + soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; + soundConfig.initialAttachmentInputBusIndex = 0; + soundConfig.channelsIn = 1; + soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. + + result = ma_sound_init_ex(&soundConfig, &sound); + if (result != MA_SUCCESS) { + return result; + } + ``` + +In the example above, the sound is being initialized without a file nor a data source. This is +valid, in which case the sound acts as a node in the middle of the node graph. This means you can +connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly +what a `ma_sound_group` is. + +When loading a sound, you specify a set of flags that control how the sound is loaded and what +features are enabled for that sound. When no flags are set, the sound will be fully loaded into +memory in exactly the same format as how it's stored on the file system. The resource manager will +allocate a block of memory and then load the file directly into it. When reading audio data, it +will be decoded dynamically on the fly. In order to save processing time on the audio thread, it +might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); + ``` + +By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until +the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously +by specifying the `MA_SOUND_FLAG_ASYNC` flag: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); + ``` + +This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully +loaded. When you start the sound, it won't output anything until some sound is available. The sound +will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` +is specified. + +If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A +fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal +counter hit's zero. You can specify a fence like so: + + ```c + ma_result result; + ma_fence fence; + ma_sound sounds[4]; + + result = ma_fence_init(&fence); + if (result != MA_SUCCESS) { + return result; + } + + // Load some sounds asynchronously. + for (int iSound = 0; iSound < 4; iSound += 1) { + ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); + } + + // ... do some other stuff here in the mean time ... + + // Wait for all sounds to finish loading. + ma_fence_wait(&fence); + ``` + +If loading the entire sound into memory is prohibitive, you can also configure the engine to stream +the audio data: + + ```c + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); + ``` + +When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work +fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music +tracks in games. + +When loading a sound from a file path, the engine will reference count the file to prevent it from +being loaded if it's already in memory. When you uninitialize a sound, the reference count will be +decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting +system is not used for streams. The engine will use a 64-bit hash of the file name when comparing +file paths which means there's a small chance you might encounter a name collision. If this is an +issue, you'll need to use a different name for one of the colliding file paths, or just not load +from files and instead load from a data source. + +You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this +only works for sounds that were initialized with `ma_sound_init_from_file()` and without the +`MA_SOUND_FLAG_STREAM` flag. + +When you initialize a sound, if you specify a sound group the sound will be attached to that group +automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. +If you would instead rather leave the sound unattached by default, you can can specify the +`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node +graph. + +Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with +`ma_sound_stop()`. + +Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the +engine's master volume. + +Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan +to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas ++1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger +value will result in a higher pitch. The pitch must be greater than 0. + +The engine supports 3D spatialization of sounds. By default sounds will have spatialization +enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways +to disable spatialization of a sound: + + ```c + // Disable spatialization at initialization time via a flag: + ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); + + // Dynamically disable or enable spatialization post-initialization: + ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); + ``` + +By default sounds will be spatialized based on the closest listener. If a sound should always be +spatialized relative to a specific listener it can be pinned to one: + + ```c + ma_sound_set_pinned_listener_index(&sound, listenerIndex); + ``` + +Like listeners, sounds have a position. By default, the position of a sound is in absolute space, +but it can be changed to be relative to a listener: + + ```c + ma_sound_set_positioning(&sound, ma_positioning_relative); + ``` + +Note that relative positioning of a sound only makes sense if there is either only one listener, or +the sound is pinned to a specific listener. To set the position of a sound: + + ```c + ma_sound_set_position(&sound, posX, posY, posZ); + ``` + +The direction works the same way as a listener and represents the sound's forward direction: + + ```c + ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); + ``` + +Sound's also have a cone for controlling directional attenuation. This works exactly the same as +listeners: + + ```c + ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); + ``` + +The velocity of a sound is used for doppler effect and can be set as such: + + ```c + ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); + ``` + +The engine supports different attenuation models which can be configured on a per-sound basis. By +default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to +OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: + + ```c + ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); + ``` + +The supported attenuation models include the following: + + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_none | No distance attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_linear | Linear attenuation. | + +----------------------------------+----------------------------------------------+ + | ma_attenuation_model_exponential | Exponential attenuation. | + +----------------------------------+----------------------------------------------+ + +To control how quickly a sound rolls off as it moves away from the listener, you need to configure +the rolloff: + + ```c + ma_sound_set_rolloff(&sound, rolloff); + ``` + +You can control the minimum and maximum gain to apply from spatialization: + + ```c + ma_sound_set_min_gain(&sound, minGain); + ma_sound_set_max_gain(&sound, maxGain); + ``` + +Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for +the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain +volume after the listener moves further away and to have sounds play a maximum volume when the +listener is within a certain distance: + + ```c + ma_sound_set_min_distance(&sound, minDistance); + ma_sound_set_max_distance(&sound, maxDistance); + ``` + +The engine's spatialization system supports doppler effect. The doppler factor can be configure on +a per-sound basis like so: + + ```c + ma_sound_set_doppler_factor(&sound, dopplerFactor); + ``` + +You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and +`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the +starting volume: + + ```c + // Fade in over 1 second. + ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); + + // ... sometime later ... + + // Fade out over 1 second, starting from the current volume. + ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); + ``` + +By default sounds will start immediately, but sometimes for timing and synchronization purposes it +can be useful to schedule a sound to start or stop: + + ```c + // Start the sound in 1 second from now. + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + + // Stop the sound in 2 seconds from now. + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ``` + +Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before +anything will play. + +The time is specified in global time which is controlled by the engine. You can get the engine's +current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented +automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` +in case it needs to be resynchronized for some reason. + +To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will +take the scheduled start and stop times into account. + +Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not +be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. + +Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping +sound this should never return true. Alternatively, you can configure a callback that will be fired +when the sound reaches the end. Note that the callback is fired from the audio thread which means +you cannot be uninitializing sound from the callback. To set the callback you can use +`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it +into the config like so: + + ```c + soundConfig.endCallback = my_end_callback; + soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; + ``` + +The end callback is declared like so: + + ```c + void my_end_callback(void* pUserData, ma_sound* pSound) + { + ... + } + ``` + +Internally a sound wraps around a data source. Some APIs exist to control the underlying data +source, mainly for convenience: + + ```c + ma_sound_seek_to_pcm_frame(&sound, frameIndex); + ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); + ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); + ma_sound_get_length_in_pcm_frames(&sound, &length); + ``` + +Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do +not have any notion of a data source, anything relating to a data source is unavailable. + +Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports +file formats that have built-in support in miniaudio. You can extend this to support any kind of +file format through the use of custom decoders. To do this you'll need to use a self-managed +resource manager and configure it appropriately. See the "Resource Management" section below for +details on how to set this up. + + +6. Resource Management +====================== +Many programs will want to manage sound resources for things such as reference counting and +streaming. This is supported by miniaudio via the `ma_resource_manager` API. + +The resource manager is mainly responsible for the following: + + * Loading of sound files into memory with reference counting. + * Streaming of sound data. + +When loading a sound file, the resource manager will give you back a `ma_data_source` compatible +object called `ma_resource_manager_data_source`. This object can be passed into any +`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you +specify whether or not you want the sound to be fully loaded into memory (and optionally +pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want +the data to be loaded asynchronously. + +The example below is how you can initialize a resource manager using it's default configuration: + + ```c + ma_resource_manager_config config; + ma_resource_manager resourceManager; + + config = ma_resource_manager_config_init(); + result = ma_resource_manager_init(&config, &resourceManager); + if (result != MA_SUCCESS) { + ma_device_uninit(&device); + printf("Failed to initialize the resource manager."); + return -1; + } + ``` + +You can configure the format, channels and sample rate of the decoded audio data. By default it +will use the file's native data format, but you can configure it to use a consistent format. This +is useful for offloading the cost of data conversion to load time rather than dynamically +converting at mixing time. To do this, you configure the decoded format, channels and sample rate +like the code below: + + ```c + config = ma_resource_manager_config_init(); + config.decodedFormat = device.playback.format; + config.decodedChannels = device.playback.channels; + config.decodedSampleRate = device.sampleRate; + ``` + +In the code above, the resource manager will be configured so that any decoded audio data will be +pre-converted at load time to the device's native data format. If instead you used defaults and +the data format of the file did not match the device's data format, you would need to convert the +data at mixing time which may be prohibitive in high-performance and large scale scenarios like +games. + +Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it +only supports decoders that are built into miniaudio. It's possible to support additional encoding +formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` +vtables into the resource manager config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; + resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + resourceManagerConfig.pCustomDecodingBackendUserData = NULL; + ``` + +This system can allow you to support any kind of file format. See the "Decoding" section for +details on how to implement custom decoders. The miniaudio repository includes examples for Opus +via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. + +Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the +decoding of a page, a job will be posted to a queue which will then be processed by a job thread. +By default there will be only one job thread running, but this can be configured, like so: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = MY_JOB_THREAD_COUNT; + ``` + +By default job threads are managed internally by the resource manager, however you can also self +manage your job threads if, for example, you want to integrate the job processing into your +existing job infrastructure, or if you simply don't like the way the resource manager does it. To +do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first +need to retrieve a job using `ma_resource_manager_next_job()` and then process it using +`ma_job_process()`: + + ```c + config = ma_resource_manager_config_init(); + config.jobThreadCount = 0; // Don't manage any job threads internally. + config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. + + // ... Initialize your custom job threads ... + + void my_custom_job_thread(...) + { + for (;;) { + ma_job job; + ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); + if (result != MA_SUCCESS) { + if (result == MA_NO_DATA_AVAILABLE) { + // No jobs are available. Keep going. Will only get this if the resource manager was initialized + // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. + continue; + } else if (result == MA_CANCELLED) { + // MA_JOB_TYPE_QUIT was posted. Exit. + break; + } else { + // Some other error occurred. + break; + } + } + + ma_job_process(&job); + } + } + ``` + +In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination +indicator, but you can use whatever you would like to terminate the thread. The call to +`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking +by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration +flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This +is to give every thread the opportunity to catch the event and terminate naturally. + +When loading a file, it's sometimes convenient to be able to customize how files are opened and +read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by +default. This can be done by setting `pVFS` member of the resource manager's config: + + ```c + // Initialize your custom VFS object. See documentation for VFS for information on how to do this. + my_custom_vfs vfs = my_custom_vfs_init(); + + config = ma_resource_manager_config_init(); + config.pVFS = &vfs; + ``` + +This is particularly useful in programs like games where you want to read straight from an archive +rather than the normal file system. If you do not specify a custom VFS, the resource manager will +use the operating system's normal file operations. + +To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When +loading a sound you need to specify the file path and options for how the sounds should be loaded. +By default a sound will be loaded synchronously. The returned data source is owned by the caller +which means the caller is responsible for the allocation and freeing of the data source. Below is +an example for initializing a data source: + + ```c + ma_resource_manager_data_source dataSource; + ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); + if (result != MA_SUCCESS) { + // Error. + } + + // ... + + // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call + // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. + result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read PCM frames. + } + + // ... + + ma_resource_manager_data_source_uninit(&dataSource); + ``` + +The `flags` parameter specifies how you want to perform loading of the sound file. It can be a +combination of the following flags: + + ``` + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT + ``` + +When no flags are specified (set to 0), the sound will be fully loaded into memory, but not +decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when +`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in +memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will +be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after +the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You +can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. +This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be +returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is +available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by +`ma_data_source_read_pcm_frames()`. + +For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you +can instead stream audio data which you can do by specifying the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 +second pages. When a new page needs to be decoded, a job will be posted to the job queue and then +subsequently processed in a job thread. + +For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means +multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in +the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be +matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful +for a program to register self-managed raw audio data and associate it with a file path. Use the +`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. +`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed +decoded audio data in the specified data format with the specified name. Likewise, +`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed +encoded audio data (the raw file data) with the specified name. Note that these names need not be +actual file paths. When `ma_resource_manager_data_source_init()` is called (without the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these +explicitly registered data buffers and, if found, will use it as the backing data for the data +source. Note that the resource manager does *not* make a copy of this data so it is up to the +caller to ensure the pointer stays valid for it's lifetime. Use +`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use +`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and +unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` +flag with a self-managed data pointer. + + +6.1. Asynchronous Loading and Synchronization +--------------------------------------------- +When loading asynchronously, it can be useful to poll whether or not loading has finished. Use +`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will +return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, +`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed +to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been +decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` +will be returned. Otherwise, some other error code will be returned if the sound failed to load. + +In addition to polling, you can also use a simple synchronization object called a "fence" to wait +for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a +fence is that it can be used to wait for a group of sounds to finish loading rather than waiting +for sounds on an individual basis. There are two stages to loading a sound: + + * Initialization of the internal decoder; and + * Completion of decoding of the file (the file is fully decoded) + +You can specify separate fences for each of the different stages. Waiting for the initialization +of the internal decoder is important for when you need to know the sample format, channels and +sample rate of the file. + +The example below shows how you could use a fence when loading a number of sounds: + + ```c + // This fence will be released when all sounds are finished loading entirely. + ma_fence fence; + ma_fence_init(&fence); + + // This will be passed into the initialization routine for each sound. + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pFence = &fence; + + // Now load a bunch of sounds: + for (iSound = 0; iSound < soundCount; iSound += 1) { + ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); + } + + // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... + + // Wait for loading of sounds to finish. + ma_fence_wait(&fence); + ``` + +In the example above we used a fence for waiting until the entire file has been fully decoded. If +you only need to wait for the initialization of the internal decoder to complete, you can use the +`init` member of the `ma_resource_manager_pipeline_notifications` object: + + ```c + notifications.init.pFence = &fence; + ``` + +If a fence is not appropriate for your situation, you can instead use a callback that is fired on +an individual sound basis. This is done in a very similar way to fences: + + ```c + typedef struct + { + ma_async_notification_callbacks cb; + void* pMyData; + } my_notification; + + void my_notification_callback(ma_async_notification* pNotification) + { + my_notification* pMyNotification = (my_notification*)pNotification; + + // Do something in response to the sound finishing loading. + } + + ... + + my_notification myCallback; + myCallback.cb.onSignal = my_notification_callback; + myCallback.pMyData = pMyData; + + ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); + notifications.done.pNotification = &myCallback; + + ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); + ``` + +In the example above we just extend the `ma_async_notification_callbacks` object and pass an +instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with +the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same +time and they should both work as expected. If using the `pNotification` system, you need to ensure +your `ma_async_notification_callbacks` object stays valid. + + + +6.2. Resource Manager Implementation Details +-------------------------------------------- +Resources are managed in two main ways: + + * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) + * By streaming audio data on the fly (referred to as a data stream) + +A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or +data stream, depending on whether or not the data source was initialized with the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a +`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` +object. Both of these objects are data sources which means they can be used with any +`ma_data_source_*()` API. + +Another major feature of the resource manager is the ability to asynchronously decode audio files. +This relieves the audio thread of time-consuming decoding which can negatively affect scalability +due to the audio thread needing to complete it's work extremely quickly to avoid glitching. +Asynchronous decoding is achieved through a job system. There is a central multi-producer, +multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is +posted to the queue which is then read by a job thread. The number of job threads can be +configured for improved scalability, and job threads can all run in parallel without needing to +worry about the order of execution (how this is achieved is explained below). + +When a sound is being loaded asynchronously, playback can begin before the sound has been fully +decoded. This enables the application to start playback of the sound quickly, while at the same +time allowing to resource manager to keep loading in the background. Since there may be less +threads than the number of sounds being loaded at a given time, a simple scheduling system is used +to keep decoding time balanced and fair. The resource manager solves this by splitting decoding +into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a +new job will be posted to start decoding the next page. By dividing up decoding into pages, an +individual sound shouldn't ever delay every other sound from having their first page decoded. Of +course, when loading many sounds at the same time, there will always be an amount of time required +to process jobs in the queue so in heavy load situations there will still be some delay. To +determine if a data source is ready to have some frames read, use +`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames +available starting from the current position. + + +6.2.1. Job Queue +---------------- +The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. +This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. +Only a fixed number of jobs can be allocated and inserted into the queue which is done through a +lock-free data structure for allocating an index into a fixed sized array, with reference counting +for mitigation of the ABA problem. The reference count is 32-bit. + +For many types of jobs it's important that they execute in a specific order. In these cases, jobs +are executed serially. For the resource manager, serial execution of jobs is only required on a +per-object basis (per data buffer or per data stream). Each of these objects stores an execution +counter. When a job is posted it is associated with an execution counter. When the job is +processed, it checks if the execution counter of the job equals the execution counter of the +owning object and if so, processes the job. If the counters are not equal, the job will be posted +back onto the job queue for later processing. When the job finishes processing the execution order +of the main object is incremented. This system means the no matter how many job threads are +executing, decoding of an individual sound will always get processed serially. The advantage to +having multiple threads comes into play when loading multiple sounds at the same time. + +The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve +thread-safety for a very small section of code. This is only relevant when the resource manager +uses more than one job thread. If only using a single job thread, which is the default, the +lock should never actually wait in practice. The amount of time spent locking should be quite +short, but it's something to be aware of for those who have pedantic lock-free requirements and +need to use more than one job thread. There are plans to remove this lock in a future version. + +In addition, posting a job will release a semaphore, which on Win32 is implemented with +`ReleaseSemaphore` and on POSIX platforms via a condition variable: + + ```c + pthread_mutex_lock(&pSemaphore->lock); + { + pSemaphore->value += 1; + pthread_cond_signal(&pSemaphore->cond); + } + pthread_mutex_unlock(&pSemaphore->lock); + ``` + +Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid +this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` +flag) and implement your own job processing routine (see the "Resource Manager" section above for +details on how to do this). + + + +6.2.2. Data Buffers +------------------- +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the +resource manager will try to load the data into an in-memory data buffer. Before doing so, however, +it will first check if the specified file is already loaded. If so, it will increment a reference +counter and just use the already loaded data. This saves both time and memory. When the data buffer +is uninitialized, the reference counter will be decremented. If the counter hits zero, the file +will be unloaded. This is a detail to keep in mind because it could result in excessive loading and +unloading of a sound. For example, the following sequence will result in a file be loaded twice, +once after the other: + + ```c + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. + ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. + + ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. + ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. + ``` + +A binary search tree (BST) is used for storing data buffers as it has good balance between +efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed +into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves +memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST +due to the random nature of the hash. The disadvantages are that file names are case-sensitive and +there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize +your file names to upper- or lower-case before initializing your data sources. If name collisions +become an issue, you'll need to change the name of one of the colliding names or just not use the +resource manager. + +When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` +flag is excluded, the file will be decoded synchronously by the calling thread. There are two +options for controlling how the audio is stored in the data buffer - encoded or decoded. When the +`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored +in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is +a very simple and standard process of simply adding an item to the BST, allocating a block of +memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). + +When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer +is done asynchronously. In this case, a job is posted to the queue to start loading and then the +function immediately returns, setting an internal result code to `MA_BUSY`. This result code is +returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully +completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. + +When loading asynchronously, a single job is posted to the queue of the type +`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and +associating it with job. When the job is processed by the job thread, it will first load the file +using the VFS associated with the resource manager. When using a custom VFS, it's important that it +be completely thread-safe because it will be used from one or more job threads at the same time. +Individual files should only ever be accessed by one thread at a time, however. After opening the +file via the VFS, the job will determine whether or not the file is being decoded. If not, it +simply allocates a block of memory and loads the raw file contents into it and returns. On the +other hand, when the file is being decoded, it will first allocate a decoder on the heap and +initialize it. Then it will check if the length of the file is known. If so it will allocate a +block of memory to store the decoded output and initialize it to silence. If the size is unknown, +it will allocate room for one page. After memory has been allocated, the first page will be +decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the +completion event will be signalled and loading is now complete. If, however, there is more to +decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job +will decode the next page and perform the same process if it reaches the end. If there is more to +decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will +keep on happening until the sound has been fully decoded. For sounds of an unknown length, each +page will be linked together as a linked list. Internally this is implemented via the +`ma_paged_audio_buffer` object. + + +6.2.3. Data Streams +------------------- +Data streams only ever store two pages worth of data for each instance. They are most useful for +large sounds like music tracks in games that would consume too much memory if fully decoded in +memory. After every frame from a page has been read, a job will be posted to load the next page +which is done from the VFS. + +For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or +not initialization of the data source waits until the two pages have been decoded. When unset, +`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise +it will return immediately. + +When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, +`MA_BUSY` will be returned if there are no frames available. If there are some frames available, +but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames +read will be less than the number requested. Due to the asynchronous nature of data streams, +seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be +returned when trying to read frames. + +When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed +a job is posted to load the next page. This will be posted from the same thread that called +`ma_resource_manager_data_source_read_pcm_frames()`. + +Data streams are uninitialized by posting a job to the queue, but the function won't return until +that job has been processed. The reason for this is that the caller owns the data stream object and +therefore miniaudio needs to ensure everything completes before handing back control to the caller. +Also, if the data stream is uninitialized while pages are in the middle of decoding, they must +complete before destroying any underlying object and the job system handles this cleanly. + +Note that when a new page needs to be loaded, a job will be posted to the resource manager's job +thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" +section above regarding locking when posting an event if you require a strictly lock-free audio +thread. + + + +7. Node Graph +============= +miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a +node whose outputs are attached to inputs of another node, thereby creating a graph. There are +different types of nodes, with each node in the graph processing input data to produce output, +which is then fed through the chain. Each node in the graph can apply their own custom effects. At +the start of the graph will usually be one or more data source nodes which have no inputs and +instead pull their data from a data source. At the end of the graph is an endpoint which represents +the end of the chain and is where the final output is ultimately extracted from. + +Each node has a number of input buses and a number of output buses. An output bus from a node is +attached to an input bus of another. Multiple nodes can connect their output buses to another +node's input bus, in which case their outputs will be mixed before processing by the node. Below is +a diagram that illustrates a hypothetical node graph setup: + + ``` + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + + +---------------+ +-----------------+ + | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ + +---------------+ | | =----+ +-----------------+ | +----------+ + +----= Splitter | +----= ENDPOINT | + +---------------+ | | =----+ +-----------------+ | +----------+ + | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ + +---------------+ +-----------------+ + ``` + +In the above graph, it starts with two data sources whose outputs are attached to the input of a +splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter +performs it's processing routine and produces two outputs which is simply a duplication of the +input stream. One output is attached to a low pass filter, whereas the other output is attached to +a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and +since they're both connected to the same input bus, they'll be mixed. + +Each input bus must be configured to accept the same number of channels, but the number of channels +used by input buses can be different to the number of channels for output buses in which case +miniaudio will automatically convert the input data to the output channel count before processing. +The number of channels of an output bus of one node must match the channel count of the input bus +it's attached to. The channel counts cannot be changed after the node has been initialized. If you +attempt to attach an output bus to an input bus with a different channel count, attachment will +fail. + +To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a +container around the entire graph. The `ma_node_graph` object is required for some thread-safety +issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's +standard config/init system: + + ```c + ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); + + result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. + if (result != MA_SUCCESS) { + // Failed to initialize node graph. + } + ``` + +When you initialize the node graph, you're specifying the channel count of the endpoint. The +endpoint is a special node which has one input bus and one output bus, both of which have the +same channel count, which is specified in the config. Any nodes that connect directly to the +endpoint must be configured such that their output buses have the same channel count. When you read +audio data from the node graph, it'll have the channel count you specified in the config. To read +data from the graph: + + ```c + ma_uint32 framesRead; + result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + // Failed to read data from the node graph. + } + ``` + +When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in +data from it's input attachments, which in turn recursively pull in data from their inputs, and so +on. At the start of the graph there will be some kind of data source node which will have zero +inputs and will instead read directly from a data source. The base nodes don't literally need to +read from a `ma_data_source` object, but they will always have some kind of underlying object that +sources some kind of audio. The `ma_data_source_node` node can be used to read from a +`ma_data_source`. Data is always in floating-point format and in the number of channels you +specified when the graph was initialized. The sample rate is defined by the underlying data sources. +It's up to you to ensure they use a consistent and appropriate sample rate. + +The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but +miniaudio includes a few stock nodes for common functionality. This is how you would initialize a +node which reads directly from a data source (`ma_data_source_node`) which is an example of one +of the stock nodes that comes with miniaudio: + + ```c + ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); + + ma_data_source_node dataSourceNode; + result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); + if (result != MA_SUCCESS) { + // Failed to create data source node. + } + ``` + +The data source node will use the output channel count to determine the channel count of the output +bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data +source). The data source must output to floating-point (`ma_format_f32`) or else an error will be +returned from `ma_data_source_node_init()`. + +By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: + + ```c + result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); + if (result != MA_SUCCESS) { + // Failed to attach node. + } + ``` + +The code above connects the data source node directly to the endpoint. Since the data source node +has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single +input bus which means the input bus index will also always be 0. + +To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use +`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to +another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll +deal with it for you. + +Less frequently you may want to create a specialized node. This will be a node where you implement +your own processing callback to apply a custom effect of some kind. This is similar to initializing +one of the stock node types, only this time you need to specify a pointer to a vtable containing a +pointer to the processing function and the number of input and output buses. Example: + + ```c + static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) + { + // Do some processing of ppFramesIn (one stream of audio data per input bus) + const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. + const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. + float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. + + // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each + // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers + // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames + // your node consumed and `pFrameCountOut` should be set the number of output frames that + // were produced. + // + // You should process as many frames as you can. If your effect consumes input frames at the + // same rate as output frames (always the case, unless you're doing resampling), you need + // only look at `ppFramesOut` and process that exact number of frames. If you're doing + // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` + // properly. + } + + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + 2, // 2 input buses. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + // Each bus needs to have a channel count specified. To do this you need to specify the channel + // counts in an array and then pass that into the node config. + ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. + ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable. + + inputChannels[0] = channelsIn; + inputChannels[1] = channelsIn; + outputChannels[0] = channelsOut; + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.pInputChannels = inputChannels; + nodeConfig.pOutputChannels = outputChannels; + + ma_node_base node; + result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); + if (result != MA_SUCCESS) { + // Failed to initialize node. + } + ``` + +When initializing a custom node, as in the code above, you'll normally just place your vtable in +static space. The number of input and output buses are specified as part of the vtable. If you need +a variable number of buses on a per-node bases, the vtable should have the relevant bus count set +to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: + + ```c + static ma_node_vtable my_custom_node_vtable = + { + my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. + NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. + MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. + 1, // 1 output bus. + 0 // Default flags. + }; + + ... + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &my_custom_node_vtable; + nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. + nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. + nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. + ``` + +In the above example it's important to never set the `inputBusCount` and `outputBusCount` members +to anything other than their defaults if the vtable specifies an explicit count. They can only be +set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. + +Most often you'll want to create a structure to encapsulate your node with some extra data. You +need to make sure the `ma_node_base` object is your first member of the structure: + + ```c + typedef struct + { + ma_node_base base; // <-- Make sure this is always the first member. + float someCustomData; + } my_custom_node; + ``` + +By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the +graph just like any other node. + +In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the +number of channels for each bus is what was specified by the config when the node was initialized +with `ma_node_init()`. In addition, all attachments to each of the input buses will have been +pre-mixed by miniaudio. The config allows you to specify different channel counts for each +individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, +return an error in it's initialization routine. + +Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable +and include the following: + + +-----------------------------------------+---------------------------------------------------+ + | Flag Name | Description | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | + | | processing, but are instead used for tracking | + | | time, handling events, etc. Also used by the | + | | internal endpoint node. It reads directly from | + | | the input bus to the output bus. Nodes with this | + | | flag must have exactly 1 input bus and 1 output | + | | bus, and both buses must have the same channel | + | | counts. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | + | | when no data is available to be read from input | + | | attachments. When a node has at least one input | + | | bus, but there are no inputs attached or the | + | | inputs do not deliver any data, the node's | + | | processing callback will not get fired. This flag | + | | will make it so the callback is always fired | + | | regardless of whether or not any input data is | + | | received. This is useful for effects like | + | | echos where there will be a tail of audio data | + | | that still needs to be processed even when the | + | | original data sources have reached their ends. It | + | | may also be useful for nodes that must always | + | | have their processing callback fired when there | + | | are no inputs attached. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | + | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | + | | is set, the `ppFramesIn` parameter of the | + | | processing callback will be set to NULL when | + | | there are no input frames are available. When | + | | this is unset, silence will be posted to the | + | | processing callback. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | + | | frames are processed at different rates. You | + | | should set this for any nodes that perform | + | | resampling. | + +-----------------------------------------+---------------------------------------------------+ + | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | + | | silent output. This is useful for nodes where you | + | | don't want the output to contribute to the final | + | | mix. An example might be if you want split your | + | | stream and have one branch be output to a file. | + | | When using this flag, you should avoid writing to | + | | the output buffer of the node's processing | + | | callback because miniaudio will ignore it anyway. | + +-----------------------------------------+---------------------------------------------------+ + + +If you need to make a copy of an audio stream for effect processing you can use a splitter node +called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. +You can use it like this: + + ```c + ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); + + ma_splitter_node splitterNode; + result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); + if (result != MA_SUCCESS) { + // Failed to create node. + } + + // Attach your output buses to two different input buses (can be on two different nodes). + ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. + ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. + ``` + +The volume of an output bus can be configured on a per-bus basis: + + ```c + ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); + ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); + ``` + +In the code above we're using the splitter node from before and changing the volume of each of the +copied streams. + +You can start and stop a node with the following: + + ```c + ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. + ma_node_set_state(&splitterNode, ma_node_state_stopped); + ``` + +By default the node is in a started state, but since it won't be connected to anything won't +actually be invoked by the node graph until it's connected. When you stop a node, data will not be +read from any of it's input connections. You can use this property to stop a group of sounds +atomically. + +You can configure the initial state of a node in it's config: + + ```c + nodeConfig.initialState = ma_node_state_stopped; + ``` + +Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member +which is the config to use with the base node. This is where the initial state can be configured +for specialized nodes: + + ```c + dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; + ``` + +When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not +modify the `vtable` member of the `nodeConfig` object. + + +7.1. Timing +----------- +The node graph supports starting and stopping nodes at scheduled times. This is especially useful +for data source nodes where you want to get the node set up, but only start playback at a specific +time. There are two clocks: local and global. + +A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can +only be done based on the global clock because the local clock will not be running while the node +is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the +other hand, the local clock only advances when the node's processing callback is fired, and is +advanced based on the output frame count. + +To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with +`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. +Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, +and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the +audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these +outside of the node processing callbacks which are always run on the audio thread. + +There is basic support for scheduling the starting and stopping of nodes. You can only schedule one +start and one stop at a time. This is mainly intended for putting nodes into a started or stopped +state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited +to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks +of several milliseconds. The following APIs can be used for scheduling node states: + + ```c + ma_node_set_state_time() + ma_node_get_state_time() + ``` + +The time is absolute and must be based on the global clock. An example is below: + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. + ``` + +An example for changing the state using a relative time. + + ```c + ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); + ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); + ``` + +Note that due to the nature of multi-threading the times may not be 100% exact. If this is an +issue, consider scheduling state changes from within a processing callback. An idea might be to +have some kind of passthrough trigger node that is used specifically for tracking time and handling +events. + + + +7.2. Thread Safety and Locking +------------------------------ +When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's +expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so +without the use of any locks. This section discusses the implementation used by miniaudio and goes +over some of the compromises employed by miniaudio to achieve this goal. Note that the current +implementation may not be ideal - feedback and critiques are most welcome. + +The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected +to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the +implementation, but are crafted in a way such that such locking is not required when reading audio +data from the graph. Locking in these areas are achieved by means of spinlocks. + +The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact +that a node can be uninitialized, and it's memory potentially freed, while in the middle of being +processed on the audio thread. There are times when the audio thread will be referencing a node, +which means the uninitialization process of a node needs to make sure it delays returning until the +audio thread is finished so that control is not handed back to the caller thereby giving them a +chance to free the node's memory. + +When the audio thread is processing a node, it does so by reading from each of the output buses of +the node. In order for a node to process data for one of it's output buses, it needs to read from +each of it's input buses, and so on an so forth. It follows that once all output buses of a node +are detached, the node as a whole will be disconnected and no further processing will occur unless +it's output buses are reattached, which won't be happening when the node is being uninitialized. +By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can +simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By +doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output +nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean +up. + +With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as +it takes to process the output bus being detached. This will happen if it's called at just the +wrong moment where the audio thread has just iterated it and has just started processing. The +caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which +includes the cost of recursively processing it's inputs. This is the biggest compromise made with +the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes +earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching +higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass +detachments, detach starting from the lowest level nodes and work your way towards the final +endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not +running, detachment will be fast and detachment in any order will be the same. The reason nodes +need to wait for their input attachments to complete is due to the potential for desyncs between +data sources. If the node was to terminate processing mid way through processing it's inputs, +there's a chance that some of the underlying data sources will have been read, but then others not. +That will then result in a potential desynchronization when detaching and reattaching higher-level +nodes. A possible solution to this is to have an option when detaching to terminate processing +before processing all input attachments which should be fairly simple. + +Another compromise, albeit less significant, is locking when attaching and detaching nodes. This +locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present +for each input bus and output bus. When an output bus is connected to an input bus, both the output +bus and input bus is locked. This locking is specifically for attaching and detaching across +different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and +unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when +considering that iterating over attachments must not break as a result of attaching or detaching a +node while iteration is occurring. + +Attaching and detaching are both quite simple. When an output bus of a node is attached to an input +bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where +each item in the list is and output bus. We have some intentional (and convenient) restrictions on +what can done with the linked list in order to simplify the implementation. First of all, whenever +something needs to iterate over the list, it must do so in a forward direction. Backwards iteration +is not supported. Also, items can only be added to the start of the list. + +The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer +to the next item in the list, and another to the previous item. A pointer to the previous item is +only required for fast detachment of the node - it is never used in iteration. This is an +important property because it means from the perspective of iteration, attaching and detaching of +an item can be done with a single atomic assignment. This is exploited by both the attachment and +detachment process. When attaching the node, the first thing that is done is the setting of the +local "next" and "previous" pointers of the node. After that, the item is "attached" to the list +by simply performing an atomic exchange with the head pointer. After that, the node is "attached" +to the list from the perspective of iteration. Even though the "previous" pointer of the next item +hasn't yet been set, from the perspective of iteration it's been attached because iteration will +only be happening in a forward direction which means the "previous" pointer won't actually ever get +used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and +`ma_node_detach_output_bus()` for the implementation of this mechanism. + + + +8. Decoding +=========== +The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from +devices and can be used independently. Built-in support is included for the following formats: + + +---------+ + | Format | + +---------+ + | WAV | + | MP3 | + | FLAC | + +---------+ + +You can disable the built-in decoders by specifying one or more of the following options before the +miniaudio implementation: + + ```c + #define MA_NO_WAV + #define MA_NO_MP3 + #define MA_NO_FLAC + ``` + +miniaudio supports the ability to plug in custom decoders. See the section below for details on how +to use custom decoders. + +A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with +`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is +an example for loading a decoder from a file: + + ```c + ma_decoder decoder; + ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); + if (result != MA_SUCCESS) { + return false; // An error occurred. + } + + ... + + ma_decoder_uninit(&decoder); + ``` + +When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object +(the `NULL` argument in the example above) which allows you to configure the output format, channel +count, sample rate and channel map: + + ```c + ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); + ``` + +When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the +same as that defined by the decoding backend. + +Data is read from the decoder as PCM frames. This will output the number of PCM frames actually +read. If this is less than the requested number of PCM frames it means you've reached the end. The +return value will be `MA_AT_END` if no samples have been read and the end has been reached. + + ```c + ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); + if (framesRead < framesToRead) { + // Reached the end. + } + ``` + +You can also seek to a specific frame like so: + + ```c + ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); + if (result != MA_SUCCESS) { + return false; // An error occurred. + } + ``` + +If you want to loop back to the start, you can simply seek back to the first PCM frame: + + ```c + ma_decoder_seek_to_pcm_frame(pDecoder, 0); + ``` + +When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding +backend. This can be unnecessarily inefficient if the type is already known. In this case you can +use `encodingFormat` variable in the device config to specify a specific encoding format you want +to decode: + + ```c + decoderConfig.encodingFormat = ma_encoding_format_wav; + ``` + +See the `ma_encoding_format` enum for possible encoding formats. + +The `ma_decoder_init_file()` API will try using the file extension to determine which decoding +backend to prefer. + + +8.1. Custom Decoders +-------------------- +It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful +when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of +the stock formats supported by miniaudio. This can be put to particularly good use when using the +`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for +example, you wanted to support Opus, you can do so with a custom decoder (there if a reference +Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). + +A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs +to be implemented which is then passed into the decoder config: + + ```c + ma_decoding_backend_vtable* pCustomBackendVTables[] = + { + &g_ma_decoding_backend_vtable_libvorbis, + &g_ma_decoding_backend_vtable_libopus + }; + + ... + + decoderConfig = ma_decoder_config_init_default(); + decoderConfig.pCustomBackendUserData = NULL; + decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; + decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); + ``` + +The `ma_decoding_backend_vtable` vtable has the following functions: + + ``` + onInit + onInitFile + onInitFileW + onInitMemory + onUninit + ``` + +There are only two functions that must be implemented - `onInit` and `onUninit`. The other +functions can be implemented for a small optimization for loading from a file path or memory. If +these are not specified, miniaudio will deal with it for you via a generic implementation. + +When you initialize a custom data source (by implementing the `onInit` function in the vtable) you +will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the +section about data sources for details on how to implement this. Alternatively, see the +"custom_decoders" example in the miniaudio repository. + +The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data +from some arbitrary source. You'll use these functions to read from the raw data and perform the +decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant +parameter. + +The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only +used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, +an optimal implementation will handle the relevant properties appropriately. + +If memory allocation is required, it should be done so via the specified allocation callbacks if +possible (the `pAllocationCallbacks` parameter). + +If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to +NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. +When multiple custom backends are specified, miniaudio will cycle through the vtables in the order +they're listed in the array that's passed into the decoder config so it's important that your +initialization routine is clean. + +When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an +opportunity to clean up and internal data. + + + +9. Encoding +=========== +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. +This can be disabled by specifying the following option before the implementation of miniaudio: + + ```c + #define MA_NO_WAV + ``` + +An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data +delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder +to output to a file. + + ```c + ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); + ma_encoder encoder; + ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); + if (result != MA_SUCCESS) { + // Error + } + + ... + + ma_encoder_uninit(&encoder); + ``` + +When initializing an encoder you must specify a config which is initialized with +`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output +channel count and output sample rate. The following file types are supported: + + +------------------------+-------------+ + | Enum | Description | + +------------------------+-------------+ + | ma_encoding_format_wav | WAV | + +------------------------+-------------+ + +If the format, channel count or sample rate is not supported by the output file type an error will +be returned. The encoder will not perform data conversion so you will need to convert it before +outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the +example below: + + ```c + ma_uint64 framesWritten; + result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); + if (result != MA_SUCCESS) { + ... handle error ... + } + ``` + +The `framesWritten` variable will contain the number of PCM frames that were actually written. This +is optionally and you can pass in `NULL` if you need this. + +Encoders must be uninitialized with `ma_encoder_uninit()`. + + + +10. Data Conversion +=================== +A data conversion API is included with miniaudio which supports the majority of data conversion +requirements. This supports conversion between sample formats, channel counts (with channel +mapping) and sample rates. + + +10.1. Sample Format Conversion +------------------------------ +Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and +`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific +formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use +`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count +and channel count as a variable instead of the total sample count. + + +10.1.1. Dithering +----------------- +Dithering can be set using the ditherMode parameter. + +The different dithering modes include the following, in order of efficiency: + + +-----------+--------------------------+ + | Type | Enum Token | + +-----------+--------------------------+ + | None | ma_dither_mode_none | + | Rectangle | ma_dither_mode_rectangle | + | Triangle | ma_dither_mode_triangle | + +-----------+--------------------------+ + +Note that even if the dither mode is set to something other than `ma_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 ma_dither_mode_none for conversions where +dither is not used. It will just be ignored. + + + +10.2. Channel Conversion +------------------------ +Channel conversion is used for channel rearrangement and conversion from one channel count to +another. The `ma_channel_converter` API is used for channel conversion. Below is an example of +initializing a simple channel converter which converts from mono to stereo. + + ```c + ma_channel_converter_config config = ma_channel_converter_config_init( + ma_format, // Sample format + 1, // Input channels + NULL, // Input channel map + 2, // Output channels + NULL, // Output channel map + ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. + + result = ma_channel_converter_init(&config, NULL, &converter); + if (result != MA_SUCCESS) { + // Error. + } + ``` + +To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: + + ```c + ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); + if (result != MA_SUCCESS) { + // Error. + } + ``` + +It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM +frames. + +Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. + + +10.2.1. Channel Mapping +----------------------- +In addition to converting from one channel count to another, like the example above, the channel +converter can also be used to rearrange channels. When initializing the channel converter, you can +optionally pass in channel maps for both the input and output frames. If the channel counts are the +same, and each channel map contains the same channel positions with the exception that they're in +a different order, a simple shuffling of the channels will be performed. If, however, there is not +a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed +based on a mixing mode which is specified when initializing the `ma_channel_converter_config` +object. + +When converting from mono to multi-channel, the mono channel is simply copied to each output +channel. When going the other way around, the audio of each output channel is simply averaged and +copied to the mono channel. + +In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess +channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th +channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and +4th channels. + +The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a +simple distribution between input and output. Imagine sitting in the middle of a room, with +speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be +thought of as being in the corner of the front and left walls. + +Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined +weights. Custom weights can be passed in as the last parameter of +`ma_channel_converter_config_init()`. + +Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a +`ma_standard_channel_map` enum as it's first parameter, which can be one of the following: + + +-----------------------------------+-----------------------------------------------------------+ + | Name | Description | + +-----------------------------------+-----------------------------------------------------------+ + | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | + | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | + | ma_standard_channel_map_alsa | Default ALSA channel map. | + | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | + | ma_standard_channel_map_flac | FLAC channel map. | + | ma_standard_channel_map_vorbis | Vorbis channel map. | + | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | + | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | + | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | + +-----------------------------------+-----------------------------------------------------------+ + +Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): + + +---------------+---------------------------------+ + | Channel Count | Mapping | + +---------------+---------------------------------+ + | 1 (Mono) | 0: MA_CHANNEL_MONO | + +---------------+---------------------------------+ + | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT | + +---------------+---------------------------------+ + | 3 | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER | + +---------------+---------------------------------+ + | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER
    | + | | 3: MA_CHANNEL_BACK_CENTER | + +---------------+---------------------------------+ + | 5 | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER
    | + | | 3: MA_CHANNEL_BACK_LEFT
    | + | | 4: MA_CHANNEL_BACK_RIGHT | + +---------------+---------------------------------+ + | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER
    | + | | 3: MA_CHANNEL_LFE
    | + | | 4: MA_CHANNEL_SIDE_LEFT
    | + | | 5: MA_CHANNEL_SIDE_RIGHT | + +---------------+---------------------------------+ + | 7 | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER
    | + | | 3: MA_CHANNEL_LFE
    | + | | 4: MA_CHANNEL_BACK_CENTER
    | + | | 4: MA_CHANNEL_SIDE_LEFT
    | + | | 5: MA_CHANNEL_SIDE_RIGHT | + +---------------+---------------------------------+ + | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
    | + | | 1: MA_CHANNEL_FRONT_RIGHT
    | + | | 2: MA_CHANNEL_FRONT_CENTER
    | + | | 3: MA_CHANNEL_LFE
    | + | | 4: MA_CHANNEL_BACK_LEFT
    | + | | 5: MA_CHANNEL_BACK_RIGHT
    | + | | 6: MA_CHANNEL_SIDE_LEFT
    | + | | 7: MA_CHANNEL_SIDE_RIGHT | + +---------------+---------------------------------+ + | Other | All channels set to 0. This | + | | is equivalent to the same | + | | mapping as the device. | + +---------------+---------------------------------+ + + + +10.3. Resampling +---------------- +Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something +like the following: + + ```c + ma_resampler_config config = ma_resampler_config_init( + ma_format_s16, + channels, + sampleRateIn, + sampleRateOut, + ma_resample_algorithm_linear); + + ma_resampler resampler; + ma_result result = ma_resampler_init(&config, &resampler); + if (result != MA_SUCCESS) { + // An error occurred... + } + ``` + +Do the following to uninitialize the resampler: + + ```c + ma_resampler_uninit(&resampler); + ``` + +The following example shows how data can be processed + + ```c + ma_uint64 frameCountIn = 1000; + ma_uint64 frameCountOut = 2000; + ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); + if (result != MA_SUCCESS) { + // An error occurred... + } + + // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the + // number of output frames written. + ``` + +To initialize the resampler you first need to set up a config (`ma_resampler_config`) with +`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of +channels, the input and output sample rate, and the algorithm. + +The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format +you will need to perform pre- and post-conversions yourself where necessary. Note that the format +is the same for both input and output. The format cannot be changed after initialization. + +The resampler supports multiple channels and is always interleaved (both input and output). The +channel count cannot be changed after initialization. + +The sample rates can be anything other than zero, and are always specified in hertz. They should be +set to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization. + +The miniaudio resampler has built-in support for the following algorithms: + + +-----------+------------------------------+ + | Algorithm | Enum Token | + +-----------+------------------------------+ + | Linear | ma_resample_algorithm_linear | + | Custom | ma_resample_algorithm_custom | + +-----------+------------------------------+ + +The algorithm cannot be changed after initialization. + +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you +can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large buffer of zeros. The output +buffer can also be NULL, in which case the processing will be treated as seek. + +The sample rate can be changed dynamically on the fly. You can change this with explicit sample +rates with `ma_resampler_set_rate()` and also with a decimal ratio with +`ma_resampler_set_rate_ratio()`. The ratio is in/out. + +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. + +Due to the nature of how resampling works, the resampler introduces some latency. This can be +retrieved in terms of both the input rate and the output rate with +`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. + + +10.3.1. Resampling Algorithms +----------------------------- +The choice of resampling algorithm depends on your situation and requirements. + + +10.3.1.1. Linear Resampling +--------------------------- +The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, +some control over the quality of the linear resampler which may make it a suitable option depending +on your requirements. + +The linear resampler performs low-pass filtering before or after downsampling or upsampling, +depending on the sample rates you're converting between. When decreasing the sample rate, the +low-pass filter will be applied before downsampling. When increasing the rate it will be performed +after upsampling. By default a fourth order low-pass filter will be applied. This can be configured +via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. + +The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of +the input and output sample rates (Nyquist Frequency). + +The API for the linear resampler is the same as the main resampler API, only it's called +`ma_linear_resampler`. + + +10.3.2. Custom Resamplers +------------------------- +You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling +algorithm and setting a vtable in the resampler config: + + ```c + ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); + config.pBackendVTable = &g_customResamplerVTable; + ``` + +Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You +need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all +functions in the vtable need to be implemented, but if it's possible to implement, they should be. + +You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The +`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom +resampler will need to make given the supplied config. When you initialize the resampler via the +`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store +the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage +it for you. + +The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` +points to a variable containing the number of frames in the `pFramesIn` buffer and +`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. +On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, +whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. + +The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If +dynamic rate changes are not supported, you can set this callback to NULL. + +The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in +input and output rates respectively. These can be NULL in which case latency calculations will be +assumed to be NULL. + +The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input +frames are required to be available to produce the given number of output frames. Likewise, the +`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be +produced given the specified number of input frames. miniaudio will use these as a hint, but they +are optional and can be set to NULL if you're unable to implement them. + + + +10.4. General Data Conversion +----------------------------- +The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and +resampling into one operation. This is what miniaudio uses internally to convert between the format +requested when the device was initialized and the format of the backend's native device. The API +for general data conversion is very similar to the resampling API. Create a `ma_data_converter` +object like this: + + ```c + ma_data_converter_config config = ma_data_converter_config_init( + inputFormat, + outputFormat, + inputChannels, + outputChannels, + inputSampleRate, + outputSampleRate + ); + + ma_data_converter converter; + ma_result result = ma_data_converter_init(&config, NULL, &converter); + if (result != MA_SUCCESS) { + // An error occurred... + } + ``` + +In the example above we use `ma_data_converter_config_init()` to initialize the config, however +there's many more properties that can be configured, such as channel maps and resampling quality. +Something like the following may be more suitable depending on your requirements: + + ```c + ma_data_converter_config config = ma_data_converter_config_init_default(); + config.formatIn = inputFormat; + config.formatOut = outputFormat; + config.channelsIn = inputChannels; + config.channelsOut = outputChannels; + config.sampleRateIn = inputSampleRate; + config.sampleRateOut = outputSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); + config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; + ``` + +Do the following to uninitialize the data converter: + + ```c + ma_data_converter_uninit(&converter, NULL); + ``` + +The following example shows how data can be processed + + ```c + ma_uint64 frameCountIn = 1000; + ma_uint64 frameCountOut = 2000; + ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); + if (result != MA_SUCCESS) { + // An error occurred... + } + + // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number + // of output frames written. + ``` + +The data converter supports multiple channels and is always interleaved (both input and output). +The channel count cannot be changed after initialization. + +Sample rates can be anything other than zero, and are always specified in hertz. They should be set +to something like 44100, etc. The sample rate is the only configuration property that can be +changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of +`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use +`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. +The resampling algorithm cannot be changed after initialization. + +Processing always happens on a per PCM frame basis and always assumes interleaved input and output. +De-interleaved processing is not supported. To process frames, use +`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames +you can fit in the output buffer and the number of input frames contained in the input buffer. On +output these variables contain the number of output frames that were written to the output buffer +and the number of input frames that were consumed in the process. You can pass in NULL for the +input buffer in which case it will be treated as an infinitely large +buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated +as seek. + +Sometimes it's useful to know exactly how many input frames will be required to output a specific +number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. +Likewise, it's sometimes useful to know exactly how many frames would be output given a certain +number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. + +Due to the nature of how resampling works, the data converter introduces some latency if resampling +is required. This can be retrieved in terms of both the input rate and the output rate with +`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. + + + +11. Filtering +============= + +11.1. Biquad Filtering +---------------------- +Biquad filtering is achieved with the `ma_biquad` API. Example: + + ```c + ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); + ma_result result = ma_biquad_init(&config, &biquad); + if (result != MA_SUCCESS) { + // Error. + } + + ... + + ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); + ``` + +Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, +b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and +coefficients must not be pre-normalized. + +Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use +fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. + +Input and output frames are always interleaved. + +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: + + ```c + ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); + ``` + +If you need to change the values of the coefficients, but maintain the values in the registers you +can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the +filter while keeping the values of registers valid to avoid glitching. Do not use +`ma_biquad_init()` for this as it will do a full initialization which involves clearing the +registers to 0. Note that changing the format or channel count after initialization is invalid and +will result in an error. + + +11.2. Low-Pass Filtering +------------------------ +Low-pass filtering is achieved with the following APIs: + + +---------+------------------------------------------+ + | API | Description | + +---------+------------------------------------------+ + | ma_lpf1 | First order low-pass filter | + | ma_lpf2 | Second order low-pass filter | + | ma_lpf | High order low-pass filter (Butterworth) | + +---------+------------------------------------------+ + +Low-pass filter example: + + ```c + ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + ma_result result = ma_lpf_init(&config, &lpf); + if (result != MA_SUCCESS) { + // Error. + } + + ... + + ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); + ``` + +Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format +you need to convert it yourself beforehand. Input and output frames are always interleaved. + +Filtering can be applied in-place by passing in the same pointer for both the input and output +buffers, like so: + + ```c + ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); + ``` + +The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, +you can chain first and second order filters together. + + ```c + for (iFilter = 0; iFilter < filterCount; iFilter += 1) { + ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); + } + ``` + +If you need to change the configuration of the filter, but need to maintain the state of internal +registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample +rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the +format or channel count after initialization is invalid and will result in an error. + +The `ma_lpf` object supports a configurable order, but if you only need a first order filter you +may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use +`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. + +If an even filter order is specified, a series of second order filters will be processed in a +chain. If an odd filter order is specified, a first order filter will be applied, followed by a +series of second order filters in a chain. + + +11.3. High-Pass Filtering +------------------------- +High-pass filtering is achieved with the following APIs: + + +---------+-------------------------------------------+ + | API | Description | + +---------+-------------------------------------------+ + | ma_hpf1 | First order high-pass filter | + | ma_hpf2 | Second order high-pass filter | + | ma_hpf | High order high-pass filter (Butterworth) | + +---------+-------------------------------------------+ + +High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, +`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. + + +11.4. Band-Pass Filtering +------------------------- +Band-pass filtering is achieved with the following APIs: + + +---------+-------------------------------+ + | API | Description | + +---------+-------------------------------+ + | ma_bpf2 | Second order band-pass filter | + | ma_bpf | High order band-pass filter | + +---------+-------------------------------+ + +Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and +`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for +band-pass filters must be an even number which means there is no first order band-pass filter, +unlike low-pass and high-pass filters. + + +11.5. Notch Filtering +--------------------- +Notch filtering is achieved with the following APIs: + + +-----------+------------------------------------------+ + | API | Description | + +-----------+------------------------------------------+ + | ma_notch2 | Second order notching filter | + +-----------+------------------------------------------+ + + +11.6. Peaking EQ Filtering +------------------------- +Peaking filtering is achieved with the following APIs: + + +----------+------------------------------------------+ + | API | Description | + +----------+------------------------------------------+ + | ma_peak2 | Second order peaking filter | + +----------+------------------------------------------+ + + +11.7. Low Shelf Filtering +------------------------- +Low shelf filtering is achieved with the following APIs: + + +-------------+------------------------------------------+ + | API | Description | + +-------------+------------------------------------------+ + | ma_loshelf2 | Second order low shelf filter | + +-------------+------------------------------------------+ + +Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to +just turn them down rather than eliminate them entirely. + + +11.8. High Shelf Filtering +-------------------------- +High shelf filtering is achieved with the following APIs: + + +-------------+------------------------------------------+ + | API | Description | + +-------------+------------------------------------------+ + | ma_hishelf2 | Second order high shelf filter | + +-------------+------------------------------------------+ + +The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` +instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, +the high shelf filter does the same thing for high frequencies. + + + + +12. Waveform and Noise Generation +================================= + +12.1. Waveforms +--------------- +miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved +with the `ma_waveform` API. Example: + + ```c + ma_waveform_config config = ma_waveform_config_init( + FORMAT, + CHANNELS, + SAMPLE_RATE, + ma_waveform_type_sine, + amplitude, + frequency); + + ma_waveform waveform; + ma_result result = ma_waveform_init(&config, &waveform); + if (result != MA_SUCCESS) { + // Error. + } + + ... + + ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); + ``` + +The amplitude, frequency, type, and sample rate can be changed dynamically with +`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and +`ma_waveform_set_sample_rate()` respectively. + +You can invert the waveform by setting the amplitude to a negative value. You can use this to +control whether or not a sawtooth has a positive or negative ramp, for example. + +Below are the supported waveform types: + + +---------------------------+ + | Enum Name | + +---------------------------+ + | ma_waveform_type_sine | + | ma_waveform_type_square | + | ma_waveform_type_triangle | + | ma_waveform_type_sawtooth | + +---------------------------+ + + + +12.2. Noise +----------- +miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: + + ```c + ma_noise_config config = ma_noise_config_init( + FORMAT, + CHANNELS, + ma_noise_type_white, + SEED, + amplitude); + + ma_noise noise; + ma_result result = ma_noise_init(&config, &noise); + if (result != MA_SUCCESS) { + // Error. + } + + ... + + ma_noise_read_pcm_frames(&noise, pOutput, frameCount); + ``` + +The noise API uses simple LCG random number generation. It supports a custom seed which is useful +for things like automated testing requiring reproducibility. Setting the seed to zero will default +to `MA_DEFAULT_LCG_SEED`. + +The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and +`ma_noise_set_seed()` respectively. + +By default, the noise API will use different values for different channels. So, for example, the +left side in a stereo stream will be different to the right side. To instead have each channel use +the same random value, set the `duplicateChannels` member of the noise config to true, like so: + + ```c + config.duplicateChannels = MA_TRUE; + ``` + +Below are the supported noise types. + + +------------------------+ + | Enum Name | + +------------------------+ + | ma_noise_type_white | + | ma_noise_type_pink | + | ma_noise_type_brownian | + +------------------------+ + + + +13. Audio Buffers +================= +miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can +read from memory that's managed by the application, but can also handle the memory management for +you internally. Memory management is flexible and should support most use cases. + +Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: + + ```c + ma_audio_buffer_config config = ma_audio_buffer_config_init( + format, + channels, + sizeInFrames, + pExistingData, + &allocationCallbacks); + + ma_audio_buffer buffer; + result = ma_audio_buffer_init(&config, &buffer); + if (result != MA_SUCCESS) { + // Error. + } + + ... + + ma_audio_buffer_uninit(&buffer); + ``` + +In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an +application can do self-managed memory allocation. If you would rather make a copy of the data, use +`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. + +Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the +raw audio data in a contiguous block of memory. That is, the raw audio data will be located +immediately after the `ma_audio_buffer` structure. To do this, use +`ma_audio_buffer_alloc_and_init()`: + + ```c + ma_audio_buffer_config config = ma_audio_buffer_config_init( + format, + channels, + sizeInFrames, + pExistingData, + &allocationCallbacks); + + ma_audio_buffer* pBuffer + result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); + if (result != MA_SUCCESS) { + // Error + } + + ... + + ma_audio_buffer_uninit_and_free(&buffer); + ``` + +If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it +with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by +`pExistingData` will be copied into the buffer, which is contrary to the behavior of +`ma_audio_buffer_init()`. + +An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the +cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should +loop. The return value is the number of frames actually read. If this is less than the number of +frames requested it means the end has been reached. This should never happen if the `loop` +parameter is set to true. If you want to manually loop back to the start, you can do so with with +`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an +audio buffer. + + ```c + ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); + if (framesRead < desiredFrameCount) { + // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. + } + ``` + +Sometimes you may want to avoid the cost of data movement between the internal buffer and the +output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: + + ```c + void* pMappedFrames; + ma_uint64 frameCount = frameCountToTryMapping; + ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); + if (result == MA_SUCCESS) { + // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be + // less due to the end of the buffer being reached. + ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); + + // You must unmap the buffer. + ma_audio_buffer_unmap(pAudioBuffer, frameCount); + } + ``` + +When you use memory mapping, the read cursor is increment by the frame count passed in to +`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller +than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is +that it does not handle looping for you. You can determine if the buffer is at the end for the +purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of +`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` +as an error when returned by `ma_audio_buffer_unmap()`. + + + +14. Ring Buffers +================ +miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via +the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` +operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around +`ma_rb`. + +Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved +streams. The caller can also allocate their own backing memory for the ring buffer to use +internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for +you. + +The examples below use the PCM frame variant of the ring buffer since that's most likely the one +you will want to use. To initialize a ring buffer, do something like the following: + + ```c + ma_pcm_rb rb; + ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); + if (result != MA_SUCCESS) { + // Error + } + ``` + +The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because +it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you +would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes +instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter +is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. +Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. + +Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is +offset from each other based on the stride. To manage your sub-buffers you can use +`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and +`ma_pcm_rb_get_subbuffer_ptr()`. + +Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section +of the ring buffer. You specify the number of frames you need, and on output it will set to what +was actually acquired. If the read or write pointer is positioned such that the number of frames +requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number +of frames you're given may be less than the number you requested. + +After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the +buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is +where the read/write pointers are updated. When you commit you need to pass in the buffer that was +returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is +only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and +`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was +originally requested. + +If you want to correct for drift between the write pointer and the read pointer you can use a +combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and +`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only +move the read pointer forward via the consumer thread, and the write pointer forward by the +producer thread. If there is too much space between the pointers, move the read pointer forward. If +there is too little space between the pointers, move the write pointer forward. + +You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` +API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and +instead of frame counts you will pass around byte counts. + +The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most +significant bit being used to encode a loop flag and the internally managed buffers always being +aligned to `MA_SIMD_ALIGNMENT`. + +Note that the ring buffer is only thread safe when used by a single consumer thread and single +producer thread. + + + +15. Backends +============ +The following backends are supported by miniaudio. These are listed in order of default priority. +When no backend is specified when initializing a context or device, miniaudio will attempt to use +each of these backends in the order listed in the table below. + +Note that backends that are not usable by the build target will not be included in the build. For +example, ALSA, which is specific to Linux, will not be included in the Windows build. + + +-------------+-----------------------+--------------------------------------------------------+ + | Name | Enum Name | Supported Operating Systems | + +-------------+-----------------------+--------------------------------------------------------+ + | WASAPI | ma_backend_wasapi | Windows Vista+ | + | DirectSound | ma_backend_dsound | Windows XP+ | + | WinMM | ma_backend_winmm | Windows 95+ | + | Core Audio | ma_backend_coreaudio | macOS, iOS | + | sndio | ma_backend_sndio | OpenBSD | + | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | + | OSS | ma_backend_oss | FreeBSD | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | ALSA | ma_backend_alsa | Linux | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | + | AAudio | ma_backend_aaudio | Android 8+ | + | OpenSL ES | ma_backend_opensl | Android (API level 16+) | + | Web Audio | ma_backend_webaudio | Web (via Emscripten) | + | Custom | ma_backend_custom | Cross Platform | + | Null | ma_backend_null | Cross Platform (not used on Web) | + +-------------+-----------------------+--------------------------------------------------------+ + +Some backends have some nuance details you may want to be aware of. + +15.1. WASAPI +------------ +- Low-latency shared mode will be disabled when using an application-defined sample rate which is + different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` + to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing + when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC + will result in miniaudio's internal resampler being used instead which will in turn enable the + use of low-latency shared mode. + +15.2. PulseAudio +---------------- +- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: + https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. + Alternatively, consider using a different backend such as ALSA. + +15.3. Android +------------- +- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: + `` +- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a + limitation with OpenSL|ES. +- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration + API (devices are enumerated through Java). You can however perform your own device enumeration + through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it + to ma_device_init(). +- The backend API will perform resampling where possible. The reason for this as opposed to using + miniaudio's built-in resampler is to take advantage of any potential device-specific + optimizations the driver may implement. + +BSD +--- +- 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. + +15.4. UWP +--------- +- UWP only supports default playback and capture devices. +- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): + + ``` + + ... + + + + + ``` + +15.5. Web Audio / Emscripten +---------------------------- +- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. +- The first time a context is initialized it will create a global object called "miniaudio" whose + primary purpose is to act as a factory for device objects. +- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as + they've been deprecated. +- Google has implemented a policy in their browsers that prevent automatic media output without + first receiving some kind of user input. The following web page has additional details: + https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device + may fail if you try to start playback without first handling some kind of user input. + + + +16. Optimization Tips +===================== +See below for some tips on improving performance. + +16.1. Low Level API +------------------- +- In the data callback, if your data is already clipped prior to copying it into the output buffer, + set the `noClip` config option in the device config to true. This will disable miniaudio's built + in clipping function. +- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you + will always write valid data to the output buffer you can disable pre-silencing by setting the + `noPreSilence` config option in the device config to true. + +16.2. High Level API +-------------------- +- If a sound does not require doppler or pitch shifting, consider disabling pitching by + initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. +- If a sound does not require spatialization, disable it by initializing the sound with the + `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with + `ma_sound_set_spatialization_enabled()`. +- If you know all of your sounds will always be the same sample rate, set the engine's sample + rate to match that of the sounds. Likewise, if you're using a self-managed resource manager, + consider setting the decoded sample rate to match your sounds. By configuring everything to + use a consistent sample rate, sample rate conversion can be avoided. + + + +17. Miscellaneous Notes +======================= +- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for + WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though + not all have been tested. +- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This + is due to 64-bit file APIs not being available. +*/ + +#ifndef miniaudio_h +#define miniaudio_h + +#ifdef __cplusplus +extern "C" { +#endif + +#define MA_STRINGIFY(x) #x +#define MA_XSTRINGIFY(x) MA_STRINGIFY(x) + +#define MA_VERSION_MAJOR 0 +#define MA_VERSION_MINOR 11 +#define MA_VERSION_REVISION 21 +#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ + #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif + + + +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) + #define MA_SIZEOF_PTR 8 +#else + #define MA_SIZEOF_PTR 4 +#endif + +#include /* For size_t. */ + +/* Sized types. */ +#if defined(MA_USE_STDINT) + #include + typedef int8_t ma_int8; + typedef uint8_t ma_uint8; + typedef int16_t ma_int16; + typedef uint16_t ma_uint16; + typedef int32_t ma_int32; + typedef uint32_t ma_uint32; + typedef int64_t ma_int64; + typedef uint64_t ma_uint64; +#else + typedef signed char ma_int8; + typedef unsigned char ma_uint8; + typedef signed short ma_int16; + typedef unsigned short ma_uint16; + typedef signed int ma_int32; + typedef unsigned int ma_uint32; + #if defined(_MSC_VER) && !defined(__clang__) + typedef signed __int64 ma_int64; + typedef unsigned __int64 ma_uint64; + #else + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif + #endif + typedef signed long long ma_int64; + typedef unsigned long long ma_uint64; + #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop + #endif + #endif +#endif /* MA_USE_STDINT */ + +#if MA_SIZEOF_PTR == 8 + typedef ma_uint64 ma_uintptr; +#else + typedef ma_uint32 ma_uintptr; +#endif + +typedef ma_uint8 ma_bool8; +typedef ma_uint32 ma_bool32; +#define MA_TRUE 1 +#define MA_FALSE 0 + +/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */ +typedef float ma_float; +typedef double ma_double; + +typedef void* ma_handle; +typedef void* ma_ptr; + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else +typedef void* ma_proc; +#endif + +#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) +typedef ma_uint16 wchar_t; +#endif + +/* Define NULL for some compilers. */ +#ifndef NULL +#define NULL 0 +#endif + +#if defined(SIZE_MAX) + #define MA_SIZE_MAX SIZE_MAX +#else + #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ +#endif + + +/* Platform/backend detection. */ +#if defined(_WIN32) || defined(__COSMOPOLITAN__) + #define MA_WIN32 + #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + #define MA_WIN32_UWP + #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) + #define MA_WIN32_GDK + #else + #define MA_WIN32_DESKTOP + #endif +#endif +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ + #define MA_POSIX + + /* + Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. + You can use this to avoid including pthread.h in the header section. The downside is that it + results in some fixed sized structures being declared for the various types that are used in + miniaudio. The risk here is that these types might be too small for a given platform. This + risk is yours to take and no support will be offered if you enable this option. + */ + #ifndef MA_NO_PTHREAD_IN_HEADER + #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ + typedef pthread_t ma_pthread_t; + typedef pthread_mutex_t ma_pthread_mutex_t; + typedef pthread_cond_t ma_pthread_cond_t; + #else + typedef ma_uintptr ma_pthread_t; + typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; + typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; + #endif + + #if defined(__unix__) + #define MA_UNIX + #endif + #if defined(__linux__) + #define MA_LINUX + #endif + #if defined(__APPLE__) + #define MA_APPLE + #endif + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) + #define MA_ANDROID + #endif + #if defined(__EMSCRIPTEN__) + #define MA_EMSCRIPTEN + #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif + #if defined(__NX__) + #define MA_NX + #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif +#endif + +#if defined(__has_c_attribute) + #if __has_c_attribute(fallthrough) + #define MA_FALLTHROUGH [[fallthrough]] + #endif +#endif +#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__)) + #if __has_attribute(fallthrough) + #define MA_FALLTHROUGH __attribute__((fallthrough)) + #endif +#endif +#if !defined(MA_FALLTHROUGH) + #define MA_FALLTHROUGH ((void)0) +#endif + +#ifdef _MSC_VER + #define MA_INLINE __forceinline + + /* noinline was introduced in Visual Studio 2005. */ + #if _MSC_VER >= 1400 + #define MA_NO_INLINE __declspec(noinline) + #else + #define MA_NO_INLINE + #endif +#elif defined(__GNUC__) + /* + I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when + the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some + case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the + command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue + I am using "__inline__" only when we're compiling in strict ANSI mode. + */ + #if defined(__STRICT_ANSI__) + #define MA_GNUC_INLINE_HINT __inline__ + #else + #define MA_GNUC_INLINE_HINT inline + #endif + + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) + #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) + #define MA_NO_INLINE __attribute__((noinline)) + #else + #define MA_INLINE MA_GNUC_INLINE_HINT + #define MA_NO_INLINE __attribute__((noinline)) + #endif +#elif defined(__WATCOMC__) + #define MA_INLINE __inline + #define MA_NO_INLINE +#else + #define MA_INLINE + #define MA_NO_INLINE +#endif + +/* MA_DLL is not officially supported. You're on your own if you want to use this. */ +#if defined(MA_DLL) + #if defined(_WIN32) + #define MA_DLL_IMPORT __declspec(dllimport) + #define MA_DLL_EXPORT __declspec(dllexport) + #define MA_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define MA_DLL_IMPORT __attribute__((visibility("default"))) + #define MA_DLL_EXPORT __attribute__((visibility("default"))) + #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define MA_DLL_IMPORT + #define MA_DLL_EXPORT + #define MA_DLL_PRIVATE static + #endif + #endif +#endif + +#if !defined(MA_API) + #if defined(MA_DLL) + #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) + #define MA_API MA_DLL_EXPORT + #else + #define MA_API MA_DLL_IMPORT + #endif + #else + #define MA_API extern + #endif +#endif + +#if !defined(MA_STATIC) + #if defined(MA_DLL) + #define MA_PRIVATE MA_DLL_PRIVATE + #else + #define MA_PRIVATE static + #endif +#endif + + +/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ +#define MA_SIMD_ALIGNMENT 32 + +/* +Special wchar_t type to ensure any structures in the public sections that reference it have a +consistent size across all platforms. + +On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use +wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +platforms. +*/ +#if !defined(MA_POSIX) && defined(MA_WIN32) +typedef wchar_t ma_wchar_win32; +#else +typedef ma_uint16 ma_wchar_win32; +#endif + + + +/* +Logging Levels +============== +Log levels are only used to give logging callbacks some context as to the severity of a log message +so they can do filtering. All log levels will be posted to registered logging callbacks. If you +don't want to output a certain log level you can discriminate against the log level in the callback. + +MA_LOG_LEVEL_DEBUG + Used for debugging. Useful for debug and test builds, but should be disabled in release builds. + +MA_LOG_LEVEL_INFO + Informational logging. Useful for debugging. This will never be called from within the data + callback. + +MA_LOG_LEVEL_WARNING + Warnings. You should enable this in you development builds and action them when encounted. These + logs usually indicate a potential problem or misconfiguration, but still allow you to keep + running. This will never be called from within the data callback. + +MA_LOG_LEVEL_ERROR + Error logging. This will be fired when an operation fails and is subsequently aborted. This can + be fired from within the data callback, in which case the device will be stopped. You should + always have this log level enabled. +*/ +typedef enum +{ + MA_LOG_LEVEL_DEBUG = 4, + MA_LOG_LEVEL_INFO = 3, + MA_LOG_LEVEL_WARNING = 2, + MA_LOG_LEVEL_ERROR = 1 +} ma_log_level; + +/* +Variables needing to be accessed atomically should be declared with this macro for two reasons: + + 1) It allows people who read the code to identify a variable as such; and + 2) It forces alignment on platforms where it's required or optimal. + +Note that for x86/64, alignment is not strictly necessary, but does have some performance +implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU +architecture does not require it, it will simply leave it unaligned. This is the case with old +versions of Visual Studio, which I've confirmed with at least VC6. +*/ +#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) + #include + #define MA_ATOMIC(alignment, type) _Alignas(alignment) type +#else + #if defined(__GNUC__) + /* GCC-style compilers. */ + #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) + #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ + /* MSVC. */ + #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type + #else + /* Other compilers. */ + #define MA_ATOMIC(alignment, type) type + #endif +#endif + +typedef struct ma_context ma_context; +typedef struct ma_device ma_device; + +typedef ma_uint8 ma_channel; +typedef enum +{ + MA_CHANNEL_NONE = 0, + MA_CHANNEL_MONO = 1, + MA_CHANNEL_FRONT_LEFT = 2, + MA_CHANNEL_FRONT_RIGHT = 3, + MA_CHANNEL_FRONT_CENTER = 4, + MA_CHANNEL_LFE = 5, + MA_CHANNEL_BACK_LEFT = 6, + MA_CHANNEL_BACK_RIGHT = 7, + MA_CHANNEL_FRONT_LEFT_CENTER = 8, + MA_CHANNEL_FRONT_RIGHT_CENTER = 9, + MA_CHANNEL_BACK_CENTER = 10, + MA_CHANNEL_SIDE_LEFT = 11, + MA_CHANNEL_SIDE_RIGHT = 12, + MA_CHANNEL_TOP_CENTER = 13, + MA_CHANNEL_TOP_FRONT_LEFT = 14, + MA_CHANNEL_TOP_FRONT_CENTER = 15, + MA_CHANNEL_TOP_FRONT_RIGHT = 16, + MA_CHANNEL_TOP_BACK_LEFT = 17, + MA_CHANNEL_TOP_BACK_CENTER = 18, + MA_CHANNEL_TOP_BACK_RIGHT = 19, + MA_CHANNEL_AUX_0 = 20, + MA_CHANNEL_AUX_1 = 21, + MA_CHANNEL_AUX_2 = 22, + MA_CHANNEL_AUX_3 = 23, + MA_CHANNEL_AUX_4 = 24, + MA_CHANNEL_AUX_5 = 25, + MA_CHANNEL_AUX_6 = 26, + MA_CHANNEL_AUX_7 = 27, + MA_CHANNEL_AUX_8 = 28, + MA_CHANNEL_AUX_9 = 29, + MA_CHANNEL_AUX_10 = 30, + MA_CHANNEL_AUX_11 = 31, + MA_CHANNEL_AUX_12 = 32, + MA_CHANNEL_AUX_13 = 33, + MA_CHANNEL_AUX_14 = 34, + MA_CHANNEL_AUX_15 = 35, + MA_CHANNEL_AUX_16 = 36, + MA_CHANNEL_AUX_17 = 37, + MA_CHANNEL_AUX_18 = 38, + MA_CHANNEL_AUX_19 = 39, + MA_CHANNEL_AUX_20 = 40, + MA_CHANNEL_AUX_21 = 41, + MA_CHANNEL_AUX_22 = 42, + MA_CHANNEL_AUX_23 = 43, + MA_CHANNEL_AUX_24 = 44, + MA_CHANNEL_AUX_25 = 45, + MA_CHANNEL_AUX_26 = 46, + MA_CHANNEL_AUX_27 = 47, + MA_CHANNEL_AUX_28 = 48, + MA_CHANNEL_AUX_29 = 49, + MA_CHANNEL_AUX_30 = 50, + MA_CHANNEL_AUX_31 = 51, + MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, + MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, + MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) +} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ + +typedef enum +{ + MA_SUCCESS = 0, + MA_ERROR = -1, /* A generic error. */ + MA_INVALID_ARGS = -2, + MA_INVALID_OPERATION = -3, + MA_OUT_OF_MEMORY = -4, + MA_OUT_OF_RANGE = -5, + MA_ACCESS_DENIED = -6, + MA_DOES_NOT_EXIST = -7, + MA_ALREADY_EXISTS = -8, + MA_TOO_MANY_OPEN_FILES = -9, + MA_INVALID_FILE = -10, + MA_TOO_BIG = -11, + MA_PATH_TOO_LONG = -12, + MA_NAME_TOO_LONG = -13, + MA_NOT_DIRECTORY = -14, + MA_IS_DIRECTORY = -15, + MA_DIRECTORY_NOT_EMPTY = -16, + MA_AT_END = -17, + MA_NO_SPACE = -18, + MA_BUSY = -19, + MA_IO_ERROR = -20, + MA_INTERRUPT = -21, + MA_UNAVAILABLE = -22, + MA_ALREADY_IN_USE = -23, + MA_BAD_ADDRESS = -24, + MA_BAD_SEEK = -25, + MA_BAD_PIPE = -26, + MA_DEADLOCK = -27, + MA_TOO_MANY_LINKS = -28, + MA_NOT_IMPLEMENTED = -29, + MA_NO_MESSAGE = -30, + MA_BAD_MESSAGE = -31, + MA_NO_DATA_AVAILABLE = -32, + MA_INVALID_DATA = -33, + MA_TIMEOUT = -34, + MA_NO_NETWORK = -35, + MA_NOT_UNIQUE = -36, + MA_NOT_SOCKET = -37, + MA_NO_ADDRESS = -38, + MA_BAD_PROTOCOL = -39, + MA_PROTOCOL_UNAVAILABLE = -40, + MA_PROTOCOL_NOT_SUPPORTED = -41, + MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, + MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, + MA_SOCKET_NOT_SUPPORTED = -44, + MA_CONNECTION_RESET = -45, + MA_ALREADY_CONNECTED = -46, + MA_NOT_CONNECTED = -47, + MA_CONNECTION_REFUSED = -48, + MA_NO_HOST = -49, + MA_IN_PROGRESS = -50, + MA_CANCELLED = -51, + MA_MEMORY_ALREADY_MAPPED = -52, + + /* General non-standard errors. */ + MA_CRC_MISMATCH = -100, + + /* General miniaudio-specific errors. */ + MA_FORMAT_NOT_SUPPORTED = -200, + MA_DEVICE_TYPE_NOT_SUPPORTED = -201, + MA_SHARE_MODE_NOT_SUPPORTED = -202, + MA_NO_BACKEND = -203, + MA_NO_DEVICE = -204, + MA_API_NOT_FOUND = -205, + MA_INVALID_DEVICE_CONFIG = -206, + MA_LOOP = -207, + MA_BACKEND_NOT_ENABLED = -208, + + /* State errors. */ + MA_DEVICE_NOT_INITIALIZED = -300, + MA_DEVICE_ALREADY_INITIALIZED = -301, + MA_DEVICE_NOT_STARTED = -302, + MA_DEVICE_NOT_STOPPED = -303, + + /* Operation errors. */ + MA_FAILED_TO_INIT_BACKEND = -400, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, + MA_FAILED_TO_START_BACKEND_DEVICE = -402, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 +} ma_result; + + +#define MA_MIN_CHANNELS 1 +#ifndef MA_MAX_CHANNELS +#define MA_MAX_CHANNELS 254 +#endif + +#ifndef MA_MAX_FILTER_ORDER +#define MA_MAX_FILTER_ORDER 8 +#endif + +typedef enum +{ + ma_stream_format_pcm = 0 +} ma_stream_format; + +typedef enum +{ + ma_stream_layout_interleaved = 0, + ma_stream_layout_deinterleaved +} ma_stream_layout; + +typedef enum +{ + ma_dither_mode_none = 0, + ma_dither_mode_rectangle, + ma_dither_mode_triangle +} ma_dither_mode; + +typedef enum +{ + /* + I like to keep these explicitly defined because they're used as a key into a lookup table. When items are + added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). + */ + ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ + ma_format_u8 = 1, + ma_format_s16 = 2, /* Seems to be the most widely supported format. */ + ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ + ma_format_s32 = 4, + ma_format_f32 = 5, + ma_format_count +} ma_format; + +typedef enum +{ + /* Standard rates need to be in priority order. */ + ma_standard_sample_rate_48000 = 48000, /* Most common */ + ma_standard_sample_rate_44100 = 44100, + + ma_standard_sample_rate_32000 = 32000, /* Lows */ + ma_standard_sample_rate_24000 = 24000, + ma_standard_sample_rate_22050 = 22050, + + ma_standard_sample_rate_88200 = 88200, /* Highs */ + ma_standard_sample_rate_96000 = 96000, + ma_standard_sample_rate_176400 = 176400, + ma_standard_sample_rate_192000 = 192000, + + ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ + ma_standard_sample_rate_11025 = 11025, + ma_standard_sample_rate_8000 = 8000, + + ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ + ma_standard_sample_rate_384000 = 384000, + + ma_standard_sample_rate_min = ma_standard_sample_rate_8000, + ma_standard_sample_rate_max = ma_standard_sample_rate_384000, + ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ +} ma_standard_sample_rate; + + +typedef enum +{ + ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ + ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ + ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ + ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular +} ma_channel_mix_mode; + +typedef enum +{ + ma_standard_channel_map_microsoft, + ma_standard_channel_map_alsa, + ma_standard_channel_map_rfc3551, /* Based off AIFF. */ + ma_standard_channel_map_flac, + ma_standard_channel_map_vorbis, + ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ + ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ + ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ + ma_standard_channel_map_default = ma_standard_channel_map_microsoft +} ma_standard_channel_map; + +typedef enum +{ + ma_performance_profile_low_latency = 0, + ma_performance_profile_conservative +} ma_performance_profile; + + +typedef struct +{ + void* pUserData; + void* (* onMalloc)(size_t sz, void* pUserData); + void* (* onRealloc)(void* p, size_t sz, void* pUserData); + void (* onFree)(void* p, void* pUserData); +} ma_allocation_callbacks; + +typedef struct +{ + ma_int32 state; +} ma_lcg; + + +/* +Atomics. + +These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too +easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By +using a struct we can enforce the use of atomics at compile time. + +These types are declared in the header section because we need to reference them in structs below, but functions for +using them are only exposed in the implementation section. I do not want these to be part of the public API. + +There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are +some macros to help with the declarations. They will be named like so: + + ma_atomic_uint32 - atomic ma_uint32 + ma_atomic_int32 - atomic ma_int32 + ma_atomic_uint64 - atomic ma_uint64 + ma_atomic_float - atomic float + ma_atomic_bool32 - atomic ma_bool32 + +The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific +type of pointer you need to make atomic. For example, an atomic ma_node* will look like this: + + MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node) + +Which will declare a type struct that's named like so: + + ma_atomic_ptr_node + +Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with +the name of the struct. For example: + + ma_atomic_uint32_set() - Atomic store of ma_uint32 + ma_atomic_uint32_get() - Atomic load of ma_uint32 + etc. + +For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in +return you get type safety and enforcement of atomic operations. +*/ +#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \ + typedef struct \ + { \ + MA_ATOMIC(typeSize, ma_##type) value; \ + } ma_atomic_##type; \ + +#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \ + typedef struct \ + { \ + MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \ + } ma_atomic_ptr_##type; \ + +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32) +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32) +MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64) +MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float) +MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) + + +/* Spinlocks are 32-bit for compatibility reasons. */ +typedef ma_uint32 ma_spinlock; + +#ifndef MA_NO_THREADING + /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ + typedef enum + { + ma_thread_priority_idle = -5, + ma_thread_priority_lowest = -4, + ma_thread_priority_low = -3, + ma_thread_priority_normal = -2, + ma_thread_priority_high = -1, + ma_thread_priority_highest = 0, + ma_thread_priority_realtime = 1, + ma_thread_priority_default = 0 + } ma_thread_priority; + + #if defined(MA_POSIX) + typedef ma_pthread_t ma_thread; + #elif defined(MA_WIN32) + typedef ma_handle ma_thread; + #endif + + #if defined(MA_POSIX) + typedef ma_pthread_mutex_t ma_mutex; + #elif defined(MA_WIN32) + typedef ma_handle ma_mutex; + #endif + + #if defined(MA_POSIX) + typedef struct + { + ma_uint32 value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_event; + #elif defined(MA_WIN32) + typedef ma_handle ma_event; + #endif + + #if defined(MA_POSIX) + typedef struct + { + int value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_semaphore; + #elif defined(MA_WIN32) + typedef ma_handle ma_semaphore; + #endif +#else + /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ + #ifndef MA_NO_DEVICE_IO + #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; + #endif +#endif /* MA_NO_THREADING */ + + +/* +Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. +*/ +MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); + +/* +Retrieves the version of miniaudio as a string which can be useful for logging purposes. +*/ +MA_API const char* ma_version_string(void); + + +/************************************************************************************************************************************************************** + +Logging + +**************************************************************************************************************************************************************/ +#include /* For va_list. */ + +#if defined(__has_attribute) + #if __has_attribute(format) + #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) + #endif +#endif +#ifndef MA_ATTRIBUTE_FORMAT +#define MA_ATTRIBUTE_FORMAT(fmt, va) +#endif + +#ifndef MA_MAX_LOG_CALLBACKS +#define MA_MAX_LOG_CALLBACKS 4 +#endif + + +/* +The callback for handling log messages. + + +Parameters +---------- +pUserData (in) + The user data pointer that was passed into ma_log_register_callback(). + +logLevel (in) + The log level. This can be one of the following: + + +----------------------+ + | Log Level | + +----------------------+ + | MA_LOG_LEVEL_DEBUG | + | MA_LOG_LEVEL_INFO | + | MA_LOG_LEVEL_WARNING | + | MA_LOG_LEVEL_ERROR | + +----------------------+ + +pMessage (in) + The log message. +*/ +typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); + +typedef struct +{ + ma_log_callback_proc onLog; + void* pUserData; +} ma_log_callback; + +MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); + + +typedef struct +{ + ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; + ma_uint32 callbackCount; + ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ +#ifndef MA_NO_THREADING + ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ +#endif +} ma_log; + +MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); +MA_API void ma_log_uninit(ma_log* pLog); +MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); +MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); +MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); +MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); +MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); + + +/************************************************************************************************************************************************************** + +Biquad Filtering + +**************************************************************************************************************************************************************/ +typedef union +{ + float f32; + ma_int32 s32; +} ma_biquad_coefficient; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + double b0; + double b1; + double b2; + double a0; + double a1; + double a2; +} ma_biquad_config; + +MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient b0; + ma_biquad_coefficient b1; + ma_biquad_coefficient b2; + ma_biquad_coefficient a1; + ma_biquad_coefficient a2; + ma_biquad_coefficient* pR1; + ma_biquad_coefficient* pR2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_biquad; + +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); +MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); + + +/************************************************************************************************************************************************************** + +Low-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_lpf1_config, ma_lpf2_config; + +MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); +MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient a; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_lpf1; + +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); +MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); + +typedef struct +{ + ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ +} ma_lpf2; + +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); +MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_lpf_config; + +MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_lpf1* pLPF1; + ma_lpf2* pLPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_lpf; + +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); +MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); + + +/************************************************************************************************************************************************************** + +High-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_hpf1_config, ma_hpf2_config; + +MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); +MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_biquad_coefficient a; + ma_biquad_coefficient* pR1; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_hpf1; + +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); +MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); + +typedef struct +{ + ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ +} ma_hpf2; + +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); +MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_hpf_config; + +MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_hpf1* pHPF1; + ma_hpf2* pHPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_hpf; + +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); +MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); + + +/************************************************************************************************************************************************************** + +Band-Pass Filtering + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + double q; +} ma_bpf2_config; + +MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); + +typedef struct +{ + ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ +} ma_bpf2; + +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); +MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double cutoffFrequency; + ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ +} ma_bpf_config; + +MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 bpf2Count; + ma_bpf2* pBPF2; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_bpf; + +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); +MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); + + +/************************************************************************************************************************************************************** + +Notching Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double q; + double frequency; +} ma_notch2_config, ma_notch_config; + +MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_notch2; + +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); +MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); + + +/************************************************************************************************************************************************************** + +Peaking EQ Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double q; + double frequency; +} ma_peak2_config, ma_peak_config; + +MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_peak2; + +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); +MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); + + +/************************************************************************************************************************************************************** + +Low Shelf Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double shelfSlope; + double frequency; +} ma_loshelf2_config, ma_loshelf_config; + +MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_loshelf2; + +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); +MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); + + +/************************************************************************************************************************************************************** + +High Shelf Filter + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double gainDB; + double shelfSlope; + double frequency; +} ma_hishelf2_config, ma_hishelf_config; + +MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); + +typedef struct +{ + ma_biquad bq; +} ma_hishelf2; + +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); +MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); + + + +/* +Delay +*/ +typedef struct +{ + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 delayInFrames; + ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ + float wet; /* 0..1. Default = 1. */ + float dry; /* 0..1. Default = 1. */ + float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ +} ma_delay_config; + +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_delay_config config; + ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ + ma_uint32 bufferSizeInFrames; + float* pBuffer; +} ma_delay; + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); +MA_API float ma_delay_get_wet(const ma_delay* pDelay); +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); +MA_API float ma_delay_get_dry(const ma_delay* pDelay); +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); +MA_API float ma_delay_get_decay(const ma_delay* pDelay); + + +/* Gainer for smooth volume changes. */ +typedef struct +{ + ma_uint32 channels; + ma_uint32 smoothTimeInFrames; +} ma_gainer_config; + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); + + +typedef struct +{ + ma_gainer_config config; + ma_uint32 t; + float masterVolume; + float* pOldGains; + float* pNewGains; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_gainer; + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume); +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume); + + + +/* Stereo panner. */ +typedef enum +{ + ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ + ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ +} ma_pan_mode; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; +} ma_panner_config; + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_pan_mode mode; + float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ +} ma_panner; + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); +MA_API float ma_panner_get_pan(const ma_panner* pPanner); + + + +/* Fader. */ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_fader_config; + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); + +typedef struct +{ + ma_fader_config config; + float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ + float volumeEnd; + ma_uint64 lengthInFrames; /* The total length of the fade. */ + ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ +} ma_fader; + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); +MA_API float ma_fader_get_current_volume(const ma_fader* pFader); + + + +/* Spatializer. */ +typedef struct +{ + float x; + float y; + float z; +} ma_vec3f; + +typedef struct +{ + ma_vec3f v; + ma_spinlock lock; +} ma_atomic_vec3f; + +typedef enum +{ + ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ + ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ + ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ + ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ +} ma_attenuation_model; + +typedef enum +{ + ma_positioning_absolute, + ma_positioning_relative +} ma_positioning; + +typedef enum +{ + ma_handedness_right, + ma_handedness_left +} ma_handedness; + + +typedef struct +{ + ma_uint32 channelsOut; + ma_channel* pChannelMapOut; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float speedOfSound; + ma_vec3f worldUp; +} ma_spatializer_listener_config; + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); + + +typedef struct +{ + ma_spatializer_listener_config config; + ma_atomic_vec3f position; /* The absolute position of the listener. */ + ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ + ma_atomic_vec3f velocity; + ma_bool32 isEnabled; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_spatializer_listener; + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ +} ma_spatializer_config; + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); + + +typedef struct +{ + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel* pChannelMapIn; + ma_attenuation_model attenuationModel; + ma_positioning positioning; + ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ + float minGain; + float maxGain; + float minDistance; + float maxDistance; + float rolloff; + float coneInnerAngleInRadians; + float coneOuterAngleInRadians; + float coneOuterGain; + float dopplerFactor; /* Set to 0 to disable doppler effect. */ + float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ + ma_atomic_vec3f position; + ma_atomic_vec3f direction; + ma_atomic_vec3f velocity; /* For doppler effect. */ + float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + float minSpatializationChannelGain; + ma_gainer gainer; /* For smooth gain transitions. */ + float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_spatializer; + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume); +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume); +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DATA CONVERSION +=============== + +This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ + +/************************************************************************************************************************************************************** + +Resampling + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ + double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ +} ma_linear_resampler_config; + +MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + +typedef struct +{ + ma_linear_resampler_config config; + ma_uint32 inAdvanceInt; + ma_uint32 inAdvanceFrac; + ma_uint32 inTimeInt; + ma_uint32 inTimeFrac; + union + { + float* f32; + ma_int16* s16; + } x0; /* The previous input frame. */ + union + { + float* f32; + ma_int16* s16; + } x1; /* The next input frame. */ + ma_lpf lpf; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_linear_resampler; + +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); +MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); +MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); +MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); +MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); + + +typedef struct ma_resampler_config ma_resampler_config; + +typedef void ma_resampling_backend; +typedef struct +{ + ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); + ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); + void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); + ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); + ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ + ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ + ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ + ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); +} ma_resampling_backend_vtable; + +typedef enum +{ + ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ + ma_resample_algorithm_custom, +} ma_resample_algorithm; + +struct ma_resampler_config +{ + ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + struct + { + ma_uint32 lpfOrder; + } linear; +}; + +MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); + +typedef struct +{ + ma_resampling_backend* pBackend; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + union + { + ma_linear_resampler linear; + } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_resampler; + +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); + +/* +Initializes a new resampler object from a config. +*/ +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); + +/* +Uninitializes a resampler. +*/ +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Converts the given input data. + +Both the input and output frames must be in the format specified in the config when the resampler was initialized. + +On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that +were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use +ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. + +On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole +input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames +you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. + +If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of +output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input +frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be +processed. In this case, any internal filter state will be updated as if zeroes were passed in. + +It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. + +It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. +*/ +MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); + + +/* +Sets the input and output sample rate. +*/ +MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + +/* +Sets the input and output sample rate as a ratio. + +The ration is in/out. +*/ +MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); + +/* +Retrieves the latency introduced by the resampler in input frames. +*/ +MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); + +/* +Retrieves the latency introduced by the resampler in output frames. +*/ +MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); + +/* +Calculates the number of whole input frames that would need to be read from the client in order to output the specified +number of output frames. + +The returned value does not include cached input frames. It only returns the number of extra frames that would need to be +read from the input buffer in order to output the specified number of output frames. +*/ +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); + +/* +Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of +input frames. +*/ +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); + +/* +Resets the resampler's timer and clears it's internal cache. +*/ +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); + + +/************************************************************************************************************************************************************** + +Channel Conversion + +**************************************************************************************************************************************************************/ +typedef enum +{ + ma_channel_conversion_path_unknown, + ma_channel_conversion_path_passthrough, + ma_channel_conversion_path_mono_out, /* Converting to mono. */ + ma_channel_conversion_path_mono_in, /* Converting from mono. */ + ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ + ma_channel_conversion_path_weights /* Blended based on weights. */ +} ma_channel_conversion_path; + +typedef enum +{ + ma_mono_expansion_mode_duplicate = 0, /* The default. */ + ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ + ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ + ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate +} ma_mono_expansion_mode; + +typedef struct +{ + ma_format format; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + const ma_channel* pChannelMapIn; + const ma_channel* pChannelMapOut; + ma_channel_mix_mode mixingMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ +} ma_channel_converter_config; + +MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); + +typedef struct +{ + ma_format format; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel_mix_mode mixingMode; + ma_channel_conversion_path conversionPath; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; + ma_uint8* pShuffleTable; /* Indexed by output channel index. */ + union + { + float** f32; + ma_int32** s16; + } weights; /* [in][out] */ + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_channel_converter; + +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); + + +/************************************************************************************************************************************************************** + +Data Conversion + +**************************************************************************************************************************************************************/ +typedef struct +{ + ma_format formatIn; + ma_format formatOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_channel* pChannelMapIn; + ma_channel* pChannelMapOut; + ma_dither_mode ditherMode; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ + ma_bool32 allowDynamicSampleRate; + ma_resampler_config resampling; +} ma_data_converter_config; + +MA_API ma_data_converter_config ma_data_converter_config_init_default(void); +MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); + + +typedef enum +{ + ma_data_converter_execution_path_passthrough, /* No conversion. */ + ma_data_converter_execution_path_format_only, /* Only format conversion. */ + ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ + ma_data_converter_execution_path_resample_only, /* Only resampling. */ + ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ + ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ +} ma_data_converter_execution_path; + +typedef struct +{ + ma_format formatIn; + ma_format formatOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRateIn; + ma_uint32 sampleRateOut; + ma_dither_mode ditherMode; + ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ + ma_channel_converter channelConverter; + ma_resampler resampler; + ma_bool8 hasPreFormatConversion; + ma_bool8 hasPostFormatConversion; + ma_bool8 hasChannelConverter; + ma_bool8 hasResampler; + ma_bool8 isPassthrough; + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; +} ma_data_converter; + +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); +MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); +MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); +MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); +MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); + + +/************************************************************************************************************************************************************ + +Format Conversion + +************************************************************************************************************************************************************/ +MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); +MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); +MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); + +/* +Deinterleaves an interleaved buffer. +*/ +MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); + +/* +Interleaves a group of deinterleaved buffers. +*/ +MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); + + +/************************************************************************************************************************************************************ + +Channel Maps + +************************************************************************************************************************************************************/ +/* +This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. +*/ +#define MA_CHANNEL_INDEX_NULL 255 + +/* +Retrieves the channel position of the specified channel in the given channel map. + +The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. +*/ +MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); + +/* +Initializes a blank channel map. + +When a blank channel map is specified anywhere it indicates that the native channel map should be used. +*/ +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for retrieving a standard channel map. + +The output channel map buffer must have a capacity of at least `channelMapCap`. +*/ +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); + +/* +Copies a channel map. + +Both input and output channel map buffers must have a capacity of at at least `channels`. +*/ +MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); + +/* +Copies a channel map if one is specified, otherwise copies the default channel map. + +The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. +*/ +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); + + +/* +Determines whether or not a channel map is valid. + +A blank channel map is valid (all channels set to MA_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 + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for comparing two channel maps for equality. + +This assumes the channel count is the same between the two. + +Both channels map buffers must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); + +/* +Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); + +/* +Helper for determining whether or not a channel is present in the given channel map. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); + +/* +Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The +index of the channel is output to `pChannelIndex`. + +The channel map buffer must have a capacity of at least `channels`. +*/ +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); + +/* +Generates a string representing the given channel map. + +This is for printing and debugging purposes, not serialization/deserialization. + +Returns the length of the string, not including the null terminator. +*/ +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); + +/* +Retrieves a human readable version of a channel position. +*/ +MA_API const char* ma_channel_position_to_string(ma_channel channel); + + +/************************************************************************************************************************************************************ + +Conversion Helpers + +************************************************************************************************************************************************************/ + +/* +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. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is +ignored. + +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 ma_data_converter APIs instead. +*/ +MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); +MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); + + +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + +/************************************************************************************************************************************************************ + +Ring Buffer + +************************************************************************************************************************************************************/ +typedef struct +{ + void* pBuffer; + ma_uint32 subbufferSizeInBytes; + ma_uint32 subbufferCount; + ma_uint32 subbufferStrideInBytes; + MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ + ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ + ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ + ma_allocation_callbacks allocationCallbacks; +} ma_rb; + +MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); +MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); +MA_API void ma_rb_uninit(ma_rb* pRB); +MA_API void ma_rb_reset(ma_rb* pRB); +MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); +MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); +MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); +MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); +MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ +MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); +MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); +MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); +MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); + + +typedef struct +{ + ma_data_source_base ds; + ma_rb rb; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ +} ma_pcm_rb; + +MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); +MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); +MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); +MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); +MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); +MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); +MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ +MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); +MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); + + +/* +The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The +capture device writes to it, and then a playback device reads from it. + +At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly +handle desyncs. Note that the API is work in progress and may change at any time in any version. + +The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size +in frames. The internal sample rate of the capture device is also needed in order to calculate the size. +*/ +typedef struct +{ + ma_pcm_rb rb; +} ma_duplex_rb; + +MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); +MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); + + +/************************************************************************************************************************************************************ + +Miscellaneous Helpers + +************************************************************************************************************************************************************/ +/* +Retrieves a human readable description of the given result code. +*/ +MA_API const char* ma_result_description(ma_result result); + +/* +malloc() +*/ +MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +calloc() +*/ +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +realloc() +*/ +MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +free() +*/ +MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Performs an aligned malloc, with the assumption that the alignment is a power of 2. +*/ +MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Free's an aligned malloc'd buffer. +*/ +MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); + +/* +Retrieves a friendly name for a format. +*/ +MA_API const char* ma_get_format_name(ma_format format); + +/* +Blends two frames in floating point format. +*/ +MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_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 API is pure. +*/ +MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); +static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } + +/* +Converts a log level to a string. +*/ +MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); + + + + +/************************************************************************************************************************************************************ + +Synchronization + +************************************************************************************************************************************************************/ +/* +Locks a spinlock. +*/ +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); + +/* +Locks a spinlock, but does not yield() when looping. +*/ +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); + +/* +Unlocks a spinlock. +*/ +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); + + +#ifndef MA_NO_THREADING + +/* +Creates a mutex. + +A mutex must be created from a valid context. A mutex is initially unlocked. +*/ +MA_API ma_result ma_mutex_init(ma_mutex* pMutex); + +/* +Deletes a mutex. +*/ +MA_API void ma_mutex_uninit(ma_mutex* pMutex); + +/* +Locks a mutex with an infinite timeout. +*/ +MA_API void ma_mutex_lock(ma_mutex* pMutex); + +/* +Unlocks a mutex. +*/ +MA_API void ma_mutex_unlock(ma_mutex* pMutex); + + +/* +Initializes an auto-reset event. +*/ +MA_API ma_result ma_event_init(ma_event* pEvent); + +/* +Uninitializes an auto-reset event. +*/ +MA_API void ma_event_uninit(ma_event* pEvent); + +/* +Waits for the specified auto-reset event to become signalled. +*/ +MA_API ma_result ma_event_wait(ma_event* pEvent); + +/* +Signals the specified auto-reset event. +*/ +MA_API ma_result ma_event_signal(ma_event* pEvent); +#endif /* MA_NO_THREADING */ + + +/* +Fence +===== +This locks while the counter is larger than 0. Counter can be incremented and decremented by any +thread, but care needs to be taken when waiting. It is possible for one thread to acquire the +fence just as another thread returns from ma_fence_wait(). + +The idea behind a fence is to allow you to wait for a group of operations to complete. When an +operation starts, the counter is incremented which locks the fence. When the operation completes, +the fence will be released which decrements the counter. ma_fence_wait() will block until the +counter hits zero. + +If threading is disabled, ma_fence_wait() will spin on the counter. +*/ +typedef struct +{ +#ifndef MA_NO_THREADING + ma_event e; +#endif + ma_uint32 counter; +} ma_fence; + +MA_API ma_result ma_fence_init(ma_fence* pFence); +MA_API void ma_fence_uninit(ma_fence* pFence); +MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ +MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ +MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ + + + +/* +Notification callback for asynchronous operations. +*/ +typedef void ma_async_notification; + +typedef struct +{ + void (* onSignal)(ma_async_notification* pNotification); +} ma_async_notification_callbacks; + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); + + +/* +Simple polling notification. + +This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() +*/ +typedef struct +{ + ma_async_notification_callbacks cb; + ma_bool32 signalled; +} ma_async_notification_poll; + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); + + +/* +Event Notification + +This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. +*/ +typedef struct +{ + ma_async_notification_callbacks cb; +#ifndef MA_NO_THREADING + ma_event e; +#endif +} ma_async_notification_event; + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); + + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ + +/* +Slot Allocator +-------------- +The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used +as the insertion point for an object. + +Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. + +The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: + + +-----------------+-----------------+ + | 32 Bits | 32 Bits | + +-----------------+-----------------+ + | Reference Count | Slot Index | + +-----------------+-----------------+ +*/ +typedef struct +{ + ma_uint32 capacity; /* The number of slots to make available. */ +} ma_slot_allocator_config; + +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); + + +typedef struct +{ + MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ +} ma_slot_allocator_group; + +typedef struct +{ + ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ + ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ + ma_uint32 count; /* Allocation count. */ + ma_uint32 capacity; + + /* Memory management. */ + ma_bool32 _ownsHeap; + void* _pHeap; +} ma_slot_allocator; + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); + + +typedef struct ma_job ma_job; + +/* +Callback for processing a job. Each job type will have their own processing callback which will be +called by ma_job_process(). +*/ +typedef ma_result (* ma_job_proc)(ma_job* pJob); + +/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ +typedef enum +{ + /* Miscellaneous. */ + MA_JOB_TYPE_QUIT = 0, + MA_JOB_TYPE_CUSTOM, + + /* Resource Manager. */ + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, + MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, + MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, + + /* Device. */ + MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, + + /* Count. Must always be last. */ + MA_JOB_TYPE_COUNT +} ma_job_type; + +struct ma_job +{ + union + { + struct + { + ma_uint16 code; /* Job type. */ + ma_uint16 slot; /* Index into a ma_slot_allocator. */ + ma_uint32 refcount; + } breakup; + ma_uint64 allocation; + } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ + MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ + ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ + + union + { + /* Miscellaneous. */ + struct + { + ma_job_proc proc; + ma_uintptr data0; + ma_uintptr data1; + } custom; + + /* Resource Manager */ + union + { + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + char* pFilePath; + wchar_t* pFilePathW; + ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ + ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ + ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ + } loadDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBufferNode; + struct + { + /*ma_resource_manager**/ void* pResourceManager; + /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; + /*ma_decoder**/ void* pDecoder; + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ + } pageDataBufferNode; + + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ + ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ + ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_uint32 isLooping; + } loadDataBuffer; + struct + { + /*ma_resource_manager_data_buffer**/ void* pDataBuffer; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataBuffer; + + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ + wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ + ma_uint64 initialSeekPoint; + ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ + ma_fence* pInitFence; + } loadDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_async_notification* pDoneNotification; + ma_fence* pDoneFence; + } freeDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint32 pageIndex; /* The index of the page to decode into. */ + } pageDataStream; + struct + { + /*ma_resource_manager_data_stream**/ void* pDataStream; + ma_uint64 frameIndex; + } seekDataStream; + } resourceManager; + + /* Device. */ + union + { + union + { + struct + { + /*ma_device**/ void* pDevice; + /*ma_device_type*/ ma_uint32 deviceType; + } reroute; + } aaudio; + } device; + } data; +}; + +MA_API ma_job ma_job_init(ma_uint16 code); +MA_API ma_result ma_job_process(ma_job* pJob); + + +/* +When set, ma_job_queue_next() will not wait and no semaphore will be signaled in +ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. + +This flag should always be used for platforms that do not support multithreading. +*/ +typedef enum +{ + MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 +} ma_job_queue_flags; + +typedef struct +{ + ma_uint32 flags; + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ +} ma_job_queue_config; + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); + + +typedef struct +{ + ma_uint32 flags; /* Flags passed in at initialization time. */ + ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ + MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ + MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ +#ifndef MA_NO_THREADING + ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ +#endif + ma_slot_allocator allocator; + ma_job* pJobs; +#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock lock; +#endif + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_job_queue; + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DEVICE I/O +========== + +This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ +#ifndef MA_NO_DEVICE_IO +/* Some backends are only supported on certain platforms. */ +#if defined(MA_WIN32) + #define MA_SUPPORT_WASAPI + + #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ + #define MA_SUPPORT_DSOUND + #define MA_SUPPORT_WINMM + + /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ + #if !defined(__COSMOPOLITAN__) + #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + #endif + #endif +#endif +#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) + #if defined(MA_LINUX) + #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ + #define MA_SUPPORT_ALSA + #endif + #endif + #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) + #define MA_SUPPORT_PULSEAUDIO + #define MA_SUPPORT_JACK + #endif + #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ + #define MA_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 MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ + #endif + #if defined(__FreeBSD__) || defined(__DragonFly__) + #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ + #endif +#endif +#if defined(MA_ANDROID) + #define MA_SUPPORT_AAUDIO + #define MA_SUPPORT_OPENSL +#endif +#if defined(MA_APPLE) + #define MA_SUPPORT_COREAUDIO +#endif +#if defined(MA_EMSCRIPTEN) + #define MA_SUPPORT_WEBAUDIO +#endif + +/* All platforms should support custom backends. */ +#define MA_SUPPORT_CUSTOM + +/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ +#if !defined(MA_EMSCRIPTEN) +#define MA_SUPPORT_NULL +#endif + + +#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) + #define MA_HAS_WASAPI +#endif +#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) + #define MA_HAS_DSOUND +#endif +#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) + #define MA_HAS_WINMM +#endif +#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) + #define MA_HAS_ALSA +#endif +#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) + #define MA_HAS_PULSEAUDIO +#endif +#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) + #define MA_HAS_JACK +#endif +#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) + #define MA_HAS_COREAUDIO +#endif +#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) + #define MA_HAS_SNDIO +#endif +#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) + #define MA_HAS_AUDIO4 +#endif +#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) + #define MA_HAS_OSS +#endif +#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) + #define MA_HAS_AAUDIO +#endif +#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) + #define MA_HAS_OPENSL +#endif +#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) + #define MA_HAS_WEBAUDIO +#endif +#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) + #define MA_HAS_CUSTOM +#endif +#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) + #define MA_HAS_NULL +#endif + +typedef enum +{ + ma_device_state_uninitialized = 0, + ma_device_state_stopped = 1, /* The device's default state after initialization. */ + ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ + ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ + ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ +} ma_device_state; + +MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state) + + +#ifdef MA_SUPPORT_WASAPI +/* We need a IMMNotificationClient object for WASAPI. */ +typedef struct +{ + void* lpVtbl; + ma_uint32 counter; + ma_device* pDevice; +} ma_IMMNotificationClient; +#endif + +/* Backend enums must be in priority order. */ +typedef enum +{ + ma_backend_wasapi, + ma_backend_dsound, + ma_backend_winmm, + ma_backend_coreaudio, + ma_backend_sndio, + ma_backend_audio4, + ma_backend_oss, + ma_backend_pulseaudio, + ma_backend_alsa, + ma_backend_jack, + ma_backend_aaudio, + ma_backend_opensl, + ma_backend_webaudio, + ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ + ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ +} ma_backend; + +#define MA_BACKEND_COUNT (ma_backend_null+1) + + +/* +Device job thread. This is used by backends that require asynchronous processing of certain +operations. It is not used by all backends. + +The device job thread is made up of a thread and a job queue. You can post a job to the thread with +ma_device_job_thread_post(). The thread will do the processing of the job. +*/ +typedef struct +{ + ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ + ma_uint32 jobQueueCapacity; + ma_uint32 jobQueueFlags; +} ma_device_job_thread_config; + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); + +typedef struct +{ + ma_thread thread; + ma_job_queue jobQueue; + ma_bool32 _hasThread; +} ma_device_job_thread; + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); + + + +/* Device notification types. */ +typedef enum +{ + ma_device_notification_type_started, + ma_device_notification_type_stopped, + ma_device_notification_type_rerouted, + ma_device_notification_type_interruption_began, + ma_device_notification_type_interruption_ended, + ma_device_notification_type_unlocked +} ma_device_notification_type; + +typedef struct +{ + ma_device* pDevice; + ma_device_notification_type type; + union + { + struct + { + int _unused; + } started; + struct + { + int _unused; + } stopped; + struct + { + int _unused; + } rerouted; + struct + { + int _unused; + } interruption; + } data; +} ma_device_notification; + +/* +The notification callback for when the application should be notified of a change to the device. + +This callback is used for notifying the application of changes such as when the device has started, +stopped, rerouted or an interruption has occurred. Note that not all backends will post all +notification types. For example, some backends will perform automatic stream routing without any +kind of notification to the host program which means miniaudio will never know about it and will +never be able to fire the rerouted notification. You should keep this in mind when designing your +program. + +The stopped notification will *not* get fired when a device is rerouted. + + +Parameters +---------- +pNotification (in) + A pointer to a structure containing information about the event. Use the `pDevice` member of + this object to retrieve the relevant device. The `type` member can be used to discriminate + against each of the notification types. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. + +Not all notifications will be triggered by all backends, however the started and stopped events +should be reliable for all backends. Some backends do not have a good way to detect device +stoppages due to unplugging the device which may result in the stopped callback not getting +fired. This has been observed with at least one BSD variant. + +The rerouted notification is fired *after* the reroute has occurred. The stopped notification will +*not* get fired when a device is rerouted. The following backends are known to do automatic stream +rerouting, but do not have a way to be notified of the change: + + * DirectSound + +The interruption notifications are used on mobile platforms for detecting when audio is interrupted +due to things like an incoming phone call. Currently this is only implemented on iOS. None of the +Android backends will report this notification. +*/ +typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); + + +/* +The callback for processing audio data from the device. + +The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data +available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the +callback will be fired with a consistent frame count. + + +Parameters +---------- +pDevice (in) + A pointer to the relevant device. + +pOutput (out) + A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or + full-duplex device and null for a capture and loopback device. + +pInput (in) + A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a + playback device. + +frameCount (in) + The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The + `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must + not assume this will always be the same value each time the callback is fired. + + +Remarks +------- +You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the +callback. The following APIs cannot be called from inside the callback: + + ma_device_init() + ma_device_init_ex() + ma_device_uninit() + ma_device_start() + ma_device_stop() + +The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. +*/ +typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + + + +/* +DEPRECATED. Use ma_device_notification_proc instead. + +The callback for when the device has been stopped. + +This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces +such as being unplugged or an internal error occurring. + + +Parameters +---------- +pDevice (in) + A pointer to the device that has just stopped. + + +Remarks +------- +Do not restart or uninitialize the device from the callback. +*/ +typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ + +typedef enum +{ + ma_device_type_playback = 1, + ma_device_type_capture = 2, + ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ + ma_device_type_loopback = 4 +} ma_device_type; + +typedef enum +{ + ma_share_mode_shared = 0, + ma_share_mode_exclusive +} ma_share_mode; + +/* iOS/tvOS/watchOS session categories. */ +typedef enum +{ + ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ + ma_ios_session_category_none, /* Leave the session category unchanged. */ + ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ + ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ + ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ + ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ + ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ + ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ +} ma_ios_session_category; + +/* iOS/tvOS/watchOS session category options */ +typedef enum +{ + ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ + ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ + ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ + ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ + ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ + ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ + ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ +} ma_ios_session_category_option; + +/* OpenSL stream types. */ +typedef enum +{ + ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ + ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ + ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ + ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ + ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ + ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ + ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ +} ma_opensl_stream_type; + +/* OpenSL recording presets. */ +typedef enum +{ + ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ + ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ + ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ + ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ + ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ + ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ +} ma_opensl_recording_preset; + +/* WASAPI audio thread priority characteristics. */ +typedef enum +{ + ma_wasapi_usage_default = 0, + ma_wasapi_usage_games, + ma_wasapi_usage_pro_audio, +} ma_wasapi_usage; + +/* AAudio usage types. */ +typedef enum +{ + ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ + ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ + ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ + ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ + ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ + ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ + ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ + ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ + ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ + ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ + ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ + ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ + ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ + ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ + ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ + ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ + ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ +} ma_aaudio_usage; + +/* AAudio content types. */ +typedef enum +{ + ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ + ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ + ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ + ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ + ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ +} ma_aaudio_content_type; + +/* AAudio input presets. */ +typedef enum +{ + ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ + ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ + ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ + ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ + ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ + ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ + ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ +} ma_aaudio_input_preset; + +typedef enum +{ + ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */ + ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */ + ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */ + ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */ +} ma_aaudio_allowed_capture_policy; + +typedef union +{ + ma_int64 counter; + double counterD; +} ma_timer; + +typedef union +{ + ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ + ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ + /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ + char alsa[256]; /* ALSA uses a name string for identification. */ + char pulse[256]; /* PulseAudio uses a name string for identification. */ + int jack; /* JACK always uses default devices. */ + char coreaudio[256]; /* Core Audio uses a string for identification. */ + char sndio[256]; /* "snd/0", etc. */ + char audio4[256]; /* "/dev/audio", etc. */ + char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ + ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ + ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ + char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ + union + { + int i; + char s[256]; + void* p; + } custom; /* The custom backend could be anything. Give them a few options. */ + int nullbackend; /* The null backend uses an integer for device IDs. */ +} ma_device_id; + + +typedef struct ma_context_config ma_context_config; +typedef struct ma_device_config ma_device_config; +typedef struct ma_backend_callbacks ma_backend_callbacks; + +#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ + +#ifndef MA_MAX_DEVICE_NAME_LENGTH +#define MA_MAX_DEVICE_NAME_LENGTH 255 +#endif + +typedef struct +{ + /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ + ma_device_id id; + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ + ma_bool32 isDefault; + + ma_uint32 nativeDataFormatCount; + struct + { + ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ + ma_uint32 channels; /* If set to 0, all channels are supported. */ + ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ + ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ + } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ +} ma_device_info; + +struct ma_device_config +{ + ma_device_type deviceType; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInMilliseconds; + ma_uint32 periods; + ma_performance_profile performanceProfile; + ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ + ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */ + ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ + ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ + ma_device_data_proc dataCallback; + ma_device_notification_proc notificationCallback; + ma_stop_proc stopCallback; + void* pUserData; + ma_resampler_config resampling; + struct + { + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + ma_share_mode shareMode; + } playback; + struct + { + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ + ma_share_mode shareMode; + } capture; + + struct + { + ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ + ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ + ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ + ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ + } wasapi; + struct + { + ma_bool32 noMMap; /* Disables MMap mode. */ + ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ + ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ + ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ + } alsa; + struct + { + const char* pStreamNamePlayback; + const char* pStreamNameCapture; + } pulse; + struct + { + ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ + } coreaudio; + struct + { + ma_opensl_stream_type streamType; + ma_opensl_recording_preset recordingPreset; + ma_bool32 enableCompatibilityWorkarounds; + } opensl; + struct + { + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_bool32 noAutoStartAfterReroute; + ma_bool32 enableCompatibilityWorkarounds; + } aaudio; +}; + + +/* +The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +deviceType (in) + The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. + +pInfo (in) + A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, + only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which + is too inefficient. + +pUserData (in) + The user data pointer passed into `ma_context_enumerate_devices()`. +*/ +typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); + + +/* +Describes some basic details about a playback or capture device. +*/ +typedef struct +{ + const ma_device_id* pDeviceID; + ma_share_mode shareMode; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInMilliseconds; + ma_uint32 periodCount; +} ma_device_descriptor; + +/* +These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context +to many devices. A device is created from a context. + +The general flow goes like this: + + 1) A context is created with `onContextInit()` + 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. + 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. + 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was + selected from device enumeration via `onContextEnumerateDevices()`. + 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` + 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call + to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by + miniaudio internally. + +Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the +callbacks defined in this structure. + +Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which +physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the +given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration +needs to stop and the `onContextEnumerateDevices()` function returns with a success code. + +Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, +and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the +case when the device ID is NULL, in which case information about the default device needs to be retrieved. + +Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. +This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a +device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, +the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to +the requested format. The conversion between the format requested by the application and the device's native format will be handled +internally by miniaudio. + +On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's +supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for +sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to +`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should +inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period +size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the +sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` +object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). + +Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses +asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. + +The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit +easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and +`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the +backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. +This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. + +If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback +which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. + +The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been +encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. + +The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this +callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated +which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, +look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to +wake up the audio thread. + +If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the +`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. +*/ +struct ma_backend_callbacks +{ + ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); + ma_result (* onContextUninit)(ma_context* pContext); + ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); + ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); + ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); + ma_result (* onDeviceUninit)(ma_device* pDevice); + ma_result (* onDeviceStart)(ma_device* pDevice); + ma_result (* onDeviceStop)(ma_device* pDevice); + ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); + ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); + ma_result (* onDeviceDataLoop)(ma_device* pDevice); + ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); + ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); +}; + +struct ma_context_config +{ + ma_log* pLog; + ma_thread_priority threadPriority; + size_t threadStackSize; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + struct + { + ma_bool32 useVerboseDeviceEnumeration; + } alsa; + struct + { + const char* pApplicationName; + const char* pServerName; + ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ + } pulse; + struct + { + ma_ios_session_category sessionCategory; + ma_uint32 sessionCategoryOptions; + ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ + ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ + } coreaudio; + struct + { + const char* pClientName; + ma_bool32 tryStartServer; + } jack; + ma_backend_callbacks custom; +}; + +/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ +typedef struct +{ + int code; + ma_event* pEvent; /* This will be signalled when the event is complete. */ + union + { + struct + { + int _unused; + } quit; + struct + { + ma_device_type deviceType; + void* pAudioClient; + void** ppAudioClientService; + ma_result* pResult; /* The result from creating the audio client service. */ + } createAudioClient; + struct + { + ma_device* pDevice; + ma_device_type deviceType; + } releaseAudioClient; + } data; +} ma_context_command__wasapi; + +struct ma_context +{ + ma_backend_callbacks callbacks; + ma_backend backend; /* DirectSound, ALSA, etc. */ + ma_log* pLog; + ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ + ma_thread_priority threadPriority; + size_t threadStackSize; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ + ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ + ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ + ma_uint32 playbackDeviceInfoCount; + ma_uint32 captureDeviceInfoCount; + ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ + + union + { +#ifdef MA_SUPPORT_WASAPI + struct + { + ma_thread commandThread; + ma_mutex commandLock; + ma_semaphore commandSem; + ma_uint32 commandIndex; + ma_uint32 commandCount; + ma_context_command__wasapi commands[4]; + ma_handle hAvrt; + ma_proc AvSetMmThreadCharacteristicsA; + ma_proc AvRevertMmThreadcharacteristics; + ma_handle hMMDevapi; + ma_proc ActivateAudioInterfaceAsync; + } wasapi; +#endif +#ifdef MA_SUPPORT_DSOUND + struct + { + ma_handle hDSoundDLL; + ma_proc DirectSoundCreate; + ma_proc DirectSoundEnumerateA; + ma_proc DirectSoundCaptureCreate; + ma_proc DirectSoundCaptureEnumerateA; + } dsound; +#endif +#ifdef MA_SUPPORT_WINMM + struct + { + ma_handle hWinMM; + ma_proc waveOutGetNumDevs; + ma_proc waveOutGetDevCapsA; + ma_proc waveOutOpen; + ma_proc waveOutClose; + ma_proc waveOutPrepareHeader; + ma_proc waveOutUnprepareHeader; + ma_proc waveOutWrite; + ma_proc waveOutReset; + ma_proc waveInGetNumDevs; + ma_proc waveInGetDevCapsA; + ma_proc waveInOpen; + ma_proc waveInClose; + ma_proc waveInPrepareHeader; + ma_proc waveInUnprepareHeader; + ma_proc waveInAddBuffer; + ma_proc waveInStart; + ma_proc waveInReset; + } winmm; +#endif +#ifdef MA_SUPPORT_ALSA + struct + { + ma_handle asoundSO; + ma_proc snd_pcm_open; + ma_proc snd_pcm_close; + ma_proc snd_pcm_hw_params_sizeof; + ma_proc snd_pcm_hw_params_any; + ma_proc snd_pcm_hw_params_set_format; + ma_proc snd_pcm_hw_params_set_format_first; + ma_proc snd_pcm_hw_params_get_format_mask; + ma_proc snd_pcm_hw_params_set_channels; + ma_proc snd_pcm_hw_params_set_channels_near; + ma_proc snd_pcm_hw_params_set_channels_minmax; + ma_proc snd_pcm_hw_params_set_rate_resample; + ma_proc snd_pcm_hw_params_set_rate; + ma_proc snd_pcm_hw_params_set_rate_near; + ma_proc snd_pcm_hw_params_set_buffer_size_near; + ma_proc snd_pcm_hw_params_set_periods_near; + ma_proc snd_pcm_hw_params_set_access; + ma_proc snd_pcm_hw_params_get_format; + ma_proc snd_pcm_hw_params_get_channels; + ma_proc snd_pcm_hw_params_get_channels_min; + ma_proc snd_pcm_hw_params_get_channels_max; + ma_proc snd_pcm_hw_params_get_rate; + ma_proc snd_pcm_hw_params_get_rate_min; + ma_proc snd_pcm_hw_params_get_rate_max; + ma_proc snd_pcm_hw_params_get_buffer_size; + ma_proc snd_pcm_hw_params_get_periods; + ma_proc snd_pcm_hw_params_get_access; + ma_proc snd_pcm_hw_params_test_format; + ma_proc snd_pcm_hw_params_test_channels; + ma_proc snd_pcm_hw_params_test_rate; + ma_proc snd_pcm_hw_params; + ma_proc snd_pcm_sw_params_sizeof; + ma_proc snd_pcm_sw_params_current; + ma_proc snd_pcm_sw_params_get_boundary; + ma_proc snd_pcm_sw_params_set_avail_min; + ma_proc snd_pcm_sw_params_set_start_threshold; + ma_proc snd_pcm_sw_params_set_stop_threshold; + ma_proc snd_pcm_sw_params; + ma_proc snd_pcm_format_mask_sizeof; + ma_proc snd_pcm_format_mask_test; + ma_proc snd_pcm_get_chmap; + ma_proc snd_pcm_state; + ma_proc snd_pcm_prepare; + ma_proc snd_pcm_start; + ma_proc snd_pcm_drop; + ma_proc snd_pcm_drain; + ma_proc snd_pcm_reset; + ma_proc snd_device_name_hint; + ma_proc snd_device_name_get_hint; + ma_proc snd_card_get_index; + ma_proc snd_device_name_free_hint; + ma_proc snd_pcm_mmap_begin; + ma_proc snd_pcm_mmap_commit; + ma_proc snd_pcm_recover; + ma_proc snd_pcm_readi; + ma_proc snd_pcm_writei; + ma_proc snd_pcm_avail; + ma_proc snd_pcm_avail_update; + ma_proc snd_pcm_wait; + ma_proc snd_pcm_nonblock; + ma_proc snd_pcm_info; + ma_proc snd_pcm_info_sizeof; + ma_proc snd_pcm_info_get_name; + ma_proc snd_pcm_poll_descriptors; + ma_proc snd_pcm_poll_descriptors_count; + ma_proc snd_pcm_poll_descriptors_revents; + ma_proc snd_config_update_free_global; + + ma_mutex internalDeviceEnumLock; + ma_bool32 useVerboseDeviceEnumeration; + } alsa; +#endif +#ifdef MA_SUPPORT_PULSEAUDIO + struct + { + ma_handle pulseSO; + ma_proc pa_mainloop_new; + ma_proc pa_mainloop_free; + ma_proc pa_mainloop_quit; + ma_proc pa_mainloop_get_api; + ma_proc pa_mainloop_iterate; + ma_proc pa_mainloop_wakeup; + ma_proc pa_threaded_mainloop_new; + ma_proc pa_threaded_mainloop_free; + ma_proc pa_threaded_mainloop_start; + ma_proc pa_threaded_mainloop_stop; + ma_proc pa_threaded_mainloop_lock; + ma_proc pa_threaded_mainloop_unlock; + ma_proc pa_threaded_mainloop_wait; + ma_proc pa_threaded_mainloop_signal; + ma_proc pa_threaded_mainloop_accept; + ma_proc pa_threaded_mainloop_get_retval; + ma_proc pa_threaded_mainloop_get_api; + ma_proc pa_threaded_mainloop_in_thread; + ma_proc pa_threaded_mainloop_set_name; + ma_proc pa_context_new; + ma_proc pa_context_unref; + ma_proc pa_context_connect; + ma_proc pa_context_disconnect; + ma_proc pa_context_set_state_callback; + ma_proc pa_context_get_state; + ma_proc pa_context_get_sink_info_list; + ma_proc pa_context_get_source_info_list; + ma_proc pa_context_get_sink_info_by_name; + ma_proc pa_context_get_source_info_by_name; + ma_proc pa_operation_unref; + ma_proc pa_operation_get_state; + ma_proc pa_channel_map_init_extend; + ma_proc pa_channel_map_valid; + ma_proc pa_channel_map_compatible; + ma_proc pa_stream_new; + ma_proc pa_stream_unref; + ma_proc pa_stream_connect_playback; + ma_proc pa_stream_connect_record; + ma_proc pa_stream_disconnect; + ma_proc pa_stream_get_state; + ma_proc pa_stream_get_sample_spec; + ma_proc pa_stream_get_channel_map; + ma_proc pa_stream_get_buffer_attr; + ma_proc pa_stream_set_buffer_attr; + ma_proc pa_stream_get_device_name; + ma_proc pa_stream_set_write_callback; + ma_proc pa_stream_set_read_callback; + ma_proc pa_stream_set_suspended_callback; + ma_proc pa_stream_set_moved_callback; + ma_proc pa_stream_is_suspended; + ma_proc pa_stream_flush; + ma_proc pa_stream_drain; + ma_proc pa_stream_is_corked; + ma_proc pa_stream_cork; + ma_proc pa_stream_trigger; + ma_proc pa_stream_begin_write; + ma_proc pa_stream_write; + ma_proc pa_stream_peek; + ma_proc pa_stream_drop; + ma_proc pa_stream_writable_size; + ma_proc pa_stream_readable_size; + + /*pa_mainloop**/ ma_ptr pMainLoop; + /*pa_context**/ ma_ptr pPulseContext; + char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ + } pulse; +#endif +#ifdef MA_SUPPORT_JACK + struct + { + ma_handle jackSO; + ma_proc jack_client_open; + ma_proc jack_client_close; + ma_proc jack_client_name_size; + ma_proc jack_set_process_callback; + ma_proc jack_set_buffer_size_callback; + ma_proc jack_on_shutdown; + ma_proc jack_get_sample_rate; + ma_proc jack_get_buffer_size; + ma_proc jack_get_ports; + ma_proc jack_activate; + ma_proc jack_deactivate; + ma_proc jack_connect; + ma_proc jack_port_register; + ma_proc jack_port_name; + ma_proc jack_port_get_buffer; + ma_proc jack_free; + + char* pClientName; + ma_bool32 tryStartServer; + } jack; +#endif +#ifdef MA_SUPPORT_COREAUDIO + struct + { + ma_handle hCoreFoundation; + ma_proc CFStringGetCString; + ma_proc CFRelease; + + ma_handle hCoreAudio; + ma_proc AudioObjectGetPropertyData; + ma_proc AudioObjectGetPropertyDataSize; + ma_proc AudioObjectSetPropertyData; + ma_proc AudioObjectAddPropertyListener; + ma_proc AudioObjectRemovePropertyListener; + + ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ + ma_proc AudioComponentFindNext; + ma_proc AudioComponentInstanceDispose; + ma_proc AudioComponentInstanceNew; + ma_proc AudioOutputUnitStart; + ma_proc AudioOutputUnitStop; + ma_proc AudioUnitAddPropertyListener; + ma_proc AudioUnitGetPropertyInfo; + ma_proc AudioUnitGetProperty; + ma_proc AudioUnitSetProperty; + ma_proc AudioUnitInitialize; + ma_proc AudioUnitRender; + + /*AudioComponent*/ ma_ptr component; + ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ + } coreaudio; +#endif +#ifdef MA_SUPPORT_SNDIO + struct + { + ma_handle sndioSO; + ma_proc sio_open; + ma_proc sio_close; + ma_proc sio_setpar; + ma_proc sio_getpar; + ma_proc sio_getcap; + ma_proc sio_start; + ma_proc sio_stop; + ma_proc sio_read; + ma_proc sio_write; + ma_proc sio_onmove; + ma_proc sio_nfds; + ma_proc sio_pollfd; + ma_proc sio_revents; + ma_proc sio_eof; + ma_proc sio_setvol; + ma_proc sio_onvol; + ma_proc sio_initpar; + } sndio; +#endif +#ifdef MA_SUPPORT_AUDIO4 + struct + { + int _unused; + } audio4; +#endif +#ifdef MA_SUPPORT_OSS + struct + { + int versionMajor; + int versionMinor; + } oss; +#endif +#ifdef MA_SUPPORT_AAUDIO + struct + { + ma_handle hAAudio; /* libaaudio.so */ + ma_proc AAudio_createStreamBuilder; + ma_proc AAudioStreamBuilder_delete; + ma_proc AAudioStreamBuilder_setDeviceId; + ma_proc AAudioStreamBuilder_setDirection; + ma_proc AAudioStreamBuilder_setSharingMode; + ma_proc AAudioStreamBuilder_setFormat; + ma_proc AAudioStreamBuilder_setChannelCount; + ma_proc AAudioStreamBuilder_setSampleRate; + ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; + ma_proc AAudioStreamBuilder_setFramesPerDataCallback; + ma_proc AAudioStreamBuilder_setDataCallback; + ma_proc AAudioStreamBuilder_setErrorCallback; + ma_proc AAudioStreamBuilder_setPerformanceMode; + ma_proc AAudioStreamBuilder_setUsage; + ma_proc AAudioStreamBuilder_setContentType; + ma_proc AAudioStreamBuilder_setInputPreset; + ma_proc AAudioStreamBuilder_setAllowedCapturePolicy; + ma_proc AAudioStreamBuilder_openStream; + ma_proc AAudioStream_close; + ma_proc AAudioStream_getState; + ma_proc AAudioStream_waitForStateChange; + ma_proc AAudioStream_getFormat; + ma_proc AAudioStream_getChannelCount; + ma_proc AAudioStream_getSampleRate; + ma_proc AAudioStream_getBufferCapacityInFrames; + ma_proc AAudioStream_getFramesPerDataCallback; + ma_proc AAudioStream_getFramesPerBurst; + ma_proc AAudioStream_requestStart; + ma_proc AAudioStream_requestStop; + ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ + } aaudio; +#endif +#ifdef MA_SUPPORT_OPENSL + struct + { + ma_handle libOpenSLES; + ma_handle SL_IID_ENGINE; + ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; + ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + ma_handle SL_IID_RECORD; + ma_handle SL_IID_PLAY; + ma_handle SL_IID_OUTPUTMIX; + ma_handle SL_IID_ANDROIDCONFIGURATION; + ma_proc slCreateEngine; + } opensl; +#endif +#ifdef MA_SUPPORT_WEBAUDIO + struct + { + int _unused; + } webaudio; +#endif +#ifdef MA_SUPPORT_NULL + struct + { + int _unused; + } null_backend; +#endif + }; + + union + { +#if defined(MA_WIN32) + struct + { + /*HMODULE*/ ma_handle hOle32DLL; + ma_proc CoInitialize; + ma_proc CoInitializeEx; + ma_proc CoUninitialize; + ma_proc CoCreateInstance; + ma_proc CoTaskMemFree; + ma_proc PropVariantClear; + ma_proc StringFromGUID2; + + /*HMODULE*/ ma_handle hUser32DLL; + ma_proc GetForegroundWindow; + ma_proc GetDesktopWindow; + + /*HMODULE*/ ma_handle hAdvapi32DLL; + ma_proc RegOpenKeyExA; + ma_proc RegCloseKey; + ma_proc RegQueryValueExA; + + /*HRESULT*/ long CoInitializeResult; + } win32; +#endif +#ifdef MA_POSIX + struct + { + int _unused; + } posix; +#endif + int _unused; + }; +}; + +struct ma_device +{ + ma_context* pContext; + ma_device_type type; + ma_uint32 sampleRate; + ma_atomic_device_state state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ + ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ + ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ + ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ + void* pUserData; /* Application defined data. */ + ma_mutex startStopLock; + ma_event wakeupEvent; + ma_event startEvent; + ma_event stopEvent; + ma_thread thread; + ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ + ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ + ma_bool8 noPreSilencedOutputBuffer; + ma_bool8 noClip; + ma_bool8 noDisableDenormals; + ma_bool8 noFixedSizedCallback; + ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ + ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ + struct + { + ma_resample_algorithm algorithm; + ma_resampling_backend_vtable* pBackendVTable; + void* pBackendUserData; + struct + { + ma_uint32 lpfOrder; + } linear; + } resampling; + struct + { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ + ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ + ma_format format; + ma_uint32 channels; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; + ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ + void* pInputCache; /* In external format. Can be null. */ + ma_uint64 inputCacheCap; + ma_uint64 inputCacheConsumed; + ma_uint64 inputCacheRemaining; + } playback; + struct + { + ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ + ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ + ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ + ma_format format; + ma_uint32 channels; + ma_channel channelMap[MA_MAX_CHANNELS]; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + ma_channel_mix_mode channelMixMode; + ma_bool32 calculateLFEFromSpatialChannels; + ma_data_converter converter; + void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ + ma_uint32 intermediaryBufferCap; + ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ + } capture; + + union + { +#ifdef MA_SUPPORT_WASAPI + struct + { + /*IAudioClient**/ ma_ptr pAudioClientPlayback; + /*IAudioClient**/ ma_ptr pAudioClientCapture; + /*IAudioRenderClient**/ ma_ptr pRenderClient; + /*IAudioCaptureClient**/ ma_ptr pCaptureClient; + /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ + ma_IMMNotificationClient notificationClient; + /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ + /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ + ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ + ma_uint32 actualBufferSizeInFramesCapture; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_uint32 periodSizeInFramesPlayback; + ma_uint32 periodSizeInFramesCapture; + void* pMappedBufferCapture; + ma_uint32 mappedBufferCaptureCap; + ma_uint32 mappedBufferCaptureLen; + void* pMappedBufferPlayback; + ma_uint32 mappedBufferPlaybackCap; + ma_uint32 mappedBufferPlaybackLen; + ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ + ma_uint32 loopbackProcessID; + ma_bool8 loopbackProcessExclude; + ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ + ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ + ma_bool8 noHardwareOffloading; + ma_bool8 allowCaptureAutoStreamRouting; + ma_bool8 allowPlaybackAutoStreamRouting; + ma_bool8 isDetachedPlayback; + ma_bool8 isDetachedCapture; + ma_wasapi_usage usage; + void* hAvrtHandle; + ma_mutex rerouteLock; + } wasapi; +#endif +#ifdef MA_SUPPORT_DSOUND + struct + { + /*LPDIRECTSOUND*/ ma_ptr pPlayback; + /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; + /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; + /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; + /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; + } dsound; +#endif +#ifdef MA_SUPPORT_WINMM + struct + { + /*HWAVEOUT*/ ma_handle hDevicePlayback; + /*HWAVEIN*/ ma_handle hDeviceCapture; + /*HANDLE*/ ma_handle hEventPlayback; + /*HANDLE*/ ma_handle hEventCapture; + ma_uint32 fragmentSizeInFrames; + ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ + ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ + ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ + ma_uint32 headerFramesConsumedCapture; /* ^^^ */ + /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ + /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ + ma_uint8* pIntermediaryBufferPlayback; + ma_uint8* pIntermediaryBufferCapture; + ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ + } winmm; +#endif +#ifdef MA_SUPPORT_ALSA + struct + { + /*snd_pcm_t**/ ma_ptr pPCMPlayback; + /*snd_pcm_t**/ ma_ptr pPCMCapture; + /*struct pollfd**/ void* pPollDescriptorsPlayback; + /*struct pollfd**/ void* pPollDescriptorsCapture; + int pollDescriptorCountPlayback; + int pollDescriptorCountCapture; + int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ + int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ + ma_bool8 isUsingMMapPlayback; + ma_bool8 isUsingMMapCapture; + } alsa; +#endif +#ifdef MA_SUPPORT_PULSEAUDIO + struct + { + /*pa_mainloop**/ ma_ptr pMainLoop; + /*pa_context**/ ma_ptr pPulseContext; + /*pa_stream**/ ma_ptr pStreamPlayback; + /*pa_stream**/ ma_ptr pStreamCapture; + } pulse; +#endif +#ifdef MA_SUPPORT_JACK + struct + { + /*jack_client_t**/ ma_ptr pClient; + /*jack_port_t**/ ma_ptr* ppPortsPlayback; + /*jack_port_t**/ ma_ptr* ppPortsCapture; + float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ + float* pIntermediaryBufferCapture; + } jack; +#endif +#ifdef MA_SUPPORT_COREAUDIO + struct + { + ma_uint32 deviceObjectIDPlayback; + ma_uint32 deviceObjectIDCapture; + /*AudioUnit*/ ma_ptr audioUnitPlayback; + /*AudioUnit*/ ma_ptr audioUnitCapture; + /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ + ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ + ma_event stopEvent; + ma_uint32 originalPeriodSizeInFrames; + ma_uint32 originalPeriodSizeInMilliseconds; + ma_uint32 originalPeriods; + ma_performance_profile originalPerformanceProfile; + ma_bool32 isDefaultPlaybackDevice; + ma_bool32 isDefaultCaptureDevice; + ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ + void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ + } coreaudio; +#endif +#ifdef MA_SUPPORT_SNDIO + struct + { + ma_ptr handlePlayback; + ma_ptr handleCapture; + ma_bool32 isStartedPlayback; + ma_bool32 isStartedCapture; + } sndio; +#endif +#ifdef MA_SUPPORT_AUDIO4 + struct + { + int fdPlayback; + int fdCapture; + } audio4; +#endif +#ifdef MA_SUPPORT_OSS + struct + { + int fdPlayback; + int fdCapture; + } oss; +#endif +#ifdef MA_SUPPORT_AAUDIO + struct + { + /*AAudioStream**/ ma_ptr pStreamPlayback; + /*AAudioStream**/ ma_ptr pStreamCapture; + ma_aaudio_usage usage; + ma_aaudio_content_type contentType; + ma_aaudio_input_preset inputPreset; + ma_aaudio_allowed_capture_policy allowedCapturePolicy; + ma_bool32 noAutoStartAfterReroute; + } aaudio; +#endif +#ifdef MA_SUPPORT_OPENSL + struct + { + /*SLObjectItf*/ ma_ptr pOutputMixObj; + /*SLOutputMixItf*/ ma_ptr pOutputMix; + /*SLObjectItf*/ ma_ptr pAudioPlayerObj; + /*SLPlayItf*/ ma_ptr pAudioPlayer; + /*SLObjectItf*/ ma_ptr pAudioRecorderObj; + /*SLRecordItf*/ ma_ptr pAudioRecorder; + /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; + /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; + ma_bool32 isDrainingCapture; + ma_bool32 isDrainingPlayback; + ma_uint32 currentBufferIndexPlayback; + ma_uint32 currentBufferIndexCapture; + ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ + ma_uint8* pBufferCapture; + } opensl; +#endif +#ifdef MA_SUPPORT_WEBAUDIO + struct + { + /* AudioWorklets path. */ + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; + float* pIntermediaryBuffer; + void* pStackBuffer; + ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ + int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ + } webaudio; +#endif +#ifdef MA_SUPPORT_NULL + struct + { + ma_thread deviceThread; + ma_event operationEvent; + ma_event operationCompletionEvent; + ma_semaphore operationSemaphore; + ma_uint32 operation; + ma_result operationResult; + ma_timer timer; + double priorRunTime; + ma_uint32 currentPeriodFramesRemainingPlayback; + ma_uint32 currentPeriodFramesRemainingCapture; + ma_uint64 lastProcessedFramePlayback; + ma_uint64 lastProcessedFrameCapture; + ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ + } null_device; +#endif + }; +}; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ +#endif + +/* +Initializes a `ma_context_config` object. + + +Return Value +------------ +A `ma_context_config` initialized to defaults. + + +Remarks +------- +You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio +is updated and new members are added to `ma_context_config`. It also sets logical defaults. + +You can override members of the returned object by changing it's members directly. + + +See Also +-------- +ma_context_init() +*/ +MA_API ma_context_config ma_context_config_init(void); + +/* +Initializes a context. + +The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual +device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. + + +Parameters +---------- +backends (in, optional) + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + +backendCount (in, optional) + The number of items in `backend`. Ignored if `backend` is NULL. + +pConfig (in, optional) + The context configuration. + +pContext (in) + A pointer to the context object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Do not call this function across multiple threads as some backends read and write to global state. + + +Remarks +------- +When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: + + |-------------|-----------------------|--------------------------------------------------------| + | Name | Enum Name | Supported Operating Systems | + |-------------|-----------------------|--------------------------------------------------------| + | WASAPI | ma_backend_wasapi | Windows Vista+ | + | DirectSound | ma_backend_dsound | Windows XP+ | + | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | + | Core Audio | ma_backend_coreaudio | macOS, iOS | + | ALSA | ma_backend_alsa | Linux | + | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | + | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | + | sndio | ma_backend_sndio | OpenBSD | + | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | + | OSS | ma_backend_oss | FreeBSD | + | AAudio | ma_backend_aaudio | Android 8+ | + | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | + | Web Audio | ma_backend_webaudio | Web (via Emscripten) | + | Null | ma_backend_null | Cross Platform (not used on Web) | + |-------------|-----------------------|--------------------------------------------------------| + +The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings +can then be set directly on the structure. Below are the members of the `ma_context_config` object. + + pLog + A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not + require logging. See the `ma_log` API for details on how to use the logging system. + + threadPriority + The desired priority to use for the audio thread. Allowable values include the following: + + |--------------------------------------| + | Thread Priority | + |--------------------------------------| + | ma_thread_priority_idle | + | ma_thread_priority_lowest | + | ma_thread_priority_low | + | ma_thread_priority_normal | + | ma_thread_priority_high | + | ma_thread_priority_highest (default) | + | ma_thread_priority_realtime | + | ma_thread_priority_default | + |--------------------------------------| + + threadStackSize + The desired size of the stack for the audio thread. Defaults to the operating system's default. + + pUserData + A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. + + allocationCallbacks + Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation + callbacks will be used for anything tied to the context, including devices. + + alsa.useVerboseDeviceEnumeration + ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique + card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes + it so the ALSA backend includes all devices. Defaults to false. + + pulse.pApplicationName + PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. + + pulse.pServerName + PulseAudio only. The name of the server to connect to with `pa_context_connect()`. + + pulse.tryAutoSpawn + PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that + miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be + intrusive for the end user. + + coreaudio.sessionCategory + iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + + |-----------------------------------------|-------------------------------------| + | miniaudio Token | Core Audio Token | + |-----------------------------------------|-------------------------------------| + | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | + | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | + | ma_ios_session_category_record | AVAudioSessionCategoryRecord | + | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | + | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | + | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | + | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | + |-----------------------------------------|-------------------------------------| + + coreaudio.sessionCategoryOptions + iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. + + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | miniaudio Token | Core Audio Token | + |---------------------------------------------------------------------------|------------------------------------------------------------------| + | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | + | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | + | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | + | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | + | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | + | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | + | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | + |---------------------------------------------------------------------------|------------------------------------------------------------------| + + coreaudio.noAudioSessionActivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. + + coreaudio.noAudioSessionDeactivate + iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. + + jack.pClientName + The name of the client to pass to `jack_client_open()`. + + jack.tryStartServer + Whether or not to try auto-starting the JACK server. Defaults to false. + + +It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the +relevant backends every time it's initialized. + +The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The +reason for this is that a pointer to the context is stored in the `ma_device` structure. + + +Example 1 - Default Initialization +---------------------------------- +The example below shows how to initialize the context using the default configuration. + +```c +ma_context context; +ma_result result = ma_context_init(NULL, 0, NULL, &context); +if (result != MA_SUCCESS) { + // Error. +} +``` + + +Example 2 - Custom Configuration +-------------------------------- +The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program +wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also +want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. + +For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. + +```c +ma_backend backends[] = { + ma_backend_alsa, + ma_backend_pulseaudio, + ma_backend_wasapi, + ma_backend_dsound +}; + +ma_log log; +ma_log_init(&log); +ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); + +ma_context_config config = ma_context_config_init(); +config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. + +ma_context context; +ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); +if (result != MA_SUCCESS) { + // Error. + if (result == MA_NO_BACKEND) { + // Couldn't find an appropriate backend. + } +} + +// You could also attach a log callback post-initialization: +ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); +``` + + +See Also +-------- +ma_context_config_init() +ma_context_uninit() +*/ +MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); + +/* +Uninitializes a context. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Do not call this function across multiple threads as some backends read and write to global state. + + +Remarks +------- +Results are undefined if you call this while any device created by this context is still active. + + +See Also +-------- +ma_context_init() +*/ +MA_API ma_result ma_context_uninit(ma_context* pContext); + +/* +Retrieves the size of the ma_context object. + +This is mainly for the purpose of bindings to know how much memory to allocate. +*/ +MA_API size_t ma_context_sizeof(void); + +/* +Retrieves a pointer to the log object associated with this context. + + +Remarks +------- +Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log +message. + +You can attach your own logging callback to the log with `ma_log_register_callback()` + + +Return Value +------------ +A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, +NULL will be returned. +*/ +MA_API ma_log* ma_context_get_log(ma_context* pContext); + +/* +Enumerates over every device (both playback and capture). + +This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur +an internal heap allocation, or it simply suits your code better. + +Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require +opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, +but don't call it from within the enumeration callback. + +Returning false from the callback will stop enumeration. Returning true will continue enumeration. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +callback (in) + The callback to fire for each enumerated device. + +pUserData (in) + A pointer to application-defined data passed to the callback. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. This is guarded using a simple mutex lock. + + +Remarks +------- +Do _not_ assume the first enumerated device of a given type is the default device. + +Some backends and platforms may only support default playback and capture devices. + +In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, +do not try to call `ma_context_get_device_info()` from within the callback. + +Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. + + +Example 1 - Simple Enumeration +------------------------------ +ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) +{ + printf("Device Name: %s\n", pInfo->name); + return MA_TRUE; +} + +ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); +if (result != MA_SUCCESS) { + // Error. +} + + +See Also +-------- +ma_context_get_devices() +*/ +MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); + +/* +Retrieves basic information about every active playback and/or capture device. + +This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` +parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the enumeration. + +ppPlaybackDeviceInfos (out) + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. + +pPlaybackDeviceCount (out) + A pointer to an unsigned integer that will receive the number of playback devices. + +ppCaptureDeviceInfos (out) + A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. + +pCaptureDeviceCount (out) + A pointer to an unsigned integer that will receive the number of capture devices. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple +threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. + + +Remarks +------- +It is _not_ safe to assume the first device in the list is the default device. + +You can pass in NULL for the playback or capture lists in which case they'll be ignored. + +The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. + + +See Also +-------- +ma_context_get_devices() +*/ +MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); + +/* +Retrieves information about a device of the given type, with the specified ID and share mode. + + +Parameters +---------- +pContext (in) + A pointer to the context performing the query. + +deviceType (in) + The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. + +pDeviceID (in) + The ID of the device being queried. + +pDeviceInfo (out) + A pointer to the `ma_device_info` structure that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. This is guarded using a simple mutex lock. + + +Remarks +------- +Do _not_ call this from within the `ma_context_enumerate_devices()` callback. + +It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in +shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify +which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if +the requested share mode is unsupported. + +This leaves pDeviceInfo unmodified in the result of an error. +*/ +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); + +/* +Determines if the given context supports loopback mode. + + +Parameters +---------- +pContext (in) + A pointer to the context getting queried. + + +Return Value +------------ +MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. +*/ +MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); + + + +/* +Initializes a device config with default settings. + + +Parameters +---------- +deviceType (in) + The type of the device this config is being initialized for. This must set to one of the following: + + |-------------------------| + | Device Type | + |-------------------------| + | ma_device_type_playback | + | ma_device_type_capture | + | ma_device_type_duplex | + | ma_device_type_loopback | + |-------------------------| + + +Return Value +------------ +A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. + + +Thread Safety +------------- +Safe. + + +Callback Safety +--------------- +Safe, but don't try initializing a device in a callback. + + +Remarks +------- +The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a +typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change +before initializing the device. + +See `ma_device_init()` for details on specific configuration options. + + +Example 1 - Simple Configuration +-------------------------------- +The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and +then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added +to the `ma_device_config` structure. + +```c +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pUserData = pMyUserData; +``` + + +See Also +-------- +ma_device_init() +ma_device_init_ex() +*/ +MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); + + +/* +Initializes a device. + +A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it +from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be +playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the +device is done via a callback which is fired by miniaudio at periodic time intervals. + +The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames +or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and +increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but +miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple +media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the +backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. + +When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the +format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you +can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. + + +Parameters +---------- +pContext (in, optional) + A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. + +pConfig (in) + A pointer to the device configuration. Cannot be null. See remarks for details. + +pDevice (out) + A pointer to the device object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to +calling this at the same time as `ma_device_uninit()`. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: + + ```c + ma_context_init(NULL, 0, NULL, &context); + ``` + +Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use +device.pContext for the initialization of other devices. + +The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can +then be set directly on the structure. Below are the members of the `ma_device_config` object. + + deviceType + Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. + + sampleRate + The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. + + periodSizeInFrames + The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will + be used depending on the selected performance profile. This value affects latency. See below for details. + + periodSizeInMilliseconds + The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be + used depending on the selected performance profile. The value affects latency. See below for details. + + periods + The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by + this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. + + performanceProfile + A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or + `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. + + noPreSilencedOutputBuffer + When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of + the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data + callback will write to every sample in the output buffer, or if you are doing your own clearing. + + noClip + When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or + not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only + applies when the playback sample format is f32. + + noDisableDenormals + By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. + + noFixedSizedCallback + Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a + consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with + whatever the backend requests, which could be anything. + + dataCallback + The callback to fire whenever data is ready to be delivered to or from the device. + + notificationCallback + The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. + + pUserData + The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. + + resampling.algorithm + The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The + default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. + + resampling.pBackendVTable + A pointer to an optional vtable that can be used for plugging in a custom resampler. + + resampling.pBackendUserData + A pointer that will passed to callbacks in pBackendVTable. + + resampling.linear.lpfOrder + The linear resampler applies a low-pass filter as part of it's processing for anti-aliasing. This setting controls the order of the filter. The higher + the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is + `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. + + playback.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's + default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + + playback.format + The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.playback.format`. + + playback.channels + The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.playback.channels`. + + playback.pChannelMap + The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. + + playback.shareMode + The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. + + capture.pDeviceID + A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's + default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. + + capture.format + The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after + initialization from the device object directly with `device.capture.format`. + + capture.channels + The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization + from the device object directly with `device.capture.channels`. + + capture.pChannelMap + The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the + device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. + + capture.shareMode + The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify + exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to + ma_share_mode_shared and reinitializing. + + wasapi.noAutoConvertSRC + WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. + + wasapi.noDefaultQualitySRC + WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. + You should usually leave this set to false, which is the default. + + wasapi.noAutoStreamRouting + WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. + + wasapi.noHardwareOffloading + WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. + + alsa.noMMap + ALSA only. When set to true, disables MMap mode. Defaults to false. + + alsa.noAutoFormat + ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. + + alsa.noAutoChannels + ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. + + alsa.noAutoResample + ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. + + pulse.pStreamNamePlayback + PulseAudio only. Sets the stream name for playback. + + pulse.pStreamNameCapture + PulseAudio only. Sets the stream name for capture. + + coreaudio.allowNominalSampleRateChange + Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This + is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate + that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will + find the closest match between the sample rate requested in the device config and the sample rates natively supported by the + hardware. When set to false, the sample rate currently set by the operating system will always be used. + + opensl.streamType + OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the + stream type will be left unset. Think of this as the type of audio you're playing. + + opensl.recordingPreset + OpenSL only. Explicitly sets the type of recording your program will be doing. When left + unset, the recording preset will be left unchanged. + + aaudio.usage + AAudio only. Explicitly sets the nature of the audio the program will be consuming. When + left unset, the usage will be left unchanged. + + aaudio.contentType + AAudio only. Sets the content type. When left unset, the content type will be left unchanged. + + aaudio.inputPreset + AAudio only. Explicitly sets the type of recording your program will be doing. When left + unset, the input preset will be left unchanged. + + aaudio.noAutoStartAfterReroute + AAudio only. Controls whether or not the device should be automatically restarted after a + stream reroute. When set to false (default) the device will be restarted automatically; + otherwise the device will be stopped. + + +Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. + +After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. + +If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or +`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or +`ma_performance_profile_conservative`. + +If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device +in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the +config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, +for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. +Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. + +When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config +and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run +on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, +`playback/capture.channels` and `sampleRate` members of the device object. + +When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message +asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. + +ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. +If these fail it will try falling back to the "hw" device. + + +Example 1 - Simple Initialization +--------------------------------- +This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default +playback device this is usually all you need. + +```c +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pMyUserData = pMyUserData; + +ma_device device; +ma_result result = ma_device_init(NULL, &config, &device); +if (result != MA_SUCCESS) { + // Error +} +``` + + +Example 2 - Advanced Initialization +----------------------------------- +This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size +and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device +enumeration. + +```c +ma_context context; +ma_result result = ma_context_init(NULL, 0, NULL, &context); +if (result != MA_SUCCESS) { + // Error +} + +ma_device_info* pPlaybackDeviceInfos; +ma_uint32 playbackDeviceCount; +result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); +if (result != MA_SUCCESS) { + // Error +} + +// ... choose a device from pPlaybackDeviceInfos ... + +ma_device_config config = ma_device_config_init(ma_device_type_playback); +config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). +config.playback.format = ma_format_f32; +config.playback.channels = 2; +config.sampleRate = 48000; +config.dataCallback = ma_data_callback; +config.pUserData = pMyUserData; +config.periodSizeInMilliseconds = 10; +config.periods = 3; + +ma_device device; +result = ma_device_init(&context, &config, &device); +if (result != MA_SUCCESS) { + // Error +} +``` + + +See Also +-------- +ma_device_config_init() +ma_device_uninit() +ma_device_start() +ma_context_init() +ma_context_get_devices() +ma_context_enumerate_devices() +*/ +MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); + +/* +Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. + +This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function +allows you to configure the internally created context. + + +Parameters +---------- +backends (in, optional) + A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. + +backendCount (in, optional) + The number of items in `backend`. Ignored if `backend` is NULL. + +pContextConfig (in, optional) + The context configuration. + +pConfig (in) + A pointer to the device configuration. Cannot be null. See remarks for details. + +pDevice (out) + A pointer to the device object being initialized. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to +calling this at the same time as `ma_device_uninit()`. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage +your own context. + +See the documentation for `ma_context_init()` for information on the different context configuration options. + + +See Also +-------- +ma_device_init() +ma_device_uninit() +ma_device_config_init() +ma_context_init() +*/ +MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); + +/* +Uninitializes a device. + +This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. + + +Parameters +---------- +pDevice (in) + A pointer to the device to stop. + + +Return Value +------------ +Nothing + + +Thread Safety +------------- +Unsafe. As soon as this API is called the device should be considered undefined. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. + + +See Also +-------- +ma_device_init() +ma_device_stop() +*/ +MA_API void ma_device_uninit(ma_device* pDevice); + + +/* +Retrieves a pointer to the context that owns the given device. +*/ +MA_API ma_context* ma_device_get_context(ma_device* pDevice); + +/* +Helper function for retrieving the log object associated with the context that owns this device. +*/ +MA_API ma_log* ma_device_get_log(ma_device* pDevice); + + +/* +Retrieves information about the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pDeviceInfo (out) + A pointer to the `ma_device_info` that will receive the device information. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. +*/ +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); + + +/* +Retrieves the name of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose information is being retrieved. + +type (in) + The device type. This parameter is required for duplex devices. When retrieving device + information, you are doing so for an individual playback or capture device. + +pName (out) + A pointer to the buffer that will receive the name. + +nameCap (in) + The capacity of the output buffer, including space for the null terminator. + +pLengthNotIncludingNullTerminator (out, optional) + A pointer to the variable that will receive the length of the name, not including the null + terminator. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Unsafe. This should be considered unsafe because it may be calling into the backend which may or +may not be safe. + + +Callback Safety +--------------- +Unsafe. You should avoid calling this in the data callback because it may call into the backend +which may or may not be safe. + + +Remarks +------- +If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to +`pName` if you want to first get the length of the name for the purpose of memory allocation of the +output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for +most cases and will avoid the need for the inefficiency of calling this function twice. + +This is implemented in terms of `ma_device_get_info()`. +*/ +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); + + +/* +Starts the device. For playback devices this begins playback. For capture devices it begins recording. + +Use `ma_device_stop()` to stop the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device to start. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. It's safe to call this from any thread with the exception of the callback thread. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. + + +Remarks +------- +For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid +audio data in the buffer, which needs to be done before the device begins playback. + +This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. + +Do not call this in any callback. + + +See Also +-------- +ma_device_stop() +*/ +MA_API ma_result ma_device_start(ma_device* pDevice); + +/* +Stops the device. For playback devices this stops playback. For capture devices it stops recording. + +Use `ma_device_start()` to start the device again. + + +Parameters +---------- +pDevice (in) + A pointer to the device to stop. + + +Return Value +------------ +MA_SUCCESS if successful; any other error code otherwise. + + +Thread Safety +------------- +Safe. It's safe to call this from any thread with the exception of the callback thread. + + +Callback Safety +--------------- +Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. + + +Remarks +------- +This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some +backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size +that was specified at initialization time). + +Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and +the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the +speakers or received from the microphone which can in turn result in de-syncs. + +Do not call this in any callback. + + +See Also +-------- +ma_device_start() +*/ +MA_API ma_result ma_device_stop(ma_device* pDevice); + +/* +Determines whether or not the device is started. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose start state is being retrieved. + + +Return Value +------------ +True if the device is started, false otherwise. + + +Thread Safety +------------- +Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return +value will be out of sync. + + +Callback Safety +--------------- +Safe. This is implemented as a simple accessor. + + +See Also +-------- +ma_device_start() +ma_device_stop() +*/ +MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); + + +/* +Retrieves the state of the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose state is being retrieved. + + +Return Value +------------ +The current state of the device. The return value will be one of the following: + + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_started | The device started and requesting and/or delivering audio data. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_starting | The device is in the process of starting. | + +-------------------------------+------------------------------------------------------------------------------+ + | ma_device_state_stopping | The device is in the process of stopping. | + +-------------------------------+------------------------------------------------------------------------------+ + + +Thread Safety +------------- +Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, +there's a possibility the return value could be out of sync. See remarks. + + +Callback Safety +--------------- +Safe. This is implemented as a simple accessor. + + +Remarks +------- +The general flow of a devices state goes like this: + + ``` + ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped + ma_device_start() -> ma_device_state_starting -> ma_device_state_started + ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped + ``` + +When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the +value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own +synchronization. +*/ +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); + + +/* +Performs post backend initialization routines for setting up internal data conversion. + +This should be called whenever the backend is initialized. The only time this should be called from +outside of miniaudio is if you're implementing a custom backend, and you would only do it if you +are reinitializing the backend due to rerouting or reinitializing for some reason. + + +Parameters +---------- +pDevice [in] + A pointer to the device. + +deviceType [in] + The type of the device that was just reinitialized. + +pPlaybackDescriptor [in] + The descriptor of the playback device containing the internal data format and buffer sizes. + +pPlaybackDescriptor [in] + The descriptor of the capture device containing the internal data format and buffer sizes. + + +Return Value +------------ +MA_SUCCESS if successful; any other error otherwise. + + +Thread Safety +------------- +Unsafe. This will be reinitializing internal data converters which may be in use by another thread. + + +Callback Safety +--------------- +Unsafe. This will be reinitializing internal data converters which may be in use by the callback. + + +Remarks +------- +For a duplex device, you can call this for only one side of the system. This is why the deviceType +is specified as a parameter rather than deriving it from the device. + +You do not need to call this manually unless you are doing a custom backend, in which case you need +only do it if you're manually performing rerouting or reinitialization. +*/ +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); + + +/* +Sets the master volume factor for the device. + +The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and +values less than 0 decreases the volume. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose volume is being set. + +volume (in) + The new volume factor. Must be >= 0. + + +Return Value +------------ +MA_SUCCESS if the volume was set successfully. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if volume is negative. + + +Thread Safety +------------- +Safe. This just sets a local member of the device object. + + +Callback Safety +--------------- +Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. + + +Remarks +------- +This applies the volume factor across all channels. + +This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. + + +See Also +-------- +ma_device_get_master_volume() +ma_device_set_master_volume_db() +ma_device_get_master_volume_db() +*/ +MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); + +/* +Retrieves the master volume factor for the device. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose volume factor is being retrieved. + +pVolume (in) + A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if pVolume is NULL. + + +Thread Safety +------------- +Safe. This just a simple member retrieval. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If an error occurs, `*pVolume` will be set to 0. + + +See Also +-------- +ma_device_set_master_volume() +ma_device_set_master_volume_gain_db() +ma_device_get_master_volume_gain_db() +*/ +MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); + +/* +Sets the master volume for the device as gain in decibels. + +A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose gain is being set. + +gainDB (in) + The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. + + +Return Value +------------ +MA_SUCCESS if the volume was set successfully. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if the gain is > 0. + + +Thread Safety +------------- +Safe. This just sets a local member of the device object. + + +Callback Safety +--------------- +Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. + + +Remarks +------- +This applies the gain across all channels. + +This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. + + +See Also +-------- +ma_device_get_master_volume_gain_db() +ma_device_set_master_volume() +ma_device_get_master_volume() +*/ +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); + +/* +Retrieves the master gain in decibels. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose gain is being retrieved. + +pGainDB (in) + A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if pDevice is NULL. +MA_INVALID_ARGS if pGainDB is NULL. + + +Thread Safety +------------- +Safe. This just a simple member retrieval. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If an error occurs, `*pGainDB` will be set to 0. + + +See Also +-------- +ma_device_set_master_volume_db() +ma_device_set_master_volume() +ma_device_get_master_volume() +*/ +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); + + +/* +Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. + + +Parameters +---------- +pDevice (in) + A pointer to device whose processing the data callback. + +pOutput (out) + A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device + this can be NULL, in which case pInput must not be NULL. + +pInput (in) + A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be + NULL, in which case `pOutput` must not be NULL. + +frameCount (in) + The number of frames being processed. + + +Return Value +------------ +MA_SUCCESS if successful; any other result code otherwise. + + +Thread Safety +------------- +This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a +playback and capture device in duplex setups. + + +Callback Safety +--------------- +Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. + + +Remarks +------- +If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in +which case `pInput` will be processed first, followed by `pOutput`. + +If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that +callback. +*/ +MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + + +/* +Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. + +This function is used by backends for helping determine an appropriately sized buffer to use with +the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the +`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a +best guess at the device's native sample rate is also required which is where `nativeSampleRate` +comes in. In addition, the performance profile is also needed for cases where both the period size +in frames and milliseconds are both zero. + + +Parameters +---------- +pDescriptor (in) + A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members + will be used for the calculation of the buffer size. + +nativeSampleRate (in) + The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of + `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which + case a sample rate is required to convert to a size in frames. + +performanceProfile (in) + When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are + zero, miniaudio will fall back to a buffer size based on the performance profile. The profile + to use for this calculation is determine by this parameter. + + +Return Value +------------ +The calculated buffer size in frames. + + +Thread Safety +------------- +This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function +should only ever be called from within the backend's device initialization routine and therefore +shouldn't have any multithreading concerns. + + +Callback Safety +--------------- +This is safe to call within the data callback, but there is no reason to ever do this. + + +Remarks +------- +If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that +is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); + + + +/* +Retrieves a friendly name for a backend. +*/ +MA_API const char* ma_get_backend_name(ma_backend backend); + +/* +Retrieves the backend enum from the given name. +*/ +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend); + +/* +Determines whether or not the given backend is available by the compilation environment. +*/ +MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); + +/* +Retrieves compile-time enabled backends. + + +Parameters +---------- +pBackends (out, optional) + A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting + the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. + +backendCap (in) + The capacity of the `pBackends` buffer. + +pBackendCount (out) + A pointer to the variable that will receive the enabled backend count. + + +Return Value +------------ +MA_SUCCESS if successful. +MA_INVALID_ARGS if `pBackendCount` is NULL. +MA_NO_SPACE if the capacity of `pBackends` is not large enough. + +If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. + + +Thread Safety +------------- +Safe. + + +Callback Safety +--------------- +Safe. + + +Remarks +------- +If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call +this function with `pBackends` set to NULL. + +This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` +when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at +compile time with `MA_NO_NULL`. + +The returned backends are determined based on compile time settings, not the platform it's currently running on. For +example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have +PulseAudio installed. + + +Example 1 +--------- +The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is +given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. +Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. + +``` +ma_backend enabledBackends[MA_BACKEND_COUNT]; +size_t enabledBackendCount; + +result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); +if (result != MA_SUCCESS) { + // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. +} +``` + + +See Also +-------- +ma_is_backend_enabled() +*/ +MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); + +/* +Determines whether or not loopback mode is support by a backend. +*/ +MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); + +#endif /* MA_NO_DEVICE_IO */ + + + +/************************************************************************************************************************************************************ + +Utilities + +************************************************************************************************************************************************************/ + +/* +Calculates a buffer size in milliseconds from the specified number of frames and sample rate. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); + +/* +Calculates a buffer size in frames from the specified number of milliseconds and sample rate. +*/ +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); + +/* +Copies PCM frames from one buffer to another. +*/ +MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + +/* +Copies silent frames into the given buffer. + +Remarks +------- +For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it +makes more sense for the purpose of mixing to initialize it to the center point. +*/ +MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + + +/* +Offsets a pointer by the specified number of PCM frames. +*/ +MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); +MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); +static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } +static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } + + +/* +Clips samples. +*/ +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); + +/* +Helper for applying a volume factor to samples. + +Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. +*/ +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); + +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); + +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); +MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); + +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); + + +/* +Helper for converting a linear factor to gain in decibels. +*/ +MA_API float ma_volume_linear_to_db(float factor); + +/* +Helper for converting gain in decibels to a linear factor. +*/ +MA_API float ma_volume_db_to_linear(float gain); + + +/* +Mixes the specified number of frames in floating point format with a volume factor. + +This will run on an optimized path when the volume is equal to 1. +*/ +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); + + + + +/************************************************************************************************************************************************************ + +VFS +=== + +The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely +appropriate for a given situation. + +************************************************************************************************************************************************************/ +typedef void ma_vfs; +typedef ma_handle ma_vfs_file; + +typedef enum +{ + MA_OPEN_MODE_READ = 0x00000001, + MA_OPEN_MODE_WRITE = 0x00000002 +} ma_open_mode_flags; + +typedef enum +{ + ma_seek_origin_start, + ma_seek_origin_current, + ma_seek_origin_end /* Not used by decoders. */ +} ma_seek_origin; + +typedef struct +{ + ma_uint64 sizeInBytes; +} ma_file_info; + +typedef struct +{ + ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); + ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); + ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); + ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); + ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); + ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); + ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); + ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); +} ma_vfs_callbacks; + +MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); +MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); +MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); +MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); +MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); +MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); +MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); +MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_vfs_callbacks cb; + ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ +} ma_default_vfs; + +MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); + + + +typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); +typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); +typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); + + + +#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) +typedef enum +{ + ma_encoding_format_unknown = 0, + ma_encoding_format_wav, + ma_encoding_format_flac, + ma_encoding_format_mp3, + ma_encoding_format_vorbis +} ma_encoding_format; +#endif + +/************************************************************************************************************************************************************ + +Decoding +======== + +Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless +you do your own synchronization. + +************************************************************************************************************************************************************/ +#ifndef MA_NO_DECODING +typedef struct ma_decoder ma_decoder; + + +typedef struct +{ + ma_format preferredFormat; + ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ +} ma_decoding_backend_config; + +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); + + +typedef struct +{ + ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); + ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ + void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); +} ma_decoding_backend_vtable; + + +typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ +typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); +typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); + +typedef struct +{ + ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ + ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ + ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ + ma_channel* pChannelMap; + ma_channel_mix_mode channelMixMode; + ma_dither_mode ditherMode; + ma_resampler_config resampling; + ma_allocation_callbacks allocationCallbacks; + ma_encoding_format encodingFormat; + ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ + ma_decoding_backend_vtable** ppCustomBackendVTables; + ma_uint32 customBackendCount; + void* pCustomBackendUserData; +} ma_decoder_config; + +struct ma_decoder +{ + ma_data_source_base ds; + ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ + const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ + void* pBackendUserData; + ma_decoder_read_proc onRead; + ma_decoder_seek_proc onSeek; + ma_decoder_tell_proc onTell; + void* pUserData; + ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ + ma_format outputFormat; + ma_uint32 outputChannels; + ma_uint32 outputSampleRate; + ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ + void* pInputCache; /* In input format. Can be null if it's not needed. */ + ma_uint64 inputCacheCap; /* The capacity of the input cache. */ + ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ + ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ + ma_allocation_callbacks allocationCallbacks; + union + { + struct + { + ma_vfs* pVFS; + ma_vfs_file file; + } vfs; + struct + { + const ma_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; /* Only used for decoders that were opened against a block of memory. */ + } data; +}; + +MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); +MA_API ma_decoder_config ma_decoder_config_init_default(void); + +MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); +MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); + +/* +Uninitializes a decoder. +*/ +MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); + +/* +Reads PCM frames from the given decoder. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + +/* +Seeks to a PCM frame based on it's absolute index. + +This is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); + +/* +Retrieves the decoder's output data format. +*/ +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + +/* +Retrieves the current position of the read cursor in PCM frames. +*/ +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); + +/* +Retrieves the length of the decoder in PCM frames. + +Do not call this on streams of an undefined length, such as internet radio. + +If the length is unknown or an error occurs, 0 will be returned. + +This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio +uses internally. + +For MP3's, this will decode the entire file. Do not call this in time critical scenarios. + +This function is not thread safe without your own synchronization. +*/ +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); + +/* +Retrieves the number of frames that can be read before reaching the end. + +This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in +particular ensuring you do not call it on streams of an undefined length, such as internet radio. + +If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be +returned. +*/ +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); + +/* +Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, +pConfig should be set to what you want. On output it will be set to what you got. +*/ +MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); +MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); +MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); + +#endif /* MA_NO_DECODING */ + + +/************************************************************************************************************************************************************ + +Encoding +======== + +Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. + +************************************************************************************************************************************************************/ +#ifndef MA_NO_ENCODING +typedef struct ma_encoder ma_encoder; + +typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); +typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); +typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); +typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); +typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); + +typedef struct +{ + ma_encoding_format encodingFormat; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_allocation_callbacks allocationCallbacks; +} ma_encoder_config; + +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); + +struct ma_encoder +{ + ma_encoder_config config; + ma_encoder_write_proc onWrite; + ma_encoder_seek_proc onSeek; + ma_encoder_init_proc onInit; + ma_encoder_uninit_proc onUninit; + ma_encoder_write_pcm_frames_proc onWritePCMFrames; + void* pUserData; + void* pInternalEncoder; + union + { + struct + { + ma_vfs* pVFS; + ma_vfs_file file; + } vfs; + } data; +}; + +MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); +MA_API void ma_encoder_uninit(ma_encoder* pEncoder); +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); + +#endif /* MA_NO_ENCODING */ + + +/************************************************************************************************************************************************************ + +Generation + +************************************************************************************************************************************************************/ +#ifndef MA_NO_GENERATION +typedef enum +{ + ma_waveform_type_sine, + ma_waveform_type_square, + ma_waveform_type_triangle, + ma_waveform_type_sawtooth +} ma_waveform_type; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_waveform_type type; + double amplitude; + double frequency; +} ma_waveform_config; + +MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); + +typedef struct +{ + ma_data_source_base ds; + ma_waveform_config config; + double advance; + double time; +} ma_waveform; + +MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); +MA_API void ma_waveform_uninit(ma_waveform* pWaveform); +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); +MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); +MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double dutyCycle; + double amplitude; + double frequency; +} ma_pulsewave_config; + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); + +typedef struct +{ + ma_waveform waveform; + ma_pulsewave_config config; +} ma_pulsewave; + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); + +typedef enum +{ + ma_noise_type_white, + ma_noise_type_pink, + ma_noise_type_brownian +} ma_noise_type; + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_noise_type type; + ma_int32 seed; + double amplitude; + ma_bool32 duplicateChannels; +} ma_noise_config; + +MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); + +typedef struct +{ + ma_data_source_base ds; + ma_noise_config config; + ma_lcg lcg; + union + { + struct + { + double** bin; + double* accumulation; + ma_uint32* counter; + } pink; + struct + { + double* accumulation; + } brownian; + } state; + + /* Memory management. */ + void* _pHeap; + ma_bool32 _ownsHeap; +} ma_noise; + +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); + +#endif /* MA_NO_GENERATION */ + + + +/************************************************************************************************************************************************************ + +Resource Manager + +************************************************************************************************************************************************************/ +/* The resource manager cannot be enabled if there is no decoder. */ +#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) +#define MA_NO_RESOURCE_MANAGER +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +typedef struct ma_resource_manager ma_resource_manager; +typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; +typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; +typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; +typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; + +typedef enum +{ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ + MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ +} ma_resource_manager_data_source_flags; + + +/* +Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. +*/ +typedef struct +{ + ma_async_notification* pNotification; + ma_fence* pFence; +} ma_resource_manager_pipeline_stage_notification; + +typedef struct +{ + ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ + ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ +} ma_resource_manager_pipeline_notifications; + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); + + + +/* BEGIN BACKWARDS COMPATIBILITY */ +/* TODO: Remove this block in version 0.12. */ +#if 1 +#define ma_resource_manager_job ma_job +#define ma_resource_manager_job_init ma_job_init +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING +#define ma_resource_manager_job_queue_config ma_job_queue_config +#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init +#define ma_resource_manager_job_queue ma_job_queue +#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size +#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated +#define ma_resource_manager_job_queue_init ma_job_queue_init +#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit +#define ma_resource_manager_job_queue_post ma_job_queue_post +#define ma_resource_manager_job_queue_next ma_job_queue_next +#endif +/* END BACKWARDS COMPATIBILITY */ + + + + +/* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ +#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT +#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 +#endif + +typedef enum +{ + /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ + MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, + + /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ + MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 +} ma_resource_manager_flags; + +typedef struct +{ + const char* pFilePath; + const wchar_t* pFilePathW; + const ma_resource_manager_pipeline_notifications* pNotifications; + ma_uint64 initialSeekPointInPCMFrames; + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_uint32 flags; +} ma_resource_manager_data_source_config; + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); + + +typedef enum +{ + ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ + ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ + ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ + ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ +} ma_resource_manager_data_supply_type; + +typedef struct +{ + MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ + union + { + struct + { + const void* pData; + size_t sizeInBytes; + } encoded; + struct + { + const void* pData; + ma_uint64 totalFrameCount; + ma_uint64 decodedFrameCount; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + } decoded; + struct + { + ma_paged_audio_buffer_data data; + ma_uint64 decodedFrameCount; + ma_uint32 sampleRate; + } decodedPaged; + } backend; +} ma_resource_manager_data_supply; + +struct ma_resource_manager_data_buffer_node +{ + ma_uint32 hashedName32; /* The hashed name. This is the key. */ + ma_uint32 refCount; + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ + ma_resource_manager_data_supply data; + ma_resource_manager_data_buffer_node* pParent; + ma_resource_manager_data_buffer_node* pChildLo; + ma_resource_manager_data_buffer_node* pChildHi; +}; + +struct ma_resource_manager_data_buffer +{ + ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ + ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ + ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ + ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ + MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ + ma_atomic_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ + union + { + ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ + ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ + ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ + } connector; /* Connects this object to the node's data supply. */ +}; + +struct ma_resource_manager_data_stream +{ + ma_data_source_base ds; /* Base data source. A data stream is a data source. */ + ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ + ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ + ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ + ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ + ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ + ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ + ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ + + /* Written by the public API, read by the job thread. */ + MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ + + /* Written by the job thread, read by the public API. */ + void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ + MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ + + /* Written and read by both the public API and the job thread. These must be atomic. */ + MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ + MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ + MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ + MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ +}; + +struct ma_resource_manager_data_source +{ + union + { + ma_resource_manager_data_buffer buffer; + ma_resource_manager_data_stream stream; + } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ + + ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ + MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ + MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ +}; + +typedef struct +{ + ma_allocation_callbacks allocationCallbacks; + ma_log* pLog; + ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ + ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ + ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ + ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + size_t jobThreadStackSize; + ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ + ma_uint32 flags; + ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ + ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; + ma_uint32 customDecodingBackendCount; + void* pCustomDecodingBackendUserData; +} ma_resource_manager_config; + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void); + +struct ma_resource_manager +{ + ma_resource_manager_config config; + ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ +#ifndef MA_NO_THREADING + ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ + ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ +#endif + ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ + ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ + ma_log log; /* Only used if no log was specified in the config. */ +}; + +/* Init. */ +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); + +/* Registration. */ +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); + +/* Data Buffers. */ +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); + +/* Data Streams. */ +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); + +/* Data Sources. */ +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); + +/* Job management. */ +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ +#endif /* MA_NO_RESOURCE_MANAGER */ + + + +/************************************************************************************************************************************************************ + +Node Graph + +************************************************************************************************************************************************************/ +#ifndef MA_NO_NODE_GRAPH +/* Must never exceed 254. */ +#ifndef MA_MAX_NODE_BUS_COUNT +#define MA_MAX_NODE_BUS_COUNT 254 +#endif + +/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ +#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT +#define MA_MAX_NODE_LOCAL_BUS_COUNT 2 +#endif + +/* Use this when the bus count is determined by the node instance rather than the vtable. */ +#define MA_NODE_BUS_COUNT_UNKNOWN 255 + +typedef struct ma_node_graph ma_node_graph; +typedef void ma_node; + + +/* Node flags. */ +typedef enum +{ + MA_NODE_FLAG_PASSTHROUGH = 0x00000001, + MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, + MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, + MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 +} ma_node_flags; + + +/* The playback state of a node. Either started or stopped. */ +typedef enum +{ + ma_node_state_started = 0, + ma_node_state_stopped = 1 +} ma_node_state; + + +typedef struct +{ + /* + Extended processing callback. This callback is used for effects that process input and output + at different rates (i.e. they perform resampling). This is similar to the simple version, only + they take two separate frame counts: one for input, and one for output. + + On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas + `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. + + On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set + `pFrameCountIn` to the number of input frames that were consumed. + */ + void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); + + /* + A callback for retrieving the number of a input frames that are required to output the + specified number of output frames. You would only want to implement this when the node performs + resampling. This is optional, even for nodes that perform resampling, but it does offer a + small reduction in latency as it allows miniaudio to calculate the exact number of input frames + to read at a time instead of having to estimate. + */ + ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); + + /* + The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` + parameters of the callbacks above. + */ + ma_uint8 inputBusCount; + + /* + The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` + parameters of the callbacks above. + */ + ma_uint8 outputBusCount; + + /* + Flags describing characteristics of the node. This is currently just a placeholder for some + ideas for later on. + */ + ma_uint32 flags; +} ma_node_vtable; + +typedef struct +{ + const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ + ma_node_state initialState; /* Defaults to ma_node_state_started. */ + ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ + const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ + const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ +} ma_node_config; + +MA_API ma_node_config ma_node_config_init(void); + + +/* +A node has multiple output buses. An output bus is attached to an input bus as an item in a linked +list. Think of the input bus as a linked list, with the output bus being an item in that list. +*/ +typedef struct ma_node_output_bus ma_node_output_bus; +struct ma_node_output_bus +{ + /* Immutable. */ + ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ + ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ + + /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ + ma_uint8 inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. Will only be used within the spinlock so does not need to be atomic. */ + MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ + MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ + MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + MA_ATOMIC(4, float) volume; /* Linear. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ +}; + +/* +A node has multiple input buses. The output buses of a node are connecting to the input busses of +another. An input bus is essentially just a linked list of output buses. +*/ +typedef struct ma_node_input_bus ma_node_input_bus; +struct ma_node_input_bus +{ + /* Mutable via multiple threads. */ + ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ + MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ + MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ + + /* Set once at startup. */ + ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ +}; + + +typedef struct ma_node_base ma_node_base; +struct ma_node_base +{ + /* These variables are set once at startup. */ + ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ + const ma_node_vtable* vtable; + float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ + ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ + + /* These variables are read and written only from the audio thread. */ + ma_uint16 cachedFrameCountOut; + ma_uint16 cachedFrameCountIn; + ma_uint16 consumedFrameCountIn; + + /* These variables are read and written between different threads. */ + MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ + MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ + MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_node_input_bus* pInputBuses; + ma_node_output_bus* pOutputBuses; + + /* Memory management. */ + ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; + void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ + ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ +}; + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state(const ma_node* pNode); +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); + + +typedef struct +{ + ma_uint32 channels; + ma_uint16 nodeCacheCapInFrames; +} ma_node_graph_config; + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); + + +struct ma_node_graph +{ + /* Immutable. */ + ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ + ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ + ma_uint16 nodeCacheCapInFrames; + + /* Read and written by multiple threads. */ + MA_ATOMIC(4, ma_bool32) isReading; +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); + + + +/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_data_source* pDataSource; +} ma_data_source_node_config; + +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); + + +typedef struct +{ + ma_node_base base; + ma_data_source* pDataSource; +} ma_data_source_node; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); + + +/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ +typedef struct +{ + ma_node_config nodeConfig; + ma_uint32 channels; + ma_uint32 outputBusCount; +} ma_splitter_node_config; + +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); + + +typedef struct +{ + ma_node_base base; +} ma_splitter_node; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Biquad Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_biquad_config biquad; +} ma_biquad_node_config; + +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); + + +typedef struct +{ + ma_node_base baseNode; + ma_biquad biquad; +} ma_biquad_node; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_lpf_config lpf; +} ma_lpf_node_config; + +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_lpf lpf; +} ma_lpf_node; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hpf_config hpf; +} ma_hpf_node_config; + +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_hpf hpf; +} ma_hpf_node; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Band Pass Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_bpf_config bpf; +} ma_bpf_node_config; + +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); + + +typedef struct +{ + ma_node_base baseNode; + ma_bpf bpf; +} ma_bpf_node; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Notching Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_notch_config notch; +} ma_notch_node_config; + +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_notch2 notch; +} ma_notch_node; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Peaking Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_peak_config peak; +} ma_peak_node_config; + +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_peak2 peak; +} ma_peak_node; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +Low Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_loshelf_config loshelf; +} ma_loshelf_node_config; + +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_loshelf2 loshelf; +} ma_loshelf_node; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +/* +High Shelf Filter Node +*/ +typedef struct +{ + ma_node_config nodeConfig; + ma_hishelf_config hishelf; +} ma_hishelf_node_config; + +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); + + +typedef struct +{ + ma_node_base baseNode; + ma_hishelf2 hishelf; +} ma_hishelf_node; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_node_config nodeConfig; + ma_delay_config delay; +} ma_delay_node_config; + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); + + +typedef struct +{ + ma_node_base baseNode; + ma_delay delay; +} ma_delay_node; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); +#endif /* MA_NO_NODE_GRAPH */ + + +/* SECTION: miniaudio_engine.h */ +/************************************************************************************************************************************************************ + +Engine + +************************************************************************************************************************************************************/ +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +typedef struct ma_engine ma_engine; +typedef struct ma_sound ma_sound; + + +/* Sound flags. */ +typedef enum +{ + /* Resource manager flags. */ + MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ + MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ + MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ + MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ +} ma_sound_flags; + +#ifndef MA_ENGINE_MAX_LISTENERS +#define MA_ENGINE_MAX_LISTENERS 4 +#endif + +#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) + +typedef enum +{ + ma_engine_node_type_sound, + ma_engine_node_type_group +} ma_engine_node_type; + +typedef struct +{ + ma_engine* pEngine; + ma_engine_node_type type; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ + ma_mono_expansion_mode monoExpansionMode; + ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ + ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ + ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ +} ma_engine_node_config; + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); + + +/* Base node object for both ma_sound and ma_sound_group. */ +typedef struct +{ + ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ + ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ + ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_uint32 volumeSmoothTimeInPCMFrames; + ma_mono_expansion_mode monoExpansionMode; + ma_fader fader; + ma_linear_resampler resampler; /* For pitch shift. */ + ma_spatializer spatializer; + ma_panner panner; + ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ + ma_atomic_float volume; /* Defaults to 1. */ + MA_ATOMIC(4, float) pitch; + float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ + float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ + MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ + MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ + MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + + /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ + struct + { + ma_atomic_float volumeBeg; + ma_atomic_float volumeEnd; + ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ + ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ + } fadeSettings; + + /* Memory management. */ + ma_bool8 _ownsHeap; + void* _pHeap; +} ma_engine_node; + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); + + +#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF + +/* Callback for when a sound reaches the end. */ +typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); + +typedef struct +{ + const char* pFilePath; /* Set this to load from the resource manager. */ + const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ + ma_data_source* pDataSource; /* Set this to load from an existing data source. */ + ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ + ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ + ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ + ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ + ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ + ma_uint64 rangeBegInPCMFrames; + ma_uint64 rangeEndInPCMFrames; + ma_uint64 loopPointBegInPCMFrames; + ma_uint64 loopPointEndInPCMFrames; + ma_bool32 isLooping; + ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ + void* pEndCallbackUserData; +#ifndef MA_NO_RESOURCE_MANAGER + ma_resource_manager_pipeline_notifications initNotifications; +#endif + ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ +} ma_sound_config; + +MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ + +struct ma_sound +{ + ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ + ma_data_source* pDataSource; + MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ + MA_ATOMIC(4, ma_bool32) atEnd; + ma_sound_end_proc endCallback; + void* pEndCallbackUserData; + ma_bool8 ownsDataSource; + + /* + We're declaring a resource manager data source object here to save us a malloc when loading a + sound via the resource manager, which I *think* will be the most common scenario. + */ +#ifndef MA_NO_RESOURCE_MANAGER + ma_resource_manager_data_source* pResourceManagerDataSource; +#endif +}; + +/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ +typedef struct ma_sound_inlined ma_sound_inlined; +struct ma_sound_inlined +{ + ma_sound sound; + ma_sound_inlined* pNext; + ma_sound_inlined* pPrev; +}; + +/* A sound group is just a sound. */ +typedef ma_sound_config ma_sound_group_config; +typedef ma_sound ma_sound_group; + +MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ + +typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); + +typedef struct +{ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_context* pContext; + ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ + ma_device_notification_proc notificationCallback; +#endif + ma_log* pLog; /* When set to NULL, will use the context's log. */ + ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ + ma_allocation_callbacks allocationCallbacks; + ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ + void* pProcessUserData; /* User data that's passed into onProcess. */ +} ma_engine_config; + +MA_API ma_engine_config ma_engine_config_init(void); + + +struct ma_engine +{ + ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ +#if !defined(MA_NO_RESOURCE_MANAGER) + ma_resource_manager* pResourceManager; +#endif +#if !defined(MA_NO_DEVICE_IO) + ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ +#endif + ma_log* pLog; + ma_uint32 sampleRate; + ma_uint32 listenerCount; + ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; + ma_allocation_callbacks allocationCallbacks; + ma_bool8 ownsResourceManager; + ma_bool8 ownsDevice; + ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ + ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ + MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; + ma_mono_expansion_mode monoExpansionMode; + ma_engine_process_proc onProcess; + void* pProcessUserData; +}; + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); +MA_API void ma_engine_uninit(ma_engine* pEngine); +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); +#endif +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); + +MA_API ma_result ma_engine_start(ma_engine* pEngine); +MA_API ma_result ma_engine_stop(ma_engine* pEngine); +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); +MA_API float ma_engine_get_volume(ma_engine* pEngine); +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); +MA_API float ma_engine_get_gain_db(ma_engine* pEngine); + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ +#endif + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +#endif +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); +MA_API void ma_sound_uninit(ma_sound* pSound); +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); +MA_API ma_result ma_sound_start(ma_sound* pSound); +MA_API ma_result ma_sound_stop(ma_sound* pSound); +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); +MA_API float ma_sound_get_volume(const ma_sound* pSound); +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); +MA_API float ma_sound_get_pan(const ma_sound* pSound); +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); +MA_API float ma_sound_get_pitch(const ma_sound* pSound); +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); +MA_API float ma_sound_get_rolloff(const ma_sound* pSound); +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); +MA_API float ma_sound_get_min_gain(const ma_sound* pSound); +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); +MA_API float ma_sound_get_max_gain(const ma_sound* pSound); +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); +MA_API float ma_sound_get_min_distance(const ma_sound* pSound); +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); +MA_API float ma_sound_get_max_distance(const ma_sound* pSound); +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); +#endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.h */ + +#ifdef __cplusplus +} +#endif +#endif /* miniaudio_h */ + + +/* +This is for preventing greying out of the implementation section. +*/ +#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) +#define MINIAUDIO_IMPLEMENTATION +#endif + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +IMPLEMENTATION + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ +#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) +#ifndef miniaudio_c +#define miniaudio_c + +#include +#include /* For INT_MAX */ +#include /* sin(), etc. */ +#include /* For malloc(), free(), wcstombs(). */ +#include /* For memset() */ + +#include +#include +#if !defined(_MSC_VER) && !defined(__DMC__) + #include /* For strcasecmp(). */ + #include /* For wcslen(), wcsrtombs() */ +#endif +#ifdef _MSC_VER + #include /* For _controlfp_s constants */ +#endif + +#if defined(MA_WIN32) + #include + + /* + There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols + such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're + unavailable. + */ + #ifndef STGM_READ + #define STGM_READ 0x00000000L + #endif + #ifndef CLSCTX_ALL + #define CLSCTX_ALL 23 + #endif + + /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ + typedef struct ma_IUnknown ma_IUnknown; +#endif + +#if !defined(MA_WIN32) +#include +#include /* select() (used for ma_sleep()). */ +#include +#endif + +#ifdef MA_NX +#include /* For nanosleep() */ +#endif + +#include /* For fstat(), etc. */ + +#ifdef MA_EMSCRIPTEN +#include +#endif + + +/* Architecture Detection */ +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#ifdef _WIN32 +#ifdef _WIN64 +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif +#endif + +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#ifdef __GNUC__ +#ifdef __LP64__ +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif +#endif + +#if !defined(MA_64BIT) && !defined(MA_32BIT) +#include +#if INTPTR_MAX == INT64_MAX +#define MA_64BIT +#else +#define MA_32BIT +#endif +#endif + +#if defined(__arm__) || defined(_M_ARM) +#define MA_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define MA_ARM64 +#endif + +#if defined(__x86_64__) || defined(_M_X64) +#define MA_X64 +#elif defined(__i386) || defined(_M_IX86) +#define MA_X86 +#elif defined(MA_ARM32) || defined(MA_ARM64) +#define MA_ARM +#endif + +/* Intrinsics Support */ +#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) + #if defined(_MSC_VER) && !defined(__clang__) + /* MSVC. */ + #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ + #define MA_SUPPORT_SSE2 + #endif + /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ + #define MA_SUPPORT_AVX2 + #endif + #else + /* Assume GNUC-style. */ + #if defined(__SSE2__) && !defined(MA_NO_SSE2) + #define MA_SUPPORT_SSE2 + #endif + /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if defined(__AVX2__) && !defined(MA_NO_AVX2) + #define MA_SUPPORT_AVX2 + #endif + #endif + + /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() + #define MA_SUPPORT_SSE2 + #endif + /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ + /* #define MA_SUPPORT_AVX*/ + /*#endif*/ + #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() + #define MA_SUPPORT_AVX2 + #endif + #endif + + #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) + #include + #elif defined(MA_SUPPORT_SSE2) + #include + #endif +#endif + +#if defined(MA_ARM) + #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_SUPPORT_NEON + #include + #endif +#endif + +/* Begin globally disabled warnings. */ +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ + #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ +#endif + +#if defined(MA_X64) || defined(MA_X86) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static MA_INLINE void ma_cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define MA_NO_CPUID + #endif + + #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) + static MA_INLINE unsigned __int64 ma_xgetbv(int reg) + { + return _xgetbv(reg); + } + #else + #define MA_NO_XGETBV + #endif + #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) + static MA_INLINE void ma_cpuid(int info[4], int fid) + { + /* + 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(MA_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 MA_INLINE ma_uint64 ma_xgetbv(int reg) + { + unsigned int hi; + unsigned int lo; + + __asm__ __volatile__ ( + "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) + ); + + return ((ma_uint64)hi << 32) | (ma_uint64)lo; + } + #else + #define MA_NO_CPUID + #define MA_NO_XGETBV + #endif +#else + #define MA_NO_CPUID + #define MA_NO_XGETBV +#endif + +static MA_INLINE ma_bool32 ma_has_sse2(void) +{ +#if defined(MA_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; /* 64-bit targets always support SSE2. */ + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ + #else + #if defined(MA_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +#if 0 +static MA_INLINE ma_bool32 ma_has_avx() +{ +#if defined(MA_SUPPORT_AVX) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) + #if defined(_AVX_) || defined(__AVX__) + return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ + #else + /* AVX requires both CPU and OS support. */ + #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) + return MA_FALSE; + #else + int info[4]; + ma_cpuid(info, 1); + if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { + ma_uint64 xrc = ma_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + #endif + #endif + #else + return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} +#endif + +static MA_INLINE ma_bool32 ma_has_avx2(void) +{ +#if defined(MA_SUPPORT_AVX2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) + #if defined(_AVX2_) || defined(__AVX2__) + return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ + #else + /* AVX2 requires both CPU and OS support. */ + #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) + return MA_FALSE; + #else + int info1[4]; + int info7[4]; + ma_cpuid(info1, 1); + ma_cpuid(info7, 7); + if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { + ma_uint64 xrc = ma_xgetbv(0); + if ((xrc & 0x06) == 0x06) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + #endif + #endif + #else + return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +static MA_INLINE ma_bool32 ma_has_neon(void) +{ +#if defined(MA_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ + #else + /* TODO: Runtime check. */ + return MA_FALSE; + #endif + #else + return MA_FALSE; /* NEON is only supported on ARM architectures. */ + #endif +#else + return MA_FALSE; /* No compiler support. */ +#endif +} + +#if defined(__has_builtin) + #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) +#else + #define MA_COMPILER_HAS_BUILTIN(x) 0 +#endif + +#ifndef MA_ASSUME + #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) + #define MA_ASSUME(x) __builtin_assume(x) + #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) + #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) + #elif defined(_MSC_VER) + #define MA_ASSUME(x) __assume(x) + #else + #define MA_ASSUME(x) (void)(x) + #endif +#endif + +#ifndef MA_RESTRICT + #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) + #define MA_RESTRICT __restrict + #else + #define MA_RESTRICT + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define MA_HAS_BYTESWAP16_INTRINSIC + #define MA_HAS_BYTESWAP32_INTRINSIC + #define MA_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) + #define MA_HAS_BYTESWAP16_INTRINSIC + #endif + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) + #define MA_HAS_BYTESWAP32_INTRINSIC + #endif + #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) + #define MA_HAS_BYTESWAP64_INTRINSIC + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_HAS_BYTESWAP32_INTRINSIC + #define MA_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_HAS_BYTESWAP16_INTRINSIC + #endif +#endif + + +static MA_INLINE ma_bool32 ma_is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static MA_INLINE ma_bool32 ma_is_big_endian(void) +{ + return !ma_is_little_endian(); +} + + +static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) +{ +#ifdef MA_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ + /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + + +#if !defined(MA_EMSCRIPTEN) +#ifdef MA_WIN32 +static void ma_sleep__win32(ma_uint32 milliseconds) +{ + Sleep((DWORD)milliseconds); +} +#endif +#ifdef MA_POSIX +static void ma_sleep__posix(ma_uint32 milliseconds) +{ +#ifdef MA_EMSCRIPTEN + (void)milliseconds; + MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ +#else + #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX) + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = milliseconds % 1000 * 1000000; + nanosleep(&ts, NULL); + #else + struct timeval tv; + tv.tv_sec = milliseconds / 1000; + tv.tv_usec = milliseconds % 1000 * 1000; + select(0, NULL, NULL, NULL, &tv); + #endif +#endif +} +#endif + +static MA_INLINE void ma_sleep(ma_uint32 milliseconds) +{ +#ifdef MA_WIN32 + ma_sleep__win32(milliseconds); +#endif +#ifdef MA_POSIX + ma_sleep__posix(milliseconds); +#endif +} +#endif + +static MA_INLINE void ma_yield(void) +{ +#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) + /* x86/x64 */ + #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) + #if _MSC_VER >= 1400 + _mm_pause(); + #else + #if defined(__DMC__) + /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ + __asm nop; + #else + __asm pause; + #endif + #endif + #else + __asm__ __volatile__ ("pause"); + #endif +#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) + /* ARM */ + #if defined(_MSC_VER) + /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ + __yield(); + #else + __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ + #endif +#else + /* Unknown or unsupported architecture. No-op. */ +#endif +} + + +#define MA_MM_DENORMALS_ZERO_MASK 0x0040 +#define MA_MM_FLUSH_ZERO_MASK 0x8000 + +static MA_INLINE unsigned int ma_disable_denormals(void) +{ + unsigned int prevState; + + #if defined(_MSC_VER) + { + /* + Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't + know which version of Visual Studio first added support for _controlfp_s(), but I do know + that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older + versions of Visual Studio, let me know and I'll make the necessary adjustment. + */ + #if _MSC_VER <= 1200 + { + prevState = _statusfp(); + _controlfp(prevState | _DN_FLUSH, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&prevState, 0, 0); + _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + prevState = _mm_getcsr(); + _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + prevState = 0; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + prevState = 0; + } + #endif + + return prevState; +} + +static MA_INLINE void ma_restore_denormals(unsigned int prevState) +{ + #if defined(_MSC_VER) + { + /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ + #if _MSC_VER <= 1200 + { + _controlfp(prevState, _MCW_DN); + } + #else + { + unsigned int unused; + _controlfp_s(&unused, prevState, _MCW_DN); + } + #endif + } + #elif defined(MA_X86) || defined(MA_X64) + { + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + { + _mm_setcsr(prevState); + } + #else + { + /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ + (void)prevState; + } + #endif + } + #else + { + /* Unknown or unsupported architecture. No-op. */ + (void)prevState; + } + #endif +} + + +#ifdef MA_ANDROID +#include + +int ma_android_sdk_version() +{ + char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; + if (__system_property_get("ro.build.version.sdk", sdkVersion)) { + return atoi(sdkVersion); + } + + return 0; +} +#endif + + +#ifndef MA_COINIT_VALUE +#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ +#endif + + +#ifndef MA_FLT_MAX + #ifdef FLT_MAX + #define MA_FLT_MAX FLT_MAX + #else + #define MA_FLT_MAX 3.402823466e+38F + #endif +#endif + + +#ifndef MA_PI +#define MA_PI 3.14159265358979323846264f +#endif +#ifndef MA_PI_D +#define MA_PI_D 3.14159265358979323846264 +#endif +#ifndef MA_TAU +#define MA_TAU 6.28318530717958647693f +#endif +#ifndef MA_TAU_D +#define MA_TAU_D 6.28318530717958647693 +#endif + + +/* The default format when ma_format_unknown (0) is requested when initializing a device. */ +#ifndef MA_DEFAULT_FORMAT +#define MA_DEFAULT_FORMAT ma_format_f32 +#endif + +/* The default channel count to use when 0 is used when initializing a device. */ +#ifndef MA_DEFAULT_CHANNELS +#define MA_DEFAULT_CHANNELS 2 +#endif + +/* The default sample rate to use when 0 is used when initializing a device. */ +#ifndef MA_DEFAULT_SAMPLE_RATE +#define MA_DEFAULT_SAMPLE_RATE 48000 +#endif + +/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ +#ifndef MA_DEFAULT_PERIODS +#define MA_DEFAULT_PERIODS 3 +#endif + +/* The default period size in milliseconds for low latency mode. */ +#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY +#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 +#endif + +/* The default buffer size in milliseconds for conservative mode. */ +#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE +#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 +#endif + +/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ +#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER + #if MA_MAX_FILTER_ORDER >= 4 + #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 + #else + #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER + #endif +#endif + + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-variable" +#endif + +/* Standard sample rates, in order of priority. */ +static ma_uint32 g_maStandardSampleRatePriorities[] = { + (ma_uint32)ma_standard_sample_rate_48000, + (ma_uint32)ma_standard_sample_rate_44100, + + (ma_uint32)ma_standard_sample_rate_32000, + (ma_uint32)ma_standard_sample_rate_24000, + (ma_uint32)ma_standard_sample_rate_22050, + + (ma_uint32)ma_standard_sample_rate_88200, + (ma_uint32)ma_standard_sample_rate_96000, + (ma_uint32)ma_standard_sample_rate_176400, + (ma_uint32)ma_standard_sample_rate_192000, + + (ma_uint32)ma_standard_sample_rate_16000, + (ma_uint32)ma_standard_sample_rate_11025, + (ma_uint32)ma_standard_sample_rate_8000, + + (ma_uint32)ma_standard_sample_rate_352800, + (ma_uint32)ma_standard_sample_rate_384000 +}; + +static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) +{ + ma_uint32 iSampleRate; + + for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { + if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { + return MA_TRUE; + } + } + + /* Getting here means the sample rate is not supported. */ + return MA_FALSE; +} + + +static ma_format g_maFormatPriorities[] = { + ma_format_s16, /* Most common */ + ma_format_f32, + + /*ma_format_s24_32,*/ /* Clean alignment */ + ma_format_s32, + + ma_format_s24, /* Unclean alignment */ + + ma_format_u8 /* Low quality */ +}; +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif + + +MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_VERSION_MAJOR; + } + + if (pMinor) { + *pMinor = MA_VERSION_MINOR; + } + + if (pRevision) { + *pRevision = MA_VERSION_REVISION; + } +} + +MA_API const char* ma_version_string(void) +{ + return MA_VERSION_STRING; +} + + +/****************************************************************************** + +Standard Library Stuff + +******************************************************************************/ +#ifndef MA_ASSERT +#define MA_ASSERT(condition) assert(condition) +#endif + +#ifndef MA_MALLOC +#define MA_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_REALLOC +#define MA_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_FREE +#define MA_FREE(p) free((p)) +#endif + +static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) +{ + if (p == NULL) { + MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */ + return; + } + + if (sz > 0) { + memset(p, 0, sz); + } +} + + +#ifndef MA_ZERO_MEMORY +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) +#endif +#ifndef MA_COPY_MEMORY +#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_MOVE_MEMORY +#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif + +#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) + +#define ma_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) +#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) +#define ma_abs(x) (((x) > 0) ? (x) : -(x)) +#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) +#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) +#define ma_align_64(x) ma_align(x, 8) + +#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) + +static MA_INLINE double ma_sind(double x) +{ + /* TODO: Implement custom sin(x). */ + return sin(x); +} + +static MA_INLINE double ma_expd(double x) +{ + /* TODO: Implement custom exp(x). */ + return exp(x); +} + +static MA_INLINE double ma_logd(double x) +{ + /* TODO: Implement custom log(x). */ + return log(x); +} + +static MA_INLINE double ma_powd(double x, double y) +{ + /* TODO: Implement custom pow(x, y). */ + return pow(x, y); +} + +static MA_INLINE double ma_sqrtd(double x) +{ + /* TODO: Implement custom sqrt(x). */ + return sqrt(x); +} + + +static MA_INLINE float ma_rsqrtf(float x) +{ + #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)) + { + /* + For SSE we can use RSQRTSS. + + This Stack Overflow post suggests that compilers don't necessarily generate optimal code + when using intrinsics: + + https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper + + I'm going to do something similar here, but a bit simpler. + */ + #if defined(__GNUC__) || defined(__clang__) + { + float result; + __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x)); + return result; + } + #else + { + return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x))); + } + #endif + } + #else + { + return 1 / (float)ma_sqrtd(x); + } + #endif +} + + +static MA_INLINE float ma_sinf(float x) +{ + return (float)ma_sind((float)x); +} + +static MA_INLINE double ma_cosd(double x) +{ + return ma_sind((MA_PI_D*0.5) - x); +} + +static MA_INLINE float ma_cosf(float x) +{ + return (float)ma_cosd((float)x); +} + +static MA_INLINE double ma_log10d(double x) +{ + return ma_logd(x) * 0.43429448190325182765; +} + +static MA_INLINE float ma_powf(float x, float y) +{ + return (float)ma_powd((double)x, (double)y); +} + +static MA_INLINE float ma_log10f(float x) +{ + return (float)ma_log10d((double)x); +} + + +static MA_INLINE double ma_degrees_to_radians(double degrees) +{ + return degrees * 0.01745329252; +} + +static MA_INLINE double ma_radians_to_degrees(double radians) +{ + return radians * 57.295779512896; +} + +static MA_INLINE float ma_degrees_to_radians_f(float degrees) +{ + return degrees * 0.01745329252f; +} + +static MA_INLINE float ma_radians_to_degrees_f(float radians) +{ + return radians * 57.295779512896f; +} + + +/* +Return Values: + 0: Success + 22: EINVAL + 34: ERANGE + +Not using symbolic constants for errors because I want to avoid #including errno.h + +These are marked as no-inline because of some bad code generation by Clang. None of these functions +are used in any performance-critical code within miniaudio. +*/ +MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstSizeInBytes) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + + +MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + size_t maxcount; + size_t i; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + maxcount = count; + if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ + maxcount = dstSizeInBytes - 1; + } + + for (i = 0; i < maxcount && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (src[i] == '\0' || i == count || count == ((size_t)-1)) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} + +MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +{ + char* dstorig; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; /* Unterminated. */ + } + + + while (dstSizeInBytes > 0 && src[0] != '\0') { + *dst++ = *src++; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +{ + char* dstorig; + + if (dst == 0) { + return 22; + } + if (dstSizeInBytes == 0) { + return 34; + } + if (src == 0) { + return 22; + } + + dstorig = dst; + + while (dstSizeInBytes > 0 && dst[0] != '\0') { + dst += 1; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + return 22; /* Unterminated. */ + } + + + if (count == ((size_t)-1)) { /* _TRUNCATE */ + count = dstSizeInBytes - 1; + } + + while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { + *dst++ = *src++; + dstSizeInBytes -= 1; + count -= 1; + } + + if (dstSizeInBytes > 0) { + dst[0] = '\0'; + } else { + dstorig[0] = '\0'; + return 34; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +{ + int sign; + unsigned int valueU; + char* dstEnd; + + if (dst == NULL || dstSizeInBytes == 0) { + return 22; + } + if (radix < 2 || radix > 36) { + dst[0] = '\0'; + return 22; + } + + sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ + + if (value < 0) { + valueU = -value; + } else { + valueU = value; + } + + dstEnd = dst; + do + { + int remainder = valueU % radix; + if (remainder > 9) { + *dstEnd = (char)((remainder - 10) + 'a'); + } else { + *dstEnd = (char)(remainder + '0'); + } + + dstEnd += 1; + dstSizeInBytes -= 1; + valueU /= radix; + } while (dstSizeInBytes > 0 && valueU > 0); + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; /* Ran out of room in the output buffer. */ + } + + if (sign < 0) { + *dstEnd++ = '-'; + dstSizeInBytes -= 1; + } + + if (dstSizeInBytes == 0) { + dst[0] = '\0'; + return 22; /* Ran out of room in the output buffer. */ + } + + *dstEnd = '\0'; + + + /* At this point the string will be reversed. */ + dstEnd -= 1; + while (dst < dstEnd) { + char temp = *dst; + *dst = *dstEnd; + *dstEnd = temp; + + dst += 1; + dstEnd -= 1; + } + + return 0; +} + +MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) +{ + if (str1 == str2) return 0; + + /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ + if (str1 == NULL) return -1; + if (str2 == NULL) return 1; + + for (;;) { + if (str1[0] == '\0') { + break; + } + if (str1[0] != str2[0]) { + break; + } + + str1 += 1; + str2 += 1; + } + + return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; +} + +MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +{ + int result; + + result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); + if (result != 0) { + return result; + } + + result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); + if (result != 0) { + return result; + } + + return result; +} + +MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t sz; + char* dst; + + if (src == NULL) { + return NULL; + } + + sz = strlen(src)+1; + dst = (char*)ma_malloc(sz, pAllocationCallbacks); + if (dst == NULL) { + return NULL; + } + + ma_strcpy_s(dst, sz, src); + + return dst; +} + +MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t sz = wcslen(src)+1; + wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); + if (dst == NULL) { + return NULL; + } + + ma_wcscpy_s(dst, sz, src); + + return dst; +} + + + +#include +static ma_result ma_result_from_errno(int e) +{ + if (e == 0) { + return MA_SUCCESS; + } +#ifdef EPERM + else if (e == EPERM) { return MA_INVALID_OPERATION; } +#endif +#ifdef ENOENT + else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ESRCH + else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EINTR + else if (e == EINTR) { return MA_INTERRUPT; } +#endif +#ifdef EIO + else if (e == EIO) { return MA_IO_ERROR; } +#endif +#ifdef ENXIO + else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef E2BIG + else if (e == E2BIG) { return MA_INVALID_ARGS; } +#endif +#ifdef ENOEXEC + else if (e == ENOEXEC) { return MA_INVALID_FILE; } +#endif +#ifdef EBADF + else if (e == EBADF) { return MA_INVALID_FILE; } +#endif +#ifdef ECHILD + else if (e == ECHILD) { return MA_ERROR; } +#endif +#ifdef EAGAIN + else if (e == EAGAIN) { return MA_UNAVAILABLE; } +#endif +#ifdef ENOMEM + else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } +#endif +#ifdef EACCES + else if (e == EACCES) { return MA_ACCESS_DENIED; } +#endif +#ifdef EFAULT + else if (e == EFAULT) { return MA_BAD_ADDRESS; } +#endif +#ifdef ENOTBLK + else if (e == ENOTBLK) { return MA_ERROR; } +#endif +#ifdef EBUSY + else if (e == EBUSY) { return MA_BUSY; } +#endif +#ifdef EEXIST + else if (e == EEXIST) { return MA_ALREADY_EXISTS; } +#endif +#ifdef EXDEV + else if (e == EXDEV) { return MA_ERROR; } +#endif +#ifdef ENODEV + else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ENOTDIR + else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } +#endif +#ifdef EISDIR + else if (e == EISDIR) { return MA_IS_DIRECTORY; } +#endif +#ifdef EINVAL + else if (e == EINVAL) { return MA_INVALID_ARGS; } +#endif +#ifdef ENFILE + else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef EMFILE + else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef ENOTTY + else if (e == ENOTTY) { return MA_INVALID_OPERATION; } +#endif +#ifdef ETXTBSY + else if (e == ETXTBSY) { return MA_BUSY; } +#endif +#ifdef EFBIG + else if (e == EFBIG) { return MA_TOO_BIG; } +#endif +#ifdef ENOSPC + else if (e == ENOSPC) { return MA_NO_SPACE; } +#endif +#ifdef ESPIPE + else if (e == ESPIPE) { return MA_BAD_SEEK; } +#endif +#ifdef EROFS + else if (e == EROFS) { return MA_ACCESS_DENIED; } +#endif +#ifdef EMLINK + else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef EPIPE + else if (e == EPIPE) { return MA_BAD_PIPE; } +#endif +#ifdef EDOM + else if (e == EDOM) { return MA_OUT_OF_RANGE; } +#endif +#ifdef ERANGE + else if (e == ERANGE) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EDEADLK + else if (e == EDEADLK) { return MA_DEADLOCK; } +#endif +#ifdef ENAMETOOLONG + else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } +#endif +#ifdef ENOLCK + else if (e == ENOLCK) { return MA_ERROR; } +#endif +#ifdef ENOSYS + else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } +#endif +#ifdef ENOTEMPTY + else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } +#endif +#ifdef ELOOP + else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef ENOMSG + else if (e == ENOMSG) { return MA_NO_MESSAGE; } +#endif +#ifdef EIDRM + else if (e == EIDRM) { return MA_ERROR; } +#endif +#ifdef ECHRNG + else if (e == ECHRNG) { return MA_ERROR; } +#endif +#ifdef EL2NSYNC + else if (e == EL2NSYNC) { return MA_ERROR; } +#endif +#ifdef EL3HLT + else if (e == EL3HLT) { return MA_ERROR; } +#endif +#ifdef EL3RST + else if (e == EL3RST) { return MA_ERROR; } +#endif +#ifdef ELNRNG + else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EUNATCH + else if (e == EUNATCH) { return MA_ERROR; } +#endif +#ifdef ENOCSI + else if (e == ENOCSI) { return MA_ERROR; } +#endif +#ifdef EL2HLT + else if (e == EL2HLT) { return MA_ERROR; } +#endif +#ifdef EBADE + else if (e == EBADE) { return MA_ERROR; } +#endif +#ifdef EBADR + else if (e == EBADR) { return MA_ERROR; } +#endif +#ifdef EXFULL + else if (e == EXFULL) { return MA_ERROR; } +#endif +#ifdef ENOANO + else if (e == ENOANO) { return MA_ERROR; } +#endif +#ifdef EBADRQC + else if (e == EBADRQC) { return MA_ERROR; } +#endif +#ifdef EBADSLT + else if (e == EBADSLT) { return MA_ERROR; } +#endif +#ifdef EBFONT + else if (e == EBFONT) { return MA_INVALID_FILE; } +#endif +#ifdef ENOSTR + else if (e == ENOSTR) { return MA_ERROR; } +#endif +#ifdef ENODATA + else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ETIME + else if (e == ETIME) { return MA_TIMEOUT; } +#endif +#ifdef ENOSR + else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ENONET + else if (e == ENONET) { return MA_NO_NETWORK; } +#endif +#ifdef ENOPKG + else if (e == ENOPKG) { return MA_ERROR; } +#endif +#ifdef EREMOTE + else if (e == EREMOTE) { return MA_ERROR; } +#endif +#ifdef ENOLINK + else if (e == ENOLINK) { return MA_ERROR; } +#endif +#ifdef EADV + else if (e == EADV) { return MA_ERROR; } +#endif +#ifdef ESRMNT + else if (e == ESRMNT) { return MA_ERROR; } +#endif +#ifdef ECOMM + else if (e == ECOMM) { return MA_ERROR; } +#endif +#ifdef EPROTO + else if (e == EPROTO) { return MA_ERROR; } +#endif +#ifdef EMULTIHOP + else if (e == EMULTIHOP) { return MA_ERROR; } +#endif +#ifdef EDOTDOT + else if (e == EDOTDOT) { return MA_ERROR; } +#endif +#ifdef EBADMSG + else if (e == EBADMSG) { return MA_BAD_MESSAGE; } +#endif +#ifdef EOVERFLOW + else if (e == EOVERFLOW) { return MA_TOO_BIG; } +#endif +#ifdef ENOTUNIQ + else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } +#endif +#ifdef EBADFD + else if (e == EBADFD) { return MA_ERROR; } +#endif +#ifdef EREMCHG + else if (e == EREMCHG) { return MA_ERROR; } +#endif +#ifdef ELIBACC + else if (e == ELIBACC) { return MA_ACCESS_DENIED; } +#endif +#ifdef ELIBBAD + else if (e == ELIBBAD) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBSCN + else if (e == ELIBSCN) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBMAX + else if (e == ELIBMAX) { return MA_ERROR; } +#endif +#ifdef ELIBEXEC + else if (e == ELIBEXEC) { return MA_ERROR; } +#endif +#ifdef EILSEQ + else if (e == EILSEQ) { return MA_INVALID_DATA; } +#endif +#ifdef ERESTART + else if (e == ERESTART) { return MA_ERROR; } +#endif +#ifdef ESTRPIPE + else if (e == ESTRPIPE) { return MA_ERROR; } +#endif +#ifdef EUSERS + else if (e == EUSERS) { return MA_ERROR; } +#endif +#ifdef ENOTSOCK + else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } +#endif +#ifdef EDESTADDRREQ + else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } +#endif +#ifdef EMSGSIZE + else if (e == EMSGSIZE) { return MA_TOO_BIG; } +#endif +#ifdef EPROTOTYPE + else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } +#endif +#ifdef ENOPROTOOPT + else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } +#endif +#ifdef EPROTONOSUPPORT + else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } +#endif +#ifdef ESOCKTNOSUPPORT + else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } +#endif +#ifdef EOPNOTSUPP + else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } +#endif +#ifdef EPFNOSUPPORT + else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EAFNOSUPPORT + else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EADDRINUSE + else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } +#endif +#ifdef EADDRNOTAVAIL + else if (e == EADDRNOTAVAIL) { return MA_ERROR; } +#endif +#ifdef ENETDOWN + else if (e == ENETDOWN) { return MA_NO_NETWORK; } +#endif +#ifdef ENETUNREACH + else if (e == ENETUNREACH) { return MA_NO_NETWORK; } +#endif +#ifdef ENETRESET + else if (e == ENETRESET) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNABORTED + else if (e == ECONNABORTED) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNRESET + else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } +#endif +#ifdef ENOBUFS + else if (e == ENOBUFS) { return MA_NO_SPACE; } +#endif +#ifdef EISCONN + else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } +#endif +#ifdef ENOTCONN + else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } +#endif +#ifdef ESHUTDOWN + else if (e == ESHUTDOWN) { return MA_ERROR; } +#endif +#ifdef ETOOMANYREFS + else if (e == ETOOMANYREFS) { return MA_ERROR; } +#endif +#ifdef ETIMEDOUT + else if (e == ETIMEDOUT) { return MA_TIMEOUT; } +#endif +#ifdef ECONNREFUSED + else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } +#endif +#ifdef EHOSTDOWN + else if (e == EHOSTDOWN) { return MA_NO_HOST; } +#endif +#ifdef EHOSTUNREACH + else if (e == EHOSTUNREACH) { return MA_NO_HOST; } +#endif +#ifdef EALREADY + else if (e == EALREADY) { return MA_IN_PROGRESS; } +#endif +#ifdef EINPROGRESS + else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } +#endif +#ifdef ESTALE + else if (e == ESTALE) { return MA_INVALID_FILE; } +#endif +#ifdef EUCLEAN + else if (e == EUCLEAN) { return MA_ERROR; } +#endif +#ifdef ENOTNAM + else if (e == ENOTNAM) { return MA_ERROR; } +#endif +#ifdef ENAVAIL + else if (e == ENAVAIL) { return MA_ERROR; } +#endif +#ifdef EISNAM + else if (e == EISNAM) { return MA_ERROR; } +#endif +#ifdef EREMOTEIO + else if (e == EREMOTEIO) { return MA_IO_ERROR; } +#endif +#ifdef EDQUOT + else if (e == EDQUOT) { return MA_NO_SPACE; } +#endif +#ifdef ENOMEDIUM + else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EMEDIUMTYPE + else if (e == EMEDIUMTYPE) { return MA_ERROR; } +#endif +#ifdef ECANCELED + else if (e == ECANCELED) { return MA_CANCELLED; } +#endif +#ifdef ENOKEY + else if (e == ENOKEY) { return MA_ERROR; } +#endif +#ifdef EKEYEXPIRED + else if (e == EKEYEXPIRED) { return MA_ERROR; } +#endif +#ifdef EKEYREVOKED + else if (e == EKEYREVOKED) { return MA_ERROR; } +#endif +#ifdef EKEYREJECTED + else if (e == EKEYREJECTED) { return MA_ERROR; } +#endif +#ifdef EOWNERDEAD + else if (e == EOWNERDEAD) { return MA_ERROR; } +#endif +#ifdef ENOTRECOVERABLE + else if (e == ENOTRECOVERABLE) { return MA_ERROR; } +#endif +#ifdef ERFKILL + else if (e == ERFKILL) { return MA_ERROR; } +#endif +#ifdef EHWPOISON + else if (e == EHWPOISON) { return MA_ERROR; } +#endif + else { + return MA_ERROR; + } +} + +MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err; +#endif + + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + err = fopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); + } +#else +#if defined(_WIN32) || defined(__APPLE__) + *ppFile = fopen(pFilePath, pOpenMode); +#else + #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) + *ppFile = fopen64(pFilePath, pOpenMode); + #else + *ppFile = fopen(pFilePath, pOpenMode); + #endif +#endif + if (*ppFile == NULL) { + ma_result result = ma_result_from_errno(errno); + if (result == MA_SUCCESS) { + result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ + } + + return result; + } +#endif + + return MA_SUCCESS; +} + + + +/* +_wfopen() isn't always available in all compilation environments. + + * Windows only. + * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). + * MinGW-64 (both 32- and 64-bit) seems to support it. + * MinGW wraps it in !defined(__STRICT_ANSI__). + * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). + +This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() +fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. +*/ +#if defined(_WIN32) + #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) + #define MA_HAS_WFOPEN + #endif +#endif + +MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (ppFile != NULL) { + *ppFile = NULL; /* Safety. */ + } + + if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_HAS_WFOPEN) + { + /* Use _wfopen() on Windows. */ + #if defined(_MSC_VER) && _MSC_VER >= 1400 + errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); + if (err != 0) { + return ma_result_from_errno(err); + } + #else + *ppFile = _wfopen(pFilePath, pOpenMode); + if (*ppFile == NULL) { + return ma_result_from_errno(errno); + } + #endif + (void)pAllocationCallbacks; + } +#else + /* + Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can + think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for + maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. + */ + { + mbstate_t mbs; + size_t lenMB; + const wchar_t* pFilePathTemp = pFilePath; + char* pFilePathMB = NULL; + char pOpenModeMB[32] = {0}; + + /* Get the length first. */ + MA_ZERO_OBJECT(&mbs); + lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); + if (lenMB == (size_t)-1) { + return ma_result_from_errno(errno); + } + + pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); + if (pFilePathMB == NULL) { + return MA_OUT_OF_MEMORY; + } + + pFilePathTemp = pFilePath; + MA_ZERO_OBJECT(&mbs); + wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); + + /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ + { + size_t i = 0; + for (;;) { + if (pOpenMode[i] == 0) { + pOpenModeMB[i] = '\0'; + break; + } + + pOpenModeMB[i] = (char)pOpenMode[i]; + i += 1; + } + } + + *ppFile = fopen(pFilePathMB, pOpenModeMB); + + ma_free(pFilePathMB, pAllocationCallbacks); + } + + if (*ppFile == NULL) { + return MA_ERROR; + } +#endif + + return MA_SUCCESS; +} + + + +static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX + MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + ma_uint64 bytesToCopyNow = sizeInBytes; + if (bytesToCopyNow > MA_SIZE_MAX) { + bytesToCopyNow = MA_SIZE_MAX; + } + + MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ + + sizeInBytes -= bytesToCopyNow; + dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); + src = (const void*)((const ma_uint8*)src + bytesToCopyNow); + } +#endif +} + +static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) +{ +#if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX + MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); +#else + while (sizeInBytes > 0) { + ma_uint64 bytesToZeroNow = sizeInBytes; + if (bytesToZeroNow > MA_SIZE_MAX) { + bytesToZeroNow = MA_SIZE_MAX; + } + + MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ + + sizeInBytes -= bytesToZeroNow; + dst = (void*)((ma_uint8*)dst + bytesToZeroNow); + } +#endif +} + + +/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ +static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + + return x; +} + +static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) +{ + return ma_next_power_of_2(x) >> 1; +} + +static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) +{ + unsigned int prev = ma_prev_power_of_2(x); + unsigned int next = ma_next_power_of_2(x); + if ((next - x) > (x - prev)) { + return prev; + } else { + return next; + } +} + +static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) +{ + unsigned int count = 0; + while (x != 0) { + if (x & 1) { + count += 1; + } + + x = x >> 1; + } + + return count; +} + + + +/************************************************************************************************************************************************************** + +Allocation Callbacks + +**************************************************************************************************************************************************************/ +static void* ma__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_MALLOC(sz); +} + +static void* ma__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_REALLOC(p, sz); +} + +static void ma__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_FREE(p); +} + +static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) +{ + ma_allocation_callbacks callbacks; + callbacks.pUserData = NULL; + callbacks.onMalloc = ma__malloc_default; + callbacks.onRealloc = ma__realloc_default; + callbacks.onFree = ma__free_default; + + return callbacks; +} + +static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) +{ + if (pDst == NULL) { + return MA_INVALID_ARGS; + } + + if (pSrc == NULL) { + *pDst = ma_allocation_callbacks_init_default(); + } else { + if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { + *pDst = ma_allocation_callbacks_init_default(); + } else { + if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { + return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ + } else { + *pDst = *pSrc; + } + } + } + + return MA_SUCCESS; +} + + + + +/************************************************************************************************************************************************************** + +Logging + +**************************************************************************************************************************************************************/ +MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) +{ + switch (logLevel) + { + case MA_LOG_LEVEL_DEBUG: return "DEBUG"; + case MA_LOG_LEVEL_INFO: return "INFO"; + case MA_LOG_LEVEL_WARNING: return "WARNING"; + case MA_LOG_LEVEL_ERROR: return "ERROR"; + default: return "ERROR"; + } +} + +#if defined(MA_DEBUG_OUTPUT) +#if defined(MA_ANDROID) + #include +#endif + +/* Customize this to use a specific tag in __android_log_print() for debug output messages. */ +#ifndef MA_ANDROID_LOG_TAG +#define MA_ANDROID_LOG_TAG "miniaudio" +#endif + +void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) +{ + (void)pUserData; + + /* Special handling for some platforms. */ + #if defined(MA_ANDROID) + { + /* Android. */ + __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); + } + #else + { + /* Everything else. */ + printf("%s: %s", ma_log_level_to_string(level), pMessage); + } + #endif +} +#endif + +MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) +{ + ma_log_callback callback; + + MA_ZERO_OBJECT(&callback); + callback.onLog = onLog; + callback.pUserData = pUserData; + + return callback; +} + + +MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) +{ + if (pLog == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLog); + ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); + + /* We need a mutex for thread safety. */ + #ifndef MA_NO_THREADING + { + ma_result result = ma_mutex_init(&pLog->lock); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + + /* If we're using debug output, enable it. */ + #if defined(MA_DEBUG_OUTPUT) + { + ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ + } + #endif + + return MA_SUCCESS; +} + +MA_API void ma_log_uninit(ma_log* pLog) +{ + if (pLog == NULL) { + return; + } + +#ifndef MA_NO_THREADING + ma_mutex_uninit(&pLog->lock); +#endif +} + +static void ma_log_lock(ma_log* pLog) +{ +#ifndef MA_NO_THREADING + ma_mutex_lock(&pLog->lock); +#else + (void)pLog; +#endif +} + +static void ma_log_unlock(ma_log* pLog) +{ +#ifndef MA_NO_THREADING + ma_mutex_unlock(&pLog->lock); +#else + (void)pLog; +#endif +} + +MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) +{ + ma_result result = MA_SUCCESS; + + if (pLog == NULL || callback.onLog == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + if (pLog->callbackCount == ma_countof(pLog->callbacks)) { + result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ + } else { + pLog->callbacks[pLog->callbackCount] = callback; + pLog->callbackCount += 1; + } + } + ma_log_unlock(pLog); + + return result; +} + +MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) +{ + if (pLog == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + ma_uint32 iLog; + for (iLog = 0; iLog < pLog->callbackCount; ) { + if (pLog->callbacks[iLog].onLog == callback.onLog) { + /* Found. Move everything down a slot. */ + ma_uint32 jLog; + for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { + pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; + } + + pLog->callbackCount -= 1; + } else { + /* Not found. */ + iLog += 1; + } + } + } + ma_log_unlock(pLog); + + return MA_SUCCESS; +} + +MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) +{ + if (pLog == NULL || pMessage == NULL) { + return MA_INVALID_ARGS; + } + + ma_log_lock(pLog); + { + ma_uint32 iLog; + for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { + if (pLog->callbacks[iLog].onLog) { + pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); + } + } + } + ma_log_unlock(pLog); + + return MA_SUCCESS; +} + + +/* +We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a +logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). +*/ +#if defined(_MSC_VER) && _MSC_VER < 1900 +static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) +{ +#if _MSC_VER > 1200 + return _vscprintf(format, args); +#else + int result; + char* pTempBuffer = NULL; + size_t tempBufferCap = 1024; + + if (format == NULL) { + errno = EINVAL; + return -1; + } + + for (;;) { + char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); + if (pNewTempBuffer == NULL) { + ma_free(pTempBuffer, pAllocationCallbacks); + errno = ENOMEM; + return -1; /* Out of memory. */ + } + + pTempBuffer = pNewTempBuffer; + + result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); + ma_free(pTempBuffer, NULL); + + if (result != -1) { + break; /* Got it. */ + } + + /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ + tempBufferCap *= 2; + } + + return result; +#endif +} +#endif + +MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) +{ + if (pLog == NULL || pFormat == NULL) { + return MA_INVALID_ARGS; + } + + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) + { + ma_result result; + int length; + char pFormattedMessageStack[1024]; + char* pFormattedMessageHeap = NULL; + + /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ + length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); + if (length < 0) { + return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ + } + + if ((size_t)length < sizeof(pFormattedMessageStack)) { + /* The string was written to the stack. */ + result = ma_log_post(pLog, level, pFormattedMessageStack); + } else { + /* The stack buffer was too small, try the heap. */ + pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); + if (pFormattedMessageHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + + length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); + if (length < 0) { + ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); + return MA_INVALID_OPERATION; + } + + result = ma_log_post(pLog, level, pFormattedMessageHeap); + ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); + } + + return result; + } + #else + { + /* + Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll + need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing + a fixed sized stack allocated buffer. + */ + #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ + { + ma_result result; + int formattedLen; + char* pFormattedMessage = NULL; + va_list args2; + + #if _MSC_VER >= 1800 + { + va_copy(args2, args); + } + #else + { + args2 = args; + } + #endif + + formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); + va_end(args2); + + if (formattedLen <= 0) { + return MA_INVALID_OPERATION; + } + + pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); + if (pFormattedMessage == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ + #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ + { + vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); + } + #else + { + vsprintf(pFormattedMessage, pFormat, args); + } + #endif + + result = ma_log_post(pLog, level, pFormattedMessage); + ma_free(pFormattedMessage, &pLog->allocationCallbacks); + + return result; + } + #else + { + /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ + (void)level; + (void)args; + + return MA_INVALID_OPERATION; + } + #endif + } + #endif +} + +MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) +{ + ma_result result; + va_list args; + + if (pLog == NULL || pFormat == NULL) { + return MA_INVALID_ARGS; + } + + va_start(args, pFormat); + { + result = ma_log_postv(pLog, level, pFormat, args); + } + va_end(args); + + return result; +} + + + +static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) +{ + return (ma_uint8)(ma_clamp(x, -128, 127) + 128); +} + +static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) +{ + return (ma_int16)ma_clamp(x, -32768, 32767); +} + +static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) +{ + return (ma_int64)ma_clamp(x, -8388608, 8388607); +} + +static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) +{ + /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ + ma_int64 clipMin; + ma_int64 clipMax; + clipMin = -((ma_int64)2147483647 + 1); + clipMax = (ma_int64)2147483647; + + return (ma_int32)ma_clamp(x, clipMin, clipMax); +} + +static MA_INLINE float ma_clip_f32(float x) +{ + if (x < -1) return -1; + if (x > +1) return +1; + return x; +} + + +static MA_INLINE float ma_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; + /*return x + (y - x)*a;*/ +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) +{ + return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); +} +#endif +#if defined(MA_SUPPORT_AVX2) +static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) +{ + return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) +{ + return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); +} +#endif + + +static MA_INLINE double ma_mix_f64(double x, double y, double a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) +{ + return x + (y - x)*a; +} + +static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) +{ + return lo + x*(hi-lo); +} + + +/* +Greatest common factor using Euclid's algorithm iteratively. +*/ +static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + ma_uint32 t = a; + a = b; + b = t % a; + } + } + + return a; +} + + +static ma_uint32 ma_ffs_32(ma_uint32 x) +{ + ma_uint32 i; + + /* Just a naive implementation just to get things working for now. Will optimize this later. */ + for (i = 0; i < 32; i += 1) { + if ((x & (1 << i)) != 0) { + return i; + } + } + + return i; +} + +static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) +{ + return (ma_int16)(x * (1 << 8)); +} + + + +/* +Random Number Generation + +miniaudio uses the LCG random number generation algorithm. This is good enough for audio. + +Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across +multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for +miniaudio's purposes. +*/ +#ifndef MA_DEFAULT_LCG_SEED +#define MA_DEFAULT_LCG_SEED 4321 +#endif + +#define MA_LCG_M 2147483647 +#define MA_LCG_A 48271 +#define MA_LCG_C 0 + +static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ + +static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) +{ + MA_ASSERT(pLCG != NULL); + pLCG->state = seed; +} + +static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) +{ + pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; + return pLCG->state; +} + +static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) +{ + return (ma_uint32)ma_lcg_rand_s32(pLCG); +} + +static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) +{ + return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); +} + +static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) +{ + return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; +} + +static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) +{ + return (float)ma_lcg_rand_f64(pLCG); +} + +static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) +{ + return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); +} + +static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) +{ + if (lo == hi) { + return lo; + } + + return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); +} + + + +static MA_INLINE void ma_seed(ma_int32 seed) +{ + ma_lcg_seed(&g_maLCG, seed); +} + +static MA_INLINE ma_int32 ma_rand_s32(void) +{ + return ma_lcg_rand_s32(&g_maLCG); +} + +static MA_INLINE ma_uint32 ma_rand_u32(void) +{ + return ma_lcg_rand_u32(&g_maLCG); +} + +static MA_INLINE double ma_rand_f64(void) +{ + return ma_lcg_rand_f64(&g_maLCG); +} + +static MA_INLINE float ma_rand_f32(void) +{ + return ma_lcg_rand_f32(&g_maLCG); +} + +static MA_INLINE float ma_rand_range_f32(float lo, float hi) +{ + return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); +} + +static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) +{ + return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); +} + + +static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) +{ + return ma_rand_range_f32(ditherMin, ditherMax); +} + +static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) +{ + float a = ma_rand_range_f32(ditherMin, 0); + float b = ma_rand_range_f32(0, ditherMax); + return a + b; +} + +static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) +{ + if (ditherMode == ma_dither_mode_rectangle) { + return ma_dither_f32_rectangle(ditherMin, ditherMax); + } + if (ditherMode == ma_dither_mode_triangle) { + return ma_dither_f32_triangle(ditherMin, ditherMax); + } + + return 0; +} + +static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) +{ + if (ditherMode == ma_dither_mode_rectangle) { + ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); + return a; + } + if (ditherMode == ma_dither_mode_triangle) { + ma_int32 a = ma_rand_range_s32(ditherMin, 0); + ma_int32 b = ma_rand_range_s32(0, ditherMax); + return a + b; + } + + return 0; +} + + +/************************************************************************************************************************************************************** + +Atomics + +**************************************************************************************************************************************************************/ +/* ma_atomic.h begin */ +#ifndef ma_atomic_h +#if defined(__cplusplus) +extern "C" { +#endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif +#endif +typedef int ma_atomic_memory_order; +#define MA_ATOMIC_HAS_8 +#define MA_ATOMIC_HAS_16 +#define MA_ATOMIC_HAS_32 +#define MA_ATOMIC_HAS_64 +#if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) + #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ + switch (order) \ + { \ + case ma_atomic_memory_order_relaxed: \ + { \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ + { \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_release: \ + { \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + } break; \ + } \ + return result; + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ + switch (order) \ + { \ + case ma_atomic_memory_order_relaxed: \ + { \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ + { \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_release: \ + { \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ + default: \ + { \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + } break; \ + } \ + return result; + #define ma_atomic_memory_order_relaxed 0 + #define ma_atomic_memory_order_consume 1 + #define ma_atomic_memory_order_acquire 2 + #define ma_atomic_memory_order_release 3 + #define ma_atomic_memory_order_acq_rel 4 + #define ma_atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(MA_X86) + #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY + #endif + #if _MSC_VER < 1600 + #undef MA_ATOMIC_HAS_8 + #undef MA_ATOMIC_HAS_16 + #endif + #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #include + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result = 0; + __asm { + mov ecx, dst + mov al, expected + mov dl, desired + lock cmpxchg [ecx], dl + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result = 0; + __asm { + mov ecx, dst + mov ax, expected + mov dx, desired + lock cmpxchg [ecx], dx + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result = 0; + __asm { + mov ecx, dst + mov eax, expected + mov edx, desired + lock cmpxchg [ecx], edx + mov result, eax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; + __asm { + mov esi, dst + mov eax, dword ptr expected + mov edx, dword ptr expected + 4 + mov ebx, dword ptr desired + mov ecx, dword ptr desired + 4 + lock cmpxchg8b qword ptr [esi] + mov resultEAX, eax + mov resultEDX, edx + } + return ((ma_uint64)resultEDX << 32) | resultEAX; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) + #endif + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + __asm { + mov ecx, dst + mov al, src + lock xchg [ecx], al + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + __asm { + mov ecx, dst + mov ax, src + lock xchg [ecx], ax + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, src + lock xchg [ecx], eax + mov result, eax + } + return result; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); + #else + (void)order; + return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); + #else + (void)order; + return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); + #else + (void)order; + return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); + #else + (void)order; + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + #endif + } + #else + #endif + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + __asm { + mov ecx, dst + mov al, src + lock xadd [ecx], al + mov result, al + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + __asm { + mov ecx, dst + mov ax, src + lock xadd [ecx], ax + mov result, ax + } + return result; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result = 0; + (void)order; + __asm { + mov ecx, dst + mov eax, src + lock xadd [ecx], eax + mov result, eax + } + return result; + } + #endif + #else + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); + #else + (void)order; + return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); + #else + (void)order; + return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); + #else + (void)order; + return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); + #else + (void)order; + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + #endif + } + #else + #endif + #endif + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) + { + (void)order; + __asm { + lock add [esp], 0 + } + } + #else + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #else + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) + { + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); + } + #endif + #endif + #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); + #else + (void)order; + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); + #else + (void)order; + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); + #else + (void)order; + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); + #else + (void)order; + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); + #else + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); + #else + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); + #else + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); + #else + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + #endif + } + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + #endif + #if defined(MA_ATOMIC_HAS_8) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) + #else + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) + #endif +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) + #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) + #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) + #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) + #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) + #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) +#else + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #if defined(__GNUC__) + #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + if (order > ma_atomic_memory_order_acquire) { + __sync_synchronize(); + } + return __sync_lock_test_and_set(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + do { + oldValue = *dst; + } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_add(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_sub(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_or(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_xor(dst, src); + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + (void)order; + return __sync_fetch_and_and(dst, src); + } + #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #else + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #else + #error Unsupported architecture. Please submit a feature request. + #endif + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) + { + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) + { + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) + { + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) + { + volatile ma_uint64 result; + #if defined(MA_X86) + ma_uint32 resultEAX; + ma_uint32 resultEDX; + __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); + result = ((ma_uint64)resultEDX << 32) | resultEAX; + #elif defined(MA_X64) + __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result = 0; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 result; + (void)order; + #if defined(MA_X86) + do { + result = *dst; + } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(MA_X64) + __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 result; + (void)order; + #if defined(MA_X86) || defined(MA_X64) + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + #else + #error Unsupported architecture. Please submit a feature request. + #endif + return result; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + #if defined(MA_X86) + ma_uint64 oldValue; + ma_uint64 newValue; + (void)order; + do { + oldValue = *dst; + newValue = oldValue + src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + return oldValue; + #elif defined(MA_X64) + ma_uint64 result; + (void)order; + __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); + return result; + #endif + } + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue - src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue & src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue ^ src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) + { + ma_uint8 oldValue; + ma_uint8 newValue; + do { + oldValue = *dst; + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) + { + ma_uint16 oldValue; + ma_uint16 newValue; + do { + oldValue = *dst; + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) + { + ma_uint32 oldValue; + ma_uint32 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) + { + ma_uint64 oldValue; + ma_uint64 newValue; + do { + oldValue = *dst; + newValue = oldValue | src; + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + (void)order; + return oldValue; + } + #endif + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); + } + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); + } + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); + } + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) + { + (void)order; + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); + } + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) +#endif +#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint8 expectedValue; + ma_uint8 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_8(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint16 expectedValue; + ma_uint16 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_16(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint32 expectedValue; + ma_uint32 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_32(expected, result, failureOrder); + return 0; + } + } + #endif + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + ma_uint64 expectedValue; + ma_uint64 result; + (void)successOrder; + (void)failureOrder; + expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); + if (result == expectedValue) { + return 1; + } else { + ma_atomic_store_explicit_64(expected, result, failureOrder); + return 0; + } + } + #endif + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) +#endif +#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) + { + (void)ptr; + return 1; + } + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) + { + (void)ptr; + #if defined(MA_64BIT) + return 1; + #else + #if defined(MA_X86) || defined(MA_X64) + return 1; + #else + return 0; + #endif + #endif + } +#endif +#if defined(MA_64BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) + { + return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); + } + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + { + return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); + } + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + } + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); + } + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); + } +#elif defined(MA_32BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) + { + return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); + } + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) + { + return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); + } + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + } + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) + { + return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + } + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) + { + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); + } + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + { + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); + } +#else + #error Unsupported architecture. +#endif +#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) +#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) +#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) +#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) +typedef union +{ + ma_uint32 i; + float f; +} ma_atomic_if32; +typedef union +{ + ma_uint64 i; + double f; +} ma_atomic_if64; +#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 x; + x.f = src; + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); +} +static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 x; + x.f = src; + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); +} +static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); + return r.f; +} +static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); + return r.f; +} +static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if32 d; + d.f = desired; + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if64 d; + d.f = desired; + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if32 d; + d.f = desired; + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) +{ + ma_atomic_if64 d; + d.f = desired; + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); +} +static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) +{ + ma_atomic_if32 r; + ma_atomic_if32 x; + x.f = src; + r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); + return r.f; +} +static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) +{ + ma_atomic_if64 r; + ma_atomic_if64 x; + x.f = src; + r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); + return r.f; +} +#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +{ + ma_atomic_if32 r; + ma_atomic_if32 e, d; + e.f = expected; + d.f = desired; + r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); + return r.f; +} +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +{ + ma_atomic_if64 r; + ma_atomic_if64 e, d; + e.f = expected; + d.f = desired; + r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); + return r.f; +} +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) +{ + for (;;) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { + break; + } + while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + } + } +} +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) +{ + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); +} +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#if defined(__cplusplus) +} +#endif +#endif +/* ma_atomic.h end */ + +#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ + static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ + { \ + return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ + } \ + static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ + { \ + ma_atomic_store_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ + { \ + return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ + { \ + return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ + { \ + return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + } \ + static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ + { \ + return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + } \ + +#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ + { \ + return ma_atomic_load_ptr((void**)&x->value); \ + } \ + static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + ma_atomic_store_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ + { \ + return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ + } \ + static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ + { \ + return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ + { \ + return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + } \ + +MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) +MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) +MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) +MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) +MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif + + +MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) +{ + /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ + ma_uint64 outputFrameCount; + ma_uint64 preliminaryInputFrameCountFromFrac; + ma_uint64 preliminaryInputFrameCount; + + if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { + return 0; + } + + if (sampleRateOut == sampleRateIn) { + return frameCountIn; + } + + outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; + + preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; + preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; + + if (preliminaryInputFrameCount <= frameCountIn) { + outputFrameCount += 1; + } + + return outputFrameCount; +} + +#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE +#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 +#endif + + + +#if defined(MA_WIN32) +static ma_result ma_result_from_GetLastError(DWORD error) +{ + switch (error) + { + case ERROR_SUCCESS: return MA_SUCCESS; + case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; + case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; + case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; + case ERROR_DISK_FULL: return MA_NO_SPACE; + case ERROR_HANDLE_EOF: return MA_AT_END; + case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; + case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; + case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; + case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; + case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; + default: break; + } + + return MA_ERROR; +} +#endif /* MA_WIN32 */ + + +/******************************************************************************* + +Threading + +*******************************************************************************/ +static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) +{ + if (pSpinlock == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { + break; + } + + while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { + if (yield) { + ma_yield(); + } + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) +{ + return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); +} + +MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) +{ + return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); +} + +MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) +{ + if (pSpinlock == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); + return MA_SUCCESS; +} + + +#ifndef MA_NO_THREADING +#if defined(MA_POSIX) + #define MA_THREADCALL + typedef void* ma_thread_result; +#elif defined(MA_WIN32) + #define MA_THREADCALL WINAPI + typedef unsigned long ma_thread_result; +#endif + +typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); + +#ifdef MA_POSIX +static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + int result; + pthread_attr_t* pAttr = NULL; + +#if !defined(__EMSCRIPTEN__) + /* Try setting the thread priority. It's not critical if anything fails here. */ + pthread_attr_t attr; + if (pthread_attr_init(&attr) == 0) { + int scheduler = -1; + + /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ + pAttr = &attr; + + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif + } + } + #endif + + if (stackSize > 0) { + pthread_attr_setstacksize(&attr, stackSize); + } + + if (scheduler != -1) { + int priorityMin = sched_get_priority_min(scheduler); + int priorityMax = sched_get_priority_max(scheduler); + int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ + + struct sched_param sched; + if (pthread_attr_getschedparam(&attr, &sched) == 0) { + if (priority == ma_thread_priority_idle) { + sched.sched_priority = priorityMin; + } else if (priority == ma_thread_priority_realtime) { + sched.sched_priority = priorityMax; + } else { + sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ + if (sched.sched_priority < priorityMin) { + sched.sched_priority = priorityMin; + } + if (sched.sched_priority > priorityMax) { + sched.sched_priority = priorityMax; + } + } + + /* I'm not treating a failure of setting the priority as a critical error so not checking the return value here. */ + pthread_attr_setschedparam(&attr, &sched); + } + } + } +#else + /* It's the emscripten build. We'll have a few unused parameters. */ + (void)priority; + (void)stackSize; +#endif + + result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); + + /* The thread attributes object is no longer required. */ + if (pAttr != NULL) { + pthread_attr_destroy(pAttr); + } + + if (result != 0) { + return ma_result_from_errno(result); + } + + return MA_SUCCESS; +} + +static void ma_thread_wait__posix(ma_thread* pThread) +{ + pthread_join((pthread_t)*pThread, NULL); +} + + +static ma_result ma_mutex_init__posix(ma_mutex* pMutex) +{ + int result; + + if (pMutex == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMutex); + + result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + if (result != 0) { + return ma_result_from_errno(result); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__posix(ma_mutex* pMutex) +{ + pthread_mutex_destroy((pthread_mutex_t*)pMutex); +} + +static void ma_mutex_lock__posix(ma_mutex* pMutex) +{ + pthread_mutex_lock((pthread_mutex_t*)pMutex); +} + +static void ma_mutex_unlock__posix(ma_mutex* pMutex) +{ + pthread_mutex_unlock((pthread_mutex_t*)pMutex); +} + + +static ma_result ma_event_init__posix(ma_event* pEvent) +{ + int result; + + result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); + if (result != 0) { + return ma_result_from_errno(result); + } + + result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); + if (result != 0) { + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); + return ma_result_from_errno(result); + } + + pEvent->value = 0; + return MA_SUCCESS; +} + +static void ma_event_uninit__posix(ma_event* pEvent) +{ + pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); +} + +static ma_result ma_event_wait__posix(ma_event* pEvent) +{ + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); + { + while (pEvent->value == 0) { + pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); + } + pEvent->value = 0; /* Auto-reset. */ + } + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); + + return MA_SUCCESS; +} + +static ma_result ma_event_signal__posix(ma_event* pEvent) +{ + pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); + { + pEvent->value = 1; + pthread_cond_signal((pthread_cond_t*)&pEvent->cond); + } + pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) +{ + int result; + + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pSemaphore->value = initialValue; + + result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); + if (result != 0) { + return ma_result_from_errno(result); /* Failed to create mutex. */ + } + + result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); + if (result != 0) { + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); + return ma_result_from_errno(result); /* Failed to create condition variable. */ + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return; + } + + pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); + pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); +} + +static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); + { + /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ + while (pSemaphore->value == 0) { + pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); + } + + pSemaphore->value -= 1; + } + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); + + return MA_SUCCESS; +} + +static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + return MA_INVALID_ARGS; + } + + pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); + { + pSemaphore->value += 1; + pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); + } + pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); + + return MA_SUCCESS; +} +#elif defined(MA_WIN32) +static int ma_thread_priority_to_win32(ma_thread_priority priority) +{ + switch (priority) { + case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; + case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; + case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; + case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; + case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; + case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; + case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; + default: return THREAD_PRIORITY_NORMAL; + } +} + +static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ + + *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); + if (*pThread == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); + + return MA_SUCCESS; +} + +static void ma_thread_wait__win32(ma_thread* pThread) +{ + WaitForSingleObject((HANDLE)*pThread, INFINITE); + CloseHandle((HANDLE)*pThread); +} + + +static ma_result ma_mutex_init__win32(ma_mutex* pMutex) +{ + *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (*pMutex == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__win32(ma_mutex* pMutex) +{ + CloseHandle((HANDLE)*pMutex); +} + +static void ma_mutex_lock__win32(ma_mutex* pMutex) +{ + WaitForSingleObject((HANDLE)*pMutex, INFINITE); +} + +static void ma_mutex_unlock__win32(ma_mutex* pMutex) +{ + SetEvent((HANDLE)*pMutex); +} + + +static ma_result ma_event_init__win32(ma_event* pEvent) +{ + *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (*pEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_event_uninit__win32(ma_event* pEvent) +{ + CloseHandle((HANDLE)*pEvent); +} + +static ma_result ma_event_wait__win32(ma_event* pEvent) +{ + DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_event_signal__win32(ma_event* pEvent) +{ + BOOL result = SetEvent((HANDLE)*pEvent); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) +{ + *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); + if (*pSemaphore == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) +{ + CloseHandle((HANDLE)*pSemaphore); +} + +static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) +{ + DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) +{ + BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} +#endif + +typedef struct +{ + ma_thread_entry_proc entryProc; + void* pData; + ma_allocation_callbacks allocationCallbacks; +} ma_thread_proxy_data; + +static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) +{ + ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; + ma_thread_entry_proc entryProc; + void* pEntryProcData; + ma_thread_result result; + + #if defined(MA_ON_THREAD_ENTRY) + MA_ON_THREAD_ENTRY + #endif + + entryProc = pProxyData->entryProc; + pEntryProcData = pProxyData->pData; + + /* Free the proxy data before getting into the real thread entry proc. */ + ma_free(pProxyData, &pProxyData->allocationCallbacks); + + result = entryProc(pEntryProcData); + + #if defined(MA_ON_THREAD_EXIT) + MA_ON_THREAD_EXIT + #endif + + return result; +} + +static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_thread_proxy_data* pProxyData; + + if (pThread == NULL || entryProc == NULL) { + return MA_INVALID_ARGS; + } + + pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ + if (pProxyData == NULL) { + return MA_OUT_OF_MEMORY; + } + +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + + pProxyData->entryProc = entryProc; + pProxyData->pData = pData; + ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); + +#if defined(MA_POSIX) + result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#elif defined(MA_WIN32) + result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#endif + + if (result != MA_SUCCESS) { + ma_free(pProxyData, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; +} + +static void ma_thread_wait(ma_thread* pThread) +{ + if (pThread == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_thread_wait__posix(pThread); +#elif defined(MA_WIN32) + ma_thread_wait__win32(pThread); +#endif +} + + +MA_API ma_result ma_mutex_init(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_mutex_init__posix(pMutex); +#elif defined(MA_WIN32) + return ma_mutex_init__win32(pMutex); +#endif +} + +MA_API void ma_mutex_uninit(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_mutex_uninit__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_uninit__win32(pMutex); +#endif +} + +MA_API void ma_mutex_lock(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_mutex_lock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_lock__win32(pMutex); +#endif +} + +MA_API void ma_mutex_unlock(ma_mutex* pMutex) +{ + if (pMutex == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_mutex_unlock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_unlock__win32(pMutex); +#endif +} + + +MA_API ma_result ma_event_init(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_init__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_init__win32(pEvent); +#endif +} + +#if 0 +static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_event* pEvent; + + if (ppEvent == NULL) { + return MA_INVALID_ARGS; + } + + *ppEvent = NULL; + + pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); + if (pEvent == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_event_init(pEvent); + if (result != MA_SUCCESS) { + ma_free(pEvent, pAllocationCallbacks); + return result; + } + + *ppEvent = pEvent; + return result; +} +#endif + +MA_API void ma_event_uninit(ma_event* pEvent) +{ + if (pEvent == NULL) { + return; + } + +#if defined(MA_POSIX) + ma_event_uninit__posix(pEvent); +#elif defined(MA_WIN32) + ma_event_uninit__win32(pEvent); +#endif +} + +#if 0 +static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pEvent == NULL) { + return; + } + + ma_event_uninit(pEvent); + ma_free(pEvent, pAllocationCallbacks); +} +#endif + +MA_API ma_result ma_event_wait(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_wait__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_wait__win32(pEvent); +#endif +} + +MA_API ma_result ma_event_signal(ma_event* pEvent) +{ + if (pEvent == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_event_signal__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_signal__win32(pEvent); +#endif +} + + +MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_init__posix(initialValue, pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_init__win32(initialValue, pSemaphore); +#endif +} + +MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return; + } + +#if defined(MA_POSIX) + ma_semaphore_uninit__posix(pSemaphore); +#elif defined(MA_WIN32) + ma_semaphore_uninit__win32(pSemaphore); +#endif +} + +MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_wait__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_wait__win32(pSemaphore); +#endif +} + +MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) +{ + if (pSemaphore == NULL) { + MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ + return MA_INVALID_ARGS; + } + +#if defined(MA_POSIX) + return ma_semaphore_release__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_release__win32(pSemaphore); +#endif +} +#else +/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ +#ifndef MA_NO_DEVICE_IO +#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; +#endif +#endif /* MA_NO_THREADING */ + + + +#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF + +MA_API ma_result ma_fence_init(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFence); + pFence->counter = 0; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + + return MA_SUCCESS; +} + +MA_API void ma_fence_uninit(ma_fence* pFence) +{ + if (pFence == NULL) { + return; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pFence->e); + } + #endif + + MA_ZERO_OBJECT(pFence); +} + +MA_API ma_result ma_fence_acquire(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter + 1; + + /* Make sure we're not about to exceed our maximum value. */ + if (newCounter > MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; + } + + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + return MA_SUCCESS; + } else { + if (oldCounter == MA_FENCE_COUNTER_MAX) { + MA_ASSERT(MA_FALSE); + return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_release(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); + ma_uint32 newCounter = oldCounter - 1; + + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ + } + + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + #ifndef MA_NO_THREADING + { + if (newCounter == 0) { + ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ + } + } + #endif + + return MA_SUCCESS; + } else { + if (oldCounter == 0) { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ + } + } + } + + /* Should never get here. */ + /*return MA_SUCCESS;*/ +} + +MA_API ma_result ma_fence_wait(ma_fence* pFence) +{ + if (pFence == NULL) { + return MA_INVALID_ARGS; + } + + for (;;) { + ma_uint32 counter; + + counter = ma_atomic_load_32(&pFence->counter); + if (counter == 0) { + /* + Counter has hit zero. By the time we get here some other thread may have acquired the + fence again, but that is where the caller needs to take care with how they se the fence. + */ + return MA_SUCCESS; + } + + /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_wait(&pFence->e); + if (result != MA_SUCCESS) { + return result; + } + } + #endif + } + + /* Should never get here. */ + /*return MA_INVALID_OPERATION;*/ +} + + +MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) +{ + ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; + + if (pNotification == NULL) { + return MA_INVALID_ARGS; + } + + if (pNotificationCallbacks->onSignal == NULL) { + return MA_NOT_IMPLEMENTED; + } + + pNotificationCallbacks->onSignal(pNotification); + return MA_INVALID_ARGS; +} + + +static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) +{ + ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; +} + +MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; + pNotificationPoll->signalled = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) +{ + if (pNotificationPoll == NULL) { + return MA_FALSE; + } + + return pNotificationPoll->signalled; +} + + +static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) +{ + ma_async_notification_event_signal((ma_async_notification_event*)pNotification); +} + +MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; + + #ifndef MA_NO_THREADING + { + ma_result result; + + result = ma_event_init(&pNotificationEvent->e); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + ma_event_uninit(&pNotificationEvent->e); + return MA_SUCCESS; + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_wait(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + +MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) +{ + if (pNotificationEvent == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + return ma_event_signal(&pNotificationEvent->e); + } + #else + { + return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ + } + #endif +} + + + +/************************************************************************************************************************************************************ + +Job Queue + +************************************************************************************************************************************************************/ +MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) +{ + ma_slot_allocator_config config; + + MA_ZERO_OBJECT(&config); + config.capacity = capacity; + + return config; +} + + +static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) +{ + ma_uint32 cap = slotCapacity / 32; + if ((slotCapacity % 32) != 0) { + cap += 1; + } + + return cap; +} + +static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) +{ + return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); +} + + +typedef struct +{ + size_t sizeInBytes; + size_t groupsOffset; + size_t slotsOffset; +} ma_slot_allocator_heap_layout; + +static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Groups. */ + pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); + + /* Slots. */ + pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_slot_allocator_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_slot_allocator_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) +{ + ma_result result; + ma_slot_allocator_heap_layout heapLayout; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pAllocator); + + if (pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pAllocator->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); + pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); + pAllocator->capacity = pConfig->capacity; + + return MA_SUCCESS; +} + +MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pAllocator->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocator == NULL) { + return; + } + + if (pAllocator->_ownsHeap) { + ma_free(pAllocator->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) +{ + ma_uint32 iAttempt; + const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ + + if (pAllocator == NULL || pSlot == NULL) { + return MA_INVALID_ARGS; + } + + for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { + /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ + ma_uint32 iGroup; + for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { + /* CAS */ + for (;;) { + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + ma_uint32 bitOffset; + + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + + /* Fast check to see if anything is available. */ + if (oldBitfield == 0xFFFFFFFF) { + break; /* No available bits in this bitfield. */ + } + + bitOffset = ma_ffs_32(~oldBitfield); + MA_ASSERT(bitOffset < 32); + + newBitfield = oldBitfield | (1 << bitOffset); + + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_uint32 slotIndex; + + /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ + ma_atomic_fetch_add_32(&pAllocator->count, 1); + + /* The slot index is required for constructing the output value. */ + slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ + if (slotIndex >= pAllocator->capacity) { + return MA_OUT_OF_MEMORY; + } + + /* Increment the reference count before constructing the output value. */ + pAllocator->pSlots[slotIndex] += 1; + + /* Construct the output value. */ + *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); + + return MA_SUCCESS; + } + } + } + + /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ + if (pAllocator->count < pAllocator->capacity) { + ma_yield(); + } else { + return MA_OUT_OF_MEMORY; + } + } + + /* We couldn't find a slot within the maximum number of attempts. */ + return MA_OUT_OF_MEMORY; +} + +MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) +{ + ma_uint32 iGroup; + ma_uint32 iBit; + + if (pAllocator == NULL) { + return MA_INVALID_ARGS; + } + + iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ + iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ + + if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ + + while (ma_atomic_load_32(&pAllocator->count) > 0) { + /* CAS */ + ma_uint32 oldBitfield; + ma_uint32 newBitfield; + + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + newBitfield = oldBitfield & ~(1 << iBit); + + /* Debugging for checking for double-frees. */ + #if defined(MA_DEBUG_OUTPUT) + { + if ((oldBitfield & (1 << iBit)) == 0) { + MA_ASSERT(MA_FALSE); /* Double free detected.*/ + } + } + #endif + + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_atomic_fetch_sub_32(&pAllocator->count, 1); + return MA_SUCCESS; + } + } + + /* Getting here means there are no allocations available for freeing. */ + return MA_INVALID_OPERATION; +} + + +#define MA_JOB_ID_NONE ~((ma_uint64)0) +#define MA_JOB_SLOT_NONE (ma_uint16)(~0) + +static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) +{ + return (ma_uint32)(toc >> 32); +} + +static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) +{ + return (ma_uint16)(toc & 0x0000FFFF); +} + +static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) +{ + return (ma_uint16)((toc & 0xFFFF0000) >> 16); +} + +static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) +{ + return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); +} + +static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) +{ + /* Clear the reference count first. */ + toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); + toc = toc | ((ma_uint64)refcount << 32); + + return toc; +} + + +MA_API ma_job ma_job_init(ma_uint16 code) +{ + ma_job job; + + MA_ZERO_OBJECT(&job); + job.toc.breakup.code = code; + job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ + job.next = MA_JOB_ID_NONE; + + return job; +} + + +static ma_result ma_job_process__noop(ma_job* pJob); +static ma_result ma_job_process__quit(ma_job* pJob); +static ma_result ma_job_process__custom(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); + +#if !defined(MA_NO_DEVICE_IO) +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); +#endif + +static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = +{ + /* Miscellaneous. */ + ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ + ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ + + /* Resource Manager. */ + ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ + ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ + ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ + ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ + ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ + ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ + ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ + + /* Device. */ +#if !defined(MA_NO_DEVICE_IO) + ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ +#endif +}; + +MA_API ma_result ma_job_process(ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) { + return MA_INVALID_OPERATION; + } + + return g_jobVTable[pJob->toc.breakup.code](pJob); +} + +static ma_result ma_job_process__noop(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op. */ + (void)pJob; + + return MA_SUCCESS; +} + +static ma_result ma_job_process__quit(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} + +static ma_result ma_job_process__custom(ma_job* pJob) +{ + MA_ASSERT(pJob != NULL); + + /* No-op if there's no callback. */ + if (pJob->data.custom.proc == NULL) { + return MA_SUCCESS; + } + + return pJob->data.custom.proc(pJob); +} + + + +MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) +{ + ma_job_queue_config config; + + config.flags = flags; + config.capacity = capacity; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t allocatorOffset; + size_t jobsOffset; +} ma_job_queue_heap_layout; + +static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->capacity == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Allocator. */ + { + ma_slot_allocator_config allocatorConfig; + size_t allocatorHeapSizeInBytes; + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; + } + + /* Jobs. */ + pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_job_queue_heap_layout layout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_job_queue_get_heap_layout(pConfig, &layout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = layout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) +{ + ma_result result; + ma_job_queue_heap_layout heapLayout; + ma_slot_allocator_config allocatorConfig; + + if (pQueue == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pQueue); + + result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pQueue->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pQueue->flags = pConfig->flags; + pQueue->capacity = pConfig->capacity; + pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); + + allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); + result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); + if (result != MA_SUCCESS) { + return result; + } + + /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_init(0, &pQueue->sem); + } + #else + { + /* Threading is disabled and we've requested non-blocking mode. */ + return MA_INVALID_OPERATION; + } + #endif + } + + /* + Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is + just a dummy item for giving us the first item in the list which is stored in the "next" member. + */ + ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ + pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; + pQueue->tail = pQueue->head; + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pQueue->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pQueue == NULL) { + return; + } + + /* All we need to do is uninitialize the semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_uninit(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); + + if (pQueue->_ownsHeap) { + ma_free(pQueue->_pHeap, pAllocationCallbacks); + } +} + +static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) +{ + /* The new counter is taken from the expected value. */ + return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; +} + +MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) +{ + /* + Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors + */ + ma_result result; + ma_uint64 slot; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* We need a new slot. */ + result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); + if (result != MA_SUCCESS) { + return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ + } + + /* At this point we should have a slot to place the job. */ + MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); + + /* We need to put the job into memory before we do anything. */ + pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; + pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ + pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ + pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ + for (;;) { + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { + if (ma_job_extract_slot(next) == 0xFFFF) { + if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { + break; + } + } else { + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } + } + } + ma_job_queue_cas(&pQueue->tail, tail, slot); + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + + /* Signal the semaphore as the last step if we're using synchronous mode. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_release(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) +{ + ma_uint64 head; + ma_uint64 tail; + ma_uint64 next; + + if (pQueue == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're running in synchronous mode we'll need to wait on a semaphore. */ + if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { + #ifndef MA_NO_THREADING + { + ma_semaphore_wait(&pQueue->sem); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ + } + #endif + } + + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_lock(&pQueue->lock); + #endif + { + /* + BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below + is stored. One thread can fall through to the freeing of this item while another is still using "head" for the + retrieval of the "next" variable. + + The slot allocator might need to make use of some reference counting to ensure it's only truely freed when + there are no more references to the item. This must be fixed before removing these locks. + */ + + /* Now we need to remove the root item from the list. */ + for (;;) { + head = ma_atomic_load_64(&pQueue->head); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { + if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { + if (ma_job_extract_slot(next) == 0xFFFF) { + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + return MA_NO_DATA_AVAILABLE; + } + ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); + } else { + *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; + if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { + break; + } + } + } + } + } + #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE + ma_spinlock_unlock(&pQueue->lock); + #endif + + ma_slot_allocator_free(&pQueue->allocator, head); + + /* + If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We + could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as + possible. + */ + if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { + ma_job_queue_post(pQueue, pJob); + return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ + } + + return MA_SUCCESS; +} + + + +/******************************************************************************* + +Dynamic Linking + +*******************************************************************************/ +#ifdef MA_POSIX + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + +MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif + #else + handle = (ma_handle)dlopen(filename, RTLD_NOW); + #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; +#endif +} + +MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) +{ +#ifndef MA_NO_RUNTIME_LINKING + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif + + (void)pLog; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; +#endif +} + +MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic pop +#endif +#endif + + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; +#endif +} + + + +/************************************************************************************************************************************************************ +************************************************************************************************************************************************************* + +DEVICE I/O +========== + +************************************************************************************************************************************************************* +************************************************************************************************************************************************************/ + +/* Disable run-time linking on certain backends and platforms. */ +#ifndef MA_NO_RUNTIME_LINKING + #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) + #define MA_NO_RUNTIME_LINKING + #endif +#endif + +#ifndef MA_NO_DEVICE_IO + +#if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) + #include /* For mach_absolute_time() */ +#endif + +#ifdef MA_POSIX + #include + #include + + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + + + +MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) +{ + if (pDeviceInfo == NULL) { + return; + } + + if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; + } +} + + +typedef struct +{ + ma_backend backend; + const char* pName; +} ma_backend_info; + +static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */ +{ + {ma_backend_wasapi, "WASAPI"}, + {ma_backend_dsound, "DirectSound"}, + {ma_backend_winmm, "WinMM"}, + {ma_backend_coreaudio, "Core Audio"}, + {ma_backend_sndio, "sndio"}, + {ma_backend_audio4, "audio(4)"}, + {ma_backend_oss, "OSS"}, + {ma_backend_pulseaudio, "PulseAudio"}, + {ma_backend_alsa, "ALSA"}, + {ma_backend_jack, "JACK"}, + {ma_backend_aaudio, "AAudio"}, + {ma_backend_opensl, "OpenSL|ES"}, + {ma_backend_webaudio, "Web Audio"}, + {ma_backend_custom, "Custom"}, + {ma_backend_null, "Null"} +}; + +MA_API const char* ma_get_backend_name(ma_backend backend) +{ + if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) { + return "Unknown"; + } + + return gBackendInfo[backend].pName; +} + +MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend) +{ + size_t iBackend; + + if (pBackendName == NULL) { + return MA_INVALID_ARGS; + } + + for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) { + if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) { + if (pBackend != NULL) { + *pBackend = gBackendInfo[iBackend].backend; + } + + return MA_SUCCESS; + } + } + + /* Getting here means the backend name is unknown. */ + return MA_INVALID_ARGS; +} + +MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) +{ + /* + This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers + about some enums not being handled by the switch statement. + */ + switch (backend) + { + case ma_backend_wasapi: + #if defined(MA_HAS_WASAPI) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_dsound: + #if defined(MA_HAS_DSOUND) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_winmm: + #if defined(MA_HAS_WINMM) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_coreaudio: + #if defined(MA_HAS_COREAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_sndio: + #if defined(MA_HAS_SNDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_audio4: + #if defined(MA_HAS_AUDIO4) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_oss: + #if defined(MA_HAS_OSS) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_pulseaudio: + #if defined(MA_HAS_PULSEAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_alsa: + #if defined(MA_HAS_ALSA) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_jack: + #if defined(MA_HAS_JACK) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_aaudio: + #if defined(MA_HAS_AAUDIO) + #if defined(MA_ANDROID) + { + return ma_android_sdk_version() >= 26; + } + #else + return MA_FALSE; + #endif + #else + return MA_FALSE; + #endif + case ma_backend_opensl: + #if defined(MA_HAS_OPENSL) + #if defined(MA_ANDROID) + { + return ma_android_sdk_version() >= 9; + } + #else + return MA_TRUE; + #endif + #else + return MA_FALSE; + #endif + case ma_backend_webaudio: + #if defined(MA_HAS_WEBAUDIO) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_custom: + #if defined(MA_HAS_CUSTOM) + return MA_TRUE; + #else + return MA_FALSE; + #endif + case ma_backend_null: + #if defined(MA_HAS_NULL) + return MA_TRUE; + #else + return MA_FALSE; + #endif + + default: return MA_FALSE; + } +} + +MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) +{ + size_t backendCount; + size_t iBackend; + ma_result result = MA_SUCCESS; + + if (pBackendCount == NULL) { + return MA_INVALID_ARGS; + } + + backendCount = 0; + + for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { + ma_backend backend = (ma_backend)iBackend; + + if (ma_is_backend_enabled(backend)) { + /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ + if (backendCount == backendCap) { + result = MA_NO_SPACE; + break; + } else { + pBackends[backendCount] = backend; + backendCount += 1; + } + } + } + + if (pBackendCount != NULL) { + *pBackendCount = backendCount; + } + + return result; +} + +MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) +{ + switch (backend) + { + case ma_backend_wasapi: return MA_TRUE; + case ma_backend_dsound: return MA_FALSE; + case ma_backend_winmm: return MA_FALSE; + case ma_backend_coreaudio: return MA_FALSE; + case ma_backend_sndio: return MA_FALSE; + case ma_backend_audio4: return MA_FALSE; + case ma_backend_oss: return MA_FALSE; + case ma_backend_pulseaudio: return MA_FALSE; + case ma_backend_alsa: return MA_FALSE; + case ma_backend_jack: return MA_FALSE; + case ma_backend_aaudio: return MA_FALSE; + case ma_backend_opensl: return MA_FALSE; + case ma_backend_webaudio: return MA_FALSE; + case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ + case ma_backend_null: return MA_FALSE; + default: return MA_FALSE; + } +} + + + +#if defined(MA_WIN32) +/* WASAPI error codes. */ +#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) +#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) +#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) +#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) +#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) +#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) +#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) +#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) +#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) +#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) +#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) +#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) +#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) +#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) +#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) +#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) +#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) +#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) +#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) +#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) +#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) +#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) +#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) +#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) +#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) +#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) +#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) +#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) +#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) +#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) +#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) +#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) +#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) +#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) +#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) +#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) +#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) +#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) +#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) +#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) + +#define MA_DS_OK ((HRESULT)0) +#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) +#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) +#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) +#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ +#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) +#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ +#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) +#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ +#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) +#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ +#define MA_DSERR_NODRIVER ((HRESULT)0x88780078) +#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) +#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ +#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) +#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) +#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) +#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ +#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ +#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) +#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) +#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) +#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) +#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) +#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) + +static ma_result ma_result_from_HRESULT(HRESULT hr) +{ + switch (hr) + { + case NOERROR: return MA_SUCCESS; + /*case S_OK: return MA_SUCCESS;*/ + + case E_POINTER: return MA_INVALID_ARGS; + case E_UNEXPECTED: return MA_ERROR; + case E_NOTIMPL: return MA_NOT_IMPLEMENTED; + case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; + case E_INVALIDARG: return MA_INVALID_ARGS; + case E_NOINTERFACE: return MA_API_NOT_FOUND; + case E_HANDLE: return MA_INVALID_ARGS; + case E_ABORT: return MA_ERROR; + case E_FAIL: return MA_ERROR; + case E_ACCESSDENIED: return MA_ACCESS_DENIED; + + /* WASAPI */ + case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; + case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; + case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; + case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; + case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; + case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; + case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; + case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; + case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; + case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; + case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; + case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; + case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; + case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; + case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; + case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; + case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; + case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; + case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; + case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; + + /* DirectSound */ + /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ + case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; + case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; + case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; + /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ + case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; + /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ + case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; + /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ + case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; + /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ + case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; + case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; + case MA_DSERR_NOAGGREGATION: return MA_ERROR; + case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; + case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; + case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; + /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ + /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ + case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; + case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; + case MA_DSERR_SENDLOOP: return MA_DEADLOCK; + case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; + case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; + case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; + + default: return MA_ERROR; + } +} + +/* PROPVARIANT */ +#define MA_VT_LPWSTR 31 +#define MA_VT_BLOB 65 + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +typedef struct +{ + WORD vt; + WORD wReserved1; + WORD wReserved2; + WORD wReserved3; + union + { + struct + { + ULONG cbSize; + BYTE* pBlobData; + } blob; + WCHAR* pwszVal; + char pad[16]; /* Just to ensure the size of the struct matches the official version. */ + }; +} MA_PROPVARIANT; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); +typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MA_PFN_CoUninitialize)(void); +typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); +typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); +typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); +typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); + +typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); +typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); + +#if defined(MA_WIN32_DESKTOP) +/* 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 * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); +typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); +#endif /* MA_WIN32_DESKTOP */ + + +MA_API size_t ma_strlen_WCHAR(const WCHAR* str) +{ + size_t len = 0; + while (str[len] != '\0') { + len += 1; + } + + return len; +} + +MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) +{ + while (*s1 != '\0' && *s1 == *s2) { + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} +#endif /* MA_WIN32 */ + + +#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" +#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" + + + + +/******************************************************************************* + +Timing + +*******************************************************************************/ +#if defined(MA_WIN32) && !defined(MA_POSIX) + static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ + static void ma_timer_init(ma_timer* pTimer) + { + LARGE_INTEGER counter; + + if (g_ma_TimerFrequency.QuadPart == 0) { + QueryPerformanceFrequency(&g_ma_TimerFrequency); + } + + QueryPerformanceCounter(&counter); + pTimer->counter = counter.QuadPart; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + LARGE_INTEGER counter; + if (!QueryPerformanceCounter(&counter)) { + return 0; + } + + return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; + } +#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) + static ma_uint64 g_ma_TimerFrequency = 0; + static void ma_timer_init(ma_timer* pTimer) + { + mach_timebase_info_data_t baseTime; + mach_timebase_info(&baseTime); + g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; + + pTimer->counter = mach_absolute_time(); + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter = mach_absolute_time(); + ma_uint64 oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; + } +#elif defined(MA_EMSCRIPTEN) + static MA_INLINE void ma_timer_init(ma_timer* pTimer) + { + pTimer->counterD = emscripten_get_now(); + } + + static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ + } +#else + #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L + #if defined(CLOCK_MONOTONIC) + #define MA_CLOCK_ID CLOCK_MONOTONIC + #else + #define MA_CLOCK_ID CLOCK_REALTIME + #endif + + static void ma_timer_init(ma_timer* pTimer) + { + struct timespec newTime; + clock_gettime(MA_CLOCK_ID, &newTime); + + pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter; + ma_uint64 oldTimeCounter; + + struct timespec newTime; + clock_gettime(MA_CLOCK_ID, &newTime); + + newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; + oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / 1000000000.0; + } + #else + static void ma_timer_init(ma_timer* pTimer) + { + struct timeval newTime; + gettimeofday(&newTime, NULL); + + pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; + } + + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) + { + ma_uint64 newTimeCounter; + ma_uint64 oldTimeCounter; + + struct timeval newTime; + gettimeofday(&newTime, NULL); + + newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; + oldTimeCounter = pTimer->counter; + + return (newTimeCounter - oldTimeCounter) / 1000000.0; + } + #endif +#endif + + + +#if 0 +static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) +{ + ma_uint32 closestRate = 0; + ma_uint32 closestDiff = 0xFFFFFFFF; + size_t iStandardRate; + + for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; + ma_uint32 diff; + + if (sampleRateIn > standardRate) { + diff = sampleRateIn - standardRate; + } else { + diff = standardRate - sampleRateIn; + } + + if (diff == 0) { + return standardRate; /* The input sample rate is a standard rate. */ + } + + if (closestDiff > diff) { + closestDiff = diff; + closestRate = standardRate; + } + } + + return closestRate; +} +#endif + + +static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + return ma_disable_denormals(); + } else { + return 0; + } +} + +static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) +{ + MA_ASSERT(pDevice != NULL); + + if (!pDevice->noDisableDenormals) { + ma_restore_denormals(prevState); + } else { + /* Do nothing. */ + (void)prevState; + } +} + +static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) +{ + ma_device_notification notification; + + MA_ZERO_OBJECT(¬ification); + notification.pDevice = pDevice; + notification.type = type; + + return notification; +} + +static void ma_device__on_notification(ma_device_notification notification) +{ + MA_ASSERT(notification.pDevice != NULL); + + if (notification.pDevice->onNotification != NULL) { + notification.pDevice->onNotification(¬ification); + } + + /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ + if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { + notification.pDevice->onStop(notification.pDevice); + } +} + +static void ma_device__on_notification_started(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); +} + +static void ma_device__on_notification_stopped(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); +} + +/* Not all platforms support reroute notifications. */ +#if !defined(MA_EMSCRIPTEN) +static void ma_device__on_notification_rerouted(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); +} +#endif + +#if defined(MA_EMSCRIPTEN) +EMSCRIPTEN_KEEPALIVE +void ma_device__on_notification_unlocked(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked)); +} +#endif + + +static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDevice->onData != NULL); + + if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); + } + + pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); +} + +static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice != NULL); + + /* Don't read more data from the client if we're in the process of stopping. */ + if (ma_device_get_state(pDevice) == ma_device_state_stopping) { + return; + } + + if (pDevice->noFixedSizedCallback) { + /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ + ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); + } else { + /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ + ma_uint32 totalFramesProcessed = 0; + + while (totalFramesProcessed < frameCount) { + ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; + ma_uint32 framesToProcessThisIteration = 0; + + if (pFramesIn != NULL) { + /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ + if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { + /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), + ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), + framesToProcessThisIteration, + pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; + } + + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + /* No room left in the intermediary buffer. Fire the data callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* We'll do the duplex data callback later after we've processed the playback data. */ + } else { + ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + /* The intermediary buffer has just been drained. */ + pDevice->capture.intermediaryBufferLen = 0; + } + } + } + + if (pFramesOut != NULL) { + /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ + if (pDevice->playback.intermediaryBufferLen > 0) { + /* There's some content in the intermediary buffer. Read from that without firing the callback. */ + if (pDevice->type == ma_device_type_duplex) { + /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ + } else { + framesToProcessThisIteration = totalFramesRemaining; + if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { + framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; + } + } + + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), + ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), + framesToProcessThisIteration, + pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; + } + + if (pDevice->playback.intermediaryBufferLen == 0) { + /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ + if (pDevice->type == ma_device_type_duplex) { + /* In duplex mode, the data callback will be fired later. Nothing to do here. */ + } else { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); + + /* The intermediary buffer has just been filled. */ + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; + } + } + } + + /* If we're in duplex mode we might need to do a refill of the data. */ + if (pDevice->type == ma_device_type_duplex) { + if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { + ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); + + pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ + pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ + } + } + + /* Make sure this is only incremented once in the duplex case. */ + totalFramesProcessed += framesToProcessThisIteration; + } + } +} + +static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + float masterVolumeFactor; + + ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ + + if (pDevice->onData) { + unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); + { + /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ + if (pFramesIn != NULL && masterVolumeFactor < 1) { + ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint32 totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; + if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { + framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; + } + + ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); + + ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); + + totalFramesProcessed += framesToProcessThisIteration; + } + } else { + ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); + } + + /* Volume control and clipping for playback devices. */ + if (pFramesOut != NULL) { + if (masterVolumeFactor < 1) { + if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ + ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); + } + } + + if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { + ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ + } + } + } + ma_device_restore_denormals(pDevice, prevDenormalState); + } +} + + + +/* A helper function for reading sample data from the client. */ +static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCount > 0); + MA_ASSERT(pFramesOut != NULL); + + if (pDevice->playback.converter.isPassthrough) { + ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); + } else { + ma_result result; + ma_uint64 totalFramesReadOut; + void* pRunningFramesOut; + + totalFramesReadOut = 0; + pRunningFramesOut = pFramesOut; + + /* + We run slightly different logic depending on whether or not we're using a heap-allocated + buffer for caching input data. This will be the case if the data converter does not have + the ability to retrieve the required input frame count for a given output frame count. + */ + if (pDevice->playback.pInputCache != NULL) { + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDevice->playback.inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { + framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; + pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ + if (pDevice->playback.inputCacheRemaining == 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; + } + } + } else { + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; + + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } + + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } + + if (framesToReadThisIterationIn > 0) { + ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationIn = framesToReadThisIterationIn; + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + } + } +} + +/* A helper for sending sample data to the client. */ +static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCountInDeviceFormat > 0); + MA_ASSERT(pFramesInDeviceFormat != NULL); + + if (pDevice->capture.converter.isPassthrough) { + ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); + } else { + ma_result result; + ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint64 totalDeviceFramesProcessed = 0; + ma_uint64 totalClientFramesProcessed = 0; + const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; + + /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ + for (;;) { + ma_uint64 deviceFramesProcessedThisIteration; + ma_uint64 clientFramesProcessedThisIteration; + + deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); + clientFramesProcessedThisIteration = framesInClientFormatCap; + + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); + if (result != MA_SUCCESS) { + break; + } + + if (clientFramesProcessedThisIteration > 0) { + ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ + } + + pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; + totalClientFramesProcessed += clientFramesProcessedThisIteration; + + /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ + (void)totalClientFramesProcessed; + + if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { + break; /* We're done. */ + } + } + } +} + +static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) +{ + ma_result result; + ma_uint32 totalDeviceFramesProcessed = 0; + const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCountInDeviceFormat > 0); + MA_ASSERT(pFramesInDeviceFormat != NULL); + MA_ASSERT(pRB != NULL); + + /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ + for (;;) { + ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); + ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint64 framesProcessedInDeviceFormat; + ma_uint64 framesProcessedInClientFormat; + void* pFramesInClientFormat; + + result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); + break; + } + + if (framesToProcessInClientFormat == 0) { + if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { + break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ + } + } + + /* Convert. */ + framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; + framesProcessedInClientFormat = framesToProcessInClientFormat; + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); + if (result != MA_SUCCESS) { + break; + } + + result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); + break; + } + + pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ + + /* We're done when we're unable to process any client nor device frames. */ + if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { + break; /* Done. */ + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) +{ + ma_result result; + ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 totalFramesReadOut = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(frameCount > 0); + MA_ASSERT(pFramesInInternalFormat != NULL); + MA_ASSERT(pRB != NULL); + MA_ASSERT(pDevice->playback.pInputCache != NULL); + + /* + Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for + the whole frameCount frames we just use silence instead for the input data. + */ + MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); + + while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { + /* + We should have a buffer allocated on the heap. Any playback frames still sitting in there + need to be sent to the internal device before we process any more data from the client. + */ + if (pDevice->playback.inputCacheRemaining > 0) { + ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; + ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); + ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); + + pDevice->playback.inputCacheConsumed += framesConvertedIn; + pDevice->playback.inputCacheRemaining -= framesConvertedIn; + + totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ + pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + } + + /* If there's no more data in the cache we'll need to fill it with some. */ + if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { + ma_uint32 inputFrameCount; + void* pInputFrames; + + inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; + result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); + if (result == MA_SUCCESS) { + if (inputFrameCount > 0) { + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); + } else { + if (ma_pcm_rb_pointer_distance(pRB) == 0) { + break; /* Underrun. */ + } + } + } else { + /* No capture data available. Feed in silence. */ + inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); + ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); + } + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = inputFrameCount; + + result = ma_pcm_rb_commit_read(pRB, inputFrameCount); + if (result != MA_SUCCESS) { + return result; /* Should never happen. */ + } + } + } + + return MA_SUCCESS; +} + +/* A helper for changing the state of the device. */ +static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) +{ + ma_atomic_device_state_set(&pDevice->state, newState); +} + + +#if defined(MA_WIN32) + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ +#endif + + + +MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ +{ + ma_uint32 i; + for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { + if (g_maFormatPriorities[i] == format) { + return i; + } + } + + /* Getting here means the format could not be found or is equal to ma_format_unknown. */ + return (ma_uint32)-1; +} + +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); + +static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) +{ + if (pDeviceDescriptor == NULL) { + return MA_FALSE; + } + + if (pDeviceDescriptor->format == ma_format_unknown) { + return MA_FALSE; + } + + if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { + return MA_FALSE; + } + + if (pDeviceDescriptor->sampleRate == 0) { + return MA_FALSE; + } + + return MA_TRUE; +} + + +static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) +{ + ma_result result = MA_SUCCESS; + ma_bool32 exitLoop = MA_FALSE; + ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 capturedDeviceDataCapInFrames = 0; + ma_uint32 playbackDeviceDataCapInFrames = 0; + + MA_ASSERT(pDevice != NULL); + + /* Just some quick validation on the device type and the available callbacks. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + if (pDevice->pContext->callbacks.onDeviceRead == NULL) { + return MA_NOT_IMPLEMENTED; + } + + capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { + return MA_NOT_IMPLEMENTED; + } + + playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + /* NOTE: The device was started outside of this function, in the worker thread. */ + + while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { + switch (pDevice->type) { + case ma_device_type_duplex: + { + /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ + ma_uint32 totalCapturedDeviceFramesProcessed = 0; + ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); + + while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { + ma_uint32 capturedDeviceFramesRemaining; + ma_uint32 capturedDeviceFramesProcessed; + ma_uint32 capturedDeviceFramesToProcess; + ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; + if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { + capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; + } + + result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; + capturedDeviceFramesProcessed = 0; + + /* At this point we have our captured data in device format and we now need to convert it to client format. */ + for (;;) { + ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); + ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; + ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + + /* Convert capture data from device format to client format. */ + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); + if (result != MA_SUCCESS) { + break; + } + + /* + If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small + which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. + */ + if (capturedClientFramesToProcessThisIteration == 0) { + break; + } + + ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ + + capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ + capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ + + /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ + for (;;) { + ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; + ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); + if (result != MA_SUCCESS) { + break; + } + + result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ + if (capturedClientFramesToProcessThisIteration == 0) { + break; + } + } + + /* In case an error happened from ma_device_write__null()... */ + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + } + + /* Make sure we don't get stuck in the inner loop. */ + if (capturedDeviceFramesProcessed == 0) { + break; + } + + totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; + } + } break; + + case ma_device_type_capture: + case ma_device_type_loopback: + { + ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + ma_uint32 framesReadThisPeriod = 0; + while (framesReadThisPeriod < periodSizeInFrames) { + ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; + ma_uint32 framesProcessed; + ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; + if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { + framesToReadThisIteration = capturedDeviceDataCapInFrames; + } + + result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + /* Make sure we don't get stuck in the inner loop. */ + if (framesProcessed == 0) { + break; + } + + ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); + + framesReadThisPeriod += framesProcessed; + } + } break; + + case ma_device_type_playback: + { + /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ + ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + ma_uint32 framesWrittenThisPeriod = 0; + while (framesWrittenThisPeriod < periodSizeInFrames) { + ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; + ma_uint32 framesProcessed; + ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; + if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { + framesToWriteThisIteration = playbackDeviceDataCapInFrames; + } + + ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); + + result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); + if (result != MA_SUCCESS) { + exitLoop = MA_TRUE; + break; + } + + /* Make sure we don't get stuck in the inner loop. */ + if (framesProcessed == 0) { + break; + } + + framesWrittenThisPeriod += framesProcessed; + } + } break; + + /* Should never get here. */ + default: break; + } + } + + return result; +} + + + +/******************************************************************************* + +Null Backend + +*******************************************************************************/ +#ifdef MA_HAS_NULL + +#define MA_DEVICE_OP_NONE__NULL 0 +#define MA_DEVICE_OP_START__NULL 1 +#define MA_DEVICE_OP_SUSPEND__NULL 2 +#define MA_DEVICE_OP_KILL__NULL 3 + +static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) +{ + ma_device* pDevice = (ma_device*)pData; + MA_ASSERT(pDevice != NULL); + + for (;;) { /* Keep the thread alive until the device is uninitialized. */ + ma_uint32 operation; + + /* Wait for an operation to be requested. */ + ma_event_wait(&pDevice->null_device.operationEvent); + + /* At this point an event should have been triggered. */ + operation = pDevice->null_device.operation; + + /* Starting the device needs to put the thread into a loop. */ + if (operation == MA_DEVICE_OP_START__NULL) { + /* Reset the timer just in case. */ + ma_timer_init(&pDevice->null_device.timer); + + /* Getting here means a suspend or kill operation has been requested. */ + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; + } + + /* Suspending the device means we need to stop the timer and just continue the loop. */ + if (operation == MA_DEVICE_OP_SUSPEND__NULL) { + /* We need to add the current run time to the prior run time, then reset the timer. */ + pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); + ma_timer_init(&pDevice->null_device.timer); + + /* We're done. */ + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; + } + + /* Killing the device means we need to get out of this loop so that this thread can terminate. */ + if (operation == MA_DEVICE_OP_KILL__NULL) { + pDevice->null_device.operationResult = MA_SUCCESS; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + break; + } + + /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ + if (operation == MA_DEVICE_OP_NONE__NULL) { + MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ + pDevice->null_device.operationResult = MA_INVALID_OPERATION; + ma_event_signal(&pDevice->null_device.operationCompletionEvent); + ma_semaphore_release(&pDevice->null_device.operationSemaphore); + continue; /* Continue the loop. Don't terminate. */ + } + } + + return (ma_thread_result)0; +} + +static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) +{ + ma_result result; + + /* + TODO: Need to review this and consider just using mutual exclusion. I think the original motivation + for this was to just post the event to a queue and return immediately, but that has since changed + and now this function is synchronous. I think this can be simplified to just use a mutex. + */ + + /* + The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later + to support queing of operations. + */ + result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); + if (result != MA_SUCCESS) { + return result; /* Failed to wait for the event. */ + } + + /* + When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to + signal an event to the worker thread to let it know that it can start work. + */ + pDevice->null_device.operation = operation; + + /* Once the operation code has been set, the worker thread can start work. */ + if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { + return MA_ERROR; + } + + /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ + if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { + return MA_ERROR; + } + + return pDevice->null_device.operationResult; +} + +static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) +{ + ma_uint32 internalSampleRate; + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + internalSampleRate = pDevice->capture.internalSampleRate; + } else { + internalSampleRate = pDevice->playback.internalSampleRate; + } + + return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); +} + +static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + (void)cbResult; /* Silence a static analysis warning. */ + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ + + /* Support everything on the null backend. */ + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; + pDeviceInfo->nativeDataFormats[0].sampleRate = 0; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + (void)pContext; + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* Keep it clean and wait for the device thread to finish before returning. */ + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); + + /* Wait for the thread to finish before continuing. */ + ma_thread_wait(&pDevice->null_device.deviceThread); + + /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ + ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); + ma_event_uninit(&pDevice->null_device.operationCompletionEvent); + ma_event_uninit(&pDevice->null_device.operationEvent); + + return MA_SUCCESS; +} + +static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->null_device); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* The null backend supports everything exactly as we specify it. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; + pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + } + + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; + pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); + } + + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + } + + /* + In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the + first period is "written" to it, and then stopped in ma_device_stop__null(). + */ + result = ma_event_init(&pDevice->null_device.operationEvent); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_event_init(&pDevice->null_device.operationCompletionEvent); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ + if (result != MA_SUCCESS) { + return result; + } + + result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); + + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE); + return MA_SUCCESS; +} + +static ma_result ma_device_stop__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); + + ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE); + return MA_SUCCESS; +} + +static ma_bool32 ma_device_is_started__null(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + return ma_atomic_bool32_get(&pDevice->null_device.isStarted); +} + +static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalPCMFramesProcessed; + ma_bool32 wasStartedOnEntry; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + wasStartedOnEntry = ma_device_is_started__null(pDevice); + + /* Keep going until everything has been read. */ + totalPCMFramesProcessed = 0; + while (totalPCMFramesProcessed < frameCount) { + ma_uint64 targetFrame; + + /* If there are any frames remaining in the current period, consume those first. */ + if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { + ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); + ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; + if (framesToProcess > framesRemaining) { + framesToProcess = framesRemaining; + } + + /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ + (void)pPCMFrames; + + pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; + totalPCMFramesProcessed += framesToProcess; + } + + /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ + if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { + pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; + + if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) { + result = ma_device_start__null(pDevice); + if (result != MA_SUCCESS) { + break; + } + } + } + + /* If we've consumed the whole buffer we can return now. */ + MA_ASSERT(totalPCMFramesProcessed <= frameCount); + if (totalPCMFramesProcessed == frameCount) { + break; + } + + /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ + targetFrame = pDevice->null_device.lastProcessedFramePlayback; + for (;;) { + ma_uint64 currentFrame; + + /* Stop waiting if the device has been stopped. */ + if (!ma_device_is_started__null(pDevice)) { + break; + } + + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + if (currentFrame >= targetFrame) { + break; + } + + /* Getting here means we haven't yet reached the target sample, so continue waiting. */ + ma_sleep(10); + } + + pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; + pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalPCMFramesProcessed; + } + + return result; +} + +static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalPCMFramesProcessed; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + /* Keep going until everything has been read. */ + totalPCMFramesProcessed = 0; + while (totalPCMFramesProcessed < frameCount) { + ma_uint64 targetFrame; + + /* If there are any frames remaining in the current period, consume those first. */ + if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); + ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; + if (framesToProcess > framesRemaining) { + framesToProcess = framesRemaining; + } + + /* We need to ensure the output buffer is zeroed. */ + MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); + + pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; + totalPCMFramesProcessed += framesToProcess; + } + + /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ + if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { + pDevice->null_device.currentPeriodFramesRemainingCapture = 0; + } + + /* If we've consumed the whole buffer we can return now. */ + MA_ASSERT(totalPCMFramesProcessed <= frameCount); + if (totalPCMFramesProcessed == frameCount) { + break; + } + + /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ + targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; + for (;;) { + ma_uint64 currentFrame; + + /* Stop waiting if the device has been stopped. */ + if (!ma_device_is_started__null(pDevice)) { + break; + } + + currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); + if (currentFrame >= targetFrame) { + break; + } + + /* Getting here means we haven't yet reached the target sample, so continue waiting. */ + ma_sleep(10); + } + + pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; + pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalPCMFramesProcessed; + } + + return result; +} + +static ma_result ma_context_uninit__null(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_null); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + (void)pContext; + + pCallbacks->onContextInit = ma_context_init__null; + pCallbacks->onContextUninit = ma_context_uninit__null; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; + pCallbacks->onDeviceInit = ma_device_init__null; + pCallbacks->onDeviceUninit = ma_device_uninit__null; + pCallbacks->onDeviceStart = ma_device_start__null; + pCallbacks->onDeviceStop = ma_device_stop__null; + pCallbacks->onDeviceRead = ma_device_read__null; + pCallbacks->onDeviceWrite = ma_device_write__null; + pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ + + /* The null backend always works. */ + return MA_SUCCESS; +} +#endif + + + +/******************************************************************************* + +WIN32 COMMON + +*******************************************************************************/ +#if defined(MA_WIN32) +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) + #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() + #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) + #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) +#else + #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) + #define ma_CoUninitialize(pContext) CoUninitialize() + #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) + #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) + #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) +#endif + +#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) +typedef size_t DWORD_PTR; +#endif + +#if !defined(WAVE_FORMAT_1M08) +#define WAVE_FORMAT_1M08 0x00000001 +#define WAVE_FORMAT_1S08 0x00000002 +#define WAVE_FORMAT_1M16 0x00000004 +#define WAVE_FORMAT_1S16 0x00000008 +#define WAVE_FORMAT_2M08 0x00000010 +#define WAVE_FORMAT_2S08 0x00000020 +#define WAVE_FORMAT_2M16 0x00000040 +#define WAVE_FORMAT_2S16 0x00000080 +#define WAVE_FORMAT_4M08 0x00000100 +#define WAVE_FORMAT_4S08 0x00000200 +#define WAVE_FORMAT_4M16 0x00000400 +#define WAVE_FORMAT_4S16 0x00000800 +#endif + +#if !defined(WAVE_FORMAT_44M08) +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + +#ifndef SPEAKER_FRONT_LEFT +#define SPEAKER_FRONT_LEFT 0x1 +#define SPEAKER_FRONT_RIGHT 0x2 +#define SPEAKER_FRONT_CENTER 0x4 +#define SPEAKER_LOW_FREQUENCY 0x8 +#define SPEAKER_BACK_LEFT 0x10 +#define SPEAKER_BACK_RIGHT 0x20 +#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 +#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 +#define SPEAKER_BACK_CENTER 0x100 +#define SPEAKER_SIDE_LEFT 0x200 +#define SPEAKER_SIDE_RIGHT 0x400 +#define SPEAKER_TOP_CENTER 0x800 +#define SPEAKER_TOP_FRONT_LEFT 0x1000 +#define SPEAKER_TOP_FRONT_CENTER 0x2000 +#define SPEAKER_TOP_FRONT_RIGHT 0x4000 +#define SPEAKER_TOP_BACK_LEFT 0x8000 +#define SPEAKER_TOP_BACK_CENTER 0x10000 +#define SPEAKER_TOP_BACK_RIGHT 0x20000 +#endif + +/* +Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this +because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The +standard version uses tight packing, but for compiler compatibility we're not doing that with ours. +*/ +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} MA_WAVEFORMATEX; + +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; + union + { + WORD wValidBitsPerSample; + WORD wSamplesPerBlock; + WORD wReserved; + } Samples; + DWORD dwChannelMask; + GUID SubFormat; +} MA_WAVEFORMATEXTENSIBLE; + + + +#ifndef WAVE_FORMAT_EXTENSIBLE +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE +#endif + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + +#ifndef WAVE_FORMAT_IEEE_FLOAT +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#endif + +/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ +static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) +{ + switch (id) + { + case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; + case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; + case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; + case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ +static DWORD ma_channel_id_to_win32(DWORD id) +{ + switch (id) + { + case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; + case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; + case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; + case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; + case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; + case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts a channel mapping to a Win32-style channel mask. */ +static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) +{ + DWORD dwChannelMask = 0; + ma_uint32 iChannel; + + for (iChannel = 0; iChannel < channels; ++iChannel) { + dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); + } + + return dwChannelMask; +} + +/* Converts a Win32-style channel mask to a miniaudio channel map. */ +static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) +{ + /* If the channel mask is set to 0, just assume a default Win32 channel map. */ + if (dwChannelMask == 0) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); + } else { + if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else { + /* Just iterate over each bit. */ + ma_uint32 iChannel = 0; + ma_uint32 iBit; + + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { + DWORD bitValue = (dwChannelMask & (1UL << iBit)); + if (bitValue != 0) { + /* The bit is set. */ + pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); + iChannel += 1; + } + } + } + } +} + +#ifdef __cplusplus +static ma_bool32 ma_is_guid_equal(const void* a, const void* b) +{ + return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); +} +#else +#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) +#endif + +static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) +{ + static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + return ma_is_guid_equal(guid, &nullguid); +} + +static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) +{ + MA_ASSERT(pWF != NULL); + + if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; + if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return ma_format_s32; + } + if (pWFEX->Samples.wValidBitsPerSample == 24) { + if (pWFEX->wBitsPerSample == 32) { + return ma_format_s32; + } + if (pWFEX->wBitsPerSample == 24) { + return ma_format_s24; + } + } + if (pWFEX->Samples.wValidBitsPerSample == 16) { + return ma_format_s16; + } + if (pWFEX->Samples.wValidBitsPerSample == 8) { + return ma_format_u8; + } + } + if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { + if (pWFEX->Samples.wValidBitsPerSample == 32) { + return ma_format_f32; + } + /* + if (pWFEX->Samples.wValidBitsPerSample == 64) { + return ma_format_f64; + } + */ + } + } else { + if (pWF->wFormatTag == WAVE_FORMAT_PCM) { + if (pWF->wBitsPerSample == 32) { + return ma_format_s32; + } + if (pWF->wBitsPerSample == 24) { + return ma_format_s24; + } + if (pWF->wBitsPerSample == 16) { + return ma_format_s16; + } + if (pWF->wBitsPerSample == 8) { + return ma_format_u8; + } + } + if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + if (pWF->wBitsPerSample == 32) { + return ma_format_f32; + } + if (pWF->wBitsPerSample == 64) { + /*return ma_format_f64;*/ + } + } + } + + return ma_format_unknown; +} +#endif + + +/******************************************************************************* + +WASAPI Backend + +*******************************************************************************/ +#ifdef MA_HAS_WASAPI +#if 0 +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ +#endif +#include +#include +#if defined(_MSC_VER) + #pragma warning(pop) +#endif +#endif /* 0 */ + +static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); + +/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ +#define MA_WIN32_WINNT_VISTA 0x0600 +#define MA_VER_MINORVERSION 0x01 +#define MA_VER_MAJORVERSION 0x02 +#define MA_VER_SERVICEPACKMAJOR 0x20 +#define MA_VER_GREATER_EQUAL 0x03 + +typedef struct { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + WCHAR szCSDVersion[128]; + WORD wServicePackMajor; + WORD wServicePackMinor; + WORD wSuiteMask; + BYTE wProductType; + BYTE wReserved; +} ma_OSVERSIONINFOEXW; + +typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); +typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); + + +#ifndef PROPERTYKEY_DEFINED +#define PROPERTYKEY_DEFINED +#ifndef __WATCOMC__ +typedef struct +{ + GUID fmtid; + DWORD pid; +} PROPERTYKEY; +#endif +#endif + +/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ +static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) +{ + MA_ZERO_OBJECT(pProp); +} + + +static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; +static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; + +static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ +#endif + +static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ +static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ +static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ +static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ +static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ +static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ +static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ +static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ +#endif + +static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ +static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +#define MA_MM_DEVICE_STATE_ACTIVE 1 +#define MA_MM_DEVICE_STATE_DISABLED 2 +#define MA_MM_DEVICE_STATE_NOTPRESENT 4 +#define MA_MM_DEVICE_STATE_UNPLUGGED 8 + +typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; +typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; +typedef struct ma_IMMDevice ma_IMMDevice; +#else +typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; +typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; +#endif +typedef struct ma_IPropertyStore ma_IPropertyStore; +typedef struct ma_IAudioClient ma_IAudioClient; +typedef struct ma_IAudioClient2 ma_IAudioClient2; +typedef struct ma_IAudioClient3 ma_IAudioClient3; +typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; +typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; + +typedef ma_int64 MA_REFERENCE_TIME; + +#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 +#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 +#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 +#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 +#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 +#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 +#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 +#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 +#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 +#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 + +/* Buffer flags. */ +#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 +#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 +#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 + +typedef enum +{ + ma_eRender = 0, + ma_eCapture = 1, + ma_eAll = 2 +} ma_EDataFlow; + +typedef enum +{ + ma_eConsole = 0, + ma_eMultimedia = 1, + ma_eCommunications = 2 +} ma_ERole; + +typedef enum +{ + MA_AUDCLNT_SHAREMODE_SHARED, + MA_AUDCLNT_SHAREMODE_EXCLUSIVE +} MA_AUDCLNT_SHAREMODE; + +typedef enum +{ + MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ +} MA_AUDIO_STREAM_CATEGORY; + +typedef struct +{ + ma_uint32 cbSize; + BOOL bIsOffload; + MA_AUDIO_STREAM_CATEGORY eCategory; +} ma_AudioClientProperties; + +/* IUnknown */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); +} ma_IUnknownVtbl; +struct ma_IUnknown +{ + ma_IUnknownVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* IMMNotificationClient */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); + + /* IMMNotificationClient */ + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); + } ma_IMMNotificationClientVtbl; + + /* IMMDeviceEnumerator */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); + + /* IMMDeviceEnumerator */ + HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); + HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); + HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); + HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); + HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); + } ma_IMMDeviceEnumeratorVtbl; + struct ma_IMMDeviceEnumerator + { + ma_IMMDeviceEnumeratorVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } + + + /* IMMDeviceCollection */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); + + /* IMMDeviceCollection */ + HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); + HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); + } ma_IMMDeviceCollectionVtbl; + struct ma_IMMDeviceCollection + { + ma_IMMDeviceCollectionVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } + static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } + + + /* IMMDevice */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); + + /* IMMDevice */ + HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); + HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); + HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); + HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); + } ma_IMMDeviceVtbl; + struct ma_IMMDevice + { + ma_IMMDeviceVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } + static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } + static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } + static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } +#else + /* IActivateAudioInterfaceAsyncOperation */ + typedef struct + { + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); + + /* IActivateAudioInterfaceAsyncOperation */ + HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); + } ma_IActivateAudioInterfaceAsyncOperationVtbl; + struct ma_IActivateAudioInterfaceAsyncOperation + { + ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; + }; + static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } + static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } + static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } + static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } +#endif + +/* IPropertyStore */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); + + /* IPropertyStore */ + HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); + HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); + HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); + HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); + HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); +} ma_IPropertyStoreVtbl; +struct ma_IPropertyStore +{ + ma_IPropertyStoreVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } +static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } +static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } + + +/* IAudioClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); +} ma_IAudioClientVtbl; +struct ma_IAudioClient +{ + ma_IAudioClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } + +/* IAudioClient2 */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); + + /* IAudioClient2 */ + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); +} ma_IAudioClient2Vtbl; +struct ma_IAudioClient2 +{ + ma_IAudioClient2Vtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } + + +/* IAudioClient3 */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); + + /* IAudioClient */ + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); + HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); + HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); + HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); + HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); + + /* IAudioClient2 */ + HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); + HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + + /* IAudioClient3 */ + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); +} ma_IAudioClient3Vtbl; +struct ma_IAudioClient3 +{ + ma_IAudioClient3Vtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } +static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } +static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } +static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } +static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } +static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } + + +/* IAudioRenderClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); + + /* IAudioRenderClient */ + HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); +} ma_IAudioRenderClientVtbl; +struct ma_IAudioRenderClient +{ + ma_IAudioRenderClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } +static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } + + +/* IAudioCaptureClient */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); + + /* IAudioRenderClient */ + HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); + HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); + HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); +} ma_IAudioCaptureClientVtbl; +struct ma_IAudioCaptureClient +{ + ma_IAudioCaptureClientVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } +static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } + +#if defined(MA_WIN32_UWP) +/* mmdevapi Functions */ +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); +#endif + +/* Avrt Functions */ +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); +typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); + +#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) +typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; + +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); + + /* IActivateAudioInterfaceCompletionHandler */ + HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); +} ma_completion_handler_uwp_vtbl; +struct ma_completion_handler_uwp +{ + ma_completion_handler_uwp_vtbl* lpVtbl; + MA_ATOMIC(4, ma_uint32) counter; + HANDLE hEvent; +}; + +static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_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 (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + /* Getting here means the IID is IUnknown or IMMNotificationClient. */ + *ppObject = (void*)pThis; + ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) +{ + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; +} + +static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) +{ + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + if (newRefCount == 0) { + return 0; /* We don't free anything here because we never allocate the object on the heap. */ + } + + return (ULONG)newRefCount; +} + +static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) +{ + (void)pActivateOperation; + SetEvent(pThis->hEvent); + return S_OK; +} + + +static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { + ma_completion_handler_uwp_QueryInterface, + ma_completion_handler_uwp_AddRef, + ma_completion_handler_uwp_Release, + ma_completion_handler_uwp_ActivateCompleted +}; + +static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) +{ + MA_ASSERT(pHandler != NULL); + MA_ZERO_OBJECT(pHandler); + + pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; + pHandler->counter = 1; + pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (pHandler->hEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) +{ + if (pHandler->hEvent != NULL) { + CloseHandle(pHandler->hEvent); + } +} + +static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) +{ + WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); +} +#endif /* !MA_WIN32_DESKTOP */ + +/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_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 (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { + *ppObject = NULL; + return E_NOINTERFACE; + } + + /* Getting here means the IID is IUnknown or IMMNotificationClient. */ + *ppObject = (void*)pThis; + ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); + return S_OK; +} + +static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) +{ + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; +} + +static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) +{ + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; + if (newRefCount == 0) { + return 0; /* We don't free anything here because we never allocate the object on the heap. */ + } + + return (ULONG)newRefCount; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) +{ + ma_bool32 isThisDevice = MA_FALSE; + ma_bool32 isCapture = MA_FALSE; + ma_bool32 isPlayback = MA_FALSE; + +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ +#endif + + /* + There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect + that the device is disabled or has been unplugged. + */ + if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { + isCapture = MA_TRUE; + if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { + isPlayback = MA_TRUE; + if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { + isThisDevice = MA_TRUE; + } + } + + + /* + If the device ID matches our device we need to mark our device as detached and stop it. When a + device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device + was started at the time of being removed. + */ + if (isThisDevice) { + if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { + /* + Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll + use this to determine whether or not we need to automatically start the device when it's + plugged back in again. + */ + if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { + if (isPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; + } + if (isCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; + } + + ma_device_stop(pThis->pDevice); + } + } + + if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { + /* The device was activated. If we were detached, we need to start it again. */ + ma_bool8 tryRestartingDevice = MA_FALSE; + + if (isPlayback) { + if (pThis->pDevice->wasapi.isDetachedPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); + tryRestartingDevice = MA_TRUE; + } + } + + if (isCapture) { + if (pThis->pDevice->wasapi.isDetachedCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + tryRestartingDevice = MA_TRUE; + } + } + + if (tryRestartingDevice) { + if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { + ma_device_start(pThis->pDevice); + } + } + } + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + /* We don't need to worry about this event for our purposes. */ + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + /* We don't need to worry about this event for our purposes. */ + (void)pThis; + (void)pDeviceID; + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ +#endif + + (void)role; + + /* We only care about devices with the same data flow as the current device. */ + if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || + (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || + (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); + return S_OK; + } + + /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */ + if (pThis->pDevice->type == ma_device_type_loopback) { + dataFlow = ma_eCapture; + } + + /* Don't do automatic stream routing if we're not allowed. */ + if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || + (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); + 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 miniaudio, we can try re-enabling this once + it's fixed. + */ + if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || + (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); + return S_OK; + } + + + + /* + Second attempt at device rerouting. We're going to retrieve the device's state at the time of + the route change. We're then going to stop the device, reinitialize the device, and then start + it again if the state before stopping was ma_device_state_started. + */ + { + ma_uint32 previousState = ma_device_get_state(pThis->pDevice); + ma_bool8 restartDevice = MA_FALSE; + + if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) { + ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n"); + return S_OK; + } + + if (previousState == ma_device_state_started) { + ma_device_stop(pThis->pDevice); + restartDevice = MA_TRUE; + } + + if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ + ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock); + { + if (dataFlow == ma_eRender) { + ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); + + if (pThis->pDevice->wasapi.isDetachedPlayback) { + pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; + + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { + restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ + } + } + } + else { + ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); + + if (pThis->pDevice->wasapi.isDetachedCapture) { + pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; + + if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { + restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ + } + else { + restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ + } + } + } + } + ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock); + + if (restartDevice) { + ma_device_start(pThis->pDevice); + } + } + } + + return S_OK; +} + +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) +{ +#ifdef MA_DEBUG_OUTPUT + /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ +#endif + + (void)pThis; + (void)pDeviceID; + (void)key; + return S_OK; +} + +static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { + ma_IMMNotificationClient_QueryInterface, + ma_IMMNotificationClient_AddRef, + ma_IMMNotificationClient_Release, + ma_IMMNotificationClient_OnDeviceStateChanged, + ma_IMMNotificationClient_OnDeviceAdded, + ma_IMMNotificationClient_OnDeviceRemoved, + ma_IMMNotificationClient_OnDefaultDeviceChanged, + ma_IMMNotificationClient_OnPropertyValueChanged +}; +#endif /* MA_WIN32_DESKTOP */ + +static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) +{ + switch (usage) + { + case ma_wasapi_usage_default: return NULL; + case ma_wasapi_usage_games: return "Games"; + case ma_wasapi_usage_pro_audio: return "Pro Audio"; + default: break; + } + + return NULL; +} + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +typedef ma_IMMDevice ma_WASAPIDeviceInterface; +#else +typedef ma_IUnknown ma_WASAPIDeviceInterface; +#endif + + +#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 +#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 +#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 + +static ma_context_command__wasapi ma_context_init_command__wasapi(int code) +{ + ma_context_command__wasapi cmd; + + MA_ZERO_OBJECT(&cmd); + cmd.code = code; + + return cmd; +} + +static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) +{ + /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ + ma_result result; + ma_bool32 isUsingLocalEvent = MA_FALSE; + ma_event localEvent; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCmd != NULL); + + if (pCmd->pEvent == NULL) { + isUsingLocalEvent = MA_TRUE; + + result = ma_event_init(&localEvent); + if (result != MA_SUCCESS) { + return result; /* Failed to create the event for this command. */ + } + } + + /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ + ma_mutex_lock(&pContext->wasapi.commandLock); + { + ma_uint32 index; + + /* Spin until we've got some space available. */ + while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { + ma_yield(); + } + + /* Space is now available. Can safely add to the list. */ + index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); + pContext->wasapi.commands[index] = *pCmd; + pContext->wasapi.commands[index].pEvent = &localEvent; + pContext->wasapi.commandCount += 1; + + /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ + ma_semaphore_release(&pContext->wasapi.commandSem); + } + ma_mutex_unlock(&pContext->wasapi.commandLock); + + if (isUsingLocalEvent) { + ma_event_wait(&localEvent); + ma_event_uninit(&localEvent); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) +{ + ma_result result = MA_SUCCESS; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCmd != NULL); + + result = ma_semaphore_wait(&pContext->wasapi.commandSem); + if (result == MA_SUCCESS) { + ma_mutex_lock(&pContext->wasapi.commandLock); + { + *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; + pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); + pContext->wasapi.commandCount -= 1; + } + ma_mutex_unlock(&pContext->wasapi.commandLock); + } + + return result; +} + +static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) +{ + ma_result result; + ma_context* pContext = (ma_context*)pUserData; + MA_ASSERT(pContext != NULL); + + for (;;) { + ma_context_command__wasapi cmd; + result = ma_context_next_command__wasapi(pContext, &cmd); + if (result != MA_SUCCESS) { + break; + } + + switch (cmd.code) + { + case MA_CONTEXT_COMMAND_QUIT__WASAPI: + { + /* Do nothing. Handled after the switch. */ + } break; + + case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: + { + if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); + } else { + *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); + } + } break; + + case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: + { + if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { + if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); + cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; + } + } + + if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { + if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); + cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; + } + } + } break; + + default: + { + /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ + MA_ASSERT(MA_FALSE); + } break; + } + + if (cmd.pEvent != NULL) { + ma_event_signal(cmd.pEvent); + } + + if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { + break; /* Received a quit message. Get out of here. */ + } + } + + return (ma_thread_result)0; +} + +static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) +{ + ma_result result; + ma_result cmdResult; + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); + cmd.data.createAudioClient.deviceType = deviceType; + cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; + cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; + cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ + + result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ + if (result != MA_SUCCESS) { + return result; + } + + return *cmd.data.createAudioClient.pResult; +} + +#if 0 /* Not used at the moment, but leaving here for future use. */ +static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); + cmd.data.releaseAudioClient.pDevice = pDevice; + cmd.data.releaseAudioClient.deviceType = deviceType; + + result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} +#endif + + +static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) +{ + MA_ASSERT(pWF != NULL); + MA_ASSERT(pInfo != NULL); + + if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { + return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ + } + + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; + pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; + pInfo->nativeDataFormatCount += 1; +} + +static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) +{ + HRESULT hr; + MA_WAVEFORMATEX* pWF = NULL; + + MA_ASSERT(pAudioClient != NULL); + MA_ASSERT(pInfo != NULL); + + /* Shared Mode. We use GetMixFormat() here. */ + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); + if (SUCCEEDED(hr)) { + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); + return ma_result_from_HRESULT(hr); + } + + /* + Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on + UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on + out, MA_SUCCESS is guaranteed to be returned. + */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + ma_IPropertyStore *pProperties; + + /* + The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is + correct which will simplify our searching. + */ + hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT var; + ma_PropVariantInit(&var); + + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); + if (SUCCEEDED(hr)) { + pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; + + /* + In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format + first. If this fails, fall back to a search. + */ + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); + if (SUCCEEDED(hr)) { + /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); + } else { + /* + The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel + count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. + */ + ma_uint32 channels = pWF->nChannels; + ma_channel defaultChannelMap[MA_MAX_CHANNELS]; + MA_WAVEFORMATEXTENSIBLE wf; + ma_bool32 found; + ma_uint32 iFormat; + + /* Make sure we don't overflow the channel map. */ + if (channels > MA_MAX_CHANNELS) { + channels = MA_MAX_CHANNELS; + } + + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); + + MA_ZERO_OBJECT(&wf); + wf.cbSize = sizeof(wf); + wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.nChannels = (WORD)channels; + wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); + + found = MA_FALSE; + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { + ma_format format = g_maFormatPriorities[iFormat]; + ma_uint32 iSampleRate; + + wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; + if (format == ma_format_f32) { + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } else { + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } + + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { + wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; + + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); + found = MA_TRUE; + break; + } + } + + if (found) { + break; + } + } + + ma_PropVariantClear(pContext, &var); + + if (!found) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); + } + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); + } + + ma_IPropertyStore_Release(pProperties); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); + } + } + #else + { + (void)pMMDevice; /* Unused. */ + } + #endif + + return MA_SUCCESS; +} + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) +static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) +{ + if (deviceType == ma_device_type_playback) { + return ma_eRender; + } else if (deviceType == ma_device_type_capture) { + return ma_eCapture; + } else { + MA_ASSERT(MA_FALSE); + return ma_eRender; /* Should never hit this. */ + } +} + +static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) +{ + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDeviceEnumerator != NULL); + + *ppDeviceEnumerator = NULL; /* Safety. */ + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + *ppDeviceEnumerator = pDeviceEnumerator; + + return MA_SUCCESS; +} + +static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) +{ + HRESULT hr; + ma_IMMDevice* pMMDefaultDevice = NULL; + WCHAR* pDefaultDeviceID = NULL; + ma_EDataFlow dataFlow; + ma_ERole role; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceEnumerator != NULL); + + (void)pContext; + + /* Grab the EDataFlow type from the device type. */ + dataFlow = ma_device_type_to_EDataFlow(deviceType); + + /* The role is always eConsole, but we may make this configurable later. */ + role = ma_eConsole; + + hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); + if (FAILED(hr)) { + return NULL; + } + + hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); + + ma_IMMDevice_Release(pMMDefaultDevice); + pMMDefaultDevice = NULL; + + if (FAILED(hr)) { + return NULL; + } + + return pDefaultDeviceID; +} + +static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ +{ + ma_result result; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + WCHAR* pDefaultDeviceID = NULL; + + MA_ASSERT(pContext != NULL); + + result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); + if (result != MA_SUCCESS) { + return NULL; + } + + pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + return pDefaultDeviceID; +} + +static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) +{ + ma_IMMDeviceEnumerator* pDeviceEnumerator; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppMMDevice != NULL); + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); + return ma_result_from_HRESULT(hr); + } + + if (pDeviceID == NULL) { + hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); + } else { + hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); + } + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); + return ma_result_from_HRESULT(hr); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) +{ + WCHAR* pDeviceIDString; + HRESULT hr; + + MA_ASSERT(pDeviceID != NULL); + + hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); + if (SUCCEEDED(hr)) { + size_t idlen = ma_strlen_WCHAR(pDeviceIDString); + if (idlen+1 > ma_countof(pDeviceID->wasapi)) { + ma_CoTaskMemFree(pContext, pDeviceIDString); + MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ + return MA_ERROR; + } + + MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); + pDeviceID->wasapi[idlen] = '\0'; + + ma_CoTaskMemFree(pContext, pDeviceIDString); + + return MA_SUCCESS; + } + + return MA_ERROR; +} + +static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pMMDevice != NULL); + MA_ASSERT(pInfo != NULL); + + /* ID. */ + result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); + if (result == MA_SUCCESS) { + if (pDefaultDeviceID != NULL) { + if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { + pInfo->isDefault = MA_TRUE; + } + } + } + + /* Description / Friendly Name */ + { + ma_IPropertyStore *pProperties; + hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT var; + + ma_PropVariantInit(&var); + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); + ma_PropVariantClear(pContext, &var); + } + + ma_IPropertyStore_Release(pProperties); + } + } + + /* Format */ + if (!onlySimpleInfo) { + ma_IAudioClient* pAudioClient; + hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); + if (SUCCEEDED(hr)) { + result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); + + ma_IAudioClient_Release(pAudioClient); + return result; + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); + return ma_result_from_HRESULT(hr); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result = MA_SUCCESS; + UINT deviceCount; + HRESULT hr; + ma_uint32 iDevice; + WCHAR* pDefaultDeviceID = NULL; + ma_IMMDeviceCollection* pDeviceCollection = NULL; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ + pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); + + /* We need to enumerate the devices which returns a device collection. */ + hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); + if (SUCCEEDED(hr)) { + hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); + result = ma_result_from_HRESULT(hr); + goto done; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + ma_IMMDevice* pMMDevice; + + MA_ZERO_OBJECT(&deviceInfo); + + hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); + if (SUCCEEDED(hr)) { + result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ + + ma_IMMDevice_Release(pMMDevice); + if (result == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + break; + } + } + } + } + } + +done: + if (pDefaultDeviceID != NULL) { + ma_CoTaskMemFree(pContext, pDefaultDeviceID); + pDefaultDeviceID = NULL; + } + + if (pDeviceCollection != NULL) { + ma_IMMDeviceCollection_Release(pDeviceCollection); + pDeviceCollection = NULL; + } + + return result; +} + +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppAudioClient != NULL); + MA_ASSERT(ppMMDevice != NULL); + + result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); + if (result != MA_SUCCESS) { + return result; + } + + hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + return MA_SUCCESS; +} +#else +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +{ + ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; + ma_completion_handler_uwp completionHandler; + IID iid; + WCHAR* iidStr; + HRESULT hr; + ma_result result; + HRESULT activateResult; + ma_IUnknown* pActivatedInterface; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppAudioClient != NULL); + + if (pDeviceID != NULL) { + iidStr = (WCHAR*)pDeviceID->wasapi; + } else { + if (deviceType == ma_device_type_capture) { + iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; + } else { + iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; + } + + #if defined(__cplusplus) + hr = StringFromIID(iid, &iidStr); + #else + hr = StringFromIID(&iid, &iidStr); + #endif + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); + return ma_result_from_HRESULT(hr); + } + } + + result = ma_completion_handler_uwp_init(&completionHandler); + if (result != MA_SUCCESS) { + ma_CoTaskMemFree(pContext, iidStr); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); + return result; + } + + hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); + if (FAILED(hr)) { + ma_completion_handler_uwp_uninit(&completionHandler); + ma_CoTaskMemFree(pContext, iidStr); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); + return ma_result_from_HRESULT(hr); + } + + if (pDeviceID == NULL) { + ma_CoTaskMemFree(pContext, iidStr); + } + + /* Wait for the async operation for finish. */ + ma_completion_handler_uwp_wait(&completionHandler); + ma_completion_handler_uwp_uninit(&completionHandler); + + hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); + ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); + + if (FAILED(hr) || FAILED(activateResult)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); + return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); + } + + /* Here is where we grab the IAudioClient interface. */ + hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); + return ma_result_from_HRESULT(hr); + } + + if (ppActivatedInterface) { + *ppActivatedInterface = pActivatedInterface; + } else { + ma_IUnknown_Release(pActivatedInterface); + } + + return MA_SUCCESS; +} +#endif + + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ +typedef enum +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, + MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK +} MA_AUDIOCLIENT_ACTIVATION_TYPE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ +typedef enum +{ + MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, + MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE +} MA_PROCESS_LOOPBACK_MODE; + +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ +typedef struct +{ + DWORD TargetProcessId; + MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; +} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; + +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ +typedef struct +{ + MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; + union + { + MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; + }; +} MA_AUDIOCLIENT_ACTIVATION_PARAMS; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" + +static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) +{ + ma_result result; + ma_bool32 usingProcessLoopback = MA_FALSE; + MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; + MA_PROPVARIANT activationParams; + MA_PROPVARIANT* pActivationParams = NULL; + ma_device_id virtualDeviceID; + + /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ + if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { + usingProcessLoopback = MA_TRUE; + } + + if (usingProcessLoopback) { + MA_ZERO_OBJECT(&audioclientActivationParams); + audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; + audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; + + ma_PropVariantInit(&activationParams); + activationParams.vt = MA_VT_BLOB; + activationParams.blob.cbSize = sizeof(audioclientActivationParams); + activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; + pActivationParams = &activationParams; + + /* When requesting a specific device ID we need to use a special device ID. */ + MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ + pDeviceID = &virtualDeviceID; + } else { + pActivationParams = NULL; /* No activation parameters required. */ + } + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#else + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); +#endif + + /* + If loopback mode was requested with a process ID and initialization failed, it could be because it's + trying to run on an older version of Windows where it's not supported. We need to let the caller + know about this with a log message. + */ + if (result != MA_SUCCESS) { + if (usingProcessLoopback) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); + } + } + + return result; +} + + +static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + /* Different enumeration for desktop and UWP. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* Desktop */ + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; + + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); + ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); + + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); +#else + /* + UWP + + The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate + over devices without using MMDevice, I'm restricting devices to defaults. + + Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ + */ + if (callback) { + ma_bool32 cbResult = MA_TRUE; + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + ma_result result; + ma_IMMDevice* pMMDevice = NULL; + WCHAR* pDefaultDeviceID = NULL; + + result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); + if (result != MA_SUCCESS) { + return result; + } + + /* We need the default device ID so we can set the isDefault flag in the device info. */ + pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); + + result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ + + if (pDefaultDeviceID != NULL) { + ma_CoTaskMemFree(pContext, pDefaultDeviceID); + pDefaultDeviceID = NULL; + } + + ma_IMMDevice_Release(pMMDevice); + + return result; +#else + ma_IAudioClient* pAudioClient; + ma_result result; + + /* UWP currently only uses default devices. */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); + + pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ + + ma_IAudioClient_Release(pAudioClient); + return result; +#endif +} + +static ma_result ma_device_uninit__wasapi(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pDevice->wasapi.pDeviceEnumerator) { + ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); + ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); + } +#endif + + if (pDevice->wasapi.pRenderClient) { + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + } + if (pDevice->wasapi.pCaptureClient) { + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + } + + if (pDevice->wasapi.pAudioClientPlayback) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + } + if (pDevice->wasapi.pAudioClientCapture) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + } + + if (pDevice->wasapi.hEventPlayback) { + CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); + } + if (pDevice->wasapi.hEventCapture) { + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + } + + return MA_SUCCESS; +} + + +typedef struct +{ + /* Input. */ + ma_format formatIn; + ma_uint32 channelsIn; + ma_uint32 sampleRateIn; + ma_channel channelMapIn[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesIn; + ma_uint32 periodSizeInMillisecondsIn; + ma_uint32 periodsIn; + ma_share_mode shareMode; + ma_performance_profile performanceProfile; + ma_bool32 noAutoConvertSRC; + ma_bool32 noDefaultQualitySRC; + ma_bool32 noHardwareOffloading; + ma_uint32 loopbackProcessID; + ma_bool32 loopbackProcessExclude; + + /* Output. */ + ma_IAudioClient* pAudioClient; + ma_IAudioRenderClient* pRenderClient; + ma_IAudioCaptureClient* pCaptureClient; + ma_format formatOut; + ma_uint32 channelsOut; + ma_uint32 sampleRateOut; + ma_channel channelMapOut[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesOut; + ma_uint32 periodsOut; + ma_bool32 usingAudioClient3; + char deviceName[256]; + ma_device_id id; +} ma_device_init_internal_data__wasapi; + +static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) +{ + HRESULT hr; + ma_result result = MA_SUCCESS; + const char* errorMsg = ""; + MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; + DWORD streamFlags = 0; + MA_REFERENCE_TIME periodDurationInMicroseconds; + ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; + MA_WAVEFORMATEXTENSIBLE wf; + ma_WASAPIDeviceInterface* pDeviceInterface = NULL; + ma_IAudioClient2* pAudioClient2; + ma_uint32 nativeSampleRate; + ma_bool32 usingProcessLoopback = MA_FALSE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pData != NULL); + + /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; + + pData->pAudioClient = NULL; + pData->pRenderClient = NULL; + pData->pCaptureClient = NULL; + + streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ + streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; + } + if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { + streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + } + if (deviceType == ma_device_type_loopback) { + streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; + } + + result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); + if (result != MA_SUCCESS) { + goto done; + } + + MA_ZERO_OBJECT(&wf); + + /* Try enabling hardware offloading. */ + if (!pData->noHardwareOffloading) { + hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); + if (SUCCEEDED(hr)) { + BOOL isHardwareOffloadingSupported = 0; + hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); + if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { + ma_AudioClientProperties clientProperties; + MA_ZERO_OBJECT(&clientProperties); + clientProperties.cbSize = sizeof(clientProperties); + clientProperties.bIsOffload = 1; + clientProperties.eCategory = MA_AudioCategory_Other; + ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); + } + + pAudioClient2->lpVtbl->Release(pAudioClient2); + } + } + + /* 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 = MA_FORMAT_NOT_SUPPORTED; + if (pData->shareMode == ma_share_mode_exclusive) { + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + /* In exclusive mode on desktop we always use the backend's native format. */ + ma_IPropertyStore* pStore = NULL; + hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT prop; + ma_PropVariantInit(&prop); + hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); + if (SUCCEEDED(hr)) { + MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); + if (SUCCEEDED(hr)) { + MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } + + ma_PropVariantClear(pContext, &prop); + } + + ma_IPropertyStore_Release(pStore); + } + #else + /* + 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) { + shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; + result = MA_SUCCESS; + } else { + result = MA_SHARE_MODE_NOT_SUPPORTED; + } + } else { + /* In shared mode we are always using the format reported by the operating system. */ + MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); + if (hr != S_OK) { + /* When using process-specific loopback, GetMixFormat() seems to always fail. */ + if (usingProcessLoopback) { + wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + wf.nChannels = 2; + wf.nSamplesPerSec = 44100; + wf.wBitsPerSample = 32; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = sizeof(MA_WAVEFORMATEX); + + result = MA_SUCCESS; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } + } else { + /* + I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself + is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE + want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be + safe and only copy the WAVEFORMATEX part. + */ + if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } else { + /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ + size_t cbSize = pNativeFormat->cbSize; + if (cbSize == 0) { + cbSize = sizeof(MA_WAVEFORMATEX); + } + + /* Make sure we don't copy more than the capacity of `wf`. */ + if (cbSize > sizeof(wf)) { + cbSize = sizeof(wf); + } + + MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); + } + + result = MA_SUCCESS; + } + + ma_CoTaskMemFree(pContext, pNativeFormat); + + shareMode = MA_AUDCLNT_SHAREMODE_SHARED; + } + + /* Return an error if we still haven't found a format. */ + if (result != MA_SUCCESS) { + errorMsg = "[WASAPI] Failed to find best device mix format."; + goto done; + } + + /* + Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use + WASAPI to perform the sample rate conversion. + */ + nativeSampleRate = wf.nSamplesPerSec; + if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { + wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + } + + pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); + if (pData->formatOut == ma_format_unknown) { + /* + The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED + in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for + completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. + */ + if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { + result = MA_SHARE_MODE_NOT_SUPPORTED; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } + + errorMsg = "[WASAPI] Native format not supported."; + goto done; + } + + pData->channelsOut = wf.nChannels; + pData->sampleRateOut = wf.nSamplesPerSec; + + /* + Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns + a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this + case we'll just use the default channel map. + */ + if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + } + + /* Period size. */ + pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; + pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; + if (pData->periodSizeInFramesOut == 0) { + if (pData->periodSizeInMillisecondsIn == 0) { + if (pData->performanceProfile == ma_performance_profile_low_latency) { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); + } + } else { + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); + } + } + + periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; + + + /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ + if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { + MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; + + /* + If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing + it and trying it again. + */ + hr = E_FAIL; + for (;;) { + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); + if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { + if (bufferDuration > 500*10000) { + break; + } else { + if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ + break; + } + + bufferDuration = bufferDuration * 2; + continue; + } + } else { + break; + } + } + + if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { + ma_uint32 bufferSizeInFrames; + hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); + if (SUCCEEDED(hr)) { + bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.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?! */ + ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); + + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); + #else + hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); + #endif + + if (SUCCEEDED(hr)) { + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); + } + } + } + + if (FAILED(hr)) { + /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; + } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); + } + goto done; + } + } + + if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { + /* + Low latency shared mode via IAudioClient3. + + NOTE + ==== + Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the + use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using + any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to + that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. + */ + #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE + { + if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { + ma_IAudioClient3* pAudioClient3 = NULL; + hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); + if (SUCCEEDED(hr)) { + ma_uint32 defaultPeriodInFrames; + ma_uint32 fundamentalPeriodInFrames; + ma_uint32 minPeriodInFrames; + ma_uint32 maxPeriodInFrames; + hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + if (SUCCEEDED(hr)) { + ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; + ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; + + /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ + actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; + actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; + + /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ + actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); + + /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ + if (actualPeriodInFrames >= desiredPeriodInFrames) { + /* + MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, + IAudioClient3_InitializeSharedAudioStream() will fail. + */ + hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); + if (SUCCEEDED(hr)) { + wasInitializedUsingIAudioClient3 = MA_TRUE; + pData->periodSizeInFramesOut = actualPeriodInFrames; + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); + } + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); + } + + ma_IAudioClient3_Release(pAudioClient3); + pAudioClient3 = NULL; + } + } + } + #else + { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); + } + #endif + + /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ + if (!wasInitializedUsingIAudioClient3) { + MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); + if (FAILED(hr)) { + if (hr == E_ACCESSDENIED) { + errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; + } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { + errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; + } else { + errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); + } + + goto done; + } + } + } + + if (!wasInitializedUsingIAudioClient3) { + ma_uint32 bufferSizeInFrames = 0; + hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); + if (FAILED(hr)) { + errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); + goto done; + } + + /* + When using process loopback mode, retrieval of the buffer size seems to result in totally + incorrect values. In this case we'll just assume it's the same size as what we requested + when we initialized the client. + */ + if (usingProcessLoopback) { + bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); + } + + pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; + } + + pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; + + + if (deviceType == ma_device_type_playback) { + result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); + } else { + result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); + } + + /*if (FAILED(hr)) {*/ + if (result != MA_SUCCESS) { + errorMsg = "[WASAPI] Failed to get audio client service."; + goto done; + } + + + /* Grab the name of the device. */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + ma_IPropertyStore *pProperties; + hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); + if (SUCCEEDED(hr)) { + MA_PROPVARIANT varName; + ma_PropVariantInit(&varName); + hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); + if (SUCCEEDED(hr)) { + WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); + ma_PropVariantClear(pContext, &varName); + } + + ma_IPropertyStore_Release(pProperties); + } + } + #endif + + /* + For the WASAPI backend we need to know the actual IDs of the device in order to do automatic + stream routing so that IDs can be compared and we can determine which device has been detached + and whether or not it matches with our ma_device. + */ + #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + { + /* Desktop */ + ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); + } + #else + { + /* UWP */ + /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ + } + #endif + +done: + /* Clean up. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pDeviceInterface != NULL) { + ma_IMMDevice_Release(pDeviceInterface); + } +#else + if (pDeviceInterface != NULL) { + ma_IUnknown_Release(pDeviceInterface); + } +#endif + + if (result != MA_SUCCESS) { + if (pData->pRenderClient) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); + pData->pRenderClient = NULL; + } + if (pData->pCaptureClient) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); + pData->pCaptureClient = NULL; + } + if (pData->pAudioClient) { + ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); + pData->pAudioClient = NULL; + } + + if (errorMsg != NULL && errorMsg[0] != '\0') { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); + } + + return result; + } else { + return MA_SUCCESS; + } +} + +static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_device_init_internal_data__wasapi data; + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* We only re-initialize the playback or capture device. Never a full-duplex device. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + + /* + Before reinitializing the device we need to free the previous audio clients. + + There's a known memory leak here. We will be calling this from the routing change callback that + is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion + this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably + need some system where we post an event, but delay the execution of it until the callback has + returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for + a command thread which might be useful for this. + */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + if (pDevice->wasapi.pCaptureClient) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + + if (pDevice->wasapi.pAudioClientCapture) { + /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ + pDevice->wasapi.pAudioClientCapture = NULL; + } + } + + if (deviceType == ma_device_type_playback) { + if (pDevice->wasapi.pRenderClient) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + + if (pDevice->wasapi.pAudioClientPlayback) { + /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ + pDevice->wasapi.pAudioClientPlayback = NULL; + } + } + + + if (deviceType == ma_device_type_playback) { + data.formatIn = pDevice->playback.format; + data.channelsIn = pDevice->playback.channels; + MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); + data.shareMode = pDevice->playback.shareMode; + } else { + data.formatIn = pDevice->capture.format; + data.channelsIn = pDevice->capture.channels; + MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); + data.shareMode = pDevice->capture.shareMode; + } + + data.sampleRateIn = pDevice->sampleRate; + data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; + data.periodsIn = pDevice->wasapi.originalPeriods; + data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; + data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; + data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; + result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); + if (result != MA_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 (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + pDevice->wasapi.pAudioClientCapture = data.pAudioClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + + pDevice->capture.internalFormat = data.formatOut; + pDevice->capture.internalChannels = data.channelsOut; + pDevice->capture.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->capture.internalPeriods = data.periodsOut; + ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); + + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + + pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + + /* We must always have a valid ID. */ + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + } + + if (deviceType == ma_device_type_playback) { + pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + + pDevice->playback.internalFormat = data.formatOut; + pDevice->playback.internalChannels = data.channelsOut; + pDevice->playback.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->playback.internalPeriods = data.periodsOut; + ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); + + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + + pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + + /* We must always have a valid ID because rerouting will look at it. */ + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result = MA_SUCCESS; + +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + HRESULT hr; + ma_IMMDeviceEnumerator* pDeviceEnumerator; +#endif + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->wasapi); + pDevice->wasapi.usage = pConfig->wasapi.usage; + pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + /* Exclusive mode is not allowed with loopback. */ + if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { + return MA_INVALID_DEVICE_CONFIG; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + ma_device_init_internal_data__wasapi data; + data.formatIn = pDescriptorCapture->format; + data.channelsIn = pDescriptorCapture->channels; + data.sampleRateIn = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; + data.periodsIn = pDescriptorCapture->periodCount; + data.shareMode = pDescriptorCapture->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); + if (result != MA_SUCCESS) { + return result; + } + + pDevice->wasapi.pAudioClientCapture = data.pAudioClient; + pDevice->wasapi.pCaptureClient = data.pCaptureClient; + pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + + /* + The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, + however, because we want to block until we actually have something for the first call to ma_device_read(). + */ + pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + if (pDevice->wasapi.hEventCapture == NULL) { + result = ma_result_from_GetLastError(GetLastError()); + + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); + return result; + } + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); + + pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); + + /* We must always have a valid ID. */ + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + + /* The descriptor needs to be updated with actual values. */ + pDescriptorCapture->format = data.formatOut; + pDescriptorCapture->channels = data.channelsOut; + pDescriptorCapture->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorCapture->periodCount = data.periodsOut; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__wasapi data; + data.formatIn = pDescriptorPlayback->format; + data.channelsIn = pDescriptorPlayback->channels; + data.sampleRateIn = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; + data.periodsIn = pDescriptorPlayback->periodCount; + data.shareMode = pDescriptorPlayback->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; + data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; + data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; + data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; + data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; + + result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + pDevice->wasapi.hEventCapture = NULL; + } + return result; + } + + pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; + pDevice->wasapi.pRenderClient = data.pRenderClient; + pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; + pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; + + /* + The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled + only after the whole available space has been filled, never before. + + The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able + to get passed WaitForMultipleObjects(). + */ + pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + if (pDevice->wasapi.hEventPlayback == NULL) { + result = ma_result_from_GetLastError(GetLastError()); + + if (pConfig->deviceType == ma_device_type_duplex) { + if (pDevice->wasapi.pCaptureClient != NULL) { + ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); + pDevice->wasapi.pCaptureClient = NULL; + } + if (pDevice->wasapi.pAudioClientCapture != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + pDevice->wasapi.pAudioClientCapture = NULL; + } + + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); + pDevice->wasapi.hEventCapture = NULL; + } + + if (pDevice->wasapi.pRenderClient != NULL) { + ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); + pDevice->wasapi.pRenderClient = NULL; + } + if (pDevice->wasapi.pAudioClientPlayback != NULL) { + ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + pDevice->wasapi.pAudioClientPlayback = NULL; + } + + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); + return result; + } + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); + + pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; + ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); + + /* We must always have a valid ID because rerouting will look at it. */ + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + + /* The descriptor needs to be updated with actual values. */ + pDescriptorPlayback->format = data.formatOut; + pDescriptorPlayback->channels = data.channelsOut; + pDescriptorPlayback->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorPlayback->periodCount = data.periodsOut; + } + + /* + We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When + we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just + stop the device outright and let the application handle it. + */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { + if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) { + pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; + } + if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { + pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; + } + } + + ma_mutex_init(&pDevice->wasapi.rerouteLock); + + hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + if (FAILED(hr)) { + ma_device_uninit__wasapi(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); + return ma_result_from_HRESULT(hr); + } + + pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; + pDevice->wasapi.notificationClient.counter = 1; + pDevice->wasapi.notificationClient.pDevice = pDevice; + + hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); + if (SUCCEEDED(hr)) { + pDevice->wasapi.pDeviceEnumerator = (ma_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. */ + ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); + } +#endif + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + + return MA_SUCCESS; +} + +static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) +{ + ma_uint32 paddingFramesCount; + HRESULT hr; + ma_share_mode shareMode; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFrameCount != NULL); + + *pFrameCount = 0; + + if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { + return MA_INVALID_OPERATION; + } + + /* + I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing + higher level function calls from doing anything because it thinks nothing is available. I have + taken a look at the documentation and it looks like this is unnecessary in exclusive mode. + + From Microsoft's documentation: + + For an exclusive-mode rendering or capture stream that was initialized with the + AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding + value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during + each processing pass. + + Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the + entire buffer. This depends on the caller making sure they wait on the event handler. + */ + shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; + if (shareMode == ma_share_mode_shared) { + /* Shared mode. */ + hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; + } else { + *pFrameCount = paddingFramesCount; + } + } else { + /* Exclusive mode. */ + if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; + } else { + *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); + + result = ma_device_reinit__wasapi(pDevice, deviceType); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); + return result; + } + + ma_device__post_init_setup(pDevice, deviceType); + ma_device__on_notification_rerouted(pDevice); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n"); + + return MA_SUCCESS; +} + +static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) +{ + HRESULT hr; + + if (pDevice->pContext->wasapi.hAvrt) { + const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + if (pTaskName) { + DWORD idx = 0; + pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); + } + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); + return ma_result_from_HRESULT(hr); + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); + return ma_result_from_HRESULT(hr); + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to start the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_start__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + +static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->wasapi.hAvrtHandle) { + ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); + pDevice->wasapi.hAvrtHandle = NULL; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); + return ma_result_from_HRESULT(hr); + } + + /* The audio client needs to be reset otherwise restarting will fail. */ + hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); + return ma_result_from_HRESULT(hr); + } + + /* If we have a mapped buffer we need to release it. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to + the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. + */ + if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) { + /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ + DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; + + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); + } + else { + ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; + ma_uint32 framesAvailablePlayback; + for (;;) { + result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); + if (result != MA_SUCCESS) { + break; + } + + if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { + break; + } + + /* + Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames + has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. + */ + if (framesAvailablePlayback == prevFramesAvaialablePlayback) { + break; + } + prevFramesAvaialablePlayback = framesAvailablePlayback; + + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); + ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ + } + } + } + + hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); + return ma_result_from_HRESULT(hr); + } + + /* The audio client needs to be reset otherwise restarting will fail. */ + hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); + return ma_result_from_HRESULT(hr); + } + + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } + + ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__wasapi(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* Wait for any rerouting to finish before attempting to stop the device. */ + ma_mutex_lock(&pDevice->wasapi.rerouteLock); + { + result = ma_device_stop__wasapi_nolock(pDevice); + } + ma_mutex_unlock(&pDevice->wasapi.rerouteLock); + + return result; +} + + +#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS +#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 +#endif + +static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; + + /* + When reading, we need to get a buffer and process all of it before releasing it. Because the + frame count (frameCount) can be different to the size of the buffer, we'll need to cache the + pointer to the buffer. + */ + + /* Keep running until we've processed the requested number of frames. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; + + /* If we have a mapped data buffer, consume that first. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { + framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; + } + + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), + framesToProcessNow, + pDevice->capture.internalFormat, pDevice->capture.internalChannels + ); + + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; + + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferCaptureLen == 0) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + } + } else { + /* We don't have any cached data pointer, so grab another one. */ + HRESULT hr; + DWORD flags = 0; + + /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == S_OK) { + /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + + /* + There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every + call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially + work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution + would be to figure out why the flag is always getting reported. + */ + #if defined(MA_DEBUG_OUTPUT) + { + if (flags != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); + + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); + } + } + } + #endif + + /* Overrun detection. */ + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + /* Glitched. Probably due to an overrun. */ + + /* + If we got an overrun it probably means we're straddling the end of the buffer. In normal capture + mode this is the fault of the client application because they're responsible for ensuring data is + processed fast enough. In duplex mode, however, the processing of audio is tied to the playback + device, so this can possibly be the result of a timing de-sync. + + In capture mode we're not going to do any kind of recovery because the real fix is for the client + application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers + to prevent a never-ending sequence of glitches due to straddling the end of the buffer. + */ + if (pDevice->type == ma_device_type_duplex) { + /* + Experiment: + + If we empty out the *entire* buffer we may end up putting ourselves into an underrun position + which isn't really any better than the overrun we're probably in right now. Instead we'll just + empty out about half. + */ + ma_uint32 i; + ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); + ma_uint32 iterationCount = periodCount / 2; + if ((periodCount % 2) > 0) { + iterationCount += 1; + } + + for (i = 0; i < iterationCount; i += 1) { + hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr); + break; + } + + flags = 0; + hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { + /* + The buffer has been completely emptied or an error occurred. In this case we'll need + to reset the state of the mapped buffer which will trigger the next iteration to get + a fresh buffer from WASAPI. + */ + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { + if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); + } + } + + if (FAILED(hr)) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr); + } + + break; + } + } + + /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ + if (pDevice->wasapi.pMappedBufferCapture != NULL) { + pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; + } + } + } + + continue; + } else { + if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* + No data is available. We need to wait for more. There's two situations to consider + here. The first is normal capture mode. If this times out it probably means the + microphone isn't delivering data for whatever reason. In this case we'll just + abort the read and return whatever we were able to get. The other situations is + loopback mode, in which case a timeout probably just means the nothing is playing + through the speakers. + */ + + /* Experiment: Use a shorter timeout for loopback mode. */ + DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; + if (pDevice->type == ma_device_type_loopback) { + timeoutInMilliseconds = 10; + } + + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { + if (pDevice->type == ma_device_type_loopback) { + continue; /* Keep waiting in loopback mode. */ + } else { + result = MA_ERROR; + break; /* Wait failed. */ + } + } + + /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ + } else { + /* An error occurred and we need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); + break; + } + } + } + } + + /* + If we were unable to process the entire requested frame count, but we still have a mapped buffer, + there's a good chance either an error occurred or the device was stopped mid-read. In this case + we'll need to make sure the buffer is released. + */ + if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { + ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); + pDevice->wasapi.pMappedBufferCapture = NULL; + pDevice->wasapi.mappedBufferCaptureCap = 0; + pDevice->wasapi.mappedBufferCaptureLen = 0; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + return result; +} + +static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + ma_uint32 totalFramesProcessed = 0; + + /* Keep writing to the device until it's stopped or we've consumed all of our input. */ + while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesProcessed; + + /* + We're going to do this in a similar way to capture. We'll first check if the cached data pointer + is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with + a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE + it means we need to wait for some data to become available. + */ + if (pDevice->wasapi.pMappedBufferPlayback != NULL) { + /* We still have some space available in the mapped data buffer. Write to it. */ + ma_uint32 framesToProcessNow = framesRemaining; + if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { + framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); + } + + /* Now just copy the data over to the output buffer. */ + ma_copy_pcm_frames( + ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), + framesToProcessNow, + pDevice->playback.internalFormat, pDevice->playback.internalChannels + ); + + totalFramesProcessed += framesToProcessNow; + pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; + + /* If the data buffer has been fully consumed we need to release it. */ + if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { + ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); + pDevice->wasapi.pMappedBufferPlayback = NULL; + pDevice->wasapi.mappedBufferPlaybackCap = 0; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + + /* + In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never + seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine + whether or not we need to wait for more data. + */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } + } + } else { + /* We don't have a mapped data buffer so we'll need to get one. */ + HRESULT hr; + ma_uint32 bufferSizeInFrames; + + /* Special rules for exclusive mode. */ + if (pDevice->playback.shareMode == ma_share_mode_exclusive) { + bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; + } else { + bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; + } + + hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); + if (hr == S_OK) { + /* We have data available. */ + pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; + pDevice->wasapi.mappedBufferPlaybackLen = 0; + } else { + if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { + /* Not enough data available. We need to wait for more. */ + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; /* Wait failed. Probably timed out. */ + } + } else { + /* Some error occurred. We'll need to abort. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); + result = ma_result_from_HRESULT(hr); + break; + } + } + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalFramesProcessed; + } + + return result; +} + +static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + SetEvent((HANDLE)pDevice->wasapi.hEventCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__wasapi(ma_context* pContext) +{ + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_wasapi); + + ma_context_post_command__wasapi(pContext, &cmd); + ma_thread_wait(&pContext->wasapi.commandThread); + + if (pContext->wasapi.hAvrt) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result = MA_SUCCESS; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + +#ifdef MA_WIN32_DESKTOP + /* + WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven + exclusive mode does not work until SP1. + + Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. + */ + { + ma_OSVERSIONINFOEXW osvi; + ma_handle kernel32DLL; + ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; + ma_PFNVerSetConditionMask _VerSetConditionMask; + + kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); + if (kernel32DLL == NULL) { + return MA_NO_BACKEND; + } + + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); + if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + return MA_NO_BACKEND; + } + + MA_ZERO_OBJECT(&osvi); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); + osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); + osvi.wServicePackMajor = 1; + if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { + result = MA_SUCCESS; + } else { + result = MA_NO_BACKEND; + } + + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); + } +#endif + + if (result != MA_SUCCESS) { + return result; + } + + MA_ZERO_OBJECT(&pContext->wasapi); + + + #if defined(MA_WIN32_UWP) + { + /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ + pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); + if (pContext->wasapi.hMMDevapi) { + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); + return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ + } + } else { + return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ + } + } + #endif + + /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ + pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); + if (pContext->wasapi.hAvrt) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + + /* If either function could not be found, disable use of avrt entirely. */ + if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; + pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; + } + } + + + /* + Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread + than the one that retrieved it with GetService(). This can result in a deadlock in two + situations: + + 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and + 2) When uninitializing and reinitializing the internal IAudioClient object in response to + automatic stream routing. + + We could define ma_device_uninit() such that it must be called on the same thread as + ma_device_init(). We could also just not release the IAudioClient when performing automatic + stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so + we're going to have to work around this with a worker thread. This is not ideal, but I can't + think of a better way to do this. + + More information about this can be found here: + + https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient + + Note this section: + + When releasing an IAudioRenderClient interface instance, the client must call the interface's + Release method from the same thread as the call to IAudioClient::GetService that created the + object. + */ + { + result = ma_mutex_init(&pContext->wasapi.commandLock); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_semaphore_init(0, &pContext->wasapi.commandSem); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pContext->wasapi.commandLock); + return result; + } + + result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return result; + } + } + + + pCallbacks->onContextInit = ma_context_init__wasapi; + pCallbacks->onContextUninit = ma_context_uninit__wasapi; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; + pCallbacks->onDeviceInit = ma_device_init__wasapi; + pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; + pCallbacks->onDeviceStart = ma_device_start__wasapi; + pCallbacks->onDeviceStop = ma_device_stop__wasapi; + pCallbacks->onDeviceRead = ma_device_read__wasapi; + pCallbacks->onDeviceWrite = ma_device_write__wasapi; + pCallbacks->onDeviceDataLoop = NULL; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; + + return MA_SUCCESS; +} +#endif + +/****************************************************************************** + +DirectSound Backend + +******************************************************************************/ +#ifdef MA_HAS_DSOUND +/*#include */ + +/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ + +/* miniaudio only uses priority or exclusive modes. */ +#define MA_DSSCL_NORMAL 1 +#define MA_DSSCL_PRIORITY 2 +#define MA_DSSCL_EXCLUSIVE 3 +#define MA_DSSCL_WRITEPRIMARY 4 + +#define MA_DSCAPS_PRIMARYMONO 0x00000001 +#define MA_DSCAPS_PRIMARYSTEREO 0x00000002 +#define MA_DSCAPS_PRIMARY8BIT 0x00000004 +#define MA_DSCAPS_PRIMARY16BIT 0x00000008 +#define MA_DSCAPS_CONTINUOUSRATE 0x00000010 +#define MA_DSCAPS_EMULDRIVER 0x00000020 +#define MA_DSCAPS_CERTIFIED 0x00000040 +#define MA_DSCAPS_SECONDARYMONO 0x00000100 +#define MA_DSCAPS_SECONDARYSTEREO 0x00000200 +#define MA_DSCAPS_SECONDARY8BIT 0x00000400 +#define MA_DSCAPS_SECONDARY16BIT 0x00000800 + +#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 +#define MA_DSBCAPS_STATIC 0x00000002 +#define MA_DSBCAPS_LOCHARDWARE 0x00000004 +#define MA_DSBCAPS_LOCSOFTWARE 0x00000008 +#define MA_DSBCAPS_CTRL3D 0x00000010 +#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 +#define MA_DSBCAPS_CTRLPAN 0x00000040 +#define MA_DSBCAPS_CTRLVOLUME 0x00000080 +#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define MA_DSBCAPS_CTRLFX 0x00000200 +#define MA_DSBCAPS_STICKYFOCUS 0x00004000 +#define MA_DSBCAPS_GLOBALFOCUS 0x00008000 +#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 +#define MA_DSBCAPS_LOCDEFER 0x00040000 +#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 + +#define MA_DSBPLAY_LOOPING 0x00000001 +#define MA_DSBPLAY_LOCHARDWARE 0x00000002 +#define MA_DSBPLAY_LOCSOFTWARE 0x00000004 +#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 +#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 +#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 + +#define MA_DSCBSTART_LOOPING 0x00000001 + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + MA_WAVEFORMATEX* lpwfxFormat; + GUID guid3DAlgorithm; +} MA_DSBUFFERDESC; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + MA_WAVEFORMATEX* lpwfxFormat; + DWORD dwFXCount; + void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ +} MA_DSCBUFFERDESC; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} MA_DSCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} MA_DSBCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFormats; + DWORD dwChannels; +} MA_DSCCAPS; + +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; +} MA_DSCBCAPS; + +typedef struct +{ + DWORD dwOffset; + HANDLE hEventNotify; +} MA_DSBPOSITIONNOTIFY; + +typedef struct ma_IDirectSound ma_IDirectSound; +typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; +typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; +typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; +typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; + + +/* +COM objects. The way these work is that you have a vtable (a list of function pointers, kind of +like how C++ works internally), and then you have a structure with a single member, which is a +pointer to the vtable. The vtable is where the methods of the object are defined. Methods need +to be in a specific order, and parent classes need to have their methods declared first. +*/ + +/* IDirectSound */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); + + /* IDirectSound */ + HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); + HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); + HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); + HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); + HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); +} ma_IDirectSoundVtbl; +struct ma_IDirectSound +{ + ma_IDirectSoundVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } +static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } +static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } +static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } +static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } +static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } +static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } +static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +/* IDirectSoundBuffer */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); + + /* IDirectSoundBuffer */ + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); + HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); + HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); + HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); + HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); + HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); + HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); + HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); + HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); +} ma_IDirectSoundBufferVtbl; +struct ma_IDirectSoundBuffer +{ + ma_IDirectSoundBufferVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } + + +/* IDirectSoundCapture */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); + + /* IDirectSoundCapture */ + HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); +} ma_IDirectSoundCaptureVtbl; +struct ma_IDirectSoundCapture +{ + ma_IDirectSoundCaptureVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } +static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } + + +/* IDirectSoundCaptureBuffer */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); + + /* IDirectSoundCaptureBuffer */ + HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); + HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); + HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); + HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); + HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); +} ma_IDirectSoundCaptureBufferVtbl; +struct ma_IDirectSoundCaptureBuffer +{ + ma_IDirectSoundCaptureBufferVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } + + +/* IDirectSoundNotify */ +typedef struct +{ + /* IUnknown */ + HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); + ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); + ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); + + /* IDirectSoundNotify */ + HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); +} ma_IDirectSoundNotifyVtbl; +struct ma_IDirectSoundNotify +{ + ma_IDirectSoundNotifyVtbl* lpVtbl; +}; +static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } +static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } +static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } +static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } + + +typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); + +static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) +{ + /* Normalize the range in case we were given something stupid. */ + if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { + sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; + } + if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { + sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; + } + if (sampleRateMin > sampleRateMax) { + sampleRateMin = sampleRateMax; + } + + if (sampleRateMin == sampleRateMax) { + return sampleRateMax; + } else { + size_t iStandardRate; + for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; + if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { + return standardRate; + } + } + } + + /* Should never get here. */ + MA_ASSERT(MA_FALSE); + return 0; +} + +/* +Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, +the channel count and channel map will be left unmodified. +*/ +static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) +{ + WORD channels; + DWORD channelMap; + + channels = 0; + if (pChannelsOut != NULL) { + channels = *pChannelsOut; + } + + channelMap = 0; + if (pChannelMapOut != NULL) { + channelMap = *pChannelMapOut; + } + + /* + The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper + 16 bits is for the geometry. + */ + switch ((BYTE)(speakerConfig)) { + case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; + case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; + case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; + case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; + case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; + case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; + default: break; + } + + if (pChannelsOut != NULL) { + *pChannelsOut = channels; + } + + if (pChannelMapOut != NULL) { + *pChannelMapOut = channelMap; + } +} + + +static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) +{ + ma_IDirectSound* pDirectSound; + HWND hWnd; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDirectSound != NULL); + + *ppDirectSound = NULL; + pDirectSound = NULL; + + if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* The cooperative level must be set before doing anything else. */ + hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); + if (hWnd == 0) { + hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); + } + + hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + *ppDirectSound = pDirectSound; + return MA_SUCCESS; +} + +static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) +{ + ma_IDirectSoundCapture* pDirectSoundCapture; + HRESULT hr; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppDirectSoundCapture != NULL); + + /* DirectSound does not support exclusive mode for capture. */ + if (shareMode == ma_share_mode_exclusive) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + *ppDirectSoundCapture = NULL; + pDirectSoundCapture = NULL; + + hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + *ppDirectSoundCapture = pDirectSoundCapture; + return MA_SUCCESS; +} + +static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) +{ + HRESULT hr; + MA_DSCCAPS caps; + WORD bitsPerSample; + DWORD sampleRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDirectSoundCapture != NULL); + + if (pChannels) { + *pChannels = 0; + } + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } + + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + if (pChannels) { + *pChannels = (WORD)caps.dwChannels; + } + + /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ + bitsPerSample = 16; + sampleRate = 48000; + + if (caps.dwChannels == 1) { + if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ + } + } + } else if (caps.dwChannels == 2) { + if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_context* pContext; + ma_device_type deviceType; + ma_enum_devices_callback_proc callback; + void* pUserData; + ma_bool32 terminated; +} ma_context_enumerate_devices_callback_data__dsound; + +static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) +{ + ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; + ma_device_info deviceInfo; + + (void)lpcstrModule; + + MA_ZERO_OBJECT(&deviceInfo); + + /* ID. */ + if (lpGuid != NULL) { + MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); + } else { + MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); + deviceInfo.isDefault = MA_TRUE; + } + + /* Name / Description */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); + + + /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ + MA_ASSERT(pData != NULL); + pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); + if (pData->terminated) { + return FALSE; /* Stop enumeration. */ + } else { + return TRUE; /* Continue enumeration. */ + } +} + +static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__dsound data; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + data.pContext = pContext; + data.callback = callback; + data.pUserData = pUserData; + data.terminated = MA_FALSE; + + /* Playback. */ + if (!data.terminated) { + data.deviceType = ma_device_type_playback; + ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + } + + /* Capture. */ + if (!data.terminated) { + data.deviceType = ma_device_type_capture; + ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); + } + + return MA_SUCCESS; +} + + +typedef struct +{ + const ma_device_id* pDeviceID; + ma_device_info* pDeviceInfo; + ma_bool32 found; +} ma_context_get_device_info_callback_data__dsound; + +static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) +{ + ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; + MA_ASSERT(pData != NULL); + + if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { + /* Default device. */ + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->pDeviceInfo->isDefault = MA_TRUE; + pData->found = MA_TRUE; + return FALSE; /* Stop enumeration. */ + } else { + /* Not the default device. */ + if (lpGuid != NULL && pData->pDeviceID != NULL) { + if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); + pData->found = MA_TRUE; + return FALSE; /* Stop enumeration. */ + } + } + } + + (void)lpcstrModule; + return TRUE; +} + +static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + HRESULT hr; + + if (pDeviceID != NULL) { + ma_context_get_device_info_callback_data__dsound data; + + /* ID. */ + MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); + + /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ + data.pDeviceID = pDeviceID; + data.pDeviceInfo = pDeviceInfo; + data.found = MA_FALSE; + if (deviceType == ma_device_type_playback) { + ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + } else { + ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); + } + + if (!data.found) { + return MA_NO_DEVICE; + } + } else { + /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ + + /* ID */ + MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; + } + + /* Retrieving detailed information is slightly different depending on the device type. */ + if (deviceType == ma_device_type_playback) { + /* Playback. */ + ma_IDirectSound* pDirectSound; + MA_DSCAPS caps; + WORD channels; + + result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); + if (result != MA_SUCCESS) { + return result; + } + + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); + if (FAILED(hr)) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + + /* Channels. Only a single channel count is reported for DirectSound. */ + if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { + /* It supports at least stereo, but could support more. */ + DWORD speakerConfig; + + channels = 2; + + /* Look at the speaker configuration to get a better idea on the channel count. */ + hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); + if (SUCCEEDED(hr)) { + ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); + } + } else { + /* It does not support stereo, which means we are stuck with mono. */ + channels = 1; + } + + + /* + In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel + count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio + in order to keep the size of this within reason. + */ + if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { + /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ + size_t iStandardSampleRate; + for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { + ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; + if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + } + } + } else { + /* Only a single sample rate is supported. */ + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + } + + ma_IDirectSound_Release(pDirectSound); + } else { + /* + Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture + devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just + reporting the best format. + */ + ma_IDirectSoundCapture* pDirectSoundCapture; + WORD channels; + WORD bitsPerSample; + DWORD sampleRate; + + result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); + if (result != MA_SUCCESS) { + ma_IDirectSoundCapture_Release(pDirectSoundCapture); + return result; + } + + ma_IDirectSoundCapture_Release(pDirectSoundCapture); + + /* The format is always an integer format and is based on the bits per sample. */ + if (bitsPerSample == 8) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + + pDeviceInfo->nativeDataFormats[0].channels = channels; + pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + } + + return MA_SUCCESS; +} + + + +static ma_result ma_device_uninit__dsound(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->dsound.pCaptureBuffer != NULL) { + ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + } + if (pDevice->dsound.pCapture != NULL) { + ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); + } + + if (pDevice->dsound.pPlaybackBuffer != NULL) { + ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + } + if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { + ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); + } + if (pDevice->dsound.pPlayback != NULL) { + ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) +{ + GUID subformat; + + if (format == ma_format_unknown) { + format = MA_DEFAULT_FORMAT; + } + + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + switch (format) + { + case ma_format_u8: + case ma_format_s16: + case ma_format_s24: + /*case ma_format_s24_32:*/ + case ma_format_s32: + { + subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + } break; + + case ma_format_f32: + { + subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } break; + + default: + return MA_FORMAT_NOT_SUPPORTED; + } + + MA_ZERO_OBJECT(pWF); + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pWF->nChannels = (WORD)channels; + pWF->nSamplesPerSec = (DWORD)sampleRate; + pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; + pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); + pWF->SubFormat = subformat; + + return MA_SUCCESS; +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for + reliable glitch-free processing so going to use 30ms instead. + */ + ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); + ma_uint32 periodSizeInFrames; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + if (periodSizeInFrames < minPeriodSizeInFrames) { + periodSizeInFrames = minPeriodSizeInFrames; + } + + return periodSizeInFrames; +} + +static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + HRESULT hr; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->dsound); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* + Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize + the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using + full-duplex mode. + */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEFORMATEXTENSIBLE wf; + MA_DSCBUFFERDESC descDS; + ma_uint32 periodSizeInFrames; + ma_uint32 periodCount; + char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ + MA_WAVEFORMATEXTENSIBLE* pActualFormat; + + result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; + wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; + + /* The size of the buffer must be a clean multiple of the period count. */ + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); + periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; + + MA_ZERO_OBJECT(&descDS); + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = 0; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; + hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + + /* Get the _actual_ properties of the buffer. */ + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); + return ma_result_from_HRESULT(hr); + } + + /* We can now start setting the output data formats. */ + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorCapture->channels = pActualFormat->nChannels; + pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; + + /* Get the native channel map based on the channel mask. */ + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + } else { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); + } + + /* + After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the + user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. + */ + if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { + descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; + ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + + hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); + return ma_result_from_HRESULT(hr); + } + } + + /* DirectSound should give us a buffer exactly the size we asked for. */ + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = periodCount; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEFORMATEXTENSIBLE wf; + MA_DSBUFFERDESC descDSPrimary; + MA_DSCAPS caps; + char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ + MA_WAVEFORMATEXTENSIBLE* pActualFormat; + ma_uint32 periodSizeInFrames; + ma_uint32 periodCount; + MA_DSBUFFERDESC descDS; + WORD nativeChannelCount; + DWORD nativeChannelMask = 0; + + result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); + if (result != MA_SUCCESS) { + ma_device_uninit__dsound(pDevice); + return result; + } + + MA_ZERO_OBJECT(&descDSPrimary); + descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); + descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; + hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + + + /* We may want to make some adjustments to the format if we are using defaults. */ + MA_ZERO_OBJECT(&caps); + caps.dwSize = sizeof(caps); + hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); + return ma_result_from_HRESULT(hr); + } + + if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { + DWORD speakerConfig; + + /* It supports at least stereo, but could support more. */ + nativeChannelCount = 2; + + /* Look at the speaker configuration to get a better idea on the channel count. */ + if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { + ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask); + } + } else { + /* It does not support stereo, which means we are stuck with mono. */ + nativeChannelCount = 1; + nativeChannelMask = 0x00000001; + } + + if (pDescriptorPlayback->channels == 0) { + wf.nChannels = nativeChannelCount; + wf.dwChannelMask = nativeChannelMask; + } + + if (pDescriptorPlayback->sampleRate == 0) { + /* We base the sample rate on the values returned by GetCaps(). */ + if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { + wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); + } else { + wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; + } + } + + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + + /* + From MSDN: + + The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest + supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer + and compare the result with the format that was requested with the SetFormat method. + */ + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + if (FAILED(hr)) { + /* + If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have + observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a + sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will + use 44100 for the sample rate. + */ + wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.wBitsPerSample = 16; + wf.nChannels = nativeChannelCount; + wf.nSamplesPerSec = 44100; + wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + } + + /* Get the _actual_ properties of the buffer. */ + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); + return ma_result_from_HRESULT(hr); + } + + /* We now have enough information to start setting some output properties. */ + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorPlayback->channels = pActualFormat->nChannels; + pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; + + /* Get the internal channel map based on the channel mask. */ + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + } else { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); + } + + /* The size of the buffer must be a clean multiple of the period count. */ + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; + + /* + Meaning of dwFlags (from MSDN): + + DSBCAPS_CTRLPOSITIONNOTIFY + The buffer has position notification capability. + + DSBCAPS_GLOBALFOCUS + With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to + another application, even if the new application uses DirectSound. + + DSBCAPS_GETCURRENTPOSITION2 + In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated + sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the + application can get a more accurate play cursor. + */ + MA_ZERO_OBJECT(&descDS); + descDS.dwSize = sizeof(descDS); + descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; + hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); + if (FAILED(hr)) { + ma_device_uninit__dsound(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); + return ma_result_from_HRESULT(hr); + } + + /* DirectSound should give us a buffer exactly the size we asked for. */ + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = periodCount; + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_data_loop__dsound(ma_device* pDevice) +{ + ma_result result = MA_SUCCESS; + ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + HRESULT hr; + DWORD lockOffsetInBytesCapture; + DWORD lockSizeInBytesCapture; + DWORD mappedSizeInBytesCapture; + DWORD mappedDeviceFramesProcessedCapture; + void* pMappedDeviceBufferCapture; + DWORD lockOffsetInBytesPlayback; + DWORD lockSizeInBytesPlayback; + DWORD mappedSizeInBytesPlayback; + void* pMappedDeviceBufferPlayback; + DWORD prevReadCursorInBytesCapture = 0; + DWORD prevPlayCursorInBytesPlayback = 0; + ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; + DWORD virtualWriteCursorInBytesPlayback = 0; + ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; + ma_bool32 isPlaybackDeviceStarted = MA_FALSE; + ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ + ma_uint32 waitTimeInMilliseconds = 1; + + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); + return ma_result_from_HRESULT(hr); + } + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + switch (pDevice->type) + { + case ma_device_type_duplex: + { + DWORD physicalCaptureCursorInBytes; + DWORD physicalReadCursorInBytes; + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + if (FAILED(hr)) { + return ma_result_from_HRESULT(hr); + } + + /* If nothing is available we just sleep for a bit and return from this iteration. */ + if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + /* + The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure + we don't return until every frame has been copied over. + */ + if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { + /* The capture position has not looped. This is the simple case. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); + } else { + /* + The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, + do it again from the start. + */ + if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { + /* Lock up to the end of the buffer. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; + } else { + /* Lock starting from the start of the buffer. */ + lockOffsetInBytesCapture = 0; + lockSizeInBytesCapture = physicalReadCursorInBytes; + } + } + + if (lockSizeInBytesCapture == 0) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + return ma_result_from_HRESULT(hr); + } + + + /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ + mappedDeviceFramesProcessedCapture = 0; + + for (;;) { /* Keep writing to the playback device. */ + ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + ma_uint32 outputFramesInClientFormatCount; + ma_uint32 outputFramesInClientFormatConsumed = 0; + ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); + ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; + void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); + + result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); + if (result != MA_SUCCESS) { + break; + } + + outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; + mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; + + ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); + + /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ + for (;;) { + ma_uint32 framesWrittenThisIteration; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + DWORD availableBytesPlayback; + DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ + + /* We need the physical play and write cursors. */ + if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } + + /* If there's no room available for writing we need to wait for more. */ + if (availableBytesPlayback == 0) { + /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ + if (!isPlaybackDeviceStarted) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } else { + ma_sleep(waitTimeInMilliseconds); + continue; + } + } + + + /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ + lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. Go up to the end of the buffer. */ + lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + } else { + /* Different loop iterations. Go up to the physical play cursor. */ + lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } + + hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + /* + Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent + endless glitching due to it constantly running out of data. + */ + if (isPlaybackDeviceStarted) { + DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; + if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { + silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; + if (silentPaddingInBytes > lockSizeInBytesPlayback) { + silentPaddingInBytes = lockSizeInBytesPlayback; + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); + } + } + + /* At this point we have a buffer for output. */ + if (silentPaddingInBytes > 0) { + MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); + framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; + } else { + ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); + ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; + void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); + void* pConvertedFramesOut = pMappedDeviceBufferPlayback; + + result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); + if (result != MA_SUCCESS) { + break; + } + + outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; + framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; + } + + + hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; + if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { + virtualWriteCursorInBytesPlayback = 0; + virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; + } + + /* + We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds + a bit of a buffer to prevent the playback buffer from getting starved. + */ + framesWrittenToPlaybackDevice += framesWrittenThisIteration; + if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } + + if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { + break; /* We're finished with the output data.*/ + } + } + + if (clientCapturedFramesToProcess == 0) { + break; /* We just consumed every input sample. */ + } + } + + + /* At this point we're done with the mapped portion of the capture buffer. */ + hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); + } + prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); + } break; + + + + case ma_device_type_capture: + { + DWORD physicalCaptureCursorInBytes; + DWORD physicalReadCursorInBytes; + hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); + if (FAILED(hr)) { + return MA_ERROR; + } + + /* If the previous capture position is the same as the current position we need to wait a bit longer. */ + if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { + ma_sleep(waitTimeInMilliseconds); + continue; + } + + /* Getting here means we have capture data available. */ + if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { + /* The capture position has not looped. This is the simple case. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); + } else { + /* + The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, + do it again from the start. + */ + if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { + /* Lock up to the end of the buffer. */ + lockOffsetInBytesCapture = prevReadCursorInBytesCapture; + lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; + } else { + /* Lock starting from the start of the buffer. */ + lockOffsetInBytesCapture = 0; + lockSizeInBytesCapture = physicalReadCursorInBytes; + } + } + + if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { + ma_sleep(waitTimeInMilliseconds); + continue; /* Nothing is available in the capture buffer. */ + } + + hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + } + + if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); + } + + ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); + + hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); + return ma_result_from_HRESULT(hr); + } + prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; + + if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { + prevReadCursorInBytesCapture = 0; + } + } break; + + + + case ma_device_type_playback: + { + DWORD availableBytesPlayback; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + if (FAILED(hr)) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + /* This is an error. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); + availableBytesPlayback = 0; + } + } + + /* If there's no room available for writing we need to wait for more. */ + if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { + /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ + if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } else { + ma_sleep(waitTimeInMilliseconds); + continue; + } + } + + /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ + lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. Go up to the end of the buffer. */ + lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + } else { + /* Different loop iterations. Go up to the physical play cursor. */ + lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } + + hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + /* At this point we have a buffer for output. */ + ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); + + hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); + result = ma_result_from_HRESULT(hr); + break; + } + + virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; + if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { + virtualWriteCursorInBytesPlayback = 0; + virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; + } + + /* + We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds + a bit of a buffer to prevent the playback buffer from getting starved. + */ + framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; + if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { + hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); + return ma_result_from_HRESULT(hr); + } + isPlaybackDeviceStarted = MA_TRUE; + } + } break; + + + default: return MA_INVALID_ARGS; /* Invalid device type. */ + } + + if (result != MA_SUCCESS) { + return result; + } + } + + /* Getting here means the device is being stopped. */ + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ + if (isPlaybackDeviceStarted) { + for (;;) { + DWORD availableBytesPlayback = 0; + DWORD physicalPlayCursorInBytes; + DWORD physicalWriteCursorInBytes; + hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); + if (FAILED(hr)) { + break; + } + + if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { + physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; + } + prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; + + if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { + /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; + availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ + } else { + break; + } + } else { + /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ + if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { + availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; + } else { + break; + } + } + + if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { + break; + } + + ma_sleep(waitTimeInMilliseconds); + } + } + + hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); + if (FAILED(hr)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); + return ma_result_from_HRESULT(hr); + } + + ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__dsound(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_dsound); + + ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); + if (pContext->dsound.hDSoundDLL == NULL) { + return MA_API_NOT_FOUND; + } + + pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + + /* + We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too + well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient + place to just disable the DirectSound backend for Windows 95. + */ + if (pContext->dsound.DirectSoundCreate == NULL || + pContext->dsound.DirectSoundEnumerateA == NULL || + pContext->dsound.DirectSoundCaptureCreate == NULL || + pContext->dsound.DirectSoundCaptureEnumerateA == NULL) { + return MA_API_NOT_FOUND; + } + + pCallbacks->onContextInit = ma_context_init__dsound; + pCallbacks->onContextUninit = ma_context_uninit__dsound; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; + pCallbacks->onDeviceInit = ma_device_init__dsound; + pCallbacks->onDeviceUninit = ma_device_uninit__dsound; + pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ + pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ + pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ + pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ + pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; + + return MA_SUCCESS; +} +#endif + + + +/****************************************************************************** + +WinMM Backend + +******************************************************************************/ +#ifdef MA_HAS_WINMM + +/* +Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN +is defined. We need to define the types and functions we need manually. +*/ +#define MA_MMSYSERR_NOERROR 0 +#define MA_MMSYSERR_ERROR 1 +#define MA_MMSYSERR_BADDEVICEID 2 +#define MA_MMSYSERR_INVALHANDLE 5 +#define MA_MMSYSERR_NOMEM 7 +#define MA_MMSYSERR_INVALFLAG 10 +#define MA_MMSYSERR_INVALPARAM 11 +#define MA_MMSYSERR_HANDLEBUSY 12 + +#define MA_CALLBACK_EVENT 0x00050000 +#define MA_WAVE_ALLOWSYNC 0x0002 + +#define MA_WHDR_DONE 0x00000001 +#define MA_WHDR_PREPARED 0x00000002 +#define MA_WHDR_BEGINLOOP 0x00000004 +#define MA_WHDR_ENDLOOP 0x00000008 +#define MA_WHDR_INQUEUE 0x00000010 + +#define MA_MAXPNAMELEN 32 + +typedef void* MA_HWAVEIN; +typedef void* MA_HWAVEOUT; +typedef UINT MA_MMRESULT; +typedef UINT MA_MMVERSION; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; +} MA_WAVEINCAPSA; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; +} MA_WAVEOUTCAPSA; + +typedef struct tagWAVEHDR +{ + char* lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD_PTR dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct tagWAVEHDR* lpNext; + DWORD_PTR reserved; +} MA_WAVEHDR; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MA_WAVEOUTCAPS2A; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + GUID ManufacturerGuid; + GUID ProductGuid; + GUID NameGuid; +} MA_WAVEINCAPS2A; + +typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); +typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); + +static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) +{ + switch (resultMM) + { + case MA_MMSYSERR_NOERROR: return MA_SUCCESS; + case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; + case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; + case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; + case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; + case MA_MMSYSERR_ERROR: return MA_ERROR; + default: return MA_ERROR; + } +} + +static char* ma_find_last_character(char* str, char ch) +{ + char* last; + + if (str == NULL) { + return NULL; + } + + last = NULL; + while (*str != '\0') { + if (*str == ch) { + last = str; + } + + str += 1; + } + + return last; +} + +static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) +{ + return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); +} + + +/* +Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so +we can do things generically and typesafely. Names are being kept the same for consistency. +*/ +typedef struct +{ + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + GUID NameGuid; +} MA_WAVECAPSA; + +static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) +{ + WORD bitsPerSample = 0; + DWORD sampleRate = 0; + + if (pBitsPerSample) { + *pBitsPerSample = 0; + } + if (pSampleRate) { + *pSampleRate = 0; + } + + if (channels == 1) { + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48M16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48M08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { + sampleRate = 96000; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + } + } else { + bitsPerSample = 16; + if ((dwFormats & WAVE_FORMAT_48S16) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { + sampleRate = 96000; + } else { + bitsPerSample = 8; + if ((dwFormats & WAVE_FORMAT_48S08) != 0) { + sampleRate = 48000; + } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { + sampleRate = 44100; + } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { + sampleRate = 22050; + } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { + sampleRate = 11025; + } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { + sampleRate = 96000; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + } + } + + if (pBitsPerSample) { + *pBitsPerSample = bitsPerSample; + } + if (pSampleRate) { + *pSampleRate = sampleRate; + } + + return MA_SUCCESS; +} + +static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) +{ + ma_result result; + + MA_ASSERT(pWF != NULL); + + MA_ZERO_OBJECT(pWF); + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_PCM; + pWF->nChannels = (WORD)channels; + if (pWF->nChannels > 2) { + pWF->nChannels = 2; + } + + result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); + if (result != MA_SUCCESS) { + return result; + } + + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) +{ + WORD bitsPerSample; + DWORD sampleRate; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* + Name / Description + + Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking + situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try + looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. + */ + + /* Set the default to begin with. */ + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); + + /* + Now try the registry. There's a few things to consider here: + - The name GUID can be null, in which we case we just need to stick to the original 31 characters. + - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. + - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The + problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), + but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to + usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component + name, and then concatenate the name from the registry. + */ + if (!ma_is_guid_null(&pCaps->NameGuid)) { + WCHAR guidStrW[256]; + if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { + char guidStr[256]; + char keyStr[1024]; + HKEY hKey; + + WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); + + ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); + ma_strcat_s(keyStr, sizeof(keyStr), guidStr); + + if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { + BYTE nameFromReg[512]; + DWORD nameFromRegSize = sizeof(nameFromReg); + LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); + ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); + + if (resultWin32 == ERROR_SUCCESS) { + /* We have the value from the registry, so now we need to construct the name string. */ + char name[1024]; + if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { + char* nameBeg = ma_find_last_character(name, '('); + if (nameBeg != NULL) { + size_t leadingLen = (nameBeg - name); + ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); + + /* The closing ")", if it can fit. */ + if (leadingLen + nameFromRegSize < sizeof(name)-1) { + ma_strcat_s(name, sizeof(name), ")"); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); + } + } + } + } + } + } + + + result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); + if (result != MA_SUCCESS) { + return result; + } + + if (bitsPerSample == 8) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; + } else if (bitsPerSample == 16) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; + } else if (bitsPerSample == 24) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; + } else if (bitsPerSample == 32) { + pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; + } else { + return MA_FORMAT_NOT_SUPPORTED; + } + pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; + pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) +{ + MA_WAVECAPSA caps; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + +static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) +{ + MA_WAVECAPSA caps; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pCaps != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); + caps.dwFormats = pCaps->dwFormats; + caps.wChannels = pCaps->wChannels; + caps.NameGuid = pCaps->NameGuid; + return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); +} + + +static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + UINT playbackDeviceCount; + UINT captureDeviceCount; + UINT iPlaybackDevice; + UINT iCaptureDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); + for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { + MA_MMRESULT result; + MA_WAVEOUTCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + ma_device_info deviceInfo; + + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.winmm = iPlaybackDevice; + + /* The first enumerated device is the default device. */ + if (iPlaybackDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + return MA_SUCCESS; /* Enumeration was stopped. */ + } + } + } + } + + /* Capture. */ + captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); + for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { + MA_MMRESULT result; + MA_WAVEINCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + ma_device_info deviceInfo; + + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.winmm = iCaptureDevice; + + /* The first enumerated device is the default device. */ + if (iCaptureDevice == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { + ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + return MA_SUCCESS; /* Enumeration was stopped. */ + } + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + UINT winMMDeviceID; + + MA_ASSERT(pContext != NULL); + + winMMDeviceID = 0; + if (pDeviceID != NULL) { + winMMDeviceID = (UINT)pDeviceID->winmm; + } + + pDeviceInfo->id.winmm = winMMDeviceID; + + /* The first ID is the default device. */ + if (winMMDeviceID == 0) { + pDeviceInfo->isDefault = MA_TRUE; + } + + if (deviceType == ma_device_type_playback) { + MA_MMRESULT result; + MA_WAVEOUTCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); + } + } else { + MA_MMRESULT result; + MA_WAVEINCAPS2A caps; + + MA_ZERO_OBJECT(&caps); + + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { + return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); + } + } + + return MA_NO_DEVICE; +} + + +static ma_result ma_device_uninit__winmm(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + CloseHandle((HANDLE)pDevice->winmm.hEventCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); + } + + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + + MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ + + return MA_SUCCESS; +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* WinMM has a minimum period size of 40ms. */ + ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); + ma_uint32 periodSizeInFrames; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); + if (periodSizeInFrames < minPeriodSizeInFrames) { + periodSizeInFrames = minPeriodSizeInFrames; + } + + return periodSizeInFrames; +} + +static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + const char* errorMsg = ""; + ma_result errorCode = MA_ERROR; + ma_result result = MA_SUCCESS; + ma_uint32 heapSize; + UINT winMMDeviceIDPlayback = 0; + UINT winMMDeviceIDCapture = 0; + + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->winmm); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exlusive mode with WinMM. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + if (pDescriptorPlayback->pDeviceID != NULL) { + winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; + } + if (pDescriptorCapture->pDeviceID != NULL) { + winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; + } + + /* The capture device needs to be initialized first. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEINCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; + + /* We use an event to know when a new fragment needs to be enqueued. */ + pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDevice->winmm.hEventCapture == NULL) { + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); + goto on_error; + } + + /* The format should be based on the device's actual format. */ + if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; + goto on_error; + } + + result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); + if (result != MA_SUCCESS) { + errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; + goto on_error; + } + + resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); + pDescriptorCapture->channels = wf.nChannels; + pDescriptorCapture->sampleRate = wf.nSamplesPerSec; + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + MA_WAVEOUTCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; + + /* We use an event to know when a new fragment needs to be enqueued. */ + pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); + if (pDevice->winmm.hEventPlayback == NULL) { + errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); + goto on_error; + } + + /* The format should be based on the device's actual format. */ + if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; + goto on_error; + } + + result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); + if (result != MA_SUCCESS) { + errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; + goto on_error; + } + + resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { + errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; + goto on_error; + } + + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); + pDescriptorPlayback->channels = wf.nChannels; + pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + } + + /* + The heap allocated data is allocated like so: + + [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] + */ + heapSize = 0; + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + } + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); + } + + pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); + if (pDevice->winmm._pHeapData == NULL) { + errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; + goto on_error; + } + + MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPeriod; + + if (pConfig->deviceType == ma_device_type_capture) { + pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + } else { + pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); + } + + /* Prepare headers. */ + for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { + ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); + + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + + /* + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + it's unlocked and available for writing. A value of 1 means it's locked. + */ + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPeriod; + + if (pConfig->deviceType == ma_device_type_playback) { + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); + } else { + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + } + + /* Prepare headers. */ + for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { + ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); + + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + + /* + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + it's unlocked and available for writing. A value of 1 means it's locked. + */ + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; + } + } + + return MA_SUCCESS; + +on_error: + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.pWAVEHDRCapture != NULL) { + ma_uint32 iPeriod; + for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { + ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + } + } + + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.pWAVEHDRCapture != NULL) { + ma_uint32 iPeriod; + for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { + ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); + } + } + + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + } + + ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); + + if (errorMsg != NULL && errorMsg[0] != '\0') { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); + } + + return errorCode; +} + +static ma_result ma_device_start__winmm(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + MA_MMRESULT resultMM; + MA_WAVEHDR* pWAVEHDR; + ma_uint32 iPeriod; + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + + /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ + for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); + return ma_result_from_MMRESULT(resultMM); + } + + /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ + pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ + } + + /* Capture devices need to be explicitly started, unlike playback devices. */ + resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); + return ma_result_from_MMRESULT(resultMM); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__winmm(ma_device* pDevice) +{ + MA_MMRESULT resultMM; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->winmm.hDeviceCapture == NULL) { + return MA_INVALID_ARGS; + } + + resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_uint32 iPeriod; + MA_WAVEHDR* pWAVEHDR; + + if (pDevice->winmm.hDevicePlayback == NULL) { + return MA_INVALID_ARGS; + } + + /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { + if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + break; /* An error occurred so just abandon ship and stop the device without draining. */ + } + + pWAVEHDR[iPeriod].dwUser = 0; + } + } + + resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + if (resultMM != MA_MMSYSERR_NOERROR) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_result result = MA_SUCCESS; + MA_MMRESULT resultMM; + ma_uint32 totalFramesWritten; + MA_WAVEHDR* pWAVEHDR; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pPCMFrames != NULL); + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + + /* Keep processing as much data as possible. */ + totalFramesWritten = 0; + while (totalFramesWritten < frameCount) { + /* If the current header has some space available we need to write part of it. */ + if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ + /* + This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to + write it out and move on to the next iteration. + */ + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; + + ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); + const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); + void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); + MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); + + pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; + totalFramesWritten += framesToCopy; + + /* If we've consumed the buffer entirely we need to write it out to the device. */ + if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); + + /* The device will be started here. */ + resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + result = ma_result_from_MMRESULT(resultMM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); + break; + } + + /* Make sure we move to the next header. */ + pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; + pDevice->winmm.headerFramesConsumedPlayback = 0; + } + + /* If at this point we have consumed the entire input buffer we can return. */ + MA_ASSERT(totalFramesWritten <= frameCount); + if (totalFramesWritten == frameCount) { + break; + } + + /* Getting here means there's more to process. */ + continue; + } + + /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; + } + + /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ + if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ + pDevice->winmm.headerFramesConsumedPlayback = 0; + } + + /* If the device has been stopped we need to break. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + break; + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = totalFramesWritten; + } + + return result; +} + +static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_result result = MA_SUCCESS; + MA_MMRESULT resultMM; + ma_uint32 totalFramesRead; + MA_WAVEHDR* pWAVEHDR; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pPCMFrames != NULL); + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + + /* Keep processing as much data as possible. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + /* If the current header has some space available we need to write part of it. */ + if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ + /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ + ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; + + ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); + const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); + void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); + MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); + + pDevice->winmm.headerFramesConsumedCapture += framesToCopy; + totalFramesRead += framesToCopy; + + /* If we've consumed the buffer entirely we need to add it back to the device. */ + if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + + /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ + ResetEvent((HANDLE)pDevice->winmm.hEventCapture); + + /* The device will be started here. */ + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { + result = ma_result_from_MMRESULT(resultMM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); + break; + } + + /* Make sure we move to the next header. */ + pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; + pDevice->winmm.headerFramesConsumedCapture = 0; + } + + /* If at this point we have filled the entire input buffer we can return. */ + MA_ASSERT(totalFramesRead <= frameCount); + if (totalFramesRead == frameCount) { + break; + } + + /* Getting here means there's more to process. */ + continue; + } + + /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ + if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { + result = MA_ERROR; + break; + } + + /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ + if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ + pDevice->winmm.headerFramesConsumedCapture = 0; + } + + /* If the device has been stopped we need to break. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + break; + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +static ma_result ma_context_uninit__winmm(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_winmm); + + ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); + return MA_SUCCESS; +} + +static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); + if (pContext->winmm.hWinMM == NULL) { + return MA_NO_BACKEND; + } + + pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); + + pCallbacks->onContextInit = ma_context_init__winmm; + pCallbacks->onContextUninit = ma_context_uninit__winmm; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; + pCallbacks->onDeviceInit = ma_device_init__winmm; + pCallbacks->onDeviceUninit = ma_device_uninit__winmm; + pCallbacks->onDeviceStart = ma_device_start__winmm; + pCallbacks->onDeviceStop = ma_device_stop__winmm; + pCallbacks->onDeviceRead = ma_device_read__winmm; + pCallbacks->onDeviceWrite = ma_device_write__winmm; + pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ + + return MA_SUCCESS; +} +#endif + + + + +/****************************************************************************** + +ALSA Backend + +******************************************************************************/ +#ifdef MA_HAS_ALSA + +#include /* poll(), struct pollfd */ +#include /* eventfd() */ + +#ifdef MA_NO_RUNTIME_LINKING + +/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ +#if !defined(__cplusplus) + #if defined(__STRICT_ANSI__) + #if !defined(inline) + #define inline __inline__ __attribute__((always_inline)) + #define MA_INLINE_DEFINED + #endif + #endif +#endif +#include +#if defined(MA_INLINE_DEFINED) + #undef inline + #undef MA_INLINE_DEFINED +#endif + +typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; +typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; +typedef snd_pcm_stream_t ma_snd_pcm_stream_t; +typedef snd_pcm_format_t ma_snd_pcm_format_t; +typedef snd_pcm_access_t ma_snd_pcm_access_t; +typedef snd_pcm_t ma_snd_pcm_t; +typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; +typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; +typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; +typedef snd_pcm_info_t ma_snd_pcm_info_t; +typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; +typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; +typedef snd_pcm_state_t ma_snd_pcm_state_t; + +/* snd_pcm_stream_t */ +#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK +#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE + +/* snd_pcm_format_t */ +#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN +#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 +#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE +#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE +#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE +#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE +#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE +#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE +#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE +#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE +#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE +#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE +#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW +#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW +#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE +#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE + +/* ma_snd_pcm_access_t */ +#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED +#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED +#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX +#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED +#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED + +/* Channel positions. */ +#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN +#define MA_SND_CHMAP_NA SND_CHMAP_NA +#define MA_SND_CHMAP_MONO SND_CHMAP_MONO +#define MA_SND_CHMAP_FL SND_CHMAP_FL +#define MA_SND_CHMAP_FR SND_CHMAP_FR +#define MA_SND_CHMAP_RL SND_CHMAP_RL +#define MA_SND_CHMAP_RR SND_CHMAP_RR +#define MA_SND_CHMAP_FC SND_CHMAP_FC +#define MA_SND_CHMAP_LFE SND_CHMAP_LFE +#define MA_SND_CHMAP_SL SND_CHMAP_SL +#define MA_SND_CHMAP_SR SND_CHMAP_SR +#define MA_SND_CHMAP_RC SND_CHMAP_RC +#define MA_SND_CHMAP_FLC SND_CHMAP_FLC +#define MA_SND_CHMAP_FRC SND_CHMAP_FRC +#define MA_SND_CHMAP_RLC SND_CHMAP_RLC +#define MA_SND_CHMAP_RRC SND_CHMAP_RRC +#define MA_SND_CHMAP_FLW SND_CHMAP_FLW +#define MA_SND_CHMAP_FRW SND_CHMAP_FRW +#define MA_SND_CHMAP_FLH SND_CHMAP_FLH +#define MA_SND_CHMAP_FCH SND_CHMAP_FCH +#define MA_SND_CHMAP_FRH SND_CHMAP_FRH +#define MA_SND_CHMAP_TC SND_CHMAP_TC +#define MA_SND_CHMAP_TFL SND_CHMAP_TFL +#define MA_SND_CHMAP_TFR SND_CHMAP_TFR +#define MA_SND_CHMAP_TFC SND_CHMAP_TFC +#define MA_SND_CHMAP_TRL SND_CHMAP_TRL +#define MA_SND_CHMAP_TRR SND_CHMAP_TRR +#define MA_SND_CHMAP_TRC SND_CHMAP_TRC +#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC +#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC +#define MA_SND_CHMAP_TSL SND_CHMAP_TSL +#define MA_SND_CHMAP_TSR SND_CHMAP_TSR +#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE +#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE +#define MA_SND_CHMAP_BC SND_CHMAP_BC +#define MA_SND_CHMAP_BLC SND_CHMAP_BLC +#define MA_SND_CHMAP_BRC SND_CHMAP_BRC + +/* Open mode flags. */ +#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE +#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS +#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT +#else +#include /* For EPIPE, etc. */ +typedef unsigned long ma_snd_pcm_uframes_t; +typedef long ma_snd_pcm_sframes_t; +typedef int ma_snd_pcm_stream_t; +typedef int ma_snd_pcm_format_t; +typedef int ma_snd_pcm_access_t; +typedef int ma_snd_pcm_state_t; +typedef struct ma_snd_pcm_t ma_snd_pcm_t; +typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; +typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; +typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; +typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; +typedef struct +{ + void* addr; + unsigned int first; + unsigned int step; +} ma_snd_pcm_channel_area_t; +typedef struct +{ + unsigned int channels; + unsigned int pos[1]; +} ma_snd_pcm_chmap_t; + +/* snd_pcm_state_t */ +#define MA_SND_PCM_STATE_OPEN 0 +#define MA_SND_PCM_STATE_SETUP 1 +#define MA_SND_PCM_STATE_PREPARED 2 +#define MA_SND_PCM_STATE_RUNNING 3 +#define MA_SND_PCM_STATE_XRUN 4 +#define MA_SND_PCM_STATE_DRAINING 5 +#define MA_SND_PCM_STATE_PAUSED 6 +#define MA_SND_PCM_STATE_SUSPENDED 7 +#define MA_SND_PCM_STATE_DISCONNECTED 8 + +/* snd_pcm_stream_t */ +#define MA_SND_PCM_STREAM_PLAYBACK 0 +#define MA_SND_PCM_STREAM_CAPTURE 1 + +/* snd_pcm_format_t */ +#define MA_SND_PCM_FORMAT_UNKNOWN -1 +#define MA_SND_PCM_FORMAT_U8 1 +#define MA_SND_PCM_FORMAT_S16_LE 2 +#define MA_SND_PCM_FORMAT_S16_BE 3 +#define MA_SND_PCM_FORMAT_S24_LE 6 +#define MA_SND_PCM_FORMAT_S24_BE 7 +#define MA_SND_PCM_FORMAT_S32_LE 10 +#define MA_SND_PCM_FORMAT_S32_BE 11 +#define MA_SND_PCM_FORMAT_FLOAT_LE 14 +#define MA_SND_PCM_FORMAT_FLOAT_BE 15 +#define MA_SND_PCM_FORMAT_FLOAT64_LE 16 +#define MA_SND_PCM_FORMAT_FLOAT64_BE 17 +#define MA_SND_PCM_FORMAT_MU_LAW 20 +#define MA_SND_PCM_FORMAT_A_LAW 21 +#define MA_SND_PCM_FORMAT_S24_3LE 32 +#define MA_SND_PCM_FORMAT_S24_3BE 33 + +/* snd_pcm_access_t */ +#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 +#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 +#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 +#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 +#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 + +/* Channel positions. */ +#define MA_SND_CHMAP_UNKNOWN 0 +#define MA_SND_CHMAP_NA 1 +#define MA_SND_CHMAP_MONO 2 +#define MA_SND_CHMAP_FL 3 +#define MA_SND_CHMAP_FR 4 +#define MA_SND_CHMAP_RL 5 +#define MA_SND_CHMAP_RR 6 +#define MA_SND_CHMAP_FC 7 +#define MA_SND_CHMAP_LFE 8 +#define MA_SND_CHMAP_SL 9 +#define MA_SND_CHMAP_SR 10 +#define MA_SND_CHMAP_RC 11 +#define MA_SND_CHMAP_FLC 12 +#define MA_SND_CHMAP_FRC 13 +#define MA_SND_CHMAP_RLC 14 +#define MA_SND_CHMAP_RRC 15 +#define MA_SND_CHMAP_FLW 16 +#define MA_SND_CHMAP_FRW 17 +#define MA_SND_CHMAP_FLH 18 +#define MA_SND_CHMAP_FCH 19 +#define MA_SND_CHMAP_FRH 20 +#define MA_SND_CHMAP_TC 21 +#define MA_SND_CHMAP_TFL 22 +#define MA_SND_CHMAP_TFR 23 +#define MA_SND_CHMAP_TFC 24 +#define MA_SND_CHMAP_TRL 25 +#define MA_SND_CHMAP_TRR 26 +#define MA_SND_CHMAP_TRC 27 +#define MA_SND_CHMAP_TFLC 28 +#define MA_SND_CHMAP_TFRC 29 +#define MA_SND_CHMAP_TSL 30 +#define MA_SND_CHMAP_TSR 31 +#define MA_SND_CHMAP_LLFE 32 +#define MA_SND_CHMAP_RLFE 33 +#define MA_SND_CHMAP_BC 34 +#define MA_SND_CHMAP_BLC 35 +#define MA_SND_CHMAP_BRC 36 + +/* Open mode flags. */ +#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 +#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 +#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 +#endif + +typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); +typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); +typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); +typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); +typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); +typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); +typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); +typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); +typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); +typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); +typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); +typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); +typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); +typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); +typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); +typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); +typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); +typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); +typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); +typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); +typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); +typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); +typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); +typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); +typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); +typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); +typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); +typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); +typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); +typedef int (* ma_snd_card_get_index_proc) (const char *name); +typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); +typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); +typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); +typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); +typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); +typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); +typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); +typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); +typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); +typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); +typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); +typedef int (* ma_snd_config_update_free_global_proc) (void); + +/* This array specifies each of the common devices that can be used for both playback and capture. */ +static const char* g_maCommonDeviceNamesALSA[] = { + "default", + "null", + "pulse", + "jack" +}; + +/* This array allows us to blacklist specific playback devices. */ +static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { + "" +}; + +/* This array allows us to blacklist specific capture devices. */ +static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { + "" +}; + + +static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) +{ + ma_snd_pcm_format_t ALSAFormats[] = { + MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ + MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ + MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ + MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ + MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ + MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ + }; + + if (ma_is_big_endian()) { + ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; + ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; + ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; + ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; + ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; + ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; + } + + return ALSAFormats[format]; +} + +static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) +{ + if (ma_is_little_endian()) { + switch (formatALSA) { + case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; + case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; + case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; + case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; + default: break; + } + } else { + switch (formatALSA) { + case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; + case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; + case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; + case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; + default: break; + } + } + + /* Endian agnostic. */ + switch (formatALSA) { + case MA_SND_PCM_FORMAT_U8: return ma_format_u8; + default: return ma_format_unknown; + } +} + +static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) +{ + switch (alsaChannelPos) + { + case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; + case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; + case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; + case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; + case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; + case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; + case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; + case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; + case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; + case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; + case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; + case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case MA_SND_CHMAP_RLC: return 0; + case MA_SND_CHMAP_RRC: return 0; + case MA_SND_CHMAP_FLW: return 0; + case MA_SND_CHMAP_FRW: return 0; + case MA_SND_CHMAP_FLH: return 0; + case MA_SND_CHMAP_FCH: return 0; + case MA_SND_CHMAP_FRH: return 0; + case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; + case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; + case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; + case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; + case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; + case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; + case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; + default: break; + } + + return 0; +} + +static ma_bool32 ma_is_common_device_name__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + +static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) +{ + size_t iName; + for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { + if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + +static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) +{ + if (deviceType == ma_device_type_playback) { + return ma_is_playback_device_blacklisted__alsa(name); + } else { + return ma_is_capture_device_blacklisted__alsa(name); + } +} + + +static const char* ma_find_char(const char* str, char c, int* index) +{ + int i = 0; + for (;;) { + if (str[i] == '\0') { + if (index) *index = -1; + return NULL; + } + + if (str[i] == c) { + if (index) *index = i; + return str + i; + } + + i += 1; + } + + /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ + if (index) *index = -1; + return NULL; +} + +static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) +{ + /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ + + int commaPos; + const char* dev; + int i; + + if (hwid == NULL) { + return MA_FALSE; + } + + if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { + return MA_FALSE; + } + + hwid += 3; + + dev = ma_find_char(hwid, ',', &commaPos); + if (dev == NULL) { + return MA_FALSE; + } else { + dev += 1; /* Skip past the ",". */ + } + + /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ + for (i = 0; i < commaPos; ++i) { + if (hwid[i] < '0' || hwid[i] > '9') { + return MA_FALSE; + } + } + + /* Check if everything after the "," is numeric. If not, return false. */ + i = 0; + while (dev[i] != '\0') { + if (dev[i] < '0' || dev[i] > '9') { + return MA_FALSE; + } + i += 1; + } + + return MA_TRUE; +} + +static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ +{ + /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ + + int colonPos; + int commaPos; + char card[256]; + const char* dev; + int cardIndex; + + if (dst == NULL) { + return -1; + } + if (dstSize < 7) { + return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ + } + + *dst = '\0'; /* Safety. */ + if (src == NULL) { + return -1; + } + + /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ + if (ma_is_device_name_in_hw_format__alsa(src)) { + return ma_strcpy_s(dst, dstSize, src); + } + + src = ma_find_char(src, ':', &colonPos); + if (src == NULL) { + return -1; /* Couldn't find a colon */ + } + + dev = ma_find_char(src, ',', &commaPos); + if (dev == NULL) { + dev = "0"; + ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ + } else { + dev = dev + 5; /* +5 = ",DEV=" */ + ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ + } + + cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); + if (cardIndex < 0) { + return -2; /* Failed to retrieve the card index. */ + } + + + /* Construction. */ + dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; + if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { + return -3; + } + if (ma_strcat_s(dst, dstSize, ",") != 0) { + return -3; + } + if (ma_strcat_s(dst, dstSize, dev) != 0) { + return -3; + } + + return 0; +} + +static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) +{ + ma_uint32 i; + + MA_ASSERT(pHWID != NULL); + + for (i = 0; i < count; ++i) { + if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) +{ + ma_snd_pcm_t* pPCM; + ma_snd_pcm_stream_t stream; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppPCM != NULL); + + *ppPCM = NULL; + pPCM = NULL; + + stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; + + if (pDeviceID == NULL) { + ma_bool32 isDeviceOpen; + size_t i; + + /* + We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes + me feel better to try as hard as we can get to get _something_ working. + */ + const char* defaultDeviceNames[] = { + "default", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + if (shareMode == ma_share_mode_exclusive) { + defaultDeviceNames[1] = "hw"; + defaultDeviceNames[2] = "hw:0"; + defaultDeviceNames[3] = "hw:0,0"; + } else { + if (deviceType == ma_device_type_playback) { + defaultDeviceNames[1] = "dmix"; + defaultDeviceNames[2] = "dmix:0"; + defaultDeviceNames[3] = "dmix:0,0"; + } else { + defaultDeviceNames[1] = "dsnoop"; + defaultDeviceNames[2] = "dsnoop:0"; + defaultDeviceNames[3] = "dsnoop:0,0"; + } + defaultDeviceNames[4] = "hw"; + defaultDeviceNames[5] = "hw:0"; + defaultDeviceNames[6] = "hw:0,0"; + } + + isDeviceOpen = MA_FALSE; + for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { + if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { + if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { + isDeviceOpen = MA_TRUE; + break; + } + } + } + + if (!isDeviceOpen) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } else { + /* + We're trying to open a specific device. There's a few things to consider here: + + miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When + an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it + finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). + */ + + /* May end up needing to make small adjustments to the ID, so make a copy. */ + ma_device_id deviceID = *pDeviceID; + int resultALSA = -ENODEV; + + if (deviceID.alsa[0] != ':') { + /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); + } else { + char hwid[256]; + + /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ + if (deviceID.alsa[1] == '\0') { + deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ + } + + if (shareMode == ma_share_mode_shared) { + if (deviceType == ma_device_type_playback) { + ma_strcpy_s(hwid, sizeof(hwid), "dmix"); + } else { + ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); + } + + if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + } + } + + /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ + if (resultALSA != 0) { + ma_strcpy_s(hwid, sizeof(hwid), "hw"); + if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { + resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); + } + } + } + + if (resultALSA < 0) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + *ppPCM = pPCM; + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + int resultALSA; + ma_bool32 cbResult = MA_TRUE; + char** ppDeviceHints; + ma_device_id* pUniqueIDs = NULL; + ma_uint32 uniqueIDCount = 0; + char** ppNextDeviceHint; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); + + resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); + if (resultALSA < 0) { + ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + return ma_result_from_errno(-resultALSA); + } + + ppNextDeviceHint = ppDeviceHints; + while (*ppNextDeviceHint != NULL) { + char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); + char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); + char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); + ma_device_type deviceType = ma_device_type_playback; + ma_bool32 stopEnumeration = MA_FALSE; + char hwid[sizeof(pUniqueIDs->alsa)]; + ma_device_info deviceInfo; + + if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { + deviceType = ma_device_type_playback; + } + if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { + deviceType = ma_device_type_capture; + } + + if (NAME != NULL) { + if (pContext->alsa.useVerboseDeviceEnumeration) { + /* Verbose mode. Use the name exactly as-is. */ + ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } else { + /* Simplified mode. Use ":%d,%d" format. */ + if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { + /* + At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the + plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device + initialization time and is used as an indicator to try and use the most appropriate plugin depending on the + device type and sharing mode. + */ + char* dst = hwid; + char* src = hwid+2; + while ((*dst++ = *src++)); + } else { + /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ + ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); + } + + if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { + goto next_device; /* The device has already been enumerated. Move on to the next one. */ + } else { + /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ + size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); + ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); + if (pNewUniqueIDs == NULL) { + goto next_device; /* Failed to allocate memory. */ + } + + pUniqueIDs = pNewUniqueIDs; + MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); + uniqueIDCount += 1; + } + } + } else { + MA_ZERO_MEMORY(hwid, sizeof(hwid)); + } + + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); + + /* + There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and + just use the name of "default" as the indicator. + */ + if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { + deviceInfo.isDefault = MA_TRUE; + } + + + /* + DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose + device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish + between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the + description. + + The value in DESC seems to be split into two lines, with the first line being the name of the device and the + second line being a description of the device. I don't like having the description be across two lines because + it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line + being put into parentheses. In simplified mode I'm just stripping the second line entirely. + */ + if (DESC != NULL) { + int lfPos; + const char* line2 = ma_find_char(DESC, '\n', &lfPos); + if (line2 != NULL) { + line2 += 1; /* Skip past the new-line character. */ + + if (pContext->alsa.useVerboseDeviceEnumeration) { + /* Verbose mode. Put the second line in brackets. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); + ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); + } else { + /* Simplified mode. Strip the second line entirely. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); + } + } else { + /* There's no second line. Just copy the whole description. */ + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); + } + } + + if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { + cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); + } + + /* + Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback + again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which + means both Input and Output. + */ + if (cbResult) { + if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { + if (deviceType == ma_device_type_playback) { + if (!ma_is_capture_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } else { + if (!ma_is_playback_device_blacklisted__alsa(NAME)) { + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + } + } + } + + if (cbResult == MA_FALSE) { + stopEnumeration = MA_TRUE; + } + + next_device: + free(NAME); + free(DESC); + free(IOID); + ppNextDeviceHint += 1; + + /* We need to stop enumeration if the callback returned false. */ + if (stopEnumeration) { + break; + } + } + + ma_free(pUniqueIDs, &pContext->allocationCallbacks); + ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); + + ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_device_type deviceType; + const ma_device_id* pDeviceID; + ma_share_mode shareMode; + ma_device_info* pDeviceInfo; + ma_bool32 foundDevice; +} ma_context_get_device_info_enum_callback_data__alsa; + +static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) +{ + ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; + MA_ASSERT(pData != NULL); + + (void)pContext; + + if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MA_TRUE; + } else { + if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); + pData->foundDevice = MA_TRUE; + } + } + + /* Keep enumerating until we have found the device. */ + return !pData->foundDevice; +} + +static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pPCM != NULL); + MA_ASSERT(pHWParams != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; + } +} + +static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + ma_uint32 iSampleRate; + unsigned int minSampleRate; + unsigned int maxSampleRate; + int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ + + /* There could be a range. */ + ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); + ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); + + /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ + minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); + maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); + + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; + + if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); + } + } + + /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ + if (!ma_is_standard_sample_rate(minSampleRate)) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); + } + + if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { + ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); + } +} + +static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_context_get_device_info_enum_callback_data__alsa data; + ma_result result; + int resultALSA; + ma_snd_pcm_t* pPCM; + ma_snd_pcm_hw_params_t* pHWParams; + ma_uint32 iFormat; + ma_uint32 iChannel; + + MA_ASSERT(pContext != NULL); + + /* We just enumerate to find basic information about the device. */ + data.deviceType = deviceType; + data.pDeviceID = pDeviceID; + data.pDeviceInfo = pDeviceInfo; + data.foundDevice = MA_FALSE; + result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); + if (result != MA_SUCCESS) { + return result; + } + + if (!data.foundDevice) { + return MA_NO_DEVICE; + } + + if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { + pDeviceInfo->isDefault = MA_TRUE; + } + + /* For detailed info we need to open the device. */ + result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); + if (result != MA_SUCCESS) { + return result; + } + + /* We need to initialize a HW parameters object in order to know what formats are supported. */ + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); + if (pHWParams == NULL) { + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); + } + + /* + Some ALSA devices can support many permutations of formats, channels and rates. We only support + a fixed number of permutations which means we need to employ some strategies to ensure the best + combinations are returned. An example is the "pulse" device which can do it's own data conversion + in software and as a result can support any combination of format, channels and rate. + + We want to ensure the the first data formats are the best. We have a list of favored sample + formats and sample rates, so these will be the basis of our iteration. + */ + + /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { + ma_format format = g_maFormatPriorities[iFormat]; + + /* + For each format we need to make sure we reset the configuration space so we don't return + channel counts and rates that aren't compatible with a format. + */ + ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + + /* Test the format first. If this fails it means the format is not supported and we can skip it. */ + if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { + /* The format is supported. */ + unsigned int minChannels; + unsigned int maxChannels; + + /* + The configuration space needs to be restricted to this format so we can get an accurate + picture of which sample rates and channel counts are support with this format. + */ + ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + + /* Now we need to check for supported channels. */ + ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); + ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); + + if (minChannels > MA_MAX_CHANNELS) { + continue; /* Too many channels. */ + } + if (maxChannels < MA_MIN_CHANNELS) { + continue; /* Not enough channels. */ + } + + /* + Make sure the channel count is clamped. This is mainly intended for the max channels + because some devices can report an unbound maximum. + */ + minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ + ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ + } else { + /* The device only supports a specific set of channels. We need to iterate over all of them. */ + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + /* Test the channel before applying it to the configuration space. */ + unsigned int channels = iChannel; + + /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ + ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); + + if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { + /* The channel count is supported. */ + + /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ + ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); + + /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ + ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); + } else { + /* The channel count is not supported. Skip. */ + } + } + } + } else { + /* The format is not supported. Skip. */ + } + } + + ma_free(pHWParams, &pContext->allocationCallbacks); + + ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__alsa(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + close(pDevice->alsa.wakeupfdCapture); + ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); + } + + if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + close(pDevice->alsa.wakeupfdPlayback); + ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + ma_result result; + int resultALSA; + ma_snd_pcm_t* pPCM; + ma_bool32 isUsingMMap; + ma_snd_pcm_format_t formatALSA; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + int openMode; + ma_snd_pcm_hw_params_t* pHWParams; + ma_snd_pcm_sw_params_t* pSWParams; + ma_snd_pcm_uframes_t bufferBoundary; + int pollDescriptorCount; + struct pollfd* pPollDescriptors; + int wakeupfd; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ + MA_ASSERT(pDevice != NULL); + + formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); + + openMode = 0; + if (pConfig->alsa.noAutoResample) { + openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; + } + if (pConfig->alsa.noAutoChannels) { + openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; + } + if (pConfig->alsa.noAutoFormat) { + openMode |= MA_SND_PCM_NO_AUTO_FORMAT; + } + + result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); + if (result != MA_SUCCESS) { + return result; + } + + + /* Hardware parameters. */ + pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + if (pHWParams == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); + return ma_result_from_errno(-resultALSA); + } + + /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ + isUsingMMap = MA_FALSE; +#if 0 /* NOTE: MMAP mode temporarily disabled. */ + if (deviceType != ma_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 (!pConfig->alsa.noMMap) { + if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { + pDevice->alsa.isUsingMMap = MA_TRUE; + } + } + } +#endif + + if (!isUsingMMap) { + resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + /* + Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't + find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. + */ + + /* Format. */ + { + /* + At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is + supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. + */ + if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { + /* We're either requesting the native format or the specified format is not supported. */ + size_t iFormat; + + formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { + if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { + formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); + break; + } + } + + if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); + return MA_FORMAT_NOT_SUPPORTED; + } + } + + resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalFormat = ma_format_from_alsa(formatALSA); + if (internalFormat == ma_format_unknown) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; + } + } + + /* Channels. */ + { + unsigned int channels = pDescriptor->channels; + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + + resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalChannels = (ma_uint32)channels; + } + + /* Sample Rate */ + { + unsigned int sampleRate; + + /* + It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes + problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable + resampling. + + To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a + sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling + doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly + faster rate. + + miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine + for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very + good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. + + I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce + this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. + */ + ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); + + sampleRate = pDescriptor->sampleRate; + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalSampleRate = (ma_uint32)sampleRate; + } + + /* Periods. */ + { + ma_uint32 periods = pDescriptor->periodCount; + + resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalPeriods = periods; + } + + /* Buffer Size */ + { + ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; + + resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); + return ma_result_from_errno(-resultALSA); + } + + internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; + } + + /* Apply hardware parameters. */ + resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); + if (resultALSA < 0) { + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); + return ma_result_from_errno(-resultALSA); + } + + ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); + pHWParams = NULL; + + + /* Software parameters. */ + pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); + if (pSWParams == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); + return MA_OUT_OF_MEMORY; + } + + resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); + if (resultALSA < 0) { + bufferBoundary = internalPeriodSizeInFrames * internalPeriods; + } + + if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ + /* + Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to + the size of a period. But for full-duplex we need to set it such that it is at least two periods. + */ + resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); + return ma_result_from_errno(-resultALSA); + } + + resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); + if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); + return ma_result_from_errno(-resultALSA); + } + } + + resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); + if (resultALSA < 0) { + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); + return ma_result_from_errno(-resultALSA); + } + + ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); + pSWParams = NULL; + + + /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ + { + ma_snd_pcm_chmap_t* pChmap = NULL; + if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { + pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); + } + + if (pChmap != NULL) { + ma_uint32 iChannel; + + /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ + if (pChmap->channels >= internalChannels) { + /* Drop excess channels. */ + for (iChannel = 0; iChannel < internalChannels; ++iChannel) { + internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); + } + } else { + ma_uint32 i; + + /* + Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate + channels. If validation fails, fall back to defaults. + */ + ma_bool32 isValid = MA_TRUE; + + /* Fill with defaults. */ + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + + /* Overwrite first pChmap->channels channels. */ + for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { + internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); + } + + /* Validate. */ + for (i = 0; i < internalChannels && isValid; ++i) { + ma_uint32 j; + for (j = i+1; j < internalChannels; ++j) { + if (internalChannelMap[i] == internalChannelMap[j]) { + isValid = MA_FALSE; + break; + } + } + } + + /* If our channel map is invalid, fall back to defaults. */ + if (!isValid) { + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + } + } + + free(pChmap); + pChmap = NULL; + } else { + /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ + ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); + } + } + + + /* + We need to retrieve the poll descriptors so we can use poll() to wait for data to become + available for reading or writing. There's no well defined maximum for this so we're just going + to allocate this on the heap. + */ + pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); + if (pollDescriptorCount <= 0) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); + return MA_ERROR; + } + + pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ + if (pPollDescriptors == NULL) { + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); + return MA_OUT_OF_MEMORY; + } + + /* + We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver + never returns from writei() and readi(). This has been observed with the "pulse" device. + */ + wakeupfd = eventfd(0, 0); + if (wakeupfd < 0) { + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); + return ma_result_from_errno(errno); + } + + /* We'll place the wakeup fd at the start of the buffer. */ + pPollDescriptors[0].fd = wakeupfd; + pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ + pPollDescriptors[0].revents = 0; + + /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ + pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ + if (pollDescriptorCount <= 0) { + close(wakeupfd); + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); + return MA_ERROR; + } + + if (deviceType == ma_device_type_capture) { + pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; + pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; + pDevice->alsa.wakeupfdCapture = wakeupfd; + } else { + pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; + pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; + pDevice->alsa.wakeupfdPlayback = wakeupfd; + } + + + /* We're done. Prepare the device. */ + resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); + if (resultALSA < 0) { + close(wakeupfd); + ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); + ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); + return ma_result_from_errno(-resultALSA); + } + + + if (deviceType == ma_device_type_capture) { + pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; + pDevice->alsa.isUsingMMapCapture = isUsingMMap; + } else { + pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; + pDevice->alsa.isUsingMMapPlayback = isUsingMMap; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->alsa); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__alsa(ma_device* pDevice) +{ + int resultALSA; + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); + return ma_result_from_errno(-resultALSA); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__alsa(ma_device* pDevice) +{ + /* + The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is + a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. + */ + int resultPoll; + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); + ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); + } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); + ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); + + /* We need to prepare the device again, otherwise we won't be able to restart the device. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); + if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); + } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + } + + } + + return MA_SUCCESS; +} + +static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) +{ + for (;;) { + unsigned short revents; + int resultALSA; + int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); + if (resultPoll < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); + return ma_result_from_errno(errno); + } + + /* + Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor + has had it's POLLIN flag set. If so, we need to actually read the data and then exit + function. The wakeup descriptor will be the first item in the descriptors buffer. + */ + if ((pPollDescriptors[0].revents & POLLIN) != 0) { + ma_uint64 t; + int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ + if (resultRead < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); + return ma_result_from_errno(errno); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); + return MA_DEVICE_NOT_STARTED; + } + + /* + Getting here means that some data should be able to be read. We need to use ALSA to + translate the revents flags for us. + */ + resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); + return ma_result_from_errno(-resultALSA); + } + + if ((revents & POLLERR) != 0) { + ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); + if (state == MA_SND_PCM_STATE_XRUN) { + /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); + } + } + + if ((revents & requiredEvent) == requiredEvent) { + break; /* We're done. Data available for reading or writing. */ + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_wait_read__alsa(ma_device* pDevice) +{ + return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ +} + +static ma_result ma_device_wait_write__alsa(ma_device* pDevice) +{ + return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ +} + +static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + ma_snd_pcm_sframes_t resultALSA = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + ma_result result; + + /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ + result = ma_device_wait_read__alsa(pDevice); + if (result != MA_SUCCESS) { + return result; + } + + /* Getting here means we should have data available. */ + resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); + if (resultALSA >= 0) { + break; /* Success. */ + } else { + if (resultALSA == -EAGAIN) { + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ + continue; /* Try again. */ + } else if (resultALSA == -EPIPE) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); + + /* Overrun. Recover and try again. If this fails we need to return an error. */ + resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); + return ma_result_from_errno((int)-resultALSA); + } + + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + continue; /* Try reading again. */ + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = resultALSA; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + ma_snd_pcm_sframes_t resultALSA = 0; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pFrames != NULL); + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + while (ma_device_get_state(pDevice) == ma_device_state_started) { + ma_result result; + + /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ + result = ma_device_wait_write__alsa(pDevice); + if (result != MA_SUCCESS) { + return result; + } + + resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); + if (resultALSA >= 0) { + break; /* Success. */ + } else { + if (resultALSA == -EAGAIN) { + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ + continue; /* Try again. */ + } else if (resultALSA == -EPIPE) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); + + /* Underrun. Recover and try again. If this fails we need to return an error. */ + resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + /* + In my testing I have had a situation where writei() does not automatically restart the device even though I've set it + up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of + frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure + if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't + quite right here. + */ + resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); + if (resultALSA < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); + return ma_result_from_errno((int)-resultALSA); + } + + continue; /* Try writing again. */ + } + } + } + + if (pFramesWritten != NULL) { + *pFramesWritten = resultALSA; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) +{ + ma_uint64 t = 1; + int resultWrite = 0; + + MA_ASSERT(pDevice != NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); + + /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ + if (pDevice->alsa.pPollDescriptorsCapture != NULL) { + resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); + } + if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { + resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); + } + + if (resultWrite < 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); + return ma_result_from_errno(errno); + } + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__alsa(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_alsa); + + /* Clean up memory for memory leak checkers. */ + ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); +#endif + + ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; +#ifndef MA_NO_RUNTIME_LINKING + const char* libasoundNames[] = { + "libasound.so.2", + "libasound.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libasoundNames); ++i) { + pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); + if (pContext->alsa.asoundSO != NULL) { + break; + } + } + + if (pContext->alsa.asoundSO == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); + return MA_NO_BACKEND; + } + + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); +#else + /* The system below is just for type safety. */ + ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; + ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; + ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; + ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; + ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; + ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; + ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; + ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; + ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; + ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; + ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; + ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; + ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; + ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; + ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; + ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; + ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; + ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; + ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; + ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; + ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; + ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; + ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; + ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; + ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; + ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; + ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; + ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; + ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; + ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; + ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; + ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; + ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; + ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; + ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; + ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; + ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; + ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; + ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; + ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; + ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; + ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; + ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; + ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; + ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; + ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; + ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; + ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; + ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; + ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; + ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; + ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; + ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; + ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; + ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; + ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; + ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; + ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; + ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; + ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; + ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; + ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; + ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; + ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; + ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; + ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; + + pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; + pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; + pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; + pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; + pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; + pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; + pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; + pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; + pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; + pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; + pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; + pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; + pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; + pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; + pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; + pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; + pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; + pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; + pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; + pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; + pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; + pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; + pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; + pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; + pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; +#endif + + pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; + + result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); + return result; + } + + pCallbacks->onContextInit = ma_context_init__alsa; + pCallbacks->onContextUninit = ma_context_uninit__alsa; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; + pCallbacks->onDeviceInit = ma_device_init__alsa; + pCallbacks->onDeviceUninit = ma_device_uninit__alsa; + pCallbacks->onDeviceStart = ma_device_start__alsa; + pCallbacks->onDeviceStop = ma_device_stop__alsa; + pCallbacks->onDeviceRead = ma_device_read__alsa; + pCallbacks->onDeviceWrite = ma_device_write__alsa; + pCallbacks->onDeviceDataLoop = NULL; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; + + return MA_SUCCESS; +} +#endif /* ALSA */ + + + +/****************************************************************************** + +PulseAudio Backend + +******************************************************************************/ +#ifdef MA_HAS_PULSEAUDIO +/* +The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on +in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. + +PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it +allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it +appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or +write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the +simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient +when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. + +Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to +get fun, and I don't mean that in a good way... + +The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands +don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is +enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost +all of PulseAudio's problems stem from. + +When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own +vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called +pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop +because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use +it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. + +To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer +to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded +main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely +specialized such as if you want to integrate it into your application's existing main loop infrastructure. + +(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. +It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) + +Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to +miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's +one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which +is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if +you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` +has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can +set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. +All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. +This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before +attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. + +The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an +internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the +host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. + +Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. +The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call +`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get +information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object +is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to +run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the +context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. +All of that just to retrieve basic information about a device! + +Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the +context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design +choices in PulseAudio. + +PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here +because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for +writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can +set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices +straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, +PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) +because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback +would be where a program will want to write or read data to or from the stream, but when it's called before the application has even +requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at +that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the +stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data +callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio +doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been +started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data +callback is not fired. + +This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will +continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device +is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in +PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call +`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always +writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if +you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to +*not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining +important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained +before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write +data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! + +This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* +write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just +resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This +disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the +callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) + +Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, +only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as +"corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think +it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you +guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is +absolutely beyond me. Would it really be that hard to just make it run synchronously? + +Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that +they were initialized in. + +That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're +embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to +run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche +requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is +constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a +parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These +changes alone will change PulseAudio from one of the worst audio APIs to one of the best. +*/ + + +/* +It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header +to check for type safety. We cannot do this when linking at run time because the header might not be available. +*/ +#ifdef MA_NO_RUNTIME_LINKING + +/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ +#if !defined(__cplusplus) + #if defined(__STRICT_ANSI__) + #if !defined(inline) + #define inline __inline__ __attribute__((always_inline)) + #define MA_INLINE_DEFINED + #endif + #endif +#endif +#include +#if defined(MA_INLINE_DEFINED) + #undef inline + #undef MA_INLINE_DEFINED +#endif + +#define MA_PA_OK PA_OK +#define MA_PA_ERR_ACCESS PA_ERR_ACCESS +#define MA_PA_ERR_INVALID PA_ERR_INVALID +#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY +#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED + +#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX +#define MA_PA_RATE_MAX PA_RATE_MAX + +typedef pa_context_flags_t ma_pa_context_flags_t; +#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS +#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL + +typedef pa_stream_flags_t ma_pa_stream_flags_t; +#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS +#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED +#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING +#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC +#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE +#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS +#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS +#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT +#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE +#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS +#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE +#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE +#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT +#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED +#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY +#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS +#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND +#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED +#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND +#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME +#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH + +typedef pa_sink_flags_t ma_pa_sink_flags_t; +#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS +#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL +#define MA_PA_SINK_LATENCY PA_SINK_LATENCY +#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE +#define MA_PA_SINK_NETWORK PA_SINK_NETWORK +#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL +#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME +#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME +#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY +#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS + +typedef pa_source_flags_t ma_pa_source_flags_t; +#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS +#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL +#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY +#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE +#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK +#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL +#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME +#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY +#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME + +typedef pa_context_state_t ma_pa_context_state_t; +#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED +#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING +#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING +#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME +#define MA_PA_CONTEXT_READY PA_CONTEXT_READY +#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED +#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED + +typedef pa_stream_state_t ma_pa_stream_state_t; +#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED +#define MA_PA_STREAM_CREATING PA_STREAM_CREATING +#define MA_PA_STREAM_READY PA_STREAM_READY +#define MA_PA_STREAM_FAILED PA_STREAM_FAILED +#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED + +typedef pa_operation_state_t ma_pa_operation_state_t; +#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING +#define MA_PA_OPERATION_DONE PA_OPERATION_DONE +#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED + +typedef pa_sink_state_t ma_pa_sink_state_t; +#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE +#define MA_PA_SINK_RUNNING PA_SINK_RUNNING +#define MA_PA_SINK_IDLE PA_SINK_IDLE +#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED + +typedef pa_source_state_t ma_pa_source_state_t; +#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE +#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING +#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE +#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED + +typedef pa_seek_mode_t ma_pa_seek_mode_t; +#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE +#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE +#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ +#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END + +typedef pa_channel_position_t ma_pa_channel_position_t; +#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID +#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER +#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT +#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT +#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER +#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT +#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT +#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 +#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 +#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 +#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 +#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 +#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 +#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 +#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 +#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 +#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 +#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 +#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 +#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 +#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 +#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 +#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 +#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 +#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 +#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 +#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 +#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 +#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 +#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 +#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 +#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 +#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 +#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 +#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 +#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 +#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 +#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 +#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 +#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT +#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT +#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER +#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT +#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT +#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER +#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER + +typedef pa_channel_map_def_t ma_pa_channel_map_def_t; +#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF +#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA +#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX +#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX +#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS +#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT + +typedef pa_sample_format_t ma_pa_sample_format_t; +#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID +#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 +#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW +#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW +#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE +#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE +#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE +#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE +#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE +#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE +#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE +#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE +#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE +#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE + +typedef pa_mainloop ma_pa_mainloop; +typedef pa_threaded_mainloop ma_pa_threaded_mainloop; +typedef pa_mainloop_api ma_pa_mainloop_api; +typedef pa_context ma_pa_context; +typedef pa_operation ma_pa_operation; +typedef pa_stream ma_pa_stream; +typedef pa_spawn_api ma_pa_spawn_api; +typedef pa_buffer_attr ma_pa_buffer_attr; +typedef pa_channel_map ma_pa_channel_map; +typedef pa_cvolume ma_pa_cvolume; +typedef pa_sample_spec ma_pa_sample_spec; +typedef pa_sink_info ma_pa_sink_info; +typedef pa_source_info ma_pa_source_info; + +typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; +typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; +typedef pa_source_info_cb_t ma_pa_source_info_cb_t; +typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; +typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; +typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; +typedef pa_free_cb_t ma_pa_free_cb_t; +#else +#define MA_PA_OK 0 +#define MA_PA_ERR_ACCESS 1 +#define MA_PA_ERR_INVALID 2 +#define MA_PA_ERR_NOENTITY 5 +#define MA_PA_ERR_NOTSUPPORTED 19 + +#define MA_PA_CHANNELS_MAX 32 +#define MA_PA_RATE_MAX 384000 + +typedef int ma_pa_context_flags_t; +#define MA_PA_CONTEXT_NOFLAGS 0x00000000 +#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 +#define MA_PA_CONTEXT_NOFAIL 0x00000002 + +typedef int ma_pa_stream_flags_t; +#define MA_PA_STREAM_NOFLAGS 0x00000000 +#define MA_PA_STREAM_START_CORKED 0x00000001 +#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 +#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 +#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 +#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 +#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 +#define MA_PA_STREAM_FIX_FORMAT 0x00000040 +#define MA_PA_STREAM_FIX_RATE 0x00000080 +#define MA_PA_STREAM_FIX_CHANNELS 0x00000100 +#define MA_PA_STREAM_DONT_MOVE 0x00000200 +#define MA_PA_STREAM_VARIABLE_RATE 0x00000400 +#define MA_PA_STREAM_PEAK_DETECT 0x00000800 +#define MA_PA_STREAM_START_MUTED 0x00001000 +#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 +#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 +#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 +#define MA_PA_STREAM_START_UNMUTED 0x00010000 +#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 +#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 +#define MA_PA_STREAM_PASSTHROUGH 0x00080000 + +typedef int ma_pa_sink_flags_t; +#define MA_PA_SINK_NOFLAGS 0x00000000 +#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 +#define MA_PA_SINK_LATENCY 0x00000002 +#define MA_PA_SINK_HARDWARE 0x00000004 +#define MA_PA_SINK_NETWORK 0x00000008 +#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 +#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 +#define MA_PA_SINK_FLAT_VOLUME 0x00000040 +#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 +#define MA_PA_SINK_SET_FORMATS 0x00000100 + +typedef int ma_pa_source_flags_t; +#define MA_PA_SOURCE_NOFLAGS 0x00000000 +#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 +#define MA_PA_SOURCE_LATENCY 0x00000002 +#define MA_PA_SOURCE_HARDWARE 0x00000004 +#define MA_PA_SOURCE_NETWORK 0x00000008 +#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 +#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 +#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 +#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 + +typedef int ma_pa_context_state_t; +#define MA_PA_CONTEXT_UNCONNECTED 0 +#define MA_PA_CONTEXT_CONNECTING 1 +#define MA_PA_CONTEXT_AUTHORIZING 2 +#define MA_PA_CONTEXT_SETTING_NAME 3 +#define MA_PA_CONTEXT_READY 4 +#define MA_PA_CONTEXT_FAILED 5 +#define MA_PA_CONTEXT_TERMINATED 6 + +typedef int ma_pa_stream_state_t; +#define MA_PA_STREAM_UNCONNECTED 0 +#define MA_PA_STREAM_CREATING 1 +#define MA_PA_STREAM_READY 2 +#define MA_PA_STREAM_FAILED 3 +#define MA_PA_STREAM_TERMINATED 4 + +typedef int ma_pa_operation_state_t; +#define MA_PA_OPERATION_RUNNING 0 +#define MA_PA_OPERATION_DONE 1 +#define MA_PA_OPERATION_CANCELLED 2 + +typedef int ma_pa_sink_state_t; +#define MA_PA_SINK_INVALID_STATE -1 +#define MA_PA_SINK_RUNNING 0 +#define MA_PA_SINK_IDLE 1 +#define MA_PA_SINK_SUSPENDED 2 + +typedef int ma_pa_source_state_t; +#define MA_PA_SOURCE_INVALID_STATE -1 +#define MA_PA_SOURCE_RUNNING 0 +#define MA_PA_SOURCE_IDLE 1 +#define MA_PA_SOURCE_SUSPENDED 2 + +typedef int ma_pa_seek_mode_t; +#define MA_PA_SEEK_RELATIVE 0 +#define MA_PA_SEEK_ABSOLUTE 1 +#define MA_PA_SEEK_RELATIVE_ON_READ 2 +#define MA_PA_SEEK_RELATIVE_END 3 + +typedef int ma_pa_channel_position_t; +#define MA_PA_CHANNEL_POSITION_INVALID -1 +#define MA_PA_CHANNEL_POSITION_MONO 0 +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 +#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 +#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 +#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 +#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 +#define MA_PA_CHANNEL_POSITION_LFE 7 +#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 +#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 +#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 +#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 +#define MA_PA_CHANNEL_POSITION_AUX0 12 +#define MA_PA_CHANNEL_POSITION_AUX1 13 +#define MA_PA_CHANNEL_POSITION_AUX2 14 +#define MA_PA_CHANNEL_POSITION_AUX3 15 +#define MA_PA_CHANNEL_POSITION_AUX4 16 +#define MA_PA_CHANNEL_POSITION_AUX5 17 +#define MA_PA_CHANNEL_POSITION_AUX6 18 +#define MA_PA_CHANNEL_POSITION_AUX7 19 +#define MA_PA_CHANNEL_POSITION_AUX8 20 +#define MA_PA_CHANNEL_POSITION_AUX9 21 +#define MA_PA_CHANNEL_POSITION_AUX10 22 +#define MA_PA_CHANNEL_POSITION_AUX11 23 +#define MA_PA_CHANNEL_POSITION_AUX12 24 +#define MA_PA_CHANNEL_POSITION_AUX13 25 +#define MA_PA_CHANNEL_POSITION_AUX14 26 +#define MA_PA_CHANNEL_POSITION_AUX15 27 +#define MA_PA_CHANNEL_POSITION_AUX16 28 +#define MA_PA_CHANNEL_POSITION_AUX17 29 +#define MA_PA_CHANNEL_POSITION_AUX18 30 +#define MA_PA_CHANNEL_POSITION_AUX19 31 +#define MA_PA_CHANNEL_POSITION_AUX20 32 +#define MA_PA_CHANNEL_POSITION_AUX21 33 +#define MA_PA_CHANNEL_POSITION_AUX22 34 +#define MA_PA_CHANNEL_POSITION_AUX23 35 +#define MA_PA_CHANNEL_POSITION_AUX24 36 +#define MA_PA_CHANNEL_POSITION_AUX25 37 +#define MA_PA_CHANNEL_POSITION_AUX26 38 +#define MA_PA_CHANNEL_POSITION_AUX27 39 +#define MA_PA_CHANNEL_POSITION_AUX28 40 +#define MA_PA_CHANNEL_POSITION_AUX29 41 +#define MA_PA_CHANNEL_POSITION_AUX30 42 +#define MA_PA_CHANNEL_POSITION_AUX31 43 +#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 +#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 +#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 +#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT +#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT +#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER +#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE + +typedef int ma_pa_channel_map_def_t; +#define MA_PA_CHANNEL_MAP_AIFF 0 +#define MA_PA_CHANNEL_MAP_ALSA 1 +#define MA_PA_CHANNEL_MAP_AUX 2 +#define MA_PA_CHANNEL_MAP_WAVEEX 3 +#define MA_PA_CHANNEL_MAP_OSS 4 +#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF + +typedef int ma_pa_sample_format_t; +#define MA_PA_SAMPLE_INVALID -1 +#define MA_PA_SAMPLE_U8 0 +#define MA_PA_SAMPLE_ALAW 1 +#define MA_PA_SAMPLE_ULAW 2 +#define MA_PA_SAMPLE_S16LE 3 +#define MA_PA_SAMPLE_S16BE 4 +#define MA_PA_SAMPLE_FLOAT32LE 5 +#define MA_PA_SAMPLE_FLOAT32BE 6 +#define MA_PA_SAMPLE_S32LE 7 +#define MA_PA_SAMPLE_S32BE 8 +#define MA_PA_SAMPLE_S24LE 9 +#define MA_PA_SAMPLE_S24BE 10 +#define MA_PA_SAMPLE_S24_32LE 11 +#define MA_PA_SAMPLE_S24_32BE 12 + +typedef struct ma_pa_mainloop ma_pa_mainloop; +typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; +typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; +typedef struct ma_pa_context ma_pa_context; +typedef struct ma_pa_operation ma_pa_operation; +typedef struct ma_pa_stream ma_pa_stream; +typedef struct ma_pa_spawn_api ma_pa_spawn_api; + +typedef struct +{ + ma_uint32 maxlength; + ma_uint32 tlength; + ma_uint32 prebuf; + ma_uint32 minreq; + ma_uint32 fragsize; +} ma_pa_buffer_attr; + +typedef struct +{ + ma_uint8 channels; + ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; +} ma_pa_channel_map; + +typedef struct +{ + ma_uint8 channels; + ma_uint32 values[MA_PA_CHANNELS_MAX]; +} ma_pa_cvolume; + +typedef struct +{ + ma_pa_sample_format_t format; + ma_uint32 rate; + ma_uint8 channels; +} ma_pa_sample_spec; + +typedef struct +{ + const char* name; + ma_uint32 index; + const char* description; + ma_pa_sample_spec sample_spec; + ma_pa_channel_map channel_map; + ma_uint32 owner_module; + ma_pa_cvolume volume; + int mute; + ma_uint32 monitor_source; + const char* monitor_source_name; + ma_uint64 latency; + const char* driver; + ma_pa_sink_flags_t flags; + void* proplist; + ma_uint64 configured_latency; + ma_uint32 base_volume; + ma_pa_sink_state_t state; + ma_uint32 n_volume_steps; + ma_uint32 card; + ma_uint32 n_ports; + void** ports; + void* active_port; + ma_uint8 n_formats; + void** formats; +} ma_pa_sink_info; + +typedef struct +{ + const char *name; + ma_uint32 index; + const char *description; + ma_pa_sample_spec sample_spec; + ma_pa_channel_map channel_map; + ma_uint32 owner_module; + ma_pa_cvolume volume; + int mute; + ma_uint32 monitor_of_sink; + const char *monitor_of_sink_name; + ma_uint64 latency; + const char *driver; + ma_pa_source_flags_t flags; + void* proplist; + ma_uint64 configured_latency; + ma_uint32 base_volume; + ma_pa_source_state_t state; + ma_uint32 n_volume_steps; + ma_uint32 card; + ma_uint32 n_ports; + void** ports; + void* active_port; + ma_uint8 n_formats; + void** formats; +} ma_pa_source_info; + +typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); +typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); +typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); +typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); +typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); +typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); +typedef void (* ma_pa_free_cb_t) (void* p); +#endif + + +typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); +typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); +typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); +typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); +typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); +typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); +typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); +typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); +typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); +typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); +typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); +typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); +typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); +typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); +typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); +typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); +typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); +typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); +typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); +typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); +typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); +typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); +typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); +typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); +typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); +typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); +typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); +typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); +typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); +typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); +typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); +typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); +typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); +typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); +typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); +typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); +typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); +typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); +typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); +typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); +typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); + +typedef struct +{ + ma_uint32 count; + ma_uint32 capacity; + ma_device_info* pInfo; +} ma_pulse_device_enum_data; + +static ma_result ma_result_from_pulse(int result) +{ + if (result < 0) { + return MA_ERROR; + } + + switch (result) { + case MA_PA_OK: return MA_SUCCESS; + case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; + case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; + case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; + default: return MA_ERROR; + } +} + +#if 0 +static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) +{ + if (ma_is_little_endian()) { + switch (format) { + case ma_format_s16: return MA_PA_SAMPLE_S16LE; + case ma_format_s24: return MA_PA_SAMPLE_S24LE; + case ma_format_s32: return MA_PA_SAMPLE_S32LE; + case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; + default: break; + } + } else { + switch (format) { + case ma_format_s16: return MA_PA_SAMPLE_S16BE; + case ma_format_s24: return MA_PA_SAMPLE_S24BE; + case ma_format_s32: return MA_PA_SAMPLE_S32BE; + case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; + default: break; + } + } + + /* Endian agnostic. */ + switch (format) { + case ma_format_u8: return MA_PA_SAMPLE_U8; + default: return MA_PA_SAMPLE_INVALID; + } +} +#endif + +static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) +{ + if (ma_is_little_endian()) { + switch (format) { + case MA_PA_SAMPLE_S16LE: return ma_format_s16; + case MA_PA_SAMPLE_S24LE: return ma_format_s24; + case MA_PA_SAMPLE_S32LE: return ma_format_s32; + case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; + default: break; + } + } else { + switch (format) { + case MA_PA_SAMPLE_S16BE: return ma_format_s16; + case MA_PA_SAMPLE_S24BE: return ma_format_s24; + case MA_PA_SAMPLE_S32BE: return ma_format_s32; + case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; + default: break; + } + } + + /* Endian agnostic. */ + switch (format) { + case MA_PA_SAMPLE_U8: return ma_format_u8; + default: return ma_format_unknown; + } +} + +static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) +{ + switch (position) + { + case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; + case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; + case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; + case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; + case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; + case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; + case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; + case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; + case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; + case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; + case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; + case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; + case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; + case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; + case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; + case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; + case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; + case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; + case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; + case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; + case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; + case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; + case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; + case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; + case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; + case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; + case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; + case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; + case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; + case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; + case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; + case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; + case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; + case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; + case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; + case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; + case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; + case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + default: return MA_CHANNEL_NONE; + } +} + +#if 0 +static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) +{ + switch (position) + { + case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; + case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; + case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; + case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; + case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; + case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; + case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; + case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; + case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; + case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; + case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; + case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; + case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; + case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; + case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; + case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; + case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; + case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; + case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; + default: return (ma_pa_channel_position_t)position; + } +} +#endif + +static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +{ + int resultPA; + ma_pa_operation_state_t state; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pOP != NULL); + + for (;;) { + state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); + if (state != MA_PA_OPERATION_RUNNING) { + break; /* Done. */ + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) +{ + ma_result result; + + if (pOP == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + return result; +} + +static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) +{ + int resultPA; + ma_pa_context_state_t state; + + for (;;) { + state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); + if (state == MA_PA_CONTEXT_READY) { + break; /* Done. */ + } + + if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); + return MA_ERROR; + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + /* Should never get here. */ + return MA_SUCCESS; +} + +static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) +{ + int resultPA; + ma_pa_stream_state_t state; + + for (;;) { + state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); + if (state == MA_PA_STREAM_READY) { + break; /* Done. */ + } + + if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); + return MA_ERROR; + } + + resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); + if (resultPA < 0) { + return ma_result_from_pulse(resultPA); + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) +{ + ma_result result; + ma_ptr pMainLoop; + ma_ptr pPulseContext; + + MA_ASSERT(ppMainLoop != NULL); + MA_ASSERT(ppPulseContext != NULL); + + /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ + pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); + if (pMainLoop == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); + return MA_FAILED_TO_INIT_BACKEND; + } + + pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); + if (pPulseContext == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return MA_FAILED_TO_INIT_BACKEND; + } + + /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ + result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ + result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); + return result; + } + + *ppMainLoop = pMainLoop; + *ppPulseContext = pPulseContext; + + return MA_SUCCESS; +} + + +static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_pa_sink_info* pInfoOut; + + if (endOfList > 0) { + return; + } + + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + + pInfoOut = (ma_pa_sink_info*)pUserData; + MA_ASSERT(pInfoOut != NULL); + + *pInfoOut = *pInfo; + + (void)pPulseContext; /* Unused. */ +} + +static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_pa_source_info* pInfoOut; + + if (endOfList > 0) { + return; + } + + /* + There has been a report that indicates that pInfo can be null which results + in a null pointer dereference below. We'll check for this for safety. + */ + if (pInfo == NULL) { + return; + } + + pInfoOut = (ma_pa_source_info*)pUserData; + MA_ASSERT(pInfoOut != NULL); + + *pInfoOut = *pInfo; + + (void)pPulseContext; /* Unused. */ +} + +#if 0 +static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_device* pDevice; + + if (endOfList > 0) { + return; + } + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); + + (void)pPulseContext; /* Unused. */ +} + +static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_device* pDevice; + + if (endOfList > 0) { + return; + } + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); + + (void)pPulseContext; /* Unused. */ +} +#endif + +static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) +{ + ma_pa_operation* pOP; + + pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); + if (pOP == NULL) { + return MA_ERROR; + } + + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); +} + +static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) +{ + ma_pa_operation* pOP; + + pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); + if (pOP == NULL) { + return MA_ERROR; + } + + return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); +} + +static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) +{ + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pIndex != NULL); + + if (pIndex != NULL) { + *pIndex = (ma_uint32)-1; + } + + if (deviceType == ma_device_type_playback) { + ma_pa_sink_info sinkInfo; + result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pIndex != NULL) { + *pIndex = sinkInfo.index; + } + } + + if (deviceType == ma_device_type_capture) { + ma_pa_source_info sourceInfo; + result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pIndex != NULL) { + *pIndex = sourceInfo.index; + } + } + + return MA_SUCCESS; +} + + +typedef struct +{ + ma_context* pContext; + ma_enum_devices_callback_proc callback; + void* pUserData; + ma_bool32 isTerminated; + ma_uint32 defaultDeviceIndexPlayback; + ma_uint32 defaultDeviceIndexCapture; +} ma_context_enumerate_devices_callback_data__pulse; + +static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_device_info deviceInfo; + + MA_ASSERT(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* The name from PulseAudio is the ID for miniaudio. */ + if (pSinkInfo->name != NULL) { + ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); + } + + /* The description from PulseAudio is the name for miniaudio. */ + if (pSinkInfo->description != NULL) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); + } + + if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { + deviceInfo.isDefault = MA_TRUE; + } + + pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); + + (void)pPulseContext; /* Unused. */ +} + +static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) +{ + ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; + ma_device_info deviceInfo; + + MA_ASSERT(pData != NULL); + + if (endOfList || pData->isTerminated) { + return; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* The name from PulseAudio is the ID for miniaudio. */ + if (pSourceInfo->name != NULL) { + ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); + } + + /* The description from PulseAudio is the name for miniaudio. */ + if (pSourceInfo->description != NULL) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); + } + + if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { + deviceInfo.isDefault = MA_TRUE; + } + + pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); + + (void)pPulseContext; /* Unused. */ +} + +static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result = MA_SUCCESS; + ma_context_enumerate_devices_callback_data__pulse callbackData; + ma_pa_operation* pOP = NULL; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + callbackData.pContext = pContext; + callbackData.callback = callback; + callbackData.pUserData = pUserData; + callbackData.isTerminated = MA_FALSE; + callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; + callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; + + /* We need to get the index of the default devices. */ + ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); + ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); + + /* Playback. */ + if (!callbackData.isTerminated) { + pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MA_ERROR; + goto done; + } + + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MA_SUCCESS) { + goto done; + } + } + + + /* Capture. */ + if (!callbackData.isTerminated) { + pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); + if (pOP == NULL) { + result = MA_ERROR; + goto done; + } + + result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); + ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); + + if (result != MA_SUCCESS) { + goto done; + } + } + +done: + return result; +} + + +typedef struct +{ + ma_device_info* pDeviceInfo; + ma_uint32 defaultDeviceIndex; + ma_bool32 foundDevice; +} ma_context_get_device_info_callback_data__pulse; + +static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) +{ + ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + + if (endOfList > 0) { + return; + } + + MA_ASSERT(pData != NULL); + pData->foundDevice = MA_TRUE; + + if (pInfo->name != NULL) { + ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + /* + We're just reporting a single data format here. I think technically PulseAudio might support + all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to + report the "native" device format. + */ + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; + pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->nativeDataFormats[0].flags = 0; + pData->pDeviceInfo->nativeDataFormatCount = 1; + + if (pData->defaultDeviceIndex == pInfo->index) { + pData->pDeviceInfo->isDefault = MA_TRUE; + } + + (void)pPulseContext; /* Unused. */ +} + +static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) +{ + ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; + + if (endOfList > 0) { + return; + } + + MA_ASSERT(pData != NULL); + pData->foundDevice = MA_TRUE; + + if (pInfo->name != NULL) { + ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); + } + + if (pInfo->description != NULL) { + ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); + } + + /* + We're just reporting a single data format here. I think technically PulseAudio might support + all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to + report the "native" device format. + */ + pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); + pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; + pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; + pData->pDeviceInfo->nativeDataFormats[0].flags = 0; + pData->pDeviceInfo->nativeDataFormatCount = 1; + + if (pData->defaultDeviceIndex == pInfo->index) { + pData->pDeviceInfo->isDefault = MA_TRUE; + } + + (void)pPulseContext; /* Unused. */ +} + +static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result = MA_SUCCESS; + ma_context_get_device_info_callback_data__pulse callbackData; + ma_pa_operation* pOP = NULL; + const char* pDeviceName = NULL; + + MA_ASSERT(pContext != NULL); + + callbackData.pDeviceInfo = pDeviceInfo; + callbackData.foundDevice = MA_FALSE; + + if (pDeviceID != NULL) { + pDeviceName = pDeviceID->pulse; + } else { + pDeviceName = NULL; + } + + result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); + + if (deviceType == ma_device_type_playback) { + pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); + } else { + pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); + } + + if (pOP != NULL) { + ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); + } else { + result = MA_ERROR; + goto done; + } + + if (!callbackData.foundDevice) { + result = MA_NO_DEVICE; + goto done; + } + +done: + return result; +} + +static ma_result ma_device_uninit__pulse(ma_device* pDevice) +{ + ma_context* pContext; + + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } + + if (pDevice->type == ma_device_type_duplex) { + ma_duplex_rb_uninit(&pDevice->duplexRB); + } + + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); + + return MA_SUCCESS; +} + +static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) +{ + ma_pa_buffer_attr attr; + attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); + attr.tlength = attr.maxlength / periods; + attr.prebuf = (ma_uint32)-1; + attr.minreq = (ma_uint32)-1; + attr.fragsize = attr.maxlength / periods; + + return attr; +} + +static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) +{ + static int g_StreamCounter = 0; + char actualStreamName[256]; + + if (pStreamName != NULL) { + ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); + } else { + ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); + ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ + } + g_StreamCounter += 1; + + return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); +} + + +static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 bpf; + ma_uint32 deviceState; + ma_uint64 frameCount; + ma_uint64 framesProcessed; + + MA_ASSERT(pDevice != NULL); + + /* + Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio + can fire this callback before the stream has even started. Ridiculous. + */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + return; + } + + bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + MA_ASSERT(bpf > 0); + + frameCount = byteCount / bpf; + framesProcessed = 0; + + while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { + const void* pMappedPCMFrames; + size_t bytesMapped; + ma_uint64 framesMapped; + + int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); + if (pulseResult < 0) { + break; /* Failed to map. Abort. */ + } + + framesMapped = bytesMapped / bpf; + if (framesMapped > 0) { + if (pMappedPCMFrames != NULL) { + ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); + } else { + /* It's a hole. */ + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); + } + + pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); + if (pulseResult < 0) { + break; /* Failed to drop the buffer. */ + } + + framesProcessed += framesMapped; + + } else { + /* Nothing was mapped. Just abort. */ + break; + } + } +} + +static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) +{ + ma_result result = MA_SUCCESS; + ma_uint64 framesProcessed = 0; + size_t bytesMapped; + ma_uint32 bpf; + ma_uint32 deviceState; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pStream != NULL); + + bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + MA_ASSERT(bpf > 0); + + deviceState = ma_device_get_state(pDevice); + + bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); + if (bytesMapped != (size_t)-1) { + if (bytesMapped > 0) { + ma_uint64 framesMapped; + void* pMappedPCMFrames; + int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); + if (pulseResult < 0) { + result = ma_result_from_pulse(pulseResult); + goto done; + } + + framesMapped = bytesMapped / bpf; + + if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ + ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); + } else { + /* Device is not started. Write silence. */ + ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); + } + + pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); + if (pulseResult < 0) { + result = ma_result_from_pulse(pulseResult); + goto done; /* Failed to write data to stream. */ + } + + framesProcessed += framesMapped; + } else { + result = MA_SUCCESS; /* No data available for writing. */ + goto done; + } + } else { + result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ + goto done; + } + +done: + if (pFramesProcessed != NULL) { + *pFramesProcessed = framesProcessed; + } + + return result; +} + +static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 bpf; + ma_uint64 frameCount; + ma_uint64 framesProcessed; + ma_uint32 deviceState; + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* + Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio + can fire this callback before the stream has even started. Ridiculous. + */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + return; + } + + bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + MA_ASSERT(bpf > 0); + + frameCount = byteCount / bpf; + framesProcessed = 0; + + while (framesProcessed < frameCount) { + ma_uint64 framesProcessedThisIteration; + + /* Don't keep trying to process frames if the device isn't started. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { + break; + } + + result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); + if (result != MA_SUCCESS) { + break; + } + + framesProcessed += framesProcessedThisIteration; + } +} + +static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + int suspended; + + (void)pStream; + + suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); + + if (suspended < 0) { + return; + } + + if (suspended == 1) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); + ma_device__on_notification_stopped(pDevice); + } else { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); + ma_device__on_notification_started(pDevice); + } +} + +static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + + (void)pStream; + (void)pUserData; + + ma_device__on_notification_rerouted(pDevice); +} + +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports from users where buffers of < ~20ms result glitches when running through + PipeWire. To work around this we're going to have to use a different default buffer size. + */ + const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; + const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; + + MA_ASSERT(nativeSampleRate != 0); + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } +} + +static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + /* + Notes for PulseAudio: + + - When both the period size in frames and milliseconds are 0, we default to miniaudio's + default buffer sizes rather than leaving it up to PulseAudio because I don't trust + PulseAudio to give us any kind of reasonable latency by default. + + - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this + flag, capture mode will just not work properly until you open another PulseAudio app. + */ + + ma_result result = MA_SUCCESS; + int error = 0; + const char* devPlayback = NULL; + const char* devCapture = NULL; + ma_format format = ma_format_unknown; + ma_uint32 channels = 0; + ma_uint32 sampleRate = 0; + ma_pa_sink_info sinkInfo; + ma_pa_source_info sourceInfo; + ma_pa_sample_spec ss; + ma_pa_channel_map cmap; + ma_pa_buffer_attr attr; + const ma_pa_sample_spec* pActualSS = NULL; + const ma_pa_buffer_attr* pActualAttr = NULL; + ma_uint32 iChannel; + ma_pa_stream_flags_t streamFlags; + + MA_ASSERT(pDevice != NULL); + MA_ZERO_OBJECT(&pDevice->pulse); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with the PulseAudio backend. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (pDescriptorPlayback->pDeviceID != NULL) { + devPlayback = pDescriptorPlayback->pDeviceID->pulse; + } + + format = pDescriptorPlayback->format; + channels = pDescriptorPlayback->channels; + sampleRate = pDescriptorPlayback->sampleRate; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (pDescriptorCapture->pDeviceID != NULL) { + devCapture = pDescriptorCapture->pDeviceID->pulse; + } + + format = pDescriptorCapture->format; + channels = pDescriptorCapture->channels; + sampleRate = pDescriptorCapture->sampleRate; + } + + + + result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); + return result; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); + goto on_error0; + } + + ss = sourceInfo.sample_spec; + cmap = sourceInfo.channel_map; + + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ + if (pDescriptorCapture->sampleRate != 0) { + ss.rate = pDescriptorCapture->sampleRate; + } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; + + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_is_little_endian()) { + ss.format = MA_PA_SAMPLE_FLOAT32LE; + } else { + ss.format = MA_PA_SAMPLE_FLOAT32BE; + } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); + } + if (ss.rate == 0) { + ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); + } + if (ss.channels == 0) { + ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); + } + + /* We now have enough information to calculate our actual period size in frames. */ + pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); + + attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + + pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); + if (pDevice->pulse.pStreamCapture == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); + result = MA_ERROR; + goto on_error0; + } + + + /* The callback needs to be set before connecting the stream. */ + ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); + + /* State callback for checking when the device has been corked. */ + ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); + + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); + + + /* Connect after we've got all of our internal state set up. */ + if (devCapture != NULL) { + streamFlags |= MA_PA_STREAM_DONT_MOVE; + } + + error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); + if (error != MA_PA_OK) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); + result = ma_result_from_pulse(error); + goto on_error1; + } + + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (result != MA_SUCCESS) { + goto on_error2; + } + + + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualSS != NULL) { + ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); + } + + pDescriptorCapture->format = ma_format_from_pulse(ss.format); + pDescriptorCapture->channels = ss.channels; + pDescriptorCapture->sampleRate = ss.rate; + + if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); + result = MA_ERROR; + goto on_error4; + } + + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorCapture->channels > 2) { + for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { + pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorCapture->channels == 1) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorCapture->channels == 2) { + pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + } + + + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + + if (attr.fragsize > 0) { + pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); + } else { + pDescriptorCapture->periodCount = 1; + } + + pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); + goto on_error2; + } + + ss = sinkInfo.sample_spec; + cmap = sinkInfo.channel_map; + + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + + /* Use the requested sample rate if one was specified. */ + if (pDescriptorPlayback->sampleRate != 0) { + ss.rate = pDescriptorPlayback->sampleRate; + } + + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; + if (ma_format_from_pulse(ss.format) == ma_format_unknown) { + if (ma_is_little_endian()) { + ss.format = MA_PA_SAMPLE_FLOAT32LE; + } else { + ss.format = MA_PA_SAMPLE_FLOAT32BE; + } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); + } + if (ss.rate == 0) { + ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); + } + if (ss.channels == 0) { + ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); + } + + /* We now have enough information to calculate the actual buffer size in frames. */ + pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); + + attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + + pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); + if (pDevice->pulse.pStreamPlayback == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); + result = MA_ERROR; + goto on_error2; + } + + + /* + Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a + device state of ma_device_state_uninitialized. + */ + ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); + + /* State callback for checking when the device has been corked. */ + ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); + + /* Rerouting notification. */ + ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); + + + /* Connect after we've got all of our internal state set up. */ + if (devPlayback != NULL) { + streamFlags |= MA_PA_STREAM_DONT_MOVE; + } + + error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); + if (error != MA_PA_OK) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); + result = ma_result_from_pulse(error); + goto on_error3; + } + + result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (result != MA_SUCCESS) { + goto on_error3; + } + + + /* Internal format. */ + pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualSS != NULL) { + ss = *pActualSS; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); + } + + pDescriptorPlayback->format = ma_format_from_pulse(ss.format); + pDescriptorPlayback->channels = ss.channels; + pDescriptorPlayback->sampleRate = ss.rate; + + if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); + result = MA_ERROR; + goto on_error4; + } + + /* Internal channel map. */ + + /* + Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting + the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono + and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For + all other channel counts we need to just put up with whatever PipeWire reports and hope it gets + fixed sooner than later. I might remove this hack later. + */ + if (pDescriptorPlayback->channels > 2) { + for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { + pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); + } + } else { + /* Hack for mono and stereo. */ + if (pDescriptorPlayback->channels == 1) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; + } else if (pDescriptorPlayback->channels == 2) { + pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; + pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + } + + + /* Buffer. */ + pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + if (pActualAttr != NULL) { + attr = *pActualAttr; + } + + if (attr.tlength > 0) { + pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); + } else { + pDescriptorPlayback->periodCount = 1; + } + + pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); + } + + + /* + We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main + part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for + us later on because that will only do it if it's a fully asynchronous backend - i.e. the + onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. + */ + if (pConfig->deviceType == ma_device_type_duplex) { + ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; + ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; + ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; + + result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); + goto on_error4; + } + } + + return MA_SUCCESS; + + +on_error4: + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } +on_error3: + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); + } +on_error2: + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } +on_error1: + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); + } +on_error0: + return result; +} + + +static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) +{ + ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; + MA_ASSERT(pIsSuccessful != NULL); + + *pIsSuccessful = (ma_bool32)success; + + (void)pStream; /* Unused. */ +} + +static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) +{ + ma_context* pContext = pDevice->pContext; + ma_bool32 wasSuccessful; + ma_pa_stream* pStream; + ma_pa_operation* pOP; + ma_result result; + + /* This should not be called with a duplex device type. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + wasSuccessful = MA_FALSE; + + pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); + MA_ASSERT(pStream != NULL); + + pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); + if (pOP == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); + return MA_ERROR; + } + + result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); + return result; + } + + if (!wasSuccessful) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); + return MA_ERROR; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__pulse(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + We need to fill some data before uncorking. Not doing this will result in the write callback + never getting fired. We're not going to abort if writing fails because I still want the device + to get uncorked. + */ + ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ + + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__pulse(ma_device* pDevice) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + /* + Ideally we would drain the device here, but there's been cases where PulseAudio seems to be + broken on some systems to the point where no audio processing seems to happen. When this + happens, draining never completes and we get stuck here. For now I'm disabling draining of + the device so we don't just freeze the application. + */ + #if 0 + ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); + ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); + #endif + + result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop__pulse(ma_device* pDevice) +{ + int resultPA; + + MA_ASSERT(pDevice != NULL); + + /* NOTE: Don't start the device here. It'll be done at a higher level. */ + + /* + All data is handled through callbacks. All we need to do is iterate over the main loop and let + the callbacks deal with it. + */ + while (ma_device_get_state(pDevice) == ma_device_state_started) { + resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); + if (resultPA < 0) { + break; + } + } + + /* NOTE: Don't stop the device here. It'll be done at a higher level. */ + return MA_SUCCESS; +} + +static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__pulse(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_pulseaudio); + + ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); + ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); + + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; +#ifndef MA_NO_RUNTIME_LINKING + const char* libpulseNames[] = { + "libpulse.so", + "libpulse.so.0" + }; + size_t i; + + for (i = 0; i < ma_countof(libpulseNames); ++i) { + pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); + if (pContext->pulse.pulseSO != NULL) { + break; + } + } + + if (pContext->pulse.pulseSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); + pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); +#else + /* This strange assignment system is just for type safety. */ + ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; + ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; + ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; + ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; + ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; + ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; + ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; + ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; + ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; + ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; + ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; + ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; + ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; + ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; + ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; + ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; + ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; + ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; + ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; + ma_pa_context_new_proc _pa_context_new = pa_context_new; + ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; + ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; + ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; + ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; + ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; + ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; + ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; + ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; + ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; + ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; + ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; + ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; + ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; + ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; + ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; + ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; + ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; + ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; + ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; + ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; + ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; + ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; + ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; + ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; + ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; + ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; + ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; + ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; + ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; + ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; + ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; + ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; + ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; + ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; + ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; + ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; + ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; + ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; + ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; + ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; + ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; + + pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; + pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; + pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; + pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; + pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; + pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; + pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; + pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; + pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; + pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; + pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; + pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; + pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; + pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; + pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; + pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; + pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; + pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; + pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; + pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; + pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; + pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; + pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; + pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; + pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; + pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; + pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; + pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; + pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; + pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; + pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; + pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; + pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; + pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; + pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; + pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; + pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; + pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; + pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; + pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; +#endif + + /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ + pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); + if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { + return MA_OUT_OF_MEMORY; + } + + pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); + if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); + if (result != MA_SUCCESS) { + ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); + ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); + #ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); + #endif + return result; + } + + /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ + pCallbacks->onContextInit = ma_context_init__pulse; + pCallbacks->onContextUninit = ma_context_uninit__pulse; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; + pCallbacks->onDeviceInit = ma_device_init__pulse; + pCallbacks->onDeviceUninit = ma_device_uninit__pulse; + pCallbacks->onDeviceStart = ma_device_start__pulse; + pCallbacks->onDeviceStop = ma_device_stop__pulse; + pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ + pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; + pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; + + return MA_SUCCESS; +} +#endif + + +/****************************************************************************** + +JACK Backend + +******************************************************************************/ +#ifdef MA_HAS_JACK + +/* It is assumed jack.h is available when compile-time linking is being used. */ +#ifdef MA_NO_RUNTIME_LINKING +#include + +typedef jack_nframes_t ma_jack_nframes_t; +typedef jack_options_t ma_jack_options_t; +typedef jack_status_t ma_jack_status_t; +typedef jack_client_t ma_jack_client_t; +typedef jack_port_t ma_jack_port_t; +typedef JackProcessCallback ma_JackProcessCallback; +typedef JackBufferSizeCallback ma_JackBufferSizeCallback; +typedef JackShutdownCallback ma_JackShutdownCallback; +#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE +#define ma_JackNoStartServer JackNoStartServer +#define ma_JackPortIsInput JackPortIsInput +#define ma_JackPortIsOutput JackPortIsOutput +#define ma_JackPortIsPhysical JackPortIsPhysical +#else +typedef ma_uint32 ma_jack_nframes_t; +typedef int ma_jack_options_t; +typedef int ma_jack_status_t; +typedef struct ma_jack_client_t ma_jack_client_t; +typedef struct ma_jack_port_t ma_jack_port_t; +typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); +typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); +typedef void (* ma_JackShutdownCallback) (void* arg); +#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" +#define ma_JackNoStartServer 1 +#define ma_JackPortIsInput 1 +#define ma_JackPortIsOutput 2 +#define ma_JackPortIsPhysical 4 +#endif + +typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); +typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_client_name_size_proc) (void); +typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); +typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); +typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); +typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); +typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); +typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); +typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); +typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); +typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); +typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); +typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); +typedef void (* ma_jack_free_proc) (void* ptr); + +static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) +{ + size_t maxClientNameSize; + char clientName[256]; + ma_jack_status_t status; + ma_jack_client_t* pClient; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppClient != NULL); + + if (ppClient) { + *ppClient = NULL; + } + + maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ + ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); + + pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); + if (pClient == NULL) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + if (ppClient) { + *ppClient = pClient; + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + (void)cbResult; /* For silencing a static analysis warning. */ + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_jack_client_t* pClient; + ma_result result; + const char** ppPorts; + + MA_ASSERT(pContext != NULL); + + if (pDeviceID != NULL && pDeviceID->jack != 0) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + + /* Name / Description */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + /* Jack only uses default devices. */ + pDeviceInfo->isDefault = MA_TRUE; + + /* Jack only supports f32 and has a specific channel count and sample rate. */ + pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; + + /* The channel count and sample rate can only be determined by opening the device. */ + result = ma_context_open_client__jack(pContext, &pClient); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; + } + + pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); + pDeviceInfo->nativeDataFormats[0].channels = 0; + + ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); + if (ppPorts == NULL) { + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { + pDeviceInfo->nativeDataFormats[0].channels += 1; + } + + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormatCount = 1; + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); + + (void)pContext; + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__jack(ma_device* pDevice) +{ + ma_context* pContext; + + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->jack.pClient != NULL) { + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +static void ma_device__jack_shutdown_callback(void* pUserData) +{ + /* JACK died. Stop the device. */ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_stop(pDevice); +} + +static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + if (pNewBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); + + pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; + pDevice->playback.internalPeriodSizeInFrames = frameCount; + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); + float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); + if (pNewBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); + + pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; + pDevice->playback.internalPeriodSizeInFrames = frameCount; + } + + return 0; +} + +static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) +{ + ma_device* pDevice; + ma_context* pContext; + ma_uint32 iChannel; + + pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + pContext = pDevice->pContext; + MA_ASSERT(pContext != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + /* Channels need to be interleaved. */ + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { + const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); + if (pSrc != NULL) { + float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; + ma_jack_nframes_t iFrame; + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += pDevice->capture.internalChannels; + pSrc += 1; + } + } + } + + ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); + + /* Channels need to be deinterleaved. */ + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { + float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); + if (pDst != NULL) { + const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; + ma_jack_nframes_t iFrame; + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + *pDst = *pSrc; + + pDst += 1; + pSrc += pDevice->playback.internalChannels; + } + } + } + } + + return 0; +} + +static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + ma_uint32 periodSizeInFrames; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDevice != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* Only supporting default devices with JACK. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); + return MA_NO_DEVICE; + } + + /* No exclusive mode with the JACK backend. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Open the client. */ + result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); + return result; + } + + /* Callbacks. */ + if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); + + + /* The buffer size in frames can change. */ + periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; + const char** ppPorts; + + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = 0; + pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + if (ppPorts == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* Need to count the number of ports first so we can allocate some memory. */ + while (ppPorts[pDescriptorCapture->channels] != NULL) { + pDescriptorCapture->channels += 1; + } + + pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsCapture == NULL) { + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { + char name[64]; + ma_strcpy_s(name, sizeof(name), "capture"); + ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ + + pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); + if (pDevice->jack.ppPortsCapture[iPort] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + ma_device_uninit__jack(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } + + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ + + pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.pIntermediaryBufferCapture == NULL) { + ma_device_uninit__jack(pDevice); + return MA_OUT_OF_MEMORY; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint32 iPort; + const char** ppPorts; + + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = 0; + pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + + ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + if (ppPorts == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* Need to count the number of ports first so we can allocate some memory. */ + while (ppPorts[pDescriptorPlayback->channels] != NULL) { + pDescriptorPlayback->channels += 1; + } + + pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.ppPortsPlayback == NULL) { + ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { + char name[64]; + ma_strcpy_s(name, sizeof(name), "playback"); + ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ + + pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); + if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + ma_device_uninit__jack(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + } + + ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); + + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ + + pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); + if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { + ma_device_uninit__jack(pDevice); + return MA_OUT_OF_MEMORY; + } + } + + return MA_SUCCESS; +} + + +static ma_result ma_device_start__jack(ma_device* pDevice) +{ + ma_context* pContext = pDevice->pContext; + int resultJACK; + size_t i; + + resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); + if (resultJACK != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); + return MA_FAILED_TO_START_BACKEND_DEVICE; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); + if (ppServerPorts == NULL) { + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; + } + + for (i = 0; ppServerPorts[i] != NULL; ++i) { + const char* pServerPort = ppServerPorts[i]; + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); + + resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); + if (resultJACK != 0) { + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; + } + } + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); + if (ppServerPorts == NULL) { + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); + return MA_ERROR; + } + + for (i = 0; ppServerPorts[i] != NULL; ++i) { + const char* pServerPort = ppServerPorts[i]; + const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); + + resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); + if (resultJACK != 0) { + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); + return MA_ERROR; + } + } + + ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__jack(ma_device* pDevice) +{ + ma_context* pContext = pDevice->pContext; + + if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); + return MA_ERROR; + } + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__jack(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_jack); + + ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); + pContext->jack.pClientName = NULL; + +#ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#ifndef MA_NO_RUNTIME_LINKING + const char* libjackNames[] = { +#if defined(MA_WIN32) + "libjack.dll", + "libjack64.dll" +#endif +#if defined(MA_UNIX) + "libjack.so", + "libjack.so.0" +#endif + }; + size_t i; + + for (i = 0; i < ma_countof(libjackNames); ++i) { + pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); + if (pContext->jack.jackSO != NULL) { + break; + } + } + + if (pContext->jack.jackSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); +#else + /* + This strange assignment system is here just to ensure type safety of miniaudio's function pointer + types. If anything differs slightly the compiler should throw a warning. + */ + ma_jack_client_open_proc _jack_client_open = jack_client_open; + ma_jack_client_close_proc _jack_client_close = jack_client_close; + ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; + ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; + ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; + ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; + ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; + ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; + ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; + ma_jack_activate_proc _jack_activate = jack_activate; + ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; + ma_jack_connect_proc _jack_connect = jack_connect; + ma_jack_port_register_proc _jack_port_register = jack_port_register; + ma_jack_port_name_proc _jack_port_name = jack_port_name; + ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; + ma_jack_free_proc _jack_free = jack_free; + + pContext->jack.jack_client_open = (ma_proc)_jack_client_open; + pContext->jack.jack_client_close = (ma_proc)_jack_client_close; + pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; + pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; + pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; + pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; + pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; + pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; + pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; + pContext->jack.jack_activate = (ma_proc)_jack_activate; + pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; + pContext->jack.jack_connect = (ma_proc)_jack_connect; + pContext->jack.jack_port_register = (ma_proc)_jack_port_register; + pContext->jack.jack_port_name = (ma_proc)_jack_port_name; + pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; + pContext->jack.jack_free = (ma_proc)_jack_free; +#endif + + if (pConfig->jack.pClientName != NULL) { + pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); + } + pContext->jack.tryStartServer = pConfig->jack.tryStartServer; + + /* + Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting + a temporary client. + */ + { + ma_jack_client_t* pDummyClient; + ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); + if (result != MA_SUCCESS) { + ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); + #ifndef MA_NO_RUNTIME_LINKING + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); + #endif + return MA_NO_BACKEND; + } + + ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); + } + + + pCallbacks->onContextInit = ma_context_init__jack; + pCallbacks->onContextUninit = ma_context_uninit__jack; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; + pCallbacks->onDeviceInit = ma_device_init__jack; + pCallbacks->onDeviceUninit = ma_device_uninit__jack; + pCallbacks->onDeviceStart = ma_device_start__jack; + pCallbacks->onDeviceStop = ma_device_stop__jack; + pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* JACK */ + + + +/****************************************************************************** + +Core Audio Backend + +References +========== +- Technical Note TN2091: Device input using the HAL Output Audio Unit + https://developer.apple.com/library/archive/technotes/tn2091/_index.html + +******************************************************************************/ +#ifdef MA_HAS_COREAUDIO +#include + +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 + #define MA_APPLE_MOBILE + #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 + #define MA_APPLE_TV + #endif + #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 + #define MA_APPLE_WATCH + #endif + #if __has_feature(objc_arc) + #define MA_BRIDGE_TRANSFER __bridge_transfer + #define MA_BRIDGE_RETAINED __bridge_retained + #else + #define MA_BRIDGE_TRANSFER + #define MA_BRIDGE_RETAINED + #endif +#else + #define MA_APPLE_DESKTOP +#endif + +#if defined(MA_APPLE_DESKTOP) +#include +#else +#include +#endif + +#include + +/* CoreFoundation */ +typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); +typedef void (* ma_CFRelease_proc)(CFTypeRef cf); + +/* CoreAudio */ +#if defined(MA_APPLE_DESKTOP) +typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); +typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); +typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); +typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); +#endif + +/* AudioToolbox */ +typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); +typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); +typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); +typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); +typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); +typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); +typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); +typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); +typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); + + +#define MA_COREAUDIO_OUTPUT_BUS 0 +#define MA_COREAUDIO_INPUT_BUS 1 + +#if defined(MA_APPLE_DESKTOP) +static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); +#endif + +/* +Core Audio + +So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation +apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose +needing to figure out how this darn thing works, I'm going to outline a few things here. + +Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be +able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen +that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent +and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the +distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. + +Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When +retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific +data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the +devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be +the central APIs for retrieving information about the system and specific devices. + +To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a +structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" +which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is +typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and +kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to +kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. + +Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size +of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property +address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the +size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of +AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. +*/ + +#if defined(MA_APPLE_MOBILE) +static void ma_device__on_notification_interruption_began(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); +} + +static void ma_device__on_notification_interruption_ended(ma_device* pDevice) +{ + ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); +} +#endif + +static ma_result ma_result_from_OSStatus(OSStatus status) +{ + switch (status) + { + case noErr: return MA_SUCCESS; + #if defined(MA_APPLE_DESKTOP) + case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; + case kAudioHardwareUnspecifiedError: return MA_ERROR; + case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; + case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; + case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; + case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; + case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; + case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; + case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; + case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; + case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; + #endif + default: return MA_ERROR; + } +} + +#if 0 +static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) +{ + switch (bit) + { + case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; + case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; + case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; + case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; + case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; + case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return MA_CHANNEL_NONE; + } +} +#endif + +static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) +{ + MA_ASSERT(pDescription != NULL); + MA_ASSERT(pFormatOut != NULL); + + *pFormatOut = ma_format_unknown; /* Safety. */ + + /* There's a few things miniaudio doesn't support. */ + if (pDescription->mFormatID != kAudioFormatLinearPCM) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* We don't support any non-packed formats that are aligned high. */ + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Only supporting native-endian. */ + if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { + return MA_FORMAT_NOT_SUPPORTED; + } + + /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ + /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { + return MA_FORMAT_NOT_SUPPORTED; + }*/ + + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { + if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = ma_format_f32; + return MA_SUCCESS; + } + } else { + if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { + if (pDescription->mBitsPerChannel == 16) { + *pFormatOut = ma_format_s16; + return MA_SUCCESS; + } else if (pDescription->mBitsPerChannel == 24) { + if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { + *pFormatOut = ma_format_s24; + return MA_SUCCESS; + } else { + if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { + /* TODO: Implement ma_format_s24_32. */ + /**pFormatOut = ma_format_s24_32;*/ + /*return MA_SUCCESS;*/ + return MA_FORMAT_NOT_SUPPORTED; + } + } + } else if (pDescription->mBitsPerChannel == 32) { + *pFormatOut = ma_format_s32; + return MA_SUCCESS; + } + } else { + if (pDescription->mBitsPerChannel == 8) { + *pFormatOut = ma_format_u8; + return MA_SUCCESS; + } + } + } + + /* Getting here means the format is not supported. */ + return MA_FORMAT_NOT_SUPPORTED; +} + +#if defined(MA_APPLE_DESKTOP) +static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) +{ + switch (label) + { + case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; + case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; + case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; + case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; + case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; + case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; + case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; + case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; + case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; + case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; + case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; + case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; + case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; + case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; + case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; + case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; + case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; + case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; + case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; + case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; + case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; + case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; + case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; + case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; + case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; + case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; + case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; + case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; + case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; + case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; + case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; + case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; + case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; + case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; + case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; + + #if 0 /* Introduced in a later version of macOS. */ + case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; + case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; + case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; + case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; + case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; + case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; + case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; + case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; + case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; + case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; + case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; + case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; + case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; + case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; + case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; + case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; + case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; + case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; + #endif + + default: return MA_CHANNEL_NONE; + } +} + +static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) +{ + MA_ASSERT(pChannelLayout != NULL); + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + UInt32 iChannel; + for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { + pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); + } + } else +#if 0 + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + /* This is the same kind of system that's used by Windows audio APIs. */ + UInt32 iChannel = 0; + UInt32 iBit; + AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; + for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { + AudioChannelBitmap bit = bitmap & (1 << iBit); + if (bit != 0) { + pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); + } + } + } else +#endif + { + /* + Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should + be updated to determine the mapping based on the tag. + */ + UInt32 channelCount; + + /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ + if (channelMapCap > 0xFFFFFFFF) { + channelMapCap = 0xFFFFFFFF; + } + + channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); + + switch (pChannelLayout->mChannelLayoutTag) + { + case kAudioChannelLayoutTag_Mono: + case kAudioChannelLayoutTag_Stereo: + case kAudioChannelLayoutTag_StereoHeadphones: + case kAudioChannelLayoutTag_MatrixStereo: + case kAudioChannelLayoutTag_MidSide: + case kAudioChannelLayoutTag_XY: + case kAudioChannelLayoutTag_Binaural: + case kAudioChannelLayoutTag_Ambisonic_B_Format: + { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } break; + + case kAudioChannelLayoutTag_Octagonal: + { + pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; + pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Hexagonal: + { + pChannelMap[5] = MA_CHANNEL_BACK_CENTER; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Pentagonal: + { + pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; + } MA_FALLTHROUGH; /* Intentional fallthrough. */ + case kAudioChannelLayoutTag_Quadraphonic: + { + pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; + pChannelMap[2] = MA_CHANNEL_BACK_LEFT; + pChannelMap[1] = MA_CHANNEL_RIGHT; + pChannelMap[0] = MA_CHANNEL_LEFT; + } break; + + /* TODO: Add support for more tags here. */ + + default: + { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } break; + } + } + + return MA_SUCCESS; +} + +#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ + (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain +#else +/* kAudioObjectPropertyElementMaster is deprecated. */ +#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster +#endif + +static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddressDevices; + UInt32 deviceObjectsDataSize; + OSStatus status; + AudioObjectID* pDeviceObjectIDs; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceCount != NULL); + MA_ASSERT(ppDeviceObjectIDs != NULL); + + /* Safety. */ + *pDeviceCount = 0; + *ppDeviceObjectIDs = NULL; + + propAddressDevices.mSelector = kAudioHardwarePropertyDevices; + propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); + if (pDeviceObjectIDs == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); + if (status != noErr) { + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); + *ppDeviceObjectIDs = pDeviceObjectIDs; + + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + propAddress.mSelector = kAudioDevicePropertyDeviceUID; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(*pUID); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + CFStringRef uid; + ma_result result; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); + if (result != MA_SUCCESS) { + return result; + } + + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MA_ERROR; + } + + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) +{ + AudioObjectPropertyAddress propAddress; + CFStringRef deviceName = NULL; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(deviceName); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { + return MA_ERROR; + } + + ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); + return MA_SUCCESS; +} + +static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioBufferList* pBufferList; + ma_bool32 isSupported; + + MA_ASSERT(pContext != NULL); + + /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ + propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; + propAddress.mScope = scope; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return MA_FALSE; + } + + pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pBufferList == NULL) { + return MA_FALSE; /* Out of memory. */ + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); + if (status != noErr) { + ma_free(pBufferList, &pContext->allocationCallbacks); + return MA_FALSE; + } + + isSupported = MA_FALSE; + if (pBufferList->mNumberBuffers > 0) { + isSupported = MA_TRUE; + } + + ma_free(pBufferList, &pContext->allocationCallbacks); + return isSupported; +} + +static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) +{ + return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); +} + +static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) +{ + return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); +} + + +static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioStreamRangedDescription* pDescriptions; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDescriptionCount != NULL); + MA_ASSERT(ppDescriptions != NULL); + + /* + TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My + MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. + */ + propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pDescriptions == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); + if (status != noErr) { + ma_free(pDescriptions, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pDescriptionCount = dataSize / sizeof(*pDescriptions); + *ppDescriptions = pDescriptions; + return MA_SUCCESS; +} + + +static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioChannelLayout* pChannelLayout; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(ppChannelLayout != NULL); + + *ppChannelLayout = NULL; /* Safety. */ + + propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pChannelLayout == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); + if (status != noErr) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *ppChannelLayout = pChannelLayout; + return MA_SUCCESS; +} + +static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) +{ + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pChannelCount != NULL); + + *pChannelCount = 0; /* Safety. */ + + result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MA_SUCCESS) { + return result; + } + + if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { + *pChannelCount = pChannelLayout->mNumberChannelDescriptions; + } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { + *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); + } else { + *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return MA_SUCCESS; +} + +#if 0 +static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) +{ + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); + if (result != MA_SUCCESS) { + return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ + } + + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; +} +#endif + +static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ +{ + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + AudioValueRange* pSampleRateRanges; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pSampleRateRangesCount != NULL); + MA_ASSERT(ppSampleRateRanges != NULL); + + /* Safety. */ + *pSampleRateRangesCount = 0; + *ppSampleRateRanges = NULL; + + propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); + if (pSampleRateRanges == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); + if (status != noErr) { + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); + *ppSampleRateRanges = pSampleRateRanges; + return MA_SUCCESS; +} + +#if 0 +static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) +{ + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + ma_result result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pSampleRateOut != NULL); + + *pSampleRateOut = 0; /* Safety. */ + + result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MA_SUCCESS) { + return result; + } + + if (sampleRateRangeCount == 0) { + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_ERROR; /* Should never hit this case should we? */ + } + + if (sampleRateIn == 0) { + /* Search in order of miniaudio's preferred priority. */ + UInt32 iMALSampleRate; + for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { + ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; + UInt32 iCASampleRate; + for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { + AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; + if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { + *pSampleRateOut = malSampleRate; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } + + /* + If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this + case we just fall back to the first one reported by Core Audio. + */ + MA_ASSERT(sampleRateRangeCount > 0); + + *pSampleRateOut = pSampleRateRanges[0].mMinimum; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } else { + /* Find the closest match to this sample rate. */ + UInt32 currentAbsoluteDifference = INT32_MAX; + UInt32 iCurrentClosestRange = (UInt32)-1; + UInt32 iRange; + for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { + if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { + *pSampleRateOut = sampleRateIn; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } else { + UInt32 absoluteDifference; + if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { + absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; + } else { + absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; + } + + if (currentAbsoluteDifference > absoluteDifference) { + currentAbsoluteDifference = absoluteDifference; + iCurrentClosestRange = iRange; + } + } + } + + MA_ASSERT(iCurrentClosestRange != (UInt32)-1); + + *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + + /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ + /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ + /*return MA_ERROR;*/ +} +#endif + +static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) +{ + AudioObjectPropertyAddress propAddress; + AudioValueRange bufferSizeRange; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pBufferSizeInFramesOut != NULL); + + *pBufferSizeInFramesOut = 0; /* Safety. */ + + propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + dataSize = sizeof(bufferSizeRange); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + /* This is just a clamp. */ + if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { + *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; + } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { + *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; + } else { + *pBufferSizeInFramesOut = bufferSizeInFramesIn; + } + + return MA_SUCCESS; +} + +static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) +{ + ma_result result; + ma_uint32 chosenBufferSizeInFrames; + AudioObjectPropertyAddress propAddress; + UInt32 dataSize; + OSStatus status; + + MA_ASSERT(pContext != NULL); + + result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); + if (result != MA_SUCCESS) { + return result; + } + + /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ + propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); + + /* Get the actual size of the buffer. */ + dataSize = sizeof(*pPeriodSizeInOut); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + *pPeriodSizeInOut = chosenBufferSizeInFrames; + return MA_SUCCESS; +} + +static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) +{ + AudioObjectPropertyAddress propAddressDefaultDevice; + UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); + AudioObjectID defaultDeviceObjectID; + OSStatus status; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceObjectID != NULL); + + /* Safety. */ + *pDeviceObjectID = 0; + + propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; + propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + if (deviceType == ma_device_type_playback) { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + } else { + propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; + } + + defaultDeviceObjectIDSize = sizeof(AudioObjectID); + status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); + if (status == noErr) { + *pDeviceObjectID = defaultDeviceObjectID; + return MA_SUCCESS; + } + + /* If we get here it means we couldn't find the device. */ + return MA_NO_DEVICE; +} + +static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceObjectID != NULL); + + /* Safety. */ + *pDeviceObjectID = 0; + + if (pDeviceID == NULL) { + /* Default device. */ + return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); + } else { + /* Explicit device. */ + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + ma_result result; + UInt32 iDevice; + + result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MA_SUCCESS) { + return result; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + + char uid[256]; + if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { + continue; + } + + if (deviceType == ma_device_type_playback) { + if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } else { + if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (strcmp(uid, pDeviceID->coreaudio) == 0) { + *pDeviceObjectID = deviceObjectID; + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + return MA_SUCCESS; + } + } + } + } + + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); + } + + /* If we get here it means we couldn't find the device. */ + return MA_NO_DEVICE; +} + + +static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) +{ + UInt32 deviceFormatDescriptionCount; + AudioStreamRangedDescription* pDeviceFormatDescriptions; + ma_result result; + ma_uint32 desiredSampleRate; + ma_uint32 desiredChannelCount; + ma_format desiredFormat; + AudioStreamBasicDescription bestDeviceFormatSoFar; + ma_bool32 hasSupportedFormat; + UInt32 iFormat; + + result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); + if (result != MA_SUCCESS) { + return result; + } + + desiredSampleRate = sampleRate; + if (desiredSampleRate == 0) { + desiredSampleRate = pOrigFormat->mSampleRate; + } + + desiredChannelCount = channels; + if (desiredChannelCount == 0) { + desiredChannelCount = pOrigFormat->mChannelsPerFrame; + } + + desiredFormat = format; + if (desiredFormat == ma_format_unknown) { + result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); + if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { + desiredFormat = g_maFormatPriorities[0]; + } + } + + /* + If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next + loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. + */ + MA_ZERO_OBJECT(&bestDeviceFormatSoFar); + + hasSupportedFormat = MA_FALSE; + for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + ma_format formatFromDescription; + ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription); + if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) { + hasSupportedFormat = MA_TRUE; + bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; + break; + } + } + + if (!hasSupportedFormat) { + ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); + return MA_FORMAT_NOT_SUPPORTED; + } + + + for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { + AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; + ma_format thisSampleFormat; + ma_result formatResult; + ma_format bestSampleFormatSoFar; + + /* If the format is not supported by miniaudio we need to skip this one entirely. */ + formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); + if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { + continue; /* The format is not supported by miniaudio. Skip. */ + } + + ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); + + /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ + if (thisDeviceFormat.mSampleRate != desiredSampleRate) { + /* + The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format + so far has an equal sample rate we can just ignore this one. + */ + if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { + continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ + } else { + /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ + if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { + /* This format has a different sample rate _and_ a different channel count. */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; /* No change to the best format. */ + } else { + /* + Both this format and the best so far have different sample rates and different channel counts. Whichever has the + best format is the new best. + */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format. */ + } + } + } else { + /* This format has a different sample rate but the desired channel count. */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } else { + /* This format has the desired channel count, but the best so far does not. We have a new best. */ + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } + } + } + } else { + /* + The sample rates match which makes this format a very high priority contender. If the best format so far has a different + sample rate it needs to be replaced with this one. + */ + if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ + if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { + /* + In this case this format has the same channel count as what the client is requesting. If the best format so far has + a different count, this one becomes the new best. + */ + if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ + if (thisSampleFormat == desiredFormat) { + bestDeviceFormatSoFar = thisDeviceFormat; + break; /* Found the exact match. */ + } else { + /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } + } + } else { + /* + In this case the channel count is different to what the client has requested. If the best so far has the same channel + count as the requested count then it remains the best. + */ + if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { + continue; + } else { + /* + This is the case where both have the same sample rate (good) but different channel counts. Right now both have about + the same priority, but we need to compare the format now. + */ + if (thisSampleFormat == bestSampleFormatSoFar) { + if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { + bestDeviceFormatSoFar = thisDeviceFormat; + continue; + } else { + continue; /* No change to the best format for now. */ + } + } + } + } + } + } + } + + *pFormat = bestDeviceFormatSoFar; + + ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); + return MA_SUCCESS; +} + +static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) +{ + AudioUnitScope deviceScope; + AudioUnitElement deviceBus; + UInt32 channelLayoutSize; + OSStatus status; + AudioChannelLayout* pChannelLayout; + ma_result result; + + MA_ASSERT(pContext != NULL); + + if (deviceType == ma_device_type_playback) { + deviceScope = kAudioUnitScope_Input; + deviceBus = MA_COREAUDIO_OUTPUT_BUS; + } else { + deviceScope = kAudioUnitScope_Output; + deviceBus = MA_COREAUDIO_INPUT_BUS; + } + + status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); + if (pChannelLayout == NULL) { + return MA_OUT_OF_MEMORY; + } + + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); + if (status != noErr) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return ma_result_from_OSStatus(status); + } + + result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return result; + } + + ma_free(pChannelLayout, &pContext->allocationCallbacks); + return MA_SUCCESS; +} +#endif /* MA_APPLE_DESKTOP */ + + +#if !defined(MA_APPLE_DESKTOP) +static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) +{ + MA_ZERO_OBJECT(pInfo); + ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); + ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); +} +#endif + +static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ +#if defined(MA_APPLE_DESKTOP) + UInt32 deviceCount; + AudioObjectID* pDeviceObjectIDs; + AudioObjectID defaultDeviceObjectIDPlayback; + AudioObjectID defaultDeviceObjectIDCapture; + ma_result result; + UInt32 iDevice; + + ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ + ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ + + result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); + if (result != MA_SUCCESS) { + return result; + } + + for (iDevice = 0; iDevice < deviceCount; ++iDevice) { + AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; + ma_device_info info; + + MA_ZERO_OBJECT(&info); + if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { + continue; + } + if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { + continue; + } + + if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { + if (deviceObjectID == defaultDeviceObjectIDPlayback) { + info.isDefault = MA_TRUE; + } + + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { + break; + } + } + if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { + if (deviceObjectID == defaultDeviceObjectIDCapture) { + info.isDefault = MA_TRUE; + } + + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { + break; + } + } + } + + ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); +#else + ma_device_info info; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; + + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { + return MA_SUCCESS; + } + } + + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); + if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { + return MA_SUCCESS; + } + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + + MA_ASSERT(pContext != NULL); + +#if defined(MA_APPLE_DESKTOP) + /* Desktop */ + { + AudioObjectID deviceObjectID; + AudioObjectID defaultDeviceObjectID; + UInt32 streamDescriptionCount; + AudioStreamRangedDescription* pStreamDescriptions; + UInt32 iStreamDescription; + UInt32 sampleRateRangeCount; + AudioValueRange* pSampleRateRanges; + + ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ + + result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); + if (result != MA_SUCCESS) { + return result; + } + + if (deviceObjectID == defaultDeviceObjectID) { + pDeviceInfo->isDefault = MA_TRUE; + } + + /* + There could be a large number of permutations here. Fortunately there is only a single channel count + being reported which reduces this quite a bit. For sample rates we're only reporting those that are + one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into + our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen + if some driver performs software data conversion and therefore reports every possible format and + sample rate. + */ + pDeviceInfo->nativeDataFormatCount = 0; + + /* Formats. */ + { + ma_format uniqueFormats[ma_format_count]; + ma_uint32 uniqueFormatCount = 0; + ma_uint32 channels; + + /* Channels. */ + result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); + if (result != MA_SUCCESS) { + return result; + } + + /* Formats. */ + result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); + if (result != MA_SUCCESS) { + return result; + } + + for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { + ma_format format; + ma_bool32 hasFormatBeenHandled = MA_FALSE; + ma_uint32 iOutputFormat; + ma_uint32 iSampleRate; + + result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); + if (result != MA_SUCCESS) { + continue; + } + + MA_ASSERT(format != ma_format_unknown); + + /* Make sure the format isn't already in the output list. */ + for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { + if (uniqueFormats[iOutputFormat] == format) { + hasFormatBeenHandled = MA_TRUE; + break; + } + } + + /* If we've already handled this format just skip it. */ + if (hasFormatBeenHandled) { + continue; + } + + uniqueFormats[uniqueFormatCount] = format; + uniqueFormatCount += 1; + + /* Sample Rates */ + result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); + if (result != MA_SUCCESS) { + return result; + } + + /* + Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are + between this range. + */ + for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { + ma_uint32 iStandardSampleRate; + for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; + if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { + /* We have a new data format. Add it to the list. */ + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; + + if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { + break; /* No more room for any more formats. */ + } + } + } + } + + ma_free(pSampleRateRanges, &pContext->allocationCallbacks); + + if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { + break; /* No more room for any more formats. */ + } + } + + ma_free(pStreamDescriptions, &pContext->allocationCallbacks); + } + } +#else + /* Mobile */ + { + AudioComponentDescription desc; + AudioComponent component; + AudioUnit audioUnit; + OSStatus status; + AudioUnitScope formatScope; + AudioUnitElement formatElement; + AudioStreamBasicDescription bestFormat; + UInt32 propSize; + + /* We want to ensure we use a consistent device name to device enumeration. */ + if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { + ma_bool32 found = MA_FALSE; + if (deviceType == ma_device_type_playback) { + NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; + for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } else { + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); + found = MA_TRUE; + break; + } + } + } + + if (!found) { + return MA_DOES_NOT_EXIST; + } + } else { + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + } + + + /* + Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is + reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to + retrieve from the AVAudioSession shared instance. + */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (component == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; + + propSize = sizeof(bestFormat); + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + return ma_result_from_OSStatus(status); + } + + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); + audioUnit = NULL; + + /* Only a single format is being reported for iOS. */ + pDeviceInfo->nativeDataFormatCount = 1; + + result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); + if (result != MA_SUCCESS) { + return result; + } + + pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; + + /* + It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do + this we just get the shared instance and inspect. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; + } + } +#endif + + (void)pDeviceInfo; /* Unused. */ + return MA_SUCCESS; +} + +static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) +{ + AudioBufferList* pBufferList; + UInt32 audioBufferSizeInBytes; + size_t allocationSize; + + MA_ASSERT(sizeInFrames > 0); + MA_ASSERT(format != ma_format_unknown); + MA_ASSERT(channels > 0); + + allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ + if (layout == ma_stream_layout_interleaved) { + /* Interleaved case. This is the simple case because we just have one buffer. */ + allocationSize += sizeof(AudioBuffer) * 1; + } else { + /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ + allocationSize += sizeof(AudioBuffer) * channels; + } + + allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); + + pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); + if (pBufferList == NULL) { + return NULL; + } + + audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); + + if (layout == ma_stream_layout_interleaved) { + pBufferList->mNumberBuffers = 1; + pBufferList->mBuffers[0].mNumberChannels = channels; + pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; + pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); + } else { + ma_uint32 iBuffer; + pBufferList->mNumberBuffers = channels; + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + pBufferList->mBuffers[iBuffer].mNumberChannels = 1; + pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; + pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); + } + } + + return pBufferList; +} + +static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(format != ma_format_unknown); + MA_ASSERT(channels > 0); + + /* Only resize the buffer if necessary. */ + if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { + AudioBufferList* pNewAudioBufferList; + + pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); + if (pNewAudioBufferList == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* At this point we'll have a new AudioBufferList and we can free the old one. */ + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; + } + + /* Getting here means the capacity of the audio is fine. */ + return MA_SUCCESS; +} + + +static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_stream_layout layout; + + MA_ASSERT(pDevice != NULL); + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ + + /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ + layout = ma_stream_layout_interleaved; + if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { + layout = ma_stream_layout_deinterleaved; + } + + if (layout == ma_stream_layout_interleaved) { + /* For now we can assume everything is interleaved. */ + UInt32 iBuffer; + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { + if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { + ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + if (frameCountForThisBuffer > 0) { + ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); + } + + /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } else { + /* + This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just + output silence here. + */ + MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } + } + } else { + /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ + MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ + + /* + For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something + very strange has happened and we're not going to support it. + */ + if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { + ma_uint8 tempBuffer[4096]; + UInt32 iBuffer; + + for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { + ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); + ma_uint32 framesRemaining = frameCountPerBuffer; + + while (framesRemaining > 0) { + void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; + ma_uint32 iChannel; + ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + if (framesToRead > framesRemaining) { + framesToRead = framesRemaining; + } + + ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); + + for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { + ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); + } + + ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); + + framesRemaining -= framesToRead; + } + } + } + } + + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + (void)frameCount; + + return noErr; +} + +static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) +{ + ma_device* pDevice = (ma_device*)pUserData; + AudioBufferList* pRenderedBufferList; + ma_result result; + ma_stream_layout layout; + ma_uint32 iBuffer; + OSStatus status; + + MA_ASSERT(pDevice != NULL); + + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + MA_ASSERT(pRenderedBufferList); + + /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ + layout = ma_stream_layout_interleaved; + if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { + layout = ma_stream_layout_deinterleaved; + } + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ + + /* + There has been a situation reported where frame count passed into this function is greater than the capacity of + our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, + so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the + number of frames requested by this callback. + */ + result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); + return noErr; + } + + pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; + MA_ASSERT(pRenderedBufferList); + + /* + When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes + that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer + being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a + problem when a future call to this callback specifies a larger number of frames. + + To work around this we need to explicitly set the size of each buffer to their respective size in bytes. + */ + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { + pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; + } + + status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); + if (status != noErr) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); + return status; + } + + if (layout == ma_stream_layout_interleaved) { + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { + if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { + ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } else { + /* + This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's + not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. + */ + ma_uint8 silentBuffer[4096]; + ma_uint32 framesRemaining; + + MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); + + framesRemaining = frameCount; + while (framesRemaining > 0) { + ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + if (framesToSend > framesRemaining) { + framesToSend = framesRemaining; + } + + ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); + + framesRemaining -= framesToSend; + } + + /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ + } + } + } else { + /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ + MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ + + /* + For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something + very strange has happened and we're not going to support it. + */ + if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { + ma_uint8 tempBuffer[4096]; + for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { + ma_uint32 framesRemaining = frameCount; + while (framesRemaining > 0) { + void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; + ma_uint32 iChannel; + ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + if (framesToSend > framesRemaining) { + framesToSend = framesRemaining; + } + + for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { + ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); + } + + ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); + ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); + + framesRemaining -= framesToSend; + } + } + } + } + + (void)pActionFlags; + (void)pTimeStamp; + (void)busNumber; + (void)frameCount; + (void)pUnusedBufferList; + + return noErr; +} + +static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ + if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + return; + } + + /* + There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like + AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) + can try waiting on the same lock. I'm going to try working around this by not calling any Core + Audio APIs in the callback when the device has been stopped or uninitialized. + */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { + ma_device__on_notification_stopped(pDevice); + } else { + UInt32 isRunning; + UInt32 isRunningSize = sizeof(isRunning); + OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); + if (status != noErr) { + goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ + } + + 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 (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { + /* + 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 stopped event and thinking that the device has stopped when it + hasn't!). + */ + if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || + ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { + goto done; + } + + /* + 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 stopped callback needs to be posted. + */ + goto done; + } + + /* Getting here means we need to stop the device. */ + ma_device__on_notification_stopped(pDevice); + } + } + + (void)propertyID; /* Unused. */ + +done: + /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ + ma_event_signal(&pDevice->coreaudio.stopEvent); +} + +#if defined(MA_APPLE_DESKTOP) +static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ +static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; +static ma_mutex g_DeviceTrackingMutex_CoreAudio; +static ma_device** g_ppTrackedDevices_CoreAudio = NULL; +static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; +static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; + +static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) +{ + ma_device_type deviceType; + + /* Not sure if I really need to check this, but it makes me feel better. */ + if (addressCount == 0) { + return noErr; + } + + if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { + deviceType = ma_device_type_playback; + } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { + deviceType = ma_device_type_capture; + } else { + return noErr; /* Should never hit this. */ + } + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + ma_result reinitResult; + ma_device* pDevice; + + pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; + if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback) { + pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; + } else { + pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; + reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); + pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; + } + + if (reinitResult == MA_SUCCESS) { + ma_device__post_init_setup(pDevice, deviceType); + + /* Restart the device if required. If this fails we need to stop the device entirely. */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + OSStatus status; + if (deviceType == ma_device_type_playback) { + status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } else if (deviceType == ma_device_type_capture) { + status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } + } + + ma_device__on_notification_rerouted(pDevice); + } + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + /* Unused parameters. */ + (void)objectID; + (void)pUserData; + + return noErr; +} + +static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + /* Don't do anything if we've already initializd device tracking. */ + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + } + g_DeviceTrackingInitCounter_CoreAudio += 1; + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); + { + if (g_DeviceTrackingInitCounter_CoreAudio > 0) + g_DeviceTrackingInitCounter_CoreAudio -= 1; + + if (g_DeviceTrackingInitCounter_CoreAudio == 0) { + AudioObjectPropertyAddress propAddress; + propAddress.mScope = kAudioObjectPropertyScopeGlobal; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); + + /* At this point there should be no tracked devices. If not there's an error somewhere. */ + if (g_ppTrackedDevices_CoreAudio != NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + return MA_INVALID_OPERATION; + } + + ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); + } + } + ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__track__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + /* Allocate memory if required. */ + if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { + ma_uint32 newCap; + ma_device** ppNewDevices; + + newCap = g_TrackedDeviceCap_CoreAudio * 2; + if (newCap == 0) { + newCap = 1; + } + + ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); + if (ppNewDevices == NULL) { + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + return MA_OUT_OF_MEMORY; + } + + g_ppTrackedDevices_CoreAudio = ppNewDevices; + g_TrackedDeviceCap_CoreAudio = newCap; + } + + g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; + g_TrackedDeviceCount_CoreAudio += 1; + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} + +static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); + { + ma_uint32 iDevice; + for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { + if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { + /* We've found the device. We now need to remove it from the list. */ + ma_uint32 jDevice; + for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { + g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; + } + + g_TrackedDeviceCount_CoreAudio -= 1; + + /* If there's nothing else in the list we need to free memory. */ + if (g_TrackedDeviceCount_CoreAudio == 0) { + ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); + g_ppTrackedDevices_CoreAudio = NULL; + g_TrackedDeviceCap_CoreAudio = 0; + } + + break; + } + } + } + ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); + + return MA_SUCCESS; +} +#endif + +#if defined(MA_APPLE_MOBILE) +@interface ma_ios_notification_handler:NSObject { + ma_device* m_pDevice; +} +@end + +@implementation ma_ios_notification_handler +-(id)init:(ma_device*)pDevice +{ + self = [super init]; + m_pDevice = pDevice; + + /* For route changes. */ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; + + /* For interruptions. */ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; + + return self; +} + +-(void)dealloc +{ + [self remove_handler]; + + #if defined(__has_feature) + #if !__has_feature(objc_arc) + [super dealloc]; + #endif + #endif +} + +-(void)remove_handler +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; +} + +-(void)handle_interruption:(NSNotification*)pNotification +{ + NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) + { + case AVAudioSessionInterruptionTypeBegan: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); + + /* + Core Audio will have stopped the internal device automatically, but we need explicitly + stop it at a higher level to ensure miniaudio-specific state is updated for consistency. + */ + ma_device_stop(m_pDevice); + + /* + Fire the notification after the device has been stopped to ensure it's in the correct + state when the notification handler is invoked. + */ + ma_device__on_notification_interruption_began(m_pDevice); + } break; + + case AVAudioSessionInterruptionTypeEnded: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); + ma_device__on_notification_interruption_ended(m_pDevice); + } break; + } +} + +-(void)handle_route_change:(NSNotification*)pNotification +{ + AVAudioSession* pSession = [AVAudioSession sharedInstance]; + + NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; + switch (reason) + { + case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); + } break; + + case AVAudioSessionRouteChangeReasonNewDeviceAvailable: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); + } break; + + case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); + } break; + + case AVAudioSessionRouteChangeReasonWakeFromSleep: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); + } break; + + case AVAudioSessionRouteChangeReasonOverride: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); + } break; + + case AVAudioSessionRouteChangeReasonCategoryChange: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); + } break; + + case AVAudioSessionRouteChangeReasonUnknown: + default: + { + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); + } break; + } + + ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); + + /* Let the application know about the route change. */ + ma_device__on_notification_rerouted(m_pDevice); +} +@end +#endif + +static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); + +#if defined(MA_APPLE_DESKTOP) + /* + Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll + just gracefully ignore it. + */ + ma_device__untrack__coreaudio(pDevice); +#endif +#if defined(MA_APPLE_MOBILE) + if (pDevice->coreaudio.pNotificationHandler != NULL) { + ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; + [pNotificationHandler remove_handler]; + } +#endif + + if (pDevice->coreaudio.audioUnitCapture != NULL) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + if (pDevice->coreaudio.audioUnitPlayback != NULL) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +typedef struct +{ + ma_bool32 allowNominalSampleRateChange; + + /* Input. */ + ma_format formatIn; + ma_uint32 channelsIn; + ma_uint32 sampleRateIn; + ma_channel channelMapIn[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesIn; + ma_uint32 periodSizeInMillisecondsIn; + ma_uint32 periodsIn; + ma_share_mode shareMode; + ma_performance_profile performanceProfile; + ma_bool32 registerStopEvent; + + /* Output. */ +#if defined(MA_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + AudioComponent component; + AudioUnit audioUnit; + AudioBufferList* pAudioBufferList; /* Only used for input devices. */ + ma_format formatOut; + ma_uint32 channelsOut; + ma_uint32 sampleRateOut; + ma_channel channelMapOut[MA_MAX_CHANNELS]; + ma_uint32 periodSizeInFramesOut; + ma_uint32 periodsOut; + char deviceName[256]; +} ma_device_init_internal_data__coreaudio; + +static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ +{ + ma_result result; + OSStatus status; + UInt32 enableIOFlag; + AudioStreamBasicDescription bestFormat; + UInt32 actualPeriodSizeInFrames; + AURenderCallbackStruct callbackInfo; +#if defined(MA_APPLE_DESKTOP) + AudioObjectID deviceObjectID; +#endif + + /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(pContext != NULL); + MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); + +#if defined(MA_APPLE_DESKTOP) + pData->deviceObjectID = 0; +#endif + pData->component = NULL; + pData->audioUnit = NULL; + pData->pAudioBufferList = NULL; + +#if defined(MA_APPLE_DESKTOP) + result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); + if (result != MA_SUCCESS) { + return result; + } + + 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. */ + pData->periodsOut = pData->periodsIn; + if (pData->periodsOut == 0) { + pData->periodsOut = MA_DEFAULT_PERIODS; + } + if (pData->periodsOut > 16) { + pData->periodsOut = 16; + } + + + /* Audio unit. */ + status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + + + /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ + enableIOFlag = 1; + if (deviceType == ma_device_type_capture) { + enableIOFlag = 0; + } + + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + enableIOFlag = (enableIOFlag == 0) ? 1 : 0; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_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(MA_APPLE_DESKTOP) + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(result); + } +#else + /* + For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change + the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. + */ + if (pDeviceID != NULL) { + if (deviceType == ma_device_type_capture) { + ma_bool32 found = MA_FALSE; + NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; + for (AVAudioSessionPortDescription* pPortDesc in pInputs) { + if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { + [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; + found = MA_TRUE; + break; + } + } + + if (found == MA_FALSE) { + return MA_DOES_NOT_EXIST; + } + } + } +#endif + + /* + Format. This is the hardest part of initialization because there's a few variables to take into account. + 1) The format must be supported by the device. + 2) The format must be supported miniaudio. + 3) There's a priority that miniaudio prefers. + + Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The + most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same + for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. + + On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. + */ + { + AudioStreamBasicDescription origFormat; + UInt32 origFormatSize = sizeof(origFormat); + AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; + AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; + + if (deviceType == ma_device_type_playback) { + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); + } else { + status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); + } + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + #if defined(MA_APPLE_DESKTOP) + result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); + if (result != MA_SUCCESS) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + /* + Technical Note TN2091: Device input using the HAL Output Audio Unit + https://developer.apple.com/library/archive/technotes/tn2091/_index.html + + This documentation says the following: + + The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY + variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate + conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with + another AudioConverter. + + The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We + therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it + safe and apply the same rule to output as well. + + I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() + returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but + this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. + + Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with + this, however, is that it actually changes the sample rate at the operating system level and not just the application. This + could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a + configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample + rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run + the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is + changed by miniaudio. + */ + if (pData->allowNominalSampleRateChange) { + AudioValueRange sampleRateRange; + AudioObjectPropertyAddress propAddress; + + sampleRateRange.mMinimum = bestFormat.mSampleRate; + sampleRateRange.mMaximum = bestFormat.mSampleRate; + + propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; + propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; + propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; + + status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); + if (status != noErr) { + bestFormat.mSampleRate = origFormat.mSampleRate; + } + } else { + bestFormat.mSampleRate = origFormat.mSampleRate; + } + + status = ((ma_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 + bestFormat = origFormat; + + /* + Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try + setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since + it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I + can tell, it looks like the sample rate is shared between playback and capture for everything. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; + bestFormat.mSampleRate = pAudioSession.sampleRate; + + /* + I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with + AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. + */ + if (deviceType == ma_device_type_playback) { + bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; + } + if (deviceType == ma_device_type_capture) { + bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; + } + } + + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + #endif + + result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); + if (result != MA_SUCCESS) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return result; + } + + if (pData->formatOut == ma_format_unknown) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MA_FORMAT_NOT_SUPPORTED; + } + + pData->channelsOut = bestFormat.mChannelsPerFrame; + pData->sampleRateOut = bestFormat.mSampleRate; + } + + /* Clamp the channel count for safety. */ + if (pData->channelsOut > MA_MAX_CHANNELS) { + pData->channelsOut = MA_MAX_CHANNELS; + } + + /* + Internal channel map. This is weird in my testing. If I use the AudioObject to get the + channel map, the channel descriptions are set to "Unknown" for some reason. To work around + this it looks like retrieving it from the AudioUnit will work. However, and this is where + it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore + I'm going to fall back to a default assumption in these cases. + */ +#if defined(MA_APPLE_DESKTOP) + result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); + if (result != MA_SUCCESS) { + #if 0 + /* Try falling back to the channel map from the AudioObject. */ + result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); + if (result != MA_SUCCESS) { + return result; + } + #else + /* Fall back to default assumptions. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + #endif + } +#else + /* TODO: Figure out how to get the channel map using AVAudioSession. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); +#endif + + + /* Buffer size. Not allowing this to be configurable on iOS. */ + if (pData->periodSizeInFramesIn == 0) { + if (pData->periodSizeInMillisecondsIn == 0) { + if (pData->performanceProfile == ma_performance_profile_low_latency) { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); + } else { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); + } + } else { + actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); + } + } else { + actualPeriodSizeInFrames = pData->periodSizeInFramesIn; + } + +#if defined(MA_APPLE_DESKTOP) + result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); + if (result != MA_SUCCESS) { + return result; + } +#else + /* + On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point + number. I don't trust any potential truncation errors due to converting from float to integer + so I'm going to explicitly set the actual period size to the next power of 2. + */ + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + MA_ASSERT(pAudioSession != NULL); + + [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; + actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); + } +#endif + + + /* + During testing I discovered that the buffer size can be too big. You'll get an error like this: + + kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 + + Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that + of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. + */ + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; + + /* We need a buffer list if this is an input device. We render into this in the input callback. */ + if (deviceType == ma_device_type_capture) { + ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + AudioBufferList* pBufferList; + + pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); + if (pBufferList == NULL) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return MA_OUT_OF_MEMORY; + } + + pData->pAudioBufferList = pBufferList; + } + + /* Callbacks. */ + callbackInfo.inputProcRefCon = pDevice_DoNotReference; + if (deviceType == ma_device_type_playback) { + callbackInfo.inputProc = ma_on_output__coreaudio; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } else { + callbackInfo.inputProc = ma_on_input__coreaudio; + status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } + + /* We need to listen for stop events. */ + if (pData->registerStopEvent) { + status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); + if (status != noErr) { + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + } + + /* Initialize the audio unit. */ + status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); + if (status != noErr) { + ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); + pData->pAudioBufferList = NULL; + ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); + return ma_result_from_OSStatus(status); + } + + /* Grab the name. */ +#if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); +#else + if (deviceType == ma_device_type_playback) { + ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); + } else { + ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); + } +#endif + + return result; +} + +#if defined(MA_APPLE_DESKTOP) +static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) +{ + ma_device_init_internal_data__coreaudio data; + ma_result result; + + /* This should only be called for playback or capture, not duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ + + if (deviceType == ma_device_type_capture) { + data.formatIn = pDevice->capture.format; + data.channelsIn = pDevice->capture.channels; + data.sampleRateIn = pDevice->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); + data.shareMode = pDevice->capture.shareMode; + data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.registerStopEvent = MA_TRUE; + + if (disposePreviousAudioUnit) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + } else if (deviceType == ma_device_type_playback) { + data.formatIn = pDevice->playback.format; + data.channelsIn = pDevice->playback.channels; + data.sampleRateIn = pDevice->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); + data.shareMode = pDevice->playback.shareMode; + data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; + data.registerStopEvent = (pDevice->type != ma_device_type_duplex); + + if (disposePreviousAudioUnit) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + } + } + data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; + data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; + data.periodsIn = pDevice->coreaudio.originalPeriods; + + /* Need at least 3 periods for duplex. */ + if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { + data.periodsIn = 3; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + return result; + } + + if (deviceType == ma_device_type_capture) { + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + #endif + pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; + + pDevice->capture.internalFormat = data.formatOut; + pDevice->capture.internalChannels = data.channelsOut; + pDevice->capture.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->capture.internalPeriods = data.periodsOut; + } else if (deviceType == ma_device_type_playback) { + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + #endif + pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; + + pDevice->playback.internalFormat = data.formatOut; + pDevice->playback.internalChannels = data.channelsOut; + pDevice->playback.internalSampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; + pDevice->playback.internalPeriods = data.periodsOut; + } + + return MA_SUCCESS; +} +#endif /* MA_APPLE_DESKTOP */ + +static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with the Core Audio backend for now. */ + if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Capture needs to be initialized first. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__coreaudio data; + data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.formatIn = pDescriptorCapture->format; + data.channelsIn = pDescriptorCapture->channels; + data.sampleRateIn = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; + data.periodsIn = pDescriptorCapture->periodCount; + data.shareMode = pDescriptorCapture->shareMode; + data.performanceProfile = pConfig->performanceProfile; + data.registerStopEvent = MA_TRUE; + + /* Need at least 3 periods for duplex. */ + if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { + data.periodsIn = 3; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + return result; + } + + pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; + #endif + pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; + pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; + pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; + pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; + pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; + pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + + pDescriptorCapture->format = data.formatOut; + pDescriptorCapture->channels = data.channelsOut; + pDescriptorCapture->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorCapture->periodCount = data.periodsOut; + + #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); + + /* + 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. + */ + if (pConfig->capture.pDeviceID == NULL) { + ma_device__track__coreaudio(pDevice); + } + #endif + } + + /* Playback. */ + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_device_init_internal_data__coreaudio data; + data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; + data.formatIn = pDescriptorPlayback->format; + data.channelsIn = pDescriptorPlayback->channels; + data.sampleRateIn = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + data.shareMode = pDescriptorPlayback->shareMode; + data.performanceProfile = pConfig->performanceProfile; + + /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ + if (pConfig->deviceType == ma_device_type_duplex) { + data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; + data.periodsIn = pDescriptorCapture->periodCount; + data.registerStopEvent = MA_FALSE; + } else { + data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; + data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; + data.periodsIn = pDescriptorPlayback->periodCount; + data.registerStopEvent = MA_TRUE; + } + + result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (pDevice->coreaudio.pAudioBufferList) { + ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); + } + } + return result; + } + + pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); + #if defined(MA_APPLE_DESKTOP) + pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; + #endif + pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; + pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; + pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; + pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; + + pDescriptorPlayback->format = data.formatOut; + pDescriptorPlayback->channels = data.channelsOut; + pDescriptorPlayback->sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; + pDescriptorPlayback->periodCount = data.periodsOut; + + #if defined(MA_APPLE_DESKTOP) + ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); + + /* + 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. + */ + if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { + ma_device__track__coreaudio(pDevice); + } + #endif + } + + + + /* + When stopping the device, a callback is called on another thread. We need to wait for this callback + before returning from ma_device_stop(). This event is used for this. + */ + ma_event_init(&pDevice->coreaudio.stopEvent); + + /* + We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done + differently on non-Desktop Apple platforms. + */ +#if defined(MA_APPLE_MOBILE) + pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; +#endif + + return MA_SUCCESS; +} + + +static ma_result ma_device_start__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + if (pDevice->type == ma_device_type_duplex) { + ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + } + return ma_result_from_OSStatus(status); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__coreaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); + if (status != noErr) { + return ma_result_from_OSStatus(status); + } + } + + /* We need to wait for the callback to finish before returning. */ + ma_event_wait(&pDevice->coreaudio.stopEvent); + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__coreaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_coreaudio); + +#if defined(MA_APPLE_MOBILE) + if (!pContext->coreaudio.noAudioSessionDeactivate) { + if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } +#endif + +#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); +#endif + +#if !defined(MA_APPLE_MOBILE) + ma_context__uninit_device_tracking__coreaudio(pContext); +#endif + + (void)pContext; + return MA_SUCCESS; +} + +#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) +static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) +{ + /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ + MA_ASSERT(category != ma_ios_session_category_default); + MA_ASSERT(category != ma_ios_session_category_none); + + switch (category) { + case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; + case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; + case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; + case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; + case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; + case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; + case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; + default: return AVAudioSessionCategoryAmbient; + } +} +#endif + +static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#if !defined(MA_APPLE_MOBILE) + ma_result result; +#endif + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pContext != NULL); + +#if defined(MA_APPLE_MOBILE) + @autoreleasepool { + AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; + AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; + + MA_ASSERT(pAudioSession != NULL); + + if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { + /* + I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails + we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. + */ + #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) + options |= AVAudioSessionCategoryOptionDefaultToSpeaker; + #endif + + if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { + /* Using PlayAndRecord */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { + /* Using Playback */ + } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { + /* Using Record */ + } else { + /* Leave as default? */ + } + } else { + if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { + #if defined(__IPHONE_12_0) + if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { + return MA_INVALID_OPERATION; /* Failed to set session category. */ + } + #else + /* Ignore the session category on version 11 and older, but post a warning. */ + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); + #endif + } + } + + if (!pConfig->coreaudio.noAudioSessionActivate) { + if (![pAudioSession setActive:true error:nil]) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); + return MA_FAILED_TO_INIT_BACKEND; + } + } + } +#endif + +#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); + if (pContext->coreaudio.hCoreFoundation == NULL) { + return MA_API_NOT_FOUND; + } + + pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); + + + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); + if (pContext->coreaudio.hCoreAudio == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); + + /* + 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. + The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to + AudioToolbox. + */ + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); + if (pContext->coreaudio.hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + + if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); + if (pContext->coreaudio.hAudioUnit == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + return MA_API_NOT_FOUND; + } + } + + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); +#else + pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; + pContext->coreaudio.CFRelease = (ma_proc)CFRelease; + + #if defined(MA_APPLE_DESKTOP) + pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; + pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; + pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; + pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; + pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; + #endif + + pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; + pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; + pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; + pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; + pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; + pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; + pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; + pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; + pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; + pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; + pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; +#endif + + /* Audio component. */ + { + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + #if defined(MA_APPLE_DESKTOP) + desc.componentSubType = kAudioUnitSubType_HALOutput; + #else + desc.componentSubType = kAudioUnitSubType_RemoteIO; + #endif + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); + if (pContext->coreaudio.component == NULL) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + #endif + return MA_FAILED_TO_INIT_BACKEND; + } + } + +#if !defined(MA_APPLE_MOBILE) + result = ma_context__init_device_tracking__coreaudio(pContext); + if (result != MA_SUCCESS) { + #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); + #endif + return result; + } +#endif + + pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; + + pCallbacks->onContextInit = ma_context_init__coreaudio; + pCallbacks->onContextUninit = ma_context_uninit__coreaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; + pCallbacks->onDeviceInit = ma_device_init__coreaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; + pCallbacks->onDeviceStart = ma_device_start__coreaudio; + pCallbacks->onDeviceStop = ma_device_stop__coreaudio; + pCallbacks->onDeviceRead = NULL; + pCallbacks->onDeviceWrite = NULL; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* Core Audio */ + + + +/****************************************************************************** + +sndio Backend + +******************************************************************************/ +#ifdef MA_HAS_SNDIO +#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 miniaudio'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 0 +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#endif +#endif + +#define MA_SIO_DEVANY "default" +#define MA_SIO_PLAY 1 +#define MA_SIO_REC 2 +#define MA_SIO_NENC 8 +#define MA_SIO_NCHAN 8 +#define MA_SIO_NRATE 16 +#define MA_SIO_NCONF 4 + +struct ma_sio_hdl; /* <-- Opaque */ + +struct ma_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 ma_sio_enc +{ + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; +}; + +struct ma_sio_conf +{ + unsigned int enc; + unsigned int rchan; + unsigned int pchan; + unsigned int rate; +}; + +struct ma_sio_cap +{ + struct ma_sio_enc enc[MA_SIO_NENC]; + unsigned int rchan[MA_SIO_NCHAN]; + unsigned int pchan[MA_SIO_NCHAN]; + unsigned int rate[MA_SIO_NRATE]; + int __pad[7]; + unsigned int nconf; + struct ma_sio_conf confs[MA_SIO_NCONF]; +}; + +typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); +typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); +typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); +typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); +typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); +typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); +typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); + +static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ +{ + ma_uint32 i; + for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { + if (g_maStandardSampleRatePriorities[i] == sampleRate) { + return i; + } + } + + return (ma_uint32)-1; +} + +static ma_format ma_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 ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { + return ma_format_unknown; + } + + if (bits == 8 && bps == 1 && sig == 0) { + return ma_format_u8; + } + if (bits == 16 && bps == 2 && sig == 1) { + return ma_format_s16; + } + if (bits == 24 && bps == 3 && sig == 1) { + return ma_format_s24; + } + if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { + /*return ma_format_s24_32;*/ + } + if (bits == 32 && bps == 4 && sig == 1) { + return ma_format_s32; + } + + return ma_format_unknown; +} + +static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) +{ + ma_format bestFormat; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + + bestFormat = ma_format_unknown; + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == ma_format_unknown) { + continue; /* Format not supported. */ + } + + if (bestFormat == ma_format_unknown) { + bestFormat = format; + } else { + if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ + bestFormat = format; + } + } + } + } + + return bestFormat; +} + +static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) +{ + ma_uint32 maxChannels; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + MA_ASSERT(requiredFormat != ma_format_unknown); + + /* Just pick whatever configuration has the most channels. */ + maxChannels = 0; + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + /* The encoding should be of requiredFormat. */ + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int iChannel; + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_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 (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + + if (deviceType == ma_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_device_type_playback) { + channels = caps->pchan[iChannel]; + } else { + channels = caps->rchan[iChannel]; + } + + if (maxChannels < channels) { + maxChannels = channels; + } + } + } + } + + return maxChannels; +} + +static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) +{ + ma_uint32 firstSampleRate; + ma_uint32 bestSampleRate; + unsigned int iConfig; + + MA_ASSERT(caps != NULL); + MA_ASSERT(requiredFormat != ma_format_unknown); + MA_ASSERT(requiredChannels > 0); + MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); + + firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ + bestSampleRate = 0; + + for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { + /* The encoding should be of requiredFormat. */ + unsigned int iEncoding; + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int iChannel; + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps->enc[iEncoding].bits; + bps = caps->enc[iEncoding].bps; + sig = caps->enc[iEncoding].sig; + le = caps->enc[iEncoding].le; + msb = caps->enc[iEncoding].msb; + format = ma_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 (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + unsigned int iRate; + + if (deviceType == ma_device_type_playback) { + chan = caps->confs[iConfig].pchan; + } else { + chan = caps->confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_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 (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { + ma_uint32 rate = (ma_uint32)caps->rate[iRate]; + ma_uint32 ratePriority; + + if (firstSampleRate == 0) { + firstSampleRate = rate; + } + + /* Disregard this rate if it's not a standard one. */ + ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); + if (ratePriority == (ma_uint32)-1) { + continue; + } + + if (ma_get_standard_sample_rate_priority_index__sndio(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; +} + + +static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 isTerminating = MA_FALSE; + struct ma_sio_hdl* handle; + + MA_ASSERT(pContext != NULL); + MA_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. */ + + /* Playback. */ + if (!isTerminating) { + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); + if (handle != NULL) { + /* Supports playback. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); + ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); + + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + /* Capture. */ + if (!isTerminating) { + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); + if (handle != NULL) { + /* Supports capture. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); + ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); + + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + char devid[256]; + struct ma_sio_hdl* handle; + struct ma_sio_cap caps; + unsigned int iConfig; + + MA_ASSERT(pContext != NULL); + + /* We need to open the device before we can get information about it. */ + if (pDeviceID == NULL) { + ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); + } else { + ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); + } + + handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); + if (handle == NULL) { + return MA_NO_DEVICE; + } + + if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { + return MA_ERROR; + } + + pDeviceInfo->nativeDataFormatCount = 0; + + for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { + /* + The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give + preference to some formats over others. + */ + unsigned int iEncoding; + unsigned int iChannel; + unsigned int iRate; + + for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { + unsigned int bits; + unsigned int bps; + unsigned int sig; + unsigned int le; + unsigned int msb; + ma_format format; + + if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { + continue; + } + + bits = caps.enc[iEncoding].bits; + bps = caps.enc[iEncoding].bps; + sig = caps.enc[iEncoding].sig; + le = caps.enc[iEncoding].le; + msb = caps.enc[iEncoding].msb; + format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); + if (format == ma_format_unknown) { + continue; /* Format not supported. */ + } + + + /* Channels. */ + for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { + unsigned int chan = 0; + unsigned int channels; + + if (deviceType == ma_device_type_playback) { + chan = caps.confs[iConfig].pchan; + } else { + chan = caps.confs[iConfig].rchan; + } + + if ((chan & (1UL << iChannel)) == 0) { + continue; + } + + if (deviceType == ma_device_type_playback) { + channels = caps.pchan[iChannel]; + } else { + channels = caps.rchan[iChannel]; + } + + + /* Sample Rates. */ + for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { + if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); + } + } + } + } + } + + ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + const char* pDeviceName; + ma_ptr handle; + int openFlags = 0; + struct ma_sio_cap caps; + struct ma_sio_par par; + const ma_device_id* pDeviceID; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + MA_ASSERT(pDevice != NULL); + + if (deviceType == ma_device_type_capture) { + openFlags = MA_SIO_REC; + } else { + openFlags = MA_SIO_PLAY; + } + + pDeviceID = pDescriptor->pDeviceID; + format = pDescriptor->format; + channels = pDescriptor->channels; + sampleRate = pDescriptor->sampleRate; + + pDeviceName = MA_SIO_DEVANY; + if (pDeviceID != NULL) { + pDeviceName = pDeviceID->sndio; + } + + handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); + if (handle == NULL) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + /* We need to retrieve the device caps to determine the most appropriate format to use. */ + if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); + return MA_ERROR; + } + + /* + 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 ma_find_best_channels_from_sio_cap__sndio(). + */ + if (deviceType == ma_device_type_capture) { + if (format == ma_format_unknown) { + format = ma_find_best_format_from_sio_cap__sndio(&caps); + } + + if (channels == 0) { + if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { + channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); + } else { + channels = MA_DEFAULT_CHANNELS; + } + } + } else { + if (format == ma_format_unknown) { + format = ma_find_best_format_from_sio_cap__sndio(&caps); + } + + if (channels == 0) { + if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { + channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); + } else { + channels = MA_DEFAULT_CHANNELS; + } + } + } + + if (sampleRate == 0) { + sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); + } + + + ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); + par.msb = 0; + par.le = ma_is_little_endian(); + + switch (format) { + case ma_format_u8: + { + par.bits = 8; + par.bps = 1; + par.sig = 0; + } break; + + case ma_format_s24: + { + par.bits = 24; + par.bps = 3; + par.sig = 1; + } break; + + case ma_format_s32: + { + par.bits = 32; + par.bps = 4; + par.sig = 1; + } break; + + case ma_format_s16: + case ma_format_f32: + case ma_format_unknown: + default: + { + par.bits = 16; + par.bps = 2; + par.sig = 1; + } break; + } + + if (deviceType == ma_device_type_capture) { + par.rchan = channels; + } else { + par.pchan = channels; + } + + par.rate = sampleRate; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); + + par.round = internalPeriodSizeInFrames; + par.appbufsz = par.round * pDescriptor->periodCount; + + if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); + return MA_ERROR; + } + + if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { + ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); + return MA_ERROR; + } + + internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); + internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; + internalSampleRate = par.rate; + internalPeriods = par.appbufsz / par.round; + internalPeriodSizeInFrames = par.round; + + if (deviceType == ma_device_type_capture) { + pDevice->sndio.handleCapture = handle; + } else { + pDevice->sndio.handlePlayback = handle; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->sndio); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__sndio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* + From the documentation: + + The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then + stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the + buffer is drained. In no case are samples in the play buffer discarded. + + Therefore, sio_stop() performs all of the necessary draining for us. + */ + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int result; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (result == 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); + return MA_IO_ERROR; + } + + if (pFramesWritten != NULL) { + *pFramesWritten = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int result; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (result == 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); + return MA_IO_ERROR; + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__sndio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_sndio); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ +#ifndef MA_NO_RUNTIME_LINKING + const char* libsndioNames[] = { + "libsndio.so" + }; + size_t i; + + for (i = 0; i < ma_countof(libsndioNames); ++i) { + pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); + if (pContext->sndio.sndioSO != NULL) { + break; + } + } + + if (pContext->sndio.sndioSO == NULL) { + return MA_NO_BACKEND; + } + + pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), 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 + + pCallbacks->onContextInit = ma_context_init__sndio; + pCallbacks->onContextUninit = ma_context_uninit__sndio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; + pCallbacks->onDeviceInit = ma_device_init__sndio; + pCallbacks->onDeviceUninit = ma_device_uninit__sndio; + pCallbacks->onDeviceStart = ma_device_start__sndio; + pCallbacks->onDeviceStop = ma_device_stop__sndio; + pCallbacks->onDeviceRead = ma_device_read__sndio; + pCallbacks->onDeviceWrite = ma_device_write__sndio; + pCallbacks->onDeviceDataLoop = NULL; + + (void)pConfig; + return MA_SUCCESS; +} +#endif /* sndio */ + + + +/****************************************************************************** + +audio(4) Backend + +******************************************************************************/ +#ifdef MA_HAS_AUDIO4 +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + #include + #if defined(OpenBSD) && OpenBSD >= 201709 + #define MA_AUDIO4_USE_NEW_API + #endif +#endif + +static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) +{ + size_t baseLen; + + MA_ASSERT(id != NULL); + MA_ASSERT(idSize > 0); + MA_ASSERT(deviceIndex >= 0); + + baseLen = strlen(base); + MA_ASSERT(idSize > baseLen); + + ma_strcpy_s(id, idSize, base); + ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); +} + +static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) +{ + size_t idLen; + size_t baseLen; + const char* deviceIndexStr; + + MA_ASSERT(id != NULL); + MA_ASSERT(base != NULL); + MA_ASSERT(pIndexOut != NULL); + + idLen = strlen(id); + baseLen = strlen(base); + if (idLen <= baseLen) { + return MA_ERROR; /* Doesn't look like the id starts with the base. */ + } + + if (strncmp(id, base, baseLen) != 0) { + return MA_ERROR; /* ID does not begin with base. */ + } + + deviceIndexStr = id + baseLen; + if (deviceIndexStr[0] == '\0') { + return MA_ERROR; /* No index specified in the ID. */ + } + + if (pIndexOut) { + *pIndexOut = atoi(deviceIndexStr); + } + + return MA_SUCCESS; +} + + +#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ +static ma_format ma_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 ma_format_u8; + } else { + if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { + if (precision == 16) { + return ma_format_s16; + } else if (precision == 24) { + return ma_format_s24; + } else if (precision == 32) { + return ma_format_s32; + } + } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { + if (precision == 16) { + return ma_format_s16; + } else if (precision == 24) { + return ma_format_s24; + } else if (precision == 32) { + return ma_format_s32; + } + } + } + + return ma_format_unknown; /* Encoding not supported. */ +} + +static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) +{ + MA_ASSERT(pEncoding != NULL); + MA_ASSERT(pPrecision != NULL); + + switch (format) + { + case ma_format_u8: + { + *pEncoding = AUDIO_ENCODING_ULINEAR; + *pPrecision = 8; + } break; + + case ma_format_s24: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 24; + } break; + + case ma_format_s32: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 32; + } break; + + case ma_format_s16: + case ma_format_f32: + case ma_format_unknown: + default: + { + *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; + *pPrecision = 16; + } break; + } +} + +static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) +{ + return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); +} + +static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) +{ + audio_encoding_t encoding; + ma_uint32 iFormat; + int counter = 0; + + /* First check to see if the preferred format is supported. */ + if (preferredFormat != ma_format_unknown) { + counter = 0; + for (;;) { + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { + return preferredFormat; /* Found the preferred format. */ + } + + /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ + counter += 1; + } + } + + /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ + for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { + ma_format format = g_maFormatPriorities[iFormat]; + + counter = 0; + for (;;) { + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { + return format; /* Found a workable format. */ + } + + /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ + counter += 1; + } + } + + /* Getting here means not appropriate format was found. */ + return ma_format_unknown; +} +#else +static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) +{ + if (par->bits == 8 && par->bps == 1 && par->sig == 0) { + return ma_format_u8; + } + if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_s16; + } + if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_s24; + } + if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { + return ma_format_f32; + } + + /* Format not supported. */ + return ma_format_unknown; +} +#endif + +static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) +{ + audio_device_t fdDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(fd >= 0); + MA_ASSERT(pDeviceInfo != NULL); + + (void)pContext; + (void)deviceType; + + if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { + return MA_ERROR; /* Failed to retrieve device info. */ + } + + /* Name. */ + ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); + + #if !defined(MA_AUDIO4_USE_NEW_API) + { + audio_info_t fdInfo; + int counter = 0; + ma_uint32 channels; + ma_uint32 sampleRate; + + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + return MA_ERROR; + } + + if (deviceType == ma_device_type_playback) { + channels = fdInfo.play.channels; + sampleRate = fdInfo.play.sample_rate; + } else { + channels = fdInfo.record.channels; + sampleRate = fdInfo.record.sample_rate; + } + + /* Supported formats. We get this by looking at the encodings. */ + pDeviceInfo->nativeDataFormatCount = 0; + for (;;) { + audio_encoding_t encoding; + ma_format format; + + MA_ZERO_OBJECT(&encoding); + encoding.index = counter; + if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { + break; + } + + format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); + if (format != ma_format_unknown) { + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); + } + + counter += 1; + } + } + #else + { + struct audio_swpar fdPar; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + return MA_ERROR; + } + + format = ma_format_from_swpar__audio4(&fdPar); + if (format == ma_format_unknown) { + return MA_FORMAT_NOT_SUPPORTED; + } + + if (deviceType == ma_device_type_playback) { + channels = fdPar.pchan; + } else { + channels = fdPar.rchan; + } + + sampleRate = fdPar.rate; + + pDeviceInfo->nativeDataFormatCount = 0; + ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); + } + #endif + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + const int maxDevices = 64; + char devpath[256]; + int iDevice; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* + 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. + */ + for (iDevice = 0; iDevice < maxDevices; ++iDevice) { + struct stat st; + int fd; + ma_bool32 isTerminating = MA_FALSE; + + ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); + ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); + + if (stat(devpath, &st) < 0) { + break; + } + + /* The device exists, but we need to check if it's usable as playback and/or capture. */ + + /* Playback. */ + if (!isTerminating) { + fd = open(devpath, O_RDONLY, 0); + if (fd >= 0) { + /* Supports playback. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + close(fd); + } + } + + /* Capture. */ + if (!isTerminating) { + fd = open(devpath, O_WRONLY, 0); + if (fd >= 0) { + /* Supports capture. */ + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); + if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + close(fd); + } + } + + if (isTerminating) { + break; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + int fd = -1; + int deviceIndex = -1; + char ctlid[256]; + ma_result result; + + MA_ASSERT(pContext != NULL); + + /* + 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. + */ + if (pDeviceID == NULL) { + /* Default device. */ + ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); + } else { + /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ + result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); + if (result != MA_SUCCESS) { + return result; + } + + ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); + } + + fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); + if (fd == -1) { + return MA_NO_DEVICE; + } + + if (deviceIndex == -1) { + ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); + } else { + ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); + } + + result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); + + close(fd); + return result; +} + +static ma_result ma_device_uninit__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + close(pDevice->audio4.fdCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + close(pDevice->audio4.fdPlayback); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + const char* pDefaultDeviceNames[] = { + "/dev/audio", + "/dev/audio0" + }; + const char* pDefaultDeviceCtlNames[] = { + "/dev/audioctl", + "/dev/audioctl0" + }; + int fd; + int fdFlags = 0; + size_t iDefaultDevice = (size_t)-1; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_uint32 internalPeriodSizeInFrames; + ma_uint32 internalPeriods; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is open the file. */ + if (deviceType == ma_device_type_capture) { + fdFlags = O_RDONLY; + } else { + fdFlags = O_WRONLY; + } + /*fdFlags |= O_NONBLOCK;*/ + + /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ + if (pDescriptor->pDeviceID == NULL) { + /* Default device. */ + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { + fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); + if (fd != -1) { + break; + } + } + } else { + /* Specific device. */ + fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); + + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { + if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { + break; + } + } + + if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { + iDefaultDevice = (size_t)-1; + } + } + + if (fd == -1) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); + return ma_result_from_errno(errno); + } + + #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ + { + audio_info_t fdInfo; + int fdInfoResult = -1; + + /* + The documentation is a little bit unclear to me as to how it handles formats. It says the + following: + + Regardless of formats supported by underlying driver, the audio driver accepts the + following formats. + + By then the next sentence says this: + + `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. + + It sounds like a direct contradiction to me. I'm going to play this safe any only use the + best sample format returned by AUDIO_GETENC. If the requested format is supported we'll + use that, but otherwise we'll just use our standard format priorities to pick an + appropriate one. + */ + AUDIO_INITINFO(&fdInfo); + + /* + Get the default format from the audioctl file if we're asking for a default device. If we + retrieve it from /dev/audio it'll default to mono 8000Hz. + */ + if (iDefaultDevice != (size_t)-1) { + /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ + int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); + if (fdctl != -1) { + fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); + close(fdctl); + } + } + + if (fdInfoResult == -1) { + /* We still don't have the default device info so just retrieve it from the main audio device. */ + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + } + + /* We get the driver to do as much of the data conversion as possible. */ + if (deviceType == ma_device_type_capture) { + fdInfo.mode = AUMODE_RECORD; + ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); + + if (pDescriptor->channels != 0) { + fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ + } + + if (pDescriptor->sampleRate != 0) { + fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ + } + } else { + fdInfo.mode = AUMODE_PLAY; + ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); + + if (pDescriptor->channels != 0) { + fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ + } + + if (pDescriptor->sampleRate != 0) { + fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ + } + } + + if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); + } + + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + + if (deviceType == ma_device_type_capture) { + internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); + internalChannels = fdInfo.record.channels; + internalSampleRate = fdInfo.record.sample_rate; + } else { + internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); + internalChannels = fdInfo.play.channels; + internalSampleRate = fdInfo.play.sample_rate; + } + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Buffer. */ + { + ma_uint32 internalPeriodSizeInBytes; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + + internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (internalPeriodSizeInBytes < 16) { + internalPeriodSizeInBytes = 16; + } + + internalPeriods = pDescriptor->periodCount; + if (internalPeriods < 2) { + internalPeriods = 2; + } + + /* What miniaudio calls a period, audio4 calls a block. */ + AUDIO_INITINFO(&fdInfo); + fdInfo.hiwat = internalPeriods; + fdInfo.lowat = internalPeriods-1; + fdInfo.blocksize = internalPeriodSizeInBytes; + if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); + return ma_result_from_errno(errno); + } + + internalPeriods = fdInfo.hiwat; + internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); + } + } + #else + { + struct audio_swpar fdPar; + + /* 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. */ + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); + return ma_result_from_errno(errno); + } + + internalFormat = ma_format_from_swpar__audio4(&fdPar); + internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; + internalSampleRate = fdPar.rate; + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + /* Buffer. */ + { + ma_uint32 internalPeriodSizeInBytes; + + internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); + + /* What miniaudio calls a period, audio4 calls a block. */ + internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (internalPeriodSizeInBytes < 16) { + internalPeriodSizeInBytes = 16; + } + + fdPar.nblks = pDescriptor->periodCount; + fdPar.round = internalPeriodSizeInBytes; + + if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); + return ma_result_from_errno(errno); + } + + if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); + return ma_result_from_errno(errno); + } + } + + internalFormat = ma_format_from_swpar__audio4(&fdPar); + internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; + internalSampleRate = fdPar.rate; + internalPeriods = fdPar.nblks; + internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); + } + #endif + + if (internalFormat == ma_format_unknown) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); + return MA_FORMAT_NOT_SUPPORTED; + } + + if (deviceType == ma_device_type_capture) { + pDevice->audio4.fdCapture = fd; + } else { + pDevice->audio4.fdPlayback = fd; + } + + pDescriptor->format = internalFormat; + pDescriptor->channels = internalChannels; + pDescriptor->sampleRate = internalSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); + pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; + pDescriptor->periodCount = internalPeriods; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + + MA_ZERO_OBJECT(&pDevice->audio4); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDevice->audio4.fdCapture = -1; + pDevice->audio4.fdPlayback = -1; + + /* + 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 + /* NetBSD 8.0+ */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } +#else + /* All other flavors. */ +#endif + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + if (pConfig->deviceType == ma_device_type_duplex) { + close(pDevice->audio4.fdCapture); + } + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->audio4.fdCapture == -1) { + return MA_INVALID_ARGS; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->audio4.fdPlayback == -1) { + return MA_INVALID_ARGS; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) +{ + if (fd == -1) { + return MA_INVALID_ARGS; + } + +#if !defined(MA_AUDIO4_USE_NEW_API) + if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); + return ma_result_from_errno(errno); + } +#else + if (ioctl(fd, AUDIO_STOP, 0) < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); + return ma_result_from_errno(errno); + } +#endif + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__audio4(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result; + + result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result; + + /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ + #if !defined(MA_AUDIO4_USE_NEW_API) + ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); + #endif + + /* Here is where the device is stopped immediately. */ + result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int result; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (result < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); + return ma_result_from_errno(errno); + } + + if (pFramesWritten != NULL) { + *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int result; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (result < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); + return ma_result_from_errno(errno); + } + + if (pFramesRead != NULL) { + *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__audio4(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_audio4); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + pCallbacks->onContextInit = ma_context_init__audio4; + pCallbacks->onContextUninit = ma_context_uninit__audio4; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; + pCallbacks->onDeviceInit = ma_device_init__audio4; + pCallbacks->onDeviceUninit = ma_device_uninit__audio4; + pCallbacks->onDeviceStart = ma_device_start__audio4; + pCallbacks->onDeviceStop = ma_device_stop__audio4; + pCallbacks->onDeviceRead = ma_device_read__audio4; + pCallbacks->onDeviceWrite = ma_device_write__audio4; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* audio4 */ + + +/****************************************************************************** + +OSS Backend + +******************************************************************************/ +#ifdef MA_HAS_OSS +#include +#include +#include +#include + +#ifndef SNDCTL_DSP_HALT +#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET +#endif + +#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" + +static int ma_open_temp_device__oss() +{ + /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ + int fd = open("/dev/mixer", O_RDONLY, 0); + if (fd >= 0) { + return fd; + } + + return -1; +} + +static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) +{ + const char* deviceName; + int flags; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pfd != NULL); + (void)pContext; + + *pfd = -1; + + /* This function should only be called for playback or capture, not duplex. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; + } + + deviceName = MA_OSS_DEFAULT_DEVICE_NAME; + if (pDeviceID != NULL) { + deviceName = pDeviceID->oss; + } + + flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; + if (shareMode == ma_share_mode_exclusive) { + flags |= O_EXCL; + } + + *pfd = open(deviceName, flags, 0); + if (*pfd == -1) { + return ma_result_from_errno(errno); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + int fd; + oss_sysinfo si; + int result; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + fd = ma_open_temp_device__oss(); + if (fd == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; + } + + result = ioctl(fd, SNDCTL_SYSINFO, &si); + if (result != -1) { + int iAudioDevice; + for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ + ma_device_info deviceInfo; + ma_bool32 isTerminating = MA_FALSE; + + MA_ZERO_OBJECT(&deviceInfo); + + /* ID */ + ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); + + /* + The human readable device name should be in the "ai.handle" variable, but it can + sometimes be empty in which case we just fall back to "ai.name" which is less user + friendly, but usually has a value. + */ + if (ai.handle[0] != '\0') { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); + } else { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); + } + + /* The device can be both playback and capture. */ + if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { + isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { + isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + if (isTerminating) { + break; + } + } + } + } + } else { + close(fd); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; + } + + close(fd); + return MA_SUCCESS; +} + +static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) +{ + unsigned int minChannels; + unsigned int maxChannels; + unsigned int iRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pAudioInfo != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* If we support all channels we just report 0. */ + minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); + + /* + OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, + which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which + case we'll need to use min_rate and max_rate and report only standard rates. + */ + if (pAudioInfo->nrates > 0) { + for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { + unsigned int rate = pAudioInfo->rates[iRate]; + + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ + } else { + unsigned int iChannel; + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); + } + } + } + } else { + for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { + ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; + + if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { + if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { + ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ + } else { + unsigned int iChannel; + for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { + ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); + } + } + } + } + } +} + +static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_bool32 foundDevice; + int fdTemp; + oss_sysinfo si; + int result; + + MA_ASSERT(pContext != NULL); + + /* Handle the default device a little differently. */ + if (pDeviceID == NULL) { + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + return MA_SUCCESS; + } + + + /* If we get here it means we are _not_ using the default device. */ + foundDevice = MA_FALSE; + + fdTemp = ma_open_temp_device__oss(); + if (fdTemp == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); + return MA_NO_BACKEND; + } + + result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); + if (result != -1) { + int iAudioDevice; + for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { + oss_audioinfo ai; + ai.dev = iAudioDevice; + result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); + if (result != -1) { + if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { + /* It has the same name, so now just confirm the type. */ + if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || + (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { + unsigned int formatMask; + + /* ID */ + ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); + + /* + The human readable device name should be in the "ai.handle" variable, but it can + sometimes be empty in which case we just fall back to "ai.name" which is less user + friendly, but usually has a value. + */ + if (ai.handle[0] != '\0') { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); + } + + + pDeviceInfo->nativeDataFormatCount = 0; + + if (deviceType == ma_device_type_playback) { + formatMask = ai.oformats; + } else { + formatMask = ai.iformats; + } + + if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); + } + if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); + } + if ((formatMask & AFMT_U8) != 0) { + ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); + } + + foundDevice = MA_TRUE; + break; + } + } + } + } + } else { + close(fdTemp); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); + return MA_NO_BACKEND; + } + + + close(fdTemp); + + if (!foundDevice) { + return MA_NO_DEVICE; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + close(pDevice->oss.fdCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + close(pDevice->oss.fdPlayback); + } + + return MA_SUCCESS; +} + +static int ma_format_to_oss(ma_format format) +{ + int ossFormat = AFMT_U8; + switch (format) { + case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; + case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; + case ma_format_u8: + default: ossFormat = AFMT_U8; break; + } + + return ossFormat; +} + +static ma_format ma_format_from_oss(int ossFormat) +{ + if (ossFormat == AFMT_U8) { + return ma_format_u8; + } else { + if (ma_is_little_endian()) { + switch (ossFormat) { + case AFMT_S16_LE: return ma_format_s16; + case AFMT_S32_LE: return ma_format_s32; + default: return ma_format_unknown; + } + } else { + switch (ossFormat) { + case AFMT_S16_BE: return ma_format_s16; + case AFMT_S32_BE: return ma_format_s32; + default: return ma_format_unknown; + } + } + } + + return ma_format_unknown; +} + +static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) +{ + ma_result result; + int ossResult; + int fd; + const ma_device_id* pDeviceID = NULL; + ma_share_mode shareMode; + int ossFormat; + int ossChannels; + int ossSampleRate; + int ossFragment; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); + + pDeviceID = pDescriptor->pDeviceID; + shareMode = pDescriptor->shareMode; + ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ + ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; + ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; + + result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + + /* + The OSS documantation is very clear about the order we should be initializing the device's properties: + 1) Format + 2) Channels + 3) Sample rate. + */ + + /* Format. */ + ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); + return ma_result_from_errno(errno); + } + + /* Channels. */ + ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); + return ma_result_from_errno(errno); + } + + /* Sample Rate. */ + ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); + return ma_result_from_errno(errno); + } + + /* + Buffer. + + The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if + it should be done before or after format/channels/rate. + + OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual + value. + */ + { + ma_uint32 periodSizeInFrames; + ma_uint32 periodSizeInBytes; + ma_uint32 ossFragmentSizePower; + + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); + + periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); + if (periodSizeInBytes < 16) { + periodSizeInBytes = 16; + } + + ossFragmentSizePower = 4; + periodSizeInBytes >>= 4; + while (periodSizeInBytes >>= 1) { + ossFragmentSizePower += 1; + } + + ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); + ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); + if (ossResult == -1) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); + return ma_result_from_errno(errno); + } + } + + /* Internal settings. */ + if (deviceType == ma_device_type_capture) { + pDevice->oss.fdCapture = fd; + } else { + pDevice->oss.fdPlayback = fd; + } + + pDescriptor->format = ma_format_from_oss(ossFormat); + pDescriptor->channels = ossChannels; + pDescriptor->sampleRate = ossSampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); + pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); + pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); + + if (pDescriptor->format == ma_format_unknown) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); + return MA_FORMAT_NOT_SUPPORTED; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + + MA_ZERO_OBJECT(&pDevice->oss); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); + return result; + } + } + + return MA_SUCCESS; +} + +/* +Note on Starting and Stopping +============================= +In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when +trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will +fail. Instead what we need to do is just not write or read to and from the device when the +device is not running. + +As a result, both the start and stop functions for OSS are just empty stubs. The starting and +stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check +the device state, and if the device is stopped they will simply not do any kind of processing. + +The downside to this technique is that I've noticed a fairly lengthy delay in stopping the +device, up to a second. This is on a virtual machine, and as such might just be due to the +virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for +the moment that's just how it's going to have to be. + +When starting the device, OSS will automatically start it when write() or read() is called. +*/ +static ma_result ma_device_start__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* The device is automatically started with reading and writing. */ + (void)pDevice; + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__oss(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* See note above on why this is empty. */ + (void)pDevice; + + return MA_SUCCESS; +} + +static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) +{ + int resultOSS; + ma_uint32 deviceState; + + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + /* Don't do any processing if the device is stopped. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { + return MA_SUCCESS; + } + + resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + if (resultOSS < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); + return ma_result_from_errno(errno); + } + + if (pFramesWritten != NULL) { + *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) +{ + int resultOSS; + ma_uint32 deviceState; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + /* Don't do any processing if the device is stopped. */ + deviceState = ma_device_get_state(pDevice); + if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { + return MA_SUCCESS; + } + + resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); + if (resultOSS < 0) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); + return ma_result_from_errno(errno); + } + + if (pFramesRead != NULL) { + *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + } + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__oss(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_oss); + + (void)pContext; + return MA_SUCCESS; +} + +static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + int fd; + int ossVersion; + int result; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + + /* Try opening a temporary device first so we can get version information. This is closed at the end. */ + fd = ma_open_temp_device__oss(); + if (fd == -1) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ + return MA_NO_BACKEND; + } + + /* Grab the OSS version. */ + ossVersion = 0; + result = ioctl(fd, OSS_GETVERSION, &ossVersion); + if (result == -1) { + close(fd); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); + return MA_NO_BACKEND; + } + + /* The file handle to temp device is no longer needed. Close ASAP. */ + close(fd); + + pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); + pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); + + pCallbacks->onContextInit = ma_context_init__oss; + pCallbacks->onContextUninit = ma_context_uninit__oss; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; + pCallbacks->onDeviceInit = ma_device_init__oss; + pCallbacks->onDeviceUninit = ma_device_uninit__oss; + pCallbacks->onDeviceStart = ma_device_start__oss; + pCallbacks->onDeviceStop = ma_device_stop__oss; + pCallbacks->onDeviceRead = ma_device_read__oss; + pCallbacks->onDeviceWrite = ma_device_write__oss; + pCallbacks->onDeviceDataLoop = NULL; + + return MA_SUCCESS; +} +#endif /* OSS */ + + + + + +/****************************************************************************** + +AAudio Backend + +******************************************************************************/ +#ifdef MA_HAS_AAUDIO + +/*#include */ + +typedef int32_t ma_aaudio_result_t; +typedef int32_t ma_aaudio_direction_t; +typedef int32_t ma_aaudio_sharing_mode_t; +typedef int32_t ma_aaudio_format_t; +typedef int32_t ma_aaudio_stream_state_t; +typedef int32_t ma_aaudio_performance_mode_t; +typedef int32_t ma_aaudio_usage_t; +typedef int32_t ma_aaudio_content_type_t; +typedef int32_t ma_aaudio_input_preset_t; +typedef int32_t ma_aaudio_allowed_capture_policy_t; +typedef int32_t ma_aaudio_data_callback_result_t; +typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; +typedef struct ma_AAudioStream_t* ma_AAudioStream; + +#define MA_AAUDIO_UNSPECIFIED 0 + +/* Result codes. miniaudio only cares about the success code. */ +#define MA_AAUDIO_OK 0 + +/* Directions. */ +#define MA_AAUDIO_DIRECTION_OUTPUT 0 +#define MA_AAUDIO_DIRECTION_INPUT 1 + +/* Sharing modes. */ +#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 +#define MA_AAUDIO_SHARING_MODE_SHARED 1 + +/* Formats. */ +#define MA_AAUDIO_FORMAT_PCM_I16 1 +#define MA_AAUDIO_FORMAT_PCM_FLOAT 2 + +/* Stream states. */ +#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 +#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 +#define MA_AAUDIO_STREAM_STATE_OPEN 2 +#define MA_AAUDIO_STREAM_STATE_STARTING 3 +#define MA_AAUDIO_STREAM_STATE_STARTED 4 +#define MA_AAUDIO_STREAM_STATE_PAUSING 5 +#define MA_AAUDIO_STREAM_STATE_PAUSED 6 +#define MA_AAUDIO_STREAM_STATE_FLUSHING 7 +#define MA_AAUDIO_STREAM_STATE_FLUSHED 8 +#define MA_AAUDIO_STREAM_STATE_STOPPING 9 +#define MA_AAUDIO_STREAM_STATE_STOPPED 10 +#define MA_AAUDIO_STREAM_STATE_CLOSING 11 +#define MA_AAUDIO_STREAM_STATE_CLOSED 12 +#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 + +/* Performance modes. */ +#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 +#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 +#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 + +/* Usage types. */ +#define MA_AAUDIO_USAGE_MEDIA 1 +#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 +#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 +#define MA_AAUDIO_USAGE_ALARM 4 +#define MA_AAUDIO_USAGE_NOTIFICATION 5 +#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 +#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 +#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 +#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 +#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 +#define MA_AAUDIO_USAGE_GAME 14 +#define MA_AAUDIO_USAGE_ASSISTANT 16 +#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 +#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 +#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 +#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 + +/* Content types. */ +#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 +#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 +#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 +#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 + +/* Input presets. */ +#define MA_AAUDIO_INPUT_PRESET_GENERIC 1 +#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 +#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 +#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 +#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 +#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 + +/* Allowed Capture Policies */ +#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2 +#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3 + +/* Callback results. */ +#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 +#define MA_AAUDIO_CALLBACK_RESULT_STOP 1 + + +typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); +typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); + +typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); +typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); +typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); +typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); +typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); +typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); +typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); +typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); +typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); +typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); +typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); +typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); +typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); +typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); +typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); +typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); +typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); + +static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) +{ + switch (resultAA) + { + case MA_AAUDIO_OK: return MA_SUCCESS; + default: break; + } + + return MA_ERROR; +} + +static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) +{ + switch (usage) { + case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; + case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; + case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; + case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; + case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; + case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; + case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; + case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; + case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; + case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; + case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; + case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; + case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; + case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; + case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; + case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; + default: break; + } + + return MA_AAUDIO_USAGE_MEDIA; +} + +static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) +{ + switch (contentType) { + case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; + case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; + case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; + case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; + default: break; + } + + return MA_AAUDIO_CONTENT_TYPE_SPEECH; +} + +static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) +{ + switch (inputPreset) { + case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; + case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; + case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; + case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; + case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; + case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; + default: break; + } + + return MA_AAUDIO_INPUT_PRESET_GENERIC; +} + +static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy) +{ + switch (allowedCapturePolicy) { + case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; + case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM; + case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE; + default: break; + } + + return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL; +} + +static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) +{ + ma_result result; + ma_job job; + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + (void)error; + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); + + /* + When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation, + we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this + cleanly and safely. + */ + job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); + job.data.device.aaudio.reroute.pDevice = pDevice; + + if (pStream == pDevice->aaudio.pStreamCapture) { + job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; + } + else { + job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; + } + + result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); + return; + } +} + +static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); + + (void)pStream; + return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) +{ + ma_device* pDevice = (ma_device*)pUserData; + MA_ASSERT(pDevice != NULL); + + ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); + + (void)pStream; + return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) +{ + ma_AAudioStreamBuilder* pBuilder; + ma_aaudio_result_t resultAA; + + /* Safety. */ + *ppBuilder = NULL; + + resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + if (pDeviceID != NULL) { + ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); + } + + ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); + ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); + + + /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ + if (pDescriptor != NULL) { + MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ + + if (pDescriptor->sampleRate != 0) { + ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); + } + + if (deviceType == ma_device_type_capture) { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } + } else { + if (pDescriptor->channels != 0) { + ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); + } + if (pDescriptor->format != ma_format_unknown) { + ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); + } + } + + + /* + There have been reports where setting the frames per data callback results in an error + later on from Android. To address this, I'm experimenting with simply not setting it on + anything from Android 11 and earlier. Suggestions welcome on how we might be able to make + this more targetted. + */ + if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { + /* + AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you + retrieve the actual sample rate until after you've opened the stream. But you need to configure + the buffer capacity before you open the stream... :/ + + To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. + */ + ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; + + ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); + ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); + } + + if (deviceType == ma_device_type_capture) { + if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { + ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); + } + + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); + } else { + if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { + ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); + } + + if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { + ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); + } + + if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) { + ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy)); + } + + ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); + } + + /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ + ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); + + /* We need to set an error callback to detect device changes. */ + if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ + ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); + } + } + + *ppBuilder = pBuilder; + + return MA_SUCCESS; +} + +static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) +{ + ma_result result; + + result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); + ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); + + return result; +} + +static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) +{ + ma_result result; + ma_AAudioStreamBuilder* pBuilder; + + *ppStream = NULL; + + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); +} + +static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +{ + ma_result result; + ma_AAudioStreamBuilder* pBuilder; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pDescriptor != NULL); + MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ + + *ppStream = NULL; + + result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); +} + +static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) +{ + return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); +} + +static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) +{ + /* The only way to know this is to try creating a stream. */ + ma_AAudioStream* pStream; + ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + ma_close_stream__aaudio(pContext, pStream); + return MA_TRUE; +} + +static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) +{ + ma_aaudio_stream_state_t actualNewState; + ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + if (newState != actualNewState) { + return MA_ERROR; /* Failed to transition into the expected state. */ + } + + return MA_SUCCESS; +} + + +static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + + if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + + if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } + + return MA_SUCCESS; +} + +static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pStream != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; + pDeviceInfo->nativeDataFormatCount += 1; +} + +static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) +{ + /* AAudio supports s16 and f32. */ + ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); + ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); +} + +static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream; + ma_result result; + + MA_ASSERT(pContext != NULL); + + /* ID */ + if (pDeviceID != NULL) { + pDeviceInfo->id.aaudio = pDeviceID->aaudio; + } else { + pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; + } + + /* Name */ + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + + pDeviceInfo->nativeDataFormatCount = 0; + + /* We'll need to open the device to get accurate sample rate and channel count information. */ + result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); + if (result != MA_SUCCESS) { + return result; + } + + ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); + + ma_close_stream__aaudio(pContext, pStream); + pStream = NULL; + + return MA_SUCCESS; +} + + +static ma_result ma_device_uninit__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) +{ + ma_result result; + int32_t bufferCapacityInFrames; + int32_t framesPerDataCallback; + ma_AAudioStream* pStream; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDescriptor != NULL); + + *ppStream = NULL; /* Safety. */ + + /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ + result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); + if (result != MA_SUCCESS) { + return result; /* Failed to open the AAudio stream. */ + } + + /* Now extract the internal configuration. */ + pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; + pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); + pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); + + /* For the channel map we need to be sure we don't overflow any buffers. */ + if (pDescriptor->channels <= MA_MAX_CHANNELS) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ + } else { + ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ + } + + bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); + framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); + + if (framesPerDataCallback > 0) { + pDescriptor->periodSizeInFrames = framesPerDataCallback; + pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; + } else { + pDescriptor->periodSizeInFrames = bufferCapacityInFrames; + pDescriptor->periodCount = 1; + } + + *ppStream = pStream; + + return MA_SUCCESS; +} + +static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + pDevice->aaudio.usage = pConfig->aaudio.usage; + pDevice->aaudio.contentType = pConfig->aaudio.contentType; + pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; + pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy; + pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) +{ + ma_aaudio_result_t resultAA; + ma_aaudio_stream_state_t currentState; + + MA_ASSERT(pDevice != NULL); + + resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + /* Do we actually need to wait for the device to transition into it's started state? */ + + /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { + ma_result result; + + if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { + return MA_ERROR; /* Expecting the stream to be a starting or started state. */ + } + + result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) +{ + ma_aaudio_result_t resultAA; + ma_aaudio_stream_state_t currentState; + + MA_ASSERT(pDevice != NULL); + + /* + From the AAudio documentation: + + The stream will stop after all of the data currently buffered has been played. + + This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. + */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { + return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ + } + + resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); + if (resultAA != MA_AAUDIO_OK) { + return ma_result_from_aaudio(resultAA); + } + + /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ + currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); + if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { + ma_result result; + + if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { + return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ + } + + result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_start__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + if (pDevice->type == ma_device_type_duplex) { + ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + } + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__aaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + +static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + /* The first thing to do is close the streams. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); + pDevice->aaudio.pStreamCapture = NULL; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); + pDevice->aaudio.pStreamPlayback = NULL; + } + + /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + { + ma_device_config deviceConfig; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + + deviceConfig = ma_device_config_init(deviceType); + deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.playback.shareMode = pDevice->playback.shareMode; + deviceConfig.playback.format = pDevice->playback.format; + deviceConfig.playback.channels = pDevice->playback.channels; + deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ + deviceConfig.capture.shareMode = pDevice->capture.shareMode; + deviceConfig.capture.format = pDevice->capture.format; + deviceConfig.capture.channels = pDevice->capture.channels; + deviceConfig.sampleRate = pDevice->sampleRate; + deviceConfig.aaudio.usage = pDevice->aaudio.usage; + deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; + deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; + deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy; + deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; + deviceConfig.periods = 1; + + /* Try to get an accurate period size. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; + } else { + deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; + descriptorCapture.shareMode = deviceConfig.capture.shareMode; + descriptorCapture.format = deviceConfig.capture.format; + descriptorCapture.channels = deviceConfig.capture.channels; + descriptorCapture.sampleRate = deviceConfig.sampleRate; + descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorCapture.periodCount = deviceConfig.periods; + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; + descriptorPlayback.shareMode = deviceConfig.playback.shareMode; + descriptorPlayback.format = deviceConfig.playback.format; + descriptorPlayback.channels = deviceConfig.playback.channels; + descriptorPlayback.sampleRate = deviceConfig.sampleRate; + descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; + descriptorPlayback.periodCount = deviceConfig.periods; + } + + result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit__aaudio(pDevice); + return result; + } + + /* We'll only ever do this in response to a reroute. */ + ma_device__on_notification_rerouted(pDevice); + + /* If the device is started, start the streams. Maybe make this configurable? */ + if (ma_device_get_state(pDevice) == ma_device_state_started) { + if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { + ma_device_start__aaudio(pDevice); + } else { + ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ + } + } + + return MA_SUCCESS; + } +} + +static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + ma_AAudioStream* pStream = NULL; + + MA_ASSERT(pDevice != NULL); + MA_ASSERT(type != ma_device_type_duplex); + MA_ASSERT(pDeviceInfo != NULL); + + if (type == ma_device_type_playback) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; + pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + if (type == ma_device_type_capture) { + pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; + pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ + } + + /* Safety. Should never happen. */ + if (pStream == NULL) { + return MA_INVALID_OPERATION; + } + + pDeviceInfo->nativeDataFormatCount = 0; + ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__aaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_aaudio); + + ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); + + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + + return MA_SUCCESS; +} + +static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + size_t i; + const char* libNames[] = { + "libaaudio.so" + }; + + for (i = 0; i < ma_countof(libNames); ++i) { + pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); + if (pContext->aaudio.hAAudio != NULL) { + break; + } + } + + if (pContext->aaudio.hAAudio == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + + + pCallbacks->onContextInit = ma_context_init__aaudio; + pCallbacks->onContextUninit = ma_context_uninit__aaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; + pCallbacks->onDeviceInit = ma_device_init__aaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; + pCallbacks->onDeviceStart = ma_device_start__aaudio; + pCallbacks->onDeviceStop = ma_device_stop__aaudio; + pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ + pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; + + + /* We need a job thread so we can deal with rerouting. */ + { + ma_result result; + ma_device_job_thread_config jobThreadConfig; + + jobThreadConfig = ma_device_job_thread_config_init(); + + result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); + pContext->aaudio.hAAudio = NULL; + return result; + } + } + + + (void)pConfig; + return MA_SUCCESS; +} + +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + ma_device* pDevice; + + MA_ASSERT(pJob != NULL); + + pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; + MA_ASSERT(pDevice != NULL); + + /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ + return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); +} +#else +/* Getting here means there is no AAudio backend so we need a no-op job implementation. */ +static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) +{ + return ma_job_process__noop(pJob); +} +#endif /* AAudio */ + + +/****************************************************************************** + +OpenSL|ES Backend + +******************************************************************************/ +#ifdef MA_HAS_OPENSL +#include +#ifdef MA_ANDROID +#include +#endif + +typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); + +/* OpenSL|ES has one-per-application objects :( */ +static SLObjectItf g_maEngineObjectSL = NULL; +static SLEngineItf g_maEngineSL = NULL; +static ma_uint32 g_maOpenSLInitCounter = 0; +static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ + +#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) +#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) +#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) +#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) + +#ifdef MA_ANDROID +#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) +#else +#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) +#endif + +static ma_result ma_result_from_OpenSL(SLuint32 result) +{ + switch (result) + { + case SL_RESULT_SUCCESS: return MA_SUCCESS; + case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; + case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; + case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; + case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; + case SL_RESULT_RESOURCE_LOST: return MA_ERROR; + case SL_RESULT_IO_ERROR: return MA_IO_ERROR; + case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; + case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; + case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; + case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; + case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; + case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; + case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; + case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; + case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; + case SL_RESULT_CONTROL_LOST: return MA_ERROR; + default: return MA_ERROR; + } +} + +/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ +static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) +{ + switch (id) + { + case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; + case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; + case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; + case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; + case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; + case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; + case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; + case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; + case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; + case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; + case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; + case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; + case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; + case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; + case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; + case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; + case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; + case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ +static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) +{ + switch (id) + { + case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; + case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; + case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; + case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; + case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; + case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; + case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; + case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; + case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; + case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; + case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; + case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; + case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; + case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; + case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; + case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; + case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; + case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; + default: return 0; + } +} + +/* Converts a channel mapping to an OpenSL-style channel mask. */ +static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) +{ + SLuint32 channelMask = 0; + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); + } + + return channelMask; +} + +/* Converts an OpenSL-style channel mask to a miniaudio channel map. */ +static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) +{ + if (channels == 1 && channelMask == 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else if (channels == 2 && channelMask == 0) { + pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; + pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; + } else { + if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { + pChannelMap[0] = MA_CHANNEL_MONO; + } else { + /* Just iterate over each bit. */ + ma_uint32 iChannel = 0; + ma_uint32 iBit; + for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { + SLuint32 bitValue = (channelMask & (1UL << iBit)); + if (bitValue != 0) { + /* The bit is set. */ + pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); + iChannel += 1; + } + } + } + } +} + +static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) +{ + if (samplesPerSec <= SL_SAMPLINGRATE_8) { + return SL_SAMPLINGRATE_8; + } + if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { + return SL_SAMPLINGRATE_11_025; + } + if (samplesPerSec <= SL_SAMPLINGRATE_12) { + return SL_SAMPLINGRATE_12; + } + if (samplesPerSec <= SL_SAMPLINGRATE_16) { + return SL_SAMPLINGRATE_16; + } + if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { + return SL_SAMPLINGRATE_22_05; + } + if (samplesPerSec <= SL_SAMPLINGRATE_24) { + return SL_SAMPLINGRATE_24; + } + if (samplesPerSec <= SL_SAMPLINGRATE_32) { + return SL_SAMPLINGRATE_32; + } + if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { + return SL_SAMPLINGRATE_44_1; + } + if (samplesPerSec <= SL_SAMPLINGRATE_48) { + return SL_SAMPLINGRATE_48; + } + + /* Android doesn't support more than 48000. */ +#ifndef MA_ANDROID + if (samplesPerSec <= SL_SAMPLINGRATE_64) { + return SL_SAMPLINGRATE_64; + } + if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { + return SL_SAMPLINGRATE_88_2; + } + if (samplesPerSec <= SL_SAMPLINGRATE_96) { + return SL_SAMPLINGRATE_96; + } + if (samplesPerSec <= SL_SAMPLINGRATE_192) { + return SL_SAMPLINGRATE_192; + } +#endif + + return SL_SAMPLINGRATE_16; +} + + +static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) +{ + switch (streamType) { + case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; + case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; + case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; + case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; + case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; + case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; + default: break; + } + + return SL_ANDROID_STREAM_VOICE; +} + +static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) +{ + switch (recordingPreset) { + case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; + case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; + case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; + case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; + default: break; + } + + return SL_ANDROID_RECORDING_PRESET_NONE; +} + + +static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + /* + TODO: Test Me. + + This is currently untested, so for now we are just returning default devices. + */ +#if 0 && !defined(MA_ANDROID) + ma_bool32 isTerminated = MA_FALSE; + + SLuint32 pDeviceIDs[128]; + SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); + + SLAudioIODeviceCapabilitiesItf deviceCaps; + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + /* The interface may not be supported so just report a default device. */ + goto return_default_device; + } + + /* Playback */ + if (!isTerminated) { + resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; + + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); + + ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + } + + /* Capture */ + if (!isTerminated) { + resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = pDeviceIDs[iDevice]; + + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); + if (resultSL == SL_RESULT_SUCCESS) { + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); + + ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + if (cbResult == MA_FALSE) { + isTerminated = MA_TRUE; + break; + } + } + } + } + + return MA_SUCCESS; +#else + goto return_default_device; +#endif + +return_default_device:; + cbResult = MA_TRUE; + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + + return MA_SUCCESS; +} + +static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; + pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; + pDeviceInfo->nativeDataFormatCount += 1; +} + +static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) +{ + ma_uint32 minChannels = 1; + ma_uint32 maxChannels = 2; + ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; + ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; + ma_uint32 iChannel; + ma_uint32 iSampleRate; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(pDeviceInfo != NULL); + + /* + Each sample format can support mono and stereo, and we'll support a small subset of standard + rates (up to 48000). A better solution would be to somehow find a native sample rate. + */ + for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { + for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { + ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; + if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { + ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); + } + } + } +} + +static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + /* + TODO: Test Me. + + This is currently untested, so for now we are just returning default devices. + */ +#if 0 && !defined(MA_ANDROID) + SLAudioIODeviceCapabilitiesItf deviceCaps; + SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); + if (resultSL != SL_RESULT_SUCCESS) { + /* The interface may not be supported so just report a default device. */ + goto return_default_device; + } + + if (deviceType == ma_device_type_playback) { + SLAudioOutputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); + } else { + SLAudioInputDescriptor desc; + resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); + if (resultSL != SL_RESULT_SUCCESS) { + return ma_result_from_OpenSL(resultSL); + } + + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); + } + + goto return_detailed_info; +#else + goto return_default_device; +#endif + +return_default_device: + if (pDeviceID != NULL) { + if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || + (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { + return MA_NO_DEVICE; /* Don't know the device. */ + } + } + + /* ID and Name / Description */ + if (deviceType == ma_device_type_playback) { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + pDeviceInfo->isDefault = MA_TRUE; + + goto return_detailed_info; + + +return_detailed_info: + + /* + For now we're just outputting a set of values that are supported by the API but not necessarily supported + by the device natively. Later on we should work on this so that it more closely reflects the device's + actual native format. + */ + pDeviceInfo->nativeDataFormatCount = 0; +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); +#endif + ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); + ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); + + return MA_SUCCESS; +} + + +#ifdef MA_ANDROID +/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ +static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + size_t periodSizeInBytes; + ma_uint8* pBuffer; + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + (void)pBufferQueue; + + /* + For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like + OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, + but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( + */ + + /* Don't do anything if the device is not started. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return; + } + + /* Don't do anything if the device is being drained. */ + if (pDevice->opensl.isDrainingCapture) { + return; + } + + periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); + + ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + + pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; +} + +static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + size_t periodSizeInBytes; + ma_uint8* pBuffer; + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + (void)pBufferQueue; + + /* Don't do anything if the device is not started. */ + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return; + } + + /* Don't do anything if the device is being drained. */ + if (pDevice->opensl.isDrainingPlayback) { + return; + } + + periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); + + ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + return; + } + + pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; +} +#endif + +static ma_result ma_device_uninit__opensl(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + if (pDevice->opensl.pAudioRecorderObj) { + MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); + } + + ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + if (pDevice->opensl.pAudioPlayerObj) { + MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); + } + if (pDevice->opensl.pOutputMixObj) { + MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); + } + + ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); + } + + return MA_SUCCESS; +} + +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 +typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; +#else +typedef SLDataFormat_PCM ma_SLDataFormat_PCM; +#endif + +static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) +{ + /* We need to convert our format/channels/rate so that they aren't set to default. */ + if (format == ma_format_unknown) { + format = MA_DEFAULT_FORMAT; + } + if (channels == 0) { + channels = MA_DEFAULT_CHANNELS; + } + if (sampleRate == 0) { + sampleRate = MA_DEFAULT_SAMPLE_RATE; + } + +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + if (format == ma_format_f32) { + pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; + pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + } else { + pDataFormat->formatType = SL_DATAFORMAT_PCM; + } +#else + pDataFormat->formatType = SL_DATAFORMAT_PCM; +#endif + + pDataFormat->numChannels = channels; + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ + pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; + pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); + pDataFormat->endianness = (ma_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. + - Only u8 and s16 formats are supported. + - Maximum sample rate of 48000. + */ +#ifdef MA_ANDROID + if (pDataFormat->numChannels > 2) { + pDataFormat->numChannels = 2; + } +#if __ANDROID_API__ >= 21 + if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + /* It's floating point. */ + MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + if (pDataFormat->bitsPerSample > 32) { + pDataFormat->bitsPerSample = 32; + } + } else { + if (pDataFormat->bitsPerSample > 16) { + pDataFormat->bitsPerSample = 16; + } + } +#else + if (pDataFormat->bitsPerSample > 16) { + pDataFormat->bitsPerSample = 16; + } +#endif + if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { + ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; + } +#endif + + pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ + + return MA_SUCCESS; +} + +static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_bool32 isFloatingPoint = MA_FALSE; +#if defined(MA_ANDROID) && __ANDROID_API__ >= 21 + if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { + MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); + isFloatingPoint = MA_TRUE; + } +#endif + if (isFloatingPoint) { + if (pDataFormat->bitsPerSample == 32) { + *pFormat = ma_format_f32; + } + } else { + if (pDataFormat->bitsPerSample == 8) { + *pFormat = ma_format_u8; + } else if (pDataFormat->bitsPerSample == 16) { + *pFormat = ma_format_s16; + } else if (pDataFormat->bitsPerSample == 24) { + *pFormat = ma_format_s24; + } else if (pDataFormat->bitsPerSample == 32) { + *pFormat = ma_format_s32; + } + } + + *pChannels = pDataFormat->numChannels; + *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; + ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); + + return MA_SUCCESS; +} + +static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ +#ifdef MA_ANDROID + SLDataLocator_AndroidSimpleBufferQueue queue; + SLresult resultSL; + size_t bufferSizeInBytes; + SLInterfaceID itfIDs[2]; + const SLboolean itfIDsRequired[] = { + SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ + SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ + }; +#endif + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* + For now, only supporting Android implementations of OpenSL|ES since that's the only one I've + been able to test with and I currently depend on Android-specific extensions (simple buffer + queues). + */ +#ifdef MA_ANDROID + itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; + + /* No exclusive mode with OpenSL|ES. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* Now we can start initializing the device properly. */ + MA_ASSERT(pDevice != NULL); + MA_ZERO_OBJECT(&pDevice->opensl); + + queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + ma_SLDataFormat_PCM pcm; + SLDataLocator_IODevice locatorDevice; + SLDataSource source; + SLDataSink sink; + SLAndroidConfigurationItf pRecorderConfig; + + ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); + + locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; + locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; + locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ + locatorDevice.device = NULL; + + source.pLocator = &locatorDevice; + source.pFormat = NULL; + + queue.numBuffers = pDescriptorCapture->periodCount; + + sink.pLocator = &queue; + sink.pFormat = (SLDataFormat_PCM*)&pcm; + + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { + /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ + pcm.formatType = SL_DATAFORMAT_PCM; + pcm.numChannels = 1; + ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ + pcm.bitsPerSample = 16; + pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ + pcm.channelMask = 0; + resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + } + + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); + return ma_result_from_OpenSL(resultSL); + } + + + /* Set the recording preset before realizing the player. */ + if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); + if (resultSL == SL_RESULT_SUCCESS) { + SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); + resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); + if (resultSL != SL_RESULT_SUCCESS) { + /* Failed to set the configuration. Just keep going. */ + } + } + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); + } + + /* The internal format is determined by the "pcm" object. */ + ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); + + /* Buffer. */ + pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); + pDevice->opensl.currentBufferIndexCapture = 0; + + bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; + pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pDevice->opensl.pBufferCapture == NULL) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; + } + MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_SLDataFormat_PCM pcm; + SLDataSource source; + SLDataLocator_OutputMix outmixLocator; + SLDataSink sink; + SLAndroidConfigurationItf pPlayerConfig; + + ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); + + resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); + return ma_result_from_OpenSL(resultSL); + } + + /* Set the output device. */ + if (pDescriptorPlayback->pDeviceID != NULL) { + SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; + MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); + } + + queue.numBuffers = pDescriptorPlayback->periodCount; + + source.pLocator = &queue; + source.pFormat = (SLDataFormat_PCM*)&pcm; + + outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; + + sink.pLocator = &outmixLocator; + sink.pFormat = NULL; + + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { + /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ + pcm.formatType = SL_DATAFORMAT_PCM; + pcm.numChannels = 2; + ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; + pcm.bitsPerSample = 16; + pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ + pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); + } + + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); + return ma_result_from_OpenSL(resultSL); + } + + + /* Set the stream type before realizing the player. */ + if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); + if (resultSL == SL_RESULT_SUCCESS) { + SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); + resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); + if (resultSL != SL_RESULT_SUCCESS) { + /* Failed to set the configuration. Just keep going. */ + } + } + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); + return ma_result_from_OpenSL(resultSL); + } + + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); + if (resultSL != SL_RESULT_SUCCESS) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); + return ma_result_from_OpenSL(resultSL); + } + + /* The internal format is determined by the "pcm" object. */ + ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); + + /* Buffer. */ + pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); + pDevice->opensl.currentBufferIndexPlayback = 0; + + bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; + pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pDevice->opensl.pBufferPlayback == NULL) { + ma_device_uninit__opensl(pDevice); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); + return MA_OUT_OF_MEMORY; + } + MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); + } + + return MA_SUCCESS; +#else + return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ +#endif +} + +static ma_result ma_device_start__opensl(ma_device* pDevice) +{ + SLresult resultSL; + size_t periodSizeInBytes; + ma_uint32 iPeriod; + + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); + return ma_result_from_OpenSL(resultSL); + } + + periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); + for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); + return ma_result_from_OpenSL(resultSL); + } + } + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); + return ma_result_from_OpenSL(resultSL); + } + + /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ + if (pDevice->type == ma_device_type_duplex) { + MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); + } else { + ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); + } + + periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); + for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { + resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); + if (resultSL != SL_RESULT_SUCCESS) { + MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); + return ma_result_from_OpenSL(resultSL); + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) +{ + SLAndroidSimpleBufferQueueItf pBufferQueue; + + MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); + + if (pDevice->type == ma_device_type_capture) { + pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; + pDevice->opensl.isDrainingCapture = MA_TRUE; + } else { + pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; + pDevice->opensl.isDrainingPlayback = MA_TRUE; + } + + for (;;) { + SLAndroidSimpleBufferQueueState state; + + MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); + if (state.count == 0) { + break; + } + + ma_sleep(10); + } + + if (pDevice->type == ma_device_type_capture) { + pDevice->opensl.isDrainingCapture = MA_FALSE; + } else { + pDevice->opensl.isDrainingPlayback = MA_FALSE; + } + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__opensl(ma_device* pDevice) +{ + SLresult resultSL; + + MA_ASSERT(pDevice != NULL); + + MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ + if (g_maOpenSLInitCounter == 0) { + return MA_INVALID_OPERATION; + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { + ma_device_drain__opensl(pDevice, ma_device_type_capture); + + resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); + return ma_result_from_OpenSL(resultSL); + } + + MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); + } + + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_device_drain__opensl(pDevice, ma_device_type_playback); + + resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); + if (resultSL != SL_RESULT_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); + return ma_result_from_OpenSL(resultSL); + } + + MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); + } + + /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + + +static ma_result ma_context_uninit__opensl(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_opensl); + (void)pContext; + + /* Uninit global data. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ + + g_maOpenSLInitCounter -= 1; + if (g_maOpenSLInitCounter == 0) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + } + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + return MA_SUCCESS; +} + +static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) +{ + /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ + ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); + if (p == NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); + return MA_NO_BACKEND; + } + + *pHandle = *p; + return MA_SUCCESS; +} + +static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) +{ + g_maOpenSLInitCounter += 1; + if (g_maOpenSLInitCounter == 1) { + SLresult resultSL; + + resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); + if (resultSL != SL_RESULT_SUCCESS) { + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + + (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); + + resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); + if (resultSL != SL_RESULT_SUCCESS) { + (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); + g_maOpenSLInitCounter -= 1; + return ma_result_from_OpenSL(resultSL); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + ma_result result; + +#if !defined(MA_NO_RUNTIME_LINKING) + size_t i; + const char* libOpenSLESNames[] = { + "libOpenSLES.so" + }; +#endif + + MA_ASSERT(pContext != NULL); + + (void)pConfig; + +#if !defined(MA_NO_RUNTIME_LINKING) + /* + Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One + report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime + and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any + references to the symbols and will hopefully skip the checks. + */ + for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { + pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); + if (pContext->opensl.libOpenSLES != NULL) { + break; + } + } + + if (pContext->opensl.libOpenSLES == NULL) { + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); + return MA_NO_BACKEND; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + return result; + } + + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); + if (pContext->opensl.slCreateEngine == NULL) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); + return MA_NO_BACKEND; + } +#else + pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; + pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; + pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; + pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; + pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; + pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; + pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; + pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; +#endif + + + /* Initialize global data first if applicable. */ + ma_spinlock_lock(&g_maOpenSLSpinlock); + { + result = ma_context_init_engine_nolock__opensl(pContext); + } + ma_spinlock_unlock(&g_maOpenSLSpinlock); + + if (result != MA_SUCCESS) { + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); + ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); + return result; + } + + pCallbacks->onContextInit = ma_context_init__opensl; + pCallbacks->onContextUninit = ma_context_uninit__opensl; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; + pCallbacks->onDeviceInit = ma_device_init__opensl; + pCallbacks->onDeviceUninit = ma_device_uninit__opensl; + pCallbacks->onDeviceStart = ma_device_start__opensl; + pCallbacks->onDeviceStop = ma_device_stop__opensl; + pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* OpenSL|ES */ + + +/****************************************************************************** + +Web Audio Backend + +******************************************************************************/ +#ifdef MA_HAS_WEBAUDIO +#include + +#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32))) + #include + #define MA_SUPPORT_AUDIO_WORKLETS +#endif + +/* +TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS. +*/ +#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS) + #define MA_USE_AUDIO_WORKLETS +#endif + +/* The thread stack size must be a multiple of 16. */ +#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE +#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 16384 +#endif + +#if defined(MA_USE_AUDIO_WORKLETS) +#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced" +#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive" +#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback" +#endif + +static ma_bool32 ma_is_capture_supported__webaudio() +{ + return EM_ASM_INT({ + return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); + }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ +} + +#ifdef __cplusplus +extern "C" { +#endif +void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_malloc(sz, pAllocationCallbacks); +} + +void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_free(p, pAllocationCallbacks); +} + +void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) +{ + ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); +} + +void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) +{ + ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); +} +#ifdef __cplusplus +} +#endif + +static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_bool32 cbResult = MA_TRUE; + + MA_ASSERT(pContext != NULL); + MA_ASSERT(callback != NULL); + + /* Only supporting default devices for now. */ + + /* Playback. */ + if (cbResult) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ + cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); + } + + /* Capture. */ + if (cbResult) { + if (ma_is_capture_supported__webaudio()) { + ma_device_info deviceInfo; + MA_ZERO_OBJECT(&deviceInfo); + ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ + cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + MA_ASSERT(pContext != NULL); + + if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { + return MA_NO_DEVICE; + } + + MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); + + /* Only supporting default devices for now. */ + (void)pDeviceID; + if (deviceType == ma_device_type_playback) { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } + + /* Only supporting default devices. */ + pDeviceInfo->isDefault = MA_TRUE; + + /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ + pDeviceInfo->nativeDataFormats[0].flags = 0; + pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; + pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ + pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ + try { + var temp = new (window.AudioContext || window.webkitAudioContext)(); + var sampleRate = temp.sampleRate; + temp.close(); + return sampleRate; + } catch(e) { + return 0; + } + }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + + if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { + return MA_NO_DEVICE; + } + + pDeviceInfo->nativeDataFormatCount = 1; + + return MA_SUCCESS; +} + +static ma_result ma_device_uninit__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + #if defined(MA_USE_AUDIO_WORKLETS) + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + }, pDevice->webaudio.deviceIndex); + + emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); + } + #else + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + device.pDevice = undefined; + }, pDevice->webaudio.deviceIndex); + } + #endif + + /* Clean up the device on the JS side. */ + EM_ASM({ + miniaudio.untrack_device_by_index($0); + }, pDevice->webaudio.deviceIndex); + + ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + + return MA_SUCCESS; +} + +#if !defined(MA_USE_AUDIO_WORKLETS) +static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + /* + There have been reports of the default buffer size being too small on some browsers. If we're using + the default buffer size, we'll make sure the period size is bigger than our standard defaults. + */ + ma_uint32 periodSizeInFrames; + + if (nativeSampleRate == 0) { + nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); + } + } else { + periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + periodSizeInFrames = pDescriptor->periodSizeInFrames; + } + + /* The size of the buffer must be a power of 2 and between 256 and 16384. */ + if (periodSizeInFrames < 256) { + periodSizeInFrames = 256; + } else if (periodSizeInFrames > 16384) { + periodSizeInFrames = 16384; + } else { + periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); + } + + return periodSizeInFrames; +} +#endif + + +#if defined(MA_USE_AUDIO_WORKLETS) +typedef struct +{ + ma_device* pDevice; + const ma_device_config* pConfig; + ma_device_descriptor* pDescriptorPlayback; + ma_device_descriptor* pDescriptorCapture; +} ma_audio_worklet_thread_initialized_data; + +static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) +{ + ma_device* pDevice = (ma_device*)pUserData; + ma_uint32 frameCount; + + (void)paramCount; + (void)pParams; + + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return EM_TRUE; + } + + /* + The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels + like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer + to variables instead of a hard coded number. In any case, will follow along for the time being. + + Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio + for further processing. + */ + frameCount = 128; + + if (inputCount > 0) { + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; + } + } + + ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + } + + if (outputCount > 0) { + /* If it's a capture-only device, we'll need to output silence. */ + if (pDevice->type == ma_device_type_capture) { + MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + } else { + ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + + /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; + } + } + } + } + + return EM_TRUE; +} + + +static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; + int channels = 0; + size_t intermediaryBufferSizeInFrames; + int sampleRate; + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.initResult = MA_ERROR; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + /* The next step is to initialize the audio worklet node. */ + MA_ZERO_OBJECT(&audioWorkletOptions); + + /* + The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel + count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an + output channel count on the capture side. This is slightly confusing for capture mode because intuitively you + wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have + proper control over the channel count. In the capture case, we'll have to output silence to it's output node. + */ + if (pParameters->pConfig->deviceType == ma_device_type_capture) { + channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); + audioWorkletOptions.numberOfInputs = 1; + } else { + channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); + + if (pParameters->pConfig->deviceType == ma_device_type_duplex) { + audioWorkletOptions.numberOfInputs = 1; + } else { + audioWorkletOptions.numberOfInputs = 0; + } + } + + audioWorkletOptions.numberOfOutputs = 1; + audioWorkletOptions.outputChannelCounts = &channels; + + + /* + Now that we know the channel count to use we can allocate the intermediary buffer. The + intermediary buffer is used for interleaving and deinterleaving. + */ + intermediaryBufferSizeInFrames = 128; + + pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); + if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { + pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + + pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + + /* With the audio worklet initialized we can now attach it to the graph. */ + if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { + ma_result attachmentResult = (ma_result)EM_ASM_INT({ + var getUserMediaResult = 0; + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + audioContext.streamNode = audioContext.createMediaStreamSource(stream); + audioContext.streamNode.connect(audioWorklet); + audioWorklet.connect(audioContext.destination); + getUserMediaResult = 0; /* 0 = MA_SUCCESS */ + }) + .catch(function(error) { + console.log("navigator.mediaDevices.getUserMedia Failed: " + error); + getUserMediaResult = -1; /* -1 = MA_ERROR */ + }); + + return getUserMediaResult; + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); + emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } + + /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ + if (pParameters->pConfig->deviceType == ma_device_type_playback) { + ma_result attachmentResult = (ma_result)EM_ASM_INT({ + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + audioWorklet.connect(audioContext.destination); + return 0; /* 0 = MA_SUCCESS */ + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } + + /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ + sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); + + if (pParameters->pDescriptorCapture != NULL) { + pParameters->pDescriptorCapture->format = ma_format_f32; + pParameters->pDescriptorCapture->channels = (ma_uint32)channels; + pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); + pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorCapture->periodCount = 1; + } + + if (pParameters->pDescriptorPlayback != NULL) { + pParameters->pDescriptorPlayback->format = ma_format_f32; + pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; + pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); + pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorPlayback->periodCount = 1; + } + + /* At this point we're done and we can return. */ + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = MA_SUCCESS; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); +} + +static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) +{ + ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; + WebAudioWorkletProcessorCreateOptions workletProcessorOptions; + + MA_ASSERT(pParameters != NULL); + + if (success == EM_FALSE) { + pParameters->pDevice->webaudio.initResult = MA_ERROR; + return; + } + + MA_ZERO_OBJECT(&workletProcessorOptions); + workletProcessorOptions.name = "miniaudio"; /* I'm not entirely sure what to call this. Does this need to be globally unique, or does it need only be unique for a given AudioContext? */ + + emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters); +} +#endif + +static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) +{ + if (pConfig->deviceType == ma_device_type_loopback) { + return MA_DEVICE_TYPE_NOT_SUPPORTED; + } + + /* No exclusive mode with Web Audio. */ + if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || + ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { + return MA_SHARE_MODE_NOT_SUPPORTED; + } + + /* + With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so + it might be worthwhile to look into that as well. + */ + #if defined(MA_USE_AUDIO_WORKLETS) + { + EmscriptenWebAudioCreateAttributes audioContextAttributes; + ma_audio_worklet_thread_initialized_data* pInitParameters; + void* pStackBuffer; + + if (pConfig->performanceProfile == ma_performance_profile_conservative) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; + } else { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + } + + /* + In my testing, Firefox does not seem to capture audio data properly if the sample rate is set + to anything other than 48K. This does not seem to be the case for other browsers. For this reason, + if the device type is anything other than playback, we'll leave the sample rate as-is and let the + browser pick the appropriate rate for us. + */ + if (pConfig->deviceType == ma_device_type_playback) { + audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; + } else { + audioContextAttributes.sampleRate = 0; + } + + /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ + pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + + + /* + With the context created we can now create the worklet. We can only have a single worklet per audio + context which means we'll need to craft this appropriately to handle duplex devices correctly. + */ + + /* + We now need to create a worker thread. This is a bit weird because we need to allocate our + own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to + allocate this on the heap to keep it simple. + */ + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + if (pStackBuffer == NULL) { + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ + pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + if (pInitParameters == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptorPlayback = pDescriptorPlayback; + pInitParameters->pDescriptorCapture = pDescriptorCapture; + + /* + We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of + the Emscripten WebAudio stuff is asynchronous. + */ + pDevice->webaudio.initResult = MA_BUSY; + { + emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + } + while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + + /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ + if (pDevice->webaudio.initResult != MA_SUCCESS) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return pDevice->webaudio.initResult; + } + + /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ + pDevice->webaudio.deviceIndex = EM_ASM_INT({ + return miniaudio.track_device({ + webaudio: emscriptenGetAudioObject($0), + state: 1 /* 1 = ma_device_state_stopped */ + }); + }, pDevice->webaudio.audioContext); + + return MA_SUCCESS; + } + #else + { + /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ + ma_uint32 deviceIndex; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + + /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ + if (pConfig->deviceType == ma_device_type_capture) { + channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + } else { + channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + } + + /* + When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's + native rate. For this reason we're leaving the sample rate untouched for capture devices. + */ + if (pConfig->deviceType == ma_device_type_playback) { + sampleRate = pDescriptorPlayback->sampleRate; + } else { + sampleRate = 0; /* Let the browser decide when capturing. */ + } + + /* The period size needs to be a power of 2. */ + if (pConfig->deviceType == ma_device_type_capture) { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); + } else { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); + } + + /* We need an intermediary buffer for doing interleaving and deinterleaving. */ + pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pDevice->webaudio.pIntermediaryBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceIndex = EM_ASM_INT({ + var deviceType = $0; + var channels = $1; + var sampleRate = $2; + var bufferSize = $3; + var pIntermediaryBuffer = $4; + var pDevice = $5; + + if (typeof(window.miniaudio) === 'undefined') { + return -1; /* Context not initialized. */ + } + + var device = {}; + + /* First thing we need is an AudioContext. */ + var audioContextOptions = {}; + if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { + audioContextOptions.sampleRate = sampleRate; + } + + device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); + device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ + device.state = window.miniaudio.device_state.stopped; + + /* + We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we + need to specify an output and configure the channel count there. + */ + var channelCountIn = 0; + var channelCountOut = channels; + if (deviceType != window.miniaudio.device_type.playback) { + channelCountIn = channels; + } + + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); + + /* The node processing callback. */ + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + } + + /* Do the capture side first. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + /* The data must be interleaved before being processed miniaudio. */ + for (var iChannel = 0; iChannel < channels; iChannel += 1) { + var inputBuffer = e.inputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; + } + } + + _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + } + + if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { + _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + } + } + } else { + /* It's a capture-only device. Make sure the output is silenced. */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } + }; + + /* Now we need to connect our node to the graph. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + console.log("Failed to get user media: " + error); + }); + } + + if (deviceType == miniaudio.device_type.playback) { + device.scriptNode.connect(device.webaudio.destination); + } + + device.pDevice = pDevice; + + return miniaudio.track_device(device); + }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); + + if (deviceIndex < 0) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + pDevice->webaudio.deviceIndex = deviceIndex; + + /* Grab the sample rate from the audio context directly. */ + sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + + if (pDescriptorCapture != NULL) { + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = channels; + pDescriptorCapture->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; + } + + if (pDescriptorPlayback != NULL) { + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = channels; + pDescriptorPlayback->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; + } + + return MA_SUCCESS; + } + #endif +} + +static ma_result ma_device_start__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = miniaudio.device_state.started; + }, pDevice->webaudio.deviceIndex); + + return MA_SUCCESS; +} + +static ma_result ma_device_stop__webaudio(ma_device* pDevice) +{ + MA_ASSERT(pDevice != NULL); + + /* + From the WebAudio API documentation for AudioContext.suspend(): + + Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the + destination, and then allows the system to release its claim on audio hardware. + + I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to + do any kind of explicit draining. + */ + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = miniaudio.device_state.stopped; + }, pDevice->webaudio.deviceIndex); + + ma_device__on_notification_stopped(pDevice); + + return MA_SUCCESS; +} + +static ma_result ma_context_uninit__webaudio(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + MA_ASSERT(pContext->backend == ma_backend_webaudio); + + (void)pContext; /* Unused. */ + + /* Remove the global miniaudio object from window if there are no more references to it. */ + EM_ASM({ + if (typeof(window.miniaudio) !== 'undefined') { + window.miniaudio.referenceCount -= 1; + if (window.miniaudio.referenceCount === 0) { + delete window.miniaudio; + } + } + }); + + return MA_SUCCESS; +} + +static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) +{ + int resultFromJS; + + MA_ASSERT(pContext != NULL); + + (void)pConfig; /* Unused. */ + + /* Here is where our global JavaScript object is initialized. */ + resultFromJS = EM_ASM_INT({ + if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { + return 0; /* Web Audio not supported. */ + } + + if (typeof(window.miniaudio) === 'undefined') { + window.miniaudio = { + referenceCount: 0 + }; + + /* Device types. */ + window.miniaudio.device_type = {}; + window.miniaudio.device_type.playback = $0; + window.miniaudio.device_type.capture = $1; + window.miniaudio.device_type.duplex = $2; + + /* Device states. */ + window.miniaudio.device_state = {}; + window.miniaudio.device_state.stopped = $3; + window.miniaudio.device_state.started = $4; + + /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + miniaudio.devices = []; + + miniaudio.track_device = function(device) { + /* Try inserting into a free slot first. */ + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == null) { + miniaudio.devices[iDevice] = device; + return iDevice; + } + } + + /* Getting here means there is no empty slots in the array so we just push to the end. */ + miniaudio.devices.push(device); + return miniaudio.devices.length - 1; + }; + + miniaudio.untrack_device_by_index = function(deviceIndex) { + /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ + miniaudio.devices[deviceIndex] = null; + + /* Trim the array if possible. */ + while (miniaudio.devices.length > 0) { + if (miniaudio.devices[miniaudio.devices.length-1] == null) { + miniaudio.devices.pop(); + } else { + break; + } + } + }; + + miniaudio.untrack_device = function(device) { + for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { + if (miniaudio.devices[iDevice] == device) { + return miniaudio.untrack_device_by_index(iDevice); + } + } + }; + + miniaudio.get_device_by_index = function(deviceIndex) { + return miniaudio.devices[deviceIndex]; + }; + + miniaudio.unlock_event_types = (function(){ + return ['touchend', 'click']; + })(); + + miniaudio.unlock = function() { + for(var i = 0; i < miniaudio.devices.length; ++i) { + var device = miniaudio.devices[i]; + if (device != null && + device.webaudio != null && + device.state === window.miniaudio.device_state.started) { + + device.webaudio.resume().then(() => { + Module._ma_device__on_notification_unlocked(device.pDevice); + }, + (error) => {console.error("Failed to resume audiocontext", error); + }); + } + } + miniaudio.unlock_event_types.map(function(event_type) { + document.removeEventListener(event_type, miniaudio.unlock, true); + }); + }; + + miniaudio.unlock_event_types.map(function(event_type) { + document.addEventListener(event_type, miniaudio.unlock, true); + }); + } + + window.miniaudio.referenceCount += 1; + + return 1; + }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); + + if (resultFromJS != 1) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pCallbacks->onContextInit = ma_context_init__webaudio; + pCallbacks->onContextUninit = ma_context_uninit__webaudio; + pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; + pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; + pCallbacks->onDeviceInit = ma_device_init__webaudio; + pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; + pCallbacks->onDeviceStart = ma_device_start__webaudio; + pCallbacks->onDeviceStop = ma_device_stop__webaudio; + pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ + pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ + + return MA_SUCCESS; +} +#endif /* Web Audio */ + + + +static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) +{ + /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ + if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { + ma_uint32 iChannel; + + if (channels == 0 || channels > MA_MAX_CHANNELS) { + return MA_FALSE; /* Channel count out of range. */ + } + + /* A channel cannot be present in the channel map more than once. */ + for (iChannel = 0; iChannel < channels; ++iChannel) { + ma_uint32 jChannel; + for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { + if (pChannelMap[iChannel] == pChannelMap[jChannel]) { + return MA_FALSE; + } + } + } + } + + return MA_TRUE; +} + + +static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) +{ + MA_ASSERT(pContext != NULL); + + if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { + if (pContext->callbacks.onDeviceDataLoop == NULL) { + return MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } +} + + +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) +{ + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + if (pDevice->capture.format == ma_format_unknown) { + pDevice->capture.format = pDevice->capture.internalFormat; + } + if (pDevice->capture.channels == 0) { + pDevice->capture.channels = pDevice->capture.internalChannels; + } + if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { + MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); + if (pDevice->capture.internalChannels == pDevice->capture.channels) { + ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); + } else { + if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); + } + } + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (pDevice->playback.format == ma_format_unknown) { + pDevice->playback.format = pDevice->playback.internalFormat; + } + if (pDevice->playback.channels == 0) { + pDevice->playback.channels = pDevice->playback.internalChannels; + } + if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { + MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); + if (pDevice->playback.internalChannels == pDevice->playback.channels) { + ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); + } else { + if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); + } + } + } + } + + if (pDevice->sampleRate == 0) { + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + pDevice->sampleRate = pDevice->capture.internalSampleRate; + } else { + pDevice->sampleRate = pDevice->playback.internalSampleRate; + } + } + + /* Data converters. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + /* Converting from internal device format to client format. */ + ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); + converterConfig.formatIn = pDevice->capture.internalFormat; + converterConfig.channelsIn = pDevice->capture.internalChannels; + converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; + converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; + converterConfig.formatOut = pDevice->capture.format; + converterConfig.channelsOut = pDevice->capture.channels; + converterConfig.sampleRateOut = pDevice->sampleRate; + converterConfig.pChannelMapOut = pDevice->capture.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); + if (result != MA_SUCCESS) { + return result; + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + /* Converting from client format to device format. */ + ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); + converterConfig.formatIn = pDevice->playback.format; + converterConfig.channelsIn = pDevice->playback.channels; + converterConfig.sampleRateIn = pDevice->sampleRate; + converterConfig.pChannelMapIn = pDevice->playback.channelMap; + converterConfig.formatOut = pDevice->playback.internalFormat; + converterConfig.channelsOut = pDevice->playback.internalChannels; + converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; + converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; + converterConfig.channelMixMode = pDevice->playback.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + + /* Make sure the old converter is uninitialized first. */ + if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); + if (result != MA_SUCCESS) { + return result; + } + } + + + /* + If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's + a couple of situations where we'll need a heap allocated cache. + + The first is a duplex device for backends that use a callback for data delivery. The reason + this is needed is that the input stage needs to have a buffer to place the input data while it + waits for the playback stage, after which the miniaudio data callback will get fired. This is + not needed for backends that use a blocking API because miniaudio manages temporary buffers on + the stack to achieve this. + + The other situation is when the data converter does not have the ability to query the number + of input frames that are required in order to process a given number of output frames. When + performing data conversion, it's useful if miniaudio know exactly how many frames it needs + from the client in order to generate a given number of output frames. This way, only exactly + the number of frames are needed to be read from the client which means no cache is necessary. + On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read + in fixed sized chunks and then cache any residual unused input frames, those of which will be + processed at a later stage. + */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + ma_uint64 unused; + + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; + + if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ + ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ + { + /* We need a heap allocated cache. We want to size this based on the period size. */ + void* pNewInputCache; + ma_uint64 newInputCacheCap; + ma_uint64 newInputCacheSizeInBytes; + + newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); + + newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + if (newInputCacheSizeInBytes > MA_SIZE_MAX) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ + } + + pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); + if (pNewInputCache == NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + return MA_OUT_OF_MEMORY; + } + + pDevice->playback.pInputCache = pNewInputCache; + pDevice->playback.inputCacheCap = newInputCacheCap; + } else { + /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + pDevice->playback.pInputCache = NULL; + pDevice->playback.inputCacheCap = 0; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* Capture. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->capture.internalFormat = pDescriptorCapture->format; + pDevice->capture.internalChannels = pDescriptorCapture->channels; + pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); + pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; + pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; + + if (pDevice->capture.internalPeriodSizeInFrames == 0) { + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); + } + } + + /* Playback. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { + return MA_INVALID_ARGS; + } + + pDevice->playback.internalFormat = pDescriptorPlayback->format; + pDevice->playback.internalChannels = pDescriptorPlayback->channels; + pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); + pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; + pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; + + if (pDevice->playback.internalPeriodSizeInFrames == 0) { + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + } + } + + /* + The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. + For loopback devices, we need to retrieve the name of the playback device. + */ + { + ma_device_info deviceInfo; + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorCapture->pDeviceID == NULL) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); + } + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (pDescriptorPlayback->pDeviceID == NULL) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); + } + } + } + } + + /* Update data conversion. */ + return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ +} + + +static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) +{ + ma_device* pDevice = (ma_device*)pData; +#ifdef MA_WIN32 + HRESULT CoInitializeResult; +#endif + + MA_ASSERT(pDevice != NULL); + +#ifdef MA_WIN32 + CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); +#endif + + /* + When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from + ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately + after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker + thread to signal an event to know when the worker thread is ready for action. + */ + ma_device__set_state(pDevice, ma_device_state_stopped); + ma_event_signal(&pDevice->stopEvent); + + for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ + ma_result startResult; + ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ + + /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ + ma_event_wait(&pDevice->wakeupEvent); + + /* Default result code. */ + pDevice->workResult = MA_SUCCESS; + + /* If the reason for the wake up is that we are terminating, just break from the loop. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + + /* + Getting to this point means the device is wanting to get started. The function that has requested that the device + be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event + in both the success and error case. It's important that the state of the device is set _before_ signaling the event. + */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); + + /* If the device has a start callback, start it now. */ + if (pDevice->pContext->callbacks.onDeviceStart != NULL) { + startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); + } else { + startResult = MA_SUCCESS; + } + + /* + If starting was not successful we'll need to loop back to the start and wait for something + to happen (pDevice->wakeupEvent). + */ + if (startResult != MA_SUCCESS) { + pDevice->workResult = startResult; + ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ + continue; + } + + /* Make sure the state is set appropriately. */ + ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ + ma_event_signal(&pDevice->startEvent); + + ma_device__on_notification_started(pDevice); + + if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { + pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); + } else { + /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ + ma_device_audio_thread__default_read_write(pDevice); + } + + /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ + if (pDevice->pContext->callbacks.onDeviceStop != NULL) { + stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); + } else { + stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ + } + + /* + After the device has stopped, make sure an event is posted. Don't post a stopped event if + stopping failed. This can happen on some backends when the underlying stream has been + stopped due to the device being physically unplugged or disabled via an OS setting. + */ + if (stopResult == MA_SUCCESS) { + ma_device__on_notification_stopped(pDevice); + } + + /* If we stopped because the device has been uninitialized, abort now. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + + /* 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. */ + ma_device__set_state(pDevice, ma_device_state_stopped); + ma_event_signal(&pDevice->stopEvent); + } + +#ifdef MA_WIN32 + if (CoInitializeResult == S_OK) { + ma_CoUninitialize(pDevice->pContext); + } +#endif + + return (ma_thread_result)0; +} + + +/* Helper for determining whether or not the given device is initialized. */ +static ma_bool32 ma_device__is_initialized(ma_device* pDevice) +{ + if (pDevice == NULL) { + return MA_FALSE; + } + + return ma_device_get_state(pDevice) != ma_device_state_uninitialized; +} + + +#ifdef MA_WIN32 +static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) +{ + /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + if (pContext->win32.CoInitializeResult == S_OK) { + ma_CoUninitialize(pContext); + } + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); + #endif + + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); +#else + (void)pContext; +#endif + + return MA_SUCCESS; +} + +static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) +{ +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #if defined(MA_WIN32_DESKTOP) + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + #endif + + /* Ole32.dll */ + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); + if (pContext->win32.hOle32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); +#else + (void)pContext; /* Unused. */ +#endif + + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + return MA_SUCCESS; +} +#else +static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) +{ + (void)pContext; + + return MA_SUCCESS; +} + +static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) +{ + (void)pContext; + + return MA_SUCCESS; +} +#endif + +static ma_result ma_context_init_backend_apis(ma_context* pContext) +{ + ma_result result; +#ifdef MA_WIN32 + result = ma_context_init_backend_apis__win32(pContext); +#else + result = ma_context_init_backend_apis__nix(pContext); +#endif + + return result; +} + +static ma_result ma_context_uninit_backend_apis(ma_context* pContext) +{ + ma_result result; +#ifdef MA_WIN32 + result = ma_context_uninit_backend_apis__win32(pContext); +#else + result = ma_context_uninit_backend_apis__nix(pContext); +#endif + + return result; +} + + +/* The default capacity doesn't need to be too big. */ +#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY +#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 +#endif + +MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) +{ + ma_device_job_thread_config config; + + MA_ZERO_OBJECT(&config); + config.noThread = MA_FALSE; + config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; + config.jobQueueFlags = 0; + + return config; +} + + +static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) +{ + ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; + MA_ASSERT(pJobThread != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_device_job_thread_next(pJobThread, &job); + if (result != MA_SUCCESS) { + break; + } + + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} + +MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJobThread); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + + /* Initialize the job queue before the thread to ensure it's in a valid state. */ + jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); + + result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize job queue. */ + } + + + /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ + if (pConfig->noThread == MA_FALSE) { + result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); + return result; /* Failed to create the job thread. */ + } + + pJobThread->_hasThread = MA_TRUE; + } else { + pJobThread->_hasThread = MA_FALSE; + } + + + return MA_SUCCESS; +} + +MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pJobThread == NULL) { + return; + } + + /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ + { + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + ma_device_job_thread_post(pJobThread, &job); + } + + /* Wait for the thread to terminate naturally. */ + if (pJobThread->_hasThread) { + ma_thread_wait(&pJobThread->thread); + } + + /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ + ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); +} + +MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) +{ + if (pJobThread == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pJobThread->jobQueue, pJob); +} + +MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) +{ + if (pJob == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pJob); + + if (pJobThread == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pJobThread->jobQueue, pJob); +} + + + +MA_API ma_context_config ma_context_config_init(void) +{ + ma_context_config config; + MA_ZERO_OBJECT(&config); + + return config; +} + +MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) +{ + ma_result result; + ma_context_config defaultConfig; + ma_backend defaultBackends[ma_backend_null+1]; + ma_uint32 iBackend; + ma_backend* pBackendsToIterate; + ma_uint32 backendsToIterateCount; + + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pContext); + + /* Always make sure the config is set first to ensure properties are available as soon as possible. */ + if (pConfig == NULL) { + defaultConfig = ma_context_config_init(); + pConfig = &defaultConfig; + } + + /* Allocation callbacks need to come first because they'll be passed around to other areas. */ + result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + /* Get a lot set up first so we can start logging ASAP. */ + if (pConfig->pLog != NULL) { + pContext->pLog = pConfig->pLog; + } else { + result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); + if (result == MA_SUCCESS) { + pContext->pLog = &pContext->log; + } else { + pContext->pLog = NULL; /* Logging is not available. */ + } + } + + pContext->threadPriority = pConfig->threadPriority; + pContext->threadStackSize = pConfig->threadStackSize; + pContext->pUserData = pConfig->pUserData; + + /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ + result = ma_context_init_backend_apis(pContext); + if (result != MA_SUCCESS) { + return result; + } + + for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { + defaultBackends[iBackend] = (ma_backend)iBackend; + } + + pBackendsToIterate = (ma_backend*)backends; + backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (ma_backend*)defaultBackends; + backendsToIterateCount = ma_countof(defaultBackends); + } + + MA_ASSERT(pBackendsToIterate != NULL); + + for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { + ma_backend backend = pBackendsToIterate[iBackend]; + + /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ + MA_ZERO_OBJECT(&pContext->callbacks); + + /* These backends are using the new callback system. */ + switch (backend) { + #ifdef MA_HAS_WASAPI + case ma_backend_wasapi: + { + pContext->callbacks.onContextInit = ma_context_init__wasapi; + } break; + #endif + #ifdef MA_HAS_DSOUND + case ma_backend_dsound: + { + pContext->callbacks.onContextInit = ma_context_init__dsound; + } break; + #endif + #ifdef MA_HAS_WINMM + case ma_backend_winmm: + { + pContext->callbacks.onContextInit = ma_context_init__winmm; + } break; + #endif + #ifdef MA_HAS_COREAUDIO + case ma_backend_coreaudio: + { + pContext->callbacks.onContextInit = ma_context_init__coreaudio; + } break; + #endif + #ifdef MA_HAS_SNDIO + case ma_backend_sndio: + { + pContext->callbacks.onContextInit = ma_context_init__sndio; + } break; + #endif + #ifdef MA_HAS_AUDIO4 + case ma_backend_audio4: + { + pContext->callbacks.onContextInit = ma_context_init__audio4; + } break; + #endif + #ifdef MA_HAS_OSS + case ma_backend_oss: + { + pContext->callbacks.onContextInit = ma_context_init__oss; + } break; + #endif + #ifdef MA_HAS_PULSEAUDIO + case ma_backend_pulseaudio: + { + pContext->callbacks.onContextInit = ma_context_init__pulse; + } break; + #endif + #ifdef MA_HAS_ALSA + case ma_backend_alsa: + { + pContext->callbacks.onContextInit = ma_context_init__alsa; + } break; + #endif + #ifdef MA_HAS_JACK + case ma_backend_jack: + { + pContext->callbacks.onContextInit = ma_context_init__jack; + } break; + #endif + #ifdef MA_HAS_AAUDIO + case ma_backend_aaudio: + { + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__aaudio; + } + } break; + #endif + #ifdef MA_HAS_OPENSL + case ma_backend_opensl: + { + if (ma_is_backend_enabled(backend)) { + pContext->callbacks.onContextInit = ma_context_init__opensl; + } + } break; + #endif + #ifdef MA_HAS_WEBAUDIO + case ma_backend_webaudio: + { + pContext->callbacks.onContextInit = ma_context_init__webaudio; + } break; + #endif + #ifdef MA_HAS_CUSTOM + case ma_backend_custom: + { + /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ + pContext->callbacks = pConfig->custom; + } break; + #endif + #ifdef MA_HAS_NULL + case ma_backend_null: + { + pContext->callbacks.onContextInit = ma_context_init__null; + } break; + #endif + + default: break; + } + + if (pContext->callbacks.onContextInit != NULL) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); + result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); + } else { + /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */ + if (backend != ma_backend_custom) { + result = MA_BACKEND_NOT_ENABLED; + } else { + #if !defined(MA_HAS_CUSTOM) + result = MA_BACKEND_NOT_ENABLED; + #else + result = MA_NO_BACKEND; + #endif + } + } + + /* If this iteration was successful, return. */ + if (result == MA_SUCCESS) { + result = ma_mutex_init(&pContext->deviceEnumLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); + } + + result = ma_mutex_init(&pContext->deviceInfoLock); + if (result != MA_SUCCESS) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); + } + + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); + + pContext->backend = backend; + return result; + } else { + if (result == MA_BACKEND_NOT_ENABLED) { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend)); + } else { + ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); + } + } + } + + /* If we get here it means an error occurred. */ + MA_ZERO_OBJECT(pContext); /* Safety. */ + return MA_NO_BACKEND; +} + +MA_API ma_result ma_context_uninit(ma_context* pContext) +{ + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextUninit != NULL) { + pContext->callbacks.onContextUninit(pContext); + } + + ma_mutex_uninit(&pContext->deviceEnumLock); + ma_mutex_uninit(&pContext->deviceInfoLock); + ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); + ma_context_uninit_backend_apis(pContext); + + if (pContext->pLog == &pContext->log) { + ma_log_uninit(&pContext->log); + } + + return MA_SUCCESS; +} + +MA_API size_t ma_context_sizeof(void) +{ + return sizeof(ma_context); +} + + +MA_API ma_log* ma_context_get_log(ma_context* pContext) +{ + if (pContext == NULL) { + return NULL; + } + + return pContext->pLog; +} + + +MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) +{ + ma_result result; + + if (pContext == NULL || callback == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextEnumerateDevices == NULL) { + return MA_INVALID_OPERATION; + } + + ma_mutex_lock(&pContext->deviceEnumLock); + { + result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); + } + ma_mutex_unlock(&pContext->deviceEnumLock); + + return result; +} + + +static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) +{ + /* + We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device + it's just appended to the end. If it's a playback device it's inserted just before the first capture device. + */ + + /* + First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a + simple fixed size increment for buffer expansion. + */ + const ma_uint32 bufferExpansionCount = 2; + const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; + + if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { + ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; + ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); + if (pNewInfos == NULL) { + return MA_FALSE; /* Out of memory. */ + } + + pContext->pDeviceInfos = pNewInfos; + pContext->deviceInfoCapacity = newCapacity; + } + + if (deviceType == ma_device_type_playback) { + /* Playback. Insert just before the first capture device. */ + + /* The first thing to do is move all of the capture devices down a slot. */ + ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; + size_t iCaptureDevice; + for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { + pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; + } + + /* Now just insert where the first capture device was before moving it down a slot. */ + pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; + pContext->playbackDeviceInfoCount += 1; + } else { + /* Capture. Insert at the end. */ + pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; + pContext->captureDeviceInfoCount += 1; + } + + (void)pUserData; + return MA_TRUE; +} + +MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) +{ + ma_result result; + + /* Safety. */ + if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; + if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; + if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; + if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; + + if (pContext == NULL) { + return MA_INVALID_ARGS; + } + + if (pContext->callbacks.onContextEnumerateDevices == NULL) { + return MA_INVALID_OPERATION; + } + + /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ + ma_mutex_lock(&pContext->deviceEnumLock); + { + /* Reset everything first. */ + pContext->playbackDeviceInfoCount = 0; + pContext->captureDeviceInfoCount = 0; + + /* Now enumerate over available devices. */ + result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); + if (result == MA_SUCCESS) { + /* Playback devices. */ + if (ppPlaybackDeviceInfos != NULL) { + *ppPlaybackDeviceInfos = pContext->pDeviceInfos; + } + if (pPlaybackDeviceCount != NULL) { + *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; + } + + /* Capture devices. */ + if (ppCaptureDeviceInfos != NULL) { + *ppCaptureDeviceInfos = pContext->pDeviceInfos; + /* Capture devices come after playback devices. */ + if (pContext->playbackDeviceInfoCount > 0) { + /* Conditional, because NULL+0 is undefined behavior. */ + *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; + } + } + if (pCaptureDeviceCount != NULL) { + *pCaptureDeviceCount = pContext->captureDeviceInfoCount; + } + } + } + ma_mutex_unlock(&pContext->deviceEnumLock); + + return result; +} + +MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) +{ + ma_result result; + ma_device_info deviceInfo; + + /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ + if (pContext == NULL || pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(&deviceInfo); + + /* Help the backend out by copying over the device ID if we have one. */ + if (pDeviceID != NULL) { + MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); + } + + if (pContext->callbacks.onContextGetDeviceInfo == NULL) { + return MA_INVALID_OPERATION; + } + + ma_mutex_lock(&pContext->deviceInfoLock); + { + result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); + } + ma_mutex_unlock(&pContext->deviceInfoLock); + + *pDeviceInfo = deviceInfo; + return result; +} + +MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) +{ + if (pContext == NULL) { + return MA_FALSE; + } + + return ma_is_loopback_supported(pContext->backend); +} + + +MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) +{ + ma_device_config config; + MA_ZERO_OBJECT(&config); + config.deviceType = deviceType; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ + + return config; +} + +MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) +{ + ma_result result; + ma_device_descriptor descriptorPlayback; + ma_device_descriptor descriptorCapture; + + /* The context can be null, in which case we self-manage it. */ + if (pContext == NULL) { + return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); + } + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDevice); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Check that we have our callbacks defined. */ + if (pContext->callbacks.onDeviceInit == NULL) { + return MA_INVALID_OPERATION; + } + + /* Basic config validation. */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { + if (pConfig->capture.channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { + return MA_INVALID_ARGS; + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + if (pConfig->playback.channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { + return MA_INVALID_ARGS; + } + } + + pDevice->pContext = pContext; + + /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ + pDevice->pUserData = pConfig->pUserData; + pDevice->onData = pConfig->dataCallback; + pDevice->onNotification = pConfig->notificationCallback; + pDevice->onStop = pConfig->stopCallback; + + if (pConfig->playback.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); + pDevice->playback.pID = &pDevice->playback.id; + } else { + pDevice->playback.pID = NULL; + } + + if (pConfig->capture.pDeviceID != NULL) { + MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); + pDevice->capture.pID = &pDevice->capture.id; + } else { + pDevice->capture.pID = NULL; + } + + pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; + pDevice->noClip = pConfig->noClip; + pDevice->noDisableDenormals = pConfig->noDisableDenormals; + pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; + ma_atomic_float_set(&pDevice->masterVolumeFactor, 1); + + pDevice->type = pConfig->deviceType; + pDevice->sampleRate = pConfig->sampleRate; + pDevice->resampling.algorithm = pConfig->resampling.algorithm; + pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; + pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; + pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; + + pDevice->capture.shareMode = pConfig->capture.shareMode; + pDevice->capture.format = pConfig->capture.format; + pDevice->capture.channels = pConfig->capture.channels; + ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); + pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; + pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; + + pDevice->playback.shareMode = pConfig->playback.shareMode; + pDevice->playback.format = pConfig->playback.format; + pDevice->playback.channels = pConfig->playback.channels; + ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); + pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; + pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; + + result = ma_mutex_init(&pDevice->startStopLock); + if (result != MA_SUCCESS) { + return result; + } + + /* + When the device is started, the worker thread is the one that does the actual startup of the backend device. We + use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. + + Each of these semaphores is released internally by the worker thread when the work is completed. The start + semaphore is also used to wake up the worker thread. + */ + result = ma_event_init(&pDevice->wakeupEvent); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + result = ma_event_init(&pDevice->startEvent); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + result = ma_event_init(&pDevice->stopEvent); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + + + MA_ZERO_OBJECT(&descriptorPlayback); + descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; + descriptorPlayback.shareMode = pConfig->playback.shareMode; + descriptorPlayback.format = pConfig->playback.format; + descriptorPlayback.channels = pConfig->playback.channels; + descriptorPlayback.sampleRate = pConfig->sampleRate; + ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); + descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; + descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; + descriptorPlayback.periodCount = pConfig->periods; + + if (descriptorPlayback.periodCount == 0) { + descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; + } + + + MA_ZERO_OBJECT(&descriptorCapture); + descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; + descriptorCapture.shareMode = pConfig->capture.shareMode; + descriptorCapture.format = pConfig->capture.format; + descriptorCapture.channels = pConfig->capture.channels; + descriptorCapture.sampleRate = pConfig->sampleRate; + ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); + descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; + descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; + descriptorCapture.periodCount = pConfig->periods; + + if (descriptorCapture.periodCount == 0) { + descriptorCapture.periodCount = MA_DEFAULT_PERIODS; + } + + + result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + return result; + } + +#if 0 + /* + On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between + the requested format and the internal format. + */ + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + if (!ma_device_descriptor_is_valid(&descriptorCapture)) { + ma_device_uninit(pDevice); + return MA_INVALID_ARGS; + } + + pDevice->capture.internalFormat = descriptorCapture.format; + pDevice->capture.internalChannels = descriptorCapture.channels; + pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; + ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); + pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; + pDevice->capture.internalPeriods = descriptorCapture.periodCount; + + if (pDevice->capture.internalPeriodSizeInFrames == 0) { + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { + ma_device_uninit(pDevice); + return MA_INVALID_ARGS; + } + + pDevice->playback.internalFormat = descriptorPlayback.format; + pDevice->playback.internalChannels = descriptorPlayback.channels; + pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; + ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); + pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; + pDevice->playback.internalPeriods = descriptorPlayback.periodCount; + + if (pDevice->playback.internalPeriodSizeInFrames == 0) { + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); + } + } + + + /* + The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. + For loopback devices, we need to retrieve the name of the playback device. + */ + { + ma_device_info deviceInfo; + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (descriptorCapture.pDeviceID == NULL) { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); + } + } + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); + if (result == MA_SUCCESS) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); + } else { + /* We failed to retrieve the device info. Fall back to a default name. */ + if (descriptorPlayback.pDeviceID == NULL) { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); + } else { + ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); + } + } + } + } + + + ma_device__post_init_setup(pDevice, pConfig->deviceType); +#endif + + result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + + + /* + If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to + be done after post_init_setup() because we'll need access to the sample rate. + */ + if (pConfig->noFixedSizedCallback == MA_FALSE) { + /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ + ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; + if (intermediaryBufferCap == 0) { + intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); + } + + if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { + ma_uint32 intermediaryBufferSizeInBytes; + + pDevice->capture.intermediaryBufferLen = 0; + pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->capture.intermediaryBufferCap == 0) { + pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; + } + + intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); + + pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->capture.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); + pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; + } + + if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { + ma_uint64 intermediaryBufferSizeInBytes; + + pDevice->playback.intermediaryBufferLen = 0; + if (pConfig->deviceType == ma_device_type_duplex) { + pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ + } else { + pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; + if (pDevice->playback.intermediaryBufferCap == 0) { + pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; + } + } + + intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + + pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); + if (pDevice->playback.pIntermediaryBuffer == NULL) { + ma_device_uninit(pDevice); + return MA_OUT_OF_MEMORY; + } + + /* Silence the buffer for safety. */ + ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); + pDevice->playback.intermediaryBufferLen = 0; + } + } else { + /* Not using a fixed sized data callback so no need for an intermediary buffer. */ + } + + + /* Some backends don't require the worker thread. */ + if (!ma_context_is_backend_asynchronous(pContext)) { + /* The worker thread. */ + result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + + /* Wait for the worker thread to put the device into it's stopped state for real. */ + ma_event_wait(&pDevice->stopEvent); + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + } else { + /* + If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done + after ma_device__post_init_setup(). + */ + if (ma_context_is_backend_asynchronous(pContext)) { + if (pConfig->deviceType == ma_device_type_duplex) { + result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); + if (result != MA_SUCCESS) { + ma_device_uninit(pDevice); + return result; + } + } + } + + ma_device__set_state(pDevice, ma_device_state_stopped); + } + + /* Log device information. */ + { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); + + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); + { + char channelMapStr[1024]; + ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); + + ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); + } + } + } + + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + return MA_SUCCESS; +} + +MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) +{ + ma_result result; + ma_context* pContext; + ma_backend defaultBackends[ma_backend_null+1]; + ma_uint32 iBackend; + ma_backend* pBackendsToIterate; + ma_uint32 backendsToIterateCount; + ma_allocation_callbacks allocationCallbacks; + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pContextConfig != NULL) { + result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + } else { + allocationCallbacks = ma_allocation_callbacks_init_default(); + } + + pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); + if (pContext == NULL) { + return MA_OUT_OF_MEMORY; + } + + for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { + defaultBackends[iBackend] = (ma_backend)iBackend; + } + + pBackendsToIterate = (ma_backend*)backends; + backendsToIterateCount = backendCount; + if (pBackendsToIterate == NULL) { + pBackendsToIterate = (ma_backend*)defaultBackends; + backendsToIterateCount = ma_countof(defaultBackends); + } + + result = MA_NO_BACKEND; + + for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { + /* + This is a hack for iOS. If the context config is null, there's a good chance the + `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this + case, set the session category based on the device type. + */ + #if defined(MA_APPLE_MOBILE) + ma_context_config contextConfig; + + if (pContextConfig == NULL) { + contextConfig = ma_context_config_init(); + switch (pConfig->deviceType) { + case ma_device_type_duplex: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; + } break; + case ma_device_type_capture: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; + } break; + case ma_device_type_playback: + default: { + contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; + } break; + } + + pContextConfig = &contextConfig; + } + #endif + + result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); + if (result == MA_SUCCESS) { + result = ma_device_init(pContext, pConfig, pDevice); + if (result == MA_SUCCESS) { + break; /* Success. */ + } else { + ma_context_uninit(pContext); /* Failure. */ + } + } + } + + if (result != MA_SUCCESS) { + ma_free(pContext, &allocationCallbacks); + return result; + } + + pDevice->isOwnerOfContext = MA_TRUE; + return result; +} + +MA_API void ma_device_uninit(ma_device* pDevice) +{ + if (!ma_device__is_initialized(pDevice)) { + return; + } + + /* + It's possible for the miniaudio side of the device and the backend to not be in sync due to + system-level situations such as the computer being put into sleep mode and the backend not + notifying miniaudio of the fact the device has stopped. It's possible for this to result in a + deadlock due to miniaudio thinking the device is in a running state, when in fact it's not + running at all. For this reason I am no longer explicitly stopping the device. I don't think + this should affect anyone in practice since uninitializing the backend will naturally stop the + device anyway. + */ + #if 0 + { + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); + } + } + #endif + + /* Putting the device into an uninitialized state will make the worker thread return. */ + ma_device__set_state(pDevice, ma_device_state_uninitialized); + + /* Wake up the worker thread and wait for it to properly terminate. */ + if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { + ma_event_signal(&pDevice->wakeupEvent); + ma_thread_wait(&pDevice->thread); + } + + if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { + pDevice->pContext->callbacks.onDeviceUninit(pDevice); + } + + + ma_event_uninit(&pDevice->stopEvent); + ma_event_uninit(&pDevice->startEvent); + ma_event_uninit(&pDevice->wakeupEvent); + ma_mutex_uninit(&pDevice->startStopLock); + + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + if (pDevice->type == ma_device_type_duplex) { + ma_duplex_rb_uninit(&pDevice->duplexRB); + } + } + + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { + ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { + ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->playback.pInputCache != NULL) { + ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->capture.pIntermediaryBuffer != NULL) { + ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + if (pDevice->playback.pIntermediaryBuffer != NULL) { + ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); + } + + if (pDevice->isOwnerOfContext) { + ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; + + ma_context_uninit(pDevice->pContext); + ma_free(pDevice->pContext, &allocationCallbacks); + } + + MA_ZERO_OBJECT(pDevice); +} + +MA_API ma_context* ma_device_get_context(ma_device* pDevice) +{ + if (pDevice == NULL) { + return NULL; + } + + return pDevice->pContext; +} + +MA_API ma_log* ma_device_get_log(ma_device* pDevice) +{ + return ma_context_get_log(ma_device_get_context(pDevice)); +} + +MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) +{ + if (pDeviceInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDeviceInfo); + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ + if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { + return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); + } + + /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ + if (type == ma_device_type_playback) { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); + } else { + return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); + } +} + +MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) +{ + ma_result result; + ma_device_info deviceInfo; + + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = 0; + } + + if (pName != NULL && nameCap > 0) { + pName[0] = '\0'; + } + + result = ma_device_get_info(pDevice, type, &deviceInfo); + if (result != MA_SUCCESS) { + return result; + } + + if (pName != NULL) { + ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); + + /* + For safety, make sure the length is based on the truncated output string rather than the + source. Otherwise the caller might assume the output buffer contains more content than it + actually does. + */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(pName); + } + } else { + /* Name not specified. Just report the length of the source string. */ + if (pLengthNotIncludingNullTerminator != NULL) { + *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_start(ma_device* pDevice) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ + } + + if (ma_device_get_state(pDevice) == ma_device_state_started) { + return MA_SUCCESS; /* Already started. */ + } + + ma_mutex_lock(&pDevice->startStopLock); + { + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); + + ma_device__set_state(pDevice, ma_device_state_starting); + + /* Asynchronous backends need to be handled differently. */ + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + if (pDevice->pContext->callbacks.onDeviceStart != NULL) { + result = pDevice->pContext->callbacks.onDeviceStart(pDevice); + } else { + result = MA_INVALID_OPERATION; + } + + if (result == MA_SUCCESS) { + ma_device__set_state(pDevice, ma_device_state_started); + ma_device__on_notification_started(pDevice); + } + } else { + /* + Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the + thread and then wait for the start event. + */ + ma_event_signal(&pDevice->wakeupEvent); + + /* + Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device + into the started state. Don't call ma_device__set_state() here. + */ + ma_event_wait(&pDevice->startEvent); + result = pDevice->workResult; + } + + /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ + if (result != MA_SUCCESS) { + ma_device__set_state(pDevice, ma_device_state_stopped); + } + } + ma_mutex_unlock(&pDevice->startStopLock); + + return result; +} + +MA_API ma_result ma_device_stop(ma_device* pDevice) +{ + ma_result result; + + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + return MA_INVALID_OPERATION; /* Not initialized. */ + } + + if (ma_device_get_state(pDevice) == ma_device_state_stopped) { + return MA_SUCCESS; /* Already stopped. */ + } + + ma_mutex_lock(&pDevice->startStopLock); + { + /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ + MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); + + ma_device__set_state(pDevice, ma_device_state_stopping); + + /* Asynchronous backends need to be handled differently. */ + if (ma_context_is_backend_asynchronous(pDevice->pContext)) { + /* Asynchronous backends must have a stop operation. */ + if (pDevice->pContext->callbacks.onDeviceStop != NULL) { + result = pDevice->pContext->callbacks.onDeviceStop(pDevice); + } else { + result = MA_INVALID_OPERATION; + } + + ma_device__set_state(pDevice, ma_device_state_stopped); + } else { + /* + Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If + the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make + sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super + important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. + */ + MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); + + if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { + pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); + } + + /* + We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be + the one who puts the device into the stopped state. Don't call ma_device__set_state() here. + */ + ma_event_wait(&pDevice->stopEvent); + result = MA_SUCCESS; + } + + /* + This is a safety measure to ensure the internal buffer has been cleared so any leftover + does not get played the next time the device starts. Ideally this should be drained by + the backend first. + */ + pDevice->playback.intermediaryBufferLen = 0; + pDevice->playback.inputCacheConsumed = 0; + pDevice->playback.inputCacheRemaining = 0; + } + ma_mutex_unlock(&pDevice->startStopLock); + + return result; +} + +MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) +{ + return ma_device_get_state(pDevice) == ma_device_state_started; +} + +MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) +{ + if (pDevice == NULL) { + return ma_device_state_uninitialized; + } + + return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ +} + +MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) +{ + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (volume < 0.0f) { + return MA_INVALID_ARGS; + } + + ma_atomic_float_set(&pDevice->masterVolumeFactor, volume); + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) +{ + if (pVolume == NULL) { + return MA_INVALID_ARGS; + } + + if (pDevice == NULL) { + *pVolume = 0; + return MA_INVALID_ARGS; + } + + *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor); + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) +{ + if (gainDB > 0) { + return MA_INVALID_ARGS; + } + + return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); +} + +MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) +{ + float factor; + ma_result result; + + if (pGainDB == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_device_get_master_volume(pDevice, &factor); + if (result != MA_SUCCESS) { + *pGainDB = 0; + return result; + } + + *pGainDB = ma_volume_linear_to_db(factor); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + if (pDevice == NULL) { + return MA_INVALID_ARGS; + } + + if (pOutput == NULL && pInput == NULL) { + return MA_INVALID_ARGS; + } + + if (pDevice->type == ma_device_type_duplex) { + if (pInput != NULL) { + ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); + } + + if (pOutput != NULL) { + ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); + } + } else { + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { + if (pInput == NULL) { + return MA_INVALID_ARGS; + } + + ma_device__send_frames_to_client(pDevice, frameCount, pInput); + } + + if (pDevice->type == ma_device_type_playback) { + if (pOutput == NULL) { + return MA_INVALID_ARGS; + } + + ma_device__read_frames_from_client(pDevice, frameCount, pOutput); + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) +{ + if (pDescriptor == NULL) { + return 0; + } + + /* + We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the + time when the size of the buffer needs to be determined. In this case we need to just take a best + guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll + just fall back to MA_DEFAULT_SAMPLE_RATE. + */ + if (nativeSampleRate == 0) { + nativeSampleRate = pDescriptor->sampleRate; + } + if (nativeSampleRate == 0) { + nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; + } + + MA_ASSERT(nativeSampleRate != 0); + + if (pDescriptor->periodSizeInFrames == 0) { + if (pDescriptor->periodSizeInMilliseconds == 0) { + if (performanceProfile == ma_performance_profile_low_latency) { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); + } + } else { + return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); + } + } else { + return pDescriptor->periodSizeInFrames; + } +} +#endif /* MA_NO_DEVICE_IO */ + + +MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) +{ + /* Prevent a division by zero. */ + if (sampleRate == 0) { + return 0; + } + + return bufferSizeInFrames*1000 / sampleRate; +} + +MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) +{ + /* Prevent a division by zero. */ + if (sampleRate == 0) { + return 0; + } + + return bufferSizeInMilliseconds*sampleRate / 1000; +} + +MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + if (dst == src) { + return; /* No-op. */ + } + + ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); +} + +MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + if (format == ma_format_u8) { + ma_uint64 sampleCount = frameCount * channels; + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ((ma_uint8*)p)[iSample] = 128; + } + } else { + ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); + } +} + +MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) +{ + return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); +} + +MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) +{ + return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); +} + + +MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(pSrc[iSample]); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(pSrc[iSample]); + } +} + +MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(pSrc[iSample]); + } +} + +MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) +{ + ma_uint64 sampleCount; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; + case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; + case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; + case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; + } +} + + +MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + ma_uint8* pSamplesOut8; + ma_uint8* pSamplesIn8; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + pSamplesOut8 = (ma_uint8*)pSamplesOut; + pSamplesIn8 = (ma_uint8*)pSamplesIn; + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ma_int32 sampleS32; + + sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); + sampleS32 = (ma_int32)(sampleS32 * factor); + + pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); + pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); + pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); + } +} + +MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); + } +} + +MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) +{ + ma_uint64 iSample; + + if (pSamplesOut == NULL || pSamplesIn == NULL) { + return; + } + + if (factor == 1) { + if (pSamplesOut == pSamplesIn) { + /* In place. No-op. */ + } else { + /* Just a copy. */ + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample]; + } + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamplesOut[iSample] = pSamplesIn[iSample] * factor; + } + } +} + +MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) +{ + ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); +} + +MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +{ + switch (format) + { + case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; + case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; + case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; + case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; + default: return; /* Do nothing. */ + } +} + +MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); +} + +MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) +{ + ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); +} + + +MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) +{ + ma_uint64 iFrame; + + if (channels == 2) { + /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; + } + } +} + + + +static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) +{ + return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); +} + +static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) +{ + return (ma_int32)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) +{ + return (ma_int64)((x * volume) >> 8); +} + +static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) +{ + return x * volume; +} + + +MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); + pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); + pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); + pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + ma_int16 volumeFixed; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + volumeFixed = ma_float_to_fixed_16(volume); + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) +{ + ma_uint64 iSample; + + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ + + for (iSample = 0; iSample < count; iSample += 1) { + pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); + } +} + +MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) +{ + MA_ASSERT(pDst != NULL); + MA_ASSERT(pSrc != NULL); + + if (volume == 1) { + ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ + } else if (volume == 0) { + ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ + } else { + ma_uint64 sampleCount = frameCount * channels; + + switch (format) { + case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; + case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; + case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; + case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; + + /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ + case ma_format_unknown: + case ma_format_count: + break; + } + } +} + + + +MA_API float ma_volume_linear_to_db(float factor) +{ + return 20*ma_log10f(factor); +} + +MA_API float ma_volume_db_to_linear(float gain) +{ + return ma_powf(10, gain/20.0f); +} + + +MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) +{ + ma_uint64 iSample; + ma_uint64 sampleCount; + + if (pDst == NULL || pSrc == NULL || channels == 0) { + return MA_INVALID_ARGS; + } + + if (volume == 0) { + return MA_SUCCESS; /* No changes if the volume is 0. */ + } + + sampleCount = frameCount * channels; + + if (volume == 1) { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += pSrc[iSample]; + } + } else { + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); + } + } + + return MA_SUCCESS; +} + + + +/************************************************************************************************************************************************************** + +Format Conversion + +**************************************************************************************************************************************************************/ + +static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) +{ + return (ma_int16)(x * 32767.0f); +} + +static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) +{ + return (ma_int16)((ma_int16)x - 128); +} + +static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) +{ + return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ +} + +static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) +{ + s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); + s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); + s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); +} + + +/* u8 */ +MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); +} + + +static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_u8[i]; + x = (ma_int16)(x - 128); + x = (ma_int16)(x << 8); + dst_s16[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_u8[i]; + x = (ma_int16)(x - 128); + + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = 0; + dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_u8[i]; + x = x - 128; + x = x << 24; + dst_s32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_u8[i]; + x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +#ifdef MA_USE_REFERENCE_CONVERSION_APIS +static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8** src_u8 = (const ma_uint8**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } +} +#else +static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8** src_u8 = (const ma_uint8**)src; + + if (channels == 1) { + ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); + } else if (channels == 2) { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; + dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; + } + } else { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } + } +} +#endif + +MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8** dst_u8 = (ma_uint8**)dst; + const ma_uint8* src_u8 = (const ma_uint8*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +/* s16 */ +static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_s16[i]; + x = (ma_int16)(x >> 8); + x = (ma_int16)(x + 128); + dst_u8[i] = (ma_uint8)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int16 x = src_s16[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); + if ((x + dither) <= 0x7FFF) { + x = (ma_int16)(x + dither); + } else { + x = 0x7FFF; + } + + x = (ma_int16)(x >> 8); + x = (ma_int16)(x + 128); + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); +} + + +static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); + dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = src_s16[i] << 16; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_s16[i]; + +#if 0 + /* The accurate way. */ + x = x + 32768.0f; /* -32768..32767 to 0..65535 */ + x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ +#else + /* The fast way. */ + x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ +#endif + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_int16** src_s16 = (const ma_int16**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int16** dst_s16 = (ma_int16**)dst; + const ma_int16* src_s16 = (const ma_int16*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +/* s24 */ +static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); + ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); + dst_s16[i] = (ma_int16)(dst_lo | dst_hi); + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } +} + +static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * 3); +} + + +static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_uint8* src_s24 = (const ma_uint8*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); + +#if 0 + /* The accurate way. */ + x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ + x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ + x = x - 1; /* 0..2 to -1..1 */ +#else + /* The fast way. */ + x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ +#endif + + dst_f32[i] = x; + } + + (void)ditherMode; +} + +static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8* dst8 = (ma_uint8*)dst; + const ma_uint8** src8 = (const ma_uint8**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; + dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; + dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_uint8** dst8 = (ma_uint8**)dst; + const ma_uint8* src8 = (const ma_uint8*)src; + + ma_uint32 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; + dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; + dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + + +/* s32 */ +static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_u8 = (ma_uint8*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 24; + x = x + 128; + dst_u8[i] = (ma_uint8)x; + } + } +} + +static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int16* dst_s16 = (ma_int16*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + if (ditherMode == ma_dither_mode_none) { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } else { + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 x = src_s32[i]; + + /* Dither. Don't overflow. */ + ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); + if ((ma_int64)x + dither <= 0x7FFFFFFF) { + x = x + dither; + } else { + x = 0x7FFFFFFF; + } + + x = x >> 16; + dst_s16[i] = (ma_int16)x; + } + } +} + +static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_uint32 x = (ma_uint32)src_s32[i]; + dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); + dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); + dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); + } + + (void)ditherMode; /* No dithering for s32 -> s24. */ +} + +static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); +} + + +static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + float* dst_f32 = (float*)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + double x = src_s32[i]; + +#if 0 + x = x + 2147483648.0; + x = x * 0.0000000004656612873077392578125; + x = x - 1; +#else + x = x / 2147483648.0; +#endif + + dst_f32[i] = (float)x; + } + + (void)ditherMode; /* No dithering for s32 -> f32. */ +} + +static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const ma_int32** src_s32 = (const ma_int32**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; + } + } +} + +static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_int32** dst_s32 = (ma_int32**)dst; + const ma_int32* src_s32 = (const ma_int32*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; + } + } +} + +static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +/* f32 */ +static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + + ma_uint8* dst_u8 = (ma_uint8*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -128; + ditherMax = 1.0f / 127; + } + + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 127.5f; /* 0..2 to 0..255 */ + + dst_u8[i] = (ma_uint8)x; + } +} + +static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); + } +#endif +} + +#ifdef MA_USE_REFERENCE_CONVERSION_APIS +static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + + ma_int16* dst_s16 = (ma_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 32767.5f; /* 0..2 to 0..65535 */ + x = x - 32768.0f; /* 0...65535 to -32768..32767 */ +#else + /* The fast way. */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ +#endif + + dst_s16[i] = (ma_int16)x; + } +} +#else +static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i4; + ma_uint64 count4; + + ma_int16* dst_s16 = (ma_int16*)dst; + const float* src_f32 = (const float*)src; + + float ditherMin = 0; + float ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + /* Unrolled. */ + i = 0; + count4 = count >> 2; + for (i4 = 0; i4 < count4; i4 += 1) { + float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); + + float x0 = src_f32[i+0]; + float x1 = src_f32[i+1]; + float x2 = src_f32[i+2]; + float x3 = src_f32[i+3]; + + x0 = x0 + d0; + x1 = x1 + d1; + x2 = x2 + d2; + x3 = x3 + d3; + + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + + dst_s16[i+0] = (ma_int16)x0; + dst_s16[i+1] = (ma_int16)x1; + dst_s16[i+2] = (ma_int16)x2; + dst_s16[i+3] = (ma_int16)x3; + + i += 4; + } + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i8; + ma_uint64 count8; + ma_int16* dst_s16; + const float* src_f32; + float ditherMin; + float ditherMax; + + /* Both the input and output buffers need to be aligned to 16 bytes. */ + if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + dst_s16 = (ma_int16*)dst; + src_f32 = (const float*)src; + + ditherMin = 0; + ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + i = 0; + + /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ + count8 = count >> 3; + for (i8 = 0; i8 < count8; i8 += 1) { + __m128 d0; + __m128 d1; + __m128 x0; + __m128 x1; + + if (ditherMode == ma_dither_mode_none) { + d0 = _mm_set1_ps(0); + d1 = _mm_set1_ps(0); + } else if (ditherMode == ma_dither_mode_rectangle) { + d0 = _mm_set_ps( + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax), + ma_dither_f32_rectangle(ditherMin, ditherMax) + ); + } else { + d0 = _mm_set_ps( + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax) + ); + d1 = _mm_set_ps( + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax), + ma_dither_f32_triangle(ditherMin, ditherMax) + ); + } + + x0 = *((__m128*)(src_f32 + i) + 0); + x1 = *((__m128*)(src_f32 + i) + 1); + + x0 = _mm_add_ps(x0, d0); + x1 = _mm_add_ps(x1, d1); + + x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); + x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); + + _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); + + i += 8; + } + + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} +#endif /* SSE2 */ + +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint64 i; + ma_uint64 i8; + ma_uint64 count8; + ma_int16* dst_s16; + const float* src_f32; + float ditherMin; + float ditherMax; + + if (!ma_has_neon()) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + /* Both the input and output buffers need to be aligned to 16 bytes. */ + if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + return; + } + + dst_s16 = (ma_int16*)dst; + src_f32 = (const float*)src; + + ditherMin = 0; + ditherMax = 0; + if (ditherMode != ma_dither_mode_none) { + ditherMin = 1.0f / -32768; + ditherMax = 1.0f / 32767; + } + + i = 0; + + /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ + count8 = count >> 3; + for (i8 = 0; i8 < count8; i8 += 1) { + float32x4_t d0; + float32x4_t d1; + float32x4_t x0; + float32x4_t x1; + int32x4_t i0; + int32x4_t i1; + + if (ditherMode == ma_dither_mode_none) { + d0 = vmovq_n_f32(0); + d1 = vmovq_n_f32(0); + } else if (ditherMode == ma_dither_mode_rectangle) { + float d0v[4]; + float d1v[4]; + + d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } else { + float d0v[4]; + float d1v[4]; + + d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); + d0 = vld1q_f32(d0v); + + d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); + d1 = vld1q_f32(d1v); + } + + x0 = *((float32x4_t*)(src_f32 + i) + 0); + x1 = *((float32x4_t*)(src_f32 + i) + 1); + + x0 = vaddq_f32(x0, d0); + x1 = vaddq_f32(x1, d1); + + x0 = vmulq_n_f32(x0, 32767.0f); + x1 = vmulq_n_f32(x1, 32767.0f); + + i0 = vcvtq_s32_f32(x0); + i1 = vcvtq_s32_f32(x1); + *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); + + i += 8; + } + + + /* Leftover. */ + for (; i < count; i += 1) { + float x = src_f32[i]; + x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + x = x * 32767.0f; /* -1..1 to -32767..32767 */ + + dst_s16[i] = (ma_int16)x; + } +} +#endif /* Neon */ +#endif /* MA_USE_REFERENCE_CONVERSION_APIS */ + +MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_uint8* dst_s24 = (ma_uint8*)dst; + const float* src_f32 = (const float*)src; + + ma_uint64 i; + for (i = 0; i < count; i += 1) { + ma_int32 r; + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 8388607.5f; /* 0..2 to 0..16777215 */ + x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ +#else + /* The fast way. */ + x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ +#endif + + r = (ma_int32)x; + dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); + dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); + dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); + } + + (void)ditherMode; /* No dithering for f32 -> s24. */ +} + +static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); + } +#endif +} + + +static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_int32* dst_s32 = (ma_int32*)dst; + const float* src_f32 = (const float*)src; + + ma_uint32 i; + for (i = 0; i < count; i += 1) { + double x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ + +#if 0 + /* The accurate way. */ + x = x + 1; /* -1..1 to 0..2 */ + x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ + x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ +#else + /* The fast way. */ + x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ +#endif + + dst_s32[i] = (ma_int32)x; + } + + (void)ditherMode; /* No dithering for f32 -> s32. */ +} + +static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +} + +#if defined(MA_SUPPORT_SSE2) +static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif +#if defined(MA_SUPPORT_NEON) +static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +#else + # if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); + } else + #elif defined(MA_SUPPORT_NEON) + if (ma_has_neon()) { + ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); + } else + #endif + { + ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); + } +#endif +} + + +MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) +{ + (void)ditherMode; + + ma_copy_memory_64(dst, src, count * sizeof(float)); +} + + +static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + float* dst_f32 = (float*)dst; + const float** src_f32 = (const float**)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; + } + } +} + +static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); +#else + ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + float** dst_f32 = (float**)dst; + const float* src_f32 = (const float*)src; + + ma_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; + } + } +} + +static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ + ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +} + +MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) +{ +#ifdef MA_USE_REFERENCE_CONVERSION_APIS + ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +#else + ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) +{ + if (formatOut == formatIn) { + ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); + return; + } + + switch (formatIn) + { + case ma_format_u8: + { + switch (formatOut) + { + case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s16: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s24: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_s32: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + case ma_format_f32: + { + switch (formatOut) + { + case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; + default: break; + } + } break; + + default: break; + } +} + +MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) +{ + ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); +} + +MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) +{ + if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { + return; /* Invalid args. */ + } + + /* For efficiency we do this per format. */ + switch (format) { + case ma_format_s16: + { + const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; + pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; + } + } + } break; + + case ma_format_f32: + { + const float* pSrcF32 = (const float*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; + pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; + } + } + } break; + + default: + { + ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); + const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); + memcpy(pDst, pSrc, sampleSizeInBytes); + } + } + } break; + } +} + +MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) +{ + switch (format) + { + case ma_format_s16: + { + ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; + pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; + } + } + } break; + + case ma_format_f32: + { + float* pDstF32 = (float*)pInterleavedPCMFrames; + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; + pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; + } + } + } break; + + default: + { + ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); + ma_uint64 iPCMFrame; + for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); + const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); + memcpy(pDst, pSrc, sampleSizeInBytes); + } + } + } break; + } +} + + +/************************************************************************************************************************************************************** + +Biquad Filter + +**************************************************************************************************************************************************************/ +#ifndef MA_BIQUAD_FIXED_POINT_SHIFT +#define MA_BIQUAD_FIXED_POINT_SHIFT 14 +#endif + +static ma_int32 ma_biquad_float_to_fp(double x) +{ + return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); +} + +MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) +{ + ma_biquad_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.b0 = b0; + config.b1 = b1; + config.b2 = b2; + config.a0 = a0; + config.a1 = a1; + config.a2 = a2; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; + size_t r2Offset; +} ma_biquad_heap_layout; + +static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R0 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* R1 */ + pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) +{ + ma_result result; + ma_biquad_heap_layout heapLayout; + + if (pBQ == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBQ); + + result = ma_biquad_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pBQ->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); + + return ma_biquad_reinit(pConfig, pBQ); +} + +MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBQ->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBQ == NULL) { + return; + } + + if (pBQ->_ownsHeap) { + ma_free(pBQ->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) +{ + if (pBQ == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->a0 == 0) { + return MA_INVALID_ARGS; /* Division by zero. */ + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + + pBQ->format = pConfig->format; + pBQ->channels = pConfig->channels; + + /* Normalize. */ + if (pConfig->format == ma_format_f32) { + pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); + pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); + pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); + pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); + pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); + } else { + pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); + pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); + pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); + pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); + pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) +{ + if (pBQ == NULL) { + return MA_INVALID_ARGS; + } + + if (pBQ->format == ma_format_f32) { + pBQ->pR1->f32 = 0; + pBQ->pR2->f32 = 0; + } else { + pBQ->pR1->s32 = 0; + pBQ->pR2->s32 = 0; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pBQ->channels; + const float b0 = pBQ->b0.f32; + const float b1 = pBQ->b1.f32; + const float b2 = pBQ->b2.f32; + const float a1 = pBQ->a1.f32; + const float a2 = pBQ->a2.f32; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pBQ->pR1[c].f32; + float r2 = pBQ->pR2[c].f32; + float x = pX[c]; + float y; + + y = b0*x + r1; + r1 = b1*x - a1*y + r2; + r2 = b2*x - a2*y; + + pY[c] = y; + pBQ->pR1[c].f32 = r1; + pBQ->pR2[c].f32 = r2; + } +} + +static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) +{ + ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); +} + +static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pBQ->channels; + const ma_int32 b0 = pBQ->b0.s32; + const ma_int32 b1 = pBQ->b1.s32; + const ma_int32 b2 = pBQ->b2.s32; + const ma_int32 a1 = pBQ->a1.s32; + const ma_int32 a2 = pBQ->a2.s32; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pBQ->pR1[c].s32; + ma_int32 r2 = pBQ->pR2[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + r1 = (b1*x - a1*y + r2); + r2 = (b2*x - a2*y); + + pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); + pBQ->pR1[c].s32 = r1; + pBQ->pR2[c].s32 = r2; + } +} + +static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) +{ + ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); +} + +MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pBQ->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); + pY += pBQ->channels; + pX += pBQ->channels; + } + } else if (pBQ->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); + pY += pBQ->channels; + pX += pBQ->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) +{ + if (pBQ == NULL) { + return 0; + } + + return 2; +} + + +/************************************************************************************************************************************************************** + +Low-Pass Filter + +**************************************************************************************************************************************************************/ +MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) +{ + ma_lpf1_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = 0.5; + + return config; +} + +MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_lpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; +} ma_lpf1_heap_layout; + +static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) +{ + ma_result result; + ma_lpf1_heap_layout heapLayout; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + + return ma_lpf1_reinit(pConfig, pLPF); +} + +MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) +{ + double a; + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + pLPF->format = pConfig->format; + pLPF->channels = pConfig->channels; + + a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); + if (pConfig->format == ma_format_f32) { + pLPF->a.f32 = (float)a; + } else { + pLPF->a.s32 = ma_biquad_float_to_fp(a); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + if (pLPF->format == ma_format_f32) { + pLPF->a.f32 = 0; + } else { + pLPF->a.s32 = 0; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pLPF->channels; + const float a = pLPF->a.f32; + const float b = 1 - a; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pLPF->pR1[c].f32; + float x = pX[c]; + float y; + + y = b*x + a*r1; + + pY[c] = y; + pLPF->pR1[c].f32 = y; + } +} + +static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pLPF->channels; + const ma_int32 a = pLPF->a.s32; + const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pLPF->pR1[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + + pY[c] = (ma_int16)y; + pLPF->pR1[c].s32 = (ma_int32)y; + } +} + +MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pLPF->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); + pY += pLPF->channels; + pX += pLPF->channels; + } + } else if (pLPF->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); + pY += pLPF->channels; + pX += pLPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return 1; +} + + +static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = (1 - c) / 2; + bqConfig.b1 = 1 - c; + bqConfig.b2 = (1 - c) / 2; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_lpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_lpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pLPF == NULL) { + return; + } + + ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_lpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pLPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + ma_biquad_clear_cache(&pLPF->bq); + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pLPF->bq); +} + + +MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_lpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t lpf1Offset; + size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_lpf_heap_layout; + +static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) +{ + MA_ASSERT(pLPF1Count != NULL); + MA_ASSERT(pLPF2Count != NULL); + + *pLPF1Count = order % 2; + *pLPF2Count = order / 2; +} + +static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* LPF 1 */ + pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { + size_t lpf1HeapSizeInBytes; + ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; + } + + /* LPF 2*/ + pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; + for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { + size_t lpf2HeapSizeInBytes; + ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 lpf1Count; + ma_uint32 lpf2Count; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pLPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); + pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + + for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { + ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + if (isNew) { + size_t lpf1HeapSizeInBytes; + + result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); + } + } else { + result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + + for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { + ma_lpf2_config lpf2Config; + double q; + double a; + + /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ + if (lpf1Count == 1) { + a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ + } else { + a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ + } + q = 1 / (2*ma_cosd(a)); + + lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t lpf2HeapSizeInBytes; + + result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); + } + } else { + result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jlpf1; + ma_uint32 jlpf2; + + for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + pLPF->lpf1Count = lpf1Count; + pLPF->lpf2Count = lpf2Count; + pLPF->format = pConfig->format; + pLPF->channels = pConfig->channels; + pLPF->sampleRate = pConfig->sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_lpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_lpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); + } + + if (pLPF->_ownsHeap) { + ma_free(pLPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) +{ + return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pLPF->format == ma_format_f32); + + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); + } +} + +static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + MA_ASSERT(pLPF->format == ma_format_s16); + + MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); + + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); + } +} + +MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ilpf1; + ma_uint32 ilpf2; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { + result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + + for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { + result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pLPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); + pFramesOutF32 += pLPF->channels; + pFramesInF32 += pLPF->channels; + } + } else if (pLPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); + pFramesOutS16 += pLPF->channels; + pFramesInS16 += pLPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) +{ + if (pLPF == NULL) { + return 0; + } + + return pLPF->lpf2Count*2 + pLPF->lpf1Count; +} + + +/************************************************************************************************************************************************************** + +High-Pass Filtering + +**************************************************************************************************************************************************************/ +MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) +{ + ma_hpf1_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + + return config; +} + +MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_hpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t r1Offset; +} ma_hpf1_heap_layout; + +static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* R1 */ + pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) +{ + ma_result result; + ma_hpf1_heap_layout heapLayout; + + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pLPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); + + return ma_hpf1_reinit(pConfig, pLPF); +} + +MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pLPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) +{ + double a; + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + pHPF->format = pConfig->format; + pHPF->channels = pConfig->channels; + + a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); + if (pConfig->format == ma_format_f32) { + pHPF->a.f32 = (float)a; + } else { + pHPF->a.s32 = ma_biquad_float_to_fp(a); + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pHPF->channels; + const float a = 1 - pHPF->a.f32; + const float b = 1 - a; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float r1 = pHPF->pR1[c].f32; + float x = pX[c]; + float y; + + y = b*x - a*r1; + + pY[c] = y; + pHPF->pR1[c].f32 = y; + } +} + +static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) +{ + ma_uint32 c; + const ma_uint32 channels = pHPF->channels; + const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); + const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int32 r1 = pHPF->pR1[c].s32; + ma_int32 x = pX[c]; + ma_int32 y; + + y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; + + pY[c] = (ma_int16)y; + pHPF->pR1[c].s32 = (ma_int32)y; + } +} + +MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 n; + + if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ + + if (pHPF->format == ma_format_f32) { + /* */ float* pY = ( float*)pFramesOut; + const float* pX = (const float*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); + pY += pHPF->channels; + pX += pHPF->channels; + } + } else if (pHPF->format == ma_format_s16) { + /* */ ma_int16* pY = ( ma_int16*)pFramesOut; + const ma_int16* pX = (const ma_int16*)pFramesIn; + + for (n = 0; n < frameCount; n += 1) { + ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); + pY += pHPF->channels; + pX += pHPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return 1; +} + + +static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = (1 + c) / 2; + bqConfig.b1 = -(1 + c); + bqConfig.b2 = (1 + c) / 2; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pHPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pHPF == NULL) { + return; + } + + ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pHPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pHPF->bq); +} + + +MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_hpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t hpf1Offset; + size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ +} ma_hpf_heap_layout; + +static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) +{ + MA_ASSERT(pHPF1Count != NULL); + MA_ASSERT(pHPF2Count != NULL); + + *pHPF1Count = order % 2; + *pHPF2Count = order / 2; +} + +static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); + + pHeapLayout->sizeInBytes = 0; + + /* HPF 1 */ + pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { + size_t hpf1HeapSizeInBytes; + ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; + } + + /* HPF 2*/ + pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; + for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { + size_t hpf2HeapSizeInBytes; + ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 hpf1Count; + ma_uint32 hpf2Count; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pHPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pHPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); + pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ + } + + for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { + ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); + + if (isNew) { + size_t hpf1HeapSizeInBytes; + + result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); + } + } else { + result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + + for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { + ma_hpf2_config hpf2Config; + double q; + double a; + + /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ + if (hpf1Count == 1) { + a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ + } else { + a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ + } + q = 1 / (2*ma_cosd(a)); + + hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t hpf2HeapSizeInBytes; + + result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); + } + } else { + result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); + } + + if (result != MA_SUCCESS) { + ma_uint32 jhpf1; + ma_uint32 jhpf2; + + for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ + } + + return result; + } + } + + pHPF->hpf1Count = hpf1Count; + pHPF->hpf2Count = hpf2Count; + pHPF->format = pConfig->format; + pHPF->channels = pConfig->channels; + pHPF->sampleRate = pConfig->sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_hpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_hpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return result; +} + +MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) +{ + if (pLPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pLPF); + + return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pHPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + if (pHPF == NULL) { + return; + } + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); + } + + if (pHPF->_ownsHeap) { + ma_free(pHPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) +{ + return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ihpf1; + ma_uint32 ihpf2; + + if (pHPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pHPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pHPF->channels; + pFramesInF32 += pHPF->channels; + } + } else if (pHPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); + + for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { + ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); + } + + for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { + ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pHPF->channels; + pFramesInS16 += pHPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) +{ + if (pHPF == NULL) { + return 0; + } + + return pHPF->hpf2Count*2 + pHPF->hpf1Count; +} + + +/************************************************************************************************************************************************************** + +Band-Pass Filtering + +**************************************************************************************************************************************************************/ +MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) +{ + ma_bpf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.q = q; + + /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = q * a; + bqConfig.b1 = 0; + bqConfig.b2 = -q * a; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_bpf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBPF); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_bpf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pBPF == NULL) { + return; + } + + ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pBPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_bpf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pBPF->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) +{ + if (pBPF == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pBPF->bq); +} + + +MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_bpf_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.cutoffFrequency = cutoffFrequency; + config.order = ma_min(order, MA_MAX_FILTER_ORDER); + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t bpf2Offset; +} ma_bpf_heap_layout; + +static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 bpf2Count; + ma_uint32 ibpf2; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + /* We must have an even number of order. */ + if ((pConfig->order & 0x1) != 0) { + return MA_INVALID_ARGS; + } + + bpf2Count = pConfig->channels / 2; + + pHeapLayout->sizeInBytes = 0; + + /* BPF 2 */ + pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { + size_t bpf2HeapSizeInBytes; + ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) +{ + ma_result result; + ma_uint32 bpf2Count; + ma_uint32 ibpf2; + ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ + + if (pBPF == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only supporting f32 and s16. */ + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + /* The format cannot be changed after initialization. */ + if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { + return MA_INVALID_OPERATION; + } + + /* The channel count cannot be changed after initialization. */ + if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { + return MA_INVALID_OPERATION; + } + + if (pConfig->order > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + /* We must have an even number of order. */ + if ((pConfig->order & 0x1) != 0) { + return MA_INVALID_ARGS; + } + + bpf2Count = pConfig->order / 2; + + /* The filter order can't change between reinits. */ + if (!isNew) { + if (pBPF->bpf2Count != bpf2Count) { + return MA_INVALID_OPERATION; + } + } + + if (isNew) { + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pBPF->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); + } else { + MA_ZERO_OBJECT(&heapLayout); + } + + for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { + ma_bpf2_config bpf2Config; + double q; + + /* TODO: Calculate Q to make this a proper Butterworth filter. */ + q = 0.707107; + + bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); + + if (isNew) { + size_t bpf2HeapSizeInBytes; + + result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); + if (result == MA_SUCCESS) { + result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); + } + } else { + result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); + } + + if (result != MA_SUCCESS) { + return result; + } + } + + pBPF->bpf2Count = bpf2Count; + pBPF->format = pConfig->format; + pBPF->channels = pConfig->channels; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_bpf_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_bpf_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) +{ + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pBPF); + + return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); +} + +MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pBPF->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint32 ibpf2; + + if (pBPF == NULL) { + return; + } + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); + } + + if (pBPF->_ownsHeap) { + ma_free(pBPF->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) +{ + return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); +} + +MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_result result; + ma_uint32 ibpf2; + + if (pBPF == NULL) { + return MA_INVALID_ARGS; + } + + /* Faster path for in-place. */ + if (pFramesOut == pFramesIn) { + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } + } + + /* Slightly slower path for copying. */ + if (pFramesOut != pFramesIn) { + ma_uint32 iFrame; + + /* */ if (pBPF->format == ma_format_f32) { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pBPF->channels; + pFramesInF32 += pBPF->channels; + } + } else if (pBPF->format == ma_format_s16) { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); + + for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { + ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pBPF->channels; + pFramesInS16 += pBPF->channels; + } + } else { + MA_ASSERT(MA_FALSE); + return MA_INVALID_OPERATION; /* Should never hit this. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) +{ + if (pBPF == NULL) { + return 0; + } + + return pBPF->bpf2Count*2; +} + + +/************************************************************************************************************************************************************** + +Notching Filter + +**************************************************************************************************************************************************************/ +MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) +{ + ma_notch2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.q = q; + config.frequency = frequency; + + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + + bqConfig.b0 = 1; + bqConfig.b1 = -2 * c; + bqConfig.b2 = 1; + bqConfig.a0 = 1 + a; + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - a; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_notch2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_notch2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_notch2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + + +/************************************************************************************************************************************************************** + +Peaking EQ Filter + +**************************************************************************************************************************************************************/ +MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_peak2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.q = q; + config.frequency = frequency; + + if (config.q == 0) { + config.q = 0.707107; + } + + return config; +} + + +static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) +{ + ma_biquad_config bqConfig; + double q; + double w; + double s; + double c; + double a; + double A; + + MA_ASSERT(pConfig != NULL); + + q = pConfig->q; + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + a = s / (2*q); + A = ma_powd(10, (pConfig->gainDB / 40)); + + bqConfig.b0 = 1 + (a * A); + bqConfig.b1 = -2 * c; + bqConfig.b2 = 1 - (a * A); + bqConfig.a0 = 1 + (a / A); + bqConfig.a1 = -2 * c; + bqConfig.a2 = 1 - (a / A); + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_peak2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_peak2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_peak2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + +/************************************************************************************************************************************************************** + +Low Shelf Filter + +**************************************************************************************************************************************************************/ +MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) +{ + ma_loshelf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.shelfSlope = shelfSlope; + config.frequency = frequency; + + return config; +} + + +static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double w; + double s; + double c; + double A; + double S; + double a; + double sqrtA; + + MA_ASSERT(pConfig != NULL); + + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + A = ma_powd(10, (pConfig->gainDB / 40)); + S = pConfig->shelfSlope; + a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); + sqrtA = 2*ma_sqrtd(A)*a; + + bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); + bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); + bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); + bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; + bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); + bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_loshelf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + +/************************************************************************************************************************************************************** + +High Shelf Filter + +**************************************************************************************************************************************************************/ +MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) +{ + ma_hishelf2_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.gainDB = gainDB; + config.shelfSlope = shelfSlope; + config.frequency = frequency; + + return config; +} + + +static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) +{ + ma_biquad_config bqConfig; + double w; + double s; + double c; + double A; + double S; + double a; + double sqrtA; + + MA_ASSERT(pConfig != NULL); + + w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; + s = ma_sind(w); + c = ma_cosd(w); + A = ma_powd(10, (pConfig->gainDB / 40)); + S = pConfig->shelfSlope; + a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); + sqrtA = 2*ma_sqrtd(A)*a; + + bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); + bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); + bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); + bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; + bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); + bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; + + bqConfig.format = pConfig->format; + bqConfig.channels = pConfig->channels; + + return bqConfig; +} + +MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_biquad_config bqConfig; + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + + return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); +} + +MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFilter); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ + return MA_SUCCESS; +} + +MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFilter == NULL) { + return; + } + + ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ +} + +MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) +{ + ma_result result; + ma_biquad_config bqConfig; + + if (pFilter == NULL || pConfig == NULL) { + return MA_INVALID_ARGS; + } + + bqConfig = ma_hishelf2__get_biquad_config(pConfig); + result = ma_biquad_reinit(&bqConfig, &pFilter->bq); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) +{ + ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); +} + +static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) +{ + ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); +} + +MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFilter == NULL) { + return MA_INVALID_ARGS; + } + + return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); +} + +MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) +{ + if (pFilter == NULL) { + return 0; + } + + return ma_biquad_get_latency(&pFilter->bq); +} + + + +/* +Delay +*/ +MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.sampleRate = sampleRate; + config.delayInFrames = delayInFrames; + config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ + config.wet = 1; + config.dry = 1; + config.decay = decay; + + return config; +} + + +MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) +{ + if (pDelay == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelay); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->decay < 0 || pConfig->decay > 1) { + return MA_INVALID_ARGS; + } + + pDelay->config = *pConfig; + pDelay->bufferSizeInFrames = pConfig->delayInFrames; + pDelay->cursor = 0; + + pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); + if (pDelay->pBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); + + return MA_SUCCESS; +} + +MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelay == NULL) { + return; + } + + ma_free(pDelay->pBuffer, pAllocationCallbacks); +} + +MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_uint32 iFrame; + ma_uint32 iChannel; + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { + ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; + + if (pDelay->config.delayStart) { + /* Delayed start. */ + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + } else { + /* Immediate start */ + + /* Feedback */ + pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); + + /* Read */ + pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; + } + } + + pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; + + pFramesOutF32 += pDelay->config.channels; + pFramesInF32 += pDelay->config.channels; + } + + return MA_SUCCESS; +} + +MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.wet = value; +} + +MA_API float ma_delay_get_wet(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.wet; +} + +MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.dry = value; +} + +MA_API float ma_delay_get_dry(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.dry; +} + +MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) +{ + if (pDelay == NULL) { + return; + } + + pDelay->config.decay = value; +} + +MA_API float ma_delay_get_decay(const ma_delay* pDelay) +{ + if (pDelay == NULL) { + return 0; + } + + return pDelay->config.decay; +} + + +MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) +{ + ma_gainer_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.smoothTimeInFrames = smoothTimeInFrames; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t oldGainsOffset; + size_t newGainsOffset; +} ma_gainer_heap_layout; + +static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Old gains. */ + pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* New gains. */ + pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + + /* Alignment. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) +{ + ma_result result; + ma_gainer_heap_layout heapLayout; + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGainer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_gainer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pGainer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); + pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); + pGainer->masterVolume = 1; + + pGainer->config = *pConfig; + pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pGainer->pOldGains[iChannel] = 1; + pGainer->pNewGains[iChannel] = 1; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap allocation. */ + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pGainer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pGainer == NULL) { + return; + } + + if (pGainer->_ownsHeap) { + ma_free(pGainer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) +{ + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); +} + +static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + ma_uint64 interpolatedFrameCount; + + MA_ASSERT(pGainer != NULL); + + /* + We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When + linear interpolation is not needed we can do a simple volume adjustment which will be more + efficient than a lerp with an alpha value of 1. + + To do this, all we need to do is determine how many frames need to have a lerp applied. Then we + just process that number of frames with linear interpolation. After that we run on an optimized + path which just applies the new gains without a lerp. + */ + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + interpolatedFrameCount = 0; + } else { + interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames; + if (interpolatedFrameCount > frameCount) { + interpolatedFrameCount = frameCount; + } + } + + /* + Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers + so that the fast path can work naturally without consideration of the interpolated path. + */ + if (interpolatedFrameCount > 0) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + /* + All we're really doing here is moving the old gains towards the new gains. We don't want to + be modifying the gains inside the ma_gainer object because that will break things. Instead + we can make a copy here on the stack. For extreme channel counts we can fall back to a slower + implementation which just uses a standard lerp. + */ + float* pFramesOutF32 = (float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + + if (pGainer->config.channels <= 32) { + float pRunningGain[32]; + float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */ + + /* Initialize the running gain. */ + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; + pRunningGainDelta[iChannel] = t * d; + pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); + } + + iFrame = 0; + + /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */ + if (pGainer->config.channels == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0)); + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + /* + Two different scalar implementations here. Clang (and I assume GCC) will vectorize + both of these, but the bottom version results in a nicer vectorization with less + instructions emitted. The problem, however, is that the bottom version runs slower + when compiled with MSVC. The top version will be partially vectorized by MSVC. + */ + #if defined(_MSC_VER) && !defined(__clang__) + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */ + pRunningGainDelta[2] = pRunningGainDelta[0]; + pRunningGainDelta[3] = pRunningGainDelta[1]; + pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0]; + pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1]; + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0]; + pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1]; + pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2]; + pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3]; + + /* Move the running gain forward towards the new gain. */ + pRunningGain[0] += pRunningGainDelta[0]; + pRunningGain[1] += pRunningGainDelta[1]; + pRunningGain[2] += pRunningGainDelta[2]; + pRunningGain[3] += pRunningGainDelta[3]; + } + + iFrame = unrolledLoopCount << 1; + #else + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel]; + } + + for (iChannel = 0; iChannel < 2; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + #endif + } + } else if (pGainer->config.channels == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* + For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames + at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays + so we can do clean 4x SIMD operations. + */ + ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1; + + /* Expand some arrays so we can have a clean SIMD loop below. */ + __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]); + __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]); + + __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]); + __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]); + __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]); + + for (; iFrame < unrolledLoopCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1)); + _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2); + } + + iFrame = unrolledLoopCount << 1; + } else + #endif + { + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 6; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } else if (pGainer->config.channels == 8) { + /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */ + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]); + __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]); + __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]); + __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]); + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0)); + _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1)); + + runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0); + runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1); + } + } else + #endif + { + /* This is crafted so that it auto-vectorizes when compiled with Clang. */ + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel]; + } + + /* Move the running gain forward towards the new gain. */ + for (iChannel = 0; iChannel < 8; iChannel += 1) { + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } + } + + for (; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel]; + pRunningGain[iChannel] += pRunningGainDelta[iChannel]; + } + } + } else { + /* Slower path for extreme channel counts where we can't fit enough on the stack. We could also move this to the heap as part of the ma_gainer object which might even be better since it'll only be updated when the gains actually change. */ + for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; + } + + a += d; + } + } + } + + /* Make sure the timer is updated. */ + pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames); + + /* Adjust our arguments so the next part can work normally. */ + frameCount -= interpolatedFrameCount; + pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float)); + pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float)); + } + + /* All we need to do here is apply the new gains using an optimized path. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + if (pGainer->config.channels <= 32) { + float gains[32]; + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + + ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains); + } else { + /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume; + } + } + } + } + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount); + } + +#if 0 + if (pGainer->t >= pGainer->config.smoothTimeInFrames) { + /* Fast path. No gain calculation required. */ + ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); + ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume); + + /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; + } + } else { + /* Slow path. Need to interpolate the gain for each channel individually. */ + + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; + float d = 1.0f / pGainer->config.smoothTimeInFrames; + ma_uint32 channelCount = pGainer->config.channels; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channelCount; iChannel += 1) { + pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume; + } + + pFramesOutF32 += channelCount; + pFramesInF32 += channelCount; + + a += d; + if (a > 1) { + a = 1; + } + } + } + + pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); + + #if 0 /* Reference implementation. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ + if (pFramesOut != NULL && pFramesIn != NULL) { + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume; + } + } + + /* Move interpolation time forward, but don't go beyond our smoothing time. */ + pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); + } + #endif + } +#endif + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + /* + ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which + helps with auto-vectorization. + */ + return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount); +} + +static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) +{ + pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); + pGainer->pNewGains[iChannel] = newGain; +} + +static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) +{ + if (pGainer->t == (ma_uint32)-1) { + pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ + } else { + pGainer->t = 0; + } +} + +MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) +{ + ma_uint32 iChannel; + + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) +{ + ma_uint32 iChannel; + + if (pGainer == NULL || pNewGains == NULL) { + return MA_INVALID_ARGS; + } + + for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { + ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); + } + + /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ + ma_gainer_reset_smoothing_time(pGainer); + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume) +{ + if (pGainer == NULL) { + return MA_INVALID_ARGS; + } + + pGainer->masterVolume = volume; + + return MA_SUCCESS; +} + +MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume) +{ + if (pGainer == NULL || pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = pGainer->masterVolume; + + return MA_SUCCESS; +} + + +MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) +{ + ma_panner_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ + config.pan = 0; + + return config; +} + + +MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) +{ + if (pPanner == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPanner); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pPanner->format = pConfig->format; + pPanner->channels = pConfig->channels; + pPanner->mode = pConfig->mode; + pPanner->pan = pConfig->pan; + + return MA_SUCCESS; +} + +static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factor = 1.0f - pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; + } + } + } else { + float factor = 1.0f + pan; + if (pFramesOut == pFramesIn) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; + pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; + } + } + } +} + +static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + + +static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) +{ + ma_uint64 iFrame; + + if (pan > 0) { + float factorL0 = 1.0f - pan; + float factorL1 = 0.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); + float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } else { + float factorR0 = 0.0f - pan; + float factorR1 = 1.0f + pan; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); + float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); + + pFramesOut[iFrame*2 + 0] = sample0; + pFramesOut[iFrame*2 + 1] = sample1; + } + } +} + +static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) +{ + if (pan == 0) { + /* Fast path. No panning required. */ + if (pFramesOut == pFramesIn) { + /* No-op */ + } else { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } + + return; + } + + switch (format) { + case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; + + /* Unknown format. Just copy. */ + default: + { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); + } break; + } +} + +MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + if (pPanner->channels == 2) { + /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ + if (pPanner->mode == ma_pan_mode_balance) { + ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } else { + ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); + } + } else { + if (pPanner->channels == 1) { + /* Panning has no effect on mono streams. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } else { + /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) +{ + if (pPanner == NULL) { + return; + } + + pPanner->mode = mode; +} + +MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return ma_pan_mode_balance; + } + + return pPanner->mode; +} + +MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) +{ + if (pPanner == NULL) { + return; + } + + pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); +} + +MA_API float ma_panner_get_pan(const ma_panner* pPanner) +{ + if (pPanner == NULL) { + return 0; + } + + return pPanner->pan; +} + + + + +MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_fader_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + + +MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFader); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* Only f32 is supported for now. */ + if (pConfig->format != ma_format_f32) { + return MA_INVALID_ARGS; + } + + pFader->config = *pConfig; + pFader->volumeBeg = 1; + pFader->volumeEnd = 1; + pFader->lengthInFrames = 0; + pFader->cursorInFrames = 0; + + return MA_SUCCESS; +} + +MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pFader == NULL) { + return MA_INVALID_ARGS; + } + + /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ + if (pFader->cursorInFrames < 0) { + ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; + if (absCursorInFrames > frameCount) { + absCursorInFrames = frameCount; + } + + ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); + + pFader->cursorInFrames += absCursorInFrames; + frameCount -= absCursorInFrames; + pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + } + + if (pFader->cursorInFrames >= 0) { + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; + } + + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); + } else { + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); + } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } else { + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; + + /* For now we only support f32. Support for other formats might be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; + } + } + } else { + return MA_NOT_IMPLEMENTED; + } + } + } + } + + pFader->cursorInFrames += frameCount; + + return MA_SUCCESS; +} + +MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) +{ + if (pFader == NULL) { + return; + } + + if (pFormat != NULL) { + *pFormat = pFader->config.format; + } + + if (pChannels != NULL) { + *pChannels = pFader->config.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pFader->config.sampleRate; + } +} + +MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) +{ + ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); +} + +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) +{ + if (pFader == NULL) { + return; + } + + /* If the volume is negative, use current volume. */ + if (volumeBeg < 0) { + volumeBeg = ma_fader_get_current_volume(pFader); + } + + /* + The length needs to be clamped to 32-bits due to how we convert it to a float for linear + interpolation reasons. I might change this requirement later, but for now it's not important. + */ + if (lengthInFrames > UINT_MAX) { + lengthInFrames = UINT_MAX; + } + + /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ + if (startOffsetInFrames > INT_MAX) { + startOffsetInFrames = INT_MAX; + } + + pFader->volumeBeg = volumeBeg; + pFader->volumeEnd = volumeEnd; + pFader->lengthInFrames = lengthInFrames; + pFader->cursorInFrames = -startOffsetInFrames; +} + +MA_API float ma_fader_get_current_volume(const ma_fader* pFader) +{ + if (pFader == NULL) { + return 0.0f; + } + + /* Any frames prior to the start of the fade period will be at unfaded volume. */ + if (pFader->cursorInFrames < 0) { + return 1.0f; + } + + /* The current volume depends on the position of the cursor. */ + if (pFader->cursorInFrames == 0) { + return pFader->volumeBeg; + } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ + return pFader->volumeEnd; + } else { + /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ + return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ + } +} + + + + + +MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) +{ + ma_vec3f v; + + v.x = x; + v.y = y; + v.z = z; + + return v; +} + +MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.x - b.x, + a.y - b.y, + a.z - b.z + ); +} + +MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) +{ + return ma_vec3f_init_3f( + -a.x, + -a.y, + -a.z + ); +} + +MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) +{ + return a.x*b.x + a.y*b.y + a.z*b.z; +} + +MA_API float ma_vec3f_len2(ma_vec3f v) +{ + return ma_vec3f_dot(v, v); +} + +MA_API float ma_vec3f_len(ma_vec3f v) +{ + return (float)ma_sqrtd(ma_vec3f_len2(v)); +} + + + +MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_len(ma_vec3f_sub(a, b)); +} + +MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) +{ + float invLen; + float len2 = ma_vec3f_len2(v); + if (len2 == 0) { + return ma_vec3f_init_3f(0, 0, 0); + } + + invLen = ma_rsqrtf(len2); + v.x *= invLen; + v.y *= invLen; + v.z *= invLen; + + return v; +} + +MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) +{ + return ma_vec3f_init_3f( + a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x + ); +} + + +MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value) +{ + v->v = value; + v->lock = 0; /* Important this is initialized to 0. */ +} + +MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value) +{ + ma_spinlock_lock(&v->lock); + { + v->v = value; + } + ma_spinlock_unlock(&v->lock); +} + +MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v) +{ + ma_vec3f r; + + ma_spinlock_lock(&v->lock); + { + r = v->v; + } + ma_spinlock_unlock(&v->lock); + + return r; +} + + + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); +static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); + + +#ifndef MA_DEFAULT_SPEED_OF_SOUND +#define MA_DEFAULT_SPEED_OF_SOUND 343.3f +#endif + +/* +These vectors represent the direction that speakers are facing from the center point. They're used +for panning in the spatializer. Must be normalized. +*/ +static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ + {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ + {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ + {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ + {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ + {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ + {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ + { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ + {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ + {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ + { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ + {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ + { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ + {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ + {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ + { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ + {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ + { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ + { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ +}; + +static ma_vec3f ma_get_channel_direction(ma_channel channel) +{ + if (channel >= MA_CHANNEL_POSITION_COUNT) { + return ma_vec3f_init_3f(0, 0, -1); + } else { + return g_maChannelDirections[channel]; + } +} + + + +static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); +} + +static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); +} + +static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) +{ + if (minDistance >= maxDistance) { + return 1; /* To avoid division by zero. Do not attenuate. */ + } + + return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); +} + + +/* +Dopper Effect calculation taken from the OpenAL spec, with two main differences: + + 1) The source to listener vector will have already been calcualted at an earlier step so we can + just use that directly. We need only the position of the source relative to the origin. + + 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight + into the resampler directly. +*/ +static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) +{ + float len; + float vls; + float vss; + + len = ma_vec3f_len(relativePosition); + + /* + There's a case where the position of the source will be right on top of the listener in which + case the length will be 0 and we'll end up with a division by zero. We can just return a ratio + of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. + */ + if (len == 0) { + return 1.0; + } + + vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; + vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; + + vls = ma_min(vls, speedOfSound / dopplerFactor); + vss = ma_min(vss, speedOfSound / dopplerFactor); + + return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); +} + + +static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) +{ + /* + Special case for stereo. Want to default the left and right speakers to side left and side + right so that they're facing directly down the X axis rather than slightly forward. Not + doing this will result in sounds being quieter when behind the listener. This might + actually be good for some scenerios, but I don't think it's an appropriate default because + it can be a bit unexpected. + */ + if (channelCount == 2) { + pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; + pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); + } +} + + +MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) +{ + ma_spatializer_listener_config config; + + MA_ZERO_OBJECT(&config); + config.channelsOut = channelsOut; + config.pChannelMapOut = NULL; + config.handedness = ma_handedness_right; + config.worldUp = ma_vec3f_init_3f(0, 1, 0); + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterGain = 0; + config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapOutOffset; +} ma_spatializer_listener_heap_layout; + +static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. We always need this, even for passthroughs. */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) +{ + ma_result result; + ma_spatializer_listener_heap_layout heapLayout; + + if (pListener == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pListener); + + result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pListener->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pListener->config = *pConfig; + ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0)); + pListener->isEnabled = MA_TRUE; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pListener->config.handedness == ma_handedness_left) { + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener)); + ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z); + } + + + /* We must always have a valid channel map. */ + pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + + /* Use a slightly different default channel map for stereo. */ + if (pConfig->pChannelMapOut == NULL) { + ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); + } else { + ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pListener->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pListener == NULL) { + return; + } + + if (pListener->_ownsHeap) { + ma_free(pListener->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return NULL; + } + + return pListener->config.pChannelMapOut; +} + +MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pListener == NULL) { + return; + } + + pListener->config.coneInnerAngleInRadians = innerAngleInRadians; + pListener->config.coneOuterAngleInRadians = outerAngleInRadians; + pListener->config.coneOuterGain = outerGain; +} + +MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pListener == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; + } + + if (pOuterGain != NULL) { + *pOuterGain = pListener->config.coneOuterGain; + } +} + +MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) +{ + if (pListener == NULL) { + return; + } + + pListener->config.speedOfSound = speedOfSound; +} + +MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return 0; + } + + return pListener->config.speedOfSound; +} + +MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) +{ + if (pListener == NULL) { + return; + } + + pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); +} + +MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return pListener->config.worldUp; +} + +MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) +{ + if (pListener == NULL) { + return; + } + + pListener->isEnabled = isEnabled; +} + +MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) +{ + if (pListener == NULL) { + return MA_FALSE; + } + + return pListener->isEnabled; +} + + + + +MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) +{ + ma_spatializer_config config; + + MA_ZERO_OBJECT(&config); + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = NULL; + config.attenuationModel = ma_attenuation_model_inverse; + config.positioning = ma_positioning_absolute; + config.handedness = ma_handedness_right; + config.minGain = 0; + config.maxGain = 1; + config.minDistance = 1; + config.maxDistance = MA_FLT_MAX; + config.rolloff = 1; + config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ + config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ + config.coneOuterGain = 0.0f; + config.dopplerFactor = 1; + config.directionalAttenuationFactor = 1; + config.minSpatializationChannelGain = 0.2f; + config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ + + return config; +} + + +static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); +} + +static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t newChannelGainsOffset; + size_t gainerOffset; +} ma_spatializer_heap_layout; + +static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_validate_config(pConfig); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel map. */ + pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); + } + + /* New channel gains for output. */ + pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); + + /* Gainer. */ + { + size_t gainerHeapSizeInBytes; + ma_gainer_config gainerConfig; + + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; /* Safety. */ + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) +{ + ma_result result; + ma_spatializer_heap_layout heapLayout; + ma_gainer_config gainerConfig; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSpatializer); + + if (pConfig == NULL || pHeap == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pSpatializer->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pSpatializer->channelsIn = pConfig->channelsIn; + pSpatializer->channelsOut = pConfig->channelsOut; + pSpatializer->attenuationModel = pConfig->attenuationModel; + pSpatializer->positioning = pConfig->positioning; + pSpatializer->handedness = pConfig->handedness; + pSpatializer->minGain = pConfig->minGain; + pSpatializer->maxGain = pConfig->maxGain; + pSpatializer->minDistance = pConfig->minDistance; + pSpatializer->maxDistance = pConfig->maxDistance; + pSpatializer->rolloff = pConfig->rolloff; + pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; + pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; + pSpatializer->coneOuterGain = pConfig->coneOuterGain; + pSpatializer->dopplerFactor = pConfig->dopplerFactor; + pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; + pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; + pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; + ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); + ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1)); + ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0)); + pSpatializer->dopplerPitch = 1; + + /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ + if (pSpatializer->handedness == ma_handedness_left) { + ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer)); + ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z); + } + + /* Channel map. This will be on the heap. */ + if (pConfig->pChannelMapIn != NULL) { + pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); + } + + /* New channel gains for output channels. */ + pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); + + /* Gainer. */ + gainerConfig = ma_spatializer_gainer_config_init(pConfig); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the gainer. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + /* We'll need a heap allocation to retrieve the size. */ + result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pSpatializer->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pSpatializer == NULL) { + return; + } + + ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); + + if (pSpatializer->_ownsHeap) { + ma_free(pSpatializer->_pHeap, pAllocationCallbacks); + } +} + +static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) +{ + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (coneInnerAngleInRadians < 6.283185f) { + float angularGain = 1; + float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); + float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); + float d; + + d = ma_vec3f_dot(dirA, dirB); + + if (d > cutoffInner) { + /* It's inside the inner angle. */ + angularGain = 1; + } else { + /* It's outside the inner angle. */ + if (d > cutoffOuter) { + /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ + angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); + } else { + /* It's outside the outer angle. */ + angularGain = coneOuterGain; + } + } + + /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ + return angularGain; + } else { + /* Inner angle is 360 degrees so no need to do any attenuation. */ + return 1; + } +} + +MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; + ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; + + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + /* If we're not spatializing we need to run an optimized path. */ + if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (ma_spatializer_listener_is_enabled(pListener)) { + /* No attenuation is required, but we'll need to do some channel conversion. */ + if (pSpatializer->channelsIn == pSpatializer->channelsOut) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); + } else { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ + } + } else { + /* The listener is disabled. Output silence. */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* + We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is + the correct thinking so might need to review this later. + */ + pSpatializer->dopplerPitch = 1; + } else { + /* + Let's first determine which listener the sound is closest to. Need to keep in mind that we + might not have a world or any listeners, in which case we just spatializer based on the + listener being positioned at the origin (0, 0, 0). + */ + ma_vec3f relativePosNormalized; + ma_vec3f relativePos; /* The position relative to the listener. */ + ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ + ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ + float speedOfSound; + float distance = 0; + float gain = 1; + ma_uint32 iChannel; + const ma_uint32 channelsOut = pSpatializer->channelsOut; + const ma_uint32 channelsIn = pSpatializer->channelsIn; + float minDistance = ma_spatializer_get_min_distance(pSpatializer); + float maxDistance = ma_spatializer_get_max_distance(pSpatializer); + float rolloff = ma_spatializer_get_rolloff(pSpatializer); + float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); + + /* + We'll need the listener velocity for doppler pitch calculations. The speed of sound is + defined by the listener, so we'll grab that here too. + */ + if (pListener != NULL) { + listenerVel = ma_spatializer_listener_get_velocity(pListener); + speedOfSound = pListener->config.speedOfSound; + } else { + listenerVel = ma_vec3f_init_3f(0, 0, 0); + speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + relativePos = ma_spatializer_get_position(pSpatializer); + relativeDir = ma_spatializer_get_direction(pSpatializer); + } else { + /* + We've found a listener and we're using absolute positioning. We need to transform the + sound's position and direction so that it's relative to listener. Later on we'll use + this for determining the factors to apply to each channel to apply the panning effect. + */ + ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); + } + + distance = ma_vec3f_len(relativePos); + + /* We've gathered the data, so now we can apply some spatialization. */ + switch (ma_spatializer_get_attenuation_model(pSpatializer)) { + case ma_attenuation_model_inverse: + { + gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_linear: + { + gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_exponential: + { + gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); + } break; + case ma_attenuation_model_none: + default: + { + gain = 1; + } break; + } + + /* Normalize the position. */ + if (distance > 0.001f) { + float distanceInv = 1/distance; + relativePosNormalized = relativePos; + relativePosNormalized.x *= distanceInv; + relativePosNormalized.y *= distanceInv; + relativePosNormalized.z *= distanceInv; + } else { + distance = 0; + relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); + } + + /* + Angular attenuation. + + Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure + this out for ourselves at the expense of possibly being inconsistent with other implementations. + + To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We + just need to get the direction from the source to the listener and then do a dot product against that and the + direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer + angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than + the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. + */ + if (distance > 0) { + /* Source anglular gain. */ + float spatializerConeInnerAngle; + float spatializerConeOuterAngle; + float spatializerConeOuterGain; + ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); + + gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); + + /* + We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that + are positioned behind the listener. On default settings, this will have no effect. + */ + if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { + ma_vec3f listenerDirection; + float listenerInnerAngle; + float listenerOuterAngle; + float listenerOuterGain; + + if (pListener->config.handedness == ma_handedness_right) { + listenerDirection = ma_vec3f_init_3f(0, 0, -1); + } else { + listenerDirection = ma_vec3f_init_3f(0, 0, +1); + } + + listenerInnerAngle = pListener->config.coneInnerAngleInRadians; + listenerOuterAngle = pListener->config.coneOuterAngleInRadians; + listenerOuterGain = pListener->config.coneOuterGain; + + gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); + } + } else { + /* The sound is right on top of the listener. Don't do any angular attenuation. */ + } + + + /* Clamp the gain. */ + gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + + /* + Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for + when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the + gain to the final output. + */ + /*printf("distance=%f; gain=%f\n", distance, gain);*/ + + /* We must have a valid channel map here to ensure we spatialize properly. */ + MA_ASSERT(pChannelMapOut != NULL); + + /* + We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being + to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that + the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and + seeing how it goes. There might be better ways to do this. + + To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a + direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will + be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized + position of the sound. + */ + + /* + Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's + relation to the direction of the channel. + */ + if (distance > 0) { + ma_vec3f unitPos = relativePos; + float distanceInv = 1/distance; + unitPos.x *= distanceInv; + unitPos.y *= distanceInv; + unitPos.z *= distanceInv; + + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + ma_channel channelOut; + float d; + float dMin; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); + if (ma_is_spatial_channel_position(channelOut)) { + d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); + } else { + d = 1; /* It's not a spatial channel so there's no real notion of direction. */ + } + + /* + In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. + The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to + 0, panning will be most extreme and any sounds that are positioned on the opposite side of the + speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it + doesn't even remotely represent the real world at all because sounds that come from your right side + are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at + all, which is also not ideal. By setting it to something greater than 0, the spatialization effect + becomes much less dramatic and a lot more bearable. + + Summary: 0 = more extreme panning; 1 = no panning. + */ + dMin = pSpatializer->minSpatializationChannelGain; + + /* + At this point, "d" will be positive if the sound is on the same side as the channel and negative if + it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to + calculate a panning value. The first is to simply convert it to 0..1, however this has a problem + which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right + in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like + the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front + of the listener. I would intuitively expect that to be played at full volume, or close to it. + + The second idea I think of is to only apply a reduction in gain when the sound is on the opposite + side of the speaker. That is, reduce the gain only when the dot product is negative. The problem + with this is that there will not be any attenuation as the sound sweeps around the 180 degrees + where the dot product is positive. The idea with this option is that you leave the gain at 1 when + the sound is being played on the same side as the speaker and then you just reduce the volume when + the sound is on the other side. + + The summarize, I think the first option should give a better sense of spatialization, but the second + option is better for preserving the sound's power. + + UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a + bit better, but you can also hear the reduction in volume when it's right in front. + */ + #if 1 + { + /* + Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power + by being played at 0.5 gain. + */ + d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ + d = ma_max(d, dMin); + pSpatializer->pNewChannelGainsOut[iChannel] *= d; + } + #else + { + /* + Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more + consistent, but comes at the expense of a worse sense of space and positioning. + */ + if (d < 0) { + d += 1; /* Move into the positive range. */ + d = ma_max(d, dMin); + channelGainsOut[iChannel] *= d; + } + } + #endif + } + } else { + /* Assume the sound is right on top of us. Don't do any panning. */ + } + + /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ + ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); + ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); + + /* + Before leaving we'll want to update our doppler pitch so that the caller can apply some + pitch shifting if they desire. Note that we need to negate the relative position here + because the doppler calculation needs to be source-to-listener, but ours is listener-to- + source. + */ + if (dopplerFactor > 0) { + pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor); + } else { + pSpatializer->dopplerPitch = 1; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_set_master_volume(&pSpatializer->gainer, volume); +} + +MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume) +{ + if (pSpatializer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume); +} + +MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsIn; +} + +MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return pSpatializer->channelsOut; +} + +MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); +} + +MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_attenuation_model_none; + } + + return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); +} + +MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); +} + +MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_positioning_absolute; + } + + return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); +} + +MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); +} + +MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->rolloff); +} + +MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); +} + +MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->minGain); +} + +MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); +} + +MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->maxGain); +} + +MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); +} + +MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->minDistance); +} + +MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); +} + +MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSpatializer->maxDistance); +} + +MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); +} + +MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pSpatializer == NULL) { + return; + } + + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + } + + if (pOuterGain != NULL) { + *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); + } +} + +MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); +} + +MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return ma_atomic_load_f32(&pSpatializer->dopplerFactor); +} + +MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); +} + +MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return 1; + } + + return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); +} + +MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) +{ + if (pSpatializer == NULL) { + return; + } + + ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z)); +} + +MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) +{ + if (pSpatializer == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity); /* Naughty const-cast. It's just for atomically loading the vec3 which should be safe. */ +} + +MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) +{ + if (pRelativePos != NULL) { + pRelativePos->x = 0; + pRelativePos->y = 0; + pRelativePos->z = 0; + } + + if (pRelativeDir != NULL) { + pRelativeDir->x = 0; + pRelativeDir->y = 0; + pRelativeDir->z = -1; + } + + if (pSpatializer == NULL) { + return; + } + + if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { + /* There's no listener or we're using relative positioning. */ + if (pRelativePos != NULL) { + *pRelativePos = ma_spatializer_get_position(pSpatializer); + } + if (pRelativeDir != NULL) { + *pRelativeDir = ma_spatializer_get_direction(pSpatializer); + } + } else { + ma_vec3f spatializerPosition; + ma_vec3f spatializerDirection; + ma_vec3f listenerPosition; + ma_vec3f listenerDirection; + ma_vec3f v; + ma_vec3f axisX; + ma_vec3f axisY; + ma_vec3f axisZ; + float m[4][4]; + + spatializerPosition = ma_spatializer_get_position(pSpatializer); + spatializerDirection = ma_spatializer_get_direction(pSpatializer); + listenerPosition = ma_spatializer_listener_get_position(pListener); + listenerDirection = ma_spatializer_listener_get_direction(pListener); + + /* + We need to calcualte the right vector from our forward and up vectors. This is done with + a cross product. + */ + axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */ + axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ + + /* + The calculation of axisX above can result in a zero-length vector if the listener is + looking straight up on the Y axis. We'll need to fall back to a +X in this case so that + the calculations below don't fall apart. This is where a quaternion based listener and + sound orientation would come in handy. + */ + if (ma_vec3f_len2(axisX) == 0) { + axisX = ma_vec3f_init_3f(1, 0, 0); + } + + axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ + + /* + We need to swap the X axis if we're left handed because otherwise the cross product above + will have resulted in it pointing in the wrong direction (right handed was assumed in the + cross products above). + */ + if (pListener->config.handedness == ma_handedness_left) { + axisX = ma_vec3f_neg(axisX); + } + + /* Lookat. */ + m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition); + m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition); + m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition); + m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; + + /* + Multiply the lookat matrix by the spatializer position to transform it to listener + space. This allows calculations to work based on the sound being relative to the + origin which makes things simpler. + */ + if (pRelativePos != NULL) { + v = spatializerPosition; + pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; + pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; + pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; + } + + /* + The direction of the sound needs to also be transformed so that it's relative to the + rotation of the listener. + */ + if (pRelativeDir != NULL) { + v = spatializerDirection; + pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; + pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; + pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; + } + } +} + + + + +/************************************************************************************************************************************************************** + +Resampling + +**************************************************************************************************************************************************************/ +MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_linear_resampler_config config; + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + config.lpfNyquistFactor = 1; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t x0Offset; + size_t x1Offset; + size_t lpfOffset; +} ma_linear_resampler_heap_layout; + + +static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) +{ + /* + So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will + be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. + */ + ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ + ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; + + pResampler->inTimeFrac = + (oldRateTimeWhole * newSampleRateOut) + + ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); + + /* Make sure the fractional part is less than the output sample rate. */ + pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; + pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; +} + +static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) +{ + ma_result result; + ma_uint32 gcf; + ma_uint32 lpfSampleRate; + double lpfCutoffFrequency; + ma_lpf_config lpfConfig; + ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MA_INVALID_ARGS; + } + + oldSampleRateOut = pResampler->config.sampleRateOut; + + pResampler->config.sampleRateIn = sampleRateIn; + pResampler->config.sampleRateOut = sampleRateOut; + + /* Simplify the sample rate. */ + gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); + pResampler->config.sampleRateIn /= gcf; + pResampler->config.sampleRateOut /= gcf; + + /* Always initialize the low-pass filter, even when the order is 0. */ + if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { + return MA_INVALID_ARGS; + } + + lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); + lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); + + lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); + + /* + If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames + getting cleared. Instead we re-initialize the filter which will maintain any cached frames. + */ + if (isResamplerAlreadyInitialized) { + result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); + } else { + result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); + } + + if (result != MA_SUCCESS) { + return result; + } + + + pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; + pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; + + /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ + ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* x0 */ + pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* x1 */ + pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; + if (pConfig->format == ma_format_f32) { + pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; + } else { + pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; + } + + /* LPF */ + pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); + { + ma_result result; + size_t lpfHeapSizeInBytes; + ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ + + result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_linear_resampler_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) +{ + ma_result result; + ma_linear_resampler_heap_layout heapLayout; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResampler); + + result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->config = *pConfig; + + pResampler->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + if (pConfig->format == ma_format_f32) { + pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } else { + pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); + pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); + } + + /* Setting the rate will set up the filter and time advances for us. */ + result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ + pResampler->inTimeFrac = 0; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pResampler == NULL) { + return; + } + + ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); + } +} + +static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) +{ + ma_int32 b; + ma_int32 c; + ma_int32 r; + + MA_ASSERT(a <= (1<> shift); +} + +static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) +{ + ma_uint32 c; + ma_uint32 a; + const ma_uint32 channels = pResampler->config.channels; + const ma_uint32 shift = 12; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameOut != NULL); + + a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); + pFrameOut[c] = s; + } +} + + +static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) +{ + ma_uint32 c; + float a; + const ma_uint32 channels = pResampler->config.channels; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameOut != NULL); + + a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; + + MA_ASSUME(channels > 0); + for (c = 0; c < channels; c += 1) { + float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); + pFrameOut[c] = s; + } +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const ma_int16* pFramesInS16; + /* */ ma_int16* pFramesOutS16; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInS16 = (const ma_int16*)pFramesIn; + pFramesOutS16 = ( ma_int16*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInS16 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; + } + pFramesInS16 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = 0; + } + } + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ + if (pFramesOutS16 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); + + pFramesOutS16 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const ma_int16* pFramesInS16; + /* */ ma_int16* pFramesOutS16; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInS16 = (const ma_int16*)pFramesIn; + pFramesOutS16 = ( ma_int16*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInS16 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; + } + pFramesInS16 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; + pResampler->x1.s16[iChannel] = 0; + } + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and we can generate the next output frame. */ + if (pFramesOutS16 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + } + + pFramesOutS16 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { + return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + + +static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const float* pFramesInF32; + /* */ float* pFramesOutF32; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInF32 = (const float*)pFramesIn; + pFramesOutF32 = ( float*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInF32 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; + } + pFramesInF32 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = 0; + } + } + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ + if (pFramesOutF32 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); + + pFramesOutF32 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + const float* pFramesInF32; + /* */ float* pFramesOutF32; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pResampler != NULL); + MA_ASSERT(pFrameCountIn != NULL); + MA_ASSERT(pFrameCountOut != NULL); + + pFramesInF32 = (const float*)pFramesIn; + pFramesOutF32 = ( float*)pFramesOut; + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + /* Before interpolating we need to load the buffers. */ + while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { + ma_uint32 iChannel; + + if (pFramesInF32 != NULL) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; + } + pFramesInF32 += pResampler->config.channels; + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; + pResampler->x1.f32[iChannel] = 0; + } + } + + framesProcessedIn += 1; + pResampler->inTimeInt -= 1; + } + + if (pResampler->inTimeInt > 0) { + break; /* Ran out of input data. */ + } + + /* Getting here means the frames have been loaded and we can generate the next output frame. */ + if (pFramesOutF32 != NULL) { + MA_ASSERT(pResampler->inTimeInt == 0); + ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); + + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + } + + pFramesOutF32 += pResampler->config.channels; + } + + framesProcessedOut += 1; + + /* Advance time forward. */ + pResampler->inTimeInt += pResampler->inAdvanceInt; + pResampler->inTimeFrac += pResampler->inAdvanceFrac; + if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { + pResampler->inTimeFrac -= pResampler->config.sampleRateOut; + pResampler->inTimeInt += 1; + } + } + + *pFrameCountIn = framesProcessedIn; + *pFrameCountOut = framesProcessedOut; + + return MA_SUCCESS; +} + +static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pResampler != NULL); + + if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { + return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + + +MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* */ if (pResampler->config.format == ma_format_s16) { + return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else if (pResampler->config.format == ma_format_f32) { + return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ + MA_ASSERT(MA_FALSE); + return MA_INVALID_ARGS; + } +} + + +MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); +} + +MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) +{ + ma_uint32 n; + ma_uint32 d; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (ratioInOut <= 0) { + return MA_INVALID_ARGS; + } + + d = 1000000; + n = (ma_uint32)(ratioInOut * d); + + if (n == 0) { + return MA_INVALID_ARGS; /* Ratio too small. */ + } + + MA_ASSERT(n != 0); + + return ma_linear_resampler_set_rate(pResampler, n, d); +} + +MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + return 1 + ma_lpf_get_latency(&pResampler->lpf); +} + +MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; +} + +MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (outputFrameCount == 0) { + return MA_SUCCESS; + } + + /* Any whole input frames are consumed before the first output frame is generated. */ + inputFrameCount = pResampler->inTimeInt; + outputFrameCount -= 1; + + /* The rest of the output frames can be calculated in constant time. */ + inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; + inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; + + *pInputFrameCount = inputFrameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + ma_uint64 outputFrameCount; + ma_uint64 preliminaryInputFrameCountFromFrac; + ma_uint64 preliminaryInputFrameCount; + + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* + The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to + determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't + be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation + of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. + */ + outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; + + /* + We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is + used in the logic below to determine whether or not we need to add an extra output frame. + */ + preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; + preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; + + /* + If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than + the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data + to actually process. Otherwise we need to add the extra output frame. + */ + if (preliminaryInputFrameCount <= inputFrameCount) { + outputFrameCount += 1; + } + + *pOutputFrameCount = outputFrameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) +{ + ma_uint32 iChannel; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + /* Timers need to be cleared back to zero. */ + pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ + pResampler->inTimeFrac = 0; + + /* Cached samples need to be cleared. */ + if (pResampler->config.format == ma_format_f32) { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.f32[iChannel] = 0; + pResampler->x1.f32[iChannel] = 0; + } + } else { + for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { + pResampler->x0.s16[iChannel] = 0; + pResampler->x1.s16[iChannel] = 0; + } + } + + /* The low pass filter needs to have it's cache reset. */ + ma_lpf_clear_cache(&pResampler->lpf); + + return MA_SUCCESS; +} + + + +/* Linear resampler backend vtable. */ +static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) +{ + ma_linear_resampler_config linearConfig; + + linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); + linearConfig.lpfOrder = pConfig->linear.lpfOrder; + + return linearConfig; +} + +static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); +} + +static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) +{ + ma_resampler* pResampler = (ma_resampler*)pUserData; + ma_result result; + ma_linear_resampler_config linearConfig; + + (void)pUserData; + + linearConfig = ma_resampling_backend_get_config__linear(pConfig); + + result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); + if (result != MA_SUCCESS) { + return result; + } + + *ppBackend = &pResampler->state.linear; + + return MA_SUCCESS; +} + +static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + (void)pUserData; + + ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); +} + +static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + (void)pUserData; + + return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); +} + +static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + (void)pUserData; + + return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); +} + +static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); +} + +static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); +} + +static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); +} + +static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + (void)pUserData; + + return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); +} + +static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) +{ + (void)pUserData; + + return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); +} + +static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = +{ + ma_resampling_backend_get_heap_size__linear, + ma_resampling_backend_init__linear, + ma_resampling_backend_uninit__linear, + ma_resampling_backend_process__linear, + ma_resampling_backend_set_rate__linear, + ma_resampling_backend_get_input_latency__linear, + ma_resampling_backend_get_output_latency__linear, + ma_resampling_backend_get_required_input_frame_count__linear, + ma_resampling_backend_get_expected_output_frame_count__linear, + ma_resampling_backend_reset__linear +}; + + + +MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) +{ + ma_resampler_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + config.algorithm = algorithm; + + /* Linear. */ + config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + + return config; +} + +static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) +{ + MA_ASSERT(pConfig != NULL); + MA_ASSERT(ppVTable != NULL); + MA_ASSERT(ppUserData != NULL); + + /* Safety. */ + *ppVTable = NULL; + *ppUserData = NULL; + + switch (pConfig->algorithm) + { + case ma_resample_algorithm_linear: + { + *ppVTable = &g_ma_linear_resampler_vtable; + *ppUserData = pResampler; + } break; + + case ma_resample_algorithm_custom: + { + *ppVTable = pConfig->pBackendVTable; + *ppUserData = pConfig->pBackendUserData; + } break; + + default: return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_resampling_backend_vtable* pVTable; + void* pVTableUserData; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); + if (result != MA_SUCCESS) { + return result; + } + + if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) +{ + ma_result result; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResampler); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pResampler->_pHeap = pHeap; + pResampler->format = pConfig->format; + pResampler->channels = pConfig->channels; + pResampler->sampleRateIn = pConfig->sampleRateIn; + pResampler->sampleRateOut = pConfig->sampleRateOut; + + result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); + if (result != MA_SUCCESS) { + return result; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { + return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ + } + + result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pResampler->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pResampler == NULL) { + return; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { + return; + } + + pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); + + if (pResampler->_ownsHeap) { + ma_free(pResampler->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pFrameCountOut == NULL && pFrameCountIn == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); +} + +MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_result result; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (sampleRateIn == 0 || sampleRateOut == 0) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); + if (result != MA_SUCCESS) { + return result; + } + + pResampler->sampleRateIn = sampleRateIn; + pResampler->sampleRateOut = sampleRateOut; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) +{ + ma_uint32 n; + ma_uint32 d; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (ratio <= 0) { + return MA_INVALID_ARGS; + } + + d = 1000; + n = (ma_uint32)(ratio * d); + + if (n == 0) { + return MA_INVALID_ARGS; /* Ratio too small. */ + } + + MA_ASSERT(n != 0); + + return ma_resampler_set_rate(pResampler, n, d); +} + +MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { + return 0; + } + + return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); +} + +MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return 0; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { + return 0; + } + + return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); +} + +MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); +} + +MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); +} + +MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) +{ + if (pResampler == NULL) { + return MA_INVALID_ARGS; + } + + if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); +} + +/************************************************************************************************************************************************************** + +Channel Conversion + +**************************************************************************************************************************************************************/ +#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT +#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 +#endif + +#define MA_PLANE_LEFT 0 +#define MA_PLANE_RIGHT 1 +#define MA_PLANE_FRONT 2 +#define MA_PLANE_BACK 3 +#define MA_PLANE_BOTTOM 4 +#define MA_PLANE_TOP 5 + +static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ + { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ + { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ + { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ + { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ + { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ + { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ + { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ + { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ + { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ + { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ + { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ + { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ + { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ + { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ + { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ + { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ + { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ +}; + +static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) +{ + /* + Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to + the following output configuration: + + - front/left + - side/left + - back/left + + The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount + of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. + + Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left + speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted + from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would + receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between + the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works + across 3 spatial dimensions. + + The first thing to do is figure out how each speaker's volume is spread over each of plane: + - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane + - side/left: 1 plane (left only) = 1/1 = entire volume from left plane + - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane + - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane + + The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other + channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be + taken by the other to produce the final contribution. + */ + + /* Contribution = Sum(Volume to Give * Volume to Take) */ + float contribution = + g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + + g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + + g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + + g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + + g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + + g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; + + return contribution; +} + +MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) +{ + ma_channel_converter_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.pChannelMapIn = pChannelMapIn; + config.pChannelMapOut = pChannelMapOut; + config.mixingMode = mixingMode; + + return config; +} + +static ma_int32 ma_channel_converter_float_to_fixed(float x) +{ + return (ma_int32)(x * (1< 0); + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { + spatialChannelCount++; + } + } + + return spatialChannelCount; +} + +static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) +{ + int i; + + if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { + return MA_FALSE; + } + + if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { + return MA_FALSE; + } + + for (i = 0; i < 6; ++i) { /* Each side of a cube. */ + if (g_maChannelPlaneRatios[channelPosition][i] != 0) { + return MA_TRUE; + } + } + + return MA_FALSE; +} + + +static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) +{ + if (channelsOut == channelsIn) { + return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); + } else { + return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ + } +} + +static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) +{ + if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { + return ma_channel_conversion_path_passthrough; + } + + if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_out; + } + + if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { + return ma_channel_conversion_path_mono_in; + } + + if (mode == ma_channel_mix_mode_custom_weights) { + return ma_channel_conversion_path_weights; + } + + /* + We can use a simple shuffle if both channel maps have the same channel count and all channel + positions are present in both. + */ + if (channelsIn == channelsOut) { + ma_uint32 iChannelIn; + ma_bool32 areAllChannelPositionsPresent = MA_TRUE; + for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { + ma_bool32 isInputChannelPositionInOutput = MA_FALSE; + if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { + isInputChannelPositionInOutput = MA_TRUE; + break; + } + + if (!isInputChannelPositionInOutput) { + areAllChannelPositionsPresent = MA_FALSE; + break; + } + } + + if (areAllChannelPositionsPresent) { + return ma_channel_conversion_path_shuffle; + } + } + + /* Getting here means we'll need to use weights. */ + return ma_channel_conversion_path_weights; +} + + +static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) +{ + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { + return MA_INVALID_ARGS; + } + + /* + When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the + input channel has more than one occurance of a channel position, the second one will be ignored. + */ + for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { + ma_channel channelOut; + + /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ + pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; + + channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { + ma_channel channelIn; + + channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); + if (channelOut == channelIn) { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + break; + } + + /* + Getting here means the channels don't exactly match, but we are going to support some + relaxed matching for practicality. If, for example, there are two stereo channel maps, + but one uses front left/right and the other uses side left/right, it makes logical + sense to just map these. The way we'll do it is we'll check if there is a logical + corresponding mapping, and if so, apply it, but we will *not* break from the loop, + thereby giving the loop a chance to find an exact match later which will take priority. + */ + switch (channelOut) + { + /* Left channels. */ + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_LEFT: + case MA_CHANNEL_SIDE_LEFT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + /* Right channels. */ + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + switch (channelIn) { + case MA_CHANNEL_FRONT_RIGHT: + case MA_CHANNEL_SIDE_RIGHT: + { + pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; + } break; + } + } break; + + default: break; + } + } + } + + return MA_SUCCESS; +} + + +static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; + pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; + pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; + } else { + pFramesOut[iChannelOut*3 + 0] = 0; + } pFramesOut[iChannelOut*3 + 1] = 0; + } pFramesOut[iChannelOut*3 + 2] = 0; + + pFramesOut += channelsOut*3; + pFramesIn += channelsIn*3; + } +} + +static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; + if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ + pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; + } else { + pFramesOut[iChannelOut] = 0; + } + } + + pFramesOut += channelsOut; + pFramesIn += channelsIn; + } +} + +static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) +{ + if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { + return MA_INVALID_ARGS; + } + + switch (format) + { + case ma_format_u8: + { + ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s16: + { + ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s24: + { + ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_s32: + { + ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + case ma_format_f32: + { + ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); + } break; + + default: return MA_INVALID_ARGS; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannelIn; + ma_uint32 accumulationCount; + + if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { + return MA_INVALID_ARGS; + } + + /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ + + /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ + accumulationCount = 0; + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { + accumulationCount += 1; + } + } + + if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + if (channelIn != MA_CHANNEL_NONE) { + accumulation += pFramesIn[iChannelIn]; + } + } + + pFramesOut[0] = accumulation / accumulationCount; + pFramesOut += 1; + pFramesIn += channelsIn; + } + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) +{ + ma_uint64 iFrame; + ma_uint32 iChannelOut; + + if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ + switch (monoExpansionMode) + { + case ma_mono_expansion_mode_average: + { + float weight; + ma_uint32 validChannelCount = 0; + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + validChannelCount += 1; + } + } + + weight = 1.0f / validChannelCount; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iChannelOut] = pFramesIn[0] * weight; + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + } break; + + case ma_mono_expansion_mode_stereo_only: + { + if (channelsOut >= 2) { + ma_uint32 iChannelLeft = (ma_uint32)-1; + ma_uint32 iChannelRight = (ma_uint32)-1; + + /* + We first need to find our stereo channels. We prefer front-left and front-right, but + if they're not available, we'll also try side-left and side-right. If neither are + available we'll fall through to the default case below. + */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_SIDE_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_SIDE_RIGHT) { + iChannelRight = iChannelOut; + } + } + + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut == MA_CHANNEL_FRONT_LEFT) { + iChannelLeft = iChannelOut; + } + if (channelOut == MA_CHANNEL_FRONT_RIGHT) { + iChannelRight = iChannelOut; + } + } + + + if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { + /* We found our stereo channels so we can duplicate the signal across those channels. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { + pFramesOut[iChannelOut] = pFramesIn[0]; + } else { + pFramesOut[iChannelOut] = 0.0f; + } + } + } + + pFramesOut += channelsOut; + pFramesIn += 1; + } + + break; /* Get out of the switch. */ + } else { + /* Fallthrough. Does not have left and right channels. */ + goto default_handler; + } + } else { + /* Fallthrough. Does not have stereo channels. */ + goto default_handler; + } + }; /* Fallthrough. See comments above. */ + + case ma_mono_expansion_mode_duplicate: + default: + { + default_handler: + { + if (channelsOut <= MA_MAX_CHANNELS) { + ma_bool32 hasEmptyChannel = MA_FALSE; + ma_channel channelPositions[MA_MAX_CHANNELS]; + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) { + hasEmptyChannel = MA_TRUE; + } + } + + if (hasEmptyChannel == MA_FALSE) { + /* + Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully + help the compiler with auto-vectorization.m + */ + if (channelsOut == 2) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) { + pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 6) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */ + ma_uint64 unrolledFrameCount = frameCount >> 1; + + for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { + __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); + __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); + + _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); + } + + /* Tail. */ + iFrame = unrolledFrameCount << 1; + goto generic_on_fastpath; + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) { + pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else if (channelsOut == 8) { + #if defined(MA_SUPPORT_SSE2) + if (ma_has_sse2()) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + __m128 in = _mm_set1_ps(pFramesIn[iFrame]); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in); + _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in); + } + } else + #endif + { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) { + pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } else { + iFrame = 0; + + #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */ + generic_on_fastpath: + #endif + { + for (; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Need to handle MA_CHANNEL_NONE. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } else { + /* Slow path. Too many channels to store on the stack. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + if (channelOut != MA_CHANNEL_NONE) { + pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame]; + } + } + } + } + } + } break; + } + + return MA_SUCCESS; +} + +static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) +{ + ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); + + /* Optimized Path: Passthrough */ + if (conversionPath == ma_channel_conversion_path_passthrough) { + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); + return; + } + + /* Special Path: Mono Output. */ + if (conversionPath == ma_channel_conversion_path_mono_out) { + ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); + return; + } + + /* Special Path: Mono Input. */ + if (conversionPath == ma_channel_conversion_path_mono_in) { + ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); + return; + } + + /* Getting here means we aren't running on an optimized conversion path. */ + if (channelsOut <= MA_MAX_CHANNELS) { + ma_result result; + + if (mode == ma_channel_mix_mode_simple) { + ma_channel shuffleTable[MA_MAX_CHANNELS]; + + result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); + if (result != MA_SUCCESS) { + return; + } + + result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); + if (result != MA_SUCCESS) { + return; + } + } else { + ma_uint32 iFrame; + ma_uint32 iChannelOut; + ma_uint32 iChannelIn; + float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ + + /* + If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to + fall back to a slower path because otherwise we'll run out of stack space. + */ + if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { + /* Pre-compute weights. */ + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + } + + iFrame = 0; + + /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */ + if (channelsOut == 8) { + /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */ + if (channelsIn == 2) { + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0]; + accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0]; + accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0]; + accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0]; + accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0]; + accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0]; + accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0]; + accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0]; + + accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1]; + accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1]; + accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1]; + accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1]; + accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1]; + accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1]; + accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1]; + accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1]; + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } else { + /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn]; + accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn]; + } + + pFramesOut[iFrame*8 + 0] = accumulation[0]; + pFramesOut[iFrame*8 + 1] = accumulation[1]; + pFramesOut[iFrame*8 + 2] = accumulation[2]; + pFramesOut[iFrame*8 + 3] = accumulation[3]; + pFramesOut[iFrame*8 + 4] = accumulation[4]; + pFramesOut[iFrame*8 + 5] = accumulation[5]; + pFramesOut[iFrame*8 + 6] = accumulation[6]; + pFramesOut[iFrame*8 + 7] = accumulation[7]; + } + } + } else if (channelsOut == 6) { + /* + When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll + expand our weights and do two frames at a time. + */ + for (; iFrame < frameCount; iFrame += 1) { + float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn]; + accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn]; + accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn]; + accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn]; + accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn]; + accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn]; + } + + pFramesOut[iFrame*6 + 0] = accumulation[0]; + pFramesOut[iFrame*6 + 1] = accumulation[1]; + pFramesOut[iFrame*6 + 2] = accumulation[2]; + pFramesOut[iFrame*6 + 3] = accumulation[3]; + pFramesOut[iFrame*6 + 4] = accumulation[4]; + pFramesOut[iFrame*6 + 5] = accumulation[5]; + } + } + + /* Leftover frames. */ + for (; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn]; + } + + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; + } + } + } else { + /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { + float accumulation = 0; + ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); + + for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { + ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); + accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); + } + + pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation; + } + } + } + } + } else { + /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); + } +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelMapInOffset; + size_t channelMapOutOffset; + size_t shuffleTableOffset; + size_t weightsOffset; +} ma_channel_converter_heap_layout; + +static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) +{ + return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); +} + +static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) +{ + ma_channel_conversion_path conversionPath; + + MA_ASSERT(pHeapLayout != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { + return MA_INVALID_ARGS; + } + + if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapIn != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; + } + + /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ + pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; + if (pConfig->pChannelMapOut != NULL) { + pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; + } + + /* Alignment for the next section. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ + conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + /* Shuffle table */ + pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_shuffle) { + pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; + } + + /* Weights */ + pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; + if (conversionPath == ma_channel_conversion_path_weights) { + pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; + pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) +{ + ma_result result; + ma_channel_converter_heap_layout heapLayout; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pConverter); + + result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); + + pConverter->format = pConfig->format; + pConverter->channelsIn = pConfig->channelsIn; + pConverter->channelsOut = pConfig->channelsOut; + pConverter->mixingMode = pConfig->mixingMode; + + if (pConfig->pChannelMapIn != NULL) { + pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); + } else { + pConverter->pChannelMapIn = NULL; /* Use default channel map. */ + } + + if (pConfig->pChannelMapOut != NULL) { + pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); + ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); + } else { + pConverter->pChannelMapOut = NULL; /* Use default channel map. */ + } + + pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); + + if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { + pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); + ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); + } + + if (pConverter->conversionPath == ma_channel_conversion_path_weights) { + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); + } + } else { + pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); + } + } + + /* Silence our weights by default. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = 0; + } + } + } + + /* + We now need to fill out our weights table. This is determined by the mixing mode. + */ + + /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (channelPosIn == channelPosOut) { + float weight = 1; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + + switch (pConverter->mixingMode) + { + case ma_channel_mix_mode_custom_weights: + { + if (pConfig->ppWeights == NULL) { + return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ + } + + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { + float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; + + if (pConverter->format == ma_format_f32) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } else { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } break; + + case ma_channel_mix_mode_simple: + { + /* + In simple mode, only set weights for channels that have exactly matching types, leave the rest at + zero. The 1:1 mappings have already been covered before this switch statement. + */ + } break; + + case ma_channel_mix_mode_rectangular: + default: + { + /* Unmapped input channels. */ + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + if (ma_is_spatial_channel_position(channelPosIn)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (ma_is_spatial_channel_position(channelPosOut)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); + } + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + } + } + } + + /* Unmapped output channels. */ + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); + + if (ma_is_spatial_channel_position(channelPosOut)) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + + if (ma_is_spatial_channel_position(channelPosIn)) { + float weight = 0; + if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { + weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); + } + + /* Only apply the weight if we haven't already got some contribution from the respective channels. */ + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOut] = weight; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); + } + } + } + } + } + } + } + + /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ + if (pConfig->calculateLFEFromSpatialChannels) { + if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { + ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); + ma_uint32 iChannelOutLFE; + + if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { + const float weightForLFE = 1.0f / spatialChannelCount; + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); + if (ma_is_spatial_channel_position(channelPosIn)) { + if (pConverter->format == ma_format_f32) { + if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; + } + } else { + if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { + pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); + } + } + } + } + } + } + } + } break; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pConverter == NULL) { + return; + } + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); + } +} + +static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + + ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); + + return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); +} + +static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsIn == 1); + + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; + } + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + if (pConverter->channelsOut == 2) { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; + pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; + } + } else { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; + } + } + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; + ma_uint64 iSampleIn = iFrame; + pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; + pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; + pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; + } + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; + } + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + if (pConverter->channelsOut == 2) { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; + pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; + } + } else { + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { + pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; + } + } + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + MA_ASSERT(pConverter->channelsOut == 1); + + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); + } + + pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int32 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); + } + + ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + ma_int64 t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; ++iFrame) { + float t = 0; + for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { + t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; + } + + pFramesOutF32[iFrame] = t / pConverter->channelsIn; + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + ma_uint32 iFrame; + ma_uint32 iChannelIn; + ma_uint32 iChannelOut; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pFramesOut != NULL); + MA_ASSERT(pFramesIn != NULL); + + /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ + + /* Clear. */ + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + + /* Accumulate. */ + switch (pConverter->format) + { + case ma_format_u8: + { + /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); + ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); + ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); + pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); + } + } + } + } break; + + case ma_format_s16: + { + /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; + const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; + s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; + + pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); + } + } + } + } break; + + case ma_format_s24: + { + /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; + const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); + ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); + ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); + ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); + } + } + } + } break; + + case ma_format_s32: + { + /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; + const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; + s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; + + pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); + } + } + } + } break; + + case ma_format_f32: + { + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + const float* pFramesInF32 = (const float*)pFramesIn; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { + for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { + pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; + } + } + } + } break; + + default: return MA_INVALID_OPERATION; /* Unknown format. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesIn == NULL) { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); + return MA_SUCCESS; + } + + switch (pConverter->conversionPath) + { + case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); + case ma_channel_conversion_path_weights: + default: + { + return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); + } + } +} + +MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); + + return MA_SUCCESS; +} + +MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); + + return MA_SUCCESS; +} + + +/************************************************************************************************************************************************************** + +Data Conversion + +**************************************************************************************************************************************************************/ +MA_API ma_data_converter_config ma_data_converter_config_init_default(void) +{ + ma_data_converter_config config; + MA_ZERO_OBJECT(&config); + + config.ditherMode = ma_dither_mode_none; + config.resampling.algorithm = ma_resample_algorithm_linear; + config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ + + /* Linear resampling defaults. */ + config.resampling.linear.lpfOrder = 1; + + return config; +} + +MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + ma_data_converter_config config = ma_data_converter_config_init_default(); + config.formatIn = formatIn; + config.formatOut = formatOut; + config.channelsIn = channelsIn; + config.channelsOut = channelsOut; + config.sampleRateIn = sampleRateIn; + config.sampleRateOut = sampleRateOut; + + return config; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t channelConverterOffset; + size_t resamplerOffset; +} ma_data_converter_heap_layout; + +static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; +} + +static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) +{ + MA_ASSERT(pConfig != NULL); + + /* + We want to avoid as much data conversion as possible. The channel converter and linear + resampler both support s16 and f32 natively. We need to decide on the format to use for this + stage. We call this the mid format because it's used in the middle stage of the conversion + pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it + will do the same thing for the input format. If it's neither we just use f32. If we are using a + custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced + to use that if resampling is required. + */ + if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { + return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ + } else { + /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { + return pConfig->formatOut; + } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { + return pConfig->formatIn; + } else { + return ma_format_f32; + } + } +} + +static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_channel_converter_config channelConverterConfig; + + MA_ASSERT(pConfig != NULL); + + channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); + channelConverterConfig.ppWeights = pConfig->ppChannelWeights; + channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; + + return channelConverterConfig; +} + +static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) +{ + ma_resampler_config resamplerConfig; + ma_uint32 resamplerChannels; + + MA_ASSERT(pConfig != NULL); + + /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ + if (pConfig->channelsIn < pConfig->channelsOut) { + resamplerChannels = pConfig->channelsIn; + } else { + resamplerChannels = pConfig->channelsOut; + } + + resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); + resamplerConfig.linear = pConfig->resampling.linear; + resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; + resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; + + return resamplerConfig; +} + +static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) +{ + ma_result result; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Channel converter. */ + pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; + { + size_t heapSizeInBytes; + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); + + result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Resampler. */ + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + if (ma_data_converter_config_is_resampler_required(pConfig)) { + size_t heapSizeInBytes; + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); + + result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes += heapSizeInBytes; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) +{ + ma_result result; + ma_data_converter_heap_layout heapLayout; + ma_format midFormat; + ma_bool32 isResamplingRequired; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pConverter); + + result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pConverter->formatIn = pConfig->formatIn; + pConverter->formatOut = pConfig->formatOut; + pConverter->channelsIn = pConfig->channelsIn; + pConverter->channelsOut = pConfig->channelsOut; + pConverter->sampleRateIn = pConfig->sampleRateIn; + pConverter->sampleRateOut = pConfig->sampleRateOut; + pConverter->ditherMode = pConfig->ditherMode; + + /* + Determine if resampling is required. We need to do this so we can determine an appropriate + mid format to use. If resampling is required, the mid format must be ma_format_f32 since + that is the only one that is guaranteed to supported by custom resampling backends. + */ + isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); + midFormat = ma_data_converter_config_get_mid_format(pConfig); + + + /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ + { + ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); + + result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); + if (result != MA_SUCCESS) { + return result; + } + + /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ + if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { + pConverter->hasChannelConverter = MA_TRUE; + } + } + + + /* Resampler. */ + if (isResamplingRequired) { + ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); + + result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); + if (result != MA_SUCCESS) { + return result; + } + + pConverter->hasResampler = MA_TRUE; + } + + + /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ + if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { + /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ + if (pConverter->formatIn == pConverter->formatOut) { + /* The formats are the same so we can just pass through. */ + pConverter->hasPreFormatConversion = MA_FALSE; + pConverter->hasPostFormatConversion = MA_FALSE; + } else { + /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ + pConverter->hasPreFormatConversion = MA_FALSE; + pConverter->hasPostFormatConversion = MA_TRUE; + } + } else { + /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ + if (pConverter->formatIn != midFormat) { + pConverter->hasPreFormatConversion = MA_TRUE; + } + if (pConverter->formatOut != midFormat) { + pConverter->hasPostFormatConversion = MA_TRUE; + } + } + + /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ + if (pConverter->hasPreFormatConversion == MA_FALSE && + pConverter->hasPostFormatConversion == MA_FALSE && + pConverter->hasChannelConverter == MA_FALSE && + pConverter->hasResampler == MA_FALSE) { + pConverter->isPassthrough = MA_TRUE; + } + + + /* We now need to determine our execution path. */ + if (pConverter->isPassthrough) { + pConverter->executionPath = ma_data_converter_execution_path_passthrough; + } else { + if (pConverter->channelsIn < pConverter->channelsOut) { + /* Do resampling first, if necessary. */ + MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); + + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Do channel conversion first, if necessary. */ + if (pConverter->hasChannelConverter) { + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_channels_first; + } else { + pConverter->executionPath = ma_data_converter_execution_path_channels_only; + } + } else { + /* Channel routing not required. */ + if (pConverter->hasResampler) { + pConverter->executionPath = ma_data_converter_execution_path_resample_only; + } else { + pConverter->executionPath = ma_data_converter_execution_path_format_only; + } + } + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pConverter->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pConverter == NULL) { + return; + } + + if (pConverter->hasResampler) { + ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); + } + + ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); + + if (pConverter->_ownsHeap) { + ma_free(pConverter->_pHeap, pAllocationCallbacks); + } +} + +static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pFramesOut != NULL) { + if (pFramesIn != NULL) { + ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pFramesOut != NULL) { + if (pFramesIn != NULL) { + ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); + } else { + ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + + +static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result = MA_SUCCESS; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + while (framesProcessedOut < frameCountOut) { + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + const void* pFramesInThisIteration; + /* */ void* pFramesOutThisIteration; + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + + if (pFramesIn != NULL) { + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } else { + pFramesInThisIteration = NULL; + } + + if (pFramesOut != NULL) { + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + pFramesOutThisIteration = NULL; + } + + /* Do a pre format conversion if necessary. */ + if (pConverter->hasPreFormatConversion) { + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountInThisIteration > tempBufferOutCap) { + frameCountInThisIteration = tempBufferOutCap; + } + } + + if (pFramesInThisIteration != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + } else { + MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); + } + + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + + if (pConverter->hasPostFormatConversion) { + /* Both input and output conversion required. Output to the temp buffer. */ + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); + } else { + /* Only pre-format required. Output straight to the output buffer. */ + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); + } + + if (result != MA_SUCCESS) { + break; + } + } else { + /* No pre-format required. Just read straight from the input buffer. */ + MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); + + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + break; + } + } + + /* If we are doing a post format conversion we need to do that now. */ + if (pConverter->hasPostFormatConversion) { + if (pFramesOutThisIteration != NULL) { + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); + } + } + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return result; +} + +static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + MA_ASSERT(pConverter != NULL); + + if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { + /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ + return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } else { + /* Format conversion required. */ + return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + } +} + +static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 frameCount; + + MA_ASSERT(pConverter != NULL); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + frameCount = ma_min(frameCountIn, frameCountOut); + + if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { + /* No format conversion required. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); + if (result != MA_SUCCESS) { + return result; + } + } else { + /* Format conversion required. */ + ma_uint64 framesProcessed = 0; + + while (framesProcessed < frameCount) { + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + const void* pFramesInThisIteration; + /* */ void* pFramesOutThisIteration; + ma_uint64 frameCountThisIteration; + + if (pFramesIn != NULL) { + pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } else { + pFramesInThisIteration = NULL; + } + + if (pFramesOut != NULL) { + pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } else { + pFramesOutThisIteration = NULL; + } + + /* Do a pre format conversion if necessary. */ + if (pConverter->hasPreFormatConversion) { + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); + + frameCountThisIteration = (frameCount - framesProcessed); + if (frameCountThisIteration > tempBufferInCap) { + frameCountThisIteration = tempBufferInCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountThisIteration > tempBufferOutCap) { + frameCountThisIteration = tempBufferOutCap; + } + } + + if (pFramesInThisIteration != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); + } else { + MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); + } + + if (pConverter->hasPostFormatConversion) { + /* Both input and output conversion required. Output to the temp buffer. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); + } else { + /* Only pre-format required. Output straight to the output buffer. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); + } + + if (result != MA_SUCCESS) { + break; + } + } else { + /* No pre-format required. Just read straight from the input buffer. */ + MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); + + frameCountThisIteration = (frameCount - framesProcessed); + if (frameCountThisIteration > tempBufferOutCap) { + frameCountThisIteration = tempBufferOutCap; + } + + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); + if (result != MA_SUCCESS) { + break; + } + } + + /* If we are doing a post format conversion we need to do that now. */ + if (pConverter->hasPostFormatConversion) { + if (pFramesOutThisIteration != NULL) { + ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); + } + } + + framesProcessed += frameCountThisIteration; + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = frameCount; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = frameCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ + ma_uint64 tempBufferInCap; + ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ + ma_uint64 tempBufferMidCap; + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ + ma_uint64 tempBufferOutCap; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); + MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + + while (framesProcessedOut < frameCountOut) { + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + const void* pRunningFramesIn = NULL; + void* pRunningFramesOut = NULL; + const void* pResampleBufferIn; + void* pChannelsBufferOut; + + if (pFramesIn != NULL) { + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } + if (pFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + + /* Run input data through the resampler and output it to the temporary buffer. */ + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + + if (pConverter->hasPreFormatConversion) { + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + } + + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferMidCap) { + frameCountOutThisIteration = tempBufferMidCap; + } + + /* We can't read more frames than can fit in the output buffer. */ + if (pConverter->hasPostFormatConversion) { + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + } + + /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ + + /* + We need to try to predict how many input frames will be required for the resampler. If the + resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further + off we are from this, the more wasted format conversions we'll end up doing. + */ + #if 1 + { + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + + if (frameCountInThisIteration > requiredInputFrameCount) { + frameCountInThisIteration = requiredInputFrameCount; + } + } + #endif + + if (pConverter->hasPreFormatConversion) { + if (pFramesIn != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + pResampleBufferIn = pTempBufferIn; + } else { + pResampleBufferIn = NULL; + } + } else { + pResampleBufferIn = pRunningFramesIn; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* + The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do + this part if we have an output buffer. + */ + if (pFramesOut != NULL) { + if (pConverter->hasPostFormatConversion) { + pChannelsBufferOut = pTempBufferOut; + } else { + pChannelsBufferOut = pRunningFramesOut; + } + + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + /* Finally we do post format conversion. */ + if (pConverter->hasPostFormatConversion) { + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); + } + } + + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return MA_SUCCESS; +} + +static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + ma_result result; + ma_uint64 frameCountIn; + ma_uint64 frameCountOut; + ma_uint64 framesProcessedIn; + ma_uint64 framesProcessedOut; + ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ + ma_uint64 tempBufferInCap; + ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ + ma_uint64 tempBufferMidCap; + ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ + ma_uint64 tempBufferOutCap; + + MA_ASSERT(pConverter != NULL); + MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); + MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); + MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); + + frameCountIn = 0; + if (pFrameCountIn != NULL) { + frameCountIn = *pFrameCountIn; + } + + frameCountOut = 0; + if (pFrameCountOut != NULL) { + frameCountOut = *pFrameCountOut; + } + + framesProcessedIn = 0; + framesProcessedOut = 0; + + tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); + tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); + tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); + + while (framesProcessedOut < frameCountOut) { + ma_uint64 frameCountInThisIteration; + ma_uint64 frameCountOutThisIteration; + const void* pRunningFramesIn = NULL; + void* pRunningFramesOut = NULL; + const void* pChannelsBufferIn; + void* pResampleBufferOut; + + if (pFramesIn != NULL) { + pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); + } + if (pFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); + } + + /* + Before doing any processing we need to determine how many frames we should try processing + this iteration, for both input and output. The resampler requires us to perform format and + channel conversion before passing any data into it. If we get our input count wrong, we'll + end up peforming redundant pre-processing. This isn't the end of the world, but it does + result in some inefficiencies proportionate to how far our estimates are off. + + If the resampler has a means to calculate exactly how much we'll need, we'll use that. + Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output + frame count first. + */ + frameCountOutThisIteration = (frameCountOut - framesProcessedOut); + if (frameCountOutThisIteration > tempBufferMidCap) { + frameCountOutThisIteration = tempBufferMidCap; + } + + if (pConverter->hasPostFormatConversion) { + if (frameCountOutThisIteration > tempBufferOutCap) { + frameCountOutThisIteration = tempBufferOutCap; + } + } + + /* Now that we have the output frame count we can determine the input frame count. */ + frameCountInThisIteration = (frameCountIn - framesProcessedIn); + if (pConverter->hasPreFormatConversion) { + if (frameCountInThisIteration > tempBufferInCap) { + frameCountInThisIteration = tempBufferInCap; + } + } + + if (frameCountInThisIteration > tempBufferMidCap) { + frameCountInThisIteration = tempBufferMidCap; + } + + #if 1 + { + ma_uint64 requiredInputFrameCount; + + result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); + if (result != MA_SUCCESS) { + /* Fall back to a best guess. */ + requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; + } + + if (frameCountInThisIteration > requiredInputFrameCount) { + frameCountInThisIteration = requiredInputFrameCount; + } + } + #endif + + + /* Pre format conversion. */ + if (pConverter->hasPreFormatConversion) { + if (pRunningFramesIn != NULL) { + ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); + pChannelsBufferIn = pTempBufferIn; + } else { + pChannelsBufferIn = NULL; + } + } else { + pChannelsBufferIn = pRunningFramesIn; + } + + + /* Channel conversion. */ + result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* Resampling. */ + if (pConverter->hasPostFormatConversion) { + pResampleBufferOut = pTempBufferOut; + } else { + pResampleBufferOut = pRunningFramesOut; + } + + result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); + if (result != MA_SUCCESS) { + return result; + } + + + /* Post format conversion. */ + if (pConverter->hasPostFormatConversion) { + if (pRunningFramesOut != NULL) { + ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); + } + } + + + framesProcessedIn += frameCountInThisIteration; + framesProcessedOut += frameCountOutThisIteration; + + MA_ASSERT(framesProcessedIn <= frameCountIn); + MA_ASSERT(framesProcessedOut <= frameCountOut); + + if (frameCountOutThisIteration == 0) { + break; /* Consumed all of our input data. */ + } + } + + if (pFrameCountIn != NULL) { + *pFrameCountIn = framesProcessedIn; + } + if (pFrameCountOut != NULL) { + *pFrameCountOut = framesProcessedOut; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + switch (pConverter->executionPath) + { + case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); + default: return MA_INVALID_OPERATION; /* Should never hit this. */ + } +} + +MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler == MA_FALSE) { + return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ + } + + return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); +} + +MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler == MA_FALSE) { + return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ + } + + return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); +} + +MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return 0; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_input_latency(&pConverter->resampler); + } + + return 0; /* No latency without a resampler. */ +} + +MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return 0; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_output_latency(&pConverter->resampler); + } + + return 0; /* No latency without a resampler. */ +} + +MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) +{ + if (pInputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pInputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); + } else { + *pInputFrameCount = outputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) +{ + if (pOutputFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + *pOutputFrameCount = 0; + + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasResampler) { + return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); + } else { + *pOutputFrameCount = inputFrameCount; /* 1:1 */ + return MA_SUCCESS; + } +} + +MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pConverter == NULL || pChannelMap == NULL) { + return MA_INVALID_ARGS; + } + + if (pConverter->hasChannelConverter) { + ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) +{ + if (pConverter == NULL) { + return MA_INVALID_ARGS; + } + + /* There's nothing to do if we're not resampling. */ + if (pConverter->hasResampler == MA_FALSE) { + return MA_SUCCESS; + } + + return ma_resampler_reset(&pConverter->resampler); +} + + + +/************************************************************************************************************************************************************** + +Channel Maps + +**************************************************************************************************************************************************************/ +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); + +MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (pChannelMap == NULL) { + return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); + } else { + if (channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + return pChannelMap[channelIndex]; + } +} + +MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) +{ + if (pChannelMap == NULL) { + return; + } + + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); +} + + +static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (channelCount == 0 || channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: /* No defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP + /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_CENTER; + #else + /* Quad. */ + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + #endif + } + } break; + + case 5: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_SIDE_LEFT; + case 5: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 7: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_CENTER; + case 5: return MA_CHANNEL_SIDE_LEFT; + case 6: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_CENTER; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_SIDE_LEFT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_FRONT_RIGHT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + } + } break; + } + + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_CENTER; + case 5: return MA_CHANNEL_SIDE_LEFT; + case 6: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_LFE; + case 4: return MA_CHANNEL_BACK_LEFT; + case 5: return MA_CHANNEL_BACK_RIGHT; + case 6: return MA_CHANNEL_SIDE_LEFT; + case 7: return MA_CHANNEL_SIDE_RIGHT; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 6: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_BACK_LEFT; + case 4: return MA_CHANNEL_BACK_RIGHT; + case 5: return MA_CHANNEL_LFE; + } + } break; + + case 7: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_CENTER; + case 6: return MA_CHANNEL_LFE; + } + } break; + + case 8: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_CENTER; + case 2: return MA_CHANNEL_FRONT_RIGHT; + case 3: return MA_CHANNEL_SIDE_LEFT; + case 4: return MA_CHANNEL_SIDE_RIGHT; + case 5: return MA_CHANNEL_BACK_LEFT; + case 6: return MA_CHANNEL_BACK_RIGHT; + case 7: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 8) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + +static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) +{ + switch (channelCount) + { + case 0: return MA_CHANNEL_NONE; + + case 1: + { + return MA_CHANNEL_MONO; + } break; + + case 2: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + } + } break; + + case 3: /* No defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 4: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + } + } break; + + case 5: /* Not defined, but best guess. */ + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + } + } break; + + case 6: + default: + { + switch (channelIndex) { + case 0: return MA_CHANNEL_FRONT_LEFT; + case 1: return MA_CHANNEL_FRONT_RIGHT; + case 2: return MA_CHANNEL_BACK_LEFT; + case 3: return MA_CHANNEL_BACK_RIGHT; + case 4: return MA_CHANNEL_FRONT_CENTER; + case 5: return MA_CHANNEL_LFE; + } + } break; + } + + if (channelCount > 6) { + if (channelIndex < 32) { /* We have 32 AUX channels. */ + return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); + } + } + + /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ + return MA_CHANNEL_NONE; +} + + +static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) +{ + if (channelCount == 0 || channelIndex >= channelCount) { + return MA_CHANNEL_NONE; + } + + switch (standardChannelMap) + { + case ma_standard_channel_map_alsa: + { + return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_rfc3551: + { + return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_flac: + { + return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_vorbis: + { + return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_sound4: + { + return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_sndio: + { + return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); + } break; + + case ma_standard_channel_map_microsoft: /* Also default. */ + /*case ma_standard_channel_map_default;*/ + default: + { + return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); + } break; + } +} + +MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) +{ + ma_uint32 iChannel; + + if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { + return; + } + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + if (channelMapCap == 0) { + break; /* Ran out of room. */ + } + + pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); + pChannelMap += 1; + channelMapCap -= 1; + } +} + +MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) +{ + if (pOut != NULL && pIn != NULL && channels > 0) { + MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); + } +} + +MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) +{ + if (pOut == NULL || channels == 0) { + return; + } + + if (pIn != NULL) { + ma_channel_map_copy(pOut, pIn, channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); + } +} + +MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) +{ + /* A channel count of 0 is invalid. */ + if (channels == 0) { + return MA_FALSE; + } + + /* It does not make sense to have a mono channel when there is more than 1 channel. */ + if (channels > 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { + return MA_FALSE; + } + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) +{ + ma_uint32 iChannel; + + if (pChannelMapA == pChannelMapB) { + return MA_TRUE; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) +{ + ma_uint32 iChannel; + + /* A null channel map is equivalent to the default channel map. */ + if (pChannelMap == NULL) { + return MA_FALSE; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { + return MA_FALSE; + } + } + + return MA_TRUE; +} + +MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) +{ + return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); +} + +MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) +{ + ma_uint32 iChannel; + + if (pChannelIndex != NULL) { + *pChannelIndex = (ma_uint32)-1; + } + + for (iChannel = 0; iChannel < channels; ++iChannel) { + if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { + if (pChannelIndex != NULL) { + *pChannelIndex = iChannel; + } + + return MA_TRUE; + } + } + + /* Getting here means the channel position was not found. */ + return MA_FALSE; +} + +MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) +{ + size_t len; + ma_uint32 iChannel; + + len = 0; + + for (iChannel = 0; iChannel < channels; iChannel += 1) { + const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); + size_t channelStrLen = strlen(pChannelStr); + + /* Append the string if necessary. */ + if (pBufferOut != NULL && bufferCap > len + channelStrLen) { + MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); + } + len += channelStrLen; + + /* Append a space if it's not the last item. */ + if (iChannel+1 < channels) { + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = ' '; + } + len += 1; + } + } + + /* Null terminate. Don't increment the length here. */ + if (pBufferOut != NULL && bufferCap > len + 1) { + pBufferOut[len] = '\0'; + } + + return len; +} + +MA_API const char* ma_channel_position_to_string(ma_channel channel) +{ + switch (channel) + { + case MA_CHANNEL_NONE : return "CHANNEL_NONE"; + case MA_CHANNEL_MONO : return "CHANNEL_MONO"; + case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; + case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; + case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; + case MA_CHANNEL_LFE : return "CHANNEL_LFE"; + case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; + case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; + case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; + case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; + case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; + case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; + case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; + case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; + case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; + case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; + case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; + case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; + case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; + case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; + case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; + case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; + case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; + case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; + case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; + case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; + case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; + case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; + case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; + case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; + case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; + case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; + case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; + case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; + case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; + case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; + case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; + case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; + case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; + case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; + case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; + case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; + case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; + case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; + case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; + case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; + case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; + case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; + case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; + case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; + case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; + case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; + default: break; + } + + return "UNKNOWN"; +} + + + +/************************************************************************************************************************************************************** + +Conversion Helpers + +**************************************************************************************************************************************************************/ +MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) +{ + ma_data_converter_config config; + + config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); + config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); + + return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); +} + +MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) +{ + ma_result result; + ma_data_converter converter; + + if (frameCountIn == 0 || pConfig == NULL) { + return 0; + } + + result = ma_data_converter_init(pConfig, NULL, &converter); + if (result != MA_SUCCESS) { + return 0; /* Failed to initialize the data converter. */ + } + + if (pOut == NULL) { + result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); + if (result != MA_SUCCESS) { + if (result == MA_NOT_IMPLEMENTED) { + /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ + frameCountOut = 0; + + while (frameCountIn > 0) { + ma_uint64 framesProcessedIn = frameCountIn; + ma_uint64 framesProcessedOut = 0xFFFFFFFF; + + result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); + if (result != MA_SUCCESS) { + break; + } + + frameCountIn -= framesProcessedIn; + } + } + } + } else { + result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); + if (result != MA_SUCCESS) { + frameCountOut = 0; + } + } + + ma_data_converter_uninit(&converter, NULL); + return frameCountOut; +} + + +/************************************************************************************************************************************************************** + +Ring Buffer + +**************************************************************************************************************************************************************/ +static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) +{ + return encodedOffset & 0x7FFFFFFF; +} + +static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) +{ + return encodedOffset & 0x80000000; +} + +static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); +} + +static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); +} + +static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) +{ + return offsetLoopFlag | offsetInBytes; +} + +static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) +{ + MA_ASSERT(pOffsetInBytes != NULL); + MA_ASSERT(pOffsetLoopFlag != NULL); + + *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); + *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); +} + + +MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) +{ + ma_result result; + const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + if (subbufferSizeInBytes == 0 || subbufferCount == 0) { + return MA_INVALID_ARGS; + } + + if (subbufferSizeInBytes > maxSubBufferSize) { + return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ + } + + + MA_ZERO_OBJECT(pRB); + + result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; + pRB->subbufferCount = (ma_uint32)subbufferCount; + + if (pOptionalPreallocatedBuffer != NULL) { + pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; + pRB->pBuffer = pOptionalPreallocatedBuffer; + } else { + size_t bufferSizeInBytes; + + /* + Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this + we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. + */ + pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; + + bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; + pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); + if (pRB->pBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); + pRB->ownsBuffer = MA_TRUE; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) +{ + return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); +} + +MA_API void ma_rb_uninit(ma_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + if (pRB->ownsBuffer) { + ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); + } +} + +MA_API void ma_rb_reset(ma_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); +} + +MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) +{ + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + size_t bytesAvailable; + size_t bytesRequested; + + if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { + return MA_INVALID_ARGS; + } + + /* The returned buffer should never move ahead of the write pointer. */ + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + /* + The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we + can only read up to the write pointer. If not, we can only read up to the end of the buffer. + */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + bytesAvailable = writeOffsetInBytes - readOffsetInBytes; + } else { + bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; + } + + bytesRequested = *pSizeInBytes; + if (bytesRequested > bytesAvailable) { + bytesRequested = bytesAvailable; + } + + *pSizeInBytes = bytesRequested; + (*ppBufferOut) = ma_rb__get_read_ptr(pRB); + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 newReadOffsetInBytes; + ma_uint32 newReadOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); + if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ + } + + /* Move the read pointer back to the start if necessary. */ + newReadOffsetLoopFlag = readOffsetLoopFlag; + if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { + newReadOffsetInBytes = 0; + newReadOffsetLoopFlag ^= 0x80000000; + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); + + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } +} + +MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + size_t bytesAvailable; + size_t bytesRequested; + + if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { + return MA_INVALID_ARGS; + } + + /* The returned buffer should never overtake the read buffer. */ + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + /* + In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only + write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should + never overtake the read pointer. + */ + if (writeOffsetLoopFlag == readOffsetLoopFlag) { + bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; + } else { + bytesAvailable = readOffsetInBytes - writeOffsetInBytes; + } + + bytesRequested = *pSizeInBytes; + if (bytesRequested > bytesAvailable) { + bytesRequested = bytesAvailable; + } + + *pSizeInBytes = bytesRequested; + *ppBufferOut = ma_rb__get_write_ptr(pRB); + + /* Clear the buffer if desired. */ + if (pRB->clearOnWriteAcquire) { + MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) +{ + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newWriteOffsetInBytes; + ma_uint32 newWriteOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); + if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ + } + + /* Move the read pointer back to the start if necessary. */ + newWriteOffsetLoopFlag = writeOffsetLoopFlag; + if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { + newWriteOffsetInBytes = 0; + newWriteOffsetLoopFlag ^= 0x80000000; + } + + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); + + if (ma_rb_pointer_distance(pRB) == 0) { + return MA_AT_END; + } else { + return MA_SUCCESS; + } +} + +MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newReadOffsetInBytes; + ma_uint32 newReadOffsetLoopFlag; + + if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + newReadOffsetLoopFlag = readOffsetLoopFlag; + + /* We cannot go past the write buffer. */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { + newReadOffsetInBytes = writeOffsetInBytes; + } else { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); + } + } else { + /* May end up looping. */ + if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; + newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ + } else { + newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); + } + } + + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + return MA_SUCCESS; +} + +MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + ma_uint32 newWriteOffsetInBytes; + ma_uint32 newWriteOffsetLoopFlag; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + newWriteOffsetLoopFlag = writeOffsetLoopFlag; + + /* We cannot go past the write buffer. */ + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + /* May end up looping. */ + if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; + newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ + } else { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); + } + } else { + if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { + newWriteOffsetInBytes = readOffsetInBytes; + } else { + newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); + } + } + + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + return MA_SUCCESS; +} + +MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) +{ + ma_uint32 readOffset; + ma_uint32 readOffsetInBytes; + ma_uint32 readOffsetLoopFlag; + ma_uint32 writeOffset; + ma_uint32 writeOffsetInBytes; + ma_uint32 writeOffsetLoopFlag; + + if (pRB == NULL) { + return 0; + } + + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); + ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); + + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); + ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); + + if (readOffsetLoopFlag == writeOffsetLoopFlag) { + return writeOffsetInBytes - readOffsetInBytes; + } else { + return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); + } +} + +MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) +{ + ma_int32 dist; + + if (pRB == NULL) { + return 0; + } + + dist = ma_rb_pointer_distance(pRB); + if (dist < 0) { + return 0; + } + + return dist; +} + +MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); +} + +MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->subbufferSizeInBytes; +} + +MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + if (pRB->subbufferStrideInBytes == 0) { + return (size_t)pRB->subbufferSizeInBytes; + } + + return (size_t)pRB->subbufferStrideInBytes; +} + +MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) +{ + if (pRB == NULL) { + return 0; + } + + return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); +} + +MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) +{ + if (pRB == NULL) { + return NULL; + } + + return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); +} + + + +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + +static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) +{ + MA_ASSERT(pRB != NULL); + + return ma_get_bytes_per_frame(pRB->format, pRB->channels); +} + +MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) +{ + ma_uint32 bpf; + ma_result result; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pRB); + + bpf = ma_get_bytes_per_frame(format, channels); + if (bpf == 0) { + return MA_INVALID_ARGS; + } + + result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); + if (result != MA_SUCCESS) { + return result; + } + + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) +{ + return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); +} + +MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_data_source_uninit(&pRB->ds); + ma_rb_uninit(&pRB->rb); +} + +MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return; + } + + ma_rb_reset(&pRB->rb); +} + +MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) +{ + size_t sizeInBytes; + ma_result result; + + if (pRB == NULL || pSizeInFrames == NULL) { + return MA_INVALID_ARGS; + } + + sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); + + result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); + if (result != MA_SUCCESS) { + return result; + } + + *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) +{ + size_t sizeInBytes; + ma_result result; + + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); + + result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); + if (result != MA_SUCCESS) { + return result; + } + + *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); + return MA_SUCCESS; +} + +MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) +{ + if (pRB == NULL) { + return MA_INVALID_ARGS; + } + + return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) +{ + if (pRB == NULL) { + return 0; + } + + return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); +} + +MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) +{ + if (pRB == NULL) { + return NULL; + } + + return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); +} + +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + + + +MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) +{ + ma_result result; + ma_uint32 sizeInFrames; + + sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); + if (sizeInFrames == 0) { + return MA_INVALID_ARGS; + } + + result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); + if (result != MA_SUCCESS) { + return result; + } + + /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ + ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); + + return MA_SUCCESS; +} + +MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) +{ + ma_pcm_rb_uninit((ma_pcm_rb*)pRB); + return MA_SUCCESS; +} + + + +/************************************************************************************************************************************************************** + +Miscellaneous Helpers + +**************************************************************************************************************************************************************/ +MA_API const char* ma_result_description(ma_result result) +{ + switch (result) + { + case MA_SUCCESS: return "No error"; + case MA_ERROR: return "Unknown error"; + case MA_INVALID_ARGS: return "Invalid argument"; + case MA_INVALID_OPERATION: return "Invalid operation"; + case MA_OUT_OF_MEMORY: return "Out of memory"; + case MA_OUT_OF_RANGE: return "Out of range"; + case MA_ACCESS_DENIED: return "Permission denied"; + case MA_DOES_NOT_EXIST: return "Resource does not exist"; + case MA_ALREADY_EXISTS: return "Resource already exists"; + case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; + case MA_INVALID_FILE: return "Invalid file"; + case MA_TOO_BIG: return "Too large"; + case MA_PATH_TOO_LONG: return "Path too long"; + case MA_NAME_TOO_LONG: return "Name too long"; + case MA_NOT_DIRECTORY: return "Not a directory"; + case MA_IS_DIRECTORY: return "Is a directory"; + case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; + case MA_AT_END: return "At end"; + case MA_NO_SPACE: return "No space available"; + case MA_BUSY: return "Device or resource busy"; + case MA_IO_ERROR: return "Input/output error"; + case MA_INTERRUPT: return "Interrupted"; + case MA_UNAVAILABLE: return "Resource unavailable"; + case MA_ALREADY_IN_USE: return "Resource already in use"; + case MA_BAD_ADDRESS: return "Bad address"; + case MA_BAD_SEEK: return "Illegal seek"; + case MA_BAD_PIPE: return "Broken pipe"; + case MA_DEADLOCK: return "Deadlock"; + case MA_TOO_MANY_LINKS: return "Too many links"; + case MA_NOT_IMPLEMENTED: return "Not implemented"; + case MA_NO_MESSAGE: return "No message of desired type"; + case MA_BAD_MESSAGE: return "Invalid message"; + case MA_NO_DATA_AVAILABLE: return "No data available"; + case MA_INVALID_DATA: return "Invalid data"; + case MA_TIMEOUT: return "Timeout"; + case MA_NO_NETWORK: return "Network unavailable"; + case MA_NOT_UNIQUE: return "Not unique"; + case MA_NOT_SOCKET: return "Socket operation on non-socket"; + case MA_NO_ADDRESS: return "Destination address required"; + case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; + case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; + case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; + case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; + case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; + case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; + case MA_CONNECTION_RESET: return "Connection reset"; + case MA_ALREADY_CONNECTED: return "Already connected"; + case MA_NOT_CONNECTED: return "Not connected"; + case MA_CONNECTION_REFUSED: return "Connection refused"; + case MA_NO_HOST: return "No host"; + case MA_IN_PROGRESS: return "Operation in progress"; + case MA_CANCELLED: return "Operation cancelled"; + case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; + + case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; + case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; + case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; + case MA_NO_BACKEND: return "No backend"; + case MA_NO_DEVICE: return "No device"; + case MA_API_NOT_FOUND: return "API not found"; + case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; + + case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; + case MA_DEVICE_NOT_STARTED: return "Device not started"; + + case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; + case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; + case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; + case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; + + default: return "Unknown error"; + } +} + +MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } else { + return NULL; /* Do not fall back to the default implementation. */ + } + } else { + return ma__malloc_default(sz, NULL); + } +} + +MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + void* p = ma_malloc(sz, pAllocationCallbacks); + if (p != NULL) { + MA_ZERO_MEMORY(p, sz); + } + + return p; +} + +MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); + } else { + return NULL; /* Do not fall back to the default implementation. */ + } + } else { + return ma__realloc_default(p, sz, NULL); + } +} + +MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL) { + return; + } + + if (pAllocationCallbacks != NULL) { + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } else { + return; /* Do no fall back to the default implementation. */ + } + } else { + ma__free_default(p, NULL); + } +} + +MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) +{ + size_t extraBytes; + void* pUnaligned; + void* pAligned; + + if (alignment == 0) { + return 0; + } + + extraBytes = alignment-1 + sizeof(void*); + + pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); + if (pUnaligned == NULL) { + return NULL; + } + + pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); + ((void**)pAligned)[-1] = pUnaligned; + + return pAligned; +} + +MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_free(((void**)p)[-1], pAllocationCallbacks); +} + +MA_API const char* ma_get_format_name(ma_format format) +{ + switch (format) + { + case ma_format_unknown: return "Unknown"; + case ma_format_u8: return "8-bit Unsigned Integer"; + case ma_format_s16: return "16-bit Signed Integer"; + case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; + case ma_format_s32: return "32-bit Signed Integer"; + case ma_format_f32: return "32-bit IEEE Floating Point"; + default: return "Invalid"; + } +} + +MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) +{ + ma_uint32 i; + for (i = 0; i < channels; ++i) { + pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); + } +} + + +MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) +{ + ma_uint32 sizes[] = { + 0, /* unknown */ + 1, /* u8 */ + 2, /* s16 */ + 3, /* s24 */ + 4, /* s32 */ + 4, /* f32 */ + }; + return sizes[format]; +} + + + +#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0) +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0 +#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0) + +MA_API ma_data_source_config ma_data_source_config_init(void) +{ + ma_data_source_config config; + + MA_ZERO_OBJECT(&config); + + return config; +} + + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSourceBase); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->vtable = pConfig->vtable; + pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; + pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ + pDataSourceBase->pNext = NULL; + pDataSourceBase->onGetNext = NULL; + + return MA_SUCCESS; +} + +MA_API void ma_data_source_uninit(ma_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return; + } + + /* + This is placeholder in case we need this later. Data sources need to call this in their + uninitialization routine to ensure things work later on if something is added here. + */ +} + +static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) +{ + ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; + + MA_ASSERT(pDataSource != NULL); + MA_ASSERT(ppCurrentDataSource != NULL); + + if (pCurrentDataSource->pCurrent == NULL) { + /* + The current data source is NULL. If we're using this in the context of a chain we need to return NULL + here so that we don't end up looping. Otherwise we just return the data source itself. + */ + if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { + pCurrentDataSource = NULL; + } else { + pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ + } + } else { + pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; + } + + *ppCurrentDataSource = pCurrentDataSource; + + return MA_SUCCESS; +} + +static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 framesRead = 0; + ma_bool32 loop = ma_data_source_is_looping(pDataSource); + + if (pDataSourceBase == NULL) { + return MA_AT_END; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { + /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + /* Need to clamp to within the range. */ + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor); + if (result != MA_SUCCESS) { + /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + ma_uint64 rangeBeg; + ma_uint64 rangeEnd; + + /* We have the cursor. We need to make sure we don't read beyond our range. */ + rangeBeg = pDataSourceBase->rangeBegInFrames; + rangeEnd = pDataSourceBase->rangeEndInFrames; + + absoluteCursor = rangeBeg + relativeCursor; + + /* If looping, make sure we're within range. */ + if (loop) { + if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { + rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); + } + } + + if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) { + frameCount = (rangeEnd - absoluteCursor); + } + + /* + If the cursor is sitting on the end of the range the frame count will be set to 0 which can + result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return + MA_AT_END so the higher level function can know about it. + */ + if (frameCount > 0) { + result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); + } else { + result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_data_source_base* pCurrentDataSource; + void* pRunningFramesOut = pFramesOut; + ma_uint64 totalFramesProcessed = 0; + ma_format format; + ma_uint32 channels; + ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ + ma_bool32 loop; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + loop = ma_data_source_is_looping(pDataSource); + + /* + We need to know the data format so we can advance the output buffer as we read frames. If this + fails, chaining will not work and we'll just read as much as we can from the current source. + */ + if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { + result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); + if (result != MA_SUCCESS) { + return result; + } + + return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); + } + + /* + Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and + only the current data source will be read from. + */ + + /* Keep reading until we've read as many frames as possible. */ + while (totalFramesProcessed < frameCount) { + ma_uint64 framesProcessed; + ma_uint64 framesRemaining = frameCount - totalFramesProcessed; + + /* We need to resolve the data source that we'll actually be reading from. */ + result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); + if (result != MA_SUCCESS) { + break; + } + + if (pCurrentDataSource == NULL) { + break; + } + + result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); + totalFramesProcessed += framesProcessed; + + /* + If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is + not necessarily considered an error. + */ + if (result != MA_SUCCESS && result != MA_AT_END) { + break; + } + + /* + We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned + MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. + */ + if (result == MA_AT_END) { + /* + The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't + accidentally return MA_AT_END when data has been read in prior loop iterations. at the + end of this function, the result will be checked for MA_SUCCESS, and if the total + number of frames processed is 0, will be explicitly set to MA_AT_END. + */ + result = MA_SUCCESS; + + /* + We reached the end. If we're looping, we just loop back to the start of the current + data source. If we're not looping we need to check if we have another in the chain, and + if so, switch to it. + */ + if (loop) { + if (framesProcessed == 0) { + emptyLoopCounter += 1; + if (emptyLoopCounter > 1) { + break; /* Infinite loop detected. Get out. */ + } + } else { + emptyLoopCounter = 0; + } + + result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); + if (result != MA_SUCCESS) { + break; /* Failed to loop. Abort. */ + } + + /* Don't return MA_AT_END for looping sounds. */ + result = MA_SUCCESS; + } else { + if (pCurrentDataSource->pNext != NULL) { + pDataSourceBase->pCurrent = pCurrentDataSource->pNext; + } else if (pCurrentDataSource->onGetNext != NULL) { + pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); + if (pDataSourceBase->pCurrent == NULL) { + break; /* Our callback did not return a next data source. We're done. */ + } + } else { + /* Reached the end of the chain. We're done. */ + break; + } + + /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ + result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); + if (result != MA_SUCCESS) { + break; + } + } + } + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) +{ + return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); +} + +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSourceBase == NULL) { + return MA_SUCCESS; + } + + if (pDataSourceBase->vtable->onSeek == NULL) { + return MA_NOT_IMPLEMENTED; + } + + if (frameIndex > pDataSourceBase->rangeEndInFrames) { + return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ + } + + return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); +} + +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + + /* Initialize to defaults for safety just in case the data source does not implement this callback. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + if (pDataSourceBase->vtable->onGetDataFormat == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); + if (result != MA_SUCCESS) { + return result; + } + + if (pFormat != NULL) { + *pFormat = format; + } + if (pChannels != NULL) { + *pChannels = channels; + } + if (pSampleRate != NULL) { + *pSampleRate = sampleRate; + } + + /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 cursor; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pDataSourceBase == NULL) { + return MA_SUCCESS; + } + + if (pDataSourceBase->vtable->onGetCursor == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); + if (result != MA_SUCCESS) { + return result; + } + + /* The cursor needs to be made relative to the start of the range. */ + if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ + *pCursor = 0; + } else { + *pCursor = cursor - pDataSourceBase->rangeBegInFrames; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pDataSourceBase == NULL) { + return MA_INVALID_ARGS; + } + + /* + If we have a range defined we'll use that to determine the length. This is one of rare times + where we'll actually trust the caller. If they've set the range, I think it's mostly safe to + assume they've set it based on some higher level knowledge of the structure of the sound bank. + */ + if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { + *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; + return MA_SUCCESS; + } + + /* + Getting here means a range is not defined so we'll need to get the data source itself to tell + us the length. + */ + if (pDataSourceBase->vtable->onGetLength == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); +} + +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) +{ + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) +{ + ma_result result; + ma_uint64 lengthInPCMFrames; + ma_uint32 sampleRate; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + + /* If there's no callback for this just treat it as a successful no-op. */ + if (pDataSourceBase->vtable->onSetLooping == NULL) { + return MA_SUCCESS; + } + + return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); +} + +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_FALSE; + } + + return ma_atomic_load_32(&pDataSourceBase->isLooping); +} + +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + ma_result result; + ma_uint64 relativeCursor; + ma_uint64 absoluteCursor; + ma_bool32 doSeekAdjustment = MA_FALSE; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if (rangeEndInFrames < rangeBegInFrames) { + return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ + } + + /* + We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now + so we can calculate it's absolute position before we change the range. + */ + result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor); + if (result == MA_SUCCESS) { + doSeekAdjustment = MA_TRUE; + absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames; + } else { + /* + We couldn't get the position of the cursor. It probably means the data source has no notion + of a cursor. We'll just leave it at position 0. Don't treat this as an error. + */ + doSeekAdjustment = MA_FALSE; + relativeCursor = 0; + absoluteCursor = 0; + } + + pDataSourceBase->rangeBegInFrames = rangeBegInFrames; + pDataSourceBase->rangeEndInFrames = rangeEndInFrames; + + /* + The commented out logic below was intended to maintain loop points in response to a change in the + range. However, this is not useful because it results in the sound breaking when you move the range + outside of the old loop points. I'm simplifying this by simply resetting the loop points. The + caller is expected to update their loop points if they change the range. + + In practice this should be mostly a non-issue because the majority of the time the range will be + set once right after initialization. + */ + pDataSourceBase->loopBegInFrames = 0; + pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); + + + /* + Seek to within range. Note that our seek positions here are relative to the new range. We don't want + do do this if we failed to retrieve the cursor earlier on because it probably means the data source + has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but + I'm just not even going to attempt it. + */ + if (doSeekAdjustment) { + if (absoluteCursor < rangeBegInFrames) { + ma_data_source_seek_to_pcm_frame(pDataSource, 0); + } else if (absoluteCursor > rangeEndInFrames) { + ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); + } + } + + return MA_SUCCESS; +} + +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return; + } + + if (pRangeBegInFrames != NULL) { + *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; + } + + if (pRangeEndInFrames != NULL) { + *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; + } +} + +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if (loopEndInFrames < loopBegInFrames) { + return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ + } + + if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { + return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ + } + + pDataSourceBase->loopBegInFrames = loopBegInFrames; + pDataSourceBase->loopEndInFrames = loopEndInFrames; + + /* The end cannot exceed the range. */ + if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { + pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); + } + + return MA_SUCCESS; +} + +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return; + } + + if (pLoopBegInFrames != NULL) { + *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; + } + + if (pLoopEndInFrames != NULL) { + *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; + } +} + +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->pCurrent = pCurrentDataSource; + + return MA_SUCCESS; +} + +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->pCurrent; +} + +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->pNext = pNextDataSource; + + return MA_SUCCESS; +} + +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->pNext; +} + +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) +{ + ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + pDataSourceBase->onGetNext = onGetNext; + + return MA_SUCCESS; +} + +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) +{ + const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; + + if (pDataSource == NULL) { + return NULL; + } + + return pDataSourceBase->onGetNext; +} + + +static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (framesRead < frameCount || framesRead == 0) { + return MA_AT_END; + } + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pFormat = pAudioBufferRef->format; + *pChannels = pAudioBufferRef->channels; + *pSampleRate = pAudioBufferRef->sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pCursor = pAudioBufferRef->cursor; + + return MA_SUCCESS; +} + +static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; + + *pLength = pAudioBufferRef->sizeInFrames; + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = +{ + ma_audio_buffer_ref__data_source_on_read, + ma_audio_buffer_ref__data_source_on_seek, + ma_audio_buffer_ref__data_source_on_get_data_format, + ma_audio_buffer_ref__data_source_on_get_cursor, + ma_audio_buffer_ref__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pAudioBufferRef); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); + if (result != MA_SUCCESS) { + return result; + } + + pAudioBufferRef->format = format; + pAudioBufferRef->channels = channels; + pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ + pAudioBufferRef->cursor = 0; + pAudioBufferRef->sizeInFrames = sizeInFrames; + pAudioBufferRef->pData = pData; + + return MA_SUCCESS; +} + +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) +{ + if (pAudioBufferRef == NULL) { + return; + } + + ma_data_source_uninit(&pAudioBufferRef->ds); +} + +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) +{ + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + pAudioBufferRef->cursor = 0; + pAudioBufferRef->sizeInFrames = sizeInFrames; + pAudioBufferRef->pData = pData; + + return MA_SUCCESS; +} + +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) +{ + ma_uint64 totalFramesRead = 0; + + if (pAudioBufferRef == NULL) { + return 0; + } + + if (frameCount == 0) { + return 0; + } + + while (totalFramesRead < frameCount) { + ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + ma_uint64 framesRemaining = frameCount - totalFramesRead; + ma_uint64 framesToRead; + + framesToRead = framesRemaining; + if (framesToRead > framesAvailable) { + framesToRead = framesAvailable; + } + + if (pFramesOut != NULL) { + ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); + } + + totalFramesRead += framesToRead; + + pAudioBufferRef->cursor += framesToRead; + if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { + if (loop) { + pAudioBufferRef->cursor = 0; + } else { + break; /* We've reached the end and we're not looping. Done. */ + } + } + + MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); + } + + return totalFramesRead; +} + +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) +{ + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + if (frameIndex > pAudioBufferRef->sizeInFrames) { + return MA_INVALID_ARGS; + } + + pAudioBufferRef->cursor = (size_t)frameIndex; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) +{ + ma_uint64 framesAvailable; + ma_uint64 frameCount = 0; + + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; /* Safety. */ + } + + if (pFrameCount != NULL) { + frameCount = *pFrameCount; + *pFrameCount = 0; /* Safety. */ + } + + if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + if (frameCount > framesAvailable) { + frameCount = framesAvailable; + } + + *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); + *pFrameCount = frameCount; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) +{ + ma_uint64 framesAvailable; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + if (frameCount > framesAvailable) { + return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ + } + + pAudioBufferRef->cursor += frameCount; + + if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { + return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ + } else { + return MA_SUCCESS; + } +} + +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) +{ + if (pAudioBufferRef == NULL) { + return MA_FALSE; + } + + return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; +} + +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pAudioBufferRef->cursor; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = pAudioBufferRef->sizeInFrames; + + return MA_SUCCESS; +} + +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pAudioBufferRef == NULL) { + return MA_INVALID_ARGS; + } + + if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; + } + + return MA_SUCCESS; +} + + + + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_audio_buffer_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ + config.sizeInFrames = sizeInFrames; + config.pData = pData; + ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); + + return config; +} + +static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) +{ + ma_result result; + + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->sizeInFrames == 0) { + return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ + } + + result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); + if (result != MA_SUCCESS) { + return result; + } + + /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ + pAudioBuffer->ref.sampleRate = pConfig->sampleRate; + + ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); + + if (doCopy) { + ma_uint64 allocationSizeInBytes; + void* pData; + + allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); + if (allocationSizeInBytes > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ + if (pData == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (pConfig->pData != NULL) { + ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } else { + ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } + + ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); + pAudioBuffer->ownsData = MA_TRUE; + } else { + ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); + pAudioBuffer->ownsData = MA_FALSE; + } + + return MA_SUCCESS; +} + +static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) +{ + if (pAudioBuffer == NULL) { + return; + } + + if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { + ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ + } + + if (doFree) { + ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); + } + + ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); +} + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) +{ + return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); +} + +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) +{ + return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); +} + +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) +{ + ma_result result; + ma_audio_buffer* pAudioBuffer; + ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ + ma_uint64 allocationSizeInBytes; + + if (ppAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + *ppAudioBuffer = NULL; /* Safety. */ + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + innerConfig = *pConfig; + ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); + + allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); + if (allocationSizeInBytes > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ + if (pAudioBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + if (pConfig->pData != NULL) { + ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } else { + ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); + } + + innerConfig.pData = &pAudioBuffer->_pExtraData[0]; + + result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); + if (result != MA_SUCCESS) { + ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); + return result; + } + + *ppAudioBuffer = pAudioBuffer; + + return MA_SUCCESS; +} + +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) +{ + ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); +} + +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) +{ + ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); +} + +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) +{ + if (pAudioBuffer == NULL) { + return 0; + } + + return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); +} + +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); +} + +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) +{ + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; /* Safety. */ + } + + if (pAudioBuffer == NULL) { + if (pFrameCount != NULL) { + *pFrameCount = 0; + } + + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); +} + +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); +} + +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) +{ + if (pAudioBuffer == NULL) { + return MA_FALSE; + } + + return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); +} + +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); +} + +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) +{ + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); +} + +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); +} + + + + + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pData); + + pData->format = format; + pData->channels = channels; + pData->pTail = &pData->head; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_paged_audio_buffer_page* pPage; + + if (pData == NULL) { + return; + } + + /* All pages need to be freed. */ + pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); + while (pPage != NULL) { + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); + + ma_free(pPage, pAllocationCallbacks); + pPage = pNext; + } +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return &pData->head; +} + +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) +{ + if (pData == NULL) { + return NULL; + } + + return pData->pTail; +} + +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) +{ + ma_paged_audio_buffer_page* pPage; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + /* Calculate the length from the linked list. */ + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + *pLength += pPage->sizeInFrames; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) +{ + ma_paged_audio_buffer_page* pPage; + ma_uint64 allocationSize; + + if (ppPage == NULL) { + return MA_INVALID_ARGS; + } + + *ppPage = NULL; + + if (pData == NULL) { + return MA_INVALID_ARGS; + } + + allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); + if (allocationSize > MA_SIZE_MAX) { + return MA_OUT_OF_MEMORY; /* Too big. */ + } + + pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ + if (pPage == NULL) { + return MA_OUT_OF_MEMORY; + } + + pPage->pNext = NULL; + pPage->sizeInFrames = pageSizeInFrames; + + if (pInitialData != NULL) { + ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); + } + + *ppPage = pPage; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* It's assumed the page is not attached to the list. */ + ma_free(pPage, pAllocationCallbacks); + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) +{ + if (pData == NULL || pPage == NULL) { + return MA_INVALID_ARGS; + } + + /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ + + /* First thing to do is update the tail. */ + for (;;) { + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pNewTail = pPage; + + if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ + ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); + break; /* Done. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ +} + + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) +{ + ma_paged_audio_buffer_config config; + + MA_ZERO_OBJECT(&config); + config.pData = pData; + + return config; +} + + +static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; + + *pFormat = pPagedAudioBuffer->pData->format; + *pChannels = pPagedAudioBuffer->pData->channels; + *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); + + return MA_SUCCESS; +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); +} + +static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = +{ + ma_paged_audio_buffer__data_source_on_read, + ma_paged_audio_buffer__data_source_on_seek, + ma_paged_audio_buffer__data_source_on_get_data_format, + ma_paged_audio_buffer__data_source_on_get_cursor, + ma_paged_audio_buffer__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pPagedAudioBuffer); + + /* A config is required for the format and channel count. */ + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pData == NULL) { + return MA_INVALID_ARGS; /* No underlying data specified. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); + if (result != MA_SUCCESS) { + return result; + } + + pPagedAudioBuffer->pData = pConfig->pData; + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); + pPagedAudioBuffer->relativeCursor = 0; + pPagedAudioBuffer->absoluteCursor = 0; + + return MA_SUCCESS; +} + +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) +{ + if (pPagedAudioBuffer == NULL) { + return; + } + + /* Nothing to do. The data needs to be deleted separately. */ +} + +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead = 0; + ma_format format; + ma_uint32 channels; + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + format = pPagedAudioBuffer->pData->format; + channels = pPagedAudioBuffer->pData->channels; + + while (totalFramesRead < frameCount) { + /* Read from the current page. The buffer should never be in a state where this is NULL. */ + ma_uint64 framesRemainingInCurrentPage; + ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; + ma_uint64 framesToReadThisIteration; + + MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); + + framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; + + framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); + totalFramesRead += framesToReadThisIteration; + + pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; + pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; + + /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ + MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); + + if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { + /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + if (pNext == NULL) { + result = MA_AT_END; + break; /* We've reached the end. */ + } else { + pPagedAudioBuffer->pCurrent = pNext; + pPagedAudioBuffer->relativeCursor = 0; + } + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) +{ + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (frameIndex == pPagedAudioBuffer->absoluteCursor) { + return MA_SUCCESS; /* Nothing to do. */ + } + + if (frameIndex < pPagedAudioBuffer->absoluteCursor) { + /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ + pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); + pPagedAudioBuffer->absoluteCursor = 0; + pPagedAudioBuffer->relativeCursor = 0; + + /* Fall through to the forward seeking section below. */ + } + + if (frameIndex > pPagedAudioBuffer->absoluteCursor) { + /* Moving forward. */ + ma_paged_audio_buffer_page* pPage; + ma_uint64 runningCursor = 0; + + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { + ma_uint64 pageRangeBeg = runningCursor; + ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; + + if (frameIndex >= pageRangeBeg) { + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + /* We found the page. */ + pPagedAudioBuffer->pCurrent = pPage; + pPagedAudioBuffer->absoluteCursor = frameIndex; + pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; + return MA_SUCCESS; + } + } + + runningCursor = pageRangeEnd; + } + + /* Getting here means we tried seeking too far forward. Don't change any state. */ + return MA_BAD_SEEK; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pPagedAudioBuffer == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pPagedAudioBuffer->absoluteCursor; + + return MA_SUCCESS; +} + +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) +{ + return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); +} + + + +/************************************************************************************************************************************************************** + +VFS + +**************************************************************************************************************************************************************/ +MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pVFS == NULL || pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onOpen == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); +} + +MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pVFS == NULL || pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onOpenW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); +} + +MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onClose == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onClose(pVFS, file); +} + +MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + ma_result result; + size_t bytesRead = 0; + + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + if (pVFS == NULL || file == NULL || pDst == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onRead == NULL) { + return MA_NOT_IMPLEMENTED; + } + + result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); + + if (pBytesRead != NULL) { + *pBytesRead = bytesRead; + } + + if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pBytesWritten != NULL) { + *pBytesWritten = 0; + } + + if (pVFS == NULL || file == NULL || pSrc == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onWrite == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +} + +MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onSeek == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onSeek(pVFS, file, offset, origin); +} + +MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onTell == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onTell(pVFS, file, pCursor); +} + +MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; + + if (pInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pInfo); + + if (pVFS == NULL || file == NULL) { + return MA_INVALID_ARGS; + } + + if (pCallbacks->onInfo == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pCallbacks->onInfo(pVFS, file, pInfo); +} + + +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) + #define MA_USE_WIN32_FILEIO +#endif + +#if defined(MA_USE_WIN32_FILEIO) +/* +We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do +not have the Ex version. We therefore need to do some dynamic branching depending on what's available. + +We load these when we load our first file from the default VFS. It's left open for the life of the +program and is left to the OS to uninitialize when the program terminates. +*/ +typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); + +static ma_handle hKernel32DLL = NULL; +static ma_SetFilePointer_proc ma_SetFilePointer = NULL; +static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; + +static void ma_win32_fileio_init(void) +{ + if (hKernel32DLL == NULL) { + hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); + if (hKernel32DLL != NULL) { + ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); + ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); + } + } +} + +static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) +{ + *pDesiredAccess = 0; + if ((openMode & MA_OPEN_MODE_READ) != 0) { + *pDesiredAccess |= GENERIC_READ; + } + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + *pDesiredAccess |= GENERIC_WRITE; + } + + *pShareMode = 0; + if ((openMode & MA_OPEN_MODE_READ) != 0) { + *pShareMode |= FILE_SHARE_READ; + } + + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ + } else { + *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ + } +} + +static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + + (void)pVFS; + + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + + hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); + } + + *pFile = hFile; + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + HANDLE hFile; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + + (void)pVFS; + + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); + + hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return ma_result_from_GetLastError(GetLastError()); + } + + *pFile = hFile; + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) +{ + (void)pVFS; + + if (CloseHandle((HANDLE)file) == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + ma_result result = MA_SUCCESS; + size_t totalBytesRead; + + (void)pVFS; + + totalBytesRead = 0; + while (totalBytesRead < sizeInBytes) { + size_t bytesRemaining; + DWORD bytesToRead; + DWORD bytesRead; + BOOL readResult; + + bytesRemaining = sizeInBytes - totalBytesRead; + if (bytesRemaining >= 0xFFFFFFFF) { + bytesToRead = 0xFFFFFFFF; + } else { + bytesToRead = (DWORD)bytesRemaining; + } + + readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); + if (readResult == 1 && bytesRead == 0) { + result = MA_AT_END; + break; /* EOF */ + } + + totalBytesRead += bytesRead; + + if (bytesRead < bytesToRead) { + break; /* EOF */ + } + + if (readResult == 0) { + result = ma_result_from_GetLastError(GetLastError()); + break; + } + } + + if (pBytesRead != NULL) { + *pBytesRead = totalBytesRead; + } + + return result; +} + +static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + ma_result result = MA_SUCCESS; + size_t totalBytesWritten; + + (void)pVFS; + + totalBytesWritten = 0; + while (totalBytesWritten < sizeInBytes) { + size_t bytesRemaining; + DWORD bytesToWrite; + DWORD bytesWritten; + BOOL writeResult; + + bytesRemaining = sizeInBytes - totalBytesWritten; + if (bytesRemaining >= 0xFFFFFFFF) { + bytesToWrite = 0xFFFFFFFF; + } else { + bytesToWrite = (DWORD)bytesRemaining; + } + + writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); + totalBytesWritten += bytesWritten; + + if (writeResult == 0) { + result = ma_result_from_GetLastError(GetLastError()); + break; + } + } + + if (pBytesWritten != NULL) { + *pBytesWritten = totalBytesWritten; + } + + return result; +} + + +static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + LARGE_INTEGER liDistanceToMove; + DWORD dwMoveMethod; + BOOL result; + + (void)pVFS; + + liDistanceToMove.QuadPart = offset; + + /* */ if (origin == ma_seek_origin_current) { + dwMoveMethod = FILE_CURRENT; + } else if (origin == ma_seek_origin_end) { + dwMoveMethod = FILE_END; + } else { + dwMoveMethod = FILE_BEGIN; + } + + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); + } else if (ma_SetFilePointer != NULL) { + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); + } else { + return MA_NOT_IMPLEMENTED; + } + + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + LARGE_INTEGER liZero; + LARGE_INTEGER liTell; + BOOL result; + + (void)pVFS; + + liZero.QuadPart = 0; + + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); + } else if (ma_SetFilePointer != NULL) { + LONG tell; + + result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; + } else { + return MA_NOT_IMPLEMENTED; + } + + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + if (pCursor != NULL) { + *pCursor = liTell.QuadPart; + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + BY_HANDLE_FILE_INFORMATION fi; + BOOL result; + + (void)pVFS; + + result = GetFileInformationByHandle((HANDLE)file, &fi); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); + + return MA_SUCCESS; +} +#else +static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_result result; + FILE* pFileStd; + const char* pOpenModeStr; + + MA_ASSERT(pFilePath != NULL); + MA_ASSERT(openMode != 0); + MA_ASSERT(pFile != NULL); + + (void)pVFS; + + if ((openMode & MA_OPEN_MODE_READ) != 0) { + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + pOpenModeStr = "r+"; + } else { + pOpenModeStr = "rb"; + } + } else { + pOpenModeStr = "wb"; + } + + result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); + if (result != MA_SUCCESS) { + return result; + } + + *pFile = pFileStd; + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + ma_result result; + FILE* pFileStd; + const wchar_t* pOpenModeStr; + + MA_ASSERT(pFilePath != NULL); + MA_ASSERT(openMode != 0); + MA_ASSERT(pFile != NULL); + + (void)pVFS; + + if ((openMode & MA_OPEN_MODE_READ) != 0) { + if ((openMode & MA_OPEN_MODE_WRITE) != 0) { + pOpenModeStr = L"r+"; + } else { + pOpenModeStr = L"rb"; + } + } else { + pOpenModeStr = L"wb"; + } + + result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); + if (result != MA_SUCCESS) { + return result; + } + + *pFile = pFileStd; + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) +{ + MA_ASSERT(file != NULL); + + (void)pVFS; + + fclose((FILE*)file); + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + size_t result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pDst != NULL); + + (void)pVFS; + + result = fread(pDst, 1, sizeInBytes, (FILE*)file); + + if (pBytesRead != NULL) { + *pBytesRead = result; + } + + if (result != sizeInBytes) { + if (result == 0 && feof((FILE*)file)) { + return MA_AT_END; + } else { + return ma_result_from_errno(ferror((FILE*)file)); + } + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + size_t result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pSrc != NULL); + + (void)pVFS; + + result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); + + if (pBytesWritten != NULL) { + *pBytesWritten = result; + } + + if (result != sizeInBytes) { + return ma_result_from_errno(ferror((FILE*)file)); + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + int result; + int whence; + + MA_ASSERT(file != NULL); + + (void)pVFS; + + if (origin == ma_seek_origin_start) { + whence = SEEK_SET; + } else if (origin == ma_seek_origin_end) { + whence = SEEK_END; + } else { + whence = SEEK_CUR; + } + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _fseeki64((FILE*)file, offset, whence); + #else + /* No _fseeki64() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = fseek((FILE*)file, (int)offset, whence); + #endif +#else + result = fseek((FILE*)file, (long int)offset, whence); +#endif + if (result != 0) { + return MA_ERROR; + } + + return MA_SUCCESS; +} + +static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + ma_int64 result; + + MA_ASSERT(file != NULL); + MA_ASSERT(pCursor != NULL); + + (void)pVFS; + +#if defined(_WIN32) + #if defined(_MSC_VER) && _MSC_VER > 1200 + result = _ftelli64((FILE*)file); + #else + result = ftell((FILE*)file); + #endif +#else + result = ftell((FILE*)file); +#endif + + *pCursor = result; + + return MA_SUCCESS; +} + +#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) +int fileno(FILE *stream); +#endif + +static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + int fd; + struct stat info; + + MA_ASSERT(file != NULL); + MA_ASSERT(pInfo != NULL); + + (void)pVFS; + +#if defined(_MSC_VER) + fd = _fileno((FILE*)file); +#else + fd = fileno((FILE*)file); +#endif + + if (fstat(fd, &info) != 0) { + return ma_result_from_errno(errno); + } + + pInfo->sizeInBytes = info.st_size; + + return MA_SUCCESS; +} +#endif + + +static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); +#else + return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); +#endif +} + +static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pFile == NULL) { + return MA_INVALID_ARGS; + } + + *pFile = NULL; + + if (pFilePath == NULL || openMode == 0) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); +#else + return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); +#endif +} + +static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) +{ + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_close__win32(pVFS, file); +#else + return ma_default_vfs_close__stdio(pVFS, file); +#endif +} + +static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + if (file == NULL || pDst == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); +#else + return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); +#endif +} + +static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + if (pBytesWritten != NULL) { + *pBytesWritten = 0; + } + + if (file == NULL || pSrc == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +#else + return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); +#endif +} + +static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_seek__win32(pVFS, file, offset, origin); +#else + return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); +#endif +} + +static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_tell__win32(pVFS, file, pCursor); +#else + return ma_default_vfs_tell__stdio(pVFS, file, pCursor); +#endif +} + +static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + if (pInfo == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pInfo); + + if (file == NULL) { + return MA_INVALID_ARGS; + } + +#if defined(MA_USE_WIN32_FILEIO) + return ma_default_vfs_info__win32(pVFS, file, pInfo); +#else + return ma_default_vfs_info__stdio(pVFS, file, pInfo); +#endif +} + + +MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pVFS == NULL) { + return MA_INVALID_ARGS; + } + + pVFS->cb.onOpen = ma_default_vfs_open; + pVFS->cb.onOpenW = ma_default_vfs_open_w; + pVFS->cb.onClose = ma_default_vfs_close; + pVFS->cb.onRead = ma_default_vfs_read; + pVFS->cb.onWrite = ma_default_vfs_write; + pVFS->cb.onSeek = ma_default_vfs_seek; + pVFS->cb.onTell = ma_default_vfs_tell; + pVFS->cb.onInfo = ma_default_vfs_info; + ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); + + return MA_SUCCESS; +} + + +MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pVFS != NULL) { + return ma_vfs_open(pVFS, pFilePath, openMode, pFile); + } else { + return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); + } +} + +MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) +{ + if (pVFS != NULL) { + return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); + } else { + return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); + } +} + +MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) +{ + if (pVFS != NULL) { + return ma_vfs_close(pVFS, file); + } else { + return ma_default_vfs_close(pVFS, file); + } +} + +MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) +{ + if (pVFS != NULL) { + return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); + } else { + return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); + } +} + +MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) +{ + if (pVFS != NULL) { + return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); + } else { + return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); + } +} + +MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) +{ + if (pVFS != NULL) { + return ma_vfs_seek(pVFS, file, offset, origin); + } else { + return ma_default_vfs_seek(pVFS, file, offset, origin); + } +} + +MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) +{ + if (pVFS != NULL) { + return ma_vfs_tell(pVFS, file, pCursor); + } else { + return ma_default_vfs_tell(pVFS, file, pCursor); + } +} + +MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) +{ + if (pVFS != NULL) { + return ma_vfs_info(pVFS, file, pInfo); + } else { + return ma_default_vfs_info(pVFS, file, pInfo); + } +} + + + +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_vfs_file file; + ma_file_info info; + void* pData; + size_t bytesRead; + + if (ppData != NULL) { + *ppData = NULL; + } + if (pSize != NULL) { + *pSize = 0; + } + + if (ppData == NULL) { + return MA_INVALID_ARGS; + } + + if (pFilePath != NULL) { + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + } else { + result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); + } + if (result != MA_SUCCESS) { + return result; + } + + result = ma_vfs_or_default_info(pVFS, file, &info); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + if (info.sizeInBytes > MA_SIZE_MAX) { + ma_vfs_or_default_close(pVFS, file); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + if (pData == NULL) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ + ma_vfs_or_default_close(pVFS, file); + + if (result != MA_SUCCESS) { + ma_free(pData, pAllocationCallbacks); + return result; + } + + if (pSize != NULL) { + *pSize = bytesRead; + } + + MA_ASSERT(ppData != NULL); + *ppData = pData; + + return MA_SUCCESS; +} + +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); +} + +MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); +} + + + +/************************************************************************************************************************************************************** + +Decoding and Encoding Headers. These are auto-generated from a tool. + +**************************************************************************************************************************************************************/ +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) +/* dr_wav_h begin */ +#ifndef ma_dr_wav_h +#define ma_dr_wav_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_WAV_STRINGIFY(x) #x +#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) +#define MA_DR_WAV_VERSION_MAJOR 0 +#define MA_DR_WAV_VERSION_MINOR 13 +#define MA_DR_WAV_VERSION_REVISION 13 +#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) +#include +#define MA_DR_WAVE_FORMAT_PCM 0x1 +#define MA_DR_WAVE_FORMAT_ADPCM 0x2 +#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define MA_DR_WAVE_FORMAT_ALAW 0x6 +#define MA_DR_WAVE_FORMAT_MULAW 0x7 +#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define MA_DR_WAV_SEQUENTIAL 0x00000001 +#define MA_DR_WAV_WITH_METADATA 0x00000002 +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_wav_version_string(void); +typedef enum +{ + ma_dr_wav_seek_origin_start, + ma_dr_wav_seek_origin_current +} ma_dr_wav_seek_origin; +typedef enum +{ + ma_dr_wav_container_riff, + ma_dr_wav_container_rifx, + ma_dr_wav_container_w64, + ma_dr_wav_container_rf64, + ma_dr_wav_container_aiff +} ma_dr_wav_container; +typedef struct +{ + union + { + ma_uint8 fourcc[4]; + ma_uint8 guid[16]; + } id; + ma_uint64 sizeInBytes; + unsigned int paddingSize; +} ma_dr_wav_chunk_header; +typedef struct +{ + ma_uint16 formatTag; + ma_uint16 channels; + ma_uint32 sampleRate; + ma_uint32 avgBytesPerSec; + ma_uint16 blockAlign; + ma_uint16 bitsPerSample; + ma_uint16 extendedSize; + ma_uint16 validBitsPerSample; + ma_uint32 channelMask; + ma_uint8 subFormat[16]; +} ma_dr_wav_fmt; +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); +typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); +typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); +typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); +typedef struct +{ + const ma_uint8* data; + size_t dataSize; + size_t currentReadPos; +} ma_dr_wav__memory_stream; +typedef struct +{ + void** ppData; + size_t* pDataSize; + size_t dataSize; + size_t dataCapacity; + size_t currentWritePos; +} ma_dr_wav__memory_stream_write; +typedef struct +{ + ma_dr_wav_container container; + ma_uint32 format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 bitsPerSample; +} ma_dr_wav_data_format; +typedef enum +{ + ma_dr_wav_metadata_type_none = 0, + ma_dr_wav_metadata_type_unknown = 1 << 0, + ma_dr_wav_metadata_type_smpl = 1 << 1, + ma_dr_wav_metadata_type_inst = 1 << 2, + ma_dr_wav_metadata_type_cue = 1 << 3, + ma_dr_wav_metadata_type_acid = 1 << 4, + ma_dr_wav_metadata_type_bext = 1 << 5, + ma_dr_wav_metadata_type_list_label = 1 << 6, + ma_dr_wav_metadata_type_list_note = 1 << 7, + ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, + ma_dr_wav_metadata_type_list_info_software = 1 << 9, + ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, + ma_dr_wav_metadata_type_list_info_title = 1 << 11, + ma_dr_wav_metadata_type_list_info_artist = 1 << 12, + ma_dr_wav_metadata_type_list_info_comment = 1 << 13, + ma_dr_wav_metadata_type_list_info_date = 1 << 14, + ma_dr_wav_metadata_type_list_info_genre = 1 << 15, + ma_dr_wav_metadata_type_list_info_album = 1 << 16, + ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, + ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software + | ma_dr_wav_metadata_type_list_info_copyright + | ma_dr_wav_metadata_type_list_info_title + | ma_dr_wav_metadata_type_list_info_artist + | ma_dr_wav_metadata_type_list_info_comment + | ma_dr_wav_metadata_type_list_info_date + | ma_dr_wav_metadata_type_list_info_genre + | ma_dr_wav_metadata_type_list_info_album + | ma_dr_wav_metadata_type_list_info_tracknumber, + ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label + | ma_dr_wav_metadata_type_list_note + | ma_dr_wav_metadata_type_list_labelled_cue_region, + ma_dr_wav_metadata_type_all = -2, + ma_dr_wav_metadata_type_all_including_unknown = -1 +} ma_dr_wav_metadata_type; +typedef enum +{ + ma_dr_wav_smpl_loop_type_forward = 0, + ma_dr_wav_smpl_loop_type_pingpong = 1, + ma_dr_wav_smpl_loop_type_backward = 2 +} ma_dr_wav_smpl_loop_type; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 type; + ma_uint32 firstSampleByteOffset; + ma_uint32 lastSampleByteOffset; + ma_uint32 sampleFraction; + ma_uint32 playCount; +} ma_dr_wav_smpl_loop; +typedef struct +{ + ma_uint32 manufacturerId; + ma_uint32 productId; + ma_uint32 samplePeriodNanoseconds; + ma_uint32 midiUnityNote; + ma_uint32 midiPitchFraction; + ma_uint32 smpteFormat; + ma_uint32 smpteOffset; + ma_uint32 sampleLoopCount; + ma_uint32 samplerSpecificDataSizeInBytes; + ma_dr_wav_smpl_loop* pLoops; + ma_uint8* pSamplerSpecificData; +} ma_dr_wav_smpl; +typedef struct +{ + ma_int8 midiUnityNote; + ma_int8 fineTuneCents; + ma_int8 gainDecibels; + ma_int8 lowNote; + ma_int8 highNote; + ma_int8 lowVelocity; + ma_int8 highVelocity; +} ma_dr_wav_inst; +typedef struct +{ + ma_uint32 id; + ma_uint32 playOrderPosition; + ma_uint8 dataChunkId[4]; + ma_uint32 chunkStart; + ma_uint32 blockStart; + ma_uint32 sampleByteOffset; +} ma_dr_wav_cue_point; +typedef struct +{ + ma_uint32 cuePointCount; + ma_dr_wav_cue_point *pCuePoints; +} ma_dr_wav_cue; +typedef enum +{ + ma_dr_wav_acid_flag_one_shot = 1, + ma_dr_wav_acid_flag_root_note_set = 2, + ma_dr_wav_acid_flag_stretch = 4, + ma_dr_wav_acid_flag_disk_based = 8, + ma_dr_wav_acid_flag_acidizer = 16 +} ma_dr_wav_acid_flag; +typedef struct +{ + ma_uint32 flags; + ma_uint16 midiUnityNote; + ma_uint16 reserved1; + float reserved2; + ma_uint32 numBeats; + ma_uint16 meterDenominator; + ma_uint16 meterNumerator; + float tempo; +} ma_dr_wav_acid; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_label_or_note; +typedef struct +{ + char* pDescription; + char* pOriginatorName; + char* pOriginatorReference; + char pOriginationDate[10]; + char pOriginationTime[8]; + ma_uint64 timeReference; + ma_uint16 version; + char* pCodingHistory; + ma_uint32 codingHistorySize; + ma_uint8* pUMID; + ma_uint16 loudnessValue; + ma_uint16 loudnessRange; + ma_uint16 maxTruePeakLevel; + ma_uint16 maxMomentaryLoudness; + ma_uint16 maxShortTermLoudness; +} ma_dr_wav_bext; +typedef struct +{ + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_info_text; +typedef struct +{ + ma_uint32 cuePointId; + ma_uint32 sampleLength; + ma_uint8 purposeId[4]; + ma_uint16 country; + ma_uint16 language; + ma_uint16 dialect; + ma_uint16 codePage; + ma_uint32 stringLength; + char* pString; +} ma_dr_wav_list_labelled_cue_region; +typedef enum +{ + ma_dr_wav_metadata_location_invalid, + ma_dr_wav_metadata_location_top_level, + ma_dr_wav_metadata_location_inside_info_list, + ma_dr_wav_metadata_location_inside_adtl_list +} ma_dr_wav_metadata_location; +typedef struct +{ + ma_uint8 id[4]; + ma_dr_wav_metadata_location chunkLocation; + ma_uint32 dataSizeInBytes; + ma_uint8* pData; +} ma_dr_wav_unknown_metadata; +typedef struct +{ + ma_dr_wav_metadata_type type; + union + { + ma_dr_wav_cue cue; + ma_dr_wav_smpl smpl; + ma_dr_wav_acid acid; + ma_dr_wav_inst inst; + ma_dr_wav_bext bext; + ma_dr_wav_list_label_or_note labelOrNote; + ma_dr_wav_list_labelled_cue_region labelledCueRegion; + ma_dr_wav_list_info_text infoText; + ma_dr_wav_unknown_metadata unknown; + } data; +} ma_dr_wav_metadata; +typedef struct +{ + ma_dr_wav_read_proc onRead; + ma_dr_wav_write_proc onWrite; + ma_dr_wav_seek_proc onSeek; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav_container container; + ma_dr_wav_fmt fmt; + ma_uint32 sampleRate; + ma_uint16 channels; + ma_uint16 bitsPerSample; + ma_uint16 translatedFormatTag; + ma_uint64 totalPCMFrameCount; + ma_uint64 dataChunkDataSize; + ma_uint64 dataChunkDataPos; + ma_uint64 bytesRemaining; + ma_uint64 readCursorInPCMFrames; + ma_uint64 dataChunkDataSizeTargetWrite; + ma_bool32 isSequentialWrite; + ma_dr_wav_metadata* pMetadata; + ma_uint32 metadataCount; + ma_dr_wav__memory_stream memoryStream; + ma_dr_wav__memory_stream_write memoryStreamWrite; + struct + { + ma_uint32 bytesRemainingInBlock; + ma_uint16 predictor[2]; + ma_int32 delta[2]; + ma_int32 cachedFrames[4]; + ma_uint32 cachedFrameCount; + ma_int32 prevFrames[2][2]; + } msadpcm; + struct + { + ma_uint32 bytesRemainingInBlock; + ma_int32 predictor[2]; + ma_int32 stepIndex[2]; + ma_int32 cachedFrames[16]; + ma_uint32 cachedFrameCount; + } ima; + struct + { + ma_bool8 isLE; + ma_bool8 isUnsigned; + } aiff; +} ma_dr_wav; +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +#endif +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); +#ifdef __cplusplus +} +#endif +#endif +/* dr_wav_h end */ +#endif /* MA_NO_WAV */ + +#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) +/* dr_flac_h begin */ +#ifndef ma_dr_flac_h +#define ma_dr_flac_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_FLAC_STRINGIFY(x) #x +#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) +#define MA_DR_FLAC_VERSION_MAJOR 0 +#define MA_DR_FLAC_VERSION_MINOR 12 +#define MA_DR_FLAC_VERSION_REVISION 42 +#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) +#include +#if defined(_MSC_VER) && _MSC_VER >= 1700 + #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) +#elif (defined(__GNUC__) && __GNUC__ >= 4) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) +#elif defined(__has_feature) + #if __has_feature(attribute_deprecated) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) + #else + #define MA_DR_FLAC_DEPRECATED + #endif +#else + #define MA_DR_FLAC_DEPRECATED +#endif +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_flac_version_string(void); +#ifndef MA_DR_FLAC_BUFFER_SIZE +#define MA_DR_FLAC_BUFFER_SIZE 4096 +#endif +#ifdef MA_64BIT +typedef ma_uint64 ma_dr_flac_cache_t; +#else +typedef ma_uint32 ma_dr_flac_cache_t; +#endif +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 +#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 +#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 +#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 +#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 +#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 +#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 +#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 +#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 +typedef enum +{ + ma_dr_flac_container_native, + ma_dr_flac_container_ogg, + ma_dr_flac_container_unknown +} ma_dr_flac_container; +typedef enum +{ + ma_dr_flac_seek_origin_start, + ma_dr_flac_seek_origin_current +} ma_dr_flac_seek_origin; +typedef struct +{ + ma_uint64 firstPCMFrame; + ma_uint64 flacFrameOffset; + ma_uint16 pcmFrameCount; +} ma_dr_flac_seekpoint; +typedef struct +{ + ma_uint16 minBlockSizeInPCMFrames; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint32 minFrameSizeInPCMFrames; + ma_uint32 maxFrameSizeInPCMFrames; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint8 md5[16]; +} ma_dr_flac_streaminfo; +typedef struct +{ + ma_uint32 type; + const void* pRawData; + ma_uint32 rawDataSize; + union + { + ma_dr_flac_streaminfo streaminfo; + struct + { + int unused; + } padding; + struct + { + ma_uint32 id; + const void* pData; + ma_uint32 dataSize; + } application; + struct + { + ma_uint32 seekpointCount; + const ma_dr_flac_seekpoint* pSeekpoints; + } seektable; + struct + { + ma_uint32 vendorLength; + const char* vendor; + ma_uint32 commentCount; + const void* pComments; + } vorbis_comment; + struct + { + char catalog[128]; + ma_uint64 leadInSampleCount; + ma_bool32 isCD; + ma_uint8 trackCount; + const void* pTrackData; + } cuesheet; + struct + { + ma_uint32 type; + ma_uint32 mimeLength; + const char* mime; + ma_uint32 descriptionLength; + const char* description; + ma_uint32 width; + ma_uint32 height; + ma_uint32 colorDepth; + ma_uint32 indexColorCount; + ma_uint32 pictureDataSize; + const ma_uint8* pPictureData; + } picture; + } data; +} ma_dr_flac_metadata; +typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); +typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); +typedef struct +{ + const ma_uint8* data; + size_t dataSize; + size_t currentReadPos; +} ma_dr_flac__memory_stream; +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + void* pUserData; + size_t unalignedByteCount; + ma_dr_flac_cache_t unalignedCache; + ma_uint32 nextL2Line; + ma_uint32 consumedBits; + ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; + ma_dr_flac_cache_t cache; + ma_uint16 crc16; + ma_dr_flac_cache_t crc16Cache; + ma_uint32 crc16CacheIgnoredBytes; +} ma_dr_flac_bs; +typedef struct +{ + ma_uint8 subframeType; + ma_uint8 wastedBitsPerSample; + ma_uint8 lpcOrder; + ma_int32* pSamplesS32; +} ma_dr_flac_subframe; +typedef struct +{ + ma_uint64 pcmFrameNumber; + ma_uint32 flacFrameNumber; + ma_uint32 sampleRate; + ma_uint16 blockSizeInPCMFrames; + ma_uint8 channelAssignment; + ma_uint8 bitsPerSample; + ma_uint8 crc8; +} ma_dr_flac_frame_header; +typedef struct +{ + ma_dr_flac_frame_header header; + ma_uint32 pcmFramesRemaining; + ma_dr_flac_subframe subframes[8]; +} ma_dr_flac_frame; +typedef struct +{ + ma_dr_flac_meta_proc onMeta; + void* pUserDataMD; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 totalPCMFrameCount; + ma_dr_flac_container container; + ma_uint32 seekpointCount; + ma_dr_flac_frame currentFLACFrame; + ma_uint64 currentPCMFrame; + ma_uint64 firstFLACFramePosInBytes; + ma_dr_flac__memory_stream memoryStream; + ma_int32* pDecodedSamples; + ma_dr_flac_seekpoint* pSeekpoints; + void* _oggbs; + ma_bool32 _noSeekTableSeek : 1; + ma_bool32 _noBinarySearchSeek : 1; + ma_bool32 _noBruteForceSeek : 1; + ma_dr_flac_bs bs; + ma_uint8 pExtraData[1]; +} ma_dr_flac; +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +typedef struct +{ + ma_uint32 countRemaining; + const char* pRunningData; +} ma_dr_flac_vorbis_comment_iterator; +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); +typedef struct +{ + ma_uint32 countRemaining; + const char* pRunningData; +} ma_dr_flac_cuesheet_track_iterator; +typedef struct +{ + ma_uint64 offset; + ma_uint8 index; + ma_uint8 reserved[3]; +} ma_dr_flac_cuesheet_track_index; +typedef struct +{ + ma_uint64 offset; + ma_uint8 trackNumber; + char ISRC[12]; + ma_bool8 isAudio; + ma_bool8 preEmphasis; + ma_uint8 indexCount; + const ma_dr_flac_cuesheet_track_index* pIndexPoints; +} ma_dr_flac_cuesheet_track; +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); +#ifdef __cplusplus +} +#endif +#endif +/* dr_flac_h end */ +#endif /* MA_NO_FLAC */ + +#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) +/* dr_mp3_h begin */ +#ifndef ma_dr_mp3_h +#define ma_dr_mp3_h +#ifdef __cplusplus +extern "C" { +#endif +#define MA_DR_MP3_STRINGIFY(x) #x +#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) +#define MA_DR_MP3_VERSION_MAJOR 0 +#define MA_DR_MP3_VERSION_MINOR 6 +#define MA_DR_MP3_VERSION_REVISION 38 +#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) +#include +#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_mp3_version_string(void); +typedef struct +{ + int frame_bytes, channels, hz, layer, bitrate_kbps; +} ma_dr_mp3dec_frame_info; +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + ma_uint8 header[4], reserv_buf[511]; +} ma_dr_mp3dec; +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); +typedef enum +{ + ma_dr_mp3_seek_origin_start, + ma_dr_mp3_seek_origin_current +} ma_dr_mp3_seek_origin; +typedef struct +{ + ma_uint64 seekPosInBytes; + ma_uint64 pcmFrameIndex; + ma_uint16 mp3FramesToDiscard; + ma_uint16 pcmFramesToDiscard; +} ma_dr_mp3_seek_point; +typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); +typedef struct +{ + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_dr_mp3_config; +typedef struct +{ + ma_dr_mp3dec decoder; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_dr_mp3_read_proc onRead; + ma_dr_mp3_seek_proc onSeek; + void* pUserData; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 mp3FrameChannels; + ma_uint32 mp3FrameSampleRate; + ma_uint32 pcmFramesConsumedInMP3Frame; + ma_uint32 pcmFramesRemainingInMP3Frame; + ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; + ma_uint64 currentPCMFrame; + ma_uint64 streamCursor; + ma_dr_mp3_seek_point* pSeekPoints; + ma_uint32 seekPointCount; + size_t dataSize; + size_t dataCapacity; + size_t dataConsumed; + ma_uint8* pData; + ma_bool32 atEnd : 1; + struct + { + const ma_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; +} ma_dr_mp3; +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#endif +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +#ifdef __cplusplus +} +#endif +#endif +/* dr_mp3_h end */ +#endif /* MA_NO_MP3 */ + + +/************************************************************************************************************************************************************** + +Decoding + +**************************************************************************************************************************************************************/ +#ifndef MA_NO_DECODING + +static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + MA_ASSERT(pDecoder != NULL); + + return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) +{ + MA_ASSERT(pDecoder != NULL); + + return pDecoder->onSeek(pDecoder, byteOffset, origin); +} + +static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + + if (pDecoder->onTell == NULL) { + return MA_NOT_IMPLEMENTED; + } + + return pDecoder->onTell(pDecoder, pCursor); +} + + +MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) +{ + ma_decoding_backend_config config; + + MA_ZERO_OBJECT(&config); + config.preferredFormat = preferredFormat; + config.seekPointCount = seekPointCount; + + return config; +} + + +MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) +{ + ma_decoder_config config; + MA_ZERO_OBJECT(&config); + config.format = outputFormat; + config.channels = outputChannels; + config.sampleRate = outputSampleRate; + config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ + config.encodingFormat = ma_encoding_format_unknown; + + /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ + + return config; +} + +MA_API ma_decoder_config ma_decoder_config_init_default() +{ + return ma_decoder_config_init(ma_format_unknown, 0, 0); +} + +MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) +{ + ma_decoder_config config; + if (pConfig != NULL) { + config = *pConfig; + } else { + MA_ZERO_OBJECT(&config); + } + + return config; +} + +static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) +{ + ma_result result; + ma_data_converter_config converterConfig; + ma_format internalFormat; + ma_uint32 internalChannels; + ma_uint32 internalSampleRate; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pConfig != NULL); + + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal data format. */ + } + + + /* Make sure we're not asking for too many channels. */ + if (pConfig->channels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ + if (internalChannels > MA_MAX_CHANNELS) { + return MA_INVALID_ARGS; + } + + + /* Output format. */ + if (pConfig->format == ma_format_unknown) { + pDecoder->outputFormat = internalFormat; + } else { + pDecoder->outputFormat = pConfig->format; + } + + if (pConfig->channels == 0) { + pDecoder->outputChannels = internalChannels; + } else { + pDecoder->outputChannels = pConfig->channels; + } + + if (pConfig->sampleRate == 0) { + pDecoder->outputSampleRate = internalSampleRate; + } else { + pDecoder->outputSampleRate = pConfig->sampleRate; + } + + converterConfig = ma_data_converter_config_init( + internalFormat, pDecoder->outputFormat, + internalChannels, pDecoder->outputChannels, + internalSampleRate, pDecoder->outputSampleRate + ); + converterConfig.pChannelMapIn = internalChannelMap; + converterConfig.pChannelMapOut = pConfig->pChannelMap; + converterConfig.channelMixMode = pConfig->channelMixMode; + converterConfig.ditherMode = pConfig->ditherMode; + converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ + converterConfig.resampling = pConfig->resampling; + + result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); + if (result != MA_SUCCESS) { + return result; + } + + /* + Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll + need this if the data converter does not support calculation of the required input frame count. To + determine support for this we'll just run a test. + */ + { + ma_uint64 unused; + + result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); + if (result != MA_SUCCESS) { + /* + We were unable to calculate the required input frame count which means we'll need to use + a heap-allocated cache. + */ + ma_uint64 inputCacheCapSizeInBytes; + + pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); + + /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ + inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); + if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ + if (pDecoder->pInputCache == NULL) { + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + } + } + + return MA_SUCCESS; +} + + + +static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_seek_bytes(pDecoder, offset, origin); +} + +static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) +{ + ma_decoder* pDecoder = (ma_decoder*)pUserData; + MA_ASSERT(pDecoder != NULL); + + return ma_decoder_tell_bytes(pDecoder, pCursor); +} + + +static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInit == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFile == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFileW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitMemory == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + + + +static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } else { + /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ + result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; /* Failed to seek back to the start. */ + } + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + + +/* WAV */ +#ifdef ma_dr_wav_h +#define MA_HAS_WAV + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32, s16 or s32. */ +#if !defined(MA_NO_WAV) + ma_dr_wav dr; +#endif +} ma_wav; + +MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); +MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); +MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); + + +static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); +} + +static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); +} + +static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_wav_ds_vtable = +{ + ma_wav_ds_read, + ma_wav_ds_seek, + ma_wav_ds_get_data_format, + ma_wav_ds_get_cursor, + ma_wav_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_WAV) +static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_wav* pWav = (ma_wav*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pWav != NULL); + + result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_wav* pWav = (ma_wav*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pWav != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_wav_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWav); + pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { + pWav->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_wav_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pWav->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_wav_post_init(ma_wav* pWav) +{ + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case MA_DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case MA_DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->onTell = onTell; + pWav->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) +{ + ma_result result; + + result = ma_wav_init_internal(pConfig, pWav); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); + if (wavResult != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_wav_post_init(pWav); + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_WAV) + { + ma_dr_wav_uninit(&pWav->dr); + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pWav->ds); +} + +MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_s32: + { + totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); + } break; + + /* Fallback to a raw read. */ + case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ + default: + { + totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); + } break; + } + + /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) +{ + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_bool32 wavResult; + + wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); + if (wavResult != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pWav == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pWav->format; + } + + #if !defined(MA_NO_WAV) + { + if (pChannels != NULL) { + *pChannels = pWav->dr.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pWav->dr.sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_WAV) + { + ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ + } + + return MA_SUCCESS; + } + #else + { + /* wav is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_wav* pWav; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); + if (result != MA_SUCCESS) { + ma_free(pWav, pAllocationCallbacks); + return result; + } + + *ppBackend = pWav; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_wav* pWav = (ma_wav*)pBackend; + + (void)pUserData; + + ma_wav_uninit(pWav, pAllocationCallbacks); + ma_free(pWav, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = +{ + ma_decoding_backend_init__wav, + ma_decoding_backend_init_file__wav, + ma_decoding_backend_init_file_w__wav, + ma_decoding_backend_init_memory__wav, + ma_decoding_backend_uninit__wav +}; + +static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_wav_h */ + +/* FLAC */ +#ifdef ma_dr_flac_h +#define MA_HAS_FLAC + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32, s16 or s32. */ +#if !defined(MA_NO_FLAC) + ma_dr_flac* dr; +#endif +} ma_flac; + +MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); +MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); +MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); +MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); + + +static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); +} + +static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); +} + +static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_flac_ds_vtable = +{ + ma_flac_ds_read, + ma_flac_ds_seek, + ma_flac_ds_get_data_format, + ma_flac_ds_get_cursor, + ma_flac_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_FLAC) +static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_flac* pFlac = (ma_flac*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pFlac != NULL); + + result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_flac* pFlac = (ma_flac*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pFlac != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_flac_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pFlac); + pFlac->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { + pFlac->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_flac_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pFlac->onRead = onRead; + pFlac->onSeek = onSeek; + pFlac->onTell = onTell; + pFlac->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) +{ + ma_result result; + + result = ma_flac_init_internal(pConfig, pFlac); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_FLAC) + { + pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); + if (pFlac->dr == NULL) { + return MA_INVALID_FILE; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFlac == NULL) { + return; + } + + (void)pAllocationCallbacks; + + #if !defined(MA_NO_FLAC) + { + ma_dr_flac_close(pFlac->dr); + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pFlac->ds); +} + +MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_s32: + { + totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); + } break; + + case ma_format_u8: + case ma_format_s24: + case ma_format_unknown: + default: + { + return MA_INVALID_OPERATION; + }; + } + + /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) +{ + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + ma_bool32 flacResult; + + flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); + if (flacResult != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pFlac == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pFlac->format; + } + + #if !defined(MA_NO_FLAC) + { + if (pChannels != NULL) { + *pChannels = pFlac->dr->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pFlac->dr->sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); + } + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + *pCursor = pFlac->dr->currentPCMFrame; + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pFlac == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_FLAC) + { + *pLength = pFlac->dr->totalPCMFrameCount; + + return MA_SUCCESS; + } + #else + { + /* flac is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_flac* pFlac; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); + if (pFlac == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); + if (result != MA_SUCCESS) { + ma_free(pFlac, pAllocationCallbacks); + return result; + } + + *ppBackend = pFlac; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_flac* pFlac = (ma_flac*)pBackend; + + (void)pUserData; + + ma_flac_uninit(pFlac, pAllocationCallbacks); + ma_free(pFlac, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = +{ + ma_decoding_backend_init__flac, + ma_decoding_backend_init_file__flac, + ma_decoding_backend_init_file_w__flac, + ma_decoding_backend_init_memory__flac, + ma_decoding_backend_uninit__flac +}; + +static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_flac_h */ + +/* MP3 */ +#ifdef ma_dr_mp3_h +#define MA_HAS_MP3 + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_format format; /* Can be f32 or s16. */ +#if !defined(MA_NO_MP3) + ma_dr_mp3 dr; + ma_uint32 seekPointCount; + ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ +#endif +} ma_mp3; + +MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); +MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); +MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); + + +static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); +} + +static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); +} + +static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_mp3_ds_vtable = +{ + ma_mp3_ds_read, + ma_mp3_ds_seek, + ma_mp3_ds_get_data_format, + ma_mp3_ds_get_cursor, + ma_mp3_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +#if !defined(MA_NO_MP3) +static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_mp3* pMP3 = (ma_mp3*)pUserData; + ma_result result; + size_t bytesRead; + + MA_ASSERT(pMP3 != NULL); + + result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); + (void)result; + + return bytesRead; +} + +static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +{ + ma_mp3* pMP3 = (ma_mp3*)pUserData; + ma_result result; + ma_seek_origin maSeekOrigin; + + MA_ASSERT(pMP3 != NULL); + + maSeekOrigin = ma_seek_origin_start; + if (origin == ma_dr_mp3_seek_origin_current) { + maSeekOrigin = ma_seek_origin_current; + } + + result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + + return MA_TRUE; +} +#endif + +static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMP3); + pMP3->format = ma_format_f32; /* f32 by default. */ + + if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { + pMP3->format = pConfig->preferredFormat; + } else { + /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 mp3Result; + ma_uint32 seekPointCount = 0; + ma_dr_mp3_seek_point* pSeekPoints = NULL; + + MA_ASSERT(pMP3 != NULL); + MA_ASSERT(pConfig != NULL); + + seekPointCount = pConfig->seekPointCount; + if (seekPointCount > 0) { + pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + if (pSeekPoints == NULL) { + return MA_OUT_OF_MEMORY; + } + } + + mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); + return MA_ERROR; + } + + mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + if (mp3Result != MA_TRUE) { + ma_free(pSeekPoints, pAllocationCallbacks); + return MA_ERROR; + } + + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + + return MA_SUCCESS; +} + +static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + + result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->onTell = onTell; + pMP3->pReadSeekTellUserData = pReadSeekTellUserData; + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) +{ + ma_result result; + + result = ma_mp3_init_internal(pConfig, pMP3); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); + if (mp3Result != MA_TRUE) { + return MA_INVALID_FILE; + } + + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) { + return; + } + + #if !defined(MA_NO_MP3) + { + ma_dr_mp3_uninit(&pMP3->dr); + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ + ma_free(pMP3->pSeekPoints, pAllocationCallbacks); + + ma_data_source_uninit(&pMP3->ds); +} + +MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + + ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); + + switch (format) + { + case ma_format_f32: + { + totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); + } break; + + case ma_format_s16: + { + totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); + } break; + + case ma_format_u8: + case ma_format_s24: + case ma_format_s32: + case ma_format_unknown: + default: + { + return MA_INVALID_OPERATION; + }; + } + + /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) +{ + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + ma_bool32 mp3Result; + + mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); + if (mp3Result != MA_TRUE) { + return MA_ERROR; + } + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pMP3 == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pMP3->format; + } + + #if !defined(MA_NO_MP3) + { + if (pChannels != NULL) { + *pChannels = pMP3->dr.channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pMP3->dr.sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); + } + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + *pCursor = pMP3->dr.currentPCMFrame; + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pMP3 == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_MP3) + { + *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); + + return MA_SUCCESS; + } + #else + { + /* mp3 is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_mp3* pMP3; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); + if (pMP3 == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); + if (result != MA_SUCCESS) { + ma_free(pMP3, pAllocationCallbacks); + return result; + } + + *ppBackend = pMP3; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_mp3* pMP3 = (ma_mp3*)pBackend; + + (void)pUserData; + + ma_mp3_uninit(pMP3, pAllocationCallbacks); + ma_free(pMP3, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = +{ + ma_decoding_backend_init__mp3, + ma_decoding_backend_init_file__mp3, + ma_decoding_backend_init_file_w__mp3, + ma_decoding_backend_init_memory__mp3, + ma_decoding_backend_uninit__mp3 +}; + +static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_mp3_h */ + +/* Vorbis */ +#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define MA_HAS_VORBIS + +/* The size in bytes of each chunk of data to read from the Vorbis stream. */ +#define MA_VORBIS_DATA_CHUNK_SIZE 4096 + +typedef struct +{ + ma_data_source_base ds; + ma_read_proc onRead; + ma_seek_proc onSeek; + ma_tell_proc onTell; + void* pReadSeekTellUserData; + ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ + ma_format format; /* Only f32 is allowed with stb_vorbis. */ + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; +#if !defined(MA_NO_VORBIS) + stb_vorbis* stb; + ma_bool32 usingPushMode; + struct + { + ma_uint8* pData; + size_t dataSize; + size_t dataCapacity; + size_t audioStartOffsetInBytes; + ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ + ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ + float** ppPacketData; + } push; +#endif +} ma_stbvorbis; + +MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); +MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); +MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); +MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); + + +static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); +} + +static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); +} + +static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = +{ + ma_stbvorbis_ds_read, + ma_stbvorbis_ds_seek, + ma_stbvorbis_ds_get_data_format, + ma_stbvorbis_ds_get_cursor, + ma_stbvorbis_ds_get_length, + NULL, /* onSetLooping */ + 0 +}; + + +static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + (void)pConfig; + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pVorbis); + pVorbis->format = ma_format_f32; /* Only supporting f32. */ + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base data source. */ + } + + return MA_SUCCESS; +} + +#if !defined(MA_NO_VORBIS) +static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) +{ + stb_vorbis_info info; + + MA_ASSERT(pVorbis != NULL); + + info = stb_vorbis_get_info(pVorbis->stb); + + pVorbis->channels = info.channels; + pVorbis->sampleRate = info.sample_rate; + + return MA_SUCCESS; +} + +static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) +{ + ma_result result; + stb_vorbis* stb; + size_t dataSize = 0; + size_t dataCapacity = 0; + ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ + + for (;;) { + int vorbisError; + int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ + size_t bytesRead; + ma_uint8* pNewData; + + /* Allocate memory for the new chunk. */ + dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pData = pNewData; + + /* Read in the next chunk. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); + dataSize += bytesRead; + + if (result != MA_SUCCESS) { + ma_free(pData, &pVorbis->allocationCallbacks); + return result; + } + + /* We have a maximum of 31 bits with stb_vorbis. */ + if (dataSize > INT_MAX) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_TOO_BIG; + } + + stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); + if (stb != NULL) { + /* + Successfully opened the Vorbis decoder. We might have some leftover unprocessed + data so we'll need to move that down to the front. + */ + dataSize -= (size_t)consumedDataSize; /* Consume the data. */ + MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); + + /* + We need to track the start point so we can seek back to the start of the audio + data when seeking. + */ + pVorbis->push.audioStartOffsetInBytes = consumedDataSize; + + break; + } else { + /* Failed to open the decoder. */ + if (vorbisError == VORBIS_need_more_data) { + continue; + } else { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ + } + } + } + + MA_ASSERT(stb != NULL); + pVorbis->stb = stb; + pVorbis->push.pData = pData; + pVorbis->push.dataSize = dataSize; + pVorbis->push.dataCapacity = dataCapacity; + + return MA_SUCCESS; +} +#endif + +MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + if (onRead == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ + } + + pVorbis->onRead = onRead; + pVorbis->onSeek = onSeek; + pVorbis->onTell = onTell; + pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; + ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); + + #if !defined(MA_NO_VORBIS) + { + /* + stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the + pushing API. In order for us to be able to successfully initialize the decoder we need to + supply it with enough data. We need to keep loading data until we have enough. + */ + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + pVorbis->usingPushMode = MA_TRUE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_VORBIS) + { + (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ + + /* We can use stb_vorbis' pull mode for file based streams. */ + pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); + if (pVorbis->stb == NULL) { + return MA_INVALID_FILE; + } + + pVorbis->usingPushMode = MA_FALSE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pFilePath; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) +{ + ma_result result; + + result = ma_stbvorbis_init_internal(pConfig, pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + #if !defined(MA_NO_VORBIS) + { + (void)pAllocationCallbacks; + + /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ + if (dataSize > INT_MAX) { + return MA_TOO_BIG; + } + + pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); + if (pVorbis->stb == NULL) { + return MA_INVALID_FILE; + } + + pVorbis->usingPushMode = MA_FALSE; + + result = ma_stbvorbis_post_init(pVorbis); + if (result != MA_SUCCESS) { + stb_vorbis_close(pVorbis->stb); + return result; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. */ + (void)pData; + (void)dataSize; + (void)pAllocationCallbacks; + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pVorbis == NULL) { + return; + } + + #if !defined(MA_NO_VORBIS) + { + stb_vorbis_close(pVorbis->stb); + + /* We'll have to clear some memory if we're using push mode. */ + if (pVorbis->usingPushMode) { + ma_free(pVorbis->push.pData, pAllocationCallbacks); + } + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + } + #endif + + ma_data_source_uninit(&pVorbis->ds); +} + +MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + /* We always use floating point format. */ + ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ + ma_uint64 totalFramesRead = 0; + ma_format format; + ma_uint32 channels; + + ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); + + if (format == ma_format_f32) { + /* We read differently depending on whether or not we're using push mode. */ + if (pVorbis->usingPushMode) { + /* Push mode. This is the complex case. */ + float* pFramesOutF32 = (float*)pFramesOut; + + while (totalFramesRead < frameCount) { + /* The first thing to do is read from any already-cached frames. */ + ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ + + /* The output pointer can be null in which case we just treate it as a seek. */ + if (pFramesOut != NULL) { + ma_uint64 iFrame; + for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { + ma_uint32 iChannel; + for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { + pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; + } + + pFramesOutF32 += pVorbis->channels; + } + } + + /* Update pointers and counters. */ + pVorbis->push.framesConsumed += framesToReadFromCache; + pVorbis->push.framesRemaining -= framesToReadFromCache; + totalFramesRead += framesToReadFromCache; + + /* Don't bother reading any more frames right now if we've just finished loading. */ + if (totalFramesRead == frameCount) { + break; + } + + MA_ASSERT(pVorbis->push.framesRemaining == 0); + + /* Getting here means we've run out of cached frames. We'll need to load some more. */ + for (;;) { + int samplesRead = 0; + int consumedDataSize; + + /* We need to case dataSize to an int, so make sure we can do it safely. */ + if (pVorbis->push.dataSize > INT_MAX) { + break; /* Too big. */ + } + + consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); + if (consumedDataSize != 0) { + /* Successfully decoded a Vorbis frame. Consume the data. */ + pVorbis->push.dataSize -= (size_t)consumedDataSize; + MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); + + pVorbis->push.framesConsumed = 0; + pVorbis->push.framesRemaining = samplesRead; + + break; + } else { + /* Not enough data. Read more. */ + size_t bytesRead; + + /* Expand the data buffer if necessary. */ + if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { + size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; + ma_uint8* pNewData; + + pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + result = MA_OUT_OF_MEMORY; + break; + } + + pVorbis->push.pData = pNewData; + pVorbis->push.dataCapacity = newCap; + } + + /* We should have enough room to load some data. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); + pVorbis->push.dataSize += bytesRead; + + if (result != MA_SUCCESS) { + break; /* Failed to read any data. Get out. */ + } + } + } + + /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ + if (result != MA_SUCCESS) { + break; + } + } + } else { + /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ + while (totalFramesRead < frameCount) { + ma_uint64 framesRemaining = (frameCount - totalFramesRead); + int framesRead; + + if (framesRemaining > INT_MAX) { + framesRemaining = INT_MAX; + } + + framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ + totalFramesRead += framesRead; + + if (framesRead < (int)framesRemaining) { + break; /* Nothing left to read. Get out. */ + } + } + } + } else { + result = MA_INVALID_ARGS; + } + + pVorbis->cursor += totalFramesRead; + + if (totalFramesRead == 0) { + result = MA_AT_END; + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + if (result == MA_SUCCESS && totalFramesRead == 0) { + result = MA_AT_END; + } + + return result; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)pFramesOut; + (void)frameCount; + (void)pFramesRead; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) +{ + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + /* Different seeking methods depending on whether or not we're using push mode. */ + if (pVorbis->usingPushMode) { + /* Push mode. This is the complex case. */ + ma_result result; + float buffer[4096]; + + /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ + if (frameIndex < pVorbis->cursor) { + if (frameIndex > 0x7FFFFFFF) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } + + /* + This is wildly inefficient due to me having trouble getting sample exact seeking working + robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work + perfectly is to reinitialize the decoder. Note that we only enter this path when seeking + backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. + */ + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); + + MA_ZERO_OBJECT(&pVorbis->push); + + /* Seek to the start of the file. */ + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + /* At this point we should be sitting on the first frame. */ + pVorbis->cursor = 0; + } + + /* We're just brute-forcing this for now. */ + while (pVorbis->cursor < frameIndex) { + ma_uint64 framesRead; + ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; + if (framesToRead > (frameIndex - pVorbis->cursor)) { + framesToRead = (frameIndex - pVorbis->cursor); + } + + result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + } + } else { + /* Pull mode. This is the simple case. */ + int vorbisResult; + + if (frameIndex > UINT_MAX) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } + + vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ + if (vorbisResult == 0) { + return MA_ERROR; /* See failed. */ + } + + pVorbis->cursor = frameIndex; + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + + (void)frameIndex; + + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* Defaults for safety. */ + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + if (pChannels != NULL) { + *pChannels = 0; + } + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pVorbis == NULL) { + return MA_INVALID_OPERATION; + } + + if (pFormat != NULL) { + *pFormat = pVorbis->format; + } + + #if !defined(MA_NO_VORBIS) + { + if (pChannels != NULL) { + *pChannels = pVorbis->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pVorbis->sampleRate; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + *pCursor = pVorbis->cursor; + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + +MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; /* Safety. */ + + if (pVorbis == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_VORBIS) + { + if (pVorbis->usingPushMode) { + *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ + } else { + *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); + } + + return MA_SUCCESS; + } + #else + { + /* vorbis is disabled. Should never hit this since initialization would have failed. */ + MA_ASSERT(MA_FALSE); + return MA_NOT_IMPLEMENTED; + } + #endif +} + + +static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) +{ + ma_result result; + ma_stbvorbis* pVorbis; + + (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ + + /* For now we're just allocating the decoder backend on the heap. */ + pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); + if (pVorbis == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); + if (result != MA_SUCCESS) { + ma_free(pVorbis, pAllocationCallbacks); + return result; + } + + *ppBackend = pVorbis; + + return MA_SUCCESS; +} + +static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; + + (void)pUserData; + + ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); + ma_free(pVorbis, pAllocationCallbacks); +} + +static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = +{ + ma_decoding_backend_init__stbvorbis, + ma_decoding_backend_init_file__stbvorbis, + NULL, /* onInitFileW() */ + ma_decoding_backend_init_memory__stbvorbis, + ma_decoding_backend_uninit__stbvorbis +}; + +static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ + + + +static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + MA_ASSERT(pDecoder != NULL); + + if (pConfig != NULL) { + return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); + } else { + pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); + return MA_SUCCESS; + } +} + +static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); +} + +static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); +} + +static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); +} + +static ma_data_source_vtable g_ma_decoder_data_source_vtable = +{ + ma_decoder__data_source_on_read, + ma_decoder__data_source_on_seek, + ma_decoder__data_source_on_get_data_format, + ma_decoder__data_source_on_get_cursor, + ma_decoder__data_source_on_get_length, + NULL, /* onSetLooping */ + 0 +}; + +static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + MA_ASSERT(pConfig != NULL); + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDecoder); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->onRead = onRead; + pDecoder->onSeek = onSeek; + pDecoder->onTell = onTell; + pDecoder->pUserData = pUserData; + + result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); + if (result != MA_SUCCESS) { + ma_data_source_uninit(&pDecoder->ds); + return result; + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__init_data_converter(pDecoder, pConfig); + + /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + return result; + } + + return result; +} + + +static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + /* Silence some warnings in the case that we don't have any decoder backends enabled. */ + (void)onRead; + (void)onSeek; + (void)pUserData; + + + /* If we've specified a specific encoding type, try that first. */ + if (pConfig->encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (pConfig->encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (pConfig->encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (pConfig->encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(pConfig, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (pConfig->encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); + } + #endif + + /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (pConfig->encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); + if (result != MA_SUCCESS) { + onSeek(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder__postinit(pConfig, pDecoder); +} + +MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_decoder_config config; + ma_result result; + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); +} + + +static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + size_t bytesRemaining; + + MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); + + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + + bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesRemaining == 0) { + return MA_AT_END; + } + + if (bytesToRead > 0) { + MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); + pDecoder->data.memory.currentReadPos += bytesToRead; + } + + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) +{ + if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { + return MA_BAD_SEEK; + } + + if (origin == ma_seek_origin_current) { + if (byteOffset > 0) { + if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { + byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ + } + + pDecoder->data.memory.currentReadPos += (size_t)byteOffset; + } else { + if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ + } + + pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; + } + } else { + if (origin == ma_seek_origin_end) { + if (byteOffset < 0) { + byteOffset = -byteOffset; + } + + if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { + pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ + } else { + pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; + } + } else { + if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { + pDecoder->data.memory.currentReadPos = (size_t)byteOffset; + } else { + pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ + } + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pCursor != NULL); + + *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + pDecoder->data.memory.pData = (const ma_uint8*)pData; + pDecoder->data.memory.dataSize = dataSize; + pDecoder->data.memory.currentReadPos = 0; + + (void)pConfig; + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* Use trial and error for stock decoders. */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ + result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + + +#if defined(MA_HAS_WAV) || \ + defined(MA_HAS_MP3) || \ + defined(MA_HAS_FLAC) || \ + defined(MA_HAS_VORBIS) || \ + defined(MA_HAS_OPUS) +#define MA_HAS_PATH_API +#endif + +#if defined(MA_HAS_PATH_API) +static const char* ma_path_file_name(const char* path) +{ + const char* fileName; + + if (path == NULL) { + return NULL; + } + + fileName = path; + + /* We just loop through the path until we find the last slash. */ + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + /* At this point the file name is sitting on a slash, so just move forward. */ + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + +static const wchar_t* ma_path_file_name_w(const wchar_t* path) +{ + const wchar_t* fileName; + + if (path == NULL) { + return NULL; + } + + fileName = path; + + /* We just loop through the path until we find the last slash. */ + while (path[0] != '\0') { + if (path[0] == '/' || path[0] == '\\') { + fileName = path; + } + + path += 1; + } + + /* At this point the file name is sitting on a slash, so just move forward. */ + while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { + fileName += 1; + } + + return fileName; +} + + +static const char* ma_path_extension(const char* path) +{ + const char* extension; + const char* lastOccurance; + + if (path == NULL) { + path = ""; + } + + extension = ma_path_file_name(path); + lastOccurance = NULL; + + /* Just find the last '.' and return. */ + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + +static const wchar_t* ma_path_extension_w(const wchar_t* path) +{ + const wchar_t* extension; + const wchar_t* lastOccurance; + + if (path == NULL) { + path = L""; + } + + extension = ma_path_file_name_w(path); + lastOccurance = NULL; + + /* Just find the last '.' and return. */ + while (extension[0] != '\0') { + if (extension[0] == '.') { + extension += 1; + lastOccurance = extension; + } + + extension += 1; + } + + return (lastOccurance != NULL) ? lastOccurance : extension; +} + + +static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) +{ + const char* ext1; + const char* ext2; + + if (path == NULL || extension == NULL) { + return MA_FALSE; + } + + ext1 = extension; + ext2 = ma_path_extension(path); + +#if defined(_MSC_VER) || defined(__DMC__) + return _stricmp(ext1, ext2) == 0; +#else + return strcasecmp(ext1, ext2) == 0; +#endif +} + +static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) +{ + const wchar_t* ext1; + const wchar_t* ext2; + + if (path == NULL || extension == NULL) { + return MA_FALSE; + } + + ext1 = extension; + ext2 = ma_path_extension_w(path); + +#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) + return _wcsicmp(ext1, ext2) == 0; +#else + /* + I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This + isn't the most efficient way to do it, but it should work OK. + */ + { + char ext1MB[4096]; + char ext2MB[4096]; + const wchar_t* pext1 = ext1; + const wchar_t* pext2 = ext2; + mbstate_t mbs1; + mbstate_t mbs2; + + MA_ZERO_OBJECT(&mbs1); + MA_ZERO_OBJECT(&mbs2); + + if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { + return MA_FALSE; + } + if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { + return MA_FALSE; + } + + return strcasecmp(ext1MB, ext2MB) == 0; + } +#endif +} +#endif /* MA_HAS_PATH_API */ + + + +static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) +{ + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pBufferOut != NULL); + + return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); +} + +static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) +{ + MA_ASSERT(pDecoder != NULL); + + return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); +} + +static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) +{ + MA_ASSERT(pDecoder != NULL); + + return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); +} + +static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->data.vfs.pVFS = pVFS; + pDecoder->data.vfs.file = file; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(&config, pDecoder); + } + #endif + + /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ + if (result != MA_SUCCESS) { + result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); + } else { + result = ma_decoder__postinit(&config, pDecoder); + } + + if (result != MA_SUCCESS) { + if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ + ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); + } + + return result; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + if (result != MA_SUCCESS) { + return result; + } + + pDecoder->data.vfs.pVFS = pVFS; + pDecoder->data.vfs.file = file; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis__internal(&config, pDecoder); + } + #endif + + /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + if (result != MA_SUCCESS) { + result = ma_decoder_init_custom__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3__internal(&config, pDecoder); + if (result != MA_SUCCESS) { + ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); + } + } + #endif + } + + /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ + if (result != MA_SUCCESS) { + result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); + } else { + result = ma_decoder__postinit(&config, pDecoder); + } + + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); + return result; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + } + + if (pDecoder->onRead == ma_decoder__on_read_vfs) { + ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); + pDecoder->data.vfs.file = NULL; + } + + ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); + ma_data_source_uninit(&pDecoder->ds); + + if (pDecoder->pInputCache != NULL) { + ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesReadOut; + void* pRunningFramesOut; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend == NULL) { + return MA_INVALID_OPERATION; + } + + /* Fast path. */ + if (pDecoder->converter.isPassthrough) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); + } else { + /* + Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we + need to run through each sample because we need to ensure it's internal cache is updated. + */ + if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); + } else { + /* Slow path. Need to run everything through the data converter. */ + ma_format internalFormat; + ma_uint32 internalChannels; + + totalFramesReadOut = 0; + pRunningFramesOut = pFramesOut; + + result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal format and channel count. */ + } + + /* + We run a different path depending on whether or not we are using a heap-allocated + intermediary buffer or not. If the data converter does not support the calculation of + the required number of input frames, we'll use the heap-allocated path. Otherwise we'll + use the stack-allocated path. + */ + if (pDecoder->pInputCache != NULL) { + /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ + while (totalFramesReadOut < frameCount) { + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + + /* If there's any data available in the cache, that needs to get processed first. */ + if (pDecoder->inputCacheRemaining > 0) { + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { + framesToReadThisIterationIn = pDecoder->inputCacheRemaining; + } + + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + pDecoder->inputCacheConsumed += framesToReadThisIterationIn; + pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; + + totalFramesReadOut += framesToReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + + /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ + if (pDecoder->inputCacheRemaining == 0) { + pDecoder->inputCacheConsumed = 0; + + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); + if (result != MA_SUCCESS) { + break; + } + } + } + } else { + /* We have a way of determining the required number of input frames so just use the stack. */ + while (totalFramesReadOut < frameCount) { + ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ + ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); + ma_uint64 framesToReadThisIterationIn; + ma_uint64 framesReadThisIterationIn; + ma_uint64 framesToReadThisIterationOut; + ma_uint64 framesReadThisIterationOut; + ma_uint64 requiredInputFrameCount; + + framesToReadThisIterationOut = (frameCount - totalFramesReadOut); + framesToReadThisIterationIn = framesToReadThisIterationOut; + if (framesToReadThisIterationIn > intermediaryBufferCap) { + framesToReadThisIterationIn = intermediaryBufferCap; + } + + ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); + if (framesToReadThisIterationIn > requiredInputFrameCount) { + framesToReadThisIterationIn = requiredInputFrameCount; + } + + if (requiredInputFrameCount > 0) { + result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); + } else { + framesReadThisIterationIn = 0; + } + + /* + At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any + input frames, we still want to try processing frames because there may some output frames generated from cached input data. + */ + framesReadThisIterationOut = framesToReadThisIterationOut; + result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); + if (result != MA_SUCCESS) { + break; + } + + totalFramesReadOut += framesReadThisIterationOut; + + if (pRunningFramesOut != NULL) { + pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); + } + + if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { + break; /* We're done. */ + } + } + } + } + } + + pDecoder->readPointerInPCMFrames += totalFramesReadOut; + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesReadOut; + } + + if (result == MA_SUCCESS && totalFramesReadOut == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + ma_result result; + ma_uint64 internalFrameIndex; + ma_uint32 internalSampleRate; + ma_uint64 currentFrameIndex; + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ + } + + if (internalSampleRate == pDecoder->outputSampleRate) { + internalFrameIndex = frameIndex; + } else { + internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); + } + + /* Only seek if we're requesting a different frame to what we're currently sitting on. */ + ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); + if (currentFrameIndex != internalFrameIndex) { + result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); + if (result == MA_SUCCESS) { + pDecoder->readPointerInPCMFrames = frameIndex; + } + + /* Reset the data converter so that any cached data in the resampler is cleared. */ + ma_data_converter_reset(&pDecoder->converter); + } + + return result; + } + + /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ + return MA_INVALID_ARGS; +} + +MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pFormat != NULL) { + *pFormat = pDecoder->outputFormat; + } + + if (pChannels != NULL) { + *pChannels = pDecoder->outputChannels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pDecoder->outputSampleRate; + } + + if (pChannelMap != NULL) { + ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = pDecoder->readPointerInPCMFrames; + + return MA_SUCCESS; +} + +MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + if (pDecoder->pBackend != NULL) { + ma_result result; + ma_uint64 internalLengthInPCMFrames; + ma_uint32 internalSampleRate; + + result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal length. */ + } + + result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the internal sample rate. */ + } + + if (internalSampleRate == pDecoder->outputSampleRate) { + *pLength = internalLengthInPCMFrames; + } else { + *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); + } + + return MA_SUCCESS; + } else { + return MA_NO_BACKEND; + } +} + +MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) +{ + ma_result result; + ma_uint64 totalFrameCount; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDecoder == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; + } + + if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { + *pAvailableFrames = 0; + } else { + *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; + } + + return MA_SUCCESS; +} + + +static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_result result; + ma_uint64 totalFrameCount; + ma_uint64 bpf; + ma_uint64 dataCapInFrames; + void* pPCMFramesOut; + + MA_ASSERT(pDecoder != NULL); + + totalFrameCount = 0; + bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + + /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ + dataCapInFrames = 0; + pPCMFramesOut = NULL; + for (;;) { + ma_uint64 frameCountToTryReading; + ma_uint64 framesJustRead; + + /* Make room if there's not enough. */ + if (totalFrameCount == dataCapInFrames) { + void* pNewPCMFramesOut; + ma_uint64 newDataCapInFrames = dataCapInFrames*2; + if (newDataCapInFrames == 0) { + newDataCapInFrames = 4096; + } + + if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + return MA_TOO_BIG; + } + + pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); + if (pNewPCMFramesOut == NULL) { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + dataCapInFrames = newDataCapInFrames; + pPCMFramesOut = pNewPCMFramesOut; + } + + frameCountToTryReading = dataCapInFrames - totalFrameCount; + MA_ASSERT(frameCountToTryReading > 0); + + result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); + totalFrameCount += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + + if (framesJustRead < frameCountToTryReading) { + break; + } + } + + + if (pConfigOut != NULL) { + pConfigOut->format = pDecoder->outputFormat; + pConfigOut->channels = pDecoder->outputChannels; + pConfigOut->sampleRate = pDecoder->outputSampleRate; + } + + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = pPCMFramesOut; + } else { + ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); + } + + if (pFrameCountOut != NULL) { + *pFrameCountOut = totalFrameCount; + } + + ma_decoder_uninit(pDecoder); + return MA_SUCCESS; +} + +MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_result result; + ma_decoder_config config; + ma_decoder decoder; + + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = NULL; + } + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); + + return result; +} + +MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); +} + +MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) +{ + ma_decoder_config config; + ma_decoder decoder; + ma_result result; + + if (pFrameCountOut != NULL) { + *pFrameCountOut = 0; + } + if (ppPCMFramesOut != NULL) { + *ppPCMFramesOut = NULL; + } + + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + config = ma_decoder_config_init_copy(pConfig); + + result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); +} +#endif /* MA_NO_DECODING */ + + +#ifndef MA_NO_ENCODING + +#if defined(MA_HAS_WAV) +static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) +{ + ma_encoder* pEncoder = (ma_encoder*)pUserData; + size_t bytesWritten = 0; + + MA_ASSERT(pEncoder != NULL); + + pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); + return bytesWritten; +} + +static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_encoder* pEncoder = (ma_encoder*)pUserData; + ma_result result; + + MA_ASSERT(pEncoder != NULL); + + result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + if (result != MA_SUCCESS) { + return MA_FALSE; + } else { + return MA_TRUE; + } +} + +static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) +{ + ma_dr_wav_data_format wavFormat; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav* pWav; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + if (pWav == NULL) { + return MA_OUT_OF_MEMORY; + } + + wavFormat.container = ma_dr_wav_container_riff; + wavFormat.channels = pEncoder->config.channels; + wavFormat.sampleRate = pEncoder->config.sampleRate; + wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; + if (pEncoder->config.format == ma_format_f32) { + wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else { + wavFormat.format = MA_DR_WAVE_FORMAT_PCM; + } + + allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; + allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; + allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; + allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; + + if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { + return MA_ERROR; + } + + pEncoder->pInternalEncoder = pWav; + + return MA_SUCCESS; +} + +static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) +{ + ma_dr_wav* pWav; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + MA_ASSERT(pWav != NULL); + + ma_dr_wav_uninit(pWav); + ma_free(pWav, &pEncoder->config.allocationCallbacks); +} + +static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) +{ + ma_dr_wav* pWav; + ma_uint64 framesWritten; + + MA_ASSERT(pEncoder != NULL); + + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; + MA_ASSERT(pWav != NULL); + + framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); + + if (pFramesWritten != NULL) { + *pFramesWritten = framesWritten; + } + + return MA_SUCCESS; +} +#endif + +MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_encoder_config config; + + MA_ZERO_OBJECT(&config); + config.encodingFormat = encodingFormat; + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + + return config; +} + +MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + + if (pEncoder == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEncoder); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { + return MA_INVALID_ARGS; + } + + pEncoder->config = *pConfig; + + result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) +{ + ma_result result = MA_SUCCESS; + + /* This assumes ma_encoder_preinit() has been called prior. */ + MA_ASSERT(pEncoder != NULL); + + if (onWrite == NULL || onSeek == NULL) { + return MA_INVALID_ARGS; + } + + pEncoder->onWrite = onWrite; + pEncoder->onSeek = onSeek; + pEncoder->pUserData = pUserData; + + switch (pEncoder->config.encodingFormat) + { + case ma_encoding_format_wav: + { + #if defined(MA_HAS_WAV) + pEncoder->onInit = ma_encoder__on_init_wav; + pEncoder->onUninit = ma_encoder__on_uninit_wav; + pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; + #else + result = MA_NO_BACKEND; + #endif + } break; + + default: + { + result = MA_INVALID_ARGS; + } break; + } + + /* Getting here means we should have our backend callbacks set up. */ + if (result == MA_SUCCESS) { + result = pEncoder->onInit(pEncoder); + } + + return result; +} + +static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) +{ + return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); +} + +static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) +{ + return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); +} + +MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + ma_vfs_file file; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + /* Now open the file. If this fails we don't need to uninitialize the encoder. */ + result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); + if (result != MA_SUCCESS) { + return result; + } + + pEncoder->data.vfs.pVFS = pVFS; + pEncoder->data.vfs.file = file; + + result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); +} + +MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); +} + +MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) +{ + ma_result result; + + result = ma_encoder_preinit(pConfig, pEncoder); + if (result != MA_SUCCESS) { + return result; + } + + return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); +} + + +MA_API void ma_encoder_uninit(ma_encoder* pEncoder) +{ + if (pEncoder == NULL) { + return; + } + + if (pEncoder->onUninit) { + pEncoder->onUninit(pEncoder); + } + + /* If we have a file handle, close it. */ + if (pEncoder->onWrite == ma_encoder__on_write_vfs) { + ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); + pEncoder->data.vfs.file = NULL; + } +} + + +MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) +{ + if (pFramesWritten != NULL) { + *pFramesWritten = 0; + } + + if (pEncoder == NULL || pFramesIn == NULL) { + return MA_INVALID_ARGS; + } + + return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); +} +#endif /* MA_NO_ENCODING */ + + + +/************************************************************************************************************************************************************** + +Generation + +**************************************************************************************************************************************************************/ +#ifndef MA_NO_GENERATION +MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) +{ + ma_waveform_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.type = type; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); +} + +static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_waveform* pWaveform = (ma_waveform*)pDataSource; + + *pFormat = pWaveform->config.format; + *pChannels = pWaveform->config.channels; + *pSampleRate = pWaveform->config.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); + + return MA_SUCCESS; +} + +static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_waveform* pWaveform = (ma_waveform*)pDataSource; + + *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); + + return MA_SUCCESS; +} + +static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) +{ + return (1.0 / (sampleRate / frequency)); +} + +static void ma_waveform__update_advance(ma_waveform* pWaveform) +{ + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); +} + +static ma_data_source_vtable g_ma_waveform_data_source_vtable = +{ + ma_waveform__data_source_on_read, + ma_waveform__data_source_on_seek, + ma_waveform__data_source_on_get_data_format, + ma_waveform__data_source_on_get_cursor, + NULL, /* onGetLength. There's no notion of a length in waveforms. */ + NULL, /* onSetLooping */ + 0 +}; + +MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); + if (result != MA_SUCCESS) { + return result; + } + + pWaveform->config = *pConfig; + pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); + pWaveform->time = 0; + + return MA_SUCCESS; +} + +MA_API void ma_waveform_uninit(ma_waveform* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_data_source_uninit(&pWaveform->ds); +} + +MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform__update_advance(pWaveform); + + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.type = type; + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform__update_advance(pWaveform); + + return MA_SUCCESS; +} + +static float ma_waveform_sine_f32(double time, double amplitude) +{ + return (float)(ma_sind(MA_TAU_D * time) * amplitude); +} + +static ma_int16 ma_waveform_sine_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); +} + +static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + if (f < dutyCycle) { + r = amplitude; + } else { + r = -amplitude; + } + + return (float)r; +} + +static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); +} + +static float ma_waveform_triangle_f32(double time, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + r = 2 * ma_abs(2 * (f - 0.5)) - 1; + + return (float)(r * amplitude); +} + +static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); +} + +static float ma_waveform_sawtooth_f32(double time, double amplitude) +{ + double f = time - (ma_int64)time; + double r; + + r = 2 * (f - 0.5); + + return (float)(r * amplitude); +} + +static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) +{ + return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); +} + +static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint64 iChannel; + ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); + ma_uint32 bpf = bps * pWaveform->config.channels; + + MA_ASSERT(pWaveform != NULL); + MA_ASSERT(pFramesOut != NULL); + + if (pWaveform->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else if (pWaveform->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); + pWaveform->time += pWaveform->advance; + + for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } +} + +MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + switch (pWaveform->config.type) + { + case ma_waveform_type_sine: + { + ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); + } break; + + case ma_waveform_type_square: + { + ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); + } break; + + case ma_waveform_type_triangle: + { + ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); + } break; + + case ma_waveform_type_sawtooth: + { + ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); + } break; + + default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ + } + } else { + pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ + + return MA_SUCCESS; +} + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) +{ + ma_pulsewave_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.dutyCycle = dutyCycle; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) +{ + ma_result result; + ma_waveform_config config; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + config = ma_waveform_config_init( + pConfig->format, + pConfig->channels, + pConfig->sampleRate, + ma_waveform_type_square, + pConfig->amplitude, + pConfig->frequency + ); + + result = ma_waveform_init(&config, &pWaveform->waveform); + ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); + + return result; +} + +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_waveform_uninit(&pWaveform->waveform); +} + +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); + } else { + pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform_set_frequency(&pWaveform->waveform, frequency); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.dutyCycle = dutyCycle; + + return MA_SUCCESS; +} + + + +MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) +{ + ma_noise_config config; + MA_ZERO_OBJECT(&config); + + config.format = format; + config.channels = channels; + config.type = type; + config.seed = seed; + config.amplitude = amplitude; + + if (config.seed == 0) { + config.seed = MA_DEFAULT_LCG_SEED; + } + + return config; +} + + +static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + /* No-op. Just pretend to be successful. */ + (void)pDataSource; + (void)frameIndex; + return MA_SUCCESS; +} + +static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_noise* pNoise = (ma_noise*)pDataSource; + + *pFormat = pNoise->config.format; + *pChannels = pNoise->config.channels; + *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_noise_data_source_vtable = +{ + ma_noise__data_source_on_read, + ma_noise__data_source_on_seek, /* No-op for noise. */ + ma_noise__data_source_on_get_data_format, + NULL, /* onGetCursor. No notion of a cursor for noise. */ + NULL, /* onGetLength. No notion of a length for noise. */ + NULL, /* onSetLooping */ + 0 +}; + + +#ifndef MA_PINK_NOISE_BIN_SIZE +#define MA_PINK_NOISE_BIN_SIZE 16 +#endif + +typedef struct +{ + size_t sizeInBytes; + struct + { + size_t binOffset; + size_t accumulationOffset; + size_t counterOffset; + } pink; + struct + { + size_t accumulationOffset; + } brownian; +} ma_noise_heap_layout; + +static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) +{ + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->channels == 0) { + return MA_INVALID_ARGS; + } + + pHeapLayout->sizeInBytes = 0; + + /* Pink. */ + if (pConfig->type == ma_noise_type_pink) { + /* bin */ + pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; + pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; + + /* accumulation */ + pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + + /* counter */ + pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; + } + + /* Brownian. */ + if (pConfig->type == ma_noise_type_brownian) { + /* accumulation */ + pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; + } + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_noise_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) +{ + ma_result result; + ma_noise_heap_layout heapLayout; + ma_data_source_config dataSourceConfig; + ma_uint32 iChannel; + + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNoise); + + result = ma_noise_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pNoise->_pHeap = pHeap; + MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); + if (result != MA_SUCCESS) { + return result; + } + + pNoise->config = *pConfig; + ma_lcg_seed(&pNoise->lcg, pConfig->seed); + + if (pNoise->config.type == ma_noise_type_pink) { + pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); + pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); + pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); + pNoise->state.pink.accumulation[iChannel] = 0; + pNoise->state.pink.counter[iChannel] = 1; + } + } + + if (pNoise->config.type == ma_noise_type_brownian) { + pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); + + for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { + pNoise->state.brownian.accumulation[iChannel] = 0; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pNoise->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pNoise == NULL) { + return; + } + + ma_data_source_uninit(&pNoise->ds); + + if (pNoise->_ownsHeap) { + ma_free(pNoise->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->config.amplitude = amplitude; + return MA_SUCCESS; +} + +MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + pNoise->lcg.state = seed; + return MA_SUCCESS; +} + + +MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) +{ + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + /* + This function should never have been implemented in the first place. Changing the type dynamically is not + supported. Instead you need to uninitialize and reinitiailize a fresh `ma_noise` object. This function + will be removed in version 0.12. + */ + MA_ASSERT(MA_FALSE); + (void)type; + + return MA_INVALID_OPERATION; +} + +static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) +{ + return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_white(pNoise); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_white(pNoise); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + + +static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) +{ + unsigned int n; + + /* Special case for odd numbers since they should happen about half the time. */ + if (x & 0x1) { + return 0; + } + + if (x == 0) { + return sizeof(x) << 3; + } + + n = 1; + if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } + if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } + if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } + if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } + n -= x & 0x00000001; + + return n; +} + +/* +Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h + +This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ +*/ +static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) +{ + double result; + double binPrev; + double binNext; + unsigned int ibin; + + ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); + + binPrev = pNoise->state.pink.bin[iChannel][ibin]; + binNext = ma_lcg_rand_f64(&pNoise->lcg); + pNoise->state.pink.bin[iChannel][ibin] = binNext; + + pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); + pNoise->state.pink.counter[iChannel] += 1; + + result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); + result /= 10; + + return (float)(result * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_pink(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_pink(pNoise, iChannel); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + + +static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) +{ + double result; + + result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); + result /= 1.005; /* Don't escape the -1..1 range on average. */ + + pNoise->state.brownian.accumulation[iChannel] = result; + result /= 20; + + return (float)(result * pNoise->config.amplitude); +} + +static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) +{ + return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); +} + +static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) +{ + ma_uint64 iFrame; + ma_uint32 iChannel; + const ma_uint32 channels = pNoise->config.channels; + MA_ASSUME(channels > 0); + + if (pNoise->config.format == ma_format_f32) { + float* pFramesOutF32 = (float*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); + } + } + } + } else if (pNoise->config.format == ma_format_s16) { + ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + ma_int16 s = ma_noise_s16_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = s; + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); + } + } + } + } else { + const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); + const ma_uint32 bpf = bps * channels; + + if (pNoise->config.duplicateChannels) { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float s = ma_noise_f32_brownian(pNoise, 0); + for (iChannel = 0; iChannel < channels; iChannel += 1) { + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } else { + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + for (iChannel = 0; iChannel < channels; iChannel += 1) { + float s = ma_noise_f32_brownian(pNoise, iChannel); + ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); + } + } + } + } + + return frameCount; +} + +MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pNoise == NULL) { + return MA_INVALID_ARGS; + } + + /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ + if (pFramesOut == NULL) { + framesRead = frameCount; + } else { + switch (pNoise->config.type) { + case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; + case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; + default: return MA_INVALID_OPERATION; /* Unknown noise type. */ + } + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + return MA_SUCCESS; +} +#endif /* MA_NO_GENERATION */ + + + +#ifndef MA_NO_RESOURCE_MANAGER +#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS +#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 +#endif + +#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY +#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 +#endif + +MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) +{ + ma_resource_manager_pipeline_notifications notifications; + + MA_ZERO_OBJECT(¬ifications); + + return notifications; +} + +static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } + if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } +} + +static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } +} + +static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) +{ + if (pPipelineNotifications == NULL) { + return; + } + + if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } + if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } +} + + + +#ifndef MA_DEFAULT_HASH_SEED +#define MA_DEFAULT_HASH_SEED 42 +#endif + +/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif + +static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) +{ + return (x << r) | (x >> (32 - r)); +} + +static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) +{ + ma_uint32 block; + + /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ + MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); + + if (ma_is_little_endian()) { + return block; + } else { + return ma_swap_endian_uint32(block); + } +} + +static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) +{ + const ma_uint8* data = (const ma_uint8*)key; + const ma_uint32* blocks; + const ma_uint8* tail; + const int nblocks = len / 4; + ma_uint32 h1 = seed; + ma_uint32 c1 = 0xcc9e2d51; + ma_uint32 c2 = 0x1b873593; + ma_uint32 k1; + int i; + + blocks = (const ma_uint32 *)(data + nblocks*4); + + for(i = -nblocks; i; i++) { + k1 = ma_hash_getblock(blocks,i); + + k1 *= c1; + k1 = ma_rotl32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ma_rotl32(h1, 13); + h1 = h1*5 + 0xe6546b64; + } + + + tail = (const ma_uint8*)(data + nblocks*4); + + k1 = 0; + switch(len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; + }; + + + h1 ^= len; + h1 = ma_hash_fmix32(h1); + + return h1; +} + +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push +#endif +/* End MurmurHash3 */ + +static ma_uint32 ma_hash_string_32(const char* str) +{ + return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); +} + +static ma_uint32 ma_hash_string_w_32(const wchar_t* str) +{ + return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); +} + + + + +/* +Basic BST Functions +*/ +static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppDataBufferNode != NULL); + + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + break; /* Found. */ + } else if (hashedName32 < pCurrentNode->hashedName32) { + pCurrentNode = pCurrentNode->pChildLo; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + + *ppDataBufferNode = pCurrentNode; + + if (pCurrentNode == NULL) { + return MA_DOES_NOT_EXIST; + } else { + return MA_SUCCESS; + } +} + +static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(ppInsertPoint != NULL); + + *ppInsertPoint = NULL; + + if (pResourceManager->pRootDataBufferNode == NULL) { + return MA_SUCCESS; /* No items. */ + } + + /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ + pCurrentNode = pResourceManager->pRootDataBufferNode; + while (pCurrentNode != NULL) { + if (hashedName32 == pCurrentNode->hashedName32) { + result = MA_ALREADY_EXISTS; + break; + } else { + if (hashedName32 < pCurrentNode->hashedName32) { + if (pCurrentNode->pChildLo == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildLo; + } + } else { + if (pCurrentNode->pChildHi == NULL) { + result = MA_SUCCESS; + break; + } else { + pCurrentNode = pCurrentNode->pChildHi; + } + } + } + } + + *ppInsertPoint = pCurrentNode; + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + /* The key must have been set before calling this function. */ + MA_ASSERT(pDataBufferNode->hashedName32 != 0); + + if (pInsertPoint == NULL) { + /* It's the first node. */ + pResourceManager->pRootDataBufferNode = pDataBufferNode; + } else { + /* It's not the first node. It needs to be inserted. */ + if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { + MA_ASSERT(pInsertPoint->pChildLo == NULL); + pInsertPoint->pChildLo = pDataBufferNode; + } else { + MA_ASSERT(pInsertPoint->pChildHi == NULL); + pInsertPoint->pChildHi = pDataBufferNode; + } + } + + pDataBufferNode->pParent = pInsertPoint; + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pInsertPoint; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); + if (result != MA_SUCCESS) { + return MA_INVALID_ARGS; + } + + return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); +} +#endif + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildLo != NULL) { + pCurrentNode = pCurrentNode->pChildLo; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + ma_resource_manager_data_buffer_node* pCurrentNode; + + MA_ASSERT(pDataBufferNode != NULL); + + pCurrentNode = pDataBufferNode; + while (pCurrentNode->pChildHi != NULL) { + pCurrentNode = pCurrentNode->pChildHi; + } + + return pCurrentNode; +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildHi != NULL); + + return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); +} + +static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->pChildLo != NULL); + + return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); +} + +static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->pChildLo == NULL) { + if (pDataBufferNode->pChildHi == NULL) { + /* Simple case - deleting a buffer with no children. */ + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ + pResourceManager->pRootDataBufferNode = NULL; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = NULL; + } else { + pDataBufferNode->pParent->pChildHi = NULL; + } + } + } else { + /* Node has one child - pChildHi != NULL. */ + pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; + } + } + } + } else { + if (pDataBufferNode->pChildHi == NULL) { + /* Node has one child - pChildLo != NULL. */ + pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; + + if (pDataBufferNode->pParent == NULL) { + MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); + pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; + } else { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; + } else { + pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; + } + } + } else { + /* Complex case - deleting a node with two children. */ + ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; + + /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ + pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); + MA_ASSERT(pReplacementDataBufferNode != NULL); + + /* + Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement + node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The + replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the + replacement node and reinserting it into the same position as the deleted node. + */ + MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ + MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ + + if (pReplacementDataBufferNode->pChildHi == NULL) { + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = NULL; + } else { + pReplacementDataBufferNode->pParent->pChildHi = NULL; + } + } else { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; + if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { + pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; + } else { + pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; + } + } + + + /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ + if (pDataBufferNode->pParent != NULL) { + if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { + pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; + } else { + pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; + } + } + + /* Now need to update the replacement node's pointers. */ + pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; + pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; + pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; + + /* Now the children of the replacement node need to have their parent pointers updated. */ + if (pReplacementDataBufferNode->pChildLo != NULL) { + pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; + } + if (pReplacementDataBufferNode->pChildHi != NULL) { + pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; + } + + /* Now the root node needs to be updated. */ + if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { + pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; + } + } + } + + return MA_SUCCESS; +} + +#if 0 /* Unused for now. */ +static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) +{ + ma_result result; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + return result; /* Could not find the data buffer. */ + } + + return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); +} +#endif + +static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); +} + +static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) +{ + ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); +} + +static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) +{ + ma_uint32 refCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + (void)pResourceManager; + + refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + + if (pNewRefCount != NULL) { + *pNewRefCount = refCount; + } + + return MA_SUCCESS; +} + +static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { + ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.encoded.pData = NULL; + pDataBufferNode->data.backend.encoded.sizeInBytes = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { + ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); + pDataBufferNode->data.backend.decoded.pData = NULL; + pDataBufferNode->data.backend.decoded.totalFrameCount = 0; + } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { + ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); + } else { + /* Should never hit this if the node was successfully initialized. */ + MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); + } + } + + /* The data buffer itself needs to be freed. */ + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); +} + +static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ +} + + +static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; +} + + +typedef struct +{ + union + { + ma_async_notification_event e; + ma_async_notification_poll p; + } backend; /* Must be the first member. */ + ma_resource_manager* pResourceManager; +} ma_resource_manager_inline_notification; + +static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pNotification != NULL); + + pNotification->pResourceManager = pResourceManager; + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + return ma_async_notification_event_init(&pNotification->backend.e); + } else { + return ma_async_notification_poll_init(&pNotification->backend.p); + } +} + +static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_uninit(&pNotification->backend.e); + } else { + /* No need to uninitialize a polling notification. */ + } +} + +static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { + ma_async_notification_event_wait(&pNotification->backend.e); + } else { + while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { + ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + break; + } + } + } +} + +static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) +{ + ma_resource_manager_inline_notification_wait(pNotification); + ma_resource_manager_inline_notification_uninit(pNotification); +} + + +static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_lock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager != NULL); + + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } else { + /* Threading not enabled. Do nothing. */ + } +} + +#ifndef MA_NO_THREADING +static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) +{ + ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; + MA_ASSERT(pResourceManager != NULL); + + for (;;) { + ma_result result; + ma_job job; + + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + break; + } + + /* Terminate if we got a quit message. */ + if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { + break; + } + + ma_job_process(&job); + } + + return (ma_thread_result)0; +} +#endif + +MA_API ma_resource_manager_config ma_resource_manager_config_init(void) +{ + ma_resource_manager_config config; + + MA_ZERO_OBJECT(&config); + config.decodedFormat = ma_format_unknown; + config.decodedChannels = 0; + config.decodedSampleRate = 0; + config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ + config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; + + /* Flags. */ + config.flags = 0; + #ifdef MA_NO_THREADING + { + /* Threading is disabled at compile time so disable threading at runtime as well by default. */ + config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + config.jobThreadCount = 0; + } + #endif + + return config; +} + + +MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job_queue_config jobQueueConfig; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pResourceManager); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + #ifndef MA_NO_THREADING + { + if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { + return MA_INVALID_ARGS; /* Requesting too many job threads. */ + } + } + #endif + + pResourceManager->config = *pConfig; + ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); + + /* Get the log set up early so we can start using it as soon as possible. */ + if (pResourceManager->config.pLog == NULL) { + result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); + if (result == MA_SUCCESS) { + pResourceManager->config.pLog = &pResourceManager->log; + } else { + pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ + } + } + + if (pResourceManager->config.pVFS == NULL) { + result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the default file system. */ + } + + pResourceManager->config.pVFS = &pResourceManager->defaultVFS; + } + + /* If threading has been disabled at compile time, enfore it at run time as well. */ + #ifdef MA_NO_THREADING + { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; + + /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; + } + } + + /* Job queue. */ + jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; + jobQueueConfig.flags = 0; + if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { + if (pResourceManager->config.jobThreadCount > 0) { + return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ + } + + jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; + } + + result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); + if (result != MA_SUCCESS) { + return result; + } + + + /* Custom decoding backends. */ + if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { + size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; + + pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); + + pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; + pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; + } + + + + /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + /* Data buffer lock. */ + result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); + if (result != MA_SUCCESS) { + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Create the job threads last to ensure the threads has access to valid data. */ + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + return result; + } + } + } + #else + { + /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ + MA_ASSERT(MA_FALSE); + } + #endif + } + + return MA_SUCCESS; +} + + +static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) +{ + MA_ASSERT(pResourceManager); + + /* If everything was done properly, there shouldn't be any active data buffers. */ + while (pResourceManager->pRootDataBufferNode != NULL) { + ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + + /* The data buffer has been removed from the BST, so now we need to free it's data. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } +} + +MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return; + } + + /* + Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the + queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. + */ + ma_resource_manager_post_job_quit(pResourceManager); + + /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_uint32 iJobThread; + + for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { + ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); + } + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ + ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); + + /* The job queue is no longer needed. */ + ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); + + /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager)) { + #ifndef MA_NO_THREADING + { + ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); + } + #else + { + MA_ASSERT(MA_FALSE); /* Should never hit this. */ + } + #endif + } + + ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); + + if (pResourceManager->config.pLog == &pResourceManager->log) { + ma_log_uninit(&pResourceManager->log); + } +} + +MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) +{ + if (pResourceManager == NULL) { + return NULL; + } + + return pResourceManager->config.pLog; +} + + + +MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) +{ + ma_resource_manager_data_source_config config; + + MA_ZERO_OBJECT(&config); + config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG; + config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END; + config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG; + config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END; + config.isLooping = MA_FALSE; + + return config; +} + + +static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) +{ + ma_decoder_config config; + + config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); + config.allocationCallbacks = pResourceManager->config.allocationCallbacks; + config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; + config.customBackendCount = pResourceManager->config.customDecodingBackendCount; + config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; + + return config; +} + +static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoder_config config; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + MA_ASSERT(pDecoder != NULL); + + config = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + return result; + } + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); + if (result != MA_SUCCESS) { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + return result; + } + } + + return MA_SUCCESS; +} + +static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized); +} + +static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) +{ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + return NULL; /* Connector not yet initialized. */ + } + + switch (pDataBuffer->pNode->data.type) + { + case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; + case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; + case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); + return NULL; + }; + }; +} + +static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) +{ + ma_result result; + + MA_ASSERT(pDataBuffer != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE); + + /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result != MA_SUCCESS && result != MA_BUSY) { + return result; /* The data buffer is in an erroneous state. */ + } + + /* + We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the + "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use + an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_config config; + config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); + result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_config config; + config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); + result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_config config; + config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); + result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + /* + Initialization of the connector is when we can fire the init notification. This will give the application access to + the format/channels/rate of the data source. + */ + if (result == MA_SUCCESS) { + /* + The resource manager supports the ability to set the range and loop settings via a config at + initialization time. This results in an case where the ranges could be set explicitly via + ma_data_source_set_*() before we get to this point here. If this happens, we'll end up + hitting a case where we just override those settings which results in what feels like a bug. + + To address this we only change the relevant properties if they're not equal to defaults. If + they're equal to defaults there's no need to change them anyway. If they're *not* set to the + default values, we can assume the user has set the range and loop settings via the config. If + they're doing their own calls to ma_data_source_set_*() in addition to setting them via the + config, that's entirely on the caller and any synchronization issue becomes their problem. + */ + if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) { + ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) { + ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + if (pConfig->isLooping != MA_FALSE) { + ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); + } + + ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE); + + if (pInitNotification != NULL) { + ma_async_notification_signal(pInitNotification); + } + + if (pInitFence != NULL) { + ma_fence_release(pInitFence); + } + } + + /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ + return result; +} + +static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBuffer != NULL); + + (void)pResourceManager; + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ + { + ma_decoder_uninit(&pDataBuffer->connector.decoder); + } break; + + case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ + { + ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ + { + ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); + } break; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown data supply type. Should never happen. Need to post an error here. */ + return MA_INVALID_ARGS; + }; + } + + return MA_SUCCESS; +} + +static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) +{ + MA_ASSERT(pDataBufferNode != NULL); + return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) +{ + ma_result result; + size_t dataSizeInBytes; + void* pData; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (result != MA_SUCCESS) { + if (pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); + #endif + } + + return result; + } + + pDataBufferNode->data.backend.encoded.pData = pData; + pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) +{ + ma_result result = MA_SUCCESS; + ma_decoder* pDecoder; + ma_uint64 totalFrameCount; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(ppDecoder != NULL); + MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); + + *ppDecoder = NULL; /* For safety. */ + + pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); + if (pDecoder == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); + if (result != MA_SUCCESS) { + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* + At this point we have the decoder and we now need to initialize the data supply. This will + be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap + allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter + is used when the length of a sound is unknown until a full decode has been performed. + */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); + if (result != MA_SUCCESS) { + return result; + } + } else { + totalFrameCount = 0; + } + + if (totalFrameCount > 0) { + /* It's a known length. The data supply is a regular decoded buffer. */ + ma_uint64 dataSizeInBytes; + void* pData; + + dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + if (dataSizeInBytes > MA_SIZE_MAX) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pData == NULL) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + /* The buffer needs to be initialized to silence in case the caller reads from it. */ + ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); + + /* Data has been allocated and the data supply can now be initialized. */ + pDataBufferNode->data.backend.decoded.pData = pData; + pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; + pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; + pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; + pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ + } else { + /* + It's an unknown length. The data supply is a paged decoded buffer. Setting this up is + actually easier than the non-paged decoded buffer because we just need to initialize + a ma_paged_audio_buffer object. + */ + result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + return result; + } + + pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; + ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ + } + + *ppDecoder = pDecoder; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 framesToTryReading; + ma_uint64 framesRead; + + MA_ASSERT(pResourceManager != NULL); + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDecoder != NULL); + + /* We need to know the size of a page in frames to know how many frames to decode. */ + pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); + framesToTryReading = pageSizeInFrames; + + /* + Here is where we do the decoding of the next page. We'll run a slightly different path depending + on whether or not we're using a flat or paged buffer because the allocation of the page differs + between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged + buffer, we need to allocate a new page and attach it to the linked list. + */ + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) + { + case ma_resource_manager_data_supply_type_decoded: + { + /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ + void* pDst; + ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; + if (framesToTryReading > framesRemaining) { + framesToTryReading = framesRemaining; + } + + if (framesToTryReading > 0) { + pDst = ma_offset_ptr( + pDataBufferNode->data.backend.decoded.pData, + pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) + ); + MA_ASSERT(pDst != NULL); + + result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); + if (framesRead > 0) { + pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; + } + } else { + framesRead = 0; + } + } break; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + /* The destination buffer is a freshly allocated page. */ + ma_paged_audio_buffer_page* pPage; + + result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); + if (framesRead > 0) { + pPage->sizeInFrames = framesRead; + + result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); + if (result == MA_SUCCESS) { + pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; + } else { + /* Failed to append the page. Just abort and set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } else { + /* No frames were read. Free the page and just set the status to MA_AT_END. */ + ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); + result = MA_AT_END; + } + } break; + + case ma_resource_manager_data_supply_type_encoded: + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unexpected data supply type. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); + return MA_ERROR; + }; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_data_buffer_node* pInsertPoint; + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; + } + + result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); + if (result == MA_ALREADY_EXISTS) { + /* The node already exists. We just need to increment the reference count. */ + pDataBufferNode = pInsertPoint; + + result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); + if (result != MA_SUCCESS) { + return result; /* Should never happen. Failed to increment the reference count. */ + } + + result = MA_ALREADY_EXISTS; + goto done; + } else { + /* + The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This + needs to be done inside the critical section to ensure an uninitialization of the node + does not occur before initialization on another thread. + */ + pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); + if (pDataBufferNode == NULL) { + return MA_OUT_OF_MEMORY; + } + + MA_ZERO_OBJECT(pDataBufferNode); + pDataBufferNode->hashedName32 = hashedName32; + pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ + + if (pExistingData == NULL) { + pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ + pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ + pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; + } else { + pDataBufferNode->data = *pExistingData; + pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ + pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; + } + + result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); + if (result != MA_SUCCESS) { + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return result; /* Should never happen. Failed to insert the data buffer into the BST. */ + } + + /* + Here is where we'll post the job, but only if we're loading asynchronously. If we're + loading synchronously we'll defer loading to a later stage, outside of the critical + section. + */ + if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + /* Loading asynchronously. Post the job. */ + ma_job job; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + } + + /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ + if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } + if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } + + /* We now have everything we need to post the job to the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; + job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataBufferNode.flags = flags; + job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; + job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; + job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; + job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + + if (result != MA_SUCCESS) { + /* Failed to post job. Probably ran out of memory. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + + /* + Fences were acquired before posting the job, but since the job was not able to + be posted, we need to make sure we release them so nothing gets stuck waiting. + */ + if (pInitFence != NULL) { ma_fence_release(pInitFence); } + if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(pInitNotification); + } else { + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + } + + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + + return result; + } + } + } + +done: + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) +{ + ma_result result = MA_SUCCESS; + ma_bool32 nodeAlreadyExists = MA_FALSE; + ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = NULL; /* Safety. */ + } + + if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { + return MA_INVALID_ARGS; + } + + /* If we're specifying existing data, it must be valid. */ + if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { + return MA_INVALID_ARGS; + } + + /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + if (hashedName32 == 0) { + if (pFilePath != NULL) { + hashedName32 = ma_hash_string_32(pFilePath); + } else { + hashedName32 = ma_hash_string_w_32(pFilePathW); + } + } + + /* + Here is where we either increment the node's reference count or allocate a new one and add it + to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is + posted inside the critical section just in case the caller immediately uninitializes the node + as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the + node is not uninitialized before initialization. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + + if (result == MA_ALREADY_EXISTS) { + nodeAlreadyExists = MA_TRUE; + result = MA_SUCCESS; + } else { + if (result != MA_SUCCESS) { + return result; + } + } + + /* + If we're loading synchronously, we'll need to load everything now. When loading asynchronously, + a job will have been posted inside the BST critical section so that an uninitialization can be + allocated an appropriate execution order thereby preventing it from being uninitialized before + the node is initialized by the decoding thread(s). + */ + if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ + if (pFilePath == NULL && pFilePathW == NULL) { + /* + If this path is hit, it means a buffer is being copied (i.e. initialized from only the + hashed name), but that node has been freed in the meantime, probably from some other + thread. This is an invalid operation. + */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); + result = MA_INVALID_OPERATION; + goto done; + } + + if (pDataBufferNode->isDataOwnedByResourceManager) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { + /* Loading synchronously. Load the sound in it's entirety here. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { + /* No decoding. This is the simple case - just store the file contents in memory. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); + if (result != MA_SUCCESS) { + goto done; + } + } else { + /* Decoding. We do this the same way as we do when loading asynchronously. */ + ma_decoder* pDecoder; + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); + if (result != MA_SUCCESS) { + goto done; + } + + /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ + for (;;) { + /* Decode next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); + if (result != MA_SUCCESS) { + break; /* Will return MA_AT_END when the last page has been decoded. */ + } + } + + /* Reaching the end needs to be considered successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* + At this point the data buffer is either fully decoded or some error occurred. Either + way, the decoder is no longer necessary. + */ + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ + ma_atomic_exchange_i32(&pDataBufferNode->result, result); + } else { + /* Loading asynchronously. We may need to wait for initialization. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + } + } + } else { + /* The data is not managed by the resource manager so there's nothing else to do. */ + MA_ASSERT(pExistingData != NULL); + } + } + +done: + /* If we failed to initialize the data buffer we need to free it. */ + if (result != MA_SUCCESS) { + if (nodeAlreadyExists == MA_FALSE) { + ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); + } + } + + /* + The init notification needs to be uninitialized. This will be used if the node does not already + exist, and we've specified ASYNC | WAIT_INIT. + */ + if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (ppDataBufferNode != NULL) { + *ppDataBufferNode = pDataBufferNode; + } + + return result; +} + +static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) +{ + ma_result result = MA_SUCCESS; + ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ + ma_uint32 hashedName32 = 0; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + if (pDataBufferNode == NULL) { + if (pName == NULL && pNameW == NULL) { + return MA_INVALID_ARGS; + } + + if (pName != NULL) { + hashedName32 = ma_hash_string_32(pName); + } else { + hashedName32 = ma_hash_string_w_32(pNameW); + } + } + + /* + The first thing to do is decrement the reference counter of the node. Then, if the reference + count is zero, we need to free the node. If the node is still in the process of loading, we'll + need to post a job to the job queue to free the node. Otherwise we'll just do it here. + */ + ma_resource_manager_data_buffer_bst_lock(pResourceManager); + { + /* Might need to find the node. Must be done inside the critical section. */ + if (pDataBufferNode == NULL) { + result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* Couldn't find the node. */ + } + } + + result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); + if (result != MA_SUCCESS) { + goto stage2; /* Should never happen. */ + } + + if (refCount == 0) { + result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); + if (result != MA_SUCCESS) { + goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ + } + } + } + ma_resource_manager_data_buffer_bst_unlock(pResourceManager); + +stage2: + if (result != MA_SUCCESS) { + return result; + } + + /* + Here is where we need to free the node. We don't want to do this inside the critical section + above because we want to keep that as small as possible for multi-threaded efficiency. + */ + if (refCount == 0) { + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ + ma_job job; + + /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ + ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); + job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; + job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; + + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); + return result; + } + + /* If we don't support threading, process the job queue here. */ + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { + result = ma_resource_manager_process_next_job(pResourceManager); + if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { + result = MA_SUCCESS; + break; + } + } + } else { + /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ + } + } else { + /* The sound isn't loading so we can just free the node here. */ + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + } + } + + return result; +} + + + +static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); +} + +static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; + MA_ASSERT(pDataBuffer != NULL); + + ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + + /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ + ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = +{ + ma_resource_manager_data_buffer_cb__read_pcm_frames, + ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, + ma_resource_manager_data_buffer_cb__get_data_format, + ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, + ma_resource_manager_data_buffer_cb__set_looping, + 0 +}; + +static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager_data_buffer_node* pDataBufferNode; + ma_data_source_config dataSourceConfig; + ma_bool32 async; + ma_uint32 flags; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataBuffer == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataBuffer); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ + flags = pConfig->flags; + if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { + flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; + } + + async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; + + /* + Fences need to be acquired before doing anything. These must be acquired and released outside of + the node to ensure there's no holes where ma_fence_wait() could prematurely return before the + data buffer has completed initialization. + + When loading asynchronously, the node acquisition routine below will acquire the fences on this + thread and then release them on the async thread when the operation is complete. + + These fences are always released at the "done" tag at the end of this function. They'll be + acquired a second if loading asynchronously. This double acquisition system is just done to + simplify code maintanence. + */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + { + /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ + result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } + + pDataBuffer->pResourceManager = pResourceManager; + pDataBuffer->pNode = pDataBufferNode; + pDataBuffer->flags = flags; + pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ + + /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ + if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { + /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); + ma_atomic_exchange_i32(&pDataBuffer->result, result); + + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + goto done; + } else { + /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ + ma_job job; + ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); + } + + /* + The status of the data buffer needs to be set to MA_BUSY before posting the job so that the + worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other + than MA_BUSY, it'll assume an error and fall through to an early exit. + */ + ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + + /* Acquire fences a second time. These will be released by the async thread. */ + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; + job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; + job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; + job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; + + /* If we need to wait for initialization to complete we can just process the job in place. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + + if (result != MA_SUCCESS) { + /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); + ma_atomic_exchange_i32(&pDataBuffer->result, result); + + /* Release the fences after the result has been set on the data buffer. */ + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + } else { + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_wait(&initNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* NOTE: Do not release the init fence here. It will have been done by the job. */ + + /* Make sure we return an error if initialization failed on the async thread. */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + } + } + + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + ma_resource_manager_inline_notification_uninit(&initNotification); + } + } + + if (result != MA_SUCCESS) { + ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); + goto done; + } + } +done: + if (result == MA_SUCCESS) { + if (pConfig->initialSeekPointInPCMFrames > 0) { + ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); + } + } + + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_resource_manager_data_source_config config; + + if (pExistingDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataBuffer->flags; + + return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); +} + +static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) +{ + MA_ASSERT(pDataBuffer != NULL); + + /* The connector should be uninitialized first. */ + ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); + + /* With the connector uninitialized we can unacquire the node. */ + ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); + + /* The base data source needs to be uninitialized as well. */ + ma_data_source_uninit(&pDataBuffer->ds); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) +{ + ma_result result; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { + /* The data buffer can be deleted synchronously. */ + return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + } else { + /* + The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will + be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event + to get processed before returning. + */ + ma_resource_manager_inline_notification notification; + ma_job job; + + /* + We need to mark the node as unavailable so we don't try reading from it anymore, but also to + let the loading thread know that it needs to abort it's loading procedure. + */ + ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + + result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); + if (result != MA_SUCCESS) { + return result; /* Failed to create the notification. This should rarely, if ever, happen. */ + } + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); + job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); + job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; + job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; + job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; + + result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_inline_notification_uninit(¬ification); + return result; + } + + ma_resource_manager_inline_notification_wait_and_uninit(¬ification); + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 framesRead = 0; + ma_bool32 isDecodedBufferBusy = MA_FALSE; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* + We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after + it's been uninitialized or is in the process of uninitializing. + */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If the node is not initialized we need to abort with a busy code. */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + return MA_BUSY; /* Still loading. */ + } + + /* + If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's + a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If + this happens, we need to keep the seek scheduled and return MA_BUSY. + */ + if (pDataBuffer->seekToCursorOnNextRead) { + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); + if (result != MA_SUCCESS) { + if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) { + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */ + return MA_BUSY; + } + + return result; + } + } + + /* + For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot + exceed this amount. We'll read as much as we can, and then return MA_BUSY. + */ + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { + ma_uint64 availableFrames; + + isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); + + if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { + /* Don't try reading more than the available frame count. */ + if (frameCount > availableFrames) { + frameCount = availableFrames; + + /* + If there's no frames available we want to set the status to MA_AT_END. The logic below + will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this + is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count + is 0 because that'll result in a situation where it's possible MA_AT_END won't get + returned. + */ + if (frameCount == 0) { + result = MA_AT_END; + } + } else { + isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ + } + } + } + + /* Don't attempt to read anything if we've got no frames available. */ + if (frameCount > 0) { + result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); + } + + /* + If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound + as at the end and terminate decoding. + */ + if (result == MA_AT_END) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + result = MA_BUSY; + } + } + + if (isDecodedBufferBusy) { + result = MA_BUSY; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (result == MA_SUCCESS && framesRead == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) +{ + ma_result result; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + /* If we haven't yet got a connector we need to abort. */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) { + pDataBuffer->seekTargetInPCMFrames = frameIndex; + pDataBuffer->seekToCursorOnNextRead = MA_TRUE; + return MA_BUSY; /* Still loading. */ + } + + result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); + if (result != MA_SUCCESS) { + return result; + } + + pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ + pDataBuffer->seekToCursorOnNextRead = MA_FALSE; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + *pFormat = pDataBuffer->pNode->data.backend.decoded.format; + *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; + *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; + *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; /* Still loading. */ + }; + + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); + }; + + case ma_resource_manager_data_supply_type_unknown: + { + return MA_BUSY; + }; + + default: + { + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); + + if (pDataBuffer == NULL || pLength == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + return MA_BUSY; /* Still loading. */ + } + + return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); +} + +MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) +{ + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ +} + +MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataBuffer, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) +{ + return ma_data_source_is_looping(pDataBuffer); +} + +MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataBuffer == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { + if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { + return MA_BUSY; + } else { + return MA_INVALID_OPERATION; /* No connector. */ + } + } + + switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) + { + case ma_resource_manager_data_supply_type_encoded: + { + return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded: + { + return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); + }; + + case ma_resource_manager_data_supply_type_decoded_paged: + { + ma_uint64 cursor; + ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); + + if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { + *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; + } else { + *pAvailableFrames = 0; + } + + return MA_SUCCESS; + }; + + case ma_resource_manager_data_supply_type_unknown: + default: + { + /* Unknown supply type. Should never hit this. */ + return MA_INVALID_ARGS; + } + } +} + +MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); +} + +MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); +} + + +static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) +{ + return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); +} + +static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_decoded; + data.backend.decoded.pData = pData; + data.backend.decoded.totalFrameCount = frameCount; + data.backend.decoded.format = format; + data.backend.decoded.channels = channels; + data.backend.decoded.sampleRate = sampleRate; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); +} + +MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); +} + + +static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) +{ + ma_resource_manager_data_supply data; + data.type = ma_resource_manager_data_supply_type_encoded; + data.backend.encoded.pData = pData; + data.backend.encoded.sizeInBytes = sizeInBytes; + + return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); +} + +MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); +} + +MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) +{ + return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); +} + + +MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) +{ + return ma_resource_manager_unregister_data(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) +{ + return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); +} + +MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); +} + +MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) +{ + return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); +} + + +static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); +} + +static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); +} + +static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); +} + + +static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); +} + +static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); +} + +static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); +} + +static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) +{ + return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); +} + +static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) +{ + ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; + MA_ASSERT(pDataStream != NULL); + + ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); + + return MA_SUCCESS; +} + +static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = +{ + ma_resource_manager_data_stream_cb__read_pcm_frames, + ma_resource_manager_data_stream_cb__seek_to_pcm_frame, + ma_resource_manager_data_stream_cb__get_data_format, + ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, + ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, + ma_resource_manager_data_stream_cb__set_looping, + 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ +}; + +static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) +{ + /* Loop if possible. */ + if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { + absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; + } + + ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); +} + +MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) +{ + ma_result result; + ma_data_source_config dataSourceConfig; + char* pFilePathCopy = NULL; + wchar_t* pFilePathWCopy = NULL; + ma_job job; + ma_bool32 waitBeforeReturning = MA_FALSE; + ma_resource_manager_inline_notification waitNotification; + ma_resource_manager_pipeline_notifications notifications; + + if (pDataStream == NULL) { + if (pConfig != NULL && pConfig->pNotifications != NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); + } + + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataStream); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pNotifications != NULL) { + notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ + } else { + MA_ZERO_OBJECT(¬ifications); + } + + dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; + + result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return result; + } + + pDataStream->pResourceManager = pResourceManager; + pDataStream->flags = pConfig->flags; + pDataStream->result = MA_BUSY; + + ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + ma_data_source_set_looping(pDataStream, pConfig->isLooping); + + if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_INVALID_ARGS; + } + + /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ + + /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ + if (pConfig->pFilePath != NULL) { + pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); + } else { + pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); + } + + if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + return MA_OUT_OF_MEMORY; + } + + /* + We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we + can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. + */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + waitBeforeReturning = MA_TRUE; + ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); + } + + ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); + + /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); + + /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.loadDataStream.pDataStream = pDataStream; + job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; + job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; + job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; + job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; + job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; + result = ma_resource_manager_post_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); + ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); + + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_uninit(&waitNotification); + } + + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); + return result; + } + + /* Wait if needed. */ + if (waitBeforeReturning) { + ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); + + if (notifications.init.pNotification != NULL) { + ma_async_notification_signal(notifications.init.pNotification); + } + + /* + If there was an error during initialization make sure we return that result here. We don't want to do this + if we're not waiting because it will most likely be in a busy state. + */ + if (pDataStream->result != MA_SUCCESS) { + return pDataStream->result; + } + + /* NOTE: Do not release pInitFence here. That will be done by the job. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); +} + +MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) +{ + ma_resource_manager_inline_notification freeEvent; + ma_job job; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ + ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + + /* + We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need + to wait for it to complete before returning which means we need an event. + */ + ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); + + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.freeDataStream.pDataStream = pDataStream; + job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; + job.data.resourceManager.freeDataStream.pDoneFence = NULL; + ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + + /* We need to wait for the job to finish processing before we return. */ + ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); + + return MA_SUCCESS; +} + + +static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + + return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); +} + +static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) +{ + MA_ASSERT(pDataStream != NULL); + MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); + MA_ASSERT(pageIndex == 0 || pageIndex == 1); + + return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); +} + +static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) +{ + ma_result result = MA_SUCCESS; + ma_uint64 pageSizeInFrames; + ma_uint64 totalFramesReadForThisPage = 0; + void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The decoder needs to inherit the stream's looping and range state. */ + { + ma_uint64 rangeBeg; + ma_uint64 rangeEnd; + ma_uint64 loopPointBeg; + ma_uint64 loopPointEnd; + + ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); + + ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); + ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); + + ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); + ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); + } + + /* Just read straight from the decoder. It will deal with ranges and looping for us. */ + result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); + if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + } + + ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); +} + +static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) +{ + ma_uint32 iPage; + + MA_ASSERT(pDataStream != NULL); + + for (iPage = 0; iPage < 2; iPage += 1) { + ma_resource_manager_data_stream_fill_page(pDataStream, iPage); + } +} + + +static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) +{ + ma_uint64 framesAvailable; + ma_uint64 frameCount = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFrameCount != NULL) { + frameCount = *pFrameCount; + *pFrameCount = 0; + } + if (ppFramesOut != NULL) { + *ppFramesOut = NULL; + } + + if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + /* If the page we're on is invalid it means we've caught up to the job thread. */ + if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + framesAvailable = 0; + } else { + /* + The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is + that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. + */ + ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); + + framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; + } + + /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ + if (framesAvailable == 0) { + if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { + return MA_AT_END; + } else { + return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ + } + } + + MA_ASSERT(framesAvailable > 0); + + if (frameCount > framesAvailable) { + frameCount = framesAvailable; + } + + *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); + *pFrameCount = frameCount; + + return MA_SUCCESS; +} + +static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) +{ + ma_uint32 newRelativeCursor; + ma_uint32 pageSizeInFrames; + ma_job job; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* The frame count should always fit inside a 32-bit integer. */ + if (frameCount > 0xFFFFFFFF) { + return MA_INVALID_ARGS; + } + + pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); + + /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + + /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ + newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; + + /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ + if (newRelativeCursor >= pageSizeInFrames) { + newRelativeCursor -= pageSizeInFrames; + + /* Here is where we post the job start decoding. */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.pageDataStream.pDataStream = pDataStream; + job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; + + /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ + ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + + /* Before posting the job we need to make sure we set some state. */ + pDataStream->relativeCursor = newRelativeCursor; + pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); + } else { + /* We haven't moved into a new page so we can just move the cursor forward. */ + pDataStream->relativeCursor = newRelativeCursor; + return MA_SUCCESS; + } +} + + +MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesProcessed; + ma_format format; + ma_uint32 channels; + + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ + if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { + return MA_BUSY; + } + + ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); + + /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ + totalFramesProcessed = 0; + while (totalFramesProcessed < frameCount) { + void* pMappedFrames; + ma_uint64 mappedFrameCount; + + mappedFrameCount = frameCount - totalFramesProcessed; + result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); + } + + totalFramesProcessed += mappedFrameCount; + + result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); + if (result != MA_SUCCESS) { + break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ + } + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesProcessed; + } + + if (result == MA_SUCCESS && totalFramesProcessed == 0) { + result = MA_AT_END; + } + + return result; +} + +MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) +{ + ma_job job; + ma_result streamResult; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ + if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { + if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + return MA_SUCCESS; + } + } + + + /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ + ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); + + /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); + + /* + We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public + API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of + the first page. + */ + pDataStream->relativeCursor = 0; + pDataStream->currentPageIndex = 0; + ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + + /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + + /* + The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages + are invalid and any content contained within them will be discarded and replaced with newly decoded data. + */ + job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); + job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); + job.data.resourceManager.seekDataStream.pDataStream = pDataStream; + job.data.resourceManager.seekDataStream.frameIndex = frameIndex; + return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pFormat != NULL) { + *pFormat = ma_format_unknown; + } + + if (pChannels != NULL) { + *pChannels = 0; + } + + if (pSampleRate != NULL) { + *pSampleRate = 0; + } + + if (pChannelMap != NULL) { + MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); + } + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + return MA_INVALID_OPERATION; + } + + /* + We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function + such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. + */ + return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); +} + +MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) +{ + ma_result result; + + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + + *pCursor = 0; + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + /* + If the stream is in an erroneous state we need to return an invalid operation. We can allow + this to be called when the data stream is in a busy state because the caller may have asked + for an initial seek position and it's convenient to return that as the cursor position. + */ + result = ma_resource_manager_data_stream_result(pDataStream); + if (result != MA_SUCCESS && result != MA_BUSY) { + return MA_INVALID_OPERATION; + } + + *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) +{ + ma_result streamResult; + + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + + *pLength = 0; + + streamResult = ma_resource_manager_data_stream_result(pDataStream); + + /* We cannot be using the data source after it's been uninitialized. */ + MA_ASSERT(streamResult != MA_UNAVAILABLE); + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + if (streamResult != MA_SUCCESS) { + return streamResult; + } + + /* + We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we + calculated when we initialized it on the job thread. + */ + *pLength = pDataStream->totalLengthInPCMFrames; + if (*pLength == 0) { + return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + return (ma_result)ma_atomic_load_i32(&pDataStream->result); +} + +MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) +{ + return ma_data_source_set_looping(pDataStream, isLooping); +} + +MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) +{ + if (pDataStream == NULL) { + return MA_FALSE; + } + + return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ +} + +MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) +{ + ma_uint32 pageIndex0; + ma_uint32 pageIndex1; + ma_uint32 relativeCursor; + ma_uint64 availableFrames; + + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataStream == NULL) { + return MA_INVALID_ARGS; + } + + pageIndex0 = pDataStream->currentPageIndex; + pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; + relativeCursor = pDataStream->relativeCursor; + + availableFrames = 0; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + } + } + + *pAvailableFrames = availableFrames; + return MA_SUCCESS; +} + + +static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSource); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + pDataSource->flags = pConfig->flags; + + return MA_SUCCESS; +} + +MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + + result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* The data source itself is just a data stream or a data buffer. */ + if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePath = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) +{ + ma_resource_manager_data_source_config config; + + config = ma_resource_manager_data_source_config_init(); + config.pFilePathW = pName; + config.flags = flags; + config.pNotifications = pNotifications; + + return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); +} + +MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) +{ + ma_result result; + ma_resource_manager_data_source_config config; + + if (pExistingDataSource == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_resource_manager_data_source_config_init(); + config.flags = pExistingDataSource->flags; + + result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); + if (result != MA_SUCCESS) { + return result; + } + + /* Copying can only be done from data buffers. Streams cannot be copied. */ + if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return MA_INVALID_OPERATION; + } + + return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); +} + +MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + /* All we need to is uninitialize the underlying data buffer or data stream. */ + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Safety. */ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); + } else { + return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); + } +} + +MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); + } else { + return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); + } +} + +MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); + } else { + return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ + } +} + +MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } else { + return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); + } else { + return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); + } else { + return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); + } +} + +MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) +{ + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); + } else { + return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); + } +} + +MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) +{ + if (pDataSource == NULL) { + return MA_FALSE; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); + } else { + return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); + } +} + +MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) +{ + if (pAvailableFrames == NULL) { + return MA_INVALID_ARGS; + } + + *pAvailableFrames = 0; + + if (pDataSource == NULL) { + return MA_INVALID_ARGS; + } + + if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { + return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); + } else { + return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); + } +} + + +MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_post(&pResourceManager->jobQueue, pJob); +} + +MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) +{ + ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); + return ma_resource_manager_post_job(pResourceManager, &job); +} + +MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_queue_next(&pResourceManager->jobQueue, pJob); +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ + + /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ + if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ + goto done; + } + + /* + We're ready to start loading. Essentially what we're doing here is initializing the data supply + of the node. Once this is complete, data buffers can have their connectors initialized which + will allow then to have audio data read from them. + + Note that when the data supply type has been moved away from "unknown", that is when other threads + will determine that the node is available for data delivery and the data buffer connectors can be + initialized. Therefore, it's important that it is set after the data supply has been initialized. + */ + if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { + /* + Decoding. This is the complex case because we're not going to be doing the entire decoding + process here. Instead it's going to be split of multiple jobs and loaded in pages. The + reason for this is to evenly distribute decoding time across multiple sounds, rather than + having one huge sound hog all the available processing resources. + + The first thing we do is initialize a decoder. This is allocated on the heap and is passed + around to the paging jobs. When the last paging job has completed it's processing, it'll + free the decoder for us. + + This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job + which is where the actual decoding work will be done. However, once this job is complete, + the node will be in a state where data buffer connectors can be initialized. + */ + ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ + ma_job pageDataBufferNodeJob; + + /* Allocate the decoder by initializing a decoded data supply. */ + result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); + + /* + Don't ever propagate an MA_BUSY result code or else the resource manager will think the + node is just busy decoding rather than in an error state. This should never happen, but + including this logic for safety just in case. + */ + if (result == MA_BUSY) { + result = MA_ERROR; + } + + if (result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); + } else { + #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); + #endif + } + + goto done; + } + + /* + At this point the node's data supply is initialized and other threads can start initializing + their data buffer connectors. However, no data will actually be available until we start to + actually decode it. To do this, we need to post a paging job which is where the decoding + work is done. + + Note that if an error occurred at an earlier point, this section will have been skipped. + */ + pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); + pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; + pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; + + /* The job has been set up so it can now be posted. */ + result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); + + /* + When we get here, we want to make sure the result code is set to MA_BUSY. The reason for + this is that the result will be copied over to the node's internal result variable. In + this case, since the decoding is still in-progress, we need to make sure the result code + is set to MA_BUSY. + */ + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); + ma_decoder_uninit(pDecoder); + ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); + } else { + result = MA_BUSY; + } + } else { + /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ + result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); + } + + +done: + /* File paths are no longer needed. */ + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* + We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads + are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY + because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then + immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any + other error code would cause the buffer to look like it's in a state that it's not. + */ + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* At this point initialization is complete and we can signal the notification if any. */ + if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); + } + + /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); + } + } + + /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + + /* A busy result should be considered successful from the point of view of the job system. */ + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); + } + + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer_node* pDataBufferNode; + + MA_ASSERT(pJob != NULL); + + pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; + MA_ASSERT(pResourceManager != NULL); + + pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; + MA_ASSERT(pDataBufferNode != NULL); + + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* Don't do any more decoding if the data buffer has started the uninitialization process. */ + result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); + if (result != MA_BUSY) { + goto done; + } + + /* We're ready to decode the next page. */ + result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + + /* + If we have a success code by this point, we want to post another job. We're going to set the + result back to MA_BUSY to make it clear that there's still more to load. + */ + if (result == MA_SUCCESS) { + ma_job newJob; + newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ + newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ + + result = ma_resource_manager_post_job(pResourceManager, &newJob); + + /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ + if (result == MA_SUCCESS) { + result = MA_BUSY; + } + } + +done: + /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ + if (result != MA_BUSY) { + ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); + ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); + } + + /* If we reached the end we need to treat it as successful. */ + if (result == MA_AT_END) { + result = MA_SUCCESS; + } + + /* Make sure we set the result of node in case some error occurred. */ + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + + /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ + if (result != MA_BUSY) { + if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); + } + + if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); + } + } + + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + return result; +} + + +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; + ma_bool32 isConnectorInitialized = MA_FALSE; + + /* + All we're doing here is checking if the node has finished loading. If not, we just re-post the job + and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. + */ + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ + } + + /* + First thing we need to do is check whether or not the data buffer is getting deleted. If so we + just abort, but making sure we increment the execution pointer. + */ + result = ma_resource_manager_data_buffer_result(pDataBuffer); + if (result != MA_BUSY) { + goto done; /* <-- This will ensure the exucution pointer is incremented. */ + } else { + result = MA_SUCCESS; /* <-- Make sure this is reset. */ + } + + /* Try initializing the connector if we haven't already. */ + isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer); + if (isConnectorInitialized == MA_FALSE) { + dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); + + if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { + /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ + ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ + dataSourceConfig = ma_resource_manager_data_source_config_init(); + dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; + dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; + dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; + dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; + dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; + + result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); + if (result != MA_SUCCESS) { + ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); + goto done; + } + } else { + /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ + } + } else { + /* The connector is already initialized. Nothing to do here. */ + } + + /* + If the data node is still loading, we need to repost the job and *not* increment the execution + pointer (i.e. we need to not fall through to the "done" label). + + There is a hole between here and the where the data connector is initialized where the data + buffer node may have finished initializing. We need to check for this by checking the result of + the data buffer node and whether or not we had an unknown data supply type at the time of + trying to initialize the data connector. + */ + result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); + if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { + return ma_resource_manager_post_job(pResourceManager, pJob); + } + +done: + /* Only move away from a busy code so that we don't trash any existing error codes. */ + ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); + } + + /* + If at this point the data buffer has not had it's connector initialized, it means the + notification event was never signalled which means we need to signal it here. + */ + if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) { + if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); + } + if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); + } + } + + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_buffer* pDataBuffer; + + MA_ASSERT(pJob != NULL); + + pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; + MA_ASSERT(pDataBuffer != NULL); + + pResourceManager = pDataBuffer->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); + } + + if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); + } + + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_decoder_config decoderConfig; + ma_uint32 pageBufferSizeInBytes; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { + result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ + goto done; + } + + /* We need to initialize the decoder first so we can determine the size of the pages. */ + decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); + + if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { + result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); + } else { + result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); + } + if (result != MA_SUCCESS) { + goto done; + } + + /* Retrieve the total length of the file before marking the decoder as loaded. */ + if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { + result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); + if (result != MA_SUCCESS) { + goto done; /* Failed to retrieve the length. */ + } + } else { + pDataStream->totalLengthInPCMFrames = 0; + } + + /* + Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file + and we don't want to have another thread trying to access the decoder while it's scanning. + */ + pDataStream->isDecoderInitialized = MA_TRUE; + + /* We have the decoder so we can now initialize our page buffer. */ + pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); + + pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); + if (pDataStream->pPageData == NULL) { + ma_decoder_uninit(&pDataStream->decoder); + result = MA_OUT_OF_MEMORY; + goto done; + } + + /* Seek to our initial seek point before filling the initial pages. */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); + + /* We have our decoder and our page buffer, so now we need to fill our pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* And now we're done. We want to make sure the result is MA_SUCCESS. */ + result = MA_SUCCESS; + +done: + ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); + ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); + + /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ + ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + + /* Only signal the other threads after the result has been set just for cleanliness sake. */ + if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); + } + if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { + ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); + } + + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) +{ + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ + MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); + + if (pDataStream->isDecoderInitialized) { + ma_decoder_uninit(&pDataStream->decoder); + } + + if (pDataStream->pPageData != NULL) { + ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); + pDataStream->pPageData = NULL; /* Just in case... */ + } + + ma_data_source_uninit(&pDataStream->ds); + + /* The event needs to be signalled last. */ + if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { + ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); + } + if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { + ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); + } + + /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + return MA_SUCCESS; +} + +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams, the status should be MA_SUCCESS. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { + result = MA_INVALID_OPERATION; + goto done; + } + + ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); + +done: + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) +{ + ma_result result = MA_SUCCESS; + ma_resource_manager* pResourceManager; + ma_resource_manager_data_stream* pDataStream; + + MA_ASSERT(pJob != NULL); + + pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; + MA_ASSERT(pDataStream != NULL); + + pResourceManager = pDataStream->pResourceManager; + + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { + return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ + } + + /* For streams the status should be MA_SUCCESS for this to do anything. */ + if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { + result = MA_INVALID_OPERATION; + goto done; + } + + /* + With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except + instead of initializing the decoder, we seek to a frame. + */ + ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); + + /* After seeking we'll need to reload the pages. */ + ma_resource_manager_data_stream_fill_pages(pDataStream); + + /* We need to let the public API know that we're done seeking. */ + ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + +done: + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); + return result; +} + +MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) +{ + if (pResourceManager == NULL || pJob == NULL) { + return MA_INVALID_ARGS; + } + + return ma_job_process(pJob); +} + +MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) +{ + ma_result result; + ma_job job; + + if (pResourceManager == NULL) { + return MA_INVALID_ARGS; + } + + /* This will return MA_CANCELLED if the next job is a quit job. */ + result = ma_resource_manager_next_job(pResourceManager, &job); + if (result != MA_SUCCESS) { + return result; + } + + return ma_job_process(&job); +} +#else +/* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ +static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } +#endif /* MA_NO_RESOURCE_MANAGER */ + + +#ifndef MA_NO_NODE_GRAPH +/* 10ms @ 48K = 480. Must never exceed 65535. */ +#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS +#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 +#endif + + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); + +MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) +{ + #ifndef MA_NO_GENERATION + { + ma_waveform_config waveformConfig; + ma_waveform waveform; + + waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); + ma_waveform_init(&waveformConfig, &waveform); + ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); + } + #else + { + (void)pFramesOut; + (void)frameCount; + (void)format; + (void)channels; + (void)sampleRate; + #if defined(MA_DEBUG_OUTPUT) + { + #if _MSC_VER + #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") + #endif + } + #endif + } + #endif +} + + + +MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) +{ + ma_node_graph_config config; + + MA_ZERO_OBJECT(&config); + config.channels = channels; + config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + + return config; +} + + +static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) +{ + MA_ASSERT(pNodeGraph != NULL); + ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); +} + +#if 0 +static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) +{ + MA_ASSERT(pNodeGraph != NULL); + return ma_atomic_load_32(&pNodeGraph->isReading); +} +#endif + + +static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; + ma_uint64 framesRead; + + ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); + + *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ + + (void)ppFramesIn; + (void)pFrameCountIn; +} + +static ma_node_vtable g_node_graph_node_vtable = +{ + ma_node_graph_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 /* Flags. */ +}; + +static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + MA_ASSERT(pNode != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); + MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); + + /* Input channel count needs to be the same as the output channel count. */ + MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); + + /* We don't need to do anything here because it's a passthrough. */ + (void)pNode; + (void)ppFramesIn; + (void)pFrameCountIn; + (void)ppFramesOut; + (void)pFrameCountOut; + +#if 0 + /* The data has already been mixed. We just need to move it to the output buffer. */ + if (ppFramesIn != NULL) { + ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); + } +#endif +} + +static ma_node_vtable g_node_graph_endpoint_vtable = +{ + ma_node_graph_endpoint_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + 1, /* 1 output bus. */ + MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ +}; + +MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) +{ + ma_result result; + ma_node_config baseConfig; + ma_node_config endpointConfig; + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeGraph); + pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; + if (pNodeGraph->nodeCacheCapInFrames == 0) { + pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; + } + + + /* Base node so we can use the node graph as a node into another graph. */ + baseConfig = ma_node_config_init(); + baseConfig.vtable = &g_node_graph_node_vtable; + baseConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); + if (result != MA_SUCCESS) { + return result; + } + + + /* Endpoint. */ + endpointConfig = ma_node_config_init(); + endpointConfig.vtable = &g_node_graph_endpoint_vtable; + endpointConfig.pInputChannels = &pConfig->channels; + endpointConfig.pOutputChannels = &pConfig->channels; + + result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); + if (result != MA_SUCCESS) { + ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); + return result; + } + + return MA_SUCCESS; +} + +MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pNodeGraph == NULL) { + return; + } + + ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); +} + +MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return NULL; + } + + return &pNodeGraph->endpoint; +} + +MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result = MA_SUCCESS; + ma_uint64 totalFramesRead; + ma_uint32 channels; + + if (pFramesRead != NULL) { + *pFramesRead = 0; /* Safety. */ + } + + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); + + + /* We'll be nice and try to do a full read of all frameCount frames. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + ma_uint32 framesJustRead; + ma_uint64 framesToRead = frameCount - totalFramesRead; + + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); + { + result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); + } + ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); + + totalFramesRead += framesJustRead; + + if (result != MA_SUCCESS) { + break; + } + + /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ + if (totalFramesRead < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); + } + + if (pFramesRead != NULL) { + *pFramesRead = totalFramesRead; + } + + return result; +} + +MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); +} + +MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) +{ + if (pNodeGraph == NULL) { + return 0; + } + + return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ +} + +MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) +{ + if (pNodeGraph == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ +} + + +#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ + +static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pOutputBus != NULL); + MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); + MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pOutputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pOutputBus->pNode = pNode; + pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; + pOutputBus->channels = (ma_uint8)channels; + pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ + pOutputBus->volume = 1; + + return MA_SUCCESS; +} + +static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_lock(&pOutputBus->lock); +} + +static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) +{ + ma_spinlock_unlock(&pOutputBus->lock); +} + + +static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) +{ + return pOutputBus->channels; +} + + +static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) +{ + if (hasRead) { + ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } else { + ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + } +} + +static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) +{ + return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; +} + + +static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) +{ + ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); +} + +static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) +{ + return ma_atomic_load_32(&pOutputBus->isAttached); +} + + +static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) +{ + MA_ASSERT(pOutputBus != NULL); + + if (volume < 0.0f) { + volume = 0.0f; + } + + ma_atomic_exchange_f32(&pOutputBus->volume, volume); + + return MA_SUCCESS; +} + +static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) +{ + return ma_atomic_load_f32((float*)&pOutputBus->volume); +} + + +static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(channels < 256); + + MA_ZERO_OBJECT(pInputBus); + + if (channels == 0) { + return MA_INVALID_ARGS; + } + + pInputBus->channels = (ma_uint8)channels; + + return MA_SUCCESS; +} + +static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + + ma_spinlock_lock(&pInputBus->lock); +} + +static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) +{ + MA_ASSERT(pInputBus != NULL); + + ma_spinlock_unlock(&pInputBus->lock); +} + + +static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) +{ + ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); +} + +static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) +{ + ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); +} + +static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) +{ + return ma_atomic_load_32(&pInputBus->nextCounter); +} + + +static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) +{ + return pInputBus->channels; +} + + +static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + /* + Mark the output bus as detached first. This will prevent future iterations on the audio thread + from iterating this output bus. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); + + /* + We cannot use the output bus lock here since it'll be getting used at a higher level, but we do + still need to use the input bus lock since we'll be updating pointers on two different output + buses. The same rules apply here as the attaching case. Although we're using a lock here, we're + *not* using a lock when iterating over the list in the audio thread. We therefore need to craft + this in a way such that the iteration on the audio thread doesn't break. + + The the first thing to do is swap out the "next" pointer of the previous output bus with the + new "next" output bus. This is the operation that matters for iteration on the audio thread. + After that, the previous pointer on the new "next" pointer needs to be updated, after which + point the linked list will be in a good state. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); + + if (pOldPrev != NULL) { + ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + } + if (pOldNext != NULL) { + ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + } + } + ma_node_input_bus_unlock(pInputBus); + + /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ + ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + pOutputBus->pInputNode = NULL; + pOutputBus->inputNodeInputBusIndex = 0; + + + /* + For thread-safety reasons, we don't want to be returning from this straight away. We need to + wait for the audio thread to finish with the output bus. There's two things we need to wait + for. The first is the part that selects the next output bus in the list, and the other is the + part that reads from the output bus. Basically all we're doing is waiting for the input bus + to stop referencing the output bus. + + We're doing this part last because we want the section above to run while the audio thread + is finishing up with the output bus, just for efficiency reasons. We marked the output bus as + detached right at the top of this function which is going to prevent the audio thread from + iterating the output bus again. + */ + + /* Part 1: Wait for the current iteration to complete. */ + while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { + ma_yield(); + } + + /* Part 2: Wait for any reads to complete. */ + while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { + ma_yield(); + } + + /* + At this point we're done detaching and we can be guaranteed that the audio thread is not going + to attempt to reference this output bus again (until attached again). + */ +} + +#if 0 /* Not used at the moment, but leaving here in case I need it later. */ +static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + ma_node_output_bus_unlock(pOutputBus); +} +#endif + +static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) +{ + MA_ASSERT(pInputBus != NULL); + MA_ASSERT(pOutputBus != NULL); + + ma_node_output_bus_lock(pOutputBus); + { + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); + + /* Detach from any existing attachment first if necessary. */ + if (pOldInputNode != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); + } + + /* + At this point we can be sure the output bus is not attached to anything. The linked list in the + old input bus has been updated so that pOutputBus will not get iterated again. + */ + pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ + pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; + + /* + Now we need to attach the output bus to the linked list. This involves updating two pointers on + two different output buses so I'm going to go ahead and keep this simple and just use a lock. + There are ways to do this without a lock, but it's just too hard to maintain for it's value. + + Although we're locking here, it's important to remember that we're *not* locking when iterating + and reading audio data since that'll be running on the audio thread. As a result we need to be + careful how we craft this so that we don't break iteration. What we're going to do is always + attach the new item so that it becomes the first item in the list. That way, as we're iterating + we won't break any links in the list and iteration will continue safely. The detaching case will + also be crafted in a way as to not break list iteration. It's important to remember to use + atomic exchanges here since no locking is happening on the audio thread during iteration. + */ + ma_node_input_bus_lock(pInputBus); + { + ma_node_output_bus* pNewPrev = &pInputBus->head; + ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); + + /* Update the local output bus. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + + /* Update the other output buses to point back to the local output bus. */ + ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + + /* Do the previous pointer last. This is only used for detachment. */ + if (pNewNext != NULL) { + ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + } + } + ma_node_input_bus_unlock(pInputBus); + + /* + Mark the node as attached last. This is used to controlling whether or the output bus will be + iterated on the audio thread. Mainly required for detachment purposes. + */ + ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); + } + ma_node_output_bus_unlock(pOutputBus); +} + +static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) +{ + ma_node_output_bus* pNext; + + MA_ASSERT(pInputBus != NULL); + + if (pOutputBus == NULL) { + return NULL; + } + + ma_node_input_bus_next_begin(pInputBus); + { + pNext = pOutputBus; + for (;;) { + pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); + if (pNext == NULL) { + break; /* Reached the end. */ + } + + if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { + continue; /* The node is not attached. Keep checking. */ + } + + /* The next node has been selected. */ + break; + } + + /* We need to increment the reference count of the selected node. */ + if (pNext != NULL) { + ma_atomic_fetch_add_32(&pNext->refCount, 1); + } + + /* The previous node is no longer being referenced. */ + ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); + } + ma_node_input_bus_next_end(pInputBus); + + return pNext; +} + +static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) +{ + return ma_node_input_bus_next(pInputBus, &pInputBus->head); +} + + + +static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_result result = MA_SUCCESS; + ma_node_output_bus* pOutputBus; + ma_node_output_bus* pFirst; + ma_uint32 inputChannels; + ma_bool32 doesOutputBufferHaveContent = MA_FALSE; + + (void)pInputNode; /* Not currently used. */ + + /* + This will be called from the audio thread which means we can't be doing any locking. Basically, + this function will not perfom any locking, whereas attaching and detaching will, but crafted in + such a way that we don't need to perform any locking here. The important thing to remember is + to always iterate in a forward direction. + + In order to process any data we need to first read from all input buses. That's where this + function comes in. This iterates over each of the attachments and accumulates/mixes them. We + also convert the channels to the nodes output channel count before mixing. We want to do this + channel conversion so that the caller of this function can invoke the processing callback + without having to do it themselves. + + When we iterate over each of the attachments on the input bus, we need to read as much data as + we can from each of them so that we don't end up with holes between each of the attachments. To + do this, we need to read from each attachment in a loop and read as many frames as we can, up + to `frameCount`. + */ + MA_ASSERT(pInputNode != NULL); + MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ + + *pFramesRead = 0; /* Safety. */ + + inputChannels = ma_node_input_bus_get_channels(pInputBus); + + /* + We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They + are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() + once per iteration, however we have an optimization to checks whether or not it's the first item in + the list. We therefore need to store a pointer to the first item rather than repeatedly calling + ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it + after calling ma_node_input_bus_next(), which we won't be. + */ + pFirst = ma_node_input_bus_first(pInputBus); + if (pFirst == NULL) { + return MA_SUCCESS; /* No attachments. Read nothing. */ + } + + for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { + ma_uint32 framesProcessed = 0; + ma_bool32 isSilentOutput = MA_FALSE; + + MA_ASSERT(pOutputBus->pNode != NULL); + MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL); + + isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; + + if (pFramesOut != NULL) { + /* Read. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; + + while (framesProcessed < frameCount) { + float* pRunningFramesOut; + ma_uint32 framesToRead; + ma_uint32 framesJustRead; + + framesToRead = frameCount - framesProcessed; + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); + + if (doesOutputBufferHaveContent == MA_FALSE) { + /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); + } else { + /* Slow path. Not the first attachment. Mixing required. */ + result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); + if (result == MA_SUCCESS || result == MA_AT_END) { + if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ + ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); + } + } + } + + framesProcessed += framesJustRead; + + /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ + if (result != MA_SUCCESS) { + break; + } + + /* If we didn't read anything, abort so we don't get stuck in a loop. */ + if (framesJustRead == 0) { + break; + } + } + + /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ + if (pOutputBus == pFirst && framesProcessed < frameCount) { + ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); + } + + if (isSilentOutput == MA_FALSE) { + doesOutputBufferHaveContent = MA_TRUE; + } + } else { + /* Seek. */ + ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); + } + } + + /* If we didn't output anything, output silence. */ + if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); + } + + /* In this path we always "process" the entire amount. */ + *pFramesRead = frameCount; + + return result; +} + + +MA_API ma_node_config ma_node_config_init(void) +{ + ma_node_config config; + + MA_ZERO_OBJECT(&config); + config.initialState = ma_node_state_started; /* Nodes are started by default. */ + config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; + + return config; +} + + + +static ma_result ma_node_detach_full(ma_node* pNode); + +static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Input data is stored at the front of the buffer. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + return pBasePtr; +} + +static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + float* pBasePtr; + + MA_ASSERT(pNodeBase != NULL); + + /* Cached output data starts after the input data. */ + pBasePtr = pNodeBase->pCachedData; + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); + } + + for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { + pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); + } + + return pBasePtr; +} + + +typedef struct +{ + size_t sizeInBytes; + size_t inputBusOffset; + size_t outputBusOffset; + size_t cachedDataOffset; + ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ + ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ +} ma_node_heap_layout; + +static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) +{ + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pInputBusCount != NULL); + MA_ASSERT(pOutputBusCount != NULL); + + /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ + if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + inputBusCount = pConfig->inputBusCount; + } else { + inputBusCount = pConfig->vtable->inputBusCount; + + if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { + outputBusCount = pConfig->outputBusCount; + } else { + outputBusCount = pConfig->vtable->outputBusCount; + + if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { + return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ + } + } + + /* Bus counts must be within limits. */ + if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; + } + + + /* We must have channel counts for each bus. */ + if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { + return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ + } + + + /* Some special rules for passthrough nodes. */ + if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) { + return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */ + } + + if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { + return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ + } + } + + + *pInputBusCount = inputBusCount; + *pOutputBusCount = outputBusCount; + + return MA_SUCCESS; +} + +static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) +{ + ma_result result; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + + MA_ASSERT(pHeapLayout != NULL); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->sizeInBytes = 0; + + /* Input buses. */ + if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); + } else { + pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ + } + + /* Output buses. */ + if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { + pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); + } else { + pHeapLayout->outputBusOffset = MA_SIZE_MAX; + } + + /* + Cached audio data. + + We need to allocate memory for a caching both input and output data. We have an optimization + where no caching is necessary for specific conditions: + + - The node has 0 inputs and 1 output. + + When a node meets the above conditions, no cache is allocated. + + The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by + allocating too much, but at the same time we want it be large enough so that enough frames can + be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For + now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile + time. It might also be worth investigating whether or not this can be configured at run time. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No cache needed. */ + pHeapLayout->cachedDataOffset = MA_SIZE_MAX; + } else { + /* Slow path. Cache needed. */ + size_t cachedDataSizeInBytes = 0; + ma_uint32 iBus; + + for (iBus = 0; iBus < inputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); + } + + for (iBus = 0; iBus < outputBusCount; iBus += 1) { + cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); + } + + pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); + } + + + /* + Not technically part of the heap, but we can output the input and output bus counts so we can + avoid a redundant call to ma_node_translate_bus_counts(). + */ + pHeapLayout->inputBusCount = inputBusCount; + pHeapLayout->outputBusCount = outputBusCount; + + /* Make sure allocation size is aligned. */ + pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result; + ma_node_heap_layout heapLayout; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNodeBase); + + result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + pNodeBase->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pNodeBase->pNodeGraph = pNodeGraph; + pNodeBase->vtable = pConfig->vtable; + pNodeBase->state = pConfig->initialState; + pNodeBase->stateTimes[ma_node_state_started] = 0; + pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ + pNodeBase->inputBusCount = heapLayout.inputBusCount; + pNodeBase->outputBusCount = heapLayout.outputBusCount; + + if (heapLayout.inputBusOffset != MA_SIZE_MAX) { + pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + } else { + pNodeBase->pInputBuses = pNodeBase->_inputBuses; + } + + if (heapLayout.outputBusOffset != MA_SIZE_MAX) { + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); + } else { + pNodeBase->pOutputBuses = pNodeBase->_outputBuses; + } + + if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { + pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); + pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; + } else { + pNodeBase->pCachedData = NULL; + } + + + + /* We need to run an initialization step for each input and output bus. */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { + result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); + if (result != MA_SUCCESS) { + return result; + } + } + + + /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ + if (pNodeBase->pCachedData != NULL) { + ma_uint32 iBus; + + #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ + /* For safety we'll go ahead and default the buffer to silence. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); + } + #else + /* For debugging. Default to a sine wave. */ + for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); + } + for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { + ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); + } + #endif + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return; + } + + /* + The first thing we need to do is fully detach the node. This will detach all inputs and + outputs. We need to do this first because it will sever the connection with the node graph and + allow us to complete uninitialization without needing to worry about thread-safety with the + audio thread. The detachment process will wait for any local processing of the node to finish. + */ + ma_node_detach_full(pNode); + + /* + At this point the node should be completely unreferenced by the node graph and we can finish up + the uninitialization process without needing to worry about thread-safety. + */ + if (pNodeBase->_ownsHeap) { + ma_free(pNodeBase->_pHeap, pAllocationCallbacks); + } +} + +MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) +{ + if (pNode == NULL) { + return NULL; + } + + return ((const ma_node_base*)pNode)->pNodeGraph; +} + +MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->inputBusCount; +} + +MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ((ma_node_base*)pNode)->outputBusCount; +} + + +MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); +} + +MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNode == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); +} + + +static ma_result ma_node_detach_full(ma_node* pNode) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iInputBus; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + /* + Make sure the node is completely detached first. This will not return until the output bus is + guaranteed to no longer be referenced by the audio thread. + */ + ma_node_detach_all_output_buses(pNode); + + /* + At this point all output buses will have been detached from the graph and we can be guaranteed + that none of it's input nodes will be getting processed by the graph. We can detach these + without needing to worry about the audio thread touching them. + */ + for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { + ma_node_input_bus* pInputBus; + ma_node_output_bus* pOutputBus; + + pInputBus = &pNodeBase->pInputBuses[iInputBus]; + + /* + This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those + functions are specifically for the audio thread. We'll instead just manually iterate using standard + linked list logic. We don't need to worry about the audio thread referencing these because the step + above severed the connection to the graph. + */ + for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { + ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pInputNodeBase; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ + ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); + { + pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; + if (pInputNodeBase != NULL) { + ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); + } + } + ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); + + return result; +} + +MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) +{ + ma_uint32 iOutputBus; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { + ma_node_detach_output_bus(pNode, iOutputBus); + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; + + if (pNodeBase == NULL || pOtherNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (pNodeBase == pOtherNodeBase) { + return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { + return MA_INVALID_OPERATION; /* Invalid bus index. */ + } + + /* The output channel count of the output node must be the same as the input channel count of the input node. */ + if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { + return MA_INVALID_OPERATION; /* Channel count is incompatible. */ + } + + /* This will deal with detaching if the output bus is already attached to something. */ + ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return MA_INVALID_ARGS; /* Invalid bus index. */ + } + + return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); +} + +MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return 0; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { + return 0; /* Invalid bus index. */ + } + + return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); +} + +MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_i32(&pNodeBase->state, state); + + return MA_SUCCESS; +} + +MA_API ma_node_state ma_node_get_state(const ma_node* pNode) +{ + const ma_node_base* pNodeBase = (const ma_node_base*)pNode; + + if (pNodeBase == NULL) { + return ma_node_state_stopped; + } + + return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); +} + +MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + + return MA_SUCCESS; +} + +MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) +{ + if (pNode == NULL) { + return 0; + } + + /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ + if (state != ma_node_state_started && state != ma_node_state_stopped) { + return 0; + } + + return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); +} + +MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) +{ + if (pNode == NULL) { + return ma_node_state_stopped; + } + + return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); +} + +MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) +{ + ma_node_state state; + + if (pNode == NULL) { + return ma_node_state_stopped; + } + + state = ma_node_get_state(pNode); + + /* An explicitly stopped node is always stopped. */ + if (state == ma_node_state_stopped) { + return ma_node_state_stopped; + } + + /* + Getting here means the node is marked as started, but it may still not be truly started due to + it's start time not having been reached yet. Also, the stop time may have also been reached in + which case it'll be considered stopped. + */ + if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { + return ma_node_state_stopped; /* Start time has not yet been reached. */ + } + + if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { + return ma_node_state_stopped; /* Stop time has been reached. */ + } + + /* Getting here means the node is marked as started and is within it's start/stop times. */ + return ma_node_state_started; +} + +MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) +{ + if (pNode == NULL) { + return 0; + } + + return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); +} + +MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) +{ + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + + return MA_SUCCESS; +} + + + +static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + + MA_ASSERT(pNode != NULL); + + if (pNodeBase->vtable->onProcess) { + pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); + } +} + +static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_result result = MA_SUCCESS; + ma_uint32 iInputBus; + ma_uint32 iOutputBus; + ma_uint32 inputBusCount; + ma_uint32 outputBusCount; + ma_uint32 totalFramesRead = 0; + float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; + float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; + ma_uint64 globalTimeBeg; + ma_uint64 globalTimeEnd; + ma_uint64 startTime; + ma_uint64 stopTime; + ma_uint32 timeOffsetBeg; + ma_uint32 timeOffsetEnd; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + + /* + pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and + expected that the number of frames read may be different to that requested. Therefore, the caller + must look at this value to correctly determine how many frames were read. + */ + MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ + if (pFramesRead == NULL) { + return MA_INVALID_ARGS; + } + + *pFramesRead = 0; /* Safety. */ + + if (pNodeBase == NULL) { + return MA_INVALID_ARGS; + } + + if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { + return MA_INVALID_ARGS; /* Invalid output bus index. */ + } + + /* Don't do anything if we're in a stopped state. */ + if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { + return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ + } + + + globalTimeBeg = globalTime; + globalTimeEnd = globalTime + frameCount; + startTime = ma_node_get_state_time(pNode, ma_node_state_started); + stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); + + /* + At this point we know that we are inside our start/stop times. However, we may need to adjust + our frame count and output pointer to accommodate since we could be straddling the time period + that this function is getting called for. + + It's possible (and likely) that the start time does not line up with the output buffer. We + therefore need to offset it by a number of frames to accommodate. The same thing applies for + the stop time. + */ + timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; + timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; + + /* Trim based on the start offset. We need to silence the start of the buffer. */ + if (timeOffsetBeg > 0) { + ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); + frameCount -= timeOffsetBeg; + } + + /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ + if (timeOffsetEnd > 0) { + frameCount -= timeOffsetEnd; + } + + + /* We run on different paths depending on the bus counts. */ + inputBusCount = ma_node_get_input_bus_count(pNode); + outputBusCount = ma_node_get_output_bus_count(pNode); + + /* + Run a simplified path when there are no inputs and one output. In this case there's nothing to + actually read and we can go straight to output. This is a very common scenario because the vast + majority of data source nodes will use this setup so this optimization I think is worthwhile. + */ + if (inputBusCount == 0 && outputBusCount == 1) { + /* Fast path. No need to read from input and no need for any caching. */ + frameCountIn = 0; + frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ + + ppFramesOut[0] = pFramesOut; + + /* + If it's a passthrough we won't be expecting the callback to output anything, so we'll + need to pre-silence the output buffer. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); + } + + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + totalFramesRead = frameCountOut; + } else { + /* Slow path. Need to read input data. */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { + /* + Fast path. We're running a passthrough. We need to read directly into the output buffer, but + still fire the callback so that event handling and trigger nodes can do their thing. Since + it's a passthrough there's no need for any kind of caching logic. + */ + MA_ASSERT(outputBusCount == inputBusCount); + MA_ASSERT(outputBusCount == 1); + MA_ASSERT(outputBusIndex == 0); + + /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ + ppFramesOut[0] = pFramesOut; + ppFramesIn[0] = ppFramesOut[0]; + + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); + if (result == MA_SUCCESS) { + /* Even though it's a passthrough, we still need to fire the callback. */ + frameCountIn = totalFramesRead; + frameCountOut = totalFramesRead; + + if (totalFramesRead > 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } + + /* + A passthrough should never have modified the input and output frame counts. If you're + triggering these assers you need to fix your processing callback. + */ + MA_ASSERT(frameCountIn == totalFramesRead); + MA_ASSERT(frameCountOut == totalFramesRead); + } + } else { + /* Slow path. Need to do caching. */ + ma_uint32 framesToProcessIn; + ma_uint32 framesToProcessOut; + ma_bool32 consumeNullInput = MA_FALSE; + + /* + We use frameCount as a basis for the number of frames to read since that's what's being + requested, however we still need to clamp it to whatever can fit in the cache. + + This will also be used as the basis for determining how many input frames to read. This is + not ideal because it can result in too many input frames being read which introduces latency. + To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount + which is used as hint to miniaudio as to how many input frames it needs to read at a time. This + callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. + + This function will be called multiple times for each period of time, once for each output node. + We cannot read from each input node each time this function is called. Instead we need to check + whether or not this is first output bus to be read from for this time period, and if so, read + from our input data. + + To determine whether or not we're ready to read data, we check a flag. There will be one flag + for each output. When the flag is set, it means data has been read previously and that we're + ready to advance time forward for our input nodes by reading fresh data. + */ + framesToProcessOut = frameCount; + if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; + } + + framesToProcessIn = frameCount; + if (pNodeBase->vtable->onGetRequiredInputFrameCount) { + pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ + } + if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { + framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; + } + + + MA_ASSERT(framesToProcessIn <= 0xFFFF); + MA_ASSERT(framesToProcessOut <= 0xFFFF); + + if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { + /* Getting here means we need to do another round of processing. */ + pNodeBase->cachedFrameCountOut = 0; + + for (;;) { + frameCountOut = 0; + + /* + We need to prepare our output frame pointers for processing. In the same iteration we need + to mark every output bus as unread so that future calls to this function for different buses + for the current time period don't pull in data when they should instead be reading from cache. + */ + for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ + ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); + } + + /* We only need to read from input buses if there isn't already some data in the cache. */ + if (pNodeBase->cachedFrameCountIn == 0) { + ma_uint32 maxFramesReadIn = 0; + + /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ma_uint32 framesRead; + + /* The first thing to do is get the offset within our bulk allocation to store this input data. */ + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); + + /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ + result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); + if (result != MA_SUCCESS) { + /* It doesn't really matter if we fail because we'll just fill with silence. */ + framesRead = 0; /* Just for safety, but I don't think it's really needed. */ + } + + /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ + /* Any leftover frames need to silenced for safety. */ + if (framesRead < framesToProcessIn) { + ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); + } + + maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); + } + + /* This was a fresh load of input data so reset our consumption counter. */ + pNodeBase->consumedFrameCountIn = 0; + + /* + We don't want to keep processing if there's nothing to process, so set the number of cached + input frames to the maximum number we read from each attachment (the lesser will be padded + with silence). If we didn't read anything, this will be set to 0 and the entire buffer will + have been assigned to silence. This being equal to 0 is an important property for us because + it allows us to detect when NULL can be passed into the processing callback for the input + buffer for the purpose of continuous processing. + */ + pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; + } else { + /* We don't need to read anything, but we do need to prepare our input frame pointers. */ + for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { + ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); + } + } + + /* + At this point we have our input data so now we need to do some processing. Sneaky little + optimization here - we can set the pointer to the output buffer for this output bus so + that the final copy into the output buffer is done directly by onProcess(). + */ + if (pFramesOut != NULL) { + ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); + } + + + /* Give the processing function the entire capacity of the output buffer. */ + frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); + + /* + We need to treat nodes with continuous processing a little differently. For these ones, + we always want to fire the callback with the requested number of frames, regardless of + pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass + in NULL for the input buffer to the callback. + */ + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { + /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ + frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ + + if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { + consumeNullInput = MA_TRUE; + } else { + consumeNullInput = MA_FALSE; + } + + /* + Since we're using continuous processing we're always passing in a full frame count + regardless of how much input data was read. If this is greater than what we read as + input, we'll end up with an underflow. We instead need to make sure our cached frame + count is set to the number of frames we'll be passing to the data callback. Not + doing this will result in an underflow when we "consume" the cached data later on. + + Note that this check needs to be done after the "consumeNullInput" check above because + we use the property of cachedFrameCountIn being 0 to determine whether or not we + should be passing in a null pointer to the processing callback for when the node is + configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. + */ + if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { + pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; + } + } else { + frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ + consumeNullInput = MA_FALSE; + } + + /* + Process data slightly differently depending on whether or not we're consuming NULL + input (checked just above). + */ + if (consumeNullInput) { + ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); + } else { + /* + We want to skip processing if there's no input data, but we can only do that safely if + we know that there is no chance of any output frames being produced. If continuous + processing is being used, this won't be a problem because the input frame count will + always be non-0. However, if continuous processing is *not* enabled and input and output + data is processed at different rates, we still need to process that last input frame + because there could be a few excess output frames needing to be produced from cached + data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for + determining whether or not we need to process the node even when there are no input + frames available right now. + */ + if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { + ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ + } else { + frameCountOut = 0; /* No data was processed. */ + } + } + + /* + Thanks to our sneaky optimization above we don't need to do any data copying directly into + the output buffer - the onProcess() callback just did that for us. We do, however, need to + apply the number of input and output frames that were processed. Note that due to continuous + processing above, we need to do explicit checks here. If we just consumed a NULL input + buffer it means that no actual input data was processed from the internal buffers and we + don't want to be modifying any counters. + */ + if (consumeNullInput == MA_FALSE) { + pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; + pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; + } + + /* The cached output frame count is always equal to what we just read. */ + pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; + + /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ + if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { + break; + } + } + } else { + /* + We're not needing to read anything from the input buffer so just read directly from our + already-processed data. + */ + if (pFramesOut != NULL) { + ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); + } + } + + /* The number of frames read is always equal to the number of cached output frames. */ + totalFramesRead = pNodeBase->cachedFrameCountOut; + + /* Now that we've read the data, make sure our read flag is set. */ + ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); + } + } + + /* Apply volume, if necessary. */ + ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); + + /* Advance our local time forward. */ + ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + + *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ + return result; +} + + + + +/* Data source node. */ +MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) +{ + ma_data_source_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.pDataSource = pDataSource; + + return config; +} + + +static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; + ma_format format; + ma_uint32 channels; + ma_uint32 frameCount; + ma_uint64 framesRead = 0; + + MA_ASSERT(pDataSourceNode != NULL); + MA_ASSERT(pDataSourceNode->pDataSource != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); + MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); + + /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + frameCount = *pFrameCountOut; + + /* miniaudio should never be calling this with a frame count of zero. */ + MA_ASSERT(frameCount > 0); + + if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ + /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ + MA_ASSERT(format == ma_format_f32); + (void)format; /* Just to silence some static analysis tools. */ + + ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); + } + + *pFrameCountOut = (ma_uint32)framesRead; +} + +static ma_node_vtable g_ma_data_source_node_vtable = +{ + ma_data_source_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* 0 input buses. */ + 1, /* 1 output bus. */ + 0 +}; + +MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) +{ + ma_result result; + ma_format format; /* For validating the format, which must be ma_format_f32. */ + ma_uint32 channels; /* For specifying the channel count of the output bus. */ + ma_node_config baseConfig; + + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDataSourceNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ + if (result != MA_SUCCESS) { + return result; + } + + MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ + if (format != ma_format_f32) { + return MA_INVALID_ARGS; /* Invalid format. */ + } + + /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ + + /* + The channel count is defined by the data source. It is invalid for the caller to manually set + the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the + channel count pointer to NULL which is how it must remain. If you trigger any of these asserts + it means you're explicitly setting the channel count. Instead, configure the output channel + count of your data source to be the necessary channel count. + */ + if (baseConfig.pOutputChannels != NULL) { + return MA_INVALID_ARGS; + } + + baseConfig.pOutputChannels = &channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); + if (result != MA_SUCCESS) { + return result; + } + + pDataSourceNode->pDataSource = pConfig->pDataSource; + + return MA_SUCCESS; +} + +MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); +} + +MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) +{ + if (pDataSourceNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) +{ + if (pDataSourceNode == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pDataSourceNode->pDataSource); +} + + + +/* Splitter Node. */ +MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) +{ + ma_splitter_node_config config; + + MA_ZERO_OBJECT(&config); + config.nodeConfig = ma_node_config_init(); + config.channels = channels; + config.outputBusCount = 2; + + return config; +} + + +static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_node_base* pNodeBase = (ma_node_base*)pNode; + ma_uint32 iOutputBus; + ma_uint32 channels; + + MA_ASSERT(pNodeBase != NULL); + MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); + + /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ + (void)pFrameCountIn; + + /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ + channels = ma_node_get_input_channels(pNodeBase, 0); + + /* Splitting is just copying the first input bus and copying it over to each output bus. */ + for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { + ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); + } +} + +static ma_node_vtable g_ma_splitter_node_vtable = +{ + ma_splitter_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* 1 input bus. */ + MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ + 0 +}; + +MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) +{ + ma_result result; + ma_node_config baseConfig; + ma_uint32 pInputChannels[1]; + ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; + ma_uint32 iOutputBus; + + if (pSplitterNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSplitterNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { + return MA_INVALID_ARGS; /* Too many output buses. */ + } + + /* Splitters require the same number of channels between inputs and outputs. */ + pInputChannels[0] = pConfig->channels; + for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { + pOutputChannels[iOutputBus] = pConfig->channels; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_splitter_node_vtable; + baseConfig.pInputChannels = pInputChannels; + baseConfig.pOutputChannels = pOutputChannels; + baseConfig.outputBusCount = pConfig->outputBusCount; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the base node. */ + } + + return MA_SUCCESS; +} + +MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_node_uninit(pSplitterNode, pAllocationCallbacks); +} + + +/* +Biquad Node +*/ +MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) +{ + ma_biquad_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); + + return config; +} + +static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_biquad_node_vtable = +{ + ma_biquad_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->biquad.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_biquad_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->biquad.channels; + baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + MA_ASSERT(pNode != NULL); + + return ma_biquad_reinit(pConfig, &pLPFNode->biquad); +} + +MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); +} + + + +/* +Low Pass Filter Node +*/ +MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_lpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_lpf_node_vtable = +{ + ma_lpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->lpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_lpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->lpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_lpf_reinit(pConfig, &pLPFNode->lpf); +} + +MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); +} + + + +/* +High Pass Filter Node +*/ +MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_hpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hpf_node_vtable = +{ + ma_hpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hpf_reinit(pConfig, &pHPFNode->hpf); +} + +MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); +} + + + + +/* +Band Pass Filter Node +*/ +MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) +{ + ma_bpf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); + + return config; +} + +static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_bpf_node_vtable = +{ + ma_bpf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->bpf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_bpf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->bpf.channels; + baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_bpf_reinit(pConfig, &pBPFNode->bpf); +} + +MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); +} + + + +/* +Notching Filter Node +*/ +MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) +{ + ma_notch_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); + + return config; +} + +static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_notch_node* pBPFNode = (ma_notch_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_notch_node_vtable = +{ + ma_notch_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->notch.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_notch_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->notch.channels; + baseNodeConfig.pOutputChannels = &pConfig->notch.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_notch2_reinit(pConfig, &pNotchNode->notch); +} + +MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_notch_node* pNotchNode = (ma_notch_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); +} + + + +/* +Peaking Filter Node +*/ +MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_peak_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_peak_node* pBPFNode = (ma_peak_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_peak_node_vtable = +{ + ma_peak_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->peak.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); + if (result != MA_SUCCESS) { + ma_node_uninit(pNode, pAllocationCallbacks); + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_peak_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->peak.channels; + baseNodeConfig.pOutputChannels = &pConfig->peak.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_peak2_reinit(pConfig, &pPeakNode->peak); +} + +MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_peak_node* pPeakNode = (ma_peak_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); +} + + + +/* +Low Shelf Filter Node +*/ +MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_loshelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_loshelf_node_vtable = +{ + ma_loshelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->loshelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); +} + +MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); +} + + + +/* +High Shelf Filter Node +*/ +MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) +{ + ma_hishelf_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); + + return config; +} + +static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; + + MA_ASSERT(pNode != NULL); + (void)pFrameCountIn; + + ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_hishelf_node_vtable = +{ + ma_hishelf_node_process_pcm_frames, + NULL, /* onGetRequiredInputFrameCount */ + 1, /* One input. */ + 1, /* One output. */ + 0 /* Default flags. */ +}; + +MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) +{ + ma_result result; + ma_node_config baseNodeConfig; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pNode); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->hishelf.format != ma_format_f32) { + return MA_INVALID_ARGS; /* The format must be f32. */ + } + + result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); + if (result != MA_SUCCESS) { + return result; + } + + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; + baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; + baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; + + result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); + if (result != MA_SUCCESS) { + return result; + } + + return result; +} + +MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return MA_INVALID_ARGS; + } + + return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); +} + +MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; + + if (pNode == NULL) { + return; + } + + ma_node_uninit(pNode, pAllocationCallbacks); + ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); +} + + + + +MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) +{ + ma_delay_node_config config; + + config.nodeConfig = ma_node_config_init(); + config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); + + return config; +} + + +static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_delay_node* pDelayNode = (ma_delay_node*)pNode; + + (void)pFrameCountIn; + + ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); +} + +static ma_node_vtable g_ma_delay_node_vtable = +{ + ma_delay_node_process_pcm_frames, + NULL, + 1, /* 1 input channels. */ + 1, /* 1 output channel. */ + MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ +}; + +MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) +{ + ma_result result; + ma_node_config baseConfig; + + if (pDelayNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pDelayNode); + + result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); + if (result != MA_SUCCESS) { + return result; + } + + baseConfig = pConfig->nodeConfig; + baseConfig.vtable = &g_ma_delay_node_vtable; + baseConfig.pInputChannels = &pConfig->delay.channels; + baseConfig.pOutputChannels = &pConfig->delay.channels; + + result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); + if (result != MA_SUCCESS) { + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); + return result; + } + + return result; +} + +MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pDelayNode == NULL) { + return; + } + + /* The base node is always uninitialized first. */ + ma_node_uninit(pDelayNode, pAllocationCallbacks); + ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); +} + +MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_wet(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_wet(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_dry(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_dry(&pDelayNode->delay); +} + +MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) +{ + if (pDelayNode == NULL) { + return; + } + + ma_delay_set_decay(&pDelayNode->delay, value); +} + +MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) +{ + if (pDelayNode == NULL) { + return 0; + } + + return ma_delay_get_decay(&pDelayNode->delay); +} +#endif /* MA_NO_NODE_GRAPH */ + + +/* SECTION: miniaudio_engine.c */ +#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) +/************************************************************************************************************************************************************** + +Engine + +**************************************************************************************************************************************************************/ +#define MA_SEEK_TARGET_NONE (~(ma_uint64)0) + + +static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) +{ + MA_ASSERT(pSound != NULL); + ma_atomic_exchange_32(&pSound->atEnd, atEnd); + + /* Fire any callbacks or events. */ + if (atEnd) { + if (pSound->endCallback != NULL) { + pSound->endCallback(pSound->pEndCallbackUserData, pSound); + } + } +} + +static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) +{ + MA_ASSERT(pSound != NULL); + return ma_atomic_load_32(&pSound->atEnd); +} + + +MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) +{ + ma_engine_node_config config; + + MA_ZERO_OBJECT(&config); + config.pEngine = pEngine; + config.type = type; + config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; + config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; + config.monoExpansionMode = pEngine->monoExpansionMode; + + return config; +} + + +static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) +{ + ma_bool32 isUpdateRequired = MA_FALSE; + float newPitch; + + MA_ASSERT(pEngineNode != NULL); + + newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); + + if (pEngineNode->oldPitch != newPitch) { + pEngineNode->oldPitch = newPitch; + isUpdateRequired = MA_TRUE; + } + + if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { + pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; + isUpdateRequired = MA_TRUE; + } + + if (isUpdateRequired) { + float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); + ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); + } +} + +static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ + return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); +} + +static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) +{ + MA_ASSERT(pEngineNode != NULL); + + return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); +} + +static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) +{ + ma_uint64 inputFrameCount = 0; + + if (ma_engine_node_is_pitching_enabled(pEngineNode)) { + ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); + if (result != MA_SUCCESS) { + inputFrameCount = 0; + } + } else { + inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ + } + + return inputFrameCount; +} + +static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume) +{ + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + ma_atomic_float_set(&pEngineNode->volume, volume); + + /* If we're not smoothing we should bypass the volume gainer entirely. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + } else { + /* We're using volume smoothing, so apply the master volume to the gainer. */ + ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); + } + + return MA_SUCCESS; +} + +static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume) +{ + if (pVolume == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = 0.0f; + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); + + return MA_SUCCESS; +} + + +static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + ma_uint32 totalFramesProcessedIn; + ma_uint32 totalFramesProcessedOut; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_bool32 isPitchingEnabled; + ma_bool32 isFadingEnabled; + ma_bool32 isSpatializationEnabled; + ma_bool32 isPanningEnabled; + ma_bool32 isVolumeSmoothingEnabled; + + frameCountIn = *pFrameCountIn; + frameCountOut = *pFrameCountOut; + + channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); + channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); + + totalFramesProcessedIn = 0; + totalFramesProcessedOut = 0; + + /* Update the fader if applicable. */ + { + ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); + if (fadeLengthInFrames != ~(ma_uint64)0) { + float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); + float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); + ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); + if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { + fadeStartOffsetInFrames = 0; + } else { + fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); + } + + ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); + + /* Reset the fade length so we don't erroneously apply it again. */ + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); + } + } + + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); + isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; + isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); + isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; + + /* Keep going while we've still got data available for processing. */ + while (totalFramesProcessedOut < frameCountOut) { + /* + We need to process in a specific order. We always do resampling first because it's likely + we're going to be increasing the channel count after spatialization. Also, I want to do + fading based on the output sample rate. + + We'll first read into a buffer from the resampler. Then we'll do all processing that + operates on the on the input channel count. We'll then get the spatializer to output to + the output buffer and then do all effects from that point directly in the output buffer + in-place. + + Note that we're always running the resampler if pitching is enabled, even when the pitch + is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch + when we move away from 1, back to 1, and then away from 1 again. We'll want to implement + any pitch=1 optimizations in the resampler itself. + + There's a small optimization here that we'll utilize since it might be a fairly common + case. When the input and output channel counts are the same, we'll read straight into the + output buffer from the resampler and do everything in-place. + */ + const float* pRunningFramesIn; + float* pRunningFramesOut; + float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ + float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; + ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; + ma_uint32 framesAvailableIn; + ma_uint32 framesAvailableOut; + ma_uint32 framesJustProcessedIn; + ma_uint32 framesJustProcessedOut; + ma_bool32 isWorkingBufferValid = MA_FALSE; + + framesAvailableIn = frameCountIn - totalFramesProcessedIn; + framesAvailableOut = frameCountOut - totalFramesProcessedOut; + + pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); + + if (channelsIn == channelsOut) { + /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ + pWorkingBuffer = pRunningFramesOut; + } else { + /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ + pWorkingBuffer = temp; + if (framesAvailableOut > tempCapInFrames) { + framesAvailableOut = tempCapInFrames; + } + } + + /* First is resampler. */ + if (isPitchingEnabled) { + ma_uint64 resampleFrameCountIn = framesAvailableIn; + ma_uint64 resampleFrameCountOut = framesAvailableOut; + + ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); + isWorkingBufferValid = MA_TRUE; + + framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; + framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; + } else { + framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); + framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ + } + + /* Fading. */ + if (isFadingEnabled) { + if (isWorkingBufferValid) { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ + } else { + ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + + /* + If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case + we'll want to apply our volume now. + */ + if (isVolumeSmoothingEnabled) { + if (isWorkingBufferValid) { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); + } else { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + + /* + If at this point we still haven't actually done anything with the working buffer we need + to just read straight from the input buffer. + */ + if (isWorkingBufferValid == MA_FALSE) { + pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ + } + + /* Spatialization. */ + if (isSpatializationEnabled) { + ma_uint32 iListener; + + /* + When determining the listener to use, we first check to see if the sound is pinned to a + specific listener. If so, we use that. Otherwise we just use the closest listener. + */ + if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { + iListener = pEngineNode->pinnedListenerIndex; + } else { + ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer); + iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z); + } + + ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); + } else { + /* No spatialization, but we still need to do channel conversion and master volume. */ + float volume; + ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */ + + if (channelsIn == channelsOut) { + /* No channel conversion required. Just copy straight to the output buffer. */ + if (isVolumeSmoothingEnabled) { + /* Volume has already been applied. Just copy straight to the output buffer. */ + ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); + } else { + /* Volume has not been applied yet. Copy and apply volume in the same pass. */ + ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + } + } else { + /* Channel conversion required. TODO: Add support for channel maps here. */ + ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); + + /* If we're using smoothing, the volume will have already been applied. */ + if (!isVolumeSmoothingEnabled) { + ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + } + } + } + + /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ + + /* Panning. */ + if (isPanningEnabled) { + ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ + } + + /* We're done for this chunk. */ + totalFramesProcessedIn += framesJustProcessedIn; + totalFramesProcessedOut += framesJustProcessedOut; + + /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ + if (framesJustProcessedOut == 0) { + break; + } + } + + /* At this point we're done processing. */ + *pFrameCountIn = totalFramesProcessedIn; + *pFrameCountOut = totalFramesProcessedOut; +} + +static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ + ma_result result = MA_SUCCESS; + ma_sound* pSound = (ma_sound*)pNode; + ma_uint32 frameCount = *pFrameCountOut; + ma_uint32 totalFramesRead = 0; + ma_format dataSourceFormat; + ma_uint32 dataSourceChannels; + ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint32 tempCapInFrames; + ma_uint64 seekTarget; + + /* This is a data source node which means no input buses. */ + (void)ppFramesIn; + (void)pFrameCountIn; + + /* If we're marked at the end we need to stop the sound and do nothing. */ + if (ma_sound_at_end(pSound)) { + ma_sound_stop(pSound); + *pFrameCountOut = 0; + return; + } + + /* If we're seeking, do so now before reading. */ + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); + + /* Any time-dependant effects need to have their times updated. */ + ma_node_set_time(pSound, seekTarget); + + ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + } + + /* + We want to update the pitch once. For sounds, this can be either at the start or at the end. If + we don't force this to only ever be updating once, we could end up in a situation where + retrieving the required input frame count ends up being different to what we actually retrieve. + What could happen is that the required input frame count is calculated, the pitch is update, + and then this processing function is called resulting in a different number of input frames + being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else + you'll hit the aforementioned bug. + */ + ma_engine_node_update_pitch_if_required(&pSound->engineNode); + + /* + For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ + from the main engine. + */ + result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); + if (result == MA_SUCCESS) { + tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); + + /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ + while (totalFramesRead < frameCount) { + ma_uint32 framesRemaining = frameCount - totalFramesRead; + ma_uint32 framesToRead; + ma_uint64 framesJustRead; + ma_uint32 frameCountIn; + ma_uint32 frameCountOut; + const float* pRunningFramesIn; + float* pRunningFramesOut; + + /* + The first thing we need to do is read into the temporary buffer. We can calculate exactly + how many input frames we'll need after resampling. + */ + framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); + if (framesToRead > tempCapInFrames) { + framesToRead = tempCapInFrames; + } + + result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); + + /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ + if (result == MA_AT_END) { + ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ + } + + pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); + + frameCountIn = (ma_uint32)framesJustRead; + frameCountOut = framesRemaining; + + /* Convert if necessary. */ + if (dataSourceFormat == ma_format_f32) { + /* Fast path. No data conversion necessary. */ + pRunningFramesIn = (float*)temp; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } else { + /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ + float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ + ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); + + /* Now that we have our samples in f32 format we can process like normal. */ + pRunningFramesIn = tempf32; + ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); + } + + /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ + MA_ASSERT(frameCountIn == framesJustRead); + totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ + + if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { + break; /* Might have reached the end. */ + } + } + } + + *pFrameCountOut = totalFramesRead; +} + +static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) +{ + /* + Make sure the pitch is updated before trying to read anything. It's important that this is done + only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that + ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), + and if another thread modifies the pitch just after that call it can result in a glitch due to + the input rate changing. + */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + /* For groups, the input data has already been read and we just need to apply the effect. */ + ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); +} + +static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) +{ + ma_uint64 inputFrameCount; + + MA_ASSERT(pInputFrameCount != NULL); + + /* Our pitch will affect this calculation. We need to update it. */ + ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); + + inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); + if (inputFrameCount > 0xFFFFFFFF) { + inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ + } + + *pInputFrameCount = (ma_uint32)inputFrameCount; + + return MA_SUCCESS; +} + + +static ma_node_vtable g_ma_engine_node_vtable__sound = +{ + ma_engine_node_process_pcm_frames__sound, + NULL, /* onGetRequiredInputFrameCount */ + 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ + 1, /* Sounds have one output bus. */ + 0 /* Default flags. */ +}; + +static ma_node_vtable g_ma_engine_node_vtable__group = +{ + ma_engine_node_process_pcm_frames__group, + ma_engine_node_get_required_input_frame_count__group, + 1, /* Groups have one input bus. */ + 1, /* Groups have one output bus. */ + MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ +}; + + + +static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) +{ + ma_node_config baseNodeConfig; + + if (pConfig->type == ma_engine_node_type_sound) { + /* Sound. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; + baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ + } else { + /* Group. */ + baseNodeConfig = ma_node_config_init(); + baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; + baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ + } + + return baseNodeConfig; +} + +static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) +{ + return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); +} + +typedef struct +{ + size_t sizeInBytes; + size_t baseNodeOffset; + size_t resamplerOffset; + size_t spatializerOffset; + size_t gainerOffset; +} ma_engine_node_heap_layout; + +static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) +{ + ma_result result; + size_t tempHeapSize; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_spatializer_config spatializerConfig; + ma_gainer_config gainerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ + + MA_ASSERT(pHeapLayout); + + MA_ZERO_OBJECT(pHeapLayout); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + if (pConfig->pEngine == NULL) { + return MA_INVALID_ARGS; /* An engine must be specified. */ + } + + pHeapLayout->sizeInBytes = 0; + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the base node. */ + } + + pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Resmapler. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ + resamplerConfig.lpfOrder = 0; + + result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the resampler. */ + } + + pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Spatializer. */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + + result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the size of the heap for the spatializer. */ + } + + pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + + + /* Gainer. Will not be used if we are not using smoothing. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + } + + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + + if (pHeapSizeInBytes == NULL) { + return MA_INVALID_ARGS; + } + + *pHeapSizeInBytes = 0; + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + *pHeapSizeInBytes = heapLayout.sizeInBytes; + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) +{ + ma_result result; + ma_engine_node_heap_layout heapLayout; + ma_node_config baseNodeConfig; + ma_linear_resampler_config resamplerConfig; + ma_fader_config faderConfig; + ma_spatializer_config spatializerConfig; + ma_panner_config pannerConfig; + ma_gainer_config gainerConfig; + ma_uint32 channelsIn; + ma_uint32 channelsOut; + ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ + + if (pEngineNode == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngineNode); + + result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { + return MA_INVALID_ARGS; /* Invalid listener. */ + } + + pEngineNode->_pHeap = pHeap; + MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); + + pEngineNode->pEngine = pConfig->pEngine; + pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; + ma_atomic_float_set(&pEngineNode->volume, 1); + pEngineNode->pitch = 1; + pEngineNode->oldPitch = 1; + pEngineNode->oldDopplerPitch = 1; + pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; + pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; + pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ + + channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); + channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); + + /* + If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler + is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used. + */ + if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) { + pEngineNode->isPitchDisabled = MA_FALSE; + } + + + /* Base node. */ + baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); + baseNodeConfig.pInputChannels = &channelsIn; + baseNodeConfig.pOutputChannels = &channelsOut; + + result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); + if (result != MA_SUCCESS) { + goto error0; + } + + + /* + We can now initialize the effects we need in order to implement the engine node. There's a + defined order of operations here, mainly centered around when we convert our channels from the + data source's native channel count to the engine's channel count. As a rule, we want to do as + much computation as possible before spatialization because there's a chance that will increase + the channel count, thereby increasing the amount of work needing to be done to process. + */ + + /* We'll always do resampling first. */ + resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); + resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ + + result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); + if (result != MA_SUCCESS) { + goto error1; + } + + + /* After resampling will come the fader. */ + faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); + + result = ma_fader_init(&faderConfig, &pEngineNode->fader); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to + ensure channels counts link up correctly in the node graph. + */ + spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); + spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; + + if (spatializerConfig.channelsIn == 2) { + spatializerConfig.pChannelMapIn = defaultStereoChannelMap; + } + + result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); + if (result != MA_SUCCESS) { + goto error2; + } + + + /* + After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't + be able to pan mono sounds. + */ + pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); + + result = ma_panner_init(&pannerConfig, &pEngineNode->panner); + if (result != MA_SUCCESS) { + goto error3; + } + + + /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); + if (result != MA_SUCCESS) { + goto error3; + } + } + + + return MA_SUCCESS; + + /* No need for allocation callbacks here because we use a preallocated heap. */ +error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); +error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); +error1: ma_node_uninit(&pEngineNode->baseNode, NULL); +error0: return result; +} + +MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) +{ + ma_result result; + size_t heapSizeInBytes; + void* pHeap; + + result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); + if (result != MA_SUCCESS) { + return result; + } + + if (heapSizeInBytes > 0) { + pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); + if (pHeap == NULL) { + return MA_OUT_OF_MEMORY; + } + } else { + pHeap = NULL; + } + + result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); + if (result != MA_SUCCESS) { + ma_free(pHeap, pAllocationCallbacks); + return result; + } + + pEngineNode->_ownsHeap = MA_TRUE; + return MA_SUCCESS; +} + +MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) +{ + /* + The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we + destroy anything that might be in the middle of being used by the processing function. + */ + ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); + + /* Now that the node has been uninitialized we can safely uninitialize the rest. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { + ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); + } + + ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); + ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); + + /* Free the heap last. */ + if (pEngineNode->_ownsHeap) { + ma_free(pEngineNode->_pHeap, pAllocationCallbacks); + } +} + + +MA_API ma_sound_config ma_sound_config_init(void) +{ + return ma_sound_config_init_2(NULL); +} + +MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) +{ + ma_sound_config config; + + MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + + config.rangeEndInPCMFrames = ~((ma_uint64)0); + config.loopPointEndInPCMFrames = ~((ma_uint64)0); + + return config; +} + +MA_API ma_sound_group_config ma_sound_group_config_init(void) +{ + return ma_sound_group_config_init_2(NULL); +} + +MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) +{ + ma_sound_group_config config; + + MA_ZERO_OBJECT(&config); + + if (pEngine != NULL) { + config.monoExpansionMode = pEngine->monoExpansionMode; + } else { + config.monoExpansionMode = ma_mono_expansion_mode_default; + } + + return config; +} + + +MA_API ma_engine_config ma_engine_config_init(void) +{ + ma_engine_config config; + + MA_ZERO_OBJECT(&config); + config.listenerCount = 1; /* Always want at least one listener. */ + config.monoExpansionMode = ma_mono_expansion_mode_default; + + return config; +} + + +#if !defined(MA_NO_DEVICE_IO) +static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + ma_engine* pEngine = (ma_engine*)pDevice->pUserData; + + (void)pFramesIn; + + /* + Experiment: Try processing a resource manager job if we're on the Emscripten build. + + This serves two purposes: + + 1) It ensures jobs are actually processed at some point since we cannot guarantee that the + caller is doing the right thing and calling ma_resource_manager_process_next_job(); and + + 2) It's an attempt at working around an issue where processing jobs on the Emscripten main + loop doesn't work as well as it should. When trying to load sounds without the `DECODE` + flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time + before the callback is processed. I think it's got something to do with the single- + threaded nature of Web, but I'm not entirely sure. + */ + #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) + { + if (pEngine->pResourceManager != NULL) { + if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { + ma_resource_manager_process_next_job(pEngine->pResourceManager); + } + } + } + #endif + + ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); +} +#endif + +MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) +{ + ma_result result; + ma_node_graph_config nodeGraphConfig; + ma_engine_config engineConfig; + ma_spatializer_listener_config listenerConfig; + ma_uint32 iListener; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pEngine); + + /* The config is allowed to be NULL in which case we use defaults for everything. */ + if (pConfig != NULL) { + engineConfig = *pConfig; + } else { + engineConfig = ma_engine_config_init(); + } + + pEngine->monoExpansionMode = engineConfig.monoExpansionMode; + pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; + pEngine->onProcess = engineConfig.onProcess; + pEngine->pProcessUserData = engineConfig.pProcessUserData; + ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + pEngine->pResourceManager = engineConfig.pResourceManager; + } + #endif + + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pDevice = engineConfig.pDevice; + + /* If we don't have a device, we need one. */ + if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { + ma_device_config deviceConfig; + + pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); + if (pEngine->pDevice == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; + deviceConfig.playback.format = ma_format_f32; + deviceConfig.playback.channels = engineConfig.channels; + deviceConfig.sampleRate = engineConfig.sampleRate; + deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; + deviceConfig.pUserData = pEngine; + deviceConfig.notificationCallback = engineConfig.notificationCallback; + deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; + deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; + deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ + deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ + + if (engineConfig.pContext == NULL) { + ma_context_config contextConfig = ma_context_config_init(); + contextConfig.allocationCallbacks = pEngine->allocationCallbacks; + contextConfig.pLog = engineConfig.pLog; + + /* If the engine config does not specify a log, use the resource manager's if we have one. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { + contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); + } + } + #endif + + result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); + } else { + result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); + } + + if (result != MA_SUCCESS) { + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + pEngine->pDevice = NULL; + return result; + } + + pEngine->ownsDevice = MA_TRUE; + } + + /* Update the channel count and sample rate of the engine config so we can reference it below. */ + if (pEngine->pDevice != NULL) { + engineConfig.channels = pEngine->pDevice->playback.channels; + engineConfig.sampleRate = pEngine->pDevice->sampleRate; + } + } + #endif + + if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { + return MA_INVALID_ARGS; + } + + pEngine->sampleRate = engineConfig.sampleRate; + + /* The engine always uses either the log that was passed into the config, or the context's log is available. */ + if (engineConfig.pLog != NULL) { + pEngine->pLog = engineConfig.pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + pEngine->pLog = ma_device_get_log(pEngine->pDevice); + } + #else + { + pEngine->pLog = NULL; + } + #endif + } + + + /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ + nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); + nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; + + result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); + if (result != MA_SUCCESS) { + goto on_error_1; + } + + + /* We need at least one listener. */ + if (engineConfig.listenerCount == 0) { + engineConfig.listenerCount = 1; + } + + if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { + result = MA_INVALID_ARGS; /* Too many listeners. */ + goto on_error_1; + } + + for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { + listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); + + /* + If we're using a device, use the device's channel map for the listener. Otherwise just use + miniaudio's default channel map. + */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + /* + Temporarily disabled. There is a subtle bug here where front-left and front-right + will be used by the device's channel map, but this is not what we want to use for + spatialization. Instead we want to use side-left and side-right. I need to figure + out a better solution for this. For now, disabling the use of device channel maps. + */ + /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ + } + } + #endif + + result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ + if (result != MA_SUCCESS) { + goto on_error_2; + } + + pEngine->listenerCount += 1; + } + + + /* Gain smoothing for spatialized sounds. */ + pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; + if (pEngine->gainSmoothTimeInFrames == 0) { + ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; + if (gainSmoothTimeInMilliseconds == 0) { + gainSmoothTimeInMilliseconds = 8; + } + + pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ + } + + + /* We need a resource manager. */ + #ifndef MA_NO_RESOURCE_MANAGER + { + if (pEngine->pResourceManager == NULL) { + ma_resource_manager_config resourceManagerConfig; + + pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); + if (pEngine->pResourceManager == NULL) { + result = MA_OUT_OF_MEMORY; + goto on_error_2; + } + + resourceManagerConfig = ma_resource_manager_config_init(); + resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ + resourceManagerConfig.decodedFormat = ma_format_f32; + resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ + resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); + ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); + resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; + + /* The Emscripten build cannot use threads. */ + #if defined(MA_EMSCRIPTEN) + { + resourceManagerConfig.jobThreadCount = 0; + resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; + } + #endif + + result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); + if (result != MA_SUCCESS) { + goto on_error_3; + } + + pEngine->ownsResourceManager = MA_TRUE; + } + } + #endif + + /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ + pEngine->inlinedSoundLock = 0; + pEngine->pInlinedSoundHead = NULL; + + /* Start the engine if required. This should always be the last step. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { + result = ma_engine_start(pEngine); + if (result != MA_SUCCESS) { + goto on_error_4; /* Failed to start the engine. */ + } + } + } + #endif + + return MA_SUCCESS; + +#if !defined(MA_NO_DEVICE_IO) +on_error_4: +#endif +#if !defined(MA_NO_RESOURCE_MANAGER) +on_error_3: + if (pEngine->ownsResourceManager) { + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif /* MA_NO_RESOURCE_MANAGER */ +on_error_2: + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); +on_error_1: + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } + } + #endif + + return result; +} + +MA_API void ma_engine_uninit(ma_engine* pEngine) +{ + ma_uint32 iListener; + + if (pEngine == NULL) { + return; + } + + /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->ownsDevice) { + ma_device_uninit(pEngine->pDevice); + ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); + } else { + if (pEngine->pDevice != NULL) { + ma_device_stop(pEngine->pDevice); + } + } + } + #endif + + /* + All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case + I want to do some kind of garbage collection later on. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + for (;;) { + ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; + if (pSoundToDelete == NULL) { + break; /* Done. */ + } + + pEngine->pInlinedSoundHead = pSoundToDelete->pNext; + + ma_sound_uninit(&pSoundToDelete->sound); + ma_free(pSoundToDelete, &pEngine->allocationCallbacks); + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); + } + + /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ + ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); + + /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pEngine->ownsResourceManager) { + ma_resource_manager_uninit(pEngine->pResourceManager); + ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); + } +#endif +} + +MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + ma_result result; + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (pEngine->onProcess) { + pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ + } + + return MA_SUCCESS; +} + +MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + return &pEngine->nodeGraph; +} + +#if !defined(MA_NO_RESOURCE_MANAGER) +MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_RESOURCE_MANAGER) + { + return pEngine->pResourceManager; + } + #else + { + return NULL; + } + #endif +} +#endif + +MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + #if !defined(MA_NO_DEVICE_IO) + { + return pEngine->pDevice; + } + #else + { + return NULL; + } + #endif +} + +MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return NULL; + } + + if (pEngine->pLog != NULL) { + return pEngine->pLog; + } else { + #if !defined(MA_NO_DEVICE_IO) + { + return ma_device_get_log(ma_engine_get_device(pEngine)); + } + #else + { + return NULL; + } + #endif + } +} + +MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) +{ + return ma_node_graph_get_endpoint(&pEngine->nodeGraph); +} + +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) +{ + return ma_node_graph_get_time(&pEngine->nodeGraph); +} + +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); +} + +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + +MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) +{ + return ma_node_graph_get_channels(&pEngine->nodeGraph); +} + +MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->sampleRate; +} + + +MA_API ma_result ma_engine_start(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_start(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_stop(ma_engine* pEngine) +{ + ma_result result; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + #if !defined(MA_NO_DEVICE_IO) + { + if (pEngine->pDevice != NULL) { + result = ma_device_stop(pEngine->pDevice); + } else { + result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ + } + } + #else + { + result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ + } + #endif + + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) +{ + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); +} + +MA_API float ma_engine_get_volume(ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); +} + +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +{ + return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); +} + +MA_API float ma_engine_get_gain_db(ma_engine* pEngine) +{ + return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); +} + + +MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) +{ + if (pEngine == NULL) { + return 0; + } + + return pEngine->listenerCount; +} + +MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) +{ + ma_uint32 iListener; + ma_uint32 iListenerClosest; + float closestLen2 = MA_FLT_MAX; + + if (pEngine == NULL || pEngine->listenerCount == 1) { + return 0; + } + + iListenerClosest = 0; + for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { + if (ma_engine_listener_is_enabled(pEngine, iListener)) { + float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); + if (closestLen2 > len2) { + closestLen2 = len2; + iListenerClosest = iListener; + } + } + } + + MA_ASSERT(iListenerClosest < 255); + return iListenerClosest; +} + +MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, -1); + } + + return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); +} + +MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return ma_vec3f_init_3f(0, 1, 0); + } + + return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); +} + +MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + + ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); +} + +MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) +{ + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return MA_FALSE; + } + + return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); +} + + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) +{ + ma_result result = MA_SUCCESS; + ma_sound_inlined* pSound = NULL; + ma_sound_inlined* pNextSound = NULL; + + if (pEngine == NULL || pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + /* Attach to the endpoint node if nothing is specicied. */ + if (pNode == NULL) { + pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); + nodeInputBusIndex = 0; + } + + /* + We want to check if we can recycle an already-allocated inlined sound. Since this is just a + helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep + the implementation simple. Maybe this can be optimized later if there's enough demand, but + if this function is being used it probably means the caller doesn't really care too much. + + What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise + we just keep iterating. If we reach the end without finding a sound to recycle we just + allocate a new one. This doesn't scale well for a massive number of sounds being played + simultaneously as we don't ever actually free the sound objects. Some kind of garbage + collection routine might be valuable for this which I'll think about. + */ + ma_spinlock_lock(&pEngine->inlinedSoundLock); + { + ma_uint32 soundFlags = 0; + + for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { + if (ma_sound_at_end(&pNextSound->sound)) { + /* + The sound is at the end which means it's available for recycling. All we need to do + is uninitialize it and reinitialize it. All we're doing is recycling memory. + */ + pSound = pNextSound; + ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + break; + } + } + + if (pSound != NULL) { + /* + We actually want to detach the sound from the list here. The reason is because we want the sound + to be in a consistent state at the non-recycled case to simplify the logic below. + */ + if (pEngine->pInlinedSoundHead == pSound) { + pEngine->pInlinedSoundHead = pSound->pNext; + } + + if (pSound->pPrev != NULL) { + pSound->pPrev->pNext = pSound->pNext; + } + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound->pPrev; + } + + /* Now the previous sound needs to be uninitialized. */ + ma_sound_uninit(&pNextSound->sound); + } else { + /* No sound available for recycling. Allocate one now. */ + pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); + } + + if (pSound != NULL) { /* Safety check for the allocation above. */ + /* + At this point we should have memory allocated for the inlined sound. We just need + to initialize it like a normal sound now. + */ + soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ + soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ + soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ + soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ + + result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); + if (result == MA_SUCCESS) { + /* Now attach the sound to the graph. */ + result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); + if (result == MA_SUCCESS) { + /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ + pSound->pNext = pEngine->pInlinedSoundHead; + pSound->pPrev = NULL; + + pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ + if (pSound->pNext != NULL) { + pSound->pNext->pPrev = pSound; + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + ma_free(pSound, &pEngine->allocationCallbacks); + } + } else { + result = MA_OUT_OF_MEMORY; + } + } + ma_spinlock_unlock(&pEngine->inlinedSoundLock); + + if (result != MA_SUCCESS) { + return result; + } + + /* Finally we can start playing the sound. */ + result = ma_sound_start(&pSound->sound); + if (result != MA_SUCCESS) { + /* Failed to start the sound. We need to mark it for recycling and return an error. */ + ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + return result; + } + + ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + return result; +} + +MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) +{ + return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); +} +#endif + + +static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pSound); + pSound->seekTarget = MA_SEEK_TARGET_NONE; + + if (pEngine == NULL) { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + +static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + ma_engine_node_config engineNodeConfig; + ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ + + /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ + MA_ASSERT(pEngine != NULL); + MA_ASSERT(pSound != NULL); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pSound->pDataSource = pConfig->pDataSource; + + if (pConfig->pDataSource != NULL) { + type = ma_engine_node_type_sound; + } else { + type = ma_engine_node_type_group; + } + + /* + Sounds are engine nodes. Before we can initialize this we need to determine the channel count. + If we can't do this we need to abort. It's up to the caller to ensure they're using a data + source that provides this information upfront. + */ + engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + + if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { + engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; + } + + /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ + if (pConfig->pDataSource != NULL) { + result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; /* Failed to retrieve the channel count. */ + } + + if (engineNodeConfig.channelsIn == 0) { + return MA_INVALID_OPERATION; /* Invalid channel count. */ + } + + if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { + engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; + } + } + + + /* Getting here means we should have a valid channel count and we can initialize the engine node. */ + result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); + if (result != MA_SUCCESS) { + return result; + } + + /* If no attachment is specified, attach the sound straight to the endpoint. */ + if (pConfig->pInitialAttachment == NULL) { + /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ + if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { + result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); + } + } else { + /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ + result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); + } + + if (result != MA_SUCCESS) { + ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); + return result; + } + + + /* Apply initial range and looping state to the data source if applicable. */ + if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); + } + + if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { + ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); + } + + ma_sound_set_looping(pSound, pConfig->isLooping); + + return MA_SUCCESS; +} + +#ifndef MA_NO_RESOURCE_MANAGER +MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result = MA_SUCCESS; + ma_uint32 flags; + ma_sound_config config; + ma_resource_manager_pipeline_notifications notifications; + + /* + The engine requires knowledge of the channel count of the underlying data source before it can + initialize the sound. Therefore, we need to make the resource manager wait until initialization + of the underlying data source to be initialized so we can get access to the channel count. To + do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. + + Because we're initializing the data source before the sound, there's a chance the notification + will get triggered before this function returns. This is OK, so long as the caller is aware of + it and can avoid accessing the sound from within the notification. + */ + flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; + + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + /* Removed in 0.12. Set pDoneFence on the notifications. */ + notifications = pConfig->initNotifications; + if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) { + notifications.done.pFence = pConfig->pDoneFence; + } + + /* + We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does + not return prematurely before the sound has finished initializing. + */ + if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } + { + ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); + resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; + resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; + resourceManagerDataSourceConfig.flags = flags; + resourceManagerDataSourceConfig.pNotifications = ¬ifications; + resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; + resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; + resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; + resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; + resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; + resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; + + result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + goto done; + } + + pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ + + /* We need to use a slightly customized version of the config so we'll need to make a copy. */ + config = *pConfig; + config.pFilePath = NULL; + config.pFilePathW = NULL; + config.pDataSource = pSound->pResourceManagerDataSource; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + goto done; + } + } +done: + if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } + return result; +} + +MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); + config.pFilePath = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) +{ + ma_sound_config config; + + if (pFilePath == NULL) { + return MA_INVALID_ARGS; + } + + config = ma_sound_config_init_2(pEngine); + config.pFilePathW = pFilePath; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.pDoneFence = pDoneFence; + + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_result result; + ma_sound_config config; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pExistingSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ + if (pExistingSound->pResourceManagerDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* + We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) + this will fail. + */ + pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); + if (pSound->pResourceManagerDataSource == NULL) { + return MA_OUT_OF_MEMORY; + } + + result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); + if (result != MA_SUCCESS) { + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + return result; + } + + config = ma_sound_config_init_2(pEngine); + config.pDataSource = pSound->pResourceManagerDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; + + result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); + if (result != MA_SUCCESS) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); + MA_ZERO_OBJECT(pSound); + return result; + } + + /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ + pSound->ownsDataSource = MA_TRUE; + + return MA_SUCCESS; +} +#endif + +MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) +{ + ma_sound_config config = ma_sound_config_init_2(pEngine); + config.pDataSource = pDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + return ma_sound_init_ex(pEngine, &config, pSound); +} + +MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) +{ + ma_result result; + + result = ma_sound_preinit(pEngine, pSound); + if (result != MA_SUCCESS) { + return result; + } + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + pSound->endCallback = pConfig->endCallback; + pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; + + /* We need to load the sound differently depending on whether or not we're loading from a file. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { + return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); + } else +#endif + { + /* + Getting here means we're not loading from a file. We may be loading from an already-initialized + data source, or none at all. If we aren't specifying any data source, we'll be initializing the + the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this + for us, so no special treatment required here. + */ + return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); + } +} + +MA_API void ma_sound_uninit(ma_sound* pSound) +{ + if (pSound == NULL) { + return; + } + + /* + Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done + so which makes thread safety beyond this point trivial. + */ + ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); + + /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ +#ifndef MA_NO_RESOURCE_MANAGER + if (pSound->ownsDataSource) { + ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); + ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); + pSound->pDataSource = NULL; + } +#else + MA_ASSERT(pSound->ownsDataSource == MA_FALSE); +#endif +} + +MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->engineNode.pEngine; +} + +MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) +{ + if (pSound == NULL) { + return NULL; + } + + return pSound->pDataSource; +} + +MA_API ma_result ma_sound_start(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* If the sound is already playing, do nothing. */ + if (ma_sound_is_playing(pSound)) { + return MA_SUCCESS; + } + + /* If the sound is at the end it means we want to start from the start again. */ + if (ma_sound_at_end(pSound)) { + ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); + if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { + return result; /* Failed to seek back to the start. */ + } + + /* Make sure we clear the end indicator. */ + ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); + } + + /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ + ma_node_set_state(pSound, ma_node_state_started); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop(ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ + ma_node_set_state(pSound, ma_node_state_stopped); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint64 sampleRate; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + +MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) +{ + if (pSound == NULL) { + return; + } + + ma_engine_node_set_volume(&pSound->engineNode, volume); +} + +MA_API float ma_sound_get_volume(const ma_sound* pSound) +{ + float volume = 0; + + if (pSound == NULL) { + return 0; + } + + ma_engine_node_get_volume(&pSound->engineNode, &volume); + + return volume; +} + +MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_pan(&pSound->engineNode.panner, pan); +} + +MA_API float ma_sound_get_pan(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_panner_get_pan(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) +{ + if (pSound == NULL) { + return; + } + + ma_panner_set_mode(&pSound->engineNode.panner, panMode); +} + +MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_pan_mode_balance; + } + + return ma_panner_get_mode(&pSound->engineNode.panner); +} + +MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) +{ + if (pSound == NULL) { + return; + } + + if (pitch <= 0) { + return; + } + + ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); +} + +MA_API float ma_sound_get_pitch(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ +} + +MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) +{ + if (pSound == NULL) { + return; + } + + ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); +} + +MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); +} + +MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) +{ + if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { + return; + } + + ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); +} + +MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_LISTENER_INDEX_CLOSEST; + } + + return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); +} + +MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) +{ + ma_uint32 listenerIndex; + + if (pSound == NULL) { + return 0; + } + + listenerIndex = ma_sound_get_pinned_listener_index(pSound); + if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { + ma_vec3f position = ma_sound_get_position(pSound); + return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); + } + + return listenerIndex; +} + +MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) +{ + ma_vec3f relativePos; + ma_engine* pEngine; + + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + pEngine = ma_sound_get_engine(pSound); + if (pEngine == NULL) { + return ma_vec3f_init_3f(0, 0, -1); + } + + ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); + + return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); +} + +MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_position(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_direction(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); +} + +MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_vec3f_init_3f(0, 0, 0); + } + + return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_attenuation_model_none; + } + + return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); +} + +MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) +{ + if (pSound == NULL) { + return ma_positioning_absolute; + } + + return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); +} + +MA_API float ma_sound_get_rolloff(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); +} + +MA_API float ma_sound_get_min_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); +} + +MA_API float ma_sound_get_max_gain(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); +} + +MA_API float ma_sound_get_min_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); +} + +MA_API float ma_sound_get_max_distance(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + if (pInnerAngleInRadians != NULL) { + *pInnerAngleInRadians = 0; + } + + if (pOuterAngleInRadians != NULL) { + *pOuterAngleInRadians = 0; + } + + if (pOuterGain != NULL) { + *pOuterGain = 0; + } + + if (pSound == NULL) { + return; + } + + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); +} + +MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); +} + +MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) +{ + if (pSound == NULL) { + return; + } + + ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); +} + +MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 1; + } + + return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); +} + + +MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); +} + +MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); +} + +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + /* + We don't want to update the fader at this point because we need to use the engine's current time + to derive the fader's start offset. The timer is being updated on the audio thread so in order to + do this as accurately as possible we'll need to defer this to the audio thread. + */ + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); +} + +MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + return ma_fader_get_current_volume(&pSound->engineNode.fader); +} + +MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); +} + +MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + if (pSound == NULL) { + return; + } + + ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + if (fadeLengthInFrames > 0) { + if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { + fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); + } + + ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + +MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; +} + +MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) +{ + if (pSound == NULL) { + return 0; + } + + return ma_node_get_time(pSound); +} + +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) +{ + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); +} + +MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) +{ + if (pSound == NULL) { + return; + } + + /* Looping is only a valid concept if the sound is backed by a data source. */ + if (pSound->pDataSource == NULL) { + return; + } + + /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ + ma_data_source_set_looping(pSound->pDataSource, isLooping); +} + +MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of looping for sounds that are not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return ma_data_source_is_looping(pSound->pDataSource); +} + +MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) +{ + if (pSound == NULL) { + return MA_FALSE; + } + + /* There is no notion of an end of a sound if it's not backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_FALSE; + } + + return ma_sound_get_at_end(pSound); +} + +MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Seeking is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ + ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ + if (pSound->pDataSource == NULL) { + ma_uint32 channels; + + if (pFormat != NULL) { + *pFormat = ma_format_f32; + } + + channels = ma_node_get_input_channels(&pSound->engineNode, 0); + if (pChannels != NULL) { + *pChannels = channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; + } + + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); + } + + return MA_SUCCESS; + } else { + return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); + } +} + +MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) +{ + ma_uint64 seekTarget; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a cursor is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + *pCursor = seekTarget; + return MA_SUCCESS; + } else { + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + } +} + +MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); +} + +MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) +{ + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor != NULL) { + *pCursor = 0; + } + + result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of a sound length is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); +} + +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of an end is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + pSound->endCallback = callback; + pSound->pEndCallbackUserData = pUserData; + + return MA_SUCCESS; +} + + +MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) +{ + ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); + config.flags = flags; + config.pInitialAttachment = pParentGroup; + return ma_sound_group_init_ex(pEngine, &config, pGroup); +} + +MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) +{ + ma_sound_config soundConfig; + + if (pGroup == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pGroup); + + if (pConfig == NULL) { + return MA_INVALID_ARGS; + } + + /* A sound group is just a sound without a data source. */ + soundConfig = *pConfig; + soundConfig.pFilePath = NULL; + soundConfig.pFilePathW = NULL; + soundConfig.pDataSource = NULL; + + /* + Groups need to have spatialization disabled by default because I think it'll be pretty rare + that programs will want to spatialize groups (but not unheard of). Certainly it feels like + disabling this by default feels like the right option. Spatialization can be enabled with a + call to ma_sound_group_set_spatialization_enabled(). + */ + soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; + + return ma_sound_init_ex(pEngine, &soundConfig, pGroup); +} + +MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) +{ + ma_sound_uninit(pGroup); +} + +MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) +{ + return ma_sound_get_engine(pGroup); +} + +MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) +{ + return ma_sound_start(pGroup); +} + +MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) +{ + return ma_sound_stop(pGroup); +} + +MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) +{ + ma_sound_set_volume(pGroup, volume); +} + +MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) +{ + return ma_sound_get_volume(pGroup); +} + +MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) +{ + ma_sound_set_pan(pGroup, pan); +} + +MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan(pGroup); +} + +MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) +{ + ma_sound_set_pan_mode(pGroup, panMode); +} + +MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) +{ + return ma_sound_get_pan_mode(pGroup); +} + +MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) +{ + ma_sound_set_pitch(pGroup, pitch); +} + +MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) +{ + return ma_sound_get_pitch(pGroup); +} + +MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) +{ + ma_sound_set_spatialization_enabled(pGroup, enabled); +} + +MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) +{ + return ma_sound_is_spatialization_enabled(pGroup); +} + +MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) +{ + ma_sound_set_pinned_listener_index(pGroup, listenerIndex); +} + +MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_pinned_listener_index(pGroup); +} + +MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) +{ + return ma_sound_get_listener_index(pGroup); +} + +MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction_to_listener(pGroup); +} + +MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_position(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) +{ + return ma_sound_get_position(pGroup); +} + +MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_direction(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) +{ + return ma_sound_get_direction(pGroup); +} + +MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) +{ + ma_sound_set_velocity(pGroup, x, y, z); +} + +MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) +{ + return ma_sound_get_velocity(pGroup); +} + +MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) +{ + ma_sound_set_attenuation_model(pGroup, attenuationModel); +} + +MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) +{ + return ma_sound_get_attenuation_model(pGroup); +} + +MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) +{ + ma_sound_set_positioning(pGroup, positioning); +} + +MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) +{ + return ma_sound_get_positioning(pGroup); +} + +MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) +{ + ma_sound_set_rolloff(pGroup, rolloff); +} + +MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) +{ + return ma_sound_get_rolloff(pGroup); +} + +MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) +{ + ma_sound_set_min_gain(pGroup, minGain); +} + +MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_gain(pGroup); +} + +MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) +{ + ma_sound_set_max_gain(pGroup, maxGain); +} + +MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_gain(pGroup); +} + +MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) +{ + ma_sound_set_min_distance(pGroup, minDistance); +} + +MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_min_distance(pGroup); +} + +MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) +{ + ma_sound_set_max_distance(pGroup, maxDistance); +} + +MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) +{ + return ma_sound_get_max_distance(pGroup); +} + +MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) +{ + ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); +} + +MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) +{ + ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); +} + +MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) +{ + ma_sound_set_doppler_factor(pGroup, dopplerFactor); +} + +MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_doppler_factor(pGroup); +} + +MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) +{ + ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); +} + +MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) +{ + return ma_sound_get_directional_attenuation_factor(pGroup); +} + +MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) +{ + ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); +} + +MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) +{ + ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); +} + +MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) +{ + return ma_sound_get_current_fade_volume(pGroup); +} + +MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) +{ + ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); +} + +MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) +{ + return ma_sound_is_playing(pGroup); +} + +MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) +{ + return ma_sound_get_time_in_pcm_frames(pGroup); +} +#endif /* MA_NO_ENGINE */ +/* END SECTION: miniaudio_engine.c */ + + + +/************************************************************************************************************************************************************** +*************************************************************************************************************************************************************** + +Auto Generated +============== +All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the +code below please report the bug to the respective repository for the relevant project (probably dr_libs). + +*************************************************************************************************************************************************************** +**************************************************************************************************************************************************************/ +#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) +#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_wav_c begin */ +#ifndef ma_dr_wav_c +#define ma_dr_wav_c +#ifdef __MRC__ +#pragma options opt off +#endif +#include +#include +#include +#ifndef MA_DR_WAV_NO_STDIO +#include +#ifndef MA_DR_WAV_NO_WCHAR +#include +#endif +#endif +#ifndef MA_DR_WAV_ASSERT +#include +#define MA_DR_WAV_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_WAV_MALLOC +#define MA_DR_WAV_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_WAV_REALLOC +#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_WAV_FREE +#define MA_DR_WAV_FREE(p) free((p)) +#endif +#ifndef MA_DR_WAV_COPY_MEMORY +#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_WAV_ZERO_MEMORY +#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef MA_DR_WAV_ZERO_OBJECT +#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) +#endif +#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) +#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 +#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) +#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) +#if defined(_MSC_VER) && _MSC_VER >= 1400 + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #endif +#endif +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_WAV_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_WAV_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_WAV_VERSION_REVISION; + } +} +MA_API const char* ma_dr_wav_version_string(void) +{ + return MA_DR_WAV_VERSION_STRING; +} +#ifndef MA_DR_WAV_MAX_SAMPLE_RATE +#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 +#endif +#ifndef MA_DR_WAV_MAX_CHANNELS +#define MA_DR_WAV_MAX_CHANNELS 256 +#endif +#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE +#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 +#endif +static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; +static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static MA_INLINE int ma_dr_wav__is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} +static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) +{ + int i; + for (i = 0; i < 16; ++i) { + guid[i] = data[i]; + } +} +static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} +static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} +static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) +{ +#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); +#endif +} +static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) +{ + return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); + } +} +static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) +{ + ma_uint8 t; + t = p[0]; + p[0] = p[2]; + p[2] = t; +} +static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + ma_uint8* pSample = pSamples + (iSample*3); + ma_dr_wav__bswap_s24(pSample); + } +} +static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) +{ + return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); + } +} +static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) +{ + return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); + } +} +static MA_INLINE float ma_dr_wav__bswap_f32(float n) +{ + union { + ma_uint32 i; + float f; + } x; + x.f = n; + x.i = ma_dr_wav__bswap32(x.i); + return x.f; +} +static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); + } +} +static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) +{ + switch (bytesPerSample) + { + case 1: + { + } break; + case 2: + { + ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); + } break; + case 3: + { + ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); + } break; + case 4: + { + ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); + } break; + case 8: + { + ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); + } break; + default: + { + MA_DR_WAV_ASSERT(MA_FALSE); + } break; + } +} +MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) +{ + if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { + return MA_TRUE; + } else { + return MA_FALSE; + } +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) +{ + return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u16_be(data); + } else { + return ma_dr_wav_bytes_to_u16_le(data); + } +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) +{ + return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) +{ + return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u32_be(data); + } else { + return ma_dr_wav_bytes_to_u32_le(data); + } +} +MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) +{ + ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; + ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); + ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); + ma_uint64 significand = (hi << 32) | lo; + int sign = exponent >> 15; + exponent &= 0x7FFF; + if (exponent == 0 && significand == 0) { + return 0; + } else if (exponent == 0x7FFF) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } + exponent -= 16383; + if (exponent > 63) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } else if (exponent < 1) { + return 0; + } + significand >>= (63 - exponent); + if (sign) { + return -(ma_int64)significand; + } else { + return (ma_int64)significand; + } +} +MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_WAV_MALLOC(sz); +} +MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_WAV_REALLOC(p, sz); +} +MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_WAV_FREE(p); +} +MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_WAV_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return *pAllocationCallbacks; + } else { + ma_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; + allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; + allocationCallbacks.onFree = ma_dr_wav__free_default; + return allocationCallbacks; + } +} +static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) +{ + return + formatTag == MA_DR_WAVE_FORMAT_ADPCM || + formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; +} +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 2); +} +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) +{ + return (unsigned int)(chunkSize % 8); +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); +MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) +{ + if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { + ma_uint8 sizeInBytes[4]; + if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { + return MA_AT_END; + } + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return MA_INVALID_FILE; + } + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 8; + } else if (container == ma_dr_wav_container_w64) { + ma_uint8 sizeInBytes[8]; + if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { + return MA_AT_END; + } + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return MA_INVALID_FILE; + } + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + *pRunningBytesReadOut += 24; + } else { + return MA_INVALID_FILE; + } + return MA_SUCCESS; +} +MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +{ + ma_uint64 bytesRemainingToSeek = offset; + while (bytesRemainingToSeek > 0) { + if (bytesRemainingToSeek > 0x7FFFFFFF) { + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + bytesRemainingToSeek -= 0x7FFFFFFF; + } else { + if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + bytesRemainingToSeek = 0; + } + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) +{ + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); + } + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + for (;;) { + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); + } + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + } +} +MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +{ + size_t bytesRead; + MA_DR_WAV_ASSERT(onRead != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); + bytesRead = onRead(pUserData, pBufferOut, bytesToRead); + *pCursor += bytesRead; + return bytesRead; +} +#if 0 +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) +{ + MA_DR_WAV_ASSERT(onSeek != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); + if (!onSeek(pUserData, offset, origin)) { + return MA_FALSE; + } + if (origin == ma_dr_wav_seek_origin_start) { + *pCursor = offset; + } else { + *pCursor += offset; + } + return MA_TRUE; +} +#endif +#define MA_DR_WAV_SMPL_BYTES 36 +#define MA_DR_WAV_SMPL_LOOP_BYTES 24 +#define MA_DR_WAV_INST_BYTES 7 +#define MA_DR_WAV_ACID_BYTES 24 +#define MA_DR_WAV_CUE_BYTES 4 +#define MA_DR_WAV_BEXT_BYTES 602 +#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 +#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 +#define MA_DR_WAV_BEXT_UMID_BYTES 64 +#define MA_DR_WAV_CUE_POINT_BYTES 24 +#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 +#define MA_DR_WAV_METADATA_ALIGNMENT 8 +typedef enum +{ + ma_dr_wav__metadata_parser_stage_count, + ma_dr_wav__metadata_parser_stage_read +} ma_dr_wav__metadata_parser_stage; +typedef struct +{ + ma_dr_wav_read_proc onRead; + ma_dr_wav_seek_proc onSeek; + void *pReadSeekUserData; + ma_dr_wav__metadata_parser_stage stage; + ma_dr_wav_metadata *pMetadata; + ma_uint32 metadataCount; + ma_uint8 *pData; + ma_uint8 *pDataCursor; + ma_uint64 metadataCursor; + ma_uint64 extraCapacity; +} ma_dr_wav__metadata_parser; +MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) +{ + ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > MA_SIZE_MAX) { + return 0; + } + return (size_t)cap; +} +MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) +{ + ma_uint8* pResult; + if (align) { + ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; + if (modulo != 0) { + pParser->pDataCursor += align - modulo; + } + } + pResult = pParser->pDataCursor; + MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); + pParser->pDataCursor += size; + return pResult; +} +MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) +{ + size_t extra = bytes + (align ? (align - 1) : 0); + pParser->extraCapacity += extra; +} +MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { + pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); + pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pDataCursor = pParser->pData; + if (pParser->pData == NULL) { + return MA_OUT_OF_MEMORY; + } + pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); + pParser->metadataCursor = 0; + } + return MA_SUCCESS; +} +MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) +{ + if (pCursor != NULL) { + return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + } else { + return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); + } +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + MA_DR_WAV_ASSERT(pChunkHeader != NULL); + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { + ma_uint32 iSampleLoop; + pMetadata->type = ma_dr_wav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); + for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { + ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + if (bytesJustRead == sizeof(smplLoopData)) { + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); + } else { + break; + } + } + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + } + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(cueHeaderSectionData)) { + pMetadata->type = ma_dr_wav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); + MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + if (pMetadata->data.cue.cuePointCount > 0) { + ma_uint32 iCuePoint; + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + if (bytesJustRead == sizeof(cuePointData)) { + pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; + pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); + } else { + break; + } + } + } + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 instData[MA_DR_WAV_INST_BYTES]; + ma_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(instData)) { + pMetadata->type = ma_dr_wav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; + pMetadata->data.inst.lowNote = (ma_int8)instData[3]; + pMetadata->data.inst.highNote = (ma_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; + pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) +{ + ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; + ma_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(acidData)) { + pMetadata->type = ma_dr_wav_metadata_type_acid; + pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); + } + return bytesRead; +} +MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) +{ + size_t result = 0; + while (*str++) { + result += 1; + } + return result; +} +MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) +{ + size_t result = 0; + while (*str++ && result < maxToRead) { + result += 1; + } + return result; +} +MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) +{ + size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); + if (len) { + char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); + MA_DR_WAV_ASSERT(result != NULL); + MA_DR_WAV_COPY_MEMORY(result, str, len); + result[len] = '\0'; + return result; + } else { + return NULL; + } +} +typedef struct +{ + const void* pBuffer; + size_t sizeInBytes; + size_t cursor; +} ma_dr_wav_buffer_reader; +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) +{ + MA_DR_WAV_ASSERT(pBuffer != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ZERO_OBJECT(pReader); + pReader->pBuffer = pBuffer; + pReader->sizeInBytes = sizeInBytes; + pReader->cursor = 0; + return MA_SUCCESS; +} +MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) +{ + MA_DR_WAV_ASSERT(pReader != NULL); + return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) +{ + MA_DR_WAV_ASSERT(pReader != NULL); + if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { + return MA_BAD_SEEK; + } + pReader->cursor += bytesToSeek; + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +{ + ma_result result = MA_SUCCESS; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pReader != NULL); + if (pBytesRead != NULL) { + *pBytesRead = 0; + } + bytesRemaining = (pReader->sizeInBytes - pReader->cursor); + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (pDst == NULL) { + result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); + } else { + MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); + pReader->cursor += bytesToRead; + } + MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == MA_SUCCESS) { + if (pBytesRead != NULL) { + *pBytesRead = bytesToRead; + } + } + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) +{ + ma_result result; + size_t bytesRead; + ma_uint8 data[2]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); + *pDst = 0; + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = ma_dr_wav_bytes_to_u16(data); + return MA_SUCCESS; +} +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) +{ + ma_result result; + size_t bytesRead; + ma_uint8 data[4]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); + *pDst = 0; + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { + return result; + } + *pDst = ma_dr_wav_bytes_to_u32(data); + return MA_SUCCESS; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +{ + ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; + size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesRead == sizeof(bextData)) { + ma_dr_wav_buffer_reader reader; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + size_t extraBytes; + pMetadata->type = ma_dr_wav_metadata_type_bext; + if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { + pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); + if (extraBytes > 0) { + pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); + MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); + } else { + pMetadata->data.bext.pCodingHistory = NULL; + pMetadata->data.bext.codingHistorySize = 0; + } + } + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +{ + ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(cueIDBuffer)) { + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = type; + pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelOrNote.stringLength = 0; + pMetadata->data.labelOrNote.pString = NULL; + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) +{ + ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); + pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; + pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; + pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; + pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; + pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (sizeIncludingNullTerminator > 0) { + pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; + pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + } else { + pMetadata->data.labelledCueRegion.stringLength = 0; + pMetadata->data.labelledCueRegion.pString = NULL; + } + } + return totalBytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) +{ + ma_uint64 bytesRead = 0; + ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + } else { + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = type; + if (stringSizeWithNullTerminator > 0) { + pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; + pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + if (bytesRead == chunkSize) { + pParser->metadataCursor += 1; + } else { + } + } else { + pMetadata->data.infoText.stringLength = 0; + pMetadata->data.infoText.pString = NULL; + pParser->metadataCursor += 1; + } + } + return bytesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) +{ + ma_uint64 bytesRead = 0; + if (location == ma_dr_wav_metadata_location_invalid) { + return 0; + } + if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { + return 0; + } + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + } else { + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = ma_dr_wav_metadata_type_unknown; + pMetadata->data.unknown.chunkLocation = location; + pMetadata->data.unknown.id[0] = pChunkId[0]; + pMetadata->data.unknown.id[1] = pChunkId[1]; + pMetadata->data.unknown.id[2] = pChunkId[2]; + pMetadata->data.unknown.id[3] = pChunkId[3]; + pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; + pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + return bytesRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) +{ + return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); +} +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) +{ + const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; + ma_uint64 bytesRead = 0; + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + ma_uint8 buffer[4]; + size_t bytesJustRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { + return bytesRead; + } + bytesRead += 28; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); + ma_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; + if (calculatedLoopCount == loopCount) { + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + if (bytesJustRead == sizeof(buffer)) { + ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + } + } else { + } + } + } else { + bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + size_t cueCount; + pParser->metadataCount += 1; + cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); + } else { + bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; + size_t bytesJustRead; + buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { + return bytesRead; + } + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + pParser->metadataCount += 1; + } else { + bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + if (bytesRead == pChunkHeader->sizeInBytes) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { + ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; + while (bytesRead < pChunkHeader->sizeInBytes) { + ma_uint8 subchunkId[4]; + ma_uint8 subchunkSizeBuffer[4]; + ma_uint64 subchunkDataSize; + ma_uint64 subchunkBytesRead = 0; + ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + if (bytesJustRead != sizeof(subchunkId)) { + break; + } + if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { + listType = ma_dr_wav_metadata_location_inside_adtl_list; + continue; + } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { + listType = ma_dr_wav_metadata_location_inside_info_list; + continue; + } + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + if (bytesJustRead != sizeof(subchunkSizeBuffer)) { + break; + } + subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { + ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + } else { + subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { + ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + pParser->metadataCount += 1; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + } else { + subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + if (subchunkBytesRead == subchunkDataSize) { + pParser->metadataCursor += 1; + } else { + } + } + } else { + } + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } + bytesRead += subchunkBytesRead; + MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + if (subchunkBytesRead < subchunkDataSize) { + ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { + break; + } + bytesRead += bytesToSeek; + } + if ((subchunkDataSize % 2) == 1) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { + break; + } + bytesRead += 1; + } + } + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); + } + return bytesRead; +} +MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) +{ + ma_uint32 bytesPerFrame; + if ((pWav->bitsPerSample & 0x7) == 0) { + bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; + } else { + bytesPerFrame = pWav->fmt.blockAlign; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + if (bytesPerFrame != pWav->fmt.channels) { + return 0; + } + } + return bytesPerFrame; +} +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) +{ + if (pFMT == NULL) { + return 0; + } + if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return pFMT->formatTag; + } else { + return ma_dr_wav_bytes_to_u16(pFMT->subFormat); + } +} +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onRead == NULL || onSeek == NULL) { + return MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pReadSeekUserData; + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) +{ + ma_result result; + ma_uint64 cursor; + ma_bool32 sequential; + ma_uint8 riff[4]; + ma_dr_wav_fmt fmt; + unsigned short translatedFormatTag; + ma_uint64 dataChunkSize = 0; + ma_uint64 sampleCountFromFactChunk = 0; + ma_uint64 metadataStartPos; + ma_dr_wav__metadata_parser metadataParser; + ma_bool8 isProcessingMetadata = MA_FALSE; + ma_bool8 foundChunk_fmt = MA_FALSE; + ma_bool8 foundChunk_data = MA_FALSE; + ma_bool8 isAIFCFormType = MA_FALSE; + ma_uint64 aiffFrameCount = 0; + cursor = 0; + sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; + MA_DR_WAV_ZERO_OBJECT(&fmt); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { + pWav->container = ma_dr_wav_container_riff; + } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { + pWav->container = ma_dr_wav_container_rifx; + } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { + int i; + ma_uint8 riff2[12]; + pWav->container = ma_dr_wav_container_w64; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return MA_FALSE; + } + for (i = 0; i < 12; ++i) { + if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { + return MA_FALSE; + } + } + } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { + pWav->container = ma_dr_wav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { + pWav->container = ma_dr_wav_container_aiff; + } else { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 wave[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + ma_uint8 chunkSizeBytes[8]; + ma_uint8 wave[16]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 aiff[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { + isAIFCFormType = MA_FALSE; + } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { + isAIFCFormType = MA_TRUE; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + if (pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 sizeBytes[8]; + ma_uint64 bytesRemainingInChunk; + ma_dr_wav_chunk_header header; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { + return MA_FALSE; + } + bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; + if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + cursor += 8; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; + } + bytesRemainingInChunk -= 8; + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); + if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return MA_FALSE; + } + cursor += bytesRemainingInChunk; + } + metadataStartPos = cursor; + isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); + if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { + isProcessingMetadata = MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); + if (isProcessingMetadata) { + metadataParser.onRead = pWav->onRead; + metadataParser.onSeek = pWav->onSeek; + metadataParser.pReadSeekUserData = pWav->pUserData; + metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; + } + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 chunkSize; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + chunkSize = header.sizeInBytes; + if (!sequential && onChunk != NULL) { + ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + if (callbackBytesRead > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { + ma_uint8 fmtData[16]; + foundChunk_fmt = MA_TRUE; + if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { + return MA_FALSE; + } + cursor += sizeof(fmtData); + fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); + fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); + fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); + fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); + fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); + fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); + fmt.extendedSize = 0; + fmt.validBitsPerSample = 0; + fmt.channelMask = 0; + MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); + if (header.sizeInBytes > 16) { + ma_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return MA_FALSE; + } + cursor += sizeof(fmt_cbSize); + bytesReadSoFar = 18; + fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); + if (fmt.extendedSize > 0) { + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmt.extendedSize != 22) { + return MA_FALSE; + } + } + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + ma_uint8 fmtext[22]; + if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { + return MA_FALSE; + } + fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); + fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); + ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); + } else { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + } + cursor += fmt.extendedSize; + bytesReadSoFar += fmt.extendedSize; + } + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + cursor += (header.sizeInBytes - bytesReadSoFar); + } + if (header.paddingSize > 0) { + if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += header.paddingSize; + } + continue; + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { + foundChunk_data = MA_TRUE; + pWav->dataChunkDataPos = cursor; + if (pWav->container != ma_dr_wav_container_rf64) { + dataChunkSize = chunkSize; + } + if (sequential || !isProcessingMetadata) { + break; + } else { + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + ma_uint8 sampleCount[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return MA_FALSE; + } + chunkSize -= 4; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); + } else { + sampleCountFromFactChunk = 0; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return MA_FALSE; + } + chunkSize -= 8; + } else if (pWav->container == ma_dr_wav_container_rf64) { + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { + ma_uint8 commData[24]; + ma_uint32 commDataBytesToRead; + ma_uint16 channels; + ma_uint32 frameCount; + ma_uint16 sampleSizeInBits; + ma_int64 sampleRate; + ma_uint16 compressionFormat; + foundChunk_fmt = MA_TRUE; + if (isAIFCFormType) { + commDataBytesToRead = 24; + if (header.sizeInBytes < commDataBytesToRead) { + return MA_FALSE; + } + } else { + commDataBytesToRead = 18; + if (header.sizeInBytes != commDataBytesToRead) { + return MA_FALSE; + } + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { + return MA_FALSE; + } + channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); + frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); + sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); + sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); + if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { + return MA_FALSE; + } + if (isAIFCFormType) { + const ma_uint8* type = commData + 18; + if (ma_dr_wav_fourcc_equal(type, "NONE")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + if (sampleSizeInBits == 8) { + pWav->aiff.isUnsigned = MA_TRUE; + } + } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + pWav->aiff.isLE = MA_TRUE; + } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { + compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_ALAW; + } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_MULAW; + } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { + compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; + sampleSizeInBits = 4; + return MA_FALSE; + } else { + return MA_FALSE; + } + } else { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } + aiffFrameCount = frameCount; + fmt.formatTag = compressionFormat; + fmt.channels = channels; + fmt.sampleRate = (ma_uint32)sampleRate; + fmt.bitsPerSample = sampleSizeInBits; + fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); + fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; + if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + fmt.blockAlign = 34 * fmt.channels; + } + if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { + if (fmt.bitsPerSample > 8) { + fmt.bitsPerSample = 8; + fmt.blockAlign = fmt.channels; + } + } + fmt.bitsPerSample += (fmt.bitsPerSample & 7); + if (isAIFCFormType) { + if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += (chunkSize - commDataBytesToRead); + } + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { + ma_uint8 offsetAndBlockSizeData[8]; + ma_uint32 offset; + foundChunk_data = MA_TRUE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { + return MA_FALSE; + } + offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += offset; + pWav->dataChunkDataPos = cursor; + dataChunkSize = chunkSize; + if (sequential || !isProcessingMetadata) { + break; + } else { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (isProcessingMetadata) { + ma_uint64 metadataBytesRead; + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + break; + } + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + } + if (!foundChunk_fmt || !foundChunk_data) { + return MA_FALSE; + } + if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || + (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return MA_FALSE; + } + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); + } + if (!sequential) { + if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return MA_FALSE; + } + cursor = pWav->dataChunkDataPos; + } + if (isProcessingMetadata && metadataParser.metadataCount > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 metadataBytesRead; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + } + if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { + dataChunkSize = 0; + for (;;) { + ma_uint8 temp[4096]; + size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); + dataChunkSize += bytesRead; + if (bytesRead < sizeof(temp)) { + break; + } + } + } + if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataChunkSize; + if (sampleCountFromFactChunk != 0) { + pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else if (aiffFrameCount != 0) { + pWav->totalPCMFrameCount = aiffFrameCount; + } else { + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if ((blockCount * fmt.blockAlign) < dataChunkSize) { + blockCount += 1; + } + totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); + pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; + pWav->totalPCMFrameCount += blockCount; + } + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->channels > 2) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } +#endif + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); +} +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); +} +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) +{ + ma_dr_wav_metadata *result = pWav->pMetadata; + pWav->pMetadata = NULL; + pWav->metadataCount = 0; + return result; +} +MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + return pWav->onWrite(pWav->pUserData, pData, dataSize); +} +MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + return pWav->onWrite(pWav->pUserData, &byte, 1); +} +MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap16(value); + } + return ma_dr_wav__write(pWav, &value, 2); +} +MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap32(value); + } + return ma_dr_wav__write(pWav, &value, 4); +} +MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +{ + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap64(value); + } + return ma_dr_wav__write(pWav, &value, 8); +} +MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) +{ + union { + ma_uint32 u32; + float f32; + } u; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + u.f32 = value; + if (!ma_dr_wav__is_little_endian()) { + u.u32 = ma_dr_wav__bswap32(u.u32); + } + return ma_dr_wav__write(pWav, &u.u32, 4); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) +{ + if (pWav == NULL) { + return dataSize; + } + return ma_dr_wav__write(pWav, pData, dataSize); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) +{ + if (pWav == NULL) { + return 1; + } + return ma_dr_wav__write_byte(pWav, byte); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) +{ + if (pWav == NULL) { + return 2; + } + return ma_dr_wav__write_u16ne_to_le(pWav, value); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) +{ + if (pWav == NULL) { + return 4; + } + return ma_dr_wav__write_u32ne_to_le(pWav, value); +} +#if 0 +MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) +{ + if (pWav == NULL) { + return 8; + } + return ma_dr_wav__write_u64ne_to_le(pWav, value); +} +#endif +MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) +{ + if (pWav == NULL) { + return 4; + } + return ma_dr_wav__write_f32ne_to_le(pWav, value); +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) +{ + size_t len; + if (pWav == NULL) { + return bufFixedSize; + } + len = ma_dr_wav__strlen_clamped(str, bufFixedSize); + ma_dr_wav__write_or_count(pWav, str, len); + if (len < bufFixedSize) { + size_t i; + for (i = 0; i < bufFixedSize - len; ++i) { + ma_dr_wav__write_byte(pWav, 0); + } + } + return bufFixedSize; +} +MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) +{ + size_t bytesWritten = 0; + ma_bool32 hasListAdtl = MA_FALSE; + ma_bool32 hasListInfo = MA_FALSE; + ma_uint32 iMetadata; + if (pMetadatas == NULL || metadataCount == 0) { + return 0; + } + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 chunkSize = 0; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { + hasListInfo = MA_TRUE; + } + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { + hasListAdtl = MA_TRUE; + } + switch (pMetadata->type) { + case ma_dr_wav_metadata_type_smpl: + { + ma_uint32 iLoop; + chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + } + if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + } + } break; + case ma_dr_wav_metadata_type_inst: + { + chunkSize = MA_DR_WAV_INST_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + } break; + case ma_dr_wav_metadata_type_cue: + { + ma_uint32 iCuePoint; + chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + } + } break; + case ma_dr_wav_metadata_type_acid: + { + chunkSize = MA_DR_WAV_ACID_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + } break; + case ma_dr_wav_metadata_type_bext: + { + char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); + bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + if (pMetadata->data.bext.codingHistorySize > 0) { + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { + chunkSize = pMetadata->data.unknown.dataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + } + } break; + default: break; + } + if ((chunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + if (hasListInfo) { + ma_uint32 chunkSize = 4; + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { + chunkSize += 8; + chunkSize += pMetadata->data.infoText.stringLength + 1; + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + chunkSize += 8; + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { + const char* pID = NULL; + switch (pMetadata->type) { + case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + default: break; + } + MA_DR_WAV_ASSERT(pID != NULL); + if (pMetadata->data.infoText.stringLength) { + subchunkSize = pMetadata->data.infoText.stringLength + 1; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { + if (pMetadata->data.unknown.dataSizeInBytes) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } + if ((subchunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + } + if (hasListAdtl) { + ma_uint32 chunkSize = 4; + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + switch (pMetadata->type) + { + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: + { + chunkSize += 8; + chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pMetadata->data.labelOrNote.stringLength > 0) { + chunkSize += pMetadata->data.labelOrNote.stringLength + 1; + } + } break; + case ma_dr_wav_metadata_type_list_labelled_cue_region: + { + chunkSize += 8; + chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + chunkSize += 8; + chunkSize += pMetadata->data.unknown.dataSizeInBytes; + } + } break; + default: break; + } + if ((chunkSize % 2) != 0) { + chunkSize += 1; + } + } + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); + for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + switch (pMetadata->type) + { + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: + { + if (pMetadata->data.labelOrNote.stringLength > 0) { + const char *pID = NULL; + if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { + pID = "labl"; + } + else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { + pID = "note"; + } + MA_DR_WAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } break; + case ma_dr_wav_metadata_type_list_labelled_cue_region: + { + subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; + } + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + if (pMetadata->data.labelledCueRegion.stringLength > 0) { + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); + } + } break; + case ma_dr_wav_metadata_type_unknown: + { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { + subchunkSize = pMetadata->data.unknown.dataSizeInBytes; + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + } + } break; + default: break; + } + if ((subchunkSize % 2) != 0) { + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); + } + } + } + MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); + return bytesWritten; +} +MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + return (ma_uint32)chunkSize; +} +MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) +{ + if (dataChunkSize <= 0xFFFFFFFFUL) { + return (ma_uint32)dataChunkSize; + } else { + return 0xFFFFFFFFUL; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) +{ + ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); + return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) +{ + return 24 + dataChunkSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) +{ + ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); + if (chunkSize > 0xFFFFFFFFUL) { + chunkSize = 0xFFFFFFFFUL; + } + return chunkSize; +} +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) +{ + return dataChunkSize; +} +MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pWav == NULL || onWrite == NULL) { + return MA_FALSE; + } + if (!isSequential && onSeek == NULL) { + return MA_FALSE; + } + if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return MA_FALSE; + } + if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return MA_FALSE; + } + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + pWav->onWrite = onWrite; + pWav->onSeek = onSeek; + pWav->pUserData = pUserData; + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + pWav->fmt.formatTag = (ma_uint16)pFormat->format; + pWav->fmt.channels = (ma_uint16)pFormat->channels; + pWav->fmt.sampleRate = pFormat->sampleRate; + pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->fmt.extendedSize = 0; + pWav->isSequentialWrite = isSequential; + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) +{ + size_t runningPos = 0; + ma_uint64 initialDataChunkSize = 0; + ma_uint64 chunkSizeFMT; + if (pWav->isSequentialWrite) { + initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; + if (pFormat->container == ma_dr_wav_container_riff) { + if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { + return MA_FALSE; + } + } + } + pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "RIFF", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "RF64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else { + return MA_FALSE; + } + if (pFormat->container == ma_dr_wav_container_rf64) { + ma_uint32 initialds64ChunkSize = 28; + ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "ds64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); + } + if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { + chunkSizeFMT = 16; + runningPos += ma_dr_wav__write(pWav, "fmt ", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); + } else if (pFormat->container == ma_dr_wav_container_w64) { + chunkSizeFMT = 40; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); + } + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { + runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + } + pWav->dataChunkDataPos = runningPos; + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + } + pWav->container = pFormat->container; + pWav->channels = (ma_uint16)pFormat->channels; + pWav->sampleRate = pFormat->sampleRate; + pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (ma_uint16)pFormat->format; + pWav->dataChunkDataPos = runningPos; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); +} +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); +} +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->pMetadata = pMetadata; + pWav->metadataCount = metadataCount; + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); +} +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) +{ + ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + ma_uint64 riffChunkSizeBytes; + ma_uint64 fileSizeBytes = 0; + if (pFormat->container == ma_dr_wav_container_riff) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); + } else if (pFormat->container == ma_dr_wav_container_w64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); + fileSizeBytes = riffChunkSizeBytes; + } else if (pFormat->container == ma_dr_wav_container_rf64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + fileSizeBytes = (8 + riffChunkSizeBytes); + } + return fileSizeBytes; +} +#ifndef MA_DR_WAV_NO_STDIO +MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} +MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +{ + return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); +} +#endif +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); +} +#endif +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + FILE* pFile; + if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); +} +#endif +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +#endif +#endif +MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + pWav->memoryStream.currentReadPos += bytesToRead; + } + return bytesToRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { + return MA_FALSE; + } + } else { + if (pWav->memoryStream.currentReadPos < (size_t)-offset) { + return MA_FALSE; + } + } + pWav->memoryStream.currentReadPos += offset; + } else { + if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { + pWav->memoryStream.currentReadPos = offset; + } else { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + size_t bytesRemaining; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; + if (bytesRemaining < bytesToWrite) { + void* pNewData; + size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; + if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { + newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; + } + pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + *pWav->memoryStreamWrite.ppData = pNewData; + pWav->memoryStreamWrite.dataCapacity = newDataCapacity; + } + MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + pWav->memoryStreamWrite.currentWritePos += bytesToWrite; + if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { + pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; + } + *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; + return bytesToWrite; +} +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) +{ + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { + if (offset > 0) { + if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { + offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); + } + } else { + if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { + offset = -(int)pWav->memoryStreamWrite.currentWritePos; + } + } + pWav->memoryStreamWrite.currentWritePos += offset; + } else { + if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + pWav->memoryStreamWrite.currentWritePos = offset; + } else { + pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return MA_FALSE; + } + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); +} +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (data == NULL || dataSize == 0) { + return MA_FALSE; + } + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStream.data = (const ma_uint8*)data; + pWav->memoryStream.dataSize = dataSize; + pWav->memoryStream.currentReadPos = 0; + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); +} +MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (ppData == NULL || pDataSize == NULL) { + return MA_FALSE; + } + *ppData = NULL; + *pDataSize = 0; + if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return MA_FALSE; + } + pWav->memoryStreamWrite.ppData = ppData; + pWav->memoryStreamWrite.pDataSize = pDataSize; + pWav->memoryStreamWrite.dataSize = 0; + pWav->memoryStreamWrite.dataCapacity = 0; + pWav->memoryStreamWrite.currentWritePos = 0; + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); +} +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pFormat == NULL) { + return MA_FALSE; + } + return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); +} +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) +{ + ma_result result = MA_SUCCESS; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + if (pWav->onWrite != NULL) { + ma_uint32 paddingSize = 0; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { + paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); + } else { + paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); + } + if (paddingSize > 0) { + ma_uint64 paddingData = 0; + ma_dr_wav__write(pWav, &paddingData, paddingSize); + } + if (pWav->onSeek && !pWav->isSequentialWrite) { + if (pWav->container == ma_dr_wav_container_riff) { + if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); + ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == ma_dr_wav_container_w64) { + if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + int ds64BodyPos = 12 + 8; + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); + } + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); + } + } + } + if (pWav->isSequentialWrite) { + if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { + result = MA_INVALID_FILE; + } + } + } else { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + } +#ifndef MA_DR_WAV_NO_STDIO + if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { + fclose((FILE*)pWav->pUserData); + } +#endif + return result; +} +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) +{ + size_t bytesRead; + ma_uint32 bytesPerFrame; + if (pWav == NULL || bytesToRead == 0) { + return 0; + } + if (bytesToRead > pWav->bytesRemaining) { + bytesToRead = (size_t)pWav->bytesRemaining; + } + if (bytesToRead == 0) { + return 0; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + if (pBufferOut != NULL) { + bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); + } else { + bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > 0x7FFFFFFF) { + bytesToSeek = 0x7FFFFFFF; + } + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { + break; + } + bytesRead += bytesToSeek; + } + while (bytesRead < bytesToRead) { + ma_uint8 buffer[4096]; + size_t bytesSeeked; + size_t bytesToSeek = (bytesToRead - bytesRead); + if (bytesToSeek > sizeof(buffer)) { + bytesToSeek = sizeof(buffer); + } + bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); + bytesRead += bytesSeeked; + if (bytesSeeked < bytesToSeek) { + break; + } + } + } + pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; + pWav->bytesRemaining -= bytesRead; + return bytesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint32 bytesPerFrame; + ma_uint64 bytesToRead; + ma_uint64 framesRemainingInFile; + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; + if (framesToRead > framesRemainingInFile) { + framesToRead = framesRemainingInFile; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesToRead = framesToRead * bytesPerFrame; + if (bytesToRead > MA_SIZE_MAX) { + bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; + } + if (bytesToRead == 0) { + return 0; + } + return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL) { + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 framesRead = 0; + if (ma_dr_wav_is_container_be(pWav->container)) { + if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } + goto post_process; + } + } + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } + post_process: + { + if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { + if (pBufferOut != NULL) { + ma_uint64 iSample; + for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { + ((ma_uint8*)pBufferOut)[iSample] += 128; + } + } + } + } + return framesRead; +} +MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) +{ + if (pWav->onWrite != NULL) { + return MA_FALSE; + } + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->ima); + } else { + MA_DR_WAV_ASSERT(MA_FALSE); + } + } + pWav->readCursorInPCMFrames = 0; + pWav->bytesRemaining = pWav->dataChunkDataSize; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) +{ + if (pWav == NULL || pWav->onSeek == NULL) { + return MA_FALSE; + } + if (pWav->onWrite != NULL) { + return MA_FALSE; + } + if (pWav->totalPCMFrameCount == 0) { + return MA_TRUE; + } + if (targetFrameIndex > pWav->totalPCMFrameCount) { + targetFrameIndex = pWav->totalPCMFrameCount; + } + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (targetFrameIndex < pWav->readCursorInPCMFrames) { + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; + } + } + if (targetFrameIndex > pWav->readCursorInPCMFrames) { + ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + ma_int16 devnull[2048]; + while (offsetInFrames > 0) { + ma_uint64 framesRead = 0; + ma_uint64 framesToRead = offsetInFrames; + if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { + framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + } else { + MA_DR_WAV_ASSERT(MA_FALSE); + } + if (framesRead != framesToRead) { + return MA_FALSE; + } + offsetInFrames -= framesRead; + } + } + } else { + ma_uint64 totalSizeInBytes; + ma_uint64 currentBytePos; + ma_uint64 targetBytePos; + ma_uint64 offset; + ma_uint32 bytesPerFrame; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return MA_FALSE; + } + totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; + currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + targetBytePos = targetFrameIndex * bytesPerFrame; + if (currentBytePos < targetBytePos) { + offset = (targetBytePos - currentBytePos); + } else { + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; + } + offset = targetBytePos; + } + while (offset > 0) { + int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); + if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; + } + pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; + pWav->bytesRemaining -= offset32; + offset -= offset32; + } + } + return MA_TRUE; +} +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) +{ + if (pCursor == NULL) { + return MA_INVALID_ARGS; + } + *pCursor = 0; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + *pCursor = pWav->readCursorInPCMFrames; + return MA_SUCCESS; +} +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) +{ + if (pLength == NULL) { + return MA_INVALID_ARGS; + } + *pLength = 0; + if (pWav == NULL) { + return MA_INVALID_ARGS; + } + *pLength = pWav->totalPCMFrameCount; + return MA_SUCCESS; +} +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) +{ + size_t bytesWritten; + if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { + return 0; + } + bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); + pWav->dataChunkDataSize += bytesWritten; + return bytesWritten; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + const ma_uint8* pRunningData; + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > MA_SIZE_MAX) { + return 0; + } + bytesWritten = 0; + pRunningData = (const ma_uint8*)pData; + while (bytesToWrite > 0) { + size_t bytesJustWritten; + ma_uint64 bytesToWriteThisIteration; + bytesToWriteThisIteration = bytesToWrite; + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + if (bytesJustWritten == 0) { + break; + } + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + ma_uint32 bytesPerSample; + const ma_uint8* pRunningData; + if (pWav == NULL || framesToWrite == 0 || pData == NULL) { + return 0; + } + bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); + if (bytesToWrite > MA_SIZE_MAX) { + return 0; + } + bytesWritten = 0; + pRunningData = (const ma_uint8*)pData; + bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + if (bytesPerSample == 0) { + return 0; + } + while (bytesToWrite > 0) { + ma_uint8 temp[4096]; + ma_uint32 sampleCount; + size_t bytesJustWritten; + ma_uint64 bytesToWriteThisIteration; + bytesToWriteThisIteration = bytesToWrite; + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + sampleCount = sizeof(temp)/bytesPerSample; + if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; + } + MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + if (bytesJustWritten == 0) { + break; + } + bytesToWrite -= bytesJustWritten; + bytesWritten += bytesJustWritten; + pRunningData += bytesJustWritten; + } + return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; +} +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) +{ + if (ma_dr_wav__is_little_endian()) { + return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); + } else { + return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + MA_DR_WAV_ASSERT(framesToRead > 0); + if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + ma_uint8 header[7]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrameCount = 2; + } else { + ma_uint8 header[14]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + pWav->msadpcm.predictor[0] = header[0]; + pWav->msadpcm.predictor[1] = header[1]; + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); + pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; + pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; + pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.cachedFrameCount = 2; + } + } + while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + ma_uint32 iSample = 0; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->msadpcm.cachedFrameCount -= 1; + } + if (framesToRead == 0) { + break; + } + if (pWav->msadpcm.cachedFrameCount == 0) { + if (pWav->msadpcm.bytesRemainingInBlock == 0) { + continue; + } else { + static ma_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + ma_uint8 nibbles; + ma_int32 nibble0; + ma_int32 nibble1; + if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { + return totalFramesRead; + } + pWav->msadpcm.bytesRemainingInBlock -= 1; + nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } + nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } + if (pWav->channels == 1) { + ma_int32 newSample0; + ma_int32 newSample1; + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[0]; + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample1; + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 2; + } else { + ma_int32 newSample0; + ma_int32 newSample1; + newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; + newSample0 += nibble0 * pWav->msadpcm.delta[0]; + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); + pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; + if (pWav->msadpcm.delta[0] < 16) { + pWav->msadpcm.delta[0] = 16; + } + pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; + pWav->msadpcm.prevFrames[0][1] = newSample0; + newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; + newSample1 += nibble1 * pWav->msadpcm.delta[1]; + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); + pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; + if (pWav->msadpcm.delta[1] < 16) { + pWav->msadpcm.delta[1] = 16; + } + pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; + pWav->msadpcm.prevFrames[1][1] = newSample1; + pWav->msadpcm.cachedFrames[2] = newSample0; + pWav->msadpcm.cachedFrames[3] = newSample1; + pWav->msadpcm.cachedFrameCount = 1; + } + } + } + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + ma_uint32 iChannel; + static ma_int32 indexTable[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + static ma_int32 stepTable[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); + while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + MA_DR_WAV_ASSERT(framesToRead > 0); + if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { + if (pWav->channels == 1) { + ma_uint8 header[4]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + if (header[2] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; + } + pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.cachedFrameCount = 1; + } else { + ma_uint8 header[8]; + if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); + if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); + pWav->ima.bytesRemainingInBlock = 0; + return totalFramesRead; + } + pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.cachedFrameCount = 1; + } + } + while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { + if (pBufferOut != NULL) { + ma_uint32 iSample; + for (iSample = 0; iSample < pWav->channels; iSample += 1) { + pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + } + pBufferOut += pWav->channels; + } + framesToRead -= 1; + totalFramesRead += 1; + pWav->readCursorInPCMFrames += 1; + pWav->ima.cachedFrameCount -= 1; + } + if (framesToRead == 0) { + break; + } + if (pWav->ima.cachedFrameCount == 0) { + if (pWav->ima.bytesRemainingInBlock == 0) { + continue; + } else { + pWav->ima.cachedFrameCount = 8; + for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { + ma_uint32 iByte; + ma_uint8 nibbles[4]; + if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { + pWav->ima.cachedFrameCount = 0; + return totalFramesRead; + } + pWav->ima.bytesRemainingInBlock -= 4; + for (iByte = 0; iByte < 4; ++iByte) { + ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + ma_int32 predictor = pWav->ima.predictor[iChannel]; + ma_int32 diff = step >> 3; + if (nibble0 & 1) diff += step >> 2; + if (nibble0 & 2) diff += step >> 1; + if (nibble0 & 4) diff += step; + if (nibble0 & 8) diff = -diff; + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + step = stepTable[pWav->ima.stepIndex[iChannel]]; + predictor = pWav->ima.predictor[iChannel]; + diff = step >> 3; + if (nibble1 & 1) diff += step >> 2; + if (nibble1 & 2) diff += step >> 1; + if (nibble1 & 4) diff += step; + if (nibble1 & 8) diff = -diff; + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); + pWav->ima.predictor[iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + } + } + } + } + } + return totalFramesRead; +} +#ifndef MA_DR_WAV_NO_CONVERSION_API +static unsigned short g_ma_dr_wavAlawTable[256] = { + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 +}; +static unsigned short g_ma_dr_wavMulawTable[256] = { + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 +}; +static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) +{ + return (short)g_ma_dr_wavAlawTable[sampleIn]; +} +static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) +{ + return (short)g_ma_dr_wavMulawTable[sampleIn]; +} +MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + size_t i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 2) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const ma_int16*)pIn)[i]; + } + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < totalSampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (ma_int16)((ma_int64)sample >> 48); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x << 8; + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; + r = x >> 8; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + int x = pIn[i]; + r = x >> 16; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + float c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5f); + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) +{ + int r; + size_t i; + for (i = 0; i < sampleCount; ++i) { + double x = pIn[i]; + double c; + c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + c = c + 1; + r = (int)(c * 32767.5); + r = r - 32768; + pOut[i] = (short)r; + } +} +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); + } +} +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + for (i = 0; i < sampleCount; ++i) { + pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); + } +} +MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 2) { + ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); + return; + } + if (bytesPerSample == 4) { + ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < sampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + unsigned int i; + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((const float*)pIn)[i]; + } + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_int16 samples16[2048]; + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (pIn[i] / 256.0f) * 2 - 1; + } +#else + for (i = 0; i < sampleCount; ++i) { + float x = pIn[i]; + x = x * 0.00784313725490196078f; + x = x - 1; + *pOut++ = x; + } +#endif +} +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] * 0.000030517578125f; + } +} +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + double x; + ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); + ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); + ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); + x = (double)((ma_int32)(a | b | c) >> 8); + *pOut++ = (float)(x * 0.00000011920928955078125); + } +} +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)(pIn[i] / 2147483648.0); + } +} +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (float)pIn[i]; + } +} +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; + } +} +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; + } +} +MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + unsigned int i; + if (bytesPerSample == 1) { + ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 2) { + ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); + return; + } + if (bytesPerSample == 3) { + ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); + return; + } + if (bytesPerSample == 4) { + for (i = 0; i < totalSampleCount; ++i) { + *pOut++ = ((const ma_int32*)pIn)[i]; + } + return; + } + if (bytesPerSample > 8) { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } + for (i = 0; i < totalSampleCount; ++i) { + ma_uint64 sample = 0; + unsigned int shift = (8 - bytesPerSample) * 8; + unsigned int j; + for (j = 0; j < bytesPerSample; j += 1) { + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; + shift += 8; + } + pIn += j; + *pOut++ = (ma_int32)((ma_int64)sample >> 32); + } +} +MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +{ + if (bytesPerSample == 4) { + ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + return; + } else if (bytesPerSample == 8) { + ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + return; + } else { + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + return; + } +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + ma_int16 samples16[2048]; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + pBufferOut += framesRead*pWav->channels; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + bytesPerSample = bytesPerFrame / pWav->channels; + if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { + return 0; + } + totalFramesRead = 0; + while (framesToRead > 0) { + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + if (framesRead == 0) { + break; + } + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + samplesRead = framesRead * pWav->channels; + if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { + MA_DR_WAV_ASSERT(MA_FALSE); + break; + } + ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif + pBufferOut += samplesRead; + framesToRead -= framesRead; + totalFramesRead += framesRead; + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); + } + if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + } + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + } + return 0; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + } + return framesRead; +} +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((int)pIn[i] - 128) << 24; + } +} +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = pIn[i] << 16; + } +} +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + unsigned int s0 = pIn[i*3 + 0]; + unsigned int s1 = pIn[i*3 + 1]; + unsigned int s2 = pIn[i*3 + 2]; + ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + *pOut++ = sample32; + } +} +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + } +} +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); + } +} +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i = 0; i < sampleCount; ++i) { + *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; + } +} +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) +{ + size_t i; + if (pOut == NULL || pIn == NULL) { + return; + } + for (i= 0; i < sampleCount; ++i) { + *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; + } +} +MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + ma_int16* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + float* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) +{ + ma_uint64 sampleDataSize; + ma_int32* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); + return NULL; + } + pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + if (pSampleData == NULL) { + ma_dr_wav_uninit(pWav); + return NULL; + } + framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + if (framesRead != pWav->totalPCMFrameCount) { + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); + return NULL; + } + ma_dr_wav_uninit(pWav); + if (sampleRate) { + *sampleRate = pWav->sampleRate; + } + if (channels) { + *channels = pWav->channels; + } + if (totalFrameCount) { + *totalFrameCount = pWav->totalPCMFrameCount; + } + return pSampleData; +} +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (channelsOut) { + *channelsOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif +#endif +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_wav wav; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalFrameCountOut) { + *totalFrameCountOut = 0; + } + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); +} +#endif +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_wav__free_default(p, NULL); + } +} +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) +{ + return (ma_int16)ma_dr_wav_bytes_to_u16(data); +} +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) +{ + return ma_dr_wav_bytes_to_u32_le(data); +} +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) +{ + union { + ma_uint32 u32; + float f32; + } value; + value.u32 = ma_dr_wav_bytes_to_u32(data); + return value.f32; +} +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) +{ + return (ma_int32)ma_dr_wav_bytes_to_u32(data); +} +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) +{ + return + ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | + ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); +} +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) +{ + return (ma_int64)ma_dr_wav_bytes_to_u64(data); +} +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) +{ + int i; + for (i = 0; i < 16; i += 1) { + if (a[i] != b[i]) { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) +{ + return + a[0] == b[0] && + a[1] == b[1] && + a[2] == b[2] && + a[3] == b[3]; +} +#ifdef __MRC__ +#pragma options opt reset +#endif +#endif +/* dr_wav_c end */ +#endif /* MA_DR_WAV_IMPLEMENTATION */ +#endif /* MA_NO_WAV */ + +#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) +#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_flac_c begin */ +#ifndef ma_dr_flac_c +#define ma_dr_flac_c +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #if __GNUC__ >= 7 + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #endif +#endif +#ifdef __linux__ + #ifndef _BSD_SOURCE + #define _BSD_SOURCE + #endif + #ifndef _DEFAULT_SOURCE + #define _DEFAULT_SOURCE + #endif + #ifndef __USE_BSD + #define __USE_BSD + #endif + #include +#endif +#include +#include +#if !defined(MA_DR_FLAC_NO_SIMD) + #if defined(MA_X64) || defined(MA_X86) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) + #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #endif + #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) + #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE2 + #endif + #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE41 + #endif + #endif + #if defined(MA_DR_FLAC_SUPPORT_SSE41) + #include + #elif defined(MA_DR_FLAC_SUPPORT_SSE2) + #include + #endif + #endif + #if defined(MA_ARM) + #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_DR_FLAC_SUPPORT_NEON + #include + #endif + #endif +#endif +#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static void ma_dr_flac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define MA_DR_FLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void ma_dr_flac__cpuid(int info[4], int fid) + { + #if defined(MA_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 + } + #else + #define MA_DR_FLAC_NO_CPUID + #endif + #endif +#else + #define MA_DR_FLAC_NO_CPUID +#endif +static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; + #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) + return MA_TRUE; + #else + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_dr_flac__cpuid(info, 1); + return (info[3] & (1 << 26)) != 0; + #endif + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) + #if defined(__SSE4_1__) || defined(__AVX__) + return MA_TRUE; + #else + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; + #else + int info[4]; + ma_dr_flac__cpuid(info, 1); + return (info[2] & (1 << 19)) != 0; + #endif + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC + #endif + #endif +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC +#elif defined(__clang__) + #if defined(__has_builtin) + #if __has_builtin(__builtin_bswap16) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap32) + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #endif + #if __has_builtin(__builtin_bswap64) + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #endif +#elif defined(__GNUC__) + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #endif + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #endif +#elif defined(__WATCOMC__) && defined(__386__) + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline ma_uint16 _watcom_bswap16(ma_uint16); + extern __inline ma_uint32 _watcom_bswap32(ma_uint32); + extern __inline ma_uint64 _watcom_bswap64(ma_uint64); +#pragma aux _watcom_bswap16 = \ + "xchg al, ah" \ + parm [ax] \ + value [ax] \ + modify nomemory; +#pragma aux _watcom_bswap32 = \ + "bswap eax" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#pragma aux _watcom_bswap64 = \ + "bswap eax" \ + "bswap edx" \ + "xchg eax,edx" \ + parm [eax edx] \ + value [eax edx] \ + modify nomemory; +#endif +#ifndef MA_DR_FLAC_ASSERT +#include +#define MA_DR_FLAC_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_FLAC_MALLOC +#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_FLAC_REALLOC +#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_FLAC_FREE +#define MA_DR_FLAC_FREE(p) free((p)) +#endif +#ifndef MA_DR_FLAC_COPY_MEMORY +#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_FLAC_ZERO_MEMORY +#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#ifndef MA_DR_FLAC_ZERO_OBJECT +#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) +#endif +#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 +#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 +#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 +#define MA_DR_FLAC_SUBFRAME_FIXED 8 +#define MA_DR_FLAC_SUBFRAME_LPC 32 +#define MA_DR_FLAC_SUBFRAME_RESERVED 255 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 +#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_FLAC_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_FLAC_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_FLAC_VERSION_REVISION; + } +} +MA_API const char* ma_dr_flac_version_string(void) +{ + return MA_DR_FLAC_VERSION_STRING; +} +#if defined(__has_feature) + #if __has_feature(thread_sanitizer) + #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #else + #define MA_DR_FLAC_NO_THREAD_SANITIZE + #endif +#else + #define MA_DR_FLAC_NO_THREAD_SANITIZE +#endif +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; +#endif +#ifndef MA_DR_FLAC_NO_CPUID +static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; +static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +{ + static ma_bool32 isCPUCapsInitialized = MA_FALSE; + if (!isCPUCapsInitialized) { +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) + int info[4] = {0}; + ma_dr_flac__cpuid(info, 0x80000001); + ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; +#endif + ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); + ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); + isCPUCapsInitialized = MA_TRUE; + } +} +#else +static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; +static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) +{ +#if defined(MA_DR_FLAC_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) + #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + return MA_TRUE; + #else + return MA_FALSE; + #endif + #else + return MA_FALSE; + #endif +#else + return MA_FALSE; +#endif +} +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) +{ + ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + ma_dr_flac__gIsLZCNTSupported = MA_TRUE; +#endif +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) +{ +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN + return MA_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} +static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) + ma_uint32 r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) + #else + "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) + #endif + ); + return r; + #else + return __builtin_bswap32(n); + #endif + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} +static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) +{ +#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + #if defined(_MSC_VER) && !defined(__clang__) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #elif defined(__WATCOMC__) && defined(__386__) + return _watcom_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint16(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) +{ + const ma_uint8* pNum = (ma_uint8*)pData; + return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); +} +static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) +{ + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint64(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) +{ + if (!ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); + } + return n; +} +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) +{ + const ma_uint8* pNum = (ma_uint8*)pData; + return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; +} +static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) +{ + ma_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + return result; +} +static ma_uint8 ma_dr_flac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; +static ma_uint16 ma_dr_flac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; +static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) +{ + return ma_dr_flac__crc8_table[crc ^ data]; +} +static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + ma_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + ma_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 32); + wholeBytes = count >> 3; + leftoverBits = count - (wholeBytes*8); + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + } + return crc; +#endif +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) +{ + return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) +{ +#ifdef MA_64BIT + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#endif + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + return crc; +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef MA_64BIT + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); + } + return crc; +} +#if 0 +static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + ma_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + ma_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + return crc; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 64); + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + default: + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) +{ +#ifdef MA_DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + MA_DR_FLAC_ASSERT(count <= 64); + wholeBytes = count >> 3; + leftoverBits = count & 7; + leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + switch (wholeBytes) { + default: + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} +static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) +{ +#ifdef MA_64BIT + return ma_dr_flac_crc16__64bit(crc, data, count); +#else + return ma_dr_flac_crc16__32bit(crc, data, count); +#endif +} +#endif +#ifdef MA_64BIT +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 +#else +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 +#endif +#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) +#ifndef MA_DR_FLAC_NO_CRC +static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} +static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) +{ + if (bs->crc16CacheIgnoredBytes == 0) { + bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); + } else { + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; + } +} +static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) +{ + MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + ma_dr_flac__update_crc16(bs); + } else { + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + return bs->crc16; +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) +{ + size_t bytesRead; + size_t alignedL1LineCount; + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } + if (bs->unalignedByteCount > 0) { + return MA_FALSE; + } + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); + bs->nextL2Line = 0; + if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } + alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + if (alignedL1LineCount > 0) { + size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t i; + for (i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + bs->nextL2Line = (ma_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return MA_TRUE; + } else { + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + return MA_FALSE; + } +} +static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) +{ + size_t bytesRead; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); +#endif + if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { + bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return MA_TRUE; + } + bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + return MA_FALSE; + } + MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); + bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); + bs->unalignedByteCount = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return MA_TRUE; +} +static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) +{ + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + bs->unalignedByteCount = 0; + bs->unalignedCache = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResultOut != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { +#ifdef MA_64BIT + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; +#else + if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + *pResultOut = (ma_uint32)bs->cache; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } +#endif + return MA_TRUE; + } else { + ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + ma_uint32 bitCountLo = bitCount - bitCountHi; + ma_uint32 resultHi; + MA_DR_FLAC_ASSERT(bitCountHi > 0); + MA_DR_FLAC_ASSERT(bitCountHi < 32); + resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + if (bitCount < 32) { + ma_uint32 signbit; + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + } + *pResult = (ma_int32)result; + return MA_TRUE; +} +#ifdef MA_64BIT +static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) +{ + ma_uint32 resultHi; + ma_uint32 resultLo; + MA_DR_FLAC_ASSERT(bitCount <= 64); + MA_DR_FLAC_ASSERT(bitCount > 32); + if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { + return MA_FALSE; + } + *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); + return MA_TRUE; +} +#endif +#if 0 +static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) +{ + ma_uint64 result; + ma_uint64 signbit; + MA_DR_FLAC_ASSERT(bitCount <= 64); + if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { + return MA_FALSE; + } + signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + *pResultOut = (ma_int64)result; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_uint16)result; + return MA_TRUE; +} +#if 0 +static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) +{ + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_int16)result; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) +{ + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_uint8)result; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) +{ + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; + } + *pResult = (ma_int8)result; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (ma_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return MA_TRUE; + } else { + bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; +#ifdef MA_64BIT + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint64 bin; + if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; + } + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint32 bin; + if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; + } + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + while (bitsToSeek >= 8) { + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { + return MA_FALSE; + } + bitsToSeek -= 8; + } + if (bitsToSeek > 0) { + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { + return MA_FALSE; + } + bitsToSeek = 0; + } + MA_DR_FLAC_ASSERT(bitsToSeek == 0); + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; + } + for (;;) { + ma_uint8 hi; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__reset_crc16(bs); +#endif + if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { + return MA_FALSE; + } + if (hi == 0xFF) { + ma_uint8 lo; + if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { + return MA_FALSE; + } + if (lo == 0x3E) { + return MA_TRUE; + } else { + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; + } + } + } + } +} +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#endif +#if defined(__WATCOMC__) && defined(__386__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +#endif +#ifdef __MRC__ +#include +#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC +#endif +static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) +{ + ma_uint32 n; + static ma_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + if (x == 0) { + return sizeof(x)*8; + } + n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef MA_64BIT + if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + return n - 1; +} +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) +{ +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return MA_TRUE; +#elif defined(__MRC__) + return MA_TRUE; +#else + #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC + return ma_dr_flac__gIsLZCNTSupported; + #else + return MA_FALSE; + #endif +#endif +} +static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) +{ +#if defined(_MSC_VER) + #ifdef MA_64BIT + return (ma_uint32)__lzcnt64(x); + #else + return (ma_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #if defined(MA_X64) + { + ma_uint64 r; + __asm__ __volatile__ ( + "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + return (ma_uint32)r; + } + #elif defined(MA_X86) + { + ma_uint32 r; + __asm__ __volatile__ ( + "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" + ); + return r; + } + #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT) + { + unsigned int r; + __asm__ __volatile__ ( + #if defined(MA_64BIT) + "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) + #else + "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) + #endif + ); + return r; + } + #else + if (x == 0) { + return sizeof(x)*8; + } + #ifdef MA_64BIT + return (ma_uint32)__builtin_clzll((ma_uint64)x); + #else + return (ma_uint32)__builtin_clzl((ma_uint32)x); + #endif + #endif + #else + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC +#include +static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) +{ + ma_uint32 n; + if (x == 0) { + return sizeof(x)*8; + } +#ifdef MA_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ + "db 0F3h, 0Fh, 0BDh, 0C0h" \ + parm [eax] \ + value [eax] \ + modify nomemory; +#else +#pragma aux ma_dr_flac__clz_watcom = \ + "bsr eax, eax" \ + "xor eax, 31" \ + parm [eax] nomemory \ + value [eax] \ + modify exact [eax] nomemory; +#endif +#endif +static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) +{ +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT + if (ma_dr_flac__is_lzcnt_supported()) { + return ma_dr_flac__clz_lzcnt(x); + } else +#endif + { +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC + return ma_dr_flac__clz_msvc(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return ma_dr_flac__clz_watcom_lzcnt(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); +#elif defined(__MRC__) + return __cntlzw(x); +#else + return ma_dr_flac__clz_software(x); +#endif + } +} +static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) +{ + ma_uint32 zeroCounter = 0; + ma_uint32 setBitOffsetPlus1; + while (bs->cache == 0) { + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + if (bs->cache == 1) { + *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + return MA_TRUE; + } + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + setBitOffsetPlus1 += 1; + if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(offsetFromStart > 0); + if (offsetFromStart > 0x7FFFFFFF) { + ma_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + } + ma_dr_flac__reset_cache(bs); + return MA_TRUE; +} +static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) +{ + ma_uint8 crc; + ma_uint64 result; + ma_uint8 utf8[7] = {0}; + int byteCount; + int i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pNumberOut != NULL); + MA_DR_FLAC_ASSERT(pCRCOut != NULL); + crc = *pCRCOut; + if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return MA_AT_END; + } + crc = ma_dr_flac_crc8(crc, utf8[0], 8); + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return MA_SUCCESS; + } + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return MA_CRC_MISMATCH; + } + MA_DR_FLAC_ASSERT(byteCount > 1); + result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (i = 1; i < byteCount; ++i) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return MA_AT_END; + } + crc = ma_dr_flac_crc8(crc, utf8[i], 8); + result = (result << 6) | (utf8[i] & 0x3F); + } + *pNumberOut = result; + *pCRCOut = crc; + return MA_SUCCESS; +} +static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) +{ +#if 1 + ma_uint32 result = 0; + while (x > 0) { + result += 1; + x >>= 1; + } + return result; +#endif +} +static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) +{ + return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; +} +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_int32 prediction = 0; + MA_DR_FLAC_ASSERT(order <= 32); + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + return (ma_int32)(prediction >> shift); +} +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_int64 prediction; + MA_DR_FLAC_ASSERT(order <= 32); +#ifndef MA_64BIT + if (order == 8) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + } + else + { + int j; + prediction = 0; + for (j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; + } + } +#endif +#ifdef MA_64BIT + prediction = 0; + switch (order) + { + case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; + } +#endif + return (ma_int32)(prediction >> shift); +} +#if 0 +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + for (i = 0; i < count; ++i) { + ma_uint32 zeroCounter = 0; + for (;;) { + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; + } + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + ma_uint32 decodedRice; + if (riceParam > 0) { + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; + } + } else { + decodedRice = 0; + } + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + return MA_TRUE; +} +#endif +#if 0 +static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_uint32 zeroCounter = 0; + ma_uint32 decodedRice; + for (;;) { + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; + } + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + if (riceParam > 0) { + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; + } + } else { + decodedRice = 0; + } + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return MA_TRUE; +} +#endif +#if 0 +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_dr_flac_cache_t riceParamMask; + ma_uint32 zeroCounter; + ma_uint32 setBitOffsetPlus1; + ma_uint32 riceParamPart; + ma_uint32 riceLength; + MA_DR_FLAC_ASSERT(riceParam > 0); + riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); + zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + } + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + ma_uint32 bitCountLo; + ma_dr_flac_cache_t resultHi; + bs->consumedBits += riceLength; + bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); + bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); +#endif + bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; +#ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + } + riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + pZeroCounterOut[0] = zeroCounter; + pRiceParamPartOut[0] = riceParamPart; + return MA_TRUE; +} +#endif +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) +{ + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + pZeroCounterOut[0] = lzcount; + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + ma_uint32 riceParamPartHi; + ma_uint32 riceParamPartLo; + ma_uint32 riceParamPartLoBitCount; + riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); + riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; + bs_cache <<= riceParamPartLoBitCount; + } + } else { + ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + for (;;) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + lzcount = ma_dr_flac__clz(bs_cache); + zeroCounter += lzcount; + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + pZeroCounterOut[0] = zeroCounter; + goto extract_rice_param_part; + } + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + return MA_TRUE; +} +static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) +{ + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + extract_rice_param_part: + bs_cache <<= lzcount; + bs_consumedBits += lzcount; + if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { + bs_cache <<= riceParamPlus1; + bs_consumedBits += riceParamPlus1; + } else { + ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = riceParamPartLoBitCount; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; + } + bs_cache <<= riceParamPartLoBitCount; + } + } else { + for (;;) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); + #endif + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_consumedBits = 0; + #ifndef MA_DR_FLAC_NO_CRC + bs->crc16Cache = bs_cache; + #endif + } else { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; + } + bs_cache = bs->cache; + bs_consumedBits = bs->consumedBits; + } + lzcount = ma_dr_flac__clz(bs_cache); + if (lzcount < sizeof(bs_cache)*8) { + break; + } + } + goto extract_rice_param_part; + } + bs->cache = bs_cache; + bs->consumedBits = bs_consumedBits; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0; + ma_uint32 riceParamPart0; + ma_uint32 riceParamMask; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + (void)bitsPerSample; + (void)order; + (void)shift; + (void)coefficients; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + i = 0; + while (i < count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + pSamplesOut[i] = riceParamPart0; + i += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0 = 0; + ma_uint32 zeroCountPart1 = 0; + ma_uint32 zeroCountPart2 = 0; + ma_uint32 zeroCountPart3 = 0; + ma_uint32 riceParamPart0 = 0; + ma_uint32 riceParamPart1 = 0; + ma_uint32 riceParamPart2 = 0; + ma_uint32 riceParamPart3 = 0; + ma_uint32 riceParamMask; + const ma_int32* pSamplesOutEnd; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder == 0) { + return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + pSamplesOutEnd = pSamplesOut + (count & ~3); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + while (pSamplesOut < pSamplesOutEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut += 4; + } + } else { + while (pSamplesOut < pSamplesOutEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart1 &= riceParamMask; + riceParamPart2 &= riceParamMask; + riceParamPart3 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut += 4; + } + } + i = (count & ~3); + while (i < count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; + } + riceParamPart0 &= riceParamMask; + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } else { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + } + i += 1; + pSamplesOut += 1; + } + return MA_TRUE; +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +{ + __m128i r; + r = _mm_packs_epi32(a, b); + r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); + return r; +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_SSE41) +static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) +{ + return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); +} +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) +{ + __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); + __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); + return _mm_add_epi32(x64, x32); +} +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) +{ + return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); +} +static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) +{ + __m128i lo = _mm_srli_epi64(x, count); + __m128i hi = _mm_srai_epi32(x, count); + hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); + return _mm_or_si128(lo, hi); +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i riceParamMask128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); +#if 1 + { + int runningOrder = order; + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i prediction128; + __m128i zeroCountPart128; + __m128i riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; + } + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); + prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); + prediction128 = _mm_srai_epi32(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + } + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; + } + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; + __m128i coefficients128_0; + __m128i coefficients128_4; + __m128i coefficients128_8; + __m128i samples128_0; + __m128i samples128_4; + __m128i samples128_8; + __m128i prediction128; + __m128i riceParamMask128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + MA_DR_FLAC_ASSERT(order <= 12); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = _mm_set1_epi32(riceParamMask); + prediction128 = _mm_setzero_si128(); + coefficients128_0 = _mm_setzero_si128(); + coefficients128_4 = _mm_setzero_si128(); + coefficients128_8 = _mm_setzero_si128(); + samples128_0 = _mm_setzero_si128(); + samples128_4 = _mm_setzero_si128(); + samples128_8 = _mm_setzero_si128(); +#if 1 + { + int runningOrder = order; + if (runningOrder >= 4) { + coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); + samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; + case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; + case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); + samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; + case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; + case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; + } + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); + samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; + case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; + case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; + } + runningOrder = 0; + } + coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); + coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); + } +#else + switch (order) + { + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + } +#endif + while (pDecodedSamples < pDecodedSamplesEnd) { + __m128i zeroCountPart128; + __m128i riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; + } + zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); + riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); + riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); + riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + for (i = 0; i < 4; i += 1) { + prediction128 = _mm_xor_si128(prediction128, prediction128); + switch (order) + { + case 12: + case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); + case 10: + case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); + case 8: + case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); + case 6: + case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); + case 4: + case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); + case 2: + case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); + } + prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); + prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); + prediction128 = _mm_add_epi32(riceParamPart128, prediction128); + samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); + samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); + samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); + riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); + } + _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; + } + riceParamParts0 &= riceParamMask; + riceParamParts0 |= (zeroCountParts0 << riceParam); + riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) +{ + vst1q_s32(p+0, x.val[0]); + vst1q_s32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) +{ + vst1q_u32(p+0, x.val[0]); + vst1q_u32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) +{ + vst1q_f32(p+0, x.val[0]); + vst1q_f32(p+4, x.val[1]); +} +static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) +{ + vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); +} +static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) +{ + vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); +} +static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) +{ + ma_int32 x[4]; + x[3] = x3; + x[2] = x2; + x[1] = x1; + x[0] = x0; + return vld1q_s32(x); +} +static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) +{ + return vextq_s32(b, a, 1); +} +static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +{ + return vextq_u32(b, a, 1); +} +static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) +{ + int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); + return vpadd_s32(r, r); +} +static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) +{ + return vadd_s64(vget_high_s64(x), vget_low_s64(x)); +} +static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) +{ + return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); +} +static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) +{ + return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); +} +static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) +{ + return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int32x2_t shift64; + uint32x4_t one128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s32(-shift); + one128 = vdupq_n_u32(1); + { + int runningOrder = order; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; + } + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; + } + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; + } + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + } + while (pDecodedSamples < pDecodedSamplesEnd) { + int32x4_t prediction128; + int32x2_t prediction64; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; + } + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + if (order <= 4) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else if (order <= 8) { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } else { + for (i = 0; i < 4; i += 1) { + prediction128 = vmulq_s32(coefficients128_8, samples128_8); + prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); + prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); + prediction64 = vshl_s32(prediction64, shift64); + prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + } + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; + } + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + int i; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; + int32x4_t coefficients128_0; + int32x4_t coefficients128_4; + int32x4_t coefficients128_8; + int32x4_t samples128_0; + int32x4_t samples128_4; + int32x4_t samples128_8; + uint32x4_t riceParamMask128; + int32x4_t riceParam128; + int64x1_t shift64; + uint32x4_t one128; + int64x2_t prediction128 = { 0 }; + uint32x4_t zeroCountPart128; + uint32x4_t riceParamPart128; + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); + riceParamMask128 = vdupq_n_u32(riceParamMask); + riceParam128 = vdupq_n_s32(riceParam); + shift64 = vdup_n_s64(-shift); + one128 = vdupq_n_u32(1); + { + int runningOrder = order; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; + if (runningOrder >= 4) { + coefficients128_0 = vld1q_s32(coefficients + 0); + samples128_0 = vld1q_s32(pSamplesOut - 4); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; + case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; + case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; + } + coefficients128_0 = vld1q_s32(tempC); + samples128_0 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder >= 4) { + coefficients128_4 = vld1q_s32(coefficients + 4); + samples128_4 = vld1q_s32(pSamplesOut - 8); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; + case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; + case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; + } + coefficients128_4 = vld1q_s32(tempC); + samples128_4 = vld1q_s32(tempS); + runningOrder = 0; + } + if (runningOrder == 4) { + coefficients128_8 = vld1q_s32(coefficients + 8); + samples128_8 = vld1q_s32(pSamplesOut - 12); + runningOrder -= 4; + } else { + switch (runningOrder) { + case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; + case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; + case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; + } + coefficients128_8 = vld1q_s32(tempC); + samples128_8 = vld1q_s32(tempS); + runningOrder = 0; + } + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); + } + while (pDecodedSamples < pDecodedSamplesEnd) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; + } + zeroCountPart128 = vld1q_u32(zeroCountParts); + riceParamPart128 = vld1q_u32(riceParamParts); + riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); + riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + for (i = 0; i < 4; i += 1) { + int64x1_t prediction64; + prediction128 = veorq_s64(prediction128, prediction128); + switch (order) + { + case 12: + case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); + case 10: + case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); + case 8: + case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); + case 6: + case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); + case 4: + case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); + case 2: + case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); + } + prediction64 = ma_dr_flac__vhaddq_s64(prediction128); + prediction64 = vshl_s64(prediction64, shift64); + prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + } + vst1q_s32(pDecodedSamples, samples128_0); + pDecodedSamples += 4; + } + i = (count & ~3); + while (i < (int)count) { + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; + } + riceParamParts[0] &= riceParamMask; + riceParamParts[0] |= (zeroCountParts[0] << riceParam); + riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + i += 1; + pDecodedSamples += 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + if (lpcOrder > 0 && lpcOrder <= 12) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } else { + return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + } + } else { + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } +} +#endif +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + if (ma_dr_flac__gIsSSE41Supported) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported) { + return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + } else +#endif + { + #if 0 + return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #else + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + #endif + } +} +static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + for (i = 0; i < count; ++i) { + if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { + return MA_FALSE; + } + } + return MA_TRUE; +} +#if defined(__clang__) +__attribute__((no_sanitize("signed-integer-overflow"))) +#endif +static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) +{ + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); + for (i = 0; i < count; ++i) { + if (unencodedBitsPerSample > 0) { + if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return MA_FALSE; + } + } else { + pSamplesOut[i] = 0; + } + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) +{ + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; + } + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; + } + pDecodedSamples += lpcOrder; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; + } + if (partitionOrder > 8) { + return MA_FALSE; + } + if ((blockSize / (1 << partitionOrder)) < lpcOrder) { + return MA_FALSE; + } + samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; + partitionsRemaining = (1 << partitionOrder); + for (;;) { + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + if (riceParam != 0xFF) { + if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + } else { + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; + } + if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + } + pDecodedSamples += samplesInPartition; + if (partitionsRemaining == 1) { + break; + } + partitionsRemaining -= 1; + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) +{ + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; + } + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; + } + if (partitionOrder > 8) { + return MA_FALSE; + } + if ((blockSize / (1 << partitionOrder)) <= order) { + return MA_FALSE; + } + samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + partitionsRemaining = (1 << partitionOrder); + for (;;) + { + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 15) { + riceParam = 0xFF; + } + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; + } + if (riceParam == 31) { + riceParam = 0xFF; + } + } + if (riceParam != 0xFF) { + if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return MA_FALSE; + } + } else { + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; + } + if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return MA_FALSE; + } + } + if (partitionsRemaining == 1) { + break; + } + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + for (i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + for (i = 0; i < blockSize; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +{ + ma_uint32 i; + static ma_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + for (i = 0; i < lpcOrder; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) +{ + ma_uint8 i; + ma_uint8 lpcPrecision; + ma_int8 lpcShift; + ma_int32 coefficients[32]; + for (i = 0; i < lpcOrder; ++i) { + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { + return MA_FALSE; + } + pDecodedSamples[i] = sample; + } + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; + } + if (lpcPrecision == 15) { + return MA_FALSE; + } + lpcPrecision += 1; + if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { + return MA_FALSE; + } + if (lpcShift < 0) { + return MA_FALSE; + } + MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + for (i = 0; i < lpcOrder; ++i) { + if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { + return MA_FALSE; + } + } + if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) +{ + const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(header != NULL); + for (;;) { + ma_uint8 crc8 = 0xCE; + ma_uint8 reserved = 0; + ma_uint8 blockingStrategy = 0; + ma_uint8 blockSize = 0; + ma_uint8 sampleRate = 0; + ma_uint8 channelAssignment = 0; + ma_uint8 bitsPerSample = 0; + ma_bool32 isVariableBlockSize; + if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); + if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { + return MA_FALSE; + } + if (blockSize == 0) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { + return MA_FALSE; + } + if (channelAssignment > 10) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); + if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { + return MA_FALSE; + } + if (bitsPerSample == 3 || bitsPerSample == 7) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; + } + if (reserved == 1) { + continue; + } + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + ma_uint64 pcmFrameNumber; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = 0; + header->pcmFrameNumber = pcmFrameNumber; + } else { + ma_uint64 flacFrameNumber = 0; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; + } else { + continue; + } + } + header->flacFrameNumber = (ma_uint32)flacFrameNumber; + header->pcmFrameNumber = 0; + } + MA_DR_FLAC_ASSERT(blockSize > 0); + if (blockSize == 1) { + header->blockSizeInPCMFrames = 192; + } else if (blockSize <= 5) { + MA_DR_FLAC_ASSERT(blockSize >= 2); + header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); + header->blockSizeInPCMFrames += 1; + } else if (blockSize == 7) { + if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); + if (header->blockSizeInPCMFrames == 0xFFFF) { + return MA_FALSE; + } + header->blockSizeInPCMFrames += 1; + } else { + MA_DR_FLAC_ASSERT(blockSize >= 8); + header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); + } + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; + } + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; + } + header->channelAssignment = channelAssignment; + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + if (header->bitsPerSample != streaminfoBitsPerSample) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { + return MA_FALSE; + } +#ifndef MA_DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; + } +#endif + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) +{ + ma_uint8 header; + int type; + if (!ma_dr_flac__read_uint8(bs, 8, &header)) { + return MA_FALSE; + } + if ((header & 0x80) != 0) { + return MA_FALSE; + } + type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (ma_uint8)(type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; + } + } + if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { + return MA_FALSE; + } + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return MA_FALSE; + } + pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) +{ + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); + pSubframe = frame->subframes + subframeIndex; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; + } + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + if (subframeBitsPerSample > 32) { + return MA_FALSE; + } + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return MA_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pSamplesS32 = pDecodedSamplesOut; + switch (pSubframe->subframeType) + { + case MA_DR_FLAC_SUBFRAME_CONSTANT: + { + ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_VERBATIM: + { + ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_FIXED: + { + ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + case MA_DR_FLAC_SUBFRAME_LPC: + { + ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + } break; + default: return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) +{ + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); + pSubframe = frame->subframes + subframeIndex; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; + } + subframeBitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + subframeBitsPerSample += 1; + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + subframeBitsPerSample += 1; + } + if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { + return MA_FALSE; + } + subframeBitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pSamplesS32 = NULL; + switch (pSubframe->subframeType) + { + case MA_DR_FLAC_SUBFRAME_CONSTANT: + { + if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; + } + } break; + case MA_DR_FLAC_SUBFRAME_LPC: + { + ma_uint8 lpcPrecision; + unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; + } + if (lpcPrecision == 15) { + return MA_FALSE; + } + lpcPrecision += 1; + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; + } + } break; + default: return MA_FALSE; + } + return MA_TRUE; +} +static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) +{ + ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + MA_DR_FLAC_ASSERT(channelAssignment <= 10); + return lookup[channelAssignment]; +} +static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) +{ + int channelCount; + int i; + ma_uint8 paddingSizeInBits; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; +#endif + MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { + return MA_ERROR; + } + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return MA_ERROR; + } + for (i = 0; i < channelCount; ++i) { + if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return MA_ERROR; + } + } + paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + if (paddingSizeInBits > 0) { + ma_uint8 padding = 0; + if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return MA_AT_END; + } + } +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#endif + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; + } +#ifndef MA_DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return MA_CRC_MISMATCH; + } +#endif + pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + return MA_SUCCESS; +} +static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) +{ + int channelCount; + int i; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; +#endif + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + for (i = 0; i < channelCount; ++i) { + if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return MA_ERROR; + } + } + if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return MA_ERROR; + } +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); +#endif + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; + } +#ifndef MA_DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return MA_CRC_MISMATCH; + } +#endif + return MA_SUCCESS; +} +static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + for (;;) { + ma_result result; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + result = ma_dr_flac__decode_flac_frame(pFlac); + if (result != MA_SUCCESS) { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + return MA_TRUE; + } +} +static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) +{ + ma_uint64 firstPCMFrame; + ma_uint64 lastPCMFrame; + MA_DR_FLAC_ASSERT(pFlac != NULL); + firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; + if (firstPCMFrame == 0) { + firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + } + lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + if (lastPCMFrame > 0) { + lastPCMFrame -= 1; + } + if (pFirstPCMFrame) { + *pFirstPCMFrame = firstPCMFrame; + } + if (pLastPCMFrame) { + *pLastPCMFrame = lastPCMFrame; + } +} +static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) +{ + ma_bool32 result; + MA_DR_FLAC_ASSERT(pFlac != NULL); + result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + pFlac->currentPCMFrame = 0; + return result; +} +static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + return ma_dr_flac__seek_flac_frame(pFlac); +} +static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) +{ + ma_uint64 pcmFramesRead = 0; + while (pcmFramesToSeek > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { + pcmFramesRead += pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; + pcmFramesToSeek = 0; + } else { + pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; + pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + } + } + } + pFlac->currentPCMFrame += pcmFramesRead; + return pcmFramesRead; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(pFlac != NULL); + if (pcmFrameIndex >= pFlac->currentPCMFrame) { + runningPCMFrameCount = pFlac->currentPCMFrame; + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } else { + isMidFrame = MA_TRUE; + } + } else { + runningPCMFrameCount = 0; + if (!ma_dr_flac__seek_to_first_frame(pFlac)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } + for (;;) { + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + if (!isMidFrame) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + if (!isMidFrame) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = MA_FALSE; + } + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return MA_TRUE; + } + } + next_iteration: + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } +} +#if !defined(MA_DR_FLAC_NO_CRC) +#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f +static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + MA_DR_FLAC_ASSERT(targetByte >= rangeLo); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; + for (;;) { + ma_uint64 lastTargetByte = targetByte; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { + if (targetByte == 0) { + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; + } + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); +#if 1 + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#else + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + targetByte = rangeLo + ((rangeHi - rangeLo)/2); + rangeHi = targetByte; + } else { + break; + } +#endif + } + if(targetByte == lastTargetByte) { + return MA_FALSE; + } + } + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); + *pLastSuccessfulSeekOffset = targetByte; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) +{ +#if 0 + if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { + if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { + return MA_FALSE; + } + } +#endif + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) +{ + ma_uint64 targetByte; + ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + ma_uint64 pcmRangeHi = 0; + ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; + ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + for (;;) { + if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + ma_uint64 newPCMRangeLo; + ma_uint64 newPCMRangeHi; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + if (pcmRangeLo == newPCMRangeLo) { + if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + break; + } + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; + } else { + break; + } + } + pcmRangeLo = newPCMRangeLo; + pcmRangeHi = newPCMRangeHi; + if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return MA_TRUE; + } else { + break; + } + } else { + const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + if (pcmRangeLo > pcmFrameIndex) { + byteRangeHi = lastSuccessfulSeekOffset; + if (byteRangeLo > byteRangeHi) { + byteRangeLo = byteRangeHi; + } + targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); + if (targetByte < byteRangeLo) { + targetByte = byteRangeLo; + } + } else { + if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; + } else { + break; + } + } else { + byteRangeLo = lastSuccessfulSeekOffset; + if (byteRangeHi < byteRangeLo) { + byteRangeHi = byteRangeLo; + } + targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + if (targetByte > byteRangeHi) { + targetByte = byteRangeHi; + } + if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { + closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; + } + } + } + } + } else { + break; + } + } + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; +} +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { + return MA_FALSE; + } + if (pcmFrameIndex < seekForwardThreshold) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + } + byteRangeLo = pFlac->firstFLACFramePosInBytes; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); +} +#endif +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_uint32 iClosestSeekpoint = 0; + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + ma_uint32 iSeekpoint; + MA_DR_FLAC_ASSERT(pFlac != NULL); + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { + return MA_FALSE; + } + for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { + break; + } + iClosestSeekpoint = iSeekpoint; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { + return MA_FALSE; + } +#if !defined(MA_DR_FLAC_NO_CRC) + if (pFlac->totalPCMFrameCount > 0) { + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; + if (iClosestSeekpoint < pFlac->seekpointCount-1) { + ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { + return MA_FALSE; + } + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { + byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; + } + } + if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return MA_TRUE; + } + } + } + } +#endif + if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { + runningPCMFrameCount = pFlac->currentPCMFrame; + if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } else { + isMidFrame = MA_TRUE; + } + } else { + runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return MA_FALSE; + } + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } + for (;;) { + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + if (!isMidFrame) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } + } else { + if (!isMidFrame) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFLACFrame; + } else { + if (result == MA_CRC_MISMATCH) { + goto next_iteration; + } else { + return MA_FALSE; + } + } + } else { + runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + isMidFrame = MA_FALSE; + } + if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { + return MA_TRUE; + } + } + next_iteration: + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + } +} +#ifndef MA_DR_FLAC_NO_OGG +typedef struct +{ + ma_uint8 capturePattern[4]; + ma_uint8 structureVersion; + ma_uint8 headerType; + ma_uint64 granulePosition; + ma_uint32 serialNumber; + ma_uint32 sequenceNumber; + ma_uint32 checksum; + ma_uint8 segmentCount; + ma_uint8 segmentTable[255]; +} ma_dr_flac_ogg_page_header; +#endif +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + ma_dr_flac_meta_proc onMeta; + ma_dr_flac_container container; + void* pUserData; + void* pUserDataMD; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 runningFilePos; + ma_bool32 hasStreamInfoBlock; + ma_bool32 hasMetadataBlocks; + ma_dr_flac_bs bs; + ma_dr_flac_frame_header firstFrameHeader; +#ifndef MA_DR_FLAC_NO_OGG + ma_uint32 oggSerial; + ma_uint64 oggFirstBytePos; + ma_dr_flac_ogg_page_header oggBosHeader; +#endif +} ma_dr_flac_init_info; +static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) +{ + blockHeader = ma_dr_flac__be2host_32(blockHeader); + *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); + *blockSize = (blockHeader & 0x00FFFFFFUL); +} +static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) +{ + ma_uint32 blockHeader; + *blockSize = 0; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return MA_FALSE; + } + ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) +{ + ma_uint32 blockSizes; + ma_uint64 frameSizes = 0; + ma_uint64 importantProps; + ma_uint8 md5[16]; + if (onRead(pUserData, &blockSizes, 4) != 4) { + return MA_FALSE; + } + if (onRead(pUserData, &frameSizes, 6) != 6) { + return MA_FALSE; + } + if (onRead(pUserData, &importantProps, 8) != 8) { + return MA_FALSE; + } + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return MA_FALSE; + } + blockSizes = ma_dr_flac__be2host_32(blockSizes); + frameSizes = ma_dr_flac__be2host_64(frameSizes); + importantProps = ma_dr_flac__be2host_64(importantProps); + pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + return MA_TRUE; +} +static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_FLAC_MALLOC(sz); +} +static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_FLAC_REALLOC(p, sz); +} +static void ma_dr_flac__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_FLAC_FREE(p); +} +static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_uint64 runningFilePos = 42; + ma_uint64 seektablePos = 0; + ma_uint32 seektableSize = 0; + for (;;) { + ma_dr_flac_metadata metadata; + ma_uint8 isLastBlock = 0; + ma_uint8 blockType = 0; + ma_uint32 blockSize; + if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { + return MA_FALSE; + } + runningFilePos += 4; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + switch (blockType) + { + case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (blockSize < 4) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); + metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + if (onMeta) { + ma_uint32 seekpointCount; + ma_uint32 iSeekpoint; + void* pRawData; + seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { + ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = seekpointCount; + metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (blockSize < 8) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + ma_uint32 i; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.vorbis_comment.pComments = pRunningData; + for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { + ma_uint32 commentLength; + if (pRunningDataEnd - pRunningData < 4) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += commentLength; + } + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (blockSize < 396) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + size_t bufferSize; + ma_uint8 iTrack; + ma_uint8 iIndex; + void* pTrackData; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; + metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = NULL; + { + const char* pRunningDataSaved = pRunningData; + bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + ma_uint8 indexCount; + ma_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += 35; + indexCount = pRunningData[0]; + pRunningData += 1; + bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningData += indexPointSize; + } + pRunningData = pRunningDataSaved; + } + { + char* pRunningTrackData; + pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + if (pTrackData == NULL) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + pRunningTrackData = (char*)pTrackData; + for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { + ma_uint8 indexCount; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + indexCount = pRunningData[0]; + pRunningData += 1; + pRunningTrackData += 1; + for (iIndex = 0; iIndex < indexCount; ++iIndex) { + ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); + pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); + } + } + metadata.data.cuesheet.pTrackData = pTrackData; + } + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + pRawData = NULL; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); + pTrackData = NULL; + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (blockSize < 32) { + return MA_FALSE; + } + if (onMeta) { + void* pRawData; + const char* pRunningData; + const char* pRunningDataEnd; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pRunningData = (const char*)pRawData; + pRunningDataEnd = (const char*)pRawData + blockSize; + metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; + metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; + if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (onMeta) { + metadata.data.padding.unused = 0; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } else { + onMeta(pUserDataMD, &metadata); + } + } + } break; + case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: + { + if (onMeta) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } + } + } break; + default: + { + if (onMeta) { + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + if (pRawData == NULL) { + return MA_FALSE; + } + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; + } + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + onMeta(pUserDataMD, &metadata); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + } + } break; + } + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; + } + } + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + *pSeektablePos = seektablePos; + *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + *pFirstFramePos = runningFilePos; + return MA_TRUE; +} +static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +{ + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + (void)onSeek; + pInit->container = ma_dr_flac_container_native; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; + } + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + return MA_FALSE; + } else { + pInit->hasStreamInfoBlock = MA_FALSE; + pInit->hasMetadataBlocks = MA_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return MA_FALSE; + } + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return MA_FALSE; + } + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSizeInPCMFrames = 65535; + return MA_TRUE; + } + } else { + ma_dr_flac_streaminfo streaminfo; + if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return MA_FALSE; + } + pInit->hasStreamInfoBlock = MA_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + if (onMeta) { + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + return MA_TRUE; + } +} +#ifndef MA_DR_FLAC_NO_OGG +#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 +#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 +typedef enum +{ + ma_dr_flac_ogg_recover_on_crc_mismatch, + ma_dr_flac_ogg_fail_on_crc_mismatch +} ma_dr_flac_ogg_crc_mismatch_recovery; +#ifndef MA_DR_FLAC_NO_CRC +static ma_uint32 ma_dr_flac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; +#endif +static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) +{ +#ifndef MA_DR_FLAC_NO_CRC + return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} +#if 0 +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) +{ + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); + return crc32; +} +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) +{ + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif +static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) +{ + ma_uint32 i; + for (i = 0; i < dataSize; ++i) { + crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); + } + return crc32; +} +static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) +{ + ma_uint32 pageBodySize = 0; + int i; + for (i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + return pageBodySize; +} +static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +{ + ma_uint8 data[23]; + ma_uint32 i; + MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); + if (onRead(pUserData, data, 23) != 23) { + return MA_AT_END; + } + *pBytesRead += 23; + pHeader->capturePattern[0] = 'O'; + pHeader->capturePattern[1] = 'g'; + pHeader->capturePattern[2] = 'g'; + pHeader->capturePattern[3] = 'S'; + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + for (i = 0; i < 23; ++i) { + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); + } + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return MA_AT_END; + } + *pBytesRead += pHeader->segmentCount; + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + return MA_SUCCESS; +} +static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) +{ + ma_uint8 id[4]; + *pBytesRead = 0; + if (onRead(pUserData, id, 4) != 4) { + return MA_AT_END; + } + *pBytesRead += 4; + for (;;) { + if (ma_dr_flac_ogg__is_capture_pattern(id)) { + ma_result result; + *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return MA_AT_END; + } + *pBytesRead += 1; + } + } +} +typedef struct +{ + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + void* pUserData; + ma_uint64 currentBytePos; + ma_uint64 firstBytePos; + ma_uint32 serialNumber; + ma_dr_flac_ogg_page_header bosPageHeader; + ma_dr_flac_ogg_page_header currentPageHeader; + ma_uint32 bytesRemainingInPage; + ma_uint32 pageDataSize; + ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; +} ma_dr_flac_oggbs; +static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + return bytesActuallyRead; +} +static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) +{ + if (origin == ma_dr_flac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + oggbs->currentBytePos = offset; + return MA_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + oggbs->currentBytePos = offset; + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + oggbs->currentBytePos += offset; + return MA_TRUE; + } +} +static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) +{ + ma_dr_flac_ogg_page_header header; + for (;;) { + ma_uint32 crc32 = 0; + ma_uint32 bytesRead; + ma_uint32 pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint32 actualCRC32; +#endif + if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + oggbs->currentBytePos += bytesRead; + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { + continue; + } + if (header.serialNumber != oggbs->serialNumber) { + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + continue; + } + if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return MA_FALSE; + } + oggbs->pageDataSize = pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { + continue; + } else { + ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); + return MA_FALSE; + } + } +#else + (void)recoveryMethod; +#endif + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return MA_TRUE; + } +} +#if 0 +static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) +{ + ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + ma_uint8 iSeg = 0; + ma_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) +{ + for (;;) { + ma_bool32 atEndOfPage = MA_FALSE; + ma_uint8 bytesRemainingInSeg; + ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = MA_TRUE; + } + break; + } + bytesToEndOfPacketOrPage += segmentSize; + } + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + if (atEndOfPage) { + if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { + return MA_FALSE; + } + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return MA_TRUE; + } + } else { + return MA_TRUE; + } + } +} +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) +{ + return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); +} +#endif +static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; + size_t bytesRead = 0; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + break; + } + } + return bytesRead; +} +static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + int bytesSeeked = 0; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (origin == ma_dr_flac_seek_origin_start) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; + } + return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); + } + MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + (void)bytesSeeked; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; + } + } + return MA_TRUE; +} +static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + ma_uint64 originalBytePos; + ma_uint64 runningGranulePosition; + ma_uint64 runningFrameBytePos; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(oggbs != NULL); + originalBytePos = oggbs->currentBytePos; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return MA_FALSE; + } + oggbs->bytesRemainingInPage = 0; + runningGranulePosition = 0; + for (;;) { + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); + return MA_FALSE; + } + runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { + break; + } + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + ma_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { + runningGranulePosition = oggbs->currentPageHeader.granulePosition; + } + continue; + } + } + } + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; + } + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + return MA_FALSE; + } + runningPCMFrameCount = runningGranulePosition; + for (;;) { + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_uint64 pcmFrameCountInThisFrame; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; + } + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; + if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + pFlac->currentPCMFrame = pcmFrameIndex; + pFlac->currentFLACFrame.pcmFramesRemaining = 0; + return MA_TRUE; + } else { + return MA_FALSE; + } + } + if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); + if (pcmFramesToDecode == 0) { + return MA_TRUE; + } + pFlac->currentPCMFrame = runningPCMFrameCount; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + } else { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { + runningPCMFrameCount += pcmFrameCountInThisFrame; + } else { + if (result == MA_CRC_MISMATCH) { + continue; + } else { + return MA_FALSE; + } + } + } + } +} +static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) +{ + ma_dr_flac_ogg_page_header header; + ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + ma_uint32 bytesRead = 0; + (void)relaxed; + pInit->container = ma_dr_flac_container_ogg; + pInit->oggFirstBytePos = 0; + if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + pInit->runningFilePos += bytesRead; + for (;;) { + int pageBodySize; + if ((header.headerType & 0x02) == 0) { + return MA_FALSE; + } + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { + ma_uint32 bytesRemainingInPage = pageBodySize; + ma_uint8 packetType; + if (onRead(pUserData, &packetType, 1) != 1) { + return MA_FALSE; + } + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + ma_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return MA_FALSE; + } + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + ma_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return MA_FALSE; + } + if (mappingVersion[0] != 1) { + return MA_FALSE; + } + if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + if (onRead(pUserData, sig, 4) != 4) { + return MA_FALSE; + } + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + ma_dr_flac_streaminfo streaminfo; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; + } + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return MA_FALSE; + } + if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + pInit->hasStreamInfoBlock = MA_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; + pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; + pInit->hasMetadataBlocks = !isLastBlock; + if (onMeta) { + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + metadata.data.streaminfo = streaminfo; + onMeta(pUserDataMD, &metadata); + } + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; + pInit->oggSerial = header.serialNumber; + pInit->oggBosHeader = header; + break; + } else { + return MA_FALSE; + } + } else { + return MA_FALSE; + } + } else { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + } + pInit->runningFilePos += pageBodySize; + if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; + } + pInit->runningFilePos += bytesRead; + } + pInit->hasMetadataBlocks = MA_TRUE; + return MA_TRUE; +} +#endif +static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) +{ + ma_bool32 relaxed; + ma_uint8 id[4]; + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return MA_FALSE; + } + MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + ma_dr_flac__reset_cache(&pInit->bs); + relaxed = container != ma_dr_flac_container_unknown; + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return MA_FALSE; + } + pInit->runningFilePos += 4; + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + ma_uint8 header[6]; + ma_uint8 flags; + ma_uint32 headerSize; + if (onRead(pUserData, header, 6) != 6) { + return MA_FALSE; + } + pInit->runningFilePos += 6; + flags = header[1]; + MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef MA_DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + if (relaxed) { + if (container == ma_dr_flac_container_native) { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef MA_DR_FLAC_NO_OGG + if (container == ma_dr_flac_container_ogg) { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + return MA_FALSE; +} +static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) +{ + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pInit != NULL); + MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + pFlac->bs = pInit->bs; + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (ma_uint8)pInit->channels; + pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; + pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; + pFlac->container = pInit->container; +} +static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac_init_info init; + ma_uint32 allocationSize; + ma_uint32 wholeSIMDVectorCountPerChannel; + ma_uint32 decodedSamplesAllocationSize; +#ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac_oggbs* pOggbs = NULL; +#endif + ma_uint64 firstFramePos; + ma_uint64 seektablePos; + ma_uint32 seekpointCount; + ma_allocation_callbacks allocationCallbacks; + ma_dr_flac* pFlac; + ma_dr_flac__init_cpu_caps(); + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + if (pAllocationCallbacks != NULL) { + allocationCallbacks = *pAllocationCallbacks; + if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { + return NULL; + } + } else { + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; + allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; + allocationCallbacks.onFree = ma_dr_flac__free_default; + } + allocationSize = sizeof(ma_dr_flac); + if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; + } + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + allocationSize += decodedSamplesAllocationSize; + allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + allocationSize += sizeof(ma_dr_flac_oggbs); + pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + if (pOggbs == NULL) { + return NULL; + } + MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + pOggbs->onRead = onRead; + pOggbs->onSeek = onSeek; + pOggbs->pUserData = pUserData; + pOggbs->currentBytePos = init.oggFirstBytePos; + pOggbs->firstBytePos = init.oggFirstBytePos; + pOggbs->serialNumber = init.oggSerial; + pOggbs->bosPageHeader = init.oggBosHeader; + pOggbs->bytesRemainingInPage = 0; + } +#endif + firstFramePos = 42; + seektablePos = 0; + seekpointCount = 0; + if (init.hasMetadataBlocks) { + ma_dr_flac_read_proc onReadOverride = onRead; + ma_dr_flac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + onReadOverride = ma_dr_flac__on_read_ogg; + onSeekOverride = ma_dr_flac__on_seek_ogg; + pUserDataOverride = (void*)pOggbs; + } +#endif + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); + } + pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + if (pFlac == NULL) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + #endif + return NULL; + } + ma_dr_flac__init_from_info(pFlac, &init); + pFlac->allocationCallbacks = allocationCallbacks; + pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); + MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); + pOggbs = NULL; + pFlac->bs.onRead = ma_dr_flac__on_read_ogg; + pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; + } +#endif + pFlac->firstFLACFramePosInBytes = firstFramePos; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + if (seektablePos != 0) { + pFlac->seekpointCount = seekpointCount; + pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); + MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { + ma_uint32 iSeekpoint; + for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + break; + } + } + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } else { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + } + } + if (!init.hasStreamInfoBlock) { + pFlac->currentFLACFrame.header = init.firstFrameHeader; + for (;;) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + break; + } else { + if (result == MA_CRC_MISMATCH) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + continue; + } else { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); + return NULL; + } + } + } + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_STDIO +#include +#ifndef MA_DR_FLAC_NO_WCHAR +#include +#endif +static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} +static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + MA_DR_FLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return NULL; + } + return pFlac; +} +#endif +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + return pFlac; +} +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + FILE* pFile; + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return NULL; + } + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + fclose(pFile); + return pFlac; + } + return pFlac; +} +#endif +#endif +static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + size_t bytesRemaining; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + return bytesToRead; +} +static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) +{ + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (offset > (ma_int64)memoryStream->dataSize) { + return MA_FALSE; + } + if (origin == ma_dr_flac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + return MA_FALSE; + } + } else { + if ((ma_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + return MA_FALSE; + } + } + return MA_TRUE; +} +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + pFlac->memoryStream = memoryStream; +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + return pFlac; +} +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + pFlac->memoryStream = memoryStream; +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + return pFlac; +} +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); +} +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); +} +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) +{ + if (pFlac == NULL) { + return; + } +#ifndef MA_DR_FLAC_NO_STDIO + if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { + fclose((FILE*)pFlac->bs.pUserData); + } +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); + if (oggbs->onRead == ma_dr_flac__on_read_stdio) { + fclose((FILE*)oggbs->pUserData); + } + } +#endif +#endif + ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; + int32x4_t wbpsShift1_4; + uint32x4_t one4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + one4 = vdupq_n_u32(1); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; + } + } else { + int32x4_t shift4; + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0; + pOutputSamples[i*8+1] = (ma_int32)tempR0; + pOutputSamples[i*8+2] = (ma_int32)tempL1; + pOutputSamples[i*8+3] = (ma_int32)tempR1; + pOutputSamples[i*8+4] = (ma_int32)tempL2; + pOutputSamples[i*8+5] = (ma_int32)tempR2; + pOutputSamples[i*8+6] = (ma_int32)tempL3; + pOutputSamples[i*8+7] = (ma_int32)tempR3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift4_0 = vdupq_n_s32(shift0); + int32x4_t shift4_1 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + } + } + return framesRead; +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + left0 >>= 16; + left1 >>= 16; + left2 >>= 16; + left3 >>= 16; + right0 >>= 16; + right1 >>= 16; + right2 >>= 16; + right3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + left = vshrq_n_u32(left, 16); + right = vshrq_n_u32(right, 16); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + left >>= 16; + right >>= 16; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = ((ma_int32)(mid0 + side0) >> 1); + temp1L = ((ma_int32)(mid1 + side1) >> 1); + temp2L = ((ma_int32)(mid2 + side2) >> 1); + temp3L = ((ma_int32)(mid3 + side3) >> 1); + temp0R = ((ma_int32)(mid0 - side0) >> 1); + temp1R = ((ma_int32)(mid1 - side1) >> 1); + temp2R = ((ma_int32)(mid2 - side2) >> 1); + temp3R = ((ma_int32)(mid3 - side3) >> 1); + temp0L >>= 16; + temp1L >>= 16; + temp2L >>= 16; + temp3L >>= 16; + temp0R >>= 16; + temp1R >>= 16; + temp2R >>= 16; + temp3R >>= 16; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i left; + __m128i right; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + int32x4_t wbpsShift0_4; + int32x4_t wbpsShift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); + } + } else { + int32x4_t shift4; + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t left; + int32x4_t right; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + tempL0 >>= 16; + tempL1 >>= 16; + tempL2 >>= 16; + tempL3 >>= 16; + tempR0 >>= 16; + tempR1 >>= 16; + tempR2 >>= 16; + tempR3 >>= 16; + pOutputSamples[i*8+0] = (ma_int16)tempL0; + pOutputSamples[i*8+1] = (ma_int16)tempR0; + pOutputSamples[i*8+2] = (ma_int16)tempL1; + pOutputSamples[i*8+3] = (ma_int16)tempR1; + pOutputSamples[i*8+4] = (ma_int16)tempL2; + pOutputSamples[i*8+5] = (ma_int16)tempR2; + pOutputSamples[i*8+6] = (ma_int16)tempL3; + pOutputSamples[i*8+7] = (ma_int16)tempR3; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + left = _mm_srai_epi32(left, 16); + right = _mm_srai_epi32(right, 16); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t left; + int32x4_t right; + left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + left = vshrq_n_s32(left, 16); + right = vshrq_n_s32(right, 16); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; + } + } + return framesRead; +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = _mm_set1_ps(1.0f / 8388608.0f); + for (i = 0; i < frameCount4; ++i) { + __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i right = _mm_sub_epi32(left, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t left; + uint32x4_t side; + uint32x4_t right; + float32x4_t leftf; + float32x4_t rightf; + left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + right = vsubq_u32(left, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + for (i = 0; i < frameCount; ++i) { + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + __m128 factor; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = _mm_set1_ps(1.0f / 8388608.0f); + for (i = 0; i < frameCount4; ++i) { + __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + __m128i left = _mm_add_epi32(right, side); + __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); + __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float32x4_t factor4; + int32x4_t shift0_4; + int32x4_t shift1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor4 = vdupq_n_f32(1.0f / 8388608.0f); + shift0_4 = vdupq_n_s32(shift0); + shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t side; + uint32x4_t right; + uint32x4_t left; + float32x4_t leftf; + float32x4_t rightf; + side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); + right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); + left = vaddq_u32(right, side); + leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + float factor = 1 / 2147483648.0; + if (shift > 0) { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (mid0 + side0) << shift; + temp1L = (mid1 + side1) << shift; + temp2L = (mid2 + side2) << shift; + temp3L = (mid3 + side3) << shift; + temp0R = (mid0 - side0) << shift; + temp1R = (mid1 - side1) << shift; + temp2R = (mid2 - side2) << shift; + temp3R = (mid3 - side3) << shift; + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + } + } else { + for (i = 0; i < frameCount4; ++i) { + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid0 = (mid0 << 1) | (side0 & 0x01); + mid1 = (mid1 << 1) | (side1 & 0x01); + mid2 = (mid2 << 1) | (side2 & 0x01); + mid3 = (mid3 << 1) | (side3 & 0x01); + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; + } + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; + float factor; + __m128 factor128; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = 1.0f / 8388608.0f; + factor128 = _mm_set1_ps(factor); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); + tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + for (i = 0; i < frameCount4; ++i) { + __m128i mid; + __m128i side; + __m128i tempL; + __m128i tempR; + __m128 leftf; + __m128 rightf; + mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); + tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); + tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + } + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; + float factor; + float32x4_t factor4; + int32x4_t shift4; + int32x4_t wbps0_4; + int32x4_t wbps1_4; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); + factor = 1.0f / 8388608.0f; + factor4 = vdupq_n_f32(factor); + wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + if (shift == 0) { + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); + righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; + } + } else { + shift -= 1; + shift4 = vdupq_n_s32(shift); + for (i = 0; i < frameCount4; ++i) { + uint32x4_t mid; + uint32x4_t side; + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); + side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); + mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); + lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + mid = (mid << 1) | (side & 0x01); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; + } + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +#if 0 +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + float factor = 1 / 2147483648.0; + for (i = 0; i < frameCount4; ++i) { + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float factor = 1.0f / 8388608.0f; + __m128 factor128 = _mm_set1_ps(factor); + for (i = 0; i < frameCount4; ++i) { + __m128i lefti; + __m128i righti; + __m128 leftf; + __m128 rightf; + lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); + righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); + leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); + rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); + _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); + _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + float factor = 1.0f / 8388608.0f; + float32x4_t factor4 = vdupq_n_f32(factor); + int32x4_t shift0_4 = vdupq_n_s32(shift0); + int32x4_t shift1_4 = vdupq_n_s32(shift1); + for (i = 0; i < frameCount4; ++i) { + int32x4_t lefti; + int32x4_t righti; + float32x4_t leftf; + float32x4_t rightf; + lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); + righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); + leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); + rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + } + for (i = (frameCount4 << 2); i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; + } +} +#endif +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) +{ +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + } else +#endif + { +#if 0 + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#else + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#endif + } +} +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) +{ + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; + if (pFlac == NULL || framesToRead == 0) { + return 0; + } + if (pBufferOut == NULL) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); + } + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); + unusedBitsPerSample = 32 - pFlac->bitsPerSample; + framesRead = 0; + while (framesToRead > 0) { + if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { + break; + } + } else { + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; + if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { + frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; + } + if (channelCount == 2) { + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + switch (pFlac->currentFLACFrame.header.channelAssignment) + { + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + } break; + } + } else { + ma_uint64 i; + for (i = 0; i < frameCountThisIteration; ++i) { + unsigned int j; + for (j = 0; j < channelCount; ++j) { + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); + } + } + } + framesRead += frameCountThisIteration; + pBufferOut += frameCountThisIteration * channelCount; + framesToRead -= frameCountThisIteration; + pFlac->currentPCMFrame += frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; + } + } + return framesRead; +} +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) +{ + if (pFlac == NULL) { + return MA_FALSE; + } + if (pFlac->currentPCMFrame == pcmFrameIndex) { + return MA_TRUE; + } + if (pFlac->firstFLACFramePosInBytes == 0) { + return MA_FALSE; + } + if (pcmFrameIndex == 0) { + pFlac->currentPCMFrame = 0; + return ma_dr_flac__seek_to_first_frame(pFlac); + } else { + ma_bool32 wasSuccessful = MA_FALSE; + ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; + if (pcmFrameIndex > pFlac->totalPCMFrameCount) { + pcmFrameIndex = pFlac->totalPCMFrameCount; + } + if (pcmFrameIndex > pFlac->currentPCMFrame) { + ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { + pFlac->currentFLACFrame.pcmFramesRemaining -= offset; + pFlac->currentPCMFrame = pcmFrameIndex; + return MA_TRUE; + } + } else { + ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + if (currentFLACFramePCMFramesConsumed > offsetAbs) { + pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; + pFlac->currentPCMFrame = pcmFrameIndex; + return MA_TRUE; + } + } +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) + { + wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + } + else +#endif + { + if (!pFlac->_noSeekTableSeek) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + } +#if !defined(MA_DR_FLAC_NO_CRC) + if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + } +#endif + if (!wasSuccessful && !pFlac->_noBruteForceSeek) { + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + } + } + if (wasSuccessful) { + pFlac->currentPCMFrame = pcmFrameIndex; + } else { + if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { + ma_dr_flac_seek_to_pcm_frame(pFlac, 0); + } + } + return wasSuccessful; + } +} +#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ +{ \ + type* pSampleData = NULL; \ + ma_uint64 totalPCMFrameCount; \ + \ + MA_DR_FLAC_ASSERT(pFlac != NULL); \ + \ + totalPCMFrameCount = pFlac->totalPCMFrameCount; \ + \ + if (totalPCMFrameCount == 0) { \ + type buffer[4096]; \ + ma_uint64 pcmFramesRead; \ + size_t sampleDataBufferSize = sizeof(buffer); \ + \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ + type* pNewSampleData; \ + size_t newSampleDataBufferSize; \ + \ + newSampleDataBufferSize = sampleDataBufferSize * 2; \ + pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + if (pNewSampleData == NULL) { \ + ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + goto on_error; \ + } \ + \ + sampleDataBufferSize = newSampleDataBufferSize; \ + pSampleData = pNewSampleData; \ + } \ + \ + MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + totalPCMFrameCount += pcmFramesRead; \ + } \ + \ + \ + MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + } else { \ + ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ + goto on_error; \ + } \ + \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ + \ + ma_dr_flac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + ma_dr_flac_close(pFlac); \ + return NULL; \ +} +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (channelsOut) { + *channelsOut = 0; + } + if (sampleRateOut) { + *sampleRateOut = 0; + } + if (totalPCMFrameCountOut) { + *totalPCMFrameCountOut = 0; + } + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); +} +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +#endif +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_flac* pFlac; + if (sampleRate) { + *sampleRate = 0; + } + if (channels) { + *channels = 0; + } + if (totalPCMFrameCount) { + *totalPCMFrameCount = 0; + } + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); + if (pFlac == NULL) { + return NULL; + } + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); +} +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_flac__free_default(p, NULL); + } +} +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) +{ + if (pIter == NULL) { + return; + } + pIter->countRemaining = commentCount; + pIter->pRunningData = (const char*)pComments; +} +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) +{ + ma_int32 length; + const char* pComment; + if (pCommentLengthOut) { + *pCommentLengthOut = 0; + } + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); + pIter->pRunningData += 4; + pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + if (pCommentLengthOut) { + *pCommentLengthOut = length; + } + return pComment; +} +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) +{ + if (pIter == NULL) { + return; + } + pIter->countRemaining = trackCount; + pIter->pRunningData = (const char*)pTrackData; +} +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) +{ + ma_dr_flac_cuesheet_track cuesheetTrack; + const char* pRunningData; + ma_uint64 offsetHi; + ma_uint64 offsetLo; + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return MA_FALSE; + } + pRunningData = pIter->pRunningData; + offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + cuesheetTrack.offset = offsetLo | (offsetHi << 32); + cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; + MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; + cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; + cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; + cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + pIter->pRunningData = pRunningData; + pIter->countRemaining -= 1; + if (pCuesheetTrack) { + *pCuesheetTrack = cuesheetTrack; + } + return MA_TRUE; +} +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif +#endif +/* dr_flac_c end */ +#endif /* MA_DR_FLAC_IMPLEMENTATION */ +#endif /* MA_NO_FLAC */ + +#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) +#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +/* dr_mp3_c begin */ +#ifndef ma_dr_mp3_c +#define ma_dr_mp3_c +#include +#include +#include +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) +{ + if (pMajor) { + *pMajor = MA_DR_MP3_VERSION_MAJOR; + } + if (pMinor) { + *pMinor = MA_DR_MP3_VERSION_MINOR; + } + if (pRevision) { + *pRevision = MA_DR_MP3_VERSION_REVISION; + } +} +MA_API const char* ma_dr_mp3_version_string(void) +{ + return MA_DR_MP3_VERSION_STRING; +} +#if defined(__TINYC__) +#define MA_DR_MP3_NO_SIMD +#endif +#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) +#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 +#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES +#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 +#endif +#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE +#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 +#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 +#define MA_DR_MP3_STOP_BLOCK_TYPE 3 +#define MA_DR_MP3_MODE_MONO 3 +#define MA_DR_MP3_MODE_JOINT_STEREO 1 +#define MA_DR_MP3_HDR_SIZE 4 +#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) +#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 +#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) +#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) +#if !defined(MA_DR_MP3_NO_SIMD) +#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#define MA_DR_MP3_ONLY_SIMD +#endif +#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) +#if defined(_MSC_VER) +#include +#endif +#include +#define MA_DR_MP3_HAVE_SSE 1 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE _mm_storeu_ps +#define MA_DR_MP3_VLD _mm_loadu_ps +#define MA_DR_MP3_VSET _mm_set1_ps +#define MA_DR_MP3_VADD _mm_add_ps +#define MA_DR_MP3_VSUB _mm_sub_ps +#define MA_DR_MP3_VMUL _mm_mul_ps +#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 ma_dr_mp3_f4; +#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) +#define ma_dr_mp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int ma_dr_mp3_have_simd(void) +{ +#ifdef MA_DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif + if (g_have_simd) + goto end; + ma_dr_mp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + ma_dr_mp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; + return g_have_simd - 1; + } +end: + return g_have_simd - 1; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) +#include +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE vst1q_f32 +#define MA_DR_MP3_VLD vld1q_f32 +#define MA_DR_MP3_VSET vmovq_n_f32 +#define MA_DR_MP3_VADD vaddq_f32 +#define MA_DR_MP3_VSUB vsubq_f32 +#define MA_DR_MP3_VMUL vmulq_f32 +#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t ma_dr_mp3_f4; +static int ma_dr_mp3_have_simd(void) +{ + return 1; +} +#else +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 0 +#ifdef MA_DR_MP3_ONLY_SIMD +#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif +#else +#define MA_DR_MP3_HAVE_SIMD 0 +#endif +#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__) +#define MA_DR_MP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) +{ + ma_int32 x = 0; + __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); + return x; +} +#else +#define MA_DR_MP3_HAVE_ARMV6 0 +#endif +#ifndef MA_DR_MP3_ASSERT +#include +#define MA_DR_MP3_ASSERT(expression) assert(expression) +#endif +#ifndef MA_DR_MP3_COPY_MEMORY +#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef MA_DR_MP3_MOVE_MEMORY +#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#endif +#ifndef MA_DR_MP3_ZERO_MEMORY +#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_MP3_MALLOC +#define MA_DR_MP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef MA_DR_MP3_REALLOC +#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef MA_DR_MP3_FREE +#define MA_DR_MP3_FREE(p) free((p)) +#endif +typedef struct +{ + const ma_uint8 *buf; + int pos, limit; +} ma_dr_mp3_bs; +typedef struct +{ + float scf[3*64]; + ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} ma_dr_mp3_L12_scale_info; +typedef struct +{ + ma_uint8 tab_offset, code_tab_width, band_count; +} ma_dr_mp3_L12_subband_alloc; +typedef struct +{ + const ma_uint8 *sfbtab; + ma_uint16 part_23_length, big_values, scalefac_compress; + ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + ma_uint8 table_select[3], region_count[3], subblock_gain[3]; + ma_uint8 preflag, scalefac_scale, count1_table, scfsi; +} ma_dr_mp3_L3_gr_info; +typedef struct +{ + ma_dr_mp3_bs bs; + ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + ma_dr_mp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + ma_uint8 ist_pos[2][39]; +} ma_dr_mp3dec_scratch; +static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} +static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) +{ + ma_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const ma_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} +static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && + (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && + (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); +} +static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) +{ + return ma_dr_mp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); +} +static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) +{ + static const ma_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; +} +static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); +} +static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) +{ + return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); +} +static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) +{ + int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); + if (MA_DR_MP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; + } + return frame_bytes ? frame_bytes : free_format_size; +} +static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) +{ + return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} +#ifndef MA_DR_MP3_ONLY_MP3 +static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) +{ + const ma_dr_mp3_L12_subband_alloc *alloc; + int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); + if (!kbps) + { + kbps = 192; + } + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + sci->total_bands = (ma_uint8)nbands; + sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); + return alloc; +} +static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = ma_dr_mp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} +static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) +{ + static const ma_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); + int i, k = 0, ba_bits = 0; + const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; + for (i = 0; i < sci->total_bands; i++) + { + ma_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); + } + ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} +static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; + unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} +static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif +static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +{ + static const ma_uint8 g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const ma_uint8 g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const ma_uint8 g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); + scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + do + { + if (MA_DR_MP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (ma_dr_mp3_bs_get_bits(bs, 1)) + { + gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = ma_dr_mp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = ma_dr_mp3_bs_get_bits(bs, 15); + gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); + gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (ma_uint8)(tables >> 10); + gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); + gr->table_select[2] = (ma_uint8)((tables) & 31); + gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + return main_data_begin; +} +static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + MA_DR_MP3_ZERO_MEMORY(scf, cnt); + MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); + scf[k] = (ma_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} +static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = MA_DR_MP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} +static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) +{ + static const ma_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + ma_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); + } else + { + static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + } + } else if (gr->preflag) + { + static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); + } + } + gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} +static const float g_ma_dr_mp3_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 ma_dr_mp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + if (x < 129) + { + return g_ma_dr_mp3_pow43[16 + x]; + } + if (x < 1024) + { + mult = 16; + x <<= 3; + } + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} +static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const ma_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, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,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,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; +#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const ma_uint8 *sfb = gr_info->sfbtab; + const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const ma_int16 *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + if (linbits) + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + while (leaf < 0) + { + MA_DR_MP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + } + MA_DR_MP3_FLUSH_BITS(leaf >> 8); + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15) + { + lsb += MA_DR_MP3_PEEK_BITS(linbits); + MA_DR_MP3_FLUSH_BITS(linbits); + MA_DR_MP3_CHECK_BITS; + *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + } + MA_DR_MP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } else + { + do + { + np = *sfb++ / 2; + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; + while (leaf < 0) + { + MA_DR_MP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; + } + MA_DR_MP3_FLUSH_BITS(leaf >> 8); + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); + } + MA_DR_MP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + } + for (np = 1 - big_val_cnt;; dst += 4) + { + const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + MA_DR_MP3_FLUSH_BITS(leaf & 7); + if (MA_DR_MP3_BSPOS > layer3gr_limit) + { + break; + } +#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(0); + MA_DR_MP3_DEQ_COUNT1(1); + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(2); + MA_DR_MP3_DEQ_COUNT1(3); + MA_DR_MP3_CHECK_BITS; + } + bs->pos = layer3gr_limit; +} +static void ma_dr_mp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) + { + for (; i < n - 3; i += 4) + { + ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); + ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); + MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); + MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); + } +#ifdef __GNUC__ + if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) + return; +#endif + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} +static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} +static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + max_band[0] = max_band[1] = max_band[2] = -1; + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} +static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) + { + ma_dr_mp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} +static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} +static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); +} +static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) + { + ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); + ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); + ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); + ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); + vd = MA_DR_MP3_VREV(vd); + MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); + vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); + } +#endif +#ifndef MA_DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} +static void ma_dr_mp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} +static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + ma_dr_mp3_L3_dct3_9(co); + ma_dr_mp3_L3_dct3_9(si); + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + i = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) + { + ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); + ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); + ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); + ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); + ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); + ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); + ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); + ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); + MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); + MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); + vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} +static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} +static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} +static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} +static void ma_dr_mp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} +static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} +static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} +static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); + MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); + MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} +static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) +{ + int ch; + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) + { + ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) + { + ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); + } + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); + ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + ma_dr_mp3_L3_change_sign(s->grbuf[ch]); + } +} +static void ma_dr_mp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) + { + ma_dr_mp3_f4 t[4][8], *x; + float *y = grbuf + k; + for (x = t[0], i = 0; i < 8; i++, x++) + { + ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); + ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); + ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); + ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); + ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); + ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); + ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); + ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = MA_DR_MP3_VADD(t0, t1); + x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = MA_DR_MP3_VADD(t3, t2); + x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); + x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); + x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); + x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); + x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); + x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); + x[0] = MA_DR_MP3_VADD(x0, x1); + x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); + x5 = MA_DR_MP3_VADD(x5, x6); + x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); + x7 = MA_DR_MP3_VADD(x7, xt); + x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); + x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); + x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); + x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); + x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); + x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); + x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); + } + if (k > n - 3) + { +#if MA_DR_MP3_HAVE_SSE +#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE2(0, t[0][i]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); + } + MA_DR_MP3_VSAVE2(0, t[0][7]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE2(2, t[1][7]); + MA_DR_MP3_VSAVE2(3, t[3][7]); + } else + { +#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE4(0, t[0][i]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); + } + MA_DR_MP3_VSAVE4(0, t[0][7]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE4(2, t[1][7]); + MA_DR_MP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef MA_DR_MP3_ONLY_SIMD + {} +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} +#ifndef MA_DR_MP3_FLOAT_OUTPUT +typedef ma_int16 ma_dr_mp3d_sample_t; +static ma_int16 ma_dr_mp3d_scale_pcm(float sample) +{ + ma_int16 s; +#if MA_DR_MP3_HAVE_ARMV6 + ma_int32 s32 = (ma_int32)(sample + .5f); + s32 -= (s32 < 0); + s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); +#else + if (sample >= 32766.5) return (ma_int16) 32767; + if (sample <= -32767.5) return (ma_int16)-32768; + s = (ma_int16)(sample + .5f); + s -= (s < 0); +#endif + return s; +} +#else +typedef float ma_dr_mp3d_sample_t; +static float ma_dr_mp3d_scale_pcm(float sample) +{ + return sample*(1.f/32768.f); +} +#endif +static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = ma_dr_mp3d_scale_pcm(a); + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); +} +static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + ma_dr_mp3d_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, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); + ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } +#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } +#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } + ma_dr_mp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) + { +#ifndef MA_DR_MP3_FLOAT_OUTPUT +#if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const ma_dr_mp3_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] = (ma_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif +#else + #if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #else + const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + #endif + a = MA_DR_MP3_VMUL(a, g_scale); + b = MA_DR_MP3_VMUL(b, g_scale); +#if MA_DR_MP3_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 + } + } else +#endif +#ifdef MA_DR_MP3_ONLY_SIMD + {} +#else + for (i = 14; i >= 0; i--) + { +#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) + dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); + } +#endif +} +static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); + } + MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + for (i = 0; i < nbands; i += 2) + { + ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} +static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); + if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} +static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) + { + if (ma_dr_mp3_hdr_valid(mp3)) + { + int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); + for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) + { + if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - ma_dr_mp3_hdr_padding(mp3); + int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); + if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return mp3_bytes; +} +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) +{ + dec->header[0] = 0; +} +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const ma_uint8 *hdr; + ma_dr_mp3_bs bs_frame[1]; + ma_dr_mp3dec_scratch scratch; + if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) + { + frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); + i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + hdr = mp3 + i; + MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); + ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); + if (MA_DR_MP3_HDR_IS_CRC(hdr)) + { + ma_dr_mp3_bs_get_bits(bs_frame, 16); + } + if (info->layer == 3) + { + int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + ma_dr_mp3dec_init(dec); + return 0; + } + success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success && pcm != NULL) + { + for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) + { + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + } + } + ma_dr_mp3_L3_save_reservoir(dec, &scratch); + } else + { +#ifdef MA_DR_MP3_ONLY_MP3 + return 0; +#else + ma_dr_mp3_L12_scale_info sci[1]; + if (pcm == NULL) { + return ma_dr_mp3_hdr_frame_samples(hdr); + } + ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); + } + if (bs_frame->pos > bs_frame->limit) + { + ma_dr_mp3dec_init(dec); + return 0; + } + } +#endif + } + return success*ma_dr_mp3_hdr_frame_samples(dec->header); +} +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) +{ + size_t i = 0; +#if MA_DR_MP3_HAVE_SIMD + size_t aligned_count = num_samples & ~7; + for(; i < aligned_count; i+=8) + { + ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); + ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); + ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); +#if MA_DR_MP3_HAVE_SSE + ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); + ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); + out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); +#else + int16x4_t pcma, pcmb; + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_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] = (ma_int16) 32767; + else if (sample <= -32767.5) + out[i] = (ma_int16)-32768; + else + { + short s = (ma_int16)(sample + .5f); + s -= (s < 0); + out[i] = s; + } + } +} +#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES +#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 +#endif +#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 +#ifndef MA_DR_MP3_DATA_CHUNK_SIZE +#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) +#endif +#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) +#ifndef MA_DR_MP3_PI_D +#define MA_DR_MP3_PI_D 3.14159265358979323846264 +#endif +#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 +static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} +static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) +{ + float r0 = (y - x); + float r1 = r0*a; + return x + r1; +} +static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) +{ + for (;;) { + if (b == 0) { + break; + } else { + ma_uint32 t = a; + a = b; + b = t % a; + } + } + return a; +} +static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_MP3_MALLOC(sz); +} +static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) +{ + (void)pUserData; + return MA_DR_MP3_REALLOC(p, sz); +} +static void ma_dr_mp3__free_default(void* p, void* pUserData) +{ + (void)pUserData; + MA_DR_MP3_FREE(p); +} +static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onMalloc != NULL) { + return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); + } + return NULL; +} +static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks == NULL) { + return NULL; + } + if (pAllocationCallbacks->onRealloc != NULL) { + return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); + } + if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { + void* p2; + p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); + if (p2 == NULL) { + return NULL; + } + if (p != NULL) { + MA_DR_MP3_COPY_MEMORY(p2, p, szOld); + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } + return p2; + } + return NULL; +} +static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (p == NULL || pAllocationCallbacks == NULL) { + return; + } + if (pAllocationCallbacks->onFree != NULL) { + pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); + } +} +static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return *pAllocationCallbacks; + } else { + ma_allocation_callbacks allocationCallbacks; + allocationCallbacks.pUserData = NULL; + allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; + allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; + allocationCallbacks.onFree = ma_dr_mp3__free_default; + return allocationCallbacks; + } +} +static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) +{ + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); + pMP3->streamCursor += bytesRead; + return bytesRead; +} +static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) +{ + MA_DR_MP3_ASSERT(offset >= 0); + if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { + return MA_FALSE; + } + if (origin == ma_dr_mp3_seek_origin_start) { + pMP3->streamCursor = (ma_uint64)offset; + } else { + pMP3->streamCursor += offset; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) +{ + if (offset <= 0x7FFFFFFF) { + return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); + } + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + while (offset > 0) { + if (offset <= 0x7FFFFFFF) { + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; + } + offset = 0; + } else { + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; + } + offset -= 0x7FFFFFFF; + } + } + return MA_TRUE; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + ma_uint32 pcmFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + if (pMP3->atEnd) { + return 0; + } + for (;;) { + ma_dr_mp3dec_frame_info info; + if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { + size_t bytesRead; + if (pMP3->pData != NULL) { + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + } + pMP3->dataConsumed = 0; + if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { + ma_uint8* pNewData; + size_t newDataCap; + newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + if (pMP3->dataSize == 0) { + pMP3->atEnd = MA_TRUE; + return 0; + } + } + pMP3->dataSize += bytesRead; + } + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = MA_TRUE; + return 0; + } + MA_DR_MP3_ASSERT(pMP3->pData != NULL); + MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); + if (pMP3->pData == NULL) { + return 0; + } + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); + if (info.frame_bytes > 0) { + pMP3->dataConsumed += (size_t)info.frame_bytes; + pMP3->dataSize -= (size_t)info.frame_bytes; + } + if (pcmFramesRead > 0) { + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes == 0) { + size_t bytesRead; + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + pMP3->dataConsumed = 0; + if (pMP3->dataCapacity == pMP3->dataSize) { + ma_uint8* pNewData; + size_t newDataCap; + newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + if (pNewData == NULL) { + return 0; + } + pMP3->pData = pNewData; + pMP3->dataCapacity = newDataCap; + } + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = MA_TRUE; + return 0; + } + pMP3->dataSize += bytesRead; + } + }; + return pcmFramesRead; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + ma_uint32 pcmFramesRead = 0; + ma_dr_mp3dec_frame_info info; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); + if (pMP3->atEnd) { + return 0; + } + for (;;) { + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + if (pcmFramesRead > 0) { + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; + pMP3->mp3FrameChannels = info.channels; + pMP3->mp3FrameSampleRate = info.hz; + break; + } else if (info.frame_bytes > 0) { + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + } else { + break; + } + } + pMP3->memory.currentReadPos += (size_t)info.frame_bytes; + return pcmFramesRead; +} +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) +{ + if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + } else { + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + } +} +static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); +} +#if 0 +static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) +{ + ma_uint32 pcmFrameCount; + MA_DR_MP3_ASSERT(pMP3 != NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFrameCount == 0) { + return 0; + } + pMP3->currentPCMFrame += pcmFrameCount; + pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; + pMP3->pcmFramesRemainingInMP3Frame = 0; + return pcmFrameCount; +} +#endif +static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(onRead != NULL); + ma_dr_mp3dec_init(&pMP3->decoder); + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { + return MA_FALSE; + } + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + return MA_FALSE; + } + pMP3->channels = pMP3->mp3FrameChannels; + pMP3->sampleRate = pMP3->mp3FrameSampleRate; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL || onRead == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); +} +static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + size_t bytesRemaining; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + if (bytesToRead > 0) { + MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + return bytesToRead; +} +static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) +{ + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (origin == ma_dr_mp3_seek_origin_current) { + if (byteOffset > 0) { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); + } + } else { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pMP3->memory.currentReadPos; + } + } + pMP3->memory.currentReadPos += byteOffset; + } else { + if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { + pMP3->memory.currentReadPos = byteOffset; + } else { + pMP3->memory.currentReadPos = pMP3->memory.dataSize; + } + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pMP3 == NULL) { + return MA_FALSE; + } + MA_DR_MP3_ZERO_OBJECT(pMP3); + if (pData == NULL || dataSize == 0) { + return MA_FALSE; + } + pMP3->memory.pData = (const ma_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); +} +#ifndef MA_DR_MP3_NO_STDIO +#include +#include +static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} +static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + FILE* pFile; + if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_bool32 result; + FILE* pFile; + if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; + } + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { + fclose(pFile); + return result; + } + return MA_TRUE; +} +#endif +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) +{ + if (pMP3 == NULL) { + return; + } +#ifndef MA_DR_MP3_NO_STDIO + if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { + FILE* pFile = (FILE*)pMP3->pUserData; + if (pFile != NULL) { + fclose(pFile); + pMP3->pUserData = NULL; + } + } +#endif + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); +} +#if defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) +{ + ma_uint64 i; + ma_uint64 i4; + ma_uint64 sampleCount4; + i = 0; + sampleCount4 = sampleCount >> 2; + for (i4 = 0; i4 < sampleCount4; i4 += 1) { + float x0 = src[i+0]; + float x1 = src[i+1]; + float x2 = src[i+2]; + float x3 = src[i+3]; + x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); + x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); + x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); + x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); + x0 = x0 * 32767.0f; + x1 = x1 * 32767.0f; + x2 = x2 * 32767.0f; + x3 = x3 * 32767.0f; + dst[i+0] = (ma_int16)x0; + dst[i+1] = (ma_int16)x1; + dst[i+2] = (ma_int16)x2; + dst[i+3] = (ma_int16)x3; + i += 4; + } + for (; i < sampleCount; i += 1) { + float x = src[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); + x = x * 32767.0f; + dst[i] = (ma_int16)x; + } +} +#endif +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) +{ + ma_uint64 i; + for (i = 0; i < sampleCount; i += 1) { + float x = (float)src[i]; + x = x * 0.000030517578125f; + dst[i] = x; + } +} +#endif +static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) +{ + ma_uint64 totalFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); + while (framesToRead > 0) { + ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + if (pBufferOut != NULL) { + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #else + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); + #endif + } + pMP3->currentPCMFrame += framesToConsume; + pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; + pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; + totalFramesRead += framesToConsume; + framesToRead -= framesToConsume; + if (framesToRead == 0) { + break; + } + MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + break; + } + } + return totalFramesRead; +} +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + { + ma_int16 pTempS16[8192]; + ma_uint64 totalPCMFramesRead = 0; + while (totalPCMFramesRead < framesToRead) { + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + if (framesJustRead == 0) { + break; + } + ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + return totalPCMFramesRead; + } +#endif +} +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) { + return 0; + } +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#else + { + float pTempF32[4096]; + ma_uint64 totalPCMFramesRead = 0; + while (totalPCMFramesRead < framesToRead) { + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; + if (framesToReadNow > framesRemaining) { + framesToReadNow = framesRemaining; + } + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + if (framesJustRead == 0) { + break; + } + ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + totalPCMFramesRead += framesJustRead; + } + return totalPCMFramesRead; + } +#endif +} +static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + pMP3->pcmFramesConsumedInMP3Frame = 0; + pMP3->pcmFramesRemainingInMP3Frame = 0; + pMP3->currentPCMFrame = 0; + pMP3->dataSize = 0; + pMP3->atEnd = MA_FALSE; + ma_dr_mp3dec_init(&pMP3->decoder); +} +static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); + if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + ma_dr_mp3_reset(pMP3); + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) +{ + ma_uint64 framesRead; +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); +#else + framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); +#endif + if (framesRead != frameOffset) { + return MA_FALSE; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (frameIndex == pMP3->currentPCMFrame) { + return MA_TRUE; + } + if (frameIndex < pMP3->currentPCMFrame) { + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + } + MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); +} +static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) +{ + ma_uint32 iSeekPoint; + MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); + *pSeekPointIndex = 0; + if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { + return MA_FALSE; + } + for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { + if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { + break; + } + *pSeekPointIndex = iSeekPoint; + } + return MA_TRUE; +} +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + ma_dr_mp3_seek_point seekPoint; + ma_uint32 priorSeekPointIndex; + ma_uint16 iMP3Frame; + ma_uint64 leftoverFrames; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); + MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); + if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; + } else { + seekPoint.seekPosInBytes = 0; + seekPoint.pcmFrameIndex = 0; + seekPoint.mp3FramesToDiscard = 0; + seekPoint.pcmFramesToDiscard = 0; + } + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; + } + ma_dr_mp3_reset(pMP3); + for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { + ma_uint32 pcmFramesRead; + ma_dr_mp3d_sample_t* pPCMFrames; + pPCMFrames = NULL; + if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { + pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; + } + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); + if (pcmFramesRead == 0) { + return MA_FALSE; + } + } + pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; + leftoverFrames = frameIndex - pMP3->currentPCMFrame; + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); +} +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) { + return MA_FALSE; + } + if (frameIndex == 0) { + return ma_dr_mp3_seek_to_start_of_stream(pMP3); + } + if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { + return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + } else { + return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + } +} +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) +{ + ma_uint64 currentPCMFrame; + ma_uint64 totalPCMFrameCount; + ma_uint64 totalMP3FrameCount; + if (pMP3 == NULL) { + return MA_FALSE; + } + if (pMP3->onSeek == NULL) { + return MA_FALSE; + } + currentPCMFrame = pMP3->currentPCMFrame; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + totalPCMFrameCount = 0; + totalMP3FrameCount = 0; + for (;;) { + ma_uint32 pcmFramesInCurrentMP3Frame; + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3Frame == 0) { + break; + } + totalPCMFrameCount += pcmFramesInCurrentMP3Frame; + totalMP3FrameCount += 1; + } + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; + } + if (pMP3FrameCount != NULL) { + *pMP3FrameCount = totalMP3FrameCount; + } + if (pPCMFrameCount != NULL) { + *pPCMFrameCount = totalPCMFrameCount; + } + return MA_TRUE; +} +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) +{ + ma_uint64 totalPCMFrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + return 0; + } + return totalPCMFrameCount; +} +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) +{ + ma_uint64 totalMP3FrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + return 0; + } + return totalMP3FrameCount; +} +static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +{ + float srcRatio; + float pcmFrameCountOutF; + ma_uint32 pcmFrameCountOut; + srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; + MA_DR_MP3_ASSERT(srcRatio > 0); + pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); + pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; + *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; + *pRunningPCMFrameCount += pcmFrameCountOut; +} +typedef struct +{ + ma_uint64 bytePos; + ma_uint64 pcmFrameIndex; +} ma_dr_mp3__seeking_mp3_frame_info; +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) +{ + ma_uint32 seekPointCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalMP3FrameCount; + ma_uint64 totalPCMFrameCount; + if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { + return MA_FALSE; + } + seekPointCount = *pSeekPointCount; + if (seekPointCount == 0) { + return MA_FALSE; + } + currentPCMFrame = pMP3->currentPCMFrame; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return MA_FALSE; + } + if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { + seekPointCount = 1; + pSeekPoints[0].seekPosInBytes = 0; + pSeekPoints[0].pcmFrameIndex = 0; + pSeekPoints[0].mp3FramesToDiscard = 0; + pSeekPoints[0].pcmFramesToDiscard = 0; + } else { + ma_uint64 pcmFramesBetweenSeekPoints; + ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; + ma_uint64 runningPCMFrameCount = 0; + float runningPCMFrameCountFractionalPart = 0; + ma_uint64 nextTargetPCMFrame; + ma_uint32 iMP3Frame; + ma_uint32 iSeekPoint; + if (seekPointCount > totalMP3FrameCount-1) { + seekPointCount = (ma_uint32)totalMP3FrameCount-1; + } + pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + return MA_FALSE; + } + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + nextTargetPCMFrame = 0; + for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { + nextTargetPCMFrame += pcmFramesBetweenSeekPoints; + for (;;) { + if (nextTargetPCMFrame < runningPCMFrameCount) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } else { + size_t i; + ma_uint32 pcmFramesInCurrentMP3FrameIn; + for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { + mp3FrameInfo[i] = mp3FrameInfo[i+1]; + } + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); + if (pcmFramesInCurrentMP3FrameIn == 0) { + pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; + pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + break; + } + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + } + } + } + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; + } + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; + } + } + *pSeekPointCount = seekPointCount; + return MA_TRUE; +} +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) +{ + if (pMP3 == NULL) { + return MA_FALSE; + } + if (seekPointCount == 0 || pSeekPoints == NULL) { + pMP3->seekPointCount = 0; + pMP3->pSeekPoints = NULL; + } else { + pMP3->seekPointCount = seekPointCount; + pMP3->pSeekPoints = pSeekPoints; + } + return MA_TRUE; +} +static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +{ + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + float* pFrames = NULL; + float temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); + for (;;) { + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + if (framesCapacity < totalFramesRead + framesJustRead) { + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesBufferSize; + ma_uint64 newFramesCap; + float* pNewFrames; + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + break; + } + pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + if (framesJustRead != framesToReadRightNow) { + break; + } + } + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + ma_dr_mp3_uninit(pMP3); + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + return pFrames; +} +static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) +{ + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + ma_int16* pFrames = NULL; + ma_int16 temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); + for (;;) { + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + if (framesCapacity < totalFramesRead + framesJustRead) { + ma_uint64 newFramesBufferSize; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesCap; + ma_int16* pNewFrames; + newFramesCap = framesCapacity * 2; + if (newFramesCap < totalFramesRead + framesJustRead) { + newFramesCap = totalFramesRead + framesJustRead; + } + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { + break; + } + pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + if (pNewFrames == NULL) { + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + break; + } + pFrames = pNewFrames; + framesCapacity = newFramesCap; + } + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); + totalFramesRead += framesJustRead; + if (framesJustRead != framesToReadRightNow) { + break; + } + } + if (pConfig != NULL) { + pConfig->channels = pMP3->channels; + pConfig->sampleRate = pMP3->sampleRate; + } + ma_dr_mp3_uninit(pMP3); + if (pTotalFrameCount) { + *pTotalFrameCount = totalFramesRead; + } + return pFrames; +} +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + return NULL; + } + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); +} +#endif +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); + } else { + return ma_dr_mp3__malloc_default(sz, NULL); + } +} +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) +{ + if (pAllocationCallbacks != NULL) { + ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); + } else { + ma_dr_mp3__free_default(p, NULL); + } +} +#endif +/* dr_mp3_c end */ +#endif /* MA_DR_MP3_IMPLEMENTATION */ +#endif /* MA_NO_MP3 */ + + +/* End globally disabled warnings. */ +#if defined(_MSC_VER) + #pragma warning(pop) +#endif + +#endif /* miniaudio_c */ +#endif /* MINIAUDIO_IMPLEMENTATION */ + + +/* +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ diff --git a/thirdparty/miniaudio/research/README.txt b/thirdparty/miniaudio/research/README.txt new file mode 100644 index 0000000..160f56d --- /dev/null +++ b/thirdparty/miniaudio/research/README.txt @@ -0,0 +1,3 @@ +This folder contains code that I'm experimenting with outside of the main miniaudio library. It's just for +my own research and experimenting which I'm putting into the repository for version control purposes and +to get feedback from the community. You should not consider any of this code to be production quality. \ No newline at end of file diff --git a/thirdparty/miniaudio/resources/branding/icon-128x128.png b/thirdparty/miniaudio/resources/branding/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..37c5650d9046d12cc57ac2d989664ec13bb29c12 GIT binary patch literal 1189 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7xzrV9d{Sb`Ho)PG(@xm{>Y-ueVd6 z#IgFUq$z?S&MwUpT$>w3T#_Z8B=KfVxVq^2`UCx^_j-9`U$~@T;-Xv2`rzs$9|irc@ZENGc`dguH8=X0Jvo%6iGcFN^XY3=f#S|};EOL05y;S|xpG6kI&-stKnb@$GUA;Ux^O1{icBHbtjozEH&rjXDUwOs%&dNhU zF0Uq>N%T3y!udeaab{`Jlo=DECY*`-{b&C^*RDqwpR5l)DB#|*dOmRpsRjbIwouYR9!Mc15V}>)$svWvM@?Xgck5IDX2UG&|SzW+v|y zzvLbK8S3o)=HO8?9=lhC%Kx-2Q*K?(tgvpLC-LRJ)AcOd)%#2RB*8Wk}a^uV-ItZ0-B)(>ZJA_~w1EXW}Y|VPOquc+Apw{5#EP z3%vu+Z-1`0U%$OR?K1nmpY^7-jI*9F%j-Kdw`0U=_AtC$2ry34d20wa&CP-Gi+L)wz-mXcj3*u3Q5{GA~?hr^{ziuzhG^` zY6Xrig`~&wAFg^tyq8dPoD`Y%{`gGG=`)QV%r`2zyi%IU`2DSq5hgbKH%P3jS9Q^L zw0d!|oJ&6! zZw}g>QubIt-EE42C)b*U2SysE`ZGhn^Zxyn79rZNSKnQlQJUu`agg9WJyWAQhry(A8#t(*9Pq8J}P_ux81irk2qQ8Jur&@w%*GxdH$mJQ?}l7 z&bZm~dG)4gcbdC(U({?diT*v6$>{G)o;`CNKQnE)>-0ECbM^jGKY4|XivA_czu7P7 z|Bjwk&+xQA{hvc2PdzXOI14-?iy0X7ltGxWVyS%@0|UFVr;B4qMcmtK2Ys0x1zZnC zK3Op5f9IMW1xCMblfQ{GUbuaAv3mUT%DCh5j7*c6;wMK(&thn_XJq^!z`?`Kap1$p zwSEo^dl;DxGcg&|)T|a3V90Mcu%Us0NB;gR7M26j3I;j~42O?zw*qQm8`VN6#N@WC z|FFCMwtUNj-z@W zX>9|;mVe8HJy=;3o|?~b&{kkryZ$fL<42gf y0>eFq#zaO&3A=q)IXN249Y(c`h8TGX#=g;yqtLBnYY(svW$<+Mb6Mw<&;$TLBuRDv literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/resources/branding/icon-64x64.png b/thirdparty/miniaudio/resources/branding/icon-64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..30118d8807a564c4c68d4477d66631ed207599e1 GIT binary patch literal 677 zcmV;W0$TlvP)^)EE`N)4Y;k1Ci8mr@LAs`;W<*cRXRxkv z|DUIm85u5h4@DvP?0N5fP`?Ys9nbqd_PpW=1fJkBU-qv`PGg^<@07LPLulEA^Q*F! z+=GiP=zO#^TlVT{2r#upc#QxeRg-1}nA#$|MgWnjNptsO#8emHwH*M2s*;QVRb7DD z2mnG=Nk)LGF2HOA0HLZRBS2LbV74D4K&UFoc7Uobz-$Bnp{gV!KvfrDHUfZ9Rgw{) zstYh10YIoK$p}!@1(=NhAXJrP1gPo)%tinZs!B2fRCNJn#}n`ZvK9vMpXv^d00000 LNkvXXu0mjf^NSh_ literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/resources/branding/miniaudio_logo1.png b/thirdparty/miniaudio/resources/branding/miniaudio_logo1.png new file mode 100644 index 0000000000000000000000000000000000000000..a73eb8c513e7af0470ed6fa15545de9b435491a5 GIT binary patch literal 183 zcmeAS@N?(olHy`uVBq!ia0vp^nn29T!3HF6@EzR^q&N#aB8wRqxP?KOkzv*x37}x6 zr;B5VM)1-gN3H_~9Nx#}w*H_0YqhAZk*1aRtnP#>OBV9-$E|shacg_kq1$<7{w6H; zRnz%yEVz1NN`a32-R4=d6Gf`FKYrdV;o@|4?z)$`4~;dwb!ravJaoFQelqIJcca7m e*`FWV#v9QU*^~KbLh*2~7ZK)j?hW literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/resources/branding/miniaudio_logo_400.png b/thirdparty/miniaudio/resources/branding/miniaudio_logo_400.png new file mode 100644 index 0000000000000000000000000000000000000000..85d06c78b09b77c5eb6fc634cc2cbeb7fad094d2 GIT binary patch literal 3183 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~teM`SSr1Gg{;GcwGYBf-GH zqv+}47*Y}U_O_!Rvmp<|0oIQi|LPxwFOb_Ybyv}=2^NP-UmuZUkJL_Ge_EcAiG@Qz z!J&a+lrb6zqbXrD7mR>h;IQ{V^k1>bWk2@pQ-AyAk6HZn`}_Jo)ryqguH727x%6I^ zWM}O@;q|{$&F-&Qev@bM<_TxbXZwqoKbr7&erM03Iok93Ekd45j`?gWson!BIQhg4 z9Qf=RPN_RIID{QwIC-j5*`eXULngKp57`}l0t?s^ToMYAmW4o;Ma%&P%RdYn^MU#z z8~bw}gcV+#YI&XcaoM^uQKMIxF&F>FuL(=v)^~Bo{|{?#8E;x~>wClIC%h_FG4q6h z<@AAAM&=_2K?Qc>lm^C0Ny09mqMNP57#we!jLa%W)jB|K;t@y(7wSPwY#v>{0-%IF z=cBC0mFKIfA6(ow?>Q^qj^z2r1w9k`8q6nHo-0!Xy4{+Qc}c9dDA348OQ7Rr(su)^ zh6QsQ7%#0`%B2LfT!xW3rEhaAu$;KycYq=DdJwCif&^;SE-hRqzT($c8I`;j$ zg0E%kJm6hYSUlAYXp|Sw)S&CPfS%9LNoX+1TXzYlKR`2~A>>umY@kznS$QTLI~ytm zbfOlJ=@pv|^yq?(KwEe0^a2K?!_tQL4qByaeIJA;Y=7^)>eJd^b?Hx*gkH6pbXjfV zJqNSQDJGl(3L9oMFm8%+f8q?Z(wBv2g0GaSFVNF#)DjxjToe2W^hts+(D{?yJSPJ! zUCk+>u)5U64(O`U+HW)vKsjbKC5#5bNT~h%8|7y-Yj$o-U^xlwK`?l_`njxgN@xNA DwvG=u literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/README.md b/thirdparty/miniaudio/tests/_build/README.md new file mode 100644 index 0000000..fe1661a --- /dev/null +++ b/thirdparty/miniaudio/tests/_build/README.md @@ -0,0 +1,23 @@ +Building +======== +Build and run from this directory. Example: + + gcc ../test_deviceio/ma_test_deviceio.c -o bin/test_deviceio -ldl -lm -lpthread -Wall -Wextra -Wpedantic -std=c89 + ./bin/test_deviceio + +Output files will be placed in the "res/output" folder. + + +Emscripten +---------- +On Windows, you need to move into the build and run emsdk_env.bat from a command prompt using an absolute +path like "C:\emsdk\emsdk_env.bat". Note that PowerShell doesn't work for me for some reason. Example: + + emcc ../test_emscripten/ma_test_emscripten.c -o bin/test_emscripten.html -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY -DMA_ENABLE_AUDIO_WORKLETS -Wall -Wextra + +If you output WASM it may not work when running the web page locally. To test you can run with something +like this: + + emrun ./bin/test_emscripten.html + +If you want to see stdout on the command line when running from emrun, add `--emrun` to your emcc command. \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_f32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s16__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_u8__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_u8__mono_8000.raw new file mode 100644 index 0000000..7babb8e --- /dev/null +++ b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_f32_to_u8__mono_8000.raw @@ -0,0 +1 @@ +ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_f32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..7ddfa62ca40378ece49bf9ef6796f04b480cff9e GIT binary patch literal 320 zcmX|*v1$TA6h&`oxuriK>=y*Fmguhc4K?$HzWpWHpZ z1O5xSKKbxRTB@yF@if;p%e8KD9U`aqj^m$Gl{h5>r!sZwGk542`w#9OUyc7v?vZ@V zA8r5mV*WSuc=}3uL;o0#Z+Loe)$k?E0zGHH;C>7~{H>MI_04GX%a{H0-RN1;ccgbg ef8Z$K30xI?PfU+~WgqxUd=>r+xgmVbee(kj!Ee+6 literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_s16__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..574838c35bd20086f1ce80d01c3061e98af5de09 GIT binary patch literal 160 zcmV;R0AK$B00I!IA9gM`JYz`RQaW2?VIyT`W+r7~VLV&fQe;RqJb5mu9|92n|NZY< z;DXW7!>Y!-&yX;QQ|b00I!DAABw{JZDJSQaM{` zVI5^`W+7!`VL)5eQfo*pJc2Ht9}5ut|NZY<;DOQ7!~F1-`6IS5*E(JzpAhn>qc6x@8X zyx<@$Z{QM@_YHKfFy5dK@ce%d1vn`2Y;kRKCg?bPxLioO9u=P;;MnKVLrC6Sx(=s; z8=EJ~toD0;w#b~JMk^*6)s$tzG-eYKhP+42V-^V~DYJ}~VytQBd~G@Wa~~~kZ5o1} zWBTWkzQ>WzRY2LN-5QzISI&J#-)VV|~y1PtEPim-zYx;L%Rn literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_u8__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_u8__mono_8000.raw new file mode 100644 index 0000000..fa7bb23 --- /dev/null +++ b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s16_to_u8__mono_8000.raw @@ -0,0 +1 @@ +ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_f32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s16__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_u8__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_u8__mono_8000.raw new file mode 100644 index 0000000..7babb8e --- /dev/null +++ b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s24_to_u8__mono_8000.raw @@ -0,0 +1 @@ +ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_f32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s16__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_u8__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_u8__mono_8000.raw new file mode 100644 index 0000000..7babb8e --- /dev/null +++ b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_s32_to_u8__mono_8000.raw @@ -0,0 +1 @@ +ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_f32__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..a39db0eb08eabd44ba7ac0e70f9a26a3e64421ed GIT binary patch literal 320 zcmd7My9t0W6ouhaT8cA>BZyT5v6A3CmX2i+;z||{;Q2#_@WRLCzCxrDK|1WR$J$xy zZM4NUi#1czXU-n`+;J}?smao5$zJPjX|Tywi?-KHO`kb?>~qIG@01At{Gaj%Hh)2J literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s16__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..293a723e42f1c6f7c5f154625a72ef8292fc0b59 GIT binary patch literal 160 zcmZQzU=U!CXV7D?VenuGVu)r)WJqO51L8y=I|wLd1C)~os`=0Gp5Xz*MTWx++Zom} dEMb_(FpFU(5YGd$*8;^31LYn7)lk>G4*={pC)xl2 literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s24__mono_8000.raw b/thirdparty/miniaudio/tests/_build/res/benchmarks/pcm_u8_to_s24__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..5d321211f2cebfd2985c86954f14d62f8998f677 GIT binary patch literal 240 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 QAN{Lx%&%ndJ8OOUe+G%e#Q*>R literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/tests/test_automated/ma_test_automated.c b/thirdparty/miniaudio/tests/test_automated/ma_test_automated.c new file mode 100644 index 0000000..4b4c644 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_automated/ma_test_automated.c @@ -0,0 +1,34 @@ + +#include "../test_common/ma_test_common.c" +#include "ma_test_automated_data_converter.c" + +int main(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + size_t iTest; + + (void)argc; + (void)argv; + + result = ma_register_test("Data Conversion", test_entry__data_converter); + if (result != MA_SUCCESS) { + return result; + } + + for (iTest = 0; iTest < g_Tests.count; iTest += 1) { + printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName); + result = g_Tests.pTests[iTest].onEntry(argc, argv); + printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED"); + + if (result != 0) { + hasError = MA_TRUE; + } + } + + if (hasError) { + return -1; /* Something failed. */ + } else { + return 0; /* Everything passed. */ + } +} \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/test_automated/ma_test_automated_data_converter.c b/thirdparty/miniaudio/tests/test_automated/ma_test_automated_data_converter.c new file mode 100644 index 0000000..26a59f5 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_automated/ma_test_automated_data_converter.c @@ -0,0 +1,379 @@ + +ma_result init_data_converter(ma_uint32 rateIn, ma_uint32 rateOut, ma_resample_algorithm algorithm, ma_data_converter* pDataConverter) +{ + ma_result result; + ma_data_converter_config config; + + config = ma_data_converter_config_init(ma_format_s16, ma_format_s16, 1, 1, rateIn, rateOut); + config.resampling.algorithm = algorithm; + + result = ma_data_converter_init(&config, NULL, pDataConverter); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + +#if 0 +ma_result test_data_converter__passthrough() +{ + /* + Notes: + - The isPassthrough flag should be set to true. Both the positive and negative cases need to be tested. + - ma_data_converter_set_rate() should fail with MA_INVALID_OPERATION. + - The output should be identical to the input. + */ + printf("Passthrough\n"); + + + + return MA_SUCCESS; +} +#endif + +ma_result test_data_converter__resampling_expected_output_fixed_interval(ma_data_converter* pDataConverter, ma_uint64 frameCountPerIteration) +{ + ma_result result = MA_SUCCESS; + ma_int16 input[4096]; + ma_int16 i; + + MA_ASSERT(frameCountPerIteration < ma_countof(input)); + + /* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */ + for (i = 0; i < ma_countof(input); i += 1) { + input[i] = i; + } + + for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) { + ma_int16 output[4096]; + ma_uint64 outputFrameCount; + ma_uint64 inputFrameCount; + ma_uint64 expectedOutputFrameCount; + + /* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */ + ma_data_converter_get_expected_output_frame_count(pDataConverter, frameCountPerIteration, &expectedOutputFrameCount); + + outputFrameCount = ma_countof(output); + inputFrameCount = frameCountPerIteration; + result = ma_data_converter_process_pcm_frames(pDataConverter, input, &inputFrameCount, output, &outputFrameCount); + if (result != MA_SUCCESS) { + printf("Failed to process frames."); + break; + } + + if (outputFrameCount != expectedOutputFrameCount) { + printf("ERROR: Predicted vs actual output count mismatch: predicted=%d, actual=%d\n", (int)expectedOutputFrameCount, (int)outputFrameCount); + result = MA_ERROR; + } + } + + if (result != MA_SUCCESS) { + printf("FAILED\n"); + } else { + printf("PASSED\n"); + } + + return result; +} + +ma_result test_data_converter__resampling_expected_output_by_algorithm_and_rate_fixed_interval(ma_resample_algorithm algorithm, ma_uint32 rateIn, ma_uint32 rateOut, ma_uint64 frameCountPerIteration) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + ma_data_converter converter; + + result = init_data_converter(rateIn, rateOut, algorithm, &converter); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_expected_output_fixed_interval(&converter, frameCountPerIteration); + + ma_data_converter_uninit(&converter, NULL); + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_expected_output_by_algorithm_fixed_interval(ma_resample_algorithm algorithm, ma_uint64 frameCountPerIteration) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_data_converter__resampling_expected_output_by_algorithm_and_rate_fixed_interval(algorithm, 44100, 48000, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_expected_output_by_algorithm_and_rate_fixed_interval(algorithm, 48000, 44100, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_data_converter__resampling_expected_output_by_algorithm_and_rate_fixed_interval(algorithm, 44100, 192000, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_expected_output_by_algorithm_and_rate_fixed_interval(algorithm, 192000, 44100, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_expected_output_by_algorithm(ma_resample_algorithm algorithm) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_data_converter__resampling_expected_output_by_algorithm_fixed_interval(algorithm, 1); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_expected_output_by_algorithm_fixed_interval(algorithm, 16); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_expected_output_by_algorithm_fixed_interval(algorithm, 127); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_expected_output() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + printf("Linear\n"); + result = test_data_converter__resampling_expected_output_by_algorithm(ma_resample_algorithm_linear); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + + + +ma_result test_data_converter__resampling_required_input_fixed_interval(ma_data_converter* pDataConverter, ma_uint64 frameCountPerIteration) +{ + ma_result result = MA_SUCCESS; + ma_int16 input[4096]; + ma_int16 i; + + MA_ASSERT(frameCountPerIteration < ma_countof(input)); + + /* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */ + for (i = 0; i < ma_countof(input); i += 1) { + input[i] = i; + } + + for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) { + ma_int16 output[4096]; + ma_uint64 outputFrameCount; + ma_uint64 inputFrameCount; + ma_uint64 requiredInputFrameCount; + + /* We retrieve the required number of input frames for the specified number of output frames, and then compare with what we actually get when reading. */ + ma_data_converter_get_required_input_frame_count(pDataConverter, frameCountPerIteration, &requiredInputFrameCount); + + outputFrameCount = frameCountPerIteration; + inputFrameCount = ma_countof(input); + result = ma_data_converter_process_pcm_frames(pDataConverter, input, &inputFrameCount, output, &outputFrameCount); + if (result != MA_SUCCESS) { + printf("Failed to process frames."); + break; + } + + if (inputFrameCount != requiredInputFrameCount) { + printf("ERROR: Predicted vs actual input count mismatch: predicted=%d, actual=%d\n", (int)requiredInputFrameCount, (int)inputFrameCount); + result = MA_ERROR; + } + } + + if (result != MA_SUCCESS) { + printf("FAILED\n"); + } else { + printf("PASSED\n"); + } + + return result; +} + +ma_result test_data_converter__resampling_required_input_by_algorithm_and_rate_fixed_interval(ma_resample_algorithm algorithm, ma_uint32 rateIn, ma_uint32 rateOut, ma_uint64 frameCountPerIteration) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + ma_data_converter converter; + + result = init_data_converter(rateIn, rateOut, algorithm, &converter); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input_fixed_interval(&converter, frameCountPerIteration); + + ma_data_converter_uninit(&converter, NULL); + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_required_input_by_algorithm_fixed_interval(ma_resample_algorithm algorithm, ma_uint64 frameCountPerIteration) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_data_converter__resampling_required_input_by_algorithm_and_rate_fixed_interval(algorithm, 44100, 48000, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input_by_algorithm_and_rate_fixed_interval(algorithm, 48000, 44100, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_data_converter__resampling_required_input_by_algorithm_and_rate_fixed_interval(algorithm, 44100, 192000, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input_by_algorithm_and_rate_fixed_interval(algorithm, 192000, 44100, frameCountPerIteration); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_required_input_by_algorithm(ma_resample_algorithm algorithm) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_data_converter__resampling_required_input_by_algorithm_fixed_interval(algorithm, 1); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input_by_algorithm_fixed_interval(algorithm, 16); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input_by_algorithm_fixed_interval(algorithm, 127); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_data_converter__resampling_required_input() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + printf("Linear\n"); + result = test_data_converter__resampling_required_input_by_algorithm(ma_resample_algorithm_linear); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + + + + +ma_result test_data_converter__resampling() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_data_converter__resampling_expected_output(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_data_converter__resampling_required_input(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + + +int test_entry__data_converter(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + (void)argc; + (void)argv; + +#if 0 + result = test_data_converter__passthrough(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } +#endif + + result = test_data_converter__resampling(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_common/ma_test_common.c b/thirdparty/miniaudio/tests/test_common/ma_test_common.c new file mode 100644 index 0000000..97ff8df --- /dev/null +++ b/thirdparty/miniaudio/tests/test_common/ma_test_common.c @@ -0,0 +1,39 @@ +#define MINIAUDIO_IMPLEMENTATION +#include "../../miniaudio.h" + +#include + +#define MAX_TESTS 64 +#define TEST_OUTPUT_DIR "res/output" + +typedef int (* ma_test_entry_proc)(int argc, char** argv); + +typedef struct +{ + const char* pName; + ma_test_entry_proc onEntry; +} ma_test; + +static struct +{ + ma_test pTests[MAX_TESTS]; + size_t count; +} g_Tests; + +ma_result ma_register_test(const char* pName, ma_test_entry_proc onEntry) +{ + MA_ASSERT(pName != NULL); + MA_ASSERT(onEntry != NULL); + + if (g_Tests.count >= MAX_TESTS) { + printf("Failed to register test %s because there are too many tests already registered. Increase the value of MAX_TESTS\n", pName); + return MA_INVALID_OPERATION; + } + + g_Tests.pTests[g_Tests.count].pName = pName; + g_Tests.pTests[g_Tests.count].onEntry = onEntry; + g_Tests.count += 1; + + return MA_SUCCESS; +} + diff --git a/thirdparty/miniaudio/tests/test_cpp/ma_test_cpp.cpp b/thirdparty/miniaudio/tests/test_cpp/ma_test_cpp.cpp new file mode 100644 index 0000000..a20bd97 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_cpp/ma_test_cpp.cpp @@ -0,0 +1 @@ +#include "../test_deviceio/ma_test_deviceio.c" diff --git a/thirdparty/miniaudio/tests/test_deviceio/ma_test_deviceio.c b/thirdparty/miniaudio/tests/test_deviceio/ma_test_deviceio.c new file mode 100644 index 0000000..6c8ceaf --- /dev/null +++ b/thirdparty/miniaudio/tests/test_deviceio/ma_test_deviceio.c @@ -0,0 +1,632 @@ +/* +USAGE: ma_test_deviceio [input/output file] [mode] [backend] [waveform] [noise] + +In playback mode the input file is optional, in which case a waveform or noise source will be used instead. For capture and loopback modes +it must specify an output parameter, and must be specified. In duplex mode it is optional, but if specified will be an output file that +will receive the captured audio. + +"mode" can be one of the following: + playback + capture + duplex + loopback + +"backend" is one of the miniaudio backends: + wasapi + dsound or directsound + winmm + coreaudio + sndio + audio4 + oss + pulseaudio or pulse + alsa + jack + aaudio + opensl + webaudio + null + +"waveform" can be one of the following: + sine + square + triangle + sawtooth + +"noise" can be one of the following: + white + pink + brownian or brown + +If multiple backends are specified, the priority will be based on the order in which you specify them. If multiple waveform or noise types +are specified the last one on the command line will have priority. +*/ +#define MA_FORCE_UWP +#include "../test_common/ma_test_common.c" + +typedef enum +{ + source_type_waveform, + source_type_noise, + source_type_decoder +} source_type; + +static struct +{ + ma_context context; + ma_device device; + source_type sourceType; + ma_waveform waveform; + ma_noise noise; + ma_decoder decoder; + ma_encoder encoder; + ma_bool32 hasEncoder; /* Used for duplex mode to determine whether or not audio data should be written to a file. */ +} g_State; + +const char* get_mode_description(ma_device_type deviceType) +{ + if (deviceType == ma_device_type_playback) { + return "Playback"; + } + if (deviceType == ma_device_type_capture) { + return "Capture"; + } + if (deviceType == ma_device_type_duplex) { + return "Duplex"; + } + if (deviceType == ma_device_type_loopback) { + return "Loopback"; + } + + /* Should never get here. */ + return "Unknown"; +} + +ma_bool32 try_parse_mode(const char* arg, ma_device_type* pDeviceType) +{ + MA_ASSERT(arg != NULL); + MA_ASSERT(pDeviceType != NULL); + + if (strcmp(arg, "playback") == 0) { + *pDeviceType = ma_device_type_playback; + return MA_TRUE; + } + if (strcmp(arg, "capture") == 0) { + *pDeviceType = ma_device_type_capture; + return MA_TRUE; + } + if (strcmp(arg, "duplex") == 0) { + *pDeviceType = ma_device_type_duplex; + return MA_TRUE; + } + if (strcmp(arg, "loopback") == 0) { + *pDeviceType = ma_device_type_loopback; + return MA_TRUE; + } + + return MA_FALSE; +} + +ma_bool32 try_parse_backend(const char* arg, ma_backend* pBackends, ma_uint32 backendCap, ma_uint32* pBackendCount) +{ + ma_uint32 backendCount; + + MA_ASSERT(arg != NULL); + MA_ASSERT(pBackends != NULL); + MA_ASSERT(pBackendCount != NULL); + + backendCount = *pBackendCount; + if (backendCount == backendCap) { + return MA_FALSE; /* No more room. */ + } + + if (strcmp(arg, "wasapi") == 0) { + pBackends[backendCount++] = ma_backend_wasapi; + goto done; + } + if (strcmp(arg, "dsound") == 0 || strcmp(arg, "directsound") == 0) { + pBackends[backendCount++] = ma_backend_dsound; + goto done; + } + if (strcmp(arg, "winmm") == 0) { + pBackends[backendCount++] = ma_backend_winmm; + goto done; + } + if (strcmp(arg, "coreaudio") == 0) { + pBackends[backendCount++] = ma_backend_coreaudio; + goto done; + } + if (strcmp(arg, "sndio") == 0) { + pBackends[backendCount++] = ma_backend_sndio; + goto done; + } + if (strcmp(arg, "audio4") == 0) { + pBackends[backendCount++] = ma_backend_audio4; + goto done; + } + if (strcmp(arg, "oss") == 0) { + pBackends[backendCount++] = ma_backend_oss; + goto done; + } + if (strcmp(arg, "pulseaudio") == 0 || strcmp(arg, "pulse") == 0) { + pBackends[backendCount++] = ma_backend_pulseaudio; + goto done; + } + if (strcmp(arg, "alsa") == 0) { + pBackends[backendCount++] = ma_backend_alsa; + goto done; + } + if (strcmp(arg, "jack") == 0) { + pBackends[backendCount++] = ma_backend_jack; + goto done; + } + if (strcmp(arg, "aaudio") == 0) { + pBackends[backendCount++] = ma_backend_aaudio; + goto done; + } + if (strcmp(arg, "opensl") == 0) { + pBackends[backendCount++] = ma_backend_opensl; + goto done; + } + if (strcmp(arg, "webaudio") == 0) { + pBackends[backendCount++] = ma_backend_webaudio; + goto done; + } + if (strcmp(arg, "null") == 0) { + pBackends[backendCount++] = ma_backend_null; + goto done; + } + +done: + if (*pBackendCount < backendCount) { + *pBackendCount = backendCount; + return MA_TRUE; /* This argument was a backend. */ + } else { + return MA_FALSE; /* This argument was not a backend. */ + } +} + +ma_bool32 try_parse_waveform(const char* arg, ma_waveform_type* pWaveformType) +{ + MA_ASSERT(arg != NULL); + MA_ASSERT(pWaveformType != NULL); + + if (strcmp(arg, "sine") == 0) { + *pWaveformType = ma_waveform_type_sine; + return MA_TRUE; + } + if (strcmp(arg, "square") == 0) { + *pWaveformType = ma_waveform_type_square; + return MA_TRUE; + } + if (strcmp(arg, "triangle") == 0) { + *pWaveformType = ma_waveform_type_triangle; + return MA_TRUE; + } + if (strcmp(arg, "sawtooth") == 0) { + *pWaveformType = ma_waveform_type_sawtooth; + return MA_TRUE; + } + + return MA_FALSE; +} + +ma_bool32 try_parse_noise(const char* arg, ma_noise_type* pNoiseType) +{ + MA_ASSERT(arg != NULL); + MA_ASSERT(pNoiseType != NULL); + + if (strcmp(arg, "white") == 0) { + *pNoiseType = ma_noise_type_white; + return MA_TRUE; + } + if (strcmp(arg, "pink") == 0) { + *pNoiseType = ma_noise_type_pink; + return MA_TRUE; + } + if (strcmp(arg, "brownian") == 0 || strcmp(arg, "brown") == 0) { + *pNoiseType = ma_noise_type_brownian; + return MA_TRUE; + } + + return MA_FALSE; +} + +ma_result print_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo) +{ + ma_result result; + ma_device_info detailedDeviceInfo; + + MA_ASSERT(pDeviceInfo != NULL); + +#if 1 + result = ma_context_get_device_info(pContext, deviceType, &pDeviceInfo->id, &detailedDeviceInfo); + if (result != MA_SUCCESS) { + return result; + } +#else + detailedDeviceInfo = *pDeviceInfo; +#endif + + { + ma_uint32 iFormat; + + printf("%s\n", pDeviceInfo->name); + printf(" Default: %s\n", (detailedDeviceInfo.isDefault) ? "Yes" : "No"); + printf(" Format Count: %d\n", detailedDeviceInfo.nativeDataFormatCount); + for (iFormat = 0; iFormat < detailedDeviceInfo.nativeDataFormatCount; ++iFormat) { + printf(" %s, %d, %d\n", ma_get_format_name(detailedDeviceInfo.nativeDataFormats[iFormat].format), detailedDeviceInfo.nativeDataFormats[iFormat].channels, detailedDeviceInfo.nativeDataFormats[iFormat].sampleRate); + } + } + + return MA_SUCCESS; +} + +ma_result enumerate_devices(ma_context* pContext) +{ + ma_result result; + ma_device_info* pPlaybackDevices; + ma_uint32 playbackDeviceCount; + ma_device_info* pCaptureDevices; + ma_uint32 captureDeviceCount; + ma_uint32 iDevice; + + MA_ASSERT(pContext != NULL); + + result = ma_context_get_devices(pContext, &pPlaybackDevices, &playbackDeviceCount, &pCaptureDevices, &captureDeviceCount); + if (result != MA_SUCCESS) { + return result; + } + + printf("Playback Devices\n"); + printf("----------------\n"); + for (iDevice = 0; iDevice < playbackDeviceCount; iDevice += 1) { + printf("%d: ", iDevice); + print_device_info(pContext, ma_device_type_playback, &pPlaybackDevices[iDevice]); + } + printf("\n"); + + printf("Capture Devices\n"); + printf("---------------\n"); + for (iDevice = 0; iDevice < captureDeviceCount; iDevice += 1) { + printf("%d: ", iDevice); + print_device_info(pContext, ma_device_type_capture, &pCaptureDevices[iDevice]); + } + printf("\n"); + + return MA_SUCCESS; +} + +void on_log(void* pUserData, ma_uint32 logLevel, const char* message) +{ + (void)pUserData; + + printf("%s: %s", ma_log_level_to_string(logLevel), message); +} + +void on_notification(const ma_device_notification* pNotification) +{ + MA_ASSERT(pNotification != NULL); + + switch (pNotification->type) + { + case ma_device_notification_type_started: + { + printf("Started\n"); + } break; + + case ma_device_notification_type_stopped: + { + printf("Stopped\n"); + } break; + + case ma_device_notification_type_rerouted: + { + printf("Rerouted\n"); + } break; + + case ma_device_notification_type_interruption_began: + { + printf("Interruption Began\n"); + } break; + + case ma_device_notification_type_interruption_ended: + { + printf("Interruption Ended\n"); + } break; + + default: break; + } +} + +void on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) +{ + switch (pDevice->type) + { + case ma_device_type_playback: + { + /* + In the playback case we just read from our input source. We're going to use ma_data_source_read_pcm_frames() for this + to ensure the data source abstraction is working properly for each type. */ + if (g_State.sourceType == source_type_decoder) { + ma_data_source_read_pcm_frames(&g_State.decoder, pFramesOut, frameCount, NULL); + } else if (g_State.sourceType == source_type_waveform) { + ma_data_source_read_pcm_frames(&g_State.waveform, pFramesOut, frameCount, NULL); + } else if (g_State.sourceType == source_type_noise) { + ma_data_source_read_pcm_frames(&g_State.noise, pFramesOut, frameCount, NULL); + } + } break; + + case ma_device_type_capture: + case ma_device_type_loopback: + { + /* In the capture and loopback cases we just output straight to a file. */ + ma_encoder_write_pcm_frames(&g_State.encoder, pFramesIn, frameCount, NULL); + } break; + + case ma_device_type_duplex: + { + /* The duplex case is easy. We just move from pFramesIn to pFramesOut. */ + MA_ASSERT(pDevice->playback.format == pDevice->capture.format); + MA_ASSERT(pDevice->playback.channels == pDevice->capture.channels); + MA_COPY_MEMORY(pFramesOut, pFramesIn, ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels) * frameCount); + + /* Also output to the encoder if necessary. */ + if (g_State.hasEncoder) { + ma_encoder_write_pcm_frames(&g_State.encoder, pFramesIn, frameCount, NULL); + } + } break; + + default: + { + /* Should never hit this. */ + MA_ASSERT(MA_FALSE); + } break; + } +} + +int main(int argc, char** argv) +{ + int iarg; + ma_result result; + ma_backend enabledBackends[MA_BACKEND_COUNT]; + size_t enabledBackendCount; + size_t iEnabledBackend; + ma_backend backends[MA_BACKEND_COUNT]; + ma_uint32 backendCount = 0; + ma_context_config contextConfig; + ma_device_type deviceType = ma_device_type_playback; + ma_format deviceFormat = ma_format_unknown; + ma_uint32 deviceChannels = 0; + ma_uint32 deviceSampleRate = 0; + ma_device_config deviceConfig; + ma_waveform_type waveformType = ma_waveform_type_sine; + ma_noise_type noiseType = ma_noise_type_white; + const char* pFilePath = NULL; /* Input or output file path, depending on the mode. */ + ma_bool32 enumerate = MA_TRUE; + + /* Default to a sine wave if nothing is passed into the command line. */ + waveformType = ma_waveform_type_sine; + g_State.sourceType = source_type_waveform; + + /* We need to iterate over the command line arguments and gather our settings. */ + for (iarg = 1; iarg < argc; iarg += 1) { + /* mode */ + if (try_parse_mode(argv[iarg], &deviceType)) { + continue; + } + + /* backend */ + if (try_parse_backend(argv[iarg], backends, ma_countof(backends), &backendCount)) { + continue; + } + + /* waveform */ + if (try_parse_waveform(argv[iarg], &waveformType)) { + g_State.sourceType = source_type_waveform; + continue; + } + + /* noise */ + if (try_parse_noise(argv[iarg], &noiseType)) { + g_State.sourceType = source_type_noise; + continue; + } + + /* Getting here means the argument should be considered the input or output file. */ + pFilePath = argv[iarg]; + g_State.sourceType = source_type_decoder; + } + + /* Here we'll quickly print the available backends. */ + printf("Enabled Backends:\n"); + result = ma_get_enabled_backends(enabledBackends, ma_countof(enabledBackends), &enabledBackendCount); + if (result != MA_SUCCESS) { + printf("Failed to retrieve available backends.\n"); + return -1; + } + + for (iEnabledBackend = 0; iEnabledBackend < enabledBackendCount; iEnabledBackend += 1) { + printf(" %s\n", ma_get_backend_name(enabledBackends[iEnabledBackend])); + } + printf("\n"); + + + /* Initialize the context first. If no backends were passed into the command line we just use defaults. */ + contextConfig = ma_context_config_init(); + result = ma_context_init((backendCount == 0) ? NULL : backends, backendCount, &contextConfig, &g_State.context); + if (result != MA_SUCCESS) { + printf("Failed to initialize context.\n"); + return -1; + } + + ma_log_register_callback(ma_context_get_log(&g_State.context), ma_log_callback_init(on_log, NULL)); + + /* Here we'll print some info about what we're doing. */ + printf("Backend: %s\n", ma_get_backend_name(g_State.context.backend)); + printf("Mode: %s\n", get_mode_description(deviceType)); + printf("\n"); + + /* Enumerate if required. */ + if (enumerate) { + enumerate_devices(&g_State.context); + } + + /* + Now that the context has been initialized we can do the device. In duplex mode we want to use the same format for both playback and capture so we don't need + to do any data conversion between the two. + */ + if (deviceType == ma_device_type_duplex) { + if (deviceFormat == ma_format_unknown) { + deviceFormat = ma_format_f32; + } + if (deviceChannels == 0) { + deviceChannels = 0; + } + if (deviceSampleRate == 0) { + deviceSampleRate = 48000; + } + } + + deviceConfig = ma_device_config_init(deviceType); + deviceConfig.playback.format = deviceFormat; + deviceConfig.playback.channels = deviceChannels; + deviceConfig.capture.format = deviceFormat; + deviceConfig.capture.channels = deviceChannels; + deviceConfig.sampleRate = deviceSampleRate; + deviceConfig.dataCallback = on_data; + deviceConfig.notificationCallback = on_notification; + result = ma_device_init(&g_State.context, &deviceConfig, &g_State.device); + if (result != MA_SUCCESS) { + printf("Failed to initialize device.\n"); + ma_context_uninit(&g_State.context); + return -1; + } + + /* We can now initialize our input and output sources. */ + if (deviceType == ma_device_type_playback) { + if (g_State.sourceType == source_type_decoder) { + ma_decoder_config decoderConfig; + decoderConfig = ma_decoder_config_init(g_State.device.playback.format, g_State.device.playback.channels, g_State.device.sampleRate); + result = ma_decoder_init_file(pFilePath, &decoderConfig, &g_State.decoder); + if (result != MA_SUCCESS) { + printf("Failed to open file for decoding \"%s\".\n", pFilePath); + ma_device_uninit(&g_State.device); + ma_context_uninit(&g_State.context); + return -1; + } + } + + if (g_State.sourceType == source_type_waveform) { + ma_waveform_config waveformConfig; + waveformConfig = ma_waveform_config_init(g_State.device.playback.format, g_State.device.playback.channels, g_State.device.sampleRate, waveformType, 0.1, 220); + result = ma_waveform_init(&waveformConfig, &g_State.waveform); + if (result != MA_SUCCESS) { + printf("Failed to initialize waveform.\n"); + ma_device_uninit(&g_State.device); + ma_context_uninit(&g_State.context); + return -1; + } + } + + if (g_State.sourceType == source_type_noise) { + ma_noise_config noiseConfig; + noiseConfig = ma_noise_config_init(g_State.device.playback.format, g_State.device.playback.channels, noiseType, 0, 0.1); + result = ma_noise_init(&noiseConfig, NULL, &g_State.noise); + if (result != MA_SUCCESS) { + printf("Failed to initialize noise.\n"); + ma_device_uninit(&g_State.device); + ma_context_uninit(&g_State.context); + return -1; + } + } + } + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback || (deviceType == ma_device_type_duplex && pFilePath != NULL && pFilePath[0] != '\0')) { + ma_encoder_config encoderConfig; + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, g_State.device.capture.format, g_State.device.capture.channels, g_State.device.sampleRate); + result = ma_encoder_init_file(pFilePath, &encoderConfig, &g_State.encoder); + if (result != MA_SUCCESS) { + printf("Failed to initialize output file for capture \"%s\".\n", pFilePath); + ma_device_uninit(&g_State.device); + ma_context_uninit(&g_State.context); + return -1; + } + + g_State.hasEncoder = MA_TRUE; + } + + + /* Print the name of the device. */ + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(&g_State.device, ma_device_type_playback, name, sizeof(name), NULL); + printf("Playback Device: %s\n", name); + } + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; + ma_device_get_name(&g_State.device, ma_device_type_capture, name, sizeof(name), NULL); + printf("Capture Device: %s\n", name); + } + + + /* Everything should be initialized at this point so we can now print our configuration and start the device. */ + result = ma_device_start(&g_State.device); + if (result != MA_SUCCESS) { + printf("Failed to start device.\n"); + goto done; + } + + /* Now we just keep looping and wait for user input. */ + for (;;) { + int c; + + if (ma_device_is_started(&g_State.device)) { + printf("Press Q to quit, P to pause.\n"); + } else { + printf("Press Q to quit, P to resume.\n"); + } + + for (;;) { + c = getchar(); + if (c != '\n') { + break; + } + } + + if (c == 'q' || c == 'Q') { + break; + } + if (c == 'p' || c == 'P') { + if (ma_device_is_started(&g_State.device)) { + result = ma_device_stop(&g_State.device); + if (result != MA_SUCCESS) { + printf("ERROR: Error when stopping the device: %s\n", ma_result_description(result)); + } + } else { + result = ma_device_start(&g_State.device); + if (result != MA_SUCCESS) { + printf("ERROR: Error when starting the device: %s\n", ma_result_description(result)); + } + } + } + } + +done: + ma_device_uninit(&g_State.device); + ma_context_uninit(&g_State.context); + if (g_State.sourceType == source_type_decoder) { + ma_decoder_uninit(&g_State.decoder); + } + if (g_State.sourceType == source_type_waveform) { + ma_waveform_uninit(&g_State.waveform); + } + if (g_State.sourceType == source_type_noise) { + ma_noise_uninit(&g_State.noise, NULL); + } + if (g_State.hasEncoder) { + ma_encoder_uninit(&g_State.encoder); + } + + return 0; +} diff --git a/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.c b/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.c new file mode 100644 index 0000000..9cdd0c6 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.c @@ -0,0 +1,138 @@ +#define MA_DEBUG_OUTPUT +#define MA_NO_DECODING +#define MA_NO_ENCODING +#define MINIAUDIO_IMPLEMENTATION +#include "../../miniaudio.h" + +#include + +#define DEVICE_FORMAT ma_format_f32 +#define DEVICE_CHANNELS 2 +#define DEVICE_SAMPLE_RATE 48000 + +ma_bool32 isRunning = MA_FALSE; +ma_device device; +ma_waveform sineWave; /* For playback example. */ + + +void main_loop__em() +{ +} + + + +void data_callback_playback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->playback.channels == DEVICE_CHANNELS); + + ma_waveform_read_pcm_frames(&sineWave, pOutput, frameCount, NULL); + + (void)pInput; /* Unused. */ +} + +static void do_playback() +{ + ma_device_config deviceConfig; + ma_waveform_config sineWaveConfig; + + deviceConfig = ma_device_config_init(ma_device_type_playback); + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = DEVICE_CHANNELS; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback_playback; + deviceConfig.pUserData = &sineWave; + + if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) { + printf("Failed to open playback device.\n"); + return; + } + + sineWaveConfig = ma_waveform_config_init(device.playback.format, device.playback.channels, device.sampleRate, ma_waveform_type_sine, 0.2, 220); + ma_waveform_init(&sineWaveConfig, &sineWave); + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start device."); + return; + } +} + + + +void data_callback_duplex(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) +{ + MA_ASSERT(pDevice->capture.format == pDevice->playback.format); + MA_ASSERT(pDevice->capture.channels == pDevice->playback.channels); + + /* In this example the format and channel count are the same for both input and output which means we can just memcpy(). */ + MA_COPY_MEMORY(pOutput, pInput, frameCount * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); +} + +static void do_duplex() +{ + ma_result result; + ma_device_config deviceConfig; + + deviceConfig = ma_device_config_init(ma_device_type_duplex); + deviceConfig.capture.pDeviceID = NULL; + deviceConfig.capture.format = DEVICE_FORMAT; + deviceConfig.capture.channels = 2; + deviceConfig.capture.shareMode = ma_share_mode_shared; + deviceConfig.playback.pDeviceID = NULL; + deviceConfig.playback.format = DEVICE_FORMAT; + deviceConfig.playback.channels = 2; + deviceConfig.sampleRate = DEVICE_SAMPLE_RATE; + deviceConfig.dataCallback = data_callback_duplex; + result = ma_device_init(NULL, &deviceConfig, &device); + if (result != MA_SUCCESS) { + return; + } + + if (ma_device_start(&device) != MA_SUCCESS) { + printf("Failed to start device."); + return; + } +} + + +static EM_BOOL on_canvas_click(int eventType, const EmscriptenMouseEvent* pMouseEvent, void* pUserData) +{ + if (isRunning == MA_FALSE) { + if (pMouseEvent->button == 0) { /* Left click. */ + do_playback(); + } else if (pMouseEvent->button == 2) { /* Right click. */ + do_duplex(); + } + + isRunning = MA_TRUE; + } else { + if (ma_device_get_state(&device) == ma_device_state_started) { + ma_device_stop(&device); + } else { + ma_device_start(&device); + } + } + + (void)eventType; + (void)pUserData; + + return EM_FALSE; +} + + + + +int main(int argc, char** argv) +{ + printf("Click inside canvas to start playing:\n"); + printf(" Left click for playback\n"); + printf(" Right click for duplex\n"); + + /* The device must be started in response to an input event. */ + emscripten_set_mouseup_callback("canvas", &device, 0, on_canvas_click); + + emscripten_set_main_loop(main_loop__em, 0, 1); + + (void)argc; + (void)argv; + return 0; +} diff --git a/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.html b/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.html new file mode 100644 index 0000000..19b29d4 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_emscripten/ma_test_emscripten.html @@ -0,0 +1,144 @@ + + + + + + miniaudio - Emscripten Test + + + +
    +
    emscripten
    +
    Downloading...
    +
    + +
    +
    + +
    +
    +
    + Resize canvas + Lock/hide mouse pointer +     + +
    + +
    + +
    + + {{{ SCRIPT }}} + + \ No newline at end of file diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering.c new file mode 100644 index 0000000..8f36560 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering.c @@ -0,0 +1,99 @@ +#include "../test_common/ma_test_common.c" + +ma_result filtering_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_encoder_config encoderConfig; + + decoderConfig = ma_decoder_config_init(format, channels, sampleRate); + result = ma_decoder_init_file(pInputFilePath, &decoderConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate); + result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, pEncoder); + if (result != MA_SUCCESS) { + ma_decoder_uninit(pDecoder); + return result; + } + + return MA_SUCCESS; +} + +#include "ma_test_filtering_dithering.c" +#include "ma_test_filtering_lpf.c" +#include "ma_test_filtering_hpf.c" +#include "ma_test_filtering_bpf.c" +#include "ma_test_filtering_notch.c" +#include "ma_test_filtering_peak.c" +#include "ma_test_filtering_loshelf.c" +#include "ma_test_filtering_hishelf.c" + +int main(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + size_t iTest; + + (void)argc; + (void)argv; + + result = ma_register_test("Dithering", test_entry__dithering); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("Low-Pass Filtering", test_entry__lpf); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("High-Pass Filtering", test_entry__hpf); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("Band-Pass Filtering", test_entry__bpf); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("Notching Filtering", test_entry__notch); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("Peaking EQ Filtering", test_entry__peak); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("Low Shelf Filtering", test_entry__loshelf); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = ma_register_test("High Shelf Filtering", test_entry__hishelf); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + for (iTest = 0; iTest < g_Tests.count; iTest += 1) { + printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName); + result = g_Tests.pTests[iTest].onEntry(argc, argv); + printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED"); + + if (result != 0) { + hasError = MA_TRUE; + } + } + + if (hasError) { + return -1; /* Something failed. */ + } else { + return 0; /* Everything passed. */ + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_bpf.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_bpf.c new file mode 100644 index 0000000..50b0592 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_bpf.c @@ -0,0 +1,170 @@ +ma_result bpf_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + + +ma_result test_bpf2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_bpf2_config bpfConfig; + ma_bpf2 bpf; + + printf(" %s\n", pOutputFilePath); + + result = bpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + bpfConfig = ma_bpf2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 0); + result = ma_bpf2_init(&bpfConfig, NULL, &bpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_bpf2_process_pcm_frames(&bpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_bpf2__f32(const char* pInputFilePath) +{ + return test_bpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/bpf2_f32.wav", ma_format_f32); +} + +ma_result test_bpf2__s16(const char* pInputFilePath) +{ + return test_bpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/bpf2_s16.wav", ma_format_s16); +} + + +ma_result test_bpf4__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_bpf_config bpfConfig; + ma_bpf bpf; + + printf(" %s\n", pOutputFilePath); + + result = bpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + bpfConfig = ma_bpf_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 4); + result = ma_bpf_init(&bpfConfig, NULL, &bpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_bpf_process_pcm_frames(&bpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_bpf4__f32(const char* pInputFilePath) +{ + return test_bpf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/bpf4_f32.wav", ma_format_f32); +} + +ma_result test_bpf4__s16(const char* pInputFilePath) +{ + return test_bpf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/bpf4_s16.wav", ma_format_s16); +} + + +int test_entry__bpf(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_bpf2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_bpf2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_bpf4__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_bpf4__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_dithering.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_dithering.c new file mode 100644 index 0000000..a262664 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_dithering.c @@ -0,0 +1,74 @@ + +ma_result test_dithering__u8(const char* pInputFilePath) +{ + const char* pOutputFilePath = TEST_OUTPUT_DIR"/dithering_u8.wav"; + ma_result result; + ma_decoder_config decoderConfig; + ma_decoder decoder; + ma_encoder_config encoderConfig; + ma_encoder encoder; + + decoderConfig = ma_decoder_config_init(ma_format_f32, 0, 0); + result = ma_decoder_init_file(pInputFilePath, &decoderConfig, &decoder); + if (result != MA_SUCCESS) { + return result; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_u8, decoder.outputChannels, decoder.outputSampleRate); + result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, &encoder); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(ma_format_u8, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Convert, with dithering. */ + ma_convert_pcm_frames_format(tempOut, ma_format_u8, tempIn, decoder.outputFormat, framesJustRead, decoder.outputChannels, ma_dither_mode_triangle); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +int test_entry__dithering(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + result = test_dithering__u8(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hishelf.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hishelf.c new file mode 100644 index 0000000..39fd966 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hishelf.c @@ -0,0 +1,170 @@ +ma_result hishelf_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + + +ma_result test_hishelf2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_hishelf2_config hishelfConfig; + ma_hishelf2 hishelf; + + printf(" %s\n", pOutputFilePath); + + result = hishelf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + hishelfConfig = ma_hishelf2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 18, 1, 16000); + result = ma_hishelf2_init(&hishelfConfig, NULL, &hishelf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_hishelf2_process_pcm_frames(&hishelf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_hishelf2__f32(const char* pInputFilePath) +{ + return test_hishelf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hishelf2_f32.wav", ma_format_f32); +} + +ma_result test_hishelf2__s16(const char* pInputFilePath) +{ + return test_hishelf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hishelf2_s16.wav", ma_format_s16); +} + +#if 0 +ma_result test_hishelf4__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_hishelf_config hishelfConfig; + ma_hishelf hishelf; + + printf(" %s\n", pOutputFilePath); + + result = hishelf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + hishelfConfig = ma_hishelf_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 4); + result = ma_hishelf_init(&hishelfConfig, &hishelf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + framesJustRead = ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead); + + /* Filter */ + ma_hishelf_process_pcm_frames(&hishelf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_hishelf4__f32(const char* pInputFilePath) +{ + return test_hishelf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hishelf4_f32.wav", ma_format_f32); +} + +ma_result test_hishelf4__s16(const char* pInputFilePath) +{ + return test_hishelf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hishelf4_s16.wav", ma_format_s16); +} +#endif + +int test_entry__hishelf(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_hishelf2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_hishelf2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + +#if 0 + result = test_hishelf4__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_hishelf4__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } +#endif + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hpf.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hpf.c new file mode 100644 index 0000000..addb9e3 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_hpf.c @@ -0,0 +1,242 @@ + +ma_result hpf_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + +ma_result test_hpf1__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_hpf1_config hpfConfig; + ma_hpf1 hpf; + + printf(" %s\n", pOutputFilePath); + + result = hpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + hpfConfig = ma_hpf1_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000); + result = ma_hpf1_init(&hpfConfig, NULL, &hpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_hpf1_process_pcm_frames(&hpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_hpf1__f32(const char* pInputFilePath) +{ + return test_hpf1__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf1_f32.wav", ma_format_f32); +} + +ma_result test_hpf1__s16(const char* pInputFilePath) +{ + return test_hpf1__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf1_s16.wav", ma_format_s16); +} + + +ma_result test_hpf2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_hpf2_config hpfConfig; + ma_hpf2 hpf; + + printf(" %s\n", pOutputFilePath); + + result = hpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + hpfConfig = ma_hpf2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 0); + result = ma_hpf2_init(&hpfConfig, NULL, &hpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_hpf2_process_pcm_frames(&hpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_hpf2__f32(const char* pInputFilePath) +{ + return test_hpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf2_f32.wav", ma_format_f32); +} + +ma_result test_hpf2__s16(const char* pInputFilePath) +{ + return test_hpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf2_s16.wav", ma_format_s16); +} + + +ma_result test_hpf3__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_hpf_config hpfConfig; + ma_hpf hpf; + + printf(" %s\n", pOutputFilePath); + + result = hpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + hpfConfig = ma_hpf_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 3); + result = ma_hpf_init(&hpfConfig, NULL, &hpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_hpf_process_pcm_frames(&hpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_hpf3__f32(const char* pInputFilePath) +{ + return test_hpf3__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf3_f32.wav", ma_format_f32); +} + +ma_result test_hpf3__s16(const char* pInputFilePath) +{ + return test_hpf3__by_format(pInputFilePath, TEST_OUTPUT_DIR"/hpf3_s16.wav", ma_format_s16); +} + + +int test_entry__hpf(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_hpf1__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_hpf1__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_hpf2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_hpf2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_hpf3__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_hpf3__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_loshelf.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_loshelf.c new file mode 100644 index 0000000..37d18d3 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_loshelf.c @@ -0,0 +1,170 @@ +ma_result loshelf_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + + +ma_result test_loshelf2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_loshelf2_config loshelfConfig; + ma_loshelf2 loshelf; + + printf(" %s\n", pOutputFilePath); + + result = loshelf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + loshelfConfig = ma_loshelf2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 6, 1, 200); + result = ma_loshelf2_init(&loshelfConfig, NULL, &loshelf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_loshelf2_process_pcm_frames(&loshelf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_loshelf2__f32(const char* pInputFilePath) +{ + return test_loshelf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/loshelf2_f32.wav", ma_format_f32); +} + +ma_result test_loshelf2__s16(const char* pInputFilePath) +{ + return test_loshelf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/loshelf2_s16.wav", ma_format_s16); +} + +#if 0 +ma_result test_loshelf4__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_loshelf_config loshelfConfig; + ma_loshelf loshelf; + + printf(" %s\n", pOutputFilePath); + + result = loshelf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + loshelfConfig = ma_loshelf_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 4); + result = ma_loshelf_init(&loshelfConfig, &loshelf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + framesJustRead = ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead); + + /* Filter */ + ma_loshelf_process_pcm_frames(&loshelf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_loshelf4__f32(const char* pInputFilePath) +{ + return test_loshelf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/loshelf4_f32.wav", ma_format_f32); +} + +ma_result test_loshelf4__s16(const char* pInputFilePath) +{ + return test_loshelf4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/loshelf4_s16.wav", ma_format_s16); +} +#endif + +int test_entry__loshelf(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_loshelf2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_loshelf2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + +#if 0 + result = test_loshelf4__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_loshelf4__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } +#endif + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_lpf.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_lpf.c new file mode 100644 index 0000000..182e421 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_lpf.c @@ -0,0 +1,242 @@ + +ma_result lpf_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + +ma_result test_lpf1__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_lpf1_config lpfConfig; + ma_lpf1 lpf; + + printf(" %s\n", pOutputFilePath); + + result = lpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + lpfConfig = ma_lpf1_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000); + result = ma_lpf1_init(&lpfConfig, NULL, &lpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_lpf1_process_pcm_frames(&lpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_lpf1__f32(const char* pInputFilePath) +{ + return test_lpf1__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf1_f32.wav", ma_format_f32); +} + +ma_result test_lpf1__s16(const char* pInputFilePath) +{ + return test_lpf1__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf1_s16.wav", ma_format_s16); +} + + +ma_result test_lpf2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_lpf2_config lpfConfig; + ma_lpf2 lpf; + + printf(" %s\n", pOutputFilePath); + + result = lpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + lpfConfig = ma_lpf2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 0); + result = ma_lpf2_init(&lpfConfig, NULL, &lpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_lpf2_process_pcm_frames(&lpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_lpf2__f32(const char* pInputFilePath) +{ + return test_lpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf2_f32.wav", ma_format_f32); +} + +ma_result test_lpf2__s16(const char* pInputFilePath) +{ + return test_lpf2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf2_s16.wav", ma_format_s16); +} + + + +ma_result test_lpf3__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_lpf_config lpfConfig; + ma_lpf lpf; + + printf(" %s\n", pOutputFilePath); + + result = lpf_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + lpfConfig = ma_lpf_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, /*poles*/3); + result = ma_lpf_init(&lpfConfig, NULL, &lpf); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_lpf_process_pcm_frames(&lpf, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_lpf3__f32(const char* pInputFilePath) +{ + return test_lpf3__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf3_f32.wav", ma_format_f32); +} + +ma_result test_lpf3__s16(const char* pInputFilePath) +{ + return test_lpf3__by_format(pInputFilePath, TEST_OUTPUT_DIR"/lpf3_s16.wav", ma_format_s16); +} + + +int test_entry__lpf(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + result = test_lpf1__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_lpf1__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_lpf2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_lpf2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + result = test_lpf3__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_lpf3__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_notch.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_notch.c new file mode 100644 index 0000000..c5963b4 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_notch.c @@ -0,0 +1,170 @@ +ma_result notch_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + + +ma_result test_notch2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_notch2_config notchConfig; + ma_notch2 notch; + + printf(" %s\n", pOutputFilePath); + + result = notch_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + notchConfig = ma_notch2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 1, 60); + result = ma_notch2_init(¬chConfig, NULL, ¬ch); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_notch2_process_pcm_frames(¬ch, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_notch2__f32(const char* pInputFilePath) +{ + return test_notch2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/notch2_f32.wav", ma_format_f32); +} + +ma_result test_notch2__s16(const char* pInputFilePath) +{ + return test_notch2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/notch2_s16.wav", ma_format_s16); +} + +#if 0 +ma_result test_notch4__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_notch_config notchConfig; + ma_notch notch; + + printf(" %s\n", pOutputFilePath); + + result = notch_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + notchConfig = ma_notch_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 4); + result = ma_notch_init(¬chConfig, ¬ch); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + framesJustRead = ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead); + + /* Filter */ + ma_notch_process_pcm_frames(¬ch, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_notch4__f32(const char* pInputFilePath) +{ + return test_notch4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/notch4_f32.wav", ma_format_f32); +} + +ma_result test_notch4__s16(const char* pInputFilePath) +{ + return test_notch4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/notch4_s16.wav", ma_format_s16); +} +#endif + +int test_entry__notch(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_notch2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_notch2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + +#if 0 + result = test_notch4__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_notch4__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } +#endif + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_peak.c b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_peak.c new file mode 100644 index 0000000..d29f68d --- /dev/null +++ b/thirdparty/miniaudio/tests/test_filtering/ma_test_filtering_peak.c @@ -0,0 +1,170 @@ +ma_result peak_init_decoder_and_encoder(const char* pInputFilePath, const char* pOutputFilePath, ma_format format, ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + return filtering_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, 0, 0, pDecoder, pEncoder); +} + + +ma_result test_peak2__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_peak2_config peakConfig; + ma_peak2 peak; + + printf(" %s\n", pOutputFilePath); + + result = peak_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + peakConfig = ma_peak2_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 24, 0, 16000); + result = ma_peak2_init(&peakConfig, NULL, &peak); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead, &framesJustRead); + + /* Filter */ + ma_peak2_process_pcm_frames(&peak, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead, NULL); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_peak2__f32(const char* pInputFilePath) +{ + return test_peak2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/peak2_f32.wav", ma_format_f32); +} + +ma_result test_peak2__s16(const char* pInputFilePath) +{ + return test_peak2__by_format(pInputFilePath, TEST_OUTPUT_DIR"/peak2_s16.wav", ma_format_s16); +} + +#if 0 +ma_result test_peak4__by_format(const char* pInputFilePath, const char* pOutputFilePath, ma_format format) +{ + ma_result result; + ma_decoder decoder; + ma_encoder encoder; + ma_peak_config peakConfig; + ma_peak peak; + + printf(" %s\n", pOutputFilePath); + + result = peak_init_decoder_and_encoder(pInputFilePath, pOutputFilePath, format, &decoder, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + peakConfig = ma_peak_config_init(decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate, 2000, 4); + result = ma_peak_init(&peakConfig, &peak); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return result; + } + + for (;;) { + ma_uint8 tempIn[4096]; + ma_uint8 tempOut[4096]; + ma_uint64 tempCapIn = sizeof(tempIn) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 tempCapOut = sizeof(tempOut) / ma_get_bytes_per_frame(decoder.outputFormat, decoder.outputChannels); + ma_uint64 framesToRead; + ma_uint64 framesJustRead; + + framesToRead = ma_min(tempCapIn, tempCapOut); + framesJustRead = ma_decoder_read_pcm_frames(&decoder, tempIn, framesToRead); + + /* Filter */ + ma_peak_process_pcm_frames(&peak, tempOut, tempIn, framesJustRead); + + /* Write to the WAV file. */ + ma_encoder_write_pcm_frames(&encoder, tempOut, framesJustRead); + + if (framesJustRead < framesToRead) { + break; + } + } + + ma_decoder_uninit(&decoder); + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_peak4__f32(const char* pInputFilePath) +{ + return test_peak4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/peak4_f32.wav", ma_format_f32); +} + +ma_result test_peak4__s16(const char* pInputFilePath) +{ + return test_peak4__by_format(pInputFilePath, TEST_OUTPUT_DIR"/peak4_s16.wav", ma_format_s16); +} +#endif + +int test_entry__peak(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + const char* pInputFilePath; + + if (argc < 2) { + printf("No input file.\n"); + return -1; + } + + pInputFilePath = argv[1]; + + + result = test_peak2__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_peak2__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + +#if 0 + result = test_peak4__f32(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_peak4__s16(pInputFilePath); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } +#endif + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_generation/ma_test_generation.c b/thirdparty/miniaudio/tests/test_generation/ma_test_generation.c new file mode 100644 index 0000000..7d16a22 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_generation/ma_test_generation.c @@ -0,0 +1,41 @@ +#define MA_NO_DEVICE_IO +#include "../test_common/ma_test_common.c" + +#include "ma_test_generation_noise.c" +#include "ma_test_generation_waveform.c" + +int main(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + size_t iTest; + + (void)argc; + (void)argv; + + result = ma_register_test("Noise", test_entry__noise); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_register_test("Waveform", test_entry__waveform); + if (result != MA_SUCCESS) { + return result; + } + + for (iTest = 0; iTest < g_Tests.count; iTest += 1) { + printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName); + result = g_Tests.pTests[iTest].onEntry(argc, argv); + printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED"); + + if (result != 0) { + hasError = MA_TRUE; + } + } + + if (hasError) { + return -1; /* Something failed. */ + } else { + return 0; /* Everything passed. */ + } +} diff --git a/thirdparty/miniaudio/tests/test_generation/ma_test_generation_noise.c b/thirdparty/miniaudio/tests/test_generation/ma_test_generation_noise.c new file mode 100644 index 0000000..dff3f74 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_generation/ma_test_generation_noise.c @@ -0,0 +1,145 @@ + +ma_result test_noise__by_format_and_type(ma_format format, ma_noise_type type, const char* pFileName) +{ + ma_result result; + ma_noise_config noiseConfig; + ma_noise noise; + ma_encoder_config encoderConfig; + ma_encoder encoder; + ma_uint32 iFrame; + + printf(" %s\n", pFileName); + + noiseConfig = ma_noise_config_init(format, 1, type, 0, 0.1); + result = ma_noise_init(&noiseConfig, NULL, &noise); + if (result != MA_SUCCESS) { + return result; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, format, noiseConfig.channels, 48000); + result = ma_encoder_init_file(pFileName, &encoderConfig, &encoder); + if (result != MA_SUCCESS) { + return result; + } + + /* We'll do a few seconds of data. */ + for (iFrame = 0; iFrame < encoder.config.sampleRate * 10; iFrame += 1) { + ma_uint8 temp[1024]; + ma_noise_read_pcm_frames(&noise, temp, 1, NULL); + ma_encoder_write_pcm_frames(&encoder, temp, 1, NULL); + } + + ma_encoder_uninit(&encoder); + return MA_SUCCESS; +} + +ma_result test_noise__f32() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_noise__by_format_and_type(ma_format_f32, ma_noise_type_white, TEST_OUTPUT_DIR"/noise_f32_white.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_f32, ma_noise_type_pink, TEST_OUTPUT_DIR"/noise_f32_pink.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_f32, ma_noise_type_brownian, TEST_OUTPUT_DIR"/noise_f32_brownian.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_noise__s16() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_white, TEST_OUTPUT_DIR"/output/noise_s16_white.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_pink, TEST_OUTPUT_DIR"/output/noise_s16_pink.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_brownian, TEST_OUTPUT_DIR"/output/noise_s16_brownian.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_noise__u8() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + result = test_noise__by_format_and_type(ma_format_u8, ma_noise_type_white, TEST_OUTPUT_DIR"/noise_u8_white.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_u8, ma_noise_type_pink, TEST_OUTPUT_DIR"/noise_u8_pink.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__by_format_and_type(ma_format_u8, ma_noise_type_brownian, TEST_OUTPUT_DIR"/noise_u8_brownian.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +int test_entry__noise(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + (void)argc; + (void)argv; + + result = test_noise__f32(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__s16(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_noise__u8(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return -1; + } else { + return 0; + } +} diff --git a/thirdparty/miniaudio/tests/test_generation/ma_test_generation_waveform.c b/thirdparty/miniaudio/tests/test_generation/ma_test_generation_waveform.c new file mode 100644 index 0000000..3268d24 --- /dev/null +++ b/thirdparty/miniaudio/tests/test_generation/ma_test_generation_waveform.c @@ -0,0 +1,240 @@ + +ma_result test_waveform__by_format_and_type(ma_format format, ma_waveform_type type, double amplitude, const char* pFileName) +{ + ma_result result; + ma_waveform_config waveformConfig; + ma_waveform waveform; + ma_encoder_config encoderConfig; + ma_encoder encoder; + ma_uint32 iFrame; + + printf(" %s\n", pFileName); + + waveformConfig = ma_waveform_config_init(format, 2, 48000, type, amplitude, 220); + result = ma_waveform_init(&waveformConfig, &waveform); + if (result != MA_SUCCESS) { + return result; + } + + encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, waveformConfig.format, waveformConfig.channels, waveformConfig.sampleRate); + result = ma_encoder_init_file(pFileName, &encoderConfig, &encoder); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize encoder. */ + } + + /* We'll do a few seconds of data. */ + for (iFrame = 0; iFrame < waveformConfig.sampleRate * 10; iFrame += 1) { + float temp[MA_MAX_CHANNELS]; + ma_waveform_read_pcm_frames(&waveform, temp, 1, NULL); + ma_encoder_write_pcm_frames(&encoder, temp, 1, NULL); + } + + ma_encoder_uninit(&encoder); + ma_waveform_uninit(&waveform); + + return MA_SUCCESS; +} + +ma_result test_waveform__f32() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + double amplitude = 0.2; + + /* Sine */ + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_sine, +amplitude, TEST_OUTPUT_DIR"/waveform_f32_sine.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_sine, -amplitude, TEST_OUTPUT_DIR"/waveform_f32_sine_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Square */ + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_square, +amplitude, TEST_OUTPUT_DIR"/waveform_f32_square.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_square, -amplitude, TEST_OUTPUT_DIR"/waveform_f32_square_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Triangle */ + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_triangle, +amplitude, TEST_OUTPUT_DIR"/waveform_f32_triangle.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_triangle, -amplitude, TEST_OUTPUT_DIR"/waveform_f32_triangle_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Sawtooth */ + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_sawtooth, +amplitude, TEST_OUTPUT_DIR"/waveform_f32_sawtooth.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_f32, ma_waveform_type_sawtooth, -amplitude, TEST_OUTPUT_DIR"/waveform_f32_sawtooth_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_waveform__s16() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + double amplitude = 0.2; + + /* Sine */ + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_sine, +amplitude, TEST_OUTPUT_DIR"/waveform_s16_sine.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_sine, -amplitude, TEST_OUTPUT_DIR"/waveform_s16_sine_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Square */ + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_square, +amplitude, TEST_OUTPUT_DIR"/waveform_s16_square.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_square, -amplitude, TEST_OUTPUT_DIR"/waveform_s16_square_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Triangle */ + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_triangle, +amplitude, TEST_OUTPUT_DIR"/waveform_s16_triangle.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_triangle, -amplitude, TEST_OUTPUT_DIR"/waveform_s16_triangle_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Sawtooth */ + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_sawtooth, +amplitude, TEST_OUTPUT_DIR"/waveform_s16_sawtooth.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_s16, ma_waveform_type_sawtooth, -amplitude, TEST_OUTPUT_DIR"/waveform_s16_sawtooth_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +ma_result test_waveform__u8() +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + double amplitude = 0.2; + + /* Sine */ + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_sine, +amplitude, TEST_OUTPUT_DIR"/waveform_u8_sine.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_sine, -amplitude, TEST_OUTPUT_DIR"/waveform_u8_sine_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Square */ + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_square, +amplitude, TEST_OUTPUT_DIR"/waveform_u8_square.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_square, -amplitude, TEST_OUTPUT_DIR"/waveform_u8_square_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Triangle */ + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_triangle, +amplitude, TEST_OUTPUT_DIR"/waveform_u8_triangle.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_triangle, -amplitude, TEST_OUTPUT_DIR"/waveform_u8_triangle_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + /* Sawtooth */ + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_sawtooth, +amplitude, TEST_OUTPUT_DIR"/waveform_u8_sawtooth.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__by_format_and_type(ma_format_u8, ma_waveform_type_sawtooth, -amplitude, TEST_OUTPUT_DIR"/waveform_u8_sawtooth_neg.wav"); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + + if (hasError) { + return MA_ERROR; + } else { + return MA_SUCCESS; + } +} + +int test_entry__waveform(int argc, char** argv) +{ + ma_result result; + ma_bool32 hasError = MA_FALSE; + + (void)argc; + (void)argv; + + result = test_waveform__f32(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__s16(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + result = test_waveform__u8(); + if (result != MA_SUCCESS) { + hasError = MA_TRUE; + } + + if (hasError) { + return -1; + } else { + return 0; + } +} \ No newline at end of file diff --git a/thirdparty/miniaudio/tools/audioconverter/audioconverter.c b/thirdparty/miniaudio/tools/audioconverter/audioconverter.c new file mode 100644 index 0000000..83b43a0 --- /dev/null +++ b/thirdparty/miniaudio/tools/audioconverter/audioconverter.c @@ -0,0 +1,305 @@ +/* +USAGE: audioconverter [input file] [output file] [format] [channels] [rate] + +EXAMPLES: + audioconverter my_file.flac my_file.wav + audioconverter my_file.flac my_file.wav f32 44100 linear --linear-order 8 +*/ +#define _CRT_SECURE_NO_WARNINGS /* For stb_vorbis' usage of fopen() instead of fopen_s(). */ + +#define STB_VORBIS_HEADER_ONLY +#include "../../extras/stb_vorbis.c" /* Enables Vorbis decoding. */ + +#define MA_NO_DEVICE_IO +#define MA_NO_THREADING +#define MINIAUDIO_IMPLEMENTATION +#include "../../miniaudio.h" + +#include + +void print_usage() +{ + printf("USAGE: audioconverter [input file] [output file] [format] [channels] [rate]\n"); + printf(" [format] is optional and can be one of the following:\n"); + printf(" u8 8-bit unsigned integer\n"); + printf(" s16 16-bit signed integer\n"); + printf(" s24 24-bit signed integer (tightly packed)\n"); + printf(" s32 32-bit signed integer\n"); + printf(" f32 32-bit floating point\n"); + printf(" [channels] is optional and in the range of %d and %d\n", MA_MIN_CHANNELS, MA_MAX_CHANNELS); + printf(" [rate] is optional and in the range of %d and %d\n", ma_standard_sample_rate_min, ma_standard_sample_rate_max); + printf("\n"); + printf("PARAMETERS:\n"); + printf(" --linear-order [0..%d]\n", MA_MAX_FILTER_ORDER); +} + +ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder) +{ + ma_result result = MA_SUCCESS; + + MA_ASSERT(pDecoder != NULL); + MA_ASSERT(pEncoder != NULL); + + /* + All we do is read from the decoder and then write straight to the encoder. All of the necessary data conversion + will happen internally. + */ + for (;;) { + ma_uint8 pRawData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; + ma_uint64 framesReadThisIteration; + ma_uint64 framesToReadThisIteration; + + framesToReadThisIteration = sizeof(pRawData) / ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); + result = ma_decoder_read_pcm_frames(pDecoder, pRawData, framesToReadThisIteration, &framesReadThisIteration); + if (result != MA_SUCCESS) { + break; /* Reached the end, or an error occurred. */ + } + + /* At this point we have the raw data from the decoder. We now just need to write it to the encoder. */ + ma_encoder_write_pcm_frames(pEncoder, pRawData, framesReadThisIteration, NULL); + + /* Get out of the loop if we've reached the end. */ + if (framesReadThisIteration < framesToReadThisIteration) { + break; + } + } + + return result; +} + +ma_bool32 is_number(const char* str) +{ + if (str == NULL || str[0] == '\0') { + return MA_FALSE; + } + + while (str[0] != '\0') { + if (str[0] < '0' || str[0] > '9') { + return MA_FALSE; + } + + str += 1; + } + + return MA_TRUE; +} + +ma_bool32 try_parse_uint32_in_range(const char* str, ma_uint32* pValue, ma_uint32 lo, ma_uint32 hi) +{ + ma_uint32 x; + + if (!is_number(str)) { + return MA_FALSE; /* Not an integer. */ + } + + x = (ma_uint32)atoi(str); + if (x < lo || x > hi) { + return MA_FALSE; /* Out of range. */ + } + + if (pValue != NULL) { + *pValue = x; + } + + return MA_TRUE; +} + +ma_bool32 try_parse_format(const char* str, ma_format* pValue) +{ + ma_format format; + + /* */ if (strcmp(str, "u8") == 0) { + format = ma_format_u8; + } else if (strcmp(str, "s16") == 0) { + format = ma_format_s16; + } else if (strcmp(str, "s24") == 0) { + format = ma_format_s24; + } else if (strcmp(str, "s32") == 0) { + format = ma_format_s32; + } else if (strcmp(str, "f32") == 0) { + format = ma_format_f32; + } else { + return MA_FALSE; /* Not a format. */ + } + + if (pValue != NULL) { + *pValue = format; + } + + return MA_TRUE; +} + +ma_bool32 try_parse_channels(const char* str, ma_uint32* pValue) +{ + return try_parse_uint32_in_range(str, pValue, MA_MIN_CHANNELS, MA_MAX_CHANNELS); +} + +ma_bool32 try_parse_sample_rate(const char* str, ma_uint32* pValue) +{ + return try_parse_uint32_in_range(str, pValue, ma_standard_sample_rate_min, ma_standard_sample_rate_max); +} + +ma_bool32 try_parse_resample_algorithm(const char* str, ma_resample_algorithm* pValue) +{ + ma_resample_algorithm algorithm; + + /* */ if (strcmp(str, "linear") == 0) { + algorithm = ma_resample_algorithm_linear; + } else { + return MA_FALSE; /* Not a valid algorithm */ + } + + if (pValue != NULL) { + *pValue = algorithm; + } + + return MA_TRUE; +} + +int main(int argc, char** argv) +{ + ma_result result; + ma_decoder_config decoderConfig; + ma_decoder decoder; + ma_encoder_config encoderConfig; + ma_encoder encoder; + ma_encoding_format outputEncodingFormat; + ma_format format = ma_format_unknown; + ma_uint32 channels = 0; + ma_uint32 rate = 0; + ma_resample_algorithm resampleAlgorithm; + ma_uint32 linearOrder = 8; + int iarg; + const char* pOutputFilePath; + + print_usage(); + + /* Print help if requested. */ + if (argc == 2) { + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { + print_usage(); + return 0; + } + } + + if (argc < 3) { + print_usage(); + return -1; + } + + resampleAlgorithm = ma_resample_algorithm_linear; + + /* + The fourth and fifth arguments can be a format and/or rate specifier. It doesn't matter which order they are in as we can identify them by whether or + not it's a number. If it's a number we assume it's a sample rate, otherwise we assume it's a format specifier. + */ + for (iarg = 3; iarg < argc; iarg += 1) { + if (strcmp(argv[iarg], "--linear-order") == 0) { + iarg += 1; + if (iarg >= argc) { + break; + } + + if (!try_parse_uint32_in_range(argv[iarg], &linearOrder, 0, 8)) { + printf("Expecting a number between 0 and %d for --linear-order.\n", MA_MAX_FILTER_ORDER); + return -1; + } + + continue; + } + + if (try_parse_resample_algorithm(argv[iarg], &resampleAlgorithm)) { + continue; + } + + if (try_parse_format(argv[iarg], &format)) { + continue; + } + + if (try_parse_channels(argv[iarg], &channels)) { + continue; + } + + if (try_parse_sample_rate(argv[iarg], &rate)) { + continue; + } + + /* Getting here means we have an unknown parameter. */ + printf("Warning: Unknown parameter \"%s\"", argv[iarg]); + } + + /* Initialize a decoder for the input file. */ + decoderConfig = ma_decoder_config_init(format, channels, rate); + decoderConfig.resampling.algorithm = resampleAlgorithm; + decoderConfig.resampling.linear.lpfOrder = linearOrder; + + result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder); + if (result != MA_SUCCESS) { + printf("Failed to open input file. Check the file exists and the format is supported. Supported input formats:\n"); + #if defined(dr_opus_h) + printf(" Opus\n"); + #endif + #if defined(dr_mp3_h) + printf(" MP3\n"); + #endif + #if defined(dr_flac_h) + printf(" FLAC\n"); + #endif + #if defined(STB_VORBIS_INCLUDE_STB_VORBIS_H) + printf(" Vorbis\n"); + #endif + #if defined(dr_wav_h) + printf(" WAV\n"); + #endif + return (int)result; + } + + + pOutputFilePath = argv[2]; + + outputEncodingFormat = ma_encoding_format_wav; /* Wave by default in case we don't know the file extension. */ + if (ma_path_extension_equal(pOutputFilePath, "wav")) { + outputEncodingFormat = ma_encoding_format_wav; + } else { + printf("Warning: Unknown file extension \"%s\". Encoding as WAV.\n", ma_path_extension(pOutputFilePath)); + } + + /* Initialize the encoder for the output file. */ + encoderConfig = ma_encoder_config_init(outputEncodingFormat, decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate); + result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, &encoder); + if (result != MA_SUCCESS) { + ma_decoder_uninit(&decoder); + printf("Failed to open output file. Check that the directory exists and that the file is not already opened by another process. %s", ma_result_description(result)); + return -1; + } + + + /* We have our decoder and encoder ready, so now we can do the conversion. */ + result = do_conversion(&decoder, &encoder); + + + /* Done. */ + ma_encoder_uninit(&encoder); + ma_decoder_uninit(&decoder); + + return (int)result; +} + + +/* stb_vorbis implementation must come after the implementation of miniaudio. */ +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4100) /* unreferenced formal parameter */ + #pragma warning(disable:4244) /* '=': conversion from '' to '', possible loss of data */ + #pragma warning(disable:4245) /* '=': conversion from '' to '', signed/unsigned mismatch */ + #pragma warning(disable:4456) /* declaration of '' hides previous local declaration */ + #pragma warning(disable:4457) /* declaration of '' hides function parameter */ + #pragma warning(disable:4701) /* potentially uninitialized local variable '' used */ +#else +#endif +#undef STB_VORBIS_HEADER_ONLY +#include "../../extras/stb_vorbis.c" +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#else +#endif diff --git a/thirdparty/miniaudio/website/.webplate/config.webplate b/thirdparty/miniaudio/website/.webplate/config.webplate new file mode 100644 index 0000000..a52d6bb --- /dev/null +++ b/thirdparty/miniaudio/website/.webplate/config.webplate @@ -0,0 +1,5 @@ +author = "David Reid" +google-analytics-tracking-id = "UA-81135233-2" + +miniaudio-header = "{{ include \".webplate/miniaudio-header.html\" }}" +miniaudio-footer = "{{ include \".webplate/miniaudio-footer.html\" }}" \ No newline at end of file diff --git a/thirdparty/miniaudio/website/.webplate/miniaudio-footer.html b/thirdparty/miniaudio/website/.webplate/miniaudio-footer.html new file mode 100644 index 0000000..5aef1f3 --- /dev/null +++ b/thirdparty/miniaudio/website/.webplate/miniaudio-footer.html @@ -0,0 +1,16 @@ + + + + + + + + +
    + +
    + Copyright © {{ year }} David Reid
    + Developed by David Reid - mackron@gmail.com +
    + + diff --git a/thirdparty/miniaudio/website/.webplate/miniaudio-header.html b/thirdparty/miniaudio/website/.webplate/miniaudio-header.html new file mode 100644 index 0000000..208e441 --- /dev/null +++ b/thirdparty/miniaudio/website/.webplate/miniaudio-header.html @@ -0,0 +1,48 @@ + + + + miniaudio - A single file audio playback and capture library. + + + + + +{{ google-analytics }} + + + + + +
    +
    + + + + + + + + + + +
    +
    +
    \ No newline at end of file diff --git a/thirdparty/miniaudio/website/CNAME b/thirdparty/miniaudio/website/CNAME new file mode 100644 index 0000000..2bb96a0 --- /dev/null +++ b/thirdparty/miniaudio/website/CNAME @@ -0,0 +1 @@ +miniaud.io \ No newline at end of file diff --git a/thirdparty/miniaudio/website/css/main.css b/thirdparty/miniaudio/website/css/main.css new file mode 100644 index 0000000..d296014 --- /dev/null +++ b/thirdparty/miniaudio/website/css/main.css @@ -0,0 +1,195 @@ +body { + font-family:sans-serif; + font-size:11pt; + line-height:18pt; + background-color:#003800; +} + +h1,h2 { + color:#333; + line-height:0.2em; + margin-bottom:0; + padding:0; +} +h1.man { + margin-top:2em; +} +h2.man { + margin-top:1.5em; +} + +a { + text-decoration:none; + color:#28f; +} +a:hover { + text-decoration:underline; + color:#26d; +} + +.a-download { + text-decoration:none; + color:#ddd; + border:solid 1px #000; + border-radius:4px; + padding:16px 32px; + background-color:#003800; +} +.a-download:hover { + background-color:#003000; + text-decoration:none; + color:#ddd; +} + +.a-sublink { + font-size:11pt; +} + +#preview { + font-family:monospace; + font-size:10pt; + text-align:left; +} + +.footer-links { + margin: 0px; + margin-bottom: 10px; + padding: 0px; +} +.footer-links li { + display: inline; + padding: 0 2px; +} +.footer-links li:first-child { + padding-left: 0; +} + +.feature-header { + color:#666; + font-size: 24pt; + font-weight:bold; +} +.feature-header2 { + color:#444; + font-size: 1.5em; + font-weight:bold; + /*margin-bottom:1em;*/ + line-height: 1em; + text-align:left; +} + +.header-link-table { +} +.header-link-table td { + padding-right:1em; + vertical-align:center; + line-height:0; + /*border:solid 1px #f00;*/ +} +.header-link-table a { + /*color:#e0d7cf;*/ + color:#dddddd; + text-decoration:none; +} +.header-link-table a:hover { + color:#ffffff; +} + +.footer-link { + color:#e0d7cf; + text-decoration:none; +} +.footer-link:hover { + color:#ffffff; +} + + + +.mobile-main-link { + text-align:left; + background-color:#e0d7cf; + color:#036; + border-bottom:solid 1px #333; + padding-left:16px; +} +.mobile-main-link a { + display:block; + padding-top:8px; + padding-bottom:8px; + color:#036; + width:100%; + height:100%; + max-width:100%; +} + + +table.doc { + border:solid 0px #333; + border-collapse:collapse; +} + +th.doc, td.doc { + padding:0.5em; +} + +th.doc { + border:solid 1px #003800; + background-color:#003800; + color:#FFF; + text-align:left; +} + +td.doc { + border:solid 1px #666; +} + +td.doc p, th.doc p { + padding:0; + margin:0; +} + +a.doc-navigation { + display:block; + padding:0.5em; + color:#003800; + border-bottom:solid 1px #bbbbbb; +} + +a.doc-navigation:hover { + color:#fff; + background-color:#003800; + text-decoration:none; + /*border-bottom:solid 1px #003800;*/ +} + +/* +a.doc-navigation:hover { + background-color:#c5ecc5; + text-decoration:none; +} +*/ + +a.doc-navigation-active { + background-color:#cccccc; +} +a.doc-navigation-active:hover { + color:#003800; + background-color:#cccccc; +} + +a.doc-navigation-l1 { + padding:0.1em; + padding-left:1.5em; +} +a.doc-navigation-l2 { + padding:0.1em; + padding-left:3em; +} +a.doc-navigation-l3 { + padding:0.1em; + padding-left:4em; +} +a.doc-navigation-l4 { + padding:0.1em; + padding-left:5em; +} \ No newline at end of file diff --git a/thirdparty/miniaudio/website/img/Discord-Logo-White.svg b/thirdparty/miniaudio/website/img/Discord-Logo-White.svg new file mode 100644 index 0000000..4613aa9 --- /dev/null +++ b/thirdparty/miniaudio/website/img/Discord-Logo-White.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/thirdparty/miniaudio/website/img/favicon.png b/thirdparty/miniaudio/website/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..37c5650d9046d12cc57ac2d989664ec13bb29c12 GIT binary patch literal 1189 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 P3m^thS3j3^P6G$rO*z! z`7Nqn_x%1nX}9UwJ8vt$e>Y8?_@-)m`mfrPckj*o`2DHu-sd-W7f-*RQ};6S{>ufH zOe`D%3Jwhpvl|{=o;IVF6U1R?jBxna`I1Kn#Aam5W$N;ePWq+_=CC9&9#vmv>;aNs z;n>2dVVB!^b2^wK(7`G)f5tflkc5E31+@h~wy37XLo84b3#du5YNpV#KTEim=WqR5 zb}OW&Oz&)dS@-qjOV6KvJ$d*4)<3&G|NipU{_oG6o$EET+fJG7!ndI z8y-zQHB%W95U*H7=I=V8;{yqaCBQ(^N~h4W#vgCry!4H}_iNU@^D>RrESI(m-QPI- zJ<#$4Cz4EA-<;U>Dz7Ka8KCU@MyDD-E?(u;O+*7ZEWRk zU9g8DbQkWik2j}ozYR{djaPvo z^y)|KJa8zy5elH(stW7hU;`WL8S^L3ujPxfS${nJ+q3zFQQmbcx5d{4Sr$W+(A zy;&>6!(3fFTo6L8k|p9*2#vzKNI%9I|GgBl?LFQ&rK}1IMTPZ~-{OBpCWw}c5W1E< z?sFVW9lLdjJO!cgx8c|6Uh>s-2u+bm#G=({(dH*}G$Z8`?=(a=zEiCYl!`p^&R4kv zWsSdoOm@1o{i5omkK_dY*&|hLclnvpJn%QzQScsW11b z1}rO#s&a9^G^yvHCT3}Aa+$b4;*oko>+}FqxpZl1jOxa${MIBg+8r%h5aOL+Eb)C- zp~w$dZByha3w)myr9=F6QAL>|&t`qDoMek8qiRd#D<|vxf(tVZHAau>OYKeW=BSy5 zOcMXlwNhtvH#fC*)|xB|GFsI%!L8w*ymwPppRUp58llHoBxd!I_=qhHn|109`hlG> zi}g)pw5_@@ZEsM$CIOOOoP5=abA*IR{PqJ|Am`qS{<+p_GV0g0WXkZyC`@6o&6Dj2|6LV^FRG zwjOPX1!ltWDbN6LXWIfR3jlfyS|ke61lZ$gIh4D+3`Bl-8F(4^O~KiKyUl+Uk%G7k zm5IT9FqJFZvV#DD@RZP2exsVU)nFDP1v#_=UXoh}ctEL0eocH+bPv(`Ue_$r){px<`(#i5?pQa5BL=0mIu zT?sBEYY*zE8@nQ>&WNbFAo_N;#$gk{Pwh}Q%1Android robot \ No newline at end of file diff --git a/thirdparty/miniaudio/website/img/platforms/apple/apple-black.svg b/thirdparty/miniaudio/website/img/platforms/apple/apple-black.svg new file mode 100644 index 0000000..2e7254d --- /dev/null +++ b/thirdparty/miniaudio/website/img/platforms/apple/apple-black.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/thirdparty/miniaudio/website/img/platforms/bsd/freebsd.svg b/thirdparty/miniaudio/website/img/platforms/bsd/freebsd.svg new file mode 100644 index 0000000..4516c4e --- /dev/null +++ b/thirdparty/miniaudio/website/img/platforms/bsd/freebsd.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/thirdparty/miniaudio/website/img/platforms/linux/linux.svg b/thirdparty/miniaudio/website/img/platforms/linux/linux.svg new file mode 100644 index 0000000..1631768 --- /dev/null +++ b/thirdparty/miniaudio/website/img/platforms/linux/linux.svg @@ -0,0 +1,405 @@ + +image/svg+xml \ No newline at end of file diff --git a/thirdparty/miniaudio/website/img/platforms/web/html5.svg b/thirdparty/miniaudio/website/img/platforms/web/html5.svg new file mode 100644 index 0000000..0171e29 --- /dev/null +++ b/thirdparty/miniaudio/website/img/platforms/web/html5.svg @@ -0,0 +1,14 @@ + + HTML5 Logo + + + + + + + + + + + + diff --git a/thirdparty/miniaudio/website/img/platforms/windows/windows-10.svg b/thirdparty/miniaudio/website/img/platforms/windows/windows-10.svg new file mode 100644 index 0000000..1f42364 --- /dev/null +++ b/thirdparty/miniaudio/website/img/platforms/windows/windows-10.svg @@ -0,0 +1,53 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/thirdparty/miniaudio/website/img/twitter_white.png b/thirdparty/miniaudio/website/img/twitter_white.png new file mode 100644 index 0000000000000000000000000000000000000000..379e215f8f2f131451dcb010aa8108ade3fc4439 GIT binary patch literal 2292 zcma)8`9BkkAK%<2N5w)?GPjUiIoIRd%(;9infpGAhly;XlB1_Wi4f&VLQO*+j6LpS znQ|_+%5BcfF|$YCzu7W&2uCS7tB4*&S>n~!(15yp%fJ@P}Vd1`TW zpe2sEr(hR2KQv%6Z%mFK+A^M}MxPT%{x6s-2}+)RFZ_9la-UDVcNlK2xDNS+OVm_{ zKIG9<;kXl#+8G@D?{&T z(=k(Q-vm=}NHj-!YHVtHWMe{};AtQ}H^cm^+9!0Wl;_QS17(@ALSUUc*wl|3pAKTh zPCbg*L+mw$<}aB28A`(=?{i8HgwiCdcG-E@mq4z* zE~t&iH1YQ%;IZS(?~4Tsd)1h?WQIGK`9WjM3fmptW}o%eIXPwxy#5wA^9j$eRXGbB z%8F4}lP9=Nw5mUCxCx57(lC@;V;06TdE=8&wZJBh;ZzIX#fD^bV`sC9>}TD*SuKP$ z7_8hnUynx}j*4LMAn5b^{&NA!GdBBeB2@2$f0&kp$a*%Gv9YrMLVErrgx8H~E`Jc_ zm`55gYii}|F!N!RNXc}Nb+n~zKw6W&zAN}U5k-a!{P}~g+oj@%ari`_oeUTFrQd^J zWl0r}vVC>w`?nHCvZ4N>vSz(}EQgE+_65;SXPKf#-@hCoz5`wr@M6jQ(W63R6Q^}#HF{mC15ScN)p=a? z*hlbBC093-YJm%Hx%}?6Rme{a3&ls!uG6HZX7DZ7DO+H>Up*hBw-d2=1$K}~xC>bh zN^w9cOl|$je#sX*afoLK6C8TA1LRH60}N>u;@wh@l%+jxBo-lNwy-d`H5?R*^Q_3( z448_{)lqr#Z2~)@{0~k#k24I}#dY20?r)ny39g*^%pjC|2cE98qu_%3ehJc^Nef_3 zNILOm9P&NAbzyMJ6?giwsi;1A6Qh%7RUu?G^vABjYJACR!nGtXB~Z}#1s2~LoY5=WB&@+qITcB;CH zS`Cv_m>7+`eUB96=#uqcqp^G&3#h*s< zEbKRuBySF=PS^M_PaXjKL0aPBqCPu}Os#JIpu2}4RR9PIi^@@ITSihkh%kF_bmJ?m zh$Y&^<$VOV+tnUVr1@o@(%#~362xGweqYHfm!?bBH3If)9=u_2(+-N<&T$j~;c}ei8UdtC#!aAdUtnNuIrSoq(!2d) zLL8&uXZ4$38daUKw{Qz0w7+@%nEkw!{g5*?=D4csV%PFpv@aO|Wo$W$Bir5sU;;K0 zZg?YiP*(M z$b|Ey+Sa)eEf*ycrCKLxP-fLx1W(^rf$1yJn};jzeC@SfK(9R{h>ixObZ=^9YL;&5 z?i8mQszSR<1Yd-F`nW7C_s*1g5c6tuC_jR6n}GnUE!eFu8EDT+5Y|@d)WR#E>Djqc za=UwH=gAH1t$ab#zz*i5;-kH#T&=*(MErec*~7BXS{^;X%d+vVcQsP2;Y4?c$%tR1 zX9_qem#oQ)5=si{ z^!c89q0fGK-RVX+h52A3&^1}e&|WC~Jn~v5XO9XK79^`59E4G~2^=S1>j=H1poI9U zejA&epT4`I<*z^IcMK9-EyK(>=}{$&3xr3prA|{4otxPcGY;d2x8e_>4eEYc`u=V? zEhukRwr=mrH*XeYD9#kh6jOmjfwYqZu@1gx#lR`N7R zOO#}O>YhR2cjDrDXj#y_6WCv-ScHlLc<=+l!(F1+9l*{S+ zIkX)e+(NrW>m+7i6;v9u;2VC!7Jd2$`hpy>C@2N*ala&1ilpgB*B`=F@5lY8QUCw& cSIuF<1BV+UQVQIU{u2Pq(%z!x53l5Z0Y%PcZvX%Q literal 0 HcmV?d00001 diff --git a/thirdparty/miniaudio/website/index.html b/thirdparty/miniaudio/website/index.html new file mode 100644 index 0000000..8881f03 --- /dev/null +++ b/thirdparty/miniaudio/website/index.html @@ -0,0 +1,251 @@ +{{ miniaudio-header }} + + + +
    +
    +
    An audio playback and capture library for C and C++.
    + +
    + +
    +
    +
    + miniaudio is an audio playback and capture library for C and C++. It's made up of a single source + file, has no external dependencies and is released into the public domain. +
    + + + + + + + + + + + + + +
    +
    Simple Build System
    +
    + Just add to your source tree and go. There's no need to install any dependencies or development + packages, nor are there any build systems to waste time on. +
    +
    +
    Cross Platform
    +
    + miniaudio works on all the major desktop and mobile platforms, including Windows, macOS, + Linux, BSD, iOS, Android and Web (via Emscripten). +
    +
    +
    Simple API
    +
    + miniaudio has a simple, flexible and modular API that gets out of your way. +
    +
    +
    Open Source
    +
    + miniaudio is open source with a permissive license of your choice of public domain or + MIT No Attribution. +
    +
    +
    Detailed Documentation
    +
    + miniaudio has some of the best documentation + of any open source audio library and includes a suite of examples. +
    +
    +
    And Much More
    +
    + Built-in decoders, advanced mixing and effect processing, resource management, 3D spatialization, + filters, data conversion and more. +
    +
    +
    + +
    +
    Major Features
    + + + + + + + + + + + + + + + + +
    +
    Flexible and Modular API
    +
    + miniaudio gives you complete flexibility. With the low level API, just initialize a connection + to the device and send or receive raw audio data. The modular design of miniaudio allows you to + use the low level API without compromising your ability to make use of other features like the + node graph and resource manager. +
    +
    +
    Advanced Mixing and Effect Processing
    +
    + miniaudio's node graph system gives you an easy way to set up advanced mixing and effect + graphs. Each node sends it's output to another node, and so on and so forth to produce all + kinds of effects. You can even implement your own custom nodes and plug them in however you + like. Plug multiple nodes into the same input node for mixing and use the splitter node for + complex routing. +
    +
    +
    Resource Management
    +
    + The resource manager is used for simplifying the hassle of dealing with the loading and management + of your audio resources. It will reference count files so they're only loaded once and handles + streaming of large audio sources to save on memory. It can even load files asynchronously and + exposes it's job system so you can handle resource management jobs from your existing job + infrastructure. +
    +
    +
    High Level Audio Engine
    +
    + The high level audio engine in miniaudio encapsulates the resource manager and node graph to give + you an easy to use API to manage sounds within a 3D scene with spatialization. The engine is a + node graph, and each sound in the scene is a node within that graph which means you can take + advantage of miniaudio's graph based effect processing and routing infrastructure. +
    +
    +
    + +
    +
    Platforms
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    + Windows + + macOS / iOS + + Linux + + FreeBSD / + OpenBSD / + NetBSD + + Android +
    + WASAPI
    + DirectSound
    + WinMM +
    + Core Audio + + ALSA
    + PulseAudio
    + JACK +
    + OSS
    + sndio
    + audio(4) +
    + AAudio
    + OpenSL | ES +
    + + + + + + + + + +
    + + + + + Web + + + + +
    + + + + + Emscripten / WebAudio + + + + +
    +
    +
    +{{ miniaudio-footer }} diff --git a/thirdparty/minilua/.gitignore b/thirdparty/minilua/.gitignore new file mode 100644 index 0000000..e23b4bd --- /dev/null +++ b/thirdparty/minilua/.gitignore @@ -0,0 +1,4 @@ +*.tar.gz +lua +luac +lua-* diff --git a/thirdparty/minilua/LICENSE.txt b/thirdparty/minilua/LICENSE.txt new file mode 100644 index 0000000..1381e19 --- /dev/null +++ b/thirdparty/minilua/LICENSE.txt @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 1994–2019 Lua.org, PUC-Rio. +Copyright (c) 2020-2023 Eduardo Bart (https://github.com/edubart). + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/minilua/README.md b/thirdparty/minilua/README.md new file mode 100644 index 0000000..f164bcb --- /dev/null +++ b/thirdparty/minilua/README.md @@ -0,0 +1,61 @@ +# MiniLua + +This is Lua contained in a single header to be bundled in C/C++ applications with ease. +[Lua](https://www.lua.org/) is a powerful, efficient, lightweight, embeddable scripting language. + +## Example Usage + +```c +#define LUA_IMPL +#include "minilua.h" + +int main() { + lua_State *L = luaL_newstate(); + if(L == NULL) + return -1; + luaL_openlibs(L); + luaL_loadstring(L, "print 'hello world'"); + lua_call(L, 0, 0); + lua_close(L); + return 0; +} +``` + +## Usage + +Copy `minilua.h` into your C or C++ project, include it anywhere you want to use Lua API. +Then do the following in *one* C file to implement Lua: +```c +#define LUA_IMPL +#include "minilua.h" +``` + +By default it detects the system platform to use, however you can explicitly define one. + +Note that almost no modification was made in the Lua implementation code, +thus there are some C variable names that may collide with your code, +therefore it is best to declare the Lua implementation in dedicated C file. + +Optionally provide the following defines: + - `LUA_MAKE_LUA` - implement the Lua command line REPL + +## Documentation + +For documentation on how to use Lua read its [official manual](https://www.lua.org/manual/). + +## Updates + +- **13-Nov-2023**: Updated to Lua 5.4.6. +- **28-Jan-2022**: Updated to Lua 5.4.4. +- **31-Mar-2021**: Updated to Lua 5.4.3. +- **03-Dec-2020**: Updated to Lua 5.4.2. +- **27-Nov-2020**: Library created, using Lua 5.4.2-rc1. + +## Notes + +This library tries to keep up with latest official Lua release. +The header is generated using the bash script `gen.sh` all modifications done is there. + +## License + +Same license as Lua, the MIT license, see LICENSE.txt for information. diff --git a/thirdparty/minilua/gen.sh b/thirdparty/minilua/gen.sh new file mode 100644 index 0000000..16f2ed9 --- /dev/null +++ b/thirdparty/minilua/gen.sh @@ -0,0 +1,172 @@ +LUAVER=5.4.6 +LUADIR=lua-$LUAVER +LUAPKG=lua-$LUAVER.tar.gz +LUAURL=https://www.lua.org/ftp/$LUAPKG + +OUTFILE=minilua.h + +if [ ! -d "$LUADIR" ]; then + rm -rf $LUAPKG + wget $LUAURL + tar xzf $LUAPKG +fi + +rm -f $OUTFILE + +cat <> $OUTFILE +/* + minilua.h -- Lua in a single header + Project URL: https://github.com/edubart/minilua + + This is Lua $LUAVER contained in a single header to be bundled in C/C++ applications with ease. + Lua is a powerful, efficient, lightweight, embeddable scripting language. + + Do the following in *one* C file to create the implementation: + #define LUA_IMPL + + By default it detects the system platform to use, however you could explicitly define one. + + Note that almost no modification was made in the Lua implementation code, + thus there are some C variable names that may collide with your code, + therefore it is best to declare the Lua implementation in dedicated C file. + + Optionally provide the following defines: + LUA_MAKE_LUA - implement the Lua command line REPL + + LICENSE + MIT License, same as Lua, see end of file. +*/ + +/* detect system platform */ +#if !defined(LUA_USE_WINDOWS) && !defined(LUA_USE_LINUX) && !defined(LUA_USE_MACOSX) && !defined(LUA_USE_POSIX) && !defined(LUA_USE_C89) && !defined(LUA_USE_IOS) +#if defined(_WIN32) +#define LUA_USE_WINDOWS +#elif defined(__linux__) +#define LUA_USE_LINUX +#elif defined(__APPLE__) +#define LUA_USE_MACOSX +#else /* probably a POSIX system */ +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif +#endif + +EOF + +echo "#ifdef LUA_IMPL" >> $OUTFILE + echo "#define LUA_CORE" >> $OUTFILE + cat $LUADIR/src/lprefix.h >> $OUTFILE +echo "#endif /* LUA_IMPL */" >> $OUTFILE + +cat <> $OUTFILE +#ifdef __cplusplus +extern "C" { +#endif +EOF + +cat $LUADIR/src/luaconf.h >> $OUTFILE +cat $LUADIR/src/lua.h >> $OUTFILE +cat $LUADIR/src/lauxlib.h >> $OUTFILE +cat $LUADIR/src/lualib.h >> $OUTFILE + +echo "#ifdef LUA_IMPL" >> $OUTFILE + # C headers + echo "typedef struct CallInfo CallInfo;" >> $OUTFILE + cat $LUADIR/src/llimits.h >> $OUTFILE + cat $LUADIR/src/lobject.h >> $OUTFILE + cat $LUADIR/src/lmem.h >> $OUTFILE + cat $LUADIR/src/ltm.h >> $OUTFILE + cat $LUADIR/src/lstate.h >> $OUTFILE + cat $LUADIR/src/lzio.h >> $OUTFILE + cat $LUADIR/src/lopcodes.h >> $OUTFILE + cat $LUADIR/src/ldebug.h >> $OUTFILE + cat $LUADIR/src/ldo.h >> $OUTFILE + cat $LUADIR/src/lgc.h >> $OUTFILE + cat $LUADIR/src/lfunc.h >> $OUTFILE + cat $LUADIR/src/lstring.h >> $OUTFILE + cat $LUADIR/src/lundump.h >> $OUTFILE + cat $LUADIR/src/lapi.h >> $OUTFILE + cat $LUADIR/src/llex.h >> $OUTFILE + cat $LUADIR/src/ltable.h >> $OUTFILE + cat $LUADIR/src/lparser.h >> $OUTFILE + cat $LUADIR/src/lcode.h >> $OUTFILE + cat $LUADIR/src/lvm.h >> $OUTFILE + cat $LUADIR/src/lctype.h >> $OUTFILE + + # C sources + cat $LUADIR/src/lzio.c >> $OUTFILE + cat $LUADIR/src/lctype.c >> $OUTFILE + cat $LUADIR/src/lopcodes.c >> $OUTFILE + cat $LUADIR/src/lmem.c >> $OUTFILE + cat $LUADIR/src/lundump.c >> $OUTFILE + cat $LUADIR/src/ldump.c >> $OUTFILE + cat $LUADIR/src/lstate.c >> $OUTFILE + cat $LUADIR/src/lgc.c >> $OUTFILE + cat $LUADIR/src/llex.c >> $OUTFILE + cat $LUADIR/src/lcode.c >> $OUTFILE + cat $LUADIR/src/lparser.c >> $OUTFILE + cat $LUADIR/src/ldebug.c >> $OUTFILE + cat $LUADIR/src/lfunc.c >> $OUTFILE + cat $LUADIR/src/lobject.c >> $OUTFILE + cat $LUADIR/src/ltm.c >> $OUTFILE + cat $LUADIR/src/lstring.c >> $OUTFILE + cat $LUADIR/src/ltable.c >> $OUTFILE + cat $LUADIR/src/ldo.c >> $OUTFILE + cat $LUADIR/src/lvm.c >> $OUTFILE + sed -i "/#include \"ljumptab.h\"/r $LUADIR/src/ljumptab.h" $OUTFILE + cat $LUADIR/src/lapi.c >> $OUTFILE + cat $LUADIR/src/lauxlib.c >> $OUTFILE + cat $LUADIR/src/lbaselib.c >> $OUTFILE + cat $LUADIR/src/lcorolib.c >> $OUTFILE + cat $LUADIR/src/ldblib.c >> $OUTFILE + cat $LUADIR/src/liolib.c >> $OUTFILE + cat $LUADIR/src/lmathlib.c >> $OUTFILE + cat $LUADIR/src/loadlib.c >> $OUTFILE + cat $LUADIR/src/loslib.c >> $OUTFILE + cat $LUADIR/src/lstrlib.c >> $OUTFILE + cat $LUADIR/src/ltablib.c >> $OUTFILE + cat $LUADIR/src/lutf8lib.c >> $OUTFILE + cat $LUADIR/src/linit.c >> $OUTFILE +echo "#endif /* LUA_IMPL */" >> $OUTFILE + +cat <> $OUTFILE +#ifdef __cplusplus +} +#endif +EOF + +# Implement Lua command line utility when LUA_MAKE_LUA is defined +echo "#ifdef LUA_MAKE_LUA" >> $OUTFILE +cat $LUADIR/src/lua.c >> $OUTFILE +echo "#endif /* LUA_MAKE_LUA */" >> $OUTFILE + +# Comment all include headers +sed -i 's/#include "\([^"]*\)"/\/\*#include "\1"\*\//' $OUTFILE + +cat <> $OUTFILE + +/* + MIT License + + Copyright (c) 1994–2019 Lua.org, PUC-Rio. + Copyright (c) 2020-2023 Eduardo Bart (https://github.com/edubart). + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +EOF diff --git a/thirdparty/minilua/minilua.h b/thirdparty/minilua/minilua.h new file mode 100644 index 0000000..ef06a96 --- /dev/null +++ b/thirdparty/minilua/minilua.h @@ -0,0 +1,29240 @@ +/* + minilua.h -- Lua in a single header + Project URL: https://github.com/edubart/minilua + + This is Lua 5.4.6 contained in a single header to be bundled in C/C++ applications with ease. + Lua is a powerful, efficient, lightweight, embeddable scripting language. + + Do the following in *one* C file to create the implementation: + #define LUA_IMPL + + By default it detects the system platform to use, however you could explicitly define one. + + Note that almost no modification was made in the Lua implementation code, + thus there are some C variable names that may collide with your code, + therefore it is best to declare the Lua implementation in dedicated C file. + + Optionally provide the following defines: + LUA_MAKE_LUA - implement the Lua command line REPL + + LICENSE + MIT License, same as Lua, see end of file. +*/ + +/* detect system platform */ +#if !defined(LUA_USE_WINDOWS) && !defined(LUA_USE_LINUX) && !defined(LUA_USE_MACOSX) && !defined(LUA_USE_POSIX) && !defined(LUA_USE_C89) && !defined(LUA_USE_IOS) +#if defined(_WIN32) +#define LUA_USE_WINDOWS +#elif defined(__linux__) +#define LUA_USE_LINUX +#elif defined(__APPLE__) +#define LUA_USE_MACOSX +#else /* probably a POSIX system */ +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif +#endif + +#ifdef LUA_IMPL +#define LUA_CORE +/* +** $Id: lprefix.h $ +** Definitions for Lua code that must come before any other header file +** See Copyright Notice in lua.h +*/ + +#ifndef lprefix_h +#define lprefix_h + + +/* +** Allows POSIX/XSI stuff +*/ +#if !defined(LUA_USE_C89) /* { */ + +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#elif _XOPEN_SOURCE == 0 +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#endif + +/* +** Allows manipulation of large files in gcc and some other compilers +*/ +#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#endif + +#endif /* } */ + + +/* +** Windows stuff +*/ +#if defined(_WIN32) /* { */ + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#endif + +#endif /* } */ + +#endif + +#endif /* LUA_IMPL */ +#ifdef __cplusplus +extern "C" { +#endif +/* +** $Id: luaconf.h $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef luaconf_h +#define luaconf_h + +#include +#include + + +/* +** =================================================================== +** General Configuration File for Lua +** +** Some definitions here can be changed externally, through the compiler +** (e.g., with '-D' options): They are commented out or protected +** by '#if !defined' guards. However, several other definitions +** should be changed directly here, either because they affect the +** Lua ABI (by making the changes here, you ensure that all software +** connected to Lua, such as C libraries, will be compiled with the same +** configuration); or because they are seldom changed. +** +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance restricting it to C89. +** ===================================================================== +*/ + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ +#endif + + +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#endif + + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#endif + + +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + +/* +@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. +*/ +#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Number types. These options should not be +** set externally, because any other code connected to Lua must +** use the same configuration. +** =================================================================== +*/ + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options supported +** by your C compiler. The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + + +/* Default configuration ('long long' and 'double', for 64-bit Lua) */ +#define LUA_INT_DEFAULT LUA_INT_LONGLONG +#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE + + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. +*/ +#define LUA_32BITS 0 + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS 1 +#else +#define LUA_C89_NUMBERS 0 +#endif + + +#if LUA_32BITS /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_IS32INT /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif LUA_C89_NUMBERS /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#else /* }{ */ +/* use defaults */ + +#define LUA_INT_TYPE LUA_INT_DEFAULT +#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT + +#endif /* } */ + + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +** LUA_PATH_SEP is the character that separates templates in a path. +** LUA_PATH_MARK is the string that marks the substitution points in a +** template. +** LUA_EXEC_DIR in a Windows path is replaced by the executable's +** directory. +*/ +#define LUA_PATH_SEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXEC_DIR "!" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +** Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +** C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" +#endif + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif + +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if !defined(LUA_DIRSEP) + +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* +** More often than not the libs go together with the core. +*/ +#define LUALIB_API LUA_API +#define LUAMOD_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. Not all elf targets support +** this attribute. Unfortunately, gcc does not offer a way to check +** whether the target offers that support, and those without support +** give a warning about it. To avoid these warnings, change to the +** default definition. +*/ +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) /* { */ +#define LUAI_FUNC __attribute__((visibility("internal"))) extern +#else /* }{ */ +#define LUAI_FUNC extern +#endif /* } */ + +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. +** You can define it to get all options, or change specific options +** to fit your specific needs. +*/ +#if defined(LUA_COMPAT_5_3) /* { */ + +/* +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) +*/ +#define LUA_COMPAT_MATHLIB + +/* +@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for +** manipulating other integer types (lua_pushunsigned, lua_tounsigned, +** luaL_checkint, luaL_checklong, etc.) +** (These macros were also officially removed in 5.3, but they are still +** available here.) +*/ +#define LUA_COMPAT_APIINTCASTS + + +/* +@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod +** using '__lt'. +*/ +#define LUA_COMPAT_LT_LE + + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +** (Once more, these macros were officially removed in 5.3, but they are +** still available here.) +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +#endif /* } */ + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Numbers (low-level part). +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUAI_UACNUMBER is the result of a 'default argument promotion' +@@ over a floating number. +@@ l_floatatt(x) corrects float attribute 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats. +@@ lua_number2str converts a float to a string. +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeral to a number. +*/ + + +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) \ + l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) + +/* +@@ lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_floatatt(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_floatatt(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_floatatt(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.14g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + + + +/* +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +@@ LUAI_UACINT is the result of a 'default argument promotion' +@@ over a LUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. +@@ lua_integer2str converts an integer to a string. +*/ + + +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" + +#define LUAI_UACINT LUA_INTEGER + +#define lua_integer2str(s,sz,n) \ + l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#define LUA_MAXUNSIGNED UINT_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#define LUA_MAXUNSIGNED ULONG_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#define LUA_MAXUNSIGNED ULLONG_MAX + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#define LUA_MAXUNSIGNED _UI64_MAX + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts a hexadecimal numeral to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_pointer2str converts a pointer to a readable string in a +** non-specified way. +*/ +#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) + + +/* +@@ lua_number2strx converts a float to a hexadecimal numeral. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) \ + ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include the header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + + +/* +** macros to improve jump prediction, used mostly for error handling +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) +*/ +#if !defined(luai_likely) + +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) +#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define luai_likely(x) (x) +#define luai_unlikely(x) (x) +#endif + +#endif + + +#if defined(LUA_CORE) || defined(LUA_LIB) +/* shorter names for Lua's own use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) +#endif + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(l,e) assert(e) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). +** ===================================================================== +*/ + +/* +@@ LUAI_MAXSTACK limits the size of the Lua stack. +** CHANGE it if you need a different limit. This limit is arbitrary; +** its only purpose is to stop Lua from consuming unlimited stack +** space (and to reserve some numbers for pseudo-indices). +** (It must fit into max(size_t)/32 and max(int)/2.) +*/ +#if LUAI_IS32INT +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK 15000 +#endif + + +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +** of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. +*/ +#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) + + +/* +@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure +** maximum alignment for the other items in that union. +*/ +#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l + +/* }================================================================== */ + + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + + + +#endif + +/* +** $Id: lua.h $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +/*#include "luaconf.h"*/ + + +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "4" +#define LUA_VERSION_RELEASE "6" + +#define LUA_VERSION_NUM 504 +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +/* mark for precompiled code ('Lua') */ +#define LUA_SIGNATURE "\x1bLua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** Pseudo-indices +** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +** space after that to help overflow detection) +*/ +#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTYPES 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +#define LUA_RIDX_MAINTHREAD 1 +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_LAST LUA_RIDX_GLOBALS + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** Type for warning functions +*/ +typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); + + +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); +LUA_API int (lua_closethread) (lua_State *L, lua_State *from); +LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */ + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API lua_Number (lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int n); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *name); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg, + int *nres); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** Warning-related functions +*/ +LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); +LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); + + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 +#define LUA_GCGEN 10 +#define LUA_GCINC 11 + +LUA_API int (lua_gc) (lua_State *L, int what, ...); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + +LUA_API void (lua_toclose) (lua_State *L, int idx); +LUA_API void (lua_closeslot) (lua_State *L, int idx); + + +/* +** {============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) + +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros +** =============================================================== +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) +#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) + +#endif + +#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) +#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) +#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) + +#define LUA_NUMTAGS LUA_NUMTYPES + +/* }============================================================== */ + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + +LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit); + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + size_t srclen; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + char istailcall; /* (t) */ + unsigned short ftransfer; /* (r) index of first value transferred */ + unsigned short ntransfer; /* (r) number of transferred values */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2023 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif +/* +** $Id: lauxlib.h $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +/*#include "luaconf.h"*/ +/*#include "lua.h"*/ + + +/* global table */ +#define LUA_GNAME "_G" + + +typedef struct luaL_Buffer luaL_Buffer; + + +/* extra error code for 'luaL_loadfilex' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +/* key, in the registry, for table of loaded modules */ +#define LUA_LOADED_TABLE "_LOADED" + + +/* key, in the registry, for table of preloaded loaders */ +#define LUA_PRELOAD_TABLE "_PRELOAD" + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); +LUALIB_API int (luaL_execresult) (lua_State *L, int stat); + + +/* predefined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); + +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + +LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, + const char *p, const char *r); +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, + const char *p, const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) + +#define luaL_argexpected(L,cond,arg,tname) \ + ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) + +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** Perform arithmetic operations on lua_Integer values with wrap-around +** semantics, as the Lua core does. +*/ +#define luaL_intop(op,v1,v2) \ + ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) + + +/* push the value used to represent failure/error */ +#define luaL_pushfail(L) lua_pushnil(L) + + +/* +** Internal assertions for in-house debugging +*/ +#if !defined(lua_assert) + +#if defined LUAI_ASSERT + #include + #define lua_assert(c) assert(c) +#else + #define lua_assert(c) ((void)0) +#endif + +#endif + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + union { + LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ + char b[LUAL_BUFFERSIZE]; /* initial buffer */ + } init; +}; + + +#define luaL_bufflen(bf) ((bf)->n) +#define luaL_buffaddr(bf) ((bf)->b) + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +#define luaL_buffsub(B,s) ((B)->n -= (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + + +/* +** {====================================================== +** File handles for IO library +** ======================================================= +*/ + +/* +** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +** initial structure 'luaL_Stream' (it may contain other fields +** after that initial structure). +*/ + +#define LUA_FILEHANDLE "FILE*" + + +typedef struct luaL_Stream { + FILE *f; /* stream (NULL for incompletely created streams) */ + lua_CFunction closef; /* to close stream (NULL for closed streams) */ +} luaL_Stream; + +/* }====================================================== */ + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + + +#endif + + +/* +** $Id: lualib.h $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +/*#include "lua.h"*/ + + +/* version suffix for environment variable names */ +#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR + + +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_COLIBNAME "coroutine" +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_UTF8LIBNAME "utf8" +LUAMOD_API int (luaopen_utf8) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUAMOD_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + +#endif +#ifdef LUA_IMPL +typedef struct CallInfo CallInfo; +/* +** $Id: llimits.h $ +** Limits, basic types, and some other 'installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include +#include + + +/*#include "lua.h"*/ + + +/* +** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count +** the total memory used by Lua (in bytes). Usually, 'size_t' and +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +*/ +#if defined(LUAI_MEM) /* { external definitions? */ +typedef LUAI_UMEM lu_mem; +typedef LUAI_MEM l_mem; +#elif LUAI_IS32INT /* }{ */ +typedef size_t lu_mem; +typedef ptrdiff_t l_mem; +#else /* 16-bit ints */ /* }{ */ +typedef unsigned long lu_mem; +typedef long l_mem; +#endif /* } */ + + +/* chars used as small naturals (so that 'char' is reserved for characters) */ +typedef unsigned char lu_byte; +typedef signed char ls_byte; + + +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +/* maximum size visible for Lua (must be representable in a lua_Integer) */ +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : (size_t)(LUA_MAXINTEGER)) + + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) + +#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) + + +#define MAX_INT INT_MAX /* maximum value of an int */ + + +/* +** floor of the log2 of the maximum signed value for integral type 't'. +** (That is, maximum 'n' such that '2^n' fits in the given signed type.) +*/ +#define log2maxs(t) (sizeof(t) * 8 - 2) + + +/* +** test whether an unsigned value is a power of 2 (or zero) +*/ +#define ispow2(x) (((x) & ((x) - 1)) == 0) + + +/* number of chars of a literal string without the ending \0 */ +#define LL(x) (sizeof(x)/sizeof(char) - 1) + + +/* +** conversion of pointer to unsigned integer: this is for hashing only; +** there is no problem if the integer cannot hold the whole pointer +** value. (In strict ISO C this may cause undefined behavior, but no +** actual machine seems to bother.) +*/ +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(UINTPTR_MAX) /* even in C99 this type is optional */ +#define L_P2I uintptr_t +#else /* no 'intptr'? */ +#define L_P2I uintmax_t /* use the largest available integer */ +#endif +#else /* C89 option */ +#define L_P2I size_t +#endif + +#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX)) + + + +/* types of 'usual argument conversions' for lua_Number and lua_Integer */ +typedef LUAI_UACNUMBER l_uacNumber; +typedef LUAI_UACINT l_uacInt; + + +/* +** Internal assertions for in-house debugging +*/ +#if defined LUAI_ASSERT +#undef NDEBUG +#include +#define lua_assert(c) assert(c) +#endif + +#if defined(lua_assert) +#define check_exp(c,e) (lua_assert(c), (e)) +/* to avoid problems with conditions too long */ +#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) +#else +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define lua_longassert(c) ((void)0) +#endif + +/* +** assertion for checking API calls +*/ +#if !defined(luai_apicheck) +#define luai_apicheck(l,e) ((void)l, lua_assert(e)) +#endif + +#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) + + +/* macro to avoid warnings about unused variables */ +#if !defined(UNUSED) +#define UNUSED(x) ((void)(x)) +#endif + + +/* type casts (a macro highlights casts in the code) */ +#define cast(t, exp) ((t)(exp)) + +#define cast_void(i) cast(void, (i)) +#define cast_voidp(i) cast(void *, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) +#define cast_uint(i) cast(unsigned int, (i)) +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_uchar(i) cast(unsigned char, (i)) +#define cast_char(i) cast(char, (i)) +#define cast_charp(i) cast(char *, (i)) +#define cast_sizet(i) cast(size_t, (i)) + + +/* cast a signed lua_Integer to lua_Unsigned */ +#if !defined(l_castS2U) +#define l_castS2U(i) ((lua_Unsigned)(i)) +#endif + +/* +** cast a lua_Unsigned to a signed lua_Integer; this cast is +** not strict ISO C, but two-complement architectures should +** work fine. +*/ +#if !defined(l_castU2S) +#define l_castU2S(i) ((lua_Integer)(i)) +#endif + + +/* +** non-return type +*/ +#if !defined(l_noret) + +#if defined(__GNUC__) +#define l_noret void __attribute__((noreturn)) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 +#define l_noret void __declspec(noreturn) +#else +#define l_noret void +#endif + +#endif + + +/* +** Inline functions +*/ +#if !defined(LUA_USE_C89) +#define l_inline inline +#elif defined(__GNUC__) +#define l_inline __inline__ +#else +#define l_inline /* empty */ +#endif + +#define l_sinline static l_inline + + +/* +** type for virtual-machine instructions; +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +#if LUAI_IS32INT +typedef unsigned int l_uint32; +#else +typedef unsigned long l_uint32; +#endif + +typedef l_uint32 Instruction; + + + +/* +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif + + +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ +#if !defined(MINSTRTABSIZE) +#define MINSTRTABSIZE 128 +#endif + + +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set (M == 1 +** makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 +#endif + + +/* minimum size for string buffer */ +#if !defined(LUA_MINBUFFER) +#define LUA_MINBUFFER 32 +#endif + + +/* +** Maximum depth for nested C calls, syntactical nested non-terminals, +** and other features implemented through recursion in C. (Value must +** fit in a 16-bit unsigned integer. It must also be compatible with +** the size of the C stack.) +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 200 +#endif + + +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** these macros allow user-specific actions when a thread is +** created/deleted/resumed/yielded. +*/ +#if !defined(luai_userstateopen) +#define luai_userstateopen(L) ((void)L) +#endif + +#if !defined(luai_userstateclose) +#define luai_userstateclose(L) ((void)L) +#endif + +#if !defined(luai_userstatethread) +#define luai_userstatethread(L,L1) ((void)L) +#endif + +#if !defined(luai_userstatefree) +#define luai_userstatefree(L,L1) ((void)L) +#endif + +#if !defined(luai_userstateresume) +#define luai_userstateresume(L,n) ((void)L) +#endif + +#if !defined(luai_userstateyield) +#define luai_userstateyield(L,n) ((void)L) +#endif + + + +/* +** The luai_num* macros define the primitive operations over numbers. +*/ + +/* floor division (defined as 'floor(a/b)') */ +#if !defined(luai_numidiv) +#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) +#endif + +/* float division */ +#if !defined(luai_numdiv) +#define luai_numdiv(L,a,b) ((a)/(b)) +#endif + +/* +** modulo: defined as 'a - floor(a/b)*b'; the direct computation +** using this definition has several problems with rounding errors, +** so it is better to use 'fmod'. 'fmod' gives the result of +** 'a - trunc(a/b)*b', and therefore must be corrected when +** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a +** non-integer negative result: non-integer result is equivalent to +** a non-zero remainder 'm'; negative result is equivalent to 'a' and +** 'b' with different signs, or 'm' and 'b' with different signs +** (as the result 'm' of 'fmod' has the same sign of 'a'). +*/ +#if !defined(luai_nummod) +#define luai_nummod(L,a,b,m) \ + { (void)L; (m) = l_mathop(fmod)(a,b); \ + if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); } +#endif + +/* exponentiation */ +#if !defined(luai_numpow) +#define luai_numpow(L,a,b) \ + ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b)) +#endif + +/* the others are quite standard operations */ +#if !defined(luai_numadd) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numgt(a,b) ((a)>(b)) +#define luai_numge(a,b) ((a)>=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + + + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#if !defined(HARDSTACKTESTS) +#define condmovestack(L,pre,pos) ((void)0) +#else +/* realloc stack keeping its size */ +#define condmovestack(L,pre,pos) \ + { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } +#endif + +#if !defined(HARDMEMTESTS) +#define condchangemem(L,pre,pos) ((void)0) +#else +#define condchangemem(L,pre,pos) \ + { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } } +#endif + +#endif +/* +** $Id: lobject.h $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include + + +/*#include "llimits.h"*/ +/*#include "lua.h"*/ + + +/* +** Extra types for collectable non-values +*/ +#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ +#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */ + + + +/* +** number of all possible types (including LUA_TNONE but excluding DEADKEY) +*/ +#define LUA_TOTALTYPES (LUA_TPROTO + 2) + + +/* +** tags for Tagged Values have the following use of bits: +** bits 0-3: actual tag (a LUA_T* constant) +** bits 4-5: variant bits +** bit 6: whether value is collectable +*/ + +/* add variant bits to a type */ +#define makevariant(t,v) ((t) | ((v) << 4)) + + + +/* +** Union of all Lua values +*/ +typedef union Value { + struct GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ + /* not used, but may avoid warnings for uninitialized value */ + lu_byte ub; +} Value; + + +/* +** Tagged Values. This is the basic representation of values in Lua: +** an actual value plus a tag with its type. +*/ + +#define TValuefields Value value_; lu_byte tt_ + +typedef struct TValue { + TValuefields; +} TValue; + + +#define val_(o) ((o)->value_) +#define valraw(o) (val_(o)) + + +/* raw type tag of a TValue */ +#define rawtt(o) ((o)->tt_) + +/* tag with no variants (bits 0-3) */ +#define novariant(t) ((t) & 0x0F) + +/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ +#define withvariant(t) ((t) & 0x3F) +#define ttypetag(o) withvariant(rawtt(o)) + +/* type of a TValue */ +#define ttype(o) (novariant(rawtt(o))) + + +/* Macros to test type */ +#define checktag(o,t) (rawtt(o) == (t)) +#define checktype(o,t) (ttype(o) == (t)) + + +/* Macros for internal tests */ + +/* collectable object has the same tag as the original value */ +#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt) + +/* +** Any value being manipulated by the program either is non +** collectable, or the collectable object has the right tag +** and it is not dead. The option 'L == NULL' allows other +** macros using this one to be used where L is not available. +*/ +#define checkliveness(L,obj) \ + ((void)L, lua_longassert(!iscollectable(obj) || \ + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))) + + +/* Macros to set values */ + +/* set a value's tag */ +#define settt_(o,t) ((o)->tt_=(t)) + + +/* main macro to copy values (from 'obj2' to 'obj1') */ +#define setobj(L,obj1,obj2) \ + { TValue *io1=(obj1); const TValue *io2=(obj2); \ + io1->value_ = io2->value_; settt_(io1, io2->tt_); \ + checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); } + +/* +** Different types of assignments, according to source and destination. +** (They are mostly equal now, but may be different in the future.) +*/ + +/* from stack to stack */ +#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2)) +/* to stack (not from same stack) */ +#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2) +/* from table to same table */ +#define setobjt2t setobj +/* to new object */ +#define setobj2n setobj +/* to table */ +#define setobj2t setobj + + +/* +** Entries in a Lua stack. Field 'tbclist' forms a list of all +** to-be-closed variables active in this stack. Dummy entries are +** used when the distance between two tbc variables does not fit +** in an unsigned short. They are represented by delta==0, and +** their real delta is always the maximum value that fits in +** that field. +*/ +typedef union StackValue { + TValue val; + struct { + TValuefields; + unsigned short delta; + } tbclist; +} StackValue; + + +/* index to stack elements */ +typedef StackValue *StkId; + + +/* +** When reallocating the stack, change all pointers to the stack into +** proper offsets. +*/ +typedef union { + StkId p; /* actual pointer */ + ptrdiff_t offset; /* used while the stack is being reallocated */ +} StkIdRel; + + +/* convert a 'StackValue' to a 'TValue' */ +#define s2v(o) (&(o)->val) + + + +/* +** {================================================================== +** Nil +** =================================================================== +*/ + +/* Standard nil */ +#define LUA_VNIL makevariant(LUA_TNIL, 0) + +/* Empty slot (which might be different from a slot containing nil) */ +#define LUA_VEMPTY makevariant(LUA_TNIL, 1) + +/* Value returned for a key not found in a table (absent key) */ +#define LUA_VABSTKEY makevariant(LUA_TNIL, 2) + + +/* macro to test for (any kind of) nil */ +#define ttisnil(v) checktype((v), LUA_TNIL) + + +/* macro to test for a standard nil */ +#define ttisstrictnil(o) checktag((o), LUA_VNIL) + + +#define setnilvalue(obj) settt_(obj, LUA_VNIL) + + +#define isabstkey(v) checktag((v), LUA_VABSTKEY) + + +/* +** macro to detect non-standard nils (used only in assertions) +*/ +#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v)) + + +/* +** By default, entries with any kind of nil are considered empty. +** (In any definition, values associated with absent keys must also +** be accepted as empty.) +*/ +#define isempty(v) ttisnil(v) + + +/* macro defining a value corresponding to an absent key */ +#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY + + +/* mark an entry as empty */ +#define setempty(v) settt_(v, LUA_VEMPTY) + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Booleans +** =================================================================== +*/ + + +#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0) +#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1) + +#define ttisboolean(o) checktype((o), LUA_TBOOLEAN) +#define ttisfalse(o) checktag((o), LUA_VFALSE) +#define ttistrue(o) checktag((o), LUA_VTRUE) + + +#define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) + + +#define setbfvalue(obj) settt_(obj, LUA_VFALSE) +#define setbtvalue(obj) settt_(obj, LUA_VTRUE) + +/* }================================================================== */ + + +/* +** {================================================================== +** Threads +** =================================================================== +*/ + +#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0) + +#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD)) + +#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) + +#define setthvalue(L,obj,x) \ + { TValue *io = (obj); lua_State *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \ + checkliveness(L,io); } + +#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t) + +/* }================================================================== */ + + +/* +** {================================================================== +** Collectable Objects +** =================================================================== +*/ + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked + + +/* Common type for all collectable objects */ +typedef struct GCObject { + CommonHeader; +} GCObject; + + +/* Bit mark for collectable types */ +#define BIT_ISCOLLECTABLE (1 << 6) + +#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE) + +/* mark a tag as collectable */ +#define ctb(t) ((t) | BIT_ISCOLLECTABLE) + +#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) + +#define gcvalueraw(v) ((v).gc) + +#define setgcovalue(L,obj,x) \ + { TValue *io = (obj); GCObject *i_g=(x); \ + val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Numbers +** =================================================================== +*/ + +/* Variant tags for numbers */ +#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */ +#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */ + +#define ttisnumber(o) checktype((o), LUA_TNUMBER) +#define ttisfloat(o) checktag((o), LUA_VNUMFLT) +#define ttisinteger(o) checktag((o), LUA_VNUMINT) + +#define nvalue(o) check_exp(ttisnumber(o), \ + (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) +#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) +#define ivalue(o) check_exp(ttisinteger(o), val_(o).i) + +#define fltvalueraw(v) ((v).n) +#define ivalueraw(v) ((v).i) + +#define setfltvalue(obj,x) \ + { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); } + +#define chgfltvalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } + +#define setivalue(obj,x) \ + { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); } + +#define chgivalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Strings +** =================================================================== +*/ + +/* Variant tags for strings */ +#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */ +#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */ + +#define ttisstring(o) checktype((o), LUA_TSTRING) +#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR)) +#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR)) + +#define tsvalueraw(v) (gco2ts((v).gc)) + +#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) + +#define setsvalue(L,obj,x) \ + { TValue *io = (obj); TString *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ + checkliveness(L,io); } + +/* set a string to the stack */ +#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s) + +/* set a string to a new object */ +#define setsvalue2n setsvalue + + +/* +** Header for a string value. +*/ +typedef struct TString { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + lu_byte shrlen; /* length for short strings */ + unsigned int hash; + union { + size_t lnglen; /* length for long strings */ + struct TString *hnext; /* linked list for hash table */ + } u; + char contents[1]; +} TString; + + + +/* +** Get the actual string (array of bytes) from a 'TString'. +*/ +#define getstr(ts) ((ts)->contents) + + +/* get the actual string (array of bytes) from a Lua value */ +#define svalue(o) getstr(tsvalue(o)) + +/* get string length from 'TString *s' */ +#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen) + +/* get string length from 'TValue *o' */ +#define vslen(o) tsslen(tsvalue(o)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Userdata +** =================================================================== +*/ + + +/* +** Light userdata should be a variant of userdata, but for compatibility +** reasons they are also different types. +*/ +#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0) + +#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0) + +#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA) +#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA)) + +#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) +#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) + +#define pvalueraw(v) ((v).p) + +#define setpvalue(obj,x) \ + { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); } + +#define setuvalue(L,obj,x) \ + { TValue *io = (obj); Udata *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \ + checkliveness(L,io); } + + +/* Ensures that addresses after this type are always fully aligned. */ +typedef union UValue { + TValue uv; + LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */ +} UValue; + + +/* +** Header for userdata with user values; +** memory area follows the end of this structure. +*/ +typedef struct Udata { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + GCObject *gclist; + UValue uv[1]; /* user values */ +} Udata; + + +/* +** Header for userdata with no user values. These userdata do not need +** to be gray during GC, and therefore do not need a 'gclist' field. +** To simplify, the code always use 'Udata' for both kinds of userdata, +** making sure it never accesses 'gclist' on userdata with no user values. +** This structure here is used only to compute the correct size for +** this representation. (The 'bindata' field in its end ensures correct +** alignment for binary data following this header.) +*/ +typedef struct Udata0 { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + union {LUAI_MAXALIGN;} bindata; +} Udata0; + + +/* compute the offset of the memory area of a userdata */ +#define udatamemoffset(nuv) \ + ((nuv) == 0 ? offsetof(Udata0, bindata) \ + : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) + +/* get the address of the memory block inside 'Udata' */ +#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) + +/* compute the size of a userdata */ +#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Prototypes +** =================================================================== +*/ + +#define LUA_VPROTO makevariant(LUA_TPROTO, 0) + + +/* +** Description of an upvalue for function prototypes +*/ +typedef struct Upvaldesc { + TString *name; /* upvalue name (for debug information) */ + lu_byte instack; /* whether it is in stack (register) */ + lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ + lu_byte kind; /* kind of corresponding variable */ +} Upvaldesc; + + +/* +** Description of a local variable for function prototypes +** (used for debug information) +*/ +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + +/* +** Associates the absolute line source for a given instruction ('pc'). +** The array 'lineinfo' gives, for each instruction, the difference in +** lines from the previous instruction. When that difference does not +** fit into a byte, Lua saves the absolute line for that instruction. +** (Lua also saves the absolute line periodically, to speed up the +** computation of a line number: we can use binary search in the +** absolute-line array, but we must traverse the 'lineinfo' array +** linearly to compute a line.) +*/ +typedef struct AbsLineInfo { + int pc; + int line; +} AbsLineInfo; + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + lu_byte numparams; /* number of fixed (named) parameters */ + lu_byte is_vararg; + lu_byte maxstacksize; /* number of registers needed by this function */ + int sizeupvalues; /* size of 'upvalues' */ + int sizek; /* size of 'k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of 'p' */ + int sizelocvars; + int sizeabslineinfo; /* size of 'abslineinfo' */ + int linedefined; /* debug information */ + int lastlinedefined; /* debug information */ + TValue *k; /* constants used by the function */ + Instruction *code; /* opcodes */ + struct Proto **p; /* functions defined inside the function */ + Upvaldesc *upvalues; /* upvalue information */ + ls_byte *lineinfo; /* information about source lines (debug information) */ + AbsLineInfo *abslineinfo; /* idem */ + LocVar *locvars; /* information about local variables (debug information) */ + TString *source; /* used for debug information */ + GCObject *gclist; +} Proto; + +/* }================================================================== */ + + +/* +** {================================================================== +** Functions +** =================================================================== +*/ + +#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0) + + +/* Variant tags for functions */ +#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */ +#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */ +#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */ + +#define ttisfunction(o) checktype(o, LUA_TFUNCTION) +#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL)) +#define ttislcf(o) checktag((o), LUA_VLCF) +#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL)) +#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o)) + + +#define isLfunction(o) ttisLclosure(o) + +#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) +#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) +#define fvalue(o) check_exp(ttislcf(o), val_(o).f) +#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) + +#define fvalueraw(v) ((v).f) + +#define setclLvalue(L,obj,x) \ + { TValue *io = (obj); LClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \ + checkliveness(L,io); } + +#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl) + +#define setfvalue(obj,x) \ + { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); } + +#define setclCvalue(L,obj,x) \ + { TValue *io = (obj); CClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ + checkliveness(L,io); } + + +/* +** Upvalues for Lua closures +*/ +typedef struct UpVal { + CommonHeader; + union { + TValue *p; /* points to stack or to its own value */ + ptrdiff_t offset; /* used while the stack is being reallocated */ + } v; + union { + struct { /* (when open) */ + struct UpVal *next; /* linked list */ + struct UpVal **previous; + } open; + TValue value; /* the value (when closed) */ + } u; +} UpVal; + + + +#define ClosureHeader \ + CommonHeader; lu_byte nupvalues; GCObject *gclist + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; /* list of upvalues */ +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; /* list of upvalues */ +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define getproto(o) (clLvalue(o)->p) + +/* }================================================================== */ + + +/* +** {================================================================== +** Tables +** =================================================================== +*/ + +#define LUA_VTABLE makevariant(LUA_TTABLE, 0) + +#define ttistable(o) checktag((o), ctb(LUA_VTABLE)) + +#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) + +#define sethvalue(L,obj,x) \ + { TValue *io = (obj); Table *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \ + checkliveness(L,io); } + +#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h) + + +/* +** Nodes for Hash tables: A pack of two TValue's (key-value pairs) +** plus a 'next' field to link colliding entries. The distribution +** of the key's fields ('key_tt' and 'key_val') not forming a proper +** 'TValue' allows for a smaller size for 'Node' both in 4-byte +** and 8-byte alignments. +*/ +typedef union Node { + struct NodeKey { + TValuefields; /* fields for value */ + lu_byte key_tt; /* key type */ + int next; /* for chaining */ + Value key_val; /* key value */ + } u; + TValue i_val; /* direct access to node's value as a proper 'TValue' */ +} Node; + + +/* copy a value into a key */ +#define setnodekey(L,node,obj) \ + { Node *n_=(node); const TValue *io_=(obj); \ + n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \ + checkliveness(L,io_); } + + +/* copy a value from a key */ +#define getnodekey(L,obj,node) \ + { TValue *io_=(obj); const Node *n_=(node); \ + io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \ + checkliveness(L,io_); } + + +/* +** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the +** real size of 'array'. Otherwise, the real size of 'array' is the +** smallest power of two not smaller than 'alimit' (or zero iff 'alimit' +** is zero); 'alimit' is then used as a hint for #t. +*/ + +#define BITRAS (1 << 7) +#define isrealasize(t) (!((t)->flags & BITRAS)) +#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS)) +#define setnorealasize(t) ((t)->flags |= BITRAS) + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

    u.key_tt) +#define keyval(node) ((node)->u.key_val) + +#define keyisnil(node) (keytt(node) == LUA_TNIL) +#define keyisinteger(node) (keytt(node) == LUA_VNUMINT) +#define keyival(node) (keyval(node).i) +#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR)) +#define keystrval(node) (gco2ts(keyval(node).gc)) + +#define setnilkey(node) (keytt(node) = LUA_TNIL) + +#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) + +#define gckey(n) (keyval(n).gc) +#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) + + +/* +** Dead keys in tables have the tag DEADKEY but keep their original +** gcvalue. This distinguishes them from regular keys but allows them to +** be found when searched in a special way. ('next' needs that to find +** keys removed from a table during a traversal.) +*/ +#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) +#define keyisdead(node) (keytt(node) == LUA_TDEADKEY) + +/* }================================================================== */ + + + +/* +** 'module' operation for hashing (size is always a power of 2) +*/ +#define lmod(s,size) \ + (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1))))) + + +#define twoto(x) (1<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) + + +/* size of buffer for 'luaO_utf8esc' function */ +#define UTF8BUFFSZ 8 + +LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); +LUAI_FUNC int luaO_ceillog2 (unsigned int x); +LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, + const TValue *p2, TValue *res); +LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, + const TValue *p2, StkId res); +LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); +LUAI_FUNC int luaO_hexavalue (int c); +LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen); + + +#endif + +/* +** $Id: lmem.h $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include + +/*#include "llimits.h"*/ +/*#include "lua.h"*/ + + +#define luaM_error(L) luaD_throw(L, LUA_ERRMEM) + + +/* +** This macro tests whether it is safe to multiply 'n' by the size of +** type 't' without overflows. Because 'e' is always constant, it avoids +** the runtime division MAX_SIZET/(e). +** (The macro is somewhat complex to avoid warnings: The 'sizeof' +** comparison avoids a runtime comparison when overflow cannot occur. +** The compiler should be able to optimize the real test by itself, but +** when it does it, it may give a warning about "comparison is always +** false due to limited range of data type"; the +1 tricks the compiler, +** avoiding this warning but also this optimization.) +*/ +#define luaM_testsize(n,e) \ + (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) + +#define luaM_checksize(L,n,e) \ + (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) + + +/* +** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that +** the result is not larger than 'n' and cannot overflow a 'size_t' +** when multiplied by the size of type 't'. (Assumes that 'n' is an +** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) +*/ +#define luaM_limitN(n,t) \ + ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ + cast_uint((MAX_SIZET/sizeof(t)))) + + +/* +** Arrays of chars do not need any test +*/ +#define luaM_reallocvchar(L,b,on,n) \ + cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + +#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) +#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) +#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) + +#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) +#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) +#define luaM_newvectorchecked(L,n,t) \ + (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) + +#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ + luaM_limitN(limit,t),e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ + cast_sizet(n) * sizeof(t)))) + +#define luaM_shrinkvector(L,v,size,fs,t) \ + ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) + +LUAI_FUNC l_noret luaM_toobig (lua_State *L); + +/* not to be called directly */ +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, + int *size, int size_elem, int limit, + const char *what); +LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, + int final_n, int size_elem); +LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); + +#endif + +/* +** $Id: ltm.h $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" and "ORDER OP" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_LEN, + TM_EQ, /* last tag method with fast access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_MOD, + TM_POW, + TM_DIV, + TM_IDIV, + TM_BAND, + TM_BOR, + TM_BXOR, + TM_SHL, + TM_SHR, + TM_UNM, + TM_BNOT, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_CLOSE, + TM_N /* number of elements in the enum */ +} TMS; + + +/* +** Mask with 1 in all fast-access methods. A 1 in any of these bits +** in the flag of a (meta)table means the metatable does not have the +** corresponding metamethod field. (Bit 7 of the flag is used for +** 'isrealasize'.) +*/ +#define maskflags (~(~0u << (TM_EQ + 1))) + + +/* +** Test whether there is no tagmethod. +** (Because tagmethods use raw accesses, the result may be an "empty" nil.) +*/ +#define notm(tm) ttisnil(tm) + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +#define ttypename(x) luaT_typenames_[(x) + 1] + +LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) + + +LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3); +LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f, + const TValue *p1, const TValue *p2, StkId p3); +LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC void luaT_tryconcatTM (lua_State *L); +LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1, + const TValue *p2, int inv, StkId res, TMS event); +LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int inv, StkId res, TMS event); +LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, + const TValue *p2, TMS event); +LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int inv, int isfloat, TMS event); + +LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, + CallInfo *ci, const Proto *p); +LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci, + StkId where, int wanted); + + +#endif +/* +** $Id: lstate.h $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +/*#include "lua.h"*/ + + +/* Some header files included here need this definition */ +typedef struct CallInfo CallInfo; + + +/*#include "lobject.h"*/ +/*#include "ltm.h"*/ +/*#include "lzio.h"*/ + + +/* +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed, so all objects always +** belong to one (and only one) of these lists, using field 'next' of +** the 'CommonHeader' for the link: +** +** 'allgc': all objects not marked for finalization; +** 'finobj': all objects marked for finalization; +** 'tobefnz': all objects ready to be finalized; +** 'fixedgc': all objects that are not to be collected (currently +** only small strings, such as reserved words). +** +** For the generational collector, some of these lists have marks for +** generations. Each mark points to the first element in the list for +** that particular generation; that generation goes until the next mark. +** +** 'allgc' -> 'survival': new objects; +** 'survival' -> 'old': objects that survived one collection; +** 'old1' -> 'reallyold': objects that became old in last collection; +** 'reallyold' -> NULL: objects old for more than one cycle. +** +** 'finobj' -> 'finobjsur': new objects marked for finalization; +** 'finobjsur' -> 'finobjold1': survived """"; +** 'finobjold1' -> 'finobjrold': just old """"; +** 'finobjrold' -> NULL: really old """". +** +** All lists can contain elements older than their main ages, due +** to 'luaC_checkfinalizer' and 'udata2finalize', which move +** objects between the normal lists and the "marked for finalization" +** lists. Moreover, barriers can age young objects in young lists as +** OLD0, which then become OLD1. However, a list never contains +** elements younger than their main ages. +** +** The generational collector also uses a pointer 'firstold1', which +** points to the first OLD1 object in the list. It is used to optimize +** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' +** and 'reallyold', but often the list has no OLD1 objects or they are +** after 'old1'.) Note the difference between it and 'old1': +** 'firstold1': no OLD1 objects before this point; there can be all +** ages after it. +** 'old1': no objects younger than OLD1 after this point. +*/ + +/* +** Moreover, there is another set of lists that control gray objects. +** These lists are linked by fields 'gclist'. (All objects that +** can become gray have such a field. The field is not the same +** in all objects, but it always has this name.) Any gray object +** must belong to one of these lists, and all objects in these lists +** must be gray (with two exceptions explained below): +** +** 'gray': regular gray objects, still waiting to be visited. +** 'grayagain': objects that must be revisited at the atomic phase. +** That includes +** - black objects got in a write barrier; +** - all kinds of weak tables during propagation phase; +** - all threads. +** 'weak': tables with weak values to be cleared; +** 'ephemeron': ephemeron tables with white->white entries; +** 'allweak': tables with weak keys and/or weak values to be cleared. +** +** The exceptions to that "gray rule" are: +** - TOUCHED2 objects in generational mode stay in a gray list (because +** they must be visited again at the end of the cycle), but they are +** marked black because assignments to them must activate barriers (to +** move them back to TOUCHED1). +** - Open upvales are kept gray to avoid barriers, but they stay out +** of gray lists. (They don't even have a 'gclist' field.) +*/ + + + +/* +** About 'nCcalls': This count has two parts: the lower 16 bits counts +** the number of recursive invocations in the C stack; the higher +** 16 bits counts the number of non-yieldable calls in the stack. +** (They are together so that we can change and save both with one +** instruction.) +*/ + + +/* true if this thread does not have non-yieldable calls in the stack */ +#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) + +/* real number of C calls */ +#define getCcalls(L) ((L)->nCcalls & 0xffff) + + +/* Increment the number of non-yieldable calls */ +#define incnny(L) ((L)->nCcalls += 0x10000) + +/* Decrement the number of non-yieldable calls */ +#define decnny(L) ((L)->nCcalls -= 0x10000) + +/* Non-yieldable call increment */ +#define nyci (0x10000 | 1) + + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* +** Atomic type (relative to signals) to better ensure that 'lua_sethook' +** is thread safe +*/ +#if !defined(l_signalT) +#include +#define l_signalT sig_atomic_t +#endif + + +/* +** Extra stack space to handle TM calls and some other extras. This +** space is not included in 'stack_last'. It is used only to avoid stack +** checks, either because the element will be promptly popped or because +** there will be a stack check soon after the push. Function frames +** never use this extra space, so it does not need to be kept clean. +*/ +#define EXTRA_STACK 5 + + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + +#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) + + +/* kinds of Garbage Collection */ +#define KGC_INC 0 /* incremental gc */ +#define KGC_GEN 1 /* generational gc */ + + +typedef struct stringtable { + TString **hash; + int nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** Information about a call. +** About union 'u': +** - field 'l' is used only for Lua functions; +** - field 'c' is used only for C functions. +** About union 'u2': +** - field 'funcidx' is used only by C functions while doing a +** protected call; +** - field 'nyield' is used only while a function is "doing" an +** yield (from the yield until the next resume); +** - field 'nres' is used only while closing tbc variables when +** returning from a function; +** - field 'transferinfo' is used only during call/returnhooks, +** before the function starts or after it ends. +*/ +struct CallInfo { + StkIdRel func; /* function index in the stack */ + StkIdRel top; /* top for this function */ + struct CallInfo *previous, *next; /* dynamic call link */ + union { + struct { /* only for Lua functions */ + const Instruction *savedpc; + volatile l_signalT trap; + int nextraargs; /* # of extra arguments in vararg functions */ + } l; + struct { /* only for C functions */ + lua_KFunction k; /* continuation in case of yields */ + ptrdiff_t old_errfunc; + lua_KContext ctx; /* context info. in case of yields */ + } c; + } u; + union { + int funcidx; /* called-function index */ + int nyield; /* number of values yielded */ + int nres; /* number of values returned */ + struct { /* info about transferred values (for call/return hooks) */ + unsigned short ftransfer; /* offset of first value transferred */ + unsigned short ntransfer; /* number of values transferred */ + } transferinfo; + } u2; + short nresults; /* expected number of results from this function */ + unsigned short callstatus; +}; + + +/* +** Bits in CallInfo status +*/ +#define CIST_OAH (1<<0) /* original value of 'allowhook' */ +#define CIST_C (1<<1) /* call is running a C function */ +#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ +#define CIST_HOOKED (1<<3) /* call is running a debug hook */ +#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ +#define CIST_TAIL (1<<5) /* call was tail called */ +#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ +#define CIST_FIN (1<<7) /* function "called" a finalizer */ +#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ +#define CIST_CLSRET (1<<9) /* function is closing tbc variables */ +/* Bits 10-12 are used for CIST_RECST (see below) */ +#define CIST_RECST 10 +#if defined(LUA_COMPAT_LT_LE) +#define CIST_LEQ (1<<13) /* using __lt for __le */ +#endif + + +/* +** Field CIST_RECST stores the "recover status", used to keep the error +** status while closing to-be-closed variables in coroutines, so that +** Lua can correctly resume after an yield from a __close method called +** because of an error. (Three bits are enough for error status.) +*/ +#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7) +#define setcistrecst(ci,st) \ + check_exp(((st) & 7) == (st), /* status must fit in three bits */ \ + ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \ + | ((st) << CIST_RECST))) + + +/* active function is a Lua function */ +#define isLua(ci) (!((ci)->callstatus & CIST_C)) + +/* call is running Lua code (not a hook) */ +#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) + +/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ +#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) +#define getoah(st) ((st) & CIST_OAH) + + +/* +** 'global state', shared by all threads of this state +*/ +typedef struct global_State { + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to 'frealloc' */ + l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ + l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ + lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ + lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ + stringtable strt; /* hash table for strings */ + TValue l_registry; + TValue nilvalue; /* a nil value */ + unsigned int seed; /* randomized seed for hashes */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gckind; /* kind of GC running */ + lu_byte gcstopem; /* stops emergency collections */ + lu_byte genminormul; /* control for minor generational collections */ + lu_byte genmajormul; /* control for major generational collections */ + lu_byte gcstp; /* control whether GC is running */ + lu_byte gcemergency; /* true if this is an emergency collection */ + lu_byte gcpause; /* size of pause between successive GCs */ + lu_byte gcstepmul; /* GC "speed" */ + lu_byte gcstepsize; /* (log2 of) GC granularity */ + GCObject *allgc; /* list of all collectable objects */ + GCObject **sweepgc; /* current position of sweep in list */ + GCObject *finobj; /* list of collectable objects with finalizers */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of tables with weak values */ + GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ + GCObject *allweak; /* list of all-weak tables */ + GCObject *tobefnz; /* list of userdata to be GC */ + GCObject *fixedgc; /* list of objects not to be collected */ + /* fields for generational collector */ + GCObject *survival; /* start of objects that survived one GC cycle */ + GCObject *old1; /* start of old1 objects */ + GCObject *reallyold; /* objects more than one cycle old ("really old") */ + GCObject *firstold1; /* first OLD1 object in the list (if any) */ + GCObject *finobjsur; /* list of survival objects with finalizers */ + GCObject *finobjold1; /* list of old1 objects with finalizers */ + GCObject *finobjrold; /* list of really old objects with finalizers */ + struct lua_State *twups; /* list of threads with open upvalues */ + lua_CFunction panic; /* to be called in unprotected errors */ + struct lua_State *mainthread; + TString *memerrmsg; /* message for memory-allocation errors */ + TString *tmname[TM_N]; /* array with tag-method names */ + struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ + lua_WarnFunction warnf; /* warning function */ + void *ud_warn; /* auxiliary data to 'warnf' */ +} global_State; + + +/* +** 'per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + lu_byte allowhook; + unsigned short nci; /* number of items in 'ci' list */ + StkIdRel top; /* first free slot in the stack */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + StkIdRel tbclist; /* list of to-be-closed variables */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ + int oldpc; /* last pc traced */ + int basehookcount; + int hookcount; + volatile l_signalT hookmask; +}; + + +#define G(L) (L->l_G) + +/* +** 'g->nilvalue' being a nil value flags that the state was completely +** build. +*/ +#define completestate(g) ttisnil(&g->nilvalue) + + +/* +** Union of all collectable objects (only for conversions) +** ISO C99, 6.5.2.3 p.5: +** "if a union contains several structures that share a common initial +** sequence [...], and if the union object currently contains one +** of these structures, it is permitted to inspect the common initial +** part of any of them anywhere that a declaration of the complete type +** of the union is visible." +*/ +union GCUnion { + GCObject gc; /* common header */ + struct TString ts; + struct Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct lua_State th; /* thread */ + struct UpVal upv; +}; + + +/* +** ISO C99, 6.7.2.1 p.14: +** "A pointer to a union object, suitably converted, points to each of +** its members [...], and vice versa." +*/ +#define cast_u(o) cast(union GCUnion *, (o)) + +/* macros to convert a GCObject into a specific value */ +#define gco2ts(o) \ + check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) +#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u)) +#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l)) +#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c)) +#define gco2cl(o) \ + check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) +#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) +#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) +#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) +#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) + + +/* +** macro to convert a Lua object into a GCObject +** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) +*/ +#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) + + +/* actual number of total bytes allocated */ +#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) + +LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); +LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); +LUAI_FUNC void luaE_freeCI (lua_State *L); +LUAI_FUNC void luaE_shrinkCI (lua_State *L); +LUAI_FUNC void luaE_checkcstack (lua_State *L); +LUAI_FUNC void luaE_incCstack (lua_State *L); +LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); +LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); +LUAI_FUNC int luaE_resetthread (lua_State *L, int status); + + +#endif + +/* +** $Id: lzio.h $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +/*#include "lua.h"*/ + +/*#include "lmem.h"*/ + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) + + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_buffremove(buff,i) ((buff)->n -= (i)) +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ + (buff)->buffsize, size), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; /* reader function */ + void *data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif +/* +** $Id: lopcodes.h $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +/*#include "llimits.h"*/ + + +/*=========================================================================== + We assume that instructions are unsigned 32-bit integers. + All instructions have an opcode in the first 7 bits. + Instructions can have the following formats: + + 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +iABC C(8) | B(8) |k| A(8) | Op(7) | +iABx Bx(17) | A(8) | Op(7) | +iAsBx sBx (signed)(17) | A(8) | Op(7) | +iAx Ax(25) | Op(7) | +isJ sJ (signed)(25) | Op(7) | + + A signed argument is represented in excess K: the represented value is + the written unsigned value minus K, where K is half the maximum for the + corresponding unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 8 +#define SIZE_B 8 +#define SIZE_Bx (SIZE_C + SIZE_B + 1) +#define SIZE_A 8 +#define SIZE_Ax (SIZE_Bx + SIZE_A) +#define SIZE_sJ (SIZE_Bx + SIZE_A) + +#define SIZE_OP 7 + +#define POS_OP 0 + +#define POS_A (POS_OP + SIZE_OP) +#define POS_k (POS_A + SIZE_A) +#define POS_B (POS_k + 1) +#define POS_C (POS_B + SIZE_B) + +#define POS_Bx POS_k + +#define POS_Ax POS_A + +#define POS_sJ POS_A + + +/* +** limits for opcode arguments. +** we use (signed) 'int' to manipulate most arguments, +** so they must fit in ints. +*/ + +/* Check whether type 'int' has at least 'b' bits ('b' < 32) */ +#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1) + + +#if L_INTHASBITS(SIZE_Bx) +#define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ + + +#if L_INTHASBITS(SIZE_Ax) +#define MAXARG_Ax ((1<> 1) + + +#define MAXARG_A ((1<> 1) + +#define int2sC(i) ((i) + OFFSET_sC) +#define sC2int(i) ((i) - OFFSET_sC) + + +/* creates a mask with 'n' 1 bits at position 'p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) + +/* creates a mask with 'n' 0 bits at position 'p' */ +#define MASK0(n,p) (~MASK1(n,p)) + +/* +** the following macros help to manipulate instructions +*/ + +#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>(pos)) & MASK1(size,0))) +#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ + ((cast(Instruction, v)<> sC */ +OP_SHLI,/* A B sC R[A] := sC << R[B] */ + +OP_ADD,/* A B C R[A] := R[B] + R[C] */ +OP_SUB,/* A B C R[A] := R[B] - R[C] */ +OP_MUL,/* A B C R[A] := R[B] * R[C] */ +OP_MOD,/* A B C R[A] := R[B] % R[C] */ +OP_POW,/* A B C R[A] := R[B] ^ R[C] */ +OP_DIV,/* A B C R[A] := R[B] / R[C] */ +OP_IDIV,/* A B C R[A] := R[B] // R[C] */ + +OP_BAND,/* A B C R[A] := R[B] & R[C] */ +OP_BOR,/* A B C R[A] := R[B] | R[C] */ +OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ +OP_SHL,/* A B C R[A] := R[B] << R[C] */ +OP_SHR,/* A B C R[A] := R[B] >> R[C] */ + +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ +OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ +OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ + +OP_UNM,/* A B R[A] := -R[B] */ +OP_BNOT,/* A B R[A] := ~R[B] */ +OP_NOT,/* A B R[A] := not R[B] */ +OP_LEN,/* A B R[A] := #R[B] (length operator) */ + +OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ + +OP_CLOSE,/* A close all upvalues >= R[A] */ +OP_TBC,/* A mark variable A "to be closed" */ +OP_JMP,/* sJ pc += sJ */ +OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */ +OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */ +OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */ + +OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */ +OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */ +OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */ +OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */ +OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ +OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ + +OP_TEST,/* A k if (not R[A] == k) then pc++ */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ + +OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ +OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ + +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ +OP_RETURN0,/* return */ +OP_RETURN1,/* A return R[A] */ + +OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */ +OP_FORPREP,/* A Bx ; + if not to run then pc+=Bx+1; */ + +OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ +OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ +OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ + +OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ + +OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ + +OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ + +OP_VARARGPREP,/*A (adjust vararg parameters) */ + +OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ +} OpCode; + + +#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1) + + + +/*=========================================================================== + Notes: + + (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean + value, in a code equivalent to (not cond ? false : true). (It + produces false and skips the next instruction producing true.) + + (*) Opcodes OP_MMBIN and variants follow each arithmetic and + bitwise opcode. If the operation succeeds, it skips this next + opcode. Otherwise, this opcode calls the corresponding metamethod. + + (*) Opcode OP_TESTSET is used in short-circuit expressions that need + both to jump and to produce a value, such as (a = b or c). + + (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then + 'top' is set to last_result+1, so next open instruction (OP_CALL, + OP_RETURN*, OP_SETLIST) may use 'top'. + + (*) In OP_VARARG, if (C == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to 'top'. + + (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always + OP_EXTRAARG. + + (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then + real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the + bits of C). + + (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a + power of 2) plus 1, or zero for size zero. If not k, the array size + is C. Otherwise, the array size is EXTRAARG _ C. + + (*) For comparisons, k specifies what condition the test should accept + (true or false). + + (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped + (the constant is the first operand). + + (*) All 'skips' (pc++) assume that next instruction is a jump. + + (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the + function builds upvalues, which may need to be closed. C > 0 means + the function is vararg, so that its 'func' must be corrected before + returning; in this case, (C - 1) is its number of fixed parameters. + + (*) In comparisons with an immediate operand, C signals whether the + original operand was a float. (It must be corrected in case of + metamethods.) + +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-2: op mode +** bit 3: instruction set register A +** bit 4: operator is a test (next instruction must be a jump) +** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) +** bit 6: instruction sets 'L->top' for next instruction (when C == 0) +** bit 7: instruction is an MM instruction (call a metamethod) +*/ + +LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) +#define testITMode(m) (luaP_opmodes[m] & (1 << 5)) +#define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) + +/* "out top" (set top for next instruction) */ +#define isOT(i) \ + ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \ + GET_OPCODE(i) == OP_TAILCALL) + +/* "in top" (uses top from previous instruction) */ +#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0) + +#define opmode(mm,ot,it,t,a,m) \ + (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + +#endif +/* +** $Id: ldebug.h $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +/*#include "lstate.h"*/ + + +#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) + + +/* Active Lua function (given call info) */ +#define ci_func(ci) (clLvalue(s2v((ci)->func.p))) + + +#define resethookcount(L) (L->hookcount = L->basehookcount) + +/* +** mark for entries in 'lineinfo' array that has absolute information in +** 'abslineinfo' array +*/ +#define ABSLINEINFO (-0x80) + + +/* +** MAXimum number of successive Instructions WiTHout ABSolute line +** information. (A power of two allows fast divisions.) +*/ +#if !defined(MAXIWTHABS) +#define MAXIWTHABS 128 +#endif + + +LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); +LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, + StkId *pos); +LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o); +LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, + const char *what); +LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, + const char *msg); +LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, + TString *src, int line); +LUAI_FUNC l_noret luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); + + +#endif +/* +** $Id: ldo.h $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +/*#include "llimits.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lzio.h"*/ + + +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** It also allows the running of one GC step when the stack is +** reallocated. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ +#define luaD_checkstackaux(L,n,pre,pos) \ + if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ + { pre; luaD_growstack(L, n, 1); pos; } \ + else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) + + + +#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) +#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) + + +/* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC, preserving 'p' */ +#define checkstackGCp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ + luaC_checkGC(L), /* stack grow uses memory */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* macro to check stack size and GC */ +#define checkstackGC(L,fsize) \ + luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) + + +/* type of protected functions, to be ran by 'runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode); +LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, + int fTransfer, int nTransfer); +LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); +LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta); +LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); +LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func); +LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); +LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); +LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); +LUAI_FUNC void luaD_shrinkstack (lua_State *L); +LUAI_FUNC void luaD_inctop (lua_State *L); + +LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +#endif + +/* +** $Id: lgc.h $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ + +/* +** Collectable objects may have one of three colors: white, which means +** the object is not marked; gray, which means the object is marked, but +** its references may be not marked; and black, which means that the +** object and all its references are marked. The main invariant of the +** garbage collector, while marking objects, is that a black object can +** never point to a white one. Moreover, any gray object must be in a +** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it +** can be visited again before finishing the collection cycle. (Open +** upvalues are an exception to this rule.) These lists have no meaning +** when the invariant is not being enforced (e.g., sweep phase). +*/ + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpropagate 0 +#define GCSenteratomic 1 +#define GCSatomic 2 +#define GCSswpallgc 3 +#define GCSswpfinobj 4 +#define GCSswptobefnz 5 +#define GCSswpend 6 +#define GCScallfin 7 +#define GCSpause 8 + + +#define issweepphase(g) \ + (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) + + +/* +** macro to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a collection, the sweep +** phase may break the invariant, as objects turned white may point to +** still-black objects. The invariant is restored when sweep ends and +** all objects are white again. +*/ + +#define keepinvariant(g) ((g)->gcstate <= GCSatomic) + + +/* +** some useful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast_byte(~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) + + +/* +** Layout for bit use in 'marked' field. First three bits are +** used for object "age" in generational mode. Last bit is used +** by tests. +*/ +#define WHITE0BIT 3 /* object is white (type 0) */ +#define WHITE1BIT 4 /* object is white (type 1) */ +#define BLACKBIT 5 /* object is black */ +#define FINALIZEDBIT 6 /* object has been marked for finalization */ + +#define TESTBIT 7 + + + +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) testbits((x)->marked, WHITEBITS) +#define isblack(x) testbit((x)->marked, BLACKBIT) +#define isgray(x) /* neither white nor black */ \ + (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) + +#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) + +#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) +#define isdeadm(ow,m) ((m) & (ow)) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) + +#define changewhite(x) ((x)->marked ^= WHITEBITS) +#define nw2black(x) \ + check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT)) + +#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) + + +/* object age in generational mode */ +#define G_NEW 0 /* created in current cycle */ +#define G_SURVIVAL 1 /* created in previous cycle */ +#define G_OLD0 2 /* marked old by frw. barrier in this cycle */ +#define G_OLD1 3 /* first full cycle as old */ +#define G_OLD 4 /* really old object (not to be visited) */ +#define G_TOUCHED1 5 /* old object touched this cycle */ +#define G_TOUCHED2 6 /* old object touched in previous cycle */ + +#define AGEBITS 7 /* all age bits (111) */ + +#define getage(o) ((o)->marked & AGEBITS) +#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) +#define isold(o) (getage(o) > G_SURVIVAL) + +#define changeage(o,f,t) \ + check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) + + +/* Default Values for GC parameters */ +#define LUAI_GENMAJORMUL 100 +#define LUAI_GENMINORMUL 20 + +/* wait memory to double before starting new cycle */ +#define LUAI_GCPAUSE 200 + +/* +** some gc parameters are stored divided by 4 to allow a maximum value +** up to 1023 in a 'lu_byte'. +*/ +#define getgcparam(p) ((p) * 4) +#define setgcparam(p,v) ((p) = (v) / 4) + +#define LUAI_GCMUL 100 + +/* how much to allocate before next GC step (log2) */ +#define LUAI_GCSTEPSIZE 13 /* 8 KB */ + + +/* +** Check whether the declared GC mode is generational. While in +** generational mode, the collector can go temporarily to incremental +** mode to improve performance. This is signaled by 'g->lastatomic != 0'. +*/ +#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) + + +/* +** Control when GC is running: +*/ +#define GCSTPUSR 1 /* bit true when GC stopped by user */ +#define GCSTPGC 2 /* bit true when GC stopped by itself */ +#define GCSTPCLS 4 /* bit true when closing Lua state */ +#define gcrunning(g) ((g)->gcstp == 0) + + +/* +** Does one step of collection when debt becomes positive. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos); } + +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) + + +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) + +#define luaC_barrier(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0)) + +#define luaC_objbarrierback(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0)) + +LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_freeallobjects (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); +LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, + size_t offset); +LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); +LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); + + +#endif +/* +** $Id: lfunc.h $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +/*#include "lobject.h"*/ + + +#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ + cast_int(sizeof(TValue)) * (n)) + +#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ + cast_int(sizeof(TValue *)) * (n)) + + +/* test whether thread is in 'twups' list */ +#define isintwups(L) (L->twups != L) + + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in a VM register.) +*/ +#define MAXUPVAL 255 + + +#define upisopen(up) ((up)->v.p != &(up)->u.value) + + +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) + + +/* +** maximum number of misses before giving up the cache of closures +** in prototypes +*/ +#define MAXMISS 10 + + + +/* special status to close upvalues preserving the top of the stack */ +#define CLOSEKTOP (-1) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals); +LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); +LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); +LUAI_FUNC void luaF_unlinkupval (UpVal *uv); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif +/* +** $Id: lstring.h $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + +/*#include "lgc.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ + + +/* +** Memory-allocation error message must be preallocated (it cannot +** be created after memory is exhausted) +*/ +#define MEMERRMSG "not enough memory" + + +/* +** Size of a TString: Size of the header plus space for the string +** itself (including final '\0'). +*/ +#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) + +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + + +/* +** test whether a string is a reserved word +*/ +#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0) + + +/* +** equality for short strings, which are always internalized +*/ +#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) + + +LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); +LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); +LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC void luaS_clearcache (global_State *g); +LUAI_FUNC void luaS_init (lua_State *L); +LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); + + +#endif +/* +** $Id: lundump.h $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +/*#include "llimits.h"*/ +/*#include "lobject.h"*/ +/*#include "lzio.h"*/ + + +/* data to catch conversion errors */ +#define LUAC_DATA "\x19\x93\r\n\x1a\n" + +#define LUAC_INT 0x5678 +#define LUAC_NUM cast_num(370.5) + +/* +** Encode major-minor version in one byte, one nibble for each +*/ +#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */ +#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) + +#define LUAC_FORMAT 0 /* this is the official format */ + +/* load one chunk; from lundump.c */ +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, + void* data, int strip); + +#endif +/* +** $Id: lapi.h $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +/*#include "llimits.h"*/ +/*#include "lstate.h"*/ + + +/* Increments 'L->top.p', checking for stack overflows */ +#define api_incr_top(L) {L->top.p++; \ + api_check(L, L->top.p <= L->ci->top.p, \ + "stack overflow");} + + +/* +** If a call returns too many multiple returns, the callee may not have +** stack space to accommodate all results. In this case, this macro +** increases its stack space ('L->ci->top.p'). +*/ +#define adjustresults(L,nres) \ + { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ + L->ci->top.p = L->top.p; } + + +/* Ensure the stack has at least 'n' elements */ +#define api_checknelems(L,n) \ + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") + + +/* +** To reduce the overhead of returning from C functions, the presence of +** to-be-closed variables in these functions is coded in the CallInfo's +** field 'nresults', in a way that functions with no to-be-closed variables +** with zero, one, or "all" wanted results have no overhead. Functions +** with other number of wanted results, as well as functions with +** variables to be closed, have an extra check. +*/ + +#define hastocloseCfunc(n) ((n) < LUA_MULTRET) + +/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ +#define codeNresults(n) (-(n) - 3) +#define decodeNresults(n) (-(n) - 3) + +#endif +/* +** $Id: llex.h $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include + +/*#include "lobject.h"*/ +/*#include "lzio.h"*/ + + +/* +** Single-char tokens (terminal symbols) are represented by their own +** numeric code. Other tokens start at the following value. +*/ +#define FIRST_RESERVED (UCHAR_MAX + 1) + + +#if !defined(LUA_ENV) +#define LUA_ENV "_ENV" +#endif + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, + TK_SHL, TK_SHR, + TK_DBCOLON, TK_EOS, + TK_FLT, TK_INT, TK_NAME, TK_STRING +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) + + +typedef union { + lua_Number r; + lua_Integer i; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +/* state of the lexer plus state of the parser when shared by all + functions */ +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token 'consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* current function (parser) */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + Table *h; /* to avoid collection/reuse strings */ + struct Dyndata *dyd; /* dynamic structures used by the parser */ + TString *source; /* current source name */ + TString *envn; /* environment variable name */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source, int firstchar); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC int luaX_lookahead (LexState *ls); +LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif +/* +** $Id: ltable.h $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +/*#include "lobject.h"*/ + + +#define gnode(t,i) (&(t)->node[i]) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->u.next) + + +/* +** Clear all bits of fast-access metamethods, which means that the table +** may have any of these metamethods. (First access that fails after the +** clearing will set the bit again.) +*/ +#define invalidateTMcache(t) ((t)->flags &= ~maskflags) + + +/* true when 't' is using 'dummynode' as its hash part */ +#define isdummy(t) ((t)->lastfree == NULL) + + +/* allocated size for hash nodes */ +#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) + + +/* returns the Node, given the value of a table entry */ +#define nodefromval(v) cast(Node *, (v)) + + +LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); +LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, + TValue *value); +LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key, + TValue *value); +LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, + TValue *value); +LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, + const TValue *slot, TValue *value); +LUAI_FUNC Table *luaH_new (lua_State *L); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, + unsigned int nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC lua_Unsigned luaH_getn (Table *t); +LUAI_FUNC unsigned int luaH_realasize (const Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +#endif + + +#endif +/* +** $Id: lparser.h $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +/*#include "llimits.h"*/ +/*#include "lobject.h"*/ +/*#include "lzio.h"*/ + + +/* +** Expression and variable descriptor. +** Code generation for variables and expressions can be delayed to allow +** optimizations; An 'expdesc' structure describes a potentially-delayed +** variable/expression. It has a description of its "main" value plus a +** list of conditional jumps that can also produce its value (generated +** by short-circuit operators 'and'/'or'). +*/ + +/* kinds of variables/expressions */ +typedef enum { + VVOID, /* when 'expdesc' describes the last expression of a list, + this kind means an empty list (so, no expression) */ + VNIL, /* constant nil */ + VTRUE, /* constant true */ + VFALSE, /* constant false */ + VK, /* constant in 'k'; info = index of constant in 'k' */ + VKFLT, /* floating constant; nval = numerical float value */ + VKINT, /* integer constant; ival = numerical integer value */ + VKSTR, /* string constant; strval = TString address; + (string is fixed by the lexer) */ + VNONRELOC, /* expression has its value in a fixed register; + info = result register */ + VLOCAL, /* local variable; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ + VCONST, /* compile-time variable; + info = absolute index in 'actvar.arr' */ + VINDEXED, /* indexed variable; + ind.t = table register; + ind.idx = key's R index */ + VINDEXUP, /* indexed upvalue; + ind.t = table upvalue; + ind.idx = key's K index */ + VINDEXI, /* indexed variable with constant integer; + ind.t = table register; + ind.idx = key's value */ + VINDEXSTR, /* indexed variable with literal string; + ind.t = table register; + ind.idx = key's K index */ + VJMP, /* expression is a test/comparison; + info = pc of corresponding jump instruction */ + VRELOC, /* expression can put result in any register; + info = instruction pc */ + VCALL, /* expression is a function call; info = instruction pc */ + VVARARG /* vararg expression; info = instruction pc */ +} expkind; + + +#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) +#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) + + +typedef struct expdesc { + expkind k; + union { + lua_Integer ival; /* for VKINT */ + lua_Number nval; /* for VKFLT */ + TString *strval; /* for VKSTR */ + int info; /* for generic use */ + struct { /* for indexed variables */ + short idx; /* index (R or "long" K) */ + lu_byte t; /* table (register or upvalue) */ + } ind; + struct { /* for local variables */ + lu_byte ridx; /* register holding the variable */ + unsigned short vidx; /* compiler index (in 'actvar.arr') */ + } var; + } u; + int t; /* patch list of 'exit when true' */ + int f; /* patch list of 'exit when false' */ +} expdesc; + + +/* kinds of variables */ +#define VDKREG 0 /* regular */ +#define RDKCONST 1 /* constant */ +#define RDKTOCLOSE 2 /* to-be-closed */ +#define RDKCTC 3 /* compile-time constant */ + +/* description of an active local variable */ +typedef union Vardesc { + struct { + TValuefields; /* constant value (if it is a compile-time constant) */ + lu_byte kind; + lu_byte ridx; /* register holding the variable */ + short pidx; /* index of the variable in the Proto's 'locvars' array */ + TString *name; /* variable name */ + } vd; + TValue k; /* constant value (if any) */ +} Vardesc; + + + +/* description of pending goto statements and label statements */ +typedef struct Labeldesc { + TString *name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + lu_byte nactvar; /* number of active variables in that position */ + lu_byte close; /* goto that escapes upvalues */ +} Labeldesc; + + +/* list of labels or gotos */ +typedef struct Labellist { + Labeldesc *arr; /* array */ + int n; /* number of entries in use */ + int size; /* array size */ +} Labellist; + + +/* dynamic structures used by the parser */ +typedef struct Dyndata { + struct { /* list of all active local variables */ + Vardesc *arr; + int n; + int size; + } actvar; + Labellist gt; /* list of pending gotos */ + Labellist label; /* list of active labels */ +} Dyndata; + + +/* control of blocks */ +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to 'ncode') */ + int lasttarget; /* 'label' of last 'jump label' */ + int previousline; /* last line that was saved in 'lineinfo' */ + int nk; /* number of elements in 'k' */ + int np; /* number of elements in 'p' */ + int nabslineinfo; /* number of elements in 'abslineinfo' */ + int firstlocal; /* index of first local var (in Dyndata array) */ + int firstlabel; /* index of first label (in 'dyd->label->arr') */ + short ndebugvars; /* number of elements in 'f->locvars' */ + lu_byte nactvar; /* number of active local variables */ + lu_byte nups; /* number of upvalues */ + lu_byte freereg; /* first free register */ + lu_byte iwthabs; /* instructions issued since last absolute line info */ + lu_byte needclose; /* function needs to close upvalues when returning */ +} FuncState; + + +LUAI_FUNC int luaY_nvarstack (FuncState *fs); +LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); + + +#endif +/* +** $Id: lcode.h $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +/*#include "llex.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lparser.h"*/ + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums (ORDER OP) +*/ +typedef enum BinOpr { + /* arithmetic operators */ + OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, + OPR_DIV, OPR_IDIV, + /* bitwise operators */ + OPR_BAND, OPR_BOR, OPR_BXOR, + OPR_SHL, OPR_SHR, + /* string operator */ + OPR_CONCAT, + /* comparison operators */ + OPR_EQ, OPR_LT, OPR_LE, + OPR_NE, OPR_GT, OPR_GE, + /* logical operators */ + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +/* true if operation is foldable (that is, it is arithmetic or bitwise) */ +#define foldbinop(op) ((op) <= OPR_SHR) + + +#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0) + + +typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +/* get (pointer to) instruction of given 'expdesc' */ +#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) + + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) + +LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); +LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, + int B, int C, int k); +LUAI_FUNC int luaK_isKint (expdesc *e); +LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, + expdesc *v2, int line); +LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, + int ra, int asize, int hsize); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); +LUAI_FUNC void luaK_finish (FuncState *fs); +LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg); + + +#endif +/* +** $Id: lvm.h $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +/*#include "ldo.h"*/ +/*#include "lobject.h"*/ +/*#include "ltm.h"*/ + + +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif + + +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif + + +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I F2Ieq +#endif + + +/* +** Rounding modes for float->integer coercion + */ +typedef enum { + F2Ieq, /* no rounding; accepts only integral values */ + F2Ifloor, /* takes the floor of the number */ + F2Iceil /* takes the ceil of the number */ +} F2Imod; + + +/* convert an object to a float (including string coercion) */ +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) + + +/* convert an object to a float (without string coercion) */ +#define tonumberns(o,n) \ + (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \ + (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0)) + + +/* convert an object to an integer (including string coercion) */ +#define tointeger(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointeger(o,i,LUA_FLOORN2I)) + + +/* convert an object to an integer (without string coercion) */ +#define tointegerns(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointegerns(o,i,LUA_FLOORN2I)) + + +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) + +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable': if 't' is a table and 't[k]' is present, +** return 1 with 'slot' pointing to 't[k]' (position of final result). +** Otherwise, return 0 (meaning it will have to check metamethod) +** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL +** (otherwise). 'f' is the raw get function to use. +*/ +#define luaV_fastget(L,t,k,slot,f) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = f(hvalue(t), k), /* else, do raw access */ \ + !isempty(slot))) /* result not empty? */ + + +/* +** Special case of 'luaV_fastget' for integers, inlining the fast case +** of 'luaH_getint'. +*/ +#define luaV_fastgeti(L,t,k,slot) \ + (!ttistable(t) \ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \ + ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ + !isempty(slot))) /* result not empty? */ + + +/* +** Finish a fast set operation (when fast get succeeds). In that case, +** 'slot' points to the place to put the value. +*/ +#define luaV_finishfastset(L,t,slot,v) \ + { setobj2t(L, cast(TValue *,slot), v); \ + luaC_barrierback(L, gcvalue(t), v); } + + +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + + + +LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode); +LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, + F2Imod mode); +LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode); +LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *slot); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot); +LUAI_FUNC void luaV_finishOp (lua_State *L); +LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); +LUAI_FUNC void luaV_concat (lua_State *L, int total); +LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y); +LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); +LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); + +#endif +/* +** $Id: lctype.h $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lctype_h +#define lctype_h + +/*#include "lua.h"*/ + + +/* +** WARNING: the functions defined here do not necessarily correspond +** to the similar functions in the standard C ctype.h. They are +** optimized for the specific needs of Lua. +*/ + +#if !defined(LUA_USE_CTYPE) + +#if 'A' == 65 && '0' == 48 +/* ASCII case: can use its own tables; faster and fixed */ +#define LUA_USE_CTYPE 0 +#else +/* must use standard C ctype */ +#define LUA_USE_CTYPE 1 +#endif + +#endif + + +#if !LUA_USE_CTYPE /* { */ + +#include + +/*#include "llimits.h"*/ + + +#define ALPHABIT 0 +#define DIGITBIT 1 +#define PRINTBIT 2 +#define SPACEBIT 3 +#define XDIGITBIT 4 + + +#define MASK(B) (1 << (B)) + + +/* +** add 1 to char to allow index -1 (EOZ) +*/ +#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) + +/* +** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' +*/ +#define lislalpha(c) testprop(c, MASK(ALPHABIT)) +#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) +#define lisdigit(c) testprop(c, MASK(DIGITBIT)) +#define lisspace(c) testprop(c, MASK(SPACEBIT)) +#define lisprint(c) testprop(c, MASK(PRINTBIT)) +#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) + + +/* +** In ASCII, this 'ltolower' is correct for alphabetic characters and +** for '.'. That is enough for Lua needs. ('check_exp' ensures that +** the character either is an upper-case letter or is unchanged by +** the transformation, which holds for lower-case letters and '.'.) +*/ +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ + (c) | ('A' ^ 'a')) + + +/* one entry for each character and for -1 (EOZ) */ +LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) + + +#else /* }{ */ + +/* +** use standard C ctypes +*/ + +#include + + +#define lislalpha(c) (isalpha(c) || (c) == '_') +#define lislalnum(c) (isalnum(c) || (c) == '_') +#define lisdigit(c) (isdigit(c)) +#define lisspace(c) (isspace(c)) +#define lisprint(c) (isprint(c)) +#define lisxdigit(c) (isxdigit(c)) + +#define ltolower(c) (tolower(c)) + +#endif /* } */ + +#endif + +/* +** $Id: lzio.c $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + +#define lzio_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "llimits.h"*/ +/*#include "lmem.h"*/ +/*#include "lstate.h"*/ +/*#include "lzio.h"*/ + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) + return EOZ; + z->n = size - 1; /* discount char being returned */ + z->p = buff; + return cast_uchar(*(z->p++)); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (z->n == 0) { /* no bytes in buffer? */ + if (luaZ_fill(z) == EOZ) /* try to read more */ + return n; /* no more input; return number of missing bytes */ + else { + z->n++; /* luaZ_fill consumed first byte; put it back */ + z->p--; + } + } + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* +** $Id: lctype.c $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#define lctype_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +/*#include "lctype.h"*/ + +#if !LUA_USE_CTYPE /* { */ + +#include + + +#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ +/* consider all non-ascii codepoints to be alphabetic */ +#define NONA 0x01 +#else +#define NONA 0x00 /* default */ +#endif + + +LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { + 0x00, /* EOZ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ + 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#endif /* } */ +/* +** $Id: lopcodes.c $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lopcodes_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +/*#include "lopcodes.h"*/ + + +/* ORDER OP */ + +LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* MM OT IT T A mode opcode */ + opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */ + ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */ + ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ +}; + +/* +** $Id: lmem.c $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#define lmem_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ + + + +/* +** About the realloc function: +** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** ('osize' is the old size, 'nsize' is the new size) +** +** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL. +** Particularly, frealloc(ud, NULL, 0, 0) does nothing, +** which is equivalent to free(NULL) in ISO C. +** +** - frealloc(ud, NULL, x, s) creates a new block of size 's' +** (no matter 'x'). Returns NULL if it cannot create the new block. +** +** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from +** size 'x' to size 'y'. Returns NULL if it cannot reallocate the +** block to the new size. +*/ + + +/* +** Macro to call the allocation function. +*/ +#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) + + +/* +** When an allocation fails, it will try again after an emergency +** collection, except when it cannot run a collection. The GC should +** not be called while the state is not fully built, as the collector +** is not yet fully initialized. Also, it should not be called when +** 'gcstopem' is true, because then the interpreter is in the middle of +** a collection step. +*/ +#define cantryagain(g) (completestate(g) && !g->gcstopem) + + + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail except when freeing a block (frees never +** fail) and when it cannot try again; this fail will trigger 'tryagain' +** and a full GC cycle at every allocation. +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ns > 0 && cantryagain(g)) + return NULL; /* fail */ + else /* normal allocation */ + return callfrealloc(g, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns) +#endif + + + + + +/* +** {================================================================== +** Functions to allocate/deallocate arrays for the Parser +** =================================================================== +*/ + +/* +** Minimum size for arrays during parsing, to avoid overhead of +** reallocating to size 1, then 2, and then 4. All these arrays +** will be reallocated to exact sizes or erased when parsing ends. +*/ +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, + int size_elems, int limit, const char *what) { + void *newblock; + int size = *psize; + if (nelems + 1 <= size) /* does one extra element still fit? */ + return block; /* nothing to be done */ + if (size >= limit / 2) { /* cannot double it? */ + if (l_unlikely(size >= limit)) /* cannot grow even a little? */ + luaG_runerror(L, "too many %s (limit is %d)", what, limit); + size = limit; /* still have at least one free place */ + } + else { + size *= 2; + if (size < MINSIZEARRAY) + size = MINSIZEARRAY; /* minimum size */ + } + lua_assert(nelems + 1 <= size && size <= limit); + /* 'limit' ensures that multiplication will not overflow */ + newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems, + cast_sizet(size) * size_elems); + *psize = size; /* update only when everything else is OK */ + return newblock; +} + + +/* +** In prototypes, the size of the array is also its number of +** elements (to save memory). So, if it cannot shrink an array +** to its number of elements, the only option is to raise an +** error. +*/ +void *luaM_shrinkvector_ (lua_State *L, void *block, int *size, + int final_n, int size_elem) { + void *newblock; + size_t oldsize = cast_sizet((*size) * size_elem); + size_t newsize = cast_sizet(final_n * size_elem); + lua_assert(newsize <= oldsize); + newblock = luaM_saferealloc_(L, block, oldsize, newsize); + *size = final_n; + return newblock; +} + +/* }================================================================== */ + + +l_noret luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); +} + + +/* +** Free memory +*/ +void luaM_free_ (lua_State *L, void *block, size_t osize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + callfrealloc(g, block, osize, 0); + g->GCdebt -= osize; +} + + +/* +** In case of allocation fail, this function will do an emergency +** collection to free some memory and then try the allocation again. +*/ +static void *tryagain (lua_State *L, void *block, + size_t osize, size_t nsize) { + global_State *g = G(L); + if (cantryagain(g)) { + luaC_fullgc(L, 1); /* try to free some memory... */ + return callfrealloc(g, block, osize, nsize); /* try again */ + } + else return NULL; /* cannot run an emergency collection */ +} + + +/* +** Generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + void *newblock; + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + newblock = firsttry(g, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) { + newblock = tryagain(L, block, osize, nsize); + if (newblock == NULL) /* still no memory? */ + return NULL; /* do not update 'GCdebt' */ + } + lua_assert((nsize == 0) == (newblock == NULL)); + g->GCdebt = (g->GCdebt + nsize) - osize; + return newblock; +} + + +void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, + size_t nsize) { + void *newblock = luaM_realloc_(L, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ + luaM_error(L); + return newblock; +} + + +void *luaM_malloc_ (lua_State *L, size_t size, int tag) { + if (size == 0) + return NULL; /* that's all */ + else { + global_State *g = G(L); + void *newblock = firsttry(g, NULL, tag, size); + if (l_unlikely(newblock == NULL)) { + newblock = tryagain(L, NULL, tag, size); + if (newblock == NULL) + luaM_error(L); + } + g->GCdebt += size; + return newblock; + } +} +/* +** $Id: lundump.c $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define lundump_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstring.h"*/ +/*#include "lundump.h"*/ +/*#include "lzio.h"*/ + + +#if !defined(luai_verifycode) +#define luai_verifycode(L,f) /* empty */ +#endif + + +typedef struct { + lua_State *L; + ZIO *Z; + const char *name; +} LoadState; + + +static l_noret error (LoadState *S, const char *why) { + luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why); + luaD_throw(S->L, LUA_ERRSYNTAX); +} + + +/* +** All high-level loads go through loadVector; you can change it to +** adapt to the endianness of the input +*/ +#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) + +static void loadBlock (LoadState *S, void *b, size_t size) { + if (luaZ_read(S->Z, b, size) != 0) + error(S, "truncated chunk"); +} + + +#define loadVar(S,x) loadVector(S,&x,1) + + +static lu_byte loadByte (LoadState *S) { + int b = zgetc(S->Z); + if (b == EOZ) + error(S, "truncated chunk"); + return cast_byte(b); +} + + +static size_t loadUnsigned (LoadState *S, size_t limit) { + size_t x = 0; + int b; + limit >>= 7; + do { + b = loadByte(S); + if (x >= limit) + error(S, "integer overflow"); + x = (x << 7) | (b & 0x7f); + } while ((b & 0x80) == 0); + return x; +} + + +static size_t loadSize (LoadState *S) { + return loadUnsigned(S, ~(size_t)0); +} + + +static int loadInt (LoadState *S) { + return cast_int(loadUnsigned(S, INT_MAX)); +} + + +static lua_Number loadNumber (LoadState *S) { + lua_Number x; + loadVar(S, x); + return x; +} + + +static lua_Integer loadInteger (LoadState *S) { + lua_Integer x; + loadVar(S, x); + return x; +} + + +/* +** Load a nullable string into prototype 'p'. +*/ +static TString *loadStringN (LoadState *S, Proto *p) { + lua_State *L = S->L; + TString *ts; + size_t size = loadSize(S); + if (size == 0) /* no string? */ + return NULL; + else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN]; + loadVector(S, buff, size); /* load string into buffer */ + ts = luaS_newlstr(L, buff, size); /* create string */ + } + else { /* long string */ + ts = luaS_createlngstrobj(L, size); /* create string */ + setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ + luaD_inctop(L); + loadVector(S, getstr(ts), size); /* load directly in final place */ + L->top.p--; /* pop string */ + } + luaC_objbarrier(L, p, ts); + return ts; +} + + +/* +** Load a non-nullable string into prototype 'p'. +*/ +static TString *loadString (LoadState *S, Proto *p) { + TString *st = loadStringN(S, p); + if (st == NULL) + error(S, "bad format for constant string"); + return st; +} + + +static void loadCode (LoadState *S, Proto *f) { + int n = loadInt(S); + f->code = luaM_newvectorchecked(S->L, n, Instruction); + f->sizecode = n; + loadVector(S, f->code, n); +} + + +static void loadFunction(LoadState *S, Proto *f, TString *psource); + + +static void loadConstants (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->k = luaM_newvectorchecked(S->L, n, TValue); + f->sizek = n; + for (i = 0; i < n; i++) + setnilvalue(&f->k[i]); + for (i = 0; i < n; i++) { + TValue *o = &f->k[i]; + int t = loadByte(S); + switch (t) { + case LUA_VNIL: + setnilvalue(o); + break; + case LUA_VFALSE: + setbfvalue(o); + break; + case LUA_VTRUE: + setbtvalue(o); + break; + case LUA_VNUMFLT: + setfltvalue(o, loadNumber(S)); + break; + case LUA_VNUMINT: + setivalue(o, loadInteger(S)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + setsvalue2n(S->L, o, loadString(S, f)); + break; + default: lua_assert(0); + } + } +} + + +static void loadProtos (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->p = luaM_newvectorchecked(S->L, n, Proto *); + f->sizep = n; + for (i = 0; i < n; i++) + f->p[i] = NULL; + for (i = 0; i < n; i++) { + f->p[i] = luaF_newproto(S->L); + luaC_objbarrier(S->L, f, f->p[i]); + loadFunction(S, f->p[i], f->source); + } +} + + +/* +** Load the upvalues for a function. The names must be filled first, +** because the filling of the other fields can raise read errors and +** the creation of the error message can call an emergency collection; +** in that case all prototypes must be consistent for the GC. +*/ +static void loadUpvalues (LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); + f->sizeupvalues = n; + for (i = 0; i < n; i++) /* make array valid for GC */ + f->upvalues[i].name = NULL; + for (i = 0; i < n; i++) { /* following calls can raise errors */ + f->upvalues[i].instack = loadByte(S); + f->upvalues[i].idx = loadByte(S); + f->upvalues[i].kind = loadByte(S); + } +} + + +static void loadDebug (LoadState *S, Proto *f) { + int i, n; + n = loadInt(S); + f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); + f->sizelineinfo = n; + loadVector(S, f->lineinfo, n); + n = loadInt(S); + f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); + f->sizeabslineinfo = n; + for (i = 0; i < n; i++) { + f->abslineinfo[i].pc = loadInt(S); + f->abslineinfo[i].line = loadInt(S); + } + n = loadInt(S); + f->locvars = luaM_newvectorchecked(S->L, n, LocVar); + f->sizelocvars = n; + for (i = 0; i < n; i++) + f->locvars[i].varname = NULL; + for (i = 0; i < n; i++) { + f->locvars[i].varname = loadStringN(S, f); + f->locvars[i].startpc = loadInt(S); + f->locvars[i].endpc = loadInt(S); + } + n = loadInt(S); + if (n != 0) /* does it have debug information? */ + n = f->sizeupvalues; /* must be this many */ + for (i = 0; i < n; i++) + f->upvalues[i].name = loadStringN(S, f); +} + + +static void loadFunction (LoadState *S, Proto *f, TString *psource) { + f->source = loadStringN(S, f); + if (f->source == NULL) /* no source in dump? */ + f->source = psource; /* reuse parent's source */ + f->linedefined = loadInt(S); + f->lastlinedefined = loadInt(S); + f->numparams = loadByte(S); + f->is_vararg = loadByte(S); + f->maxstacksize = loadByte(S); + loadCode(S, f); + loadConstants(S, f); + loadUpvalues(S, f); + loadProtos(S, f); + loadDebug(S, f); +} + + +static void checkliteral (LoadState *S, const char *s, const char *msg) { + char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ + size_t len = strlen(s); + loadVector(S, buff, len); + if (memcmp(s, buff, len) != 0) + error(S, msg); +} + + +static void fchecksize (LoadState *S, size_t size, const char *tname) { + if (loadByte(S) != size) + error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); +} + + +#define checksize(S,t) fchecksize(S,sizeof(t),#t) + +static void checkHeader (LoadState *S) { + /* skip 1st char (already read and checked) */ + checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); + if (loadByte(S) != LUAC_VERSION) + error(S, "version mismatch"); + if (loadByte(S) != LUAC_FORMAT) + error(S, "format mismatch"); + checkliteral(S, LUAC_DATA, "corrupted chunk"); + checksize(S, Instruction); + checksize(S, lua_Integer); + checksize(S, lua_Number); + if (loadInteger(S) != LUAC_INT) + error(S, "integer format mismatch"); + if (loadNumber(S) != LUAC_NUM) + error(S, "float format mismatch"); +} + + +/* +** Load precompiled chunk. +*/ +LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { + LoadState S; + LClosure *cl; + if (*name == '@' || *name == '=') + S.name = name + 1; + else if (*name == LUA_SIGNATURE[0]) + S.name = "binary string"; + else + S.name = name; + S.L = L; + S.Z = Z; + checkHeader(&S); + cl = luaF_newLclosure(L, loadByte(&S)); + setclLvalue2s(L, L->top.p, cl); + luaD_inctop(L); + cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + loadFunction(&S, cl->p, NULL); + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luai_verifycode(L, cl->p); + return cl; +} + +/* +** $Id: ldump.c $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define ldump_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include + +/*#include "lua.h"*/ + +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lundump.h"*/ + + +typedef struct { + lua_State *L; + lua_Writer writer; + void *data; + int strip; + int status; +} DumpState; + + +/* +** All high-level dumps go through dumpVector; you can change it to +** change the endianness of the result +*/ +#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0])) + +#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) + + +static void dumpBlock (DumpState *D, const void *b, size_t size) { + if (D->status == 0 && size > 0) { + lua_unlock(D->L); + D->status = (*D->writer)(D->L, b, size, D->data); + lua_lock(D->L); + } +} + + +#define dumpVar(D,x) dumpVector(D,&x,1) + + +static void dumpByte (DumpState *D, int y) { + lu_byte x = (lu_byte)y; + dumpVar(D, x); +} + + +/* +** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" +** rounds up the division.) +*/ +#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) + +static void dumpSize (DumpState *D, size_t x) { + lu_byte buff[DIBS]; + int n = 0; + do { + buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ + x >>= 7; + } while (x != 0); + buff[DIBS - 1] |= 0x80; /* mark last byte */ + dumpVector(D, buff + DIBS - n, n); +} + + +static void dumpInt (DumpState *D, int x) { + dumpSize(D, x); +} + + +static void dumpNumber (DumpState *D, lua_Number x) { + dumpVar(D, x); +} + + +static void dumpInteger (DumpState *D, lua_Integer x) { + dumpVar(D, x); +} + + +static void dumpString (DumpState *D, const TString *s) { + if (s == NULL) + dumpSize(D, 0); + else { + size_t size = tsslen(s); + const char *str = getstr(s); + dumpSize(D, size + 1); + dumpVector(D, str, size); + } +} + + +static void dumpCode (DumpState *D, const Proto *f) { + dumpInt(D, f->sizecode); + dumpVector(D, f->code, f->sizecode); +} + + +static void dumpFunction(DumpState *D, const Proto *f, TString *psource); + +static void dumpConstants (DumpState *D, const Proto *f) { + int i; + int n = f->sizek; + dumpInt(D, n); + for (i = 0; i < n; i++) { + const TValue *o = &f->k[i]; + int tt = ttypetag(o); + dumpByte(D, tt); + switch (tt) { + case LUA_VNUMFLT: + dumpNumber(D, fltvalue(o)); + break; + case LUA_VNUMINT: + dumpInteger(D, ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + dumpString(D, tsvalue(o)); + break; + default: + lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE); + } + } +} + + +static void dumpProtos (DumpState *D, const Proto *f) { + int i; + int n = f->sizep; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpFunction(D, f->p[i], f->source); +} + + +static void dumpUpvalues (DumpState *D, const Proto *f) { + int i, n = f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpByte(D, f->upvalues[i].instack); + dumpByte(D, f->upvalues[i].idx); + dumpByte(D, f->upvalues[i].kind); + } +} + + +static void dumpDebug (DumpState *D, const Proto *f) { + int i, n; + n = (D->strip) ? 0 : f->sizelineinfo; + dumpInt(D, n); + dumpVector(D, f->lineinfo, n); + n = (D->strip) ? 0 : f->sizeabslineinfo; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpInt(D, f->abslineinfo[i].pc); + dumpInt(D, f->abslineinfo[i].line); + } + n = (D->strip) ? 0 : f->sizelocvars; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpString(D, f->locvars[i].varname); + dumpInt(D, f->locvars[i].startpc); + dumpInt(D, f->locvars[i].endpc); + } + n = (D->strip) ? 0 : f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpString(D, f->upvalues[i].name); +} + + +static void dumpFunction (DumpState *D, const Proto *f, TString *psource) { + if (D->strip || f->source == psource) + dumpString(D, NULL); /* no debug info or same source as its parent */ + else + dumpString(D, f->source); + dumpInt(D, f->linedefined); + dumpInt(D, f->lastlinedefined); + dumpByte(D, f->numparams); + dumpByte(D, f->is_vararg); + dumpByte(D, f->maxstacksize); + dumpCode(D, f); + dumpConstants(D, f); + dumpUpvalues(D, f); + dumpProtos(D, f); + dumpDebug(D, f); +} + + +static void dumpHeader (DumpState *D) { + dumpLiteral(D, LUA_SIGNATURE); + dumpByte(D, LUAC_VERSION); + dumpByte(D, LUAC_FORMAT); + dumpLiteral(D, LUAC_DATA); + dumpByte(D, sizeof(Instruction)); + dumpByte(D, sizeof(lua_Integer)); + dumpByte(D, sizeof(lua_Number)); + dumpInteger(D, LUAC_INT); + dumpNumber(D, LUAC_NUM); +} + + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { + DumpState D; + D.L = L; + D.writer = w; + D.data = data; + D.strip = strip; + D.status = 0; + dumpHeader(&D); + dumpByte(&D, f->sizeupvalues); + dumpFunction(&D, f, NULL); + return D.status; +} + +/* +** $Id: lstate.c $ +** Global State +** See Copyright Notice in lua.h +*/ + +#define lstate_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include + +/*#include "lua.h"*/ + +/*#include "lapi.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "llex.h"*/ +/*#include "lmem.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ + + + +/* +** thread state + extra space +*/ +typedef struct LX { + lu_byte extra_[LUA_EXTRASPACE]; + lua_State l; +} LX; + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + LX l; + global_State g; +} LG; + + + +#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) + + +/* +** A macro to create a "random" seed when a state is created; +** the seed is used to randomize string hashes. +*/ +#if !defined(luai_makeseed) + +#include + +/* +** Compute an initial seed with some level of randomness. +** Rely on Address Space Layout Randomization (if present) and +** current time. +*/ +#define addbuff(b,p,e) \ + { size_t t = cast_sizet(e); \ + memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } + +static unsigned int luai_makeseed (lua_State *L) { + char buff[3 * sizeof(size_t)]; + unsigned int h = cast_uint(time(NULL)); + int p = 0; + addbuff(buff, p, L); /* heap variable */ + addbuff(buff, p, &h); /* local variable */ + addbuff(buff, p, &lua_newstate); /* public function */ + lua_assert(p == sizeof(buff)); + return luaS_hash(buff, p, h); +} + +#endif + + +/* +** set GCdebt to a new value keeping the value (totalbytes + GCdebt) +** invariant (and avoiding underflows in 'totalbytes') +*/ +void luaE_setdebt (global_State *g, l_mem debt) { + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt < tb - MAX_LMEM) + debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ + g->totalbytes = tb - debt; + g->GCdebt = debt; +} + + +LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { + UNUSED(L); UNUSED(limit); + return LUAI_MAXCCALLS; /* warning?? */ +} + + +CallInfo *luaE_extendCI (lua_State *L) { + CallInfo *ci; + lua_assert(L->ci->next == NULL); + ci = luaM_new(L, CallInfo); + lua_assert(L->ci->next == NULL); + L->ci->next = ci; + ci->previous = L->ci; + ci->next = NULL; + ci->u.l.trap = 0; + L->nci++; + return ci; +} + + +/* +** free all CallInfo structures not in use by a thread +*/ +void luaE_freeCI (lua_State *L) { + CallInfo *ci = L->ci; + CallInfo *next = ci->next; + ci->next = NULL; + while ((ci = next) != NULL) { + next = ci->next; + luaM_free(L, ci); + L->nci--; + } +} + + +/* +** free half of the CallInfo structures not in use by a thread, +** keeping the first one. +*/ +void luaE_shrinkCI (lua_State *L) { + CallInfo *ci = L->ci->next; /* first free CallInfo */ + CallInfo *next; + if (ci == NULL) + return; /* no extra elements */ + while ((next = ci->next) != NULL) { /* two extra elements? */ + CallInfo *next2 = next->next; /* next's next */ + ci->next = next2; /* remove next from the list */ + L->nci--; + luaM_free(L, next); /* free next */ + if (next2 == NULL) + break; /* no more elements */ + else { + next2->previous = ci; + ci = next2; /* continue */ + } + } +} + + +/* +** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS. +** If equal, raises an overflow error. If value is larger than +** LUAI_MAXCCALLS (which means it is handling an overflow) but +** not much larger, does not report an error (to allow overflow +** handling to work). +*/ +void luaE_checkcstack (lua_State *L) { + if (getCcalls(L) == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) + luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ +} + + +LUAI_FUNC void luaE_incCstack (lua_State *L) { + L->nCcalls++; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + luaE_checkcstack(L); +} + + +static void stack_init (lua_State *L1, lua_State *L) { + int i; CallInfo *ci; + /* initialize stack array */ + L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + L1->tbclist.p = L1->stack.p; + for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) + setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ + L1->top.p = L1->stack.p; + L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; + /* initialize first ci */ + ci = &L1->base_ci; + ci->next = ci->previous = NULL; + ci->callstatus = CIST_C; + ci->func.p = L1->top.p; + ci->u.c.k = NULL; + ci->nresults = 0; + setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ + L1->top.p++; + ci->top.p = L1->top.p + LUA_MINSTACK; + L1->ci = ci; +} + + +static void freestack (lua_State *L) { + if (L->stack.p == NULL) + return; /* stack not completely built yet */ + L->ci = &L->base_ci; /* free the entire 'ci' list */ + luaE_freeCI(L); + lua_assert(L->nci == 0); + luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ +} + + +/* +** Create registry table and its predefined values +*/ +static void init_registry (lua_State *L, global_State *g) { + /* create registry */ + Table *registry = luaH_new(L); + sethvalue(L, &g->l_registry, registry); + luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[LUA_RIDX_MAINTHREAD] = L */ + setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L); + /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */ + sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L)); +} + + +/* +** open parts of the state that may cause memory-allocation errors. +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + init_registry(L, g); + luaS_init(L); + luaT_init(L); + luaX_init(L); + g->gcstp = 0; /* allow gc */ + setnilvalue(&g->nilvalue); /* now state is complete */ + luai_userstateopen(L); +} + + +/* +** preinitialize a thread with consistent values without allocating +** any memory (to avoid errors) +*/ +static void preinit_thread (lua_State *L, global_State *g) { + G(L) = g; + L->stack.p = NULL; + L->ci = NULL; + L->nci = 0; + L->twups = L; /* thread has no upvalues */ + L->nCcalls = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->status = LUA_OK; + L->errfunc = 0; + L->oldpc = 0; +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + if (!completestate(g)) /* closing a partially built state? */ + luaC_freeallobjects(L); /* just collect its objects */ + else { /* closing a fully built state */ + L->ci = &L->base_ci; /* unwind CallInfo list */ + luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + luaC_freeallobjects(L); /* collect all objects */ + luai_userstateclose(L); + } + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); + freestack(L); + lua_assert(gettotalbytes(g) == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + global_State *g = G(L); + GCObject *o; + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + /* create new thread */ + o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l)); + L1 = gco2th(o); + /* anchor it on L stack */ + setthvalue2s(L, L->top.p, L1); + api_incr_top(L); + preinit_thread(L1, g); + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + /* initialize L1 extra space */ + memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + LUA_EXTRASPACE); + luai_userstatethread(L, L1); + stack_init(L1, L); /* init stack */ + lua_unlock(L); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + LX *l = fromstate(L1); + luaF_closeupval(L1, L1->stack.p); /* close all upvalues */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L, L1); + freestack(L1); + luaM_free(L, l); +} + + +int luaE_resetthread (lua_State *L, int status) { + CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ + setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ + ci->func.p = L->stack.p; + ci->callstatus = CIST_C; + if (status == LUA_YIELD) + status = LUA_OK; + L->status = LUA_OK; /* so it can run __close metamethods */ + status = luaD_closeprotected(L, 1, status); + if (status != LUA_OK) /* errors? */ + luaD_seterrorobj(L, status, L->stack.p + 1); + else + L->top.p = L->stack.p + 1; + ci->top.p = L->top.p + LUA_MINSTACK; + luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); + return status; +} + + +LUA_API int lua_closethread (lua_State *L, lua_State *from) { + int status; + lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; + status = luaE_resetthread(L, L->status); + lua_unlock(L); + return status; +} + + +/* +** Deprecated! Use 'lua_closethread' instead. +*/ +LUA_API int lua_resetthread (lua_State *L) { + return lua_closethread(L, NULL); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); + if (l == NULL) return NULL; + L = &l->l.l; + g = &l->g; + L->tt = LUA_VTHREAD; + g->currentwhite = bitmask(WHITE0BIT); + L->marked = luaC_white(g); + preinit_thread(L, g); + g->allgc = obj2gco(L); /* by now, only object is the main thread */ + L->next = NULL; + incnny(L); /* main thread is always non yieldable */ + g->frealloc = f; + g->ud = ud; + g->warnf = NULL; + g->ud_warn = NULL; + g->mainthread = L; + g->seed = luai_makeseed(L); + g->gcstp = GCSTPGC; /* no GC while building state */ + g->strt.size = g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(&g->l_registry); + g->panic = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->gcstopem = 0; + g->gcemergency = 0; + g->finobj = g->tobefnz = g->fixedgc = NULL; + g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; + g->finobjsur = g->finobjold1 = g->finobjrold = NULL; + g->sweepgc = NULL; + g->gray = g->grayagain = NULL; + g->weak = g->ephemeron = g->allweak = NULL; + g->twups = NULL; + g->totalbytes = sizeof(LG); + g->GCdebt = 0; + g->lastatomic = 0; + setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ + setgcparam(g->gcpause, LUAI_GCPAUSE); + setgcparam(g->gcstepmul, LUAI_GCMUL); + g->gcstepsize = LUAI_GCSTEPSIZE; + setgcparam(g->genmajormul, LUAI_GENMAJORMUL); + g->genminormul = LUAI_GENMINORMUL; + for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + return L; +} + + +LUA_API void lua_close (lua_State *L) { + lua_lock(L); + L = G(L)->mainthread; /* only the main thread can be closed */ + close_state(L); +} + + +void luaE_warning (lua_State *L, const char *msg, int tocont) { + lua_WarnFunction wf = G(L)->warnf; + if (wf != NULL) + wf(G(L)->ud_warn, msg, tocont); +} + + +/* +** Generate a warning from an error message +*/ +void luaE_warnerror (lua_State *L, const char *where) { + TValue *errobj = s2v(L->top.p - 1); /* error object */ + const char *msg = (ttisstring(errobj)) + ? svalue(errobj) + : "error object is not a string"; + /* produce warning "error in %s (%s)" (where, msg) */ + luaE_warning(L, "error in ", 1); + luaE_warning(L, where, 1); + luaE_warning(L, " (", 1); + luaE_warning(L, msg, 1); + luaE_warning(L, ")", 0); +} + +/* +** $Id: lgc.c $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#define lgc_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + +#include +#include + + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ + + +/* +** Maximum number of elements to sweep in each single step. +** (Large enough to dissipate fixed overheads but small enough +** to allow small steps for the collector.) +*/ +#define GCSWEEPMAX 100 + +/* +** Maximum number of finalizers to call in each single step. +*/ +#define GCFINMAX 10 + + +/* +** Cost of calling one finalizer. +*/ +#define GCFINALIZECOST 50 + + +/* +** The equivalent, in bytes, of one unit of "work" (visiting a slot, +** sweeping an object, etc.) +*/ +#define WORK2MEM sizeof(TValue) + + +/* +** macro to adjust 'pause': 'pause' is actually used like +** 'pause / PAUSEADJ' (value chosen by tests) +*/ +#define PAUSEADJ 100 + + +/* mask with all color bits */ +#define maskcolors (bitmask(BLACKBIT) | WHITEBITS) + +/* mask with all GC bits */ +#define maskgcbits (maskcolors | AGEBITS) + + +/* macro to erase all color bits then set only the current white bit */ +#define makewhite(g,x) \ + (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) + +/* make an object gray (neither white nor black) */ +#define set2gray(x) resetbits(x->marked, maskcolors) + + +/* make an object black (coming from any color) */ +#define set2black(x) \ + (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) + + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) + + +/* +** Protected access to objects in values +*/ +#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) + + +#define markvalue(g,o) { checkliveness(g->mainthread,o); \ + if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } + +#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } + +#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } + +/* +** mark an object that can be NULL (either because it is really optional, +** or it was stripped as debug info, or inside an uncompleted structure) +*/ +#define markobjectN(g,t) { if (t) markobject(g,t); } + +static void reallymarkobject (global_State *g, GCObject *o); +static lu_mem atomic (lua_State *L); +static void entersweep (lua_State *L); + + +/* +** {====================================================== +** Generic functions +** ======================================================= +*/ + + +/* +** one after last element in a hash array +*/ +#define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) + + +static GCObject **getgclist (GCObject *o) { + switch (o->tt) { + case LUA_VTABLE: return &gco2t(o)->gclist; + case LUA_VLCL: return &gco2lcl(o)->gclist; + case LUA_VCCL: return &gco2ccl(o)->gclist; + case LUA_VTHREAD: return &gco2th(o)->gclist; + case LUA_VPROTO: return &gco2p(o)->gclist; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + lua_assert(u->nuvalue > 0); + return &u->gclist; + } + default: lua_assert(0); return 0; + } +} + + +/* +** Link a collectable object 'o' with a known type into the list 'p'. +** (Must be a macro to access the 'gclist' field in different types.) +*/ +#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) + +static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { + lua_assert(!isgray(o)); /* cannot be in a gray list */ + *pnext = *list; + *list = o; + set2gray(o); /* now it is */ +} + + +/* +** Link a generic collectable object 'o' into the list 'p'. +*/ +#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) + + + +/* +** Clear keys for empty entries in tables. If entry is empty, mark its +** entry as dead. This allows the collection of the key, but keeps its +** entry in the table: its removal could break a chain and could break +** a table traversal. Other places never manipulate dead keys, because +** its associated empty value is enough to signal that the entry is +** logically empty. +*/ +static void clearkey (Node *n) { + lua_assert(isempty(gval(n))); + if (keyiscollectable(n)) + setdeadkey(n); /* unused key; remove it */ +} + + +/* +** tells whether a key or value can be cleared from a weak +** table. Non-collectable objects are never removed from weak +** tables. Strings behave as 'values', so are never removed too. for +** other objects: if really collected, cannot keep them; for objects +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (global_State *g, const GCObject *o) { + if (o == NULL) return 0; /* non-collectable value */ + else if (novariant(o->tt) == LUA_TSTRING) { + markobject(g, o); /* strings are 'values', so are never weak */ + return 0; + } + else return iswhite(o); +} + + +/* +** Barrier that moves collector forward, that is, marks the white object +** 'v' being pointed by the black object 'o'. In the generational +** mode, 'v' must also become old, if 'o' is old; however, it cannot +** be changed directly to OLD, because it may still point to non-old +** objects. So, it is marked as OLD0. In the next cycle it will become +** OLD1, and in the next it will finally become OLD (regular old). By +** then, any object it points to will also be old. If called in the +** incremental sweep phase, it clears the black object to white (sweep +** it) to avoid other barrier calls for this same object. (That cannot +** be done is generational mode, as its sweep does not distinguish +** whites from deads.) +*/ +void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + if (keepinvariant(g)) { /* must keep invariant? */ + reallymarkobject(g, v); /* restore invariant */ + if (isold(o)) { + lua_assert(!isold(v)); /* white object could not be old */ + setage(v, G_OLD0); /* restore generational invariant */ + } + } + else { /* sweep phase */ + lua_assert(issweepphase(g)); + if (g->gckind == KGC_INC) /* incremental mode? */ + makewhite(g, o); /* mark 'o' as white to avoid other barriers */ + } +} + + +/* +** barrier that moves collector backward, that is, mark the black object +** pointing to a white object as gray again. +*/ +void luaC_barrierback_ (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); + if (getage(o) == G_TOUCHED2) /* already in gray list? */ + set2gray(o); /* make it gray to become touched1 */ + else /* link it in 'grayagain' and paint it gray */ + linkobjgclist(o, g->grayagain); + if (isold(o)) /* generational mode? */ + setage(o, G_TOUCHED1); /* touched in current cycle */ +} + + +void luaC_fix (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ + set2gray(o); /* they will be gray forever */ + setage(o, G_OLD); /* and old forever */ + g->allgc = o->next; /* remove object from 'allgc' list */ + o->next = g->fixedgc; /* link it to 'fixedgc' list */ + g->fixedgc = o; +} + + +/* +** create a new collectable object (with given type, size, and offset) +** and link it to 'allgc' list. +*/ +GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { + global_State *g = G(L); + char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); + GCObject *o = cast(GCObject *, p + offset); + o->marked = luaC_white(g); + o->tt = tt; + o->next = g->allgc; + g->allgc = o; + return o; +} + + +GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { + return luaC_newobjdt(L, tt, sz, 0); +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Mark functions +** ======================================================= +*/ + + +/* +** Mark an object. Userdata with no user values, strings, and closed +** upvalues are visited and turned black here. Open upvalues are +** already indirectly linked through their respective threads in the +** 'twups' list, so they don't go to the gray list; nevertheless, they +** are kept gray to avoid barriers, as their values will be revisited +** by the thread or by 'remarkupvals'. Other objects are added to the +** gray list to be visited (and turned black) later. Both userdata and +** upvalues can call this function recursively, but this recursion goes +** for at most two levels: An upvalue cannot refer to another upvalue +** (only closures can), and a userdata's metatable must be a table. +*/ +static void reallymarkobject (global_State *g, GCObject *o) { + switch (o->tt) { + case LUA_VSHRSTR: + case LUA_VLNGSTR: { + set2black(o); /* nothing to visit */ + break; + } + case LUA_VUPVAL: { + UpVal *uv = gco2upv(o); + if (upisopen(uv)) + set2gray(uv); /* open upvalues are kept gray */ + else + set2black(uv); /* closed upvalues are visited here */ + markvalue(g, uv->v.p); /* mark its content */ + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + if (u->nuvalue == 0) { /* no user values? */ + markobjectN(g, u->metatable); /* mark its metatable */ + set2black(u); /* nothing else to mark */ + break; + } + /* else... */ + } /* FALLTHROUGH */ + case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: + case LUA_VTHREAD: case LUA_VPROTO: { + linkobjgclist(o, g->gray); /* to be visited later */ + break; + } + default: lua_assert(0); break; + } +} + + +/* +** mark metamethods for basic types +*/ +static void markmt (global_State *g) { + int i; + for (i=0; i < LUA_NUMTAGS; i++) + markobjectN(g, g->mt[i]); +} + + +/* +** mark all objects in list of being-finalized +*/ +static lu_mem markbeingfnz (global_State *g) { + GCObject *o; + lu_mem count = 0; + for (o = g->tobefnz; o != NULL; o = o->next) { + count++; + markobject(g, o); + } + return count; +} + + +/* +** For each non-marked thread, simulates a barrier between each open +** upvalue and its value. (If the thread is collected, the value will be +** assigned to the upvalue, but then it can be too late for the barrier +** to act. The "barrier" does not need to check colors: A non-marked +** thread must be young; upvalues cannot be older than their threads; so +** any visited upvalue must be young too.) Also removes the thread from +** the list, as it was already visited. Removes also threads with no +** upvalues, as they have nothing to be checked. (If the thread gets an +** upvalue later, it will be linked in the list again.) +*/ +static int remarkupvals (global_State *g) { + lua_State *thread; + lua_State **p = &g->twups; + int work = 0; /* estimate of how much work was done here */ + while ((thread = *p) != NULL) { + work++; + if (!iswhite(thread) && thread->openupval != NULL) + p = &thread->twups; /* keep marked thread with upvalues in the list */ + else { /* thread is not marked or without upvalues */ + UpVal *uv; + lua_assert(!isold(thread) || thread->openupval == NULL); + *p = thread->twups; /* remove thread from the list */ + thread->twups = thread; /* mark that it is out of list */ + for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + lua_assert(getage(uv) <= getage(thread)); + work++; + if (!iswhite(uv)) { /* upvalue already visited? */ + lua_assert(upisopen(uv) && isgray(uv)); + markvalue(g, uv->v.p); /* mark its value */ + } + } + } + } + return work; +} + + +static void cleargraylists (global_State *g) { + g->gray = g->grayagain = NULL; + g->weak = g->allweak = g->ephemeron = NULL; +} + + +/* +** mark root set and reset all gray lists, to start a new collection +*/ +static void restartcollection (global_State *g) { + cleargraylists(g); + markobject(g, g->mainthread); + markvalue(g, &g->l_registry); + markmt(g); + markbeingfnz(g); /* mark any finalizing object left from previous cycle */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Traverse functions +** ======================================================= +*/ + + +/* +** Check whether object 'o' should be kept in the 'grayagain' list for +** post-processing by 'correctgraylist'. (It could put all old objects +** in the list and leave all the work to 'correctgraylist', but it is +** more efficient to avoid adding elements that will be removed.) Only +** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go +** back to a gray list, but then it must become OLD. (That is what +** 'correctgraylist' does when it finds a TOUCHED2 object.) +*/ +static void genlink (global_State *g, GCObject *o) { + lua_assert(isblack(o)); + if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ + linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ + } /* everything else do not need to be linked back */ + else if (getage(o) == G_TOUCHED2) + changeage(o, G_TOUCHED2, G_OLD); /* advance age */ +} + + +/* +** Traverse a table with weak values and link it to proper list. During +** propagate phase, keep it in 'grayagain' list, to be revisited in the +** atomic phase. In the atomic phase, if table has any white value, +** put it in 'weak' list, to be cleared. +*/ +static void traverseweakvalue (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + /* if there is array part, assume it may have white values (it is not + worth traversing it now just to check) */ + int hasclears = (h->alimit > 0); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ + hasclears = 1; /* table will have to be cleared */ + } + } + if (g->gcstate == GCSatomic && hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ +} + + +/* +** Traverse an ephemeron table and link it to proper list. Returns true +** iff any object was marked during this traversal (which implies that +** convergence has to continue). During propagation phase, keep table +** in 'grayagain' list, to be visited again in the atomic phase. In +** the atomic phase, if table has any white->white entry, it has to +** be revisited during ephemeron convergence (as that key may turn +** black). Otherwise, if it has any white key, table has to be cleared +** (in the atomic phase). In generational mode, some tables +** must be kept in some gray list for post-processing; this is done +** by 'genlink'. +*/ +static int traverseephemeron (global_State *g, Table *h, int inv) { + int marked = 0; /* true if an object is marked in this traversal */ + int hasclears = 0; /* true if table has white keys */ + int hasww = 0; /* true if table has entry "white-key -> white-value" */ + unsigned int i; + unsigned int asize = luaH_realasize(h); + unsigned int nsize = sizenode(h); + /* traverse array part */ + for (i = 0; i < asize; i++) { + if (valiswhite(&h->array[i])) { + marked = 1; + reallymarkobject(g, gcvalue(&h->array[i])); + } + } + /* traverse hash part; if 'inv', traverse descending + (see 'convergeephemerons') */ + for (i = 0; i < nsize; i++) { + Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ + hasclears = 1; /* table must be cleared */ + if (valiswhite(gval(n))) /* value not marked yet? */ + hasww = 1; /* white-white entry */ + } + else if (valiswhite(gval(n))) { /* value not marked yet? */ + marked = 1; + reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ + } + } + /* link table into proper list */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasww) /* table has white->white entries? */ + linkgclist(h, g->ephemeron); /* have to propagate again */ + else if (hasclears) /* table has white keys? */ + linkgclist(h, g->allweak); /* may have to clean white keys */ + else + genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ + return marked; +} + + +static void traversestrongtable (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) /* traverse array part */ + markvalue(g, &h->array[i]); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + markvalue(g, gval(n)); + } + } + genlink(g, obj2gco(h)); +} + + +static lu_mem traversetable (global_State *g, Table *h) { + const char *weakkey, *weakvalue; + const TValue *mode = gfasttm(g, h->metatable, TM_MODE); + markobjectN(g, h->metatable); + if (mode && ttisstring(mode) && /* is there a weak mode? */ + (cast_void(weakkey = strchr(svalue(mode), 'k')), + cast_void(weakvalue = strchr(svalue(mode), 'v')), + (weakkey || weakvalue))) { /* is really weak? */ + if (!weakkey) /* strong keys? */ + traverseweakvalue(g, h); + else if (!weakvalue) /* strong values? */ + traverseephemeron(g, h, 0); + else /* all weak */ + linkgclist(h, g->allweak); /* nothing to traverse now */ + } + else /* not weak */ + traversestrongtable(g, h); + return 1 + h->alimit + 2 * allocsizenode(h); +} + + +static int traverseudata (global_State *g, Udata *u) { + int i; + markobjectN(g, u->metatable); /* mark its metatable */ + for (i = 0; i < u->nuvalue; i++) + markvalue(g, &u->uv[i].uv); + genlink(g, obj2gco(u)); + return 1 + u->nuvalue; +} + + +/* +** Traverse a prototype. (While a prototype is being build, its +** arrays can be larger than needed; the extra slots are filled with +** NULL, so the use of 'markobjectN') +*/ +static int traverseproto (global_State *g, Proto *f) { + int i; + markobjectN(g, f->source); + for (i = 0; i < f->sizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ + markobjectN(g, f->upvalues[i].name); + for (i = 0; i < f->sizep; i++) /* mark nested protos */ + markobjectN(g, f->p[i]); + for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ + markobjectN(g, f->locvars[i].varname); + return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; +} + + +static int traverseCclosure (global_State *g, CClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->upvalue[i]); + return 1 + cl->nupvalues; +} + +/* +** Traverse a Lua closure, marking its prototype and its upvalues. +** (Both can be NULL while closure is being created.) +*/ +static int traverseLclosure (global_State *g, LClosure *cl) { + int i; + markobjectN(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ + UpVal *uv = cl->upvals[i]; + markobjectN(g, uv); /* mark upvalue */ + } + return 1 + cl->nupvalues; +} + + +/* +** Traverse a thread, marking the elements in the stack up to its top +** and cleaning the rest of the stack in the final traversal. That +** ensures that the entire stack have valid (non-dead) objects. +** Threads have no barriers. In gen. mode, old threads must be visited +** at every cycle, because they might point to young objects. In inc. +** mode, the thread can still be modified before the end of the cycle, +** and therefore it must be visited again in the atomic phase. To ensure +** these visits, threads must return to a gray list if they are not new +** (which can only happen in generational mode) or if the traverse is in +** the propagate phase (which can only happen in incremental mode). +*/ +static int traversethread (global_State *g, lua_State *th) { + UpVal *uv; + StkId o = th->stack.p; + if (isold(th) || g->gcstate == GCSpropagate) + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + if (o == NULL) + return 1; /* stack not completely built yet */ + lua_assert(g->gcstate == GCSatomic || + th->openupval == NULL || isintwups(th)); + for (; o < th->top.p; o++) /* mark live elements in the stack */ + markvalue(g, s2v(o)); + for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) + markobject(g, uv); /* open upvalues cannot be collected */ + if (g->gcstate == GCSatomic) { /* final traversal? */ + for (; o < th->stack_last.p + EXTRA_STACK; o++) + setnilvalue(s2v(o)); /* clear dead stack slice */ + /* 'remarkupvals' may have removed thread from 'twups' list */ + if (!isintwups(th) && th->openupval != NULL) { + th->twups = g->twups; /* link it back to the list */ + g->twups = th; + } + } + else if (!g->gcemergency) + luaD_shrinkstack(th); /* do not change stack in emergency cycle */ + return 1 + stacksize(th); +} + + +/* +** traverse one gray object, turning it to black. +*/ +static lu_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + nw2black(o); + g->gray = *getgclist(o); /* remove from 'gray' list */ + switch (o->tt) { + case LUA_VTABLE: return traversetable(g, gco2t(o)); + case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); + case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); + case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); + case LUA_VPROTO: return traverseproto(g, gco2p(o)); + case LUA_VTHREAD: return traversethread(g, gco2th(o)); + default: lua_assert(0); return 0; + } +} + + +static lu_mem propagateall (global_State *g) { + lu_mem tot = 0; + while (g->gray) + tot += propagatemark(g); + return tot; +} + + +/* +** Traverse all ephemeron tables propagating marks from keys to values. +** Repeat until it converges, that is, nothing new is marked. 'dir' +** inverts the direction of the traversals, trying to speed up +** convergence on chains in the same table. +** +*/ +static void convergeephemerons (global_State *g) { + int changed; + int dir = 0; + do { + GCObject *w; + GCObject *next = g->ephemeron; /* get ephemeron list */ + g->ephemeron = NULL; /* tables may return to this list when traversed */ + changed = 0; + while ((w = next) != NULL) { /* for each ephemeron table */ + Table *h = gco2t(w); + next = h->gclist; /* list is rebuilt during loop */ + nw2black(h); /* out of the list (for now) */ + if (traverseephemeron(g, h, dir)) { /* marked some value? */ + propagateall(g); /* propagate changes */ + changed = 1; /* will have to revisit all ephemeron tables */ + } + } + dir = !dir; /* invert direction next time */ + } while (changed); /* repeat until no more changes */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Sweep Functions +** ======================================================= +*/ + + +/* +** clear entries with unmarked keys from all weaktables in list 'l' +*/ +static void clearbykeys (global_State *g, GCObject *l) { + for (; l; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *limit = gnodelast(h); + Node *n; + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gckeyN(n))) /* unmarked key? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +/* +** clear entries with unmarked values from all weaktables in list 'l' up +** to element 'f' +*/ +static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { + for (; l != f; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *n, *limit = gnodelast(h); + unsigned int i; + unsigned int asize = luaH_realasize(h); + for (i = 0; i < asize; i++) { + TValue *o = &h->array[i]; + if (iscleared(g, gcvalueN(o))) /* value was collected? */ + setempty(o); /* remove entry */ + } + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +static void freeupval (lua_State *L, UpVal *uv) { + if (upisopen(uv)) + luaF_unlinkupval(uv); + luaM_free(L, uv); +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (o->tt) { + case LUA_VPROTO: + luaF_freeproto(L, gco2p(o)); + break; + case LUA_VUPVAL: + freeupval(L, gco2upv(o)); + break; + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); + break; + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); + break; + } + case LUA_VTABLE: + luaH_free(L, gco2t(o)); + break; + case LUA_VTHREAD: + luaE_freethread(L, gco2th(o)); + break; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); + break; + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + luaS_remove(L, ts); /* remove it from hash table */ + luaM_freemem(L, ts, sizelstring(ts->shrlen)); + break; + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + luaM_freemem(L, ts, sizelstring(ts->u.lnglen)); + break; + } + default: lua_assert(0); + } +} + + +/* +** sweep at most 'countin' elements from a list of GCObjects erasing dead +** objects, where a dead object is one marked with the old (non current) +** white; change all non-dead objects back to white, preparing for next +** collection cycle. Return where to continue the traversal or NULL if +** list is finished. ('*countout' gets the number of elements traversed.) +*/ +static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, + int *countout) { + global_State *g = G(L); + int ow = otherwhite(g); + int i; + int white = luaC_white(g); /* current white */ + for (i = 0; *p != NULL && i < countin; i++) { + GCObject *curr = *p; + int marked = curr->marked; + if (isdeadm(ow, marked)) { /* is 'curr' dead? */ + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* change mark to 'white' */ + curr->marked = cast_byte((marked & ~maskgcbits) | white); + p = &curr->next; /* go to next element */ + } + } + if (countout) + *countout = i; /* number of elements traversed */ + return (*p == NULL) ? NULL : p; +} + + +/* +** sweep a list until a live object (or end of list) +*/ +static GCObject **sweeptolive (lua_State *L, GCObject **p) { + GCObject **old = p; + do { + p = sweeplist(L, p, 1, NULL); + } while (p == old); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Finalization +** ======================================================= +*/ + +/* +** If possible, shrink string table. +*/ +static void checkSizes (lua_State *L, global_State *g) { + if (!g->gcemergency) { + if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ + l_mem olddebt = g->GCdebt; + luaS_resize(L, g->strt.size / 2); + g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ + } + } +} + + +/* +** Get the next udata to be finalized from the 'tobefnz' list, and +** link it back into the 'allgc' list. +*/ +static GCObject *udata2finalize (global_State *g) { + GCObject *o = g->tobefnz; /* get first element */ + lua_assert(tofinalize(o)); + g->tobefnz = o->next; /* remove it from 'tobefnz' list */ + o->next = g->allgc; /* return it to 'allgc' list */ + g->allgc = o; + resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ + if (issweepphase(g)) + makewhite(g, o); /* "sweep" object */ + else if (getage(o) == G_OLD1) + g->firstold1 = o; /* it is the first OLD1 object in the list */ + return o; +} + + +static void dothecall (lua_State *L, void *ud) { + UNUSED(ud); + luaD_callnoyield(L, L->top.p - 2, 0); +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + const TValue *tm; + TValue v; + lua_assert(!g->gcemergency); + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); + if (!notm(tm)) { /* is there a finalizer? */ + int status; + lu_byte oldah = L->allowhook; + int oldgcstp = g->gcstp; + g->gcstp |= GCSTPGC; /* avoid GC steps */ + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + setobj2s(L, L->top.p++, tm); /* push finalizer... */ + setobj2s(L, L->top.p++, &v); /* ... and its argument */ + L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); + L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ + L->allowhook = oldah; /* restore hooks */ + g->gcstp = oldgcstp; /* restore state */ + if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ + luaE_warnerror(L, "__gc"); + L->top.p--; /* pops error object */ + } + } +} + + +/* +** Call a few finalizers +*/ +static int runafewfinalizers (lua_State *L, int n) { + global_State *g = G(L); + int i; + for (i = 0; i < n && g->tobefnz; i++) + GCTM(L); /* call one finalizer */ + return i; +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers (lua_State *L) { + global_State *g = G(L); + while (g->tobefnz) + GCTM(L); +} + + +/* +** find last 'next' field in list 'p' list (to add elements in its end) +*/ +static GCObject **findlast (GCObject **p) { + while (*p != NULL) + p = &(*p)->next; + return p; +} + + +/* +** Move all unreachable objects (or 'all' objects) that need +** finalization from list 'finobj' to list 'tobefnz' (to be finalized). +** (Note that objects after 'finobjold1' cannot be white, so they +** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, +** so the whole list is traversed.) +*/ +static void separatetobefnz (global_State *g, int all) { + GCObject *curr; + GCObject **p = &g->finobj; + GCObject **lastnext = findlast(&g->tobefnz); + while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ + lua_assert(tofinalize(curr)); + if (!(iswhite(curr) || all)) /* not being collected? */ + p = &curr->next; /* don't bother with it */ + else { + if (curr == g->finobjsur) /* removing 'finobjsur'? */ + g->finobjsur = curr->next; /* correct it */ + *p = curr->next; /* remove 'curr' from 'finobj' list */ + curr->next = *lastnext; /* link at the end of 'tobefnz' list */ + *lastnext = curr; + lastnext = &curr->next; + } + } +} + + +/* +** If pointer 'p' points to 'o', move it to the next element. +*/ +static void checkpointer (GCObject **p, GCObject *o) { + if (o == *p) + *p = o->next; +} + + +/* +** Correct pointers to objects inside 'allgc' list when +** object 'o' is being removed from the list. +*/ +static void correctpointers (global_State *g, GCObject *o) { + checkpointer(&g->survival, o); + checkpointer(&g->old1, o); + checkpointer(&g->reallyold, o); + checkpointer(&g->firstold1, o); +} + + +/* +** if object 'o' has a finalizer, remove it from 'allgc' list (must +** search the list to find it) and link it in 'finobj' list. +*/ +void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { + global_State *g = G(L); + if (tofinalize(o) || /* obj. is already marked... */ + gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ + (g->gcstp & GCSTPCLS)) /* or closing state? */ + return; /* nothing to be done */ + else { /* move 'o' to 'finobj' list */ + GCObject **p; + if (issweepphase(g)) { + makewhite(g, o); /* "sweep" object 'o' */ + if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ + g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ + } + else + correctpointers(g, o); + /* search for pointer pointing to 'o' */ + for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } + *p = o->next; /* remove 'o' from 'allgc' list */ + o->next = g->finobj; /* link it in 'finobj' list */ + g->finobj = o; + l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generational Collector +** ======================================================= +*/ + + +/* +** Set the "time" to wait before starting a new GC cycle; cycle will +** start when memory use hits the threshold of ('estimate' * pause / +** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, +** because Lua cannot even start with less than PAUSEADJ bytes). +*/ +static void setpause (global_State *g) { + l_mem threshold, debt; + int pause = getgcparam(g->gcpause); + l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ + lua_assert(estimate > 0); + threshold = (pause < MAX_LMEM / estimate) /* overflow? */ + ? estimate * pause /* no overflow */ + : MAX_LMEM; /* overflow; truncate to maximum */ + debt = gettotalbytes(g) - threshold; + if (debt > 0) debt = 0; + luaE_setdebt(g, debt); +} + + +/* +** Sweep a list of objects to enter generational mode. Deletes dead +** objects and turns the non dead to old. All non-dead threads---which +** are now old---must be in a gray list. Everything else is not in a +** gray list. Open upvalues are also kept gray. +*/ +static void sweep2old (lua_State *L, GCObject **p) { + GCObject *curr; + global_State *g = G(L); + while ((curr = *p) != NULL) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* all surviving objects become old */ + setage(curr, G_OLD); + if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ + lua_State *th = gco2th(curr); + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + } + else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) + set2gray(curr); /* open upvalues are always gray */ + else /* everything else is black */ + nw2black(curr); + p = &curr->next; /* go to next element */ + } + } +} + + +/* +** Sweep for generational mode. Delete dead objects. (Because the +** collection is not incremental, there are no "new white" objects +** during the sweep. So, any white object must be dead.) For +** non-dead objects, advance their ages and clear the color of +** new objects. (Old objects keep their colors.) +** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced +** here, because these old-generation objects are usually not swept +** here. They will all be advanced in 'correctgraylist'. That function +** will also remove objects turned white here from any gray list. +*/ +static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, + GCObject *limit, GCObject **pfirstold1) { + static const lu_byte nextage[] = { + G_SURVIVAL, /* from G_NEW */ + G_OLD1, /* from G_SURVIVAL */ + G_OLD1, /* from G_OLD0 */ + G_OLD, /* from G_OLD1 */ + G_OLD, /* from G_OLD (do not change) */ + G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ + G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ + }; + int white = luaC_white(g); + GCObject *curr; + while ((curr = *p) != limit) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(!isold(curr) && isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* correct mark and age */ + if (getage(curr) == G_NEW) { /* new objects go back to white */ + int marked = curr->marked & ~maskgcbits; /* erase GC bits */ + curr->marked = cast_byte(marked | G_SURVIVAL | white); + } + else { /* all other objects will be old, and so keep their color */ + setage(curr, nextage[getage(curr)]); + if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) + *pfirstold1 = curr; /* first OLD1 object in the list */ + } + p = &curr->next; /* go to next element */ + } + } + return p; +} + + +/* +** Traverse a list making all its elements white and clearing their +** age. In incremental mode, all objects are 'new' all the time, +** except for fixed strings (which are always old). +*/ +static void whitelist (global_State *g, GCObject *p) { + int white = luaC_white(g); + for (; p != NULL; p = p->next) + p->marked = cast_byte((p->marked & ~maskgcbits) | white); +} + + +/* +** Correct a list of gray objects. Return pointer to where rest of the +** list should be linked. +** Because this correction is done after sweeping, young objects might +** be turned white and still be in the list. They are only removed. +** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; +** Non-white threads also remain on the list; 'TOUCHED2' objects become +** regular old; they and anything else are removed from the list. +*/ +static GCObject **correctgraylist (GCObject **p) { + GCObject *curr; + while ((curr = *p) != NULL) { + GCObject **next = getgclist(curr); + if (iswhite(curr)) + goto remove; /* remove all white objects */ + else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ + lua_assert(isgray(curr)); + nw2black(curr); /* make it black, for next barrier */ + changeage(curr, G_TOUCHED1, G_TOUCHED2); + goto remain; /* keep it in the list and go to next element */ + } + else if (curr->tt == LUA_VTHREAD) { + lua_assert(isgray(curr)); + goto remain; /* keep non-white threads on the list */ + } + else { /* everything else is removed */ + lua_assert(isold(curr)); /* young objects should be white here */ + if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ + changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */ + nw2black(curr); /* make object black (to be removed) */ + goto remove; + } + remove: *p = *next; continue; + remain: p = next; continue; + } + return p; +} + + +/* +** Correct all gray lists, coalescing them into 'grayagain'. +*/ +static void correctgraylists (global_State *g) { + GCObject **list = correctgraylist(&g->grayagain); + *list = g->weak; g->weak = NULL; + list = correctgraylist(list); + *list = g->allweak; g->allweak = NULL; + list = correctgraylist(list); + *list = g->ephemeron; g->ephemeron = NULL; + correctgraylist(list); +} + + +/* +** Mark black 'OLD1' objects when starting a new young collection. +** Gray objects are already in some gray list, and so will be visited +** in the atomic step. +*/ +static void markold (global_State *g, GCObject *from, GCObject *to) { + GCObject *p; + for (p = from; p != to; p = p->next) { + if (getage(p) == G_OLD1) { + lua_assert(!iswhite(p)); + changeage(p, G_OLD1, G_OLD); /* now they are old */ + if (isblack(p)) + reallymarkobject(g, p); + } + } +} + + +/* +** Finish a young-generation collection. +*/ +static void finishgencycle (lua_State *L, global_State *g) { + correctgraylists(g); + checkSizes(L, g); + g->gcstate = GCSpropagate; /* skip restart */ + if (!g->gcemergency) + callallpendingfinalizers(L); +} + + +/* +** Does a young collection. First, mark 'OLD1' objects. Then does the +** atomic step. Then, sweep all lists and advance pointers. Finally, +** finish the collection. +*/ +static void youngcollection (lua_State *L, global_State *g) { + GCObject **psurvival; /* to point to first non-dead survival object */ + GCObject *dummy; /* dummy out parameter to 'sweepgen' */ + lua_assert(g->gcstate == GCSpropagate); + if (g->firstold1) { /* are there regular OLD1 objects? */ + markold(g, g->firstold1, g->reallyold); /* mark them */ + g->firstold1 = NULL; /* no more OLD1 objects (for now) */ + } + markold(g, g->finobj, g->finobjrold); + markold(g, g->tobefnz, NULL); + atomic(L); + + /* sweep nursery and get a pointer to its last live element */ + g->gcstate = GCSswpallgc; + psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->old1, &g->firstold1); + g->reallyold = g->old1; + g->old1 = *psurvival; /* 'survival' survivals are old now */ + g->survival = g->allgc; /* all news are survivals */ + + /* repeat for 'finobj' lists */ + dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ + psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->finobjold1, &dummy); + g->finobjrold = g->finobjold1; + g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ + g->finobjsur = g->finobj; /* all news are survivals */ + + sweepgen(L, g, &g->tobefnz, NULL, &dummy); + finishgencycle(L, g); +} + + +/* +** Clears all gray lists, sweeps objects, and prepare sublists to enter +** generational mode. The sweeps remove dead objects and turn all +** surviving objects to old. Threads go back to 'grayagain'; everything +** else is turned black (not in any gray list). +*/ +static void atomic2gen (lua_State *L, global_State *g) { + cleargraylists(g); + /* sweep all elements making them old */ + g->gcstate = GCSswpallgc; + sweep2old(L, &g->allgc); + /* everything alive now is old */ + g->reallyold = g->old1 = g->survival = g->allgc; + g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ + + /* repeat for 'finobj' lists */ + sweep2old(L, &g->finobj); + g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; + + sweep2old(L, &g->tobefnz); + + g->gckind = KGC_GEN; + g->lastatomic = 0; + g->GCestimate = gettotalbytes(g); /* base for memory control */ + finishgencycle(L, g); +} + + +/* +** Set debt for the next minor collection, which will happen when +** memory grows 'genminormul'%. +*/ +static void setminordebt (global_State *g) { + luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); +} + + +/* +** Enter generational mode. Must go until the end of an atomic cycle +** to ensure that all objects are correctly marked and weak tables +** are cleared. Then, turn all objects into old and finishes the +** collection. +*/ +static lu_mem entergen (lua_State *L, global_State *g) { + lu_mem numobjs; + luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + numobjs = atomic(L); /* propagates all and then do the atomic stuff */ + atomic2gen(L, g); + setminordebt(g); /* set debt assuming next cycle will be minor */ + return numobjs; +} + + +/* +** Enter incremental mode. Turn all objects white, make all +** intermediate lists point to NULL (to avoid invalid pointers), +** and go to the pause state. +*/ +static void enterinc (global_State *g) { + whitelist(g, g->allgc); + g->reallyold = g->old1 = g->survival = NULL; + whitelist(g, g->finobj); + whitelist(g, g->tobefnz); + g->finobjrold = g->finobjold1 = g->finobjsur = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->lastatomic = 0; +} + + +/* +** Change collector mode to 'newmode'. +*/ +void luaC_changemode (lua_State *L, int newmode) { + global_State *g = G(L); + if (newmode != g->gckind) { + if (newmode == KGC_GEN) /* entering generational mode? */ + entergen(L, g); + else + enterinc(g); /* entering incremental mode */ + } + g->lastatomic = 0; +} + + +/* +** Does a full collection in generational mode. +*/ +static lu_mem fullgen (lua_State *L, global_State *g) { + enterinc(g); + return entergen(L, g); +} + + +/* +** Does a major collection after last collection was a "bad collection". +** +** When the program is building a big structure, it allocates lots of +** memory but generates very little garbage. In those scenarios, +** the generational mode just wastes time doing small collections, and +** major collections are frequently what we call a "bad collection", a +** collection that frees too few objects. To avoid the cost of switching +** between generational mode and the incremental mode needed for full +** (major) collections, the collector tries to stay in incremental mode +** after a bad collection, and to switch back to generational mode only +** after a "good" collection (one that traverses less than 9/8 objects +** of the previous one). +** The collector must choose whether to stay in incremental mode or to +** switch back to generational mode before sweeping. At this point, it +** does not know the real memory in use, so it cannot use memory to +** decide whether to return to generational mode. Instead, it uses the +** number of objects traversed (returned by 'atomic') as a proxy. The +** field 'g->lastatomic' keeps this count from the last collection. +** ('g->lastatomic != 0' also means that the last collection was bad.) +*/ +static void stepgenfull (lua_State *L, global_State *g) { + lu_mem newatomic; /* count of traversed objects */ + lu_mem lastatomic = g->lastatomic; /* count from last collection */ + if (g->gckind == KGC_GEN) /* still in generational mode? */ + enterinc(g); /* enter incremental mode */ + luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ + newatomic = atomic(L); /* mark everybody */ + if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ + atomic2gen(L, g); /* return to generational mode */ + setminordebt(g); + } + else { /* another bad collection; stay in incremental mode */ + g->GCestimate = gettotalbytes(g); /* first estimate */; + entersweep(L); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); + g->lastatomic = newatomic; + } +} + + +/* +** Does a generational "step". +** Usually, this means doing a minor collection and setting the debt to +** make another collection when memory grows 'genminormul'% larger. +** +** However, there are exceptions. If memory grows 'genmajormul'% +** larger than it was at the end of the last major collection (kept +** in 'g->GCestimate'), the function does a major collection. At the +** end, it checks whether the major collection was able to free a +** decent amount of memory (at least half the growth in memory since +** previous major collection). If so, the collector keeps its state, +** and the next collection will probably be minor again. Otherwise, +** we have what we call a "bad collection". In that case, set the field +** 'g->lastatomic' to signal that fact, so that the next collection will +** go to 'stepgenfull'. +** +** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; +** in that case, do a minor collection. +*/ +static void genstep (lua_State *L, global_State *g) { + if (g->lastatomic != 0) /* last collection was a bad one? */ + stepgenfull(L, g); /* do a full step */ + else { + lu_mem majorbase = g->GCestimate; /* memory after last major collection */ + lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); + if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { + lu_mem numobjs = fullgen(L, g); /* do a major collection */ + if (gettotalbytes(g) < majorbase + (majorinc / 2)) { + /* collected at least half of memory growth since last major + collection; keep doing minor collections. */ + lua_assert(g->lastatomic == 0); + } + else { /* bad collection */ + g->lastatomic = numobjs; /* signal that last collection was bad */ + setpause(g); /* do a long wait for next (major) collection */ + } + } + else { /* regular case; do a minor collection */ + youngcollection(L, g); + setminordebt(g); + g->GCestimate = majorbase; /* preserve base value */ + } + } + lua_assert(isdecGCmodegen(g)); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** GC control +** ======================================================= +*/ + + +/* +** Enter first sweep phase. +** The call to 'sweeptolive' makes the pointer point to an object +** inside the list (instead of to the header), so that the real sweep do +** not need to skip objects created between "now" and the start of the +** real sweep. +*/ +static void entersweep (lua_State *L) { + global_State *g = G(L); + g->gcstate = GCSswpallgc; + lua_assert(g->sweepgc == NULL); + g->sweepgc = sweeptolive(L, &g->allgc); +} + + +/* +** Delete all objects in list 'p' until (but not including) object +** 'limit'. +*/ +static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { + while (p != limit) { + GCObject *next = p->next; + freeobj(L, p); + p = next; + } +} + + +/* +** Call all finalizers of the objects in the given Lua state, and +** then free all objects, except for the main thread. +*/ +void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); + g->gcstp = GCSTPCLS; /* no extra finalizers after here */ + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(g->mainthread)); + lua_assert(g->finobj == NULL); /* no new finalizers */ + deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ + lua_assert(g->strt.nuse == 0); +} + + +static lu_mem atomic (lua_State *L) { + global_State *g = G(L); + lu_mem work = 0; + GCObject *origweak, *origall; + GCObject *grayagain = g->grayagain; /* save original list */ + g->grayagain = NULL; + lua_assert(g->ephemeron == NULL && g->weak == NULL); + lua_assert(!iswhite(g->mainthread)); + g->gcstate = GCSatomic; + markobject(g, L); /* mark running thread */ + /* registry and global metatables may be changed by API */ + markvalue(g, &g->l_registry); + markmt(g); /* mark global metatables */ + work += propagateall(g); /* empties 'gray' list */ + /* remark occasional upvalues of (maybe) dead threads */ + work += remarkupvals(g); + work += propagateall(g); /* propagate changes */ + g->gray = grayagain; + work += propagateall(g); /* traverse 'grayagain' list */ + convergeephemerons(g); + /* at this point, all strongly accessible objects are marked. */ + /* Clear values from weak tables, before checking finalizers */ + clearbyvalues(g, g->weak, NULL); + clearbyvalues(g, g->allweak, NULL); + origweak = g->weak; origall = g->allweak; + separatetobefnz(g, 0); /* separate objects to be finalized */ + work += markbeingfnz(g); /* mark objects that will be finalized */ + work += propagateall(g); /* remark, to propagate 'resurrection' */ + convergeephemerons(g); + /* at this point, all resurrected objects are marked. */ + /* remove dead objects from weak tables */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ + /* clear values from resurrected weak tables */ + clearbyvalues(g, g->weak, origweak); + clearbyvalues(g, g->allweak, origall); + luaS_clearcache(g); + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ + lua_assert(g->gray == NULL); + return work; /* estimate of slots marked by 'atomic' */ +} + + +static int sweepstep (lua_State *L, global_State *g, + int nextstate, GCObject **nextlist) { + if (g->sweepgc) { + l_mem olddebt = g->GCdebt; + int count; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); + g->GCestimate += g->GCdebt - olddebt; /* update estimate */ + return count; + } + else { /* enter next state */ + g->gcstate = nextstate; + g->sweepgc = nextlist; + return 0; /* no work done */ + } +} + + +static lu_mem singlestep (lua_State *L) { + global_State *g = G(L); + lu_mem work; + lua_assert(!g->gcstopem); /* collector is not reentrant */ + g->gcstopem = 1; /* no emergency collections while collecting */ + switch (g->gcstate) { + case GCSpause: { + restartcollection(g); + g->gcstate = GCSpropagate; + work = 1; + break; + } + case GCSpropagate: { + if (g->gray == NULL) { /* no more gray objects? */ + g->gcstate = GCSenteratomic; /* finish propagate phase */ + work = 0; + } + else + work = propagatemark(g); /* traverse one gray object */ + break; + } + case GCSenteratomic: { + work = atomic(L); /* work is what was traversed by 'atomic' */ + entersweep(L); + g->GCestimate = gettotalbytes(g); /* first estimate */; + break; + } + case GCSswpallgc: { /* sweep "regular" objects */ + work = sweepstep(L, g, GCSswpfinobj, &g->finobj); + break; + } + case GCSswpfinobj: { /* sweep objects with finalizers */ + work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + break; + } + case GCSswptobefnz: { /* sweep objects to be finalized */ + work = sweepstep(L, g, GCSswpend, NULL); + break; + } + case GCSswpend: { /* finish sweeps */ + checkSizes(L, g); + g->gcstate = GCScallfin; + work = 0; + break; + } + case GCScallfin: { /* call remaining finalizers */ + if (g->tobefnz && !g->gcemergency) { + g->gcstopem = 0; /* ok collections during finalizers */ + work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; + } + else { /* emergency mode or no more finalizers */ + g->gcstate = GCSpause; /* finish collection */ + work = 0; + } + break; + } + default: lua_assert(0); return 0; + } + g->gcstopem = 0; + return work; +} + + +/* +** advances the garbage collector until it reaches a state allowed +** by 'statemask' +*/ +void luaC_runtilstate (lua_State *L, int statesmask) { + global_State *g = G(L); + while (!testbit(statesmask, g->gcstate)) + singlestep(L); +} + + + +/* +** Performs a basic incremental step. The debt and step size are +** converted from bytes to "units of work"; then the function loops +** running single steps until adding that many units of work or +** finishing a cycle (pause state). Finally, it sets the debt that +** controls when next step will be performed. +*/ +static void incstep (lua_State *L, global_State *g) { + int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ + l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; + l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) + ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul + : MAX_LMEM; /* overflow; keep maximum value */ + do { /* repeat until pause or enough "credit" (negative debt) */ + lu_mem work = singlestep(L); /* perform one single step */ + debt -= work; + } while (debt > -stepsize && g->gcstate != GCSpause); + if (g->gcstate == GCSpause) + setpause(g); /* pause until next cycle */ + else { + debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ + luaE_setdebt(g, debt); + } +} + +/* +** Performs a basic GC step if collector is running. (If collector is +** not running, set a reasonable debt to avoid it being called at +** every single check.) +*/ +void luaC_step (lua_State *L) { + global_State *g = G(L); + if (!gcrunning(g)) /* not running? */ + luaE_setdebt(g, -2000); + else { + if(isdecGCmodegen(g)) + genstep(L, g); + else + incstep(L, g); + } +} + + +/* +** Perform a full collection in incremental mode. +** Before running the collection, check 'keepinvariant'; if it is true, +** there may be some objects marked as black, so the collector has +** to sweep all objects to turn them back to white (as white has not +** changed, nothing will be collected). +*/ +static void fullinc (lua_State *L, global_State *g) { + if (keepinvariant(g)) /* black objects? */ + entersweep(L); /* sweep everything to turn them back to white */ + /* finish any pending sweep phase to start a new cycle */ + luaC_runtilstate(L, bitmask(GCSpause)); + luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ + /* estimate must be correct after a full GC cycle */ + lua_assert(g->GCestimate == gettotalbytes(g)); + luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + setpause(g); +} + + +/* +** Performs a full GC cycle; if 'isemergency', set a flag to avoid +** some operations which could change the interpreter state in some +** unexpected ways (running finalizers and shrinking some structures). +*/ +void luaC_fullgc (lua_State *L, int isemergency) { + global_State *g = G(L); + lua_assert(!g->gcemergency); + g->gcemergency = isemergency; /* set flag */ + if (g->gckind == KGC_INC) + fullinc(L, g); + else + fullgen(L, g); + g->gcemergency = 0; +} + +/* }====================================================== */ + + +/* +** $Id: llex.c $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#define llex_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include + +/*#include "lua.h"*/ + +/*#include "lctype.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lgc.h"*/ +/*#include "llex.h"*/ +/*#include "lobject.h"*/ +/*#include "lparser.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "lzio.h"*/ + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +static const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "", + "", "", "", "" +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static l_noret lexerror (LexState *ls, const char *msg, int token); + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { + size_t newsize; + if (luaZ_sizebuffer(b) >= MAX_SIZE/2) + lexerror(ls, "lexical element too long", 0); + newsize = luaZ_sizebuffer(b) * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[luaZ_bufflen(b)++] = cast_char(c); +} + + +void luaX_init (lua_State *L) { + int i; + TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ + luaC_fix(L, obj2gco(e)); /* never collect this name */ + for (i=0; iextra = cast_byte(i+1); /* reserved word */ + } +} + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { /* single-byte symbols? */ + if (lisprint(token)) + return luaO_pushfstring(ls->L, "'%c'", token); + else /* control character */ + return luaO_pushfstring(ls->L, "'<\\%d>'", token); + } + else { + const char *s = luaX_tokens[token - FIRST_RESERVED]; + if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ + return luaO_pushfstring(ls->L, "'%s'", s); + else /* names, strings, and numerals */ + return s; + } +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: case TK_STRING: + case TK_FLT: case TK_INT: + save(ls, '\0'); + return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); + default: + return luaX_token2str(ls, token); + } +} + + +static l_noret lexerror (LexState *ls, const char *msg, int token) { + msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); + if (token) + luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +l_noret luaX_syntaxerror (LexState *ls, const char *msg) { + lexerror(ls, msg, ls->t.token); +} + + +/* +** Creates a new string and anchors it in scanner's table so that it +** will not be collected until the end of the compilation; by that time +** it should be anchored somewhere. It also internalizes long strings, +** ensuring there is only one copy of each unique string. The table +** here is used as a set: the string enters as the key, while its value +** is irrelevant. We use the string itself as the value only because it +** is a TValue readily available. Later, the code generation can change +** this value. +*/ +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TString *ts = luaS_newlstr(L, str, l); /* create new string */ + const TValue *o = luaH_getstr(ls->h, ts); + if (!ttisnil(o)) /* string already present? */ + ts = keystrval(nodefromval(o)); /* get saved copy */ + else { /* not in use yet */ + TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ + setsvalue(L, stv, ts); /* temporarily anchor the string */ + luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ + /* table is not a metatable, so it does not need to invalidate cache */ + luaC_checkGC(L); + L->top.p--; /* remove string from stack */ + } + return ts; +} + + +/* +** increment line number and skips newline sequence (any of +** \n, \r, \n\r, or \r\n) +*/ +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip '\n' or '\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip '\n\r' or '\r\n' */ + if (++ls->linenumber >= MAX_INT) + lexerror(ls, "chunk has too many lines", 0); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, + int firstchar) { + ls->t.token = 0; + ls->L = L; + ls->current = firstchar; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + +static int check_next1 (LexState *ls, int c) { + if (ls->current == c) { + next(ls); + return 1; + } + else return 0; +} + + +/* +** Check whether current char is in set 'set' (with two chars) and +** saves it +*/ +static int check_next2 (LexState *ls, const char *set) { + lua_assert(set[2] == '\0'); + if (ls->current == set[0] || ls->current == set[1]) { + save_and_next(ls); + return 1; + } + else return 0; +} + + +/* LUA_NUMBER */ +/* +** This function is quite liberal in what it accepts, as 'luaO_str2num' +** will reject ill-formed numerals. Roughly, it accepts the following +** pattern: +** +** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))* +** +** The only tricky part is to accept [+-] only after a valid exponent +** mark, to avoid reading '3-4' or '0xe+1' as a single number. +** +** The caller might have already read an initial dot. +*/ +static int read_numeral (LexState *ls, SemInfo *seminfo) { + TValue obj; + const char *expo = "Ee"; + int first = ls->current; + lua_assert(lisdigit(ls->current)); + save_and_next(ls); + if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ + expo = "Pp"; + for (;;) { + if (check_next2(ls, expo)) /* exponent mark? */ + check_next2(ls, "-+"); /* optional exponent sign */ + else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */ + save_and_next(ls); + else break; + } + if (lislalpha(ls->current)) /* is numeral touching a letter? */ + save_and_next(ls); /* force an error */ + save(ls, '\0'); + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ + lexerror(ls, "malformed number", TK_FLT); + if (ttisinteger(&obj)) { + seminfo->i = ivalue(&obj); + return TK_INT; + } + else { + lua_assert(ttisfloat(&obj)); + seminfo->r = fltvalue(&obj); + return TK_FLT; + } +} + + +/* +** read a sequence '[=*[' or ']=*]', leaving the last bracket. If +** sequence is well formed, return its number of '='s + 2; otherwise, +** return 1 if it is a single bracket (no '='s and no 2nd bracket); +** otherwise (an unfinished '[==...') return 0. +*/ +static size_t skip_sep (LexState *ls) { + size_t count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count + 2 + : (count == 0) ? 1 + : 0; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) { + int line = ls->linenumber; /* initial line (for error message) */ + save_and_next(ls); /* skip 2nd '[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: { /* error */ + const char *what = (seminfo ? "string" : "comment"); + const char *msg = luaO_pushfstring(ls->L, + "unfinished long %s (starting at line %d)", what, line); + lexerror(ls, msg, TK_EOS); + break; /* to avoid warnings */ + } + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd ']' */ + goto endloop; + } + break; + } + case '\n': case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep, + luaZ_bufflen(ls->buff) - 2 * sep); +} + + +static void esccheck (LexState *ls, int c, const char *msg) { + if (!c) { + if (ls->current != EOZ) + save_and_next(ls); /* add current to buffer for error message */ + lexerror(ls, msg, TK_STRING); + } +} + + +static int gethexa (LexState *ls) { + save_and_next(ls); + esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); + return luaO_hexavalue(ls->current); +} + + +static int readhexaesc (LexState *ls) { + int r = gethexa(ls); + r = (r << 4) + gethexa(ls); + luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ + return r; +} + + +static unsigned long readutf8esc (LexState *ls) { + unsigned long r; + int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ + save_and_next(ls); /* skip 'u' */ + esccheck(ls, ls->current == '{', "missing '{'"); + r = gethexa(ls); /* must have at least one digit */ + while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { + i++; + esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); + r = (r << 4) + luaO_hexavalue(ls->current); + } + esccheck(ls, ls->current == '}', "missing '}'"); + next(ls); /* skip '}' */ + luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ + return r; +} + + +static void utf8esc (LexState *ls) { + char buff[UTF8BUFFSZ]; + int n = luaO_utf8esc(buff, readutf8esc(ls)); + for (; n > 0; n--) /* add 'buff' to string */ + save(ls, buff[UTF8BUFFSZ - n]); +} + + +static int readdecesc (LexState *ls) { + int i; + int r = 0; /* result accumulator */ + for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ + r = 10*r + ls->current - '0'; + save_and_next(ls); + } + esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); + luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ + return r; +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); /* keep delimiter (for error messages) */ + while (ls->current != del) { + switch (ls->current) { + case EOZ: + lexerror(ls, "unfinished string", TK_EOS); + break; /* to avoid warnings */ + case '\n': + case '\r': + lexerror(ls, "unfinished string", TK_STRING); + break; /* to avoid warnings */ + case '\\': { /* escape sequences */ + int c; /* final character to be saved */ + save_and_next(ls); /* keep '\\' for error messages */ + switch (ls->current) { + case 'a': c = '\a'; goto read_save; + case 'b': c = '\b'; goto read_save; + case 'f': c = '\f'; goto read_save; + case 'n': c = '\n'; goto read_save; + case 'r': c = '\r'; goto read_save; + case 't': c = '\t'; goto read_save; + case 'v': c = '\v'; goto read_save; + case 'x': c = readhexaesc(ls); goto read_save; + case 'u': utf8esc(ls); goto no_save; + case '\n': case '\r': + inclinenumber(ls); c = '\n'; goto only_save; + case '\\': case '\"': case '\'': + c = ls->current; goto read_save; + case EOZ: goto no_save; /* will raise an error next loop */ + case 'z': { /* zap following span of spaces */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + next(ls); /* skip the 'z' */ + while (lisspace(ls->current)) { + if (currIsNewline(ls)) inclinenumber(ls); + else next(ls); + } + goto no_save; + } + default: { + esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); + c = readdecesc(ls); /* digital escape '\ddd' */ + goto only_save; + } + } + read_save: + next(ls); + /* go through */ + only_save: + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + save(ls, c); + /* go through */ + no_save: break; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': case '\r': { /* line breaks */ + inclinenumber(ls); + break; + } + case ' ': case '\f': case '\t': case '\v': { /* spaces */ + next(ls); + break; + } + case '-': { /* '-' or '--' (comment) */ + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { /* long comment? */ + size_t sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ + if (sep >= 2) { + read_long_string(ls, NULL, sep); /* skip long comment */ + luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ + break; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); /* skip until end of line (or end of file) */ + break; + } + case '[': { /* long string or simply '[' */ + size_t sep = skip_sep(ls); + if (sep >= 2) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == 0) /* '[=...' missing second bracket? */ + lexerror(ls, "invalid long string delimiter", TK_STRING); + return '['; + } + case '=': { + next(ls); + if (check_next1(ls, '=')) return TK_EQ; /* '==' */ + else return '='; + } + case '<': { + next(ls); + if (check_next1(ls, '=')) return TK_LE; /* '<=' */ + else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */ + else return '<'; + } + case '>': { + next(ls); + if (check_next1(ls, '=')) return TK_GE; /* '>=' */ + else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */ + else return '>'; + } + case '/': { + next(ls); + if (check_next1(ls, '/')) return TK_IDIV; /* '//' */ + else return '/'; + } + case '~': { + next(ls); + if (check_next1(ls, '=')) return TK_NE; /* '~=' */ + else return '~'; + } + case ':': { + next(ls); + if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */ + else return ':'; + } + case '"': case '\'': { /* short literal strings */ + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { /* '.', '..', '...', or number */ + save_and_next(ls); + if (check_next1(ls, '.')) { + if (check_next1(ls, '.')) + return TK_DOTS; /* '...' */ + else return TK_CONCAT; /* '..' */ + } + else if (!lisdigit(ls->current)) return '.'; + else return read_numeral(ls, seminfo); + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + return read_numeral(ls, seminfo); + } + case EOZ: { + return TK_EOS; + } + default: { + if (lislalpha(ls->current)) { /* identifier or reserved word? */ + TString *ts; + do { + save_and_next(ls); + } while (lislalnum(ls->current)); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + seminfo->ts = ts; + if (isreserved(ts)) /* reserved word? */ + return ts->extra - 1 + FIRST_RESERVED; + else { + return TK_NAME; + } + } + else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */ + int c = ls->current; + next(ls); + return c; + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +int luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); + return ls->lookahead.token; +} + +/* +** $Id: lcode.c $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#define lcode_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lcode.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lgc.h"*/ +/*#include "llex.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lparser.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "lvm.h"*/ + + +/* Maximum number of registers in a Lua function (must fit in 8 bits) */ +#define MAXREGS 255 + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int codesJ (FuncState *fs, OpCode o, int sj, int k); + + + +/* semantic error */ +l_noret luaK_semerror (LexState *ls, const char *msg) { + ls->t.token = 0; /* remove "near " from final message */ + luaX_syntaxerror(ls, msg); +} + + +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +static int tonumeral (const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a numeral */ + switch (e->k) { + case VKINT: + if (v) setivalue(v, e->u.ival); + return 1; + case VKFLT: + if (v) setfltvalue(v, e->u.nval); + return 1; + default: return 0; + } +} + + +/* +** Get the constant value from a constant expression +*/ +static TValue *const2val (FuncState *fs, const expdesc *e) { + lua_assert(e->k == VCONST); + return &fs->ls->dyd->actvar.arr[e->u.info].k; +} + + +/* +** If expression is a constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a constant */ + switch (e->k) { + case VFALSE: + setbfvalue(v); + return 1; + case VTRUE: + setbtvalue(v); + return 1; + case VNIL: + setnilvalue(v); + return 1; + case VKSTR: { + setsvalue(fs->ls->L, v, e->u.strval); + return 1; + } + case VCONST: { + setobj(fs->ls->L, v, const2val(fs, e)); + return 1; + } + default: return tonumeral(e, v); + } +} + + +/* +** Return the previous instruction of the current code. If there +** may be a jump target between the current instruction and the +** previous one, return an invalid instruction (to avoid wrong +** optimizations). +*/ +static Instruction *previousinstruction (FuncState *fs) { + static const Instruction invalidinstruction = ~(Instruction)0; + if (fs->pc > fs->lasttarget) + return &fs->f->code[fs->pc - 1]; /* previous instruction */ + else + return cast(Instruction*, &invalidinstruction); +} + + +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ +void luaK_nil (FuncState *fs, int from, int n) { + int l = from + n - 1; /* last register to set nil */ + Instruction *previous = previousinstruction(fs); + if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ + int pfrom = GETARG_A(*previous); /* get previous range */ + int pl = pfrom + GETARG_B(*previous); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + SETARG_A(*previous, from); + SETARG_B(*previous, l - from); + return; + } /* else go through */ + } + luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ +} + + +/* +** Gets the destination address of a jump instruction. Used to traverse +** a list of jumps. +*/ +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sJ(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +/* +** Fix jump instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua) +*/ +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + lua_assert(dest != NO_JUMP); + if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ)) + luaX_syntaxerror(fs->ls, "control structure too long"); + lua_assert(GET_OPCODE(*jmp) == OP_JMP); + SETARG_sJ(*jmp, offset); +} + + +/* +** Concatenate jump-list 'l2' into jump-list 'l1' +*/ +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; /* nothing to concatenate? */ + else if (*l1 == NO_JUMP) /* no original list? */ + *l1 = l2; /* 'l1' points to 'l2' */ + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); /* last element links to 'l2' */ + } +} + + +/* +** Create a jump instruction and return its position, so its destination +** can be fixed later (with 'fixjump'). +*/ +int luaK_jump (FuncState *fs) { + return codesJ(fs, OP_JMP, NO_JUMP, 0); +} + + +/* +** Code a 'return' instruction +*/ +void luaK_ret (FuncState *fs, int first, int nret) { + OpCode op; + switch (nret) { + case 0: op = OP_RETURN0; break; + case 1: op = OP_RETURN1; break; + default: op = OP_RETURN; break; + } + luaK_codeABC(fs, op, first, nret + 1, 0); +} + + +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ +static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) { + luaK_codeABCk(fs, op, A, B, C, k); + return luaK_jump(fs); +} + + +/* +** returns current 'pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +/* +** Returns the position of the instruction "controlling" a given +** jump (that is, its condition), or the jump itself if it is +** unconditional. +*/ +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** Patch destination register for a TESTSET instruction. +** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). +** Otherwise, if 'reg' is not 'NO_REG', set it as the destination +** register. Otherwise, change instruction to a simple 'TEST' (produces +** no register value) +*/ +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else { + /* no register to put value or register already has the value; + change instruction to simple test */ + *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i)); + } + return 1; +} + + +/* +** Traverse a list of tests ensuring no one produces a value +*/ +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +/* +** Traverse a list of tests, patching their destination address and +** registers: tests producing values jump to 'vtarget' (and put their +** values in 'reg'), other tests jump to 'dtarget'. +*/ +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +/* +** Path all jumps in 'list' to jump to 'target'. +** (The assert means that we cannot fix a jump to a forward address +** because we only know addresses once code is generated.) +*/ +void luaK_patchlist (FuncState *fs, int list, int target) { + lua_assert(target <= fs->pc); + patchlistaux(fs, list, target, NO_REG, target); +} + + +void luaK_patchtohere (FuncState *fs, int list) { + int hr = luaK_getlabel(fs); /* mark "here" as a jump target */ + luaK_patchlist(fs, list, hr); +} + + +/* limit for difference between lines in relative line info. */ +#define LIMLINEDIFF 0x80 + + +/* +** Save line info for a new instruction. If difference from last line +** does not fit in a byte, of after that many instructions, save a new +** absolute line info; (in that case, the special value 'ABSLINEINFO' +** in 'lineinfo' signals the existence of this absolute information.) +** Otherwise, store the difference from last line in 'lineinfo'. +*/ +static void savelineinfo (FuncState *fs, Proto *f, int line) { + int linedif = line - fs->previousline; + int pc = fs->pc - 1; /* last instruction coded */ + if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) { + luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, + f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines"); + f->abslineinfo[fs->nabslineinfo].pc = pc; + f->abslineinfo[fs->nabslineinfo++].line = line; + linedif = ABSLINEINFO; /* signal that there is absolute information */ + fs->iwthabs = 1; /* restart counter */ + } + luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, + MAX_INT, "opcodes"); + f->lineinfo[pc] = linedif; + fs->previousline = line; /* last line saved */ +} + + +/* +** Remove line information from the last instruction. +** If line information for that instruction is absolute, set 'iwthabs' +** above its max to force the new (replacing) instruction to have +** absolute line info, too. +*/ +static void removelastlineinfo (FuncState *fs) { + Proto *f = fs->f; + int pc = fs->pc - 1; /* last instruction coded */ + if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */ + fs->previousline -= f->lineinfo[pc]; /* correct last line saved */ + fs->iwthabs--; /* undo previous increment */ + } + else { /* absolute line information */ + lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc); + fs->nabslineinfo--; /* remove it */ + fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */ + } +} + + +/* +** Remove the last instruction created, correcting line information +** accordingly. +*/ +static void removelastinstruction (FuncState *fs) { + removelastlineinfo(fs); + fs->pc--; +} + + +/* +** Emit instruction 'i', checking for array sizes and saving also its +** line information. Return 'i' position. +*/ +int luaK_code (FuncState *fs, Instruction i) { + Proto *f = fs->f; + /* put new instruction in code array */ + luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "opcodes"); + f->code[fs->pc++] = i; + savelineinfo(fs, f, fs->ls->lastline); + return fs->pc - 1; /* index of new instruction */ +} + + +/* +** Format and emit an 'iABC' instruction. (Assertions check consistency +** of parameters versus opcode.) +*/ +int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) { + lua_assert(getOpMode(o) == iABC); + lua_assert(a <= MAXARG_A && b <= MAXARG_B && + c <= MAXARG_C && (k & ~1) == 0); + return luaK_code(fs, CREATE_ABCk(o, a, b, c, k)); +} + + +/* +** Format and emit an 'iABx' instruction. +*/ +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx); + lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, bc)); +} + + +/* +** Format and emit an 'iAsBx' instruction. +*/ +int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) { + unsigned int b = bc + OFFSET_sBx; + lua_assert(getOpMode(o) == iAsBx); + lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, a, b)); +} + + +/* +** Format and emit an 'isJ' instruction. +*/ +static int codesJ (FuncState *fs, OpCode o, int sj, int k) { + unsigned int j = sj + OFFSET_sJ; + lua_assert(getOpMode(o) == isJ); + lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); + return luaK_code(fs, CREATE_sJ(o, j, k)); +} + + +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ +static int codeextraarg (FuncState *fs, int a) { + lua_assert(a <= MAXARG_Ax); + return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); +} + + +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ +static int luaK_codek (FuncState *fs, int reg, int k) { + if (k <= MAXARG_Bx) + return luaK_codeABx(fs, OP_LOADK, reg, k); + else { + int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); + codeextraarg(fs, k); + return p; + } +} + + +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXREGS) + luaX_syntaxerror(fs->ls, + "function or expression needs too many registers"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +/* +** Reserve 'n' registers in register stack +*/ +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +) +*/ +static void freereg (FuncState *fs, int reg) { + if (reg >= luaY_nvarstack(fs)) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +/* +** Free two registers in proper order +*/ +static void freeregs (FuncState *fs, int r1, int r2) { + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } + else { + freereg(fs, r2); + freereg(fs, r1); + } +} + + +/* +** Free register used by expression 'e' (if any) +*/ +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.info); +} + + +/* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { + int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; + int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; + freeregs(fs, r1, r2); +} + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. +** Note that all functions share the same table, so entering or exiting +** a function can make some indices wrong. +*/ +static int addk (FuncState *fs, TValue *key, TValue *v) { + TValue val; + lua_State *L = fs->ls->L; + Proto *f = fs->f; + const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */ + int k, oldsize; + if (ttisinteger(idx)) { /* is there an index there? */ + k = cast_int(ivalue(idx)); + /* correct value? (warning: must distinguish floats from integers!) */ + if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && + luaV_rawequalobj(&f->k[k], v)) + return k; /* reuse index */ + } + /* constant not found; create a new entry */ + oldsize = f->sizek; + k = fs->nk; + /* numerical value does not need GC barrier; + table has no metatable, so it does not need to invalidate cache */ + setivalue(&val, k); + luaH_finishset(L, fs->ls->h, key, idx, &val); + luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[k], v); + fs->nk++; + luaC_barrier(L, f, v); + return k; +} + + +/* +** Add a string to list of constants and return its index. +*/ +static int stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->ls->L, &o, s); + return addk(fs, &o, &o); /* use string itself as key */ +} + + +/* +** Add an integer to list of constants and return its index. +*/ +static int luaK_intK (FuncState *fs, lua_Integer n) { + TValue o; + setivalue(&o, n); + return addk(fs, &o, &o); /* use integer itself as key */ +} + +/* +** Add a float to list of constants and return its index. Floats +** with integral values need a different key, to avoid collision +** with actual integers. To that, we add to the number its smaller +** power-of-two fraction that is still significant in its scale. +** For doubles, that would be 1/2^52. +** (This method is not bulletproof: there may be another float +** with that value, and for floats larger than 2^53 the result is +** still an integer. At worst, this only wastes an entry with +** a duplicate.) +*/ +static int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + lua_Integer ik; + setfltvalue(&o, r); + if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */ + return addk(fs, &o, &o); /* use number itself as key */ + else { /* must build an alternative key */ + const int nbm = l_floatatt(MANT_DIG); + const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); + const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */ + TValue kv; + setfltvalue(&kv, k); + /* result is not an integral value, unless value is too large */ + lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) || + l_mathop(fabs)(r) >= l_mathop(1e6)); + return addk(fs, &kv, &o); + } +} + + +/* +** Add a false to list of constants and return its index. +*/ +static int boolF (FuncState *fs) { + TValue o; + setbfvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add a true to list of constants and return its index. +*/ +static int boolT (FuncState *fs) { + TValue o; + setbtvalue(&o); + return addk(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add nil to list of constants and return its index. +*/ +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->ls->L, &k, fs->ls->h); + return addk(fs, &k, &v); +} + + +/* +** Check whether 'i' can be stored in an 'sC' operand. Equivalent to +** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of +** overflows in the hidden addition inside 'int2sC'. +*/ +static int fitsC (lua_Integer i) { + return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C)); +} + + +/* +** Check whether 'i' can be stored in an 'sBx' operand. +*/ +static int fitsBx (lua_Integer i) { + return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx); +} + + +void luaK_int (FuncState *fs, int reg, lua_Integer i) { + if (fitsBx(i)) + luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i)); + else + luaK_codek(fs, reg, luaK_intK(fs, i)); +} + + +static void luaK_float (FuncState *fs, int reg, lua_Number f) { + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) + luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); + else + luaK_codek(fs, reg, luaK_numberK(fs, f)); +} + + +/* +** Convert a constant in 'v' into an expression description 'e' +*/ +static void const2exp (TValue *v, expdesc *e) { + switch (ttypetag(v)) { + case LUA_VNUMINT: + e->k = VKINT; e->u.ival = ivalue(v); + break; + case LUA_VNUMFLT: + e->k = VKFLT; e->u.nval = fltvalue(v); + break; + case LUA_VFALSE: + e->k = VFALSE; + break; + case LUA_VTRUE: + e->k = VTRUE; + break; + case LUA_VNIL: + e->k = VNIL; + break; + case LUA_VSHRSTR: case LUA_VLNGSTR: + e->k = VKSTR; e->u.strval = tsvalue(v); + break; + default: lua_assert(0); + } +} + + +/* +** Fix an expression to return the number of results 'nresults'. +** 'e' must be a multi-ret expression (function call or vararg). +*/ +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + Instruction *pc = &getinstruction(fs, e); + if (e->k == VCALL) /* expression is an open function call? */ + SETARG_C(*pc, nresults + 1); + else { + lua_assert(e->k == VVARARG); + SETARG_C(*pc, nresults + 1); + SETARG_A(*pc, fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +/* +** Convert a VKSTR to a VK +*/ +static void str2K (FuncState *fs, expdesc *e) { + lua_assert(e->k == VKSTR); + e->u.info = stringK(fs, e->u.strval); + e->k = VK; +} + + +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOC (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + /* already returns 1 value */ + lua_assert(GETARG_C(getinstruction(fs, e)) == 2); + e->k = VNONRELOC; /* result has fixed position */ + e->u.info = GETARG_A(getinstruction(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_C(getinstruction(fs, e), 2); + e->k = VRELOC; /* can relocate its simple result */ + } +} + + +/* +** Ensure that expression 'e' is not a variable (nor a ). +** (Expression still may have jump lists.) +*/ +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VCONST: { + const2exp(const2val(fs, e), e); + break; + } + case VLOCAL: { /* already in a register */ + e->u.info = e->u.var.ridx; + e->k = VNONRELOC; /* becomes a non-relocatable value */ + break; + } + case VUPVAL: { /* move value to some (pending) register */ + e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + case VINDEXUP: { + e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXI: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXSTR: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXED: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VVARARG: case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +/* +** Ensure expression value is in register 'reg', making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: { + luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0); + break; + } + case VTRUE: { + luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0); + break; + } + case VKSTR: { + str2K(fs, e); + } /* FALLTHROUGH */ + case VK: { + luaK_codek(fs, reg, e->u.info); + break; + } + case VKFLT: { + luaK_float(fs, reg, e->u.nval); + break; + } + case VKINT: { + luaK_int(fs, reg, e->u.ival); + break; + } + case VRELOC: { + Instruction *pc = &getinstruction(fs, e); + SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ + break; + } + case VNONRELOC: { + if (reg != e->u.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); + break; + } + default: { + lua_assert(e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensure expression value is in a register, making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs->freereg-1); /* put value there */ + } +} + + +static int code_loadbool (FuncState *fs, int A, OpCode op) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, op, A, 0, 0); +} + + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +/* +** Ensures final expression result (which includes results from its +** jump lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) /* expression itself is a test? */ + luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */ + p_t = code_loadbool(fs, reg, OP_LOADTRUE); + /* jump around these booleans if 'e' is not a test */ + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensures final expression result is in next available register. +*/ +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +/* +** Ensures final expression result is in some (any) register +** and return that register. +*/ +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e->u.info; /* result is already in a register */ + if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.info); /* put final result in it */ + return e->u.info; + } + /* else expression has jumps and cannot change its register + to hold the jump values, because it is a local variable. + Go through to the default case. */ + } + luaK_exp2nextreg(fs, e); /* default: use next available register */ + return e->u.info; +} + + +/* +** Ensures final expression result is either in a register +** or in an upvalue. +*/ +void luaK_exp2anyregup (FuncState *fs, expdesc *e) { + if (e->k != VUPVAL || hasjumps(e)) + luaK_exp2anyreg(fs, e); +} + + +/* +** Ensures final expression result is either in a register +** or it is a constant. +*/ +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +/* +** Try to make 'e' a K expression with an index in the range of R/K +** indices. Return true iff succeeded. +*/ +static int luaK_exp2K (FuncState *fs, expdesc *e) { + if (!hasjumps(e)) { + int info; + switch (e->k) { /* move constants to 'k' */ + case VTRUE: info = boolT(fs); break; + case VFALSE: info = boolF(fs); break; + case VNIL: info = nilK(fs); break; + case VKINT: info = luaK_intK(fs, e->u.ival); break; + case VKFLT: info = luaK_numberK(fs, e->u.nval); break; + case VKSTR: info = stringK(fs, e->u.strval); break; + case VK: info = e->u.info; break; + default: return 0; /* not a constant */ + } + if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */ + e->k = VK; /* make expression a 'K' expression */ + e->u.info = info; + return 1; + } + } + /* else, expression doesn't fit; leave it unchanged */ + return 0; +} + + +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns 1 iff expression is K. +*/ +int luaK_exp2RK (FuncState *fs, expdesc *e) { + if (luaK_exp2K(fs, e)) + return 1; + else { /* not a constant in the right range: put it in a register */ + luaK_exp2anyreg(fs, e); + return 0; + } +} + + +static void codeABRK (FuncState *fs, OpCode o, int a, int b, + expdesc *ec) { + int k = luaK_exp2RK(fs, ec); + luaK_codeABCk(fs, o, a, b, ec->u.info, k); +} + + +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */ + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); + break; + } + case VINDEXUP: { + codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXI: { + codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXSTR: { + codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXED: { + codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); + break; + } + default: lua_assert(0); /* invalid var kind to store */ + } + freeexp(fs, ex); +} + + +/* +** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). +*/ +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int ereg; + luaK_exp2anyreg(fs, e); + ereg = e->u.info; /* register where 'e' was placed */ + freeexp(fs, e); + e->u.info = fs->freereg; /* base register for op_self */ + e->k = VNONRELOC; /* self expression has a fixed register */ + luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ + codeABRK(fs, OP_SELF, e->u.info, ereg, key); + freeexp(fs, key); +} + + +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +static void negatecondition (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_k(*pc, (GETARG_k(*pc) ^ 1)); +} + + +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOC) { + Instruction ie = getinstruction(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + removelastinstruction(fs); /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond); +} + + +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e->u.info; /* save jump position */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 0); /* jump when false */ + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ + e->t = NO_JUMP; +} + + +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ +void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { + pc = e->u.info; /* already jump if true */ + break; + } + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 1); /* jump if true */ + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ + e->f = NO_JUMP; +} + + +/* +** Code 'not e', doing constant folding. +*/ +static void codenot (FuncState *fs, expdesc *e) { + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; /* true == not nil == not false */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ + break; + } + case VJMP: { + negatecondition(fs, e); + break; + } + case VRELOC: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + default: lua_assert(0); /* cannot happen */ + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); /* values are useless when negated */ + removevalues(fs, e->t); +} + + +/* +** Check whether expression 'e' is a small literal string +*/ +static int isKstr (FuncState *fs, expdesc *e) { + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && + ttisshrstring(&fs->f->k[e->u.info])); +} + +/* +** Check whether expression 'e' is a literal integer. +*/ +int luaK_isKint (expdesc *e) { + return (e->k == VKINT && !hasjumps(e)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register C +*/ +static int isCint (expdesc *e) { + return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register sC +*/ +static int isSCint (expdesc *e) { + return luaK_isKint(e) && fitsC(e->u.ival); +} + + +/* +** Check whether expression 'e' is a literal integer or float in +** proper range to fit in a register (sB or sC). +*/ +static int isSCnumber (expdesc *e, int *pi, int *isfloat) { + lua_Integer i; + if (e->k == VKINT) + i = e->u.ival; + else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq)) + *isfloat = 1; + else + return 0; /* not a number */ + if (!hasjumps(e) && fitsC(i)) { + *pi = int2sC(cast_int(i)); + return 1; + } + else + return 0; +} + + +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. Upvalues can only be indexed by literal strings. +** Keys can be literal strings in the constant table or arbitrary +** values in registers. +*/ +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + if (k->k == VKSTR) + str2K(fs, k); + lua_assert(!hasjumps(t) && + (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); + if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ + luaK_exp2anyreg(fs, t); /* put it in a register */ + if (t->k == VUPVAL) { + t->u.ind.t = t->u.info; /* upvalue index */ + t->u.ind.idx = k->u.info; /* literal string */ + t->k = VINDEXUP; + } + else { + /* register index of the table */ + t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info; + if (isKstr(fs, k)) { + t->u.ind.idx = k->u.info; /* literal string */ + t->k = VINDEXSTR; + } + else if (isCint(k)) { + t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ + t->k = VINDEXI; + } + else { + t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ + t->k = VINDEXED; + } + } +} + + +/* +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. +*/ +static int validop (int op, TValue *v1, TValue *v2) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ + lua_Integer i; + return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) && + luaV_tointegerns(v2, &i, LUA_FLOORN2I)); + } + case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ + return (nvalue(v2) != 0); + default: return 1; /* everything else is valid */ + } +} + + +/* +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) +*/ +static int constfolding (FuncState *fs, int op, expdesc *e1, + const expdesc *e2) { + TValue v1, v2, res; + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) + return 0; /* non-numeric operands or not safe to fold */ + luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ + if (ttisinteger(&res)) { + e1->k = VKINT; + e1->u.ival = ivalue(&res); + } + else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ + lua_Number n = fltvalue(&res); + if (luai_numisnan(n) || n == 0) + return 0; + e1->k = VKFLT; + e1->u.nval = n; + } + return 1; +} + + +/* +** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) { + lua_assert(baser <= opr && + ((baser == OPR_ADD && opr <= OPR_SHR) || + (baser == OPR_LT && opr <= OPR_LE))); + return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base)); +} + + +/* +** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode unopr2op (UnOpr opr) { + return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) + + cast_int(OP_UNM)); +} + + +/* +** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM) +*/ +l_sinline TMS binopr2TM (BinOpr opr) { + lua_assert(OPR_ADD <= opr && opr <= OPR_SHR); + return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD)); +} + + +/* +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. +*/ +static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { + int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int v2, int flip, int line, + OpCode mmop, TMS event) { + int v1 = luaK_exp2anyreg(fs, e1); + int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0); + freeexps(fs, e1, e2); + e1->u.info = pc; + e1->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); + luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" over +** two registers. +*/ +static void codebinexpval (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + OpCode op = binopr2op(opr, OPR_ADD, OP_ADD); + int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */ + /* 'e1' must be already in a register or it is a constant */ + lua_assert((VNIL <= e1->k && e1->k <= VKSTR) || + e1->k == VNONRELOC || e1->k == VRELOC); + lua_assert(OP_ADD <= op && op <= OP_SHR); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr)); +} + + +/* +** Code binary operators with immediate operands. +*/ +static void codebini (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int flip, int line, + TMS event) { + int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */ + lua_assert(e2->k == VKINT); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event); +} + + +/* +** Code binary operators with K operand. +*/ +static void codebinK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = binopr2TM(opr); + int v2 = e2->u.info; /* K index */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); +} + + +/* Try to code a binary operator negating its second operand. +** For the metamethod, 2nd operand must keep its original value. +*/ +static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int line, TMS event) { + if (!luaK_isKint(e2)) + return 0; /* not an integer constant */ + else { + lua_Integer i2 = e2->u.ival; + if (!(fitsC(i2) && fitsC(-i2))) + return 0; /* not in the proper range */ + else { /* operating a small integer constant */ + int v2 = cast_int(i2); + finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event); + /* correct metamethod argument */ + SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2)); + return 1; /* successfully coded */ + } + } +} + + +static void swapexps (expdesc *e1, expdesc *e2) { + expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */ +} + + +/* +** Code binary operators with no constant operand. +*/ +static void codebinNoK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, opr, e1, e2, line); /* use standard operators */ +} + + +/* +** Code arithmetic operators ('+', '-', ...). If second operand is a +** constant in the proper range, use variant opcodes with K operands. +*/ +static void codearith (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* 'e2' is neither an immediate nor a K operand */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Code commutative operators ('+', '*'). If first operand is a +** numeric constant, change order of operands to try to use an +** immediate or K operator. +*/ +static void codecommutative (FuncState *fs, BinOpr op, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ + swapexps(e1, e2); /* change order */ + flip = 1; + } + if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ + codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD); + else + codearith(fs, op, e1, e2, flip, line); +} + + +/* +** Code bitwise operations; they are all commutative, so the function +** tries to put an integer constant as the 2nd operand (a K operand). +*/ +static void codebitwise (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (e1->k == VKINT) { + swapexps(e1, e2); /* 'e2' will be the constant operand */ + flip = 1; + } + if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* no constants */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Emit code for order comparisons. When using an immediate operand, +** 'isfloat' tells whether the original value was a float. +*/ +static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; + OpCode op; + if (isSCnumber(e2, &im, &isfloat)) { + /* use immediate operand */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_LTI); + } + else if (isSCnumber(e1, &im, &isfloat)) { + /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ + r1 = luaK_exp2anyreg(fs, e2); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_GTI); + } + else { /* regular case, compare two registers */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = luaK_exp2anyreg(fs, e2); + op = binopr2op(opr, OPR_LT, OP_LT); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); + e1->k = VJMP; +} + + +/* +** Emit code for equality comparisons ('==', '~='). +** 'e1' was already put as RK by 'luaK_infix'. +*/ +static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; /* not needed here, but kept for symmetry */ + OpCode op; + if (e1->k != VNONRELOC) { + lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT); + swapexps(e1, e2); + } + r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */ + if (isSCnumber(e2, &im, &isfloat)) { + op = OP_EQI; + r2 = im; /* immediate operand */ + } + else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */ + op = OP_EQK; + r2 = e2->u.info; /* constant index */ + } + else { + op = OP_EQ; /* will compare two registers */ + r2 = luaK_exp2anyreg(fs, e2); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ)); + e1->k = VJMP; +} + + +/* +** Apply prefix operation 'op' to expression 'e'. +*/ +void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) { + static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; + luaK_dischargevars(fs, e); + switch (opr) { + case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ + if (constfolding(fs, opr + LUA_OPUNM, e, &ef)) + break; + /* else */ /* FALLTHROUGH */ + case OPR_LEN: + codeunexpval(fs, unopr2op(opr), e, line); + break; + case OPR_NOT: codenot(fs, e); break; + default: lua_assert(0); + } +} + + +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + luaK_dischargevars(fs, v); + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the stack */ + break; + } + case OPR_ADD: case OPR_SUB: + case OPR_MUL: case OPR_DIV: case OPR_IDIV: + case OPR_MOD: case OPR_POW: + case OPR_BAND: case OPR_BOR: case OPR_BXOR: + case OPR_SHL: case OPR_SHR: { + if (!tonumeral(v, NULL)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be folded or used as an immediate + operand */ + break; + } + case OPR_EQ: case OPR_NE: { + if (!tonumeral(v, NULL)) + luaK_exp2RK(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + case OPR_LT: case OPR_LE: + case OPR_GT: case OPR_GE: { + int dummy, dummy2; + if (!isSCnumber(v, &dummy, &dummy2)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + default: lua_assert(0); + } +} + +/* +** Create code for '(e1 .. e2)'. +** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))', +** because concatenation is right associative), merge both CONCATs. +*/ +static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) { + Instruction *ie2 = previousinstruction(fs); + if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */ + int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */ + lua_assert(e1->u.info + 1 == GETARG_A(*ie2)); + freeexp(fs, e2); + SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */ + SETARG_B(*ie2, n + 1); /* will concatenate one more element */ + } + else { /* 'e2' is not a concatenation */ + luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */ + freeexp(fs, e2); + luaK_fixline(fs, line); + } +} + + +/* +** Finalize code for binary operation, after reading 2nd operand. +*/ +void luaK_posfix (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + luaK_dischargevars(fs, e2); + if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2)) + return; /* done by folding */ + switch (opr) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { /* e1 .. e2 */ + luaK_exp2nextreg(fs, e2); + codeconcat(fs, e1, e2, line); + break; + } + case OPR_ADD: case OPR_MUL: { + codecommutative(fs, opr, e1, e2, line); + break; + } + case OPR_SUB: { + if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB)) + break; /* coded as (r1 + -I) */ + /* ELSE */ + } /* FALLTHROUGH */ + case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: { + codearith(fs, opr, e1, e2, 0, line); + break; + } + case OPR_BAND: case OPR_BOR: case OPR_BXOR: { + codebitwise(fs, opr, e1, e2, line); + break; + } + case OPR_SHL: { + if (isSCint(e1)) { + swapexps(e1, e2); + codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */ + } + else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) { + /* coded as (r1 >> -I) */; + } + else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); + break; + } + case OPR_SHR: { + if (isSCint(e2)) + codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ + else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); + break; + } + case OPR_EQ: case OPR_NE: { + codeeq(fs, opr, e1, e2); + break; + } + case OPR_GT: case OPR_GE: { + /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ + swapexps(e1, e2); + opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT); + } /* FALLTHROUGH */ + case OPR_LT: case OPR_LE: { + codeorder(fs, opr, e1, e2); + break; + } + default: lua_assert(0); + } +} + + +/* +** Change line information associated with current position, by removing +** previous info and adding it again with new line. +*/ +void luaK_fixline (FuncState *fs, int line) { + removelastlineinfo(fs); + savelineinfo(fs, fs->f, line); +} + + +void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { + Instruction *inst = &fs->f->code[pc]; + int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */ + int extra = asize / (MAXARG_C + 1); /* higher bits of array size */ + int rc = asize % (MAXARG_C + 1); /* lower bits of array size */ + int k = (extra > 0); /* true iff needs extra argument */ + *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k); + *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); +} + + +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); + if (tostore == LUA_MULTRET) + tostore = 0; + if (nelems <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems); + else { + int extra = nelems / (MAXARG_C + 1); + nelems %= (MAXARG_C + 1); + luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1); + codeextraarg(fs, extra); + } + fs->freereg = base + 1; /* free registers with list values */ +} + + +/* +** return the final target of a jump (skipping jumps to jumps) +*/ +static int finaltarget (Instruction *code, int i) { + int count; + for (count = 0; count < 100; count++) { /* avoid infinite loops */ + Instruction pc = code[i]; + if (GET_OPCODE(pc) != OP_JMP) + break; + else + i += GETARG_sJ(pc) + 1; + } + return i; +} + + +/* +** Do a final pass over the code of a function, doing small peephole +** optimizations and adjustments. +*/ +void luaK_finish (FuncState *fs) { + int i; + Proto *p = fs->f; + for (i = 0; i < fs->pc; i++) { + Instruction *pc = &p->code[i]; + lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); + switch (GET_OPCODE(*pc)) { + case OP_RETURN0: case OP_RETURN1: { + if (!(fs->needclose || p->is_vararg)) + break; /* no extra work */ + /* else use OP_RETURN to do the extra work */ + SET_OPCODE(*pc, OP_RETURN); + } /* FALLTHROUGH */ + case OP_RETURN: case OP_TAILCALL: { + if (fs->needclose) + SETARG_k(*pc, 1); /* signal that it needs to close */ + if (p->is_vararg) + SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + break; + } + case OP_JMP: { + int target = finaltarget(p->code, i); + fixjump(fs, i, target); + break; + } + default: break; + } + } +} +/* +** $Id: lparser.c $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#define lparser_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include + +/*#include "lua.h"*/ + +/*#include "lcode.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "llex.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lparser.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ + + + +/* maximum number of local variables per function (must be smaller + than 250, due to the bytecode format) */ +#define MAXVARS 200 + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + + +/* because all strings are unified by the scanner, the parser + can use pointer equality for string equality */ +#define eqstr(a,b) ((a) == (b)) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int firstlabel; /* index of first label in this block */ + int firstgoto; /* index of first pending goto in this block */ + lu_byte nactvar; /* # active locals outside the block */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isloop; /* true if 'block' is a loop */ + lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void statement (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static l_noret error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); +} + + +static l_noret errorlimit (FuncState *fs, int limit, const char *what) { + lua_State *L = fs->ls->L; + const char *msg; + int line = fs->f->linedefined; + const char *where = (line == 0) + ? "main function" + : luaO_pushfstring(L, "function at line %d", line); + msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", + what, limit, where); + luaX_syntaxerror(fs->ls, msg); +} + + +static void checklimit (FuncState *fs, int v, int l, const char *what) { + if (v > l) errorlimit(fs, l, what); +} + + +/* +** Test whether next token is 'c'; if so, skip it. +*/ +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +/* +** Check that next token is 'c'. +*/ +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + + +/* +** Check that next token is 'c' and skip it. +*/ +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + +/* +** Check that next token is 'what' and skip it. In case of error, +** raise an error that the expected 'what' should match a 'who' +** in line 'where' (if that is not the current line). +*/ +static void check_match (LexState *ls, int what, int who, int where) { + if (l_unlikely(!testnext(ls, what))) { + if (where == ls->linenumber) /* all in the same line? */ + error_expected(ls, what); /* do not need a complex message */ + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + "%s expected (to close %s at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.info = i; +} + + +static void codestring (expdesc *e, TString *s) { + e->f = e->t = NO_JUMP; + e->k = VKSTR; + e->u.strval = s; +} + + +static void codename (LexState *ls, expdesc *e) { + codestring(e, str_checkname(ls)); +} + + +/* +** Register a new local variable in the active 'Proto' (for debug +** information). +*/ +static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, + LocVar, SHRT_MAX, "local variables"); + while (oldsize < f->sizelocvars) + f->locvars[oldsize++].varname = NULL; + f->locvars[fs->ndebugvars].varname = varname; + f->locvars[fs->ndebugvars].startpc = fs->pc; + luaC_objbarrier(ls->L, f, varname); + return fs->ndebugvars++; +} + + +/* +** Create a new local variable with the given 'name'. Return its index +** in the function. +*/ +static int new_localvar (LexState *ls, TString *name) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Dyndata *dyd = ls->dyd; + Vardesc *var; + checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, + MAXVARS, "local variables"); + luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, + dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); + var = &dyd->actvar.arr[dyd->actvar.n++]; + var->vd.kind = VDKREG; /* default */ + var->vd.name = name; + return dyd->actvar.n - 1 - fs->firstlocal; +} + +#define new_localvarliteral(ls,v) \ + new_localvar(ls, \ + luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); + + + +/* +** Return the "variable description" (Vardesc) of a given variable. +** (Unless noted otherwise, all variables are referred to by their +** compiler indices.) +*/ +static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { + return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx]; +} + + +/* +** Convert 'nvar', a compiler index level, to its corresponding +** register. For that, search for the highest variable below that level +** that is in a register and uses its register index ('ridx') plus one. +*/ +static int reglevel (FuncState *fs, int nvar) { + while (nvar-- > 0) { + Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ + if (vd->vd.kind != RDKCTC) /* is in a register? */ + return vd->vd.ridx + 1; + } + return 0; /* no variables in registers */ +} + + +/* +** Return the number of variables in the register stack for the given +** function. +*/ +int luaY_nvarstack (FuncState *fs) { + return reglevel(fs, fs->nactvar); +} + + +/* +** Get the debug-information entry for current variable 'vidx'. +*/ +static LocVar *localdebuginfo (FuncState *fs, int vidx) { + Vardesc *vd = getlocalvardesc(fs, vidx); + if (vd->vd.kind == RDKCTC) + return NULL; /* no debug info. for constants */ + else { + int idx = vd->vd.pidx; + lua_assert(idx < fs->ndebugvars); + return &fs->f->locvars[idx]; + } +} + + +/* +** Create an expression representing variable 'vidx' +*/ +static void init_var (FuncState *fs, expdesc *e, int vidx) { + e->f = e->t = NO_JUMP; + e->k = VLOCAL; + e->u.var.vidx = vidx; + e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; +} + + +/* +** Raises an error if variable described by 'e' is read only +*/ +static void check_readonly (LexState *ls, expdesc *e) { + FuncState *fs = ls->fs; + TString *varname = NULL; /* to be set if variable is const */ + switch (e->k) { + case VCONST: { + varname = ls->dyd->actvar.arr[e->u.info].vd.name; + break; + } + case VLOCAL: { + Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); + if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ + varname = vardesc->vd.name; + break; + } + case VUPVAL: { + Upvaldesc *up = &fs->f->upvalues[e->u.info]; + if (up->kind != VDKREG) + varname = up->name; + break; + } + default: + return; /* other cases cannot be read-only */ + } + if (varname) { + const char *msg = luaO_pushfstring(ls->L, + "attempt to assign to const variable '%s'", getstr(varname)); + luaK_semerror(ls, msg); /* error */ + } +} + + +/* +** Start the scope for the last 'nvars' created variables. +*/ +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + int reglevel = luaY_nvarstack(fs); + int i; + for (i = 0; i < nvars; i++) { + int vidx = fs->nactvar++; + Vardesc *var = getlocalvardesc(fs, vidx); + var->vd.ridx = reglevel++; + var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + } +} + + +/* +** Close the scope for all variables up to level 'tolevel'. +** (debug info.) +*/ +static void removevars (FuncState *fs, int tolevel) { + fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); + while (fs->nactvar > tolevel) { + LocVar *var = localdebuginfo(fs, --fs->nactvar); + if (var) /* does it have debug information? */ + var->endpc = fs->pc; + } +} + + +/* +** Search the upvalues of the function 'fs' for one +** with the given 'name'. +*/ +static int searchupvalue (FuncState *fs, TString *name) { + int i; + Upvaldesc *up = fs->f->upvalues; + for (i = 0; i < fs->nups; i++) { + if (eqstr(up[i].name, name)) return i; + } + return -1; /* not found */ +} + + +static Upvaldesc *allocupvalue (FuncState *fs) { + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); + luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, + Upvaldesc, MAXUPVAL, "upvalues"); + while (oldsize < f->sizeupvalues) + f->upvalues[oldsize++].name = NULL; + return &f->upvalues[fs->nups++]; +} + + +static int newupvalue (FuncState *fs, TString *name, expdesc *v) { + Upvaldesc *up = allocupvalue(fs); + FuncState *prev = fs->prev; + if (v->k == VLOCAL) { + up->instack = 1; + up->idx = v->u.var.ridx; + up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; + lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); + } + else { + up->instack = 0; + up->idx = cast_byte(v->u.info); + up->kind = prev->f->upvalues[v->u.info].kind; + lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); + } + up->name = name; + luaC_objbarrier(fs->ls->L, fs->f, name); + return fs->nups - 1; +} + + +/* +** Look for an active local variable with the name 'n' in the +** function 'fs'. If found, initialize 'var' with it and return +** its expression kind; otherwise return -1. +*/ +static int searchvar (FuncState *fs, TString *n, expdesc *var) { + int i; + for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { + Vardesc *vd = getlocalvardesc(fs, i); + if (eqstr(n, vd->vd.name)) { /* found? */ + if (vd->vd.kind == RDKCTC) /* compile-time constant? */ + init_exp(var, VCONST, fs->firstlocal + i); + else /* real variable */ + init_var(fs, var, i); + return var->k; + } + } + return -1; /* not found */ +} + + +/* +** Mark block where variable at given level was defined +** (to emit close instructions later). +*/ +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl->nactvar > level) + bl = bl->previous; + bl->upval = 1; + fs->needclose = 1; +} + + +/* +** Mark that current block has a to-be-closed variable. +*/ +static void marktobeclosed (FuncState *fs) { + BlockCnt *bl = fs->bl; + bl->upval = 1; + bl->insidetbc = 1; + fs->needclose = 1; +} + + +/* +** Find a variable with the given name 'n'. If it is an upvalue, add +** this upvalue into all intermediate functions. If it is a global, set +** 'var' as 'void' as a flag. +*/ +static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) /* no more levels? */ + init_exp(var, VVOID, 0); /* default is global */ + else { + int v = searchvar(fs, n, var); /* look up locals at current level */ + if (v >= 0) { /* found? */ + if (v == VLOCAL && !base) + markupval(fs, var->u.var.vidx); /* local will be used as an upval */ + } + else { /* not found as local at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + singlevaraux(fs->prev, n, var, 0); /* try upper levels */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ + } + init_exp(var, VUPVAL, idx); /* new or old upvalue */ + } + } +} + + +/* +** Find a variable with the given name 'n', handling global variables +** too. +*/ +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + singlevaraux(fs, varname, var, 1); + if (var->k == VVOID) { /* global name? */ + expdesc key; + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + lua_assert(var->k != VVOID); /* this one must exist */ + luaK_exp2anyregup(fs, var); /* but could be a constant */ + codestring(&key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* env[varname] */ + } +} + + +/* +** Adjust the number of results from an expression list 'e' with 'nexps' +** expressions to 'nvars' values. +*/ +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int needed = nvars - nexps; /* extra values needed */ + if (hasmultret(e->k)) { /* last expression has multiple returns? */ + int extra = needed + 1; /* discount last expression itself */ + if (extra < 0) + extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + } + else { + if (e->k != VVOID) /* at least one expression? */ + luaK_exp2nextreg(fs, e); /* close last expression */ + if (needed > 0) /* missing values? */ + luaK_nil(fs, fs->freereg, needed); /* complete with nils */ + } + if (needed > 0) + luaK_reserveregs(fs, needed); /* registers for extra values */ + else /* adding 'needed' is actually a subtraction */ + fs->freereg += needed; /* remove extra values */ +} + + +#define enterlevel(ls) luaE_incCstack(ls->L) + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +/* +** Generates an error that a goto jumps into the scope of some +** local variable. +*/ +static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { + const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); + const char *msg = " at line %d jumps into the scope of local '%s'"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); + luaK_semerror(ls, msg); /* raise the error */ +} + + +/* +** Solves the goto at index 'g' to given 'label' and removes it +** from the list of pending gotos. +** If it jumps into the scope of some variable, raises an error. +*/ +static void solvegoto (LexState *ls, int g, Labeldesc *label) { + int i; + Labellist *gl = &ls->dyd->gt; /* list of gotos */ + Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ + lua_assert(eqstr(gt->name, label->name)); + if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ + jumpscopeerror(ls, gt); + luaK_patchlist(ls->fs, gt->pc, label->pc); + for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ + gl->arr[i] = gl->arr[i + 1]; + gl->n--; +} + + +/* +** Search for an active label with the given name. +*/ +static Labeldesc *findlabel (LexState *ls, TString *name) { + int i; + Dyndata *dyd = ls->dyd; + /* check labels in current function for a match */ + for (i = ls->fs->firstlabel; i < dyd->label.n; i++) { + Labeldesc *lb = &dyd->label.arr[i]; + if (eqstr(lb->name, name)) /* correct label? */ + return lb; + } + return NULL; /* label not found */ +} + + +/* +** Adds a new label/goto in the corresponding list. +*/ +static int newlabelentry (LexState *ls, Labellist *l, TString *name, + int line, int pc) { + int n = l->n; + luaM_growvector(ls->L, l->arr, n, l->size, + Labeldesc, SHRT_MAX, "labels/gotos"); + l->arr[n].name = name; + l->arr[n].line = line; + l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].close = 0; + l->arr[n].pc = pc; + l->n = n + 1; + return n; +} + + +static int newgotoentry (LexState *ls, TString *name, int line, int pc) { + return newlabelentry(ls, &ls->dyd->gt, name, line, pc); +} + + +/* +** Solves forward jumps. Check whether new label 'lb' matches any +** pending gotos in current block and solves them. Return true +** if any of the gotos need to close upvalues. +*/ +static int solvegotos (LexState *ls, Labeldesc *lb) { + Labellist *gl = &ls->dyd->gt; + int i = ls->fs->bl->firstgoto; + int needsclose = 0; + while (i < gl->n) { + if (eqstr(gl->arr[i].name, lb->name)) { + needsclose |= gl->arr[i].close; + solvegoto(ls, i, lb); /* will remove 'i' from the list */ + } + else + i++; + } + return needsclose; +} + + +/* +** Create a new label with the given 'name' at the given 'line'. +** 'last' tells whether label is the last non-op statement in its +** block. Solves all pending gotos to this new label and adds +** a close instruction if necessary. +** Returns true iff it added a close instruction. +*/ +static int createlabel (LexState *ls, TString *name, int line, + int last) { + FuncState *fs = ls->fs; + Labellist *ll = &ls->dyd->label; + int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); + if (last) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll->arr[l].nactvar = fs->bl->nactvar; + } + if (solvegotos(ls, &ll->arr[l])) { /* need close? */ + luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0); + return 1; + } + return 0; +} + + +/* +** Adjust pending gotos to outer level of a block. +*/ +static void movegotosout (FuncState *fs, BlockCnt *bl) { + int i; + Labellist *gl = &fs->ls->dyd->gt; + /* correct pending gotos to current block */ + for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */ + Labeldesc *gt = &gl->arr[i]; + /* leaving a variable scope? */ + if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar)) + gt->close |= bl->upval; /* jump may need a close */ + gt->nactvar = bl->nactvar; /* update goto level */ + } +} + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { + bl->isloop = isloop; + bl->nactvar = fs->nactvar; + bl->firstlabel = fs->ls->dyd->label.n; + bl->firstgoto = fs->ls->dyd->gt.n; + bl->upval = 0; + bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == luaY_nvarstack(fs)); +} + + +/* +** generates an error for an undefined 'goto'. +*/ +static l_noret undefgoto (LexState *ls, Labeldesc *gt) { + const char *msg; + if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) { + msg = "break outside loop at line %d"; + msg = luaO_pushfstring(ls->L, msg, gt->line); + } + else { + msg = "no visible label '%s' for at line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); + } + luaK_semerror(ls, msg); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + LexState *ls = fs->ls; + int hasclose = 0; + int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop) /* has to fix pending breaks? */ + hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); + if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */ + luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); + fs->freereg = stklevel; /* free registers */ + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ + fs->bl = bl->previous; /* current block now is previous one */ + if (bl->previous) /* was it a nested block? */ + movegotosout(fs, bl); /* update pending gotos to enclosing block */ + else { + if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ + } +} + + +/* +** adds a new prototype into list of prototypes +*/ +static Proto *addprototype (LexState *ls) { + Proto *clp; + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; /* prototype of current function */ + if (fs->np >= f->sizep) { + int oldsize = f->sizep; + luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); + while (oldsize < f->sizep) + f->p[oldsize++] = NULL; + } + f->p[fs->np++] = clp = luaF_newproto(L); + luaC_objbarrier(L, f, clp); + return clp; +} + + +/* +** codes instruction to create new closure in parent function. +** The OP_CLOSURE instruction uses the last available register, +** so that, if it invokes the GC, the GC knows which registers +** are in use at that time. + +*/ +static void codeclosure (LexState *ls, expdesc *v) { + FuncState *fs = ls->fs->prev; + init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + luaK_exp2nextreg(fs, v); /* fix it at the last register */ +} + + +static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { + Proto *f = fs->f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + ls->fs = fs; + fs->pc = 0; + fs->previousline = f->linedefined; + fs->iwthabs = 0; + fs->lasttarget = 0; + fs->freereg = 0; + fs->nk = 0; + fs->nabslineinfo = 0; + fs->np = 0; + fs->nups = 0; + fs->ndebugvars = 0; + fs->nactvar = 0; + fs->needclose = 0; + fs->firstlocal = ls->dyd->actvar.n; + fs->firstlabel = ls->dyd->label.n; + fs->bl = NULL; + f->source = ls->source; + luaC_objbarrier(ls->L, f, f->source); + f->maxstacksize = 2; /* registers 0/1 are always valid */ + enterblock(fs, bl, 0); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ + leaveblock(fs); + lua_assert(fs->bl == NULL); + luaK_finish(fs); + luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction); + luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte); + luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo, + fs->nabslineinfo, AbsLineInfo); + luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue); + luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); + luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); + luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); + ls->fs = fs->prev; + luaC_checkGC(L); +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +/* +** check whether current token is in the follow set of a block. +** 'until' closes syntactical blocks, but do not close scope, +** so it is handled in separate. +*/ +static int block_follow (LexState *ls, int withuntil) { + switch (ls->t.token) { + case TK_ELSE: case TK_ELSEIF: + case TK_END: case TK_EOS: + return 1; + case TK_UNTIL: return withuntil; + default: return 0; + } +} + + +static void statlist (LexState *ls) { + /* statlist -> { stat [';'] } */ + while (!block_follow(ls, 1)) { + if (ls->t.token == TK_RETURN) { + statement(ls); + return; /* 'return' must be last statement */ + } + statement(ls); + } +} + + +static void fieldsel (LexState *ls, expdesc *v) { + /* fieldsel -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyregup(fs, v); + luaX_next(ls); /* skip the dot or colon */ + codename(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +typedef struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of 'record' elements */ + int na; /* number of array elements already stored */ + int tostore; /* number of array elements pending to be stored */ +} ConsControl; + + +static void recfield (LexState *ls, ConsControl *cc) { + /* recfield -> (NAME | '['exp']') = exp */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc tab, key, val; + if (ls->t.token == TK_NAME) { + checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + codename(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + tab = *cc->t; + luaK_indexed(fs, &tab, &key); + expr(ls, &val); + luaK_storevar(fs, &tab, &val); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ + cc->na += cc->tostore; + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); + } + cc->na += cc->tostore; +} + + +static void listfield (LexState *ls, ConsControl *cc) { + /* listfield -> exp */ + expr(ls, &cc->v); + cc->tostore++; +} + + +static void field (LexState *ls, ConsControl *cc) { + /* field -> listfield | recfield */ + switch(ls->t.token) { + case TK_NAME: { /* may be 'listfield' or 'recfield' */ + if (luaX_lookahead(ls) != '=') /* expression? */ + listfield(ls, cc); + else + recfield(ls, cc); + break; + } + case '[': { + recfield(ls, cc); + break; + } + default: { + listfield(ls, cc); + break; + } + } +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> '{' [ field { sep field } [sep] ] '}' + sep -> ',' | ';' */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + ConsControl cc; + luaK_code(fs, 0); /* space for extra arg. */ + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ + luaK_reserveregs(fs, 1); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + field(ls, &cc); + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); +} + +/* }====================================================================== */ + + +static void setvararg (FuncState *fs, int nparams) { + fs->f->is_vararg = 1; + luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); +} + + +static void parlist (LexState *ls) { + /* parlist -> [ {NAME ','} (NAME | '...') ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + int isvararg = 0; + if (ls->t.token != ')') { /* is 'parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { + new_localvar(ls, str_checkname(ls)); + nparams++; + break; + } + case TK_DOTS: { + luaX_next(ls); + isvararg = 1; + break; + } + default: luaX_syntaxerror(ls, " or '...' expected"); + } + } while (!isvararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar); + if (isvararg) + setvararg(fs, f->numparams); /* declared vararg */ + luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int ismethod, int line) { + /* body -> '(' parlist ')' block END */ + FuncState new_fs; + BlockCnt bl; + new_fs.f = addprototype(ls); + new_fs.f->linedefined = line; + open_func(ls, &new_fs, &bl); + checknext(ls, '('); + if (ismethod) { + new_localvarliteral(ls, "self"); /* create 'self' parameter */ + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + statlist(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + codeclosure(ls, e); + close_func(ls); +} + + +static int explist (LexState *ls, expdesc *v) { + /* explist -> expr { ',' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f, int line) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + switch (ls->t.token) { + case '(': { /* funcargs -> '(' [ explist ] ')' */ + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist(ls, &args); + if (hasmultret(args.k)) + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(&args, ls->t.seminfo.ts); + luaX_next(ls); /* must use 'seminfo' before 'next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + } + } +} + + +static void suffixedexp (LexState *ls, expdesc *v) { + /* suffixedexp -> + primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + primaryexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* fieldsel */ + fieldsel(ls, v); + break; + } + case '[': { /* '[' exp ']' */ + expdesc key; + luaK_exp2anyregup(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* ':' NAME funcargs */ + expdesc key; + luaX_next(ls); + codename(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v, line); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v, line); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | + constructor | FUNCTION body | suffixedexp */ + switch (ls->t.token) { + case TK_FLT: { + init_exp(v, VKFLT, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_INT: { + init_exp(v, VKINT, 0); + v->u.ival = ls->t.seminfo.i; + break; + } + case TK_STRING: { + codestring(v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use '...' outside a vararg function"); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + suffixedexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '~': return OPR_BNOT; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case '/': return OPR_DIV; + case TK_IDIV: return OPR_IDIV; + case '&': return OPR_BAND; + case '|': return OPR_BOR; + case '~': return OPR_BXOR; + case TK_SHL: return OPR_SHL; + case TK_SHR: return OPR_SHR; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +/* +** Priority table for binary operators. +*/ +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {10, 10}, {10, 10}, /* '+' '-' */ + {11, 11}, {11, 11}, /* '*' '%' */ + {14, 13}, /* '^' (right associative) */ + {11, 11}, {11, 11}, /* '/' '//' */ + {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ + {7, 7}, {7, 7}, /* '<<' '>>' */ + {9, 8}, /* '..' (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ +}; + +#define UNARY_PRIORITY 12 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where 'binop' is any binary operator with a priority higher than 'limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */ + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v, line); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than 'limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2, line); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static void block (LexState *ls) { + /* block -> statlist */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + statlist(ls); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to an upvalue/local variable, the +** upvalue/local variable is begin used in a previous assignment to a +** table. If so, save original upvalue/local value in a safe place and +** use this safe copy in the previous assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { /* check all previous assignments */ + if (vkisindexed(lh->v.k)) { /* assignment to table field? */ + if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ + if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { + conflict = 1; /* table is the upvalue being assigned now */ + lh->v.k = VINDEXSTR; + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + } + else { /* table is a register */ + if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) { + conflict = 1; /* table is the local being assigned now */ + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + /* is index the local being assigned? */ + if (lh->v.k == VINDEXED && v->k == VLOCAL && + lh->v.u.ind.idx == v->u.var.ridx) { + conflict = 1; + lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + } + } + } + } + if (conflict) { + /* copy upvalue/local value to a temporary (in position 'extra') */ + if (v->k == VLOCAL) + luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0); + else + luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); + luaK_reserveregs(fs, 1); + } +} + +/* +** Parse and compile a multiple assignment. The first "variable" +** (a 'suffixedexp') was already read by the caller. +** +** assignment -> suffixedexp restassign +** restassign -> ',' suffixedexp restassign | '=' explist +*/ +static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, vkisvar(lh->v.k), "syntax error"); + check_readonly(ls, &lh->v); + if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ + struct LHS_assign nv; + nv.prev = lh; + suffixedexp(ls, &nv.v); + if (!vkisindexed(nv.v.k)) + check_conflict(ls, lh, &nv.v); + enterlevel(ls); /* control recursion depth */ + restassign(ls, &nv, nvars+1); + leavelevel(ls); + } + else { /* restassign -> '=' explist */ + int nexps; + checknext(ls, '='); + nexps = explist(ls, &e); + if (nexps != nvars) + adjust_assign(ls, nvars, nexps, &e); + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void gotostat (LexState *ls) { + FuncState *fs = ls->fs; + int line = ls->linenumber; + TString *name = str_checkname(ls); /* label's name */ + Labeldesc *lb = findlabel(ls, name); + if (lb == NULL) /* no label? */ + /* forward jump; will be resolved when the label is declared */ + newgotoentry(ls, name, line, luaK_jump(fs)); + else { /* found a label */ + /* backward jump; will be resolved here */ + int lblevel = reglevel(fs, lb->nactvar); /* label level */ + if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */ + luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0); + /* create jump and link it to the label */ + luaK_patchlist(fs, luaK_jump(fs), lb->pc); + } +} + + +/* +** Break statement. Semantically equivalent to "goto break". +*/ +static void breakstat (LexState *ls) { + int line = ls->linenumber; + luaX_next(ls); /* skip break */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs)); +} + + +/* +** Check whether there is already a label with the given 'name'. +*/ +static void checkrepeated (LexState *ls, TString *name) { + Labeldesc *lb = findlabel(ls, name); + if (l_unlikely(lb != NULL)) { /* already defined? */ + const char *msg = "label '%s' already defined on line %d"; + msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); + luaK_semerror(ls, msg); /* error */ + } +} + + +static void labelstat (LexState *ls, TString *name, int line) { + /* label -> '::' NAME '::' */ + checknext(ls, TK_DBCOLON); /* skip double colon */ + while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) + statement(ls); /* skip other no-op statements */ + checkrepeated(ls, name); /* check for repeated labels */ + createlabel(ls, name, line, block_follow(ls, 0)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_jumpto(fs, whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + statlist(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + leaveblock(fs); /* finish scope */ + if (bl2.upval) { /* upvalues? */ + int exit = luaK_jump(fs); /* normal exit must jump over fix */ + luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ + luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0); + condexit = luaK_jump(fs); /* repeat after closing upvalues */ + luaK_patchtohere(fs, exit); /* normal exit comes to here */ + } + luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ + leaveblock(fs); /* finish loop */ +} + + +/* +** Read an expression and generate code to put its results in next +** stack slot. +** +*/ +static void exp1 (LexState *ls) { + expdesc e; + expr(ls, &e); + luaK_exp2nextreg(ls->fs, &e); + lua_assert(e.k == VNONRELOC); +} + + +/* +** Fix for instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua). 'back' true means +** a back jump. +*/ +static void fixforjump (FuncState *fs, int pc, int dest, int back) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + if (back) + offset = -offset; + if (l_unlikely(offset > MAXARG_Bx)) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_Bx(*jmp, offset); +} + + +/* +** Generate code for a 'for' loop. +*/ +static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { + /* forbody -> DO block */ + static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; + static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + checknext(ls, TK_DO); + prep = luaK_codeABx(fs, forprep[isgen], base, 0); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + fixforjump(fs, prep, luaK_getlabel(fs), 0); + if (isgen) { /* generic for? */ + luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); + luaK_fixline(fs, line); + } + endfor = luaK_codeABx(fs, forloop[isgen], base, 0); + fixforjump(fs, endfor, prep + 1, 1); + luaK_fixline(fs, line); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp,exp[,exp] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvar(ls, varname); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_int(fs, fs->freereg, 1); + luaK_reserveregs(fs, 1); + } + adjustlocalvars(ls, 3); /* control variables */ + forbody(ls, base, line, 1, 0); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 5; /* gen, state, control, toclose, 'indexname' */ + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + /* create declared variables */ + new_localvar(ls, indexname); + while (testnext(ls, ',')) { + new_localvar(ls, str_checkname(ls)); + nvars++; + } + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 4, explist(ls, &e), &e); + adjustlocalvars(ls, 4); /* control variables */ + marktobeclosed(fs); /* last control var. must be closed */ + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 4, 1); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip 'for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, "'=' or 'in' expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope ('break' jumps to this point) */ +} + + +static void test_then_block (LexState *ls, int *escapelist) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + BlockCnt bl; + FuncState *fs = ls->fs; + expdesc v; + int jf; /* instruction to skip 'then' code (if condition is false) */ + luaX_next(ls); /* skip IF or ELSEIF */ + expr(ls, &v); /* read condition */ + checknext(ls, TK_THEN); + if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */ + int line = ls->linenumber; + luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */ + luaX_next(ls); /* skip 'break' */ + enterblock(fs, &bl, 0); /* must enter block before 'goto' */ + newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t); + while (testnext(ls, ';')) {} /* skip semicolons */ + if (block_follow(ls, 0)) { /* jump is the entire block? */ + leaveblock(fs); + return; /* and that is it */ + } + else /* must skip over 'then' part if condition is false */ + jf = luaK_jump(fs); + } + else { /* regular case (not a break) */ + luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ + enterblock(fs, &bl, 0); + jf = v.f; + } + statlist(ls); /* 'then' part */ + leaveblock(fs); + if (ls->t.token == TK_ELSE || + ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ + luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ + luaK_patchtohere(fs, jf); +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int escapelist = NO_JUMP; /* exit list for finished parts */ + test_then_block(ls, &escapelist); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) + test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ + if (testnext(ls, TK_ELSE)) + block(ls); /* 'else' part */ + check_match(ls, TK_END, TK_IF, line); + luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ +} + + +static void localfunc (LexState *ls) { + expdesc b; + FuncState *fs = ls->fs; + int fvar = fs->nactvar; /* function's variable index */ + new_localvar(ls, str_checkname(ls)); /* new local variable */ + adjustlocalvars(ls, 1); /* enter its scope */ + body(ls, &b, 0, ls->linenumber); /* function created in next register */ + /* debug information will only see the variable after this point! */ + localdebuginfo(fs, fvar)->startpc = fs->pc; +} + + +static int getlocalattribute (LexState *ls) { + /* ATTRIB -> ['<' Name '>'] */ + if (testnext(ls, '<')) { + const char *attr = getstr(str_checkname(ls)); + checknext(ls, '>'); + if (strcmp(attr, "const") == 0) + return RDKCONST; /* read-only variable */ + else if (strcmp(attr, "close") == 0) + return RDKTOCLOSE; /* to-be-closed variable */ + else + luaK_semerror(ls, + luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); + } + return VDKREG; /* regular variable */ +} + + +static void checktoclose (FuncState *fs, int level) { + if (level != -1) { /* is there a to-be-closed variable? */ + marktobeclosed(fs); + luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0); + } +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ + FuncState *fs = ls->fs; + int toclose = -1; /* index of to-be-closed variable (if any) */ + Vardesc *var; /* last variable */ + int vidx, kind; /* index and kind of last variable */ + int nvars = 0; + int nexps; + expdesc e; + do { + vidx = new_localvar(ls, str_checkname(ls)); + kind = getlocalattribute(ls); + getlocalvardesc(fs, vidx)->vd.kind = kind; + if (kind == RDKTOCLOSE) { /* to-be-closed? */ + if (toclose != -1) /* one already present? */ + luaK_semerror(ls, "multiple to-be-closed variables in local list"); + toclose = fs->nactvar + nvars; + } + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + var = getlocalvardesc(fs, vidx); /* get last variable */ + if (nvars == nexps && /* no adjustments? */ + var->vd.kind == RDKCONST && /* last variable is const? */ + luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ + var->vd.kind = RDKCTC; /* variable is a compile-time constant */ + adjustlocalvars(ls, nvars - 1); /* exclude last variable */ + fs->nactvar++; /* but count it */ + } + else { + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); + } + checktoclose(fs, toclose); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {fieldsel} [':' NAME] */ + int ismethod = 0; + singlevar(ls, v); + while (ls->t.token == '.') + fieldsel(ls, v); + if (ls->t.token == ':') { + ismethod = 1; + fieldsel(ls, v); + } + return ismethod; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int ismethod; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + ismethod = funcname(ls, &v); + body(ls, &b, ismethod, line); + check_readonly(ls, &v); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + suffixedexp(ls, &v.v); + if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ + v.prev = NULL; + restassign(ls, &v, 1); + } + else { /* stat -> func */ + Instruction *inst; + check_condition(ls, v.v.k == VCALL, "syntax error"); + inst = &getinstruction(fs, &v.v); + SETARG_C(*inst, 1); /* call statement uses no results */ + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN [explist] [';'] */ + FuncState *fs = ls->fs; + expdesc e; + int nret; /* number of values being returned */ + int first = luaY_nvarstack(fs); /* first slot to be returned */ + if (block_follow(ls, 1) || ls->t.token == ';') + nret = 0; /* return no values */ + else { + nret = explist(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */ + SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs)); + } + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); /* can use original slot */ + else { /* values must go to the top of the stack */ + luaK_exp2nextreg(fs, &e); + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); + testnext(ls, ';'); /* skip optional semicolon */ +} + + +static void statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + enterlevel(ls); + switch (ls->t.token) { + case ';': { /* stat -> ';' (empty statement) */ + luaX_next(ls); /* skip ';' */ + break; + } + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + break; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + break; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + break; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + break; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + break; + } + case TK_FUNCTION: { /* stat -> funcstat */ + funcstat(ls, line); + break; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + break; + } + case TK_DBCOLON: { /* stat -> label */ + luaX_next(ls); /* skip double colon */ + labelstat(ls, str_checkname(ls), line); + break; + } + case TK_RETURN: { /* stat -> retstat */ + luaX_next(ls); /* skip RETURN */ + retstat(ls); + break; + } + case TK_BREAK: { /* stat -> breakstat */ + breakstat(ls); + break; + } + case TK_GOTO: { /* stat -> 'goto' NAME */ + luaX_next(ls); /* skip 'goto' */ + gotostat(ls); + break; + } + default: { /* stat -> func | assignment */ + exprstat(ls); + break; + } + } + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= luaY_nvarstack(ls->fs)); + ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */ + leavelevel(ls); +} + +/* }====================================================================== */ + + +/* +** compiles the main function, which is a regular vararg function with an +** upvalue named LUA_ENV +*/ +static void mainfunc (LexState *ls, FuncState *fs) { + BlockCnt bl; + Upvaldesc *env; + open_func(ls, fs, &bl); + setvararg(fs, 0); /* main function is always declared vararg */ + env = allocupvalue(fs); /* ...set environment upvalue */ + env->instack = 1; + env->idx = 0; + env->kind = VDKREG; + env->name = ls->envn; + luaC_objbarrier(ls->L, fs->f, env->name); + luaX_next(ls); /* read first token */ + statlist(ls); /* parse main body */ + check(ls, TK_EOS); + close_func(ls); +} + + +LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { + LexState lexstate; + FuncState funcstate; + LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ + setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ + luaD_inctop(L); + lexstate.h = luaH_new(L); /* create table for scanner */ + sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ + luaD_inctop(L); + funcstate.f = cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ + luaC_objbarrier(L, funcstate.f, funcstate.f->source); + lexstate.buff = buff; + lexstate.dyd = dyd; + dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; + luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); + mainfunc(&lexstate, &funcstate); + lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); + /* all scopes should be correctly finished */ + lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); + L->top.p--; /* remove scanner's table */ + return cl; /* closure is on the stack, too */ +} + +/* +** $Id: ldebug.c $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + +#define ldebug_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lapi.h"*/ +/*#include "lcode.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ +/*#include "lvm.h"*/ + + + +#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) + + +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, + const char **name); + + +static int currentpc (CallInfo *ci) { + lua_assert(isLua(ci)); + return pcRel(ci->u.l.savedpc, ci_func(ci)->p); +} + + +/* +** Get a "base line" to find the line corresponding to an instruction. +** Base lines are regularly placed at MAXIWTHABS intervals, so usually +** an integer division gets the right place. When the source file has +** large sequences of empty/comment lines, it may need extra entries, +** so the original estimate needs a correction. +** If the original estimate is -1, the initial 'if' ensures that the +** 'while' will run at least once. +** The assertion that the estimate is a lower bound for the correct base +** is valid as long as the debug info has been generated with the same +** value for MAXIWTHABS or smaller. (Previous releases use a little +** smaller value.) +*/ +static int getbaseline (const Proto *f, int pc, int *basepc) { + if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { + *basepc = -1; /* start from the beginning */ + return f->linedefined; + } + else { + int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ + /* estimate must be a lower bound of the correct base */ + lua_assert(i < 0 || + (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); + while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) + i++; /* low estimate; adjust it */ + *basepc = f->abslineinfo[i].pc; + return f->abslineinfo[i].line; + } +} + + +/* +** Get the line corresponding to instruction 'pc' in function 'f'; +** first gets a base line and from there does the increments until +** the desired instruction. +*/ +int luaG_getfuncline (const Proto *f, int pc) { + if (f->lineinfo == NULL) /* no debug information? */ + return -1; + else { + int basepc; + int baseline = getbaseline(f, pc, &basepc); + while (basepc++ < pc) { /* walk until given instruction */ + lua_assert(f->lineinfo[basepc] != ABSLINEINFO); + baseline += f->lineinfo[basepc]; /* correct line */ + } + return baseline; + } +} + + +static int getcurrentline (CallInfo *ci) { + return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); +} + + +/* +** Set 'trap' for all active Lua frames. +** This function can be called during a signal, under "reasonable" +** assumptions. A new 'ci' is completely linked in the list before it +** becomes part of the "active" list, and we assume that pointers are +** atomic; see comment in next function. +** (A compiler doing interprocedural optimizations could, theoretically, +** reorder memory writes in such a way that the list could be +** temporarily broken while inserting a new element. We simply assume it +** has no good reasons to do that.) +*/ +static void settraps (CallInfo *ci) { + for (; ci != NULL; ci = ci->previous) + if (isLua(ci)) + ci->u.l.trap = 1; +} + + +/* +** This function can be called during a signal, under "reasonable" +** assumptions. +** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount') +** are for debug only, and it is no problem if they get arbitrary +** values (causes at most one wrong hook call). 'hookmask' is an atomic +** value. We assume that pointers are atomic too (e.g., gcc ensures that +** for all platforms where it runs). Moreover, 'hook' is always checked +** before being called (see 'luaD_hook'). +*/ +LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + if (mask) + settraps(L->ci); /* to trace inside 'luaV_execute' */ +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + if (level < 0) return 0; /* invalid (negative) level */ + lua_lock(L); + for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) + level--; + if (level == 0 && ci != &L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = ci; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static const char *upvalname (const Proto *p, int uv) { + TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); + if (s == NULL) return "?"; + else return getstr(s); +} + + +static const char *findvararg (CallInfo *ci, int n, StkId *pos) { + if (clLvalue(s2v(ci->func.p))->p->is_vararg) { + int nextra = ci->u.l.nextraargs; + if (n >= -nextra) { /* 'n' is negative */ + *pos = ci->func.p - nextra - (n + 1); + return "(vararg)"; /* generic name for any vararg */ + } + } + return NULL; /* no such vararg */ +} + + +const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { + StkId base = ci->func.p + 1; + const char *name = NULL; + if (isLua(ci)) { + if (n < 0) /* access to vararg values? */ + return findvararg(ci, n, pos); + else + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); + } + if (name == NULL) { /* no 'standard' name? */ + StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p; + if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ + /* generic name for any valid slot */ + name = isLua(ci) ? "(temporary)" : "(C temporary)"; + } + else + return NULL; /* no name */ + } + if (pos) + *pos = base + (n - 1); + return name; +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + lua_lock(L); + if (ar == NULL) { /* information about non-active function? */ + if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */ + name = NULL; + else /* consider live variables at function start (parameters) */ + name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0); + } + else { /* active function; get information through 'ar' */ + StkId pos = NULL; /* to avoid warnings */ + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobjs2s(L, L->top.p, pos); + api_incr_top(L); + } + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + StkId pos = NULL; /* to avoid warnings */ + const char *name; + lua_lock(L); + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobjs2s(L, pos, L->top.p - 1); + L->top.p--; /* pop value */ + } + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (noLuaClosure(cl)) { + ar->source = "=[C]"; + ar->srclen = LL("=[C]"); + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + const Proto *p = cl->l.p; + if (p->source) { + ar->source = getstr(p->source); + ar->srclen = tsslen(p->source); + } + else { + ar->source = "=?"; + ar->srclen = LL("=?"); + } + ar->linedefined = p->linedefined; + ar->lastlinedefined = p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, ar->srclen); +} + + +static int nextline (const Proto *p, int currentline, int pc) { + if (p->lineinfo[pc] != ABSLINEINFO) + return currentline + p->lineinfo[pc]; + else + return luaG_getfuncline(p, pc); +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (noLuaClosure(f)) { + setnilvalue(s2v(L->top.p)); + api_incr_top(L); + } + else { + int i; + TValue v; + const Proto *p = f->l.p; + int currentline = p->linedefined; + Table *t = luaH_new(L); /* new table to store active lines */ + sethvalue2s(L, L->top.p, t); /* push it on stack */ + api_incr_top(L); + setbtvalue(&v); /* boolean 'true' to be the value of all indices */ + if (!p->is_vararg) /* regular function? */ + i = 0; /* consider all instructions */ + else { /* vararg function */ + lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); + currentline = nextline(p, currentline, 0); + i = 1; /* skip first instruction (OP_VARARGPREP) */ + } + for (; i < p->sizelineinfo; i++) { /* for each instruction */ + currentline = nextline(p, currentline, i); /* get its line */ + luaH_setint(L, t, currentline, &v); /* table[line] = true */ + } + } +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + /* calling function is a known function? */ + if (ci != NULL && !(ci->callstatus & CIST_TAIL)) + return funcnamefromcall(L, ci->previous, name); + else return NULL; /* no way to find a name */ +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1; + break; + } + case 'u': { + ar->nups = (f == NULL) ? 0 : f->c.nupvalues; + if (noLuaClosure(f)) { + ar->isvararg = 1; + ar->nparams = 0; + } + else { + ar->isvararg = f->l.p->is_vararg; + ar->nparams = f->l.p->numparams; + } + break; + } + case 't': { + ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; + break; + } + case 'n': { + ar->namewhat = getfuncname(L, ci, &ar->name); + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'r': { + if (ci == NULL || !(ci->callstatus & CIST_TRAN)) + ar->ftransfer = ar->ntransfer = 0; + else { + ar->ftransfer = ci->u2.transferinfo.ftransfer; + ar->ntransfer = ci->u2.transferinfo.ntransfer; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *cl; + CallInfo *ci; + TValue *func; + lua_lock(L); + if (*what == '>') { + ci = NULL; + func = s2v(L->top.p - 1); + api_check(L, ttisfunction(func), "function expected"); + what++; /* skip the '>' */ + L->top.p--; /* pop function */ + } + else { + ci = ar->i_ci; + func = s2v(ci->func.p); + lua_assert(ttisfunction(func)); + } + cl = ttisclosure(func) ? clvalue(func) : NULL; + status = auxgetinfo(L, what, ar, cl, ci); + if (strchr(what, 'f')) { + setobj2s(L, L->top.p, func); + api_incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, cl); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution +** ======================================================= +*/ + +static const char *getobjname (const Proto *p, int lastpc, int reg, + const char **name); + + +/* +** Find a "name" for the constant 'c'. +*/ +static void kname (const Proto *p, int c, const char **name) { + TValue *kvalue = &p->k[c]; + *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?"; +} + + +/* +** Find a "name" for the register 'c'. +*/ +static void rname (const Proto *p, int pc, int c, const char **name) { + const char *what = getobjname(p, pc, c, name); /* search for 'c' */ + if (!(what && *what == 'c')) /* did not find a constant name? */ + *name = "?"; +} + + +/* +** Find a "name" for a 'C' value in an RK instruction. +*/ +static void rkname (const Proto *p, int pc, Instruction i, const char **name) { + int c = GETARG_C(i); /* key index */ + if (GETARG_k(i)) /* is 'c' a constant? */ + kname(p, c, name); + else /* 'c' is a register */ + rname(p, pc, c, name); +} + + +static int filterpc (int pc, int jmptarget) { + if (pc < jmptarget) /* is code conditional (inside a jump)? */ + return -1; /* cannot know who sets that register */ + else return pc; /* current position sets that register */ +} + + +/* +** Try to find last instruction before 'lastpc' that modified register 'reg'. +*/ +static int findsetreg (const Proto *p, int lastpc, int reg) { + int pc; + int setreg = -1; /* keep last instruction that changed 'reg' */ + int jmptarget = 0; /* any code before this address is conditional */ + if (testMMMode(GET_OPCODE(p->code[lastpc]))) + lastpc--; /* previous instruction was not actually executed */ + for (pc = 0; pc < lastpc; pc++) { + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int change; /* true if current instruction changed 'reg' */ + switch (op) { + case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ + int b = GETARG_B(i); + change = (a <= reg && reg <= a + b); + break; + } + case OP_TFORCALL: { /* affect all regs above its base */ + change = (reg >= a + 2); + break; + } + case OP_CALL: + case OP_TAILCALL: { /* affect all registers above base */ + change = (reg >= a); + break; + } + case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ + int b = GETARG_sJ(i); + int dest = pc + 1 + b; + /* jump does not skip 'lastpc' and is larger than current one? */ + if (dest <= lastpc && dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + change = 0; + break; + } + default: /* any instruction that sets A */ + change = (testAMode(op) && reg == a); + break; + } + if (change) + setreg = filterpc(pc, jmptarget); + } + return setreg; +} + + +/* +** Check whether table being indexed by instruction 'i' is the +** environment '_ENV' +*/ +static const char *gxf (const Proto *p, int pc, Instruction i, int isup) { + int t = GETARG_B(i); /* table index */ + const char *name; /* name of indexed variable */ + if (isup) /* is an upvalue? */ + name = upvalname(p, t); + else + getobjname(p, pc, t, &name); + return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; +} + + +static const char *getobjname (const Proto *p, int lastpc, int reg, + const char **name) { + int pc; + *name = luaF_getlocalname(p, reg + 1, lastpc); + if (*name) /* is a local? */ + return "local"; + /* else try symbolic execution */ + pc = findsetreg(p, lastpc, reg); + if (pc != -1) { /* could find instruction? */ + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_MOVE: { + int b = GETARG_B(i); /* move from 'b' to 'a' */ + if (b < GETARG_A(i)) + return getobjname(p, pc, b, name); /* get name for 'b' */ + break; + } + case OP_GETTABUP: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return gxf(p, pc, i, 1); + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + rname(p, pc, k, name); + return gxf(p, pc, i, 0); + } + case OP_GETI: { + *name = "integer index"; + return "field"; + } + case OP_GETFIELD: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return gxf(p, pc, i, 0); + } + case OP_GETUPVAL: { + *name = upvalname(p, GETARG_B(i)); + return "upvalue"; + } + case OP_LOADK: + case OP_LOADKX: { + int b = (op == OP_LOADK) ? GETARG_Bx(i) + : GETARG_Ax(p->code[pc + 1]); + if (ttisstring(&p->k[b])) { + *name = svalue(&p->k[b]); + return "constant"; + } + break; + } + case OP_SELF: { + rkname(p, pc, i, name); + return "method"; + } + default: break; /* go through to return NULL */ + } + } + return NULL; /* could not find reasonable name */ +} + + +/* +** Try to find a name for a function based on the code that called it. +** (Only works when function was called by a Lua function.) +** Returns what the name is (e.g., "for iterator", "method", +** "metamethod") and sets '*name' to point to the name. +*/ +static const char *funcnamefromcode (lua_State *L, const Proto *p, + int pc, const char **name) { + TMS tm = (TMS)0; /* (initial value avoids warnings) */ + Instruction i = p->code[pc]; /* calling instruction */ + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + return getobjname(p, pc, GETARG_A(i), name); /* get function name */ + case OP_TFORCALL: { /* for iterator */ + *name = "for iterator"; + return "for iterator"; + } + /* other instructions can do calls through metamethods */ + case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: + case OP_GETI: case OP_GETFIELD: + tm = TM_INDEX; + break; + case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD: + tm = TM_NEWINDEX; + break; + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + tm = cast(TMS, GETARG_C(i)); + break; + } + case OP_UNM: tm = TM_UNM; break; + case OP_BNOT: tm = TM_BNOT; break; + case OP_LEN: tm = TM_LEN; break; + case OP_CONCAT: tm = TM_CONCAT; break; + case OP_EQ: tm = TM_EQ; break; + /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */ + case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break; + case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break; + case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break; + default: + return NULL; /* cannot find a reasonable name */ + } + *name = getstr(G(L)->tmname[tm]) + 2; + return "metamethod"; +} + + +/* +** Try to find a name for a function based on how it was called. +*/ +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, + const char **name) { + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } + else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */ + *name = "__gc"; + return "metamethod"; /* report it as such */ + } + else if (isLua(ci)) + return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name); + else + return NULL; +} + +/* }====================================================== */ + + + +/* +** Check whether pointer 'o' points to some value in the stack frame of +** the current function and, if so, returns its index. Because 'o' may +** not point to a value in this stack, we cannot compare it with the +** region boundaries (undefined behavior in ISO C). +*/ +static int instack (CallInfo *ci, const TValue *o) { + int pos; + StkId base = ci->func.p + 1; + for (pos = 0; base + pos < ci->top.p; pos++) { + if (o == s2v(base + pos)) + return pos; + } + return -1; /* not found */ +} + + +/* +** Checks whether value 'o' came from an upvalue. (That can only happen +** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on +** upvalues.) +*/ +static const char *getupvalname (CallInfo *ci, const TValue *o, + const char **name) { + LClosure *c = ci_func(ci); + int i; + for (i = 0; i < c->nupvalues; i++) { + if (c->upvals[i]->v.p == o) { + *name = upvalname(c->p, i); + return "upvalue"; + } + } + return NULL; +} + + +static const char *formatvarinfo (lua_State *L, const char *kind, + const char *name) { + if (kind == NULL) + return ""; /* no information */ + else + return luaO_pushfstring(L, " (%s '%s')", kind, name); +} + +/* +** Build a string with a "description" for the value 'o', such as +** "variable 'x'" or "upvalue 'y'". +*/ +static const char *varinfo (lua_State *L, const TValue *o) { + CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ + const char *kind = NULL; + if (isLua(ci)) { + kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ + if (!kind) { /* not an upvalue? */ + int reg = instack(ci, o); /* try a register */ + if (reg >= 0) /* is 'o' a register? */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name); + } + } + return formatvarinfo(L, kind, name); +} + + +/* +** Raise a type error +*/ +static l_noret typeerror (lua_State *L, const TValue *o, const char *op, + const char *extra) { + const char *t = luaT_objtypename(L, o); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra); +} + + +/* +** Raise a type error with "standard" information about the faulty +** object 'o' (using 'varinfo'). +*/ +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + typeerror(L, o, op, varinfo(L, o)); +} + + +/* +** Raise an error for calling a non-callable object. Try to find a name +** for the object based on how it was called ('funcnamefromcall'); if it +** cannot get a name there, try 'varinfo'. +*/ +l_noret luaG_callerror (lua_State *L, const TValue *o) { + CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ + const char *kind = funcnamefromcall(L, ci, &name); + const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o); + typeerror(L, o, "call", extra); +} + + +l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) { + luaG_runerror(L, "bad 'for' %s (number expected, got %s)", + what, luaT_objtypename(L, o)); +} + + +l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { + if (ttisstring(p1) || cvt2str(p1)) p1 = p2; + luaG_typeerror(L, p1, "concatenate"); +} + + +l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, const char *msg) { + if (!ttisnumber(p1)) /* first operand is wrong? */ + p2 = p1; /* now second is wrong */ + luaG_typeerror(L, p2, msg); +} + + +/* +** Error when both values are convertible to numbers, but not to integers +*/ +l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { + lua_Integer temp; + if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I)) + p2 = p1; + luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); +} + + +l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_objtypename(L, p1); + const char *t2 = luaT_objtypename(L, p2); + if (strcmp(t1, t2) == 0) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); +} + + +/* add src:line information to 'msg' */ +const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, + int line) { + char buff[LUA_IDSIZE]; + if (src) + luaO_chunkid(buff, getstr(src), tsslen(src)); + else { /* no source available; use "?" instead */ + buff[0] = '?'; buff[1] = '\0'; + } + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); +} + + +l_noret luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + lua_assert(ttisfunction(s2v(errfunc))); + setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */ + setobjs2s(L, L->top.p - 1, errfunc); /* push function */ + L->top.p++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; + va_list argp; + luaC_checkGC(L); /* error message uses memory */ + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); /* format message */ + va_end(argp); + if (isLua(ci)) { /* if Lua function, add source:line information */ + luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ + L->top.p--; + } + luaG_errormsg(L); +} + + +/* +** Check whether new instruction 'newpc' is in a different line from +** previous instruction 'oldpc'. More often than not, 'newpc' is only +** one or a few instructions after 'oldpc' (it must be after, see +** caller), so try to avoid calling 'luaG_getfuncline'. If they are +** too far apart, there is a good chance of a ABSLINEINFO in the way, +** so it goes directly to 'luaG_getfuncline'. +*/ +static int changedline (const Proto *p, int oldpc, int newpc) { + if (p->lineinfo == NULL) /* no debug information? */ + return 0; + if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ + int delta = 0; /* line difference */ + int pc = oldpc; + for (;;) { + int lineinfo = p->lineinfo[++pc]; + if (lineinfo == ABSLINEINFO) + break; /* cannot compute delta; fall through */ + delta += lineinfo; + if (pc == newpc) + return (delta != 0); /* delta computed successfully */ + } + } + /* either instructions are too far apart or there is an absolute line + info in the way; compute line difference explicitly */ + return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc)); +} + + +/* +** Traces the execution of a Lua function. Called before the execution +** of each opcode, when debug is on. 'L->oldpc' stores the last +** instruction traced, to detect line changes. When entering a new +** function, 'npci' will be zero and will test as a new line whatever +** the value of 'oldpc'. Some exceptional conditions may return to +** a function without setting 'oldpc'. In that case, 'oldpc' may be +** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' +** at most causes an extra call to a line hook.) +** This function is not "Protected" when called, so it should correct +** 'L->top.p' before calling anything that can run the GC. +*/ +int luaG_traceexec (lua_State *L, const Instruction *pc) { + CallInfo *ci = L->ci; + lu_byte mask = L->hookmask; + const Proto *p = ci_func(ci)->p; + int counthook; + if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ + ci->u.l.trap = 0; /* don't need to stop again */ + return 0; /* turn off 'trap' */ + } + pc++; /* reference is always next instruction */ + ci->u.l.savedpc = pc; /* save 'pc' */ + counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); + if (counthook) + resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return 1; /* no line hook and count != 0; nothing to be done now */ + if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return 1; /* do not call hook again (VM yielded, so it did not move) */ + } + if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ + L->top.p = ci->top.p; /* correct top */ + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ + if (mask & LUA_MASKLINE) { + /* 'L->oldpc' may be invalid; use zero in this case */ + int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0; + int npci = pcRel(pc, p); + if (npci <= oldpc || /* call hook when jump back (loop), */ + changedline(p, oldpc, npci)) { /* or when enter new line */ + int newline = luaG_getfuncline(p, npci); + luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ + } + L->oldpc = npci; /* 'pc' of last call to line hook */ + } + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + luaD_throw(L, LUA_YIELD); + } + return 1; /* keep 'trap' on */ +} + +/* +** $Id: lfunc.c $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#define lfunc_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ + + + +CClosure *luaF_newCclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); + CClosure *c = gco2ccl(o); + c->nupvalues = cast_byte(nupvals); + return c; +} + + +LClosure *luaF_newLclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals)); + LClosure *c = gco2lcl(o); + c->p = NULL; + c->nupvalues = cast_byte(nupvals); + while (nupvals--) c->upvals[nupvals] = NULL; + return c; +} + + +/* +** fill a closure with new closed upvalues +*/ +void luaF_initupvals (lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + uv->v.p = &uv->u.value; /* make it closed */ + setnilvalue(uv->v.p); + cl->upvals[i] = uv; + luaC_objbarrier(L, cl, uv); + } +} + + +/* +** Create a new upvalue at the given level, and link it to the list of +** open upvalues of 'L' after entry 'prev'. +**/ +static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + UpVal *next = *prev; + uv->v.p = s2v(level); /* current value lives in the stack */ + uv->u.open.next = next; /* link it to list of open upvalues */ + uv->u.open.previous = prev; + if (next) + next->u.open.previous = &uv->u.open.next; + *prev = uv; + if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ + L->twups = G(L)->twups; /* link it to the list */ + G(L)->twups = L; + } + return uv; +} + + +/* +** Find and reuse, or create if it does not exist, an upvalue +** at the given level. +*/ +UpVal *luaF_findupval (lua_State *L, StkId level) { + UpVal **pp = &L->openupval; + UpVal *p; + lua_assert(isintwups(L) || L->openupval == NULL); + while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ + lua_assert(!isdead(G(L), p)); + if (uplevel(p) == level) /* corresponding upvalue? */ + return p; /* return it */ + pp = &p->u.open.next; + } + /* not found: create a new upvalue after 'pp' */ + return newupval(L, level, pp); +} + + +/* +** Call closing method for object 'obj' with error message 'err'. The +** boolean 'yy' controls whether the call is yieldable. +** (This function assumes EXTRA_STACK.) +*/ +static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { + StkId top = L->top.p; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + setobj2s(L, top, tm); /* will call metamethod... */ + setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ + setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ + L->top.p = top + 3; /* add function and arguments */ + if (yy) + luaD_call(L, top, 0); + else + luaD_callnoyield(L, top, 0); +} + + +/* +** Check whether object at given level has a close metamethod and raise +** an error if not. +*/ +static void checkclosemth (lua_State *L, StkId level) { + const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); + if (ttisnil(tm)) { /* no metamethod? */ + int idx = cast_int(level - L->ci->func.p); /* variable index */ + const char *vname = luaG_findlocal(L, L->ci, idx, NULL); + if (vname == NULL) vname = "?"; + luaG_runerror(L, "variable '%s' got a non-closable value", vname); + } +} + + +/* +** Prepare and call a closing method. +** If status is CLOSEKTOP, the call to the closing method will be pushed +** at the top of the stack. Otherwise, values can be pushed right after +** the 'level' of the upvalue being closed, as everything after that +** won't be used again. +*/ +static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { + TValue *uv = s2v(level); /* value being closed */ + TValue *errobj; + if (status == CLOSEKTOP) + errobj = &G(L)->nilvalue; /* error object is nil */ + else { /* 'luaD_seterrorobj' will set top to level + 2 */ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + } + callclosemethod(L, uv, errobj, yy); +} + + +/* +** Maximum value for deltas in 'tbclist', dependent on the type +** of delta. (This macro assumes that an 'L' is in scope where it +** is used.) +*/ +#define MAXDELTA \ + ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) + + +/* +** Insert a variable in the list of to-be-closed variables. +*/ +void luaF_newtbcupval (lua_State *L, StkId level) { + lua_assert(level > L->tbclist.p); + if (l_isfalse(s2v(level))) + return; /* false doesn't need to be closed */ + checkclosemth(L, level); /* value must have a close method */ + while (cast_uint(level - L->tbclist.p) > MAXDELTA) { + L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist.p->tbclist.delta = 0; + } + level->tbclist.delta = cast(unsigned short, level - L->tbclist.p); + L->tbclist.p = level; +} + + +void luaF_unlinkupval (UpVal *uv) { + lua_assert(upisopen(uv)); + *uv->u.open.previous = uv->u.open.next; + if (uv->u.open.next) + uv->u.open.next->u.open.previous = uv->u.open.previous; +} + + +/* +** Close all upvalues up to the given stack level. +*/ +void luaF_closeupval (lua_State *L, StkId level) { + UpVal *uv; + StkId upl; /* stack index pointed by 'uv' */ + while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { + TValue *slot = &uv->u.value; /* new position for value */ + lua_assert(uplevel(uv) < L->top.p); + luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ + setobj(L, slot, uv->v.p); /* move value to upvalue slot */ + uv->v.p = slot; /* now current value lives here */ + if (!iswhite(uv)) { /* neither white nor dead? */ + nw2black(uv); /* closed upvalues cannot be gray */ + luaC_barrier(L, uv, slot); + } + } +} + + +/* +** Remove first element from the tbclist plus its dummy nodes. +*/ +static void poptbclist (lua_State *L) { + StkId tbc = L->tbclist.p; + lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ + tbc -= tbc->tbclist.delta; + while (tbc > L->stack.p && tbc->tbclist.delta == 0) + tbc -= MAXDELTA; /* remove dummy nodes */ + L->tbclist.p = tbc; +} + + +/* +** Close all upvalues and to-be-closed variables up to the given stack +** level. Return restored 'level'. +*/ +StkId luaF_close (lua_State *L, StkId level, int status, int yy) { + ptrdiff_t levelrel = savestack(L, level); + luaF_closeupval(L, level); /* first, close the upvalues */ + while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ + StkId tbc = L->tbclist.p; /* get variable index */ + poptbclist(L); /* remove it from list */ + prepcallclosemth(L, tbc, status, yy); /* close variable */ + level = restorestack(L, levelrel); + } + return level; +} + + +Proto *luaF_newproto (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto)); + Proto *f = gco2p(o); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->lineinfo = NULL; + f->sizelineinfo = 0; + f->abslineinfo = NULL; + f->sizeabslineinfo = 0; + f->upvalues = NULL; + f->sizeupvalues = 0; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->locvars = NULL; + f->sizelocvars = 0; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode); + luaM_freearray(L, f->p, f->sizep); + luaM_freearray(L, f->k, f->sizek); + luaM_freearray(L, f->lineinfo, f->sizelineinfo); + luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); + luaM_freearray(L, f->locvars, f->sizelocvars); + luaM_freearray(L, f->upvalues, f->sizeupvalues); + luaM_free(L, f); +} + + +/* +** Look for n-th local variable at line 'line' in function 'func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + +/* +** $Id: lobject.c $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#define lobject_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lctype.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "lvm.h"*/ + + +/* +** Computes ceil(log2(x)) +*/ +int luaO_ceillog2 (unsigned int x) { + static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = 0; + x--; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; +} + + +static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, + lua_Integer v2) { + switch (op) { + case LUA_OPADD: return intop(+, v1, v2); + case LUA_OPSUB:return intop(-, v1, v2); + case LUA_OPMUL:return intop(*, v1, v2); + case LUA_OPMOD: return luaV_mod(L, v1, v2); + case LUA_OPIDIV: return luaV_idiv(L, v1, v2); + case LUA_OPBAND: return intop(&, v1, v2); + case LUA_OPBOR: return intop(|, v1, v2); + case LUA_OPBXOR: return intop(^, v1, v2); + case LUA_OPSHL: return luaV_shiftl(v1, v2); + case LUA_OPSHR: return luaV_shiftr(v1, v2); + case LUA_OPUNM: return intop(-, 0, v1); + case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); + default: lua_assert(0); return 0; + } +} + + +static lua_Number numarith (lua_State *L, int op, lua_Number v1, + lua_Number v2) { + switch (op) { + case LUA_OPADD: return luai_numadd(L, v1, v2); + case LUA_OPSUB: return luai_numsub(L, v1, v2); + case LUA_OPMUL: return luai_nummul(L, v1, v2); + case LUA_OPDIV: return luai_numdiv(L, v1, v2); + case LUA_OPPOW: return luai_numpow(L, v1, v2); + case LUA_OPIDIV: return luai_numidiv(L, v1, v2); + case LUA_OPUNM: return luai_numunm(L, v1); + case LUA_OPMOD: return luaV_modf(L, v1, v2); + default: lua_assert(0); return 0; + } +} + + +int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, + TValue *res) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: + case LUA_OPBNOT: { /* operate only on integers */ + lua_Integer i1; lua_Integer i2; + if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { + setivalue(res, intarith(L, op, i1, i2)); + return 1; + } + else return 0; /* fail */ + } + case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ + lua_Number n1; lua_Number n2; + if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + default: { /* other operations */ + lua_Number n1; lua_Number n2; + if (ttisinteger(p1) && ttisinteger(p2)) { + setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); + return 1; + } + else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + } +} + + +void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, + StkId res) { + if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { + /* could not perform raw operation; try metamethod */ + luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); + } +} + + +int luaO_hexavalue (int c) { + if (lisdigit(c)) return c - '0'; + else return (ltolower(c) - 'a') + 10; +} + + +static int isneg (const char **s) { + if (**s == '-') { (*s)++; return 1; } + else if (**s == '+') (*s)++; + return 0; +} + + + +/* +** {================================================================== +** Lua's implementation for 'lua_strx2number' +** =================================================================== +*/ + +#if !defined(lua_strx2number) + +/* maximum number of significant digits to read (to avoid overflows + even with single floats) */ +#define MAXSIGDIG 30 + +/* +** convert a hexadecimal numeric string to a number, following +** C99 specification for 'strtod' +*/ +static lua_Number lua_strx2number (const char *s, char **endptr) { + int dot = lua_getlocaledecpoint(); + lua_Number r = l_mathop(0.0); /* result (accumulator) */ + int sigdig = 0; /* number of significant digits */ + int nosigdig = 0; /* number of non-significant digits */ + int e = 0; /* exponent correction */ + int neg; /* 1 if number is negative */ + int hasdot = 0; /* true after seen a dot */ + *endptr = cast_charp(s); /* nothing is valid yet */ + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); /* check sign */ + if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ + return l_mathop(0.0); /* invalid format (no '0x') */ + for (s += 2; ; s++) { /* skip '0x' and read numeral */ + if (*s == dot) { + if (hasdot) break; /* second dot? stop loop */ + else hasdot = 1; + } + else if (lisxdigit(cast_uchar(*s))) { + if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ + nosigdig++; + else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ + r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); + else e++; /* too many digits; ignore, but still count for exponent */ + if (hasdot) e--; /* decimal digit? correct exponent */ + } + else break; /* neither a dot nor a digit */ + } + if (nosigdig + sigdig == 0) /* no digits? */ + return l_mathop(0.0); /* invalid format */ + *endptr = cast_charp(s); /* valid up to here */ + e *= 4; /* each digit multiplies/divides value by 2^4 */ + if (*s == 'p' || *s == 'P') { /* exponent part? */ + int exp1 = 0; /* exponent value */ + int neg1; /* exponent sign */ + s++; /* skip 'p' */ + neg1 = isneg(&s); /* sign */ + if (!lisdigit(cast_uchar(*s))) + return l_mathop(0.0); /* invalid; must have at least one digit */ + while (lisdigit(cast_uchar(*s))) /* read exponent */ + exp1 = exp1 * 10 + *(s++) - '0'; + if (neg1) exp1 = -exp1; + e += exp1; + *endptr = cast_charp(s); /* valid up to here */ + } + if (neg) r = -r; + return l_mathop(ldexp)(r, e); +} + +#endif +/* }====================================================== */ + + +/* maximum length of a numeral to be converted to a number */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + +/* +** Convert string 's' to a Lua number (put in 'result'). Return NULL on +** fail or the address of the ending '\0' on success. ('mode' == 'x') +** means a hexadecimal numeral. +*/ +static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { + char *endptr; + *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ + : lua_str2number(s, &endptr); + if (endptr == s) return NULL; /* nothing recognized? */ + while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ + return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */ +} + + +/* +** Convert string 's' to a Lua number (put in 'result') handling the +** current locale. +** This function accepts both the current locale or a dot as the radix +** mark. If the conversion fails, it may mean number has a dot but +** locale accepts something else. In that case, the code copies 's' +** to a buffer (because 's' is read-only), changes the dot to the +** current locale radix mark, and tries to convert again. +** The variable 'mode' checks for special characters in the string: +** - 'n' means 'inf' or 'nan' (which should be rejected) +** - 'x' means a hexadecimal numeral +** - '.' just optimizes the search for the common case (no special chars) +*/ +static const char *l_str2d (const char *s, lua_Number *result) { + const char *endptr; + const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */ + int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; + if (mode == 'n') /* reject 'inf' and 'nan' */ + return NULL; + endptr = l_str2dloc(s, result, mode); /* try to convert */ + if (endptr == NULL) { /* failed? may be a different locale */ + char buff[L_MAXLENNUM + 1]; + const char *pdot = strchr(s, '.'); + if (pdot == NULL || strlen(s) > L_MAXLENNUM) + return NULL; /* string too long or no dot; fail */ + strcpy(buff, s); /* copy string to buffer */ + buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ + endptr = l_str2dloc(buff, result, mode); /* try again */ + if (endptr != NULL) + endptr = s + (endptr - buff); /* make relative to 's' */ + } + return endptr; +} + + +#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXLASTD cast_int(LUA_MAXINTEGER % 10) + +static const char *l_str2int (const char *s, lua_Integer *result) { + lua_Unsigned a = 0; + int empty = 1; + int neg; + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); + if (s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { /* hex? */ + s += 2; /* skip '0x' */ + for (; lisxdigit(cast_uchar(*s)); s++) { + a = a * 16 + luaO_hexavalue(*s); + empty = 0; + } + } + else { /* decimal */ + for (; lisdigit(cast_uchar(*s)); s++) { + int d = *s - '0'; + if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 10 + d; + empty = 0; + } + } + while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ + if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ + else { + *result = l_castU2S((neg) ? 0u - a : a); + return s; + } +} + + +size_t luaO_str2num (const char *s, TValue *o) { + lua_Integer i; lua_Number n; + const char *e; + if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ + setivalue(o, i); + } + else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ + setfltvalue(o, n); + } + else + return 0; /* conversion failed */ + return (e - s) + 1; /* success; return string size */ +} + + +int luaO_utf8esc (char *buff, unsigned long x) { + int n = 1; /* number of bytes put in buffer (backwards) */ + lua_assert(x <= 0x7FFFFFFFu); + if (x < 0x80) /* ascii? */ + buff[UTF8BUFFSZ - 1] = cast_char(x); + else { /* need continuation bytes */ + unsigned int mfb = 0x3f; /* maximum that fits in first byte */ + do { /* add continuation bytes */ + buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); + x >>= 6; /* remove added bits */ + mfb >>= 1; /* now there is one less bit available in first byte */ + } while (x > mfb); /* still needs continuation byte? */ + buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ + } + return n; +} + + +/* +** Maximum length of the conversion of a number to a string. Must be +** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. +** (For a long long int, this is 19 digits plus a sign and a final '\0', +** adding to 21. For a long double, it can go to a sign, 33 digits, +** the dot, an exponent letter, an exponent sign, 5 exponent digits, +** and a final '\0', adding to 43.) +*/ +#define MAXNUMBER2STR 44 + + +/* +** Convert a number object to a string, adding it to a buffer +*/ +static int tostringbuff (TValue *obj, char *buff) { + int len; + lua_assert(ttisnumber(obj)); + if (ttisinteger(obj)) + len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); + else { + len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); + if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } + } + return len; +} + + +/* +** Convert a number object to a Lua string, replacing the value at 'obj' +*/ +void luaO_tostring (lua_State *L, TValue *obj) { + char buff[MAXNUMBER2STR]; + int len = tostringbuff(obj, buff); + setsvalue(L, obj, luaS_newlstr(L, buff, len)); +} + + + + +/* +** {================================================================== +** 'luaO_pushvfstring' +** =================================================================== +*/ + +/* +** Size for buffer space used by 'luaO_pushvfstring'. It should be +** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the buffer. +*/ +#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95) + +/* buffer used by 'luaO_pushvfstring' */ +typedef struct BuffFS { + lua_State *L; + int pushed; /* true if there is a part of the result on the stack */ + int blen; /* length of partial string in 'space' */ + char space[BUFVFS]; /* holds last part of the result */ +} BuffFS; + + +/* +** Push given string to the stack, as part of the result, and +** join it to previous partial result if there is one. +** It may call 'luaV_concat' while using one slot from EXTRA_STACK. +** This call cannot invoke metamethods, as both operands must be +** strings. It can, however, raise an error if the result is too +** long. In that case, 'luaV_concat' frees the extra slot before +** raising the error. +*/ +static void pushstr (BuffFS *buff, const char *str, size_t lstr) { + lua_State *L = buff->L; + setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr)); + L->top.p++; /* may use one slot from EXTRA_STACK */ + if (!buff->pushed) /* no previous string on the stack? */ + buff->pushed = 1; /* now there is one */ + else /* join previous string with new one */ + luaV_concat(L, 2); +} + + +/* +** empty the buffer space into the stack +*/ +static void clearbuff (BuffFS *buff) { + pushstr(buff, buff->space, buff->blen); /* push buffer contents */ + buff->blen = 0; /* space now is empty */ +} + + +/* +** Get a space of size 'sz' in the buffer. If buffer has not enough +** space, empty it. 'sz' must fit in an empty buffer. +*/ +static char *getbuff (BuffFS *buff, int sz) { + lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); + if (sz > BUFVFS - buff->blen) /* not enough space? */ + clearbuff(buff); + return buff->space + buff->blen; +} + + +#define addsize(b,sz) ((b)->blen += (sz)) + + +/* +** Add 'str' to the buffer. If string is larger than the buffer space, +** push the string directly to the stack. +*/ +static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { + if (slen <= BUFVFS) { /* does string fit into buffer? */ + char *bf = getbuff(buff, cast_int(slen)); + memcpy(bf, str, slen); /* add string to buffer */ + addsize(buff, cast_int(slen)); + } + else { /* string larger than buffer */ + clearbuff(buff); /* string comes after buffer's content */ + pushstr(buff, str, slen); /* push string */ + } +} + + +/* +** Add a numeral to the buffer. +*/ +static void addnum2buff (BuffFS *buff, TValue *num) { + char *numbuff = getbuff(buff, MAXNUMBER2STR); + int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ + addsize(buff, len); +} + + +/* +** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' + conventional formats, plus Lua-specific '%I' and '%U' +*/ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + BuffFS buff; /* holds last part of the result */ + const char *e; /* points to next '%' */ + buff.pushed = buff.blen = 0; + buff.L = L; + while ((e = strchr(fmt, '%')) != NULL) { + addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ + switch (*(e + 1)) { /* conversion specifier */ + case 's': { /* zero-terminated string */ + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + addstr2buff(&buff, s, strlen(s)); + break; + } + case 'c': { /* an 'int' as a character */ + char c = cast_uchar(va_arg(argp, int)); + addstr2buff(&buff, &c, sizeof(char)); + break; + } + case 'd': { /* an 'int' */ + TValue num; + setivalue(&num, va_arg(argp, int)); + addnum2buff(&buff, &num); + break; + } + case 'I': { /* a 'lua_Integer' */ + TValue num; + setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); + addnum2buff(&buff, &num); + break; + } + case 'f': { /* a 'lua_Number' */ + TValue num; + setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); + addnum2buff(&buff, &num); + break; + } + case 'p': { /* a pointer */ + const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ + char *bf = getbuff(&buff, sz); + void *p = va_arg(argp, void *); + int len = lua_pointer2str(bf, sz, p); + addsize(&buff, len); + break; + } + case 'U': { /* a 'long' as a UTF-8 sequence */ + char bf[UTF8BUFFSZ]; + int len = luaO_utf8esc(bf, va_arg(argp, long)); + addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); + break; + } + case '%': { + addstr2buff(&buff, "%", 1); + break; + } + default: { + luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", + *(e + 1)); + } + } + fmt = e + 2; /* skip '%' and the specifier */ + } + addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ + clearbuff(&buff); /* empty buffer into the stack */ + lua_assert(buff.pushed == 1); + return svalue(s2v(L->top.p - 1)); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + +/* }================================================================== */ + + +#define RETS "..." +#define PRE "[string \"" +#define POS "\"]" + +#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) + +void luaO_chunkid (char *out, const char *source, size_t srclen) { + size_t bufflen = LUA_IDSIZE; /* free space in buffer */ + if (*source == '=') { /* 'literal' source */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* truncate it */ + addstr(out, source + 1, bufflen - 1); + *out = '\0'; + } + } + else if (*source == '@') { /* file name */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* add '...' before rest of name */ + addstr(out, RETS, LL(RETS)); + bufflen -= LL(RETS); + memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); + } + } + else { /* string; format as [string "source"] */ + const char *nl = strchr(source, '\n'); /* find first new line (if any) */ + addstr(out, PRE, LL(PRE)); /* add prefix */ + bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ + if (srclen < bufflen && nl == NULL) { /* small one-line source? */ + addstr(out, source, srclen); /* keep it */ + } + else { + if (nl != NULL) srclen = nl - source; /* stop at first newline */ + if (srclen > bufflen) srclen = bufflen; + addstr(out, source, srclen); + addstr(out, RETS, LL(RETS)); + } + memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); + } +} + +/* +** $Id: ltm.c $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#define ltm_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lgc.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ +/*#include "lvm.h"*/ + + +static const char udatatypename[] = "userdata"; + +LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { + "no value", + "nil", "boolean", udatatypename, "number", + "string", "table", "function", udatatypename, "thread", + "upvalue", "proto" /* these last cases are used for tests only */ +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__len", "__eq", + "__add", "__sub", "__mul", "__mod", "__pow", + "__div", "__idiv", + "__band", "__bor", "__bxor", "__shl", "__shr", + "__unm", "__bnot", "__lt", "__le", + "__concat", "__call", "__close" + }; + int i; + for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); + luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getshortstr(events, ename); + lua_assert(event <= TM_EQ); + if (notm(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); +} + + +/* +** Return the name of the type of an object. For tables and userdata +** with metatable, use their '__name' metafield, if present. +*/ +const char *luaT_objtypename (lua_State *L, const TValue *o) { + Table *mt; + if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || + (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { + const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); + if (ttisstring(name)) /* is '__name' a string? */ + return getstr(tsvalue(name)); /* use it as type name */ + } + return ttypename(ttype(o)); /* else use standard type name */ +} + + +void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + setobj2s(L, func + 3, p3); /* 3rd argument */ + L->top.p = func + 4; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 0); + else + luaD_callnoyield(L, func, 0); +} + + +void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, StkId res) { + ptrdiff_t result = savestack(L, res); + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top.p += 3; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 1); + else + luaD_callnoyield(L, func, 1); + res = restorestack(L, result); + setobjs2s(L, res, --L->top.p); /* move result to its place */ +} + + +static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (notm(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (notm(tm)) return 0; + luaT_callTMres(L, tm, p1, p2, res); + return 1; +} + + +void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + if (l_unlikely(!callbinTM(L, p1, p2, res, event))) { + switch (event) { + case TM_BAND: case TM_BOR: case TM_BXOR: + case TM_SHL: case TM_SHR: case TM_BNOT: { + if (ttisnumber(p1) && ttisnumber(p2)) + luaG_tointerror(L, p1, p2); + else + luaG_opinterror(L, p1, p2, "perform bitwise operation on"); + } + /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ + default: + luaG_opinterror(L, p1, p2, "perform arithmetic on"); + } + } +} + + +void luaT_tryconcatTM (lua_State *L) { + StkId top = L->top.p; + if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, + TM_CONCAT))) + luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); +} + + +void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2, + int flip, StkId res, TMS event) { + if (flip) + luaT_trybinTM(L, p2, p1, res, event); + else + luaT_trybinTM(L, p1, p2, res, event); +} + + +void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int flip, StkId res, TMS event) { + TValue aux; + setivalue(&aux, i2); + luaT_trybinassocTM(L, p1, &aux, flip, res, event); +} + + +/* +** Calls an order tag method. +** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old +** behavior: if there is no '__le', try '__lt', based on l <= r iff +** !(r < l) (assuming a total order). If the metamethod yields during +** this substitution, the continuation has to know about it (to negate +** the result of rtop.p, event)) /* try original event */ + return !l_isfalse(s2v(L->top.p)); +#if defined(LUA_COMPAT_LT_LE) + else if (event == TM_LE) { + /* try '!(p2 < p1)' for '(p1 <= p2)' */ + L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ + if (callbinTM(L, p2, p1, L->top.p, TM_LT)) { + L->ci->callstatus ^= CIST_LEQ; /* clear mark */ + return l_isfalse(s2v(L->top.p)); + } + /* else error will remove this 'ci'; no need to clear mark */ + } +#endif + luaG_ordererror(L, p1, p2); /* no metamethod found */ + return 0; /* to avoid warnings */ +} + + +int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int flip, int isfloat, TMS event) { + TValue aux; const TValue *p2; + if (isfloat) { + setfltvalue(&aux, cast_num(v2)); + } + else + setivalue(&aux, v2); + if (flip) { /* arguments were exchanged? */ + p2 = p1; p1 = &aux; /* correct them */ + } + else + p2 = &aux; + return luaT_callorderTM(L, p1, p2, event); +} + + +void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, + const Proto *p) { + int i; + int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ + int nextra = actual - nfixparams; /* number of extra arguments */ + ci->u.l.nextraargs = nextra; + luaD_checkstack(L, p->maxstacksize + 1); + /* copy function to the top of the stack */ + setobjs2s(L, L->top.p++, ci->func.p); + /* move fixed parameters to the top of the stack */ + for (i = 1; i <= nfixparams; i++) { + setobjs2s(L, L->top.p++, ci->func.p + i); + setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ + } + ci->func.p += actual + 1; + ci->top.p += actual + 1; + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); +} + + +void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { + int i; + int nextra = ci->u.l.nextraargs; + if (wanted < 0) { + wanted = nextra; /* get all extra arguments available */ + checkstackGCp(L, nextra, where); /* ensure stack space */ + L->top.p = where + nextra; /* next instruction will need top */ + } + for (i = 0; i < wanted && i < nextra; i++) + setobjs2s(L, where + i, ci->func.p - nextra + i); + for (; i < wanted; i++) /* complete required results with nil */ + setnilvalue(s2v(where + i)); +} + +/* +** $Id: lstring.c $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#define lstring_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ + + +/* +** Maximum size for string table. +*/ +#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*)) + + +/* +** equality for long strings +*/ +int luaS_eqlngstr (TString *a, TString *b) { + size_t len = a->u.lnglen; + lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); + return (a == b) || /* same instance or... */ + ((len == b->u.lnglen) && /* equal length and ... */ + (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ +} + + +unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { + unsigned int h = seed ^ cast_uint(l); + for (; l > 0; l--) + h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); + return h; +} + + +unsigned int luaS_hashlongstr (TString *ts) { + lua_assert(ts->tt == LUA_VLNGSTR); + if (ts->extra == 0) { /* no hash? */ + size_t len = ts->u.lnglen; + ts->hash = luaS_hash(getstr(ts), len, ts->hash); + ts->extra = 1; /* now it has its hash */ + } + return ts->hash; +} + + +static void tablerehash (TString **vect, int osize, int nsize) { + int i; + for (i = osize; i < nsize; i++) /* clear new elements */ + vect[i] = NULL; + for (i = 0; i < osize; i++) { /* rehash old part of the array */ + TString *p = vect[i]; + vect[i] = NULL; + while (p) { /* for each string in the list */ + TString *hnext = p->u.hnext; /* save next */ + unsigned int h = lmod(p->hash, nsize); /* new position */ + p->u.hnext = vect[h]; /* chain it into array */ + vect[h] = p; + p = hnext; + } + } +} + + +/* +** Resize the string table. If allocation fails, keep the current size. +** (This can degrade performance, but any non-zero size should work +** correctly.) +*/ +void luaS_resize (lua_State *L, int nsize) { + stringtable *tb = &G(L)->strt; + int osize = tb->size; + TString **newvect; + if (nsize < osize) /* shrinking table? */ + tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ + newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*); + if (l_unlikely(newvect == NULL)) { /* reallocation failed? */ + if (nsize < osize) /* was it shrinking table? */ + tablerehash(tb->hash, nsize, osize); /* restore to original size */ + /* leave table as it was */ + } + else { /* allocation succeeded */ + tb->hash = newvect; + tb->size = nsize; + if (nsize > osize) + tablerehash(newvect, osize, nsize); /* rehash for new size */ + } +} + + +/* +** Clear API string cache. (Entries cannot be empty, so fill them with +** a non-collectable string.) +*/ +void luaS_clearcache (global_State *g) { + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } +} + + +/* +** Initialize the string table and the string cache +*/ +void luaS_init (lua_State *L) { + global_State *g = G(L); + int i, j; + stringtable *tb = &G(L)->strt; + tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*); + tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */ + tb->size = MINSTRTABSIZE; + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; +} + + + +/* +** creates a new string object +*/ +static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { + TString *ts; + GCObject *o; + size_t totalsize; /* total size of TString object */ + totalsize = sizelstring(l); + o = luaC_newobj(L, tag, totalsize); + ts = gco2ts(o); + ts->hash = h; + ts->extra = 0; + getstr(ts)[l] = '\0'; /* ending 0 */ + return ts; +} + + +TString *luaS_createlngstrobj (lua_State *L, size_t l) { + TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); + ts->u.lnglen = l; + return ts; +} + + +void luaS_remove (lua_State *L, TString *ts) { + stringtable *tb = &G(L)->strt; + TString **p = &tb->hash[lmod(ts->hash, tb->size)]; + while (*p != ts) /* find previous element */ + p = &(*p)->u.hnext; + *p = (*p)->u.hnext; /* remove element from its list */ + tb->nuse--; +} + + +static void growstrtab (lua_State *L, stringtable *tb) { + if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ + luaC_fullgc(L, 1); /* try to free some... */ + if (tb->nuse == MAX_INT) /* still too many? */ + luaM_error(L); /* cannot even create a message... */ + } + if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ + luaS_resize(L, tb->size * 2); +} + + +/* +** Checks whether short string exists and reuses it or creates a new one. +*/ +static TString *internshrstr (lua_State *L, const char *str, size_t l) { + TString *ts; + global_State *g = G(L); + stringtable *tb = &g->strt; + unsigned int h = luaS_hash(str, l, g->seed); + TString **list = &tb->hash[lmod(h, tb->size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ + for (ts = *list; ts != NULL; ts = ts->u.hnext) { + if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { + /* found! */ + if (isdead(g, ts)) /* dead (but not collected yet)? */ + changewhite(ts); /* resurrect it */ + return ts; + } + } + /* else must create a new string */ + if (tb->nuse >= tb->size) { /* need to grow string table? */ + growstrtab(L, tb); + list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ + } + ts = createstrobj(L, l, LUA_VSHRSTR, h); + memcpy(getstr(ts), str, l * sizeof(char)); + ts->shrlen = cast_byte(l); + ts->u.hnext = *list; + *list = ts; + tb->nuse++; + return ts; +} + + +/* +** new string (with explicit length) +*/ +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + if (l <= LUAI_MAXSHORTLEN) /* short string? */ + return internshrstr(L, str, l); + else { + TString *ts; + if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) + luaM_toobig(L); + ts = luaS_createlngstrobj(L, l); + memcpy(getstr(ts), str, l * sizeof(char)); + return ts; + } +} + + +/* +** Create or reuse a zero-terminated string, first checking in the +** cache (using the string address as a key). The cache can contain +** only zero-terminated strings, so it is safe to use 'strcmp' to +** check hits. +*/ +TString *luaS_new (lua_State *L, const char *str) { + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; + TString **p = G(L)->strcache[i]; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ + } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; +} + + +Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { + Udata *u; + int i; + GCObject *o; + if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) + luaM_toobig(L); + o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); + u = gco2u(o); + u->len = s; + u->nuvalue = nuvalue; + u->metatable = NULL; + for (i = 0; i < nuvalue; i++) + setnilvalue(&u->uv[i].uv); + return u; +} + +/* +** $Id: ltable.c $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#define ltable_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest 'n' such that +** more than half the slots between 1 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the 'original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "lvm.h"*/ + + +/* +** MAXABITS is the largest integer such that MAXASIZE fits in an +** unsigned int. +*/ +#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) + + +/* +** MAXASIZE is the maximum size of the array part. It is the minimum +** between 2^MAXABITS and the maximum size that, measured in bytes, +** fits in a 'size_t'. +*/ +#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) + +/* +** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a +** signed int. +*/ +#define MAXHBITS (MAXABITS - 1) + + +/* +** MAXHSIZE is the maximum size of the hash part. It is the minimum +** between 2^MAXHBITS and the maximum size such that, measured in bytes, +** it fits in a 'size_t'. +*/ +#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) + + +/* +** When the original hash value is good, hashing by a power of 2 +** avoids the cost of '%'. +*/ +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +/* +** for other types, it is better to avoid modulo by power of 2, as +** they can have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashboolean(t,p) hashpow2(t, p) + + +#define hashpointer(t,p) hashmod(t, point2uint(p)) + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {{NULL}, LUA_VEMPTY, /* value's value and type */ + LUA_VNIL, 0, {NULL}} /* key type, next, and key value */ +}; + + +static const TValue absentkey = {ABSTKEYCONSTANT}; + + +/* +** Hash for integers. To allow a good hash, use the remainder operator +** ('%'). If integer fits as a non-negative int, compute an int +** remainder, which is faster. Otherwise, use an unsigned-integer +** remainder, which uses all bits and ensures a non-negative result. +*/ +static Node *hashint (const Table *t, lua_Integer i) { + lua_Unsigned ui = l_castS2U(i); + if (ui <= cast_uint(INT_MAX)) + return hashmod(t, cast_int(ui)); + else + return hashmod(t, ui); +} + + +/* +** Hash for floating-point numbers. +** The main computation should be just +** n = frexp(n, &i); return (n * INT_MAX) + i +** but there are some numerical subtleties. +** In a two-complement representation, INT_MAX does not has an exact +** representation as a float, but INT_MIN does; because the absolute +** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the +** absolute value of the product 'frexp * -INT_MIN' is smaller or equal +** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when +** adding 'i'; the use of '~u' (instead of '-u') avoids problems with +** INT_MIN. +*/ +#if !defined(l_hashfloat) +static int l_hashfloat (lua_Number n) { + int i; + lua_Integer ni; + n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); + if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); + return 0; + } + else { /* normal case */ + unsigned int u = cast_uint(i) + cast_uint(ni); + return cast_int(u <= cast_uint(INT_MAX) ? u : ~u); + } +} +#endif + + +/* +** returns the 'main' position of an element in a table (that is, +** the index of its hash value). +*/ +static Node *mainpositionTV (const Table *t, const TValue *key) { + switch (ttypetag(key)) { + case LUA_VNUMINT: { + lua_Integer i = ivalue(key); + return hashint(t, i); + } + case LUA_VNUMFLT: { + lua_Number n = fltvalue(key); + return hashmod(t, l_hashfloat(n)); + } + case LUA_VSHRSTR: { + TString *ts = tsvalue(key); + return hashstr(t, ts); + } + case LUA_VLNGSTR: { + TString *ts = tsvalue(key); + return hashpow2(t, luaS_hashlongstr(ts)); + } + case LUA_VFALSE: + return hashboolean(t, 0); + case LUA_VTRUE: + return hashboolean(t, 1); + case LUA_VLIGHTUSERDATA: { + void *p = pvalue(key); + return hashpointer(t, p); + } + case LUA_VLCF: { + lua_CFunction f = fvalue(key); + return hashpointer(t, f); + } + default: { + GCObject *o = gcvalue(key); + return hashpointer(t, o); + } + } +} + + +l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { + TValue key; + getnodekey(cast(lua_State *, NULL), &key, nd); + return mainpositionTV(t, &key); +} + + +/* +** Check whether key 'k1' is equal to the key in node 'n2'. This +** equality is raw, so there are no metamethods. Floats with integer +** values have been normalized, so integers cannot be equal to +** floats. It is assumed that 'eqshrstr' is simply pointer equality, so +** that short strings are handled in the default case. +** A true 'deadok' means to accept dead keys as equal to their original +** values. All dead keys are compared in the default case, by pointer +** identity. (Only collectable objects can produce dead keys.) Note that +** dead long strings are also compared by identity. +** Once a key is dead, its corresponding value may be collected, and +** then another value can be created with the same address. If this +** other value is given to 'next', 'equalkey' will signal a false +** positive. In a regular traversal, this situation should never happen, +** as all keys given to 'next' came from the table itself, and therefore +** could not have been collected. Outside a regular traversal, we +** have garbage in, garbage out. What is relevant is that this false +** positive does not break anything. (In particular, 'next' will return +** some other valid item on the table or nil.) +*/ +static int equalkey (const TValue *k1, const Node *n2, int deadok) { + if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ + !(deadok && keyisdead(n2) && iscollectable(k1))) + return 0; /* cannot be same key */ + switch (keytt(n2)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case ctb(LUA_VLNGSTR): + return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); + default: + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } +} + + +/* +** True if value of 'alimit' is equal to the real size of the array +** part of table 't'. (Otherwise, the array part must be larger than +** 'alimit'.) +*/ +#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) + + +/* +** Returns the real size of the 'array' array +*/ +LUAI_FUNC unsigned int luaH_realasize (const Table *t) { + if (limitequalsasize(t)) + return t->alimit; /* this is the size */ + else { + unsigned int size = t->alimit; + /* compute the smallest power of 2 not smaller than 'n' */ + size |= (size >> 1); + size |= (size >> 2); + size |= (size >> 4); + size |= (size >> 8); +#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */ + size |= (size >> 16); +#if (UINT_MAX >> 30) > 3 + size |= (size >> 32); /* unsigned int has more than 32 bits */ +#endif +#endif + size++; + lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); + return size; + } +} + + +/* +** Check whether real size of the array is a power of 2. +** (If it is not, 'alimit' cannot be changed to any other value +** without changing the real size.) +*/ +static int ispow2realasize (const Table *t) { + return (!isrealasize(t) || ispow2(t->alimit)); +} + + +static unsigned int setlimittosize (Table *t) { + t->alimit = luaH_realasize(t); + setrealasize(t); + return t->alimit; +} + + +#define limitasasize(t) check_exp(isrealasize(t), t->alimit) + + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +** See explanation about 'deadok' in function 'equalkey'. +*/ +static const TValue *getgeneric (Table *t, const TValue *key, int deadok) { + Node *n = mainpositionTV(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (equalkey(key, n, deadok)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +/* +** returns the index for 'k' if 'k' is an appropriate key to live in +** the array part of a table, 0 otherwise. +*/ +static unsigned int arrayindex (lua_Integer k) { + if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */ + return cast_uint(k); /* 'key' is an appropriate array index */ + else + return 0; +} + + +/* +** returns the index of a 'key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signaled by 0. +*/ +static unsigned int findindex (lua_State *L, Table *t, TValue *key, + unsigned int asize) { + unsigned int i; + if (ttisnil(key)) return 0; /* first iteration */ + i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0; + if (i - 1u < asize) /* is 'key' inside array part? */ + return i; /* yes; that's the index */ + else { + const TValue *n = getgeneric(t, key, 1); + if (l_unlikely(isabstkey(n))) + luaG_runerror(L, "invalid key to 'next'"); /* key not found */ + i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return (i + 1) + asize; + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + unsigned int asize = luaH_realasize(t); + unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ + for (; i < asize; i++) { /* try first array part */ + if (!isempty(&t->array[i])) { /* a non-empty entry? */ + setivalue(s2v(key), i + 1); + setobj2s(L, key + 1, &t->array[i]); + return 1; + } + } + for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */ + if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ + Node *n = gnode(t, i); + getnodekey(L, s2v(key), n); + setobj2s(L, key + 1, gval(n)); + return 1; + } + } + return 0; /* no more elements */ +} + + +static void freehash (lua_State *L, Table *t) { + if (!isdummy(t)) + luaM_freearray(L, t->node, cast_sizet(sizenode(t))); +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + +/* +** Compute the optimal size for the array part of table 't'. 'nums' is a +** "count array" where 'nums[i]' is the number of integers in the table +** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of +** integer keys in the table and leaves with the number of keys that +** will go to the array part; return the optimal size. (The condition +** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) +*/ +static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { + int i; + unsigned int twotoi; /* 2^i (candidate for optimal size) */ + unsigned int a = 0; /* number of elements smaller than 2^i */ + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* loop while keys can fill more than half of total size */ + for (i = 0, twotoi = 1; + twotoi > 0 && *pna > twotoi / 2; + i++, twotoi *= 2) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + optimal = twotoi; /* optimal size (till now) */ + na = a; /* all elements up to 'optimal' will go to array part */ + } + } + lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); + *pna = na; + return optimal; +} + + +static int countint (lua_Integer key, unsigned int *nums) { + unsigned int k = arrayindex(key); + if (k != 0) { /* is 'key' an appropriate array index? */ + nums[luaO_ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +/* +** Count keys in array part of table 't': Fill 'nums[i]' with +** number of keys that will go into corresponding slice and return +** total number of non-nil keys. +*/ +static unsigned int numusearray (const Table *t, unsigned int *nums) { + int lg; + unsigned int ttlg; /* 2^lg */ + unsigned int ause = 0; /* summation of 'nums' */ + unsigned int i = 1; /* count to traverse all array keys */ + unsigned int asize = limitasasize(t); /* real array size */ + /* traverse each slice */ + for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { + unsigned int lc = 0; /* counter */ + unsigned int lim = ttlg; + if (lim > asize) { + lim = asize; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg - 1), 2^lg] */ + for (; i <= lim; i++) { + if (!isempty(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* elements added to 'nums' (can go to array part) */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!isempty(gval(n))) { + if (keyisinteger(n)) + ause += countint(keyival(n), nums); + totaluse++; + } + } + *pna += ause; + return totaluse; +} + + +/* +** Creates an array for the hash part of a table with the given +** size, or reuses the dummy node if size is zero. +** The computation for size overflow is in two steps: the first +** comparison ensures that the shift in the second one does not +** overflow. +*/ +static void setnodevector (lua_State *L, Table *t, unsigned int size) { + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common 'dummynode' */ + t->lsizenode = 0; + t->lastfree = NULL; /* signal that it is using dummy node */ + } + else { + int i; + int lsize = luaO_ceillog2(size); + if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i = 0; i < cast_int(size); i++) { + Node *n = gnode(t, i); + gnext(n) = 0; + setnilkey(n); + setempty(gval(n)); + } + t->lsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ + } +} + + +/* +** (Re)insert all elements from the hash part of 'ot' into table 't'. +*/ +static void reinsert (lua_State *L, Table *ot, Table *t) { + int j; + int size = sizenode(ot); + for (j = 0; j < size; j++) { + Node *old = gnode(ot, j); + if (!isempty(gval(old))) { + /* doesn't need barrier/invalidate cache, as entry was + already present in the table */ + TValue k; + getnodekey(L, &k, old); + luaH_set(L, t, &k, gval(old)); + } + } +} + + +/* +** Exchange the hash part of 't1' and 't2'. +*/ +static void exchangehashpart (Table *t1, Table *t2) { + lu_byte lsizenode = t1->lsizenode; + Node *node = t1->node; + Node *lastfree = t1->lastfree; + t1->lsizenode = t2->lsizenode; + t1->node = t2->node; + t1->lastfree = t2->lastfree; + t2->lsizenode = lsizenode; + t2->node = node; + t2->lastfree = lastfree; +} + + +/* +** Resize table 't' for the new given sizes. Both allocations (for +** the hash part and for the array part) can fail, which creates some +** subtleties. If the first allocation, for the hash part, fails, an +** error is raised and that is it. Otherwise, it copies the elements from +** the shrinking part of the array (if it is shrinking) into the new +** hash. Then it reallocates the array part. If that fails, the table +** is in its original state; the function frees the new hash part and then +** raises the allocation error. Otherwise, it sets the new hash part +** into the table, initializes the new part of the array (if any) with +** nils and reinserts the elements of the old hash back into the new +** parts of the table. +*/ +void luaH_resize (lua_State *L, Table *t, unsigned int newasize, + unsigned int nhsize) { + unsigned int i; + Table newt; /* to keep the new hash part */ + unsigned int oldasize = setlimittosize(t); + TValue *newarray; + /* create new hash part with appropriate size into 'newt' */ + setnodevector(L, &newt, nhsize); + if (newasize < oldasize) { /* will array shrink? */ + t->alimit = newasize; /* pretend array has new size... */ + exchangehashpart(t, &newt); /* and new hash */ + /* re-insert into the new hash the elements from vanishing slice */ + for (i = newasize; i < oldasize; i++) { + if (!isempty(&t->array[i])) + luaH_setint(L, t, i + 1, &t->array[i]); + } + t->alimit = oldasize; /* restore current size... */ + exchangehashpart(t, &newt); /* and hash (in case of errors) */ + } + /* allocate new array */ + newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ + freehash(L, &newt); /* release new hash part */ + luaM_error(L); /* raise error (with array unchanged) */ + } + /* allocation ok; initialize new part of the array */ + exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ + t->array = newarray; /* set new array part */ + t->alimit = newasize; + for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ + setempty(&t->array[i]); + /* re-insert elements from old hash part into new parts */ + reinsert(L, &newt, t); /* 'newt' now has the old hash */ + freehash(L, &newt); /* free old hash part */ +} + + +void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { + int nsize = allocsizenode(t); + luaH_resize(L, t, nasize, nsize); +} + +/* +** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i +*/ +static void rehash (lua_State *L, Table *t, const TValue *ek) { + unsigned int asize; /* optimal size for array part */ + unsigned int na; /* number of keys in the array part */ + unsigned int nums[MAXABITS + 1]; + int i; + int totaluse; + for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ + setlimittosize(t); + na = numusearray(t, nums); /* count keys in array part */ + totaluse = na; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &na); /* count keys in hash part */ + /* count extra key */ + if (ttisinteger(ek)) + na += countint(ivalue(ek), nums); + totaluse++; + /* compute new size for array part */ + asize = computesizes(nums, &na); + /* resize the table to new computed sizes */ + luaH_resize(L, t, asize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); + Table *t = gco2t(o); + t->metatable = NULL; + t->flags = cast_byte(maskflags); /* table has no metamethod fields */ + t->array = NULL; + t->alimit = 0; + setnodevector(L, t, 0); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + freehash(L, t); + luaM_freearray(L, t->array, luaH_realasize(t)); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + if (!isdummy(t)) { + while (t->lastfree > t->node) { + t->lastfree--; + if (keyisnil(t->lastfree)) + return t->lastfree; + } + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { + Node *mp; + TValue aux; + if (l_unlikely(ttisnil(key))) + luaG_runerror(L, "table index is nil"); + else if (ttisfloat(key)) { + lua_Number f = fltvalue(key); + lua_Integer k; + if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ + setivalue(&aux, k); + key = &aux; /* insert it as an integer */ + } + else if (l_unlikely(luai_numisnan(f))) + luaG_runerror(L, "table index is NaN"); + } + if (ttisnil(value)) + return; /* do not insert nil values */ + mp = mainpositionTV(t, key); + if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ + Node *othern; + Node *f = getfreepos(t); /* get a free place */ + if (f == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + /* whatever called 'newkey' takes care of TM cache */ + luaH_set(L, t, key, value); /* insert key into grown table */ + return; + } + lua_assert(!isdummy(t)); + othern = mainpositionfromnode(t, mp); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (othern + gnext(othern) != mp) /* find previous */ + othern += gnext(othern); + gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ + *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + if (gnext(mp) != 0) { + gnext(f) += cast_int(mp - f); /* correct 'next' */ + gnext(mp) = 0; /* now 'mp' is free */ + } + setempty(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + if (gnext(mp) != 0) + gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ + else lua_assert(gnext(f) == 0); + gnext(mp) = cast_int(f - mp); + mp = f; + } + } + setnodekey(L, mp, key); + luaC_barrierback(L, obj2gco(t), key); + lua_assert(isempty(gval(mp))); + setobj2t(L, gval(mp), value); +} + + +/* +** Search function for integers. If integer is inside 'alimit', get it +** directly from the array part. Otherwise, if 'alimit' is not equal to +** the real size of the array, key still can be in the array part. In +** this case, try to avoid a call to 'luaH_realasize' when key is just +** one more than the limit (so that it can be incremented without +** changing the real size of the array). +*/ +const TValue *luaH_getint (Table *t, lua_Integer key) { + if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */ + return &t->array[key - 1]; + else if (!limitequalsasize(t) && /* key still may be in the array part? */ + (l_castS2U(key) == t->alimit + 1 || + l_castS2U(key) - 1u < luaH_realasize(t))) { + t->alimit = cast_uint(key); /* probably '#t' is here now */ + return &t->array[key - 1]; + } + else { + Node *n = hashint(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisinteger(n) && keyival(n) == key) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; + } + } + return &absentkey; + } +} + + +/* +** search function for short strings +*/ +const TValue *luaH_getshortstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + lua_assert(key->tt == LUA_VSHRSTR); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +const TValue *luaH_getstr (Table *t, TString *key) { + if (key->tt == LUA_VSHRSTR) + return luaH_getshortstr(t, key); + else { /* for long strings, use generic case */ + TValue ko; + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko, 0); + } +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttypetag(key)) { + case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key)); + case LUA_VNUMINT: return luaH_getint(t, ivalue(key)); + case LUA_VNIL: return &absentkey; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ + return luaH_getint(t, k); /* use specialized version */ + /* else... */ + } /* FALLTHROUGH */ + default: + return getgeneric(t, key, 0); + } +} + + +/* +** Finish a raw "set table" operation, where 'slot' is where the value +** should have been (the result of a previous "get table"). +** Beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +void luaH_finishset (lua_State *L, Table *t, const TValue *key, + const TValue *slot, TValue *value) { + if (isabstkey(slot)) + luaH_newkey(L, t, key, value); + else + setobj2t(L, cast(TValue *, slot), value); +} + + +/* +** beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) { + const TValue *slot = luaH_get(t, key); + luaH_finishset(L, t, key, slot, value); +} + + +void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { + const TValue *p = luaH_getint(t, key); + if (isabstkey(p)) { + TValue k; + setivalue(&k, key); + luaH_newkey(L, t, &k, value); + } + else + setobj2t(L, cast(TValue *, p), value); +} + + +/* +** Try to find a boundary in the hash part of table 't'. From the +** caller, we know that 'j' is zero or present and that 'j + 1' is +** present. We want to find a larger key that is absent from the +** table, so that we can do a binary search between the two keys to +** find a boundary. We keep doubling 'j' until we get an absent index. +** If the doubling would overflow, we try LUA_MAXINTEGER. If it is +** absent, we are ready for the binary search. ('j', being max integer, +** is larger or equal to 'i', but it cannot be equal because it is +** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is +** not a valid integer in Lua.) +*/ +static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { + lua_Unsigned i; + if (j == 0) j++; /* the caller ensures 'j + 1' is present */ + do { + i = j; /* 'i' is a present index */ + if (j <= l_castS2U(LUA_MAXINTEGER) / 2) + j *= 2; + else { + j = LUA_MAXINTEGER; + if (isempty(luaH_getint(t, j))) /* t[j] not present? */ + break; /* 'j' now is an absent index */ + else /* weird case */ + return j; /* well, max integer is a boundary... */ + } + } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */ + /* i < j && t[i] present && t[j] absent */ + while (j - i > 1u) { /* do a binary search between them */ + lua_Unsigned m = (i + j) / 2; + if (isempty(luaH_getint(t, m))) j = m; + else i = m; + } + return i; +} + + +static unsigned int binsearch (const TValue *array, unsigned int i, + unsigned int j) { + while (j - i > 1u) { /* binary search */ + unsigned int m = (i + j) / 2; + if (isempty(&array[m - 1])) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table 't'. (A 'boundary' is an integer index +** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent +** and 'maxinteger' if t[maxinteger] is present.) +** (In the next explanation, we use Lua indices, that is, with base 1. +** The code itself uses base 0 when indexing the array part of the table.) +** The code starts with 'limit = t->alimit', a position in the array +** part that may be a boundary. +** +** (1) If 't[limit]' is empty, there must be a boundary before it. +** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1' +** is present. If so, it is a boundary. Otherwise, do a binary search +** between 0 and limit to find a boundary. In both cases, try to +** use this boundary as the new 'alimit', as a hint for the next call. +** +** (2) If 't[limit]' is not empty and the array has more elements +** after 'limit', try to find a boundary there. Again, try first +** the special case (which should be quite frequent) where 'limit+1' +** is empty, so that 'limit' is a boundary. Otherwise, check the +** last element of the array part. If it is empty, there must be a +** boundary between the old limit (present) and the last element +** (absent), which is found with a binary search. (This boundary always +** can be a new limit.) +** +** (3) The last case is when there are no elements in the array part +** (limit == 0) or its last element (the new limit) is present. +** In this case, must check the hash part. If there is no hash part +** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call +** 'hash_search' to find a boundary in the hash part of the table. +** (In those cases, the boundary is not inside the array part, and +** therefore cannot be used as a new limit.) +*/ +lua_Unsigned luaH_getn (Table *t) { + unsigned int limit = t->alimit; + if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ + /* there must be a boundary before 'limit' */ + if (limit >= 2 && !isempty(&t->array[limit - 2])) { + /* 'limit - 1' is a boundary; can it be a new limit? */ + if (ispow2realasize(t) && !ispow2(limit - 1)) { + t->alimit = limit - 1; + setnorealasize(t); /* now 'alimit' is not the real size */ + } + return limit - 1; + } + else { /* must search for a boundary in [0, limit] */ + unsigned int boundary = binsearch(t->array, 0, limit); + /* can this boundary represent the real size of the array? */ + if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { + t->alimit = boundary; /* use it as the new limit */ + setnorealasize(t); + } + return boundary; + } + } + /* 'limit' is zero or present in table */ + if (!limitequalsasize(t)) { /* (2)? */ + /* 'limit' > 0 and array has more elements after 'limit' */ + if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */ + return limit; /* this is the boundary */ + /* else, try last element in the array */ + limit = luaH_realasize(t); + if (isempty(&t->array[limit - 1])) { /* empty? */ + /* there must be a boundary in the array after old limit, + and it must be a valid new limit */ + unsigned int boundary = binsearch(t->array, t->alimit, limit); + t->alimit = boundary; + return boundary; + } + /* else, new limit is present in the table; check the hash part */ + } + /* (3) 'limit' is the last element and either is zero or present in table */ + lua_assert(limit == luaH_realasize(t) && + (limit == 0 || !isempty(&t->array[limit - 1]))); + if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1)))) + return limit; /* 'limit + 1' is absent */ + else /* 'limit + 1' is also present */ + return hash_search(t, limit); +} + + + +#if defined(LUA_DEBUG) + +/* export these functions for the test library */ + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainpositionTV(t, key); +} + +#endif +/* +** $Id: ldo.c $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#define ldo_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lapi.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lparser.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ +/*#include "lundump.h"*/ +/*#include "lvm.h"*/ +/*#include "lzio.h"*/ + + + +#define errorstatus(s) ((s) > LUA_YIELD) + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + +/* +** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By +** default, Lua handles errors with exceptions when compiling as +** C++ code, with _longjmp/_setjmp when asked to use them, and with +** longjmp/setjmp otherwise. +*/ +#if !defined(LUAI_THROW) /* { */ + +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ + +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) \ + try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_POSIX) /* }{ */ + +/* in POSIX, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else /* }{ */ + +/* ISO C handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif /* } */ + +#endif /* } */ + + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + case LUA_OK: { /* special case only for closing upvalues */ + setnilvalue(s2v(oldtop)); /* no error message */ + break; + } + default: { + lua_assert(errorstatus(errcode)); /* real error */ + setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ + break; + } + } + L->top.p = oldtop + 1; +} + + +l_noret luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { /* thread has an error handler? */ + L->errorJmp->status = errcode; /* set status */ + LUAI_THROW(L, L->errorJmp); /* jump to it */ + } + else { /* thread has no error handler */ + global_State *g = G(L); + errcode = luaE_resetthread(L, errcode); /* close all upvalues */ + if (g->mainthread->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ + luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ + } + else { /* no handler at all; abort */ + if (g->panic) { /* panic function? */ + lua_unlock(L); + g->panic(L); /* call panic function (last chance to jump out) */ + } + abort(); + } + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + l_uint32 oldnCcalls = L->nCcalls; + struct lua_longjmp lj; + lj.status = LUA_OK; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + L->nCcalls = oldnCcalls; + return lj.status; +} + +/* }====================================================== */ + + +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ + + +/* +** Change all pointers to the stack into offsets. +*/ +static void relstack (lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.offset = savestack(L, L->top.p); + L->tbclist.offset = savestack(L, L->tbclist.p); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.offset = savestack(L, uplevel(up)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.offset = savestack(L, ci->top.p); + ci->func.offset = savestack(L, ci->func.p); + } +} + + +/* +** Change back all offsets into pointers. +*/ +static void correctstack (lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.p = restorestack(L, L->top.offset); + L->tbclist.p = restorestack(L, L->tbclist.offset); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(restorestack(L, up->v.offset)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = restorestack(L, ci->top.offset); + ci->func.p = restorestack(L, ci->func.offset); + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ + } +} + + +/* some space for error handling */ +#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) + +/* +** Reallocate the stack to a new size, correcting all pointers into it. +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before the reallocation, all pointers are +** changed to offsets, and after the reallocation they are changed back +** to pointers. As during the reallocation the pointers are invalid, the +** reallocation cannot run emergency collections. +** +** In case of allocation error, raise an error or return false according +** to 'raiseerror'. +*/ +int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { + int oldsize = stacksize(L); + int i; + StkId newstack; + int oldgcstop = G(L)->gcstopem; + lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + relstack(L); /* change pointers to offsets */ + G(L)->gcstopem = 1; /* stop emergency collection */ + newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, + newsize + EXTRA_STACK, StackValue); + G(L)->gcstopem = oldgcstop; /* restore emergency collection */ + if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ + correctstack(L); /* change offsets back to pointers */ + if (raiseerror) + luaM_error(L); + else return 0; /* do not raise an error */ + } + L->stack.p = newstack; + correctstack(L); /* change offsets back to pointers */ + L->stack_last.p = L->stack.p + newsize; + for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) + setnilvalue(s2v(newstack + i)); /* erase new segment */ + return 1; +} + + +/* +** Try to grow the stack by at least 'n' elements. When 'raiseerror' +** is true, raises any error; otherwise, return 0 in case of errors. +*/ +int luaD_growstack (lua_State *L, int n, int raiseerror) { + int size = stacksize(L); + if (l_unlikely(size > LUAI_MAXSTACK)) { + /* if stack is larger than maximum, thread is already using the + extra space reserved for errors, that is, thread is handling + a stack error; cannot grow further than that. */ + lua_assert(stacksize(L) == ERRORSTACKSIZE); + if (raiseerror) + luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + return 0; /* if not 'raiseerror', just signal it */ + } + else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ + int newsize = 2 * size; /* tentative new size */ + int needed = cast_int(L->top.p - L->stack.p) + n; + if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ + newsize = LUAI_MAXSTACK; + if (newsize < needed) /* but must respect what was asked for */ + newsize = needed; + if (l_likely(newsize <= LUAI_MAXSTACK)) + return luaD_reallocstack(L, newsize, raiseerror); + } + /* else stack overflow */ + /* add extra size to be able to handle the error message */ + luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); + if (raiseerror) + luaG_runerror(L, "stack overflow"); + return 0; +} + + +/* +** Compute how much of the stack is being used, by computing the +** maximum top of all call frames in the stack and the current top. +*/ +static int stackinuse (lua_State *L) { + CallInfo *ci; + int res; + StkId lim = L->top.p; + for (ci = L->ci; ci != NULL; ci = ci->previous) { + if (lim < ci->top.p) lim = ci->top.p; + } + lua_assert(lim <= L->stack_last.p + EXTRA_STACK); + res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */ + if (res < LUA_MINSTACK) + res = LUA_MINSTACK; /* ensure a minimum size */ + return res; +} + + +/* +** If stack size is more than 3 times the current use, reduce that size +** to twice the current use. (So, the final stack size is at most 2/3 the +** previous size, and half of its entries are empty.) +** As a particular case, if stack was handling a stack overflow and now +** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than +** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack +** will be reduced to a "regular" size. +*/ +void luaD_shrinkstack (lua_State *L) { + int inuse = stackinuse(L); + int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; + /* if thread is currently not handling a stack overflow and its + size is larger than maximum "reasonable" size, shrink it */ + if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; + luaD_reallocstack(L, nsize, 0); /* ok if that fails */ + } + else /* don't change stack */ + condmovestack(L,{},{}); /* (change only for debugging) */ + luaE_shrinkCI(L); /* shrink CI list */ +} + + +void luaD_inctop (lua_State *L) { + luaD_checkstack(L, 1); + L->top.p++; +} + +/* }================================================================== */ + + +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which trigger this +** function, can be changed asynchronously by signals.) +*/ +void luaD_hook (lua_State *L, int event, int line, + int ftransfer, int ntransfer) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { /* make sure there is a hook */ + int mask = CIST_HOOKED; + CallInfo *ci = L->ci; + ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ + ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ + lua_Debug ar; + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + if (ntransfer != 0) { + mask |= CIST_TRAN; /* 'ci' has transfer information */ + ci->u2.transferinfo.ftransfer = ftransfer; + ci->u2.transferinfo.ntransfer = ntransfer; + } + if (isLua(ci) && L->top.p < ci->top.p) + L->top.p = ci->top.p; /* protect entire activation register */ + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + if (ci->top.p < L->top.p + LUA_MINSTACK) + ci->top.p = L->top.p + LUA_MINSTACK; + L->allowhook = 0; /* cannot call hooks inside a hook */ + ci->callstatus |= mask; + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + ci->top.p = restorestack(L, ci_top); + L->top.p = restorestack(L, top); + ci->callstatus &= ~mask; + } +} + + +/* +** Executes a call hook for Lua functions. This function is called +** whenever 'hookmask' is not zero, so it checks whether call hooks are +** active. +*/ +void luaD_hookcall (lua_State *L, CallInfo *ci) { + L->oldpc = 0; /* set 'oldpc' for new function */ + if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */ + int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL + : LUA_HOOKCALL; + Proto *p = ci_func(ci)->p; + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_hook(L, event, -1, 1, p->numparams); + ci->u.l.savedpc--; /* correct 'pc' */ + } +} + + +/* +** Executes a return hook for Lua and C functions and sets/corrects +** 'oldpc'. (Note that this correction is needed by the line hook, so it +** is done even when return hooks are off.) +*/ +static void rethook (lua_State *L, CallInfo *ci, int nres) { + if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ + StkId firstres = L->top.p - nres; /* index of first result */ + int delta = 0; /* correction for vararg functions */ + int ftransfer; + if (isLua(ci)) { + Proto *p = ci_func(ci)->p; + if (p->is_vararg) + delta = ci->u.l.nextraargs + p->numparams + 1; + } + ci->func.p += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast(unsigned short, firstres - ci->func.p); + luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ + ci->func.p -= delta; + } + if (isLua(ci = ci->previous)) + L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ +} + + +/* +** Check whether 'func' has a '__call' metafield. If so, put it in the +** stack, below original 'func', so that 'luaD_precall' can call it. Raise +** an error if there is no '__call' metafield. +*/ +StkId luaD_tryfuncTM (lua_State *L, StkId func) { + const TValue *tm; + StkId p; + checkstackGCp(L, 1, func); /* space for metamethod */ + tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ + if (l_unlikely(ttisnil(tm))) + luaG_callerror(L, s2v(func)); /* nothing to call */ + for (p = L->top.p; p > func; p--) /* open space for metamethod */ + setobjs2s(L, p, p-1); + L->top.p++; /* stack space pre-allocated by the caller */ + setobj2s(L, func, tm); /* metamethod is the new function to be called */ + return func; +} + + +/* +** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. +** Handle most typical cases (zero results for commands, one result for +** expressions, multiple results for tail calls/single parameters) +** separated. +*/ +l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { + StkId firstresult; + int i; + switch (wanted) { /* handle typical cases separately */ + case 0: /* no values needed */ + L->top.p = res; + return; + case 1: /* one value needed */ + if (nres == 0) /* no results? */ + setnilvalue(s2v(res)); /* adjust with nil */ + else /* at least one result */ + setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ + L->top.p = res + 1; + return; + case LUA_MULTRET: + wanted = nres; /* we want all results */ + break; + default: /* two/more results and/or to-be-closed variables */ + if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ + L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ + L->ci->u2.nres = nres; + res = luaF_close(L, res, CLOSEKTOP, 1); + L->ci->callstatus &= ~CIST_CLSRET; + if (L->hookmask) { /* if needed, call hook after '__close's */ + ptrdiff_t savedres = savestack(L, res); + rethook(L, L->ci, nres); + res = restorestack(L, savedres); /* hook can move stack */ + } + wanted = decodeNresults(wanted); + if (wanted == LUA_MULTRET) + wanted = nres; /* we want all results */ + } + break; + } + /* generic case */ + firstresult = L->top.p - nres; /* index of first result */ + if (nres > wanted) /* extra results? */ + nres = wanted; /* don't need them */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstresult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(s2v(res + i)); + L->top.p = res + wanted; /* top points after the last result */ +} + + +/* +** Finishes a function call: calls hook if necessary, moves current +** number of results to proper place, and returns to previous call +** info. If function has to close variables, hook must be called after +** that. +*/ +void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { + int wanted = ci->nresults; + if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) + rethook(L, ci, nres); + /* move results to proper place */ + moveresults(L, ci->func.p, nres, wanted); + /* function cannot be in any of these cases when returning */ + lua_assert(!(ci->callstatus & + (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); + L->ci = ci->previous; /* back to caller (after closing variables) */ +} + + + +#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) + + +l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, + int mask, StkId top) { + CallInfo *ci = L->ci = next_ci(L); /* new frame */ + ci->func.p = func; + ci->nresults = nret; + ci->callstatus = mask; + ci->top.p = top; + return ci; +} + + +/* +** precall for C functions +*/ +l_sinline int precallC (lua_State *L, StkId func, int nresults, + lua_CFunction f) { + int n; /* number of returns */ + CallInfo *ci; + checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, + L->top.p + LUA_MINSTACK); + lua_assert(ci->top.p <= L->stack_last.p); + if (l_unlikely(L->hookmask & LUA_MASKCALL)) { + int narg = cast_int(L->top.p - func) - 1; + luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); + } + lua_unlock(L); + n = (*f)(L); /* do the actual call */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, ci, n); + return n; +} + + +/* +** Prepare a function for a tail call, building its call info on top +** of the current call info. 'narg1' is the number of arguments plus 1 +** (so that it includes the function itself). Return the number of +** results, if it was a C function, or -1 for a Lua function. +*/ +int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta) { + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); + case LUA_VLCF: /* light C function */ + return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); + case LUA_VLCL: { /* Lua function */ + Proto *p = clLvalue(s2v(func))->p; + int fsize = p->maxstacksize; /* frame size */ + int nfixparams = p->numparams; + int i; + checkstackGCp(L, fsize - delta, func); + ci->func.p -= delta; /* restore 'func' (if vararg) */ + for (i = 0; i < narg1; i++) /* move down function and arguments */ + setobjs2s(L, ci->func.p + i, func + i); + func = ci->func.p; /* moved-down function */ + for (; narg1 <= nfixparams; narg1++) + setnilvalue(s2v(func + narg1)); /* complete missing arguments */ + ci->top.p = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top.p <= L->stack_last.p); + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus |= CIST_TAIL; + L->top.p = func + narg1; /* set top */ + return -1; + } + default: { /* not a function */ + func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ + narg1++; + goto retry; /* try again */ + } + } +} + + +/* +** Prepares the call to a function (C or Lua). For C functions, also do +** the call. The function to be called is at '*func'. The arguments +** are on the stack, right after the function. Returns the CallInfo +** to be executed, if it was a Lua function. Otherwise (a C function) +** returns NULL, with all the results on the stack, starting at the +** original function position. +*/ +CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + precallC(L, func, nresults, clCvalue(s2v(func))->f); + return NULL; + case LUA_VLCF: /* light C function */ + precallC(L, func, nresults, fvalue(s2v(func))); + return NULL; + case LUA_VLCL: { /* Lua function */ + CallInfo *ci; + Proto *p = clLvalue(s2v(func))->p; + int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ + int nfixparams = p->numparams; + int fsize = p->maxstacksize; /* frame size */ + checkstackGCp(L, fsize, func); + L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); + ci->u.l.savedpc = p->code; /* starting point */ + for (; narg < nfixparams; narg++) + setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + lua_assert(ci->top.p <= L->stack_last.p); + return ci; + } + default: { /* not a function */ + func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ + /* return luaD_precall(L, func, nresults); */ + goto retry; /* try again with metamethod */ + } + } +} + + +/* +** Call a function (C or Lua) through C. 'inc' can be 1 (increment +** number of recursive invocations in the C stack) or nyci (the same +** plus increment number of non-yieldable calls). +** This function can be called with some use of EXTRA_STACK, so it should +** check the stack before doing anything else. 'luaD_precall' already +** does that. +*/ +l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) { + CallInfo *ci; + L->nCcalls += inc; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) { + checkstackp(L, 0, func); /* free any use of EXTRA_STACK */ + luaE_checkcstack(L); + } + if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ + ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ + luaV_execute(L, ci); /* call it */ + } + L->nCcalls -= inc; +} + + +/* +** External interface for 'ccall' +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, 1); +} + + +/* +** Similar to 'luaD_call', but does not allow yields during the call. +*/ +void luaD_callnoyield (lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, nyci); +} + + +/* +** Finish the job of 'lua_pcallk' after it was interrupted by an yield. +** (The caller, 'finishCcall', does the final call to 'adjustresults'.) +** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'. +** If a '__close' method yields here, eventually control will be back +** to 'finishCcall' (when that '__close' method finally returns) and +** 'finishpcallk' will run again and close any still pending '__close' +** methods. Similarly, if a '__close' method errs, 'precover' calls +** 'unroll' which calls ''finishCcall' and we are back here again, to +** close any pending '__close' methods. +** Note that, up to the call to 'luaF_close', the corresponding +** 'CallInfo' is not modified, so that this repeated run works like the +** first one (except that it has at least one less '__close' to do). In +** particular, field CIST_RECST preserves the error status across these +** multiple runs, changing only if there is a new error. +*/ +static int finishpcallk (lua_State *L, CallInfo *ci) { + int status = getcistrecst(ci); /* get original status */ + if (l_likely(status == LUA_OK)) /* no error? */ + status = LUA_YIELD; /* was interrupted by an yield */ + else { /* error */ + StkId func = restorestack(L, ci->u2.funcidx); + L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ + func = luaF_close(L, func, status, 1); /* can yield or raise an error */ + luaD_seterrorobj(L, status, func); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ + setcistrecst(ci, LUA_OK); /* clear original status */ + } + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + /* if it is here, there were errors or yields; unlike 'lua_pcallk', + do not change status */ + return status; +} + + +/* +** Completes the execution of a C function interrupted by an yield. +** The interruption must have happened while the function was either +** closing its tbc variables in 'moveresults' or executing +** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes +** 'luaD_poscall'. In the second case, the call to 'finishpcallk' +** finishes the interrupted execution of 'lua_pcallk'. After that, it +** calls the continuation of the interrupted function and finally it +** completes the job of the 'luaD_call' that called the function. In +** the call to 'adjustresults', we do not know the number of results +** of the function called by 'lua_callk'/'lua_pcallk', so we are +** conservative and use LUA_MULTRET (always adjust). +*/ +static void finishCcall (lua_State *L, CallInfo *ci) { + int n; /* actual number of results from C function */ + if (ci->callstatus & CIST_CLSRET) { /* was returning? */ + lua_assert(hastocloseCfunc(ci->nresults)); + n = ci->u2.nres; /* just redo 'luaD_poscall' */ + /* don't need to reset CIST_CLSRET, as it will be set again anyway */ + } + else { + int status = LUA_YIELD; /* default if there were no errors */ + /* must have a continuation and must be able to call it */ + lua_assert(ci->u.c.k != NULL && yieldable(L)); + if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ + status = finishpcallk(L, ci); /* finish it */ + adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ + lua_unlock(L); + n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ +} + + +/* +** Executes "full continuation" (everything in the stack) of a +** previously interrupted coroutine until the stack is empty (or another +** interruption long-jumps out of the loop). +*/ +static void unroll (lua_State *L, void *ud) { + CallInfo *ci; + UNUSED(ud); + while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ + if (!isLua(ci)) /* C function? */ + finishCcall(L, ci); /* complete its execution */ + else { /* Lua function */ + luaV_finishOp(L); /* finish interrupted instruction */ + luaV_execute(L, ci); /* execute down to higher C 'boundary' */ + } + } +} + + +/* +** Try to find a suspended protected call (a "recover point") for the +** given thread. +*/ +static CallInfo *findpcall (lua_State *L) { + CallInfo *ci; + for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ + if (ci->callstatus & CIST_YPCALL) + return ci; + } + return NULL; /* no pending pcall */ +} + + +/* +** Signal an error in the call to 'lua_resume', not in the execution +** of the coroutine itself. (Such errors should not be handled by any +** coroutine error handler and should not kill the coroutine.) +*/ +static int resume_error (lua_State *L, const char *msg, int narg) { + L->top.p -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ + api_incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +/* +** Do the work for 'lua_resume' in protected mode. Most of the work +** depends on the status of the coroutine: initial state, suspended +** inside a hook, or regularly suspended (optionally with a continuation +** function), plus erroneous cases: non-suspended coroutine or dead +** coroutine. +*/ +static void resume (lua_State *L, void *ud) { + int n = *(cast(int*, ud)); /* number of arguments */ + StkId firstArg = L->top.p - n; /* first argument */ + CallInfo *ci = L->ci; + if (L->status == LUA_OK) /* starting a coroutine? */ + ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = LUA_OK; /* mark that it is running (again) */ + if (isLua(ci)) { /* yielded inside a hook? */ + L->top.p = firstArg; /* discard arguments */ + luaV_execute(L, ci); /* just continue running Lua code */ + } + else { /* 'common' yield */ + if (ci->u.c.k != NULL) { /* does it have a continuation function? */ + lua_unlock(L); + n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ + } + unroll(L, NULL); /* run continuation */ + } +} + + +/* +** Unrolls a coroutine in protected mode while there are recoverable +** errors, that is, errors inside a protected call. (Any error +** interrupts 'unroll', and this loop protects it again so it can +** continue.) Stops with a normal end (status == LUA_OK), an yield +** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't +** find a recover point). +*/ +static int precover (lua_State *L, int status) { + CallInfo *ci; + while (errorstatus(status) && (ci = findpcall(L)) != NULL) { + L->ci = ci; /* go down to recovery functions */ + setcistrecst(ci, status); /* status to finish 'pcall' */ + status = luaD_rawrunprotected(L, unroll, NULL); + } + return status; +} + + +LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, + int *nresults) { + int status; + lua_lock(L); + if (L->status == LUA_OK) { /* may be starting a coroutine */ + if (L->ci != &L->base_ci) /* not in base level? */ + return resume_error(L, "cannot resume non-suspended coroutine", nargs); + else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + } + else if (L->status != LUA_YIELD) /* ended with errors? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + L->nCcalls = (from) ? getCcalls(from) : 0; + if (getCcalls(L) >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow", nargs); + L->nCcalls++; + luai_userstateresume(L, nargs); + api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + status = luaD_rawrunprotected(L, resume, &nargs); + /* continue running after recoverable errors */ + status = precover(L, status); + if (l_likely(!errorstatus(status))) + lua_assert(status == L->status); /* normal end or yield */ + else { /* unrecoverable error */ + L->status = cast_byte(status); /* mark thread as 'dead' */ + luaD_seterrorobj(L, status, L->top.p); /* push error message */ + L->ci->top.p = L->top.p; + } + *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield + : cast_int(L->top.p - (L->ci->func.p + 1)); + lua_unlock(L); + return status; +} + + +LUA_API int lua_isyieldable (lua_State *L) { + return yieldable(L); +} + + +LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k) { + CallInfo *ci; + luai_userstateyield(L, nresults); + lua_lock(L); + ci = L->ci; + api_checknelems(L, nresults); + if (l_unlikely(!yieldable(L))) { + if (L != G(L)->mainthread) + luaG_runerror(L, "attempt to yield across a C-call boundary"); + else + luaG_runerror(L, "attempt to yield from outside a coroutine"); + } + L->status = LUA_YIELD; + ci->u2.nyield = nresults; /* save number of results */ + if (isLua(ci)) { /* inside a hook? */ + lua_assert(!isLuacode(ci)); + api_check(L, nresults == 0, "hooks cannot yield values"); + api_check(L, k == NULL, "hooks cannot continue after yielding"); + } + else { + if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ + ci->u.c.ctx = ctx; /* save context */ + luaD_throw(L, LUA_YIELD); + } + lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ + lua_unlock(L); + return 0; /* return to 'luaD_hook' */ +} + + +/* +** Auxiliary structure to call 'luaF_close' in protected mode. +*/ +struct CloseP { + StkId level; + int status; +}; + + +/* +** Auxiliary function to call 'luaF_close' in protected mode. +*/ +static void closepaux (lua_State *L, void *ud) { + struct CloseP *pcl = cast(struct CloseP *, ud); + luaF_close(L, pcl->level, pcl->status, 0); +} + + +/* +** Calls 'luaF_close' in protected mode. Return the original status +** or, in case of errors, the new status. +*/ +int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + for (;;) { /* keep closing upvalues until no more errors */ + struct CloseP pcl; + pcl.level = restorestack(L, level); pcl.status = status; + status = luaD_rawrunprotected(L, &closepaux, &pcl); + if (l_likely(status == LUA_OK)) /* no more errors? */ + return pcl.status; + else { /* an error occurred; restore saved state and repeat */ + L->ci = old_ci; + L->allowhook = old_allowhooks; + } + } +} + + +/* +** Call the C function 'func' in protected mode, restoring basic +** thread information ('allowhook', etc.) and in particular +** its stack level in case of errors. +*/ +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (l_unlikely(status != LUA_OK)) { /* an error occurred? */ + L->ci = old_ci; + L->allowhook = old_allowhooks; + status = luaD_closeprotected(L, old_top, status); + luaD_seterrorobj(L, status, restorestack(L, old_top)); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to 'f_parser' */ + ZIO *z; + Mbuffer buff; /* dynamic structure used by the scanner */ + Dyndata dyd; /* dynamic structures used by the parser */ + const char *mode; + const char *name; +}; + + +static void checkmode (lua_State *L, const char *mode, const char *x) { + if (mode && strchr(mode, x[0]) == NULL) { + luaO_pushfstring(L, + "attempt to load a %s chunk (mode is '%s')", x, mode); + luaD_throw(L, LUA_ERRSYNTAX); + } +} + + +static void f_parser (lua_State *L, void *ud) { + LClosure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = zgetc(p->z); /* read first character */ + if (c == LUA_SIGNATURE[0]) { + checkmode(L, p->mode, "binary"); + cl = luaU_undump(L, p->z, p->name); + } + else { + checkmode(L, p->mode, "text"); + cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); + } + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luaF_initupvals(L, cl); +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { + struct SParser p; + int status; + incnny(L); /* cannot yield during parsing */ + p.z = z; p.name = name; p.mode = mode; + p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; + p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; + p.dyd.label.arr = NULL; p.dyd.label.size = 0; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); + luaZ_freebuffer(L, &p.buff); + luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); + luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); + luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); + decnny(L); + return status; +} + + +/* +** $Id: lvm.c $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lvm_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + +#include +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "lobject.h"*/ +/*#include "lopcodes.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ +/*#include "lvm.h"*/ + + +/* +** By default, use jump tables in the main interpreter loop on gcc +** and compatible compilers. +*/ +#if !defined(LUA_USE_JUMPTABLE) +#if defined(__GNUC__) +#define LUA_USE_JUMPTABLE 1 +#else +#define LUA_USE_JUMPTABLE 0 +#endif +#endif + + + +/* limit for table tag-method chains (to avoid infinite loops) */ +#define MAXTAGLOOP 2000 + + +/* +** 'l_intfitsf' checks whether a given integer is in the range that +** can be converted to a float without rounding. Used in comparisons. +*/ + +/* number of bits in the mantissa of a float */ +#define NBM (l_floatatt(MANT_DIG)) + +/* +** Check whether some integers may not fit in a float, testing whether +** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.) +** (The shifts are done in parts, to avoid shifting by more than the size +** of an integer. In a worst case, NBM == 113 for long double and +** sizeof(long) == 32.) +*/ +#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ + >> (NBM - (3 * (NBM / 4)))) > 0 + +/* limit for integers that fit in a float */ +#define MAXINTFITSF ((lua_Unsigned)1 << NBM) + +/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */ +#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF)) + +#else /* all integers fit in a float precisely */ + +#define l_intfitsf(i) 1 + +#endif + + +/* +** Try to convert a value from string to a number value. +** If the value is not a string or is a string not representing +** a valid numeral (or if coercions from strings to numbers +** are disabled via macro 'cvt2num'), do not modify 'result' +** and return 0. +*/ +static int l_strton (const TValue *obj, TValue *result) { + lua_assert(obj != result); + if (!cvt2num(obj)) /* is object not a string? */ + return 0; + else + return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1); +} + + +/* +** Try to convert a value to a float. The float case is already handled +** by the macro 'tonumber'. +*/ +int luaV_tonumber_ (const TValue *obj, lua_Number *n) { + TValue v; + if (ttisinteger(obj)) { + *n = cast_num(ivalue(obj)); + return 1; + } + else if (l_strton(obj, &v)) { /* string coercible to number? */ + *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ + return 1; + } + else + return 0; /* conversion failed */ +} + + +/* +** try to convert a float to an integer, rounding according to 'mode'. +*/ +int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) { + lua_Number f = l_floor(n); + if (n != f) { /* not an integral value? */ + if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ + else if (mode == F2Iceil) /* needs ceil? */ + f += 1; /* convert floor to ceil (remember: n != f) */ + } + return lua_numbertointeger(f, p); +} + + +/* +** try to convert a value to an integer, rounding according to 'mode', +** without string coercion. +** ("Fast track" handled by macro 'tointegerns'.) +*/ +int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) { + if (ttisfloat(obj)) + return luaV_flttointeger(fltvalue(obj), p, mode); + else if (ttisinteger(obj)) { + *p = ivalue(obj); + return 1; + } + else + return 0; +} + + +/* +** try to convert a value to an integer. +*/ +int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) { + TValue v; + if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */ + obj = &v; /* change it to point to its corresponding number */ + return luaV_tointegerns(obj, p, mode); +} + + +/* +** Try to convert a 'for' limit to an integer, preserving the semantics +** of the loop. Return true if the loop must not run; otherwise, '*p' +** gets the integer limit. +** (The following explanation assumes a positive step; it is valid for +** negative steps mutatis mutandis.) +** If the limit is an integer or can be converted to an integer, +** rounding down, that is the limit. +** Otherwise, check whether the limit can be converted to a float. If +** the float is too large, clip it to LUA_MAXINTEGER. If the float +** is too negative, the loop should not run, because any initial +** integer value is greater than such limit; so, the function returns +** true to signal that. (For this latter case, no integer limit would be +** correct; even a limit of LUA_MININTEGER would run the loop once for +** an initial value equal to LUA_MININTEGER.) +*/ +static int forlimit (lua_State *L, lua_Integer init, const TValue *lim, + lua_Integer *p, lua_Integer step) { + if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) { + /* not coercible to in integer */ + lua_Number flim; /* try to convert to float */ + if (!tonumber(lim, &flim)) /* cannot convert to float? */ + luaG_forerror(L, lim, "limit"); + /* else 'flim' is a float out of integer bounds */ + if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ + if (step < 0) return 1; /* initial value must be less than it */ + *p = LUA_MAXINTEGER; /* truncate */ + } + else { /* it is less than min integer */ + if (step > 0) return 1; /* initial value must be greater than it */ + *p = LUA_MININTEGER; /* truncate */ + } + } + return (step > 0 ? init > *p : init < *p); /* not to run? */ +} + + +/* +** Prepare a numerical for loop (opcode OP_FORPREP). +** Return true to skip the loop. Otherwise, +** after preparation, stack will be as follows: +** ra : internal index (safe copy of the control variable) +** ra + 1 : loop counter (integer loops) or limit (float loops) +** ra + 2 : step +** ra + 3 : control variable +*/ +static int forprep (lua_State *L, StkId ra) { + TValue *pinit = s2v(ra); + TValue *plimit = s2v(ra + 1); + TValue *pstep = s2v(ra + 2); + if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */ + lua_Integer init = ivalue(pinit); + lua_Integer step = ivalue(pstep); + lua_Integer limit; + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + setivalue(s2v(ra + 3), init); /* control variable */ + if (forlimit(L, init, plimit, &limit, step)) + return 1; /* skip the loop */ + else { /* prepare loop counter */ + lua_Unsigned count; + if (step > 0) { /* ascending loop? */ + count = l_castS2U(limit) - l_castS2U(init); + if (step != 1) /* avoid division in the too common case */ + count /= l_castS2U(step); + } + else { /* step < 0; descending loop */ + count = l_castS2U(init) - l_castS2U(limit); + /* 'step+1' avoids negating 'mininteger' */ + count /= l_castS2U(-(step + 1)) + 1u; + } + /* store the counter in place of the limit (which won't be + needed anymore) */ + setivalue(plimit, l_castU2S(count)); + } + } + else { /* try making all values floats */ + lua_Number init; lua_Number limit; lua_Number step; + if (l_unlikely(!tonumber(plimit, &limit))) + luaG_forerror(L, plimit, "limit"); + if (l_unlikely(!tonumber(pstep, &step))) + luaG_forerror(L, pstep, "step"); + if (l_unlikely(!tonumber(pinit, &init))) + luaG_forerror(L, pinit, "initial value"); + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (luai_numlt(0, step) ? luai_numlt(limit, init) + : luai_numlt(init, limit)) + return 1; /* skip the loop */ + else { + /* make sure internal values are all floats */ + setfltvalue(plimit, limit); + setfltvalue(pstep, step); + setfltvalue(s2v(ra), init); /* internal index */ + setfltvalue(s2v(ra + 3), init); /* control variable */ + } + } + return 0; +} + + +/* +** Execute a step of a float numerical for loop, returning +** true iff the loop must continue. (The integer case is +** written online with opcode OP_FORLOOP, for performance.) +*/ +static int floatforloop (StkId ra) { + lua_Number step = fltvalue(s2v(ra + 2)); + lua_Number limit = fltvalue(s2v(ra + 1)); + lua_Number idx = fltvalue(s2v(ra)); /* internal index */ + idx = luai_numadd(L, idx, step); /* increment index */ + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + chgfltvalue(s2v(ra), idx); /* update internal index */ + setfltvalue(s2v(ra + 3), idx); /* and control variable */ + return 1; /* jump back */ + } + else + return 0; /* finish the loop */ +} + + +/* +** Finish the table access 'val = t[key]'. +** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to +** t[k] entry (which must be empty). +*/ +void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *slot) { + int loop; /* counter to avoid infinite loops */ + const TValue *tm; /* metamethod */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + if (slot == NULL) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (l_unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ + } + else { /* 't' is a table */ + lua_assert(isempty(slot)); + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(s2v(val)); /* result is nil */ + return; + } + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ + luaT_callTMres(L, tm, t, key, val); /* call it */ + return; + } + t = tm; /* else try to access 'tm[key]' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */ + setobj2s(L, val, slot); /* done */ + return; + } + /* else repeat (tail call 'luaV_finishget') */ + } + luaG_runerror(L, "'__index' chain too long; possible loop"); +} + + +/* +** Finish a table assignment 't[key] = val'. +** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points +** to the entry 't[key]', or to a value with an absent key if there +** is no such entry. (The value at 'slot' must be empty, otherwise +** 'luaV_fastget' would have done the job.) +*/ +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, const TValue *slot) { + int loop; /* counter to avoid infinite loops */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; /* '__newindex' metamethod */ + if (slot != NULL) { /* is 't' a table? */ + Table *h = hvalue(t); /* save 't' table */ + lua_assert(isempty(slot)); /* slot must be empty */ + tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ + if (tm == NULL) { /* no metamethod? */ + luaH_finishset(L, h, key, slot, val); /* set new value */ + invalidateTMcache(h); + luaC_barrierback(L, obj2gco(h), val); + return; + } + /* else will try the metamethod */ + } + else { /* not a table; check metamethod */ + tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); + if (l_unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); + } + /* try the metamethod */ + if (ttisfunction(tm)) { + luaT_callTM(L, tm, t, key, val); + return; + } + t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastget(L, t, key, slot, luaH_get)) { + luaV_finishfastset(L, t, slot, val); + return; /* done */ + } + /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ + } + luaG_runerror(L, "'__newindex' chain too long; possible loop"); +} + + +/* +** Compare two strings 'ls' x 'rs', returning an integer less-equal- +** -greater than zero if 'ls' is less-equal-greater than 'rs'. +** The code is a little tricky because it allows '\0' in the strings +** and it uses 'strcoll' (to respect locales) for each segments +** of the strings. +*/ +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = tsslen(ls); + const char *r = getstr(rs); + size_t lr = tsslen(rs); + for (;;) { /* for each segment */ + int temp = strcoll(l, r); + if (temp != 0) /* not equal? */ + return temp; /* done */ + else { /* strings are equal up to a '\0' */ + size_t len = strlen(l); /* index of first '\0' in both strings */ + if (len == lr) /* 'rs' is finished? */ + return (len == ll) ? 0 : 1; /* check 'ls' */ + else if (len == ll) /* 'ls' is finished? */ + return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */ + /* both strings longer than 'len'; go on comparing after the '\0' */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +/* +** Check whether integer 'i' is less than float 'f'. If 'i' has an +** exact representation as a float ('l_intfitsf'), compare numbers as +** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'. +** If 'ceil(f)' is out of integer range, either 'f' is greater than +** all integers or less than all integers. +** (The test with 'l_intfitsf' is only for performance; the else +** case is correct for all values, but it is slow due to the conversion +** from float to int.) +** When 'f' is NaN, comparisons must result in false. +*/ +l_sinline int LTintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numlt(cast_num(i), f); /* compare them as floats */ + else { /* i < f <=> i < ceil(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return i < fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether integer 'i' is less than or equal to float 'f'. +** See comments on previous function. +*/ +l_sinline int LEintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numle(cast_num(i), f); /* compare them as floats */ + else { /* i <= f <=> i <= floor(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return i <= fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether float 'f' is less than integer 'i'. +** See comments on previous function. +*/ +l_sinline int LTfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numlt(f, cast_num(i)); /* compare them as floats */ + else { /* f < i <=> floor(f) < i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return fi < i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Check whether float 'f' is less than or equal to integer 'i'. +** See comments on previous function. +*/ +l_sinline int LEfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numle(f, cast_num(i)); /* compare them as floats */ + else { /* f <= i <=> ceil(f) <= i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return fi <= i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Return 'l < r', for numbers. +*/ +l_sinline int LTnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li < ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LTintfloat(li, fltvalue(r)); /* l < r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numlt(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LTfloatint(lf, ivalue(r)); + } +} + + +/* +** Return 'l <= r', for numbers. +*/ +l_sinline int LEnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li <= ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LEintfloat(li, fltvalue(r)); /* l <= r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numle(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LEfloatint(lf, ivalue(r)); + } +} + + +/* +** return 'l < r' for non-numbers. +*/ +static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) < 0; + else + return luaT_callorderTM(L, l, r, TM_LT); +} + + +/* +** Main operation less than; return 'l < r'. +*/ +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LTnum(l, r); + else return lessthanothers(L, l, r); +} + + +/* +** return 'l <= r' for non-numbers. +*/ +static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; + else + return luaT_callorderTM(L, l, r, TM_LE); +} + + +/* +** Main operation less than or equal to; return 'l <= r'. +*/ +int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LEnum(l, r); + else return lessequalothers(L, l, r); +} + + +/* +** Main operation for equality of Lua values; return 't1 == t2'. +** L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ + if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) + return 0; /* only numbers can be equal with different variants */ + else { /* two numbers with different variants */ + /* One of them is an integer. If the other does not have an + integer value, they cannot be equal; otherwise, compare their + integer values. */ + lua_Integer i1, i2; + return (luaV_tointegerns(t1, &i1, F2Ieq) && + luaV_tointegerns(t2, &i2, F2Ieq) && + i1 == i2); + } + } + /* values have same type and same variant */ + switch (ttypetag(t1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; + case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); + case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_VLCF: return fvalue(t1) == fvalue(t2); + case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: + return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !l_isfalse(s2v(L->top.p)); + } +} + + +/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ +#define tostring(L,o) \ + (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) + +#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) + +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff (StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ + do { + size_t l = vslen(s2v(top - n)); /* length of string being copied */ + memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + +/* +** Main operation for concatenation: concat 'total' values in the stack, +** from 'L->top.p - total' up to 'L->top.p - 1'. +*/ +void luaV_concat (lua_State *L, int total) { + if (total == 1) + return; /* "all" values already concatenated */ + do { + StkId top = L->top.p; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || + !tostring(L, s2v(top - 1))) + luaT_tryconcatTM(L); /* may invalidate 'top' */ + else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ + cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ + else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ + setobjs2s(L, top - 2, top - 1); /* result is second op. */ + } + else { + /* at least two non-empty string values; get as many as possible */ + size_t tl = vslen(s2v(top - 1)); + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { + size_t l = vslen(s2v(top - n - 1)); + if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) { + L->top.p = top - total; /* pop strings to avoid wasting stack */ + luaG_runerror(L, "string length overflow"); + } + tl += l; + } + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } + else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ + } + total -= n - 1; /* got 'n' strings to create one new */ + L->top.p -= n - 1; /* popped 'n' strings and pushed one */ + } while (total > 1); /* repeat until only 1 result left */ +} + + +/* +** Main operation 'ra = #rb'. +*/ +void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { + const TValue *tm; + switch (ttypetag(rb)) { + case LUA_VTABLE: { + Table *h = hvalue(rb); + tm = fasttm(L, h->metatable, TM_LEN); + if (tm) break; /* metamethod? break switch to call it */ + setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ + return; + } + case LUA_VSHRSTR: { + setivalue(s2v(ra), tsvalue(rb)->shrlen); + return; + } + case LUA_VLNGSTR: { + setivalue(s2v(ra), tsvalue(rb)->u.lnglen); + return; + } + default: { /* try metamethod */ + tm = luaT_gettmbyobj(L, rb, TM_LEN); + if (l_unlikely(notm(tm))) /* no metamethod? */ + luaG_typeerror(L, rb, "get length of"); + break; + } + } + luaT_callTMres(L, tm, rb, rb, ra); +} + + +/* +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. +*/ +lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } + else { + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_idiv.) +*/ +lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } + else { + lua_Integer r = m % n; + if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } +} + + +/* +** Float modulus +*/ +lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { + lua_Number r; + luai_nummod(L, m, n, r); + return r; +} + + +/* number of bits in an integer */ +#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) + + +/* +** Shift left operation. (Shift right just negates 'y'.) +*/ +lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { + if (y < 0) { /* shift right? */ + if (y <= -NBITS) return 0; + else return intop(>>, x, -y); + } + else { /* shift left */ + if (y >= NBITS) return 0; + else return intop(<<, x, y); + } +} + + +/* +** create a new Lua closure, push it in the stack, and initialize +** its upvalues. +*/ +static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, + StkId ra) { + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + LClosure *ncl = luaF_newLclosure(L, nup); + ncl->p = p; + setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */ + for (i = 0; i < nup; i++) { /* fill in its upvalues */ + if (uv[i].instack) /* upvalue refers to local variable? */ + ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); + else /* get upvalue from enclosing function */ + ncl->upvals[i] = encup[uv[i].idx]; + luaC_objbarrier(L, ncl, ncl->upvals[i]); + } +} + + +/* +** finish execution of an opcode interrupted by a yield +*/ +void luaV_finishOp (lua_State *L) { + CallInfo *ci = L->ci; + StkId base = ci->func.p + 1; + Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ + OpCode op = GET_OPCODE(inst); + switch (op) { /* finish its execution */ + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p); + break; + } + case OP_UNM: case OP_BNOT: case OP_LEN: + case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: + case OP_GETFIELD: case OP_SELF: { + setobjs2s(L, base + GETARG_A(inst), --L->top.p); + break; + } + case OP_LT: case OP_LE: + case OP_LTI: case OP_LEI: + case OP_GTI: case OP_GEI: + case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ + int res = !l_isfalse(s2v(L->top.p - 1)); + L->top.p--; +#if defined(LUA_COMPAT_LT_LE) + if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ + ci->callstatus ^= CIST_LEQ; /* clear mark */ + res = !res; /* negate result */ + } +#endif + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); + if (res != GETARG_k(inst)) /* condition failed? */ + ci->u.l.savedpc++; /* skip jump instruction */ + break; + } + case OP_CONCAT: { + StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */ + int a = GETARG_A(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ + setobjs2s(L, top - 2, top); /* put TM result in proper position */ + L->top.p = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ + break; + } + case OP_CLOSE: { /* yielded closing variables */ + ci->u.l.savedpc--; /* repeat instruction to close other vars. */ + break; + } + case OP_RETURN: { /* yielded closing variables */ + StkId ra = base + GETARG_A(inst); + /* adjust top to signal correct number of returns, in case the + return is "up to top" ('isIT') */ + L->top.p = ra + ci->u2.nres; + /* repeat instruction to close other vars. and complete the return */ + ci->u.l.savedpc--; + break; + } + default: { + /* only these other opcodes can yield */ + lua_assert(op == OP_TFORCALL || op == OP_CALL || + op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE || + op == OP_SETI || op == OP_SETFIELD); + break; + } + } +} + + + + +/* +** {================================================================== +** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** =================================================================== +*/ + +#define l_addi(L,a,b) intop(+, a, b) +#define l_subi(L,a,b) intop(-, a, b) +#define l_muli(L,a,b) intop(*, a, b) +#define l_band(a,b) intop(&, a, b) +#define l_bor(a,b) intop(|, a, b) +#define l_bxor(a,b) intop(^, a, b) + +#define l_lti(a,b) (a < b) +#define l_lei(a,b) (a <= b) +#define l_gti(a,b) (a > b) +#define l_gei(a,b) (a >= b) + + +/* +** Arithmetic operations with immediate operands. 'iop' is the integer +** operation, 'fop' is the float operation. +*/ +#define op_arithI(L,iop,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + int imm = GETARG_sC(i); \ + if (ttisinteger(v1)) { \ + lua_Integer iv1 = ivalue(v1); \ + pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ + } \ + else if (ttisfloat(v1)) { \ + lua_Number nb = fltvalue(v1); \ + lua_Number fimm = cast_num(imm); \ + pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ + }} + + +/* +** Auxiliary function for arithmetic operations over floats and others +** with two register operands. +*/ +#define op_arithf_aux(L,v1,v2,fop) { \ + lua_Number n1; lua_Number n2; \ + if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ + }} + + +/* +** Arithmetic operations over floats and others with register operands. +*/ +#define op_arithf(L,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with K operands for floats. +*/ +#define op_arithfK(L,fop) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations over integers and floats. +*/ +#define op_arith_aux(L,v1,v2,iop,fop) { \ + StkId ra = RA(i); \ + if (ttisinteger(v1) && ttisinteger(v2)) { \ + lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ + pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ + } \ + else op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with register operands. +*/ +#define op_arith(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Arithmetic operations with K operands. +*/ +#define op_arithK(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Bitwise operations with constant operand. +*/ +#define op_bitwiseK(L,op) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + lua_Integer i1; \ + lua_Integer i2 = ivalue(v2); \ + if (tointegerns(v1, &i1)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Bitwise operations with register operands. +*/ +#define op_bitwise(L,op) { \ + StkId ra = RA(i); \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + lua_Integer i1; lua_Integer i2; \ + if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Order operations with register operands. 'opn' actually works +** for all numbers, but the fast track improves performance for +** integers. +*/ +#define op_order(L,opi,opn,other) { \ + StkId ra = RA(i); \ + int cond; \ + TValue *rb = vRB(i); \ + if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(s2v(ra)); \ + lua_Integer ib = ivalue(rb); \ + cond = opi(ia, ib); \ + } \ + else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ + cond = opn(s2v(ra), rb); \ + else \ + Protect(cond = other(L, s2v(ra), rb)); \ + docondjump(); } + + +/* +** Order operations with immediate operand. (Immediate operand is +** always small enough to have an exact representation as a float.) +*/ +#define op_orderI(L,opi,opf,inv,tm) { \ + StkId ra = RA(i); \ + int cond; \ + int im = GETARG_sB(i); \ + if (ttisinteger(s2v(ra))) \ + cond = opi(ivalue(s2v(ra)), im); \ + else if (ttisfloat(s2v(ra))) { \ + lua_Number fa = fltvalue(s2v(ra)); \ + lua_Number fim = cast_num(im); \ + cond = opf(fa, fim); \ + } \ + else { \ + int isf = GETARG_C(i); \ + Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + } \ + docondjump(); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Function 'luaV_execute': main interpreter loop +** =================================================================== +*/ + +/* +** some macros for common tasks in 'luaV_execute' +*/ + + +#define RA(i) (base+GETARG_A(i)) +#define RB(i) (base+GETARG_B(i)) +#define vRB(i) s2v(RB(i)) +#define KB(i) (k+GETARG_B(i)) +#define RC(i) (base+GETARG_C(i)) +#define vRC(i) s2v(RC(i)) +#define KC(i) (k+GETARG_C(i)) +#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i))) + + + +#define updatetrap(ci) (trap = ci->u.l.trap) + +#define updatebase(ci) (base = ci->func.p + 1) + + +#define updatestack(ci) \ + { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } } + + +/* +** Execute a jump instruction. The 'updatetrap' allows signals to stop +** tight loops. (Without it, the local copy of 'trap' could never change.) +*/ +#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } + + +/* for test instructions, execute the jump instruction that follows it */ +#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); } + +/* +** do a conditional jump: skip next instruction if 'cond' is not what +** was expected (parameter 'k'), else do next instruction, which must +** be a jump. +*/ +#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci); + + +/* +** Correct global 'pc'. +*/ +#define savepc(L) (ci->u.l.savedpc = pc) + + +/* +** Whenever code can raise errors, the global 'pc' and the global +** 'top' must be correct to report occasional errors. +*/ +#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) + + +/* +** Protect code that, in general, can raise errors, reallocate the +** stack, and change the hooks. +*/ +#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) + +/* special version that does not change the top */ +#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) + +/* +** Protect code that can only raise errors. (That is, it cannot change +** the stack or hooks.) +*/ +#define halfProtect(exp) (savestate(L,ci), (exp)) + +/* 'c' is the limit of live values in the stack */ +#define checkGC(L,c) \ + { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ + updatetrap(ci)); \ + luai_threadyield(L); } + + +/* fetch an instruction and prepare its execution */ +#define vmfetch() { \ + if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \ + trap = luaG_traceexec(L, pc); /* handle hooks */ \ + updatebase(ci); /* correct stack */ \ + } \ + i = *(pc++); \ +} + +#define vmdispatch(o) switch(o) +#define vmcase(l) case l: +#define vmbreak break + + +void luaV_execute (lua_State *L, CallInfo *ci) { + LClosure *cl; + TValue *k; + StkId base; + const Instruction *pc; + int trap; +#if LUA_USE_JUMPTABLE +/*#include "ljumptab.h"*/ +/* +** $Id: ljumptab.h $ +** Jump Table for the Lua interpreter +** See Copyright Notice in lua.h +*/ + + +#undef vmdispatch +#undef vmcase +#undef vmbreak + +#define vmdispatch(x) goto *disptab[x]; + +#define vmcase(l) L_##l: + +#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); + + +static const void *const disptab[NUM_OPCODES] = { + +#if 0 +** you can update the following list with this command: +** +** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h +** +#endif + +&&L_OP_MOVE, +&&L_OP_LOADI, +&&L_OP_LOADF, +&&L_OP_LOADK, +&&L_OP_LOADKX, +&&L_OP_LOADFALSE, +&&L_OP_LFALSESKIP, +&&L_OP_LOADTRUE, +&&L_OP_LOADNIL, +&&L_OP_GETUPVAL, +&&L_OP_SETUPVAL, +&&L_OP_GETTABUP, +&&L_OP_GETTABLE, +&&L_OP_GETI, +&&L_OP_GETFIELD, +&&L_OP_SETTABUP, +&&L_OP_SETTABLE, +&&L_OP_SETI, +&&L_OP_SETFIELD, +&&L_OP_NEWTABLE, +&&L_OP_SELF, +&&L_OP_ADDI, +&&L_OP_ADDK, +&&L_OP_SUBK, +&&L_OP_MULK, +&&L_OP_MODK, +&&L_OP_POWK, +&&L_OP_DIVK, +&&L_OP_IDIVK, +&&L_OP_BANDK, +&&L_OP_BORK, +&&L_OP_BXORK, +&&L_OP_SHRI, +&&L_OP_SHLI, +&&L_OP_ADD, +&&L_OP_SUB, +&&L_OP_MUL, +&&L_OP_MOD, +&&L_OP_POW, +&&L_OP_DIV, +&&L_OP_IDIV, +&&L_OP_BAND, +&&L_OP_BOR, +&&L_OP_BXOR, +&&L_OP_SHL, +&&L_OP_SHR, +&&L_OP_MMBIN, +&&L_OP_MMBINI, +&&L_OP_MMBINK, +&&L_OP_UNM, +&&L_OP_BNOT, +&&L_OP_NOT, +&&L_OP_LEN, +&&L_OP_CONCAT, +&&L_OP_CLOSE, +&&L_OP_TBC, +&&L_OP_JMP, +&&L_OP_EQ, +&&L_OP_LT, +&&L_OP_LE, +&&L_OP_EQK, +&&L_OP_EQI, +&&L_OP_LTI, +&&L_OP_LEI, +&&L_OP_GTI, +&&L_OP_GEI, +&&L_OP_TEST, +&&L_OP_TESTSET, +&&L_OP_CALL, +&&L_OP_TAILCALL, +&&L_OP_RETURN, +&&L_OP_RETURN0, +&&L_OP_RETURN1, +&&L_OP_FORLOOP, +&&L_OP_FORPREP, +&&L_OP_TFORPREP, +&&L_OP_TFORCALL, +&&L_OP_TFORLOOP, +&&L_OP_SETLIST, +&&L_OP_CLOSURE, +&&L_OP_VARARG, +&&L_OP_VARARGPREP, +&&L_OP_EXTRAARG + +}; +#endif + startfunc: + trap = L->hookmask; + returning: /* trap already set */ + cl = clLvalue(s2v(ci->func.p)); + k = cl->p->k; + pc = ci->u.l.savedpc; + if (l_unlikely(trap)) { + if (pc == cl->p->code) { /* first instruction (not resuming)? */ + if (cl->p->is_vararg) + trap = 0; /* hooks will start after VARARGPREP instruction */ + else /* check 'call' hook */ + luaD_hookcall(L, ci); + } + ci->u.l.trap = 1; /* assume trap is on, for now */ + } + base = ci->func.p + 1; + /* main loop of interpreter */ + for (;;) { + Instruction i; /* instruction being executed */ + vmfetch(); + #if 0 + /* low-level line tracing for debugging Lua */ + printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); + #endif + lua_assert(base == ci->func.p + 1); + lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); + /* invalidate top for instructions not expecting it */ + lua_assert(isIT(i) || (cast_void(L->top.p = base), 1)); + vmdispatch (GET_OPCODE(i)) { + vmcase(OP_MOVE) { + StkId ra = RA(i); + setobjs2s(L, ra, RB(i)); + vmbreak; + } + vmcase(OP_LOADI) { + StkId ra = RA(i); + lua_Integer b = GETARG_sBx(i); + setivalue(s2v(ra), b); + vmbreak; + } + vmcase(OP_LOADF) { + StkId ra = RA(i); + int b = GETARG_sBx(i); + setfltvalue(s2v(ra), cast_num(b)); + vmbreak; + } + vmcase(OP_LOADK) { + StkId ra = RA(i); + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADKX) { + StkId ra = RA(i); + TValue *rb; + rb = k + GETARG_Ax(*pc); pc++; + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADFALSE) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LFALSESKIP) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + pc++; /* skip next instruction */ + vmbreak; + } + vmcase(OP_LOADTRUE) { + StkId ra = RA(i); + setbtvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LOADNIL) { + StkId ra = RA(i); + int b = GETARG_B(i); + do { + setnilvalue(s2v(ra++)); + } while (b--); + vmbreak; + } + vmcase(OP_GETUPVAL) { + StkId ra = RA(i); + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v.p); + vmbreak; + } + vmcase(OP_SETUPVAL) { + StkId ra = RA(i); + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v.p, s2v(ra)); + luaC_barrier(L, uv, s2v(ra)); + vmbreak; + } + vmcase(OP_GETTABUP) { + StkId ra = RA(i); + const TValue *slot; + TValue *upval = cl->upvals[GETARG_B(i)]->v.p; + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, upval, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETTABLE) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = vRC(i); + lua_Unsigned n; + if (ttisinteger(rc) /* fast track for integers? */ + ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot)) + : luaV_fastget(L, rb, rc, slot, luaH_get)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_GETI) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + int c = GETARG_C(i); + if (luaV_fastgeti(L, rb, c, slot)) { + setobj2s(L, ra, slot); + } + else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishget(L, rb, &key, ra, slot)); + } + vmbreak; + } + vmcase(OP_GETFIELD) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a string */ + if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_SETTABUP) { + const TValue *slot; + TValue *upval = cl->upvals[GETARG_A(i)]->v.p; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a string */ + if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, upval, slot, rc); + } + else + Protect(luaV_finishset(L, upval, rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETTABLE) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); /* key (table is in 'ra') */ + TValue *rc = RKC(i); /* value */ + lua_Unsigned n; + if (ttisinteger(rb) /* fast track for integers? */ + ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot)) + : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_SETI) { + StkId ra = RA(i); + const TValue *slot; + int c = GETARG_B(i); + TValue *rc = RKC(i); + if (luaV_fastgeti(L, s2v(ra), c, slot)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else { + TValue key; + setivalue(&key, c); + Protect(luaV_finishset(L, s2v(ra), &key, rc, slot)); + } + vmbreak; + } + vmcase(OP_SETFIELD) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a string */ + if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { + luaV_finishfastset(L, s2v(ra), slot, rc); + } + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + vmbreak; + } + vmcase(OP_NEWTABLE) { + StkId ra = RA(i); + int b = GETARG_B(i); /* log2(hash size) + 1 */ + int c = GETARG_C(i); /* array size */ + Table *t; + if (b > 0) + b = 1 << (b - 1); /* size is 2^(b - 1) */ + lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0)); + if (TESTARG_k(i)) /* non-zero extra argument? */ + c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ + pc++; /* skip extra argument */ + L->top.p = ra + 1; /* correct top in case of emergency GC */ + t = luaH_new(L); /* memory allocation */ + sethvalue2s(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, c, b); /* idem */ + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_SELF) { + StkId ra = RA(i); + const TValue *slot; + TValue *rb = vRB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rc); /* key must be a string */ + setobj2s(L, ra + 1, rb); + if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { + setobj2s(L, ra, slot); + } + else + Protect(luaV_finishget(L, rb, rc, ra, slot)); + vmbreak; + } + vmcase(OP_ADDI) { + op_arithI(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_ADDK) { + op_arithK(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUBK) { + op_arithK(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MULK) { + op_arithK(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MODK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POWK) { + op_arithfK(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIVK) { + op_arithfK(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIVK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BANDK) { + op_bitwiseK(L, l_band); + vmbreak; + } + vmcase(OP_BORK) { + op_bitwiseK(L, l_bor); + vmbreak; + } + vmcase(OP_BXORK) { + op_bitwiseK(L, l_bxor); + vmbreak; + } + vmcase(OP_SHRI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + } + vmbreak; + } + vmcase(OP_SHLI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); + } + vmbreak; + } + vmcase(OP_ADD) { + op_arith(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUB) { + op_arith(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MUL) { + op_arith(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MOD) { + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POW) { + op_arithf(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIV) { /* float division (always with floats) */ + op_arithf(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIV) { /* floor division */ + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BAND) { + op_bitwise(L, l_band); + vmbreak; + } + vmcase(OP_BOR) { + op_bitwise(L, l_bor); + vmbreak; + } + vmcase(OP_BXOR) { + op_bitwise(L, l_bxor); + vmbreak; + } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } + vmcase(OP_SHL) { + op_bitwise(L, luaV_shiftl); + vmbreak; + } + vmcase(OP_MMBIN) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *rb = vRB(i); + TMS tm = (TMS)GETARG_C(i); + StkId result = RA(pi); + lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR); + Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm)); + vmbreak; + } + vmcase(OP_MMBINI) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + int imm = GETARG_sB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_MMBINK) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *imm = KB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_UNM) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Number nb; + if (ttisinteger(rb)) { + lua_Integer ib = ivalue(rb); + setivalue(s2v(ra), intop(-, 0, ib)); + } + else if (tonumberns(rb, nb)) { + setfltvalue(s2v(ra), luai_numunm(L, nb)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); + vmbreak; + } + vmcase(OP_BNOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); + vmbreak; + } + vmcase(OP_NOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb)) + setbtvalue(s2v(ra)); + else + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LEN) { + StkId ra = RA(i); + Protect(luaV_objlen(L, ra, vRB(i))); + vmbreak; + } + vmcase(OP_CONCAT) { + StkId ra = RA(i); + int n = GETARG_B(i); /* number of elements to concatenate */ + L->top.p = ra + n; /* mark the end of concat operands */ + ProtectNT(luaV_concat(L, n)); + checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */ + vmbreak; + } + vmcase(OP_CLOSE) { + StkId ra = RA(i); + Protect(luaF_close(L, ra, LUA_OK, 1)); + vmbreak; + } + vmcase(OP_TBC) { + StkId ra = RA(i); + /* create new to-be-closed upvalue */ + halfProtect(luaF_newtbcupval(L, ra)); + vmbreak; + } + vmcase(OP_JMP) { + dojump(ci, i, 0); + vmbreak; + } + vmcase(OP_EQ) { + StkId ra = RA(i); + int cond; + TValue *rb = vRB(i); + Protect(cond = luaV_equalobj(L, s2v(ra), rb)); + docondjump(); + vmbreak; + } + vmcase(OP_LT) { + op_order(L, l_lti, LTnum, lessthanothers); + vmbreak; + } + vmcase(OP_LE) { + op_order(L, l_lei, LEnum, lessequalothers); + vmbreak; + } + vmcase(OP_EQK) { + StkId ra = RA(i); + TValue *rb = KB(i); + /* basic types do not use '__eq'; we can use raw equality */ + int cond = luaV_rawequalobj(s2v(ra), rb); + docondjump(); + vmbreak; + } + vmcase(OP_EQI) { + StkId ra = RA(i); + int cond; + int im = GETARG_sB(i); + if (ttisinteger(s2v(ra))) + cond = (ivalue(s2v(ra)) == im); + else if (ttisfloat(s2v(ra))) + cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im)); + else + cond = 0; /* other types cannot be equal to a number */ + docondjump(); + vmbreak; + } + vmcase(OP_LTI) { + op_orderI(L, l_lti, luai_numlt, 0, TM_LT); + vmbreak; + } + vmcase(OP_LEI) { + op_orderI(L, l_lei, luai_numle, 0, TM_LE); + vmbreak; + } + vmcase(OP_GTI) { + op_orderI(L, l_gti, luai_numgt, 1, TM_LT); + vmbreak; + } + vmcase(OP_GEI) { + op_orderI(L, l_gei, luai_numge, 1, TM_LE); + vmbreak; + } + vmcase(OP_TEST) { + StkId ra = RA(i); + int cond = !l_isfalse(s2v(ra)); + docondjump(); + vmbreak; + } + vmcase(OP_TESTSET) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb) == GETARG_k(i)) + pc++; + else { + setobj2s(L, ra, rb); + donextjump(ci); + } + vmbreak; + } + vmcase(OP_CALL) { + StkId ra = RA(i); + CallInfo *newci; + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) /* fixed number of arguments? */ + L->top.p = ra + b; /* top signals number of arguments */ + /* else previous instruction set top */ + savepc(L); /* in case of errors */ + if ((newci = luaD_precall(L, ra, nresults)) == NULL) + updatetrap(ci); /* C call; nothing else to be done */ + else { /* Lua call: run function in this same C frame */ + ci = newci; + goto startfunc; + } + vmbreak; + } + vmcase(OP_TAILCALL) { + StkId ra = RA(i); + int b = GETARG_B(i); /* number of arguments + 1 (function) */ + int n; /* number of results when calling a C function */ + int nparams1 = GETARG_C(i); + /* delta is virtual 'func' - real 'func' (vararg functions) */ + int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; + if (b != 0) + L->top.p = ra + b; + else /* previous instruction set top */ + b = cast_int(L->top.p - ra); + savepc(ci); /* several calls here can raise errors */ + if (TESTARG_k(i)) { + luaF_closeupval(L, base); /* close upvalues from current call */ + lua_assert(L->tbclist.p < base); /* no pending tbc variables */ + lua_assert(base == ci->func.p + 1); + } + if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ + goto startfunc; /* execute the callee */ + else { /* C function? */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ + luaD_poscall(L, ci, n); /* finish caller */ + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; /* caller returns after the tail call */ + } + } + vmcase(OP_RETURN) { + StkId ra = RA(i); + int n = GETARG_B(i) - 1; /* number of results */ + int nparams1 = GETARG_C(i); + if (n < 0) /* not fixed? */ + n = cast_int(L->top.p - ra); /* get what is available */ + savepc(ci); + if (TESTARG_k(i)) { /* may there be open upvalues? */ + ci->u2.nres = n; /* save number of returns */ + if (L->top.p < ci->top.p) + L->top.p = ci->top.p; + luaF_close(L, base, CLOSEKTOP, 1); + updatetrap(ci); + updatestack(ci); + } + if (nparams1) /* vararg function? */ + ci->func.p -= ci->u.l.nextraargs + nparams1; + L->top.p = ra + n; /* set call for 'luaD_poscall' */ + luaD_poscall(L, ci, n); + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; + } + vmcase(OP_RETURN0) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra; + savepc(ci); + luaD_poscall(L, ci, 0); /* no hurry... */ + trap = 1; + } + else { /* do the 'poscall' here */ + int nres; + L->ci = ci->previous; /* back to caller */ + L->top.p = base - 1; + for (nres = ci->nresults; l_unlikely(nres > 0); nres--) + setnilvalue(s2v(L->top.p++)); /* all results are nil */ + } + goto ret; + } + vmcase(OP_RETURN1) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra + 1; + savepc(ci); + luaD_poscall(L, ci, 1); /* no hurry... */ + trap = 1; + } + else { /* do the 'poscall' here */ + int nres = ci->nresults; + L->ci = ci->previous; /* back to caller */ + if (nres == 0) + L->top.p = base - 1; /* asked for no results */ + else { + StkId ra = RA(i); + setobjs2s(L, base - 1, ra); /* at least this result */ + L->top.p = base; + for (; l_unlikely(nres > 1); nres--) + setnilvalue(s2v(L->top.p++)); /* complete missing results */ + } + } + ret: /* return from a Lua function */ + if (ci->callstatus & CIST_FRESH) + return; /* end this frame */ + else { + ci = ci->previous; + goto returning; /* continue running caller in this frame */ + } + } + vmcase(OP_FORLOOP) { + StkId ra = RA(i); + if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); + if (count > 0) { /* still more iterations? */ + lua_Integer step = ivalue(s2v(ra + 2)); + lua_Integer idx = ivalue(s2v(ra)); /* internal index */ + chgivalue(s2v(ra + 1), count - 1); /* update counter */ + idx = intop(+, idx, step); /* add step to index */ + chgivalue(s2v(ra), idx); /* update internal index */ + setivalue(s2v(ra + 3), idx); /* and control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + } + else if (floatforloop(ra)) /* float loop */ + pc -= GETARG_Bx(i); /* jump back */ + updatetrap(ci); /* allows a signal to break the loop */ + vmbreak; + } + vmcase(OP_FORPREP) { + StkId ra = RA(i); + savestate(L, ci); /* in case of errors */ + if (forprep(L, ra)) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + vmbreak; + } + vmcase(OP_TFORPREP) { + StkId ra = RA(i); + /* create to-be-closed upvalue (if needed) */ + halfProtect(luaF_newtbcupval(L, ra + 3)); + pc += GETARG_Bx(i); + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); + goto l_tforcall; + } + vmcase(OP_TFORCALL) { + l_tforcall: { + StkId ra = RA(i); + /* 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the control variable, and 'ra + 3' has the + to-be-closed variable. The call will use the stack after + these values (starting at 'ra + 4') + */ + /* push function, state, and control variable */ + memcpy(ra + 4, ra, 3 * sizeof(*ra)); + L->top.p = ra + 4 + 3; + ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ + updatestack(ci); /* stack may have changed */ + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); + goto l_tforloop; + }} + vmcase(OP_TFORLOOP) { + l_tforloop: { + StkId ra = RA(i); + if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ + setobjs2s(L, ra + 2, ra + 4); /* save control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + vmbreak; + }} + vmcase(OP_SETLIST) { + StkId ra = RA(i); + int n = GETARG_B(i); + unsigned int last = GETARG_C(i); + Table *h = hvalue(s2v(ra)); + if (n == 0) + n = cast_int(L->top.p - ra) - 1; /* get up to the top */ + else + L->top.p = ci->top.p; /* correct top in case of emergency GC */ + last += n; + if (TESTARG_k(i)) { + last += GETARG_Ax(*pc) * (MAXARG_C + 1); + pc++; + } + if (last > luaH_realasize(h)) /* needs more space? */ + luaH_resizearray(L, h, last); /* preallocate it at once */ + for (; n > 0; n--) { + TValue *val = s2v(ra + n); + setobj2t(L, &h->array[last - 1], val); + last--; + luaC_barrierback(L, obj2gco(h), val); + } + vmbreak; + } + vmcase(OP_CLOSURE) { + StkId ra = RA(i); + Proto *p = cl->p->p[GETARG_Bx(i)]; + halfProtect(pushclosure(L, p, cl->upvals, base, ra)); + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_VARARG) { + StkId ra = RA(i); + int n = GETARG_C(i) - 1; /* required results */ + Protect(luaT_getvarargs(L, ci, ra, n)); + vmbreak; + } + vmcase(OP_VARARGPREP) { + ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); + if (l_unlikely(trap)) { /* previous "Protect" updated trap */ + luaD_hookcall(L, ci); + L->oldpc = 1; /* next opcode will be seen as a "new" line */ + } + updatebase(ci); /* function has new base after adjustment */ + vmbreak; + } + vmcase(OP_EXTRAARG) { + lua_assert(0); + vmbreak; + } + } + } +} + +/* }================================================================== */ +/* +** $Id: lapi.c $ +** Lua API +** See Copyright Notice in lua.h +*/ + +#define lapi_c +#define LUA_CORE + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lapi.h"*/ +/*#include "ldebug.h"*/ +/*#include "ldo.h"*/ +/*#include "lfunc.h"*/ +/*#include "lgc.h"*/ +/*#include "lmem.h"*/ +/*#include "lobject.h"*/ +/*#include "lstate.h"*/ +/*#include "lstring.h"*/ +/*#include "ltable.h"*/ +/*#include "ltm.h"*/ +/*#include "lundump.h"*/ +/*#include "lvm.h"*/ + + + +const char lua_ident[] = + "$LuaVersion: " LUA_COPYRIGHT " $" + "$LuaAuthors: " LUA_AUTHORS " $"; + + + +/* +** Test for a valid index (one that is not the 'nilvalue'). +** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. +** However, it covers the most common cases in a faster way. +*/ +#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) + + +/* test for pseudo index */ +#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) + +/* test for upvalue */ +#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) + + +/* +** Convert an acceptable index to a pointer to its respective value. +** Non-valid indices return the special nil value 'G(L)->nilvalue'. +*/ +static TValue *index2value (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func.p + idx; + api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index"); + if (o >= L->top.p) return &G(L)->nilvalue; + else return s2v(o); + } + else if (!ispseudo(idx)) { /* negative index */ + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + return s2v(L->top.p + idx); + } + else if (idx == LUA_REGISTRYINDEX) + return &G(L)->l_registry; + else { /* upvalues */ + idx = LUA_REGISTRYINDEX - idx; + api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); + if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */ + CClosure *func = clCvalue(s2v(ci->func.p)); + return (idx <= func->nupvalues) ? &func->upvalue[idx-1] + : &G(L)->nilvalue; + } + else { /* light C function or Lua function (through a hook)?) */ + api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function"); + return &G(L)->nilvalue; /* no upvalues */ + } + } +} + + + +/* +** Convert a valid actual index (not a pseudo-index) to its address. +*/ +l_sinline StkId index2stack (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func.p + idx; + api_check(L, o < L->top.p, "invalid index"); + return o; + } + else { /* non-positive index */ + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + api_check(L, !ispseudo(idx), "invalid index"); + return L->top.p + idx; + } +} + + +LUA_API int lua_checkstack (lua_State *L, int n) { + int res; + CallInfo *ci; + lua_lock(L); + ci = L->ci; + api_check(L, n >= 0, "negative 'n'"); + if (L->stack_last.p - L->top.p > n) /* stack large enough? */ + res = 1; /* yes; check is OK */ + else /* need to grow stack */ + res = luaD_growstack(L, n, 0); + if (res && ci->top.p < L->top.p + n) + ci->top.p = L->top.p + n; /* adjust frame top */ + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to), "moving among independent states"); + api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); + from->top.p -= n; + for (i = 0; i < n; i++) { + setobjs2s(to, to->top.p, from->top.p + i); + to->top.p++; /* stack already checked by previous 'api_check' */ + } + lua_unlock(to); +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_Number lua_version (lua_State *L) { + UNUSED(L); + return LUA_VERSION_NUM; +} + + + +/* +** basic stack manipulation +*/ + + +/* +** convert an acceptable stack index into an absolute index +*/ +LUA_API int lua_absindex (lua_State *L, int idx) { + return (idx > 0 || ispseudo(idx)) + ? idx + : cast_int(L->top.p - L->ci->func.p) + idx; +} + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top.p - (L->ci->func.p + 1)); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + CallInfo *ci; + StkId func, newtop; + ptrdiff_t diff; /* difference for new top */ + lua_lock(L); + ci = L->ci; + func = ci->func.p; + if (idx >= 0) { + api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top.p; + for (; diff > 0; diff--) + setnilvalue(s2v(L->top.p++)); /* clear new slots */ + } + else { + api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); + diff = idx + 1; /* will "subtract" index (as it is negative) */ + } + api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); + newtop = L->top.p + diff; + if (diff < 0 && L->tbclist.p >= newtop) { + lua_assert(hastocloseCfunc(ci->nresults)); + newtop = luaF_close(L, newtop, CLOSEKTOP, 0); + } + L->top.p = newtop; /* correct top only after closing any upvalue */ + lua_unlock(L); +} + + +LUA_API void lua_closeslot (lua_State *L, int idx) { + StkId level; + lua_lock(L); + level = index2stack(L, idx); + api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, + "no variable to close at given level"); + level = luaF_close(L, level, CLOSEKTOP, 0); + setnilvalue(s2v(level)); + lua_unlock(L); +} + + +/* +** Reverse the stack segment from 'from' to 'to' +** (auxiliary to 'lua_rotate') +** Note that we move(copy) only the value inside the stack. +** (We do not move additional fields that may exist.) +*/ +l_sinline void reverse (lua_State *L, StkId from, StkId to) { + for (; from < to; from++, to--) { + TValue temp; + setobj(L, &temp, s2v(from)); + setobjs2s(L, from, to); + setobj2s(L, to, &temp); + } +} + + +/* +** Let x = AB, where A is a prefix of length 'n'. Then, +** rotate x n == BA. But BA == (A^r . B^r)^r. +*/ +LUA_API void lua_rotate (lua_State *L, int idx, int n) { + StkId p, t, m; + lua_lock(L); + t = L->top.p - 1; /* end of stack segment being rotated */ + p = index2stack(L, idx); /* start of segment */ + api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); + m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ + reverse(L, p, m); /* reverse the prefix with length 'n' */ + reverse(L, m + 1, t); /* reverse the suffix */ + reverse(L, p, t); /* reverse the entire segment */ + lua_unlock(L); +} + + +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { + TValue *fr, *to; + lua_lock(L); + fr = index2value(L, fromidx); + to = index2value(L, toidx); + api_check(L, isvalid(L, to), "invalid index"); + setobj(L, to, fr); + if (isupvalue(toidx)) /* function upvalue? */ + luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr); + /* LUA_REGISTRYINDEX does not need gc barrier + (collector revisits it before finishing collection) */ + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top.p, index2value(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (isvalid(L, o) ? ttype(o) : LUA_TNONE); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type"); + return ttypename(t); +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttislcf(o) || (ttisCclosure(o))); +} + + +LUA_API int lua_isinteger (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return ttisinteger(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + lua_Number n; + const TValue *o = index2value(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisstring(o) || cvt2str(o)); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisfulluserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + const TValue *o1 = index2value(L, index1); + const TValue *o2 = index2value(L, index2); + return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; +} + + +LUA_API void lua_arith (lua_State *L, int op) { + lua_lock(L); + if (op != LUA_OPUNM && op != LUA_OPBNOT) + api_checknelems(L, 2); /* all other operations expect two operands */ + else { /* for unary operations, add fake 2nd operand */ + api_checknelems(L, 1); + setobjs2s(L, L->top.p, L->top.p - 1); + api_incr_top(L); + } + /* first operand at top - 2, second at top - 1; result go to top - 2 */ + luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); + L->top.p--; /* remove second operand */ + lua_unlock(L); +} + + +LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { + const TValue *o1; + const TValue *o2; + int i = 0; + lua_lock(L); /* may call tag method */ + o1 = index2value(L, index1); + o2 = index2value(L, index2); + if (isvalid(L, o1) && isvalid(L, o2)) { + switch (op) { + case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; + case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; + case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; + default: api_check(L, 0, "invalid option"); + } + } + lua_unlock(L); + return i; +} + + +LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { + size_t sz = luaO_str2num(s, s2v(L->top.p)); + if (sz != 0) + api_incr_top(L); + return sz; +} + + +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { + lua_Number n = 0; + const TValue *o = index2value(L, idx); + int isnum = tonumber(o, &n); + if (pisnum) + *pisnum = isnum; + return n; +} + + +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { + lua_Integer res = 0; + const TValue *o = index2value(L, idx); + int isnum = tointeger(o, &res); + if (pisnum) + *pisnum = isnum; + return res; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + TValue *o; + lua_lock(L); + o = index2value(L, idx); + if (!ttisstring(o)) { + if (!cvt2str(o)) { /* not convertible? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaO_tostring(L, o); + luaC_checkGC(L); + o = index2value(L, idx); /* previous call may reallocate the stack */ + } + if (len != NULL) + *len = vslen(o); + lua_unlock(L); + return svalue(o); +} + + +LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VSHRSTR: return tsvalue(o)->shrlen; + case LUA_VLNGSTR: return tsvalue(o)->u.lnglen; + case LUA_VUSERDATA: return uvalue(o)->len; + case LUA_VTABLE: return luaH_getn(hvalue(o)); + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + if (ttislcf(o)) return fvalue(o); + else if (ttisCclosure(o)) + return clCvalue(o)->f; + else return NULL; /* not a C function */ +} + + +l_sinline void *touserdata (const TValue *o) { + switch (ttype(o)) { + case LUA_TUSERDATA: return getudatamem(uvalue(o)); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return touserdata(o); +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +/* +** Returns a pointer to the internal representation of an object. +** Note that ANSI C does not allow the conversion of a pointer to +** function to a 'void*', so the conversion here goes through +** a 'size_t'. (As the returned pointer is only informative, this +** conversion should not be a problem.) +*/ +LUA_API const void *lua_topointer (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); + case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA: + return touserdata(o); + default: { + if (iscollectable(o)) + return gcvalue(o); + else + return NULL; + } + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(s2v(L->top.p)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setfltvalue(s2v(L->top.p), n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setivalue(s2v(L->top.p), n); + api_incr_top(L); + lua_unlock(L); +} + + +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ +LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { + TString *ts; + lua_lock(L); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); + setsvalue2s(L, L->top.p, ts); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getstr(ts); +} + + +LUA_API const char *lua_pushstring (lua_State *L, const char *s) { + lua_lock(L); + if (s == NULL) + setnilvalue(s2v(L->top.p)); + else { + TString *ts; + ts = luaS_new(L, s); + setsvalue2s(L, L->top.p, ts); + s = getstr(ts); /* internal copy's address */ + } + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return s; +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + ret = luaO_pushvfstring(L, fmt, argp); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + lua_lock(L); + if (n == 0) { + setfvalue(s2v(L->top.p), fn); + api_incr_top(L); + } + else { + CClosure *cl; + api_checknelems(L, n); + api_check(L, n <= MAXUPVAL, "upvalue index too large"); + cl = luaF_newCclosure(L, n); + cl->f = fn; + L->top.p -= n; + while (n--) { + setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); + /* does not need barrier because closure is white */ + lua_assert(iswhite(cl)); + } + setclCvalue(L, s2v(L->top.p), cl); + api_incr_top(L); + luaC_checkGC(L); + } + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + if (b) + setbtvalue(s2v(L->top.p)); + else + setbfvalue(s2v(L->top.p)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(s2v(L->top.p), p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, s2v(L->top.p), L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + setobj2s(L, L->top.p, slot); + api_incr_top(L); + } + else { + setsvalue2s(L, L->top.p, str); + api_incr_top(L); + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + } + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); +} + + +/* +** Get the global table in the registry. Since all predefined +** indices in the registry were inserted right when the registry +** was created and never removed, they must always be in the array +** part of the registry. +*/ +#define getGtable(L) \ + (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1]) + + +LUA_API int lua_getglobal (lua_State *L, const char *name) { + const TValue *G; + lua_lock(L); + G = getGtable(L); + return auxgetstr(L, G, name); +} + + +LUA_API int lua_gettable (lua_State *L, int idx) { + const TValue *slot; + TValue *t; + lua_lock(L); + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { + setobj2s(L, L->top.p - 1, slot); + } + else + luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); +} + + +LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { + lua_lock(L); + return auxgetstr(L, index2value(L, idx), k); +} + + +LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; + lua_lock(L); + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + setobj2s(L, L->top.p, slot); + } + else { + TValue aux; + setivalue(&aux, n); + luaV_finishget(L, t, &aux, L->top.p, slot); + } + api_incr_top(L); + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); +} + + +l_sinline int finishrawget (lua_State *L, const TValue *val) { + if (isempty(val)) /* avoid copying empty items to the stack */ + setnilvalue(s2v(L->top.p)); + else + setobj2s(L, L->top.p, val); + api_incr_top(L); + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); +} + + +static Table *gettable (lua_State *L, int idx) { + TValue *t = index2value(L, idx); + api_check(L, ttistable(t), "table expected"); + return hvalue(t); +} + + +LUA_API int lua_rawget (lua_State *L, int idx) { + Table *t; + const TValue *val; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + val = luaH_get(t, s2v(L->top.p - 1)); + L->top.p--; /* remove key */ + return finishrawget(L, val); +} + + +LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + t = gettable(L, idx); + return finishrawget(L, luaH_getint(t, n)); +} + + +LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { + Table *t; + TValue k; + lua_lock(L); + t = gettable(L, idx); + setpvalue(&k, cast_voidp(p)); + return finishrawget(L, luaH_get(t, &k)); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + Table *t; + lua_lock(L); + t = luaH_new(L); + sethvalue2s(L, L->top.p, t); + api_incr_top(L); + if (narray > 0 || nrec > 0) + luaH_resize(L, t, narray, nrec); + luaC_checkGC(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt; + int res = 0; + lua_lock(L); + obj = index2value(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt != NULL) { + sethvalue2s(L, L->top.p, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int t; + lua_lock(L); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (n <= 0 || n > uvalue(o)->nuvalue) { + setnilvalue(s2v(L->top.p)); + t = LUA_TNONE; + } + else { + setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top.p)); + } + api_incr_top(L); + lua_unlock(L); + return t; +} + + +/* +** set functions (stack -> Lua) +*/ + +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *slot; + TString *str = luaS_new(L, k); + api_checknelems(L, 1); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + L->top.p--; /* pop value */ + } + else { + setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + L->top.p -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} + + +LUA_API void lua_setglobal (lua_State *L, const char *name) { + const TValue *G; + lua_lock(L); /* unlock done in 'auxsetstr' */ + G = getGtable(L); + auxsetstr(L, G, name); +} + + +LUA_API void lua_settable (lua_State *L, int idx) { + TValue *t; + const TValue *slot; + lua_lock(L); + api_checknelems(L, 2); + t = index2value(L, idx); + if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + } + else + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + L->top.p -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2value(L, idx), k); +} + + +LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + const TValue *slot; + lua_lock(L); + api_checknelems(L, 1); + t = index2value(L, idx); + if (luaV_fastgeti(L, t, n, slot)) { + luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + } + else { + TValue aux; + setivalue(&aux, n); + luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); + } + L->top.p--; /* pop value */ + lua_unlock(L); +} + + +static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { + Table *t; + lua_lock(L); + api_checknelems(L, n); + t = gettable(L, idx); + luaH_set(L, t, key, s2v(L->top.p - 1)); + invalidateTMcache(t); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p -= n; + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + aux_rawset(L, idx, s2v(L->top.p - 2), 2); +} + + +LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { + TValue k; + setpvalue(&k, cast_voidp(p)); + aux_rawset(L, idx, &k, 1); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + luaH_setint(L, t, n, s2v(L->top.p - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2value(L, objindex); + if (ttisnil(s2v(L->top.p - 1))) + mt = NULL; + else { + api_check(L, ttistable(s2v(L->top.p - 1)), "table expected"); + mt = hvalue(s2v(L->top.p - 1)); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, gcvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, uvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top.p--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int res; + lua_lock(L); + api_checknelems(L, 1); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) + res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ + else { + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1)); + res = 1; + } + L->top.p--; + lua_unlock(L); + return res; +} + + +/* +** 'load' and 'call' functions (run Lua code) +*/ + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET \ + || (L->ci->top.p - L->top.p >= (nr) - (na)), \ + "results from function overflow current stack size") + + +LUA_API void lua_callk (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k) { + StkId func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + func = L->top.p - (nargs+1); + if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ + L->ci->u.c.k = k; /* save continuation */ + L->ci->u.c.ctx = ctx; /* save context */ + luaD_call(L, func, nresults); /* do the call */ + } + else /* no continuation or no yieldable */ + luaD_callnoyield(L, func, nresults); /* just do the call */ + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to 'f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_callnoyield(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checknelems(L, nargs+1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2stack(L, errfunc); + api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); + func = savestack(L, o); + } + c.func = L->top.p - (nargs+1); /* function to be called */ + if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ + c.nresults = nresults; /* do a 'conventional' protected call */ + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + } + else { /* prepare continuation (call is already protected by 'resume') */ + CallInfo *ci = L->ci; + ci->u.c.k = k; /* save continuation */ + ci->u.c.ctx = ctx; /* save context */ + /* save information for error recovery */ + ci->u2.funcidx = cast_int(savestack(L, c.func)); + ci->u.c.old_errfunc = L->errfunc; + L->errfunc = func; + setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ + ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ + luaD_call(L, c.func, nresults); /* do the call */ + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + status = LUA_OK; /* if it is here, there were no errors */ + } + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname, const char *mode) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname, mode); + if (status == LUA_OK) { /* no errors? */ + LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ + if (f->nupvalues >= 1) { /* does it have an upvalue? */ + /* get global table from registry */ + const TValue *gt = getGtable(L); + /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ + setobj(L, f->upvals[0]->v.p, gt); + luaC_barrier(L, f->upvals[0], gt); + } + } + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = s2v(L->top.p - 1); + if (isLfunction(o)) + status = luaU_dump(L, getproto(o), writer, data, strip); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ +LUA_API int lua_gc (lua_State *L, int what, ...) { + va_list argp; + int res = 0; + global_State *g = G(L); + if (g->gcstp & GCSTPGC) /* internal stop? */ + return -1; /* all options are invalid when stopped */ + lua_lock(L); + va_start(argp, what); + switch (what) { + case LUA_GCSTOP: { + g->gcstp = GCSTPUSR; /* stopped by the user */ + break; + } + case LUA_GCRESTART: { + luaE_setdebt(g, 0); + g->gcstp = 0; /* (GCSTPGC must be already zero here) */ + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L, 0); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(gettotalbytes(g) >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(gettotalbytes(g) & 0x3ff); + break; + } + case LUA_GCSTEP: { + int data = va_arg(argp, int); + l_mem debt = 1; /* =1 to signal that it did an actual step */ + lu_byte oldstp = g->gcstp; + g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ + if (data == 0) { + luaE_setdebt(g, 0); /* do a basic step */ + luaC_step(L); + } + else { /* add 'data' to total debt */ + debt = cast(l_mem, data) * 1024 + g->GCdebt; + luaE_setdebt(g, debt); + luaC_checkGC(L); + } + g->gcstp = oldstp; /* restore previous state */ + if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + break; + } + case LUA_GCSETPAUSE: { + int data = va_arg(argp, int); + res = getgcparam(g->gcpause); + setgcparam(g->gcpause, data); + break; + } + case LUA_GCSETSTEPMUL: { + int data = va_arg(argp, int); + res = getgcparam(g->gcstepmul); + setgcparam(g->gcstepmul, data); + break; + } + case LUA_GCISRUNNING: { + res = gcrunning(g); + break; + } + case LUA_GCGEN: { + int minormul = va_arg(argp, int); + int majormul = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (minormul != 0) + g->genminormul = minormul; + if (majormul != 0) + setgcparam(g->genmajormul, majormul); + luaC_changemode(L, KGC_GEN); + break; + } + case LUA_GCINC: { + int pause = va_arg(argp, int); + int stepmul = va_arg(argp, int); + int stepsize = va_arg(argp, int); + res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; + if (pause != 0) + setgcparam(g->gcpause, pause); + if (stepmul != 0) + setgcparam(g->gcstepmul, stepmul); + if (stepsize != 0) + g->gcstepsize = stepsize; + luaC_changemode(L, KGC_INC); + break; + } + default: res = -1; /* invalid option */ + } + va_end(argp); + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + TValue *errobj; + lua_lock(L); + errobj = s2v(L->top.p - 1); + api_checknelems(L, 1); + /* error object is the memory error message? */ + if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) + luaM_error(L); /* raise a memory error */ + else + luaG_errormsg(L); /* raise a regular error */ + /* code unreachable; will unlock when control actually leaves the kernel */ + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + Table *t; + int more; + lua_lock(L); + api_checknelems(L, 1); + t = gettable(L, idx); + more = luaH_next(L, t, L->top.p - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top.p -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_toclose (lua_State *L, int idx) { + int nresults; + StkId o; + lua_lock(L); + o = index2stack(L, idx); + nresults = L->ci->nresults; + api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); + luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ + if (!hastocloseCfunc(nresults)) /* function not marked yet? */ + L->ci->nresults = codeNresults(nresults); /* mark it */ + lua_assert(hastocloseCfunc(L->ci->nresults)); + lua_unlock(L); +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n > 0) + luaV_concat(L, n); + else { /* nothing to concatenate */ + setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ + api_incr_top(L); + } + luaC_checkGC(L); + lua_unlock(L); +} + + +LUA_API void lua_len (lua_State *L, int idx) { + TValue *t; + lua_lock(L); + t = index2value(L, idx); + luaV_objlen(L, L->top.p, t); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { + lua_lock(L); + G(L)->ud_warn = ud; + G(L)->warnf = f; + lua_unlock(L); +} + + +void lua_warning (lua_State *L, const char *msg, int tocont) { + lua_lock(L); + luaE_warning(L, msg, tocont); + lua_unlock(L); +} + + + +LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { + Udata *u; + lua_lock(L); + api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); + u = luaS_newudata(L, size, nuvalue); + setuvalue(L, s2v(L->top.p), u); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getudatamem(u); +} + + + +static const char *aux_upvalue (TValue *fi, int n, TValue **val, + GCObject **owner) { + switch (ttypetag(fi)) { + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) + return NULL; /* 'n' not in [1, f->nupvalues] */ + *val = &f->upvalue[n-1]; + if (owner) *owner = obj2gco(f); + return ""; + } + case LUA_VLCL: { /* Lua closure */ + LClosure *f = clLvalue(fi); + TString *name; + Proto *p = f->p; + if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) + return NULL; /* 'n' not in [1, p->sizeupvalues] */ + *val = f->upvals[n-1]->v.p; + if (owner) *owner = obj2gco(f->upvals[n - 1]); + name = p->upvalues[n-1].name; + return (name == NULL) ? "(no name)" : getstr(name); + } + default: return NULL; /* not a closure */ + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + lua_lock(L); + name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); + if (name) { + setobj2s(L, L->top.p, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + GCObject *owner = NULL; /* to avoid warnings */ + TValue *fi; + lua_lock(L); + fi = index2value(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val, &owner); + if (name) { + L->top.p--; + setobj(L, val, s2v(L->top.p)); + luaC_barrier(L, owner, val); + } + lua_unlock(L); + return name; +} + + +static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { + static const UpVal *const nullup = NULL; + LClosure *f; + TValue *fi = index2value(L, fidx); + api_check(L, ttisLclosure(fi), "Lua function expected"); + f = clLvalue(fi); + if (pf) *pf = f; + if (1 <= n && n <= f->p->sizeupvalues) + return &f->upvals[n - 1]; /* get its upvalue pointer */ + else + return (UpVal**)&nullup; +} + + +LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { + TValue *fi = index2value(L, fidx); + switch (ttypetag(fi)) { + case LUA_VLCL: { /* lua closure */ + return *getupvalref(L, fidx, n, NULL); + } + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (1 <= n && n <= f->nupvalues) + return &f->upvalue[n - 1]; + /* else */ + } /* FALLTHROUGH */ + case LUA_VLCF: + return NULL; /* light C functions have no upvalues */ + default: { + api_check(L, 0, "function expected"); + return NULL; + } + } +} + + +LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, + int fidx2, int n2) { + LClosure *f1; + UpVal **up1 = getupvalref(L, fidx1, n1, &f1); + UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index"); + *up1 = *up2; + luaC_objbarrier(L, f1, *up1); +} + + +/* +** $Id: lauxlib.c $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + +#define lauxlib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include + + +/* +** This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ + + +#if !defined(MAX_SIZET) +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) +#endif + + +/* +** {====================================================== +** Traceback +** ======================================================= +*/ + + +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ + + + +/* +** Search for 'objidx' in table at index -1. ('objidx' must be an +** absolute index.) Return 1 + string at top if it found a good name. +*/ +static int findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (findfield(L, objidx, level - 1)) { /* try recursively */ + /* stack: lib_name, lib_table, field_name (top) */ + lua_pushliteral(L, "."); /* place '.' between the two names */ + lua_replace(L, -3); /* (in the slot occupied by table) */ + lua_concat(L, 3); /* lib_name.field_name */ + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + + +/* +** Search for a name for a function in all loaded modules +*/ +static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + if (findfield(L, top + 1, 2)) { + const char *name = lua_tostring(L, -1); + if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ + lua_pushstring(L, name + 3); /* push name without prefix */ + lua_remove(L, -2); /* remove original name */ + } + lua_copy(L, -1, top + 1); /* copy name to proper place */ + lua_settop(L, top + 1); /* remove table "loaded" and name copy */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + + +static void pushfuncname (lua_State *L, lua_Debug *ar) { + if (pushglobalfuncname(L, ar)) { /* try first a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else if (*ar->namewhat != '\0') /* is there a name from code? */ + lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what != 'C') /* for Lua functions, use */ + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); + else /* nothing left... */ + lua_pushliteral(L, "?"); +} + + +static int lastlevel (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + + +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + luaL_Buffer b; + lua_Debug ar; + int last = lastlevel(L1); + int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + luaL_buffinit(L, &b); + if (msg) { + luaL_addstring(&b, msg); + luaL_addchar(&b, '\n'); + } + luaL_addstring(&b, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (limit2show-- == 0) { /* too many levels? */ + int n = last - level - LEVELS2 + 1; /* number of levels to skip */ + lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); + luaL_addvalue(&b); /* add warning about skip */ + level += n; /* and skip to last levels */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + if (ar.currentline <= 0) + lua_pushfstring(L, "\n\t%s: in ", ar.short_src); + else + lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); + luaL_addvalue(&b); + pushfuncname(L, &ar); + luaL_addvalue(&b); + if (ar.istailcall) + luaL_addstring(&b, "\n\t(...tail calls...)"); + } + } + luaL_pushresult(&b); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + +LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + arg--; /* do not count 'self' */ + if (arg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling '%s' on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; + return luaL_error(L, "bad argument #%d to '%s' (%s)", + arg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); +} + + +static void tag_error (lua_State *L, int arg, int tag) { + luaL_typeerror(L, arg, lua_typename(L, tag)); +} + + +/* +** The use of 'lua_pushfstring' ensures this function does not +** need reserved stack space when called. +*/ +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushfstring(L, ""); /* else, no information available... */ +} + + +/* +** Again, the use of 'lua_pushvfstring' ensures this function does +** not need reserved stack space when called. (At worst, it generates +** an error with "stack overflow" instead of the given message.) +*/ +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + +LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { + int en = errno; /* calls to Lua API may change this value */ + if (stat) { + lua_pushboolean(L, 1); + return 1; + } + else { + luaL_pushfail(L); + if (fname) + lua_pushfstring(L, "%s: %s", fname, strerror(en)); + else + lua_pushstring(L, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +#if !defined(l_inspectstat) /* { */ + +#if defined(LUA_USE_POSIX) + +#include + +/* +** use appropriate macros to interpret 'pclose' return status +*/ +#define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } + +#else + +#define l_inspectstat(stat,what) /* no op */ + +#endif + +#endif /* } */ + + +LUALIB_API int luaL_execresult (lua_State *L, int stat) { + if (stat != 0 && errno != 0) /* error with an 'errno'? */ + return luaL_fileresult(L, 0, NULL); + else { + const char *what = "exit"; /* type of termination */ + l_inspectstat(stat, what); /* interpret result */ + if (*what == 'e' && stat == 0) /* successful termination? */ + lua_pushboolean(L, 1); + else + luaL_pushfail(L); + lua_pushstring(L, what); + lua_pushinteger(L, stat); + return 3; /* return true/fail,what,code */ + } +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Userdata's metatable manipulation +** ======================================================= +*/ + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_createtable(L, 0, 2); /* create metatable */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = luaL_testudata(L, ud, tname); + luaL_argexpected(L, p != NULL, ud, tname); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Argument check functions +** ======================================================= +*/ + +LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, arg, def) : + luaL_checkstring(L, arg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, arg, + lua_pushfstring(L, "invalid option '%s'", name)); +} + + +/* +** Ensures the stack has at least 'space' extra slots, raising an error +** if it cannot fulfill the request. (The error handling needs a few +** extra slots to format the error message. In case of an error without +** this extra space, Lua will generate the same 'stack overflow' error, +** but without 'msg'.) +*/ +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { + if (l_unlikely(!lua_checkstack(L, space))) { + if (msg) + luaL_error(L, "stack overflow (%s)", msg); + else + luaL_error(L, "stack overflow"); + } +} + + +LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { + if (l_unlikely(lua_type(L, arg) != t)) + tag_error(L, arg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int arg) { + if (l_unlikely(lua_type(L, arg) == LUA_TNONE)) + luaL_argerror(L, arg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { + const char *s = lua_tolstring(L, arg, len); + if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, arg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, arg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { + int isnum; + lua_Number d = lua_tonumberx(L, arg, &isnum); + if (l_unlikely(!isnum)) + tag_error(L, arg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, arg, def); +} + + +static void interror (lua_State *L, int arg) { + if (lua_isnumber(L, arg)) + luaL_argerror(L, arg, "number has no integer representation"); + else + tag_error(L, arg, LUA_TNUMBER); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { + int isnum; + lua_Integer d = lua_tointegerx(L, arg, &isnum); + if (l_unlikely(!isnum)) { + interror(L, arg); + } + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, arg, def); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +static void *resizebox (lua_State *L, int idx, size_t newsize) { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + UBox *box = (UBox *)lua_touserdata(L, idx); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + box->box = temp; + box->bsize = newsize; + return temp; +} + + +static int boxgc (lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static const luaL_Reg boxmt[] = { /* box metamethods */ + {"__gc", boxgc}, + {"__close", boxgc}, + {NULL, NULL} +}; + + +static void newbox (lua_State *L) { + UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ + luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ + lua_setmetatable(L, -2); +} + + +/* +** check whether buffer is using a userdata on the stack as a temporary +** buffer +*/ +#define buffonstack(B) ((B)->b != (B)->init.b) + + +/* +** Whenever buffer is accessed, slot 'idx' must either be a box (which +** cannot be NULL) or it is a placeholder for the buffer. +*/ +#define checkbufferlevel(B,idx) \ + lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ + : lua_touserdata(B->L, idx) == (void*)B) + + +/* +** Compute new size for buffer 'B', enough to accommodate extra 'sz' +** bytes. (The test for "not big enough" also gets the case when the +** computation of 'newsize' overflows.) +*/ +static size_t newbuffsize (luaL_Buffer *B, size_t sz) { + size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ + if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ + return luaL_error(B->L, "buffer too large"); + if (newsize < B->n + sz) /* not big enough? */ + newsize = B->n + sz; + return newsize; +} + + +/* +** Returns a pointer to a free area with at least 'sz' bytes in buffer +** 'B'. 'boxidx' is the relative position in the stack where is the +** buffer's box or its placeholder. +*/ +static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { + checkbufferlevel(B, boxidx); + if (B->size - B->n >= sz) /* enough space? */ + return B->b + B->n; + else { + lua_State *L = B->L; + char *newbuff; + size_t newsize = newbuffsize(B, sz); + /* create larger buffer */ + if (buffonstack(B)) /* buffer already has a box? */ + newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ + else { /* no box yet */ + lua_remove(L, boxidx); /* remove placeholder */ + newbox(L); /* create a new box */ + lua_insert(L, boxidx); /* move box to its intended position */ + lua_toclose(L, boxidx); + newbuff = (char *)resizebox(L, boxidx, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } + B->b = newbuff; + B->size = newsize; + return newbuff + B->n; + } +} + +/* +** returns a pointer to a free area with at least 'sz' bytes +*/ +LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + return prepbuffsize(B, sz, -1); +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = prepbuffsize(B, l, -1); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + checkbufferlevel(B, -1); + lua_pushlstring(L, B->b, B->n); + if (buffonstack(B)) + lua_closeslot(L, -2); /* close the box */ + lua_remove(L, -2); /* remove box or placeholder from the stack */ +} + + +LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luaL_pushresult(B); +} + + +/* +** 'luaL_addvalue' is the only function in the Buffer system where the +** box (if existent) is not on the top of the stack. So, instead of +** calling 'luaL_addlstring', it replicates the code using -2 as the +** last argument to 'prepbuffsize', signaling that the box is (or will +** be) below the string being added to the buffer. (Box creation can +** trigger an emergency GC, so we should not remove the string from the +** stack before we have the space guaranteed.) +*/ +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t len; + const char *s = lua_tolstring(L, -1, &len); + char *b = prepbuffsize(B, len, -2); + memcpy(b, s, len * sizeof(char)); + luaL_addsize(B, len); + lua_pop(L, 1); /* pop string */ +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->b = B->init.b; + B->n = 0; + B->size = LUAL_BUFFERSIZE; + lua_pushlightuserdata(L, (void*)B); /* push placeholder */ +} + + +LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { + luaL_buffinit(L, B); + return prepbuffsize(B, sz, -1); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Reference system +** ======================================================= +*/ + +/* index of free-list header (after the predefined values) */ +#define freelist (LUA_RIDX_LAST + 1) + +/* +** The previously freed references form a linked list: +** t[freelist] is the index of a first free index, or zero if list is +** empty; t[t[freelist]] is the index of the second element; etc. +*/ +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* 'nil' has a unique fixed reference */ + } + t = lua_absindex(L, t); + if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ + ref = 0; /* list is empty */ + lua_pushinteger(L, 0); /* initialize as an empty list */ + lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ + } + else { /* already initialized */ + lua_assert(lua_isinteger(L, -1)); + ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + } + lua_pop(L, 1); /* remove element from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ + } + else /* no free elements */ + ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = lua_absindex(L, t); + lua_rawgeti(L, t, freelist); + lua_assert(lua_isinteger(L, -1)); + lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, freelist); /* t[freelist] = ref */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + int n; /* number of pre-read characters */ + FILE *f; /* file being read */ + char buff[BUFSIZ]; /* area for reading file */ +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; /* not used */ + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } + else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = strerror(errno); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +/* +** Skip an optional BOM at the start of a stream. If there is an +** incomplete BOM (the first character is correct but the rest is +** not), returns the first character anyway to force an error +** (as no chunk can start with 0xEF). +*/ +static int skipBOM (FILE *f) { + int c = getc(f); /* read first character */ + if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */ + return getc(f); /* ignore BOM and return next char */ + else /* no (valid) BOM */ + return c; /* return first character */ +} + + +/* +** reads the first character of file 'f' and skips an optional BOM mark +** in its beginning plus its first line if it starts with '#'. Returns +** true if it skipped the first line. In any case, '*cp' has the +** first "valid" character of the file (after the optional BOM and +** a first-line comment). +*/ +static int skipcomment (FILE *f, int *cp) { + int c = *cp = skipBOM(f); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(f); + } while (c != EOF && c != '\n'); + *cp = getc(f); /* next character after comment, if present */ + return 1; /* there was a comment */ + } + else return 0; /* no comment */ +} + + +LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, + const char *mode) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + lf.n = 0; + if (skipcomment(lf.f, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */ + if (c == LUA_SIGNATURE[0]) { /* binary file? */ + lf.n = 0; /* remove possible newline */ + if (filename) { /* "real" file? */ + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(lf.f, &c); /* re-read initial portion */ + } + } + if (c != EOF) + lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; /* not used */ + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, + const char *name, const char *mode) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name, mode); +} + + +LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + +/* }====================================================== */ + + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return LUA_TNIL; + else { + int tt; + lua_pushstring(L, event); + tt = lua_rawget(L, -2); + if (tt == LUA_TNIL) /* is metafield nil? */ + lua_pop(L, 2); /* remove metatable and metafield */ + else + lua_remove(L, -2); /* remove only metatable */ + return tt; /* return metafield type */ + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = lua_absindex(L, obj); + if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { + lua_Integer l; + int isnum; + lua_len(L, idx); + l = lua_tointegerx(L, -1, &isnum); + if (l_unlikely(!isnum)) + luaL_error(L, "object length is not an integer"); + lua_pop(L, 1); /* remove object */ + return l; +} + + +LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + idx = lua_absindex(L,idx); + if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + else { + switch (lua_type(L, idx)) { + case LUA_TNUMBER: { + if (lua_isinteger(L, idx)) + lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); + else + lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); + break; + } + case LUA_TSTRING: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: { + int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ + const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : + luaL_typename(L, idx); + lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_remove(L, -2); /* remove '__name' */ + break; + } + } + } + return lua_tolstring(L, -1, len); +} + + +/* +** set functions from list 'l' into table at top - 'nup'; each +** function gets the 'nup' elements at the top as upvalues. +** Returns with only the table at the stack. +*/ +LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + if (l->func == NULL) /* place holder? */ + lua_pushboolean(L, 0); + else { + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + } + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + +/* +** ensure that stack[idx][fname] has a table and push that table +** into the stack +*/ +LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { + if (lua_getfield(L, idx, fname) == LUA_TTABLE) + return 1; /* table already there */ + else { + lua_pop(L, 1); /* remove previous result */ + idx = lua_absindex(L, idx); + lua_newtable(L); + lua_pushvalue(L, -1); /* copy to be left at top */ + lua_setfield(L, idx, fname); /* assign new table to field */ + return 0; /* false, because did not find table there */ + } +} + + +/* +** Stripped-down 'require': After checking "loaded" table, calls 'openf' +** to open a module, registers the result in 'package.loaded' table and, +** if 'glb' is true, also registers the result in the global table. +** Leaves resulting module on the top. +*/ +LUALIB_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, -1, modname); /* LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove LOADED table */ + if (glb) { + lua_pushvalue(L, -1); /* copy of module */ + lua_setglobal(L, modname); /* _G[modname] = module */ + } +} + + +LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, + const char *p, const char *r) { + const char *wild; + size_t l = strlen(p); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(b, s, wild - s); /* push prefix */ + luaL_addstring(b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after 'p' */ + } + luaL_addstring(b, s); /* push last suffix */ +} + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, + const char *p, const char *r) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addgsub(&b, s, p, r); + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void)ud; (void)osize; /* not used */ + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + + +static int panic (lua_State *L) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "error object is not a string"; + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + msg); + return 0; /* return to Lua to abort */ +} + + +/* +** Warning functions: +** warnfoff: warning system is off +** warnfon: ready to start a new message +** warnfcont: previous message is to be continued +*/ +static void warnfoff (void *ud, const char *message, int tocont); +static void warnfon (void *ud, const char *message, int tocont); +static void warnfcont (void *ud, const char *message, int tocont); + + +/* +** Check whether message is a control message. If so, execute the +** control or ignore it if unknown. +*/ +static int checkcontrol (lua_State *L, const char *message, int tocont) { + if (tocont || *(message++) != '@') /* not a control message? */ + return 0; + else { + if (strcmp(message, "off") == 0) + lua_setwarnf(L, warnfoff, L); /* turn warnings off */ + else if (strcmp(message, "on") == 0) + lua_setwarnf(L, warnfon, L); /* turn warnings on */ + return 1; /* it was a control message */ + } +} + + +static void warnfoff (void *ud, const char *message, int tocont) { + checkcontrol((lua_State *)ud, message, tocont); +} + + +/* +** Writes the message and handle 'tocont', finishing the message +** if needed and setting the next warn function. +*/ +static void warnfcont (void *ud, const char *message, int tocont) { + lua_State *L = (lua_State *)ud; + lua_writestringerror("%s", message); /* write message */ + if (tocont) /* not the last part? */ + lua_setwarnf(L, warnfcont, L); /* to be continued */ + else { /* last part */ + lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ + lua_setwarnf(L, warnfon, L); /* next call is a new message */ + } +} + + +static void warnfon (void *ud, const char *message, int tocont) { + if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */ + return; /* nothing else to be done */ + lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ + warnfcont(ud, message, tocont); /* finish processing */ +} + + +LUALIB_API lua_State *luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + if (l_likely(L)) { + lua_atpanic(L, &panic); + lua_setwarnf(L, warnfoff, L); /* default is warnings off */ + } + return L; +} + + +LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { + lua_Number v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ + luaL_error(L, "core and library have incompatible numeric types"); + else if (v != ver) + luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", + (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); +} + +/* +** $Id: lbaselib.c $ +** Basic library +** See Copyright Notice in lua.h +*/ + +#define lbaselib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + for (i = 1; i <= n; i++) { /* for each argument */ + size_t l; + const char *s = luaL_tolstring(L, i, &l); /* convert it to string */ + if (i > 1) /* not the first element? */ + lua_writestring("\t", 1); /* add a tab before it */ + lua_writestring(s, l); /* print it */ + lua_pop(L, 1); /* pop result */ + } + lua_writeline(); + return 0; +} + + +/* +** Creates a warning with all given arguments. +** Check first for errors; otherwise an error may interrupt +** the composition of a warning, leaving it unfinished. +*/ +static int luaB_warn (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_checkstring(L, 1); /* at least one argument */ + for (i = 2; i <= n; i++) + luaL_checkstring(L, i); /* make sure all arguments are strings */ + for (i = 1; i < n; i++) /* compose warning */ + lua_warning(L, lua_tostring(L, i), 1); + lua_warning(L, lua_tostring(L, n), 0); /* close warning */ + return 0; +} + + +#define SPACECHARS " \f\n\r\t\v" + +static const char *b_str2int (const char *s, int base, lua_Integer *pn) { + lua_Unsigned n = 0; + int neg = 0; + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle sign */ + else if (*s == '+') s++; + if (!isalnum((unsigned char)*s)) /* no digit? */ + return NULL; + do { + int digit = (isdigit((unsigned char)*s)) ? *s - '0' + : (toupper((unsigned char)*s) - 'A') + 10; + if (digit >= base) return NULL; /* invalid numeral */ + n = n * base + digit; + s++; + } while (isalnum((unsigned char)*s)); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + *pn = (lua_Integer)((neg) ? (0u - n) : n); + return s; +} + + +static int luaB_tonumber (lua_State *L) { + if (lua_isnoneornil(L, 2)) { /* standard conversion? */ + if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ + lua_settop(L, 1); /* yes; return it */ + return 1; + } + else { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + if (s != NULL && lua_stringtonumber(L, s) == l + 1) + return 1; /* successful conversion to number */ + /* else not a number */ + luaL_checkany(L, 1); /* (but there must be some parameter) */ + } + } + else { + size_t l; + const char *s; + lua_Integer n = 0; /* to avoid warnings */ + lua_Integer base = luaL_checkinteger(L, 2); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + if (b_str2int(s, (int)base, &n) == s + l) { + lua_pushinteger(L, n); + return 1; + } /* else not a number */ + } /* else not a number */ + luaL_pushfail(L); /* not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = (int)luaL_optinteger(L, 2, 1); + lua_settop(L, 1); + if (lua_type(L, 1) == LUA_TSTRING && level > 0) { + luaL_where(L, level); /* add extra information */ + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)) + return luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawlen (lua_State *L) { + int t = lua_type(L, 1); + luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, + "table or string"); + lua_pushinteger(L, lua_rawlen(L, 1)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int pushmode (lua_State *L, int oldmode) { + if (oldmode == -1) + luaL_pushfail(L); /* invalid call to 'lua_gc' */ + else + lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" + : "generational"); + return 1; +} + + +/* +** check whether call to 'lua_gc' was valid (not inside a finalizer) +*/ +#define checkvalres(res) { if (res == -1) break; } + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", + "isrunning", "generational", "incremental", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, + LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; + int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; + switch (o) { + case LUA_GCCOUNT: { + int k = lua_gc(L, o); + int b = lua_gc(L, LUA_GCCOUNTB); + checkvalres(k); + lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + int step = (int)luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, step); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCSETPAUSE: + case LUA_GCSETSTEPMUL: { + int p = (int)luaL_optinteger(L, 2, 0); + int previous = lua_gc(L, o, p); + checkvalres(previous); + lua_pushinteger(L, previous); + return 1; + } + case LUA_GCISRUNNING: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCGEN: { + int minormul = (int)luaL_optinteger(L, 2, 0); + int majormul = (int)luaL_optinteger(L, 3, 0); + return pushmode(L, lua_gc(L, o, minormul, majormul)); + } + case LUA_GCINC: { + int pause = (int)luaL_optinteger(L, 2, 0); + int stepmul = (int)luaL_optinteger(L, 3, 0); + int stepsize = (int)luaL_optinteger(L, 4, 0); + return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); + } + default: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushinteger(L, res); + return 1; + } + } + luaL_pushfail(L); /* invalid call (inside a finalizer) */ + return 1; +} + + +static int luaB_type (lua_State *L) { + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int pairscont (lua_State *L, int status, lua_KContext k) { + (void)L; (void)status; (void)k; /* unused */ + return 3; +} + +static int luaB_pairs (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ + lua_pushcfunction(L, luaB_next); /* will return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + } + else { + lua_pushvalue(L, 1); /* argument 'self' to metamethod */ + lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ + } + return 3; +} + + +/* +** Traversal function for 'ipairs' +*/ +static int ipairsaux (lua_State *L) { + lua_Integer i = luaL_checkinteger(L, 2); + i = luaL_intop(+, i, 1); + lua_pushinteger(L, i); + return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; +} + + +/* +** 'ipairs' function. Returns 'ipairsaux', given "table", 0. +** (The given "table" may not be a table.) +*/ +static int luaB_ipairs (lua_State *L) { + luaL_checkany(L, 1); + lua_pushcfunction(L, ipairsaux); /* iteration function */ + lua_pushvalue(L, 1); /* state */ + lua_pushinteger(L, 0); /* initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status, int envidx) { + if (l_likely(status == LUA_OK)) { + if (envidx != 0) { /* 'env' parameter? */ + lua_pushvalue(L, envidx); /* environment for loaded function */ + if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ + lua_pop(L, 1); /* remove 'env' if not used by previous call */ + } + return 1; + } + else { /* error (message is on top of the stack) */ + luaL_pushfail(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return fail plus error message */ + } +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + const char *mode = luaL_optstring(L, 2, NULL); + int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ + int status = luaL_loadfilex(L, fname, mode); + return load_aux(L, status, env); +} + + +/* +** {====================================================== +** Generic Read function +** ======================================================= +*/ + + +/* +** reserved slot, above all arguments, to hold a copy of the returned +** string to avoid it being collected while parsed. 'load' has four +** optional arguments (chunk, source name, mode, and environment). +*/ +#define RESERVEDSLOT 5 + + +/* +** Reader for generic 'load' function: 'lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)(ud); /* not used */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop result */ + *size = 0; + return NULL; + } + else if (l_unlikely(!lua_isstring(L, -1))) + luaL_error(L, "reader function must return a string"); + lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ + return lua_tolstring(L, RESERVEDSLOT, size); +} + + +static int luaB_load (lua_State *L) { + int status; + size_t l; + const char *s = lua_tolstring(L, 1, &l); + const char *mode = luaL_optstring(L, 3, "bt"); + int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ + if (s != NULL) { /* loading a string? */ + const char *chunkname = luaL_optstring(L, 2, s); + status = luaL_loadbufferx(L, s, l, chunkname, mode); + } + else { /* loading from a reader function */ + const char *chunkname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, RESERVEDSLOT); /* create reserved slot */ + status = lua_load(L, generic_reader, NULL, chunkname, mode); + } + return load_aux(L, status, env); +} + +/* }====================================================== */ + + +static int dofilecont (lua_State *L, int d1, lua_KContext d2) { + (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ + return lua_gettop(L) - 1; +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + lua_settop(L, 1); + if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) + return lua_error(L); + lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); + return dofilecont(L, 0, 0); +} + + +static int luaB_assert (lua_State *L) { + if (l_likely(lua_toboolean(L, 1))) /* condition is true? */ + return lua_gettop(L); /* return all arguments */ + else { /* error */ + luaL_checkany(L, 1); /* there must be a condition */ + lua_remove(L, 1); /* remove it */ + lua_pushliteral(L, "assertion failed!"); /* default message */ + lua_settop(L, 1); /* leave only message (default if no other one) */ + return luaB_error(L); /* call 'error' */ + } +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + lua_Integer i = luaL_checkinteger(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - (int)i; + } +} + + +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +static int finishpcall (lua_State *L, int status, lua_KContext extra) { + if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */ + lua_pushboolean(L, 0); /* first result (false) */ + lua_pushvalue(L, -2); /* error message */ + return 2; /* return false, msg */ + } + else + return lua_gettop(L) - (int)extra; /* return all results */ +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + lua_pushboolean(L, 1); /* first result if no errors */ + lua_insert(L, 1); /* put it in place */ + status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); +} + + +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have ; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ +static int luaB_xpcall (lua_State *L) { + int status; + int n = lua_gettop(L); + luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ + lua_pushboolean(L, 1); /* first result */ + lua_pushvalue(L, 1); /* function */ + lua_rotate(L, 3, 2); /* move them below function's arguments */ + status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + luaL_tolstring(L, 1, NULL); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"ipairs", luaB_ipairs}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"next", luaB_next}, + {"pairs", luaB_pairs}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"warn", luaB_warn}, + {"rawequal", luaB_rawequal}, + {"rawlen", luaB_rawlen}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"xpcall", luaB_xpcall}, + /* placeholders */ + {LUA_GNAME, NULL}, + {"_VERSION", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_base (lua_State *L) { + /* open lib into global table */ + lua_pushglobaltable(L); + luaL_setfuncs(L, base_funcs, 0); + /* set global _G */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, LUA_GNAME); + /* set global _VERSION */ + lua_pushliteral(L, LUA_VERSION); + lua_setfield(L, -2, "_VERSION"); + return 1; +} + +/* +** $Id: lcorolib.c $ +** Coroutine Library +** See Copyright Notice in lua.h +*/ + +#define lcorolib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +static lua_State *getco (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argexpected(L, co, 1, "thread"); + return co; +} + + +/* +** Resumes a coroutine. Returns the number of results for non-error +** cases or -1 for errors. +*/ +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status, nres; + if (l_unlikely(!lua_checkstack(co, narg))) { + lua_pushliteral(L, "too many arguments to resume"); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + status = lua_resume(co, L, narg, &nres); + if (l_likely(status == LUA_OK || status == LUA_YIELD)) { + if (l_unlikely(!lua_checkstack(L, nres + 1))) { + lua_pop(co, nres); /* remove results anyway */ + lua_pushliteral(L, "too many results to resume"); + return -1; /* error flag */ + } + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = getco(L); + int r; + r = auxresume(L, co, lua_gettop(L) - 1); + if (l_unlikely(r < 0)) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + 'resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (l_unlikely(r < 0)) { /* error? */ + int stat = lua_status(co); + if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ + stat = lua_closethread(co, L); /* close its tbc variables */ + lua_assert(stat != LUA_OK); + lua_xmove(co, L, 1); /* move error message to the caller */ + } + if (stat != LUA_ERRMEM && /* not a memory error and ... */ + lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ + luaL_where(L, 1); /* add extra info, if available */ + lua_insert(L, -2); + lua_concat(L, 2); + } + return lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL; + luaL_checktype(L, 1, LUA_TFUNCTION); + NL = lua_newthread(L); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +#define COS_RUN 0 +#define COS_DEAD 1 +#define COS_YIELD 2 +#define COS_NORM 3 + + +static const char *const statname[] = + {"running", "dead", "suspended", "normal"}; + + +static int auxstatus (lua_State *L, lua_State *co) { + if (L == co) return COS_RUN; + else { + switch (lua_status(co)) { + case LUA_YIELD: + return COS_YIELD; + case LUA_OK: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar)) /* does it have frames? */ + return COS_NORM; /* it is running */ + else if (lua_gettop(co) == 0) + return COS_DEAD; + else + return COS_YIELD; /* initial state */ + } + default: /* some error occurred */ + return COS_DEAD; + } + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = getco(L); + lua_pushstring(L, statname[auxstatus(L, co)]); + return 1; +} + + +static int luaB_yieldable (lua_State *L) { + lua_State *co = lua_isnone(L, 1) ? L : getco(L); + lua_pushboolean(L, lua_isyieldable(co)); + return 1; +} + + +static int luaB_corunning (lua_State *L) { + int ismain = lua_pushthread(L); + lua_pushboolean(L, ismain); + return 2; +} + + +static int luaB_close (lua_State *L) { + lua_State *co = getco(L); + int status = auxstatus(L, co); + switch (status) { + case COS_DEAD: case COS_YIELD: { + status = lua_closethread(co, L); + if (status == LUA_OK) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushboolean(L, 0); + lua_xmove(co, L, 1); /* move error message */ + return 2; + } + } + default: /* normal or running coroutine */ + return luaL_error(L, "cannot close a %s coroutine", statname[status]); + } +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {"isyieldable", luaB_yieldable}, + {"close", luaB_close}, + {NULL, NULL} +}; + + + +LUAMOD_API int luaopen_coroutine (lua_State *L) { + luaL_newlib(L, co_funcs); + return 1; +} + +/* +** $Id: ldblib.c $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + +#define ldblib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. +*/ +static const char *const HOOKKEY = "_HOOKKEY"; + + +/* +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be +** checked. +*/ +static void checkstack (lua_State *L, lua_State *L1, int n) { + if (l_unlikely(L != L1 && !lua_checkstack(L1, n))) + luaL_error(L, "stack overflow"); +} + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; /* return 1st argument */ +} + + +static int db_getuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 2, 1); + if (lua_type(L, 1) != LUA_TUSERDATA) + luaL_pushfail(L); + else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) { + lua_pushboolean(L, 1); + return 2; + } + return 1; +} + + +static int db_setuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 3, 1); + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checkany(L, 2); + lua_settop(L, 2); + if (!lua_setiuservalue(L, 1, n)) + luaL_pushfail(L); + return 1; +} + + +/* +** Auxiliary function used by several library functions: check for +** an optional thread as function's first argument and set 'arg' with +** 1 if this argument is present (so that functions can skip it to +** access their other arguments) +*/ +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; /* function will operate over current thread */ + } +} + + +/* +** Variations of 'lua_settable', used by 'db_getinfo' to put results +** from 'lua_getinfo' into result table. Key is always a string; +** value can be a string, an int, or a boolean. +*/ +static void settabss (lua_State *L, const char *k, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, k); +} + +static void settabsi (lua_State *L, const char *k, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static void settabsb (lua_State *L, const char *k, int v) { + lua_pushboolean(L, v); + lua_setfield(L, -2, k); +} + + +/* +** In function 'db_getinfo', the call to 'lua_getinfo' may push +** results on the stack; later it creates the result table to put +** these objects. Function 'treatstackoption' puts the result from +** 'lua_getinfo' on top of the result table so that it can call +** 'lua_setfield'. +*/ +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) + lua_rotate(L, -2, 1); /* exchange object and table */ + else + lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lua_setfield(L, -2, fname); /* put object into table */ +} + + +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSrtu"); + checkstack(L, L1, 3); + luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'"); + if (lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ + lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lua_xmove(L, L1, 1); + } + else { /* stack level */ + if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { + luaL_pushfail(L); /* level out of range */ + return 1; + } + } + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_newtable(L); /* table to collect results */ + if (strchr(options, 'S')) { + lua_pushlstring(L, ar.source, ar.srclen); + lua_setfield(L, -2, "source"); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) { + settabsi(L, "nups", ar.nups); + settabsi(L, "nparams", ar.nparams); + settabsb(L, "isvararg", ar.isvararg); + } + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'r')) { + settabsi(L, "ftransfer", ar.ftransfer); + settabsi(L, "ntransfer", ar.ntransfer); + } + if (strchr(options, 't')) + settabsb(L, "istailcall", ar.istailcall); + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ + if (lua_isfunction(L, arg + 1)) { /* function argument? */ + lua_pushvalue(L, arg + 1); /* push function */ + lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ + return 1; /* return only name (there is no value) */ + } + else { /* stack-level argument */ + lua_Debug ar; + const char *name; + int level = (int)luaL_checkinteger(L, arg + 1); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + checkstack(L, L1, 1); + name = lua_getlocal(L1, &ar, nvar); + if (name) { + lua_xmove(L1, L, 1); /* move local value */ + lua_pushstring(L, name); /* push name */ + lua_rotate(L, -2, 1); /* re-order */ + return 2; + } + else { + luaL_pushfail(L); /* no name (nor value) */ + return 1; + } + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + const char *name; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + int level = (int)luaL_checkinteger(L, arg + 1); + int nvar = (int)luaL_checkinteger(L, arg + 2); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + checkstack(L, L1, 1); + lua_xmove(L, L1, 1); + name = lua_setlocal(L1, &ar, nvar); + if (name == NULL) + lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lua_pushstring(L, name); + return 1; +} + + +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ + luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); /* no-op if get is false */ + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + +/* +** Check whether a given upvalue from a given closure exists and +** returns its index +*/ +static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) { + void *id; + int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ + luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ + id = lua_upvalueid(L, argf, nup); + if (pnup) { + luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index"); + *pnup = nup; + } + return id; +} + + +static int db_upvalueid (lua_State *L) { + void *id = checkupval(L, 1, 2, NULL); + if (id != NULL) + lua_pushlightuserdata(L, id); + else + luaL_pushfail(L); + return 1; +} + + +static int db_upvaluejoin (lua_State *L) { + int n1, n2; + checkupval(L, 1, 2, &n1); + checkupval(L, 3, 4, &n2); + luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); + luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); + lua_upvaluejoin(L, 1, n1, 3, n2); + return 0; +} + + +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail call"}; + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + lua_pushthread(L); + if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ + lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); /* push current line */ + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); /* call hook function */ + } +} + + +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { /* no hook? */ + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = (int)luaL_optinteger(L, arg + 3, 0); + func = hookf; mask = makemask(smask, count); + } + if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) { + /* table just created; initialize it */ + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */ + } + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ + lua_pushvalue(L, arg + 1); /* value (hook function) */ + lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + lua_sethook(L1, func, mask, count); + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook == NULL) { /* no hook? */ + luaL_pushfail(L); + return 1; + } + else if (hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { /* hook table must exist */ + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); + lua_rawget(L, -2); /* 1st result = hooktable[L1] */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + lua_writestringerror("%s", "lua_debug> "); + if (fgets(buffer, sizeof(buffer), stdin) == NULL || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) + lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +static int db_traceback (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + const char *msg = lua_tostring(L, arg + 1); + if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ + lua_pushvalue(L, arg + 1); /* return it untouched */ + else { + int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); + luaL_traceback(L, L1, msg, level); + } + return 1; +} + + +static int db_setcstacklimit (lua_State *L) { + int limit = (int)luaL_checkinteger(L, 1); + int res = lua_setcstacklimit(L, limit); + lua_pushinteger(L, res); + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getuservalue", db_getuservalue}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"upvaluejoin", db_upvaluejoin}, + {"upvalueid", db_upvalueid}, + {"setuservalue", db_setuservalue}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_traceback}, + {"setcstacklimit", db_setcstacklimit}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_debug (lua_State *L) { + luaL_newlib(L, dblib); + return 1; +} + +/* +** $Id: liolib.c $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + +#define liolib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + + + +/* +** Change this macro to accept other modes for 'fopen' besides +** the standard ones. +*/ +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ +static int l_checkmode (const char *mode) { + return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && + (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ + (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ +} + +#endif + +/* +** {====================================================== +** l_popen spawns a new process connected to the current +** one through the file streams. +** ======================================================= +*/ + +#if !defined(l_popen) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) +#define l_pclose(L,file) (pclose(file)) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + +#define l_popen(L,c,m) (_popen(c,m)) +#define l_pclose(L,file) (_pclose(file)) + +#if !defined(l_checkmodep) +/* Windows accepts "[rw][bt]?" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \ + (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0'))) +#endif + +#else /* }{ */ + +/* ISO C definitions */ +#define l_popen(L,c,m) \ + ((void)c, (void)m, \ + luaL_error(L, "'popen' not supported"), \ + (FILE*)0) +#define l_pclose(L,file) ((void)L, (void)file, -1) + +#endif /* } */ + +#endif /* } */ + + +#if !defined(l_checkmodep) +/* By default, Lua accepts only "r" or "w" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') +#endif + +/* }====================================================== */ + + +#if !defined(l_getc) /* { */ + +#if defined(LUA_USE_POSIX) +#define l_getc(f) getc_unlocked(f) +#define l_lockfile(f) flockfile(f) +#define l_unlockfile(f) funlockfile(f) +#else +#define l_getc(f) getc(f) +#define l_lockfile(f) ((void)0) +#define l_unlockfile(f) ((void)0) +#endif + +#endif /* } */ + + +/* +** {====================================================== +** l_fseek: configuration for longer offsets +** ======================================================= +*/ + +#if !defined(l_fseek) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define l_fseek(f,o,w) fseeko(f,o,w) +#define l_ftell(f) ftello(f) +#define l_seeknum off_t + +#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ + && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ + +/* Windows (but not DDK) and Visual C++ 2005 or higher */ +#define l_fseek(f,o,w) _fseeki64(f,o,w) +#define l_ftell(f) _ftelli64(f) +#define l_seeknum __int64 + +#else /* }{ */ + +/* ISO C definitions */ +#define l_fseek(f,o,w) fseek(f,o,w) +#define l_ftell(f) ftell(f) +#define l_seeknum long + +#endif /* } */ + +#endif /* } */ + +/* }====================================================== */ + + + +#define IO_PREFIX "_IO_" +#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) +#define IO_INPUT (IO_PREFIX "input") +#define IO_OUTPUT (IO_PREFIX "output") + + +typedef luaL_Stream LStream; + + +#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + +#define isclosed(p) ((p)->closef == NULL) + + +static int io_type (lua_State *L) { + LStream *p; + luaL_checkany(L, 1); + p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); + if (p == NULL) + luaL_pushfail(L); /* not a file */ + else if (isclosed(p)) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static int f_tostring (lua_State *L) { + LStream *p = tolstream(L); + if (isclosed(p)) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", p->f); + return 1; +} + + +static FILE *tofile (lua_State *L) { + LStream *p = tolstream(L); + if (l_unlikely(isclosed(p))) + luaL_error(L, "attempt to use a closed file"); + lua_assert(p->f); + return p->f; +} + + +/* +** When creating file handles, always creates a 'closed' file handle +** before opening the actual file; so, if there is a memory error, the +** handle is in a consistent state. +*/ +static LStream *newprefile (lua_State *L) { + LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); + p->closef = NULL; /* mark file handle as 'closed' */ + luaL_setmetatable(L, LUA_FILEHANDLE); + return p; +} + + +/* +** Calls the 'close' function from a file handle. The 'volatile' avoids +** a bug in some versions of the Clang compiler (e.g., clang 3.0 for +** 32 bits). +*/ +static int aux_close (lua_State *L) { + LStream *p = tolstream(L); + volatile lua_CFunction cf = p->closef; + p->closef = NULL; /* mark stream as closed */ + return (*cf)(L); /* close it */ +} + + +static int f_close (lua_State *L) { + tofile(L); /* make sure argument is an open stream */ + return aux_close(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ + return f_close(L); +} + + +static int f_gc (lua_State *L) { + LStream *p = tolstream(L); + if (!isclosed(p) && p->f != NULL) + aux_close(L); /* ignore closed and incompletely open files */ + return 0; +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + LStream *p = tolstream(L); + int res = fclose(p->f); + return luaL_fileresult(L, (res == 0), NULL); +} + + +static LStream *newfile (lua_State *L) { + LStream *p = newprefile(L); + p->f = NULL; + p->closef = &io_fclose; + return p; +} + + +static void opencheck (lua_State *L, const char *fname, const char *mode) { + LStream *p = newfile(L); + p->f = fopen(fname, mode); + if (l_unlikely(p->f == NULL)) + luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newfile(L); + const char *md = mode; /* to traverse/check mode */ + luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); + p->f = fopen(filename, mode); + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + LStream *p = tolstream(L); + errno = 0; + return luaL_execresult(L, l_pclose(L, p->f)); +} + + +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newprefile(L); + luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); + p->f = l_popen(L, filename, mode); + p->closef = &io_pclose; + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + LStream *p = newfile(L); + p->f = tmpfile(); + return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *findex) { + LStream *p; + lua_getfield(L, LUA_REGISTRYINDEX, findex); + p = (LStream *)lua_touserdata(L, -1); + if (l_unlikely(isclosed(p))) + luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); + return p->f; +} + + +static int g_iofile (lua_State *L, const char *f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) + opencheck(L, filename, mode); + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_setfield(L, LUA_REGISTRYINDEX, f); + } + /* return current value */ + lua_getfield(L, LUA_REGISTRYINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + +/* +** Auxiliary function to create the iteration function for 'lines'. +** The iteration function is a closure over 'io_readline', with +** the following upvalues: +** 1) The file being read (first value in the stack) +** 2) the number of arguments to read +** 3) a boolean, true iff file has to be closed when finished ('toclose') +** *) a variable number of format arguments (rest of the stack) +*/ +static void aux_lines (lua_State *L, int toclose) { + int n = lua_gettop(L) - 1; /* number of arguments to read */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); + lua_pushvalue(L, 1); /* file */ + lua_pushinteger(L, n); /* number of arguments to read */ + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_rotate(L, 2, 3); /* move the three values to their positions */ + lua_pushcclosure(L, io_readline, 3 + n); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 0); + return 1; +} + + +/* +** Return an iteration function for 'io.lines'. If file has to be +** closed, also returns the file itself as a second result (to be +** closed as the state at the exit of a generic for). +*/ +static int io_lines (lua_State *L) { + int toclose; + if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ + if (lua_isnil(L, 1)) { /* no file name? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ + lua_replace(L, 1); /* put it at index 1 */ + tofile(L); /* check that it's a valid file handle */ + toclose = 0; /* do not close it after iteration */ + } + else { /* open a new file */ + const char *filename = luaL_checkstring(L, 1); + opencheck(L, filename, "r"); + lua_replace(L, 1); /* put file at index 1 */ + toclose = 1; /* close it after iteration */ + } + aux_lines(L, toclose); /* push iteration function */ + if (toclose) { + lua_pushnil(L); /* state */ + lua_pushnil(L); /* control */ + lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ + return 4; + } + else + return 1; +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +/* maximum length of a numeral */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + + +/* auxiliary structure used by 'read_number' */ +typedef struct { + FILE *f; /* file being read */ + int c; /* current character (look ahead) */ + int n; /* number of elements in buffer 'buff' */ + char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ +} RN; + + +/* +** Add current char to buffer (if not out of space) and read next one +*/ +static int nextc (RN *rn) { + if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ + rn->buff[0] = '\0'; /* invalidate result */ + return 0; /* fail */ + } + else { + rn->buff[rn->n++] = rn->c; /* save current char */ + rn->c = l_getc(rn->f); /* read next one */ + return 1; + } +} + + +/* +** Accept current char if it is in 'set' (of size 2) +*/ +static int test2 (RN *rn, const char *set) { + if (rn->c == set[0] || rn->c == set[1]) + return nextc(rn); + else return 0; +} + + +/* +** Read a sequence of (hex)digits +*/ +static int readdigits (RN *rn, int hex) { + int count = 0; + while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) + count++; + return count; +} + + +/* +** Read a number: first reads a valid prefix of a numeral into a buffer. +** Then it calls 'lua_stringtonumber' to check whether the format is +** correct and to convert it to a Lua number. +*/ +static int read_number (lua_State *L, FILE *f) { + RN rn; + int count = 0; + int hex = 0; + char decp[2]; + rn.f = f; rn.n = 0; + decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ + decp[1] = '.'; /* always accept a dot */ + l_lockfile(rn.f); + do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ + test2(&rn, "-+"); /* optional sign */ + if (test2(&rn, "00")) { + if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ + else count = 1; /* count initial '0' as a valid digit */ + } + count += readdigits(&rn, hex); /* integral part */ + if (test2(&rn, decp)) /* decimal point? */ + count += readdigits(&rn, hex); /* fractional part */ + if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ + test2(&rn, "-+"); /* exponent sign */ + readdigits(&rn, 0); /* exponent digits */ + } + ungetc(rn.c, rn.f); /* unread look-ahead char */ + l_unlockfile(rn.f); + rn.buff[rn.n] = '\0'; /* finish string */ + if (l_likely(lua_stringtonumber(L, rn.buff))) + return 1; /* ok, it is a valid number */ + else { /* invalid format */ + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); /* no-op when c == EOF */ + lua_pushliteral(L, ""); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f, int chop) { + luaL_Buffer b; + int c; + luaL_buffinit(L, &b); + do { /* may need to read several chunks to get whole line */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ + int i = 0; + l_lockfile(f); /* no memory errors can happen inside the lock */ + while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') + buff[i++] = c; /* read up to end of line or buffer limit */ + l_unlockfile(f); + luaL_addsize(&b, i); + } while (c != EOF && c != '\n'); /* repeat until end of line */ + if (!chop && c == '\n') /* want a newline and have one? */ + luaL_addchar(&b, c); /* add ending newline to result */ + luaL_pushresult(&b); /* close buffer */ + /* return ok if read something (either a newline or something else) */ + return (c == '\n' || lua_rawlen(L, -1) > 0); +} + + +static void read_all (lua_State *L, FILE *f) { + size_t nr; + luaL_Buffer b; + luaL_buffinit(L, &b); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); + luaL_addsize(&b, nr); + } while (nr == LUAL_BUFFERSIZE); + luaL_pushresult(&b); /* close buffer */ +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t nr; /* number of chars actually read */ + char *p; + luaL_Buffer b; + luaL_buffinit(L, &b); + p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ + nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ + luaL_addsize(&b, nr); + luaL_pushresult(&b); /* close buffer */ + return (nr > 0); /* true iff read something */ +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int n, success; + clearerr(f); + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f, 1); + n = first + 1; /* to return 1 result */ + } + else { + /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)luaL_checkinteger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = luaL_checkstring(L, n); + if (*p == '*') p++; /* skip optional '*' (for compatibility) */ + switch (*p) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f, 1); + break; + case 'L': /* line with end-of-line */ + success = read_line(L, f, 0); + break; + case 'a': /* file */ + read_all(L, f); /* read entire file */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return luaL_fileresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + luaL_pushfail(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +/* +** Iteration function for 'lines'. +*/ +static int io_readline (lua_State *L) { + LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); + int i; + int n = (int)lua_tointeger(L, lua_upvalueindex(2)); + if (isclosed(p)) /* file is already closed? */ + return luaL_error(L, "file is already closed"); + lua_settop(L , 1); + luaL_checkstack(L, n, "too many arguments"); + for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ + lua_pushvalue(L, lua_upvalueindex(3 + i)); + n = g_read(L, p->f, 2); /* 'n' is number of results */ + lua_assert(n > 0); /* should return at least a nil */ + if (lua_toboolean(L, -n)) /* read at least one value? */ + return n; /* return them */ + else { /* first result is false: EOF or error */ + if (n > 1) { /* is there error information? */ + /* 2nd result is error message */ + return luaL_error(L, "%s", lua_tostring(L, -n + 1)); + } + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); /* clear stack */ + lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - arg; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + int len = lua_isinteger(L, arg) + ? fprintf(f, LUA_INTEGER_FMT, + (LUAI_UACINT)lua_tointeger(L, arg)) + : fprintf(f, LUA_NUMBER_FMT, + (LUAI_UACNUMBER)lua_tonumber(L, arg)); + status = status && (len > 0); + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + if (l_likely(status)) + return 1; /* file handle already on stack top */ + else return luaL_fileresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + FILE *f = tofile(L); + lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ + return g_write(L, f, 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + lua_Integer p3 = luaL_optinteger(L, 3, 0); + l_seeknum offset = (l_seeknum)p3; + luaL_argcheck(L, (lua_Integer)offset == p3, 3, + "not an integer in proper range"); + op = l_fseek(f, offset, mode[op]); + if (l_unlikely(op)) + return luaL_fileresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, (lua_Integer)l_ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res = setvbuf(f, NULL, mode[op], (size_t)sz); + return luaL_fileresult(L, res == 0, NULL); +} + + + +static int io_flush (lua_State *L) { + return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); +} + + +/* +** functions for 'io' library +*/ +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +/* +** methods for file handles +*/ +static const luaL_Reg meth[] = { + {"read", f_read}, + {"write", f_write}, + {"lines", f_lines}, + {"flush", f_flush}, + {"seek", f_seek}, + {"close", f_close}, + {"setvbuf", f_setvbuf}, + {NULL, NULL} +}; + + +/* +** metamethods for file handles +*/ +static const luaL_Reg metameth[] = { + {"__index", NULL}, /* place holder */ + {"__gc", f_gc}, + {"__close", f_gc}, + {"__tostring", f_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ + luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ + luaL_newlibtable(L, meth); /* create method table */ + luaL_setfuncs(L, meth, 0); /* add file methods to method table */ + lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + LStream *p = tolstream(L); + p->closef = &io_noclose; /* keep file opened */ + luaL_pushfail(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +static void createstdfile (lua_State *L, FILE *f, const char *k, + const char *fname) { + LStream *p = newprefile(L); + p->f = f; + p->closef = &io_noclose; + if (k != NULL) { + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ + } + lua_setfield(L, -2, fname); /* add file to module */ +} + + +LUAMOD_API int luaopen_io (lua_State *L) { + luaL_newlib(L, iolib); /* new module */ + createmeta(L); + /* create (and set) default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, NULL, "stderr"); + return 1; +} + +/* +** $Id: lmathlib.c $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + +#define lmathlib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +#undef PI +#define PI (l_mathop(3.141592653589793238462643383279502884)) + + +static int math_abs (lua_State *L) { + if (lua_isinteger(L, 1)) { + lua_Integer n = lua_tointeger(L, 1); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); + lua_pushinteger(L, n); + } + else + lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_Number y = luaL_checknumber(L, 1); + lua_Number x = luaL_optnumber(L, 2, 1); + lua_pushnumber(L, l_mathop(atan2)(y, x)); + return 1; +} + + +static int math_toint (lua_State *L) { + int valid; + lua_Integer n = lua_tointegerx(L, 1, &valid); + if (l_likely(valid)) + lua_pushinteger(L, n); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); /* value is not convertible to integer */ + } + return 1; +} + + +static void pushnumint (lua_State *L, lua_Number d) { + lua_Integer n; + if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ + lua_pushinteger(L, n); /* result is integer */ + else + lua_pushnumber(L, d); /* result is float */ +} + + +static int math_floor (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own floor */ + else { + lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_ceil (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own ceil */ + else { + lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_fmod (lua_State *L) { + if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { + lua_Integer d = lua_tointeger(L, 2); + if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ + luaL_argcheck(L, d != 0, 2, "zero"); + lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ + } + else + lua_pushinteger(L, lua_tointeger(L, 1) % d); + } + else + lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), + luaL_checknumber(L, 2))); + return 1; +} + + +/* +** next function does not use 'modf', avoiding problems with 'double*' +** (which is not compatible with 'float*') when lua_Number is not +** 'double'. +*/ +static int math_modf (lua_State *L) { + if (lua_isinteger(L ,1)) { + lua_settop(L, 1); /* number is its own integer part */ + lua_pushnumber(L, 0); /* no fractional part */ + } + else { + lua_Number n = luaL_checknumber(L, 1); + /* integer part (rounds toward zero) */ + lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); + pushnumint(L, ip); + /* fractional part (test needed for inf/-inf) */ + lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); + } + return 2; +} + + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_ult (lua_State *L) { + lua_Integer a = luaL_checkinteger(L, 1); + lua_Integer b = luaL_checkinteger(L, 2); + lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); + return 1; +} + +static int math_log (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number res; + if (lua_isnoneornil(L, 2)) + res = l_mathop(log)(x); + else { + lua_Number base = luaL_checknumber(L, 2); +#if !defined(LUA_USE_C89) + if (base == l_mathop(2.0)) + res = l_mathop(log2)(x); + else +#endif + if (base == l_mathop(10.0)) + res = l_mathop(log10)(x); + else + res = l_mathop(log)(x)/l_mathop(log)(base); + } + lua_pushnumber(L, res); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); + return 1; +} + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imin = 1; /* index of current minimum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, i, imin, LUA_OPLT)) + imin = i; + } + lua_pushvalue(L, imin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imax = 1; /* index of current maximum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, imax, i, LUA_OPLT)) + imax = i; + } + lua_pushvalue(L, imax); + return 1; +} + + +static int math_type (lua_State *L) { + if (lua_type(L, 1) == LUA_TNUMBER) + lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float"); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); + } + return 1; +} + + + +/* +** {================================================================== +** Pseudo-Random Number Generator based on 'xoshiro256**'. +** =================================================================== +*/ + +/* number of binary digits in the mantissa of a float */ +#define FIGS l_floatatt(MANT_DIG) + +#if FIGS > 64 +/* there are only 64 random bits; use them all */ +#undef FIGS +#define FIGS 64 +#endif + + +/* +** LUA_RAND32 forces the use of 32-bit integers in the implementation +** of the PRN generator (mainly for testing). +*/ +#if !defined(LUA_RAND32) && !defined(Rand64) + +/* try to find an integer type with at least 64 bits */ + +#if ((ULONG_MAX >> 31) >> 31) >= 3 + +/* 'long' has at least 64 bits */ +#define Rand64 unsigned long + +#elif !defined(LUA_USE_C89) && defined(LLONG_MAX) + +/* there is a 'long long' type (which must have at least 64 bits) */ +#define Rand64 unsigned long long + +#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 + +/* 'lua_Unsigned' has at least 64 bits */ +#define Rand64 lua_Unsigned + +#endif + +#endif + + +#if defined(Rand64) /* { */ + +/* +** Standard implementation, using 64-bit integers. +** If 'Rand64' has more than 64 bits, the extra bits do not interfere +** with the 64 initial bits, except in a right shift. Moreover, the +** final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim64(x) ((x) & 0xffffffffffffffffu) + + +/* rotate left 'x' by 'n' bits */ +static Rand64 rotl (Rand64 x, int n) { + return (x << n) | (trim64(x) >> (64 - n)); +} + +static Rand64 nextrand (Rand64 *state) { + Rand64 state0 = state[0]; + Rand64 state1 = state[1]; + Rand64 state2 = state[2] ^ state0; + Rand64 state3 = state[3] ^ state1; + Rand64 res = rotl(state1 * 5, 7) * 9; + state[0] = state0 ^ state3; + state[1] = state1 ^ state2; + state[2] = state2 ^ (state1 << 17); + state[3] = rotl(state3, 45); + return res; +} + + +/* must take care to not shift stuff by more than 63 slots */ + + +/* +** Convert bits from a random integer into a float in the +** interval [0,1), getting the higher FIG bits from the +** random unsigned integer and converting that to a float. +*/ + +/* must throw out the extra (64 - FIGS) bits */ +#define shift64_FIG (64 - FIGS) + +/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) + +static lua_Number I2d (Rand64 x) { + return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG; +} + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +#define I2UInt(x) ((lua_Unsigned)trim64(x)) + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +#define Int2I(x) ((Rand64)(x)) + + +#else /* no 'Rand64' }{ */ + +/* get an integer with at least 32 bits */ +#if LUAI_IS32INT +typedef unsigned int lu_int32; +#else +typedef unsigned long lu_int32; +#endif + + +/* +** Use two 32-bit integers to represent a 64-bit quantity. +*/ +typedef struct Rand64 { + lu_int32 h; /* higher half */ + lu_int32 l; /* lower half */ +} Rand64; + + +/* +** If 'lu_int32' has more than 32 bits, the extra bits do not interfere +** with the 32 initial bits, except in a right shift and comparisons. +** Moreover, the final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim32(x) ((x) & 0xffffffffu) + + +/* +** basic operations on 'Rand64' values +*/ + +/* build a new Rand64 value */ +static Rand64 packI (lu_int32 h, lu_int32 l) { + Rand64 result; + result.h = h; + result.l = l; + return result; +} + +/* return i << n */ +static Rand64 Ishl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n); +} + +/* i1 ^= i2 */ +static void Ixor (Rand64 *i1, Rand64 i2) { + i1->h ^= i2.h; + i1->l ^= i2.l; +} + +/* return i1 + i2 */ +static Rand64 Iadd (Rand64 i1, Rand64 i2) { + Rand64 result = packI(i1.h + i2.h, i1.l + i2.l); + if (trim32(result.l) < trim32(i1.l)) /* carry? */ + result.h++; + return result; +} + +/* return i * 5 */ +static Rand64 times5 (Rand64 i) { + return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */ +} + +/* return i * 9 */ +static Rand64 times9 (Rand64 i) { + return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */ +} + +/* return 'i' rotated left 'n' bits */ +static Rand64 rotl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), + (trim32(i.h) >> (32 - n)) | (i.l << n)); +} + +/* for offsets larger than 32, rotate right by 64 - offset */ +static Rand64 rotl1 (Rand64 i, int n) { + lua_assert(n > 32 && n < 64); + n = 64 - n; + return packI((trim32(i.h) >> n) | (i.l << (32 - n)), + (i.h << (32 - n)) | (trim32(i.l) >> n)); +} + +/* +** implementation of 'xoshiro256**' algorithm on 'Rand64' values +*/ +static Rand64 nextrand (Rand64 *state) { + Rand64 res = times9(rotl(times5(state[1]), 7)); + Rand64 t = Ishl(state[1], 17); + Ixor(&state[2], state[0]); + Ixor(&state[3], state[1]); + Ixor(&state[1], state[2]); + Ixor(&state[0], state[3]); + Ixor(&state[2], t); + state[3] = rotl1(state[3], 45); + return res; +} + + +/* +** Converts a 'Rand64' into a float. +*/ + +/* an unsigned 1 with proper type */ +#define UONE ((lu_int32)1) + + +#if FIGS <= 32 + +/* 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) + +/* +** get up to 32 bits from higher half, shifting right to +** throw out the extra bits. +*/ +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS)); + return h * scaleFIG; +} + +#else /* 32 < FIGS <= 64 */ + +/* must take care to not shift stuff by more than 31 slots */ + +/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ +#define scaleFIG \ + (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) + +/* +** use FIGS - 32 bits from lower half, throwing out the other +** (32 - (FIGS - 32)) = (64 - FIGS) bits +*/ +#define shiftLOW (64 - FIGS) + +/* +** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) +*/ +#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0)) + + +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)trim32(x.h) * shiftHI; + lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); + return (h + l) * scaleFIG; +} + +#endif + + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +static lua_Unsigned I2UInt (Rand64 x) { + return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l); +} + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +static Rand64 Int2I (lua_Unsigned n) { + return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n); +} + +#endif /* } */ + + +/* +** A state uses four 'Rand64' values. +*/ +typedef struct { + Rand64 s[4]; +} RanState; + + +/* +** Project the random integer 'ran' into the interval [0, n]. +** Because 'ran' has 2^B possible values, the projection can only be +** uniform when the size of the interval is a power of 2 (exact +** division). Otherwise, to get a uniform projection into [0, n], we +** first compute 'lim', the smallest Mersenne number not smaller than +** 'n'. We then project 'ran' into the interval [0, lim]. If the result +** is inside [0, n], we are done. Otherwise, we try with another 'ran', +** until we have a result inside the interval. +*/ +static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, + RanState *state) { + if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ + return ran & n; /* no bias */ + else { + lua_Unsigned lim = n; + /* compute the smallest (2^b - 1) not smaller than 'n' */ + lim |= (lim >> 1); + lim |= (lim >> 2); + lim |= (lim >> 4); + lim |= (lim >> 8); + lim |= (lim >> 16); +#if (LUA_MAXUNSIGNED >> 31) >= 3 + lim |= (lim >> 32); /* integer type has more than 32 bits */ +#endif + lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ + && lim >= n /* not smaller than 'n', */ + && (lim >> 1) < n); /* and it is the smallest one */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; + } +} + + +static int math_random (lua_State *L) { + lua_Integer low, up; + lua_Unsigned p; + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + Rand64 rv = nextrand(state->s); /* next pseudo-random value */ + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */ + return 1; + } + case 1: { /* only upper limit */ + low = 1; + up = luaL_checkinteger(L, 1); + if (up == 0) { /* single 0 as argument? */ + lua_pushinteger(L, I2UInt(rv)); /* full random integer */ + return 1; + } + break; + } + case 2: { /* lower and upper limits */ + low = luaL_checkinteger(L, 1); + up = luaL_checkinteger(L, 2); + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + /* random integer in the interval [low, up] */ + luaL_argcheck(L, low <= up, 1, "interval is empty"); + /* project random integer into the interval [0, up - low] */ + p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); + lua_pushinteger(L, p + (lua_Unsigned)low); + return 1; +} + + +static void setseed (lua_State *L, Rand64 *state, + lua_Unsigned n1, lua_Unsigned n2) { + int i; + state[0] = Int2I(n1); + state[1] = Int2I(0xff); /* avoid a zero state */ + state[2] = Int2I(n2); + state[3] = Int2I(0); + for (i = 0; i < 16; i++) + nextrand(state); /* discard initial values to "spread" seed */ + lua_pushinteger(L, n1); + lua_pushinteger(L, n2); +} + + +/* +** Set a "random" seed. To get some randomness, use the current time +** and the address of 'L' (in case the machine does address space layout +** randomization). +*/ +static void randseed (lua_State *L, RanState *state) { + lua_Unsigned seed1 = (lua_Unsigned)time(NULL); + lua_Unsigned seed2 = (lua_Unsigned)(size_t)L; + setseed(L, state->s, seed1, seed2); +} + + +static int math_randomseed (lua_State *L) { + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + if (lua_isnone(L, 1)) { + randseed(L, state); + } + else { + lua_Integer n1 = luaL_checkinteger(L, 1); + lua_Integer n2 = luaL_optinteger(L, 2, 0); + setseed(L, state->s, n1, n2); + } + return 2; /* return seeds */ +} + + +static const luaL_Reg randfuncs[] = { + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Register the random functions and initialize their state. +*/ +static void setrandfunc (lua_State *L) { + RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); + randseed(L, state); /* initialize with a "random" seed */ + lua_pop(L, 2); /* remove pushed seeds */ + luaL_setfuncs(L, randfuncs, 1); +} + +/* }================================================================== */ + + +/* +** {================================================================== +** Deprecated functions (for compatibility only) +** =================================================================== +*/ +#if defined(LUA_COMPAT_MATHLIB) + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number y = luaL_checknumber(L, 2); + lua_pushnumber(L, l_mathop(pow)(x, y)); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); + return 1; +} + +#endif +/* }================================================================== */ + + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"tointeger", math_toint}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"ult", math_ult}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"rad", math_rad}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tan", math_tan}, + {"type", math_type}, +#if defined(LUA_COMPAT_MATHLIB) + {"atan2", math_atan}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, +#endif + /* placeholders */ + {"random", NULL}, + {"randomseed", NULL}, + {"pi", NULL}, + {"huge", NULL}, + {"maxinteger", NULL}, + {"mininteger", NULL}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUAMOD_API int luaopen_math (lua_State *L) { + luaL_newlib(L, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, (lua_Number)HUGE_VAL); + lua_setfield(L, -2, "huge"); + lua_pushinteger(L, LUA_MAXINTEGER); + lua_setfield(L, -2, "maxinteger"); + lua_pushinteger(L, LUA_MININTEGER); + lua_setfield(L, -2, "mininteger"); + setrandfunc(L); + return 1; +} + +/* +** $Id: loadlib.c $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Windows, and a stub for other +** systems. +*/ + +#define loadlib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +/* +** LUA_IGMARK is a mark to ignore all before it when building the +** luaopen_ function name. +*/ +#if !defined (LUA_IGMARK) +#define LUA_IGMARK "-" +#endif + + +/* +** LUA_CSUBSEP is the character that replaces dots in submodule names +** when searching for a C loader. +** LUA_LSUBSEP is the character that replaces dots in submodule names +** when searching for a Lua loader. +*/ +#if !defined(LUA_CSUBSEP) +#define LUA_CSUBSEP LUA_DIRSEP +#endif + +#if !defined(LUA_LSUBSEP) +#define LUA_LSUBSEP LUA_DIRSEP +#endif + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +/* +** key for table in the registry that keeps handles +** for all loaded C libraries +*/ +static const char *const CLIBS = "_CLIBS"; + +#define LIB_FAIL "open" + + +#define setprogdir(L) ((void)0) + + +/* +** Special type equivalent to '(void*)' for functions in gcc +** (to suppress warnings when converting function pointers) +*/ +typedef void (*voidf)(void); + + +/* +** system-dependent functions +*/ + +/* +** unload library 'lib' +*/ +static void lsys_unloadlib (void *lib); + +/* +** load C library in file 'path'. If 'seeglb', load with all names in +** the library global. +** Returns the library; in case of error, returns NULL plus an +** error string in the stack. +*/ +static void *lsys_load (lua_State *L, const char *path, int seeglb); + +/* +** Try to find a function named 'sym' in library 'lib'. +** Returns the function; in case of error, returns NULL plus an +** error string in the stack. +*/ +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); + + + + +#if defined(LUA_USE_DLOPEN) /* { */ +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (lua_CFunction)(p)) +#else +#define cast_func(p) ((lua_CFunction)(p)) +#endif + + +static void lsys_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); + if (l_unlikely(lib == NULL)) + lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_func(dlsym(lib, sym)); + if (l_unlikely(f == NULL)) + lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) /* }{ */ +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +/* +** optional flags for LoadLibraryEx +*/ +#if !defined(LUA_LLE_FLAGS) +#define LUA_LLE_FLAGS 0 +#endif + + +#undef setprogdir + + +/* +** Replace in the path (on the top of the stack) any occurrence +** of LUA_EXEC_DIR with the executable's path. +*/ +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; /* cut name on the last '\\' to get the path */ + luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void lsys_unloadlib (void *lib) { + FreeLibrary((HMODULE)lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); + (void)(seeglb); /* not used: symbols are 'global' by default */ + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + +#else /* }{ */ +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void lsys_unloadlib (void *lib) { + (void)(lib); /* not used */ +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + (void)(path); (void)(seeglb); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + (void)(lib); (void)(sym); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif /* } */ + + +/* +** {================================================================== +** Set Paths +** =================================================================== +*/ + +/* +** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment +** variables that Lua check to set its paths. +*/ +#if !defined(LUA_PATH_VAR) +#define LUA_PATH_VAR "LUA_PATH" +#endif + +#if !defined(LUA_CPATH_VAR) +#define LUA_CPATH_VAR "LUA_CPATH" +#endif + + + +/* +** return registry.LUA_NOENV as a boolean +*/ +static int noenv (lua_State *L) { + int b; + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + b = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + return b; +} + + +/* +** Set a path +*/ +static void setpath (lua_State *L, const char *fieldname, + const char *envname, + const char *dft) { + const char *dftmark; + const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); + const char *path = getenv(nver); /* try versioned name */ + if (path == NULL) /* no versioned environment variable? */ + path = getenv(envname); /* try unversioned name */ + if (path == NULL || noenv(L)) /* no environment variable? */ + lua_pushstring(L, dft); /* use default */ + else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) + lua_pushstring(L, path); /* nothing to change */ + else { /* path contains a ";;": insert default path in its place */ + size_t len = strlen(path); + luaL_Buffer b; + luaL_buffinit(L, &b); + if (path < dftmark) { /* is there a prefix before ';;'? */ + luaL_addlstring(&b, path, dftmark - path); /* add it */ + luaL_addchar(&b, *LUA_PATH_SEP); + } + luaL_addstring(&b, dft); /* add default */ + if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ + luaL_addchar(&b, *LUA_PATH_SEP); + luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark); + } + luaL_pushresult(&b); + } + setprogdir(L); + lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ + lua_pop(L, 1); /* pop versioned variable name ('nver') */ +} + +/* }================================================================== */ + + +/* +** return registry.CLIBS[path] +*/ +static void *checkclib (lua_State *L, const char *path) { + void *plib; + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_getfield(L, -1, path); + plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ + lua_pop(L, 2); /* pop CLIBS table and 'plib' */ + return plib; +} + + +/* +** registry.CLIBS[path] = plib -- for queries +** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_pushlightuserdata(L, plib); + lua_pushvalue(L, -1); + lua_setfield(L, -3, path); /* CLIBS[path] = plib */ + lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ + lua_pop(L, 1); /* pop CLIBS table */ +} + + +/* +** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib +** handles in list CLIBS +*/ +static int gctm (lua_State *L) { + lua_Integer n = luaL_len(L, 1); + for (; n >= 1; n--) { /* for each handle, in reverse order */ + lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ + lsys_unloadlib(lua_touserdata(L, -1)); + lua_pop(L, 1); /* pop handle */ + } + return 0; +} + + + +/* error codes for 'lookforfunc' */ +#define ERRLIB 1 +#define ERRFUNC 2 + +/* +** Look for a C function named 'sym' in a dynamically loaded library +** 'path'. +** First, check whether the library is already loaded; if not, try +** to load it. +** Then, if 'sym' is '*', return true (as library has been loaded). +** Otherwise, look for symbol 'sym' in the library and push a +** C function with that symbol. +** Return 0 and 'true' or a function in the stack; in case of +** errors, return an error code and an error message in the stack. +*/ +static int lookforfunc (lua_State *L, const char *path, const char *sym) { + void *reg = checkclib(L, path); /* check loaded C libraries */ + if (reg == NULL) { /* must load library? */ + reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ + if (reg == NULL) return ERRLIB; /* unable to load library */ + addtoclib(L, path, reg); + } + if (*sym == '*') { /* loading only library (no function)? */ + lua_pushboolean(L, 1); /* return 'true' */ + return 0; /* no errors */ + } + else { + lua_CFunction f = lsys_sym(L, reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); /* else create new function */ + return 0; /* no errors */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = lookforfunc(L, path, init); + if (l_likely(stat == 0)) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + luaL_pushfail(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return fail, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +/* +** Get the next name in '*path' = 'name1;name2;name3;...', changing +** the ending ';' to '\0' to create a zero-terminated string. Return +** NULL when list ends. +*/ +static const char *getnextfilename (char **path, char *end) { + char *sep; + char *name = *path; + if (name == end) + return NULL; /* no more names */ + else if (*name == '\0') { /* from previous iteration? */ + *name = *LUA_PATH_SEP; /* restore separator */ + name++; /* skip it */ + } + sep = strchr(name, *LUA_PATH_SEP); /* find next separator */ + if (sep == NULL) /* separator not found? */ + sep = end; /* name goes until the end */ + *sep = '\0'; /* finish file name */ + *path = sep; /* will start next search from here */ + return name; +} + + +/* +** Given a path such as ";blabla.so;blublu.so", pushes the string +** +** no file 'blabla.so' +** no file 'blublu.so' +*/ +static void pusherrornotfound (lua_State *L, const char *path) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addstring(&b, "no file '"); + luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '"); + luaL_addstring(&b, "'"); + luaL_pushresult(&b); +} + + +static const char *searchpath (lua_State *L, const char *name, + const char *path, + const char *sep, + const char *dirsep) { + luaL_Buffer buff; + char *pathname; /* path with name inserted */ + char *endpathname; /* its end */ + const char *filename; + /* separator is non-empty and appears in 'name'? */ + if (*sep != '\0' && strchr(name, *sep) != NULL) + name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ + luaL_buffinit(L, &buff); + /* add path to the buffer, replacing marks ('?') with the file name */ + luaL_addgsub(&buff, path, LUA_PATH_MARK, name); + luaL_addchar(&buff, '\0'); + pathname = luaL_buffaddr(&buff); /* writable list of file names */ + endpathname = pathname + luaL_bufflen(&buff) - 1; + while ((filename = getnextfilename(&pathname, endpathname)) != NULL) { + if (readable(filename)) /* does file exist and is readable? */ + return lua_pushstring(L, filename); /* save and return name */ + } + luaL_pushresult(&buff); /* push path to create error message */ + pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ + return NULL; /* not found */ +} + + +static int ll_searchpath (lua_State *L) { + const char *f = searchpath(L, luaL_checkstring(L, 1), + luaL_checkstring(L, 2), + luaL_optstring(L, 3, "."), + luaL_optstring(L, 4, LUA_DIRSEP)); + if (f != NULL) return 1; + else { /* error message is on top of the stack */ + luaL_pushfail(L); + lua_insert(L, -2); + return 2; /* return fail + error message */ + } +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname, + const char *dirsep) { + const char *path; + lua_getfield(L, lua_upvalueindex(1), pname); + path = lua_tostring(L, -1); + if (l_unlikely(path == NULL)) + luaL_error(L, "'package.%s' must be a string", pname); + return searchpath(L, name, path, ".", dirsep); +} + + +static int checkload (lua_State *L, int stat, const char *filename) { + if (l_likely(stat)) { /* module loaded successfully? */ + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; /* return open function and file name */ + } + else + return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int searcher_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path", LUA_LSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); +} + + +/* +** Try to find a load function for module 'modname' at file 'filename'. +** First, change '.' to '_' in 'modname'; then, if 'modname' has +** the form X-Y (that is, it has an "ignore mark"), build a function +** name "luaopen_X" and look for it. (For compatibility, if that +** fails, it also tries "luaopen_Y".) If there is no ignore mark, +** look for a function named "luaopen_modname". +*/ +static int loadfunc (lua_State *L, const char *filename, const char *modname) { + const char *openfunc; + const char *mark; + modname = luaL_gsub(L, modname, ".", LUA_OFSEP); + mark = strchr(modname, *LUA_IGMARK); + if (mark) { + int stat; + openfunc = lua_pushlstring(L, modname, mark - modname); + openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); + stat = lookforfunc(L, filename, openfunc); + if (stat != ERRFUNC) return stat; + modname = mark + 1; /* else go ahead and try old-style name */ + } + openfunc = lua_pushfstring(L, LUA_POF"%s", modname); + return lookforfunc(L, filename, openfunc); +} + + +static int searcher_C (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (loadfunc(L, filename, name) == 0), filename); +} + + +static int searcher_Croot (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* root not found */ + if ((stat = loadfunc(L, filename, name)) != 0) { + if (stat != ERRFUNC) + return checkload(L, 0, filename); /* real error */ + else { /* open function not found */ + lua_pushfstring(L, "no module '%s' in file '%s'", name, filename); + return 1; + } + } + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; +} + + +static int searcher_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ + lua_pushfstring(L, "no field package.preload['%s']", name); + return 1; + } + else { + lua_pushliteral(L, ":preload:"); + return 2; + } +} + + +static void findloader (lua_State *L, const char *name) { + int i; + luaL_Buffer msg; /* to build error message */ + /* push 'package.searchers' to index 3 in the stack */ + if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers") + != LUA_TTABLE)) + luaL_error(L, "'package.searchers' must be a table"); + luaL_buffinit(L, &msg); + /* iterate over available searchers to find a loader */ + for (i = 1; ; i++) { + luaL_addstring(&msg, "\n\t"); /* error-message prefix */ + if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ + lua_pop(L, 1); /* remove nil */ + luaL_buffsub(&msg, 2); /* remove prefix */ + luaL_pushresult(&msg); /* create error message */ + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); + } + lua_pushstring(L, name); + lua_call(L, 1, 2); /* call it */ + if (lua_isfunction(L, -2)) /* did it find a loader? */ + return; /* module loader found */ + else if (lua_isstring(L, -2)) { /* searcher returned error message? */ + lua_pop(L, 1); /* remove extra return */ + luaL_addvalue(&msg); /* concatenate error message */ + } + else { /* no error message */ + lua_pop(L, 2); /* remove both returns */ + luaL_buffsub(&msg, 2); /* remove prefix */ + } + } +} + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_settop(L, 1); /* LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, 2, name); /* LOADED[name] */ + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded */ + /* else must load package */ + lua_pop(L, 1); /* remove 'getfield' result */ + findloader(L, name); + lua_rotate(L, -2, 1); /* function <-> loader data */ + lua_pushvalue(L, 1); /* name is 1st argument to module loader */ + lua_pushvalue(L, -3); /* loader data is 2nd argument */ + /* stack: ...; loader data; loader function; mod. name; loader data */ + lua_call(L, 2, 1); /* run loader to load module */ + /* stack: ...; loader data; result from loader */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + else + lua_pop(L, 1); /* pop nil */ + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_copy(L, -1, -2); /* replace loader result */ + lua_setfield(L, 2, name); /* LOADED[name] = true */ + } + lua_rotate(L, -2, 1); /* loader data <-> module result */ + return 2; /* return module result and loader data */ +} + +/* }====================================================== */ + + + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"searchpath", ll_searchpath}, + /* placeholders */ + {"preload", NULL}, + {"cpath", NULL}, + {"path", NULL}, + {"searchers", NULL}, + {"loaded", NULL}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"require", ll_require}, + {NULL, NULL} +}; + + +static void createsearcherstable (lua_State *L) { + static const lua_CFunction searchers[] = { + searcher_preload, + searcher_Lua, + searcher_C, + searcher_Croot, + NULL + }; + int i; + /* create 'searchers' table */ + lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); + /* fill it with predefined searchers */ + for (i=0; searchers[i] != NULL; i++) { + lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ + lua_pushcclosure(L, searchers[i], 1); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ +} + + +/* +** create table CLIBS to keep track of loaded C libraries, +** setting a finalizer to close all libraries when closing state. +*/ +static void createclibstable (lua_State *L) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_createtable(L, 0, 1); /* create metatable for CLIBS */ + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ + lua_setmetatable(L, -2); +} + + +LUAMOD_API int luaopen_package (lua_State *L) { + createclibstable(L); + luaL_newlib(L, pk_funcs); /* create 'package' table */ + createsearcherstable(L); + /* set paths */ + setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); + setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" + LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); + lua_setfield(L, -2, "config"); + /* set field 'loaded' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_setfield(L, -2, "loaded"); + /* set field 'preload' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + lua_setfield(L, -2, "preload"); + lua_pushglobaltable(L); + lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ + luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ + lua_pop(L, 1); /* pop global table */ + return 1; /* return 'package' table */ +} + +/* +** $Id: loslib.c $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + +#define loslib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +/* +** {================================================================== +** List of valid conversion specifiers for the 'strftime' function; +** options are grouped by length; group of length 2 start with '||'. +** =================================================================== +*/ +#if !defined(LUA_STRFTIMEOPTIONS) /* { */ + +#if defined(LUA_USE_WINDOWS) +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ +#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" +#else /* C99 specification */ +#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ +#endif + +#endif /* } */ +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for time-related stuff +** =================================================================== +*/ + +/* +** type to represent time_t in Lua +*/ +#if !defined(LUA_NUMTIME) /* { */ + +#define l_timet lua_Integer +#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) +#define l_gettime(L,arg) luaL_checkinteger(L, arg) + +#else /* }{ */ + +#define l_timet lua_Number +#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) +#define l_gettime(L,arg) luaL_checknumber(L, arg) + +#endif /* } */ + + +#if !defined(l_gmtime) /* { */ +/* +** By default, Lua uses gmtime/localtime, except when POSIX is available, +** where it uses gmtime_r/localtime_r +*/ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_gmtime(t,r) gmtime_r(t,r) +#define l_localtime(t,r) localtime_r(t,r) + +#else /* }{ */ + +/* ISO C definitions */ +#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) +#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for 'tmpnam': +** By default, Lua uses tmpnam except when POSIX is available, where +** it uses mkstemp. +** =================================================================== +*/ +#if !defined(lua_tmpnam) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include + +#define LUA_TMPNAMBUFSIZE 32 + +#if !defined(LUA_TMPNAMTEMPLATE) +#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" +#endif + +#define lua_tmpnam(b,e) { \ + strcpy(b, LUA_TMPNAMTEMPLATE); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else /* }{ */ + +/* ISO C definitions */ +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } + +#endif /* } */ + +#endif /* } */ +/* }================================================================== */ + + +#if !defined(l_system) +#if defined(LUA_USE_IOS) +/* Despite claiming to be ISO C, iOS does not implement 'system'. */ +#define l_system(cmd) ((cmd) == NULL ? 0 : -1) +#else +#define l_system(cmd) system(cmd) /* default definition */ +#endif +#endif + + +static int os_execute (lua_State *L) { + const char *cmd = luaL_optstring(L, 1, NULL); + int stat; + errno = 0; + stat = l_system(cmd); + if (cmd != NULL) + return luaL_execresult(L, stat); + else { + lua_pushboolean(L, stat); /* true if there is a shell */ + return 1; + } +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return luaL_fileresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (l_unlikely(err)) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +/* +** About the overflow check: an overflow cannot occur when time +** is represented by a lua_Integer, because either lua_Integer is +** large enough to represent all int fields or it is not large enough +** to represent a time that cause a field to overflow. However, if +** times are represented as doubles and lua_Integer is int, then the +** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 +** to compute the year. +*/ +static void setfield (lua_State *L, const char *key, int value, int delta) { + #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) + if (l_unlikely(value > LUA_MAXINTEGER - delta)) + luaL_error(L, "field '%s' is out-of-bound", key); + #endif + lua_pushinteger(L, (lua_Integer)value + delta); + lua_setfield(L, -2, key); +} + + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + + +/* +** Set all fields from structure 'tm' in the table on top of the stack +*/ +static void setallfields (lua_State *L, struct tm *stm) { + setfield(L, "year", stm->tm_year, 1900); + setfield(L, "month", stm->tm_mon, 1); + setfield(L, "day", stm->tm_mday, 0); + setfield(L, "hour", stm->tm_hour, 0); + setfield(L, "min", stm->tm_min, 0); + setfield(L, "sec", stm->tm_sec, 0); + setfield(L, "yday", stm->tm_yday, 1); + setfield(L, "wday", stm->tm_wday, 1); + setboolfield(L, "isdst", stm->tm_isdst); +} + + +static int getboolfield (lua_State *L, const char *key) { + int res; + res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); /* get field and its type */ + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not an integer? */ + if (l_unlikely(t != LUA_TNIL)) /* some other value? */ + return luaL_error(L, "field '%s' is not an integer", key); + else if (l_unlikely(d < 0)) /* absent field; no default? */ + return luaL_error(L, "field '%s' missing in date table", key); + res = d; + } + else { + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) + return luaL_error(L, "field '%s' is out-of-bound", key); + res -= delta; + } + lua_pop(L, 1); + return (int)res; +} + + +static const char *checkoption (lua_State *L, const char *conv, + ptrdiff_t convlen, char *buff) { + const char *option = LUA_STRFTIMEOPTIONS; + int oplen = 1; /* length of options being checked */ + for (; *option != '\0' && oplen <= convlen; option += oplen) { + if (*option == '|') /* next block? */ + oplen++; /* will check options with next length (+1) */ + else if (memcmp(conv, option, oplen) == 0) { /* match? */ + memcpy(buff, conv, oplen); /* copy valid option to buffer */ + buff[oplen] = '\0'; + return conv + oplen; /* return next item */ + } + } + luaL_argerror(L, 1, + lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); + return conv; /* to avoid warnings */ +} + + +static time_t l_checktime (lua_State *L, int arg) { + l_timet t = l_gettime(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} + + +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + +static int os_date (lua_State *L) { + size_t slen; + const char *s = luaL_optlstring(L, 1, "%c", &slen); + time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); + const char *se = s + slen; /* 's' end */ + struct tm tmr, *stm; + if (*s == '!') { /* UTC? */ + stm = l_gmtime(&t, &tmr); + s++; /* skip '!' */ + } + else + stm = l_localtime(&t, &tmr); + if (stm == NULL) /* invalid date? */ + return luaL_error(L, + "date result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setallfields(L, stm); + } + else { + char cc[4]; /* buffer for individual conversion specifiers */ + luaL_Buffer b; + cc[0] = '%'; + luaL_buffinit(L, &b); + while (s < se) { + if (*s != '%') /* not a conversion specifier? */ + luaL_addchar(&b, *s++); + else { + size_t reslen; + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); + s++; /* skip '%' */ + s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_year = getfield(L, "year", -1, 1900); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_sec = getfield(L, "sec", 0, 0); + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + setallfields(L, &ts); /* update fields with normalized values */ + } + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + return luaL_error(L, + "time result cannot be represented in this installation"); + l_pushtime(L, t); + return 1; +} + + +static int os_difftime (lua_State *L) { + time_t t1 = l_checktime(L, 1); + time_t t2 = l_checktime(L, 2); + lua_pushnumber(L, (lua_Number)difftime(t1, t2)); + return 1; +} + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + int status; + if (lua_isboolean(L, 1)) + status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); + else + status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); + if (lua_toboolean(L, 2)) + lua_close(L); + if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ + return 0; +} + + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUAMOD_API int luaopen_os (lua_State *L) { + luaL_newlib(L, syslib); + return 1; +} + +/* +** $Id: lstrlib.c $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + +#define lstrlib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +/* +** maximum number of captures that a pattern can do during +** pattern-matching. This limit is arbitrary, but must fit in +** an unsigned char. +*/ +#if !defined(LUA_MAXCAPTURES) +#define LUA_MAXCAPTURES 32 +#endif + + +/* macro to 'unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + +/* +** Some sizes are better limited to fit in 'int', but must also fit in +** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) +*/ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +#define MAXSIZE \ + (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) + + + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, (lua_Integer)l); + return 1; +} + + +/* +** translate a relative initial string position +** (negative means back from end): clip result to [1, inf). +** The length of any string in Lua must fit in a lua_Integer, +** so there are no overflows in the casts. +** The inverted comparison avoids a possible overflow +** computing '-pos'. +*/ +static size_t posrelatI (lua_Integer pos, size_t len) { + if (pos > 0) + return (size_t)pos; + else if (pos == 0) + return 1; + else if (pos < -(lua_Integer)len) /* inverted comparison */ + return 1; /* clip to 1 */ + else return len + (size_t)pos + 1; +} + + +/* +** Gets an optional ending string position from argument 'arg', +** with default value 'def'. +** Negative means back from end: clip result to [0, len] +*/ +static size_t getendpos (lua_State *L, int arg, lua_Integer def, + size_t len) { + lua_Integer pos = luaL_optinteger(L, arg, def); + if (pos > (lua_Integer)len) + return len; + else if (pos >= 0) + return (size_t)pos; + else if (pos < -(lua_Integer)len) + return 0; + else return len + (size_t)pos + 1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t start = posrelatI(luaL_checkinteger(L, 2), l); + size_t end = getendpos(L, 3, -1, l); + if (start <= end) + lua_pushlstring(L, s + start - 1, (end - start) + 1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l, i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i = 0; i < l; i++) + p[i] = s[l - i - 1]; + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i MAXSIZE / n)) + return luaL_error(L, "resulting string too large"); + else { + size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, totallen); + while (n-- > 1) { /* first n-1 copies (followed by separator) */ + memcpy(p, s, l * sizeof(char)); p += l; + if (lsep > 0) { /* empty 'memcpy' is not that cheap */ + memcpy(p, sep, lsep * sizeof(char)); + p += lsep; + } + } + memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ + luaL_pushresultsize(&b, totallen); + } + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + lua_Integer pi = luaL_optinteger(L, 2, 1); + size_t posi = posrelatI(pi, l); + size_t pose = getendpos(L, 3, pi, l); + int n, i; + if (posi > pose) return 0; /* empty interval; return no values */ + if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; + luaL_checkstack(L, n, "string slice too long"); + for (i=0; iinit) { + state->init = 1; + luaL_buffinit(L, &state->B); + } + luaL_addlstring(&state->B, (const char *)b, size); + return 0; +} + + +static int str_dump (lua_State *L) { + struct str_Writer state; + int strip = lua_toboolean(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); /* ensure function is on the top of the stack */ + state.init = 0; + if (l_unlikely(lua_dump(L, writer, &state, strip) != 0)) + return luaL_error(L, "unable to dump given function"); + luaL_pushresult(&state.B); + return 1; +} + + + +/* +** {====================================================== +** METAMETHODS +** ======================================================= +*/ + +#if defined(LUA_NOCVTS2N) /* { */ + +/* no coercion from strings to numbers */ + +static const luaL_Reg stringmetamethods[] = { + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#else /* }{ */ + +static int tonum (lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */ + lua_pushvalue(L, arg); + return 1; + } + else { /* check whether it is a numerical string */ + size_t len; + const char *s = lua_tolstring(L, arg, &len); + return (s != NULL && lua_stringtonumber(L, s) == len + 1); + } +} + + +static void trymt (lua_State *L, const char *mtname) { + lua_settop(L, 2); /* back to the original arguments */ + if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || + !luaL_getmetafield(L, 2, mtname))) + luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, + luaL_typename(L, -2), luaL_typename(L, -1)); + lua_insert(L, -3); /* put metamethod before arguments */ + lua_call(L, 2, 1); /* call metamethod */ +} + + +static int arith (lua_State *L, int op, const char *mtname) { + if (tonum(L, 1) && tonum(L, 2)) + lua_arith(L, op); /* result will be on the top */ + else + trymt(L, mtname); + return 1; +} + + +static int arith_add (lua_State *L) { + return arith(L, LUA_OPADD, "__add"); +} + +static int arith_sub (lua_State *L) { + return arith(L, LUA_OPSUB, "__sub"); +} + +static int arith_mul (lua_State *L) { + return arith(L, LUA_OPMUL, "__mul"); +} + +static int arith_mod (lua_State *L) { + return arith(L, LUA_OPMOD, "__mod"); +} + +static int arith_pow (lua_State *L) { + return arith(L, LUA_OPPOW, "__pow"); +} + +static int arith_div (lua_State *L) { + return arith(L, LUA_OPDIV, "__div"); +} + +static int arith_idiv (lua_State *L) { + return arith(L, LUA_OPIDIV, "__idiv"); +} + +static int arith_unm (lua_State *L) { + return arith(L, LUA_OPUNM, "__unm"); +} + + +static const luaL_Reg stringmetamethods[] = { + {"__add", arith_add}, + {"__sub", arith_sub}, + {"__mul", arith_mul}, + {"__mod", arith_mod}, + {"__pow", arith_pow}, + {"__div", arith_div}, + {"__idiv", arith_idiv}, + {"__unm", arith_unm}, + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#endif /* } */ + +/* }====================================================== */ + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end ('\0') of source string */ + const char *p_end; /* end ('\0') of pattern */ + lua_State *L; + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + unsigned char level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + ptrdiff_t len; + } capture[LUA_MAXCAPTURES]; +} MatchState; + + +/* recursive function */ +static const char *match (MatchState *ms, const char *s, const char *p); + + +/* maximum recursion depth for 'match' */ +#if !defined(MAXCCALLS) +#define MAXCCALLS 200 +#endif + + +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l_unlikely(l < 0 || l >= ms->level || + ms->capture[l].len == CAP_UNFINISHED)) + return luaL_error(ms->L, "invalid capture index %%%d", l + 1); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (ends with '%%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a ']' */ + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (missing ']')"); + if (*(p++) == L_ESC && p < ms->p_end) + p++; /* skip escapes (e.g. '%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'g' : res = isgraph(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; /* deprecated option */ + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the '^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (MatchState *ms, const char *s, const char *p, + const char *ep) { + if (s >= ms->src_end) + return 0; + else { + int c = uchar(*s); + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } + } +} + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (l_unlikely(p >= ms->p_end - 1)) + luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while (singlematch(ms, s + i, p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (singlematch(ms, s, p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + if (l_unlikely(ms->matchdepth-- == 0)) + luaL_error(ms->L, "pattern too complex"); + init: /* using goto to optimize tail recursion */ + if (p != ms->p_end) { /* end of pattern? */ + switch (*p) { + case '(': { /* start capture */ + if (*(p + 1) == ')') /* position capture? */ + s = start_capture(ms, s, p + 2, CAP_POSITION); + else + s = start_capture(ms, s, p + 1, CAP_UNFINISHED); + break; + } + case ')': { /* end capture */ + s = end_capture(ms, s, p + 1); + break; + } + case '$': { + if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ + goto dflt; /* no; go to default */ + s = (s == ms->src_end) ? s : NULL; /* check end of string */ + break; + } + case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ + switch (*(p + 1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p + 2); + if (s != NULL) { + p += 4; goto init; /* return match(ms, s, p + 4); */ + } /* else fail (s == NULL) */ + break; + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (l_unlikely(*p != '[')) + luaL_error(ms->L, "missing '[' after '%%f' in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s - 1); + if (!matchbracketclass(uchar(previous), p, ep - 1) && + matchbracketclass(uchar(*s), p, ep - 1)) { + p = ep; goto init; /* return match(ms, s, ep); */ + } + s = NULL; /* match failed */ + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p + 1))); + if (s != NULL) { + p += 2; goto init; /* return match(ms, s, p + 2) */ + } + break; + } + default: goto dflt; + } + break; + } + default: dflt: { /* pattern class plus optional suffix */ + const char *ep = classend(ms, p); /* points to optional suffix */ + /* does not match at least once? */ + if (!singlematch(ms, s, p, ep)) { + if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ + p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ + } + else /* '+' or no suffix */ + s = NULL; /* fail */ + } + else { /* matched once */ + switch (*ep) { /* handle optional suffix */ + case '?': { /* optional */ + const char *res; + if ((res = match(ms, s + 1, ep + 1)) != NULL) + s = res; + else { + p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ + } + break; + } + case '+': /* 1 or more repetitions */ + s++; /* 1 match already done */ + /* FALLTHROUGH */ + case '*': /* 0 or more repetitions */ + s = max_expand(ms, s, p, ep); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand(ms, s, p, ep); + break; + default: /* no suffix */ + s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ + } + } + break; + } + } + } + ms->matchdepth++; + return s; +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ + else { + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct 'l1' and 's1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +/* +** get information about the i-th capture. If there are no captures +** and 'i==0', return information about the whole match, which +** is the range 's'..'e'. If the capture is a string, return +** its length and put its address in '*cap'. If it is an integer +** (a position), push it on the stack and return CAP_POSITION. +*/ +static size_t get_onecapture (MatchState *ms, int i, const char *s, + const char *e, const char **cap) { + if (i >= ms->level) { + if (l_unlikely(i != 0)) + luaL_error(ms->L, "invalid capture index %%%d", i + 1); + *cap = s; + return e - s; + } + else { + ptrdiff_t capl = ms->capture[i].len; + *cap = ms->capture[i].init; + if (l_unlikely(capl == CAP_UNFINISHED)) + luaL_error(ms->L, "unfinished capture"); + else if (capl == CAP_POSITION) + lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); + return capl; + } +} + + +/* +** Push the i-th capture on the stack. +*/ +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + const char *cap; + ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); + if (l != CAP_POSITION) + lua_pushlstring(ms->L, cap, l); + /* else position was already pushed */ +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +/* check whether pattern has no special characters */ +static int nospecials (const char *p, size_t l) { + size_t upto = 0; + do { + if (strpbrk(p + upto, SPECIALS)) + return 0; /* pattern has a special character */ + upto += strlen(p + upto) + 1; /* may have more after \0 */ + } while (upto <= l); + return 1; /* no special chars found */ +} + + +static void prepstate (MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; +} + + +static void reprepstate (MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + +static int str_find_aux (lua_State *L, int find) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + if (init > ls) { /* start after string's end? */ + luaL_pushfail(L); /* cannot find anything */ + return 1; + } + /* explicit request or no special characters? */ + if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { + /* do a plain search */ + const char *s2 = lmemfind(s + init, ls - init, p, lp); + if (s2) { + lua_pushinteger(L, (s2 - s) + 1); + lua_pushinteger(L, (s2 - s) + lp); + return 2; + } + } + else { + MatchState ms; + const char *s1 = s + init; + int anchor = (*p == '^'); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, s, ls, p, lp); + do { + const char *res; + reprepstate(&ms); + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, (s1 - s) + 1); /* start */ + lua_pushinteger(L, res - s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + luaL_pushfail(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + const char *lastmatch; /* end of last match */ + MatchState ms; /* match state */ +} GMatchState; + + +static int gmatch_aux (lua_State *L) { + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); + const char *src; + gm->ms.L = L; + for (src = gm->src; src <= gm->ms.src_end; src++) { + const char *e; + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { + gm->src = gm->lastmatch = e; + return push_captures(&gm->ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + GMatchState *gm; + lua_settop(L, 2); /* keep strings on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0); + if (init > ls) /* start after string's end? */ + init = ls + 1; /* avoid overflows in 's + init' */ + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s + init; gm->p = p; gm->lastmatch = NULL; + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l; + lua_State *L = ms->L; + const char *news = lua_tolstring(L, 3, &l); + const char *p; + while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { + luaL_addlstring(b, news, p - news); + p++; /* skip ESC */ + if (*p == L_ESC) /* '%%' */ + luaL_addchar(b, *p); + else if (*p == '0') /* '%0' */ + luaL_addlstring(b, s, e - s); + else if (isdigit(uchar(*p))) { /* '%n' */ + const char *cap; + ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); + if (resl == CAP_POSITION) + luaL_addvalue(b); /* add position to accumulated result */ + else + luaL_addlstring(b, cap, resl); + } + else + luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); + l -= p + 1 - news; + news = p + 1; + } + luaL_addlstring(b, news, l); +} + + +/* +** Add the replacement value to the string buffer 'b'. +** Return true if the original string was changed. (Function calls and +** table indexing resulting in nil or false do not change the subject.) +*/ +static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e, int tr) { + lua_State *L = ms->L; + switch (tr) { + case LUA_TFUNCTION: { /* call the function */ + int n; + lua_pushvalue(L, 3); /* push the function */ + n = push_captures(ms, s, e); /* all captures as arguments */ + lua_call(L, n, 1); /* call it */ + break; + } + case LUA_TTABLE: { /* index the table */ + push_onecapture(ms, 0, s, e); /* first capture is the index */ + lua_gettable(L, 3); + break; + } + default: { /* LUA_TNUMBER or LUA_TSTRING */ + add_s(ms, b, s, e); /* add value to the buffer */ + return 1; /* something changed */ + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); /* remove value */ + luaL_addlstring(b, s, e - s); /* keep original text */ + return 0; /* no changes */ + } + else if (l_unlikely(!lua_isstring(L, -1))) + return luaL_error(L, "invalid replacement value (a %s)", + luaL_typename(L, -1)); + else { + luaL_addvalue(b); /* add result to accumulator */ + return 1; /* something changed */ + } +} + + +static int str_gsub (lua_State *L) { + size_t srcl, lp; + const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ + const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ + const char *lastmatch = NULL; /* end of last match */ + int tr = lua_type(L, 3); /* replacement type */ + lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ + int anchor = (*p == '^'); + lua_Integer n = 0; /* replacement count */ + int changed = 0; /* change flag */ + MatchState ms; + luaL_Buffer b; + luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table"); + luaL_buffinit(L, &b); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, src, srcl, p, lp); + while (n < max_s) { + const char *e; + reprepstate(&ms); /* (re)prepare state for new match */ + if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ + n++; + changed = add_value(&ms, &b, src, e, tr) | changed; + src = lastmatch = e; + } + else if (src < ms.src_end) /* otherwise, skip one character */ + luaL_addchar(&b, *src++); + else break; /* end of subject */ + if (anchor) break; + } + if (!changed) /* no changes? */ + lua_pushvalue(L, 1); /* return original string */ + else { /* something changed */ + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); /* create and return new string */ + } + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** STRING FORMAT +** ======================================================= +*/ + +#if !defined(lua_number2strx) /* { */ + +/* +** Hexadecimal floating-point formatter +*/ + +#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) + + +/* +** Number of bits that goes into the first digit. It can be any value +** between 1 and 4; the following definition tries to align the number +** to nibble boundaries by making what is left after that first digit a +** multiple of 4. +*/ +#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1) + + +/* +** Add integer part of 'x' to buffer and return new 'x' +*/ +static lua_Number adddigit (char *buff, int n, lua_Number x) { + lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ + int d = (int)dd; + buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + return x - dd; /* return what is left */ +} + + +static int num2straux (char *buff, int sz, lua_Number x) { + /* if 'inf' or 'NaN', format it like '%g' */ + if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) + return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); + else if (x == 0) { /* can be -0... */ + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); + } + else { + int e; + lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ + int n = 0; /* character count */ + if (m < 0) { /* is number negative? */ + buff[n++] = '-'; /* add sign */ + m = -m; /* make it positive */ + } + buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ + m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ + e -= L_NBFD; /* this digit goes before the radix point */ + if (m > 0) { /* more digits? */ + buff[n++] = lua_getlocaledecpoint(); /* add radix point */ + do { /* add as many digits as needed */ + m = adddigit(buff, n++, m * 16); + } while (m > 0); + } + n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + lua_assert(n < sz); + return n; + } +} + + +static int lua_number2strx (lua_State *L, char *buff, int sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); + if (fmt[SIZELENMOD] == 'A') { + int i; + for (i = 0; i < n; i++) + buff[i] = toupper(uchar(buff[i])); + } + else if (l_unlikely(fmt[SIZELENMOD] != 'a')) + return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; +} + +#endif /* } */ + + +/* +** Maximum size for items formatted with '%f'. This size is produced +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1, adding some extra, 110) +*/ +#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP)) + + +/* +** All formats except '%f' do not need that large limit. The other +** float formats use exponents, so that they fit in the 99 limit for +** significant digits; 's' for large strings and 'q' add items directly +** to the buffer; all integer formats also fit in the 99 limit. The +** worst case are floats: they may need 99 significant digits, plus +** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120. +*/ +#define MAX_ITEM 120 + + +/* valid flags in a format specification */ +#if !defined(L_FMTFLAGSF) + +/* valid flags for a, A, e, E, f, F, g, and G conversions */ +#define L_FMTFLAGSF "-+#0 " + +/* valid flags for o, x, and X conversions */ +#define L_FMTFLAGSX "-#0" + +/* valid flags for d and i conversions */ +#define L_FMTFLAGSI "-+0 " + +/* valid flags for u conversions */ +#define L_FMTFLAGSU "-0" + +/* valid flags for c, p, and s conversions */ +#define L_FMTFLAGSC "-" + +#endif + + +/* +** Maximum size of each format specification (such as "%-099.99d"): +** Initial '%', flags (up to 5), width (2), period, precision (2), +** length modifier (8), conversion specifier, and final '\0', plus some +** extra. +*/ +#define MAX_FORMAT 32 + + +static void addquoted (luaL_Buffer *b, const char *s, size_t len) { + luaL_addchar(b, '"'); + while (len--) { + if (*s == '"' || *s == '\\' || *s == '\n') { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + } + else if (iscntrl(uchar(*s))) { + char buff[10]; + if (!isdigit(uchar(*(s+1)))) + l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); + else + l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); + luaL_addstring(b, buff); + } + else + luaL_addchar(b, *s); + s++; + } + luaL_addchar(b, '"'); +} + + +/* +** Serialize a floating-point number in such a way that it can be +** scanned back by Lua. Use hexadecimal format for "common" numbers +** (to preserve precision); inf, -inf, and NaN are handled separately. +** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.) +*/ +static int quotefloat (lua_State *L, char *buff, lua_Number n) { + const char *s; /* for the fixed representations */ + if (n == (lua_Number)HUGE_VAL) /* inf? */ + s = "1e9999"; + else if (n == -(lua_Number)HUGE_VAL) /* -inf? */ + s = "-1e9999"; + else if (n != n) /* NaN? */ + s = "(0/0)"; + else { /* format number as hexadecimal */ + int nb = lua_number2strx(L, buff, MAX_ITEM, + "%" LUA_NUMBER_FRMLEN "a", n); + /* ensures that 'buff' string uses a dot as the radix character */ + if (memchr(buff, '.', nb) == NULL) { /* no dot? */ + char point = lua_getlocaledecpoint(); /* try locale point */ + char *ppoint = (char *)memchr(buff, point, nb); + if (ppoint) *ppoint = '.'; /* change it to a dot */ + } + return nb; + } + /* for the fixed representations */ + return l_sprintf(buff, MAX_ITEM, "%s", s); +} + + +static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { + switch (lua_type(L, arg)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + addquoted(b, s, len); + break; + } + case LUA_TNUMBER: { + char *buff = luaL_prepbuffsize(b, MAX_ITEM); + int nb; + if (!lua_isinteger(L, arg)) /* float? */ + nb = quotefloat(L, buff, lua_tonumber(L, arg)); + else { /* integers */ + lua_Integer n = lua_tointeger(L, arg); + const char *format = (n == LUA_MININTEGER) /* corner case? */ + ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */ + : LUA_INTEGER_FMT; /* else use default format */ + nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); + } + luaL_addsize(b, nb); + break; + } + case LUA_TNIL: case LUA_TBOOLEAN: { + luaL_tolstring(L, arg, NULL); + luaL_addvalue(b); + break; + } + default: { + luaL_argerror(L, arg, "value has no literal form"); + } + } +} + + +static const char *get2digits (const char *s) { + if (isdigit(uchar(*s))) { + s++; + if (isdigit(uchar(*s))) s++; /* (2 digits at most) */ + } + return s; +} + + +/* +** Check whether a conversion specification is valid. When called, +** first character in 'form' must be '%' and last character must +** be a valid conversion specifier. 'flags' are the accepted flags; +** 'precision' signals whether to accept a precision. +*/ +static void checkformat (lua_State *L, const char *form, const char *flags, + int precision) { + const char *spec = form + 1; /* skip '%' */ + spec += strspn(spec, flags); /* skip flags */ + if (*spec != '0') { /* a width cannot start with '0' */ + spec = get2digits(spec); /* skip width */ + if (*spec == '.' && precision) { + spec++; + spec = get2digits(spec); /* skip precision */ + } + } + if (!isalpha(uchar(*spec))) /* did not go to the end? */ + luaL_error(L, "invalid conversion specification: '%s'", form); +} + + +/* +** Get a conversion specification and copy it to 'form'. +** Return the address of its last character. +*/ +static const char *getformat (lua_State *L, const char *strfrmt, + char *form) { + /* spans flags, width, and precision ('0' is included as a flag) */ + size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789."); + len++; /* adds following character (should be the specifier) */ + /* still needs space for '%', '\0', plus a length modifier */ + if (len >= MAX_FORMAT - 10) + luaL_error(L, "invalid format (too long)"); + *(form++) = '%'; + memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; +} + + +/* +** add length modifier into formats +*/ +static void addlenmod (char *form, const char *lenmod) { + size_t l = strlen(form); + size_t lm = strlen(lenmod); + char spec = form[l - 1]; + strcpy(form + l - 1, lenmod); + form[l + lm - 1] = spec; + form[l + lm] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + const char *flags; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format ('%...') */ + int maxitem = MAX_ITEM; /* maximum length for the result */ + char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ + int nb = 0; /* number of bytes in result */ + if (++arg > top) + return luaL_argerror(L, arg, "no value"); + strfrmt = getformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + checkformat(L, form, L_FMTFLAGSC, 0); + nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); + break; + } + case 'd': case 'i': + flags = L_FMTFLAGSI; + goto intcase; + case 'u': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': case 'x': case 'X': + flags = L_FMTFLAGSX; + intcase: { + lua_Integer n = luaL_checkinteger(L, arg); + checkformat(L, form, flags, 1); + addlenmod(form, LUA_INTEGER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); + break; + } + case 'a': case 'A': + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = lua_number2strx(L, buff, maxitem, form, + luaL_checknumber(L, arg)); + break; + case 'f': + maxitem = MAX_ITEMF; /* extra space for '%f' */ + buff = luaL_prepbuffsize(&b, maxitem); + /* FALLTHROUGH */ + case 'e': case 'E': case 'g': case 'G': { + lua_Number n = luaL_checknumber(L, arg); + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); + break; + } + case 'p': { + const void *p = lua_topointer(L, arg); + checkformat(L, form, L_FMTFLAGSC, 0); + if (p == NULL) { /* avoid calling 'printf' with argument NULL */ + p = "(null)"; /* result */ + form[strlen(form) - 1] = 's'; /* format it as a string */ + } + nb = l_sprintf(buff, maxitem, form, p); + break; + } + case 'q': { + if (form[2] != '\0') /* modifiers? */ + return luaL_error(L, "specifier '%%q' cannot have modifiers"); + addliteral(L, &b, arg); + break; + } + case 's': { + size_t l; + const char *s = luaL_tolstring(L, arg, &l); + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ + else { + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + checkformat(L, form, L_FMTFLAGSC, 1); + if (strchr(form, '.') == NULL && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } + else { /* format the string into 'buff' */ + nb = l_sprintf(buff, maxitem, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } + } + break; + } + default: { /* also treat cases 'pnLlh' */ + return luaL_error(L, "invalid conversion '%s' to 'format'", form); + } + } + lua_assert(nb < maxitem); + luaL_addsize(&b, nb); + } + } + luaL_pushresult(&b); + return 1; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUAL_PACKPADBYTE) +#define LUAL_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = {1}; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + int maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* single-precision floating-point numbers */ + Knumber, /* Lua "native" floating-point numbers */ + Kdouble, /* double-precision floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit (int c) { return '0' <= c && c <= '9'; } + +static int getnum (const char **fmt, int df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + int a = 0; + do { + a = a*10 + (*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size for integers. +*/ +static int getnumlimit (Header *h, const char **fmt, int df) { + int sz = getnum(fmt, df); + if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) + return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", + sz, MAXINTSIZE); + return sz; +} + + +/* +** Initialize Header +*/ +static void initheader (lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption (Header *h, const char **fmt, int *size) { + /* dummy structure to get native alignment requirements */ + struct cD { char c; union { LUAI_MAXALIGN; } u; }; + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': *size = sizeof(char); return Kint; + case 'B': *size = sizeof(char); return Kuint; + case 'h': *size = sizeof(short); return Kint; + case 'H': *size = sizeof(short); return Kuint; + case 'l': *size = sizeof(long); return Kint; + case 'L': *size = sizeof(long); return Kuint; + case 'j': *size = sizeof(lua_Integer); return Kint; + case 'J': *size = sizeof(lua_Integer); return Kuint; + case 'T': *size = sizeof(size_t); return Kuint; + case 'f': *size = sizeof(float); return Kfloat; + case 'n': *size = sizeof(lua_Number); return Knumber; + case 'd': *size = sizeof(double); return Kdouble; + case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; + case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; + case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; + case 'c': + *size = getnum(fmt, -1); + if (l_unlikely(*size == -1)) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': return Kzstr; + case 'x': *size = 1; return Kpadding; + case 'X': return Kpaddalign; + case ' ': break; + case '<': h->islittle = 1; break; + case '>': h->islittle = 0; break; + case '=': h->islittle = nativeendian.little; break; + case '!': { + const int maxalign = offsetof(struct cD, u); + h->maxalign = getnumlimit(h, fmt, maxalign); + break; + } + default: luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails (Header *h, size_t totalsize, + const char **fmt, int *psize, int *ntoalign) { + KOption opt = getoption(h, fmt, psize); + int align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint (luaL_Buffer *b, lua_Unsigned n, + int islittle, int size, int neg) { + char *buff = luaL_prepbuffsize(b, size); + int i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + luaL_addsize(b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian (char *dest, const char *src, + int size, int islittle) { + if (islittle == nativeendian.little) + memcpy(dest, src, size); + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int str_pack (lua_State *L) { + luaL_Buffer b; + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + int arg = 1; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(&b, (lua_Unsigned)n, h.islittle, size, 0); + break; + } + case Kfloat: { /* C float */ + float f = (float)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Knumber: { /* Lua float */ + lua_Number f = luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kdouble: { /* C double */ + double f = (double)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, len <= (size_t)size, arg, + "string longer than given size"); + luaL_addlstring(&b, s, len); /* add string */ + while (len++ < (size_t)size) /* pad extra space */ + luaL_addchar(&b, LUAL_PACKPADBYTE); + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= (int)sizeof(size_t) || + len < ((size_t)1 << (size * NB)), + arg, "string length does not fit in given size"); + packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + luaL_addlstring(&b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + luaL_addlstring(&b, s, len); + luaL_addchar(&b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: case Knop: + arg--; /* undo increment */ + break; + } + } + luaL_pushresult(&b); + return 1; +} + + +static int str_packsize (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, + "variable-length format"); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, + "format result too large"); + totalsize += size; + } + lua_pushinteger(L, (lua_Integer)totalsize); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint (lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } + else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask)) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int str_unpack (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); + size_t ld; + const char *data = luaL_checklstring(L, 2, &ld); + size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); + initheader(L, &h); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, + "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, size, + (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + float f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Knumber: { + lua_Number f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, f); + break; + } + case Kdouble: { + double f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, len); + pos += len; /* skip string */ + break; + } + case Kzstr: { + size_t len = strlen(data + pos); + luaL_argcheck(L, pos + len < ld, 2, + "unfinished string for format 'z'"); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: case Kpadding: case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, pos + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {"pack", str_pack}, + {"packsize", str_packsize}, + {"unpack", str_unpack}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + /* table to be metatable for strings */ + luaL_newlibtable(L, stringmetamethods); + luaL_setfuncs(L, stringmetamethods, 0); + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); /* copy table */ + lua_setmetatable(L, -2); /* set table as metatable for strings */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* get string library */ + lua_setfield(L, -2, "__index"); /* metatable.__index = string */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUAMOD_API int luaopen_string (lua_State *L) { + luaL_newlib(L, strlib); + createmetatable(L); + return 1; +} + +/* +** $Id: ltablib.c $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + +#define ltablib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ + + +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) + + +static int checkfield (lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab (lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } + else + luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ + } +} + + +static int tinsert (lua_State *L) { + lua_Integer pos; /* where to insert new element */ + lua_Integer e = aux_getn(L, 1, TAB_RW); + e = luaL_intop(+, e, 1); /* first empty element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ + /* check whether 'pos' is in [1, e] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2, + "position out of bounds"); + for (i = e; i > pos; i--) { /* move up elements */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to 'insert'"); + } + } + lua_seti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); + if (pos != size) /* validate 'pos' if given */ + /* check whether 'pos' is in [1, size + 1] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, + "position out of bounds"); + lua_geti(L, 1, pos); /* result = t[pos] */ + for ( ; pos < size; pos++) { + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ + } + lua_pushnil(L); + lua_seti(L, 1, pos); /* remove entry t[pos] */ + return 1; +} + + +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return destination table */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); + if (l_unlikely(!lua_isstring(L, -1))) + luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", + luaL_typename(L, -1), (LUAI_UACINT)i); + luaL_addvalue(b); +} + + +static int tconcat (lua_State *L) { + luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); + size_t lsep; + const char *sep = luaL_optlstring(L, 2, "", &lsep); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_optinteger(L, 4, last); + luaL_buffinit(L, &b); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); + } + if (i == last) /* add last value (if interval was not empty) */ + addfield(L, &b, i); + luaL_pushresult(&b); + return 1; +} + + +/* +** {====================================================== +** Pack/unpack +** ======================================================= +*/ + +static int tpack (lua_State *L) { + int i; + int n = lua_gettop(L); /* number of elements to pack */ + lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); + lua_pushinteger(L, n); + lua_setfield(L, 1, "n"); /* t.n = number of elements */ + return 1; /* return table */ +} + + +static int tunpack (lua_State *L) { + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); + if (i > e) return 0; /* empty range */ + n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + if (l_unlikely(n >= (unsigned int)INT_MAX || + !lua_checkstack(L, (int)(++n)))) + return luaL_error(L, "too many results to unpack"); + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Quicksort +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ + + +/* type for array indices */ +typedef unsigned int IdxT; + + +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) /* { */ + +#include + +/* size of 'e' measured in number of 'unsigned int's */ +#define sof(e) (sizeof(e) / sizeof(unsigned int)) + +/* +** Use 'time' and 'clock' as sources of "randomness". Because we don't +** know the types 'clock_t' and 'time_t', we cannot cast them to +** anything without risking overflows. A safe way to use their values +** is to copy them to an array of a known type and use the array values. +*/ +static unsigned int l_randomizePivot (void) { + clock_t c = clock(); + time_t t = time(NULL); + unsigned int buff[sof(c) + sof(t)]; + unsigned int i, rnd = 0; + memcpy(buff, &c, sof(c) * sizeof(unsigned int)); + memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); + for (i = 0; i < sof(buff); i++) + rnd += buff[i]; + return rnd; +} + +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2 (lua_State *L, IdxT i, IdxT j) { + lua_seti(L, 1, i); + lua_seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ +static int sort_comp (lua_State *L, int a, int b) { + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ + int res; + lua_pushvalue(L, 2); /* push function */ + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ + return res; + } +} + + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static IdxT partition (lua_State *L, IdxT lo, IdxT up) { + IdxT i = lo; /* will be incremented before first use */ + IdxT j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* next loop: repeat --j while P < a[j] */ + while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { + IdxT r4 = (up - lo) / 4; /* range/4 */ + IdxT p = rnd % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** Quicksort algorithm (recursive function) +*/ +static void auxsort (lua_State *L, IdxT lo, IdxT up, + unsigned int rnd) { + while (lo < up) { /* loop for tail recursion */ + IdxT p; /* Pivot index */ + IdxT n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + lua_geti(L, 1, lo); + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ + else + lua_pop(L, 2); /* remove both values */ + if (up - lo == 1) /* only 2 elements? */ + return; /* already sorted */ + if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ + p = (lo + up)/2; /* middle element is a good pivot */ + else /* for larger intervals, it is worth a random pivot */ + p = choosePivot(lo, up, rnd); + lua_geti(L, 1, p); + lua_geti(L, 1, lo); + if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ + set2(L, p, lo); /* swap a[p] - a[lo] */ + else { + lua_pop(L, 1); /* remove a[lo] */ + lua_geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ + set2(L, p, up); /* swap a[up] - a[p] */ + else + lua_pop(L, 2); + } + if (up - lo == 2) /* only 3 elements? */ + return; /* already sorted */ + lua_geti(L, 1, p); /* get middle element (Pivot) */ + lua_pushvalue(L, -1); /* push Pivot */ + lua_geti(L, 1, up - 1); /* push a[up - 1] */ + set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ + p = partition(L, lo, up); + /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ + if (p - lo < up - p) { /* lower interval is smaller? */ + auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ + n = p - lo; /* size of smaller interval */ + lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ + } + else { + auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ + n = up - p; /* size of smaller interval */ + up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ + } + if ((up - lo) / 128 > n) /* partition too imbalanced? */ + rnd = l_randomizePivot(); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ +} + + +static int sort (lua_State *L) { + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (IdxT)n, 0); + } + return 0; +} + +/* }====================================================== */ + + +static const luaL_Reg tab_funcs[] = { + {"concat", tconcat}, + {"insert", tinsert}, + {"pack", tpack}, + {"unpack", tunpack}, + {"remove", tremove}, + {"move", tmove}, + {"sort", sort}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_table (lua_State *L) { + luaL_newlib(L, tab_funcs); + return 1; +} + +/* +** $Id: lutf8lib.c $ +** Standard library for UTF-8 manipulation +** See Copyright Notice in lua.h +*/ + +#define lutf8lib_c +#define LUA_LIB + +/*#include "lprefix.h"*/ + + +#include +#include +#include +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +#define MAXUNICODE 0x10FFFFu + +#define MAXUTF 0x7FFFFFFFu + + +#define MSGInvalid "invalid UTF-8 code" + +/* +** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. +*/ +#if (UINT_MAX >> 30) >= 1 +typedef unsigned int utfint; +#else +typedef unsigned long utfint; +#endif + + +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) + + +/* from strlib */ +/* translate a relative string position: negative means back from end */ +static lua_Integer u_posrelat (lua_Integer pos, size_t len) { + if (pos >= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + + +/* +** Decode one UTF-8 sequence, returning NULL if byte sequence is +** invalid. The array 'limits' stores the minimum value for each +** sequence length, to check for overlong representations. Its first +** entry forces an error for non-ascii bytes with no continuation +** bytes (count == 0). +*/ +static const char *utf8_decode (const char *s, utfint *val, int strict) { + static const utfint limits[] = + {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; + unsigned int c = (unsigned char)s[0]; + utfint res = 0; /* final result */ + if (c < 0x80) /* ascii? */ + res = c; + else { + int count = 0; /* to count number of continuation bytes */ + for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ + unsigned int cc = (unsigned char)s[++count]; /* read next byte */ + if (!iscont(cc)) /* not a continuation byte? */ + return NULL; /* invalid byte sequence */ + res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ + } + res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ + if (count > 5 || res > MAXUTF || res < limits[count]) + return NULL; /* invalid byte sequence */ + s += count; /* skip continuation bytes read */ + } + if (strict) { + /* check for invalid code points; too large or surrogates */ + if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu)) + return NULL; + } + if (val) *val = res; + return s + 1; /* +1 to include first byte */ +} + + +/* +** utf8len(s [, i [, j [, lax]]]) --> number of characters that +** start in the range [i,j], or nil + current position if 's' is not +** well formed in that interval +*/ +static int utflen (lua_State *L) { + lua_Integer n = 0; /* counter for the number of characters */ + size_t len; /* string length in bytes */ + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); + int lax = lua_toboolean(L, 4); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, + "initial position out of bounds"); + luaL_argcheck(L, --posj < (lua_Integer)len, 3, + "final position out of bounds"); + while (posi <= posj) { + const char *s1 = utf8_decode(s + posi, NULL, !lax); + if (s1 == NULL) { /* conversion error? */ + luaL_pushfail(L); /* return fail ... */ + lua_pushinteger(L, posi + 1); /* ... and current position */ + return 2; + } + posi = s1 - s; + n++; + } + lua_pushinteger(L, n); + return 1; +} + + +/* +** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all +** characters that start in the range [i,j] +*/ +static int codepoint (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); + int lax = lua_toboolean(L, 4); + int n; + const char *se; + luaL_argcheck(L, posi >= 1, 2, "out of bounds"); + luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds"); + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; /* upper bound for number of returns */ + luaL_checkstack(L, n, "string slice too long"); + n = 0; /* count the number of returns */ + se = s + pose; /* string end */ + for (s += posi - 1; s < se;) { + utfint code; + s = utf8_decode(s, &code, !lax); + if (s == NULL) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, code); + n++; + } + return n; +} + + +static void pushutfchar (lua_State *L, int arg) { + lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); + luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); + lua_pushfstring(L, "%U", (long)code); +} + + +/* +** utfchar(n1, n2, ...) -> char(n1)..char(n2)... +*/ +static int utfchar (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + if (n == 1) /* optimize common case of single char */ + pushutfchar(L, 1); + else { + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i = 1; i <= n; i++) { + pushutfchar(L, i); + luaL_addvalue(&b); + } + luaL_pushresult(&b); + } + return 1; +} + + +/* +** offset(s, n, [i]) -> index where n-th character counting from +** position 'i' starts; 0 means character at 'i'. +*/ +static int byteoffset (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + lua_Integer posi = (n >= 0) ? 1 : len + 1; + posi = u_posrelat(luaL_optinteger(L, 3, posi), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, + "position out of bounds"); + if (n == 0) { + /* find beginning of current byte sequence */ + while (posi > 0 && iscontp(s + posi)) posi--; + } + else { + if (iscontp(s + posi)) + return luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscontp(s + posi)); + n++; + } + } + else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } + } + if (n == 0) /* did it find given character? */ + lua_pushinteger(L, posi + 1); + else /* no such character */ + luaL_pushfail(L); + return 1; +} + + +static int iter_aux (lua_State *L, int strict) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); + if (n < len) { + while (iscontp(s + n)) n++; /* go to next character */ + } + if (n >= len) /* (also handles original 'n' being negative) */ + return 0; /* no more codepoints */ + else { + utfint code; + const char *next = utf8_decode(s + n, &code, strict); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, n + 1); + lua_pushinteger(L, code); + return 2; + } +} + + +static int iter_auxstrict (lua_State *L) { + return iter_aux(L, 1); +} + +static int iter_auxlax (lua_State *L) { + return iter_aux(L, 0); +} + + +static int iter_codes (lua_State *L) { + int lax = lua_toboolean(L, 2); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); + lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + + +/* pattern to match a single UTF-8 character */ +#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" + + +static const luaL_Reg funcs[] = { + {"offset", byteoffset}, + {"codepoint", codepoint}, + {"char", utfchar}, + {"len", utflen}, + {"codes", iter_codes}, + /* placeholders */ + {"charpattern", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_utf8 (lua_State *L) { + luaL_newlib(L, funcs); + lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); + lua_setfield(L, -2, "charpattern"); + return 1; +} + +/* +** $Id: linit.c $ +** Initialization of libraries for lua.c and other clients +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +/* +** If you embed Lua in your program and need to open the standard +** libraries, call luaL_openlibs in your program. If you need a +** different set of libraries, copy this file to your project and edit +** it to suit your needs. +** +** You can also *preload* libraries, so that a later 'require' can +** open the library, which is already linked to the application. +** For that, do the following code: +** +** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); +** lua_pushcfunction(L, luaopen_modname); +** lua_setfield(L, -2, modname); +** lua_pop(L, 1); // remove PRELOAD table +*/ + +/*#include "lprefix.h"*/ + + +#include + +/*#include "lua.h"*/ + +/*#include "lualib.h"*/ +/*#include "lauxlib.h"*/ + + +/* +** these libs are loaded by lua.c and are readily available to any Lua +** program +*/ +static const luaL_Reg loadedlibs[] = { + {LUA_GNAME, luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_COLIBNAME, luaopen_coroutine}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, + {LUA_DBLIBNAME, luaopen_debug}, + {NULL, NULL} +}; + + +LUALIB_API void luaL_openlibs (lua_State *L) { + const luaL_Reg *lib; + /* "require" functions from 'loadedlibs' and set results to global table */ + for (lib = loadedlibs; lib->func; lib++) { + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); /* remove lib */ + } +} + +#endif /* LUA_IMPL */ +#ifdef __cplusplus +} +#endif +#ifdef LUA_MAKE_LUA +/* +** $Id: lua.c $ +** Lua stand-alone interpreter +** See Copyright Notice in lua.h +*/ + +#define lua_c + +/*#include "lprefix.h"*/ + + +#include +#include +#include + +#include + +/*#include "lua.h"*/ + +/*#include "lauxlib.h"*/ +/*#include "lualib.h"*/ + + +#if !defined(LUA_PROGNAME) +#define LUA_PROGNAME "lua" +#endif + +#if !defined(LUA_INIT_VAR) +#define LUA_INIT_VAR "LUA_INIT" +#endif + +#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX + + +static lua_State *globalL = NULL; + +static const char *progname = LUA_PROGNAME; + + +#if defined(LUA_USE_POSIX) /* { */ + +/* +** Use 'sigaction' when available. +*/ +static void setsignal (int sig, void (*handler)(int)) { + struct sigaction sa; + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); /* do not mask any signal */ + sigaction(sig, &sa, NULL); +} + +#else /* }{ */ + +#define setsignal signal + +#endif /* } */ + + +/* +** Hook set by signal function to stop the interpreter. +*/ +static void lstop (lua_State *L, lua_Debug *ar) { + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); /* reset hook */ + luaL_error(L, "interrupted!"); +} + + +/* +** Function to be called at a C signal. Because a C signal cannot +** just change a Lua state (as there is no proper synchronization), +** this function only sets a hook that, when called, will stop the +** interpreter. +*/ +static void laction (int i) { + int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; + setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ + lua_sethook(globalL, lstop, flag, 1); +} + + +static void print_usage (const char *badoption) { + lua_writestringerror("%s: ", progname); + if (badoption[1] == 'e' || badoption[1] == 'l') + lua_writestringerror("'%s' needs argument\n", badoption); + else + lua_writestringerror("unrecognized option '%s'\n", badoption); + lua_writestringerror( + "usage: %s [options] [script [args]]\n" + "Available options are:\n" + " -e stat execute string 'stat'\n" + " -i enter interactive mode after executing 'script'\n" + " -l mod require library 'mod' into global 'mod'\n" + " -l g=mod require library 'mod' into global 'g'\n" + " -v show version information\n" + " -E ignore environment variables\n" + " -W turn warnings on\n" + " -- stop handling options\n" + " - stop handling options and execute stdin\n" + , + progname); +} + + +/* +** Prints an error message, adding the program name in front of it +** (if present) +*/ +static void l_message (const char *pname, const char *msg) { + if (pname) lua_writestringerror("%s: ", pname); + lua_writestringerror("%s\n", msg); +} + + +/* +** Check whether 'status' is not OK and, if so, prints the error +** message on the top of the stack. It assumes that the error object +** is a string, as it was either generated by Lua or by 'msghandler'. +*/ +static int report (lua_State *L, int status) { + if (status != LUA_OK) { + const char *msg = lua_tostring(L, -1); + l_message(progname, msg); + lua_pop(L, 1); /* remove message */ + } + return status; +} + + +/* +** Message handler used to run all chunks +*/ +static int msghandler (lua_State *L) { + const char *msg = lua_tostring(L, 1); + if (msg == NULL) { /* is error object not a string? */ + if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ + lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ + return 1; /* that is the message */ + else + msg = lua_pushfstring(L, "(error object is a %s value)", + luaL_typename(L, 1)); + } + luaL_traceback(L, L, msg, 1); /* append a standard traceback */ + return 1; /* return the traceback */ +} + + +/* +** Interface to 'lua_pcall', which sets appropriate message function +** and C-signal handler. Used to run all chunks. +*/ +static int docall (lua_State *L, int narg, int nres) { + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, msghandler); /* push message handler */ + lua_insert(L, base); /* put it under function and args */ + globalL = L; /* to be available to 'laction' */ + setsignal(SIGINT, laction); /* set C-signal handler */ + status = lua_pcall(L, narg, nres, base); + setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */ + lua_remove(L, base); /* remove message handler from the stack */ + return status; +} + + +static void print_version (void) { + lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); + lua_writeline(); +} + + +/* +** Create the 'arg' table, which stores all arguments from the +** command line ('argv'). It should be aligned so that, at index 0, +** it has 'argv[script]', which is the script name. The arguments +** to the script (everything after 'script') go to positive indices; +** other arguments (before the script name) go to negative indices. +** If there is no script name, assume interpreter's name as base. +** (If there is no interpreter's name either, 'script' is -1, so +** table sizes are zero.) +*/ +static void createargtable (lua_State *L, char **argv, int argc, int script) { + int i, narg; + narg = argc - (script + 1); /* number of positive indices */ + lua_createtable(L, narg, script + 1); + for (i = 0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - script); + } + lua_setglobal(L, "arg"); +} + + +static int dochunk (lua_State *L, int status) { + if (status == LUA_OK) status = docall(L, 0, 0); + return report(L, status); +} + + +static int dofile (lua_State *L, const char *name) { + return dochunk(L, luaL_loadfile(L, name)); +} + + +static int dostring (lua_State *L, const char *s, const char *name) { + return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); +} + + +/* +** Receives 'globname[=modname]' and runs 'globname = require(modname)'. +*/ +static int dolibrary (lua_State *L, char *globname) { + int status; + char *modname = strchr(globname, '='); + if (modname == NULL) /* no explicit name? */ + modname = globname; /* module name is equal to global name */ + else { + *modname = '\0'; /* global name ends here */ + modname++; /* module name starts after the '=' */ + } + lua_getglobal(L, "require"); + lua_pushstring(L, modname); + status = docall(L, 1, 1); /* call 'require(modname)' */ + if (status == LUA_OK) + lua_setglobal(L, globname); /* globname = require(modname) */ + return report(L, status); +} + + +/* +** Push on the stack the contents of table 'arg' from 1 to #arg +*/ +static int pushargs (lua_State *L) { + int i, n; + if (lua_getglobal(L, "arg") != LUA_TTABLE) + luaL_error(L, "'arg' is not a table"); + n = (int)luaL_len(L, -1); + luaL_checkstack(L, n + 3, "too many arguments to script"); + for (i = 1; i <= n; i++) + lua_rawgeti(L, -i, i); + lua_remove(L, -i); /* remove table from the stack */ + return n; +} + + +static int handle_script (lua_State *L, char **argv) { + int status; + const char *fname = argv[0]; + if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + if (status == LUA_OK) { + int n = pushargs(L); /* push arguments to script */ + status = docall(L, n, LUA_MULTRET); + } + return report(L, status); +} + + +/* bits of various argument indicators in 'args' */ +#define has_error 1 /* bad option */ +#define has_i 2 /* -i */ +#define has_v 4 /* -v */ +#define has_e 8 /* -e */ +#define has_E 16 /* -E */ + + +/* +** Traverses all arguments from 'argv', returning a mask with those +** needed before running any Lua code or an error code if it finds any +** invalid argument. In case of error, 'first' is the index of the bad +** argument. Otherwise, 'first' is -1 if there is no program name, +** 0 if there is no script name, or the index of the script name. +*/ +static int collectargs (char **argv, int *first) { + int args = 0; + int i; + if (argv[0] != NULL) { /* is there a program name? */ + if (argv[0][0]) /* not empty? */ + progname = argv[0]; /* save it */ + } + else { /* no program name */ + *first = -1; + return 0; + } + for (i = 1; argv[i] != NULL; i++) { /* handle arguments */ + *first = i; + if (argv[i][0] != '-') /* not an option? */ + return args; /* stop handling options */ + switch (argv[i][1]) { /* else check option */ + case '-': /* '--' */ + if (argv[i][2] != '\0') /* extra characters after '--'? */ + return has_error; /* invalid option */ + *first = i + 1; + return args; + case '\0': /* '-' */ + return args; /* script "name" is '-' */ + case 'E': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_E; + break; + case 'W': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + break; + case 'i': + args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ + case 'v': + if (argv[i][2] != '\0') /* extra characters? */ + return has_error; /* invalid option */ + args |= has_v; + break; + case 'e': + args |= has_e; /* FALLTHROUGH */ + case 'l': /* both options need an argument */ + if (argv[i][2] == '\0') { /* no concatenated argument? */ + i++; /* try next 'argv' */ + if (argv[i] == NULL || argv[i][0] == '-') + return has_error; /* no next argument or it is another option */ + } + break; + default: /* invalid option */ + return has_error; + } + } + *first = 0; /* no script name */ + return args; +} + + +/* +** Processes options 'e' and 'l', which involve running Lua code, and +** 'W', which also affects the state. +** Returns 0 if some code raises an error. +*/ +static int runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + int option = argv[i][1]; + lua_assert(argv[i][0] == '-'); /* already checked */ + switch (option) { + case 'e': case 'l': { + int status; + char *extra = argv[i] + 2; /* both options need an argument */ + if (*extra == '\0') extra = argv[++i]; + lua_assert(extra != NULL); + status = (option == 'e') + ? dostring(L, extra, "=(command line)") + : dolibrary(L, extra); + if (status != LUA_OK) return 0; + break; + } + case 'W': + lua_warning(L, "@on", 0); /* warnings on */ + break; + } + } + return 1; +} + + +static int handle_luainit (lua_State *L) { + const char *name = "=" LUA_INITVARVERSION; + const char *init = getenv(name + 1); + if (init == NULL) { + name = "=" LUA_INIT_VAR; + init = getenv(name + 1); /* try alternative name */ + } + if (init == NULL) return LUA_OK; + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, name); +} + + +/* +** {================================================================== +** Read-Eval-Print Loop (REPL) +** =================================================================== +*/ + +#if !defined(LUA_PROMPT) +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " +#endif + +#if !defined(LUA_MAXINPUT) +#define LUA_MAXINPUT 512 +#endif + + +/* +** lua_stdin_is_tty detects whether the standard input is a 'tty' (that +** is, whether we're running lua interactively). +*/ +#if !defined(lua_stdin_is_tty) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include +#define lua_stdin_is_tty() isatty(0) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + +#include +#include + +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) + +#else /* }{ */ + +/* ISO C definition */ +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ + +#endif /* } */ + +#endif /* } */ + + +/* +** lua_readline defines how to show a prompt and then read a line from +** the standard input. +** lua_saveline defines how to "save" a read line in a "history". +** lua_freeline defines how to free a line read by lua_readline. +*/ +#if !defined(lua_readline) /* { */ + +#if defined(LUA_USE_READLINE) /* { */ + +#include +#include +#define lua_initreadline(L) ((void)L, rl_readline_name="lua") +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,line) ((void)L, add_history(line)) +#define lua_freeline(L,b) ((void)L, free(b)) + +#else /* }{ */ + +#define lua_initreadline(L) ((void)L) +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,line) { (void)L; (void)line; } +#define lua_freeline(L,b) { (void)L; (void)b; } + +#endif /* } */ + +#endif /* } */ + + +/* +** Return the string to be used as a prompt by the interpreter. Leave +** the string (or nil, if using the default value) on the stack, to keep +** it anchored. +*/ +static const char *get_prompt (lua_State *L, int firstline) { + if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL) + return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */ + else { /* apply 'tostring' over the value */ + const char *p = luaL_tolstring(L, -1, NULL); + lua_remove(L, -2); /* remove original value */ + return p; + } +} + +/* mark in error messages for incomplete statements */ +#define EOFMARK "" +#define marklen (sizeof(EOFMARK)/sizeof(char) - 1) + + +/* +** Check whether 'status' signals a syntax error and the error +** message at the top of the stack ends with the above mark for +** incomplete statements. +*/ +static int incomplete (lua_State *L, int status) { + if (status == LUA_ERRSYNTAX) { + size_t lmsg; + const char *msg = lua_tolstring(L, -1, &lmsg); + if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { + lua_pop(L, 1); + return 1; + } + } + return 0; /* else... */ +} + + +/* +** Prompt the user, read a line, and push it into the Lua stack. +*/ +static int pushline (lua_State *L, int firstline) { + char buffer[LUA_MAXINPUT]; + char *b = buffer; + size_t l; + const char *prmt = get_prompt(L, firstline); + int readstatus = lua_readline(L, b, prmt); + if (readstatus == 0) + return 0; /* no input (prompt will be popped by caller) */ + lua_pop(L, 1); /* remove prompt */ + l = strlen(b); + if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ + b[--l] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ + lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ + else + lua_pushlstring(L, b, l); + lua_freeline(L, b); + return 1; +} + + +/* +** Try to compile line on the stack as 'return ;'; on return, stack +** has either compiled chunk or original line (if compilation failed). +*/ +static int addreturn (lua_State *L) { + const char *line = lua_tostring(L, -1); /* original line */ + const char *retline = lua_pushfstring(L, "return %s;", line); + int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); + if (status == LUA_OK) { + lua_remove(L, -2); /* remove modified line */ + if (line[0] != '\0') /* non empty? */ + lua_saveline(L, line); /* keep history */ + } + else + lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ + return status; +} + + +/* +** Read multiple lines until a complete Lua statement +*/ +static int multiline (lua_State *L) { + for (;;) { /* repeat until gets a complete statement */ + size_t len; + const char *line = lua_tolstring(L, 1, &len); /* get what it has */ + int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ + if (!incomplete(L, status) || !pushline(L, 0)) { + lua_saveline(L, line); /* keep history */ + return status; /* cannot or should not try to add continuation line */ + } + lua_pushliteral(L, "\n"); /* add newline... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } +} + + +/* +** Read a line and try to load (compile) it first as an expression (by +** adding "return " in front of it) and second as a statement. Return +** the final status of load/call with the resulting function (if any) +** in the top of the stack. +*/ +static int loadline (lua_State *L) { + int status; + lua_settop(L, 0); + if (!pushline(L, 1)) + return -1; /* no input */ + if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ + status = multiline(L); /* try as command, maybe with continuation lines */ + lua_remove(L, 1); /* remove line from the stack */ + lua_assert(lua_gettop(L) == 1); + return status; +} + + +/* +** Prints (calling the Lua 'print' function) any values on the stack +*/ +static void l_print (lua_State *L) { + int n = lua_gettop(L); + if (n > 0) { /* any result to be printed? */ + luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, n, 0, 0) != LUA_OK) + l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", + lua_tostring(L, -1))); + } +} + + +/* +** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and +** print any results. +*/ +static void doREPL (lua_State *L) { + int status; + const char *oldprogname = progname; + progname = NULL; /* no 'progname' on errors in interactive mode */ + lua_initreadline(L); + while ((status = loadline(L)) != -1) { + if (status == LUA_OK) + status = docall(L, 0, LUA_MULTRET); + if (status == LUA_OK) l_print(L); + else report(L, status); + } + lua_settop(L, 0); /* clear stack */ + lua_writeline(); + progname = oldprogname; +} + +/* }================================================================== */ + + +/* +** Main body of stand-alone interpreter (to be called in protected mode). +** Reads the options and handles them all. +*/ +static int pmain (lua_State *L) { + int argc = (int)lua_tointeger(L, 1); + char **argv = (char **)lua_touserdata(L, 2); + int script; + int args = collectargs(argv, &script); + int optlim = (script > 0) ? script : argc; /* first argv not an option */ + luaL_checkversion(L); /* check that interpreter has correct version */ + if (args == has_error) { /* bad arg? */ + print_usage(argv[script]); /* 'script' has index of bad arg. */ + return 0; + } + if (args & has_v) /* option '-v'? */ + print_version(); + if (args & has_E) { /* option '-E'? */ + lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + } + luaL_openlibs(L); /* open standard libraries */ + createargtable(L, argv, argc, script); /* create table 'arg' */ + lua_gc(L, LUA_GCRESTART); /* start GC... */ + lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ + if (!(args & has_E)) { /* no option '-E'? */ + if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ + return 0; /* error running LUA_INIT */ + } + if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ + return 0; /* something failed */ + if (script > 0) { /* execute main script (if there is one) */ + if (handle_script(L, argv + script) != LUA_OK) + return 0; /* interrupt in case of error */ + } + if (args & has_i) /* -i option? */ + doREPL(L); /* do read-eval-print loop */ + else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */ + if (lua_stdin_is_tty()) { /* running in interactive mode? */ + print_version(); + doREPL(L); /* do read-eval-print loop */ + } + else dofile(L, NULL); /* executes stdin as a file */ + } + lua_pushboolean(L, 1); /* signal no errors */ + return 1; +} + + +int main (int argc, char **argv) { + int status, result; + lua_State *L = luaL_newstate(); /* create state */ + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } + lua_gc(L, LUA_GCSTOP); /* stop GC while building state */ + lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ + lua_pushinteger(L, argc); /* 1st argument */ + lua_pushlightuserdata(L, argv); /* 2nd argument */ + status = lua_pcall(L, 2, 1, 0); /* do the call */ + result = lua_toboolean(L, -1); /* get result */ + report(L, status); + lua_close(L); + return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; +} + +#endif /* LUA_MAKE_LUA */ + +/* + MIT License + + Copyright (c) 1994–2019 Lua.org, PUC-Rio. + Copyright (c) 2020-2023 Eduardo Bart (https://github.com/edubart). + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ diff --git a/thirdparty/ode-0.16.5/CHANGELOG.txt b/thirdparty/ode-0.16.5/CHANGELOG.txt new file mode 100644 index 0000000..59cda1c --- /dev/null +++ b/thirdparty/ode-0.16.5/CHANGELOG.txt @@ -0,0 +1,1311 @@ +ODE CHANGELOG +------------- + +the rules for this file: + * entries are sorted newest-first. + * summarize sets of changes - dont reproduce every CVS log comment here. + * don't ever delete anything. + * keep the format consistent (79 char width, M/D/Y date format). + +------------------------------------------------------------------------------ +03/26/2024 Oleh Derevenko + * Autotools build scripts, when configured with "--enable-libccd", + will now assign all the relevant colliders to the libccd. + Before, it used to keep default collider for Box-Cylinder pair and + that was inconsistent with other configuration methods (CMake and + premake4.exe). To obtain the old behavior configure as + "--enable-libccd --with-box-cylinder=default". + +06/28/2023 Oleh Derevenko + * dJointAddPUTorques() was added to replace orphaned declaration of + dJointAddPUTorque() + +06/24/2023 Oleh Derevenko (by Tilmann) + * Fixed a missing break in a switch statement in demo_jointPU.cpp while + handling gravity switching request. + * Fixed wrong face index being returned in convex-trimesh libCCD + collision check routine. + * Fixed use of potentially outdated AABBs in GIIMPACT cylinder-trimesh + collision check routine. + +07/28/2020 Oleh Derevenko + * A threaded job data race fixed on job release (issue #70). + The bug could cause writes to functions's stack area after + the function returned and World simulation errors not being reported + with the function result if the simulation failed. + +11/12/2018 Oleh Derevenko + * The commentary from 11/05/2018 was wrong. The constraints were not + reset to their natural order and remained randomized. + The other thing that was missing was full contraint reorder without + separation into independent and dependent ones. The algorithm doesn't + converge without it well. + +11/10/2018 Oleh Derevenko + * An incorrect optimization to Jacobian Copy building code from #1938 + that resulted in corrupt data in multi-threaded execution mode was + fixed. + +11/05/2018 Oleh Derevenko + * An unintended change from commit #1898 has been reverted. + The QuickStep used to solve with randomized constraint order + each 8th iteration. The other iterations, the constraints + were reset to their natural order, as generated, with the dependent + constraints gathered in reverse order at end (the reverse order is + somehow important). With the commit #1898 the constraints were + randomly reordered each 8th iteration but then remained + in that randomized order and only were re-randomized on subsequent + multiples of 8. + +10/09/2017 Markus Rickert + * CMake support for project file generation has been added. + +06/14/2017 Oleh Derevenko + * dxHashSpace::collide() has been changed to fault host program + if scene gets too large and causes integer overflow. + +06/06/2017 Oleh Derevenko + * Memory and pointer size integer type use has been changed so that + internal typedefs are used instead of "_t" suffixed types. + +05/09/2017 Oleh Derevenko + * Introduction of cooperative algorithms API. + L*D*LT cooperative factorization and linear equation system + cooperative solving have been implemented. + * AtomicReadReorderBarrier, AtomicStore, AtomicStorePointer functions + have been added and some atomic function implementations have been + improved in OU. + +02/20/2017 Oleh Derevenko + * Project generation options have been changed to have built-in + multithreaded threading implementation enabled by default. + +02/19/2017 Oleh Derevenko + * dWorldStep threaded implementation has been extended to the final + steps of constraint force applications and body position updates + after the LCP solving. Note that body callbacks (if set) may be + called from multiple threads if threaded execution is enabled. + * OU atomicord32 type has been fixed to be unsigned on all supported + platforms. + +01/09/2017 Oleh Derevenko + * dGeomTriMeshDataPreprocess2() public function has been added to + replace dGeomTriMeshDataPreprocess(). Face angles pre-computation + for triangle meshes has been implemented. + +11/13/2016 Oleh Derevenko + * dGeomTriMeshDataGetBuff and dGeomTriMeshDataSetBuff have been marked + deprecated and their functionality implemented via + dGeomTriMeshDataGet and dGeomTriMeshDataSet. Extra function variant + dGeomTriMeshDataGet2() has been added to allow returning data size. + +11/07/2016 Oleh Derevenko + * The implementation of OPCODE TriMesh data pre-processing + (dGeomTriMeshDataPreprocess()) has been optimized to only contain + a sort and a single pass over edges (used to be a sort and O(N^2)). + +10/29/2016 Oleh Derevenko + * dGeomTriMeshDataPreprocess() public function has been changed to + return a boolean status (it can fail in low memory conditions). + +07/10/2016 Oleh Derevenko + * The correct handling of dJOINT_REVERSE mode for AngularMotor Joint + implemented (issue #37). + +06/29/2016 Oleh Derevenko + * A bug fixed with HashSpace calling big boxes collision twice + (both straight and reverse geometries order) since revision #1831. + +06/03/2016 Oleh Derevenko + * dJointSetHinge2Axes public function has been added and + dJointSetHinge2Axis1/2 have been marked deprecated due to being + unsafe. + +05/13/2016 Oleh Derevenko + * ICE Container class allocation strategy fixed to avoid reserving + excess memory with large collections. + +05/10/2016 Oleh Derevenko + * dSafeNormalize3 and dSafeNormalize4 functions changed to leave the + parameter intact instead of replacing it with the X-axis unit in case + of fault. + +04/12/2016 Oleh Derevenko + * A function to create a self-threaded threading implementation object + has been moved back to public headers as there could be a use for it + while running several worlds in parallel threads. + +01/09/2016 Oleh Derevenko + * Hinge2 joint corected to avoid faulting asserts when the axes get + temporarily invalid during assignments (suggested by David Mansolino) + +01/03/2016 Oleh Derevenko + * An invalid memory access fixed in dxSAPSpace::BoxPruning() in case + if there were NaN values in AABBs to be sorted. + +12/25/2015 Oleh Derevenko + * Unexpected joint mode assignment (instead of comparison) fixed within + an dUASSERT in dJointSetTransmissionAxis2() of transmission joint + +11/28/2015 Oleh Derevenko + * Convex-Trimesh collider added (libccd+GIMPACT only)(by Piotr Piastucki) + * dCreateConvex() and dGeomSetConvex() public APIs changed to expect + their parameter arrays as const pointers + +11/01/2015 Oleh Derevenko + * OPCODE mesh colliders' input coordinates have been offset to + mesh-relative frames to decrease potential computational errors + (suggested by luckytrashsc2@g***l.com) + +08/05/2015 Oleh Derevenko + * Implemented change to return highest depth contacts subset for GIMPACT + in cases if contacts count exceeds requested maximum (as suggested in + the issue #36 by Piotr Piastucki) + +11/17/2014 Daniel K. O. + * Added support for using libccd from the system (if found via + pkg-config) + +11/10/2014 Oleh Derevenko + * Floating point division by zero in capsule-ray collision routine + in case if the ray axis was parallel the cylinder and the ray started + from within it fixed (issue #26) + +11/08/2014 Oleh Derevenko + * Threading support has been extended to complete implementation + of QuickStep + +10/29/2014 Daniel K. O. + * Added dJointSetDBallDistance + +10/19/2014 Oleh Derevenko + * Built-in threading implementation compilation fixed for OSX + (clock_gettime() is missing from the system - reported by Bram) + +08/10/2014 Oleh Derevenko + * Declarations of dWorld[Get/Set]AutoDisableLinearAverageThreshold and + dWorld[Get/Set]AutoDisableAngularAverageThreshold have been removed + from public headers (were orphaned since rev.1052) + +07/16/2014 Oleh Derevenko + * Two fixes by Francesco Cat applied (fixes for mistakes made during + code style improvements in the past) + +02/27/14 Daniel K. O. + * Added dODE_VERSION macro to public headers (issue #24). + +02/11/14 Daniel K. O. + * Added dJointGetHinge2Angle2 (issue #12). + +02/07/14 Daniel K. O. + * Added dWorldSetData/dWorldGetData (issue #21). + +01/31/14 Daniel K. O. + * Applied patch #185: Stable, implicit gyroscopic forces. + +01/27/14 Daniel K. O. + * Fixed cylinder AABB computation. + +01/25/14 Daniel K. O. + * Removed ALLOCA calls from dHashSpace; it should not depend + on stack size limits anymore. + +12/06/13 Daniel K. O. + * Applied patch #181: fix some bugs in AMotor joint. + * Applied patch #186: fix some bugs in PU joint. + * Applied patch #182: Transmission joint. + * Applied patch #184: implement rolling friction for contacts. + +08/08/13 Oleh Derevenko + * Joint feedback forces application fixed in QuickStep implementation + (was broken since revision #1919 in old repository (#1927 in new one)) + +08/04/13 Oleh Derevenko + * Bugfix #89 by Luc applied (dJointAddSliderForce() adds a zero force + when the parent body is NULL) + +05/18/13 Oleh Derevenko + * OU library has been included in ODE at revision #46 instead of + being used as an external link due to difficulties using external + references with new protocol used for storage at SF. + +03/03/13 Oleh Derevenko + * Fixed issue with "findex==-1" constraints being intermixed with + "findex!=-1" ones during constraints random reordering in QuickStep. + +02/03/13 Oleh Derevenko + * [u]int[8/16/32] renamed to contain "d" prefix so that global namespace + was not polluted with these names unconditionally. + If your project depended on these types other than just for passing + parameters to ODE calls, add similar typedefs for yourself in some + of your project's global headers. + +01/02/13 Oleh Derevenko + * Applied patch #183 by Joseph Cooper (complementary matrix + calculation fix). + +12/28/12 Oleh Derevenko + * A bug with heightfield data assigned to a wrong field in + dGeomHeightfieldSetHeightfieldData fixed (bug report #88 by Luc). + +12/18/12 Oleh Derevenko + * Fixed issue with some kinds of joints (Ball, DBall, DHinge, Fixed) + might overwrite world ERP value with their custom ERP during + getInfo2() call and that inappropriate value would then be passed + to subsequent joints in solver instead of world ERP. + +12/01/12 Oleh Derevenko + * Fixed issues reported in patches #151 and #22 (collisions with + SAPSpace and QuadTreeSpace might not work because geometries list + was misused in them). + * Applied patch #160 "IsPointInPolygon in convex.cpp returns wrong + results" (by Janis Rucis) + +11/25/12 Oleh Derevenko + * Configuration option --disable-threading-intf added + (--no-threading-intf for Windows/Premake). This allows disabling + threading interface support (external implementations may not be + assigned) but eliminates dependency on OU and use of atomics in + steppers. + +11/05/12 Oleh Derevenko + * Fixed zero comparisons in OPCODE to use relative error instead of + absolute epsilon value (found by Bill Sellers) + +06/08/12 Daniel K. O. + * Removed the need for defining dSINGLE/dDOUBLE; this is stored now in + the generated ode/precision.h header. + * Some code cleanup to get rid of GCC warnings. + +05/30/12 Daniel K. O. + * Made drawstuff draw shadows for lines. + * Fixed dhinge's last constraint to properly handle rotations. + +05/03/12 Daniel K. O. + * Added two new joints: Double Ball and Double Hinge. + +04/22/12 Daniel K. O. + * Fixed plane2d joint: uninitialized variables (reported by Dimitris + Papavasiliou) + +04/14/12 Oleh Derevenko + * Assertion checking macros moved into library private headers. + +04/13/12 Daniel K. O. + * Applied patch from bug #3431829 - better handling of capsule-box with + deep penetrations. + * Fixed zero-mu issues: now either mu or mu2 can be set to zero. + +03/17/12 Oleh Derevenko + * Threaded execution support interface added. Optional built-in threading + implementation added. + Internal threading implementation is excluded by default and to be used, + it must be enabled with configure/premake. + At present, if threading interface is assigned to a world, island + selection and stepping is performed in multiple threads (one thread per + island). + +03/12/12 Oleh Derevenko + * PURE_INLINE macro renamed to ODE_PURE_INLINE and definition of + dNextAfter()/dCopySign() corrected to avoid creating conflicts with + other libraries. + +02/03/12 Oleh Derevenko + * Assertion checking macros moved from common.h to error.h + +12/18/11 Oleh Derevenko + * dIVERIFY macro added (same as dIASSERT in debug mode but evaluates its + expression in release mode) to be used to assert variable value + which is not used further in text while avoiding compiler warning. + * dICHECK macro added (same as dIASSERT but evaluates its expression and + raises assertion fault regardless of compilation mode) to be used to + generate a fault in cases when error is very unlikely but must be + handled and handling is very troublesome (e.g. failure to lock a mutex + due to lack of resources). + +12/07/11 Oleh Derevenko + * Partially fixed size_t to integer conversion warnings + * Fixed type signedness and added casts to size_t wherever necessary + in Step/QuickStep + +11/04/11 Daniel K. O. + * Applied patch #3429454 - fix compilation on some platforms. + +10/28/11 Daniel K. O. + * Fixed a box-capsule bug: more reasonable normal for deep penetrations + (contributed by Georg Martius.) + +10/27/11 Daniel K. O. + * Disabled merging of contacts for trimesh-sphere by default. + * Added new demo: demo_tracks. + +10/17/11 Daniel K. O. + * Added python bindings, contributed by Gideon Klompje. + * Updated some build scripts. + * Changed spheres distribution in demo_space_stress. + +05/17/11 Oleh Derevenko + * A typo in step.cpp fixed (assignment operator in a conditional + instead of comparison) (reported by Bram Stolk) + +01/29/11 Oleh Derevenko + * Heightfield zone boundaries calculation code fixed to also consider + whole next cell after the AABB if the AABB ends exactly at the cell + boundary. + +01/23/11 Daniel K. O. + * Applied patch from Daniel Fiser, add libccd collider for + box-cylinder. + +01/20/11 Daniel K. O. + * Applied patch from Daniel Fiser, fix infinite loop in libccd caused + by numerical problems. + +01/06/11 Daniel K. O. + * Applied patch from Daniel Fiser, efficient libccd tests when using + CONTACTS_UNIMPORTANT. + +12/17/10 Daniel K. O. + * Applied patches from Daniel Fiser for new colliders based on libccd. + +11/08/10 Daniel K. O. + * Applied patches from Daniel Fiser to incorporate libccd for + Cylinder-Cylinder collision tests. + +08/21/10 Oleh Derevenko + * Fix applied to dxReallocateTemporayWorldProcessContext() to remove typo + which caused segmentation fault (by Kyle McKay). + dTestSolveLCP() fixed to avoid exceeding allocated memory pool + (by Kyle McKay). + +07/19/10 Oleh Derevenko + * Patch applied (#3030783) to fix drawstuff dimensions being ignored + in OSX GLUT port (by Danny Price). + + Daniel K. O. + * Applied patch #2991622: dGeomGetRelPointPos, dGeomGetPosRelPoint, + dGeomVectorToWorld, and dGeomVectorFromWorld. + +07/16/10 Daniel K. O. + * Fixed bug #2937076: don't try to build demos if drawstuff is disabled. + +05/02/10 Oleh Derevenko + * Missing extern "C" wrapper has been added to include/ode/export-dif.h + (reported by Danny Price). The change affects dWorldExportDIF() public + function. + +05/02/10 Oleh Derevenko + * Patch applied (#2995450) to generate up to four contacts for box- + plane collision test (by alexdu) and fix contact depths. + +05/02/10 Oleh Derevenko + * dGeomLowLevelControl function added with ability to change/query OPCODE + trimesh-sphere contact merging behavior at runtime. + +02/18/10 Daniel K. O. + * Fixed bug affecting disabled joints and dWorldStep. + +01/16/10 Oleh Derevenko + * Patch applied (#2931174) to make demos work for recent MacOS. + * Patch applied (#2931177) to fix the demos' framerate on X11. + +12/20/09 Oleh Derevenko + * QuadTreeSpace implementation corrected to avoid object-block relation + ambiguity due to numeric errors. + +12/04/09 Oleh Derevenko + * odecpp classes changed to be inheritable and easily expandable + +11/29/09 Oleh Derevenko + * Improvement for trimesh-plane collision (also used in trimesh-heightfield) + to exclude mesh vertices that have already generated contacts from further + examination and contact generation in other triangles (suggested by LR). + +10/25/09 Oleh Derevenko + * Macros changed to static inline functions in odemath.h and related files. + Some code duplication has been eliminated across the files. + + * Fixed handling of --disable-asserts and --enable-double-precision + (absence of --enable-double-precision) in configure script. The script + was not appending compiler defines correctly. + + * dWorldStep implementation changed to remove allocation on stack. + dUSE_MALLOC_FOR_ALLOCA define has been removed as well as corresponding + configuration parameter. Also dMemoryFlag public variable has been removed. + (look for presence of ODE_EXT_malloc_not_alloca configuration string if + your application is dependent on that variable). + +09/05/09 Oleh Derevenko + * dWorldStepFast1 API removed along with dWorld[Get/Set]AutoEnableDepthSF1 + +08/29/09 Oleh Derevenko + * Fixed uninitialized floating point array used in computations. + +08/12/09 Oleh Derevenko + * A typo fixed in dGeomCopyOffsetRotation() (final_posr was used instead + of offset_posr). Reported by Tilmann. + +08/11/09 Daniel K. O. + * Made sure neither dSINGLE or dDOUBLE is defined by default; the user + should always explicitly specify the precision. + +06/27/09 Oleh Derevenko + * New functions have been added: + - dWorldUseSharedWorkingMemory + - dWorldCleanupWorkingMemory + - dWorldSetStepMemoryReservationPolicy + - dWorldSetStepMemoryManager + +06/25/09 Remi Ricard (papaDoc) + * Add limit to the to the second axis of the universal joint + for the pu joint. + +06/14/09 Oleh Derevenko + * dWorldQuickStep re-implemented to avoid memory allocation on stack. + Also several optimizations have been made to decrease memory + requirements and optimize algorithm implementation of dWorldQuickStep. + dWorldStep still remains with old memory allocation however new APIs + mentioned below are fully functional for it. + Both dWorldStep and dWorldQuickStep have been changed to return boolean + success status. + + * dInitODE2() changed to automatically call + AllocateODEDataForThread(dAllocateFlagBasicData) after library + initialization as this is a required initialization minimum that + must always be performed anyway. + +06/05/09 Daniel K. O. + * Removed aliasing issues from OPCODE/Ice, plus some other warnings. + Now it builds on gcc 4.3.2 with '-Wall -Werror -O3". + +05/30/09 Oleh Derevenko + * A minor memory usage optimization for QuickStep. + +05/24/09 Daniel K. O. + * Made the new trimesh collider the default. + * Added a "-texturepath" option to drawstuff. + +05/18/09 Oleh Derevenko + * Heightfield rotation fixed to avoid NaNs while rotating infinite + MIN/MAX heights. + +05/03/09 Oleh Derevenko + * Incorrect parameter order fixed on contact merging in Sphere-Trimesh + collisions. The bug resulted in merged contact remaining with normal + of first contact found. Thanks to Dimitris Papavasiliou for reporting. + +04/23/09 Daniel K. O. + * Fixed bug #2685170: use the C99 __func__ instead of __FUNCTION__ when + a C99 implementation is available. + +04/07/09 Remi Ricard (papaDoc) + * Remove unused code in demo_joints.cpp, reported by Tilmann. + +04/07/09 Remi Ricard (papaDoc) + * Fix bug in collision categories in demo_jointPU, reported by Tilmann + +03/14/09 Oleh Derevenko + * A possibility to initialize/close ODE multiple times recursively has + been added. + Also, now a call to dSpaceSetManualCleanup() is required for each + space right after creation if ODE has been initialized in thread data + manual cleanup mode. + +03/07/09 Oleh Derevenko + * Thread local data has been cleaned up from OPCODE and OdeTls as it is + not used (OPC_SweepAndPrune.* and OPC_BoxPruning.* have been removed + - rebuilding project files is necessary). + +02/07/09 Daniel K. O. + * New house of cards demo, which stresses the friction handling stability. + +01/29/09 Remi Ricard (papaDoc) + * Fix bug: Fix problem when attaching no body to a joint. Before calling + setRelativeValues a check is made for bodies. + * Add unittest + +01/28/09 Daniel K. O. + * Applied patch #2538046: Heightfield AABB bounds patch. + +01/23/09 Remi Ricard (papaDoc) + * Add new function dJointSetUniversalAxis1Offset and dJointSetUniversalAxis2Offset + * Add unittest for those funcitons. + +01/23/09 Remi Ricard (papaDoc) + * Fix problem with dJointGetUniversalAngle2 when the joint is attached to + only a body 2. The sign was inverted. + * Add unit test to verify for this problem + +01/21/09 Remi Ricard (papaDoc) + * Fix bug reported by Tilman: dxJointPU::getInfo1 was setting twice the + limit of limot1 to zero and not limot2 + +01/17/09 Daniel K. O. + * Fixed a bug in dSpaceCollide2: if both geoms are not in spaces they would + not have valid AABBs. + +12/20/08 Daniel K. O. + * New functions: dJointEnable, dJointDisable, dJointIsEnabled + (patch #2454764). + +12/19/08 Daniel K. O. + * Removed inline asm statements that break builds on 64-bit VC++. + +12/09/08 Daniel K. O. + * Applied patch #2381592, which adds support for Kinematic Bodies. + +12/06/08 Oleh Derevenko + + * Applied a patch by Martijn Buijs to make GIMPACT trimesh-ray collisions to + be consistent with those in OPCODE. + * Swapped geometries returned in contacts for OPCODE Trimesh-Plane collisions + as they were returned in unnatural order being different from that in GIMPACT + * Applied a patch by Martijn Buijs to make side1, side2 fields of contact + structure be always initialized, either with -1 for non-trmesh geometries + or with triangle index for trimeshes. These fields were only assigned for + trimesh-trimesh collisions before. + * dGeomTriMeshSetTriMergeCallback/dGeomTriMeshGetTriMergeCallback API added + to set/get user defined callback procedure for trimeshes that would be + invoked when contacts are merged to let user code accumulate attributes of + original contact triangles and generate a fake index by which it would + later be able to determine those attributes. If the callback is not + assigned (the default) -1 is generated as triangle index for merged + contacts (there was an index of first of merged triangles before!!!). + The callback is currently used within OPCODE trimesh-sphere and OPCODE + new trimesh-trimesh collisions. + +11/20/08 Remi Ricard (papaDoc) + * Fix problem with dJointGetPUPosition and + dJointGetPUPositionRate when the joint is attached to only + a body 2. The sign was inverted. + * Fix bug: When a pu joint had only one body attached to position 2, + dJointAttach(jId, 0, bId). The body was not push in the right direction to + move back between the limits. + * Add unit test to check the above problem + * Add the function void dJointSetPUAnchorOffset + * Make the function void dJointSetPUAnchorDelta deprecated + +11/19/08 Remi Ricard (papaDoc) + * Fix bug: When a pr joint had only one body attached to position 2, + dJointAttach(jId, 0, bId). The body was not push in the right direction to + move back between the limits. + * Add unit test to check the above problem + +11/19/08 Remi Ricard (papaDoc) + * Fix problem with dJointGetPRPosition and + dJointGetPRPositionRate when the joint is attached to only + a body 2. The sign was inverted. + * Add unit test to check the above problem + * Increase the tolerance to remove failure in unit test + * Remove compilation warning in unit test with the use of REAL() + +11/18/08 Remi Ricard (papaDoc) + * Fix bug: When a piston joint had only one body attached to position 2, + dJointAttach(jId, 0, bId). The body was not push in the right direction to + move back between the limits. + * Add more functionality to demo_piston.cpp + * Run astyle on modified files. + +11/18/08 Remi Ricard (papaDoc) + * Fix bug: When a slider joint had only one body attached to position 2, + dJointAttach(jId, 0, bId). The body was not push in the right direction to + move back between the limits. + +10/29/08 Oleh Derevenko + + * Premake scripts changed to only include chosen collision library + sources in project on Windows. --all-collis-libs premake option + added to allow inclusion of all collision library sources into the + project + +10/15/08 Remi Ricard (papaDoc) + * Applying patch #2158425 64-bit GIMPACT provided by Mark + William. This patch enable GIMPACT to works on 64-bit machine. + +10/15/08 Remi Ricard (papaDoc) + * Add function dJointGetPRAngle and dJointGetPRAngleRate + +10/15/08 Remi Ricard (papaDoc) + * Enable the motor on the rotoide part of the PR joint + +10/15/08 Remi Ricard (papaDoc) + * Add unit test to check if using directly a joint + or using after setting with default values is the same. + * Add function setRelativeValues called in dJointAttach for + all joints. + +10/10/08 Remi Ricard (papaDoc) + * Fix bug in dJointGetPUAxis2. The axis was not multiplied with the + the rotation matrix of the good body. + * Fix bug if there is only one body on the PU joint the axis returned + was not the right one. + * Add unit test to verify previous bug. + +10/03/08 Rodrigo Hernandez (Kwizatz) + * Added Blender script to create ODE convex geoms under tools. + +10/01/08 Rodrigo Hernandez (Kwizatz) + * Convex-Convex collision detection code is finally stable. + +08/31/08 Daniel K. O. + * Applied patch 2080674: Improved dBodySetRotation; now exact rotation + matrices are preserved until the next simulation step. + +08/07/08 Daniel K. O. + * Fixed strict aliasing issue that was breaking the new trimesh collider. + +07/24/08 Daniel K. O. + * New functions: dBodyGetGyroscopicMode and dBodySetGyroscopicMode + (patch #2019242). + +07/15/08 Remi Ricard (papaDoc) + * Add a new define ODE_API_DEPRECATED to mark function as deprecated + when necessary. + +07/14/08 Remi Ricard (papaDoc) + * Finish adding patch 1336066: Joint feedback in quickstep by jsinecky + * demo_boxstack.cpp can now print joint feedback + +07/11/08 Daniel K. O. + * Bumped version for 0.10.1 + * Added proper usage of libtool's version info. + +07/10/08 Remi Ricard (papaDoc) + * Add new function dJointSetPistonAnchorOffset + * Add unittest for the piston joint + * Fix problem with dJointGetPistonPosition and + dJointGetPistonPositionRate when the joint is attached to only + a body 2. The sign was inverted. + +07/09/08 Remi Ricard (papaDoc) + * Optimize function Multiply1_12q1 in quickstep + Patch proposed by Riemer v.d. Zee and modified by Patrick Baggett + +07/08/08 Remi Ricard (papaDoc) + * Update the slider joint to have the same behavior as the other joint + when there is only a body2 attached to it. + * Update documentation for the slider joint. + * Remove warning by using REAL() + * Add new unittest for dJointGetSliderPositionRate + +07/08/08 Remi Ricard (papaDoc) + * Update unittest for the slider. + * Rename the new function dJointSetHingeAxisDelta to + dJointSetHingeAxisOffset. This remove will remove confusion with + the old function dJointSetHingeAnchorDelta + * Update documentation for the Hinge unittest + * Remove warning by using REAL() + +07/07/08 Daniel K. O. + * Max Correcting Vel doesn't affect bounciness, as before. + +07/03/08 Remi Ricard (papaDoc) + * Add new function dJointSetHingeAxisDelta + * Add unittest for this new function + +06/17/08 Remi Ricard (papaDoc) + + * Move the computation of the Relative Rotation for the slider joint + into a function. + * Add unittest for to check qrel + +06/17/08 Remi Ricard (papaDoc) + + * Remove unused variables. + * Remove a conversion warning between unsigned int and int + +06/17/08 Remi Ricard (papaDoc) + + * Move the function hingeComputeRelativeRotation as a member of + the hinge structure/class. + +06/17/08 Remi Ricard (papaDoc) + + * Move the computation of the Relative Rotation for the fixed joint + into a function. + +06/16/08 Remi Ricard (papaDoc) + + * Add testunit for the dxJointFixed + +06/04/08 Daniel K. O. + + * Moved joints to ode/src/joints, converted them to true virtual + methods. + +06/02/08 Daniel K. O. + + * Added an Auto template to step.cpp to handle memory deallocation. + +05/09/08 Daniel K. O. + + * Applied patch #1335202: Contact Joint Motion (with some corrections), + and added demo_motion. + +05/01/08 Oleh Derevenko + + * Memory leak in GIMPACT fixed (reported by Derek) + +04/28/08 Oleh Derevenko + + * Added possibility to collide a space of lower sublevel as a geometry + against another space of a higher level with dSpaceCollide2. + dSpaceSetSublevel/dSpaceGetSublevel are used for sublevel assignment/ + retrieval. + +04/27/08 Oleh Derevenko + + * Fixed incorrect memory copying which could lead to memory corruption + in GIMPACT (luckily, in unused code) + * Fixed possible memory read beyond the end of allocated buffer along + with unnecessary extra memory copying in GIMPACT. + * Fixed buffer reserve being incorrectly reset to zero for bitsets + what resulted in unnecessary memory reallocations in GIMPACT. + * Implemented support for ability to run collision detection from + multiple threads for separate spaces. + +04/14/08 Oleh Derevenko + + * Fixed possible memory corruption in new trimesh-trimesh collider + in case if two degenerated triangles are checked against each other. + +04/12/08 Oleh Derevenko + + * Fixed sporadic assertion failure on vector normalization caused + by small triangles degenerating into segments during space + transformations. + +03/28/08 Remi + + * Fix a bug in dJointXXXGetInfo. The value in limot.limit was not + always updated. (Ex: If hi and lo limit were changed). + +03/27/08 Remi + + * Added a new Joint: Prismatic Universal (patch #1828454). + + Daniel K. O. + + * Fixed bug #1841309: collide2() method buggy. + +03/18/08 Rodrigo + + * New function: dVector4Copy. + +03/14/08 david + + * Added stub calls for trimesh functions. + * Applied patch #1914232: dGetConfiguration. + * Applied patch #1655333: Optimize the function dNormalize3. + * New function: dSetColliderOverride. + * New function: dCheckConfiguration. + + Daniel K. O. + + * Disabled building shared library by default with autotools. + +03/13/08 david + + * New function: dJointGetNumBodies (patch #1901550). + * New function: dSpaceGetClass (patch #1901637). + * Applied patch #1901649: Add missing function in the export + +03/12/08 Rodrigo + + * Fixed drawstuff build issues on OSX. + +01/12/08 Daniel K. O. + + * Fixed a typedef bug in configure.in. + * Added dCylinder to the C++ wrappers. + * Applied patch 1851394: support for GIMPACT with double precision, + dCollide fix. + * Moved bunny geometry to bunny_geom.h. + +12/11/07 Daniel K. O. + + * Added damping and MaxAngularVel() functions. + * Const-correctness: added const qualifier to some dWorldGet and dBodyGet + functions. + * Updated the odecpp.h header. + +12/07/07 Daniel K. O. + + * Removed most of the compiler warnings from Drawstuff, ODE and + OPCODE + * Upgraded automake requirement to 1.10, and change some Makefile.am + +12/06/07 Rodrigo + + * Modified autotools to use libtool for + library generation and administration + * Removed release and debug flags for configure.in + CPPFLAGS, CFLAGS, CXXFLAGS should be set by the + user to their liking, respecting autotools policies. + +11/30/07 Daniel K. O. + * Applied patch 1813079 (moved callback) + * Replaced moveAndRotateBody by dxStepBody in stepfast.cpp + +11/10/07 david + + * Added 'Sweep and Prune' collision space. + * New Piston joint type with demo, by Remi Ricard + * Added build option to use 16-bit indices for OPCODE trimesh + +11/03/06 david + + * Integrated Christoph Beyer's average based sampling system for body + disabling. + +10/26/06 Francisco Leon + + * Totally refactored trimesh collision system. + Using GIMPACT instead of OPCODE. Now works correctly, and faster. + Visit http://gimpact.sourceforge.net. + + * Finally, test_moving_trimesh.exe works nicely. + + * Fixed autodisable system. Now is possible to set bigger sleeping + threshold values and objects won't be sleeping on the air. They will + rest on the floor properly. + + * dInitODE function added. + + * Is Obligatory to call dInitODE() at the beginning for initialize ODE, + and calling dCloseODE() when the program ends. + +09/20/06 bram + + * Fixed two bugs in cyl/plane collision test. + +09/13/06 Remi + + * New Rotoide - Prismatic joint type + * dJointGetUniversalAngles for efficient angle retrieval. + +08/09/06 david + + * Integrated plane2d joint type which constrains bodies to z == 0. + +07/06/06 david + + * Added heightfield primitive collision code. Simple test available in + ode/test/test_heightfield + +04/03/06 rodrigo + + * Added Convex primitive collision code, + currently only convex-sphere and convex-plane work + +04/01/06 bram + + * Added program to test trimesh vs sphere: ode/test/test_basket + +03/20/06 jason379 + + * Added new autogenerated Visual Studio projects, with Premake scripts + +03/17/06 bram + + * Added plane/cyl intersection test + * Renamed CCylinder to Capsule + +02/04/06 gcarlton + + * Added support for geom offsets. + +10/26/05 rodrigo + + * Removed LIBTOOL from autotools since it was not really required. + * Added a target to build ODE as a shared library, this shared + library gets build alongside the static one, no flags required. + +10/24/05 tfautre + + (Backported patches from STABLE branch, applied by Adam) + + * dRandInt changed for a non-double all-int version. + * mics minor fixes and improvements. + +04/05/05 tfautre + + * Fixed segmentation fault with OPCODE on 64 bits systems. + +03/31/05 tfautre + + * Fixed timer.cpp compiler error on x86-64 using GCC. + +03/29/05 colin + + * Added trimesh preprocessing to mark unneeded edges and verts. Also + added support for preprocessed info to the ccylinder-trimesh + collider. + +12/07/04 adam + + * Important AMotors bugfix + +09/22/04 jeff + + * Assorted small bugfixes and tweaks for + trimesh_{box,ccylinder,trimesh} collisions + +09/21/04 jeff + + * added functions to joint.cpp to allow joint attachment to moving + geoms. + + * added malloc-based memory allocation in step.cpp & lcp.cpp (turned + on with a #define switch in common.h) + +05/29/04 russ + + * added joint feedback to the QuickStep solver + +05/18/04 russ + + * added warm starting to the QuickStep solver + +05/18/04 russ + + * added the QuickStep solver + + * added contact parameter functions. + +05/05/04 adam + + * use dRandInt instead of rand() in stepfast. + +04/21/04 russ + + * added auto-disable support from Aras Pranckevicius (with + modifications by russ). this useful feature can speed up + simulation significantly in some cases. + + * various internal tidyups. + +04/20/04 russ + + * changed the meaning of the 'index' argument to dJointGetBody(): + it was the only remaining API function that does not respect + dJOINT_REVERSE (spotted by Matthew D. Hancher). + + * updated the C++ headers: fixed two minor bugs and added + support for dQuadTreeSpace, dRay, and the dGeom::getSpace() method + (from Matthew D. Hancher). + +04/18/04 russ + + * changed the way that the dInfinity constant is implemented: now it + is #defined to be one of: FLT_MAX, DBL_MAX, HUGE_VAL, HUGE_VALF, or + a large numeric constant. previously it was a variable that was + exported from the library. this simplifies the configuration and + build process quite a bit, especially in the case of DLLs. + + * removed the old, deprecated collision system (geom.cpp,space.cpp, + geom.h,space.h,odecpp_old_collision.h). the ODE_OLD_COLLISION + configuration setting no longer has any meaning. + + * removed support for dGeomGroups, which have been deprecated for + a while and are equivalent to 'spaces' anyway. + +04/13/04 russ + + * bug fix in dMassSetCappedCylinder(), from Matthew D. Hancher. + +04/08/04 russ + + * added trimesh-CCylinder capability, from Vadim Macagon + . + +04/04/04 adam + + * yet another rewrite of triangle-box collision code, this + time based on code donated by Croteam, ported by asko@jetti.org + and tweaked by Erwin. + +04/04/04 adam + + * merged trimesh-trimesh collision code by + Jeffrey Smith . + + * changed it to not break the trimesh interface, fix + some GCC compilation problems, bring it up to date with + ODE changes from 2003-11-15 -> 2004-04-04. + + * add ability to drop meshes on meshes in test_moving_trimesh, + not as good as it could be but it's illustrative. + +01/16/04 adam + + * implement a bunch of ultra-simple TriMesh functions that were + in the headers but not in the code -- patch by + Vadim Macagon + + * disable temporal coherence on trimeshes by default, since + it has scaleability issues that don't make it a general clear win. + +12/01/03 adam + + * implement dxHashSpace::collide2(), not particularly efficiently. + +11/14/03 adam + + * applied several Trimesh fixes and improvements from + Aras Pranckevicius + +10/22/03 adam + + * apply Nguyen Binh's work for removing many dSetZero() calls + and some other extraneous initializations. + +07/29/03 martin + + * added dJointAdd*Torque/Force(). + +07/10/03 russ + + * added the StepFast code, by David Whittaker. + +07/02/03 martin + + * added dMassSet*Total(). + +07/01/03 martin + + * added joint limits and motors to universal joints. + + * reversed the polarity of the dJOINT_REVERSE flag. + +06/30/03 russ + + * added the TriMesh geom class and the quad tree space to the ODE + core. both of these were developed by Erwin de Vries. added OPCODE + to the ODE distribution, this is required by TriMesh. + +06/23/03 martin + + * added dGeomSetQuaternion() and dGeomGetQuaternion() + + * added dJointGet*Anchor2() + +05/07/03 russ + + * added dGeomGetSpace(). + +02/05/03 russ + + * added dMassSetCylinder(). + +12/07/02 russ + + * added dAreConnectedExcluding(). + +11/30/02 russ + + * added the ray geom class. + + * added the dGeomXXXPointDepth() functions. + + * added a collision test infrastructure, and some more tests. + +11/24/02 russ + + * added support for multiple box-box contacts. + +11/10/02 russ + + * added new collision system. select between the old/new system by + setting the ODE_OLD_COLLISION variable in config/user-settings. + +10/28/02 russ + + * fixed two problems in the LCP code to improve the reliability of + the dContactApprox1 contact mode. + + * added a FAQ question about rolling bodies getting stuck when they + hit multiple geoms. + +09/08/02 russ + + * added dClosestLineSegmentPoints(). + * implemented dCollideCB(). + +08/28/02 russ + + * added dJointSetFeedback() and dJointGetFeedback(). + +08/05/02 russ + + * added dGeomTransformSetInfo() and dGeomTransformGetInfo(). + +07/13/02 russ + + * added dBodySetForce(), dBodySetTorque(), dWorldImpulseToForce(), + dBodyGetPosRelPoint(), dBodyGetPosRelPoint(), dBodyVectorToWorld(), + dBodyVectorFromWorld(). + + * added dBodyGetPointVel() (thanks to Colin Reed). + + * added a new C++ interface (from Martin C. Martin, with modifications + by russ). the old C++ interface is now in odecpp_old.h. + +06/25/02 russ + + * added an additional BSD-style licensing option for ODE. + +06/23/02 russ + + * added dCloseODE(), contributed by Nate Waddoups and David McClurg. + +05/16/02 russ + + * added dSpaceQuery(), contributed by Nate Waddoups. + +04/07/02 russ + + * added a section to the documentation for universal joints. + this includes a picture of the joint. + +04/05/02 russ + + * added a universal joint class (generously contributed by + Martin C. Martin). it doesn't (yet) have a motor or joint limits, + but it does come with tests. + +03/11/02 russ + + * makefile changes to accomodate OSs with command line length + limitations (thanks to Norman Lin). + +01/06/02 russ + + * added the dBodySetGravityMode() and dBodyGetGravityMode() + functions, which change the dxBodyNoGravity body flag. + + * added support for building a DLL with MSVC - there is now a + msvc-dll target. thanks to Norman Lin for doing this. + +12/28/01 russ + + * added the dParamCFM joint parameter. + +12/24/01 russ + + * reworked the build system to make it more cross-platform. + there is now a single top-level makefile and a configurator.c + program. see the INSTALL file for details. + +12/04/01 russ + + * the "angular motor" joint has been completed, and a new section + has been added to the documentation. + +11/26/01 russ + + * added a new joint type: "angular motor". using this joint is a good + way to get ball-joint motors and limits. this is work in progress - + it has not been fully implemented or tested yet. + +11/22/01 russ + + * replaced the mmap()-based joint group stack (stack.cpp) with a + malloc()-based arena stack (obstack.cpp). this will be more + portable and should not impact performance. + +11/12/01 russ + + * changed the meaning of the 'flags' parameter to dCollide() and + related functions: now the size of the contact buffer is kept in + the lower 16 bits. this change will be backward compatible. + + * added dBodyGetFiniteRotationMode() and dBodyGetFiniteRotationAxis(). + + * added dBodyAddForceAtRelPos() function. + +11/11/01 russ + + * added the ability to manually enable and disable bodies. + see dBodyEnable(), dBodyDisable(), dBodyIsEnabled(). + + * fixed a potential bug: when a world is destroyed that contains + joints in joint groups, those joints are marked as "deactivated" in + the joint group, so when the joint group is destroyed they can be + ignored. + + * the test_boxstack demo has new options to enable and disable bodies. + + * new configuration parameter in config.h: dEFFICIENT_SIZE. + +11/11/01 russ + + * started the change log for ODE. changes older than today were added + to this file by inspecting the CVS logs. + +11/05/01 russ + + * added REAL() constructions for floating point numbers, to prevent + many warnings when compiling under VC++. + +11/03/01 russ + + * added geometry transform class, documented composite objects. + + * added collision rule: no contacts if both geoms on the same body. + this is not the best rule, may have to remove this in the future. + + * new dMassAdd() function. + + * capped cylinder to capped cylinder collision function. + +10/31/01 russ + + * increase CFM in some demos to make them more robust. + +10/29/01 russ + + * added new accessor functions. + +10/19/01 russ + + * added the dJOINT_TWOBODIES flag to the joint, that says it can not + be attached to just one body. + +10/12/01 russ + + * fixed a collision bug in dCollide() that was causing memory + corruption when multiple contacts were being returned. + +10/11/01 russ + + * joints can now return m=0 to be "inactive". added a "null" joint + to test this. + +10/09/01 russ + + * in the LCP solver, try to fail gracefully when s <= 0. + + * dAABBTestFn() API change. + +10/08/01 russ + + * fixed a contact swapping bug in dCollide(). + +10/07/01 russ + + * added capped cylinder geometry object. + +09/30/01 russ + + * the test_buggy demo now uses geometry groups. + + * added a dAABBTestFn field in the geometry classes. + +09/29/01 russ + + * added geometry groups. + +09/20/01 russ + + * added finite rotation stuff. diff --git a/thirdparty/ode-0.16.5/CMakeLists.txt b/thirdparty/ode-0.16.5/CMakeLists.txt new file mode 100644 index 0000000..3774c73 --- /dev/null +++ b/thirdparty/ode-0.16.5/CMakeLists.txt @@ -0,0 +1,982 @@ +cmake_minimum_required(VERSION 2.8.11) + +project(ODE) + +include(CheckFunctionExists) +include(CheckIncludeFiles) +include(CMakeDependentOption) +include(GNUInstallDirs) + +set(VERSION_MAJOR 0) +set(VERSION_MINOR 16) +set(VERSION_PATCH 5) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +option(BUILD_SHARED_LIBS "Build shared libraries." ON) +option(ODE_16BIT_INDICES "Use 16-bit indices for trimeshes (default is 32-bit)." OFF) +option(ODE_NO_BUILTIN_THREADING_IMPL "Disable built-in multithreaded threading implementation." OFF) +option(ODE_NO_THREADING_INTF "Disable threading interface support (external implementations cannot be assigned." OFF) +option(ODE_OLD_TRIMESH "Use old OPCODE trimesh-trimesh collider." OFF) +option(ODE_WITH_DEMOS "Builds the demo applications and DrawStuff library." ON) +option(ODE_WITH_GIMPACT "Use GIMPACT for trimesh collisions (experimental)." OFF) +option(ODE_WITH_LIBCCD "Use libccd for handling some collision tests absent in ODE." OFF) +option(ODE_WITH_OPCODE "Use old OPCODE trimesh-trimesh collider." ON) +option(ODE_WITH_OU "Use TLS for global caches (allows threaded collision checks for separated spaces)." OFF) +option(ODE_WITH_TESTS "Builds the unit test application." ON) + +cmake_dependent_option(ODE_WITH_LIBCCD_BOX_CYL "Use libccd for box-cylinder." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CAP_CYL "Use libccd for capsule-cylinder." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CYL_CYL "Use libccd for cylinder-cylinder." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CONVEX_BOX "Use libccd for convex-box." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CONVEX_CAP "Use libccd for convex-capsule." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CONVEX_CONVEX "Use libccd for convex-convex." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CONVEX_CYL "Use libccd for convex-cylinder." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_CONVEX_SPHERE "Use libccd for convex-sphere." ON "ODE_WITH_LIBCCD" OFF) +cmake_dependent_option(ODE_WITH_LIBCCD_SYSTEM "Use system libccd." OFF "ODE_WITH_LIBCCD" OFF) + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + option(ODE_DOUBLE_PRECISION "Use double-precision math." OFF) +else() + option(ODE_DOUBLE_PRECISION "Use double-precision math." ON) +endif() + +find_package(OpenGL) +find_package(Threads) + +set(CMAKE_REQUIRED_INCLUDES ${OPENGL_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${OPENGL_LIBRARIES}) + +if(APPLE AND OPENGL_FOUND) + set(HAVE_APPLE_OPENGL_FRAMEWORK ON) +endif() +check_include_files(alloca.h HAVE_ALLOCA_H) +check_function_exists(gettimeofday HAVE_GETTIMEOFDAY) +check_include_files(inttypes.h HAVE_INTTYPES_H) +check_function_exists(isnan HAVE_ISNAN) +check_function_exists(isnanf HAVE_ISNANF) +check_include_files(malloc.h HAVE_MALLOC_H) +check_function_exists(pthread_attr_setstacklazy HAVE_PTHREAD_ATTR_SETSTACKLAZY) +check_function_exists(pthread_condattr_setclock HAVE_PTHREAD_CONDATTR_SETCLOCK) +check_include_files(stdint.h HAVE_STDINT_H) +check_include_files(sys/time.h HAVE_SYS_TIME_H) +check_include_files(sys/types.h HAVE_SYS_TYPES_H) +check_include_files(unistd.h HAVE_UNISTD_H) +check_function_exists(_isnan HAVE__ISNAN) +check_function_exists(_isnanf HAVE__ISNANF) +check_function_exists(__isnan HAVE___ISNAN) +check_function_exists(__isnanf HAVE___ISNANF) +if(APPLE) + set(MAC_OS_X_VERSION 1000) + check_function_exists(OSAtomicAdd32Barrier MAC_OS_X_VERSION_1040) + if(MAC_OS_X_VERSION_1040) + set(MAC_OS_X_VERSION 1040) + endif() + check_function_exists(OSAtomicAnd32OrigBarrier MAC_OS_X_VERSION_1050) + if(MAC_OS_X_VERSION_1050) + set(MAC_OS_X_VERSION 1050) + endif() +endif() +if(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|x86.*|x86_64.*|amd64.*|AMD64.*") + set(PENTIUM ON) +endif() +if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64.*|amd64.*|AMD64.*") + set(X86_64_SYSTEM ON) +endif() +if(ODE_WITH_OU) + set(_OU_FEATURE_SET _OU_FEATURE_SET_TLS) +elseif(NOT ODE_NO_THREADING_INTF) + set(_OU_FEATURE_SET _OU_FEATURE_SET_ATOMICS) +else() + set(_OU_FEATURE_SET _OU_FEATURE_SET_BASICS) +endif() +set(_OU_NAMESPACE odeou) +if(WIN32 OR CYGWIN) + set(_OU_TARGET_OS _OU_TARGET_OS_WINDOWS) +elseif(APPLE) + set(_OU_TARGET_OS _OU_TARGET_OS_MAC) +elseif(QNXNTO) + set(_OU_TARGET_OS _OU_TARGET_OS_QNX) +elseif(CMAKE_SYSTEM MATCHES "SunOS-4") + set(_OU_TARGET_OS _OU_TARGET_OS_SUNOS) +else() + set(_OU_TARGET_OS _OU_TARGET_OS_GENUNIX) +endif() + +configure_file(config.h.cmake.in ode/src/config.h) + +if(ODE_DOUBLE_PRECISION) + set(CCD_PRECISION CCD_DOUBLE) + set(ODE_PRECISION dDOUBLE) +else() + set(CCD_PRECISION CCD_SINGLE) + set(ODE_PRECISION dSINGLE) +endif() + +configure_file(libccd/src/ccd/precision.h.in include/ccd/precision.h) +configure_file(include/ode/precision.h.in include/ode/precision.h) + +set(ODE_VERSION ${VERSION}) + +configure_file(include/ode/version.h.in include/ode/version.h) + +set( + HDRS + include/ode/collision.h + include/ode/collision_space.h + include/ode/collision_trimesh.h + include/ode/common.h + include/ode/compatibility.h + include/ode/contact.h + include/ode/cooperative.h + include/ode/error.h + include/ode/export-dif.h + include/ode/mass.h + include/ode/matrix.h + include/ode/matrix_coop.h + include/ode/memory.h + include/ode/misc.h + include/ode/objects.h + include/ode/ode.h + include/ode/odeconfig.h + include/ode/odecpp.h + include/ode/odecpp_collision.h + include/ode/odeinit.h + include/ode/odemath.h + include/ode/odemath_legacy.h + include/ode/rotation.h + include/ode/threading.h + include/ode/threading_impl.h + include/ode/timer.h + ${CMAKE_CURRENT_BINARY_DIR}/include/ode/precision.h + ${CMAKE_CURRENT_BINARY_DIR}/include/ode/version.h +) + +set( + SRCS + ode/src/array.cpp + ode/src/array.h + ode/src/box.cpp + ode/src/capsule.cpp + ode/src/collision_cylinder_box.cpp + ode/src/collision_cylinder_plane.cpp + ode/src/collision_cylinder_sphere.cpp + ode/src/collision_kernel.cpp + ode/src/collision_kernel.h + ode/src/collision_quadtreespace.cpp + ode/src/collision_sapspace.cpp + ode/src/collision_space.cpp + ode/src/collision_space_internal.h + ode/src/collision_std.h + ode/src/collision_transform.cpp + ode/src/collision_transform.h + ode/src/collision_trimesh_colliders.h + ode/src/collision_trimesh_disabled.cpp + ode/src/collision_trimesh_gimpact.h + ode/src/collision_trimesh_internal.h + ode/src/collision_trimesh_opcode.h + ode/src/collision_util.cpp + ode/src/collision_util.h + ode/src/common.h + ode/src/convex.cpp + ode/src/coop_matrix_types.h + ode/src/cylinder.cpp + ode/src/default_threading.cpp + ode/src/default_threading.h + ode/src/error.cpp + ode/src/error.h + ode/src/export-dif.cpp + ode/src/fastdot.cpp + ode/src/fastdot_impl.h + ode/src/fastldltfactor.cpp + ode/src/fastldltfactor_impl.h + ode/src/fastldltsolve.cpp + ode/src/fastldltsolve_impl.h + ode/src/fastlsolve.cpp + ode/src/fastlsolve_impl.h + ode/src/fastltsolve.cpp + ode/src/fastltsolve_impl.h + ode/src/fastvecscale.cpp + ode/src/fastvecscale_impl.h + ode/src/heightfield.cpp + ode/src/heightfield.h + ode/src/lcp.cpp + ode/src/lcp.h + ode/src/mass.cpp + ode/src/mat.cpp + ode/src/mat.h + ode/src/matrix.cpp + ode/src/matrix.h + ode/src/memory.cpp + ode/src/misc.cpp + ode/src/nextafterf.c + ode/src/objects.cpp + ode/src/objects.h + ode/src/obstack.cpp + ode/src/obstack.h + ode/src/ode.cpp + ode/src/odeinit.cpp + ode/src/odemath.cpp + ode/src/odemath.h + ode/src/odeou.h + ode/src/odetls.h + ode/src/plane.cpp + ode/src/quickstep.cpp + ode/src/quickstep.h + ode/src/ray.cpp + ode/src/resource_control.cpp + ode/src/resource_control.h + ode/src/rotation.cpp + ode/src/simple_cooperative.cpp + ode/src/simple_cooperative.h + ode/src/sphere.cpp + ode/src/step.cpp + ode/src/step.h + ode/src/threaded_solver_ldlt.h + ode/src/threading_atomics_provs.h + ode/src/threading_base.cpp + ode/src/threading_base.h + ode/src/threading_fake_sync.h + ode/src/threading_impl.cpp + ode/src/threading_impl.h + ode/src/threading_impl_posix.h + ode/src/threading_impl_templates.h + ode/src/threading_impl_win.h + ode/src/threading_pool_posix.cpp + ode/src/threading_pool_win.cpp + ode/src/threadingutils.h + ode/src/timer.cpp + ode/src/typedefs.h + ode/src/util.cpp + ode/src/util.h + ode/src/joints/amotor.cpp + ode/src/joints/amotor.h + ode/src/joints/ball.cpp + ode/src/joints/ball.h + ode/src/joints/contact.cpp + ode/src/joints/contact.h + ode/src/joints/dball.cpp + ode/src/joints/dball.h + ode/src/joints/dhinge.cpp + ode/src/joints/dhinge.h + ode/src/joints/fixed.cpp + ode/src/joints/fixed.h + ode/src/joints/hinge.cpp + ode/src/joints/hinge.h + ode/src/joints/hinge2.cpp + ode/src/joints/hinge2.h + ode/src/joints/joint.cpp + ode/src/joints/joint.h + ode/src/joints/joint_internal.h + ode/src/joints/joints.h + ode/src/joints/lmotor.cpp + ode/src/joints/lmotor.h + ode/src/joints/null.cpp + ode/src/joints/null.h + ode/src/joints/piston.cpp + ode/src/joints/piston.h + ode/src/joints/plane2d.cpp + ode/src/joints/plane2d.h + ode/src/joints/pr.cpp + ode/src/joints/pr.h + ode/src/joints/pu.cpp + ode/src/joints/pu.h + ode/src/joints/slider.cpp + ode/src/joints/slider.h + ode/src/joints/transmission.cpp + ode/src/joints/transmission.h + ode/src/joints/universal.cpp + ode/src/joints/universal.h +) + +if(ODE_WITH_GIMPACT AND NOT ODE_NO_TRIMESH) + list( + APPEND SRCS + GIMPACT/src/gim_boxpruning.cpp + GIMPACT/src/gim_contact.cpp + GIMPACT/src/gim_math.cpp + GIMPACT/src/gim_memory.cpp + GIMPACT/src/gim_tri_tri_overlap.cpp + GIMPACT/src/gim_trimesh.cpp + GIMPACT/src/gim_trimesh_capsule_collision.cpp + GIMPACT/src/gim_trimesh_ray_collision.cpp + GIMPACT/src/gim_trimesh_sphere_collision.cpp + GIMPACT/src/gim_trimesh_trimesh_collision.cpp + GIMPACT/src/gimpact.cpp + ode/src/collision_convex_trimesh.cpp + ode/src/collision_cylinder_trimesh.cpp + ode/src/collision_trimesh_box.cpp + ode/src/collision_trimesh_ccylinder.cpp + ode/src/collision_trimesh_gimpact.cpp + ode/src/collision_trimesh_internal.cpp + ode/src/collision_trimesh_internal_impl.h + ode/src/collision_trimesh_internal.h + ode/src/collision_trimesh_plane.cpp + ode/src/collision_trimesh_ray.cpp + ode/src/collision_trimesh_sphere.cpp + ode/src/collision_trimesh_trimesh.cpp + ode/src/gimpact_contact_export_helper.cpp + ode/src/gimpact_contact_export_helper.h + ode/src/gimpact_gim_contact_accessor.h + ode/src/gimpact_plane_contact_accessor.h + ) +endif() + +if(ODE_WITH_LIBCCD) + if(NOT ODE_WITH_LIBCCD_SYSTEM) + list( + APPEND SRCS + libccd/src/alloc.c + libccd/src/ccd.c + libccd/src/mpr.c + libccd/src/polytope.c + libccd/src/support.c + libccd/src/vec3.c + libccd/src/ccd/alloc.h + libccd/src/ccd/ccd.h + libccd/src/ccd/compiler.h + libccd/src/ccd/dbg.h + libccd/src/ccd/list.h + libccd/src/ccd/quat.h + libccd/src/ccd/polytope.h + libccd/src/ccd/simplex.h + libccd/src/ccd/support.h + libccd/src/ccd/vec3.h + libccd/src/custom/ccdcustom/quat.h + libccd/src/custom/ccdcustom/vec3.h + ) + endif() + + list( + APPEND SRCS + ode/src/collision_libccd.cpp + ode/src/collision_libccd.h + ) +endif() + +if(ODE_WITH_OPCODE AND NOT ODE_NO_TRIMESH) + list( + APPEND SRCS + ode/src/collision_convex_trimesh.cpp + ode/src/collision_cylinder_trimesh.cpp + ode/src/collision_trimesh_box.cpp + ode/src/collision_trimesh_ccylinder.cpp + ode/src/collision_trimesh_internal.cpp + ode/src/collision_trimesh_internal_impl.h + ode/src/collision_trimesh_internal.h + ode/src/collision_trimesh_opcode.cpp + ode/src/collision_trimesh_plane.cpp + ode/src/collision_trimesh_ray.cpp + ode/src/collision_trimesh_sphere.cpp + ode/src/collision_trimesh_trimesh.cpp + ode/src/collision_trimesh_trimesh_old.cpp + OPCODE/OPC_AABBCollider.cpp + OPCODE/OPC_AABBCollider.h + OPCODE/OPC_AABBTree.cpp + OPCODE/OPC_AABBTree.h + OPCODE/OPC_BaseModel.cpp + OPCODE/OPC_BaseModel.h + OPCODE/OPC_BoxBoxOverlap.h + OPCODE/OPC_Collider.cpp + OPCODE/OPC_Collider.h + OPCODE/OPC_Common.cpp + OPCODE/OPC_Common.h + OPCODE/OPC_HybridModel.cpp + OPCODE/OPC_HybridModel.h + OPCODE/OPC_IceHook.h + OPCODE/OPC_LSSAABBOverlap.h + OPCODE/OPC_LSSCollider.cpp + OPCODE/OPC_LSSCollider.h + OPCODE/OPC_LSSTriOverlap.h + OPCODE/OPC_MeshInterface.cpp + OPCODE/OPC_MeshInterface.h + OPCODE/OPC_Model.cpp + OPCODE/OPC_Model.h + OPCODE/OPC_OBBCollider.cpp + OPCODE/OPC_OBBCollider.h + OPCODE/OPC_OptimizedTree.cpp + OPCODE/OPC_OptimizedTree.h + OPCODE/OPC_Picking.cpp + OPCODE/OPC_Picking.h + OPCODE/OPC_PlanesAABBOverlap.h + OPCODE/OPC_PlanesCollider.cpp + OPCODE/OPC_PlanesCollider.h + OPCODE/OPC_PlanesTriOverlap.h + OPCODE/OPC_RayAABBOverlap.h + OPCODE/OPC_RayCollider.cpp + OPCODE/OPC_RayCollider.h + OPCODE/OPC_RayTriOverlap.h + OPCODE/OPC_Settings.h + OPCODE/OPC_SphereAABBOverlap.h + OPCODE/OPC_SphereCollider.cpp + OPCODE/OPC_SphereCollider.h + OPCODE/OPC_SphereTriOverlap.h + OPCODE/OPC_TreeBuilders.cpp + OPCODE/OPC_TreeBuilders.h + OPCODE/OPC_TreeCollider.cpp + OPCODE/OPC_TreeCollider.h + OPCODE/OPC_TriBoxOverlap.h + OPCODE/OPC_TriTriOverlap.h + OPCODE/OPC_VolumeCollider.cpp + OPCODE/OPC_VolumeCollider.h + OPCODE/Opcode.cpp + OPCODE/Opcode.h + OPCODE/Stdafx.h + OPCODE/Ice/IceAABB.cpp + OPCODE/Ice/IceAABB.h + OPCODE/Ice/IceAxes.h + OPCODE/Ice/IceBoundingSphere.h + OPCODE/Ice/IceContainer.cpp + OPCODE/Ice/IceContainer.h + OPCODE/Ice/IceFPU.h + OPCODE/Ice/IceHPoint.cpp + OPCODE/Ice/IceHPoint.h + OPCODE/Ice/IceIndexedTriangle.cpp + OPCODE/Ice/IceIndexedTriangle.h + OPCODE/Ice/IceLSS.h + OPCODE/Ice/IceMatrix3x3.cpp + OPCODE/Ice/IceMatrix3x3.h + OPCODE/Ice/IceMatrix4x4.cpp + OPCODE/Ice/IceMatrix4x4.h + OPCODE/Ice/IceMemoryMacros.h + OPCODE/Ice/IceOBB.cpp + OPCODE/Ice/IceOBB.h + OPCODE/Ice/IcePairs.h + OPCODE/Ice/IcePlane.cpp + OPCODE/Ice/IcePlane.h + OPCODE/Ice/IcePoint.cpp + OPCODE/Ice/IcePoint.h + OPCODE/Ice/IcePreprocessor.h + OPCODE/Ice/IceRandom.cpp + OPCODE/Ice/IceRandom.h + OPCODE/Ice/IceRay.cpp + OPCODE/Ice/IceRay.h + OPCODE/Ice/IceRevisitedRadix.cpp + OPCODE/Ice/IceRevisitedRadix.h + OPCODE/Ice/IceSegment.cpp + OPCODE/Ice/IceSegment.h + OPCODE/Ice/IceTriangle.cpp + OPCODE/Ice/IceTriangle.h + OPCODE/Ice/IceTriList.h + OPCODE/Ice/IceTypes.h + OPCODE/Ice/IceUtils.cpp + OPCODE/Ice/IceUtils.h + ) +endif() + +list( + APPEND SRCS + ode/src/odeou.cpp + ode/src/odeou.h + ode/src/odetls.cpp + ode/src/odetls.h + ou/src/ou/atomic.cpp + ou/src/ou/customization.cpp + ou/src/ou/malloc.cpp + ou/src/ou/threadlocalstorage.cpp +) + +add_library(ODE ${SRCS}) + +set_target_properties( + ODE + PROPERTIES + OUTPUT_NAME ode + POSITION_INDEPENDENT_CODE ON + VERSION ${VERSION} +) + +if(WIN32) + if(BUILD_SHARED_LIBS) + set_target_properties(ODE PROPERTIES DEBUG_POSTFIX d) + else() + set_target_properties( + ODE + PROPERTIES + DEBUG_POSTFIX sd + MINSIZEREL_POSTFIX s + RELEASE_POSTFIX s + RELWITHDEBINFO_POSTFIX s + ) + endif() + + if(ODE_DOUBLE_PRECISION) + set_target_properties(ODE PROPERTIES OUTPUT_NAME ode_double) + else() + set_target_properties(ODE PROPERTIES OUTPUT_NAME ode_single) + endif() +endif() + +target_compile_definitions( + ODE + PRIVATE + -D_OU_NAMESPACE=${_OU_NAMESPACE} + -D_OU_FEATURE_SET=${_OU_FEATURE_SET} + -D_OU_TARGET_OS=${_OU_TARGET_OS} + $<$>:dNODEBUG> + -DdOU_ENABLED +) + +if(APPLE) + target_compile_definitions(ODE PRIVATE -DMAC_OS_X_VERSION=${MAC_OS_X_VERSION}) + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + find_library(CORESERVICES_FW NAMES CoreServices) + if (CORESERVICES_FW) + target_link_libraries(ODE PRIVATE ${CORESERVICES_FW}) + endif() + endif() +endif() + +if(WIN32) + target_compile_definitions(ODE PRIVATE -D_CRT_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_USE_MATH_DEFINES) +endif() + +if(BUILD_SHARED_LIBS) + target_compile_definitions(ODE PRIVATE -DODE_DLL) +else() + target_compile_definitions(ODE PRIVATE -DODE_LIB) +endif() + +if(ODE_DOUBLE_PRECISION) + target_compile_definitions(ODE PUBLIC -DdIDEDOUBLE PRIVATE -DCCD_IDEDOUBLE) +else() + target_compile_definitions(ODE PUBLIC -DdIDESINGLE PRIVATE -DCCD_IDESINGLE) +endif() + +target_include_directories( + ODE + PUBLIC + $ + $ + $ + $ + $ + $ + $/${CMAKE_INSTALL_INCLUDEDIR}> +) + +if(ODE_16BIT_INDICES) + target_compile_definitions(ODE PRIVATE -DdTRIMESH_16BIT_INDICES) +endif() + +if(NOT ODE_NO_BUILTIN_THREADING_IMPL) + target_compile_definitions(ODE PRIVATE -DdBUILTIN_THREADING_IMPL_ENABLED) +endif() + +if(ODE_NO_THREADING_INTF) + target_compile_definitions(ODE PRIVATE -DdTHREADING_INTF_DISABLED) +endif() + +if(ODE_WITH_GIMPACT AND NOT ODE_NO_TRIMESH) + target_compile_definitions(ODE PRIVATE -DdTRIMESH_ENABLED -DdTRIMESH_GIMPACT) + target_include_directories(ODE PRIVATE $) +endif() + +if(ODE_WITH_LIBCCD) + if(ODE_WITH_LIBCCD_SYSTEM) + find_package(ccd) + target_compile_definitions(ode PRIVATE -DdLIBCCD_ENABLED -DdLIBCCD_SYSTEM) + target_link_libraries(ODE ccd::ccd) + else() + target_compile_definitions(ODE PRIVATE -DdLIBCCD_ENABLED -DdLIBCCD_INTERNAL) + target_include_directories( + ODE + PRIVATE + $ + $ + ) + endif() + + if(ODE_WITH_LIBCCD_BOX_CYL) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_BOX_CYL) + endif() + + if(ODE_WITH_LIBCCD_CAP_CYL) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CAP_CYL) + endif() + + if(ODE_WITH_LIBCCD_CYL_CYL) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CYL_CYL) + endif() + + if(ODE_WITH_LIBCCD_CONVEX_BOX) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CONVEX_BOX) + endif() + + if(ODE_WITH_LIBCCD_CONVEX_CAP) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CONVEX_CAP) + endif() + + if(ODE_WITH_LIBCCD_CONVEX_CONVEX) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CONVEX_CONVEX) + endif() + + if(ODE_WITH_LIBCCD_CONVEX_CYL) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CONVEX_CYL) + endif() + + if(ODE_WITH_LIBCCD_CONVEX_SPHERE) + target_compile_definitions(ODE PRIVATE -DdLIBCCD_CONVEX_SPHERE) + endif() +endif() + +if(ODE_WITH_OPCODE AND NOT ODE_NO_TRIMESH) + target_compile_definitions(ODE PRIVATE -DdTRIMESH_ENABLED -DdTRIMESH_OPCODE) + + if(ODE_OLD_TRIMESH) + target_compile_definitions(ODE PRIVATE -DdTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER) + endif() + + target_include_directories( + ODE + PRIVATE + $ + $ + ) +endif() + +if(ODE_WITH_OU) + target_compile_definitions(ODE PRIVATE -DdATOMICS_ENABLED -DdTLS_ENABLED) +elseif(NOT ODE_NO_THREADING_INTF) + target_compile_definitions(ODE PRIVATE -DdATOMICS_ENABLED) +endif() + +if(ODE_WITH_OU OR NOT ODE_NO_THREADING_INTF) + target_link_libraries(ODE ${CMAKE_THREAD_LIBS_INIT}) +endif() + +install( + TARGETS ODE + EXPORT ODE + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT development + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime NAMELINK_SKIP + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime +) + +if(BUILD_SHARED_LIBS) + install( + TARGETS ODE + EXPORT ODE + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT development NAMELINK_ONLY + ) +endif() + +if(MSVC AND BUILD_SHARED_LIBS AND NOT CMAKE_VERSION VERSION_LESS 3.1) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} CONFIGURATIONS Debug RelWithDebInfo COMPONENT debug) +endif() + +install(FILES ${HDRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ode COMPONENT development) + +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix "\${prefix}") +set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") +set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +configure_file(ode.pc.in ode.pc @ONLY) +configure_file(ode-config.in ode-config @ONLY) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ode.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT development) +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/ode-config DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT development) + +unset(DRAWSTUFF_RSRC) + +if(ODE_WITH_DEMOS) + set( + DRAWSTUFF_SRCS + include/drawstuff/drawstuff.h + include/drawstuff/version.h + drawstuff/src/drawstuff.cpp + drawstuff/src/internal.h + ) + + if(WIN32) + set(DRAWSTUFF_RSRC drawstuff/src/resources.rc) + list( + APPEND DRAWSTUFF_SRCS + drawstuff/src/resource.h + drawstuff/src/windows.cpp + ${DRAWSTUFF_RSRC} + ) + elseif(APPLE) + list(APPEND DRAWSTUFF_SRCS drawstuff/src/osx.cpp) + else() + list(APPEND DRAWSTUFF_SRCS drawstuff/src/x11.cpp) + endif() + + add_library(drawstuff ${DRAWSTUFF_SRCS}) + target_compile_definitions(drawstuff PUBLIC -DDRAWSTUFF_TEXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/drawstuff/textures") + + if(BUILD_SHARED_LIBS) + target_compile_definitions(drawstuff PRIVATE -DDS_DLL -DUSRDLL) + else() + target_compile_definitions(drawstuff PRIVATE -DDS_LIB) + endif() + + target_include_directories(drawstuff PUBLIC ${OPENGL_INCLUDE_DIR}) + target_link_libraries(drawstuff ODE ${OPENGL_LIBRARIES}) + + if(WIN32) + target_link_libraries(drawstuff winmm) + elseif(APPLE) + find_package(GLUT) + target_include_directories(drawstuff PUBLIC ${GLUT_INCLUDE_DIR}) + target_link_libraries(drawstuff ${GLUT_LIBRARIES}) + else() + find_package(X11) + target_include_directories(drawstuff PUBLIC ${X11_INCLUDE_DIR}) + target_link_libraries(drawstuff ${X11_LIBRARIES}) + endif() + + set( + ALL_DEMO_SRCS + ode/demo/demo_boxstack.cpp + ode/demo/demo_buggy.cpp + ode/demo/demo_cards.cpp + ode/demo/demo_chain1.c + ode/demo/demo_chain2.cpp + ode/demo/demo_collision.cpp + ode/demo/demo_convex.cpp + ode/demo/demo_crash.cpp + ode/demo/demo_cylvssphere.cpp + ode/demo/demo_dball.cpp + ode/demo/demo_dhinge.cpp + ode/demo/demo_feedback.cpp + ode/demo/demo_friction.cpp + ode/demo/demo_gyro2.cpp + ode/demo/demo_gyroscopic.cpp + ode/demo/demo_heightfield.cpp + ode/demo/demo_hinge.cpp + ode/demo/demo_I.cpp + ode/demo/demo_jointPR.cpp + ode/demo/demo_jointPU.cpp + ode/demo/demo_joints.cpp + ode/demo/demo_kinematic.cpp + ode/demo/demo_motion.cpp + ode/demo/demo_motor.cpp + ode/demo/demo_ode.cpp + ode/demo/demo_piston.cpp + ode/demo/demo_plane2d.cpp + ode/demo/demo_rfriction.cpp + ode/demo/demo_slider.cpp + ode/demo/demo_space.cpp + ode/demo/demo_space_stress.cpp + ode/demo/demo_step.cpp + ode/demo/demo_transmission.cpp + ) + + if(NOT ODE_NO_TRIMESH) + list( + APPEND ALL_DEMO_SRCS + ode/demo/demo_basket.cpp + ode/demo/demo_cyl.cpp + ode/demo/demo_moving_convex.cpp + ode/demo/demo_moving_trimesh.cpp + ode/demo/demo_tracks.cpp + ode/demo/demo_trimesh.cpp + ) + endif() + + foreach(MAIN_DEMO_SRC ${ALL_DEMO_SRCS}) + get_filename_component(DEMO ${MAIN_DEMO_SRC} NAME_WE) + set( + DEMO_SRC + ${MAIN_DEMO_SRC} + ) + + if(NOT WIN32 OR ${DEMO} STREQUAL "demo_ode") + add_executable(${DEMO} ${DEMO_SRC}) + else() + if(NOT BUILD_SHARED_LIBS) + list( + APPEND DEMO_SRC + ${DRAWSTUFF_RSRC} + ) + endif() + + add_executable(${DEMO} WIN32 ${DEMO_SRC}) + if(WIN32 AND MSVC) + set_target_properties(${DEMO} PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup) + endif() + endif() + target_link_libraries(${DEMO} drawstuff) + + if(NOT WIN32 AND ${DEMO} STREQUAL "demo_chain1") + target_link_libraries(${DEMO} m) + endif() + endforeach() +endif() + +if(ODE_WITH_TESTS) + set( + TEST_SRCS + tests/collision.cpp + tests/friction.cpp + tests/joint.cpp + tests/main.cpp + tests/odemath.cpp + tests/joints/amotor.cpp + tests/joints/ball.cpp + tests/joints/dball.cpp + tests/joints/fixed.cpp + tests/joints/hinge.cpp + tests/joints/hinge2.cpp + tests/joints/piston.cpp + tests/joints/pr.cpp + tests/joints/pu.cpp + tests/joints/slider.cpp + tests/joints/universal.cpp + tests/UnitTest++/src/AssertException.cpp + tests/UnitTest++/src/AssertException.h + tests/UnitTest++/src/CheckMacros.h + tests/UnitTest++/src/Checks.cpp + tests/UnitTest++/src/Checks.h + tests/UnitTest++/src/Config.h + tests/UnitTest++/src/DeferredTestReporter.cpp + tests/UnitTest++/src/DeferredTestReporter.h + tests/UnitTest++/src/DeferredTestResult.cpp + tests/UnitTest++/src/DeferredTestResult.h + tests/UnitTest++/src/MemoryOutStream.cpp + tests/UnitTest++/src/MemoryOutStream.h + tests/UnitTest++/src/ReportAssert.cpp + tests/UnitTest++/src/ReportAssert.h + tests/UnitTest++/src/Test.cpp + tests/UnitTest++/src/TestDetails.cpp + tests/UnitTest++/src/TestDetails.h + tests/UnitTest++/src/Test.h + tests/UnitTest++/src/TestList.cpp + tests/UnitTest++/src/TestList.h + tests/UnitTest++/src/TestMacros.h + tests/UnitTest++/src/TestReporter.cpp + tests/UnitTest++/src/TestReporter.h + tests/UnitTest++/src/TestReporterStdout.cpp + tests/UnitTest++/src/TestReporterStdout.h + tests/UnitTest++/src/TestResults.cpp + tests/UnitTest++/src/TestResults.h + tests/UnitTest++/src/TestRunner.cpp + tests/UnitTest++/src/TestRunner.h + tests/UnitTest++/src/TestSuite.h + tests/UnitTest++/src/TimeConstraint.cpp + tests/UnitTest++/src/TimeConstraint.h + tests/UnitTest++/src/TimeHelpers.h + tests/UnitTest++/src/UnitTest++.h + tests/UnitTest++/src/XmlTestReporter.cpp + tests/UnitTest++/src/XmlTestReporter.h + ) + + if(WIN32) + list( + APPEND TEST_SRCS + tests/UnitTest++/src/Win32/TimeHelpers.cpp + tests/UnitTest++/src/Win32/TimeHelpers.h + ) + else() + list( + APPEND TEST_SRCS + tests/UnitTest++/src/Posix/SignalTranslator.cpp + tests/UnitTest++/src/Posix/SignalTranslator.h + tests/UnitTest++/src/Posix/TimeHelpers.cpp + tests/UnitTest++/src/Posix/TimeHelpers.h + ) + endif() + + add_executable(tests ${TEST_SRCS}) + target_include_directories(tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/tests/UnitTest++/src) + target_link_libraries(tests ODE) + + enable_testing() + add_test(tests ${CMAKE_CURRENT_BINARY_DIR}/tests) +endif() + +include(CMakePackageConfigHelpers) + +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/ode-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/ode-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode +) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/ode-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode-${VERSION} + COMPONENT development +) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/ode-config-version.cmake + VERSION ${VERSION} + COMPATIBILITY ExactVersion +) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/ode-config-version.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode-${VERSION} + COMPONENT development +) +install( + EXPORT ODE + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ode-${VERSION} + NAMESPACE ODE:: + FILE ode-export.cmake + COMPONENT development +) + +configure_file(cmake/cmake_uninstall.cmake.in cmake_uninstall.cmake @ONLY) +add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) + +set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "ODE is a free, industrial quality library for simulating articulated rigid body dynamics - for example ground vehicles, legged creatures, and moving objects in VR environments. It is fast, flexible, robust and platform independent, with advanced joints, contact with friction, and built-in collision detection.") + +set(CPACK_COMPONENT_DEVELOPMENT_DEPENDS runtime) +set(CPACK_COMPONENT_DEVELOPMENT_DESCRIPTION "Open Dynamics Engine - development files\n${CPACK_DEBIAN_PACKAGE_DESCRIPTION}") +set(CPACK_COMPONENT_RUNTIME_DESCRIPTION "Open Dynamics Engine - runtime library\n${CPACK_DEBIAN_PACKAGE_DESCRIPTION}") +set(CPACK_DEB_COMPONENT_INSTALL ON) +set(CPACK_DEBIAN_DEVELOPMENT_FILE_NAME "DEB-DEFAULT") +set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_SECTION "libdevel") +set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://www.ode.org/") +set(CPACK_DEBIAN_PACKAGE_NAME "libode") +set(CPACK_DEBIAN_PACKAGE_SECTION "devel") +set(CPACK_DEBIAN_RUNTIME_FILE_NAME "DEB-DEFAULT") +set(CPACK_DEBIAN_RUNTIME_PACKAGE_SECTION "libs") +set(CPACK_DEBIAN_RUNTIME_PACKAGE_SHLIBDEPS ON) +set(CPACK_NSIS_PACKAGE_NAME "ODE ${VERSION}") +set(CPACK_NSIS_URL_INFO_ABOUT "http://www.ode.org/") +set(CPACK_PACKAGE_CONTACT "ode@ode.org") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High performance library for simulating rigid body dynamics") +set(CPACK_PACKAGE_DISPLAY_NAME "ODE ${VERSION}") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "ode-${VERSION}") +set(CPACK_PACKAGE_NAME "ode") +set(CPACK_PACKAGE_VENDOR "") +set(CPACK_PACKAGE_VERSION ${VERSION}) +set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) +set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/COPYING) +set(CPACK_RPM_COMPONENT_INSTALL ON) +set(CPACK_RPM_DEVELOPMENT_FILE_NAME "RPM-DEFAULT") +set(CPACK_RPM_development_PACKAGE_DESCRIPTION "The ode-devel package contains libraries and header files for developing applications that use ode or ode-double.") +set(CPACK_RPM_development_PACKAGE_NAME "ode-devel") +set(CPACK_RPM_development_PACKAGE_SUMMARY "Development files for ODE") +set(CPACK_RPM_PACKAGE_DESCRIPTION "ODE is an open source, high performance library for simulating rigid body dynamics. It is fully featured, stable, mature and platform independent with an easy to use C/C++ API. It has advanced joint types and integrated collision detection with friction. ODE is useful for simulating vehicles, objects in virtual reality environments and virtual creatures. It is currently used in many computer games, 3D authoring tools and simulation tools.") +set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") +set(CPACK_RPM_PACKAGE_LICENSE "BSD or LGPLv2+") +set(CPACK_RPM_PACKAGE_NAME "ode") +set(CPACK_RPM_PACKAGE_SUMMARY "High performance library for simulating rigid body dynamics") +set(CPACK_RPM_PACKAGE_URL "http://www.ode.org/") +set(CPACK_RPM_RUNTIME_FILE_NAME "RPM-DEFAULT") + +if(ODE_DOUBLE_PRECISION) + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_CONFLICTS "libode-sp-dev") + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS "libode6") + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME "libode-dev") + set(CPACK_DEBIAN_RUNTIME_PACKAGE_CONFLICTS "libode6sp") + set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "libode6") + set(CPACK_RPM_development_PACKAGE_REQUIRES "ode-double") + set(CPACK_RPM_runtime_PACKAGE_CONFLICTS "ode") + set(CPACK_RPM_runtime_PACKAGE_DESCRIPTION "The ode-double package contains a version of the ODE library for simulating rigid body dynamics compiled with double precision.") + set(CPACK_RPM_runtime_PACKAGE_NAME "ode-double") + set(CPACK_RPM_runtime_PACKAGE_SUMMARY "ODE physics library compiled with double precision") +else() + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_CONFLICTS "libode-dev") + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_DEPENDS "libode6sp") + set(CPACK_DEBIAN_DEVELOPMENT_PACKAGE_NAME "libode-sp-dev") + set(CPACK_DEBIAN_RUNTIME_PACKAGE_CONFLICTS "libode6") + set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME "libode6sp") + set(CPACK_RPM_development_PACKAGE_REQUIRES "ode") + set(CPACK_RPM_runtime_PACKAGE_CONFLICTS "ode-double") + set(CPACK_RPM_runtime_PACKAGE_NAME "ode") +endif() + +include(CPack) diff --git a/thirdparty/ode-0.16.5/COPYING b/thirdparty/ode-0.16.5/COPYING new file mode 100644 index 0000000..b21a778 --- /dev/null +++ b/thirdparty/ode-0.16.5/COPYING @@ -0,0 +1,32 @@ +ODE is dual-licensed under either: + + * GNU Lesser General Public License v 2.1 or later. + see LICENSE.TXT + + * Modified 3-clause BSD license. + see LICENSE-BSD.TXT + + +Third-party libraries bundled with ODE: + + * GIMPACT: dual-licensed under either: + - GNU Lesser General Public License v 2.1 or later. + see GIMPACT-LICENSE-LGPL.TXT + - Modified 3-clause BSD license. + see GIMPACT/GIMPACT-LICENSE-BSD.TXT + + * libccd: Modified 3-clause BSD License + see libccd/BSD-LICENSE + + * OU/ODER: triple-licensed under either: + - GNU Lesser General Public License v 3 or later. + see ou/LICENSE.TXT + see ou/LICENSE-LESSER.TXT + - Modified 3-clause BSD license. + see ou/LICENSE-BSD.TXT + - ZLIB license. + see ou/LICENSE-ZLIB.TXT + + * OPCODE: under the same terms as ODE + see OPCODE/COPYING + diff --git a/thirdparty/ode-0.16.5/CSR.txt b/thirdparty/ode-0.16.5/CSR.txt new file mode 100644 index 0000000..d2ab6ad --- /dev/null +++ b/thirdparty/ode-0.16.5/CSR.txt @@ -0,0 +1,422 @@ +CODING STYLE REQUIREMENTS + +Copyright (c) 2011-2017 Oleh Derevenko +This article is provided to you under the terms of Artistic License 2.0 +(http://www.opensource.org/licenses/artistic-license-2.0). + +(I) General Coding Requirements +============================================================================= + +1. Not more than one complex construction per function +---------------------------------------------------------------------------- + +A function must not contain more than one* operator from the following set: +for, while, do..while, switch and constructions try..catch, try..finally. +Moreover, those operators/constructions must not appear inside of a +conditional operator. + +* Loop inclusion is allowed if multi-dimensional array must be iterated and +all the elements are uniform and need to be processed linearly. +try..finally construction inclusion is allowed for several resource +allocations that need to be performed together. + + +2. Absence of jumps +---------------------------------------------------------------------------- + +goto and continue operators must not be used. + + +3. Single exit point at the end of function +---------------------------------------------------------------------------- + +A function must not have other exit points except for the end of its +definition. If a function returns a value, return operator must be the last +syntactical construction in the function. + + +4. Zero value means failure +---------------------------------------------------------------------------- + +Function results must be chosen the way that binary zero value had a meaning +of failure. Similarly, types must be designed so that binary zero element +had the meaning of an invalid value (if invalid element concept is applicable +for the type). + + +5. Variables and parameters are initialized with zeroes +---------------------------------------------------------------------------- + +Variables and class fields must be initialized with values of binary zero +presentation. Public enumerated types must be designed to have zero element +and that element is to be the default element. Function default parameters +must be chosen in the form to have binary zero value. + + +6. Variables are not reused +---------------------------------------------------------------------------- + +Variables must not be reused for other purposes after they have already been +used for something. The only value that might be stored in a variable is that +described with its name. + + +7. Parameters passed by value are treated as constants +---------------------------------------------------------------------------- + +Parameters that are passed by value must not be modified*. All of them must +be treated as if they have been implicitly declared with const modifier. + +* An exception could be the case when a value loses its meaning (e.g. a +pointer to an object being deleted). + + +8. Result assignment is performed at the end of function +---------------------------------------------------------------------------- + +Every function returning a result must have a variable to contain the result +of that function. It is to be declared (initialized if necessary) at the +beginning of the function* and the only access to it after that should be its +final value assignment. The assignment should be the last meaningful operator +in an execution branch**. Several assignments, one per each execution branch, +are allowed. + +* It is allowed to declare result variable with assignment immediately before +return operator. +** It is allowed to include technical constructions like logging or +performance measuring after result variable assignment. + + +9. Parameters by reference are not used in expressions +---------------------------------------------------------------------------- + +Parameters of simple types passed by reference must be copied into local +variables at function entry and then be assigned their final values at +function exit. +Output parameters can be initialized at function entry and must be assigned +their final values immediately before the function result variable assignment. + + +(II) Class Design Requirements +============================================================================= + +1. Classes work with their fields on their own +---------------------------------------------------------------------------- + +A function or method must not call several methods of other class, some of +them being used to return and the others being used to assign the class fields. +Such a code must be implemented as a method of that other class. + + +2. No direct access to the fields +---------------------------------------------------------------------------- + +All the work with class fields (including fields of own class) must be +performed with aid of dedicated methods (accessors) that return and assign +field values. Exceptions can be made for constructors/destructors and methods +dedicated for field initialization/finalization. + + +3. Private fields only +---------------------------------------------------------------------------- + +All class fields must be private. + + +4. No code in constructors and destructors +---------------------------------------------------------------------------- + +Class constructors must not have raw code other than doing trivial field +initialization. If creation of contained objects is necessary or other +operations need to be done they are to be performed via calls to the class +methods rather than placed directly in constructor. Initial zero-assignment +to a field is always required even if that field is later to be +unconditionally assigned in methods called from the constructor. +Similarly, a destructor must free contained objects with calls to the class +methods rather than containing that code inline. + + +5. No code in callbacks +---------------------------------------------------------------------------- + +Event handlers, callback interface methods and static callback methods must +not have meaningful code within them. Their implementation should validate +input arguments, convert them to proper internal types if necessary, and call +one or more other methods of the class. These methods must not be declated in +public section. + + +6. No public virtual methods +---------------------------------------------------------------------------- + +Methods declared as virtual must not be public. The public calls to such +methods must be wrapped with ordinary class methods. + + +7. No logical level violations +---------------------------------------------------------------------------- + +Methods of lower logical levels must not call any methods of higher logical +levels of the class. In particular, methods declared as protected and private +must not call methods declared in public sections of own or ancestor classes. +Methods declared as public may only call protected and private methods. +Similarly classes of lower logical levels must not call public methods of +classes at higher logical levels. Such calls are only possible via dedicated +callback methods or callback interfaces. + + +(III) Canonical Function Structures +============================================================================= + +0. Preamble +---------------------------------------------------------------------------- + +Following are general function structures encouraged to be used for coding +all the program logic. Any algorithm with branching can be implemented +with these types of functions. + +Using these function structures helps to make code clear and error-proof. + + +1. A Boolean Function +---------------------------------------------------------------------------- + +The Boolean Function can be used to implement algorithms with conditional +branching. + +bool PerformSomeAction(...) +{ + bool bResult = false; + + // Some linear code + + if (...) + { + // Some linear code + + if (...) // Optionally... + { + bResult = true; + } + } + // Optionally... + else if (...) + { + // Some linear code + + bResult = true; + } + + return bResult; +} + +The idea is to have result variable initialized with false at entry and then +have an arbitrary structure of conditional operators with some branches +changing result variable to true on exit. + + +2. A Validation Function +---------------------------------------------------------------------------- + +The Validation Function is an alternative to Boolean Function to implement +conditional logic. It's mostly convenient for implementing multi-step +algorithms that may fail (like validations or initializations of multiple +items of non-uniform nature). + +bool PerformSomeValidation(...) +{ + bool bResult = false; + + do + { + // Some linear code + + // Optionally... + if ( !(...) ) + { + // Some error handling // Optionally... + break; + } + + // Optionally... + if (...) + { + // Some linear code + + if ( !(...) ) + { + // Some error handling // Optionally... + break; + } + + // Some linear code + } + + bResult = true; + } + while (false); + + return bResult; +} + +If function execution has side effects which need to be rolled back in case +of failures on subsequent steps the function structure can be altered to the +following form. + +bool PerformSomeInitialization(...) +{ + bool bResult = false; + + bool bFirstSideEffectApplied = false, bSecondSideEffectApplied = false, ...; + + do + { + // Some linear code + + if ( !ExecuteFirstSideEffectApplication(...) ) + { + // Some error handling // Optionally... + break; + } + + bFirstSideEffectApplied = true + + // Some linear code + + if ( !ExecuteSecondSideEffectApplication(...) ) + { + // Some error handling // Optionally... + break; + } + + bSecondSideEffectApplied = true + + ... + + // Some linear code + + if ( !ExecuteLastSideEffectApplication(...) ) + { + // Some error handling // Optionally... + break; + } + + bResult = true; + } + while (false); + + if (!bResult) + { + if (bFirstSideEffectApplied) + { + if (bSecondSideEffectApplied) + { + if (...) + { + ... + } + + ExecuteSecondSideEffectRollback(...); + } + + ExecuteFirstSideEffectRollback(...); + } + } + + return bResult; +} + + +3. A Loop Validation Function +---------------------------------------------------------------------------- + +The Loop Validation Function can be used for processing sequences of items +while the processing of each or some individual items can fail. + +bool PerformLoopValidation(...) +{ + bool bAnyFailure = false; + + for (...) // Or any other loop control operator + { + // Some linear code + + if ( !(...) ) + { + // Some error handling // Optional + bAnyFailure = true; + break; + } + + // Some linear code + } + + bool bResult = !bAnyFailure; + return bResult; +} + +In case if a loop processing function may apply side effects on each step +which need to be reverted in case of a failure on subsequent steps the +functions need to be organized in the following four-function two-level +structure. + +bool PerformLoopInitialization(...) +{ + bool bResult = false; + + size_t nFailureItem; + + if (DoPerformLoopInitialization(..., nFailureItem)) + { + bResult = true; + } + else + { + DoPerformLoopFinalization(..., nFailureItem); + } + + return bResult; +} + +void PerformLoopFinalization(...) +{ + DoPerformLoopFinalization(..., npos); // Here "npos" stands for the invalid item index +} + +bool DoPerformLoopInitialization(..., size_t &nOutFailureItem) +{ + bool bAnyFailure = false; + size_t nOutFailureItem = npos; + + for (...) // Or any other loop control operator + { + // Some linear code + + if ( !(...) ) + { + // Some error handling // Optional + nOutFailureItem = ...; + bAnyFailure = true; + break; + } + + // Some linear code + } + + bool bResult = !bAnyFailure; + return bResult; +} + +void DoPerformLoopFinalization(..., size_t nExternalFinalizationEndItem/*=npos*/) +{ + size_t nFinalizationEndItem = nExternalFinalizationEndItem == npos + ? ... /* total item count */ + : nExternalFinalizationEndItem; + + for (... /* loop until nFinalizationEndItem */) // Or any other loop control operator + { + // Some linear code + RevertLoopItemSideEffects(...); + } +} + diff --git a/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-BSD.TXT b/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-BSD.TXT new file mode 100644 index 0000000..d501dcc --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-BSD.TXT @@ -0,0 +1,29 @@ +GIMPACT : Geometric tools for VR. + +Copyright (c) 2006 , Francisco Len. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the GIMPACT nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. \ No newline at end of file diff --git a/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-LGPL.TXT b/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-LGPL.TXT new file mode 100644 index 0000000..60b8156 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/GIMPACT-LICENSE-LGPL.TXT @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/thirdparty/ode-0.16.5/GIMPACT/Makefile.am b/thirdparty/ode-0.16.5/GIMPACT/Makefile.am new file mode 100644 index 0000000..7a4f8e5 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/Makefile.am @@ -0,0 +1,4 @@ +EXTRA_DIST = GIMPACT-LICENSE-BSD.TXT GIMPACT-LICENSE-LGPL.TXT + +SUBDIRS = include src + diff --git a/thirdparty/ode-0.16.5/GIMPACT/Makefile.in b/thirdparty/ode-0.16.5/GIMPACT/Makefile.in new file mode 100644 index 0000000..e8aa375 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/Makefile.in @@ -0,0 +1,641 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = GIMPACT +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = GIMPACT-LICENSE-BSD.TXT GIMPACT-LICENSE-LGPL.TXT +SUBDIRS = include src +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign GIMPACT/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign GIMPACT/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.am b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.am new file mode 100644 index 0000000..347ae72 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.am @@ -0,0 +1,6 @@ +noinst_HEADERS = \ + gim_boxpruning.h gim_contact.h gim_geometry.h \ + gim_math.h gim_memory.h gimpact.h \ + gim_radixsort.h gim_tri_capsule_collision.h gim_tri_collision.h \ + gim_trimesh.h gim_tri_sphere_collision.h + diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.in b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.in new file mode 100644 index 0000000..78758f7 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/Makefile.in @@ -0,0 +1,533 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = GIMPACT/include/GIMPACT +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_HEADERS = \ + gim_boxpruning.h gim_contact.h gim_geometry.h \ + gim_math.h gim_memory.h gimpact.h \ + gim_radixsort.h gim_tri_capsule_collision.h gim_tri_collision.h \ + gim_trimesh.h gim_tri_sphere_collision.h + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign GIMPACT/include/GIMPACT/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign GIMPACT/include/GIMPACT/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_boxpruning.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_boxpruning.h new file mode 100644 index 0000000..e5415f6 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_boxpruning.h @@ -0,0 +1,323 @@ +#ifndef GIM_BOXPRUNING_H_INCLUDED +#define GIM_BOXPRUNING_H_INCLUDED + +/*! \file gim_boxpruning.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_radixsort.h" +#include "GIMPACT/gim_geometry.h" + +/*! \defgroup BOX_PRUNNING + +\brief +Tools for find overlapping objects on a scenary. These functions sort boxes for faster collisioin queries, using radix sort or quick sort as convenience. See \ref SORTING . +

      +
    • For using these collision routines, you must create a \ref GIM_AABB_SET by using this function : \ref gim_aabbset_alloc. +
    • The GIM_AABB_SET objects must be updated on their boxes on each query, and they must be update by calling \ref gim_aabbset_update +
    • Before calling collision functions, you must create a pair set with \ref GIM_CREATE_PAIR_SET +
    • For finding collision pairs on a scene (common space for objects), call \ref gim_aabbset_self_intersections +
    • For finding collision pairs between two box sets , call \ref gim_aabbset_box_collision +
    • After using collision routines, you must destroy the pairset with \ref GIM_DESTROY_PAIR_SET +
    • When the box set is no longer used, you must destroy it by calling \ref gim_aabbset_destroy +
    +*/ +//! @{ +//! Overlapping pair +struct GIM_PAIR +{ + GUINT32 m_index1; + GUINT32 m_index2; +}; +//typedef struct _GIM_PAIR GIM_PAIR; + +//! Box container +struct GIM_AABB_SET +{ + GUINT32 m_count; + aabb3f m_global_bound;//!< Global calculated bound of all boxes + aabb3f * m_boxes; + GUINT32 * m_maxcoords;//!m_sorted_mincoords == 0, then it allocs the sorted coordinates +*/ +void gim_aabbset_sort(GIM_AABB_SET * aabbset, char calc_global_bound); + +//! log(N) Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +\pre aabbset must be allocated and sorted, the boxes must be already set. +\param aabbset Must be sorted. Global bound isn't required +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_self_intersections_sorted(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs); + +//! NxN Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +\pre aabbset must be allocated, the boxes must be already set. +\param aabbset Global bound isn't required. Doen't need to be sorted. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_self_intersections_brute_force(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs); + +//! log(N) Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. +/*! +\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set. +\param aabbset1 Must be sorted, Global bound is required. +\param aabbset2 Must be sorted, Global bound is required. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_bipartite_intersections_sorted(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs); + +//! NxM Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. +/*! +\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set. +\param aabbset1 Must be sorted, Global bound is required. +\param aabbset2 Must be sorted, Global bound is required. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_bipartite_intersections_brute_force(GIM_AABB_SET * aabbset1,GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs); + + +/* + Brute-Force Vs Sorted pruning +Different approaches must be applied when colliding sets with different number of +elements. When sets have less of 100 boxes, it is often better to apply brute force +approach instead of sorted methods, because at lowlevel bruteforce routines gives +better performance and consumes less resources, due of their simplicity. +But when sets are larger, the complexiity of bruteforce increases exponentially. +In the case of large sets, sorted approach is applied. So GIMPACT has the following +strategies: + +On Sorting sets: +!) When sets have more of 140 boxes, the boxes are sorted by its coded min coord +and the global box is calculated. But when sets are smaller (less of 140 boxes), +it is convenient to apply brute force approach. + +*******************************************************************************/ + +//! Constant for apply approaches between brute force and sorted pruning on bipartite queries +#define GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES 600 +//! Constant for apply approaches between brute force and sorted pruning for box collision +#define GIM_MIN_SORTED_PRUNING_BOXES 140 + + +//Use these functions for general initialization + +//! Initalizes the set. Sort boxes if needed. +/*! +\pre aabbset must be allocated. And the boxes must be already set. +\post If the set has less of GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES boxes, only calcs the global box, + else it Sorts the entire set( Only applicable for large sets) +*/ +void gim_aabbset_update(GIM_AABB_SET * aabbset); + +///Use these functions for general collision + +//! Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +This function sorts the set and then it calls to gim_aabbset_self_intersections_brute_force or gim_aabbset_self_intersections_sorted. This is an example of how to use this function: +\code +//Create contact list +GDYNAMIC_ARRAY collision_pairs; +GIM_CREATE_PAIR_SET(collision_pairs); +//Do collision +gim_aabbset_self_intersections(&aabbset,&collision_pairs); +if(collision_pairs.m_size==0) +{ + GIM_DYNARRAY_DESTROY(collision_pairs);// + return; //no collisioin +} + +//pair pointer +GIM_PAIR *pairs = GIM_DYNARRAY_POINTER(GIM_PAIR,collision_pairs); +GUINT i, ti1,ti2; +for (i=0;im_count >= GIM_MIN_SORTED_PRUNING_BOXES, then it calls to gim_aabbset_sort and then to gim_aabbset_self_intersections_sorted. Global box won't be calculated. +*/ +void gim_aabbset_self_intersections(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs); + +//! Collides two sets. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. +/*! +\pre aabbset1 and aabbset2 must be allocated and updated. See gim_aabbset_update. +\param aabbset1 Must be updated. +\param aabbset2 Must be updated. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_bipartite_intersections(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs); + +///Function for create Box collision result set + +#define GIM_CREATE_BOXQUERY_LIST(dynarray) GIM_DYNARRAY_CREATE(GUINT32,dynarray,G_ARRAY_GROW_SIZE) + +//! Finds intersections between a box and a set. Return the colliding boxes of the set +/*! +\pre aabbset must be allocated and initialized. +\param test_aabb Box for collision query +\param aabbset Set of boxes .Global bound is required. +\param collided Array of GUINT elements, indices of boxes. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_box_collision(aabb3f *test_aabb, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided); + +//! Finds intersections between a box and a set. Return the colliding boxes of the set +/*! +\pre aabbset must be allocated and initialized. +\param vorigin Origin point of ray. +\param vdir Direction vector of ray. +\param tmax Max distance param for ray. +\param aabbset Set of boxes. Global bound is required. +\param collided Array of GUINT elements, indices of boxes. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_ray_collision(vec3f vorigin,vec3f vdir, GREAL tmax, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided); + + +/* +For sorting, each box corner must be discretized to a 32 bit integer. +For this, we take the x and z coordinates from the box corner (a vector vec3f) +Then convert the (x,z) pair to an integer. For convenience, we choose an error +constant for converting the coordinates (0.05). +*******************************************************************************/ + +/** + For fitting the coordinate to an integer, we need to constraint the range of its values. So each coord component (x, z) must lie between 0 and 65536. + 20 give us a 0.05 floating point error +*/ +#define ERROR_AABB 20.0f + +/** +An error of 0.05 allows to make coordinates up to 1638.0f and no less of -1638.0f. +So the maximum size of a room should be about 3276x3276 . Its dimensions must lie between [-1638,1638.0f] +*/ +#define MAX_AABB_SIZE 1638.0f + +//! Converts a vector coordinate to an integer for box sorting +/*! +\param vx X component +\param vz Z component +\param uint_key a GUINT +*/ +#define GIM_CONVERT_VEC3F_GUINT_XZ(vx,vz,uint_key)\ +{\ + GUINT32 _z = ((GUINT32)(vz*ERROR_AABB))+32768;\ + uint_key = ((GUINT32)(vx*ERROR_AABB))+32768;\ + uint_key = (uint_key<<16) + _z;\ +}\ + +//! Converts a vector coordinate to an integer for box sorting, rounding to the upper int +/*! +\param vx X component +\param vz Z component +\param uint_key a GUINT +*/ +#define GIM_CONVERT_VEC3F_GUINT_XZ_UPPER(vx,vz,uint_key)\ +{\ + GUINT32 _z = ((GUINT32)ceilf(vz*ERROR_AABB))+32768;\ + uint_key = ((GUINT32)ceilf(vx*ERROR_AABB))+32768;\ + uint_key = (uint_key<<16) + _z;\ +}\ + + +//! Converts a vector coordinate to an integer for box sorting. Secure clamped +/*! +\param vx X component +\param vz Z component +\param uint_key a GUINT +*/ +#define GIM_CONVERT_VEC3F_GUINT_XZ_CLAMPED(vx,vz,uint_key)\ +{\ + GREAL _cx = CLAMP(vx,-MAX_AABB_SIZE,MAX_AABB_SIZE);\ + GREAL _cz = CLAMP(vz,-MAX_AABB_SIZE,MAX_AABB_SIZE);\ + GUINT32 _z = ((GUINT32)(_cz*ERROR_AABB))+32768;\ + uint_key = ((GUINT32)(_cx*ERROR_AABB))+32768;\ + uint_key = (uint_key<<16) + _z;\ +}\ + +//! Converts a vector coordinate to an integer for box sorting. Secure clamped, rounded +/*! +\param vx X component +\param vz Z component +\param uint_key a GUINT +*/ +#define GIM_CONVERT_VEC3F_GUINT_XZ_UPPER_CLAMPED(vx,vz,uint_key)\ +{\ + GREAL _cx = CLAMP(vx,-MAX_AABB_SIZE,MAX_AABB_SIZE);\ + GREAL _cz = CLAMP(vz,-MAX_AABB_SIZE,MAX_AABB_SIZE);\ + GUINT32 _z = ((GUINT32)ceilf(_cz*ERROR_AABB))+32768;\ + uint_key = ((GUINT32)ceilf(_cx*ERROR_AABB))+32768;\ + uint_key = (uint_key<<16) + _z;\ +}\ + +//! @} + +#endif // GIM_BOXPRUNING_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_contact.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_contact.h new file mode 100644 index 0000000..0c59461 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_contact.h @@ -0,0 +1,115 @@ +#ifndef GIM_CONTACT_H_INCLUDED +#define GIM_CONTACT_H_INCLUDED + +/*! \file gim_contact.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_geometry.h" +#include "GIMPACT/gim_radixsort.h" + +/*! \defgroup CONTACTS +\brief +Functions for managing and sorting contacts resulting from a collision query. +
      +
    • Contact lists must be created by calling \ref GIM_CREATE_CONTACT_LIST +
    • After querys, contact lists must be destroy by calling \ref GIM_DYNARRAY_DESTROY +
    • Contacts can be merged to avoid duplicate results by calling \ref gim_merge_contacts +
    + +*/ +//! @{ +/// Structure for collision results +struct GIM_CONTACT +{ + vec3f m_point; + vec3f m_normal; + GREAL m_depth;//Positive value indicates interpenetration + void * m_handle1; + void * m_handle2; + GUINT32 m_feature1;//Face number + GUINT32 m_feature2;//Face number +}; +//typedef struct _GIM_CONTACT GIM_CONTACT; + +#define CONTACT_DIFF_EPSILON 0.00001f + +#define GIM_CALC_KEY_CONTACT(pos,hash)\ +{\ + GINT32 _coords[] = {(GINT32)(pos[0]*1000.0f+1.0f),(GINT32)(pos[1]*1333.0f),(GINT32)(pos[2]*2133.0f+3.0f)};\ + GUINT32 _hash=0;\ + GUINT32 *_uitmp = (GUINT32 *)(&_coords[0]);\ + _hash = *_uitmp;\ + _uitmp++;\ + _hash += (*_uitmp)<<4;\ + _uitmp++;\ + _hash += (*_uitmp)<<8;\ + hash = _hash;\ +}\ + +///Creates a contact list for queries +#define GIM_CREATE_CONTACT_LIST(contact_array) GIM_DYNARRAY_CREATE(GIM_CONTACT,contact_array,100) + +#define GIM_PUSH_CONTACT(contact_array, point, normal, deep,handle1, handle2, feat1, feat2)\ +{\ + GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,contact_array);\ + GIM_CONTACT * _last = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,contact_array);\ + VEC_COPY(_last->m_point,point);\ + VEC_COPY(_last->m_normal,normal);\ + _last->m_depth = deep;\ + _last->m_handle1 = handle1;\ + _last->m_handle2 = handle2;\ + _last->m_feature1 = feat1;\ + _last->m_feature2 = feat2;\ +}\ + +///Receive pointer to contacts +#define GIM_COPY_CONTACTS(dest_contact, source_contact)\ +{\ + VEC_COPY(dest_contact->m_point,source_contact->m_point);\ + VEC_COPY(dest_contact->m_normal,source_contact->m_normal);\ + dest_contact->m_depth = source_contact->m_depth;\ + dest_contact->m_handle1 = source_contact->m_handle1;\ + dest_contact->m_handle2 = source_contact->m_handle2;\ + dest_contact->m_feature1 = source_contact->m_feature1;\ + dest_contact->m_feature2 = source_contact->m_feature2;\ +}\ + +//! Merges duplicate contacts with minimum depth criterion +void gim_merge_contacts(GDYNAMIC_ARRAY * source_contacts, + GDYNAMIC_ARRAY * dest_contacts); + + +//! Merges to an unique contact +void gim_merge_contacts_unique(GDYNAMIC_ARRAY * source_contacts, + GDYNAMIC_ARRAY * dest_contacts); + +//! @} +#endif // GIM_CONTACT_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_geometry.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_geometry.h new file mode 100644 index 0000000..c8931bf --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_geometry.h @@ -0,0 +1,1885 @@ +#ifndef GIM_VECTOR_H_INCLUDED +#define GIM_VECTOR_H_INCLUDED + +/*! \file gim_geometry.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_math.h" + +/*! \defgroup GEOMETRIC_TYPES +\brief +Basic types and constants for geometry +*/ +//! @{ + +//! Integer vector 2D +typedef GINT32 vec2i[2]; +//! Integer vector 3D +typedef GINT32 vec3i[3]; +//! Integer vector 4D +typedef GINT32 vec4i[4]; + +//! Float vector 2D +typedef GREAL vec2f[2]; +//! Float vector 3D +typedef GREAL vec3f[3]; +//! Float vector 4D +typedef GREAL vec4f[4]; + +//! Matrix 2D, row ordered +typedef GREAL mat2f[2][2]; +//! Matrix 3D, row ordered +typedef GREAL mat3f[3][3]; +//! Matrix 4D, row ordered +typedef GREAL mat4f[4][4]; + +//! Quaternion +typedef GREAL quatf[4]; + +//! Axis aligned box +struct aabb3f{ + aabb3f() {} + + template + aabb3f(const tboundfloat &_minX, const tboundfloat &_maxX, const tboundfloat &_minY, const tboundfloat &_maxY, const tboundfloat &_minZ, const tboundfloat &_maxZ): + minX((GREAL)_minX), + maxX((GREAL)_maxX), + minY((GREAL)_minY), + maxY((GREAL)_maxY), + minZ((GREAL)_minZ), + maxZ((GREAL)_maxZ) + { + } + + GREAL minX; + GREAL maxX; + GREAL minY; + GREAL maxY; + GREAL minZ; + GREAL maxZ; +}; +//typedef struct _aabb3f aabb3f; +//! @} + + +/*! \defgroup VECTOR_OPERATIONS +Operations for vectors : vec2f,vec3f and vec4f +*/ +//! @{ + +//! Zero out a 2D vector +#define VEC_ZERO_2(a) \ +{ \ + (a)[0] = (a)[1] = 0.0f; \ +}\ + + +//! Zero out a 3D vector +#define VEC_ZERO(a) \ +{ \ + (a)[0] = (a)[1] = (a)[2] = 0.0f; \ +}\ + + +/// Zero out a 4D vector +#define VEC_ZERO_4(a) \ +{ \ + (a)[0] = (a)[1] = (a)[2] = (a)[3] = 0.0f; \ +}\ + + +/// Vector copy +#define VEC_COPY_2(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ +}\ + + +/// Copy 3D vector +#define VEC_COPY(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ + (b)[2] = (a)[2]; \ +}\ + + +/// Copy 4D vector +#define VEC_COPY_4(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ + (b)[2] = (a)[2]; \ + (b)[3] = (a)[3]; \ +}\ + + +/// Vector difference +#define VEC_DIFF_2(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ +}\ + + +/// Vector difference +#define VEC_DIFF(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ + (v21)[2] = (v2)[2] - (v1)[2]; \ +}\ + + +/// Vector difference +#define VEC_DIFF_4(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ + (v21)[2] = (v2)[2] - (v1)[2]; \ + (v21)[3] = (v2)[3] - (v1)[3]; \ +}\ + + +/// Vector sum +#define VEC_SUM_2(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ +}\ + + +/// Vector sum +#define VEC_SUM(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ + (v21)[2] = (v2)[2] + (v1)[2]; \ +}\ + + +/// Vector sum +#define VEC_SUM_4(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ + (v21)[2] = (v2)[2] + (v1)[2]; \ + (v21)[3] = (v2)[3] + (v1)[3]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE_2(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ + (c)[2] = (a)*(b)[2]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE_4(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ + (c)[2] = (a)*(b)[2]; \ + (c)[3] = (a)*(b)[3]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM_2(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ + (c)[2] += (a)*(b)[2]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM_4(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ + (c)[2] += (a)*(b)[2]; \ + (c)[3] += (a)*(b)[3]; \ +}\ + + +/// Vector dot product +#define VEC_DOT_2(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1]) + + +/// Vector dot product +#define VEC_DOT(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2]) + +/// Vector dot product +#define VEC_DOT_4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3]) + +/// vector impact parameter (squared) +#define VEC_IMPACT_SQ(bsq,direction,position) {\ + GREAL _llel_ = VEC_DOT(direction, position);\ + bsq = VEC_DOT(position, position) - _llel_*_llel_;\ +}\ + + +/// vector impact parameter +#define VEC_IMPACT(bsq,direction,position) {\ + VEC_IMPACT_SQ(bsq,direction,position); \ + GIM_SQRT(bsq,bsq); \ +}\ + +/// Vector length +#define VEC_LENGTH_2(a,l)\ +{\ + GREAL _pp = VEC_DOT_2(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_LENGTH(a,l)\ +{\ + GREAL _pp = VEC_DOT(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_LENGTH_4(a,l)\ +{\ + GREAL _pp = VEC_DOT_4(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + +/// Vector inv length +#define VEC_INV_LENGTH_2(a,l)\ +{\ + GREAL _pp = VEC_DOT_2(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + +/// Vector inv length +#define VEC_INV_LENGTH(a,l)\ +{\ + GREAL _pp = VEC_DOT(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + +/// Vector inv length +#define VEC_INV_LENGTH_4(a,l)\ +{\ + GREAL _pp = VEC_DOT_4(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + + +/// distance between two points +#define VEC_DISTANCE(_len,_va,_vb) {\ + vec3f _tmp_; \ + VEC_DIFF(_tmp_, _vb, _va); \ + VEC_LENGTH(_tmp_,_len); \ +}\ + + +/// Vector length +#define VEC_CONJUGATE_LENGTH(a,l)\ +{\ + GREAL _pp = 1.0 - a[0]*a[0] - a[1]*a[1] - a[2]*a[2];\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_NORMALIZE(a) { \ + GREAL len;\ + VEC_INV_LENGTH(a,len); \ + if(len +Last column is added as the position +*/ +#define MAT_DOT_VEC_3X4(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2] + m[0][3]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2] + m[1][3]; \ + p[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2] + m[2][3]; \ +}\ + +/*! vector transpose times matrix */ +/*! p[j] = v[0]*m[0][j] + v[1]*m[1][j] + v[2]*m[2][j]; */ +#define VEC_DOT_MAT_3X3(p,v,m) \ +{ \ + p[0] = v[0]*m[0][0] + v[1]*m[1][0] + v[2]*m[2][0]; \ + p[1] = v[0]*m[0][1] + v[1]*m[1][1] + v[2]*m[2][1]; \ + p[2] = v[0]*m[0][2] + v[1]*m[1][2] + v[2]*m[2][2]; \ +}\ + + +/*! affine matrix times vector */ +/** The matrix is assumed to be an affine matrix, with last two + * entries representing a translation */ +#define MAT_DOT_VEC_2X3(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]; \ +}\ + + +/** inverse transpose of matrix times vector + * + * This macro computes inverse transpose of matrix m, + * and multiplies vector v into it, to yield vector p + * + * DANGER !!! Do not use this on normal vectors!!! + * It will leave normals the wrong length !!! + * See macro below for use on normals. + */ +#define INV_TRANSP_MAT_DOT_VEC_2X2(p,m,v) \ +{ \ + GREAL det; \ + \ + det = m[0][0]*m[1][1] - m[0][1]*m[1][0]; \ + p[0] = m[1][1]*v[0] - m[1][0]*v[1]; \ + p[1] = - m[0][1]*v[0] + m[0][0]*v[1]; \ + \ + /* if matrix is not singular, and not orthonormal, then renormalize */ \ + if ((det!=1.0f) && (det != 0.0f)) { \ + det = 1.0f / det; \ + p[0] *= det; \ + p[1] *= det; \ + } \ +}\ + + +/** transform normal vector by inverse transpose of matrix + * and then renormalize the vector + * + * This macro computes inverse transpose of matrix m, + * and multiplies vector v into it, to yield vector p + * Vector p is then normalized. + */ +#define NORM_XFORM_2X2(p,m,v) \ +{ \ + double len; \ + \ + /* do nothing if off-diagonals are zero and diagonals are \ + * equal */ \ + if ((m[0][1] != 0.0) || (m[1][0] != 0.0) || (m[0][0] != m[1][1])) { \ + p[0] = m[1][1]*v[0] - m[1][0]*v[1]; \ + p[1] = - m[0][1]*v[0] + m[0][0]*v[1]; \ + \ + len = p[0]*p[0] + p[1]*p[1]; \ + GIM_INV_SQRT(len,len); \ + p[0] *= len; \ + p[1] *= len; \ + } else { \ + VEC_COPY_2 (p, v); \ + } \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define OUTER_PRODUCT_2X2(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define OUTER_PRODUCT_3X3(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + m[0][2] = v[0] * t[2]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ + m[1][2] = v[1] * t[2]; \ + \ + m[2][0] = v[2] * t[0]; \ + m[2][1] = v[2] * t[1]; \ + m[2][2] = v[2] * t[2]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define OUTER_PRODUCT_4X4(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + m[0][2] = v[0] * t[2]; \ + m[0][3] = v[0] * t[3]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ + m[1][2] = v[1] * t[2]; \ + m[1][3] = v[1] * t[3]; \ + \ + m[2][0] = v[2] * t[0]; \ + m[2][1] = v[2] * t[1]; \ + m[2][2] = v[2] * t[2]; \ + m[2][3] = v[2] * t[3]; \ + \ + m[3][0] = v[3] * t[0]; \ + m[3][1] = v[3] * t[1]; \ + m[3][2] = v[3] * t[2]; \ + m[3][3] = v[3] * t[3]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_2X2(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_3X3(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + m[0][2] += v[0] * t[2]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ + m[1][2] += v[1] * t[2]; \ + \ + m[2][0] += v[2] * t[0]; \ + m[2][1] += v[2] * t[1]; \ + m[2][2] += v[2] * t[2]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yields + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_4X4(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + m[0][2] += v[0] * t[2]; \ + m[0][3] += v[0] * t[3]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ + m[1][2] += v[1] * t[2]; \ + m[1][3] += v[1] * t[3]; \ + \ + m[2][0] += v[2] * t[0]; \ + m[2][1] += v[2] * t[1]; \ + m[2][2] += v[2] * t[2]; \ + m[2][3] += v[2] * t[3]; \ + \ + m[3][0] += v[3] * t[0]; \ + m[3][1] += v[3] * t[1]; \ + m[3][2] += v[3] * t[2]; \ + m[3][3] += v[3] * t[3]; \ +}\ + + +/** determinant of matrix + * + * Computes determinant of matrix m, returning d + */ +#define DETERMINANT_2X2(d,m) \ +{ \ + d = m[0][0] * m[1][1] - m[0][1] * m[1][0]; \ +}\ + + +/** determinant of matrix + * + * Computes determinant of matrix m, returning d + */ +#define DETERMINANT_3X3(d,m) \ +{ \ + d = m[0][0] * (m[1][1]*m[2][2] - m[1][2] * m[2][1]); \ + d -= m[0][1] * (m[1][0]*m[2][2] - m[1][2] * m[2][0]); \ + d += m[0][2] * (m[1][0]*m[2][1] - m[1][1] * m[2][0]); \ +}\ + + +/** i,j,th cofactor of a 4x4 matrix + * + */ +#define COFACTOR_4X4_IJ(fac,m,i,j) \ +{ \ + int __ii[4], __jj[4], __k; \ + \ + for (__k=0; __k (aabb2).maxX ||\ + (aabb1).maxX < (aabb2).minX ||\ + (aabb1).minY > (aabb2).maxY ||\ + (aabb1).maxY < (aabb2).minY ||\ + (aabb1).minZ > (aabb2).maxZ ||\ + (aabb1).maxZ < (aabb2).minZ )\ + {\ + intersected = 0;\ + }\ +}\ + +#define AXIS_INTERSECT(min,max, a, d,tfirst, tlast,is_intersected) {\ + if(IS_ZERO(d))\ + {\ + is_intersected = !(a < min || a > max);\ + }\ + else\ + {\ + GREAL a0, a1;\ + a0 = (min - a) / (d);\ + a1 = (max - a) / (d);\ + if(a0 > a1) SWAP_NUMBERS(a0, a1);\ + tfirst = MAX(a0, tfirst);\ + tlast = MIN(a1, tlast);\ + if (tlast < tfirst)\ + {\ + is_intersected = 0;\ + }\ + else\ + {\ + is_intersected = 1;\ + }\ + }\ +}\ + +/*! \brief Finds the Ray intersection parameter. + +\param aabb Aligned box +\param vorigin A vec3f with the origin of the ray +\param vdir A vec3f with the direction of the ray +\param tparam Output parameter +\param tmax Max lenght of the ray +\param is_intersected 1 if the ray collides the box, else false + +*/ +#define BOX_INTERSECTS_RAY(aabb, vorigin, vdir, tparam, tmax,is_intersected) { \ + GREAL _tfirst = 0.0f, _tlast = tmax;\ + AXIS_INTERSECT(aabb.minX,aabb.maxX,vorigin[0], vdir[0], _tfirst, _tlast,is_intersected);\ + if(is_intersected)\ + {\ + AXIS_INTERSECT(aabb.minY,aabb.maxY,vorigin[1], vdir[1], _tfirst, _tlast,is_intersected);\ + }\ + if(is_intersected)\ + {\ + AXIS_INTERSECT(aabb.minZ,aabb.maxZ,vorigin[2], vdir[2], _tfirst, _tlast,is_intersected);\ + }\ + tparam = _tfirst;\ +}\ + +#define AABB_PROJECTION_INTERVAL(aabb,direction, vmin, vmax)\ +{\ + GREAL _center[] = {(aabb.minX + aabb.maxX)*0.5f, (aabb.minY + aabb.maxY)*0.5f, (aabb.minZ + aabb.maxZ)*0.5f};\ + \ + GREAL _extend[] = {aabb.maxX-_center[0],aabb.maxY-_center[1],aabb.maxZ-_center[2]};\ + GREAL _fOrigin = VEC_DOT(direction,_center);\ + GREAL _fMaximumExtent = _extend[0]*fabsf(direction[0]) + \ + _extend[1]*fabsf(direction[1]) + \ + _extend[2]*fabsf(direction[2]); \ +\ + vmin = _fOrigin - _fMaximumExtent; \ + vmax = _fOrigin + _fMaximumExtent; \ +}\ + +/*! +classify values: +
      +
    1. 0 : In back of plane +
    2. 1 : Spanning +
    3. 2 : In front of +
    +*/ +#define PLANE_CLASSIFY_BOX(plane,aabb,classify)\ +{\ + GREAL _fmin,_fmax; \ + AABB_PROJECTION_INTERVAL(aabb,plane, _fmin, _fmax); \ + if(plane[3] >= _fmax) \ + { \ + classify = 0;/*In back of*/ \ + } \ + else \ + { \ + if(plane[3]+0.000001f>=_fmin) \ + { \ + classify = 1;/*Spanning*/ \ + } \ + else \ + { \ + classify = 2;/*In front of*/ \ + } \ + } \ +}\ +//! @} + +/*! \defgroup GEOMETRIC_OPERATIONS +*/ +//! @{ + + +#define PLANEDIREPSILON 0.0000001f +#define PARALELENORMALS 0.000001f + +#define TRIANGLE_NORMAL(v1,v2,v3,n){\ + vec3f _dif1,_dif2; \ + VEC_DIFF(_dif1,v2,v1); \ + VEC_DIFF(_dif2,v3,v1); \ + VEC_CROSS(n,_dif1,_dif2); \ + VEC_NORMALIZE(n); \ +}\ + +/// plane is a vec4f +#define TRIANGLE_PLANE(v1,v2,v3,plane) {\ + TRIANGLE_NORMAL(v1,v2,v3,plane);\ + plane[3] = VEC_DOT(v1,plane);\ +}\ + +/// Calc a plane from an edge an a normal. plane is a vec4f +#define EDGE_PLANE(e1,e2,n,plane) {\ + vec3f _dif; \ + VEC_DIFF(_dif,e2,e1); \ + VEC_CROSS(plane,_dif,n); \ + VEC_NORMALIZE(plane); \ + plane[3] = VEC_DOT(e1,plane);\ +}\ + +#define DISTANCE_PLANE_POINT(plane,point) (VEC_DOT(plane,point) - plane[3]) + +#define PROJECT_POINT_PLANE(point,plane,projected) {\ + GREAL _dis;\ + _dis = DISTANCE_PLANE_POINT(plane,point);\ + VEC_SCALE(projected,-_dis,plane);\ + VEC_SUM(projected,projected,point); \ +}\ + +#define POINT_IN_HULL(point,planes,plane_count,outside)\ +{\ + GREAL _dis;\ + outside = 0;\ + GUINT32 _i = 0;\ + do\ + {\ + _dis = DISTANCE_PLANE_POINT(planes[_i],point);\ + if(_dis>0.0f) outside = 1;\ + _i++;\ + }while(_i +
  1. 0 : Segment in front of plane, s1 closest +
  2. 1 : Segment in front of plane, s2 closest +
  3. 2 : Segment in back of plane, s1 closest +
  4. 3 : Segment in back of plane, s2 closest +
  5. 4 : Segment collides plane, s1 in back +
  6. 5 : Segment collides plane, s2 in back + +*/ +#define PLANE_CLIP_SEGMENT2(s1,s2,plane,clipped,intersection_type) \ +{\ + GREAL _dis1,_dis2;\ + _dis1 = DISTANCE_PLANE_POINT(plane,s1);\ + _dis2 = DISTANCE_PLANE_POINT(plane,s2);\ + if(_dis1 >-G_EPSILON && _dis2 >-G_EPSILON)\ + {\ + if(_dis1<_dis2) intersection_type = 0;\ + else intersection_type = 1;\ + }\ + else if(_dis1 _dis2) intersection_type = 2;\ + else intersection_type = 3; \ + }\ + else\ + {\ + if(_dis1<_dis2) intersection_type = 4;\ + else intersection_type = 5;\ + VEC_DIFF(clipped,s2,s1);\ + _dis2 = VEC_DOT(clipped,plane);\ + VEC_SCALE(clipped,-_dis1/_dis2,clipped);\ + VEC_SUM(clipped,clipped,s1); \ + }\ +}\ + +//! Confirms if the plane intersect the edge or not +/*! +clipped1 and clipped2 are the vertices behind the plane. +clipped1 is the closest + +intersection_type must have the following values +
      +
    • 0 : Segment in front of plane, s1 closest +
    • 1 : Segment in front of plane, s2 closest +
    • 2 : Segment in back of plane, s1 closest +
    • 3 : Segment in back of plane, s2 closest +
    • 4 : Segment collides plane, s1 in back +
    • 5 : Segment collides plane, s2 in back +
    +*/ +#define PLANE_CLIP_SEGMENT_CLOSEST(s1,s2,plane,clipped1,clipped2,intersection_type)\ +{\ + PLANE_CLIP_SEGMENT2(s1,s2,plane,clipped1,intersection_type);\ + if(intersection_type == 0)\ + {\ + VEC_COPY(clipped1,s1);\ + VEC_COPY(clipped2,s2);\ + }\ + else if(intersection_type == 1)\ + {\ + VEC_COPY(clipped1,s2);\ + VEC_COPY(clipped2,s1);\ + }\ + else if(intersection_type == 2)\ + {\ + VEC_COPY(clipped1,s1);\ + VEC_COPY(clipped2,s2);\ + }\ + else if(intersection_type == 3)\ + {\ + VEC_COPY(clipped1,s2);\ + VEC_COPY(clipped2,s1);\ + }\ + else if(intersection_type == 4)\ + { \ + VEC_COPY(clipped2,s1);\ + }\ + else if(intersection_type == 5)\ + { \ + VEC_COPY(clipped2,s2);\ + }\ +}\ + + +//! Finds the 2 smallest cartesian coordinates of a plane normal +#define PLANE_MINOR_AXES(plane, i0, i1)\ +{\ + GREAL A[] = {fabs(plane[0]),fabs(plane[1]),fabs(plane[2])};\ + if(A[0]>A[1])\ + {\ + if(A[0]>A[2])\ + {\ + i0=1; /* A[0] is greatest */ \ + i1=2;\ + }\ + else \ + {\ + i0=0; /* A[2] is greatest */ \ + i1=1; \ + }\ + }\ + else /* A[0]<=A[1] */ \ + {\ + if(A[2]>A[1]) \ + { \ + i0=0; /* A[2] is greatest */ \ + i1=1; \ + }\ + else \ + {\ + i0=0; /* A[1] is greatest */ \ + i1=2; \ + }\ + } \ +}\ + +//! Ray plane collision +#define RAY_PLANE_COLLISION(plane,vDir,vPoint,pout,tparam,does_intersect)\ +{\ + GREAL _dis,_dotdir; \ + _dotdir = VEC_DOT(plane,vDir);\ + if(_dotdir1.0f)\ + {\ + VEC_COPY(cp,e2);\ + }\ + else \ + {\ + VEC_SCALE(cp,_scalar,_n);\ + VEC_SUM(cp,cp,e1);\ + } \ +}\ + + +/*! \brief Finds the line params where these lines intersect. + +\param dir1 Direction of line 1 +\param point1 Point of line 1 +\param dir2 Direction of line 2 +\param point2 Point of line 2 +\param t1 Result Parameter for line 1 +\param t2 Result Parameter for line 2 +\param dointersect 0 if the lines won't intersect, else 1 + +*/ +#define LINE_INTERSECTION_PARAMS(dir1,point1, dir2, point2,t1,t2,dointersect) {\ + GREAL det;\ + GREAL e1e1 = VEC_DOT(dir1,dir1);\ + GREAL e1e2 = VEC_DOT(dir1,dir2);\ + GREAL e2e2 = VEC_DOT(dir2,dir2);\ + vec3f p1p2;\ + VEC_DIFF(p1p2,point1,point2);\ + GREAL p1p2e1 = VEC_DOT(p1p2,dir1);\ + GREAL p1p2e2 = VEC_DOT(p1p2,dir2);\ + det = e1e2*e1e2 - e1e1*e2e2;\ + if(IS_ZERO(det))\ + {\ + dointersect = 0;\ + }\ + else\ + {\ + t1 = (e1e2*p1p2e2 - e2e2*p1p2e1)/det;\ + t2 = (e1e1*p1p2e2 - e1e2*p1p2e1)/det;\ + dointersect = 1;\ + }\ +}\ + +//! Find closest points on segments +#define SEGMENT_COLLISION(vA1,vA2,vB1,vB2,vPointA,vPointB)\ +{\ + vec3f _AD,_BD,_N;\ + vec4f _M;\ + VEC_DIFF(_AD,vA2,vA1);\ + VEC_DIFF(_BD,vB2,vB1);\ + VEC_CROSS(_N,_AD,_BD);\ + VEC_CROSS(_M,_N,_BD);\ + _M[3] = VEC_DOT(_M,vB1);\ + float _tp; \ + LINE_PLANE_COLLISION(_M,_AD,vA1,vPointA,_tp,0.0f, 1.0f);\ + /*Closest point on segment*/ \ + VEC_DIFF(vPointB,vPointA,vB1);\ + _tp = VEC_DOT(vPointB, _BD); \ + _tp/= VEC_DOT(_BD, _BD); \ + _tp = CLAMP(_tp,0.0f,1.0f); \ + VEC_SCALE(vPointB,_tp,_BD);\ + VEC_SUM(vPointB,vPointB,vB1);\ +}\ + +//! @} + +///Additional Headers for Collision +#include "GIMPACT/gim_tri_collision.h" +#include "GIMPACT/gim_tri_sphere_collision.h" +#include "GIMPACT/gim_tri_capsule_collision.h" + +#endif // GIM_VECTOR_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_math.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_math.h new file mode 100644 index 0000000..e0c97c9 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_math.h @@ -0,0 +1,164 @@ +#ifndef GIM_MATH_H_INCLUDED +#define GIM_MATH_H_INCLUDED + +/*! \file gim_math.h +\author Francisco Len +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "config.h" + +#include +#include +#if HAVE_SYS_TYPES_H +#include +#elif defined(_MSC_VER) +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +#elif defined(__GNUC__) +#include +#else +#error "GIMPACT: Must define int32_t and uint32_t" +#endif + + +/*! \defgroup BASIC_TYPES +Basic types and constants +Conventions: +Types starting with G +Constants starting with G_ +*/ +//! @{ +/*! Types */ +#define GREAL float +#define GINT32 int32_t +#define GUINT32 uint32_t + +#ifdef GPTR +#undef GPTR +#endif +#define GPTR void* + +/*! Constants for integers*/ +#define GUINT32_BIT_COUNT 32 +#define GUINT32_EXPONENT 5 + +#define G_FASTMATH 1 +#define G_PI 3.14159265358979f +#define G_HALF_PI 1.5707963f +//267948966 +#define G_TWO_PI 6.28318530f +//71795864 +#define G_ROOT3 1.73205f +#define G_ROOT2 1.41421f +#define G_UINT_INFINITY 65534 +#define G_REAL_INFINITY FLT_MAX +#define G_SIGN_BITMASK 0x80000000 +#define G_USE_EPSILON_TEST +#define G_EPSILON 0.0000001f +//! @} + +/*! \defgroup MATH_FUNCTIONS +mathematical functions +*/ +//! @{ +#define G_DEGTORAD(X) ((X)*3.1415926f/180.0f) +#define G_RADTODEG(X) ((X)*180.0f/3.1415926f) + +//! Integer representation of a floating-point value. +#define IR(x) ((GUINT32&)(x)) + +//! Signed integer representation of a floating-point value. +#define SIR(x) ((GINT32&)(x)) + +//! Absolute integer representation of a floating-point value +#define AIR(x) (IR(x)&0x7fffffff) + +//! Floating-point representation of an integer value. +#define FR(x) ((GREAL&)(x)) + +#define MAX(a,b) ((a)<(b)?(b):(a)) +#define MIN(a,b) ((a)>(b)?(b):(a)) + +#define MAX3(a,b,c) MAX(a,MAX(b,c)) +#define MIN3(a,b,c) MIN(a,MIN(b,c)) + +#define IS_ZERO(value) ((value) < G_EPSILON && (value) > -G_EPSILON) + +#define IS_NEGATIVE(value) ((value) <= -G_EPSILON) + +#define IS_POSISITVE(value) ((value) >= G_EPSILON) + +///returns a clamped number +#define CLAMP(number,minval,maxval) ((number)<(minval)?(minval):((number)>(maxval)?(maxval):(number))) + +///Swap numbers +#define SWAP_NUMBERS(a,b){ \ + (a) = (a)+(b); \ + (b) = (a)-(b); \ + (a) = (a)-(b); \ +}\ + +#define GIM_INV_SQRT(va,isva)\ +{\ + if((va)<=0.0000001f)\ + {\ + (isva) = G_REAL_INFINITY;\ + }\ + else\ + {\ + GREAL _x = (va) * 0.5f;\ + GUINT32 _y = 0x5f3759df - ( IR(va) >> 1);\ + (isva) = FR(_y);\ + (isva) = (isva) * ( 1.5f - ( _x * (isva) * (isva) ) );\ + }\ +}\ + +#define GIM_SQRT(va,sva)\ +{\ + GIM_INV_SQRT(va,sva);\ + (sva) = 1.0f/(sva);\ +}\ + +//! Computes 1.0f / sqrtf(x). Comes from Quake3. See http://www.magic-software.com/3DGEDInvSqrt.html +GREAL gim_inv_sqrt(GREAL f); + +//! Computes sqrtf(x) faster. +/*! +\sa gim_inv_sqrt +*/ +GREAL gim_sqrt(GREAL f); + +//!Initializes mathematical functions +void gim_init_math(); + +//! Generates an unit random +GREAL gim_unit_random(); +//! @} + +#endif // GIM_MATH_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_memory.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_memory.h new file mode 100644 index 0000000..1da14f1 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_memory.h @@ -0,0 +1,1056 @@ +#ifndef GIM_MEMORY_H_INCLUDED +#define GIM_MEMORY_H_INCLUDED +/*! \file gim_memory.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_math.h" +#include + +//#define PREFETCH 1 +//! \defgroup PREFETCH +//! @{ +#ifdef PREFETCH +#include // for prefetch +#define pfval 64 +#define pfval2 128 +//! Prefetch 64 +#define pf(_x,_i) _mm_prefetch((void *)(_x + _i + pfval), 0) +//! Prefetch 128 +#define pf2(_x,_i) _mm_prefetch((void *)(_x + _i + pfval2), 0) +#else +//! Prefetch 64 +#define pf(_x,_i) +//! Prefetch 128 +#define pf2(_x,_i) +#endif +//! @} + +/*! \defgroup ARRAY_UTILITIES +\brief +Functions for manip packed arrays of numbers +*/ +//! @{ +#define GIM_COPY_ARRAYS(dest_array, source_array, element_count)\ +{\ + GUINT32 _i_;\ + for (_i_ = 0; _i_ < (element_count); _i_++)\ + {\ + (dest_array)[_i_] = (source_array)[_i_];\ + }\ +}\ + +#define GIM_COPY_ARRAYS_1(dest_array, source_array, element_count, copy_macro)\ +{\ + GUINT32 _i_;\ + for (_i_=0; _i_ < (element_count); _i_++)\ + {\ + copy_macro((dest_array)[_i_], (source_array)[_i_]);\ + }\ +}\ + + +#define GIM_ZERO_ARRAY(array, element_count)\ +{\ + GUINT32 _i_;\ + for (_i_=0; _i_ < (element_count); _i_++)\ + {\ + (array)[_i_] = 0;\ + }\ +}\ + +#define GIM_CONSTANT_ARRAY(array, element_count, constant)\ +{\ + GUINT32 _i_;\ + for (_i_ = 0; _i_ < (element_count); _i_++)\ + {\ + (array)[_i_] = (constant);\ + }\ +}\ +//! @} + +/*! \defgroup MEMORY_FUNCTION_PROTOTYPES +Function prototypes to allocate and free memory. +*/ +//! @{ +typedef void * gim_alloc_function (size_t size); +typedef void * gim_alloca_function (size_t size);//Allocs on the heap +typedef void * gim_realloc_function (void *ptr, size_t oldsize, size_t newsize); +typedef void gim_free_function (void *ptr, size_t size); +//! @} + +/*! \defgroup MEMORY_FUNCTION_HANDLERS +\brief +Memory Function Handlers + set new memory management functions. if fn is 0, the default handlers are + used. */ +//! @{ +void gim_set_alloc_handler (gim_alloc_function *fn); +// void gim_set_alloca_handler (gim_alloca_function *fn); -- a nonsense +void gim_set_realloc_handler (gim_realloc_function *fn); +void gim_set_free_handler (gim_free_function *fn); +//! @} + +/*! \defgroup MEMORY_FUNCTION_GET_HANDLERS +\brief +get current memory management functions. +*/ +//! @{ +gim_alloc_function *gim_get_alloc_handler (void); +// gim_alloca_function *gim_get_alloca_handler(void); -- a nonsense +gim_realloc_function *gim_get_realloc_handler (void); +gim_free_function *gim_get_free_handler (void); +//! @} + +/*! \defgroup MEMORY_FUNCTIONS +Standard Memory functions +*/ +//! @{ +void * gim_alloc(size_t size); +// void * gim_alloca(size_t size); -- a nonsense +void * gim_realloc(void *ptr, size_t oldsize, size_t newsize); +void gim_free(void *ptr, size_t size); +//! @} + +/*! \defgroup DYNAMIC_ARRAYS +\brief +Dynamic Arrays. Allocated from system memory. +
      +
    • For initializes a dynamic array, use GIM_DYNARRAY_CREATE or GIM_DYNARRAY_CREATE_SIZED. +
    • When an array is no longer used, must be terminated with the macro GIM_DYNARRAY_DESTROY. +
    +*/ +//! @{ +#define G_ARRAY_GROW_SIZE 64 +#define G_ARRAY_BUFFERMANAGER_INIT_SIZE 2 + +//! Dynamic array handle. +struct GDYNAMIC_ARRAY +{ + char * m_pdata; + GUINT32 m_size; + GUINT32 m_reserve_size; +}; +//typedef struct _GDYNAMIC_ARRAY GDYNAMIC_ARRAY; + +//! Creates a dynamic array zero sized +#define GIM_DYNARRAY_CREATE(type, array_data, reserve_size) \ +{ \ + (array_data).m_pdata = (char *)gim_alloc((reserve_size) * sizeof(type)); \ + (array_data).m_size = 0; \ + (array_data).m_reserve_size = (reserve_size); \ +} \ + +//! Creates a dynamic array with n = size elements +#define GIM_DYNARRAY_CREATE_SIZED(type, array_data, size) \ +{ \ + (array_data).m_pdata = (char *)gim_alloc((size) * sizeof(type)); \ + (array_data).m_size = (size); \ + (array_data).m_reserve_size = (size); \ +} \ + +//! Reserves memory for a dynamic array. +#define GIM_DYNARRAY_RESERVE_SIZE(type, array_data, old_size, reserve_size) \ +{ \ + if ((reserve_size) > (array_data).m_reserve_size) \ + { \ + (array_data).m_pdata = (char *) gim_realloc((array_data).m_pdata, (old_size) * sizeof(type), (reserve_size) * sizeof(type)); \ + (array_data).m_reserve_size = (reserve_size); \ + } \ +} \ + +//! Set the size of the array +#define GIM_DYNARRAY_SET_SIZE(type, array_data, size) \ +{ \ + GIM_DYNARRAY_RESERVE_SIZE(type, array_data, (array_data).m_size, size); \ + (array_data).m_size = size; \ +} \ + +//! Gets a pointer from the beginning of the array +#define GIM_DYNARRAY_POINTER(type, array_data) ((type *)((array_data).m_pdata)) + +//! Gets a pointer from the last elemento of the array +#define GIM_DYNARRAY_POINTER_LAST(type, array_data) (((type *)(array_data).m_pdata) + ((array_data).m_size - 1)) + +//! Inserts an element at the last position +#define GIM_DYNARRAY_PUSH_ITEM(type, array_data, item)\ +{ \ + if ((array_data).m_reserve_size <= (array_data).m_size)\ + {\ + GIM_DYNARRAY_RESERVE_SIZE(type, array_data, (array_data).m_size, (array_data).m_size + G_ARRAY_GROW_SIZE); \ + }\ + type * _pt = GIM_DYNARRAY_POINTER(type, array_data); \ + memcpy(&_pt[(array_data).m_size], &(item), sizeof(type)); \ + (array_data).m_size++; \ +} \ + +//! Inserts an element at the last position +#define GIM_DYNARRAY_PUSH_EMPTY(type, array_data) \ +{ \ + if ((array_data).m_reserve_size <= (array_data).m_size) \ + { \ + GIM_DYNARRAY_RESERVE_SIZE(type, array_data, (array_data).m_size, (array_data).m_size + G_ARRAY_GROW_SIZE); \ + } \ + (array_data).m_size++; \ +} \ + +//! Inserts an element +#define GIM_DYNARRAY_INSERT_ITEM(type, array_data, item, index) \ +{ \ + if ((array_data).m_reserve_size <= (array_data).m_size) \ + { \ + GIM_DYNARRAY_RESERVE_SIZE(type, array_data, (array_data).m_size, (array_data).m_size + G_ARRAY_GROW_SIZE); \ + } \ + type * _pt = GIM_DYNARRAY_POINTER(type, array_data); \ + if ((index) < (array_data).m_size - 1) \ + { \ + memmove(&_pt[(index) + 1], &_pt[(index)], ((array_data).m_size - (index)) * sizeof(type)); \ + } \ + memcpy(&_pt[(index)], &(item), sizeof(type)); \ + array_data.m_size++; \ +} \ + +//! Removes an element +#define GIM_DYNARRAY_DELETE_ITEM(type, array_data, index) \ +{ \ + if ((index) < (array_data).m_size - 1) \ + { \ + type * _pt = GIM_DYNARRAY_POINTER(type, array_data);\ + memmove(&_pt[(index)], &_pt[(index) + 1], ((array_data).m_size - (index) - 1) * sizeof(type)); \ + } \ + (array_data).m_size--; \ +} \ + +//! Removes an element at the last position +#define GIM_DYNARRAY_POP_ITEM(array_data) \ +{ \ + if ((array_data).m_size > 0) \ + { \ + (array_data).m_size--; \ + } \ +}\ + +//! Destroys the array +void GIM_DYNARRAY_DESTROY(GDYNAMIC_ARRAY & array_data); +//! @} + +/*! \defgroup BITSET +\brief +Bitsets , based on \ref DYNAMIC_ARRAYS . +
      +
    • For initializes a bitset array, use \ref GIM_BITSET_CREATE or \ref GIM_BITSET_CREATE_SIZED. +
    • When the bitset is no longer used, must be terminated with the macro \ref GIM_DYNARRAY_DESTROY. +
    • For putting a mark on the bitset, call \ref GIM_BITSET_SET +
    • For clearing a mark on the bitset, call \ref GIM_BITSET_CLEAR +
    • For retrieving a bit value from a bitset, call \ref GIM_BITSET_GET- +
    +*/ +//! @{ + +//! Creates a bitset +#define GIM_BITSET_CREATE(array_data) GIM_DYNARRAY_CREATE(GUINT32, array_data, G_ARRAY_GROW_SIZE) + +//! Creates a bitset, with their bits set to 0. +#define GIM_BITSET_CREATE_SIZED(array_data, bits_count) \ +{ \ + GUINT32 array_size = (bits_count) / GUINT32_BIT_COUNT + 1; \ + GIM_DYNARRAY_CREATE(GUINT32, array_data, array_size); \ + GUINT32 * _pt = GIM_DYNARRAY_POINTER(GUINT32, array_data); \ + memset(_pt, 0, sizeof(GUINT32) * ((array_data).m_size)); \ +} \ + +//! Gets the bitset bit count. +#define GIM_BITSET_SIZE(array_data) ((array_data).m_size * GUINT32_BIT_COUNT) + +//! Resizes a bitset, with their bits set to 0. +#define GIM_BITSET_RESIZE(array_data, new_bits_count) \ +{ \ + GUINT32 _oldsize = (array_data).m_size; \ + (array_data).m_size = (new_bits_count) / GUINT32_BIT_COUNT + 1; \ + if (_oldsize < (array_data).m_size) \ + { \ + if ((array_data).m_size > (array_data).m_reserve_size) \ + { \ + GIM_DYNARRAY_RESERVE_SIZE(GUINT32, array_data, _oldsize, (array_data).m_size + G_ARRAY_GROW_SIZE); \ + } \ + GUINT32 * _pt = GIM_DYNARRAY_POINTER(GUINT32, array_data); \ + memset(&_pt[_oldsize], 0, sizeof(GUINT32) * ((array_data).m_size - _oldsize)); \ + } \ +} \ + +//! Sets all bitset bit to 0. +#define GIM_BITSET_CLEAR_ALL(array_data) \ +{ \ + memset((array_data).m_pdata, 0, sizeof(GUINT32) * (array_data).m_size); \ +} \ + +//! Sets all bitset bit to 1. +#define GIM_BITSET_SET_ALL(array_data) \ +{ \ + memset((array_data).m_pdata, 0xFF, sizeof(GUINT32) * (array_data).m_size); \ +} \ + +///Sets the desired bit to 1 +#define GIM_BITSET_SET(array_data, bit_index) \ +{ \ + if ((bit_index) >= GIM_BITSET_SIZE(array_data)) \ + { \ + GIM_BITSET_RESIZE(array_data, bit_index); \ + } \ + GUINT32 * _pt = GIM_DYNARRAY_POINTER(GUINT32, array_data); \ + _pt[(bit_index) >> GUINT32_EXPONENT] |= (1 << ((bit_index) & (GUINT32_BIT_COUNT - 1))); \ +} \ + +///Return 0 or 1 +#define GIM_BITSET_GET(array_data, bit_index, get_value) \ +{ \ + if ((bit_index) >= GIM_BITSET_SIZE(array_data)) \ + { \ + (get_value) = 0; \ + } \ + else \ + { \ + GUINT32 * _pt = GIM_DYNARRAY_POINTER(GUINT32, array_data); \ + (get_value) = _pt[(bit_index) >> GUINT32_EXPONENT] & (1 << ((bit_index) & (GUINT32_BIT_COUNT - 1))); \ + } \ +} \ + +///Sets the desired bit to 0 +#define GIM_BITSET_CLEAR(array_data, bit_index) \ +{ \ + if ((bit_index) < GIM_BITSET_SIZE(array_data)) \ + { \ + GUINT32 * _pt = GIM_DYNARRAY_POINTER(GUINT32, array_data); \ + _pt[(bit_index) >> GUINT32_EXPONENT] &= ~(1 << ((bit_index) & (GUINT32_BIT_COUNT - 1))); \ + } \ +} \ +//! @} + +/*! \defgroup MEMORY_ACCESS_CONSTANTS +\brief +Memory Access constants. +\sa BUFFERS +*/ +//! @{ +#define G_MA_READ_ONLY 1 +#define G_MA_WRITE_ONLY 2 +#define G_MA_READ_WRITE 3 +//! @} + +/*! \defgroup MEMORY_USAGE_CONSTANTS +\brief +Memory usage constants. +\sa BUFFERS +*/ +//! @{ +/// Don't care how memory is used +#define G_MU_EITHER 0 +/// specified once, doesn't allow read information +#define G_MU_STATIC_WRITE 1 +/// specified once, allows to read information from a shadow buffer +#define G_MU_STATIC_READ 2 +/// write directly on buffer, allows to read information from a shadow buffer +#define G_MU_STATIC_READ_DYNAMIC_WRITE 3 +/// upload data to buffer from the shadow buffer, allows to read information from a shadow buffer +#define G_MU_STATIC_READ_DYNAMIC_WRITE_COPY 4 +/// specified once, allows to read information directly from memory +#define G_MU_STATIC_WRITE_DYNAMIC_READ 5 +/// write directly on buffer, allows to read information directly from memory +#define G_MU_DYNAMIC_READ_WRITE 6 +//! @} + +/*! \defgroup BUFFER_ERRORS +\brief +Buffer operation errors +\sa BUFFERS +*/ +//! @{ +#define G_BUFFER_OP_SUCCESS 0 +#define G_BUFFER_OP_INVALID 1 +#define G_BUFFER_OP_STILLREFCOUNTED 2 +//! @} + +/*! \defgroup BUFFER_MANAGER_IDS +\brief +Buffer manager identifiers +\sa BUFFERS, BUFFER_MANAGERS +*/ +//! @{ +enum +{ + G_BUFFER_MANAGER_SYSTEM, + G_BUFFER_MANAGER_SHARED, + + G_BUFFER_MANAGER__MAX +}; +//! @} + +/*! \defgroup BUFFERS +\brief +Buffer operations and structs. +
      +
    • Before using buffers you must initializes GIMPACT buffer managers by calling \ref gimpact_init. +
    • For initializes a buffer, use \ref gim_create_buffer, \ref gim_create_buffer_from_data , \ref gim_create_common_buffer, \ref gim_create_common_buffer_from_data or \ref gim_create_shared_buffer_from_data. +
    • For accessing the buffer memory, you must call \ref gim_lock_buffer, and then \ref gim_unlock_buffer to finish the access. +
    • When a buffer is no longer needed, you must free it by calling \ref gim_buffer_free. +
    • You must call \ref gimpact_terminate when finish your application. +
    • For a safe manipulation of buffers, use \ref BUFFER_ARRAYS +
    +\sa BUFFER_MANAGERS, BUFFER_ARRAYS +*/ +//! @{ + +struct GBUFFER_MANAGER_DATA; + +//! Buffer handle. +struct GBUFFER_ID +{ + GBUFFER_MANAGER_DATA * m_bm_data; + GUINT32 m_buffer_id; +}; +//typedef struct _GBUFFER_ID GBUFFER_ID; + +//! Buffer internal data +struct GBUFFER_DATA +{ + GPTR m_buffer_handle;//!< if 0, buffer doesn't exists + GUINT32 m_size; + GUINT32 m_usage; + GINT32 m_access; + GUINT32 m_lock_count; + char * m_mapped_pointer; + GBUFFER_ID m_shadow_buffer; + GUINT32 m_refcount;//! Reference counting for safe garbage collection +}; +//typedef struct _GBUFFER_DATA GBUFFER_DATA; +//! @} + +/*! \defgroup BUFFERS_MANAGER_PROTOTYPES +\brief +Function prototypes to allocate and free memory for buffers +\sa BUFFER_MANAGERS, BUFFERS +*/ +//! @{ + +//! Returns a Buffer handle +typedef GPTR gim_buffer_alloc_function(GUINT32 size,int usage); + +//! Returns a Buffer handle, and copies the pdata to the buffer +typedef GPTR gim_buffer_alloc_data_function(const void * pdata,GUINT32 size,int usage); + +//! Changes the size of the buffer preserving the content, and returns the new buffer id +typedef GPTR gim_buffer_realloc_function(GPTR buffer_handle,GUINT32 oldsize,int old_usage,GUINT32 newsize,int new_usage); + +//! It changes the m_buffer_handle member to 0/0 +typedef void gim_buffer_free_function(GPTR buffer_handle,GUINT32 size); + +//! It maps the m_mapped_pointer. Returns a pointer +typedef char * gim_lock_buffer_function(GPTR buffer_handle,int access); + +//! It sets the m_mapped_pointer to 0 +typedef void gim_unlock_buffer_function(GPTR buffer_handle); + +typedef void gim_download_from_buffer_function( + GPTR source_buffer_handle, + GUINT32 source_pos, + void * destdata, + GUINT32 copysize); + +typedef void gim_upload_to_buffer_function( + GPTR dest_buffer_handle, + GUINT32 dest_pos, + void * sourcedata, + GUINT32 copysize); + +typedef void gim_copy_buffers_function( + GPTR source_buffer_handle, + GUINT32 source_pos, + GPTR dest_buffer_handle, + GUINT32 dest_pos, + GUINT32 copysize); +//! @} + + +/*! \defgroup BUFFER_MANAGERS +\brief +Buffer Manager operations +*/ +//! @{ +//! Buffer manager prototype +struct GBUFFER_MANAGER_PROTOTYPE +{ + gim_buffer_alloc_function * alloc_fn; + gim_buffer_alloc_data_function *alloc_data_fn; + gim_buffer_realloc_function * realloc_fn; + gim_buffer_free_function * free_fn; + gim_lock_buffer_function * lock_buffer_fn; + gim_unlock_buffer_function * unlock_buffer_fn; + gim_download_from_buffer_function * download_from_buffer_fn; + gim_upload_to_buffer_function * upload_to_buffer_fn; + gim_copy_buffers_function * copy_buffers_fn; +}; +//typedef struct _GBUFFER_MANAGER_PROTOTYPE GBUFFER_MANAGER_PROTOTYPE; + +//! Buffer manager +struct GBUFFER_MANAGER_DATA +{ + GDYNAMIC_ARRAY m_buffer_array;//!< Array of GBUFFER_DATA objects + GDYNAMIC_ARRAY m_free_positions;//!< Array of GUINT elements. Free positions + const GBUFFER_MANAGER_PROTOTYPE *m_prototype;//!< Prototype of functions + GUINT32 m_buffer_manager_id;//!< Buffer manager id +}; +//typedef struct _GBUFFER_MANAGER_DATA GBUFFER_MANAGER_DATA; + +//! Checks if buffer manager is used +int gim_is_buffer_manager_active(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id); +//! Adds a buffer Manager to the Memory Singleton +void gim_create_buffer_manager(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id); +//! Destroys a buffer manager +void gim_destroy_buffer_manager(GBUFFER_MANAGER_DATA buffer_managers[], GUINT32 buffer_manager_id); +void gim_get_buffer_manager_data(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id,GBUFFER_MANAGER_DATA ** pbm_data); +void gim_init_buffer_managers(GBUFFER_MANAGER_DATA buffer_managers[]); +void gim_terminate_buffer_managers(GBUFFER_MANAGER_DATA buffer_managers[]); + +//! @} + + +/*! \addtogroup BUFFERS +*/ +//! @{ + +//!Creates a buffer on the buffer manager specified by buffer_manager_id +/*! +\param buffer_manager_id +\param buffer_size +\param usage An usage constant. Use G_MU_DYNAMIC_READ_WRITE as default. +\param buffer_id a pointer to receive the new buffer id +\return An error code. 0 if success. +\post m_refcount = 0 +*/ +GUINT32 gim_create_buffer( + GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id, + GUINT32 buffer_size, + int usage, + GBUFFER_ID * buffer_id); + +//!Creates a buffer on the buffer manager specified by buffer_manager_id +/*! +\param buffer_manager_id +\param pdata Data for allocating +\param buffer_size Size of the data buffer +\param usage An usage constant. Use G_MU_DYNAMIC_READ_WRITE as default. +\param buffer_id a pointer to receive the new buffer id +\return An error code. 0 if success. +\post m_refcount = 0 +*/ +GUINT32 gim_create_buffer_from_data( + GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id, + const void * pdata, + GUINT32 buffer_size, + int usage, + GBUFFER_ID * buffer_id); + +//!Allocates on the G_BUFFER_MANAGER_SYSTEM +GUINT32 gim_create_common_buffer(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_size, GBUFFER_ID * buffer_id); +//!Allocates on the G_BUFFER_MANAGER_SYSTEM, and copies the data +GUINT32 gim_create_common_buffer_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + const void * pdata, GUINT32 buffer_size, GBUFFER_ID * buffer_id); +//!Creates a buffer with shared data +GUINT32 gim_create_shared_buffer_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + const void * pdata, GUINT32 buffer_size, GBUFFER_ID * buffer_id); + + +//! Add reference counting to buffer. +GINT32 gim_buffer_add_ref(GBUFFER_ID * buffer_id); + +//! Function for resize buffer, preserving the content +/*! +\param buffer_id +\param newsize +\return An error code. 0 if success. +\post If m_refcount>0 then it decrements it. +*/ +GINT32 gim_buffer_realloc(GBUFFER_ID * buffer_id,GUINT32 newsize); + +//! Eliminates the buffer. +/*! +If the buffer reference counting is <= 1 and is unlocked, then it eliminates the buffer. +*/ +GINT32 gim_buffer_free(GBUFFER_ID * buffer_id); + +//! Locks the buffer for memory access. +/*! +\param buffer_id Id from buffer. +\param access Must have the following values: G_MA_READ_ONLY,G_MA_WRITE_ONLY or G_MA_READ_WRITE. +\param map_pointer Dest Pointer of the memory address from buffer. +\post m_lock_count increases. +*/ +GINT32 gim_lock_buffer(GBUFFER_ID * buffer_id,int access,char ** map_pointer); + +//! Unlocks the buffer for memory access. +GINT32 gim_unlock_buffer(GBUFFER_ID * buffer_id); + +//! Gets the buffer size in bytes +GINT32 gim_get_buffer_size(GBUFFER_ID * buffer_id,GUINT32 * buffer_size); + +//! Determines if the buffer is locked +GINT32 gim_get_buffer_is_locked(GBUFFER_ID * buffer_id,GUINT32 * lock_count); + +//! Copies the content of the buffer to a dest pointer +GINT32 gim_download_from_buffer( + GBUFFER_ID * buffer_id, + GUINT32 source_pos, + void * destdata, + GUINT32 copysize); + +//! Copies the content of a memory pointer to the buffer +GINT32 gim_upload_to_buffer( + GBUFFER_ID * buffer_id, + GUINT32 dest_pos, + void * sourcedata, + GUINT32 copysize); + +//! Copies two buffers. +GINT32 gim_copy_buffers( + GBUFFER_ID * source_buffer_id, + GUINT32 source_pos, + GBUFFER_ID * dest_buffer_id, + GUINT32 dest_pos, + GUINT32 copysize); +//! @} + + +/*! \defgroup BUFFER_ARRAYS + +\brief +Buffered Arrays, for manip elements on a buffer and treat it as an array. +
      +
    • Before using buffer arrays you must initializes GIMPACT buffer managers by calling gimpact_init. +
    • Before creating buffer arrays, you must create a buffer. see \ref BUFFERS. +
    • Create a buffer narray by calling \ref GIM_BUFFER_ARRAY_INIT_TYPE, \ref GIM_BUFFER_ARRAY_INIT_TYPE_OFFSET or \ref GIM_BUFFER_ARRAY_INIT_OFFSET_STRIDE. +
    • For accessing to the array elements, you must call \ref gim_buffer_array_lock, and then \ref gim_buffer_array_unlock to finish the access. +
    • When a buffer array is no longer needed, you must free it by calling \ref GIM_BUFFER_ARRAY_DESTROY. +
    +The following example shows how Buffer arrays can be used: + +\code +int main() +{ + //init gimpact + gimpact_init(); + + //Buffer handle to use + GBUFFER_ID bufferhandle; + + //Create a memory buffer of 100 float numbers + gim_create_common_buffer(100*sizeof(float), &bufferhandle); + + //Create a buffer array from the bufferhandle + GBUFFER_ARRAY buffer_float_array; + GIM_BUFFER_ARRAY_INIT_TYPE(float,buffer_float_array,bufferhandle,100); + + ////Access to the buffer data, set all elements of the array + + int i, count; + count = buffer_float_array.m_element_count; + //Locks the array + gim_buffer_array_lock(&buffer_float_array,G_MA_READ_WRITE); + float * pelements = GIM_BUFFER_ARRAY_POINTER(float, buffer_float_array, 0); // A pointer to the buffer memory + + //fill the array with random numbers + for (i = 0;i < count;i++ ) + { + pelements[i] = gim_unit_random(); + } + //unlock buffer + gim_buffer_array_unlock(&buffer_float_array); + + //Program code + .... + .... + + //Destroy array + GIM_BUFFER_ARRAY_DESTROY(buffer_float_array); + + //terminate gimpact + gimpact_terminate(); +} +\endcode + +\sa BUFFERS +*/ +//! @{ + +//! Buffer managed array struct. +struct GBUFFER_ARRAY +{ + GBUFFER_ID m_buffer_id; + char * m_buffer_data; + char m_byte_stride; + GUINT32 m_byte_offset; + GUINT32 m_element_count; +}; +//typedef struct _GBUFFER_ARRAY GBUFFER_ARRAY; + +//! Sets offset for a buffered array. +#define GIM_BUFFER_ARRAY_SET_OFFSET(_array_data,_offset) (_array_data).m_byte_offset = (_offset)*(_array_data).m_byte_stride; + +//! Sets offset for a buffered array. +#define GIM_BUFFER_ARRAY_GET_OFFSET(_array_data,_offset) (_offset) = (_array_data).m_byte_offset/(_array_data).m_byte_stride; + +//!Return a pointer of the element at the _index +#define GIM_BUFFER_ARRAY_POINTER(_type,_array_data,_index) (_type *)((_array_data).m_buffer_data + (_index)*(_array_data).m_byte_stride) + +//! Sets stride for a buffered array. +#define GIM_BUFFER_ARRAY_SET_STRIDE(_type,_array_data) (_array_data).m_byte_stride = sizeof(_type); + +//! Is array stride equal to the size of the type ? +#define GIM_BUFFER_ARRAY_IS_ALIGNED(_type,_array_data) ((_array_data).m_byte_stride == sizeof(_type)) + +///Verify if two arrays have the same data +#define GIM_BUFFER_ARRAY_ARE_SAME(_array_data1,_array_data2,aresame) \ +{ \ + (aresame) = 1; \ + if ((_array_data1).m_buffer_id.m_buffer_id != (_array_data2).m_buffer_id.m_buffer_id || (_array_data1).m_buffer_id.m_buffer_manager_id != (_array_data2).m_buffer_id.m_buffer_manager_id || (_array_data1).m_byte_offset != (_array_data2).m_byte_offset) \ + { \ + (aresame) = 0; \ + } \ +} \ + +//! Reserve size for a buffered array. +/*! +\pre array_data must be unlocked, and must be the aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_RESERVE_SIZE(type, array_data, reserve_size) \ +{ \ + if ((reserve_size) > (array_data).m_element_count) \ + { \ + GUINT32 _buffer_size, _newarray_size; \ + gim_get_buffer_size(&(array_data).m_buffer_id, _buffer_size); \ + _newarray_size = (reserve_size) * (array_data).m_byte_stride; \ + if(_newarray_size > _buffer_size) \ + { \ + _newarray_size += G_ARRAY_GROW_SIZE * (array_data).m_byte_stride; \ + gim_buffer_realloc(&(array_data).m_buffer_id, _newarray_size); \ + } \ + } \ +} \ + +//! Pushes an element at last position +/*! +\pre array_data must be unlocked, and must be the aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_PUSH_ITEM(type, array_data, item) \ +{ \ + GIM_BUFFER_ARRAY_RESERVE_SIZE(type, array_data, (array_data).m_element_count + 1); \ + gim_buffer_array_lock(&(array_data), G_MA_WRITE_ONLY); \ + type * _pt = GIM_BUFFER_ARRAY_POINTER(type, array_data, (array_data).m_element_count); \ + memcpy(_pt, &(item), sizeof(type)); \ + gim_buffer_array_unlock(&(array_data)); \ + (array_data)->m_element_count++; \ +} \ + +//! Pushes a new element at last position +/*! +\pre array_data must be unlocked, and must be aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_PUSH_EMPTY(type, array_data) \ +{\ + GIM_BUFFER_ARRAY_RESERVE_SIZE(type, array_data, (array_data).m_element_count + 1); \ + (array_data)->m_element_count++; \ +}\ + +//! Inserts an element +/*! +\pre array_data must be unlocked, and must be aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_INSERT_ITEM(type, array_data, item, index) \ +{ \ + GIM_BUFFER_ARRAY_RESERVE_SIZE(type, array_data, (array_data).m_element_count + 1); \ + gim_buffer_array_lock(&(array_data), G_MA_WRITE_ONLY); \ + type * _pt = GIM_BUFFER_ARRAY_POINTER(type, array_data, 0); \ + if ((index) < (array_data)->m_element_count - 1) \ + { \ + memmove(&_pt[(index) + 1], &_pt[(index)], ((array_data).m_element_count - (index)) * sizeof(type)); \ + } \ + memcpy(&_pt[(index)], &(item), sizeof(type)); \ + gim_buffer_array_unlock(&(array_data)); \ + (array_data).m_element_count++; \ +} \ + +//! Deletes an element +/*! +\pre array_data must be unlocked, and must be aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_DELETE_ITEM(type, array_data, index) \ +{ \ + if ((index) < (array_data).m_element_count - 1) \ + { \ + gim_buffer_array_lock(&(array_data), G_MA_WRITE_ONLY); \ + type * _pt = GIM_BUFFER_ARRAY_POINTER(type, array_data, 0); \ + memmove(&_pt[(index)], &_pt[(index) + 1],((array_data).m_element_count - (index) - 1) * sizeof(type)); \ + gim_buffer_array_unlock(&(array_data)); \ + } \ + (array_data).m_element_count--; \ +} \ + +//! Deletes an element at last position +/*! +\pre array_data must be unlocked, and must be aligned (GIM_BUFFER_ARRAY_IS_ALIGNED ) +*/ +#define GIM_BUFFER_ARRAY_POP_ITEM(array_data) \ +{ \ + if ((array_data).m_element_count > 0) \ + { \ + (array_data).m_element_count--; \ + } \ +} \ + + +//! Initializes an GBUFFER_ARRAY object from a buffer ID +/*! +m_buffer_data will be 0, for access to the elements, you'd need to call lock_array +\param array_data Array structure to be filled +\param buffer_id A GBUFFER_ID structure which this array_daya will refer to +\param element_count Number of elements +\param offset element offset, it isn't byte offset. 0 is recomended +\param byte_stride size of each element. 0 is recomended. +\post Adds reference to the buffer +\sa gim_buffer_add_ref +*/ +#define GIM_BUFFER_ARRAY_INIT_OFFSET_STRIDE(array_data, buffer_id, element_count, offset, byte_stride) \ +{ \ + (array_data).m_buffer_id.m_buffer_id = (buffer_id).m_buffer_id; \ + (array_data).m_buffer_id.m_buffer_manager_id = (buffer_id).m_buffer_manager_id; \ + (array_data).m_buffer_data = 0; \ + (array_data).m_element_count = (element_count); \ + (array_data).m_byte_stride = (byte_stride); \ + GIM_BUFFER_ARRAY_SET_OFFSET(array_data, offset); \ + gim_buffer_add_ref(&(buffer_id)); \ +} \ + +//! Initializes an GBUFFER_ARRAY object from a buffer ID and a Given type +/*! +m_buffer_data will be 0, for access to the elements, you'd need to call lock_array +\param type Type of the Array. It determines the stride. +\param array_data Array structure to be filled +\param buffer_id A GBUFFER_ID structure which this array_daya will refer to +\param element_count Number of elements +\param offset element offset, it isn't byte offset. 0 is recomended +\post Adds reference to the buffer +\sa gim_buffer_add_ref +*/ +#define GIM_BUFFER_ARRAY_INIT_TYPE_OFFSET(type, array_data, buffer_id, element_count, offset) \ +{ \ + (array_data).m_buffer_id.m_buffer_id = (buffer_id).m_buffer_id; \ + (array_data).m_buffer_id.m_bm_data = (buffer_id).m_bm_data; \ + (array_data).m_buffer_data = 0; \ + (array_data).m_element_count = (element_count);\ + GIM_BUFFER_ARRAY_SET_STRIDE(type, array_data); \ + GIM_BUFFER_ARRAY_SET_OFFSET(array_data, offset); \ + gim_buffer_add_ref(&(buffer_id)); \ +}\ + +//! Initializes a buffer array giving a data type and a buffer id +/*! +m_buffer_data will be 0, for access to the elements, you'd need to call lock_array. +\param type Type of the Array. It determines the stride. +\param array_data Array structure to be filled +\param buffer_id A GBUFFER_ID structure which this array_daya will refer to +\param element_count Number of elements +\post Adds reference to the buffer +\sa gim_buffer_add_ref +*/ +#define GIM_BUFFER_ARRAY_INIT_TYPE(type, array_data, buffer_id, element_count) GIM_BUFFER_ARRAY_INIT_TYPE_OFFSET(type, array_data, buffer_id, element_count, 0) + +//! Gain access to the array buffer through the m_buffer_data element +/*! +m_buffer_data pointer will be located at the m_byte_offset position of the buffer m_buffer +Then, You'd need to call unlock_array when finish to using the array access. + +\pre if m_buffer_data != 0, the function returns +\param array_data Array structure to be locked +\param access A constant for access to the buffer. can be G_MA_READ_ONLY,G_MA_WRITE_ONLY or G_MA_READ_WRITE +\return an Buffer error code +*/ +GINT32 gim_buffer_array_lock(GBUFFER_ARRAY * array_data, int access); + +//! close the access to the array buffer through the m_buffer_data element +/*! +\param array_data Array structure to be locked +\return an Buffer error code +*/ +GINT32 gim_buffer_array_unlock(GBUFFER_ARRAY * array_data); + +//! Copy an array by reference +/*! +\post A reference to the m_buffer_id is increased. +*/ +void gim_buffer_array_copy_ref(GBUFFER_ARRAY * source_data,GBUFFER_ARRAY * dest_data); + + +//! Copy an array by value +/*! +\post A new buffer is created +*/ +void gim_buffer_array_copy_value(GBUFFER_ARRAY * source_data, + GBUFFER_MANAGER_DATA dest_buffer_managers[],GBUFFER_ARRAY * dest_data, + GUINT32 buffer_manager_id,int usage); + +//! Destroys an GBUFFER_ARRAY object +/*! +\post Attemps to destroy the buffer, decreases reference counting +*/ +void GIM_BUFFER_ARRAY_DESTROY(GBUFFER_ARRAY & array_data); + +//! Copy the content of the array to a pointer +/*! +\pre dest_data must have the same size as the array_data +\param type +\param array_data A GBUFFERED_ARRAY structure +\param dest_data A type pointer +*/ +#define GIM_BUFFER_ARRAY_DOWNLOAD(type,array_data,dest_data) \ +{ \ + if (GIM_BUFFER_ARRAY_IS_ALIGNED(type, array_data)) \ + { \ + gim_download_from_buffer(&(array_data).m_buffer_id, (array_data).m_byte_offset, (void *)(dest_data), (array_data).m_element_count * (array_data).m_byte_stride); \ + } \ + else \ + { \ + GUINT32 _k_, _ecount_= (array_data).m_element_count; \ + type * _source_vert_; \ + type * _dest_vert_ = (dest_data); \ + gim_buffer_array_lock(&(array_data), G_MA_READ_ONLY); \ + for (_k_ = 0; _k_ < _ecount_; _k_++) \ + { \ + _source_vert_ = GIM_BUFFER_ARRAY_POINTER(type, array_data, _k_); \ + memcpy(_dest_vert_, _source_vert_, sizeof(type)); \ + _dest_vert_++; \ + } \ + gim_buffer_array_unlock(&(array_data)); \ + } \ +} \ + +//! Upload the content of a pointer to a buffered array +/*! +\pre source_data must have the same size as the array_data +\param type +\param array_data A GBUFFERED_ARRAY structure +\param source_data A void pointer +*/ +#define GIM_BUFFER_ARRAY_UPLOAD(type, array_data, source_data) \ +{ \ + if (GIM_BUFFER_ARRAY_IS_ALIGNED(type, array_data)) \ + { \ + gim_upload_to_buffer(&(array_data).m_buffer_id, (array_data).m_byte_offset, (void *)(source_data), (array_data).m_element_count * (array_data).m_byte_stride); \ + } \ + else \ + { \ + GUINT32 _k_, _ecount_= (array_data).m_element_count; \ + type * _source_vert_ = (source_data); \ + type * _dest_vert_; \ + gim_buffer_array_lock(&(array_data), G_MA_WRITE_ONLY); \ + for (_k_ = 0; _k_ < _ecount_; _k_++) \ + { \ + _dest_vert_ = GIM_BUFFER_ARRAY_POINTER(type, array_data, _k_); \ + memcpy(_dest_vert_, _source_vert_, sizeof(type)); \ + _source_vert_++; \ + } \ + gim_buffer_array_unlock(&(array_data)); \ + } \ +} \ + + +//!Kernel function prototype for process streams, given a buffered array as source and +/*! +\param 1 the uniform arguments +\param 2 the source stream +\param 3 the destination stream +*/ +typedef void (* gim_kernel_func)(void *,GBUFFER_ARRAY *,GBUFFER_ARRAY *); + +//! Generic Stream Processingp loop +/*! + +This macro executes a kernel macro or function for each element of the streams +\pre _src_array->m_count <= _dst_array->m_count + +\param _uniform_data An argument to be passed to the Kernel function +\param _src_array An GBUFFER_ARRAY structure passed as the source stream +\param _dst_array An GBUFFER_ARRAY structure passed as the source stream +\param _kernel Macro or function of the kernel +\param _src_type Required. Type of all elements of the source stream +\param _dst_type Required. Type of all elements of the dest stream +*/ +#define GIM_PROCESS_BUFFER_ARRAY(_uniform_data, _src_array, _dst_array, _kernel, _src_type, _dst_type) \ +{ \ +\ + gim_buffer_array_lock(&(_src_array), G_MA_READ_ONLY); \ + gim_buffer_array_lock(&(_dst_array), G_MA_WRITE_ONLY); \ +\ + GUINT32 _i_, _count_=(_src_array).m_element_count; \ +\ + _src_type * _source_vert_; \ + _dst_type * _dest_vert_; \ + if (GIM_BUFFER_ARRAY_IS_ALIGNED(_src_type, _src_array) && GIM_BUFFER_ARRAY_IS_ALIGNED(_dst_type, _dst_array)) \ + { \ +\ + _source_vert_ = GIM_BUFFER_ARRAY_POINTER(_src_type, _src_array, 0); \ + _dest_vert_ = GIM_BUFFER_ARRAY_POINTER(_dst_type, _dst_array, 0); \ + for (_i_ = 0;_i_< _count_; _i_++) \ + { \ + _kernel(_uniform_data, *_source_vert_, *_dest_vert_); \ + _source_vert_++; \ + _dest_vert_++; \ + } \ + } \ + else \ + { \ + for (_i_ = 0; _i_ < _count_; _i_++) \ + { \ + _source_vert_ = GIM_BUFFER_ARRAY_POINTER(_src_type, _src_array, _i_); \ + _dest_vert_ = GIM_BUFFER_ARRAY_POINTER(_dst_type, _dst_array, _i_); \ + _kernel(_uniform_data, *_source_vert_, *_dest_vert_); \ + } \ + } \ + gim_buffer_array_unlock(&(_src_array)); \ + gim_buffer_array_unlock(&(_dst_array)); \ +} \ + +//! @} + +#endif // GIM_MEMORY_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_radixsort.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_radixsort.h new file mode 100644 index 0000000..cb85e71 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_radixsort.h @@ -0,0 +1,258 @@ +#ifndef GIM_RADIXSORT_H_INCLUDED +#define GIM_RADIXSORT_H_INCLUDED +/*! \file gim_radixsort.h +\author Francisco Len. +Based on the work of Michael Herf : "fast floating-point radix sort" +Avaliable on http://www.stereopsis.com/radix.html +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_memory.h" + +/*! \defgroup SORTING +\brief +Macros for sorting. +*/ +//! @{ +struct GIM_RSORT_TOKEN +{ + GUINT32 m_key; + GUINT32 m_value; +}; +//typedef struct _GIM_RSORT_TOKEN GIM_RSORT_TOKEN; + +//comparator for sorting +#define RSORT_TOKEN_COMPARATOR(x, y) ((int)((x.m_key) - (y.m_key))) + +// ---- utils for accessing 11-bit quantities +#define D11_0(x) (x & 0x7FF) +#define D11_1(x) (x >> 11 & 0x7FF) +#define D11_2(x) (x >> 22 ) + + +//COMMON FUNCTIONS FOR ACCESSING THE KEY OF AN ELEMENT + + +//For the type of your array, you need to declare a macro for obtaining the key, like these: +#define SIMPLE_GET_FLOAT32KEY(e,key) {key =(GREAL)(e);} + +#define SIMPLE_GET_INTKEY(e,key) {key =(GINT32)(e);} + +#define SIMPLE_GET_UINTKEY(e,key) {key =(GUINT32)(e);} + +//For the type of your array, you need to declare a macro for copy elements, like this: + +#define SIMPLE_COPY_ELEMENTS(dest,src) {dest = src;} + +#define kHist 2048 + +///Radix sort for unsigned integer keys + +#define GIM_RADIX_SORT_RTOKENS(array,sorted,element_count)\ +{\ + GUINT32 i;\ + GUINT32 b0[kHist * 3];\ + GUINT32 *b1 = b0 + kHist;\ + GUINT32 *b2 = b1 + kHist;\ + for (i = 0; i < kHist * 3; i++)\ + {\ + b0[i] = 0;\ + }\ + GUINT32 fi;\ + GUINT32 pos;\ + for (i = 0; i < element_count; i++)\ + {\ + fi = array[i].m_key;\ + b0[D11_0(fi)] ++;\ + b1[D11_1(fi)] ++;\ + b2[D11_2(fi)] ++;\ + }\ + {\ + GUINT32 sum0 = 0, sum1 = 0, sum2 = 0;\ + GUINT32 tsum;\ + for (i = 0; i < kHist; i++)\ + {\ + tsum = b0[i] + sum0;\ + b0[i] = sum0 - 1;\ + sum0 = tsum;\ + tsum = b1[i] + sum1;\ + b1[i] = sum1 - 1;\ + sum1 = tsum;\ + tsum = b2[i] + sum2;\ + b2[i] = sum2 - 1;\ + sum2 = tsum;\ + }\ + }\ + for (i = 0; i < element_count; i++)\ + {\ + fi = array[i].m_key;\ + pos = D11_0(fi);\ + pos = ++b0[pos];\ + sorted[pos].m_key = array[i].m_key;\ + sorted[pos].m_value = array[i].m_value;\ + }\ + for (i = 0; i < element_count; i++)\ + {\ + fi = sorted[i].m_key;\ + pos = D11_1(fi);\ + pos = ++b1[pos];\ + array[pos].m_key = sorted[i].m_key;\ + array[pos].m_value = sorted[i].m_value;\ + }\ + for (i = 0; i < element_count; i++)\ + {\ + fi = array[i].m_key;\ + pos = D11_2(fi);\ + pos = ++b2[pos];\ + sorted[pos].m_key = array[i].m_key;\ + sorted[pos].m_value = array[i].m_value;\ + }\ +}\ + +/// Get the sorted tokens from an array. For generic use. Tokens are GIM_RSORT_TOKEN +#define GIM_RADIX_SORT_ARRAY_TOKENS(array, sorted_tokens, element_count, get_uintkey_macro)\ +{\ + GIM_RSORT_TOKEN * _unsorted = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN )*element_count);\ + GUINT32 _i;\ + for (_i=0;_i 0)\ + {\ + _stack_index_ --;\ + _start_ = _start_stack_[_stack_index_];\ + _end_ = _end_stack_[_stack_index_];\ + while (_end_ - _start_ > 2)\ + {\ + _p_ = _start_;\ + _i_ = _start_ + 1;\ + _j_ = _end_ - 1;\ + while (_i_<_j_) \ + {\ + for(; _i_<=_j_ && comp_macro(((array)[_i_]),((array)[_p_]))<=0; _i_++) ;\ + if (_i_ > _j_) \ + {\ + exchange_macro(type, array, _j_, _p_);\ + _i_ = _j_;\ + }\ + else\ + {\ + for(; _i_<=_j_ && comp_macro(((array)[_j_]),((array)[_p_]))>=0; _j_--) ;\ + if (_i_ > _j_) \ + {\ + exchange_macro(type, array, _j_, _p_);\ + _i_ = _j_;\ + }\ + else if (_i_ < _j_)\ + {\ + exchange_macro(type, array, _i_, _j_);\ + if (_i_+2 < _j_) {_i_++; _j_--;}\ + else if (_i_+1 < _j_) _i_++;\ + }\ + }\ + }\ + if (_i_-_start_ > 1 && _end_-_j_ > 1) \ + {\ + if (_i_-_start_ < _end_-_j_-1) \ + {\ + _start_stack_[_stack_index_] = _j_+1;\ + _end_stack_[_stack_index_] = _end_;\ + _stack_index_ ++;\ + _end_ = _i_;\ + }\ + else\ + {\ + _start_stack_[_stack_index_] = _start_;\ + _end_stack_[_stack_index_] = _i_;\ + _stack_index_ ++;\ + _start_ = _j_+1;\ + }\ + }\ + else\ + {\ + if (_i_-_start_ > 1)\ + {\ + _end_ = _i_;\ + }\ + else \ + {\ + _start_ = _j_+1;\ + }\ + }\ + }\ + if (_end_ - _start_ == 2) \ + {\ + if (comp_macro(((array)[_start_]),((array)[_end_-1])) > 0) \ + {\ + exchange_macro(type, array, _start_, _end_-1);\ + }\ + }\ + }\ +}\ + +#define GIM_DEF_EXCHANGE_MACRO(type, _array, _i, _j)\ +{\ + type _e_tmp_ =(_array)[(_i)];\ + (_array)[(_i)]=(_array)[(_j)];\ + (_array)[(_j)]= _e_tmp_;\ +}\ + +#define GIM_COMP_MACRO(x, y) ((GINT32)((x) - (y))) +//! @} +#endif // GIM_RADIXSORT_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_capsule_collision.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_capsule_collision.h new file mode 100644 index 0000000..8adace5 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_capsule_collision.h @@ -0,0 +1,111 @@ +#ifndef GIM_TRI_CAPSULE_COLLISION_H_INCLUDED +#define GIM_TRI_CAPSULE_COLLISION_H_INCLUDED + +/*! \file gim_tri_capsule_collision.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_memory.h" + +/*! \addtogroup GEOMETRIC_OPERATIONS +*/ +//! @{ + +//! Capsule struct +struct GIM_CAPSULE_DATA +{ + GREAL m_radius; + vec3f m_point1; + vec3f m_point2; +}; +//typedef struct _GIM_CAPSULE_DATA GIM_CAPSULE_DATA; + +#define CALC_CAPSULE_AABB(capsule,aabb)\ +{\ + if(capsule.m_point1[0]G_EPSILON ?1:0; \ + if(_classif == 0) \ + { \ + if(_prevclassif==1) \ + {\ + if(clipped_count u*axe1[i1] + ((vecproj[i2] - u*axe1[i2])/axe2[i2])*axe2[i1] = vecproj[i1] + + --> u*axe1[i1] + vecproj[i2]*axe2[i1]/axe2[i2] - u*axe1[i2]*axe2[i1]/axe2[i2] = vecproj[i1] + + --> u*(axe1[i1] - axe1[i2]*axe2[i1]/axe2[i2]) = vecproj[i1] - vecproj[i2]*axe2[i1]/axe2[i2] + + --> u*((axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1])/axe2[i2]) = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1])/axe2[i2] + + --> u*(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1]) = vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1] + + --> u = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1]) /(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1]) + +if 0.0<= u+v <=1.0 then they are inside of triangle + + */ +#define TRIANGLE_GET_UVPARAMETERS(point,vec1,vec2,vec3,tri_plane,u,v,outside)\ +{\ + vec3f _axe1, _axe2, _vecproj;\ + VEC_DIFF(_axe1,vec2,vec1);\ + VEC_DIFF(_axe2,vec3,vec1);\ + VEC_DIFF(_vecproj,point,vec1);\ + GUINT32 _i1,_i2;\ + PLANE_MINOR_AXES(tri_plane, _i1, _i2);\ + if(fabsf(_axe2[_i2])G_EPSILON)\ + {\ + outside = 1;\ + }\ + else\ + {\ + outside = 0;\ + }\ + }\ +}\ + +//! Finds the collision of a ray and a triangle. +#define RAY_TRIANGLE_INTERSECTION(vOrigin,vDir,vec1,vec2,vec3,tri_plane,pout,u,v,tparam,tmax,does_intersect)\ +{\ + RAY_PLANE_COLLISION(tri_plane,vDir,vOrigin,pout,tparam,does_intersect);\ + if(does_intersect != 0)\ + {\ + if(tparam<-G_EPSILON||tparam>tmax+G_EPSILON)\ + {\ + does_intersect = 0;\ + }\ + else\ + {\ + TRIANGLE_GET_UVPARAMETERS(pout,vec1,vec2,vec3,tri_plane,u,v,does_intersect);\ + does_intersect = !does_intersect;\ + }\ + }\ +}\ + + +//! @} + +#endif // GIM_TRI_COLLISION_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_sphere_collision.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_sphere_collision.h new file mode 100644 index 0000000..184d5bf --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_tri_sphere_collision.h @@ -0,0 +1,51 @@ +#ifndef GIM_TRI_SPHERE_COLLISION_H_INCLUDED +#define GIM_TRI_SPHERE_COLLISION_H_INCLUDED + +/*! \file gim_tri_sphere_collision.h +\author Francisco Len +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +/*! \addtogroup GEOMETRIC_OPERATIONS +*/ +//! @{ + +//! Finds the contact points from a collision of a triangle and a sphere +/*! +\param tri +\param center +\param radius +\param contact_data Contains the closest points on the Sphere, and the normal is pointing to triangle +*/ +int gim_triangle_sphere_collision( + GIM_TRIANGLE_DATA *tri, + vec3f center, GREAL radius, + GIM_TRIANGLE_CONTACT_DATA * contact_data); + +//! @} +#endif // GIM_TRI_SPHERE_COLLISION_H_INCLUDED diff --git a/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_trimesh.h b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_trimesh.h new file mode 100644 index 0000000..0fb0fcf --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/include/GIMPACT/gim_trimesh.h @@ -0,0 +1,544 @@ +#ifndef GIM_TRIMESH_H_INCLUDED +#define GIM_TRIMESH_H_INCLUDED +/*! \file gim_trimesh.h +\author Francisco Le�n +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_boxpruning.h" +#include "GIMPACT/gim_contact.h" + + +///MAsk defines +#define GIM_TRIMESH_TRANSFORMED_REPLY 1 +#define GIM_TRIMESH_NEED_UPDATE 2 + +/*! \addtogroup TRIMESH +\brief +A Trimesh is the basic geometric structure for representing solid objects. +

    CREATING TRIMESHES

    +
      +
    • For creating trimeshes, you must initialize Buffer managers by calling \ref gimpact_init +
    • Then you must define the vertex and index sources by creating them with \ref BUFFER_ARRAYS routines, and then call \ref gim_trimesh_create_from_arrays. +
    • An alternative way for creating trimesh objects is calling \ref gim_trimesh_create_from_data. +
    • For access to the trimesh data (vertices, triangle indices), you must call \ref gim_trimesh_locks_work_data , and \ref gim_trimesh_unlocks_work_data to finish the access. +
    • Each time when the trimesh data is modified, you must call \ref gim_trimesh_update after. +
    • When a trimesh is no longer needed, you must call \ref gim_trimesh_destroy. +
    + +

    This is an example of how to create a deformable trimesh that shares vertices with the user application:

    +\code +//Declaration of vertices +vec3f trimeshvertices[200]; +//Declaration of indices +GUINT trimeshindices[100]; + +... Initializing vertices and triangle indices at beginning + +//Then create trimesh +GIM_TRIMESH mytrimesh; + +//Calling trimesh create function + +gim_trimesh_create_from_data( +&mytrimesh, +trimeshvertices,200, +0 ,//copy_vertices is 0 +trimeshindices, +100, +0, //copy_indices is 0 +0 //transformed_reply is 0 +); +\endcode +

    Note that parameter transformed_reply is 0, that means that m_transformed_vertex_buffer is a reference to m_source_vertex on the trimesh, and transformations are not avaliable. Use that configuration if you have to simulate a deformable trimesh like cloth or elastic bodies.

    +

    When the trimesh is no longer needed, destroy it safely with gim_trimesh_destroy()

    +

    UPDATING TRIMESHES

    +

    On simulation loops, is needed to update trimeshes every time to update vertices althought updating triangle boxes and planes cache. There is two ways to update trimeshes:

    +
      +
    • Updating vertices directly. You need to access to the \ref GIM_TRIMESH.m_source_vertex_buffer member; a vertex buffer which has access to the source vertices. +\code +// Access to the source vertices +gim_buffer_array_lock(&mytrimesh.m_source_vertex_buffer, G_MA_READ_WRITE); + +//Get a pointer to the vertex buffer +vec3f * vertexpointer = GIM_BUFFER_ARRAY_POINTER(vec3f,mytrimesh.m_source_vertex_buffer,0); + +//Get the amount of vertices +int veccount = mytrimesh.m_source_vertex_buffer.m_element_count; + +//Modify vertices +for (int i=0;itransformed_reply = 0. +
    +
      +
    • Aplying a transformation. Simply use \ref gim_trimesh_set_tranform . Remember that with this method trimeshes must be created with \ref gim_trimesh_create_from_data with parameter transformed_reply = 1. +
    +

    After updating vertices, you must call \ref gim_trimesh_update()

    +

    TRIMESHES COLLISION

    +

    Before collide trimeshes, you need to update them first.

    +

    Then you must use \ref gim_trimesh_trimesh_collision().

    + +*/ +//! @{ + +//! Prototype for updating vertices +typedef void * gim_update_trimesh_function(struct _GIM_TRIMESH *); + +//! Trimesh +struct GIM_TRIMESH +{ + ///Original + //@{ + GBUFFER_ARRAY m_source_vertex_buffer;//!< Buffer of vec3f coordinates + + //! (GUINT) Indices of triangles, groups of three elements. + /*! + Array of GUINT. Triangle indices. Each triple contains indices of the vertices for each triangle. + \invariant must be aligned + */ + GBUFFER_ARRAY m_tri_index_buffer; + //@} + ///Allocated + //@{ + char m_mask;//!< Don't use directly + + //! Allocated transformed vertices vec3f + /*! + Array of vec3f.If gim_trimesh_has_tranformed_reply(this) == 1 then it refers to the m_source_vertex_buffer + \invariant must be aligned + */ + GBUFFER_ARRAY m_transformed_vertex_buffer; + //@} + ///Auxiliary data + //@{ + GIM_AABB_SET m_aabbset; + GDYNAMIC_ARRAY m_planes_cache_buffer;//! Allocated GIM_TRIPLANES_CACHE + GDYNAMIC_ARRAY m_planes_cache_bitset; + gim_update_trimesh_function * m_update_callback;//! If null, then m_transform is applied. + mat4f m_transform; + //@} +}; +//typedef struct _GIM_TRIMESH GIM_TRIMESH; + +/// Info about mesh +//! Return the trimesh triangle count +GUINT32 gim_trimesh_get_triangle_count(GIM_TRIMESH * trimesh); + +//! Returns 1 if the m_transformed_vertex_buffer is a reply of m_source_vertex_buffer +char gim_trimesh_has_tranformed_reply(GIM_TRIMESH * trimesh); + +//! Returns 1 if the trimesh needs to update their aabbset and the planes cache. +char gim_trimesh_needs_update(GIM_TRIMESH * trimesh); + +//! Change the state of the trimesh to force it to update +/*! +Call it after having made changes to the trimesh. +\post gim_trimesh_need_update(trimesh) will return 1 +\sa gim_trimesh_needs_update,gim_trimesh_has_tranformed_reply +*/ +void gim_trimesh_post_update(GIM_TRIMESH * trimesh); + +//! Creates the aabb set and the triangles cache +/*! + +\param trimesh +\param vertex_array +\param triindex_array +\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just is a reference to the original array. +\post it copies the arrays by reference, and creates the auxiliary data (m_aabbset,m_planes_cache_buffer) +*/ +void gim_trimesh_create_from_arrays(GBUFFER_MANAGER_DATA buffer_managers[], + GIM_TRIMESH * trimesh, GBUFFER_ARRAY * vertex_array, GBUFFER_ARRAY * triindex_array,char transformed_reply); + + + +//! Create a trimesh from vertex array and an index array +/*! +\param trimesh An uninitialized GIM_TRIMESH structure +\param vertex_array A buffer to a vec3f array +\param vertex_count +\param triindex_array +\param index_count +\param copy_vertices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data. +\param copy_indices If 1, it copies the source vertices in another buffer. Else (0) it constructs a reference to the data. +\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just be a reference to the original array. Use 1 if you will apply transformations to the trimesh. See \ref gim_trimesh_set_tranform(). +*/ +void gim_trimesh_create_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + GIM_TRIMESH * trimesh, vec3f * vertex_array, GUINT32 vertex_count,char copy_vertices, + GUINT32 * triindex_array, GUINT32 index_count,char copy_indices,char transformed_reply); + +//! Clears auxiliary data and releases buffer arrays +void gim_trimesh_destroy(GIM_TRIMESH * trimesh); + +//! Copies two meshes +/*! +\param source_trimesh +\param dest_trimesh +\param copy_by_reference If 1, it attaches a reference to the source vertices, else it copies the vertices +\param transformed_reply If 1, transformed vertices are reply of source vertives. 1 Is recommended +*/ +void gim_trimesh_copy(GIM_TRIMESH * source_trimesh, + GBUFFER_MANAGER_DATA dest_buffer_managers[], GIM_TRIMESH * dest_trimesh, + char copy_by_reference, char transformed_reply); + + +//! Locks the trimesh for working with it +/*! +\post locks m_tri_index_buffer and m_transformed_vertex_buffer. +\param trimesh +*/ +void gim_trimesh_locks_work_data(GIM_TRIMESH * trimesh); + + +//! unlocks the trimesh +/*! +\post unlocks m_tri_index_buffer and m_transformed_vertex_buffer. +\param trimesh +*/ +void gim_trimesh_unlocks_work_data(GIM_TRIMESH * trimesh); + +//! Updates m_transformed_vertex_buffer +/*! +\pre m_transformed_vertex_buffer must be unlocked +*/ +void gim_trimesh_update_vertices(GIM_TRIMESH * trimesh); + +//! Updates m_aabbset and m_planes_cache_bitset +/*! +\pre gim_trimesh_locks_work_data must be called before +*/ +void gim_trimesh_update_aabbset(GIM_TRIMESH * trimesh); + +//! Calls before perfom collisions. Updates the trimesh if needed +/*! +\post If gim_trimesh_needs_update returns 1, then it calls gim_trimesh_update_vertices and gim_trimesh_update_aabbset +*/ +void gim_trimesh_update(GIM_TRIMESH * trimesh); + +//! Set the transform of a trimesh +/*! +\post This function calls to gim_trimesh_post_update +*/ +void gim_trimesh_set_tranform(GIM_TRIMESH * trimesh, mat4f transform); + +//! Fetch triangle data +/*! +\pre gim_trimesh_locks_work_data must be called before +*/ +void gim_trimesh_get_triangle_data(GIM_TRIMESH * trimesh, GUINT32 triangle_index, GIM_TRIANGLE_DATA * tri_data); + +//! Fetch triangle vertices +/*! +\pre gim_trimesh_locks_work_data must be called before +*/ +void gim_trimesh_get_triangle_vertices(GIM_TRIMESH * trimesh, GUINT32 triangle_index, vec3f v1,vec3f v2,vec3f v3); + +//! Trimesh Trimesh Collisions +/*! +Before use of this function you must update each trimesh: +\code +gim_trimesh_update(TriMesh1); +gim_trimesh_update(TriMesh2); +\endcode +Then you must use the trimesh collision in this way: +\code +int collide_trimeshes(GIM_TRIMESH * TriMesh1, GIM_TRIMESH * TriMesh2) +{ + //Create contact list + GDYNAMIC_ARRAY trimeshcontacts; + GIM_CREATE_CONTACT_LIST(trimeshcontacts); + + //Collide trimeshes + gim_trimesh_trimesh_collision(TriMesh1,TriMesh2,&trimeshcontacts); + + if(trimeshcontacts.m_size == 0) //do nothing + { + GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array + return 0; + } + + //Getting a pointer to the contact array + GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts); + + int contactcount = trimeshcontacts.m_size; + int i; + //Process contacts + for (i=0;i +
  7. m_handle1 points to trimesh1. +
  8. m_handle2 points to trimesh2. +
  9. m_feature1 Is a triangle index of trimesh1. +
  10. m_feature2 Is a triangle index of trimesh2. + + +\param trimesh1 Collider +\param trimesh2 Collidee +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_trimesh_collision(GIM_TRIMESH * trimesh1, GIM_TRIMESH * trimesh2, GDYNAMIC_ARRAY * contacts); + + +//! Trimesh Sphere Collisions +/*! +Before use of this function you must update the trimesh: +\code +gim_trimesh_update(trimesh); +\endcode +Then you must use this function in this way: +\code +int collide_trimesh_sphere(GIM_TRIMESH * trimesh, vec3f center,GREAL radius) +{ + //Create contact list + GDYNAMIC_ARRAY trimeshcontacts; + GIM_CREATE_CONTACT_LIST(trimeshcontacts); + + //Collide trimeshes + gim_trimesh_sphere_collision(trimesh,center,radius,&trimeshcontacts); + + if(trimeshcontacts.m_size == 0) //do nothing + { + GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array + return 0; + } + + //Getting a pointer to the contact array + GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts); + + int contactcount = trimeshcontacts.m_size; + int i; + //Process contacts + for (i=0;i +
  11. m_handle1 points to trimesh. +
  12. m_handle2 points to NULL. +
  13. m_feature1 Is a triangle index of trimesh. + + +\param trimesh +\param center +\param radius +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_sphere_collision(GIM_TRIMESH * trimesh,vec3f center,GREAL radius, GDYNAMIC_ARRAY * contacts); + + +//! Trimesh Capsule collision +/*! +Finds the closest primitive collided with the ray. + +Before use of this function you must update the trimesh: +\code +gim_trimesh_update(trimesh); +\endcode +Then you must use this function in this way: +\code +int collide_trimesh_capsule(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule) +{ + //Create contact list + GDYNAMIC_ARRAY trimeshcontacts; + GIM_CREATE_CONTACT_LIST(trimeshcontacts); + + //Collide trimeshes + gim_trimesh_capsule_collision(trimesh,capsule,&trimeshcontacts); + + if(trimeshcontacts.m_size == 0) //do nothing + { + GIM_DYNARRAY_DESTROY(trimeshcontacts);//clean contact array + return 0; + } + + //Getting a pointer to the contact array + GIM_CONTACT * ptrimeshcontacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,trimeshcontacts); + + int contactcount = trimeshcontacts.m_size; + int i; + //Process contacts + for (i=0;i +
  14. m_handle1 points to trimesh. +
  15. m_handle2 points to NULL. +
  16. m_feature1 Is a triangle index of trimesh. + + +\param trimesh +\param capsule +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_capsule_collision(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts); + + +///Function for create Trimesh Plane collision result +#define GIM_CREATE_TRIMESHPLANE_CONTACTS(dynarray) GIM_DYNARRAY_CREATE(vec4f,dynarray,G_ARRAY_GROW_SIZE) + +//! Trimesh Plane Collisions +/*! + +Before use of this function you must update the trimesh: +\code +gim_trimesh_update(trimesh); +\endcode +Then you must use this function in this way: +\code +int collide_trimesh_plane(GIM_TRIMESH * trimesh, vec4f plane) +{ + //Create contact list + GDYNAMIC_ARRAY tri_plane_contacts; + GIM_CREATE_TRIMESHPLANE_CONTACTS(tri_plane_contacts); + + //Collide trimeshes + gim_trimesh_plane_collision(trimesh,plane,&tri_plane_contacts); + + if(tri_plane_contacts.m_size == 0) //do nothing + { + GIM_DYNARRAY_DESTROY(tri_plane_contacts);//clean contact array + return 0; + } + + //Getting a pointer to the contact array + vec4f * planecontacts = GIM_DYNARRAY_POINTER(vec4f,tri_plane_contacts); + + int contactcount = tri_plane_contacts.m_size; + int i; + //Process contacts + for (i=0;i&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = GIMPACT/include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = GIMPACT +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign GIMPACT/include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign GIMPACT/include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.am b/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.am new file mode 100644 index 0000000..2cd230b --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.am @@ -0,0 +1,19 @@ +noinst_LTLIBRARIES = libGIMPACT.la +AM_CPPFLAGS = -fno-strict-aliasing \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/ode/src \ + -I$(top_srcdir)/GIMPACT/include + +libGIMPACT_la_SOURCES = gim_boxpruning.cpp \ + gim_contact.cpp \ + gim_math.cpp \ + gim_memory.cpp \ + gim_tri_tri_overlap.cpp \ + gim_trimesh.cpp \ + gim_trimesh_capsule_collision.cpp \ + gim_trimesh_ray_collision.cpp \ + gim_trimesh_sphere_collision.cpp \ + gim_trimesh_trimesh_collision.cpp \ + gimpact.cpp + diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.in b/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.in new file mode 100644 index 0000000..70ab49e --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/Makefile.in @@ -0,0 +1,638 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = GIMPACT/src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libGIMPACT_la_LIBADD = +am_libGIMPACT_la_OBJECTS = gim_boxpruning.lo gim_contact.lo \ + gim_math.lo gim_memory.lo gim_tri_tri_overlap.lo \ + gim_trimesh.lo gim_trimesh_capsule_collision.lo \ + gim_trimesh_ray_collision.lo gim_trimesh_sphere_collision.lo \ + gim_trimesh_trimesh_collision.lo gimpact.lo +libGIMPACT_la_OBJECTS = $(am_libGIMPACT_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libGIMPACT_la_SOURCES) +DIST_SOURCES = $(libGIMPACT_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libGIMPACT.la +AM_CPPFLAGS = -fno-strict-aliasing \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/ode/src \ + -I$(top_srcdir)/GIMPACT/include + +libGIMPACT_la_SOURCES = gim_boxpruning.cpp \ + gim_contact.cpp \ + gim_math.cpp \ + gim_memory.cpp \ + gim_tri_tri_overlap.cpp \ + gim_trimesh.cpp \ + gim_trimesh_capsule_collision.cpp \ + gim_trimesh_ray_collision.cpp \ + gim_trimesh_sphere_collision.cpp \ + gim_trimesh_trimesh_collision.cpp \ + gimpact.cpp + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign GIMPACT/src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign GIMPACT/src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libGIMPACT.la: $(libGIMPACT_la_OBJECTS) $(libGIMPACT_la_DEPENDENCIES) $(EXTRA_libGIMPACT_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(libGIMPACT_la_OBJECTS) $(libGIMPACT_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_boxpruning.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_contact.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_math.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_memory.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_tri_tri_overlap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_trimesh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_trimesh_capsule_collision.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_trimesh_ray_collision.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_trimesh_sphere_collision.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gim_trimesh_trimesh_collision.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gimpact.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_boxpruning.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_boxpruning.cpp new file mode 100644 index 0000000..c9ccf74 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_boxpruning.cpp @@ -0,0 +1,519 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_boxpruning.h" + + + +//! Allocate memory for all aabb set. +void gim_aabbset_alloc(GIM_AABB_SET * aabbset, GUINT32 count) +{ + aabbset->m_count = count; + aabbset->m_boxes = (aabb3f *)gim_alloc(sizeof(aabb3f)*count); + + if(countm_maxcoords = 0; + aabbset->m_sorted_mincoords = 0; + } + else + { + aabbset->m_maxcoords = (GUINT32 *)gim_alloc(sizeof(GUINT32)*aabbset->m_count ); + aabbset->m_sorted_mincoords = (GIM_RSORT_TOKEN *)gim_alloc(sizeof(GIM_RSORT_TOKEN)*aabbset->m_count); + } + aabbset->m_shared = 0; + INVALIDATE_AABB(aabbset->m_global_bound); +} + +//! Destroys the aabb set. +void gim_aabbset_destroy(GIM_AABB_SET * aabbset) +{ + aabbset->m_count = 0; + if(aabbset->m_shared==0) + { + gim_free(aabbset->m_boxes,0); + gim_free(aabbset->m_maxcoords,0); + gim_free(aabbset->m_sorted_mincoords,0); + } + aabbset->m_boxes = 0; + aabbset->m_sorted_mincoords = 0; + aabbset->m_maxcoords = 0; +} + +void gim_aabbset_calc_global_bound(GIM_AABB_SET * aabbset) +{ + aabb3f * paabb = aabbset->m_boxes; + aabb3f * globalbox = &aabbset->m_global_bound; + AABB_COPY((*globalbox),(*paabb)); + + GUINT32 count = aabbset->m_count-1; + paabb++; + while(count) + { + MERGEBOXES(*globalbox,*paabb) + paabb++; + count--; + } +} + + +//! Sorts the boxes for box prunning. +/*! +1) find the integer representation of the aabb coords +2) Sorts the min coords +3) Calcs the global bound +\pre aabbset must be allocated. And the boxes must be already set. +\param aabbset +\param calc_global_bound If 1 , calcs the global bound +\post If aabbset->m_sorted_mincoords == 0, then it allocs the sorted coordinates +*/ +void gim_aabbset_sort(GIM_AABB_SET * aabbset, char calc_global_bound) +{ + if(aabbset->m_sorted_mincoords == 0) + {//allocate + aabbset->m_maxcoords = (GUINT32 *)gim_alloc(sizeof(GUINT32)*aabbset->m_count ); + aabbset->m_sorted_mincoords = (GIM_RSORT_TOKEN *)gim_alloc(sizeof(GIM_RSORT_TOKEN)*aabbset->m_count); + } + + GUINT32 i, count = aabbset->m_count; + aabb3f * paabb = aabbset->m_boxes; + GUINT32 * maxcoords = aabbset->m_maxcoords; + GIM_RSORT_TOKEN * sorted_tokens = aabbset->m_sorted_mincoords; + + if(count<860)//Calibrated on a Pentium IV + { + //Sort by quick sort + //Calculate keys + for(i=0;im_index1 = i;\ + _pair->m_index2 = j;\ +} + +#define PUSH_PAIR_INV(i,j,pairset)\ +{\ + GIM_DYNARRAY_PUSH_EMPTY(GIM_PAIR,pairset);\ + GIM_PAIR * _pair = GIM_DYNARRAY_POINTER(GIM_PAIR,pairset) + (pairset).m_size - 1;\ + _pair->m_index1 = j;\ + _pair->m_index2 = i;\ +} + +#define FIND_OVERLAPPING_FOWARD(\ + curr_index,\ + test_count,\ + test_aabb,\ + max_coord_uint,\ + sorted_tokens,\ + aabbarray,\ + pairset,\ + push_pair_macro)\ +{\ + GUINT32 _i = test_count;\ + char _intersected;\ + GIM_RSORT_TOKEN * _psorted_tokens = sorted_tokens;\ + while(_i>0 && max_coord_uint >= _psorted_tokens->m_key)\ + {\ + AABBCOLLISION(_intersected,test_aabb,aabbarray[_psorted_tokens->m_value]);\ + if(_intersected)\ + {\ + push_pair_macro(curr_index, _psorted_tokens->m_value,pairset);\ + }\ + _psorted_tokens++;\ + _i--;\ + }\ +} + +//! log(N) Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +\pre aabbset must be allocated and sorted, the boxes must be already set. +\param aabbset Must be sorted. Global bound isn't required +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_self_intersections_sorted(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs) +{ + collision_pairs->m_size = 0; + GUINT32 count = aabbset->m_count; + aabb3f * paabb = aabbset->m_boxes; + GUINT32 * maxcoords = aabbset->m_maxcoords; + GIM_RSORT_TOKEN * sorted_tokens = aabbset->m_sorted_mincoords; + aabb3f test_aabb; + while(count>1) + { + ///current cache variables + GUINT32 curr_index = sorted_tokens->m_value; + GUINT32 max_coord_uint = maxcoords[curr_index]; + AABB_COPY(test_aabb,paabb[curr_index]); + + ///next pairs + sorted_tokens++; + count--; + FIND_OVERLAPPING_FOWARD( curr_index, count, test_aabb, max_coord_uint, sorted_tokens , paabb, (*collision_pairs),PUSH_PAIR); + } +} + +//! NxN Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +\pre aabbset must be allocated, the boxes must be already set. +\param aabbset Global bound isn't required. Doen't need to be sorted. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_self_intersections_brute_force(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs) +{ + collision_pairs->m_size = 0; + GUINT32 i,j; + GUINT32 count = aabbset->m_count; + aabb3f * paabb = aabbset->m_boxes; + char intersected; + for (i=0;i< count-1 ;i++ ) + { + for (j=i+1;jm_size = 0; + + AABBCOLLISION(intersected,aabbset1->m_global_bound,aabbset2->m_global_bound); + if(intersected == 0) return; + + GUINT32 count1 = aabbset1->m_count; + aabb3f * paabb1 = aabbset1->m_boxes; + GUINT32 * maxcoords1 = aabbset1->m_maxcoords; + GIM_RSORT_TOKEN * sorted_tokens1 = aabbset1->m_sorted_mincoords; + + GUINT32 count2 = aabbset2->m_count; + aabb3f * paabb2 = aabbset2->m_boxes; + GUINT32 * maxcoords2 = aabbset2->m_maxcoords; + GIM_RSORT_TOKEN * sorted_tokens2 = aabbset2->m_sorted_mincoords; + + GUINT32 curr_index; + + GUINT32 max_coord_uint; + aabb3f test_aabb; + + //Classify boxes + //Find Set intersection + aabb3f int_abbb; + BOXINTERSECTION(aabbset1->m_global_bound,aabbset2->m_global_bound, int_abbb); + + //Clasify set 1 + GIM_RSORT_TOKEN * classified_tokens1 = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN)*count1); + GUINT32 i,classified_count1 = 0,classified_count2 = 0; + + + for (i=0;i0&&classified_count2>0) + { + if(sorted_tokens1->m_key <= sorted_tokens2->m_key) + { + ///current cache variables + curr_index = sorted_tokens1->m_value; + max_coord_uint = maxcoords1[curr_index]; + AABB_COPY(test_aabb,paabb1[curr_index]); + ///next pairs + sorted_tokens1++; + classified_count1--; + FIND_OVERLAPPING_FOWARD( curr_index, classified_count2, test_aabb, max_coord_uint, sorted_tokens2 , paabb2, (*collision_pairs), PUSH_PAIR); + } + else ///Switch test + { + ///current cache variables + curr_index = sorted_tokens2->m_value; + max_coord_uint = maxcoords2[curr_index]; + AABB_COPY(test_aabb,paabb2[curr_index]); + ///next pairs + sorted_tokens2++; + classified_count2--; + FIND_OVERLAPPING_FOWARD( curr_index, classified_count1, test_aabb, max_coord_uint, sorted_tokens1 , paabb1, (*collision_pairs), PUSH_PAIR_INV ); + } + } + gim_free(classified_tokens1 ,0); + gim_free(classified_tokens2 ,0); +} + +//! NxM Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. +/*! +\pre aabbset1 and aabbset2 must be allocated and sorted, the boxes must be already set. +\param aabbset1 Must be sorted, Global bound is required. +\param aabbset2 Must be sorted, Global bound is required. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_bipartite_intersections_brute_force(GIM_AABB_SET * aabbset1,GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs) +{ + char intersected; + collision_pairs->m_size = 0; + AABBCOLLISION(intersected,aabbset1->m_global_bound,aabbset2->m_global_bound); + if(intersected == 0) return; + + aabb3f int_abbb; + //Find Set intersection + BOXINTERSECTION(aabbset1->m_global_bound,aabbset2->m_global_bound, int_abbb); + //Clasify set 1 + GUINT32 i,j; + GUINT32 classified_count = 0; + + GUINT32 count = aabbset1->m_count; + aabb3f * paabb1 = aabbset1->m_boxes; + aabb3f * paabb2 = aabbset2->m_boxes; + + GUINT32 * classified = (GUINT32 *) gim_alloc(sizeof(GUINT32)*count); + + for (i=0;im_count; + for (i=0;im_count < GIM_MIN_SORTED_BIPARTITE_PRUNING_BOXES) + {//Brute force approach + gim_aabbset_calc_global_bound(aabbset); + } + else + {//Sorted force approach + gim_aabbset_sort(aabbset,1); + } +} + +//! Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. +/*! +This function sorts the set and then it calls to gim_aabbset_self_intersections_brute_force or gim_aabbset_self_intersections_sorted. + +\param aabbset Set of boxes. Sorting isn't required. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +\pre aabbset must be allocated and initialized. +\post If aabbset->m_count >= GIM_MIN_SORTED_PRUNING_BOXES, then it calls to gim_aabbset_sort and then to gim_aabbset_self_intersections_sorted. +*/ +void gim_aabbset_self_intersections(GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collision_pairs) +{ + if(aabbset->m_count < GIM_MIN_SORTED_PRUNING_BOXES) + {//Brute force approach + gim_aabbset_self_intersections_brute_force(aabbset,collision_pairs); + } + else + {//Sorted force approach + gim_aabbset_sort(aabbset,0); + gim_aabbset_self_intersections_sorted(aabbset,collision_pairs); + } +} + +//! Collides two sets. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. +/*! +\pre aabbset1 and aabbset2 must be allocated and updated. See . +\param aabbset1 Must be sorted, Global bound is required. +\param aabbset2 Must be sorted, Global bound is required. +\param collision_pairs Array of GIM_PAIR elements. Must be initialized before (Reserve size ~ 100) +*/ +void gim_aabbset_bipartite_intersections(GIM_AABB_SET * aabbset1, GIM_AABB_SET * aabbset2, GDYNAMIC_ARRAY * collision_pairs) +{ + if(aabbset1->m_sorted_mincoords == 0||aabbset2->m_sorted_mincoords == 0) + {//Brute force approach + gim_aabbset_bipartite_intersections_brute_force(aabbset1,aabbset2,collision_pairs); + } + else + {//Sorted force approach + gim_aabbset_bipartite_intersections_sorted(aabbset1,aabbset2,collision_pairs); + } +} + +void gim_aabbset_box_collision(aabb3f *test_aabb, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided) +{ + collided->m_size = 0; + char intersected; + AABBCOLLISION(intersected,aabbset->m_global_bound,(*test_aabb)); + if(intersected == 0) return; + + GUINT32 i; + GUINT32 count = aabbset->m_count; + aabb3f * paabb = aabbset->m_boxes; + aabb3f _testaabb; + AABB_COPY(_testaabb,*test_aabb); + + for (i=0;i< count;i++ ) + { + AABBCOLLISION(intersected,paabb[i],_testaabb); + if(intersected) + { + GIM_DYNARRAY_PUSH_ITEM(GUINT32,(*collided),i); + } + } +} + +void gim_aabbset_ray_collision(vec3f vorigin,vec3f vdir, GREAL tmax, GIM_AABB_SET * aabbset, GDYNAMIC_ARRAY * collided) +{ + collided->m_size = 0; + char intersected; + GREAL tparam = 0; + BOX_INTERSECTS_RAY(aabbset->m_global_bound, vorigin, vdir, tparam, tmax,intersected); + if(intersected==0) return; + + GUINT32 i; + GUINT32 count = aabbset->m_count; + aabb3f * paabb = aabbset->m_boxes; + + for (i=0;i< count;i++ ) + { + BOX_INTERSECTS_RAY(paabb[i], vorigin, vdir, tparam, tmax,intersected); + if(intersected) + { + GIM_DYNARRAY_PUSH_ITEM(GUINT32,(*collided),i); + } + } + (void)tparam; +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_contact.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_contact.cpp new file mode 100644 index 0000000..d16aac5 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_contact.cpp @@ -0,0 +1,132 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_contact.h" + +void gim_merge_contacts(GDYNAMIC_ARRAY * source_contacts, + GDYNAMIC_ARRAY * dest_contacts) +{ + dest_contacts->m_size = 0; + + GUINT32 source_count = source_contacts->m_size; + GIM_CONTACT * psource_contacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,(*source_contacts)); + //create keys + GIM_RSORT_TOKEN * keycontacts = (GIM_RSORT_TOKEN * )gim_alloc(sizeof(GIM_RSORT_TOKEN)*source_count); + + GUINT32 i; + for(i=0;im_size;i++) + { + key = keycontacts[i].m_key; + scontact = &psource_contacts[keycontacts[i].m_value]; + + if(i>0 && last_key == key) + { + //merge contact + if(pcontact->m_depth > scontact->m_depth + CONTACT_DIFF_EPSILON) + { + GIM_COPY_CONTACTS(pcontact, scontact); + } + } + else + {//add new contact + GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,(*dest_contacts)); + pcontact = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,(*dest_contacts)); + GIM_COPY_CONTACTS(pcontact, scontact); + } + last_key = key; + } + gim_free(keycontacts,0); +} + +void gim_merge_contacts_unique(GDYNAMIC_ARRAY * source_contacts, + GDYNAMIC_ARRAY * dest_contacts) +{ + dest_contacts->m_size = 0; + //Traverse the source contacts + GUINT32 source_count = source_contacts->m_size; + if(source_count==0) return; + + GIM_CONTACT * psource_contacts = GIM_DYNARRAY_POINTER(GIM_CONTACT,(*source_contacts)); + + //add the unique contact + GIM_CONTACT * pcontact = 0; + GIM_DYNARRAY_PUSH_EMPTY(GIM_CONTACT,(*dest_contacts)); + pcontact = GIM_DYNARRAY_POINTER_LAST(GIM_CONTACT,(*dest_contacts)); + //set the first contact + GIM_COPY_CONTACTS(pcontact, psource_contacts); + + if(source_count==1) return; + //scale the first contact + VEC_SCALE(pcontact->m_normal,pcontact->m_depth,pcontact->m_normal); + + psource_contacts++; + + //Average the contacts + GUINT32 i; + for(i=1;im_point,pcontact->m_point,psource_contacts->m_point); + VEC_ACCUM(pcontact->m_normal,psource_contacts->m_depth,psource_contacts->m_normal); + psource_contacts++; + } + + GREAL divide_average = 1.0f/((GREAL)source_count); + + VEC_SCALE(pcontact->m_point,divide_average,pcontact->m_point); + + pcontact->m_depth = VEC_DOT(pcontact->m_normal,pcontact->m_normal)*divide_average; + GIM_SQRT(pcontact->m_depth,pcontact->m_depth); + + VEC_NORMALIZE(pcontact->m_normal); + + /*GREAL normal_len; + VEC_INV_LENGTH(pcontact->m_normal,normal_len); + VEC_SCALE(pcontact->m_normal,normal_len,pcontact->m_normal); + + //Deep = LEN(normal)/SQRT(source_count) + GIM_SQRT(divide_average,divide_average); + pcontact->m_depth = divide_average/normal_len; + */ +} + + + diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_math.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_math.cpp new file mode 100644 index 0000000..cc50bb3 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_math.cpp @@ -0,0 +1,60 @@ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "GIMPACT/gim_math.h" +#include "stdlib.h" +#include "time.h" + + +GREAL gim_inv_sqrt(GREAL f) +{ + GREAL r; + GIM_INV_SQRT(f,r); + return r; +} + +GREAL gim_sqrt(GREAL f) +{ + GREAL r; + GIM_SQRT(f,r); + return r; +} + +//!Initializes mathematical functions +void gim_init_math() +{ + srand( static_cast< unsigned int >( time( 0 ) ) ); +} + +//! Generates an unit random +GREAL gim_unit_random() +{ + GREAL rn = static_cast< GREAL >( rand() ); + rn/=(GREAL)RAND_MAX; + return rn; +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_memory.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_memory.cpp new file mode 100644 index 0000000..064d3a2 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_memory.cpp @@ -0,0 +1,878 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include +#include +#include "GIMPACT/gim_memory.h" +#include +#include "config.h" +//#include "malloc.h" +//#include "mm_malloc.h" + +static gim_alloc_function *g_allocfn = 0; +// static gim_alloca_function *g_allocafn = 0; -- a nonsense +static gim_realloc_function *g_reallocfn = 0; +static gim_free_function *g_freefn = 0; + + +#define VALIDATE_BUFFER_MANAGER(buffer_managers,buffer_manager_id)\ + if(buffer_manager_id>=G_BUFFER_MANAGER__MAX) return G_BUFFER_OP_INVALID;\ + GBUFFER_MANAGER_DATA * bm_data;\ + gim_get_buffer_manager_data(buffer_managers,buffer_manager_id,&bm_data);\ + if(bm_data == 0) return G_BUFFER_OP_INVALID;\ + +#define VALIDATE_BUFFER_ID_PT(buffer_id)\ + GBUFFER_MANAGER_DATA * bm_data = buffer_id->m_bm_data;\ + if(bm_data == 0) return G_BUFFER_OP_INVALID;\ + if(buffer_id->m_buffer_id>=bm_data->m_buffer_array.m_size) return G_BUFFER_OP_INVALID;\ + GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array);\ + pbuffer += buffer_id->m_buffer_id;\ + if(pbuffer->m_buffer_handle==0) return G_BUFFER_OP_INVALID;\ + + +void GIM_BUFFER_ARRAY_DESTROY(GBUFFER_ARRAY & array_data) +{ + gim_buffer_array_unlock(&array_data); + gim_buffer_free(&(array_data).m_buffer_id); +} + +void GIM_DYNARRAY_DESTROY(GDYNAMIC_ARRAY & array_data) +{ + if(array_data.m_pdata != 0) + { + gim_free(array_data.m_pdata,0); + array_data.m_reserve_size = 0; + array_data.m_size = 0; + array_data.m_pdata = 0; + } +} + +void gim_set_alloc_handler (gim_alloc_function *fn) +{ + g_allocfn = fn; +} + +/* -- a nonsense +void gim_set_alloca_handler (gim_alloca_function *fn) +{ + g_allocafn = fn; +} +*/ + +void gim_set_realloc_handler (gim_realloc_function *fn) +{ + g_reallocfn = fn; +} + +void gim_set_free_handler (gim_free_function *fn) +{ + g_freefn = fn; +} + +gim_alloc_function *gim_get_alloc_handler() +{ + return g_allocfn; +} + +/* -- a nonsense +gim_alloca_function *gim_get_alloca_handler() +{ + return g_allocafn; +} +*/ + +gim_realloc_function *gim_get_realloc_handler () +{ + return g_reallocfn; +} + + +gim_free_function *gim_get_free_handler () +{ + return g_freefn; +} + + +void * gim_alloc(size_t size) +{ + void * ptr; +/* + if (g_allocfn) + { + ptr = g_allocfn(size); + } + else +*/ + { + ptr = malloc(size);//_mm_malloc(size,0);*/ + } + assert(ptr); + return ptr; +} + +/* -- a nonsense +void * gim_alloca(size_t size) +{ + if (g_allocafn) return g_allocafn(size); else return alloca(size); +} +*/ + +void * gim_realloc(void *ptr, size_t oldsize, size_t newsize) +{ + /*if (g_reallocfn) return g_reallocfn(ptr,oldsize,newsize); + else return realloc(ptr,newsize);*/ + //return realloc(ptr,newsize); + void * newptr = gim_alloc(newsize); + size_t copysize = newsize> oldsize? oldsize: newsize; + memcpy(newptr,ptr,copysize); + gim_free(ptr,oldsize); + return newptr; +} + +void gim_free(void *ptr, size_t size) +{ + if (!ptr) return; +/* -- if custom allocation function is not used, custom free must not be used too + if (g_freefn) + { + g_freefn(ptr,size); + } + else +*/ + { + free(ptr);//_mm_free(ptr); + } +} + +///******************************* BUFFER MANAGERS ******************************/// + +//!** Basic buffer prototype functions + +static GPTR _system_buffer_alloc_function(GUINT32 size,int usage) +{ + void * newdata = gim_alloc(size); + memset(newdata,0,size); + return (GPTR)newdata; +} + +static GPTR _system_buffer_alloc_data_function(const void * pdata,GUINT32 size,int usage) +{ + void * newdata = gim_alloc(size); + memcpy(newdata,pdata,size); + return (GPTR)(newdata); +} + +static GPTR _system_buffer_realloc_function(GPTR buffer_handle,GUINT32 oldsize,int old_usage,GUINT32 newsize,int new_usage) +{ + void * newdata = gim_realloc(buffer_handle,oldsize,newsize); + return (GPTR)(newdata); +} + +static void _system_buffer_free_function(GPTR buffer_handle,GUINT32 size) +{ + gim_free(buffer_handle,size); +} + +static char * _system_lock_buffer_function(GPTR buffer_handle,int access) +{ + return (char * )(buffer_handle); +} + + +static void _system_unlock_buffer_function(GPTR buffer_handle) +{ +} + +static void _system_download_from_buffer_function( + GPTR source_buffer_handle, + GUINT32 source_pos, + void * destdata, + GUINT32 copysize) +{ + char * pdata; + pdata = (char *)source_buffer_handle; + memcpy(destdata,pdata+source_pos,copysize); +} + +static void _system_upload_to_buffer_function( + GPTR dest_buffer_handle, + GUINT32 dest_pos, + void * sourcedata, + GUINT32 copysize) +{ + char * pdata; + pdata = (char * )dest_buffer_handle; + memcpy(pdata+dest_pos,sourcedata,copysize); +} + +static void _system_copy_buffers_function( + GPTR source_buffer_handle, + GUINT32 source_pos, + GPTR dest_buffer_handle, + GUINT32 dest_pos, + GUINT32 copysize) +{ + char * pdata1,*pdata2; + pdata1 = (char *)source_buffer_handle; + pdata2 = (char *)dest_buffer_handle; + memcpy(pdata2+dest_pos,pdata1+source_pos,copysize); +} + +static GPTR _shared_buffer_alloc_function(GUINT32 size,int usage) +{ + return 0; +} + +static GPTR _shared_buffer_alloc_data_function(const void * pdata,GUINT32 size,int usage) +{ + return (GPTR)pdata; +} + +#if 0 +static GPTR _shared_buffer_realloc_function(GPTR buffer_handle,GUINT32 oldsize,int old_usage,GUINT32 newsize,int new_usage) +{ + return 0; +} +#endif + +static void _shared_buffer_free_function(GPTR buffer_handle,GUINT32 size) +{ +} + +static inline int _is_buffer_manager_data_active(GBUFFER_MANAGER_DATA * bm_data) +{ + return bm_data->m_buffer_array.m_pdata != 0; +} + +static inline void _init_buffer_manager_data(GBUFFER_MANAGER_DATA * bm_data) +{ + bm_data->m_buffer_array.m_pdata = 0; +} + +static const GBUFFER_MANAGER_PROTOTYPE g_bm_prototypes[G_BUFFER_MANAGER__MAX] = +{ + { + &_system_buffer_alloc_function, // alloc_fn; + &_system_buffer_alloc_data_function, // alloc_data_fn; + &_system_buffer_realloc_function, // realloc_fn; + &_system_buffer_free_function, // free_fn; + &_system_lock_buffer_function, // lock_buffer_fn; + &_system_unlock_buffer_function, // unlock_buffer_fn; + &_system_download_from_buffer_function, // download_from_buffer_fn; + &_system_upload_to_buffer_function, // upload_to_buffer_fn; + &_system_copy_buffers_function, // copy_buffers_fn; + }, // G_BUFFER_MANAGER_SYSTEM + + { + &_shared_buffer_alloc_function, // alloc_fn; + &_shared_buffer_alloc_data_function, // alloc_data_fn; + &_system_buffer_realloc_function, // realloc_fn; + &_shared_buffer_free_function, // free_fn; + &_system_lock_buffer_function, // lock_buffer_fn; + &_system_unlock_buffer_function, // unlock_buffer_fn; + &_system_download_from_buffer_function, // download_from_buffer_fn; + &_system_upload_to_buffer_function, // upload_to_buffer_fn; + &_system_copy_buffers_function, // copy_buffers_fn; + }, // G_BUFFER_MANAGER_SHARED +}; + +int gim_is_buffer_manager_active(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id) +{ + GBUFFER_MANAGER_DATA * bm_data; + bm_data = &buffer_managers[buffer_manager_id]; + return _is_buffer_manager_data_active(bm_data); +} + +//!** Buffer manager operations +void gim_create_buffer_manager(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id) +{ + GBUFFER_MANAGER_DATA * bm_data; + bm_data = &buffer_managers[buffer_manager_id]; + + if (_is_buffer_manager_data_active(bm_data)) + { + gim_destroy_buffer_manager(buffer_managers, buffer_manager_id); + } + + //CREATE ARRAYS + GIM_DYNARRAY_CREATE(GBUFFER_DATA,bm_data->m_buffer_array,G_ARRAY_BUFFERMANAGER_INIT_SIZE); + GIM_DYNARRAY_CREATE(GUINT32,bm_data->m_free_positions,G_ARRAY_BUFFERMANAGER_INIT_SIZE); + bm_data->m_prototype = g_bm_prototypes + buffer_manager_id; + bm_data->m_buffer_manager_id = buffer_manager_id; +} + +void gim_destroy_buffer_manager(GBUFFER_MANAGER_DATA buffer_managers[], GUINT32 buffer_manager_id) +{ + GBUFFER_MANAGER_DATA * bm_data; + gim_get_buffer_manager_data(buffer_managers,buffer_manager_id,&bm_data); + if(bm_data == 0) return; + //Destroy all buffers + + GBUFFER_DATA * buffers = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array); + GUINT32 i, buffer_count = bm_data->m_buffer_array.m_size; + for (i=0;im_buffer_handle!=0) //Is active + { + // free handle + bm_data->m_prototype->free_fn(current_buffer->m_buffer_handle,current_buffer->m_size); + } + } + + //destroy buffer array + GIM_DYNARRAY_DESTROY(bm_data->m_buffer_array); + //destroy free positions + GIM_DYNARRAY_DESTROY(bm_data->m_free_positions); +} +void gim_get_buffer_manager_data(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id,GBUFFER_MANAGER_DATA ** pbm_data) +{ + GBUFFER_MANAGER_DATA * bm_data; + bm_data = &buffer_managers[buffer_manager_id]; + + if (_is_buffer_manager_data_active(bm_data)) + { + *pbm_data = bm_data; + } + else + { + *pbm_data = 0; + } +} + +void gim_init_buffer_managers(GBUFFER_MANAGER_DATA buffer_managers[]) +{ + GUINT32 i; + for (i=0;im_free_positions.m_size>0)\ + { + GUINT32 * _pointer = GIM_DYNARRAY_POINTER(GUINT32,buffer_manager->m_free_positions); + buffer_id = _pointer[buffer_manager->m_free_positions.m_size-1]; + GIM_DYNARRAY_POP_ITEM(buffer_manager->m_free_positions); + } + else + { + buffer_id = buffer_manager->m_buffer_array.m_size; + GIM_DYNARRAY_PUSH_EMPTY(GBUFFER_DATA,buffer_manager->m_buffer_array); + } +} + +GINT32 _validate_buffer_id(GBUFFER_ID * buffer_id,GBUFFER_DATA ** ppbuffer,GBUFFER_MANAGER_DATA ** pbm_data) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + *ppbuffer = pbuffer; + *pbm_data = bm_data; + return G_BUFFER_OP_SUCCESS; +} + +GUINT32 gim_create_buffer( + GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id, + GUINT32 buffer_size, + int usage, + GBUFFER_ID * buffer_id) +{ + VALIDATE_BUFFER_MANAGER(buffer_managers,buffer_manager_id) + + GPTR newbufferhandle = bm_data->m_prototype->alloc_fn(buffer_size,usage); + if(newbufferhandle==0) return G_BUFFER_OP_INVALID; + + GET_AVALIABLE_BUFFER_ID(bm_data,buffer_id->m_buffer_id); + buffer_id->m_bm_data = bm_data; + + GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array); + pbuffer += buffer_id->m_buffer_id ; + pbuffer->m_buffer_handle = newbufferhandle; + pbuffer->m_size = buffer_size; + pbuffer->m_usage = usage; + pbuffer->m_lock_count = 0; + pbuffer->m_refcount = 0; + pbuffer->m_mapped_pointer = 0; + + //set shadow buffer if needed + + if(usage == G_MU_STATIC_READ || + usage == G_MU_STATIC_READ_DYNAMIC_WRITE|| + usage == G_MU_STATIC_READ_DYNAMIC_WRITE_COPY) + { + gim_create_common_buffer(buffer_managers,buffer_size,&pbuffer->m_shadow_buffer); + } + else + { + pbuffer->m_shadow_buffer.m_bm_data = 0; + pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY; + } + return G_BUFFER_OP_SUCCESS; +} + + +GUINT32 gim_create_buffer_from_data( + GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_manager_id, + const void * pdata, + GUINT32 buffer_size, + int usage, + GBUFFER_ID * buffer_id) +{ + VALIDATE_BUFFER_MANAGER(buffer_managers,buffer_manager_id) + + GPTR newbufferhandle = bm_data->m_prototype->alloc_data_fn(pdata,buffer_size,usage); + if(newbufferhandle==0) return G_BUFFER_OP_INVALID; + + GET_AVALIABLE_BUFFER_ID(bm_data,buffer_id->m_buffer_id); + buffer_id->m_bm_data = bm_data; + + GBUFFER_DATA * pbuffer = GIM_DYNARRAY_POINTER(GBUFFER_DATA,bm_data->m_buffer_array); + pbuffer += buffer_id->m_buffer_id ; + pbuffer->m_buffer_handle = newbufferhandle; + pbuffer->m_size = buffer_size; + pbuffer->m_usage = usage; + pbuffer->m_lock_count = 0; + pbuffer->m_mapped_pointer = 0; + pbuffer->m_refcount = 0; + + //set shadow buffer if needed + + if(usage == G_MU_STATIC_READ || + usage == G_MU_STATIC_READ_DYNAMIC_WRITE|| + usage == G_MU_STATIC_READ_DYNAMIC_WRITE_COPY) + { + gim_create_common_buffer_from_data(buffer_managers,pdata,buffer_size,&pbuffer->m_shadow_buffer); + } + else + { + pbuffer->m_shadow_buffer.m_bm_data = 0; + pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY; + } + return G_BUFFER_OP_SUCCESS; +} + +GUINT32 gim_create_common_buffer(GBUFFER_MANAGER_DATA buffer_managers[], + GUINT32 buffer_size, GBUFFER_ID * buffer_id) +{ + return gim_create_buffer(buffer_managers,G_BUFFER_MANAGER_SYSTEM,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id); +} + +GUINT32 gim_create_common_buffer_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + const void * pdata, GUINT32 buffer_size, GBUFFER_ID * buffer_id) +{ + return gim_create_buffer_from_data(buffer_managers,G_BUFFER_MANAGER_SYSTEM,pdata,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id); +} + +GUINT32 gim_create_shared_buffer_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + const void * pdata, GUINT32 buffer_size, GBUFFER_ID * buffer_id) +{ + return gim_create_buffer_from_data(buffer_managers,G_BUFFER_MANAGER_SHARED,pdata,buffer_size,G_MU_DYNAMIC_READ_WRITE,buffer_id); +} + +GINT32 gim_buffer_realloc(GBUFFER_ID * buffer_id,GUINT32 newsize) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + if(pbuffer->m_lock_count>0) return G_BUFFER_OP_INVALID; + GPTR newhandle = buffer_id->m_bm_data->m_prototype->realloc_fn( + pbuffer->m_buffer_handle,pbuffer->m_size,pbuffer->m_usage,newsize,pbuffer->m_usage); + if(newhandle==0) return G_BUFFER_OP_INVALID; + pbuffer->m_buffer_handle = newhandle; + //realloc shadow buffer if any + gim_buffer_realloc(&pbuffer->m_shadow_buffer,newsize); + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_buffer_add_ref(GBUFFER_ID * buffer_id) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + pbuffer->m_refcount++; + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_buffer_free(GBUFFER_ID * buffer_id) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + if(pbuffer->m_lock_count>0) return G_BUFFER_OP_INVALID; + if(pbuffer->m_refcount>0) pbuffer->m_refcount--; + if(pbuffer->m_refcount>0) return G_BUFFER_OP_STILLREFCOUNTED; + + buffer_id->m_bm_data->m_prototype->free_fn( + pbuffer->m_buffer_handle,pbuffer->m_size); + //destroy shadow buffer if needed + gim_buffer_free(&pbuffer->m_shadow_buffer); + // Obtain a free slot index for a new buffer + GIM_DYNARRAY_PUSH_ITEM(GUINT32,bm_data->m_free_positions,buffer_id->m_buffer_id); + pbuffer->m_buffer_handle = 0; + pbuffer->m_size = 0; + pbuffer->m_shadow_buffer.m_bm_data = 0; + pbuffer->m_shadow_buffer.m_buffer_id = G_UINT_INFINITY; + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_lock_buffer(GBUFFER_ID * buffer_id,int access,char ** map_pointer) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + if(pbuffer->m_lock_count>0) + { + if(pbuffer->m_access!=access) return G_BUFFER_OP_INVALID; + pbuffer->m_lock_count++; + *map_pointer = pbuffer->m_mapped_pointer; + return G_BUFFER_OP_SUCCESS; + } + + pbuffer->m_access = access; + + GUINT32 result; + if(pbuffer->m_usage==G_MU_STATIC_WRITE) + { + *map_pointer = 0;///no access + return G_BUFFER_OP_INVALID; + } + else if(pbuffer->m_usage==G_MU_STATIC_READ) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = *map_pointer; + pbuffer->m_lock_count++; + } + else + { + *map_pointer = 0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = *map_pointer; + pbuffer->m_lock_count++; + } + else if(pbuffer->m_access == G_MA_WRITE_ONLY) + { + pbuffer->m_mapped_pointer = buffer_id->m_bm_data->m_prototype->lock_buffer_fn( + pbuffer->m_buffer_handle,access); + *map_pointer = pbuffer->m_mapped_pointer; + pbuffer->m_lock_count++; + } + else if(pbuffer->m_access == G_MA_READ_WRITE) + { + *map_pointer = 0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE_COPY) + { + result = gim_lock_buffer(&pbuffer->m_shadow_buffer,access,map_pointer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = *map_pointer; + pbuffer->m_lock_count++; + } + else if(pbuffer->m_usage==G_MU_STATIC_WRITE_DYNAMIC_READ) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + pbuffer->m_mapped_pointer = buffer_id->m_bm_data->m_prototype->lock_buffer_fn( + pbuffer->m_buffer_handle,access); + *map_pointer = pbuffer->m_mapped_pointer; + pbuffer->m_lock_count++; + } + else + { + *map_pointer = 0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_DYNAMIC_READ_WRITE) + { + pbuffer->m_mapped_pointer = buffer_id->m_bm_data->m_prototype->lock_buffer_fn( + pbuffer->m_buffer_handle,access); + *map_pointer = pbuffer->m_mapped_pointer; + pbuffer->m_lock_count++; + } + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_unlock_buffer(GBUFFER_ID * buffer_id) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + if(pbuffer->m_lock_count==0) return G_BUFFER_OP_INVALID; + + if(pbuffer->m_lock_count>1) + { + pbuffer->m_lock_count--; + return G_BUFFER_OP_SUCCESS; + } + + + GUINT32 result; + if(pbuffer->m_usage==G_MU_STATIC_WRITE) + { + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + return G_BUFFER_OP_INVALID; + } + else if(pbuffer->m_usage==G_MU_STATIC_READ) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + result = gim_unlock_buffer(&pbuffer->m_shadow_buffer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + } + else + { + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + result = gim_unlock_buffer(&pbuffer->m_shadow_buffer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + } + else if(pbuffer->m_access == G_MA_WRITE_ONLY) + { + buffer_id->m_bm_data->m_prototype->unlock_buffer_fn( + pbuffer->m_buffer_handle); + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + } + else if(pbuffer->m_access == G_MA_READ_WRITE) + { + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_STATIC_READ_DYNAMIC_WRITE_COPY) + { + result = gim_unlock_buffer(&pbuffer->m_shadow_buffer); + if(result!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + if(pbuffer->m_access == G_MA_WRITE_ONLY||pbuffer->m_access == G_MA_READ_WRITE) + { + gim_copy_buffers(&pbuffer->m_shadow_buffer,0,buffer_id,0,pbuffer->m_size); + } + } + else if(pbuffer->m_usage==G_MU_STATIC_WRITE_DYNAMIC_READ) + { + if(pbuffer->m_access == G_MA_READ_ONLY) + { + buffer_id->m_bm_data->m_prototype->unlock_buffer_fn( + pbuffer->m_buffer_handle); + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + } + else + { + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + return G_BUFFER_OP_INVALID; + } + } + else if(pbuffer->m_usage==G_MU_DYNAMIC_READ_WRITE) + { + buffer_id->m_bm_data->m_prototype->unlock_buffer_fn( + pbuffer->m_buffer_handle); + pbuffer->m_mapped_pointer = 0; + pbuffer->m_lock_count=0; + } + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_get_buffer_size(GBUFFER_ID * buffer_id,GUINT32 * buffer_size) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + *buffer_size = pbuffer->m_size; + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_get_buffer_is_locked(GBUFFER_ID * buffer_id,GUINT32 * lock_count) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + *lock_count = pbuffer->m_lock_count; + return G_BUFFER_OP_SUCCESS; +} + + +GINT32 gim_download_from_buffer( + GBUFFER_ID * buffer_id, + GUINT32 source_pos, + void * destdata, + GUINT32 copysize) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + buffer_id->m_bm_data->m_prototype->download_from_buffer_fn( + pbuffer->m_buffer_handle,source_pos,destdata,copysize); + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_upload_to_buffer( + GBUFFER_ID * buffer_id, + GUINT32 dest_pos, + void * sourcedata, + GUINT32 copysize) +{ + VALIDATE_BUFFER_ID_PT(buffer_id) + buffer_id->m_bm_data->m_prototype->upload_to_buffer_fn( + pbuffer->m_buffer_handle,dest_pos,sourcedata,copysize); + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_copy_buffers( + GBUFFER_ID * source_buffer_id, + GUINT32 source_pos, + GBUFFER_ID * dest_buffer_id, + GUINT32 dest_pos, + GUINT32 copysize) +{ + GBUFFER_MANAGER_DATA * bm_data1,* bm_data2; + GBUFFER_DATA * pbuffer1, * pbuffer2; + void * tempdata; + if(_validate_buffer_id(source_buffer_id,&pbuffer1,&bm_data1)!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + + if(_validate_buffer_id(dest_buffer_id,&pbuffer2,&bm_data2)!= G_BUFFER_OP_SUCCESS) return G_BUFFER_OP_INVALID; + + if((bm_data1->m_buffer_manager_id == bm_data2->m_buffer_manager_id)|| + (bm_data1->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM && bm_data2->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED)|| + (bm_data1->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED && bm_data2->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM) + ) + {//smooth copy + bm_data1->m_prototype->copy_buffers_fn( + pbuffer1->m_buffer_handle,source_pos,pbuffer2->m_buffer_handle,dest_pos,copysize); + } + else if(bm_data1->m_buffer_manager_id == G_BUFFER_MANAGER_SYSTEM || + bm_data1->m_buffer_manager_id == G_BUFFER_MANAGER_SHARED) + { + //hard copy + tempdata = (void *)pbuffer1->m_buffer_handle; + //upload data + bm_data2->m_prototype->upload_to_buffer_fn( + pbuffer2->m_buffer_handle,dest_pos,tempdata,copysize); + } + else + { + //very hard copy + void * tempdata = gim_alloc(copysize); + //download data + bm_data1->m_prototype->download_from_buffer_fn( + pbuffer1->m_buffer_handle,source_pos,tempdata,copysize); + + //upload data + bm_data2->m_prototype->upload_to_buffer_fn( + pbuffer2->m_buffer_handle,dest_pos,tempdata,copysize); + //delete temp buffer + gim_free(tempdata,copysize); + } + return G_BUFFER_OP_SUCCESS; +} + +GINT32 gim_buffer_array_lock(GBUFFER_ARRAY * array_data, int access) +{ + if(array_data->m_buffer_data != 0) return G_BUFFER_OP_SUCCESS; + GINT32 result = gim_lock_buffer(&array_data->m_buffer_id,access,&array_data->m_buffer_data); + if(result!= G_BUFFER_OP_SUCCESS) return result; + array_data->m_buffer_data += array_data->m_byte_offset; + return result; +} + +GINT32 gim_buffer_array_unlock(GBUFFER_ARRAY * array_data) +{ + if(array_data->m_buffer_data == 0) return G_BUFFER_OP_SUCCESS; + GINT32 result = gim_unlock_buffer(&array_data->m_buffer_id); + if(result!= G_BUFFER_OP_SUCCESS) return result; + array_data->m_buffer_data = 0; + return result; +} + +void gim_buffer_array_copy_ref(GBUFFER_ARRAY * source_data,GBUFFER_ARRAY * dest_data) +{ + dest_data->m_buffer_id.m_buffer_id = source_data->m_buffer_id.m_buffer_id; + dest_data->m_buffer_id.m_bm_data = source_data->m_buffer_id.m_bm_data; + dest_data->m_buffer_data = 0; + dest_data->m_byte_stride = source_data->m_byte_stride; + dest_data->m_byte_offset = source_data->m_byte_offset; + dest_data->m_element_count = source_data->m_element_count; + gim_buffer_add_ref(&dest_data->m_buffer_id); +} + +void gim_buffer_array_copy_value(GBUFFER_ARRAY * source_data, + GBUFFER_MANAGER_DATA dest_buffer_managers[],GBUFFER_ARRAY * dest_data, + GUINT32 buffer_manager_id,int usage) +{ + //Create new buffer + GUINT32 buffsize = source_data->m_element_count*source_data->m_byte_stride; + gim_create_buffer(dest_buffer_managers,buffer_manager_id,buffsize,usage,&dest_data->m_buffer_id); + + //copy ref data + dest_data->m_buffer_data = 0; + dest_data->m_byte_stride = source_data->m_byte_stride; + dest_data->m_byte_offset = 0; + dest_data->m_element_count = source_data->m_element_count; + gim_buffer_add_ref(&dest_data->m_buffer_id); + //copy buffers + gim_copy_buffers(&source_data->m_buffer_id,source_data->m_byte_offset,&dest_data->m_buffer_id,0,buffsize); +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_tri_tri_overlap.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_tri_tri_overlap.cpp new file mode 100644 index 0000000..6a58ab4 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_tri_tri_overlap.cpp @@ -0,0 +1,251 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_trimesh.h" + + +#define FABS(x) (float(fabs(x))) /* implement as is fastest on your machine */ + +/* some macros */ + +#define CLASSIFY_TRIPOINTS_BY_FACE(v1,v2,v3,faceplane,out_of_face)\ +{ \ + _distances[0] = DISTANCE_PLANE_POINT(faceplane,v1);\ + _distances[1] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v2);\ + _distances[2] = _distances[0] * DISTANCE_PLANE_POINT(faceplane,v3); \ + if(_distances[1]>0.0f && _distances[2]>0.0f)\ + {\ + out_of_face = 1;\ + }\ + else\ + {\ + out_of_face = 0;\ + }\ +}\ + +/* sort so that a<=b */ +#define SORT(a,b) \ + if(a>b) \ + { \ + float c; \ + c=a; \ + a=b; \ + b=c; \ + } + + +/* this edge to edge test is based on Franlin Antonio's gem: + "Faster Line Segment Intersection", in Graphics Gems III, + pp. 199-202 */ +#define EDGE_EDGE_TEST(V0,U0,U1) \ + Bx=U0[i0]-U1[i0]; \ + By=U0[i1]-U1[i1]; \ + Cx=V0[i0]-U0[i0]; \ + Cy=V0[i1]-U0[i1]; \ + f=Ay*Bx-Ax*By; \ + d=By*Cx-Bx*Cy; \ + if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \ + { \ + e=Ax*Cy-Ay*Cx; \ + if(f>0) \ + { \ + if(e>=0 && e<=f) return 1; \ + } \ + else \ + { \ + if(e<=0 && e>=f) return 1; \ + } \ + } + +#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \ +{ \ + float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \ + Ax=V1[i0]-V0[i0]; \ + Ay=V1[i1]-V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U0,U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U1,U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U2,U0); \ +} + +#define POINT_IN_TRI(V0,U0,U1,U2) \ +{ \ + float a,b,c,d0,d1,d2; \ + /* is T1 completely inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + a=U1[i1]-U0[i1]; \ + b=-(U1[i0]-U0[i0]); \ + c=-a*U0[i0]-b*U0[i1]; \ + d0=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U2[i1]-U1[i1]; \ + b=-(U2[i0]-U1[i0]); \ + c=-a*U1[i0]-b*U1[i1]; \ + d1=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U0[i1]-U2[i1]; \ + b=-(U0[i0]-U2[i0]); \ + c=-a*U2[i0]-b*U2[i1]; \ + d2=a*V0[i0]+b*V0[i1]+c; \ + if(d0*d1>0.0) \ + { \ + if(d0*d2>0.0) return 1; \ + } \ +} + +int coplanar_tri_tri(GIM_TRIANGLE_DATA *tri1, + GIM_TRIANGLE_DATA *tri2) +{ + short i0,i1; + /* first project onto an axis-aligned plane, that maximizes the area */ + /* of the triangles, compute indices: i0,i1. */ + PLANE_MINOR_AXES(tri1->m_planes.m_planes[0], i0, i1); + + /* test all edges of triangle 1 against the edges of triangle 2 */ + EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[0],tri1->m_vertices[1],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]); + EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]); + EDGE_AGAINST_TRI_EDGES(tri1->m_vertices[2],tri1->m_vertices[0],tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2]); + + /* finally, test if tri1 is totally contained in tri2 or vice versa */ + POINT_IN_HULL(tri1->m_vertices[0],(&tri2->m_planes.m_planes[1]),3,i0); + if(i0==0) return 1; + + POINT_IN_HULL(tri2->m_vertices[0],(&tri1->m_planes.m_planes[1]),3,i0); + if(i0==0) return 1; + + return 0; +} + + + +#define NEWCOMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,A,B,C,X0,X1) \ +{ \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + A=VV2; B=(VV0-VV2)*D2; C=(VV1-VV2)*D2; X0=D2-D0; X1=D2-D1; \ + } \ + else if(D0D2>0.0f)\ + { \ + /* here we know that d0d1<=0.0 */ \ + A=VV1; B=(VV0-VV1)*D1; C=(VV2-VV1)*D1; X0=D1-D0; X1=D1-D2; \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + A=VV0; B=(VV1-VV0)*D0; C=(VV2-VV0)*D0; X0=D0-D1; X1=D0-D2; \ + } \ + else if(D1!=0.0f) \ + { \ + A=VV1; B=(VV0-VV1)*D1; C=(VV2-VV1)*D1; X0=D1-D0; X1=D1-D2; \ + } \ + else if(D2!=0.0f) \ + { \ + A=VV2; B=(VV0-VV2)*D2; C=(VV1-VV2)*D2; X0=D2-D0; X1=D2-D1; \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return coplanar_tri_tri(tri1,tri2); \ + } \ +}\ + + + +int gim_triangle_triangle_overlap( + GIM_TRIANGLE_DATA *tri1, + GIM_TRIANGLE_DATA *tri2) +{ + vec3f _distances; + char out_of_face; + CLASSIFY_TRIPOINTS_BY_FACE(tri1->m_vertices[0],tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_planes.m_planes[0],out_of_face); + if(out_of_face==1) return 0; + + CLASSIFY_TRIPOINTS_BY_FACE(tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2],tri1->m_planes.m_planes[0],out_of_face); + if(out_of_face==1) return 0; + + + float du0=0,du1=0,du2=0,dv0=0,dv1=0,dv2=0; + float D[3]; + float isect1[2], isect2[2]; + float du0du1=0,du0du2=0,dv0dv1=0,dv0dv2=0; + short index; + float vp0,vp1,vp2; + float up0,up1,up2; + float bb,cc,max; + + /* compute direction of intersection line */ + VEC_CROSS(D,tri1->m_planes.m_planes[0],tri2->m_planes.m_planes[0]); + + /* compute and index to the largest component of D */ + max=(float)FABS(D[0]); + index=0; + bb=(float)FABS(D[1]); + cc=(float)FABS(D[2]); + if(bb>max) max=bb,index=1; + if(cc>max) max=cc,index=2; + + /* this is the simplified projection onto L*/ + vp0= tri1->m_vertices[0][index]; + vp1= tri1->m_vertices[1][index]; + vp2= tri1->m_vertices[2][index]; + + up0= tri2->m_vertices[0][index]; + up1= tri2->m_vertices[1][index]; + up2= tri2->m_vertices[2][index]; + + /* compute interval for triangle 1 */ + float a,b,c,x0,x1; + NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1); + + /* compute interval for triangle 2 */ + float d,e,f,y0,y1; + NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1); + + float xx,yy,xxyy,tmp; + xx=x0*x1; + yy=y0*y1; + xxyy=xx*yy; + + tmp=a*xxyy; + isect1[0]=tmp+b*x1*yy; + isect1[1]=tmp+c*x0*yy; + + tmp=d*xxyy; + isect2[0]=tmp+e*xx*y1; + isect2[1]=tmp+f*xx*y0; + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + if(isect1[1] +#include "GIMPACT/gim_trimesh.h" + +GUINT32 gim_trimesh_get_triangle_count(GIM_TRIMESH * trimesh) +{ + return trimesh->m_tri_index_buffer.m_element_count/3; +} + +//! Creates the aabb set and the triangles cache +/*! + +\param trimesh +\param vertex_array +\param triindex_array +\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else it just is a reference to the original array. +\post it copies the arrays by reference, and creates the auxiliary data (m_aabbset,m_planes_cache_buffer) +*/ +void gim_trimesh_create_from_arrays(GBUFFER_MANAGER_DATA buffer_managers[], + GIM_TRIMESH * trimesh, GBUFFER_ARRAY * vertex_array, GBUFFER_ARRAY * triindex_array,char transformed_reply) +{ + assert(trimesh); + assert(vertex_array); + assert(triindex_array); + gim_buffer_array_copy_ref(vertex_array,&trimesh->m_source_vertex_buffer); + gim_buffer_array_copy_ref(triindex_array,&trimesh->m_tri_index_buffer); + + trimesh->m_mask = GIM_TRIMESH_NEED_UPDATE;//needs update + //Create the transformed vertices + if(transformed_reply==1) + { + trimesh->m_mask |= GIM_TRIMESH_TRANSFORMED_REPLY; + gim_buffer_array_copy_value(vertex_array, + buffer_managers,&trimesh->m_transformed_vertex_buffer,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE); + } + else + { + gim_buffer_array_copy_ref(vertex_array,&trimesh->m_transformed_vertex_buffer); + } + //create the box set + GUINT32 facecount = gim_trimesh_get_triangle_count(trimesh); + + gim_aabbset_alloc(&trimesh->m_aabbset,facecount); + //create the planes cache + GIM_DYNARRAY_CREATE_SIZED(GIM_TRIPLANES_CACHE,trimesh->m_planes_cache_buffer,facecount); + //Create the bitset + GIM_BITSET_CREATE_SIZED(trimesh->m_planes_cache_bitset,facecount); + //Callback is 0 + trimesh->m_update_callback = 0; + //set to identity + IDENTIFY_MATRIX_4X4(trimesh->m_transform); +} + + + +//! Create a trimesh from vertex array and an index array +/*! + +\param trimesh An uninitialized GIM_TRIMESH structure +\param vertex_array A buffer to a vec3f array +\param vertex_count +\param triindex_array +\param index_count +\param copy_vertices If 1, it copies the source vertices to another buffer. Else (0) it constructs a reference to the data. +\param copy_indices If 1, it copies the source vertices to another buffer. Else (0) it constructs a reference to the data. +\param transformed_reply If 1, then the m_transformed_vertices is a reply of the source vertices. Else (0) it just is a reference to the original array. +*/ +void gim_trimesh_create_from_data(GBUFFER_MANAGER_DATA buffer_managers[], + GIM_TRIMESH * trimesh, vec3f * vertex_array, GUINT32 vertex_count,char copy_vertices, + GUINT32 * triindex_array, GUINT32 index_count,char copy_indices,char transformed_reply) +{ + GBUFFER_ARRAY buffer_vertex_array; + GBUFFER_ARRAY buffer_triindex_array; + + //Create vertices + if(copy_vertices == 1) + { + gim_create_common_buffer_from_data(buffer_managers, + vertex_array, vertex_count*sizeof(vec3f), &buffer_vertex_array.m_buffer_id); + } + else//Create a shared buffer + { + gim_create_shared_buffer_from_data(buffer_managers, + vertex_array, vertex_count*sizeof(vec3f), &buffer_vertex_array.m_buffer_id); + } + GIM_BUFFER_ARRAY_INIT_TYPE(vec3f,buffer_vertex_array,buffer_vertex_array.m_buffer_id,vertex_count); + + + //Create vertices + if(copy_indices == 1) + { + gim_create_common_buffer_from_data(buffer_managers, + triindex_array, index_count*sizeof(GUINT32), &buffer_triindex_array.m_buffer_id); + } + else//Create a shared buffer + { + gim_create_shared_buffer_from_data(buffer_managers, + triindex_array, index_count*sizeof(GUINT32), &buffer_triindex_array.m_buffer_id); + } + GIM_BUFFER_ARRAY_INIT_TYPE(GUINT32,buffer_triindex_array,buffer_triindex_array.m_buffer_id,index_count); + + gim_trimesh_create_from_arrays(buffer_managers, trimesh, + &buffer_vertex_array, &buffer_triindex_array,transformed_reply); + + ///always call this after create a buffer_array + GIM_BUFFER_ARRAY_DESTROY(buffer_vertex_array); + GIM_BUFFER_ARRAY_DESTROY(buffer_triindex_array); +} + +//! Clears auxiliary data and releases buffer arrays +void gim_trimesh_destroy(GIM_TRIMESH * trimesh) +{ + gim_aabbset_destroy(&trimesh->m_aabbset); + + GIM_DYNARRAY_DESTROY(trimesh->m_planes_cache_buffer); + GIM_DYNARRAY_DESTROY(trimesh->m_planes_cache_bitset); + + GIM_BUFFER_ARRAY_DESTROY(trimesh->m_transformed_vertex_buffer); + GIM_BUFFER_ARRAY_DESTROY(trimesh->m_source_vertex_buffer); + GIM_BUFFER_ARRAY_DESTROY(trimesh->m_tri_index_buffer); +} + +//! Copies two meshes +/*! +\pre dest_trimesh shouldn't be created +\post dest_trimesh will be created +\param source_trimesh +\param dest_trimesh +\param copy_by_reference If 1, it attaches a reference to the source vertices, else it copies the vertices +\param transformed_reply IF 1, then it forces the m_trasnformed_vertices to be a reply of the source vertices +*/ +void gim_trimesh_copy(GIM_TRIMESH * source_trimesh, + GBUFFER_MANAGER_DATA dest_buffer_managers[], GIM_TRIMESH * dest_trimesh, + char copy_by_reference, char transformed_reply) +{ +/* -- trimesh can not be copied by reference until GBUFFER_MANAGER_DATA is rewritten + to be thread safe and until it is moved back to global variables. + if(copy_by_reference==1) + { + gim_trimesh_create_from_arrays(dest_trimesh, &source_trimesh->m_source_vertex_buffer, &source_trimesh->m_tri_index_buffer,transformed_reply); + } + else +*/ + { + GBUFFER_ARRAY buffer_vertex_array; + GBUFFER_ARRAY buffer_triindex_array; + + gim_buffer_array_copy_value(&source_trimesh->m_source_vertex_buffer, + dest_buffer_managers,&buffer_vertex_array,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE); + + gim_buffer_array_copy_value(&source_trimesh->m_tri_index_buffer, + dest_buffer_managers,&buffer_triindex_array,G_BUFFER_MANAGER_SYSTEM,G_MU_DYNAMIC_READ_WRITE); + + gim_trimesh_create_from_arrays(dest_buffer_managers, dest_trimesh, + &buffer_vertex_array, &buffer_triindex_array,transformed_reply); + + ///always call this after create a buffer_array + GIM_BUFFER_ARRAY_DESTROY(buffer_vertex_array); + GIM_BUFFER_ARRAY_DESTROY(buffer_triindex_array); + } +} +//! Locks the trimesh for working with it +/*! +\post locks m_tri_index_buffer and m_transformed_vertex_buffer. +\param trimesh +*/ +void gim_trimesh_locks_work_data(GIM_TRIMESH * trimesh) +{ + GINT32 res; + res=gim_buffer_array_lock(&trimesh->m_tri_index_buffer,G_MA_READ_ONLY); + assert(res==G_BUFFER_OP_SUCCESS); + res=gim_buffer_array_lock(&trimesh->m_transformed_vertex_buffer,G_MA_READ_ONLY); + assert(res==G_BUFFER_OP_SUCCESS); +} + +//! unlocks the trimesh +/*! +\post unlocks m_tri_index_buffer and m_transformed_vertex_buffer. +\param trimesh +*/ +void gim_trimesh_unlocks_work_data(GIM_TRIMESH * trimesh) +{ + gim_buffer_array_unlock(&trimesh->m_tri_index_buffer); + gim_buffer_array_unlock(&trimesh->m_transformed_vertex_buffer); +} + + +//! Returns 1 if the m_transformed_vertex_buffer is a reply of m_source_vertex_buffer +char gim_trimesh_has_tranformed_reply(GIM_TRIMESH * trimesh) +{ + if(trimesh->m_mask&GIM_TRIMESH_TRANSFORMED_REPLY) return 1; + return 0; +} + +//! Returns 1 if the trimesh needs to update their aabbset and the planes cache. +char gim_trimesh_needs_update(GIM_TRIMESH * trimesh) +{ + if(trimesh->m_mask&GIM_TRIMESH_NEED_UPDATE) return 1; + return 0; +} + +//! Change the state of the trimesh for force it to update +/*! +Call it after having made changes to the trimesh. +\post gim_trimesh_need_update(trimesh) will return 1 +*/ +void gim_trimesh_post_update(GIM_TRIMESH * trimesh) +{ + trimesh->m_mask |= GIM_TRIMESH_NEED_UPDATE; +} + +//kernel +#define MULT_MAT_VEC4_KERNEL(_mat,_src,_dst) MAT_DOT_VEC_3X4((_dst),(_mat),(_src)) + +//! Updates m_transformed_vertex_buffer +/*! +\pre m_transformed_vertex_buffer must be unlocked +*/ +void gim_trimesh_update_vertices(GIM_TRIMESH * trimesh) +{ + if(gim_trimesh_has_tranformed_reply(trimesh) == 0) return; //Don't perform transformation + + //Vertices + GBUFFER_ARRAY * psource_vertex_buffer = &trimesh->m_source_vertex_buffer; + GBUFFER_ARRAY * ptransformed_vertex_buffer = &trimesh->m_transformed_vertex_buffer; + //Temp transform + mat4f transform; + COPY_MATRIX_4X4(transform,trimesh->m_transform); + + GIM_PROCESS_BUFFER_ARRAY(transform,(*psource_vertex_buffer),(*ptransformed_vertex_buffer),MULT_MAT_VEC4_KERNEL,vec3f,vec3f); +} + +//! Updates m_aabbset and m_planes_cache_bitset +/*! +\pre gim_trimesh_locks_work_data must be called before +*/ +void gim_trimesh_update_aabbset(GIM_TRIMESH * trimesh) +{ + vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0); + assert(transformed_vertices); + + GUINT32 * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT32,trimesh->m_tri_index_buffer,0); + assert(triangle_indices); + // box set + aabb3f * paabb = trimesh->m_aabbset.m_boxes; + GUINT32 triangle_count = gim_trimesh_get_triangle_count(trimesh); + float * v1,*v2,*v3; + GUINT32 i; + for (i=0; im_planes_cache_bitset); + //Sorts set + gim_aabbset_update(&trimesh->m_aabbset); +} + +//! Updates the trimesh if needed +/*! +\post If gim_trimesh_needs_update returns 1, then it calls gim_trimesh_update_vertices and gim_trimesh_update_aabbset +*/ +void gim_trimesh_update(GIM_TRIMESH * trimesh) +{ + if(gim_trimesh_needs_update(trimesh)==0) return; + gim_trimesh_update_vertices(trimesh); + gim_trimesh_locks_work_data(trimesh); + gim_trimesh_update_aabbset(trimesh); + gim_trimesh_unlocks_work_data(trimesh); + + //Clear update flag + trimesh->m_mask &= ~GIM_TRIMESH_NEED_UPDATE; +} + +void gim_trimesh_set_tranform(GIM_TRIMESH * trimesh, mat4f transform) +{ + GREAL diff = 0.0f; + float * originaltrans = &trimesh->m_transform[0][0]; + float * newtrans = &transform[0][0]; + GUINT32 i; + for (i=0;i<16;i++) + { + diff += fabs(originaltrans[i]-newtrans[i]); + } + +// if(IS_ZERO(diff)) return ;///don't need to update + if(diff< 0.00001f) return ;///don't need to update + + COPY_MATRIX_4X4(trimesh->m_transform,transform); + + gim_trimesh_post_update(trimesh); +} + +void gim_trimesh_get_triangle_data(GIM_TRIMESH * trimesh, GUINT32 triangle_index, GIM_TRIANGLE_DATA * tri_data) +{ + vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0); + + GUINT32 * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT32,trimesh->m_tri_index_buffer,triangle_index*3); + + + //Copy the vertices + VEC_COPY(tri_data->m_vertices[0],transformed_vertices[triangle_indices[0]]); + VEC_COPY(tri_data->m_vertices[1],transformed_vertices[triangle_indices[1]]); + VEC_COPY(tri_data->m_vertices[2],transformed_vertices[triangle_indices[2]]); + + //Get the planes + GIM_TRIPLANES_CACHE * planes = GIM_DYNARRAY_POINTER(GIM_TRIPLANES_CACHE,trimesh->m_planes_cache_buffer); + planes += triangle_index; + + //verify planes cache + GUINT32 bit_eval; + GIM_BITSET_GET(trimesh->m_planes_cache_bitset,triangle_index,bit_eval); + if(bit_eval == 0)// Needs to calc the planes + { + //Calc the face plane + TRIANGLE_PLANE(tri_data->m_vertices[0],tri_data->m_vertices[1],tri_data->m_vertices[2],planes->m_planes[0]); + //Calc the edge 1 + EDGE_PLANE(tri_data->m_vertices[0],tri_data->m_vertices[1],(planes->m_planes[0]),(planes->m_planes[1])); + + //Calc the edge 2 + EDGE_PLANE(tri_data->m_vertices[1],tri_data->m_vertices[2],(planes->m_planes[0]),(planes->m_planes[2])); + + //Calc the edge 3 + EDGE_PLANE(tri_data->m_vertices[2],tri_data->m_vertices[0],(planes->m_planes[0]), (planes->m_planes[3])); + + //mark + GIM_BITSET_SET(trimesh->m_planes_cache_bitset,triangle_index); + } + + + VEC_COPY_4((tri_data->m_planes.m_planes[0]),(planes->m_planes[0]));//face plane + VEC_COPY_4((tri_data->m_planes.m_planes[1]),(planes->m_planes[1]));//edge1 + VEC_COPY_4((tri_data->m_planes.m_planes[2]),(planes->m_planes[2]));//edge2 + VEC_COPY_4((tri_data->m_planes.m_planes[3]),(planes->m_planes[3]));//edge3 +} + +void gim_trimesh_get_triangle_vertices(GIM_TRIMESH * trimesh, GUINT32 triangle_index, vec3f v1, vec3f v2, vec3f v3) +{ + vec3f * transformed_vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0); + + GUINT32 * triangle_indices = GIM_BUFFER_ARRAY_POINTER(GUINT32,trimesh->m_tri_index_buffer,triangle_index*3); + + //Copy the vertices + if (v1 != NULL) + { + VEC_COPY(v1,transformed_vertices[triangle_indices[0]]); + } + + if (v2 != NULL) + { + VEC_COPY(v2,transformed_vertices[triangle_indices[1]]); + } + + if (v3 != NULL) + { + VEC_COPY(v3,transformed_vertices[triangle_indices[2]]); + } +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_capsule_collision.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_capsule_collision.cpp new file mode 100644 index 0000000..32789d2 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_capsule_collision.cpp @@ -0,0 +1,285 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_trimesh.h" + +//! Utility function to find the closest point between a segment and a triangle +/*! + +\param triangle +\param s1 +\param s2 +\param contacts Contains the closest points on the segment (1,2), and the normal points to segment, and m_depth contains the distance + +\post The contacts array is not set to 0. It adds additional contacts +*/ +void gim_closest_point_triangle_segment(GIM_TRIANGLE_DATA * triangle, vec3f s1,vec3f s2, GDYNAMIC_ARRAY * contacts) +{ + vec3f segment_points[4] = {{0}}; + vec3f closest_points[2] = {{0}}; + GUINT32 intersection_type, out_edge= 10; + GREAL dis, dis_temp,perpend; + vec4f sdiff; + + dis = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s1); + dis_temp = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],s2); + + if(dis<=0.0f && dis_temp<=0.0f) return; + + VEC_DIFF(sdiff,s2,s1); + perpend = VEC_DOT(sdiff,triangle->m_planes.m_planes[0]); + + if(!IS_ZERO(perpend)) // Not perpendicular + { + if(dis=0.0f && dis_temp>=0.0f) + { + POINT_IN_HULL(closest_points[0],(&triangle->m_planes.m_planes[1]),3,out_edge); + + if(out_edge==0)//Point over face + { + GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0); + return; + } + } + else + { + + PLANE_CLIP_SEGMENT(s1,s2,triangle->m_planes.m_planes[0],closest_points[1]); + + POINT_IN_HULL(closest_points[1],(&triangle->m_planes.m_planes[1]),3,out_edge); + + if(out_edge==0)//Point over face + { + GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0); + return; + } + } + + } + else // Perpendicular Face + { + //out_edge=10 + //Clip segment by triangle + // Edge1 + PLANE_CLIP_SEGMENT_CLOSEST(s1,s2,triangle->m_planes.m_planes[1],segment_points[0],segment_points[1],intersection_type); + if(intersection_type==0||intersection_type==1) + { + out_edge = 0; + VEC_COPY(closest_points[0],segment_points[0]); + } + else + { + //Edge2 + PLANE_CLIP_SEGMENT_CLOSEST(segment_points[0],segment_points[1],triangle->m_planes.m_planes[2],segment_points[2],segment_points[3],intersection_type); + if(intersection_type==0||intersection_type==1) + { + out_edge = 1; + VEC_COPY(closest_points[0],segment_points[3]); + } + else + { + //Edge3 + PLANE_CLIP_SEGMENT_CLOSEST(segment_points[2],segment_points[3],triangle->m_planes.m_planes[3],closest_points[0],closest_points[1],intersection_type); + if(intersection_type==0||intersection_type==1) + { + out_edge = 2; + } + } + } + //POST closest_points[0] and closest_points[1] are inside the triangle, if out_edge>2 + if(out_edge>2) // Over triangle + { + dis = DISTANCE_PLANE_POINT(triangle->m_planes.m_planes[0],closest_points[0]); + GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0); + GIM_PUSH_CONTACT((*contacts),closest_points[1] ,triangle->m_planes.m_planes[0] ,dis,0, 0, 0,0); + return; + } + } + + //Find closest edges + out_edge = 10; + dis = G_REAL_INFINITY; + GUINT32 i; + for(i=0;i<3;i++) + { + SEGMENT_COLLISION(s1,s2,triangle->m_vertices[i],triangle->m_vertices[(i+1)%3],segment_points[0],segment_points[1]); + VEC_DIFF(sdiff,segment_points[0],segment_points[1]); + dis_temp = VEC_DOT(sdiff,sdiff); + if(dis_temp< dis) + { + dis = dis_temp; + out_edge = i; + VEC_COPY(closest_points[0],segment_points[0]); + VEC_COPY(closest_points[1],sdiff);//normal + } + } + if(out_edge>2) return ;// ???? ASSERT this please + + if(IS_ZERO(dis)) + { + //Set face plane + GIM_PUSH_CONTACT((*contacts),closest_points[0] ,triangle->m_planes.m_planes[0] ,0.0f,0, 0, 0,0); + + } + else + { + GIM_SQRT(dis,dis); + VEC_SCALE(closest_points[1],(1.0f/dis),closest_points[1]);//normal + GIM_PUSH_CONTACT((*contacts),closest_points[0] ,closest_points[1],dis,0, 0, 0,0); + } +} + + +//! Utility function to find the closest point between a capsule and a triangle +/*! + +\param triangle +\param capsule +\param contacts Contains the closest points on the capsule, and the normal points to triangle + +\post The contacts array is not set to 0. It adds additional contacts +*/ +int gim_triangle_capsule_collision(GIM_TRIANGLE_DATA * triangle, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts) +{ + GUINT32 old_contact_size = contacts->m_size; + gim_closest_point_triangle_segment(triangle,capsule->m_point1,capsule->m_point2,contacts); + + if (contacts->m_size == old_contact_size) + { + return 0; + } + + GIM_CONTACT * pcontact = GIM_DYNARRAY_POINTER(GIM_CONTACT ,(*contacts)); + pcontact+= old_contact_size; + + if(pcontact->m_depth > capsule->m_radius) + { + contacts->m_size = old_contact_size; + return 0; + } + + vec3f vec; + while(old_contact_sizem_size) + { + //Scale the normal for pointing to triangle + VEC_SCALE(pcontact->m_normal,-1.0f,pcontact->m_normal); + //Fix the contact point + VEC_SCALE(vec,capsule->m_radius,pcontact->m_normal); + VEC_SUM(pcontact->m_point,vec,pcontact->m_point); + //Fix the depth + pcontact->m_depth = capsule->m_radius - pcontact->m_depth; + + pcontact++; + old_contact_size++; + } + + return 1; +} + + +//! Trimesh Capsule collision +/*! +Finds the closest primitive collided by the ray +\param trimesh +\param capsule +\param contact +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_capsule_collision(GIM_TRIMESH * trimesh, GIM_CAPSULE_DATA * capsule, GDYNAMIC_ARRAY * contacts) +{ + contacts->m_size = 0; + + aabb3f test_aabb; + CALC_CAPSULE_AABB((*capsule),test_aabb); + + GDYNAMIC_ARRAY collision_result; + GIM_CREATE_BOXQUERY_LIST(collision_result); + + gim_aabbset_box_collision(&test_aabb, &trimesh->m_aabbset , &collision_result); + + if(collision_result.m_size==0) + { + GIM_DYNARRAY_DESTROY(collision_result); + } + + //collide triangles + //Locks trimesh + gim_trimesh_locks_work_data(trimesh); + //dummy contacts + GDYNAMIC_ARRAY dummycontacts; + GIM_CREATE_CONTACT_LIST(dummycontacts); + + int cresult; + unsigned int i; + GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result); + GIM_TRIANGLE_DATA tri_data; + GUINT32 old_contact_size; + GIM_CONTACT * pcontact; + + for(i=0;im_handle1 = trimesh; + pcontact->m_handle2 = capsule; + pcontact->m_feature1 = boxesresult[i]; + pcontact->m_feature2 = 0; + pcontact++; + old_contact_size++; + } + } + } + ///unlocks + gim_trimesh_unlocks_work_data(trimesh); + ///Destroy box result + GIM_DYNARRAY_DESTROY(collision_result); + + //merge contacts + gim_merge_contacts(&dummycontacts,contacts); + + //Destroy dummy + GIM_DYNARRAY_DESTROY(dummycontacts); +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_ray_collision.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_ray_collision.cpp new file mode 100644 index 0000000..2466b11 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_ray_collision.cpp @@ -0,0 +1,149 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_trimesh.h" + + +//! Trimesh Ray Collisions +/*! + +\param trimesh +\param contact +\return 1 if the ray collides, else 0 +*/ +int gim_trimesh_ray_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact) +{ + GDYNAMIC_ARRAY collision_result; + GIM_CREATE_BOXQUERY_LIST(collision_result); + + gim_aabbset_ray_collision(origin,dir,tmax,&trimesh->m_aabbset,&collision_result); + + if(collision_result.m_size==0) + { + GIM_DYNARRAY_DESTROY(collision_result); + return 0; + } + + //collide triangles + + GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result); + GIM_TRIANGLE_DATA tridata; + vec3f pout; + GREAL tparam,u,v; + char does_intersect; + + gim_trimesh_locks_work_data(trimesh); + + for(unsigned int i=0;itparam = tparam; + contact->u = u; + contact->v = v; + contact->m_face_id = boxesresult[i]; + VEC_COPY(contact->m_point,pout); + VEC_COPY(contact->m_normal,flippedPlane); + + gim_trimesh_unlocks_work_data(trimesh); + GIM_DYNARRAY_DESTROY(collision_result); + return 1; + } + } + + gim_trimesh_unlocks_work_data(trimesh); + GIM_DYNARRAY_DESTROY(collision_result); + return 0;//no collisiion +} + + +//! Trimesh Ray Collisions closest +/*! +Finds the closest primitive collided by the ray +\param trimesh +\param contact +\return 1 if the ray collides, else 0 +*/ +int gim_trimesh_ray_closest_collision(GIM_TRIMESH * trimesh,vec3f origin,vec3f dir, GREAL tmax, GIM_TRIANGLE_RAY_CONTACT_DATA * contact) +{ + GDYNAMIC_ARRAY collision_result; + GIM_CREATE_BOXQUERY_LIST(collision_result); + + gim_aabbset_ray_collision(origin,dir,tmax,&trimesh->m_aabbset,&collision_result); + + if(collision_result.m_size==0) + { + GIM_DYNARRAY_DESTROY(collision_result); + return 0; + } + + //collide triangles + + GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result); + GIM_TRIANGLE_DATA tridata; + vec3f pout; + GREAL tparam,u,v; + char does_intersect; + contact->tparam = tmax + 0.1f; + + gim_trimesh_locks_work_data(trimesh); + + for(unsigned int i=0;itparam)) + { + contact->tparam = tparam; + contact->u = u; + contact->v = v; + contact->m_face_id = boxesresult[i]; + VEC_COPY(contact->m_point,pout); + VEC_COPY(contact->m_normal,flippedPlane); + } + } + + gim_trimesh_unlocks_work_data(trimesh); + GIM_DYNARRAY_DESTROY(collision_result); + if(contact->tparam > tmax) return 0; + return 1; +} diff --git a/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_sphere_collision.cpp b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_sphere_collision.cpp new file mode 100644 index 0000000..37ff8f6 --- /dev/null +++ b/thirdparty/ode-0.16.5/GIMPACT/src/gim_trimesh_sphere_collision.cpp @@ -0,0 +1,196 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + + 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 files + GIMPACT-LICENSE-LGPL.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "GIMPACT/gim_trimesh.h" + +int gim_triangle_sphere_collision( + GIM_TRIANGLE_DATA *tri, + vec3f center, GREAL radius, + GIM_TRIANGLE_CONTACT_DATA * contact_data) +{ + contact_data->m_point_count = 0; + + //Find Face plane distance + GREAL dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[0],center); + if(dis>radius) return 0; //out + if(dis<-radius) return 0;//Out of triangle + contact_data->m_penetration_depth = dis; + + //Find the most edge + GUINT32 most_edge = 4;//no edge + GREAL max_dis = 0.0f; + dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[1],center); + if(dis>radius) return 0;//Out of triangle + if(dis>0.0f) + { + max_dis = dis; + most_edge = 0; + } + + dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[2],center); + if(dis>radius) return 0;//Out of triangle + if(dis>max_dis)// && dis>0.0f) + { + max_dis = dis; + most_edge = 1; + } + + dis = DISTANCE_PLANE_POINT(tri->m_planes.m_planes[3],center); + if(dis>radius) return 0;//Out of triangle + if(dis>max_dis)// && dis>0.0f) + { + max_dis = dis; + most_edge = 2; + } + + if(most_edge == 4) //Box is into triangle + { + //contact_data->m_penetration_depth = dis is set above + //Find Face plane point + VEC_COPY(contact_data->m_separating_normal,tri->m_planes.m_planes[0]); + //Find point projection on plane + if(contact_data->m_penetration_depth>=0.0f) + { + VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal); + } + else + { + VEC_SCALE(contact_data->m_points[0],radius,contact_data->m_separating_normal); + } + contact_data->m_penetration_depth = radius - contact_data->m_penetration_depth; + + VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center); + //Scale normal for pointing to triangle + VEC_SCALE(contact_data->m_separating_normal,-1.0f,contact_data->m_separating_normal); + contact_data->m_point_count = 1; + return 1; + } + //find the edge + vec3f e1,e2; + VEC_COPY(e1,tri->m_vertices[most_edge]); + VEC_COPY(e2,tri->m_vertices[(most_edge+1)%3]); + + CLOSEST_POINT_ON_SEGMENT(contact_data->m_points[0],center,e1,e2); + //find distance + VEC_DIFF(e1,center,contact_data->m_points[0]); + VEC_LENGTH(e1,dis); + if(dis>radius) return 0; + + contact_data->m_penetration_depth = radius - dis; + + if(IS_ZERO(dis)) + { + VEC_COPY(contact_data->m_separating_normal,tri->m_planes.m_planes[most_edge+1]); + VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal); + VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center); + } + else + { + VEC_SCALE(contact_data->m_separating_normal,1.0f/dis,e1); + VEC_SCALE(contact_data->m_points[0],-radius,contact_data->m_separating_normal); + VEC_SUM(contact_data->m_points[0],contact_data->m_points[0],center); + } + + //Scale normal for pointing to triangle + VEC_SCALE(contact_data->m_separating_normal,-1.0f,contact_data->m_separating_normal); + + contact_data->m_point_count = 1; + return 1; + +} + +//! Trimesh Sphere Collisions +/*! +In each contact +
      +
    • m_handle1 points to trimesh. +
    • m_handle2 points to NULL. +
    • m_feature1 Is a triangle index of trimesh. +
    + +\param trimesh +\param center +\param radius +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_sphere_collision(GIM_TRIMESH * trimesh,vec3f center,GREAL radius, GDYNAMIC_ARRAY * contacts) +{ + contacts->m_size = 0; + + aabb3f test_aabb; + test_aabb.minX = center[0]-radius; + test_aabb.maxX = center[0]+radius; + test_aabb.minY = center[1]-radius; + test_aabb.maxY = center[1]+radius; + test_aabb.minZ = center[2]-radius; + test_aabb.maxZ = center[2]+radius; + + GDYNAMIC_ARRAY collision_result; + GIM_CREATE_BOXQUERY_LIST(collision_result); + + gim_aabbset_box_collision(&test_aabb, &trimesh->m_aabbset , &collision_result); + + if(collision_result.m_size==0) + { + GIM_DYNARRAY_DESTROY(collision_result); + } + + //collide triangles + //Locks trimesh + gim_trimesh_locks_work_data(trimesh); + //dummy contacts + GDYNAMIC_ARRAY dummycontacts; + GIM_CREATE_CONTACT_LIST(dummycontacts); + + int cresult; + unsigned int i; + GUINT32 * boxesresult = GIM_DYNARRAY_POINTER(GUINT32,collision_result); + GIM_TRIANGLE_CONTACT_DATA tri_contact_data; + GIM_TRIANGLE_DATA tri_data; + + for(i=0;i0.0f && _distances[2]>0.0f)\ + {\ + out_of_face = 1;\ + }\ + else\ + {\ + out_of_face = 0;\ + }\ +}\ + + +//! Receives the 3 edge planes +#define MOST_DEEP_POINTS(plane,points,point_count,deep_points,deep_points_count,maxdeep)\ +{\ + maxdeep=-1000.0f;\ + GUINT32 _k;\ + GREAL _dist;\ + deep_points_count = 0;\ + for(_k=0;_kmaxdeep)\ + {\ + maxdeep = _dist;\ + _max_candidates[0] = _k;\ + deep_points_count=1;\ + }\ + else if((_dist+G_EPSILON)>=maxdeep)\ + {\ + _max_candidates[deep_points_count] = _k;\ + deep_points_count++;\ + }\ + }\ + if(maxdeep<0.0f)\ + {\ + deep_points_count = 0;\ + }\ + else\ + {\ + for(_k=0;_k0)\ + {\ + _temp_clip_count2 = 0;\ + PLANE_CLIP_POLYGON(tri_edge_planes[1],_temp_clip,_temp_clip_count,_temp_clip2,_temp_clip_count2,MAX_TRI_CLIPPING);\ + if(_temp_clip_count2>0)\ + {\ + PLANE_CLIP_POLYGON(tri_edge_planes[2],_temp_clip2,_temp_clip_count2,clipped_points,clipped_point_count,MAX_TRI_CLIPPING);\ + }\ + }\ +}\ + + + +int _gim_triangle_triangle_collision( + GIM_TRIANGLE_DATA *tri1, + GIM_TRIANGLE_DATA *tri2, + GIM_TRIANGLE_CONTACT_DATA * contact_data) +{ + //Cache variables for triangle intersection + GUINT32 _max_candidates[MAX_TRI_CLIPPING]; + vec3f _temp_clip[MAX_TRI_CLIPPING]; + GUINT32 _temp_clip_count = 0; + vec3f _temp_clip2[MAX_TRI_CLIPPING]; + GUINT32 _temp_clip_count2 = 0; + vec3f clipped_points2[MAX_TRI_CLIPPING]; + vec3f deep_points2[MAX_TRI_CLIPPING]; + vec3f clipped_points1[MAX_TRI_CLIPPING]; + vec3f deep_points1[MAX_TRI_CLIPPING]; + + + + //State variables + GUINT32 mostdir=0; + GUINT32 clipped2_count=0; + + //Clip tri2 by tri1 edges + + CLIP_TRI_POINTS_BY_TRI_EDGE_PLANES(tri2->m_vertices,(&tri1->m_planes.m_planes[1]), clipped_points2, clipped2_count); + + if(clipped2_count == 0 ) + { + return 0;//Reject + } + + //find deepest interval face1 + GUINT32 deep2_count=0; + + GREAL maxdeep; + + MOST_DEEP_POINTS((tri1->m_planes.m_planes[0]), clipped_points2, clipped2_count, deep_points2, deep2_count, maxdeep); + if(deep2_count==0) + { +// *perror = 0.0f; + return 0;//Reject + } + + //Normal pointing to triangle1 + VEC_SCALE(contact_data->m_separating_normal,-1.0f,(tri1->m_planes.m_planes[0])); + + + //Clip tri1 by tri2 edges + + GUINT32 clipped1_count=0; + + CLIP_TRI_POINTS_BY_TRI_EDGE_PLANES(tri1->m_vertices,(&tri2->m_planes.m_planes[1]), clipped_points1, clipped1_count); + + if(clipped2_count == 0 ) + { +// *perror = 0.0f; + return 0;//Reject + } + + + //find interval face2 + GUINT32 deep1_count=0; + + GREAL dist; + + MOST_DEEP_POINTS((tri2->m_planes.m_planes[0]), clipped_points1, clipped1_count, deep_points1, deep1_count, dist); + + if(deep1_count==0) + { +// *perror = 0.0f; + return 0; + } + + if(distm_separating_normal,(tri2->m_planes.m_planes[0])); + } + //set deep + contact_data->m_penetration_depth = maxdeep; + + ////check most dir for contacts + if(mostdir==0) + { + contact_data->m_point_count = deep2_count; + for(mostdir=0;mostdirm_points[mostdir] ,deep_points2[mostdir]); + } + } + else + { + contact_data->m_point_count = deep1_count; + for(mostdir=0;mostdirm_points[mostdir] ,deep_points1[mostdir]); + } + } + return 1; +} + + + +//! Finds the contact points from a collision of two triangles +/*! +Returns the contact points, the penetration depth and the separating normal of the collision +between two triangles. The normal is pointing toward triangle 1 from triangle 2 +*/ +int gim_triangle_triangle_collision( + GIM_TRIANGLE_DATA *tri1, + GIM_TRIANGLE_DATA *tri2, + GIM_TRIANGLE_CONTACT_DATA * contact_data) +{ + vec3f _distances; + char out_of_face=0; + + CLASSIFY_TRI_BY_FACE(tri1->m_vertices[0],tri1->m_vertices[1],tri1->m_vertices[2],tri2->m_planes.m_planes[0],out_of_face); + if(out_of_face==1) return 0; + + CLASSIFY_TRI_BY_FACE(tri2->m_vertices[0],tri2->m_vertices[1],tri2->m_vertices[2],tri1->m_planes.m_planes[0],out_of_face); + if(out_of_face==1) return 0; + + return _gim_triangle_triangle_collision(tri1,tri2,contact_data); +} + +//! Trimesh Trimesh Collisions +/*! + +In each contact +
      +
    • m_handle1 points to trimesh1. +
    • m_handle2 points to trimesh2. +
    • m_feature1 Is a triangle index of trimesh1. +
    • m_feature2 Is a triangle index of trimesh2. +
    + +\param trimesh1 Collider +\param trimesh2 Collidee +\param contacts A GIM_CONTACT array. Must be initialized +*/ +void gim_trimesh_trimesh_collision(GIM_TRIMESH * trimesh1, GIM_TRIMESH * trimesh2, GDYNAMIC_ARRAY * contacts) +{ + contacts->m_size = 0; + GDYNAMIC_ARRAY collision_pairs; + GIM_CREATE_PAIR_SET(collision_pairs) + + gim_aabbset_bipartite_intersections(&trimesh1->m_aabbset,&trimesh2->m_aabbset,&collision_pairs); + + if(collision_pairs.m_size==0) + { + GIM_DYNARRAY_DESTROY(collision_pairs); + return; //no collisioin + } + + //Locks meshes + gim_trimesh_locks_work_data(trimesh1); + gim_trimesh_locks_work_data(trimesh2); + + + //pair pointer + GIM_PAIR *pairs = GIM_DYNARRAY_POINTER(GIM_PAIR,collision_pairs); + //dummy contacts + GDYNAMIC_ARRAY dummycontacts; + GIM_CREATE_CONTACT_LIST(dummycontacts); + + //Auxiliary triangle data + GIM_TRIANGLE_CONTACT_DATA tri_contact_data; + GIM_TRIANGLE_DATA tri1data,tri2data; + + + GUINT32 i, ti1,ti2,ci; + int colresult; + for (i=0;im_size = 0; + char classify; + PLANE_CLASSIFY_BOX(plane,trimesh->m_aabbset.m_global_bound,classify); + if(classify>1) return; // in front of plane + + //Locks mesh + gim_trimesh_locks_work_data(trimesh); + //Get vertices + GUINT32 i, vertcount = trimesh->m_transformed_vertex_buffer.m_element_count; + vec3f * vertices = GIM_BUFFER_ARRAY_POINTER(vec3f,trimesh->m_transformed_vertex_buffer,0); + + GREAL dist; + vec4f * result_contact; + + for (i=0;i premake4.exe --with-tests --with-demos vs2008 + + To see a complete list of options use: + + > premake4.exe --help + + Note that Visual Studio 6 is not supported and users are advised to upgrade + to at least Visual Studio 2005 Express (it's free!) + + Using CMake is another option for generating project files for Visual Studio. + See section 5 below for more details on this. + + + + +2. BUILDING WITH AUTOTOOLS (Linux, OS X, MSYS, etc.) +==================================================== + +2.1 FROM SUBVERSION REPOSITORY +------------------------------ + + If you downloaded the source code from Subversion you must bootstrap the + process by running the command: + + $ ./bootstrap + + For this command to work you need a set of tools typically available + on BSD and Linux distributions with development packages installed. OS X + users may need to manually install libtool, autoconf, automake, + pkg-config, and maybe some more. + + If you downloaded a source code package from SourceForge this has + already been done for you. You may see some "underquoted definition" + warnings depending on your platform, these are (for now) harmless + warnings regarding scripts from other m4 installed packages. + +2.2 FROM A RELEASED TARBALL +--------------------------- + + First extract the archive (e.g. tar xvfz ) and enter + the created directory (ode-x.y). + + Run the configure script to autodetect your build environment: + + $ ./configure + + By default this will build ODE as a static library with single-precision + math, trimesh support with OPCODE, and debug symbols enabled. You can + modify these defaults by passing additional parameters to + configure. For a full list of available options, type: + + $ ./configure --help + + Some of the more popular options are + + --enable-double-precision enable double-precision math + --with-trimesh=none disables the trimesh support + --with-trimesh=opcode use OPCODE for trimesh code + --with-trimesh=gimpact use GIMPACT for trimesh code + + --enabled-shared builds a shared library + + To pass specific flags for an optimized build, you must do so + in the CFLAGS and CXXFLAGS enviroment variables, or as arguments + to ./configure. For example if you are building for an athlon xp processor + and you want the compiler to use SSE instructions you can run configure as + follows: + + $ ./configure CFLAGS="-msse -march=atlon-xp" CXXFLAGS="-msse -march=atlon-xp" + + Note that you must set both CFLAGS and CXXFLAGS as ODE contains a mixture of + C and C++ files. + + Once configure has run successfully, build and install ODE: + + $ make + $ make install + + The latter command will also create a pkg-config script that provides + compilation and linking flags for programs. The old stand-alone + "ode-config" script is also installed for compatibility. + + + + +3. BUILDING WITH Code::Blocks +============================= + + Because Code::Blocks supports so many different platforms, we do not + provide workspaces. Instead, use Premake to create a workspace tailored + for your platform and project. Like so: + + $ cd build + $ premake4 --with-tests --with-demos codeblocks + + To see a complete list of options: + + $ cd build + $ premake4 --help + + Using CMake is another option for generating project files for Code::Blocks. + See section 5 below for more details on this. + + + + +4. BUILDING WITH SOMETHING ELSE +=============================== + + ODE uses the Premake tool to provide support for several different toolsets. + Premake adds support for new toolsets on a regular basis, so yours might be + supported. Check the Premake website at http://premake.sourceforge.net/, + and then follow the directions for Code::Blocks above, substituting your + toolset target in place of `codeblocks`. + + Using CMake is another option for generating project files for other + toolsets. See section 5 below for more details on this. + + + + +5. BUILDING WITH CMAKE +====================== + + ODE includes support for CMake to generate project files for various platforms + and IDEs including Unix Makefiles, Ninja, Code::Blocks, Visual Studio. A full + overview of all supported generators can be found at the latest version of the + manual at https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html + + CMake supports and encourages out-of-source builds. In order to generate build + files for your platform, create a build directory at your preferred location + and then call CMake with the path to ODE's source directory as argument, e.g., + one level above the source directory: + + $ cd .. + $ mkdir ode-build + $ cmake ../ode-src + + The existing build directory in the source directory can also be used as a + location for the project files. A different generator than the default one + for the system can be specified as well: + + $ cd build + $ cmake -G"Visual Studio 15 2017 Win64" .. + + QtCreator, CLion, and Visual Studio 2017 also offer the option to open the + source directory with the CMakeLists.txt file directly in the IDE. + + + + diff --git a/thirdparty/ode-0.16.5/LICENSE-BSD.TXT b/thirdparty/ode-0.16.5/LICENSE-BSD.TXT new file mode 100644 index 0000000..112f6a2 --- /dev/null +++ b/thirdparty/ode-0.16.5/LICENSE-BSD.TXT @@ -0,0 +1,34 @@ + +This is the BSD-style license for the Open Dynamics Engine +---------------------------------------------------------- + +Open Dynamics Engine +Copyright (c) 2001-2007, Russell L. Smith. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +Neither the names of ODE's copyright owner nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/ode-0.16.5/LICENSE.TXT b/thirdparty/ode-0.16.5/LICENSE.TXT new file mode 100644 index 0000000..cfe59bc --- /dev/null +++ b/thirdparty/ode-0.16.5/LICENSE.TXT @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/thirdparty/ode-0.16.5/Makefile.am b/thirdparty/ode-0.16.5/Makefile.am new file mode 100644 index 0000000..c786f5c --- /dev/null +++ b/thirdparty/ode-0.16.5/Makefile.am @@ -0,0 +1,48 @@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 --install + +if ENABLE_OU +OU_DIR = ou +endif + +if LIBCCD +if LIBCCD_INTERNAL +LIBCCD_DIR = libccd +endif +endif + +if ENABLE_DEMOS +DRAWSTUFF_DIR = drawstuff +endif + +if GIMPACT +GIMPACT_DIR = GIMPACT +endif + +if OPCODE +OPCODE_DIR = OPCODE +endif + +SUBDIRS = include \ + $(DRAWSTUFF_DIR) \ + $(GIMPACT_DIR) \ + $(OPCODE_DIR) \ + $(OU_DIR) \ + $(LIBCCD_DIR) \ + ode \ + tests + +bin_SCRIPTS = ode-config + +# Utility rule for making a release +release: dist-gzip dist-bzip2 + @echo Created release packages for ${PACKAGE}-${VERSION}. + +EXTRA_DIST = bootstrap build tools \ + CHANGELOG.txt COPYING INSTALL.txt CSR.txt README.md \ + LICENSE.TXT LICENSE-BSD.TXT \ + bindings \ + CMakeLists.txt ode-config.cmake.in config.h.cmake.in cmake + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = ode.pc diff --git a/thirdparty/ode-0.16.5/Makefile.in b/thirdparty/ode-0.16.5/Makefile.in new file mode 100644 index 0000000..2b8787e --- /dev/null +++ b/thirdparty/ode-0.16.5/Makefile.in @@ -0,0 +1,941 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = ode-config ode.pc +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)" +SCRIPTS = $(bin_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = include drawstuff GIMPACT OPCODE ou libccd ode tests +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/ode-config.in \ + $(srcdir)/ode.pc.in COPYING compile config.guess config.sub \ + depcomp install-sh ltmain.sh missing +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 --install +@ENABLE_OU_TRUE@OU_DIR = ou +@LIBCCD_INTERNAL_TRUE@@LIBCCD_TRUE@LIBCCD_DIR = libccd +@ENABLE_DEMOS_TRUE@DRAWSTUFF_DIR = drawstuff +@GIMPACT_TRUE@GIMPACT_DIR = GIMPACT +@OPCODE_TRUE@OPCODE_DIR = OPCODE +SUBDIRS = include \ + $(DRAWSTUFF_DIR) \ + $(GIMPACT_DIR) \ + $(OPCODE_DIR) \ + $(OU_DIR) \ + $(LIBCCD_DIR) \ + ode \ + tests + +bin_SCRIPTS = ode-config +EXTRA_DIST = bootstrap build tools \ + CHANGELOG.txt COPYING INSTALL.txt CSR.txt README.md \ + LICENSE.TXT LICENSE-BSD.TXT \ + bindings \ + CMakeLists.txt ode-config.cmake.in config.h.cmake.in cmake + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = ode.pc +all: all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): +ode-config: $(top_builddir)/config.status $(srcdir)/ode-config.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +ode.pc: $(top_builddir)/config.status $(srcdir)/ode.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(SCRIPTS) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-pkgconfigDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-binSCRIPTS + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-pkgconfigDATA + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + clean-libtool cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ + dist-xz dist-zip distcheck distclean distclean-generic \ + distclean-libtool distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-binSCRIPTS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binSCRIPTS \ + uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +# Utility rule for making a release +release: dist-gzip dist-bzip2 + @echo Created release packages for ${PACKAGE}-${VERSION}. + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/OPCODE/COPYING b/thirdparty/ode-0.16.5/OPCODE/COPYING new file mode 100644 index 0000000..54a62aa --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/COPYING @@ -0,0 +1,30 @@ +The OPCODE library distributed as part of ODE is licensed under +the same terms as ODE (LGPLv2.1+ and BSD). + +Quoting a public e-mail from the author: + + Re: TriMesh support and OPCODE added to ODE core + Pierre Terdiman wanadoo.fr> + 2003-07-01 21:18:44 GMT + + > If he wants + > to explicitly make it clear that OpCode is good under ODE's + > license, that would be A-1 Super... + + "Opcode is good under ODE's license" + + I didn't put a license to prevent boring questions about licenses, but it + seems it's not enough - I still get as many questions, regarding missing + license. + + The only thing that would NOT be good would be renaming it "TopCode", + changing the author's name, selling it at a very expensive price, and still + managing to make money out of it :) + + ...I should add a license explicitely against this :) + + Pierre + +Source: + http://permalink.gmane.org/gmane.comp.lib.ode/3237 + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.cpp new file mode 100644 index 0000000..d96cd88 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceAABB.cpp @@ -0,0 +1,405 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains AABB-related code. + * \file IceAABB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * AABB class. + * \class AABB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the sum of two AABBs. + * \param aabb [in] the other AABB + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABB& AABB::Add(const AABB& aabb) +{ + // Compute new min & max values + Point Min; GetMin(Min); + Point Tmp; aabb.GetMin(Tmp); + Min.Min(Tmp); + + Point Max; GetMax(Max); + aabb.GetMax(Tmp); + Max.Max(Tmp); + + // Update this + SetMinMax(Min, Max); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a cube from the AABB. + * \param cube [out] the cube AABB + * \return cube edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABB::MakeCube(AABB& cube) const +{ + Point Ext; GetExtents(Ext); + float Max = Ext.Max(); + + Point Cnt; GetCenter(Cnt); + cube.SetCenterExtents(Cnt, Point(Max, Max, Max)); + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a sphere from the AABB. + * \param sphere [out] sphere containing the AABB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABB::MakeSphere(Sphere& sphere) const +{ + GetExtents(sphere.mCenter); + sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f; // To make sure sphere::Contains(*this) succeeds + GetCenter(sphere.mCenter); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a box is inside another box. + * \param box [in] the other AABB + * \return true if current box is inside input box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABB::IsInside(const AABB& box) const +{ + if(box.GetMin(0)>GetMin(0)) return false; + if(box.GetMin(1)>GetMin(1)) return false; + if(box.GetMin(2)>GetMin(2)) return false; + if(box.GetMax(0) max.x) ? 2 : 0) // 2 = right + + ((local_eye.y < min.y) ? 4 : 0) // 4 = bottom + + ((local_eye.y > max.y) ? 8 : 0) // 8 = top + + ((local_eye.z < min.z) ? 16 : 0) // 16 = front + + ((local_eye.z > max.z) ? 32 : 0); // 32 = back + + // Look up number of vertices in outline + num = (sdword)gIndexList[pos][7]; + // Zero indicates invalid case + if(!num) return null; + + return &gIndexList[pos][0]; +} + +// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box + +//const Point& eye, //eye point (in bbox object coordinates) +//const AABB& box, //3d bbox +//const Matrix4x4& mat, //free transformation for bbox +//float width, float height, int& num) +float AABB::ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const +{ + const sbyte* Outline = ComputeOutline(eye, num); + if(!Outline) return -1.0f; + + // Compute box vertices + Point vertexBox[8], dst[8]; + ComputePoints(vertexBox); + + // Transform all outline corners into 2D screen space + for(sdword i=0;i GetMax(0) || p.x < GetMin(0)) return FALSE; \ + if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \ + if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \ + return TRUE; \ + } + + enum AABBType + { + AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered. + AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated. + + AABB_FORCE_DWORD = 0x7fffffff, + }; + +#ifdef USE_MINMAX + + struct ICEMATHS_API ShadowAABB + { + Point mMin; + Point mMax; + }; + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mMin = min; mMax = max; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mMin = c - e; mMax = c + e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { Point p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mMin = mMax = pt; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { Point e; GetExtents(e); return e.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + if(p.x > mMax.x) mMax.x = p.x; + if(p.x < mMin.x) mMin.x = p.x; + + if(p.y > mMax.y) mMax.y = p.y; + if(p.y < mMin.y) mMin.y = p.y; + + if(p.z > mMax.z) mMax.z = p.z; + if(p.z < mMin.z) mMin.z = p.z; + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mMin; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mMax; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mMin[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mMax[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = (mMax + mMin)*0.5f; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = (mMax - mMin)*0.5f; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; } + inline_ float GetWidth() const { return mMax.x - mMin.x; } + inline_ float GetHeight() const { return mMax.y - mMin.y; } + inline_ float GetDepth() const { return mMax.z - mMin.z; } + + //! Volume + inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + if(mMax.x < a.mMin.x + || a.mMax.x < mMin.x + || mMax.y < a.mMin.y + || a.mMax.y < mMin.y + || mMax.z < a.mMin.z + || a.mMax.z < mMin.z) return FALSE; + + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it) + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // The three edges transformed: you can efficiently transform an X-only vector + // by just getting the "X" column of the matrix + Point vx,vy,vz; + mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x); + mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y); + mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z); + + // Transform the min point + aabb.mMin = aabb.mMax = mMin * mtx; + + // Take the transformed min & axes and find new extents + // Using CPU code in the right place is faster... + if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x; + if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y; + if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z; + if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x; + if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y; + if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z; + if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x; + if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y; + if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Min, Max) boxes: min < max + if(mMin.x > mMax.x) return FALSE; + if(mMin.y > mMax.y) return FALSE; + if(mMin.z > mMax.z) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents * s); + return *this; + } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents / s); + return *this; + } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mMin+=trans; + mMax+=trans; + return *this; + } + private: + Point mMin; //!< Min point + Point mMax; //!< Max point + }; + +#else + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mCenter = c; mExtents = e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { return mExtents.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + Point Max = mCenter + mExtents; + Point Min = mCenter - mExtents; + + if(p.x > Max.x) Max.x = p.x; + if(p.x < Min.x) Min.x = p.x; + + if(p.y > Max.y) Max.y = p.y; + if(p.y < Min.y) Min.y = p.y; + + if(p.z > Max.z) Max.z = p.z; + if(p.z < Min.z) Min.z = p.z; + + SetMinMax(Min, Max); + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = mCenter; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = mExtents; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return mCenter[axis]; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return mExtents[axis]; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; } + inline_ float GetWidth() const { return mExtents.x * 2.0f; } + inline_ float GetHeight() const { return mExtents.y * 2.0f; } + inline_ float GetDepth() const { return mExtents.z * 2.0f; } + + //! Volume + inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE; + float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE; + float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * The standard intersection method from Gamasutra. Just here to check its speed against the one above. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool GomezIntersect(const AABB& a) + { + Point T = mCenter - a.mCenter; // Vector from A to B + return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x)) + && (fabsf(T.y) <= (a.mExtents.y + mExtents.y)) + && (fabsf(T.z) <= (a.mExtents.z + mExtents.z))); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + float t = mCenter[axis] - a.mCenter[axis]; + float e = a.mExtents[axis] + mExtents[axis]; + if(AIR(t) > IR(e)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // Compute new center + aabb.mCenter = mCenter * mtx; + + // Compute new extents. FPU code & CPU code have been interleaved for improved performance. + Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x); + //IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff; + Ex.x = FR( AIR(Ex.x) ); + Ex.y = FR( AIR(Ex.y) ); + Ex.z = FR( AIR(Ex.z) ); + + Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y); + //IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff; + Ey.x = FR( AIR(Ey.x) ); + Ey.y = FR( AIR(Ey.y) ); + Ey.z = FR( AIR(Ey.z) ); + + Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z); + //IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff; + Ez.x = FR( AIR(Ez.x) ); + Ez.y = FR( AIR(Ez.y) ); + Ez.z = FR( AIR(Ez.z) ); + + aabb.mExtents.x = Ex.x + Ey.x + Ez.x; + aabb.mExtents.y = Ex.y + Ey.y + Ez.y; + aabb.mExtents.z = Ex.z + Ey.z + Ez.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0 + if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) { mExtents*=s; return *this; } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) { mExtents/=s; return *this; } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mCenter+=trans; + return *this; + } + private: + Point mCenter; //!< AABB Center + Point mExtents; //!< x, y and z extents + }; + +#endif + + inline_ void ComputeMinMax(const Point& p, Point& min, Point& max) + { + if(p.x > max.x) max.x = p.x; + if(p.x < min.x) min.x = p.x; + + if(p.y > max.y) max.y = p.y; + if(p.y < min.y) min.y = p.y; + + if(p.z > max.z) max.z = p.z; + if(p.z < min.z) min.z = p.z; + } + + inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts) + { + if(list) + { + Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); + while(nb_pts--) + { +// _prefetch(list+1); // off by one ? + ComputeMinMax(*list++, Mini, Maxi); + } + aabb.SetMinMax(Mini, Maxi); + } + } + +#endif // __ICEAABB_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceAxes.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceAxes.h new file mode 100644 index 0000000..8af57e1 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceAxes.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains axes definition. + * \file IceAxes.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEAXES_H__ +#define __ICEAXES_H__ + + enum PointComponent + { + X = 0, + Y = 1, + Z = 2, + W = 3, + + FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (X)|(Y<<2)|(Z<<4), + AXES_XZY = (X)|(Z<<2)|(Y<<4), + AXES_YXZ = (Y)|(X<<2)|(Z<<4), + AXES_YZX = (Y)|(Z<<2)|(X<<4), + AXES_ZXY = (Z)|(X<<2)|(Y<<4), + AXES_ZYX = (Z)|(Y<<2)|(X<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Axes + { + public: + + inline_ Axes(AxisOrder order) + { + mAxis0 = (order ) & 3; + mAxis1 = (order>>2) & 3; + mAxis2 = (order>>4) & 3; + } + inline_ ~Axes() {} + + udword mAxis0; + udword mAxis1; + udword mAxis2; + }; + +#endif // __ICEAXES_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceBoundingSphere.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceBoundingSphere.h new file mode 100644 index 0000000..945d38c --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceBoundingSphere.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to compute the minimal bounding sphere. + * \file IceBoundingSphere.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEBOUNDINGSPHERE_H__ +#define __ICEBOUNDINGSPHERE_H__ + + enum BSphereMethod + { + BS_NONE, + BS_GEMS, + BS_MINIBALL, + + BS_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Sphere + { + public: + //! Constructor + inline_ Sphere() {} + //! Constructor + inline_ Sphere(const Point& center, float radius) : mCenter(center), mRadius(radius) {} + //! Constructor + Sphere(udword nb_verts, const Point* verts); + //! Copy constructor + inline_ Sphere(const Sphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius) {} + //! Destructor + inline_ ~Sphere() {} + + BSphereMethod Compute(udword nb_verts, const Point* verts); + bool FastCompute(udword nb_verts, const Point* verts); + + // Access methods + inline_ const Point& GetCenter() const { return mCenter; } + inline_ float GetRadius() const { return mRadius; } + + inline_ const Point& Center() const { return mCenter; } + inline_ float Radius() const { return mRadius; } + + inline_ Sphere& Set(const Point& center, float radius) { mCenter = center; mRadius = radius; return *this; } + inline_ Sphere& SetCenter(const Point& center) { mCenter = center; return *this; } + inline_ Sphere& SetRadius(float radius) { mRadius = radius; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the sphere. + * \param p [in] the point to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Point& p) const + { + return mCenter.SquareDistance(p) <= mRadius*mRadius; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a sphere is contained within the sphere. + * \param sphere [in] the sphere to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Sphere& sphere) const + { + // If our radius is the smallest, we can't possibly contain the other sphere + if(mRadius < sphere.mRadius) return false; + // So r is always positive or null now + float r = mRadius - sphere.mRadius; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a box is contained within the sphere. + * \param aabb [in] the box to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Contains(const AABB& aabb) const + { + // I assume if all 8 box vertices are inside the sphere, so does the whole box. + // Sounds ok but maybe there's a better way? + float R2 = mRadius * mRadius; +#ifdef USE_MIN_MAX + const Point& Max = ((ShadowAABB&)&aabb).mMax; + const Point& Min = ((ShadowAABB&)&aabb).mMin; +#else + Point Max; aabb.GetMax(Max); + Point Min; aabb.GetMin(Min); +#endif + Point p; + p.x=Max.x; p.y=Max.y; p.z=Max.z; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Max.y; p.z=Min.z; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if the sphere intersects another sphere + * \param sphere [in] the other sphere + * \return true if spheres overlap + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Intersect(const Sphere& sphere) const + { + float r = mRadius + sphere.mRadius; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the sphere is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + if(mRadius < 0.0f) return FALSE; + return TRUE; + } + public: + Point mCenter; //!< Sphere center + float mRadius; //!< Sphere radius + }; + +#endif // __ICEBOUNDINGSPHERE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceContainer.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceContainer.cpp new file mode 100644 index 0000000..3eeefe0 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceContainer.cpp @@ -0,0 +1,357 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple container class. + * \file IceContainer.cpp + * \author Pierre Terdiman + * \date February, 5, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a list of 32-bits values. + * Use this class when you need to store an unknown number of values. The list is automatically + * resized and can contains 32-bits entities (dwords or floats) + * + * \class Container + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +#define MAX_RESERVE_GROWTH_SIZE 65536U + +// Static members +#ifdef CONTAINER_STATS +udword Container::mNbContainers = 0; +udword Container::mUsedRam = 0; +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. No entries allocated there. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. Also allocates a given number of entries. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif + SetSize(size); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Copy constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::Container(const Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(Container); +#endif + *this = object; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. Frees everything and leaves. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container::~Container() +{ + Empty(); +#ifdef CONTAINER_STATS + mNbContainers--; + mUsedRam-=GetUsedRam(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Container& Container::Empty() +{ +#ifdef CONTAINER_STATS + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + DELETEARRAY(mEntries); + mCurNbEntries = mMaxNbEntries = 0; + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Resizes the container. + * \param needed [in] assume the container can be added at least "needed" values + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Container::Resize(udword needed) +{ +#ifdef CONTAINER_STATS + // Subtract previous amount of bytes + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + + if (MAX_UDWORD - mCurNbEntries < needed) + { + CHECKALLOC(null); + } + + // Get more entries + udword NewMaxNbEntries = mMaxNbEntries ? udword(mMaxNbEntries * mGrowthFactor) : 2; // Default nb Entries = 2 + + if (NewMaxNbEntries <= mMaxNbEntries) NewMaxNbEntries = MAX_UDWORD - mMaxNbEntries < MAX_RESERVE_GROWTH_SIZE ? MAX_UDWORD : mMaxNbEntries + MAX_RESERVE_GROWTH_SIZE; + else if (NewMaxNbEntries - mMaxNbEntries > MAX_RESERVE_GROWTH_SIZE) NewMaxNbEntries = mMaxNbEntries + MAX_RESERVE_GROWTH_SIZE; + + if (NewMaxNbEntries < mCurNbEntries + needed) NewMaxNbEntries = mCurNbEntries + needed; + + // Get some bytes for new entries + udword* NewEntries = new udword[NewMaxNbEntries]; + CHECKALLOC(NewEntries); + + // Copy old data if needed + if(mCurNbEntries) CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword)); + + // Delete old data + DELETEARRAY(mEntries); + + // Assign new pointer + mEntries = NewEntries; + mMaxNbEntries = NewMaxNbEntries; + +#ifdef CONTAINER_STATS + // Add current amount of bytes + mUsedRam+=mMaxNbEntries*sizeof(udword); +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Sets the initial size of the container. If it already contains something, it's discarded. + * \param nb [in] Number of entries + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Container::SetSize(udword nb) +{ + // Make sure it's empty + Empty(); + + // Checkings + if(!nb) return false; + + // Initialize for nb entries + mMaxNbEntries = nb; + + // Get some bytes for new entries + mEntries = new udword[mMaxNbEntries]; + CHECKALLOC(mEntries); + +#ifdef CONTAINER_STATS + // Add current amount of bytes + mUsedRam+=mMaxNbEntries*sizeof(udword); +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the container and get rid of unused bytes. + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Container::Refit() +{ +#ifdef CONTAINER_STATS + // Subtract previous amount of bytes + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + + // Get just enough entries + mMaxNbEntries = mCurNbEntries; + if(!mMaxNbEntries) return false; + + // Get just enough bytes + udword* NewEntries = new udword[mMaxNbEntries]; + CHECKALLOC(NewEntries); + +#ifdef CONTAINER_STATS + // Add current amount of bytes + mUsedRam+=mMaxNbEntries*sizeof(udword); +#endif + + // Copy old data + CopyMemory(NewEntries, mEntries, mCurNbEntries*sizeof(udword)); + + // Delete old data + DELETEARRAY(mEntries); + + // Assign new pointer + mEntries = NewEntries; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the container already contains a given value. + * \param entry [in] the value to look for in the container + * \param location [out] a possible pointer to store the entry location + * \see Add(udword entry) + * \see Add(float entry) + * \see Empty() + * \return true if the value has been found in the container, else false. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Container::Contains(udword entry, udword* location) const +{ + // Look for the entry + for(udword i=0;i mMaxNbEntries + && !Resize(nb)) + { + IceAbort(); + } + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(uword)); + mCurNbEntries+=nb; + return *this; + } + + inline_ Container& Add(const udword* entries, udword nb) + { + // Resize if needed + if (mCurNbEntries + nb > mMaxNbEntries + && !Resize(nb)) + { + IceAbort(); + } + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); + mCurNbEntries+=nb; + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * A O(1) method to add a value in the container. The container is automatically resized if needed. + * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation + * costs a lot more than the call overhead... + * + * \param entry [in] a float to store in the container + * \see Add(udword entry) + * \see Empty() + * \see Contains(udword entry) + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ Container& Add(float entry) + { + // Resize if needed + if (mCurNbEntries == mMaxNbEntries + && !Resize()) + { + IceAbort(); + } + + // Add new entry + mEntries[mCurNbEntries++] = IR(entry); + return *this; + } + + inline_ Container& Add(const float* entries, udword nb) + { + // Resize if needed + if (mCurNbEntries + nb > mMaxNbEntries + && !Resize(nb)) + { + IceAbort(); + } + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float)); + mCurNbEntries+=nb; + return *this; + } + + //! Add unique [slow] + inline_ Container& AddUnique(udword entry) + { + if(!Contains(entry)) Add(entry); + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Container& Empty(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again. + * That's a kind of temporal coherence. + * \see Empty() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Reset() + { + // Avoid the write if possible + // ### CMOV + if(mCurNbEntries) mCurNbEntries = 0; + } + + // HANDLE WITH CARE + inline_ void ForceSize(udword size) + { + mCurNbEntries = size; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the initial size of the container. If it already contains something, it's discarded. + * \param nb [in] Number of entries + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetSize(udword nb); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the container and get rid of unused bytes. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Refit(); + + // Checks whether the container already contains a given value. + bool Contains(udword entry, udword* location=null) const; + // Deletes an entry - doesn't preserve insertion order. + bool Delete(udword entry); + // Deletes an entry - does preserve insertion order. + bool DeleteKeepingOrder(udword entry); + //! Deletes the very last entry. + inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; } + //! Deletes the entry whose index is given + inline_ void DeleteIndex(udword index) { ASSERT(index < mCurNbEntries); mEntries[index] = mEntries[--mCurNbEntries]; } + + // Helpers + Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP); + Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP); + // Data access. + inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. + inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry + inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. + + inline_ udword GetFirst() const { return mEntries[0]; } + inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; } + + // Growth control + inline_ udword GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor + inline_ void SetGrowthFactor(udword growth) { mGrowthFactor = growth; } //!< Sets the growth factor + inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full + inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty + + //! Read-access as an array + inline_ udword operator[](udword i) const { ASSERT(i>=0 && i=0 && i>31); + return FR(y); + } + + //! Computes 1.0f / sqrtf(x). + inline_ float frsqrt(float f) + { + float x = f * 0.5f; + udword y = 0x5f3759df - (IR(f) >> 1); + // Iteration... + const float fy = FR(y); + const float result = fy * ( 1.5f - ( x * fy * fy ) ); + // Result + return result; + } + + //! Computes 1.0f / sqrtf(x). Comes from NVIDIA. + inline_ float InvSqrt(const float& x) + { + const udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - IR(x)) >> 1; + const float y = FR(tmp); + return y * (1.47f - 0.47f * x * y * y); + } + + //! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above. + //! See http://www.magic-software.com/3DGEDInvSqrt.html + inline_ float RSqrt(float number) + { + int i; + float x2, y; + const float threehalfs = 1.5f; + + x2 = number * 0.5f; + y = number; + i = IR(y); + i = 0x5f3759df - (i >> 1); + y = FR(i); + y = y * (threehalfs - (x2 * y * y)); + + return y; + } + + //! TO BE DOCUMENTED + inline_ float fsqrt(float f) + { + udword y = ( ( SIR(f) - 0x3f800000 ) >> 1 ) + 0x3f800000; + // Iteration...? + // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f; + // Result + return FR(y); + } + + //! Returns the float ranged espilon value. + inline_ float fepsilon(float f) + { + udword b = IR(f) & 0xff800000; + udword a = b | 0x00000001; + // Result + return FR(a) - FR(b); + } + + //! Is the float valid ? + inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; } + inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; } + inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; } + inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; } + + inline_ bool IsValidFloat(float value) + { + if(IsNAN(value)) return false; + if(IsIndeterminate(value)) return false; + if(IsPlusInf(value)) return false; + if(IsMinusInf(value)) return false; + return true; + } + + #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x)); + +/* + //! FPU precision setting function. + inline_ void SetFPU() + { + // This function evaluates whether the floating-point + // control word is set to single precision/round to nearest/ + // exceptions disabled. If these conditions don't hold, the + // function changes the control word to set them and returns + // TRUE, putting the old control word value in the passback + // location pointed to by pwOldCW. + { + uword wTemp, wSave; + + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } + } + } +*/ + //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON) + inline_ float ComputeFloatEpsilon() + { + const float f = FR( IR(1.0f) ^ 1 ); + return f - 1.0f; // You can check it's the same as FLT_EPSILON + } + + inline_ bool IsFloatZero(float x, float epsilon=1e-6f) + { + return x*x < epsilon; + } + + #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 + #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 + #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 + #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0 + + #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1 + #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1 + #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1 + #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1 + + #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2 + #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2 + #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 + #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 + + #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3 + #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3 + #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3 + #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3 + + #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4 + #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4 + #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4 + #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4 + + #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5 + #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5 + #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5 + #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5 + + #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6 + #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6 + #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6 + #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6 + + #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7 + #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7 + #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7 + #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7 + + //! A global function to find MAX(a,b) using FCOMI/FCMOV + inline_ float FCMax2(float a, float b) + { + return (a > b) ? a : b; + } + + //! A global function to find MIN(a,b) using FCOMI/FCMOV + inline_ float FCMin2(float a, float b) + { + return (a < b) ? a : b; + } + + //! A global function to find MAX(a,b,c) using FCOMI/FCMOV + inline_ float FCMax3(float a, float b, float c) + { + return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); + } + + //! A global function to find MIN(a,b,c) using FCOMI/FCMOV + inline_ float FCMin3(float a, float b, float c) + { + return (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); + } + + inline_ int ConvertToSortable(float f) + { + int Fi = SIR(f); + int Fmask = (Fi>>31); + Fi ^= Fmask; + Fmask &= ~(1<<31); + Fi -= Fmask; + return Fi; + } + + enum FPUMode + { + FPU_FLOOR = 0, + FPU_CEIL = 1, + FPU_BEST = 2, + + FPU_FORCE_DWORD = 0x7fffffff + }; + + FUNCTION ICECORE_API FPUMode GetFPUMode(); + FUNCTION ICECORE_API void SaveFPU(); + FUNCTION ICECORE_API void RestoreFPU(); + FUNCTION ICECORE_API void SetFPUFloorMode(); + FUNCTION ICECORE_API void SetFPUCeilMode(); + FUNCTION ICECORE_API void SetFPUBestMode(); + + FUNCTION ICECORE_API void SetFPUPrecision24(); + FUNCTION ICECORE_API void SetFPUPrecision53(); + FUNCTION ICECORE_API void SetFPUPrecision64(); + FUNCTION ICECORE_API void SetFPURoundingChop(); + FUNCTION ICECORE_API void SetFPURoundingUp(); + FUNCTION ICECORE_API void SetFPURoundingDown(); + FUNCTION ICECORE_API void SetFPURoundingNear(); + + FUNCTION ICECORE_API int intChop(const float& f); + FUNCTION ICECORE_API int intFloor(const float& f); + FUNCTION ICECORE_API int intCeil(const float& f); + +#endif // __ICEFPU_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.cpp new file mode 100644 index 0000000..f806a0c --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.cpp @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous points. + * \file IceHPoint.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Homogeneous point. + * + * Use it: + * - for clipping in homogeneous space (standard way) + * - to differentiate between points (w=1) and vectors (w=0). + * - in some cases you can also use it instead of Point for padding reasons. + * + * \class HPoint + * \author Pierre Terdiman + * \version 1.0 + * \warning No cross-product in 4D. + * \warning HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Point Mul = HPoint * Matrix3x3; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point HPoint::operator*(const Matrix3x3& mat) const +{ + return Point( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint Mul = HPoint * Matrix4x4; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint HPoint::operator*(const Matrix4x4& mat) const +{ + return HPoint( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2], + x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint *= Matrix4x4 +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint& HPoint::operator*=(const Matrix4x4& mat) +{ + float xp = x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0]; + float yp = x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1]; + float zp = x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2]; + float wp = x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]; + + x = xp; y = yp; z = zp; w = wp; + + return *this; +} + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.h new file mode 100644 index 0000000..a3770cd --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceHPoint.h @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous points. + * \file IceHPoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEHPOINT_H__ +#define __ICEHPOINT_H__ + + class ICEMATHS_API HPoint : public Point + { + public: + + //! Empty constructor + inline_ HPoint() {} + //! Constructor from floats + inline_ HPoint(float xx, float yy, float zz, float ww=0.0f) : Point(xx, yy, zz), w(ww) {} + //! Constructor from array + inline_ HPoint(const float f[4]) : Point(f), w(f[3]) {} + //! Constructor from a Point + inline_ HPoint(const Point& p, float ww=0.0f) : Point(p), w(ww) {} + //! Destructor + inline_ ~HPoint() {} + + //! Clear the point + inline_ HPoint& Zero() { x = y = z = w = 0.0f; return *this; } + + //! Assignment from values + inline_ HPoint& Set(float xx, float yy, float zz, float ww ) { x = xx; y = yy; z = zz; w = ww; return *this; } + //! Assignment from array + inline_ HPoint& Set(const float f[4]) { x = f[X]; y = f[Y]; z = f[Z]; w = f[W]; return *this; } + //! Assignment from another h-point + inline_ HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; } + + //! Add a vector + inline_ HPoint& Add(float xx, float yy, float zz, float ww ) { x += xx; y += yy; z += zz; w += ww; return *this; } + //! Add a vector + inline_ HPoint& Add(const float f[4]) { x += f[X]; y += f[Y]; z += f[Z]; w += f[W]; return *this; } + + //! Subtract a vector + inline_ HPoint& Sub(float xx, float yy, float zz, float ww ) { x -= xx; y -= yy; z -= zz; w -= ww; return *this; } + //! Subtract a vector + inline_ HPoint& Sub(const float f[4]) { x -= f[X]; y -= f[Y]; z -= f[Z]; w -= f[W]; return *this; } + + //! Multiplies by a scalar + inline_ HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } + + //! Returns MIN(x, y, z, w); + float Min() const { return MIN(x, MIN(y, MIN(z, w))); } + //! Returns MAX(x, y, z, w); + float Max() const { return MAX(x, MAX(y, MAX(z, w))); } + //! Sets each element to be componentwise minimum + HPoint& Min(const HPoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w); return *this; } + //! Sets each element to be componentwise maximum + HPoint& Max(const HPoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w); return *this; } + + //! Computes square magnitude + inline_ float SquareMagnitude() const { return x*x + y*y + z*z + w*w; } + //! Computes magnitude + inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z + w*w); } + + //! Normalize the vector + inline_ HPoint& Normalize() + { + float M = Magnitude(); + if(M) + { + M = 1.0f / M; + x *= M; + y *= M; + z *= M; + w *= M; + } + return *this; + } + + // Arithmetic operators + //! Operator for HPoint Negate = - HPoint; + inline_ HPoint operator-() const { return HPoint(-x, -y, -z, -w); } + + //! Operator for HPoint Plus = HPoint + HPoint; + inline_ HPoint operator+(const HPoint& p) const { return HPoint(x + p.x, y + p.y, z + p.z, w + p.w); } + //! Operator for HPoint Minus = HPoint - HPoint; + inline_ HPoint operator-(const HPoint& p) const { return HPoint(x - p.x, y - p.y, z - p.z, w - p.w); } + + //! Operator for HPoint Mul = HPoint * HPoint; + inline_ HPoint operator*(const HPoint& p) const { return HPoint(x * p.x, y * p.y, z * p.z, w * p.w); } + //! Operator for HPoint Scale = HPoint * float; + inline_ HPoint operator*(float s) const { return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float * HPoint; + inline_ friend HPoint operator*(float s, const HPoint& p) { return HPoint(s * p.x, s * p.y, s * p.z, s * p.w); } + + //! Operator for HPoint Div = HPoint / HPoint; + inline_ HPoint operator/(const HPoint& p) const { return HPoint(x / p.x, y / p.y, z / p.z, w / p.w); } + //! Operator for HPoint Scale = HPoint / float; + inline_ HPoint operator/(float s) const { s = 1.0f / s; return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float / HPoint; + inline_ friend HPoint operator/(float s, const HPoint& p) { return HPoint(s / p.x, s / p.y, s / p.z, s / p.w); } + + //! Operator for float DotProd = HPoint | HPoint; + inline_ float operator|(const HPoint& p) const { return x*p.x + y*p.y + z*p.z + w*p.w; } + // No cross-product in 4D + + //! Operator for HPoint += HPoint; + inline_ HPoint& operator+=(const HPoint& p) { x += p.x; y += p.y; z += p.z; w += p.w; return *this; } + //! Operator for HPoint += float; + inline_ HPoint& operator+=(float s) { x += s; y += s; z += s; w += s; return *this; } + + //! Operator for HPoint -= HPoint; + inline_ HPoint& operator-=(const HPoint& p) { x -= p.x; y -= p.y; z -= p.z; w -= p.w; return *this; } + //! Operator for HPoint -= float; + inline_ HPoint& operator-=(float s) { x -= s; y -= s; z -= s; w -= s; return *this; } + + //! Operator for HPoint *= HPoint; + inline_ HPoint& operator*=(const HPoint& p) { x *= p.x; y *= p.y; z *= p.z; w *= p.w; return *this; } + //! Operator for HPoint *= float; + inline_ HPoint& operator*=(float s) { x*=s; y*=s; z*=s; w*=s; return *this; } + + //! Operator for HPoint /= HPoint; + inline_ HPoint& operator/=(const HPoint& p) { x /= p.x; y /= p.y; z /= p.z; w /= p.w; return *this; } + //! Operator for HPoint /= float; + inline_ HPoint& operator/=(float s) { s = 1.0f / s; x*=s; y*=s; z*=s; w*=s; return *this; } + + // Arithmetic operators + + //! Operator for Point Mul = HPoint * Matrix3x3; + Point operator*(const Matrix3x3& mat) const; + //! Operator for HPoint Mul = HPoint * Matrix4x4; + HPoint operator*(const Matrix4x4& mat) const; + + // HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + //! Operator for HPoint *= Matrix4x4 + HPoint& operator*=(const Matrix4x4& mat); + + // Logical operators + + //! Operator for "if(HPoint==HPoint)" + inline_ bool operator==(const HPoint& p) const { return ( (x==p.x)&&(y==p.y)&&(z==p.z)&&(w==p.w)); } + //! Operator for "if(HPoint!=HPoint)" + inline_ bool operator!=(const HPoint& p) const { return ( (x!=p.x)||(y!=p.y)||(z!=p.z)||(w!=p.w)); } + + // Cast operators + + //! Cast a HPoint to a Point. w is discarded. +#ifdef _MSC_VER + inline_ operator Point() const { return Point(x, y, z); } + // gcc complains that conversion to a base class will never use a type conversion operator +#endif + + public: + float w; + }; + +#endif // __ICEHPOINT_H__ + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.cpp new file mode 100644 index 0000000..d317113 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.cpp @@ -0,0 +1,548 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an indexed triangle class. + * + * \class Triangle + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Flip() +{ + Swap(mVRef[1], mVRef[2]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \param verts [in] the list of indexed vertices + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Area(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return ((p0-p1)^(p0-p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \param verts [in] the list of indexed vertices + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Perimeter(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \param verts [in] the list of indexed vertices + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Compacity(const Point* verts) const +{ + if(!verts) return 0.0f; + float P = Perimeter(verts); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area(verts)/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Normal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::DenormalizedNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param verts [in] the list of indexed vertices + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Center(const Point* verts, Point& center) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + center = (p0+p1+p2)*INV3; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the centered normal + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::CenteredNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + Point Center = (p0+p1+p2)*INV3; + normal = Center + ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a random point within the triangle. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::RandomPoint(const Point* verts, Point& random) const +{ + if(!verts) return; + + // Random barycentric coords + float Alpha = UnitRandomFloat(); + float Beta = UnitRandomFloat(); + float Gamma = UnitRandomFloat(); + float OneOverTotal = 1.0f / (Alpha + Beta + Gamma); + Alpha *= OneOverTotal; + Beta *= OneOverTotal; + Gamma *= OneOverTotal; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + random = Alpha*p0 + Beta*p1 + Gamma*p2; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsVisible(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling + return (Normal | source) >= 0.0f; + +// Same as: +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source) > PL.d; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::BackfaceCulling(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute base +// Point Base = (p0 + p1 + p2)*INV3; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling +// return (Normal | (source - Base)) >= 0.0f; + return (Normal | (source - p0)) >= 0.0f; + +// Same as: (but a bit faster) +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source)>0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the occlusion potential of the triangle. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which occlusion potential must be computed + * \return the occlusion potential + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::ComputeOcclusionPotential(const Point* verts, const Point& view) const +{ + if(!verts) return 0.0f; + // Occlusion potential: -(A * (N|V) / d^2) + // A = polygon area + // N = polygon normal + // V = view vector + // d = distance viewpoint-center of polygon + + float A = Area(verts); + Point N; Normal(verts, N); + Point C; Center(verts, C); + float d = view.Distance(C); + return -(A*(N|view))/(d*d); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Replaces a vertex reference with another one. + * \param oldref [in] the vertex reference to replace + * \param newref [in] the new vertex reference + * \return true if success, else false if the input vertex reference doesn't belong to the triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::ReplaceVertex(dTriIndex oldref, dTriIndex newref) +{ + if(mVRef[0]==oldref) { mVRef[0] = newref; return true; } + else if(mVRef[1]==oldref) { mVRef[1] = newref; return true; } + else if(mVRef[2]==oldref) { mVRef[2] = newref; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the triangle is degenerate or not. A degenerate triangle has two common vertex references. This is a zero-area triangle. + * \return true if the triangle is degenerate + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsDegenerate() const +{ + if(mVRef[0]==mVRef[1]) return true; + if(mVRef[1]==mVRef[2]) return true; + if(mVRef[2]==mVRef[0]) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(dTriIndex ref) const +{ + if(mVRef[0]==ref) return true; + if(mVRef[1]==ref) return true; + if(mVRef[2]==ref) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \param index [out] the corresponding index in the triangle + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(dTriIndex ref, dTriIndex* index) const +{ + if(mVRef[0]==ref) { *index = 0; return true; } + if(mVRef[1]==ref) { *index = 1; return true; } + if(mVRef[2]==ref) { *index = 2; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Finds an edge in a tri, given two vertex references. + * \param vref0 [in] the edge's first vertex reference + * \param vref1 [in] the edge's second vertex reference + * \return the edge number between 0 and 2, or 0xff if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ubyte IndexedTriangle::FindEdge(dTriIndex vref0, dTriIndex vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return 0; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return 0; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return 1; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return 1; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return 2; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return 2; + return 0xff; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the last reference given the first two. + * \param vref0 [in] the first vertex reference + * \param vref1 [in] the second vertex reference + * \return the last reference, or INVALID_ID if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +dTriIndex IndexedTriangle::OppositeVertex(dTriIndex vref0, dTriIndex vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return mVRef[2]; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return mVRef[2]; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return mVRef[1]; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return mVRef[1]; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return mVRef[0]; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return mVRef[0]; + return (dTriIndex)INVALID_ID; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the three sorted vertex references according to an edge number. + * edgenb = 0 => edge 0-1, returns references 0, 1, 2 + * edgenb = 1 => edge 0-2, returns references 0, 2, 1 + * edgenb = 2 => edge 1-2, returns references 1, 2, 0 + * + * \param edgenb [in] the edge number, 0, 1 or 2 + * \param vref0 [out] the returned first vertex reference + * \param vref1 [out] the returned second vertex reference + * \param vref2 [out] the returned third vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::GetVRefs(ubyte edgenb, dTriIndex& vref0, dTriIndex& vref1, dTriIndex& vref2) const +{ + if(edgenb==0) + { + vref0 = mVRef[0]; + vref1 = mVRef[1]; + vref2 = mVRef[2]; + } + else if(edgenb==1) + { + vref0 = mVRef[0]; + vref1 = mVRef[2]; + vref2 = mVRef[1]; + } + else if(edgenb==2) + { + vref0 = mVRef[1]; + vref1 = mVRef[2]; + vref2 = mVRef[0]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \param verts [in] the list of indexed vertices + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MinEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Min = MAX_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \param verts [in] the list of indexed vertices + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MaxEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Max = MIN_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param verts [in] the list of indexed vertices + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::ComputePoint(const Point* verts, float u, float v, Point& pt, dTriIndex* nearvtx) const +{ + // Checkings + if(!verts) return; + + // Get face in local or global space + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute point coordinates + pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(p0.SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + p1.SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + p2.SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = mVRef[d.SmallestAxis()]; + } +} + + //************************************** + // Angle between two vectors (in radians) + // we use this formula + // uv = |u||v| cos(u,v) + // u ^ v = w + // |w| = |u||v| |sin(u,v)| + //************************************** + float Angle(const Point& u, const Point& v) + { + float NormU = u.Magnitude(); // |u| + float NormV = v.Magnitude(); // |v| + float Product = NormU*NormV; // |u||v| + if(Product==0.0f) return 0.0f; + float OneOverProduct = 1.0f / Product; + + // Cosinus + float Cosinus = (u|v) * OneOverProduct; + + // Sinus + Point w = u^v; + float NormW = w.Magnitude(); + + float AbsSinus = NormW * OneOverProduct; + + // Remove degeneracy + if(AbsSinus > 1.0f) AbsSinus = 1.0f; + if(AbsSinus < -1.0f) AbsSinus = -1.0f; + + if(Cosinus>=0.0f) return asinf(AbsSinus); + else return (PI-asinf(AbsSinus)); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the angle between two triangles. + * \param tri [in] the other triangle + * \param verts [in] the list of indexed vertices + * \return the angle in radians + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Angle(const IndexedTriangle& tri, const Point* verts) const +{ + // Checkings + if(!verts) return 0.0f; + + // Compute face normals + Point n0, n1; + Normal(verts, n0); + tri.Normal(verts, n1); + + // Compute angle + float dp = n0|n1; + if(dp>1.0f) return 0.0f; + if(dp<-1.0f) return PI; + return acosf(dp); + +// return ::Angle(n0,n1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a triangle is the same as another one. + * \param tri [in] the other triangle + * \return true if same triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::Equal(const IndexedTriangle& tri) const +{ + // Test all vertex references + return (HasVertex(tri.mVRef[0]) && + HasVertex(tri.mVRef[1]) && + HasVertex(tri.mVRef[2])); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.h new file mode 100644 index 0000000..d545e41 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceIndexedTriangle.h @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "ode/common.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEINDEXEDTRIANGLE_H__ +#define __ICEINDEXEDTRIANGLE_H__ + + // Forward declarations +#ifdef _MSC_VER + enum CubeIndex; +#else + typedef int CubeIndex; +#endif + + // An indexed triangle class. + class ICEMATHS_API IndexedTriangle + { + public: + + //! Constructor + inline_ IndexedTriangle() {} + //! Constructor + inline_ IndexedTriangle(dTriIndex r0, dTriIndex r1, dTriIndex r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; } + //! Copy constructor + inline_ IndexedTriangle(const IndexedTriangle& triangle) + { + mVRef[0] = triangle.mVRef[0]; + mVRef[1] = triangle.mVRef[1]; + mVRef[2] = triangle.mVRef[2]; + } + //! Destructor + inline_ ~IndexedTriangle() {} + + //! Vertex-references + dTriIndex mVRef[3]; + + // Methods + void Flip(); + float Area(const Point* verts) const; + float Perimeter(const Point* verts) const; + float Compacity(const Point* verts) const; + void Normal(const Point* verts, Point& normal) const; + void DenormalizedNormal(const Point* verts, Point& normal) const; + void Center(const Point* verts, Point& center) const; + void CenteredNormal(const Point* verts, Point& normal) const; + void RandomPoint(const Point* verts, Point& random) const; + bool IsVisible(const Point* verts, const Point& source) const; + bool BackfaceCulling(const Point* verts, const Point& source) const; + float ComputeOcclusionPotential(const Point* verts, const Point& view) const; + bool ReplaceVertex(dTriIndex oldref, dTriIndex newref); + bool IsDegenerate() const; + bool HasVertex(dTriIndex ref) const; + bool HasVertex(dTriIndex ref, dTriIndex* index) const; + ubyte FindEdge(dTriIndex vref0, dTriIndex vref1) const; + dTriIndex OppositeVertex(dTriIndex vref0, dTriIndex vref1) const; + inline_ dTriIndex OppositeVertex(ubyte edgenb) const { return mVRef[2-edgenb]; } + void GetVRefs(ubyte edgenb, dTriIndex& vref0, dTriIndex& vref1, dTriIndex& vref2) const; + float MinEdgeLength(const Point* verts) const; + float MaxEdgeLength(const Point* verts) const; + void ComputePoint(const Point* verts, float u, float v, Point& pt, dTriIndex* nearvtx=null) const; + float Angle(const IndexedTriangle& tri, const Point* verts) const; + inline_ Plane PlaneEquation(const Point* verts) const { return Plane(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); } + bool Equal(const IndexedTriangle& tri) const; + CubeIndex ComputeCubeIndex(const Point* verts) const; + }; + +#endif // __ICEINDEXEDTRIANGLE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceLSS.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceLSS.h new file mode 100644 index 0000000..bd260c1 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceLSS.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for line-swept spheres. + * \file IceLSS.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICELSS_H__ +#define __ICELSS_H__ + + class ICEMATHS_API LSS : public Segment + { + public: + //! Constructor + inline_ LSS() {} + //! Constructor + inline_ LSS(const Segment& seg, float radius) : Segment(seg), mRadius(radius) {} + //! Destructor + inline_ ~LSS() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an OBB surrounding the LSS. + * \param box [out] the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeOBB(OBB& box); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the LSS. + * \param pt [in] the point to test + * \return true if inside the LSS + * \warning point and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Point& pt) const { return SquareDistance(pt) <= mRadius*mRadius; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a sphere is contained within the LSS. + * \param sphere [in] the sphere to test + * \return true if inside the LSS + * \warning sphere and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Sphere& sphere) + { + float d = mRadius - sphere.mRadius; + if(d>=0.0f) return SquareDistance(sphere.mCenter) <= d*d; + else return false; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if an LSS is contained within the LSS. + * \param lss [in] the LSS to test + * \return true if inside the LSS + * \warning both LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const LSS& lss) + { + // We check the LSS contains the two spheres at the start and end of the sweep + return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius)); + } + + float mRadius; //!< Sphere radius + }; + +#endif // __ICELSS_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.cpp new file mode 100644 index 0000000..af56d3e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.cpp @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 3x3 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 first row. + * m21 m22 m23 second row. + * m31 m32 m33 third row. + * Stored in memory as m11 m12 m13 m21... + * + * Multiplication rules: + * + * [x'y'z'] = [xyz][M] + * + * x' = x*m11 + y*m21 + z*m31 + * y' = x*m12 + y*m22 + z*m32 + * z' = x*m13 + y*m23 + z*m33 + * + * \class Matrix3x3 + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +// Cast operator +Matrix3x3::operator Matrix4x4() const +{ + return Matrix4x4( + m[0][0], m[0][1], m[0][2], 0.0f, + m[1][0], m[1][1], m[1][2], 0.0f, + m[2][0], m[2][1], m[2][2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.h new file mode 100644 index 0000000..e3b950f --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix3x3.h @@ -0,0 +1,499 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX3X3_H__ +#define __ICEMATRIX3X3_H__ + + // Forward declarations + class Quat; + + #define MATRIX3X3_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix3x3 + { + public: + //! Empty constructor + inline_ Matrix3x3() {} + //! Constructor from 9 values + inline_ Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + //! Copy constructor + inline_ Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); } + //! Destructor + inline_ ~Matrix3x3() {} + + //! Assign values + template + inline_ void Set(trotationfloat m00, trotationfloat m01, trotationfloat m02, + trotationfloat m10, trotationfloat m11, trotationfloat m12, + trotationfloat m20, trotationfloat m21, trotationfloat m22) + { + m[0][0] = (float)m00; m[0][1] = (float)m01; m[0][2] = (float)m02; + m[1][0] = (float)m10; m[1][1] = (float)m11; m[1][2] = (float)m12; + m[2][0] = (float)m20; m[2][1] = (float)m21; m[2][2] = (float)m22; + } + + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; } + + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; } + + //! Scales from a Point. Each row is multiplied by a component. + inline_ void Scale(const Point& p) + { + m[0][0] *= p.x; m[0][1] *= p.x; m[0][2] *= p.x; + m[1][0] *= p.y; m[1][1] *= p.y; m[1][2] *= p.y; + m[2][0] *= p.z; m[2][1] *= p.z; m[2][2] *= p.z; + } + + //! Scales from floats. Each row is multiplied by a value. + inline_ void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[0][1] *= sx; m[0][2] *= sx; + m[1][0] *= sy; m[1][1] *= sy; m[1][2] *= sy; + m[2][0] *= sz; m[2][1] *= sz; m[2][2] *= sz; + } + + //! Copy from a Matrix3x3 + inline_ void Copy(const Matrix3x3& source) { CopyMemory(m, source.m, 9*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x = m[r][0]; p.y = m[r][1]; p.z = m[r][2]; } + //! Returns a row. + inline_ const Point& GetRow(const udword r) const { return *(const Point*)&m[r][0]; } + //! Returns a row. + inline_ Point& GetRow(const udword r) { return *(Point*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0] = p.x; m[r][1] = p.y; m[r][2] = p.z; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x = m[0][c]; p.y = m[1][c]; p.z = m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c] = p.x; m[1][c] = p.y; m[2][c] = p.z; } + + //! Computes the trace. The trace is the sum of the 3 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<3;j++) + { + for(udword i=0;i<3;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Makes a skew-symmetric matrix (a.k.a. Star(*) Matrix) + //! [ 0.0 -a.z a.y ] + //! [ a.z 0.0 -a.x ] + //! [ -a.y a.x 0.0 ] + //! This is also called a "cross matrix" since for any vectors A and B, + //! A^B = Skew(A) * B = - B * Skew(A); + inline_ void SkewSymmetric(const Point& a) + { + m[0][0] = 0.0f; + m[0][1] = -a.z; + m[0][2] = a.y; + + m[1][0] = a.z; + m[1][1] = 0.0f; + m[1][2] = -a.x; + + m[2][0] = -a.y; + m[2][1] = a.x; + m[2][2] = 0.0f; + } + + //! Negates the matrix + inline_ void Neg() + { + m[0][0] = -m[0][0]; m[0][1] = -m[0][1]; m[0][2] = -m[0][2]; + m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; m[1][2] = -m[1][2]; + m[2][0] = -m[2][0]; m[2][1] = -m[2][1]; m[2][2] = -m[2][2]; + } + + //! Neg from another matrix + inline_ void Neg(const Matrix3x3& mat) + { + m[0][0] = -mat.m[0][0]; m[0][1] = -mat.m[0][1]; m[0][2] = -mat.m[0][2]; + m[1][0] = -mat.m[1][0]; m[1][1] = -mat.m[1][1]; m[1][2] = -mat.m[1][2]; + m[2][0] = -mat.m[2][0]; m[2][1] = -mat.m[2][1]; m[2][2] = -mat.m[2][2]; + } + + //! Add another matrix + inline_ void Add(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + } + + //! Sub another matrix + inline_ void Sub(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, const Matrix3x3& b, float s) + { + m[0][0] = a.m[0][0] + b.m[0][0] * s; + m[0][1] = a.m[0][1] + b.m[0][1] * s; + m[0][2] = a.m[0][2] + b.m[0][2] * s; + + m[1][0] = a.m[1][0] + b.m[1][0] * s; + m[1][1] = a.m[1][1] + b.m[1][1] * s; + m[1][2] = a.m[1][2] + b.m[1][2] * s; + + m[2][0] = a.m[2][0] + b.m[2][0] * s; + m[2][1] = a.m[2][1] + b.m[2][1] * s; + m[2][2] = a.m[2][2] + b.m[2][2] * s; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, float s) + { + m[0][0] += a.m[0][0] * s; m[0][1] += a.m[0][1] * s; m[0][2] += a.m[0][2] * s; + m[1][0] += a.m[1][0] * s; m[1][1] += a.m[1][1] * s; m[1][2] += a.m[1][2] * s; + m[2][0] += a.m[2][0] * s; m[2][1] += a.m[2][1] * s; m[2][2] += a.m[2][2] * s; + } + + //! this = A * s + inline_ void Mult(const Matrix3x3& a, float s) + { + m[0][0] = a.m[0][0] * s; m[0][1] = a.m[0][1] * s; m[0][2] = a.m[0][2] * s; + m[1][0] = a.m[1][0] * s; m[1][1] = a.m[1][1] * s; m[1][2] = a.m[1][2] * s; + m[2][0] = a.m[2][0] * s; m[2][1] = a.m[2][1] * s; m[2][2] = a.m[2][2] * s; + } + + inline_ void Add(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] + b.m[0][0]; m[0][1] = a.m[0][1] + b.m[0][1]; m[0][2] = a.m[0][2] + b.m[0][2]; + m[1][0] = a.m[1][0] + b.m[1][0]; m[1][1] = a.m[1][1] + b.m[1][1]; m[1][2] = a.m[1][2] + b.m[1][2]; + m[2][0] = a.m[2][0] + b.m[2][0]; m[2][1] = a.m[2][1] + b.m[2][1]; m[2][2] = a.m[2][2] + b.m[2][2]; + } + + inline_ void Sub(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] - b.m[0][0]; m[0][1] = a.m[0][1] - b.m[0][1]; m[0][2] = a.m[0][2] - b.m[0][2]; + m[1][0] = a.m[1][0] - b.m[1][0]; m[1][1] = a.m[1][1] - b.m[1][1]; m[1][2] = a.m[1][2] - b.m[1][2]; + m[2][0] = a.m[2][0] - b.m[2][0]; m[2][1] = a.m[2][1] - b.m[2][1]; m[2][2] = a.m[2][2] - b.m[2][2]; + } + + //! this = a * b + inline_ void Mult(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[1][0] + a.m[0][2] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[0][1] * b.m[1][2] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[1][2] * b.m[2][0]; + m[1][1] = a.m[1][0] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[2][1]; + m[1][2] = a.m[1][0] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[2][0] * b.m[0][1] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[2][0] * b.m[0][2] + a.m[2][1] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = transpose(a) * b + inline_ void MultAtB(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[1][0] * b.m[1][0] + a.m[2][0] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[1][0] * b.m[1][1] + a.m[2][0] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[1][0] * b.m[1][2] + a.m[2][0] * b.m[2][2]; + m[1][0] = a.m[0][1] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[2][1] * b.m[2][0]; + m[1][1] = a.m[0][1] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[2][1] * b.m[2][1]; + m[1][2] = a.m[0][1] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[2][1] * b.m[2][2]; + m[2][0] = a.m[0][2] * b.m[0][0] + a.m[1][2] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[0][2] * b.m[0][1] + a.m[1][2] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[0][2] * b.m[0][2] + a.m[1][2] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = a * transpose(b) + inline_ void MultABt(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[0][1] + a.m[0][2] * b.m[0][2]; + m[0][1] = a.m[0][0] * b.m[1][0] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[1][2]; + m[0][2] = a.m[0][0] * b.m[2][0] + a.m[0][1] * b.m[2][1] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[0][1] + a.m[1][2] * b.m[0][2]; + m[1][1] = a.m[1][0] * b.m[1][0] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[1][2]; + m[1][2] = a.m[1][0] * b.m[2][0] + a.m[1][1] * b.m[2][1] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[0][1] + a.m[2][2] * b.m[0][2]; + m[2][1] = a.m[2][0] * b.m[1][0] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[1][2]; + m[2][2] = a.m[2][0] * b.m[2][0] + a.m[2][1] * b.m[2][1] + a.m[2][2] * b.m[2][2]; + } + + //! Makes a rotation matrix mapping vector "from" to vector "to". + Matrix3x3& FromTo(const Point& from, const Point& to); + + //! Set a rotation matrix around the X axis. + //! 1 0 0 + //! RX = 0 cx sx + //! 0 -sx cx + void RotX(float angle); + //! Set a rotation matrix around the Y axis. + //! cy 0 -sy + //! RY = 0 1 0 + //! sy 0 cy + void RotY(float angle); + //! Set a rotation matrix around the Z axis. + //! cz sz 0 + //! RZ = -sz cz 0 + //! 0 0 1 + void RotZ(float angle); + //! cy sx.sy -sy.cx + //! RY.RX 0 cx sx + //! sy -sx.cy cx.cy + void RotYX(float y, float x); + + //! Make a rotation matrix about an arbitrary axis + Matrix3x3& Rot(float angle, const Point& axis); + + //! Transpose the matrix. + void Transpose() + { + TSwap(m[1][0], m[0][1]); + TSwap(m[2][0], m[0][2]); + TSwap(m[2][1], m[1][2]); + } + + //! this = Transpose(a) + void Transpose(const Matrix3x3& a) + { + m[0][0] = a.m[0][0]; m[0][1] = a.m[1][0]; m[0][2] = a.m[2][0]; + m[1][0] = a.m[0][1]; m[1][1] = a.m[1][1]; m[1][2] = a.m[2][1]; + m[2][0] = a.m[0][2]; m[2][1] = a.m[1][2]; m[2][2] = a.m[2][2]; + } + + //! Compute the determinant of the matrix. We use the rule of Sarrus. + float Determinant() const + { + return (m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1]) + - (m[2][0]*m[1][1]*m[0][2] + m[2][1]*m[1][2]*m[0][0] + m[2][2]*m[1][0]*m[0][1]); + } +/* + //! Compute a cofactor. Used for matrix inversion. + float CoFactor(ubyte row, ubyte column) const + { + static const sdword gIndex[3+2] = { 0, 1, 2, 0, 1 }; + return (m[gIndex[row+1]][gIndex[column+1]]*m[gIndex[row+2]][gIndex[column+2]] - m[gIndex[row+2]][gIndex[column+1]]*m[gIndex[row+1]][gIndex[column+2]]); + } +*/ + //! Invert the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix3x3& Invert() + { + float Det = Determinant(); // Must be !=0 + float OneOverDet = 1.0f / Det; + + Matrix3x3 Temp; + Temp.m[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDet; + Temp.m[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDet; + Temp.m[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDet; + Temp.m[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDet; + Temp.m[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDet; + Temp.m[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDet; + Temp.m[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDet; + Temp.m[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDet; + Temp.m[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDet; + + *this = Temp; + + return *this; + } + + Matrix3x3& Normalize(); + + //! this = exp(a) + Matrix3x3& Exp(const Matrix3x3& a); + +void FromQuat(const Quat &q); +void FromQuatL2(const Quat &q, float l2); + + // Arithmetic operators + //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3; + inline_ Matrix3x3 operator+(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2], + m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2], + m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]); + } + + //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3; + inline_ Matrix3x3 operator-(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2], + m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2], + m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]); + } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3; + inline_ Matrix3x3 operator*(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]); + } + + //! Operator for Point Mul = Matrix3x3 * Point; + inline_ Point operator*(const Point& v) const { return Point(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v); } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * float; + inline_ Matrix3x3 operator*(float s) const + { + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Mul = float * Matrix3x3; + inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]); + } + + //! Operator for Matrix3x3 Div = Matrix3x3 / float; + inline_ Matrix3x3 operator/(float s) const + { + if (s) s = 1.0f / s; + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Div = float / Matrix3x3; + inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]); + } + + //! Operator for Matrix3x3 += Matrix3x3 + inline_ Matrix3x3& operator+=(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 -= Matrix3x3 + inline_ Matrix3x3& operator-=(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= Matrix3x3 + inline_ Matrix3x3& operator*=(const Matrix3x3& mat) + { + Point TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= float + inline_ Matrix3x3& operator*=(float s) + { + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + //! Operator for Matrix3x3 /= float + inline_ Matrix3x3& operator/=(float s) + { + if (s) s = 1.0f / s; + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + // Cast operators + //! Cast a Matrix3x3 to a Matrix4x4. + operator Matrix4x4() const; + //! Cast a Matrix3x3 to a Quat. + operator Quat() const; + + inline_ const Point& operator[](int row) const { return *(const Point*)&m[row][0]; } + inline_ Point& operator[](int row) { return *(Point*)&m[row][0]; } + + public: + + float m[3][3]; + }; + +#endif // __ICEMATRIX3X3_H__ + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.cpp new file mode 100644 index 0000000..0b258f0 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.cpp @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 4x4 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 m14 first row. + * m21 m22 m23 m24 second row. + * m31 m32 m33 m34 third row. + * m41 m42 m43 m44 fourth row. + * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1). + * Stored in memory as m11 m12 m13 m14 m21... + * + * Multiplication rules: + * + * [x'y'z'1] = [xyz1][M] + * + * x' = x*m11 + y*m21 + z*m31 + m41 + * y' = x*m12 + y*m22 + z*m32 + m42 + * z' = x*m13 + y*m23 + z*m33 + m43 + * 1' = 0 + 0 + 0 + m44 + * + * \class Matrix4x4 + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Inverts a PR matrix. (which only contains a rotation and a translation) + * This is faster and less subject to FPU errors than the generic inversion code. + * + * \relates Matrix4x4 + * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) + * \param dest [out] destination matrix + * \param src [in] source matrix + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) +{ + dest.m[0][0] = src.m[0][0]; + dest.m[1][0] = src.m[0][1]; + dest.m[2][0] = src.m[0][2]; + dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]); + + dest.m[0][1] = src.m[1][0]; + dest.m[1][1] = src.m[1][1]; + dest.m[2][1] = src.m[1][2]; + dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]); + + dest.m[0][2] = src.m[2][0]; + dest.m[1][2] = src.m[2][1]; + dest.m[2][2] = src.m[2][2]; + dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]); + + dest.m[0][3] = 0.0f; + dest.m[1][3] = 0.0f; + dest.m[2][3] = 0.0f; + dest.m[3][3] = 1.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the cofactor of the Matrix at a specified location +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::CoFactor(udword row, udword col) const +{ + return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] + + m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] + + m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3]) + - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] + + m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] + + m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the determinant of the Matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::Determinant() const +{ + return m[0][0] * CoFactor(0, 0) + + m[0][1] * CoFactor(0, 1) + + m[0][2] * CoFactor(0, 2) + + m[0][3] * CoFactor(0, 3); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the inverse of the matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Matrix4x4& Matrix4x4::Invert() +{ + float Det = Determinant(); + Matrix4x4 Temp; + + if(fabsf(Det) < MATRIX4X4_EPSILON) + return *this; // The matrix is not invertible! Singular case! + + float IDet = 1.0f / Det; + + Temp.m[0][0] = CoFactor(0,0) * IDet; + Temp.m[1][0] = CoFactor(0,1) * IDet; + Temp.m[2][0] = CoFactor(0,2) * IDet; + Temp.m[3][0] = CoFactor(0,3) * IDet; + Temp.m[0][1] = CoFactor(1,0) * IDet; + Temp.m[1][1] = CoFactor(1,1) * IDet; + Temp.m[2][1] = CoFactor(1,2) * IDet; + Temp.m[3][1] = CoFactor(1,3) * IDet; + Temp.m[0][2] = CoFactor(2,0) * IDet; + Temp.m[1][2] = CoFactor(2,1) * IDet; + Temp.m[2][2] = CoFactor(2,2) * IDet; + Temp.m[3][2] = CoFactor(2,3) * IDet; + Temp.m[0][3] = CoFactor(3,0) * IDet; + Temp.m[1][3] = CoFactor(3,1) * IDet; + Temp.m[2][3] = CoFactor(3,2) * IDet; + Temp.m[3][3] = CoFactor(3,3) * IDet; + + *this = Temp; + + return *this; +} + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.h new file mode 100644 index 0000000..e2db104 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMatrix4x4.h @@ -0,0 +1,457 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX4X4_H__ +#define __ICEMATRIX4X4_H__ + + // Forward declarations + class PRS; + class PR; + + #define MATRIX4X4_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix4x4 + { +// void LUBackwardSubstitution( sdword *indx, float* b ); +// void LUDecomposition( sdword* indx, float* d ); + + public: + //! Empty constructor. + inline_ Matrix4x4() {} + //! Constructor from 16 values + inline_ Matrix4x4( float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + } + //! Copy constructor + inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } + //! Destructor. + inline_ ~Matrix4x4() {} + + //! Assign values (rotation only) + template + inline_ Matrix4x4& Set( trotationfloat m00, trotationfloat m01, trotationfloat m02, + trotationfloat m10, trotationfloat m11, trotationfloat m12, + trotationfloat m20, trotationfloat m21, trotationfloat m22) + { + m[0][0] = (float)m00; m[0][1] = (float)m01; m[0][2] = (float)m02; + m[1][0] = (float)m10; m[1][1] = (float)m11; m[1][2] = (float)m12; + m[2][0] = (float)m20; m[2][1] = (float)m21; m[2][2] = (float)m22; + return *this; + } + //! Assign values + template + inline_ Matrix4x4& Set( trotationfloat m00, trotationfloat m01, trotationfloat m02, textrafloat m03, + trotationfloat m10, trotationfloat m11, trotationfloat m12, textrafloat m13, + trotationfloat m20, trotationfloat m21, trotationfloat m22, textrafloat m23, + toffsetfloat m30, toffsetfloat m31, toffsetfloat m32, textrafloat m33) + { + m[0][0] = (float)m00; m[0][1] = (float)m01; m[0][2] = (float)m02; m[0][3] = (float)m03; + m[1][0] = (float)m10; m[1][1] = (float)m11; m[1][2] = (float)m12; m[1][3] = (float)m13; + m[2][0] = (float)m20; m[2][1] = (float)m21; m[2][2] = (float)m22; m[2][3] = (float)m23; + m[3][0] = (float)m30; m[3][1] = (float)m31; m[3][2] = (float)m32; m[3][3] = (float)m33; + return *this; + } + + //! Copy from a Matrix4x4 + inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; } + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; } + //! Returns a row. + inline_ const HPoint& GetRow(const udword r) const { return *(const HPoint*)&m[r][0]; } + //! Returns a row. + inline_ HPoint& GetRow(const udword r) { return *(HPoint*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; } + //! Returns a column. + inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; } + + // Translation + //! Returns the translation part of the matrix. + inline_ const HPoint& GetTrans() const { return GetRow(3); } + //! Gets the translation part of the matrix + inline_ void GetTrans(Point& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; } + //! Sets the translation part of the matrix, from a Point. + inline_ void SetTrans(const Point& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; } + //! Sets the translation part of the matrix, from a HPoint. + inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; } + //! Sets the translation part of the matrix, from floats. + inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; } + + // Scale + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; } + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; } + //! Scales from a Point. Each row is multiplied by a component. + void Scale(const Point& p) + { + m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z; + m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z; + m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z; + } + //! Scales from floats. Each row is multiplied by a value. + void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz; + m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz; + m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz; + } +/* + //! Returns a row. + inline_ HPoint GetRow(const udword row) const { return mRow[row]; } + //! Sets a row. + inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; } + //! Sets a row. + Matrix4x4& SetRow(const udword row, const Point& p) + { + m[row][0] = p.x; + m[row][1] = p.y; + m[row][2] = p.z; + m[row][3] = (row != 3) ? 0.0f : 1.0f; + return *this; + } + //! Returns a column. + HPoint GetCol(const udword col) const + { + HPoint Res; + Res.x = m[0][col]; + Res.y = m[1][col]; + Res.z = m[2][col]; + Res.w = m[3][col]; + return Res; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const HPoint& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = p.w; + return *this; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const Point& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = (col != 3) ? 0.0f : 1.0f; + return *this; + } +*/ + //! Computes the trace. The trace is the sum of the 4 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; } + //! Computes the trace of the upper 3x3 matrix. + inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + if(IR(m[0][3])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + if(IR(m[1][3])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + if(IR(m[2][3])!=0) return false; + + if(IR(m[3][0])!=0) return false; + if(IR(m[3][1])!=0) return false; + if(IR(m[3][2])!=0) return false; + if(IR(m[3][3])!=IEEE_1_0) return false; + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<4;j++) + { + for(udword i=0;i<4;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Sets a rotation matrix around the X axis. + void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; } + //! Sets a rotation matrix around the Y axis. + void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; } + //! Sets a rotation matrix around the Z axis. + void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; } + + //! Makes a rotation matrix about an arbitrary axis + Matrix4x4& Rot(float angle, Point& p1, Point& p2); + + //! Transposes the matrix. + void Transpose() + { + TSwap(m[1][0], m[0][1]); + TSwap(m[2][0], m[0][2]); + TSwap(m[3][0], m[0][3]); + TSwap(m[1][2], m[2][1]); + TSwap(m[1][3], m[3][1]); + TSwap(m[2][3], m[3][2]); + } + + //! Computes a cofactor. Used for matrix inversion. + float CoFactor(udword row, udword col) const; + //! Computes the determinant of the matrix. + float Determinant() const; + //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix4x4& Invert(); +// Matrix& ComputeAxisMatrix(Point& axis, float angle); + + // Cast operators + //! Casts a Matrix4x4 to a Matrix3x3. + inline_ operator Matrix3x3() const + { + return Matrix3x3( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + //! Casts a Matrix4x4 to a Quat. + operator Quat() const; + //! Casts a Matrix4x4 to a PR. + operator PR() const; + + // Arithmetic operators + //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4; + inline_ Matrix4x4 operator+(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]+mat.m[0][0], m[0][1]+mat.m[0][1], m[0][2]+mat.m[0][2], m[0][3]+mat.m[0][3], + m[1][0]+mat.m[1][0], m[1][1]+mat.m[1][1], m[1][2]+mat.m[1][2], m[1][3]+mat.m[1][3], + m[2][0]+mat.m[2][0], m[2][1]+mat.m[2][1], m[2][2]+mat.m[2][2], m[2][3]+mat.m[2][3], + m[3][0]+mat.m[3][0], m[3][1]+mat.m[3][1], m[3][2]+mat.m[3][2], m[3][3]+mat.m[3][3]); + } + + //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4; + inline_ Matrix4x4 operator-(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]-mat.m[0][0], m[0][1]-mat.m[0][1], m[0][2]-mat.m[0][2], m[0][3]-mat.m[0][3], + m[1][0]-mat.m[1][0], m[1][1]-mat.m[1][1], m[1][2]-mat.m[1][2], m[1][3]-mat.m[1][3], + m[2][0]-mat.m[2][0], m[2][1]-mat.m[2][1], m[2][2]-mat.m[2][2], m[2][3]-mat.m[2][3], + m[3][0]-mat.m[3][0], m[3][1]-mat.m[3][1], m[3][2]-mat.m[3][2], m[3][3]-mat.m[3][3]); + } + + //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4; + inline_ Matrix4x4 operator*(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2], + m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2], + m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2], + m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3], + + m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0], + m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1], + m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2], + m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]); + } + + //! Operator for HPoint Mul = Matrix4x4 * HPoint; + inline_ HPoint operator*(const HPoint& v) const { return HPoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v, GetRow(3)|v); } + + //! Operator for Point Mul = Matrix4x4 * Point; + inline_ Point operator*(const Point& v) const + { + return Point( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3], + m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3], + m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] ); + } + + //! Operator for Matrix4x4 Scale = Matrix4x4 * float; + inline_ Matrix4x4 operator*(float s) const + { + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Scale = float * Matrix4x4; + inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3], + s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]); + } + + //! Operator for Matrix4x4 Div = Matrix4x4 / float; + inline_ Matrix4x4 operator/(float s) const + { + if(s) s = 1.0f / s; + + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Div = float / Matrix4x4; + inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3], + s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]); + } + + //! Operator for Matrix4x4 += Matrix4x4; + inline_ Matrix4x4& operator+=(const Matrix4x4& mat) + { + m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3]; + m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3]; + m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3]; + m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 -= Matrix4x4; + inline_ Matrix4x4& operator-=(const Matrix4x4& mat) + { + m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3]; + m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3]; + m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3]; + m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 *= Matrix4x4; + Matrix4x4& operator*=(const Matrix4x4& mat) + { + HPoint TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(3, TempRow); + m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + return *this; + } + + //! Operator for Matrix4x4 *= float; + inline_ Matrix4x4& operator*=(float s) + { + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + //! Operator for Matrix4x4 /= float; + inline_ Matrix4x4& operator/=(float s) + { + if(s) s = 1.0f / s; + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + inline_ const HPoint& operator[](int row) const { return *(const HPoint*)&m[row][0]; } + inline_ HPoint& operator[](int row) { return *(HPoint*)&m[row][0]; } + + public: + + float m[4][4]; + }; + + //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix + inline_ void TransformPoint4x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix + inline_ void TransformPoint3x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); + +#endif // __ICEMATRIX4X4_H__ + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceMemoryMacros.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMemoryMacros.h new file mode 100644 index 0000000..ad25c44 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceMemoryMacros.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains all memory macros. + * \file IceMemoryMacros.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMEMORYMACROS_H__ +#define __ICEMEMORYMACROS_H__ + +#undef ZeroMemory +#undef CopyMemory +#undef MoveMemory +#undef FillMemory + + //! Clears a buffer. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \see FillMemory + //! \see StoreDwords + //! \see CopyMemory + //! \see MoveMemory + inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } + + //! Fills a buffer with a given byte. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \param val [in] the byte value + //! \see StoreDwords + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } + + //! Fills a buffer with a given dword. + //! \param addr [in] buffer address + //! \param nb [in] number of dwords to write + //! \param value [in] the dword value + //! \see FillMemory + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + //! \warning writes nb*4 bytes ! + inline_ void StoreDwords(udword* dest, udword nb, udword value) + { + while(nb--) *dest++ = value; + } + + //! Copies a buffer. + //! \param addr [in] destination buffer address + //! \param addr [in] source buffer address + //! \param size [in] buffer length + //! \see ZeroMemory + //! \see FillMemory + //! \see StoreDwords + //! \see MoveMemory + inline_ void CopyMemory(void* dest, const void* src, udword size) { memcpy(dest, src, size); } + + //! Moves a buffer. + //! \param addr [in] destination buffer address + //! \param addr [in] source buffer address + //! \param size [in] buffer length + //! \see ZeroMemory + //! \see FillMemory + //! \see StoreDwords + //! \see CopyMemory + inline_ void MoveMemory(void* dest, const void* src, udword size) { memmove(dest, src, size); } + + #define SIZEOFOBJECT sizeof(*this) //!< Gives the size of current object. Avoid some mistakes (e.g. "sizeof(this)"). + //#define CLEAROBJECT { memset(this, 0, SIZEOFOBJECT); } //!< Clears current object. Laziness is my business. HANDLE WITH CARE. + #define DELETESINGLE(x) if (x) { delete x; x = null; } //!< Deletes an instance of a class. + #define DELETEARRAY(x) if (x) { delete []x; x = null; } //!< Deletes an array. + #define SAFE_RELEASE(x) if (x) { (x)->Release(); (x) = null; } //!< Safe D3D-style release + #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release + +#ifdef __ICEERROR_H__ + #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE. +#else + #define CHECKALLOC(x) if(!x) return false; +#endif + +#endif // __ICEMEMORYMACROS_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.cpp new file mode 100644 index 0000000..0ad1ce3 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.cpp @@ -0,0 +1,324 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. + * \file IceOBB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An Oriented Bounding Box (OBB). + * \class OBB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ContainsPoint(const Point& p) const +{ + // Point in OBB test using lazy evaluation and early exits + + // Translate to box space + Point RelPoint = p - mCenter; + + // Point * mRot maps from box space to world space + // mRot * Point maps from world space to box space (what we need here) + + float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z; + if(f >= mExtents.x || f <= -mExtents.x) return false; + + f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z; + if(f >= mExtents.y || f <= -mExtents.y) return false; + + f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z; + if(f >= mExtents.z || f <= -mExtents.z) return false; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::Create(const AABB& aabb, const Matrix4x4& mat) +{ + // Note: must be coherent with Rotate() + + aabb.GetCenter(mCenter); + aabb.GetExtents(mExtents); + // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity). + + // So following what's done in Rotate: + // - x-form the center + mCenter *= mat; + // - combine rotation with identity, i.e. just use given matrix + mRot = mat; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePlanes(Plane* planes) const +{ + // Checkings + if(!planes) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + // Writes normals + planes[0].n = Axis0; + planes[1].n = -Axis0; + planes[2].n = Axis1; + planes[3].n = -Axis1; + planes[4].n = Axis2; + planes[5].n = -Axis2; + + // Compute a point on each plane + Point p0 = mCenter + Axis0 * mExtents.x; + Point p1 = mCenter - Axis0 * mExtents.x; + Point p2 = mCenter + Axis1 * mExtents.y; + Point p3 = mCenter - Axis1 * mExtents.y; + Point p4 = mCenter + Axis2 * mExtents.z; + Point p5 = mCenter - Axis2 * mExtents.z; + + // Compute d + planes[0].d = -(planes[0].n|p0); + planes[1].d = -(planes[1].n|p1); + planes[2].d = -(planes[2].n|p2); + planes[3].d = -(planes[3].n|p3); + planes[4].d = -(planes[4].n|p4); + planes[5].d = -(planes[5].n|p5); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePoints(Point* pts) const +{ + // Checkings + if(!pts) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + Axis0 *= mExtents.x; + Axis1 *= mExtents.y; + Axis2 *= mExtents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + pts[0] = mCenter - Axis0 - Axis1 - Axis2; + pts[1] = mCenter + Axis0 - Axis1 - Axis2; + pts[2] = mCenter + Axis0 + Axis1 - Axis2; + pts[3] = mCenter - Axis0 + Axis1 - Axis2; + pts[4] = mCenter - Axis0 - Axis1 + Axis2; + pts[5] = mCenter + Axis0 - Axis1 + Axis2; + pts[6] = mCenter + Axis0 + Axis1 + Axis2; + pts[7] = mCenter - Axis0 + Axis1 + Axis2; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputeVertexNormals(Point* pts) const +{ + static const float VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + if(!pts) return false; + + const Point* VN = (const Point*)VertexNormals; + for(udword i=0;i<8;i++) + { + pts[i] = VN[i] * mRot; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const udword* OBB::GetEdges() const +{ + static const udword Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns local edge normals. + * \return edge normals in local space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const Point* OBB::GetLocalEdgeNormals() const +{ + static const float EdgeNormals[] = + { + 0, -INVSQRT2, -INVSQRT2, // 0-1 + INVSQRT2, 0, -INVSQRT2, // 1-2 + 0, INVSQRT2, -INVSQRT2, // 2-3 + -INVSQRT2, 0, -INVSQRT2, // 3-0 + + 0, INVSQRT2, INVSQRT2, // 7-6 + INVSQRT2, 0, INVSQRT2, // 6-5 + 0, -INVSQRT2, INVSQRT2, // 5-4 + -INVSQRT2, 0, INVSQRT2, // 4-7 + + INVSQRT2, -INVSQRT2, 0, // 1-5 + INVSQRT2, INVSQRT2, 0, // 6-2 + -INVSQRT2, INVSQRT2, 0, // 3-7 + -INVSQRT2, -INVSQRT2, 0 // 4-0 + }; + return (const Point*)EdgeNormals; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const +{ + ASSERT(edge_index<12); + world_normal = GetLocalEdgeNormals()[edge_index] * mRot; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeLSS(LSS& lss) const +{ + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + switch(mExtents.LargestAxis()) + { + case 0: + lss.mRadius = (mExtents.y + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius); + lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius); + break; + case 1: + lss.mRadius = (mExtents.x + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius); + lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius); + break; + case 2: + lss.mRadius = (mExtents.x + mExtents.y)*0.5f; + lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius); + lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius); + break; + default: {} + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL OBB::IsInside(const OBB& box) const +{ + // Make a 4x4 from the box & inverse it + Matrix4x4 M0Inv; + { + Matrix4x4 M0 = box.mRot; + M0.SetTrans(box.mCenter); + InvertPRMatrix(M0Inv, M0); + } + + // With our inversed 4x4, create box1 in space of box0 + OBB _1in0; + Rotate(M0Inv, _1in0); + + // This should cancel out box0's rotation, i.e. it's now an AABB. + // => Center(0,0,0), Rot(identity) + + // The two boxes are in the same space so now we can compare them. + + // Create the AABB of (box1 in space of box0) + const Matrix3x3& mtx = _1in0.mRot; + + float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x; + if(f > _1in0.mCenter.x) return FALSE; + if(-f < _1in0.mCenter.x) return FALSE; + + f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y; + if(f > _1in0.mCenter.y) return FALSE; + if(-f < _1in0.mCenter.y) return FALSE; + + f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z; + if(f > _1in0.mCenter.z) return FALSE; + if(-f < _1in0.mCenter.z) return FALSE; + + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.h new file mode 100644 index 0000000..d6cf43e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceOBB.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. (oriented bounding box) + * \file IceOBB.h + * \author Pierre Terdiman + * \date January, 13, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEOBB_H__ +#define __ICEOBB_H__ + + // Forward declarations + class LSS; + + class ICEMATHS_API OBB + { + public: + //! Constructor + inline_ OBB() {} + //! Constructor + inline_ OBB(const Point& center, const Point& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot) {} + //! Destructor + inline_ ~OBB() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty OBB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() + { + mCenter.Zero(); + mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + mRot.Identity(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ContainsPoint(const Point& p) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Create(const AABB& aabb, const Matrix4x4& mat); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the OBB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param obb [out] the transformed OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const + { + // The extents remain constant + obb.mExtents = mExtents; + // The center gets x-formed + obb.mCenter = mCenter * mtx; + // Combine rotations + obb.mRot = mRot * Matrix3x3(mtx); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(mExtents.x < 0.0f) return FALSE; + if(mExtents.y < 0.0f) return FALSE; + if(mExtents.z < 0.0f) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePlanes(Plane* planes) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePoints(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputeVertexNormals(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const udword* GetEdges() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns local edge normals. + * \return edge normals in local space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const Point* GetLocalEdgeNormals() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeLSS(LSS& lss) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + BOOL IsInside(const OBB& box) const; + + inline_ const Point& GetCenter() const { return mCenter; } + inline_ const Point& GetExtents() const { return mExtents; } + inline_ const Matrix3x3& GetRot() const { return mRot; } + + inline_ void GetRotatedExtents(Matrix3x3& extents) const + { + extents = mRot; + extents.Scale(mExtents); + } + + Point mCenter; //!< B for Box + Point mExtents; //!< B for Bounding + Matrix3x3 mRot; //!< O for Oriented + + // Orientation is stored in row-major format, + // i.e. rows = eigen vectors of the covariance matrix + }; + +#endif // __ICEOBB_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePairs.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePairs.h new file mode 100644 index 0000000..2c09b92 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePairs.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple pair class. + * \file IcePairs.h + * \author Pierre Terdiman + * \date January, 13, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPAIRS_H__ +#define __ICEPAIRS_H__ + + //! A generic couple structure + struct ICECORE_API Pair + { + inline_ Pair() {} + inline_ Pair(udword i0, udword i1) : id0(i0), id1(i1) {} + + udword id0; //!< First index of the pair + udword id1; //!< Second index of the pair + }; + + class ICECORE_API Pairs : private Container + { + public: + // Constructor / Destructor + Pairs() {} + ~Pairs() {} + + inline_ udword GetNbPairs() const { return GetNbEntries()>>1; } + inline_ const Pair* GetPairs() const { return (const Pair*)GetEntries(); } + inline_ const Pair* GetPair(udword i) const { return (const Pair*)&GetEntries()[i+i]; } + + inline_ BOOL HasPairs() const { return IsNotEmpty(); } + + inline_ void ResetPairs() { Reset(); } + inline_ void DeleteLastPair() { DeleteLastEntry(); DeleteLastEntry(); } + + inline_ void AddPair(const Pair& p) { Add(p.id0).Add(p.id1); } + inline_ void AddPair(udword id0, udword id1) { Add(id0).Add(id1); } + }; + +#endif // __ICEPAIRS_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.cpp new file mode 100644 index 0000000..394b31b --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.cpp @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for planes. + * \file IcePlane.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Plane class. + * \class Plane + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the plane equation from 3 points. + * \param p0 [in] first point + * \param p1 [in] second point + * \param p2 [in] third point + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Plane& Plane::Set(const Point& p0, const Point& p1, const Point& p2) +{ + Point Edge0 = p1 - p0; + Point Edge1 = p2 - p0; + + n = Edge0 ^ Edge1; + n.Normalize(); + + d = -(p0 | n); + + return *this; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.h new file mode 100644 index 0000000..4d47081 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePlane.h @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for planes. + * \file IcePlane.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPLANE_H__ +#define __ICEPLANE_H__ + + #define PLANE_EPSILON (1.0e-7f) + + class ICEMATHS_API Plane + { + public: + //! Constructor + inline_ Plane() { } + //! Constructor from a normal and a distance + inline_ Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); } + //! Constructor from a point on the plane and a normal + inline_ Plane(const Point& p, const Point& n) { Set(p, n); } + //! Constructor from three points + inline_ Plane(const Point& p0, const Point& p1, const Point& p2) { Set(p0, p1, p2); } + //! Constructor from a normal and a distance + inline_ Plane(const Point& _n, float _d) { n = _n; d = _d; } + //! Copy constructor + inline_ Plane(const Plane& plane) : n(plane.n), d(plane.d) { } + //! Destructor + inline_ ~Plane() { } + + inline_ Plane& Zero() { n.Zero(); d = 0.0f; return *this; } + inline_ Plane& Set(float nx, float ny, float nz, float _d) { n.Set(nx, ny, nz); d = _d; return *this; } + inline_ Plane& Set(const Point& p, const Point& _n) { n = _n; d = - p | _n; return *this; } + Plane& Set(const Point& p0, const Point& p1, const Point& p2); + + inline_ float Distance(const Point& p) const { return (p | n) + d; } + inline_ bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; } + + inline_ void Normalize() + { + float Denom = 1.0f / n.Magnitude(); + n.x *= Denom; + n.y *= Denom; + n.z *= Denom; + d *= Denom; + } + public: + // Members + Point n; //!< The normal to the plane + float d; //!< The distance from the origin + + // Cast operators + inline_ operator Point() const { return n; } + inline_ operator HPoint() const { return HPoint(n, d); } + + // Arithmetic operators + inline_ Plane operator*(const Matrix4x4& m) const + { + // Old code from Irion. Kept for reference. + Plane Ret(*this); + return Ret *= m; + } + + inline_ Plane& operator*=(const Matrix4x4& m) + { + // Old code from Irion. Kept for reference. + Point n2 = HPoint(n, 0.0f) * m; + d = -((Point) (HPoint( -d*n, 1.0f ) * m) | n2); + n = n2; + return *this; + } + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster. + * \param transformed [out] transformed plane + * \param plane [in] source plane + * \param transform [in] transform matrix + * \warning the plane normal must be unit-length + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void TransformPlane(Plane& transformed, const Plane& plane, const Matrix4x4& transform) + { + // Rotate the normal using the rotation part of the 4x4 matrix + transformed.n = plane.n * Matrix3x3(transform); + + // Compute new d + transformed.d = plane.d - (Point(transform.GetTrans())|transformed.n); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster. + * \param plane [in/out] source plane (transformed on return) + * \param transform [in] transform matrix + * \warning the plane normal must be unit-length + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void TransformPlane(Plane& plane, const Matrix4x4& transform) + { + // Rotate the normal using the rotation part of the 4x4 matrix + plane.n *= Matrix3x3(transform); + + // Compute new d + plane.d -= Point(transform.GetTrans())|plane.n; + } + +#endif // __ICEPLANE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.cpp new file mode 100644 index 0000000..428908a --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.cpp @@ -0,0 +1,191 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3D vectors. + * \file IcePoint.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 3D point. + * + * The name is "Point" instead of "Vector" since a vector is N-dimensional, whereas a point is an implicit "vector of dimension 3". + * So the choice was between "Point" and "Vector3", the first one looked better (IMHO). + * + * Some people, then, use a typedef to handle both points & vectors using the same class: typedef Point Vector3; + * This is bad since it opens the door to a lot of confusion while reading the code. I know it may sounds weird but check this out: + * + * \code + * Point P0,P1 = some 3D points; + * Point Delta = P1 - P0; + * \endcode + * + * This compiles fine, although you should have written: + * + * \code + * Point P0,P1 = some 3D points; + * Vector3 Delta = P1 - P0; + * \endcode + * + * Subtle things like this are not caught at compile-time, and when you find one in the code, you never know whether it's a mistake + * from the author or something you don't get. + * + * One way to handle it at compile-time would be to use different classes for Point & Vector3, only overloading operator "-" for vectors. + * But then, you get a lot of redundant code in thoses classes, and basically it's really a lot of useless work. + * + * Another way would be to use homogeneous points: w=1 for points, w=0 for vectors. That's why the HPoint class exists. Now, to store + * your model's vertices and in most cases, you really want to use Points to save ram. + * + * \class Point + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a positive unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::PositiveUnitRandomVector() +{ + x = UnitRandomFloat(); + y = UnitRandomFloat(); + z = UnitRandomFloat(); + Normalize(); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::UnitRandomVector() +{ + x = UnitRandomFloat() - 0.5f; + y = UnitRandomFloat() - 0.5f; + z = UnitRandomFloat() - 0.5f; + Normalize(); + return *this; +} + +// Cast operator +// WARNING: not inlined +Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); } + +Point& Point::Refract(const Point& eye, const Point& n, float refractindex, Point& refracted) +{ + // Point EyePt = eye position + // Point p = current vertex + // Point n = vertex normal + // Point rv = refracted vector + // Eye vector - doesn't need to be normalized + Point Env; + Env.x = eye.x - x; + Env.y = eye.y - y; + Env.z = eye.z - z; + + float NDotE = n|Env; + float NDotN = n|n; + NDotE /= refractindex; + + // Refracted vector + refracted = n*NDotE - Env*NDotN; + + return *this; +} + +Point& Point::ProjectToPlane(const Plane& p) +{ + *this-= (p.d + (*this|p.n))*p.n; + return *this; +} + +void Point::ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const +{ + projected = HPoint(x, y, z, 1.0f) * mat; + projected.w = 1.0f / projected.w; + + projected.x*=projected.w; + projected.y*=projected.w; + projected.z*=projected.w; + + projected.x *= halfrenderwidth; projected.x += halfrenderwidth; + projected.y *= -halfrenderheight; projected.y += halfrenderheight; +} + +void Point::SetNotUsed() +{ + // We use a particular integer pattern : 0xffffffff everywhere. This is a NAN. + x = y = z = FR(0xffffffff); +} + +BOOL Point::IsNotUsed() const +{ + if(IR(x)!=0xffffffff) return FALSE; + if(IR(y)!=0xffffffff) return FALSE; + if(IR(z)!=0xffffffff) return FALSE; + return TRUE; +} + +Point& Point::Mult(const Matrix3x3& mat, const Point& a) +{ + x = a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2]; + y = a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2]; + z = a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2) +{ + x = a1.x * mat1.m[0][0] + a1.y * mat1.m[0][1] + a1.z * mat1.m[0][2] + a2.x * mat2.m[0][0] + a2.y * mat2.m[0][1] + a2.z * mat2.m[0][2]; + y = a1.x * mat1.m[1][0] + a1.y * mat1.m[1][1] + a1.z * mat1.m[1][2] + a2.x * mat2.m[1][0] + a2.y * mat2.m[1][1] + a2.z * mat2.m[1][2]; + z = a1.x * mat1.m[2][0] + a1.y * mat1.m[2][1] + a1.z * mat1.m[2][2] + a2.x * mat2.m[2][0] + a2.y * mat2.m[2][1] + a2.z * mat2.m[2][2]; + return *this; +} + +Point& Point::Mac(const Matrix3x3& mat, const Point& a) +{ + x += a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2]; + y += a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2]; + z += a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::TransMult(const Matrix3x3& mat, const Point& a) +{ + x = a.x * mat.m[0][0] + a.y * mat.m[1][0] + a.z * mat.m[2][0]; + y = a.x * mat.m[0][1] + a.y * mat.m[1][1] + a.z * mat.m[2][1]; + z = a.x * mat.m[0][2] + a.y * mat.m[1][2] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos) +{ + x = r.x * rotpos.m[0][0] + r.y * rotpos.m[0][1] + r.z * rotpos.m[0][2] + linpos.x; + y = r.x * rotpos.m[1][0] + r.y * rotpos.m[1][1] + r.z * rotpos.m[1][2] + linpos.y; + z = r.x * rotpos.m[2][0] + r.y * rotpos.m[2][1] + r.z * rotpos.m[2][2] + linpos.z; + return *this; +} + +Point& Point::InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos) +{ + float sx = r.x - linpos.x; + float sy = r.y - linpos.y; + float sz = r.z - linpos.z; + x = sx * rotpos.m[0][0] + sy * rotpos.m[1][0] + sz * rotpos.m[2][0]; + y = sx * rotpos.m[0][1] + sy * rotpos.m[1][1] + sz * rotpos.m[2][1]; + z = sx * rotpos.m[0][2] + sy * rotpos.m[1][2] + sz * rotpos.m[2][2]; + return *this; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.h new file mode 100644 index 0000000..7c8a931 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePoint.h @@ -0,0 +1,530 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3D vectors. + * \file IcePoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPOINT_H__ +#define __ICEPOINT_H__ + + // Forward declarations + class HPoint; + class Plane; + class Matrix3x3; + class Matrix4x4; + + #define CROSS2D(a, b) (a.x*b.y - b.x*a.y) + + const float EPSILON2 = 1.0e-20f; + + class ICEMATHS_API Point + { + public: + + //! Empty constructor + inline_ Point() {} + //! Constructor from a single float +// inline_ Point(float val) : x(val), y(val), z(val) {} +// Removed since it introduced the nasty "Point T = *Matrix4x4.GetTrans();" bug....... + //! Constructor from floats + template + inline_ Point(toffsetfloat xx, toffsetfloat yy, toffsetfloat zz) : x((float)xx), y((float)yy), z((float)zz) {} + //! Constructor from array + inline_ Point(const float f[3]) : x(f[X]), y(f[Y]), z(f[Z]) {} + //! Copy constructor + inline_ Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} + //! Destructor + inline_ ~Point() {} + + //! Clears the vector + inline_ Point& Zero() { x = y = z = 0.0f; return *this; } + + //! + infinity + inline_ Point& SetPlusInfinity() { x = y = z = MAX_FLOAT; return *this; } + //! - infinity + inline_ Point& SetMinusInfinity() { x = y = z = MIN_FLOAT; return *this; } + + //! Sets positive unit random vector + Point& PositiveUnitRandomVector(); + //! Sets unit random vector + Point& UnitRandomVector(); + + //! Assignment from values + template + inline_ Point& Set(toffsetfloat xx, toffsetfloat yy, toffsetfloat zz) { x = (float)xx; y = (float)yy; z = (float)zz; return *this; } + //! Assignment from array + inline_ Point& Set(const float f[3]) { x = f[X]; y = f[Y]; z = f[Z]; return *this; } + //! Assignment from another point + inline_ Point& Set(const Point& src) { x = src.x; y = src.y; z = src.z; return *this; } + + //! Adds a vector + inline_ Point& Add(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Adds a vector + inline_ Point& Add(float xx, float yy, float zz) { x += xx; y += yy; z += zz; return *this; } + //! Adds a vector + inline_ Point& Add(const float f[3]) { x += f[X]; y += f[Y]; z += f[Z]; return *this; } + //! Adds vectors + inline_ Point& Add(const Point& p, const Point& q) { x = p.x+q.x; y = p.y+q.y; z = p.z+q.z; return *this; } + + //! Subtracts a vector + inline_ Point& Sub(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Subtracts a vector + inline_ Point& Sub(float xx, float yy, float zz) { x -= xx; y -= yy; z -= zz; return *this; } + //! Subtracts a vector + inline_ Point& Sub(const float f[3]) { x -= f[X]; y -= f[Y]; z -= f[Z]; return *this; } + //! Subtracts vectors + inline_ Point& Sub(const Point& p, const Point& q) { x = p.x-q.x; y = p.y-q.y; z = p.z-q.z; return *this; } + + //! this = -this + inline_ Point& Neg() { x = -x; y = -y; z = -z; return *this; } + //! this = -a + inline_ Point& Neg(const Point& a) { x = -a.x; y = -a.y; z = -a.z; return *this; } + + //! Multiplies by a scalar + inline_ Point& Mult(float s) { x *= s; y *= s; z *= s; return *this; } + + //! this = a * scalar + inline_ Point& Mult(const Point& a, float scalar) + { + x = a.x * scalar; + y = a.y * scalar; + z = a.z * scalar; + return *this; + } + + //! this = a + b * scalar + inline_ Point& Mac(const Point& a, const Point& b, float scalar) + { + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; + z = a.z + b.z * scalar; + return *this; + } + + //! this = this + a * scalar + inline_ Point& Mac(const Point& a, float scalar) + { + x += a.x * scalar; + y += a.y * scalar; + z += a.z * scalar; + return *this; + } + + //! this = a - b * scalar + inline_ Point& Msc(const Point& a, const Point& b, float scalar) + { + x = a.x - b.x * scalar; + y = a.y - b.y * scalar; + z = a.z - b.z * scalar; + return *this; + } + + //! this = this - a * scalar + inline_ Point& Msc(const Point& a, float scalar) + { + x -= a.x * scalar; + y -= a.y * scalar; + z -= a.z * scalar; + return *this; + } + + //! this = a + b * scalarb + c * scalarc + inline_ Point& Mac2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x + b.x * scalarb + c.x * scalarc; + y = a.y + b.y * scalarb + c.y * scalarc; + z = a.z + b.z * scalarb + c.z * scalarc; + return *this; + } + + //! this = a - b * scalarb - c * scalarc + inline_ Point& Msc2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x - b.x * scalarb - c.x * scalarc; + y = a.y - b.y * scalarb - c.y * scalarc; + z = a.z - b.z * scalarb - c.z * scalarc; + return *this; + } + + //! this = mat * a + inline_ Point& Mult(const Matrix3x3& mat, const Point& a); + + //! this = mat1 * a1 + mat2 * a2 + inline_ Point& Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2); + + //! this = this + mat * a + inline_ Point& Mac(const Matrix3x3& mat, const Point& a); + + //! this = transpose(mat) * a + inline_ Point& TransMult(const Matrix3x3& mat, const Point& a); + + //! Linear interpolate between two vectors: this = a + t * (b - a) + inline_ Point& Lerp(const Point& a, const Point& b, float t) + { + x = a.x + t * (b.x - a.x); + y = a.y + t * (b.y - a.y); + z = a.z + t * (b.z - a.z); + return *this; + } + + //! Hermite interpolate between p1 and p2. p0 and p3 are used for finding gradient at p1 and p2. + //! this = p0 * (2t^2 - t^3 - t)/2 + //! + p1 * (3t^3 - 5t^2 + 2)/2 + //! + p2 * (4t^2 - 3t^3 + t)/2 + //! + p3 * (t^3 - t^2)/2 + inline_ Point& Herp(const Point& p0, const Point& p1, const Point& p2, const Point& p3, float t) + { + float t2 = t * t; + float t3 = t2 * t; + float kp0 = (2.0f * t2 - t3 - t) * 0.5f; + float kp1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f; + float kp2 = (4.0f * t2 - 3.0f * t3 + t) * 0.5f; + float kp3 = (t3 - t2) * 0.5f; + x = p0.x * kp0 + p1.x * kp1 + p2.x * kp2 + p3.x * kp3; + y = p0.y * kp0 + p1.y * kp1 + p2.y * kp2 + p3.y * kp3; + z = p0.z * kp0 + p1.z * kp1 + p2.z * kp2 + p3.z * kp3; + return *this; + } + + //! this = rotpos * r + linpos + inline_ Point& Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! this = trans(rotpos) * (r - linpos) + inline_ Point& InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! Returns MIN(x, y, z); + inline_ float Min() const { return MIN(x, MIN(y, z)); } + //! Returns MAX(x, y, z); + inline_ float Max() const { return MAX(x, MAX(y, z)); } + //! Sets each element to be componentwise minimum + inline_ Point& Min(const Point& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; } + //! Sets each element to be componentwise maximum + inline_ Point& Max(const Point& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; } + + //! Clamps each element + inline_ Point& Clamp(float min, float max) + { + if(xmax) x=max; + if(ymax) y=max; + if(zmax) z=max; + return *this; + } + + //! Computes square magnitude + inline_ float SquareMagnitude() const { return x*x + y*y + z*z; } + //! Computes magnitude + inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z); } + //! Computes volume + inline_ float Volume() const { return x * y * z; } + + //! Checks the point is near zero + inline_ bool ApproxZero() const { return SquareMagnitude() < EPSILON2; } + + //! Tests for exact zero vector + inline_ BOOL IsZero() const + { + if(IR(x) || IR(y) || IR(z)) return FALSE; + return TRUE; + } + + //! Checks point validity + inline_ BOOL IsValid() const + { + if(!IsValidFloat(x)) return FALSE; + if(!IsValidFloat(y)) return FALSE; + if(!IsValidFloat(z)) return FALSE; + return TRUE; + } + + //! Slighty moves the point + void Tweak(udword coord_mask, udword tweak_mask) + { + if(coord_mask&1) { udword Dummy = IR(x); Dummy^=tweak_mask; x = FR(Dummy); } + if(coord_mask&2) { udword Dummy = IR(y); Dummy^=tweak_mask; y = FR(Dummy); } + if(coord_mask&4) { udword Dummy = IR(z); Dummy^=tweak_mask; z = FR(Dummy); } + } + + #define TWEAKMASK 0x3fffff + #define TWEAKNOTMASK ~TWEAKMASK + //! Slighty moves the point out + inline_ void TweakBigger() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! Slighty moves the point in + inline_ void TweakSmaller() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! Normalizes the vector + inline_ Point& Normalize() + { + float M = x*x + y*y + z*z; + if(M) + { + M = 1.0f / sqrtf(M); + x *= M; + y *= M; + z *= M; + } + return *this; + } + + //! Sets vector length + inline_ Point& SetLength(float length) + { + float NewLength = length / Magnitude(); + x *= NewLength; + y *= NewLength; + z *= NewLength; + return *this; + } + + //! Clamps vector length + inline_ Point& ClampLength(float limit_length) + { + if(limit_length>=0.0f) // Magnitude must be positive + { + float CurrentSquareLength = SquareMagnitude(); + + if(CurrentSquareLength > limit_length * limit_length) + { + float Coeff = limit_length / sqrtf(CurrentSquareLength); + x *= Coeff; + y *= Coeff; + z *= Coeff; + } + } + return *this; + } + + //! Computes distance to another point + inline_ float Distance(const Point& b) const + { + return sqrtf((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Computes square distance to another point + inline_ float SquareDistance(const Point& b) const + { + return ((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Dot product dp = this|a + inline_ float Dot(const Point& p) const { return p.x * x + p.y * y + p.z * z; } + + //! Cross product this = a x b + inline_ Point& Cross(const Point& a, const Point& b) + { + x = a.y * b.z - a.z * b.y; + y = a.z * b.x - a.x * b.z; + z = a.x * b.y - a.y * b.x; + return *this; + } + + //! Vector code ( bitmask = sign(z) | sign(y) | sign(x) ) + inline_ udword VectorCode() const + { + return (IR(x)>>31) | ((IR(y)&SIGN_BITMASK)>>30) | ((IR(z)&SIGN_BITMASK)>>29); + } + + //! Returns largest axis + inline_ PointComponent LargestAxis() const + { + const float* Vals = &x; + PointComponent m = X; + if(Vals[Y] > Vals[m]) m = Y; + if(Vals[Z] > Vals[m]) m = Z; + return m; + } + + //! Returns closest axis + inline_ PointComponent ClosestAxis() const + { + const float* Vals = &x; + PointComponent m = X; + if(AIR(Vals[Y]) > AIR(Vals[m])) m = Y; + if(AIR(Vals[Z]) > AIR(Vals[m])) m = Z; + return m; + } + + //! Returns smallest axis + inline_ PointComponent SmallestAxis() const + { + const float* Vals = &x; + PointComponent m = X; + if(Vals[Y] < Vals[m]) m = Y; + if(Vals[Z] < Vals[m]) m = Z; + return m; + } + + //! Refracts the point + Point& Refract(const Point& eye, const Point& n, float refractindex, Point& refracted); + + //! Projects the point onto a plane + Point& ProjectToPlane(const Plane& p); + + //! Projects the point onto the screen + void ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const; + + //! Unfolds the point onto a plane according to edge(a,b) + Point& Unfold(Plane& p, Point& a, Point& b); + + //! Hash function from Ville Miettinen + inline_ udword GetHashValue() const + { + const udword* h = (const udword*)(this); + udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 + return (f>>22)^(f>>12)^(f); + } + + //! Stuff magic values in the point, marking it as explicitely not used. + void SetNotUsed(); + //! Checks the point is marked as not used + BOOL IsNotUsed() const; + + // Arithmetic operators + + //! Unary operator for Point Negate = - Point + inline_ Point operator-() const { return Point(-x, -y, -z); } + + //! Operator for Point Plus = Point + Point. + inline_ Point operator+(const Point& p) const { return Point(x + p.x, y + p.y, z + p.z); } + //! Operator for Point Minus = Point - Point. + inline_ Point operator-(const Point& p) const { return Point(x - p.x, y - p.y, z - p.z); } + + //! Operator for Point Mul = Point * Point. + inline_ Point operator*(const Point& p) const { return Point(x * p.x, y * p.y, z * p.z); } + //! Operator for Point Scale = Point * float. + inline_ Point operator*(float s) const { return Point(x * s, y * s, z * s ); } + //! Operator for Point Scale = float * Point. + inline_ friend Point operator*(float s, const Point& p) { return Point(s * p.x, s * p.y, s * p.z); } + + //! Operator for Point Div = Point / Point. + inline_ Point operator/(const Point& p) const { return Point(x / p.x, y / p.y, z / p.z); } + //! Operator for Point Scale = Point / float. + inline_ Point operator/(float s) const { s = 1.0f / s; return Point(x * s, y * s, z * s); } + //! Operator for Point Scale = float / Point. + inline_ friend Point operator/(float s, const Point& p) { return Point(s / p.x, s / p.y, s / p.z); } + + //! Operator for float DotProd = Point | Point. + inline_ float operator|(const Point& p) const { return x*p.x + y*p.y + z*p.z; } + //! Operator for Point VecProd = Point ^ Point. + inline_ Point operator^(const Point& p) const + { + return Point( + y * p.z - z * p.y, + z * p.x - x * p.z, + x * p.y - y * p.x ); + } + + //! Operator for Point += Point. + inline_ Point& operator+=(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Operator for Point += float. + inline_ Point& operator+=(float s) { x += s; y += s; z += s; return *this; } + + //! Operator for Point -= Point. + inline_ Point& operator-=(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Operator for Point -= float. + inline_ Point& operator-=(float s) { x -= s; y -= s; z -= s; return *this; } + + //! Operator for Point *= Point. + inline_ Point& operator*=(const Point& p) { x *= p.x; y *= p.y; z *= p.z; return *this; } + //! Operator for Point *= float. + inline_ Point& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } + + //! Operator for Point /= Point. + inline_ Point& operator/=(const Point& p) { x /= p.x; y /= p.y; z /= p.z; return *this; } + //! Operator for Point /= float. + inline_ Point& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; } + + // Logical operators + + //! Operator for "if(Point==Point)" + inline_ bool operator==(const Point& p) const { return ( (IR(x)==IR(p.x))&&(IR(y)==IR(p.y))&&(IR(z)==IR(p.z))); } + //! Operator for "if(Point!=Point)" + inline_ bool operator!=(const Point& p) const { return ( (IR(x)!=IR(p.x))||(IR(y)!=IR(p.y))||(IR(z)!=IR(p.z))); } + + // Arithmetic operators + + //! Operator for Point Mul = Point * Matrix3x3. + inline_ Point operator*(const Matrix3x3& mat) const + { + class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining + const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat; + + return Point( + x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0], + x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1], + x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] ); + } + + //! Operator for Point Mul = Point * Matrix4x4. + inline_ Point operator*(const Matrix4x4& mat) const + { + class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining + const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat; + + return Point( + x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0], + x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1], + x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]); + } + + //! Operator for Point *= Matrix3x3. + inline_ Point& operator*=(const Matrix3x3& mat) + { + class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining + const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat; + + float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0]; + float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1]; + float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2]; + + x = xp; y = yp; z = zp; + + return *this; + } + + //! Operator for Point *= Matrix4x4. + inline_ Point& operator*=(const Matrix4x4& mat) + { + class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining + const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat; + + float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0]; + float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1]; + float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]; + + x = xp; y = yp; z = zp; + + return *this; + } + + // Cast operators + + //! Cast a Point to a HPoint. w is set to zero. + operator HPoint() const; + + inline_ operator const float*() const { return &x; } + inline_ operator float*() { return &x; } + + public: + float x, y, z; + }; + + FUNCTION ICEMATHS_API void Normalize1(Point& a); + FUNCTION ICEMATHS_API void Normalize2(Point& a); + +#endif //__ICEPOINT_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IcePreprocessor.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePreprocessor.h new file mode 100644 index 0000000..dbeca38 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IcePreprocessor.h @@ -0,0 +1,132 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains preprocessor stuff. This should be the first included header. + * \file IcePreprocessor.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPREPROCESSOR_H__ +#define __ICEPREPROCESSOR_H__ + + // Check platform + #if defined( _WIN32 ) || defined( WIN32 ) + // #pragma message("Compiling on Windows...") + #define PLATFORM_WINDOWS + #else + // don't issue pragmas on unknown platforms + // #pragma message("Compiling on unknown platform...") + #endif + + // Check compiler + #if defined(_MSC_VER) + // #pragma message("Compiling with VC++...") + #define COMPILER_VISUAL_CPP + #else + // don't issue pragmas on unknown platforms + // #pragma message("Compiling with unknown compiler...") + #endif + + // Check compiler options. If this file is included in user-apps, this + // shouldn't be needed, so that they can use what they like best. + #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS + #ifdef COMPILER_VISUAL_CPP + #if defined(_CHAR_UNSIGNED) + #endif + + #if defined(_CPPRTTI) + #error Please disable RTTI... + #endif + + #if defined(_CPPUNWIND) + #error Please disable exceptions... + #endif + + #if defined(_MT) + // Multithreading + #endif + #endif + #endif + + // Check debug mode + #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it. + #ifndef _DEBUG + #define _DEBUG + #endif + #endif + + #ifdef _DEBUG + // Here you may define items for debug builds + #endif + + #ifndef THIS_FILE + #define THIS_FILE __FILE__ + #endif + + #ifndef ICE_NO_DLL + #ifdef ICECORE_EXPORTS + #define ICECORE_API __declspec(dllexport) + #else + #define ICECORE_API __declspec(dllimport) + #endif + #else + #define ICECORE_API + #endif + + // Don't override new/delete +// #define DEFAULT_NEWDELETE + #define DONT_TRACK_MEMORY_LEAKS + + #define FUNCTION extern "C" + + // Cosmetic stuff [mainly useful with multiple inheritance] + #define override(base_class) virtual + + // Our own inline keyword, so that: + // - we can switch to __forceinline to check it's really better or not + // - we can remove __forceinline if the compiler doesn't support it +// #define inline_ __forceinline +// #define inline_ inline + + // Contributed by Bruce Mitchener + #if defined(COMPILER_VISUAL_CPP) + #define inline_ __forceinline +// #define inline_ inline + #elif defined(__GNUC__) && __GNUC__ < 3 + #define inline_ inline + #elif defined(__GNUC__) + #define inline_ inline __attribute__ ((always_inline)) + #else + #define inline_ inline + #endif + + // Down the hatch +#ifdef _MSC_VER + #pragma inline_depth( 255 ) +#endif + + #ifdef COMPILER_VISUAL_CPP + #pragma intrinsic(memcmp) + #pragma intrinsic(memcpy) + #pragma intrinsic(memset) + #pragma intrinsic(strcat) + #pragma intrinsic(strcmp) + #pragma intrinsic(strcpy) + #pragma intrinsic(strlen) + #pragma intrinsic(abs) + #pragma intrinsic(labs) + #endif + + // ANSI compliance + #ifdef _DEBUG + // Remove painful warning in debug + inline_ bool __False__(){ return false; } + #define for if(__False__()){} else for + #else + #define for if(0){} else for + #endif + +#endif // __ICEPREPROCESSOR_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.cpp new file mode 100644 index 0000000..cc63a04 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.cpp @@ -0,0 +1,35 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.cpp + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +void IceCore:: SRand(udword seed) +{ + srand(seed); +} + +udword IceCore::Rand() +{ + return rand(); +} + + +static BasicRandom gRandomGenerator(42); + +udword IceCore::GetRandomIndex(udword max_index) +{ + // We don't use rand() since it's limited to RAND_MAX + udword Index = gRandomGenerator.Randomize(); + return Index % max_index; +} + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.h new file mode 100644 index 0000000..3170b33 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRandom.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.h + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERANDOM_H__ +#define __ICERANDOM_H__ + + FUNCTION ICECORE_API void SRand(udword seed); + FUNCTION ICECORE_API udword Rand(); + + //! Returns a unit random floating-point value + inline_ float UnitRandomFloat() { return float(Rand()) * ONE_OVER_RAND_MAX; } + + //! Returns a random index so that 0<= index < max_index + ICECORE_API udword GetRandomIndex(udword max_index); + + class ICECORE_API BasicRandom + { + public: + + //! Constructor + inline_ BasicRandom(udword seed=0) : mRnd(seed) {} + //! Destructor + inline_ ~BasicRandom() {} + + inline_ void SetSeed(udword seed) { mRnd = seed; } + inline_ udword GetCurrentValue() const { return mRnd; } + inline_ udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; } + + private: + udword mRnd; + }; + +#endif // __ICERANDOM_H__ + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.cpp new file mode 100644 index 0000000..6cf0330 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.cpp @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Ray class. + * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity + * \class Ray + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + O = Origin = impact point + i = normalized vector along the x axis + j = normalized vector along the y axis = actually the normal vector in O + D = Direction vector, norm |D| = 1 + N = Projection of D on y axis, norm |N| = normal reaction + T = Projection of D on x axis, norm |T| = tangential reaction + R = Reflexion vector + + ^y + | + | + | + _ _ _| _ _ _ + * * *| + \ | / + \ |N / | + R\ | /D + \ | / | + \ | / + _________\|/______*_______>x + O T + + Let define theta = angle between D and N. Then cos(theta) = |N| / |D| = |N| since D is normalized. + + j|D = |j|*|D|*cos(theta) => |N| = j|D + + Then we simply have: + + D = N + T + + To compute tangential reaction : + + T = D - N + + To compute reflexion vector : + + R = N - T = N - (D-N) = 2*N - D +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +float Ray::SquareDistance(const Point& point, float* t) const +{ + Point Diff = point - mOrig; + float fT = Diff | mDir; + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + fT /= mDir.SquareMagnitude(); + Diff -= fT*mDir; + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.h new file mode 100644 index 0000000..0268287 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRay.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERAY_H__ +#define __ICERAY_H__ + + class ICEMATHS_API Ray + { + public: + //! Constructor + inline_ Ray() {} + //! Constructor + inline_ Ray(const Point& orig, const Point& dir) : mOrig(orig), mDir(dir) {} + //! Copy constructor + inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {} + //! Destructor + inline_ ~Ray() {} + + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + + Point mOrig; //!< Ray origin + Point mDir; //!< Normalized direction + }; + + inline_ void ComputeReflexionVector(Point& reflected, const Point& incoming_dir, const Point& outward_normal) + { + reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal); + } + + inline_ void ComputeReflexionVector(Point& reflected, const Point& source, const Point& impact, const Point& normal) + { + Point V = impact - source; + reflected = V - normal * 2.0f * (V|normal); + } + + inline_ void DecomposeVector(Point& normal_compo, Point& tangent_compo, const Point& outward_dir, const Point& outward_normal) + { + normal_compo = outward_normal * (outward_dir|outward_normal); + tangent_compo = outward_dir - normal_compo; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a direction vector from world space to local space + * \param local_dir [out] direction vector in local space + * \param world_dir [in] direction vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalDirection(Point& local_dir, const Point& world_dir, const Matrix4x4& world) + { + // Get world direction back in local space +// Matrix3x3 InvWorld = world; +// local_dir = InvWorld * world_dir; + local_dir = Matrix3x3(world) * world_dir; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a position vector from world space to local space + * \param local_pt [out] position vector in local space + * \param world_pt [in] position vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalPoint(Point& local_pt, const Point& world_pt, const Matrix4x4& world) + { + // Get world vertex back in local space + Matrix4x4 InvWorld = world; + InvWorld.Invert(); + local_pt = world_pt * InvWorld; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a ray from world space to local space + * \param local_ray [out] ray in local space + * \param world_ray [in] ray in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalRay(Ray& local_ray, const Ray& world_ray, const Matrix4x4& world) + { + // Get world ray back in local space + ComputeLocalDirection(local_ray.mDir, world_ray.mDir, world); + ComputeLocalPoint(local_ray.mOrig, world_ray.mOrig, world); + } + +#endif // __ICERAY_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.cpp new file mode 100644 index 0000000..99a586f --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceRevisitedRadix.cpp @@ -0,0 +1,520 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains source code from the article "Radix Sort Revisited". + * \file IceRevisitedRadix.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Revisited Radix Sort. + * This is my new radix routine: + * - it uses indices and doesn't recopy the values anymore, hence wasting less ram + * - it creates all the histograms in one run instead of four + * - it sorts words faster than dwords and bytes faster than words + * - it correctly sorts negative floating-point values by patching the offsets + * - it automatically takes advantage of temporal coherence + * - multiple keys support is a side effect of temporal coherence + * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway] + * + * History: + * - 08.15.98: very first version + * - 04.04.00: recoded for the radix article + * - 12.xx.00: code lifting + * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here) + * - 10.11.01: added local ram support + * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting...... + * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all. + * - ranks are not "reset" anymore, but implicit on first calls + * - 07.05.02: - offsets rewritten with one less indirection. + * - 11.03.02: - "bool" replaced with RadixHint enum + * + * \class RadixSort + * \author Pierre Terdiman + * \version 1.4 + * \date August, 15, 1998 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +To do: + - add an offset parameter between two input values (avoid some data recopy sometimes) + - unroll ? asm ? + - 11 bits trick & 3 passes as Michael did + - prefetch stuff the day I have a P3 + - make a version with 16-bits indices ? +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceCore; + +#define INVALIDATE_RANKS mCurrentSize|=0x80000000 +#define VALIDATE_RANKS mCurrentSize&=0x7fffffff +#define CURRENT_SIZE (mCurrentSize&0x7fffffff) +#define INVALID_RANKS (mCurrentSize&0x80000000) + +#define CHECK_RESIZE(n) \ + if(n!=mPreviousSize) \ + { \ + if(n>mCurrentSize) Resize(n); \ + else ResetRanks(); \ + mPreviousSize = n; \ + } + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + ZeroMemory(mHistogram, 256*4*sizeof(udword)); \ + \ + /* Prepare to count */ \ + ubyte* p = (ubyte*)input; \ + ubyte* pe = &p[nb*4]; \ + udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \ + udword* h1= &mHistogram[256]; /* Histogram for second pass */ \ + udword* h2= &mHistogram[512]; /* Histogram for third pass */ \ + udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + type* Running = (type*)buffer; \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(ValCurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Resize lists if needed + CheckResize(nb); + +#ifdef RADIX_LOCAL_RAM + // Allocate histograms & offsets on the stack + udword mHistogram[256*4]; +// udword mOffset[256]; + udword* mLink[256]; +#endif + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); } + else { CREATE_HISTOGRAMS(sdword, input); } + + // Compute #negative values involved if needed + udword NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + udword* h3= &mHistogram[768]; + for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(udword j=0;j<4;j++) + { + CHECK_PASS_VALIDITY(j); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(PerformPass) + { + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets +// mOffset[0] = 0; +// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + mLink[0] = mRanks2; + for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well +// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones + mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones +// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values +// mOffset[128] = 0; + mLink[128] = mRanks2; +// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + ubyte* InputBytes = (ubyte*)input; + InputBytes += j; + if(INVALID_RANKS) + { +// for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above + else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above + else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(udword i=0;i=SqrLen) + { + fT = 1.0f; + Diff -= Dir; + } + else + { + fT /= SqrLen; + Diff -= fT*Dir; + } + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.h new file mode 100644 index 0000000..8d66322 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceSegment.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for segments. + * \file IceSegment.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICESEGMENT_H__ +#define __ICESEGMENT_H__ + + class ICEMATHS_API Segment + { + public: + //! Constructor + inline_ Segment() {} + //! Constructor + inline_ Segment(const Point& p0, const Point& p1) : mP0(p0), mP1(p1) {} + //! Copy constructor + inline_ Segment(const Segment& seg) : mP0(seg.mP0), mP1(seg.mP1) {} + //! Destructor + inline_ ~Segment() {} + + inline_ const Point& GetOrigin() const { return mP0; } + inline_ Point ComputeDirection() const { return mP1 - mP0; } + inline_ void ComputeDirection(Point& dir) const { dir = mP1 - mP0; } + inline_ float ComputeLength() const { return mP1.Distance(mP0); } + inline_ float ComputeSquareLength() const { return mP1.SquareDistance(mP0); } + + inline_ void SetOriginDirection(const Point& origin, const Point& direction) + { + mP0 = mP1 = origin; + mP1 += direction; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes a point on the segment + * \param pt [out] point on segment + * \param t [in] point's parameter [t=0 => pt = mP0, t=1 => pt = mP1] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputePoint(Point& pt, float t) const { pt = mP0 + t * (mP1 - mP0); } + + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + + Point mP0; //!< Start of segment + Point mP1; //!< End of segment + }; + +#endif // __ICESEGMENT_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriList.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriList.h new file mode 100644 index 0000000..73f5257 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriList.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a triangle container. + * \file IceTrilist.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRILIST_H__ +#define __ICETRILIST_H__ + + class ICEMATHS_API TriList : public Container + { + public: + // Constructor / Destructor + TriList() {} + ~TriList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/9; } + inline_ Triangle* GetTriangles() const { return (Triangle*)GetEntries(); } + + void AddTri(const Triangle& tri) + { + Add(tri.mVerts[0].x).Add(tri.mVerts[0].y).Add(tri.mVerts[0].z); + Add(tri.mVerts[1].x).Add(tri.mVerts[1].y).Add(tri.mVerts[1].z); + Add(tri.mVerts[2].x).Add(tri.mVerts[2].y).Add(tri.mVerts[2].z); + } + + void AddTri(const Point& p0, const Point& p1, const Point& p2) + { + Add(p0.x).Add(p0.y).Add(p0.z); + Add(p1.x).Add(p1.y).Add(p1.z); + Add(p2.x).Add(p2.y).Add(p2.z); + } + }; + + class ICEMATHS_API TriangleList : public Container + { + public: + // Constructor / Destructor + TriangleList() {} + ~TriangleList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/3; } + inline_ IndexedTriangle* GetTriangles() const { return (IndexedTriangle*)GetEntries();} + + void AddTriangle(const IndexedTriangle& tri) + { + Add((udword)tri.mVRef[0]).Add((udword)tri.mVRef[1]).Add((udword)tri.mVRef[2]); + } + + void AddTriangle(udword vref0, udword vref1, udword vref2) + { + Add(vref0).Add(vref1).Add(vref2); + } + }; + +#endif //__ICETRILIST_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.cpp b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.cpp new file mode 100644 index 0000000..4268ff4 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.cpp @@ -0,0 +1,286 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a triangle class. + * + * \class Tri + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static sdword VPlaneSideEps(const Point& v, const Plane& plane, float epsilon) +{ + // Compute distance from current vertex to the plane + float Dist = plane.Distance(v); + // Compute side: + // 1 = the vertex is on the positive side of the plane + // -1 = the vertex is on the negative side of the plane + // 0 = the vertex is on the plane (within epsilon) + return Dist > epsilon ? 1 : Dist < -epsilon ? -1 : 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Flip() +{ + Point Tmp = mVerts[1]; + mVerts[1] = mVerts[2]; + mVerts[2] = Tmp; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Area() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return ((p0 - p1)^(p0 - p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Perimeter() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Compacity() const +{ + float P = Perimeter(); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area()/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Normal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::DenormalizedNormal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Center(Point& center) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + center = (p0 + p1 + p2)*INV3; +} + +PartVal Triangle::TestAgainstPlane(const Plane& plane, float epsilon) const +{ + bool Pos = false, Neg = false; + + // Loop through all vertices + for(udword i=0;i<3;i++) + { + // Compute side: + sdword Side = VPlaneSideEps(mVerts[i], plane, epsilon); + + if (Side < 0) Neg = true; + else if (Side > 0) Pos = true; + } + + if (!Pos && !Neg) return TRI_ON_PLANE; + else if (Pos && Neg) return TRI_INTERSECT; + else if (Pos && !Neg) return TRI_PLUS_SPACE; + else if (!Pos && Neg) return TRI_MINUS_SPACE; + + // What?! + return TRI_FORCEDWORD; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle moment. + * \param m [out] the moment + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* +void Triangle::ComputeMoment(Moment& m) +{ + // Compute the area of the triangle + m.mArea = Area(); + + // Compute the centroid + Center(m.mCentroid); + + // Second-order components. Handle zero-area faces. + Point& p = mVerts[0]; + Point& q = mVerts[1]; + Point& r = mVerts[2]; + if(m.mArea==0.0f) + { + // This triangle has zero area. The second order components would be eliminated with the usual formula, so, for the + // sake of robustness we use an alternative form. These are the centroid and second-order components of the triangle's vertices. + m.mCovariance.m[0][0] = (p.x*p.x + q.x*q.x + r.x*r.x); + m.mCovariance.m[0][1] = (p.x*p.y + q.x*q.y + r.x*r.y); + m.mCovariance.m[0][2] = (p.x*p.z + q.x*q.z + r.x*r.z); + m.mCovariance.m[1][1] = (p.y*p.y + q.y*q.y + r.y*r.y); + m.mCovariance.m[1][2] = (p.y*p.z + q.y*q.z + r.y*r.z); + m.mCovariance.m[2][2] = (p.z*p.z + q.z*q.z + r.z*r.z); + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } + else + { + const float OneOverTwelve = 1.0f / 12.0f; + m.mCovariance.m[0][0] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.x + p.x*p.x + q.x*q.x + r.x*r.x) * OneOverTwelve; + m.mCovariance.m[0][1] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.y + p.x*p.y + q.x*q.y + r.x*r.y) * OneOverTwelve; + m.mCovariance.m[1][1] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.y + p.y*p.y + q.y*q.y + r.y*r.y) * OneOverTwelve; + m.mCovariance.m[0][2] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.z + p.x*p.z + q.x*q.z + r.x*r.z) * OneOverTwelve; + m.mCovariance.m[1][2] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.z + p.y*p.z + q.y*q.z + r.y*r.z) * OneOverTwelve; + m.mCovariance.m[2][2] = m.mArea * (9.0f * m.mCentroid.z*m.mCentroid.z + p.z*p.z + q.z*q.z + r.z*r.z) * OneOverTwelve; + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } +} +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MinEdgeLength() const +{ + float Min = MAX_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MaxEdgeLength() const +{ + float Max = MIN_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::ComputePoint(float u, float v, Point& pt, udword* nearvtx) const +{ + // Compute point coordinates + pt = (1.0f - u - v)*mVerts[0] + u*mVerts[1] + v*mVerts[2]; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(mVerts[0].SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + mVerts[1].SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + mVerts[2].SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = d.SmallestAxis(); + } +} + +void Triangle::Inflate(float fat_coeff, bool constant_border) +{ + // Compute triangle center + Point TriangleCenter; + Center(TriangleCenter); + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(udword i=0;i<3;i++) + { + Point v = mVerts[i] - TriangleCenter; + + if(constant_border) v.Normalize(); + + mVerts[i] += v * fat_coeff; + } +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.h new file mode 100644 index 0000000..a984db8 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTriangle.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRIANGLE_H__ +#define __ICETRIANGLE_H__ + + // Forward declarations + class Moment; + + // Partitioning values + enum PartVal + { + TRI_MINUS_SPACE = 0, //!< Triangle is in the negative space + TRI_PLUS_SPACE = 1, //!< Triangle is in the positive space + TRI_INTERSECT = 2, //!< Triangle intersects plane + TRI_ON_PLANE = 3, //!< Triangle and plane are coplanar + + TRI_FORCEDWORD = 0x7fffffff + }; + + // A triangle class. + class ICEMATHS_API Triangle + { + public: + //! Constructor + inline_ Triangle() {} + //! Constructor + inline_ Triangle(const Point& p0, const Point& p1, const Point& p2) { mVerts[0]=p0; mVerts[1]=p1; mVerts[2]=p2; } + //! Copy constructor + inline_ Triangle(const Triangle& triangle) + { + mVerts[0] = triangle.mVerts[0]; + mVerts[1] = triangle.mVerts[1]; + mVerts[2] = triangle.mVerts[2]; + } + //! Destructor + inline_ ~Triangle() {} + //! Vertices + Point mVerts[3]; + + // Methods + void Flip(); + float Area() const; + float Perimeter() const; + float Compacity() const; + void Normal(Point& normal) const; + void DenormalizedNormal(Point& normal) const; + void Center(Point& center) const; + inline_ Plane PlaneEquation() const { return Plane(mVerts[0], mVerts[1], mVerts[2]); } + + PartVal TestAgainstPlane(const Plane& plane, float epsilon) const; +// float Distance(Point& cp, Point& cq, Tri& tri); + void ComputeMoment(Moment& m); + float MinEdgeLength() const; + float MaxEdgeLength() const; + void ComputePoint(float u, float v, Point& pt, udword* nearvtx=null) const; + void Inflate(float fat_coeff, bool constant_border); + }; + +#endif // __ICETRIANGLE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/IceTypes.h b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTypes.h new file mode 100644 index 0000000..c896623 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/IceTypes.h @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains custom types. + * \file IceTypes.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETYPES_H__ +#define __ICETYPES_H__ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Things to help us compile on non-windows platforms + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + #define USE_HANDLE_MANAGER + + // Constants + #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI + #define HALFPI 1.57079632679489661923f //!< 0.5 * PI + #define TWOPI 6.28318530717958647692f //!< 2.0 * PI + #define INVPI 0.31830988618379067154f //!< 1.0 / PI + + #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees + #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians + + #define EXP 2.71828182845904523536f //!< e + #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2) + #define LN2 0.693147180559945f //!< ln(2) + #define INVLN2 1.44269504089f //!< 1.0f / ln(2) + + #define INV3 0.33333333333333333333f //!< 1/3 + #define INV6 0.16666666666666666666f //!< 1/6 + #define INV7 0.14285714285714285714f //!< 1/7 + #define INV9 0.11111111111111111111f //!< 1/9 + #define INV255 0.00392156862745098039f //!< 1/255 + + #define SQRT2 1.41421356237f //!< sqrt(2) + #define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + + #define SQRT3 1.73205080757f //!< sqrt(3) + #define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + + #define null 0 //!< our own NULL pointer + + // Custom types used in ICE + typedef signed char sbyte; //!< sizeof(sbyte) must be 1 + typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 + typedef signed short sword; //!< sizeof(sword) must be 2 + typedef unsigned short uword; //!< sizeof(uword) must be 2 + typedef signed int sdword; //!< sizeof(sdword) must be 4 + typedef unsigned int udword; //!< sizeof(udword) must be 4 + typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 + typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 + typedef float float32; //!< sizeof(float32) must be 4 + typedef double float64; //!< sizeof(float64) must be 4 + + ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8); + ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8); + + //! TO BE DOCUMENTED + #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name + + typedef udword DynID; //!< Dynamic identifier +#ifdef USE_HANDLE_MANAGER + typedef udword KID; //!< Kernel ID +// DECLARE_ICE_HANDLE(KID); +#else + typedef uword KID; //!< Kernel ID +#endif + typedef udword RTYPE; //!< Relationship-type (!) between owners and references + #define INVALID_ID 0xffffffff //!< Invalid dword ID (counterpart of null pointers) +#ifdef USE_HANDLE_MANAGER + #define INVALID_KID 0xffffffff //!< Invalid Kernel ID +#else + #define INVALID_KID 0xffff //!< Invalid Kernel ID +#endif + #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value + + // Define BOOL if needed + #ifndef BOOL + typedef int BOOL; //!< Another boolean type. + #endif + + //! Union of a float and a sdword + typedef union { + float f; //!< The float + sdword d; //!< The integer + }scell; + + //! Union of a float and a udword + typedef union { + float f; //!< The float + udword d; //!< The integer + }ucell; + + // Type ranges + #define MAX_SBYTE 0x7f //!< max possible sbyte value + #define MIN_SBYTE 0x80 //!< min possible sbyte value + #define MAX_UBYTE 0xff //!< max possible ubyte value + #define MIN_UBYTE 0x00 //!< min possible ubyte value + #define MAX_SWORD 0x7fff //!< max possible sword value + #define MIN_SWORD 0x8000 //!< min possible sword value + #define MAX_UWORD 0xffff //!< max possible uword value + #define MIN_UWORD 0x0000 //!< min possible uword value + #define MAX_SDWORD 0x7fffffff //!< max possible sdword value + #define MIN_SDWORD 0x80000000 //!< min possible sdword value + #define MAX_UDWORD 0xffffffff //!< max possible udword value + #define MIN_UDWORD 0x00000000 //!< min possible udword value + #define MAX_FLOAT FLT_MAX //!< max possible float value + #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0 + #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT + #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT + #define IEEE_UNDERFLOW_LIMIT 0x1a000000 + + #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand() + + // typedef int (__stdcall* PROC)(); -- Oleh Derevenko: Conflicts with Windows headers in x64 mode //!< A standard procedure call. + typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call + typedef void** VTABLE; //!< A V-Table. + + #undef MIN + #undef MAX + #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b + #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b + #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c + + template inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; } + template inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; } + template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } + template inline_ void TSetMax (T& a, const T& b) { if(a> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); + n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); + n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); + n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); + n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + } + + //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection) + inline_ udword CountBits(udword n) + { + // This relies of the fact that the count of n bits can NOT overflow + // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts + // 2 bit interger, 3 bit count requires only a 2 bit interger. + // So we add all bit pairs, then each nible, then each byte etc... + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + return n; + } + + //! Even faster? + inline_ udword CountBits2(udword bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333); + bits = ((bits >> 4) + bits) & 0x0F0F0F0F; + return (bits * 0x01010101) >> 24; + } + + //! Spread out bits. EG 00001111 -> 0101010101 + //! 00001010 -> 0100010000 + //! This is used to interleve to intergers to produce a `Morten Key' + //! used in Space Filling Curves (See DrDobbs Journal, July 1999) + //! Order is important. + inline_ void SpreadBits(udword& n) + { + n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16); + n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8); + n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4); + n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2); + n = ( n & 0x11111111) | (( n & 0x22222222) << 1); + } + + // Next Largest Power of 2 + // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm + // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with + // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next + // largest power of 2. For a 32-bit value: + inline_ udword nlpo2(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; + } + + //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection) + inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); } + + //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection) + inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); } + + //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection) + inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<> 31; return (x^y)-y; } + + //!< Alternative min function + inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); } + + // Determine if one of the bytes in a 4 byte word is zero + inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } + + // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0 + inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } +// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } + + // Most Significant 1 Bit + // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set) + // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits. + // This process yields a bit vector with the same most significant 1 as x, but all 1's below it. + // Bitwise AND of the original value with the complement of the "folded" value shifted down by one + // yields the most significant bit. For a 32-bit value: + inline_ udword msb32(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x & ~(x >> 1)); + } + + /* + "Just call it repeatedly with various input values and always with the same variable as "memory". + The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1 + does no filtering at all. + + I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed + to the more typical FIR (Finite Impulse Response). + + Also, I'd say that you can make more intelligent and interesting filters than this, for example filters + that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter + to be applied before this one, of course." + + (JCAB on Flipcode) + */ + inline_ float FeedbackFilter(float val, float& memory, float sharpness) + { + ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter"); + if(sharpness<0.0f) sharpness = 0.0f; + else if(sharpness>1.0f) sharpness = 1.0f; + return memory = val * sharpness + memory * (1.0f - sharpness); + } + + //! If you can guarantee that your input domain (i.e. value of x) is slightly + //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the + //! following code to clamp the resulting value into [-32768,+32767] range: + inline_ int ClampToInt16(int x) + { +// ASSERT(abs(x) < (int)((1<<31u)-32767)); + + int delta = 32767 - x; + x += (delta>>31) & delta; + delta = x + 32768; + x -= (delta>>31) & delta; + return x; + } + + // Generic functions + template inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; } + template inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((xhi) ? hi : x); } + + template inline_ void TSort(Type& a, Type& b) + { + if(a>b) TSwap(a, b); + } + + template inline_ void TSort(Type& a, Type& b, Type& c) + { + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + } + + // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom) +// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); } + // ... actually this is better ! + #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object); + + //! TO BE DOCUMENTED + #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member) + //! TO BE DOCUMENTED +#ifndef ARRAYSIZE + #define ARRAYSIZE(p) (sizeof(p)/sizeof((p)[0])) +#endif // #ifndef ARRAYSIZE + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns the alignment of the input address. + * \fn Alignment() + * \param address [in] address to check + * \return the best alignment (e.g. 1 for odd addresses, etc) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + FUNCTION ICECORE_API udword Alignment(udword address); + + #define IS_ALIGNED_2(x) ((x&1)==0) + #define IS_ALIGNED_4(x) ((x&3)==0) + #define IS_ALIGNED_8(x) ((x&7)==0) + + inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } + + // Compute implicit coords from an index: + // The idea is to get back 2D coords from a 1D index. + // For example: + // + // 0 1 2 ... nbu-1 + // nbu nbu+1 i ... + // + // We have i, we're looking for the equivalent (u=2, v=1) location. + // i = u + v*nbu + // <=> i/nbu = u/nbu + v + // Since 0 <= u < nbu, u/nbu = 0 (integer) + // Hence: v = i/nbu + // Then we simply put it back in the original equation to compute u = i - v*nbu + inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu) + { + v = i / nbu; + u = i - (v * nbu); + } + + // In 3D: i = u + v*nbu + w*nbu*nbv + // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w + // u/(nbu*nbv) is null since u/nbu was null already. + // v/nbv is null as well for the same reason. + // Hence w = i/(nbu*nbv) + // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu + inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv) + { + w = i / (nbu_nbv); + Compute2DCoords(u, v, i - (w * nbu_nbv), nbu); + } + +#endif // __ICEUTILS_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.am b/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.am new file mode 100644 index 0000000..4f3256c --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = -I$(top_srcdir)/OPCODE \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include + +noinst_LTLIBRARIES = libIce.la +libIce_la_SOURCES = \ + IceAABB.cpp IceAABB.h IceAxes.h \ + IceBoundingSphere.h IceContainer.cpp IceContainer.h \ + IceFPU.h IceHPoint.cpp IceHPoint.h \ + IceIndexedTriangle.cpp IceIndexedTriangle.h IceLSS.h \ + IceMatrix3x3.cpp IceMatrix3x3.h IceMatrix4x4.cpp \ + IceMatrix4x4.h IceMemoryMacros.h IceOBB.cpp \ + IceOBB.h IcePairs.h IcePlane.cpp \ + IcePlane.h IcePoint.cpp IcePoint.h \ + IcePreprocessor.h IceRandom.cpp IceRandom.h \ + IceRay.cpp IceRay.h IceRevisitedRadix.cpp \ + IceRevisitedRadix.h IceSegment.cpp IceSegment.h \ + IceTriangle.cpp IceTriangle.h IceTriList.h \ + IceTypes.h IceUtils.cpp IceUtils.h + diff --git a/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.in b/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.in new file mode 100644 index 0000000..d5f05b7 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Ice/Makefile.in @@ -0,0 +1,660 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = OPCODE/Ice +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libIce_la_LIBADD = +am_libIce_la_OBJECTS = IceAABB.lo IceContainer.lo IceHPoint.lo \ + IceIndexedTriangle.lo IceMatrix3x3.lo IceMatrix4x4.lo \ + IceOBB.lo IcePlane.lo IcePoint.lo IceRandom.lo IceRay.lo \ + IceRevisitedRadix.lo IceSegment.lo IceTriangle.lo IceUtils.lo +libIce_la_OBJECTS = $(am_libIce_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libIce_la_SOURCES) +DIST_SOURCES = $(libIce_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I$(top_srcdir)/OPCODE \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include + +noinst_LTLIBRARIES = libIce.la +libIce_la_SOURCES = \ + IceAABB.cpp IceAABB.h IceAxes.h \ + IceBoundingSphere.h IceContainer.cpp IceContainer.h \ + IceFPU.h IceHPoint.cpp IceHPoint.h \ + IceIndexedTriangle.cpp IceIndexedTriangle.h IceLSS.h \ + IceMatrix3x3.cpp IceMatrix3x3.h IceMatrix4x4.cpp \ + IceMatrix4x4.h IceMemoryMacros.h IceOBB.cpp \ + IceOBB.h IcePairs.h IcePlane.cpp \ + IcePlane.h IcePoint.cpp IcePoint.h \ + IcePreprocessor.h IceRandom.cpp IceRandom.h \ + IceRay.cpp IceRay.h IceRevisitedRadix.cpp \ + IceRevisitedRadix.h IceSegment.cpp IceSegment.h \ + IceTriangle.cpp IceTriangle.h IceTriList.h \ + IceTypes.h IceUtils.cpp IceUtils.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign OPCODE/Ice/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign OPCODE/Ice/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libIce.la: $(libIce_la_OBJECTS) $(libIce_la_DEPENDENCIES) $(EXTRA_libIce_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(libIce_la_OBJECTS) $(libIce_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceAABB.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceContainer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceHPoint.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceIndexedTriangle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceMatrix3x3.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceMatrix4x4.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceOBB.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IcePlane.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IcePoint.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceRandom.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceRay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceRevisitedRadix.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceSegment.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceTriangle.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IceUtils.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/OPCODE/Makefile.am b/thirdparty/ode-0.16.5/OPCODE/Makefile.am new file mode 100644 index 0000000..1e82910 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Makefile.am @@ -0,0 +1,45 @@ +EXTRA_DIST = COPYING \ + ReadMe.txt \ + README-ODE.txt \ + TemporalCoherence.txt + +SUBDIRS = Ice + +noinst_LTLIBRARIES = libOPCODE.la +AM_CPPFLAGS= -I$(top_srcdir)/include \ + -I$(top_builddir)/include + +libOPCODE_la_SOURCES = OPC_AABBCollider.cpp OPC_AABBCollider.h \ + OPC_AABBTree.cpp OPC_AABBTree.h \ + OPC_BaseModel.cpp OPC_BaseModel.h \ + OPC_Collider.cpp OPC_Collider.h \ + OPC_Common.cpp OPC_Common.h \ + OPC_HybridModel.cpp OPC_HybridModel.h \ + OPC_LSSCollider.cpp OPC_LSSCollider.h \ + OPC_MeshInterface.cpp OPC_MeshInterface.h \ + OPC_Model.cpp OPC_Model.h \ + OPC_OBBCollider.cpp OPC_OBBCollider.h \ + Opcode.cpp Opcode.h \ + OPC_OptimizedTree.cpp OPC_OptimizedTree.h \ + OPC_Picking.cpp OPC_Picking.h \ + OPC_PlanesCollider.cpp OPC_PlanesCollider.h \ + OPC_RayCollider.cpp OPC_RayCollider.h \ + OPC_SphereCollider.cpp OPC_SphereCollider.h \ + OPC_TreeBuilders.cpp OPC_TreeBuilders.h \ + OPC_TreeCollider.cpp OPC_TreeCollider.h \ + OPC_VolumeCollider.cpp OPC_VolumeCollider.h \ + OPC_Settings.h \ + OPC_SphereAABBOverlap.h \ + OPC_BoxBoxOverlap.h \ + OPC_SphereTriOverlap.h \ + OPC_PlanesAABBOverlap.h \ + OPC_TriBoxOverlap.h \ + OPC_IceHook.h \ + OPC_PlanesTriOverlap.h \ + OPC_TriTriOverlap.h \ + OPC_LSSAABBOverlap.h \ + OPC_RayAABBOverlap.h \ + Stdafx.h \ + OPC_LSSTriOverlap.h \ + OPC_RayTriOverlap.h + diff --git a/thirdparty/ode-0.16.5/OPCODE/Makefile.in b/thirdparty/ode-0.16.5/OPCODE/Makefile.in new file mode 100644 index 0000000..bc96d41 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Makefile.in @@ -0,0 +1,807 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = OPCODE +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libOPCODE_la_LIBADD = +am_libOPCODE_la_OBJECTS = OPC_AABBCollider.lo OPC_AABBTree.lo \ + OPC_BaseModel.lo OPC_Collider.lo OPC_Common.lo \ + OPC_HybridModel.lo OPC_LSSCollider.lo OPC_MeshInterface.lo \ + OPC_Model.lo OPC_OBBCollider.lo Opcode.lo OPC_OptimizedTree.lo \ + OPC_Picking.lo OPC_PlanesCollider.lo OPC_RayCollider.lo \ + OPC_SphereCollider.lo OPC_TreeBuilders.lo OPC_TreeCollider.lo \ + OPC_VolumeCollider.lo +libOPCODE_la_OBJECTS = $(am_libOPCODE_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libOPCODE_la_SOURCES) +DIST_SOURCES = $(libOPCODE_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp COPYING +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +EXTRA_DIST = COPYING \ + ReadMe.txt \ + README-ODE.txt \ + TemporalCoherence.txt + +SUBDIRS = Ice +noinst_LTLIBRARIES = libOPCODE.la +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/include + +libOPCODE_la_SOURCES = OPC_AABBCollider.cpp OPC_AABBCollider.h \ + OPC_AABBTree.cpp OPC_AABBTree.h \ + OPC_BaseModel.cpp OPC_BaseModel.h \ + OPC_Collider.cpp OPC_Collider.h \ + OPC_Common.cpp OPC_Common.h \ + OPC_HybridModel.cpp OPC_HybridModel.h \ + OPC_LSSCollider.cpp OPC_LSSCollider.h \ + OPC_MeshInterface.cpp OPC_MeshInterface.h \ + OPC_Model.cpp OPC_Model.h \ + OPC_OBBCollider.cpp OPC_OBBCollider.h \ + Opcode.cpp Opcode.h \ + OPC_OptimizedTree.cpp OPC_OptimizedTree.h \ + OPC_Picking.cpp OPC_Picking.h \ + OPC_PlanesCollider.cpp OPC_PlanesCollider.h \ + OPC_RayCollider.cpp OPC_RayCollider.h \ + OPC_SphereCollider.cpp OPC_SphereCollider.h \ + OPC_TreeBuilders.cpp OPC_TreeBuilders.h \ + OPC_TreeCollider.cpp OPC_TreeCollider.h \ + OPC_VolumeCollider.cpp OPC_VolumeCollider.h \ + OPC_Settings.h \ + OPC_SphereAABBOverlap.h \ + OPC_BoxBoxOverlap.h \ + OPC_SphereTriOverlap.h \ + OPC_PlanesAABBOverlap.h \ + OPC_TriBoxOverlap.h \ + OPC_IceHook.h \ + OPC_PlanesTriOverlap.h \ + OPC_TriTriOverlap.h \ + OPC_LSSAABBOverlap.h \ + OPC_RayAABBOverlap.h \ + Stdafx.h \ + OPC_LSSTriOverlap.h \ + OPC_RayTriOverlap.h + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign OPCODE/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign OPCODE/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libOPCODE.la: $(libOPCODE_la_OBJECTS) $(libOPCODE_la_DEPENDENCIES) $(EXTRA_libOPCODE_la_DEPENDENCIES) + $(AM_V_CXXLD)$(CXXLINK) $(libOPCODE_la_OBJECTS) $(libOPCODE_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_AABBCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_AABBTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_BaseModel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_Collider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_Common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_HybridModel.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_LSSCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_MeshInterface.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_Model.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_OBBCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_OptimizedTree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_Picking.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_PlanesCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_RayCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_SphereCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_TreeBuilders.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_TreeCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OPC_VolumeCollider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Opcode.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.cpp new file mode 100644 index 0000000..a192311 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.cpp @@ -0,0 +1,696 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an AABB collider. + * \file OPC_AABBCollider.cpp + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an AABB-vs-tree collider. + * + * \class AABBCollider + * \author Pierre Terdiman + * \version 1.3 + * \date January, 1st, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_BoxBoxOverlap.h" +#include "OPC_TriBoxOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(udword(prim_index)); + +//! AABB-triangle test +#define AABB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \ + mLeafVerts[0] = *VP.Vertex[0]; \ + mLeafVerts[1] = *VP.Vertex[1]; \ + mLeafVerts[2] = *VP.Vertex[2]; \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollider::AABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollider::~AABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param model [in] Opcode model to collide with + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - check temporal coherence + * + * \param cache [in/out] a box cache + * \param box [in] AABB in world space + * \return TRUE if we can return immediately + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Keep track of the query box + mBox = box; + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + AABB_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the box (and set contact status if needed) + AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): + if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat box so that coherence will work for subsequent frames + mBox.mExtents *= cache.FatCoeff; + + // Update cache with query data (signature for cached faces) + cache.FatBox = mBox; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + // 5) Precompute min & max bounds if needed + mMin = box.mCenter - box.mExtents; + mMax = box.mCenter + box.mExtents; + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the AABB completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the AABB contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBCollider::AABBContainsBox(const Point& bc, const Point& be) +{ + if(mMin.x > bc.x - be.x) return FALSE; + if(mMin.y > bc.y - be.y) return FALSE; + if(mMin.z > bc.z - be.z) return FALSE; + + if(mMax.x < bc.x + be.x) return FALSE; + if(mMax.y < bc.y + be.y) return FALSE; + if(mMax.z < bc.z + be.z) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_AABB(center, extents) \ + if(AABBContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->IsLeaf()) + { + AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBTreeNode* node) +{ + // Perform AABB-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!AABBAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || AABBContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::HybridAABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::~HybridAABBCollider() +{ +} + +bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;i(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.h new file mode 100644 index 0000000..315d2d3 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBCollider.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an AABB collider. + * \file OPC_AABBCollider.h + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_AABBCOLLIDER_H__ +#define __OPC_AABBCOLLIDER_H__ + + struct OPCODE_API AABBCache : VolumeCache + { + AABBCache() : FatCoeff(1.1f) + { + FatBox.mCenter.Zero(); + FatBox.mExtents.Zero(); + } + + // Cached faces signature + CollisionAABB FatBox; //!< Box used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere + }; + + class OPCODE_API AABBCollider : public VolumeCollider + { + public: + // Constructor / Destructor + AABBCollider(); + virtual ~AABBCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param model [in] Opcode model to collide with + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(AABBCache& cache, const CollisionAABB& box, const Model& model); + // + bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree); + protected: + CollisionAABB mBox; //!< Query box in (center, extents) form + Point mMin; //!< Query box min point + Point mMax; //!< Query box max point + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL AABBContainsBox(const Point& bc, const Point& be); + inline_ BOOL AABBAABBOverlap(const Point& b, const Point& Pb); + inline_ BOOL TriBoxOverlap(); + // Init methods + BOOL InitQuery(AABBCache& cache, const CollisionAABB& box); + }; + + class OPCODE_API HybridAABBCollider : public AABBCollider + { + public: + // Constructor / Destructor + HybridAABBCollider(); + virtual ~HybridAABBCollider(); + + bool Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model); + protected: + Container mTouchedBoxes; + }; + +#endif // __OPC_AABBCOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.cpp new file mode 100644 index 0000000..48129a8 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.cpp @@ -0,0 +1,568 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a versatile AABB tree. + * \file OPC_AABBTree.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a generic AABB tree node. + * + * \class AABBTreeNode + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a generic AABB tree. + * This is a vanilla AABB tree, without any particular optimization. It contains anonymous references to + * user-provided primitives, which can theoretically be anything - triangles, boxes, etc. Each primitive + * is surrounded by an AABB, regardless of the primitive's nature. When the primitive is a triangle, the + * resulting tree can be converted into an optimized tree. If the primitive is a box, the resulting tree + * can be used for culling - VFC or occlusion -, assuming you cull on a mesh-by-mesh basis (modern way). + * + * \class AABBTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTreeNode::AABBTreeNode() : + mPos (null), +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg (null), +#endif + mNodePrimitives (null), + mNbPrimitives (0) +{ +#ifdef OPC_USE_TREE_COHERENCE + mBitmask = 0; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTreeNode::~AABBTreeNode() +{ + // Opcode 1.3: + const AABBTreeNode* Pos = GetPos(); +#ifndef OPC_NO_NEG_VANILLA_TREE + const AABBTreeNode* Neg = GetNeg(); + if(!(mPos&1)) DELETESINGLE(Pos); + if(!(mNeg&1)) DELETESINGLE(Neg); +#else + if(!(mPos&1)) DELETEARRAY(Pos); +#endif + mNodePrimitives = null; // This was just a shortcut to the global list => no release + mNbPrimitives = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Splits the node along a given axis. + * The list of indices is reorganized according to the split values. + * \param axis [in] splitting axis index + * \param builder [in] the tree builder + * \return the number of primitives assigned to the first child + * \warning this method reorganizes the internal list of primitives + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTreeNode::Split(udword axis, AABBTreeBuilder* builder) +{ + // Get node split value + float SplitValue = builder->GetSplittingValue(mNodePrimitives, mNbPrimitives, mBV, axis); + + udword NbPos = 0; + // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1]. + // Those indices map the global list in the tree builder. + for(udword i=0;iGetSplittingValue(Index, axis); + + // Reorganize the list of indices in this order: positive - negative. + if(PrimitiveValue > SplitValue) + { + // Swap entries + udword Tmp = mNodePrimitives[i]; + mNodePrimitives[i] = mNodePrimitives[NbPos]; + mNodePrimitives[NbPos] = Tmp; + // Count primitives assigned to positive space + NbPos++; + } + } + return NbPos; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Subdivides the node. + * + * N + * / \ + * / \ + * N/2 N/2 + * / \ / \ + * N/4 N/4 N/4 N/4 + * (etc) + * + * A well-balanced tree should have a O(log n) depth. + * A degenerate tree would have a O(n) depth. + * Note a perfectly-balanced tree is not well-suited to collision detection anyway. + * + * \param builder [in] the tree builder + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder) return false; + + // Stop subdividing if we reach a leaf node. This is always performed here, + // else we could end in trouble if user overrides this. + if(mNbPrimitives==1) return true; + + // Let the user validate the subdivision + if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true; + + bool ValidSplit = true; // Optimism... + udword NbPos; + if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS) + { + // Find the largest axis to split along + Point Extents; mBV.GetExtents(Extents); // Box extents + udword Axis = Extents.LargestAxis(); // Index of largest axis + + // Split along the axis + NbPos = Split(Axis, builder); + + // Check split validity + if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; + } + else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS) + { + // Compute the means + Point Means(0.0f, 0.0f, 0.0f); + for(udword i=0;iGetSplittingValues(Index); + } + Means/=float(mNbPrimitives); + + // Compute variances + Point Vars(0.0f, 0.0f, 0.0f); + for(udword i=0;iGetSplittingValues(Index); + Point Delta = Center - Means; + Vars += Delta * Delta; + } + Vars/=float(mNbPrimitives-1); + + // Choose axis with greatest variance + udword Axis = Vars.LargestAxis(); + + // Split along the axis + NbPos = Split(Axis, builder); + + // Check split validity + if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; + } + else if(builder->mSettings.mRules & SPLIT_BALANCED) + { + // Test 3 axis, take the best + float Results[3]; + NbPos = Split(0, builder); Results[0] = float(NbPos)/float(mNbPrimitives); + NbPos = Split(1, builder); Results[1] = float(NbPos)/float(mNbPrimitives); + NbPos = Split(2, builder); Results[2] = float(NbPos)/float(mNbPrimitives); + Results[0]-=0.5f; Results[0]*=Results[0]; + Results[1]-=0.5f; Results[1]*=Results[1]; + Results[2]-=0.5f; Results[2]*=Results[2]; + udword Min=0; + if(Results[1]mSettings.mRules & SPLIT_BEST_AXIS) + { + // Test largest, then middle, then smallest axis... + + // Sort axis + Point Extents; mBV.GetExtents(Extents); // Box extents + udword SortedAxis[] = { 0, 1, 2 }; + float* Keys = (float*)&Extents.x; + for(udword j=0;j<3;j++) + { + for(udword i=0;i<2;i++) + { + if(Keys[SortedAxis[i]]mSettings.mRules & SPLIT_FIFTY) + { + // Don't even bother splitting (mainly a performance test) + NbPos = mNbPrimitives>>1; + } + else return false; // Unknown splitting rules + + // Check the subdivision has been successful + if(!ValidSplit) + { + // Here, all boxes lie in the same sub-space. Two strategies: + // - if the tree *must* be complete, make an arbitrary 50-50 split + // - else stop subdividing +// if(builder->mSettings.mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) + { + builder->IncreaseNbInvalidSplits(); + NbPos = mNbPrimitives>>1; + } + else return true; + } + + // Now create children and assign their pointers. + if(builder->mNodeBase) + { + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + AABBTreeNode* Pool = (AABBTreeNode*)builder->mNodeBase; + udword Count = builder->GetCount() - 1; // Count begins to 1... + // Set last bit to tell it shouldn't be freed ### pretty ugly, find a better way. Maybe one bit in mNbPrimitives + ASSERT(!(udword(&Pool[Count+0])&1)); + ASSERT(!(udword(&Pool[Count+1])&1)); + mPos = size_t(&Pool[Count+0])|1; +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg = size_t(&Pool[Count+1])|1; +#endif + } + else + { + // Non-complete trees and/or Opcode 1.2 allocate nodes on-the-fly +#ifndef OPC_NO_NEG_VANILLA_TREE + mPos = (size_t)new AABBTreeNode; CHECKALLOC(mPos); + mNeg = (size_t)new AABBTreeNode; CHECKALLOC(mNeg); +#else + AABBTreeNode* PosNeg = new AABBTreeNode[2]; + CHECKALLOC(PosNeg); + mPos = (size_t)PosNeg; +#endif + } + + // Update stats + builder->IncreaseCount(2); + + // Assign children + AABBTreeNode* Pos = const_cast(GetPos()); + AABBTreeNode* Neg = const_cast(GetNeg()); + Pos->mNodePrimitives = &mNodePrimitives[0]; + Pos->mNbPrimitives = NbPos; + Neg->mNodePrimitives = &mNodePrimitives[NbPos]; + Neg->mNbPrimitives = mNbPrimitives - NbPos; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive hierarchy building in a top-down fashion. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder) +{ + // 1) Compute the global box for current node. The box is stored in mBV. + builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV); + + // 2) Subdivide current node + Subdivide(builder); + + // 3) Recurse + AABBTreeNode* Pos = const_cast(GetPos()); + AABBTreeNode* Neg = const_cast(GetNeg()); + if(Pos) Pos->_BuildHierarchy(builder); + if(Neg) Neg->_BuildHierarchy(builder); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree (top-down). + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeNode::_Refit(AABBTreeBuilder* builder) +{ + // 1) Recompute the new global box for current node + builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV); + + // 2) Recurse + AABBTreeNode* Pos = const_cast(GetPos()); + AABBTreeNode* Neg = const_cast(GetNeg()); + if(Pos) Pos->_Refit(builder); + if(Neg) Neg->_Refit(builder); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTree::AABBTree() : mIndices(null), mPool(null), mTotalNbNodes(0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTree::~AABBTree() +{ + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the tree. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTree::Release() +{ + DELETEARRAY(mPool); + DELETEARRAY(mIndices); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a generic AABB tree from a tree builder. + * \param builder [in] the tree builder + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Build(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder || !builder->mNbPrimitives) return false; + + // Release previous tree + Release(); + + // Init stats + builder->SetCount(1); + builder->SetNbInvalidSplits(0); + + // Initialize indices. This list will be modified during build. + mIndices = new dTriIndex[builder->mNbPrimitives]; + CHECKALLOC(mIndices); + // Identity permutation + for(udword i=0;imNbPrimitives;i++) mIndices[i] = i; + + // Setup initial node. Here we have a complete permutation of the app's primitives. + mNodePrimitives = mIndices; + mNbPrimitives = builder->mNbPrimitives; + + // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3] +// if(builder->mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) + { + // Allocate a pool of nodes + mPool = new AABBTreeNode[builder->mNbPrimitives*2 - 1]; + + builder->mNodeBase = mPool; // ### ugly ! + } + + // Build the hierarchy + _BuildHierarchy(builder); + + // Get back total number of nodes + mTotalNbNodes = builder->GetCount(); + + // For complete trees, check the correct number of nodes has been created [Opcode 1.3] + if(mPool) ASSERT(mTotalNbNodes==builder->mNbPrimitives*2 - 1); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the depth of the tree. + * A well-balanced tree should have a log(n) depth. A degenerate tree O(n) depth. + * \return depth of the tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::ComputeDepth() const +{ + return Walk(null, null); // Use the walking code without callback +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree, calling the user back for each node. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::Walk(WalkingCallback callback, void* user_data) const +{ + // Call it without callback to compute max depth + udword MaxDepth = 0; + udword CurrentDepth = 0; + + struct Local + { + static void _Walk(const AABBTreeNode* current_node, udword& max_depth, udword& current_depth, WalkingCallback callback, void* user_data) + { + // Checkings + if(!current_node) return; + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, user_data)) return; + + // Recurse + if(current_node->GetPos()) { _Walk(current_node->GetPos(), max_depth, current_depth, callback, user_data); current_depth--; } + if(current_node->GetNeg()) { _Walk(current_node->GetNeg(), max_depth, current_depth, callback, user_data); current_depth--; } + } + }; + Local::_Walk(this, MaxDepth, CurrentDepth, callback, user_data); + return MaxDepth; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a top-down way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit(AABBTreeBuilder* builder) +{ + if(!builder) return false; + _Refit(builder); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a bottom-up way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit2(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder) return false; + + ASSERT(mPool); + + // Bottom-up update + Point Min,Max; + Point Min_,Max_; + udword Index = mTotalNbNodes; + while(Index--) + { + AABBTreeNode& Current = mPool[Index]; + + if(Current.IsLeaf()) + { + builder->ComputeGlobalBox(Current.GetPrimitives(), Current.GetNbPrimitives(), *const_cast(Current.GetAABB())); + } + else + { + Current.GetPos()->GetAABB()->GetMin(Min); + Current.GetPos()->GetAABB()->GetMax(Max); + + Current.GetNeg()->GetAABB()->GetMin(Min_); + Current.GetNeg()->GetAABB()->GetMax(Max_); + + Min.Min(Min_); + Max.Max(Max_); + + const_cast(Current.GetAABB())->SetMinMax(Min, Max); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of bytes used by the tree. + * \return number of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::GetUsedBytes() const +{ + udword TotalSize = mTotalNbNodes*GetNodeSize(); + if(mIndices) TotalSize+=mNbPrimitives*sizeof(udword); + return TotalSize; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the tree is a complete tree or not. + * A complete tree is made of 2*N-1 nodes, where N is the number of primitives in the tree. + * \return true for complete trees + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::IsComplete() const +{ + return (GetNbNodes()==GetNbPrimitives()*2-1); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.h b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.h new file mode 100644 index 0000000..681be9d --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_AABBTree.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a versatile AABB tree. + * \file OPC_AABBTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_AABBTREE_H__ +#define __OPC_AABBTREE_H__ + +#ifdef OPC_NO_NEG_VANILLA_TREE + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { const base_class* P = GetPos(); return P ? P+1 : null;} \ + \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + size_t mPos; /* "Positive" & "Negative" children */ +#else + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { return (const base_class*)(mNeg&~1); } \ + \ +/* inline_ bool IsLeaf() const { return (!GetPos() && !GetNeg()); } */ \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + size_t mPos; /* "Positive" child */ \ + size_t mNeg; /* "Negative" child */ +#endif + + typedef void (*CullingCallback) (udword nb_primitives, udword* node_primitives, BOOL need_clipping, void* user_data); + + class OPCODE_API AABBTreeNode + { + IMPLEMENT_TREE(AABBTreeNode, AABB) + public: + // Data access + inline_ const dTriIndex* GetPrimitives() const { return mNodePrimitives; } + inline_ udword GetNbPrimitives() const { return mNbPrimitives; } + + protected: + // Tree-dependent data + dTriIndex* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + udword mNbPrimitives; //!< Number of primitives for this node + // Internal methods + udword Split(udword axis, AABBTreeBuilder* builder); + bool Subdivide(AABBTreeBuilder* builder); + void _BuildHierarchy(AABBTreeBuilder* builder); + void _Refit(AABBTreeBuilder* builder); + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called for each node by the walking code. + * \param current [in] current node + * \param depth [in] current node's depth + * \param user_data [in] user-defined data + * \return true to recurse through children, else false to bypass them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef bool (*WalkingCallback) (const AABBTreeNode* current, udword depth, void* user_data); + + class OPCODE_API AABBTree : public AABBTreeNode + { + public: + // Constructor / Destructor + AABBTree(); + ~AABBTree(); + // Build + bool Build(AABBTreeBuilder* builder); + void Release(); + + // Data access + inline_ const dTriIndex* GetIndices() const { return mIndices; } //!< Catch the indices + inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + // Infos + bool IsComplete() const; + // Stats + udword ComputeDepth() const; + udword GetUsedBytes() const; + udword Walk(WalkingCallback callback, void* user_data) const; + + bool Refit(AABBTreeBuilder* builder); + bool Refit2(AABBTreeBuilder* builder); + private: + dTriIndex* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + // Stats + udword mTotalNbNodes; //!< Number of nodes in the tree. + }; + +#endif // __OPC_AABBTREE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.cpp new file mode 100644 index 0000000..987c4f9 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.cpp @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base model interface. + * \file OPC_BaseModel.cpp + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * The base class for collision models. + * + * \class BaseModel + * \author Pierre Terdiman + * \version 1.3 + * \date May, 18, 2003 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::BaseModel() : mIMesh(null), mModelCode(0), mSource(null), mTree(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::~BaseModel() +{ + ReleaseBase(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases everything. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void BaseModel::ReleaseBase() +{ + DELETESINGLE(mSource); + DELETESINGLE(mTree); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates an optimized tree according to user-settings, and setups mModelCode. + * \param no_leaf [in] true for "no leaf" tree + * \param quantized [in] true for quantized tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::CreateTree(bool no_leaf, bool quantized) +{ + DELETESINGLE(mTree); + + // Setup model code + if(no_leaf) mModelCode |= OPC_NO_LEAF; + else mModelCode &= ~OPC_NO_LEAF; + + if(quantized) mModelCode |= OPC_QUANTIZED; + else mModelCode &= ~OPC_QUANTIZED; + + // Create the correct class + if(mModelCode & OPC_NO_LEAF) + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedNoLeafTree; + else mTree = new AABBNoLeafTree; + } + else + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedTree; + else mTree = new AABBCollisionTree; + } + CHECKALLOC(mTree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::Refit() +{ + // Refit the optimized tree + return mTree->Refit(mIMesh); + +// Old code kept for reference : refit the source tree then rebuild ! +// if(!mSource) return false; +// // Ouch... +// mSource->Refit(&mTB); +// // Ouch... +// return mTree->Build(mSource); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.h b/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.h new file mode 100644 index 0000000..eef77a6 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_BaseModel.h @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base model interface. + * \file OPC_BaseModel.h + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_BASEMODEL_H__ +#define __OPC_BASEMODEL_H__ + + //! Model creation structure + struct OPCODE_API OPCODECREATE + { + //! Constructor + inline_ OPCODECREATE(): + mIMesh(null), + mSettings(SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER), + mNoLeaf(true), + mQuantized(true), +#ifdef __MESHMERIZER_H__ + mCollisionHull(false), +#endif // __MESHMERIZER_H__ + mKeepOriginal(false), + mCanRemap(false) + { + } + + inline_ OPCODECREATE(MeshInterface *IMesh, const BuildSettings &Settings, bool NoLeaf, bool Quantized): + mIMesh(IMesh), + mSettings(Settings), + mNoLeaf(NoLeaf), + mQuantized(Quantized), +#ifdef __MESHMERIZER_H__ + mCollisionHull(false), +#endif // __MESHMERIZER_H__ + mKeepOriginal(false), + mCanRemap(false) + { + } + + MeshInterface* mIMesh; //!< Mesh interface (access to triangles & vertices) (*) + BuildSettings mSettings; //!< Builder's settings + bool mNoLeaf; //!< true => discard leaf nodes (else use a normal tree) + bool mQuantized; //!< true => quantize the tree (else use a normal tree) +#ifdef __MESHMERIZER_H__ + bool mCollisionHull; //!< true => use convex hull + GJK +#endif // __MESHMERIZER_H__ + bool mKeepOriginal; //!< true => keep a copy of the original tree (debug purpose) + bool mCanRemap; //!< true => allows OPCODE to reorganize client arrays + + // (*) This pointer is saved internally and used by OPCODE until collision structures are released, + // so beware of the object's lifetime. + }; + + enum ModelFlag + { + OPC_QUANTIZED = (1<<0), //!< Compressed/uncompressed tree + OPC_NO_LEAF = (1<<1), //!< Leaf/NoLeaf tree + OPC_SINGLE_NODE = (1<<2) //!< Special case for 1-node models + }; + + class OPCODE_API BaseModel + { + public: + // Constructor/Destructor + BaseModel(); + virtual ~BaseModel(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(const OPCODECREATE& create) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual udword GetUsedBytes() const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the source tree. + * \return generic tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBTree* GetSourceTree() const { return mSource; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBOptimizedTree* GetTree() const { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ AABBOptimizedTree* GetTree() { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of nodes in the tree. + * Should be 2*N-1 for normal trees and N-1 for optimized ones. + * \return number of nodes + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbNodes() const { return mTree->GetNbNodes(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree has leaf nodes or not. + * \return true if the tree has leaf nodes (normal tree), else false (optimized tree) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasLeafNodes() const { return !(mModelCode & OPC_NO_LEAF); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree is quantized or not. + * \return true if the tree is quantized + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsQuantized() const { return mModelCode & OPC_QUANTIZED; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the model has a single node or not. This special case must be handled separately. + * \return true if the model has only 1 node + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasSingleNode() const { return mModelCode & OPC_SINGLE_NODE; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the model's code. + * \return model's code + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetModelCode() const { return mModelCode; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the mesh interface. + * \return mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const MeshInterface* GetMeshInterface() const { return mIMesh; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the mesh interface. + * \param imesh [in] mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMeshInterface(const MeshInterface* imesh) { mIMesh = imesh; } + + protected: + const MeshInterface* mIMesh; //!< User-defined mesh interface + udword mModelCode; //!< Model code = combination of ModelFlag(s) + AABBTree* mSource; //!< Original source tree + AABBOptimizedTree* mTree; //!< Optimized tree owned by the model + // Internal methods + void ReleaseBase(); + bool CreateTree(bool no_leaf, bool quantized); + }; + +#endif //__OPC_BASEMODEL_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_BoxBoxOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_BoxBoxOverlap.h new file mode 100644 index 0000000..757a17d --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_BoxBoxOverlap.h @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * OBB-OBB overlap test using the separating axis theorem. + * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID) + * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion) + * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory) + * - Class III axes can be disabled... (SOLID & Intel fashion) + * - ...or enabled to perform some profiling + * - CPU comparisons used when appropriate + * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID) + * + * \param ea [in] extents from box A + * \param ca [in] center from box A + * \param eb [in] extents from box B + * \param cb [in] center from box B + * \return true if boxes overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb) +{ + // Stats + mNbBVBVTests++; + + float t,t2; + + // Class I : A's basis vectors + float Tx = (mR1to0.m[0][0]*cb.x + mR1to0.m[1][0]*cb.y + mR1to0.m[2][0]*cb.z) + mT1to0.x - ca.x; + t = ea.x + eb.x*mAR.m[0][0] + eb.y*mAR.m[1][0] + eb.z*mAR.m[2][0]; + if(GREATER(Tx, t)) return FALSE; + + float Ty = (mR1to0.m[0][1]*cb.x + mR1to0.m[1][1]*cb.y + mR1to0.m[2][1]*cb.z) + mT1to0.y - ca.y; + t = ea.y + eb.x*mAR.m[0][1] + eb.y*mAR.m[1][1] + eb.z*mAR.m[2][1]; + if(GREATER(Ty, t)) return FALSE; + + float Tz = (mR1to0.m[0][2]*cb.x + mR1to0.m[1][2]*cb.y + mR1to0.m[2][2]*cb.z) + mT1to0.z - ca.z; + t = ea.z + eb.x*mAR.m[0][2] + eb.y*mAR.m[1][2] + eb.z*mAR.m[2][2]; + if(GREATER(Tz, t)) return FALSE; + + // Class II : B's basis vectors + t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = ea.x*mAR.m[0][0] + ea.y*mAR.m[0][1] + ea.z*mAR.m[0][2] + eb.x; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = ea.x*mAR.m[1][0] + ea.y*mAR.m[1][1] + ea.z*mAR.m[1][2] + eb.y; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = ea.x*mAR.m[2][0] + ea.y*mAR.m[2][1] + ea.z*mAR.m[2][2] + eb.z; + if(GREATER(t, t2)) return FALSE; + + // Class III : 9 cross products + // Cool trick: always perform the full test for first level, regardless of settings. + // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! + if(mFullBoxBoxTest || mNbBVBVTests==1) + { + t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = ea.y*mAR.m[0][2] + ea.z*mAR.m[0][1] + eb.y*mAR.m[2][0] + eb.z*mAR.m[1][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B0 + t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = ea.y*mAR.m[1][2] + ea.z*mAR.m[1][1] + eb.x*mAR.m[2][0] + eb.z*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B1 + t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = ea.y*mAR.m[2][2] + ea.z*mAR.m[2][1] + eb.x*mAR.m[1][0] + eb.y*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B2 + t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = ea.x*mAR.m[0][2] + ea.z*mAR.m[0][0] + eb.y*mAR.m[2][1] + eb.z*mAR.m[1][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B0 + t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = ea.x*mAR.m[1][2] + ea.z*mAR.m[1][0] + eb.x*mAR.m[2][1] + eb.z*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B1 + t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = ea.x*mAR.m[2][2] + ea.z*mAR.m[2][0] + eb.x*mAR.m[1][1] + eb.y*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B2 + t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = ea.x*mAR.m[0][1] + ea.y*mAR.m[0][0] + eb.y*mAR.m[2][2] + eb.z*mAR.m[1][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B0 + t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = ea.x*mAR.m[1][1] + ea.y*mAR.m[1][0] + eb.x*mAR.m[2][2] + eb.z*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B1 + t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = ea.x*mAR.m[2][1] + ea.y*mAR.m[2][0] + eb.x*mAR.m[1][2] + eb.y*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B2 + } + return TRUE; +} + +//! A dedicated version when one box is constant +inline_ BOOL OBBCollider::BoxBoxOverlap(const Point& extents, const Point& center) +{ + // Stats + mNbVolumeBVTests++; + + float t,t2; + + // Class I : A's basis vectors + float Tx = mTBoxToModel.x - center.x; t = extents.x + mBBx1; if(GREATER(Tx, t)) return FALSE; + float Ty = mTBoxToModel.y - center.y; t = extents.y + mBBy1; if(GREATER(Ty, t)) return FALSE; + float Tz = mTBoxToModel.z - center.z; t = extents.z + mBBz1; if(GREATER(Tz, t)) return FALSE; + + // Class II : B's basis vectors + t = Tx*mRBoxToModel.m[0][0] + Ty*mRBoxToModel.m[0][1] + Tz*mRBoxToModel.m[0][2]; + t2 = extents.x*mAR.m[0][0] + extents.y*mAR.m[0][1] + extents.z*mAR.m[0][2] + mBoxExtents.x; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mRBoxToModel.m[1][0] + Ty*mRBoxToModel.m[1][1] + Tz*mRBoxToModel.m[1][2]; + t2 = extents.x*mAR.m[1][0] + extents.y*mAR.m[1][1] + extents.z*mAR.m[1][2] + mBoxExtents.y; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mRBoxToModel.m[2][0] + Ty*mRBoxToModel.m[2][1] + Tz*mRBoxToModel.m[2][2]; + t2 = extents.x*mAR.m[2][0] + extents.y*mAR.m[2][1] + extents.z*mAR.m[2][2] + mBoxExtents.z; + if(GREATER(t, t2)) return FALSE; + + // Class III : 9 cross products + // Cool trick: always perform the full test for first level, regardless of settings. + // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! + if(mFullBoxBoxTest || mNbVolumeBVTests==1) + { + t = Tz*mRBoxToModel.m[0][1] - Ty*mRBoxToModel.m[0][2]; t2 = extents.y*mAR.m[0][2] + extents.z*mAR.m[0][1] + mBB_1; if(GREATER(t, t2)) return FALSE; // L = A0 x B0 + t = Tz*mRBoxToModel.m[1][1] - Ty*mRBoxToModel.m[1][2]; t2 = extents.y*mAR.m[1][2] + extents.z*mAR.m[1][1] + mBB_2; if(GREATER(t, t2)) return FALSE; // L = A0 x B1 + t = Tz*mRBoxToModel.m[2][1] - Ty*mRBoxToModel.m[2][2]; t2 = extents.y*mAR.m[2][2] + extents.z*mAR.m[2][1] + mBB_3; if(GREATER(t, t2)) return FALSE; // L = A0 x B2 + t = Tx*mRBoxToModel.m[0][2] - Tz*mRBoxToModel.m[0][0]; t2 = extents.x*mAR.m[0][2] + extents.z*mAR.m[0][0] + mBB_4; if(GREATER(t, t2)) return FALSE; // L = A1 x B0 + t = Tx*mRBoxToModel.m[1][2] - Tz*mRBoxToModel.m[1][0]; t2 = extents.x*mAR.m[1][2] + extents.z*mAR.m[1][0] + mBB_5; if(GREATER(t, t2)) return FALSE; // L = A1 x B1 + t = Tx*mRBoxToModel.m[2][2] - Tz*mRBoxToModel.m[2][0]; t2 = extents.x*mAR.m[2][2] + extents.z*mAR.m[2][0] + mBB_6; if(GREATER(t, t2)) return FALSE; // L = A1 x B2 + t = Ty*mRBoxToModel.m[0][0] - Tx*mRBoxToModel.m[0][1]; t2 = extents.x*mAR.m[0][1] + extents.y*mAR.m[0][0] + mBB_7; if(GREATER(t, t2)) return FALSE; // L = A2 x B0 + t = Ty*mRBoxToModel.m[1][0] - Tx*mRBoxToModel.m[1][1]; t2 = extents.x*mAR.m[1][1] + extents.y*mAR.m[1][0] + mBB_8; if(GREATER(t, t2)) return FALSE; // L = A2 x B1 + t = Ty*mRBoxToModel.m[2][0] - Tx*mRBoxToModel.m[2][1]; t2 = extents.x*mAR.m[2][1] + extents.y*mAR.m[2][0] + mBB_9; if(GREATER(t, t2)) return FALSE; // L = A2 x B2 + } + return TRUE; +} + +//! A special version for 2 axis-aligned boxes +inline_ BOOL AABBCollider::AABBAABBOverlap(const Point& extents, const Point& center) +{ + // Stats + mNbVolumeBVTests++; + + float tx = mBox.mCenter.x - center.x; float ex = extents.x + mBox.mExtents.x; if(GREATER(tx, ex)) return FALSE; + float ty = mBox.mCenter.y - center.y; float ey = extents.y + mBox.mExtents.y; if(GREATER(ty, ey)) return FALSE; + float tz = mBox.mCenter.z - center.z; float ez = extents.z + mBox.mExtents.z; if(GREATER(tz, ez)) return FALSE; + + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.cpp new file mode 100644 index 0000000..f41fc36 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.cpp @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains the abstract class for colliders. + * + * \class Collider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::Collider() : + mFlags (0), + mCurrentModel (null), + mIMesh (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::~Collider() +{ +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.h new file mode 100644 index 0000000..d718e02 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Collider.h @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_COLLIDER_H__ +#define __OPC_COLLIDER_H__ + + enum CollisionFlag + { + OPC_FIRST_CONTACT = (1<<0), //!< Report all contacts (false) or only first one (true) + OPC_TEMPORAL_COHERENCE = (1<<1), //!< Use temporal coherence or not + OPC_CONTACT = (1<<2), //!< Final contact status after a collision query + OPC_TEMPORAL_HIT = (1<<3), //!< There has been an early exit due to temporal coherence + OPC_NO_PRIMITIVE_TESTS = (1<<4), //!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries) + + OPC_CONTACT_FOUND = OPC_FIRST_CONTACT | OPC_CONTACT, + OPC_TEMPORAL_CONTACT = OPC_TEMPORAL_HIT | OPC_CONTACT, + + OPC_FORCE_DWORD = 0x7fffffff + }; + + class OPCODE_API Collider + { + public: + // Constructor / Destructor + Collider(); + virtual ~Collider(); + + // Collision report + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the last collision status after a collision query. + * \return true if a collision occured + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL GetContactStatus() const { return mFlags & OPC_CONTACT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the "first contact" mode. + * \return true if "first contact" mode is on + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL FirstContactEnabled() const { return mFlags & OPC_FIRST_CONTACT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the temporal coherence mode. + * \return true if temporal coherence is on + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL TemporalCoherenceEnabled() const { return mFlags & OPC_TEMPORAL_COHERENCE; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks a first contact has already been found. + * \return true if a first contact has been found and we can stop a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL ContactFound() const { return (mFlags&OPC_CONTACT_FOUND)==OPC_CONTACT_FOUND; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks there's been an early exit due to temporal coherence; + * \return true if a temporal hit has occured + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL TemporalHit() const { return mFlags & OPC_TEMPORAL_HIT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks primitive tests are enabled; + * \return true if primitive tests must be skipped + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL SkipPrimitiveTests() const { return mFlags & OPC_NO_PRIMITIVE_TESTS; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Reports all contacts (false) or first contact only (true) + * \param flag [in] true for first contact, false for all contacts + * \see SetTemporalCoherence(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFirstContact(bool flag) + { + if(flag) mFlags |= OPC_FIRST_CONTACT; + else mFlags &= ~OPC_FIRST_CONTACT; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable temporal coherence. + * \param flag [in] true to enable temporal coherence, false to discard it + * \see SetFirstContact(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetTemporalCoherence(bool flag) + { + if(flag) mFlags |= OPC_TEMPORAL_COHERENCE; + else mFlags &= ~OPC_TEMPORAL_COHERENCE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable primitive tests. + * \param flag [in] true to enable primitive tests, false to discard them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetPrimitiveTests(bool flag) + { + if(!flag) mFlags |= OPC_NO_PRIMITIVE_TESTS; + else mFlags &= ~OPC_NO_PRIMITIVE_TESTS; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const char* ValidateSettings() = 0; + + protected: + udword mFlags; //!< Bit flags + const BaseModel* mCurrentModel; //!< Current model for collision query (owner of touched faces) + // User mesh interface + const MeshInterface* mIMesh; //!< User-defined mesh interface + + // Internal methods + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups current collision model + * \param model [in] current collision model + * \return TRUE if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Setup(const BaseModel* model) + { + // Keep track of current model + mCurrentModel = model; + if(!mCurrentModel) return FALSE; + + mIMesh = model->GetMeshInterface(); + return mIMesh!=null; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual inline_ void InitQuery() { mFlags &= ~OPC_TEMPORAL_CONTACT; } + }; + +#endif // __OPC_COLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Common.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_Common.cpp new file mode 100644 index 0000000..5b9a9c8 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Common.cpp @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains common classes & defs used in OPCODE. + * \file OPC_Common.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An AABB dedicated to collision detection. + * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends + * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth + * using an extra special class. + * + * \class CollisionAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized AABB. + * Center/Extent model, using 16-bits integers. + * + * \class QuantizedAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Common.h b/thirdparty/ode-0.16.5/OPCODE/OPC_Common.h new file mode 100644 index 0000000..f134990 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Common.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains common classes & defs used in OPCODE. + * \file OPC_Common.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_COMMON_H__ +#define __OPC_COMMON_H__ + +// [GOTTFRIED]: Just a small change for readability. +#ifdef OPC_CPU_COMPARE + #define GREATER(x, y) AIR(x) > IR(y) +#else + #define GREATER(x, y) fabsf(x) > (y) +#endif + + class OPCODE_API CollisionAABB + { + public: + //! Constructor + inline_ CollisionAABB() {} + //! Constructor + inline_ CollisionAABB(const AABB& b) { b.GetCenter(mCenter); b.GetExtents(mExtents); } + //! Destructor + inline_ ~CollisionAABB() {} + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks a box is inside another box. + * \param box [in] the other box + * \return true if current box is inside input box + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsInside(const CollisionAABB& box) const + { + if(box.GetMin(0)>GetMin(0)) return FALSE; + if(box.GetMin(1)>GetMin(1)) return FALSE; + if(box.GetMin(2)>GetMin(2)) return FALSE; + if(box.GetMax(0)IsValid()) return false; + + // Look for degenerate faces. + //udword NbDegenerate = create.mIMesh->CheckTopology(); + //if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); + // We continue nonetheless.... + + Release(); // Make sure previous tree has been discarded + + // 1-1) Setup mesh interface automatically + SetMeshInterface(create.mIMesh); + + bool Status = false; + AABBTree* LeafTree = null; + Internal Data; + + // 2) Build a generic AABB Tree. + mSource = new AABBTree; + CHECKALLOC(mSource); + + // 2-1) Setup a builder. Our primitives here are triangles from input mesh, + // so we use an AABBTreeOfTrianglesBuilder..... + { + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mNbPrimitives = create.mIMesh->GetNbTriangles(); + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ... + if(!mSource->Build(&TB)) goto FreeAndExit; + } + + // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time) + struct Local + { + // A callback to count leaf nodes + static bool CountLeaves(const AABBTreeNode* current, udword /*depth*/, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + Data->mNbLeaves++; + } + return true; + } + + // A callback to setup leaf nodes in our internal structures + static bool SetupLeafData(const AABBTreeNode* current, udword /*depth*/, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + + // Get current leaf's box + Data->mLeaves[Data->mNbLeaves] = *current->GetAABB(); + + // Setup leaf data + udword Index = udword((size_t(current->GetPrimitives()) - size_t(Data->mBase)) / sizeof(udword)); + Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index); + + Data->mNbLeaves++; + } + return true; + } + }; + + // Walk the tree & count number of leaves + Data.mNbLeaves = 0; + mSource->Walk(Local::CountLeaves, &Data); + mNbLeaves = Data.mNbLeaves; // Keep track of it + + // Special case for 1-leaf meshes + if(mNbLeaves==1) + { + mModelCode |= OPC_SINGLE_NODE; + Status = true; + goto FreeAndExit; + } + + // Allocate our structures + Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves); + mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles); + + // Walk the tree again & setup leaf data + Data.mTriangles = mTriangles; + Data.mBase = mSource->GetIndices(); + Data.mNbLeaves = 0; // Reset for incoming walk + mSource->Walk(Local::SetupLeafData, &Data); + + // Handle source indices + { + bool MustKeepIndices = true; + if(create.mCanRemap) + { + // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays... + // Remap can fail when we use callbacks => keep track of indices in that case (it still + // works, only using more memory) + if(create.mIMesh->RemapClient(mSource->GetNbPrimitives(), mSource->GetIndices())) + { + MustKeepIndices = false; + } + } + + if(MustKeepIndices) + { + // Keep track of source indices (from vanilla tree) + mNbPrimitives = mSource->GetNbPrimitives(); + mIndices = new udword[mNbPrimitives]; + CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword)); + } + } + + // Now, create our optimized tree using previous leaf nodes + LeafTree = new AABBTree; + CHECKALLOC(LeafTree); + { + AABBTreeOfAABBsBuilder TB; // Now using boxes ! + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it + TB.mNbPrimitives = Data.mNbLeaves; + TB.mAABBArray = Data.mLeaves; + if(!LeafTree->Build(&TB)) goto FreeAndExit; + } + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit; + + // 3-2) Create optimized tree + if(!mTree->Build(LeafTree)) goto FreeAndExit; + + // Finally ok... + Status = true; + +FreeAndExit: // Allow me this one... + DELETESINGLE(LeafTree); + + // 3-3) Delete generic tree if needed + if(!create.mKeepOriginal) DELETESINGLE(mSource); + + return Status; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword HybridModel::GetUsedBytes() const +{ + udword UsedBytes = 0; + if(mTree) UsedBytes += mTree->GetUsedBytes(); + if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices + if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles + return UsedBytes; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool HybridModel::Refit() +{ + if(!mIMesh) return false; + if(!mTree) return false; + + if(IsQuantized()) return false; + if(HasLeafNodes()) return false; + + const LeafTriangles* LT = GetLeafTriangles(); + const udword* Indices = GetIndices(); + + // Bottom-up update + VertexPointers VP; + ConversionArea VC; + Point Min,Max; + Point Min_,Max_; + udword Index = mTree->GetNbNodes(); + AABBNoLeafNode* Nodes = const_cast(static_cast(static_cast(mTree)->GetNodes())); + while(Index--) + { + AABBNoLeafNode& Current = Nodes[Index]; + + if(Current.HasPosLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()]; + + Min.SetPlusInfinity(); + Max.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++, VC); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++, VC); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + + if(Current.HasNegLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()]; + + Min_.SetPlusInfinity(); + Max_.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++, VC); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++, VC); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_HybridModel.h b/thirdparty/ode-0.16.5/OPCODE/OPC_HybridModel.h new file mode 100644 index 0000000..c7eb59d --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_HybridModel.h @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for hybrid models. + * \file OPC_HybridModel.h + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_HYBRIDMODEL_H__ +#define __OPC_HYBRIDMODEL_H__ + + //! Leaf descriptor + struct LeafTriangles + { + udword Data; //!< Packed data + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets number of triangles in the leaf. + * \return number of triangles N, with 0 < N <= 16 + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbTriangles() const { return (Data & 15)+1; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets triangle index for this leaf. Indexed model's array of indices retrieved with HybridModel::GetIndices() + * \return triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetTriangleIndex() const { return Data>>4; } + inline_ void SetData(udword nb, udword index) { ASSERT(nb>0 && nb<=16); nb--; Data = (index<<4)|(nb&15); } + }; + + class OPCODE_API HybridModel : public BaseModel + { + public: + // Constructor/Destructor + HybridModel(); + virtual ~HybridModel(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Build(const OPCODECREATE& create); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) udword GetUsedBytes() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Refit(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets array of triangles. + * \return array of triangles + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const LeafTriangles* GetLeafTriangles() const { return mTriangles; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets array of indices. + * \return array of indices + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const udword* GetIndices() const { return mIndices; } + + private: + udword mNbLeaves; //!< Number of leaf nodes in the model + LeafTriangles* mTriangles; //!< Array of mNbLeaves leaf descriptors + udword mNbPrimitives; //!< Number of primitives in the model + udword* mIndices; //!< Array of primitive indices + + // Internal methods + void Release(); + }; + +#endif // __OPC_HYBRIDMODEL_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_IceHook.h b/thirdparty/ode-0.16.5/OPCODE/OPC_IceHook.h new file mode 100644 index 0000000..4fc6c8a --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_IceHook.h @@ -0,0 +1,80 @@ + +// Should be included by Opcode.h if needed + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_ICEHOOK_H__ +#define __OPC_ICEHOOK_H__ + + #define ICE_DONT_CHECK_COMPILER_OPTIONS + + // From Windows... + typedef int BOOL; + #ifndef FALSE + #define FALSE 0 + #endif + + #ifndef TRUE + #define TRUE 1 + #endif + + #include + #include + #include + #include + #include + #include + + #ifndef ASSERT + #define ASSERT(exp) {} + #endif + #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ] + + #define Log {} + #define SetIceError(a,b) false + #define EC_OUTOFMEMORY "Out of memory" + + extern void OPCODE_NORETURN IceAbort(); + + #include "Ice/IcePreprocessor.h" + + #undef ICECORE_API + #define ICECORE_API OPCODE_API + + #include "Ice/IceTypes.h" + #include "Ice/IceFPU.h" + #include "Ice/IceMemoryMacros.h" + + namespace IceCore + { + #include "Ice/IceUtils.h" + #include "Ice/IceContainer.h" + #include "Ice/IcePairs.h" + #include "Ice/IceRevisitedRadix.h" + #include "Ice/IceRandom.h" + } + using namespace IceCore; + + #define ICEMATHS_API OPCODE_API + namespace IceMaths + { + #include "Ice/IceAxes.h" + #include "Ice/IcePoint.h" + #include "Ice/IceHPoint.h" + #include "Ice/IceMatrix3x3.h" + #include "Ice/IceMatrix4x4.h" + #include "Ice/IcePlane.h" + #include "Ice/IceRay.h" + #include "Ice/IceIndexedTriangle.h" + #include "Ice/IceTriangle.h" + #include "Ice/IceTriList.h" + #include "Ice/IceAABB.h" + #include "Ice/IceOBB.h" + #include "Ice/IceBoundingSphere.h" + #include "Ice/IceSegment.h" + #include "Ice/IceLSS.h" + } + using namespace IceMaths; + + +#endif // __OPC_ICEHOOK_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_LSSAABBOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSAABBOverlap.h new file mode 100644 index 0000000..c6ba9d3 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSAABBOverlap.h @@ -0,0 +1,525 @@ + +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +inline_ float OPC_PointAABBSqrDist(const Point& point, const Point& center, const Point& extents) +{ + // Compute coordinates of point in box coordinate system + Point Closest = point - center; + + float SqrDistance = 0.0f; + + if(Closest.x < -extents.x) + { + float Delta = Closest.x + extents.x; + SqrDistance += Delta*Delta; + } + else if(Closest.x > extents.x) + { + float Delta = Closest.x - extents.x; + SqrDistance += Delta*Delta; + } + + if(Closest.y < -extents.y) + { + float Delta = Closest.y + extents.y; + SqrDistance += Delta*Delta; + } + else if(Closest.y > extents.y) + { + float Delta = Closest.y - extents.y; + SqrDistance += Delta*Delta; + } + + if(Closest.z < -extents.z) + { + float Delta = Closest.z + extents.z; + SqrDistance += Delta*Delta; + } + else if(Closest.z > extents.z) + { + float Delta = Closest.z - extents.z; + SqrDistance += Delta*Delta; + } + return SqrDistance; +} + +static void Face(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, const Point& rkPmE, float* pfLParam, float& rfSqrDistance) +{ + Point kPpE; + float fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void CaseNoZeros(Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + Point kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + float fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + Face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + Face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void Case0(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fPmE0 = rkPnt[i0] - extents[i0]; + float fPmE1 = rkPnt[i1] - extents[i1]; + float fProd0 = rkDir[i1]*fPmE0; + float fProd1 = rkDir[i0]*fPmE1; + float fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + float fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + float fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case00(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case000(Point& rkPnt, const Point& extents, float& rfSqrDistance) +{ + float fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +static float SqrDistance(const Ray& rkLine, const Point& center, const Point& extents, float* pfLParam) +{ + // compute coordinates of line in box coordinate system + Point kDiff = rkLine.mOrig - center; + Point kPnt = kDiff; + Point kDir = rkLine.mDir; + +#if 0 + // Apply reflections so that direction vector has nonnegative components. + bool bReflect[3]; + for(int i=0;i<3;i++) + { + if(kDir[i]<0.0f) + { + kPnt[i] = -kPnt[i]; + kDir[i] = -kDir[i]; + bReflect[i] = true; + } + else + { + bReflect[i] = false; + } + } +#endif + + float fSqrDistance = 0.0f; + + if(kDir.x>0.0f) + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) CaseNoZeros(kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,+) + else Case0(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,0) + } + else + { + if(kDir.z>0.0f) Case0(0, 2, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,+) + else Case00(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,0) + } + } + else + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) Case0(1, 2, 0, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,+) + else Case00(1, 0, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,0) + } + else + { + if(kDir.z>0.0f) Case00(2, 0, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,0,+) + else + { + Case000(kPnt, extents, fSqrDistance); // (0,0,0) + if(pfLParam) *pfLParam = 0.0f; + } + } + } + return fSqrDistance; +} + +inline_ float OPC_SegmentOBBSqrDist(const Segment& segment, const Point& c0, const Point& e0) +{ + float fLP; + float fSqrDistance = SqrDistance(Ray(segment.GetOrigin(), segment.ComputeDirection()), c0, e0, &fLP); + if(fLP>=0.0f) + { + if(fLP<=1.0f) return fSqrDistance; + else return OPC_PointAABBSqrDist(segment.mP1, c0, e0); + } + else return OPC_PointAABBSqrDist(segment.mP0, c0, e0); +} + +inline_ BOOL LSSCollider::LSSAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbVolumeBVTests++; + + float s2 = OPC_SegmentOBBSqrDist(mSeg, center, extents); + if(s2Add(udword(prim_index)); + +//! LSS-triangle overlap test +#define LSS_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \ + \ + /* Perform LSS-tri overlap test */ \ + if(LSSTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::LSSCollider() +{ +// mCenter.Zero(); +// mRadius2 = 0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::~LSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] an lss cache + * \param lss [in] lss in local space + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL LSSCollider::InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute LSS in model space: + // - Precompute R^2 + mRadius2 = lss.mRadius * lss.mRadius; + // - Compute segment + mSeg.mP0 = lss.mP0; + mSeg.mP1 = lss.mP1; + // -> to world space + if(worldl) + { + mSeg.mP0 *= *worldl; + mSeg.mP1 *= *worldl; + } + // -> to model space + if(worldm) + { + // Invert model matrix + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + mSeg.mP0 *= InvWorldM; + mSeg.mP1 *= InvWorldM; + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the LSS (and set contact status if needed) + LSS_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the LSS (and set contact status if needed) + LSS_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real LSS N(ew) against the previous fat LSS P(revious): + + // ### rewrite this + + LSS Test(mSeg, lss.mRadius); // in model space + LSS Previous(cache.Previous, sqrtf(cache.Previous.mRadius)); + +// if(cache.Previous.Contains(Test)) + if(IsCacheValid(cache) && Previous.Contains(Test)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat sphere so that coherence will work for subsequent frames + mRadius2 *= cache.FatCoeff; +// mRadius2 = (lss.mRadius * cache.FatCoeff)*(lss.mRadius * cache.FatCoeff); + + + // Update cache with query data (signature for cached faces) + cache.Previous.mP0 = mSeg.mP0; + cache.Previous.mP1 = mSeg.mP1; + cache.Previous.mRadius = mRadius2; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, lss)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the LSS completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the LSS contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL LSSCollider::LSSContainsBox(const Point& bc, const Point& be) +{ + // Not implemented + return FALSE; +} + +#define TEST_BOX_IN_LSS(center, extents) \ + if(LSSContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBTreeNode* node) +{ + // Perform LSS-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!LSSAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || LSSContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::HybridLSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::~HybridLSSCollider() +{ +} + +bool HybridLSSCollider::Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;i(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_LSSCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSCollider.h new file mode 100644 index 0000000..b4d0893 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSCollider.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an LSS collider. + * \file OPC_LSSCollider.h + * \author Pierre Terdiman + * \date December, 28, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_LSSCOLLIDER_H__ +#define __OPC_LSSCOLLIDER_H__ + + struct OPCODE_API LSSCache : VolumeCache + { + LSSCache() + { + Previous.mP0 = Point(0.0f, 0.0f, 0.0f); + Previous.mP1 = Point(0.0f, 0.0f, 0.0f); + Previous.mRadius = 0.0f; + FatCoeff = 1.1f; + } + + // Cached faces signature + LSS Previous; //!< LSS used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat LSS + }; + + class OPCODE_API LSSCollider : public VolumeCollider + { + public: + // Constructor / Destructor + LSSCollider(); + virtual ~LSSCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + // + bool Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree); + protected: + // LSS in model space + Segment mSeg; //!< Segment + float mRadius2; //!< LSS radius squared + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL LSSContainsBox(const Point& bc, const Point& be); + inline_ BOOL LSSAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridLSSCollider : public LSSCollider + { + public: + // Constructor / Destructor + HybridLSSCollider(); + virtual ~HybridLSSCollider(); + + bool Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + +#endif // __OPC_LSSCOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_LSSTriOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSTriOverlap.h new file mode 100644 index 0000000..f1d17e4 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_LSSTriOverlap.h @@ -0,0 +1,679 @@ +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +static const float gs_fTolerance = 1e-05f; + +static float OPC_PointTriangleSqrDist(const Point& point, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + Point TriEdge0 = p1 - p0; + Point TriEdge1 = p2 - p0; + + Point kDiff = p0 - point; + float fA00 = TriEdge0.SquareMagnitude(); + float fA01 = TriEdge0 | TriEdge1; + float fA11 = TriEdge1.SquareMagnitude(); + float fB0 = kDiff | TriEdge0; + float fB1 = kDiff | TriEdge1; + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11 - fA01*fA01); + float fS = fA01*fB1-fA11*fB0; + float fT = fA01*fB0-fA00*fB1; + float fSqrDist; + + if(fS + fT <= fDet) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else // region 3 + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 5 + { + if(fB0 >= 0.0f) fSqrDist = fC; + else if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { + fSqrDist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(fS < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(fB1 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = fNumer/fDenom; + fS = 1.0f - fT; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(fB0 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + } + return fabsf(fSqrDist); +} + +static float OPC_SegmentSegmentSqrDist(const Segment& rkSeg0, const Segment& rkSeg1) +{ + // Hook + Point rkSeg0Direction = rkSeg0.ComputeDirection(); + Point rkSeg1Direction = rkSeg1.ComputeDirection(); + + Point kDiff = rkSeg0.mP0 - rkSeg1.mP0; + float fA00 = rkSeg0Direction.SquareMagnitude(); + float fA01 = -rkSeg0Direction.Dot(rkSeg1Direction); + float fA11 = rkSeg1Direction.SquareMagnitude(); + float fB0 = kDiff.Dot(rkSeg0Direction); + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11-fA01*fA01); + + float fB1, fS, fT, fSqrDist, fTmp; + + if(fDet>=gs_fTolerance) + { + // line segments are not parallel + fB1 = -kDiff.Dot(rkSeg1Direction); + fS = fA01*fB1-fA11*fB0; + fT = fA01*fB0-fA00*fB1; + + if(fS >= 0.0f) + { + if(fS <= fDet) + { + if(fT >= 0.0f) + { + if(fT <= fDet) // region 0 (interior) + { + // minimum at two interior points of 3D lines + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fTmp = fA01+fB0; + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + } + else // region 7 (side) + { + if(fB0>=0.0f) fSqrDist = fC; + else if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else + { + if ( fT >= 0.0 ) + { + if ( fT <= fDet ) // region 1 (side) + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + else // region 2 (corner) + { + fTmp = fA01+fB0; + if ( -fTmp <= fA00 ) + { + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + else // region 8 (corner) + { + if ( -fB0 < fA00 ) + { + if(fB0>=0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + } + else + { + if ( fT >= 0.0f ) + { + if ( fT <= fDet ) // region 5 (side) + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + else // region 4 (corner) + { + fTmp = fA01+fB0; + if ( fTmp < 0.0f ) + { + if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + else // region 6 (corner) + { + if ( fB0 < 0.0f ) + { + if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + } + else + { + // line segments are parallel + if ( fA01 > 0.0f ) + { + // direction vectors form an obtuse angle + if ( fB0 >= 0.0f ) + { + fSqrDist = fC; + } + else if ( -fB0 <= fA00 ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + fTmp = fA00+fB0; + if ( -fTmp >= fA01 ) + { + fSqrDist = fA00+fA11+fC+2.0f*(fA01+fB0+fB1); + } + else + { + fT = -fTmp/fA01; + fSqrDist = fA00+2.0f*fB0+fC+fT*(fA11*fT+2.0f*(fA01+fB1)); + } + } + } + else + { + // direction vectors form an acute angle + if ( -fB0 >= fA00 ) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else if ( fB0 <= 0.0f ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + if ( fB0 >= -fA01 ) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = -fB0/fA01; + fSqrDist = fC+fT*(2.0f*fB1+fA11*fT); + } + } + } + } + return fabsf(fSqrDist); +} + +inline_ float OPC_SegmentRaySqrDist(const Segment& rkSeg0, const Ray& rkSeg1) +{ + return OPC_SegmentSegmentSqrDist(rkSeg0, Segment(rkSeg1.mOrig, rkSeg1.mOrig + rkSeg1.mDir)); +} + +static float OPC_SegmentTriangleSqrDist(const Segment& segment, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + const Point TriEdge0 = p1 - p0; + const Point TriEdge1 = p2 - p0; + + const Point& rkSegOrigin = segment.GetOrigin(); + Point rkSegDirection = segment.ComputeDirection(); + + Point kDiff = p0 - rkSegOrigin; + float fA00 = rkSegDirection.SquareMagnitude(); + float fA01 = -rkSegDirection.Dot(TriEdge0); + float fA02 = -rkSegDirection.Dot(TriEdge1); + float fA11 = TriEdge0.SquareMagnitude(); + float fA12 = TriEdge0.Dot(TriEdge1); + float fA22 = TriEdge1.Dot(TriEdge1); + float fB0 = -kDiff.Dot(rkSegDirection); + float fB1 = kDiff.Dot(TriEdge0); + float fB2 = kDiff.Dot(TriEdge1); + float fCof00 = fA11*fA22-fA12*fA12; + float fCof01 = fA02*fA12-fA01*fA22; + float fCof02 = fA01*fA12-fA02*fA11; + float fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + Ray kTriSeg; + Point kPt; + float fSqrDist, fSqrDist0; + + if(fabsf(fDet)>=gs_fTolerance) + { + float fCof11 = fA00*fA22-fA02*fA02; + float fCof12 = fA02*fA01-fA00*fA12; + float fCof22 = fA00*fA11-fA01*fA01; + float fInvDet = 1.0f/fDet; + float fRhs0 = -fB0*fInvDet; + float fRhs1 = -fB1*fInvDet; + float fRhs2 = -fB2*fInvDet; + + float fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + float fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + float fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if ( fR < 0.0f ) + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4m + { + // min on face s=0 or t=0 or r=0 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0 1 + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4p + { + // min on face s=0 or t=0 or r=1 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0GetTriangle(triangle_index); + * // Setup pointers to vertices for the collision system + * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]); + * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]); + * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]); + * } + * + * // Setup callbacks + * MeshInterface0->SetCallback(ColCallback, udword(Mesh0)); + * MeshInterface1->SetCallback(ColCallback, udword(Mesh1)); + * \endcode + * + * Of course, you should make this callback as fast as possible. And you're also not supposed + * to modify the geometry *after* the collision trees have been built. The alternative was to + * store the geometry & topology in the collision system as well (as in RAPID) but we have found + * this approach to waste a lot of ram in many cases. + * + * + * POINTERS: + * + * If you're internally using the following canonical structures: + * - a vertex made of three 32-bits floating point values + * - a triangle made of three 32-bits integer vertex references + * ...then you may want to use pointers instead of callbacks. This is the same, except OPCODE will directly + * use provided pointers to access the topology and geometry, without using a callback. It might be faster, + * but probably not as safe. Pointers have been introduced in OPCODE 1.2. + * + * Ex: + * + * \code + * // Setup pointers + * MeshInterface0->SetPointers(Mesh0->GetFaces(), Mesh0->GetVerts()); + * MeshInterface1->SetPointers(Mesh1->GetFaces(), Mesh1->GetVerts()); + * \endcode + * + * + * STRIDES: + * + * If your vertices are D3D-like entities interleaving a position, a normal and/or texture coordinates + * (i.e. if your vertices are FVFs), you might want to use a vertex stride to skip extra data OPCODE + * doesn't need. Using a stride shouldn't be notably slower than not using it, but it might increase + * cache misses. Please also note that you *shouldn't* read from AGP or video-memory buffers ! + * + * + * In any case, compilation flags are here to select callbacks/pointers/strides at compile time, so + * choose what's best for your application. All of this has been wrapped into this MeshInterface. + * + * \class MeshInterface + * \author Pierre Terdiman + * \version 1.3 + * \date November, 27, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::MeshInterface() : + mNbTris (0), + mNbVerts (0), +#ifdef OPC_USE_CALLBACKS + mUserData (null), + mObjCallback (null), + mExUserData (null), + mObjExCallback (null), +#else + #ifdef OPC_USE_STRIDE + mTriStride (sizeof(IndexedTriangle)), + mVertexStride (sizeof(Point)), + mFetchTriangle (&MeshInterface::FetchTriangleFromSingles), + mFetchExTriangle (&MeshInterface::FetchExTriangleFromSingles), + #endif + mTris (null), + mVerts (null) +#endif +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::~MeshInterface() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool MeshInterface::IsValid() const +{ + if(!mNbTris || !mNbVerts) return false; +#ifdef OPC_USE_CALLBACKS + if(!mObjCallback) return false; +#else + if(!mTris || !mVerts) return false; +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword MeshInterface::CheckTopology() const +{ + // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases. + // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner + // you can try this: www.codercorner.com/Consolidation.zip + + udword NbDegenerate = 0; + + VertexPointers VP; + ConversionArea VC; + + // Using callbacks, we don't have access to vertex indices. Nevertheless we still can check for + // redundant vertex pointers, which cover all possibilities (callbacks/pointers/strides). + for(udword i=0;imVRef[0] * VertexStride); + vp.Vertex[1] = (const Point*)(((ubyte*)Verts) + T->mVRef[1] * VertexStride); + vp.Vertex[2] = (const Point*)(((ubyte*)Verts) + T->mVRef[2] * VertexStride); +} + +void MeshInterface::FetchTriangleFromDoubles(VertexPointers& vp, udword index, ConversionArea vc) const +{ + const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride); + + const Point* Verts = GetVerts(); + udword VertexStride = GetVertexStride(); + + for (int i = 0; i < 3; i++){ + const double* v = (const double*)(((ubyte*)Verts) + T->mVRef[i] * VertexStride); + + vc[i].x = (float)v[0]; + vc[i].y = (float)v[1]; + vc[i].z = (float)v[2]; + vp.Vertex[i] = &vc[i]; + } +} + +void MeshInterface::FetchExTriangleFromSingles(VertexPointersEx& vpe, udword index, ConversionArea vc) const +{ + const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride); + + const Point* Verts = GetVerts(); + udword VertexStride = GetVertexStride(); + + dTriIndex VertIndex0 = T->mVRef[0]; + vpe.Index[0] = VertIndex0; + vpe.vp.Vertex[0] = (const Point*)(((ubyte*)Verts) + VertIndex0 * VertexStride); + + dTriIndex VertIndex1 = T->mVRef[1]; + vpe.Index[1] = VertIndex1; + vpe.vp.Vertex[1] = (const Point*)(((ubyte*)Verts) + VertIndex1 * VertexStride); + + dTriIndex VertIndex2 = T->mVRef[2]; + vpe.Index[2] = VertIndex2; + vpe.vp.Vertex[2] = (const Point*)(((ubyte*)Verts) + VertIndex2 * VertexStride); +} + +void MeshInterface::FetchExTriangleFromDoubles(VertexPointersEx& vpe, udword index, ConversionArea vc) const +{ + const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride); + + const Point* Verts = GetVerts(); + udword VertexStride = GetVertexStride(); + + for (int i = 0; i < 3; i++){ + dTriIndex VertIndex = T->mVRef[i]; + vpe.Index[i] = VertIndex; + + const double* v = (const double*)(((ubyte*)Verts) + VertIndex * VertexStride); + vc[i].x = (float)v[0]; + vc[i].y = (float)v[1]; + vc[i].z = (float)v[2]; + vpe.vp.Vertex[i] = &vc[i]; + } +} +#endif +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Remaps client's mesh according to a permutation. + * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles) + * \param permutation [in] list of triangle indices + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool MeshInterface::RemapClient(udword nb_indices, const dTriIndex* permutation) const +{ + // Checkings + if(!nb_indices || !permutation) return false; + if(nb_indices!=mNbTris) return false; + +#ifdef OPC_USE_CALLBACKS + // We can't really do that using callbacks + return false; +#else + IndexedTriangle* Tmp = new IndexedTriangle[mNbTris]; + CHECKALLOC(Tmp); + + #ifdef OPC_USE_STRIDE + udword Stride = mTriStride; + #else + udword Stride = sizeof(IndexedTriangle); + #endif + + for(udword i=0;i= 0.0f; + } + }; + + struct VertexPointersEx + { + VertexPointers vp; + dTriIndex Index[3]; + }; + + typedef Point ConversionArea[3]; + +#ifdef OPC_USE_CALLBACKS + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to request vertices from the app. + * \param triangle_index [in] face index for which the system is requesting the vertices + * \param triangle [out] triangle's vertices (must be provided by the user) + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*RequestCallback) (udword triangle_index, VertexPointers& triangle, void* user_data); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to request vertex indices from the app. + * \param triangle_index [in] face index for which the system is requesting the vertices + * \param triangle [out] triangle's vertices with indices (must be provided by the user) + * \param user_data [in] user-defined data from SetExCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*RequestExCallback) (udword triangle_index, VertexPointersEx& triangle, void* user_data); +#endif + + class OPCODE_API MeshInterface + { + public: + // Constructor / Destructor + MeshInterface(); + ~MeshInterface(); + // Common settings + inline_ udword GetNbTriangles() const { return mNbTris; } + inline_ udword GetNbVertices() const { return mNbVerts; } + inline_ void SetNbTriangles(udword nb) { mNbTris = nb; } + inline_ void SetNbVertices(udword nb) { mNbVerts = nb; } + +#ifdef OPC_USE_CALLBACKS + // Callback settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \param user_data [in] user-defined data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetCallback(RequestCallback callback, void* user_data); + inline_ void* GetUserData() const { return mUserData; } + inline_ RequestCallback GetCallback() const { return mObjCallback; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \param user_data [in] user-defined data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetExCallback(RequestExCallback callback, void* user_data); + inline_ void* GetExUserData() const { return mExUserData; } + inline_ RequestExCallback GetExCallback() const { return mObjExCallback; } +#else + // Pointers settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object. + * \param tris [in] pointer to triangles + * \param verts [in] pointer to vertices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetPointers(const IndexedTriangle* tris, const Point* verts); + inline_ const IndexedTriangle* GetTris() const { return mTris; } + inline_ const Point* GetVerts() const { return mVerts; } + + #ifdef OPC_USE_STRIDE + // Strides settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Strides control + * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices. + * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(Point) bytes are used to get vertex position. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(Point)); + inline_ udword GetTriStride() const { return mTriStride; } + inline_ udword GetVertexStride() const { return mVertexStride; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Single/Double control + * \param value [in] Indicates if mesh data is provided as array of \c single values. If \c false, data is expected to contain \c double elements. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetSingle(bool value) + { + mFetchTriangle = (value ? &MeshInterface::FetchTriangleFromSingles : &MeshInterface::FetchTriangleFromDoubles); + mFetchExTriangle = (value ? &MeshInterface::FetchExTriangleFromSingles : &MeshInterface::FetchExTriangleFromDoubles); + } + + #else + inline_ bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(Point)) { return true; } + inline_ void SetSingle(bool value) {} + inline_ udword GetTriStride() const { return sizeof(IndexedTriangle); } + inline_ udword GetVertexStride() const { return sizeof(Point); } + #endif +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Fetches a triangle given a triangle index. + * \param vp [out] required triangle's vertex pointers + * \param index [in] triangle index + * \param vc [in,out] storage required for data conversion (pass local variable with same scope as \a vp, as \a vp may point to this memory on return) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void GetTriangle(VertexPointers& vp, udword index, ConversionArea vc) const + { +#ifdef OPC_USE_CALLBACKS + (mObjCallback)(index, vp, mUserData); +#else + #ifdef OPC_USE_STRIDE + // Since there was conditional statement "if (Single)" which was unpredictable for compiler + // and required both branches to be always generated what made inlining a questionable + // benefit, I consider it better to introduce a forced call + // but get rig of branching and dead code injection. + ((*this).*mFetchTriangle)(vp, index, vc); + #else + const Point* Verts = GetVerts(); + const IndexedTriangle* T = &mTris[index]; + vp.Vertex[0] = &Verts[T->mVRef[0]]; + vp.Vertex[1] = &Verts[T->mVRef[1]]; + vp.Vertex[2] = &Verts[T->mVRef[2]]; + #endif +#endif + } + + inline_ bool GetExTriangle(VertexPointersEx& vpe, udword index, ConversionArea vc) const + { +#ifdef OPC_USE_CALLBACKS + if (mObjExCallback) { (mObjExCallback)(index, vpe, mUserData); return true; } + else { (mObjCallback)(index, vpe.vp, mUserData); return false; } +#else + #ifdef OPC_USE_STRIDE + // Since there was conditional statement "if (Single)" which was unpredictable for compiler + // and required both branches to be always generated what made inlining a questionable + // benefit, I consider it better to introduce a forced call + // but get rig of branching and dead code injection. + ((*this).*mFetchExTriangle)(vpe, index, vc); + return true; + #else + const Point* Verts = GetVerts(); + const IndexedTriangle* T = &mTris[index]; + dTriIndex VertIndex0 = T->mVRef[0]; + vpe.Index[0] = VertIndex0; + vpe.vp.Vertex[0] = &Verts[VertIndex0]; + dTriIndex VertIndex1 = T->mVRef[1]; + vpe.Index[1] = VertIndex1; + vpe.vp.Vertex[1] = &Verts[VertIndex1]; + dTriIndex VertIndex2 = T->mVRef[2]; + vpe.Index[2] = VertIndex2; + vpe.vp.Vertex[2] = &Verts[VertIndex2]; + return true; + #endif +#endif + } + + private: +#ifndef OPC_USE_CALLBACKS + #ifdef OPC_USE_STRIDE + void FetchTriangleFromSingles(VertexPointers& vp, udword index, ConversionArea vc) const; + void FetchTriangleFromDoubles(VertexPointers& vp, udword index, ConversionArea vc) const; + void FetchExTriangleFromSingles(VertexPointersEx& vpe, udword index, ConversionArea vc) const; + void FetchExTriangleFromDoubles(VertexPointersEx& vpe, udword index, ConversionArea vc) const; + #endif +#endif + + public: + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Remaps client's mesh according to a permutation. + * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles) + * \param permutation [in] list of triangle indices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool RemapClient(udword nb_indices, const dTriIndex* permutation) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool IsValid() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + udword CheckTopology() const; + private: + + udword mNbTris; //!< Number of triangles in the input model + udword mNbVerts; //!< Number of vertices in the input model +#ifdef OPC_USE_CALLBACKS + // User callback + void* mUserData; //!< User-defined data sent to callback + RequestCallback mObjCallback; //!< Object callback + void* mExUserData; //!< User-defined data sent to ex-callback + RequestExCallback mObjExCallback; //!< Object ex-callback +#else + // User pointers + #ifdef OPC_USE_STRIDE + udword mTriStride; //!< Possible triangle stride in bytes [Opcode 1.3] + udword mVertexStride; //!< Possible vertex stride in bytes [Opcode 1.3] + typedef void (MeshInterface:: *TriangleFetchProc)(VertexPointers& vp, udword index, ConversionArea vc) const; + TriangleFetchProc mFetchTriangle; + typedef void (MeshInterface:: *ExTriangleFetchProc)(VertexPointersEx& vpe, udword index, ConversionArea vc) const; + ExTriangleFetchProc mFetchExTriangle; + #endif + const IndexedTriangle* mTris; //!< Array of indexed triangles + const Point* mVerts; //!< Array of vertices +#endif + }; + +#endif //__OPC_MESHINTERFACE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Model.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_Model.cpp new file mode 100644 index 0000000..418dd7e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Model.cpp @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for OPCODE models. + * \file OPC_Model.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * The main collision wrapper, for all trees. Supported trees are: + * - Normal trees (2*N-1 nodes, full size) + * - No-leaf trees (N-1 nodes, full size) + * - Quantized trees (2*N-1 nodes, half size) + * - Quantized no-leaf trees (N-1 nodes, half size) + * + * Usage: + * + * 1) Create a static mesh interface using callbacks or pointers. (see OPC_MeshInterface.cpp). + * Keep it around in your app, since a pointer to this interface is saved internally and + * used until you release the collision structures. + * + * 2) Build a Model using a creation structure: + * + * \code + * Model Sample; + * + * OPCODECREATE OPCC; + * OPCC.IMesh = ...; + * OPCC.Rules = ...; + * OPCC.NoLeaf = ...; + * OPCC.Quantized = ...; + * OPCC.KeepOriginal = ...; + * bool Status = Sample.Build(OPCC); + * \endcode + * + * 3) Create a tree collider and set it up: + * + * \code + * AABBTreeCollider TC; + * TC.SetFirstContact(...); + * TC.SetFullBoxBoxTest(...); + * TC.SetFullPrimBoxTest(...); + * TC.SetTemporalCoherence(...); + * \endcode + * + * 4) Perform a collision query + * + * \code + * // Setup cache + * static BVTCache ColCache; + * ColCache.Model0 = &Model0; + * ColCache.Model1 = &Model1; + * + * // Collision query + * bool IsOk = TC.Collide(ColCache, World0, World1); + * + * // Get collision status => if true, objects overlap + * BOOL Status = TC.GetContactStatus(); + * + * // Number of colliding pairs and list of pairs + * udword NbPairs = TC.GetNbPairs(); + * const Pair* p = TC.GetPairs() + * \endcode + * + * 5) Stats + * + * \code + * Model0.GetUsedBytes() = number of bytes used for this collision tree + * TC.GetNbBVBVTests() = number of BV-BV overlap tests performed during last query + * TC.GetNbPrimPrimTests() = number of Triangle-Triangle overlap tests performed during last query + * TC.GetNbBVPrimTests() = number of Triangle-BV overlap tests performed during last query + * \endcode + * + * \class Model + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Model::Model() +{ +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + mHull = null; +#endif // __MESHMERIZER_H__ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Model::~Model() +{ + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the model. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Model::Release() +{ + ReleaseBase(); +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + DELETESINGLE(mHull); +#endif // __MESHMERIZER_H__ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Model::Build(const OPCODECREATE& create) +{ + // 1) Checkings + if(!create.mIMesh || !create.mIMesh->IsValid()) return false; + + // For this model, we only support complete trees + if(create.mSettings.mLimit!=1) return SetIceError("OPCODE WARNING: supports complete trees only! Use mLimit = 1.\n", null); + + // Look for degenerate faces. + //udword NbDegenerate = create.mIMesh->CheckTopology(); + //if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); + // We continue nonetheless.... + + Release(); // Make sure previous tree has been discarded [Opcode 1.3, thanks Adam] + + // 1-1) Setup mesh interface automatically [Opcode 1.3] + SetMeshInterface(create.mIMesh); + + // Special case for 1-triangle meshes [Opcode 1.3] + udword NbTris = create.mIMesh->GetNbTriangles(); + if(NbTris==1) + { + // We don't need to actually create a tree here, since we'll only have a single triangle to deal with anyway. + // It's a waste to use a "model" for this but at least it will work. + mModelCode |= OPC_SINGLE_NODE; + return true; + } + + // 2) Build a generic AABB Tree. + mSource = new AABBTree; + CHECKALLOC(mSource); + + // 2-1) Setup a builder. Our primitives here are triangles from input mesh, + // so we use an AABBTreeOfTrianglesBuilder..... + { + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mSettings = create.mSettings; + TB.mNbPrimitives = NbTris; + if(!mSource->Build(&TB)) return false; + } + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) return false; + + // 3-2) Create optimized tree + if(!mTree->Build(mSource)) return false; + + // 3-3) Delete generic tree if needed + if(!create.mKeepOriginal) DELETESINGLE(mSource); + +#ifdef __MESHMERIZER_H__ + // 4) Convex hull + if(create.mCollisionHull) + { + // Create hull + mHull = new CollisionHull; + CHECKALLOC(mHull); + + CONVEXHULLCREATE CHC; + // ### doesn't work with strides + CHC.NbVerts = create.mIMesh->GetNbVertices(); + CHC.Vertices = create.mIMesh->GetVerts(); + CHC.UnifyNormals = true; + CHC.ReduceVertices = true; + CHC.WordFaces = false; + mHull->Compute(CHC); + } +#endif // __MESHMERIZER_H__ + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword Model::GetUsedBytes() const +{ + if(!mTree) return 0; + return mTree->GetUsedBytes(); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Model.h b/thirdparty/ode-0.16.5/OPCODE/OPC_Model.h new file mode 100644 index 0000000..98dee56 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Model.h @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for OPCODE models. + * \file OPC_Model.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_MODEL_H__ +#define __OPC_MODEL_H__ + + class OPCODE_API Model : public BaseModel + { + public: + // Constructor/Destructor + Model(); + virtual ~Model(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Build(const OPCODECREATE& create); + +#ifdef __MESHMERIZER_H__ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the collision hull. + * \return the collision hull if it exists + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const CollisionHull* GetHull() const { return mHull; } +#endif // __MESHMERIZER_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) udword GetUsedBytes() const; + + private: +#ifdef __MESHMERIZER_H__ + CollisionHull* mHull; //!< Possible convex hull +#endif // __MESHMERIZER_H__ + // Internal methods + void Release(); + }; + +#endif //__OPC_MODEL_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.cpp new file mode 100644 index 0000000..730c7cc --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.cpp @@ -0,0 +1,767 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an OBB collider. + * \file OPC_OBBCollider.cpp + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an OBB-vs-tree collider. + * + * \class OBBCollider + * \author Pierre Terdiman + * \version 1.3 + * \date January, 1st, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_BoxBoxOverlap.h" +#include "OPC_TriBoxOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(udword(prim_index)); + +//! OBB-triangle test +#define OBB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OBBCollider::OBBCollider() : mFullBoxBoxTest(true) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OBBCollider::~OBBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* OBBCollider::ValidateSettings() +{ + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; + + return VolumeCollider::ValidateSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision OBB in local space + * \param model [in] Opcode model to collide with + * \param worldb [in] OBB's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] a box cache + * \param box [in] obb in local space + * \param worldb [in] obb's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute obb in world space + mBoxExtents = box.mExtents; + + Matrix4x4 WorldB; + + if(worldb) + { + WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) ); + WorldB.SetTrans(box.mCenter * *worldb); + } + else + { + WorldB = box.mRot; + WorldB.SetTrans(box.mCenter); + } + + // Setup matrices + Matrix4x4 InvWorldB; + InvertPRMatrix(InvWorldB, WorldB); + + if(worldm) + { + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + Matrix4x4 WorldBtoM = WorldB * InvWorldM; + Matrix4x4 WorldMtoB = *worldm * InvWorldB; + + mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox); + mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel); + } + else + { + mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox); + mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel); + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + OBB_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence: + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the box (and set contact status if needed) + OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // ### rewrite this + OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel); + + // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): + if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat box so that coherence will work for subsequent frames + TestBox.mExtents *= cache.FatCoeff; + mBoxExtents *= cache.FatCoeff; + + // Update cache with query data (signature for cached faces) + cache.FatBox = TestBox; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + // Now we can precompute box-box data + + // Precompute absolute box-to-model rotation matrix + for(udword i=0;i<3;i++) + { + for(udword j=0;j<3;j++) + { + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]); + } + } + + // Precompute bounds for box-in-box test + mB0 = mBoxExtents - mTModelToBox; + mB1 = - mBoxExtents - mTModelToBox; + + // Precompute box-box data - Courtesy of Erwin de Vries + mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0]; + mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1]; + mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2]; + + mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0]; + mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0]; + mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0]; + mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1]; + mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1]; + mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1]; + mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2]; + mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2]; + mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2]; + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the OBB completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the OBB contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be) +{ + // I assume if all 8 box vertices are inside the OBB, so does the whole box. + // Sounds ok but maybe there's a better way? +/* +#define TEST_PT(a,b,c) \ + p.x=a; p.y=b; p.z=c; p+=bc; \ + f = p.x * mRModelToBox.m[0][0] + p.y * mRModelToBox.m[1][0] + p.z * mRModelToBox.m[2][0]; if(f>mB0.x || fmB0.y || fmB0.z || f NCx-NEx) return FALSE; + + float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1]; + float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z); + + if(mB0.y < NCy+NEy) return FALSE; + if(mB1.y > NCy-NEy) return FALSE; + + float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2]; + float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z); + + if(mB0.z < NCz+NEz) return FALSE; + if(mB1.z > NCz-NEz) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_OBB(center, extents) \ + if(OBBContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->IsLeaf()) + { + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::HybridOBBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::~HybridOBBCollider() +{ +} + +bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;i(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.h new file mode 100644 index 0000000..a050118 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_OBBCollider.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an OBB collider. + * \file OPC_OBBCollider.h + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_OBBCOLLIDER_H__ +#define __OPC_OBBCOLLIDER_H__ + + struct OPCODE_API OBBCache : VolumeCache + { + OBBCache() : FatCoeff(1.1f) + { + FatBox.mCenter.Zero(); + FatBox.mExtents.Zero(); + FatBox.mRot.Identity(); + } + + // Cached faces signature + OBB FatBox; //!< Box used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< extents multiplier used to create a fat box + }; + + class OPCODE_API OBBCollider : public VolumeCollider + { + public: + // Constructor / Destructor + OBBCollider(); + virtual ~OBBCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision OBB in local space + * \param model [in] Opcode model to collide with + * \param worldb [in] OBB's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: select between full box-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Precomputed data + Matrix3x3 mAR; //!< Absolute rotation matrix + Matrix3x3 mRModelToBox; //!< Rotation from model space to obb space + Matrix3x3 mRBoxToModel; //!< Rotation from obb space to model space + Point mTModelToBox; //!< Translation from model space to obb space + Point mTBoxToModel; //!< Translation from obb space to model space + + Point mBoxExtents; + Point mB0; //!< - mTModelToBox + mBoxExtents + Point mB1; //!< - mTModelToBox - mBoxExtents + + float mBBx1; + float mBBy1; + float mBBz1; + + float mBB_1; + float mBB_2; + float mBB_3; + float mBB_4; + float mBB_5; + float mBB_6; + float mBB_7; + float mBB_8; + float mBB_9; + + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + // Settings + bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL OBBContainsBox(const Point& bc, const Point& be); + inline_ BOOL BoxBoxOverlap(const Point& extents, const Point& center); + inline_ BOOL TriBoxOverlap(); + // Init methods + BOOL InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridOBBCollider : public OBBCollider + { + public: + // Constructor / Destructor + HybridOBBCollider(); + virtual ~HybridOBBCollider(); + + bool Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + +#endif // __OPC_OBBCOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.cpp new file mode 100644 index 0000000..057853e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.cpp @@ -0,0 +1,795 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for optimized trees. Implements 4 trees: + * - normal + * - no leaf + * - quantized + * - no leaf / quantized + * + * \file OPC_OptimizedTree.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A standard AABB tree. + * + * \class AABBCollisionTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A no-leaf AABB tree. + * + * \class AABBNoLeafTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized AABB tree. + * + * \class AABBQuantizedTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized no-leaf AABB tree. + * + * \class AABBQuantizedNoLeafTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +//! Compilation flag: +//! - true to fix quantized boxes (i.e. make sure they enclose the original ones) +//! - false to see the effects of quantization errors (faster, but wrong results in some cases) +static const bool gFixQuantized = true; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an implicit tree from a standard one. An implicit tree is a complete tree (2*N-1 nodes) whose negative + * box pointers and primitive pointers have been made implicit, hence packing 3 pointers in one. + * + * Layout for implicit trees: + * Node: + * - box + * - data (32-bits value) + * + * if data's LSB = 1 => remaining bits are a primitive pointer + * else remaining bits are a P-node pointer, and N = P + 1 + * + * \relates AABBCollisionNode + * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) +{ + // Current node from input tree is "current_node". Must be flattened into "linear[boxid]". + + // Store the AABB + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); + // Store remaining info + if(current_node->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(current_node->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = current_node->GetPrimitives()[0]; + // Setup box data as the primitive index, marked as leaf + linear[box_id].mData = (PrimitiveIndex<<1)|1; + } + else + { + // To make the negative one implicit, we must store P and N in successive order + udword PosID = current_id++; // Get a new id for positive child + udword NegID = current_id++; // Get a new id for negative child + // Setup box data as the forthcoming new P pointer + linear[box_id].mData = (size_t)&linear[PosID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mData&1)); + // Recurse with new IDs + _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos()); + _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a "no-leaf" tree from a standard one. This is a tree whose leaf nodes have been removed. + * + * Layout for no-leaf trees: + * + * Node: + * - box + * - P pointer => a node (LSB=0) or a primitive (LSB=1) + * - N pointer => a node (LSB=0) or a primitive (LSB=1) + * + * \relates AABBNoLeafNode + * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) +{ + const AABBTreeNode* P = current_node->GetPos(); + const AABBTreeNode* N = current_node->GetNeg(); + // Leaf nodes here?! + ASSERT(P); + ASSERT(N); + // Internal node => keep the box + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); + + if(P->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(P->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = P->GetPrimitives()[0]; + // Setup prev box data as the primitive index, marked as leaf + linear[box_id].mPosData = (PrimitiveIndex<<1)|1; + } + else + { + // Get a new id for positive child + udword PosID = current_id++; + // Setup box data + linear[box_id].mPosData = (size_t)&linear[PosID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mPosData&1)); + // Recurse + _BuildNoLeafTree(linear, PosID, current_id, P); + } + + if(N->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(N->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = N->GetPrimitives()[0]; + // Setup prev box data as the primitive index, marked as leaf + linear[box_id].mNegData = (PrimitiveIndex<<1)|1; + } + else + { + // Get a new id for negative child + udword NegID = current_id++; + // Setup box data + linear[box_id].mNegData = (size_t)&linear[NegID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mNegData&1)); + // Recurse + _BuildNoLeafTree(linear, NegID, current_id, N); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollisionTree::AABBCollisionTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollisionTree::~AABBCollisionTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + if(mNbNodes!=NbNodes) // Same number of nodes => keep moving + { + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + mNodes = new AABBCollisionNode[NbNodes]; + CHECKALLOC(mNodes); + } + + // Build the tree + udword CurID = 1; + _BuildCollisionTree(mNodes, 0, CurID, tree); + ASSERT(CurID==NbNodes); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Refit(const MeshInterface* /*mesh_interface*/) +{ + ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!"); + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->IsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBNoLeafTree::AABBNoLeafTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBNoLeafTree::~AABBNoLeafTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbExistingNodes = tree->GetNbNodes(); + if(NbExistingNodes!=NbTriangles*2-1) return false; + + udword NbNodes = NbTriangles-1; + // Get nodes + if(mNbNodes!=NbNodes) // Same number of nodes => keep moving + { + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + mNodes = new AABBNoLeafNode[NbNodes]; + CHECKALLOC(mNodes); + } + + // Build the tree + udword CurID = 1; + _BuildNoLeafTree(mNodes, 0, CurID, tree); + ASSERT(CurID==NbNodes); + + return true; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface) +{ + // Checkings + if(!mesh_interface) return false; + + // Bottom-up update + VertexPointers VP; + ConversionArea VC; + Point Min,Max; + Point Min_,Max_; + udword Index = mNbNodes; + while(Index--) + { + AABBNoLeafNode& Current = mNodes[Index]; + + if(Current.HasPosLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetPosPrimitive(), VC); + ComputeMinMax(Min, Max, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + + if(Current.HasNegLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetNegPrimitive(), VC); + ComputeMinMax(Min_, Max_, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + +// Quantization notes: +// - We could use the highest bits of mData to store some more quantized bits. Dequantization code +// would be slightly more complex, but number of overlap tests would be reduced (and anyhow those +// bits are currently wasted). Of course it's not possible if we move to 16 bits mData. +// - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion. +// - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some +// lazy-dequantization which may save some work in case of early exits). At the very least some +// muls could be saved by precomputing several more matrices. But maybe not worth the pain. +// - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has +// been scaled, for example. +// - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed +// number of quantization bits. Even better, could probably be best delta-encoded. + + +// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't. +// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal +// centers/extents in order to quantize them. The first node would only give a single center and +// a single extents. While extents would be the biggest, the center wouldn't. +#define FIND_MAX_VALUES \ + /* Get max values */ \ + Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ + Point EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ + const udword NbNodes = mNbNodes; \ + for(udword i=0;iCMax.x) CMax.x = cx; \ + float cy = fabsf(Nodes[i].mAABB.mCenter.y); if(cy>CMax.y) CMax.y = cy; \ + float cz = fabsf(Nodes[i].mAABB.mCenter.z); if(cz>CMax.z) CMax.z = cz; \ + float ex = fabsf(Nodes[i].mAABB.mExtents.x); if(ex>EMax.x) EMax.x = ex; \ + float ey = fabsf(Nodes[i].mAABB.mExtents.y); if(ey>EMax.y) EMax.y = ey; \ + float ez = fabsf(Nodes[i].mAABB.mExtents.z); if(ez>EMax.z) EMax.z = ez; \ + } + +#define INIT_QUANTIZATION \ + udword nbc=15; /* Keep one bit for sign */ \ + udword nbe=15; /* Keep one bit for fix */ \ + if(!gFixQuantized) nbe++; \ + \ + /* Compute quantization coeffs */ \ + Point CQuantCoeff, EQuantCoeff; \ + CQuantCoeff.x = CMax.x!=0.0f ? float((1<Min[j]) \ + { \ + mNodes[i].mAABB.mExtents[j]++; \ + /* Prevent wrapping */ \ + if(!mNodes[i].mAABB.mExtents[j]) \ + { \ + mNodes[i].mAABB.mExtents[j]=0xffff; \ + FixMe=false; \ + } \ + } \ + else FixMe=false; \ + }while(FixMe); \ + } \ + } + +#define REMAP_DATA(member, NodeType) \ + /* Fix data */ \ + Data = Nodes[i].member; \ + if(!(Data&1)) \ + { \ + /* Compute box number */ \ + size_t Nb = ((NodeType *)(Data) - (Nodes)); \ + Data = (size_t) &mNodes[Nb]; \ + } \ + /* ...remapped */ \ + mNodes[i].member = Data; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedTree::AABBQuantizedTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedTree::~AABBQuantizedTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + AABBCollisionNode* Nodes = new AABBCollisionNode[NbNodes]; + CHECKALLOC(Nodes); + + // Build the tree + udword CurID = 1; + _BuildCollisionTree(Nodes, 0, CurID, tree); + + // Quantize + { + mNodes = new AABBQuantizedNode[NbNodes]; + + if (mNodes != null) + { + // Get max values + FIND_MAX_VALUES + + // Quantization + INIT_QUANTIZATION + + // Quantize + size_t Data; + for(udword i=0;iIsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedNoLeafTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbExistingNodes = tree->GetNbNodes(); + if(NbExistingNodes!=NbTriangles*2-1) return false; + + // Get nodes + udword NbNodes = NbTriangles-1; + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + AABBNoLeafNode* Nodes = new AABBNoLeafNode[NbNodes]; + CHECKALLOC(Nodes); + + // Build the tree + udword CurID = 1; + _BuildNoLeafTree(Nodes, 0, CurID, tree); + ASSERT(CurID==NbNodes); + + // Quantize + { + mNodes = new AABBQuantizedNoLeafNode[NbNodes]; + + if (mNodes != null) + { + // Get max values + FIND_MAX_VALUES + + // Quantization + INIT_QUANTIZATION + + // Quantize + size_t Data; + for(udword i=0;iHasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.h b/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.h new file mode 100644 index 0000000..36aea07 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_OptimizedTree.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for optimized trees. + * \file OPC_OptimizedTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_OPTIMIZEDTREE_H__ +#define __OPC_OPTIMIZEDTREE_H__ + + //! Common interface for a node of an implicit tree + #define IMPLEMENT_IMPLICIT_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf test */ \ + inline_ BOOL IsLeaf() const { return (mData&1)!=0; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mData; } \ + inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \ + inline_ size_t GetPrimitive() const { return (mData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + size_t mData; + + //! Common interface for a node of a no-leaf tree + #define IMPLEMENT_NOLEAF_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mPosData(0), mNegData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf tests */ \ + inline_ BOOL HasPosLeaf() const { return (mPosData&1)!=0; } \ + inline_ BOOL HasNegLeaf() const { return (mNegData&1)!=0; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \ + inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \ + inline_ size_t GetPosPrimitive() const { return (mPosData>>1); } \ + inline_ size_t GetNegPrimitive() const { return (mNegData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + size_t mPosData; \ + size_t mNegData; + + class OPCODE_API AABBCollisionNode + { + IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB) + + inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; } + inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); } + inline_ udword GetRadius() const + { + udword* Bits = (udword*)&mAABB.mExtents.x; + udword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + + // NB: using the square-magnitude or the true volume of the box, seems to yield better results + // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size" + // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's + // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is + // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices + // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very + // good strategy. + }; + + class OPCODE_API AABBQuantizedNode + { + IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB) + + inline_ uword GetSize() const + { + const uword* Bits = mAABB.mExtents; + uword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all + // over the place.......! + }; + + class OPCODE_API AABBNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB) + }; + + class OPCODE_API AABBQuantizedNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB) + }; + + //! Common interface for a collision tree + #define IMPLEMENT_COLLISION_TREE(base_class, node) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + virtual ~base_class(); \ + /* Builds from a standard tree */ \ + override(AABBOptimizedTree) bool Build(AABBTree* tree); \ + /* Refits the tree */ \ + override(AABBOptimizedTree) bool Refit(const MeshInterface* mesh_interface); \ + /* Walks the tree */ \ + override(AABBOptimizedTree) bool Walk(GenericWalkingCallback callback, void* user_data) const; \ + /* Data access */ \ + inline_ const node* GetNodes() const { return mNodes; } \ + /* Stats */ \ + override(AABBOptimizedTree) udword GetUsedBytes() const { return mNbNodes*sizeof(node); } \ + private: \ + node* mNodes; + + typedef bool (*GenericWalkingCallback) (const void* current, void* user_data); + + class OPCODE_API AABBOptimizedTree + { + public: + // Constructor / Destructor + AABBOptimizedTree() : + mNbNodes (0) + {} + virtual ~AABBOptimizedTree() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(AABBTree* tree) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(const MeshInterface* mesh_interface) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Walk(GenericWalkingCallback callback, void* user_data) const = 0; + + // Data access + virtual udword GetUsedBytes() const = 0; + inline_ udword GetNbNodes() const { return mNbNodes; } + + protected: + udword mNbNodes; + }; + + class OPCODE_API AABBCollisionTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode) + }; + + class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode) + }; + + class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + + class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + +#endif // __OPC_OPTIMIZEDTREE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.cpp new file mode 100644 index 0000000..0f4c6c3 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.cpp @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#ifdef OPC_RAYHIT_CALLBACK + +/* + Possible RayCollider usages: + - boolean query (shadow feeler) + - closest hit + - all hits + - number of intersection (boolean) + +*/ + +bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts) +{ + struct Local + { + static void AllContacts(const CollisionFace& hit, void* user_data) + { + CollisionFaces* CF = (CollisionFaces*)user_data; + CF->AddFace(hit); + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::AllContacts); + collider.SetUserData(&contacts); + return true; +} + +bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact) +{ + struct Local + { + static void ClosestContact(const CollisionFace& hit, void* user_data) + { + CollisionFace* CF = (CollisionFace*)user_data; + if(hit.mDistancemDistance) *CF = hit; + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::ClosestContact); + collider.SetUserData(&closest_contact); + closest_contact.mDistance = MAX_FLOAT; + return true; +} + +bool Opcode::SetupShadowFeeler(RayCollider& collider) +{ + collider.SetFirstContact(true); + collider.SetHitCallback(null); + return true; +} + +bool Opcode::SetupInOutTest(RayCollider& collider) +{ + collider.SetFirstContact(false); + collider.SetHitCallback(null); + // Results with collider.GetNbIntersections() + return true; +} + +bool Opcode::Picking( +CollisionFace& picked_face, +const Ray& world_ray, const Model& model, const Matrix4x4* world, +float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data) +{ + struct Local + { + struct CullData + { + CollisionFace* Closest; + float MinLimit; + CullModeCallback Callback; + void* UserData; + Point ViewPoint; + const MeshInterface* IMesh; + }; + + // Called for each stabbed face + static void RenderCullingCallback(const CollisionFace& hit, void* user_data) + { + CullData* Data = (CullData*)user_data; + + // Discard face if we already have a closer hit + if(hit.mDistance>=Data->Closest->mDistance) return; + + // Discard face if hit point is smaller than min limit. This mainly happens when the face is in front + // of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an + // object that he may not even be able to see, which is very annoying. + if(hit.mDistance<=Data->MinLimit) return; + + // This is the index of currently stabbed triangle. + udword StabbedFaceIndex = hit.mFaceID; + + // We may keep it or not, depending on backface culling + bool KeepIt = true; + + // Catch *render* cull mode for this face + CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData); + + if(CM!=CULLMODE_NONE) // Don't even compute culling for double-sided triangles + { + // Compute backface culling for current face + + VertexPointers VP; + ConversionArea VC; + Data->IMesh->GetTriangle(VP, StabbedFaceIndex, VC); + if(VP.BackfaceCulling(Data->ViewPoint)) + { + if(CM==CULLMODE_CW) KeepIt = false; + } + else + { + if(CM==CULLMODE_CCW) KeepIt = false; + } + } + + if(KeepIt) *Data->Closest = hit; + } + }; + + RayCollider RC; + RC.SetMaxDist(max_dist); + RC.SetTemporalCoherence(false); + RC.SetCulling(false); // We need all faces since some of them can be double-sided + RC.SetFirstContact(false); + RC.SetHitCallback(Local::RenderCullingCallback); + + picked_face.mFaceID = INVALID_ID; + picked_face.mDistance = MAX_FLOAT; + picked_face.mU = 0.0f; + picked_face.mV = 0.0f; + + Local::CullData Data; + Data.Closest = &picked_face; + Data.MinLimit = min_dist; + Data.Callback = callback; + Data.UserData = user_data; + Data.ViewPoint = view_point; + Data.IMesh = model.GetMeshInterface(); + + if(world) + { + // Get matrices + Matrix4x4 InvWorld; + InvertPRMatrix(InvWorld, *world); + + // Compute camera position in mesh space + Data.ViewPoint *= InvWorld; + } + + RC.SetUserData(&Data); + if(RC.Collide(world_ray, model, world)) + { + return picked_face.mFaceID!=INVALID_ID; + } + return false; +} + +#endif diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.h b/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.h new file mode 100644 index 0000000..d22fa38 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Picking.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_PICKING_H__ +#define __OPC_PICKING_H__ + +#ifdef OPC_RAYHIT_CALLBACK + + enum CullMode + { + CULLMODE_NONE = 0, + CULLMODE_CW = 1, + CULLMODE_CCW = 2 + }; + + typedef CullMode (*CullModeCallback)(udword triangle_index, void* user_data); + + OPCODE_API bool SetupAllHits (RayCollider& collider, CollisionFaces& contacts); + OPCODE_API bool SetupClosestHit (RayCollider& collider, CollisionFace& closest_contact); + OPCODE_API bool SetupShadowFeeler (RayCollider& collider); + OPCODE_API bool SetupInOutTest (RayCollider& collider); + + OPCODE_API bool Picking( + CollisionFace& picked_face, + const Ray& world_ray, const Model& model, const Matrix4x4* world, + float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data); +#endif + +#endif //__OPC_PICKING_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_PlanesAABBOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_PlanesAABBOverlap.h new file mode 100644 index 0000000..5d7576e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_PlanesAABBOverlap.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Planes-AABB overlap test. + * - original code by Ville Miettinen, from Umbra/dPVS (released on the GD-Algorithms mailing list) + * - almost used "as-is", I even left the comments (hence the frustum-related notes) + * + * \param center [in] box center + * \param extents [in] box extents + * \param out_clip_mask [out] bitmask for active planes + * \param in_clip_mask [in] bitmask for active planes + * \return TRUE if boxes overlap planes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL PlanesCollider::PlanesAABBOverlap(const Point& center, const Point& extents, udword& out_clip_mask, udword in_clip_mask) +{ + // Stats + mNbVolumeBVTests++; + + const Plane* p = mPlanes; + + // Evaluate through all active frustum planes. We determine the relation + // between the AABB and a plane by using the concept of "near" and "far" + // vertices originally described by Zhang (and later by Mller). Our + // variant here uses 3 fabs ops, 6 muls, 7 adds and two floating point + // comparisons per plane. The routine early-exits if the AABB is found + // to be outside any of the planes. The loop also constructs a new output + // clip mask. Most FPUs have a native single-cycle fabsf() operation. + + udword Mask = 1; // current mask index (1,2,4,8,..) + udword TmpOutClipMask = 0; // initialize output clip mask into empty. + + while(Mask<=in_clip_mask) // keep looping while we have active planes left... + { + if(in_clip_mask & Mask) // if clip plane is active, process it.. + { + float NP = extents.x*fabsf(p->n.x) + extents.y*fabsf(p->n.y) + extents.z*fabsf(p->n.z); // ### fabsf could be precomputed + float MP = center.x*p->n.x + center.y*p->n.y + center.z*p->n.z + p->d; + + if(NP < MP) // near vertex behind the clip plane... + return FALSE; // .. so there is no intersection.. + if((-NP) < MP) // near and far vertices on different sides of plane.. + TmpOutClipMask |= Mask; // .. so update the clip mask... + } + Mask+=Mask; // mk = (1<Add(udword(prim_index)); + +//! Planes-triangle test +#define PLANES_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + mIMesh->GetTriangle(mVP, prim_index, mVC); \ + /* Perform triangle-box overlap test */ \ + if(PlanesTriOverlap(clip_mask)) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::PlanesCollider() : + mNbPlanes (0), + mPlanes (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::~PlanesCollider() +{ + DELETEARRAY(mPlanes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* PlanesCollider::ValidateSettings() +{ + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; + + return VolumeCollider::ValidateSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes in world space + * \param nb_planes [in] number of planes + * \param model [in] Opcode model to collide with + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const Model& model, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + udword PlaneMask = (1<(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - compute planes in model space + * - check temporal coherence + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes + * \param nb_planes [in] number of planes + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL PlanesCollider::InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute planes in model space + if(nb_planes>mNbPlanes) + { + DELETEARRAY(mPlanes); + mPlanes = new Plane[nb_planes]; + } + mNbPlanes = nb_planes; + + if(worldm) + { + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + +// for(udword i=0;iHasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the planes (and set contact status if needed) + udword clip_mask = (1< check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the planes (and set contact status if needed) + udword clip_mask = (1< we'll have to perform a normal query + } + else mTouchedPrimitives->Reset(); + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +#define TEST_CLIP_MASK \ + /* If the box is completely included, so are its children. We don't need to do extra tests, we */ \ + /* can immediately output a list of visible children. Those ones won't need to be clipped. */ \ + if(!OutClipMask) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::HybridPlanesCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::~HybridPlanesCollider() +{ +} + +bool HybridPlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const HybridModel& model, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + udword clip_mask = (1<(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + udword clip_mask = (1<Distance(*mVP.Vertex[0]); + float d1 = p->Distance(*mVP.Vertex[1]); + float d2 = p->Distance(*mVP.Vertex[2]); + if(d0>0.0f && d1>0.0f && d2>0.0f) return FALSE; +// if(!(IR(d0)&SIGN_BITMASK) && !(IR(d1)&SIGN_BITMASK) && !(IR(d2)&SIGN_BITMASK)) return FALSE; + } + Mask+=Mask; + p++; + } +/* + for(udword i=0;i<6;i++) + { + float d0 = p[i].Distance(mLeafVerts[0]); + float d1 = p[i].Distance(mLeafVerts[1]); + float d2 = p[i].Distance(mLeafVerts[2]); + if(d0>0.0f && d1>0.0f && d2>0.0f) return false; + } +*/ + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_RayAABBOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_RayAABBOverlap.h new file mode 100644 index 0000000..a8162bf --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_RayAABBOverlap.h @@ -0,0 +1,63 @@ +// Opcode 1.1: ray-AABB overlap tests based on Woo's code +// Opcode 1.2: ray-AABB overlap tests based on the separating axis theorem +// +// The point of intersection is not computed anymore. The distance to impact is not needed anymore +// since we now have two different queries for segments or rays. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a segment-AABB overlap test using the separating axis theorem. Segment is cached within the class. + * \param center [in] AABB center + * \param extents [in] AABB extents + * \return true on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::SegmentAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbRayBVTests++; + + float Dx = mData2.x - center.x; if(fabsf(Dx) > extents.x + mFDir.x) return FALSE; + float Dy = mData2.y - center.y; if(fabsf(Dy) > extents.y + mFDir.y) return FALSE; + float Dz = mData2.z - center.z; if(fabsf(Dz) > extents.z + mFDir.z) return FALSE; + + float f; + f = mData.y * Dz - mData.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE; + f = mData.z * Dx - mData.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE; + f = mData.x * Dy - mData.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE; + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-AABB overlap test using the separating axis theorem. Ray is cached within the class. + * \param center [in] AABB center + * \param extents [in] AABB extents + * \return true on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::RayAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbRayBVTests++; + +// float Dx = mOrigin.x - center.x; if(fabsf(Dx) > extents.x && Dx*mDir.x>=0.0f) return FALSE; +// float Dy = mOrigin.y - center.y; if(fabsf(Dy) > extents.y && Dy*mDir.y>=0.0f) return FALSE; +// float Dz = mOrigin.z - center.z; if(fabsf(Dz) > extents.z && Dz*mDir.z>=0.0f) return FALSE; + + float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && Dx*mDir.x>=0.0f) return FALSE; + float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && Dy*mDir.y>=0.0f) return FALSE; + float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && Dz*mDir.z>=0.0f) return FALSE; + +// float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && ((SIR(Dx)-1)^SIR(mDir.x))>=0.0f) return FALSE; +// float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && ((SIR(Dy)-1)^SIR(mDir.y))>=0.0f) return FALSE; +// float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && ((SIR(Dz)-1)^SIR(mDir.z))>=0.0f) return FALSE; + + float f; + f = mDir.y * Dz - mDir.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE; + f = mDir.z * Dx - mDir.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE; + f = mDir.x * Dy - mDir.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE; + + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.cpp new file mode 100644 index 0000000..6a81857 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.cpp @@ -0,0 +1,764 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a ray-vs-tree collider. + * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision. + * + * HIGHER DISTANCE BOUND: + * + * If P0 and P1 are two 3D points, let's define: + * - d = distance between P0 and P1 + * - Origin = P0 + * - Direction = (P1 - P0) / d = normalized direction vector + * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction + * - t = 0 --> P = P0 + * - t = d --> P = P1 + * + * Then we can define a general "ray" as: + * + * struct Ray + * { + * Point Origin; + * Point Direction; + * }; + * + * But it actually maps three different things: + * - a segment, when 0 <= t <= d + * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d + * - a line, when -infinity < t < +infinity + * + * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity. + * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin. + * + * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist(). + * + * Query |segment |half-line |line + * --------|-------------------|---------------|---------------- + * Usages |-shadow feelers |-raytracing |- + * |-sweep tests |-in/out tests | + * + * FIRST CONTACT: + * + * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact(). + * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where + * you want to know whether the path to the light is free or not (a boolean answer is enough). + * - In "all contacts" mode we return all faces hit by the ray. + * + * TEMPORAL COHERENCE: + * + * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence(). + * - It currently only works in "first contact" mode. + * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries + * start by colliding the ray against the cached triangle. If they still collide, we return immediately. + * + * CLOSEST HIT: + * + * - You can enable or disable "closest hit" with RayCollider::SetClosestHit(). + * - It currently only works in "all contacts" mode. + * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported. + * + * BACKFACE CULLING: + * + * - You can enable or disable backface culling with RayCollider::SetCulling(). + * - If culling is enabled, ray will not hit back faces (only front faces). + * + * + * + * \class RayCollider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * This class describes a face hit by a ray or segment. + * This is a particular class dedicated to stabbing queries. + * + * \class CollisionFace + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * This class is a dedicated collection of CollisionFace. + * + * \class CollisionFaces + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_RayAABBOverlap.h" +#include "OPC_RayTriOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + mNbIntersections++; \ + /* Set contact status */ \ + mFlags |= flag; \ + /* In any case the contact has been found and recorded in mStabbedFace */ \ + mStabbedFace.mFaceID = prim_index; + +#ifdef OPC_RAYHIT_CALLBACK + + #define HANDLE_CONTACT(prim_index, flag) \ + SET_CONTACT(prim_index, flag) \ + \ + if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData); + + #define UPDATE_CACHE \ + if(cache && GetContactStatus()) \ + { \ + *cache = mStabbedFace.mFaceID; \ + } +#else + + #define HANDLE_CONTACT(prim_index, flag) \ + SET_CONTACT(prim_index, flag) \ + \ + /* Now we can also record it in mStabbedFaces if available */ \ + if(mStabbedFaces) \ + { \ + /* If we want all faces or if that's the first one we hit */ \ + if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \ + { \ + mStabbedFaces->AddFace(mStabbedFace); \ + } \ + else \ + { \ + /* We only keep closest hit */ \ + CollisionFace* Current = const_cast(mStabbedFaces->GetFaces()); \ + if(Current && mStabbedFace.mDistancemDistance) \ + { \ + *Current = mStabbedFace; \ + } \ + } \ + } + + #define UPDATE_CACHE \ + if(cache && GetContactStatus() && mStabbedFaces) \ + { \ + const CollisionFace* Current = mStabbedFaces->GetFaces(); \ + if(Current) *cache = Current->mFaceID; \ + else *cache = INVALID_ID; \ + } +#endif + +#define SEGMENT_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + /* Intersection point is valid if dist < segment's length */ \ + /* We know dist>0 so we can use integers */ \ + if(IR(mStabbedFace.mDistance)GetTriangle(VP, prim_index, VC); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + HANDLE_CONTACT(prim_index, flag) \ + } + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RayCollider::RayCollider() : +#ifdef OPC_RAYHIT_CALLBACK + mHitCallback (null), + mUserData (0), +#else + mStabbedFaces (null), + mClosestHit (false), +#endif + mNbRayBVTests (0), + mNbRayPrimTests (0), + mNbIntersections (0), + mMaxDist (MAX_FLOAT), + mCulling (true) + +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RayCollider::~RayCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* RayCollider::ValidateSettings() +{ + if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; +#ifndef OPC_RAYHIT_CALLBACK + if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!"; + if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!"; +#endif + if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)"; + return null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param world_ray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix, or null + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(world_ray, world, cache)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + } + + // Update cache if needed + UPDATE_CACHE; + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a stabbing query : + * - reset stats & contact status + * - compute ray in local space + * - check temporal coherence + * + * \param world_ray [in] stabbing ray in world space + * \param world [in] object's world matrix, or null + * \param face_id [in] index of previously stabbed triangle + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id) +{ + // Reset stats & contact status + Collider::InitQuery(); + mNbRayBVTests = 0; + mNbRayPrimTests = 0; + mNbIntersections = 0; +#ifndef OPC_RAYHIT_CALLBACK + if(mStabbedFaces) mStabbedFaces->Reset(); +#endif + + // Compute ray in local space + // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests) + if(world) + { + Matrix3x3 InvWorld = *world; + mDir = InvWorld * world_ray.mDir; + + Matrix4x4 World; + InvertPRMatrix(World, *world); + mOrigin = world_ray.mOrig * World; + } + else + { + mDir = world_ray.mDir; + mOrigin = world_ray.mOrig; + } + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + if(!SkipPrimitiveTests()) + { + // Perform overlap test between the unique triangle and the ray (and set contact status if needed) + SEGMENT_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // Check temporal coherence : + + // Test previously colliding primitives first + if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID) + { +#ifdef OLD_CODE +#ifndef OPC_RAYHIT_CALLBACK + if(!mClosestHit) +#endif + { + // Request vertices from the app + VertexPointers VP; + ConversionArea VC; + mIMesh->GetTriangle(VP, *face_id, VC); + // Perform ray-cached tri overlap test + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Intersection point is valid if: + // - distance is positive (else it can just be a face behind the orig point) + // - distance is smaller than a given max distance (useful for shadow feelers) +// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistanceAddFace(mStabbedFace); +#endif + return TRUE; + } + } + } +#else + // New code + // We handle both Segment/ray queries with the same segment code, and a possible infinite limit + SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; +#endif + } + + // Precompute data (moved after temporal coherence since only needed for ray-AABB) + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + // For Segment-AABB overlap + mData = 0.5f * mDir * mMaxDist; + mData2 = mOrigin + mData; + + // Precompute mFDir; + mFDir.x = fabsf(mData.x); + mFDir.y = fabsf(mData.y); + mFDir.z = fabsf(mData.z); + } + else + { + // For Ray-AABB overlap +// udword x = SIR(mDir.x)-1; +// udword y = SIR(mDir.y)-1; +// udword z = SIR(mDir.z)-1; +// mData.x = FR(x); +// mData.y = FR(y); +// mData.z = FR(z); + + // Precompute mFDir; + mFDir.x = fabsf(mDir.x); + mFDir.y = fabsf(mDir.y); + mFDir.z = fabsf(mDir.z); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for vanilla AABB trees. + * \param world_ray [in] stabbing ray in world space + * \param tree [in] AABB tree + * \param box_indices [out] indices of stabbed boxes + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices) +{ + // ### bad design here + + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + // Basically this is only called to initialize precomputed data + if(InitQuery(world_ray)) return true; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices); + else _RayStab(tree, box_indices); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBCollisionNode* node) +{ + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->IsLeaf()) + { + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + _SegmentStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + _SegmentStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBNoLeafNode* node) +{ + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->HasPosLeaf()) + { + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->HasPosLeaf()) + { + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for vanilla AABB trees. + * \param node [in] current collision node + * \param box_indices [out] indices of stabbed boxes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBTreeNode* node, Container& box_indices) +{ + // Test the box against the segment + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _SegmentStab(node->GetPos(), box_indices); + _SegmentStab(node->GetNeg(), box_indices); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBCollisionNode* node) +{ + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->IsLeaf()) + { + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _RayStab(node->GetPos()); + + if(ContactFound()) return; + + _RayStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _RayStab(node->GetPos()); + + if(ContactFound()) return; + + _RayStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBNoLeafNode* node) +{ + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->HasPosLeaf()) + { + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->HasPosLeaf()) + { + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for vanilla AABB trees. + * \param node [in] current collision node + * \param box_indices [out] indices of stabbed boxes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBTreeNode* node, Container& box_indices) +{ + // Test the box against the ray + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + mFlags |= OPC_CONTACT; + box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _RayStab(node->GetPos(), box_indices); + _RayStab(node->GetNeg(), box_indices); + } +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.h new file mode 100644 index 0000000..4a8ab7e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_RayCollider.h @@ -0,0 +1,224 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_RAYCOLLIDER_H__ +#define __OPC_RAYCOLLIDER_H__ + + class OPCODE_API CollisionFace + { + public: + //! Constructor + inline_ CollisionFace() {} + //! Destructor + inline_ ~CollisionFace() {} + + udword mFaceID; //!< Index of touched face + float mDistance; //!< Distance from collider to hitpoint + float mU, mV; //!< Impact barycentric coordinates + }; + + class OPCODE_API CollisionFaces : public Container + { + public: + //! Constructor + CollisionFaces() {} + //! Destructor + ~CollisionFaces() {} + + inline_ udword GetNbFaces() const { return GetNbEntries()>>2; } + inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); } + + inline_ void Reset() { Container::Reset(); } + + inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); } + }; + +#ifdef OPC_RAYHIT_CALLBACK + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to record a hit. + * \param hit [in] current hit + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*HitCallback) (const CollisionFace& hit, void* user_data); +#endif + + class OPCODE_API RayCollider : public Collider + { + public: + // Constructor / Destructor + RayCollider(); + virtual ~RayCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param world_ray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix, or null + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world=null, udword* cache=null); + // + bool Collide(const Ray& world_ray, const AABBTree* tree, Container& box_indices); + // Settings + +#ifndef OPC_RAYHIT_CALLBACK + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: enable or disable "closest hit" mode. + * \param flag [in] true to report closest hit only + * \see SetCulling(bool flag) + * \see SetMaxDist(float max_dist) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetClosestHit(bool flag) { mClosestHit = flag; } +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: enable or disable backface culling. + * \param flag [in] true to enable backface culling + * \see SetClosestHit(bool flag) + * \see SetMaxDist(float max_dist) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetCulling(bool flag) { mCulling = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the higher distance bound. + * \param max_dist [in] higher distance bound. Default = maximal value, for ray queries (else segment) + * \see SetClosestHit(bool flag) + * \see SetCulling(bool flag) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMaxDist(float max_dist=MAX_FLOAT) { mMaxDist = max_dist; } + +#ifdef OPC_RAYHIT_CALLBACK + inline_ void SetHitCallback(HitCallback cb) { mHitCallback = cb; } + inline_ void SetUserData(void* user_data) { mUserData = user_data; } +#else + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the destination array for stabbed faces. + * \param cf [in] destination array, filled during queries + * \see SetClosestHit(bool flag) + * \see SetCulling(bool flag) + * \see SetMaxDist(float max_dist) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; } +#endif + // Stats + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-BV overlap tests after a collision query. + * \see GetNbRayPrimTests() + * \see GetNbIntersections() + * \return the number of Ray-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbRayBVTests() const { return mNbRayBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-Triangle overlap tests after a collision query. + * \see GetNbRayBVTests() + * \see GetNbIntersections() + * \return the number of Ray-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbRayPrimTests() const { return mNbRayPrimTests; } + + // In-out test + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of intersection found after a collision query. Can be used for in/out tests. + * \see GetNbRayBVTests() + * \see GetNbRayPrimTests() + * \return the number of valid intersections during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbIntersections() const { return mNbIntersections; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Ray in local space + Point mOrigin; //!< Ray origin + Point mDir; //!< Ray direction (normalized) + Point mFDir; //!< fabsf(mDir) + Point mData, mData2; + // Stabbed faces + CollisionFace mStabbedFace; //!< Current stabbed face +#ifdef OPC_RAYHIT_CALLBACK + HitCallback mHitCallback; //!< Callback used to record a hit + void* mUserData; //!< User-defined data +#else + CollisionFaces* mStabbedFaces; //!< List of stabbed faces + bool mClosestHit; //!< Report closest hit only +#endif + // Stats + udword mNbRayBVTests; //!< Number of Ray-BV tests + udword mNbRayPrimTests; //!< Number of Ray-Primitive tests + // In-out test + udword mNbIntersections; //!< Number of valid intersections + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Settings + float mMaxDist; //!< Valid segment on the ray + + bool mCulling; //!< Stab culled faces or not + // Internal methods + void _SegmentStab(const AABBCollisionNode* node); + void _SegmentStab(const AABBNoLeafNode* node); + void _SegmentStab(const AABBQuantizedNode* node); + void _SegmentStab(const AABBQuantizedNoLeafNode* node); + void _SegmentStab(const AABBTreeNode* node, Container& box_indices); + void _RayStab(const AABBCollisionNode* node); + void _RayStab(const AABBNoLeafNode* node); + void _RayStab(const AABBQuantizedNode* node); + void _RayStab(const AABBQuantizedNoLeafNode* node); + void _RayStab(const AABBTreeNode* node, Container& box_indices); + // Overlap tests + inline_ BOOL RayAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL SegmentAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* face_id=null); + }; + +#endif // __OPC_RAYCOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_RayTriOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_RayTriOverlap.h new file mode 100644 index 0000000..04110a1 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_RayTriOverlap.h @@ -0,0 +1,89 @@ +#define LOCAL_EPSILON 0.000001f + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-triangle intersection test. + * Original code from Tomas Mller's "Fast Minimum Storage Ray-Triangle Intersection". + * It's been optimized a bit with integer code, and modified to return a non-intersection if distance from + * ray origin to triangle is negative. + * + * \param vert0 [in] triangle vertex + * \param vert1 [in] triangle vertex + * \param vert2 [in] triangle vertex + * \return true on overlap. mStabbedFace is filled with relevant info. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) +{ + // Stats + mNbRayPrimTests++; + + // Find vectors for two edges sharing vert0 + Point edge1 = vert1 - vert0; + Point edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + Point pvec = mDir^edge2; + + // If determinant is near zero, ray lies in plane of triangle + float det = edge1|pvec; + + if(mCulling) + { + if(det <= LOCAL_EPSILON * FCMin2(edge1.SquareMagnitude(), edge2.SquareMagnitude())) return FALSE; + // From here, det is > 0. So we can use integer cmp. + + // Calculate distance from vert0 to ray origin + Point tvec = mOrigin - vert0; + + // Calculate U parameter and test bounds + mStabbedFace.mU = tvec|pvec; +// if(IR(u)&0x80000000 || u>det) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IR(det)) return FALSE; + + // Prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + mStabbedFace.mV = mDir|qvec; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE; + + // Calculate t, scale parameters, ray intersects triangle + mStabbedFace.mDistance = edge2|qvec; + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; + // Else go on + float OneOverDet = 1.0f / det; + mStabbedFace.mDistance *= OneOverDet; + mStabbedFace.mU *= OneOverDet; + mStabbedFace.mV *= OneOverDet; + } + else + { + // the non-culling branch + if(FastFabs(det) <= LOCAL_EPSILON * FCMin2(edge1.SquareMagnitude(), edge2.SquareMagnitude())) return FALSE; + float OneOverDet = 1.0f / det; + + // Calculate distance from vert0 to ray origin + Point tvec = mOrigin - vert0; + + // Calculate U parameter and test bounds + mStabbedFace.mU = (tvec|pvec) * OneOverDet; +// if(IR(u)&0x80000000 || u>1.0f) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE; + + // prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + mStabbedFace.mV = (mDir|qvec) * OneOverDet; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE; + + // Calculate t, ray intersects triangle + mStabbedFace.mDistance = (edge2|qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; + } + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_Settings.h b/thirdparty/ode-0.16.5/OPCODE/OPC_Settings.h new file mode 100644 index 0000000..7509888 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_Settings.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains compilation flags. + * \file OPC_Settings.h + * \author Pierre Terdiman + * \date May, 12, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SETTINGS_H__ +#define __OPC_SETTINGS_H__ + + //! Use CPU comparisons (comment that line to use standard FPU compares) + //#define OPC_CPU_COMPARE + + //! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++) + #define OPC_USE_FCOMI + + //! Use epsilon value in tri-tri overlap test + #define OPC_TRITRI_EPSILON_TEST + + //! Use tree-coherence or not [not implemented yet] +// #define OPC_USE_TREE_COHERENCE + + //! Use callbacks or direct pointers. Using callbacks might be a bit slower (but probably not much) +// #define OPC_USE_CALLBACKS + + //! Support triangle and vertex strides or not. Using strides might be a bit slower (but probably not much) + #define OPC_USE_STRIDE + + //! Discard negative pointer in vanilla trees + #define OPC_NO_NEG_VANILLA_TREE + + //! Use a callback in the ray collider + //#define OPC_RAYHIT_CALLBACK + + // NB: no compilation flag to enable/disable stats since they're actually needed in the box/box overlap test + +#endif //__OPC_SETTINGS_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_SphereAABBOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereAABBOverlap.h new file mode 100644 index 0000000..2278bc0 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereAABBOverlap.h @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Sphere-AABB overlap test, based on Jim Arvo's code. + * \param center [in] box center + * \param extents [in] box extents + * \return TRUE on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL SphereCollider::SphereAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbVolumeBVTests++; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box +#ifdef OLDIES + for(udword i=0;i<3;i++) + { + float tmp = mCenter[i] - center[i]; + float s = tmp + extents[i]; + + if(s<0.0f) d += s*s; + else + { + s = tmp - extents[i]; + if(s>0.0f) d += s*s; + } + } +#endif + +//#ifdef NEW_TEST + +// float tmp = mCenter.x - center.x; +// float s = tmp + extents.x; + + float tmp,s; + + tmp = mCenter.x - center.x; + s = tmp + extents.x; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.x; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } + + tmp = mCenter.y - center.y; + s = tmp + extents.y; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.y; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } + + tmp = mCenter.z - center.z; + s = tmp + extents.z; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.z; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } +//#endif + +#ifdef OLDIES +// Point Min = center - extents; +// Point Max = center + extents; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(udword i=0;i<3;i++) + { +float Min = center[i] - extents[i]; + +// if(mCenter[i]Max[i]) + if(mCenter[i]>Max) + { + float s = mCenter[i] - Max; + d += s*s; + } + } + } +#endif + return d <= mRadius2; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.cpp new file mode 100644 index 0000000..e2406ec --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.cpp @@ -0,0 +1,739 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a sphere-vs-tree collider. + * This class performs a collision test between a sphere and an AABB tree. You can use this to do a standard player vs world collision, + * in a Nettle/Telemachos way. It doesn't suffer from all reported bugs in those two classic codes - the "new" one by Paul Nettle is a + * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of + * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever). + * + * \class SphereCollider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +#include "OPC_SphereAABBOverlap.h" +#include "OPC_SphereTriOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(udword(prim_index)); + +//! Sphere-triangle overlap test +#define SPHERE_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; mIMesh->GetTriangle(VP, prim_index, VC); \ + \ + /* Perform sphere-tri overlap test */ \ + if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SphereCollider::SphereCollider() +{ + mCenter.Zero(); + mRadius2 = 0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SphereCollider::~SphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + // Loop through all triangles + for(udword i=0;i(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] a sphere cache + * \param sphere [in] sphere in local space + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute sphere in model space: + // - Precompute R^2 + mRadius2 = sphere.mRadius * sphere.mRadius; + // - Compute center position + mCenter = sphere.mCenter; + // -> to world space + if(worlds) mCenter *= *worlds; + // -> to model space + if(worldm) + { + // Invert model matrix + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + mCenter *= InvWorldM; + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the sphere (and set contact status if needed) + SPHERE_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the sphere (and set contact status if needed) + SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious): + float r = sqrtf(cache.FatRadius2) - sphere.mRadius; + if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat sphere so that coherence will work for subsequent frames + mRadius2 *= cache.FatCoeff; +// mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff); + + // Update cache with query data (signature for cached faces) + cache.Center = mCenter; + cache.FatRadius2 = mRadius2; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, sphere)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the sphere completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the sphere contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be) +{ + // I assume if all 8 box vertices are inside the sphere, so does the whole box. + // Sounds ok but maybe there's a better way? + Point p; + p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z+be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z-be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_SPHERE(center, extents) \ + if(SphereContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBTreeNode* node) +{ + // Perform Sphere-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!SphereAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || SphereContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::HybridSphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::~HybridSphereCollider() +{ +} + +bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;i(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = static_cast(model.GetTree()); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = static_cast(model.GetTree()); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.h new file mode 100644 index 0000000..ac6495e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereCollider.h @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SPHERECOLLIDER_H__ +#define __OPC_SPHERECOLLIDER_H__ + + struct OPCODE_API SphereCache : VolumeCache + { + SphereCache() : Center(0.0f,0.0f,0.0f), FatRadius2(0.0f), FatCoeff(1.1f) {} + ~SphereCache() {} + + // Cached faces signature + Point Center; //!< Sphere used when performing the query resulting in cached faces + float FatRadius2; //!< Sphere used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere + }; + + class OPCODE_API SphereCollider : public VolumeCollider + { + public: + // Constructor / Destructor + SphereCollider(); + virtual ~SphereCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + + // + bool Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree); + protected: + // Sphere in model space + Point mCenter; //!< Sphere center + float mRadius2; //!< Sphere radius squared + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL SphereContainsBox(const Point& bc, const Point& be); + inline_ BOOL SphereAABBOverlap(const Point& center, const Point& extents); + BOOL SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridSphereCollider : public SphereCollider + { + public: + // Constructor / Destructor + HybridSphereCollider(); + virtual ~HybridSphereCollider(); + + bool Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + protected: + Container mTouchedBoxes; + }; + +#endif // __OPC_SPHERECOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_SphereTriOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereTriOverlap.h new file mode 100644 index 0000000..77e59f3 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_SphereTriOverlap.h @@ -0,0 +1,187 @@ + +// This is collision detection. If you do another distance test for collision *response*, +// if might be useful to simply *skip* the test below completely, and report a collision. +// - if sphere-triangle overlap, result is ok +// - if they don't, we'll discard them during collision response with a similar test anyway +// Overall this approach should run faster. + +// Original code by David Eberly in Magic. +BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) +{ + // Stats + mNbVolumePrimTests++; + + // Early exit if one of the vertices is inside the sphere + Point kDiff = vert2 - mCenter; + float fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert1 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert0 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + // Else do the full distance test + Point TriEdge0 = vert1 - vert0; + Point TriEdge1 = vert2 - vert0; + +//Point kDiff = vert0 - mCenter; + float fA00 = TriEdge0.SquareMagnitude(); + float fA01 = TriEdge0 | TriEdge1; + float fA11 = TriEdge1.SquareMagnitude(); + float fB0 = kDiff | TriEdge0; + float fB1 = kDiff | TriEdge1; +//float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11 - fA01*fA01); + float u = fA01*fB1-fA11*fB0; + float v = fA01*fB0-fA00*fB1; + float SqrDist; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { +// v = 0.0f; + if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + else + { +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else // region 3 + { +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else if(v < 0.0f) // region 5 + { +// v = 0.0f; + if(fB0>=0.0f) { /*u = 0.0f;*/ SqrDist = fC; } + else if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { +// u = 0.0f; +// v = 0.0f; + SqrDist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + u *= fInvDet; + v *= fInvDet; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(u < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// u = 1.0f; +// v = 0.0f; + SqrDist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + else + { +// u = 0.0f; + if(fTmp1 <= 0.0f) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else if(fB1 >= 0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else if(v < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// v = 1.0f; +// u = 0.0f; + SqrDist = fA11+2.0f*fB1+fC; + } + else + { + v = fNumer/fDenom; + u = 1.0f - v; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + else + { +// v = 0.0f; + if(fTmp1 <= 0.0f) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else if(fB0 >= 0.0f) { /*u = 0.0f;*/ SqrDist = fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { +// u = 0.0f; +// v = 1.0f; + SqrDist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// u = 1.0f; +// v = 0.0f; + SqrDist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + } + + return fabsf(SqrDist) < mRadius2; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_TreeBuilders.cpp b/thirdparty/ode-0.16.5/OPCODE/OPC_TreeBuilders.cpp new file mode 100644 index 0000000..4a78914 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_TreeBuilders.cpp @@ -0,0 +1,306 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for tree builders. + * \file OPC_TreeBuilders.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of vertices. + * + * \class AABBTreeOfVerticesBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of AABBs. + * + * \class AABBTreeOfAABBsBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of triangles. + * + * \class AABBTreeOfTrianglesBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the AABB of a set of primitives. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [out] global AABB enclosing the set of input primitives + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const dTriIndex* primitives, udword nb_prims, AABB& global_box) const +{ + // Checkings + if(!primitives || !nb_prims) return false; + + // Initialize global box + global_box = mAABBArray[primitives[0]]; + + // Loop through boxes + for(udword i=1;iGetTriangle(VP, *primitives++, VC); + // Update global box + Min.Min(*VP.Vertex[0]).Min(*VP.Vertex[1]).Min(*VP.Vertex[2]); + Max.Max(*VP.Vertex[0]).Max(*VP.Vertex[1]).Max(*VP.Vertex[2]); + } + global_box.SetMinMax(Min, Max); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the splitting value along a given axis for a given primitive. + * \param index [in] index of the primitive to split + * \return splitting value + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point AABBTreeOfTrianglesBuilder::GetSplittingValues(udword index) const +{ + VertexPointers VP; + ConversionArea VC; + mIMesh->GetTriangle(VP, index, VC); + + return ( *VP.Vertex[0] + *VP.Vertex[1] + *VP.Vertex[2] ) * INV3; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the splitting value along a given axis for a given primitive. + * \param index [in] index of the primitive to split + * \param axis [in] axis index (0,1,2) + * \return splitting value + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) const +{ +/* // Compute center of triangle + Point Center; + mTriList[index].Center(mVerts, Center); + // Return value + return Center[axis];*/ + + // Compute correct component from center of triangle +// return (mVerts[mTriList[index].mVRef[0]][axis] +// +mVerts[mTriList[index].mVRef[1]][axis] +// +mVerts[mTriList[index].mVRef[2]][axis])*INV3; + + VertexPointers VP; + ConversionArea VC; + mIMesh->GetTriangle(VP, index, VC); + + // Compute correct component from center of triangle + return ((*VP.Vertex[0])[axis] + +(*VP.Vertex[1])[axis] + +(*VP.Vertex[2])[axis])*INV3; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the splitting value along a given axis for a given node. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [in] global AABB enclosing the set of input primitives + * \param axis [in] axis index (0,1,2) + * \return splitting value + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABBTreeOfTrianglesBuilder::GetSplittingValue(const dTriIndex* primitives, udword nb_prims, const AABB& global_box, udword axis) const +{ + if(mSettings.mRules&SPLIT_GEOM_CENTER) + { + // Loop through triangles + float SplitValue = 0.0f; + VertexPointers VP; + ConversionArea VC; + for(udword i=0;iGetTriangle(VP, primitives[i], VC); + // Update split value + SplitValue += (*VP.Vertex[0])[axis]; + SplitValue += (*VP.Vertex[1])[axis]; + SplitValue += (*VP.Vertex[2])[axis]; + } + return SplitValue / float(nb_prims*3); + } + else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the AABB of a set of primitives. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [out] global AABB enclosing the set of input primitives + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const dTriIndex* primitives, udword nb_prims, AABB& global_box) const +{ + // Checkings + if(!primitives || !nb_prims) return false; + + // Initialize global box + global_box.SetEmpty(); + + // Loop through vertices + for(udword i=0;iHasLeafNodes()!=cache.Model1->HasLeafNodes()) return false; + if(cache.Model0->IsQuantized()!=cache.Model1->IsQuantized()) return false; + + /* + + Rules: + - perform hull test + - when hulls collide, disable hull test + - if meshes overlap, reset countdown + - if countdown reaches 0, enable hull test + + */ + +#ifdef __MESHMERIZER_H__ + // Handle hulls + if(cache.HullTest) + { + if(cache.Model0->GetHull() && cache.Model1->GetHull()) + { + struct Local + { + static Point* SVCallback(const Point& sv, udword& previndex, udword user_data) + { + CollisionHull* Hull = (CollisionHull*)user_data; + previndex = Hull->ComputeSupportingVertex(sv, previndex); + return (Point*)&Hull->GetVerts()[previndex]; + } + }; + + bool Collide; + + if(0) + { + static GJKEngine GJK; -- not thread safe, store in ThreadLocalData + static bool GJKInitDone=false; -- not thread safe, to be removed + if(!GJKInitDone) + { + GJK.Enable(GJK_BACKUP_PROCEDURE); + GJK.Enable(GJK_DEGENERATE); + GJK.Enable(GJK_HILLCLIMBING); + GJKInitDone = true; + } + GJK.SetCallbackObj0(Local::SVCallback); + GJK.SetCallbackObj1(Local::SVCallback); + GJK.SetUserData0(udword(cache.Model0->GetHull())); + GJK.SetUserData1(udword(cache.Model1->GetHull())); + Collide = GJK.Collide(*world0, *world1, &cache.SepVector); + } + else + { + static SVEngine SVE; -- not thread safe, store in ThreadLocalData + SVE.SetCallbackObj0(Local::SVCallback); + SVE.SetCallbackObj1(Local::SVCallback); + SVE.SetUserData0(udword(cache.Model0->GetHull())); + SVE.SetUserData1(udword(cache.Model1->GetHull())); + Collide = SVE.Collide(*world0, *world1, &cache.SepVector); + } + + if(!Collide) + { + // Reset stats & contact status + mFlags &= ~OPC_CONTACT; + mNbBVBVTests = 0; + mNbPrimPrimTests = 0; + mNbBVPrimTests = 0; + mPairs.Reset(); + return true; + } + } + } + + // Here, hulls collide + cache.HullTest = false; +#endif // __MESHMERIZER_H__ + + // Checkings + if(!Setup(cache.Model0->GetMeshInterface(), cache.Model1->GetMeshInterface())) return false; + + // Simple double-dispatch + bool Status; + if(!cache.Model0->HasLeafNodes()) + { + if(cache.Model0->IsQuantized()) + { + const AABBQuantizedNoLeafTree* T0 = static_cast(cache.Model0->GetTree()); + const AABBQuantizedNoLeafTree* T1 = static_cast(cache.Model1->GetTree()); + Status = Collide(T0, T1, world0, world1, &cache); + } + else + { + const AABBNoLeafTree* T0 = static_cast(cache.Model0->GetTree()); + const AABBNoLeafTree* T1 = static_cast(cache.Model1->GetTree()); + Status = Collide(T0, T1, world0, world1, &cache); + } + } + else + { + if(cache.Model0->IsQuantized()) + { + const AABBQuantizedTree* T0 = static_cast(cache.Model0->GetTree()); + const AABBQuantizedTree* T1 = static_cast(cache.Model1->GetTree()); + Status = Collide(T0, T1, world0, world1, &cache); + } + else + { + const AABBCollisionTree* T0 = static_cast(cache.Model0->GetTree()); + const AABBCollisionTree* T1 = static_cast(cache.Model1->GetTree()); + Status = Collide(T0, T1, world0, world1, &cache); + } + } + +#ifdef __MESHMERIZER_H__ + if(Status) + { + // Reset counter as long as overlap occurs + if(GetContactStatus()) cache.ResetCountDown(); + + // Enable hull test again when counter reaches zero + cache.CountDown--; + if(!cache.CountDown) + { + cache.ResetCountDown(); + cache.HullTest = true; + } + } +#endif + return Status; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::InitQuery(const Matrix4x4* world0, const Matrix4x4* world1) +{ + // Reset stats & contact status + Collider::InitQuery(); + mNbBVBVTests = 0; + mNbPrimPrimTests = 0; + mNbBVPrimTests = 0; + mPairs.Reset(); + + // Setup matrices + Matrix4x4 InvWorld0, InvWorld1; + if(world0) InvertPRMatrix(InvWorld0, *world0); + else InvWorld0.Identity(); + + if(world1) InvertPRMatrix(InvWorld1, *world1); + else InvWorld1.Identity(); + + Matrix4x4 World0to1 = world0 ? (*world0 * InvWorld1) : InvWorld1; + Matrix4x4 World1to0 = world1 ? (*world1 * InvWorld0) : InvWorld0; + + mR0to1 = World0to1; World0to1.GetTrans(mT0to1); + mR1to0 = World1to0; World1to0.GetTrans(mT1to0); + + // Precompute absolute 1-to-0 rotation matrix + for(udword i=0;i<3;i++) + { + for(udword j=0;j<3;j++) + { + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + mAR.m[i][j] = 1e-6f + fabsf(mR1to0.m[i][j]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Takes advantage of temporal coherence. + * \param cache [in] cache for a pair of previously colliding primitives + * \return true if we can return immediately + * \warning only works for "First Contact" mode + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::CheckTemporalCoherence(Pair* cache) +{ + // Checkings + if(!cache) return false; + + // Test previously colliding primitives first + if(TemporalCoherenceEnabled() && FirstContactEnabled()) + { + PrimTest(cache->id0, cache->id1); + if(GetContactStatus()) return true; + } + return false; +} + +#define UPDATE_CACHE \ + if(cache && GetContactStatus()) \ + { \ + cache->id0 = mPairs.GetEntry(0); \ + cache->id1 = mPairs.GetEntry(1); \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for normal AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for no-leaf AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff0 = tree0->mCenterCoeff; + mExtentsCoeff0 = tree0->mExtentsCoeff; + mCenterCoeff1 = tree1->mCenterCoeff; + mExtentsCoeff1 = tree1->mExtentsCoeff; + + // Dequantize box A + const AABBQuantizedNode* N0 = tree0->GetNodes(); + const Point a(float(N0->mAABB.mExtents[0]) * mExtentsCoeff0.x, float(N0->mAABB.mExtents[1]) * mExtentsCoeff0.y, float(N0->mAABB.mExtents[2]) * mExtentsCoeff0.z); + const Point Pa(float(N0->mAABB.mCenter[0]) * mCenterCoeff0.x, float(N0->mAABB.mCenter[1]) * mCenterCoeff0.y, float(N0->mAABB.mCenter[2]) * mCenterCoeff0.z); + // Dequantize box B + const AABBQuantizedNode* N1 = tree1->GetNodes(); + const Point b(float(N1->mAABB.mExtents[0]) * mExtentsCoeff1.x, float(N1->mAABB.mExtents[1]) * mExtentsCoeff1.y, float(N1->mAABB.mExtents[2]) * mExtentsCoeff1.z); + const Point Pb(float(N1->mAABB.mCenter[0]) * mCenterCoeff1.x, float(N1->mAABB.mCenter[1]) * mCenterCoeff1.y, float(N1->mAABB.mCenter[2]) * mCenterCoeff1.z); + + // Perform collision query + _Collide(N0, N1, a, Pa, b, Pb); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized no-leaf AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff0 = tree0->mCenterCoeff; + mExtentsCoeff0 = tree0->mExtentsCoeff; + mCenterCoeff1 = tree1->mCenterCoeff; + mExtentsCoeff1 = tree1->mExtentsCoeff; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Standard trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The normal AABB tree can use 2 different descent rules (with different performances) +//#define ORIGINAL_CODE //!< UNC-like descent rules +#define ALTERNATIVE_CODE //!< Alternative descent rules + +#ifdef ORIGINAL_CODE +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) return; + + if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; } + + if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize()))) + { + _Collide(b0->GetNeg(), b1); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1); + } + else + { + _Collide(b0, b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0, b1->GetPos()); + } +} +#endif + +#ifdef ALTERNATIVE_CODE +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) + { + return; + } + + if(b0->IsLeaf()) + { + if(b1->IsLeaf()) + { + PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); + } + else + { + _Collide(b0, b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0, b1->GetPos()); + } + } + else if(b1->IsLeaf()) + { + _Collide(b0->GetNeg(), b1); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1); + } + else + { + _Collide(b0->GetNeg(), b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0->GetNeg(), b1->GetPos()); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1->GetPos()); + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// No-leaf trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for two primitive indices. + * \param id0 [in] index from first leaf-triangle + * \param id1 [in] index from second leaf-triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::PrimTest(udword id0, udword id1) +{ + // Request vertices from the app + VertexPointers VP0; + VertexPointers VP1; + ConversionArea VC0; + ConversionArea VC1; + mIMesh0->GetTriangle(VP0, id0, VC0); + mIMesh1->GetTriangle(VP1, id1, VC1); + + // Transform from space 1 to space 0 + Point u0,u1,u2; + TransformPoint(u0, *VP1.Vertex[0], mR1to0, mT1to0); + TransformPoint(u1, *VP1.Vertex[1], mR1to0, mT1to0); + TransformPoint(u2, *VP1.Vertex[2], mR1to0, mT1to0); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(*VP0.Vertex[0], *VP0.Vertex[1], *VP0.Vertex[2], u0, u1, u2)) + { + // Keep track of colliding pairs + mPairs.Add(id0).Add(id1); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for a previously fetched triangle from tree A (in B's space) and a new leaf from B. + * \param id1 [in] leaf-triangle index from tree B + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void AABBTreeCollider::PrimTestTriIndex(udword id1) +{ + // Request vertices from the app + VertexPointers VP; + ConversionArea VC; + mIMesh1->GetTriangle(VP, id1, VC); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Keep track of colliding pairs + mPairs.Add(mLeafIndex).Add(id1); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for a previously fetched triangle from tree B (in A's space) and a new leaf from A. + * \param id0 [in] leaf-triangle index from tree A + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void AABBTreeCollider::PrimTestIndexTri(udword id0) +{ + // Request vertices from the app + VertexPointers VP; + ConversionArea VC; + mIMesh0->GetTriangle(VP, id0, VC); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Keep track of colliding pairs + mPairs.Add(id0).Add(mLeafIndex); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from A and a branch from B. + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideTriBox(const AABBNoLeafNode* b) +{ + // Perform triangle-box overlap test + if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; + + // Keep same triangle, deal with first child + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + // Keep same triangle, deal with second child + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from B and a branch from A. + * \param b [in] collision node from first tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideBoxTri(const AABBNoLeafNode* b) +{ + // Perform triangle-box overlap test + if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; + + // Keep same triangle, deal with first child + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); + else _CollideBoxTri(b->GetPos()); + + if(ContactFound()) return; + + // Keep same triangle, deal with second child + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); + else _CollideBoxTri(b->GetNeg()); +} + +//! Request triangle vertices from the app and transform them +#define FETCH_LEAF(prim_index, imesh, rot, trans) \ + mLeafIndex = prim_index; \ + /* Request vertices from the app */ \ + VertexPointers VP; ConversionArea VC; imesh->GetTriangle(VP, prim_index, VC); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param a [in] collision node from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(a->mAABB.mExtents, a->mAABB.mCenter, b->mAABB.mExtents, b->mAABB.mCenter)) return; + + // Catch leaf status + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); + + if(a->HasPosLeaf()) + { + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetNeg()); + } + + if(ContactFound()) return; + + if(a->HasNegLeaf()) + { + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantized trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + * \param a [in] extent from box A + * \param Pa [in] center from box A + * \param b [in] extent from box B + * \param Pb [in] center from box B + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(a, Pa, b, Pb)) return; + + if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; } + + if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize()))) + { + // Dequantize box + const QuantizedAABB* Box = &b0->GetNeg()->mAABB; + const Point negPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z); + const Point nega(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z); + _Collide(b0->GetNeg(), b1, nega, negPa, b, Pb); + + if(ContactFound()) return; + + // Dequantize box + Box = &b0->GetPos()->mAABB; + const Point posPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z); + const Point posa(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z); + _Collide(b0->GetPos(), b1, posa, posPa, b, Pb); + } + else + { + // Dequantize box + const QuantizedAABB* Box = &b1->GetNeg()->mAABB; + const Point negPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z); + const Point negb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z); + _Collide(b0, b1->GetNeg(), a, Pa, negb, negPb); + + if(ContactFound()) return; + + // Dequantize box + Box = &b1->GetPos()->mAABB; + const Point posPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z); + const Point posb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z); + _Collide(b0, b1->GetPos(), a, Pa, posb, posPb); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantized no-leaf trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from A and a quantized branch from B. + * \param leaf [in] leaf triangle from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideTriBox(const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box + const QuantizedAABB* bb = &b->mAABB; + const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z); + const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z); + + // Perform triangle-box overlap test + if(!TriBoxOverlap(Pb, eb)) return; + + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from B and a quantized branch from A. + * \param b [in] collision node from first tree + * \param leaf [in] leaf triangle from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideBoxTri(const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box + const QuantizedAABB* bb = &b->mAABB; + const Point Pa(float(bb->mCenter[0]) * mCenterCoeff0.x, float(bb->mCenter[1]) * mCenterCoeff0.y, float(bb->mCenter[2]) * mCenterCoeff0.z); + const Point ea(float(bb->mExtents[0]) * mExtentsCoeff0.x, float(bb->mExtents[1]) * mExtentsCoeff0.y, float(bb->mExtents[2]) * mExtentsCoeff0.z); + + // Perform triangle-box overlap test + if(!TriBoxOverlap(Pa, ea)) return; + + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); + else _CollideBoxTri(b->GetPos()); + + if(ContactFound()) return; + + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); + else _CollideBoxTri(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param a [in] collision node from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box A + const QuantizedAABB* ab = &a->mAABB; + const Point Pa(float(ab->mCenter[0]) * mCenterCoeff0.x, float(ab->mCenter[1]) * mCenterCoeff0.y, float(ab->mCenter[2]) * mCenterCoeff0.z); + const Point ea(float(ab->mExtents[0]) * mExtentsCoeff0.x, float(ab->mExtents[1]) * mExtentsCoeff0.y, float(ab->mExtents[2]) * mExtentsCoeff0.z); + // Dequantize box B + const QuantizedAABB* bb = &b->mAABB; + const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z); + const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z); + + // Perform BV-BV overlap test + if(!BoxBoxOverlap(ea, Pa, eb, Pb)) return; + + // Catch leaf status + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); + + if(a->HasPosLeaf()) + { + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetNeg()); + } + + if(ContactFound()) return; + + if(a->HasNegLeaf()) + { + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetNeg()); + } +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_TreeCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_TreeCollider.h new file mode 100644 index 0000000..1e943a4 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_TreeCollider.h @@ -0,0 +1,246 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a tree collider. + * \file OPC_TreeCollider.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_TREECOLLIDER_H__ +#define __OPC_TREECOLLIDER_H__ + + //! This structure holds cached information used by the algorithm. + //! Two model pointers and two colliding primitives are cached. Model pointers are assigned + //! to their respective meshes, and the pair of colliding primitives is used for temporal + //! coherence. That is, in case temporal coherence is enabled, those two primitives are + //! tested for overlap before everything else. If they still collide, we're done before + //! even entering the recursive collision code. + struct OPCODE_API BVTCache : Pair + { + //! Constructor + inline_ BVTCache() + { + ResetCache(); + ResetCountDown(); + } + + void ResetCache() + { + Model0 = null; + Model1 = null; + id0 = 0; + id1 = 1; +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + HullTest = true; + SepVector.pid = 0; + SepVector.qid = 0; + SepVector.SV = Point(1.0f, 0.0f, 0.0f); +#endif // __MESHMERIZER_H__ + } + +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + inline_ void ResetCountDown() + { + CountDown = 50; + } +#else + void ResetCountDown(){}; +#endif // __MESHMERIZER_H__ + + const Model* Model0; //!< Model for first object + const Model* Model1; //!< Model for second object + +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + SVCache SepVector; + udword CountDown; + bool HullTest; +#endif // __MESHMERIZER_H__ + }; + + class OPCODE_API AABBTreeCollider : public Collider + { + public: + // Constructor / Destructor + AABBTreeCollider(); + virtual ~AABBTreeCollider(); + // Generic collision query + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results with: + * - GetContactStatus() + * - GetNbPairs() + * - GetPairs() + * + * \param cache [in] collision cache for model pointers and a colliding pair of primitives + * \param world0 [in] world matrix for first object, or null + * \param world1 [in] world matrix for second object, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(BVTCache& cache, const Matrix4x4* world0=null, const Matrix4x4* world1=null); + + // Collision queries + bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: selects between full box-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + * \see SetFullPrimBoxTest(bool flag) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: selects between full triangle-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + * \see SetFullBoxBoxTest(bool flag) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of BV-BV overlap tests after a collision query. + * \see GetNbPrimPrimTests() + * \see GetNbBVPrimTests() + * \return the number of BV-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbBVBVTests() const { return mNbBVBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Triangle-Triangle overlap tests after a collision query. + * \see GetNbBVBVTests() + * \see GetNbBVPrimTests() + * \return the number of Triangle-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of BV-Triangle overlap tests after a collision query. + * \see GetNbBVBVTests() + * \see GetNbPrimPrimTests() + * \return the number of BV-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbBVPrimTests() const { return mNbBVPrimTests; } + + // Data access + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of contacts after a collision query. + * \see GetContactStatus() + * \see GetPairs() + * \return the number of contacts / colliding pairs. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the pairs of colliding triangles after a collision query. + * \see GetContactStatus() + * \see GetNbPairs() + * \return the list of colliding pairs (triangle indices) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const Pair* GetPairs() const { return (const Pair*)mPairs.GetEntries(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Colliding pairs + Container mPairs; //!< Pairs of colliding primitives + // User mesh interfaces + const MeshInterface* mIMesh0; //!< User-defined mesh interface for object0 + const MeshInterface* mIMesh1; //!< User-defined mesh interface for object1 + // Stats + udword mNbBVBVTests; //!< Number of BV-BV tests + udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests + udword mNbBVPrimTests; //!< Number of BV-Primitive tests + // Precomputed data + Matrix3x3 mAR; //!< Absolute rotation matrix + Matrix3x3 mR0to1; //!< Rotation from object0 to object1 + Matrix3x3 mR1to0; //!< Rotation from object1 to object0 + Point mT0to1; //!< Translation from object0 to object1 + Point mT1to0; //!< Translation from object1 to object0 + // Dequantization coeffs + Point mCenterCoeff0; + Point mExtentsCoeff0; + Point mCenterCoeff1; + Point mExtentsCoeff1; + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + udword mLeafIndex; //!< Triangle index + // Settings + bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) + bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false) + // Internal methods + + // Standard AABB trees + void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1); + // Quantized AABB trees + void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb); + // No-leaf AABB trees + void _CollideTriBox(const AABBNoLeafNode* b); + void _CollideBoxTri(const AABBNoLeafNode* b); + void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b); + // Quantized no-leaf AABB trees + void _CollideTriBox(const AABBQuantizedNoLeafNode* b); + void _CollideBoxTri(const AABBQuantizedNoLeafNode* b); + void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b); + // Overlap tests + void PrimTest(udword id0, udword id1); + inline_ void PrimTestTriIndex(udword id1); + inline_ void PrimTestIndexTri(udword id0); + + inline_ BOOL BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb); + inline_ BOOL TriBoxOverlap(const Point& center, const Point& extents); + inline_ BOOL TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2); + // Init methods + void InitQuery(const Matrix4x4* world0=null, const Matrix4x4* world1=null); + bool CheckTemporalCoherence(Pair* cache); + + inline_ BOOL Setup(const MeshInterface* mi0, const MeshInterface* mi1) + { + mIMesh0 = mi0; + mIMesh1 = mi1; + + if(!mIMesh0 || !mIMesh1) return FALSE; + + return TRUE; + } + }; + +#endif // __OPC_TREECOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_TriBoxOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_TriBoxOverlap.h new file mode 100644 index 0000000..b3a9bde --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_TriBoxOverlap.h @@ -0,0 +1,339 @@ + +//! This macro quickly finds the min & max values among 3 variables +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if(x1max) max=x1; \ + if(x2max) max=x2; + +//! TO BE DOCUMENTED +inline_ BOOL planeBoxOverlap(const Point& normal, const float d, const Point& maxbox) +{ + Point vmin, vmax; + for(udword q=0;q<=2;q++) + { + if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; } + else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; } + } + if((normal|vmin)+d>0.0f) return FALSE; + if((normal|vmax)+d>=0.0f) return TRUE; + + return FALSE; +} + +//! TO BE DOCUMENTED +#define AXISTEST_X01(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v2.y - b*v2.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_X2(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v1.y - b*v1.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Y02(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v2.z - a*v2.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Y1(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v1.z - a*v1.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Z12(a, b, fa, fb) \ + min = a*v1.x - b*v1.y; \ + max = a*v2.x - b*v2.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Z0(a, b, fa, fb) \ + min = a*v0.x - b*v0.y; \ + max = a*v1.x - b*v1.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return FALSE; + +// compute triangle edges +// - edges lazy evaluated to take advantage of early exits +// - fabs precomputed (half less work, possible since extents are always >0) +// - customized macros to take advantage of the null component +// - axis vector discarded, possibly saves useless movs +#define IMPLEMENT_CLASS3_TESTS \ + float rad; \ + float min, max; \ + \ + const float fey0 = fabsf(e0.y); \ + const float fez0 = fabsf(e0.z); \ + AXISTEST_X01(e0.z, e0.y, fez0, fey0); \ + const float fex0 = fabsf(e0.x); \ + AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \ + AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \ + \ + const float fey1 = fabsf(e1.y); \ + const float fez1 = fabsf(e1.z); \ + AXISTEST_X01(e1.z, e1.y, fez1, fey1); \ + const float fex1 = fabsf(e1.x); \ + AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \ + AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \ + \ + const Point e2 = mLeafVerts[0] - mLeafVerts[2]; \ + const float fey2 = fabsf(e2.y); \ + const float fez2 = fabsf(e2.z); \ + AXISTEST_X2(e2.z, e2.y, fez2, fey2); \ + const float fex2 = fabsf(e2.x); \ + AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \ + AXISTEST_Z12(e2.y, e2.x, fey2, fex2); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle-Box overlap test using the separating axis theorem. + * This is the code from Tomas Mller, a bit optimized: + * - with some more lazy evaluation (faster path on PC) + * - with a tiny bit of assembly + * - with "SAT-lite" applied if needed + * - and perhaps with some more minor modifs... + * + * \param center [in] box center + * \param extents [in] box extents + * \return true if triangle & box overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbBVPrimTests++; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // move everything so that the boxcenter is in (0,0,0) + Point v0, v1, v2; + v0.x = mLeafVerts[0].x - center.x; + v1.x = mLeafVerts[1].x - center.x; + v2.x = mLeafVerts[2].x - center.x; + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE; + + // same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE; + + // same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>extents.x || max<-extents.x) return FALSE; + + // Same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>extents.y || max<-extents.y) return FALSE; + + // Same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>extents.z || max<-extents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, extents)) return FALSE; + + // 3) "Class III" tests + if(mFullPrimBoxTest) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} + +//! A dedicated version where the box is constant +inline_ BOOL OBBCollider::TriBoxOverlap() +{ + // Stats + mNbVolumePrimTests++; + + // Hook + const Point& extents = mBoxExtents; + const Point& v0 = mLeafVerts[0]; + const Point& v1 = mLeafVerts[1]; + const Point& v2 = mLeafVerts[2]; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // Box center is already in (0,0,0) + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE; + + if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE; + + if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE; + + // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} + +//! ...and another one, jeez +inline_ BOOL AABBCollider::TriBoxOverlap() +{ + // Stats + mNbVolumePrimTests++; + + // Hook + const Point& center = mBox.mCenter; + const Point& extents = mBox.mExtents; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // move everything so that the boxcenter is in (0,0,0) + Point v0, v1, v2; + v0.x = mLeafVerts[0].x - center.x; + v1.x = mLeafVerts[1].x - center.x; + v2.x = mLeafVerts[2].x - center.x; + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE; + + // same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE; + + // same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>extents.x || max<-extents.x) return FALSE; + + // Same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>extents.y || max<-extents.y) return FALSE; + + // Same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>extents.z || max<-extents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, extents)) return FALSE; + + // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_TriTriOverlap.h b/thirdparty/ode-0.16.5/OPCODE/OPC_TriTriOverlap.h new file mode 100644 index 0000000..fd652c9 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_TriTriOverlap.h @@ -0,0 +1,299 @@ + +//! if OPC_TRITRI_EPSILON_TEST is true then we do a check (if |dv|b) \ + { \ + const float c=a; \ + a=b; \ + b=c; \ + } + +//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202 +#define EDGE_EDGE_TEST(V0, U0, U1) \ + Bx = U0[i0] - U1[i0]; \ + By = U0[i1] - U1[i1]; \ + Cx = V0[i0] - U0[i0]; \ + Cy = V0[i1] - U0[i1]; \ + f = Ay*Bx - Ax*By; \ + d = By*Cx - Bx*Cy; \ + if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \ + { \ + const float e=Ax*Cy - Ay*Cx; \ + if(f>0.0f) \ + { \ + if(e>=0.0f && e<=f) return TRUE; \ + } \ + else \ + { \ + if(e<=0.0f && e>=f) return TRUE; \ + } \ + } + +//! TO BE DOCUMENTED +#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \ +{ \ + float Bx,By,Cx,Cy,d,f; \ + const float Ax = V1[i0] - V0[i0]; \ + const float Ay = V1[i1] - V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U0, U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U1, U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U2, U0); \ +} + +//! TO BE DOCUMENTED +#define POINT_IN_TRI(V0, U0, U1, U2) \ +{ \ + /* is T1 completly inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + float a = U1[i1] - U0[i1]; \ + float b = -(U1[i0] - U0[i0]); \ + float c = -a*U0[i0] - b*U0[i1]; \ + float d0 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U2[i1] - U1[i1]; \ + b = -(U2[i0] - U1[i0]); \ + c = -a*U1[i0] - b*U1[i1]; \ + const float d1 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U0[i1] - U2[i1]; \ + b = -(U0[i0] - U2[i0]); \ + c = -a*U2[i0] - b*U2[i1]; \ + const float d2 = a*V0[i0] + b*V0[i1] + c; \ + if(d0*d1>0.0f) \ + { \ + if(d0*d2>0.0f) return TRUE; \ + } \ +} + +//! TO BE DOCUMENTED +BOOL CoplanarTriTri(const Point& n, const Point& v0, const Point& v1, const Point& v2, const Point& u0, const Point& u1, const Point& u2) +{ + float A[3]; + short i0,i1; + /* first project onto an axis-aligned plane, that maximizes the area */ + /* of the triangles, compute indices: i0,i1. */ + A[0] = fabsf(n[0]); + A[1] = fabsf(n[1]); + A[2] = fabsf(n[2]); + if(A[0]>A[1]) + { + if(A[0]>A[2]) + { + i0=1; /* A[0] is greatest */ + i1=2; + } + else + { + i0=0; /* A[2] is greatest */ + i1=1; + } + } + else /* A[0]<=A[1] */ + { + if(A[2]>A[1]) + { + i0=0; /* A[2] is greatest */ + i1=1; + } + else + { + i0=0; /* A[1] is greatest */ + i1=2; + } + } + + /* test all edges of triangle 1 against the edges of triangle 2 */ + EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2); + + /* finally, test if tri1 is totally contained in tri2 or vice versa */ + POINT_IN_TRI(v0, u0, u1, u2); + POINT_IN_TRI(u0, v0, v1, v2); + + return FALSE; +} + +//! TO BE DOCUMENTED +#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \ +{ \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else if(D0D2>0.0f) \ + { \ + /* here we know that d0d1<=0.0 */ \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \ + } \ + else if(D1!=0.0f) \ + { \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D2!=0.0f) \ + { \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \ + } \ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle/triangle intersection test routine, + * by Tomas Moller, 1997. + * See article "A Fast Triangle-Triangle Intersection Test", + * Journal of Graphics Tools, 2(2), 1997 + * + * Updated June 1999: removed the divisions -- a little faster now! + * Updated October 1999: added {} to CROSS and SUB macros + * + * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3], + * float U0[3],float U1[3],float U2[3]) + * + * \param V0 [in] triangle 0, vertex 0 + * \param V1 [in] triangle 0, vertex 1 + * \param V2 [in] triangle 0, vertex 2 + * \param U0 [in] triangle 1, vertex 0 + * \param U1 [in] triangle 1, vertex 1 + * \param U2 [in] triangle 1, vertex 2 + * \return true if triangles overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2) +{ + // Stats + mNbPrimPrimTests++; + + // Compute plane equation of triangle(V0,V1,V2) + Point E1 = V1 - V0; + Point E2 = V2 - V0; + const Point N1 = E1 ^ E2; + const float d1 =-N1 | V0; + // Plane equation 1: N1.X+d1=0 + + // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane + float du0 = (N1|U0) + d1; + float du1 = (N1|U1) + d1; + float du2 = (N1|U2) + d1; + + // Coplanarity robustness check +#ifdef OPC_TRITRI_EPSILON_TEST + float absd1 = FastFabs(d1), sqmagN1 = N1.SquareMagnitude(); + if (absd1>=sqmagN1) + { + if(FastFabs(du0)<=LOCAL_EPSILON*absd1) du0 = 0.0f; + if(FastFabs(du1)<=LOCAL_EPSILON*absd1) du1 = 0.0f; + if(FastFabs(du2)<=LOCAL_EPSILON*absd1) du2 = 0.0f; + } + else + { + if(FastFabs(du0)<=LOCAL_EPSILON*FCMax2(absd1, FCMin2(sqmagN1, U0.SquareMagnitude()))) du0 = 0.0f; + if(FastFabs(du1)<=LOCAL_EPSILON*FCMax2(absd1, FCMin2(sqmagN1, U1.SquareMagnitude()))) du1 = 0.0f; + if(FastFabs(du2)<=LOCAL_EPSILON*FCMax2(absd1, FCMin2(sqmagN1, U2.SquareMagnitude()))) du2 = 0.0f; + } +#endif + const float du0du1 = du0 * du1; + const float du0du2 = du0 * du2; + + if(du0du1>0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? + return FALSE; // no intersection occurs + + // Compute plane of triangle (U0,U1,U2) + E1 = U1 - U0; + E2 = U2 - U0; + const Point N2 = E1 ^ E2; + const float d2=-N2 | U0; + // plane equation 2: N2.X+d2=0 + + // put V0,V1,V2 into plane equation 2 + float dv0 = (N2|V0) + d2; + float dv1 = (N2|V1) + d2; + float dv2 = (N2|V2) + d2; + +#ifdef OPC_TRITRI_EPSILON_TEST + float absd2 = FastFabs(d2), sqmagN2 = N2.SquareMagnitude(); + if (absd2>=sqmagN2) + { + if(FastFabs(dv0)<=LOCAL_EPSILON*absd2) dv0 = 0.0f; + if(FastFabs(dv1)<=LOCAL_EPSILON*absd2) dv1 = 0.0f; + if(FastFabs(dv2)<=LOCAL_EPSILON*absd2) dv2 = 0.0f; + } + else + { + if(FastFabs(dv0)<=LOCAL_EPSILON*FCMax2(absd2, FCMin2(sqmagN2, V0.SquareMagnitude()))) dv0 = 0.0f; + if(FastFabs(dv1)<=LOCAL_EPSILON*FCMax2(absd2, FCMin2(sqmagN2, V1.SquareMagnitude()))) dv1 = 0.0f; + if(FastFabs(dv2)<=LOCAL_EPSILON*FCMax2(absd2, FCMin2(sqmagN2, V2.SquareMagnitude()))) dv2 = 0.0f; + } +#endif + + const float dv0dv1 = dv0 * dv1; + const float dv0dv2 = dv0 * dv2; + + if(dv0dv1>0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? + return FALSE; // no intersection occurs + + // Compute direction of intersection line + const Point D = N1^N2; + + // Compute and index to the largest component of D + float max=fabsf(D[0]); + short index=0; + float bb=fabsf(D[1]); + float cc=fabsf(D[2]); + if(bb>max) max=bb,index=1; + if(cc>max) max=cc,index=2; + + // This is the simplified projection onto L + const float vp0 = V0[index]; + const float vp1 = V1[index]; + const float vp2 = V2[index]; + + const float up0 = U0[index]; + const float up1 = U1[index]; + const float up2 = U2[index]; + + // Compute interval for triangle 1 + float a,b,c,x0,x1; + NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1); + + // Compute interval for triangle 2 + float d,e,f,y0,y1; + NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1); + + const float xx=x0*x1; + const float yy=y0*y1; + const float xxyy=xx*yy; + + float isect1[2], isect2[2]; + + float tmp=a*xxyy; + isect1[0]=tmp+b*x1*yy; + isect1[1]=tmp+c*x0*yy; + + tmp=d*xxyy; + isect2[0]=tmp+e*xx*y1; + isect2[1]=tmp+f*xx*y0; + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + if(isect1[1]HasPosLeaf()) mTouchedPrimitives->Add(udword(node->GetPosPrimitive())); \ + else _Dump(node->GetPos()); \ + \ + if(ContactFound()) return; \ + \ + if(node->HasNegLeaf()) mTouchedPrimitives->Add(udword(node->GetNegPrimitive())); \ + else _Dump(node->GetNeg()); \ +} + +#define IMPLEMENT_LEAFDUMP(type) \ +void VolumeCollider::_Dump(const type* node) \ +{ \ + if(node->IsLeaf()) \ + { \ + mTouchedPrimitives->Add(udword(node->GetPrimitive())); \ + } \ + else \ + { \ + _Dump(node->GetPos()); \ + \ + if(ContactFound()) return; \ + \ + _Dump(node->GetNeg()); \ + } \ +} + +IMPLEMENT_NOLEAFDUMP(AABBNoLeafNode) +IMPLEMENT_NOLEAFDUMP(AABBQuantizedNoLeafNode) + +IMPLEMENT_LEAFDUMP(AABBCollisionNode) +IMPLEMENT_LEAFDUMP(AABBQuantizedNode) diff --git a/thirdparty/ode-0.16.5/OPCODE/OPC_VolumeCollider.h b/thirdparty/ode-0.16.5/OPCODE/OPC_VolumeCollider.h new file mode 100644 index 0000000..c0b812e --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/OPC_VolumeCollider.h @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base volume collider class. + * \file OPC_VolumeCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_VOLUMECOLLIDER_H__ +#define __OPC_VOLUMECOLLIDER_H__ + + struct OPCODE_API VolumeCache + { + VolumeCache() : Model(null) {} + ~VolumeCache() {} + + Container TouchedPrimitives; //!< Indices of touched primitives + const BaseModel* Model; //!< Owner + }; + + class OPCODE_API VolumeCollider : public Collider + { + public: + // Constructor / Destructor + VolumeCollider(); + virtual ~VolumeCollider() = 0; + + // Collision report + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of touched primitives after a collision query. + * \see GetContactStatus() + * \see GetTouchedPrimitives() + * \return the number of touched primitives + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetNbEntries() : 0; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the list of touched primitives after a collision query. + * \see GetContactStatus() + * \see GetNbTouchedPrimitives() + * \return the list of touched primitives (primitive indices) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Volume-BV overlap tests after a collision query. + * \see GetNbVolumePrimTests() + * \return the number of Volume-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbVolumeBVTests() const { return mNbVolumeBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Volume-Triangle overlap tests after a collision query. + * \see GetNbVolumeBVTests() + * \return the number of Volume-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbVolumePrimTests() const { return mNbVolumePrimTests; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Touched primitives + Container* mTouchedPrimitives; //!< List of touched primitives + + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Stats + udword mNbVolumeBVTests; //!< Number of Volume-BV tests + udword mNbVolumePrimTests; //!< Number of Volume-Primitive tests + // Internal methods + void _Dump(const AABBCollisionNode* node); + void _Dump(const AABBNoLeafNode* node); + void _Dump(const AABBQuantizedNode* node); + void _Dump(const AABBQuantizedNoLeafNode* node); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) inline_ void InitQuery() + { + // Reset stats & contact status + mNbVolumeBVTests = 0; + mNbVolumePrimTests = 0; + Collider::InitQuery(); + } + + inline_ BOOL IsCacheValid(VolumeCache& cache) + { + // We're going to do a volume-vs-model query. + if(cache.Model!=mCurrentModel) + { + // Cached list was for another model so we can't keep it + // Keep track of new owner and reset cache + cache.Model = mCurrentModel; + return FALSE; + } + else + { + // Same models, no problem + return TRUE; + } + } + }; + +#endif // __OPC_VOLUMECOLLIDER_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/Opcode.cpp b/thirdparty/ode-0.16.5/OPCODE/Opcode.cpp new file mode 100644 index 0000000..444619a --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Opcode.cpp @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main file for Opcode.dll. + * \file Opcode.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + Finding a good name is difficult! + Here's the draft for this lib.... Spooky, uh? + + VOID? Very Optimized Interference Detection + ZOID? Zappy's Optimized Interference Detection + CID? Custom/Clever Interference Detection + AID / ACID! Accurate Interference Detection + QUID? Quick Interference Detection + RIDE? Realtime Interference DEtection + WIDE? Wicked Interference DEtection (....) + GUID! + KID ! k-dop interference detection :) + OPCODE! OPtimized COllision DEtection +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Precompiled Header +#include "Stdafx.h" + +using namespace Opcode; + + +static OPCODE_AbortHandler g_fnAbortHandler = NULL; + + +bool Opcode::InitOpcode(OPCODE_AbortHandler fnAbortHandler/*=NULL*/) +{ + //Log("// Initializing OPCODE\n\n"); +// LogAPIInfo(); + + g_fnAbortHandler = fnAbortHandler; + return true; +} + +bool Opcode::CloseOpcode() +{ + //Log("// Closing OPCODE\n\n"); + + return true; +} + + +#ifdef ICE_MAIN + +void ModuleAttach(HINSTANCE hinstance) +{ +} + +void ModuleDetach() +{ +} + +#endif + +/*extern */ +void OPCODE_NORETURN IceAbort() +{ + if (g_fnAbortHandler != NULL) + { + g_fnAbortHandler(); + } + + abort(); +} diff --git a/thirdparty/ode-0.16.5/OPCODE/Opcode.h b/thirdparty/ode-0.16.5/OPCODE/Opcode.h new file mode 100644 index 0000000..4a98e36 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Opcode.h @@ -0,0 +1,126 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main file for Opcode.dll. + * \file Opcode.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPCODE_H__ +#define __OPCODE_H__ + +// stddef.h and stdarg.h must be included before Opcode headers +// as they latermay not compile being not able to find types in std:: +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Things to help us compile on non-windows platforms + +#if defined(__APPLE__) || defined(__MACOSX__) +#if __APPLE_CC__ < 1495 +#define sqrtf sqrt +#define sinf sin +#define cosf cos +#define acosf acos +#define asinf asin +#endif +#endif + +#ifndef _MSC_VER +#ifndef __int64 +#define __int64 long long int +#endif +#ifndef __stdcall /* this is defined in MinGW and CygWin, so avoid the warning */ +#define __stdcall /* */ +#endif +#endif + +#if defined(__GNUC__) +#define OPCODE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define OPCODE_NORETURN __declspec(noreturn) +#else // #if !defined(_MSC_VER) +#define OPCODE_NORETURN +#endif // #if !defined(__GNUC__) + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compilation messages +#ifdef _MSC_VER + #if defined(OPCODE_EXPORTS) + // #pragma message("Compiling OPCODE") + #elif !defined(OPCODE_EXPORTS) + // #pragma message("Using OPCODE") + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Automatic linking + #ifndef BAN_OPCODE_AUTOLINK + #ifdef _DEBUG + //#pragma comment(lib, "Opcode_D.lib") + #else + //#pragma comment(lib, "Opcode.lib") + #endif + #endif + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Preprocessor +#ifndef ICE_NO_DLL + #ifdef OPCODE_EXPORTS + #define OPCODE_API// __declspec(dllexport) + #else + #define OPCODE_API// __declspec(dllimport) + #endif +#else + #define OPCODE_API +#endif + + #include "OPC_Settings.h" + #include "OPC_IceHook.h" + + namespace Opcode + { + // Bulk-of-the-work + #include "OPC_Common.h" + #include "OPC_MeshInterface.h" + // Builders + #include "OPC_TreeBuilders.h" + // Trees + #include "OPC_AABBTree.h" + #include "OPC_OptimizedTree.h" + // Models + #include "OPC_BaseModel.h" + #include "OPC_Model.h" + #include "OPC_HybridModel.h" + // Colliders + #include "OPC_Collider.h" + #include "OPC_VolumeCollider.h" + #include "OPC_TreeCollider.h" + #include "OPC_RayCollider.h" + #include "OPC_SphereCollider.h" + #include "OPC_OBBCollider.h" + #include "OPC_AABBCollider.h" + #include "OPC_LSSCollider.h" + #include "OPC_PlanesCollider.h" + // Usages + #include "OPC_Picking.h" + + + typedef void (*OPCODE_AbortHandler)(); + FUNCTION OPCODE_API bool InitOpcode(OPCODE_AbortHandler fnAbortHandler=NULL); + FUNCTION OPCODE_API bool CloseOpcode(); + } + +#endif // __OPCODE_H__ diff --git a/thirdparty/ode-0.16.5/OPCODE/README-ODE.txt b/thirdparty/ode-0.16.5/OPCODE/README-ODE.txt new file mode 100644 index 0000000..c5d5800 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/README-ODE.txt @@ -0,0 +1,13 @@ + +This is a copy of the OPCODE collision detection library by Pierre Terdiman. +See http://www.codercorner.com/Opcode.htm for more information, and read +the ReadMe.txt in this directory. + +If you want to use the TriList (triangle mesh) geometry class in ODE, the +OPCODE library must be compiled. If you are using the autotools support to +compile ODE, you just have to specify --with-trimesh=opcode when calling ./configure. + +This code was originally written for and compiled on windows, but it has been +ported so that it should compile under unix/gcc too. Your mileage may vary. + +Russ Smith, April 12 2005. diff --git a/thirdparty/ode-0.16.5/OPCODE/ReadMe.txt b/thirdparty/ode-0.16.5/OPCODE/ReadMe.txt new file mode 100644 index 0000000..8a39eff --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/ReadMe.txt @@ -0,0 +1,171 @@ + + OPCODE distribution 1.3 (june 2003) + ----------------------- + + New in Opcode 1.3: + - fixed the divide by 0 bug that was happening when all centers where located on a coordinate axis (thanks to Jorrit T) + - linearized "complete" vanilla AABB trees + - ANSI-compliant "for" loops (for the ones porting it to Linux...) + - callbacks & pointers moved to mesh interface + - support for triangle & vertex strides + - optimized the sphere-triangle overlap code a bit + - dynamic trees (refit) + - more builders + - ValidateSubdivision in builders + - LSS collider + - primitive-bv tests can now be skipped in most volume queries + - temporal coherence now also works for airborne objects + - temporal coherence completed for boxes / all contacts, LSS, etc + - ray-collider now uses a callback + - some common "usages" have been introduced (only picking for now) + - SPLIT_COMPLETE removed (now implicitely using mLimit = 1) + - hybrid collision models + - sweep-and-prune code added, moved from my old Z-Collide lib + - it now works with meshes made of only 1 triangle (except in mesh-mesh case!) + + Disclaimer: + + - I forced myself to actually *do* the release today no matter what. Else it would never have been done. That's + why the code may not be very polished. I also removed a *lot* of things (more usages, distance queries, etc...) + that weren't ready for prime-time (or that were linked to too many of my supporting libs) + + - Some comments may also be obsolete here and there. The old User Manual for Opcode 1.2 may not fit version 1.3 + either, since there's a new "mesh interface" to support strides, etc. + + - Everything in the "Ice" directory has been hacked out of my engine and edited until everything compiled. Don't + expect anything out there to be cute or something. In particular, some CPP files are not even included when not + needed, so you can expect some linker errors if you try messing around with them... + + Otherwise, it should be just like previous version, only better. In particular, hybrid models can be very + memory-friendly (sometimes using like 10 times less ram than the best trees from version 1.2). The possible + speed hit is often invisible (if it even exists), especially using temporal coherence in "all contacts" mode. + (Admittedly, this depends on your particular usage pattern / what you do on collided triangles). + + The sweep-and-prune code is similar to the "vanilla" version found in V-Collide (but that one's better IMHO...) + The simple "radix" version is often just as good, see for yourself. + + OPCODE distribution 1.2 (august 2002) + ----------------------- + + New in Opcode 1.2: + - new VolumeCollider base class + - simplified callback setup + - you can now use callbacks or pointers (setup at compile time) + - destination array not needed anymore in the RayCollider (faster in-out tests) + - renamed classes: AABBRayCollider => RayCollider, AABBSphereCollider => SphereCollider + - the sphere query now only returns a list of faces (extra info discarded). On the other hand it's a lot faster. + - OBB, AABB and planes queries. Original OBB and AABB queries contributed by Erwin de Vries. + - cosmetic changes in OPC_BoxBoxOverlap.h contributed by Gottfried Chen + - some inlining problems fixed + - faster ray-mesh tests using the separating axis theorem + - new split value in AABB tree construction (contributed by Igor Kravtchenko). Provides faster queries most of the time. + - improved temporal coherence for sphere & AABB queries (works in "All contacts" mode) + + Notes: + + - Everything in the "Ice code" directory (in VC++) is basically copy-pasted from my engine, with a lot + of code removed until there was no link error anymore. Don't expect those files to be cute or anything, + they've never been meant to be released and they're often updated/modified/messy. + - Some experimental features have been removed as well. Else I would never have released the 1.2... + - Not as polished/optimal as I would like it to be, but that's life. I promised myself to release it + before october 2002 (one YEAR later ?!).... That's the only reason why it's there. + - Some people reported ColDet was faster. Uh, come on. They were using Opcode in + "All contacts" mode whereas ColDet was doing "first contact"... + + OPCODE distribution 1.1 (october 2001) + ----------------------- + + New in Opcode 1.1: + - stabbing queries + - sphere queries + - abtract base class for colliders + - settings validation methods + - compilation flags now grouped in OPC_Settings.h + - smaller files, new VC++ virtual dirs (cleaner) + + Notes: + + - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info. + - I code in 1600*1200, so some lines may look a bit long.. + - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries + can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB + code, but the newer one seems better. Tim Schrder's one is good as well. See: www.codercorner.com/RayAABB.cpp + - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!) + - I removed various tests before releasing this one: + - a separation line, a.k.a. "front" in QuickCD, because gains were unclear + - distance queries in a PQP style, because it was way too slow + - support for deformable models, too slow as well + - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way. + If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current + demo uses copyrighted art I'm not allowed to spread) + - Sorry for the lack of real docs and/or solid examples. I just don't have enough time. + + OPCODE distribution 1.0 (march 2001) + ----------------------- + + - First release + + =============================================================================== + + WHAT ? + + OPCODE means OPtimized COllision DEtection. + So this is a collision detection package similar to RAPID. Here's a + quick list of features: + + - C++ interface, developed for Windows systems using VC++ 6.0 + - Works on arbitrary meshes (convex or non-convex), even polygon soups + - Current implementation uses AABB-trees + - Introduces Primitive-BV overlap tests during recursive collision queries (whereas + standard libraries only rely on Primitive-Primitive and BV-BV tests) + - Introduces no-leaf trees, i.e. collision trees whose leaf nodes have been removed + - Supports collision queries on quantized trees (decompressed on-the-fly) + - Supports "first contact" or "all contacts" modes ( la RAPID) + - Uses temporal coherence for "first contact" mode (~10 to 20 times faster, useful + in rigid body simulation during bisection) + - Memory footprint is 7.2 times smaller than RAPID's one, which is ideal for console + games with limited ram (actually, if you use the unmodified RAPID code using double + precision, it's more like 13 times smaller...) + - And yet it often runs faster than RAPID (according to RDTSC, sometimes more than 5 + times faster when objects are deeply overlapping) + - Performance is usually close to RAPID's one in close-proximity situations + - Stabbing, planes & volume queries (sphere, AABB, OBB, LSS) + - Sweep-and-prune + - Now works with deformable meshes + - Hybrid trees + + + What it can be used for: + - standard mesh-mesh collision detection (similar to RAPID, SOLID, QuickCD, PQP, ColDet...) + - N-body collisions (similar to V-Collide) + - camera-vs-world collisions (similar to Telemachos/Paul Nettle/Stan Melax articles) + - shadow feelers to speed up lightmap computations + - in-out tests to speed up voxelization processes + - picking + - rigid body simulation + - view frustum culling + - etc + + WHY ? + + - Because RAPID uses too many bytes. + - Because the idea was nice... + + WHEN ? + + It's been coded in march 2001 following a thread on the GD-Algorithms list. + + GDAlgorithms-list mailing list + GDAlgorithms-list@lists.sourceforge.net + http://lists.sourceforge.net/lists/listinfo/gdalgorithms-list + + WHO ? + + Pierre Terdiman + June, 1, 2003 + + p.terdiman@wanadoo.fr + p.terdiman@codercorner.com + + http://www.codercorner.com + http://www.codercorner.com/Opcode.htm diff --git a/thirdparty/ode-0.16.5/OPCODE/Stdafx.h b/thirdparty/ode-0.16.5/OPCODE/Stdafx.h new file mode 100644 index 0000000..0223a6c --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/Stdafx.h @@ -0,0 +1,24 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_) +#define AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +// Insert your headers here +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include "Opcode.h" + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__EFB95044_1D31_11D5_8B0F_0050BAC83302__INCLUDED_) diff --git a/thirdparty/ode-0.16.5/OPCODE/TemporalCoherence.txt b/thirdparty/ode-0.16.5/OPCODE/TemporalCoherence.txt new file mode 100644 index 0000000..fb85931 --- /dev/null +++ b/thirdparty/ode-0.16.5/OPCODE/TemporalCoherence.txt @@ -0,0 +1,32 @@ + +> Hi John, +> +> I know I'll forget to tell you this if I don't write it right now.... +> +> >(2) How is the receiving geometry for the shadow decided? +> +> I wrote about an LSS-test but actually performing a new VFC test (from the +> light's view) is the same. In both cases, here's a trick to take advantage +> of temporal coherence : test the world against a slightly larger than +> necessary LSS or frustum. Keep the list of touched surfaces. Then next +> frame, if the new volume is still contained within the previous one used +for +> the query, you can reuse the same list immediately. Actually it's a bit +> similar to what you did in your sphere-tree, I think. Anyway, now the +O(log +> N) VFC is O(1) for some frames. It's not worth it for the "real" VFC, but +> when you have N virtual frustum to test to drop N shadows, that's another +> story. +> +> Two downsides: +> - You need more ram to keep track of one list of meshes / shadow, but +> usually it's not a lot. +> - By using a larger volume for the query you possibly touch more +> faces/surfaces, which will be rendered in the shadow pass. Usually it's +not +> a problem either since rendering is simply faster than geometric queries +> those days. But of course, "your mileage may vary". +> +> Happy new year ! +> +> Pierre diff --git a/thirdparty/ode-0.16.5/README.md b/thirdparty/ode-0.16.5/README.md new file mode 100644 index 0000000..b28a3b5 --- /dev/null +++ b/thirdparty/ode-0.16.5/README.md @@ -0,0 +1,34 @@ +The Open Dynamics Engine (ODE) +============================== + +![ODE logo](http://bitbucket.org/odedevs/ode/raw/default/web/ODElogo.png) + +Copyright (C) 2001-2007 Russell L. Smith. + + +ODE is a free, industrial quality library for simulating articulated +rigid body dynamics - for example ground vehicles, legged creatures, +and moving objects in VR environments. It is fast, flexible, robust +and platform independent, with advanced joints, contact with friction, +and built-in collision detection. + +This library is free software; you can redistribute it and/or +modify it under the terms of EITHER: + + * The GNU Lesser General Public License version 2.1 or any later. + + * The BSD-style License. + +See the [COPYING](http://bitbucket.org/odedevs/ode/raw/default/COPYING) file for more details. + + * Installation instructions are in the [INSTALL.txt](http://bitbucket.org/odedevs/ode/raw/default/INSTALL.txt) file. + + * The ODE web pages are at [ode.org](http://www.ode.org/). + + * An online manual is at [the Wiki](http://ode-wiki.org/wiki/index.php?title=Manual). + + * API documentation is in the file ode/docs/index.html, or you + can view it on the web at [opende.sf.net/docs/index.html](http://opende.sf.net/docs/index.html). + + * Coding style requirements can be found in the [CSR.txt](http://bitbucket.org/odedevs/ode/raw/default/CSR.txt) file. + diff --git a/thirdparty/ode-0.16.5/aclocal.m4 b/thirdparty/ode-0.16.5/aclocal.m4 new file mode 100644 index 0000000..df157e5 --- /dev/null +++ b/thirdparty/ode-0.16.5/aclocal.m4 @@ -0,0 +1,1158 @@ +# generated automatically by aclocal 1.15 -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/libtool.m4]) +m4_include([m4/ltoptions.m4]) +m4_include([m4/ltsugar.m4]) +m4_include([m4/ltversion.m4]) +m4_include([m4/lt~obsolete.m4]) +m4_include([m4/pkg.m4]) diff --git a/thirdparty/ode-0.16.5/bindings/python/INSTALL.txt b/thirdparty/ode-0.16.5/bindings/python/INSTALL.txt new file mode 100644 index 0000000..27325dd --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/INSTALL.txt @@ -0,0 +1,64 @@ +1. REQUIREMENTS: + +1. Python 2.4 or higher (http://www.python.org/) + - Tested with Python 2.7 (2.6 on earlier builds) +2. Cython 0.14.1** or higher (http://cython.org/) + - Tested with Cython 0.15 (0.14.1 on earlier builds) +3. ODE shared*** library (or static with -fPIC) + - See the notes on building ODE below. +4. pkg-config (http://www.freedesktop.org/wiki/Software/pkg-config) + - Windows executable (and GLib dependency) can be downloaded from + http://www.gtk.org/download/win32.php + - If you used premake to configure ODE, you may need to create an ode.pc file + in your PKG_CONFIG_PATH manually. See /ode.pc.in + + + +2. BUILDING ODE + + On certain systems (*nix) there is a requirement that a shared library + (such as the python module) contains only position-independent code + (PIC). In those cases, ODE needs to be either compiled as a shared library + too (--enable-shared), or as a static library with PIC (-fPIC). + + Once ODE is built and installed in your desired destination, proceed with + building the wrapper. + + + +3a. BUILDING WITH Visual Studio (Windows) + + python setup.py build_ext + + +3b. BUILDING WITH MINGW (Windows) + + python setup.py build_ext -c mingw32 + + +3c. BUILDING WITH GCC/G++ (Linux, OS X, etc.) + + python setup.py build_ext + + + +4. INSTALLATION + +4a. For system-wide installation (needs administrator privileges): + + python setup.py install + +4b. For user installation: + + python setup.py install --user + + + +5. DEMOS and DOCUMENTATION + + Try running the tutorials in the 'demos' directory. The tutorials were taken + from the PyODE website (http://pyode.sourceforge.net/). + + For usage documentation, please refer to the PyODE API documentation at + http://pyode.sourceforge.net/api-1.2.0/index.html. + diff --git a/thirdparty/ode-0.16.5/bindings/python/TODO.txt b/thirdparty/ode-0.16.5/bindings/python/TODO.txt new file mode 100644 index 0000000..67aa2bb --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/TODO.txt @@ -0,0 +1,15 @@ +CODE: +* (setup.py) add package information (version, authors, etc.) +* (setup.py) add 'install' action +* (setup.py) add configurable ODE DLL (currently hard-coded to default single precision) +* (ode.pxd) clean up, add more comments +* (ode.pyx) refactor for a more Pythonic implementation (e.g. replace getters and setters with + properties)? +* (?) Add option to build bindings in ODE's makefiles + + +DOCS: + +* Update and include API docs from PyODE +* Adapt and include PyODE tutorials/demos +* Update license text in ode.pxd and ode.pyx diff --git a/thirdparty/ode-0.16.5/bindings/python/demos/tutorial1.py b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial1.py new file mode 100644 index 0000000..c4266d4 --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial1.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +# http://pyode.sourceforge.net/tutorials/tutorial1.html + +# pyODE example 1: Getting started + +# modified by Gideon Klompje (removed literals and using +# 'ode.Mass.setSphereTotal' instead of 'ode.Mass.setSphere') + +import ode + +# Simulation constants +GRAVITY = (0, -9.81, 0) + +SPHERE_RADIUS = 0.05 +SPHERE_MASS = 1.0 +SPHERE_START_POS = (0, 2, 0) +SPHERE_FORCE = (0, 200, 0) # Initial force to apply to the sphere + +TIME_STEP = 0.04 +TIME_STOP = 2.0 # When to stop the simulation + +# Create a world object +world = ode.World() +world.setGravity(GRAVITY) + +# Create a spherical body inside the world +body = ode.Body(world) +mass = ode.Mass() +mass.setSphereTotal(SPHERE_MASS, SPHERE_RADIUS) +body.setMass(mass) + +body.setPosition(SPHERE_START_POS) +body.addForce(SPHERE_FORCE) + +# Do the simulation... +if __name__ == "__main__": + total_time = 0.0 + while total_time < TIME_STOP: + # output the body's position and velocity + x, y, z = body.getPosition() + u, v, w = body.getLinearVel() + print "%1.2fsec: pos=(%6.3f, %6.3f, %6.3f) vel=(%6.3f, %6.3f, %6.3f)" % \ + (total_time, x, y, z, u, v, w) + + # advance the simulation + world.step(TIME_STEP) + total_time += TIME_STEP + diff --git a/thirdparty/ode-0.16.5/bindings/python/demos/tutorial2.py b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial2.py new file mode 100644 index 0000000..1a4a228 --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial2.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python + +# http://pyode.sourceforge.net/tutorials/tutorial2.html + +# pyODE example 2: Connecting bodies with joints + +# modified by Gideon Klompje (removed literals and using +# 'ode.Mass.setSphereTotal' instead of 'ode.Mass.setSphere') + + +import ode +import pygame + +from pygame.locals import QUIT, KEYDOWN + +# Constants +WINDOW_RESOLUTION = (640, 480) + +DRAW_SCALE = WINDOW_RESOLUTION[0] / 5 +"""Factor to multiply physical coordinates by to obtain screen size in pixels""" + +DRAW_OFFSET = (WINDOW_RESOLUTION[0] / 2, 50) +"""Screen coordinates (in pixels) that map to the physical origin (0, 0, 0)""" + +BACKGROUND_COLOR = (255, 255, 255) + +GRAVITY = (0, -9.81, 0) + +SPHERE1_POSITION = (1, 0, 0) +SPHERE1_MASS = 1 +SPHERE1_RADIUS = 0.15 +SPHERE1_COLOR = (55, 0, 200) + +SPHERE2_POSITION = (2, 0, 0) +SPHERE2_MASS = 1 +SPHERE2_RADIUS = 0.15 +SPHERE2_COLOR = (55, 0, 200) + +JOINT1_ANCHOR = (0, 0, 0) +JOINT1_COLOR = (200, 0, 55) +JOINT1_WIDTH = 2 +"""Width of the line (in pixels) representing the joint""" + +JOINT2_ANCHOR = SPHERE1_POSITION +JOINT2_COLOR = (200, 0, 55) +JOINT2_WIDTH = 2 +"""Width of the line (in pixels) representing the joint""" + +TIME_STEP = 0.04 + +# Utility functions +def coord(x, y, integer=False): + """ + Convert world coordinates to pixel coordinates. Setting 'integer' to + True will return integer coordinates. + """ + xs = (DRAW_OFFSET[0] + DRAW_SCALE*x) + ys = (DRAW_OFFSET[1] - DRAW_SCALE*y) + + if integer: + return int(round(xs)), int(round(ys)) + else: + return xs, ys + +# Initialize pygame +pygame.init() + +# Open a display +screen = pygame.display.set_mode(WINDOW_RESOLUTION) + +# Create a world object +world = ode.World() +world.setGravity(GRAVITY) + +# Create two bodies +body1 = ode.Body(world) +M = ode.Mass() +M.setSphereTotal(SPHERE1_MASS, SPHERE1_RADIUS) +body1.setMass(M) +body1.setPosition(SPHERE1_POSITION) + +body2 = ode.Body(world) +M = ode.Mass() +M.setSphereTotal(SPHERE2_MASS, SPHERE2_RADIUS) +body2.setMass(M) +body2.setPosition(SPHERE2_POSITION) + +# Connect body1 with the static environment +j1 = ode.BallJoint(world) +j1.attach(body1, ode.environment) +j1.setAnchor(JOINT1_ANCHOR) + +# Connect body2 with body1 +j2 = ode.BallJoint(world) +j2.attach(body1, body2) +j2.setAnchor(JOINT2_ANCHOR) + +# Simulation loop... +if __name__ == "__main__": + fps = 1.0 / TIME_STEP + clk = pygame.time.Clock() + + sph1_rad = int(DRAW_SCALE * SPHERE1_RADIUS) + sph2_rad = int(DRAW_SCALE * SPHERE2_RADIUS) + + loopFlag = True + while loopFlag: + for e in pygame.event.get(): + if e.type==QUIT: + loopFlag=False + if e.type==KEYDOWN: + loopFlag=False + + # Clear the screen + screen.fill(BACKGROUND_COLOR) + + # Draw the two bodies and the lines representing the joints + x1, y1, z1 = body1.getPosition() + x2, y2, z2 = body2.getPosition() + xj1, yj1, zj1 = j1.getAnchor() + xj2, yj2, zj2 = j2.getAnchor() + + pygame.draw.line(screen, JOINT1_COLOR, coord(xj1, yj1), coord(x1, y1), JOINT1_WIDTH) + pygame.draw.line(screen, JOINT2_COLOR, coord(xj2, yj2), coord(x2, y2), JOINT2_WIDTH) + pygame.draw.circle(screen, SPHERE1_COLOR, coord(x1, y1, integer=True), sph1_rad, 0) + pygame.draw.circle(screen, SPHERE2_COLOR, coord(x2, y2, integer=True), sph2_rad, 0) + + pygame.display.flip() + + # Next simulation step + world.step(TIME_STEP) + + # Try to keep the specified framerate + clk.tick(fps) + diff --git a/thirdparty/ode-0.16.5/bindings/python/demos/tutorial3.py b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial3.py new file mode 100644 index 0000000..2644d62 --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/demos/tutorial3.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python + +# http://pyode.sourceforge.net/tutorials/tutorial3.html + +# pyODE example 3: Collision detection + +# Originally by Matthias Baas. +# Updated by Pierre Gay to work without pygame or cgkit. + +import sys, os, random, time +from math import * +from OpenGL.GL import * +from OpenGL.GLU import * +from OpenGL.GLUT import * + +import ode + +# geometric utility functions +def scalp (vec, scal): + vec[0] *= scal + vec[1] *= scal + vec[2] *= scal + +def length (vec): + return sqrt (vec[0]**2 + vec[1]**2 + vec[2]**2) + +# prepare_GL +def prepare_GL(): + """Prepare drawing. + """ + + # Viewport + glViewport(0,0,640,480) + + # Initialize + glClearColor(0.8,0.8,0.9,0) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST) + glDisable(GL_LIGHTING) + glEnable(GL_LIGHTING) + glEnable(GL_NORMALIZE) + glShadeModel(GL_FLAT) + + # Projection + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + gluPerspective (45,1.3333,0.2,20) + + # Initialize ModelView matrix + glMatrixMode(GL_MODELVIEW) + glLoadIdentity() + + # Light source + glLightfv(GL_LIGHT0,GL_POSITION,[0,0,1,0]) + glLightfv(GL_LIGHT0,GL_DIFFUSE,[1,1,1,1]) + glLightfv(GL_LIGHT0,GL_SPECULAR,[1,1,1,1]) + glEnable(GL_LIGHT0) + + # View transformation + gluLookAt (2.4, 3.6, 4.8, 0.5, 0.5, 0, 0, 1, 0) + +# draw_body +def draw_body(body): + """Draw an ODE body. + """ + + x,y,z = body.getPosition() + R = body.getRotation() + rot = [R[0], R[3], R[6], 0., + R[1], R[4], R[7], 0., + R[2], R[5], R[8], 0., + x, y, z, 1.0] + glPushMatrix() + glMultMatrixd(rot) + if body.shape=="box": + sx,sy,sz = body.boxsize + glScalef(sx, sy, sz) + glutSolidCube(1) + glPopMatrix() + + +# create_box +def create_box(world, space, density, lx, ly, lz): + """Create a box body and its corresponding geom.""" + + # Create body + body = ode.Body(world) + M = ode.Mass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + + # Set parameters for drawing the body + body.shape = "box" + body.boxsize = (lx, ly, lz) + + # Create a box geom for collision detection + geom = ode.GeomBox(space, lengths=body.boxsize) + geom.setBody(body) + + return body, geom + +# drop_object +def drop_object(): + """Drop an object into the scene.""" + + global bodies, geom, counter, objcount + + body, geom = create_box(world, space, 1000, 1.0,0.2,0.2) + body.setPosition( (random.gauss(0,0.1),3.0,random.gauss(0,0.1)) ) + theta = random.uniform(0,2*pi) + ct = cos (theta) + st = sin (theta) + body.setRotation([ct, 0., -st, 0., 1., 0., st, 0., ct]) + bodies.append(body) + geoms.append(geom) + counter=0 + objcount+=1 + +# explosion +def explosion(): + """Simulate an explosion. + + Every object is pushed away from the origin. + The force is dependent on the objects distance from the origin. + """ + global bodies + + for b in bodies: + l=b.getPosition () + d = length (l) + a = max(0, 40000*(1.0-0.2*d*d)) + l = [l[0] / 4, l[1], l[2] /4] + scalp (l, a / length (l)) + b.addForce(l) + +# pull +def pull(): + """Pull the objects back to the origin. + + Every object will be pulled back to the origin. + Every couple of frames there'll be a thrust upwards so that + the objects won't stick to the ground all the time. + """ + global bodies, counter + + for b in bodies: + l=list (b.getPosition ()) + scalp (l, -1000 / length (l)) + b.addForce(l) + if counter%60==0: + b.addForce((0,10000,0)) + +# Collision callback +def near_callback(args, geom1, geom2): + """Callback function for the collide() method. + + This function checks if the given geoms do collide and + creates contact joints if they do. + """ + + # Check if the objects do collide + contacts = ode.collide(geom1, geom2) + + # Create contact joints + world,contactgroup = args + for c in contacts: + c.setBounce(0.2) + c.setMu(5000) + j = ode.ContactJoint(world, contactgroup, c) + j.attach(geom1.getBody(), geom2.getBody()) + + + +###################################################################### + +# Initialize Glut +glutInit ([]) + +# Open a window +glutInitDisplayMode (GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE) + +x = 0 +y = 0 +width = 640 +height = 480 +glutInitWindowPosition (x, y); +glutInitWindowSize (width, height); +glutCreateWindow ("testode") + +# Create a world object +world = ode.World() +world.setGravity( (0,-9.81,0) ) +world.setERP(0.8) +world.setCFM(1E-5) + +# Create a space object +space = ode.Space() + +# Create a plane geom which prevent the objects from falling forever +floor = ode.GeomPlane(space, (0,1,0), 0) + +# A list with ODE bodies +bodies = [] + +# The geoms for each of the bodies +geoms = [] + +# A joint group for the contact joints that are generated whenever +# two bodies collide +contactgroup = ode.JointGroup() + +# Some variables used inside the simulation loop +fps = 50 +dt = 1.0/fps +running = True +state = 0 +counter = 0 +objcount = 0 +lasttime = time.time() + + +# keyboard callback +def _keyfunc (c, x, y): + sys.exit (0) + +glutKeyboardFunc (_keyfunc) + +# draw callback +def _drawfunc (): + # Draw the scene + prepare_GL() + for b in bodies: + draw_body(b) + + glutSwapBuffers () + +glutDisplayFunc (_drawfunc) + +# idle callback +def _idlefunc (): + global counter, state, lasttime + + t = dt - (time.time() - lasttime) + if (t > 0): + time.sleep(t) + + counter += 1 + + if state==0: + if counter==20: + drop_object() + if objcount==30: + state=1 + counter=0 + # State 1: Explosion and pulling back the objects + elif state==1: + if counter==100: + explosion() + if counter>300: + pull() + if counter==500: + counter=20 + + glutPostRedisplay () + + # Simulate + n = 4 + + for i in range(n): + # Detect collisions and create contact joints + space.collide((world,contactgroup), near_callback) + + # Simulation step + world.step(dt/n) + + # Remove all contact joints + contactgroup.empty() + + lasttime = time.time() + +glutIdleFunc (_idlefunc) + +glutMainLoop () + diff --git a/thirdparty/ode-0.16.5/bindings/python/ode.pxd b/thirdparty/ode-0.16.5/bindings/python/ode.pxd new file mode 100644 index 0000000..2b33643 --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/ode.pxd @@ -0,0 +1,503 @@ +###################################################################### +# Python Open Dynamics Engine Wrapper +# Copyright (C) 2004 PyODE developers (see file AUTHORS) +# All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of EITHER: +# (1) The GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. The text of the GNU Lesser +# General Public License is included with this library in the +# file LICENSE. +# (2) The BSD-style license that is included with this library in +# the file LICENSE-BSD. +# +# 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 files +# LICENSE and LICENSE-BSD for more details. +###################################################################### + +cdef extern from "stdlib.h": + + void* malloc(long) + void free(void*) + +cdef extern from "stdio.h": + int printf(char*) + +# Include the basic floating point type -> dReal (either float or double) +#include "_precision.pyx" + +cdef extern from "ode/ode.h": + + ctypedef double dReal + + # Dummy structs + cdef struct dxWorld: + int _dummy + cdef struct dxSpace: + int _dummy + cdef struct dxBody: + int _dummy + cdef struct dxGeom: + int _dummy + cdef struct dxJoint: + int _dummy + cdef struct dxJointGroup: + int _dummy + cdef struct dxTriMeshData: + int _dummy + cdef struct dxHeightfieldData: + int _dummy + + # Types + ctypedef dxWorld* dWorldID + ctypedef dxSpace* dSpaceID + ctypedef dxBody* dBodyID + ctypedef dxGeom* dGeomID + ctypedef dxJoint* dJointID + ctypedef dxJointGroup* dJointGroupID + ctypedef dxTriMeshData* dTriMeshDataID + ctypedef dxHeightfieldData* dHeightfieldDataID + ctypedef dReal dVector3[4] + ctypedef dReal dVector4[4] + ctypedef dReal dMatrix3[4*3] + ctypedef dReal dMatrix4[4*4] + ctypedef dReal dMatrix6[8*6] + ctypedef dReal dQuaternion[4] + + cdef extern dReal dInfinity + cdef extern int dAMotorUser + cdef extern int dAMotorEuler + + ctypedef struct dMass: + dReal mass + dVector4 c + dMatrix3 I + + ctypedef struct dJointFeedback: + dVector3 f1 + dVector3 t1 + dVector3 f2 + dVector3 t2 + + ctypedef void dNearCallback(void* data, dGeomID o1, dGeomID o2) + ctypedef dReal dHeightfieldGetHeight( void* p_user_data, int x, int z ) + + ctypedef struct dSurfaceParameters: + int mode + dReal mu + + dReal mu2 + dReal bounce + dReal bounce_vel + dReal soft_erp + dReal soft_cfm + dReal motion1,motion2 + dReal slip1,slip2 + + ctypedef struct dContactGeom: + dVector3 pos + dVector3 normal + dReal depth + dGeomID g1,g2 + + ctypedef struct dContact: + dSurfaceParameters surface + dContactGeom geom + dVector3 fdir1 + + + # World + dWorldID dWorldCreate() + void dWorldDestroy (dWorldID) + + void dCloseODE() + void dInitODE() + + void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z) + void dWorldGetGravity (dWorldID, dVector3 gravity) + void dWorldSetERP (dWorldID, dReal erp) + dReal dWorldGetERP (dWorldID) + void dWorldSetCFM (dWorldID, dReal cfm) + dReal dWorldGetCFM (dWorldID) + void dWorldStep (dWorldID, dReal stepsize) + void dWorldQuickStep (dWorldID, dReal stepsize) + void dWorldSetQuickStepNumIterations (dWorldID, int num) + int dWorldGetQuickStepNumIterations (dWorldID) + void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel) + dReal dWorldGetContactMaxCorrectingVel (dWorldID) + void dWorldSetContactSurfaceLayer (dWorldID, dReal depth) + dReal dWorldGetContactSurfaceLayer (dWorldID) + void dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable) + int dWorldGetAutoDisableFlag (dWorldID) + void dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_threshold) + dReal dWorldGetAutoDisableLinearThreshold (dWorldID) + void dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_threshold) + dReal dWorldGetAutoDisableAngularThreshold (dWorldID) + void dWorldSetAutoDisableSteps (dWorldID, int steps) + int dWorldGetAutoDisableSteps (dWorldID) + void dWorldSetAutoDisableTime (dWorldID, dReal time) + dReal dWorldGetAutoDisableTime (dWorldID) + dReal dWorldGetLinearDamping (dWorldID) + void dWorldSetLinearDamping (dWorldID, dReal scale) + dReal dWorldGetAngularDamping (dWorldID) + void dWorldSetAngularDamping (dWorldID, dReal scale) + void dWorldImpulseToForce (dWorldID, dReal stepsize, + dReal ix, dReal iy, dReal iz, dVector3 force) + + # Body + dBodyID dBodyCreate (dWorldID) + void dBodyDestroy (dBodyID) + + void dBodySetData (dBodyID, void *data) + void *dBodyGetData (dBodyID) + + void dBodySetPosition (dBodyID, dReal x, dReal y, dReal z) + void dBodySetRotation (dBodyID, dMatrix3 R) + void dBodySetQuaternion (dBodyID, dQuaternion q) + void dBodySetLinearVel (dBodyID, dReal x, dReal y, dReal z) + void dBodySetAngularVel (dBodyID, dReal x, dReal y, dReal z) + dReal * dBodyGetPosition (dBodyID) + dReal * dBodyGetRotation (dBodyID) + dReal * dBodyGetQuaternion (dBodyID) + dReal * dBodyGetLinearVel (dBodyID) + dReal * dBodyGetAngularVel (dBodyID) + + void dBodySetMass (dBodyID, dMass *mass) + void dBodyGetMass (dBodyID, dMass *mass) + + void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz) + void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz) + void dBodyAddRelForce (dBodyID, dReal fx, dReal fy, dReal fz) + void dBodyAddRelTorque (dBodyID, dReal fx, dReal fy, dReal fz) + void dBodyAddForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz) + void dBodyAddForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz) + void dBodyAddRelForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz) + void dBodyAddRelForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz) + + dReal * dBodyGetForce (dBodyID) + dReal * dBodyGetTorque (dBodyID) + + void dBodySetForce(dBodyID, dReal x, dReal y, dReal z) + void dBodySetTorque(dBodyID, dReal x, dReal y, dReal z) + + void dBodyGetRelPointPos (dBodyID, dReal px, dReal py, dReal pz, dVector3 result) + void dBodyGetRelPointVel (dBodyID, dReal px, dReal py, dReal pz, dVector3 result) + void dBodyGetPointVel (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result) + void dBodyGetPosRelPoint (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result) + void dBodyVectorToWorld (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result) + void dBodyVectorFromWorld (dBodyID, dReal px, dReal py, dReal pz, + dVector3 result) + + void dBodySetFiniteRotationMode (dBodyID, int mode) + void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z) + + int dBodyGetFiniteRotationMode (dBodyID) + void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result) + + int dBodyGetNumJoints (dBodyID b) + dJointID dBodyGetJoint (dBodyID, int index) + + void dBodyEnable (dBodyID) + void dBodyDisable (dBodyID) + int dBodyIsEnabled (dBodyID) + + void dBodySetGravityMode (dBodyID b, int mode) + int dBodyGetGravityMode (dBodyID b) + + void dBodySetDynamic (dBodyID) + void dBodySetKinematic (dBodyID) + int dBodyIsKinematic (dBodyID) + + void dBodySetMaxAngularSpeed (dBodyID, dReal max_speed) + + # Joints + dJointID dJointCreateBall (dWorldID, dJointGroupID) + dJointID dJointCreateHinge (dWorldID, dJointGroupID) + dJointID dJointCreateSlider (dWorldID, dJointGroupID) + dJointID dJointCreateContact (dWorldID, dJointGroupID, dContact *) + dJointID dJointCreateUniversal (dWorldID, dJointGroupID) + dJointID dJointCreatePR (dWorldID, dJointGroupID) + dJointID dJointCreateHinge2 (dWorldID, dJointGroupID) + dJointID dJointCreateFixed (dWorldID, dJointGroupID) + dJointID dJointCreateNull (dWorldID, dJointGroupID) + dJointID dJointCreateAMotor (dWorldID, dJointGroupID) + dJointID dJointCreateLMotor (dWorldID, dJointGroupID) + dJointID dJointCreatePlane2D (dWorldID, dJointGroupID) + + void dJointDestroy (dJointID) + + void dJointEnable (dJointID) + void dJointDisable (dJointID) + int dJointIsEnabled (dJointID) + + dJointGroupID dJointGroupCreate (int max_size) + void dJointGroupDestroy (dJointGroupID) + void dJointGroupEmpty (dJointGroupID) + + void dJointAttach (dJointID, dBodyID body1, dBodyID body2) + void dJointSetData (dJointID, void *data) + void *dJointGetData (dJointID) + int dJointGetType (dJointID) + dBodyID dJointGetBody (dJointID, int index) + + void dJointSetBallAnchor (dJointID, dReal x, dReal y, dReal z) + void dJointSetHingeAnchor (dJointID, dReal x, dReal y, dReal z) + void dJointSetHingeAxis (dJointID, dReal x, dReal y, dReal z) + void dJointSetHingeParam (dJointID, int parameter, dReal value) + void dJointAddHingeTorque(dJointID joint, dReal torque) + void dJointSetSliderAxis (dJointID, dReal x, dReal y, dReal z) + void dJointSetSliderParam (dJointID, int parameter, dReal value) + void dJointAddSliderForce(dJointID joint, dReal force) + void dJointSetHinge2Anchor (dJointID, dReal x, dReal y, dReal z) + void dJointSetHinge2Axis1 (dJointID, dReal x, dReal y, dReal z) + void dJointSetHinge2Axis2 (dJointID, dReal x, dReal y, dReal z) + void dJointSetHinge2Param (dJointID, int parameter, dReal value) + void dJointAddHinge2Torques(dJointID joint, dReal torque1, dReal torque2) + void dJointSetUniversalAnchor (dJointID, dReal x, dReal y, dReal z) + void dJointSetUniversalAxis1 (dJointID, dReal x, dReal y, dReal z) + void dJointSetUniversalAxis2 (dJointID, dReal x, dReal y, dReal z) + void dJointSetUniversalParam (dJointID, int parameter, dReal value) + void dJointAddUniversalTorques(dJointID joint, dReal torque1, dReal torque2) + void dJointSetFixed (dJointID) + void dJointSetAMotorNumAxes (dJointID, int num) + void dJointSetAMotorAxis (dJointID, int anum, int rel, dReal x, dReal y, dReal z) + void dJointSetAMotorAngle (dJointID, int anum, dReal angle) + void dJointSetAMotorParam (dJointID, int parameter, dReal value) + void dJointSetAMotorMode (dJointID, int mode) + void dJointAddAMotorTorques (dJointID, dReal torque1, dReal torque2, dReal torque3) + void dJointSetLMotorAxis (dJointID, int anum, int rel, dReal x, dReal y, dReal z) + void dJointSetLMotorNumAxes (dJointID, int num) + void dJointSetLMotorParam (dJointID, int parameter, dReal value) + + void dJointGetBallAnchor (dJointID, dVector3 result) + void dJointGetBallAnchor2 (dJointID, dVector3 result) + void dJointGetHingeAnchor (dJointID, dVector3 result) + void dJointGetHingeAnchor2 (dJointID, dVector3 result) + void dJointGetHingeAxis (dJointID, dVector3 result) + dReal dJointGetHingeParam (dJointID, int parameter) + dReal dJointGetHingeAngle (dJointID) + dReal dJointGetHingeAngleRate (dJointID) + dReal dJointGetSliderPosition (dJointID) + dReal dJointGetSliderPositionRate (dJointID) + void dJointGetSliderAxis (dJointID, dVector3 result) + dReal dJointGetSliderParam (dJointID, int parameter) + void dJointGetHinge2Anchor (dJointID, dVector3 result) + void dJointGetHinge2Anchor2 (dJointID, dVector3 result) + void dJointGetHinge2Axis1 (dJointID, dVector3 result) + void dJointGetHinge2Axis2 (dJointID, dVector3 result) + dReal dJointGetHinge2Param (dJointID, int parameter) + dReal dJointGetHinge2Angle1 (dJointID) + dReal dJointGetHinge2Angle1Rate (dJointID) + dReal dJointGetHinge2Angle2Rate (dJointID) + void dJointGetUniversalAnchor (dJointID, dVector3 result) + void dJointGetUniversalAnchor2 (dJointID, dVector3 result) + void dJointGetUniversalAxis1 (dJointID, dVector3 result) + void dJointGetUniversalAxis2 (dJointID, dVector3 result) + dReal dJointGetUniversalParam (dJointID, int parameter) + dReal dJointGetUniversalAngle1 (dJointID) + dReal dJointGetUniversalAngle2 (dJointID) + dReal dJointGetUniversalAngle1Rate (dJointID) + dReal dJointGetUniversalAngle2Rate (dJointID) + int dJointGetAMotorNumAxes (dJointID) + void dJointGetAMotorAxis (dJointID, int anum, dVector3 result) + int dJointGetAMotorAxisRel (dJointID, int anum) + dReal dJointGetAMotorAngle (dJointID, int anum) + dReal dJointGetAMotorAngleRate (dJointID, int anum) + dReal dJointGetAMotorParam (dJointID, int parameter) + int dJointGetAMotorMode (dJointID) + int dJointGetLMotorNumAxes (dJointID) + void dJointGetLMotorAxis (dJointID, int anum, dVector3 result) + dReal dJointGetLMotorParam (dJointID, int parameter) + void dJointSetPlane2DXParam (dJointID, int parameter, dReal value) + void dJointSetPlane2DYParam (dJointID, int parameter, dReal value) + void dJointSetPlane2DAngleParam (dJointID, int parameter, dReal value) + dReal dJointGetPRPosition (dJointID j) + void dJointSetPRAnchor (dJointID j, dReal x, dReal y, dReal z) + void dJointSetPRAxis1 (dJointID j, dReal x, dReal y, dReal z) + void dJointSetPRAxis2 (dJointID j, dReal x, dReal y, dReal z) + void dJointGetPRAnchor (dJointID j, dVector3 result) + void dJointGetPRAxis1 (dJointID j, dVector3 result) + void dJointGetPRAxis2 (dJointID j, dVector3 result) + + void dJointSetFeedback (dJointID, dJointFeedback *) + dJointFeedback *dJointGetFeedback (dJointID) + + int dAreConnected (dBodyID, dBodyID) + + # Mass + void dMassSetZero (dMass *) + void dMassSetParameters (dMass *, dReal themass, + dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23) + void dMassSetSphere (dMass *, dReal density, dReal radius) + void dMassSetSphereTotal (dMass *, dReal total_mass, dReal radius) + void dMassSetCapsule (dMass *, dReal density, int direction, dReal radius, dReal length) + void dMassSetCapsuleTotal (dMass *, dReal total_mass, int direction, dReal radius, dReal length) + void dMassSetCylinder (dMass *, dReal density, int direction, + dReal radius, dReal length) + void dMassSetCylinderTotal (dMass *, dReal total_mass, int direction, + dReal radius, dReal length) + void dMassSetBox (dMass *, dReal density, + dReal lx, dReal ly, dReal lz) + void dMassSetBoxTotal (dMass *, dReal total_mass, + dReal lx, dReal ly, dReal lz) + void dMassAdjust (dMass *, dReal newmass) + void dMassTranslate (dMass *, dReal x, dReal y, dReal z) + void dMassRotate (dMass *, dMatrix3 R) + void dMassAdd (dMass *a, dMass *b) + + # Space +# dSpaceID dSimpleSpaceCreate(int space) +# dSpaceID dHashSpaceCreate(int space) + dSpaceID dSimpleSpaceCreate(dSpaceID space) + dSpaceID dHashSpaceCreate(dSpaceID space) + dSpaceID dQuadTreeSpaceCreate (dSpaceID space, dVector3 Center, + dVector3 Extents, int Depth) + + void dSpaceDestroy (dSpaceID) + void dSpaceAdd (dSpaceID, dGeomID) + void dSpaceRemove (dSpaceID, dGeomID) + int dSpaceQuery (dSpaceID, dGeomID) + void dSpaceCollide (dSpaceID space, void *data, dNearCallback *callback) + void dSpaceCollide2 (dGeomID o1, dGeomID o2, void *data, dNearCallback *callback) + + void dHashSpaceSetLevels (dSpaceID space, int minlevel, int maxlevel) + void dHashSpaceGetLevels (dSpaceID space, int *minlevel, int *maxlevel) + + void dSpaceSetCleanup (dSpaceID space, int mode) + int dSpaceGetCleanup (dSpaceID space) + + int dSpaceGetNumGeoms (dSpaceID) + dGeomID dSpaceGetGeom (dSpaceID, int i) + + # Geom + dGeomID dCreateSphere (dSpaceID space, dReal radius) + dGeomID dCreateBox (dSpaceID space, dReal lx, dReal ly, dReal lz) + dGeomID dCreatePlane (dSpaceID space, dReal a, dReal b, dReal c, dReal d) + dGeomID dCreateCapsule (dSpaceID space, dReal radius, dReal length) + dGeomID dCreateCylinder (dSpaceID space, dReal radius, dReal length) + dGeomID dCreateGeomGroup (dSpaceID space) + + void dGeomSphereSetRadius (dGeomID sphere, dReal radius) + void dGeomBoxSetLengths (dGeomID box, dReal lx, dReal ly, dReal lz) + void dGeomPlaneSetParams (dGeomID plane, dReal a, dReal b, dReal c, dReal d) + void dGeomCapsuleSetParams (dGeomID ccylinder, dReal radius, dReal length) + void dGeomCylinderSetParams (dGeomID ccylinder, dReal radius, dReal length) + + dReal dGeomSphereGetRadius (dGeomID sphere) + void dGeomBoxGetLengths (dGeomID box, dVector3 result) + void dGeomPlaneGetParams (dGeomID plane, dVector4 result) + void dGeomCapsuleGetParams (dGeomID ccylinder, dReal *radius, dReal *length) + void dGeomCylinderGetParams (dGeomID ccylinder, dReal *radius, dReal *length) + + dReal dGeomSpherePointDepth (dGeomID sphere, dReal x, dReal y, dReal z) + dReal dGeomBoxPointDepth (dGeomID box, dReal x, dReal y, dReal z) + dReal dGeomPlanePointDepth (dGeomID plane, dReal x, dReal y, dReal z) + dReal dGeomCapsulePointDepth (dGeomID ccylinder, dReal x, dReal y, dReal z) + + dGeomID dCreateRay (dSpaceID space, dReal length) + void dGeomRaySetLength (dGeomID ray, dReal length) + dReal dGeomRayGetLength (dGeomID ray) + void dGeomRaySet (dGeomID ray, dReal px, dReal py, dReal pz, + dReal dx, dReal dy, dReal dz) + void dGeomRayGet (dGeomID ray, dVector3 start, dVector3 dir) + + void dGeomSetData (dGeomID, void *) + void *dGeomGetData (dGeomID) + void dGeomSetBody (dGeomID, dBodyID) + dBodyID dGeomGetBody (dGeomID) + void dGeomSetPosition (dGeomID, dReal x, dReal y, dReal z) + void dGeomSetRotation (dGeomID, dMatrix3 R) + void dGeomSetQuaternion (dGeomID, dQuaternion) + dReal * dGeomGetPosition (dGeomID) + dReal * dGeomGetRotation (dGeomID) + void dGeomGetQuaternion (dGeomID, dQuaternion result) + void dGeomSetOffsetPosition (dGeomID, dReal x, dReal y, dReal z) + void dGeomSetOffsetRotation (dGeomID, dMatrix3 R) + void dGeomClearOffset (dGeomID) + dReal * dGeomGetOffsetPosition (dGeomID) + dReal * dGeomGetOffsetRotation (dGeomID) + void dGeomDestroy (dGeomID) + void dGeomGetAABB (dGeomID, dReal aabb[6]) + dReal *dGeomGetSpaceAABB (dGeomID) + int dGeomIsSpace (dGeomID) + dSpaceID dGeomGetSpace (dGeomID) + int dGeomGetClass (dGeomID) + + void dGeomSetCategoryBits(dGeomID, unsigned long bits) + void dGeomSetCollideBits(dGeomID, unsigned long bits) + unsigned long dGeomGetCategoryBits(dGeomID) + unsigned long dGeomGetCollideBits(dGeomID) + + void dGeomEnable (dGeomID) + void dGeomDisable (dGeomID) + int dGeomIsEnabled (dGeomID) + + void dGeomGroupAdd (dGeomID group, dGeomID x) + void dGeomGroupRemove (dGeomID group, dGeomID x) + int dGeomGroupGetNumGeoms (dGeomID group) + dGeomID dGeomGroupGetGeom (dGeomID group, int i) + + dGeomID dCreateGeomTransform (dSpaceID space) + void dGeomTransformSetGeom (dGeomID g, dGeomID obj) + dGeomID dGeomTransformGetGeom (dGeomID g) + void dGeomTransformSetCleanup (dGeomID g, int mode) + int dGeomTransformGetCleanup (dGeomID g) + void dGeomTransformSetInfo (dGeomID g, int mode) + int dGeomTransformGetInfo (dGeomID g) + + int dCollide (dGeomID o1, dGeomID o2, int flags, dContactGeom *contact, int skip) + + # Trimesh + dTriMeshDataID dGeomTriMeshDataCreate() + void dGeomTriMeshDataDestroy(dTriMeshDataID g) + void dGeomTriMeshDataBuildSingle1 (dTriMeshDataID g, void* Vertices, + int VertexStride, int VertexCount, + void* Indices, int IndexCount, + int TriStride, void* Normals) + + void dGeomTriMeshDataBuildSimple(dTriMeshDataID g, + dReal* Vertices, int VertexCount, + int* Indices, int IndexCount) + + dGeomID dCreateTriMesh (dSpaceID space, dTriMeshDataID Data, + void* Callback, + void* ArrayCallback, + void* RayCallback) + + void dGeomTriMeshSetData (dGeomID g, dTriMeshDataID Data) + + void dGeomTriMeshClearTCCache (dGeomID g) + + void dGeomTriMeshGetTriangle (dGeomID g, int Index, dVector3 *v0, + dVector3 *v1, dVector3 *v2) + + int dGeomTriMeshGetTriangleCount (dGeomID g) + + void dGeomTriMeshGetPoint (dGeomID g, int Index, dReal u, dReal v, + dVector3 Out) + + void dGeomTriMeshEnableTC(dGeomID g, int geomClass, int enable) + int dGeomTriMeshIsTCEnabled(dGeomID g, int geomClass) + + # Heightfield + dHeightfieldDataID dGeomHeightfieldDataCreate() + void dGeomHeightfieldDataDestroy(dHeightfieldDataID g) + void dGeomHeightfieldDataBuildCallback(dHeightfieldDataID d, + void* pUserData, + dHeightfieldGetHeight* pCallback, + dReal width, dReal depth, + int widthSamples, int depthSamples, + dReal scale, dReal offset, + dReal thickness, int bWrap) + dGeomID dCreateHeightfield (dSpaceID space, dHeightfieldDataID data, + int bPlaceable) + diff --git a/thirdparty/ode-0.16.5/bindings/python/ode.pyx b/thirdparty/ode-0.16.5/bindings/python/ode.pyx new file mode 100644 index 0000000..08d7d5b --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/ode.pyx @@ -0,0 +1,4506 @@ +###################################################################### +# Python Open Dynamics Engine Wrapper +# Copyright (C) 2004 PyODE developers (see file AUTHORS) +# All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of EITHER: +# (1) The GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. The text of the GNU Lesser +# General Public License is included with this library in the +# file LICENSE. +# (2) The BSD-style license that is included with this library in +# the file LICENSE-BSD. +# +# 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 files +# LICENSE and LICENSE-BSD for more details. +###################################################################### + +from ode cimport * + + +paramLoStop = 0 +paramHiStop = 1 +paramVel = 2 +paramLoVel = 3 +paramHiVel = 4 +paramFMax = 5 +paramFudgeFactor = 6 +paramBounce = 7 +paramCFM = 8 +paramStopERP = 9 +paramStopCFM = 10 +paramSuspensionERP = 11 +paramSuspensionCFM = 12 +paramERP = 13 + +ParamLoStop = 0 +ParamHiStop = 1 +ParamVel = 2 +ParamLoVel = 3 +ParamHiVel = 4 +ParamFMax = 5 +ParamFudgeFactor = 6 +ParamBounce = 7 +ParamCFM = 8 +ParamStopERP = 9 +ParamStopCFM = 10 +ParamSuspensionERP = 11 +ParamSuspensionCFM = 12 +ParamERP = 13 + +ParamLoStop2 = 256 + 0 +ParamHiStop2 = 256 + 1 +ParamVel2 = 256 + 2 +ParamLoVel2 = 256 + 3 +ParamHiVel2 = 256 + 4 +ParamFMax2 = 256 + 5 +ParamFudgeFactor2 = 256 + 6 +ParamBounce2 = 256 + 7 +ParamCFM2 = 256 + 8 +ParamStopERP2 = 256 + 9 +ParamStopCFM2 = 256 + 10 +ParamSuspensionERP2 = 256 + 11 +ParamSuspensionCFM2 = 256 + 12 +ParamERP2 = 256 + 13 + +ParamLoStop3 = 512 + 0 +ParamHiStop3 = 512 + 1 +ParamVel3 = 512 + 2 +ParamLoVel3 = 512 + 3 +ParamHiVel3 = 512 + 4 +ParamFMax3 = 512 + 5 +ParamFudgeFactor3 = 512 + 6 +ParamBounce3 = 512 + 7 +ParamCFM3 = 512 + 8 +ParamStopERP3 = 512 + 9 +ParamStopCFM3 = 512 + 10 +ParamSuspensionERP3 = 512 + 11 +ParamSuspensionCFM3 = 512 + 12 +ParamERP3 = 512 + 13 + +ParamGroup = 256 + +ContactMu2 = 0x001 +ContactAxisDep = 0x001 +ContactFDir1 = 0x002 +ContactBounce = 0x004 +ContactSoftERP = 0x008 +ContactSoftCFM = 0x010 +ContactMotion1 = 0x020 +ContactMotion2 = 0x040 +ContactMotionN = 0x080 +ContactSlip1 = 0x100 +ContactSlip2 = 0x200 +ContactRolling = 0x400 + +ContactApprox0 = 0x0000 +ContactApprox1_1 = 0x1000 +ContactApprox1_2 = 0x2000 +ContactApprox1_N = 0x4000 +ContactApprox1 = 0x7000 + +AMotorUser = dAMotorUser +AMotorEuler = dAMotorEuler + +Infinity = dInfinity + + +import weakref +_geom_c2py_lut = weakref.WeakValueDictionary() + + +cdef class Mass: + """Mass parameters of a rigid body. + + This class stores mass parameters of a rigid body which can be + accessed through the following attributes: + + - mass: The total mass of the body (float) + - c: The center of gravity position in body frame (3-tuple of floats) + - I: The 3x3 inertia tensor in body frame (3-tuple of 3-tuples) + + This class wraps the dMass structure from the C API. + + @ivar mass: The total mass of the body + @ivar c: The center of gravity position in body frame (cx, cy, cz) + @ivar I: The 3x3 inertia tensor in body frame ((I11, I12, I13), (I12, I22, I23), (I13, I23, I33)) + @type mass: float + @type c: 3-tuple of floats + @type I: 3-tuple of 3-tuples of floats + """ + cdef dMass _mass + + def __cinit__(self): + dMassSetZero(&self._mass) + + def setZero(self): + """setZero() + + Set all the mass parameters to zero.""" + dMassSetZero(&self._mass) + + def setParameters(self, mass, cgx, cgy, cgz, I11, I22, I33, I12, I13, I23): + """setParameters(mass, cgx, cgy, cgz, I11, I22, I33, I12, I13, I23) + + Set the mass parameters to the given values. + + @param mass: Total mass of the body. + @param cgx: Center of gravity position in the body frame (x component). + @param cgy: Center of gravity position in the body frame (y component). + @param cgz: Center of gravity position in the body frame (z component). + @param I11: Inertia tensor + @param I22: Inertia tensor + @param I33: Inertia tensor + @param I12: Inertia tensor + @param I13: Inertia tensor + @param I23: Inertia tensor + @type mass: float + @type cgx: float + @type cgy: float + @type cgz: float + @type I11: float + @type I22: float + @type I33: float + @type I12: float + @type I13: float + @type I23: float + """ + dMassSetParameters(&self._mass, mass, cgx, cgy, cgz, + I11, I22, I33, I12, I13, I23) + + def setSphere(self, density, radius): + """setSphere(density, radius) + + Set the mass parameters to represent a sphere of the given radius + and density, with the center of mass at (0,0,0) relative to the body. + + @param density: The density of the sphere + @param radius: The radius of the sphere + @type density: float + @type radius: float + """ + dMassSetSphere(&self._mass, density, radius) + + def setSphereTotal(self, total_mass, radius): + """setSphereTotal(total_mass, radius) + + Set the mass parameters to represent a sphere of the given radius + and mass, with the center of mass at (0,0,0) relative to the body. + + @param total_mass: The total mass of the sphere + @param radius: The radius of the sphere + @type total_mass: float + @type radius: float + """ + dMassSetSphereTotal(&self._mass, total_mass, radius) + + def setCapsule(self, density, direction, radius, length): + """setCapsule(density, direction, radius, length) + + Set the mass parameters to represent a capsule of the given parameters + and density, with the center of mass at (0,0,0) relative to the body. + The radius of the cylinder (and the spherical cap) is radius. The length + of the cylinder (not counting the spherical cap) is length. The + cylinder's long axis is oriented along the body's x, y or z axis + according to the value of direction (1=x, 2=y, 3=z). The first function + accepts the density of the object, the second accepts its total mass. + + @param density: The density of the capsule + @param direction: The direction of the capsule's cylinder (1=x axis, 2=y axis, 3=z axis) + @param radius: The radius of the capsule's cylinder + @param length: The length of the capsule's cylinder (without the caps) + @type density: float + @type direction: int + @type radius: float + @type length: float + """ + dMassSetCapsule(&self._mass, density, direction, radius, length) + + def setCapsuleTotal(self, total_mass, direction, radius, length): + """setCapsuleTotal(total_mass, direction, radius, length) + + Set the mass parameters to represent a capsule of the given parameters + and mass, with the center of mass at (0,0,0) relative to the body. The + radius of the cylinder (and the spherical cap) is radius. The length of + the cylinder (not counting the spherical cap) is length. The cylinder's + long axis is oriented along the body's x, y or z axis according to the + value of direction (1=x, 2=y, 3=z). The first function accepts the + density of the object, the second accepts its total mass. + + @param total_mass: The total mass of the capsule + @param direction: The direction of the capsule's cylinder (1=x axis, 2=y axis, 3=z axis) + @param radius: The radius of the capsule's cylinder + @param length: The length of the capsule's cylinder (without the caps) + @type total_mass: float + @type direction: int + @type radius: float + @type length: float + """ + dMassSetCapsuleTotal(&self._mass, total_mass, direction, + radius, length) + + def setCylinder(self, density, direction, r, h): + """setCylinder(density, direction, r, h) + + Set the mass parameters to represent a flat-ended cylinder of + the given parameters and density, with the center of mass at + (0,0,0) relative to the body. The radius of the cylinder is r. + The length of the cylinder is h. The cylinder's long axis is + oriented along the body's x, y or z axis according to the value + of direction (1=x, 2=y, 3=z). + + @param density: The density of the cylinder + @param direction: The direction of the cylinder (1=x axis, 2=y axis, 3=z axis) + @param r: The radius of the cylinder + @param h: The length of the cylinder + @type density: float + @type direction: int + @type r: float + @type h: float + """ + dMassSetCylinder(&self._mass, density, direction, r, h) + + def setCylinderTotal(self, total_mass, direction, r, h): + """setCylinderTotal(total_mass, direction, r, h) + + Set the mass parameters to represent a flat-ended cylinder of + the given parameters and mass, with the center of mass at + (0,0,0) relative to the body. The radius of the cylinder is r. + The length of the cylinder is h. The cylinder's long axis is + oriented along the body's x, y or z axis according to the value + of direction (1=x, 2=y, 3=z). + + @param total_mass: The total mass of the cylinder + @param direction: The direction of the cylinder (1=x axis, 2=y axis, 3=z axis) + @param r: The radius of the cylinder + @param h: The length of the cylinder + @type total_mass: float + @type direction: int + @type r: float + @type h: float + """ + dMassSetCylinderTotal(&self._mass, total_mass, direction, r, h) + + def setBox(self, density, lx, ly, lz): + """setBox(density, lx, ly, lz) + + Set the mass parameters to represent a box of the given + dimensions and density, with the center of mass at (0,0,0) + relative to the body. The side lengths of the box along the x, + y and z axes are lx, ly and lz. + + @param density: The density of the box + @param lx: The length along the x axis + @param ly: The length along the y axis + @param lz: The length along the z axis + @type density: float + @type lx: float + @type ly: float + @type lz: float + """ + dMassSetBox(&self._mass, density, lx, ly, lz) + + def setBoxTotal(self, total_mass, lx, ly, lz): + """setBoxTotal(total_mass, lx, ly, lz) + + Set the mass parameters to represent a box of the given + dimensions and mass, with the center of mass at (0,0,0) + relative to the body. The side lengths of the box along the x, + y and z axes are lx, ly and lz. + + @param total_mass: The total mass of the box + @param lx: The length along the x axis + @param ly: The length along the y axis + @param lz: The length along the z axis + @type total_mass: float + @type lx: float + @type ly: float + @type lz: float + """ + dMassSetBoxTotal(&self._mass, total_mass, lx, ly, lz) + + def adjust(self, newmass): + """adjust(newmass) + + Adjust the total mass. Given mass parameters for some object, + adjust them so the total mass is now newmass. This is useful + when using the setXyz() methods to set the mass parameters for + certain objects - they take the object density, not the total + mass. + + @param newmass: The new total mass + @type newmass: float + """ + dMassAdjust(&self._mass, newmass) + + def translate(self, t): + """translate(t) + + Adjust mass parameters. Given mass parameters for some object, + adjust them to represent the object displaced by (x,y,z) + relative to the body frame. + + @param t: Translation vector (x, y, z) + @type t: 3-tuple of floats + """ + dMassTranslate(&self._mass, t[0], t[1], t[2]) + +# def rotate(self, R): +# """ +# Given mass parameters for some object, adjust them to +# represent the object rotated by R relative to the body frame. +# """ +# pass + + def add(self, Mass b): + """add(b) + + Add the mass b to the mass object. Masses can also be added using + the + operator. + + @param b: The mass to add to this mass + @type b: Mass + """ + dMassAdd(&self._mass, &b._mass) + + def __getattr__(self, name): + if name == "mass": + return self._mass.mass + elif name == "c": + return self._mass.c[0], self._mass.c[1], self._mass.c[2] + elif name == "I": + return ((self._mass.I[0], self._mass.I[1], self._mass.I[2]), + (self._mass.I[4], self._mass.I[5], self._mass.I[6]), + (self._mass.I[8], self._mass.I[9], self._mass.I[10])) + else: + raise AttributeError("Mass object has no attribute '%s'" % name) + + def __setattr__(self, name, value): + if name == "mass": + self.adjust(value) + elif name == "c": + raise AttributeError("Use the setParameter() method to change c") + elif name == "I": + raise AttributeError("Use the setParameter() method to change I") + else: + raise AttributeError("Mass object has no attribute '%s" % name) + + def __add__(self, Mass b): + self.add(b) + return self + + def __str__(self): + m = str(self._mass.mass) + sc0 = str(self._mass.c[0]) + sc1 = str(self._mass.c[1]) + sc2 = str(self._mass.c[2]) + I11 = str(self._mass.I[0]) + I22 = str(self._mass.I[5]) + I33 = str(self._mass.I[10]) + I12 = str(self._mass.I[1]) + I13 = str(self._mass.I[2]) + I23 = str(self._mass.I[6]) + return ("Mass=%s\n" + "Cg=(%s, %s, %s)\n" + "I11=%s I22=%s I33=%s\n" + "I12=%s I13=%s I23=%s" % + (m, sc0, sc1, sc2, I11, I22, I33, I12, I13, I23)) +# return ("Mass=%s / " +# "Cg=(%s, %s, %s) / " +# "I11=%s I22=%s I33=%s " +# "I12=%s I13=%s I23=%s" % +# (m, sc0, sc1, sc2, I11, I22, I33, I12, I13, I23)) + + +cdef class Contact: + """This class represents a contact between two bodies in one point. + + A Contact object stores all the input parameters for a ContactJoint. + This class wraps the ODE dContact structure which has 3 components:: + + struct dContact { + dSurfaceParameters surface; + dContactGeom geom; + dVector3 fdir1; + }; + + This wrapper class provides methods to get and set the items of those + structures. + """ + + cdef dContact _contact + + def __cinit__(self): + self._contact.surface.mode = ContactBounce + self._contact.surface.mu = dInfinity + + self._contact.surface.bounce = 0.1 + + # getMode + def getMode(self): + """getMode() -> flags + + Return the contact flags. + """ + return self._contact.surface.mode + + # setMode + def setMode(self, flags): + """setMode(flags) + + Set the contact flags. The argument m is a combination of the + ContactXyz flags (ContactMu2, ContactBounce, ...). + + @param flags: Contact flags + @type flags: int + """ + self._contact.surface.mode = flags + + # getMu + def getMu(self): + """getMu() -> float + + Return the Coulomb friction coefficient. + """ + return self._contact.surface.mu + + # setMu + def setMu(self, mu): + """setMu(mu) + + Set the Coulomb friction coefficient. + + @param mu: Coulomb friction coefficient (0..Infinity) + @type mu: float + """ + self._contact.surface.mu = mu + + # getMu2 + def getMu2(self): + """getMu2() -> float + + Return the optional Coulomb friction coefficient for direction 2. + """ + return self._contact.surface.mu2 + + # setMu2 + def setMu2(self, mu): + """setMu2(mu) + + Set the optional Coulomb friction coefficient for direction 2. + + @param mu: Coulomb friction coefficient (0..Infinity) + @type mu: float + """ + self._contact.surface.mu2 = mu + + # getBounce + def getBounce(self): + """getBounce() -> float + + Return the restitution parameter. + """ + return self._contact.surface.bounce + + # setBounce + def setBounce(self, b): + """setBounce(b) + + @param b: Restitution parameter (0..1) + @type b: float + """ + self._contact.surface.bounce = b + + # getBounceVel + def getBounceVel(self): + """getBounceVel() -> float + + Return the minimum incoming velocity necessary for bounce. + """ + return self._contact.surface.bounce_vel + + # setBounceVel + def setBounceVel(self, bv): + """setBounceVel(bv) + + Set the minimum incoming velocity necessary for bounce. Incoming + velocities below this will effectively have a bounce parameter of 0. + + @param bv: Velocity + @type bv: float + """ + self._contact.surface.bounce_vel = bv + + # getSoftERP + def getSoftERP(self): + """getSoftERP() -> float + + Return the contact normal "softness" parameter. + """ + return self._contact.surface.soft_erp + + # setSoftERP + def setSoftERP(self, erp): + """setSoftERP(erp) + + Set the contact normal "softness" parameter. + + @param erp: Softness parameter + @type erp: float + """ + self._contact.surface.soft_erp = erp + + # getSoftCFM + def getSoftCFM(self): + """getSoftCFM() -> float + + Return the contact normal "softness" parameter. + """ + return self._contact.surface.soft_cfm + + # setSoftCFM + def setSoftCFM(self, cfm): + """setSoftCFM(cfm) + + Set the contact normal "softness" parameter. + + @param cfm: Softness parameter + @type cfm: float + """ + self._contact.surface.soft_cfm = cfm + + # getMotion1 + def getMotion1(self): + """getMotion1() -> float + + Get the surface velocity in friction direction 1. + """ + return self._contact.surface.motion1 + + # setMotion1 + def setMotion1(self, m): + """setMotion1(m) + + Set the surface velocity in friction direction 1. + + @param m: Surface velocity + @type m: float + """ + self._contact.surface.motion1 = m + + # getMotion2 + def getMotion2(self): + """getMotion2() -> float + + Get the surface velocity in friction direction 2. + """ + return self._contact.surface.motion2 + + # setMotion2 + def setMotion2(self, m): + """setMotion2(m) + + Set the surface velocity in friction direction 2. + + @param m: Surface velocity + @type m: float + """ + self._contact.surface.motion2 = m + + # getSlip1 + def getSlip1(self): + """getSlip1() -> float + + Get the coefficient of force-dependent-slip (FDS) for friction + direction 1. + """ + return self._contact.surface.slip1 + + # setSlip1 + def setSlip1(self, s): + """setSlip1(s) + + Set the coefficient of force-dependent-slip (FDS) for friction + direction 1. + + @param s: FDS coefficient + @type s: float + """ + self._contact.surface.slip1 = s + + # getSlip2 + def getSlip2(self): + """getSlip2() -> float + + Get the coefficient of force-dependent-slip (FDS) for friction + direction 2. + """ + return self._contact.surface.slip2 + + # setSlip2 + def setSlip2(self, s): + """setSlip2(s) + + Set the coefficient of force-dependent-slip (FDS) for friction + direction 1. + + @param s: FDS coefficient + @type s: float + """ + self._contact.surface.slip2 = s + + # getFDir1 + def getFDir1(self): + """getFDir1() -> (x, y, z) + + Get the "first friction direction" vector that defines a direction + along which frictional force is applied. + """ + return (self._contact.fdir1[0], + self._contact.fdir1[1], + self._contact.fdir1[2]) + + # setFDir1 + def setFDir1(self, fdir): + """setFDir1(fdir) + + Set the "first friction direction" vector that defines a direction + along which frictional force is applied. It must be of unit length + and perpendicular to the contact normal (so it is typically + tangential to the contact surface). + + @param fdir: Friction direction + @type fdir: 3-sequence of floats + """ + self._contact.fdir1[0] = fdir[0] + self._contact.fdir1[1] = fdir[1] + self._contact.fdir1[2] = fdir[2] + + # getContactGeomParams + def getContactGeomParams(self): + """getContactGeomParams() -> (pos, normal, depth, geom1, geom2) + + Get the ContactGeom structure of the contact. + + The return value is a tuple (pos, normal, depth, geom1, geom2) + where pos and normal are 3-tuples of floats and depth is a single + float. geom1 and geom2 are the Geom objects of the geoms in contact. + """ + cdef size_t id1, id2 + + pos = (self._contact.geom.pos[0], + self._contact.geom.pos[1], + self._contact.geom.pos[2]) + normal = (self._contact.geom.normal[0], + self._contact.geom.normal[1], + self._contact.geom.normal[2]) + depth = self._contact.geom.depth + + id1 = self._contact.geom.g1 + id2 = self._contact.geom.g2 + g1 = _geom_c2py_lut[id1] + g2 = _geom_c2py_lut[id2] + return pos, normal, depth, g1, g2 + + # setContactGeomParams + def setContactGeomParams(self, pos, normal, depth, g1=None, g2=None): + """setContactGeomParams(pos, normal, depth, geom1=None, geom2=None) + + Set the ContactGeom structure of the contact. + + @param pos: Contact position, in global coordinates + @type pos: 3-sequence of floats + @param normal: Unit length normal vector + @type normal: 3-sequence of floats + @param depth: Depth to which the two bodies inter-penetrate + @type depth: float + @param geom1: Geometry object 1 that collided + @type geom1: Geom + @param geom2: Geometry object 2 that collided + @type geom2: Geom + """ + + cdef size_t id + + self._contact.geom.pos[0] = pos[0] + self._contact.geom.pos[1] = pos[1] + self._contact.geom.pos[2] = pos[2] + self._contact.geom.normal[0] = normal[0] + self._contact.geom.normal[1] = normal[1] + self._contact.geom.normal[2] = normal[2] + self._contact.geom.depth = depth + if g1 != None: + id = g1._id() + self._contact.geom.g1 = id + else: + self._contact.geom.g1 = 0 + + if g2 != None: + id = g2._id() + self._contact.geom.g2 = id + else: + self._contact.geom.g2 = 0 + + +# World +cdef class World: + """Dynamics world. + + The world object is a container for rigid bodies and joints. + + + Constructor:: + + World() + """ + + cdef dWorldID wid + + def __cinit__(self): + self.wid = dWorldCreate() + + def __dealloc__(self): + if self.wid != NULL: + dWorldDestroy(self.wid) + + # setGravity + def setGravity(self, gravity): + """setGravity(gravity) + + Set the world's global gravity vector. + + @param gravity: Gravity vector + @type gravity: 3-sequence of floats + """ + dWorldSetGravity(self.wid, gravity[0], gravity[1], gravity[2]) + + # getGravity + def getGravity(self): + """getGravity() -> 3-tuple + + Return the world's global gravity vector as a 3-tuple of floats. + """ + cdef dVector3 g + dWorldGetGravity(self.wid, g) + return g[0], g[1], g[2] + + # setERP + def setERP(self, erp): + """setERP(erp) + + Set the global ERP value, that controls how much error + correction is performed in each time step. Typical values are + in the range 0.1-0.8. The default is 0.2. + + @param erp: Global ERP value + @type erp: float + """ + dWorldSetERP(self.wid, erp) + + # getERP + def getERP(self): + """getERP() -> float + + Get the global ERP value, that controls how much error + correction is performed in each time step. Typical values are + in the range 0.1-0.8. The default is 0.2. + """ + return dWorldGetERP(self.wid) + + # setCFM + def setCFM(self, cfm): + """setCFM(cfm) + + Set the global CFM (constraint force mixing) value. Typical + values are in the range 10E-9 - 1. The default is 10E-5 if + single precision is being used, or 10E-10 if double precision + is being used. + + @param cfm: Constraint force mixing value + @type cfm: float + """ + dWorldSetCFM(self.wid, cfm) + + # getCFM + def getCFM(self): + """getCFM() -> float + + Get the global CFM (constraint force mixing) value. Typical + values are in the range 10E-9 - 1. The default is 10E-5 if + single precision is being used, or 10E-10 if double precision + is being used. + """ + return dWorldGetCFM(self.wid) + + # step + def step(self, stepsize): + """step(stepsize) + + Step the world. This uses a "big matrix" method that takes + time on the order of O(m3) and memory on the order of O(m2), where m + is the total number of constraint rows. + + For large systems this will use a lot of memory and can be + very slow, but this is currently the most accurate method. + + @param stepsize: Time step + @type stepsize: float + """ + + dWorldStep(self.wid, stepsize) + + # quickStep + def quickStep(self, stepsize): + """quickStep(stepsize) + + Step the world. This uses an iterative method that takes time + on the order of O(m*N) and memory on the order of O(m), where m is + the total number of constraint rows and N is the number of + iterations. + + For large systems this is a lot faster than dWorldStep, but it + is less accurate. + + @param stepsize: Time step + @type stepsize: float + """ + dWorldQuickStep(self.wid, stepsize) + + # setQuickStepNumIterations + def setQuickStepNumIterations(self, num): + """setQuickStepNumIterations(num) + + Set the number of iterations that the QuickStep method + performs per step. More iterations will give a more accurate + solution, but will take longer to compute. The default is 20 + iterations. + + @param num: Number of iterations + @type num: int + """ + + dWorldSetQuickStepNumIterations(self.wid, num) + + # getQuickStepNumIterations + def getQuickStepNumIterations(self): + """getQuickStepNumIterations() -> int + + Get the number of iterations that the QuickStep method + performs per step. More iterations will give a more accurate + solution, but will take longer to compute. The default is 20 + iterations. + """ + return dWorldGetQuickStepNumIterations(self.wid) + + # setQuickStepNumIterations + def setContactMaxCorrectingVel(self, vel): + """setContactMaxCorrectingVel(vel) + + Set the maximum correcting velocity that contacts are allowed + to generate. The default value is infinity (i.e. no + limit). Reducing this value can help prevent "popping" of + deeply embedded objects. + + @param vel: Maximum correcting velocity + @type vel: float + """ + dWorldSetContactMaxCorrectingVel(self.wid, vel) + + # getQuickStepNumIterations + def getContactMaxCorrectingVel(self): + """getContactMaxCorrectingVel() -> float + + Get the maximum correcting velocity that contacts are allowed + to generate. The default value is infinity (i.e. no + limit). Reducing this value can help prevent "popping" of + deeply embedded objects. + + """ + return dWorldGetContactMaxCorrectingVel(self.wid) + + # setContactSurfaceLayer + def setContactSurfaceLayer(self, depth): + """setContactSurfaceLayer(depth) + + Set the depth of the surface layer around all geometry + objects. Contacts are allowed to sink into the surface layer + up to the given depth before coming to rest. The default value + is zero. Increasing this to some small value (e.g. 0.001) can + help prevent jittering problems due to contacts being + repeatedly made and broken. + + @param depth: Surface layer depth + @type depth: float + """ + dWorldSetContactSurfaceLayer(self.wid, depth) + + # getContactSurfaceLayer + def getContactSurfaceLayer(self): + """getContactSurfaceLayer() + + Get the depth of the surface layer around all geometry + objects. Contacts are allowed to sink into the surface layer + up to the given depth before coming to rest. The default value + is zero. Increasing this to some small value (e.g. 0.001) can + help prevent jittering problems due to contacts being + repeatedly made and broken. + """ + return dWorldGetContactSurfaceLayer(self.wid) + + # setAutoDisableFlag + def setAutoDisableFlag(self, flag): + """setAutoDisableFlag(flag) + + Set the default auto-disable flag for newly created bodies. + + @param flag: True = Do auto disable + @type flag: bool + """ + dWorldSetAutoDisableFlag(self.wid, flag) + + # getAutoDisableFlag + def getAutoDisableFlag(self): + """getAutoDisableFlag() -> bool + + Get the default auto-disable flag for newly created bodies. + """ + return dWorldGetAutoDisableFlag(self.wid) + + # setAutoDisableLinearThreshold + def setAutoDisableLinearThreshold(self, threshold): + """setAutoDisableLinearThreshold(threshold) + + Set the default auto-disable linear threshold for newly created + bodies. + + @param threshold: Linear threshold + @type threshold: float + """ + dWorldSetAutoDisableLinearThreshold(self.wid, threshold) + + # getAutoDisableLinearThreshold + def getAutoDisableLinearThreshold(self): + """getAutoDisableLinearThreshold() -> float + + Get the default auto-disable linear threshold for newly created + bodies. + """ + return dWorldGetAutoDisableLinearThreshold(self.wid) + + # setAutoDisableAngularThreshold + def setAutoDisableAngularThreshold(self, threshold): + """setAutoDisableAngularThreshold(threshold) + + Set the default auto-disable angular threshold for newly created + bodies. + + @param threshold: Angular threshold + @type threshold: float + """ + dWorldSetAutoDisableAngularThreshold(self.wid, threshold) + + # getAutoDisableAngularThreshold + def getAutoDisableAngularThreshold(self): + """getAutoDisableAngularThreshold() -> float + + Get the default auto-disable angular threshold for newly created + bodies. + """ + return dWorldGetAutoDisableAngularThreshold(self.wid) + + # setAutoDisableSteps + def setAutoDisableSteps(self, steps): + """setAutoDisableSteps(steps) + + Set the default auto-disable steps for newly created bodies. + + @param steps: Auto disable steps + @type steps: int + """ + dWorldSetAutoDisableSteps(self.wid, steps) + + # getAutoDisableSteps + def getAutoDisableSteps(self): + """getAutoDisableSteps() -> int + + Get the default auto-disable steps for newly created bodies. + """ + return dWorldGetAutoDisableSteps(self.wid) + + # setAutoDisableTime + def setAutoDisableTime(self, time): + """setAutoDisableTime(time) + + Set the default auto-disable time for newly created bodies. + + @param time: Auto disable time + @type time: float + """ + dWorldSetAutoDisableTime(self.wid, time) + + # getAutoDisableTime + def getAutoDisableTime(self): + """getAutoDisableTime() -> float + + Get the default auto-disable time for newly created bodies. + """ + return dWorldGetAutoDisableTime(self.wid) + + # setLinearDamping + def setLinearDamping(self, scale): + """setLinearDamping(scale) + + Set the world's linear damping scale. + @param scale The linear damping scale that is to be applied to bodies. + Default is 0 (no damping). Should be in the interval [0, 1]. + @type scale: float + """ + dWorldSetLinearDamping(self.wid, scale) + + # getLinearDamping + def getLinearDamping(self): + """getLinearDamping() -> float + + Get the world's linear damping scale. + """ + return dWorldGetLinearDamping(self.wid) + + # setAngularDamping + def setAngularDamping(self, scale): + """setAngularDamping(scale) + + Set the world's angular damping scale. + @param scale The angular damping scale that is to be applied to bodies. + Default is 0 (no damping). Should be in the interval [0, 1]. + @type scale: float + """ + dWorldSetAngularDamping(self.wid, scale) + + # getAngularDamping + def getAngularDamping(self): + """getAngularDamping() -> float + + Get the world's angular damping scale. + """ + return dWorldGetAngularDamping(self.wid) + + # impulseToForce + def impulseToForce(self, stepsize, impulse): + """impulseToForce(stepsize, impulse) -> 3-tuple + + If you want to apply a linear or angular impulse to a rigid + body, instead of a force or a torque, then you can use this + function to convert the desired impulse into a force/torque + vector before calling the dBodyAdd... function. + + @param stepsize: Time step + @param impulse: Impulse vector + @type stepsize: float + @type impulse: 3-tuple of floats + """ + cdef dVector3 force + dWorldImpulseToForce(self.wid, stepsize, + impulse[0], impulse[1], impulse[2], force) + return force[0], force[1], force[2] + + # createBody +# def createBody(self): +# return Body(self) + + # createBallJoint +# def createBallJoint(self, jointgroup=None): +# return BallJoint(self, jointgroup) + + # createHingeJoint +# def createHingeJoint(self, jointgroup=None): +# return HingeJoint(self, jointgroup) + + # createHinge2Joint +# def createHinge2Joint(self, jointgroup=None): +# return Hinge2Joint(self, jointgroup) + + # createSliderJoint +# def createSliderJoint(self, jointgroup=None): +# return SliderJoint(self, jointgroup) + + # createFixedJoint +# def createFixedJoint(self, jointgroup=None): +# return FixedJoint(self, jointgroup) + + # createContactJoint +# def createContactJoint(self, jointgroup, contact): +# return ContactJoint(self, jointgroup, contact) + + +# Body +cdef class Body: + """The rigid body class encapsulating the ODE body. + + This class represents a rigid body that has a location and orientation + in space and that stores the mass properties of an object. + + When creating a Body object you have to pass the world it belongs to + as argument to the constructor:: + + >>> import ode + >>> w = ode.World() + >>> b = ode.Body(w) + """ + + cdef dBodyID bid + # A reference to the world so that the world won't be destroyed while + # there are still joints using it. + cdef object world + + # A dictionary with user attributes + # (set via __getattr__ and __setattr__) + cdef object userattribs + + def __cinit__(self, World world not None): + self.bid = dBodyCreate(world.wid) + + def __init__(self, World world not None): + """Constructor. + + @param world: The world in which the body should be created. + @type world: World + """ + self.world = world + self.userattribs = {} + + def __dealloc__(self): + if self.bid != NULL: + dBodyDestroy(self.bid) + + def __getattr__(self, name): + try: + return self.userattribs[name] + except: + raise AttributeError("Body object has no attribute '%s'" % name) + + def __setattr__(self, name, value): + self.userattribs[name] = value + + def __delattr__(self, name): + try: + del self.userattribs[name] + except: + raise AttributeError("Body object has no attribute '%s'" % name) + + # setPosition + def setPosition(self, pos): + """setPosition(pos) + + Set the position of the body. + + @param pos: The new position + @type pos: 3-sequence of floats + """ + dBodySetPosition(self.bid, pos[0], pos[1], pos[2]) + + # getPosition + def getPosition(self): + """getPosition() -> 3-tuple + + Return the current position of the body. + """ + cdef dReal* p + # The "const" in the original return value is cast away + p = dBodyGetPosition(self.bid) + return p[0], p[1], p[2] + + # setRotation + def setRotation(self, R): + """setRotation(R) + + Set the orientation of the body. The rotation matrix must be + given as a sequence of 9 floats which are the elements of the + matrix in row-major order. + + @param R: Rotation matrix + @type R: 9-sequence of floats + """ + cdef dMatrix3 m + m[0] = R[0] + m[1] = R[1] + m[2] = R[2] + m[3] = 0 + m[4] = R[3] + m[5] = R[4] + m[6] = R[5] + m[7] = 0 + m[8] = R[6] + m[9] = R[7] + m[10] = R[8] + m[11] = 0 + dBodySetRotation(self.bid, m) + + # getRotation + def getRotation(self): + """getRotation() -> 9-tuple + + Return the current rotation matrix as a tuple of 9 floats (row-major + order). + """ + cdef dReal* m + # The "const" in the original return value is cast away + m = dBodyGetRotation(self.bid) + return m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10] + + # getQuaternion + def getQuaternion(self): + """getQuaternion() -> 4-tuple + + Return the current rotation as a quaternion. The return value + is a list of 4 floats. + """ + cdef dReal* q + q = dBodyGetQuaternion(self.bid) + return q[0], q[1], q[2], q[3] + + # setQuaternion + def setQuaternion(self, q): + """setQuaternion(q) + + Set the orientation of the body. The quaternion must be given as a + sequence of 4 floats. + + @param q: Quaternion + @type q: 4-sequence of floats + """ + cdef dQuaternion w + w[0] = q[0] + w[1] = q[1] + w[2] = q[2] + w[3] = q[3] + dBodySetQuaternion(self.bid, w) + + # setLinearVel + def setLinearVel(self, vel): + """setLinearVel(vel) + + Set the linear velocity of the body. + + @param vel: New velocity + @type vel: 3-sequence of floats + """ + dBodySetLinearVel(self.bid, vel[0], vel[1], vel[2]) + + # getLinearVel + def getLinearVel(self): + """getLinearVel() -> 3-tuple + + Get the current linear velocity of the body. + """ + cdef dReal* p + # The "const" in the original return value is cast away + p = dBodyGetLinearVel(self.bid) + return p[0], p[1], p[2] + + # setAngularVel + def setAngularVel(self, vel): + """setAngularVel(vel) + + Set the angular velocity of the body. + + @param vel: New angular velocity + @type vel: 3-sequence of floats + """ + dBodySetAngularVel(self.bid, vel[0], vel[1], vel[2]) + + # getAngularVel + def getAngularVel(self): + """getAngularVel() -> 3-tuple + + Get the current angular velocity of the body. + """ + cdef dReal* p + # The "const" in the original return value is cast away + p = dBodyGetAngularVel(self.bid) + return p[0], p[1], p[2] + + # setMass + def setMass(self, Mass mass): + """setMass(mass) + + Set the mass properties of the body. The argument mass must be + an instance of a Mass object. + + @param mass: Mass properties + @type mass: Mass + """ + dBodySetMass(self.bid, &mass._mass) + + # getMass + def getMass(self): + """getMass() -> mass + + Return the mass properties as a Mass object. + """ + cdef Mass m + m = Mass() + dBodyGetMass(self.bid, &m._mass) + return m + + # addForce + def addForce(self, f): + """addForce(f) + + Add an external force f given in absolute coordinates. The force + is applied at the center of mass. + + @param f: Force + @type f: 3-sequence of floats + """ + dBodyAddForce(self.bid, f[0], f[1], f[2]) + + # addTorque + def addTorque(self, t): + """addTorque(t) + + Add an external torque t given in absolute coordinates. + + @param t: Torque + @type t: 3-sequence of floats + """ + dBodyAddTorque(self.bid, t[0], t[1], t[2]) + + # addRelForce + def addRelForce(self, f): + """addRelForce(f) + + Add an external force f given in relative coordinates + (relative to the body's own frame of reference). The force + is applied at the center of mass. + + @param f: Force + @type f: 3-sequence of floats + """ + dBodyAddRelForce(self.bid, f[0], f[1], f[2]) + + # addRelTorque + def addRelTorque(self, t): + """addRelTorque(t) + + Add an external torque t given in relative coordinates + (relative to the body's own frame of reference). + + @param t: Torque + @type t: 3-sequence of floats + """ + dBodyAddRelTorque(self.bid, t[0], t[1], t[2]) + + # addForceAtPos + def addForceAtPos(self, f, p): + """addForceAtPos(f, p) + + Add an external force f at position p. Both arguments must be + given in absolute coordinates. + + @param f: Force + @param p: Position + @type f: 3-sequence of floats + @type p: 3-sequence of floats + """ + dBodyAddForceAtPos(self.bid, f[0], f[1], f[2], p[0], p[1], p[2]) + + # addForceAtRelPos + def addForceAtRelPos(self, f, p): + """addForceAtRelPos(f, p) + + Add an external force f at position p. f is given in absolute + coordinates and p in absolute coordinates. + + @param f: Force + @param p: Position + @type f: 3-sequence of floats + @type p: 3-sequence of floats + """ + dBodyAddForceAtRelPos(self.bid, f[0], f[1], f[2], p[0], p[1], p[2]) + + # addRelForceAtPos + def addRelForceAtPos(self, f, p): + """addRelForceAtPos(f, p) + + Add an external force f at position p. f is given in relative + coordinates and p in relative coordinates. + + @param f: Force + @param p: Position + @type f: 3-sequence of floats + @type p: 3-sequence of floats + """ + dBodyAddRelForceAtPos(self.bid, f[0], f[1], f[2], p[0], p[1], p[2]) + + # addRelForceAtRelPos + def addRelForceAtRelPos(self, f, p): + """addRelForceAtRelPos(f, p) + + Add an external force f at position p. Both arguments must be + given in relative coordinates. + + @param f: Force + @param p: Position + @type f: 3-sequence of floats + @type p: 3-sequence of floats + """ + dBodyAddRelForceAtRelPos(self.bid, f[0], f[1], f[2], p[0], p[1], p[2]) + + # getForce + def getForce(self): + """getForce() -> 3-tuple + + Return the current accumulated force. + """ + cdef dReal* f + # The "const" in the original return value is cast away + f = dBodyGetForce(self.bid) + return f[0], f[1], f[2] + + # getTorque + def getTorque(self): + """getTorque() -> 3-tuple + + Return the current accumulated torque. + """ + cdef dReal* f + # The "const" in the original return value is cast away + f = dBodyGetTorque(self.bid) + return f[0], f[1], f[2] + + # setForce + def setForce(self, f): + """setForce(f) + + Set the body force accumulation vector. + + @param f: Force + @type f: 3-tuple of floats + """ + dBodySetForce(self.bid, f[0], f[1], f[2]) + + # setTorque + def setTorque(self, t): + """setTorque(t) + + Set the body torque accumulation vector. + + @param t: Torque + @type t: 3-tuple of floats + """ + dBodySetTorque(self.bid, t[0], t[1], t[2]) + + # getRelPointPos + def getRelPointPos(self, p): + """getRelPointPos(p) -> 3-tuple + + Utility function that takes a point p on a body and returns + that point's position in global coordinates. The point p + must be given in body relative coordinates. + + @param p: Body point (local coordinates) + @type p: 3-sequence of floats + """ + + cdef dVector3 res + dBodyGetRelPointPos(self.bid, p[0], p[1], p[2], res) + return res[0], res[1], res[2] + + # getRelPointVel + def getRelPointVel(self, p): + """getRelPointVel(p) -> 3-tuple + + Utility function that takes a point p on a body and returns + that point's velocity in global coordinates. The point p + must be given in body relative coordinates. + + @param p: Body point (local coordinates) + @type p: 3-sequence of floats + """ + cdef dVector3 res + dBodyGetRelPointVel(self.bid, p[0], p[1], p[2], res) + return res[0], res[1], res[2] + + # getPointVel + def getPointVel(self, p): + """getPointVel(p) -> 3-tuple + + Utility function that takes a point p on a body and returns + that point's velocity in global coordinates. The point p + must be given in global coordinates. + + @param p: Body point (global coordinates) + @type p: 3-sequence of floats + """ + cdef dVector3 res + dBodyGetPointVel(self.bid, p[0], p[1], p[2], res) + return res[0], res[1], res[2] + + # getPosRelPoint + def getPosRelPoint(self, p): + """getPosRelPoint(p) -> 3-tuple + + This is the inverse of getRelPointPos(). It takes a point p in + global coordinates and returns the point's position in + body-relative coordinates. + + @param p: Body point (global coordinates) + @type p: 3-sequence of floats + """ + cdef dVector3 res + dBodyGetPosRelPoint(self.bid, p[0], p[1], p[2], res) + return res[0], res[1], res[2] + + # vectorToWorld + def vectorToWorld(self, v): + """vectorToWorld(v) -> 3-tuple + + Given a vector v expressed in the body coordinate system, rotate + it to the world coordinate system. + + @param v: Vector in body coordinate system + @type v: 3-sequence of floats + """ + cdef dVector3 res + dBodyVectorToWorld(self.bid, v[0], v[1], v[2], res) + return res[0], res[1], res[2] + + # vectorFromWorld + def vectorFromWorld(self, v): + """vectorFromWorld(v) -> 3-tuple + + Given a vector v expressed in the world coordinate system, rotate + it to the body coordinate system. + + @param v: Vector in world coordinate system + @type v: 3-sequence of floats + """ + cdef dVector3 res + dBodyVectorFromWorld(self.bid, v[0], v[1], v[2], res) + return res[0], res[1], res[2] + + # Enable + def enable(self): + """enable() + + Manually enable a body. + """ + dBodyEnable(self.bid) + + # Disable + def disable(self): + """disable() + + Manually disable a body. Note that a disabled body that is connected + through a joint to an enabled body will be automatically re-enabled + at the next simulation step. + """ + dBodyDisable(self.bid) + + # isEnabled + def isEnabled(self): + """isEnabled() -> bool + + Check if a body is currently enabled. + """ + return dBodyIsEnabled(self.bid) + + # setFiniteRotationMode + def setFiniteRotationMode(self, mode): + """setFiniteRotationMode(mode) + + This function controls the way a body's orientation is updated at + each time step. The mode argument can be: + + - 0: An "infinitesimal" orientation update is used. This is + fast to compute, but it can occasionally cause inaccuracies + for bodies that are rotating at high speed, especially when + those bodies are joined to other bodies. This is the default + for every new body that is created. + + - 1: A "finite" orientation update is used. This is more + costly to compute, but will be more accurate for high speed + rotations. Note however that high speed rotations can result + in many types of error in a simulation, and this mode will + only fix one of those sources of error. + + @param mode: Rotation mode (0/1) + @type mode: int + """ + dBodySetFiniteRotationMode(self.bid, mode) + + # getFiniteRotationMode + def getFiniteRotationMode(self): + """getFiniteRotationMode() -> mode (0/1) + + Return the current finite rotation mode of a body (0 or 1). + See setFiniteRotationMode(). + """ + return dBodyGetFiniteRotationMode(self.bid) + + # setFiniteRotationAxis + def setFiniteRotationAxis(self, a): + """setFiniteRotationAxis(a) + + Set the finite rotation axis of the body. This axis only has a + meaning when the finite rotation mode is set + (see setFiniteRotationMode()). + + @param a: Axis + @type a: 3-sequence of floats + """ + dBodySetFiniteRotationAxis(self.bid, a[0], a[1], a[2]) + + # getFiniteRotationAxis + def getFiniteRotationAxis(self): + """getFiniteRotationAxis() -> 3-tuple + + Return the current finite rotation axis of the body. + """ + cdef dVector3 p + # The "const" in the original return value is cast away + dBodyGetFiniteRotationAxis(self.bid, p) + return p[0], p[1], p[2] + + # getNumJoints + def getNumJoints(self): + """getNumJoints() -> int + + Return the number of joints that are attached to this body. + """ + return dBodyGetNumJoints(self.bid) + + # setGravityMode + def setGravityMode(self, mode): + """setGravityMode(mode) + + Set whether the body is influenced by the world's gravity + or not. If mode is True it is, otherwise it isn't. + Newly created bodies are always influenced by the world's gravity. + + @param mode: Gravity mode + @type mode: bool + """ + dBodySetGravityMode(self.bid, mode) + + # getGravityMode + def getGravityMode(self): + """getGravityMode() -> bool + + Return True if the body is influenced by the world's gravity. + """ + return dBodyGetGravityMode(self.bid) + + def setDynamic(self): + """setDynamic() + + Set a body to the (default) "dynamic" state, instead of "kinematic". + See setKinematic() for more information. + """ + dBodySetDynamic(self.bid) + + def setKinematic(self): + """setKinematic() + + Set the kinematic state of the body (change it into a kinematic body) + + Kinematic bodies behave as if they had infinite mass. This means they don't react + to any force (gravity, constraints or user-supplied); they simply follow + velocity to reach the next position. [from ODE wiki] + + """ + dBodySetKinematic(self.bid) + + def isKinematic(self): + """isKinematic() -> bool + + Return True if the body is kinematic (not influenced by other forces). + + Kinematic bodies behave as if they had infinite mass. This means they don't react + to any force (gravity, constraints or user-supplied); they simply follow + velocity to reach the next position. [from ODE wiki] + + """ + return dBodyIsKinematic(self.bid) + + def setMaxAngularSpeed(self, max_speed): + """setMaxAngularSpeed(max_speed) + + You can also limit the maximum angular speed. In contrast to the damping + functions, the angular velocity is affected before the body is moved. + This means that it will introduce errors in joints that are forcing the + body to rotate too fast. Some bodies have naturally high angular + velocities (like cars' wheels), so you may want to give them a very high + (like the default, dInfinity) limit. + + """ + dBodySetMaxAngularSpeed(self.bid, max_speed) + + +# JointGroup +cdef class JointGroup: + """Joint group. + + Constructor:: + + JointGroup() + """ + + # JointGroup ID + cdef dJointGroupID gid + # A list of Python joints that were added to the group + cdef object jointlist + + def __cinit__(self): + self.gid = dJointGroupCreate(0) + + def __init__(self): + self.jointlist = [] + + def __dealloc__(self): + if self.gid != NULL: + for j in self.jointlist: + j._destroyed() + dJointGroupDestroy(self.gid) + + # empty + def empty(self): + """empty() + + Destroy all joints in the group. + """ + dJointGroupEmpty(self.gid) + for j in self.jointlist: + j._destroyed() + self.jointlist = [] + + def _addjoint(self, j): + """_addjoint(j) + + Add a joint to the group. This is an internal method that is + called by the joints. The group has to know the Python + wrappers because it has to notify them when the group is + emptied (so that the ODE joints won't get destroyed + twice). The notification is done by calling _destroyed() on + the Python joints. + + @param j: The joint to add + @type j: Joint + """ + self.jointlist.append(j) + + +###################################################################### + +# Joint +cdef class Joint: + """Base class for all joint classes.""" + + # Joint id as returned by dJointCreateXxx() + cdef dJointID jid + # A reference to the world so that the world won't be destroyed while + # there are still joints using it. + cdef object world + # The feedback buffer + cdef dJointFeedback* feedback + + cdef object body1 + cdef object body2 + + # A dictionary with user attributes + # (set via __getattr__ and __setattr__) + cdef object userattribs + + def __cinit__(self, *a, **kw): + self.jid = NULL + self.world = None + self.feedback = NULL + self.body1 = None + self.body2 = None + self.userattribs = {} + + def __init__(self, *a, **kw): + raise NotImplementedError("Joint base class can't be used directly") + + def __dealloc__(self): + self.setFeedback(False) + if self.jid != NULL: + dJointDestroy(self.jid) + + def __getattr__(self, name): + try: + return self.userattribs[name] + except: + raise AttributeError("Joint object has no attribute '%s'" % name) + + def __setattr__(self, name, value): + self.userattribs[name] = value + + def __delattr__(self, name): + try: + del self.userattribs[name] + except: + raise AttributeError("Joint object has no attribute '%s'" % name) + + # _destroyed + def _destroyed(self): + """Notify the joint object about an external destruction of the ODE joint. + + This method has to be called when the underlying ODE object + was destroyed by someone else (e.g. by a joint group). The Python + wrapper will then refrain from destroying it again. + """ + self.jid = NULL + + # enable + def enable(self): + """enable() + + Enable the joint. Disabled joints are completely ignored during the + simulation. Disabled joints don't lose the already computed information + like anchors and axes. + """ + dJointEnable(self.jid) + + # disable + def disable(self): + """disable() + + Disable the joint. Disabled joints are completely ignored during the + simulation. Disabled joints don't lose the already computed information + like anchors and axes. + """ + dJointDisable(self.jid) + + # isEnabled + def isEnabled(self): + """isEnabled() -> bool + + Determine whether the joint is enabled. Disabled joints are completely + ignored during the simulation. Disabled joints don't lose the already + computed information like anchors and axes. + """ + return dJointIsEnabled(self.jid) + + # attach + def attach(self, Body body1, Body body2): + """attach(body1, body2) + + Attach the joint to some new bodies. A body can be attached + to the environment by passing None as second body. + + @param body1: First body + @param body2: Second body + @type body1: Body + @type body2: Body + """ + cdef dBodyID id1, id2 + + if body1 == None: + id1 = NULL + else: + id1 = body1.bid + + if body2 == None: + id2 = NULL + else: + id2 = body2.bid + + self.body1 = body1 + self.body2 = body2 + dJointAttach(self.jid, id1, id2) + + # getBody + def getBody(self, index): + """getBody(index) -> Body + + Return the bodies that this joint connects. If index is 0 the + "first" body will be returned, corresponding to the body1 + argument of the attach() method. If index is 1 the "second" body + will be returned, corresponding to the body2 argument of the + attach() method. + + @param index: Bodx index (0 or 1). + @type index: int + """ + + if index == 0: + return self.body1 + elif index == 1: + return self.body2 + else: + raise IndexError() + + # setFeedback + def setFeedback(self, flag=1): + """setFeedback(flag=True) + + Create a feedback buffer. If flag is True then a buffer is + allocated and the forces/torques applied by the joint can + be read using the getFeedback() method. If flag is False the + buffer is released. + + @param flag: Specifies whether a buffer should be created or released + @type flag: bool + """ + + if flag: + # Was there already a buffer allocated? then we're finished + if self.feedback != NULL: + return + # Allocate a buffer and pass it to ODE + self.feedback = malloc(sizeof(dJointFeedback)) + if self.feedback == NULL: + raise MemoryError("can't allocate feedback buffer") + dJointSetFeedback(self.jid, self.feedback) + else: + if self.feedback != NULL: + # Free a previously allocated buffer + dJointSetFeedback(self.jid, NULL) + free(self.feedback) + self.feedback = NULL + + # getFeedback + def getFeedback(self): + """getFeedback() -> (force1, torque1, force2, torque2) + + Get the forces/torques applied by the joint. If feedback is + activated (i.e. setFeedback(True) was called) then this method + returns a tuple (force1, torque1, force2, torque2) with the + forces and torques applied to body 1 and body 2. The + forces/torques are given as 3-tuples. + + If feedback is deactivated then the method always returns None. + """ + cdef dJointFeedback* fb + + fb = dJointGetFeedback(self.jid) + if fb == NULL: + return None + + f1 = (fb.f1[0], fb.f1[1], fb.f1[2]) + t1 = (fb.t1[0], fb.t1[1], fb.t1[2]) + f2 = (fb.f2[0], fb.f2[1], fb.f2[2]) + t2 = (fb.t2[0], fb.t2[1], fb.t2[2]) + return f1, t1, f2, t2 + +###################################################################### + + +# BallJoint +cdef class BallJoint(Joint): + """Ball joint. + + Constructor:: + + BallJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateBall(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setAnchor + def setAnchor(self, pos): + """setAnchor(pos) + + Set the joint anchor point which must be specified in world + coordinates. + + @param pos: Anchor position + @type pos: 3-sequence of floats + """ + dJointSetBallAnchor(self.jid, pos[0], pos[1], pos[2]) + + # getAnchor + def getAnchor(self): + """getAnchor() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This + returns the point on body 1. If the joint is perfectly + satisfied, this will be the same as the point on body 2. + """ + + cdef dVector3 p + dJointGetBallAnchor(self.jid, p) + return p[0], p[1], p[2] + + # getAnchor2 + def getAnchor2(self): + """getAnchor2() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This + returns the point on body 2. If the joint is perfectly + satisfied, this will be the same as the point on body 1. + """ + + cdef dVector3 p + dJointGetBallAnchor2(self.jid, p) + return p[0], p[1], p[2] + + # setParam + def setParam(self, param, value): + pass + + # getParam + def getParam(self, param): + return 0.0 + + +# HingeJoint +cdef class HingeJoint(Joint): + """Hinge joint. + + Constructor:: + + HingeJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateHinge(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setAnchor + def setAnchor(self, pos): + """setAnchor(pos) + + Set the hinge anchor which must be given in world coordinates. + + @param pos: Anchor position + @type pos: 3-sequence of floats + """ + dJointSetHingeAnchor(self.jid, pos[0], pos[1], pos[2]) + + # getAnchor + def getAnchor(self): + """getAnchor() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 1. If the joint is perfectly satisfied, this + will be the same as the point on body 2. + """ + cdef dVector3 p + dJointGetHingeAnchor(self.jid, p) + return p[0], p[1], p[2] + + # getAnchor2 + def getAnchor2(self): + """getAnchor2() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 2. If the joint is perfectly satisfied, this + will be the same as the point on body 1. + """ + cdef dVector3 p + dJointGetHingeAnchor2(self.jid, p) + return p[0], p[1], p[2] + + # setAxis + def setAxis(self, axis): + """setAxis(axis) + + Set the hinge axis. + + @param axis: Hinge axis + @type axis: 3-sequence of floats + """ + dJointSetHingeAxis(self.jid, axis[0], axis[1], axis[2]) + + # getAxis + def getAxis(self): + """getAxis() -> 3-tuple of floats + + Get the hinge axis. + """ + cdef dVector3 a + dJointGetHingeAxis(self.jid, a) + return a[0], a[1], a[2] + + # getAngle + def getAngle(self): + """getAngle() -> float + + Get the hinge angle. The angle is measured between the two + bodies, or between the body and the static environment. The + angle will be between -pi..pi. + + When the hinge anchor or axis is set, the current position of + the attached bodies is examined and that position will be the + zero angle. + """ + + return dJointGetHingeAngle(self.jid) + + # getAngleRate + def getAngleRate(self): + """getAngleRate() -> float + + Get the time derivative of the angle. + """ + return dJointGetHingeAngleRate(self.jid) + + # addTorque + def addTorque(self, torque): + """addTorque(torque) + + Applies the torque about the hinge axis. + + @param torque: Torque magnitude + @type torque: float + """ + dJointAddHingeTorque(self.jid, torque) + + # setParam + def setParam(self, param, value): + """setParam(param, value) + + Set limit/motor parameters for the joint. + + param is one of ParamLoStop, ParamHiStop, ParamVel, ParamFMax, + ParamFudgeFactor, ParamBounce, ParamCFM, ParamStopERP, ParamStopCFM, + ParamSuspensionERP, ParamSuspensionCFM. + + These parameter names can be optionally followed by a digit (2 + or 3) to indicate the second or third set of parameters. + + @param param: Selects the parameter to set + @param value: Parameter value + @type param: int + @type value: float + """ + + dJointSetHingeParam(self.jid, param, value) + + # getParam + def getParam(self, param): + """getParam(param) -> float + + Get limit/motor parameters for the joint. + + param is one of ParamLoStop, ParamHiStop, ParamVel, ParamFMax, + ParamFudgeFactor, ParamBounce, ParamCFM, ParamStopERP, ParamStopCFM, + ParamSuspensionERP, ParamSuspensionCFM. + + These parameter names can be optionally followed by a digit (2 + or 3) to indicate the second or third set of parameters. + + @param param: Selects the parameter to read + @type param: int + """ + return dJointGetHingeParam(self.jid, param) + + +# SliderJoint +cdef class SliderJoint(Joint): + """Slider joint. + + Constructor:: + + SlideJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateSlider(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setAxis + def setAxis(self, axis): + """setAxis(axis) + + Set the slider axis parameter. + + @param axis: Slider axis + @type axis: 3-sequence of floats + """ + dJointSetSliderAxis(self.jid, axis[0], axis[1], axis[2]) + + # getAxis + def getAxis(self): + """getAxis() -> 3-tuple of floats + + Get the slider axis parameter. + """ + cdef dVector3 a + dJointGetSliderAxis(self.jid, a) + return a[0], a[1], a[2] + + # getPosition + def getPosition(self): + """getPosition() -> float + + Get the slider linear position (i.e. the slider's "extension"). + + When the axis is set, the current position of the attached + bodies is examined and that position will be the zero + position. + """ + + return dJointGetSliderPosition(self.jid) + + # getPositionRate + def getPositionRate(self): + """getPositionRate() -> float + + Get the time derivative of the position. + """ + return dJointGetSliderPositionRate(self.jid) + + # addForce + def addForce(self, force): + """addForce(force) + + Applies the given force in the slider's direction. + + @param force: Force magnitude + @type force: float + """ + dJointAddSliderForce(self.jid, force) + + # setParam + def setParam(self, param, value): + dJointSetSliderParam(self.jid, param, value) + + # getParam + def getParam(self, param): + return dJointGetSliderParam(self.jid, param) + + +# UniversalJoint +cdef class UniversalJoint(Joint): + """Universal joint. + + Constructor:: + + UniversalJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateUniversal(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setAnchor + def setAnchor(self, pos): + """setAnchor(pos) + + Set the universal anchor. + + @param pos: Anchor position + @type pos: 3-sequence of floats + """ + dJointSetUniversalAnchor(self.jid, pos[0], pos[1], pos[2]) + + # getAnchor + def getAnchor(self): + """getAnchor() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 1. If the joint is perfectly satisfied, this + will be the same as the point on body 2. + """ + + cdef dVector3 p + dJointGetUniversalAnchor(self.jid, p) + return p[0], p[1], p[2] + + # getAnchor2 + def getAnchor2(self): + """getAnchor2() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 2. If the joint is perfectly satisfied, this + will be the same as the point on body 1. + """ + + cdef dVector3 p + dJointGetUniversalAnchor2(self.jid, p) + return p[0], p[1], p[2] + + # setAxis1 + def setAxis1(self, axis): + """setAxis1(axis) + + Set the first universal axis. Axis 1 and axis 2 should be + perpendicular to each other. + + @param axis: Joint axis + @type axis: 3-sequence of floats + """ + dJointSetUniversalAxis1(self.jid, axis[0], axis[1], axis[2]) + + # getAxis1 + def getAxis1(self): + """getAxis1() -> 3-tuple of floats + + Get the first univeral axis. + """ + cdef dVector3 a + dJointGetUniversalAxis1(self.jid, a) + return a[0], a[1], a[2] + + # setAxis2 + def setAxis2(self, axis): + """setAxis2(axis) + + Set the second universal axis. Axis 1 and axis 2 should be + perpendicular to each other. + + @param axis: Joint axis + @type axis: 3-sequence of floats + """ + dJointSetUniversalAxis2(self.jid, axis[0], axis[1], axis[2]) + + # getAxis2 + def getAxis2(self): + """getAxis2() -> 3-tuple of floats + + Get the second univeral axis. + """ + cdef dVector3 a + dJointGetUniversalAxis2(self.jid, a) + return a[0], a[1], a[2] + + # addTorques + def addTorques(self, torque1, torque2): + """addTorques(torque1, torque2) + + Applies torque1 about axis 1, and torque2 about axis 2. + + @param torque1: Torque 1 magnitude + @param torque2: Torque 2 magnitude + @type torque1: float + @type torque2: float + """ + dJointAddUniversalTorques(self.jid, torque1, torque2) + + def getAngle1(self): + return dJointGetUniversalAngle1(self.jid) + + def getAngle2(self): + return dJointGetUniversalAngle2(self.jid) + + def getAngle1Rate(self): + return dJointGetUniversalAngle1Rate(self.jid) + + def getAngle2Rate(self): + return dJointGetUniversalAngle2Rate(self.jid) + + # setParam + def setParam(self, param, value): + dJointSetUniversalParam(self.jid, param, value) + + # getParam + def getParam(self, param): + return dJointGetUniversalParam(self.jid, param) + + +# Hinge2Joint +cdef class Hinge2Joint(Joint): + """Hinge2 joint. + + Constructor:: + + Hinge2Joint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateHinge2(world.wid, jgid) + + def __init__(self, World world, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setAnchor + def setAnchor(self, pos): + """setAnchor(pos) + + Set the hinge-2 anchor. + + @param pos: Anchor position + @type pos: 3-sequence of floats + """ + dJointSetHinge2Anchor(self.jid, pos[0], pos[1], pos[2]) + + # getAnchor + def getAnchor(self): + """getAnchor() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 1. If the joint is perfectly satisfied, this + will be the same as the point on body 2. + """ + + cdef dVector3 p + dJointGetHinge2Anchor(self.jid, p) + return p[0], p[1], p[2] + + # getAnchor2 + def getAnchor2(self): + """getAnchor2() -> 3-tuple of floats + + Get the joint anchor point, in world coordinates. This returns + the point on body 2. If the joint is perfectly satisfied, this + will be the same as the point on body 1. + """ + + cdef dVector3 p + dJointGetHinge2Anchor2(self.jid, p) + return p[0], p[1], p[2] + + # setAxis1 + def setAxis1(self, axis): + """setAxis1(axis) + + Set the first hinge-2 axis. Axis 1 and axis 2 must not lie + along the same line. + + @param axis: Joint axis + @type axis: 3-sequence of floats + """ + + dJointSetHinge2Axis1(self.jid, axis[0], axis[1], axis[2]) + + # getAxis1 + def getAxis1(self): + """getAxis1() -> 3-tuple of floats + + Get the first hinge-2 axis. + """ + cdef dVector3 a + dJointGetHinge2Axis1(self.jid, a) + return a[0], a[1], a[2] + + # setAxis2 + def setAxis2(self, axis): + """setAxis2(axis) + + Set the second hinge-2 axis. Axis 1 and axis 2 must not lie + along the same line. + + @param axis: Joint axis + @type axis: 3-sequence of floats + """ + dJointSetHinge2Axis2(self.jid, axis[0], axis[1], axis[2]) + + # getAxis2 + def getAxis2(self): + """getAxis2() -> 3-tuple of floats + + Get the second hinge-2 axis. + """ + cdef dVector3 a + dJointGetHinge2Axis2(self.jid, a) + return a[0], a[1], a[2] + + # getAngle + def getAngle1(self): + """getAngle1() -> float + + Get the first hinge-2 angle (around axis 1). + + When the anchor or axis is set, the current position of the + attached bodies is examined and that position will be the zero + angle. + """ + return dJointGetHinge2Angle1(self.jid) + + # getAngle1Rate + def getAngle1Rate(self): + """getAngle1Rate() -> float + + Get the time derivative of the first hinge-2 angle. + """ + return dJointGetHinge2Angle1Rate(self.jid) + + # getAngle2Rate + def getAngle2Rate(self): + """getAngle2Rate() -> float + + Get the time derivative of the second hinge-2 angle. + """ + return dJointGetHinge2Angle2Rate(self.jid) + + # addTorques + def addTorques(self, torque1, torque2): + """addTorques(torque1, torque2) + + Applies torque1 about axis 1, and torque2 about axis 2. + + @param torque1: Torque 1 magnitude + @param torque2: Torque 2 magnitude + @type torque1: float + @type torque2: float + """ + dJointAddHinge2Torques(self.jid, torque1, torque2) + + # setParam + def setParam(self, param, value): + dJointSetHinge2Param(self.jid, param, value) + + # getParam + def getParam(self, param): + return dJointGetHinge2Param(self.jid, param) + + +# FixedJoint +cdef class FixedJoint(Joint): + """Fixed joint. + + Constructor:: + + FixedJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateFixed(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setFixed + def setFixed(self): + """setFixed() + + Call this on the fixed joint after it has been attached to + remember the current desired relative offset and desired + relative rotation between the bodies. + """ + dJointSetFixed(self.jid) + + +# ContactJoint +cdef class ContactJoint(Joint): + """Contact joint. + + Constructor:: + + ContactJoint(world, jointgroup, contact) + """ + + def __cinit__(self, World world not None, jointgroup, Contact contact): + cdef JointGroup jg + cdef dJointGroupID jgid + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateContact(world.wid, jgid, &contact._contact) + + def __init__(self, World world not None, jointgroup, Contact contact): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + +# AMotor +cdef class AMotor(Joint): + """AMotor joint. + + Constructor:: + + AMotor(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateAMotor(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setMode + def setMode(self, mode): + """setMode(mode) + + Set the angular motor mode. mode must be either AMotorUser or + AMotorEuler. + + @param mode: Angular motor mode + @type mode: int + """ + dJointSetAMotorMode(self.jid, mode) + + # getMode + def getMode(self): + """getMode() + + Return the angular motor mode (AMotorUser or AMotorEuler). + """ + return dJointGetAMotorMode(self.jid) + + # setNumAxes + def setNumAxes(self, int num): + """setNumAxes(num) + + Set the number of angular axes that will be controlled by the AMotor. + num may be in the range from 0 to 3. + + @param num: Number of axes (0-3) + @type num: int + """ + dJointSetAMotorNumAxes(self.jid, num) + + # getNumAxes + def getNumAxes(self): + """getNumAxes() -> int + + Get the number of angular axes that are controlled by the AMotor. + """ + return dJointGetAMotorNumAxes(self.jid) + + # setAxis + def setAxis(self, int anum, int rel, axis): + """setAxis(anum, rel, axis) + + Set an AMotor axis. + + The anum argument selects the axis to change (0,1 or 2). + Each axis can have one of three "relative orientation" modes, + selected by rel: + + 0: The axis is anchored to the global frame. + 1: The axis is anchored to the first body. + 2: The axis is anchored to the second body. + + The axis vector is always specified in global coordinates + regardless of the setting of rel. + + @param anum: Axis number + @param rel: Relative orientation mode + @param axis: Axis + @type anum: int + @type rel: int + @type axis: 3-sequence of floats + """ + dJointSetAMotorAxis(self.jid, anum, rel, axis[0], axis[1], axis[2]) + + # getAxis + def getAxis(self, int anum): + """getAxis(anum) + + Get an AMotor axis. + + @param anum: Axis index (0-2) + @type anum: int + """ + cdef dVector3 a + dJointGetAMotorAxis(self.jid, anum, a) + return a[0], a[1], a[2] + + # getAxisRel + def getAxisRel(self, int anum): + """getAxisRel(anum) -> int + + Get the relative mode of an axis. + + @param anum: Axis index (0-2) + @type anum: int + """ + return dJointGetAMotorAxisRel(self.jid, anum) + + # setAngle + def setAngle(self, int anum, angle): + """setAngle(anum, angle) + + Tell the AMotor what the current angle is along axis anum. + + @param anum: Axis index + @param angle: Angle + @type anum: int + @type angle: float + """ + dJointSetAMotorAngle(self.jid, anum, angle) + + # getAngle + def getAngle(self, int anum): + """getAngle(anum) -> float + + Return the current angle for axis anum. + + @param anum: Axis index + @type anum: int + """ + return dJointGetAMotorAngle(self.jid, anum) + + # getAngleRate + def getAngleRate(self, int anum): + """getAngleRate(anum) -> float + + Return the current angle rate for axis anum. + + @param anum: Axis index + @type anum: int + """ + return dJointGetAMotorAngleRate(self.jid, anum) + + # addTorques + def addTorques(self, torque0, torque1, torque2): + """addTorques(torque0, torque1, torque2) + + Applies torques about the AMotor's axes. + + @param torque0: Torque 0 magnitude + @param torque1: Torque 1 magnitude + @param torque2: Torque 2 magnitude + @type torque0: float + @type torque1: float + @type torque2: float + """ + dJointAddAMotorTorques(self.jid, torque0, torque1, torque2) + + # setParam + def setParam(self, param, value): + dJointSetAMotorParam(self.jid, param, value) + + # getParam + def getParam(self, param): + return dJointGetAMotorParam(self.jid, param) + + +# LMotor +cdef class LMotor(Joint): + """LMotor joint. + + Constructor:: + + LMotor(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreateLMotor(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + # setNumAxes + def setNumAxes(self, int num): + """setNumAxes(num) + + Set the number of angular axes that will be controlled by the LMotor. + num may be in the range from 0 to 3. + + @param num: Number of axes (0-3) + @type num: int + """ + dJointSetLMotorNumAxes(self.jid, num) + + # getNumAxes + def getNumAxes(self): + """getNumAxes() -> int + + Get the number of angular axes that are controlled by the LMotor. + """ + return dJointGetLMotorNumAxes(self.jid) + + # setAxis + def setAxis(self, int anum, int rel, axis): + """setAxis(anum, rel, axis) + + Set an LMotor axis. + + The anum argument selects the axis to change (0,1 or 2). + Each axis can have one of three "relative orientation" modes, + selected by rel: + + 0: The axis is anchored to the global frame. + 1: The axis is anchored to the first body. + 2: The axis is anchored to the second body. + + @param anum: Axis number + @param rel: Relative orientation mode + @param axis: Axis + @type anum: int + @type rel: int + @type axis: 3-sequence of floats + """ + dJointSetLMotorAxis(self.jid, anum, rel, axis[0], axis[1], axis[2]) + + # getAxis + def getAxis(self, int anum): + """getAxis(anum) + + Get an LMotor axis. + + @param anum: Axis index (0-2) + @type anum: int + """ + cdef dVector3 a + dJointGetLMotorAxis(self.jid, anum, a) + return a[0], a[1], a[2] + + # setParam + def setParam(self, param, value): + dJointSetLMotorParam(self.jid, param, value) + + # getParam + def getParam(self, param): + return dJointGetLMotorParam(self.jid, param) + + +# Plane2DJoint +cdef class Plane2DJoint(Joint): + """Plane-2D Joint. + + Constructor:: + + Plane2DJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreatePlane2D(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + def setXParam(self, param, value): + dJointSetPlane2DXParam(self.jid, param, value) + + def setYParam(self, param, value): + dJointSetPlane2DYParam(self.jid, param, value) + + def setAngleParam(self, param, value): + dJointSetPlane2DAngleParam(self.jid, param, value) + + +# PRJoint +cdef class PRJoint(Joint): + """Prismatic and Rotoide Joint. + + Constructor:: + + PRJoint(world, jointgroup=None) + """ + + def __cinit__(self, World world not None, jointgroup=None): + cdef JointGroup jg + cdef dJointGroupID jgid + + jgid = NULL + if jointgroup != None: + jg = jointgroup + jgid = jg.gid + self.jid = dJointCreatePR(world.wid, jgid) + + def __init__(self, World world not None, jointgroup=None): + self.world = world + if jointgroup != None: + jointgroup._addjoint(self) + + def getPosition(self): + """getPosition() + + Get a PRJoint's linear extension. (i.e. the prismatic's extension) + """ + return dJointGetPRPosition(self.jid) + + def setAnchor(self, pos): + """setAnchor(pos) + + Set a PRJoint anchor. + + @param pos: Anchor position + @type pos: 3-sequence of floats + """ + dJointSetPRAnchor(self.jid, pos[0], pos[1], pos[2]) + + def getAnchor(self): + """getAnchor() + + Get a PRJoint anchor. + """ + cdef dVector3 a + dJointGetPRAnchor(self.jid, a) + return a[0], a[1], a[2] + + def setAxis1(self, axis): + """setAxis1(axis) + + Set a PRJoint's prismatic axis. + + @param axis: Axis + @type axis: 3-sequence of floats + """ + dJointSetPRAxis1(self.jid, axis[0], axis[1], axis[2]) + + def getAxis1(self): + """getAxis1() + + Get a PRJoint's prismatic axis. + """ + cdef dVector3 a + dJointGetPRAxis1(self.jid, a) + return a[0], a[1], a[2] + + def setAxis2(self, axis): + """setAxis2(axis) + + Set a PRJoint's rotoide axis. + + @param axis: Axis + @type axis: 3-sequence of floats + """ + dJointSetPRAxis2(self.jid, axis[0], axis[1], axis[2]) + + def getAxis2(self): + """getAxis2() + + Get a PRJoint's rotoide axis. + """ + cdef dVector3 a + dJointGetPRAxis2(self.jid, a) + return a[0], a[1], a[2] + + +# Geom base class +cdef class GeomObject: + """This is the abstract base class for all geom objects. + """ + + # The id of the geom object as returned by dCreateXxxx() + cdef dGeomID gid + # The space in which the geom was placed (or None). This reference + # is kept so that the space won't be destroyed while there are still + # geoms around that might use it. + cdef object space + # The body that the geom was attached to (or None). + cdef object body + + # A dictionary with user defined attributes + cdef object attribs + + cdef object __weakref__ + + def __cinit__(self, *a, **kw): + self.gid = NULL + self.space = None + self.body = None + self.attribs = {} + + def __init__(self, *a, **kw): + raise NotImplementedError( + "GeomObject base class can't be used directly") + + def __dealloc__(self): + if self.gid != NULL: + dGeomDestroy(self.gid) + self.gid = NULL + + def __getattr__(self, name): + if name in self.attribs: + return self.attribs[name] + else: + raise AttributeError("geom has no attribute '%s'" % name) + + def __setattr__(self, name, val): + self.attribs[name] = val + + def __delattr__(self, name): + if name in self.attribs: + del self.attribs[name] + else: + raise AttributeError("geom has no attribute '%s'" % name) + + def _id(self): + """_id() -> int + + Return the internal id of the geom (dGeomID) as returned by + the dCreateXyz() functions. + + This method has to be overwritten in derived methods. + """ + raise NotImplementedError("Bug: The _id() method is not implemented") + + def placeable(self): + """placeable() -> bool + + Returns True if the geom object is a placeable geom. + + This method has to be overwritten in derived methods. + """ + return False + + def setBody(self, Body body): + """setBody(body) + + Set the body associated with a placeable geom. + + @param body: The Body object or None. + @type body: Body + """ + + if not self.placeable(): + raise ValueError( + "Non-placeable geoms cannot have a body associated to them") + + if body == None: + dGeomSetBody(self.gid, NULL) + else: + dGeomSetBody(self.gid, body.bid) + self.body = body + + def getBody(self): + """getBody() -> Body + + Get the body associated with this geom. + """ + if not self.placeable(): + return environment + + return self.body + + def setPosition(self, pos): + """setPosition(pos) + + Set the position of the geom. If the geom is attached to a body, + the body's position will also be changed. + + @param pos: Position + @type pos: 3-sequence of floats + """ + if not self.placeable(): + raise ValueError("Cannot set a position on non-placeable geoms") + dGeomSetPosition(self.gid, pos[0], pos[1], pos[2]) + + def getPosition(self): + """getPosition() -> 3-tuple + + Get the current position of the geom. If the geom is attached to + a body the returned value is the body's position. + """ + if not self.placeable(): + raise ValueError("Non-placeable geoms do not have a position") + + cdef dReal* p + p = dGeomGetPosition(self.gid) + return p[0], p[1], p[2] + + def setRotation(self, R): + """setRotation(R) + + Set the orientation of the geom. If the geom is attached to a body, + the body's orientation will also be changed. + + @param R: Rotation matrix + @type R: 9-sequence of floats + """ + if not self.placeable(): + raise ValueError("Cannot set a rotation on non-placeable geoms") + + cdef dMatrix3 m + m[0] = R[0] + m[1] = R[1] + m[2] = R[2] + m[3] = 0 + m[4] = R[3] + m[5] = R[4] + m[6] = R[5] + m[7] = 0 + m[8] = R[6] + m[9] = R[7] + m[10] = R[8] + m[11] = 0 + dGeomSetRotation(self.gid, m) + + def getRotation(self): + """getRotation() -> 9-tuple + + Get the current orientation of the geom. If the geom is attached to + a body the returned value is the body's orientation. + """ + if not self.placeable(): + raise ValueError("Non-placeable geoms do not have a rotation") + + cdef dReal* m + m = dGeomGetRotation(self.gid) + return [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]] + + def getQuaternion(self): + """getQuaternion() -> (w,x,y,z) + + Get the current orientation of the geom. If the geom is attached to + a body the returned value is the body's orientation. + """ + if not self.placeable(): + raise ValueError("Non-placeable geoms do not have an orientation") + + cdef dQuaternion q + dGeomGetQuaternion(self.gid, q) + return q[0], q[1], q[2], q[3] + + def setQuaternion(self, q): + """setQuaternion(q) + + Set the orientation of the geom. If the geom is attached to a body, + the body's orientation will also be changed. + + @param q: Quaternion (w,x,y,z) + @type q: 4-sequence of floats + """ + if not self.placeable(): + raise ValueError("Cannot set a quaternion on non-placeable geoms") + + cdef dQuaternion cq + cq[0] = q[0] + cq[1] = q[1] + cq[2] = q[2] + cq[3] = q[3] + dGeomSetQuaternion(self.gid, cq) + + def setOffsetPosition(self, pos): + """setOffsetPosition(pos) + + Set the offset position of the geom. The geom must be attached to a + body. If the geom did not have an offset, it is automatically created. + This sets up an additional (local) transformation for the geom, since + geoms attached to a body share their global position and rotation. + + @param pos: Position + @type pos: 3-sequence of floats + """ + if self.body == None: + raise ValueError("Cannot set an offset position on a geom before " + "calling setBody") + dGeomSetOffsetPosition(self.gid, pos[0], pos[1], pos[2]) + + def getOffsetPosition(self): + """getOffsetPosition() -> 3-tuple + + Get the offset position of the geom. + """ + cdef dReal* p + p = dGeomGetOffsetPosition(self.gid) + return (p[0],p[1],p[2]) + + def setOffsetRotation(self, R): + """setOffsetRotation(R) + + Set the offset rotation of the geom. The geom must be attached to a + body. If the geom did not have an offset, it is automatically created. + This sets up an additional (local) transformation for the geom, since + geoms attached to a body share their global position and rotation. + + @param R: Rotation matrix + @type R: 9-sequence of floats + """ + if self.body == None: + raise ValueError("Cannot set an offset rotation on a geom before " + "calling setBody") + + cdef dMatrix3 m + m[0] = R[0] + m[1] = R[1] + m[2] = R[2] + m[3] = 0 + m[4] = R[3] + m[5] = R[4] + m[6] = R[5] + m[7] = 0 + m[8] = R[6] + m[9] = R[7] + m[10] = R[8] + m[11] = 0 + dGeomSetOffsetRotation(self.gid, m) + + def getOffsetRotation(self): + """getOffsetRotation() -> 9-tuple + + Get the offset rotation of the geom. + """ + cdef dReal* m + m = dGeomGetOffsetRotation(self.gid) + return [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10]] + + def clearOffset(self): + """clearOffset() + + Disable the offset transform of the geom. + """ + dGeomClearOffset(self.gid) + + def getAABB(self): + """getAABB() -> 6-tuple + + Return an axis aligned bounding box that surrounds the geom. + The return value is a 6-tuple (minx, maxx, miny, maxy, minz, maxz). + """ + cdef dReal aabb[6] + + dGeomGetAABB(self.gid, aabb) + return aabb[0], aabb[1], aabb[2], aabb[3], aabb[4], aabb[5] + + def isSpace(self): + """isSpace() -> bool + + Return 1 if the given geom is a space, or 0 if not.""" + return dGeomIsSpace(self.gid) + + def getSpace(self): + """getSpace() -> Space + + Return the space that the given geometry is contained in, + or return None if it is not contained in any space.""" + return self.space + + def setCollideBits(self, bits): + """setCollideBits(bits) + + Set the "collide" bitfields for this geom. + + @param bits: Collide bit field + @type bits: int/long + """ + dGeomSetCollideBits(self.gid, long(bits)) + + def setCategoryBits(self, bits): + """setCategoryBits(bits) + + Set the "category" bitfields for this geom. + + @param bits: Category bit field + @type bits: int/long + """ + dGeomSetCategoryBits(self.gid, long(bits)) + + def getCollideBits(self): + """getCollideBits() -> long + + Return the "collide" bitfields for this geom. + """ + return dGeomGetCollideBits(self.gid) + + def getCategoryBits(self): + """getCategoryBits() -> long + + Return the "category" bitfields for this geom. + """ + return dGeomGetCategoryBits(self.gid) + + def enable(self): + """enable() + + Enable the geom.""" + dGeomEnable(self.gid) + + def disable(self): + """disable() + + Disable the geom.""" + dGeomDisable(self.gid) + + def isEnabled(self): + """isEnabled() -> bool + + Return True if the geom is enabled.""" + return dGeomIsEnabled(self.gid) + + +# _SpaceIterator +class _SpaceIterator: + """Iterates over the geoms inside a Space. + """ + + def __init__(self, space): + self.space = space + self.idx = 0 + + def __iter__(self): + return self + + def next(self): + if self.idx >= self.space.getNumGeoms(): + raise StopIteration + else: + res = self.space.getGeom(self.idx) + self.idx = self.idx + 1 + return res + + +# SpaceBase +cdef class SpaceBase(GeomObject): + """Space class (container for geometry objects). + + A Space object is a container for geometry objects which are used + to do collision detection. + The space does high level collision culling, which means that it + can identify which pairs of geometry objects are potentially + touching. + + This Space class can be used for both, a SimpleSpace and a HashSpace + (see ODE documentation). + + >>> space = Space(type=0) # Create a SimpleSpace + >>> space = Space(type=1) # Create a HashSpace + """ + + # The id of the space. Actually this is a copy of the value in self.gid + # (as the Space is derived from GeomObject) which can be used without + # casting whenever a *space* id is required. + cdef dSpaceID sid + + # Dictionary with Geomobjects. Key is the ID (geom._id()) and the value + # is the geom object (Python wrapper). This is used in collide_callback() +# cdef object geom_dict + + def __cinit__(self, *a, **kw): + pass + + def __init__(self, *a, **kw): + raise NotImplementedError("The SpaceBase class can't be used directly") + + def __dealloc__(self): + if self.gid != NULL: + dSpaceDestroy(self.sid) + self.sid = NULL + self.gid = NULL + +# def _addgeom(self, geom): +# """Insert the geom object into the dictionary (internal method). +# +# This method has to called in the constructor of a geom object. +# """ +# self.geom_dict[geom._id()]=geom + +# def _id2geom(self, id): +# """Get the Python wrapper that corresponds to an ID. +# +# The ID is the integer value, as returned by geom._id(). +# If the ID is unknown then None is returned. +# """ +# if id in self.geom_dict: +# return self.geom_dict[id] +# else: +# return None + + def _id(self): + cdef size_t id + id = self.sid + return id + + def __len__(self): + return self.getNumGeoms() + + def __iter__(self): + return _SpaceIterator(self) + + def add(self, GeomObject geom): + """add(geom) + + Add a geom to a space. This does nothing if the geom is + already in the space. + + @param geom: Geom object to add + @type geom: GeomObject + """ + + dSpaceAdd(self.sid, geom.gid) + + def remove(self, GeomObject geom): + """remove(geom) + + Remove a geom from a space. + + @param geom: Geom object to remove + @type geom: GeomObject + """ + dSpaceRemove(self.sid, geom.gid) + + def query(self, GeomObject geom): + """query(geom) -> bool + + Return True if the given geom is in the space. + + @param geom: Geom object to check + @type geom: GeomObject + """ + return dSpaceQuery(self.sid, geom.gid) + + def getNumGeoms(self): + """getNumGeoms() -> int + + Return the number of geoms contained within the space. + """ + return dSpaceGetNumGeoms(self.sid) + + def getGeom(self, int idx): + """getGeom(idx) -> GeomObject + + Return the geom with the given index contained within the space. + + @param idx: Geom index (0,1,...,getNumGeoms()-1) + @type idx: int + """ + cdef dGeomID gid + + # Check the index + if idx < 0 or idx >= dSpaceGetNumGeoms(self.sid): + raise IndexError("geom index out of range") + + gid = dSpaceGetGeom(self.sid, idx) + if gid not in _geom_c2py_lut: + raise RuntimeError( + "geom id cannot be translated to a Python object") + + return _geom_c2py_lut[gid] + + def collide(self, arg, callback): + """collide(arg, callback) + + Call a callback function one or more times, for all + potentially intersecting objects in the space. The callback + function takes 3 arguments: + + def NearCallback(arg, geom1, geom2): + + The arg parameter is just passed on to the callback function. + Its meaning is user defined. The geom1 and geom2 arguments are + the geometry objects that may be near each other. The callback + function can call the function collide() (not the Space + method) on geom1 and geom2, perhaps first determining + whether to collide them at all based on other information. + + @param arg: A user argument that is passed to the callback function + @param callback: Callback function + @type callback: callable + """ + + cdef void* data + cdef object tup + tup = (callback, arg) + data = tup + dSpaceCollide(self.sid, data, collide_callback) + + +# Callback function for the dSpaceCollide() call in the Space.collide() method +# The data parameter is a tuple (Python-Callback, Arguments). +# The function calls a Python callback function with 3 arguments: +# def callback(UserArg, Geom1, Geom2) +# Geom1 and Geom2 are instances of GeomXyz classes. +cdef void collide_callback(void* data, dGeomID o1, dGeomID o2): + cdef object tup +# cdef Space space + cdef size_t id1, id2 + +# if (dGeomGetBody(o1)==dGeomGetBody(o2)): +# return + + tup = data + callback, arg = tup + id1 = o1 + id2 = o2 + g1 = _geom_c2py_lut[id1] + g2 = _geom_c2py_lut[id2] + callback(arg, g1, g2) + + +# SimpleSpace +cdef class SimpleSpace(SpaceBase): + """Simple space. + + This does not do any collision culling - it simply checks every + possible pair of geoms for intersection, and reports the pairs + whose AABBs overlap. The time required to do intersection testing + for n objects is O(n**2). This should not be used for large numbers + of objects, but it can be the preferred algorithm for a small + number of objects. This is also useful for debugging potential + problems with the collision system. + """ + + def __cinit__(self, space=None): + cdef SpaceBase sp + cdef dSpaceID parentid + + parentid = NULL + if space != None: + sp = space + parentid = sp.sid + + self.sid = dSimpleSpaceCreate(parentid) + + # Copy the ID + self.gid = self.sid + + dSpaceSetCleanup(self.sid, 0) + _geom_c2py_lut[self.sid] = self + + def __init__(self, space=None): + pass + +# HashSpace +cdef class HashSpace(SpaceBase): + """Multi-resolution hash table space. + + This uses an internal data structure that records how each geom + overlaps cells in one of several three dimensional grids. Each + grid has cubical cells of side lengths 2**i, where i is an integer + that ranges from a minimum to a maximum value. The time required + to do intersection testing for n objects is O(n) (as long as those + objects are not clustered together too closely), as each object + can be quickly paired with the objects around it. + """ + + def __cinit__(self, space=None): + cdef SpaceBase sp + cdef dSpaceID parentid + + parentid = NULL + if space != None: + sp = space + parentid = sp.sid + + self.sid = dHashSpaceCreate(parentid) + + # Copy the ID + self.gid = self.sid + + dSpaceSetCleanup(self.sid, 0) + _geom_c2py_lut[self.sid] = self + + def __init__(self, space=None): + pass + + def setLevels(self, int minlevel, int maxlevel): + """setLevels(minlevel, maxlevel) + + Sets the size of the smallest and largest cell used in the + hash table. The actual size will be 2^minlevel and 2^maxlevel + respectively. + """ + + if minlevel > maxlevel: + raise ValueError( + "minlevel (%d) must be less than or equal to maxlevel (%d)" % + (minlevel, maxlevel)) + + dHashSpaceSetLevels(self.sid, minlevel, maxlevel) + + def getLevels(self): + """getLevels() -> (minlevel, maxlevel) + + Gets the size of the smallest and largest cell used in the + hash table. The actual size is 2^minlevel and 2^maxlevel + respectively. + """ + + cdef int minlevel + cdef int maxlevel + dHashSpaceGetLevels(self.sid, &minlevel, &maxlevel) + return minlevel, maxlevel + + +# QuadTreeSpace +cdef class QuadTreeSpace(SpaceBase): + """Quadtree space. + + This uses a pre-allocated hierarchical grid-based AABB tree to + quickly cull collision checks. It's exceptionally quick for large + amounts of objects in landscape-shaped worlds. The amount of + memory used is 4**depth * 32 bytes. + + Currently getGeom() is not implemented for the quadtree space. + """ + + def __cinit__(self, center, extents, depth, space=None): + cdef SpaceBase sp + cdef dSpaceID parentid + cdef dVector3 c + cdef dVector3 e + + parentid = NULL + if space != None: + sp = space + parentid = sp.sid + + c[0] = center[0] + c[1] = center[1] + c[2] = center[2] + e[0] = extents[0] + e[1] = extents[1] + e[2] = extents[2] + self.sid = dQuadTreeSpaceCreate(parentid, c, e, depth) + + # Copy the ID + self.gid = self.sid + + dSpaceSetCleanup(self.sid, 0) + _geom_c2py_lut[self.sid] = self + + def __init__(self, center, extents, depth, space=None): + pass + + +def Space(space_type=0): + """Space factory function. + + Depending on the type argument this function either returns a + SimpleSpace (space_type=0) or a HashSpace (space_type=1). + + This function is provided to remain compatible with previous + versions of PyODE where there was only one Space class. + + >>> space = Space(space_type=0) # Create a SimpleSpace + >>> space = Space(space_type=1) # Create a HashSpace + """ + if space_type == 0: + return SimpleSpace() + elif space_type == 1: + return HashSpace() + else: + raise ValueError("Unknown space type (%d)" % space_type) + + +# GeomSphere +cdef class GeomSphere(GeomObject): + """Sphere geometry. + + This class represents a sphere centered at the origin. + + Constructor:: + + GeomSphere(space=None, radius=1.0) + """ + + def __cinit__(self, space=None, radius=1.0): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateSphere(sid, radius) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, radius=1.0): + self.space = space + self.body = None + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setRadius(self, radius): + """setRadius(radius) + + Set the radius of the sphere. + + @param radius: New radius + @type radius: float + """ + dGeomSphereSetRadius(self.gid, radius) + + def getRadius(self): + """getRadius() -> float + + Return the radius of the sphere. + """ + return dGeomSphereGetRadius(self.gid) + + def pointDepth(self, p): + """pointDepth(p) -> float + + Return the depth of the point p in the sphere. Points inside + the geom will have positive depth, points outside it will have + negative depth, and points on the surface will have zero + depth. + + @param p: Point + @type p: 3-sequence of floats + """ + return dGeomSpherePointDepth(self.gid, p[0], p[1], p[2]) + + +# GeomBox +cdef class GeomBox(GeomObject): + """Box geometry. + + This class represents a box centered at the origin. + + Constructor:: + + GeomBox(space=None, lengths=(1.0, 1.0, 1.0)) + """ + + def __cinit__(self, space=None, lengths=(1.0, 1.0, 1.0)): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateBox(sid, lengths[0], lengths[1], lengths[2]) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, lengths=(1.0, 1.0, 1.0)): + self.space = space + self.body = None + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setLengths(self, lengths): + dGeomBoxSetLengths(self.gid, lengths[0], lengths[1], lengths[2]) + + def getLengths(self): + cdef dVector3 res + dGeomBoxGetLengths(self.gid, res) + return res[0], res[1], res[2] + + def pointDepth(self, p): + """pointDepth(p) -> float + + Return the depth of the point p in the box. Points inside the + geom will have positive depth, points outside it will have + negative depth, and points on the surface will have zero + depth. + + @param p: Point + @type p: 3-sequence of floats + """ + return dGeomBoxPointDepth(self.gid, p[0], p[1], p[2]) + + +# GeomPlane +cdef class GeomPlane(GeomObject): + """Plane geometry. + + This class represents an infinite plane. The plane equation is: + n.x*x + n.y*y + n.z*z = dist + + This object can't be attached to a body. + If you call getBody() on this object it always returns ode.environment. + + Constructor:: + + GeomPlane(space=None, normal=(0,0,1), dist=0) + + """ + + def __cinit__(self, space=None, normal=(0, 0, 1), dist=0): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreatePlane(sid, normal[0], normal[1], normal[2], dist) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, normal=(0, 0, 1), dist=0): + self.space = space + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setParams(self, normal, dist): + dGeomPlaneSetParams(self.gid, normal[0], normal[1], normal[2], dist) + + def getParams(self): + cdef dVector4 res + dGeomPlaneGetParams(self.gid, res) + return ((res[0], res[1], res[2]), res[3]) + + def pointDepth(self, p): + """pointDepth(p) -> float + + Return the depth of the point p in the plane. Points inside the + geom will have positive depth, points outside it will have + negative depth, and points on the surface will have zero + depth. + + @param p: Point + @type p: 3-sequence of floats + """ + return dGeomPlanePointDepth(self.gid, p[0], p[1], p[2]) + + +# GeomCapsule +cdef class GeomCapsule(GeomObject): + """Capped cylinder geometry. + + This class represents a capped cylinder aligned along the local Z axis + and centered at the origin. + + Constructor:: + + GeomCapsule(space=None, radius=0.5, length=1.0) + + The length parameter does not include the caps. + """ + + def __cinit__(self, space=None, radius=0.5, length=1.0): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateCapsule(sid, radius, length) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, radius=0.5, length=1.0): + self.space = space + self.body = None + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setParams(self, radius, length): + dGeomCapsuleSetParams(self.gid, radius, length) + + def getParams(self): + cdef dReal radius, length + dGeomCapsuleGetParams(self.gid, &radius, &length) + return radius, length + + def pointDepth(self, p): + """pointDepth(p) -> float + + Return the depth of the point p in the cylinder. Points inside the + geom will have positive depth, points outside it will have + negative depth, and points on the surface will have zero + depth. + + @param p: Point + @type p: 3-sequence of floats + """ + return dGeomCapsulePointDepth(self.gid, p[0], p[1], p[2]) + +GeomCCylinder = GeomCapsule # backwards compatibility + + +# GeomCylinder +cdef class GeomCylinder(GeomObject): + """Plain cylinder geometry. + + This class represents an uncapped cylinder aligned along the local Z axis + and centered at the origin. + + Constructor:: + + GeomCylinder(space=None, radius=0.5, length=1.0) + """ + + def __cinit__(self, space=None, radius=0.5, length=1.0): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateCylinder(sid, radius, length) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, radius=0.5, length=1.0): + self.space = space + self.body = None + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setParams(self, radius, length): + dGeomCylinderSetParams(self.gid, radius, length) + + def getParams(self): + cdef dReal radius, length + dGeomCylinderGetParams(self.gid, &radius, &length) + return radius, length + + ## dGeomCylinderPointDepth not implemented upstream in ODE 0.7 + + +# GeomRay +cdef class GeomRay(GeomObject): + """Ray object. + + A ray is different from all the other geom classes in that it does + not represent a solid object. It is an infinitely thin line that + starts from the geom's position and extends in the direction of + the geom's local Z-axis. + + Constructor:: + + GeomRay(space=None, rlen=1.0) + + """ + + def __cinit__(self, space=None, rlen=1.0): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateRay(sid, rlen) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None, rlen=1.0): + self.space = space + self.body = None + + def _id(self): + cdef size_t id + id = self.gid + return id + + def placeable(self): + return True + + def setLength(self, rlen): + '''setLength(rlen) + + Set length of the ray. + + @param rlen: length of the ray + @type rlen: float''' + dGeomRaySetLength(self.gid, rlen) + + def getLength(self): + '''getLength() -> length + + Get the length of the ray. + + @returns: length of the ray (float)''' + return dGeomRayGetLength(self.gid) + + def set(self, p, u): + '''set(p, u) + + Set the position and rotation of a ray. + + @param p: position + @type p: 3-sequence of floats + @param u: rotation + @type u: 3-sequence of floats''' + dGeomRaySet(self.gid, p[0], p[1], p[2], u[0], u[1], u[2]) + + def get(self): + '''get() -> ((p[0], p[1], p[2]), (u[0], u[1], u[2])) + + Return the position and rotation as a pair of + tuples. + + @returns: position and rotation''' + cdef dVector3 start + cdef dVector3 dir + dGeomRayGet(self.gid, start, dir) + return (start[0], start[1], start[2]), (dir[0], dir[1], dir[2]) + + +# GeomTransform +cdef class GeomTransform(GeomObject): + """GeomTransform. + + A geometry transform "T" is a geom that encapsulates another geom + "E", allowing E to be positioned and rotated arbitrarily with + respect to its point of reference. + + Constructor:: + + GeomTransform(space=None) + """ + + cdef object geom + + def __cinit__(self, space=None): + cdef SpaceBase sp + cdef dSpaceID sid + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateGeomTransform(sid) + # Set cleanup mode to 0 as a contained geom will be deleted + # by its Python wrapper class + dGeomTransformSetCleanup(self.gid, 0) +# if space != None: +# space._addgeom(self) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, space=None): + self.space = space + self.body = None + self.geom = None + + self.attribs = {} + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def setGeom(self, GeomObject geom not None): + """setGeom(geom) + + Set the geom that the geometry transform encapsulates. + A ValueError exception is thrown if a) the geom is not placeable, + b) the geom was already inserted into a space or c) the geom is + already associated with a body. + + @param geom: Geom object to encapsulate + @type geom: GeomObject + """ + cdef size_t id + + if not geom.placeable(): + raise ValueError( + "Only placeable geoms can be encapsulated by a GeomTransform") + if dGeomGetSpace(geom.gid) != 0: + raise ValueError( + "The encapsulated geom was already inserted into a space") + if dGeomGetBody(geom.gid) != 0: + raise ValueError( + "The encapsulated geom is already associated with a body") + + id = geom._id() + dGeomTransformSetGeom(self.gid, id) + self.geom = geom + + def getGeom(self): + """getGeom() -> GeomObject + + Get the geom that the geometry transform encapsulates. + """ + return self.geom + + def setInfo(self, int mode): + """setInfo(mode) + + Set the "information" mode of the geometry transform. + + With mode 0, when a transform object is collided with another + object, the geom field of the ContactGeom structure is set to the + geom that is encapsulated by the transform object. + + With mode 1, the geom field of the ContactGeom structure is set + to the transform object itself. + + @param mode: Information mode (0 or 1) + @type mode: int + """ + if mode < 0 or mode > 1: + raise ValueError( + "Invalid information mode (%d). Must be either 0 or 1." % mode) + dGeomTransformSetInfo(self.gid, mode) + + def getInfo(self): + """getInfo() -> int + + Get the "information" mode of the geometry transform (0 or 1). + + With mode 0, when a transform object is collided with another + object, the geom field of the ContactGeom structure is set to the + geom that is encapsulated by the transform object. + + With mode 1, the geom field of the ContactGeom structure is set + to the transform object itself. + """ + return dGeomTransformGetInfo(self.gid) + +###################################################################### + + +cdef class TriMeshData: + """This class stores the mesh data. + """ + + cdef dTriMeshDataID tmdid + cdef dReal* vertex_buffer + cdef int* face_buffer + + def __cinit__(self): + self.tmdid = dGeomTriMeshDataCreate() + self.vertex_buffer = NULL + self.face_buffer = NULL + + def __dealloc__(self): + if self.tmdid != NULL: + dGeomTriMeshDataDestroy(self.tmdid) + if self.vertex_buffer != NULL: + free(self.vertex_buffer) + if self.face_buffer != NULL: + free(self.face_buffer) + + def build(self, verts, faces): + """build(verts, faces) + + @param verts: Vertices + @type verts: Sequence of 3-sequences of floats + @param faces: Face definitions (three indices per face) + @type faces: Sequence of 3-sequences of ints + """ + cdef int numverts + cdef int numfaces + cdef dReal* vp + cdef int* fp + cdef int a, b, c + + numverts = len(verts) + numfaces = len(faces) + # Allocate the vertex and face buffer + self.vertex_buffer = malloc(numverts * 4 * sizeof(dReal)) + self.face_buffer = malloc(numfaces * 3 * sizeof(int)) + + # Fill the vertex buffer + vp = self.vertex_buffer + for v in verts: + vp[0] = v[0] + vp[1] = v[1] + vp[2] = v[2] + vp[3] = 0 + vp = vp + 4 + + # Fill the face buffer + fp = self.face_buffer + for f in faces: + a = f[0] + b = f[1] + c = f[2] + if (a < 0 or b < 0 or c < 0 or a >= numverts or b >= numverts or c >= numverts): + raise ValueError("Vertex index out of range") + fp[0] = a + fp[1] = b + fp[2] = c + fp = fp + 3 + + # Pass the data to ODE + dGeomTriMeshDataBuildSimple(self.tmdid, self.vertex_buffer, numverts, + self.face_buffer, numfaces * 3) + +###################################################################### + + +# GeomTriMesh +cdef class GeomTriMesh(GeomObject): + """TriMesh object. + + To construct the trimesh geom you need a TriMeshData object that + stores the actual mesh. This object has to be passed as first + argument to the constructor. + + Constructor:: + + GeomTriMesh(data, space=None) + """ + + # Keep a reference to the data + cdef TriMeshData data + + def __cinit__(self, TriMeshData data not None, space=None): + cdef SpaceBase sp + cdef dSpaceID sid + + self.data = data + + sid = NULL + if space != None: + sp = space + sid = sp.sid + self.gid = dCreateTriMesh(sid, data.tmdid, NULL, NULL, NULL) + + _geom_c2py_lut[self.gid] = self + + def __init__(self, TriMeshData data not None, space=None): + self.space = space + self.body = None + + def placeable(self): + return True + + def _id(self): + cdef size_t id + id = self.gid + return id + + def clearTCCache(self): + """clearTCCache() + + Clears the internal temporal coherence caches. + """ + dGeomTriMeshClearTCCache(self.gid) + + def getTriangle(self, int idx): + """getTriangle(idx) -> (v0, v1, v2) + + @param idx: Triangle index + @type idx: int + """ + + cdef dVector3 v0, v1, v2 + cdef dVector3* vp0 + cdef dVector3* vp1 + cdef dVector3* vp2 + + vp0 = v0 + vp1 = v1 + vp2 = v2 + + dGeomTriMeshGetTriangle(self.gid, idx, vp0, vp1, vp2) + return ((v0[0], v0[1], v0[2]), + (v1[0], v1[1], v1[2]), + (v2[0], v2[1], v2[2])) + + def getTriangleCount(self): + """getTriangleCount() -> n + + Returns the number of triangles in the TriMesh.""" + + return dGeomTriMeshGetTriangleCount(self.gid) + +###################################################################### + + +def collide(geom1, geom2): + """collide(geom1, geom2) -> contacts + + Generate contact information for two objects. + + Given two geometry objects that potentially touch (geom1 and geom2), + generate contact information for them. Internally, this just calls + the correct class-specific collision functions for geom1 and geom2. + + [flags specifies how contacts should be generated if the objects + touch. Currently the lower 16 bits of flags specifies the maximum + number of contact points to generate. If this number is zero, this + function just pretends that it is one - in other words you can not + ask for zero contacts. All other bits in flags must be zero. In + the future the other bits may be used to select other contact + generation strategies.] + + If the objects touch, this returns a list of Contact objects, + otherwise it returns an empty list. + + @param geom1: First Geom + @type geom1: GeomObject + @param geom2: Second Geom + @type geom2: GeomObject + @returns: Returns a list of Contact objects. + """ + + cdef dContactGeom c[150] + cdef size_t id1 + cdef size_t id2 + cdef int i, n + cdef Contact cont + + id1 = geom1._id() + id2 = geom2._id() + + n = dCollide(id1, id2, 150, c, sizeof(dContactGeom)) + res = [] + i = 0 + while i < n: + cont = Contact() + cont._contact.geom = c[i] + res.append(cont) + i = i + 1 + + return res + + +def collide2(geom1, geom2, arg, callback): + """collide2(geom1, geom2, arg, callback) + + Calls the callback for all potentially intersecting pairs that contain + one geom from geom1 and one geom from geom2. + + @param geom1: First Geom + @type geom1: GeomObject + @param geom2: Second Geom + @type geom2: GeomObject + @param arg: A user argument that is passed to the callback function + @param callback: Callback function + @type callback: callable + """ + cdef void* data + cdef object tup + cdef size_t id1 + cdef size_t id2 + + id1 = geom1._id() + id2 = geom2._id() + + tup = (callback, arg) + data = tup + # collide_callback is defined in space.pyx + dSpaceCollide2(id1, id2, data, collide_callback) + + +def areConnected(Body body1, Body body2): + """areConnected(body1, body2) -> bool + + Return True if the two bodies are connected together by a joint, + otherwise return False. + + @param body1: First body + @type body1: Body + @param body2: Second body + @type body2: Body + @returns: True if the bodies are connected + """ + + if body1 is environment: + return False + if body2 is environment: + return False + + return bool(dAreConnected( body1.bid, body2.bid)) + + +def CloseODE(): + """CloseODE() + + Deallocate some extra memory used by ODE that can not be deallocated + using the normal destroy functions. + """ + dCloseODE() + + +def InitODE(): + '''InitODE() + + Initialize some ODE internals. This will be called for you when you + "import ode", but you should call this again if you CloseODE().''' + dInitODE() + + +#environment = Body(None) +environment = None +InitODE() diff --git a/thirdparty/ode-0.16.5/bindings/python/setup.py b/thirdparty/ode-0.16.5/bindings/python/setup.py new file mode 100644 index 0000000..f0c5763 --- /dev/null +++ b/thirdparty/ode-0.16.5/bindings/python/setup.py @@ -0,0 +1,48 @@ +#! /usr/bin/env python +from distutils.core import setup +from distutils.extension import Extension +from subprocess import Popen, PIPE, CalledProcessError + + +try: + from Cython.Distutils import build_ext +except ImportError: + raise SystemExit("Requires Cython (http://cython.org/)") + +try: + ode_cflags = Popen( + ["pkg-config", "--cflags", "ode"], + stdout=PIPE).stdout.read().decode('ascii').split() + ode_libs = Popen( + ["pkg-config", "--libs", "ode"], + stdout=PIPE).stdout.read().decode('ascii').split() +except (OSError, CalledProcessError): + raise SystemExit("Failed to find ODE with 'pkg-config'. Please make sure " + "that it is installed and available on your system path.") + +ode_ext = Extension("ode", ["ode.pyx"], + extra_compile_args=ode_cflags, + extra_link_args=ode_libs) + +if __name__ == "__main__": + setup( + name="Open Dynamics Engine", + version="0.12", + author="Gideon Klompje", +# author_email="", +# maintainer="", +# maintainer_email="", + url="http://www.ode.org", + description="Bindings for the Open Dynamics Engine", + long_description=( + "A free, industrial quality library for simulating articulated " + "rigid body dynamics - for example ground vehicles, legged " + "creatures, and moving objects in VR environments. It's fast, " + "flexible & robust. Built-in collision detection."), +# download_url="https://opende.svn.sourceforge.net/svnroot/opende", +# classifiers=[], +# platforms=[], + license="BSD License, GNU Lesser General Public License (LGPL)", + cmdclass={"build_ext": build_ext}, + ext_modules=[ode_ext] + ) diff --git a/thirdparty/ode-0.16.5/bootstrap b/thirdparty/ode-0.16.5/bootstrap new file mode 100644 index 0000000..24e91f0 --- /dev/null +++ b/thirdparty/ode-0.16.5/bootstrap @@ -0,0 +1,28 @@ +#!/bin/sh + +echo "Please make sure that you use automake 1.11 or later" + echo "Warnings about underquoted definitions are harmless" +if [ `uname -s` = Darwin ]; then + echo "On OSX, install latest libtool, as the OS provided glibtoolize will not work" +fi + +echo "Running aclocal" +aclocal -I m4 --install || exit 1 +echo "Running libtoolize" +libtoolize --copy --automake --install || exit 1 +echo "Running autoheader" +autoheader || exit 1 +echo "Running automake" +automake --foreign --add-missing --copy || exit 1 +echo "Running autoconf" +autoconf || exit 1 + +echo "Running bootstrap in ou directory" +(cd ou && ./bootstrap) + +if [ -d libccd ]; then + echo "Running bootstrap in libccd directory" + (cd libccd && ./bootstrap) +fi; + +echo "Now you are ready to run ./configure" diff --git a/thirdparty/ode-0.16.5/build/config-default.h b/thirdparty/ode-0.16.5/build/config-default.h new file mode 100644 index 0000000..0a69b75 --- /dev/null +++ b/thirdparty/ode-0.16.5/build/config-default.h @@ -0,0 +1,113 @@ +/* This file was autogenerated by Premake */ +#ifndef _ODE_CONFIG_H_ +#define _ODE_CONFIG_H_ + + +/****************************************************************** + * CONFIGURATON SETTINGS - you can change these, and then rebuild + * ODE to modify the behavior of the library. + * + * dTRIMESH_ENABLED - enable/disable trimesh support + * dTRIMESH_OPCODE - use the OPCODE trimesh engine + * dTRIMESH_GIMPACT - use the GIMPACT trimesh engine + * Only one trimesh engine should be enabled. + * + * dTRIMESH_16BIT_INDICES (todo: opcode only) + * Setup the trimesh engine to use 16 bit + * triangle indices. The default is to use + * 32 bit indices. Use the dTriIndex type to + * detect the correct index size. + * + * dTRIMESH_OPCODE_USE_NEWOLD_TRIMESH_TRIMESH_COLLIDER + * Use old implementation of trimesh-trimesh collider + * (for backward compatibility only) + * + * dOU_ENABLED + * dATOMICS_ENABLED + * dTLS_ENABLED + * Use generic features of OU library, atomic API + * and TLS API respectively. + * Generic features and atomic API are always enabled, + * unless threading interface support is disabled. + * Using TLS for global variables allows calling ODE + * collision detection functions from multiple threads. + * + * dBUILTIN_THREADING_IMPL_ENABLED + * Include built-in multithreaded threading + * implementation (still must be created and assigned + * to be used). + * + ******************************************************************/ + +#define dTRIMESH_ENABLED 1 +#define dTRIMESH_OPCODE 1 +#define dTRIMESH_16BIT_INDICES 0 + +#define dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER 0 + +/* #define dOU_ENABLED 1 */ +/* #define dATOMICS_ENABLED 1 */ +/* #define dTLS_ENABLED 1 */ + +/* #define dTHREADING_INTF_DISABLED 1 */ +/* #define dBUILTIN_THREADING_IMPL_ENABLED 1 */ + + +/****************************************************************** + * SYSTEM SETTINGS - you shouldn't need to change these. If you + * run into an issue with these settings, please report it to + * the ODE bug tracker at: + * http://sf.net/tracker/?group_id=24884&atid=382799 + ******************************************************************/ + +/* Try to identify the platform */ +#if defined(_XENON) + #define ODE_PLATFORM_XBOX360 +#elif defined(SN_TARGET_PSP_HW) + #define ODE_PLATFORM_PSP +#elif defined(SN_TARGET_PS3) + #define ODE_PLATFORM_PS3 +#elif defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) + #define ODE_PLATFORM_WINDOWS +#elif defined(__linux__) + #define ODE_PLATFORM_LINUX +#elif defined(__APPLE__) && defined(__MACH__) + #define ODE_PLATFORM_OSX +#else + #error "Need some help identifying the platform!" +#endif + +/* Additional platform defines used in the code */ +#if defined(ODE_PLATFORM_WINDOWS) && !defined(WIN32) + #define WIN32 +#endif + +#if defined(__CYGWIN__) || defined(__MINGW32__) + #define CYGWIN +#endif + +#if defined(ODE_PLATFORM_OSX) + #define macintosh +#endif + +#if !defined(ODE_PLATFORM_OSX) && !defined(ODE_PLATFORM_PS3) + #include +#endif + +#if !defined(ODE_PLATFORM_WINDOWS) + #include +#endif + + +/* Basic OU functionality is required if either atomic API or TLS support + * is enabled. */ +#if (dATOMICS_ENABLED || dTLS_ENABLED) && !dOU_ENABLED +#undef dOU_ENABLED +#define dOU_ENABLED 1 +#endif + + +#include "typedefs.h" + + +#endif diff --git a/thirdparty/ode-0.16.5/build/premake4.exe b/thirdparty/ode-0.16.5/build/premake4.exe new file mode 100644 index 0000000000000000000000000000000000000000..b4230f197acc9b44cb4df64b56dbfb305ae1eaf0 GIT binary patch literal 261632 zcmeFadwf*Yx%fSknIr=Y%m4#Ki5M|jD5ylSWdv%-FiGU*kTH`B2DDbs7_~*15mXXF zhqUbOj^*_9qURiK`|HhPFUPh@DYiBNNrAcW$0hI!_jf&8oS_=Kcv8F@_)qdl7A{! ze&uJ^u9ccS@~7lu9ZDMi+N8ZZ?e;Woz>&jC#xbFkp?~F+8+!ZypZ>pyfm-A3R9}(N zG+@?x$_^XJvu0k|Nz*a^Uzyu=q4{Zk)~t9|=%~wG>fxvCfN@i)TYc^`PDg6MnRuYR zBQYkMmD&GHhJQx4Ni$6YMqkrTw`2Y{Xd$x1W$7@EJ|@m8P2p=-1&dcc^zO=&{eO)_mIOQ1{GnI=aFGQgXYaWT7Knd&0Q6 z)NhXNxMhLEphJ-B0Z^|DmwYvoUcNVrCvb4v_xDrEoUr$5UJv-%L4zZ8x+hVh3rKDA zBFQrs&oaEF>dkr1Y~j@Dj>P5WMbZqtxy{kK)bfZCC{@o;DxJ7I2fz9csdbTbWE81~ zJ_YPY`_&ziC~$eq2?Dl|!1n|`!(FQWRsjBYsjDet6ql-zj90^=;WR7!Ql?fK&KJb~W!{uw)S9FB z=iaySkNQo$zXt2_!av?)X3`UFb~HO<>e(p{M+DgKSim@Ud)qsb7sS-Ph~sG7v3h|c zrdEw*qNHnUw3IJJ3u)R&-#)NnWz#O*UY*(EiRlLG)IBuV3^tnc>eQ|F^PWglP1Nld zN&9tbEbYhQvuM9gWq2NJzfSFz?gSI*+fS@mxrz>Iw>!!~@7qyz^>s|tqi27g$p9Q_ z>cw&=+z4G<(e^t@b{O4>F>P~hPP+Cut}1VvnyX$BN&OQpTdY>g4)K;q`TQ^O-YAeh}5Xr^xn|k(U|o5ejPq=nE(*6@|qn{ zu%2Lf=a{j|6Z}f5wB*L#vU~mkd0mGa!(|tp1$7ch}Iddu8>=B zpf=DRIM(fHL`&z*PC?Y)M>`yi#xB#*kQ&G&3L?soy6RVe4g_T#`vw68UArlq>X^+G z?-?^3!rWLW@;!=}<(^LEPW3xeJx>!fN(RT+9LYahuMMZqVf6l|$u@aL)FF|r zIf~y%uhDo!Q~W)QV~`?a>grs-*%k%gv1kmE?+W(|WAr}7pG>m9PTeuWAtbWGxA`%W zn(f4!>CxClPt9|q>T7eHj+GEmVyv-edUqsRr=o(t-Ck={wCrS^`XX-)E1ENP%0kt| zS8Eozhs0)Qo$4m3*;S{;O_VAnkSpaX5be~DsBe6f$uxopU-P*-Ra}@u*39OQ>eOgn z8`@8)h0jFzb6WnKl0O6T=R^5(QvUoy{+y6M$K}s4`SW-A(=UHMkU#IspZDZX zUnKr(I*Y0?=%m!{>Zs6tKEwED7W>%vFK_^ghoKw0+D={N`$k9GshfS@*g0bVe32hHLuS@!bY>(5ZO}2T0-z*Z&$8TNCcqzK6&kZ#-&#y7s`Gl&8ilx^%BkC)Z+_QOxB*>gtfta29?Cdpy&lP0X zH^$PBN`cJNy73F4nrQP!*|%q)`1rLHj;bl5?{*g>r$@DY#*rv6t}T(7xcx*~M@~v; zY4fbiX;fWbhD!fLsZA3#n`ow#X5#a8TXT?L@$cTtdxd_VChuN(59{|LEorC4m*}@I zk0dwAWBmPYB)qy@fNIlOcT3jhp{!A4rFKt?e^ci!liY(tx$i-8@pp9AHInuIP}Tvm zs0il(`;V9z&}F|oV!s5>wX8R5y{7w@zHR=t(^n<)(-EgNrtS0<$#HG#omV8i=}4Z@ zt*XvtGS*@NNb=Sl$qk zXBmujsM6XTKTY+rliA^a!fqh$SUZT3fSr_@@4z;3B$i}mKP^vDb?sD|6O}SvWP*(C zD+sOBZa?B+a5uhxN<&2>Kc9d>44LQR9@><_)%1 z)a#M=3b*B@SO*9(VY4>GmwwCR%?S6aFWB-d z)zXjB@_n-d^?1_v&7JN06E~bSTl!PaA(@O9kNy0^7mW|o4H^3}y*v{v3e_d|ZIBaQu?J#N&+5 zq?gy5$>l5aJz%$Z~ygfHEEtzkYS6d#l!fub8tQqctNXn$=%&-<7h`IPxwkPuEo1MDN^j?Y+qr+fWVE`=GS-at1FfGhD~pYToqu&t z-y7`SIHCq~VU^ITS?Muj0cUrx!n4+8RCtgB1`-UjOnM1>gJDmdS?iBQ6Z6fb9*wf; z3p_MG~B5!|FU7QCtn(B(&PJeWHv3|VMJzUd+|s+ z)qWFHF0a>VVkDVSzgbZptXR0FFsep82>(UZZ@LlSQIC%Kh?fcA_XZ59pi5G*UD`dW|CWgHLgx$ix%ky1{8&6i`yAwA=YOHyb*pv}EHW`!4TrgljjUi9V%+&qV zxZJ#<|KtK{Tm!phm6d&f>7CHiyJ|AOnn*9<$nwI#Y?A?6EvC0wAWNet=j-(aws5mKi!i9*x;&5p4P9XaNZdjB|s z!E+$B-cyhqX*$~07dR4mzjW%#dO_eXJ8aXhdY+P$a~V4m<1k5B3QQk^UA?pYKz&0Z z-(C)P=g2IR^ooqT8BN=mQZ5$SzHe!PlqEvp2z5U#HZiW$Kq00>Y9J4xHDW)OvCt*P zrI0r?pY%fzODPLA9OJQq$4wbM(24eVeMZ=8wCK6-z5NL$wyYz^oQQz3Ob(wOYQm?+ zE@u7gX<9iWtuUaeb~}>+CK&Q0$Jaz-YSH+tX_s){ffPBP=Z6UWDcZ@k`S@U)iGbbW z=m;%6G)}i<4Ou>nFV*MIB6bb6vWT6S#!RbG`@MqL`t}2_yx)*;AL%}lr_qLgCR^qD zp(+8p%Ab zB&3&zHZTgzyi^CouFXG?z#J!Kj+u|UrRXkA4erE*jfJTJS7N4Bks8QPOn^WVBdkv$ zG|RnKnf&;zPns?xj3kz(dbgg%3X7>XMzO5W%gRJPr6OLG&Ur`|Ib7>i4trDu$Y@C6dC+R=b#%Spd*?p008&dBGR@!cGr+j)c9; z-jNGav(WIh0dO!HF?J@hfM*FmsPKW*2Ff}i! znV4-K;`4|}QtRg!Vz?iT3Y2DTzo_X7zj?pM=ttKro8D#I?@67@B+88AsQJ*+fsO0t}XWgewN+IVRiNsTQw8 ziY@YB)frA*Y=K7%lib`1lB+$&SUs%^JhGe~%tG7c<&=2DFC~H%tD9{5F3Yf4W}SW# zij8_O;%aJO*4-ntqe63k7F$}N!k3wUFo$M~-`K+>mUY`K43qRO=$tF34;Pxa_?qxc1_89Qx7mhg+{DnMLaof_~YCRj7f zabiB!c%ck!3j3|8=19@$3#!fKOT=o3*j5AGR}OML%s40wa!}tOP)9xKMg6IFOP0$N zABKujcX>*3sMXUP7_K)b2yEdV<3k))gAB`j-ut4SX9k#^hPo(c zLj{Bf^RziMB2uTC?`0AjQ(y3u45Kf4lr^^9+)xOWD_9UvceOF<+^iZzD!T22-uWz)j z%9XfWmng}WC|aJmCZ?(=1CC_E{l-@{z1c?KfK6aIHQPiGmr-s>ElPQa;r@vsEqIU^S z24wM?l|PzQ5s^{m(rofi--&dIvM{=#URMYu$B1oP>6u&MSz|Aaa=Kl-0;{oeMf^^t zbWRQ}UvAb0%JzxkZ}PSuKnf+a{{Op-ZpN|_qotqrkiyHScSDp&;Te@BkS4yWWuGj^ zbU%gZp4uI#GCKdc^ApD2++qao%rVZ-X*(h-_;o9xn284JLY7Bnr5nL2;+og=BEPX% zD+O4>7Qt!6EdWIEh{J1jb;D&z1gghpf><}g17;X7&+28BKspeK{|QKI)Qv!z8+NL% z#7qXD=k}Ay{AW8*|3b)3heWZ}sXL3E4#-{oOXD!zfsk41F_(K%4-6C}r|VI|=zl%~k$&8!o{Mh)qXHcwM5mVUQKrR19w8p2=5L*jWJ0Ow!NJpR;4WTA+FRJ zXY1_qEFX23D2qyuz5K6^L?N@7$`qm~D?N}KwpOLbx}^PZvIGg;?1&!)M=`bVg<(2H zG-B4&8RS#u33Y);_hU#Jc}@Dkt4D%?0=YpTm>aB8D!iJpL=T;37A+IVU~(7ZwT+!K zbgIcT)lrmP$m6%l+COY!+QZZm`%yoPC`!+3la8OmkjT#o@82n!w+o8g*3Jtf67k5Mpg+6VRK3 zCpiihkH*i^lXYb*KCUlBt^?5G0Uk5Sud0@dWlb5tK|OJCa=aD@TjG;-xqF16FHW^= zaU=`7!drs61_EPCG(J}sSVDmumhAb#St10P^w&kz<4|B@0|M_^`ym!@pjK2hX-mS7 zJN10ieHN_=a`D|8yO@fIYR^`Ts>gIsIgOB$$lw~6NmP9fjEW2OQ?- zp%QV8;?csSl_E#q2dCL;0jzqxfhn45e^yh84@--CY@0cvD7)G-^^J)+yynq{ELsyY zKvTAjUYVJ5((=FR&e@Soc$+_ps(1USJ7D3xz@4XF$G<(a9VDaFjH|_OR6Sy4GI}et zi1kcY*n1Cn(rX&MCVl5))6G)XD?HWYkJYIV<7o(XHzvm0iy-6J3S&SPLcgTPVRMrc zWh`#}Ql-2x>5FfY`G~1s)4_RyNbg7p8U*+{bydI4M^^4idYj$&P6kwz<{L!vcIVWj zaHnvdy;5rmVAj<|)c2VI!P&;i?S~UrVGN&4j6RF~XnZJZh4g}q;j#!8L8SEyHONBT zUdSbF+8UDVHup;h(zJ*`mQ`VK$>XJmb^F*dR0(Q9Se9ROn@+hxr+Cp=*RySym>A8> z{Oq-=F}qwm$S8`K z`aGDeKho6y%KJyy1bnskjh@GnlOMQ%oB^mo`#ZeNF1?T+56)LoYKV`uaMLXv3Hxb; zT6bh?wT`h@_jX}x%X#%|rzZ;iK=msiZI^gWC&CIfc8?mB}_px#b0{G!GZ9j z$b@&Z+&83e2k9>TCEq8|l*i}G&$>}{%A{gL`sP_|c=)ky)@LVGVihLxK89|ZowBF9 z6;Y7o=@Y`!gRXT9tgdoVxek^QX*@c9tv_?vL#*Mz*aJ-LC|!vtxlr#Gt}?ok{5sl|CDxOs!Tj%Yz9*KUMjOU8dE=D z=!j=&ph$)?@Zo;eIg5E@#H`~|*EtfQ_Dt(I;|zJ-0il(db zKJ3n1O_3X=(QrRI$VHT4P}Vc}!9IK$X_6BqtNx}HOU~RCE&mD-W8e~chQ8SoH67FY zQ_7o|6iKf{>fPs2UgTD$(yd9!m@-^J2=QZIb&#J~>&CgE4nT9+Fp#13Gp(ljys7nW zXKOQo4dSFR*Lx(wV5#sJAsK*MjtpaAqr+!P(df&E!N^{bdyiB2WWU%40#@l5bY*Kkeh`*fm+X+e^ zr;6Dkn?n?-E9;0+fKB)SZ6aIHCdYoBZa<3H!_z00mXYq^MXf|6typDUVsuaMkDzRB zrI}1JP^YaQvt7zSGwInbI6wV6sUK6uBrU#;T@A^-REblAicBfGp2{B80^JNkY`l$) z2ty#JJ!}Ow}`Ugl-x&gaXDl=>!UK-&&mkYW3gj7=)^c}Q$H@w>KUq?f zZt%n<{#q>GhbORaQGiqc$XAnju<8CGMOkOO9K%9XQ3~PkyO|#X6UlNU7?#E)pf zb{q_UPof>gJZM+YgEa@;5kIPPH_>5S@ko+77GM;*h;3l|GfxXS?|7H|-n+qo`Awo2Od}CmcSjE2%xk8aFz{kis+iyC% z7HEq>TMF7ygJc?X(`f(9wt3~uwMX3oY+2EVY}77N!R;&BJi*y53X!niF#A^s}N z*jmrN-YRzXYSj0}I&+SToFw~|W_oW#lU)2914(t1M^qu@vn~!ILE0kz1B1Y9!6pAO`NBheLbQGze#zW+A-`tk| zjrWKd_j7Q?itfk!h?x~t)3xxCtZ^E^X)x*@_QcnJAZ#}qoorR6GS1dWgJN_IR)fMopO{12GP(O@-5~oYZFL0iwTn7S%^!G zDv_PfBmS(>@uvk5LsM0=+Denb3P%-V7A$|>Dmv=#`~Fkz41=uoxFkkUQl{FyFd4kOEUOfvM2$W7`{QS_!`lhN0!O-KGfF@mo{@Fn z0Qtmz8v)<7OVq_nNrL19v6(HM=de)v-G2fc=Wikt)NHk!#`o}pMBB6wOGOq9QYtuoQ zy!L~I`d8k6WuIPnHYST7xR59^h)X!^?bqzz_n2|nXqU9>sjnBMW9021Ir?pMyk3v$ z^AORB!8q(8py=u+LT^Cwef=w)|C@%zi?^o+GCGfLvLm6;{A{(mv%h-|+XET+zppYf zTQn%YuBa-@{+d)pvrE^HgS>z4kjUPss?_>lpt=O1a9$zXM2qidsPRYmBa6pxN#l44 zB-XwMtVc4lAMGcvPJJYH)gIHWUx|2>paQn%@)MU@pG2a#>(pUN#16Yl2`=X^$1}P| zd;cDCyxwVv3uQh6hzC0d?yplxSoPyvuQA1lmOxK3vOAcxhDM;@kbC$gway#H5ORCO zU}R42ebugfPRnp2Pj_HcIg+DrNkJf*G-*Ks_HzOuU;6qU)#LAJcUnhwTDJ2WRZme% zx|C=DU1v}ZY!q}M2SUQLw^E-q@oy+i*gF91`P^Q)CPMx)`?(lm=K0XF7aB;>w$__!-}2f*#Do zh~Q``FRVexS5pU=*_{YM80@Knf<=q3;zqbOr`PK5WI|h~Jon<2He#z}O?OJS$!jn!b@)hIe8Z4#yUX znecsWZs`p>2FCDvzjaOz%l3{y;I`7Ftr&*)xtGOE=!&Tws0dpC6~@#?S4lbmjJ_5B z6Q%z^nTGn@G!*lelMWE`Zy_@w>WO+lEYI|>9T~ma=JR2#F4tN-s(w!?8f5yehWQ0Tu6TNL;zG0Bd2fwIVBY-HCD5je^Cf`X-jIrqmSZwXTp#TbUB} zE9CJ#K-XIrE`g#U5`Rh;|GrJI4Cmuk5d!bjA;EV{{flT^-e1&nj_cF6*%Z5zq;Jve zit0wpRX8f9l7c80C8E)ZQ%o3JL@>TRtxij=B~tAxl+%VoX*PE+1*PKWDJ#B#nA$++ z;(e1FNp zS8l^+yvouni5ShyZtr+c48lKB`eQK4nqN%drBV*x5$=%B9k8*&szcEtk+>kfp`N|k zL;$@lO~vz%Nk$*-Sxr={i>Yr=i=Q5TBtcyglra@EhILohsZz!x2fUoDP23cLwpi$n zj+f|mn&`mxtSloBPb@+O-M&By?iEp&b1Mvv`w}L|X5wMvuBI6p#>}N+HN(ef=vR{f zNEf|-lF)`1ViYIS6+mPk|C!kA4B?<>EJn9>j*b&x;e6z)UZCn;vlQ|;bSt7dDuB|y z;P`1|x*m|R>;73nK952)nyw}bjg(@9NsrG^MJVMgrUoPxQ$ON?t)2m`8N_JNIwq=i zy+}d5FnJb8Xc>b%IY!v$9o}`r{`g!lCrMFSr-(e>N-iD3uQc$nc@`Ccd;u4Dr0-iS z%BcYyp2u-^nfs}%W!k`}HOUQ{Gpu^Sk&rNf>>mopombo5UKC%jH=;fQOvgdP6-Lj`kNYHo+tI!Ue!%AAk80 za)|y=v^u65nJ_OcVX+baeKN+4NTCdWU1gPGjN_`X$=2qR6NyhQjeDn+}{OP<`t%&h;`#&vcQ;Sl@PG~Dw~(T0HIV& z_OnwU3BtF%1etN-KE6><#a~vWG;feG7 z6Pm+z(uC=tbZTI7qR>|6KAm$tIh-;$G-RihofOhiUy~|+qtn1LmTJjz&ZQwl6H_?- zp(X`)B_?$KwP4d**LM(Q{?`sc(5uE+Yl6<*^p>MzBd9AN*nRJXhEoU8tT6hs@?a3U z;D|(k*ev>oVf`USXWE z_6?JNHiyo)Yo|jcm>S}phYvNE$}aIz30j|9U6gb+S${N^%C5JDajB=N_g*>@F{7hz z3e1(It|l)*NQ;BJT8lTGImJ*Qqn`*F-RD)$5gOG{Zx(E+UpzDts^<3ae??=?wWYbd zlY5-T+XQ@#qQR?!J2$v8HWv9I*-azj9Ci;ikSEX(0#A}LLJW|BT zk@C5rX|0#U{{-;q(6o4lc&KJ#lGn1Gs?_5IOi6gk?xML5PHVY9Vs-|InCn{T*8$x4 z%=~0*_<(jGaB^G(D@`(QbmOY5%LYiMHWJ%1S%1uZ}Zqdd?_7$T(%RhG#>ZI36~s)qz|A7%!rC z8znYDykrHbfzi003nfCeTDH;6z$4{ZHgC&ZCX@@W2OhC%QUha>OaH0hB;-LRTSnTb zDy0Ev{W4(!n;~?4C`TOQY(ms)KK|$<#9~epLuwGC*BqZZJvuq1%jK03N;FhXiWFR? z$u8uvE=`>tBSl8pMHUlog`oM$WdZ~T1szVq{S|$ntj1%!tzJF@_jDpBc6O>Pdv5MX zx>C=T8)G&72$B2EYBnXkW=+6Y_?5FZS;^*wIg0}dk&?{nBF&ims+O~|hgbr!X3S6# z(z$$YQF(V}6rKv!$m?hOd|at zPtUsbnbP*NVAv)M>rTvqwM6SRyRD%8kRMa;0)^&EzfBuf$c##y=`WxFVbOl|W1cc= znHZ^xL$^pE6h01QYmChWQ;4b7dpmPE1QR^d>?p?>;ccG60M+3khSKs6fMZ2-W5unU z;z^G5ZL7RGxNE}_tBLb!<=*tXt!5q8w$>f4&d0x67y5fOIwK%b?QB!V|mx?lqlwbW{0{9$aPFJ>jUDa22>*n+?EmUQ%9(c z5UNvC=vYj!U9<{p62!S)Mw09^27rbnWQQ(X+*PRug-GhE3IUc)#kqakY+!fw0>XAc{>Ds+DkT0jXjqqM*brMKv0Eb-#qWATok@>o> z{I(aIRv~zNi!lz33k^@~IVLnGE>Lc4aR2?db|-Cqiu%aGx3aH4bF z5$aAr-3h20m-xM}@3Zb$V+_T^}kz<=O^9#%DyADJ#3e~(s`um z(Cer0o2zpi)i@IFsXr1v^vb^1pKE;mO{?^kePW&5Tq@gY>OBgGJQ;K3l~;N!4`&{` zUwPjeaim9r@w^X=ghY=tdaU~-gA?-O^!a!m0&IGhWjG^0ih;)%cmuItnh0-@7tRZZ z`x;xv2!(g67jOa_$1JZVvfuM!CqK;-&ci-gUG{U2jYyqZ(E}X3>&g?uVvt^6d0!Sv z63Z`Em1Z+hOih?!th<=I0cTgZTjuvjQ%_>vnlW;!b@P+F_wL~jFeHv?rW^qYUWXWI z;B;H}4sH{2L=5^~0?_r1z5mWT1>~F}MzX6KslDz-=HX%Hq01#316FyLOKdu8jutEz zK@`>%`tfNLw|;p2gmAO1-o_K|25AjfZ<=6y>Q9lzwKm#%#BPldEA3C~RY&C3}7C#m# zXDobQ{vr*5t-W9is@Os0%(_6jGGLA7^(Xq(FJcsT{oj#FVSf>A%6v13p`e&W97&`6 zn<800GpeCJc>}tRQ!siVt5x@ew)6yMVNz zP9p)(jXkC7FVvIE< z>qQ>PS*LNLXt|&33i#IY+xpe1N&ault>{-j`t?U_b0JDIYXdQLG1FY%*cH-R2tFC! z=X#h7zI=_%JCx=5HlLi99-?+JL$yikQGHS=wx)o6aiMQ*E5qOSdHb)b6+%Ovok*AviB` zEtHO}u72dhQ|WpqfivQ_;ea(>66kBAKZ+@csq{nLSiqX1Z9@{iL4sL{>jJzWioS@j z1denSD_?v8wk>7{#GsLN*sF+#JG@h{L_RQ=Z<#q)n&leU+K;YQSL3?eE{-w_OL zh(3pv=i#oVpFXoaHn$K~I z67}Sq#27#qeT{3F3k&|i>P~o#C?6b`Yw0t`6Y1JEZGa@eG9Iei|@+-+|}o3~a54sz2gj>urao z>_Qe`*%2nQWAXpesQe*=Vu6R}b;|FDb@O#SfD*m_`IHU5H7foyst6p`Mba_51Say- z?rc(gubpYb9DIR}2^WGkWKna=E_y+9J#A@Cg1_y?O!6B$UZx7e7N1%={P>R7d6lKi z6})GX-`%0?>~j73gB>UA*E0ROb%*<;uI%6y<Znh_1 ze46BmJUOPDx+A9clc}$7^TlWDis-<QeWN^YINUP<@@I%i*kT}rtc;0K5WrOxmt}ww}7wf&8;~<};39-bEV(RUzHt_*I7ovx+kM5<101>gC%Cu?O5mzYb-19*7=!@S z_RCKIL~oM(_1D={q3_YzVX}{BgHGCa{s*wK+hxsp5%nqpfB*n^mDnp58}eJJ$+TFX zlbX|>NsQ7F8%q%$)(HLhH7!THQb1zj<+Q;Zy2)-lJpydTt0K6w2Q0GjP8j@6)+T04 zOF{w-Hq##)8n7^DME3RG28qb}C+nPz*F&XI6$01U!&Ln|Mv3v!*vDYvSGK3&xSM~I zW5-XM1#A?qW?ng6@;g$wWCv0AbGK3b=K1V*zxJ|URk?!zF0Qo_|o05G#7$3So|Q(Dbo9SKzo-E5bx z?a?^EQztTew-^9Mm#U*2ETR_jtA)@7^9A0-xaQIE&3a-VdPdKg#i;=1qNaJvNd?2c&Yay5kWNZml?{b0 z$JZpAwNGeT^G4Kbv^tbGQvH;7sz~koo`M2b^;R=^ADahTcWz)Fy4p98Ehq!GR}Vlo z?dwU-R_P_ueZhlsf12eMchEeB^Cj%+u1Xx`GMT!ZhhxM(jp@e4lQl-fqNlR%&HGRQ@ArRV(_p?cp#-Z2i z;&~s6p!=lm$IpElzszK2gxfUfu7Fln)$0J($}Gf$^4kOTwQqxB*>)dReg@@*)G0G~ z03zPQ$|+vXA2EMlM#f~#-)ykB?DrZkibSt*bLr{>-0tNu^Yrar`LcMraPHzjl7rtx z#sV)lGjTwDfp>;n371!GA2VWq)b`kSQoMcN`T)Kw{Hx_$wG+@+qqx{(v5T_vgd;Z4 zzApx9U_G>mU&j?|1i8x&b5?!XLJhvrI?re=-?DN}j%7vlnB@z_YL`bg_vYlD&GLEr zQMc}=b=`C<(0Y-%a$4}!`zD$d?i=juo06^>ivp>4i`w^Xe5ZY1@&dW)ZRdScr|ZUK!Bec=@sKJAke?-IJreQ(^;Be$SsuY{6&2ZyeN zl3hh(zaZrRgxt<;q={m^;FN$nHUWr8*?Mty4%wxgId>(82U&VqE)LhvaQ&tPa^Swb zxnYm*QGHkg_s4g**^woYY`_j(TEVgpF$MQr80(z=57ecXdHJ9WyHm}p%gSB@oTT9?U|aM`}#&XyAE0=9J%p0;<+anYkyp!bS* zZjj3f&byIJ>!R7>!0a>E&rDZ|LiTM7EuMGU_hr#tzHN&a;T`pDi{4=TDfN!ic`Ufo z_q8eHoa}TbxNpO^d8{rLst$%qeP46)Vuoe~2gvL2wf_U1!Un-Z@9}Gfyyn8~5;%|= znBr@Hm87h0=l-W)t@5P{soWJlBt&E`=6EvKs~JmsOz$RRFSU&DYh0d~-N{L{ympxp z?)H88-)IKt7W=mC42HY6)E@Mu+oZGbYrbu55}&Xo+>z>$OY#y;YewQROeA{$LI+&X z+hP2vvv~^Loj^ppX9msILL+9 zm>b_?aAz>-@jV7}`ZmkiLSQgzkM=rwgy^b+dwNeu@sOwU9d~L&fyeFJ{5A=_Kj#k^ zJZRQ-S3rO1$9NCcdVKA2ey$f~@0goB)GM3=unylttvs3GZgc6O^i3YlkA)9Pt+<^E z9=KD~_f=EBDhR%}jmK>Jshut<%T4+YS5j?i@re6Bz09e7;=!fL_n#X2W#rFY+UNUP zmt>dAGpQMA=LEGgOAi572H6S9L*7hct6RBP5@F{hFX=r%34uB> z(F`9lrKU3@94NK7k;j;2s9$|ep7o6OzR2S;Snr{|k`|`Qtc<=PoTD$Jv*Qp(ZMk2g zqy2jn%Y^BBW_2*EdbjdAckS$!6>=k&`$^xn>T1ZtxfkkasnYQ>!9Q&*B`F<1jY(vY zo?W*bf$IPd$bGCM7Q5&*+}0^e~;=n6;aTEZsImzV6bL}u!JL^YR^U=f)10o1PA zY$QwKLSsKy`?MWBM?gqwgWuyuL%X>`*UvpZ9guW3nuVo^c+zwic-Z?0Xh9UA`$Sp! z+TUUTwi5KUza-D~BDz)PLv6CBE5&hg5_c^l)0UKxml(NyC(ZXhLFt%Y)<41;r-4{{ z;W9RdzeWaZb(BIv47Wv-91t;N=$MrQUx*yHM8X zW}6x^Q{ri*3g!PS<*!E%qAGqPBLlGorgtm%&4}>yZ2yZCm@>Tb%SbM}@)60yZ#YaD zX>}m+8QJ;6@1H#y-!|tn$qO^+zFGp6*vaF*fPO9DiiTv%?@MBbYo9BUq z%sPKWjb>Pkr}s^|#WIw|XJV8PBzOD#0e8Gn73lo~d3593R#9kF74?dMkd2-`#{5;u zO7^9ReOme&2jI2EH70At6{K1N4(?|b^6t|)9y=%D=JJUe>ZRT%7>kQcutbHoE@=}WLkM(iy+z!ChSO!Bo(F)wx*$0Abe`y$wkoehLt zRAB-nCuL`vX$C%r#jQC#@*wQKRJ8>iy~~6k(Q)CU&3EA3SATw4JHS>Y%7t!(-YPuF z5k!nhHA}AgE8qb{8(d#UX7P*)Zn^buMP&1``E*b$keS5%&ni*a#?2fcS8Eo%Y8qB24z7k zQ^t(YguF1})=R=5+^RH-G{CWka2}M;fQuC?vrwAc2sva9+6N`fss!wGr@5}gxhK`? zDM*ZL&aYF;Ff#}>;bN!-{pp5&lY5b~w{y!q%?mtcfi^PbiZ?bR&8p(*d$WkCQ`L5t zRq(C8c&*NZbGc5vZu@)XbeEVZ!vNu8P%RA>V(nrNd+XGXqo}&@ljbTSro60KG#OQ7 z3G6z(899Y5hUR(L^W7@{1k+kxl5TnmRzOl-RjW~zNj@2i#y<`1*C~%q;$8zc%s#AZ zgoL=uxIoLqf54^65=6%fZfRb)gDY=NMP71jO?&~(aCV2fI<8V0jY9)asvCr+;y?m z%bw$dOuFyap-+L$J)xlU4kI)h7HFoIu24cFE5!Z3tK})0iPRIguC~xTOLHhQ8>Ex2 zEHED|L0zUR3x$Av>n=o0VZlT&+~fzuR8c$eY-$S{} zW3=l)%m8jloEhxgSm-2r#%p;vweQ@^Q&Vp#;ET^&?wp=AUrz`EEUMC=$-3Ct0ruoW zA)6l5lhsh)Iyye`l(6z<3_2(#6yRPyL3K7dS_>lCOyJ5xR_ft4`}|PbgF*D(CZWY> zz$7{~RAR327=NDLH65R@munD+QQj<@rs0RtAmO8ef6F%`I&H{xf&V##oSa{d^hSjQ z*vZng0TyXAUy?x^&NJqfn<_K4ZLYN?GRH!Wt357e>_N|Ym&|Bj3GS6^zfP9V$xGZ~ zoS9LN%U+1;DjFBLj{se6Ut6#5oxJws0t#M5fE3qW1j-YaQQ9p+Ap^!}it@Ut(U3J> zqD4j|P+`3ydglHT=Nr-&kC8ySLKzEmvx^vWzsc*;KFK2J17}pliT0Dmf}+Hgh`wMK zcG%id<<>CcKV4&vHk|((YPoc z`)0uMAvXzM-;r>x7LRBz-)31QH_M@4&wq&y=`;GVL}pZ#FfMVR0ZPIbzW@@T$WlyZ z!Ox8z<9TE0-r(Pdf1;rO@-Z`%Q* z_Oj@Jy|0JZJAlxll(hXS&vhK-cD95Vk!JEEtqx03HQvv zz|$Y5FqZC@tBLNx7H(*EmYvKb_t&XE69Iy61RTyy?r)jb74DF}&G7z8{8BLhoKEm? zx2vh6PPzDqR4UxT#e>9yQ+g0p8c`db$2mQPS*cTxt=HNc-Pg_OE@~YP@23(lW0Mfa z-+6nFo7Rn{7iKi|;G7}!tTxH3UL%i-d@fs(?{sEox}g5Fk2Q$)2@OG`F6uHlNjT>NQo_k{E8 zNe#kMukaXxm4Js#dO<&m;03{>G@ZE8TC^ubu6&M0FSm-86H&&6 zR}yv+JBSa|%pmmBzNSBGILDMvjK39ffj=14_|AE5ZLHQpRiT1;&ct{QJD`H51w(;E z;f^veYWW03g6#Xo?GO~R;cCa&%C>JgQzoe6zfXzI-XDoOQ*svVTTFw-h_NRz8T*4M zLrjb!QbC-+$;Igv9?R|9wzfbz&=Vj{a|5IHY=|KUy7`D;<)`ilR?fa}bi9tO)d z2_37&fz4i7n6kvzGgx6Z1zamj*vA`S;e%D%A5*u;GBVtEhe3Ol)0_haAArG{_Zl40 zs4T+!V{NGn61*^9kD4B)c}eYp)UYa!#*2&tGwV0NkuN(b0T8U>MMX$P(FHw9O9ek*=`# z@PG>}c5ABymz-xts3ao6%JF&aGWqLK_yqBAb=Ogr)*RcOBV3f7+uRP6*j6;1deHA6 zY<(?9Kp0~bQ6wR!IiqQLjgCRs5+*t(qu3j_Pc4zm2vHDc)8Vsu;Rm?T3QrY}s)f>K z)jWBWie^>0JYLG9Z(CKhJUi^?8{}CwsYzcX&&qyYEYGj;%xs%=GfiADZgraXdUMsZ zMl!M75AoW5xb^nz9yN^&N8gm8i)JHxfZP!n#&M)i38;EtthFwUK;l z>Jirt(IH%}PDe+Y`c$R#K}@+WBYTml@KOJ=Q#~~Yd9~YlijextaQ^}xeqr7O_mE@BZN`x9OO#)s9O31h;w!+0ay~QtH#n6udq+q z0riG}hhN$zl^iz=U;m=kqbPm}5J27)U5nXCpbrsF8wun^yAUo3k07$c-Yua*VXW?) zuN5;x6O>LEr+m`3gc9raZ+?6P3JLcN5Jm1*MAuOlyJSzG{yUQAN0bTAXcXL9DLn`Y% zZEv}@MDoq?hCdx!Z@A>9{%?uXki`UskMSjW9-K6@ns3Chd2K9*5B)}UKx-C_pOByL zIO*FnUxJAkU5WNz{N$RP*43+8{XvT=y$q>}qup- zt;}Z_Be4AcgXXMhMn7h>dXQ)NnjEJb_1DcR*+%ymj3eZsWO<**iC5I=9Yp#{ z%4|CYXVFchNW>S3)1YoS7#ma9XN%3UixGu=MXuOgp*da(aY@02*+Qjup<-gcbA_6h zprcQh4Tncu=MS*{!Lyv;Byb0P!AKUw(~)+!%lL>Ru0iUkoz&B=6Ii(pF90JUGJE}; zvcthMEz`~NqTp)x#!Jc$%uVEZT1MS~ly%FOsVsjewbM(Hf?y)g-TG!WCQ7(CwZ6!O zPHQdfDlZyEPqr09bPdqBje~8U%|DxBV|Jim-M#FdY{ix_Ydmd>^W|Q`(fW%Ug$O$W z^u%GU-*2#XoA}K*mE7tx{ewk*ND(+szC-p2sn4f9Doe$f;v5~nMb;Sod$(1fe-|1n zOLO8upXD9&Ag=eD>sFgnxLk1!yU+Qu0qPnR_9m`5xzm%p66FA4UV5^;z@5BEq+*3X z^W<(pVII?`UV20?reIYWgvWT@dKv}m!p+2z1@-IFsRd@$EQ+{|$<_#bPA*=JB4gDo zYs)`bN2$Jmxw@39$Od=YvV1|6Z?mZXjcj@6CyIN0=zJoQeVcD&bu0JiZeGB`tyhjN zXE%6YNwcyjCz>ltIgKH=K5Ep7{#SR3wy2bWwJJdATtqR)KYQ zvcW8BzKhi!afiRx{&#!hkgI(An(yP@EfQf~v!%~YAJNqyPJl}hRrye!xwNnIO;_7n zfAj41?A#K|7vL&pVlNtNX6oGz`GRMvMcI*1zV=@Lg}f%OHdhvFnLOQiqw{ZW!rfCF z3L%2AW}t9rD375>7ND(gh6 z1*P}oq}0YDX|v9zERCK;WNQ#6>htD)bJ9?MS4!SJJtfk9@^P%;QH<5yc1Qjh>3l~C z(6jhB_!c8iFZ7r6-X<;xnGXzoCc-9Z-OEuHVr{K_j^e5^ehVFm6~^(@gCzn?v}mZq z-%$)&D6C=zFkz2a@~MuY?8nG1JFM$ZHg9??FqE@7TQS6m+P9@6X(cgcP#*N|g{X!^ zLazk-n3Z=!3V33XP)6n5#@cBzvQ%pX?3uA@-MHMfJdL)^Rz5s2&0H2RT8;+yt{Y=U zJi(XO<{2-Gta;yDSu(fj=-Mf%2bnUzIerMPK3OI1^3PrAv3i1M)``y=q&Y~kKvMuW z*0NbMDyPj@S;A7V^Nf4bDe*dPTykf}Px1Z+_qJS^I^}PjVC>cR^cXKc@*y8DQ(xd_ zl2!HQGCZepS*yb?*Em$};3FTNq1;XHvZ>W@S8NtrWa5ftlGgV=O9iu%?GaR2saaV9 zW4POH$v<0mc<#z+Eti)Gv;0rQcL_rY;cTPShS#KUDaKP}F_u%Vg^=~Gq|a`d%r}h8 zz{BVB;hU_8LspNJnA37W*#Wo~0XtY18B4~7-n*8~aha^SV$)oeGt+^suGlQ+i>_s* zX|4tV@{eu&^7$I9`P74@Y&kMjKLkeS9_xd_i!-9~3AKap+$mS<^wcR<=*a>ZtcBaF zJfE#VS8{afRAF*7*DZ~BoR7!owZ{r$e|D=KS#Dn(Aq(&p+?D-$S&29Eg4r(U3Fg-b zy)gyhZvHI*wmsMrMhCzb^Q)>}YDs!g8C1d@e}@+-k&pq5OcIIjmapG4leY1K4ez{adQ?@$yJK zQVQf~&C&u8gKA}+M{WE!fdo$qeuG;XbWo$Be@plSv!-0!*XDwSMyxEs>zHsC<~MC2LW= zNivJARj9`U3at0D5scUfd?Yl=rgA!3yeAY0VKp>=q~r@8bMc(FT*$&@d{y&DBs-6( zKa*Ua+T$i`19tx49-17#;cKbicx zHs}V$5h8$9-0IYNEvF(wa;i--D%taiix7(3GFU^7UNgLV7~Y=H1m}=tkm@ToSBt4z z)`0?>kUH)5efdRXI3I%AE5-`iQ#?Ms&xrDEg~!G)G<%)UdmVIWKK50?dF6WUwwcb% z{6?iwd)9d1+QHvszSU%WJOnS~DkzS@dgJqhh+byn9E04OAMRdToH|wBI_W%Bg(pq@%Pk0_ zRc2`3|Hat5z(-Y_jsLl9V3FW15+q8Lpr9b231TG%B;-Z}(Ism(7fhn9+HJg)$}XUg z3*BtxxNd7(seN1NYg=vY``VVaf;1J%%>*fJ5quRvG%B{UJ=m^FF$T^4zt5brxlr); z$B)@_=FB|v%ri63%slg4LOn$*t}?n_{uicup`OoHW>qJa&q*wwC7CX}Omia>saRZP z@WS2cEMxT?@MAMnI<>5O%cIhus_v=23NtU%!j!#8u)BrUM9M2dJ$I}a7E*W6@-27J z26Gf8Q@YA!$w1R&(2TSd6&l@lFTdp+9e0gU*2)Flw34FKk{h9TH)CrmlC`lZfy=@t zTi=hA+(?wOxf1Ver8D~V6bIlZ7~Y~8@?@0UXc7{#qC;vmUd})8@dvJ7mw1Sq)-P_= zQq2Z808>|9W*@SmB{@b(?inXKQ#6gdD5Eh@Zvhwc10IghPK}bJUawQnQmcL7OEPShhqD7pk^hSsry}#GaIP?xOwr&ZooiEB z2y%?kMun`NtY7QqXNW{$zQa2B*y7vBO`LgiIH=c>%}Z4~VGHwN3v)FhQ(+EmdmXHN z`j|5%!m7Z`&po-*5g0Ek(_98OOxtAlHb0BC(q1S3Lwcg+C!x8MdP~q+le+(q%VS`w z*tZj2~gDdzwE2Smf{>t6z{06@Jc_{{5#!R=tqAL|7-d~zW#tJx4I1g zTNq6PQVUBkiIcyD6d@3TcPLb@ohd>KFax;v8=5*2e|7J7Mg62FxqirUCLS_gUr%0s z%O1kY=`eL0owixiaGD`Dr6Y^d>0L+ZI!vsu=M>R2 z(%%gnlrS9r<2>LOh_Cb!wd+fpBnm8Hx<QazGifp^Nqcd5) z=@$Veo%j#PmB@q0L8#j$^p_1#{sG(O9cyGXML|qg5sR~;{|AVlCqdl*+cSY+oAcNJ zgkM<@*q;hHj?xbQRPdBeY?1Edw>c~ubZ=sRQuv+h!)w3Jhaxe=P4nwu_3AYlb;PMS zq`oW5qK*S#J_&|W#BL*A<3W&I5M2wpnrShhZEd0#gRspfQvRS_-`Ocs6@pD`ZSm$C1Hc zO&nMl>*cONEoT7ME{$%of*93)4=9`D1NNvA_{O8=g9O|K6mos)+rp9U2qx-+wRX>1 z5+rlB6@}R{CN%a`1ctZa%J7_kGga4X{vcid`YH_lt4aa(A2;O$z_4;{lj@U^aPXI2 zZLAlx)lOl&HFi_fGr*`TcymRX@unR^MOf7Pzr;131_S0(cfkz{xFX|dAbRt=t+M$i z@e=@NuSJA-t^_q&$iS8U3S?*%{GlVbJjwJP5w<%`_H; z^SBDbE|54Di(SP+ddKf)oJC8k_jg(ZMZz)7k1TLhaI{?DASBOl*XHaZW4MW*t9;s8$7u1Y=bvSNTEl>1@qU}e zpqC5-J;h?MJw_&M^98@h!7mo!RZbmL_%|xB=I5?JM%(Nw0;Bz_!B~Hr$x_8P*R~S& zx%UL-O7!pB1M|f@;(fuzI%@I5!! zQ8`*{;zvIy)9XjfIBJZP9h=r49dXxbfiH$L!C(sZv^j(Z=S6_FS0C0SF)rXcV^5}) zd>7x1vVO|O%=Tpb95h3|N@ySU0~!qs$7fBCZ{{Wo8a=g8p)&W-pCbG_R2uN;~ab3pn>(^{|o(ErN_gNF9cSXk0P|1@&=9ojzef)^a#U-Fk-8kU)sUJoGIBUZTeBw^=fNt&-j=|#q3(B&WWogSlAS?QF=xd;R zMIJiOr7P3rPNFCj!nk|C+5s-pxRZ2 zc25_#271qx^lVf>)u+*B5d*-Z;%M;IN_M-jwS%H;Y?u_W_EF$-4fw#~$gP86{zvXZ zA6ayhYge|rn15bN2_u_m1LMTF`zD4_wY_jH(yU%bNxYV1nR2sP*U?Is7>c(s)BC$K zc>)jz1s-)7QW>e+C=| zoWD)Bz^utT;G?{ANu9C*uLA4{)FM{wab;N@!=1+Cu?BX+w~LPf?MeyMpF* zY^4qeFAyce`I=~xy(^)x7t?e5(jGDPe31hAwi#pnt>i>Kf?NkO+W*Js#3zkx((l~uJYUoplg)RMn9yh zTno#)${o^zu5xPyEiY=y)l{uiGscjW3y(Q00TJJ9nwl#;ea1$;|3l1gKn^KvKNG;A zZeFGG{*>lg!F%&ilsKe@L+-OS@@e4cM(Rxl6RbaiP|=)q2VI-F_{E6Dflw_GIA?Z_ zGx+aySxz%@-Od{n)orqfMTv|iKb7hpDwjze z{8^~5G4Nv@nd{GVIy<-0jw|U0F{pkQQlbYo2eC}k#WFTUo%B{!^pspEdP|KR;g>dE z8OVsFx;Ce*D=Bqt+wR(SKnng31w{o&7rlz2)YK5X#I?;^+H@t~ALctp5t--yKj`t* z#V!l@1KnFK_gfLhInI=3f!JB8eSm9hd%Vx(9$)C>_`Li-A-|?mx%uaULL(kX`*{DL zi7QTrG<$S&EdB#n4OBdS&_v4~c#c(QiY^?$st95;pClY~w{t8wplR+&@=4 zo1m1~e*T1dmIiV|J@tX6*bZL(GMB=ME&Cu4v6tWF$79Y1mV{w1hzB?7wq_4O@ibnr znY$O=R*vOso0j@dX<9Gs)wGV?!^i@hn9XzA3D@(gLB7cM{#Luf=aF8i?gkWOHXAz5 z0XK^MH@Wz^oBHt=5S}2VOXk?osbg+l@yRqo0}KoXcsjp2FNtGJH2jQ_l)5#LaiPSOuJ#!^ZH}w`2c*UR zpx>6d+D{7fb3=*iUF}EZ!%y^w7FYW;0rVJe(B@_^*0Htvsd;+i$D;Ue58fGpb2?@K zHTLo_xZv5i^s(>RJsSFCX)vEUUS?p4rPZA90lWRHF03>Tv2I=^+@iln8e)LBoRIID7dxLV}3lh@cOq>4oyOKE_s z-9-!h7ef|*PC^!se{e*S#r15^T|iGx;f}ciBD@a@o;Ckl>un-*WDD(|{JgxsBQF7H zWjUPNA?jVO@UqX#A0oeQX~%^EyY%z&R+G0M@9Q<}tk27Tc|YD42yEr}oHm|%Y!Kcp z8upgY%l{|xTX>Hb*pHBxBl98E!{O$vg!~?bbOQ=LakYO)Ibl)f1ae!QMS-l?uStMo z1xAN@ZVgO|J;|%)NoI!Cx!38=9YqCnZcH0+FPa@mtuRt$Q!<}TiMz;{&!%LFnn6Vp zZ;l8CUkog4oe=6hC(zoOS?p>L3bqFABep@{o>1@Yfr(~wsQ3OracgF%_pZR@q2Buf z)6J$(Zy-<*>Rl1I-fY+=_ua8fN^X+_SS(GRGUcgfrX#E@-6;-d>l`3C0SU5R5PTrLKlXAfy=IQRvbD(p2 zdUVy*#mAG{=c6d}G6jkFG#?y>V>etP!+%+}9?c@Z%6!qjmWts(a`sL!q^C;2{JMwNu*TDy}l=_#=n z`1AkjnD4U#YG@=>r0idiV@SCWj~%j*#vV*UI4#`mDm&b5wW^UF6a)`kepXuC`E7ev z#=eKx;s1ruZ~#ik)&N%g!YH>uunyVu#NG$3L8XW6)6&VnK23gzpUtkKv5rA7GIDjO zuhA9$7A19`P2er5_P+5h-92SBU{0r%BF3ZBa!D-H&BDI zllx9CFf!Y>YaP?*qjF?4^SH|}+#MIm^Q5z4aV=PA-fMg)Cm7q$q9t$Ol!2}a_Mda5 zcL)@O8AQpXt`h46f#Hx??xIaG9s5{wRMeXd{YE0rAfL4xm$OvVJB8#EEH7LslRQMf zTr$XAU(V`8;`O(YSP}N}ZfG|6N++48=RqygQdRVr&iTaIa{k568A;~6|79KQ0lQ2&pTt8!rF0*s)O6J^qww#|LHv~U5HJS6tv*kQs z=lmC2Hr=8XXUqAFopVz%=Z$B~DUL0zoYGOt?Lx~aR8yxbz~=1C&0ApSot3P>{K{E! zUS#Kdm`#R;x#NsEK}E!%k}vO3~6 z9;i=8L_-1ptF7!(a*AE@X?7eoDfjx%LZD9(oC)H;ND}DDv*kQs=lp6i=ZdrCe8$ds z4VY`{-FUW~E#!RT8fR*$m2cA7^7$m+D0ed7r(Zs;HN>&UEZ^>nsdn{0lZ~aWe;@hj z6|Z{JE;zIgpx#Q#;Cxr8_m348MXD)ob$;~p>Nt3aYFG;1{)4Dl{N{Efc&QRi_E(gJ zUDEvpr&=#Ur(=TvD?46uH1<|^-H%pe@Wo6!dZ@&FD|zpc2XOeKmhC51XHJ8^q#yoG zgW*pUoe>NE>o3(DeVZ^8uMer?NV1N7cb%~gNY+gFHg;zn1c*Y52M^_yM;wz5T~i9B zjX9IV3hI^HkgSS3w%pbcEM~440zr|_1s2Cd2iK}1_TN0p#rRd=V?~Q3$ZIxGB=N%g zHC4QTCaClqi;o|zIxb`G-|Pe4$fzKf3FWKRhe7C=Cc$PUm?nauf=**siw%c6_5)wj zaHf6~ElKqZ0L#*NJ_E3&HFkLFH+KM^W%LSQS1Ui|%yX;Ni+Aesn$&|1{g!|7AXPE~ z)G{_|FXiK$%GNN*O~FN~2RL|*U|#u8O&n3=&z@0w3$D6>=dY4RBC{0^f$Z0xk`iwf zk#miFtg%1d%SZnvHajBxeSjX9ob~oc&yXoCs~C0r+xRNh8xuPaAX@k!Ruo?xS)wk9 zQyUQ@VliptO9zen_nANFikNCZdZ{KR#KoW93EMFjTo+zwTtCD1X7ST3t^Durx7#pTHxYR^7&h$v%+wR!6r6 z_#jscq1|ojVOW>k!1&v!fJjMLcllG!W52$Z4-CVRs1tqV4r!X{^7^yWO1T2t!#k5N z2~|4KKb0?5{~lW=Bh%LW2n_S61wgX2VK7wecU)nGiSo~+oPQ2~JnCy2tlL)0d7N&o zZIo_&q|v?r<~o=Qh%YjHjILVq4jBv^5vMfPG&$IR+-s$G(GE^ff{zX9erq&Jb_2dw zTjLw36%X$yph7VDdZIzvX_xw#sZ4~}c`3oMK2^%prQCYSEXaVbB4!^bHudVZY+pqpvFES{1ExG^vj0 zNffx;r)p@pT<|QQ$&0qkr+tBpE#*QvpX%b9t?-o;+``%Rt>2pTMy>`m^)Jcl*QCneQ zFula=$=}{qm=d_UsnDw?McEh&h{m8BFUDM7-Q?4exLYHdQ%eX+;-04wy;EPU-_5;6cYFy$Nd<4;in$;@1 zK`P9IC_NevRX2cvz9b0D)i=;$ZY*f@=%9&^%KC`BHccbcqpHZ^_4&JK4BU>vs&fdl z?LYW%b;FrNciU3X^S>tz@3ikB?+}8)N4~(wkP9)az()DsCx=)4f*}HTkgwO#3QV-zF*cV9ie@S3k7pvd3;oM38Lw~D5ck3dDSn%MX zDSxDhHF3SF(dvn6VR*;vqE}c&X(cKS^5H8Qr%aE}AJu@q1t7)?_r>)T=L(q_Q7KFV zgx{x)Oe`$x#5)h?3xILXt;01Nt$YVIid_gWGiuw}~X=DqT~ zjc4mBzKl#<#qX7}e7Il35~o|gha#+F8YmxXtq)CC`qZs9Pr-OHZl)H=EG165)!F_( z+kmFp`N8KMTGbE@wlgW9>pM?r>eH%;KL4w_Rc}9=Owx<<1ws_zZ=?K&^miXyu-b;E zzMJvh0W2#7^?JIawqcuW>;>`TRq~;ruk})2xmSIaY-mf7ey3GH&*S&3jyGQ)vX1I0 zmm#RR7@+bSfq<4`-bGdjG3-V?tXXn!1XQ~P&R|aUJ1OkX&= z1Awe4YW^d#rQp(duB5P|6wl>^M8B0tA_!`QiYPIS1{lHNWD-s36kc#83Bppn>P0<9 z#NpcWaCdtPbNm*$K-QD{i?n>u7EP}P`A77|$fw=~B5V1Je=K4Rxs;~(^8_&4i)osI zx&BYt7W=Ou5k+_5XK0?hLOV3m!TAf1O&ETmJE=#{l+fHm6u_VX^|e_WHod``4&F2t$}ouodV8!Rgi+$2?i3`9RCAQ zk3REn2Yt!+5gb9XEc6atr~3t9Rk)cKwbf9MYsIjl`7bN;--O39cCN^bX8l=&<{{-A zv9sRTZ@Y%`;{Ed=hM1x534xKJcZNqR645&6!FNTWjiBXw6B?S1thmA%+UeY(oaP7k z>+Yr%_4xPORfUTlt&%0y5?*KF5@+xWq2AQM<*gVcZ%hqLH7_ERY9Kq*n;slB%qY#HrHn(~tL5ENFsXM-oM1x&v*%mG#=a7M%AP_0r zA}mvmdq0~lHCy_k*xbtTEOMIvn_mlCV|AgQqWH4HtgwEIrLuWf;47_@LOov!l#BD- z$sKQlldF9zFhpB{L;oFtc`@XKJS__>3-v5_g<;$%tCUCBG3O1YVfMN)@lM6ht)mIK&WkA)LFKBa-?btZU|DNRl7UOI&mleB1F;=SVCnl zq(y>TLY--`UsHf&9Og)TDItb9_95njqk?nr3v$14@-Cc?-T8t;zLdQn(&{4d7P1sg zCfjWu9#`dXD`cMol%$pq0E8>!)sw;I+B|H|++eO(yidz^|HC$L*0&~|r57edwMX~= zoTA{>pO*7M2Usya{_s}>fcg04km-!hPZS(lG1~t=O`duvwX;$+GB%es65h~k@P9yR z$Q%u~kO9lgK(m?Ccf2cY>GHt+UB!;C-1p_&ySj>m)3jrInGVQdT;`OS52hE5NS7iTXg5i?NjI2 zr@mbtl>kw#5`?QUJ!^ZkRdj3tlL*8IZHWJm)tf_Zr)&sc(kpp3hoN6n-qI;T_uc}a z4==G{NdU-wqxDKESNBV1HmIZg59!8z*1(dO$zf<8yjfFX;5at06Tj8kHh7em39Z|< zf$T%trkp^wgfYl46KC;!}r!z%O5#D7M2(8!A{MWbkeF`ITJs+0ey0(3p zcNyVNd3^ns^7qJw9{nsIBVl<+K$)AfMDb~W^ZDpqvMG6`m}y^cD!Z1 zir6HvGPC2D_4*L6xO?hQycUVN(+Zt{qqp3Sf0CAQ&u~kBszIk`cYKu0xL7CTbo3+> zglj|=3?6T1+22x>Y(H!2 z+OAIte7WuV$yoTeq^1O?Ma#OQ>m^r#&U^pxO0|(g5$5>kM|8?McS8S~SC&UNILHW> zs$N(2UbO6($G-QIVXr1j|`W`|y%C%%;j+{A@ z3ZogKD?QFc8^S%3Ois_qOY7N57LCxth8%um3VxL|i0C5@or5HG1)z|AdsPJqIgjUT-31;CH{1Khqtsd9e-R9UOm5F6@{J*l3+jMdkLQH%xsfVe9 z#sf)qy#*e3a3oeYZzmEuu+dA#o?WDYQD9iT4&P^W2>N~RF|WOeUxL#Ek&xms${F0K+^o24H5JE)grBPd02py1VJ{D@~p7G-xioIp+?jAUlPt7|^c zfN+KE&AxkkrjhABIUh^u^G}v!I0Eh^>`T0fWwX?%oq902p0CcsXDqSW?lP#pOx?)?Q0Nu;>FNXKgawmSwePW#|^8hUC%#ulYqtVM}7S61DK#^7v`h|-wVHF z{ApFe5~HUBExQyYCT_URPH7B|$3s4C!_!=`v*1vR1IOWU-W{FABT{wVn5bZ5hlnP8 zIU%~>-y$b+>bh%P?f;OlNbs4dL&HpgKR4%&xz$Z zi4|_u%=|;WLyv?>1y#rKqKGr}#m6}*03>C8!OJ+qUh_{VS|L8KaU78Os2&Gn^f(x` z#4Avo+pR$$xrVO;TfDAF5gum=3a-uj`?jwuslW|!2h2aEYqz~PLIVJC>I2!VDHpgpT zp0sA!tjGj)_>^p21c6UhF>tEkX_+vgxwK&3%8Q~7t{2%u4R#h-@Qx6VOHh3M@klfP z{72(Ae>#QFRC2&uV1h-;F4@f^PD1b@P074S^%T3LYvlzvD6T|v3Wryg*TI(tO8pOy z)JKpJ6sxA-M)gef4LC9U)`BnCv%e_`D~ zp+c)Vn5?%#c)3wB`%D!McsjgzplqUBH&EO_;T|>~y3d&_zRDKhTP-hA?4BWNXEMpg zM2fQ$%W&}ICg}DL_vta;-(hqYvpO1lg|%&xsEFrr%~-Zy9b_tDe)T%z@( zi0@ZZ*A7~gqw79p>W4lVZ@jm1jNZp-A=1I_Ukp!4brPuXfF(O4fQ98OX<1&RAq&gk zthUwXqy(8~B_kr^M|7FTwaKTKX<4a`{Yi!u3hwrdDv=q*?JAR;$?a2j^T1gUAIKa+ z|NYfL=tyI_MCyA7O~-}nDMnswIeZKoUnBf1s76D5f^g3m*Fzt$FyJ+74|R5hHhH7(4YwXJRu>~f3BiMofWD^==zZ2nrQl~D|{>s`ipX)yPaT`YKlGrj6hr)93} z&-`aH*VJ*de~5hn^(bt6%+VYlqnRqBUR6gGG^Asg z&Sh6MmKSwC1!|2$4axzS#3ljkRhha0v0;#|TGfI>4KObOrd?7hvtg*3 zmQTXZ$Q|?-(Y#;Mc*}hxryp&+>XHHi`*VZ7r>c3>f+3CQG5go(?~e`oo~9;P z-_!XXSue=Ys*$ACXf=CYK02Y(Mxj{IF`Zl_bz4cjqX&|nA_-A}c?B+!loD>a(2)?9g7JX3-Y!(q@wT>LYtyVO)mdKg1Hg? zzR>ZNJww_LscD~X|KnEs2Ysjg*7s2W^#9aq|Df-*-}*k#esd&kFldg?*jdhFACI-k zmT&z?Z}g9LNEh+(RoFfhW5A47 zJ%#IA%XfpCLe)Hzi+ItG!NSIoAVcpbugJjeQGe0nTIN=Vgwo|=TkeXKM0#RW*QtwG z>)M((2nTRe{Iic`8b~!hT>18>%PHHC6-UFf0MGbU>-lUG*{jyjxLVEgjUKMZvdE$~Q3xrK=mRh}e@uHp zPDMQwbT#u`7 z9NqIVYBDzH)!n1=TU#&K?mzQH>lfRpObCKbAn0@3-t5WEeFeloD z5ZhrPz8q>SHAnII!TsPXd6_N(l?8FVwT6fq-3uLqU`6Un! zZI$;?(M`|s!`Uc365Y*{h0Wx|ytYK!z9R53&leVD^mK(KqmWen2zFDOzrCZ77wypJ z>3>nMnp0r$OuW0Ypr2<^QqJ}=!d8Mtq;MAtvZU$sPol}GXO?U~atMcNxz-T$Pk zaq<)}@;INnFO&O?h~FEHI+a3XTgF;xW-ZHLwDY zjB~|!Baky!c5K07fS*fQ8TxV6&Qnc^c^%%V{;KDla900Cy1u_!=X%NKEm3vktOe|F z80kd){fB%<>e;%GUV4sSQt!8zh;Bxt!dS1*icl-l0GLUsPkDY)dkK^{Cy z)CQCuYCERt7B5xrzb`FP>)}***o=gQD2As`R1x zMbjgzXB8FnaMHaF1fwOh0H2c(`DT}63)QC(4|vUq zNL0ho9TS;9J+jDcWUY;?mqhHkr&}0lXL0t?7d zljH7?mD3XmOSh{{pxBNBz6i^TRPAS%9_pFFo(KOgmj<%TlDX$XKr}O3xj|AIm*S>$JII z#_VeUCe4#TpsQW&FY_4tgg06|<$l zW!m0WJT&d=kRzjt9G4!Z8D;y97H1!=acXxsI$?j}{%mknA+nhD2(AH;V&2FD^NZ-Y zJjg&cab&O`$q}BxS20Rm{z}G$w6^1czE1f^ijCC z!PWjGRkW6cdS`4*wc@3=jt})-u`$in{tfvue4``OJK4OKYslVd=3SxQIa}B+qy+Ba zOM0mHD$~0~##E^Hdh>?0z1g&Wi^%N)H@057w%5h$#&ZKjt>Yx;9H3-$RPC=$+@Gg@!y+NsZYP^@7`p9O_b_5nmEw_n)yBs%h~8t%g0^)i;R^nb zrp6FXJ)jB=DQ8ezOHDX9BIRXZ=OPy3I0{ERlqz0o4oUCg7=rB&Qw+!817cK5%IZ-U zf~7uNK0`U3_1Sv_1Gy659x_$iXgj{6-F_9JPc|8dV!39%OLO8dc8( zr$x#(u3eP_Uac4aHZzJMWzTlVtJWDLHxp_k zl|{>5VR?C0T@N;#tYmK{7n*3iKC7-EjYujRYdt=FsCG6nf}T}l(2|N&Jwug-XHeKi z1j^d~OBYM#+SR!ha&SbAmqt=u4ss0%q&X7qvHvDg%7u>4HVz_2!OwySPx$YgYNKqs z`Z=0E9?h@}Zrcxal_}_i!<%mF=a~E$_1xG1xrrbxyRmV8hi5opp4y@L-J}lgC1efD zA)Q62+10L<6`FTT{y;wNd%iCjo7K!3JJMNQaD3BD!nWbQDzOZyXffootL7jhPKB|` zw-zw;34#~9H)w&4?0Nyb_?XCr zF0mzuTi_&|F@kDPo9@$G)EyZfSw{I6B01jxz}j~iLn-Ru5h7K0j~hX#r5B8xZw$%P zO&&H%>eo8YxsxS;eKO9k^RUB1{RK8~Zw>vN-k~_zf@HS6J>upYJifC0N%X z?Z(`uOZWWL8VMgMoIb_OhIJTmHnbk7#pbnIo%cmD?M827pS=ZWnxYa2qs<92h2m-n zBicl^nQgs34#GZF0%X~p`d`!)j-x`e4t|1i6k+R8!imlo(eH|J;gX37*W?L6hgD@<$4$QD^!9V?A0$UgOq2r};J40|* z8I@B4BlNZ^kZ;cRMH(FHr=8%ZRi^Ve!Fk!C$@YepO{QfCXJnderD8LS77m9W(3H1z zQ*Qjdv`j<2E>PX-65!3Do-2t$@h~7*UIX{ZO1?C3zPZNgfCh89u4?^p;JjE3g$Wa^ zR(_cdm;Kj2Iplefs%=yU?-f#KnLqZyj#Pq+C!OKT;ACYuKn4x!V~Ea3js+lvF8cmm z_Lrl4i7h30Fik90QV}7{U)9Et8iorzqNaVM8?<_hG$=U>|Hep{ZTd#q0ym8|zd(Iu z&w-{$L_*Ck3=7_g8}0m;;D20xgz?e&Y``0O&^aP_8A8W`V(t^+fci4gC`JTIQr6(4 ztjKxc1Hr%0?`SC}RysLMifrW7+jY}L7;>>z$1? zE#s!4%2b{=wPuo;sk{7zM#yOr1|g45(h-^vx>R^LEj$fCf;(*tZ6{nbgX4c$1BOr- z;c5RG_y%VjH1S@Md40>BI^)3PjaDgrg@~ASSso{BT-t#URg{?g#MKXY8(0P8s>P#D z?9w?N1+LNAah|MuhtwTVfuCRKz8(mVXETQ^HdIDcx87i>DaYWcS_T+bGbknwqyl68 zeTh`|S>58e>Le`})<{g^(YLYn1+gwILR9ZD-Q>^{SMNXrIdZ8-Zq=Ax#VZKJ$bc5~ zNd_H*_cYKGyy}NG=rNQPw}{0 zIuFvPvKXj;yG&aCt5lhLk&cRE5)c<_=&X!q^fw>(AhPE|Caxq(976r*qsz!=cyow1 zsJ&}XpCK8FMR(2i)Q*%~u3oG-jQt@i2la|x(wkE5E+(RL;{GW<^$4)p6kDN;6U%db z>OoQlN^l(_It`H$w{bYSyn<^HD0ejkY~uKU0QnL@u>#LeviFQE$DuFFcFS#E)h%Dj zZ|fp*rZkAIn#-;VWdxKDW{U2~xxWL*Xn!tq12Y7^i9<8Fw=6CtGLlhTlq4TlSM@~2 z>MF_wP)b*kh%otCX8n}PPiN6=Qsh>W$AZ+Zq9S?hD)L)D+$g1_+|^a|1^suHWE#fT zEbHqO>nGd#30OZK`AOcI;U*}tEZ3uc{}_`=ZT*oZnb!?l(lMNnnwRa7w8V-zoY=hT zTHXk(hyy?4a96oT3#zT@lRW-a%mTU7O!#E4XkS8G)-tPPOc159i;PTt140^ z*LQ&?Ok7Tc)Vd0J#9FEyz=)35s0vCWq%ToNwpTCmD`cSj$J%ZJc9{r3_6 zMivm5n~suFHTuWu%jiRo@y6)S-);x;!k>JKu{x(ex|f!j4!u*3wuCyOC;mSJYx`ySZD(=FzREEKGE-C zk$DoSS*A(+$akPD%Ytt`n@ua+I27c5lTpa^9S^imZut1)gecTl(o@7-n31e8c)(Kc zh=~JdmwtjP-Ay#Hd+ zyNMeg?)XRAj_!=q{jSZoxDr!ePu-v2S@62qEvmJvWDSmttC$#%a;Wst(^%(WkG7;wS(`u+Ml6C5`fQ}p{5<9nl*L zv74|lnpV;PNka?USjNnQ!ZO-d;cCl`G$gCD#Nm)2xWoh~$)J9=E}0&cQw1j}Ep&@a zPi$&$c1M;F7jNOj4`5)-M>BKG=(XzE3twQ+h`s!;rT}-qWd^F}?>!*Vlv3-BotA=wHX*>< ztK5!@IXUI4)f&p<6kTjMFP^QBmhoD(P1Xih`=)eURg;Qh3*=#A{xlzCBAsltTq&cz zf);Z@XY08H9iJL6@%3e3cvLv+1lDbN{3XL3!U3h3yFgbeIP?I# zCbB^iGR%WuI+1>$X~2?NKQp!vHYfPPTo@ znQGL7(&u9o$Sjt<2?95aZiQ{B-Bc&q1-moVWT{ccrDS_n(vu@|BwzH%GG{4Xc!ZK8j|WH(}KsEcJg$sJ_Cf+9{60!qsB!-N1!Z>Mr^2@_)nr zeh1%c>Rg-COXf^Qt$%a+oYc^fL|b8f@HJ+Z3BwuK;aGd|N;UQtu7X19_E66Su7`Z= z5YVf526+9pLt_U#Y}QPc(@5+R{%Ge-F}Zc^&p2jK#!z=|>=dlIDK~bUpR?p~#9os; zj+pF4&XOlBCdznCX|boyp2r;%pHWTjSli${gN&YV_3Je6YjT=j?5}0$*Hm%NVV3rP z9pp=@#_}bVr`?L06VIGQDn{5KKVJ3WO&G5*M9ne8UK4|_L*@u()0;e-SNE46gut>; zcN!)LxFL9+qGFR}91af0nw#P~jr{|iR&C3nL^Rp1#U$-&Vj zM%=Y|jJlB3m4SE6oYJ;Bbw+S})SFgV?WaBQ+wQW!6lPd#mWF((V4!F(fIA;uuGbig6!6ZCbWvXRAB5q@~ z?jOpzY`wKXik_;P5J1+8ht@_E_w2%_;;wCoHYU5;rH5P37dgfi%&<_;Bv<5Wg2w-Y(cvByKGtG=9YD`7Q6vvV3-=u)}yoB$SM~X8m*u^Z4ht}-K zLMO@m!0$d+#LIC6q3R1v&!tF&<8lK!t@N|dfLa{yDt$eR zq(rQRcXS6+UE6ksdcG(q-4$qwy$$AVe2O-t5&L*G1_~&P4%D=7nl&%!((8zngcn&Y zX^B>+TANGPwxy{O0iniGaCmtRETFJV1&7@bJh#}yb^LLQbg4Uv39xto+0AQcvNG|A zNEh2dH)HG7cKy=8dHzDFu_~Up->K@TQ3AlT%T)h;3a(mqj8^1tFDwY)v~U63|CsS1 z{j$ibo~FmJcy)!p2Ta%I;mAYOw@F(n9r1P2@O2-nr5-(UU7HV_=3UNXyZfzFy^E~D z>uML78V9n}kjh^rp5Y3MGx3nUIBx9JJrT_EHw;kcS66_mv2!WkWxsgq%UM%<3J%>r z7U5aoWw)Ajv%S%cW=$a8x=jGN_7Wqv?O+=%${+2OP`UGSgNB8|plZ0)e#Or4oZobKq%KV23LLSDGzy%~u}knHLPO5g!}D z@T5AmNQRr5NkK7;x(A>B<*{pc1Dw(8GeGF;(6_dldU#|; zx~b9`l7JxUy_R*MJW-Ajgu09l<{DCBKn@&QI25csK8tx`gby)G*cbCx_G6pm)mSgu zBkE=V8sYu=yQqwK*rVz1>KeYVWkbzMXAkd_>?2`KkqrU}EI2)U`6G&VakqZ+1CIQJ zIjrCencZK(oIigXCY?2yM zdyMcNey8?DwhC$}Z+^Al;}xS~dhUlsrNnHwd1{||d38i%S0a|9KLQ@OF-YGq4o%Yl zj13x0C(fDL%Y|>OmztSQKEmtBCuLB`&=7S|LWeyYlUr)xG+K{q*R&0qM!3GRTQhn* zY6cqwFRz`FpnclIPwWF)(iPs$Hy&t{RM$4UA-LE|1aL}`p;OK4{sZt9z)agDwfSto zKO;p)Qt459n98B;X?j~>(@G;2S#NccxD!Y{m9D$mSbv;1Bdqvq5j(n7z9ak!$N3f9 z* z@by|mQYHG^`b`4LaN4y-q8^^v(88l5r^tSMc(ydu=+ZG(B;n*ZX@C%!@_zG>@G++m z*6r%CTWPgPW2bH>e>4Y2)wzWmq}eM`VUp4aNyF0gfrcwrPqNupOQ-`lJKLb0sr&W8 zU>1!^R;OmOz(NcQf+~ltm>DJJkB0D+0qfE}<|1+)bcIgH6TcJ3$ZW|Kx$GO#z;yqc zBj)q6gGvS+@9{eJluQenea$5MDm)tAPj#A7MTlbU!_*IT6=LNnnWOS1rzd6$nbrNE zi>O4Gq}n2n<%(03KxZ`aD#JhqC;DjeY7b5U)fIlh{vIv)jO#Vm_jZO( z4P(*QUMa?fPGzvR9{^|T@b;TaHCMn$ky4tByQs4ffs*!e0l!VxNDUsBHK>M>yC5nx zxuVrbXE}_?IiGnkLvlVu&BD=xm&n_@qj?kC#!svV{|5{h90g6(brY|L zw$+5TO1p?pk1At<)yBs~lhGN`FXlCcn`AmKzs_PE7}$~>16%%zpyp=TR$yRTjC0kCzGj=?w3P1eU)M@(%SgsQ+6P@ zm_WN_-G1>i5nVz*-FklFuCCJa6ZfR7yY<{eXDMnn$#p#%-p;N>&)eIOu3PiAEBq9R z45bwH`X!9A&WO&1U%>E_e1Q0%spm}uxddXe!cCIy3VjF&ZDCw4f2W^8l5i7^SZWbK zfCvHP*W8-Wbw=0k=FRxnSpNh7&C~_#FmWoJ#eU!8KhK2)>biN>DjYm!^h}N8!Dxlc zpUnrM$`D1;3ksK~z6IQmjpwPIp?#TUQt`o_x7j zNbS=9pYWYYWPtzw#Qcafp0SP0X13kt-(4(ijzooC#wqRdvNLt^e2>rQD_>&hWc3to z=Obas0juaNWhrxQLp<{3*6Tw(cf!l)=}=FNt6jtmL^S9rbG4V~v<0sA>v)JAWt54- zkA?X5uvEabB6q0gYp(V(GKlWQR{~!P^?cRDlUC5x{u_Y=3tR4Lzm~GGCp3Unu6DU3 ziOGpDu}Q|5^xBV^V6Si8%$u0`%dc(bFHs9$|E2srvf&y1EFU9bdG^He0TSJyhYRu5 zWLES;ycvj%LqS@O13nL;d1F7c-wc74+K=Da(0gm^v;FARM#A#q@x~s9FatRKAy+1j zpo=NChbPIT_Hwt!ML|r0H%G%SkQUWaT=s5B@>DaZF?*0tWg>@6N`1X5O@xA$GSreAnJcLx8< znqCu0?@YNh!Z{(mphptFrxT+B2)*^qsphqKE4q_0sDs}6dEm%P9uPEhqah6~T?*Fg zf?JaXhqHpDCv)|ttQFG-}wejyDGZ{hDCS}bC)9d z#!TKXt;iXm+`A(Mgs>NndK=j0OuvmZE+Cs$A_@1m|FT7Gqa|WFVWxM5nk}GQew=|6!$>YDBcUf^BXQA;g zl@}JOuj$Pf1~Rh!Dr04Wl=fAISNUDmJNZV@D$5bsgR;$ko^&EU> zVLE=I_NGb*Z=>wQ#~`N96w@^CoU^m{RE?wv}r%0mpc8)oIAji!713ESIQYu!n z9~dw1csnC~5vKM%#vWTHR#_<_vxAo$O?f?II}|vy<7jCRcKE_--+U+#m$vssKv=5ypcJ>&G!F-W_Wu`W#NLH*2NVu+^$=WR(3nkR$z~t za`fW#n={REh)bfG?#O&D@3M?xk@?w%&ta5vj~7|OFCUzSFN^TqfqO?!M*4yrG<25YR5kdLf44nU zulOSUoxgo)q%kMzNi8_sJV6NKh%Cqqtx0vJpiJfpm(!5Y4tK#JW9OQ`xi&XiB%fBD zv+nhS-32?FXBhjrM3t-E1#Sc>x}YlzbD>nO#=6*89JeyBIWqJ#L3}@i4T*~U19Qor zzS8AUAN`J}(vZU%KFR1=JFp){R*BRVIf~?UtsHN}#Re6}Q`!{u{>&xn`k%`M9e} zfE060v;;O#4Q_P|$_r^NVapDafmo&J{VZiywm7b(7zf`)(k~beUpdw|1ZDIMuTTBt zXEts=72>hhSl|vHX_sbq~bqD$0?{7Iqb7%ddU88tis$E{pGYHzU2#y>7v9 zqj8XlwndaC&Rn9nF%P&+8Cr4UXKS&NC>f@eEILbuqjjKL-c^#RA(!Zix=M5#MY!|> zdVxcfs4#N(qT|WQ)Hq#vgUP+NSLhPRZc5pb3oL88d9W_o@w9HBX!BS-vYha9&yomo zx#)NNn8yKfu1rjQwU|rLpIT_Tzn2{BHNCM`4r% zcc~3Mn4KOReYDtp6kjGUB`4&cBb7O5$Gx*-egLO4Rjr6s5f zWTay?uqxXT%qXaF6G5gZ2k!q*T`tLpxM0Sw)?#PG=fsg*Ag6UCF;N9@;7)T+sMis^ zHt~fe>ihJtdCSpZuV);G9KtH%%w+Lv9w2?brmYK`0= z*Zsqd#oa^%ZW)Cxpy*<#yV>G2FmCMEt-KaCtEH}JJ3~V5AA5~Hdeo?T!&oKW79v$| zMCQX&(lGhCG@9d<>w+aB(EC1|kT`NEFf4M2#sK#64t$P;%HD977Y5auU8uHM)BWNd zcPht@zdL}&g{Mk<4b|GXy78yhtU|3KuBP#V;lgpFm_A>8Opk(t-A3n4s|pY+3%kgm z?$BzGxI`GI6(v@Ehp~Sq9L(IC9yqYJxm%BS48?&cBfcr+U^hj(rN%%1L{~qUlp+yc zdt7c(7=1=~4V?zNzJLR%1YJbpHgc{2Bo-Pw&`5O_Y8%nwZeu}K!QK_)d1r*D1eg|* zWSU$yyy^M5N+cQ}E&n7A2nu$z zI16?eJ6ac*&O&WJ**e}hEM2reeF4*;M~*0OCQ3p(-35PY`A6y|0UpsXUw&Gkqz1z6 zn)B(jmZ!SHXmSozA6YL2y25@t(Y5*J;YL;4!FLxC>p75Tyt(6V8N%k`LWICL6a}MI z?dlV#lB&xjG`K~ff98@u_?)SNZ~-VuU^9YB|q{3XF)I zCRIkN+6vZ76~=nW5}2F_3&5yjgazf`e;GSIJ{8d)(Yp!X!bfx6v`tdh=#AL$WL)@` z7>nD~nonhda+Ik~Y?4%!MuPcZlBOT`PA#QX%33PV|Fn z)8bd*niV-HN?#`zGke$^gw_n}NeNENKN9*RbwzG8YvjxSkUfi??l4~FV*a49XX+s) zf%zt^`azkxdyKzCA4s(^4$d&|H2R`*=B-V9hE2nYi?vCynOd&>VNSPacOxnm+`XFD zX_bug?bO<(!QGa$A$7&Y(VXjFHZg~Bs*igtmH;Pr7;l+JjW^BL2Q>SV`gXb=Ep^qd zekZZ#7iEW;Y5Wc&s55d`H)*Wh_z+$ouhSBJq6bl%&H$6FpJqrA`LcmMytsko>~Ia4 zM9LMgc5|kysSo$Vji2=~6wOUTH)-sMnsntqcT1%j5>F^ofCLWP%u-2u4&M+>pLx}@ z_U}{jtE;_CS360n-A1B_%#zh+ONrR0;DQyuR|PnnXxZDZ#I2NeJoWlNITqLm9IZd0 zF4Z;Pplfz>jO3K^3(5=d+X2q*Pu9T+@;)L(qN1EQEo#XLiHstZ4W}|KzWW18XgZG0 zNT3c8R1eVoS^ESFn{W7cfsds8F%$kWs@lpqjL$>5dk|XBpPNK$7sG`p!dBJ)q6>^pMjW?B zKiXc<=lbuRBlhPXz=88Z2AtGjF!o?d1%1uA(S}qTr+^Gd<4r5aw-}TfE6Ki^M%(DD zBwg?PWw$cacao?o?4kmXx&lbj!NVY_b$-88`>nr?fuMV4g^Z{Mk3Xh+;IDMFs8iw- z$=dS7&O`cX-On(YY4F#PCwedcMm1E)Cf<(5a`i$jO9v~4^JtpR`h0RXRk`owrJ~ate zDKZ({je}}U-2$csZg(lmbkcNc>eFU`aLHh&nNwIFZ%&p~P8nfR_WP8rt+DFuW9Cv; ztR^7?oY-14T z?9Q2173V&2Cz>Vo@tdOy(l|btnbF47Xi1+wOgOC~CPh$%aTb)tV;}H5XrRam$6j4- zK=vdfwwiINry(BE{}V`$l;$7ekUzpSxwtpms_#IaqtuZ6AO$d*Hg-ASbKEnf4*Y zSu$)OJ`e^|`T)w=kE2j;22$4E;lbgN6kg49L{#+Hqaq2%9CMt!wSQmgHJAovnM>+vK-}^7d5n+ah_>Va|6&i}WdY-pcV&#|E%WVF#>4 z=tE;}cuZ!2o2`G9LOgmIuMJY~PGdfb{yMi&J6#HBH;}dRa0_*yB5py&r<>8EMQQyo z|NY$|Fc0urC(TV5oFX2W;`-(ma6pvX*Hy}qo`0MhZwh{hZO%7;#G7qUZS+MTa(#EY zIFDEAPfSaZ>t574j1hIN*(jTuX(HV%HoeIMOts|vF*!+)$;=*O{dV-^jC6Aj9vsS4 zwCcF>@x76NcjVEBj`60Z|NoF|q?0HI15t`xQeMfLB!DLC zR?0A!m1U4IQ+@cIBncL)M85hfugDgT<)19*Gm72DgR`2ab~!}Wn^IoTx8{VTH;;ox zpcq+_8>w}xJERWB@5)V-LX>)&a|-)`&*{jv{u*^h*7GVvmyXQp@5nsekyQZ0fJCWC zKUCXS-mc6+L*vS!5J&RbDBXC(K+Ie_yX6+T@XaoVETZ&a=1swm5J}F8|+wb^QzWGS8C40QqGMJaSbOSwL;vETi6lDxXH%XpPh z3mM-u=I0o-lbSEkQ!!|~g4Q(DEqjXw@K|3~%pB8GmE&_Rw}mXUdYIPDCr6DXIML81%ohlejgO&a9kF^CI== zEskvaqzL_xwom4wOCwq$NNlj2VEG%}_Q`lL3y+Tn4Kv+9EQl;4_)4^Xw=dd!Ot_9* zi72dpWTz5jJff%|QdzBrw1(rZ{m!ncV{(%JW_C;k9(%@X$-v#&EZ$@t%J~zd-wBWA z(+NH$nzP=~AO)z;#0bdPBjU{IBEg)DA&MuGDozd6i6``apf-R=6{Wg?DV3I7 zkmL+sP1AFMgYYapI47~(5iL6uZ9bH^+Ft}F9#xc^lERIb9iD=ZH!RUN{A>1tU%Fo8 z?0G4Y0n6A(yBn0lU8E>CahmneKQ-T{Tao{Znu7lf9?nhYcxEkT} z{Om}&v1Ah5SM(6)PeH^rbx)`#k9{~>^sg;uG;yNDS!c93uM&5v)z%V+NmqS5y)if9 zIT5Xh8&p~pHvpHer6bAnfgC(J6^OrLkuw~V>4EuIWN{WfET&_K7#FKht1}~bYh(hu zKK!V~XlJKf0M*n5r)$A%VDibLVW~l84l-4DLhlV=*yu+d|5(O8%PP{qV(i@(D`{gY zO{2NAHQ9-H{S+ai9opU&ACC5yLvj6~;06tcJ{cCg*kBiipWaWF%3Ps%}Zw zs;XPmy`@Kt6CBwIl|pH{n^~b(oD2^13^SdTMW^WrdGs0!t1%{CSuDa#;{h=yA&Dwt z(T32D^O(_m-+r9`{QteT9>$QYo-Qx>{{Mf@*=L`9_St9eefBwrB)Ms3&o_Y}4vyHz z=J&mbMiEO80OvH*IbAP;2CqZd=uQyeX_SHW2LfD$SIY@7LHF>n7u8cG{eb@HT~0^U z%3m&E3Q%X2o@R&v49^upi~jAB1J52w=5`uolU zkVi*^`?iAlp6y$O3ZS6H`2I{kd*#Z^*Kr;PdWF{kpjVEeSE5G!(vEaUlPTd`md%XM zJcvc)Fa1|-5h;EP{ZQEUt)FmC8r`f6orUY|5I5p~2r{~fVg1CHu=4uHxL}s&kk{Xb z%?UTmeCLm^z?Lr?-+o#c7Y?O66*jo`+4}w1@G$e)das<}bq_=gj=b`M9od4EQh)b7 z>+kwX>^pqNM=?6#dz$%Mo^cB9iwu2ggxKK6IN?KR;(yVCw0xeCCOtsU8UnZQ9idiN ze}!;wMlx8f8!0dwAB3L$(!dpltDp{SP+z$tX?q3K&@ARy6m=^*$&Fu3Cy zY4DR_I0Ilzy>jE3b=X}r2__Sfe02)zMK{PQ8Y}))-jeiVvw9Zo>`ixnp}zm>3M?br zrAdg~+Yjf)%q!R42PYDhn~TucpLp)w^*g}o4+6J$U0H)U2peZxll}8CdLm<=?fVuL z5YnY@`Ncn7cKzWM@O{s2y8<3Kf8}~;jAw2+|1;1MH;dbJ99~s$&*1bO53jsX=gvdsPQT^+xhs$$-{6S+{P`=1%u}}l z>O)87p2tVv0{|iqK6iD^Z0}36XIIG8v+Xb4{p{4rlOG24$kgB7Mtl1jazo8sasKM6 zyZgTN=xyg80j6KL(EE~reHuPqC?5$B)Ar;z>L!Ajk_p_X4&xKyto> z#V{!E?KIl1yzv=Mw>Hy~^ZQoZ(f2J0*Y8`!CmiDrHD%SxfBp;LqI_4}iv!khI|r`m z#Rw!gp1Jvu#7yg%XnNwzC-{}V<8zl`F0HzJ!}%)+1G-`5Fa9#f#?5OW(AO_s?f?37 zS9gE?J6G?&eDKl>&+%sQgP-~O-ynSH$}k#ox$iTFFZUtg>tA^H*S~S~cIH@h`RMB9 z%&;FBOc8-)bNMH)-gEhd7hb&lllJXu2TWwT#lDBWNLdqScZkS-ZuLH2K%MiZ8zg6AZ&x*^E}!=yLA=K!M@MaO+1TKqQdOyrO$e=g8{8_NpzZLdb{_{!Fg|*?Y(!Vck9Cvw|WnBR^a&Z69l-dE_Y@1-i!nGhzm|ro87iz_5mmy zPXmcN`kub{%IogOn#T3BZ+;9W(MO)X`_W4XQLHx8 z!C`|{)+@Kp-1yj^^B|{{kJIrsw+-s80+51!Sjd%CcfV5m?%b~+%j~f)0{raue}D7r z{kCXzBUQ3P%P@V;O(O?Jd zV(y!J0hyoe`q3gc8AG6b^h{iit%#8vr zSd|UDb#@>2SA7cS-!I2%H&2I`)$X68uJg|!QD<-D!raQo&*Pc*53GFr9G>9tz+ilQ z5IC%C(U^JKB0ciN%jYt!y}=sF5p(56A__J9$cmX`&(FOZCiTo;fy$5lIVtks#jCeo zx#z=>E4&?RgFdj-&tAs28)YzEvSU`jY%>>U`fuYP>fd^I|MrzXe;CJfyz9!J9D~T1 z+l^;*!yYi(t*HOHi>mY?Y=~WrZEBZhcEaXd4hTg0rbFN&^wm6VLks~{PoNBN6bduX z{VFCCZ*m(tL;CnCE~|T=h>p~Vj4_G z&TpXmU*n25&v83L+O*|YpLk7b*d(3Rb@_FiX9_P|{K|J;c<%DC4`DL7eCz{PzKhxR za^HtA;e5a*oVlJ18gH0`(;5D8FeXOI_r*4E6HF4gPp_>*$Ra#)kE3=B5b;#K1@qb5 zzaf=F@3zhT93`L#Sdov;cQ3p+_p20nn1R4|58O8Mw-5ga!lrP~;5(sKgaL~|H#Y!? z7+J@jU;mtrt=R*&QRjWb1Jgfp{@&XP)28&UG$}n-6ci}aS><1ea=5IXARvDFF8G6g z`$b5duP$31&_42>x&OjSugBdjTUXf4m&-4|#WKu&iHQd<+2H-Z9$s33IC{&>*^BV!Xm7j7=58S8qjw``f>=6?zZt9Ax241}D47 zl_@z!OAzdR5lxv^0;Xb0nLJaR8?z{1s4p=DQ%& zV}HV#?d~tmJx2zLD^mV`G_(^lKiY{&KW^tSONO^3ZRa!4>uIy#TC(e>S6#l*ZI;(V z8@alj2FofKEH_^Ht?x4M6)VG6yC23(jis}JD{sX&?1Eqj-*o50aMR(urbAbL#J|?t zM5nxs2>sL^58yTHlJfofrhz_$%u|Jy2NT^g{3WoIQY42MjGo0dIrcK%PsSLarTp z*OjvY@(Qe=Qc0cr4o4qqpS=0}*^}5je+5Hk_7MaCeg>f!r?2#1*#WP~;dkSnSmZ`v z=WT%4{>t^eh)lkM!j1v54?cSOHcow?i`j^j3c-X4ho9~<5ec!TcI0&@wGcz~e zfZI}McHgjUcKdz6>7z5-e{A{r&#k)q0?SN2FZZAJeFj7P`o2H7)_e2$hi|@ZW*bHZ zj+w&kGb`ranVH~E;7!~5Mi78)-G1Xs%#0;TGy;fEKaV@%cG3k1kwW1DWpDqLGmtpf z_N>4TpU3|eodS+~{JR)0BqC110zrG1&wT?QF7HfU`1a)+U--i1Z~WgcUVh>7&Q&jb z;n%-@@$_=cC@|8m-k{%qedW)86}e3SqW{2U&+fbnkP8<+^4rFpmv6m1xNh!0ARoMi z*I(F|u%GvQ8mL~tIr;s!)o(t8P=mizdkT@9UxIi<)H|H4^&h{ma}|`xpF-lyGf#XK zZp9l<{xy@|I=ko7^o}8hac6>K#&6N$IBoI>P9nJ($~p&7|0d};opxrAeHz$ZzW!_7 zysP-qg~vZ7`1M_y`w3EDXL4cRy4gdk$tyeiXK{JYp#z9SoY{F8e!H1N+h?hUZ{<}$ zhc=O%J8=mXbZy>)hWm2huiW`w_7_EnN<|mORL1}17SzDiV%j09l0yT8(FOVZ&|RQA zRwK48ufKKXq0%fIYV@Wh@a(eZuHz8>&|`w@o=fNZE-jmT7$Sq`cHIOC2N{a_Yx-8{ zL9iw6)x9uzEp#RVf^onYpvp)!`}~Z0dX&!1UP@{x92_q&?4sk0u~zF4j;k$>_IUpFvCH4e)FdgSAGAr zDVS%U6ujQo3uLei{W%1&VZY|FPd>VpVjWA7Vysd2!X)`im{9-X=9%rke)H9zphEGN zw<5yRw)*}KAs_{JPJxiUSAT!zr%1Ry>h9wIM7$x!_qMHfZ`;nDH{JU=xuVPV!?t;T z?mI65MW~}tGQJ2IIlJrT`)B$-!6Uw|yQ62>%$|?II^KmIX#e8<=O6t9oWV~{WBrnH zZT8rcW)!>`6ZIVs8DIH1jL|3Gb>$21AdezfI(r{{5-h@ca2v_9*Zno@w+qYO0v22U z@;%d^Sb5XkzK2;xK>+rZGe0V59h@?44YSAvWr9SM`zx-?EabS zFZAGu{~yI2U^8Fz1#B+spT`M6{{)AaLAtyfp2)2bKseU{r()uqk5$Mx^F@hdlf&tj zKMS=RlLmSj{`GB;zI;J1+&r^wg)TC=%kGJn>l<;DG@w4T6#;@TU0sVo_g8m+4(AR$ zgCi1_F>3!OU{JL~rCf2{;IiwEt!V%H)j!O{a)FR=|M5h(Rq%`6{l}*}&;Q+9&bKWA1B>}NRpveV>y**WoN(`d-d4Kwe-nu~>TYK+9e-~H4r@J|2P)tfK; zZ$um$RIrA0art#WPb=-QzhUasU#2k!Tz`7<$-^_BeMg~?6#9Sd$-}?4{)_($pI1Kq zG|I1kdgX6W3V#~U;*MPT8!vwj8&3XbywCjcV}Dxs0M5&VJLAUlZ`_(}FTC!rKa1_5 z1%F+}*TQxFx?ElvDJPj%J@zL^UFNUJ%NIk=tuOzlq~7+JWJ3)FE@gjUHcx1eE35BZ3S!?(8{ix!+H}g3lWf+_J8$H(^sy? z!8&>B#rO6U=!d@b+N#+%@&$Cf(6j90e1y!Jd+wPFpMD#VDUg2WzqRbC4OnT?^%UCgdX;)9-%EQH&(h@D!qyAZLIHaaX9v3}TO%_ERwL;9lk2~Vhh@C}=a$J0+Y7&k z5Rii4bpBiP-rjh1$92y=aQ3DTy#MW=?3w=F8~zY)=e~RXo_nWn)YB@T`Q7hf3jDpl zBUtak;uYUZavVW zG=_uqr3ZQnPfen+&tA{Mv$xdw+*`skj|+Y3C9o;oT(^9W54~@G>I3Z5f#liVb*LBz z1$WHe^7lx3>J`a&mvkWR^P72j<|}W-676+A{`BhPy62YleDLwNL%F>6z`4H=8qUZ* z0HNB9Yz7cg&B!hXq0fx$Yf##VmZ{{C9TrBR4;Y0WPbu`l3s0~X;ivnBPOo50;UJuC zJje*krf@fNH%uTTQb9|ELZi?#Gyjy`j?3~s{$crD{_#)o*Vc5f=6)BlVQ1XmPXD{B z|L)2u%%ds0fB0Q9-+*-7Qo!Qh)T+nk_MLD9I)0x(CtsO^wGc44w&qD>|L}*| zbNsuu<}3;~ zO78u`x%azr@1M-QC%N}^o3icSolAc@*WPdC(zoZ{m*wDX%B5e*!Mit?z9LtCYcBoy zT=|Y%`jff$Qtti5T>0B_=^x0wug<-HCii|c_kK9{{@&dCvfTTD+4%=ibM1 z@BO*=H|O4O%fVljOTUzRAIZHJa^=65OTUtP|8nkqd9M7YbLltd-anFie<}A~&%K|_ zy}vnE-|}4g>RkG#a_PUBdta5y|Djy^-{;;xn0xQYl`rMqU(DsdlH-pL=iU$G-anLk z-<^A}=ic9&dq0|cAIZJ<=iWb4+1&fd-1}JW{r$Q3$=o{?qj>(iKl6_H z_SoE^|M>s%Y;*omefa>jX3mxPlZPMR|B_ly;WLl+6dwI0XkQ=dDO~**J%vwxq^I!r z|GKAe_J8at{HKri6fXX+J%#??>M30R#h${yxzbbkg|=;l=T>biJaNmm!m8r7LLdHp zbnUjnzv|ppcP)yR`|uA+E)1e?b{0f=gw_~&fVJz%a0*_bX(!a$F~*c z9^Y1Y<=3_qe(QhVR;XRtR(R@5+Y0Ufd0XKZR`nK2Kh|6LXaB6X@Uc643-{jDTlnCX z-opQc)197wqPOsOf7Dy}S6}Td-14WrLX`#@ZOszvV@AvPtC#WLIPL#$&(jk_$H&S^ zy_(d{RO-Vg`7~0QDi7DIQ|FSkYieTUrBq4t4_7D8C8HHB95P1fQ{|z0IS;Hm1tm{u z{;BeK^>jo2eY^WMZs^*+dv~F?+A9!uPFF?>Yu0Tz9RKU|mw6sMi~maf<%tnC{G`0R zucz?dpWG^s{NM0NP@=$gvYCZSjjt!l_2Dxkc#&`8L!jA6b)sC5r}*o{(71h_D4(rs zdaXV*g~w9_G&xj1St}1s4WBGbjq}4a8dT8?8ROQ6U zdQ$3bPuAak_omJb>(auMDwSHPXpJhilL`}UmI3HI zk`&j}x<C>!M_1`Y??R>1gHprq_rYTrCoJyV`SUu@ZuR7U(wsjMvx zm5x+J(3TXjj%0kOcB&&eGc-}BJg!MQ_-(U@S`qD~Vpq3wG?PFE%`ttvv@x#a_)x73 zL2T(Z`UqzdEwVx!qov}Sw=0Wzq71;axIJyIp@#Jx5DyBXGg+_Ir8(Id9FnM3r`(dI zNkjaFbSZXO8~zX97}Mm6t?a0_ovuWy14-`$-(lIF20XWC0}|Qu3nhZD&qW zcOVZ)GAGH>czeX%I+;cg^aC<+beBq@(`6>m#CCFCX`)(BSwd3z#d4T5Fut-O)T9{! zF$@wS#!5$-eY0|)5RE+}A5{it#DTM@q^BZ-J?5~fIaqtV`-GKVCEb7_8|u&ly%afZ zF*wcI?AiEAB;pQlLJunlLbxPw0Nx7!9s>%T!^FT_p(N#+Or(mOi5pH~k8(nz5EPcB z$W|%H8I>}-xgZ!F!hEI}W+YgHBoZ7&WW1*J>nLMe8>`nQ9-pp^)hiQ&q68XqQyNxCBTTufnj}wkkw%d5XAZS}7T&F5%8o zR>mkK(g~#kTV>Eh>mr+D$wK14srAc8Fm7GXscWXB!%#6t;Gn1IM0FFyUeL;su@NGr`wx}1zPzf@D zWSgKaq5Nlqb#in0?BrN=qzn}eTq>hOsHlLES1?i5;`92z8-{}PKgM*XK$p*j7-g2v z$$);ciByx&L2+sFd5gj_iMh-cjt?9eYT*#nm(Az}(=KEb+E<(LJ2zlp>ZhRX=&QgC za?r7+loT->hP=sW&yY>5pv(AWvses6HE6(HPF@*jl|V)@JS4B7R!LM1q2BK!5THU^ z8*oc08jxfClTqcNZ!?vn%PPrz$$F6xc!UIF-NI;AGsKyyNEPiT#|X`t2cfCTX)v@4 z$KjLf1s#P14NFJk@W~C~^M;NM!uj$I902`Zq?Ta;2(4>_T2h{o2`=j*EQr^=P$x0d7&^wEDFO)uOO`r;2+Z^DC9^I5goGI1F7BxOR7oP4p?nMQ$?;xNucB%tf@Zmy+K*AQ>hJeR`h95 z@ed=B(>Ht^UBgs-x@z+Oak>sdzKBUfDFf!#rK6)NJG=&U_0pctya zL30AqTtA^l{Rx@|IT+%G>SV~75jd7tdXG7+ThpZj#frsCqKmT;k`% zQe^X;9vYigKb;zVC=HR`)Vh|QO)Hj?XhuXo6*$4uDO^M3g2<;2BI8iCBxItrJbDzp zOtEh7W|6RlTnk`zaq1}QBn`(8NnpL`SfCV3W(ntBks2AbUY?o|?;#&llJUu4myWEt zV@=nZ&Nc5@vwBTwP5YWP$J!}T)~s2x9(sY5Zz#6r?eJM4+tGFF=FeuCHp~M{U{G+; zUTRe#GHoC%+Mmp4WU%Z)gVcyI2f7;zkiC`<4`YzaX16G9$Vh9~Y9G0uQtbZ7gi(*( zI_sbzu}Pi3YW0rVB#mV61B_S0uBK{2T6vYI2nGb+z=QydUY5BGHPX}9xt?Q4{Dr6l zYKF8l!@1c)$ZnP6dzLkPS%J#}RPvjUMHvu|KN{WZ0BI77Zq{mg)bKUmCdn-NPefYg zHHsQADNRnDo~T#GRMtu^8Pk#pLe55twSZrx2^kKzW{Ce!O{SAW=kPWRZ^1;lJTe9e zWdKT^qWz$$9YI%93o*w#xd|N3ZZa?mqrUJG8|m|lo>lp5SgXtcaz2oMeZW? zPE<3dD6q`(ZKRbBj(Iz?Wp5kS(V%JF_*3O`wNm}~nD-#x$*bz`g-95T1}w5Q$Xpia zNU&ZJRmX452$$*w!b|=NYu>959h(kS6%528H(r(H>LNENMr4GPMy_I|HK>L!cPs~bakqP|94=%3j~gc9JeKlFf%znkW`b1 zlcVGT{lT+@^&g9I$wxSY|V#IlwsT^MJ5-vG!$nW5tw?+CJ04 zt6gWND)32jwbq}F0D-)NRK4+0Db`l7=nQ=1K()6nXfSnR%a*r2(zaK8S@N8;4Ua+@ zA04fnP4tg`kB+uSJKD}}q-U`0k)~NTLTQyucW!E(>CR1>>EHxhXHzxAXtabkjV!s$ z^HtC6jzWMwA1W;1==2Y4Y>midZ6ol2?_#Q2s|{DjC*fbA(nyu3qiv#EJ2^B3U957v zMskJka1Gb$=de&4h=^LWL5Gi5&u+YPUF#~(YKHl$Z3OR8Ez%3FzxW6nsnTnK8ucL^ zTl${$7s0H>;si2B3l*j|Ajtp?4)pHd)qg;=LRp=nDHyp%Y)I+=q%2W*e6k=45hs|o zERg6((W0Y@$6$&_@irFYq`@%3pgCz32o%5;an%KXXIGbeN(Y1@>RDn--UOQB2vc2K z-6;61t?gWAlGwJAX7;&o2Q^_7wn>;e+EG-pCcDAGN2F>`B@~)~8-U$f#*^;1l$FZa*Gd*=e zP7~v1pxH~;;2;ZTX)|88&w&uuVDCq-i|uon+65zq5pG%0svi+0C0=XMjyx33rLScf z8W|}K1@tSm8vJ&?j5=B=kBvmC)FZ`_vW^ebNcR9D2Tz>94EAWS%{U5&j!3~{*1B;8 z3Jm3QXq=Em(=4aX)kSL&?MK83CKAqNKspkO`N_3{4Lvf{W%wLJT|;>;kT64|7Br}_ zF$tjZmJgvwGnib@IVSX|jX|Zv+PnrDB3KRKl^{b`d1&|~d=P4zB2;q1Q*?HXzmx#O zxRa~>QxCTUIH}Q)QXm3tpHs-553sVz4Tw9`Wg(BA-Cm;rxG>PXRmK!_1(YYE=&)Qm zIJ{($m<^*XLuY0r34=h%0g`@G8s`RW28`0!#J}4dX^@FA40}gh1x+exWr320s}e;WR16` zHZW^SX!g8{1W`v3$Oj14%xrAmyl6~MPJ#fnl9q&EhieKphe$ISo3s+YNml*>fs&~d;yI={Ff>VtB{74&z4IU;s+J+ITJA&!lU_a`OYB4rhO&29o9eTYk z0>YX{7b40`#H!Jxu8Re3TTx$_fIA1ZWFa(oaJWvXr35oW)D0fKGFS(4JxXTvx%v_0 zWmSrdi6JT)X$II!N`fN+bV zhK7O)5dZ~SqhUyGE+z$HrY0D8Y)X69Z?9N{R)$PY!!Dt*bY3p25=7FeGz)OSMV^2& zX^aGYaL!4yxf&D%i=Yg#BHckE!a-)vKRy^lB}U9ml1MZ&mZ24?pA?*$>MK&A#a(Ju z5hp0u#}y0e_$-2xnj0dCZugI-Y5ObmH8MVnD;KCXVU>9{4s|Qj7o`;h`0^#;2;)pt2x%9Mpl&q3VrBXm>Nj6~ah)vVPK3NH9aCrnHouU=xY6 zmyis+J30YtN`R45lD6eCZvcPM_J|uar*?b^OSq&Ko$WLS(&ok~MvE}_1)DkR#_-ScD$^m8 z%z_XhKE2RlS9m&TT$1K3c&e^g{ikj2vuc2u8mIt;EC?fsoICi?feBlkIa`We3UOD6 z#2lN@v1iU#@ym*YV+uoixWr+9SvMy$m}M{iTq#PuKuUryPWfm0dOd-A=PDrP4p4S%Mo#4jtGqc;!Im*c&5xP;InWO8 z-NoTpQY2%FW)<89ajkaV;H3=V*H7b#n#Lh`00v{g~Z1_w}6m70xSz;Ju*&+HW2AcqtSuMw8na+j+ zh{_@OWBlhGY;3lyo1jHDE!2fgnl@K`bD1jJqwp;qnZ^f30_jvIexQ0@qS$E7*`Or* z;2m>>>EvBEGSGqZ`70-=pP zrCuohM9TWem+?6rsEM7FrQPdpoo37SdWGSwNKb}Ak zz1AYj+ItnsSSuK7@6!w?T4p#pg&f7V-F|l6_Ewb~o$xF@%wi;vMg@5v`rnC$zD0ac zSP!P#n_CAF4m(mF9m3{kmw%Z)(r$x5^~nw$ErLF*E6YeR*MI~KT1-eLm;3Hf-;v2< zA@Emw862mf<*Y_|K z5gWwji6=ck0Pz*&=WcbSQKe)qinmrv9u5gCNMbmq#ZaV&MN8D3_6MB2uZnQKZ)SKxHv6{gr0yS<57{ zMgtp)bTH84kYg-Lw=hA3A;8R!z((`Rl#&4|cZCb)VGKadmLT$dsC|+vS$w~EHa&ab4wGZt zLKxSM7+BE2cctiC>J4bdQm`UH^76!R1?N|Q;`6XkJT1A1Ghv2C$jCH@cGPb&7%dm} zV4Pw$lX@t`b2CoG$`#O&8Cs`w4n7~)%qcv?EOh2J;awG9kc-IES}0n2pk>5z20)S; zsAZUNpDHB+DNTxj(>%;xDWqeWCagr?Tc|xk>epaSBvZ2_1c-Gty;8b$f@uUngNatF zWOK#J{FY%CMHGh;^0Fnth7Vi^e1*&$8i;iSO^Gr#VVlC|LZ5rWxw*wQ(uS>?x|N&d zM0~)1jdp0~9Y-dR^AXsW+ID=lW|1|SNnd4TxmGIQFmW3gZKsweF*HOdRo8)k_)L&q z7t1xt8$QQ*LuU>TO^_HRH51@;LVRl(q+rwaxl3Bg(B1)wV%m*!RD|Igk`Yr;8D_Hb zT5uSpLEeDsV_!CUIWueR+4-nxMn{jniwv-lYJDQI3}q8mD|p$w0i;bfiyxc_vm3X5 z2CFVKPML+{G?24~IQ=qqr$tg1dC;K|B=m&}d>vWFPRedB1f{bAJH`S`<}~kNl$GH| zj?p?>u(ziIIbyG`dTfmNcg0dMOa+@0B$uBMDF%Z45NYQTgn@&a4%3Lo zv~uZO%=|@k>~Y=k1<+~P?2w$NI1khBsPdw@6-n6pW77MnvRBpI|x+O;vDA9Fa`gJS8cDy{69=b#+9Cwu%JNB={&g zxAj40vW!Lt1V-q*EKSfSUROjaJ!dA!p4@~US}mZ+|28=pX2pRp!~3Af)~;PFvs{zP zv$J0cOF24%ly+?x=}I9Dc%)OE+Ef-@adtw3!cH7&Fnp?Ds}r>mo1<9+{rRGMX#@@y zt@*WhOjeh{-db34f`Y>E8tNy^Y=BU)tP5lT;cFfaGjeb=lRj*bDz>b}WRata;Uuy3 z=(?g+>f!d$C)m!0N62-28rvDmtPtVG^31j@LW~EV=#>b`at}7v6Zi1_5Ll7{9g-j; zj@1JjhA{?-9EnU350e?pUVt2gr>KpghZNjpzISvkH&<>p%CeOEE&>rU_j6k-i>;#d(Y7Dz5&Yk ze8W?XTAE?~4ytO?g~H_Z#wGxKdR`+H9m{%i^QDuJXl00tXOY zYy=0pk}HDxBI8~-k7CJL&?>bomaLM?S_MAMW~R%CHCY*Or5@Bouz2zBRy>B~N!Dgf z%3|OOBGTfqkgv-RWfDJ75eIfQnolHDyTh+o40*OOV{wQj<2lNbtFnxH=oXcstd8@rWxdwBi5dY z)+JsRaGR^5F&mIU;GNc&&D?cjcsQd`dHt%mhk3d|u{t@#16_ih&-LL~;kx$PGI|i~ zD>ID@*ATPkT6;5WvX%zje4!3Y8&&XXfaU z_Z;my#>3mB1j!(0hY_e`&wxNPC0l@D?be(e)QAqM2MQnY{Jz%Zq)!t7NybCdzVqS{ zS%_WSg+7M-SKVT-nqZ>*OB2}d^O7jnn^L9s<}Mh_ac5e*7ga>Hn6T^4_;Eqb=$jac zK`CDlOv5N@LPMPB)of7EhuaA{(gRFT1(#gv#Y3lX@^7d>QxR}CQa(O?LQEMkq1o{? z4{$8RiCWh_Y-bp+{EUXeHhdjuoid57TA5iU7)oxrZtv(s1w!4_n*l2Smd z-nMus&G+US@r}tFL3Fzh&qY2Ct`7i5dV7O zo#XCz z76`0a(E^@*iP!H0xRjlrprKl{mE`wwrugcfws`-RXP_g@F*Mf8;+#>6jFLPF*Ba$3 zhjF1Mm6BDR12KdHU3Dx4>JE)OJYCb(sgjRPY}7surGr$X=x^8x!_+iLR$V-)ek<>_ zBP=O<0yLdi>yUH&O-yLUV6jSWUBDutXvZ6oP^;xNlP2~Kr>ILM?eNm6ah!t(%ZdT0 zqLt;6s6}X+P?N2%V8tQg76T3bmM4j3Z*&!gzp1&PBwRl-uj7}6+zgn5o>omtU(9EztpzSC^bDv>=#brjb7!)lJ!2kkajuo93jfTI@13bWf8n7Z#(LyQMUH+MqN+!ZW zq4EDP`Yo3rE|N>`=%{cFks3r=VYmmPO|!Wp9~ljX&pF0XD2+7ikcc@7AFa{c6o+%P z;t>yugatn#29CXtxKYX?kIDk{`|+_j{DGxad2pVC0i?2l;P^w*w7?)i=+h96Za!;OdW78K?+2n6Au;JE2PB>HIR54PBc3 zXk0We&2Q{#vGQ3t0;NGawC~ZVAma8_HN>c>x*6!>v5)HI@Ej`J5;^%g?0p3-#3zu= zlr<>CmyrpFA|n$MNkMbvH&V_-5g^vtqZ;IM@QSFegQI+Cmg=Oe)R0M(R74e~vzfNc ztPwFZBS)<2akV4NK!Hl;4sCR7GvVb+=_EE)1zON}-AiNFBDwyAoCK^UU&G`?IA*oJ zx=AV^;>W1P6V)hEWjag+4nOHsW%x?3W$0jMSpsWj&UQRYXEK8c@GXU(o3F&>fa9*- zH_dHNsg;Z)&<0Oq0b8tWcI>89I~Zh6rI|uIS*asD6-S!dS9U$;F>;g5!S<1R~ch7uparXFjk}@o>Cgzw=S{k_XK?oKl@G>XBG1nJ&j-xho zProv@0~V};-$1rap;?vCN-g5grINUCZDzw92Tzdzw`h2Wwy_Dc4cIBdyqp!R(oIdW zZM1BTuVzO<=_v|n79xJui8v>g!Hoe^7U)Pe;7nMZd2y1f+^+>ABF6}@T8!2P@yO|8 zZE61>8DUMO><3U2o?1V(mqXkC{R1+}Ye5OJiio_F&Ljr}bWDKC6lj7C&1!#TWjQ++ zEQK{YUV{l>!cMVC${Z<=B2zk)or>Ba!4{Vxt?>*T&;L*~W=m|%K1cyHEPPf!r{Yfj zYmzs7*1RM|)HqVhqzm9`gP9RSOmbnIh04IkUWJY*)j~`iLcog_7ZacbObLs@`ryJa z(f=WnjQwoDQu97fmn5_4XiZp<^bYsm(B)1CX}2npuXWeU;#2B%&Ah6(4{S>D$#mBv zV^c9MCxMtj)z2D*QXb4uEWWM=ee%*ZmHN|1sxjjDJcSLA4?(_OS? zd{*ySM;p$!lymlqU@-2z!ufJ?oTPx)HDz(uhjMz9A2P)yu@xF$?Ccjnf20l3GIDMv zKI3un9Dn<804o-0+KPK|mtV2%Q63&D_2@Y_&@*BL#oyMf8kEw};P$AtGGLf5=voSD zMp10(;%%mjuL~jAW7Tuxi2PYHnx@R>DhtlU7L7AyKBmQjMMQO99Qj<)_vM>Rul30t z*8>B6(qI06VXQh--+|-zC6zA&ILrw#*2uhP zXaYx)N$>C3(aSHYJ00pp(3HGZ@7%PlQtv%khIr5qeIu26&%{VCI1}NOW&3z`7$;G1 z)4jg$n8F;;k6Sfi80eQ><2+1lH(~`@OBFR&MlliD8&f&dv&}H7aF2@f8eNtLOBKPx70K_EnyROCY5$Dwo<0ub(tPPFI6C&OA zU&IuT`O-ElCx0$Y7-?fhL*LSVw&jx+l=s)k^$4z+CVF;R>kU+y| zEHxJrI@B?cq{xzoghp|STI9`YA&sJn82FinVFh}yU{iwRic(8Rskqoukfe;-Tykv| zdbN~tdejSul9Lqpk{4zw`Otaj0rh~yWSoZ@8Q#Ee~SP_RK9Vu{x@mX|LfrcoIs z4OLmN-sWs6W4{+ME&$$pyRjeocgd}%>b7L|W1$?8TT(csn5$&b9(QqhXyC)z?zPZF z!^OesL6LN@8g)kQRS1TvBdJxJ-nDk&f2vI) z#&JgXI6Q`n?N~Mjd!RT0ysDC9qNNvvr+LY`2g=yVKT94SD7xg)q4pB0T6`C4kyj<0 zwdhiXvzA)AaMp557tYXN#>wLmb79(9Kr&BHSKvclaBPJ!oN|3$)To_O+D{Y|4mh~MQ?@ZU?aT=GOgu!sMN(W9-bFcI8Nsr;`+19(g zZ^zK_Vesm@^&2*BddHnVb=TdU#~9&3*L#wyiN*<`2uc!2a(Bnw?XqR96M-m6(?Zz- z>o;|*-;@?&_4lc{usnywR&CY(wiYkV2*Tb7HbI`%=(GpXBNbeI;EL zfa!@h=CrlIiI51K!gE*(4)A&TUr5#g5n89Ug;W6YLn$}{m1HRE~(2 zgkk{4(_uBTF7Zm$u8Xu-CYbQ&v-AiUao2p zvNdoLPK?9-MMLLG!|WD@z~WSW&M_7a6Aio^pZR_4&NNWkj3)(DBrJ2JjV`^}q%wfS zrZ5QzYorX(@DSf-N$3TxSEd~rS~mjwwpPXw=`xP&bVS9ddBBc!OQRqV0I~*?!ZdhM z^eqEwtSYn)csWrPkYjAL)gbG%5ozE?;c1j?TqfkF86m8%GGVNZNs>+WZ}+Jr13k3Xz+`J8XJqECWemnA&c3C6T9qiYr%wGQ5s;Iu~d#j zsp|-?RE!a54_=bvVWK7lf|4otFN3*2luIj&5u9u15ekaFVi|feDhEYxUhW9{FGi}1 zVXhR>Pl=GoFt5O&y$s0$iaiFwR zSNIyIZVTW|fm#!UUr?JgLu8&kwsgdZxwHf%&?v~VOoPOM)Ur7`ikH?Jw=P=(4N|EQ zZW9E<>Oo6*skmth(1b0u31RN&-bojg%6g($4DJlE*e+3RDse@w`A~70?uD}2U`Yah zMCYNFA#XaX<$5nw_H!!yqV>JV&RCSzw?R4G;A`X&^f)g0Dw?p}BDx7;$+Nyz=HR5QaXq)o+#P1vTKGu)bUZon=s#F%b4N8=ZBzeA~ z$XKK*HGgKAy(prjjawE&m`tcOxX8#=`7X;sQvs^f&K z>uT?`$}Z%1I6Fq-TG=fKKaW2JUO`Is@Nij_f22(h4+rF;Tj+4t$fSPpRZgJz}CrPoe11o_d(Xn-sLHvN8Oqj^PZ}1fna=N?DAUlgg6r{pSjKY>e&OI6! z6k!y2Pq!Kd;Eg4)e`e^k>a0LM4fCjOGyXgQ*$t^f-H> z^?lMGz%+D%m;E^w`b92#!B^H2`V0BhSFaaTc?ta{CGeRK>5*D)yeNR%^I7DCzCSVjQMgyX6$75;CW%KP;_SS2q{92&wAcQ^*m{=^&lXj?$K5og`e zzI431_VyiE0PXP_4iO|~NpVkwf$P=LdLrdY!?odn{W$K>kX+cQP{r2wv1AXv;1Motwx$oA!$=3b$^_#~WIG1*#of#xtfzyw0MJ|r>NpNr9VJ`}SBZqt;OL4#t z{L;c6^T(u=rF8uqr-Cia0ltE{Xu9=`V6V5Fft3tR;o6rvoFupzFeyQX*Yw&z_)LzU zvtvTq8wrOyoSkr_-Np@_$1B+6s0}*H3z3DWLfAVu#fiKyS?+9>EC}IIIwgo+(eE(w zHgw8a04)ki)vXEwjifp;cFu7L)d@~bOKCNkQgT^y+Jb(tF#@4w8~MhB7v`3+fQR5M z+>+}Rh0B(uSdo`AVH>QFX?h3Kb%W3$YCdTE&?* zIRWBw!vL`|*1Ah!S4S|7sQb&;2$U?k z^jb6|7dZqR@a-D5g9P202{DuIV`vIWqbM_g;Kan?gW~)!#Y{49np z=T1;cC-SWKqKIiXU6pEDiu1Y^B4irBX;d99caqq+98^sDDb#F<`%r=rbGZ^Dp-4C) zxp+dqkpwi?oHEoD(p9Z_I8jEJ(nMKM-PGVh)jP6r<&qmqT?_&Z{%9?+P#4fEm(rf? z4SqUwE@ncB5zDo>FFGPns2a+|kz~0-NNQa!jWWxXLKG+b@8<&1g6x6{3TniH3txEr z7XQ#uR9S;elccj}Jqf{QnWP~<0J4ZsR9}8hgQ0i~(ShJ|IE0)|k%GQI_ef)vHrP zX>T&OQAs-_ZtrXx=PZ%&H!z2Gce+Vjqd|?(XT@R5v0PMY$1(cWzn& zL~1uJAW9NY3<_2wSd`5mC<;Yo<6UTj=oGz4yR8Iz?;!wA@G2REHR4B z+TIjO9y7-Whf=bcZ8mn<^C0u&l zj9I8DYc(nifXw`n%5>FCnvw$opnq4dol|Gip|^;GjNwnL|=@GD8?0eGeGAh%Av82&a-v~Y;!&;cGiq)RI;pJ!aSU( zeYW2kuuzI)XlWnG(YMYKaPM@EoP*1=G7OmqOAuWK9v-9a0%JJ9T%#7F-UE!&FFjhU%*M+~uc%GHGpbt-8~i z5ieF^0$ck52QkY_2e)YSxHn7dF`u^$eo(rF9BAIzo>>`efuY-F47(&Am-u^O-3M4CW#A~IE~iI8F)`Kwu|k@}BA z6l?1uN&176#A`cuC91?h0%sJqM$#1;SWkkKZGAsI{6-zS7`8shZ9#TeHwP^GPz?^U z+vA`EI(YriYC}(VN%+b&)J?X5WvEB+8I7C3c44gS)e}T|*0>T<24)^?uR6WflUa<0 zIQl+n%Yss`8mDpuBXF{G$~fY3u8)V*0DXxXV*9D4Gl0Q&gJ!*U+IlOejSq1YnYJfh zUNIqL>lA1&m61VDMih}7Q&P3cGBk$4549F$Bs?U=uo;Q~sv*W!YkG(q(X=Hss0%nQ zfGxK5HZ2l2UwYJgkN#sie({f_g*=W)o4xWnLr_X?T5!!UT?{eB`a>^fI@x%Suv6Y^ zL2d$A3aKT(p1dM>H$(jUA+Zyr6?x5gv(Bqd#N%EWrfCvS;!pMBNVyA#>o%7ta`ytn zgxYcndOmUD1I3U~mKGpNsIXihhol9DVGc91xE2p%1Cioc9ycJim&?~aj_|YZFY(HA zLq)6t9C4Z}^BVSCx5n%P73sDus!{LjKd@`x-T_5X`?GWoH&fbJpUR98bIuo|@!>0%5R<2WvQIVY3U`P05j} zo@IfXuyLw6R)CLSt$&*9)irFn48@{Hqh$02j}$ZtDV8<*B|4l};TrWr;pYe4~AqE;4RT!TJ-0sP=!HT_Uc@p7zn=JPb-y5m+F8BeZ*Dnyij)E(#Q< zD%96Co$KFWonh5;uSf`a!fDH`sx(`+!LgA?52G#ANAm+UZn7GM3}93nwnsMLC|#ce zybOAYpZv9_R|Z65HyR&Av$4sK^tNz^K@@(3esg4Ger|P-8{+1343o2sR(3)Bou7BfxKdr>Y^r&y^t5mxXWHf;|WXGa-hPTPN zpd2+Bs8O-_e+QI0AsI_f*C(gzCAcjdb)p3HHxG{?j1JO%MGg9@z!vB9DC0 zkWUz!sjk6PN$*Aa=FQtMO63HcsP8;Sh@y(Ky5gasRR9fkgz_Y_vlWCBw>MqRg%3CO zapE;P@*{IGFsxez7Bn%mgTUwq<{=&oBhnl)CwXJI(McFlLx_A(ddYjW=?3A2?Az3yYD&6UlTc$ZbU`J7N}vm0DdWv+iptomZfh4i zmVy$W*}2`JcLPs!&&w1dWL;#m7^Yp+O*NJJ6JC`}gzeC_zxdOOd(JHX5jYLp)C z0C~7N#U4+_D-(RWvq)0QSRgSX5*gL@iFOt-zIBZWNv@LwHWV-@A1n&`{m1u=2*S$7s{~Iqw)GD7Go>HB~dTPf!TQj|~n@*Q;BK zl&pR~W%@}+YQzo%E(T6+$+NK{Ih}SeuCFODJph5H*aC!(DQ+C0PyCT2(joA^t|LiR z47{UvgI0l8977xyrSZBEYe?C)6E0egE0+ZmsYYTXsK-E`&gNc-f2q|4_W{?tL**X6 zmxDJ|MsWJ}@+gO_(A&pm)IGzJDYPmYJAOE{c<;NSWosyrJbyWeJwofXy%s4uvRK54 z%GV-&Ez~4k6_6puwT0NR?jPM<%$D0TWOCpNwP_pyFE;VE`A9M1y_f1#p_pGVuuW?3<9Ge!ZH7s?6 zpN{CsT#?(_$E#Nh?$$Nx78E$0?LJIVx>v@8H@g%&3{I7&7T=-qT7`BH*RH?c{%LHv z(+Uj33hcheR*Umdk1duaORdt^v~4Xtsu95@jPzV&AEX42CJ!uEk5HfC$j9WQl)Lj|3tT3%&amao53u89&_Z?hMis_KS95YT9PyWjJv0h!WRXnM&>WHqEXaww zTW4-Il3y)Fl-Hm;=mZ$#jkpd(*Z1QT&4mzhV4`a4F(OP;EibFd3EYI7>HTC$0>2X!Q3(E;&bJ2_c%h>jY6F&Xg-BPS!IT15@K^wexH; ze8(M~dg5l(O6$aK_M-EheFLZJW#ly_3OSxo_ zSb{4tYE{Uxu+uBsn~QaMP1i@^^2$26I3fmvpCj1|--ymu+i`k2wV^G=u5LJagot_R z9lTt0IH@0xHe~p0c6A3@7OA^THe%{*q4Sn3|3Yp?fKV>u#WLZ#c<&DZ_+++_SFExH z2a+bmhu{RHX8>EXC~4OwiMu``;`C##BAL7$#iU0ZY|%uoD+z)z*7YP+g`LgPZq0KJ zwkBhy%D66sgDvq$*l1cQCJwsT6Ih*QYi}PeG4@Rx?BT>W_93LxPng&A?Yh?>${?O14uAn%2!yqTT5>>rMY}8``laCZ-uyk!)uuDnd^6D^DLeF>+i3RPnW#WdjNk zxKa!duGOTUGIk(p8+D3{CIxS$Sg?7&skInxP{tvqGl>_9{G*n23JaR!yg!oWcMOyG zrFj%wK2PwvSjNerX`Im#(U}}BG>%3bsVxVX$og7JYz!D(E*?wv$@29M3~il2ck!~6 zaoi0^pJ&$lg`S|buUbhFE?s%;@t~9~h?f`q`G;^yIAqiC0aQ_$Kn*pi zRHU&@*GkWhM2RK8XgYY3FkJYE<(H#*^Obtf1b>?;A~Gj9D<=Ex zQgvImRC7?Sm*BkfJ65XLH3XNYd#FUr0c@AnoDH>!HP?k;I)bJiTn}OXs zw^R7s!~a3bD^wd)0jiAfoQA*99G#fm{o^#IJQjVpLN5 zKxYnw$5qD#(Ue-~Mp8c|2IO%sgNTf-_koV_Yd)`lOY>%4^LYm_E02{q&8Gz#zvWBq z!LjAk`aPJg8ll!YEf3|u$;#A7KQ5p>cK|^av`OycIWX|6HzwrE<`oLiq~TMh3}}|p zI!j)9UhQC|{nIdIvZ*A#l2 zv=8*+_;$v}uPxqpbb|bW8KX&^eG?<|*4Uk|GKJRG6h%sn z7%jDv1d(nlYn)n;?Xe(LynB`B@Y|=*9qBe&tocs|y&hV?^jKXM&uF8?mleo#?d{Th z4phQ+;@L}Kw%p4bRSuP$BP_Rbj5BelLV#^P!F!Q-F)!4UqxgeP6LRQ-Wg%Z6N76FG znv&+gZz~vUa8w_G;I~zl)krOsx;on>7jmy&Q(Cj$3g8sLiA1pqLL`vhhbOxQm$*-! zn}RGX>B1jPmnkruPD*QPn_+86n{+%{$5)(Yx%WVOGCY}dj_wB#t+^Rm zWcy|%fk@K*5D-CEVbff92m7f5=rs8R44ZTE01RFnvHVVaXGFgri3;`l3 zgk~XmDXkaiN#|q^M2i+{?#A|_aiMm!L|R0-o^P0r1N1Cefz!Om8J_dW;C>z%1o0Rg ze#2E zc)Fg!3>*~*&z!YV{dkybf=p5C1c7~1SifKn3~}a$Kp+)Pb%?yv{a;#0v|4>UatasF zC$X3l9CA_)SS_$A;e_Ddbm5mCH^j_$?5RxbJcPv= z_^f)*oj8N5ZGE!<{a85r41cTbr{1cXIgL;M>`MP`KVEe>D~ln#|5MF z?ohD$>8xZvKXQ;;9PCtJJ9`kYmd)v_%A5HVGXfH_IK*d+JINcn4N>QCkn(d#+bfvaI`zsDi zN<9D&Em#yNhIThT_88#5p`z~Qbv3IMS9Ne$mfEmU6j?1aliLS6SzAHq9=xlhq zxZZh`6|_D%QCf!mG&eWOyG`BpQyGOWjFZE}_OWk?y>5|-G!q1 zJ!eWH2^1IAzNilxjJxLTbw#nb1%HV+DO5>HN~?LLAr^w+Tw@|Pyaas2ErPPEAK}j1 zN3o3)r$Nf@#!hHp8kC!K_L7e~N40~ZRxzFpY_gaQJ7{@YP@h^$$g~eRsgtJCcbm~k z(hbQ%@fIwygjOkQfG#PDZWR6?+UAWip5yRk(||NW%v9g^ZOSsfk7)mGAqO2xj;Qf) zj0dP?iZp!a-M4qguAK+>_sB-q%}h7pH+n*J8kzh3D5=G*X)Yy>A%h(>4Y-XX5to%O zaCoqJU`-Re7%7}ARkRz$Qt~vk^+3=5oqY!eq>=f0Irw>i%Mh&I_J)!`Ot^~)iyq`D zCQ-XQlxJaVcR)oOM?@|(Y*eWy{nHv)#8%(#_f1!ZPvNv#x;%g~D=WCXAiMA&OY2ze z?LoZdGqkX~P*hY9=Mx^QFzCs?0rSO&$B@X&4l<+)c6z#zgCOgNnToX;X$A= zsKJ`VhNV)zfTiTUWSkP2DFGo-)0q!^67~Xeqe{Y}6uCD?~*g4{hSOliX`u*V=92V3B#-SG1&Bn|jP|7hwR&99P$dJL zbX<qY3i}2Q!`J2^+f73Y)EFQgOf^wKM+SkOVywc!m#Gg) znva?2F7kJlYwtgGVi)92vm|~;>>cg{1+HREy!xsM!KQD_#Wus)R@=3XP&?JFoxS&4 zYp4qHargG^ef{zjc-S>H*L1kz8vHPJuMIIZ{NUyZ3lPl-j44B zo?xBin2}UO+NAtO1bD#rQXFCd*lhsV zk$br^)`H4Qak)*6Q-2I>=p>JQlFTwSiY!JR>y8#)IZ&>A7Ho?A8Aqh|3RjZLW35vS zdW=A8kcLrvGBwV@)Qvp>?`s6@xg7CG^4KRIrcQm;wAsafY@W*d0CY{6hy;pQ0x<8bCV*${6_^^~ae7JQ$wl>D&jadUQ06so79z_lP=>eKr&#;Q7VYf~rEs5{`97c>Q_CbJ=P7yka9Z^6;kfMCvkMjO z*&oXG?2n52Dod`~1-K!v9usk$6@t%!D?=F#mMhz-Gs`VfSKNV8Y$9Zj=%={wj68`q z3bp|a{m*C_WmK-t6vj!tacPjz{u)Y?rX)jL9B27Pr9nvl%a4wZ13B#YcFhw zB)P+I$oR0(Ul5iS3np9s?K$I*OOjoCdv_n)PB`;`iDJP7RP>$$*W2GOa?Zd(o*AAL z^PQFF1|GD$F5rR_>5yT)3qDcQ){)B&0#ySE#l<^*rRa^7%JG2PM6*dz`n@yrW~nMi`Yuo3|XR2XcNa4J~rc0iSm{NT}Vd`bq1FSqYp1S%#aS=;(%_wXZYi|?k(R#%09>~jnbfw?(14ClTe@7>dB5Wx}6-bvE8ll~m zBJN6m7H1&#LDX2rGqS4#E@<*GUR7W3rtX zKAD;09X{uF5qe?&K73y4FU0H<&#Ilf0>C6|H#E~B=7le`Y@}yov#Rbm!5AiZ=Rm07 zh*STNEX(0*U|d}&$r+S{at)Q^l-QR1X|oXY&>NOFsUXFBz5uvx78xk0b7HO02H z0DT{D^xLXmP&X}RQZiytFcQb5}ZB~!`Ae>Rkr1 z6y3Ge>;fXg7NeKMV{_3a^*u!ZEGLXd0TFG#8A~Cv;4TQ@iJAU15d?ST8w136#ngk@aG< zAl}j?D)#Xqa#vyr1z$a96SD&4h)5)40Dr;zp8+fR9wA7}_$U@8nIkMpVvve#YI+_h zH7aq@LgMLlW;qjp!PLrcfCD_x9;_Bg4H79@GVnNPxod=XnI_W{xB>2zI8kw|oH$gq zs4XfgH1!Yw+Tur`)dZLdLb(HWWEoq)Wq(2~8OPqGl(AC*z7RggkX zklMT{9jQc%wbWnx9Y^N$No)sVEZlM|_!)F?>#_5ERg@OmH=4=rc10+X1t6;6IZxCln};uI{>21>Gd=c3p--02k7df~B)!#~{ibM*w%@S^w) z<#e`ltxlCXOk%PlP-k4~I=Y9_r}mt_>Ys1t-pQt|b~aRi(af8fE|-SH?TOp%jZ{YH zv}gvAwCTu|vW}g9S8^xKms=$uqmGxxggA4Dz02HRBhYfcD5&H=k4Y z)57}pYs5JO;|tmldr8I91yaRc?6ubHMxSs~gDjP>8w;K0`NtsMUMH8_Z! z!r+4ga1p_eE7v4rKw)PRPLUYtfTfPT9oRjG`$rO-CNaT79>&gvnBCL0`t(Sps)fyw zm45R9YhUuE27@<3%D2G~6N?^Rv>rV6_8-J}Ya6ee#gxi*_2M3R?Dw66Ox2@HB^8@$ zH}azFvl}-!82gm8oxO9Df5Rg(^$%?H4|v3f!^f*Dq>M>)zpszW74-M|R(JXtzub5+3fEwJG{No+*gd zabEgkCr~V=W-jncc1WZH`w#X}tnKL8JyG$uPF3&RNIEb60I@*m@DQZ3z{l+mTaJ9S*5%qcV=; zlVwT{FKwun3-~ucbP!RWHFR~jGznLxGKi)DW&py~PW*=-uhBAx92n}EQN>n?cWMik zCivn7W##n!0!tGdghAw$14Cm#J6zEuEocAh<`dsY#bsOXQd1Ljp$U^a)B^#Y4lNt{ zOhZMHcE~Z0uX45wesxs>4^c>ypE#S7IU9TxNdgp_L_lSahuD-62kQlk3?@EYte?8X zrQo~K=HJrJI}gFQU~UH@g=I4rOM?5EW16RE&zECjn0H*xmjfqmP9c4iN4PJPuRc`6 z&6L^)4cd67l`a@C3%DRr>m!vomL+q~r#LXtGSq>UC!JY!HCET>PY<)y{Ha}RQR^&c z=Z7H4ivhu|fD|SmALUzPHXj@AIASb-weRpd?wxV(_q?sU!W8tv&eC;Ee!jyV5VWVOrhk z7HfBFeG=dcTt+lurQLD&PAY!&(4<$AnfENaPgm4k*lqv@AQ%`eXYVtaSG#bIzUDKt zVfjT2!t#wpxuy@{T)HosFGKEdt8W<53RM@jgDTJrz4>AMiJ{WipV$dSPEv^M+xPkt z%Z$h1g&6@mjBlp1AoB1WV_TpQ0@*cZ$k3D43IP`3MCs+WI>3m!*}a5VvkAyLJDW}& zk7e^+7}H{1?w*3#c0xwB;Z3FtItLvxZTmd!v` z7I6@9tfQcL-IYO{4BZIkjOfj-L4#`|p&_p{o$KsO-t6A&UZ5Yx+@gKq&J3uC0?$-tv3TW5j#tklVQ77^PU5J6<~a9 zIE<^(FEN230yLVvj&-b(o^i+SteRMWaKXj)jb8HeibaglV9`jVZ?2OG*+UA2$8qhY zKc5_z$Sb9e$%2cEyj@ZHelKMnpu{8D+Jwf-I2kG8LgUg0DpNxg84Yho%U`+{ZKMSm z6+trXv|sO-r*033<@Oj{Ck)S&rpHfBPmYhC96vrYdi?m*$BG0XVq*{S2lk)<>~dG0vAo;s2GINQggn8@?gHTZ*8 zqD&Aq#N+i2bR@VLR1_nS`u|kjUu}-rF1_d6;@c%vCmrmJ%TW@M8p)+!Dr(ys!m-VnN z!YG#5y6&l8<5MvM);_g1G-*l})11l{_sL!+d#~(?msffVGC?6z#@({UE9(rLm*;cL z^*oYi94p{67$yu=C{YIc?$awLj;T%speVh5hf3`ri;F6`gDftlyT`?K@|DG9i^l_3 zDt7lSu9L4Ut}m3ub^3sdi|Ouhah-l;aUEuHo&G#8uG8>vNql~fl*?s*g>sFLpFQB> zV!C@=TxVZdT(2xHT82QgoaHaS5ZJ;a&m4i4llP)|VJDTB55yNjuHJifWks#yJaFJi&v_&5X?`LGuWvLt!ZQ(Ax#p?zE6w>SE=gf~TU|3hRcFXlKo zsh^nRzWWTHD>=LOG!W~js#tC{ zcEm|N)!!q98F9H;cpPH~C(DI1#qn|2B2f_y-42z7Zf@LIiNAdtCuwzhPK&WrHL;D< zIHg*1`6-rps#|Bq;5>JpGS3Xv7WG8Q@Zd8?JI_KJ3FHFTsZeR?zYlrn>21`r;c-M93Jc(SkdS%^^J_Y(rB%%HV}ay@M@|*m*M#rLo@6KE>hmIK^P(T z&4#Wn3UpDvrDxyRWmUX8uhF7+MO7z8hP2RmsOySHc}`Q#w$uxw1sEqlPpe8gYbIKT z)<=!&(`4&mc#(}X7~5h&i?y?dE3$VG;xZ2lKeCw znYaXMwt3hhQy{Yyl5-LtmnW0z+AgG?*fq;u*-Q-1@Y*JeT3Lvm`8)>7T(v++m+myk zQPjcZoi*OH%4?QX*9$dZP@FR@UNT<*eagkw!ChCf>crit1&pt@0;fW-*bAeNxSbQ+ z5i0wYoB^USw|(hz%3d{FECw{j-qH@-?L@#>)XMPNC)z2$5_f^ZTp9rw0*zCd^QtoK zv)}1)SlGyhN`SiQ;fcl|iN>HHGTiY*&9*AG4GZvsDg~hnlgn32ON;s&kZ%b%-01V* z9QFqq+;LXags;_|O4m^cMZiG0+;7Y7kx+qN7z7b1o;z_GwtIX(K3+UIe&!6fOSQn| z$|e-ShbZrO5iDY1@B|*d5Nq=ltZ=s3(E+#WU01Es=%ij)Zog0Bx4YX+J9=tl2)9W$ z>gr--7Koa~+9>d2rvn4^#_k(yyTE-sCs}I~UFWBAU!0fk_*|m{Bxz(&_5v7b-cj3X zqZX3qxbP|U+CAB*W63R7+VyR2?+v~|U(=>qx~nVK<`!`U!rbaEwiKE2O1oa|Nwy+K z2Xs!DXmz3`Zj6g6#J@*mDD>I|XX$#|7xmcji~_%+|E>MY{`cnntj!HtmnUS13n zq!KJa$eYEUyd7JC!M&h#8o4ksr!g%Cv94D-%4XiU(#8OU;pL--7q1k2yWCovB z8v-{>vFB;}zZ744QLS%7!`uvhN>;Ah!qZ$$vT`VmD3i)5S z<==lC6dNq65Ro%woFy^!)E%m1P(fQEl-fdi*87(Yc3oJ>mR#8^mKqu?qc@0$S(x$1P{?k z$MU((!Oe(P?eT$Q1v1DqWEF!v+`YC>{t#ALX6PNtaP(EUVaO~1=1+xbS2((;R%j{d z=z$+UrS9rkazbN*Lju-_*vBOx*a6)%cB(Z;Z4T=e$bnx_xC>@PFw#@I_6 zBGM?sS8`5}3`=P>!kz@}5u+eQC64#+Uf)*cvX>xbMjmHyIOgb7Ya1z1=v<|rRRs;uw9Q^JhX0VpNDle5?MDzC|1(*PWJwz*9 z0hMqpiK&&yhoA*}u>E`>0f<)DMB^pqAYN5PW86V?*5#Zc8CG`E1nx{X^c+BwTuL4< z;)wTf&BZi>@olE$)}mQ*=@UJjBl{Jov7C zn}W)M(kGeRRs$S((6Tt?^=8EZw~bN!om`-zI44QsyVde|y(VnE{S*w77Gkk0ZMqgh z|6P7`Oh_C0eP}%7Xo)j$(p@){p8CS`QTb`pv)-Dp7zrVAo=WQ@&+qbCfId7TEz&@FxxoZY_HB{f_ zP1$&YB?2vy^GPZTUYmh;7Dw|=jueJqHodTp*o%WA?}0EB#Jwsj!MP_8%~~5k>B^6# zCOB~B<>})8>V($CzFSp_VuIR+u4$C1J|X*Egn4#K^iU@$28mO?Ud`DIrH5UWKq*au zrJwVlXkfT#XT^kdGDZ~*jCj-o+3h=?<>p(S<{&V#GF*7 zaJ%3dnhS~?($vHsU2}_#1I6GLohwDUdw@~ws7$Oz>4cNmMX<@lv4(V9Or;4`=}gbJ zrmOXCsaeIc0*2ZV$&wyc35~6+`8zr4amt=dkrraG5q@x;79NU6jTM$m3#*3iPA7}g zbv7CY=s3?Bgl7tL1Tc(%Ksm^u^8kb%q67!~zF9nhm^r}^mP!#P_f&(f6!GURd^d*( ziy`tYjk=+esq@2XL-93Mi*lqM`smUjL{uEjutahy;$p#|1ckCFcN)Q!mpe^@InwEw zZ_R*+))re3en5E~3So&`^=|mhHC0WZnc2?-JBlK$6)I@_>sam$661Wq}41>UzMJ^oR{oqf1nuybmdg)2$g8L zb1@7~gpB8mV$Kjj_gSw%0gOdaQytUA!IO?80W@-#hGMW_WR_uJ)7)5u5lJvAdE;!t zkJiWb?+Xi3OItI5C2}}ONi10Au5Z3j;gdShM-G@{e58Owg(-HhkP8_@>5019HT~E`Mpk+lZo@umJyECg`q609R-2$q) zU6XGDPr$VxsU50In-xDD9T_oBo3*3!z#Y}k8)ZF0-JTF?QvA5GU=ZK<2Z9C+2WclZkYbSc$I!B_ol zJVAatY_O`f8C&j_I#i%^XPa4OKO(T%L7s2D=%O=?&XOhG5#iUdyYM0G1mp{l&`NYH z(+zz;x&gU_(ggT1V4I%YE1T`s&c>!IM94qXK@`$N&p6$ zMG2+mpuJ&34k>}8HLCQ%6V*Xn7VhR-pgEXzZas#CVxx)szWpbd2lNsjhb{vax`FP# zq_Rv7K^ zrbQO;j4`HGD05y@oDqWm1}{=LfnBSzoqFAPOmgu38lB(=jhm)}DbY-*%Ie}EUFAm~EtB&S5u}UN+|?D!N7xAAN=6XB2}7Bd=>i=`pF4 z7=W!dNvJqh|3RYHw9&kaWDly)A7{E{or5MLbQ<7HhbxWuNgMH(L6?*sXqbUqaGwSZi&=-_~GU{~*;2dQTU~I#7$Xn5#?(@vPyS+rvlYselv4;Y2 za#_lF_XBUNR8I@`W4=R1{vJSo&#P)bFTnkUz&AU%0JNmbzdvs8yABS)563L!zFep# z4;Vj6x61z#iuCgmv?nd_UbQO4Wb}k)xF&LH3bKsTcWV_n6t>tF$HBmH7j^IbAoTo@ zsQMLnP1$PQ?LU;9m+}CgM@Hmc_yTuQC)~r_pEJnI6*7^4=a*C`kd~LL5-^e`90K%r zLd%gQj1D9REGMYk`|j@bMH>1+z00R`R2lrVHn}C+6IKTo!G2`6mDx|yQeC~m(?$#y z$yboUS02PUB$Fb@Sv4HZQ5b~0K?WyA!@!0pzX0ozU>_C8>QE%wq0dx1rXAff!Jigh zdtd#@Gsj?FXgZ9sbrMv~xkfW@mSQ}RdJ$DgPkUr`8NNn;FpjGyT|s#k0gq3H?RW6* zOX|;{x@<%#Ub`BOeKgpc@eHCfqw(GH8)DjBk)4}^w+#QBP*x0C7} zamv})uz~|qv;o6S4IqP@*>lDMnLZg|WrMd?d4>Vb7P7V_^dx}4RC^X{n6k-7dI}&1 zdx!#r^lwF)JkD&jGy6d@yV@O?6YPBb{R;?_v;J!ahd%4x^}FQEx@6}~ zLn^jM)AN$^ssf!_ui=M=xTD9+JHs%BYQ z<67ZbJ11Xh=PvH`#E#2eDbvw*Feq^6jL{TDhvVG`GIC*W&pzz~7%`k?v_|0o^oPFw zU(kshILPZ5n_66#gkLurNY_hnVy-uu!2u#3p)dk!<*20-A@6q zC~(#RXOL?Lh7!(jRM63XyvyU|F(1Zndvd#NN4p|kX^+E5kKNsBIKl(v3H%V?{tuX@ z%RZFuD0N1UpE!MD@G*ZjD(7gFYGrVM0d$XNyVcmn+*?h;X_Rh`I8zJfcp6R>JX4I! z08a7;7wKj}v{VE)5XPQtjxd6&jI+Vx7HaC3?R0)^XZNWM1|d%J4Qo5+vSALiA-XTr zt3Y=?dvqT<9i=PRXQwXu1Er~hhoH-WQBBZ>=q$h6xAe6*2@Dm%+rphzyXul~hmFZ} zht23DSao}w0f3hlfwMl9tcO@!nJY`&%r z)7Er38pk zH{|7(0&6Bx)ls%Ev`Q|*$1&wu&R6s2&vjPV%q>6_DkwlwS5$_i|1w?8nc8E<4HrZip8v3w_m*JQKjPZ?6BD7xiHF7+kt7ETQK5~ChrI7NNP?^K-8 zwxwCOJ4w)8Ohc;q?n@8i1G7cq8S}?Q+naS!VjAhyt3<;3 z%-dL4SsZmwhrCn&<1ZJ#hU8u_)jXsJ(oRC+Momt6gP-~W@N1|_B_JCrw_X20DG>wU z1IG$OMdNaT?i{&byio(`imm+!X`-2j>) z_YriZzu!jb7_JgFca4_SmytGFx)imlPh0mGaMul=6C-hmB71ue@*9+S*pJ;--h!!8An!B%KH8Q?A2%GS z6Z@AQL*G1c^4!hiXV2U`e(tzI>%BRnRVS#y=|3>zLn5=E1`ZjbXvrrW3=+BP>VGhE zGtWL{@UCB$f#+EkaoA;`D0kbO%o6R!T#MT_Y}=)B2XTDMT_M~qC&@v+)O{J?eTi1aJ-J#wbih_c1qeW^&?0merc74!?+HQADoqQN5!@tB@xz2Cr@)Y%7k9_HGiQ%JX0KXThRzpH{v|V`H%{L28}F*E_Nggm zxzdH{;;iZDD-(Ah;vE2niugSEqIJk_rmIdZ;9Y%(=v}r&z4V`ae(C^v&jzuJE@;8i zy}T6@90YoMmU=&a6yR%VdzYr`-c>labd-M{<^zBhIrP346Uc@p4*ZCO*fysv3u&5HmrD*fYbM#u*~4~T$tYxun73wc^#Zt7A87*7wo%>3ZFA}l z8oAU>GrHxRMZ;4OJZ09}f!AnVe(ro9+N_y6=G*x=4z6nw=X4oKd}`;Dmb-4W-NAee2QkQm1J?r?3ZmTNiDr#6W?Qy8gc09Gr*FI5374kAgC<71mGG-(H6KK(0$l^?M@B%XA3~O_#6-BiZtAvt%fO7 z8$oP$!Kis*O1)gG=@(4c1hWqoumeYbPMh8C_W7}~PK7SSJH;(`A17{B*cz+SqbiZ= z*j7i#&KU62NqDuP|2;VZX%tk)hAC9o zg+hUyAWq+vo3l-+hIo95xRe5Zsq>wSgN2(z4-H3OkKG(1io7|5=isHI9k^W#w|B&u zpkXOFN(4IBu}eKL4BZ9#RX+a!gjTS(*^MP*V2b~C=(Mlmv#GYjcmcuqD9WP=3^XXI(DDBe~D6W_I z)s6aX0%V_WR-G>~6$mg^oRN5O?8Q>;b;E&DyxX1I&Qh+6#cMx^0eg1B-Ilh`f)0Z3 zPJ!@9Py)VSufpxa^_1oIxie*@Fq|oCkhnyUBU%<#vzkYBA^d})!NfnqOGq6QE`FhzdH1Lj|^&_nNg z$ZABkkG=h&VMj?loy849N@a!A3;N8xk^!iLg1K6xnB&9@x|U5%`*exkGcJrpQ6cG( z4?_5O;@lF-Z!T1a5QGY(N#RSS)oko$Q~PBN6wl+aJZz@))cruW;zR2$nUnXmTQ2tA z?ha6%L239RxaEMP%TF|dBN$Rs95n&U&}#1n>kiy!cyEN1l8r-A(J2ZKCUAejI_xlz zKlbXm1-U(7sYZ_|m$ut*0MorN7RH5B6#D(Sbn3!b^cx8yh|nuIG62zYfrmjwZ#*Ym zCAJjEqPq+-CFfp>V5R4Ovl{v?x_~&$!h64GpKX^3{;>lUoEvZNRz*bgJ#RNq=rESh z@1_^*xRMxt<@o4!QkPfWlZhnr!9(DB=NzVyd?$C=htWzolBG`h-K=9-SiLc0%KDHa zy&XZ?h}5))5V(^Uq;mZwv(Fr&Y7Jo4vAVurj@;Vl(a48+4lsoN?gWQy(+eVW2z?#V z*cD&}5x3LTX)=_X_-Jx1GKyo9zu>MLz*=>hG0}aAbevgJ(5r{gs3WTSGOt04F>@vd zXCRY4Td&h1#Z?33?8BuMAsg-&7cFQ(pHfU^&{LKvgf2O`2vVEg8$swk!&y%k?JN0Cu=0cw ztx)e4eO2VL3gV-`&;j$*fRjdl#uPU|!bfKZ5%AGDQs~rbg`oK;3}#DKPg>vw70wGJ zA5fpwfKt_X6|$f2pGojROq8p%4lm3yaC>GN9d!kGeG4NvOMUGQ4tD*A&EShs`?;nm z0gg2fszCrTC4AC=M;W@$=`A4{0!na{HRDOD#PqJ2h}U#Y#)=7V3UTxm!xndxAA0A; z_6%-!$^ee7)o$68Cm0B)?Hu;o28D|_cB(t&_-(%LxDUsmr?sVkQOHky=*LZlAuYtJAS)^`|USt z4IpAJ({RH*`jB>|H(xr=VQEEhPjKK}cJgSM)N7csCk)r{FM$D%^BWy(dk-Eh9xZ~a z8X3Z{<$ZPE3^_B2pk+VBQPupMQ(u&3kLD=^v%P2}T%}{@45sPLst(0^!CnnO zP&>yG9pz)Xi_biJ#9AGdk=s?x95tP28TAk?Tk|(SZ!4SPCv{|yy&grci??1+23TE< zJjjYED4JlBfxg^kBua|Ub#4UJ#ByWvJPG%b>4wXRxbM7&8 z!i#Y11RU@W*u;*^22L*N99v|8BgfvuNci?@W2d%4ul_=wR^j5s@{kf)BJjoSU4E}` zOM*0$*pX}DkX?2fycsH*3^uyogi5h$*I6!)*i={*TAK6l;`uDSbjSL^EXR0$g(_8* zfb7eS2VGZn(2-C^j-%;9y0$n_dkic@1szk*`^yNqK}Br215Up?!Z(zjw~Dah0Z+Pw z)M2jArVY2&I7svh$tQdLaaVpSZsnr`TvyV?qS@ZjX4`y@FuGQy;h^4-?b_ePCM%I4 zILS?HyiGPt*MTG2o7>yjn7el~F%|jkOom2|R4h*rV|jv@Y^HWaHvN`j2Ofi9PYnVq zJ8j&nsvQad5wmP=fD{J3kWdwrfnIUl2lsP^<-oLnPK<1AV4$?DH|`-7qDYg-YSRxm?s$3T05IA&;sSwxLZS%F#X2=*m^ zruxE|i^A-?&PFYE9nMEk6)}V<6V`|I-7P{aL7*Y;oEgh#aB3HgsaMRsBao8!mCvvK z2OZVpBjy956+K<^D;|xyZVBLDXoM>{u%28V?HWZ;7bLbvK2+0xMC&zitf+QBJ&1?h%$SV+zT z1nD=893>qV+AXSXx2XqT+s4Wn0Uw;f&Liw1ROgzLgWG_;xp3#y^7d+_wr5UWF`kok z_3pBK$x>(SYTn@JGzk!u<08{|2&Ufyi&(EVX}^GJhtu@A}m86tCfko5zi+^-<6C8b}s z1~S#{2LQ4{GI@ye`p*7Hep^zxC__haAOZUT#4q-ckeT(Ggy0u_vn$2rh=<>cFY%)+MhA1U7OX_pfVd((mt z3E_DU=G3-wh(XI(!qkAzKq`A&?LRMZ_n~-l{N(X-#p4&o@=^eQzMQZZJA!>#ppOI0 z!_Au)#x(KXL^=!0eQceuui(IK|9GcnzwMB!?i^UAH|TpX(${DCeDRmKc=>D5-NwW{zX zwDcEtEsC9*n}e=ObPv`PAikLGQ~As8SIidDTdEca^k=e?>tPPr1NO(3{5DWY6&icA zw5>zK&CNmqR4cw4Nbcy{<32)>^3x)(N{N?1;<*FsYqDq+Yjd*@w+X@O$t7Rh7BPUD z+&Pa=@&XsiK z4UHurX^UJ&`_}B`H{Hf)uOt|#y*_90M@CE8Gk8IBbpTCw$#Kl*8X-8tFs4$2u0j@( zE*_&U89ggWcmzx*4UWQ5v^XnaET@Q^MO^c@No)OBwuA+Tqx}ufok$$?(eAeCONNGf zKcMBNrbx@>?b3n{t*yPeN}b(i1@^tJb-o@J*JnI&qAa%$(Ot7#)`kozYtQ1MO4s&m zbpNXER3K=VyQ*QRH}h(RV0yb2-x|xR`(7Ix835;8D=YdsRrjRhn16-r-u2i-^>0Z3 zZg)C@F1<*jK;o}u_EX9R>~+EOCER{ogUYbEu{?WiYTDfi04aRWTzQb>D^Kr)zSr7M z9H9Dq$Xn-q(dbQC49*l991Tn-OKwk&Zk1+Bd+$`f#5C|VW`)Ytd2(BGE(l06jAwY8 zbf0oHRo{~H`zFd9wJ|_foEA8ZrJO1vYTD80ghj6KN-;Q9{$QIk&5Zl)akjJ6tU_6a zF?G9!QH9*;aQ8KmVfE@v7UT4L+;x=e^%N}Q0y1@I0Y~CA3ytb>celaPZD@e4IJ8sL zZxL}sH{1%#(dX09NLsYwuM9QBT7Wyih9VS?PJr>Z;C$JRrRK1dP;d7zS`O$92Ah#`2-G(s1!lPb=Bp<@ z;wD^LSk*;*f}2k#--T629w2S-S&qB;Q@|^V7#eA1cOTHJ9EKo|aoEZQxSj%Y)U*yV zF0Xd#6(gB?#q=db#zbElabYYckq&*j;DP?r3%Db2T?QC8;6@x|TFNtk?TL2a2kPh= zM^x9`Sj53rJY2pg&NA6ZTR%*_BV(P3g4obj*-b!7)BuE#8QZ9xL2b>)5&L;L|IN5t5ia0K6K>iUXmKPpUvg^+y?gh7^V-VaI zH#?4#>*$hfdu}b4newd0dJb((k!gfa9M39;u7zyY+7Uo4Y}LEr$Bs3-H{Kd;)09D9 zhE2vIbdtwf-=;mpW~=qI_1U4E?X2%!??;kcgB2fNZ$OM?unHWu1;j5v>fX60oF08N zGX%RcM~Gs!M#w0X{TcinI<8;6H5+gUzC=a3v1UG(bK4#|e15S_&(ybP%d_=n8V+pW z9X9uAHzLX)plO>OQw772N_0E^vUl$2>Cqk2 z`EU{Rs56h`4Cx6Ktg#kEFKAWunG$xktP0Zd7RClp(!W13alYPKB57VUu>`fE(v-fk~ z-co4GD1ixMz-&}eJ8iFBIX`m5ZNk`h+4m6s$wgcm9~pXdi02EA9qEf;Fw1+I0#QC2 z3T)_xq@H-+1*qlPDlEgpwKz?4abb~lbzIs)ipixF5ZrcmXPcU^OaiyEq8)laDKQaA zeH=ML1G3@@;8f}Vh!DW4ij;jt($2B$P7S$e12;^bqsbEI- z^4jhuA3{6TbSuY~7ewD!(80J1_mtCY z;x>m63k94L=AW}K_M{=TEq)u;%k4_>cI7ViwB9m!35O&tQ=0A4GiTSUXHPt`es=Wa znUm*6Po1hgGJ3XBIXimp+?nGiPSj4HTsvD$y_yAu2!^Lcgs<9vybzp`&RS*b*{pT! zG=}Dl{M{MZXt#E@C0dzsi4|7dFF`k@ez)0~SL|m)ZvY)QFSrQ@eK?>ZE+o>LM?x!l z9q@2Ye7$ZaGU~{ndstuG;WYzJ?IA5Kb(;zj-8%=U}LrWmLpMXP7yjMB+1)}p$^ZR(pUBP>fGY3X3z1<|GeWPwUERv+AZ()z#HU=QGw4ksThK%5*YfMaEE2})-R`H zhWIZiL{MvQ%FIZO3}qsW!t}#wu%E+zR;961rAv7|awQrwzqyq%lvv@>AWOQQ6MS(Y zM2KJs9Zy=gl-?235AP#6?cnw3UXWL;-VKIl9lZqFE6m^D)g3H~OtibGi zu?>1OHAH37k|>+wjX4xO#FfZ3?S>qi?R#F$Jh7lx6nSk>$q(Br6tzKfg@2ae7iTSL zC8?mE+oB$FpCv;9A7NCehYSVWO~8`rkkZAa^Q9n0rs6*sgFCbmIr=@tyF6cnZ{Z>1 zt58H#ox2{CHD1{B`p}*3`si8S8=B?N0=!I+6kyczCofy{?41v&N}kKQ-@pjq_+xo+ zz+5c$nKPVO;iC47Gza?yUuztwbP0PLZxpL9X~@szC<6$Jy~YG3FPD&kLH@gc+6>+w z8O%hW1dL&?2N!=z!||5%hX7J)Fr#2{@3R3xZ50P=R3=V+aPL1!)(quov=tVWcK}fp zqv64d$0x1MMc|m&Srd*{ShL)T3h~!u>{CEvMf`P0S%m$A*8mA^JMaW0)Gj?h;f`H+ zd!J?V0S-DQ6Qv?!nt{3&y5a<%xE!PV8f7;7h-buEx@E@QK zcjMbV4hXa<5L;YhqC!Lh=ou25zr98Tr5qZf%REZshwmjrD)-bBUG&B(2Sc5~(qzyR zw?6OUwrHN%rZ2Kh&cTpnnAMS-P?MWOa#c_(ZnE2XPe0x&L;YaX( zm3CeBzvMGMI(2w~`m|cX(n9`}6BFVaq*EBK-38dy>4$8!aSR2uI#zsI%hqb8g=;EC zj^H3?y;`OUR}--VPU(+IQoX;Nc;(OZU<-eAVO7RswSxtS4O*D9nNi;;gSaamCF1Vu zGzIB^ukJ_%$M*3jHE&cZlFk?ARB>9^k$JIMhkc#VS4q2 z6M+-(AZ}M}!w9z_sWl0}4s+UclOB{Hh|!g>L8mku*HmSd zo(z>V#J^NH2}8!ZDvjFez7j7*w2IPmj7>F}WEgBMe!43~aOWBzazPZ+Jx7x$b{Xk)e?ov>qo>Axf;7|bwr~`k1 zZMs-kZHlKqMUDlQ^Qc&{b#}Ii>}%D++O94?fXlctu2OSavD2vlB90{5B^g{yx$BDj zHm(rnrZp}C_l>_(CqX(b+{oFfH>$9PuWi7s@o*D(#EFC!i7f_{^30JNgT>WBs(%K( zgZ~bo6VVloUKY!47nd7}^)DsFN$=jxiLLHJ37TJ@zbqjC^Hxr2TX~TkF^=kJa>qtl z`B)Y#fD3+-6g=J04L3pjjrky8 z@>R5w{NWa(Tjjb=y^$z)&W@{E)=H2EVHW=h7^_*0lANMRk;BJ|ijF-7rQ*q#s!g{8 zG}0VWv)-w$2hzrtWl7Q)huNO-u@TO($m8`?5 z^T{|fa184JS)kK@3|kXcm4V4SZ76_ok~fW(c5l?z!>?xO>!$xEL_MskxM@kc zTMd^AJ(8wwGENF;>~MwVwUu8a3D<%9%M1yR-r4xda>*?SV;iK?RpKjjy=Ju32DPxS zFI$`S2xbzv3P>B}dL(g0uxwgNx=v+Wq|=(jeUZv^Y5K+x(29(ckG4MLE2`0vmq?;h z16u*ZehZZA_Sh|2u*k5hg-I`bWAX8_hCM)h(wjp_cc5V`0$9*k%R0)uBHE(q8Sfx{ z5;K45QN;65=q>u%ClAmoy^|C>!f~N0&xai-TU8yPAYW$qNJ5H^SoT9`N(T=^M0{+;d=7+psdaT`R7(dwbtop%5gg>s~r6sGNwI zs8OX7CE;@G9Hz;UX@|T)Aqc@Pd4}Nv<6|%@`Gqyez+%1Qkz6CKO#YzKrEjF zoTY3?rd-G(^=j%@Mwehw4+g-6;P|O0^8!W~UhFoDZEUf$wuZ+?z*nDnpaxn)*l7}n;06YWozCdouql(HP{qU;2}6Vca>=d8V!uq93VGO3KWp&C=zcoB z$oj;GW2v)z$~G+NVdvB74)Y(H2X3e)pFblD(^Dhxl!V?4LtiebY!8xJmcY#q^SO}Fs>#vU%a;1G!_ZU`13I{l#Lcz!mJ&MMP=JK}O(i4Xsta?#o)W}D1mjtB?93w*SY_Y(o7T8tn31*wKaPn{$ zZ5)S63-IfpT^67Jq0;OMl~NZAm#@#xt&|s+7N)LGt}K_QW|tJ}I`$yIo&mm9C`g5d zC{^!D-;*t|1{tq{8V6Xxlrvt03I-F>;7PQ}0=G=GXh9(V?p7p^Z&PM4P!7FNQc*D)x1 z`enbB@P+-+7#WAR;KmW$G9_c<(4!2c^PeL&t2%_+-A%BZ?Fw~O%J_w1id6u*1dT18?Vn{n5VcE&_s<+B|@$;`02Cd{&(kv^iu$npYDEDd49jghcCVu#?5 zjU0S&<#_vSbhCq-$r#0P9uV+81DpF&0!gv8<6pItc9an z+{DR&e8lD_B~92A3t^G)Y+4doH~=H;;qXN+nnmi+SDg1;Or+2zQAAe}qEE@Yrm5KlV@ zF*UXGr+~no24ZS79*(OIVOybD3Y4^LOZqDX=>V=AC$jf20SXOcx^ZIr-C(3&BR*Q| zODk}EkkJ}Isx*@GO@d=TjM%%xbjd7q479=1^K4R2*Fc+uztRp0BvE=KYrm~ujIBhI zBbI0If=n@RJ^H0^A!~=ppC$6!QN}-5NHUFwSUI|by(0D(xeF*v5SP1`tQTWsfeIa9 zo3McQVT^Sy!`L|Bb(}l~)QLJ6DG#|}3sV3V#0lm+^*%gDJ&Y6E1@ zl<7Q9AD^M~Z9aKg+B|(`I@4xm?bGBG>4yyoNOyMhvWN5YhwtPWZOYl1s3|UU+!z2- zK*?kRmv&O>9^fQ>t-5;(b&ET|ja>c#T1*EpYWsEV0+e!uv{V7SCZ2~YGjYXA>T;mNLMk(jfAuL;$Bt!v52fg&{SGUkVzg#oK zFDl(+Q&+mm7r^RPbjm#UAYc>|;N1vcMC=CB^;OJ)a8U-!#k}?{M!F~sRDS&I%;hs@ z&Yhf@K6~o;>C;n_<0nd`$?-FaC)5s0m9hXQjLI`gKSHc&r5^xc9-o@NeD1{AbK_^v zoIE)-b#@%+Y5Lsc6f4^gjk-Q?e9;t62^K~;huovX*1}r7QSa`iDr9GZRy^*A9~uuq zV!5)~Y}TsaHK%dUW!2701u7#R&{QJmP`w8R)J#ywG+e`aHOGePb`w`pa*W$&xFD#n7E_aSxK*=+R;=F1UV6& z%jR^XTrT!s0AxVy&jK|De2d>^z}KMP@K4Uw8O)Mmt%Hn14ws-+2aJH1gaR%AnATt@ zWtheOZeO}X>A+;_ayrB1VTOW9H@+@ydOGvM z?Yt;u$3B~F9+IS{*kDlX27I4V6|?72fdjCEGip+g85j(bv2 z7;1@tsAn+a`3)ec7m9EhM5D1R#?~WoHOAn1n z?}4{R3U@Z^6)ZjD_}Xu&Y=K7)_0cyZ#Vt$W*?1)3g*!!2#bU#o`7obQEE$-B?~7p+ zTWFA`I)OBYBvu{XTs@?m?!%?RgI2WifLf(p8RnXTy=~;VQ5byanaPFunb|AXmr5(M z3-iwolCtnPNV}YR2D#Zg4u;icBP3?U9QROV1LS33Z#z1Sr3snhO*$P-TkY%I5<2Vrv1?`J^Z z9c;Lj;ITn&Z7&8ND83I?n%vb#KFY}dqec>##JzL!&{8Ef!V0=Ct(`7BRPB@fz>C?H z+Tp@up{SJ7%~)qMXpMZ;Wr^=j4$#>LumNe|VlS0^VP$gjW^?eE-h7IiuEd$iARUwF zRCwhKVZ*J|!G1zLjJ9D5t_lIZXO#mmOUabcD`Z<46tb;|P*l$;p+`2B5}a%-Ww;-% z+b9JPodLrTny1w;As4!0SmwAe@r7SzR*X9AHV_(v0jq6KT)9!x0$!gCq#*<1IFqGR1Hjl5i>#I!_lX8XV zFhN{y{(xh*uvsCdaCK!&hrALc((vo@52O|YAz_U(j0)0*KomAGg`a%&I}bGlBw4jH zX0sDWkwH5+x6CDiue=Dt&5Cu}rWi4&LY98*$og2(&G@}E(_G*^R!d(jf}@Kn9UqS|Ky|~ODG^ywR~9%#LPN@ zMjRA6juw2Gb>5@A3=e3;H-LH@d1N(XEA1i|q7@gjMtRLj$9|1wiS7)p6fLsQLN^D_%e*M#jY z>l+Fgtln8|uaDlT_DR&KY#B@MEkQYBl{CPs(Hlp)50;*}%VrL2d)ac!$#xf` z=0wbYnS%Zl&3t^LG^5u^VeGCElf?%l~=CM z&(2@L=PP2Iigr*P2tzwL72-A2$it~voSR)Lt{Ig_Lz(rnNL`31S)hS9~yLu_egh+bm`d&qzX#GAq;+XQ_eDazCO!EA$?h1 zDb39ZSR$cLZrETsk3~3w*rQp@!m5l>4Ljd6sW9pYF_7ET2W`tBE9CSEL(JkRtno5= zi_l%NCddKhyrf707>_!^mk=xH%Ag^&0)lP|VB9gs^H|%DPnQK#lqOfMm*#jXn#YFq z=uG`2-1s2^hkZ5T?%7Am^z)aV1J1&IIRgh=l@*8{}hgJJx>dA%0r^<_0OUu(3{H57N42I$wqU03cOqSoUr?wd(w-83~x=(3uc~NTr5vNIXS&Zl}Jij0y5x=itg{} zEkr(tO*SeL_m}h|_KVr)rjfM6)=Pn&S~qu-NwvJ9;n~W zU9R&Jt=)04yx%!Q?xYwC7|Lt2U$4Y=Zu;@*IW!gK7-(qr;vioPuXH|8IVW8?0Yv0h zCLB=^z!n;Qh5q|!~CqkrZPM!aOdBQV69(Snl7!BpC~Pn zEH9T!%jM~%rG+H|cmOqN516uCy1a0GrMxt~aveWbo>~lGER*v3JlL_R^5dnYS+1hA z)V%JQRWcA3yQrh+xH@6z(6hXJpv0DEZuat0Y3ZqKCT)}<8MGlB_S6Blqr40Ab5E5) z8yuZL2E{K*SHxX`5|$f-qwDK1?;Yj&IDcAJ=teO7yb*$zjPNik((~tNrwiW#X%0OJ zu1hAkAxU$za1F%hI&CyZ<-(>~8yUkK^DQyquqz~Zs#9Ak&yC&19e+|;CjPLjjG3G*tf6PE%7eN~N>8Oz zkDNtlvPiK7&TpX$hpwp3jKi?GRE|aU>}mQG+zE)GV{p;4q^E3oXx$(9*Tw2i?F+AB z2+uhi2uMqmQ=!SF%Ix-|2?1NAm8g#`036!`tFp#w7ggbiy2mEkaR0NY_)ZSzYi)Xx zpUtq^mBdiZ9jI*60zB}es+0&2(pKbSG$Ey!8)GH#cB}4lg%!m<8vqd;{VwI+G0A zYH?74x$E5o(keIF+mh0sXOJTdZeQt!D@p9=ZaGD@%OpIY`$1xhF|vV_`gA*krj+K} zAu0}t$MTxZabeJQ%ar6>C!}fulntmK6^@o2Z?a0QILHB(SLaWvr?uH_=NgJL%79%9 z%(%!dcUo;+{9>J;ABxZynHr1$WH_P@vgs@%1?_O{kO>dQ-et&_5HN}99WPRf&WtSE zwA=g%ml4gUWxGJ3Ig}(6Je*o4>Q#E=jfrlle!^xwoU=su-7+dfaFAAH_Sz*GPucdIy-kOHWE4$m|A&ZJ7lI%pi^_+t>llj zYjZHt4#uSjt>PFus3pi=0tQ9neWZ30@u)J0PshH0eawe6s7{BT%P^0=APvY0h(k=&#N|xR47g$N z$cc1=aIL*j%i$R6Gb!A%)x)*gEc`3$!qx-&DK4KEM;?X;E&V*kbZak(m3Q6gh0zQ8rkPSNR zM!1Qlw8ob33QFy0bA}s9+qJj3q{^h>v=qIiY11QbL^vSslKS&ZeUjokk~(j4Z5cfW z1fC>CqtnAj>Q@;9EE5j1Cee8F0YslV8Ue$A%aN-cA3f=uq{Mz={P!748;s!AnkUIL@tD{@!X;CeM7*v05xQ^^b-P_t3jFl!4L!Y=?%v zs}a2J2zXjVY3G9CmtPi!;B)PB2p* zE&rSMtn4u8ULKb3wIPOK^V(sb6nAip89)&jK?j~>xiI<8(S`}HxgM7@Iiw_=ekUH1 zqhl&JFsUm!ZsZZBQ8RVxD#n5I(}NXwI1?|iQ{&uky9T*0C^^uf2N|C6Sqcyk1b9^y z>e7Tfq$|zy|KtOLIgh1;K(9;=ou7|u*$SvZo;6$0|Eq|Gjn6hv>Px^?(Z zFAUy+0Y~c&+{2}>$BK>)%0=q3lM$@ULso#BxV0wmQ9ED|KR1P&L-?1r^u^_q{l*0l zM&`lWbOx3=Cce8V=mX?_gJ_@m={tPl^_#9-!2%E-UuE(`(YM4HhHIt^qg!mja;sP0|?tpgE1MDqjoqoDg$x~cQo?j zP=V;2Z!RCzQSi5EIg!uj7+%!8cJ|heF4(0EEnsKZ=eT`X#}(9@MBWa|c^v8u6x|o0 z3I)%L?$inbl!gigW9vl9b}d|c?|{>^uVmx(<9fZ;sCH6>UIfW)-KoK|0DH;(n*fb^ z{GSrfUP3{KqpqG#Kp!4VBk71miCU!h^9isVl-snGc3A4-T<7vomU=)X^ve1L!Urp% zw2Jqbz)eLLK7zd_^k&lx!CYgo8`ka0(PbQa#BjfL2lo|#{lMq|CIJ&Z;W}>n+iBN| z{w)386w&2#!da*Rb>BcZ-Kzq4Jh)i4j`9gc3V=Z=rh_TmH6zoFI|bp(OBVxIyZ}}B zTW3WlRg>Y`4QMr5g5J`MGL~Z~u32BNb-J>OoPy_eK$)!!q04;=kH2k$X^S1h zKP-k83-iZ97sDMG`3!@{#r@kg-ub3&b_}c74Z#HK#vmIGQwRdHoaf_jVU&;;syqBL z2HEDpS{ZP!C<7m>0-V`uO(zXY@iCmgT-PxX2l}Z z2N>J2uW^F%~*IILBdy=~WxnL4mU& zRA^Ubw4R2Ny&dkysGWsgUBJsr+)OEGv+CB(w13#1ypCaHw{+S@Zc1#O=D+l9gNEoO8EEC+|KIx z0{$tSE*>wQDonPvcX4;pX16drd8}~!+__VuC&tImGDWe~hE>zz^~uqtdaLlzk%vk< zU0kKqIbT>R7G`Vh9o+6y-|AF0cSj0y#lqDh6uE_&`o>O;*uGWx+QJjX!eoukW{~;% z(%kvNojZ4k_E<9F5c(oV@VVod+k9ziFX^Cj;a7d<=;` z{rZXIvk0#mnMl48;Xgxo5aCS->j>fBaw9p~Ie)ZzzK|qqtJMM&8#}DD@ZiyEVfbk0 zSkg?kYUpP*aZQ5}!4NL0DKL4b)&NK|*<-!gyRCwxmG7`T;1y97Tw{YIwUaK6prVkCguW*d zj6T33xvQnS4)=L}41N6SnTg~#@qGUBMDow^{72Ih$sgf)`LT)Q-{Se#5&x%n4wNR6 zfqy-jychBR37)?^g|&g_Gf4l}@cbtH9>(+O$%$kX&u6bpBq#Cw{)-dIIXwR<@?XaD zD^T7no+UgN@%$|6e*(`bd@tkqf1vyZo=;qwNOtgi-PMWY8}K}fa=#JJD$@ULJbxAC zyT2hf21A9d9r!IXOtot2c>*4U2n_TP)>)^*^a?`&tbYwK>|XZ3t)USD_?H)`^k48Zf|A%F(W(1+rLH zy9F)tXf+u!2pGaAS(Vn5FgN`NTwW z6@gENH)X;ien0T_6G;={D8ey>3kYq5cOrZX!jB;Q-5V3hhY;M8@BdFG{49R|l7(-) zHIcj<;d>B1fbi1@zl88t5k86VI|#{}fCmx&ON0{$zj+h*6XDPCo)55gCgSZCvz#S>25Z4nFK`E4O19gR5H#!BaEiCB<6!;WEIg zr&3$v^6CFK@h6kXFK&Xq;`zqaiDVwnSL65Nc&@Bre(+qXP9z(6{=}QXQ{nk5cs_^c zAJ-<5{~FIS@_jp=_is!j@5A#Ck^Xr+KaF%B!ZTS19O3!yI>y-kHuLfFy`aVQMss;5 zxda3ft^mcfe%Zx*Q=5UX{)Gq`9-*5pGNV zkgbcd z`OYvEyt}x(=Ni)e{XS{`1>zZ&+kiDZ8Qy}hiQg>i+wlD72#)fRxx_6H ztQs=%W8I15{Xr<>{j~^_L7e;k9O7igFzPaKoYZ5<#T%2ilF5W4NAyk-L>U4wxe9Ma zTid|6kY(uVj~M$-i$p|Lb#4NDr0GE>J7V#_N)W5Q0MOF327FJyB)CiuDzz=hFenUt ztydtX5N2p60Uccp^F@IGCUrX-n}sdx4+8xN6HGAB7jQ^bfVJ@Ofp7w00<3N`nFJ*Y zg%-pMJ7ctxT$5+bF#wK^1#K320KzOTW5&&&;IwsC#wJHuMPZHV5kMD#3x6N|I}^zd zBD@b_3E}S{{1n0t!VBL%k$e#0yAeKy@DYS>MfgF4&wl4b@@4-m(tOuM@@a(kzGp(p z1JSKKO%Xu(`+6&BQ4SOCn}>@Gfos~Fe?R{JO(y>sa>(!Dx%rPKl0U$6eW;ZD8$7@J zM<$X##`D2{GLif{JPWTaC7;3b{$H3#?)%fp z{sHvs>+$?GJl&s3zF3$%-2QMP;|n&HGM)SrKqgpeET=Gqi>)P{A~@$wjYlCT6aa`LO6!I&U?41s_5TiOXcn=OzptZbok`28uQ`L_uF-qM!K z!ipzYD5^ZbzW_u20KTw-f(M2uSwridhK!x?c&YqTUzdg`%Z|=?%AMc~P^jW;^76n> z3BKc|IqU@B0hYI!aN|muexBKCByjMC=>vHqiMx0)xpXnPa54D?{Hp@>CA*Ka3u8pwEe+yP_0Sj;hR@LX;fA(0)E2%rs|Z)Auj1OM z4)h{0NHxiYmP1J~fSC9d*ODZ>CLIzZ-~w8h<_Y;Xsnjs>_^`=;fTcIUCT#;zwg^oS zpOW=9BN~t!@o#J6_&EQ?pxjNiP!dwC199GgUZ#q`3l{CcO^^hYZStR_^H#{(sCAS7 z#7&c5&wUp7=BFo;ci?&CXTkI1`SG8DoPy_@@cTRPJcZ|b@%&CazZcIneE&f_&*J$b zc)sZ$fR5pL3eO+M^B0lsBY6G+oQ3!{KaQyw46^p zCNy=so^0-5+Br@8{3%mHqb0C$a_gP2CFcv`22ucjt?rN=%0iLQcmh$#o??>x#C=oA zGrtV_gy*mP(uA*V8SlUJ4=0kR{s-_62>hdJ0w+>QtH|6WpG2NNd0`^?6Fe{cs-}^I z_(xB{8jo}oR%xPy#o5q=%2+(l7qFf|W`s6l*>#hf0CV_`6?UUYov|pkpjvWDtpwa^ z8!S4tNu&?LkSiC)rg1H5_ml}-Z@0G6gy)aE2DNbmGASpJ5m;{-F=EcGg@&2I-=%e6 zMYDpmbrumeQTZcl95lGFZqS}s+8b=`Ew78lI^0&xOYL^OwF4mGMiKB%Pd{YXvD=^4 z&P0gltv&1j8CTglJC9E8t@##LE=l=X0yi*|Z-ie8XzYfxtf2zSA)`rM76)`y6xvQG z0+*un?$P@mO&)zTxesWKo62P8A(|Ji3AoK*w&p=#k_VKvaXe-5=40H>c=Umr_&0vP zNszFQF41E?GMT$x5}!2*i>NjlD8fBCI%qFrCE4U6TrMRyk34$hu_F&086|%Y=TbVV z`!25}xo=?Li|}{9{e5xf?@RXid)15ld-dn}FFBeFH^K0L0B;NXaOBIA!CP*XC&`=N zy7K15w>FM{<%N~T+vV@gZ+`RU;733D(NBD0W#uc^*52{jFFHI-#C{bFv;s@w!zho+T zJ;HH>^9Yv_-iUA=;Y|pe2yaDr8^Sjtyc6MF2=7Mt4utn0ydUBF5uQi*QG^d7{1n1Z zBm5k~&m+8m@INB_&j|k;!vBfz`v`xC@W%*$hVVtNno3@S@Kp%z4-7W+UsVdb)%yB6 zEgQfn@EUABft9g}xyyrXdZ<3}AS$7tA}lDxLS}$eCDE?X;Q0dTdhTPO%Xk+4*NNmG z<2nBupvQRb{4W#9Z{zu;kpF%U&t;_hmv}yp_V&?C$FALPW|tozj%HP z(z!pAS3<1Qtl=(VQjMKv9h{Wlackb&rbNM^^kJ*6cTWM;v;PIf;NUYK=KfPf*q}`~ zHN@!bf^IfJId;KxCU^LIm;cl|)lOGi;^c@g!q58=xGz8=$&EA)Zbc+clmY=B!aCT1 zTDx9JlGnWk3-$YmCV!kw7nNHWrfx<=I*Cv79s?F7=Y5-AHi7knEsE?1 zQz@ZGIb3;X7sk7Hb@KYGiM$I7ZSUO3u~#$HhK-vhgFxA> z*}Lrv97~2SVlx_&16-{Y{KzHA`~TTQ@@KybeJq|~82mW)TK{k8`2R1B|4F=u-~amW z1ExO_$8}K-H@XwBcqUm%m5>pFI{WT5sDg;XH4Pci+MO;Ak`SOV&M30rra0kE&*B9g zQaE=mxa9%%esv2h-wv4y8`xo&LnwLSE`+B$kZv$K6l5TUJDV-flcqzngPmL5hDT{z zYpn)YJDDwESQ0<=n4khSkLw$1gCGEme|q(2?u$OxcDXnpVQgQ>&Pai2It9X70&rLjE{iUU33(rse=|u8YJTH6(b`yAh z4EdhLa}nkK4LpD9&mh;}Ig01s!Sk1q&i|=w2dl=qljyh?<#ZRibCF^%4uWlJx=9J- zRB*4^5`@-;?CON=ixdzEW}(&xXD8cV2jR=KF_(-@j$= zpSI^~QN|f7r;4ZRm}@E|TJFMuEnHj0x!nrbNc#l*s`z*4#!z8(9UTK&KDs_cKypc3 zX;un8L0nDzUZh2LLRs2X_@;**e*I&`;TuOsv6n1@d{F>usBi}ePw7~dIL3b!m>Pnn zhEo5S8~!of3UrIWcM!1G!1j@79Ap*?!&EIFOGaN`cyQb&Q&=wGE>kLK$V9`}5mLFH zx$k%CRVcK9n5aE~GYs39r0U>Ua93M{$^~Nnnk5-1H4_b_Cyywb26z=iz!c(p- zPuF&(FxqMislqqf?@VAzq@yA8K+%>es7%!S>U>Ze@Dk)wE@|5Dx@Vni1(Xb{cbgaq zu{bdX1Dpkv3b=yX$|!v0RPqfEmXaYnpD&b>F+2-+p2hR~9w;Rj@q7T!%XoeU&sjXb zeXx`);`!_NeI3vHpl^16+&}~7N7Gxp&=t&0f5BV^Lz_3`C(Py=Gfg6@0|6=upw)3e zPYb+E9K9Y&e6ChI+WTiBc9Q!BlFc=*=g9%Kjrs&6p$$%DmD~f(U=&iV00*y7{!!Tp zE1Oogr8_x2g$5CC{;H{@iqJsVL3lgDHzIs90^`07;k^johu~zra+zltWht7b8p0y2 zN@{P#KdaR${v|rbHa#pX%H?X^w(Rjvl~TF9)7-+pMCbUoE3f#+UOLsP|70 zK8tYwe+pg^;VTjTD#Gg!1`&o4iU_9=&Ld19Oe4GjVHshsKjj}eeO!3hfP$zB$33#S z7mz-xg=xP=wax~HUJL?=hpIKuAbt@74ATMof$@+-WV=p_$TXW`0+L*OENzkMqIEEZ z>p@GfHWVma1Yp)d$+M?Q$q!q2hdsY5cpACE&~6Z)n5kTn{PrWI&k?>*tK9Vu({r17LrR2j1zmD)n2w!@x zl#C)gjqqWFPa>Q>UrOGG@b3`5{?StMKOu}d=_DIqLeI`kPhJw5Fk?~k4~17??U(^gm+G%j_FeJ7YJW5Q%XLB;QmITnt~6* zCr+LioxVDHVs!Zpb8i^EvT}84^cwu5AsPwqz@ushEK9Ar-EEIv61xx_FW|GaK8!Tw z>8s`CtEHvsDTvM3>D)PQW<2DOU|vV;{${9yjwBGN$MF9~r6K`91l`0KeZ7vMmQk%` z6x9?VD_p5{OWoEMH7O-f8@%G60shx|JMcL701i30p8VxJSH>yr=-+G4-=ADPaewlo zLEJFn-jBEs2XQYviZqDp_jmTx{mB%q$eNwCdJ|gck z@Y34V!jSeCByJuYtwwz;nUTHWYq0)Q&*r1cv^pfr9~qc~p4#d}s_wv&XxPH?|NDXE z+)tZZ|1cL@$W<_atQ~0k9*EzYiqk^?HNG z3rX?^Nx9xyOOk(uXGaxZ$(!ygL-$36BFx?HD{qTF3GW+xx5D3oci9d?{B!rQ>bF28 zkRQ8CYGTbnw*!vg?`r=L53wnS9Lv_db*4zmVsN037Dh=mdW!2i7sn zo#c@L-CjL2ARDa7*#X=h03P?;z`7n*LE7_p-|1G8N9EZ`E)8HG7VP?Cd}7om__RSN z@f|f^9$3fDaFauuOyZrUBGUtqn877q8CdsbEN>h@GpMY^-@?Ggmgr{j{`7$E@@@^N z(iU8Xq-pAJN!G&q2GVZxiM`;r;@RBL@ox`cs~lNb?(G5WI94_ZX2~6Smnll_%KP>% z4e- zm&`xN_wBVd?Mje_@jqUZAK`mf_kBO0?_#C?V*|RQ$}~SdpnL5({m?J i$QB%mel5z~>fElhyBaf$->-z1#~6h3B5-iXfBt_F+C`%P literal 0 HcmV?d00001 diff --git a/thirdparty/ode-0.16.5/build/premake4.lua b/thirdparty/ode-0.16.5/build/premake4.lua new file mode 100644 index 0000000..0ca5f03 --- /dev/null +++ b/thirdparty/ode-0.16.5/build/premake4.lua @@ -0,0 +1,590 @@ +---------------------------------------------------------------------- +-- Premake4 configuration script for OpenDE +-- Contributed by Jason Perkins (starkos@industriousone.com) +-- For more information on Premake: http://industriousone.com/premake +---------------------------------------------------------------------- + + ode_version = "0.16.5" + +---------------------------------------------------------------------- +-- Demo list: add/remove demos from here and the rest of the build +-- should just work. +---------------------------------------------------------------------- + + local demos = { + "boxstack", + "buggy", + "cards", + "chain1", + "chain2", + "collision", + "convex", + "crash", + "cylvssphere", + "dball", + "dhinge", + "feedback", + "friction", + "gyroscopic", + "gyro2", + "heightfield", + "hinge", + "I", + "jointPR", + "jointPU", + "joints", + "kinematic", + "motion", + "motor", + "ode", + "piston", + "plane2d", + "rfriction", + "slider", + "space", + "space_stress", + "step", + "transmission" + } + + local trimesh_demos = { + "basket", + "cyl", + "moving_convex", + "moving_trimesh", + "tracks", + "trimesh" + } + + if not _OPTIONS["no-trimesh"] then + demos = table.join(demos, trimesh_demos) + end + + + +---------------------------------------------------------------------- +-- Configuration options +---------------------------------------------------------------------- + + newoption { + trigger = "with-demos", + description = "Builds the demo applications and DrawStuff library" + } + + newoption { + trigger = "with-tests", + description = "Builds the unit test application" + } + + newoption { + trigger = "with-gimpact", + description = "Use GIMPACT for trimesh collisions (experimental)" + } + + newoption { + trigger = "all-collis-libs", + description = "Include sources of all collision libraries into the project" + } + + newoption { + trigger = "with-libccd", + description = "Uses libccd for handling some collision tests absent in ODE." + } + + newoption { + trigger = "no-dif", + description = "Exclude DIF (Dynamics Interchange Format) exports" + } + + newoption { + trigger = "no-trimesh", + description = "Exclude trimesh collision geometry" + } + + newoption { + trigger = "with-ou", + description = "Use TLS for global caches (allows threaded collision checks for separated spaces)" + } + + newoption { + trigger = "no-builtin-threading-impl", + description = "Disable built-in multithreaded threading implementation" + } + + newoption { + trigger = "no-threading-intf", + description = "Disable threading interface support (external implementations cannot be assigned)" + } + + newoption { + trigger = "16bit-indices", + description = "Use 16-bit indices for trimeshes (default is 32-bit)" + } + + newoption { + trigger = "old-trimesh", + description = "Use old OPCODE trimesh-trimesh collider" + } + + newoption { + trigger = "to", + value = "path", + description = "Set the output location for the generated project files" + } + + newoption { + trigger = "only-shared", + description = "Only build shared (DLL) version of the library" + } + + newoption { + trigger = "only-static", + description = "Only build static versions of the library" + } + + newoption { + trigger = "only-single", + description = "Only use single-precision math" + } + + newoption { + trigger = "only-double", + description = "Only use double-precision math" + } + + -- always clean all of the optional components and toolsets + if _ACTION == "clean" then + _OPTIONS["with-demos"] = "" + _OPTIONS["with-tests"] = "" + for action in premake.action.each() do + os.rmdir(action.trigger) + end + os.remove("../ode/src/config.h") + os.remove("../include/ode/version.h") + os.remove("../include/ode/precision.h") + os.remove("../libccd/src/ccd/precision.h") + end + + -- special validation for Xcode + if _ACTION == "xcode3" and (not _OPTIONS["only-static"] and not _OPTIONS["only-shared"]) then + error( + "Xcode does not support different library types in a single project.\n" .. + "Please use one of the flags: --only-static or --only-shared", 0) + end + + -- build the list of configurations, based on the flags. Ends up + -- with configurations like "Debug", "DebugSingle" or "DebugSingleShared" + local configs = { "Debug", "Release" } + + local function addconfigs(...) + local newconfigs = { } + for _, root in ipairs(configs) do + for _, suffix in ipairs(arg) do + table.insert(newconfigs, root .. suffix) + end + end + configs = newconfigs + end + + + if not _OPTIONS["only-single"] and not _OPTIONS["only-double"] then + addconfigs("Single", "Double") + end + + if not _OPTIONS["only-shared"] and not _OPTIONS["only-static"] then + addconfigs("DLL", "Lib") + end + + +---------------------------------------------------------------------- +-- The solution, and solution-wide settings +---------------------------------------------------------------------- + + solution "ode" + + language "C++" + uuid "4DA77C12-15E5-497B-B1BB-5100D5161E15" + location ( _OPTIONS["to"] or _ACTION ) + + includedirs { + "../include", + "../ode/src" + } + + defines { "_MT" } + + -- apply the configuration list built above + configurations (configs) + + configuration { "Debug*" } + defines { "_DEBUG" } + flags { "Symbols" } + + configuration { "Release*" } + defines { "NDEBUG", "dNODEBUG" } + flags { "OptimizeSpeed", "NoFramePointer" } + + configuration { "*Single*" } + defines { "dIDESINGLE", "CCD_IDESINGLE" } + + configuration { "*Double*" } + defines { "dIDEDOUBLE", "CCD_IDEDOUBLE" } + + configuration { "Windows" } + defines { "WIN32" } + + configuration { "MacOSX" } + linkoptions { "-framework Carbon" } + + -- give each configuration a unique output directory + for _, name in ipairs(configurations()) do + configuration { name } + targetdir ( "../lib/" .. name ) + end + + -- disable Visual Studio security warnings + configuration { "vs*" } + defines { "_CRT_SECURE_NO_DEPRECATE", "_SCL_SECURE_NO_WARNINGS" } + + -- enable M_* macros from math.h + configuration { "vs*" } + defines { "_USE_MATH_DEFINES" } + + -- don't remember why we had to do this + configuration { "vs2002 or vs2003", "*Lib" } + flags { "StaticRuntime" } + + + +---------------------------------------------------------------------- +-- The demo projects, automated from list above. These go first so +-- they will be selected as the active project automatically in IDEs +---------------------------------------------------------------------- + + if _OPTIONS["with-demos"] then + for _, name in ipairs(demos) do + + project ( "demo_" .. name ) + + kind "ConsoleApp" + location ( _OPTIONS["to"] or _ACTION ) + files { "../ode/demo/demo_" .. name .. ".*" } + links { "ode", "drawstuff" } + + configuration { "Windows" } + files { "../drawstuff/src/resources.rc" } + links { "user32", "winmm", "gdi32", "opengl32", "glu32" } + + configuration { "MacOSX" } + linkoptions { "-framework Carbon -framework OpenGL -framework AGL" } + + configuration { "not Windows", "not MacOSX" } + links { "GL", "GLU" } + + end + end + + + +---------------------------------------------------------------------- +-- The ODE library project +---------------------------------------------------------------------- + + project "ode" + + -- kind "StaticLib" + location ( _OPTIONS["to"] or _ACTION ) + + includedirs { + "../ode/src/joints", + "../OPCODE", + "../GIMPACT/include", + "../libccd/src/custom", + "../libccd/src" + } + + files { + "../include/ode/*.h", + "../ode/src/joints/*.h", + "../ode/src/joints/*.cpp", + "../ode/src/*.h", + "../ode/src/*.c", + "../ode/src/*.cpp", + } + + excludes { + "../ode/src/collision_std.cpp", + } + + includedirs { "../ou/include" } + files { "../ou/include/**.h", "../ou/src/**.h", "../ou/src/**.cpp" } + defines { "_OU_NAMESPACE=odeou" } + + if _OPTIONS["with-ou"] then + defines { "_OU_FEATURE_SET=_OU_FEATURE_SET_TLS" } + elseif not _OPTIONS["no-threading-intf"] then + defines { "_OU_FEATURE_SET=_OU_FEATURE_SET_ATOMICS" } + else + defines { "_OU_FEATURE_SET=_OU_FEATURE_SET_BASICS" } + end + + if _OPTIONS["with-ou"] or not _OPTIONS["no-threading-intf"] then + if _ACTION == "gmake" then + if os.get() == "windows" then + buildoptions { "-mthreads" } + linkoptions { "-mthreads" } + defines { "HAVE_PTHREAD_ATTR_SETINHERITSCHED=1", "HAVE_PTHREAD_ATTR_SETSTACKLAZY=1" } + else + buildoptions { "-pthread" } + linkoptions { "-pthread" } + end + end + end + + + configuration { "no-dif" } + excludes { "../ode/src/export-dif.cpp" } + + configuration { "no-trimesh" } + excludes { + "../ode/src/collision_trimesh_colliders.h", + "../ode/src/gimpact_contact_export_helper.cpp", + "../ode/src/gimpact_contact_export_helper.h", + "../ode/src/gimpact_gim_contact_accessor.h", + "../ode/src/gimpact_plane_contact_accessor.h", + "../ode/src/collision_trimesh_internal.cpp", + "../ode/src/collision_trimesh_opcode.cpp", + "../ode/src/collision_trimesh_gimpact.cpp", + "../ode/src/collision_trimesh_box.cpp", + "../ode/src/collision_trimesh_ccylinder.cpp", + "../ode/src/collision_cylinder_trimesh.cpp", + "../ode/src/collision_trimesh_ray.cpp", + "../ode/src/collision_trimesh_sphere.cpp", + "../ode/src/collision_trimesh_trimesh.cpp", + "../ode/src/collision_trimesh_trimesh_old.cpp", + "../ode/src/collision_trimesh_plane.cpp", + "../ode/src/collision_convex_trimesh.cpp" + } + + configuration { "not no-trimesh", "with-gimpact or all-collis-libs" } + files { "../GIMPACT/**.h", "../GIMPACT/**.cpp" } + + configuration { "not no-trimesh", "not with-gimpact" } + files { "../OPCODE/**.h", "../OPCODE/**.cpp" } + + configuration { "not no-trimesh", "not all-collis-libs", "with-gimpact" } + excludes { + "../ode/src/collision_trimesh_opcode.cpp" + } + + configuration { "not no-trimesh", "not all-collis-libs", "not with-gimpact" } + excludes { + "../ode/src/gimpact_contact_export_helper.cpp", + "../ode/src/gimpact_contact_export_helper.h", + "../ode/src/gimpact_gim_contact_accessor.h", + "../ode/src/gimpact_plane_contact_accessor.h", + "../ode/src/collision_trimesh_gimpact.cpp" + } + + configuration { "with-libccd" } + files { "../libccd/src/custom/ccdcustom/*.h", "../libccd/src/ccd/*.h", "../libccd/src/*.c" } + defines { "dLIBCCD_ENABLED", "dLIBCCD_INTERNAL", + "dLIBCCD_BOX_CYL", "dLIBCCD_CYL_CYL", "dLIBCCD_CAP_CYL", "dLIBCCD_CONVEX_BOX", + "dLIBCCD_CONVEX_CAP", "dLIBCCD_CONVEX_CYL", "dLIBCCD_CONVEX_SPHERE", "dLIBCCD_CONVEX_CONVEX" } + + configuration { "not with-libccd" } + excludes { "../ode/src/collision_libccd.cpp", "../ode/src/collision_libccd.h" } + + configuration { "windows" } + links { "user32" } + + configuration { "only-static or *Lib" } + kind "StaticLib" + defines "ODE_LIB" + + configuration { "only-shared or *DLL" } + kind "SharedLib" + defines "ODE_DLL" + + configuration { "*DLL" } + defines "_DLL" + + configuration { "Debug" } + targetname "oded" + + configuration { "Release" } + targetname "ode" + + configuration { "DebugSingle*" } + targetname "ode_singled" + + configuration { "ReleaseSingle*" } + targetname "ode_single" + + configuration { "DebugDouble*" } + targetname "ode_doubled" + + configuration { "ReleaseDouble*" } + targetname "ode_double" + + +---------------------------------------------------------------------- +-- Write a custom to build, based on the supplied flags +---------------------------------------------------------------------- + + if _ACTION and _ACTION ~= "clean" then + local infile = io.open("config-default.h", "r") + local text = infile:read("*a") + + if _OPTIONS["no-trimesh"] then + text = string.gsub(text, "#define dTRIMESH_ENABLED 1", "/* #define dTRIMESH_ENABLED 1 */") + text = string.gsub(text, "#define dTRIMESH_OPCODE 1", "/* #define dTRIMESH_OPCODE 1 */") + elseif (_OPTIONS["with-gimpact"]) then + text = string.gsub(text, "#define dTRIMESH_OPCODE 1", "#define dTRIMESH_GIMPACT 1") + end + + text = string.gsub(text, "/%* #define dOU_ENABLED 1 %*/", "#define dOU_ENABLED 1") + if _OPTIONS["with-ou"] or not _OPTIONS["no-threading-intf"] then + text = string.gsub(text, "/%* #define dATOMICS_ENABLED 1 %*/", "#define dATOMICS_ENABLED 1") + end + + if _OPTIONS["with-ou"] then + text = string.gsub(text, "/%* #define dTLS_ENABLED 1 %*/", "#define dTLS_ENABLED 1") + end + + if _OPTIONS["no-threading-intf"] then + text = string.gsub(text, "/%* #define dTHREADING_INTF_DISABLED 1 %*/", "#define dTHREADING_INTF_DISABLED 1") + elseif not _OPTIONS["no-builtin-threading-impl"] then + text = string.gsub(text, "/%* #define dBUILTIN_THREADING_IMPL_ENABLED 1 %*/", "#define dBUILTIN_THREADING_IMPL_ENABLED 1") + end + + if _OPTIONS["16bit-indices"] then + text = string.gsub(text, "#define dTRIMESH_16BIT_INDICES 0", "#define dTRIMESH_16BIT_INDICES 1") + end + + if _OPTIONS["old-trimesh"] then + text = string.gsub(text, "#define dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER 0", "#define dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER 1") + end + + local outfile = io.open("../ode/src/config.h", "w") + outfile:write(text) + outfile:close() + end + +---------------------------- +-- Write precision headers +---------------------------- + if _ACTION and _ACTION ~= "clean" then + function generateheader(headerfile, placeholder, precstr) + local outfile = io.open(headerfile, "w") + for i in io.lines(headerfile .. ".in") + do + local j,_ = string.gsub(i, placeholder, precstr) + --print("writing " .. j .. " into " .. headerfile) + outfile:write(j .. "\n") + end + outfile:close() + end + + function generate(precstr) + generateheader("../include/ode/precision.h", "@ODE_PRECISION@", "d" .. precstr) + generateheader("../libccd/src/ccd/precision.h", "@CCD_PRECISION@", "CCD_" .. precstr) + end + + if _OPTIONS["only-single"] then + generate("SINGLE") + elseif _OPTIONS["only-double"] then + generate("DOUBLE") + else + generate("UNDEFINEDPRECISION") + end + + generateheader("../include/ode/version.h", "@ODE_VERSION@", ode_version) + + end + + +---------------------------------------------------------------------- +-- The DrawStuff library project +---------------------------------------------------------------------- + + if _OPTIONS["with-demos"] then + + project "drawstuff" + + location ( _OPTIONS["to"] or _ACTION ) + + files { + "../include/drawstuff/*.h", + "../drawstuff/src/internal.h", + "../drawstuff/src/drawstuff.cpp" + } + + configuration { "Debug*" } + targetname "drawstuffd" + + configuration { "only-static or *Lib" } + kind "StaticLib" + defines { "DS_LIB" } + + configuration { "only-shared or *DLL" } + kind "SharedLib" + defines { "DS_DLL", "USRDLL" } + + configuration { "Windows" } + files { "../drawstuff/src/resource.h", "../drawstuff/src/resources.rc", "../drawstuff/src/windows.cpp" } + links { "user32", "opengl32", "glu32", "winmm", "gdi32" } + + configuration { "MacOSX" } + defines { "HAVE_APPLE_OPENGL_FRAMEWORK" } + files { "../drawstuff/src/osx.cpp" } + linkoptions { "-framework Carbon -framework OpenGL -framework AGL" } + + configuration { "not Windows", "not MacOSX" } + files { "../drawstuff/src/x11.cpp" } + links { "X11", "GL", "GLU" } + + end + + +---------------------------------------------------------------------- +-- The automated test application +---------------------------------------------------------------------- + + + if _OPTIONS["with-tests"] then + + project "tests" + + kind "ConsoleApp" + location ( _OPTIONS["to"] or _ACTION ) + + includedirs { + "../tests/UnitTest++/src" + } + + files { + "../tests/*.cpp", + "../tests/joints/*.cpp", + "../tests/UnitTest++/src/*" + } + + links { "ode" } + + configuration { "Windows" } + files { "../tests/UnitTest++/src/Win32/*" } + + configuration { "not Windows" } + files { "../tests/UnitTest++/src/Posix/*" } + + -- add post-build step to automatically run test executable + local path_to_lib = path.getrelative(location(), "../lib") + local command = path.translate(path.join(path_to_lib, "%s/tests")) + + for _, name in ipairs(configurations()) do + configuration { name } + postbuildcommands { command:format(name) } + end + + end + diff --git a/thirdparty/ode-0.16.5/cmake/cmake_uninstall.cmake.in b/thirdparty/ode-0.16.5/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..6aff17a --- /dev/null +++ b/thirdparty/ode-0.16.5/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,17 @@ +if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif() + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" FILES) +string(REGEX REPLACE "\n" ";" FILES "${FILES}") + +foreach(FILE ${FILES}) + message(STATUS "Uninstalling: ${FILE}") + if(EXISTS "${FILE}") + file(REMOVE ${FILE}) + elseif(IS_SYMLINK "${FILE}") + file(REMOVE ${FILE}) + else() + message(STATUS "File \"${FILE}\" does not exist.") + endif() +endforeach() diff --git a/thirdparty/ode-0.16.5/compile b/thirdparty/ode-0.16.5/compile new file mode 100644 index 0000000..a85b723 --- /dev/null +++ b/thirdparty/ode-0.16.5/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/thirdparty/ode-0.16.5/config.guess b/thirdparty/ode-0.16.5/config.guess new file mode 100644 index 0000000..1659250 --- /dev/null +++ b/thirdparty/ode-0.16.5/config.guess @@ -0,0 +1,1441 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2015 Free Software Foundation, Inc. + +timestamp='2015-08-20' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2015 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH="i386" + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH="x86_64" + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC="gnulibc1" ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configury will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; +esac + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/thirdparty/ode-0.16.5/config.h.cmake.in b/thirdparty/ode-0.16.5/config.h.cmake.in new file mode 100644 index 0000000..6016f67 --- /dev/null +++ b/thirdparty/ode-0.16.5/config.h.cmake.in @@ -0,0 +1,109 @@ +#ifndef ODE_CONFIG_H +#define ODE_CONFIG_H + +/* Define to 1 if you have and it should be used (not on Ultrix). */ +#cmakedefine HAVE_ALLOCA_H 1 + +/* Use the Apple OpenGL framework. */ +#cmakedefine HAVE_APPLE_OPENGL_FRAMEWORK 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#cmakedefine HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `isnan' function. */ +#cmakedefine HAVE_ISNAN 1 + +/* Define to 1 if you have the `isnanf' function. */ +#cmakedefine HAVE_ISNANF 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `pthread_attr_setstacklazy' function. */ +#cmakedefine HAVE_PTHREAD_ATTR_SETSTACKLAZY 1 + +/* Define to 1 if you have the `pthread_condattr_setclock' function. */ +#cmakedefine HAVE_PTHREAD_CONDATTR_SETCLOCK 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `_isnan' function. */ +#cmakedefine HAVE__ISNAN 1 + +/* Define to 1 if you have the `_isnanf' function. */ +#cmakedefine HAVE__ISNANF 1 + +/* Define to 1 if you have the `__isnan' function. */ +#cmakedefine HAVE___ISNAN 1 + +/* Define to 1 if you have the `__isnanf' function. */ +#cmakedefine HAVE___ISNANF 1 + +/* compiling for a pentium on a gcc-based platform? */ +#cmakedefine PENTIUM 1 + +/* compiling for a X86_64 system on a gcc-based platform? */ +#cmakedefine X86_64_SYSTEM 1 + +/* Try to identify the platform */ +#if defined(_XENON) +#define ODE_PLATFORM_XBOX360 +#elif defined(SN_TARGET_PSP_HW) +#define ODE_PLATFORM_PSP +#elif defined(SN_TARGET_PS3) +#define ODE_PLATFORM_PS3 +#elif defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) +#define ODE_PLATFORM_WINDOWS +#elif defined(__linux__) +#define ODE_PLATFORM_LINUX +#elif defined(__APPLE__) && defined(__MACH__) +#define ODE_PLATFORM_OSX +#else +#error "Need some help identifying the platform!" +#endif + +/* Additional platform defines used in the code */ +#if defined(ODE_PLATFORM_WINDOWS) && !defined(WIN32) +#define WIN32 +#endif + +#if defined(__CYGWIN__) || defined(__MINGW32__) +#define CYGWIN +#endif + +#if defined(ODE_PLATFORM_OSX) +#define macintosh +#endif + +#ifdef HAVE_ALLOCA_H +#include +#endif + +#ifdef HAVE_MALLOC_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#endif + +#ifdef HAVE_INTTYPES_H +#include +#endif + +#include "typedefs.h" + +#endif // ODE_CONFIG_H diff --git a/thirdparty/ode-0.16.5/config.sub b/thirdparty/ode-0.16.5/config.sub new file mode 100644 index 0000000..1acc966 --- /dev/null +++ b/thirdparty/ode-0.16.5/config.sub @@ -0,0 +1,1813 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2015 Free Software Foundation, Inc. + +timestamp='2015-08-20' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2015 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/thirdparty/ode-0.16.5/configure b/thirdparty/ode-0.16.5/configure new file mode 100644 index 0000000..bead4ad --- /dev/null +++ b/thirdparty/ode-0.16.5/configure @@ -0,0 +1,21594 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for ODE 0.16.5. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and ode@ode.org about +$0: your system, including any error possibly output before +$0: this message. Then install a modern shell, or manually +$0: run the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='ODE' +PACKAGE_TARNAME='ode' +PACKAGE_VERSION='0.16.5' +PACKAGE_STRING='ODE 0.16.5' +PACKAGE_BUGREPORT='ode@ode.org' +PACKAGE_URL='' + +ac_unique_file="ode/src/ode.cpp" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +enable_option_checking=no +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBCCD_CONVEX_CONVEX_FALSE +LIBCCD_CONVEX_CONVEX_TRUE +LIBCCD_CONVEX_SPHERE_FALSE +LIBCCD_CONVEX_SPHERE_TRUE +LIBCCD_CONVEX_CYL_FALSE +LIBCCD_CONVEX_CYL_TRUE +LIBCCD_CONVEX_CAP_FALSE +LIBCCD_CONVEX_CAP_TRUE +LIBCCD_CONVEX_BOX_FALSE +LIBCCD_CONVEX_BOX_TRUE +LIBCCD_CAP_CYL_FALSE +LIBCCD_CAP_CYL_TRUE +LIBCCD_CYL_CYL_FALSE +LIBCCD_CYL_CYL_TRUE +LIBCCD_BOX_CYL_FALSE +LIBCCD_BOX_CYL_TRUE +LIBCCD_INTERNAL_FALSE +LIBCCD_INTERNAL_TRUE +LIBCCD_FALSE +LIBCCD_TRUE +CCD_LIBS +CCD_CFLAGS +ENABLE_OU_FALSE +ENABLE_OU_TRUE +subdirs +ALLOCA +LIBOBJS +ENABLE_DEMOS_FALSE +ENABLE_DEMOS_TRUE +LIBSTDCXX +ENABLE_DRAWSTUFF_FALSE +ENABLE_DRAWSTUFF_TRUE +OSX_FALSE +OSX_TRUE +X11_FALSE +X11_TRUE +WIN32_FALSE +WIN32_TRUE +GL_LIBS +X11_LIBS +X11_CFLAGS +EXTRA_LIBTOOL_LDFLAGS +ODE_PRECISION +TRIMESH_FALSE +TRIMESH_TRUE +GIMPACT_FALSE +GIMPACT_TRUE +OPCODE_FALSE +OPCODE_TRUE +X86_64_SYSTEM_FALSE +X86_64_SYSTEM_TRUE +HAVE_DOXYGEN_FALSE +HAVE_DOXYGEN_TRUE +DOXYGEN +PKG_CONFIG_LIBDIR +PKG_CONFIG_PATH +PKG_CONFIG +ac_ct_WINDRES +WINDRES +CXXCPP +LT_SYS_LIBRARY_PATH +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +LIBTOOL +OBJDUMP +DLLTOOL +AS +LN_S +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +ac_ct_CC +CFLAGS +CC +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CXX +CPPFLAGS +LDFLAGS +CXXFLAGS +CXX +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +ODE_VERSION_INFO +ODE_VERSION +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_version_info +enable_silent_rules +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_aix_soname +with_gnu_ld +with_sysroot +enable_libtool_lock +with_trimesh +enable_double_precision +with_drawstuff +enable_demos +enable_old_trimesh +enable_gprof +enable_threading_intf +enable_ou +enable_builtin_threading_impl +enable_libccd +with_cylinder_cylinder +with_box_cylinder +with_capsule_cylinder +with_convex_box +with_convex_capsule +with_convex_cylinder +with_convex_sphere +with_convex_convex +with_libccd +enable_asserts +' + ac_precious_vars='build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +CPP +LT_SYS_LIBRARY_PATH +CXXCPP +PKG_CONFIG +PKG_CONFIG_PATH +PKG_CONFIG_LIBDIR +DOXYGEN +X11_CFLAGS +X11_LIBS +CCD_CFLAGS +CCD_LIBS' +ac_subdirs_all='ou +libccd' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures ODE 0.16.5 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/ode] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of ODE 0.16.5:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-version-info don't encode version information in the generated + library + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=no] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-double-precision + Configure ODE to work with double precision, if not + specified, single precision is used [default=no] + --disable-demos don't build demos + --enable-old-trimesh enable use of the old trimesh collider + --enable-gprof enable profiling with gprof + --disable-threading-intf + disable threading interface support (external + implementations cannot be assigned) + --enable-ou use TLS for global caches (allows threaded collision + checks for isolated spaces) + --disable-builtin-threading-impl + disable built-in multithreaded threading + implementation + --enable-libccd enable all libccd colliders + --disable-asserts disables debug error checking + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-aix-soname=aix|svr4|both + shared library versioning (aka "SONAME") variant to + provide on AIX, [default=aix]. + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot[=DIR] Search for dependent libraries within DIR (or the + compiler's sysroot if not specified). + --with-trimesh=[opcode|gimpact|none] + use the specified system for trimesh support + [default=opcode] + --with-drawstuff=X11|Win32|OSX|none + force a particular drawstuff implementation or + disable it.[default=autodetect] + --with-cylinder-cylinder=[none,libccd] + use specific collider for cylinder-cylinder + --with-box-cylinder=[default,libccd] + use specific collider for box-cylinder + --with-capsule-cylinder=[none,libccd] + use specific collider for capsule-cylinder + --with-convex-box=[none,libccd] + use specific collider for convex-box + --with-convex-capsule=[none,libccd] + use specific collider for convex-capsule + --with-convex-cylinder=[none,libccd] + use specific collider for convex-cylinder + --with-convex-sphere=[default,libccd] + use specific collider for convex-sphere + --with-convex-convex=[default,libccd] + use specific collider for convex-convex + --with-libccd=[internal|system] + use the specified libccd [default=system] + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + CPP C preprocessor + LT_SYS_LIBRARY_PATH + User-defined run-time library search path. + CXXCPP C++ preprocessor + PKG_CONFIG path to pkg-config utility + PKG_CONFIG_PATH + directories to add to pkg-config's search path + PKG_CONFIG_LIBDIR + path overriding pkg-config's built-in search path + DOXYGEN set to doxygen binary to generate doxygen docs + X11_CFLAGS C compiler flags for X11, overriding pkg-config + X11_LIBS linker flags for X11, overriding pkg-config + CCD_CFLAGS C compiler flags for CCD, overriding pkg-config + CCD_LIBS linker flags for CCD, overriding pkg-config + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +ODE configure 0.16.5 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## -------------------------- ## +## Report this to ode@ode.org ## +## -------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_find_intX_t LINENO BITS VAR +# ----------------------------------- +# Finds a signed integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_intX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 +$as_echo_n "checking for int$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in int$2_t 'int' 'long int' \ + 'long long int' 'short int' 'signed char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + enum { N = $2 / 2 - 1 }; +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) + < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + case $ac_type in #( + int$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_intX_t + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +$as_echo_n "checking for uint$2_t... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no"; then : + +else + break +fi + done +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by ODE $as_me 0.16.5, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ODE_VERSION=0.16.5 + + +# Those are instructions from the Libtool manual: +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. Update the version information only immediately before a public +# release of your software. More frequent updates are unnecessary, +# and only guarantee that the current interface number gets larger +# faster. +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:r+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed since the last public release, +# then set AGE to 0. +CURRENT=9 +REVISION=1 +AGE=1 + +# Check whether --enable-version-info was given. +if test "${enable_version_info+set}" = set; then : + enableval=$enable_version_info; version_info=$enableval +else + version_info=yes +fi + +if test x$version_info = xyes +then + ODE_VERSION_INFO="-version-info $CURRENT:$REVISION:$AGE" +else + ODE_VERSION_INFO="-avoid-version" +fi + + + + + + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +am__api_version='1.15' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='ode' + VERSION='0.16.5' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +ac_config_headers="$ac_config_headers ode/src/config.h" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C++ compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.6' +macro_revision='2.4.6' + + + + + + + + + + + + + +ltmain=$ac_aux_dir/ltmain.sh + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case $ECHO in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n "$lt_cv_sys_max_cmd_len"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test yes != "$GCC"; then + reload_cmds=false + fi + ;; + darwin*) + if test yes = "$GCC"; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 +$as_echo "$with_sysroot" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 +$as_echo_n "checking for a working dd... " >&6; } +if ${ac_cv_path_lt_DD+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +if test -z "$lt_DD"; then + ac_path_lt_DD_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in dd; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_lt_DD" || continue +if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi + $ac_path_lt_DD_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_lt_DD"; then + : + fi +else + ac_cv_path_lt_DD=$lt_DD +fi + +rm -f conftest.i conftest2.i conftest.out +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 +$as_echo "$ac_cv_path_lt_DD" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 +$as_echo_n "checking how to truncate binary pipes... " >&6; } +if ${lt_cv_truncate_bin+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 +$as_echo "$lt_cv_truncate_bin" >&6; } + + + + + + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[012][,.]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + +func_stripname_cnf () +{ + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; + esac +} # func_stripname_cnf + + + + + +# Set options +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_shared=no +fi + + + + + + + +enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. +set dummy ${ac_tool_prefix}as; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AS+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AS"; then + ac_cv_prog_AS="$AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AS="${ac_tool_prefix}as" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AS=$ac_cv_prog_AS +if test -n "$AS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AS" >&5 +$as_echo "$AS" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AS"; then + ac_ct_AS=$AS + # Extract the first word of "as", so it can be a program name with args. +set dummy as; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AS+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AS"; then + ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AS="as" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AS=$ac_cv_prog_ac_ct_AS +if test -n "$ac_ct_AS"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5 +$as_echo "$ac_ct_AS" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_AS" = x; then + AS="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AS=$ac_ct_AS + fi +else + AS="$ac_cv_prog_AS" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + + ;; +esac + +test -z "$AS" && AS=as + + + + + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + enable_dlopen=no + + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + pic_mode=default +fi + + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[5-9]*,yes) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 +$as_echo_n "checking which variant of shared library versioning to provide... " >&6; } + +# Check whether --with-aix-soname was given. +if test "${with_aix_soname+set}" = set; then : + withval=$with_aix_soname; case $withval in + aix|svr4|both) + ;; + *) + as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname +else + if ${lt_cv_with_aix_soname+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_with_aix_soname=aix +fi + + with_aix_soname=$lt_cv_with_aix_soname +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 +$as_echo "$with_aix_soname" >&6; } + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/${ac_tool_prefix}file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC=$CC +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test yes = "$GCC"; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + lt_prog_compiler_pic='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works"; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works"; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + export_dynamic_flag_spec='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='$wl--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + export_dynamic_flag_spec='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test no = "$ld_shlibs"; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct=no + hardcode_direct_absolute=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' $wl-bernotok' + allow_undefined_flag=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test yes = "$GCC"; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test yes = "$lt_cv_prog_compiler__b"; then + archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test yes = "$lt_cv_irix_exported_symbol"; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + link_all_deplibs=no + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + ld_shlibs=yes + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + else + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + osf3*) + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='$wl-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='$wl-z,text' + allow_undefined_flag='$wl-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='$wl-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test no = "$ld_shlibs" && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([A-Za-z]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test yes = "$hardcode_automatic"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && + test no != "$hardcode_minus_L"; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test relink = "$hardcode_action" || + test yes = "$inherit_rpath"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen=shl_load +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen=dlopen +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report what library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC=$lt_save_CC + + if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct_CXX=no + hardcode_direct_absolute_CXX=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec_CXX='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + no_undefined_flag_CXX='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' $wl-bernotok' + allow_undefined_flag_CXX=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='$wl--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + if test yes != "$lt_cv_apple_cc_single_mod"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + os2*) + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_minus_L_CXX=yes + allow_undefined_flag_CXX=unsupported + shrext_cmds=.dll + archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes_CXX=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='$wl-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='$wl-E' + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + no_undefined_flag_CXX=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='$wl-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='$wl-z,text' + allow_undefined_flag_CXX='$wl-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test no = "$ld_shlibs_CXX" && can_build_shared=no + + GCC_CXX=$GXX + LD_CXX=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX=$prev$p + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX=$prev$p + else + postdeps_CXX="${postdeps_CXX} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX=$p + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX=$p + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + lt_prog_compiler_pic_CXX='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static_CXX='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test no = "$ld_shlibs_CXX" && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec_CXX='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test yes = "$hardcode_automatic_CXX"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct_CXX" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && + test no != "$hardcode_minus_L_CXX"; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test relink = "$hardcode_action_CXX" || + test yes = "$inherit_rpath_CXX"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + +if test -n "$ac_tool_prefix"; then + for ac_prog in windres + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$WINDRES"; then + ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_WINDRES="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +WINDRES=$ac_cv_prog_WINDRES +if test -n "$WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $WINDRES" >&5 +$as_echo "$WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$WINDRES" && break + done +fi +if test -z "$WINDRES"; then + ac_ct_WINDRES=$WINDRES + for ac_prog in windres +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_WINDRES+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_WINDRES"; then + ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_WINDRES="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES +if test -n "$ac_ct_WINDRES"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_WINDRES" >&5 +$as_echo "$ac_ct_WINDRES" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_WINDRES" && break +done + + if test "x$ac_ct_WINDRES" = x; then + WINDRES="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + WINDRES=$ac_ct_WINDRES + fi +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 +$as_echo_n "checking for working volatile... " >&6; } +if ${ac_cv_c_volatile+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + +volatile int x; +int * volatile y = (int *) 0; +return !x && !y; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_volatile=yes +else + ac_cv_c_volatile=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_volatile" >&5 +$as_echo "$ac_cv_c_volatile" >&6; } +if test $ac_cv_c_volatile = no; then + +$as_echo "#define volatile /**/" >>confdefs.h + +fi + + + + + + + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. +set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG +if test -n "$PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 +$as_echo "$PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_path_PKG_CONFIG"; then + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG +if test -n "$ac_pt_PKG_CONFIG"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 +$as_echo "$ac_pt_PKG_CONFIG" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_pt_PKG_CONFIG" = x; then + PKG_CONFIG="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + PKG_CONFIG=$ac_pt_PKG_CONFIG + fi +else + PKG_CONFIG="$ac_cv_path_PKG_CONFIG" +fi + +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 +$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + PKG_CONFIG="" + fi +fi + + +for ac_prog in doxygen +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DOXYGEN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DOXYGEN"; then + ac_cv_prog_DOXYGEN="$DOXYGEN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DOXYGEN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DOXYGEN=$ac_cv_prog_DOXYGEN +if test -n "$DOXYGEN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DOXYGEN" >&5 +$as_echo "$DOXYGEN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DOXYGEN" && break +done + + if test x$DOXYGEN = xdoxygen; then + HAVE_DOXYGEN_TRUE= + HAVE_DOXYGEN_FALSE='#' +else + HAVE_DOXYGEN_TRUE='#' + HAVE_DOXYGEN_FALSE= +fi + + + +pentium=no +cpu64=no +case "$host_cpu" in + i586 | i686 | i786 ) + pentium=yes + +$as_echo "#define PENTIUM 1" >>confdefs.h + + ;; + x86_64* ) + pentium=yes + cpu64=yes + +$as_echo "#define X86_64_SYSTEM 1" >>confdefs.h + + ;; +esac + + if test x$cpu64 = xyes; then + X86_64_SYSTEM_TRUE= + X86_64_SYSTEM_FALSE='#' +else + X86_64_SYSTEM_TRUE='#' + X86_64_SYSTEM_FALSE= +fi + + + + + + + +for ac_header in alloca.h stdio.h inttypes.h stdint.h stdlib.h math.h \ + string.h stdarg.h malloc.h float.h time.h sys/time.h \ + limits.h stddef.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +opcode=no +gimpact=no + +# Check whether --with-trimesh was given. +if test "${with_trimesh+set}" = set; then : + withval=$with_trimesh; trimesh=$withval +else + trimesh=opcode + +fi + +if test "$trimesh" = opcode +then + opcode=yes +fi +if test "$trimesh" = gimpact +then + gimpact=yes +fi + + if test $opcode = yes; then + OPCODE_TRUE= + OPCODE_FALSE='#' +else + OPCODE_TRUE='#' + OPCODE_FALSE= +fi + + if test $gimpact = yes; then + GIMPACT_TRUE= + GIMPACT_FALSE='#' +else + GIMPACT_TRUE='#' + GIMPACT_FALSE= +fi + + if test $opcode = yes -o $gimpact = yes; then + TRIMESH_TRUE= + TRIMESH_FALSE='#' +else + TRIMESH_TRUE='#' + TRIMESH_FALSE= +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if double precision is requested" >&5 +$as_echo_n "checking if double precision is requested... " >&6; } +# Check whether --enable-double-precision was given. +if test "${enable_double_precision+set}" = set; then : + enableval=$enable_double_precision; usedouble=$enableval +else + usedouble=no +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $usedouble" >&5 +$as_echo "$usedouble" >&6; } +if test "$usedouble" = yes; +then + ODE_PRECISION=dDOUBLE +else + ODE_PRECISION=dSINGLE +fi + + + + +# Check whether --with-drawstuff was given. +if test "${with_drawstuff+set}" = set; then : + withval=$with_drawstuff; drawstuff=$withval +else + drawstuff= +fi + + +EXTRA_LIBTOOL_LDFLAGS= +case "$host_os" in + cygwin* | mingw*) + if test "x$drawstuff" = x + then + drawstuff="Win32" # if in a Windows enviroment + fi + EXTRA_LIBTOOL_LDFLAGS="-no-undefined" + ;; + *apple* | *darwin*) # For Mac OS X + if test "x$drawstuff" = x + then + drawstuff="OSX" + fi + CC="$CXX" + LINK="$CXXLINK" + ;; + *) + if test "x$drawstuff" = x + then + drawstuff="X11" # if anything else default to X11 + fi + ;; +esac + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which drawstuff lib to build" >&5 +$as_echo_n "checking which drawstuff lib to build... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $drawstuff" >&5 +$as_echo "$drawstuff" >&6; } + +if test "x$drawstuff" = "xX11" +then + # The built-in macro, X_PATH, causes too many problems, these days everyone uses Xorg, + # so we can ask pkg-config to find it for us. + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for X11" >&5 +$as_echo_n "checking for X11... " >&6; } + +if test -n "$X11_CFLAGS"; then + pkg_cv_X11_CFLAGS="$X11_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"x11\""; } >&5 + ($PKG_CONFIG --exists --print-errors "x11") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_X11_CFLAGS=`$PKG_CONFIG --cflags "x11" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$X11_LIBS"; then + pkg_cv_X11_LIBS="$X11_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"x11\""; } >&5 + ($PKG_CONFIG --exists --print-errors "x11") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_X11_LIBS=`$PKG_CONFIG --libs "x11" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + X11_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "x11" 2>&1` + else + X11_PKG_ERRORS=`$PKG_CONFIG --print-errors "x11" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$X11_PKG_ERRORS" >&5 + + drawstuff="none" +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + drawstuff="none" +else + X11_CFLAGS=$pkg_cv_X11_CFLAGS + X11_LIBS=$pkg_cv_X11_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi +fi + +if test "x$drawstuff" = "xOSX"; then + +$as_echo "#define HAVE_APPLE_OPENGL_FRAMEWORK 1" >>confdefs.h + + GL_LIBS="-framework OpenGL -framework GLUT" +elif test "x$drawstuff" != "xnone"; then + have_gl_headers=yes + for ac_header in GL/gl.h GL/glu.h GL/glext.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef WIN32 + #include + #endif + #if HAVE_GL_GL_H + #include + #endif + #if HAVE_GL_GLU_H + #include + #endif + +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +else + have_gl_headers=no +fi + +done + + have_gl=no + have_glu=no + TEMP_LDFLAGS="$LDFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lGL" >&5 +$as_echo_n "checking for main in -lGL... " >&6; } +if ${ac_cv_lib_GL_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lGL $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_GL_main=yes +else + ac_cv_lib_GL_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_GL_main" >&5 +$as_echo "$ac_cv_lib_GL_main" >&6; } +if test "x$ac_cv_lib_GL_main" = xyes; then : + GL_LIBS="-lGL"; have_gl=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lGLU" >&5 +$as_echo_n "checking for main in -lGLU... " >&6; } +if ${ac_cv_lib_GLU_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lGLU -lGL $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_GLU_main=yes +else + ac_cv_lib_GLU_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_GLU_main" >&5 +$as_echo "$ac_cv_lib_GLU_main" >&6; } +if test "x$ac_cv_lib_GLU_main" = xyes; then : + GL_LIBS="-lGLU $GL_LIBS"; have_glu=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lopengl32" >&5 +$as_echo_n "checking for main in -lopengl32... " >&6; } +if ${ac_cv_lib_opengl32_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lopengl32 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_opengl32_main=yes +else + ac_cv_lib_opengl32_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_opengl32_main" >&5 +$as_echo "$ac_cv_lib_opengl32_main" >&6; } +if test "x$ac_cv_lib_opengl32_main" = xyes; then : + GL_LIBS="-lopengl32"; have_gl=yes +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lglu32" >&5 +$as_echo_n "checking for main in -lglu32... " >&6; } +if ${ac_cv_lib_glu32_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lglu32 -lopengl32 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_glu32_main=yes +else + ac_cv_lib_glu32_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_glu32_main" >&5 +$as_echo "$ac_cv_lib_glu32_main" >&6; } +if test "x$ac_cv_lib_glu32_main" = xyes; then : + GL_LIBS="-lglu32 $GL_LIBS"; have_glu=yes +fi + + LDFLAGS="$TEMP_LDFLAGS" + if test $have_gl = no -o $have_glu = no -o $have_gl_headers = no; then + drawstuff="none" + fi +fi + + + if test x$drawstuff = xWin32; then + WIN32_TRUE= + WIN32_FALSE='#' +else + WIN32_TRUE='#' + WIN32_FALSE= +fi + + if test x$drawstuff = xX11; then + X11_TRUE= + X11_FALSE='#' +else + X11_TRUE='#' + X11_FALSE= +fi + + if test x$drawstuff = xOSX; then + OSX_TRUE= + OSX_FALSE='#' +else + OSX_TRUE='#' + OSX_FALSE= +fi + + if test x$drawstuff != xnone; then + ENABLE_DRAWSTUFF_TRUE= + ENABLE_DRAWSTUFF_FALSE='#' +else + ENABLE_DRAWSTUFF_TRUE='#' + ENABLE_DRAWSTUFF_FALSE= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if demos should be built" >&5 +$as_echo_n "checking if demos should be built... " >&6; } +# Check whether --enable-demos was given. +if test "${enable_demos+set}" = set; then : + enableval=$enable_demos; enable_demos=$enableval +else + enable_demos=yes +fi + +if test x$drawstuff = xnone -a x$enable_demos = xyes ; then + enable_demos=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_demos" >&5 +$as_echo "$enable_demos" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Demos will not be built because OpenGL doesn't seem to work. See \`config.log' for details." >&5 +$as_echo "$as_me: WARNING: Demos will not be built because OpenGL doesn't seem to work. See \`config.log' for details." >&2;} +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_demos" >&5 +$as_echo "$enable_demos" >&6; } +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lstdc++" >&5 +$as_echo_n "checking for main in -lstdc++... " >&6; } +if ${ac_cv_lib_stdcpp_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lstdc++ $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_stdcpp_main=yes +else + ac_cv_lib_stdcpp_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_stdcpp_main" >&5 +$as_echo "$ac_cv_lib_stdcpp_main" >&6; } +if test "x$ac_cv_lib_stdcpp_main" = xyes; then : + LIBSTDCXX="-lstdc++" +else + LIBSTDCXX= +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpthread" >&5 +$as_echo_n "checking for main in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_main=yes +else + ac_cv_lib_pthread_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_main" >&5 +$as_echo "$ac_cv_lib_pthread_main" >&6; } +if test "x$ac_cv_lib_pthread_main" = xyes; then : + LIBS="$LIBS -lpthread" +fi + + + + if test x$enable_demos = xyes; then + ENABLE_DEMOS_TRUE= + ENABLE_DEMOS_FALSE='#' +else + ENABLE_DEMOS_TRUE='#' + ENABLE_DEMOS_FALSE= +fi + + + +old_trimesh=no +# Check whether --enable-old-trimesh was given. +if test "${enable_old_trimesh+set}" = set; then : + enableval=$enable_old_trimesh; old_trimesh=$enableval + +fi + +if test x$old_trimesh = xyes -a $trimesh = opcode; then + +$as_echo "#define dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER 1" >>confdefs.h + +else + old_trimesh=no +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gprof" >&5 +$as_echo_n "checking for gprof... " >&6; } +# Check whether --enable-gprof was given. +if test "${enable_gprof+set}" = set; then : + enableval=$enable_gprof; gprof=$enableval +else + gprof=no +fi + +if test "$gprof" != no +then + CFLAGS="-pg $CFLAGS" + CXXFLAGS="-pg $CXXFLAGS" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 +$as_echo "enabled" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lgmon" >&5 +$as_echo_n "checking for main in -lgmon... " >&6; } +if ${ac_cv_lib_gmon_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lgmon $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_gmon_main=yes +else + ac_cv_lib_gmon_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmon_main" >&5 +$as_echo "$ac_cv_lib_gmon_main" >&6; } +if test "x$ac_cv_lib_gmon_main" = xyes; then : + LIBS="$LIBS -lgmon" +fi + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + +# Checks for typedefs, structures, and compiler characteristics. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 +$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } +if ${ac_cv_header_stdbool_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include + #ifndef bool + "error: bool is not defined" + #endif + #ifndef false + "error: false is not defined" + #endif + #if false + "error: false is not 0" + #endif + #ifndef true + "error: true is not defined" + #endif + #if true != 1 + "error: true is not 1" + #endif + #ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" + #endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + /* See body of main program for 'e'. */ + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + bool e = &s; + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdbool_h=yes +else + ac_cv_header_stdbool_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 +$as_echo "$ac_cv_header_stdbool_h" >&6; } + ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = xyes; then : + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + + +if test $ac_cv_header_stdbool_h = yes; then + +$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 +$as_echo_n "checking for inline... " >&6; } +if ${ac_cv_c_inline+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_inline=$ac_kw +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 +$as_echo "$ac_cv_c_inline" >&6; } + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" +case $ac_cv_c_int32_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<_ACEOF +#define int32_t $ac_cv_c_int32_t +_ACEOF +;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for obstacks" >&5 +$as_echo_n "checking for obstacks... " >&6; } +if ${ac_cv_func_obstack+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default + #include "obstack.h" +int +main () +{ +struct obstack mem; + #define obstack_chunk_alloc malloc + #define obstack_chunk_free free + obstack_init (&mem); + obstack_free (&mem, 0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_func_obstack=yes +else + ac_cv_func_obstack=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_obstack" >&5 +$as_echo "$ac_cv_func_obstack" >&6; } +if test $ac_cv_func_obstack = yes; then + +$as_echo "#define HAVE_OBSTACK 1" >>confdefs.h + +else + case " $LIBOBJS " in + *" obstack.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS obstack.$ac_objext" + ;; +esac + +fi + +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +$as_echo "#define _UINT32_T 1" >>confdefs.h + + +cat >>confdefs.h <<_ACEOF +#define uint32_t $ac_cv_c_uint32_t +_ACEOF +;; + esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 +$as_echo_n "checking for main in -lm... " >&6; } +if ${ac_cv_lib_m_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_m_main=yes +else + ac_cv_lib_m_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5 +$as_echo "$ac_cv_lib_m_main" >&6; } +if test "x$ac_cv_lib_m_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lsunmath" >&5 +$as_echo_n "checking for main in -lsunmath... " >&6; } +if ${ac_cv_lib_sunmath_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsunmath $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_sunmath_main=yes +else + ac_cv_lib_sunmath_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sunmath_main" >&5 +$as_echo "$ac_cv_lib_sunmath_main" >&6; } +if test "x$ac_cv_lib_sunmath_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSUNMATH 1 +_ACEOF + + LIBS="-lsunmath $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lrt" >&5 +$as_echo_n "checking for main in -lrt... " >&6; } +if ${ac_cv_lib_rt_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_main=yes +else + ac_cv_lib_rt_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_main" >&5 +$as_echo "$ac_cv_lib_rt_main" >&6; } +if test "x$ac_cv_lib_rt_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRT 1 +_ACEOF + + LIBS="-lrt $LIBS" + +fi + +for ac_func in atan2f clock_gettime copysign copysignf cosf fabsf floor fmodf gettimeofday isnan _isnan __isnan isnanf _isnanf __isnanf memmove memset pthread_attr_setstacklazy pthread_attr_setinheritsched pthread_condattr_setclock sinf snprintf sqrt sqrtf strchr strstr vsnprintf +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5 +$as_echo_n "checking for working alloca.h... " >&6; } +if ${ac_cv_working_alloca_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + if (p) return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_working_alloca_h=yes +else + ac_cv_working_alloca_h=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_alloca_h" >&5 +$as_echo "$ac_cv_working_alloca_h" >&6; } +if test $ac_cv_working_alloca_h = yes; then + +$as_echo "#define HAVE_ALLOCA_H 1" >>confdefs.h + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5 +$as_echo_n "checking for alloca... " >&6; } +if ${ac_cv_func_alloca_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include +# define alloca _alloca +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +void *alloca (size_t); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + if (p) return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_func_alloca_works=yes +else + ac_cv_func_alloca_works=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_alloca_works" >&5 +$as_echo "$ac_cv_func_alloca_works" >&6; } + +if test $ac_cv_func_alloca_works = yes; then + +$as_echo "#define HAVE_ALLOCA 1" >>confdefs.h + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=\${LIBOBJDIR}alloca.$ac_objext + +$as_echo "#define C_ALLOCA 1" >>confdefs.h + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether \`alloca.c' needs Cray hooks" >&5 +$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; } +if ${ac_cv_os_cray+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#if defined CRAY && ! defined CRAY2 +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then : + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_os_cray" >&5 +$as_echo "$ac_cv_os_cray" >&6; } +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5 +$as_echo_n "checking stack direction for C alloca... " >&6; } +if ${ac_cv_c_stack_direction+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_c_stack_direction=0 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +find_stack_direction (int *addr, int depth) +{ + int dir, dummy = 0; + if (! addr) + addr = &dummy; + *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; + dir = depth ? find_stack_direction (addr, depth - 1) : 0; + return dir + dummy; +} + +int +main (int argc, char **argv) +{ + return find_stack_direction (0, argc + !argv + 20) < 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_stack_direction=1 +else + ac_cv_c_stack_direction=-1 +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_stack_direction" >&5 +$as_echo "$ac_cv_c_stack_direction" >&6; } +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + + +ac_fn_c_check_func "$LINENO" "pthread_condattr_setclock" "ac_cv_func_pthread_condattr_setclock" +if test "x$ac_cv_func_pthread_condattr_setclock" = xyes; then : + +else + ac_cv_func_no_pthread_condattr_setclock=yes +fi + +for ac_func in no_pthread_condattr_setclock +do : + ac_fn_c_check_func "$LINENO" "no_pthread_condattr_setclock" "ac_cv_func_no_pthread_condattr_setclock" +if test "x$ac_cv_func_no_pthread_condattr_setclock" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NO_PTHREAD_CONDATTR_SETCLOCK 1 +_ACEOF + +fi +done + + + +# Check whether --enable-threading-intf was given. +if test "${enable_threading_intf+set}" = set; then : + enableval=$enable_threading_intf; threading_intf=$enableval +else + threading_intf=yes +fi + +# Check whether --enable-ou was given. +if test "${enable_ou+set}" = set; then : + enableval=$enable_ou; use_ou_tls=$enableval +else + use_ou_tls=no +fi + +use_ou="no" +if test x$use_ou_tls = xyes -o x$threading_intf = xyes +then + use_ou="yes" +fi + +OU_NAMESPACE=odeou + + +$as_echo "#define _OU_NAMESPACE odeou" >>confdefs.h + + +$as_echo "#define dOU_ENABLED 1" >>confdefs.h + + +if test x$use_ou_tls = xyes +then + OU_FEATURE_SET=_OU_FEATURE_SET_TLS + +$as_echo "#define _OU_FEATURE_SET _OU_FEATURE_SET_TLS" >>confdefs.h + +elif test x$use_ou = xyes +then + OU_FEATURE_SET=_OU_FEATURE_SET_ATOMICS + +$as_echo "#define _OU_FEATURE_SET _OU_FEATURE_SET_ATOMICS" >>confdefs.h + +else + OU_FEATURE_SET=_OU_FEATURE_SET_BASICS + +$as_echo "#define _OU_FEATURE_SET _OU_FEATURE_SET_BASICS" >>confdefs.h + +fi + + +if test x$use_ou = xyes +then + +$as_echo "#define dATOMICS_ENABLED 1" >>confdefs.h + + if test x$use_ou_tls = xyes + then + +$as_echo "#define dTLS_ENABLED 1" >>confdefs.h + + fi +fi + +case "$host_os" in + cygwin* | mingw*) + targetos=_OU_TARGET_OS_WINDOWS + ;; + *qnx*) + targetos=_OU_TARGET_OS_QNX + ;; + *apple* | *darwin*) + targetos=_OU_TARGET_OS_MAC + ;; + *sunos*) + targetos=_OU_TARGET_OS_SUNOS + ;; + *aix*) + targetos=_OU_TARGET_OS_AIX + ;; + *) + targetos=_OU_TARGET_OS_GENUNIX + ;; +esac + +if test $targetos = _OU_TARGET_OS_MAC +then + MAC_OS_X_VERSION=1000 + ac_fn_c_check_func "$LINENO" "OSAtomicAdd32Barrier" "ac_cv_func_OSAtomicAdd32Barrier" +if test "x$ac_cv_func_OSAtomicAdd32Barrier" = xyes; then : + MAC_OS_X_VERSION=1040 +fi + + ac_fn_c_check_func "$LINENO" "OSAtomicAnd32OrigBarrier" "ac_cv_func_OSAtomicAnd32OrigBarrier" +if test "x$ac_cv_func_OSAtomicAnd32OrigBarrier" = xyes; then : + MAC_OS_X_VERSION=1050 +fi + + +cat >>confdefs.h <<_ACEOF +#define MAC_OS_X_VERSION $MAC_OS_X_VERSION +_ACEOF + +fi + +if test $targetos = _OU_TARGET_OS_SUNOS +then + ac_fn_c_check_func "$LINENO" "atomic_inc_32_nv" "ac_cv_func_atomic_inc_32_nv" +if test "x$ac_cv_func_atomic_inc_32_nv" = xyes; then : + +else + targetos=_OU_TARGET_OS_GENUNIX +fi + +fi + + +cat >>confdefs.h <<_ACEOF +#define _OU_TARGET_OS $targetos +_ACEOF + + + + +subdirs="$subdirs ou" + + if true; then + ENABLE_OU_TRUE= + ENABLE_OU_FALSE='#' +else + ENABLE_OU_TRUE='#' + ENABLE_OU_FALSE= +fi + + +if test x$threading_intf = xyes +then + # Check whether --enable-builtin-threading-impl was given. +if test "${enable_builtin_threading_impl+set}" = set; then : + enableval=$enable_builtin_threading_impl; use_builtin_threading_impl=$enableval +else + use_builtin_threading_impl=yes +fi + + if test x$use_builtin_threading_impl = xyes + then + +$as_echo "#define dBUILTIN_THREADING_IMPL_ENABLED 1" >>confdefs.h + + fi +else + +$as_echo "#define dTHREADING_INTF_DISABLED 1" >>confdefs.h + + use_builtin_threading_impl=no +fi + +col_cylinder_cylinder=none +col_box_cylinder=default +col_capsule_cylinder=none +col_convex_box=none +col_convex_capsule=none +col_convex_cylinder=none +col_convex_sphere=default +col_convex_convex=default + + +use_libccd=no +libccd_all=no +# Check whether --enable-libccd was given. +if test "${enable_libccd+set}" = set; then : + enableval=$enable_libccd; libccd_all=$enableval +fi + +if test x$libccd_all = xyes +then + col_cylinder_cylinder=libccd + col_box_cylinder=libccd + col_capsule_cylinder=libccd + col_convex_box=libccd + col_convex_capsule=libccd + col_convex_cylinder=libccd + col_convex_sphere=libccd + col_convex_convex=libccd + use_libccd=yes +fi + + + +# Check whether --with-cylinder-cylinder was given. +if test "${with_cylinder_cylinder+set}" = set; then : + withval=$with_cylinder_cylinder; col_cylinder_cylinder=$withval +fi + + + +# Check whether --with-box-cylinder was given. +if test "${with_box_cylinder+set}" = set; then : + withval=$with_box_cylinder; col_box_cylinder=$withval +fi + + + +# Check whether --with-capsule-cylinder was given. +if test "${with_capsule_cylinder+set}" = set; then : + withval=$with_capsule_cylinder; col_capsule_cylinder=$withval +fi + + + +# Check whether --with-convex-box was given. +if test "${with_convex_box+set}" = set; then : + withval=$with_convex_box; col_convex_box=$withval +fi + + + +# Check whether --with-convex-capsule was given. +if test "${with_convex_capsule+set}" = set; then : + withval=$with_convex_capsule; col_convex_capsule=$withval +fi + + + +# Check whether --with-convex-cylinder was given. +if test "${with_convex_cylinder+set}" = set; then : + withval=$with_convex_cylinder; col_convex_cylinder=$withval +fi + + + +# Check whether --with-convex-sphere was given. +if test "${with_convex_sphere+set}" = set; then : + withval=$with_convex_sphere; col_convex_sphere=$withval +fi + + + +# Check whether --with-convex-convex was given. +if test "${with_convex_convex+set}" = set; then : + withval=$with_convex_convex; col_convex_convex=$withval +fi + + +if test x$col_cylinder_cylinder = xlibccd -o \ + x$col_box_cylinder = xlibccd -o \ + x$col_capsule_cylinder = xlibccd -o \ + x$col_convex_box = xlibccd -o \ + x$col_convex_capsule = libccd -o \ + x$col_convex_cylinder = xlibccd -o \ + x$col_convex_sphere = libccd -o \ + x$col_convex_convex = libccd +then + use_libccd=yes +fi + + +libccd_source=internal + + +# Check whether --with-libccd was given. +if test "${with_libccd+set}" = set; then : + withval=$with_libccd; libccd_source=$withval +else + libccd_source=system +fi + + +if test x$use_libccd = xyes +then + if test x$libccd_source = xsystem + then + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for CCD" >&5 +$as_echo_n "checking for CCD... " >&6; } + +if test -n "$CCD_CFLAGS"; then + pkg_cv_CCD_CFLAGS="$CCD_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ccd\""; } >&5 + ($PKG_CONFIG --exists --print-errors "ccd") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CCD_CFLAGS=`$PKG_CONFIG --cflags "ccd" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$CCD_LIBS"; then + pkg_cv_CCD_LIBS="$CCD_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"ccd\""; } >&5 + ($PKG_CONFIG --exists --print-errors "ccd") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_CCD_LIBS=`$PKG_CONFIG --libs "ccd" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + CCD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "ccd" 2>&1` + else + CCD_PKG_ERRORS=`$PKG_CONFIG --print-errors "ccd" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$CCD_PKG_ERRORS" >&5 + + libccd_source=internal +elif test $pkg_failed = untried; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + libccd_source=internal +else + CCD_CFLAGS=$pkg_cv_CCD_CFLAGS + CCD_LIBS=$pkg_cv_CCD_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +fi + fi +fi + +# Configure libccd unconditionally as that may be needed for special make targets +subdirs="$subdirs libccd" + + + if test x$use_libccd != xno; then + LIBCCD_TRUE= + LIBCCD_FALSE='#' +else + LIBCCD_TRUE='#' + LIBCCD_FALSE= +fi + + if test x$libccd_source = xinternal; then + LIBCCD_INTERNAL_TRUE= + LIBCCD_INTERNAL_FALSE='#' +else + LIBCCD_INTERNAL_TRUE='#' + LIBCCD_INTERNAL_FALSE= +fi + + if test x$col_box_cylinder = xlibccd; then + LIBCCD_BOX_CYL_TRUE= + LIBCCD_BOX_CYL_FALSE='#' +else + LIBCCD_BOX_CYL_TRUE='#' + LIBCCD_BOX_CYL_FALSE= +fi + + if test x$col_cylinder_cylinder = xlibccd; then + LIBCCD_CYL_CYL_TRUE= + LIBCCD_CYL_CYL_FALSE='#' +else + LIBCCD_CYL_CYL_TRUE='#' + LIBCCD_CYL_CYL_FALSE= +fi + + if test x$col_capsule_cylinder = xlibccd; then + LIBCCD_CAP_CYL_TRUE= + LIBCCD_CAP_CYL_FALSE='#' +else + LIBCCD_CAP_CYL_TRUE='#' + LIBCCD_CAP_CYL_FALSE= +fi + + if test x$col_convex_box = xlibccd; then + LIBCCD_CONVEX_BOX_TRUE= + LIBCCD_CONVEX_BOX_FALSE='#' +else + LIBCCD_CONVEX_BOX_TRUE='#' + LIBCCD_CONVEX_BOX_FALSE= +fi + + if test x$col_convex_capsule = xlibccd; then + LIBCCD_CONVEX_CAP_TRUE= + LIBCCD_CONVEX_CAP_FALSE='#' +else + LIBCCD_CONVEX_CAP_TRUE='#' + LIBCCD_CONVEX_CAP_FALSE= +fi + + if test x$col_convex_cylinder = xlibccd; then + LIBCCD_CONVEX_CYL_TRUE= + LIBCCD_CONVEX_CYL_FALSE='#' +else + LIBCCD_CONVEX_CYL_TRUE='#' + LIBCCD_CONVEX_CYL_FALSE= +fi + + if test x$col_convex_sphere = xlibccd; then + LIBCCD_CONVEX_SPHERE_TRUE= + LIBCCD_CONVEX_SPHERE_FALSE='#' +else + LIBCCD_CONVEX_SPHERE_TRUE='#' + LIBCCD_CONVEX_SPHERE_FALSE= +fi + + if test x$col_convex_convex = xlibccd; then + LIBCCD_CONVEX_CONVEX_TRUE= + LIBCCD_CONVEX_CONVEX_FALSE='#' +else + LIBCCD_CONVEX_CONVEX_TRUE='#' + LIBCCD_CONVEX_CONVEX_FALSE= +fi + + + + +# Check whether --enable-asserts was given. +if test "${enable_asserts+set}" = set; then : + enableval=$enable_asserts; asserts=$enableval +else + asserts=yes +fi + +if test x$asserts = xno +then + CPPFLAGS="$CPPFLAGS -DdNODEBUG -DNDEBUG" +fi + + + + + +ac_config_files="$ac_config_files Makefile drawstuff/Makefile drawstuff/src/Makefile drawstuff/dstest/Makefile include/Makefile include/drawstuff/Makefile include/ode/Makefile include/ode/version.h include/ode/precision.h ode/Makefile ode/doc/Doxyfile ode/doc/Makefile ode/src/Makefile ode/src/joints/Makefile ode/demo/Makefile OPCODE/Makefile OPCODE/Ice/Makefile GIMPACT/Makefile GIMPACT/include/Makefile GIMPACT/include/GIMPACT/Makefile GIMPACT/src/Makefile tests/Makefile tests/joints/Makefile tests/UnitTest++/Makefile tests/UnitTest++/src/Makefile tests/UnitTest++/src/Posix/Makefile tests/UnitTest++/src/Win32/Makefile ode-config ode.pc" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +if test -z "${HAVE_DOXYGEN_TRUE}" && test -z "${HAVE_DOXYGEN_FALSE}"; then + as_fn_error $? "conditional \"HAVE_DOXYGEN\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${X86_64_SYSTEM_TRUE}" && test -z "${X86_64_SYSTEM_FALSE}"; then + as_fn_error $? "conditional \"X86_64_SYSTEM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${OPCODE_TRUE}" && test -z "${OPCODE_FALSE}"; then + as_fn_error $? "conditional \"OPCODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${GIMPACT_TRUE}" && test -z "${GIMPACT_FALSE}"; then + as_fn_error $? "conditional \"GIMPACT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${TRIMESH_TRUE}" && test -z "${TRIMESH_FALSE}"; then + as_fn_error $? "conditional \"TRIMESH\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${WIN32_TRUE}" && test -z "${WIN32_FALSE}"; then + as_fn_error $? "conditional \"WIN32\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${X11_TRUE}" && test -z "${X11_FALSE}"; then + as_fn_error $? "conditional \"X11\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${OSX_TRUE}" && test -z "${OSX_FALSE}"; then + as_fn_error $? "conditional \"OSX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_DRAWSTUFF_TRUE}" && test -z "${ENABLE_DRAWSTUFF_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_DRAWSTUFF\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_DEMOS_TRUE}" && test -z "${ENABLE_DEMOS_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_DEMOS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ENABLE_OU_TRUE}" && test -z "${ENABLE_OU_FALSE}"; then + as_fn_error $? "conditional \"ENABLE_OU\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_TRUE}" && test -z "${LIBCCD_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_INTERNAL_TRUE}" && test -z "${LIBCCD_INTERNAL_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_INTERNAL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_BOX_CYL_TRUE}" && test -z "${LIBCCD_BOX_CYL_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_BOX_CYL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CYL_CYL_TRUE}" && test -z "${LIBCCD_CYL_CYL_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CYL_CYL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CAP_CYL_TRUE}" && test -z "${LIBCCD_CAP_CYL_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CAP_CYL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CONVEX_BOX_TRUE}" && test -z "${LIBCCD_CONVEX_BOX_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CONVEX_BOX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CONVEX_CAP_TRUE}" && test -z "${LIBCCD_CONVEX_CAP_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CONVEX_CAP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CONVEX_CYL_TRUE}" && test -z "${LIBCCD_CONVEX_CYL_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CONVEX_CYL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CONVEX_SPHERE_TRUE}" && test -z "${LIBCCD_CONVEX_SPHERE_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CONVEX_SPHERE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LIBCCD_CONVEX_CONVEX_TRUE}" && test -z "${LIBCCD_CONVEX_CONVEX_FALSE}"; then + as_fn_error $? "conditional \"LIBCCD_CONVEX_CONVEX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by ODE $as_me 0.16.5, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +ODE config.status 0.16.5 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +AS='`$ECHO "$AS" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' +configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in AS \ +DLLTOOL \ +OBJDUMP \ +SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_import \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +lt_cv_nm_interface \ +nm_file_list_spec \ +lt_cv_truncate_bin \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +configure_time_dlsearch_path \ +configure_time_lt_sys_library_path \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' + +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "ode/src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS ode/src/config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "drawstuff/Makefile") CONFIG_FILES="$CONFIG_FILES drawstuff/Makefile" ;; + "drawstuff/src/Makefile") CONFIG_FILES="$CONFIG_FILES drawstuff/src/Makefile" ;; + "drawstuff/dstest/Makefile") CONFIG_FILES="$CONFIG_FILES drawstuff/dstest/Makefile" ;; + "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; + "include/drawstuff/Makefile") CONFIG_FILES="$CONFIG_FILES include/drawstuff/Makefile" ;; + "include/ode/Makefile") CONFIG_FILES="$CONFIG_FILES include/ode/Makefile" ;; + "include/ode/version.h") CONFIG_FILES="$CONFIG_FILES include/ode/version.h" ;; + "include/ode/precision.h") CONFIG_FILES="$CONFIG_FILES include/ode/precision.h" ;; + "ode/Makefile") CONFIG_FILES="$CONFIG_FILES ode/Makefile" ;; + "ode/doc/Doxyfile") CONFIG_FILES="$CONFIG_FILES ode/doc/Doxyfile" ;; + "ode/doc/Makefile") CONFIG_FILES="$CONFIG_FILES ode/doc/Makefile" ;; + "ode/src/Makefile") CONFIG_FILES="$CONFIG_FILES ode/src/Makefile" ;; + "ode/src/joints/Makefile") CONFIG_FILES="$CONFIG_FILES ode/src/joints/Makefile" ;; + "ode/demo/Makefile") CONFIG_FILES="$CONFIG_FILES ode/demo/Makefile" ;; + "OPCODE/Makefile") CONFIG_FILES="$CONFIG_FILES OPCODE/Makefile" ;; + "OPCODE/Ice/Makefile") CONFIG_FILES="$CONFIG_FILES OPCODE/Ice/Makefile" ;; + "GIMPACT/Makefile") CONFIG_FILES="$CONFIG_FILES GIMPACT/Makefile" ;; + "GIMPACT/include/Makefile") CONFIG_FILES="$CONFIG_FILES GIMPACT/include/Makefile" ;; + "GIMPACT/include/GIMPACT/Makefile") CONFIG_FILES="$CONFIG_FILES GIMPACT/include/GIMPACT/Makefile" ;; + "GIMPACT/src/Makefile") CONFIG_FILES="$CONFIG_FILES GIMPACT/src/Makefile" ;; + "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;; + "tests/joints/Makefile") CONFIG_FILES="$CONFIG_FILES tests/joints/Makefile" ;; + "tests/UnitTest++/Makefile") CONFIG_FILES="$CONFIG_FILES tests/UnitTest++/Makefile" ;; + "tests/UnitTest++/src/Makefile") CONFIG_FILES="$CONFIG_FILES tests/UnitTest++/src/Makefile" ;; + "tests/UnitTest++/src/Posix/Makefile") CONFIG_FILES="$CONFIG_FILES tests/UnitTest++/src/Posix/Makefile" ;; + "tests/UnitTest++/src/Win32/Makefile") CONFIG_FILES="$CONFIG_FILES tests/UnitTest++/src/Win32/Makefile" ;; + "ode-config") CONFIG_FILES="$CONFIG_FILES ode-config" ;; + "ode.pc") CONFIG_FILES="$CONFIG_FILES ode.pc" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# The names of the tagged configurations supported by this script. +available_tags='CXX ' + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Assembler program. +AS=$lt_AS + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Object dumper program. +OBJDUMP=$lt_OBJDUMP + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shared archive member basename,for filename based shared library versioning on AIX. +shared_archive_member_spec=$shared_archive_member_spec + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm into a list of symbols to manually relocate. +global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# The name lister interface. +nm_interface=$lt_lt_cv_nm_interface + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and where our libraries should be installed. +lt_sysroot=$lt_sysroot + +# Command to truncate a binary pipe. +lt_truncate_bin=$lt_lt_cv_truncate_bin + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Detected run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path + +# Explicit LT_SYS_LIBRARY_PATH set during ./configure time. +configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain=$ac_aux_dir/ltmain.sh + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + +export OU_NAMESPACE=$OU_NAMESPACE +export OU_FEATURE_SET=$OU_FEATURE_SET + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi + +# +# CONFIG_SUBDIRS section. +# +if test "$no_recursion" != yes; then + + # Remove --cache-file, --srcdir, and --disable-option-checking arguments + # so they do not pile up. + ac_sub_configure_args= + ac_prev= + eval "set x $ac_configure_args" + shift + for ac_arg + do + if test -n "$ac_prev"; then + ac_prev= + continue + fi + case $ac_arg in + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ + | --c=*) + ;; + --config-cache | -C) + ;; + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + ;; + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + ;; + --disable-option-checking) + ;; + *) + case $ac_arg in + *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_sub_configure_args " '$ac_arg'" ;; + esac + done + + # Always prepend --prefix to ensure using the same prefix + # in subdir configurations. + ac_arg="--prefix=$prefix" + case $ac_arg in + *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + ac_sub_configure_args="'$ac_arg' $ac_sub_configure_args" + + # Pass --silent + if test "$silent" = yes; then + ac_sub_configure_args="--silent $ac_sub_configure_args" + fi + + # Always prepend --disable-option-checking to silence warnings, since + # different subdirs can have different --enable and --with options. + ac_sub_configure_args="--disable-option-checking $ac_sub_configure_args" + + ac_popdir=`pwd` + for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue + + # Do not complain, so a configure script can configure whichever + # parts of a large source tree are present. + test -d "$srcdir/$ac_dir" || continue + + ac_msg="=== configuring in $ac_dir (`pwd`/$ac_dir)" + $as_echo "$as_me:${as_lineno-$LINENO}: $ac_msg" >&5 + $as_echo "$ac_msg" >&6 + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + cd "$ac_dir" + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f "$ac_srcdir/configure.gnu"; then + ac_sub_configure=$ac_srcdir/configure.gnu + elif test -f "$ac_srcdir/configure"; then + ac_sub_configure=$ac_srcdir/configure + elif test -f "$ac_srcdir/configure.in"; then + # This should be Cygnus configure. + ac_sub_configure=$ac_aux_dir/configure + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no configuration information is in $ac_dir" >&5 +$as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;} + ac_sub_configure= + fi + + # The recursion is here. + if test -n "$ac_sub_configure"; then + # Make the cache file name correct relative to the subdirectory. + case $cache_file in + [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;; + *) # Relative name. + ac_sub_cache_file=$ac_top_build_prefix$cache_file ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5 +$as_echo "$as_me: running $SHELL $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;} + # The eval makes quoting arguments work. + eval "\$SHELL \"\$ac_sub_configure\" $ac_sub_configure_args \ + --cache-file=\"\$ac_sub_cache_file\" --srcdir=\"\$ac_srcdir\"" || + as_fn_error $? "$ac_sub_configure failed for $ac_dir" "$LINENO" 5 + fi + + cd "$ac_popdir" + done +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +chmod +x ode-config + +BUILDDIR=`pwd` + +echo "Configuration:" +echo " Build system type: $build" +echo " Host system type: $host" +echo " Use double precision: $usedouble" +echo " Use drawstuff: $drawstuff" +echo " Demos enabled: $enable_demos" +echo " Use OPCODE: $opcode" +echo " Use GIMPACT: $gimpact" +echo " Use libccd: $use_libccd" + +if test x$use_libccd = xyes +then +echo " libccd source: $libccd_source" +fi + +echo " Custom colliders:" +echo " cylinder-cylinder: $col_cylinder_cylinder" +echo " box-cylinder: $col_box_cylinder" +echo " capsule-cylinder: $col_capsule_cylinder" +echo " convex-box: $col_convex_box" +echo " convex-capsule: $col_convex_capsule" +echo " convex-cylinder: $col_convex_cylinder" +echo " convex-sphere: $col_convex_sphere" +echo " convex-convex: $col_convex_convex" +echo " Is target a Pentium: $pentium" +echo " Is target x86-64: $cpu64" +echo " Use old opcode trimesh collider: $old_trimesh" +echo " TLS for global caches: $use_ou_tls" +echo " Threading intf enabled: $threading_intf" +echo " Built-in threading included: $use_builtin_threading_impl" +echo " Enable debug error check: $asserts" +echo " Headers will be installed in $includedir/ode" +echo " Libraries will be installed in $libdir" +echo " Building in directory $BUILDDIR" + diff --git a/thirdparty/ode-0.16.5/configure.ac b/thirdparty/ode-0.16.5/configure.ac new file mode 100644 index 0000000..424c91d --- /dev/null +++ b/thirdparty/ode-0.16.5/configure.ac @@ -0,0 +1,606 @@ +dnl AC_INIT does not take a macro as a version nr: set it separately! - Bram +AC_INIT([ODE],[0.16.5],[ode@ode.org]) +ODE_VERSION=0.16.5 +AC_SUBST(ODE_VERSION) + +# Those are instructions from the Libtool manual: +# 1. Start with version information of `0:0:0' for each libtool library. +# +# 2. Update the version information only immediately before a public +# release of your software. More frequent updates are unnecessary, +# and only guarantee that the current interface number gets larger +# faster. +# +# 3. If the library source code has changed at all since the last +# update, then increment REVISION (`C:R:A' becomes `C:r+1:A'). +# +# 4. If any interfaces have been added, removed, or changed since the +# last update, increment CURRENT, and set REVISION to 0. +# +# 5. If any interfaces have been added since the last public release, +# then increment AGE. +# +# 6. If any interfaces have been removed since the last public release, +# then set AGE to 0. +CURRENT=9 +REVISION=1 +AGE=1 + +AC_ARG_ENABLE(version-info, + AS_HELP_STRING([--disable-version-info], + [don't encode version information in the generated library]), + version_info=$enableval, + version_info=yes) +if test x$version_info = xyes +then + ODE_VERSION_INFO="-version-info $CURRENT:$REVISION:$AGE" +else + ODE_VERSION_INFO="-avoid-version" +fi +AC_SUBST(ODE_VERSION_INFO) + + +AC_CONFIG_SRCDIR([ode/src/ode.cpp]) +AC_CONFIG_MACRO_DIR([m4]) + +AC_CANONICAL_HOST + +AM_INIT_AUTOMAKE([1.10 foreign]) +AC_CONFIG_HEADERS([ode/src/config.h]) + +dnl This is needed because we have subdirectories +AC_PROG_MAKE_SET +AC_PROG_CXX +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CPP +AC_PROG_AWK +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MKDIR_P +LT_INIT([disable-shared win32-dll]) +AC_CHECK_TOOLS([WINDRES], [windres]) + +AC_C_BIGENDIAN +AC_C_INLINE +AC_C_VOLATILE +PKG_PROG_PKG_CONFIG + +AC_ARG_VAR([DOXYGEN], [set to doxygen binary to generate doxygen docs]) +AC_CHECK_PROGS([DOXYGEN], [doxygen]) +AM_CONDITIONAL([HAVE_DOXYGEN], [test x$DOXYGEN = xdoxygen]) + + +dnl this may NOT be the machine on which the code is going to run in, +dnl so allow users to compile programs for their target machine. +pentium=no +cpu64=no +case "$host_cpu" in + i586 | i686 | i786 ) + pentium=yes + AC_DEFINE(PENTIUM,1,[compiling for a pentium on a gcc-based platform?]) + ;; + x86_64* ) + pentium=yes + cpu64=yes + AC_DEFINE(X86_64_SYSTEM,1,[compiling for a X86_64 system on a gcc-based platform?]) + ;; +esac + +AM_CONDITIONAL(X86_64_SYSTEM, test x$cpu64 = xyes) + + + + + + +dnl check for required headers +AC_CHECK_HEADERS( [alloca.h stdio.h inttypes.h stdint.h stdlib.h math.h \ + string.h stdarg.h malloc.h float.h time.h sys/time.h \ + limits.h stddef.h]) + + +opcode=no +gimpact=no +AC_ARG_WITH(trimesh, AS_HELP_STRING([--with-trimesh=@<:@opcode|gimpact|none@:>@], + [use the specified system for trimesh support @<:@default=opcode@:>@]), + trimesh=$withval,trimesh=opcode +) +if test "$trimesh" = opcode +then + opcode=yes +fi +if test "$trimesh" = gimpact +then + gimpact=yes +fi + +AM_CONDITIONAL(OPCODE, test $opcode = yes) +AM_CONDITIONAL(GIMPACT, test $gimpact = yes) +AM_CONDITIONAL(TRIMESH, test $opcode = yes -o $gimpact = yes) + + +AC_MSG_CHECKING(if double precision is requested) +AC_ARG_ENABLE(double-precision, + AS_HELP_STRING([--enable-double-precision], + [Configure ODE to work with double precision, if not specified, single precision is used @<:@default=no@:>@]), + usedouble=$enableval,usedouble=no) +AC_MSG_RESULT([$usedouble]) +if test "$usedouble" = yes; +then + ODE_PRECISION=dDOUBLE +else + ODE_PRECISION=dSINGLE +fi +AC_SUBST(ODE_PRECISION) + + +AC_ARG_WITH([drawstuff], + AS_HELP_STRING([--with-drawstuff=X11|Win32|OSX|none], + [force a particular drawstuff implementation or disable it.[default=autodetect]]), + [drawstuff=$withval],[drawstuff=]) + +dnl Set some Platform Specific Variables +EXTRA_LIBTOOL_LDFLAGS= +case "$host_os" in + cygwin* | mingw*) + if test "x$drawstuff" = x + then + drawstuff="Win32" # if in a Windows enviroment + fi + EXTRA_LIBTOOL_LDFLAGS="-no-undefined" + ;; + *apple* | *darwin*) # For Mac OS X + if test "x$drawstuff" = x + then + drawstuff="OSX" + fi + dnl We need to use C++ compilation and linking for ode on Mac + dnl Might as well do it for all code. + CC="$CXX" + LINK="$CXXLINK" + ;; + *) + if test "x$drawstuff" = x + then + drawstuff="X11" # if anything else default to X11 + fi + ;; +esac +AC_SUBST(EXTRA_LIBTOOL_LDFLAGS) + + +dnl Set Drawstuff variables +AC_MSG_CHECKING([which drawstuff lib to build]) +AC_MSG_RESULT($drawstuff) + +if test "x$drawstuff" = "xX11" +then + # The built-in macro, X_PATH, causes too many problems, these days everyone uses Xorg, + # so we can ask pkg-config to find it for us. + PKG_CHECK_MODULES(X11, x11, [], [drawstuff="none"]) +fi + +dnl Check for OpenGL +if test "x$drawstuff" = "xOSX"; then + AC_DEFINE([HAVE_APPLE_OPENGL_FRAMEWORK], [1], + [Use the Apple OpenGL framework.]) + GL_LIBS="-framework OpenGL -framework GLUT" +elif test "x$drawstuff" != "xnone"; then + have_gl_headers=yes + AC_CHECK_HEADERS(GL/gl.h GL/glu.h GL/glext.h, , + [have_gl_headers=no], + [[#ifdef WIN32 + #include + #endif + #if HAVE_GL_GL_H + #include + #endif + #if HAVE_GL_GLU_H + #include + #endif + ]]) + have_gl=no + have_glu=no + TEMP_LDFLAGS="$LDFLAGS" + AC_CHECK_LIB(GL, main, [GL_LIBS="-lGL"; have_gl=yes]) + AC_CHECK_LIB(GLU, main, [GL_LIBS="-lGLU $GL_LIBS"; have_glu=yes], , -lGL) + AC_CHECK_LIB(opengl32, main, [GL_LIBS="-lopengl32"; have_gl=yes]) + AC_CHECK_LIB(glu32, main, [GL_LIBS="-lglu32 $GL_LIBS"; have_glu=yes], , -lopengl32) + LDFLAGS="$TEMP_LDFLAGS" + if test $have_gl = no -o $have_glu = no -o $have_gl_headers = no; then + drawstuff="none" + fi +fi +AC_SUBST(GL_LIBS) + +dnl Set Conditionals +AM_CONDITIONAL(WIN32, test x$drawstuff = xWin32) +AM_CONDITIONAL(X11, test x$drawstuff = xX11) +AM_CONDITIONAL(OSX, test x$drawstuff = xOSX) +AM_CONDITIONAL(ENABLE_DRAWSTUFF, test x$drawstuff != xnone) + +dnl Check if we want to build demos +AC_MSG_CHECKING(if demos should be built) +AC_ARG_ENABLE(demos, + AS_HELP_STRING([--disable-demos], [don't build demos]), + enable_demos=$enableval,enable_demos=yes) +if test x$drawstuff = xnone -a x$enable_demos = xyes ; then + enable_demos=no + AC_MSG_RESULT($enable_demos) + AC_MSG_WARN([Demos will not be built because OpenGL doesn't seem to work. See `config.log' for details.]) +else + AC_MSG_RESULT($enable_demos) +fi + + +dnl stdc++ is required when linking C programs against ode +AC_CHECK_LIB(stdc++,main,[LIBSTDCXX="-lstdc++"],[LIBSTDCXX=]) +AC_SUBST(LIBSTDCXX) +AC_CHECK_LIB(pthread,main,[LIBS="$LIBS -lpthread"]) + + +dnl test if we will build demos +AM_CONDITIONAL(ENABLE_DEMOS, test x$enable_demos = xyes) + + +dnl Check if the user wants the old timesh collider +old_trimesh=no +AC_ARG_ENABLE([old-trimesh], AS_HELP_STRING([--enable-old-trimesh],[enable use of the old trimesh collider]), + [old_trimesh=$enableval] + ) +if test x$old_trimesh = xyes -a $trimesh = opcode; then + AC_DEFINE(dTRIMESH_OPCODE_USE_OLD_TRIMESH_TRIMESH_COLLIDER, 1, + [Use the old trimesh-trimesh collider]) +else + old_trimesh=no +fi + + +dnl Check if the user wants to profile ODE using gprof +AC_MSG_CHECKING(for gprof) +AC_ARG_ENABLE([gprof], + AS_HELP_STRING([--enable-gprof],[enable profiling with gprof]), + gprof=$enableval, + gprof=no) +if test "$gprof" != no +then + CFLAGS="-pg $CFLAGS" + CXXFLAGS="-pg $CXXFLAGS" + AC_MSG_RESULT(enabled) + AC_CHECK_LIB(gmon, main,[LIBS="$LIBS -lgmon"]) +else + AC_MSG_RESULT(no) +fi + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_INT32_T +AC_FUNC_OBSTACK +AC_TYPE_SIZE_T +AC_TYPE_UINT32_T + +dnl Check for autoscan sugested functions +AC_CHECK_LIB(m, [main]) +AC_CHECK_LIB(sunmath, [main]) +AC_CHECK_LIB(rt, [main]) +AC_CHECK_FUNCS([atan2f clock_gettime copysign copysignf cosf fabsf floor fmodf gettimeofday isnan _isnan __isnan isnanf _isnanf __isnanf memmove memset pthread_attr_setstacklazy pthread_attr_setinheritsched pthread_condattr_setclock sinf snprintf sqrt sqrtf strchr strstr vsnprintf]) +AC_FUNC_ALLOCA + +dnl This trick allows having additional define in case if a function is not found. +dnl It fakes cached value for an inexistent function which is then used to fool function check to produce desired result. +AC_CHECK_FUNC(pthread_condattr_setclock,,ac_cv_func_no_pthread_condattr_setclock=yes) +AC_CHECK_FUNCS(no_pthread_condattr_setclock) + + +AC_ARG_ENABLE([threading-intf], + AS_HELP_STRING([--disable-threading-intf], + [disable threading interface support (external implementations cannot be assigned)] + ), + threading_intf=$enableval,threading_intf=yes) +AC_ARG_ENABLE([ou], + AS_HELP_STRING([--enable-ou], + [use TLS for global caches (allows threaded collision checks for isolated spaces)] + ), + use_ou_tls=$enableval,use_ou_tls=no) +use_ou="no" +if test x$use_ou_tls = xyes -o x$threading_intf = xyes +then + use_ou="yes" +fi + +OU_NAMESPACE=odeou +AC_CONFIG_COMMANDS_POST([export OU_NAMESPACE=$OU_NAMESPACE]) +AC_DEFINE([_OU_NAMESPACE],[odeou],[libou namespace for ODE]) +AC_DEFINE([dOU_ENABLED],[1],[Generic OU features are enabled]) + +if test x$use_ou_tls = xyes +then + OU_FEATURE_SET=_OU_FEATURE_SET_TLS + AC_DEFINE([_OU_FEATURE_SET],[_OU_FEATURE_SET_TLS],[OU features enabled]) +elif test x$use_ou = xyes +then + OU_FEATURE_SET=_OU_FEATURE_SET_ATOMICS + AC_DEFINE([_OU_FEATURE_SET],[_OU_FEATURE_SET_ATOMICS],[OU features enabled]) +else + OU_FEATURE_SET=_OU_FEATURE_SET_BASICS + AC_DEFINE([_OU_FEATURE_SET],[_OU_FEATURE_SET_BASICS],[OU features enabled]) +fi +AC_CONFIG_COMMANDS_POST([export OU_FEATURE_SET=$OU_FEATURE_SET]) + +if test x$use_ou = xyes +then + AC_DEFINE([dATOMICS_ENABLED],[1],[Atomic API of OU is enabled]) + if test x$use_ou_tls = xyes + then + AC_DEFINE([dTLS_ENABLED],[1],[Thread Local Storage API of OU is enabled]) + fi +fi + +case "$host_os" in + cygwin* | mingw*) + targetos=_OU_TARGET_OS_WINDOWS + ;; + *qnx*) + targetos=_OU_TARGET_OS_QNX + ;; + *apple* | *darwin*) + targetos=_OU_TARGET_OS_MAC + ;; + *sunos*) + targetos=_OU_TARGET_OS_SUNOS + ;; + *aix*) + targetos=_OU_TARGET_OS_AIX + ;; + *) + targetos=_OU_TARGET_OS_GENUNIX + ;; +esac + +if test $targetos = _OU_TARGET_OS_MAC +then + MAC_OS_X_VERSION=1000 + AC_CHECK_FUNC([OSAtomicAdd32Barrier], [MAC_OS_X_VERSION=1040]) + AC_CHECK_FUNC([OSAtomicAnd32OrigBarrier], [MAC_OS_X_VERSION=1050]) + AC_DEFINE_UNQUOTED(MAC_OS_X_VERSION, $MAC_OS_X_VERSION, [Mac OS X version setting for OU Library]) +fi + +if test $targetos = _OU_TARGET_OS_SUNOS +then + AC_CHECK_FUNC(atomic_inc_32_nv, [], + [targetos=_OU_TARGET_OS_GENUNIX]) +fi + +AC_DEFINE_UNQUOTED(_OU_TARGET_OS, $targetos, [Target OS setting for OU Library]) + +AC_CONFIG_SUBDIRS([ou]) +AM_CONDITIONAL(ENABLE_OU, true) + +if test x$threading_intf = xyes +then + AC_ARG_ENABLE([builtin-threading-impl], + AS_HELP_STRING([--disable-builtin-threading-impl], + [disable built-in multithreaded threading implementation] + ), + use_builtin_threading_impl=$enableval,use_builtin_threading_impl=yes) + if test x$use_builtin_threading_impl = xyes + then + AC_DEFINE([dBUILTIN_THREADING_IMPL_ENABLED],[1],[Built-in multithreaded threading implementation is included]) + fi +else + AC_DEFINE([dTHREADING_INTF_DISABLED],[1],[Threading interface is disabled]) + use_builtin_threading_impl=no +fi + +col_cylinder_cylinder=none +col_box_cylinder=default +col_capsule_cylinder=none +col_convex_box=none +col_convex_capsule=none +col_convex_cylinder=none +col_convex_sphere=default +col_convex_convex=default + + +use_libccd=no +libccd_all=no +AC_ARG_ENABLE(libccd, AS_HELP_STRING([--enable-libccd], + [enable all libccd colliders]), + libccd_all=$enableval) +if test x$libccd_all = xyes +then + col_cylinder_cylinder=libccd + col_box_cylinder=libccd + col_capsule_cylinder=libccd + col_convex_box=libccd + col_convex_capsule=libccd + col_convex_cylinder=libccd + col_convex_sphere=libccd + col_convex_convex=libccd + use_libccd=yes +fi + + +AC_ARG_WITH([cylinder-cylinder], AS_HELP_STRING([--with-cylinder-cylinder=@<:@none,libccd@:>@], [use specific collider for cylinder-cylinder]), + col_cylinder_cylinder=$withval) + +AC_ARG_WITH([box-cylinder], + AS_HELP_STRING([--with-box-cylinder=@<:@default,libccd@:>@], [use specific collider for box-cylinder]), + col_box_cylinder=$withval) + +AC_ARG_WITH([capsule-cylinder], AS_HELP_STRING([--with-capsule-cylinder=@<:@none,libccd@:>@], [use specific collider for capsule-cylinder]), + col_capsule_cylinder=$withval) + +AC_ARG_WITH([convex-box], AS_HELP_STRING([--with-convex-box=@<:@none,libccd@:>@], [use specific collider for convex-box]), + col_convex_box=$withval) + +AC_ARG_WITH([convex-capsule], AS_HELP_STRING([--with-convex-capsule=@<:@none,libccd@:>@], [use specific collider for convex-capsule]), + col_convex_capsule=$withval) + +AC_ARG_WITH([convex-cylinder], AS_HELP_STRING([--with-convex-cylinder=@<:@none,libccd@:>@], [use specific collider for convex-cylinder]), + col_convex_cylinder=$withval) + +AC_ARG_WITH([convex-sphere], AS_HELP_STRING([--with-convex-sphere=@<:@default,libccd@:>@], [use specific collider for convex-sphere]), + col_convex_sphere=$withval) + +AC_ARG_WITH([convex-convex], AS_HELP_STRING([--with-convex-convex=@<:@default,libccd@:>@], [use specific collider for convex-convex]), + col_convex_convex=$withval) + +if test x$col_cylinder_cylinder = xlibccd -o \ + x$col_box_cylinder = xlibccd -o \ + x$col_capsule_cylinder = xlibccd -o \ + x$col_convex_box = xlibccd -o \ + x$col_convex_capsule = libccd -o \ + x$col_convex_cylinder = xlibccd -o \ + x$col_convex_sphere = libccd -o \ + x$col_convex_convex = libccd +then + use_libccd=yes +fi + + +libccd_source=internal + +AC_ARG_WITH(libccd, + [AS_HELP_STRING([--with-libccd=@<:@internal|system@:>@], + [use the specified libccd @<:@default=system@:>@])], + [libccd_source=$withval], + [libccd_source=system]) + +if test x$use_libccd = xyes +then + if test x$libccd_source = xsystem + then + PKG_CHECK_MODULES(CCD, ccd, ,[libccd_source=internal]) + fi +fi + +# Configure libccd unconditionally as that may be needed for special make targets +AC_CONFIG_SUBDIRS([libccd]) + +AM_CONDITIONAL(LIBCCD, test x$use_libccd != xno) +AM_CONDITIONAL(LIBCCD_INTERNAL, test x$libccd_source = xinternal) +AM_CONDITIONAL(LIBCCD_BOX_CYL, test x$col_box_cylinder = xlibccd) +AM_CONDITIONAL(LIBCCD_CYL_CYL, test x$col_cylinder_cylinder = xlibccd) +AM_CONDITIONAL(LIBCCD_CAP_CYL, test x$col_capsule_cylinder = xlibccd) +AM_CONDITIONAL(LIBCCD_CONVEX_BOX, test x$col_convex_box = xlibccd) +AM_CONDITIONAL(LIBCCD_CONVEX_CAP, test x$col_convex_capsule = xlibccd) +AM_CONDITIONAL(LIBCCD_CONVEX_CYL, test x$col_convex_cylinder = xlibccd) +AM_CONDITIONAL(LIBCCD_CONVEX_SPHERE, test x$col_convex_sphere = xlibccd) +AM_CONDITIONAL(LIBCCD_CONVEX_CONVEX, test x$col_convex_convex = xlibccd) + + + +AC_ARG_ENABLE([asserts], + AS_HELP_STRING([--disable-asserts], + [disables debug error checking]), + asserts=$enableval,asserts=yes) +if test x$asserts = xno +then + CPPFLAGS="$CPPFLAGS -DdNODEBUG -DNDEBUG" +fi + + +dnl include found system headers into config.h +AH_TOP([ +#ifndef ODE_CONFIG_H +#define ODE_CONFIG_H +]) +AH_BOTTOM([ + +#ifdef HAVE_ALLOCA_H +#include +#endif +#ifdef HAVE_MALLOC_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_INTTYPES_H +#include +#endif + + +#include "typedefs.h" + + +#endif /* #define ODE_CONFIG_H */ +]) + +dnl Finally write our Makefiles +AC_CONFIG_FILES([ + Makefile + drawstuff/Makefile + drawstuff/src/Makefile + drawstuff/dstest/Makefile + include/Makefile + include/drawstuff/Makefile + include/ode/Makefile + include/ode/version.h + include/ode/precision.h + ode/Makefile + ode/doc/Doxyfile + ode/doc/Makefile + ode/src/Makefile + ode/src/joints/Makefile + ode/demo/Makefile + OPCODE/Makefile + OPCODE/Ice/Makefile + GIMPACT/Makefile + GIMPACT/include/Makefile + GIMPACT/include/GIMPACT/Makefile + GIMPACT/src/Makefile + tests/Makefile + tests/joints/Makefile + tests/UnitTest++/Makefile + tests/UnitTest++/src/Makefile + tests/UnitTest++/src/Posix/Makefile + tests/UnitTest++/src/Win32/Makefile + ode-config + ode.pc + ]) +AC_OUTPUT + +chmod +x ode-config + +BUILDDIR=`pwd` + +dnl Print some useful information +echo "Configuration:" +echo " Build system type: $build" +echo " Host system type: $host" +echo " Use double precision: $usedouble" +echo " Use drawstuff: $drawstuff" +echo " Demos enabled: $enable_demos" +echo " Use OPCODE: $opcode" +echo " Use GIMPACT: $gimpact" +echo " Use libccd: $use_libccd" + +if test x$use_libccd = xyes +then +echo " libccd source: $libccd_source" +fi + +echo " Custom colliders:" +echo " cylinder-cylinder: $col_cylinder_cylinder" +echo " box-cylinder: $col_box_cylinder" +echo " capsule-cylinder: $col_capsule_cylinder" +echo " convex-box: $col_convex_box" +echo " convex-capsule: $col_convex_capsule" +echo " convex-cylinder: $col_convex_cylinder" +echo " convex-sphere: $col_convex_sphere" +echo " convex-convex: $col_convex_convex" +echo " Is target a Pentium: $pentium" +echo " Is target x86-64: $cpu64" +echo " Use old opcode trimesh collider: $old_trimesh" +echo " TLS for global caches: $use_ou_tls" +echo " Threading intf enabled: $threading_intf" +echo " Built-in threading included: $use_builtin_threading_impl" +echo " Enable debug error check: $asserts" +echo " Headers will be installed in $includedir/ode" +echo " Libraries will be installed in $libdir" +echo " Building in directory $BUILDDIR" + diff --git a/thirdparty/ode-0.16.5/depcomp b/thirdparty/ode-0.16.5/depcomp new file mode 100644 index 0000000..fc98710 --- /dev/null +++ b/thirdparty/ode-0.16.5/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/thirdparty/ode-0.16.5/drawstuff/Makefile.am b/thirdparty/ode-0.16.5/drawstuff/Makefile.am new file mode 100644 index 0000000..def863c --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/Makefile.am @@ -0,0 +1,4 @@ +if ENABLE_DRAWSTUFF +SUBDIRS = src dstest +EXTRA_DIST = textures +endif diff --git a/thirdparty/ode-0.16.5/drawstuff/Makefile.in b/thirdparty/ode-0.16.5/drawstuff/Makefile.in new file mode 100644 index 0000000..a57e158 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/Makefile.in @@ -0,0 +1,641 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = drawstuff +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = src dstest +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@ENABLE_DRAWSTUFF_TRUE@SUBDIRS = src dstest +@ENABLE_DRAWSTUFF_TRUE@EXTRA_DIST = textures +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign drawstuff/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign drawstuff/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.am b/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.am new file mode 100644 index 0000000..cf7550c --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.am @@ -0,0 +1,14 @@ +noinst_PROGRAMS= dstest +AM_CPPFLAGS = -I$(top_srcdir)/drawstuff/src -I$(top_srcdir)/include + +dstest_SOURCES= dstest.cpp +dstest_LDADD=$(top_builddir)/drawstuff/src/libdrawstuff.la \ + @GL_LIBS@ + +if WIN32 +resources.o: $(top_srcdir)/drawstuff/src/resources.rc $(top_srcdir)/drawstuff/src/resource.h + $(WINDRES) $(top_srcdir)/drawstuff/src/resources.rc -o resources.o + +dstest_LDADD += resources.o +endif + diff --git a/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.in b/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.in new file mode 100644 index 0000000..e043d07 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/dstest/Makefile.in @@ -0,0 +1,614 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = dstest$(EXEEXT) +@WIN32_TRUE@am__append_1 = resources.o +subdir = drawstuff/dstest +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +PROGRAMS = $(noinst_PROGRAMS) +am_dstest_OBJECTS = dstest.$(OBJEXT) +dstest_OBJECTS = $(am_dstest_OBJECTS) +dstest_DEPENDENCIES = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(am__append_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(dstest_SOURCES) +DIST_SOURCES = $(dstest_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I$(top_srcdir)/drawstuff/src -I$(top_srcdir)/include +dstest_SOURCES = dstest.cpp +dstest_LDADD = $(top_builddir)/drawstuff/src/libdrawstuff.la @GL_LIBS@ \ + $(am__append_1) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign drawstuff/dstest/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign drawstuff/dstest/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +dstest$(EXEEXT): $(dstest_OBJECTS) $(dstest_DEPENDENCIES) $(EXTRA_dstest_DEPENDENCIES) + @rm -f dstest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(dstest_OBJECTS) $(dstest_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstest.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +@WIN32_TRUE@resources.o: $(top_srcdir)/drawstuff/src/resources.rc $(top_srcdir)/drawstuff/src/resource.h +@WIN32_TRUE@ $(WINDRES) $(top_srcdir)/drawstuff/src/resources.rc -o resources.o + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/drawstuff/dstest/dstest.cpp b/thirdparty/ode-0.16.5/drawstuff/dstest/dstest.cpp new file mode 100644 index 0000000..27f042f --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/dstest/dstest.cpp @@ -0,0 +1,125 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include + + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + + +void start() +{ + // adjust the starting viewpoint a bit + float xyz[3],hpr[3]; + dsGetViewpoint (xyz,hpr); + hpr[0] += 7; + dsSetViewpoint (xyz,hpr); +} + + +void simLoop (int pause) +{ + float pos[3]; + float R[12]; + static float a = 0; + + if (!pause) a += 0.02f; + if (a > (2*M_PI)) a -= (float) (2*M_PI); + float ca = (float) cos(a); + float sa = (float) sin(a); + + dsSetTexture (DS_WOOD); + + float b = (a > M_PI) ? (2*(a-(float)M_PI)) : a*2; + pos[0] = -0.3f; + pos[1] = 0; + pos[2] = (float) (0.1f*(2*M_PI*b - b*b) + 0.65f); + R[0] = ca; R[1] = 0; R[2] = -sa; + R[4] = 0; R[5] = 1; R[6] = 0; + R[8] = sa; R[9] = 0; R[10] = ca; + dsSetColor (1,0.8f,0.6f); + dsDrawSphere (pos,R,0.3f); + + dsSetTexture (DS_NONE); + + pos[0] = -0.2f; + pos[1] = 0.8f; + pos[2] = 0.4f; + R[0] = ca; R[1] = -sa; R[2] = 0; + R[4] = sa; R[5] = ca; R[6] = 0; + R[8] = 0; R[9] = 0; R[10] = 1; + float sides[3] = {0.1f,0.4f,0.8f}; + dsSetColor (0.6f,0.6f,1); + dsDrawBox (pos,R,sides); + + dsSetTexture (DS_WOOD); + + float r = 0.3f; // cylinder radius + float d = (float)cos(a*2) * 0.4f; + float cd = (float)cos(-d/r); + float sd = (float)sin(-d/r); + pos[0] = -0.2f; + pos[1] = -1 + d; + pos[2] = 0.3f; + R[0] = 0; R[1] = 0; R[2] = -1; + R[4] = -sd; R[5] = cd; R[6] = 0; + R[8] = cd; R[9] = sd; R[10] = 0; + dsSetColor (0.4f,1,1); + dsDrawCylinder (pos,R,0.8f,r); + + pos[0] = 0; + pos[1] = 0; + pos[2] = 0.2f; + R[0] = 0; R[1] = sa; R[2] = -ca; + R[4] = 0; R[5] = ca; R[6] = sa; + R[8] = 1; R[9] = 0; R[10] = 0; + dsSetColor (1,0.9f,0.2f); + dsDrawCappedCylinder (pos,R,0.8f,0.2f); +} + + +void command (int cmd) +{ + dsPrint ("received command %d (`%c')\n",cmd,cmd); +} + + +int main (int argc, char **argv) +{ + // setup pointers to callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = command; + fn.stop = 0; + fn.path_to_textures = 0; // uses default + + // run simulation + dsSimulationLoop (argc,argv,400,400,&fn); + + return 0; +} diff --git a/thirdparty/ode-0.16.5/drawstuff/src/Makefile.am b/thirdparty/ode-0.16.5/drawstuff/src/Makefile.am new file mode 100644 index 0000000..911f4fd --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/Makefile.am @@ -0,0 +1,26 @@ +# Drawstuff is meant as an aid for testing and not as a full +# rendering library. + +noinst_LTLIBRARIES = libdrawstuff.la +libdrawstuff_la_SOURCES = drawstuff.cpp internal.h +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/ode/src \ + -DDEFAULT_PATH_TO_TEXTURES='"$(top_srcdir)/drawstuff/textures/"' \ + $(X11_CFLAGS) + +if WIN32 +libdrawstuff_la_SOURCES+= windows.cpp resource.h resources.rc +libdrawstuff_la_LIBADD = -lwinmm -lgdi32 +libdrawstuff_la_LDFLAGS = -no-undefined +endif + +if X11 +libdrawstuff_la_SOURCES+= x11.cpp +libdrawstuff_la_LIBADD = $(X11_LIBS) +endif + +if OSX +libdrawstuff_la_SOURCES+= osx.cpp +endif + diff --git a/thirdparty/ode-0.16.5/drawstuff/src/Makefile.in b/thirdparty/ode-0.16.5/drawstuff/src/Makefile.in new file mode 100644 index 0000000..4fb86f7 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/Makefile.in @@ -0,0 +1,655 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Drawstuff is meant as an aid for testing and not as a full +# rendering library. + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@WIN32_TRUE@am__append_1 = windows.cpp resource.h resources.rc +@X11_TRUE@am__append_2 = x11.cpp +@OSX_TRUE@am__append_3 = osx.cpp +subdir = drawstuff/src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +@X11_TRUE@libdrawstuff_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__libdrawstuff_la_SOURCES_DIST = drawstuff.cpp internal.h \ + windows.cpp resource.h resources.rc x11.cpp osx.cpp +@WIN32_TRUE@am__objects_1 = windows.lo +@X11_TRUE@am__objects_2 = x11.lo +@OSX_TRUE@am__objects_3 = osx.lo +am_libdrawstuff_la_OBJECTS = drawstuff.lo $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) +libdrawstuff_la_OBJECTS = $(am_libdrawstuff_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdrawstuff_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libdrawstuff_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdrawstuff_la_SOURCES) +DIST_SOURCES = $(am__libdrawstuff_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libdrawstuff.la +libdrawstuff_la_SOURCES = drawstuff.cpp internal.h $(am__append_1) \ + $(am__append_2) $(am__append_3) +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/ode/src \ + -DDEFAULT_PATH_TO_TEXTURES='"$(top_srcdir)/drawstuff/textures/"' \ + $(X11_CFLAGS) + +@WIN32_TRUE@libdrawstuff_la_LIBADD = -lwinmm -lgdi32 +@X11_TRUE@libdrawstuff_la_LIBADD = $(X11_LIBS) +@WIN32_TRUE@libdrawstuff_la_LDFLAGS = -no-undefined +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign drawstuff/src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign drawstuff/src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdrawstuff.la: $(libdrawstuff_la_OBJECTS) $(libdrawstuff_la_DEPENDENCIES) $(EXTRA_libdrawstuff_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libdrawstuff_la_LINK) $(libdrawstuff_la_OBJECTS) $(libdrawstuff_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/drawstuff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windows.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11.Plo@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/drawstuff/src/drawstuff.cpp b/thirdparty/ode-0.16.5/drawstuff/src/drawstuff.cpp new file mode 100644 index 0000000..cb9988d --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/drawstuff.cpp @@ -0,0 +1,1671 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +simple graphics. + +the following command line flags can be used (typically under unix) + -notex Do not use any textures + -noshadow[s] Do not draw any shadows + -pause Start the simulation paused + -texturepath Inform an alternative textures path + +TODO +---- + +manage openGL state changes better + +*/ + +#ifdef WIN32 +#include +#endif + +#include +#include "config.h" + +#ifdef HAVE_APPLE_OPENGL_FRAMEWORK +#include +#include +#else +#include +#include +#endif + +#include "drawstuff/drawstuff.h" +#include "internal.h" + +//*************************************************************************** +// misc + +#ifndef DEFAULT_PATH_TO_TEXTURES +#if 0 +#define DEFAULT_PATH_TO_TEXTURES "..\\textures\\" +#else +#define DEFAULT_PATH_TO_TEXTURES "../textures/" +#endif +#endif + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +// constants to convert degrees to radians and the reverse +#define RAD_TO_DEG (180.0/M_PI) +#define DEG_TO_RAD (M_PI/180.0) + +// light vector. LIGHTZ is implicitly 1 +#define LIGHTX (1.0f) +#define LIGHTY (0.4f) + +// ground and sky +#define SHADOW_INTENSITY (0.65f) +#define GROUND_R (0.5f) // ground color for when there's no texture +#define GROUND_G (0.5f) +#define GROUND_B (0.3f) + +const float ground_scale = 1.0f/1.0f; // ground texture scale (1/size) +const float ground_ofsx = 0.5; // offset of ground texture +const float ground_ofsy = 0.5; +const float sky_scale = 1.0f/4.0f; // sky texture scale (1/size) +const float sky_height = 1.0f; // sky height above viewpoint + +//*************************************************************************** +// misc mathematics stuff + +static void normalizeVector3 (float v[3]) +{ + float len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; + if (len <= 0.0f) { + v[0] = 1; + v[1] = 0; + v[2] = 0; + } + else { + len = 1.0f / (float)sqrt(len); + v[0] *= len; + v[1] *= len; + v[2] *= len; + } +} + +static void crossProduct3(float res[3], const float a[3], const float b[3]) +{ + float res_0 = a[1]*b[2] - a[2]*b[1]; + float res_1 = a[2]*b[0] - a[0]*b[2]; + float res_2 = a[0]*b[1] - a[1]*b[0]; + // Only assign after all the calculations are over to avoid incurring memory aliasing + res[0] = res_0; + res[1] = res_1; + res[2] = res_2; +} + +//*************************************************************************** +// PPM image object + +typedef unsigned char byte; + +class Image { + int image_width,image_height; + byte *image_data; +public: + Image (char *filename); + // load from PPM file + ~Image(); + int width() { return image_width; } + int height() { return image_height; } + byte *data() { return image_data; } +}; + + +// skip over whitespace and comments in a stream. + +static void skipWhiteSpace (char *filename, FILE *f) +{ + int c,d; + for(;;) { + c = fgetc(f); + if (c==EOF) dsError ("unexpected end of file in \"%s\"",filename); + + // skip comments + if (c == '#') { + do { + d = fgetc(f); + if (d==EOF) dsError ("unexpected end of file in \"%s\"",filename); + } while (d != '\n'); + continue; + } + + if (c > ' ') { + ungetc (c,f); + return; + } + } +} + + +// read a number from a stream, this return 0 if there is none (that's okay +// because 0 is a bad value for all PPM numbers anyway). + +static int readNumber (char *filename, FILE *f) +{ + int c,n=0; + for(;;) { + c = fgetc(f); + if (c==EOF) dsError ("unexpected end of file in \"%s\"",filename); + if (c >= '0' && c <= '9') n = n*10 + (c - '0'); + else { + ungetc (c,f); + return n; + } + } +} + + +Image::Image (char *filename) +{ + FILE *f = fopen (filename,"rb"); + if (!f) dsError ("Can't open image file `%s'",filename); + + // read in header + if (fgetc(f) != 'P' || fgetc(f) != '6') + dsError ("image file \"%s\" is not a binary PPM (no P6 header)",filename); + skipWhiteSpace (filename,f); + + // read in image parameters + image_width = readNumber (filename,f); + skipWhiteSpace (filename,f); + image_height = readNumber (filename,f); + skipWhiteSpace (filename,f); + int max_value = readNumber (filename,f); + + // check values + if (image_width < 1 || image_height < 1) + dsError ("bad image file \"%s\"",filename); + if (max_value != 255) + dsError ("image file \"%s\" must have color range of 255",filename); + + // read either nothing, LF (10), or CR,LF (13,10) + int c = fgetc(f); + if (c == 10) { + // LF + } + else if (c == 13) { + // CR + c = fgetc(f); + if (c != 10) ungetc (c,f); + } + else ungetc (c,f); + + // read in rest of data + image_data = new byte [image_width*image_height*3]; + if (fread (image_data,image_width*image_height*3,1,f) != 1) + dsError ("Can not read data from image file `%s'",filename); + fclose (f); +} + + +Image::~Image() +{ + delete[] image_data; +} + +//*************************************************************************** +// Texture object. + +class Texture { + Image *image; + GLuint name; +public: + Texture (char *filename); + ~Texture(); + void bind (int modulate); +}; + + +Texture::Texture (char *filename) +{ + image = new Image (filename); + glGenTextures (1,&name); + glBindTexture (GL_TEXTURE_2D,name); + + // set pixel unpacking mode + glPixelStorei (GL_UNPACK_SWAP_BYTES, 0); + glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glPixelStorei (GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0); + + // glTexImage2D (GL_TEXTURE_2D, 0, 3, image->width(), image->height(), 0, + // GL_RGB, GL_UNSIGNED_BYTE, image->data()); + gluBuild2DMipmaps (GL_TEXTURE_2D, 3, image->width(), image->height(), + GL_RGB, GL_UNSIGNED_BYTE, image->data()); + + // set texture parameters - will these also be bound to the texture??? + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + GL_LINEAR_MIPMAP_LINEAR); + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); +} + + +Texture::~Texture() +{ + delete image; + glDeleteTextures (1,&name); +} + + +void Texture::bind (int modulate) +{ + glBindTexture (GL_TEXTURE_2D,name); + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, + modulate ? GL_MODULATE : GL_DECAL); +} + +//*************************************************************************** +// the current drawing state (for when the user's step function is drawing) + +static float color[4] = {0,0,0,0}; // current r,g,b,alpha color +static int tnum = 0; // current texture number + +//*************************************************************************** +// OpenGL utility stuff + +static void setCamera (float x, float y, float z, float h, float p, float r) +{ + glMatrixMode (GL_MODELVIEW); + glLoadIdentity(); + glRotatef (90, 0,0,1); + glRotatef (90, 0,1,0); + glRotatef (r, 1,0,0); + glRotatef (p, 0,1,0); + glRotatef (-h, 0,0,1); + glTranslatef (-x,-y,-z); +} + + +// sets the material color, not the light color + +static void setColor (float r, float g, float b, float alpha) +{ + GLfloat light_ambient[4],light_diffuse[4],light_specular[4]; + light_ambient[0] = r*0.3f; + light_ambient[1] = g*0.3f; + light_ambient[2] = b*0.3f; + light_ambient[3] = alpha; + light_diffuse[0] = r*0.7f; + light_diffuse[1] = g*0.7f; + light_diffuse[2] = b*0.7f; + light_diffuse[3] = alpha; + light_specular[0] = r*0.2f; + light_specular[1] = g*0.2f; + light_specular[2] = b*0.2f; + light_specular[3] = alpha; + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, light_ambient); + glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, light_diffuse); + glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, light_specular); + glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 5.0f); +} + + +static void setTransform (const float pos[3], const float R[12]) +{ + GLfloat matrix[16]; + matrix[0]=R[0]; + matrix[1]=R[4]; + matrix[2]=R[8]; + matrix[3]=0; + matrix[4]=R[1]; + matrix[5]=R[5]; + matrix[6]=R[9]; + matrix[7]=0; + matrix[8]=R[2]; + matrix[9]=R[6]; + matrix[10]=R[10]; + matrix[11]=0; + matrix[12]=pos[0]; + matrix[13]=pos[1]; + matrix[14]=pos[2]; + matrix[15]=1; + glPushMatrix(); + glMultMatrixf (matrix); +} +static void setTransformD (const double pos[3], const double R[12]) +{ + GLdouble matrix[16]; + matrix[0]=R[0]; + matrix[1]=R[4]; + matrix[2]=R[8]; + matrix[3]=0; + matrix[4]=R[1]; + matrix[5]=R[5]; + matrix[6]=R[9]; + matrix[7]=0; + matrix[8]=R[2]; + matrix[9]=R[6]; + matrix[10]=R[10]; + matrix[11]=0; + matrix[12]=pos[0]; + matrix[13]=pos[1]; + matrix[14]=pos[2]; + matrix[15]=1; + glPushMatrix(); + glMultMatrixd (matrix); +} + + +// set shadow projection transform + +static void setShadowTransform() +{ + GLfloat matrix[16]; + for (int i=0; i<16; i++) matrix[i] = 0; + matrix[0]=1; + matrix[5]=1; + matrix[8]=-LIGHTX; + matrix[9]=-LIGHTY; + matrix[15]=1; + glPushMatrix(); + glMultMatrixf (matrix); +} + +static void drawConvex (const float *_planes, unsigned int _planecount, + const float *_points, unsigned int /*_pointcount*/, + const unsigned int *_polygons) +{ + unsigned int polyindex=0; + for(unsigned int i=0;i<_planecount;++i) + { + unsigned int pointcount=_polygons[polyindex]; + polyindex++; + glBegin (GL_POLYGON); + glNormal3f(_planes[(i*4)+0], + _planes[(i*4)+1], + _planes[(i*4)+2]); + for(unsigned int j=0;j 0) { + float q1[3],q2[3],q3[3]; // sub-vertices + for (i=0; i<3; i++) { + q1[i] = 0.5f*(p1[i]+p2[i]); + q2[i] = 0.5f*(p2[i]+p3[i]); + q3[i] = 0.5f*(p3[i]+p1[i]); + } + float length1 = (float)(1.0/sqrt(q1[0]*q1[0]+q1[1]*q1[1]+q1[2]*q1[2])); + float length2 = (float)(1.0/sqrt(q2[0]*q2[0]+q2[1]*q2[1]+q2[2]*q2[2])); + float length3 = (float)(1.0/sqrt(q3[0]*q3[0]+q3[1]*q3[1]+q3[2]*q3[2])); + for (i=0; i<3; i++) { + q1[i] *= length1; + q2[i] *= length2; + q3[i] *= length3; + } + drawPatch (p1,q1,q3,level-1); + drawPatch (q1,p2,q2,level-1); + drawPatch (q1,q2,q3,level-1); + drawPatch (q3,q2,p3,level-1); + } + else { + glNormal3f (p1[0],p1[1],p1[2]); + glVertex3f (p1[0],p1[1],p1[2]); + glNormal3f (p2[0],p2[1],p2[2]); + glVertex3f (p2[0],p2[1],p2[2]); + glNormal3f (p3[0],p3[1],p3[2]); + glVertex3f (p3[0],p3[1],p3[2]); + } +} + + +// draw a sphere of radius 1 + +static int sphere_quality = 1; + +static void drawSphere() +{ + // icosahedron data for an icosahedron of radius 1.0 +# define ICX 0.525731112119133606f +# define ICZ 0.850650808352039932f + static GLfloat idata[12][3] = { + {-ICX, 0, ICZ}, + {ICX, 0, ICZ}, + {-ICX, 0, -ICZ}, + {ICX, 0, -ICZ}, + {0, ICZ, ICX}, + {0, ICZ, -ICX}, + {0, -ICZ, ICX}, + {0, -ICZ, -ICX}, + {ICZ, ICX, 0}, + {-ICZ, ICX, 0}, + {ICZ, -ICX, 0}, + {-ICZ, -ICX, 0} + }; + + static int index[20][3] = { + {0, 4, 1}, {0, 9, 4}, + {9, 5, 4}, {4, 5, 8}, + {4, 8, 1}, {8, 10, 1}, + {8, 3, 10}, {5, 3, 8}, + {5, 2, 3}, {2, 7, 3}, + {7, 10, 3}, {7, 6, 10}, + {7, 11, 6}, {11, 0, 6}, + {0, 1, 6}, {6, 1, 10}, + {9, 0, 11}, {9, 11, 2}, + {9, 2, 5}, {7, 2, 11}, + }; + + static GLuint listnum = 0; + if (listnum==0) { + listnum = glGenLists (1); + glNewList (listnum,GL_COMPILE); + glBegin (GL_TRIANGLES); + for (int i=0; i<20; i++) { + drawPatch (&idata[index[i][2]][0],&idata[index[i][1]][0], + &idata[index[i][0]][0],sphere_quality); + } + glEnd(); + glEndList(); + } + glCallList (listnum); +} + + +static void drawSphereShadow (float px, float py, float pz, float radius) +{ + // calculate shadow constants based on light vector + static int init=0; + static float len2,len1,scale; + if (!init) { + len2 = LIGHTX*LIGHTX + LIGHTY*LIGHTY; + len1 = 1.0f/(float)sqrt(len2); + scale = (float) sqrt(len2 + 1); + init = 1; + } + + // map sphere center to ground plane based on light vector + px -= LIGHTX*pz; + py -= LIGHTY*pz; + + const float kx = 0.96592582628907f; + const float ky = 0.25881904510252f; + float x=radius, y=0; + + glBegin (GL_TRIANGLE_FAN); + for (int i=0; i<24; i++) { + // for all points on circle, scale to elongated rotated shadow and draw + float x2 = (LIGHTX*x*scale - LIGHTY*y)*len1 + px; + float y2 = (LIGHTY*x*scale + LIGHTX*y)*len1 + py; + glTexCoord2f (x2*ground_scale+ground_ofsx,y2*ground_scale+ground_ofsy); + glVertex3f (x2,y2,0); + + // rotate [x,y] vector + float xtmp = kx*x - ky*y; + y = ky*x + kx*y; + x = xtmp; + } + glEnd(); +} + + +static void drawTriangle (const float *v0, const float *v1, const float *v2, int solid) +{ + float u[3],v[3],normal[3]; + u[0] = v1[0] - v0[0]; + u[1] = v1[1] - v0[1]; + u[2] = v1[2] - v0[2]; + v[0] = v2[0] - v0[0]; + v[1] = v2[1] - v0[1]; + v[2] = v2[2] - v0[2]; + crossProduct3(normal,u,v); + normalizeVector3 (normal); + + glBegin(solid ? GL_TRIANGLES : GL_LINE_STRIP); + glNormal3fv (normal); + glVertex3fv (v0); + glVertex3fv (v1); + glVertex3fv (v2); + glEnd(); +} + +static void drawTriangleD (const double *v0, const double *v1, const double *v2, int solid) +{ + float u[3],v[3],normal[3]; + u[0] = float( v1[0] - v0[0] ); + u[1] = float( v1[1] - v0[1] ); + u[2] = float( v1[2] - v0[2] ); + v[0] = float( v2[0] - v0[0] ); + v[1] = float( v2[1] - v0[1] ); + v[2] = float( v2[2] - v0[2] ); + crossProduct3(normal,u,v); + normalizeVector3 (normal); + + glBegin(solid ? GL_TRIANGLES : GL_LINE_STRIP); + glNormal3fv (normal); + glVertex3dv (v0); + glVertex3dv (v1); + glVertex3dv (v2); + glEnd(); +} + + +// draw a capped cylinder of length l and radius r, aligned along the x axis + +static int capped_cylinder_quality = 3; + +static void drawCapsule (float l, float r) +{ + int i,j; + float tmp,nx,ny,nz,start_nx,start_ny,a,ca,sa; + // number of sides to the cylinder (divisible by 4): + const int n = capped_cylinder_quality*4; + + l *= 0.5; + a = float(M_PI*2.0)/float(n); + sa = (float) sin(a); + ca = (float) cos(a); + + // draw cylinder body + ny=1; nz=0; // normal vector = (0,ny,nz) + glBegin (GL_TRIANGLE_STRIP); + for (i=0; i<=n; i++) { + glNormal3d (ny,nz,0); + glVertex3d (ny*r,nz*r,l); + glNormal3d (ny,nz,0); + glVertex3d (ny*r,nz*r,-l); + // rotate ny,nz + tmp = ca*ny - sa*nz; + nz = sa*ny + ca*nz; + ny = tmp; + } + glEnd(); + + // draw first cylinder cap + start_nx = 0; + start_ny = 1; + for (j=0; j<(n/4); j++) { + // get start_n2 = rotated start_n + float start_nx2 = ca*start_nx + sa*start_ny; + float start_ny2 = -sa*start_nx + ca*start_ny; + // get n=start_n and n2=start_n2 + nx = start_nx; ny = start_ny; nz = 0; + float nx2 = start_nx2, ny2 = start_ny2, nz2 = 0; + glBegin (GL_TRIANGLE_STRIP); + for (i=0; i<=n; i++) { + glNormal3d (ny2,nz2,nx2); + glVertex3d (ny2*r,nz2*r,l+nx2*r); + glNormal3d (ny,nz,nx); + glVertex3d (ny*r,nz*r,l+nx*r); + // rotate n,n2 + tmp = ca*ny - sa*nz; + nz = sa*ny + ca*nz; + ny = tmp; + tmp = ca*ny2- sa*nz2; + nz2 = sa*ny2 + ca*nz2; + ny2 = tmp; + } + glEnd(); + start_nx = start_nx2; + start_ny = start_ny2; + } + + // draw second cylinder cap + start_nx = 0; + start_ny = 1; + for (j=0; j<(n/4); j++) { + // get start_n2 = rotated start_n + float start_nx2 = ca*start_nx - sa*start_ny; + float start_ny2 = sa*start_nx + ca*start_ny; + // get n=start_n and n2=start_n2 + nx = start_nx; ny = start_ny; nz = 0; + float nx2 = start_nx2, ny2 = start_ny2, nz2 = 0; + glBegin (GL_TRIANGLE_STRIP); + for (i=0; i<=n; i++) { + glNormal3d (ny,nz,nx); + glVertex3d (ny*r,nz*r,-l+nx*r); + glNormal3d (ny2,nz2,nx2); + glVertex3d (ny2*r,nz2*r,-l+nx2*r); + // rotate n,n2 + tmp = ca*ny - sa*nz; + nz = sa*ny + ca*nz; + ny = tmp; + tmp = ca*ny2- sa*nz2; + nz2 = sa*ny2 + ca*nz2; + ny2 = tmp; + } + glEnd(); + start_nx = start_nx2; + start_ny = start_ny2; + } +} + + +// draw a cylinder of length l and radius r, aligned along the z axis + +static void drawCylinder (float l, float r, float zoffset) +{ + int i; + float tmp,ny,nz,a,ca,sa; + const int n = 24; // number of sides to the cylinder (divisible by 4) + + l *= 0.5; + a = float(M_PI*2.0)/float(n); + sa = (float) sin(a); + ca = (float) cos(a); + + // draw cylinder body + ny=1; nz=0; // normal vector = (0,ny,nz) + glBegin (GL_TRIANGLE_STRIP); + for (i=0; i<=n; i++) { + glNormal3d (ny,nz,0); + glVertex3d (ny*r,nz*r,l+zoffset); + glNormal3d (ny,nz,0); + glVertex3d (ny*r,nz*r,-l+zoffset); + // rotate ny,nz + tmp = ca*ny - sa*nz; + nz = sa*ny + ca*nz; + ny = tmp; + } + glEnd(); + + // draw top cap + glShadeModel (GL_FLAT); + ny=1; nz=0; // normal vector = (0,ny,nz) + glBegin (GL_TRIANGLE_FAN); + glNormal3d (0,0,1); + glVertex3d (0,0,l+zoffset); + for (i=0; i<=n; i++) { + if (i==1 || i==n/2+1) + setColor (color[0]*0.75f,color[1]*0.75f,color[2]*0.75f,color[3]); + glNormal3d (0,0,1); + glVertex3d (ny*r,nz*r,l+zoffset); + if (i==1 || i==n/2+1) + setColor (color[0],color[1],color[2],color[3]); + + // rotate ny,nz + tmp = ca*ny - sa*nz; + nz = sa*ny + ca*nz; + ny = tmp; + } + glEnd(); + + // draw bottom cap + ny=1; nz=0; // normal vector = (0,ny,nz) + glBegin (GL_TRIANGLE_FAN); + glNormal3d (0,0,-1); + glVertex3d (0,0,-l+zoffset); + for (i=0; i<=n; i++) { + if (i==1 || i==n/2+1) + setColor (color[0]*0.75f,color[1]*0.75f,color[2]*0.75f,color[3]); + glNormal3d (0,0,-1); + glVertex3d (ny*r,nz*r,-l+zoffset); + if (i==1 || i==n/2+1) + setColor (color[0],color[1],color[2],color[3]); + + // rotate ny,nz + tmp = ca*ny + sa*nz; + nz = -sa*ny + ca*nz; + ny = tmp; + } + glEnd(); +} + +//*************************************************************************** +// motion model + +// current camera position and orientation +static float view_xyz[3]; // position x,y,z +static float view_hpr[3]; // heading, pitch, roll (degrees) + + +// initialize the above variables + +static void initMotionModel() +{ + view_xyz[0] = 2; + view_xyz[1] = 0; + view_xyz[2] = 1; + view_hpr[0] = 180; + view_hpr[1] = 0; + view_hpr[2] = 0; +} + + +static void wrapCameraAngles() +{ + for (int i=0; i<3; i++) { + while (view_hpr[i] > 180) view_hpr[i] -= 360; + while (view_hpr[i] < -180) view_hpr[i] += 360; + } +} + + +// call this to update the current camera position. the bits in 'mode' say +// if the left (1), middle (2) or right (4) mouse button is pressed, and +// (deltax,deltay) is the amount by which the mouse pointer has moved. + +void dsMotion (int mode, int deltax, int deltay) +{ + float side = 0.01f * float(deltax); + float fwd = (mode==4) ? (0.01f * float(deltay)) : 0.0f; + float s = (float) sin (view_hpr[0]*DEG_TO_RAD); + float c = (float) cos (view_hpr[0]*DEG_TO_RAD); + + if (mode==1) { + view_hpr[0] += float (deltax) * 0.5f; + view_hpr[1] += float (deltay) * 0.5f; + } + else { + view_xyz[0] += -s*side + c*fwd; + view_xyz[1] += c*side + s*fwd; + if (mode==2 || mode==5) view_xyz[2] += 0.01f * float(deltay); + } + wrapCameraAngles(); +} + +//*************************************************************************** +// drawing loop stuff + +// the current state: +// 0 = uninitialized +// 1 = dsSimulationLoop() called +// 2 = dsDrawFrame() called +static int current_state = 0; + +// textures and shadows +static int use_textures=1; // 1 if textures to be drawn +static int use_shadows=1; // 1 if shadows to be drawn +static Texture *sky_texture = 0; +static Texture *ground_texture = 0; +static Texture *wood_texture = 0; +static Texture *checkered_texture = 0; + +static Texture *texture[4+1]; // +1 since index 0 is not used + + + +#if !defined(macintosh) || defined(ODE_PLATFORM_OSX) + +void dsStartGraphics (int /*width*/, int /*height*/, dsFunctions *fn) +{ + + const char *prefix = DEFAULT_PATH_TO_TEXTURES; + if (fn->version >= 2 && fn->path_to_textures) prefix = fn->path_to_textures; + char *s = (char*) alloca (strlen(prefix) + 20); + + strcpy (s,prefix); + strcat (s,"/sky.ppm"); + texture[DS_SKY] = sky_texture = new Texture (s); + + strcpy (s,prefix); + strcat (s,"/ground.ppm"); + texture[DS_GROUND] = ground_texture = new Texture (s); + + strcpy (s,prefix); + strcat (s,"/wood.ppm"); + texture[DS_WOOD] = wood_texture = new Texture (s); + + strcpy (s,prefix); + strcat (s,"/checkered.ppm"); + texture[DS_CHECKERED] = checkered_texture = new Texture (s); +} + +#else // macintosh + +void dsStartGraphics (int width, int height, dsFunctions *fn) +{ + + // All examples build into the same dir + char *prefix = "::::drawstuff:textures"; + char *s = (char*) alloca (strlen(prefix) + 20); + + strcpy (s,prefix); + strcat (s,":sky.ppm"); + sky_texture = new Texture (s); + + strcpy (s,prefix); + strcat (s,":ground.ppm"); + ground_texture = new Texture (s); + + strcpy (s,prefix); + strcat (s,":wood.ppm"); + wood_texture = new Texture (s); +} + +#endif + + +void dsStopGraphics() +{ + delete sky_texture; + delete ground_texture; + delete wood_texture; + sky_texture = 0; + ground_texture = 0; + wood_texture = 0; +} + + +static void drawSky (float view_xyz[3]) +{ + glDisable (GL_LIGHTING); + if (use_textures) { + glEnable (GL_TEXTURE_2D); + sky_texture->bind (0); + } + else { + glDisable (GL_TEXTURE_2D); + glColor3f (0,0.5,1.0); + } + + // make sure sky depth is as far back as possible + glShadeModel (GL_FLAT); + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LEQUAL); + glDepthRange (1,1); + + const float ssize = 1000.0f; + static float offset = 0.0f; + + float x = ssize*sky_scale; + float z = view_xyz[2] + sky_height; + + glBegin (GL_QUADS); + glNormal3f (0,0,-1); + glTexCoord2f (-x+offset,-x+offset); + glVertex3f (-ssize+view_xyz[0],-ssize+view_xyz[1],z); + glTexCoord2f (-x+offset,x+offset); + glVertex3f (-ssize+view_xyz[0],ssize+view_xyz[1],z); + glTexCoord2f (x+offset,x+offset); + glVertex3f (ssize+view_xyz[0],ssize+view_xyz[1],z); + glTexCoord2f (x+offset,-x+offset); + glVertex3f (ssize+view_xyz[0],-ssize+view_xyz[1],z); + glEnd(); + + offset = offset + 0.002f; + if (offset > 1) offset -= 1; + + glDepthFunc (GL_LESS); + glDepthRange (0,1); +} + + +static void drawGround() +{ + glDisable (GL_LIGHTING); + glShadeModel (GL_FLAT); + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + // glDepthRange (1,1); + + if (use_textures) { + glEnable (GL_TEXTURE_2D); + ground_texture->bind (0); + } + else { + glDisable (GL_TEXTURE_2D); + glColor3f (GROUND_R,GROUND_G,GROUND_B); + } + + // ground fog seems to cause problems with TNT2 under windows + /* + GLfloat fogColor[4] = {0.5, 0.5, 0.5, 1}; + glEnable (GL_FOG); + glFogi (GL_FOG_MODE, GL_EXP2); + glFogfv (GL_FOG_COLOR, fogColor); + glFogf (GL_FOG_DENSITY, 0.05f); + glHint (GL_FOG_HINT, GL_NICEST); // GL_DONT_CARE); + glFogf (GL_FOG_START, 1.0); + glFogf (GL_FOG_END, 5.0); + */ + + const float gsize = 100.0f; + const float offset = 0; // -0.001f; ... polygon offsetting doesn't work well + + glBegin (GL_QUADS); + glNormal3f (0,0,1); + glTexCoord2f (-gsize*ground_scale + ground_ofsx, + -gsize*ground_scale + ground_ofsy); + glVertex3f (-gsize,-gsize,offset); + glTexCoord2f (gsize*ground_scale + ground_ofsx, + -gsize*ground_scale + ground_ofsy); + glVertex3f (gsize,-gsize,offset); + glTexCoord2f (gsize*ground_scale + ground_ofsx, + gsize*ground_scale + ground_ofsy); + glVertex3f (gsize,gsize,offset); + glTexCoord2f (-gsize*ground_scale + ground_ofsx, + gsize*ground_scale + ground_ofsy); + glVertex3f (-gsize,gsize,offset); + glEnd(); + + glDisable (GL_FOG); +} + + +static void drawPyramidGrid() +{ + // setup stuff + glEnable (GL_LIGHTING); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_FLAT); + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + + // draw the pyramid grid + for (int i=-1; i<=1; i++) { + for (int j=-1; j<=1; j++) { + glPushMatrix(); + glTranslatef ((float)i,(float)j,(float)0); + if (i==1 && j==0) setColor (1,0,0,1); + else if (i==0 && j==1) setColor (0,0,1,1); + else setColor (1,1,0,1); + const float k = 0.03f; + glBegin (GL_TRIANGLE_FAN); + glNormal3f (0,-1,1); + glVertex3f (0,0,k); + glVertex3f (-k,-k,0); + glVertex3f ( k,-k,0); + glNormal3f (1,0,1); + glVertex3f ( k, k,0); + glNormal3f (0,1,1); + glVertex3f (-k, k,0); + glNormal3f (-1,0,1); + glVertex3f (-k,-k,0); + glEnd(); + glPopMatrix(); + } + } +} + + +void dsDrawFrame (int width, int height, dsFunctions *fn, int pause) +{ + if (current_state < 1) dsDebug ("internal error"); + current_state = 2; + + // setup stuff + glEnable (GL_LIGHTING); + glEnable (GL_LIGHT0); + glDisable (GL_TEXTURE_2D); + glDisable (GL_TEXTURE_GEN_S); + glDisable (GL_TEXTURE_GEN_T); + glShadeModel (GL_FLAT); + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + glEnable (GL_CULL_FACE); + glCullFace (GL_BACK); + glFrontFace (GL_CCW); + + // setup viewport + glViewport (0,0,width,height); + glMatrixMode (GL_PROJECTION); + glLoadIdentity(); + const float vnear = 0.1f; + const float vfar = 100.0f; + const float k = 0.8f; // view scale, 1 = +/- 45 degrees + if (width >= height) { + float k2 = float(height)/float(width); + glFrustum (-vnear*k,vnear*k,-vnear*k*k2,vnear*k*k2,vnear,vfar); + } + else { + float k2 = float(width)/float(height); + glFrustum (-vnear*k*k2,vnear*k*k2,-vnear*k,vnear*k,vnear,vfar); + } + + // setup lights. it makes a difference whether this is done in the + // GL_PROJECTION matrix mode (lights are scene relative) or the + // GL_MODELVIEW matrix mode (lights are camera relative, bad!). + static GLfloat light_ambient[] = { 0.5, 0.5, 0.5, 1.0 }; + static GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; + static GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; + glLightfv (GL_LIGHT0, GL_AMBIENT, light_ambient); + glLightfv (GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv (GL_LIGHT0, GL_SPECULAR, light_specular); + glColor3f (1.0, 1.0, 1.0); + + // clear the window + glClearColor (0.5,0.5,0.5,0); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // snapshot camera position (in MS Windows it is changed by the GUI thread) + float view2_xyz[3]; + float view2_hpr[3]; + memcpy (view2_xyz,view_xyz,sizeof(float)*3); + memcpy (view2_hpr,view_hpr,sizeof(float)*3); + + // go to GL_MODELVIEW matrix mode and set the camera + glMatrixMode (GL_MODELVIEW); + glLoadIdentity(); + setCamera (view2_xyz[0],view2_xyz[1],view2_xyz[2], + view2_hpr[0],view2_hpr[1],view2_hpr[2]); + + // set the light position (for some reason we have to do this in model view. + static GLfloat light_position[] = { LIGHTX, LIGHTY, 1.0, 0.0 }; + glLightfv (GL_LIGHT0, GL_POSITION, light_position); + + // draw the background (ground, sky etc) + drawSky (view2_xyz); + drawGround(); + + // draw the little markers on the ground + drawPyramidGrid(); + + // leave openGL in a known state - flat shaded white, no textures + glEnable (GL_LIGHTING); + glDisable (GL_TEXTURE_2D); + glShadeModel (GL_FLAT); + glEnable (GL_DEPTH_TEST); + glDepthFunc (GL_LESS); + glColor3f (1,1,1); + setColor (1,1,1,1); + + // draw the rest of the objects. set drawing state first. + color[0] = 1; + color[1] = 1; + color[2] = 1; + color[3] = 1; + tnum = 0; + if (fn->step) fn->step (pause); +} + + +int dsGetShadows() +{ + return use_shadows; +} + + +void dsSetShadows (int a) +{ + use_shadows = (a != 0); +} + + +int dsGetTextures() +{ + return use_textures; +} + + +void dsSetTextures (int a) +{ + use_textures = (a != 0); +} + +//*************************************************************************** +// C interface + +// sets lighting and texture modes, sets current color +static void setupDrawingMode() +{ + glEnable (GL_LIGHTING); + if (tnum) { + if (use_textures) { + glEnable (GL_TEXTURE_2D); + texture[tnum]->bind (1); + glEnable (GL_TEXTURE_GEN_S); + glEnable (GL_TEXTURE_GEN_T); + glTexGeni (GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + glTexGeni (GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); + static GLfloat s_params[4] = {1.0f,1.0f,0.0f,1}; + static GLfloat t_params[4] = {0.817f,-0.817f,0.817f,1}; + glTexGenfv (GL_S,GL_OBJECT_PLANE,s_params); + glTexGenfv (GL_T,GL_OBJECT_PLANE,t_params); + } + else { + glDisable (GL_TEXTURE_2D); + } + } + else { + glDisable (GL_TEXTURE_2D); + } + setColor (color[0],color[1],color[2],color[3]); + + if (color[3] < 1) { + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + } + else { + glDisable (GL_BLEND); + } +} + + +static void setShadowDrawingMode() +{ + glDisable (GL_LIGHTING); + if (use_textures) { + glEnable (GL_TEXTURE_2D); + ground_texture->bind (1); + glColor3f (SHADOW_INTENSITY,SHADOW_INTENSITY,SHADOW_INTENSITY); + glEnable (GL_TEXTURE_2D); + glEnable (GL_TEXTURE_GEN_S); + glEnable (GL_TEXTURE_GEN_T); + glTexGeni (GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); + glTexGeni (GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); + static GLfloat s_params[4] = {ground_scale,0,0,ground_ofsx}; + static GLfloat t_params[4] = {0,ground_scale,0,ground_ofsy}; + glTexGenfv (GL_S,GL_EYE_PLANE,s_params); + glTexGenfv (GL_T,GL_EYE_PLANE,t_params); + } + else { + glDisable (GL_TEXTURE_2D); + glColor3f (GROUND_R*SHADOW_INTENSITY,GROUND_G*SHADOW_INTENSITY, + GROUND_B*SHADOW_INTENSITY); + } + glDepthRange (0,0.9999); +} + + +extern "C" void dsSimulationLoop (int argc, char **argv, + int window_width, int window_height, + dsFunctions *fn) +{ + if (current_state != 0) dsError ("dsSimulationLoop() called more than once"); + current_state = 1; + + // look for flags that apply to us + int initial_pause = 0; + for (int i=1; ipath_to_textures = argv[i]; + } + + if (fn->version > DS_VERSION) + dsDebug ("bad version number in dsFunctions structure"); + + initMotionModel(); + dsPlatformSimLoop (window_width,window_height,fn,initial_pause); + + current_state = 0; +} + + +extern "C" void dsSetViewpoint (float xyz[3], float hpr[3]) +{ + if (current_state < 1) dsError ("dsSetViewpoint() called before simulation started"); + if (xyz) { + view_xyz[0] = xyz[0]; + view_xyz[1] = xyz[1]; + view_xyz[2] = xyz[2]; + } + if (hpr) { + view_hpr[0] = hpr[0]; + view_hpr[1] = hpr[1]; + view_hpr[2] = hpr[2]; + wrapCameraAngles(); + } +} + + +extern "C" void dsGetViewpoint (float xyz[3], float hpr[3]) +{ + if (current_state < 1) dsError ("dsGetViewpoint() called before simulation started"); + if (xyz) { + xyz[0] = view_xyz[0]; + xyz[1] = view_xyz[1]; + xyz[2] = view_xyz[2]; + } + if (hpr) { + hpr[0] = view_hpr[0]; + hpr[1] = view_hpr[1]; + hpr[2] = view_hpr[2]; + } +} + + +extern "C" void dsSetTexture (int texture_number) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + tnum = texture_number; +} + + +extern "C" void dsSetColor (float red, float green, float blue) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = 1; +} + + +extern "C" void dsSetColorAlpha (float red, float green, float blue, + float alpha) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + color[0] = red; + color[1] = green; + color[2] = blue; + color[3] = alpha; +} + + +extern "C" void dsDrawBox (const float pos[3], const float R[12], + const float sides[3]) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos,R); + drawBox (sides); + glPopMatrix(); + + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + setTransform (pos,R); + drawBox (sides); + glPopMatrix(); + glPopMatrix(); + glDepthRange (0,1); + } +} + +extern "C" void dsDrawConvex (const float pos[3], const float R[12], + const float *_planes,unsigned int _planecount, + const float *_points, unsigned int _pointcount, + const unsigned int *_polygons) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos,R); + drawConvex(_planes,_planecount,_points,_pointcount,_polygons); + glPopMatrix(); + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + setTransform (pos,R); + drawConvex(_planes,_planecount,_points,_pointcount,_polygons); + glPopMatrix(); + glPopMatrix(); + glDepthRange (0,1); + } +} + + +extern "C" void dsDrawSphere (const float pos[3], const float R[12], + float radius) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glEnable (GL_NORMALIZE); + glShadeModel (GL_SMOOTH); + setTransform (pos,R); + glScaled (radius,radius,radius); + drawSphere(); + glPopMatrix(); + glDisable (GL_NORMALIZE); + + // draw shadows + if (use_shadows) { + glDisable (GL_LIGHTING); + if (use_textures) { + ground_texture->bind (1); + glEnable (GL_TEXTURE_2D); + glDisable (GL_TEXTURE_GEN_S); + glDisable (GL_TEXTURE_GEN_T); + glColor3f (SHADOW_INTENSITY,SHADOW_INTENSITY,SHADOW_INTENSITY); + } + else { + glDisable (GL_TEXTURE_2D); + glColor3f (GROUND_R*SHADOW_INTENSITY,GROUND_G*SHADOW_INTENSITY, + GROUND_B*SHADOW_INTENSITY); + } + glShadeModel (GL_FLAT); + glDepthRange (0,0.9999); + drawSphereShadow (pos[0],pos[1],pos[2],radius); + glDepthRange (0,1); + } +} + + +extern "C" void dsDrawTriangle (const float pos[3], const float R[12], + const float *v0, const float *v1, + const float *v2, int solid) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos,R); + drawTriangle (v0, v1, v2, solid); + glPopMatrix(); +} + + +extern "C" void dsDrawTriangles (const float pos[3], const float R[12], + const float *v, int n, int solid) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos,R); + int i; + for (i = 0; i < n; ++i, v += 9) + drawTriangle (v, v + 3, v + 6, solid); + glPopMatrix(); +} + + +extern "C" void dsDrawCylinder (const float pos[3], const float R[12], + float length, float radius) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_SMOOTH); + setTransform (pos,R); + drawCylinder (length,radius,0); + glPopMatrix(); + + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + setTransform (pos,R); + drawCylinder (length,radius,0); + glPopMatrix(); + glPopMatrix(); + glDepthRange (0,1); + } +} + + +extern "C" void dsDrawCapsule (const float pos[3], const float R[12], + float length, float radius) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_SMOOTH); + setTransform (pos,R); + drawCapsule (length,radius); + glPopMatrix(); + + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + setTransform (pos,R); + drawCapsule (length,radius); + glPopMatrix(); + glPopMatrix(); + glDepthRange (0,1); + } +} + + +static void drawLine(const float pos1[3], const float pos2[3]) +{ + glDisable (GL_LIGHTING); + glLineWidth (2); + glShadeModel (GL_FLAT); + glBegin (GL_LINES); + glVertex3f (pos1[0],pos1[1],pos1[2]); + glVertex3f (pos2[0],pos2[1],pos2[2]); + glEnd(); +} + + +extern "C" void dsDrawLine (const float pos1[3], const float pos2[3]) +{ + setupDrawingMode(); + glColor4f(color[0], color[1], color[2], color[3]); + drawLine(pos1, pos2); + + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + + drawLine(pos1, pos2); + + glPopMatrix(); + glDepthRange (0,1); + } +} + + +extern "C" void dsDrawBoxD (const double pos[3], const double R[12], + const double sides[3]) +{ + int i; + float pos2[3],R2[12],fsides[3]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + for (i=0; i<3; i++) fsides[i]=(float)sides[i]; + dsDrawBox (pos2,R2,fsides); +} + +extern "C" void dsDrawConvexD (const double pos[3], const double R[12], + const double *_planes, unsigned int _planecount, + const double *_points, unsigned int _pointcount, + const unsigned int *_polygons) +{ + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransformD (pos,R); + drawConvexD(_planes,_planecount,_points,_pointcount,_polygons); + glPopMatrix(); + if (use_shadows) { + setShadowDrawingMode(); + setShadowTransform(); + setTransformD (pos,R); + drawConvexD(_planes,_planecount,_points,_pointcount,_polygons); + glPopMatrix(); + glPopMatrix(); + glDepthRange (0,1); + } +} + +void dsDrawSphereD (const double pos[3], const double R[12], float radius) +{ + int i; + float pos2[3],R2[12]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + dsDrawSphere (pos2,R2,radius); +} + + +void dsDrawTriangleD (const double pos[3], const double R[12], + const double *v0, const double *v1, + const double *v2, int solid) +{ + int i; + float pos2[3],R2[12]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos2,R2); + drawTriangleD (v0, v1, v2, solid); + glPopMatrix(); +} + + +extern "C" void dsDrawTrianglesD (const double pos[3], const double R[12], + const double *v, int n, int solid) +{ + int i; + float pos2[3],R2[12]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + + if (current_state != 2) dsError ("drawing function called outside simulation loop"); + setupDrawingMode(); + glShadeModel (GL_FLAT); + setTransform (pos2,R2); + for (i = 0; i < n; ++i, v += 9) + drawTriangleD (v, v + 3, v + 6, solid); + glPopMatrix(); +} + + +void dsDrawCylinderD (const double pos[3], const double R[12], + float length, float radius) +{ + int i; + float pos2[3],R2[12]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + dsDrawCylinder (pos2,R2,length,radius); +} + + +void dsDrawCapsuleD (const double pos[3], const double R[12], + float length, float radius) +{ + int i; + float pos2[3],R2[12]; + for (i=0; i<3; i++) pos2[i]=(float)pos[i]; + for (i=0; i<12; i++) R2[i]=(float)R[i]; + dsDrawCapsule (pos2,R2,length,radius); +} + + +void dsDrawLineD (const double _pos1[3], const double _pos2[3]) +{ + int i; + float pos1[3],pos2[3]; + for (i=0; i<3; i++) pos1[i]=(float)_pos1[i]; + for (i=0; i<3; i++) pos2[i]=(float)_pos2[i]; + dsDrawLine (pos1,pos2); +} + + +void dsSetSphereQuality (int n) +{ + sphere_quality = n; +} + + +void dsSetCapsuleQuality (int n) +{ + capped_cylinder_quality = n; +} + +void dsSetDrawMode(int mode) +{ + switch(mode) + { + case DS_POLYFILL: + glPolygonMode(GL_FRONT,GL_FILL); + break; + case DS_WIREFRAME: + glPolygonMode(GL_FRONT,GL_LINE); + break; + } +} diff --git a/thirdparty/ode-0.16.5/drawstuff/src/internal.h b/thirdparty/ode-0.16.5/drawstuff/src/internal.h new file mode 100644 index 0000000..de1aa11 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/internal.h @@ -0,0 +1,50 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* functions supplied and used by the platform specific code */ + +#ifndef __DS_INTERNAL_H +#define __DS_INTERNAL_H + +#include "drawstuff/drawstuff.h" + + +// supplied by platform specific code + +void dsPlatformSimLoop (int window_width, int window_height, + dsFunctions *fn, int initial_pause); + + +// used by platform specific code + +void dsStartGraphics (int width, int height, dsFunctions *fn); +void dsDrawFrame (int width, int height, dsFunctions *fn, int pause); +void dsStopGraphics(); +void dsMotion (int mode, int deltax, int deltay); + +int dsGetShadows(); +void dsSetShadows (int a); + +int dsGetTextures(); +void dsSetTextures (int a); + +#endif diff --git a/thirdparty/ode-0.16.5/drawstuff/src/osx.cpp b/thirdparty/ode-0.16.5/drawstuff/src/osx.cpp new file mode 100644 index 0000000..bc69bfe --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/osx.cpp @@ -0,0 +1,347 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// Platform-specific code for Mac OS X using Carbon+AGL +// +// Created using x11.cpp and the window-initialization -routines from GLFW +// as reference. +// Not thoroughly tested and is certain to contain deficiencies and bugs + +#include +#include +#include +#include +#include "config.h" +#include "common.h" + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#include +#include +#include "internal.h" + +#include +#include + +// Global variables + +static bool paused = false; // 1 if in `pause' mode +static bool singlestep = false; // 1 if single step key pressed +static bool writeframes = false; // 1 if frame files to be written + +static int windowWidth = -1; +static int windowHeight = -1; +static int mouseButtonMode = 0; +static bool mouseWithOption = false; // Set if dragging the mouse with alt pressed +static bool mouseWithControl = false; // Set if dragging the mouse with ctrl pressed + +static dsFunctions* functions = NULL; +static int windowReference; +static int frame = 1; +static int prev_x = 0; +static int prev_y = 0; + +//*************************************************************************** +// error handling for unix + +static void printMessage (const char *msg1, const char *msg2, va_list ap) +{ + fflush (stderr); + fflush (stdout); + fprintf (stderr,"\n%s: ",msg1); + vfprintf (stderr,msg2,ap); + fprintf (stderr,"\n"); + fflush (stderr); +} + +extern "C" void dsError (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + printMessage ("Error",msg,ap); + va_end (ap); + exit (1); +} + + +extern "C" void dsDebug (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + printMessage ("INTERNAL ERROR",msg,ap); + va_end (ap); + // *((char *)0) = 0; ... commit SEGVicide ? + abort(); +} + +extern "C" void dsPrint (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + vprintf (msg,ap); + va_end (ap); +} + +static void captureFrame( int num ){ + + fprintf( stderr,"\rcapturing frame %04d", num ); + unsigned char buffer[windowWidth*windowHeight][3]; + glReadPixels( 0, 0, windowWidth, windowHeight, GL_RGB, GL_UNSIGNED_BYTE, &buffer ); + char s[100]; + sprintf (s,"frame%04d.ppm",num); + FILE *f = fopen (s,"wb"); + if( !f ){ + dsError( "can't open \"%s\" for writing", s ); + } + fprintf( f,"P6\n%d %d\n255\n", windowWidth, windowHeight ); + for( int y=windowHeight-1; y>-1; y-- ){ + fwrite( buffer[y*windowWidth], 3*windowWidth, 1, f ); + } + fclose (f); +} + +extern "C" void dsStop() +{ +} + +extern "C" double dsElapsedTime() +{ +#if HAVE_GETTIMEOFDAY + static double prev=0.0; + timeval tv ; + + gettimeofday(&tv, 0); + double curr = tv.tv_sec + (double) tv.tv_usec / 1000000.0 ; + if (!prev) + prev=curr; + double retval = curr-prev; + prev=curr; + if (retval>1.0) retval=1.0; + if (retval= 'a' && key <= 'z') + uppercase = key - ('a' - 'A'); + else + uppercase = key; + + int modifierMask = osxGetModifierMask(); + if (modifierMask == 0) + { + if( key >= ' ' && key <= 126 && functions -> command ) + functions -> command( key ); + } + else if (modifierMask & GLUT_ACTIVE_CTRL) + { + // ctrl+key was pressed + uppercase += 'A' - 1; + switch(uppercase ){ + case 'T': + dsSetTextures( !dsGetTextures() ); + break; + case 'S': + dsSetShadows( !dsGetShadows() ); + break; + case 'X': + exit(0); + break; + case 'P': + paused = !paused; + singlestep = false; + break; + case 'O': + if( paused ){ + singlestep = true; + } + break; + case 'V': { + float xyz[3],hpr[3]; + dsGetViewpoint( xyz,hpr ); + printf( "Viewpoint = (%.4f,%.4f,%.4f,%.4f,%.4f,%.4f)\n", xyz[0], xyz[1], xyz[2], hpr[0], hpr[1], hpr[2] ); + break; + } + case 'W': + writeframes = !writeframes; + if( writeframes ){ + printf( "Now writing frames to PPM files\n" ); + } + break; + } + } +} + +void osxMouseEventHandler(int button, int state, int x, int y) +{ + prev_x = x; + prev_y = y; + bool buttonDown = false; + switch( state ){ + case GLUT_DOWN: + buttonDown = true; + case GLUT_UP: + if( button == GLUT_LEFT_BUTTON ){ + int modifierMask = osxGetModifierMask(); + if( modifierMask & GLUT_ACTIVE_CTRL ){ + // Ctrl+button == right + button = GLUT_RIGHT_BUTTON; + mouseWithControl = true; + } + else if( modifierMask & GLUT_ACTIVE_ALT ){ + // Alt+button == left+right + mouseButtonMode = 5; + mouseWithOption = true; + return; + } + } + if( buttonDown ){ + if( button == GLUT_LEFT_BUTTON ) mouseButtonMode |= 1; // Left + if( button == GLUT_MIDDLE_BUTTON ) mouseButtonMode |= 2; // Middle + if( button == GLUT_RIGHT_BUTTON ) mouseButtonMode |= 4; // Right + } + else{ + if( button == GLUT_LEFT_BUTTON ) mouseButtonMode &= (~1); // Left + if( button == GLUT_MIDDLE_BUTTON ) mouseButtonMode &= (~2); // Middle + if( button == GLUT_RIGHT_BUTTON ) mouseButtonMode &= (~4); // Right + } + return; + } +} + +void osxMotionEventHandler(int x, int y) +{ + dsMotion( mouseButtonMode, x - prev_x, y - prev_y ); + prev_x = x; + prev_y = y; +} + +void osxWindowReshapeEventHandler(int width, int height) +{ + windowWidth = width; + windowHeight = height; +} + +static void osxCreateMainWindow( int width, int height ) +{ + int argc = 1; + char* argv[2]; + argv[0] = (char*)""; + argv[1] = NULL; + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); + glutInitWindowSize(width, height); + + windowReference = glutCreateWindow("ODE - Drawstuff"); + windowWidth = width; + windowHeight = height; +} + +void osxRedisplayEventHandler() +{ + dsDrawFrame( windowWidth, windowHeight, functions, paused && !singlestep ); + singlestep = false; + glutSwapBuffers(); + + // capture frames if necessary + if( !paused && writeframes ){ + captureFrame( frame ); + frame++; + } +} + +void osxTimerEventHandler(int); + +void osxInstallTimerHandler() +{ + glutTimerFunc(1000/60, osxTimerEventHandler, 0); +} + +void osxTimerEventHandler(int) +{ + glutPostRedisplay(); + osxInstallTimerHandler(); +} + +int osxInstallEventHandlers() +{ + glutKeyboardFunc(osxKeyEventHandler); + glutMouseFunc(osxMouseEventHandler); + glutMotionFunc(osxMotionEventHandler); + glutDisplayFunc(osxRedisplayEventHandler); + glutReshapeFunc(osxWindowReshapeEventHandler); + osxInstallTimerHandler(); + return GL_TRUE; +} + +extern void dsPlatformSimLoop( int givenWindowWidth, int givenWindowHeight, dsFunctions *fn, int givenPause ){ + + functions = fn; + + paused = givenPause; + + osxCreateMainWindow( givenWindowWidth, givenWindowHeight ); + osxInstallEventHandlers(); + + dsStartGraphics( windowWidth, windowHeight, fn ); + + static bool firsttime=true; + if( firsttime ) + { + fprintf + ( + stderr, + "\n" + "Simulation test environment v%d.%02d\n" + " Ctrl-P : pause / unpause (or say `-pause' on command line).\n" + " Ctrl-O : single step when paused.\n" + " Ctrl-T : toggle textures (or say `-notex' on command line).\n" + " Ctrl-S : toggle shadows (or say `-noshadow' on command line).\n" + " Ctrl-V : print current viewpoint coordinates (x,y,z,h,p,r).\n" + " Ctrl-W : write frames to ppm files: frame/frameNNN.ppm\n" + " Ctrl-X : exit.\n" + "\n" + "Change the camera position by clicking + dragging in the window.\n" + " Left button - pan and tilt.\n" + " Right button (or Ctrl + button) - forward and sideways.\n" + " Left + Right button (or middle button, or Alt + button) - sideways and up.\n" + "\n",DS_VERSION >> 8,DS_VERSION & 0xff + ); + firsttime = false; + } + + if( fn -> start ) fn->start(); + + glutMainLoop(); +} diff --git a/thirdparty/ode-0.16.5/drawstuff/src/resource.h b/thirdparty/ode-0.16.5/drawstuff/src/resource.h new file mode 100644 index 0000000..15802b6 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/resource.h @@ -0,0 +1,28 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by resources.rc +// +#define IDD_MSGDLG 101 +#define IDR_MENU1 102 +#define IDD_ABOUT 103 +#define IDR_ACCELERATOR1 104 +#define IDC_LIST1 1000 +#define IDM_EXIT 40001 +#define IDM_ABOUT 40002 +#define IDM_PAUSE 40003 +#define IDM_PERF_MONITOR 40004 +#define IDM_SHADOWS 40005 +#define IDM_TEXTURES 40006 +#define IDM_SAVE_SETTINGS 40007 +#define IDM_SINGLE_STEP 40008 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40009 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/thirdparty/ode-0.16.5/drawstuff/src/resources.rc b/thirdparty/ode-0.16.5/drawstuff/src/resources.rc new file mode 100644 index 0000000..61611f7 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/resources.rc @@ -0,0 +1,153 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +//#include "afxres.h" + +// added by RLS to make this work with windres +#include "winresrc.h" +#define IDC_STATIC (-1) + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 257, 105 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,200,84,50,14 + LTEXT "Simulation test environment",IDC_STATIC,7,7,243,8 + LTEXT "Change the camera position by clicking + dragging in the main window.", + IDC_STATIC,7,24,243,8 + LTEXT "Left button - pan and tilt.",IDC_STATIC,25,37,225,8 + LTEXT "Right button - forward and sideways.",IDC_STATIC,25,48, + 225,8 + LTEXT "Left + Right button (or middle button) - sideways and up.", + IDC_STATIC,25,59,225,8 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + //"#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MENU1 MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Exit\tCtrl+X", IDM_EXIT + END + POPUP "&Simulation" + BEGIN + MENUITEM "&Pause\tCtrl+P", IDM_PAUSE + MENUITEM "Single Step\tCtrl+O", IDM_SINGLE_STEP + MENUITEM "Performance &Monitor", IDM_PERF_MONITOR, GRAYED + MENUITEM SEPARATOR + MENUITEM "&Shadows\tCtrl+S", IDM_SHADOWS, CHECKED + MENUITEM "&Textures\tCtrl+T", IDM_TEXTURES, CHECKED + MENUITEM SEPARATOR + MENUITEM "S&ave Settings", IDM_SAVE_SETTINGS, GRAYED + END + POPUP "&Help" + BEGIN + MENUITEM "&About", IDM_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 250 + VERTGUIDE, 25 + TOPMARGIN, 7 + BOTTOMMARGIN, 98 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE +BEGIN + "O", IDM_SINGLE_STEP, VIRTKEY, CONTROL, NOINVERT + "P", IDM_PAUSE, VIRTKEY, CONTROL, NOINVERT + "S", IDM_SHADOWS, VIRTKEY, CONTROL, NOINVERT + "T", IDM_TEXTURES, VIRTKEY, CONTROL, NOINVERT + "X", IDM_EXIT, VIRTKEY, CONTROL, NOINVERT +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/thirdparty/ode-0.16.5/drawstuff/src/windows.cpp b/thirdparty/ode-0.16.5/drawstuff/src/windows.cpp new file mode 100644 index 0000000..b136ddc --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/src/windows.cpp @@ -0,0 +1,536 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#if defined(WIN32) || defined(__CYGWIN__)// this prevents warnings when dependencies built +#include +#endif +#include +#include +#include + +#include "config.h" +#include "common.h" +#include "resource.h" +#include "internal.h" + +//*************************************************************************** +// application globals + +static HINSTANCE ghInstance = 0; +static int gnCmdShow = 0; +static HACCEL accelerators = 0; +static HWND main_window = 0; + +//*************************************************************************** +// error and message handling + +static void errorBox (const char *title, const char *msg, va_list ap) +{ + char s[1000]; + vsprintf (s,msg,ap); + MessageBox (0,s,title,MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION); +} + + +static void dsWarning (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + errorBox ("Warning",msg,ap); + va_end (ap); +} + + +extern "C" void dsError (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + errorBox ("Error",msg,ap); + va_end (ap); + exit (1); +} + + +extern "C" void dsDebug (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + errorBox ("INTERNAL ERROR",msg,ap); + va_end (ap); + // *((char *)0) = 0; ... commit SEGVicide ? + abort(); + exit (1); // should never get here, but just in case... +} + + +extern "C" void dsPrint (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + vprintf (msg,ap); + va_end (ap); +} + +//*************************************************************************** +// rendering thread + +// globals used to communicate with rendering thread + +static volatile int renderer_run = 1; +static volatile int renderer_pause = 0; // 0=run, 1=pause +static volatile int renderer_ss = 0; // single step command +static volatile int renderer_width = 1; +static volatile int renderer_height = 1; +static dsFunctions *renderer_fn = 0; +static volatile HDC renderer_dc = 0; +static volatile int keybuffer[16]; // fifo ring buffer for keypresses +static volatile int keybuffer_head = 0; // index of next key to put in (modified by GUI) +static volatile int keybuffer_tail = 0; // index of next key to take out (modified by renderer) + + +static void setupRendererGlobals() +{ + renderer_run = 1; + renderer_pause = 0; + renderer_ss = 0; + renderer_width = 1; + renderer_height = 1; + renderer_fn = 0; + renderer_dc = 0; + keybuffer[16]; + keybuffer_head = 0; + keybuffer_tail = 0; +} + + +static unsigned CALLBACK renderingThread (LPVOID lpParam) +{ + // create openGL context and make it current + HGLRC glc = wglCreateContext (renderer_dc); + if (glc==NULL) dsError ("could not create OpenGL context"); + if (wglMakeCurrent (renderer_dc,glc) != TRUE) + dsError ("could not make OpenGL context current"); + + // test openGL capabilities + int maxtsize=0; + glGetIntegerv (GL_MAX_TEXTURE_SIZE,&maxtsize); + if (maxtsize < 128) dsWarning ("max texture size too small (%dx%d)", + maxtsize,maxtsize); + + dsStartGraphics (renderer_width,renderer_height,renderer_fn); + if (renderer_fn->start) renderer_fn->start(); + + while (renderer_run) { + // need to make local copy of renderer_ss to help prevent races + int ss = renderer_ss; + dsDrawFrame (renderer_width,renderer_height,renderer_fn, + renderer_pause && !ss); + if (ss) renderer_ss = 0; + + // read keys out of ring buffer and feed them to the command function + while (keybuffer_head != keybuffer_tail) { + if (renderer_fn->command) renderer_fn->command (keybuffer[keybuffer_tail]); + keybuffer_tail = (keybuffer_tail+1) & 15; + } + + // swap buffers + SwapBuffers (renderer_dc); + } + + if (renderer_fn->stop) renderer_fn->stop(); + dsStopGraphics(); + + // delete openGL context + wglMakeCurrent (NULL,NULL); + wglDeleteContext (glc); + + return 123; // magic value used to test for thread termination +} + +//*************************************************************************** +// window handling + +// callback function for "about" dialog box + +static LRESULT CALLBACK AboutDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, + LPARAM lParam) +{ + switch (uMsg) { + case WM_INITDIALOG: + return TRUE; + case WM_COMMAND: + switch (wParam) { + case IDOK: + EndDialog (hDlg, TRUE); + return TRUE; + } + break; + } + return FALSE; +} + + +// callback function for the main window + +static LRESULT CALLBACK mainWndProc (HWND hWnd, UINT msg, WPARAM wParam, + LPARAM lParam) +{ + static int button=0,lastx=0,lasty=0; + int ctrl = int(wParam & MK_CONTROL); + + switch (msg) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + if (msg==WM_LBUTTONDOWN) button |= 1; + else if (msg==WM_MBUTTONDOWN) button |= 2; + else button |= 4; + lastx = SHORT(LOWORD(lParam)); + lasty = SHORT(HIWORD(lParam)); + SetCapture (hWnd); + break; + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + if (msg==WM_LBUTTONUP) button &= ~1; + else if (msg==WM_MBUTTONUP) button &= ~2; + else button &= ~4; + if (button==0) ReleaseCapture(); + break; + + case WM_MOUSEMOVE: { + int x = SHORT(LOWORD(lParam)); + int y = SHORT(HIWORD(lParam)); + if (button) dsMotion (button,x-lastx,y-lasty); + lastx = x; + lasty = y; + break; + } + + case WM_CHAR: { + if (wParam >= ' ' && wParam <= 126) { + int nexth = (keybuffer_head+1) & 15; + if (nexth != keybuffer_tail) { + keybuffer[keybuffer_head] = int(wParam); + keybuffer_head = nexth; + } + } + break; + } + + case WM_SIZE: + // lParam will contain the size of the *client* area! + renderer_width = LOWORD(lParam); + renderer_height = HIWORD(lParam); + break; + + case WM_COMMAND: + switch (wParam & 0xffff) { + case IDM_ABOUT: + DialogBox (ghInstance,MAKEINTRESOURCE(IDD_ABOUT),hWnd, + (DLGPROC) AboutDlgProc); + break; + case IDM_PAUSE: { + renderer_pause ^= 1; + CheckMenuItem (GetMenu(hWnd),IDM_PAUSE, + renderer_pause ? MF_CHECKED : MF_UNCHECKED); + if (renderer_pause) renderer_ss = 0; + break; + } + case IDM_SINGLE_STEP: { + if (renderer_pause) + renderer_ss = 1; + else + SendMessage( hWnd, WM_COMMAND, IDM_PAUSE, 0 ); + break; + } + case IDM_PERF_MONITOR: { + dsWarning ("Performance monitor not yet implemented."); + break; + } + case IDM_TEXTURES: { + static int tex = 1; + tex ^= 1; + CheckMenuItem (GetMenu(hWnd),IDM_TEXTURES, + tex ? MF_CHECKED : MF_UNCHECKED); + dsSetTextures (tex); + break; + } + case IDM_SHADOWS: { + static int shadows = 1; + shadows ^= 1; + CheckMenuItem (GetMenu(hWnd),IDM_SHADOWS, + shadows ? MF_CHECKED : MF_UNCHECKED); + dsSetShadows (shadows); + break; + } + case IDM_SAVE_SETTINGS: { + dsWarning ("\"Save Settings\" not yet implemented."); + break; + } + case IDM_EXIT: + PostQuitMessage (0); + break; + } + break; + + case WM_CLOSE: + PostQuitMessage (0); + break; + + default: + return (DefWindowProc (hWnd, msg, wParam, lParam)); + } + + return 0; +} + + +// this comes from an MSDN example. believe it or not, this is the recommended +// way to get the console window handle. + +static HWND GetConsoleHwnd() +{ + // the console window title to a "unique" value, then find the window + // that has this title. + char title[1024]; + wsprintf (title,"DrawStuff:%d/%d",GetTickCount(),GetCurrentProcessId()); + SetConsoleTitle (title); + Sleep(40); // ensure window title has been updated + return FindWindow (NULL,title); +} + + +static void drawStuffStartup() +{ + static int startup_called = 0; + if (startup_called) return; + startup_called = 1; + if (!ghInstance) + ghInstance = GetModuleHandleA (NULL); + gnCmdShow = SW_SHOWNORMAL; // @@@ fix this later + + // redirect standard I/O to a new console (except on cygwin and mingw) +#if !defined(__CYGWIN__) && !defined(__MINGW32__) + FreeConsole(); + if (AllocConsole()==0) dsError ("AllocConsole() failed"); + if (freopen ("CONIN$","rt",stdin)==0) dsError ("could not open stdin"); + if (freopen ("CONOUT$","wt",stdout)==0) dsError ("could not open stdout"); + if (freopen ("CONOUT$","wt",stderr)==0) dsError ("could not open stderr"); + BringWindowToTop (GetConsoleHwnd()); + SetConsoleTitle ("DrawStuff Messages"); +#endif + + // register the window class + WNDCLASS wc; + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + wc.lpfnWndProc = mainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = ghInstance; + wc.hIcon = LoadIcon (NULL,IDI_APPLICATION); + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1); + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1); + wc.lpszClassName = "SimAppClass"; + if (RegisterClass (&wc)==0) dsError ("could not register window class"); + + // load accelerators + accelerators = LoadAccelerators (ghInstance, + MAKEINTRESOURCE(IDR_ACCELERATOR1)); + if (accelerators==NULL) dsError ("could not load accelerators"); +} + + +void dsPlatformSimLoop (int window_width, int window_height, + dsFunctions *fn, int initial_pause) +{ + drawStuffStartup(); + setupRendererGlobals(); + renderer_pause = initial_pause; + + // create window - but first get window size for desired size of client area. + // if this adjustment isn't made then the openGL area will be shifted into + // the nonclient area and determining the frame buffer coordinate from the + // client area coordinate will be hard. + RECT winrect; + winrect.left = 50; + winrect.top = 80; + winrect.right = winrect.left + window_width; + winrect.bottom = winrect.top + window_height; + DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + AdjustWindowRect (&winrect,style,1); + char title[100]; + sprintf (title,"Simulation test environment v%d.%02d", + DS_VERSION >> 8,DS_VERSION & 0xff); + main_window = CreateWindow ("SimAppClass",title,style, + winrect.left,winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top, + NULL,NULL,ghInstance,NULL); + if (main_window==NULL) dsError ("could not create main window"); + ShowWindow (main_window, gnCmdShow); + + HDC dc = GetDC (main_window); // get DC for this window + if (dc==NULL) dsError ("could not get window DC"); + + // set pixel format for DC + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW | // support window + PFD_SUPPORT_OPENGL | // support OpenGL + PFD_DOUBLEBUFFER, // double buffered + PFD_TYPE_RGBA, // RGBA type + 24, // 24-bit color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + 32, // 32-bit z-buffer + 0, // no stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + // get the best available match of pixel format for the device context + int iPixelFormat = ChoosePixelFormat (dc,&pfd); + if (iPixelFormat==0) + dsError ("could not find a good OpenGL pixel format"); + // set the pixel format of the device context + if (SetPixelFormat (dc,iPixelFormat,&pfd)==FALSE) + dsError ("could not set DC pixel format for OpenGL"); + + // ********** + // start the rendering thread + + // set renderer globals + renderer_dc = dc; + renderer_width = window_width; + renderer_height = window_height; + renderer_fn = fn; + + unsigned threadId; + HANDLE hThread; + + hThread = (HANDLE)_beginthreadex( + NULL, // no security attributes + 0, // use default stack size + &renderingThread, // thread function + NULL, // argument to thread function + 0, // use default creation flags + &threadId); // returns the thread identifier + + if (hThread==NULL) dsError ("Could not create rendering thread"); + + // ********** + // start GUI message processing + + MSG msg; + while (GetMessage (&msg,main_window,0,0)) { + if (!TranslateAccelerator (main_window,accelerators,&msg)) { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + + // terminate rendering thread + renderer_run = 0; + DWORD ret = WaitForSingleObject (hThread,2000); + if (ret==WAIT_TIMEOUT) dsWarning ("Could not kill rendering thread (1)"); + DWORD exitcode=0; + if (!(GetExitCodeThread (hThread,&exitcode) && exitcode == 123)) + dsWarning ("Could not kill rendering thread (2)"); + CloseHandle (hThread); // dont need thread handle anymore + + // destroy window + DestroyWindow (main_window); +} + + +extern "C" void dsStop() +{ + // just calling PostQuitMessage() here wont work, as this function is + // typically called from the rendering thread, not the GUI thread. + // instead we must post the message to the GUI window explicitly. + + if (main_window) PostMessage (main_window,WM_QUIT,0,0); +} + + +extern "C" double dsElapsedTime() +{ + static double prev=0.0; + double curr = timeGetTime()/1000.0; + if (!prev) + prev=curr; + double retval = curr-prev; + prev=curr; + if (retval>1.0) retval=1.0; + if (retval +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "common.h" + +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include "internal.h" + +//*************************************************************************** +// error handling for unix + +static void printMessage (const char *msg1, const char *msg2, va_list ap) +{ + fflush (stderr); + fflush (stdout); + fprintf (stderr,"\n%s: ",msg1); + vfprintf (stderr,msg2,ap); + fprintf (stderr,"\n"); + fflush (stderr); +} + + +extern "C" void dsError (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + printMessage ("Error",msg,ap); + va_end (ap); + exit (1); +} + + +extern "C" void dsDebug (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + printMessage ("INTERNAL ERROR",msg,ap); + va_end (ap); + // *((char *)0) = 0; ... commit SEGVicide ? + abort(); +} + + +extern "C" void dsPrint (const char *msg, ...) +{ + va_list ap; + va_start (ap,msg); + vprintf (msg,ap); + va_end (ap); +} + +//*************************************************************************** +// openGL window + +// X11 display info +static Display *display=0; +static int screen=0; +static XVisualInfo *visual=0; // best visual for openGL +static Colormap colormap=0; // window's colormap +static Atom wm_protocols_atom = 0; +static Atom wm_delete_window_atom = 0; + +// window and openGL +static Window win=0; // X11 window, 0 if not initialized +static int width=0,height=0; // window size +static GLXContext glx_context=0; // openGL rendering context +static int last_key_pressed=0; // last key pressed in the window +static int run=1; // 1 if simulation running +static int pausemode=0; // 1 if in `pause' mode +static int singlestep=0; // 1 if single step key pressed +static int writeframes=0; // 1 if frame files to be written + + +static void createMainWindow (int _width, int _height) +{ + // create X11 display connection + display = XOpenDisplay (NULL); + if (!display) dsError ("can not open X11 display"); + screen = DefaultScreen(display); + + // get GL visual + static int attribListDblBuf[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE,16, + GLX_RED_SIZE,4, GLX_GREEN_SIZE,4, GLX_BLUE_SIZE,4, None}; + static int attribList[] = {GLX_RGBA, GLX_DEPTH_SIZE,16, + GLX_RED_SIZE,4, GLX_GREEN_SIZE,4, GLX_BLUE_SIZE,4, None}; + visual = glXChooseVisual (display,screen,attribListDblBuf); + if (!visual) visual = glXChooseVisual (display,screen,attribList); + if (!visual) dsError ("no good X11 visual found for OpenGL"); + + // create colormap + colormap = XCreateColormap (display,RootWindow(display,screen), + visual->visual,AllocNone); + + // initialize variables + win = 0; + width = _width; + height = _height; + glx_context = 0; + last_key_pressed = 0; + + if (width < 1 || height < 1) dsDebug (0,"bad window width or height"); + + // create the window + XSetWindowAttributes attributes; + attributes.background_pixel = BlackPixel(display,screen); + attributes.colormap = colormap; + attributes.event_mask = ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | ButtonMotionMask | PointerMotionHintMask | + StructureNotifyMask; + win = XCreateWindow (display,RootWindow(display,screen),50,50,width,height, + 0,visual->depth, InputOutput,visual->visual, + CWBackPixel | CWColormap | CWEventMask,&attributes); + + // associate a GLX context with the window + glx_context = glXCreateContext (display,visual,0,GL_TRUE); + if (!glx_context) dsError ("can't make an OpenGL context"); + + // set the window title + XTextProperty window_name; + window_name.value = (unsigned char *) "Simulation"; + window_name.encoding = XA_STRING; + window_name.format = 8; + window_name.nitems = strlen((char *) window_name.value); + XSetWMName (display,win,&window_name); + + // participate in the window manager 'delete yourself' protocol + wm_protocols_atom = XInternAtom (display,"WM_PROTOCOLS",False); + wm_delete_window_atom = XInternAtom (display,"WM_DELETE_WINDOW",False); + if (XSetWMProtocols (display,win,&wm_delete_window_atom,1)==0) + dsError ("XSetWMProtocols() call failed"); + + // pop up the window + XMapWindow (display,win); + XSync (display,win); +} + + +static void destroyMainWindow() +{ + glXDestroyContext (display,glx_context); + XDestroyWindow (display,win); + XSync (display,0); + XCloseDisplay(display); + display = 0; + win = 0; + glx_context = 0; +} + + +static void handleEvent (XEvent &event, dsFunctions *fn) +{ + static int mx=0,my=0; // mouse position + static int mode = 0; // mouse button bits + + switch (event.type) { + + case ButtonPress: { + if (event.xbutton.button == Button1) mode |= 1; + if (event.xbutton.button == Button2) mode |= 2; + if (event.xbutton.button == Button3) mode |= 4; + mx = event.xbutton.x; + my = event.xbutton.y; + } + return; + + case ButtonRelease: { + if (event.xbutton.button == Button1) mode &= (~1); + if (event.xbutton.button == Button2) mode &= (~2); + if (event.xbutton.button == Button3) mode &= (~4); + mx = event.xbutton.x; + my = event.xbutton.x; + } + return; + + case MotionNotify: { + if (event.xmotion.is_hint) { + Window root,child; + unsigned int mask; + XQueryPointer (display,win,&root,&child,&event.xbutton.x_root, + &event.xbutton.y_root,&event.xbutton.x,&event.xbutton.y, + &mask); + } + dsMotion (mode, event.xmotion.x - mx, event.xmotion.y - my); + mx = event.xmotion.x; + my = event.xmotion.y; + } + return; + + case KeyPress: { + KeySym key; + XLookupString (&event.xkey,NULL,0,&key,0); + if ((event.xkey.state & ControlMask) == 0) { + if (key >= ' ' && key <= 126 && fn->command) fn->command (key); + } + else if (event.xkey.state & ControlMask) { + switch (key) { + case 't': case 'T': + dsSetTextures (dsGetTextures() ^ 1); + break; + case 's': case 'S': + dsSetShadows (dsGetShadows() ^ 1); + break; + case 'x': case 'X': + run = 0; + break; + case 'p': case 'P': + pausemode ^= 1; + singlestep = 0; + break; + case 'o': case 'O': + if (pausemode) singlestep = 1; + break; + case 'v': case 'V': { + float xyz[3],hpr[3]; + dsGetViewpoint (xyz,hpr); + printf ("Viewpoint = (%.4f,%.4f,%.4f,%.4f,%.4f,%.4f)\n", + xyz[0],xyz[1],xyz[2],hpr[0],hpr[1],hpr[2]); + break; + } + case 'w': case 'W': + writeframes ^= 1; + if (writeframes) printf ("Now writing frames to PPM files\n"); + break; + } + } + last_key_pressed = key; // a kludgy place to put this... + } + return; + + case KeyRelease: { + // hmmmm... + } + return; + + case ClientMessage: + if (event.xclient.message_type == wm_protocols_atom && + event.xclient.format == 32 && + Atom(event.xclient.data.l[0]) == wm_delete_window_atom) { + run = 0; + return; + } + return; + + case ConfigureNotify: + width = event.xconfigure.width; + height = event.xconfigure.height; + return; + } +} + + +// return the index of the highest bit +static int getHighBitIndex (unsigned int x) +{ + int i = 0; + while (x) { + i++; + x >>= 1; + } + return i-1; +} + + +// shift x left by i, where i can be positive or negative +#define SHIFTL(x,i) (((i) >= 0) ? ((x) << (i)) : ((x) >> (-i))) + + +static void captureFrame (int num) +{ + fprintf (stderr,"capturing frame %04d\n",num); + + char s[100]; + sprintf (s,"frame/frame%04d.ppm",num); + FILE *f = fopen (s,"wb"); + if (!f) dsError ("can't open \"%s\" for writing",s); + fprintf (f,"P6\n%d %d\n255\n",width,height); + XImage *image = XGetImage (display,win,0,0,width,height,~0,ZPixmap); + + int rshift = 7 - getHighBitIndex (image->red_mask); + int gshift = 7 - getHighBitIndex (image->green_mask); + int bshift = 7 - getHighBitIndex (image->blue_mask); + + for (int y=0; yred_mask,rshift); + b[1] = SHIFTL(pixel & image->green_mask,gshift); + b[2] = SHIFTL(pixel & image->blue_mask,bshift); + fwrite (b,3,1,f); + } + } + fclose (f); + XDestroyImage (image); +} + +void processDrawFrame(int *frame, dsFunctions *fn) +{ + dsDrawFrame (width,height,fn,pausemode && !singlestep); + singlestep = 0; + + glFlush(); + glXSwapBuffers (display,win); + XSync (display,0); + + // capture frames if necessary + if (pausemode==0 && writeframes) { + captureFrame (*frame); + (*frame)++; + } +} + +void microsleep(int usecs) +{ +#ifdef HAVE_UNISTD_H + usleep(usecs); +#endif +} + +void dsPlatformSimLoop (int window_width, int window_height, dsFunctions *fn, + int initial_pause) +{ + pausemode = initial_pause; + createMainWindow (window_width, window_height); + glXMakeCurrent (display,win,glx_context); + + dsStartGraphics (window_width,window_height,fn); + + static bool firsttime=true; + if (firsttime) + { + fprintf + ( + stderr, + "\n" + "Simulation test environment v%d.%02d\n" + " Ctrl-P : pause / unpause (or say `-pause' on command line).\n" + " Ctrl-O : single step when paused.\n" + " Ctrl-T : toggle textures (or say `-notex' on command line).\n" + " Ctrl-S : toggle shadows (or say `-noshadow' on command line).\n" + " Ctrl-V : print current viewpoint coordinates (x,y,z,h,p,r).\n" + " Ctrl-W : write frames to ppm files: frame/frameNNN.ppm\n" + " Ctrl-X : exit.\n" + "\n" + "Change the camera position by clicking + dragging in the window.\n" + " Left button - pan and tilt.\n" + " Right button - forward and sideways.\n" + " Left + Right button (or middle button) - sideways and up.\n" + "\n",DS_VERSION >> 8,DS_VERSION & 0xff + ); + firsttime = false; + } + + if (fn->start) fn->start(); + +#if HAVE_GETTIMEOFDAY + timeval tv; + gettimeofday(&tv, 0); + double prev = tv.tv_sec + (double) tv.tv_usec / 1000000.0 ; +#endif + + int frame = 1; + run = 1; + while (run) { + // read in and process all pending events for the main window + XEvent event; + while (run && XPending (display)) { + XNextEvent (display,&event); + handleEvent (event,fn); + } + +#if HAVE_GETTIMEOFDAY + gettimeofday(&tv, 0); + double curr = tv.tv_sec + (double) tv.tv_usec / 1000000.0 ; + if (curr-prev >= 1.0/60.0) + { + prev = curr; + processDrawFrame(&frame, fn); + } + else + microsleep(1000); +#else + processDrawFrame(&frame, fn); +#endif + }; + + if (fn->stop) fn->stop(); + dsStopGraphics(); + + destroyMainWindow(); +} + + +extern "C" void dsStop() +{ + run = 0; +} + + +extern "C" double dsElapsedTime() +{ +#if HAVE_GETTIMEOFDAY + static double prev=0.0; + timeval tv ; + + gettimeofday(&tv, 0); + double curr = tv.tv_sec + (double) tv.tv_usec / 1000000.0 ; + if (!prev) + prev=curr; + double retval = curr-prev; + prev=curr; + if (retval>1.0) retval=1.0; + if (retvalB9owulttslnv}{|||ultnv{{u{{u{{|lksmk{{}{{|z{||red{u{lri|z||{tttsl||{ttslezl|uzt{u{{u{ztl{{tllduslesle}tt{j]\f]cf]cultultlks{ttedk\[b{{llkddc]bZ{{tlld{tt}]cd{u{ttsUTS|zttsuztekdlld|ztl\[bmttlksSKJ{ttlrikk]dc\UUYsle[[TjcVmttkd\||rg]tts{||tllsleJHFddcdc\b[Uult{||\[[:97zll{{ttslztlsreUMRztltts\UZ{{t{{tttslek}|ult{{t}kk]d\\{u{vtzltslzlllrif]c|uutUTSulttzlf]c||ult{u{{u{|}ldd[TTSKJ{{t~ztl{tt{ttreddc\{tt{||uztSKJ{zmek]lrittslddvu{{UMRultD;9UTSLKRlri{u{{u{{{{{tt{lekttslri}tts]cd{{u{{{{lksmtt{{^fs|mtt{{~tt{tll{{{|||zedk||{ttSKJ{tt}|j]\tllJHF{{{{lri|zdc\tsl{||{{{{kd\ztl\UZ}|{||f]cv~f]c{{{tt||lek|u{ttuztulttts}||}}lld}{ttu{{ultlks{{tts|z\UZllk[TTddctsl]bZtzlSKJreduztu{{ldd}|{ttekdztfred[[T{{tek]redllk}[TT:97zllkk]d\\kd\lldred\[bf]clldlri{u{{||tzllddf]c|llkult{zmztl}v{kd\ttslks}mttu{{mttuzt]cdultut||[[T|{tt|{||||lri||ztlbVZ[[T||cbVlldMRKd\\ekd}SKJj]\SKJUMRult{zm|mw{{}}ult{{|]cd]cd}lkslkstt{{{\[b{{f]cmtt{zm}v{Ó{zmtts|]cdlektsltt{ztltts|ztsl{zmtslultu{{tll|v|tlltsl{{}b[Uv{}v{ldd{{fkk}UTS}{|||llk{tt{{t}tlllekv}ldd{u{||tt{|{u{cbV|lldiq]jV[}ztltslSKJutVTLlrivlriUTSlddJHFFD;llkttsSKJ:97lld{u{||rg]JHFlek[[T|zekdlddbVU|zVTLek]|zdc\|z}||tsltllut{||tt{llkekdtllu{{t[[T{{|z{{VTLtt{tlltts}tsl{tttts[TTyl{u{lddekdVTLUMRwlsre{u{kd\lksu{{\UZf]csleg\rredmttv{{||{u{}||lek{||{{{{{||}{{tt{|mttmtt^fs{{|{{{{{{|}ă}lddv{tlluzttsl{{tf]cJHFf]culttt{u||[[Tsle[[Tkd\f]c||tt{\UZ{{|\[blkslri]bZttssle{ttttszllkd\tllult}lekmttedk{{{{{{{{lld|zlldlrittscbVUTStt{|z\[[{{t\UZ{ttttssreslerfk|||u{ttfkkddc[[TllkzlmttlriztlUMR:97||ukd\ztlu{{ylekd\{tttllek]llk|{tt||{||wl}]bZlriuztldd}{{t|uuztult{{ult{||||||z{{f]czllult||SKJ{ttsleuzt||{{MRKUMRlekf]c{ttdc\tlld\\tsltllj]\VTLVTLutt{vtsl|[[T[TT\UZultzllultedkldd|z{u{lddddc{{tts{{{{yllks|\UZmtt|{{nv{{|{{nv{{{{}u{{}lriuztllklek{||f]c\[[llk|z{ttultmwlekb[Ulrimttf]csle{ttVZ[mtt]cdnv|lks]bZzlltslldd{ttultfkku{{{{lddtsl\[[lri{{ttt{d\\VTL||ttslrikd\|SKJaWM[[Tf]c[TTVTLlldlriJHFUUYsle||ldd||{ttultedkvlritsltts{{||{zmultntzlsretts|zztl{tt{{t{{|ztllut|]bZkd\{u{ztlldd{{d\\edktt{||UUY[[TLKR{{f]cultttslksultUMRlksddc\UZ\UZlek}rfk}|tsl|{{tulttts}{||tllf]cmtt{{tj]\f]c}mk{tt|zmttu{{lks{{|lksult\UZtlllek\[b|^^q\[[mtt|n{{MSSult{{~{{^fs}{||}|llk{tt|]bZ|}{{uttll|u||{{t||lri|ztt{{||SKJekd{{t|{{lri||{{tuzt{u{}mttfkkult|uuztuztlksultttsb[U|zultlek|||srerg]||d\\ddcztllldFD;f]clkskk]|z|zllk3+*ttsUMRf]cr]g{u{tsllek{zmsletzllek}}ttsldd|zztl{{t{u{{||}||}|}|kd\ult{{tsl|}{u{[[Tddclld{||VTLJHFVZTtsllks{u{{||{{t{{\[[\[[}f]c{ttlldttsf]c|uttskk]aWMVTLedklksmtt||f]clddtt{\[bult{||{{{u{|{{t}{{ttsttsmtt\[bedk\[bmtt{{mttnv~{{tttssre|tts{||||tts|z}|z}{{t|zwlu{u{ulttt{lek\[[||{||llk{{\[blks}}VZ[UMRedkuzt}{{tfkk{{{{{{f]c}ultult[[Tmttfkklek{tt{tt|ddckk]yfdtts||kk]|ttsSKJtt{tzl[[T]cdlek}UTS*)(ultbVZlek{{t{u{||{{wluztlriu{{{||f]cu{{|z||}|ultrg]wlsleut||{tt|tt{{{t|z|uztf]c[TTddctlld\\tllkd\ldd\[[ultttsekdlkslks||UTSd\\llkVTLtt{tts|{||{||[[Tg\rLKRlksf]cek]dc\VTLkd\]cd]cdek]||}ult|llklksmtt{{{{|{{edk|\[[edk{{{{vuzt{{{{tt{}{u{|ztsllksultult||tll||{||]bZttstt{\[[lri|ztzl]bZttsu{{tllwl}u|zedkuztu{{{{mttult{{t{{lri\[b]cdmtt|zz}lri|lksf]c|u{{\UZ}|ult}tll{tt|uUMRllk{{tlksultlks{u{mtttts}}[[TSKJbVZ}tt{}[TTllktslkd\||sle|ztll{{tllk74,SKJtllddcmttldd{{tslef]cslesle|lri|z|u{tt|usre|lldmtt|ddctt{{u{tslr]Zu{{|uultllkfkkSKJ]bZ]bZF=CF=Cmttlri|{{ttsllkstlltts]bZlekuzt|ddclkslrid\\sleu{{slesred\\cbVtll}uztlritll{tt{{llk{u{}lks|{{|tt{||nv|{{lks{||}{{tlltts[TT{tt}v{ztl||mtt{u{{||SKJ|u{{t~{{|edk|tsltzl{{t{{lektt{{{mttldd]cdvlksu{{\[bUUYmtt|{{{{VZ[|mtttt{lkslksult\[[dc\||UMRyflreddc\j]\j]\{ttllkedkb[Uultult]cddc\[TTVTLtll[[Ttllr]g}{{tekdultbVZj]\UTSC8.JHFlddJHF!ekd{{{{tultllksle|zb[U{{|||}{tt}v{|ttssleuf]c}mtt{{tlri|{||tt{dc\tllekdlldvmkmtt{|||rfkekd[TTekdlldmttek]}lek\UZVTLlks}redultd\\{{{||}\[[ddcult{||}lks|tts|v||~|lks{u{ldd{{mttMSSUTStts~ztlv|]bZcbV|z\UZddc\[[[TT{|||mtt\UZlksultJHF\[bUMR}|||ult{{tult||z~{{}f]c[TTleksleztl{u{red{ttrfkult|ddcztl|||j]\u|cbVddcztlllk}kd\u{{t||{zmMRK*)(|ztlltts[TTlriredtsl|}lld||n||u}{zmlriztlbVU{tt|ztt{uzttts|z||ttsmttUTS^^qlks{u{{{{||lddztldc\ult}||}{{}\UZ{{||}~|{u{uzt{{|ultmttedk{ttllkult|ult|z|{||tzlsle{|||]bZult{||{zm||{{|z{{t}{{t{{t|{{tdc\tsl[TTkk]f]cf]c{{tt{tt{lek|tt{fkkmtt]cd}|Ù{{ult|}{||tsl{||llkedk}||{u{ldd{tt||slesle{{tj]\}kd\ek]dc\ekdredVTLb[UVTL]bZSKJlksVTL*)(|{u{{u{ztlttsutsre|u{{b[U|zultut|iq]{{t{||b[U{zm{{t|z{||{u{tt{tt{tslmttuzt|z}ttsuztlritsl]cdMRKllkmtttll\[[|ztl{{{{t{tt|utsl{tt|zbVZ}}{ttmttlldSKJlddJHF|z}|}lrimttmtt{{|}wl~mtt|ztt{|{||ultu{{mttmttlks{||lks|ddcf]c{{{u{lks[TT\[[dc\|ztsl|z}}{{d\\llktsl{{t}ttsdc\ult[[TUUYlriuztMRKtllllkylddc{{ult{||}tt{ztl{||tsltt{u{{ddc\[blldlddedkdc\{{tlldj]\{ttSKJSKJ]bZlridc\d\\j]\j]\lddekdttsUTS*)({tti\Vldd{tt}zllsre{{t{{tllktsllrid\\yle||{u{lldyletts}|{||d\\|u|{|||lrikd\kd\uztu{{uzt]cdUMR]cdddcd\\]bZult\UZd\\nsleu{{||{ttf]c{{tdc\|}JHF{tt[TT|utsl|mwUMRttslksultlkstts{u{{{||{||{{t|lks{{{{{{{{{{{{nvlksnjultu{{ut{{tts{{{||\UZult]cdult{ttultllk|ultedktts\[bf]cult{u{{{ttsmtt|tt{mtt{{}lld|zdc\v{tts|zd\\lks|d\\red{{}\[[tslSKJ||tsl{ttultdkVSKJVTLVZTmtt\UZredtllztl\[[SKJ{{tJHF*)(dc\ultd\\i\V{tt|~sred\\v{lldek]f]c|z||zlltll{tttt{|z|llk|||}ek]|cbVlldmttd\\lldllkedk{{llkulttts~bVU[[T|sreddcttsf]cv|||sleb[U|utt{d\\\[bLKR{{llku{{{||}rfk}{{{u{zllu{{ultult{{{{{{tsl}|{||lldmttlrittsslemtt\[bult}|zuztlldttslriurfk}dc\JHFf]cultlldult{u{f]c}{||mttlld{{}{u{ztl|z{{ttslsleudc\ztl|tzlslelksekd{{t{||ultleksleUTS{||d\\slett{vldd{ttsle{{tUUYztlult|sremttSKJcbVF=C*)(d\\tts[TTv{{tt{{tu{zmultred{{t}lddllk|z\[blld{{|uu|ult|zf]c|z}ttsu{{]cdJHF}[[Tf]c\[[tt{ulttlllri{{}lekedkllk|z|u|udc\llkb[Ui\VUTSlddUMRkd\VTLrg]f]cf]c|u|v{{{||tll}|uztlks{{{{|}{{^fs}|~}tll|tt{tsllriullk}lektt{}lldf]ctts{tt|uztj]\|}tzl}ukjV||ztltll\[[ddcultult{u{{{tlluzt|utll{{|{{{u{u{{{||]bZlriUTSd\\u{{f]c}ult{||d\\{ttsle|uztl}u{{|{||{u{||{|||||u}JHF[TTkd\VTL74,ulttslttsbVU|wl[TTsle{tt{ttultvleklldlriekd||tll|ut|u}|{|||z{tt{{t|zulttslult}{||ekdf]cUMRVZ[\[b}||u{{ek][TTlri{{|ddclridc\j]\lriultlek}[TT}}{{~|tt{}uztlri}|zu{{lriuzt{||lkstt{{{||lld||z|||ztts{ttlekUTStts}}|{zm{{tv{ttsVTLlldlldSKJlekedkuzttts}}{u{{ttult~tts||mttf]c}lldv{}tt{ztlrfkultbVUu{{|ultslev{{}{||cbV{{tedk{||JHF*)({{lddttsztlultcbV||rfk{zm}tllj]\uztuztztflddd\\|tt{u|{||lri{{t|llklek{||||tts||}mtt}d\\{||srett{lekztltzlkd\|kk]kd\{zm}{ttu{{lrilldf]cztl~{{tddcvmttrfkUTStsl{{|lldtslylult{||mtt{||{{tleku{{}|{u{lriuzt{u{ē}bVZsleult}lekrg]sle}lri{zmtzl{{t[[Tllduztu{{mtt}]bZultu{u{ulttsllksVZT{{u{{}{u{}{{}lldsle|z{{ttslkd\ultlksult}ddcu{{bVZ[TTd\\{tt{tttygtts{{tlektllkd\|ulkslldtslFD;*)(|vtt{rg]||utsltsltll{u{kd\|vztllriedk{{|uultzllld{{}lldf]c}{{ttlltts|\[[rfk||uzt|zu{{lri{{{{|z||}{tt}rg]{{t|z}}|zlriuzt}leklldllk~u{{llk{u{||{||tts{||vtts{ttf]ctt{|{{t{{tddc}u{{tts{{|mtttt{u{{{{|||{u{edk{{u{{redtll}VTLztl}usleztlVTL{u{}f]c|u{{mtttt{lks||z||u{{~bVUtsl~JHFek]lks\[[lrilri\UZbVUultkd\zll[TTd\\uzt|usred\\uztmw[[TSKJ|JHFd\\ultJHF*)(|uztlredulekrg]ztlult}sreult|zttsfkk|z}srellk{u{{||{u{lldu{{|uztultLKRVTL\UZlekuzttts{u{[[T{{lldultllk||uztl}redvlddtsl{ttuztlldlks||~lri[TTtslf]cuttsltllv{}||||lks|||zult{{||{{{{}{u{llkĂvyfd||rfk}{||ztluztlri|lrillktt{|{{]cd]cdmttfkkfkk{{Ù|}|{{mtt{||lldlrikk]lldddc|zlld||ddcldd{{{{JHFUUYu{{lddJHFf]cldd||ttsllklriv{ddclldj]\dc\}}lri|JHFJHFuztlld{ttztlllkjV[|z|zkd\uzttsl}|zztl}tsltts{tt{||ult|zlekuzt]bZMRKlld]bZ|tt{{{tllk||u{{||zbVUtsllldfkk|tslsred\\fkkldd}|{{t}{||tts]bZ{u{~|||{u{tts{||f]c|u|tts~ldduztu{{|}||mtt|{{||ttstt{uzt{ttut~}~f]cmw}llklektts{{lrilriedkultJHFlks{{{{lksedk|z|zlks|zuzt}|}|uVTL|v}slerr]tzl|z|[TT|lri}|tslUTSd\\|ekduztyl[[T}~VZTlekvlldultztlf]c{ttF=Ckk]rg]UTS*)(slej]\}||uldd}}ttsrg]sle|utt{VTLsleztl}zll|}u{{{{tts|zlriUTSJHF[TT{{{{}|uzt{{t{||fkktslmttsre{ttu{{}b[U}{||tzlkd\74,{u{ztl}ztl||uztmtt{u{f]clrid\\lddttsultult}[TTlks{{|z|fkk||{{ttsu{{]cdtts{u{||\[[}|kd\{zmttsekdttsf]ctlldc\tzlJHF{||lkstll{{lkszlld\\dc\v{redv|{u{f]cedku{{~llkekduzt|zttsmtt}{||tllkk]{||tllllklri|zVTLek]dc\|z|dc\{{t{||lksmttultmtt{{}{{|[TTsresle{||lld]bZrr]JHFttsJHF{zm|uJHF:97tsllrilld|}|}}uukd\v{|tt{}{u{u{{lks||lri||{||llkmttf]c{{{||{{t|zuztlekdc\tt{kk]lek||d\\|z|z{||||||kd\VTL{||tllsre|||UMR~}{tt||kk]}{{{u{{{|{tt|z|{||{||{{{||}ttsuztUTSUTSSKJSKJ|}utultslelksddcd\\lek||uztddctsl}{u{\[b}{{f]c]cdJHFultllktsl||lddD;9lldbVUuztekd|z{ttek]|z|z|z|z|z||yleekd}||||[[TbVU{||||{{t|dc\\[[mttlld[[TSKJVTL}tts|\[b{{tj]\JHF*)({tt|z|u}}|u{zmdc\leklld|{{tslellk||uzt{||}zl||||llk{u{|z~{{v{mttlekullk{{ldd|{{ekd{tt|z|ttsldd{u{|z|u|ztllztldc\b[U[[TddcVZTtlldc\ekdldduztlri||{ttlek}u{{ekd{||}zll{{ultmtt{{{{{{mttek]lri||z|z{{tlldultttsUUYdc\v|Ē}|v{s_qred}VTL]cdtzltzlmtt{||f]c[[T|lksedk\[[}tt{|u{{}b[U{{d\\tt{|mtt{{{{}|||redslej]\ultbVUr]ZcbVkd\slelddkd\{tttll\UZVZ[dc\edkuzt[[T[TT\UZlld[[Tzllkd\VTLj]\>EC:97ult|z{{tzllztlVTL|}}|}|red{tt{tttllttsuult||ultdc\{u{}vedk{{{|||LKRtslmttu{{ttsd\\ttsuztldduztj]\|tt{llklddkk]tt{|uldd|{zm|zlrid\\||ekdlek|z|z{u{{zm[TT|zttsUTS|u{{t||{{{ttfkklks{{|]cdtt{fkktt{||mttultttsf]c|z|z{u{{u{ultkd\SKJkd\tll}{{t{{t}uttslSKJJHFb[U|tslvldd|]bZdc\mtt|ttsVZTult|{{}{u{}ċ||bVU~ult|z}dc\ldd|ztslUTSSKJlld[TTslej]\\[[MRK[[T{{tlksllk{||{||mttlrimttddc[TTj]\}kk]lddtslu{{{u{\[bVZ[}|JHF*)(tll|z|uyleutkd\sle||j]\{{tvddc}kk]ztl||}tts}|{tttt{|ztts{tttsl{{t{u{tsllriuVTL\[b[[Tedk|u|zlektt{{{lddtts||leklddlldtll}tll||tsl\[[{||ult{{tMSS|z|zu{{|lddu{{|{tt|sleSKJfkkVTL{ttztltzl|u}{{t{{{zmtllv{ult{{|znvultmttult|zttstt{|{{tkd\dc\{{kd\ult}{tt}kd\iq]|zUTS|{ttllk[TTedklksu{{]bZVZ[lri]cdlek|lekllkllkkk]{tt{{tllk||u|ulksllkb[Udc\VTLjcVcbV]bZlddVTL}dc\\[[uzt{{tlksttslddmtt|z]cdlld{u{[[TVTL{{tlrillk{{tuddc[[TedkmttUMR>EC74,|||ztsl|utllkd\{zmldd|z]bZ|u}}tt{{zm|ztl{u{lks|z}|}u{{||{ttf]c[[Tsre{|||ztslldd]cdtt{{{lks|lksfkk||tllSKJlddSKJ[[Tlek}ultekdlld\[[||uMSS\[b\[[lldd\\edkf]ckd\|zult|||zultult}|edkf]c{{lks|{{mttmtt|{u{|tsl{{{{Ǧ}sreutsre}{{t{u{{tt|zwv{{tukd\|ulldtsl{{ttt{leklksult}u{{ulttts{tt~ultllk|||llku{{tts|kd\||ult|zllkkd\usre}VZTekdlksJHFUMRlri|]cdbVZ|zlldUTS|ulld||dc\sleVTLJHFJHFJHFztlJHF3+*{u{u]bZtsl|u{ttut{{tjcVb[Ukd\ttssle||{u{{u{rfkutfkk}llkult{||}|zf]c{u{zll\[[tt{lekdc\\[bf]cult{{tllkllkldd}uf]cv{|uultVTLuztttsslef]c||ddc\[[|z}u{{ldd|||tllrfkttslek{||{||tzl{{t|{u{|}lriedkllk}{{|||z|{{||bVUredtllƨ|udc\}|uv~]bZuztkd\lek~\UZ|bVZultf]cf]c}}}fkk{{{{mttUTStzl}tllultult{{tultdc\u{{uztekd[[Tlridc\|zlrisleddctzldc\VZ[lriUTS\[[ddcVZT|fkk}bVUtt{|tsllld{zmlriulldf]c{ttUTS|zJHF*)(lksuztuztzlluu{tt{ttuzt}{u{|bVU|u}tll}{||uzt{u{lekvtts{||}v{{u{{||{{t{u{SKJlld{{t|{u{ult{u{lld{||{||{tt}j]\}s_qzll}tsl|ud\\zllcbVtslmtttsl\[[tll\UZ{zmuztu{{u{{|{{t}ttslek}uzt{{tult}{tt{{~ttstt{{{uzttt{ldd|zu{{|z|tsltt{tt{ddcddcSKJ{tttllv{}||ztlmw{||uztfkkMRKlrilrillkddc}{||edk{{f]cleku{{u{{mtt{{tsl|mtt|z{||tsl|dc\{u{edk|JHFVTLVTLiq]VTLu{{]bZlekllkuzt\UZttsJHF]bZvJHF]cd]bZSKJut}ub[UlldcbVzll|}|{{t|zmttmttJHF*)(ldd{zmvu{zm}|{{tut|vutv{{||tts|u{tttts{zm}{u{tlltt{ztl|{||}v{||llktlllldlddlri}{{f]cult||}mttf]c||ultrfkulttll~}{tt{||{ttddctzluztttsuzt||edktts{||{{{{tmtt||{tt}|tts}d\\lekttstt{|}}tts{ttekd}|b[Uzll{tt}lksult{u{~}tllv{v{tll{{|z|zlldf]c}{{]cduzt}{{}v{[[Tuzt{||{{tVTLcbVllk|tzl[[TuztUTSuzt\UZVZ[ekdtll\[[uztUMRlri[[Ttts}|zkk]ekd{zm||ztzl[[Tedkv{:973+*|zttslld{zm}||ztlldd|utsl||{ttą{{t{u{||lrif]c{{\UZ\[bf]c{{}||[TT}\UZ{u{sle~ut|z|lriuztSKJ{||ultlksMSS|{{}|zlks{||||}|uzt}{tttts||ult}vuztttslddlddtsllks}ultlksultlddldd~||}ulttll}|zlridc\|zekdlri|tslf]c||{{t{{uzt|z|lldllduztkk]]bZ{ttllk[[T|mttddcVTL{{}f]cUTSlkslkslri{||lksttsultultlks}|{{tdc\]bZ|z[[T[[TLKR*)({{tll{zm|z{{t}z}|{tttt{uzt}{||}}|ddc{u{{{{||ttsleku{{ttslek\UZlddVZ[}lkstt{{||tllttsrfk}|v|lddrg]v{utlddlldekdtzlekdtlllld~{{}\[[f]c|z{||llkllk{tt|lldtts{u{{{{{ddcttsultult{{|z{{}d\\{ttv{}tt{tllf]cut{{\UZrfkult|||uztllk}sle{u{d\\]bZtsllrillkmtt{ttmttd\\|||v{ult|tsltzl{|||lriuztlksztl||z{{t{{t[TTlldekd}d\\{u{d\\[TTJHFVZTuzt{||ekdf]c{{lek||lrizllztltzl|z{{t]bZ]cdu{{:97*)(kk][TTu|uzt}}}sle}|ult}|{||edk|z}{{d\\]cd|z}}~ylemttkd\ddcf]c{u{sre}ttskd\zlldc\llktslttsd\\|ultu{{ttslrilksddclksddc||lks}}||z{{{ttrfk{zmtsl{{tv{{}tts|{{t|tts|{{tv|}}}}\[b}ddc{{ttllf]culritsl}|{zm||}}]cdu{{u{{[TT{{mttv{|ztl{{tll{u{{{|z|z|zlrikk]|z}tzltllllk{tt|\[[kk]||tllVZT|u{{tt{|z]bZ}[TTtt{lri{||ultlld|zllkekd[TT{||mtttzltll{||F=C7-2utztl||{zmsrekd\{{tsleultzlut{u{}}mtt}{u{uzt|lksmtt{ttu{{{{tuztdc\tt{ult{||||tll[TT{{tddcut{||\[[{||ekdlld|{{t{||tts\[blks|lrilks}llklddllkllkf]c\[[tts|ulldu{{edk}}ut}|u{||}|ttssre}}}u}{{f]c{u{zlllddztl{ttttslksedkFD;kd\zl[TT}UMR\UZ|lksedkUTSu{{u{{|zyl{{t}{{tllkd\\||{{|ult{||]bZ{{tlddVTL{||||{{t|mttf]cuztultlksllktt{{u{edkrfktll}lri{{{tt|u}ztl[[Tztl~|{{tlldlldJHF*)(tsld\\|zztlwl[[T}v{u{{}{tt}~zll}~ttstts{u{|z{||||tlllritt{\[b|}ekdldd||llk{{t|z|ztsl}{{ult{{tcbV{tt}||cbV|u{||VTL\UZ|{{||||uyle|{tt|tllllktts|ztts||~|f]c{{~|lld{u{{{lri{{{{ztl}tlltsl}f]c{u{tts{||[[Td\\zllj]\vSKJv{sre\[[}|{{mttf]c}tts}tzlVZT}\[[ultdc\lri|{{tu{{ult}ddc}|VZTVTLuztmttJHF|z|zlld}lld}tts|ultf]cddc[TTtslf]c{{trfk>EC{{tVTLlldllk{ttfkktsl|z]bZ:97:97}||tll|u{{t|ztl{{t|u}u}|ultdc\slerfk|v{{{t|{||vtt{{{t]cdtt{uzt{ttddclri]bZrfk|{{t|u{{ultlriredu{{|r]g{{t}ldd||lddredredmttUMRu{{{tt{||srett{}{{||rfk{||{||ttslri{||}{tt~f]cv|\[[vtt{llk|u|z|||lldtt{ztf}{||dc\f]c}}|}kd\lekVZTlrisleredzlltllztlsreVTL{{tv{{lks|zuztsle\[b]cd}~uztmttttsmttddc{|||z{||sleJHF|\[[[TTdc\}lri|z\[[]cdrg]ekdtsl||ddcuzttt{|lld|vdc\u{{SKJdc\tslVTLlld[TTsre||lriaWMSKJ|zD;9*)(}tslu}{{t|lekaWMut|}}|[[T[[T]bZmttd\\{zmtsl{{t||}lriztl}{ttldd}{u{}ult||z|zekdu{{f]cztltts}ztfddc{{tsre||{tt{||lksulttt{llk{{sle{{|{{tult}vllktllcbV|z|ztltt{{||mwsresleult{tt}uzllutylelritlltlltzlkd\b[Uddc}lekedkut|||u{{|zllk{{|tslu{{}lld}UTS|z[[Tultuzttt{mtt{{t{{tlld]bZbVUultultUMR}{||b[Uztlf]c{{ultult|{||{{{{kd\tt{fkkf]cd\\utzll{{tlrilridc\\[[ekduzt:97*)(tzlsre}{tttts|{tttts||z{tt||ddcldd|]bZ\[b||tt{v}b[U|z|{||u{{ldd{u{}kd\zllkd\lekv{kd\edk}{{|tts[[T{{mttdc\]cdSKJleklkskk]{||tsllldllkslelld[[T{tt}|ddc|zldd}v{ultuztttslddmw{ttultv{{|||tlltsl{{t{||\UZ{{|{ttSKJttsjcVldd]bZ|}|lekf]cdc\tts{{}lri{{}yledkbVZj]\[[T|uekdd\\{{{ttddcttsf]c[[Tek]sleultlksFD;zllztl||tt{UMRfkkultu{{|uJHFred}}{{{||\UZztl{||||z]bZkk]dc\\[[UTS]bZ:97*)({{t{ttlddred}{zm}ztfztl|}{||{tt||{||ddc{{||lektlld\\lld}sredc\u{{{||sleSKJ|tlltllzllr]gmtt{||tsl}|z{{tuztd\\||ddc|ztllrittsztl|z|||{u{ut{{lld{{t}}|lekult}tt{tts{{UTSlksttslritts{u{||}|uek]|u{||tsl{{t||{||tts|z{{t\UZddcztldc\VTLVTLek]tll{ttyfltt{\[[}[TTtt{leku{{VTL]cduztu{{lksd\\|zf]clri{u{j]\edkttsllk]bZllk{{\[[SKJek]tll|vVZT[TT{||dc\ldd[[Tvlksf]c}ttsf]cJHF]cd{{lektt{fkk}v{|zsle\[b~lrisre]bZ[[TFD;||:97:97rfkkd\{tt{{t|||zult}{{lektts{{tllk{u{{{{ttb[UbVZtsl|ultztfv{{u{llkztlUTS{{sre|mtt||mtt}ek]}{{\[[lrisle{u{}rfk}lri|||}tslultmtt||llk|u}{u{}ttsllk}{||dc\{||ult|z{{ttslult||uztÓ|{||{||lldv}}ultbVUf]cldd}edk[[Tlriultultlriek]{{tdkVmtt}ztlulttt{llkUMRLKR}{tttts{{\[[ttstts}|kk]llk|||uztlek}{{f]cJHF{||VTLlri}|mttllk\[b{u{ultllkttssre{{trg]|zuztMRK\[[dc\:97*)(dc\ztl}||{u{u}|{||}uuzt{{t{{{u{SKJ[TT|zkd\u{{UTS||j]\{zmldd}rfk{{tll{tt{tt{||JHFtslv{{{tu{{uzttts|zlldd\\v{|z}|ullkf]cultult|u{tt|z{||v|ylult{u{|{u{{||ztlult[TT{tt{{[TTtlltsl{u{tlltslred}{||tts{tt|uzt{u{||||z{tt{{[TT]bZb[Ulriuztdc\{tt{{tredult|z{||{{t{zm{{t{{tVTL\UZttsd\\}ddc||\UZek]SKJ{{ttt{UMR{{td\\VZTUMRlri{ttddc||}d\\tsltts[TT|}|u{{zllri}ddcUTS[TTlektslFD;,55}yle}ut||}{{|}~|sre|z|ulri|tllu{{|z}ult|v{||dc\fkkleklks~|{||j]\tll~{u{ututtts{u{}{||uztuztllklld\UZVTL{{|z|lrikk]{{t}tllrr]{{ttt{lddf]cv{lri|~{{|]bZ}~fkk}|llkb[U{u{||d\\||tzldc\cbV{{dc\j]\ddc}|zu{{tsl}{||leklek}zll|zvsle}v{VZ[ultultbVZ}|tllSKJVZ[uztUUYUTS|ddctslllk{{}{ttleku{{lri{{{{tztltll[TT{{{ttut}ddcUUYllklritt{ztlVTL|{{tlddtslmttlriztltslUTSJHF:97*)(}{||ut}tll{{tztlultuzt{{t|{|||u|}|zlks{tt|tsl{tt||v{ddcutJHF}tts}bVU{u{{{t{u{{{slerfk{{|{{tult\[[]cd[[TmttlriVTLlri|ztl||lld|{ttd\\{{}{{tll~||}}lri{tt{||llkzll|slellk{u{sletll|u||}lkstzl||ulld|v~{tt{u{\[[}{||uzt[[Trfkd\\{u{tsl|z|uult{u{UUY\[[ldd{{t{u{utult{{t}ultj]\lektt{ztllriddc{ttb[ULKRVZ[lksf]cttslddult||{u{ultut{{tbVU{||VZT\[[{{ttllVZ[mtt{ttmtt{{tf]cSKJ{u{|\UZztldc\SKJv{tsl{{tuztuzt|\[[VZTUTS:97*)(||kd\{{tztl||srettstsltt{}|z{u{UTSztl|z{||tts{||ekdbVZekdu|uVTL\UZ{||f]cult{u{}|lekv{{u{leklksf]clldUMR{{cbVddcSKJuztddc|usle{u{}kd\ult}}{{t{{{ttdc\zllztlut||zv{{tttsl|kd\}j]\~{{{tttts[TTmttu{{kk]leku{ttuztlekedklrimttd\\\[[||{{ultwl{u{UTSedkd\\|UUYtllf]ctts}|z||UTS\[[llkUMRrededk{{|lek|sre||utddclrimtttt{{u{|lri\[b\[b[[Tlri{||lldf]cmtttlllekdc\uuttsmttv{[[Tekd:97*)(ztlutsl}||{tt||{{}tsl}{tt|zUTS]cdlritll|lkslldtslmtt||uultwltslrfk}j]\\[[uztleksresre{||{u{||{||d\\}{tttt{|z|ulks}|ulld}|z|tllu{{|||u{{|sle}{u{}lddv{|umwtll{{t}}ttsfkklri{{t{|||ztts{u{b[U{||i\Vvllk}tt{lriSKJb[U]bZ{u{llk{ttdc\uztfkkddc{u{||}tzl{zmtsl{ttztllldllktt{||lri{u{slef]c|{{tMRKsretzlsretslUUYUTS~ttsldd{||{u{\[b[TTf]ctslcbVtlltt{b[UUUY{{kd\ultJHFleklld{ttfkk}UTSJHFVTL|z}JHF*)(}ut|z||}{tt}{||v{||uzt|ztts|lri|uedk]cd]cdf]ctsl{u{|z{{tt{ult|bVUlrif]cJHFbVZlek|ultvslezlltt{}fkkj]\}{ttult{zm{||||}|~n|ztll{u{{{t|zddctllztl{{|z|}}}}{||||||||~ultzll{||llkkd\lek}sle{tt|ullkllkzll~[[Ttlltt{[TT\[[ddcSKJutredu{{tt{|z|zlridc\tt{lrif]c{u{{||{u{lkslektllylezlltslmttsleultJHF{{ek]lldzlllldekdztl|z]cd|ekdUTSultJHFVTLkd\v{sle|tt{tll{{ttts}lek{{tlks]bZF=C*)(lld|z|tslvtll~rfk{{tts{||ttstll||sremttu{{fkk|uzt{ttuztmtt}{||||z{||uzt||tts}tt{f]cJHFfkk{||ddc{tt}ultult|zddcsle}lrillk|sle||tll|{{||lrilks|u{||||{u{tll|ztt{v[[T|u|ztl|uwlzllsleuztut{u{yfdllklld{||{u{cbV}||ttszllb[U}ttsrg]ultUMRrfk[TT{||ttslldllkJHFedk{zm]bZred||cbVlkslks}|ultrfkulttts|ult[TTztlVTL{{|ult[[T{zmfkklrilekJHFdc\tsl~lddlldlri|{{tztlSKJ||mttiWUd\\lritzl}ldd{||tzlfkkLKR*)(u{{|||v{|z{|||z}{||{||lkslddlrilri{{||u||ztl{|||uJHF|rfk{tt{{kd\leklksf]cmttbVUult[TTtts]bZdc\tslv{ztlttsekd||uddc\UZsle{ttu{{tts|z{tt|z{u{tts||\UZztlwl|mw||urfk}|f]cultsleu{{}{{uzt\[[{||ut}}zllztfVTLlekddc}lddultkd\mttekd]bZ||SKJuzt\UZdc\kk]{||zllv{|tts{{t{{tUTSdc\tt{JHF]cdddcjcVlriyflu{{{{[[Tmtt|zlri|zlks{{tmttVTLredcbVlksdc\ttsuzt||tsl{||ddcfkk]bZJHF*)(||z|u}|~}sre}|z|tsl||tt{|u{{mttlekulttsltts{||lldslelldultf]c}fkktt{f]c{{tlritll|lddultultttslld}{u{{zmtllultekd|{zm|uultult|ttsddc{{tedkred{{}tts}|llk{{}{||ult|u|usle}sleyfl[TT{||ddc{||lri{tttsllksi\V|u{u{{tttll{u{{{rg]tll}v{ttsredtslztltslVZT\[[ekdu{{ek]ultbVZf]clek|ldduzt}rfklksu{{ult|z|{||lld[TTf]clri|{||]bZddcllduzt|llduzt}bVUtlltlltlluv|zfkktzltslllkllklek\[b|zVTLsle\[b:97]bZrr]ut}ztl|\[[{tt|kd\lld{{tultu{{tt{ultu{{lksmttedk{{||{||tll|zf]c||kd\tlllldlld}llk||{{|zultedkekd{u{||JHFtllUTSmttultuzt\UZ}|zd\\ult]bZd\\tts{zmtts|||[TT||lld|||z|z{{|z||u{{dc\{u{llk}}|||v{||||||lks}{tt|uultultleklek}||ututfkkddc}wlj]\{yfj]\f]c|uekdllkv{ultd\\i\V[TTv{v{ttsutek]ldd||\[[ult{{ttllfkk|f]cuztldd{u{uzt}u{{|UTSult{{t]cdVZ[}uztJHFMRK{{t|zSKJ\UZlld}{{llduztllktll|JHFJHFVTLllk\[[&&ek]{||}|||{{ldd||z}{||}|lriultlksmttlriUMR{{sle{{lriult}bVZtslfkk}{u{ddc{tt]cdUTS|mw{||d\\|dc\llk{||lri||{ttu{{ultldd}{ttUTSVTLrfk{u{slelritllztllld{||}ddc{tt|ttsv|b[Uuzt|{||sle{{tttsulttsltllultred|ulddVTLcbV}||ldd||}v}||ttsttsddctsllekSKJd\\{{t\[[dc\}ult{||llk||v{{u{mttlldf]cbVZf]c}ddcttslek}\UZv|uultredUUY{zmmtt|z{{tult||||~lrilritsltsl|z|zf]cu{{{tttllllklddekd|ztzl|ztl}\UZlek\[[VTLslemttMRK%&tsllld{tt||ultuztuzt|z|UTS|{{|[[T\[[]cdlekd\\{ttleklrif]cldd{{}sle}mttSKJllklldsle{ttddc{ttj]\llklritlluzt|lld|}{ttbVU]cdVZTlldultslef]cdc\ztl||{||||mtttslmtt{{tlltt{|tll{{sleztl{u{|||ukk]v{dc\v|f]c}|v}f]c}|||ukd\||kd\}\[[ztlsre{tt{{t{||sre[[Tlddkk]{||bVZ{{t{{tzllf]clek{tttsltt{ztllekult}{{tf]c|uddc{{j]\{u{f]c\[b|zlek|mttlek]cdtts|z}[[T{ttlddlldlld}ekd\[[dc\lriu{{{u{}llkmttf]clddddcMRK*)(VZTmttztl|u}|}{{t|zlekllktsl|z}[TTd\\{{{u{}f]cttstsl{tttslmttJHFlld[[T{{llkj]\VTL||ldd|z{ttllkult|z{||MRKutll|v|j]\}lldsleulttts}|}|z}}tt{|z}{tt}utrfkb[Utll[[TlksbVZ}|{ttrg]}}ulttt{tts}}lksrfk|j]\{{tllkllk}ut{u{lri}ddckk]|{u{ttstll|lekb[U{tttts{zmuzt{ttuztztlldd}{{}}|tsl[[T||}tllyl\UZ|}[TTVZTtlllri|tllztlrfkultd\\ult|zu{tt{{t{{tlld{||]bZtzl{{lekmttlldztldc\>ECtts{u{}|||z{{|zuzt}SKJ|]bZVTLkd\lld]cd\[blek{u{{tt{{ekdlldult{||{tt{{t{ttSKJlld|lriuzt|{tt||u{{u{{{{|ulttt{lri|zllttsttstt{kk]ut}{u{tlltts}tts}v{|umttldd[[Tlldsleztlultllk|lddztl{{tmtt{tt{u{zllut}{{t||}||tsl{||sle[[Tlldlri{{{{tt{{u{lksuzt{||sled\\SKJ[[Tuzt{|||}{u{|tsl|z{|||||lks]bZmttslemttedktslmttldd{{t{||ultttsj]\llklld{tt}lek{{ttstll[[Tmtt}lek{||{{tlldlld]cd-1+tsl|{||Ĥult|||z}}|lddu{{uztllktt{lek{u{|ztlu{||{||}}}kd\lld{||u{{zlllkstzlmtttts{||ldd{||mtt|z|}}ddc{zm|z{ttlriddc}}tt{{{{{{{tllttsfkktsl{||{tt{u{d\\SKJu{{rfkv{zll{{t||{{tu{||ttskd\tsl||lekut|u{{ztlsre{||]bZtsl{||tlluztuztsle{ttedkzll|ztsltllj]\zllslelld|{tt||tsldc\tsltt{lrikd\slered{u{lddddc|u\[bllkmttSKJtsllrib[Uedk{{ultedk}|ztll}zll\UZllktt{ttslksmtttt{[[Tultlri{{tlrisle\[bLKR%&}{||Ħ|{{{tt}{{{{t||utll|zVTLlddmttmttlksttsult|z\[[ztld\\tlllld{{tsl{{sle|lld||{ttUTSlek{zm{{LKRekd|ukk]|z{ttttsuuzt}||{{t{|||u|fkktts{||{|||z{||ttsu{{lddlddd\\tts~[[Ttsl||}}}|u{{t\[[j]\lriSKJ}||}||ztzl}{tt}ldd|{{ttll{||lddrg]{u{lrilri\[[|mtt{{t|z{{tVTLbVZztl{{ttsldc\]bZllk|ttsldd{u{|tt{u|u{||[[T{{\[btt{b[Umtt{{{ttrfk{u{UTSlritlltts}ult{||ddclri{||tsl|b[Uttsllk[TT\[[ek]FD;}~|{{t||{tt|lri{u{llklri\[[{{{||{u{dc\{tttslzll||{tt}}||sreut{||ddc}ttslrikk]llkllklld{{tUMRttstslultuzt}{ttultlekekdlriddcu{{}lldtt{uzttts{{t|}lddult~|z}SKJ{u{v{sre|ztl}||||mttllk}{ttv{}ldd\[[lldjcV}]bZkk]bVZ{tt}u{zmVTL\[[|\[[f]clddekd|sle{ttutf]cVTL|UTS]bZztluztlridc\}rfk\UZ|z|u{{{{tts}u{{tt{u{{|z}|lrilddf]cVZT{||UTSttsVTLtsl~dc\ultu{{ultllkUTS)+2|||zl{{t|||z{{ddc{||lks}uzt{{t}{u{{u{utlddf]ctllztluVTLult|d\\ultVTL}{{llkllk{tt{u{u{{ztlttskd\JHFlri|tll|||lld|tllsle|ztl\[[}{||tt{||zll{{t}llkdc\yleutlltts}{tt}uzt}{tt{u{ttsdc\{||{{tllkztlztflddlrillkddcek]|zdc\|zdc\lddUTSf]clekred[[T{{|u{{t}edk}|srett{{u{lrimttuztddcekduzt]bZedk|ziq]lks{||sletllutult{||uztu{{dc\tts{{dc\tts\[[|zddc{ttuzt}{u{tll\[bLKR|zu}v|||Ĝ||||fkk{{{||{{}ult{u{tsltt{}tslldddc\rg]llk{ttlldsleutult}llk{||d\\|u|{{llkdc\]bZttslek|uldd{u{}cbV{||{ttu{tt{||{{||z{u{ttsult{||fkk\[[u{{{u{{tt|{tt{zm{tt|u|ldd\UZsled\\kd\{tt{{tlldmtt]bZekdtts}bVZ}llklldkd\SKJddc{u{ztlllktzlb[U{u{{tt{{tu{{lri||r]g||ucbV|z}||zekd|z|zmttuzt||d\\UMR||lksf]c{{t]bZ|uuztmtt[[Tttslri|zdc\JHF}tll}[TT{ttultd\\MSSLKRv{|||tzledk\[b}lks||||llkztl|z{{t||lld|utsl{||}sleu{{|z\[[ttsu}ekdllktt{ddctzltsljcV||{|||zzll}||{ttu{{zll{{t||{tt|tt{tslttstlltt{tt{\[[ultllk}lksu{{t|}tts{u{||tts||}|{ttsle{tt[TTkd\tsl{u{{{tkd\{u{v{JHF}|ztllri{ttultbVUdc\{u{lek}ttsttsut||{{ut{||ztl|zsle|tzl|]bZ|]bZu{{\[buztJHF\[b{tt]bZyle\UZ{||tllredVZTek]fkklddlekedk|ttsu{{]bZ]bZ|zv{ult|||sle\[bMRKUTS|cbV~}|znsle|{||lldllklri|u{|||u{{ult{{}{{lkslldd\\ut}}ztlutv{lek|ured{u{{u{{tt}lddldddc\sle||z\[[{||ult{u{\UZllkf]cf]ctzlsreSKJ|tzl[TTutsle|u[[Tu{||tsltsltsluzttts}|u}uztldd{{tldd||ztl}f]cuztult|u|ztsl}ult}}|ult{||d\\}ult|{{t||uyle{|||usle{{t||tsllldFD;uztllktzllld\[[lld}dc\bVZSKJtzlztllriultztlkd\{{tkk]ztltsluztu{{mttv{u{{{||lksddcllkMSSlldult}{{tUMRf]clektll{{tlld{||u{{tll\[[dc\]cd]bZlriutult}~UMRf]cJHF?;C|}}u}|ztsluztUUY}lksultlksmtt|||{u{}{{t|||z}ldd~{u{cbV{{tuu{zmj]\||ttstts|ztllf]c}|lrilriulri{tt{||zllut||||||z{u{{{ulttll{u{u{{tsl{{t|tllf]c{{||{tt{{zll\[[||{||uu{{b[UslejV[|uztl}ttsv{}j]\|v{tt{tt[TT|u}|ylett{sre||tlllddiq]{u{|||kd\{{ttslf]ctll{u{b[Ulksztl|uzt{{tmtt{||\[b|lksUTS{||\[[|lkssle\[bllk\[bUMRttsuzt\[b{{lekdc\tt{u\[[{{t|[[T{{t|{{tultldd}tlllddbVZldd\UZJHFlks||u}{{t|z|uyleult]cd{{tlekultedklksu{{|dc\lek{||{{tlri{ttultlddUTS|||sleddc\[[lri|lek[[T{tttll|u|uztJHFlkscbV|zllk|z}}|kd\|ztll}tll}{{||{tt|ddculttll[TT{u{}}|zn{||{{tlddmw}||}ylebVZ}|ekd}{ttkd\zll{tt{u{ult[TT}[TTlri{u{{tt}mtttt{ultllkztlUTStll{tt{tt[[Tllk\[[ttsLKR[TTtzl\[[SKJmtt|mttddcu{{lekdc\ultlks{u{ult[[Ttzl{{t}fkkfkk|{||uztuzt{tt{{t}ultkd\tllJHF*)(|zzlī}|}lld}ult{u{llk|lri{tt||utddc||{u{f]c[TTlldcbV}UTSn[[Tlrikd\{{lri{{tlri{zm{tt|zll{{t}|z||{{t|}fkkuttslultu{{}}ddctt{||lddldd{{d\\ultv{{ttlriut|z}uztztlzll||tllttsuzt|ulddztltts||lek|uttsek]||}}}lek||ztlslewldc\uztztl{ttuzt|zllklddu{{|{{t]bZ{u{SKJUMRmttSKJ]cd]bZ|zmttlriMRK}f]clldJHFdc\UTSd\\mtt|tlllriekdlri|\[[tlltt{dc\lridc\{u{{{lddlektll}d\\lddJHF$|lektsl{{t\UZUTS|{ttlri|lri|zltllutv{||ttsslebVUldd|zdc\|j]\dc\redzlluztttsJHFlek{{sretsl{||{zm{||}sleult|uzt}tts||ulttzl{||tt{|lriv{{u{|||zmttultek]{u{ttszllv{lekutslcbV|zuztkd\}zll}tsl|z|uv{red{{r]glrilld{u{{u{}|[[Tkd\}|u|zj]\lddf]ckd\{u{}[TTdc\|ek][TTlld{{t{||{u{ult{|||mtt{{tUTSddc|z\[blrimttuztlriu{{leku{{lksmttlri{{{||{{t{{|zllddcttsuztllkuzt|zlekdc\ztl{u{ult}}{tt{||ttsultVZT*)(|utslwl}Ǥ|zĜ||tzl|z||z[TTVTL[TTlriu{{utv{tll||v{}}zll}lld{{tlldlekyfd~}kd\tsl{tt{{t{tt||v|uztllklddcbV{u{SKJlri|ztzldc\{u{{zm{tt{tt}tsltsl|z|zutsl|utts||}ultult|ttsuzt{{t}|utsl{{tlltts{{tult|zdc\tslv{||{{t|uut{{tuzttzl{zm}||u|uuzt||}|ztllztfb[Uekdttsj]\u|zllk{u{~{u{{||||[[T|]cdUTS|uf]cbVUb[U{{tj]\llk{||}{tt[[T[[Td\\llk|z{tt{{|lks{{MSSlriVTLbVUf]cUMR>B9|||]cdtts]cd|ek]}{{t|z{ttb[U{u{ult{ttr]Z{ttMRK*)(ultmw¿wl{||{ttllku|zFD;ultu{{u{{fkkUTS{ttzll}d\\{u{lld}|||}uzt{ttkk]UMRrfkutttsSKJztlrfktsl\UZwlllkttsb[Uldd{||{||kk]}ek]|ultlldllk{tt}slesle{tttsl{u{}}|ut|uztu{{{{{{t}lri||ttslriult{tt{{tkk]{{tultttsllddc\{{t{zm{{tj]\zllv{{u{tll|tzlb[U{{t}|tll}|}ult}lrilriVTL{zm|u|uultlddzllztlf]ctts|z{tt{||uztlldtsl{||fkkek]tts{zmddctllVTLultuztbVUlriuztekdf]cttstt{u{{UUYJHF]bZ}{{[[Ttsl{{mttv{{tldduztttslriuztuzt{zmllkf]ctllkd\lddv{rfkztlv{leklekJHF,55llklks}|z~v{uzt}|z{tt||ztl|v\[[lritlllld{{t{{t~tyguzttll|z}{||rg]kk]}|ult|tsllek}v{zll[[Tb[Uultrfk||u{u{}{{}|{u{}tt{jcVi\Vu|||{u{|sreztf{u{|u|z{zm|tsl|z|z|}|tll{{t}~{u{}fkku{{tllult{tt\[b{{t|sle|lektll|zu{u{|u}{||tsltslrg]|sle{{t|tslkk]ult|||lld}ut{||bVUrg]kd\lek||tsluzttts{u{zlmtt{zmztlj]\tts{{zlekdtll|yl]bZ|u{||fkk|\[bUTSD;9lksmtt|u{{lksmtt{{}{||ttsuttsltt{|ztlllekredf]cf]c}{tt}tlltllMRK*)(||vƤtts}ttsVTLttsuzt{tttts]bZ|uztllk{{tztl|uztd\\ldd|||uztl}utedkttslldldd}}}f]c{{tldduttts[TTSKJlriekdu}ttsv|}ukk]||ztlult|}}{|||{ttlrikk]{u{|tll{||}{||u{{{{tult|u}tslv{{{trg]{{t||ut{{td\\ttskk]~tts|vsre|u{{tdc\kk]|||d\\{u{slered{tt{tttll\[[{u{lld{||VTL{tt|ttslldztllks{||lrittsuztlddtll|zllk{||VZT\UZlrislef]cu{{{{tlldc\UTSlrilks|{|||{|||zddctsl}ekd{tt[TT|{tt{u{}{tt}dc\JHF*)(}}Ǧ|||tzl}}uuztuzt}tsltts}ddc|z}|zlridc\||ttsuzt||ldd{u{d\\zll|{{tll}ttsyletll|mttVTLtt{|kd\uVTL|uwltt{|ullkdc\tslult||jcVmw{zm{u{{||{u{{||{{ttsl|||{||}tts|z{zmtll{u{|{{t{{t||||{{t||u}yle||red||dc\uzt||slekd\mtttzl\[[ult||sle|||z{{tb[Utlllld|||ztllddtts}|zmtt{tt{{mtt|tt{}redv{lksekdleklksztllekuztdc\[[Tekdlek}lld{||[TTtlllddlek{ttut}}|]bZJHFF=C~|~|ēllk{tt}}lrittstsldc\tts|\[[[[Tkd\VTL[[Tr]gek]{ttlldllkut|ttszlldc\{||rfktsl|||tllllktzl||wl{||redsleSKJ{{t|zllsletsl{zmlekf]crg]vtts}{||sle|z}u{{{ttut}tt{}llkllk|lld{tt{ttultf]c|u}sre{|||u{ttllk{ttJHFtts{zmrg]{{t{{tjcVdc\sre]cd{{tutttsut[TTddcsref]clldultu{{kk]ztl}ultu{{|lri]bZlldfkkVZT]cdllk{tt|uVZ[mtt}llk{ttmtt]bZedkdc\{||fkk{u{lddzll{{tllultyfdredldd|||JHF*)(tsl~wl|vu{{}{||dc\uzt]bZ{|||tzllri}|u{tt{tt|}kd\r]gztltsl{||llkVTLtt{}uzt{||sre{{tsletsl|lddtt{{{kd\sre|ztll||{zm}f]cVTLztl|vsleu|}mtttslultd\\lksldd|ztts}tsl[TT|{zmdc\ztl{tt|u}{{tult{ttlri}zllulttll|uf]cult}sre|lddredSKJtslcbVtll||||sleVTLb[Uddc|tll{{tllkuzt|zSKJlri{||{ttlri{{kd\uzt}JHF|lri}f]c\UZ]bZult|}}lkslrimtt|zttsSKJlriuztedklks|||ttsttsrfkd\\{u{}}{||:97*)(}}}llk{{tuzttll||ztzllri{zm{tt{tt|utztltsl}{||{zm{||{{{u{|uzt{{tVTLf]c|z{||d\\{zmdkV[[Tddc}|u[[T{{{ttsle||f]c{u{}{tt|tsltt{ultzllddc{tt||{{tu{{ut|utll{zm|||wlv{yl}{ttsle}{{t{tt{ttlddekdut}{{tredkd\VTLsleldd{|||u{tt}|z{ttlldlriztl{{tVZ[tts]bZ{||}tll}{zm{tt{||ztlfkkrg]tt{mtttslddcMRKlkslri}tlltts{{{{}|{||ekduztttsedklrilks|lri[TT{{{u{||kd\{tttlltts{{t:97*)(||}}||}}}}}|u|z[[Tkk]|z|z}{zm||uztlu|z{||f]cttstll{{tkd\tzlf]c\UZ{{tut{tt|zekd}ttszlrg]u||}{||utf]c{||lddkd\tllsle|z||{||{tttts}|||{u{|z|z]cdtts{{|||ttsttsult{||ztlwlzll}{{t\UZ{{t{{tn{||wlldd{zm{u{mwzllbVUkd\lddslef]c[[Tredtlluzt{zm\[bj]\VTL|z{{tVTLlri{tt\UZek]lld{{tt{ddclddUMRlldztltt{ddcleklddf]cv{lksVZTf]ccbV{{lldddc{{ultvlksfkk|zuztlld\[[mtt{||ldd|ztt{}llklddztftts|lriVTL:97*)(}|}}~}u||dc\llk||{{tiq]|zuzttzl{{t|u}sle}ttstlltsluzttzld\\{ttlddrfkkd\{{tdc\{ttutu{{tllrg]UTStllsleultred|u{{t|[[Trg]j]\tllldd{{ttsl{ttzll|uddclld{{{||{||lek{{lldultmtt{ttdc\{tt}{ttuzt{u{}ztl}tslulttts|zultutrg]{{t|uVTLSKJkd\|||{{tztlttsd\\ztltllzllkd\f]clld[TT\UZultVTLsrelri]bZ{ttd\\:97{ttekdntt{ztljV[{u{VTL[[Tdc\|lek|{ttultldd{||lld}|UMRbVU}mttFD;llkuztf]clri{{ult{tt[[TVZ[nv\[b]cdtsllkstsluztf]clddd\\lkstsllek}bVUzll||tll|ddcb[U:97*)(ǜu{zmzll}|{||VZT|sleulttzlut|u{{|]bZ[TT{{ukd\sletsl{zm}{{ttts}{|||ddc{||SKJzllslesreUUY|u|UTS{ttVTLJHF|{u{{{taWMmtttsl{u{}|tzldc\uztut{||}{tt}ultut}||lek||{{llk\[[uzttlllldlks|u|z||}}||utll}u||z}VTLvek]tll{zmaWMlldVTLkk]rg]\[[{tttts{u{|u{{tult||[TTultbVUSKJ}dc\dc\{||ldd[TTlrildddc\|u||v{tzl{{t{{tutll{{t{tt}v{~}edkVZ[\UZVZTfkklldUUY{|||z]cdekd||mttlekVZTlksuzt|u{{lekf]clddultsleult||ztl{ttsreJHF74,}}||v{{{{{ttsl|||{tttslddcMRKuztsre\UZ|||}ztlulldVTL||{u{}lddf]c{{rfk~rr]}v{||}||tzl|z{||lks|{{|u||}z|{{t|tt{ut{tt{{|z|z||tt{mtt||u{{tztl||lek}lrirg]|z{ttddcwl|z{tt}dc\|u}wlcbVkd\ztld\\tzl{{tlldkd\tllllk{ttsred\\llklek}redkd\SKJekd\[[dc\\[[UTSsre\[b{{dc\rfkultult}llk}UMR\[[lldddc{||{u{|{{t{ttulttll}||z|tt{}MRK{{mtt{||tsl{||mtt|tts}{{UMRSKJred|lddd\\ttsu>B9:97}|}}}||u||usre|u{||}tlllrilritslultmtt|z|utsltslrfk{tt}|}kd\[[T|zkk]}redUMRbVUrfk||VTLldd{ttut|uut[[TVTLb[Uztlf]clksut}|zred|ulritsllekzll||z{tt}|||ekd}|tts||tsl~{u{sle{{yleut}|f]cttslriUTSd\\lldtsltzlllk{yfslewltsltll|lek|uuiWUslettsslelek||ultult|VTLztlkk]}{||]bZulddtt{tts{zmbVU}f]c|ttstsl{u{}lektll|zaWMlks{{{||}fkk|\[[VZ[ult\[bttsekdf]c{{{||srett{j]\|uj]\lddlddddclriJHF-1+slev|u}{zm|ztf}{tt[TT~|utll[[Tr]Zrfkult{||llk||UMR}}}tllbVZ{zm}tslkk]uzttslUTSmw\UZ{{SKJkd\ztfdc\{u{|uut||}lld{tt|z}|zll{{t||ekd\UZ{{rg]{zmjcVlldult|||lri||{zmu{{tsltt{llkmttUUYredu{{llk|zuzt{||}|dc\}||lksf]credult{{tsle{ttrr]tt{||||{zm{{twltllztl|j]\VTL|}vd\\ulttllultf]cuztlri{||[[Tztlf]cutek]ekduzt{{ztltllult|lritt{ztllek{{t|z{tt}{{{{\[[||lks|u{{{{VZTllkztlult{{t{{{{tts|tsl}lddbVUbVUtts{{tuzttslvMRK*)(wl{tt}}||||{{}|ljsleutyfl||{{}}}|}ultuztztl\[[vlldddc{ttut}ttsb[UlksttsbVUUMRj]\zllztfiWU}d\\}|utult|lddtslSKJlddred||i\V}|zv{}lldedktsl|uzllddclksztl|}{u{llkkd\~llktsltts}d\\{{tlldllk||||v{|u{{|srekd\{u{lek||uzt{{tddcttsfkkv{}UTS{{t||~uzt{||}kd\kd\rg]lritzluztzltts{tttll|{{||f]cddcut|lrif]c}ddcuztu{{tts[TTllk{tt{u{bVUlek{ttldd{{tJHF*)(zllwlü¿wl}|}}i\Vtslzllllk||tllekdlri}u{{tslelldlrilldaWM[[Tj]\}|zdc\ultutSKJ\[[VTLtllztlv{lekUMR{||\UZzllusledc\{{{u{ut{tt|ullkd\\|{tt}{tt|||ttsfkkv{tllleklks|z|||ult{{ult{||{ttlld}lddUTStsl}}{ttztff]cUTSd\\lri|tsllldVTL[[Tsre{u{lldtslddc{tt}slekd\llku}{tt{{t}tsl}|vkk]llkuztult}ultldd{ttttstts{zmtsl|||lkstzllkslekdc\utsleVTLlek|{||b[U{u{}|sle{||tll{{||vtll{{~tslldd{tt{{\[[mtt|lks|z{{\[[slered{u{|:97*)(}}}}ult{tt}{{tzll}slezllUTSVTLtsltts{||{u{{tt}uttsl{|||u|uUTSredrfkuztdc\VTL}{ttVTL}d\\uztyfd|udc\ut||}|f]c{tt|ttsUTSu{||kd\|sle}{tt}||||}}lektsldc\{zmtt{{{tt{|{{||bVZ{||{ttttsd\\[TT[[Tulttll~tll{u{||}VTL|ztl{||aWMbVUVTLSKJcbVlek[[TaWMMRKrg]{||}d\\v{}{tt{{tVTLub[U{tt\UZ{{tUTSkd\{||ult{{ttslb[Uult{{t|{{tbVU}lek\UZzll|tll]bZ{{t{u{tts|z}ttsf]c{{\[[\[b]cd]cd\[b{u{{{{{uzt|tslu{{{{utztlkd\ztllld[[TD;9:97wl|utult{tt}|ulld|ubVZuzt}{yf}lri}}ttsu||ztl|usrelddkk]kd\[[T[[T{tt|ub[Ured}ultv{|}|||f]cr]ZultcbVjcV{||ultred{{tlldrfklrillk|zrfk}}||||}tllu{{edk|zlld|utddcmtt||~jV[edkzlllriu{{tt{sleuzt|uult{u{rg]mtt|zu{tt[TTbVUb[U[TT3+*kd\kd\ek]cbVlld}ztl}{tt|uttslVTLtsl|u|z{ttv{\[[dc\b[Uddcb[Ub[Ulld\[[{u{lldultllddc\tsllekllk~sleuztlritslu{{ultlddv}}|}|{u{f]cllk}lddlekmttmttdc\tt{mttedktll|}lri\[[{{tuztutslezllkd\red{ttdc\uztlldUMR:97}ljlj}}sle}d\\|ztsltzl|uv||ztlttsv{{{uzt{{lld{ttdc\{{t|lldrfk{||||}||ulldlldbVU{ttv{yleek]{ttddc||]bZtsl{zmlddutdc\aWMddcttsredtll{{tzll{{tlddutb[U}uzt}{ttsle\[[||llktts{||lrifkk|rg]ult|ullk{||llk||ztllkd\llkuztf]c{{ttslsle|ztllultVTLj]\{ttrfk|ub[UvVTL|ulld{{tdc\|uzttslsledkVu|llk{ttv{tslrg]lldVTLlrivttsllkb[Ullkttsrfk{u{llk|z|{{d\\ldd|ulriuultf]cldd|tt{ddctt{{{edklri\[bu{{ultd\\lksek]lkslri\[[{tt}}|tlllddi\Vlriwlj]\||tzlVTLJHFwlv~}|¾Ʋtts}}ttslddtll{{utultlrileku{{t||ttslriulttts}ukk]SKJj]\ldd[TT}|ztt{||zllbVUVTLbVZ{tt{tt{{t]bZyle||d\\|{{t|u}sletsltlltll|ztllek{u{{tt{{tttsttsu{{ultttstllulttt{ddcllkulttlltt{|ulttsl[TTtlltsl||ultztlkd\|ullkkk]UMR}srezll|uyle}SKJllk{ttsreVTLuuzt{{tztl\[[{tt{tt}|j]\{tt|sletllzll}}LKRuztutljedkv{|uttslridc\|u{tt|utsltsl~lksttsf]cztl]cd[TTtzl{||mtt{{u{{UUY]cdkd\uzt|lriUTS{||d\\{tt{u{yleyleyleJHFkd\red{{t{{t}JHF*)(wlüü|||}|ult|rfkjcV{{tldd{|||zultj]\lldldd{{tcbV{ttwl{{tcbVSKJ|ukd\j]\lld{tttll}kd\tsluttyg|zdc\uttll}tll{{t}f]csleuzt}||rg]b[U{u{{u{lld{tt{tttts{|||}|ldd{u{u{{tlllek{{{tt|v{}|||ztl{{tsleVTL[[Tzll}}z|}|redtsldc\]bZldd{||ek]mtt|rfkzllult}kd\tslcbV}edktll{ttuzt{{d\\VTLbVUlrib[Uldddc\dc\}}|uztl{tt{{tf]ctsl{tt|zmttVTLdc\|{{{{tt{ekdddc{{t{|||d\\uztlldtlltll||tllztlllkr]Z}}}kk]{{tFD;74,~~}|||u{{ttsl}sretts}bVUddc}lek||||u{{ttsl}|z|u[[Tj]\\[[tts|u|mttultbVU}VTLcbV[TT}rfkekd\[[uztj]\MRKultrg]sle{{t{tt{||tll||tts|||zj]\yle|||z|llk|z{u{d\\|ult{|||z}}|[TT||ztl{zmrg]d\\ekdztlwltsl}||}ut}utdc\kk]}{{t|||ultlekkk]{ttsle}|yle||[[Tek]tzltllultultutb[Ulri\[[bVZwld\\sleultztl{u{}{zmuttll}|u|tsl{{tMRK{||{||lri{||lddekdd\\f]cddc{tt}{ttcbV|u{{t|u|uVTL-1+}}uzt{{t}}llkdc\uztfsrerg]lddttsulturfk}{{t{tt{||u{{tzllddultsle}}ttsVTLrfkf]cv{|lri{||{||lld|uztlkd\ztl|u[TTdc\|utsltllbVZ||}|tslsre|z|cbVkk]{||{tt{||tsl|utts{||{ttuzt\[[|lksztl|uekd{tt||f]c}dc\{||lldmttrfkldd{tt}|zll}|ztltll||{{t{tt{{t||ukd\kk]tslv|red}|utllztlddclri|z{||uzt}{ttzllztlddcllduzt{{ttll{tt}|zllultldd{u{lldr]g{{sleztl{{uzttsl|u{|||v|{u{{u{{u{rfklri|zulttts}VZ[|zlritts{|||z]bZtslsleztl}||vdc\llk}|ut{tt|z[[T*)(tllwl}ü}}}|{tt}ztf{zmuultb[UlldVTLrg]ultzlltsltllultslef]c}|zllk{{tztlbVU}tll\UZJHF[TTtllllk[TTldd{u{f]c{tt{||sleulti\VVTLVTLkd\ztlekdcbVcbVttstlllddtllut}ldd}}ttsult||{tt|f]c{{t{{}{{|znv{u{||ult{u{|}tts|rg]{tt|jcVSKJjcV{{tf]c{zmtsltzltll\UZzll||}ultv{|u}|||}{||zllf]cdc\tsl{{|u|[[T}{||}|ek]~|slef]cmttzlltllbVZSKJ{||||{{{{t{||\UZv{{ttlekutztl{tt}VTL}{{tu{{ultult{ttuzt|z}{ttb[U|utllyle{ttutv}sreD;9*)(}||v}~sre}}|||}|zltsl}tt{v{}VTLultulttt{|f]c{||kd\}srebVUbVUult{u{\[[sleultlld||b[UtslbVUSKJFD;]bZSKJsrekd\[[Tlddd\\ztltzlttskk]sleut|u{ttvztl|||z|zlksf]ctt{fkklksUMR}tsl{{lldleku||||lldsletslb[Uztfldd{tt{{t{{tlldultcbVut|u|uult}j]\{tttts|vrg]tll{ttd\\uzt{{tuztrg]bVZlddlddtts|u}MRK|tslrfkv{UMRbVZ}}|{{tll{tt}sretts}f]cut}|{{tdc\dc\}||||ddcf]cf]c{||{{ut}zlltllredj]\|utslj]\red||ztlJHF*)(~utüütsl||ztluztztlutkd\rfklddutJHFuzt{||{||uzttzl{{t{{lrizllrg]{||lddlrijcVttsv{ultf]cSKJtll{||}|{{t{tt[[TUTSVTLj]\llklddlksttsv}{||r]Zekdddc|||ztl{||||}j]\{ttllkuzt{{|u{{{||{{lksedk{u{tsl|z|zttslks{{tkd\lksttsedk{tt{u{{{tultllddc\redredut{tttts||ttstllkd\yfdzll|uultdc\d\\{tt{u{}ldd{{tttsv|lddllk{tt}|usre{||tsllddlek|uUMRultbVZtts[[T{u{||tsl|tllult||ultbVU{{td\\[TT|tzlfkk}sre{||lri|uldd{||{||||}|{u{wl{zmtlld\\utwlv|rg]rg]FD;74,}zj]\ü|zv}||{{tslellk{{t||{tttt{{tttts{zmztl||tll||}|||zlddtts[[T{{ttzlkd\{tt{u{[TTd\\[TT}{tt{{tddc[TTd\\ekdrg]kk]bVZllkldd[TTtzllritll[[Td\\b[U{{t{tt{{ttts}|}}|{u{||u{{{{tllktt{{{|}tts}|vtt{}|{u{tll{u{{u{}||llkdc\red}zf]c}{||ut{{tUTS|utllsreyletts}|yfd{{t||sleyfl}{{tut}{{||v{{u{}v{VTLv{{edktllf]c[[Tslelddllk{zmu{{ultztlrg]{zm{{t|tll{{t|]bZtt{|zlek|}ddclekd\\kd\llkddc{tt}|uztldd\UZf]c}|v>B93+*||ü}}||ztt{u~lld{ttkd\tts{ttlldj]\tllv{f]ctsl{tt{ttrg]ddctll|}|llkiWU]bZtlluzttsl{{t{||ultlekdc\}|JHF{|||ttsrfktllJHF{||v{srellkddckd\b[Utslslekk]d\\{|||lekyleztl}ddctts}u{{uzt|zuztddc\[[|z|}\[bf]clksldd||}}}|j]\lldi\V{{tzllkd\ldd}|i\V||llktlllld{||llk}|b[UbVUutbVZ{ttsrettszll}ult||{tt}lek{{t}[[Tddcttstlledktll{{|}srecbV{{tsreu{{kk]tll|vzll|u||||ult{tt|{||{||n{u{}ult|tsld\\rfk}|{zmtll{zm}|zllyleztlkd\kd\:97*)(}u|||ztf{u{yfdut{zmulddtslv{}}}llksle{{|uldd}tzl|uddclri|FD;d\\u{{]bZllkd\\rfklddlddsrezllSKJ}tsl|zsleVTLtsldc\|lddsleVTLzlllldrg]srelddultd\\{u{||{tt|uztl||mttllk|{{|}||ddclriuzt{ttlkslekUUY}||kk]ldddc\}tllsrekd\bVZultlldv{bVZbVZlddlri~kd\v}{{|}|f]clddultldd{||llkd\\kk]{u{v{|||||{||tsl{u{VTL{zmbVUddcttslriztltllUTSulttsl|vttsrg]||tslv||kd\[[T{{tll|lek||{tt|vutztfzllsle||kd\wl:97*)(lddut}}{{tuut|u||}ttswlv{{{t|u|u}|sle{||f]crfk{{t||}zll{||d\\rg]redlddztlllkf]c}rfk[[Ttll}tt{j]\VTLkk]lldslettslridc\||tll}dc\tyg|ztllddj]\mtt{zmut{{t||f]cv{tlllddd\\}}ekd|{{tt{|z{u{tlllek{||~{{{{tllkleksle|zsle|u{ttllklriredkk]VZTzllredbVZ[[T{{t{tt}ultddc{tt|utredrfk{u{{{{||}lriztl]bZ\[[utVTL|lldlriddctsl{zm[[Tf]ctsl{tt}v{lek{||bVZ}{u{ztl{{tkd\uztuzt|z|{{||ztt{ut{||{{t{zmlldlldd\\sletll|uzllldd}|yle|}>B9&&~{tt}u{{t|||utrr]redtsl{u{tsl}ztl}|{{t}{{t{||zllsle|tll}}ult{tttllsleultllksrettskd\\[[tllf]csreztld\\ttstll}{{tVTLUTS[[Tlldztlkd\b[U{||}kk]ekduzt||kd\lddlkszll||{ttkd\u{{{{ddcek]lks|mtt|edk}||{u{}ddc{||||~cbVkk]tllkd\f]clrib[U||j]\ztf{||{u{{zm||ldd{tt|ud\\}||rfk}|utsltts{||tslkk]}ut{ttredsrej]\ttsUTStts{u{{||{tt~ult}|uztttszll{|||u~{zmztl|zllsrelri}|zekdekdslellkldd|u|u|tzl|u{zm|{tt}|vredkk]i\VD;9*)({{t|v||}u|uutztl}}yleb[U[[Tzllf]czll~{ttulttsl{u{kd\{tt|zkd\||zll|ultlldzll|v}|sledkVv{dc\dc\{tt||}lridc\kk]mttSKJ{{trfkFD;sre{{ttll{{t|[TTb[Usle}b[U{{tsre||tll{||tll}~v{||tt{lldultuztmtt{{{{{{lrilks|z{u{uztult||}ttslddlks{u{{{tvu{{{u{|}yle{||tsl{{ztlkd\d\\f]cuzt}tzl{||bVUrg]}|v{tttslkd\}|||{u{{ttrg]kd\[[Tlldtsl]bZult{tt{{sleztlztfztlut}|||uztlsle}lek|zwl}tll}ek]{||{||tsl||{||{tt||JHF}i\Vutztl}|wlredFD;*)(v|}ut~vldd{ttubVU{||tsltll{{t{||ultuztttsu}|{tt{|||u}lri}sleldd}FD;{||ult|z}lri{ttttsllkekdu{{lldb[UlriutUTStygtllsreuzt}ztlsre[TTaWM|zztlrfklld||}tll}tll|||zlkslrilek]cd||lkstt{}fkkllk{ttultleksle{{{{t{{t|z}{u{ultrfktsltt{rg]lldutUTSzllyleslekk]{{t{||lddutlekf]c{u{{u{r]g||||{|||}tllredtzlsleVTL|z{{ttllttsllk{ttv{||]bZ{{vulttsl||}tsl{{t}}mttmttu{{{{tlddlldtzlllk{ttj]\lddsle|||uztlsletslrg]JHF*)(~{{t}}|uzll}{{t}|v|z}{||srelri}}}|u|z{{}{{tyfl}slellkr]ZbVZllkred{ttddc[[Tlld}lldlld||tzltll[[Tddc{||rg]dc\b[Usresleslensre}{{ttll|lritzlzlltll}tts{{ult]cd|z{{||u{{|mttlriultuzt{{ultf]clks\[bmttultlddztldc\kk]{ttut}llkddc|sre{ttutzllztl{{{u{kd\d\\ut{u{utred{tt{||ztltllkk]\[[lri}ult}uztwl|zmtt}tt{mtt]bZ}tts}sretts{u{{||v{ztl[[T}zl}lldttscbVlldttssrerg]sle{{tutztl|ui\VzllutllJHF!¾ƣu}{{t}{tt{{tttsztl|u{|||lld}tsldc\dc\kk]tsl|ut|||}zllllk{u{{ttb[U}{{ttsl{{lribVZlritlllriVZTUTStlluztf]c}mtttslkd\||vuztyle{zm{u{JHF||||{zm|}{{||lkslksekd}|fkkdc\ulttt{|lddd\\tsl{u{tts|uVTLn}ultf]cult|zlks{{||ztl|u{{tlekttsldd{||lddrfkbVZdc\{{tu{{tll{{t{tt|ut}||z}|u{zmkd\u{{ztlrg]||llku}}|||}{ttlri{tt}|}{||sre{{t|z{{t||sletsl}{{t{{tztl[[Tu[TTsleultb[Uztl}|{tt}v{ult>B9*)(||v{v{zlltts~dc\lekylev{{{{{ldd{||{{td\\[[Tuztllkn}ulddsle|zztlu|u||tts|ztts{tt|u{{t[[T\[[|||ldd}|tts|||}|sleslejcV||kd\tzltts|u|v}{tt{||{{{{}{{tslmttmttmttmttuzt\[b\[[{||lddlekultult|rfk{ttldddc\}tsllldttsldddc\[[Tlldlld{{t{{[[TbVUkd\ttslddedkzll\UZd\\tll{||uztlriultzll\UZ{{ttyg[TTMRK\[[llk\UZ{ttult}wlkk]||ttsztldc\{{tulld||tzl|z{zmkd\|lriiq]lldtllsrebVU{zmztlztl}|utllyle}D;9*)(}wl|wl||kd\{{t{||{tt{{tu{||{{trfkrg]}}{{t{u{wl|{tt{u{d\\|uztlu|ztl|SKJttssle}bVU[[Tultllkultlks|llk|u[[Td\\tts|u{{t|u|u|u{{tf]csle||{{tsleztllldv{ztltsltlledklld{{{{|ztt{|mtt{{mttu{{{||{{llduztd\\\UZlks[[Tf]clld{u{llk{{tlddcbVultlddult||}dc\{{t{tt[TTUMR{ttb[UmwVTLrfk{u{VTLSKJ{tt{{tv{vredldd[TTtsldc\|utsle}z}ekdllkllkldd{||{{uzt}sre}|vlekllk}|zll}||tts|{{t}|ztlb[U|{tt{||{{tlddtts|uutztl||ut||wl}:97*)(||zutll{||tts||f]cultttssleb[Uvu]bZ[TT|zddcb[U|z|u|tsl}kd\{zmv{||llklddsle}ultttsb[U}cbV}||tlli\VVTLSKJkd\tll{{t|uyletll|u}|utslsle{||b[U{tttsltsldc\{u{{ttlks{{{||uzt{{mtt|mtt|z||z|{||llk{ttlek{||{{d\\bVZbVZ{u{|z}|uzlulttlluztdc\redUTSztfkk]SKJ|lekkd\f]ctllVTLmttd\\tsl|||u\[[kd\{||{ttsle}ldd{{v{}lksmtttllsleultztlultUMRlddutllkuztttszllsre|}|u}}lddztfsre||]bZred||u{{twlztlztl||tslwlJHF*)(Ƶ}u|v|u}yle}||uuztmtt{{td\\||u}{zm|u||u}|{||f]csresre}ultllksle{||ultultuztlldd\\{u{VTL]bZ{{tsre{zm}JHFSKJdc\dc\|uztf[[Ttllkd\|u||}}lldkd\ztlj]\{tttsl{{t{ttttstllddclddlekmttu{{mtt|tt{{{{{ttslkstt{|}||ttsf]c}bVZylelri|zsre{||kk]{|||uldd|zkd\rg]|vsreztlrfk|}rfkztlkk]b[U|u}zlltlltllf]c{{trfkrg]rfkuzt|z}{u{leklektlllek||ttsmttut}|vzllnllk}|ultf]c}|lddtsl{u{}}tll{||}wl}{{t[[Tdc\tlltsl}|u|||u}{tt~ztlD;9*)(uut}lldwl{tt{u{zll{{ttts}MRKsre|z}|udc\||{{tlriuut}|utsl}|||zll||||ztld\\ddcsre\UZ{{t|}|u{{tdc\j]\f]cVTLslelddttsuzt}}b[Utll|vyleztlkd\kd\wlf]ctsl}}d\\||}{ttllktts{{n{{mttlkslksultlek|mtttt{ult]cdv{bVZ{tttt{tsllld{{tzl{{tutslf]cj]\llk{{t{{tut{{tu{{tlek{zm|urfklks||kd\}|SKJf]ccbV||sreldd}slesletsl|uzllttsultlriekd|{tt||{tt{||{ttrfk||}{u{}tll|ud\\|u[[Ttsl|usrekd\lld}lldrg]u|v{kd\||r]Zsle|uredredVTLcbVFD;*)(|v{zm{{t}}{{tu|u{u{||{{taWMllk|{{tredllk{||ut{tt}tzlSKJJHFVTL{tt{zmb[U|ult{ttutldd[TTult{ttd\\slej]\kd\|zudc\slelddkd\f]ctslb[UJHF|tts{u{dc\{tt|u}llkvtts|lddtts{{ttslcbV||{{t}ttslks||{{{{VZ[]bZfkklksleklkslrimtt|\[[\UZUUY{ttldd{u{{{tcbV[[Ttllultldd{||sretsltts{zmkk]lld}||{||ud\\tll|zv{kd\b[Utzlu{{tsl|ztsl||tll}{{tkd\|vVTLlldslelri{tt{u{lksmtttt{|z|}||{||tsl{ttult}{||uztkd\||}|}bVU{ttd\\ttstsluuzttsl|{||{{tuztf]cult{zmzlluzt}ztlrg]d\\j]\tllrg]rg]}|||{{tztlUTS*)(}|u{ttrfk{{tztltll{{t{||rg]{{t{||zll|z||tts{||{{t\[[llk|u}||ztygv{ttsj]\}||u{u{bVUztl}VTL]bZlddllk\[[UMRtll[[Ttsl{||dc\{zmlrij]\tll{zmtsltlldc\{{tztl}{ttkd\VTLlldbVUb[U{{t}|}rfk}|VTLsre{{|tt{|{{llknv{||mttd\\{{fkk|ttsf]cUTSlks\[[bVZ}v|u}~sre|llk{{tttsllkd\\ztllri[[T||z}sre}}dc\tllf]c{tt}|tts{{{u{sle{{t|z}{{t{{{{t[TTtllVZT[[T||{|||bVU{u{llk|zu{{||{||\UZ{{lrizllzllsletts{ttv|z{{tlddj]\ztl{zmslesle|}ztltsl{ttSKJlldek]v||sleutsleSKJ*)(}ƨ}{zm{zmtll}|||tll{u{lld[TTlrisle}|{{t|zek]{tt}|usle{{tlddzll{tt||{ttkd\{u{{{tttstts}lldSKJ]bZuztkd\tsl}{{ttllVTLd\\[TTD;9{{tlldv{sre{||kd\ztldkVkd\|b[U{{t}ztl|{{tlrislelksmttmtt{{VZ[{{fkkmtt{u{ulttsllksd\\d\\llk|ukk]sreuztttskd\tt{\[[lldredztl{{t}llksle|utll\UZ||ult{u{{tt{||dc\jcVtslult{{t|u{zm{{ttts{tt{u{j]\d\\edkekd|{{{||tts{{}u{{{{ttts}uzt|{ttv{|{||{{t}ttslldttsult{{t}lridc\b[UcbV|llkkk]|utsl{ttutztlrg]ddc||redD;9:97wl}||}}{tt||||{tt|uuztrg]llktts{zmultb[U\[[[TT{||kd\{||{ttsled\\||uv{tslj]\dc\sled\\||kd\lek{{ddcldd\[[dc\[[Td\\f]cslesle{ttb[U[TT]bZrfklddut|bVZj]\ztlztl}|kd\|z{zmztf|z}}rfktsl{|||zkd\tt{{{lri{ttlri\[[|mtt{{]cd\[bttsf]c{u{lks{{f]cddc\UZ{|||lld{{t|kk]kk]{{tlrildd{{ttllddcult]bZ~uzt{{tb[U}f]cf]c{ttj]\{ttsresle|ulddult{{ttts||u{|||u|zultUTS{ttttsttsf]c{{{u{ztl}ut{{{||}{||lrizll}uu{|||uuzttts||{{ttsluzt|||sre{{twl{{ttslztfkd\ztl}|sre||{{ttsl{||FD;*)(vvu||redredu{u{||}{ttztl}{||}lld{{tred[TTldd{ttslelekrg]sledc\tsl|||}tllv{[[Td\\|uztltll{||}uztf]c]bZlddb[Ulddd\\{u{{{tutulddvredVTLkk]lddbVZ|usleldd}|ldd{ttllk||u|u{{t}{||sleutztl||lri|{{{{tt{]cdult{{mtt{{|tt{edkf]clksllkekdmtt{{tlks{u{lek\[[d\\ek]lldllkkd\|tslsreldd\[[d\\sle}llktslsleddclddtt{utv{{ttultldd|uVTLkd\UTSu|}{tt{||uztldd]bZtsllks{{{u{lksult[[Tlektslllkuzt|ztzl}lek}|zlld{u{||b[USKJd\\mwFD;[TTllk|ttsudc\{tt{zmek]llkcbV[[T}{{t]bZtllsre{ttjcVyle}sleSKJ*)(}|}{ttlld{ttlld||}|z{||{{t|z{{ttts{||ult|u}|v{zmtygrfk{{t}|{tttsl||zll||ztl}{ttldd[TT}{{tddc{||tsltlltsl{u{ek]lektllrfkztl{ttlddkk]kd\ddcf]ckd\b[Uekdd\\zllaWMMRK||{||lri{ttj]\{zm}{yf|zkk]}{tt}uztddc||zlksmttuztllkmttf]c\[bultekdlksult||iq]b[U{u{}{u{\[[ldd{{t{{t[TT{{tlldtsl{zmlld}dc\sleddcd\\aWMlri|{{tVTLlldtllr]gtt{}lddd\\tslutddcztlddcb[U{||[[Tlld{{t]bZ|||u{{{||\UZ\UZSKJkd\ekdtll{tt|zf]c{||lksfkktslttsb[Uf]c{||v{{u{{ttllktsltllaWM|z{tt{||ztl{ttj]\tsl||dc\dc\lri{zm{{[[Tred|u||lldtslsre{zmtsltllcbV{ttkd\ztl{{t{ttmttlddD;9:97||uzt{ttv{|{u{|u{zm|z||{ttultlri|z||z}||{zmtsldc\red{{t||{|||slered{u{{ttredlldslelddlekj]\dc\SKJslelritslb[U{||ddcuzt{{t{{tldd{{t[TT{tt{zmSKJ|utsle{{ttll{ttztlv[TTslelrilld{ttj]\}|kd\tslllkztlbVZtts}{{tdc\lkslksddc{{{{llkmttu{{ddc\[[UUYUMR{{{{uztmttlddlekdc\ddclekllkult}lrilddlriu{ttd\\lek{{t{||ztlldd|zkk]VTL{u{j]\b[U\[bd\\[TT|ub[Ured{||[TTllkVTLb[Uredb[Uj]\]cd{||j]\tllttsekd{u{{||{ttVTLlri{u{||tll{||tllkd\{||]cd|zdc\lks||ddcbVUSKJztlVTL[TTlrittscbV{||sletygtts|zrg]||||{u{lddztllri}|z}dc\ldd[[T|zttskd\[[Td\\tslb[Utzl[[T[TTkd\{||}red|||tslj]\SKJ*)(ult}{tt|uztl||{zm|wl}{{tlks}{{t{|||zztlsredc\sre{tt}|ut}{{tzllekdttslddllktll{||kk]{{t|uultulttll\[b]bZbVU[TTtslf]c{||tsltlltll}lldkk]tlld\\kd\j]\|uFD;tll{ttsleVTLb[UlddVTLztl||ttsultkd\cbVdc\{zmusletts}tll[[T]cdslelks{{{{edkddc{||u{{mtt\UZ\[bJHFVZ[lkstt{ddclks}lks[[Td\\lekllkf]ctts}VTLtslttsbVZ[[T[TTzllkk]||mttzldc\lks||VTL\UZSKJd\\lekkd\zlllddlldddcSKJVTL[TTtsl}\[[dc\tslVTLVTLlrildd\UZlldd\\{u{llkllk{ttttstllbVZlkslridc\uztmtt}lektlluztlekj]\b[Uu{{d\\{tttts[TTVTLtslVTLkk]ddc|uddc}{{t}tll||ekd|[TTdc\lld|uultttsf]c{||kk]sredc\kd\lddztlddcb[Uutlld{tt||sleJHF*)(}|uu|{tttsl{{tt{{ttsletsl{{tzllztlllk||UTSkk]dc\ekd{ttlddtslulttsltslkd\|z|zkk]{||d\\{||lddlri|utts}ttsSKJddctllult[[TVTLSKJllkJHFSKJ\UZdc\ldd|tsllldtslb[UUTS[TT]bZVTLj]\b[UVTL{tttslVTLzllztlztfldd{u{lrilddJHFb[UVTLkd\redtsltts||{ttllk{{tsllldUTSttsekd{{]cdf]cllktts{{\[bfkkVZ[}u{{lksddc|{u{{tttlledkuztlekutuzttsltslVTLtt{\[[d\\[TTekdj]\{{tlddkk]tzl[TT\[[d\\b[Umttllkllkldddc\\UZlekbVZlldtllzll}tslj]\[[Tdc\lldttslldllkuztkd\edk\UZVTLb[UJHFf]cUTSu{{ddcb[Ukd\UTSlriuzt]bZttslddMRK{ttUMR[TT{ttllk[[TlddcbV\[[{ttlldj]\[[Ttslsre||kd\ddcb[Uddctll]bZkd\|lldtlltsl||{||ult}d\\dc\ztldc\kd\tsl{u{sle|vsleddc{{tlld[TTD;9ztl||{|||{{t|zllk|z|tts{||llkd\\kd\{u{}lrilldlldztlkk]lldtsltslu{{sleedk[[TVTLrfkf]credkd\kd\]cd|zsle}lldtts{||ekdf]ctlllld|zslelldtts{tt{||tllcbVzll[TTdc\d\\{u{]bZbVU{tt\[[VTL[[Tkd\\[bb[U[TTtllslekd\|z[TTutztl\[[b[Usletsl{||tts[TT[TTlld|zd\\tsl{{t{{t{||lldtllttsd\\[[Td\\{{lrif]cfkklkslks|z{||mtttt{VZ[LKRVZ[mttmtt]cd\[[\[bd\\d\\lddlekmttedk\[[r]guztekdekdd\\lridc\{{t[TTlldd\\FD;[TTd\\kd\tslUTSlld\[[[TTkk]f]cj]\f]cf]c[TTtllf]cbVZUTSllkkd\ttslksllkd\\SKJmttUTSUMRd\\tllSKJ[TT\[br]gf]cUMRUTSztlddcslelkszlltll\UZtllVTL{||llddc\lldtt{VZTtt{}ultd\\d\\tt{mttekdlkssleVTLkd\{{tdc\dc\{{tldd[TTztl\[[dc\d\\b[Uddcd\\f]cb[UcbVlldlld{{ttslcbV[TTztlVTLkd\dc\lldtllb[Urg][TTUTSredkd\[TT:97{||}|ttsllk|tt{mtt|}{{{u{ztltts{u{{u{llk{{}{tt{ttkd\kd\dc\{u{tsldc\slelld|{{{||{|||tllttstlldc\{ttkd\{||{u{|zlld|zVZTSKJkd\lek|ztts\[[JHFf]clrildd{||sledc\ttsttsllddc\\[[JHFd\\UTSdc\ztfkd\\UZsleVTLd\\tslaWM[[Tllk{ttkd\lldlritsl||uzttts{||{{tlld{u{kk]ztf}{{tlridc\lddultekd]bZdc\llkSKJtlllksmttlksVZ[f]cllkUTSlks\[b\[btt{\[bult\[bVTL\[bJHFUUY[TTdc\UMR\[bd\\UTS[[T[[TVTLMRK\[[|ztllttsekdd\\b[USKJUTSSKJ\[[dc\lldttsllkj]\d\\leklddUMR[TT{tt\UZleklldtsld\\VTLkd\[TTuzt[[Tddcu{{{ttd\\JHFmtt[TTldddc\\UZtts{u{u{{SKJ\UZ[TTVTLUTSekdlldedkddc[TTllkddcddclridc\JHFedkttsult\[[[[Tllktslf]ccbV[TTkk][[TlldSKJSKJtlltlld\\fkkd\\sretslllkVTL{||{{t[[TVTLUTSd\\ldd[[Tdc\tllrg]]bZttskd\ldddc\d\\[TTztlJHF*)(||{{ttzl{||tll|llk|zttstll|tt{ldd}b[U{||ddcllk[[Tultlksultu{{{ttd\\JHFdc\VTLek]lld{{t{{tlddldd||sre|||lri||ddc{||{{t|u{{ddcddcedk{u{{u{tsl[[TJHF[[T\[[ddc{u{lddultkd\f]c[TTddcd\\dc\cbVlddultuztkk]b[U[TT[[TVTL\UZtllVTLb[Udc\kd\redlldVTLb[Ulldtzl{||tllekddc\lldtll{{tlddztltsl]bZlrilldlddttslld{||\[[UUY]cdedktt{mtt\[b]cd\[bUUY\[bmttlkslksUUYfkkUTS]bZUMRJHFUMR:97JHF:97UTSUTSJHFllkllkVTLSKJllk]cdcbVLKRb[Ub[U[[T\[[SKJVTL\[[dc\lriddcVTLdc\llkf]cllklddlldlddUMRUTStts\[bkd\VTLllkJHFtllbVZuztttslddddcbVU[TT[TTUTSJHFJHFddcUUYUMR\[bJHFJHF[TTdc\d\\ekd[TTUTSJHFVTL\[[]cdD;9f]clld]bZd\\VTLSKJVTLJHFllktt{|uekdUTS]bZdc\JHFkd\[TT[TTtll[[TVTL]cdb[U[[TSKJVTLdc\lldFD;[TTUTSSKJJHF[TT[TTdc\dc\b[Ulddd\\VTL\[[bVUd\\jcVkd\}JHF-1+{tt|u}{tttts{||{ttttsdc\uztllkttsu{{tts|zJHFedkleksle\[[sleek]llk\UZf]c{{tf]cedkSKJtts{ttlddlrillddc\\[[]bZ{{ttslUTSldd{u{llklksfkkllkultkd\[[TUTS]bZ[[Td\\tllUTSddctts\[[\[[dc\VZTJHFJHFJHFFD;UTSVZTVZTtllredd\\VTLVTLdc\kd\UTSd\\kk]dc\ddcUTS[TTdc\bVUcbVJHF[[T]bZtllb[UJHFd\\tllb[Umtt[TTUTS\[[VTLb[U[TTdc\VTLb[U[[Tddc[TTUTS\[[cbVVTLddcf]cf]cUTSlksllkVZ[]cdultmtt\[[edk\UZJHFLKRUMRJHFLKRUTSJHFJHF)+2:97F=CF=CJHF:97D;9d\\d\\VTLdc\VZTLKRJHFUTSJHFFD;JHFFD;UTSMSStllMRKUTS\[[UTS\[[\[[[TTf]cSKJSKJD;9JHFUTSUMRlddVZTUTSlekdc\lks\[[[TTddcF=CSKJFD;UMR:97JHF:97:97MSS:97\[[UUY\[bllkult{||JHFJHFFD;>B9>ECJHF:97JHFLKRUTS\UZ[TTUMRMRKd\\VTLJHFekdlldlddJHFF=CFD;MRKSKJSKJlldlddSKJllkUTS[[TJHFJHFD;9D;9:97:97>B9:97>EC:97D;9:97JHF[[TSKJSKJlddJHFFD;\UZJHFJHFSKJSKJSKJ*)(ztl{{t||{{t{tt{ttllkdc\lldldddc\[[TJHFedkdc\ddcmttlkstts]bZllklddtllultVTLMRKJHFUTS]bZ[[TUTSJHF\[[UTS\[[tslbVZllktsl[TT]bZ]bZFD;JHF>ECJHFJHFJHFVZ[UTSddc[[Td\\UTSVTLd\\SKJ\[[\[[UTSVZ[JHFJHFD;9dc\JHFJHFUTSSKJUTSJHFJHFd\\JHFJHFJHF:97UTSJHF:97FD;JHFVTLJHFJHFJHFdc\[[Tkd\JHFD;9JHFJHFJHFFD;FD;SKJ[TT[[Tldd\[[JHFddc\[[lldd\\[[Tddcllddc\VTLFD;JHF]cdllkddcldd\[[[TTd\\]cd\[[]cdJHFLKRJHF]cd*)($:97*)(*)(?;C:97:97*)()+2:97:97:97:97UUY3+**)(74,!*)(*)(*)(*)(*)(74,*)(*)(*)(JHF*)(:97*)(:97*)(74,3+**)(*)(3+**)(*)(7-2*)(:97*)(*)(*)(:97*)(,55*)(*)(*)(*)(*)(*)(*)(:97&&%&*)(>EC-1+%&FD;)+2LKRLKRUTS?;CJHF*)($*)(*)(,55*)(*)(F=C*)(*)(*)(*)(*)(74,:97-1+*)(*)(*)(:97:97JHF*)(74,-1+*)(*)(*)(74,3+**)(*)(&&*)(*)(*)(!*)(*)(*)(*)(*)(*)(*)(*)(:97*)(*)(:97*)(*)(D;9:97*)(-1+VTLSKJFD;\UZ[[TJHFSKJJHFLKRJHFSKJSKJ3+*:97:97JHFJHF74,:97LKRUTSMRKMRKJHF:97*)(:97*)(:973+*:97*)(:97:97JHFLKR>ECJHF,55:977-2*)(-1+74,:9774,-1+3+**)(*)()+2*)(*)(*)()+2:97JHF:97,55*)(*)(*)()+2*)(*)(?;C*)(74,:97*)(:977-2*)(JHF*)(&&*)(*)(3+*:97*)(3+**)(*)(*)(*)(74,3+*&&&&3+**)(*)(&&-1+*)(*)(*)(SKJ*)(*)(:97*)(D;9:97:9774,74,*)(*)(>EC*)(:97*)(*)(*)(*)(*)(*)(74,*)(-1+,55,55:97)+2*)(,55:97:97:97LKRJHF?;CUUYf]c\[b\[b\[[[TTSKJUMRJHFllkUTSllkJHFMRKVTLUTSJHFF=CVTLJHFFD;JHFJHFUTSJHFJHF>ECJHF>ECJHFJHFJHF:97LKR:97F=CJHF:97D;9:97:97:97:97FD;:97:97:97JHFF=CLKRJHF\[b\[[MRKMRKdc\]cdLKRek]UTS\[bMSSMRKJHF\UZJHFJHFVZTMRKJHFMRKJHFJHFJHF:97:97:97:97JHF>B9JHFMRKJHF:97D;9UMRVTLJHFFD;VTL[[TD;9JHFFD;>B9:97:97>B9D;9FD;JHFJHF>B9D;9:97JHFD;9FD;UTSSKJD;9FD;SKJD;9SKJJHF[TT[TTJHFJHFVTLVTL:97[[Tdc\VTLllkbVZf]cek]>B9VZ[\UZ>EC\[[VZ[UUY\[[\[bLKRVZTfkk]cd\[[[TTUTS>B9MRKMSSJHFSKJddc[TTVTLVZ[UUY\UZUMR\[[MRKllkVTLJHF:97SKJ[[TVZTJHFJHFVTL\[bJHFMSS[TTLKRJHF:97JHFD;9:97JHF:97JHF\[bf]cJHF:97VTLJHFJHF>EC:97>B9JHFSKJ:97:97SKJJHFJHFJHFJHFFD;SKJD;9MRKJHF74,JHF:97:97JHFFD;UMRD;9SKJbVUFD;MRKb[UJHF:97UMRMRKMSSLKRFD;:97FD;JHFekd:97JHFUMR]bZJHFUTS:97JHF\[[LKR\[bLKRMSSJHFLKRMSSOS`OS`JHF|ztlv{tsl}Ĥv}ult}wlztl}[[T\UZ¿¾Ƶ}Ʀü||}||lks)+2?;CLKRdc\ultd\\7-2>/Att{\[[f]c]bZ!:97%lrilridc\ddc:97SKJlddlldSKJ|:97*)(JHF[[TLKRJHF\[[JHFSKJVTLldd{u{f]ckd\]cdddcred{zmSKJVTL3+*:97%JHF\UZlld7-274,%&$VZTuzttll[[TUTSMSSVZTVTLVTLtlldc\tll[[Tddc[[TJHFJHFUMRekdddcUTS74,@/7 74,73FD;VTLRP:tslredbVUVTLD;9MRKFD;FD;D;9C8.]bZ&&&&3+*FD;D;93+*3)D;9dc\VTL3)FD;%&&0FD;SKJD;9D;9VTLdc\[[TVTLjcV||SKJ>R.VTLJHFGSu]cd|z|zlrityglldsreD;9SKJsre{{t|u{ttlddf]cMRK}{tttllek]f]c[[T]cdUMRD;9UUY"%VTLFD;JHF\[bf]cMRKfkktsllri[[T ,55-1+>B9VTLUMRD;9VTLVTLlldSKJ\[[sre{||VTLd\\{ttSKJfkkJHFD;9SKJsleVTLcbV>B9VTLVTLyledc\3+*"FD;FD;d\\D;9Q?@SKJ >B9*)(:97*)(]bZ7-2SKJ3+*dc\zllUTScbVJHFlldtslJHFlldf]cJHF\[[LKR%&-1+,55{{VZ[llkUMRlks]bZJHFF=CLKRf]clddlek\UZf]cMSSSKJf]c>ECf]c74,[[T]bZVTLttstll|]bZedk{u{[TT{ttUTSLKR\[bUMRJHFVZTUTSJHFJHFVTLdc\lldultVTLtlldc\uzt{ttSKJSKJSKJ74,UTSf]cf]cJHFkk]JHFD;974,*)(>B9*)(JHF3+*fkkultUTSJHF\UZUMRVZ[[TTd\\lriowd\\uzt]bZtsl\UZLKRultuzttts\[[lddFD;-1+FD;>B9%)*)("*)(FD;SKJFD;j]\UMR[[TFD;SKJ:97C8.D;9D;9SKJFD;JHFFD;JHFtllUMR:97SKJb[UVTLFD;JHFVTLD;9F=CPF=JHFD;9D;9SKJJHFVTLJHFvu|u|vlricbVJHFtsl\[bSKJ7-2[TTuzt|lldllklrib[UJHFb[Uultult[[Tlek{{tlldSKJ\[b{{{{tj]\tllultekdSKJSKJ\UZD;9ldd\[[[[TekdF=CVZ[JHFdc\uztultlri:97]cdfkkMRKJHFttsddc\[bJHF[[Tdc\]bZlriUTSddcVTLSKJUTSf]clri[TTztlb[UVTLVTLlldVTLVTLlddd\\cbVSKJMRKVTLkd\{ttb[UUMR\UZFD;*)(:97[[TFD;[TT:97>EC:97[[TJHFFD;sle[[TMRKVZTSKJVZT\[[>EC\[[UUYUUY>EC>ECJHFJHFUUY]cdJHF\UZ]cdlksddc>ECedkJHFddcj]\UTS\UZ\[[edkbVZf]c\[b[TT:97D;9[[TMRKb[Uek]VZTUUYJHFUUYVTL]cdd\\f]c\[b\[bD;9lriMSSSKJd\\[[T[TTsle\UZ\[[UTSb[Ullk\[bSKJtll\[[SKJSKJ*)(UMRUMRSKJJHF&&3+*:97JHF74,:97JHFLKRSKJf]c]cdf]c]cdedkVTLb[UVZTmtt[[T[[T]bZ[[TUTSu{{ddc{{t{u{lksf]c]bZ]bZVTLlri&&&&SKJaWM[TT[[TVTL[TTb[UFD;3+*FD;FD;VTL74,D;9VTLSKJ[TTd\\[TTD;9SKJJHFdc\JHFJHF[[T[TTJHFbVUUTSaWM[TTVTLtllllkSKJtsl{{t{zm{u{kk]]bZ|zMRKdc\VTLJHF%UMR{||ttstts\[[|tzlrfklddult{||ekdlddldddc\ddcb[Umtttt{ldddc\{ttultlriUMRUUY|dc\lekmttVTL\[[F=CMRKVTLfkk{u{{ttMSSMRK\[b:97ekdb[Udc\ult\[[:97dc\JHF[TTMRKddcu{{dc\j]\d\\[TTllkSKJkd\j]\d\\tll[[TlddmttVTLlldVTLJHFFD;D;9SKJJHF[TTddcD;9D;9*)(FD;b[U:97SKJsle:97SKJVTLddcVTLlld{||VZTddc{||llkF=CultlrimttJHF]cd>ECLKR:97MSS\[bMSS]cdVTL\[bddc>ECSKJ\[b\[[{||}JHFddcbVUulttts\[[FD;:97VTL3+*JHFVTL:97d\\:97]bZdc\ekdddcUMRLKRD;9[[T[[TJHFf]cbVZkk]tlldc\zllSKJb[Ukk]edkf]c{u{lek]bZUTStt{f]clddUMRUTSSKJJHFVZTFD;VTL:97FD;FD;lddJHFv{fkkVZ[VZ[\[[slelldlkslriJHF[[Tkk]\UZdc\VZT{ttVTLlks]bZ[[TJHFJHF&& +&&3)[TTSKJbVUkd\f]csleSKJ:97FD;&&FD;PF=FD;VTL[[T[TT[TTb[Ured:97[TT[[TVTLFD;VTLJHF[TTVTLf]cVTLSKJVTLD;9lldbVZJHF{zm}{ttlddMRKmttiq]zl|zUMRVZTJHFSKJ|tt{d\\lkstts[TTJHF{{tlrid\\[TT[TT]bZ[TTddclldlddlldSKJJHFedkUUYJHFlekJHFUMR[TT\[[{zmUTS\[bMSSVTL]cd{{UTSekdSKJLKRddcSKJllkJHF[[TFD;]bZlek]bZek]\UZVZ[UMRkd\lddztluzt[TTb[Ulekd\\{u{FD;VTLmttmttllkSKJMRK74,D;9VTLVTLb[USKJbVZ[TT&&PF=VTLVTL\UZek]>B9D;9tsl\[[ekdkk]mttdc\llk{ttllkSKJ\UZ{{llk\[b]cd>ECLKR:97LKR{{|z\[bf]ctt{MSS\[bVZ[LKRttslldj]\iWUUUYldd[TTf]cf]cJHFJHFJHFFD;JHF[TTtlldc\FD;JHFbVZlldlldddcJHFUMRSKJSKJSKJJHFSKJ{ttSKJkd\ulttll[TTSKJ{zm\[[ult{ttj]\cbV]cd\[bJHFf]cv\UZkk]jcVVTLMRKVTLsreUMRFD;UTSlekllkUMRUTS]cd{u{{{tddcu{{]bZuzt[TTsred\\b[UVTLVTL{ttVTLf]cFD;74,VTLVTL>B9 :97*)([[TD;9SKJVTLkd\d\\VTLredb[UPF=FD;sreVTLjcVek]UMRldd[TTUMRlddSKJkd\b[UFD;D;9SKJdc\JHFtll[[TSKJ74,SKJlldf]cLKRuslev{JHFVTLsreFD;ZbNlldf]cmttUMR{{ekdttsttstll{|||{||ultsleek]llkdc\ddctlltllddcd\\\[[mtttt{\UZUTSJHFb[U\[[{{JHFUMRVZ[UTStslu{{{||\UZ\[b[TT]cd>B9FD;f]ctt{:97\[[VTLdc\[[TlritslMRKFD;[TT[[Tredtts{zmdc\UTSd\\SKJSKJD;9VTL]bZrfkttsFD;!D;9VTLJHF\[[SKJUMRVTLJHFSKJj]\d\\D;9i\VJHFD;9ztlUTSlek[[TekdVZTekdlddtsllekd\\]cd{{tbVZJHF\[b>ECLKR]cdlkstsl{{mtt{{f]cJHFUTSultlddVTLUMRf]c\UZf]cv{lekUTS]cd>B9MRKVTLVZT{{tlekfkktlltts{||]bZF=C74,SKJddc|lldlekf]cdc\zlltll|ubVUb[Ulekd\\tllultddcSKJSKJf]cUMRldd\UZSKJD;9JHF[[TVTLSKJVTLVTLJHF}}{{llk]cd[TTd\\sleultleku{{|tslf]cVTLFD;[[Td\\UMRD;974,MSSVTLFD;%>B974,MRKJHFj]\kd\FD;UMRcbVjcVkd\ztfcbVC8.VTLFD;VTLek]VTL[TTkd\PF=JHFVTLjcV{zmd\\PF=FD;SKJ[TTSKJbVUVTLcbV]bZtslek]JHFzllf]clri]bZztl|g\rq^è|~tllultuztddcf]c{u{{tt{{tuztutllultfkkf]cSKJv{dc\{u{ddc}JHF{{lrid\\|z\[[UTSUUYlksJHFddcD;9edkVTL:97lld||{||ttslldldd[TTUTSj]\b[UVTLSKJ:97{u{lddSKJrg]b[Ullkllk{{t{{D;9JHFMRKVTLUTSuzt[TTddcD;9:97sle[TTdc\SKJHR>SKJrfktslmttJHFdc\ttslrilri|z|lek{{t~MSSmtt^fsfkktt{mtt{{\[b{||[[T\[bultkd\\[bVZTmttlek~mtt{{mtt{u{JHF[[Tdc\|llktlltsluzt{u{ultllkJHFUMR]cdcbVVZTddcF=Cb[U||tlltlllddlld{{tdc\mtttll}zlldc\d\\SKJ[TTlekrfkbVZwlaWM74,JHFlriD;9ddctllekdttszllu{{u{{VTL{zmb[U{{u{{tllutll]bZ]bZztlcbVultUMRF=CSKJiq]MRK3+**)( *)(JHFredsleb[Ub[Ukk]cbVtslVTLFD;ZbNdc\jcVztfFD;]bZbVUtslb[UVTL[TTtsl}VTLZbNkd\rg]j]\{tt|vcbVFD;tll|z>B9SKJ}}]cdjcVmttkk]ekdultǜË{{{u{{||}ztl{{|uttsllk\[[b[U\[b]cd|z{u{\UZ]bZlkslld}{||llkVZTMRKF=C>ECrfk[TTMRKttsJHFlddllk|mtttllj]\redUMRJHFSKJcbVVTLFD;ek]dc\j]\lldJHFlekttsuzt]bZSKJ]bZult|u[TTUTS]cdVTL[[TsleVTLD;9aWMVTLtll{{tuzttsl{||lri|zzlllksOS`]bZlks]cdtts\[b~\[bddcUTS{||||{ttddcedkzllf]c{{fkkUMRttsJHFllkVTLlddztl{ttttsdc\lri]bZddcttsVZ[JHFrfkf]cllk[[TSKJ}lddrfkjcVslekk]lld|||SKJ{{UTSv{\[b[TTb[Uuzt[[T\UZkjVJHFD;9VTLllkbVZ{{ddc\[[\[[|lldddcrfktzl{||b[UFD;d\\VTL{ttd\\UTSFD;\[[FD;&&%74,>B9uzt{tt{yfztl{zmb[UaWMSKJVTLJHFVTLVTLVTL|zkk]VTLkd\lldbVUSKJ[TTkk]sleaWMVTLkd\SKJj]\tllutd\\ulektslVZ[SKJzllv}aWMtts]bZUTS|}}]bZdc\|d\\ztl|tts{zmj]\f]c{tt}\[[\[bf]cf]c}|||mtttyg{||UUYtt{ttsllkUMRUMRnv\[[}ddctt{{{t{{t\[[lddUMRredkd\lldaWMb[Urfklddsletllldddc\lldvUTSJHFuztv{tllkd\||LKRJHF]bZb[UuMRKUTSkk]JHFbVUdc\{{sleult|ztts{{t{{tulttts]cdMSS>ECMSSlddmttf]c}ult{{t]bZdc\f]cSKJf]c[TTleklrif]ctts]cdD;9MRKFD;[[TSKJbVU[[T{ttlld|\[bekdlritslUMRlldttsbVUtts}|||ulldiq]{tt||lkslektll|vFD;JHF\[[dc\ultlri}llk{||tts{{{{b[UztflldddcUUYiq]zlluddctsl]bZf]cultf]ckk]\[bC8.D;9]bZJHF!FD; 74,VZTtllwllddwl[TTSKJrg]C8.dc\sreaWMaWMPF=JHFFD;{ttSKJSKJ[TTVTLek]i\VSKJVTLVTLSKJ|ukd\VTLsrellktsl\UZ3+*}||zrr]tzlrg]ekd|zlldvut!VTL{yfu{{|rfk[TTlri|uj]\|zLKR{||tt{|FD;}||lks}ultlri{||lrif]cuztedklksf]ctllttsllkUUYVTLdc\dc\SKJ[TTd\\llk{||||SKJsle\UZf]cylemwrg]ekdddc}VZ[JHFVTL{{tslekk]tll\[[ekdd\\VTLb[U{ttSKJkk]kd\{u{|uldd{zm{ttu|z{ttlldultyltzlddcMSS\[bMSSUUYddc\[bMSSlks{{mtt\UZlld{{tsl{tt||f]c]bZ~ultfkkztfMRKMRKtt{SKJr]ZVTLj]\sle{{tlkskk]ttsdc\JHF]cdlritll{tt||ztllldtts}|f]cf]culti\Vfkk]bZultkk]SKJ>B9VTL|tsl{{mtt]bZdc\{zm\[b|ztll|tt{f]clri}|tllek]JHFsleJHFaWMJHF*)(:97!JHFbVZd\\j]\sle|ubVUVTLsleaWMztfztfHI/VTLFD;jcVVTLVTLSKJSKJJHFkjV}VTLd\\d\\bVUSKJztl[TTkd\VTLult{tt>EC:97{||vvu|v{{||}*)(&&nddc|}||VTLD;9tll|u{{||SKJtzl{zm\[[ztf}{||VTL[TTb[U{{tztl[TTSKJUTSMRKf]cUMRllklddultlekddcztld\\VTLwl\[[rfkkd\}|{||b[Ukd\red{u{iq]|u[TT|tslekd[TTJHFD;9[[Td\\|usle{||JHFVZTdc\kk]VTLVTLVTLjcVd\\{{t]bZ{{t{zm||z}||lek{{tsl{u{{u{>EC>EC]cd\[[\[bmtt\[b{{ultFD;d\\lkslld[TTlekUMR|{u{ult~mtt[[TVTLllkSKJztlredlld[[TtsluztVZTJHFFD;lri}f]cbVZztftzlred~{zmsleFD;sletsl{u{ddcJHFUTSSKJf]crfk\[[d\\JHFJHFlldMRK|ult{{tll{u{{{ekdsletslmtt}|||}|z|zzl||lks}UMR\UZlekb[U\UZJHFVZT?;C3+*:97SKJ|u}ztlrg]r]ZcbVd\\SKJi\VjcVcbVcbVVTLVTLaWMztl[TT[TTsrejcVredb[Utllred{ttredrg]cbV[[T|\[[:97{u{Ɣ]bZ{{|lks}|dc\iWUred||tslredcbVf]cfkklri}|lri]bZldd\UZj]\lddllduttslekSKJUTS\[[{u{rfkbVZJHF[[Tllduztwld\\[[T\[[tll|u||{{tlldf]cutb[UUMRztlbVZ}JHF[TT\[[uztdc\|u}\[[srelddkjVnVTLaWMj]\VZTllkd\\ekd}{u{{u{[[T}{u{f]cMSSn{{~ttstt{\UZJHF\[[|z|zultlekf]cvddcekdJHFmtt{tt}|{tt{||SKJkd\|ztzluztkd\j]\F=C\UZSKJ{||{u{kd\uttlltllult{u{ztlwl\[[{zmuztl[TT\[b\[[\[[llklri}UTS\[[tll}|mttiq]FD;]bZut}ddcVTLddckd\{tt\UZtsl||tyg|f]cUMR\[bd\\|VTLUTS>B9JHFUUY3)JHFj]\|u|uslecbVdc\jcV}aWMSKJb[Udc\rg]nrg]d\\rg]lldb[UztfaWMSKJlldd\\||VTLdc\j]\¿VZ[JHFtslu{{]bZ}~^~Y3+*}|zVTLiWUyle|z}|slesle|}\UZ]bZ|ztzl{{llkmttj]\dc\{{ttsltts\UZttsJHFSKJd\\lriult{u{{tt||UMRd\\ut{tt}lddldd[TTut}ultkd\lddttstllUTSSKJUTSddcb[Ulri|z}lrisresreJHFldd|{zm\[b{u{f]clrilri{||uzt{{{{uzt}\[bJHFVTLfkk|z{{tult}lriwl{u{tsltygmttlriVTLmw~sle|zbVU{zmVTLf]cbVZldd}|v{zllf]cSKJutult}zllllkcbVkd\{||b[U}}SKJSKJllk]bZekdd\\f]cFD;VTLaWMcbVj]\ddc{u{\[b~d\\ekdFD;|z}|}}tt{D;9lksJHF:C+MSSJHF MRKttsi\Vlld{{tkd\f]crr]rr]ultzllkd\VTLrg]VTLrg]j]\|uj]\FD;sletllu\[[{yf[TTUMRaWM}|VTL|u|u¾UUYJHFnVTL}JHF{{LKRr]g|zkk]kk]|{||d\\lld{||{tttsl}{||]bZ{{redtts|ldd{{lek]bZbVZlks|{{tllk|||z|u{tti\Vredd\\|j]\\[[tsl}[[T||lks\UZtt{slev}|z|uf]cuztek]ztlkk]tslSKJ\[[{zmztl}{zmuztfkktt{{{tts[TTUTSlld[TTtts|mtt]bZtt{ekdtslllk}mttd\\UMRekdtlltllleku{{MRK]bZ|kd\|zkd\wlcbV|ztslekdlddttslldyleultzll{ttutSKJzllttssle{tttslldd|{ttultlriuztVTLJHF[[TUTSFD;JHFkk]rg]|ztsl|z|z{tt{{td\\tt{fkkVTLredtts[TT\UZ:97SKJVTLFD;:97%:97\[[dc\llkztlb[UjcVd\\kjVd\\aWMdc\VTLPF=SKJylelddVTLSKJVTLdc\{zmrg]{yfrg]bVZjV[zllwl{{t|u||\[[74,ldd}rr]b[U}j]\lj}:97SKJztlnjwl||llk~{||v{kk]{{||{zm}{ttmtt[[Tf]cldd}{u{tllmttllk}tts{{tsl|VTLut|z{||rg]}{||sleVZTFD;red|vztfsle\[btsltts{ttldd[[Ttll||u\UZultlldkd\cbVztlj]\bVUlriuzttsl|}{tt{{JHFiq]llk[[T{u{{{|mtt{{ttsekdVTLddc|ttsj]\~ddc{ttf]c{||]bZMRKmttu{{|z}{||tslllk|utsltts{tt\UZ{tt|bVUllkredrg]}ut|v{ttutbVUd\\VTLtllttslks}}¾Ɯ|f]c{ttdkVkk]jV[llkf]c{zmfkkVTLzldkV}|UTSekd:97r]gUTS:97ultedkJHFD;9dc\,553+*[[TD;9JHF[TTUTS|u}{{t||zll{zmutrg]rg]kd\SKJVTLVTL[[TVTLVTLldd[TTVTLsreusreiq]VTLsleutred||lldwlztl|\[b:97wl{tt}|}zlJHFjcV.1SKJŲultf]clldv{||v{|tll|u|z|wl{ttrfk}ultlritsl}tzllrilksSKJf]c{tt}UTS|zred{||v{tslutb[Uztl{||[[Tzllkd\i\Vldd}ultuzt||tllb[USKJ[[Tllkv{yleldd~[[TMRKkk]d\\SKJkk]j]\}{{tult}|tslu{{uzt{||v{{u{{{|LKR\UZlddllk{{{{lksnvmttUTSVTL]bZtt{llktll}}|tslMRK]bZ]bZiq]]cdtzl[[Tsre{zm{{t||ztts||rg]f]c|v{dc\llkSKJv{{tt}llkztl|ztslztf}j]\rfk}sle\[[ultekd{{{{lrizlJHFtzltslyle|b[Uuztlrilrilld|ztt{]cdlrisreztlf]c{{tll*)(SKJkd\VTL3+**)(D;93+*JHFJHF{{tztl}ztfVTLSKJjcVVTLSKJb[UVTLkk]jcVwlj]\kd\i\V[[Tkk]u{{ttslZbNzllr]Zzllyle[TTkd\{{tLKRLKRf]c}}tt{j]\JHFUTSldd]bZ|u|z{{tred[TTslemttmttuzt{{t|z{{t|{{{{t}{||{||ultlld\UZllk{||lddb[Ud\\v{uztutlri{||dc\jcV{{llk||{tt\[[kd\{ttb[U|tsl||sleSKJr]glldf]c\UZVTL}|{{tVTL{ttlksSKJtsl[[TJHF{{tlrimttuztuzt|z|ultllk{{{{lks{{}|edk{{|{||sleddcddclld|f]c{tttslVTLVZ[tzltsl{ttddc||z}ut|mwUTS{{tslelekv{}}ttsutsre\[[b[U{tt}{{tslelksd\\ldduztmtt}[TT|zttsJHFz}llk|{{\UZek][[T||tlllks|mttSKJzllekd\[b?;CFD;"[TTslei\VsleutVTLrr]bVUPF=VTLjcVd\\lldVTLsrelddi\VVTLSKJwlzl}|lrirg]b[U{ttbVUut|v|ukk]|zVZTUTSult}zrg]uj]\j]\bVUMRKSKJ{ttutztl{{t|z}|usleu{{{||{ttu{zm{tt|{||}{ttlri|uv{d\\lek\[[]bZ[TTf]c}uztbVZ||wldc\lddd\\{zmtlltsl{ttj]\ztfkk]SKJ}|{tt{ttJHF[TTd\\ultekdJHF[[TSKJ\UZdc\dc\SKJVTLultult|uu{{{u{||z{tt\[b\UZultlksu{{{u{kjVu{{\UZJHFj]\]bZ]cd\UZ{tt}tzldc\{{t{||sre|z|uredf]cSKJllkf]cVTLJHFSKJbVU~ylebVZFD;kd\{yfd\\{zmzll|rfku{{d\\r]g{{tf]cJHFultf]c]bZ]bZek]{{t\[[{{{{llkf]cslett{edk{{tldd|u|jcV{||ekd\[[VZT[TTF=CFD;74,lldf]ctllwl|utVTL[[TVTLb[UVTLb[Ulrikk]bVZddcSKJsle{ttVTL{zmu|ztsllldlddtllv{rr]wlztf{{twlfkkMRKtts}|tzl}jcVVTL|zF=C?;C}tslsle||z}u]bZ|{{t||rfk{u{tsllddekd||d\\}lldllkbVUzll|z[[Tsreultulddtslwl|u{{ttslf]cekdPF=bVU}j]\[[T}lddtt{f]c{tt:97lldUMRultekd]cdVTLbVUbVZfkk|ztltslSKJ{tttsllri|u{{lrifkklkstt{JHFVZ[g\rmtt\[b{{tt{d\\[[TVTLVTL{zmcbVtts{u{bVU{u{uztVZTttsuztuztlddf]c}[TTtslMRK[[T{{v{d\\||tslf]cutrg]VTLkd\{{tlek[[T}tygllkljVTL[TT\[[|zldd|dc\lriedkekd]cd{{\UZ[[T}|uzt{{tmttttslriVTLslelldr]ZD;93+*|zkd\{yfFD;JHFSKJjcVMRK|u|dc\utkk]SKJtsl]bZbVUkjVsre[[TtllD;9aWMslesreultj]\sreusreredttsutultwlu{{t]cdMRK\[[{ttldd]bZ]bZzllwl|}||SKJFD;}u{tttll{{t}|tll|||zred{||cbV]bZ{u{ztl\UZ}|z}{||tsldc\{tt||}slekd\ultsleztlult|vtll{ttllktllredUMRtslVTLtts{tt{ttekd]bZaWMredllk|kd\u|u{||ultlek{{ddc]cdf]ctsl{{lks{{j]\VTLFD;[[Tldd|ultllk{||lri|{{t{{t{|||zd\\tzl{{t|zu}[[T3+*lektllyfltsld\\ldd}|ut{ttwl{u{VTLcbV}||[TTVTLVZTuttsl{u{{yfmtt\[[{u{kjV}|tzl|lkslldkd\llku{{|ztt{VTLusleJHFbVUUTS:97UTSD;9VTLiq]JHF%PF=b[Ulekrg]}|z|||utslkk]kjVVTLdc\llkdc\JHFb[Ukd\[[Tb[U{ttwlwlvtsl{zmred|vsle}}\[[JHF}ultdc\dc\kd\VTL}{{[[Tdc\b[Ukd\lri||}tzluzt|zsretllr]gzllri|u}}|zttsVZT}|{{tsl[[T|u}||llktll|tsl}}lddredaWMtllcbVJHF[TTult{u{j]\UTSllkztl|tzlkd\ult{{tztltslkk]|u{||lkscbVmtt}lksJHFuztmtt|ulttt{{||mtt\[[{zmSKJMRKlekultult[TTlritsl|ztsllri{tt||zdc\b[Uedkrfkred[TT}}v{[TTkk]|uutwv{lld||SKJd\\tll{||j]\mtt}tsl{zmztl|z}f]c]bZu{{{tt||ztlredu{{tslu|z|UMR7-2SKJFD;MRKLKRtt{3+*73JHF[[TutsledkV[TTPF=VTLFD;bVZu|u{zmtsl]bZ{||i\VztlSKJedkwl|zr]Zljzll|vlj|u}|v{[TT:97tsluuzt\[[}|lld]cd~f]c[TTb[Uyletzl{{ztl[TTbVU|{{tddclrisreVTLtllultkd\u|z{zm}lritzl{tt{{ttslkk]}redtslutztllri{zmaWMujcVd\\SKJVTLkd\{u{\[[lri{{tf]cSKJ{{[TTrfktts||lriyfllek{{tult{ttsresre{zm}sretll{||lrikk]{||{{tultf]cVTLVZ[|z]cdmttllk}ttsdc\MRKjcVtslztlultf]ctslult}dc\|zVTLttsult|||uztliq]lriSKJu{{UMR|zSKJf]c}{||}f]csre{u{ldd}ultuzt\UZVTLlrilld{u{ddcek]lddkk]b[Usle}\[b{zmztl{|||z}]bZu}|zttsmttlks[[TVTLFD;74,UTS3):97iq]dc\|z{{tVTL|ucbV[TTVTL|zsle\[[VTL|uztl}|edktllwl|utsllldd\\d\\}|ztl||u{zmuv{UTS*)(ztfVTL|vlriVTLllkr]Zb[U:973+*rfkyfl|ubVUVTLFD;red|ultult{{ttslv{lrilksuzttt{}zedkaWMek]|uztfzll|tll{ttwlkd\rg]sle}|ztllrir]gSKJf]cb[Uekd|u{ttd\\UTSulttslsle|kd\uztlkskd\SKJlld|zlld{{tzllldlld}ddcf]clksJHF]bZmtt\[[tt{}{{u{{bVZFD;tslddclld|uu{{lks{{t|{{tuztekdUTS~ztltt{uzt{{tztl}kjVuztultjV[|vjV[v||utf]c{{tcbV|{||ztfultb[UttstllddcMRKlriVTLFD;lld|llk{u{llkmttllktt{}lriekd]cdf]cbVUVTL>B9FD;:97FD;&&C8.{{t}kk]ultsleulttslJHFVTLVTLsre]bZVTLbVZlldlddcbVlld}wlttskk]||||}|wlbVU|usrei\V|u}>B9:97jV[bVZu{{i\Vekdllkkd\aWMVTLJHF|tts{u{sle\[[tzlr]gtslldd|z||||lri|z|SKJ}{|||zkk]tsl|}VZ[|{zm}ztflld{zm{||}SKJb[U|[TTredzllztlldduylelj{u{tll||llktllultb[U{{JHFtsl{||lriuv{]bZ}|}u{{ttswlsle|ulld}ztl}|udc\ekdrfk{{mttF=C>EC]bZf]cdc\\[bVTLVTLUTS|ui\VF=Clldiq]dc\[[T||{||uuztuzttsl]bZredult|rg]f]c[TT|v{}|z|uv{}|{zm||redbVUUTS{u{lld|||lri|zkd\d\\kk]u{{ult\UZtts\UZtt{u{{{||lrilld>EC%FD;74,FD;SKJVZT3+*&&C8.yle|z|||z}JHFJHFlri]bZVTL:97VTL{u{tzl}lddrfkllk{zmlri|VTLrfk}|v{u{}utztfMRK*)(ut|z}{zmJHF|zllkuztlekttsi\VD;9D;9}{{cbV[TTedk||b[Ulrikk]ddclldVTLekdSKJj]\F=Cj]\{{sle]bZlld{zmrg]ddcf]cj]\j]\ddcldd}tzlztlsle}ut{||~fkk}|tslmttf]ctslUMRSKJlksslef]clddddc{{tslttslriuzttslzl{||utsre||uztVTLtllultddcedkekdLKRu{{lkstts{tt{{j]\MRKVZTrfklektsluzt}ttstsllkstts|lld{||ldd~}mtt{{lek||tts||wlb[U\UZtllrfklddv{|z}j]\|uSKJ{{JHFddcredulttt{ult{zmultlddutlldllk]bZ:97aWM3+*C8.Q?@F=C%&&&tlllri{zmVTL||VTL|ztl{u{JHFVTLlldJHF\UZ[TTtsllddtlld\\tts[[Tsre[[TVTLkd\kd\}rg]}sleutMSS:97tsl|wlekdJHF|ztt{uztdc\|rg]74,bVUUTS{||ddcekdlritsl}slellkVZ[{tt\UZ{|||VTLSKJ]bZFD;[TT|zmttrg]ttsd\\rg]}|{ttkk]tsllldtslv{{zmkd\lddmtt}}{u{f]cultUMRztlultj]\}kd\llk||lri{||ttsf]c[[Tsre{tt{||ttstsl{tt{||}lek{ttuztMSSmtt{u{ek]{u{{u{f]ckd\lddd\\SKJtslf]cdc\{{t[[Ttllf]c\[[u{{mtt{||ekd|vkd\|u{ttVTL[[TVZTlri|u}|{u{tsl{u{{tt|u|zllkultlddslej]\{u{j]\}tzl}UMR[[Tj]\{ttultf]c\UZslerg]f]c{ttvfkkf]ctsl\UZ}{u{{{:97lldmttF=CFD;>B9FD;|zj]\|z|zwlsletzlaWMrfk[[T[TTlri{zm\UZlddcbVtsllldSKJttsttsVTL[[Ttllsre[[Td\\{zmrg]lldrg]||v{zmJHF3+*{{trr][TT||llk{{t|uC8.JHF{{tuzt{{t}lksdkVlld|\[[|ztltzl|zlri}{u{utult{{ttslkd\jcV||usler]gyledc\d\\|ulld{{tsletllrfklddllkyleekd{u{edktzl{zmlekf]c{{VTLrfk]bZ|z[TT{u{tzlfkk{{tredlriv{lld[[Ttslzll|z{{t}lek{||{{{{{u{ttsttsfkkd\\\[[zll{u{||||]cd||ut|tslJHFmtt{{tuulttt{kd\{zmsre{u{\UZlekult|lrirg]iq]||ttsldd[TTult||lldredlldv{||kk]u{{VTLb[U\[bSKJ{{r]gUMRuVTL]bZJHF\[b{|||udc\kk]ultrg]\UZtsltzlddclddu{{tts{{tVTLredUTSUTS]bZdc\]bZrg]SKJrg]{yfkd\i\VSKJsrelddSKJ:97{zm]bZttslriukd\dc\rg]PF=bVUv{ztl}tsl||ut{||ddcb[Uttslddkd\lrilritzlutts~d\\dc\lddjcVVTL{{t{ttztltsl|ztl{{t}|}rg]mw{zm{zmredrg]v{lddredultv{fkkd\\kk]|lek\[[kd\}tll|zek]uzt}uztlri|ufkk|{tttsl|{ttldd|ulttllb[Ukk]|fkk|z{tt[[Tslelri|z{||{{tVZTdc\\UZ||llk}||ekdttstts{u{{{tttsztluztv{tlld\\sre{ttd\\}{{tldd[TTlldbVUtllutllktzl|ubVZlekllkSKJlri[TTttsd\\{zm||tllbVZkd\}]cdu{{:97{{JHFD;9|]bZ:97dc\{||rg]wlekdtslztlllkultsretts}|mtt{{{tt|z{||||d\\slesrett{}b[Ub[UcbVrg]cbVjcVwlrg]ddc*)(zllu{{lldsleUTScbV{zmHI/ultultredf]c{zmtts{{tultultdc\{||tts}||cbVtt{{{ttsutvddc||f]cUTSkk]lldVTL{{t||ud\\ztlsre||vbVUutlrilldVTLVTLd\\kd\b[U]bZ{{JHFd\\ultek]tsl||}lri]bZdc\lek]cduzt{{kd\b[UUTSult}slelekek]tsl{{tred{{t{zmj]\lkscbV{{t|zultkd\lekUTS{{ekd{{{||{||tllek]tt{}{||lldlek{{tsl||zmttf]c]cdddcztl|u|zuzt{{f]clks|{u{zllultfkktlllldsle|u{{||lrildd|ubVUttsultztf}tllrfk{{tcbV{||mtt|UTS]bZUMRD;9\[bekdzllVTL{ttedkSKJ>B9f]c{zmredVTL}}b[U\[[{{fkk{||lks{{[[Tllkdc\dc\u{{}dc\lri{zmljsle~[TT:97uztttsj]\f]cVTLbVU{{t{ttFD;JHFyflzllmtttsldc\d\\kk]lks}llk{|||z|{||uztultlritts{ttult{||sle}{zmslesreVTLtllsrei\Vwlultlldztllldtllkd\VTLSKJ|}lektslzlllriVTLd\\{ttultuzt\[[VTLrfkttsttsuztkd\b[Ulri}{zmrfkJHFJHF74,b[U{{tVTLllktslsrelriutsl|zultbVU{u{ekdmttfkk}{{u{{f]c}JHFcbVllk}{||{||lld}||z}|{{u{{JHF]bZredyllksllksreztf|z{{t{u{f]cd\\lldsle|z\[[tllztlred{tt}tllek]bVUtslv{{}|ultekdlekylelldtzl{||slemttuztMRKVTLJHFFD;FD;tzlD;9lldsre]cdJHFJHFddcaWMrg]VTLtsl|edklddlek{{llkUTSddc{tttzlult{||llkult}|{{tSKJi\V{zm{||}VTL:97v[TTjcVkk]kk]VTLbVU%UTStsl|{{tlrid\\lksuztldd{||}zlekddc\||\UZultkd\v{VTLddcleklldultkd\rg]|u{{tlddlrid\\}wlred\[[sref]c|uVTLSKJVTL|uultredfkkd\\|dc\lldlektlllld[[Tv{f]c{ttlrid\\zlllriultztl\[buztddclldrfkuztcbVllk}{||ldd}|z||}mttlks|tsl]bZ|tts}tts\UZtts]bZultlldlks|u||tlllddllk}fkklld\[blldtt{d\\dc\}|v|uedk{||[[T{{t|r]g}|{{|zrg]llk|u}||rfktt{{u{zllkd\}|tt{sretll|\UZddcek]|z\[b>B9\[[ddc\UZVTLJHFC8.JHFFD;ddcylecbVztllriztlllkddclek{ttttsllkllkttsddclek{tt|tslsrerfkcbVv|v|v|uVZ[JHF|u{{t}||{{~lldtllkk]vsleD;9UMRultsle{{tuztu{{ultutldd[[Tddctts}b[Ullk|ztslllklldlldrg]cbV|uVTLj]\{{tztlztlredd\\u{ttFD;cbV{{tultztlSKJ]bZMRKkk]}u{{tsl|zu{{}tts}ylef]c]cdlridc\{tt{zmultlld{||tllllk{u{[[Tdc\]cdlkslks{{tt{lksubVZldd\[[{tt\[[]cdut}tll}|lksċkk]ttsuztllk{{t{||edkJHFutuztlekv{~tlltsllri|||ultj]\|utts{ttldd||b[Uslelri{||||||{{tslemttjV[tslllkmttSKJMRK]cdJHFD;9JHFSKJD;9fkkaWMedkv{ddcrg]{zmlld{zmtts{u{bVU|ztll}|lddtts}lldtslrg]b[U|ztl||v}rg]UUYLKRv}|[TT{ttuzt|ui\Vrg]FD;VTL[TT{||dc\|z||tts]bZ\UZ}|tt{dc\}}jcVzlllrilksekdd\\lkstsl}srerfksre{{tutjV[}{||u||tzlek]cbVmtt[[TlekSKJlld|zut|z[TTtsl|slelldFD;sre|||z{{t|u|v{}{ttultuzt|MSSmttu{{ttsllklkstt{{||}lekuztuddculttts{{lddĄ|zfkktslztl|uzttlllekdc\||VTL}|||{ttttsu{{||wllks|zkk]}sle~v{redrfkntslcbVztlu{||||UTSFD;ZbN:97:97SKJ[TTcbVsreSKJVTL[[T{u{[TTsleultwlcbVdc\{ttlekztlnvmtt}tllf]clddcbVdc\kk]}||uwl|u\UZ>ECztfrg]iq][[T}llkztlkd\ttsredFD;{tt{tt||zekd{{|||mtttll{{tekdkk]tt{utekd{{ttt{}|{{tjcV|||wl{{t\[[d\\lld{||iq]rfkvdc\d\\[TT\[[ttsllk|v{f]csle{u{|lldSKJd\\tll|uztfllduzt{{t|ztlbVZddctsllld}|z{{ddclks}ddc\[[{||tt{{{{tt||lkstt{{{t{u{lriztlrfkf]czllztltsl}ultldd|z||dc\b[Ukd\{zmtsl}|||}lriSKJrfkultf]c}lks|u}}|||}{zm}|zttsb[Ullk\[bFD;%UMR[TTtzlJHFVTLVTLlri}|kd\}|llkttsllkddcllk|ult{{{u{u{{lksult{||u{tt|uv{|vUMRJHF{{tj]\|uttsb[Ub[Ured|v|u{ttztlD;9FD;{{sle|zllk[TT|zv|mtt|z|{{t{||{||lkskd\f]c{u{{tt}{ttrg]|uldd{u{}|tllkk]tsl[[TVTL|z~wtlllritsld\\JHF|tsl||{tttts\[bekdllktsl|ulritlllek|zdc\lldttsd\\bVUtlltts[[T[TT{{ttsVTL{u{lridc\}||||lddf]c{{t{{mttfkklldmttuzttt{lksVZ[{||ztlVZT|llkult\[bv{||{{u{{|||f]csretllllkkd\{||d\\llkulttts]bZlld[[TSKJultv{{||{||tzli\V{tt}fkk\UZ{u{tll||redvuztdc\{||ekdj]\tlllddVTLMRK[[T>B9\[[kd\tsl\[[MRK*)(u{{{||redj]\ttsekd[TT[TTddcedk\[[tts|ztts|lekldd}~vtll\[[,55|jcVzl]bZttsztl|utFD;sref]cVTLd\\lekttsӛ|z{{tf]cult|{ttd\\}tzl}tll|redu{{zllkd\wl|}i\Vw}{zm|||lldbVUttsb[U|tygredzllv{\UZ[[Tkk]{{lkskd\rr]llduztmw||d\\ddcultzll}|ylesle|ubVU{||SKJ}|[[Tult}||zllllkttsutuzt\UZMSSMSS|UTSUTS\[[ult\[b|{||{||ultedkddcv{{{t{||{||mttutt{llk{|||zf]ccbVtllmtt|z\UZv{jV[sle[[Tkd\{||ttstts{tt|uztlkd\ttskd\r]gtslsleut||||zut|ztts|lrimttut|{ttek]JHFJHFFD;JHF{||MSSMRK}JHF|zdc\UTSyfllekf]cmttredllktts{tttll|v{zm}{tt}MRK:97{tt|usreSKJsrezll{{t|u|ubVZsrekk]SKJrfk}}lks|zedkUTS|||lritllddcrfkf]cult{u{{{t|zddcf]c|ulri|zrfk{{t{tttslzlluzll|ttsb[Ullkdc\uVTLd\\f]cddcekdf]cf]c|}tsl{ttv{tt}lldSKJtllbVZztf}|ultdkV|zllv{{{tv{ttred{{t}ult{|||ultllk>ECMSSUTSllkddc{{mttfkk{tt{||ekd{||f]clksddctlltll{{tultult}Ц|uuztsre||{{t||{{t{{t{tt{||ttsztl{ttkd\|ult{||lld||}{{|ulddult|ztzlztluztl{ttyfllddtll{u{VTL\[[FD;F=CUMRd\\[[TZbNedkUTS|u{{t{||rfk||z|ttstsl}lriu{{]cd{{t{{tkk]{{tuttll{zmllk7-2}cbVkd\wl|tzlyfd{||SKJPF=kd\|z{{]bZztld\\|ztsl}vd\\lldf]c{{rfktlllkstts{{}tslMRKlks{tt{zmtll{|||[TTredkd\}uddc||||u{{lldbVZ[[Tek]\[[{u{leklek}|zultsleekdtllkd\redllksleuztcbVlldFD;lekj]\ztl|v{tt|||}ult||i\Vredrfkult{ttultlrifkklld|z]cdmttultuztult^^qf]c||}fkk}||uztf]c{|||f]ctlltslddclri}}{||{tttt{ultlddzlluzttzl{u{v|}ddclldlld|ut|utslekd\ult|kd\{{twl|ulriztllek|}tll}lld}|kd\VZTVTL:97UMR{zmekdUTSFD;MRK]bZ|zsle{{t|z{{tztl{{tllkd\\sle|ttskk]llklddlektslztl{tt{zmv{}||}VTL*)(tzlljcbV|zzlltzlkd\sre}|74,D;9{{ttll}{||}ultjV[yfd|z|}~||tsl}}{{|tt{[[Tddcek]sletts|{zmultsle\[[kd\tslVTL|{{tlddlddulttllJHFultredtsl{{bVZmttlriv|mttkd\kd\dc\|z}{{ttslUTS{{t{ttljtlltll{u{ttsultredlddkd\{ttult{||edkultJHFmtt\[bfkk{tt|zult|{||vUTSdc\llk|vv{u{{mtt{{|{{}v{{tt[[T|u|||}}|z}|{zm||{{tmttttszll|uztd\\{{ttslztf|ztsl}}tslsle}{u{kd\d\\}ekdrfkJHF||bVUlekb[USKJJHFFD;MRKSKJVTLJHF|\UZ|f]c||u}|llkekd||lri{||kd\MRKred{|||ztts||ultu{{{||{||kd\|u}}{{tsle|u}JHF-1+|zrg]kk]jcVvb[Ub[UVTL3+*ttsVTL}tts|zmtttt{}llktt{{tttslf]clkstsl{{||vultttsdc\f]cut||redi\Vkd\zllVTLldd{||[[Tllk||ttssredc\bVUttsSKJ}ultllklriredztltsl}d\\ztl{ttdc\d\\|uztl}|cbVkk]kd\||}|wlkd\ult{{ultlldfkk|JHF\[btlluzt{{}|{{t{u{ddctll{||lri{tt||z||}edk|zslelek{||tsl}}{{t{{t{{t|zdc\rfk}{||llkult|{{tslett{{{|}|bVZlldlekztluztkd\D;9VTLFD;[TTsleUTSf]c&&\[b|u}~sle|}}lldztlttsttsutlld|v{yletsl|:9774,|u{{t|urr]rg]tslyle{u{||VTLSKJ}}|}tt{lriuztult|z|||dc\d\\[[TUTSrfkldd|}j]\b[U{{tkk]{tt|kk]uzt|zttsf]c{{tsletsl}d\\v{|z{{ult{||}uzt{||jcV\[b||{u{ultut{{ttll}|}{u{f]c}lri|lri]cd\[[ekdtt{{{tsl{u{{||||ekdtll~llkwlultwlult|z}|ultult{tt{{t{tt||sletsldc\{||lek{{t{{t}|tllmwmttd\\mttztl{u{{{t|redlldtll}|rg]uzttygC8.dc\[TTldd\[bmtt\[bkd\rfk{{t[[T|ttsv{{{tslellk|ztlutüSKJ:97kd\kjV}ztlkd\}d\\tsl|zuzt}{zm[TT{zmuztu|b[U{||{u{{{{{t{||tts\[[ek]|u{{ulksult{{t{{uu{{ut}||b[Usre||sre}tt{|||uult{ttsretzld\\lrir]Z|zuttts{{t{{{ttek]tslkd\d\\tlltlllddf]c|llk}|||lddsre}{{|]bZ{||{u{|z|}mtt{|||zttstt{}tllultldduf]cttstzl|z}ut{||ldd|{tt{{t{zmuzt{u{||{tttts}lddldd{{t[TTllk|u|}u{{~ztf{{tslekk]D;9ult{{|zmttUUYd\\fkkzll|uuzt}{u{\[[~ddc]bZ[[Tddctsltslb[U|{||v{ztl[[T74,{zm}}|z{zm|zllduzt|||zztlldd[TTddc|{u{{u{}}VZ[u{{|ldduztdc\lkslriult{tttsluzt{{d\\}}ult}zll{ttldd}{{t{{lddtsl{||u||kd\|uultj]\tllllkrfk||{{t|{ttlld|zrfkj]\v{mtt}lekrg]}ztltslldd{tt{{}reduztkd\b[Utts{||{{|{u{~mttlks[[T{||llkdc\rfkmtttt{|}f]c~tsl{{tt{{zm}|u{||v{}|u}lld|lldututttsut~mw{tt|ddc|lks{u{|z}||d\\sred\\|z|vtts\[b>B9f]cJHFtll{||uztkd\tsl{{}|z}{||lld{{t\UZmtt|llktslSKJVTLkk]kk]{{t}|tllVZT-1+|||ukd\tzlVTL[TTllk}ulrilks{{t|zlks|sre[[TUTSUUY}zll||lri|cbVslettslrikk]lddlkstts||u[[Tttsekdlddtt{[TTztltts{u{ut|zuutll{u{lddkk]vlddredbVUtlllld}tt{||||z|b[Utts{{t{u{sleSKJd\\d\\}sleldd[TTddcek]utllk{u{{{t[TTkd\|slelrimttlek|||{{{{{||llk{|||z|{||||{u{}{{}|zult{||{{|zlld}{||}|{u{{u{||tsl|||{zmd\\f]csle{||}{u{uztbVU|zkd\tsl|z]bZtll}}sletzl{{lddtzltt{SKJJHFFD;llk|ztsl|ztt{sleztl{u{kd\ekd{u{[[TbVUtts|ztsltt{lldlrib[UaWMcbVdc\tsl}tsl|vJHF3+*}|uwltsltslsletlllri|u{{sle{||ddclri\[[ttsf]cUTSzll~{u{sre||mtt|zf]cttssleddctsl:97ek]VTL[[Tttslri{|||{||v|||z{u{zllv{}ult|uztlultsreyfdsleredd\\kd\bVUttssre|]cddc\{||ttsredredUMRd\\|ud\\yle~|u{tt}{u{ttsreduzt}tll{u{\[b]cdyllks||}llk{{|}}||ztsllld|uult|z\[b}{tt|zsleult|uv{}tsl}{zmsle||u{u{utlld{{tult||}||tts|udc\tt{zll|zj]\[[T}|SKJ}SKJVTL[TTlrimttddcwl{{tu{{tt{||{u{v{b[Utsl{{}lldlek|ultttstslv{{zmtsl{ttJHF*)(}uzt||f]c|udc\|ulritllUTS|ulri}SKJvlldekd\[[UMR{tt}lks}{{tredlldSKJ{||tsl~uztlri||z||ult||zllztl}dc\|}rg]|zd\\tlltsl{ttrg]ultSKJ{|||{{tsre{{t}||tsld\\mtt{ttsleldddkVbVU{||{||mttuztllk{{}lld{||[[TbVU\[bd\\\[bedk|]bZu{{|ztts|u{u{|}{{zll{u{|zleklld[[Tlld{{||zztlutVZT}}{tt[TTkd\ek]}}|}}|ttstsl||{u{}||||}|||||ubVU||rg]ztl|ult[TT{{tslelrif]c{{ldd{|||z{{t{||d\\f]ckd\VZTtts{u{{ttv{lksVTL*)(sre|lriztl|uVTL{{t}{tt[[T|zb[U{tt]cdut||}|}zl}vsle[TTUTScbV|udc\{||u}u{{v{}tsl}tllut|zek]zll{zmv{{tuzttll|v}bVUztlslered}ldd}sle|llddc\{ttllkd\\{ttkd\tll|zj]\}}{tt}llkllk||v{}tll{{ttstslmttf]c{{mttuzt{{|z|{ttVZ[{tt}uztv{~||tll{||ddcv{}dc\{||rfk}}ztl}tslztf{{t{ttkd\sle|u|zult}ċ|j]\}red|utsl{||utlldtzl{{tSKJutVTL{{t~f]cJHFlritts}ultj]\}llktsllks|{zm{{t}tsl~\[b)+2}|}dkV|u}sreVTLsle|ulrimttztl|\[[lrizll|uuztVTLlld}lrif]ctsllri{{{{{u{tllsle}{u{}sle{ttj]\}ldd{zm|u||sledc\|vj]\]bZtll{{t{zm{u{|ukd\ttsedktlllddsleztf{u{||v{tttsldc\v{|u{u{uedk{u{ttslldult{{t{|||zuzt||||zlksu{{{zmlek}tt{}ultlks|{|||tts}{{ultyfl|usre}yfdztl|u|u{tt|z|zrfklri{ttult|{tt{{tuzt}lldztfVTLSKJsleuzt[TT:97f]cmtt\[[lri{{tmtt}{u{||}ek]tlltt{[TTu}JHF*)(lriztliq]lld{u{VTLtsld\\|uzt||uztu{{u{{tllulttts{||rfkldd\[[{{t{tttt{u{{|zultred||{{tllb[U{||tts{zmslei\V}{ttutddcddckd\|ud\\}tll||zllUTStll|zusle{u{tts}}{||{ttf]ctllwllldsle|||z{u{ultttstts{tt||||lks||lriu{{||VZT]cdu{{{{]bZ}{u{tlllri}|sle|ulttts{{tJHFztl{{t}ult~tll}|}ljlri{zm}|zllkd\|z||UTS{tt{||tll\[[}|{tt{u{|z]cdztlVTLSKJ}MRKSKJuztd\\mtt|tt{|}}ttslkstlltsl{{t|ullk||tts|MSS*)(cbV}{{t|cbVdc\sreztlekdldd|u|zJHF|[TTred[[T}~{zmllk{{tut}|{{lks}|z}wlv{yflf]clld{|||uddcdc\uztb[U||{||}|tllkd\lekut{tt}tt{|lektllztlrg]tts{u{lldv{{{tddcVTL||ult|sleiq]]bZlri}\[[v|sle||{tt{{tu{{u{{fkk}mtt{{\[btsl{{tkd\lekultddc{u{UMRuztuztu{{tllk{u{iq]tll}{{}{tttsl|ttsuzt}|||b[Utsluzt{u{||ult}{ttedkSKJslett{u}slecbVrr]SKJdkVMRKf]ctzlmwekd}ldd}tts}{||tll|z{{ttslztl}ult[TT*)({{tlks{zm{tt||{{tuzttslyle{{t|zultuzt{||f]c{ttmtttlltzlttssle]bZ}lri\[[\[[uzttt{tts{tt|uztttslddb[U|zddc{{tlriztl}ztlttsztllddb[U|uslekd\{{t}|z{||{{j]\|VTLtll|z{u{ttsllk{{ttllztl|z}tlledku{{ddc|z|ekdmttmtt||tllztlztlultulttts}v{|}|{{t{zm{{ldd||~|u|udc\|||u||}}u{{}}{||||tlltts|lddedklldztlkd\|ukk]b[U{tt~mtt}tts{||llkVZ[{tt||mwmttuzt{||||}ddc||[[T{u{{zm{{}}|LKR)+2ud\\}|usleuzttt{VZ[VTL}\UZ|u|u{{|zlriuztcbV|u|}|tsl\[[|zu{{|ulksu{{tzl|z|ztl{u{{{tlrittsvtslsleylelrirg]ztl|uult}ztlcbVf]cd\\|u{zm}|{u{lldtslddc|zu|tsl{||d\\sle|u{||sre|ud\\|vkk]tlltsluzt}sre}|||{u{{{|tts|ztl}{||}tt{sre}ddctslult{{}tll|z|z|u}}yle{||llk}f]credlddldd|zuultdc\ztl|zVTL{u{lek{{tv{llku{{v{lek}v{|{tt|llk}}kk]ultf]c[TTJHF:97sre|{tt|[[Tb[U||lldtlllrilld[[T|z}tzl{||mtt{{t~ult||u||}tsl|dc\\UZ\[[{{ddcztlVZ[ztl}}|z}lriddc}{{tsre}{|||z{ttdc\tts|lldkk]ztlSKJi\V[[T{{ek]||{ttzll{ttj]\|zllutztlllk{tt{|||ztl}zllllkdc\lldultUTS|ub[Usle}sre||{{t|z}{{t{||\[[{u{{|||]cd|{|||{{t{u{{zm}zllred|{{t|tzl{||{{||{ttwllri|||uvutlri{u{{tt{||{||{||ult}|||{tt{zmyle|uzt]bZ|zu{{ult]cdf]c|ultdc\|lekredsrev{||{|||zbVUrfk{{tlld|uldd{ttzllut:97JHF{{t|sle|||||u|ekdlritsl|||tll|VZT{{t||}{||b[U|sre}lekttslri\[[lkssleVZ[{u{{tt{{t||lri{tttllVZ[|||f]c|{||sretsllddsre{{tsre{{td\\{||lldd\\|v||b[Ukd\lddtsllldb[UUMRtllred[TTtsl{||}{||{ttut|ukd\tt{uztldd{tttt{fkkmtt{ttu{{{{ulddtt{{|||utslvrfk{||{u{tts{|||vvv{{{t{{{{t{{ttzlzll||lld}||\[[||ttsMRKztl|lkslks}bVZult}}ut{u{{u{SKJlrifkkred|u|}||utlddJHF:97rg]|z|tzllri}tll}{{t]bZVTL||llktts|d\\tt{d\\\[[yle{tttlllksttslldVTLleku{{edkd\\UTSult{u{slelrilldu||}{ttztlldd|zttsv{lld}j]\jcVkd\VTLtzl~ult{zm|u[TTjcVult{u{{{tdc\}bVUldddc\{{ztl|utslrg]}||}{{tlddztlkd\sle{|||zult||{u{ttslriedkUMRtll{{t{u{}|rfk}{{t}ultiWUzllrg]utztf}|ztl|ukd\|u}u{{ttsmttllk{||ulttsluzt}|zultuztzlllddttsVTL[[Tsref]cJHFultultzll{tttll{u{|z||{{tsledc\|zw{{ddc||tsl{||ztltll|vlddultldduD;9,55|UTS{||lld\[[{u{tll|zuztult|u{ttv{lkslks]cdddc{{tcbV~d\\wlf]c||utzl[[Tmttlld\[bttstt{lrittstsl|ztl|zlldc\{||sleVZTuztlri|tts{tt}|MRKwl{{tsletllkd\tslsle{u{lriwltt{}tlltlllldutb[Uredllk||}{tt{{t}}|ulttzlf]c{{tsled\\|u{u{\[[]cd}tts}{||edk{u{kd\|z}}kk]ttsut[[Tztl|}{u{{{t}|b[Uztl|uek]}~utztf{||{||tllulttt{tsl||usreuu{{lld|JHFdc\tsl[[TUMRMSSUMRultlks{{t{{t{ttldd{||llkuzt{{ttts{ttlld[TTv{ut}lldldd~:97*)(|}ztlu{{|d\\}|u|tts{{tUTSttsredldd{u{llkekdtt{{{tlld{||ttstllv{mttdc\||ekdlrikd\{ttlri|tts}|ztll}tzl}{u{LKRd\\rr]f]cd\\ddcf]cutdc\kk]ztltt{srekd\[TT[TTj]\ultsle}ut}|tslult{{|||uulttsl}{yf|uzluzttllrg]|tsl{u{{ttvdc\{tt{{{u{{{{ttred}ztl|u{u{|u|rfkllk|u|ttsutzllrg]}jcVsrev{red}ztlkd\}VTLtslv{kk]lriedktll{{t{{mttd\\VZT||||[TTJHFf]c{{f]ctslutlri|{||uzt}|u|wlult{||ut}JHF*)(|||mttztl|uut{{tlri{||d\\ldd]bZ}}|z|z|uuztlrillddc\{{tztlrfkkk]|[TT{||tts{||{ttlldllkmw\UZ{zm{u{||v{tslsreztfwlkk]ddcdc\ultzll{ttv{{{tkd\dc\d\\d\\[[Tkd\rfktslVTLuttslb[U{ttulttllztltllut|zlrif]c}{zm}d\\f]clri{|||zlldtt{{{|||}|tts}uztsle}ult}v{}}|zlldc\dc\{||||mtttllv{{tt}|}|kk]slev{utztf}|zsreSKJllk{||kk]ldd|}|z{{lekttslrilddlriv{ttf]clri}|{u{}||~ztl}{{ttts|z{u{{u{}|||}:97*)(}{tt}|}||{||llkkd\{u{tts\[[zl{|||u{{tslerfk{{tdc\}ek]f]c||v{zll{tt{u{[TT{{tllkzll{ttllklribVUultf]cdc\{{tVTL|||lri{zmkk]|lddtll}VTLred}f]c|zsle||tllddc{zmbVUudc\[[T{{t||}ztlttsultttsfkkllk|z\[bttsmttlrittsultutult|u{{tttstll{{tredf]c}|\UZ|ztsldc\{||ult{{tv{ztl}zllb[Utzl}dc\}wl{zm{{tuzt{tttzl{tt}{{tuztu{{}tsl{ttfkk}|{||sle]cdtts|u|z|edkf]cult}}||u||vlddultultredwlJHF)+2}ultv{slelldlek[TTlksv{|}uztu{{{u{}||umtt[TT|zlek{||{||lldultddctslslejV[{{trfklekv{f]c||lddd\\d\\b[Ullkrg]{{tkd\|}|ttstts||slen|u}lddlddtllkd\utrg]f]cztl|u}b[Ullddc\||}{u{llk{||tsl|u{ttddc}{u{ddctt{{{ttt{UTSv{lek|zddcd\\kd\ztl¾ƣ}ult|vuzt}}{||{ttd\\||sresretslv{}{{t||lld|z|ldd}uzt||{u{tts|]bZ|u|z|u\[b\[bbVUkk]|tsllld{u{}|dc\{zmzll\[b*)(zll[[T{tt|uddc{tt||ult{{t|u}edk{||tt{|uztl|zulttzl||ztltts]cd|z|u{{llklddtllmtt|}tts{u{}tllUMRllkttstll{ttmwSKJ[[Td\\cbV|uyle|ulld}ttssle}{u{zllrfkf]ctsl{tt{tt{zm}|||kd\llk||zzlltsl{zm|{tttlltsl|{{t{||{u{lriv{ult{|||||ü{zm{{tf]c||}{{tlri|v{lekf]cwl{|||{{tsre|zddc{{ttsl}llk\[[}ztl}}fkkultllksle|z{||VTL\UZ}lddedkUMRf]c{{tult{||utttslddllk~uzttsl~|z}j]\|tt{f]c*)(tll|umtt{u{}|{zmtslf]c|zlrikd\[TTtt{|ulri}dc\}{tttts{{t|||utt{~{{VTLv{|z|}}utdc\kd\\[[ut||{{t|ulriv|ttsttsllk|zj]\{u{i\Vsre}||VTLylellkztfmwlddtsl{zm|}{u{vutullksre}d\\kk]}ut{ttut{||ekdtsl}|{||{{t{{{u{{{t|tllv{u{ekd}|kk]ttsrfkv{||zlllriut}{{t{u{tsl{{tu{{ddcttsu{||{{t{u{tlllek{{|d\\ult{zmultlkslks{u{rfkdc\}lri{{t}{{tut{tt|llk{zm}||tts{zm{{ttllrfk}{ttsre||{yfztlJHF?;C}tzlulttll[[T\[[ult||tll|||z||lriekdlldu{{ult|ukk][TT{ttkd\ddc|u{{t||tllek]ddcw[[Tllkd\\f]c{u{llkutlrittstllultsle{ttj]\{||ddcsleztl[TTredlddzll||\[[v{{||{{t{ttlldu{{{||lektsl||ztltsl|z|{{ulri|llddc\{tt|ztfult||lks{{t|{u{tllllkdc\|zulttll}ult|lldlritll||kk]uttts{||rg]||}}|||{||}ukk]v|ldd||~]bZult|slellk}|z}{u{|z]bZ}|JHFlksult||tll}{{wlvslettsut{||ttsztl|uztl{{tek]:97*)(tlltslekdult}|ultkd\mtt||u{{kd\{u{lddekd{{lekVTL{{{|||lldtll|z||tslult|ddc||}{{t\UZllk{u{}ultj]\r]g{||||{u{j]\}tsl|v}ult{yftzlutbVUllkzllf]c||uttts\UZ{{tztl|||lld{{tzltts{{tttsut{||d\\ztfzllult||{u{lksllkztl{{t{{tllkv{{{utkd\lld{{ttzl|u{{t{u{dc\ut{{tjcV{u{lekmttuzttzllrimtt\UZmtt{ttztltt{tll|UUYUMR\[bultuzttll}{||lksulttt{lddsretsl||tll{u{utv{}{{t|z||lldVTL74,tt{||v{[[T]bZlekztllri{{ult{||llkultkd\sleut{{t||}{||b[Umttultf]clddttsuztSKJ[TTultultllki\V}{ttj]\||dc\}{{tVTLutSKJtzlb[U{ttSKJ{ttkd\bVU[TT}{ttuztzllkk]tll{||lddbVUutkk]lritts}|z{ttrfk{tt}ttstll{{{ttkd\ultttslldsle}|}u{{tzl}edkv|redut|}{||{zm}}{{t||z|zllk|zddc||{u{u}tt{lri}[TTf]clek{||lld|zmtttt{{{t||tt{{u{redd\\}{u{tslztlulttllut}}{tttzlmtt{zmJHF:97{{t}dc\ztlztlllkdc\|cbVzlultllk{||ult}v||}tt{{tttt{lriztl{||tll|VZT|ztt{fkkllk||[TTJHFf]c\[[lldultf]cf]c\UZllkultlekyfllek}{zm}wlSKJd\\JHF{{t|zsre{{t||red{{t|usre{||}|cbVtllzll{ttztluttllllk||{tttzlVTLtll]bZkd\u|lld{tt|v{}|{|||tt{{ttmttultzll}lddlddddc||tts}u|uredu{{ztl~}||{ttb[Ukd\}kd\|}|{{ttts{{tlritsllriz}tslultztlzll}||{ttztl||z\[[lld{u{uzt{tttll|{zm}|ulttll{zm{{ttllJHF*)(ultcbV|ztts]bZred{ttj]\tsl|kk]ultf]ckd\bVUdc\d\\|z}|}|zll{{t{{llkuztowlkstt{u{{UMRd\\lddv{UMRj]\ultmwtsledkUTS||||tt{tll{{t[[T{ttredztllldlri}tzl{{td\\u|u||u{||ldd{tt[TTd\\FD;rr]{tt{tttlltsllldd\\ztl{{tztltzl{{t|zztlrfk{u{{{ztllddj]\mtt{ttlektt{\[bllk|z|||tll{||{||u}ģf]c{{tf]cldd{tt}tll}cbVddc{u{||ztltts|}|ztl{{tu{{lddlrimtt]cd]cd}edkj]\|uredultuzt}|dc\tlllri||ztt{u{{{{t|||}tsl}|z}|umtt{u{{zmztlddcztlult{ttztl||kd\|uultf]c{{||>EC:97tslSKJ{{tVTLwl}|{u{d\\rg]|z}mtt{{tbVZ{u{sre||{||zll|z{u{uztulttll{{tldd}{tt|zUTSult\[blrilldf]ctllmwtsllekJHFuzt{tt[[T{||kk]utSKJredv{llk|zvd\\tslultedk}||b[Uztl}kjV|utll{{t{ttdc\||{||lldu}}tts{tt{{t}|{|||{||ttslektt{{tt{||}{{tv{tsltsl{tt}{tttts{zm{tt{{t{tt\UZred{u{uzt}{ttbVU{|||]bZultult|zultdc\llkkd\tlledkttsultfkklks\[blekllk{{kk]ekdtts}{||{u{uzt{{tdc\|z{{tztfllk:977-2}llk{u{|{u{tzllld|zlldu{{ttstt{lriult}]bZkd\{tt||tlllld}|z}rfk|z|{||llktt{lksf]c{u{SKJ}{ttlldtll{ttv{|z|zuzt[TT||llkwlrfkVTLsle||slettslldtts|zsle||aWMSKJFD;ztlultu{{[TTslelldkd\|utslttsuzttllb[Utyg{{ztlsrekk]{{tddcsle|ztts}{tttt{{||ddc{||{{tts~}{{tf]cmwwl|uuztbVUlldddctsl{ttsle|uult{||rg]|}lri{||mtt|ztt{ddc{||rfk}ztlzll||ldd|v{uztVTLultSKJtsl[[Ttzl{||{||{tt{{ult|{{t{u{}|||tsl{u{|u{tt|uzt>B9*)(zll|||{{tllklddbVUd\\lddek]UTSlri[[T{{tslult{{td\\}u{{cbVbVZ}sleb[U{||zll{||}tsl{ttu|zuzt||{{\[bJHFJHF\[[f]cd\\\UZb[Ud\\ttsd\\VTL}{zmtsl{|||u{{t|f]c]bZslelddddclld|ztzl|utllaWMsre{{{zmj]\tll||tslztlf]cb[U|z{||}dc\|zllek]tslut{tt|lriv}tlliq]tsld\\{||{{t|z{{}}{{{||}{u{||}{{}}sle~|||u{{t|||z\[[v{}ztl{||tsllldj]\ttstzl{||wl}lld||lldmttlks|lksuzt{{ttts}{{t||lrir]glldmtt|zlddv{tt{{{|~{{t{{tdc\VTLtllVTL]bZJHFJHF|z|]bZSKJd\\ultddcd\\b[U||zlekttssleultuzt{{t{{||zllkldd|ztts{u{ldd}|z|zMRKddcult]bZlddultf]cmtttsl[TTultd\\lddu{{}tllj]\f]c|ukd\{||{u{[TT}|{u{sreztl{zmcbV||tzltzlultkd\{||llkzllutultkd\VTLsle|zdc\||ztlsresrekk]tslutslelddulttlllddllkSKJiq]tsl}{tt{{{||llk{{|z||tts{{tkd\||{||}ult|zuzttsl|{{t|u}ldddc\ldd|utt{tt{tsl||{{t{{|ttsut}sleut{||ekd|ztsl}ztl{u{llk||}v{{{t{||{ttj]\tts}|zttsddcSKJ*)({||dc\lddtllVTLf]c{u{uztttstslu{{lriu{{f]cu{{lld||||dc\b[Ullkuzttzlddcu{{d\\ddcJHFVZTSKJv{tt{[TTlks||tsld\\[TTddc|utts{||red{ttd\\sled\\}\[[zlbVZ}|||||{||v{rg]rfkkd\|kd\{{tslesrelldred|zsletslztl{tt|u}|}{{t|u{tttsl|sle|v{tsl{tttts||{u{ttstt{mttv{llk{tt}ztl}|z}¾ƣ~}{tt{||ttsut{||}}}ult|lri|||zult|dc\{u{}|u{zm{tt}{ttttsu{{j]\uzttslv{}uztbVU||ztl{{t{tt{{tut{ttllk|u{{t|z\[[dc\:97&&UTSd\\d\\u{{ztl|{{t|z|mttsle{{tlldsletts{ttekdekdsrelrif]c{{mttsle{zmzll||lddlektlledkult{{t\[[|f]cttslddtslkd\sleSKJ\[[leklldtts{u{tll{{t}ztlrfkttssleVTLtllsleztf{u{uzt}f]csredc\}sre{u{{zm{||}|lldkd\{tttlld\\sre{{t||ttstll{u{mtt{tt{{{{{tt{{f]c~ttstts}|tsllriǔtsltsl}|ult|u{u{{{t{u{tts}{{llk|||z{{|mtt\[[||mttf]cd\\|z|uttllmtttll|mttztl{ttu{{tts}|lrilektts}|{||{tt{{t|uuztutv{lks{||{{JHF:97*)([[TSKJJHF\[[lri{||tsl{ttlri|z}u{{kd\v|zf]cf]cVTLek]kk]ultd\\VTL}{||f]ctt{edklddedkuztVTLmttldd||kd\lddzlllks|||{||{u{||lddtlld\\u{{|f]c[TTtll|||u{zmj]\zllj]\{{ttll{{tSKJ|z}|ldd}{ttuldd|z{{{tt{{t{{sle{||uzttts{u{||{{{tt{u{tsltslllktt{lek|vtt{v{}||}|{||{{|{ttv{}zllmtt|JHFlldlks{ttsleldddc\}tts{u{lldllk{||tts|zlek{{ult|uztlv{|utzl|vtlluztttsllklld{{tSKJ*)(lkslldVTLUTSsref]ctllbVZtzllldmtt|z{{t||zlld{||\[[}{||tll|zultlld{||mttdc\MSS{{}lriztlsrezllttslddztl{||llkf]cllkultVTLsre{zmzll|f]ctsl{||{u{dc\SKJultlldnf]c{||}v}ult{|||tts{zm{tt{||{tt{tttsl}|kd\sleulttll|{{t}utllkllkttsllk{u{ddc|u|||z{{{u{}|z}]cd|uztkd\{tttsl{tt|lek{tt}tzl|tll}|redUMRtt{UTS}}}|tlllri{u{u{{tt{uzttts|tslv||}kk]|ldd{u{{||JHF3+*||UTSdc\}|ut[TT{||lkstsl|z||z|z}lri{{|z{||||ekdrfkkd\utts||mtt>B9JHFlks|tsl\UZ{{\UZfkkVTLult|ud\\\[[ultek]llkbVUj]\tzlztlslekd\ult}ldd{tt{u{tt{b[USKJiWU{u{{zmVTLrg]sreedk||{tttts}|zlltll||rg]ztl||ztlbVUlddu{{}kd\lldtsltllsle{{t{ttllkedk{ttttslldedk{u{tt{llklks|sre}Ӕ|zu{u{{{v{lrif]cdc\|uwsretts{ttultvtllult|z{{tv{ldd{tt{||{{lkslek|u{{{{t}{{tult|zllktslmtt|llklddlek{{{{t|v{||vkd\redulttsldc\sle}lekJHF{{tlldkd\UMRUTSlksSKJUTS]bZ||lritslkk]tslllk{tt{||||}|ut|zv|z|ztslllkj]\|z{u{tt{\[[|zllduztkk]||tt{lek{u{dc\f]cuzt|ucbVu{{tzlddc|dc\sle|VTLb[Ulri{zmvek]lldsle{||llkd\\JHFttsb[UbVUztl{zmtzl}SKJ]bZfkk|ztll[[Ttslult}|}llkyle|utll||v{ztld\\{||yletlllriwlj]\uztldduztult{{{u{{{lekmttddc{||{||Ĩ|zu{{tsl||tsl}tll}tts{{t||[[T{|||ztll}mtt|fkkv{|ztlllrilddw|{{u{{lrillk{{tlksylelks{{tsl|lksv{{{}ztlztlbVZ}ztld\\}{u{}srelekedkJHF:97llklldj]\lek[[Ttslldd{||lekdc\dc\{ttb[Ulkstsl|u}}|{{sle{u{tts}lld}lddmtt|mtttll{{slef]cJHFlks|b[UJHF{{tD;9u{{|tts{||b[U]bZ[TT||dc\||ttstsllldllkztl{{lekbVZf]c||v{}|||ztfrg]VTLd\\lldllkttsf]cu|z|u}lld|utll}zllrg]j]\{{td\\|zztltt{uztv{lldlldmtt}{u{|mttllk\[[{{|z|{{lks||{{tuzt{u{||ultddcslerfk{u{{tt{{ttzl]bZmttlri{tt{tt\[[mtt{{}tt{fkk{{t}ztluzt{{uedk}{u{lriultn|{{lriut{{fkk|u{tttzl{{tv{{u{tsllddcbVSKJlddf]cJHF*)(\[[[TTSKJdc\}[TT{{tlddSKJtsliq]b[U{{tek]slelldlrittsultlld]bZddcttsf]cttsu{{ultUMR|ulddult}|z{ttyfl{|||utsltt{edkSKJlksuzt{{tJHFtll\[[kk]||lld}{zm{ttddcb[U{||zl{||b[Ud\\VTLf]c\[[b[UUMRj]\|u{tt|uredsre|{||uztkd\kd\}{{t|tsl{u{lritlllld{ttzllyle|tslttslldekd{{yl]cdultuztult{{{||tt{uztlrimtt||ċ}{{{u{{{|}{tt}||}{tt}|zmttddctzl{{ultf]c||UUYldd|rfkmttf]cd\\edk{u{||||lld|umttf]c}ttslri||llk|ttsred{{t}{{t|tll\[[{{tkd\ek]fkkFD;3+*VTLb[Ulrisreldd||[TTJHFd\\\[[||sre{zm|ztlutztlztllldult|uzt{u{uztult||tt{llklektlld\\[TT\[[[[TbVUztlb[Uttsf]cf]c{||}[[TVZT|ztzl|z{tt{u{{zmrg]ztfkd\dc\{{tultlrikk][TTtts|dc\ek]\[[mwiWUzlltsllldldd|lri|tslut}|srekk]kd\ekdv{bVZdc\}tygb[U||redlldlekult|ztts{{t{{t}{{{{]cd{{tt{{|||z{{tt{{{}|{||edkUUY{{tt{ultuztult{||{{tldd}{{tredztltsltts{tt{u{}tslu{{lksmtt}]cdttsut{{|zrfk|utt{{u{lksttstllultttsf]czllri{u{uttts|]cd{u{{{tllkSKJ*)(|zd\\\UZ[[TJHFultddc|zsleut|zv{ultlldzll|zkd\{{tv{}kk]f]czllddcnultulttt{kd\{{f]cult\[b{||\[[llktt{dkV]bZ{u{|zslesleztlekdlri{||[TT{zmslewl}}|{||wl||dc\~wl}n}kk]tsllriv|ztsl}}ztlcbV|zd\\utult}edk{{ttlltslb[Utlltll||mtt{{tuztkk]lld{{tlks{{}{{{{tt{||tll{{t{{mtt|{{}ztl{u{}ttstsl{tt|||z{||ztlultnv{||lri[[T{||{{f]c[[T{{tlritsltt{|u}|z{{||{||{||sle{u{{u{j]\j]\v{{{t}tll|uztuzt{||D;9*)(f]cVTLlriu{{sre{u{ztlVTLttsddc[TTuztlritzluztlriult|z{tt{{t{||d\\llklri|ttsv{{tt||ultult}uztJHF[[Tutlritlldc\tt{]bZkd\tsl{u{redslelddtsl}sle\[[{{tbVU{{tztfrfklddsre~r]g{|||{u{lri|lek[TTb[Utsl[[Tsledc\tllrg]|z|u{tt{u{kd\{zm[TT||ztltt{uztlri}tt{}{{||edk{{{{|}ќ}tzl{{lks{u{}tts{u{llkv{{u{tll{u{tll|{ttultllkttsu{{|{u{lks~edklks{{lksb[Uyle|{ttut|||ztt{|tlltts{{ttslu}}||{tt||tts{||{tttslfkkMRK*)(leksre]bZ{ttlddkd\d\\UTSUMR{{tuztmttdc\{zmvtllldd|SKJu{{|||uukk]\[blksedklddbVU{tt{zm{{{u{{u{tll|tzl|[[TbVUlriFD;ttsredkd\b[Ud\\{zmVTL|[TTllk||{||b[U{tt}|sle||kd\lldn{{tlld{|||}lri|ttsb[Uek]ztlrg]uztf[TTttsvzllkd\llkuztddc|u{tttt{tsl|ttsfkk}{u{{{{{}||}mtt|ttsęttsrfk{u{zll{||wl|zuztlkslri|zttsf]clekekd|z|lkslriultlksdc\tsl|}UUY{{}kd\sleult{{t{{||{u{{{{||f]crfk}|lriztltt{~|ddc}||{u{ttsultddc{ttultsre\[[||JHF*)(llklldkd\llduddcd\\UTS{u{ddcttszllddc{zmtll{||tts|u}lkssretsllrij]\j]\{||lek}}{||[[Tf]c|zzllkd\ttstsllri>EC|z{u{uztztl}{{t{{tslebVZu{{ldd{{tlld|zlriVTLVTLultkd\||j]\||]cdv{VTLdc\dc\tzl{ttuztyle{{tllkut||}llktsltllztl{{tek]sle|ut}ldd|lld}{||mtt{{{{lks|tt{{||{{|}}|zttslksnvmtt|z{{}|}mttztlult{u{|||{{t||llkddctts|v{lks{u{{{t{u{{{ultlksUMR|lri|z]bZUMRd\\b[Ullk|z{zm}ldd}|{ttsrev{||llkd\\{ttsre|}{{kd\ldd|||v}|ddc\[[d\\[TT\UZ[[T}74,74,lddVTLb[Uuztdc\ddc{u{u{{dc\d\\tsl]bZddcredlritllsle|ekd|leklekultu{{b[Utlllri{{tt{SKJUTS{u{{tttsllldtt{ylttsztltll|kk]\[[ddc}}ulttts}|zj]\|uztlu{{tll|ttsd\\fkk{ttult{|||{zm|lksdc\}|ztf{u{ttsutult[TTek]||ztl{ttkd\ztl{tt{zm|uultldduzt{{|{{tuztllkultf]c|nvfkk{||{{u{{|||}tts||{{mttdc\{{|{ttultzlltllllkttsu{{ekd}tzl}lldttsf]crfk\UZd\\\UZ|ztsluzt]cd|lks[[T{u{|uzt{tttts{||{tt}{u{bVU|v{|||uult}|zult{||}kd\|u|lrif]c|tt{JHF3+*bVZllkj]\tll]cdlld]cdult{{tUTS[[TlriVTL|ulksd\\j]\ttslddlddlddutd\\uztttsedklek{u{|v{|{{tslellkVZTlri|zzlddcttstll|u{{sreb[U{tt}sled\\tts{ttldd\[bldd{zmtt{vtzlf]cdc\lri|ukk]{yfd\\llkf]c{{t}tlltll{u{utj]\zll|sle{{tv{{{tsl{{|||z||lks{||red}{ttultă|lddtt{sleuzt|fkk}}lld{||{{{u{MSSf]cUTSf]cmtt{{|u{{edkedktsl||}}wl{||ult}bVU[[T}tts]bZ}|{||v{{tttsl{tt[[Ttsl}|slev||ultuztuzt|tllUUYddc\[[:97&&red{zmJHFVTLMRKu\[[[TTVTLJHFVTLtslsle[TTllddc\i\Vtslttslri\[bddc||ult{tt|||}{{lksSKJedk{{}lldsle]cdbVZUTSVTLdkVfkkFD;aWM]bZVTLSKJ||uztlrilddldd}tts{ttddctll||redf]c}zlld\\tzl{ttuztdkVVTL}JHFredf]cult{||tll{u{lek{tt{|||utll||ldd|z}ttsut{ttf]c|u{|||z}||v{}lks{u{|tt{}{||{{v{ult|rfk}|u{||{{}{tt{{||zd\\{{]bZ}lld}|{{JHFulttt{tzl{||{tt||lldldd||||zsre|{yff]c|}{{lriVTLek]}llkcbVldd[[Ttlledk\[buztztl[TTUTSedk:97&&\[[SKJVTLzlVZTek]FD;ultd\\|zlldsreslekd\jcV|llklld\[[}uzt{tttsl{||tllf]ctlllldut\[[ultSKJ{{{zm[[TVZTlddJHFu{{lddVTLtlllldd\\tsltslv{ztllrillkttsj]\lriUTSb[Uldd}ldd}ultzllf]ckd\lritzluztwllddtts|utreddc\uttts{{||}wl{{t{{tddctlllri}redddc{{tztltsluzt|u{{}{{{{u{{{{{{{{edk~|||tt{lrimttekd{u{rfklks\[buztu{{f]c[TT}ult{|||zttsult}|zb[Utts{||{tt}}|u|zldd\[[|u]bZkk]sle}ldd{||{{t{tt||ztlllek{||JHFJHF3+*edkek]lldb[Udc\lld\UZVTLVTL[TTlddlld{{ttllldd|}yle{||{{t|{u{{u{{{fkkr]Z{{s_qrfklekllduztv{uldd|MRK]bZVZ[llklldutekdddcztl]bZUMRztfdc\}JHF\[[f]c|}{||j]\tllkd\d\\||{ttttslks|}}|lldllkultlddlld{ttv{rg]{tt{ttlldf]c{{ttllllklddred|{{tu{{mtt{{lks{{{||lrisreu{{||z|zulttts}}}u{{ztl{{tttsj]\uzt|ubVZmttVZ[|ztts{{|uf]ctllekdUTSlks~[[T{{tlks{{ttll|lddtt{}ultedktt{{{t|u||{||lldlksultb[Ulld{{t[[Trfkult|lldekd[TTtslddcFD;*)(ultddcSKJD;9JHFcbVf]cSKJ[TTldd\[[tsl[[Trfk}|lld{zmuzt{{tlri{tt\[[{{f]c{||}ult}bVZbVU{{tult]bZultldd[[Tlddlld[TT|ub[Ulld|u{ttek]ddcddcVTLSKJFD;kk]ult{{tddcuztVTLtllbVZ{||[TTtsllddlritll}|{{t||bVUttsredredzll{{t|z{{tztl||zutkd\ztledk|zllduzt|{{}}}|{{{{|f]ctt{ult{||mtt~{{}|}||{tt{{d\\{||f]cv{r]g{{wl{|||z]cd|z|z{{|z{||f]ctsl||lld~||{{lld||}||tsllddrfk}ldd{u{|tt{||lri}{||{||d\\kd\tsl\UZlddultu{{uzt|z}|f]cVZT\UZUMRlddVTLFD;FD;[[Tut[TTfkk}[TT[[Tlek[[TztfVTLcbVdc\tslfkk|zmttek]||tzl{{tttstt{ddc}VTL\UZ|lriedkdc\|zztlttstllekdSKJttskk]|uztlldSKJ|utsltlld\\f]ckjV|SKJb[Uztluztulttsl|zsle{tt}lks[[Tkd\red|kk]u{{lddllk{{sledc\f]c}ulttllztl||llk{||{{}mtt}|{{nv}ult{{}|{{}|||tzl||zult||lks{tt{||ow|{ttsle|ultredekd|z]cd|mtttslmttedkuztcbV\[[|||f]c}|v{[TT{{tll}{{d\\}}{{t{||ddcddcrfktsluztddcultllkdc\|uUMR}tsl{||tsld\\ttsddcu{{{u{{ttlekVZTddcD;9*)(dc\VTLFD;D;9ek]kk]bVUult{{tlksd\\lrilriVTLlks|uj]\cbVlldVZTuztMRKu{{{ttlriu{{u{ttmtttts|mttf]ctslult}|ulttts]bZ{ttultztl{{tztl]bZ{zmlri\UZ}{u{}|d\\uzt}|}|sre}f]cv{{tlddSKJddcvek]VTL[TTdc\llkddc\[b||tsllldredUTSlldkd\redVTL{||[[TFD;llklddtsltsl{{f]cfkktsl||llkultlrifkkmtt||{{mk}|{{lks{tt||zlri|{u{}{{t||z]cdlks}{ttlek{{{u{mtt{||VTL|z]bZtslultdc\|v{{ult{u{||tsllek}{u{{tt}{u{slev{f]c{{t}||lriztledk{{|sle{u{d\\{||mtttslultdc\|z{{kd\ldd\[[tt{\[[SKJ&&j]\}f]cVTLJHF]bZzlltll]bZllklldtzlsleuztultVTLtll]bZ[[Tu{{tzlllk|z|z{{td\\{||ekdlld{{f]cedklldbVUlddult[[Ttt{SKJ{ttlddu{{lriUTSdc\rfk|tsl{{ttslzll|z||{ttttslldddcMRKf]clkstslekd|{{ldddc\{u{FD;Q?@ult}dc\||z{{t~mttlldult}|z{zmf]cuzt}{||UTSylerfk{ttdc\sle{||VTLtzlbVUlddllkdc\ldd||ztfllkldd}u{{mtttt{uztnvedklkstt{lks}}|u{{||{{u{{ultyltts\[[lkstslllklri{{{||{{ldd|zlks}mttlrirfk\UZd\\\[b{{tslmttlkslks{tt{tt|}||{||{{ultult{{|z|z{{t{{|||z||{||}{{tddc{tt{{kd\lri|lld{ttttsztlfkk]cdllkbVU-1+ldd{{tF=CsletllSKJ{zmzllldd\UZslelldddcd\\r]Z}}|JHF{zmfkku{{}lriuzt}|tt{kd\{||VZTlksllk[TT|uzt}lddtlllriUUYVZTVZTUTS{{tslettskd\VTLkd\tsl[[T\UZ|vkd\|{{tsle||SKJf]csre{tt{{ult|rg]}lddtslbVUlriv{d\\UTSslekk]||{{t}|{ttttsek]{||ztlttslddjV[\[[\UZ{ttult|{{{||fkk{{nv{||u{{{{}{{{{yl{{tts~tzllldlrimtttllztl}zll|{{bVZ|{tt{{[[Ttll|z[[Ttts|z]cduzt{{t{{UMR|d\\{zm}|sleldd}{u{{u{}|zddc{||{{ult|zkd\red}|tzlsle[[T|z]bZb[U[TT\[[UTSf]cFD;*)(f]c|uSKJ]bZlddJHFddclldddc\[[~tzlekd{||ulttzlu{{llklldtt{ekdlkszlltt{uztu}|tsltllVTL{ttztluztVTLlri|{tt]bZJHFb[U]cd]bZmtt{tt{zm[[Tlld{u{sreult{||]bZttslld[TTcbVVTLb[U[TT{zmi\VbVZtll}sletlledkUMRek]kk]VTLlddUTSutultkd\{||cbV{tt|ztllbVZuzttsl{|||lldvtsllksbVZ{||ulttll{{ultmttfkk{{lksmtt|~ekd}||{{{{ldd{{}|zztl{u{{||{{utf]clriUUYmttUTSdc\]cdtts{{ekdleklekttstzlwl~lks|{{tsl{{~u{{}}||u{||tt{fkk}|ultlri{{t|u}}||ztltslVTL{ttultfkkUTSMRK*)({tti\Vkd\FD;bVUPF=dc\UMRdc\sle}tsl{u{ttstlljcV|utyg|u{{d\\mttekd}}|}uzt||zb[Uddctll}}\[[{zmj]\[TTtllttsu{{lrillktllztld\\v{|ztlVTLttstzl{{{||[[Tb[U{{t}\[[VTLJHFlddiq]d\\VTLlld]bZSKJJHF||||f]c}}ddcd\\tll|ulld{u{|mtttsl{||mtt|mwtts[TTtll|z|ddcMSSleklks|}|{{{{nv{u{{{}|zNjlks\[b{tt]bZult{{t}ttsult{||{{{{tJHF\[[LKRMRKVTL\[bJHFu{{u{{red~|zttsv||ttstt{ult}||ekdddcedkldd{zmmttultdc\|llk{{sledc\{tt]bZ]bZVZTb[U*)(f]ckk]}|FD;UMR{tt[[TVTLSKJVTLj]\VTL{u{||tlllldkk]ztltslmtt\UZ{u{{{{||{{tutekdbVZUMR{u{{|||ulek{{uzt{{tekdJHFUMRldd|z|vf]c{||ztl{ttVZ[~{{ttll{tt[[Tkd\}FD;VZTd\\lddb[UtlllldlddJHFultekdudc\tsl||dc\{{tkk]{tt|ultredtt{|ztsl{{tzllLKRmtt}bVU{{lriuztmtt{{tt{}|{||{{|{{|Ѩ{{|}|zfkkf]clkszllvÚlldred|||lld|{tt{u{{||uzt|z{tttt{JHF~}zlluztlriVZTlrillk}|llk{u{|uztl}ult|}f]clld}{{ldd|llkttsekdvtll||{ttleksletll||JHF{||zllkd\JHFUTSlekutfkk\[[ddc}JHFSKJbVZkk]b[UPF=3+*FD;[TTD;9JHFlddtsldc\{ttdc\ut{u{{||||ttslddddcult[TT|è~iq]tllb[UddcUUYf]c{tt|zddc{u{dc\v{tslultrg]|zekd[TTlks{||lek||b[U[[TUTS{ttrfksreslej]\ttsUUYuzttllVTLleksreutwl\[b|ddcultlld||\[[{{{{t{{tlksredult{||tllmttrg]ttstt{{u{ult{u{{{u{{{{ǔtzlu{{ylMSSuzt{{t}}}tts||z\[bu{{{{t|[TTmtt\[[{{UTSult|zlddllk{{llk|uv{ttstt{ultztlv{|\UZ}rfkldd||llkmtt}mttu{{ulttts|v{{zllSKJd\\lriu{{ek]|zsleztl[TTVZTllk:97*)(u{{SKJJHFSKJ3+*SKJD;9F=Cf]crfkVTL|{tt~ddcsrejcVlldlldedk[TTmttulriek]lri|ztl{tt}|u{{[TTd\\VTL{{tultu{{u{{ttsfkkUMRb[Ulld{tt\UZ}VTLv{ult||redlksi\V[[Ttyglddult|uuf]c|ztlltslVZ[tzl[[T{u{ult|u|tts{{t{u{VTLlri{u{ddcultkd\u{{mtt{{||{{|{{t}~|\[[tt{{||}|ddc}}}UTSVTLb[Ulriuzttt{lri[TTultultult{{tlllrilks|zlksutf]c{{ult}tts{||{tt~||v{tzllkssreUTSddcnVZ[lks||ldd|||uzt{{{u{}{||sle[TT\[b]bZsletslVTLekdztlu[TTu{{llkUMR*)(\[[rg]bVUVTLVTLkd\tts|rfk~tzl|||[TTtt{kd\VTL{{tsl|uf]c[[T{{ttsmttek]{{tuztlriVTLlekttsslettsuztkk]r]Z}|kd\f]cJHFddcfkkf]cUMR{u{lldult[TT[TTrfk{{td\\|ultlks|u|tlllldkd\{u{|zuttslb[Uddctsluzt|u}v{{tlltts{||ttsutzll}{{ldd{u{}llk|u|}{{{{}|mtt}|ddcttsult|z|lkslks{{uuzttllmtt\[bmttmtttt{ultlks[TTuztVTLddcfkk{tt~lddyl{{ttslri{{|ztlult|z}|{{t{u{{u{tts{||lritt{ddcu{{\UZtsl{tt}ult}}{tt{{tt{llk{||ddcf]crfk||}|tslbVUf]c|u\UZ{zm|tts[[Ttsld\\|{||MRK:97{ttdc\D;9SKJVTLVTLf]c\UZbVZv{uzt{||slekd\ekdlkstzl}uztlrifkk||tts||\[[ekdtll\[[\[[mttv{ttVTLdc\rfklddtll{u{|tts|UTS{{tv{tllbVUVTL\[bttsult\[bllk}{{ttllddc|UTS|zVTL|zultrfkult}i\V|{{ttt{utlriztllriu{{lddlj|{||{tt{u{}}ldd}{{t}|uttskd\tts}tts|zlks{{|z|edklri||}||æ}lriultttsd\\{||f]c|tt{}u{{lks|mttlld[TTekd|||{{{{tekdek]ddckd\ult{u{ttsldd|tlllek|zuzt|sre{u{[TTtslultult{{t|||z}tt{lksmtt{{{{llk{{ttstsl{{t}rfkVTLtllVTL]bZ{||||tllf]c[[TddcMSS*)(tslb[U[[TaWMJHFSKJuztVTL[TTultddcdc\{zmlldcbVUTScbV}|\UZ{ttult}u{{tll|ttsztl{{tdc\tt{{tt{{tekd\[[srekd\||v{u{r]g|zddclkslldyld\\ttstlltll[[T[[T}|{ttlri|zVTLrfktllsreldd{ttUTSkd\UTS]bZu{{uzttlllld{{sle\[[|uztf]c|ttsddclek|{u{ttsttskd\edklld{||{{\[b|{{tt{{{{{lks}\UZ]cdldd|z|tt{||lriedk\UZttsttsmtt]bZlri|u{{\[[|zkd\lddu||SKJtt{u{{ultulttt{|{|||ult]bZ}UUY]cdldd{{{{t|zlek{{|lks|zuztulttt{tts}ut||dc\|urfkv{{||{tt|z]bZUTSsle[TTllkUTStt{LKRD;9lldkd\[TTVTLPF=VTLVTLf]cultf]cddc{{tdc\{u{kd\lrilldtslkd\VZTred{u{uztu{{|lddldd{tt]bZMSSlddkd\|{u{lks|zdc\}ldd}}lriUUY|z{{:97lksult|zult{{td\\mtt{{tllkrfk[[Tdc\}|}|{||u{{tsle||lek||}tll{{t|z|u{{tlld|zuztldd}lks|]bZUTS{u{{||tts{||mtt\[b||Ù|~|{u{{{||u{{ekd|tllmttd\\lksrfk}{{rfku{{llkekdlddztltt{ekd{{t}|tll|z|zmttrfk||fkk{||ddcultultrfk{{}{{}[[T{u{u{||edktzlult{tt|{{tts{{u{{|lld}|{{tddc|uJHFJHFlddttsult{{tlritsl[TTllkdc\ddcFD;:97UTS]bZd\\VTLSKJrg]lriyle{{tekdlrilldkk]]bZedktsluzt{||edk}{u{|{tttsl|lld{tt|mtt{{t{||[[Ttts\[[|z{{\[[ekdUTSf]cUTS|v[[Tlks{ttlks[[T}SKJr]gSKJUMRkd\}b[UultekdllkaWM}||{{}{u{zlluzt}|zlddtsllddlek||{ttuzt||uztllktt{|z{{{{tmtt|lks|Ī{{{{{{tts{{~ċ\[[tllf]cekd||}u{{tt{{u{{{tts{u{lrittstt{mtt{{mttlekrfku{{\[[|ztldc\lldkd\|lldu{{d\\||ult{{tllF=C}}z~}{tt|}{{llk|zuzt{u{|mtt|}llk{{|rg]VTLFD;[TTUTSekdlddztlllkllkdc\[[TUMRztlUTS:97:97sreb[UlddSKJsreD;9lddSKJldd{||lldsrezllf]csleutuultlld{tt||]bZlri||tsl|ut[[T[[T[[Ttsltsl{{tldd}ttstts~uzt|MSS\[[[TT]bZ[TTi\V|{u{{{t||{ttsrej]\leklddcbVrg]{{t{tttlltyg|uttslldmttdc\UTSf]cut|z{tt|u{||{||ddcultedk{{|zlks|}|||^fsх{{{{{{|ult}tsllksu{{mttmttlksddc{||}mttlkslrimtt}|lritsllri|{{tsre{{tredlld]bZ\[bUMR{{{{redv{}{u{||uzttsl|]bZ||tlltt{mttv{uzt}lksmttuztekdllktlltzlek][[Tlldslettsv{|ddcekdtslztlultdc\,55||FD;74,{{tlrif]c{zmlriFD;\[[VTL{|||zsle|zttsjV[tslrg]tslslej]\sle||lekllk{{ttzlllddc\|{||ek]b[U{ttlrivlri\[[ekduzt|utt{{{lri|kd\UTS\UZdc\[TTlldbVU{{t\[[lldVZTultyle}cbVkk]lri{zmzll[TTsreztlf]c]bZ|UTSlri]bZztl{||tsl||d\\{ttmtt{u{tt{}]cdtts{{lks||}{tt}|}{{|}}{{|zultmttttsuzt}edk|zmttUTS\[b]bZdc\tsl{{nv]cdd\\\[[|z{u{tllj]\|ldddc\|mtt|z}v{lks}v{{wltt{ylv{}ut}llktllulttt{~tzl}|}nvddcmttlksd\\lrilddd\\ekd{tt{{tddc{||}{tttzltts{zmj]\\UZUTSUTSJHF74,lridkVSKJVTLJHFbVZVTLbVUtllu{{d\\srelksztlkk]tslztlddclri{tt||ztlfkk{||tzl}]bZmtt{u{llk|zFD;MSSlrilri]bZ{{tlllkslddllkddctll\[bultuzt\[b}tsl}lks]bZUMR||b[Uultrg]LKR{ttkk]UTSlddVTL[TTUMRlriu{u{lld{||{tt|kd\tzl\[bek]tt{{{u{{|mttÙ}}{{|zulttt{tt{UTSultlksu{{}|lksekd]bZ]bZtsl}|mk\[b\[[kk]|zultztltlllri|zddcultttsek]]cdMRK}UMRv{~{{ultv{llk|zmttmttf]c|ek]\[bultllktts|{{}{{lksVZTJHF{ttultUTSu{{tlllld{{tedk||sletsl{||kk]SKJSKJaWM|ekd*)(kd\kk]bVUVTL:97D;9\UZbVUllk]bZtsl}f]ckd\mttcbVldd\[[ztlztllldmttcbV|dc\b[Ulld|]bZ]cdekdJHFu{{MRKVZ[llkkd\edk{||{{|lddldd{{lks|zllktslztlutUMRjcVj]\lddbVZ{|||z|uf]crfkrg]\[[ldddc\{||mtt|z{{t[TT\[[\[btsltts|||{||lek|redlldmttu{{uzt{u{{{lkstsl}{{u{{{{fkkmttu{{|mttmtt}~{||~~{u{|nv{u{|{|||u{{|ttsult{u{mtt}|mttfkk|mtt|z|ultmttleklldllk{||VTL}UTS[TTlldVTL{||utJHFtllJHFtslkd\lkstslu}JHFleksle}ttsuztlri\[blksu{{tsl\[b}f]c||~tt{{|||tts{tt{||mtt|uztlldf]clrilld{{tztl]cd\[[redVTL[[T|zrg]FD;[[Tdc\SKJ:97*)(UTSVTLaWMVTLSKJJHFf]ckk]llkSKJlrikd\zllrfkd\\lrilriVTL}yle|ubVUmtt{zm{{tkd\tts{{t{tt||z||lddlrilksekdmtttzlult||{{lrimtt}tt{|zredrg]lrifkk{{tlddsle|utsllri}|}|D;9||lldulttt{[[Tlddttslrid\\b[UJHFd\\lldtt{{ttuztkd\{{f]ctsl{{t{u{{ttult{{mtt||{{tt{]bZ]cd{{mttmttmtt{{nvmtt{{|lks{{}lrif]c{u{[TTedklekuztlks]bZlrimttmttmtt~u{{mtt{{{u{||ztllkk]lddb[U||{{tultlekdc\MRKllkrfkf]cf]cUMRtll{{||ultuzt||}mttlksVTL\[[u{{tslultttsult{{{{edk}mttVZ[tts{{uzt\[[tlltsl\[btts[TTztlVZTkd\VTLb[UyleUTS[TTJHF>ECSKJVTLJHF74,[TTrfk}b[U}f]cllklld{||UMR\[[VZTi\V]bZlriekdVTL|ztll|zfkktzlfkk[[Trg]ekd{{tll||{u{g\rUTSedkkd\u{{mttmttSKJult|z{u{ddcd\\{u{lddsleldd|z]cdred|cbVd\\f]c|zmttd\\\[[utttsu{{uztuztttsult|z{{t|||lri}mtt|zlkskd\{{Å|{{lks|}{{{{uzttsl|llktt{|z[[Tmttu{{|||u{{lek|lkslrimttu{{lld|z}lrib[Uekdr]g\[[VTLf]c}{||f]cmttult|{u{f]c||tyg{tt}lekrfk{tt}|z|z{||llkllk{u{tts{{{{lksttstll}uzt{{\[[d\\ek]d\\edkllklddtsllri[[TVTLb[Uultlddu{{UMR*)(aWMVTLd\\VTL[[TD;9[TTb[U{u{]bZ{{tttsztlSKJlld||tsltsluztddc|u{zmekdkd\ddc\[[lldttstygb[U}sle\[[|zlri|uzt]cdlri{{|z]bZf]c{{lrimttb[Ud\\\[[||lldlld{{lldtslsleekd|zldddc\tslj]\VTLv{u{{MRKVZT|uztsre}|z|z{||||{{ttsl|{{ultmttVZTtt{{{|}}}}~|lri|z}ultult|{{lldu{{uzttsl}|nv{{mtt{u{ttsekd}lddddckd\uzt{{SKJttsUMRdc\D;9{ttMRKsle{{}{{tll{{t}lrid\\kd\{tt|}}{{t{u{}|u{{mttu{{tlllks|{{f]c{||{tt{{{{lrilkstsl{ttlld{{tmttultkd\rfk{{]bZkk]tllVTLUTS]bZ]bZ:97}VTLtzli\VJHFi\VUMRVTL\[[\[[mttnv|uzttll}|bVUultlld]bZedk|sre{ttkd\bVUf]c}|z[TTtll[[Tllkd\\||}u{{edkVTL\[b\[bVTLdc\{||||ztlttsred]bZek]cbVllk]cdedkd\\lld]bZkk]tslUTS[[Tedk{||lriddcu{{mttsrei\V{||uzt{||{{{{UTS||||ttstt{tt{}tll|u{{}{{Ümtt]cd|~{||lri]cdu{{]cd~|u{{mtt}mtt{u{cbV]cdf]cSKJttstllztlddcttsekdlrilksSKJ]cd|[TTlriddc[[T{u{lksylttszll{|||ztsl{{{{|{tt}tt{lri|]bZmtttt{tllddcuztbVU\[[tsl|zsrecbVyfdlddred[[TJHF*)(kd\VTLek]VTLMRKD;9{{t[TTlddJHFVTL\[[tt{{u{{||zll}ekd|tts}tsllekkd\VZT\[[{ttSKJ{{tzll|u|ztsltlluzt{tt}u||\[bcbVnfkkbVZSKJzlttstllkd\}VTL|zlek[TTtsld\\]bZult[TTUMRultrfklri]bZUMR|lriVZT{tt{{|z|ztslult||[[Tfkkuzt}tts{||{{mtt{{nv|{{u{{f]c{u{}uztedk]cd|zVZ[mtt]cdddcuztfkklkslksUTSSKJtsl{{t]cd{{tuzt|ztts||fkktt{lrif]ctts[TT[[TVTL{u{ttsmtttzl{tt||}{u{{u{{||u{{{{lkslksttsf]c]cdlks|\[b|ttstsl{tt|tll{||uztlld|z[[TVTLJHFlld[[T]bZultUTS*)(dc\[[TJHFaWMD;9D;9jV[FD;JHF\[[b[Uf]cultd\\lek}tt{ultiq][[T}zl|ek]u{{}uttzl|}rg]j]\}zlllddek]mtt{{tttslri{u{|tll{{||lldMRKSKJF=C{{trfkr]ZtllbVU]bZekdsle{tttlluekdtlltllv{ult{u{\[[ultfkk>B9tll|cbVekdu{{||{{|ttszlllddmtttslttstt{{{ttts|{ttmttlriuzt|\[b}}{{~u{{lkskd\f]c|z|mttVZTnv|llku{{ult}f]c{|||zllk|z{{t]bZsle||dc\||\[[{tt\[b[TT}tzl|VTLek]JHF{u{{{{ttuzttsl}~tll|ztl|]bZ{|||u{{{{{ttu{{mtt]cd{{||b[Uekd{ttfkkZbN|z\UZekdSKJVTLldd[[Tdc\VTL:97*)(slejcVD;93+*JHF74,JHF[TTult}|ulldttsult||ult[[TttsUTS[[Tek]ddc{{|z|uultultultlksuzt\UZlritslek]{u{tsllri]cd?;C{tt[[Tsle{tt|ulriJHFddcdc\ddcd\\tlluVTL[TTttsd\\ultttsb[Uult[TTUMR[TTlri{||ttsekdfkk}|}}tllllkēmttuztztl{|||zu{{tslmtt{{|}|}}ã|vlks]cduztmttu{{{{tts|fkkmtt|{{edk]bZlks}|}rfk{{ttsl]bZ[[TUTS~u{{|uekd[[Tdc\u{{VZ[lrilekuztuzttsllrilriultd\\|tt{mwtslVTL{{{||}tslllklriult{{}tt{lks]bZ{{{u{{||f]cdc\SKJtsl|llk[TT[[TztlVTLsrett{d\\JHF*)(j]\cbVJHFSKJF=CF=C[[T[[TbVU}{{t||{u{tlllekllktll{zmdc\kd\{{t{{tlri]bZwl}||red|}sleSKJlektll~}\[bultleklritsld\\{ttddcv{mtttllb[Ud\\kd\b[UbVZdc\lddF=Cf]clekmttf]cmttttsu{{lri}|lldtsl{u{fkkuztf]c|{ttlld{{|{{}}|tt{{{tÜlkslek~mttlddmttUTS|mtt|ztt{{||{{lks{{ttllkk]uztVZTddclekbVZtslkd\lksVTL]bZtlluzt[TTtll]bZekdu{{{{{{|zlri{{{u{lld{u{v{ekd{{tzll||}|{{tts}f]cmttf]clri{{mtt]bZedkmtttt{dc\]cdu{{ttsllk{tt|zztl]cdlek[[T{{trg]VTL[[TSKJldd\[[*)(sleJHFbVZJHFJHFVTLVTLkd\[TT{{t||ttsrfk{{{{ttslllklrillkttsu{{f]c}}{{tek]lksfkk}ttstzld\\uzt]cdultttsd\\UUY|||zddcdc\UMRdc\mttrg]leklddlri{ttjcV[TTj]\ek][TT{tt}|llk{ttv{}|ztt{\[[]cd\[[||{||{||tsl|ztt{{||ekd}|zsle|ekd]bZ|zdc\}||{{{{{||{{|ultČlks]cdlksmttlri\[bmttmttult{{t{{{{t}tts{{t]bZ[[T|zuztllkf]c{{]bZek]tsl\[[JHFmttmtt|{||ddcztl[[T\[[}lld{{tllduzt}lkslld\[[tsl}uzt|{{lks{||VZ[{{||leklddlri{{[[T{{tsl{{t]bZ|{u{{zmedkddc[[Tdc\VTLztl\UZlddLKR*)(ztf{{t{ttUMRVTLj]\|zrg]v{f]ckd\tts{u{fkkv{tt{slekd\dc\ttsuztlks{u{tt{{||{tt\UZ|uf]ckk]{{t{{\[buzttslmttlkstsl{||SKJ\[[[TT|[[Tdc\tllkd\lkskd\uttzl|u{zmtsld\\kk]lld{{ultf]c||ult]cdlkslks{||lek}uċekd{||ult{{|ztts{||{||{ttlks}||}|{{|{u{\[bѣř{||u{{|mtt\[[tts}|mtt]cdfkkddc|ultlriv{||d\\[TTkd\kd\fkksre\UZJHFmtttts{{VTLb[Uutmtt\[b\[b|{||u{{|[[Tlld}}{{|u{tt{u{|{tt{ttmttunvmttmtttslvultmtttt{||uztu{{ultmttUUYult{{t{||]bZsle\[[UTS[[Tllkdc\VTLVTLVTLd\\jV[SKJ\[b74,red]bZlddekd[TTSKJ]bZSKJf]cb[Utslddcztl{{rfkvultdc\kk]{{tkk]dc\ult{{t|ud\\}[TTtzllri[[Tekdtsltlltts|z|zfkk}u{{{||uzt]cdJHFlksdc\[[TtllSKJtsl|zmtttllVTLsretll|{zmf]cllk{{t{ttUTSztl{ttllkf]ctllf]cSKJJHFultu{{JHFtt{}{tt{u{mttvdc\leklldkk]{||edk{{t||ult{{mtttts|ztt{uzt{{mk{{{{}lekd\\|}f]clks|z|z{{tfkkVZ[|{{^fs]cdmtt\[bult{{f]c||tslddcJHF|uutf]cult\[[lkstt{{ttultnv\[[]bZekd]bZJHFddcultkk]|{||tll||tslult\[[|SKJf]c|{||tslmttlks{{mtttslultyl\UZtsluztultuzt||ddcd\\kd\UTStslVTL[TTlld]bZrg]d\\LKR*)(bVUVTL74,JHFJHFf]clldUMR{tt|z|uztb[U}{u{ztl{{tkk]b[U[[T{{tdc\v{ut{{{tt{{tlricbV|zlek||tts{tttslult{u{{u{tts|zmttUTS\[bUMRd\\ekdVTL[[TbVZult{{srered[TTbVUf]cSKJsletsl{zmu{{}[TTSKJ||bVZlddllk{u{SKJf]clkstt{MSS{u{||uddclkstslult{{t{||{{{{\[btt{{{mtt}}lksmk~tts{{t}ttsu{{|tsl]cdu{{{ttlks]cdUTS}nvmttVZ[\[b\[b|}\[[|}|{{{u{MRK{{f]cllkkd\{{lkslksmtt|z^fs\[[mttu{{{{t|zVTLtsltslVTLfkk{{ultuj]\u{{|f]cultLKR}lddtts}lekuzt{u{|lksmttlri|lriuztlekultd\\dc\UMRek]sle[TT]bZSKJ\[b[TTMSS-1+VTL[TTPF=UTSddc\[[lldutred|z|z||u{{{ttbVZlek{zmj]\VTLmttdc\tzl|{||lek}{{t{{t\[[}UMR}tt{|z{{fkk{{|zwlVTLtsl[TTlksvSKJ|zddc{||}lddj]\tts|vJHF[TT|urfk{{tultslettstll{ttMSSf]cult\[bldd{ttultf]clek|z{||||}lld|uzt{||mttedk|z|{{mttuztllkult||zf]cf]c{{}mtt]cd\[[ldd|mtt{{VZ[lks{{lksmtt{{lek\[blks{u{{ttSKJ{u{tll{u{VZ[tt{llkddclddrfk|{{tt{edk|mtt]cd]cdlld{||{ttult{{]cd{{t{ttu{{u{{|z[TTtt{{{|u{{ekd{{t{||tllfkk{u{VZT|{||ddc|ultldd{tt|zekdddcUMRztlfkkSKJVTLdc\D;9JHFlldVTLlddJHF,55sled\\:97JHFredbVUlri{u{tllUTSVTLlriultf]cultult]bZ{{tsl]bZ|uf]clekultdc\llkVTLVZTlek{{UTSd\\{||tt{}ult{{tJHFf]cult{ttsleztlrfkredrr]sletts{||{{rfk{zm\UZJHFddc{{\UZLKR}{u{ldd|zmtt||lksut|u{||{tt[[Tmkuztlks{{Č}~mtt{{t{{t{{}]cd{||{{ult{{{{ttstt{{||]cdtts]cd|znmttlkslksultu{{}}}uztg\r}edk||f]c|{{|lks\[blksnv|z{{}|tlltsltslulttt{}mtt~mttfkk}ttsfkkttsu{{{{{{\[[mttmttUMRllk}mttuztUTSfkkUTSSKJ|ztsl[TTSKJ[[Trg]aWMlrilddcbVultLKR,55wl[TT\UZddcJHFtll[[Tultllk]bZtzl{ttult[TTtsl||SKJmtt|zttsek]lddllkd\\{||d\\mttJHFVZTbVZlksmtt|}}{{u{{lks{ttsle[[TUMR?;Cf]cSKJ}lekttstt{f]c|UMRkk]ztl}|VTLuztf]c||VTLlddldd[[Tekdd\\mttlks\[b{|||lksldd{{tts}|zu{{{{||||}tll}||tll{{{{љu{{b[Ulld{{||ult~|z|mttlkslddmtt|{{nv]cd|\[b|zlek\UZlkslek~}}lks}lks{||{{LKR{{\[b~|mttd\\lri}{{t||lks{u{|tllf]cfkklriu{{ultu{{{{|||mttlkslkslks]cddc\\[[dc\[TTultddcsre{ttlrikd\ztlVTLMSS:97tzl}ultSKJVTLVZTVTL[TT[[Tf]cutllkdc\lekb[Utts}tt{|||mtt|zuztlld|f]clksultult|zddc{ttJHFtsl|u|u{{|ttstt{}tlld\\iq]lriUMR|d\\llktlllri\UZ[TTsle}\[[ult}ztl\[[||]cdJHF[TTVZTVTL\[[ekdSKJ^fsmttSKJdc\lddult}|}{||uztllklldtll{||{{tzlltts\[b}ultu{{ulttt{lek{u{}{u{|v{}|lri{{|}mttlektt{v[TT{{t{{t{{tztl]bZ{{||tts{||JHFVZ[lksnMSSmttmtt{||fkk\[[|ldd}|nvultdc\tt{edkUUY{||{{mtt]cd\[b^fsmttmttmttf]cmtttslekduztUTS|utleklrid\\f]ccbVlddSKJlldUMRr]ZVTLOS`)+2|llkFD;[[TSKJFD;SKJVZTUMRJHFJHFtsllldredSKJ{||UTS\[bekd|z|tts{||mtt{||lri]cdvuztztl[TTztltts\[btsl{{{u{UTS|]bZ{{JHFddcult}|\[[d\\srelri{tt}|rfksrebVZdc\edksleddcu{{\[[ekdf]c\UZ|d\\|z[[T\[[\[bd\\{{td\\||lritts}{tt||||ttsUUY{{}Æ~uzttt{~{tt{||{tt}lks]cdlriddcult|uzt|Ìddcultult[TTtllekdiq]]bZekdkd\{{\[[ekdf]c{u{lksmtttt{||nek]|lksuztmtt{{lks{{~tll}|{{lksddc{u{{{tult|mtt|{{lksfkk\[b]cd\[b]cdfkkddcuzt]cdlri|z{||[TTb[Ukd\sleb[UyleyfdllkJHFaWMlriOS`*)(tts}{ttd\\sre{{tb[UVTL74,JHFFD;UTSVZTkd\redjV[{u{ekdmtt{{tult{{t|zu{{lkslri\UZuzt}[TTlri]cdddc{||UTStt{|tts{u{|uztultUTSlddztl|zek]{{:97f]c\[[{ttlld{u{|z{ttrfk{ttsref]cUMR[[TlriJHF||\[b{{ult]bZult}||mttekdtt{{||{||}{{t}u{{|{{ultmk}~ult|tll}|lks{ttVZ[{{]cdmttUUYultylf]cf]cwlddcJHF[[T|edkulttt{edkmtt}]cdMRK{{|OS`lri]bZLKR{{lksuztyl}edkttslekmttfkk{|||\[[f]c\[b]cd{{lks|}||lksVZTf]ckd\b[Uf]c[[Trg]red{{tekd]bZtslJHF,55mttlldbVUFD;b[Uzlekd[[TekdlriSKJlld}{u{{u{d\\UTSlks|zlrimttu{{mttllk\[[llk\[[ldd{tt{||lri{{ulttsl{{mtt|{||}}ult{{ttsdc\{{t{{t[[TlksJHFJHFd\\|z{{|utsl[TTrfkVTLrfkdc\FD;sleekd[TTkk]|lld}{u{JHF{{\[[UUY]bZ{u{SKJllkzlllek{||||}lldlri}ldd|z{{llklkslekmtt{{|{{ \ No newline at end of file diff --git a/thirdparty/ode-0.16.5/drawstuff/textures/sky.ppm b/thirdparty/ode-0.16.5/drawstuff/textures/sky.ppm new file mode 100644 index 0000000..8b541b1 --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/textures/sky.ppm @@ -0,0 +1,5 @@ +P6 +# Created by Paint Shop Pro +128 128 +255 +}}~~}}y{}}z~|zz~zz|~{y~~~~|{x|yz}~xvuwzzzzyw~{zz}wuttyzyxw{~~zy~||{xwvwvwz~|{~}}~|zwyxx|~~|{||zz~~~}y}~}z{~||}}~~||||yw|{{|wtz}||~~vsuz}xwvw}~~zxwz}}|{{}|}|z|~|wuux{{~zxvuw||}}}||yv||~z|||~~|{y~}}z{|~~|yz}~}||x{|}|}z}~ \ No newline at end of file diff --git a/thirdparty/ode-0.16.5/drawstuff/textures/wood.ppm b/thirdparty/ode-0.16.5/drawstuff/textures/wood.ppm new file mode 100644 index 0000000..a53d2dc --- /dev/null +++ b/thirdparty/ode-0.16.5/drawstuff/textures/wood.ppm @@ -0,0 +1,5 @@ +P6 +# Created by Paint Shop Pro +256 256 +255 +ǿżŰ̻ÿǻʻǻŻÿ¸øŸۼƿ̼ÿÿʻõƼɿǾøشſ·ɻþǼƾŻǼʿƿÿǿٳſ̺ɿǼɿ»Ƽǿпƿпǵص̸Ǽǿ¾ſѾ׼ʾǸ¿ŵƻɿƿ̼¾׿Ƽ°»ºɺغǸۼſƿžؿ̼ƾ¿μſžÿúƸ¿¾žÿɻͿŸɿÿ»οüʿſ¿»ξǼſ¿μǸ¿¾ƾ׼δ¼ǻͻüп»ſŻžǼþÿþǿ¿ǿ¾þÿ¿¾úƿ¼̾źſº¼ƿѻŷ÷þþ׾ŷƵüſ¿¾ƸɵƼ¸ÿƺʵǼǻ¿ƾƼɺžžżƾƿſƾ÷ÿ̷ǻþ̿úƿÿԻżξþɸûпž̺Żí¾ǼʾʻŻñɼͿλпƻ¿ŻѼտúǾÿżԼԿʸѺǿÿſ̼;ͿռżԼƸǾſʼŵ¿ǺǺƾǿÿóοƾ»üѿǿƾÿƷҼƾü׻źþûѸܾҿûǿÿ·຺ƿÿ¾¸߾»żƻƸ߻þſҸмݺп̼Ƽ¼ſ¾;վÿٺԵλŵƾ¼¿ǷǸ¾»ܴѻǼúüʺ¼ưźžб׼Ƿ¼¾Ŵƾ¾ܵٿƼʼż´¾ƿº׾຺¾¾Ϳ;º̴¾ƺûɸžҸҿ;ƿǾ㸸Ե̸Żſ¸¼ÿ걱ƻ۾Ʊſ¼о»ǸµǷ߸ƿ¿Ǿؼ¿Ѻ¿¾ξûú侾ѵ¼¿ͺƿ¸Ƹ¾ƻǼվ¼üÿƴ۳һǼŻ¾¼ػƺɸ¿రưǸƸøſ·¼žõ¼ƿʿԾ÷·̼ƿ¿ƺ¿ƾ¸·̿Ʒ;ʼøſҾŵźҺǼͻþǿžտ±ŷɸžƿٳʼ̼ο¾¿÷þɸþſԷߴɼûþû÷żƸǺÿ¼ÿ¿»Я¾ſɼǻÿ¿üγɿƿɻǿÿɼż̼ſұǿ¿»ǺƵ¿ٸſɾſ¸ɾſǿºþغƾ̿¾¿λ¿¿Ǵƾſǵɼ»θƸ¿»ŵǾƿǿɻɺž¾żÿ¿úþµɺ¼¾ŵǻ¿ÿ¿Ǽɾ¼ÿƿ¿ſº¿ʺ¼źô俿ƺºŵžð¿Կúɻÿɿٻ»źƾξǾƻ⺺üξƻûǿƿÿǻÿ¾úµݵþǸ¿ǿ¼Ŵÿ¾ʨ¼¾ջƼƿÿɺþø¼¿ÿΰμ¾¿θѸ»º»¿´¿»ǿͼɼο¼ž¿üŷǸǼλ¿ÿŸ°Ƽ¾ø¿μ¿¿¾þɾžƿÿôŸ¼¿ż¼ÿ¿ƿſʼƿž¾ɸź¿ú¿ü¾û¿̿ʾ¾ø¾¿¾µǼſ¼µǼµƾƿÿ÷¿ƿƿ÷Żÿɺǿú÷¿¼ŷƿ¿ɼѾ¿¾üʿ¾üԱͿž¿Լ¼»¾ر¿÷Ÿüп¾ɿ̵þ¿Ǽҿ¿ʿ¸¿¿ɻɸþºҾ¿ż»Ǿпž¿¾¿ܿ̾·ÿƷǼɼ¿øƿþɻ¾㾾̼¼óɺú۷¼ñ뾾ɺǸ۷þɾ¾ų̺;¼̿謬ǾƼǿʺɿſżٺ¿ƿƼҾþżž̵¿ÿûʴݻǼ̼ü¿üƻƸݵÿǺ¾¸õƴٵɾ͸¾ͻƿپƻƾ»ƻƺ̼÷ѳǿ¾ſƸ¼һſƻ͸þѻʾξóοüƺͿ»۱̾Űеüʾíº¿ɾ㷷¾¾ƴ÷Ƽŭ¸ºŵ߷ɿƵ߼Ǹɼɵ¬ƿܷ̾´ݸºõ̻¿ŵ´üɷܺÿ;õžþƵƷƸαÿɻɷÿÿǸ¿»Ÿŭΰ¾Ŵʿþ໻ƿʸþǺſɾǷ͸ü̿Ƴ¿ⳳƿþþǴ俿ÿƺú´źźúǩÿźпʻ¾¾ſ¿ʼпʻ¿úÿкÿûþ¸ѻ;¸Żѱü۷ƿǿƿ´Իͺô¿ұÿþ¿̾ɸǿǺżͼºƴƸǿ߾ÿͻ³żûɾƳſ´ſհǺ״ſ̺¿¾ǿ¾̼ɿû³ÿץШǻƿøŻµ̻ɻôǿצɬúǾкþмѿŵ¾ըűþǾƻžźлƿѿǿêſõɱž̼¸ÿɻƿɾ׿ŭ¼μ¿̴¿¿οƻƼ¿ѺůǻоǬ̿¿ɻÿǿ̿ƻð¾ʼɵÿǻ¾Ϳþÿżűž¼ɾƯʿżôǰſüʿƯ躺Ǿ¯¸ÿüʼ绻žõ»Ŵͷɿ¼Ϳ̾ǿƺҿ¾λ¿»ЬɭʺǾ̿¾׾Ծ¿þ¸ҬƸƿ¿ƿׯŻɻž¼տ¾üҿٵ¸ÿ¿­翿DZ¼ʸܻÿŻÿ÷⼼ž¯¾ܼɷ¿ɸ¬»պǿ۷̿ǻǺñǺƸͿÿþ۱¼طÿǼǻۼѾ»Ǽ̯µ̷¿¿õʻ¿Ǽÿº̱¼ÿ¼ʿ¿Һ¿ʿͿǾʵŷǾƿ¼ú׻оſͰʼ¿ÿüظмʻͿ;еξ̾¼о¼Ǿ¿¿ǵƼʿοƿŻ¿;͵ۼŸտƺԸż̿ذƺſɾγŷݸ꿿ſɿ۵¼մоʼ󿿿ۼ¾ѿʿ¼¾үѻþվſǴǷоſƾ羾ʺʿ¾¿θƦοǾ྾ѾػǼŻ̼ûҨþͿûԾԴҺ¾߿СɾŻ»ÿº׻հһʻؼټШüǾɻƻǻݼ׺ŵü¿Һ¿ʨ¿ټźƿþغƵ̼ƺÿɾԻίɿǰǾžʼǺǺǿʿʸʼԩվƷ»ƻ྾ɿ̼ƾŷ̺似ŷ̿ſɳԸ̾»ǾŻµ¿ߵͿ»ø¾ҺƼŷǿŵǾ¾ܿǸɼǺſŴôξͿο¿̵ƸǾƿҾҿ뼼Һʴɿ¿þÿͷξóݻ۷̷ǿɻƺǸ¼Ǿƴ¾۸ε¿ÿþ̾ŻɷصʰǼиɻƺ̺ͬþûŸ±ƻƻͼ¢Ѵƿʸ㾾ƿ»̼üǸ¿̿ɰ»׻θ¸¼ŸкɼʰʿƯºƿԷǸշʱ·ͪ¿ǻƸþܸҴ¾¿лµͿ侾¾üſûҴл·»л俿ÿ¿þƻ׸ǷʿԿüþպ¾¾һͿԻ»»¿¿ɻƾžſżз̸Լ¼λ°¾÷տͿξŸ»оλúۿÿͿܸƾɰƳ¸Ƶ»ѾɼŸŵ⺺¿»ſվ¼ұƼ¼¿̷ƻ¿ݷþø㻻վ»ǵ¾ø¸ʸͿÿ׵ſٱѺƿǷžʰ¿̷ɻƺο¿̺üͼ·ǰ¾ԾƼDZÿƿƺ׵ÿԾſгԿ·þ׿õ¿źÿճɺſþű³»ü¾Ѻñп¿ɼ͵Ƽÿκ³Ƹû¿з·ſѺҺǾʿѷǴż»һ̼źԪžż¿¾һµõƾžþλ±þԯǼǻþѼҼ¼ÿǿμƾ¾Ԭÿ¿¿¿űʴ̾ƿſûܭɻѾ¿ѵǿúüƿ¿կпżƼƸ̻úƾ¾ñʻſ¼ðκºǺµҵ÷Ʒſÿʷ÷Ǹ̴ƾǾ¿¾ûþξ»Ϳܿ̿;ƿ¿ɾŸ״Ǿʵиſdzʼɼǵ׿»ÿξͼ̾Ѹʻż¿¿ѻƼ̾¿ſ¼¾̺øμεžſ̿ŻͻͼžżÿʻººþοøҼž»žͻɿű¾¿ž¾̿̿ۿŸżݼƺ͵¿ʿƴ¾ɳʹѸǸسǾǷ»¿ż¸ó̸¿ѿŻ绻ɿžǻʿ¸¿ʿú¸ŸžǾպ¼ƿǿ¿ɸ¿̼ƾѻƼþſүܾʾ̺ǿûԷƻԾ¼¼żƻÿɿø״ÿƷǭƿſ¿׼¿¿ƿ¿Ǿʺͼÿſżյþƿ¿ŵżһƿռξ»οžüбƾʿÿüκ¿Ƹ¼Żƿ¾ǿθŻûǵ¼ɳºǿ߾¼þüſ·μɻþѭѼɾʵþ·ǿž¿αͿɺ¼žŵžƿſ¼ÿƾǾ̷ʿοúɯ¾ÿҺؿǼǿſ⼼¼þôǷÿǾдźѾÿþüżſǾſɺſüа¿ſѿ;ÿǾÿƾ»¿иܴ̻»¿¾ʷƾſźºž¿ûºüǺóþſþŵºż¸ƿǼѴ³̾ÿþ»ôп¾žþǸüʵ¿¿̸þ̸¾üʼƻƵƸſƸ¿ôÿþžûмǻʿ;̻žɷƿƿ¿ʾøþɸƯ»ѸʻŻɵ»ſɼ±ǿλҼð̺Ƹžɻɼÿοÿ°¿žپ׼þɿ»۾žŻûƾƾ¿Ǽغ͸һǿ¿ۼú¾ʾ»ÿǿ·¸¾¾ԿżǷͻŷþ¿λ¸»ûÿÿξǿ̻ſ¾ô»Ƽɺɿ¾ƻƿ̿¾ɻ¾мɼǺؼ̼ɷɾÿɸŵÿ̾ſøźδ̷¸ûžƾױſ¾¾ɺʴɯɾǾ̴Ƽµ̷¼ƸÿƱ¼ƾÿɻƼ۴Ժ̼¿ɳƸͻ¿иźżû±ǵμ·¼¾¿¿ǾƼ¿ɻú¸͸ſô³Ʊǿûžûƻ¾¿»ɼʻԷŵ·ǼżüÿԿŻƿ±ƻɻǿŷξſ̿¾̸ͻҷøɼ»ʿ¿Ÿüٿſɼ¼Żͺ¿ż¸λƵƿʿŰƾ¾͸ƸɼžŴú׷żüʿҷ÷ŷǾͱɾ¿̷Ƹóż»ƺԸƿŵżƼηžʹоʿѼǴξøñǿ¿ѿɾ´¼żô¿ʺûƺƸп̺Ѱðűɿɴ¿͵õú»¸̺»ž¾Хƿǻ׾ʵ¾Ʊ̾¿ǼʿüǿDZ¼Żʷ¿þžûүǼɴ¿ƿο¾Ǫ¼µÿʾºƾǸ¼Կ¿ɺúɼͺɾѾǻþŪ´û¿ǾžøջóżǭƿʸǾͿþʿſ;ɿǾÿ¿ƺƼʿ¿̷ʻüؾ̱ƺɸ߿þ¬̿þ»Ǿíƻվ¾ʿÿ۾мͻɼ̺úſ÷°Ǽ¿þ»ŸͰµʺ¿пźº̾ʼ¿ǿ¿¬оʷʼ·ͼǿúεɿƾоÿż»þ̺ͿпøûƸžʾþʾտʸũտſðҸǼ¸žпƺÿüɺѿ;þƾλÿŸ·¾̿ƻþſξ»վ·¿ÿ⻻ſ¬ø¼̺̿ѿɼÿɻƾǾ»ⳳ¿ʻ¼κ¼þ̾з͸οſ´人ºðºƸ̸ûѴпºûżܿž㷷ǿ¼ʴ¾Ƽɺθ;ɾʷδξ̻Ǽʾ侾зƿɺγصѿаʸ̺¼̿໻͵žÿźſÿҷ̻ǿ¾þ似㿿̴̿¿ú̾ʾɷºɿƻøµ侾ʴɼüͿഴξǼ;ǿ°ɾƸ㾾ѿ̾ǻžѳ̺ɿ͸ǿƾƿǺǴſ÷Ǽ¼ߴοԺ¸ɿǿǿκ¾ʹÿûž糳Ǽü´ºŷ¾׿¾ź¾ɺؿ̱ \ No newline at end of file diff --git a/thirdparty/ode-0.16.5/include/Makefile.am b/thirdparty/ode-0.16.5/include/Makefile.am new file mode 100644 index 0000000..0a0830e --- /dev/null +++ b/thirdparty/ode-0.16.5/include/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = ode drawstuff diff --git a/thirdparty/ode-0.16.5/include/Makefile.in b/thirdparty/ode-0.16.5/include/Makefile.in new file mode 100644 index 0000000..3a7f2b2 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/Makefile.in @@ -0,0 +1,640 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = ode drawstuff +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/include/drawstuff/Makefile.am b/thirdparty/ode-0.16.5/include/drawstuff/Makefile.am new file mode 100644 index 0000000..14a9fb1 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/drawstuff/Makefile.am @@ -0,0 +1,2 @@ +noinst_HEADERS = drawstuff.h version.h + diff --git a/thirdparty/ode-0.16.5/include/drawstuff/Makefile.in b/thirdparty/ode-0.16.5/include/drawstuff/Makefile.in new file mode 100644 index 0000000..3bc421d --- /dev/null +++ b/thirdparty/ode-0.16.5/include/drawstuff/Makefile.in @@ -0,0 +1,528 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/drawstuff +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_HEADERS = drawstuff.h version.h +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/drawstuff/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/drawstuff/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/include/drawstuff/drawstuff.h b/thirdparty/ode-0.16.5/include/drawstuff/drawstuff.h new file mode 100644 index 0000000..9a3ac20 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/drawstuff/drawstuff.h @@ -0,0 +1,325 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/** @defgroup drawstuff DrawStuff + +DrawStuff is a library for rendering simple 3D objects in a virtual +environment, for the purposes of demonstrating the features of ODE. +It is provided for demonstration purposes and is not intended for +production use. + +@section Notes + +In the virtual world, the z axis is "up" and z=0 is the floor. + +The user is able to click+drag in the main window to move the camera: + * left button - pan and tilt. + * right button - forward and sideways. + * left + right button (or middle button) - sideways and up. +*/ + + +#ifndef __DRAWSTUFF_H__ +#define __DRAWSTUFF_H__ + +/* Define a DLL export symbol for those platforms that need it */ +#if defined(ODE_PLATFORM_WINDOWS) + #if defined(DS_DLL) + #define DS_API __declspec(dllexport) + #elif !defined(DS_LIB) + #define DS_DLL_API __declspec(dllimport) + #endif +#endif + +#if !defined(DS_API) + #define DS_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +#include + + +/* texture numbers */ + enum DS_TEXTURE_NUMBER + { + DS_NONE = 0, /* uses the current color instead of a texture */ + DS_WOOD, + DS_CHECKERED, + DS_GROUND, + DS_SKY + }; + +/* draw modes */ + +#define DS_POLYFILL 0 +#define DS_WIREFRAME 1 + +/** + * @struct dsFunctions + * @brief Set of functions to be used as callbacks by the simulation loop. + * @ingroup drawstuff + */ +typedef struct dsFunctions { + int version; /* put DS_VERSION here */ + /* version 1 data */ + void (*start)(); /* called before sim loop starts */ + void (*step) (int pause); /* called before every frame */ + void (*command) (int cmd); /* called if a command key is pressed */ + void (*stop)(); /* called after sim loop exits */ + /* version 2 data */ + const char *path_to_textures; /* if nonzero, path to texture files */ +} dsFunctions; + + +/** + * @brief Does the complete simulation. + * @ingroup drawstuff + * This function starts running the simulation, and only exits when the simulation is done. + * Function pointers should be provided for the callbacks. + * @param argv supports flags like '-notex' '-noshadow' '-pause' + * @param fn Callback functions. + */ +DS_API void dsSimulationLoop (int argc, char **argv, + int window_width, int window_height, + struct dsFunctions *fn); + +/** + * @brief exit with error message. + * @ingroup drawstuff + * This function displays an error message then exit. + * @param msg format strin, like printf, without the newline character. + */ +DS_API void dsError (const char *msg, ...); + +/** + * @brief exit with error message and core dump. + * @ingroup drawstuff + * this functions tries to dump core or start the debugger. + * @param msg format strin, like printf, without the newline character. + */ +DS_API void dsDebug (const char *msg, ...); + +/** + * @brief print log message + * @ingroup drawstuff + * @param msg format string, like printf, without the \n. + */ +DS_API void dsPrint (const char *msg, ...); + +/** + * @brief Sets the viewpoint + * @ingroup drawstuff + * @param xyz camera position. + * @param hpr contains heading, pitch and roll numbers in degrees. heading=0 + * points along the x axis, pitch=0 is looking towards the horizon, and + * roll 0 is "unrotated". + */ +DS_API void dsSetViewpoint (float xyz[3], float hpr[3]); + + +/** + * @brief Gets the viewpoint + * @ingroup drawstuff + * @param xyz position + * @param hpr heading,pitch,roll. + */ +DS_API void dsGetViewpoint (float xyz[3], float hpr[3]); + +/** + * @brief Stop the simulation loop. + * @ingroup drawstuff + * Calling this from within dsSimulationLoop() + * will cause it to exit and return to the caller. it is the same as if the + * user used the exit command. using this outside the loop will have no + * effect. + */ +DS_API void dsStop(); + +/** + * @brief Get the elapsed time (on wall-clock) + * @ingroup drawstuff + * It returns the nr of seconds since the last call to this function. + */ +DS_API double dsElapsedTime(); + +/** + * @brief Toggle the rendering of textures. + * @ingroup drawstuff + * It changes the way objects are drawn. these changes will apply to all further + * dsDrawXXX() functions. + * @param the texture number must be a DS_xxx texture constant. + * The current texture is colored according to the current color. + * At the start of each frame, the texture is reset to none and the color is + * reset to white. + */ +DS_API void dsSetTexture (int texture_number); + +/** + * @brief Set the color with which geometry is drawn. + * @ingroup drawstuff + * @param red Red component from 0 to 1 + * @param green Green component from 0 to 1 + * @param blue Blue component from 0 to 1 + */ +DS_API void dsSetColor (float red, float green, float blue); + +/** + * @brief Set the color and transparency with which geometry is drawn. + * @ingroup drawstuff + * @param alpha Note that alpha transparency is a misnomer: it is alpha opacity. + * 1.0 means fully opaque, and 0.0 means fully transparent. + */ +DS_API void dsSetColorAlpha (float red, float green, float blue, float alpha); + +/** + * @brief Draw a box. + * @ingroup drawstuff + * @param pos is the x,y,z of the center of the object. + * @param R is a 3x3 rotation matrix for the object, stored by row like this: + * [ R11 R12 R13 0 ] + * [ R21 R22 R23 0 ] + * [ R31 R32 R33 0 ] + * @param sides[] is an array of x,y,z side lengths. + */ +DS_API void dsDrawBox (const float pos[3], const float R[12], const float sides[3]); + +/** + * @brief Draw a sphere. + * @ingroup drawstuff + * @param pos Position of center. + * @param R orientation. + * @param radius + */ +DS_API void dsDrawSphere (const float pos[3], const float R[12], float radius); + +/** + * @brief Draw a triangle. + * @ingroup drawstuff + * @param pos Position of center + * @param R orientation + * @param v0 first vertex + * @param v1 second + * @param v2 third vertex + * @param solid set to 0 for wireframe + */ +DS_API void dsDrawTriangle (const float pos[3], const float R[12], + const float *v0, const float *v1, const float *v2, int solid); + +/** + * @brief Draw triangles. + * @ingroup drawstuff + * @param pos Position of center + * @param R orientation + * @param v list of vertices (x0, y0, z0, x1, y1, z1, ...) + * @param n number of vertices + * @param solid set to 0 for wireframe + */ +DS_API void dsDrawTriangles (const float pos[3], const float R[12], + const float *v, const int n, int solid); + +/** + * @brief Draw a z-aligned cylinder + * @ingroup drawstuff + */ +DS_API void dsDrawCylinder (const float pos[3], const float R[12], + float length, float radius); + +/** + * @brief Draw a z-aligned capsule + * @ingroup drawstuff + */ +DS_API void dsDrawCapsule (const float pos[3], const float R[12], + float length, float radius); + +/** + * @brief Draw a line. + * @ingroup drawstuff + */ +DS_API void dsDrawLine (const float pos1[3], const float pos2[3]); + +/** + * @brief Draw a convex shape. + * @ingroup drawstuff + */ +DS_API void dsDrawConvex(const float pos[3], const float R[12], + const float *_planes, + unsigned int _planecount, + const float *_points, + unsigned int _pointcount, + const unsigned int *_polygons); + + /* these drawing functions are identical to the ones above, except they take + * double arrays for `pos' and `R'. + */ +DS_API void dsDrawBoxD (const double pos[3], const double R[12], + const double sides[3]); +DS_API void dsDrawSphereD (const double pos[3], const double R[12], + const float radius); +DS_API void dsDrawTriangleD (const double pos[3], const double R[12], + const double *v0, const double *v1, const double *v2, int solid); +DS_API void dsDrawTrianglesD (const double pos[3], const double R[12], + const double *v, const int n, int solid); +DS_API void dsDrawCylinderD (const double pos[3], const double R[12], + float length, float radius); +DS_API void dsDrawCapsuleD (const double pos[3], const double R[12], + float length, float radius); +DS_API void dsDrawLineD (const double pos1[3], const double pos2[3]); +DS_API void dsDrawConvexD(const double pos[3], const double R[12], + const double *_planes, + unsigned int _planecount, + const double *_points, + unsigned int _pointcount, + const unsigned int *_polygons); + +/** + * @brief Set the quality with which curved objects are rendered. + * @ingroup drawstuff + * Higher numbers are higher quality, but slower to draw. + * This must be set before the first objects are drawn to be effective. + * Default sphere quality is 1, default capsule quality is 3. + */ +DS_API void dsSetSphereQuality (int n); /* default = 1 */ +DS_API void dsSetCapsuleQuality (int n); /* default = 3 */ + +/** + * @brief Set Drawmode 0=Polygon Fill,1=Wireframe). + * Use the DS_POLYFILL and DS_WIREFRAME macros. + * @ingroup drawstuff + */ +DS_API void dsSetDrawMode(int mode); + +/* Backwards compatible API */ +#define dsDrawCappedCylinder dsDrawCapsule +#define dsDrawCappedCylinderD dsDrawCapsuleD +#define dsSetCappedCylinderQuality dsSetCapsuleQuality + +/* closing bracket for extern "C" */ +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/thirdparty/ode-0.16.5/include/drawstuff/version.h b/thirdparty/ode-0.16.5/include/drawstuff/version.h new file mode 100644 index 0000000..71d95f4 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/drawstuff/version.h @@ -0,0 +1,29 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef __VERSION_H +#define __VERSION_H + +/* high byte is major version, low byte is minor version */ +#define DS_VERSION 0x0002 + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/Makefile.am b/thirdparty/ode-0.16.5/include/ode/Makefile.am new file mode 100644 index 0000000..ad06509 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/Makefile.am @@ -0,0 +1,34 @@ +libode_la_includedir = $(includedir)/ode +libode_la_include_HEADERS = \ + collision.h \ + collision_space.h \ + collision_trimesh.h \ + common.h \ + compatibility.h \ + contact.h \ + cooperative.h \ + error.h \ + export-dif.h \ + mass.h \ + matrix.h matrix_coop.h \ + memory.h \ + misc.h \ + objects.h \ + ode.h \ + odeconfig.h \ + odecpp.h \ + odecpp_collision.h \ + odeinit.h \ + odemath.h \ + odemath_legacy.h \ + rotation.h \ + threading.h \ + threading_impl.h \ + timer.h + + +EXTRA_DIST = README precision.h.in version.h.in + +dist_libode_la_include_HEADERS = precision.h version.h + + diff --git a/thirdparty/ode-0.16.5/include/ode/Makefile.in b/thirdparty/ode-0.16.5/include/ode/Makefile.in new file mode 100644 index 0000000..1dac65e --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/Makefile.in @@ -0,0 +1,642 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = include/ode +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_libode_la_include_HEADERS) \ + $(libode_la_include_HEADERS) $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = version.h precision.h +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libode_la_includedir)" \ + "$(DESTDIR)$(libode_la_includedir)" +HEADERS = $(dist_libode_la_include_HEADERS) \ + $(libode_la_include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/precision.h.in \ + $(srcdir)/version.h.in README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +libode_la_includedir = $(includedir)/ode +libode_la_include_HEADERS = \ + collision.h \ + collision_space.h \ + collision_trimesh.h \ + common.h \ + compatibility.h \ + contact.h \ + cooperative.h \ + error.h \ + export-dif.h \ + mass.h \ + matrix.h matrix_coop.h \ + memory.h \ + misc.h \ + objects.h \ + ode.h \ + odeconfig.h \ + odecpp.h \ + odecpp_collision.h \ + odeinit.h \ + odemath.h \ + odemath_legacy.h \ + rotation.h \ + threading.h \ + threading_impl.h \ + timer.h + +EXTRA_DIST = README precision.h.in version.h.in +dist_libode_la_include_HEADERS = precision.h version.h +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/ode/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign include/ode/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +version.h: $(top_builddir)/config.status $(srcdir)/version.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +precision.h: $(top_builddir)/config.status $(srcdir)/precision.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-dist_libode_la_includeHEADERS: $(dist_libode_la_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(dist_libode_la_include_HEADERS)'; test -n "$(libode_la_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libode_la_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libode_la_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libode_la_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libode_la_includedir)" || exit $$?; \ + done + +uninstall-dist_libode_la_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(dist_libode_la_include_HEADERS)'; test -n "$(libode_la_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libode_la_includedir)'; $(am__uninstall_files_from_dir) +install-libode_la_includeHEADERS: $(libode_la_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libode_la_include_HEADERS)'; test -n "$(libode_la_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libode_la_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libode_la_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libode_la_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libode_la_includedir)" || exit $$?; \ + done + +uninstall-libode_la_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libode_la_include_HEADERS)'; test -n "$(libode_la_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libode_la_includedir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(libode_la_includedir)" "$(DESTDIR)$(libode_la_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_libode_la_includeHEADERS \ + install-libode_la_includeHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-dist_libode_la_includeHEADERS \ + uninstall-libode_la_includeHEADERS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am \ + install-dist_libode_la_includeHEADERS install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libode_la_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-dist_libode_la_includeHEADERS \ + uninstall-libode_la_includeHEADERS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/include/ode/README b/thirdparty/ode-0.16.5/include/ode/README new file mode 100644 index 0000000..9d7e99a --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/README @@ -0,0 +1,18 @@ + +this is the public C interface to the ODE library. + +all these files should be includable from C, i.e. they should not use any +C++ features. everything should be protected with + + #ifdef __cplusplus + extern "C" { + #endif + + ... + + #ifdef __cplusplus + } + #endif + +the only exceptions are the odecpp.h and odecpp_collisioh.h files, which define a C++ wrapper for +the C interface. remember to keep this in sync! diff --git a/thirdparty/ode-0.16.5/include/ode/collision.h b/thirdparty/ode-0.16.5/include/ode/collision.h new file mode 100644 index 0000000..f78bf18 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/collision.h @@ -0,0 +1,1526 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COLLISION_H_ +#define _ODE_COLLISION_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup collide Collision Detection + * + * ODE has two main components: a dynamics simulation engine and a collision + * detection engine. The collision engine is given information about the + * shape of each body. At each time step it figures out which bodies touch + * each other and passes the resulting contact point information to the user. + * The user in turn creates contact joints between bodies. + * + * Using ODE's collision detection is optional - an alternative collision + * detection system can be used as long as it can supply the right kinds of + * contact information. + */ + + +/* ************************************************************************ */ +/* general functions */ + +/** + * @brief Destroy a geom, removing it from any space. + * + * Destroy a geom, removing it from any space it is in first. This one + * function destroys a geom of any type, but to create a geom you must call + * a creation function for that type. + * + * When a space is destroyed, if its cleanup mode is 1 (the default) then all + * the geoms in that space are automatically destroyed as well. + * + * @param geom the geom to be destroyed. + * @ingroup collide + */ +ODE_API void dGeomDestroy (dGeomID geom); + + +/** + * @brief Set the user-defined data pointer stored in the geom. + * + * @param geom the geom to hold the data + * @param data the data pointer to be stored + * @ingroup collide + */ +ODE_API void dGeomSetData (dGeomID geom, void* data); + + +/** + * @brief Get the user-defined data pointer stored in the geom. + * + * @param geom the geom containing the data + * @ingroup collide + */ +ODE_API void *dGeomGetData (dGeomID geom); + + +/** + * @brief Set the body associated with a placeable geom. + * + * Setting a body on a geom automatically combines the position vector and + * rotation matrix of the body and geom, so that setting the position or + * orientation of one will set the value for both objects. Setting a body + * ID of zero gives the geom its own position and rotation, independent + * from any body. If the geom was previously connected to a body then its + * new independent position/rotation is set to the current position/rotation + * of the body. + * + * Calling these functions on a non-placeable geom results in a runtime + * error in the debug build of ODE. + * + * @param geom the geom to connect + * @param body the body to attach to the geom + * @ingroup collide + */ +ODE_API void dGeomSetBody (dGeomID geom, dBodyID body); + + +/** + * @brief Get the body associated with a placeable geom. + * @param geom the geom to query. + * @sa dGeomSetBody + * @ingroup collide + */ +ODE_API dBodyID dGeomGetBody (dGeomID geom); + + +/** + * @brief Set the position vector of a placeable geom. + * + * If the geom is attached to a body, the body's position will also be changed. + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to set. + * @param x the new X coordinate. + * @param y the new Y coordinate. + * @param z the new Z coordinate. + * @sa dBodySetPosition + * @ingroup collide + */ +ODE_API void dGeomSetPosition (dGeomID geom, dReal x, dReal y, dReal z); + + +/** + * @brief Set the rotation matrix of a placeable geom. + * + * If the geom is attached to a body, the body's rotation will also be changed. + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to set. + * @param R the new rotation matrix. + * @sa dBodySetRotation + * @ingroup collide + */ +ODE_API void dGeomSetRotation (dGeomID geom, const dMatrix3 R); + + +/** + * @brief Set the rotation of a placeable geom. + * + * If the geom is attached to a body, the body's rotation will also be changed. + * + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to set. + * @param Q the new rotation. + * @sa dBodySetQuaternion + * @ingroup collide + */ +ODE_API void dGeomSetQuaternion (dGeomID geom, const dQuaternion Q); + + +/** + * @brief Get the position vector of a placeable geom. + * + * If the geom is attached to a body, the body's position will be returned. + * + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to query. + * @returns A pointer to the geom's position vector. + * @remarks The returned value is a pointer to the geom's internal + * data structure. It is valid until any changes are made + * to the geom. + * @sa dBodyGetPosition + * @ingroup collide + */ +ODE_API const dReal * dGeomGetPosition (dGeomID geom); + + +/** + * @brief Copy the position of a geom into a vector. + * @ingroup collide + * @param geom the geom to query + * @param pos a copy of the geom position + * @sa dGeomGetPosition + */ +ODE_API void dGeomCopyPosition (dGeomID geom, dVector3 pos); + + +/** + * @brief Get the rotation matrix of a placeable geom. + * + * If the geom is attached to a body, the body's rotation will be returned. + * + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to query. + * @returns A pointer to the geom's rotation matrix. + * @remarks The returned value is a pointer to the geom's internal + * data structure. It is valid until any changes are made + * to the geom. + * @sa dBodyGetRotation + * @ingroup collide + */ +ODE_API const dReal * dGeomGetRotation (dGeomID geom); + + +/** + * @brief Get the rotation matrix of a placeable geom. + * + * If the geom is attached to a body, the body's rotation will be returned. + * + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to query. + * @param R a copy of the geom rotation + * @sa dGeomGetRotation + * @ingroup collide + */ +ODE_API void dGeomCopyRotation(dGeomID geom, dMatrix3 R); + + +/** + * @brief Get the rotation quaternion of a placeable geom. + * + * If the geom is attached to a body, the body's quaternion will be returned. + * + * Calling this function on a non-placeable geom results in a runtime error in + * the debug build of ODE. + * + * @param geom the geom to query. + * @param result a copy of the rotation quaternion. + * @sa dBodyGetQuaternion + * @ingroup collide + */ +ODE_API void dGeomGetQuaternion (dGeomID geom, dQuaternion result); + + +/** + * @brief Return the axis-aligned bounding box. + * + * Return in aabb an axis aligned bounding box that surrounds the given geom. + * The aabb array has elements (minx, maxx, miny, maxy, minz, maxz). If the + * geom is a space, a bounding box that surrounds all contained geoms is + * returned. + * + * This function may return a pre-computed cached bounding box, if it can + * determine that the geom has not moved since the last time the bounding + * box was computed. + * + * @param geom the geom to query + * @param aabb the returned bounding box + * @ingroup collide + */ +ODE_API void dGeomGetAABB (dGeomID geom, dReal aabb[6]); + + +/** + * @brief Determing if a geom is a space. + * @param geom the geom to query + * @returns Non-zero if the geom is a space, zero otherwise. + * @ingroup collide + */ +ODE_API int dGeomIsSpace (dGeomID geom); + + +/** + * @brief Query for the space containing a particular geom. + * @param geom the geom to query + * @returns The space that contains the geom, or NULL if the geom is + * not contained by a space. + * @ingroup collide + */ +ODE_API dSpaceID dGeomGetSpace (dGeomID); + + +/** + * @brief Given a geom, this returns its class. + * + * The ODE classes are: + * @li dSphereClass + * @li dBoxClass + * @li dCylinderClass + * @li dPlaneClass + * @li dRayClass + * @li dConvexClass + * @li dGeomTransformClass + * @li dTriMeshClass + * @li dSimpleSpaceClass + * @li dHashSpaceClass + * @li dQuadTreeSpaceClass + * @li dFirstUserClass + * @li dLastUserClass + * + * User-defined class will return their own number. + * + * @param geom the geom to query + * @returns The geom class ID. + * @ingroup collide + */ +ODE_API int dGeomGetClass (dGeomID geom); + + +/** + * @brief Set the "category" bitfield for the given geom. + * + * The category bitfield is used by spaces to govern which geoms will + * interact with each other. The bitfield is guaranteed to be at least + * 32 bits wide. The default category values for newly created geoms + * have all bits set. + * + * @param geom the geom to set + * @param bits the new bitfield value + * @ingroup collide + */ +ODE_API void dGeomSetCategoryBits (dGeomID geom, unsigned long bits); + + +/** + * @brief Set the "collide" bitfield for the given geom. + * + * The collide bitfield is used by spaces to govern which geoms will + * interact with each other. The bitfield is guaranteed to be at least + * 32 bits wide. The default category values for newly created geoms + * have all bits set. + * + * @param geom the geom to set + * @param bits the new bitfield value + * @ingroup collide + */ +ODE_API void dGeomSetCollideBits (dGeomID geom, unsigned long bits); + + +/** + * @brief Get the "category" bitfield for the given geom. + * + * @param geom the geom to set + * @param bits the new bitfield value + * @sa dGeomSetCategoryBits + * @ingroup collide + */ +ODE_API unsigned long dGeomGetCategoryBits (dGeomID); + + +/** + * @brief Get the "collide" bitfield for the given geom. + * + * @param geom the geom to set + * @param bits the new bitfield value + * @sa dGeomSetCollideBits + * @ingroup collide + */ +ODE_API unsigned long dGeomGetCollideBits (dGeomID); + + +/** + * @brief Enable a geom. + * + * Disabled geoms are completely ignored by dSpaceCollide and dSpaceCollide2, + * although they can still be members of a space. New geoms are created in + * the enabled state. + * + * @param geom the geom to enable + * @sa dGeomDisable + * @sa dGeomIsEnabled + * @ingroup collide + */ +ODE_API void dGeomEnable (dGeomID geom); + + +/** + * @brief Disable a geom. + * + * Disabled geoms are completely ignored by dSpaceCollide and dSpaceCollide2, + * although they can still be members of a space. New geoms are created in + * the enabled state. + * + * @param geom the geom to disable + * @sa dGeomEnable + * @sa dGeomIsEnabled + * @ingroup collide + */ +ODE_API void dGeomDisable (dGeomID geom); + + +/** + * @brief Check to see if a geom is enabled. + * + * Disabled geoms are completely ignored by dSpaceCollide and dSpaceCollide2, + * although they can still be members of a space. New geoms are created in + * the enabled state. + * + * @param geom the geom to query + * @returns Non-zero if the geom is enabled, zero otherwise. + * @sa dGeomDisable + * @sa dGeomEnable + * @ingroup collide + */ +ODE_API int dGeomIsEnabled (dGeomID geom); + + +enum +{ + dGeomCommonControlClass = 0, + dGeomColliderControlClass = 1 +}; + +enum +{ + dGeomCommonAnyControlCode = 0, + + dGeomColliderSetMergeSphereContactsControlCode = 1, + dGeomColliderGetMergeSphereContactsControlCode = 2 +}; + +enum +{ + dGeomColliderMergeContactsValue__Default = 0, /* Used with Set... to restore default value*/ + dGeomColliderMergeContactsValue_None = 1, + dGeomColliderMergeContactsValue_Normals = 2, + dGeomColliderMergeContactsValue_Full = 3 +}; + +/** + * @brief Execute low level control operation for geometry. + * + * The variable the dataSize points to must be initialized before the call. + * If the size does not match the one expected for the control class/code function + * changes it to the size expected and returns failure. This implies the function + * can be called with NULL data and zero size to test if control class/code is supported + * and obtain required data size for it. + * + * dGeomCommonAnyControlCode applies to any control class and returns success if + * at least one control code is available for the given class with given geom. + * + * Currently there are the following control classes supported: + * @li dGeomColliderControlClass + * + * For dGeomColliderControlClass there are the following codes available: + * @li dGeomColliderSetMergeSphereContactsControlCode (arg of type int, dGeomColliderMergeContactsValue_*) + * @li dGeomColliderGetMergeSphereContactsControlCode (arg of type int, dGeomColliderMergeContactsValue_*) + * + * @param geom the geom to control + * @param controlClass the control class + * @param controlCode the control code for the class + * @param dataValue the control argument pointer + * @param dataSize the control argument size provided or expected + * @returns Boolean execution status + * @ingroup collide + */ +ODE_API int dGeomLowLevelControl (dGeomID geom, int controlClass, int controlCode, void *dataValue, int *dataSize); + + +/** + * @brief Get world position of a relative point on geom. + * + * Calling this function on a non-placeable geom results in the same point being + * returned. + * + * @ingroup collide + * @param result will contain the result. + */ +ODE_API void dGeomGetRelPointPos +( + dGeomID geom, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief takes a point in global coordinates and returns + * the point's position in geom-relative coordinates. + * + * Calling this function on a non-placeable geom results in the same point being + * returned. + * + * @remarks + * This is the inverse of dGeomGetRelPointPos() + * @ingroup collide + * @param result will contain the result. + */ +ODE_API void dGeomGetPosRelPoint +( + dGeomID geom, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Convert from geom-local to world coordinates. + * + * Calling this function on a non-placeable geom results in the same vector being + * returned. + * + * @ingroup collide + * @param result will contain the result. + */ +ODE_API void dGeomVectorToWorld +( + dGeomID geom, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Convert from world to geom-local coordinates. + * + * Calling this function on a non-placeable geom results in the same vector being + * returned. + * + * @ingroup collide + * @param result will contain the result. + */ +ODE_API void dGeomVectorFromWorld +( + dGeomID geom, dReal px, dReal py, dReal pz, + dVector3 result +); + + +/* ************************************************************************ */ +/* geom offset from body */ + +/** + * @brief Set the local offset position of a geom from its body. + * + * Sets the geom's positional offset in local coordinates. + * After this call, the geom will be at a new position determined from the + * body's position and the offset. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param x the new X coordinate. + * @param y the new Y coordinate. + * @param z the new Z coordinate. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetPosition (dGeomID geom, dReal x, dReal y, dReal z); + + +/** + * @brief Set the local offset rotation matrix of a geom from its body. + * + * Sets the geom's rotational offset in local coordinates. + * After this call, the geom will be at a new position determined from the + * body's position and the offset. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param R the new rotation matrix. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetRotation (dGeomID geom, const dMatrix3 R); + + +/** + * @brief Set the local offset rotation of a geom from its body. + * + * Sets the geom's rotational offset in local coordinates. + * After this call, the geom will be at a new position determined from the + * body's position and the offset. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param Q the new rotation. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetQuaternion (dGeomID geom, const dQuaternion Q); + + +/** + * @brief Set the offset position of a geom from its body. + * + * Sets the geom's positional offset to move it to the new world + * coordinates. + * After this call, the geom will be at the world position passed in, + * and the offset will be the difference from the current body position. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param x the new X coordinate. + * @param y the new Y coordinate. + * @param z the new Z coordinate. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetWorldPosition (dGeomID geom, dReal x, dReal y, dReal z); + + +/** + * @brief Set the offset rotation of a geom from its body. + * + * Sets the geom's rotational offset to orient it to the new world + * rotation matrix. + * After this call, the geom will be at the world orientation passed in, + * and the offset will be the difference from the current body orientation. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param R the new rotation matrix. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetWorldRotation (dGeomID geom, const dMatrix3 R); + + +/** + * @brief Set the offset rotation of a geom from its body. + * + * Sets the geom's rotational offset to orient it to the new world + * rotation matrix. + * After this call, the geom will be at the world orientation passed in, + * and the offset will be the difference from the current body orientation. + * The geom must be attached to a body. + * If the geom did not have an offset, it is automatically created. + * + * @param geom the geom to set. + * @param Q the new rotation. + * @ingroup collide + */ +ODE_API void dGeomSetOffsetWorldQuaternion (dGeomID geom, const dQuaternion); + + +/** + * @brief Clear any offset from the geom. + * + * If the geom has an offset, it is eliminated and the geom is + * repositioned at the body's position. If the geom has no offset, + * this function does nothing. + * This is more efficient than calling dGeomSetOffsetPosition(zero) + * and dGeomSetOffsetRotation(identiy), because this function actually + * eliminates the offset, rather than leaving it as the identity transform. + * + * @param geom the geom to have its offset destroyed. + * @ingroup collide + */ +ODE_API void dGeomClearOffset(dGeomID geom); + + +/** + * @brief Check to see whether the geom has an offset. + * + * This function will return non-zero if the offset has been created. + * Note that there is a difference between a geom with no offset, + * and a geom with an offset that is the identity transform. + * In the latter case, although the observed behaviour is identical, + * there is a unnecessary computation involved because the geom will + * be applying the transform whenever it needs to recalculate its world + * position. + * + * @param geom the geom to query. + * @returns Non-zero if the geom has an offset, zero otherwise. + * @ingroup collide + */ +ODE_API int dGeomIsOffset(dGeomID geom); + + +/** + * @brief Get the offset position vector of a geom. + * + * Returns the positional offset of the geom in local coordinates. + * If the geom has no offset, this function returns the zero vector. + * + * @param geom the geom to query. + * @returns A pointer to the geom's offset vector. + * @remarks The returned value is a pointer to the geom's internal + * data structure. It is valid until any changes are made + * to the geom. + * @ingroup collide + */ +ODE_API const dReal * dGeomGetOffsetPosition (dGeomID geom); + + +/** + * @brief Copy the offset position vector of a geom. + * + * Returns the positional offset of the geom in local coordinates. + * If the geom has no offset, this function returns the zero vector. + * + * @param geom the geom to query. + * @param pos returns the offset position + * @ingroup collide + */ +ODE_API void dGeomCopyOffsetPosition (dGeomID geom, dVector3 pos); + + +/** + * @brief Get the offset rotation matrix of a geom. + * + * Returns the rotational offset of the geom in local coordinates. + * If the geom has no offset, this function returns the identity + * matrix. + * + * @param geom the geom to query. + * @returns A pointer to the geom's offset rotation matrix. + * @remarks The returned value is a pointer to the geom's internal + * data structure. It is valid until any changes are made + * to the geom. + * @ingroup collide + */ +ODE_API const dReal * dGeomGetOffsetRotation (dGeomID geom); + + +/** + * @brief Copy the offset rotation matrix of a geom. + * + * Returns the rotational offset of the geom in local coordinates. + * If the geom has no offset, this function returns the identity + * matrix. + * + * @param geom the geom to query. + * @param R returns the rotation matrix. + * @ingroup collide + */ +ODE_API void dGeomCopyOffsetRotation (dGeomID geom, dMatrix3 R); + + +/** + * @brief Get the offset rotation quaternion of a geom. + * + * Returns the rotation offset of the geom as a quaternion. + * If the geom has no offset, the identity quaternion is returned. + * + * @param geom the geom to query. + * @param result a copy of the rotation quaternion. + * @ingroup collide + */ +ODE_API void dGeomGetOffsetQuaternion (dGeomID geom, dQuaternion result); + + +/* ************************************************************************ */ +/* collision detection */ + +/* + * Just generate any contacts (disables any contact refining). + */ +#define CONTACTS_UNIMPORTANT 0x80000000 + +/** + * + * @brief Given two geoms o1 and o2 that potentially intersect, + * generate contact information for them. + * + * Internally, this just calls the correct class-specific collision + * functions for o1 and o2. + * + * @param o1 The first geom to test. + * @param o2 The second geom to test. + * + * @param flags The flags specify how contacts should be generated if + * the geoms touch. The lower 16 bits of flags is an integer that + * specifies the maximum number of contact points to generate. You must + * ask for at least one contact. + * Additionally, following bits may be set: + * CONTACTS_UNIMPORTANT -- just generate any contacts (skip contact refining). + * All other bits in flags must be set to zero. In the future the other bits + * may be used to select from different contact generation strategies. + * + * @param contact Points to an array of dContactGeom structures. The array + * must be able to hold at least the maximum number of contacts. These + * dContactGeom structures may be embedded within larger structures in the + * array -- the skip parameter is the byte offset from one dContactGeom to + * the next in the array. If skip is sizeof(dContactGeom) then contact + * points to a normal (C-style) array. It is an error for skip to be smaller + * than sizeof(dContactGeom). + * + * @returns If the geoms intersect, this function returns the number of contact + * points generated (and updates the contact array), otherwise it returns 0 + * (and the contact array is not touched). + * + * @remarks If a space is passed as o1 or o2 then this function will collide + * all objects contained in o1 with all objects contained in o2, and return + * the resulting contact points. This method for colliding spaces with geoms + * (or spaces with spaces) provides no user control over the individual + * collisions. To get that control, use dSpaceCollide or dSpaceCollide2 instead. + * + * @remarks If o1 and o2 are the same geom then this function will do nothing + * and return 0. Technically speaking an object intersects with itself, but it + * is not useful to find contact points in this case. + * + * @remarks This function does not care if o1 and o2 are in the same space or not + * (or indeed if they are in any space at all). + * + * @ingroup collide + */ +ODE_API int dCollide (dGeomID o1, dGeomID o2, int flags, dContactGeom *contact, + int skip); + +/** + * @brief Determines which pairs of geoms in a space may potentially intersect, + * and calls the callback function for each candidate pair. + * + * @param space The space to test. + * + * @param data Passed from dSpaceCollide directly to the callback + * function. Its meaning is user defined. The o1 and o2 arguments are the + * geoms that may be near each other. + * + * @param callback A callback function is of type @ref dNearCallback. + * + * @remarks Other spaces that are contained within the colliding space are + * not treated specially, i.e. they are not recursed into. The callback + * function may be passed these contained spaces as one or both geom + * arguments. + * + * @remarks dSpaceCollide() is guaranteed to pass all intersecting geom + * pairs to the callback function, but may also pass close but + * non-intersecting pairs. The number of these calls depends on the + * internal algorithms used by the space. Thus you should not expect + * that dCollide will return contacts for every pair passed to the + * callback. + * + * @sa dSpaceCollide2 + * @ingroup collide + */ +ODE_API void dSpaceCollide (dSpaceID space, void *data, dNearCallback *callback); + + +/** + * @brief Determines which geoms from one space may potentially intersect with + * geoms from another space, and calls the callback function for each candidate + * pair. + * + * @param space1 The first space to test. + * + * @param space2 The second space to test. + * + * @param data Passed from dSpaceCollide directly to the callback + * function. Its meaning is user defined. The o1 and o2 arguments are the + * geoms that may be near each other. + * + * @param callback A callback function is of type @ref dNearCallback. + * + * @remarks This function can also test a single non-space geom against a + * space. This function is useful when there is a collision hierarchy, i.e. + * when there are spaces that contain other spaces. + * + * @remarks Other spaces that are contained within the colliding space are + * not treated specially, i.e. they are not recursed into. The callback + * function may be passed these contained spaces as one or both geom + * arguments. + * + * @remarks Sublevel value of space affects how the spaces are iterated. + * Both spaces are recursed only if their sublevels match. Otherwise, only + * the space with greater sublevel is recursed and the one with lesser sublevel + * is used as a geom itself. + * + * @remarks dSpaceCollide2() is guaranteed to pass all intersecting geom + * pairs to the callback function, but may also pass close but + * non-intersecting pairs. The number of these calls depends on the + * internal algorithms used by the space. Thus you should not expect + * that dCollide will return contacts for every pair passed to the + * callback. + * + * @sa dSpaceCollide + * @sa dSpaceSetSublevel + * @ingroup collide + */ +ODE_API void dSpaceCollide2 (dGeomID space1, dGeomID space2, void *data, dNearCallback *callback); + + +/* ************************************************************************ */ +/* standard classes */ + +/* the maximum number of user classes that are supported */ +enum { + dMaxUserClasses = 4 +}; + +/* class numbers - each geometry object needs a unique number */ +enum { + dSphereClass = 0, + dBoxClass, + dCapsuleClass, + dCylinderClass, + dPlaneClass, + dRayClass, + dConvexClass, + dGeomTransformClass, + dTriMeshClass, + dHeightfieldClass, + + dFirstSpaceClass, + dSimpleSpaceClass = dFirstSpaceClass, + dHashSpaceClass, + dSweepAndPruneSpaceClass, /* SAP */ + dQuadTreeSpaceClass, + dLastSpaceClass = dQuadTreeSpaceClass, + + dFirstUserClass, + dLastUserClass = dFirstUserClass + dMaxUserClasses - 1, + dGeomNumClasses +}; + + +/** + * @defgroup collide_sphere Sphere Class + * @ingroup collide + */ + +/** + * @brief Create a sphere geom of the given radius, and return its ID. + * + * @param space a space to contain the new geom. May be null. + * @param radius the radius of the sphere. + * + * @returns A new sphere geom. + * + * @remarks The point of reference for a sphere is its center. + * + * @sa dGeomDestroy + * @sa dGeomSphereSetRadius + * @ingroup collide_sphere + */ +ODE_API dGeomID dCreateSphere (dSpaceID space, dReal radius); + + +/** + * @brief Set the radius of a sphere geom. + * + * @param sphere the sphere to set. + * @param radius the new radius. + * + * @sa dGeomSphereGetRadius + * @ingroup collide_sphere + */ +ODE_API void dGeomSphereSetRadius (dGeomID sphere, dReal radius); + + +/** + * @brief Retrieves the radius of a sphere geom. + * + * @param sphere the sphere to query. + * + * @sa dGeomSphereSetRadius + * @ingroup collide_sphere + */ +ODE_API dReal dGeomSphereGetRadius (dGeomID sphere); + + +/** + * @brief Calculate the depth of the given point within a sphere. + * + * @param sphere the sphere to query. + * @param x the X coordinate of the point. + * @param y the Y coordinate of the point. + * @param z the Z coordinate of the point. + * + * @returns The depth of the point. Points inside the sphere will have a + * positive depth, points outside it will have a negative depth, and points + * on the surface will have a depth of zero. + * + * @ingroup collide_sphere + */ +ODE_API dReal dGeomSpherePointDepth (dGeomID sphere, dReal x, dReal y, dReal z); + + +/*--> Convex Functions*/ +ODE_API dGeomID dCreateConvex (dSpaceID space, + const dReal *_planes, + unsigned int _planecount, + const dReal *_points, + unsigned int _pointcount, + const unsigned int *_polygons); + +ODE_API void dGeomSetConvex (dGeomID g, + const dReal *_planes, + unsigned int _count, + const dReal *_points, + unsigned int _pointcount, + const unsigned int *_polygons); +/*<-- Convex Functions*/ + +/** + * @defgroup collide_box Box Class + * @ingroup collide + */ + +/** + * @brief Create a box geom with the provided side lengths. + * + * @param space a space to contain the new geom. May be null. + * @param lx the length of the box along the X axis + * @param ly the length of the box along the Y axis + * @param lz the length of the box along the Z axis + * + * @returns A new box geom. + * + * @remarks The point of reference for a box is its center. + * + * @sa dGeomDestroy + * @sa dGeomBoxSetLengths + * @ingroup collide_box + */ +ODE_API dGeomID dCreateBox (dSpaceID space, dReal lx, dReal ly, dReal lz); + + +/** + * @brief Set the side lengths of the given box. + * + * @param box the box to set + * @param lx the length of the box along the X axis + * @param ly the length of the box along the Y axis + * @param lz the length of the box along the Z axis + * + * @sa dGeomBoxGetLengths + * @ingroup collide_box + */ +ODE_API void dGeomBoxSetLengths (dGeomID box, dReal lx, dReal ly, dReal lz); + + +/** + * @brief Get the side lengths of a box. + * + * @param box the box to query + * @param result the returned side lengths + * + * @sa dGeomBoxSetLengths + * @ingroup collide_box + */ +ODE_API void dGeomBoxGetLengths (dGeomID box, dVector3 result); + + +/** + * @brief Return the depth of a point in a box. + * + * @param box the box to query + * @param x the X coordinate of the point to test. + * @param y the Y coordinate of the point to test. + * @param z the Z coordinate of the point to test. + * + * @returns The depth of the point. Points inside the box will have a + * positive depth, points outside it will have a negative depth, and points + * on the surface will have a depth of zero. + */ +ODE_API dReal dGeomBoxPointDepth (dGeomID box, dReal x, dReal y, dReal z); + + +ODE_API dGeomID dCreatePlane (dSpaceID space, dReal a, dReal b, dReal c, dReal d); +ODE_API void dGeomPlaneSetParams (dGeomID plane, dReal a, dReal b, dReal c, dReal d); +ODE_API void dGeomPlaneGetParams (dGeomID plane, dVector4 result); +ODE_API dReal dGeomPlanePointDepth (dGeomID plane, dReal x, dReal y, dReal z); + +ODE_API dGeomID dCreateCapsule (dSpaceID space, dReal radius, dReal length); +ODE_API void dGeomCapsuleSetParams (dGeomID ccylinder, dReal radius, dReal length); +ODE_API void dGeomCapsuleGetParams (dGeomID ccylinder, dReal *radius, dReal *length); +ODE_API dReal dGeomCapsulePointDepth (dGeomID ccylinder, dReal x, dReal y, dReal z); + +/* For now we want to have a backwards compatible C-API, note: C++ API is not.*/ +#define dCreateCCylinder dCreateCapsule +#define dGeomCCylinderSetParams dGeomCapsuleSetParams +#define dGeomCCylinderGetParams dGeomCapsuleGetParams +#define dGeomCCylinderPointDepth dGeomCapsulePointDepth +#define dCCylinderClass dCapsuleClass + +ODE_API dGeomID dCreateCylinder (dSpaceID space, dReal radius, dReal length); +ODE_API void dGeomCylinderSetParams (dGeomID cylinder, dReal radius, dReal length); +ODE_API void dGeomCylinderGetParams (dGeomID cylinder, dReal *radius, dReal *length); + +ODE_API dGeomID dCreateRay (dSpaceID space, dReal length); +ODE_API void dGeomRaySetLength (dGeomID ray, dReal length); +ODE_API dReal dGeomRayGetLength (dGeomID ray); +ODE_API void dGeomRaySet (dGeomID ray, dReal px, dReal py, dReal pz, + dReal dx, dReal dy, dReal dz); +ODE_API void dGeomRayGet (dGeomID ray, dVector3 start, dVector3 dir); + +/* + * Set/get ray flags that influence ray collision detection. + * These flags are currently only noticed by the trimesh collider, because + * they can make a major differences there. + */ +ODE_API_DEPRECATED ODE_API void dGeomRaySetParams (dGeomID g, int FirstContact, int BackfaceCull); +ODE_API_DEPRECATED ODE_API void dGeomRayGetParams (dGeomID g, int *FirstContact, int *BackfaceCull); +ODE_API void dGeomRaySetFirstContact (dGeomID g, int firstContact); +ODE_API int dGeomRayGetFirstContact (dGeomID g); +ODE_API void dGeomRaySetBackfaceCull (dGeomID g, int backfaceCull); +ODE_API int dGeomRayGetBackfaceCull (dGeomID g); +ODE_API void dGeomRaySetClosestHit (dGeomID g, int closestHit); +ODE_API int dGeomRayGetClosestHit (dGeomID g); + +#include "collision_trimesh.h" + +ODE_API_DEPRECATED ODE_API dGeomID dCreateGeomTransform (dSpaceID space); +ODE_API_DEPRECATED ODE_API void dGeomTransformSetGeom (dGeomID g, dGeomID obj); +ODE_API_DEPRECATED ODE_API dGeomID dGeomTransformGetGeom (dGeomID g); +ODE_API_DEPRECATED ODE_API void dGeomTransformSetCleanup (dGeomID g, int mode); +ODE_API_DEPRECATED ODE_API int dGeomTransformGetCleanup (dGeomID g); +ODE_API_DEPRECATED ODE_API void dGeomTransformSetInfo (dGeomID g, int mode); +ODE_API_DEPRECATED ODE_API int dGeomTransformGetInfo (dGeomID g); + + +/* ************************************************************************ */ +/* heightfield functions */ + + +/* Data storage for heightfield data.*/ +struct dxHeightfieldData; +typedef struct dxHeightfieldData* dHeightfieldDataID; + + +/** + * @brief Callback prototype + * + * Used by the callback heightfield data type to sample a height for a + * given cell position. + * + * @param p_user_data User data specified when creating the dHeightfieldDataID + * @param x The index of a sample in the local x axis. It is a value + * in the range zero to ( nWidthSamples - 1 ). + * @param x The index of a sample in the local z axis. It is a value + * in the range zero to ( nDepthSamples - 1 ). + * + * @return The sample height which is then scaled and offset using the + * values specified when the heightfield data was created. + * + * @ingroup collide + */ +typedef dReal dHeightfieldGetHeight( void* p_user_data, int x, int z ); + + + +/** + * @brief Creates a heightfield geom. + * + * Uses the information in the given dHeightfieldDataID to construct + * a geom representing a heightfield in a collision space. + * + * @param space The space to add the geom to. + * @param data The dHeightfieldDataID created by dGeomHeightfieldDataCreate and + * setup by dGeomHeightfieldDataBuildCallback, dGeomHeightfieldDataBuildByte, + * dGeomHeightfieldDataBuildShort or dGeomHeightfieldDataBuildFloat. + * @param bPlaceable If non-zero this geom can be transformed in the world using the + * usual functions such as dGeomSetPosition and dGeomSetRotation. If the geom is + * not set as placeable, then it uses a fixed orientation where the global y axis + * represents the dynamic 'height' of the heightfield. + * + * @return A geom id to reference this geom in other calls. + * + * @ingroup collide + */ +ODE_API dGeomID dCreateHeightfield( dSpaceID space, + dHeightfieldDataID data, int bPlaceable ); + + +/** + * @brief Creates a new empty dHeightfieldDataID. + * + * Allocates a new dHeightfieldDataID and returns it. You must call + * dGeomHeightfieldDataDestroy to destroy it after the geom has been removed. + * The dHeightfieldDataID value is used when specifying a data format type. + * + * @return A dHeightfieldDataID for use with dGeomHeightfieldDataBuildCallback, + * dGeomHeightfieldDataBuildByte, dGeomHeightfieldDataBuildShort or + * dGeomHeightfieldDataBuildFloat. + * @ingroup collide + */ +ODE_API dHeightfieldDataID dGeomHeightfieldDataCreate(void); + + +/** + * @brief Destroys a dHeightfieldDataID. + * + * Deallocates a given dHeightfieldDataID and all managed resources. + * + * @param d A dHeightfieldDataID created by dGeomHeightfieldDataCreate + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataDestroy( dHeightfieldDataID d ); + + + +/** + * @brief Configures a dHeightfieldDataID to use a callback to + * retrieve height data. + * + * Before a dHeightfieldDataID can be used by a geom it must be + * configured to specify the format of the height data. + * This call specifies that the heightfield data is computed by + * the user and it should use the given callback when determining + * the height of a given element of it's shape. + * + * @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate + * + * @param width Specifies the total 'width' of the heightfield along + * the geom's local x axis. + * @param depth Specifies the total 'depth' of the heightfield along + * the geom's local z axis. + * + * @param widthSamples Specifies the number of vertices to sample + * along the width of the heightfield. Each vertex has a corresponding + * height value which forms the overall shape. + * Naturally this value must be at least two or more. + * @param depthSamples Specifies the number of vertices to sample + * along the depth of the heightfield. + * + * @param scale A uniform scale applied to all raw height data. + * @param offset An offset applied to the scaled height data. + * + * @param thickness A value subtracted from the lowest height + * value which in effect adds an additional cuboid to the base of the + * heightfield. This is used to prevent geoms from looping under the + * desired terrain and not registering as a collision. Note that the + * thickness is not affected by the scale or offset parameters. + * + * @param bWrap If non-zero the heightfield will infinitely tile in both + * directions along the local x and z axes. If zero the heightfield is + * bounded from zero to width in the local x axis, and zero to depth in + * the local z axis. + * + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataBuildCallback( dHeightfieldDataID d, + void* pUserData, dHeightfieldGetHeight* pCallback, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ); + +/** + * @brief Configures a dHeightfieldDataID to use height data in byte format. + * + * Before a dHeightfieldDataID can be used by a geom it must be + * configured to specify the format of the height data. + * This call specifies that the heightfield data is stored as a rectangular + * array of bytes (8 bit unsigned) representing the height at each sample point. + * + * @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate + * + * @param pHeightData A pointer to the height data. + * @param bCopyHeightData When non-zero the height data is copied to an + * internal store. When zero the height data is accessed by reference and + * so must persist throughout the lifetime of the heightfield. + * + * @param width Specifies the total 'width' of the heightfield along + * the geom's local x axis. + * @param depth Specifies the total 'depth' of the heightfield along + * the geom's local z axis. + * + * @param widthSamples Specifies the number of vertices to sample + * along the width of the heightfield. Each vertex has a corresponding + * height value which forms the overall shape. + * Naturally this value must be at least two or more. + * @param depthSamples Specifies the number of vertices to sample + * along the depth of the heightfield. + * + * @param scale A uniform scale applied to all raw height data. + * @param offset An offset applied to the scaled height data. + * + * @param thickness A value subtracted from the lowest height + * value which in effect adds an additional cuboid to the base of the + * heightfield. This is used to prevent geoms from looping under the + * desired terrain and not registering as a collision. Note that the + * thickness is not affected by the scale or offset parameters. + * + * @param bWrap If non-zero the heightfield will infinitely tile in both + * directions along the local x and z axes. If zero the heightfield is + * bounded from zero to width in the local x axis, and zero to depth in + * the local z axis. + * + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataBuildByte( dHeightfieldDataID d, + const unsigned char* pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ); + +/** + * @brief Configures a dHeightfieldDataID to use height data in short format. + * + * Before a dHeightfieldDataID can be used by a geom it must be + * configured to specify the format of the height data. + * This call specifies that the heightfield data is stored as a rectangular + * array of shorts (16 bit signed) representing the height at each sample point. + * + * @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate + * + * @param pHeightData A pointer to the height data. + * @param bCopyHeightData When non-zero the height data is copied to an + * internal store. When zero the height data is accessed by reference and + * so must persist throughout the lifetime of the heightfield. + * + * @param width Specifies the total 'width' of the heightfield along + * the geom's local x axis. + * @param depth Specifies the total 'depth' of the heightfield along + * the geom's local z axis. + * + * @param widthSamples Specifies the number of vertices to sample + * along the width of the heightfield. Each vertex has a corresponding + * height value which forms the overall shape. + * Naturally this value must be at least two or more. + * @param depthSamples Specifies the number of vertices to sample + * along the depth of the heightfield. + * + * @param scale A uniform scale applied to all raw height data. + * @param offset An offset applied to the scaled height data. + * + * @param thickness A value subtracted from the lowest height + * value which in effect adds an additional cuboid to the base of the + * heightfield. This is used to prevent geoms from looping under the + * desired terrain and not registering as a collision. Note that the + * thickness is not affected by the scale or offset parameters. + * + * @param bWrap If non-zero the heightfield will infinitely tile in both + * directions along the local x and z axes. If zero the heightfield is + * bounded from zero to width in the local x axis, and zero to depth in + * the local z axis. + * + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataBuildShort( dHeightfieldDataID d, + const short* pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ); + +/** + * @brief Configures a dHeightfieldDataID to use height data in + * single precision floating point format. + * + * Before a dHeightfieldDataID can be used by a geom it must be + * configured to specify the format of the height data. + * This call specifies that the heightfield data is stored as a rectangular + * array of single precision floats representing the height at each + * sample point. + * + * @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate + * + * @param pHeightData A pointer to the height data. + * @param bCopyHeightData When non-zero the height data is copied to an + * internal store. When zero the height data is accessed by reference and + * so must persist throughout the lifetime of the heightfield. + * + * @param width Specifies the total 'width' of the heightfield along + * the geom's local x axis. + * @param depth Specifies the total 'depth' of the heightfield along + * the geom's local z axis. + * + * @param widthSamples Specifies the number of vertices to sample + * along the width of the heightfield. Each vertex has a corresponding + * height value which forms the overall shape. + * Naturally this value must be at least two or more. + * @param depthSamples Specifies the number of vertices to sample + * along the depth of the heightfield. + * + * @param scale A uniform scale applied to all raw height data. + * @param offset An offset applied to the scaled height data. + * + * @param thickness A value subtracted from the lowest height + * value which in effect adds an additional cuboid to the base of the + * heightfield. This is used to prevent geoms from looping under the + * desired terrain and not registering as a collision. Note that the + * thickness is not affected by the scale or offset parameters. + * + * @param bWrap If non-zero the heightfield will infinitely tile in both + * directions along the local x and z axes. If zero the heightfield is + * bounded from zero to width in the local x axis, and zero to depth in + * the local z axis. + * + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataBuildSingle( dHeightfieldDataID d, + const float* pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ); + +/** + * @brief Configures a dHeightfieldDataID to use height data in + * double precision floating point format. + * + * Before a dHeightfieldDataID can be used by a geom it must be + * configured to specify the format of the height data. + * This call specifies that the heightfield data is stored as a rectangular + * array of double precision floats representing the height at each + * sample point. + * + * @param d A new dHeightfieldDataID created by dGeomHeightfieldDataCreate + * + * @param pHeightData A pointer to the height data. + * @param bCopyHeightData When non-zero the height data is copied to an + * internal store. When zero the height data is accessed by reference and + * so must persist throughout the lifetime of the heightfield. + * + * @param width Specifies the total 'width' of the heightfield along + * the geom's local x axis. + * @param depth Specifies the total 'depth' of the heightfield along + * the geom's local z axis. + * + * @param widthSamples Specifies the number of vertices to sample + * along the width of the heightfield. Each vertex has a corresponding + * height value which forms the overall shape. + * Naturally this value must be at least two or more. + * @param depthSamples Specifies the number of vertices to sample + * along the depth of the heightfield. + * + * @param scale A uniform scale applied to all raw height data. + * @param offset An offset applied to the scaled height data. + * + * @param thickness A value subtracted from the lowest height + * value which in effect adds an additional cuboid to the base of the + * heightfield. This is used to prevent geoms from looping under the + * desired terrain and not registering as a collision. Note that the + * thickness is not affected by the scale or offset parameters. + * + * @param bWrap If non-zero the heightfield will infinitely tile in both + * directions along the local x and z axes. If zero the heightfield is + * bounded from zero to width in the local x axis, and zero to depth in + * the local z axis. + * + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataBuildDouble( dHeightfieldDataID d, + const double* pHeightData, int bCopyHeightData, + dReal width, dReal depth, int widthSamples, int depthSamples, + dReal scale, dReal offset, dReal thickness, int bWrap ); + +/** + * @brief Manually set the minimum and maximum height bounds. + * + * This call allows you to set explicit min / max values after initial + * creation typically for callback heightfields which default to +/- infinity, + * or those whose data has changed. This must be set prior to binding with a + * geom, as the the AABB is not recomputed after it's first generation. + * + * @remarks The minimum and maximum values are used to compute the AABB + * for the heightfield which is used for early rejection of collisions. + * A close fit will yield a more efficient collision check. + * + * @param d A dHeightfieldDataID created by dGeomHeightfieldDataCreate + * @param min_height The new minimum height value. Scale, offset and thickness is then applied. + * @param max_height The new maximum height value. Scale and offset is then applied. + * @ingroup collide + */ +ODE_API void dGeomHeightfieldDataSetBounds( dHeightfieldDataID d, + dReal minHeight, dReal maxHeight ); + + +/** + * @brief Assigns a dHeightfieldDataID to a heightfield geom. + * + * Associates the given dHeightfieldDataID with a heightfield geom. + * This is done without affecting the GEOM_PLACEABLE flag. + * + * @param g A geom created by dCreateHeightfield + * @param d A dHeightfieldDataID created by dGeomHeightfieldDataCreate + * @ingroup collide + */ +ODE_API void dGeomHeightfieldSetHeightfieldData( dGeomID g, dHeightfieldDataID d ); + + +/** + * @brief Gets the dHeightfieldDataID bound to a heightfield geom. + * + * Returns the dHeightfieldDataID associated with a heightfield geom. + * + * @param g A geom created by dCreateHeightfield + * @return The dHeightfieldDataID which may be NULL if none was assigned. + * @ingroup collide + */ +ODE_API dHeightfieldDataID dGeomHeightfieldGetHeightfieldData( dGeomID g ); + + + +/* ************************************************************************ */ +/* utility functions */ + +ODE_API void dClosestLineSegmentPoints (const dVector3 a1, const dVector3 a2, + const dVector3 b1, const dVector3 b2, + dVector3 cp1, dVector3 cp2); + +ODE_API int dBoxTouchesBox (const dVector3 _p1, const dMatrix3 R1, + const dVector3 side1, const dVector3 _p2, + const dMatrix3 R2, const dVector3 side2); + +/* The meaning of flags parameter is the same as in dCollide()*/ +ODE_API int dBoxBox (const dVector3 p1, const dMatrix3 R1, + const dVector3 side1, const dVector3 p2, + const dMatrix3 R2, const dVector3 side2, + dVector3 normal, dReal *depth, int *return_code, + int flags, dContactGeom *contact, int skip); + +ODE_API void dInfiniteAABB (dGeomID geom, dReal aabb[6]); + + +/* ************************************************************************ */ +/* custom classes */ + +typedef void dGetAABBFn (dGeomID, dReal aabb[6]); +typedef int dColliderFn (dGeomID o1, dGeomID o2, + int flags, dContactGeom *contact, int skip); +typedef dColliderFn * dGetColliderFnFn (int num); +typedef void dGeomDtorFn (dGeomID o); +typedef int dAABBTestFn (dGeomID o1, dGeomID o2, dReal aabb[6]); + +typedef struct dGeomClass { + int bytes; + dGetColliderFnFn *collider; + dGetAABBFn *aabb; + dAABBTestFn *aabb_test; + dGeomDtorFn *dtor; +} dGeomClass; + +ODE_API int dCreateGeomClass (const dGeomClass *classptr); +ODE_API void * dGeomGetClassData (dGeomID); +ODE_API dGeomID dCreateGeom (int classnum); + +/** + * @brief Sets a custom collider function for two geom classes. + * + * @param i The first geom class handled by this collider + * @param j The second geom class handled by this collider + * @param fn The collider function to use to determine collisions. + * @ingroup collide + */ +ODE_API void dSetColliderOverride (int i, int j, dColliderFn *fn); + + +/* ************************************************************************ */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/collision_space.h b/thirdparty/ode-0.16.5/include/ode/collision_space.h new file mode 100644 index 0000000..9004cdd --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/collision_space.h @@ -0,0 +1,182 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COLLISION_SPACE_H_ +#define _ODE_COLLISION_SPACE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct dContactGeom; + +/** + * @brief User callback for geom-geom collision testing. + * + * @param data The user data object, as passed to dSpaceCollide. + * @param o1 The first geom being tested. + * @param o2 The second geom being tested. + * + * @remarks The callback function can call dCollide on o1 and o2 to generate + * contact points between each pair. Then these contact points may be added + * to the simulation as contact joints. The user's callback function can of + * course chose not to call dCollide for any pair, e.g. if the user decides + * that those pairs should not interact. + * + * @ingroup collide + */ +typedef void dNearCallback (void *data, dGeomID o1, dGeomID o2); + + +ODE_API dSpaceID dSimpleSpaceCreate (dSpaceID space); +ODE_API dSpaceID dHashSpaceCreate (dSpaceID space); +ODE_API dSpaceID dQuadTreeSpaceCreate (dSpaceID space, const dVector3 Center, const dVector3 Extents, int Depth); + + +/* SAP */ +/* Order XZY or ZXY usually works best, if your Y is up. */ +#define dSAP_AXES_XYZ ((0)|(1<<2)|(2<<4)) +#define dSAP_AXES_XZY ((0)|(2<<2)|(1<<4)) +#define dSAP_AXES_YXZ ((1)|(0<<2)|(2<<4)) +#define dSAP_AXES_YZX ((1)|(2<<2)|(0<<4)) +#define dSAP_AXES_ZXY ((2)|(0<<2)|(1<<4)) +#define dSAP_AXES_ZYX ((2)|(1<<2)|(0<<4)) + +ODE_API dSpaceID dSweepAndPruneSpaceCreate( dSpaceID space, int axisorder ); + + + +ODE_API void dSpaceDestroy (dSpaceID); + +ODE_API void dHashSpaceSetLevels (dSpaceID space, int minlevel, int maxlevel); +ODE_API void dHashSpaceGetLevels (dSpaceID space, int *minlevel, int *maxlevel); + +ODE_API void dSpaceSetCleanup (dSpaceID space, int mode); +ODE_API int dSpaceGetCleanup (dSpaceID space); + +/** +* @brief Sets sublevel value for a space. +* +* Sublevel affects how the space is handled in dSpaceCollide2 when it is collided +* with another space. If sublevels of both spaces match, the function iterates +* geometries of both spaces and collides them with each other. If sublevel of one +* space is greater than the sublevel of another one, only the geometries of the +* space with greater sublevel are iterated, another space is passed into +* collision callback as a geometry itself. By default all the spaces are assigned +* zero sublevel. +* +* @note +* The space sublevel @e IS @e NOT automatically updated when one space is inserted +* into another or removed from one. It is a client's responsibility to update sublevel +* value if necessary. +* +* @param space the space to modify +* @param sublevel the sublevel value to be assigned +* @ingroup collide +* @see dSpaceGetSublevel +* @see dSpaceCollide2 +*/ +ODE_API void dSpaceSetSublevel (dSpaceID space, int sublevel); + +/** +* @brief Gets sublevel value of a space. +* +* Sublevel affects how the space is handled in dSpaceCollide2 when it is collided +* with another space. See @c dSpaceSetSublevel for more details. +* +* @param space the space to query +* @returns the sublevel value of the space +* @ingroup collide +* @see dSpaceSetSublevel +* @see dSpaceCollide2 +*/ +ODE_API int dSpaceGetSublevel (dSpaceID space); + + +/** +* @brief Sets manual cleanup flag for a space. +* +* Manual cleanup flag marks a space as eligible for manual thread data cleanup. +* This function should be called for every space object right after creation in +* case if ODE has been initialized with @c dInitFlagManualThreadCleanup flag. +* +* Failure to set manual cleanup flag for a space may lead to some resources +* remaining leaked until the program exit. +* +* @param space the space to modify +* @param mode 1 for manual cleanup mode and 0 for default cleanup mode +* @ingroup collide +* @see dSpaceGetManualCleanup +* @see dInitODE2 +*/ +ODE_API void dSpaceSetManualCleanup (dSpaceID space, int mode); + +/** +* @brief Get manual cleanup flag of a space. +* +* Manual cleanup flag marks a space space as eligible for manual thread data cleanup. +* See @c dSpaceSetManualCleanup for more details. +* +* @param space the space to query +* @returns 1 for manual cleanup mode and 0 for default cleanup mode of the space +* @ingroup collide +* @see dSpaceSetManualCleanup +* @see dInitODE2 +*/ +ODE_API int dSpaceGetManualCleanup (dSpaceID space); + +ODE_API void dSpaceAdd (dSpaceID, dGeomID); +ODE_API void dSpaceRemove (dSpaceID, dGeomID); +ODE_API int dSpaceQuery (dSpaceID, dGeomID); +ODE_API void dSpaceClean (dSpaceID); +ODE_API int dSpaceGetNumGeoms (dSpaceID); +ODE_API dGeomID dSpaceGetGeom (dSpaceID, int i); + +/** + * @brief Given a space, this returns its class. + * + * The ODE classes are: + * @li dSimpleSpaceClass + * @li dHashSpaceClass + * @li dSweepAndPruneSpaceClass + * @li dQuadTreeSpaceClass + * @li dFirstUserClass + * @li dLastUserClass + * + * The class id not defined by the user should be between + * dFirstSpaceClass and dLastSpaceClass. + * + * User-defined class will return their own number. + * + * @param space the space to query + * @returns The space class ID. + * @ingroup collide + */ +ODE_API int dSpaceGetClass(dSpaceID space); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/collision_trimesh.h b/thirdparty/ode-0.16.5/include/ode/collision_trimesh.h new file mode 100644 index 0000000..914caa9 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/collision_trimesh.h @@ -0,0 +1,316 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + * TriMesh code by Erwin de Vries. + * + * Trimesh data. + * This is where the actual vertexdata (pointers), and BV tree is stored. + * Vertices should be single precision! + * This should be more sophisticated, so that the user can easily implement + * another collision library, but this is a lot of work, and also costs some + * performance because some data has to be copied. + */ + +#ifndef _ODE_COLLISION_TRIMESH_H_ +#define _ODE_COLLISION_TRIMESH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Data storage for triangle meshes. + */ +struct dxTriMeshData; +typedef struct dxTriMeshData* dTriMeshDataID; + + +typedef enum +{ + dMTV__MIN, + + dMTV_FIRST = dMTV__MIN, + dMTV_SECOND, + dMTV_THIRD, + + dMTV__MAX, + +} dMeshTriangleVertex; + +/* + * These don't make much sense now, but they will later when we add more + * features. + */ +ODE_API dTriMeshDataID dGeomTriMeshDataCreate(void); +ODE_API void dGeomTriMeshDataDestroy(dTriMeshDataID g); + + +/* + * The values of data_id that can be used with dGeomTriMeshDataSet/dGeomTriMeshDataGet + */ +enum +{ + dTRIMESHDATA__MIN, + + dTRIMESHDATA_FACE_NORMALS = dTRIMESHDATA__MIN, + dTRIMESHDATA_USE_FLAGS, + + dTRIMESHDATA__MAX, + +#ifndef TRIMESH_FACE_NORMALS // Define this name during the header inclusion if you need it for something else + // Included for backward compatibility -- please use the corrected name above. Sorry. + TRIMESH_FACE_NORMALS = dTRIMESHDATA_FACE_NORMALS, +#endif +}; + +/* + * The flags of the dTRIMESHDATA_USE_FLAGS data elements + */ +enum +{ + dMESHDATAUSE_EDGE1 = 0x01, + dMESHDATAUSE_EDGE2 = 0x02, + dMESHDATAUSE_EDGE3 = 0x04, + dMESHDATAUSE_VERTEX1 = 0x08, + dMESHDATAUSE_VERTEX2 = 0x10, + dMESHDATAUSE_VERTEX3 = 0x20, +}; + +/* + * Set and get the TriMeshData additional data + * Note: The data is NOT COPIED on assignment + */ +ODE_API void dGeomTriMeshDataSet(dTriMeshDataID g, int data_id, void *in_data); +ODE_API void *dGeomTriMeshDataGet(dTriMeshDataID g, int data_id); +ODE_API void *dGeomTriMeshDataGet2(dTriMeshDataID g, int data_id, dsizeint *pout_size/*=NULL*/); + + + +/** + * We need to set the last transform after each time step for + * accurate collision response. These functions get and set that transform. + * It is stored per geom instance, rather than per dTriMeshDataID. + */ +ODE_API void dGeomTriMeshSetLastTransform( dGeomID g, const dMatrix4 last_trans ); +ODE_API const dReal* dGeomTriMeshGetLastTransform( dGeomID g ); + +/* + * Build a TriMesh data object with single precision vertex data. + */ +ODE_API void dGeomTriMeshDataBuildSingle(dTriMeshDataID g, + const void* Vertices, int VertexStride, int VertexCount, + const void* Indices, int IndexCount, int TriStride); +/* same again with a normals array (used as trimesh-trimesh optimization) */ +ODE_API void dGeomTriMeshDataBuildSingle1(dTriMeshDataID g, + const void* Vertices, int VertexStride, int VertexCount, + const void* Indices, int IndexCount, int TriStride, + const void* Normals); +/* +* Build a TriMesh data object with double precision vertex data. +*/ +ODE_API void dGeomTriMeshDataBuildDouble(dTriMeshDataID g, + const void* Vertices, int VertexStride, int VertexCount, + const void* Indices, int IndexCount, int TriStride); +/* same again with a normals array (used as trimesh-trimesh optimization) */ +ODE_API void dGeomTriMeshDataBuildDouble1(dTriMeshDataID g, + const void* Vertices, int VertexStride, int VertexCount, + const void* Indices, int IndexCount, int TriStride, + const void* Normals); + +/* + * Simple build. Single/double precision based on dSINGLE/dDOUBLE! + */ +ODE_API void dGeomTriMeshDataBuildSimple(dTriMeshDataID g, + const dReal* Vertices, int VertexCount, + const dTriIndex* Indices, int IndexCount); +/* same again with a normals array (used as trimesh-trimesh optimization) */ +ODE_API void dGeomTriMeshDataBuildSimple1(dTriMeshDataID g, + const dReal* Vertices, int VertexCount, + const dTriIndex* Indices, int IndexCount, + const int* Normals); + + +/* + * Data preprocessing build request flags. + */ +enum +{ + dTRIDATAPREPROCESS_BUILD__MIN, + + dTRIDATAPREPROCESS_BUILD_CONCAVE_EDGES = dTRIDATAPREPROCESS_BUILD__MIN, // Used to optimize OPCODE trimesh-capsule collisions; allocates 1 byte per triangle; no extra data associated + dTRIDATAPREPROCESS_BUILD_FACE_ANGLES, // Used to aid trimesh-convex collisions; memory requirements depend on extra data + + dTRIDATAPREPROCESS_BUILD__MAX, +}; + +/* + * Data preprocessing extra values for dTRIDATAPREPROCESS_BUILD_FACE_ANGLES. + */ +enum +{ + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MIN, + + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_BYTE_POSITIVE = dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MIN, // Build angles for convex edges only and store as bytes; allocates 3 bytes per triangle; stores angles (0..180] in 1/254 fractions leaving two values for the flat and all the concaves + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_BYTE_ALL, // Build angles for all the edges and store in bytes; allocates 3 bytes per triangle; stores angles [-180..0) and (0..180] in 1/127 fractions plus a value for the flat angle + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_WORD_ALL, // Build angles for all the edges and store in words; allocates 6 bytes per triangle; stores angles [-180..0) and (0..180] in 1/32767 fractions plus a value for the flat angle + + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__MAX, + + dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA__DEFAULT = dTRIDATAPREPROCESS_FACE_ANGLES_EXTRA_BYTE_POSITIVE, // The default value assumed if the extra data is not provided +}; + + +/* + * Pre-process the trimesh data according to the request flags. + * + * buildRequestFlags is a bitmask of 1U << dTRIDATAPREPROCESS_BUILD_... + * It is allowed to call the function multiple times provided the bitmasks are different each time. + * + * requestExtraData is an optional pointer to array of extra parameters per bitmask bits + * (only the elements indexed by positions of raised bits are examined; + * defaults are assumed if the pointer is NULL) + * + * The function returns a boolean status the only failure reason being insufficient memory. + */ +ODE_API int dGeomTriMeshDataPreprocess2(dTriMeshDataID g, unsigned int buildRequestFlags, const dintptr *requestExtraData/*=NULL | const dintptr (*)[dTRIDATAPREPROCESS_BUILD__MAX]*/); + +/* + * Obsolete. Equivalent to calling dGeomTriMeshDataPreprocess2(g, (1U << dTRIDATAPREPROCESS_BUILD_CONCAVE_EDGES), NULL) + */ +ODE_API int dGeomTriMeshDataPreprocess(dTriMeshDataID g); + + + +/* + * Get and set the internal preprocessed trimesh data buffer (see the enumerated type above), for loading and saving + * These functions are deprecated. Use dGeomTriMeshDataSet/dGeomTriMeshDataGet2 with dTRIMESHDATA_USE_FLAGS instead. + */ +ODE_API_DEPRECATED ODE_API void dGeomTriMeshDataGetBuffer(dTriMeshDataID g, unsigned char** buf, int* bufLen); +ODE_API_DEPRECATED ODE_API void dGeomTriMeshDataSetBuffer(dTriMeshDataID g, unsigned char* buf); + + +/* + * Per triangle callback. Allows the user to say if he wants a collision with + * a particular triangle. + */ +typedef int dTriCallback(dGeomID TriMesh, dGeomID RefObject, int TriangleIndex); +ODE_API void dGeomTriMeshSetCallback(dGeomID g, dTriCallback* Callback); +ODE_API dTriCallback* dGeomTriMeshGetCallback(dGeomID g); + +/* + * Per object callback. Allows the user to get the list of triangles in 1 + * shot. Maybe we should remove this one. + */ +typedef void dTriArrayCallback(dGeomID TriMesh, dGeomID RefObject, const int* TriIndices, int TriCount); +ODE_API void dGeomTriMeshSetArrayCallback(dGeomID g, dTriArrayCallback* ArrayCallback); +ODE_API dTriArrayCallback* dGeomTriMeshGetArrayCallback(dGeomID g); + +/* + * Ray callback. + * Allows the user to say if a ray collides with a triangle on barycentric + * coords. The user can for example sample a texture with alpha transparency + * to determine if a collision should occur. + */ +typedef int dTriRayCallback(dGeomID TriMesh, dGeomID Ray, int TriangleIndex, dReal u, dReal v); +ODE_API void dGeomTriMeshSetRayCallback(dGeomID g, dTriRayCallback* Callback); +ODE_API dTriRayCallback* dGeomTriMeshGetRayCallback(dGeomID g); + +/* + * Triangle merging callback. + * Allows the user to generate a fake triangle index for a new contact generated + * from merging of two other contacts. That index could later be used by the + * user to determine attributes of original triangles used as sources for a + * merged contact. + */ +typedef int dTriTriMergeCallback(dGeomID TriMesh, int FirstTriangleIndex, int SecondTriangleIndex); +ODE_API void dGeomTriMeshSetTriMergeCallback(dGeomID g, dTriTriMergeCallback* Callback); +ODE_API dTriTriMergeCallback* dGeomTriMeshGetTriMergeCallback(dGeomID g); + +/* + * Trimesh class + * Construction. Callbacks are optional. + */ +ODE_API dGeomID dCreateTriMesh(dSpaceID space, dTriMeshDataID Data, dTriCallback* Callback, dTriArrayCallback* ArrayCallback, dTriRayCallback* RayCallback); + +ODE_API void dGeomTriMeshSetData(dGeomID g, dTriMeshDataID Data); +ODE_API dTriMeshDataID dGeomTriMeshGetData(dGeomID g); + + +/* enable/disable/check temporal coherence*/ +ODE_API void dGeomTriMeshEnableTC(dGeomID g, int geomClass, int enable); +ODE_API int dGeomTriMeshIsTCEnabled(dGeomID g, int geomClass); + +/* + * Clears the internal temporal coherence caches. When a geom has its + * collision checked with a trimesh once, data is stored inside the trimesh. + * With large worlds with lots of seperate objects this list could get huge. + * We should be able to do this automagically. + */ +ODE_API void dGeomTriMeshClearTCCache(dGeomID g); + + +/* + * returns the TriMeshDataID + */ +ODE_API dTriMeshDataID dGeomTriMeshGetTriMeshDataID(dGeomID g); + +/* + * Gets a triangle. + */ +ODE_API void dGeomTriMeshGetTriangle(dGeomID g, int Index, dVector3* v0, dVector3* v1, dVector3* v2); + +/* + * Gets the point on the requested triangle and the given barycentric + * coordinates. + */ +ODE_API void dGeomTriMeshGetPoint(dGeomID g, int Index, dReal u, dReal v, dVector3 Out); + +/* + +This is how the strided data works: + +struct StridedVertex{ + dVector3 Vertex; + // Userdata +}; +int VertexStride = sizeof(StridedVertex); + +struct StridedTri{ + int Indices[3]; + // Userdata +}; +int TriStride = sizeof(StridedTri); + +*/ + + +ODE_API int dGeomTriMeshGetTriangleCount (dGeomID g); + +ODE_API void dGeomTriMeshDataUpdate(dTriMeshDataID g); + +#ifdef __cplusplus +} +#endif + +#endif /* _ODE_COLLISION_TRIMESH_H_ */ + diff --git a/thirdparty/ode-0.16.5/include/ode/common.h b/thirdparty/ode-0.16.5/include/ode/common.h new file mode 100644 index 0000000..b0a5793 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/common.h @@ -0,0 +1,568 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COMMON_H_ +#define _ODE_COMMON_H_ + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* configuration stuff */ + +/* constants */ + +/* pi and 1/sqrt(2) are defined here if necessary because they don't get + * defined in on some platforms (like MS-Windows) + */ + +#ifndef M_PI +#define M_PI REAL(3.1415926535897932384626433832795029) +#endif +#ifndef M_PI_2 +#define M_PI_2 REAL(1.5707963267948966192313216916398) +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 REAL(0.7071067811865475244008443621048490) +#endif + + +/* floating point data type, vector, matrix and quaternion types */ + +#if defined(dSINGLE) +typedef float dReal; +#ifdef dDOUBLE +#error You can only #define dSINGLE or dDOUBLE, not both. +#endif /* dDOUBLE */ +#elif defined(dDOUBLE) +typedef double dReal; +#else +#error You must #define dSINGLE or dDOUBLE +#endif + +/* Detect if we've got both trimesh engines enabled. */ +#if dTRIMESH_ENABLED +#if dTRIMESH_OPCODE && dTRIMESH_GIMPACT +#error You can only #define dTRIMESH_OPCODE or dTRIMESH_GIMPACT, not both. +#endif +#endif /* dTRIMESH_ENABLED */ + +/* + * Define a type for indices, either 16 or 32 bit, based on build option + * TODO: Currently GIMPACT only supports 32 bit indices. + */ +#if dTRIMESH_16BIT_INDICES +#if dTRIMESH_GIMPACT +typedef duint32 dTriIndex; +#else /* dTRIMESH_GIMPACT */ +typedef duint16 dTriIndex; +#endif /* dTRIMESH_GIMPACT */ +#else /* dTRIMESH_16BIT_INDICES */ +typedef duint32 dTriIndex; +#endif /* dTRIMESH_16BIT_INDICES */ + +/* round an integer up to a multiple of 4, except that 0 and 1 are unmodified + * (used to compute matrix leading dimensions) + */ +#define dPAD(a) (((a) > 1) ? (((a) + 3) & (int)(~3)) : (a)) + +typedef enum { + dSA__MIN, + + dSA_X = dSA__MIN, + dSA_Y, + dSA_Z, + + dSA__MAX, +} dSpaceAxis; + +typedef enum { + dMD__MIN, + + dMD_LINEAR = dMD__MIN, + dMD_ANGULAR, + + dMD__MAX, +} dMotionDynamics; + +typedef enum { + dDA__MIN, + + dDA__L_MIN = dDA__MIN + dMD_LINEAR * dSA__MAX, + + dDA_LX = dDA__L_MIN + dSA_X, + dDA_LY = dDA__L_MIN + dSA_Y, + dDA_LZ = dDA__L_MIN + dSA_Z, + + dDA__L_MAX = dDA__L_MIN + dSA__MAX, + + dDA__A_MIN = dDA__MIN + dMD_ANGULAR * dSA__MAX, + + dDA_AX = dDA__A_MIN + dSA_X, + dDA_AY = dDA__A_MIN + dSA_Y, + dDA_AZ = dDA__A_MIN + dSA_Z, + + dDA__A_MAX = dDA__A_MIN + dSA__MAX, + + dDA__MAX = dDA__MIN + dMD__MAX * dSA__MAX, +} dDynamicsAxis; + +typedef enum { + dV3E__MIN, + + dV3E__AXES_MIN = dV3E__MIN, + + dV3E_X = dV3E__AXES_MIN + dSA_X, + dV3E_Y = dV3E__AXES_MIN + dSA_Y, + dV3E_Z = dV3E__AXES_MIN + dSA_Z, + + dV3E__AXES_MAX = dV3E__AXES_MIN + dSA__MAX, + + dV3E_PAD = dV3E__AXES_MAX, + + dV3E__MAX, + + dV3E__AXES_COUNT = dV3E__AXES_MAX - dV3E__AXES_MIN, +} dVec3Element; + +typedef enum { + dV4E__MIN, + + dV4E_X = dV4E__MIN + dSA_X, + dV4E_Y = dV4E__MIN + dSA_Y, + dV4E_Z = dV4E__MIN + dSA_Z, + dV4E_O = dV4E__MIN + dSA__MAX, + + dV4E__MAX, +} dVec4Element; + +typedef enum { + dM3E__MIN, + + dM3E__X_MIN = dM3E__MIN + dSA_X * dV3E__MAX, + + dM3E__X_AXES_MIN = dM3E__X_MIN + dV3E__AXES_MIN, + + dM3E_XX = dM3E__X_MIN + dV3E_X, + dM3E_XY = dM3E__X_MIN + dV3E_Y, + dM3E_XZ = dM3E__X_MIN + dV3E_Z, + + dM3E__X_AXES_MAX = dM3E__X_MIN + dV3E__AXES_MAX, + + dM3E_XPAD = dM3E__X_MIN + dV3E_PAD, + + dM3E__X_MAX = dM3E__X_MIN + dV3E__MAX, + + dM3E__Y_MIN = dM3E__MIN + dSA_Y * dV3E__MAX, + + dM3E__Y_AXES_MIN = dM3E__Y_MIN + dV3E__AXES_MIN, + + dM3E_YX = dM3E__Y_MIN + dV3E_X, + dM3E_YY = dM3E__Y_MIN + dV3E_Y, + dM3E_YZ = dM3E__Y_MIN + dV3E_Z, + + dM3E__Y_AXES_MAX = dM3E__Y_MIN + dV3E__AXES_MAX, + + dM3E_YPAD = dM3E__Y_MIN + dV3E_PAD, + + dM3E__Y_MAX = dM3E__Y_MIN + dV3E__MAX, + + dM3E__Z_MIN = dM3E__MIN + dSA_Z * dV3E__MAX, + + dM3E__Z_AXES_MIN = dM3E__Z_MIN + dV3E__AXES_MIN, + + dM3E_ZX = dM3E__Z_MIN + dV3E_X, + dM3E_ZY = dM3E__Z_MIN + dV3E_Y, + dM3E_ZZ = dM3E__Z_MIN + dV3E_Z, + + dM3E__Z_AXES_MAX = dM3E__Z_MIN + dV3E__AXES_MAX, + + dM3E_ZPAD = dM3E__Z_MIN + dV3E_PAD, + + dM3E__Z_MAX = dM3E__Z_MIN + dV3E__MAX, + + dM3E__MAX = dM3E__MIN + dSA__MAX * dV3E__MAX, +} dMat3Element; + +typedef enum { + dM4E__MIN, + + dM4E__X_MIN = dM4E__MIN + dV4E_X * dV4E__MAX, + + dM4E_XX = dM4E__X_MIN + dV4E_X, + dM4E_XY = dM4E__X_MIN + dV4E_Y, + dM4E_XZ = dM4E__X_MIN + dV4E_Z, + dM4E_XO = dM4E__X_MIN + dV4E_O, + + dM4E__X_MAX = dM4E__X_MIN + dV4E__MAX, + + dM4E__Y_MIN = dM4E__MIN + dV4E_Y * dV4E__MAX, + + dM4E_YX = dM4E__Y_MIN + dV4E_X, + dM4E_YY = dM4E__Y_MIN + dV4E_Y, + dM4E_YZ = dM4E__Y_MIN + dV4E_Z, + dM4E_YO = dM4E__Y_MIN + dV4E_O, + + dM4E__Y_MAX = dM4E__Y_MIN + dV4E__MAX, + + dM4E__Z_MIN = dM4E__MIN + dV4E_Z * dV4E__MAX, + + dM4E_ZX = dM4E__Z_MIN + dV4E_X, + dM4E_ZY = dM4E__Z_MIN + dV4E_Y, + dM4E_ZZ = dM4E__Z_MIN + dV4E_Z, + dM4E_ZO = dM4E__Z_MIN + dV4E_O, + + dM4E__Z_MAX = dM4E__Z_MIN + dV4E__MAX, + + dM4E__O_MIN = dM4E__MIN + dV4E_O * dV4E__MAX, + + dM4E_OX = dM4E__O_MIN + dV4E_X, + dM4E_OY = dM4E__O_MIN + dV4E_Y, + dM4E_OZ = dM4E__O_MIN + dV4E_Z, + dM4E_OO = dM4E__O_MIN + dV4E_O, + + dM4E__O_MAX = dM4E__O_MIN + dV4E__MAX, + + dM4E__MAX = dM4E__MIN + dV4E__MAX * dV4E__MAX, +} dMat4Element; + +typedef enum { + dQUE__MIN, + + dQUE_R = dQUE__MIN, + + dQUE__AXIS_MIN, + + dQUE_I = dQUE__AXIS_MIN + dSA_X, + dQUE_J = dQUE__AXIS_MIN + dSA_Y, + dQUE_K = dQUE__AXIS_MIN + dSA_Z, + + dQUE__AXIS_MAX = dQUE__AXIS_MIN + dSA__MAX, + + dQUE__MAX = dQUE__AXIS_MAX, +} dQuatElement; + +/* these types are mainly just used in headers */ +typedef dReal dVector3[dV3E__MAX]; +typedef dReal dVector4[dV4E__MAX]; +typedef dReal dMatrix3[dM3E__MAX]; +typedef dReal dMatrix4[dM4E__MAX]; +typedef dReal dMatrix6[(dMD__MAX * dV3E__MAX) * (dMD__MAX * dSA__MAX)]; +typedef dReal dQuaternion[dQUE__MAX]; + + +/* precision dependent scalar math functions */ + +#if defined(dSINGLE) + +#define REAL(x) (x##f) /* form a constant */ +#define dRecip(x) ((1.0f/(x))) /* reciprocal */ +#define dSqrt(x) (sqrtf(x)) /* square root */ +#define dRecipSqrt(x) ((1.0f/sqrtf(x))) /* reciprocal square root */ +#define dSin(x) (sinf(x)) /* sine */ +#define dCos(x) (cosf(x)) /* cosine */ +#define dFabs(x) (fabsf(x)) /* absolute value */ +#define dAtan2(y,x) (atan2f(y,x)) /* arc tangent with 2 args */ +#define dAsin(x) (asinf(x)) +#define dAcos(x) (acosf(x)) +#define dFMod(a,b) (fmodf(a,b)) /* modulo */ +#define dFloor(x) floorf(x) /* floor */ +#define dCeil(x) ceilf(x) /* ceil */ +#define dCopySign(a,b) _ode_copysignf(a, b) /* copy value sign */ +#define dNextAfter(x, y) _ode_nextafterf(x, y) /* next value after */ + +#ifdef HAVE___ISNANF +#define dIsNan(x) (__isnanf(x)) +#elif defined(HAVE__ISNANF) +#define dIsNan(x) (_isnanf(x)) +#elif defined(HAVE_ISNANF) +#define dIsNan(x) (isnanf(x)) +#else + /* + fall back to _isnan which is the VC way, + this may seem redundant since we already checked + for _isnan before, but if isnan is detected by + configure but is not found during compilation + we should always make sure we check for __isnanf, + _isnanf and isnanf in that order before falling + back to a default + */ +#define dIsNan(x) (_isnan(x)) +#endif + +#elif defined(dDOUBLE) + +#define REAL(x) (x) +#define dRecip(x) (1.0/(x)) +#define dSqrt(x) sqrt(x) +#define dRecipSqrt(x) (1.0/sqrt(x)) +#define dSin(x) sin(x) +#define dCos(x) cos(x) +#define dFabs(x) fabs(x) +#define dAtan2(y,x) atan2((y),(x)) +#define dAsin(x) asin(x) +#define dAcos(x) acos(x) +#define dFMod(a,b) (fmod((a),(b))) +#define dFloor(x) floor(x) +#define dCeil(x) ceil(x) +#define dCopySign(a,b) _ode_copysign(a, b) +#define dNextAfter(x, y) _ode_nextafter(x, y) + +#ifdef HAVE___ISNAN +#define dIsNan(x) (__isnan(x)) +#elif defined(HAVE__ISNAN) +#define dIsNan(x) (_isnan(x)) +#elif defined(HAVE_ISNAN) +#define dIsNan(x) (isnan(x)) +#else +#define dIsNan(x) (_isnan(x)) +#endif + +#else +#error You must #define dSINGLE or dDOUBLE +#endif + +ODE_PURE_INLINE dReal dMin(dReal x, dReal y) { return x <= y ? x : y; } +ODE_PURE_INLINE dReal dMax(dReal x, dReal y) { return x <= y ? y : x; } + + +/* internal object types (all prefixed with `dx') */ + +struct dxWorld; /* dynamics world */ +struct dxSpace; /* collision space */ +struct dxBody; /* rigid body (dynamics object) */ +struct dxGeom; /* geometry (collision object) */ +struct dxJoint; /* joint */ +struct dxJointGroup;/* joint group */ + + +typedef struct dxWorld *dWorldID; +typedef struct dxSpace *dSpaceID; +typedef struct dxBody *dBodyID; +typedef struct dxGeom *dGeomID; +typedef struct dxJoint *dJointID; +typedef struct dxJointGroup *dJointGroupID; + + +/* error numbers */ + +enum { + d_ERR_UNKNOWN = 0, /* unknown error */ + d_ERR_IASSERT, /* internal assertion failed */ + d_ERR_UASSERT, /* user assertion failed */ + d_ERR_LCP /* user assertion failed */ +}; + + +/* joint type numbers */ + +typedef enum { + dJointTypeNone = 0, /* or "unknown" */ + dJointTypeBall, + dJointTypeHinge, + dJointTypeSlider, + dJointTypeContact, + dJointTypeUniversal, + dJointTypeHinge2, + dJointTypeFixed, + dJointTypeNull, + dJointTypeAMotor, + dJointTypeLMotor, + dJointTypePlane2D, + dJointTypePR, + dJointTypePU, + dJointTypePiston, + dJointTypeDBall, + dJointTypeDHinge, + dJointTypeTransmission, +} dJointType; + + +/* an alternative way of setting joint parameters, using joint parameter + * structures and member constants. we don't actually do this yet. + */ + +/* +typedef struct dLimot { + int mode; + dReal lostop, histop; + dReal vel, fmax; + dReal fudge_factor; + dReal bounce, soft; + dReal suspension_erp, suspension_cfm; +} dLimot; + +enum { + dLimotLoStop = 0x0001, + dLimotHiStop = 0x0002, + dLimotVel = 0x0004, + dLimotFMax = 0x0008, + dLimotFudgeFactor = 0x0010, + dLimotBounce = 0x0020, + dLimotSoft = 0x0040 +}; +*/ + + +/* standard joint parameter names. why are these here? - because we don't want + * to include all the joint function definitions in joint.cpp. hmmmm. + * MSVC complains if we call D_ALL_PARAM_NAMES_X with a blank second argument, + * which is why we have the D_ALL_PARAM_NAMES macro as well. please copy and + * paste between these two. + */ + +#define D_ALL_PARAM_NAMES(start) \ + /* parameters for limits and motors */ \ + dParamLoStop = start, \ + dParamHiStop, \ + dParamVel, \ + dParamLoVel, \ + dParamHiVel, \ + dParamFMax, \ + dParamFudgeFactor, \ + dParamBounce, \ + dParamCFM, \ + dParamStopERP, \ + dParamStopCFM, \ + /* parameters for suspension */ \ + dParamSuspensionERP, \ + dParamSuspensionCFM, \ + dParamERP, \ + + /* + * \enum D_ALL_PARAM_NAMES_X + * + * \var dParamGroup This is the starting value of the different group + * (i.e. dParamGroup1, dParamGroup2, dParamGroup3) + * It also helps in the use of parameter + * (dParamGroup2 | dParamFMax) == dParamFMax2 + */ +#define D_ALL_PARAM_NAMES_X(start,x) \ + dParamGroup ## x = start, \ + /* parameters for limits and motors */ \ + dParamLoStop ## x = start, \ + dParamHiStop ## x, \ + dParamVel ## x, \ + dParamLoVel ## x, \ + dParamHiVel ## x, \ + dParamFMax ## x, \ + dParamFudgeFactor ## x, \ + dParamBounce ## x, \ + dParamCFM ## x, \ + dParamStopERP ## x, \ + dParamStopCFM ## x, \ + /* parameters for suspension */ \ + dParamSuspensionERP ## x, \ + dParamSuspensionCFM ## x, \ + dParamERP ## x, + +enum { + D_ALL_PARAM_NAMES(0) + dParamsInGroup, /* < Number of parameter in a group */ + D_ALL_PARAM_NAMES_X(0x000,1) + D_ALL_PARAM_NAMES_X(0x100,2) + D_ALL_PARAM_NAMES_X(0x200,3) + + /* add a multiple of this constant to the basic parameter numbers to get + * the parameters for the second, third etc axes. + */ + dParamGroup=0x100 +}; + + +/* angular motor mode numbers */ + +enum { + dAMotorUser = 0, + dAMotorEuler = 1 +}; + +/* transmission joint mode numbers */ + +enum { + dTransmissionParallelAxes = 0, + dTransmissionIntersectingAxes = 1, + dTransmissionChainDrive = 2 +}; + + +/* joint force feedback information */ + +typedef struct dJointFeedback { + dVector3 f1; /* force applied to body 1 */ + dVector3 t1; /* torque applied to body 1 */ + dVector3 f2; /* force applied to body 2 */ + dVector3 t2; /* torque applied to body 2 */ +} dJointFeedback; + + +/* private functions that must be implemented by the collision library: + * (1) indicate that a geom has moved, (2) get the next geom in a body list. + * these functions are called whenever the position of geoms connected to a + * body have changed, e.g. with dBodySetPosition(), dBodySetRotation(), or + * when the ODE step function updates the body state. + */ + +void dGeomMoved (dGeomID); +dGeomID dGeomGetBodyNext (dGeomID); + +/** + * dGetConfiguration returns the specific ODE build configuration as + * a string of tokens. The string can be parsed in a similar way to + * the OpenGL extension mechanism, the naming convention should be + * familiar too. The following extensions are reported: + * + * ODE + * ODE_single_precision + * ODE_double_precision + * ODE_EXT_no_debug + * ODE_EXT_trimesh + * ODE_EXT_opcode + * ODE_EXT_gimpact + * ODE_OPC_16bit_indices + * ODE_OPC_new_collider + * ODE_EXT_mt_collisions + * ODE_EXT_threading + * ODE_THR_builtin_impl + */ +ODE_API const char* dGetConfiguration (void); + +/** + * Helper to check for a token in the ODE configuration string. + * Caution, this function is case sensitive. + * + * @param token A configuration token, see dGetConfiguration for details + * + * @return 1 if exact token is present, 0 if not present + */ +ODE_API int dCheckConfiguration( const char* token ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/compatibility.h b/thirdparty/ode-0.16.5/include/ode/compatibility.h new file mode 100644 index 0000000..b370986 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/compatibility.h @@ -0,0 +1,40 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COMPATIBILITY_H_ +#define _ODE_COMPATIBILITY_H_ + +/* + * ODE's backward compatibility system ensures that as ODE's API + * evolves, user code will not break. + */ + +/* + * These new rotation function names are more consistent with the + * rest of the API. + */ +#define dQtoR(q,R) dRfromQ((R),(q)) +#define dRtoQ(R,q) dQfromR((q),(R)) +#define dWtoDQ(w,q,dq) dDQfromW((dq),(w),(q)) + + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/contact.h b/thirdparty/ode-0.16.5/include/ode/contact.h new file mode 100644 index 0000000..9756f26 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/contact.h @@ -0,0 +1,110 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_CONTACT_H_ +#define _ODE_CONTACT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +enum { + dContactMu2 = 0x001, /**< Use axis dependent friction */ + dContactAxisDep = 0x001, /**< Same as above */ + dContactFDir1 = 0x002, /**< Use FDir for the first friction value */ + dContactBounce = 0x004, /**< Restore collision energy anti-parallel to the normal */ + dContactSoftERP = 0x008, /**< Don't use global erp for penetration reduction */ + dContactSoftCFM = 0x010, /**< Don't use global cfm for penetration constraint */ + dContactMotion1 = 0x020, /**< Use a non-zero target velocity for the constraint */ + dContactMotion2 = 0x040, + dContactMotionN = 0x080, + dContactSlip1 = 0x100, /**< Force-dependent slip. */ + dContactSlip2 = 0x200, + dContactRolling = 0x400, /**< Rolling/Angular friction */ + + dContactApprox0 = 0x0000, + dContactApprox1_1 = 0x1000, + dContactApprox1_2 = 0x2000, + dContactApprox1_N = 0x4000, /**< For rolling friction */ + dContactApprox1 = 0x7000 +}; + + +typedef struct dSurfaceParameters { + /* must always be defined */ + int mode; + dReal mu; + + /* only defined if the corresponding flag is set in mode */ + dReal mu2; + dReal rho; /**< Rolling friction */ + dReal rho2; + dReal rhoN; /**< Spinning friction */ + dReal bounce; /**< Coefficient of restitution */ + dReal bounce_vel; /**< Bouncing threshold */ + dReal soft_erp; + dReal soft_cfm; + dReal motion1,motion2,motionN; + dReal slip1,slip2; +} dSurfaceParameters; + + +/** + * @brief Describe the contact point between two geoms. + * + * If two bodies touch, or if a body touches a static feature in its + * environment, the contact is represented by one or more "contact + * points", described by dContactGeom. + * + * The convention is that if body 1 is moved along the normal vector by + * a distance depth (or equivalently if body 2 is moved the same distance + * in the opposite direction) then the contact depth will be reduced to + * zero. This means that the normal vector points "in" to body 1. + * + * @ingroup collide + */ +typedef struct dContactGeom { + dVector3 pos; /*< contact position*/ + dVector3 normal; /*< normal vector*/ + dReal depth; /*< penetration depth*/ + dGeomID g1,g2; /*< the colliding geoms*/ + int side1,side2; /*< (to be documented)*/ +} dContactGeom; + + +/* contact info used by contact joint */ + +typedef struct dContact { + dSurfaceParameters surface; + dContactGeom geom; + dVector3 fdir1; +} dContact; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/cooperative.h b/thirdparty/ode-0.16.5/include/ode/cooperative.h new file mode 100644 index 0000000..59757dc --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/cooperative.h @@ -0,0 +1,229 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_COOPERATIVE_H_ +#define _ODE_COOPERATIVE_H_ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup coop Cooperative Algorithms + * + * Algorithms implemented as multiple threads doing work cooperatively. + */ + + +struct dxCooperative; +struct dxResourceRequirements; +struct dxResourceContainer; + +/** + * @brief A container for cooperative algorithms shared context + * + * The Cooperative is a container for cooperative algorithms shared context. + * At present it contains threading object (either a real one or a defaulted + * self-threading). + * + * Cooperative use in functions performing computations must be serialized. That is, + * functions referring to a single instance of Cooperative object must not be called in + * parallel. + */ +typedef struct dxCooperative *dCooperativeID; + + +/** + * @brief A container for resource requirements information + * + * The ResourceRequirements object is a container for descriptive information + * regarding what resources (memory, synchronization objects, etc.) need to be + * allocated for particular computations. The object can be used for accumulating + * resource requirement maxima over multiple functions and then allocating resources + * that would suffice for any of those function calls. + * + * ResourceRequirements objects maintain relations to Cooperative objects since + * amounts of resources that could be required can depend on characteristics of + * shared context, e.g. on maximal number of threads in the threading object. + * + * @ingroup coop + * @see dCooperativeID + * @see dResourceContainerID + */ +typedef struct dxResourceRequirements *dResourceRequirementsID; + +/** + * @brief A container for algorithm allocated resources + * + * The ResourceContainer object can contain resources allocated according to information + * in a ResourceRequirements. The resources inherit link to the threading object + * from the requirements they are allocated according to. + * + * @ingroup coop + * @see dResourceRequirementsID + * @see dCooperativeID + */ +typedef struct dxResourceContainer *dResourceContainerID; + + + /** + * @brief Creates a Cooperative object related to the specified threading. + * + * NULL's are allowed for the threading. In this case the default (global) self-threading + * object will be used. + * + * Use @c dCooperativeDestroy to destroy the object. The Cooperative object must exist + * until after all the objects referencing it are destroyed. + * + * @param functionInfo The threading functions to use + * @param threadingImpl The threading implementation object to use + * @returns The Cooperative object instance or NULL if allocation fails. + * @ingroup coop + * @see dCooperativeDestroy + */ +ODE_API dCooperativeID dCooperativeCreate(const dThreadingFunctionsInfo *functionInfo/*=NULL*/, dThreadingImplementationID threadingImpl/*=NULL*/); + + /** + * @brief Destroys Cooperative object. + * + * The Cooperative object can only be destroyed after all the objects referencing it. + * + * @param cooperative A Cooperative object to be deleted (NULL is allowed) + * @ingroup coop + * @see dCooperativeCreate + */ +ODE_API void dCooperativeDestroy(dCooperativeID cooperative); + + + /** + * @brief Creates a ResourceRequirements object related to a Cooperative. + * + * The object is purely descriptive and does not contain any resources by itself. + * The actual resources are allocated by means of ResourceContainer object. + * + * The object is created with empty requirements. It can be then used to accumulate + * requirements for one or more function calls and can be cloned or merged with others. + * The actual requirements information is added to the object by computation related + * functions. + * + * Use @c dResourceRequirementsDestroy to delete the object when it is no longer needed. + * + * @param cooperative A Cooperative object to be used + * @returns The ResourceRequirements object instance or NULL if allocation fails + * @ingroup coop + * @see dResourceRequirementsDestroy + * @see dResourceRequirementsClone + * @see dResourceRequirementsMergeIn + * @see dCooperativeCreate + * @see dResourceContainerAcquire + */ +ODE_API dResourceRequirementsID dResourceRequirementsCreate(dCooperativeID cooperative); + + /** + * @brief Destroys ResourceRequirements object. + * + * The ResourceRequirements object can be destroyed at any time with no regards + * to other objects' lifetime. + * + * @param requirements A ResourceRequirements object to be deleted (NULL is allowed) + * @ingroup coop + * @see dResourceRequirementsCreate + */ +ODE_API void dResourceRequirementsDestroy(dResourceRequirementsID requirements); + + /** + * @brief Clones ResourceRequirements object. + * + * The function creates a copy of the ResourceRequirements object with all the + * contents and the relation to Cooperative matching. The object passed in + * the parameter is not changed. + * + * The object created with the function must later be destroyed with @c dResourceRequirementsDestroy. + * + * @param requirements A ResourceRequirements object to be cloned + * @returns A handle to the new object or NULL if allocation fails + * @ingroup coop + * @see dResourceRequirementsCreate + * @see dResourceRequirementsDestroy + * @see dResourceRequirementsMergeIn + */ +ODE_API dResourceRequirementsID dResourceRequirementsClone(/*const */dResourceRequirementsID requirements); + + /** + * @brief Merges one ResourceRequirements object into another ResourceRequirements object. + * + * The function updates @a summaryRequirements requirements to be also sufficient + * for the purposes @a extraRequirements could be used for. The @a extraRequirements + * object is not changed. Both objects should normally have been created + * with the same Cooperative object. + * + * @param summaryRequirements A ResourceRequirements object to be changed + * @param extraRequirements A ResourceRequirements the requirements to be taken from + * @ingroup coop + * @see dResourceRequirementsCreate + * @see dResourceRequirementsDestroy + * @see dResourceRequirementsClone + */ +ODE_API void dResourceRequirementsMergeIn(dResourceRequirementsID summaryRequirements, /*const */dResourceRequirementsID extraRequirements); + + + /** + * @brief Allocates resources as specified in ResourceRequirements object. + * + * The ResourceContainer object can be used in cooperative computation algorithms. + * + * The same @a requirements object can be passed to many resource allocations + * (with or without modifications) and can be deleted immediately, without waiting + * for the ResourceContainer object destruction. + * + * Use @c dResourceContainerDestroy to delete the object and release the resources + * when they are no longer needed. + * + * @param requirements The ResourceRequirements object to allocate resources according to + * @returns A ResourceContainer object instance with the resources allocated or NULL if allocation fails + * @ingroup coop + * @see dResourceContainerDestroy + * @see dResourceRequirementsCreate + */ +ODE_API dResourceContainerID dResourceContainerAcquire(/*const */dResourceRequirementsID requirements); + + /** + * @brief Destroys ResourceContainer object and releases resources allocated in it. + * + * @param resources A ResourceContainer object to be deleted (NULL is allowed) + * @ingroup coop + * @see dResourceContainerAcquire + */ +ODE_API void dResourceContainerDestroy(dResourceContainerID resources); + + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // #ifndef _ODE_COOPERATIVE_H_ diff --git a/thirdparty/ode-0.16.5/include/ode/error.h b/thirdparty/ode-0.16.5/include/ode/error.h new file mode 100644 index 0000000..ae511e7 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/error.h @@ -0,0 +1,63 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source */ + +#ifndef _ODE_ERROR_H_ +#define _ODE_ERROR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* all user defined error functions have this type. error and debug functions + * should not return. + */ +typedef void dMessageFunction (int errnum, const char *msg, va_list ap); + +/* set a new error, debug or warning handler. if fn is 0, the default handlers + * are used. + */ +ODE_API void dSetErrorHandler (dMessageFunction *fn); +ODE_API void dSetDebugHandler (dMessageFunction *fn); +ODE_API void dSetMessageHandler (dMessageFunction *fn); + +/* return the current error, debug or warning handler. if the return value is + * 0, the default handlers are in place. + */ +ODE_API dMessageFunction *dGetErrorHandler(void); +ODE_API dMessageFunction *dGetDebugHandler(void); +ODE_API dMessageFunction *dGetMessageHandler(void); + +/* generate a fatal error, debug trap or a message. */ +ODE_API void ODE_NORETURN dError (int num, const char *msg, ...); +ODE_API void ODE_NORETURN dDebug (int num, const char *msg, ...); +ODE_API void dMessage (int num, const char *msg, ...); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/export-dif.h b/thirdparty/ode-0.16.5/include/ode/export-dif.h new file mode 100644 index 0000000..f6578ac --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/export-dif.h @@ -0,0 +1,40 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_EXPORT_DIF_ +#define _ODE_EXPORT_DIF_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +ODE_API void dWorldExportDIF (dWorldID w, FILE *file, const char *world_name); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/mass.h b/thirdparty/ode-0.16.5/include/ode/mass.h new file mode 100644 index 0000000..798c3cf --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/mass.h @@ -0,0 +1,144 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_MASS_H_ +#define _ODE_MASS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct dMass; +typedef struct dMass dMass; + +/** + * Check if a mass structure has valid value. + * The function check if the mass and inertia matrix are positive definits + * + * @param m A mass structure to check + * + * @return 1 if both codition are met + */ +ODE_API int dMassCheck(const dMass *m); + +ODE_API void dMassSetZero (dMass *); + +ODE_API void dMassSetParameters (dMass *, dReal themass, + dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23); + +ODE_API void dMassSetSphere (dMass *, dReal density, dReal radius); +ODE_API void dMassSetSphereTotal (dMass *, dReal total_mass, dReal radius); + +ODE_API void dMassSetCapsule (dMass *, dReal density, int direction, + dReal radius, dReal length); +ODE_API void dMassSetCapsuleTotal (dMass *, dReal total_mass, int direction, + dReal radius, dReal length); + +ODE_API void dMassSetCylinder (dMass *, dReal density, int direction, + dReal radius, dReal length); +ODE_API void dMassSetCylinderTotal (dMass *, dReal total_mass, int direction, + dReal radius, dReal length); + +ODE_API void dMassSetBox (dMass *, dReal density, + dReal lx, dReal ly, dReal lz); +ODE_API void dMassSetBoxTotal (dMass *, dReal total_mass, + dReal lx, dReal ly, dReal lz); + +ODE_API void dMassSetTrimesh (dMass *, dReal density, dGeomID g); + +ODE_API void dMassSetTrimeshTotal (dMass *m, dReal total_mass, dGeomID g); + +ODE_API void dMassAdjust (dMass *, dReal newmass); + +ODE_API void dMassTranslate (dMass *, dReal x, dReal y, dReal z); + +ODE_API void dMassRotate (dMass *, const dMatrix3 R); + +ODE_API void dMassAdd (dMass *a, const dMass *b); + + +/* Backwards compatible API */ +ODE_API_DEPRECATED ODE_API void dMassSetCappedCylinder(dMass *a, dReal b, int c, dReal d, dReal e); +ODE_API_DEPRECATED ODE_API void dMassSetCappedCylinderTotal(dMass *a, dReal b, int c, dReal d, dReal e); + + +struct dMass { + dReal mass; + dVector3 c; + dMatrix3 I; + +#ifdef __cplusplus + dMass() + { dMassSetZero (this); } + void setZero() + { dMassSetZero (this); } + void setParameters (dReal themass, dReal cgx, dReal cgy, dReal cgz, + dReal I11, dReal I22, dReal I33, + dReal I12, dReal I13, dReal I23) + { dMassSetParameters (this,themass,cgx,cgy,cgz,I11,I22,I33,I12,I13,I23); } + + void setSphere (dReal density, dReal radius) + { dMassSetSphere (this,density,radius); } + void setSphereTotal (dReal total, dReal radius) + { dMassSetSphereTotal (this,total,radius); } + + void setCapsule (dReal density, int direction, dReal radius, dReal length) + { dMassSetCapsule (this,density,direction,radius,length); } + void setCapsuleTotal (dReal total, int direction, dReal radius, dReal length) + { dMassSetCapsule (this,total,direction,radius,length); } + + void setCylinder(dReal density, int direction, dReal radius, dReal length) + { dMassSetCylinder (this,density,direction,radius,length); } + void setCylinderTotal(dReal total, int direction, dReal radius, dReal length) + { dMassSetCylinderTotal (this,total,direction,radius,length); } + + void setBox (dReal density, dReal lx, dReal ly, dReal lz) + { dMassSetBox (this,density,lx,ly,lz); } + void setBoxTotal (dReal total, dReal lx, dReal ly, dReal lz) + { dMassSetBoxTotal (this,total,lx,ly,lz); } + + void setTrimesh(dReal density, dGeomID g) + { dMassSetTrimesh (this, density, g); } + void setTrimeshTotal(dReal total, dGeomID g) + { dMassSetTrimeshTotal (this, total, g); } + + void adjust (dReal newmass) + { dMassAdjust (this,newmass); } + void translate (dReal x, dReal y, dReal z) + { dMassTranslate (this,x,y,z); } + void rotate (const dMatrix3 R) + { dMassRotate (this,R); } + void add (const dMass *b) + { dMassAdd (this,b); } +#endif +}; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/matrix.h b/thirdparty/ode-0.16.5/include/ode/matrix.h new file mode 100644 index 0000000..65939b4 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/matrix.h @@ -0,0 +1,200 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* optimized and unoptimized vector and matrix functions */ + +#ifndef _ODE_MATRIX_H_ +#define _ODE_MATRIX_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* set a vector/matrix of size n to all zeros, or to a specific value. */ + +ODE_API void dSetZero (dReal *a, int n); +ODE_API void dSetValue (dReal *a, int n, dReal value); + + +/* get the dot product of two n*1 vectors. if n <= 0 then + * zero will be returned (in which case a and b need not be valid). + */ + +ODE_API dReal dDot (const dReal *a, const dReal *b, int n); + + +/* get the dot products of (a0,b), (a1,b), etc and return them in outsum. + * all vectors are n*1. if n <= 0 then zeroes will be returned (in which case + * the input vectors need not be valid). this function is somewhat faster + * than calling dDot() for all of the combinations separately. + */ + +/* NOT INCLUDED in the library for now. +void dMultidot2 (const dReal *a0, const dReal *a1, + const dReal *b, dReal *outsum, int n); +*/ + + +/* matrix multiplication. all matrices are stored in standard row format. + * the digit refers to the argument that is transposed: + * 0: A = B * C (sizes: A:p*r B:p*q C:q*r) + * 1: A = B' * C (sizes: A:p*r B:q*p C:q*r) + * 2: A = B * C' (sizes: A:p*r B:p*q C:r*q) + * case 1,2 are equivalent to saying that the operation is A=B*C but + * B or C are stored in standard column format. + */ + +ODE_API void dMultiply0 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); +ODE_API void dMultiply1 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); +ODE_API void dMultiply2 (dReal *A, const dReal *B, const dReal *C, int p,int q,int r); + + +/* do an in-place cholesky decomposition on the lower triangle of the n*n + * symmetric matrix A (which is stored by rows). the resulting lower triangle + * will be such that L*L'=A. return 1 on success and 0 on failure (on failure + * the matrix is not positive definite). + */ + +ODE_API int dFactorCholesky (dReal *A, int n); + + +/* solve for x: L*L'*x = b, and put the result back into x. + * L is size n*n, b is size n*1. only the lower triangle of L is considered. + */ + +ODE_API void dSolveCholesky (const dReal *L, dReal *b, int n); + + +/* compute the inverse of the n*n positive definite matrix A and put it in + * Ainv. this is not especially fast. this returns 1 on success (A was + * positive definite) or 0 on failure (not PD). + */ + +ODE_API int dInvertPDMatrix (const dReal *A, dReal *Ainv, int n); + + +/* check whether an n*n matrix A is positive definite, return 1/0 (yes/no). + * positive definite means that x'*A*x > 0 for any x. this performs a + * cholesky decomposition of A. if the decomposition fails then the matrix + * is not positive definite. A is stored by rows. A is not altered. + */ + +ODE_API int dIsPositiveDefinite (const dReal *A, int n); + + +/* factorize a matrix A into L*D*L', where L is lower triangular with ones on + * the diagonal, and D is diagonal. + * A is an n*n matrix stored by rows, with a leading dimension of n rounded + * up to 4. L is written into the strict lower triangle of A (the ones are not + * written) and the reciprocal of the diagonal elements of D are written into + * d. + */ +ODE_API void dFactorLDLT (dReal *A, dReal *d, int n, int nskip); + + +/* solve L*x=b, where L is n*n lower triangular with ones on the diagonal, + * and x,b are n*1. b is overwritten with x. + * the leading dimension of L is `nskip'. + */ +ODE_API void dSolveL1 (const dReal *L, dReal *b, int n, int nskip); + + +/* solve L'*x=b, where L is n*n lower triangular with ones on the diagonal, + * and x,b are n*1. b is overwritten with x. + * the leading dimension of L is `nskip'. + */ +ODE_API void dSolveL1T (const dReal *L, dReal *b, int n, int nskip); + + +/* in matlab syntax: a(1:n) = a(1:n) .* d(1:n) + */ + +ODE_API void dScaleVector (dReal *a, const dReal *d, int n); + +/* The function is an alias for @c dScaleVector. + * It has been deprecated because of a wrong naming schema used. + */ +ODE_API_DEPRECATED ODE_API void dVectorScale (dReal *a, const dReal *d, int n); + + +/* given `L', a n*n lower triangular matrix with ones on the diagonal, + * and `d', a n*1 vector of the reciprocal diagonal elements of an n*n matrix + * D, solve L*D*L'*x=b where x,b are n*1. x overwrites b. + * the leading dimension of L is `nskip'. + */ + +ODE_API void dSolveLDLT (const dReal *L, const dReal *d, dReal *b, int n, int nskip); + + +/* given an L*D*L' factorization of an n*n matrix A, return the updated + * factorization L2*D2*L2' of A plus the following "top left" matrix: + * + * [ b a' ] <-- b is a[0] + * [ a 0 ] <-- a is a[1..n-1] + * + * - L has size n*n, its leading dimension is nskip. L is lower triangular + * with ones on the diagonal. only the lower triangle of L is referenced. + * - d has size n. d contains the reciprocal diagonal elements of D. + * - a has size n. + * the result is written into L, except that the left column of L and d[0] + * are not actually modified. see ldltaddTL.m for further comments. + */ +ODE_API void dLDLTAddTL (dReal *L, dReal *d, const dReal *a, int n, int nskip); + + +/* given an L*D*L' factorization of a permuted matrix A, produce a new + * factorization for row and column `r' removed. + * - A has size n1*n1, its leading dimension in nskip. A is symmetric and + * positive definite. only the lower triangle of A is referenced. + * A itself may actually be an array of row pointers. + * - L has size n2*n2, its leading dimension in nskip. L is lower triangular + * with ones on the diagonal. only the lower triangle of L is referenced. + * - d has size n2. d contains the reciprocal diagonal elements of D. + * - p is a permutation vector. it contains n2 indexes into A. each index + * must be in the range 0..n1-1. + * - r is the row/column of L to remove. + * the new L will be written within the old L, i.e. will have the same leading + * dimension. the last row and column of L, and the last element of d, are + * undefined on exit. + * + * a fast O(n^2) algorithm is used. see ldltremove.m for further comments. + */ +ODE_API void dLDLTRemove (dReal **A, const int *p, dReal *L, dReal *d, + int n1, int n2, int r, int nskip); + + +/* given an n*n matrix A (with leading dimension nskip), remove the r'th row + * and column by moving elements. the new matrix will have the same leading + * dimension. the last row and column of A are untouched on exit. + */ +ODE_API void dRemoveRowCol (dReal *A, int n, int nskip, int r); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/matrix_coop.h b/thirdparty/ode-0.16.5/include/ode/matrix_coop.h new file mode 100644 index 0000000..523eeff --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/matrix_coop.h @@ -0,0 +1,291 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_MATRIX_COOP_H_ +#define _ODE_MATRIX_COOP_H_ + + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup matrix_coop Matrix Cooperative Algorithms + * + * Cooperative algorithms operating on matrices and vectors. + * + * @ingroup coop + */ + + +/** + * @brief Estimates resource requirements for a @c dCooperativelyFactorLDLT call + * + * The function updates the contents of @a requirements to also suffice for calling + * @c dCooperativelyFactorLDLT with the given parameters. + * + * Note: The requirements that could have already been in the @a requirements parameter + * are never decreased. + * + * @param requirements The ResourceRequirements object to update + * @param maximalAllowedThreadCount Maximal value of allowedThreadCount parameter that is going to be used + * @param maximalRowCount Maximal value of rowCount parameter that is going to be used + * @ingroup matrix_coop + * @see dCooperativelyFactorLDLT + * @see dResourceRequirementsCreate + */ +ODE_API void dEstimateCooperativelyFactorLDLTResourceRequirements(dResourceRequirementsID requirements, + unsigned maximalAllowedThreadCount, unsigned maximalRowCount); + +/** + * @brief Cooperatively factorizes a matrix `A' into L*D*L' + * + * The function factorizes a matrix `A' into L*D*L', where `L' is lower triangular with ones on + * the diagonal, and `D' is diagonal. + * @a A is a rowCount*rowCount matrix stored by rows, with a leading dimension of @a rowCount rounded + * up at least to 4 elements. `L; is written into the strict lower triangle of @a A + * (the ones are not written) and the reciprocal of the diagonal elements of `D' are written into @a d. + * + * The @a resources must have had been allocated from a ResourceRequirements object + * estimated in @c dEstimateCooperativelyFactorLDLTResourceRequirements. + * + * The operation is performed cooperatively by up to @a allowedThreadCount threads + * from thread pool available in @a resources. The threading must not be simultaneously + * used (via other @c dResourceContainerID instances) in other calls that employ its features. + * + * @param resources The resources allocated for the function + * @param allowedThreadCount Maximum thread count to use (the actual thread count could be less, depending on other parameters) + * @param A The `A' matrix + * @param d The `d' vector + * @param rowCount The row count in @a A and @a d + * @param rowskip The actual number of elements to be added to skip to next row in @a A + * @ingroup matrix_coop + * @see dEstimateCooperativelyFactorLDLTResourceRequirements + * @see dResourceContainerAcquire + * @see dCooperativelySolveLDLT + */ +ODE_API void dCooperativelyFactorLDLT(dResourceContainerID resources, unsigned allowedThreadCount, + dReal *A, dReal *d, unsigned rowCount, unsigned rowSkip); + + +/** + * @brief Estimates resource requirements for a @c dCooperativelySolveLDLT call + * + * The function updates the contents of @a requirements to also suffice for calling + * @c dCooperativelySolveLDLT with the given parameters. + * + * Note: The requirements that could have already been in the @a requirements parameter + * are never decreased. + * + * @param requirements The ResourceRequirements object to update + * @param maximalAllowedThreadCount Maximal value of allowedThreadCount parameter that is going to be used + * @param maximalRowCount Maximal value of rowCount parameter that is going to be used + * @ingroup matrix_coop + * @see dCooperativelySolveLDLT + * @see dResourceRequirementsCreate + */ +ODE_API void dEstimateCooperativelySolveLDLTResourceRequirements(dResourceRequirementsID requirements, + unsigned maximalAllowedThreadCount, unsigned maximalRowCount); + +/** + * @brief Cooperatively solves L*D*L'*x=b + * + * Given `L', a rowCount*rowCount lower triangular matrix with ones on the diagonal, + * and `d', a rowCount*1 vector of the reciprocal diagonal elements of a rowCount*rowCount matrix + * D, the function solves L*D*L'*x=b where `x' and `b' are rowCount*1. + * The leading dimension of @a L is @a rowSkip. The resulting vector `x' overwrites @a b. + * + * The @a resources must have had been allocated from a ResourceRequirements object + * estimated in @c dEstimateCooperativelySolveLDLTResourceRequirements. + * + * The operation is performed cooperatively by up to @a allowedThreadCount threads + * from thread pool available in @a resources. The threading must not be simultaneously + * used (via other @c dResourceContainerID instances) in other calls that employ its features. + * + * @param resources The resources allocated for the function + * @param allowedThreadCount Maximum thread count to use (the actual thread count could be less, depending on other parameters) + * @param L The `L' matrix + * @param d The `d' vector + * @param b The `b' vector; also the result is stored here + * @param rowCount The row count in @a L, @a d and @a b + * @param rowskip The actual number of elements to be added to skip to next row in @a L + * @ingroup matrix_coop + * @see dEstimateCooperativelySolveLDLTResourceRequirements + * @see dResourceContainerAcquire + * @see dCooperativelyFactorLDLT + */ +ODE_API void dCooperativelySolveLDLT(dResourceContainerID resources, unsigned allowedThreadCount, + const dReal *L, const dReal *d, dReal *b, unsigned rowCount, unsigned rowSkip); + + +/** + * @brief Estimates resource requirements for a @c dCooperativelySolveL1Straight call + * + * The function updates the contents of @a requirements to also suffice for calling + * @c dCooperativelySolveL1Straight with the given parameters. + * + * Note: The requirements that could have already been in the @a requirements parameter + * are never decreased. + * + * @param requirements The ResourceRequirements object to update + * @param maximalAllowedThreadCount Maximal value of allowedThreadCount parameter that is going to be used + * @param maximalRowCount Maximal value of rowCount parameter that is going to be used + * @ingroup matrix_coop + * @see dCooperativelySolveL1Straight + * @see dResourceRequirementsCreate + */ +ODE_API void dEstimateCooperativelySolveL1StraightResourceRequirements(dResourceRequirementsID requirements, + unsigned maximalAllowedThreadCount, unsigned maximalRowCount); + +/** + * @brief Cooperatively solves L*x=b + * + * The function solves L*x=b, where `L' is rowCount*rowCount lower triangular with ones on the diagonal, + * and `x', `b' are rowCount*1. The leading dimension of @a L is @a rowSkip. + * @a b is overwritten with `x'. + * + * The @a resources must have had been allocated from a ResourceRequirements object + * estimated in @c dEstimateCooperativelySolveL1StraightResourceRequirements. + * + * The operation is performed cooperatively by up to @a allowedThreadCount threads + * from thread pool available in @a resources. The threading must not be simultaneously + * used (via other @c dResourceContainerID instances) in other calls that employ its features. + * + * @param resources The resources allocated for the function + * @param allowedThreadCount Maximum thread count to use (the actual thread count could be less, depending on other parameters) + * @param L The `L' matrix + * @param b The `b' vector; also the result is stored here + * @param rowCount The row count in @a L and @a b + * @param rowskip The actual number of elements to be added to skip to next row in @a L + * @ingroup matrix_coop + * @see dEstimateCooperativelySolveL1StraightResourceRequirements + * @see dResourceContainerAcquire + * @see dCooperativelyFactorLDLT + */ +ODE_API void dCooperativelySolveL1Straight(dResourceContainerID resources, unsigned allowedThreadCount, + const dReal *L, dReal *b, unsigned rowCount, unsigned rowSkip); + + +/** + * @brief Estimates resource requirements for a @c dCooperativelySolveL1Transposed call + * + * The function updates the contents of @a requirements to also suffice for calling + * @c dCooperativelySolveL1Transposed with the given parameters. + * + * Note: The requirements that could have already been in the @a requirements parameter + * are never decreased. + * + * @param requirements The ResourceRequirements object to update + * @param maximalAllowedThreadCount Maximal value of allowedThreadCount parameter that is going to be used + * @param maximalRowCount Maximal value of rowCount parameter that is going to be used + * @ingroup matrix_coop + * @see dCooperativelySolveL1Transposed + * @see dResourceRequirementsCreate + */ +ODE_API void dEstimateCooperativelySolveL1TransposedResourceRequirements(dResourceRequirementsID requirements, + unsigned maximalAllowedThreadCount, unsigned maximalRowCount); + +/** + * @brief Cooperatively solves L'*x=b + * + * The function solves L'*x=b, where `L' is rowCount*rowCount lower triangular with ones on the diagonal, + * and `x', b are rowCount*1. The leading dimension of @a L is @a rowSkip. + * @a b is overwritten with `x'. + * + * The @a resources must have had been allocated from a ResourceRequirements object + * estimated in @c dEstimateCooperativelySolveL1TransposedResourceRequirements. + * + * The operation is performed cooperatively by up to @a allowedThreadCount threads + * from thread pool available in @a resources. The threading must not be simultaneously + * used (via other @c dResourceContainerID instances) in other calls that employ its features. + * + * @param resources The resources allocated for the function + * @param allowedThreadCount Maximum thread count to use (the actual thread count could be less, depending on other parameters) + * @param L The `L' matrix + * @param b The `b' vector; also the result is stored here + * @param rowCount The row count in @a L and @a b + * @param rowskip The actual number of elements to be added to skip to next row in @a L + * @ingroup matrix_coop + * @see dEstimateCooperativelySolveL1TransposedResourceRequirements + * @see dResourceContainerAcquire + * @see dCooperativelyFactorLDLT + */ +ODE_API void dCooperativelySolveL1Transposed(dResourceContainerID resources, unsigned allowedThreadCount, + const dReal *L, dReal *b, unsigned rowCount, unsigned rowSkip); + + +/** + * @brief Estimates resource requirements for a @c dCooperativelyScaleVector call + * + * The function updates the contents of @a requirements to also suffice for calling + * @c dCooperativelyScaleVector with the given parameters. + * + * Note: The requirements that could have already been in the @a requirements parameter + * are never decreased. + * + * @param requirements The ResourceRequirements object to update + * @param maximalAllowedThreadCount Maximal value of allowedThreadCount parameter that is going to be used + * @param maximalElementCount Maximal value of elementCount parameter that is going to be used + * @ingroup matrix_coop + * @see dCooperativelyScaleVector + * @see dResourceRequirementsCreate + */ +ODE_API void dEstimateCooperativelyScaleVectorResourceRequirements(dResourceRequirementsID requirements, + unsigned maximalAllowedThreadCount, unsigned maximalElementCount); + +/** + * @brief Multiplies elements of one vector by corresponding element of another one + * + * In matlab syntax, the operation performed is: dataVector(1:elementCount) = dataVector(1:elementCount) .* scaleVector(1:elementCount) + * + * The @a resources must have had been allocated from a ResourceRequirements object + * estimated in @c dEstimateCooperativelyScaleVectorResourceRequirements. + * + * The operation is performed cooperatively by up to @a allowedThreadCount threads + * from thread pool available in @a resources. The threading must not be simultaneously + * used (via other @c dResourceContainerID instances) in other calls that employ its features. + * + * @param resources The resources allocated for the function + * @param allowedThreadCount Maximum thread count to use (the actual thread count could be less, depending on other parameters) + * @param dataVector The vector to be scaled in place + * @param scaleVector The scale vector + * @param elementCount The number of elements in @a dataVector and @a scaleVector + * @ingroup matrix_coop + * @see dEstimateCooperativelyScaleVectorResourceRequirements + * @see dResourceContainerAcquire + * @see dCooperativelyFactorLDLT + */ +ODE_API void dCooperativelyScaleVector(dResourceContainerID resources, unsigned allowedThreadCount, + dReal *dataVector, const dReal *scaleVector, unsigned elementCount); + + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // #ifndef _ODE_MATRIX_COOP_H_ diff --git a/thirdparty/ode-0.16.5/include/ode/memory.h b/thirdparty/ode-0.16.5/include/ode/memory.h new file mode 100644 index 0000000..8e49202 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/memory.h @@ -0,0 +1,59 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* this comes from the `reuse' library. copy any changes back to the source */ + +#ifndef _ODE_MEMORY_H_ +#define _ODE_MEMORY_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* function types to allocate and free memory */ +typedef void * dAllocFunction (dsizeint size); +typedef void * dReallocFunction (void *ptr, dsizeint oldsize, dsizeint newsize); +typedef void dFreeFunction (void *ptr, dsizeint size); + +/* set new memory management functions. if fn is 0, the default handlers are + * used. */ +ODE_API void dSetAllocHandler (dAllocFunction *fn); +ODE_API void dSetReallocHandler (dReallocFunction *fn); +ODE_API void dSetFreeHandler (dFreeFunction *fn); + +/* get current memory management functions */ +ODE_API dAllocFunction *dGetAllocHandler (void); +ODE_API dReallocFunction *dGetReallocHandler (void); +ODE_API dFreeFunction *dGetFreeHandler (void); + +/* allocate and free memory. */ +ODE_API void * dAlloc (dsizeint size); +ODE_API void * dRealloc (void *ptr, dsizeint oldsize, dsizeint newsize); +ODE_API void dFree (void *ptr, dsizeint size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/misc.h b/thirdparty/ode-0.16.5/include/ode/misc.h new file mode 100644 index 0000000..01655ea --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/misc.h @@ -0,0 +1,86 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* miscellaneous math functions. these are mostly useful for testing */ + +#ifndef _ODE_MISC_H_ +#define _ODE_MISC_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* return 1 if the random number generator is working. */ +ODE_API int dTestRand(void); + +/* return next 32 bit random number. this uses a not-very-random linear + * congruential method. + */ +ODE_API unsigned long dRand(void); + +/* get and set the current random number seed. */ +ODE_API unsigned long dRandGetSeed(void); +ODE_API void dRandSetSeed (unsigned long s); + +/* return a random integer between 0..n-1. the distribution will get worse + * as n approaches 2^32. + */ +ODE_API int dRandInt (int n); + +/* return a random real number between 0..1 */ +ODE_API dReal dRandReal(void); + +/* print out a matrix */ +ODE_API void dPrintMatrix (const dReal *A, int n, int m, const char *fmt, FILE *f); + +/* make a random vector with entries between +/- range. A has n elements. */ +ODE_API void dMakeRandomVector (dReal *A, int n, dReal range); + +/* make a random matrix with entries between +/- range. A has size n*m. */ +ODE_API void dMakeRandomMatrix (dReal *A, int n, int m, dReal range); + +/* clear the upper triangle of a square matrix */ +ODE_API void dClearUpperTriangle (dReal *A, int n); + +/* return the maximum element difference between the two n*m matrices */ +ODE_API dReal dMaxDifference (const dReal *A, const dReal *B, int n, int m); + +/* return the maximum element difference between the lower triangle of two + * n*n matrices */ +ODE_API dReal dMaxDifferenceLowerTriangle (const dReal *A, const dReal *B, int n); + + +#ifdef __cplusplus +} +#endif + + +#ifdef __cplusplus +static inline void dPrintMatrix (const dReal *A, int n, int m, const char *fmt="%10.4f ") { dPrintMatrix(A, n, m, fmt, stdout); } +#endif + + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/objects.h b/thirdparty/ode-0.16.5/include/ode/objects.h new file mode 100644 index 0000000..274e2de --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/objects.h @@ -0,0 +1,3398 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_OBJECTS_H_ +#define _ODE_OBJECTS_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup world World + * + * The world object is a container for rigid bodies and joints. Objects in + * different worlds can not interact, for example rigid bodies from two + * different worlds can not collide. + * + * All the objects in a world exist at the same point in time, thus one + * reason to use separate worlds is to simulate systems at different rates. + * Most applications will only need one world. + */ + +/** + * @brief Create a new, empty world and return its ID number. + * @return an identifier + * @ingroup world + */ +ODE_API dWorldID dWorldCreate(void); + + +/** + * @brief Destroy a world and everything in it. + * + * This includes all bodies, and all joints that are not part of a joint + * group. Joints that are part of a joint group will be deactivated, and + * can be destroyed by calling, for example, dJointGroupEmpty(). + * @ingroup world + * @param world the identifier for the world the be destroyed. + */ +ODE_API void dWorldDestroy (dWorldID world); + + +/** + * @brief Set the user-data pointer + * @param world the world to set the data on + * @param data + * @ingroup world + */ +ODE_API void dWorldSetData (dWorldID world, void* data); + + +/** + * @brief Get the user-data pointer + * @param world the world to set the data on + * @param data + * @ingroup world + */ +ODE_API void* dWorldGetData (dWorldID world); + + +/** + * @brief Set the world's global gravity vector. + * + * The units are m/s^2, so Earth's gravity vector would be (0,0,-9.81), + * assuming that +z is up. The default is no gravity, i.e. (0,0,0). + * + * @ingroup world + */ +ODE_API void dWorldSetGravity (dWorldID, dReal x, dReal y, dReal z); + + +/** + * @brief Get the gravity vector for a given world. + * @ingroup world + */ +ODE_API void dWorldGetGravity (dWorldID, dVector3 gravity); + + +/** + * @brief Set the global ERP value, that controls how much error + * correction is performed in each time step. + * @ingroup world + * @param dWorldID the identifier of the world. + * @param erp Typical values are in the range 0.1--0.8. The default is 0.2. + */ +ODE_API void dWorldSetERP (dWorldID, dReal erp); + +/** + * @brief Get the error reduction parameter. + * @ingroup world + * @return ERP value + */ +ODE_API dReal dWorldGetERP (dWorldID); + + +/** + * @brief Set the global CFM (constraint force mixing) value. + * @ingroup world + * @param cfm Typical values are in the range @m{10^{-9}} -- 1. + * The default is 10^-5 if single precision is being used, or 10^-10 + * if double precision is being used. + */ +ODE_API void dWorldSetCFM (dWorldID, dReal cfm); + +/** + * @brief Get the constraint force mixing value. + * @ingroup world + * @return CFM value + */ +ODE_API dReal dWorldGetCFM (dWorldID); + + +#define dWORLDSTEP_THREADCOUNT_UNLIMITED dTHREADING_THREAD_COUNT_UNLIMITED + +/** + * @brief Set maximum threads to be used for island stepping + * + * The actual number of threads that is going to be used will be the minimum + * of this limit and number of threads in the threading pool. By default + * there is no limit (@c dWORLDSTEP_THREADCOUNT_UNLIMITED). + * + * @warning + * WARNING! Running island stepping in multiple threads requires allocating + * individual stepping memory buffer for each of those threads. The size of buffers + * allocated is the size needed to handle the largest island in the world. + * + * Note: Setting a limit for island stepping does not affect threading at lower + * levels in stepper functions. The sub-calls scheduled from them can be executed + * in as many threads as there are available in the pool. + * + * @param w The world affected + * @param count Thread count limit value for island stepping + * @ingroup world + * @see dWorldGetStepIslandsProcessingMaxThreadCount + */ +ODE_API void dWorldSetStepIslandsProcessingMaxThreadCount(dWorldID w, unsigned count); +/** + * @brief Get maximum threads that are allowed to be used for island stepping. + * + * Please read commentaries to @c dWorldSetStepIslandsProcessingMaxThreadCount for + * important information regarding the value returned. + * + * @param w The world queried + * @returns Current thread count limit value for island stepping + * @ingroup world + * @see dWorldSetStepIslandsProcessingMaxThreadCount + */ +ODE_API unsigned dWorldGetStepIslandsProcessingMaxThreadCount(dWorldID w); + +/** + * @brief Set the world to use shared working memory along with another world. + * + * The worlds allocate working memory internally for simulation stepping. This + * memory is cached among the calls to @c dWordStep and @c dWorldQuickStep. + * Similarly, several worlds can be set up to share this memory caches thus + * reducing overall memory usage by cost of making worlds inappropriate for + * simultaneous simulation in multiple threads. + * + * If null value is passed for @a from_world parameter the world is detached from + * sharing and returns to defaults for working memory, reservation policy and + * memory manager as if just created. This can also be used to enable use of shared + * memory for a world that has already had working memory allocated privately. + * Normally using shared memory after a world has its private working memory allocated + * is prohibited. + * + * Allocation policy used can only increase world's internal reserved memory size + * and never decreases it. @c dWorldCleanupWorkingMemory can be used to release + * working memory for a world in case if number of objects/joint decreases + * significantly in it. + * + * With sharing working memory worlds also automatically share memory reservation + * policy and memory manager. Thus, these parameters need to be customized for + * initial world to be used as sharing source only. + * + * If worlds share working memory they must also use compatible threading implementations + * (i.e. it is illegal for one world to perform stepping with self-threaded implementation + * when the other world is assigned a multi-threaded implementation). + * For more information read section about threading approaches in ODE. + * + * Failure result status means a memory allocation failure. + * + * @param w The world to use the shared memory with. + * @param from_world Null or the world the shared memory is to be used from. + * @returns 1 for success and 0 for failure. + * + * @ingroup world + * @see dWorldCleanupWorkingMemory + * @see dWorldSetStepMemoryReservationPolicy + * @see dWorldSetStepMemoryManager + */ +ODE_API int dWorldUseSharedWorkingMemory(dWorldID w, dWorldID from_world/*=NULL*/); + +/** + * @brief Release internal working memory allocated for world + * + * The worlds allocate working memory internally for simulation stepping. This + * function can be used to free world's internal memory cache in case if number of + * objects/joints in the world decreases significantly. By default, internal + * allocation policy is used to only increase cache size as necessary and never + * decrease it. + * + * If a world shares its working memory with other worlds the cache deletion + * affects all the linked worlds. However the shared status itself remains intact. + * + * The function call does affect neither memory reservation policy nor memory manager. + * + * @param w The world to release working memory for. + * + * @ingroup world + * @see dWorldUseSharedWorkingMemory + * @see dWorldSetStepMemoryReservationPolicy + * @see dWorldSetStepMemoryManager + */ +ODE_API void dWorldCleanupWorkingMemory(dWorldID w); + + +#define dWORLDSTEP_RESERVEFACTOR_DEFAULT 1.2f +#define dWORLDSTEP_RESERVESIZE_DEFAULT 65536U + +/** + * @struct dWorldStepReserveInfo + * @brief Memory reservation policy descriptor structure for world stepping functions. + * + * @c struct_size should be assigned the size of the structure. + * + * @c reserve_factor is a quotient that is multiplied by required memory size + * to allocate extra reserve whenever reallocation is needed. + * + * @c reserve_minimum is a minimum size that is checked against whenever reallocation + * is needed to allocate expected working memory minimum at once without extra + * reallocations as number of bodies/joints grows. + * + * @ingroup world + * @see dWorldSetStepMemoryReservationPolicy + */ +typedef struct +{ + unsigned struct_size; + float reserve_factor; /* Use float as precision does not matter here*/ + unsigned reserve_minimum; + +} dWorldStepReserveInfo; + +/** + * @brief Set memory reservation policy for world to be used with simulation stepping functions + * + * The function allows to customize reservation policy to be used for internal + * memory which is allocated to aid simulation for a world. By default, values + * of @c dWORLDSTEP_RESERVEFACTOR_DEFAULT and @c dWORLDSTEP_RESERVESIZE_DEFAULT + * are used. + * + * Passing @a policyinfo argument as NULL results in reservation policy being + * reset to defaults as if the world has been just created. The content of + * @a policyinfo structure is copied internally and does not need to remain valid + * after the call returns. + * + * If the world uses working memory sharing, changing memory reservation policy + * affects all the worlds linked together. + * + * Failure result status means a memory allocation failure. + * + * @param w The world to change memory reservation policy for. + * @param policyinfo Null or a pointer to policy descriptor structure. + * @returns 1 for success and 0 for failure. + * + * @ingroup world + * @see dWorldUseSharedWorkingMemory + */ +ODE_API int dWorldSetStepMemoryReservationPolicy(dWorldID w, const dWorldStepReserveInfo *policyinfo/*=NULL*/); + +/** +* @struct dWorldStepMemoryFunctionsInfo +* @brief World stepping memory manager descriptor structure +* +* This structure is intended to define the functions of memory manager to be used +* with world stepping functions. +* +* @c struct_size should be assigned the size of the structure +* +* @c alloc_block is a function to allocate memory block of given size. +* +* @c shrink_block is a function to shrink existing memory block to a smaller size. +* It must preserve the contents of block head while shrinking. The new block size +* is guaranteed to be always less than the existing one. +* +* @c free_block is a function to delete existing memory block. +* +* @ingroup init +* @see dWorldSetStepMemoryManager +*/ +typedef struct +{ + unsigned struct_size; + void *(*alloc_block)(dsizeint block_size); + void *(*shrink_block)(void *block_pointer, dsizeint block_current_size, dsizeint block_smaller_size); + void (*free_block)(void *block_pointer, dsizeint block_current_size); + +} dWorldStepMemoryFunctionsInfo; + +/** +* @brief Set memory manager for world to be used with simulation stepping functions +* +* The function allows to customize memory manager to be used for internal +* memory allocation during simulation for a world. By default, @c dAlloc/@c dRealloc/@c dFree +* based memory manager is used. +* +* Passing @a memfuncs argument as NULL results in memory manager being +* reset to default one as if the world has been just created. The content of +* @a memfuncs structure is copied internally and does not need to remain valid +* after the call returns. +* +* If the world uses working memory sharing, changing memory manager +* affects all the worlds linked together. +* +* Failure result status means a memory allocation failure. +* +* @param w The world to change memory reservation policy for. +* @param memfuncs Null or a pointer to memory manager descriptor structure. +* @returns 1 for success and 0 for failure. +* +* @ingroup world +* @see dWorldUseSharedWorkingMemory +*/ +ODE_API int dWorldSetStepMemoryManager(dWorldID w, const dWorldStepMemoryFunctionsInfo *memfuncs); + +/** + * @brief Assign threading implementation to be used for [quick]stepping the world. + * + * @warning It is not recommended to assign the same threading implementation to + * different worlds if they are going to be called in parallel. In particular this + * makes resources preallocation for threaded calls to lose its sense. + * Built-in threading implementation is likely to crash if misused this way. + * + * @param w The world to change threading implementation for. + * @param functions_info Pointer to threading functions structure + * @param threading_impl ID of threading implementation object + * + * @ingroup world + */ +ODE_API void dWorldSetStepThreadingImplementation(dWorldID w, const dThreadingFunctionsInfo *functions_info, dThreadingImplementationID threading_impl); + +/** + * @brief Step the world. + * + * This uses a "big matrix" method that takes time on the order of m^3 + * and memory on the order of m^2, where m is the total number of constraint + * rows. For large systems this will use a lot of memory and can be very slow, + * but this is currently the most accurate method. + * + * Failure result status means that the memory allocation has failed for operation. + * In such a case all the objects remain in unchanged state and simulation can be + * retried as soon as more memory is available. + * + * @param w The world to be stepped + * @param stepsize The number of seconds that the simulation has to advance. + * @returns 1 for success and 0 for failure + * + * @ingroup world + */ +ODE_API int dWorldStep (dWorldID w, dReal stepsize); + +/** + * @brief Quick-step the world. + * + * This uses an iterative method that takes time on the order of m*N + * and memory on the order of m, where m is the total number of constraint + * rows N is the number of iterations. + * For large systems this is a lot faster than dWorldStep(), + * but it is less accurate. + * + * QuickStep is great for stacks of objects especially when the + * auto-disable feature is used as well. + * However, it has poor accuracy for near-singular systems. + * Near-singular systems can occur when using high-friction contacts, motors, + * or certain articulated structures. For example, a robot with multiple legs + * sitting on the ground may be near-singular. + * + * There are ways to help overcome QuickStep's inaccuracy problems: + * + * \li Increase CFM. + * \li Reduce the number of contacts in your system (e.g. use the minimum + * number of contacts for the feet of a robot or creature). + * \li Don't use excessive friction in the contacts. + * \li Use contact slip if appropriate + * \li Avoid kinematic loops (however, kinematic loops are inevitable in + * legged creatures). + * \li Don't use excessive motor strength. + * \liUse force-based motors instead of velocity-based motors. + * + * Increasing the number of QuickStep iterations may help a little bit, but + * it is not going to help much if your system is really near singular. + * + * Failure result status means that the memory allocation has failed for operation. + * In such a case all the objects remain in unchanged state and simulation can be + * retried as soon as more memory is available. + * + * @param w The world to be stepped + * @param stepsize The number of seconds that the simulation has to advance. + * @returns 1 for success and 0 for failure + * + * @ingroup world + */ +ODE_API int dWorldQuickStep (dWorldID w, dReal stepsize); + + +/** +* @brief Converts an impulse to a force. +* @ingroup world +* @remarks +* If you want to apply a linear or angular impulse to a rigid body, +* instead of a force or a torque, then you can use this function to convert +* the desired impulse into a force/torque vector before calling the +* BodyAdd... function. +* The current algorithm simply scales the impulse by 1/stepsize, +* where stepsize is the step size for the next step that will be taken. +* This function is given a dWorldID because, in the future, the force +* computation may depend on integrator parameters that are set as +* properties of the world. +*/ +ODE_API void dWorldImpulseToForce +( + dWorldID, dReal stepsize, + dReal ix, dReal iy, dReal iz, dVector3 force + ); + + +/** + * @brief Set the number of iterations that the QuickStep method performs per + * step. + * @ingroup world + * @remarks + * More iterations will give a more accurate solution, but will take + * longer to compute. + * @param num The default is 20 iterations. + */ +ODE_API void dWorldSetQuickStepNumIterations (dWorldID, int num); + + +/** + * @brief Get the number of iterations that the QuickStep method performs per + * step. + * @ingroup world + * @return nr of iterations + */ +ODE_API int dWorldGetQuickStepNumIterations (dWorldID); + +/** + * @brief Set the SOR over-relaxation parameter + * @ingroup world + * @param over_relaxation value to use by SOR + */ +ODE_API void dWorldSetQuickStepW (dWorldID, dReal over_relaxation); + +/** + * @brief Get the SOR over-relaxation parameter + * @ingroup world + * @returns the over-relaxation setting + */ +ODE_API dReal dWorldGetQuickStepW (dWorldID); + +/* World contact parameter functions */ + +/** + * @brief Set the maximum correcting velocity that contacts are allowed + * to generate. + * @ingroup world + * @param vel The default value is infinity (i.e. no limit). + * @remarks + * Reducing this value can help prevent "popping" of deeply embedded objects. + */ +ODE_API void dWorldSetContactMaxCorrectingVel (dWorldID, dReal vel); + +/** + * @brief Get the maximum correcting velocity that contacts are allowed + * to generated. + * @ingroup world + */ +ODE_API dReal dWorldGetContactMaxCorrectingVel (dWorldID); + +/** + * @brief Set the depth of the surface layer around all geometry objects. + * @ingroup world + * @remarks + * Contacts are allowed to sink into the surface layer up to the given + * depth before coming to rest. + * @param depth The default value is zero. + * @remarks + * Increasing this to some small value (e.g. 0.001) can help prevent + * jittering problems due to contacts being repeatedly made and broken. + */ +ODE_API void dWorldSetContactSurfaceLayer (dWorldID, dReal depth); + +/** + * @brief Get the depth of the surface layer around all geometry objects. + * @ingroup world + * @returns the depth + */ +ODE_API dReal dWorldGetContactSurfaceLayer (dWorldID); + + +/** + * @defgroup disable Automatic Enabling and Disabling + * @ingroup world bodies + * + * Every body can be enabled or disabled. Enabled bodies participate in the + * simulation, while disabled bodies are turned off and do not get updated + * during a simulation step. New bodies are always created in the enabled state. + * + * A disabled body that is connected through a joint to an enabled body will be + * automatically re-enabled at the next simulation step. + * + * Disabled bodies do not consume CPU time, therefore to speed up the simulation + * bodies should be disabled when they come to rest. This can be done automatically + * with the auto-disable feature. + * + * If a body has its auto-disable flag turned on, it will automatically disable + * itself when + * @li It has been idle for a given number of simulation steps. + * @li It has also been idle for a given amount of simulation time. + * + * A body is considered to be idle when the magnitudes of both its + * linear average velocity and angular average velocity are below given thresholds. + * The sample size for the average defaults to one and can be disabled by setting + * to zero with @c dWorldSetAutoDisableAverageSamplesCount + * + * Thus, every body has six auto-disable parameters: enabled flag, idle step + * count, idle time, linear/angular average velocity thresholds and + * average samples count. + * + * Newly created bodies get these parameters from world. + */ + +/** + * @brief Get auto disable linear average threshold for newly created bodies. + * @ingroup disable + * @return the threshold + */ +ODE_API dReal dWorldGetAutoDisableLinearThreshold (dWorldID); + +/** + * @brief Set auto disable linear average threshold for newly created bodies. + * @param linear_average_threshold default is 0.01 + * @ingroup disable + */ +ODE_API void dWorldSetAutoDisableLinearThreshold (dWorldID, dReal linear_average_threshold); + +/** + * @brief Get auto disable angular average threshold for newly created bodies. + * @ingroup disable + * @return the threshold + */ +ODE_API dReal dWorldGetAutoDisableAngularThreshold (dWorldID); + +/** + * @brief Set auto disable angular average threshold for newly created bodies. + * @param linear_average_threshold default is 0.01 + * @ingroup disable + */ +ODE_API void dWorldSetAutoDisableAngularThreshold (dWorldID, dReal angular_average_threshold); + +/** + * @brief Get auto disable sample count for newly created bodies. + * @ingroup disable + * @return number of samples used + */ +ODE_API int dWorldGetAutoDisableAverageSamplesCount (dWorldID); + +/** + * @brief Set auto disable average sample count for newly created bodies. + * @ingroup disable + * @param average_samples_count Default is 1, meaning only instantaneous velocity is used. + * Set to zero to disable sampling and thus prevent any body from auto-disabling. + */ +ODE_API void dWorldSetAutoDisableAverageSamplesCount (dWorldID, unsigned int average_samples_count ); + +/** + * @brief Get auto disable steps for newly created bodies. + * @ingroup disable + * @return nr of steps + */ +ODE_API int dWorldGetAutoDisableSteps (dWorldID); + +/** + * @brief Set auto disable steps for newly created bodies. + * @ingroup disable + * @param steps default is 10 + */ +ODE_API void dWorldSetAutoDisableSteps (dWorldID, int steps); + +/** + * @brief Get auto disable time for newly created bodies. + * @ingroup disable + * @return nr of seconds + */ +ODE_API dReal dWorldGetAutoDisableTime (dWorldID); + +/** + * @brief Set auto disable time for newly created bodies. + * @ingroup disable + * @param time default is 0 seconds + */ +ODE_API void dWorldSetAutoDisableTime (dWorldID, dReal time); + +/** + * @brief Get auto disable flag for newly created bodies. + * @ingroup disable + * @return 0 or 1 + */ +ODE_API int dWorldGetAutoDisableFlag (dWorldID); + +/** + * @brief Set auto disable flag for newly created bodies. + * @ingroup disable + * @param do_auto_disable default is false. + */ +ODE_API void dWorldSetAutoDisableFlag (dWorldID, int do_auto_disable); + + +/** + * @defgroup damping Damping + * @ingroup bodies world + * + * Damping serves two purposes: reduce simulation instability, and to allow + * the bodies to come to rest (and possibly auto-disabling them). + * + * Bodies are constructed using the world's current damping parameters. Setting + * the scales to 0 disables the damping. + * + * Here is how it is done: after every time step linear and angular + * velocities are tested against the corresponding thresholds. If they + * are above, they are multiplied by (1 - scale). So a negative scale value + * will actually increase the speed, and values greater than one will + * make the object oscillate every step; both can make the simulation unstable. + * + * To disable damping just set the damping scale to zero. + * + * You can also limit the maximum angular velocity. In contrast to the damping + * functions, the angular velocity is affected before the body is moved. + * This means that it will introduce errors in joints that are forcing the body + * to rotate too fast. Some bodies have naturally high angular velocities + * (like cars' wheels), so you may want to give them a very high (like the default, + * dInfinity) limit. + * + * @note The velocities are damped after the stepper function has moved the + * object. Otherwise the damping could introduce errors in joints. First the + * joint constraints are processed by the stepper (moving the body), then + * the damping is applied. + * + * @note The damping happens right after the moved callback is called; this way + * it still possible use the exact velocities the body has acquired during the + * step. You can even use the callback to create your own customized damping. + */ + +/** + * @brief Get the world's linear damping threshold. + * @ingroup damping + */ +ODE_API dReal dWorldGetLinearDampingThreshold (dWorldID w); + +/** + * @brief Set the world's linear damping threshold. + * @param threshold The damping won't be applied if the linear speed is + * below this threshold. Default is 0.01. + * @ingroup damping + */ +ODE_API void dWorldSetLinearDampingThreshold(dWorldID w, dReal threshold); + +/** + * @brief Get the world's angular damping threshold. + * @ingroup damping + */ +ODE_API dReal dWorldGetAngularDampingThreshold (dWorldID w); + +/** + * @brief Set the world's angular damping threshold. + * @param threshold The damping won't be applied if the angular speed is + * below this threshold. Default is 0.01. + * @ingroup damping + */ +ODE_API void dWorldSetAngularDampingThreshold(dWorldID w, dReal threshold); + +/** + * @brief Get the world's linear damping scale. + * @ingroup damping + */ +ODE_API dReal dWorldGetLinearDamping (dWorldID w); + +/** + * @brief Set the world's linear damping scale. + * @param scale The linear damping scale that is to be applied to bodies. + * Default is 0 (no damping). Should be in the interval [0, 1]. + * @ingroup damping + */ +ODE_API void dWorldSetLinearDamping (dWorldID w, dReal scale); + +/** + * @brief Get the world's angular damping scale. + * @ingroup damping + */ +ODE_API dReal dWorldGetAngularDamping (dWorldID w); + +/** + * @brief Set the world's angular damping scale. + * @param scale The angular damping scale that is to be applied to bodies. + * Default is 0 (no damping). Should be in the interval [0, 1]. + * @ingroup damping + */ +ODE_API void dWorldSetAngularDamping(dWorldID w, dReal scale); + +/** + * @brief Convenience function to set body linear and angular scales. + * @param linear_scale The linear damping scale that is to be applied to bodies. + * @param angular_scale The angular damping scale that is to be applied to bodies. + * @ingroup damping + */ +ODE_API void dWorldSetDamping(dWorldID w, + dReal linear_scale, + dReal angular_scale); + +/** + * @brief Get the default maximum angular speed. + * @ingroup damping + * @sa dBodyGetMaxAngularSpeed() + */ +ODE_API dReal dWorldGetMaxAngularSpeed (dWorldID w); + + +/** + * @brief Set the default maximum angular speed for new bodies. + * @ingroup damping + * @sa dBodySetMaxAngularSpeed() + */ +ODE_API void dWorldSetMaxAngularSpeed (dWorldID w, dReal max_speed); + + + +/** + * @defgroup bodies Rigid Bodies + * + * A rigid body has various properties from the point of view of the + * simulation. Some properties change over time: + * + * @li Position vector (x,y,z) of the body's point of reference. + * Currently the point of reference must correspond to the body's center of mass. + * @li Linear velocity of the point of reference, a vector (vx,vy,vz). + * @li Orientation of a body, represented by a quaternion (qs,qx,qy,qz) or + * a 3x3 rotation matrix. + * @li Angular velocity vector (wx,wy,wz) which describes how the orientation + * changes over time. + * + * Other body properties are usually constant over time: + * + * @li Mass of the body. + * @li Position of the center of mass with respect to the point of reference. + * In the current implementation the center of mass and the point of + * reference must coincide. + * @li Inertia matrix. This is a 3x3 matrix that describes how the body's mass + * is distributed around the center of mass. Conceptually each body has an + * x-y-z coordinate frame embedded in it that moves and rotates with the body. + * + * The origin of this coordinate frame is the body's point of reference. Some values + * in ODE (vectors, matrices etc) are relative to the body coordinate frame, and others + * are relative to the global coordinate frame. + * + * Note that the shape of a rigid body is not a dynamical property (except insofar as + * it influences the various mass properties). It is only collision detection that cares + * about the detailed shape of the body. + */ + + +/** + * @brief Get auto disable linear average threshold. + * @ingroup bodies disable + * @return the threshold + */ +ODE_API dReal dBodyGetAutoDisableLinearThreshold (dBodyID); + +/** + * @brief Set auto disable linear average threshold. + * @ingroup bodies disable + * @return the threshold + */ +ODE_API void dBodySetAutoDisableLinearThreshold (dBodyID, dReal linear_average_threshold); + +/** + * @brief Get auto disable angular average threshold. + * @ingroup bodies disable + * @return the threshold + */ +ODE_API dReal dBodyGetAutoDisableAngularThreshold (dBodyID); + +/** + * @brief Set auto disable angular average threshold. + * @ingroup bodies disable + * @return the threshold + */ +ODE_API void dBodySetAutoDisableAngularThreshold (dBodyID, dReal angular_average_threshold); + +/** + * @brief Get auto disable average size (samples count). + * @ingroup bodies disable + * @return the nr of steps/size. + */ +ODE_API int dBodyGetAutoDisableAverageSamplesCount (dBodyID); + +/** + * @brief Set auto disable average buffer size (average steps). + * @ingroup bodies disable + * @param average_samples_count the nr of samples to review. + */ +ODE_API void dBodySetAutoDisableAverageSamplesCount (dBodyID, unsigned int average_samples_count); + + +/** + * @brief Get auto steps a body must be thought of as idle to disable + * @ingroup bodies disable + * @return the nr of steps + */ +ODE_API int dBodyGetAutoDisableSteps (dBodyID); + +/** + * @brief Set auto disable steps. + * @ingroup bodies disable + * @param steps the nr of steps. + */ +ODE_API void dBodySetAutoDisableSteps (dBodyID, int steps); + +/** + * @brief Get auto disable time. + * @ingroup bodies disable + * @return nr of seconds + */ +ODE_API dReal dBodyGetAutoDisableTime (dBodyID); + +/** + * @brief Set auto disable time. + * @ingroup bodies disable + * @param time nr of seconds. + */ +ODE_API void dBodySetAutoDisableTime (dBodyID, dReal time); + +/** + * @brief Get auto disable flag. + * @ingroup bodies disable + * @return 0 or 1 + */ +ODE_API int dBodyGetAutoDisableFlag (dBodyID); + +/** + * @brief Set auto disable flag. + * @ingroup bodies disable + * @param do_auto_disable 0 or 1 + */ +ODE_API void dBodySetAutoDisableFlag (dBodyID, int do_auto_disable); + +/** + * @brief Set auto disable defaults. + * @remarks + * Set the values for the body to those set as default for the world. + * @ingroup bodies disable + */ +ODE_API void dBodySetAutoDisableDefaults (dBodyID); + + +/** + * @brief Retrieves the world attached to te given body. + * @remarks + * + * @ingroup bodies + */ +ODE_API dWorldID dBodyGetWorld (dBodyID); + +/** + * @brief Create a body in given world. + * @remarks + * Default mass parameters are at position (0,0,0). + * @ingroup bodies + */ +ODE_API dBodyID dBodyCreate (dWorldID); + +/** + * @brief Destroy a body. + * @remarks + * All joints that are attached to this body will be put into limbo: + * i.e. unattached and not affecting the simulation, but they will NOT be + * deleted. + * @ingroup bodies + */ +ODE_API void dBodyDestroy (dBodyID); + +/** + * @brief Set the body's user-data pointer. + * @ingroup bodies + * @param data arbitraty pointer + */ +ODE_API void dBodySetData (dBodyID, void *data); + +/** + * @brief Get the body's user-data pointer. + * @ingroup bodies + * @return a pointer to the user's data. + */ +ODE_API void *dBodyGetData (dBodyID); + +/** + * @brief Set position of a body. + * @remarks + * After setting, the outcome of the simulation is undefined + * if the new configuration is inconsistent with the joints/constraints + * that are present. + * @ingroup bodies + */ +ODE_API void dBodySetPosition (dBodyID, dReal x, dReal y, dReal z); + +/** + * @brief Set the orientation of a body. + * @ingroup bodies + * @remarks + * After setting, the outcome of the simulation is undefined + * if the new configuration is inconsistent with the joints/constraints + * that are present. + */ +ODE_API void dBodySetRotation (dBodyID, const dMatrix3 R); + +/** + * @brief Set the orientation of a body. + * @ingroup bodies + * @remarks + * After setting, the outcome of the simulation is undefined + * if the new configuration is inconsistent with the joints/constraints + * that are present. + */ +ODE_API void dBodySetQuaternion (dBodyID, const dQuaternion q); + +/** + * @brief Set the linear velocity of a body. + * @ingroup bodies + */ +ODE_API void dBodySetLinearVel (dBodyID, dReal x, dReal y, dReal z); + +/** + * @brief Set the angular velocity of a body. + * @ingroup bodies + */ +ODE_API void dBodySetAngularVel (dBodyID, dReal x, dReal y, dReal z); + +/** + * @brief Get the position of a body. + * @ingroup bodies + * @remarks + * When getting, the returned values are pointers to internal data structures, + * so the vectors are valid until any changes are made to the rigid body + * system structure. + * @sa dBodyCopyPosition + */ +ODE_API const dReal * dBodyGetPosition (dBodyID); + + +/** + * @brief Copy the position of a body into a vector. + * @ingroup bodies + * @param body the body to query + * @param pos a copy of the body position + * @sa dBodyGetPosition + */ +ODE_API void dBodyCopyPosition (dBodyID body, dVector3 pos); + + +/** + * @brief Get the rotation of a body. + * @ingroup bodies + * @return pointer to a 4x3 rotation matrix. + */ +ODE_API const dReal * dBodyGetRotation (dBodyID); + + +/** + * @brief Copy the rotation of a body. + * @ingroup bodies + * @param body the body to query + * @param R a copy of the rotation matrix + * @sa dBodyGetRotation + */ +ODE_API void dBodyCopyRotation (dBodyID, dMatrix3 R); + + +/** + * @brief Get the rotation of a body. + * @ingroup bodies + * @return pointer to 4 scalars that represent the quaternion. + */ +ODE_API const dReal * dBodyGetQuaternion (dBodyID); + + +/** + * @brief Copy the orientation of a body into a quaternion. + * @ingroup bodies + * @param body the body to query + * @param quat a copy of the orientation quaternion + * @sa dBodyGetQuaternion + */ +ODE_API void dBodyCopyQuaternion(dBodyID body, dQuaternion quat); + + +/** + * @brief Get the linear velocity of a body. + * @ingroup bodies + */ +ODE_API const dReal * dBodyGetLinearVel (dBodyID); + +/** + * @brief Get the angular velocity of a body. + * @ingroup bodies + */ +ODE_API const dReal * dBodyGetAngularVel (dBodyID); + +/** + * @brief Set the mass of a body. + * @ingroup bodies + */ +ODE_API void dBodySetMass (dBodyID, const dMass *mass); + +/** + * @brief Get the mass of a body. + * @ingroup bodies + */ +ODE_API void dBodyGetMass (dBodyID, dMass *mass); + +/** + * @brief Add force at centre of mass of body in absolute coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddForce (dBodyID, dReal fx, dReal fy, dReal fz); + +/** + * @brief Add torque at centre of mass of body in absolute coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddTorque (dBodyID, dReal fx, dReal fy, dReal fz); + +/** + * @brief Add force at centre of mass of body in coordinates relative to body. + * @ingroup bodies + */ +ODE_API void dBodyAddRelForce (dBodyID, dReal fx, dReal fy, dReal fz); + +/** + * @brief Add torque at centre of mass of body in coordinates relative to body. + * @ingroup bodies + */ +ODE_API void dBodyAddRelTorque (dBodyID, dReal fx, dReal fy, dReal fz); + +/** + * @brief Add force at specified point in body in global coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +/** + * @brief Add force at specified point in body in local coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +/** + * @brief Add force at specified point in body in global coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddRelForceAtPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); +/** + * @brief Add force at specified point in body in local coordinates. + * @ingroup bodies + */ +ODE_API void dBodyAddRelForceAtRelPos (dBodyID, dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz); + +/** + * @brief Return the current accumulated force vector. + * @return points to an array of 3 reals. + * @remarks + * The returned values are pointers to internal data structures, so + * the vectors are only valid until any changes are made to the rigid + * body system. + * @ingroup bodies + */ +ODE_API const dReal * dBodyGetForce (dBodyID); + +/** + * @brief Return the current accumulated torque vector. + * @return points to an array of 3 reals. + * @remarks + * The returned values are pointers to internal data structures, so + * the vectors are only valid until any changes are made to the rigid + * body system. + * @ingroup bodies + */ +ODE_API const dReal * dBodyGetTorque (dBodyID); + +/** + * @brief Set the body force accumulation vector. + * @remarks + * This is mostly useful to zero the force and torque for deactivated bodies + * before they are reactivated, in the case where the force-adding functions + * were called on them while they were deactivated. + * @ingroup bodies + */ +ODE_API void dBodySetForce (dBodyID b, dReal x, dReal y, dReal z); + +/** + * @brief Set the body torque accumulation vector. + * @remarks + * This is mostly useful to zero the force and torque for deactivated bodies + * before they are reactivated, in the case where the force-adding functions + * were called on them while they were deactivated. + * @ingroup bodies + */ +ODE_API void dBodySetTorque (dBodyID b, dReal x, dReal y, dReal z); + +/** + * @brief Get world position of a relative point on body. + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyGetRelPointPos +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Get velocity vector in global coords of a relative point on body. + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyGetRelPointVel +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Get velocity vector in global coords of a globally + * specified point on a body. + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyGetPointVel +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief takes a point in global coordinates and returns + * the point's position in body-relative coordinates. + * @remarks + * This is the inverse of dBodyGetRelPointPos() + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyGetPosRelPoint +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Convert from local to world coordinates. + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyVectorToWorld +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief Convert from world to local coordinates. + * @ingroup bodies + * @param result will contain the result. + */ +ODE_API void dBodyVectorFromWorld +( + dBodyID, dReal px, dReal py, dReal pz, + dVector3 result +); + +/** + * @brief controls the way a body's orientation is updated at each timestep. + * @ingroup bodies + * @param mode can be 0 or 1: + * \li 0: An ``infinitesimal'' orientation update is used. + * This is fast to compute, but it can occasionally cause inaccuracies + * for bodies that are rotating at high speed, especially when those + * bodies are joined to other bodies. + * This is the default for every new body that is created. + * \li 1: A ``finite'' orientation update is used. + * This is more costly to compute, but will be more accurate for high + * speed rotations. + * @remarks + * Note however that high speed rotations can result in many types of + * error in a simulation, and the finite mode will only fix one of those + * sources of error. + */ +ODE_API void dBodySetFiniteRotationMode (dBodyID, int mode); + +/** + * @brief sets the finite rotation axis for a body. + * @ingroup bodies + * @remarks + * This axis only has meaning when the finite rotation mode is set + * If this axis is zero (0,0,0), full finite rotations are performed on + * the body. + * If this axis is nonzero, the body is rotated by performing a partial finite + * rotation along the axis direction followed by an infinitesimal rotation + * along an orthogonal direction. + * @remarks + * This can be useful to alleviate certain sources of error caused by quickly + * spinning bodies. For example, if a car wheel is rotating at high speed + * you can call this function with the wheel's hinge axis as the argument to + * try and improve its behavior. + */ +ODE_API void dBodySetFiniteRotationAxis (dBodyID, dReal x, dReal y, dReal z); + +/** + * @brief Get the way a body's orientation is updated each timestep. + * @ingroup bodies + * @return the mode 0 (infinitesimal) or 1 (finite). + */ +ODE_API int dBodyGetFiniteRotationMode (dBodyID); + +/** + * @brief Get the finite rotation axis. + * @param result will contain the axis. + * @ingroup bodies + */ +ODE_API void dBodyGetFiniteRotationAxis (dBodyID, dVector3 result); + +/** + * @brief Get the number of joints that are attached to this body. + * @ingroup bodies + * @return nr of joints + */ +ODE_API int dBodyGetNumJoints (dBodyID b); + +/** + * @brief Return a joint attached to this body, given by index. + * @ingroup bodies + * @param index valid range is 0 to n-1 where n is the value returned by + * dBodyGetNumJoints(). + */ +ODE_API dJointID dBodyGetJoint (dBodyID, int index); + + + + +/** + * @brief Set rigid body to dynamic state (default). + * @param dBodyID identification of body. + * @ingroup bodies + */ +ODE_API void dBodySetDynamic (dBodyID); + +/** + * @brief Set rigid body to kinematic state. + * When in kinematic state the body isn't simulated as a dynamic + * body (it's "unstoppable", doesn't respond to forces), + * but can still affect dynamic bodies (e.g. in joints). + * Kinematic bodies can be controlled by position and velocity. + * @note A kinematic body has infinite mass. If you set its mass + * to something else, it loses the kinematic state and behaves + * as a normal dynamic body. + * @param dBodyID identification of body. + * @ingroup bodies + */ +ODE_API void dBodySetKinematic (dBodyID); + +/** + * @brief Check wether a body is in kinematic state. + * @ingroup bodies + * @return 1 if a body is kinematic or 0 if it is dynamic. + */ +ODE_API int dBodyIsKinematic (dBodyID); + +/** + * @brief Manually enable a body. + * @param dBodyID identification of body. + * @ingroup bodies + */ +ODE_API void dBodyEnable (dBodyID); + +/** + * @brief Manually disable a body. + * @ingroup bodies + * @remarks + * A disabled body that is connected through a joint to an enabled body will + * be automatically re-enabled at the next simulation step. + */ +ODE_API void dBodyDisable (dBodyID); + +/** + * @brief Check wether a body is enabled. + * @ingroup bodies + * @return 1 if a body is currently enabled or 0 if it is disabled. + */ +ODE_API int dBodyIsEnabled (dBodyID); + +/** + * @brief Set whether the body is influenced by the world's gravity or not. + * @ingroup bodies + * @param mode when nonzero gravity affects this body. + * @remarks + * Newly created bodies are always influenced by the world's gravity. + */ +ODE_API void dBodySetGravityMode (dBodyID b, int mode); + +/** + * @brief Get whether the body is influenced by the world's gravity or not. + * @ingroup bodies + * @return nonzero means gravity affects this body. + */ +ODE_API int dBodyGetGravityMode (dBodyID b); + +/** + * @brief Set the 'moved' callback of a body. + * + * Whenever a body has its position or rotation changed during the + * timestep, the callback will be called (with body as the argument). + * Use it to know which body may need an update in an external + * structure (like a 3D engine). + * + * @param b the body that needs to be watched. + * @param callback the callback to be invoked when the body moves. Set to zero + * to disable. + * @ingroup bodies + */ +ODE_API void dBodySetMovedCallback(dBodyID b, void (*callback)(dBodyID)); + + +/** + * @brief Return the first geom associated with the body. + * + * You can traverse through the geoms by repeatedly calling + * dBodyGetNextGeom(). + * + * @return the first geom attached to this body, or 0. + * @ingroup bodies + */ +ODE_API dGeomID dBodyGetFirstGeom (dBodyID b); + + +/** + * @brief returns the next geom associated with the same body. + * @param g a geom attached to some body. + * @return the next geom attached to the same body, or 0. + * @sa dBodyGetFirstGeom + * @ingroup bodies + */ +ODE_API dGeomID dBodyGetNextGeom (dGeomID g); + + +/** + * @brief Resets the damping settings to the current world's settings. + * @ingroup bodies damping + */ +ODE_API void dBodySetDampingDefaults(dBodyID b); + +/** + * @brief Get the body's linear damping scale. + * @ingroup bodies damping + */ +ODE_API dReal dBodyGetLinearDamping (dBodyID b); + +/** + * @brief Set the body's linear damping scale. + * @param scale The linear damping scale. Should be in the interval [0, 1]. + * @ingroup bodies damping + * @remarks From now on the body will not use the world's linear damping + * scale until dBodySetDampingDefaults() is called. + * @sa dBodySetDampingDefaults() + */ +ODE_API void dBodySetLinearDamping(dBodyID b, dReal scale); + +/** + * @brief Get the body's angular damping scale. + * @ingroup bodies damping + * @remarks If the body's angular damping scale was not set, this function + * returns the world's angular damping scale. + */ +ODE_API dReal dBodyGetAngularDamping (dBodyID b); + +/** + * @brief Set the body's angular damping scale. + * @param scale The angular damping scale. Should be in the interval [0, 1]. + * @ingroup bodies damping + * @remarks From now on the body will not use the world's angular damping + * scale until dBodyResetAngularDamping() is called. + * @sa dBodyResetAngularDamping() + */ +ODE_API void dBodySetAngularDamping(dBodyID b, dReal scale); + +/** + * @brief Convenience function to set linear and angular scales at once. + * @param linear_scale The linear damping scale. Should be in the interval [0, 1]. + * @param angular_scale The angular damping scale. Should be in the interval [0, 1]. + * @ingroup bodies damping + * @sa dBodySetLinearDamping() dBodySetAngularDamping() + */ +ODE_API void dBodySetDamping(dBodyID b, dReal linear_scale, dReal angular_scale); + +/** + * @brief Get the body's linear damping threshold. + * @ingroup bodies damping + */ +ODE_API dReal dBodyGetLinearDampingThreshold (dBodyID b); + +/** + * @brief Set the body's linear damping threshold. + * @param threshold The linear threshold to be used. Damping + * is only applied if the linear speed is above this limit. + * @ingroup bodies damping + */ +ODE_API void dBodySetLinearDampingThreshold(dBodyID b, dReal threshold); + +/** + * @brief Get the body's angular damping threshold. + * @ingroup bodies damping + */ +ODE_API dReal dBodyGetAngularDampingThreshold (dBodyID b); + +/** + * @brief Set the body's angular damping threshold. + * @param threshold The angular threshold to be used. Damping is + * only used if the angular speed is above this limit. + * @ingroup bodies damping + */ +ODE_API void dBodySetAngularDampingThreshold(dBodyID b, dReal threshold); + +/** + * @brief Get the body's maximum angular speed. + * @ingroup damping bodies + * @sa dWorldGetMaxAngularSpeed() + */ +ODE_API dReal dBodyGetMaxAngularSpeed (dBodyID b); + +/** + * @brief Set the body's maximum angular speed. + * @ingroup damping bodies + * @sa dWorldSetMaxAngularSpeed() dBodyResetMaxAngularSpeed() + * The default value is dInfinity, but it's a good idea to limit + * it at less than 500 if the body has the gyroscopic term + * enabled. + */ +ODE_API void dBodySetMaxAngularSpeed(dBodyID b, dReal max_speed); + + + +/** + * @brief Get the body's gyroscopic state. + * + * @return nonzero if gyroscopic term computation is enabled (default), + * zero otherwise. + * @ingroup bodies + */ +ODE_API int dBodyGetGyroscopicMode(dBodyID b); + + +/** + * @brief Enable/disable the body's gyroscopic term. + * + * Disabling the gyroscopic term of a body usually improves + * stability. It also helps turning spining objects, like cars' + * wheels. + * + * @param enabled nonzero (default) to enable gyroscopic term, 0 + * to disable. + * @ingroup bodies + */ +ODE_API void dBodySetGyroscopicMode(dBodyID b, int enabled); + + + + +/** + * @defgroup joints Joints + * + * In real life a joint is something like a hinge, that is used to connect two + * objects. + * In ODE a joint is very similar: It is a relationship that is enforced between + * two bodies so that they can only have certain positions and orientations + * relative to each other. + * This relationship is called a constraint -- the words joint and + * constraint are often used interchangeably. + * + * A joint has a set of parameters that can be set. These include: + * + * + * \li dParamLoStop Low stop angle or position. Setting this to + * -dInfinity (the default value) turns off the low stop. + * For rotational joints, this stop must be greater than -pi to be + * effective. + * \li dParamHiStop High stop angle or position. Setting this to + * dInfinity (the default value) turns off the high stop. + * For rotational joints, this stop must be less than pi to be + * effective. + * If the high stop is less than the low stop then both stops will + * be ineffective. + * \li dParamVel Desired motor velocity (this will be an angular or + * linear velocity). + * \li dParamFMax The maximum force or torque that the motor will use to + * achieve the desired velocity. + * This must always be greater than or equal to zero. + * Setting this to zero (the default value) turns off the motor. + * \li dParamFudgeFactor The current joint stop/motor implementation has + * a small problem: + * when the joint is at one stop and the motor is set to move it away + * from the stop, too much force may be applied for one time step, + * causing a ``jumping'' motion. + * This fudge factor is used to scale this excess force. + * It should have a value between zero and one (the default value). + * If the jumping motion is too visible in a joint, the value can be + * reduced. + * Making this value too small can prevent the motor from being able to + * move the joint away from a stop. + * \li dParamBounce The bouncyness of the stops. + * This is a restitution parameter in the range 0..1. + * 0 means the stops are not bouncy at all, 1 means maximum bouncyness. + * \li dParamCFM The constraint force mixing (CFM) value used when not + * at a stop. + * \li dParamStopERP The error reduction parameter (ERP) used by the + * stops. + * \li dParamStopCFM The constraint force mixing (CFM) value used by the + * stops. Together with the ERP value this can be used to get spongy or + * soft stops. + * Note that this is intended for unpowered joints, it does not really + * work as expected when a powered joint reaches its limit. + * \li dParamSuspensionERP Suspension error reduction parameter (ERP). + * Currently this is only implemented on the hinge-2 joint. + * \li dParamSuspensionCFM Suspension constraint force mixing (CFM) value. + * Currently this is only implemented on the hinge-2 joint. + * + * If a particular parameter is not implemented by a given joint, setting it + * will have no effect. + * These parameter names can be optionally followed by a digit (2 or 3) + * to indicate the second or third set of parameters, e.g. for the second axis + * in a hinge-2 joint, or the third axis in an AMotor joint. + */ + + +/** + * @brief Create a new joint of the ball type. + * @ingroup joints + * @remarks + * The joint is initially in "limbo" (i.e. it has no effect on the simulation) + * because it does not connect to any bodies. + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateBall (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the hinge type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateHinge (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the slider type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateSlider (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the contact type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateContact (dWorldID, dJointGroupID, const dContact *); + +/** + * @brief Create a new joint of the hinge2 type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateHinge2 (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the universal type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateUniversal (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the PR (Prismatic and Rotoide) type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreatePR (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the PU (Prismatic and Universal) type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreatePU (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the Piston type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given + * joint group. + */ +ODE_API dJointID dJointCreatePiston (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the fixed type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateFixed (dWorldID, dJointGroupID); + +ODE_API dJointID dJointCreateNull (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the A-motor type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateAMotor (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the L-motor type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateLMotor (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the plane-2d type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreatePlane2D (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the double ball type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateDBall (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the double hinge type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateDHinge (dWorldID, dJointGroupID); + +/** + * @brief Create a new joint of the Transmission type. + * @ingroup joints + * @param dJointGroupID set to 0 to allocate the joint normally. + * If it is nonzero the joint is allocated in the given joint group. + */ +ODE_API dJointID dJointCreateTransmission (dWorldID, dJointGroupID); + + +/** + * @brief Destroy a joint. + * @ingroup joints + * + * disconnects it from its attached bodies and removing it from the world. + * However, if the joint is a member of a group then this function has no + * effect - to destroy that joint the group must be emptied or destroyed. + */ +ODE_API void dJointDestroy (dJointID); + + +/** + * @brief Create a joint group + * @ingroup joints + * @param max_size deprecated. Set to 0. + */ +ODE_API dJointGroupID dJointGroupCreate (int max_size); + +/** + * @brief Destroy a joint group. + * @ingroup joints + * + * All joints in the joint group will be destroyed. + */ +ODE_API void dJointGroupDestroy (dJointGroupID); + +/** + * @brief Empty a joint group. + * @ingroup joints + * + * All joints in the joint group will be destroyed, + * but the joint group itself will not be destroyed. + */ +ODE_API void dJointGroupEmpty (dJointGroupID); + +/** + * @brief Return the number of bodies attached to the joint + * @ingroup joints + */ +ODE_API int dJointGetNumBodies(dJointID); + +/** + * @brief Attach the joint to some new bodies. + * @ingroup joints + * + * If the joint is already attached, it will be detached from the old bodies + * first. + * To attach this joint to only one body, set body1 or body2 to zero - a zero + * body refers to the static environment. + * Setting both bodies to zero puts the joint into "limbo", i.e. it will + * have no effect on the simulation. + * @remarks + * Some joints, like hinge-2 need to be attached to two bodies to work. + */ +ODE_API void dJointAttach (dJointID, dBodyID body1, dBodyID body2); + +/** + * @brief Manually enable a joint. + * @param dJointID identification of joint. + * @ingroup joints + */ +ODE_API void dJointEnable (dJointID); + +/** + * @brief Manually disable a joint. + * @ingroup joints + * @remarks + * A disabled joint will not affect the simulation, but will maintain the anchors and + * axes so it can be enabled later. + */ +ODE_API void dJointDisable (dJointID); + +/** + * @brief Check wether a joint is enabled. + * @ingroup joints + * @return 1 if a joint is currently enabled or 0 if it is disabled. + */ +ODE_API int dJointIsEnabled (dJointID); + +/** + * @brief Set the user-data pointer + * @ingroup joints + */ +ODE_API void dJointSetData (dJointID, void *data); + +/** + * @brief Get the user-data pointer + * @ingroup joints + */ +ODE_API void *dJointGetData (dJointID); + +/** + * @brief Get the type of the joint + * @ingroup joints + * @return the type, being one of these: + * \li dJointTypeBall + * \li dJointTypeHinge + * \li dJointTypeSlider + * \li dJointTypeContact + * \li dJointTypeUniversal + * \li dJointTypeHinge2 + * \li dJointTypeFixed + * \li dJointTypeNull + * \li dJointTypeAMotor + * \li dJointTypeLMotor + * \li dJointTypePlane2D + * \li dJointTypePR + * \li dJointTypePU + * \li dJointTypePiston + */ +ODE_API dJointType dJointGetType (dJointID); + +/** + * @brief Return the bodies that this joint connects. + * @ingroup joints + * @param index return the first (0) or second (1) body. + * @remarks + * If one of these returned body IDs is zero, the joint connects the other body + * to the static environment. + * If both body IDs are zero, the joint is in ``limbo'' and has no effect on + * the simulation. + */ +ODE_API dBodyID dJointGetBody (dJointID, int index); + +/** + * @brief Sets the datastructure that is to receive the feedback. + * + * The feedback can be used by the user, so that it is known how + * much force an individual joint exerts. + * @ingroup joints + */ +ODE_API void dJointSetFeedback (dJointID, dJointFeedback *); + +/** + * @brief Gets the datastructure that is to receive the feedback. + * @ingroup joints + */ +ODE_API dJointFeedback *dJointGetFeedback (dJointID); + +/** + * @brief Set the joint anchor point. + * @ingroup joints + * + * The joint will try to keep this point on each body + * together. The input is specified in world coordinates. + */ +ODE_API void dJointSetBallAnchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Set the joint anchor point. + * @ingroup joints + */ +ODE_API void dJointSetBallAnchor2 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Param setting for Ball joints + * @ingroup joints + */ +ODE_API void dJointSetBallParam (dJointID, int parameter, dReal value); + +/** + * @brief Set hinge anchor parameter. + * @ingroup joints + */ +ODE_API void dJointSetHingeAnchor (dJointID, dReal x, dReal y, dReal z); + +ODE_API void dJointSetHingeAnchorDelta (dJointID, dReal x, dReal y, dReal z, dReal ax, dReal ay, dReal az); + +/** + * @brief Set hinge axis. + * @ingroup joints + */ +ODE_API void dJointSetHingeAxis (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Set the Hinge axis as if the 2 bodies were already at angle appart. + * @ingroup joints + * + * This function initialize the Axis and the relative orientation of each body + * as if body1 was rotated around the axis by the angle value. \br + * Ex: + *
    + * dJointSetHingeAxis(jId, 1, 0, 0);
    + * // If you request the position you will have: dJointGetHingeAngle(jId) == 0
    + * dJointSetHingeAxisDelta(jId, 1, 0, 0, 0.23);
    + * // If you request the position you will have: dJointGetHingeAngle(jId) == 0.23
    + * 
    + + * @param j The Hinge joint ID for which the axis will be set + * @param x The X component of the axis in world frame + * @param y The Y component of the axis in world frame + * @param z The Z component of the axis in world frame + * @param angle The angle for the offset of the relative orientation. + * As if body1 was rotated by angle when the Axis was set (see below). + * The rotation is around the new Hinge axis. + * + * @note Usually the function dJointSetHingeAxis set the current position of body1 + * and body2 as the zero angle position. This function set the current position + * as the if the 2 bodies where \b angle appart. + * @warning Calling dJointSetHingeAnchor or dJointSetHingeAxis will reset the "zero" + * angle position. + */ +ODE_API void dJointSetHingeAxisOffset (dJointID j, dReal x, dReal y, dReal z, dReal angle); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetHingeParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies the torque about the hinge axis. + * + * That is, it applies a torque with specified magnitude in the direction + * of the hinge axis, to body 1, and with the same magnitude but in opposite + * direction to body 2. This function is just a wrapper for dBodyAddTorque()} + * @ingroup joints + */ +ODE_API void dJointAddHingeTorque(dJointID joint, dReal torque); + +/** + * @brief set the joint axis + * @ingroup joints + */ +ODE_API void dJointSetSliderAxis (dJointID, dReal x, dReal y, dReal z); + +/** + * @ingroup joints + */ +ODE_API void dJointSetSliderAxisDelta (dJointID, dReal x, dReal y, dReal z, dReal ax, dReal ay, dReal az); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetSliderParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies the given force in the slider's direction. + * + * That is, it applies a force with specified magnitude, in the direction of + * slider's axis, to body1, and with the same magnitude but opposite + * direction to body2. This function is just a wrapper for dBodyAddForce(). + * @ingroup joints + */ +ODE_API void dJointAddSliderForce(dJointID joint, dReal force); + +/** + * @brief set anchor + * @ingroup joints + */ +ODE_API void dJointSetHinge2Anchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set both axes (optionally) + * + * This can change both axes at once avoiding transitions via invalid states + * while changing axes one by one and having the first changed axis coincide + * with the other axis existing direction. + * + * At least one of the axes must be not NULL. If NULL is passed, the corresponding + * axis retains its existing value. + * + * @ingroup joints + */ +ODE_API void dJointSetHinge2Axes (dJointID j, const dReal *axis1/*=[dSA__MAX],=NULL*/, const dReal *axis2/*=[dSA__MAX],=NULL*/); + +/** + * @brief set axis + * + * Deprecated. Use @fn dJointSetHinge2Axes instead. + * + * @ingroup joints + * @see dJointSetHinge2Axes + */ +ODE_API_DEPRECATED ODE_API void dJointSetHinge2Axis1 (dJointID j, dReal x, dReal y, dReal z); + +/** + * @brief set axis + * + * Deprecated. Use @fn dJointSetHinge2Axes instead. + * + * @ingroup joints + * @see dJointSetHinge2Axes + */ +ODE_API_DEPRECATED ODE_API void dJointSetHinge2Axis2 (dJointID j, dReal x, dReal y, dReal z); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetHinge2Param (dJointID, int parameter, dReal value); + +/** + * @brief Applies torque1 about the hinge2's axis 1, torque2 about the + * hinge2's axis 2. + * @remarks This function is just a wrapper for dBodyAddTorque(). + * @ingroup joints + */ +ODE_API void dJointAddHinge2Torques(dJointID joint, dReal torque1, dReal torque2); + +/** + * @brief set anchor + * @ingroup joints + */ +ODE_API void dJointSetUniversalAnchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set axis + * @ingroup joints + */ +ODE_API void dJointSetUniversalAxis1 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Set the Universal axis1 as if the 2 bodies were already at + * offset1 and offset2 appart with respect to axis1 and axis2. + * @ingroup joints + * + * This function initializes the axis1 and the relative orientation of + * each body as if body1 was rotated around the new axis1 by the offset1 + * value and as if body2 was rotated around the axis2 by offset2. \br + * Ex: +*
    + * dJointSetHuniversalAxis1(jId, 1, 0, 0);
    + * // If you request the position you will have: dJointGetUniversalAngle1(jId) == 0
    + * // If you request the position you will have: dJointGetUniversalAngle2(jId) == 0
    + * dJointSetHuniversalAxis1Offset(jId, 1, 0, 0, 0.2, 0.17);
    + * // If you request the position you will have: dJointGetUniversalAngle1(jId) == 0.2
    + * // If you request the position you will have: dJointGetUniversalAngle2(jId) == 0.17
    + * 
    + * + * @param j The Hinge joint ID for which the axis will be set + * @param x The X component of the axis in world frame + * @param y The Y component of the axis in world frame + * @param z The Z component of the axis in world frame + * @param angle The angle for the offset of the relative orientation. + * As if body1 was rotated by angle when the Axis was set (see below). + * The rotation is around the new Hinge axis. + * + * @note Usually the function dJointSetHingeAxis set the current position of body1 + * and body2 as the zero angle position. This function set the current position + * as the if the 2 bodies where \b offsets appart. + * + * @note Any previous offsets are erased. + * + * @warning Calling dJointSetUniversalAnchor, dJointSetUnivesalAxis1, + * dJointSetUniversalAxis2, dJointSetUniversalAxis2Offset + * will reset the "zero" angle position. + */ +ODE_API void dJointSetUniversalAxis1Offset (dJointID, dReal x, dReal y, dReal z, + dReal offset1, dReal offset2); + +/** + * @brief set axis + * @ingroup joints + */ +ODE_API void dJointSetUniversalAxis2 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Set the Universal axis2 as if the 2 bodies were already at + * offset1 and offset2 appart with respect to axis1 and axis2. + * @ingroup joints + * + * This function initialize the axis2 and the relative orientation of + * each body as if body1 was rotated around the axis1 by the offset1 + * value and as if body2 was rotated around the new axis2 by offset2. \br + * Ex: + *
    + * dJointSetHuniversalAxis2(jId, 0, 1, 0);
    + * // If you request the position you will have: dJointGetUniversalAngle1(jId) == 0
    + * // If you request the position you will have: dJointGetUniversalAngle2(jId) == 0
    + * dJointSetHuniversalAxis2Offset(jId, 0, 1, 0, 0.2, 0.17);
    + * // If you request the position you will have: dJointGetUniversalAngle1(jId) == 0.2
    + * // If you request the position you will have: dJointGetUniversalAngle2(jId) == 0.17
    + * 
    + + * @param j The Hinge joint ID for which the axis will be set + * @param x The X component of the axis in world frame + * @param y The Y component of the axis in world frame + * @param z The Z component of the axis in world frame + * @param angle The angle for the offset of the relative orientation. + * As if body1 was rotated by angle when the Axis was set (see below). + * The rotation is around the new Hinge axis. + * + * @note Usually the function dJointSetHingeAxis set the current position of body1 + * and body2 as the zero angle position. This function set the current position + * as the if the 2 bodies where \b offsets appart. + * + * @note Any previous offsets are erased. + * + * @warning Calling dJointSetUniversalAnchor, dJointSetUnivesalAxis1, + * dJointSetUniversalAxis2, dJointSetUniversalAxis2Offset + * will reset the "zero" angle position. + */ + + +ODE_API void dJointSetUniversalAxis2Offset (dJointID, dReal x, dReal y, dReal z, + dReal offset1, dReal offset2); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetUniversalParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies torque1 about the universal's axis 1, torque2 about the + * universal's axis 2. + * @remarks This function is just a wrapper for dBodyAddTorque(). + * @ingroup joints + */ +ODE_API void dJointAddUniversalTorques(dJointID joint, dReal torque1, dReal torque2); + + +/** + * @brief set anchor + * @ingroup joints + */ +ODE_API void dJointSetPRAnchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set the axis for the prismatic articulation + * @ingroup joints + */ +ODE_API void dJointSetPRAxis1 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set the axis for the rotoide articulation + * @ingroup joints + */ +ODE_API void dJointSetPRAxis2 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set joint parameter + * @ingroup joints + * + * @note parameterX where X equal 2 refer to parameter for the rotoide articulation + */ +ODE_API void dJointSetPRParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies the torque about the rotoide axis of the PR joint + * + * That is, it applies a torque with specified magnitude in the direction + * of the rotoide axis, to body 1, and with the same magnitude but in opposite + * direction to body 2. This function is just a wrapper for dBodyAddTorque()} + * @ingroup joints + */ +ODE_API void dJointAddPRTorque (dJointID j, dReal torque); + + +/** +* @brief set anchor +* @ingroup joints +*/ +ODE_API void dJointSetPUAnchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set anchor + * @ingroup joints + */ +ODE_API_DEPRECATED ODE_API void dJointSetPUAnchorDelta (dJointID, dReal x, dReal y, dReal z, + dReal dx, dReal dy, dReal dz); + +/** + * @brief Set the PU anchor as if the 2 bodies were already at [dx, dy, dz] appart. + * @ingroup joints + * + * This function initializes the anchor and the relative position of each body + * as if the position between body1 and body2 was already the projection of [dx, dy, dz] + * along the Piston axis. (i.e as if the body1 was at its current position - [dx,dy,dy] when the + * axis is set). + * Ex: + *
    + * dReal offset = 3;
    + * dVector3 axis;
    + * dJointGetPUAxis(jId, axis);
    + * dJointSetPUAnchor(jId, 0, 0, 0);
    + * // If you request the position you will have: dJointGetPUPosition(jId) == 0
    + * dJointSetPUAnchorOffset(jId, 0, 0, 0, axis[X]*offset, axis[Y]*offset, axis[Z]*offset);
    + * // If you request the position you will have: dJointGetPUPosition(jId) == offset
    + * 
    + * @param j The PU joint for which the anchor point will be set + * @param x The X position of the anchor point in world frame + * @param y The Y position of the anchor point in world frame + * @param z The Z position of the anchor point in world frame + * @param dx A delta to be substracted to the X position as if the anchor was set + * when body1 was at current_position[X] - dx + * @param dx A delta to be substracted to the Y position as if the anchor was set + * when body1 was at current_position[Y] - dy + * @param dx A delta to be substracted to the Z position as if the anchor was set + * when body1 was at current_position[Z] - dz + */ +ODE_API void dJointSetPUAnchorOffset (dJointID, dReal x, dReal y, dReal z, + dReal dx, dReal dy, dReal dz); + +/** + * @brief set the axis for the first axis or the universal articulation + * @ingroup joints + */ +ODE_API void dJointSetPUAxis1 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set the axis for the second axis or the universal articulation + * @ingroup joints + */ +ODE_API void dJointSetPUAxis2 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set the axis for the prismatic articulation + * @ingroup joints + */ +ODE_API void dJointSetPUAxis3 (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set the axis for the prismatic articulation + * @ingroup joints + * @note This function was added for convenience it is the same as + * dJointSetPUAxis3 + */ +ODE_API void dJointSetPUAxisP (dJointID id, dReal x, dReal y, dReal z); + + + +/** + * @brief set joint parameter + * @ingroup joints + * + * @note parameterX where X equal 2 refer to parameter for second axis of the + * universal articulation + * @note parameterX where X equal 3 refer to parameter for prismatic + * articulation + */ +ODE_API void dJointSetPUParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies torques about the rotoide axes of PU joint + * + * That is, it applies torque1 about the universal axis 1 and torque2 about the + * universal axis 2 to body 1, and with the same magnitude but in opposite + * direction to body 2. + * @ingroup joints + * @remarks This function is just a wrapper for dBodyAddTorque(). + * @ingroup joints + */ +ODE_API void dJointAddPUTorques (dJointID joint, dReal torque1, dReal torque2); + + + + +/** + * @brief set the joint anchor + * @ingroup joints + */ +ODE_API void dJointSetPistonAnchor (dJointID, dReal x, dReal y, dReal z); + +/** + * @brief Set the Piston anchor as if the 2 bodies were already at [dx,dy, dz] appart. + * @ingroup joints + * + * This function initializes the anchor and the relative position of each body + * as if the position between body1 and body2 was already the projection of [dx, dy, dz] + * along the Piston axis. (i.e as if the body1 was at its current position - [dx,dy,dy] when the + * axis is set). + * Ex: + *
    + * dReal offset = 3;
    + * dVector3 axis;
    + * dJointGetPistonAxis(jId, axis);
    + * dJointSetPistonAnchor(jId, 0, 0, 0);
    + * // If you request the position you will have: dJointGetPistonPosition(jId) == 0
    + * dJointSetPistonAnchorOffset(jId, 0, 0, 0, axis[X]*offset, axis[Y]*offset, axis[Z]*offset);
    + * // If you request the position you will have: dJointGetPistonPosition(jId) == offset
    + * 
    + * @param j The Piston joint for which the anchor point will be set + * @param x The X position of the anchor point in world frame + * @param y The Y position of the anchor point in world frame + * @param z The Z position of the anchor point in world frame + * @param dx A delta to be substracted to the X position as if the anchor was set + * when body1 was at current_position[X] - dx + * @param dx A delta to be substracted to the Y position as if the anchor was set + * when body1 was at current_position[Y] - dy + * @param dx A delta to be substracted to the Z position as if the anchor was set + * when body1 was at current_position[Z] - dz + */ +ODE_API void dJointSetPistonAnchorOffset(dJointID j, dReal x, dReal y, dReal z, + dReal dx, dReal dy, dReal dz); + + /** + * @brief set the joint axis + * @ingroup joints + */ +ODE_API void dJointSetPistonAxis (dJointID, dReal x, dReal y, dReal z); + +/** + * This function set prismatic axis of the joint and also set the position + * of the joint. + * + * @ingroup joints + * @param j The joint affected by this function + * @param x The x component of the axis + * @param y The y component of the axis + * @param z The z component of the axis + * @param dx The Initial position of the prismatic joint in the x direction + * @param dy The Initial position of the prismatic joint in the y direction + * @param dz The Initial position of the prismatic joint in the z direction + */ +ODE_API_DEPRECATED ODE_API void dJointSetPistonAxisDelta (dJointID j, dReal x, dReal y, dReal z, dReal ax, dReal ay, dReal az); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetPistonParam (dJointID, int parameter, dReal value); + +/** + * @brief Applies the given force in the slider's direction. + * + * That is, it applies a force with specified magnitude, in the direction of + * prismatic's axis, to body1, and with the same magnitude but opposite + * direction to body2. This function is just a wrapper for dBodyAddForce(). + * @ingroup joints + */ +ODE_API void dJointAddPistonForce (dJointID joint, dReal force); + + +/** + * @brief Call this on the fixed joint after it has been attached to + * remember the current desired relative offset and desired relative + * rotation between the bodies. + * @ingroup joints + */ +ODE_API void dJointSetFixed (dJointID); + +/* + * @brief Sets joint parameter + * + * @ingroup joints + */ +ODE_API void dJointSetFixedParam (dJointID, int parameter, dReal value); + +/** + * @brief set the nr of axes + * @param num 0..3 + * @ingroup joints + */ +ODE_API void dJointSetAMotorNumAxes (dJointID, int num); + +/** + * @brief set axis + * @ingroup joints + */ +ODE_API void dJointSetAMotorAxis (dJointID, int anum, int rel, + dReal x, dReal y, dReal z); + +/** + * @brief Tell the AMotor what the current angle is along axis anum. + * + * This function should only be called in dAMotorUser mode, because in this + * mode the AMotor has no other way of knowing the joint angles. + * The angle information is needed if stops have been set along the axis, + * but it is not needed for axis motors. + * @ingroup joints + */ +ODE_API void dJointSetAMotorAngle (dJointID, int anum, dReal angle); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetAMotorParam (dJointID, int parameter, dReal value); + +/** + * @brief set mode + * @ingroup joints + */ +ODE_API void dJointSetAMotorMode (dJointID, int mode); + +/** + * @brief Applies torque0 about the AMotor's axis 0, torque1 about the + * AMotor's axis 1, and torque2 about the AMotor's axis 2. + * @remarks + * If the motor has fewer than three axes, the higher torques are ignored. + * This function is just a wrapper for dBodyAddTorque(). + * @ingroup joints + */ +ODE_API void dJointAddAMotorTorques (dJointID, dReal torque1, dReal torque2, dReal torque3); + +/** + * @brief Set the number of axes that will be controlled by the LMotor. + * @param num can range from 0 (which effectively deactivates the joint) to 3. + * @ingroup joints + */ +ODE_API void dJointSetLMotorNumAxes (dJointID, int num); + +/** + * @brief Set the AMotor axes. + * @param anum selects the axis to change (0,1 or 2). + * @param rel Each axis can have one of three ``relative orientation'' modes + * \li 0: The axis is anchored to the global frame. + * \li 1: The axis is anchored to the first body. + * \li 2: The axis is anchored to the second body. + * @remarks The axis vector is always specified in global coordinates + * regardless of the setting of rel. + * @ingroup joints + */ +ODE_API void dJointSetLMotorAxis (dJointID, int anum, int rel, dReal x, dReal y, dReal z); + +/** + * @brief set joint parameter + * @ingroup joints + */ +ODE_API void dJointSetLMotorParam (dJointID, int parameter, dReal value); + +/** + * @ingroup joints + */ +ODE_API void dJointSetPlane2DXParam (dJointID, int parameter, dReal value); + +/** + * @ingroup joints + */ + +ODE_API void dJointSetPlane2DYParam (dJointID, int parameter, dReal value); + +/** + * @ingroup joints + */ +ODE_API void dJointSetPlane2DAngleParam (dJointID, int parameter, dReal value); + +/** + * @brief Get the joint anchor point, in world coordinates. + * + * This returns the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + */ +ODE_API void dJointGetBallAnchor (dJointID, dVector3 result); + +/** + * @brief Get the joint anchor point, in world coordinates. + * + * This returns the point on body 2. You can think of a ball and socket + * joint as trying to keep the result of dJointGetBallAnchor() and + * dJointGetBallAnchor2() the same. If the joint is perfectly satisfied, + * this function will return the same value as dJointGetBallAnchor() to + * within roundoff errors. dJointGetBallAnchor2() can be used, along with + * dJointGetBallAnchor(), to see how far the joint has come apart. + */ +ODE_API void dJointGetBallAnchor2 (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetBallParam (dJointID, int parameter); + +/** + * @brief Get the hinge anchor point, in world coordinates. + * + * This returns the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + * @ingroup joints + */ +ODE_API void dJointGetHingeAnchor (dJointID, dVector3 result); + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return The point on body 2. If the joint is perfectly satisfied, + * this will return the same value as dJointGetHingeAnchor(). + * If not, this value will be slightly different. + * This can be used, for example, to see how far the joint has come apart. + * @ingroup joints + */ +ODE_API void dJointGetHingeAnchor2 (dJointID, dVector3 result); + +/** + * @brief get axis + * @ingroup joints + */ +ODE_API void dJointGetHingeAxis (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetHingeParam (dJointID, int parameter); + +/** + * @brief Get the hinge angle. + * + * The angle is measured between the two bodies, or between the body and + * the static environment. + * The angle will be between -pi..pi. + * Give the relative rotation with respect to the Hinge axis of Body 1 with + * respect to Body 2. + * When the hinge anchor or axis is set, the current position of the attached + * bodies is examined and that position will be the zero angle. + * @ingroup joints + */ +ODE_API dReal dJointGetHingeAngle (dJointID); + +/** + * @brief Get the hinge angle time derivative. + * @ingroup joints + */ +ODE_API dReal dJointGetHingeAngleRate (dJointID); + +/** + * @brief Get the slider linear position (i.e. the slider's extension) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + + * The position is the distance, with respect to the zero position, + * along the slider axis of body 1 with respect to + * body 2. (A NULL body is replaced by the world). + * @ingroup joints + */ +ODE_API dReal dJointGetSliderPosition (dJointID); + +/** + * @brief Get the slider linear position's time derivative. + * @ingroup joints + */ +ODE_API dReal dJointGetSliderPositionRate (dJointID); + +/** + * @brief Get the slider axis + * @ingroup joints + */ +ODE_API void dJointGetSliderAxis (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetSliderParam (dJointID, int parameter); + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + * @ingroup joints + */ +ODE_API void dJointGetHinge2Anchor (dJointID, dVector3 result); + +/** + * @brief Get the joint anchor point, in world coordinates. + * This returns the point on body 2. If the joint is perfectly satisfied, + * this will return the same value as dJointGetHinge2Anchor. + * If not, this value will be slightly different. + * This can be used, for example, to see how far the joint has come apart. + * @ingroup joints + */ +ODE_API void dJointGetHinge2Anchor2 (dJointID, dVector3 result); + +/** + * @brief Get joint axis + * @ingroup joints + */ +ODE_API void dJointGetHinge2Axis1 (dJointID, dVector3 result); + +/** + * @brief Get joint axis + * @ingroup joints + */ +ODE_API void dJointGetHinge2Axis2 (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetHinge2Param (dJointID, int parameter); + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetHinge2Angle1 (dJointID); + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetHinge2Angle2 (dJointID); + +/** + * @brief Get time derivative of angle + * @ingroup joints + */ +ODE_API dReal dJointGetHinge2Angle1Rate (dJointID); + +/** + * @brief Get time derivative of angle + * @ingroup joints + */ +ODE_API dReal dJointGetHinge2Angle2Rate (dJointID); + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + * @ingroup joints + */ +ODE_API void dJointGetUniversalAnchor (dJointID, dVector3 result); + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return This returns the point on body 2. + * @remarks + * You can think of the ball and socket part of a universal joint as + * trying to keep the result of dJointGetBallAnchor() and + * dJointGetBallAnchor2() the same. If the joint is + * perfectly satisfied, this function will return the same value + * as dJointGetUniversalAnchor() to within roundoff errors. + * dJointGetUniversalAnchor2() can be used, along with + * dJointGetUniversalAnchor(), to see how far the joint has come apart. + * @ingroup joints + */ +ODE_API void dJointGetUniversalAnchor2 (dJointID, dVector3 result); + +/** + * @brief Get axis + * @ingroup joints + */ +ODE_API void dJointGetUniversalAxis1 (dJointID, dVector3 result); + +/** + * @brief Get axis + * @ingroup joints + */ +ODE_API void dJointGetUniversalAxis2 (dJointID, dVector3 result); + + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetUniversalParam (dJointID, int parameter); + +/** + * @brief Get both angles at the same time. + * @ingroup joints + * + * @param joint The universal joint for which we want to calculate the angles + * @param angle1 The angle between the body1 and the axis 1 + * @param angle2 The angle between the body2 and the axis 2 + * + * @note This function combine getUniversalAngle1 and getUniversalAngle2 together + * and try to avoid redundant calculation + */ +ODE_API void dJointGetUniversalAngles (dJointID, dReal *angle1, dReal *angle2); + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetUniversalAngle1 (dJointID); + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetUniversalAngle2 (dJointID); + +/** + * @brief Get time derivative of angle + * @ingroup joints + */ +ODE_API dReal dJointGetUniversalAngle1Rate (dJointID); + +/** + * @brief Get time derivative of angle + * @ingroup joints + */ +ODE_API dReal dJointGetUniversalAngle2Rate (dJointID); + + + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + * @ingroup joints + */ +ODE_API void dJointGetPRAnchor (dJointID, dVector3 result); + +/** + * @brief Get the PR linear position (i.e. the prismatic's extension) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + * + * The position is the "oriented" length between the + * position = (Prismatic axis) dot_product [(body1 + offset) - (body2 + anchor2)] + * + * @ingroup joints + */ +ODE_API dReal dJointGetPRPosition (dJointID); + +/** + * @brief Get the PR linear position's time derivative + * + * @ingroup joints + */ +ODE_API dReal dJointGetPRPositionRate (dJointID); + + +/** + * @brief Get the PR angular position (i.e. the twist between the 2 bodies) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + * @ingroup joints + */ +ODE_API dReal dJointGetPRAngle (dJointID); + +/** + * @brief Get the PR angular position's time derivative + * + * @ingroup joints + */ +ODE_API dReal dJointGetPRAngleRate (dJointID); + + +/** + * @brief Get the prismatic axis + * @ingroup joints + */ +ODE_API void dJointGetPRAxis1 (dJointID, dVector3 result); + +/** + * @brief Get the Rotoide axis + * @ingroup joints + */ +ODE_API void dJointGetPRAxis2 (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetPRParam (dJointID, int parameter); + + + +/** + * @brief Get the joint anchor point, in world coordinates. + * @return the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2. + * @ingroup joints + */ +ODE_API void dJointGetPUAnchor (dJointID, dVector3 result); + +/** + * @brief Get the PU linear position (i.e. the prismatic's extension) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + * + * The position is the "oriented" length between the + * position = (Prismatic axis) dot_product [(body1 + offset) - (body2 + anchor2)] + * + * @ingroup joints + */ +ODE_API dReal dJointGetPUPosition (dJointID); + +/** + * @brief Get the PR linear position's time derivative + * + * @ingroup joints + */ +ODE_API dReal dJointGetPUPositionRate (dJointID); + +/** + * @brief Get the first axis of the universal component of the joint + * @ingroup joints + */ +ODE_API void dJointGetPUAxis1 (dJointID, dVector3 result); + +/** + * @brief Get the second axis of the Universal component of the joint + * @ingroup joints + */ +ODE_API void dJointGetPUAxis2 (dJointID, dVector3 result); + +/** + * @brief Get the prismatic axis + * @ingroup joints + */ +ODE_API void dJointGetPUAxis3 (dJointID, dVector3 result); + +/** + * @brief Get the prismatic axis + * @ingroup joints + * + * @note This function was added for convenience it is the same as + * dJointGetPUAxis3 + */ +ODE_API void dJointGetPUAxisP (dJointID id, dVector3 result); + + + + +/** + * @brief Get both angles at the same time. + * @ingroup joints + * + * @param joint The Prismatic universal joint for which we want to calculate the angles + * @param angle1 The angle between the body1 and the axis 1 + * @param angle2 The angle between the body2 and the axis 2 + * + * @note This function combine dJointGetPUAngle1 and dJointGetPUAngle2 together + * and try to avoid redundant calculation + */ +ODE_API void dJointGetPUAngles (dJointID, dReal *angle1, dReal *angle2); + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetPUAngle1 (dJointID); + +/** + * @brief * @brief Get time derivative of angle1 + * + * @ingroup joints + */ +ODE_API dReal dJointGetPUAngle1Rate (dJointID); + + +/** + * @brief Get angle + * @ingroup joints + */ +ODE_API dReal dJointGetPUAngle2 (dJointID); + +/** + * @brief * @brief Get time derivative of angle2 + * + * @ingroup joints + */ +ODE_API dReal dJointGetPUAngle2Rate (dJointID); + + /** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetPUParam (dJointID, int parameter); + + + + + +/** + * @brief Get the Piston linear position (i.e. the piston's extension) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + * @ingroup joints + */ +ODE_API dReal dJointGetPistonPosition (dJointID); + +/** + * @brief Get the piston linear position's time derivative. + * @ingroup joints + */ +ODE_API dReal dJointGetPistonPositionRate (dJointID); + +/** + * @brief Get the Piston angular position (i.e. the twist between the 2 bodies) + * + * When the axis is set, the current position of the attached bodies is + * examined and that position will be the zero position. + * @ingroup joints + */ +ODE_API dReal dJointGetPistonAngle (dJointID); + +/** + * @brief Get the piston angular position's time derivative. + * @ingroup joints + */ +ODE_API dReal dJointGetPistonAngleRate (dJointID); + + +/** + * @brief Get the joint anchor + * + * This returns the point on body 1. If the joint is perfectly satisfied, + * this will be the same as the point on body 2 in direction perpendicular + * to the prismatic axis. + * + * @ingroup joints + */ +ODE_API void dJointGetPistonAnchor (dJointID, dVector3 result); + +/** + * @brief Get the joint anchor w.r.t. body 2 + * + * This returns the point on body 2. You can think of a Piston + * joint as trying to keep the result of dJointGetPistonAnchor() and + * dJointGetPistonAnchor2() the same in the direction perpendicular to the + * pirsmatic axis. If the joint is perfectly satisfied, + * this function will return the same value as dJointGetPistonAnchor() to + * within roundoff errors. dJointGetPistonAnchor2() can be used, along with + * dJointGetPistonAnchor(), to see how far the joint has come apart. + * + * @ingroup joints + */ +ODE_API void dJointGetPistonAnchor2 (dJointID, dVector3 result); + +/** + * @brief Get the prismatic axis (This is also the rotoide axis. + * @ingroup joints + */ +ODE_API void dJointGetPistonAxis (dJointID, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetPistonParam (dJointID, int parameter); + + +/** + * @brief Get the number of angular axes that will be controlled by the + * AMotor. + * @param num can range from 0 (which effectively deactivates the + * joint) to 3. + * This is automatically set to 3 in dAMotorEuler mode. + * @ingroup joints + */ +ODE_API int dJointGetAMotorNumAxes (dJointID); + +/** + * @brief Get the AMotor axes. + * @param anum selects the axis to change (0,1 or 2). + * @param rel Each axis can have one of three ``relative orientation'' modes. + * \li 0: The axis is anchored to the global frame. + * \li 1: The axis is anchored to the first body. + * \li 2: The axis is anchored to the second body. + * @ingroup joints + */ +ODE_API void dJointGetAMotorAxis (dJointID, int anum, dVector3 result); + +/** + * @brief Get axis + * @remarks + * The axis vector is always specified in global coordinates regardless + * of the setting of rel. + * There are two GetAMotorAxis functions, one to return the axis and one to + * return the relative mode. + * + * For dAMotorEuler mode: + * \li Only axes 0 and 2 need to be set. Axis 1 will be determined + automatically at each time step. + * \li Axes 0 and 2 must be perpendicular to each other. + * \li Axis 0 must be anchored to the first body, axis 2 must be anchored + to the second body. + * @ingroup joints + */ +ODE_API int dJointGetAMotorAxisRel (dJointID, int anum); + +/** + * @brief Get the current angle for axis. + * @remarks + * In dAMotorUser mode this is simply the value that was set with + * dJointSetAMotorAngle(). + * In dAMotorEuler mode this is the corresponding euler angle. + * @ingroup joints + */ +ODE_API dReal dJointGetAMotorAngle (dJointID, int anum); + +/** + * @brief Get the current angle rate for axis anum. + * @remarks + * In dAMotorUser mode this is always zero, as not enough information is + * available. + * In dAMotorEuler mode this is the corresponding euler angle rate. + * @ingroup joints + */ +ODE_API dReal dJointGetAMotorAngleRate (dJointID, int anum); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetAMotorParam (dJointID, int parameter); + +/** + * @brief Get the angular motor mode. + * @param mode must be one of the following constants: + * \li dAMotorUser The AMotor axes and joint angle settings are entirely + * controlled by the user. This is the default mode. + * \li dAMotorEuler Euler angles are automatically computed. + * The axis a1 is also automatically computed. + * The AMotor axes must be set correctly when in this mode, + * as described below. + * When this mode is initially set the current relative orientations + * of the bodies will correspond to all euler angles at zero. + * @ingroup joints + */ +ODE_API int dJointGetAMotorMode (dJointID); + +/** + * @brief Get nr of axes. + * @ingroup joints + */ +ODE_API int dJointGetLMotorNumAxes (dJointID); + +/** + * @brief Get axis. + * @ingroup joints + */ +ODE_API void dJointGetLMotorAxis (dJointID, int anum, dVector3 result); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetLMotorParam (dJointID, int parameter); + +/** + * @brief get joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetFixedParam (dJointID, int parameter); + + +/** + * @brief get the contact point of the first wheel of the Transmission joint. + * @ingroup joints + */ +ODE_API void dJointGetTransmissionContactPoint1(dJointID, dVector3 result); + +/** + * @brief get contact point of the second wheel of the Transmission joint. + * @ingroup joints + */ +ODE_API void dJointGetTransmissionContactPoint2(dJointID, dVector3 result); + +/** + * @brief set the first axis for the Transmission joint + * @remarks This is the axis around which the first body is allowed to + * revolve and is attached to it. It is given in global coordinates + * and can only be set explicitly in intersecting-axes mode. For the + * parallel-axes and chain modes which share one common axis of + * revolution for both gears dJointSetTransmissionAxis should be used. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionAxis1(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get first axis for the Transmission joint + * @remarks In parallel-axes and chain mode the common axis with + * respect to the first body is returned. If the joint constraint is + * satisfied it should be the same as the axis return with + * dJointGetTransmissionAxis2 or dJointGetTransmissionAxis. + * @ingroup joints + */ +ODE_API void dJointGetTransmissionAxis1(dJointID, dVector3 result); + +/** + * @brief set second axis for the Transmission joint + * @remarks This is the axis around which the second body is allowed + * to revolve and is attached to it. It is given in global + * coordinates and can only be set explicitly in intersecting-axes + * mode. For the parallel-axes and chain modes which share one common + * axis of revolution for both gears dJointSetTransmissionAxis should + * be used. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionAxis2(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get second axis for the Transmission joint + * @remarks In parallel-axes and chain mode the common axis with + * respect to the second body is returned. If the joint constraint is + * satisfied it should be the same as the axis return with + * dJointGetTransmissionAxis1 or dJointGetTransmissionAxis. + * @ingroup joints + */ +ODE_API void dJointGetTransmissionAxis2(dJointID, dVector3 result); + +/** + * @brief set the first anchor for the Transmission joint + * @remarks This is the point of attachment of the wheel on the + * first body. It is given in global coordinates. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionAnchor1(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get the first anchor of the Transmission joint + * @ingroup joints + */ +ODE_API void dJointGetTransmissionAnchor1(dJointID, dVector3 result); + +/** + * @brief set the second anchor for the Transmission joint + * @remarks This is the point of attachment of the wheel on the + * second body. It is given in global coordinates. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionAnchor2(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get the second anchor for the Transmission joint + * @ingroup joints + */ +ODE_API void dJointGetTransmissionAnchor2(dJointID, dVector3 result); + +/** + * @brief set a Transmission joint parameter + * @ingroup joints + */ +ODE_API void dJointSetTransmissionParam(dJointID, int parameter, dReal value); + +/** + * @brief get a Transmission joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionParam(dJointID, int parameter); + +/** + * @brief set the Transmission joint mode + * @remarks The mode can be one of dTransmissionParallelAxes, + * dTransmissionIntersectingAxes and dTransmissionChainDrive simulating a + * set of parallel-axes gears, intersecting-axes beveled gears or + * chain and sprockets respectively. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionMode( dJointID j, int mode ); + +/** + * @brief get the Transmission joint mode + * @ingroup joints + */ +ODE_API int dJointGetTransmissionMode( dJointID j ); + +/** + * @brief set the Transmission ratio + * @remarks This is the ratio of the angular speed of the first gear + * to that of the second gear. It can only be set explicitly in + * parallel-axes mode. In intersecting-axes mode the ratio is defined + * implicitly by the initial configuration of the wheels and in chain + * mode it is defined implicitly be the wheel radii. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionRatio( dJointID j, dReal ratio ); + +/** + * @brief get the Transmission joint ratio + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionRatio( dJointID j ); + +/** + * @brief set the common axis for both wheels of the Transmission joint + * @remarks This sets the common axis of revolution for both wheels + * and should only be used in parallel-axes or chain mode. For + * intersecting-axes mode where each wheel axis needs to be specified + * individually dJointSetTransmissionAxis1 and + * dJointSetTransmissionAxis2 should be used. The axis is given in + * global coordinates + * @ingroup joints + */ +ODE_API void dJointSetTransmissionAxis( dJointID j, dReal x, dReal y, dReal z ); + +/** + * @brief get the common axis for both wheels of the Transmission joint + * @ingroup joints + */ +ODE_API void dJointGetTransmissionAxis( dJointID j, dVector3 result ); + +/** + * @brief get the phase, that is the traversed angle for the first + * wheel of the Transmission joint + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionAngle1( dJointID j ); + +/** + * @brief get the phase, that is the traversed angle for the second + * wheel of the Transmission joint + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionAngle2( dJointID j ); + +/** + * @brief get the radius of the first wheel of the Transmission joint + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionRadius1( dJointID j ); + +/** + * @brief get the radius of the second wheel of the Transmission joint + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionRadius2( dJointID j ); + +/** + * @brief set the radius of the first wheel of the Transmission joint + * @remarks The wheel radii can only be set explicitly in chain mode. + * In the other modes they're defined implicitly by the initial + * configuration and ratio of the wheels. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionRadius1( dJointID j, dReal radius ); + +/** + * @brief set the radius of the second wheel of the Transmission joint + * @remarks The wheel radii can only be set explicitly in chain mode. + * In the other modes they're defined implicitly by the initial + * configuration and ratio of the wheels. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionRadius2( dJointID j, dReal radius ); + +/** + * @brief get the backlash of the Transmission joint + * @ingroup joints + */ +ODE_API dReal dJointGetTransmissionBacklash( dJointID j ); + +/** + * @brief set the backlash of the Transmission joint + * @remarks Backlash is the clearance in the mesh of the wheels of the + * transmission and is defined as the maximum distance that the + * geometric contact point can travel without any actual contact or + * transfer of power between the wheels. This can be converted in + * degrees of revolution for each wheel by dividing by the wheel's + * radius. To further illustrate this consider the situation where a + * wheel of radius r_1 is driving another wheel of radius r_2 and + * there is an amount of backlash equal to b in their mesh. If the + * driving wheel were to instantaneously stop there would be no + * contact and hence the driven wheel would continue to turn for + * another b / r_2 radians until all the backlash in the mesh was take + * up and contact restored with the relationship of driving and driven + * wheel reversed. The backlash is therefore given in untis of + * length. + * @ingroup joints + */ +ODE_API void dJointSetTransmissionBacklash( dJointID j, dReal backlash ); + +/** + * @brief set anchor1 for double ball joint + * @ingroup joints + */ +ODE_API void dJointSetDBallAnchor1(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set anchor2 for double ball joint + * @ingroup joints + */ +ODE_API void dJointSetDBallAnchor2(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get anchor1 from double ball joint + * @ingroup joints + */ +ODE_API void dJointGetDBallAnchor1(dJointID, dVector3 result); + +/** + * @brief get anchor2 from double ball joint + * @ingroup joints + */ +ODE_API void dJointGetDBallAnchor2(dJointID, dVector3 result); + +/** + * @brief get the target distance from double ball joint + * @ingroup joints + */ +ODE_API dReal dJointGetDBallDistance(dJointID); + +/** + * @brief set the target distance for the double ball joint + * @ingroup joints + */ +ODE_API void dJointSetDBallDistance(dJointID, dReal dist); + +/** + * @brief set double ball joint parameter + * @ingroup joints + */ +ODE_API void dJointSetDBallParam(dJointID, int parameter, dReal value); + +/** + * @brief get double ball joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetDBallParam(dJointID, int parameter); + +/** + * @brief set axis for double hinge joint + * @ingroup joints + */ +ODE_API void dJointSetDHingeAxis(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get axis for double hinge joint + * @ingroup joints + */ +ODE_API void dJointGetDHingeAxis(dJointID, dVector3 result); + +/** + * @brief set anchor1 for double hinge joint + * @ingroup joints + */ +ODE_API void dJointSetDHingeAnchor1(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief set anchor2 for double hinge joint + * @ingroup joints + */ +ODE_API void dJointSetDHingeAnchor2(dJointID, dReal x, dReal y, dReal z); + +/** + * @brief get anchor1 from double hinge joint + * @ingroup joints + */ +ODE_API void dJointGetDHingeAnchor1(dJointID, dVector3 result); + +/** + * @brief get anchor2 from double hinge joint + * @ingroup joints + */ +ODE_API void dJointGetDHingeAnchor2(dJointID, dVector3 result); + +/** + * @brief get the set distance from double hinge joint + * @ingroup joints + */ +ODE_API dReal dJointGetDHingeDistance(dJointID); + +/** + * @brief set double hinge joint parameter + * @ingroup joints + */ +ODE_API void dJointSetDHingeParam(dJointID, int parameter, dReal value); + +/** + * @brief get double hinge joint parameter + * @ingroup joints + */ +ODE_API dReal dJointGetDHingeParam(dJointID, int parameter); + + + + +/** + * @ingroup joints + */ +ODE_API dJointID dConnectingJoint (dBodyID, dBodyID); + +/** + * @ingroup joints + */ +ODE_API int dConnectingJointList (dBodyID, dBodyID, dJointID*); + +/** + * @brief Utility function + * @return 1 if the two bodies are connected together by + * a joint, otherwise return 0. + * @ingroup joints + */ +ODE_API int dAreConnected (dBodyID, dBodyID); + +/** + * @brief Utility function + * @return 1 if the two bodies are connected together by + * a joint that does not have type @arg{joint_type}, otherwise return 0. + * @param body1 A body to check. + * @param body2 A body to check. + * @param joint_type is a dJointTypeXXX constant. + * This is useful for deciding whether to add contact joints between two bodies: + * if they are already connected by non-contact joints then it may not be + * appropriate to add contacts, however it is okay to add more contact between- + * bodies that already have contacts. + * @ingroup joints + */ +ODE_API int dAreConnectedExcluding (dBodyID body1, dBodyID body2, int joint_type); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/ode.h b/thirdparty/ode-0.16.5/include/ode/ode.h new file mode 100644 index 0000000..a69f46a --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/ode.h @@ -0,0 +1,56 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODE_H_ +#define _ODE_ODE_H_ + +/* include *everything* here */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +# include +# include +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/odeconfig.h b/thirdparty/ode-0.16.5/include/ode/odeconfig.h new file mode 100644 index 0000000..1a0c747 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odeconfig.h @@ -0,0 +1,218 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODECONFIG_H_ +#define _ODE_ODECONFIG_H_ + +/* Pull in the standard headers */ +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + + +#if defined(ODE_DLL) || defined(ODE_LIB) +#define __ODE__ +#endif + +/* Define a DLL export symbol for those platforms that need it */ +#if defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32)) + #if defined(ODE_DLL) + #define ODE_API __declspec(dllexport) + #else + #define ODE_API + #endif +#endif + +#if !defined(ODE_API) + #define ODE_API +#endif + +#if defined(_MSC_VER) +# define ODE_API_DEPRECATED __declspec(deprecated) +#elif defined (__GNUC__) && ( (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) ) +# define ODE_API_DEPRECATED __attribute__((__deprecated__)) +#else +# define ODE_API_DEPRECATED +#endif + +#define ODE_PURE_INLINE static __inline +#define ODE_INLINE __inline + +#if defined(__cplusplus) + #define ODE_EXTERN_C extern "C" +#else + #define ODE_EXTERN_C +#endif + +#if defined(__GNUC__) +#define ODE_NORETURN __attribute__((noreturn)) +#elif defined(_MSC_VER) +#define ODE_NORETURN __declspec(noreturn) +#else // #if !defined(_MSC_VER) +#define ODE_NORETURN +#endif // #if !defined(__GNUC__) + + +/* Well-defined common data types...need to be defined for 64 bit systems */ +#if defined(__aarch64__) || defined(__alpha__) || defined(__ppc64__) \ + || defined(__s390__) || defined(__s390x__) || defined(__zarch__) \ + || defined(__mips__) || defined(__powerpc64__) || defined(__riscv) \ + || (defined(__sparc__) && defined(__arch64__)) + #include + typedef int64_t dint64; + typedef uint64_t duint64; + typedef int32_t dint32; + typedef uint32_t duint32; + typedef int16_t dint16; + typedef uint16_t duint16; + typedef int8_t dint8; + typedef uint8_t duint8; + + typedef intptr_t dintptr; + typedef uintptr_t duintptr; + typedef ptrdiff_t ddiffint; + typedef size_t dsizeint; + +#elif (defined(_M_IA64) || defined(__ia64__) || defined(_M_AMD64) || defined(__x86_64__)) && !defined(__ILP32__) && !defined(_ILP32) + #define X86_64_SYSTEM 1 +#if defined(_MSC_VER) + typedef __int64 dint64; + typedef unsigned __int64 duint64; +#else +#if defined(_LP64) || defined(__LP64__) +typedef long dint64; +typedef unsigned long duint64; +#else + typedef long long dint64; + typedef unsigned long long duint64; +#endif +#endif + typedef int dint32; + typedef unsigned int duint32; + typedef short dint16; + typedef unsigned short duint16; + typedef signed char dint8; + typedef unsigned char duint8; + + typedef dint64 dintptr; + typedef duint64 duintptr; + typedef dint64 ddiffint; + typedef duint64 dsizeint; + +#else +#if defined(_MSC_VER) + typedef __int64 dint64; + typedef unsigned __int64 duint64; +#else + typedef long long dint64; + typedef unsigned long long duint64; +#endif + typedef int dint32; + typedef unsigned int duint32; + typedef short dint16; + typedef unsigned short duint16; + typedef signed char dint8; + typedef unsigned char duint8; + + typedef dint32 dintptr; + typedef duint32 duintptr; + typedef dint32 ddiffint; + typedef duint32 dsizeint; + +#endif + + +/* Define the dInfinity macro */ +#ifdef INFINITY + #ifdef dSINGLE + #define dInfinity ((float)INFINITY) + #else + #define dInfinity ((double)INFINITY) + #endif +#elif defined(HUGE_VAL) + #ifdef dSINGLE + #ifdef HUGE_VALF + #define dInfinity HUGE_VALF + #else + #define dInfinity ((float)HUGE_VAL) + #endif + #else + #define dInfinity HUGE_VAL + #endif +#else + #ifdef dSINGLE + #define dInfinity ((float)(1.0/0.0)) + #else + #define dInfinity (1.0/0.0) + #endif +#endif + + +/* Define the dNaN macro */ +#if defined(NAN) + #define dNaN NAN +#elif defined(__GNUC__) + #define dNaN ({ union { duint32 m_ui; float m_f; } un; un.m_ui = 0x7FC00000; un.m_f; }) +#elif defined(__cplusplus) + union _dNaNUnion + { + _dNaNUnion(): m_ui(0x7FC00000) {} + duint32 m_ui; + float m_f; + }; + #define dNaN (_dNaNUnion().m_f) +#else + #ifdef dSINGLE + #define dNaN ((float)(dInfinity - dInfinity)) + #else + #define dNaN (dInfinity - dInfinity) + #endif +#endif + + + /* Visual C does not define these functions */ +#if defined(_MSC_VER) + #define _ode_copysignf(x, y) ((float)_copysign(x, y)) + #define _ode_copysign(x, y) _copysign(x, y) + #define _ode_nextafterf(x, y) _nextafterf(x, y) + #define _ode_nextafter(x, y) _nextafter(x, y) + #if !defined(_WIN64) && defined(dSINGLE) + #define _ODE__NEXTAFTERF_REQUIRED + ODE_EXTERN_C float _nextafterf(float x, float y); + #endif +#else + #define _ode_copysignf(x, y) copysignf(x, y) + #define _ode_copysign(x, y) copysign(x, y) + #define _ode_nextafterf(x, y) nextafterf(x, y) + #define _ode_nextafter(x, y) nextafter(x, y) +#endif + + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/odecpp.h b/thirdparty/ode-0.16.5/include/ode/odecpp.h new file mode 100644 index 0000000..f604d0d --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odecpp.h @@ -0,0 +1,1355 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* C++ interface for non-collision stuff */ + + +#ifndef _ODE_ODECPP_H_ +#define _ODE_ODECPP_H_ +#ifdef __cplusplus + + + + +//namespace ode { + + +class dWorldSimpleIDContainer { +protected: + dWorldID _id; + + dWorldSimpleIDContainer(): _id(0) {} + ~dWorldSimpleIDContainer() { destroy(); } + + void destroy() { + if (_id) { + dWorldDestroy(_id); + _id = 0; + } + } +}; + +class dWorldDynamicIDContainer: public dWorldSimpleIDContainer { +protected: + virtual ~dWorldDynamicIDContainer() {} +}; + +template +class dWorldTemplate: public dWorldTemplateBase { + // intentionally undefined, don't use these + dWorldTemplate (const dWorldTemplate &); + void operator= (const dWorldTemplate &); + +protected: + dWorldID get_id() const { return dWorldTemplateBase::_id; } + void set_id(dWorldID value) { dWorldTemplateBase::_id = value; } + +public: + dWorldTemplate() + { set_id(dWorldCreate()); } + + dWorldID id() const + { return get_id(); } + operator dWorldID() const + { return get_id(); } + + void setGravity (dReal x, dReal y, dReal z) + { dWorldSetGravity (get_id(), x, y, z); } + void setGravity (const dVector3 g) + { setGravity (g[0], g[1], g[2]); } + void getGravity (dVector3 g) const + { dWorldGetGravity (get_id(), g); } + + void setERP (dReal erp) + { dWorldSetERP(get_id(), erp); } + dReal getERP() const + { return dWorldGetERP(get_id()); } + + void setCFM (dReal cfm) + { dWorldSetCFM(get_id(), cfm); } + dReal getCFM() const + { return dWorldGetCFM(get_id()); } + + void step (dReal stepsize) + { dWorldStep (get_id(), stepsize); } + + void quickStep(dReal stepsize) + { dWorldQuickStep (get_id(), stepsize); } + void setQuickStepNumIterations(int num) + { dWorldSetQuickStepNumIterations (get_id(), num); } + int getQuickStepNumIterations() const + { return dWorldGetQuickStepNumIterations (get_id()); } + void setQuickStepW(dReal over_relaxation) + { dWorldSetQuickStepW (get_id(), over_relaxation); } + dReal getQuickStepW() const + { return dWorldGetQuickStepW (get_id()); } + + void setAutoDisableLinearThreshold (dReal threshold) + { dWorldSetAutoDisableLinearThreshold (get_id(), threshold); } + dReal getAutoDisableLinearThreshold() const + { return dWorldGetAutoDisableLinearThreshold (get_id()); } + void setAutoDisableAngularThreshold (dReal threshold) + { dWorldSetAutoDisableAngularThreshold (get_id(), threshold); } + dReal getAutoDisableAngularThreshold() const + { return dWorldGetAutoDisableAngularThreshold (get_id()); } + void setAutoDisableSteps (int steps) + { dWorldSetAutoDisableSteps (get_id(), steps); } + int getAutoDisableSteps() const + { return dWorldGetAutoDisableSteps (get_id()); } + void setAutoDisableTime (dReal time) + { dWorldSetAutoDisableTime (get_id(), time); } + dReal getAutoDisableTime() const + { return dWorldGetAutoDisableTime (get_id()); } + void setAutoDisableFlag (int do_auto_disable) + { dWorldSetAutoDisableFlag (get_id(), do_auto_disable); } + int getAutoDisableFlag() const + { return dWorldGetAutoDisableFlag (get_id()); } + + dReal getLinearDampingThreshold() const + { return dWorldGetLinearDampingThreshold(get_id()); } + void setLinearDampingThreshold(dReal threshold) + { dWorldSetLinearDampingThreshold(get_id(), threshold); } + dReal getAngularDampingThreshold() const + { return dWorldGetAngularDampingThreshold(get_id()); } + void setAngularDampingThreshold(dReal threshold) + { dWorldSetAngularDampingThreshold(get_id(), threshold); } + dReal getLinearDamping() const + { return dWorldGetLinearDamping(get_id()); } + void setLinearDamping(dReal scale) + { dWorldSetLinearDamping(get_id(), scale); } + dReal getAngularDamping() const + { return dWorldGetAngularDamping(get_id()); } + void setAngularDamping(dReal scale) + { dWorldSetAngularDamping(get_id(), scale); } + void setDamping(dReal linear_scale, dReal angular_scale) + { dWorldSetDamping(get_id(), linear_scale, angular_scale); } + + dReal getMaxAngularSpeed() const + { return dWorldGetMaxAngularSpeed(get_id()); } + void setMaxAngularSpeed(dReal max_speed) + { dWorldSetMaxAngularSpeed(get_id(), max_speed); } + + void setContactSurfaceLayer(dReal depth) + { dWorldSetContactSurfaceLayer (get_id(), depth); } + dReal getContactSurfaceLayer() const + { return dWorldGetContactSurfaceLayer (get_id()); } + + void impulseToForce (dReal stepsize, dReal ix, dReal iy, dReal iz, + dVector3 force) + { dWorldImpulseToForce (get_id(), stepsize, ix, iy, iz, force); } +}; + + +class dBodySimpleIDContainer { +protected: + dBodyID _id; + + dBodySimpleIDContainer(): _id(0) {} + ~dBodySimpleIDContainer() { destroy(); } + + void destroy() { + if (_id) { + dBodyDestroy(_id); + _id = 0; + } + } +}; + +class dBodyDynamicIDContainer: public dBodySimpleIDContainer { +protected: + virtual ~dBodyDynamicIDContainer() {} +}; + +template +class dBodyTemplate: public dBodyTemplateBase { + // intentionally undefined, don't use these + dBodyTemplate (const dBodyTemplate &); + void operator= (const dBodyTemplate &); + +protected: + dBodyID get_id() const { return dBodyTemplateBase::_id; } + void set_id(dBodyID value) { dBodyTemplateBase::_id = value; } + + void destroy() { dBodyTemplateBase::destroy(); } + +public: + dBodyTemplate() + { } + dBodyTemplate (dWorldID world) + { set_id(dBodyCreate(world)); } + dBodyTemplate (dWorldTemplate& world) + { set_id(dBodyCreate(world.id())); } + + void create (dWorldID world) { + destroy(); + set_id(dBodyCreate(world)); + } + void create (dWorldTemplate& world) { + create(world.id()); + } + + dBodyID id() const + { return get_id(); } + operator dBodyID() const + { return get_id(); } + + void setData (void *data) + { dBodySetData (get_id(), data); } + void *getData() const + { return dBodyGetData (get_id()); } + + void setPosition (dReal x, dReal y, dReal z) + { dBodySetPosition (get_id(), x, y, z); } + void setPosition (const dVector3 p) + { setPosition(p[0], p[1], p[2]); } + + void setRotation (const dMatrix3 R) + { dBodySetRotation (get_id(), R); } + void setQuaternion (const dQuaternion q) + { dBodySetQuaternion (get_id(), q); } + void setLinearVel (dReal x, dReal y, dReal z) + { dBodySetLinearVel (get_id(), x, y, z); } + void setLinearVel (const dVector3 v) + { setLinearVel(v[0], v[1], v[2]); } + void setAngularVel (dReal x, dReal y, dReal z) + { dBodySetAngularVel (get_id(), x, y, z); } + void setAngularVel (const dVector3 v) + { setAngularVel (v[0], v[1], v[2]); } + + const dReal * getPosition() const + { return dBodyGetPosition (get_id()); } + const dReal * getRotation() const + { return dBodyGetRotation (get_id()); } + const dReal * getQuaternion() const + { return dBodyGetQuaternion (get_id()); } + const dReal * getLinearVel() const + { return dBodyGetLinearVel (get_id()); } + const dReal * getAngularVel() const + { return dBodyGetAngularVel (get_id()); } + + void setMass (const dMass *mass) + { dBodySetMass (get_id(), mass); } + void setMass (const dMass &mass) + { setMass (&mass); } + dMass getMass () const + { dMass mass; dBodyGetMass (get_id(), &mass); return mass; } + + void addForce (dReal fx, dReal fy, dReal fz) + { dBodyAddForce (get_id(), fx, fy, fz); } + void addForce (const dVector3 f) + { addForce (f[0], f[1], f[2]); } + void addTorque (dReal fx, dReal fy, dReal fz) + { dBodyAddTorque (get_id(), fx, fy, fz); } + void addTorque (const dVector3 t) + { addTorque(t[0], t[1], t[2]); } + + void addRelForce (dReal fx, dReal fy, dReal fz) + { dBodyAddRelForce (get_id(), fx, fy, fz); } + void addRelForce (const dVector3 f) + { addRelForce (f[0], f[1], f[2]); } + void addRelTorque (dReal fx, dReal fy, dReal fz) + { dBodyAddRelTorque (get_id(), fx, fy, fz); } + void addRelTorque (const dVector3 t) + { addRelTorque (t[0], t[1], t[2]); } + + void addForceAtPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddForceAtPos (get_id(), fx, fy, fz, px, py, pz); } + void addForceAtPos (const dVector3 f, const dVector3 p) + { addForceAtPos (f[0], f[1], f[2], p[0], p[1], p[2]); } + + void addForceAtRelPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddForceAtRelPos (get_id(), fx, fy, fz, px, py, pz); } + void addForceAtRelPos (const dVector3 f, const dVector3 p) + { addForceAtRelPos (f[0], f[1], f[2], p[0], p[1], p[2]); } + + void addRelForceAtPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddRelForceAtPos (get_id(), fx, fy, fz, px, py, pz); } + void addRelForceAtPos (const dVector3 f, const dVector3 p) + { addRelForceAtPos (f[0], f[1], f[2], p[0], p[1], p[2]); } + + void addRelForceAtRelPos (dReal fx, dReal fy, dReal fz, + dReal px, dReal py, dReal pz) + { dBodyAddRelForceAtRelPos (get_id(), fx, fy, fz, px, py, pz); } + void addRelForceAtRelPos (const dVector3 f, const dVector3 p) + { addRelForceAtRelPos (f[0], f[1], f[2], p[0], p[1], p[2]); } + + const dReal * getForce() const + { return dBodyGetForce(get_id()); } + const dReal * getTorque() const + { return dBodyGetTorque(get_id()); } + void setForce (dReal x, dReal y, dReal z) + { dBodySetForce (get_id(), x, y, z); } + void setForce (const dVector3 f) + { setForce (f[0], f[1], f[2]); } + void setTorque (dReal x, dReal y, dReal z) + { dBodySetTorque (get_id(), x, y, z); } + void setTorque (const dVector3 t) + { setTorque (t[0], t[1], t[2]); } + + void setDynamic() + { dBodySetDynamic (get_id()); } + void setKinematic() + { dBodySetKinematic (get_id()); } + bool isKinematic() const + { return dBodyIsKinematic (get_id()) != 0; } + + void enable() + { dBodyEnable (get_id()); } + void disable() + { dBodyDisable (get_id()); } + bool isEnabled() const + { return dBodyIsEnabled (get_id()) != 0; } + + void getRelPointPos (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetRelPointPos (get_id(), px, py, pz, result); } + void getRelPointPos (const dVector3 p, dVector3 result) const + { getRelPointPos (p[0], p[1], p[2], result); } + + void getRelPointVel (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetRelPointVel (get_id(), px, py, pz, result); } + void getRelPointVel (const dVector3 p, dVector3 result) const + { getRelPointVel (p[0], p[1], p[2], result); } + + void getPointVel (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetPointVel (get_id(), px, py, pz, result); } + void getPointVel (const dVector3 p, dVector3 result) const + { getPointVel (p[0], p[1], p[2], result); } + + void getPosRelPoint (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyGetPosRelPoint (get_id(), px, py, pz, result); } + void getPosRelPoint (const dVector3 p, dVector3 result) const + { getPosRelPoint (p[0], p[1], p[2], result); } + + void vectorToWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyVectorToWorld (get_id(), px, py, pz, result); } + void vectorToWorld (const dVector3 p, dVector3 result) const + { vectorToWorld (p[0], p[1], p[2], result); } + + void vectorFromWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dBodyVectorFromWorld (get_id(), px, py, pz, result); } + void vectorFromWorld (const dVector3 p, dVector3 result) const + { vectorFromWorld (p[0], p[1], p[2], result); } + + void setFiniteRotationMode (bool mode) + { dBodySetFiniteRotationMode (get_id(), mode); } + + void setFiniteRotationAxis (dReal x, dReal y, dReal z) + { dBodySetFiniteRotationAxis (get_id(), x, y, z); } + void setFiniteRotationAxis (const dVector3 a) + { setFiniteRotationAxis (a[0], a[1], a[2]); } + + bool getFiniteRotationMode() const + { return dBodyGetFiniteRotationMode (get_id()) != 0; } + void getFiniteRotationAxis (dVector3 result) const + { dBodyGetFiniteRotationAxis (get_id(), result); } + + int getNumJoints() const + { return dBodyGetNumJoints (get_id()); } + dJointID getJoint (int index) const + { return dBodyGetJoint (get_id(), index); } + + void setGravityMode (bool mode) + { dBodySetGravityMode (get_id(), mode); } + bool getGravityMode() const + { return dBodyGetGravityMode (get_id()) != 0; } + + bool isConnectedTo (dBodyID body) const + { return dAreConnected (get_id(), body) != 0; } + + void setAutoDisableLinearThreshold (dReal threshold) + { dBodySetAutoDisableLinearThreshold (get_id(), threshold); } + dReal getAutoDisableLinearThreshold() const + { return dBodyGetAutoDisableLinearThreshold (get_id()); } + void setAutoDisableAngularThreshold (dReal threshold) + { dBodySetAutoDisableAngularThreshold (get_id(), threshold); } + dReal getAutoDisableAngularThreshold() const + { return dBodyGetAutoDisableAngularThreshold (get_id()); } + void setAutoDisableSteps (int steps) + { dBodySetAutoDisableSteps (get_id(), steps); } + int getAutoDisableSteps() const + { return dBodyGetAutoDisableSteps (get_id()); } + void setAutoDisableTime (dReal time) + { dBodySetAutoDisableTime (get_id(), time); } + dReal getAutoDisableTime() const + { return dBodyGetAutoDisableTime (get_id()); } + void setAutoDisableFlag (bool do_auto_disable) + { dBodySetAutoDisableFlag (get_id(), do_auto_disable); } + bool getAutoDisableFlag() const + { return dBodyGetAutoDisableFlag (get_id()) != 0; } + + dReal getLinearDamping() const + { return dBodyGetLinearDamping(get_id()); } + void setLinearDamping(dReal scale) + { dBodySetLinearDamping(get_id(), scale); } + dReal getAngularDamping() const + { return dBodyGetAngularDamping(get_id()); } + void setAngularDamping(dReal scale) + { dBodySetAngularDamping(get_id(), scale); } + void setDamping(dReal linear_scale, dReal angular_scale) + { dBodySetDamping(get_id(), linear_scale, angular_scale); } + dReal getLinearDampingThreshold() const + { return dBodyGetLinearDampingThreshold(get_id()); } + void setLinearDampingThreshold(dReal threshold) const + { dBodySetLinearDampingThreshold(get_id(), threshold); } + dReal getAngularDampingThreshold() const + { return dBodyGetAngularDampingThreshold(get_id()); } + void setAngularDampingThreshold(dReal threshold) + { dBodySetAngularDampingThreshold(get_id(), threshold); } + void setDampingDefaults() + { dBodySetDampingDefaults(get_id()); } + + dReal getMaxAngularSpeed() const + { return dBodyGetMaxAngularSpeed(get_id()); } + void setMaxAngularSpeed(dReal max_speed) + { dBodySetMaxAngularSpeed(get_id(), max_speed); } + + bool getGyroscopicMode() const + { return dBodyGetGyroscopicMode(get_id()) != 0; } + void setGyroscopicMode(bool mode) + { dBodySetGyroscopicMode(get_id(), mode); } + +}; + + +class dJointGroupSimpleIDContainer { +protected: + dJointGroupID _id; + + dJointGroupSimpleIDContainer(): _id(0) {} + ~dJointGroupSimpleIDContainer() { destroy(); } + + void destroy() { + if (_id) { + dJointGroupDestroy(_id); + _id = 0; + } + } +}; + +class dJointGroupDynamicIDContainer: public dJointGroupSimpleIDContainer { +protected: + virtual ~dJointGroupDynamicIDContainer() {} +}; + +template +class dJointGroupTemplate: public dJointGroupTemplateBase { + // intentionally undefined, don't use these + dJointGroupTemplate (const dJointGroupTemplate &); + void operator= (const dJointGroupTemplate &); + +protected: + dJointGroupID get_id() const { return dJointGroupTemplateBase::_id; } + void set_id(dJointGroupID value) { dJointGroupTemplateBase::_id = value; } + + void destroy() { dJointGroupTemplateBase::destroy(); } + +public: + dJointGroupTemplate () + { set_id(dJointGroupCreate(0)); } + + void create () { + destroy(); + set_id(dJointGroupCreate(0)); + } + + dJointGroupID id() const + { return get_id(); } + operator dJointGroupID() const + { return get_id(); } + + void empty() + { dJointGroupEmpty (get_id()); } + void clear() + { empty(); } +}; + + +class dJointSimpleIDContainer { +protected: + dJointID _id; + + dJointSimpleIDContainer(): _id(0) {} + ~dJointSimpleIDContainer() { destroy(); } + + void destroy() { + if (_id) { + dJointDestroy (_id); + _id = 0; + } + } +}; + +class dJointDynamicIDContainer: public dJointSimpleIDContainer { +protected: + virtual ~dJointDynamicIDContainer() {} +}; + +template +class dJointTemplate: public dJointTemplateBase { +private: + // intentionally undefined, don't use these + dJointTemplate (const dJointTemplate &) ; + void operator= (const dJointTemplate &); + +protected: + dJointID get_id() const { return dJointTemplateBase::_id; } + void set_id(dJointID value) { dJointTemplateBase::_id = value; } + + void destroy() { dJointTemplateBase::destroy(); } + +protected: + dJointTemplate() // don't let user construct pure dJointTemplate objects + { } + +public: + dJointID id() const + { return get_id(); } + operator dJointID() const + { return get_id(); } + + int getNumBodies() const + { return dJointGetNumBodies(get_id()); } + + void attach (dBodyID body1, dBodyID body2) + { dJointAttach (get_id(), body1, body2); } + void attach (dBodyTemplate& body1, dBodyTemplate& body2) + { attach(body1.id(), body2.id()); } + + void enable() + { dJointEnable (get_id()); } + void disable() + { dJointDisable (get_id()); } + bool isEnabled() const + { return dJointIsEnabled (get_id()) != 0; } + + void setData (void *data) + { dJointSetData (get_id(), data); } + void *getData() const + { return dJointGetData (get_id()); } + + dJointType getType() const + { return dJointGetType (get_id()); } + + dBodyID getBody (int index) const + { return dJointGetBody (get_id(), index); } + + void setFeedback(dJointFeedback *fb) + { dJointSetFeedback(get_id(), fb); } + dJointFeedback *getFeedback() const + { return dJointGetFeedback(get_id()); } + + // If not implemented it will do nothing as describe in the doc + virtual void setParam (int, dReal) {}; + virtual dReal getParam (int) const { return 0; } +}; + + +template +class dBallJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dBallJointTemplate (const dBallJointTemplate &); + void operator= (const dBallJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dBallJointTemplate() { } + dBallJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateBall(world, group)); } + dBallJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateBall(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateBall(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetBallAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor (a[0], a[1], a[2]); } + void getAnchor (dVector3 result) const + { dJointGetBallAnchor (get_id(), result); } + void getAnchor2 (dVector3 result) const + { dJointGetBallAnchor2 (get_id(), result); } + virtual void setParam (int parameter, dReal value) + { dJointSetBallParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetBallParam (get_id(), parameter); } + // TODO: expose params through methods +} ; + + +template +class dHingeJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dHingeJointTemplate (const dHingeJointTemplate &); + void operator = (const dHingeJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dHingeJointTemplate() { } + dHingeJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateHinge(world, group)); } + dHingeJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateHinge(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateHinge (world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetHingeAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor (a[0], a[1], a[2]); } + void getAnchor (dVector3 result) const + { dJointGetHingeAnchor (get_id(), result); } + void getAnchor2 (dVector3 result) const + { dJointGetHingeAnchor2 (get_id(), result); } + + void setAxis (dReal x, dReal y, dReal z) + { dJointSetHingeAxis (get_id(), x, y, z); } + void setAxis (const dVector3 a) + { setAxis(a[0], a[1], a[2]); } + void getAxis (dVector3 result) const + { dJointGetHingeAxis (get_id(), result); } + + dReal getAngle() const + { return dJointGetHingeAngle (get_id()); } + dReal getAngleRate() const + { return dJointGetHingeAngleRate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetHingeParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetHingeParam (get_id(), parameter); } + // TODO: expose params through methods + + void addTorque (dReal torque) + { dJointAddHingeTorque(get_id(), torque); } +}; + + +template +class dSliderJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dSliderJointTemplate (const dSliderJointTemplate &); + void operator = (const dSliderJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dSliderJointTemplate() { } + dSliderJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateSlider(world, group)); } + dSliderJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateSlider(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateSlider(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAxis (dReal x, dReal y, dReal z) + { dJointSetSliderAxis (get_id(), x, y, z); } + void setAxis (const dVector3 a) + { setAxis (a[0], a[1], a[2]); } + void getAxis (dVector3 result) const + { dJointGetSliderAxis (get_id(), result); } + + dReal getPosition() const + { return dJointGetSliderPosition (get_id()); } + dReal getPositionRate() const + { return dJointGetSliderPositionRate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetSliderParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetSliderParam (get_id(), parameter); } + // TODO: expose params through methods + + void addForce (dReal force) + { dJointAddSliderForce(get_id(), force); } +}; + + +template +class dUniversalJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dUniversalJointTemplate (const dUniversalJointTemplate &); + void operator = (const dUniversalJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dUniversalJointTemplate() { } + dUniversalJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateUniversal(world, group)); } + dUniversalJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateUniversal(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateUniversal(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetUniversalAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor(a[0], a[1], a[2]); } + void setAxis1 (dReal x, dReal y, dReal z) + { dJointSetUniversalAxis1 (get_id(), x, y, z); } + void setAxis1 (const dVector3 a) + { setAxis1 (a[0], a[1], a[2]); } + void setAxis2 (dReal x, dReal y, dReal z) + { dJointSetUniversalAxis2 (get_id(), x, y, z); } + void setAxis2 (const dVector3 a) + { setAxis2 (a[0], a[1], a[2]); } + + void getAnchor (dVector3 result) const + { dJointGetUniversalAnchor (get_id(), result); } + void getAnchor2 (dVector3 result) const + { dJointGetUniversalAnchor2 (get_id(), result); } + void getAxis1 (dVector3 result) const + { dJointGetUniversalAxis1 (get_id(), result); } + void getAxis2 (dVector3 result) const + { dJointGetUniversalAxis2 (get_id(), result); } + + virtual void setParam (int parameter, dReal value) + { dJointSetUniversalParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetUniversalParam (get_id(), parameter); } + // TODO: expose params through methods + + void getAngles(dReal *angle1, dReal *angle2) const + { dJointGetUniversalAngles (get_id(), angle1, angle2); } + + dReal getAngle1() const + { return dJointGetUniversalAngle1 (get_id()); } + dReal getAngle1Rate() const + { return dJointGetUniversalAngle1Rate (get_id()); } + dReal getAngle2() const + { return dJointGetUniversalAngle2 (get_id()); } + dReal getAngle2Rate() const + { return dJointGetUniversalAngle2Rate (get_id()); } + + void addTorques (dReal torque1, dReal torque2) + { dJointAddUniversalTorques(get_id(), torque1, torque2); } +}; + + +template +class dHinge2JointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dHinge2JointTemplate (const dHinge2JointTemplate &); + void operator = (const dHinge2JointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dHinge2JointTemplate() { } + dHinge2JointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateHinge2(world, group)); } + dHinge2JointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateHinge2(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateHinge2(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetHinge2Anchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor(a[0], a[1], a[2]); } + void setAxes (const dReal *axis1/*=NULL*/, const dReal *axis2/*=NULL*/) + { dJointSetHinge2Axes (get_id(), axis1, axis2); } + ODE_API_DEPRECATED void setAxis1 (dReal x, dReal y, dReal z) + { dVector3 a = { x, y, z }; dJointSetHinge2Axes (get_id(), a, NULL); } + ODE_API_DEPRECATED void setAxis1 (const dVector3 a) + { dJointSetHinge2Axes (get_id(), a, NULL); } + ODE_API_DEPRECATED void setAxis2 (dReal x, dReal y, dReal z) + { dVector3 a = { x, y, z }; dJointSetHinge2Axes (get_id(), NULL, a); } + ODE_API_DEPRECATED void setAxis2 (const dVector3 a) + { dJointSetHinge2Axes (get_id(), NULL, a); } + + void getAnchor (dVector3 result) const + { dJointGetHinge2Anchor (get_id(), result); } + void getAnchor2 (dVector3 result) const + { dJointGetHinge2Anchor2 (get_id(), result); } + void getAxis1 (dVector3 result) const + { dJointGetHinge2Axis1 (get_id(), result); } + void getAxis2 (dVector3 result) const + { dJointGetHinge2Axis2 (get_id(), result); } + + dReal getAngle1() const + { return dJointGetHinge2Angle1 (get_id()); } + dReal getAngle1Rate() const + { return dJointGetHinge2Angle1Rate (get_id()); } + dReal getAngle2Rate() const + { return dJointGetHinge2Angle2Rate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetHinge2Param (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetHinge2Param (get_id(), parameter); } + // TODO: expose params through methods + + void addTorques(dReal torque1, dReal torque2) + { dJointAddHinge2Torques(get_id(), torque1, torque2); } +}; + + +template +class dPRJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dPRJointTemplate (const dPRJointTemplate &); + void operator = (const dPRJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dPRJointTemplate() { } + dPRJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreatePR(world, group)); } + dPRJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreatePR(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreatePR(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetPRAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor (a[0], a[1], a[2]); } + void setAxis1 (dReal x, dReal y, dReal z) + { dJointSetPRAxis1 (get_id(), x, y, z); } + void setAxis1 (const dVector3 a) + { setAxis1(a[0], a[1], a[2]); } + void setAxis2 (dReal x, dReal y, dReal z) + { dJointSetPRAxis2 (get_id(), x, y, z); } + void setAxis2 (const dVector3 a) + { setAxis2(a[0], a[1], a[2]); } + + void getAnchor (dVector3 result) const + { dJointGetPRAnchor (get_id(), result); } + void getAxis1 (dVector3 result) const + { dJointGetPRAxis1 (get_id(), result); } + void getAxis2 (dVector3 result) const + { dJointGetPRAxis2 (get_id(), result); } + + dReal getPosition() const + { return dJointGetPRPosition (get_id()); } + dReal getPositionRate() const + { return dJointGetPRPositionRate (get_id()); } + + dReal getAngle() const + { return dJointGetPRAngle (get_id()); } + dReal getAngleRate() const + { return dJointGetPRAngleRate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetPRParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetPRParam (get_id(), parameter); } +}; + + + +template +class dPUJointTemplate : public dJointTemplate +{ +private: + // intentionally undefined, don't use these + dPUJointTemplate (const dPUJointTemplate &); + void operator = (const dPUJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dPUJointTemplate() { } + dPUJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreatePU(world, group)); } + dPUJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreatePU(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) + { + destroy(); + set_id(dJointCreatePU(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetPUAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor (a[0], a[1], a[2]); } + void setAxis1 (dReal x, dReal y, dReal z) + { dJointSetPUAxis1 (get_id(), x, y, z); } + void setAxis1 (const dVector3 a) + { setAxis1(a[0], a[1], a[2]); } + void setAxis2 (dReal x, dReal y, dReal z) + { dJointSetPUAxis2 (get_id(), x, y, z); } + void setAxis3 (dReal x, dReal y, dReal z) + { dJointSetPUAxis3 (get_id(), x, y, z); } + void setAxis3 (const dVector3 a) + { setAxis3(a[0], a[1], a[2]); } + void setAxisP (dReal x, dReal y, dReal z) + { dJointSetPUAxis3 (get_id(), x, y, z); } + void setAxisP (const dVector3 a) + { setAxisP(a[0], a[1], a[2]); } + + virtual void getAnchor (dVector3 result) const + { dJointGetPUAnchor (get_id(), result); } + void getAxis1 (dVector3 result) const + { dJointGetPUAxis1 (get_id(), result); } + void getAxis2 (dVector3 result) const + { dJointGetPUAxis2 (get_id(), result); } + void getAxis3 (dVector3 result) const + { dJointGetPUAxis3 (get_id(), result); } + void getAxisP (dVector3 result) const + { dJointGetPUAxis3 (get_id(), result); } + + dReal getAngle1() const + { return dJointGetPUAngle1 (get_id()); } + dReal getAngle1Rate() const + { return dJointGetPUAngle1Rate (get_id()); } + dReal getAngle2() const + { return dJointGetPUAngle2 (get_id()); } + dReal getAngle2Rate() const + { return dJointGetPUAngle2Rate (get_id()); } + + dReal getPosition() const + { return dJointGetPUPosition (get_id()); } + dReal getPositionRate() const + { return dJointGetPUPositionRate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetPUParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetPUParam (get_id(), parameter); } + // TODO: expose params through methods +}; + + + + + +template +class dPistonJointTemplate : public dJointTemplate +{ +private: + // intentionally undefined, don't use these + dPistonJointTemplate (const dPistonJointTemplate &); + void operator = (const dPistonJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dPistonJointTemplate() { } + dPistonJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreatePiston(world, group)); } + dPistonJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreatePiston(world, group)); } + + void create (dWorldID world, dJointGroupID group=0) + { + destroy(); + set_id(dJointCreatePiston(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setAnchor (dReal x, dReal y, dReal z) + { dJointSetPistonAnchor (get_id(), x, y, z); } + void setAnchor (const dVector3 a) + { setAnchor (a[0], a[1], a[2]); } + void getAnchor (dVector3 result) const + { dJointGetPistonAnchor (get_id(), result); } + void getAnchor2 (dVector3 result) const + { dJointGetPistonAnchor2 (get_id(), result); } + + void setAxis (dReal x, dReal y, dReal z) + { dJointSetPistonAxis (get_id(), x, y, z); } + void setAxis (const dVector3 a) + { setAxis(a[0], a[1], a[2]); } + void getAxis (dVector3 result) const + { dJointGetPistonAxis (get_id(), result); } + + dReal getPosition() const + { return dJointGetPistonPosition (get_id()); } + dReal getPositionRate() const + { return dJointGetPistonPositionRate (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetPistonParam (get_id(), parameter, value); } + virtual dReal getParam (int parameter) const + { return dJointGetPistonParam (get_id(), parameter); } + // TODO: expose params through methods + + void addForce (dReal force) + { dJointAddPistonForce (get_id(), force); } +}; + + + +template +class dFixedJointTemplate : public dJointTemplate +{ +private: + // intentionally undefined, don't use these + dFixedJointTemplate (const dFixedJointTemplate &); + void operator = (const dFixedJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dFixedJointTemplate() { } + dFixedJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateFixed(world, group)); } + dFixedJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateFixed(world, group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateFixed(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void set() + { dJointSetFixed (get_id()); } + + virtual void setParam (int parameter, dReal value) + { dJointSetFixedParam (get_id(), parameter, value); } + + virtual dReal getParam (int parameter) const + { return dJointGetFixedParam (get_id(), parameter); } + // TODO: expose params through methods +}; + + +template +class dContactJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dContactJointTemplate (const dContactJointTemplate &); + void operator = (const dContactJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dContactJointTemplate() { } + dContactJointTemplate (dWorldID world, dJointGroupID group, dContact *contact) + { set_id(dJointCreateContact(world, group, contact)); } + dContactJointTemplate (dWorldTemplate& world, dJointGroupID group, dContact *contact) + { set_id(dJointCreateContact(world.id(), group, contact)); } + + void create (dWorldID world, dJointGroupID group, dContact *contact) { + destroy(); + set_id(dJointCreateContact(world, group, contact)); + } + + void create (dWorldTemplate& world, dJointGroupID group, dContact *contact) + { create(world.id(), group, contact); } +}; + + +template +class dNullJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dNullJointTemplate (const dNullJointTemplate &); + void operator = (const dNullJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dNullJointTemplate() { } + dNullJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateNull(world, group)); } + dNullJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateNull (world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateNull(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } +}; + + +template +class dAMotorJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dAMotorJointTemplate (const dAMotorJointTemplate &); + void operator = (const dAMotorJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dAMotorJointTemplate() { } + dAMotorJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateAMotor(world, group)); } + dAMotorJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateAMotor(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateAMotor(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setMode (int mode) + { dJointSetAMotorMode (get_id(), mode); } + int getMode() const + { return dJointGetAMotorMode (get_id()); } + + void setNumAxes (int num) + { dJointSetAMotorNumAxes (get_id(), num); } + int getNumAxes() const + { return dJointGetAMotorNumAxes (get_id()); } + + void setAxis (int anum, int rel, dReal x, dReal y, dReal z) + { dJointSetAMotorAxis (get_id(), anum, rel, x, y, z); } + void setAxis (int anum, int rel, const dVector3 a) + { setAxis(anum, rel, a[0], a[1], a[2]); } + void getAxis (int anum, dVector3 result) const + { dJointGetAMotorAxis (get_id(), anum, result); } + int getAxisRel (int anum) const + { return dJointGetAMotorAxisRel (get_id(), anum); } + + void setAngle (int anum, dReal angle) + { dJointSetAMotorAngle (get_id(), anum, angle); } + dReal getAngle (int anum) const + { return dJointGetAMotorAngle (get_id(), anum); } + dReal getAngleRate (int anum) + { return dJointGetAMotorAngleRate (get_id(), anum); } + + void setParam (int parameter, dReal value) + { dJointSetAMotorParam (get_id(), parameter, value); } + dReal getParam (int parameter) const + { return dJointGetAMotorParam (get_id(), parameter); } + // TODO: expose params through methods + + void addTorques(dReal torque1, dReal torque2, dReal torque3) + { dJointAddAMotorTorques(get_id(), torque1, torque2, torque3); } +}; + + +template +class dLMotorJointTemplate : public dJointTemplate { +private: + // intentionally undefined, don't use these + dLMotorJointTemplate (const dLMotorJointTemplate &); + void operator = (const dLMotorJointTemplate &); + +protected: + typedef dJointTemplate dBaseTemplate; + + dJointID get_id() const { return dBaseTemplate::get_id(); } + void set_id(dJointID value) { dBaseTemplate::set_id(value); } + + void destroy() { dBaseTemplate::destroy(); } + +public: + dLMotorJointTemplate() { } + dLMotorJointTemplate (dWorldID world, dJointGroupID group=0) + { set_id(dJointCreateLMotor(world, group)); } + dLMotorJointTemplate (dWorldTemplate& world, dJointGroupID group=0) + { set_id(dJointCreateLMotor(world.id(), group)); } + + void create (dWorldID world, dJointGroupID group=0) { + destroy(); + set_id(dJointCreateLMotor(world, group)); + } + void create (dWorldTemplate& world, dJointGroupID group=0) + { create(world.id(), group); } + + void setNumAxes (int num) + { dJointSetLMotorNumAxes (get_id(), num); } + int getNumAxes() const + { return dJointGetLMotorNumAxes (get_id()); } + + void setAxis (int anum, int rel, dReal x, dReal y, dReal z) + { dJointSetLMotorAxis (get_id(), anum, rel, x, y, z); } + void setAxis (int anum, int rel, const dVector3 a) + { setAxis(anum, rel, a[0], a[1], a[2]); } + void getAxis (int anum, dVector3 result) const + { dJointGetLMotorAxis (get_id(), anum, result); } + + void setParam (int parameter, dReal value) + { dJointSetLMotorParam (get_id(), parameter, value); } + dReal getParam (int parameter) const + { return dJointGetLMotorParam (get_id(), parameter); } + // TODO: expose params through methods +}; + +//} + +#if !defined(dODECPP_WORLD_TEMPLATE_BASE) + +#if defined(dODECPP_BODY_TEMPLATE_BASE) || defined(dODECPP_JOINTGROUP_TEMPLATE_BASE) || defined(dODECPP_JOINT_TEMPLATE_BASE) +#error All the odecpp template bases must be defined or not defined together +#endif + +#define dODECPP_WORLD_TEMPLATE_BASE dWorldDynamicIDContainer +#define dODECPP_BODY_TEMPLATE_BASE dBodyDynamicIDContainer +#define dODECPP_JOINTGROUP_TEMPLATE_BASE dJointGroupDynamicIDContainer +#define dODECPP_JOINT_TEMPLATE_BASE dJointDynamicIDContainer + +#else // #if defined(dODECPP_WORLD_TEMPLATE_BASE) + +#if !defined(dODECPP_BODY_TEMPLATE_BASE) || !defined(dODECPP_JOINTGROUP_TEMPLATE_BASE) || !defined(dODECPP_JOINT_TEMPLATE_BASE) +#error All the odecpp template bases must be defined or not defined together +#endif + +#endif // #if defined(dODECPP_WORLD_TEMPLATE_BASE) + + +typedef dWorldTemplate dWorld; +typedef dBodyTemplate dBody; +typedef dJointGroupTemplate dJointGroup; +typedef dJointTemplate dJoint; +typedef dBallJointTemplate dBallJoint; +typedef dHingeJointTemplate dHingeJoint; +typedef dSliderJointTemplate dSliderJoint; +typedef dUniversalJointTemplate dUniversalJoint; +typedef dHinge2JointTemplate dHinge2Joint; +typedef dPRJointTemplate dPRJoint; +typedef dPUJointTemplate dPUJoint; +typedef dPistonJointTemplate dPistonJoint; +typedef dFixedJointTemplate dFixedJoint; +typedef dContactJointTemplate dContactJoint; +typedef dNullJointTemplate dNullJoint; +typedef dAMotorJointTemplate dAMotorJoint; +typedef dLMotorJointTemplate dLMotorJoint; + + +#endif +#endif + +// Local variables: +// mode:c++ +// c-basic-offset:2 +// End: diff --git a/thirdparty/ode-0.16.5/include/ode/odecpp_collision.h b/thirdparty/ode-0.16.5/include/ode/odecpp_collision.h new file mode 100644 index 0000000..55c05e7 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odecpp_collision.h @@ -0,0 +1,467 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* C++ interface for new collision API */ + + +#ifndef _ODE_ODECPP_COLLISION_H_ +#define _ODE_ODECPP_COLLISION_H_ +#ifdef __cplusplus + +//#include + +//namespace ode { + +class dGeom { + // intentionally undefined, don't use these + dGeom (dGeom &); + void operator= (dGeom &); + +protected: + dGeomID _id; + + dGeom() + { _id = 0; } +public: + ~dGeom() + { if (_id) dGeomDestroy (_id); } + + dGeomID id() const + { return _id; } + operator dGeomID() const + { return _id; } + + void destroy() { + if (_id) dGeomDestroy (_id); + _id = 0; + } + + int getClass() const + { return dGeomGetClass (_id); } + + dSpaceID getSpace() const + { return dGeomGetSpace (_id); } + + void setData (void *data) + { dGeomSetData (_id,data); } + void *getData() const + { return dGeomGetData (_id); } + + void setBody (dBodyID b) + { dGeomSetBody (_id,b); } + dBodyID getBody() const + { return dGeomGetBody (_id); } + + void setPosition (dReal x, dReal y, dReal z) + { dGeomSetPosition (_id,x,y,z); } + const dReal * getPosition() const + { return dGeomGetPosition (_id); } + + void setRotation (const dMatrix3 R) + { dGeomSetRotation (_id,R); } + const dReal * getRotation() const + { return dGeomGetRotation (_id); } + + void setQuaternion (const dQuaternion quat) + { dGeomSetQuaternion (_id,quat); } + void getQuaternion (dQuaternion quat) const + { dGeomGetQuaternion (_id,quat); } + + void getAABB (dReal aabb[6]) const + { dGeomGetAABB (_id, aabb); } + + int isSpace() + { return dGeomIsSpace (_id); } + + void setCategoryBits (unsigned long bits) + { dGeomSetCategoryBits (_id, bits); } + void setCollideBits (unsigned long bits) + { dGeomSetCollideBits (_id, bits); } + unsigned long getCategoryBits() + { return dGeomGetCategoryBits (_id); } + unsigned long getCollideBits() + { return dGeomGetCollideBits (_id); } + + void enable() + { dGeomEnable (_id); } + void disable() + { dGeomDisable (_id); } + int isEnabled() + { return dGeomIsEnabled (_id); } + + void getRelPointPos (dReal px, dReal py, dReal pz, dVector3 result) const + { dGeomGetRelPointPos (_id, px, py, pz, result); } + void getRelPointPos (const dVector3 p, dVector3 result) const + { getRelPointPos (p[0], p[1], p[2], result); } + + void getPosRelPoint (dReal px, dReal py, dReal pz, dVector3 result) const + { dGeomGetPosRelPoint (_id, px, py, pz, result); } + void getPosRelPoint (const dVector3 p, dVector3 result) const + { getPosRelPoint (p[0], p[1], p[2], result); } + + void vectorToWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dGeomVectorToWorld (_id, px, py, pz, result); } + void vectorToWorld (const dVector3 p, dVector3 result) const + { vectorToWorld (p[0], p[1], p[2], result); } + + void vectorFromWorld (dReal px, dReal py, dReal pz, dVector3 result) const + { dGeomVectorFromWorld (_id, px, py, pz, result); } + void vectorFromWorld (const dVector3 p, dVector3 result) const + { vectorFromWorld (p[0], p[1], p[2], result); } + + void collide2 (dGeomID g, void *data, dNearCallback *callback) + { dSpaceCollide2 (_id,g,data,callback); } +}; + + +class dSpace : public dGeom { + // intentionally undefined, don't use these + dSpace (dSpace &); + void operator= (dSpace &); + +protected: + // the default constructor is protected so that you + // can't instance this class. you must instance one + // of its subclasses instead. + dSpace () { _id = 0; } + +public: + dSpaceID id() const + { return (dSpaceID) _id; } + operator dSpaceID() const + { return (dSpaceID) _id; } + + void setCleanup (int mode) + { dSpaceSetCleanup (id(), mode); } + int getCleanup() + { return dSpaceGetCleanup (id()); } + + void add (dGeomID x) + { dSpaceAdd (id(), x); } + void remove (dGeomID x) + { dSpaceRemove (id(), x); } + int query (dGeomID x) + { return dSpaceQuery (id(),x); } + + int getNumGeoms() + { return dSpaceGetNumGeoms (id()); } + dGeomID getGeom (int i) + { return dSpaceGetGeom (id(),i); } + + void collide (void *data, dNearCallback *callback) + { dSpaceCollide (id(),data,callback); } +}; + + +class dSimpleSpace : public dSpace { + // intentionally undefined, don't use these + dSimpleSpace (dSimpleSpace &); + void operator= (dSimpleSpace &); + +public: + dSimpleSpace () + { _id = (dGeomID) dSimpleSpaceCreate (0); } + dSimpleSpace (dSpace &space) + { _id = (dGeomID) dSimpleSpaceCreate (space.id()); } + dSimpleSpace (dSpaceID space) + { _id = (dGeomID) dSimpleSpaceCreate (space); } +}; + + +class dHashSpace : public dSpace { + // intentionally undefined, don't use these + dHashSpace (dHashSpace &); + void operator= (dHashSpace &); + +public: + dHashSpace () + { _id = (dGeomID) dHashSpaceCreate (0); } + dHashSpace (dSpace &space) + { _id = (dGeomID) dHashSpaceCreate (space.id()); } + dHashSpace (dSpaceID space) + { _id = (dGeomID) dHashSpaceCreate (space); } + + void setLevels (int minlevel, int maxlevel) + { dHashSpaceSetLevels (id(),minlevel,maxlevel); } +}; + + +class dQuadTreeSpace : public dSpace { + // intentionally undefined, don't use these + dQuadTreeSpace (dQuadTreeSpace &); + void operator= (dQuadTreeSpace &); + +public: + dQuadTreeSpace (const dVector3 center, const dVector3 extents, int depth) + { _id = (dGeomID) dQuadTreeSpaceCreate (0,center,extents,depth); } + dQuadTreeSpace (dSpace &space, const dVector3 center, const dVector3 extents, int depth) + { _id = (dGeomID) dQuadTreeSpaceCreate (space.id(),center,extents,depth); } + dQuadTreeSpace (dSpaceID space, const dVector3 center, const dVector3 extents, int depth) + { _id = (dGeomID) dQuadTreeSpaceCreate (space,center,extents,depth); } +}; + + +class dSphere : public dGeom { + // intentionally undefined, don't use these + dSphere (dSphere &); + void operator= (dSphere &); + +public: + dSphere () { } + dSphere (dReal radius) + { _id = dCreateSphere (0, radius); } + dSphere (dSpace &space, dReal radius) + { _id = dCreateSphere (space.id(), radius); } + dSphere (dSpaceID space, dReal radius) + { _id = dCreateSphere (space, radius); } + + void create (dSpaceID space, dReal radius) { + if (_id) dGeomDestroy (_id); + _id = dCreateSphere (space, radius); + } + + void setRadius (dReal radius) + { dGeomSphereSetRadius (_id, radius); } + dReal getRadius() const + { return dGeomSphereGetRadius (_id); } +}; + + +class dBox : public dGeom { + // intentionally undefined, don't use these + dBox (dBox &); + void operator= (dBox &); + +public: + dBox () { } + dBox (dReal lx, dReal ly, dReal lz) + { _id = dCreateBox (0,lx,ly,lz); } + dBox (dSpace &space, dReal lx, dReal ly, dReal lz) + { _id = dCreateBox (space,lx,ly,lz); } + dBox (dSpaceID space, dReal lx, dReal ly, dReal lz) + { _id = dCreateBox (space,lx,ly,lz); } + + void create (dSpaceID space, dReal lx, dReal ly, dReal lz) { + if (_id) dGeomDestroy (_id); + _id = dCreateBox (space,lx,ly,lz); + } + + void setLengths (dReal lx, dReal ly, dReal lz) + { dGeomBoxSetLengths (_id, lx, ly, lz); } + void getLengths (dVector3 result) const + { dGeomBoxGetLengths (_id,result); } +}; + + +class dPlane : public dGeom { + // intentionally undefined, don't use these + dPlane (dPlane &); + void operator= (dPlane &); + +public: + dPlane() { } + dPlane (dReal a, dReal b, dReal c, dReal d) + { _id = dCreatePlane (0,a,b,c,d); } + dPlane (dSpace &space, dReal a, dReal b, dReal c, dReal d) + { _id = dCreatePlane (space.id(),a,b,c,d); } + dPlane (dSpaceID space, dReal a, dReal b, dReal c, dReal d) + { _id = dCreatePlane (space,a,b,c,d); } + + void create (dSpaceID space, dReal a, dReal b, dReal c, dReal d) { + if (_id) dGeomDestroy (_id); + _id = dCreatePlane (space,a,b,c,d); + } + + void setParams (dReal a, dReal b, dReal c, dReal d) + { dGeomPlaneSetParams (_id, a, b, c, d); } + void getParams (dVector4 result) const + { dGeomPlaneGetParams (_id,result); } +}; + + +class dCapsule : public dGeom { + // intentionally undefined, don't use these + dCapsule (dCapsule &); + void operator= (dCapsule &); + +public: + dCapsule() { } + dCapsule (dReal radius, dReal length) + { _id = dCreateCapsule (0,radius,length); } + dCapsule (dSpace &space, dReal radius, dReal length) + { _id = dCreateCapsule (space.id(),radius,length); } + dCapsule (dSpaceID space, dReal radius, dReal length) + { _id = dCreateCapsule (space,radius,length); } + + void create (dSpaceID space, dReal radius, dReal length) { + if (_id) dGeomDestroy (_id); + _id = dCreateCapsule (space,radius,length); + } + + void setParams (dReal radius, dReal length) + { dGeomCapsuleSetParams (_id, radius, length); } + void getParams (dReal *radius, dReal *length) const + { dGeomCapsuleGetParams (_id,radius,length); } +}; + + +class dCylinder : public dGeom { + // intentionally undefined, don't use these + dCylinder (dCylinder &); + void operator= (dCylinder &); + +public: + dCylinder() { } + dCylinder (dReal radius, dReal length) + { _id = dCreateCylinder (0,radius,length); } + dCylinder (dSpace &space, dReal radius, dReal length) + { _id = dCreateCylinder (space.id(),radius,length); } + dCylinder (dSpaceID space, dReal radius, dReal length) + { _id = dCreateCylinder (space,radius,length); } + + void create (dSpaceID space, dReal radius, dReal length) { + if (_id) dGeomDestroy (_id); + _id = dCreateCylinder (space,radius,length); + } + + void setParams (dReal radius, dReal length) + { dGeomCylinderSetParams (_id, radius, length); } + void getParams (dReal *radius, dReal *length) const + { dGeomCylinderGetParams (_id,radius,length); } +}; + + +class dRay : public dGeom { + // intentionally undefined, don't use these + dRay (dRay &); + void operator= (dRay &); + +public: + dRay() { } + dRay (dReal length) + { _id = dCreateRay (0,length); } + dRay (dSpace &space, dReal length) + { _id = dCreateRay (space.id(),length); } + dRay (dSpaceID space, dReal length) + { _id = dCreateRay (space,length); } + + void create (dSpaceID space, dReal length) { + if (_id) dGeomDestroy (_id); + _id = dCreateRay (space,length); + } + + void setLength (dReal length) + { dGeomRaySetLength (_id, length); } + dReal getLength() + { return dGeomRayGetLength (_id); } + + void set (dReal px, dReal py, dReal pz, dReal dx, dReal dy, dReal dz) + { dGeomRaySet (_id, px, py, pz, dx, dy, dz); } + void get (dVector3 start, dVector3 dir) + { dGeomRayGet (_id, start, dir); } + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4996 ) +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + ODE_API_DEPRECATED + void setParams (int firstContact, int backfaceCull) + { dGeomRaySetParams (_id, firstContact, backfaceCull); } + + ODE_API_DEPRECATED + void getParams (int *firstContact, int *backfaceCull) + { dGeomRayGetParams (_id, firstContact, backfaceCull); } +#ifdef _MSC_VER +#pragma warning( pop ) +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + void setBackfaceCull (int backfaceCull) + { dGeomRaySetBackfaceCull (_id, backfaceCull); } + int getBackfaceCull() + { return dGeomRayGetBackfaceCull (_id); } + + void setFirstContact (int firstContact) + { dGeomRaySetFirstContact (_id, firstContact); } + int getFirstContact() + { return dGeomRayGetFirstContact (_id); } + + void setClosestHit (int closestHit) + { dGeomRaySetClosestHit (_id, closestHit); } + int getClosestHit() + { return dGeomRayGetClosestHit (_id); } +}; + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4996 ) +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +class ODE_API_DEPRECATED dGeomTransform : public dGeom { + // intentionally undefined, don't use these + dGeomTransform (dGeomTransform &); + void operator= (dGeomTransform &); + +public: + dGeomTransform() { } + dGeomTransform (dSpace &space) + { _id = dCreateGeomTransform (space.id()); } + dGeomTransform (dSpaceID space) + { _id = dCreateGeomTransform (space); } + + void create (dSpaceID space=0) { + if (_id) dGeomDestroy (_id); + _id = dCreateGeomTransform (space); + } + + void setGeom (dGeomID geom) + { dGeomTransformSetGeom (_id, geom); } + dGeomID getGeom() const + { return dGeomTransformGetGeom (_id); } + + void setCleanup (int mode) + { dGeomTransformSetCleanup (_id,mode); } + int getCleanup () + { return dGeomTransformGetCleanup (_id); } + + void setInfo (int mode) + { dGeomTransformSetInfo (_id,mode); } + int getInfo() + { return dGeomTransformGetInfo (_id); } +}; + +#ifdef _MSC_VER +#pragma warning( pop ) +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +//} + +#endif +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/odeinit.h b/thirdparty/ode-0.16.5/include/ode/odeinit.h new file mode 100644 index 0000000..f5b9e51 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odeinit.h @@ -0,0 +1,236 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* Library initialization/finalization functions. */ + +#ifndef _ODE_ODEINIT_H_ +#define _ODE_ODEINIT_H_ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ************************************************************************ */ +/* Library initialization */ + +/** + * @defgroup init Library Initialization + * + * Library initialization functions prepare ODE internal data structures for use + * and release allocated resources after ODE is not needed anymore. + */ + + +/** + * @brief Library initialization flags. + * + * These flags define ODE library initialization options. + * + * @c dInitFlagManualThreadCleanup indicates that resources allocated in TLS for threads + * using ODE are to be cleared by library client with explicit call to @c dCleanupODEAllDataForThread. + * If this flag is not specified the automatic resource tracking algorithm is used. + * + * With automatic resource tracking, On Windows, memory allocated for a thread may + * remain not freed for some time after the thread exits. The resources may be + * released when one of other threads calls @c dAllocateODEDataForThread. Ultimately, + * the resources are released when library is closed with @c dCloseODE. On other + * operating systems resources are always released by the thread itself on its exit + * or on library closure with @c dCloseODE. + * + * With manual thread data cleanup mode every collision space object must be + * explicitly switched to manual cleanup mode with @c dSpaceSetManualCleanup + * after creation. See description of the function for more details. + * + * If @c dInitFlagManualThreadCleanup was not specified during initialization, + * calls to @c dCleanupODEAllDataForThread are not allowed. + * + * @see dInitODE2 + * @see dAllocateODEDataForThread + * @see dSpaceSetManualCleanup + * @see dCloseODE + * @ingroup init + */ +enum dInitODEFlags { + dInitFlagManualThreadCleanup = 0x00000001 /*@< Thread local data is to be cleared explicitly on @c dCleanupODEAllDataForThread function call*/ +}; + +/** + * @brief Initializes ODE library. + * + * @c dInitODE is obsolete. @c dInitODE2 is to be used for library initialization. + * + * A call to @c dInitODE is equal to the following initialization sequence + * @code + * dInitODE2(0); + * dAllocateODEDataForThread(dAllocateMaskAll); + * @endcode + * + * @see dInitODE2 + * @see dAllocateODEDataForThread + * @ingroup init + */ +ODE_API void dInitODE(void); + +/** + * @brief Initializes ODE library. + * @param uiInitFlags Initialization options bitmask + * @return A nonzero if initialization succeeded and zero otherwise. + * + * This function must be called to initialize ODE library before first use. If + * initialization succeeds the function may not be called again until library is + * closed with a call to @c dCloseODE. + * + * The @a uiInitFlags parameter specifies initialization options to be used. These + * can be combination of zero or more @c dInitODEFlags flags. + * + * @note + * If @c dInitFlagManualThreadCleanup flag is used for initialization, + * @c dSpaceSetManualCleanup must be called to set manual cleanup mode for every + * space object right after creation. Failure to do so may lead to resource leaks. + * + * @see dInitODEFlags + * @see dCloseODE + * @see dSpaceSetManualCleanup + * @ingroup init + */ +ODE_API int dInitODE2(unsigned int uiInitFlags/*=0*/); + + +/** + * @brief ODE data allocation flags. + * + * These flags are used to indicate which data is to be pre-allocated in call to + * @c dAllocateODEDataForThread. + * + * @c dAllocateFlagBasicData tells to allocate the basic data set required for + * normal library operation. This flag is equal to zero and is always implicitly + * included. + * + * @c dAllocateFlagCollisionData tells that collision detection data is to be allocated. + * Collision detection functions may not be called if the data has not be allocated + * in advance. If collision detection is not going to be used, it is not necessary + * to specify this flag. + * + * @c dAllocateMaskAll is a mask that can be used for allocating all possible + * data in cases when it is not known what exactly features of ODE will be used. + * The mask may not be used in combination with other flags. It is guaranteed to + * include all the current and future legal allocation flags. However, mature + * applications should use explicit flags they need rather than allocating everything. + * + * @see dAllocateODEDataForThread + * @ingroup init + */ +enum dAllocateODEDataFlags { + dAllocateFlagBasicData = 0, /*@< Allocate basic data required for library to operate*/ + + dAllocateFlagCollisionData = 0x00000001, /*@< Allocate data for collision detection*/ + + dAllocateMaskAll = ~0 /*@< Allocate all the possible data that is currently defined or will be defined in the future.*/ +}; + +/** + * @brief Allocate thread local data to allow the thread calling ODE. + * @param uiAllocateFlags Allocation options bitmask. + * @return A nonzero if allocation succeeded and zero otherwise. + * + * The function is required to be called for every thread that is going to use + * ODE. This function allocates the data that is required for accessing ODE from + * current thread along with optional data required for particular ODE subsystems. + * + * @a uiAllocateFlags parameter can contain zero or more flags from @c dAllocateODEDataFlags + * enumerated type. Multiple calls with different allocation flags are allowed. + * The flags that are already allocated are ignored in subsequent calls. If zero + * is passed as the parameter, it means to only allocate the set of most important + * data the library can not operate without. + * + * If the function returns failure status it means that none of the requested + * data has been allocated. The client may retry allocation attempt with the same + * flags when more system resources are available. + * + * @see dAllocateODEDataFlags + * @see dCleanupODEAllDataForThread + * @ingroup init + */ +ODE_API int dAllocateODEDataForThread(unsigned int uiAllocateFlags); + +/** + * @brief Free thread local data that was allocated for current thread. + * + * If library was initialized with @c dInitFlagManualThreadCleanup flag the function + * is required to be called on exit of every thread that was calling @c dAllocateODEDataForThread. + * Failure to call @c dCleanupODEAllDataForThread may result in some resources remaining + * not freed until program exit. The function may also be called when ODE is still + * being used to release resources allocated for all the current subsystems and + * possibly proceed with data pre-allocation for other subsystems. + * + * The function can safely be called several times in a row. The function can be + * called without prior invocation of @c dAllocateODEDataForThread. The function + * may not be called before ODE is initialized with @c dInitODE2 or after library + * has been closed with @c dCloseODE. A call to @c dCloseODE implicitly releases + * all the thread local resources that might be allocated for all the threads that + * were using ODE. + * + * If library was initialized without @c dInitFlagManualThreadCleanup flag + * @c dCleanupODEAllDataForThread must not be called. + * + * @see dAllocateODEDataForThread + * @see dInitODE2 + * @see dCloseODE + * @ingroup init + */ +ODE_API void dCleanupODEAllDataForThread(); + + +/** + * @brief Close ODE after it is not needed anymore. + * + * The function is required to be called when program does not need ODE features anymore. + * The call to @c dCloseODE releases all the resources allocated for library + * including all the thread local data that might be allocated for all the threads + * that were using ODE. + * + * @c dCloseODE is a paired function for @c dInitODE2 and must only be called + * after successful library initialization. + * + * @note Important! + * Make sure that all the threads that were using ODE have already terminated + * before calling @c dCloseODE. In particular it is not allowed to call + * @c dCleanupODEAllDataForThread after @c dCloseODE. + * + * @see dInitODE2 + * @see dCleanupODEAllDataForThread + * @ingroup init + */ +ODE_API void dCloseODE(void); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* _ODE_ODEINIT_H_ */ diff --git a/thirdparty/ode-0.16.5/include/ode/odemath.h b/thirdparty/ode-0.16.5/include/ode/odemath.h new file mode 100644 index 0000000..d4461b3 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odemath.h @@ -0,0 +1,545 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODEMATH_H_ +#define _ODE_ODEMATH_H_ + +#include + +/* + * macro to access elements i,j in an NxM matrix A, independent of the + * matrix storage convention. + */ +#define dACCESS33(A,i,j) ((A)[(i)*4+(j)]) + +/* + * Macro to test for valid floating point values + */ +#define dVALIDVEC3(v) (!(dIsNan(v[0]) || dIsNan(v[1]) || dIsNan(v[2]))) +#define dVALIDVEC4(v) (!(dIsNan(v[0]) || dIsNan(v[1]) || dIsNan(v[2]) || dIsNan(v[3]))) +#define dVALIDMAT3(m) (!(dIsNan(m[0]) || dIsNan(m[1]) || dIsNan(m[2]) || dIsNan(m[3]) || dIsNan(m[4]) || dIsNan(m[5]) || dIsNan(m[6]) || dIsNan(m[7]) || dIsNan(m[8]) || dIsNan(m[9]) || dIsNan(m[10]) || dIsNan(m[11]))) +#define dVALIDMAT4(m) (!(dIsNan(m[0]) || dIsNan(m[1]) || dIsNan(m[2]) || dIsNan(m[3]) || dIsNan(m[4]) || dIsNan(m[5]) || dIsNan(m[6]) || dIsNan(m[7]) || dIsNan(m[8]) || dIsNan(m[9]) || dIsNan(m[10]) || dIsNan(m[11]) || dIsNan(m[12]) || dIsNan(m[13]) || dIsNan(m[14]) || dIsNan(m[15]) )) + + +ODE_PURE_INLINE void dZeroVector3(dVector3 res) +{ + res[dV3E_X] = REAL(0.0); + res[dV3E_Y] = REAL(0.0); + res[dV3E_Z] = REAL(0.0); +} + +ODE_PURE_INLINE void dAssignVector3(dVector3 res, dReal x, dReal y, dReal z) +{ + res[dV3E_X] = x; + res[dV3E_Y] = y; + res[dV3E_Z] = z; +} + +ODE_PURE_INLINE void dZeroMatrix3(dMatrix3 res) +{ + res[dM3E_XX] = REAL(0.0); res[dM3E_XY] = REAL(0.0); res[dM3E_XZ] = REAL(0.0); + res[dM3E_YX] = REAL(0.0); res[dM3E_YY] = REAL(0.0); res[dM3E_YZ] = REAL(0.0); + res[dM3E_ZX] = REAL(0.0); res[dM3E_ZY] = REAL(0.0); res[dM3E_ZZ] = REAL(0.0); +} + +ODE_PURE_INLINE void dZeroMatrix4(dMatrix4 res) +{ + res[dM4E_XX] = REAL(0.0); res[dM4E_XY] = REAL(0.0); res[dM4E_XZ] = REAL(0.0); res[dM4E_XO] = REAL(0.0); + res[dM4E_YX] = REAL(0.0); res[dM4E_YY] = REAL(0.0); res[dM4E_YZ] = REAL(0.0); res[dM4E_YO] = REAL(0.0); + res[dM4E_ZX] = REAL(0.0); res[dM4E_ZY] = REAL(0.0); res[dM4E_ZZ] = REAL(0.0); res[dM4E_ZO] = REAL(0.0); + res[dM4E_OX] = REAL(0.0); res[dM4E_OY] = REAL(0.0); res[dM4E_OZ] = REAL(0.0); res[dM4E_OO] = REAL(0.0); +} + +/* Some vector math */ +ODE_PURE_INLINE void dAddVectors3(dReal *res, const dReal *a, const dReal *b) +{ + const dReal res_0 = a[0] + b[0]; + const dReal res_1 = a[1] + b[1]; + const dReal res_2 = a[2] + b[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dSubtractVectors3(dReal *res, const dReal *a, const dReal *b) +{ + const dReal res_0 = a[0] - b[0]; + const dReal res_1 = a[1] - b[1]; + const dReal res_2 = a[2] - b[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dAddVectorScaledVector3(dReal *res, const dReal *a, const dReal *b, dReal b_scale) +{ + const dReal res_0 = a[0] + b_scale * b[0]; + const dReal res_1 = a[1] + b_scale * b[1]; + const dReal res_2 = a[2] + b_scale * b[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dAddScaledVectors3(dReal *res, const dReal *a, const dReal *b, dReal a_scale, dReal b_scale) +{ + const dReal res_0 = a_scale * a[0] + b_scale * b[0]; + const dReal res_1 = a_scale * a[1] + b_scale * b[1]; + const dReal res_2 = a_scale * a[2] + b_scale * b[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dAddThreeScaledVectors3(dReal *res, const dReal *a, const dReal *b, const dReal *c, dReal a_scale, dReal b_scale, dReal c_scale) +{ + const dReal res_0 = a_scale * a[0] + b_scale * b[0] + c_scale * c[0]; + const dReal res_1 = a_scale * a[1] + b_scale * b[1] + c_scale * c[1]; + const dReal res_2 = a_scale * a[2] + b_scale * b[2] + c_scale * c[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dScaleVector3(dReal *res, dReal nScale) +{ + res[0] *= nScale ; + res[1] *= nScale ; + res[2] *= nScale ; +} + +ODE_PURE_INLINE void dNegateVector3(dReal *res) +{ + res[0] = -res[0]; + res[1] = -res[1]; + res[2] = -res[2]; +} + +ODE_PURE_INLINE void dCopyVector3(dReal *res, const dReal *a) +{ + const dReal res_0 = a[0]; + const dReal res_1 = a[1]; + const dReal res_2 = a[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dCopyScaledVector3(dReal *res, const dReal *a, dReal nScale) +{ + const dReal res_0 = a[0] * nScale; + const dReal res_1 = a[1] * nScale; + const dReal res_2 = a[2] * nScale; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dCopyNegatedVector3(dReal *res, const dReal *a) +{ + const dReal res_0 = -a[0]; + const dReal res_1 = -a[1]; + const dReal res_2 = -a[2]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dCopyVector4(dReal *res, const dReal *a) +{ + const dReal res_0 = a[0]; + const dReal res_1 = a[1]; + const dReal res_2 = a[2]; + const dReal res_3 = a[3]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; res[3] = res_3; +} + +ODE_PURE_INLINE void dCopyMatrix4x4(dReal *res, const dReal *a) +{ + dCopyVector4(res + 0, a + 0); + dCopyVector4(res + 4, a + 4); + dCopyVector4(res + 8, a + 8); +} + +ODE_PURE_INLINE void dCopyMatrix4x3(dReal *res, const dReal *a) +{ + dCopyVector3(res + 0, a + 0); + dCopyVector3(res + 4, a + 4); + dCopyVector3(res + 8, a + 8); +} + +ODE_PURE_INLINE void dGetMatrixColumn3(dReal *res, const dReal *a, unsigned n) +{ + const dReal res_0 = a[n + 0]; + const dReal res_1 = a[n + 4]; + const dReal res_2 = a[n + 8]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE dReal dCalcVectorLength3(const dReal *a) +{ + return dSqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); +} + +ODE_PURE_INLINE dReal dCalcVectorLengthSquare3(const dReal *a) +{ + return (a[0] * a[0] + a[1] * a[1] + a[2] * a[2]); +} + +ODE_PURE_INLINE dReal dCalcPointDepth3(const dReal *test_p, const dReal *plane_p, const dReal *plane_n) +{ + return (plane_p[0] - test_p[0]) * plane_n[0] + (plane_p[1] - test_p[1]) * plane_n[1] + (plane_p[2] - test_p[2]) * plane_n[2]; +} + + +/* +* 3-way dot product. _dCalcVectorDot3 means that elements of `a' and `b' are spaced +* step_a and step_b indexes apart respectively. dCalcVectorDot3() means dDot311. +*/ + +ODE_PURE_INLINE dReal _dCalcVectorDot3(const dReal *a, const dReal *b, unsigned step_a, unsigned step_b) +{ + return a[0] * b[0] + a[step_a] * b[step_b] + a[2 * step_a] * b[2 * step_b]; +} + + +ODE_PURE_INLINE dReal dCalcVectorDot3 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,1,1); } +ODE_PURE_INLINE dReal dCalcVectorDot3_13 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,1,3); } +ODE_PURE_INLINE dReal dCalcVectorDot3_31 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,3,1); } +ODE_PURE_INLINE dReal dCalcVectorDot3_33 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,3,3); } +ODE_PURE_INLINE dReal dCalcVectorDot3_14 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,1,4); } +ODE_PURE_INLINE dReal dCalcVectorDot3_41 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,4,1); } +ODE_PURE_INLINE dReal dCalcVectorDot3_44 (const dReal *a, const dReal *b) { return _dCalcVectorDot3(a,b,4,4); } + + +/* + * cross product, set res = a x b. _dCalcVectorCross3 means that elements of `res', `a' + * and `b' are spaced step_res, step_a and step_b indexes apart respectively. + * dCalcVectorCross3() means dCross3111. + */ + +ODE_PURE_INLINE void _dCalcVectorCross3(dReal *res, const dReal *a, const dReal *b, unsigned step_res, unsigned step_a, unsigned step_b) +{ + const dReal res_0 = a[ step_a]*b[2*step_b] - a[2*step_a]*b[ step_b]; + const dReal res_1 = a[2*step_a]*b[ 0] - a[ 0]*b[2*step_b]; + const dReal res_2 = a[ 0]*b[ step_b] - a[ step_a]*b[ 0]; + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[ 0] = res_0; + res[ step_res] = res_1; + res[2*step_res] = res_2; +} + +ODE_PURE_INLINE void dCalcVectorCross3 (dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 1, 1, 1); } +ODE_PURE_INLINE void dCalcVectorCross3_114(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 1, 1, 4); } +ODE_PURE_INLINE void dCalcVectorCross3_141(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 1, 4, 1); } +ODE_PURE_INLINE void dCalcVectorCross3_144(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 1, 4, 4); } +ODE_PURE_INLINE void dCalcVectorCross3_411(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 4, 1, 1); } +ODE_PURE_INLINE void dCalcVectorCross3_414(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 4, 1, 4); } +ODE_PURE_INLINE void dCalcVectorCross3_441(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 4, 4, 1); } +ODE_PURE_INLINE void dCalcVectorCross3_444(dReal *res, const dReal *a, const dReal *b) { _dCalcVectorCross3(res, a, b, 4, 4, 4); } + +ODE_PURE_INLINE void dAddVectorCross3(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dCalcVectorCross3(tmp, a, b); + dAddVectors3(res, res, tmp); +} + +ODE_PURE_INLINE void dSubtractVectorCross3(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dCalcVectorCross3(tmp, a, b); + dSubtractVectors3(res, res, tmp); +} + + +/* + * set a 3x3 submatrix of A to a matrix such that submatrix(A)*b = a x b. + * A is stored by rows, and has `skip' elements per row. the matrix is + * assumed to be already zero, so this does not write zero elements! + * if (plus,minus) is (+,-) then a positive version will be written. + * if (plus,minus) is (-,+) then a negative version will be written. + */ + +ODE_PURE_INLINE void dSetCrossMatrixPlus(dReal *res, const dReal *a, unsigned skip) +{ + const dReal a_0 = a[0], a_1 = a[1], a_2 = a[2]; + res[1] = -a_2; + res[2] = +a_1; + res[skip+0] = +a_2; + res[skip+2] = -a_0; + res[2*skip+0] = -a_1; + res[2*skip+1] = +a_0; +} + +ODE_PURE_INLINE void dSetCrossMatrixMinus(dReal *res, const dReal *a, unsigned skip) +{ + const dReal a_0 = a[0], a_1 = a[1], a_2 = a[2]; + res[1] = +a_2; + res[2] = -a_1; + res[skip+0] = -a_2; + res[skip+2] = +a_0; + res[2*skip+0] = +a_1; + res[2*skip+1] = -a_0; +} + + +/* + * compute the distance between two 3D-vectors + */ + +ODE_PURE_INLINE dReal dCalcPointsDistance3(const dReal *a, const dReal *b) +{ + dReal res; + dReal tmp[3]; + dSubtractVectors3(tmp, a, b); + res = dCalcVectorLength3(tmp); + return res; +} + +/* + * special case matrix multiplication, with operator selection + */ + +ODE_PURE_INLINE void dMultiplyHelper0_331(dReal *res, const dReal *a, const dReal *b) +{ + const dReal res_0 = dCalcVectorDot3(a, b); + const dReal res_1 = dCalcVectorDot3(a + 4, b); + const dReal res_2 = dCalcVectorDot3(a + 8, b); + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dMultiplyHelper1_331(dReal *res, const dReal *a, const dReal *b) +{ + const dReal res_0 = dCalcVectorDot3_41(a, b); + const dReal res_1 = dCalcVectorDot3_41(a + 1, b); + const dReal res_2 = dCalcVectorDot3_41(a + 2, b); + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +ODE_PURE_INLINE void dMultiplyHelper0_133(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper1_331(res, b, a); +} + +ODE_PURE_INLINE void dMultiplyHelper1_133(dReal *res, const dReal *a, const dReal *b) +{ + const dReal res_0 = dCalcVectorDot3_44(a, b); + const dReal res_1 = dCalcVectorDot3_44(a + 1, b); + const dReal res_2 = dCalcVectorDot3_44(a + 2, b); + /* Only assign after all the calculations are over to avoid incurring memory aliasing*/ + res[0] = res_0; res[1] = res_1; res[2] = res_2; +} + +/* +Note: NEVER call any of these functions/macros with the same variable for A and C, +it is not equivalent to A*=B. +*/ + +ODE_PURE_INLINE void dMultiply0_331(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper0_331(res, a, b); +} + +ODE_PURE_INLINE void dMultiply1_331(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper1_331(res, a, b); +} + +ODE_PURE_INLINE void dMultiply0_133(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper0_133(res, a, b); +} + +ODE_PURE_INLINE void dMultiply0_333(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper0_133(res + 0, a + 0, b); + dMultiplyHelper0_133(res + 4, a + 4, b); + dMultiplyHelper0_133(res + 8, a + 8, b); +} + +ODE_PURE_INLINE void dMultiply1_333(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper1_133(res + 0, b, a + 0); + dMultiplyHelper1_133(res + 4, b, a + 1); + dMultiplyHelper1_133(res + 8, b, a + 2); +} + +ODE_PURE_INLINE void dMultiply2_333(dReal *res, const dReal *a, const dReal *b) +{ + dMultiplyHelper0_331(res + 0, b, a + 0); + dMultiplyHelper0_331(res + 4, b, a + 4); + dMultiplyHelper0_331(res + 8, b, a + 8); +} + +ODE_PURE_INLINE void dMultiplyAdd0_331(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper0_331(tmp, a, b); + dAddVectors3(res, res, tmp); +} + +ODE_PURE_INLINE void dMultiplyAdd1_331(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper1_331(tmp, a, b); + dAddVectors3(res, res, tmp); +} + +ODE_PURE_INLINE void dMultiplyAdd0_133(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper0_133(tmp, a, b); + dAddVectors3(res, res, tmp); +} + +ODE_PURE_INLINE void dMultiplyAdd0_333(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper0_133(tmp, a + 0, b); + dAddVectors3(res+ 0, res + 0, tmp); + dMultiplyHelper0_133(tmp, a + 4, b); + dAddVectors3(res + 4, res + 4, tmp); + dMultiplyHelper0_133(tmp, a + 8, b); + dAddVectors3(res + 8, res + 8, tmp); +} + +ODE_PURE_INLINE void dMultiplyAdd1_333(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper1_133(tmp, b, a + 0); + dAddVectors3(res + 0, res + 0, tmp); + dMultiplyHelper1_133(tmp, b, a + 1); + dAddVectors3(res + 4, res + 4, tmp); + dMultiplyHelper1_133(tmp, b, a + 2); + dAddVectors3(res + 8, res + 8, tmp); +} + +ODE_PURE_INLINE void dMultiplyAdd2_333(dReal *res, const dReal *a, const dReal *b) +{ + dReal tmp[3]; + dMultiplyHelper0_331(tmp, b, a + 0); + dAddVectors3(res + 0, res + 0, tmp); + dMultiplyHelper0_331(tmp, b, a + 4); + dAddVectors3(res + 4, res + 4, tmp); + dMultiplyHelper0_331(tmp, b, a + 8); + dAddVectors3(res + 8, res + 8, tmp); +} + +ODE_PURE_INLINE dReal dCalcMatrix3Det( const dReal* mat ) +{ + dReal det; + + det = mat[0] * ( mat[5]*mat[10] - mat[9]*mat[6] ) + - mat[1] * ( mat[4]*mat[10] - mat[8]*mat[6] ) + + mat[2] * ( mat[4]*mat[9] - mat[8]*mat[5] ); + + return( det ); +} + +/** + Closed form matrix inversion, copied from + collision_util.h for use in the stepper. + + Returns the determinant. + returns 0 and does nothing + if the matrix is singular. +*/ +ODE_PURE_INLINE dReal dInvertMatrix3(dReal *dst, const dReal *ma) +{ + dReal det; + dReal detRecip; + + det = dCalcMatrix3Det( ma ); + + + /* Setting an arbitrary non-zero threshold + for the determinant doesn't do anyone + any favors. The condition number is the + important thing. If all the eigen-values + of the matrix are small, so is the + determinant, but it can still be well + conditioned. + A single extremely large eigen-value could + push the determinant over threshold, but + produce a very unstable result if the other + eigen-values are small. So we just say that + the determinant must be non-zero and trust the + caller to provide well-conditioned matrices. + */ + if ( det == 0 ) + { + return 0; + } + + detRecip = dRecip(det); + + dst[0] = ( ma[5]*ma[10] - ma[6]*ma[9] ) * detRecip; + dst[1] = ( ma[9]*ma[2] - ma[1]*ma[10] ) * detRecip; + dst[2] = ( ma[1]*ma[6] - ma[5]*ma[2] ) * detRecip; + + dst[4] = ( ma[6]*ma[8] - ma[4]*ma[10] ) * detRecip; + dst[5] = ( ma[0]*ma[10] - ma[8]*ma[2] ) * detRecip; + dst[6] = ( ma[4]*ma[2] - ma[0]*ma[6] ) * detRecip; + + dst[8] = ( ma[4]*ma[9] - ma[8]*ma[5] ) * detRecip; + dst[9] = ( ma[8]*ma[1] - ma[0]*ma[9] ) * detRecip; + dst[10] = ( ma[0]*ma[5] - ma[1]*ma[4] ) * detRecip; + + return det; +} + + +/* Include legacy macros here */ +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * normalize 3x1 and 4x1 vectors (i.e. scale them to unit length) + */ + +/* For DLL export*/ +ODE_API int dSafeNormalize3 (dVector3 a); +ODE_API int dSafeNormalize4 (dVector4 a); +ODE_API void dNormalize3 (dVector3 a); /* Potentially asserts on zero vec*/ +ODE_API void dNormalize4 (dVector4 a); /* Potentially asserts on zero vec*/ + +/* + * given a unit length "normal" vector n, generate vectors p and q vectors + * that are an orthonormal basis for the plane space perpendicular to n. + * i.e. this makes p,q such that n,p,q are all perpendicular to each other. + * q will equal n x p. if n is not unit length then p will be unit length but + * q wont be. + */ + +ODE_API void dPlaneSpace (const dVector3 n, dVector3 p, dVector3 q); +/* Makes sure the matrix is a proper rotation, returns a boolean status */ +ODE_API int dOrthogonalizeR(dMatrix3 m); + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/odemath_legacy.h b/thirdparty/ode-0.16.5/include/ode/odemath_legacy.h new file mode 100644 index 0000000..676cd83 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/odemath_legacy.h @@ -0,0 +1,162 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ODEMATH_LEGACY_H_ +#define _ODE_ODEMATH_LEGACY_H_ + + +/* + * These macros are not used anymore inside of ODE + * They are kept for backward compatibility with external code that + * might still be using them. + */ + +/* + * General purpose vector operations with other vectors or constants. + */ + +#define dOP(a,op,b,c) do { \ + (a)[0] = ((b)[0]) op ((c)[0]); \ + (a)[1] = ((b)[1]) op ((c)[1]); \ + (a)[2] = ((b)[2]) op ((c)[2]); \ +} while (0) +#define dOPC(a,op,b,c) do { \ + (a)[0] = ((b)[0]) op (c); \ + (a)[1] = ((b)[1]) op (c); \ + (a)[2] = ((b)[2]) op (c); \ +} while (0) +#define dOPE(a,op,b) do {\ + (a)[0] op ((b)[0]); \ + (a)[1] op ((b)[1]); \ + (a)[2] op ((b)[2]); \ +} while (0) +#define dOPEC(a,op,c) do { \ + (a)[0] op (c); \ + (a)[1] op (c); \ + (a)[2] op (c); \ +} while (0) + +/* Define an equation with operators + * For example this function can be used to replace + *
    + * for (int i=0; i<3; ++i)
    + *   a[i] += b[i] + c[i];
    + * 
    + */ +#define dOPE2(a,op1,b,op2,c) do { \ + (a)[0] op1 ((b)[0]) op2 ((c)[0]); \ + (a)[1] op1 ((b)[1]) op2 ((c)[1]); \ + (a)[2] op1 ((b)[2]) op2 ((c)[2]); \ +} while (0) + + +#define dLENGTHSQUARED(a) dCalcVectorLengthSquare3(a) +#define dLENGTH(a) dCalcVectorLength3(a) +#define dDISTANCE(a, b) dCalcPointsDistance3(a, b) + + +#define dDOT(a, b) dCalcVectorDot3(a, b) +#define dDOT13(a, b) dCalcVectorDot3_13(a, b) +#define dDOT31(a, b) dCalcVectorDot3_31(a, b) +#define dDOT33(a, b) dCalcVectorDot3_33(a, b) +#define dDOT14(a, b) dCalcVectorDot3_14(a, b) +#define dDOT41(a, b) dCalcVectorDot3_41(a, b) +#define dDOT44(a, b) dCalcVectorDot3_44(a, b) + + +/* + * cross product, set a = b x c. dCROSSpqr means that elements of `a', `b' + * and `c' are spaced p, q and r indexes apart respectively. + * dCROSS() means dCROSS111. `op' is normally `=', but you can set it to + * +=, -= etc to get other effects. + */ + +#define dCROSS(a,op,b,c) \ + do { \ + (a)[0] op ((b)[1]*(c)[2] - (b)[2]*(c)[1]); \ + (a)[1] op ((b)[2]*(c)[0] - (b)[0]*(c)[2]); \ + (a)[2] op ((b)[0]*(c)[1] - (b)[1]*(c)[0]); \ + } while(0) +#define dCROSSpqr(a,op,b,c,p,q,r) \ + do { \ + (a)[ 0] op ((b)[ q]*(c)[2*r] - (b)[2*q]*(c)[ r]); \ + (a)[ p] op ((b)[2*q]*(c)[ 0] - (b)[ 0]*(c)[2*r]); \ + (a)[2*p] op ((b)[ 0]*(c)[ r] - (b)[ q]*(c)[ 0]); \ + } while(0) +#define dCROSS114(a,op,b,c) dCROSSpqr(a,op,b,c,1,1,4) +#define dCROSS141(a,op,b,c) dCROSSpqr(a,op,b,c,1,4,1) +#define dCROSS144(a,op,b,c) dCROSSpqr(a,op,b,c,1,4,4) +#define dCROSS411(a,op,b,c) dCROSSpqr(a,op,b,c,4,1,1) +#define dCROSS414(a,op,b,c) dCROSSpqr(a,op,b,c,4,1,4) +#define dCROSS441(a,op,b,c) dCROSSpqr(a,op,b,c,4,4,1) +#define dCROSS444(a,op,b,c) dCROSSpqr(a,op,b,c,4,4,4) + + +/* +* set a 3x3 submatrix of A to a matrix such that submatrix(A)*b = a x b. +* A is stored by rows, and has `skip' elements per row. the matrix is +* assumed to be already zero, so this does not write zero elements! +* if (plus,minus) is (+,-) then a positive version will be written. +* if (plus,minus) is (-,+) then a negative version will be written. +*/ + +#define dCROSSMAT(A,a,skip,plus,minus) \ + do { \ + (A)[1] = minus (a)[2]; \ + (A)[2] = plus (a)[1]; \ + (A)[(skip)+0] = plus (a)[2]; \ + (A)[(skip)+2] = minus (a)[0]; \ + (A)[2*(skip)+0] = minus (a)[1]; \ + (A)[2*(skip)+1] = plus (a)[0]; \ + } while(0) + + + + +/* +Note: NEVER call any of these functions/macros with the same variable for A and C, +it is not equivalent to A*=B. +*/ + +#define dMULTIPLY0_331(A, B, C) dMultiply0_331(A, B, C) +#define dMULTIPLY1_331(A, B, C) dMultiply1_331(A, B, C) +#define dMULTIPLY0_133(A, B, C) dMultiply0_133(A, B, C) +#define dMULTIPLY0_333(A, B, C) dMultiply0_333(A, B, C) +#define dMULTIPLY1_333(A, B, C) dMultiply1_333(A, B, C) +#define dMULTIPLY2_333(A, B, C) dMultiply2_333(A, B, C) + +#define dMULTIPLYADD0_331(A, B, C) dMultiplyAdd0_331(A, B, C) +#define dMULTIPLYADD1_331(A, B, C) dMultiplyAdd1_331(A, B, C) +#define dMULTIPLYADD0_133(A, B, C) dMultiplyAdd0_133(A, B, C) +#define dMULTIPLYADD0_333(A, B, C) dMultiplyAdd0_333(A, B, C) +#define dMULTIPLYADD1_333(A, B, C) dMultiplyAdd1_333(A, B, C) +#define dMULTIPLYADD2_333(A, B, C) dMultiplyAdd2_333(A, B, C) + + +/* + * These macros are not used anymore inside of ODE + * They are kept for backward compatibility with external code that + * might still be using them. + */ + + +#endif /* #ifndef _ODE_ODEMATH_LEGACY_H_ */ diff --git a/thirdparty/ode-0.16.5/include/ode/precision.h b/thirdparty/ode-0.16.5/include/ode/precision.h new file mode 100644 index 0000000..a8728bd --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/precision.h @@ -0,0 +1,16 @@ +#ifndef _ODE_PRECISION_H_ +#define _ODE_PRECISION_H_ + +/* Define dSINGLE for single precision, dDOUBLE for double precision, + * but never both! + */ + +#if defined(dIDESINGLE) +#define dSINGLE +#elif defined(dIDEDOUBLE) +#define dDOUBLE +#else +#define dUNDEFINEDPRECISION +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/precision.h.in b/thirdparty/ode-0.16.5/include/ode/precision.h.in new file mode 100644 index 0000000..47f3c95 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/precision.h.in @@ -0,0 +1,16 @@ +#ifndef _ODE_PRECISION_H_ +#define _ODE_PRECISION_H_ + +/* Define dSINGLE for single precision, dDOUBLE for double precision, + * but never both! + */ + +#if defined(dIDESINGLE) +#define dSINGLE +#elif defined(dIDEDOUBLE) +#define dDOUBLE +#else +#define @ODE_PRECISION@ +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/rotation.h b/thirdparty/ode-0.16.5/include/ode/rotation.h new file mode 100644 index 0000000..a72be27 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/rotation.h @@ -0,0 +1,70 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_ROTATION_H_ +#define _ODE_ROTATION_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +ODE_API void dRSetIdentity (dMatrix3 R); + +ODE_API void dRFromAxisAndAngle (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal angle); + +ODE_API void dRFromEulerAngles (dMatrix3 R, dReal phi, dReal theta, dReal psi); + +ODE_API void dRFrom2Axes (dMatrix3 R, dReal ax, dReal ay, dReal az, + dReal bx, dReal by, dReal bz); + +ODE_API void dRFromZAxis (dMatrix3 R, dReal ax, dReal ay, dReal az); + +ODE_API void dQSetIdentity (dQuaternion q); + +ODE_API void dQFromAxisAndAngle (dQuaternion q, dReal ax, dReal ay, dReal az, + dReal angle); + +/* Quaternion multiplication, analogous to the matrix multiplication routines. */ +/* qa = rotate by qc, then qb */ +ODE_API void dQMultiply0 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by qc, then by inverse of qb */ +ODE_API void dQMultiply1 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by inverse of qc, then by qb */ +ODE_API void dQMultiply2 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); +/* qa = rotate by inverse of qc, then by inverse of qb */ +ODE_API void dQMultiply3 (dQuaternion qa, const dQuaternion qb, const dQuaternion qc); + +ODE_API void dRfromQ (dMatrix3 R, const dQuaternion q); +ODE_API void dQfromR (dQuaternion q, const dMatrix3 R); +ODE_API void dDQfromW (dReal dq[4], const dVector3 w, const dQuaternion q); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/threading.h b/thirdparty/ode-0.16.5/include/ode/threading.h new file mode 100644 index 0000000..00dcc85 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/threading.h @@ -0,0 +1,412 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * Threading support header file. * + * Copyright (C) 2011-2024 Oleh Derevenko. All rights reserved. * + * e-mail: odar@eleks.com (change all "a" to "e") * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + * ODE threading support interfaces + */ + + +#ifndef _ODE_THREADING_H_ +#define _ODE_THREADING_H_ + +#include +// Include since time_t is used and it is not available by default in some OSes +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct dxThreadingImplementation; +typedef struct dxThreadingImplementation *dThreadingImplementationID; + +typedef unsigned dmutexindex_t; +struct dxMutexGroup; +typedef struct dxMutexGroup *dMutexGroupID; + + +#define dTHREADING_THREAD_COUNT_UNLIMITED 0U + + + +/** + * @brief Allocates a group of mutexes. + * + * The Mutex allocated do not need to support recursive locking. + * + * The Mutex names are provided to aid in debugging and thread state tracking. + * + * @param impl Threading implementation ID + * @param Mutex_count Number of Mutex to create + * @Mutex_names_ptr Pointer to optional Mutex names array to be associated with individual Mutex + * @returns MutexGroup ID or NULL if error occurred. + * + * @ingroup threading + * @see dMutexGroupFreeFunction + * @see dMutexGroupMutexLockFunction + * @see dMutexGroupMutexUnlockFunction + */ +typedef dMutexGroupID dMutexGroupAllocFunction (dThreadingImplementationID impl, dmutexindex_t Mutex_count, const char *const *Mutex_names_ptr/*=NULL*/); + +/** + * @brief Deletes a group of mutexes. + * + * @param impl Threading implementation ID + * @param mutex_group Mutex group to deallocate + * + * @ingroup threading + * @see dMutexGroupAllocFunction + * @see dMutexGroupMutexLockFunction + * @see dMutexGroupMutexUnlockFunction + */ +typedef void dMutexGroupFreeFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group); + +/** + * @brief Locks a mutex in a group of mutexes. + * + * The function is to block execution until requested mutex can be locked. + * + * Note: Mutex provided may not support recursive locking. Calling this function + * while mutex is already locked by current thread will result in unpredictable behavior. + * + * @param impl Threading implementation ID + * @param mutex_group Mutex group to use for locking + * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1) + * + * @ingroup threading + * @see dMutexGroupAllocFunction + * @see dMutexGroupFreeFunction + * @see dMutexGroupMutexUnlockFunction + */ +typedef void dMutexGroupMutexLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index); + +/** + * @brief Attempts to lock a mutex in a group of mutexes. + * + * The function is to lock the requested mutex if it is unoccupied or + * immediately return failure if mutex is already locked by other thread. + * + * Note: Mutex provided may not support recursive locking. Calling this function + * while mutex is already locked by current thread will result in unpredictable behavior. + * + * @param impl Threading implementation ID + * @param mutex_group Mutex group to use for locking + * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1) + * @returns 1 for success (mutex is locked) and 0 for failure (mutex is not locked) + * + * @ingroup threading + * @see dMutexGroupAllocFunction + * @see dMutexGroupFreeFunction + * @see dMutexGroupMutexLockFunction + * @see dMutexGroupMutexUnlockFunction + */ +/* typedef int dMutexGroupMutexTryLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);*/ + +/** + * @brief Unlocks a mutex in a group of mutexes. + * + * The function is to unlock the given mutex provided it had been locked before. + * + * @param impl Threading implementation ID + * @param mutex_group Mutex group to use for unlocking + * @param mutex_index The index of mutex to be unlocked (0..Mutex_count - 1) + * + * @ingroup threading + * @see dMutexGroupAllocFunction + * @see dMutexGroupFreeFunction + * @see dMutexGroupMutexLockFunction + */ +typedef void dMutexGroupMutexUnlockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index); + + +struct dxCallReleasee; +typedef struct dxCallReleasee *dCallReleaseeID; + +struct dxCallWait; +typedef struct dxCallWait *dCallWaitID; + +typedef dsizeint ddependencycount_t; +typedef ddiffint ddependencychange_t; +typedef dsizeint dcallindex_t; +typedef int dThreadedCallFunction(void *call_context, dcallindex_t instance_index, + dCallReleaseeID this_releasee); + +typedef struct dxThreadedWaitTime +{ + time_t wait_sec; + unsigned long wait_nsec; + +} dThreadedWaitTime; + + +/** + * @brief Allocates a Wait ID that can be used to wait for a call. + * + * @param impl Threading implementation ID + * @returns Wait ID or NULL if error occurred + * + * @ingroup threading + * @see dThreadedCallWaitResetFunction + * @see dThreadedCallWaitFreeFunction + * @see dThreadedCallPostFunction + * @see dThreadedCallWaitFunction + */ +typedef dCallWaitID dThreadedCallWaitAllocFunction(dThreadingImplementationID impl); + +/** + * @brief Resets a Wait ID so that it could be used to wait for another call. + * + * @param impl Threading implementation ID + * @param call_wait Wait ID to reset + * + * @ingroup threading + * @see dThreadedCallWaitAllocFunction + * @see dThreadedCallWaitFreeFunction + * @see dThreadedCallPostFunction + * @see dThreadedCallWaitFunction + */ +typedef void dThreadedCallWaitResetFunction(dThreadingImplementationID impl, dCallWaitID call_wait); + +/** + * @brief Frees a Wait ID. + * + * @param impl Threading implementation ID + * @param call_wait Wait ID to delete + * + * @ingroup threading + * @see dThreadedCallWaitAllocFunction + * @see dThreadedCallPostFunction + * @see dThreadedCallWaitFunction + */ +typedef void dThreadedCallWaitFreeFunction(dThreadingImplementationID impl, dCallWaitID call_wait); + + +/** + * @brief Post a function to be called in another thread. + * + * A call is scheduled to be executed asynchronously. + * + * A @a out_summary_fault variable can be provided for call to accumulate any + * possible faults from its execution and execution of any possible sub-calls. + * This variable gets result that @a call_func returns. Also, if dependent calls + * are executed after the call already exits, the variable is also going to be + * updated with results of all those calls before control is released to master. + * + * @a out_post_releasee parameter receives a value of @c dCallReleaseeID that can + * later be used for @a dependent_releasee while scheduling sub-calls to make + * current call depend on them. The value is only returned if @a dependencies_count + * is not zero (i.e. if any dependencies are expected at all). The call is not going + * to start until all its dependencies complete. + * + * In case if number of dependencies is unknown in advance 1 can be passed on call + * scheduling. Then @c dThreadedCallDependenciesCountAlterFunction can be used to + * add one more extra dependencies before scheduling each subcall. And then, after + * all sub-calls had been scheduled, @c dThreadedCallDependenciesCountAlterFunction + * can be used again to subtract initial extra dependency from total number. + * Adding one dependency in advance is necessary to obtain releasee ID and to make + * sure the call will not start and will not terminate before all sub-calls are scheduled. + * + * Extra dependencies can also be added from the call itself after it has already + * been started (with parameter received in @c dThreadedCallFunction). + * In that case those dependencies will start immediately or after call returns + * but the call's master will not be released/notified until all additional + * dependencies complete. This can be used to schedule sub-calls from a call and + * then pass own job to another sub-call dependent on those initial sub-calls. + * + * By using @ call_wait it is possible to assign a Wait ID that can later + * be passed into @c dThreadedCallWaitFunction to wait for call completion. + * + * If @a call_name is available (and it should!) the string must remain valid until + * after call completion. In most cases this should be a static string literal. + * + * Since the function is an analogue of normal method call it is not supposed to fail. + * Any complications with resource allocation on call scheduling should be + * anticipated, avoided and worked around by implementation. + * + * @param impl Threading implementation ID + * @param out_summary_fault Optional pointer to variable to be set to 1 if function + * call (or any sub-call) fails internally, or 0 if all calls return success + * @param out_post_releasee Optional pointer to variable to receive releasee ID + * associated with the call + * @param dependencies_count Number of dependencies that are going to reference + * this call as dependent releasee + * @param dependent_releasee Optional releasee ID to reference with this call + * @param call_wait Optional Wait ID that can later be used to wait for the call + * @param call_func Pointer to function to be called + * @param call_context Context parameter to be passed into the call + * @param instance_index Index parameter to be passed into the call + * @param call_name Optional name to be associated with the call (for debugging and state tracking) + * + * @ingroup threading + * @see dThreadedCallWaitFunction + * @see dThreadedCallDependenciesCountAlterFunction + * @see dThreadingImplResourcesForCallsPreallocateFunction + */ +typedef void dThreadedCallPostFunction(dThreadingImplementationID impl, int *out_summary_fault/*=NULL*/, + dCallReleaseeID *out_post_releasee/*=NULL*/, ddependencycount_t dependencies_count, dCallReleaseeID dependent_releasee/*=NULL*/, + dCallWaitID call_wait/*=NULL*/, + dThreadedCallFunction *call_func, void *call_context, dcallindex_t instance_index, + const char *call_name/*=NULL*/); + +/** + * @brief Add or remove extra dependencies from call that has been scheduled + * or is in process of execution. + * + * Extra dependencies can be added to a call if exact number of sub-calls is + * not known in advance at the moment the call is scheduled. Also, some dependencies + * can be removed if sub-calls were planned but then dropped. + * + * In case if total dependency count of a call reaches zero by result of invoking + * this function, the call is free to start executing immediately. + * + * After the call execution had been started, any additional dependencies can only + * be added from the call function itself! + * + * @param impl Threading implementation ID + * @param target_releasee ID of releasee to apply dependencies count change to + * @param dependencies_count_change Number of dependencies to add or remove + * + * @ingroup threading + * @see dThreadedCallPostFunction + */ +typedef void dThreadedCallDependenciesCountAlterFunction(dThreadingImplementationID impl, dCallReleaseeID target_releasee, + ddependencychange_t dependencies_count_change); + +/** + * @brief Wait for a posted call to complete. + * + * Function blocks until a call identified by @a call_wait completes or + * timeout elapses. + * + * IT IS ILLEGAL TO INVOKE THIS FUNCTION FROM WITHIN A THREADED CALL! + * This is because doing so will block a physical thread and will require + * increasing worker thread count to avoid starvation. Use call dependencies + * if it is necessary make sure sub-calls have been completed instead! + * + * If @a timeout_time_ptr is NULL, the function waits without time limit. If @a timeout_time_ptr + * points to zero value, the function only checks status and does not block. + * + * If @a wait_name is available (and it should!) the string must remain valid for + * the duration of wait. In most cases this should be a static string literal. + * + * Function is not expected to return failures caused by system call faults as + * those are hardly ever possible to be handled in this case anyway. In event of + * system call fault the function is supposed to terminate application. + * + * @param impl Threading implementation ID + * @param out_wait_status Optional pointer to variable to receive 1 if waiting succeeded + * or 0 in case of timeout + * @param call_wait Wait ID that had been passed to scheduling a call that needs to be waited for + * @param timeout_time_ptr Optional pointer to time specification the wait must not + * last longer than (pass NULL for infinite timeout) + * @param wait_name Optional name to be associated with the wait (for debugging and state tracking) + * + * @ingroup threading + * @see dThreadedCallPostFunction + */ +typedef void dThreadedCallWaitFunction(dThreadingImplementationID impl, int *out_wait_status/*=NULL*/, + dCallWaitID call_wait, const dThreadedWaitTime *timeout_time_ptr/*=NULL*/, + const char *wait_name/*=NULL*/); + +/** + * @brief Retrieve number of active threads that serve the implementation. + * + * @param impl Threading implementation ID + * @returns Number of active threads + * + * @ingroup threading + */ +typedef unsigned dThreadingImplThreadCountRetrieveFunction(dThreadingImplementationID impl); + +/** + * @brief Preallocate resources to handle posted calls. + * + * The function is intended to make sure enough resources is preallocated for the + * implementation to be able to handle posted calls. Then @c max_simultaneous_calls_estimate + * is an estimate of how many posted calls can potentially be active or scheduled + * at the same time. The value is usually derived from the way the calls are posted + * in library code and dependencies between them. + * + * @warning While working on an implementation be prepared that the estimate provided + * yet rarely but theoretically can be exceeded due to unpredictability of thread execution. + * + * This function is normally going to be invoked by library each time it is entered + * from outside to do the job but before any threaded calls are going to be posted. + * + * @param impl Threading implementation ID + * @param max_simultaneous_calls_estimate An estimated number of calls that can be posted simultaneously + * @returns 1 or 0 to indicate success or failure + * + * @ingroup threading + * @see dThreadedCallPostFunction + */ +typedef int dThreadingImplResourcesForCallsPreallocateFunction(dThreadingImplementationID impl, + ddependencycount_t max_simultaneous_calls_estimate); + + +/** + * @brief An interface structure with function pointers to be provided by threading implementation. + */ +typedef struct dxThreadingFunctionsInfo +{ + unsigned struct_size; + + dMutexGroupAllocFunction *alloc_mutex_group; + dMutexGroupFreeFunction *free_mutex_group; + dMutexGroupMutexLockFunction *lock_group_mutex; + dMutexGroupMutexUnlockFunction *unlock_group_mutex; + + dThreadedCallWaitAllocFunction *alloc_call_wait; + dThreadedCallWaitResetFunction *reset_call_wait; + dThreadedCallWaitFreeFunction *free_call_wait; + + dThreadedCallPostFunction *post_call; + dThreadedCallDependenciesCountAlterFunction *alter_call_dependencies_count; + dThreadedCallWaitFunction *wait_call; + + dThreadingImplThreadCountRetrieveFunction *retrieve_thread_count; + dThreadingImplResourcesForCallsPreallocateFunction *preallocate_resources_for_calls; + + /* + * Beware of Jon Watte's anger if you dare to uncomment this! + * May cryptic text below be you a warning! + * Стародавні легенди розказують, що кожного сміливця, хто наважиться порушити табу + * і відкрити заборонений код, спіткає страшне прокляття і він відразу почне робити + * одні лиш помилки. + * + * dMutexGroupMutexTryLockFunction *trylock_group_mutex; + */ + +} dThreadingFunctionsInfo; + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _ODE_THREADING_H_ */ diff --git a/thirdparty/ode-0.16.5/include/ode/threading_impl.h b/thirdparty/ode-0.16.5/include/ode/threading_impl.h new file mode 100644 index 0000000..fe262a9 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/threading_impl.h @@ -0,0 +1,292 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * Builtin ODE threading implementation header. * + * Copyright (C) 2011-2024 Oleh Derevenko. All rights reserved. * + * e-mail: odar@eleks.com (change all "a" to "e") * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + * A threading implementation built into ODE for those who does not care to + * or can't implement an own one. + */ + +#ifndef _ODE_THREADING_IMPL_H_ +#define _ODE_THREADING_IMPL_H_ + + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct dxThreadingThreadPool; +typedef struct dxThreadingThreadPool *dThreadingThreadPoolID; + + +/** + * @brief Allocates built-in self-threaded threading implementation object. + * + * A self-threaded implementation is a type of implementation that performs + * processing of posted calls by means of caller thread itself. This type of + * implementation does not need thread pool to serve it. + * + * Note that since May 9th, 2017 (rev. #2066) the Self-Threaded implementation + * returns 0 rather than 1 as available thread count to distinguish from + * thread pools with just one thread in them. + * + * The processing is arranged in a way to prevent call stack depth growth + * as more and more nested calls are posted. + * + * Note that it is not necessary to create and assign a self-threaded + * implementation to a world, as there is a global one used by default + * if no implementation is explicitly assigned. You should only assign + * each world an individual threading implementation instance if simulations + * need to be run in parallel in multiple threads for the worlds. + * + * @returns ID of object allocated or NULL on failure + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingFreeImplementation + */ +ODE_API dThreadingImplementationID dThreadingAllocateSelfThreadedImplementation(); + +/** + * @brief Allocates built-in multi-threaded threading implementation object. + * + * A multi-threaded implementation is a type of implementation that has to be + * served with a thread pool. The thread pool can be either the built-in ODE object + * or set of external threads that dedicate themselves to this purpose and stay + * in ODE until implementation releases them. + * + * @returns ID of object allocated or NULL on failure + * + * @ingroup threading + * @see dThreadingThreadPoolServeMultiThreadedImplementation + * @see dExternalThreadingServeMultiThreadedImplementation + * @see dThreadingFreeImplementation + */ +ODE_API dThreadingImplementationID dThreadingAllocateMultiThreadedImplementation(); + +/** + * @brief Retrieves the functions record of a built-in threading implementation. + * + * The implementation can be the one allocated by ODE (from @c dThreadingAllocateMultiThreadedImplementation). + * Do not use this function with self-made custom implementations - + * they should be bundled with their own set of functions. + * + * @param impl Threading implementation ID + * @returns Pointer to associated functions structure + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + */ +ODE_API const dThreadingFunctionsInfo *dThreadingImplementationGetFunctions(dThreadingImplementationID impl); + +/** + * @brief Requests a built-in implementation to release threads serving it. + * + * The function unblocks threads employed in implementation serving and lets them + * return to from where they originate. It's the responsibility of external code + * to make sure all the calls to ODE that might be dependent on given threading + * implementation object had already returned before this call is made. If threading + * implementation is still processing some posted calls while this function is + * invoked the behavior is implementation dependent. + * + * This call is to be used to request the threads to be released before waiting + * for them in host pool or before waiting for them to exit. Implementation object + * must not be destroyed before it is known that all the serving threads have already + * returned from it. If implementation needs to be reused after this function is called + * and all the threads have exited from it a call to @c dThreadingImplementationCleanupForRestart + * must be made to restore internal state of the object. + * + * If this function is called for self-threaded built-in threading implementation + * the call has no effect. + * + * @param impl Threading implementation ID + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingImplementationCleanupForRestart + */ +ODE_API void dThreadingImplementationShutdownProcessing(dThreadingImplementationID impl); + +/** + * @brief Restores built-in implementation's state to let it be reused after shutdown. + * + * If a multi-threaded built-in implementation needs to be reused after a call + * to @c dThreadingImplementationShutdownProcessing this call is to be made to + * restore object's internal state. After that the implementation can be served again. + * + * If this function is called for self-threaded built-in threading implementation + * the call has no effect. + * + * @param impl Threading implementation ID + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingImplementationShutdownProcessing + */ +ODE_API void dThreadingImplementationCleanupForRestart(dThreadingImplementationID impl); + +/** + * @brief Deletes an instance of built-in threading implementation. + * + * @warning A care must be taken to make sure the implementation is unassigned + * from all the objects it was assigned to and that there are no more threads + * serving it before attempting to call this function. + * + * @param impl Threading implementation ID + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + */ +ODE_API void dThreadingFreeImplementation(dThreadingImplementationID impl); + + +typedef void (dThreadReadyToServeCallback)(void *callback_context); + +/** + * @brief An entry point for external threads that would like to serve a built-in + * threading implementation object. + * + * A thread that calls this function remains blocked in ODE and serves implementation + * object @p impl until being released with @c dThreadingImplementationShutdownProcessing call. + * This function can be used to provide external threads instead of ODE's built-in + * thread pools. + * + * The optional callback @readiness_callback is called after the thread has reached + * and has registered within the implementation. The implementation should not + * be used until all dedicated threads register within it as otherwise it will not + * have accurate view of the execution resources available. + * + * @param impl Threading implementation ID + * @param readiness_callback Optional readiness callback to be called after thread enters the implementation + * @param callback_context A value to be passed as parameter to readiness callback + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingImplementationShutdownProcessing + */ +ODE_API void dExternalThreadingServeMultiThreadedImplementation(dThreadingImplementationID impl, + dThreadReadyToServeCallback *readiness_callback/*=NULL*/, void *callback_context/*=NULL*/); + + +/** + * @brief Creates an instance of built-in thread pool object that can be used to serve + * multi-threaded threading implementations. + * + * The threads allocated inherit priority of caller thread. Their affinity is not + * explicitly adjusted and gets the value the system assigns by default. Threads + * have their stack memory fully committed immediately on start. On POSIX platforms + * threads are started with all the possible signals blocked. Threads execute + * calls to @c dAllocateODEDataForThread with @p ode_data_allocate_flags + * on initialization. + * + * On POSIX platforms this function must be called with signals masked + * or other measures must be taken to prevent reception of signals by calling thread + * for the duration of the call. + * + * @param thread_count Number of threads to start in pool + * @param stack_size Size of stack to be used for every thread or 0 for system default value + * @param ode_data_allocate_flags Flags to be passed to @c dAllocateODEDataForThread on behalf of each thread + * @returns ID of object allocated or NULL on failure + * + * @ingroup threading + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingImplementationShutdownProcessing + * @see dThreadingFreeThreadPool + */ +ODE_API dThreadingThreadPoolID dThreadingAllocateThreadPool(unsigned thread_count, + dsizeint stack_size, unsigned int ode_data_allocate_flags, void *reserved/*=NULL*/); + +/** + * @brief Commands an instance of built-in thread pool to serve a built-in multi-threaded + * threading implementation. + * + * A pool can only serve one threading implementation at a time. + * Call @c dThreadingImplementationShutdownProcessing to release pool threads + * from implementation serving and make them idle. Pool threads must be released + * from any implementations before pool is attempted to be deleted. + * + * This function waits for threads to register within implementation before returning. + * So, after the function call exits the implementation can be used immediately. + * + * @param pool Thread pool ID to serve the implementation + * @param impl Implementation ID of implementation to be served + * + * @ingroup threading + * @see dThreadingAllocateThreadPool + * @see dThreadingAllocateMultiThreadedImplementation + * @see dThreadingImplementationShutdownProcessing + */ +ODE_API void dThreadingThreadPoolServeMultiThreadedImplementation(dThreadingThreadPoolID pool, dThreadingImplementationID impl); + +/** + * @brief Waits until all pool threads are released from threading implementation + * they might be serving. + * + * The function can be used after a call to @c dThreadingImplementationShutdownProcessing + * to make sure all the threads have already been released by threading implementation + * and it can be deleted or it can be cleaned up for restart and served by another pool + * or this pool's threads can be used to serve another threading implementation. + * + * Note that is it not necessary to call this function before pool destruction + * since @c dThreadingFreeThreadPool performs similar wait operation implicitly on its own. + * + * It is OK to call this function even if pool was not serving any threading implementation + * in which case the call exits immediately with minimal delay. + * + * @param pool Thread pool ID to wait for + * + * @ingroup threading + * @see dThreadingAllocateThreadPool + * @see dThreadingImplementationShutdownProcessing + * @see dThreadingFreeThreadPool + */ +ODE_API void dThreadingThreadPoolWaitIdleState(dThreadingThreadPoolID pool); + +/** + * @brief Deletes a built-in thread pool instance. + * + * The pool threads must be released from any implementations they might be serving + * before this function is called. Otherwise the call is going to block + * and wait until pool's threads return. + * + * @param pool Thread pool ID to delete + * + * @ingroup threading + * @see dThreadingAllocateThreadPool + * @see dThreadingImplementationShutdownProcessing + */ +ODE_API void dThreadingFreeThreadPool(dThreadingThreadPoolID pool); + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _ODE_THREADING_IMPL_H_ */ diff --git a/thirdparty/ode-0.16.5/include/ode/timer.h b/thirdparty/ode-0.16.5/include/ode/timer.h new file mode 100644 index 0000000..fe1483f --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/timer.h @@ -0,0 +1,76 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef _ODE_TIMER_H_ +#define _ODE_TIMER_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* stop watch objects */ + +typedef struct dStopwatch { + double time; /* total clock count */ + unsigned long cc[2]; /* clock count since last `start' */ +} dStopwatch; + +ODE_API void dStopwatchReset (dStopwatch *); +ODE_API void dStopwatchStart (dStopwatch *); +ODE_API void dStopwatchStop (dStopwatch *); +ODE_API double dStopwatchTime (dStopwatch *); /* returns total time in secs */ + + +/* code timers */ + +ODE_API void dTimerStart (const char *description); /* pass a static string here */ +ODE_API void dTimerNow (const char *description); /* pass a static string here */ +ODE_API void dTimerEnd(void); + +/* print out a timer report. if `average' is nonzero, print out the average + * time for each slot (this is only meaningful if the same start-now-end + * calls are being made repeatedly. + */ +ODE_API void dTimerReport (FILE *fout, int average); + + +/* resolution */ + +/* returns the timer ticks per second implied by the timing hardware or API. + * the actual timer resolution may not be this great. + */ +ODE_API double dTimerTicksPerSecond(void); + +/* returns an estimate of the actual timer resolution, in seconds. this may + * be greater than 1/ticks_per_second. + */ +ODE_API double dTimerResolution(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/version.h b/thirdparty/ode-0.16.5/include/ode/version.h new file mode 100644 index 0000000..42ade96 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/version.h @@ -0,0 +1,6 @@ +#ifndef _ODE_VERSION_H_ +#define _ODE_VERSION_H_ + +#define dODE_VERSION "0.16.5" + +#endif diff --git a/thirdparty/ode-0.16.5/include/ode/version.h.in b/thirdparty/ode-0.16.5/include/ode/version.h.in new file mode 100644 index 0000000..b5d3c81 --- /dev/null +++ b/thirdparty/ode-0.16.5/include/ode/version.h.in @@ -0,0 +1,6 @@ +#ifndef _ODE_VERSION_H_ +#define _ODE_VERSION_H_ + +#define dODE_VERSION "@ODE_VERSION@" + +#endif diff --git a/thirdparty/ode-0.16.5/install-sh b/thirdparty/ode-0.16.5/install-sh new file mode 100644 index 0000000..0b0fdcb --- /dev/null +++ b/thirdparty/ode-0.16.5/install-sh @@ -0,0 +1,501 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2013-12-25.23; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/thirdparty/ode-0.16.5/libccd/BSD-LICENSE b/thirdparty/ode-0.16.5/libccd/BSD-LICENSE new file mode 100644 index 0000000..6d7fa23 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/BSD-LICENSE @@ -0,0 +1,44 @@ +libccd +------- + +Copyright (c)2010 Daniel Fiser , +Intelligent and Mobile Robotics Group, Department of Cybernetics, +Faculty of Electrical Engineering, Czech Technical University in Prague. +All rights reserved. + + +This work was supported by SYMBRION and REPLICATOR projects. +The SYMBRION project is funded by European Commission within the work +"Future and Emergent Technologies Proactive" under grant agreement no. +216342. +The REPLICATOR project is funded within the work programme "Cognitive +Systems, Interaction, Robotics" under grant agreement no. 216240. +http://www.symbrion.eu/ +http://www.replicators.eu/ + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/ode-0.16.5/libccd/Makefile.am b/thirdparty/ode-0.16.5/libccd/Makefile.am new file mode 100644 index 0000000..c08c4b3 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = src + +EXTRA_DIST = \ + bootstrap \ + BSD-LICENSE \ + README diff --git a/thirdparty/ode-0.16.5/libccd/Makefile.in b/thirdparty/ode-0.16.5/libccd/Makefile.in new file mode 100644 index 0000000..0973929 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/Makefile.in @@ -0,0 +1,804 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = src/ccd/precision.h +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + cscope distdir dist dist-all distcheck +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../compile \ + $(top_srcdir)/../config.guess $(top_srcdir)/../config.sub \ + $(top_srcdir)/../install-sh $(top_srcdir)/../ltmain.sh \ + $(top_srcdir)/../missing $(top_srcdir)/src/ccd/precision.h.in \ + README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_PRECISION = @CCD_PRECISION@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = src +EXTRA_DIST = \ + bootstrap \ + BSD-LICENSE \ + README + +all: all-recursive + +.SUFFIXES: +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): +src/ccd/precision.h: $(top_builddir)/config.status $(top_srcdir)/src/ccd/precision.h.in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool config.lt + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +distdir: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--refresh check check-am clean clean-cscope clean-generic \ + clean-libtool cscope cscopelist-am ctags ctags-am dist \ + dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ + dist-xz dist-zip distcheck distclean distclean-generic \ + distclean-libtool distclean-tags distcleancheck distdir \ + distuninstallcheck dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/libccd/README b/thirdparty/ode-0.16.5/libccd/README new file mode 100644 index 0000000..a04781c --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/README @@ -0,0 +1,72 @@ +libccd is library for a collision detection between two convex shapes. +libccd implements variation on Gilbert–Johnson–Keerthi algorithm plus Expand +Polytope Algorithm (EPA) and also implements algorithm Minkowski Portal +Refinement (MPR, a.k.a. XenoCollide) as described in Game Programming Gems 7. + +For more info see home of libccd: http://libccd.danfis.cz. + + +Dependencies +------------- +This library is currently based only on standard libraries. +The only exception are testsuites that are built on top of CU +(cu.danfis.cz) library licensed under LGPL, however only testing depends on +it and libccd library itself can be distributed without it. + + +License +-------- +libccd is licensed under OSI-approved 3-clause BSD License, text of license +is distributed along with source code in BSD-LICENSE file. +Each file should include license notice, the rest should be considered as +licensed under 3-clause BSD License. + + +Compile And Install +-------------------- +Simply type 'make' and 'make install' in src/ directory. + +Library libccd is by default compiled in double precision of floating point +numbers - you can control this by options USE_SINGLE/USE_DOUBLE, i.e.: + $ make USE_SINGLE=yes +will compile library in single precision. + +Installation directory can be changed by options PREFIX, INCLUDEDIR and +LIBDIR. + +For more info type 'make help'. + + +Compile And Install Using Autotools +------------------------------------ +libccd also contains support for autotools: +1) Generate configure script etc.: $ ./bootstrap +2) Create new build/ directory: $ mkdir build && cd build +3) Run configure script: $ ../configure +4) Run make and make install: $ make && make install + +configure script can change the way libccd is compiled and installed, most +significant option is --enable-double-precision which enables double +precision (single is default in this case). + + +Usage +------ +See ccd.h for public API. +In your application include , setup ccd_t structure and run some +of functions (all functions are reentrant). Then link with libccd.a. + + +Directories +------------ +src/ + - contains source files of libccd. +src/testsuites + - testsuites - libccd must be compiled before compiling this. +src/testsuites/cu + - CU unit testing framework +src/testsuites/regressions + - files ready for regression tests + +doc/ + - some documentation. diff --git a/thirdparty/ode-0.16.5/libccd/aclocal.m4 b/thirdparty/ode-0.16.5/libccd/aclocal.m4 new file mode 100644 index 0000000..f7ad092 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/aclocal.m4 @@ -0,0 +1,10200 @@ +# generated automatically by aclocal 1.15 -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +]) + +# serial 58 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + +# _LT_CC_BASENAME(CC) +# ------------------- +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. +m4_defun([_LT_CC_BASENAME], +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from 'configure', and 'config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain=$ac_aux_dir/ltmain.sh +])# _LT_PROG_LTMAIN + + + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the 'libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags='_LT_TAGS'dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# '#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test 0 = "$lt_write_fail" && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +'$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test 0 != $[#] +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try '$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try '$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test yes = "$silent" && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +_LT_COPYING +_LT_LIBTOOL_TAGS + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS=$save_LDFLAGS + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + m4_if([$1], [CXX], +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case $ECHO in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([$with_sysroot]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and where our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test yes = "[$]$2"; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS +]) + +if test yes = "[$]$2"; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n "$lt_cv_sys_max_cmd_len"; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes = "$cross_compiling"; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen=shl_load], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen=dlopen], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then + + # We can hardcode non-existent directories. + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program that can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac]) +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program that can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test no = "$withval" || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + +# _LT_CHECK_MAGIC_METHOD +# ---------------------- +# how to check for library dependencies +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_MAGIC_METHOD], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +AC_CACHE_CHECK([how to recognize dependent libraries], +lt_cv_deplibs_check_method, +[lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[[4-9]]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[[45]]*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi]) +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM=-lm) + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test yes = "$GCC"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + osf3*) + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting $shlibpath_var if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC=$CC +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report what library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC=$lt_save_CC +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)=$prev$p + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)=$p + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)=$p + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test no = "$F77"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_F77"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test no = "$FC"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_FC"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_FC" + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code=$lt_simple_compile_test_code + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f "$lt_ac_sed" && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test 10 -lt "$lt_ac_count" && break + lt_ac_count=`expr $lt_ac_count + 1` + if test "$lt_ac_count" -gt "$lt_ac_max"; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine what file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS + +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 8 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option '$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) + ]) +])# _LT_SET_OPTIONS + + + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' +# LT_INIT options. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) + +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59, which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) + +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 4179 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.6' +macro_revision='2.4.6' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) + +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) + +# Copyright (C) 2002-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.15' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.15], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.15])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each '.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2014 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + diff --git a/thirdparty/ode-0.16.5/libccd/bootstrap b/thirdparty/ode-0.16.5/libccd/bootstrap new file mode 100644 index 0000000..33795bf --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/bootstrap @@ -0,0 +1,12 @@ +#!/bin/sh + +# on Mac libtoolize is called glibtoolize +LIBTOOLIZE=libtoolize +if [ `uname -s` = Darwin ]; then + LIBTOOLIZE=glibtoolize +fi +$LIBTOOLIZE -c --automake +aclocal +autoheader +autoconf +automake -a --foreign -c diff --git a/thirdparty/ode-0.16.5/libccd/configure b/thirdparty/ode-0.16.5/libccd/configure new file mode 100644 index 0000000..839345d --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/configure @@ -0,0 +1,18785 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for libccd 1.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 + + test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ + || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and danfis@danfis.cz +$0: about your system, including any error possibly output +$0: before this message. Then install a modern shell, or +$0: manually run the script under such a shell if you do +$0: have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + +SHELL=${CONFIG_SHELL-/bin/sh} + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='libccd' +PACKAGE_TARNAME='libccd' +PACKAGE_VERSION='1.0' +PACKAGE_STRING='libccd 1.0' +PACKAGE_BUGREPORT='danfis@danfis.cz' +PACKAGE_URL='' + +ac_unique_file="src/ccd.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +CCD_PRECISION +CXXCPP +CPP +LT_SYS_LIBRARY_PATH +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +MANIFEST_TOOL +RANLIB +ac_ct_AR +AR +DLLTOOL +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +LIBTOOL +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +ac_ct_CC +CFLAGS +CC +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CXX +CPPFLAGS +LDFLAGS +CXXFLAGS +CXX +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_aix_soname +with_gnu_ld +with_sysroot +enable_libtool_lock +enable_double_precision +' + ac_precious_vars='build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +LT_SYS_LIBRARY_PATH +CPP +CXXCPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures libccd 1.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/libccd] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of libccd 1.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-shared[=PKGS] build shared libraries [default=no] + --enable-static[=PKGS] build static libraries [default=yes] + --enable-fast-install[=PKGS] + optimize for fast installation [default=yes] + --disable-libtool-lock avoid locking (might break parallel builds) + --enable-double-precision + enable double precision computations instead of + single precision + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use + both] + --with-aix-soname=aix|svr4|both + shared library versioning (aka "SONAME") variant to + provide on AIX, [default=aix]. + --with-gnu-ld assume the C compiler uses GNU ld [default=no] + --with-sysroot[=DIR] Search for dependent libraries within DIR (or the + compiler's sysroot if not specified). + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CC C compiler command + CFLAGS C compiler flags + LT_SYS_LIBRARY_PATH + User-defined run-time library search path. + CPP C preprocessor + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +libccd configure 1.0 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## ------------------------------- ## +## Report this to danfis@danfis.cz ## +## ------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by libccd $as_me 1.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers src/config.h" + +am__api_version='1.15' + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='libccd' + VERSION='1.0' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar pax cpio none' + +am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + + +# Checks for programs. +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 +$as_echo_n "checking whether the C++ compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C++ compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 +$as_echo_n "checking for C++ compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo this is the am__doit target +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# Ignore all kinds of additional output from 'make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_shared=no +fi + + + + + + + + + +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac + + + +macro_version='2.4.6' +macro_revision='2.4.6' + + + + + + + + + + + + + +ltmain=$ac_aux_dir/ltmain.sh + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 +$as_echo_n "checking how to print strings... " >&6; } +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "" +} + +case $ECHO in + printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 +$as_echo "printf" >&6; } ;; + print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 +$as_echo "print -r" >&6; } ;; + *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 +$as_echo "cat" >&6; } ;; +esac + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if ${ac_cv_path_SED+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_SED" || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if ${ac_cv_path_FGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_FGREP" || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if ${lt_cv_path_NM+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + if test -n "$ac_tool_prefix"; then + for ac_prog in dumpbin "link -dump" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in dumpbin "link -dump" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_DUMPBIN" && break +done + + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi + + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if ${lt_cv_nm_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:$LINENO: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi + +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if ${lt_cv_sys_max_cmd_len+:} false; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac + +fi + +if test -n "$lt_cv_sys_max_cmd_len"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } +fi +max_cmd_len=$lt_cv_sys_max_cmd_len + + + + + + +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi + + + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 +$as_echo_n "checking how to convert $build file names to $host format... " >&6; } +if ${lt_cv_to_host_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac + +fi + +to_host_file_cmd=$lt_cv_to_host_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 +$as_echo "$lt_cv_to_host_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 +$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } +if ${lt_cv_to_tool_file_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + #assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac + +fi + +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 +$as_echo "$lt_cv_to_tool_file_cmd" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if ${lt_cv_ld_reload_flag+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + if test yes != "$GCC"; then + reload_cmds=false + fi + ;; + darwin*) + if test yes = "$GCC"; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" +fi + +test -z "$OBJDUMP" && OBJDUMP=objdump + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if ${lt_cv_deplibs_check_method+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + + + + + + + + + + + + + + + + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. +set dummy ${ac_tool_prefix}dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DLLTOOL"; then + ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DLLTOOL=$ac_cv_prog_DLLTOOL +if test -n "$DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 +$as_echo "$DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DLLTOOL"; then + ac_ct_DLLTOOL=$DLLTOOL + # Extract the first word of "dlltool", so it can be a program name with args. +set dummy dlltool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DLLTOOL"; then + ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DLLTOOL="dlltool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL +if test -n "$ac_ct_DLLTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 +$as_echo "$ac_ct_DLLTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DLLTOOL" = x; then + DLLTOOL="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DLLTOOL=$ac_ct_DLLTOOL + fi +else + DLLTOOL="$ac_cv_prog_DLLTOOL" +fi + +test -z "$DLLTOOL" && DLLTOOL=dlltool + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 +$as_echo_n "checking how to associate runtime and link libraries... " >&6; } +if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 +$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + + + + + + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} +: ${AR_FLAGS=cru} + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 +$as_echo_n "checking for archiver @FILE support... " >&6; } +if ${lt_cv_ar_at_file+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ar_at_file=no + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 + (eval $lt_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 +$as_echo "$lt_cv_ar_at_file" >&6; } + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi + + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +test -z "$STRIP" && STRIP=: + + + + + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +test -z "$RANLIB" && RANLIB=: + + + + + + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + +# Check for command to grab the raw symbol name followed by C symbol from nm. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if ${lt_cv_sys_global_symbol_pipe+:} false; then : + $as_echo_n "(cached) " >&6 +else + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRST]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[BCDT]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[ABCDGISTW]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[ABCDEGRST]' + fi + ;; +irix* | nonstopux*) + symcode='[BCDEGRST]' + ;; +osf*) + symcode='[BCDEGQRST]' + ;; +solaris*) + symcode='[BDRT]' + ;; +sco3.2v5*) + symcode='[DT]' + ;; +sysv4.2uw2*) + symcode='[DT]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[ABDT]' + ;; +sysv4) + symcode='[DFNSTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Now try to grab the symbols. + nlist=conftest.nm + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 + (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 + fi + else + echo "$progname: failed program was:" >&5 + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done + +fi + +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 +$as_echo_n "checking for sysroot... " >&6; } + +# Check whether --with-sysroot was given. +if test "${with_sysroot+set}" = set; then : + withval=$with_sysroot; +else + with_sysroot=no +fi + + +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 +$as_echo "$with_sysroot" >&6; } + as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 + ;; +esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 +$as_echo "${lt_sysroot:-no}" >&6; } + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 +$as_echo_n "checking for a working dd... " >&6; } +if ${ac_cv_path_lt_DD+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +if test -z "$lt_DD"; then + ac_path_lt_DD_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in dd; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_lt_DD" || continue +if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi + $ac_path_lt_DD_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_lt_DD"; then + : + fi +else + ac_cv_path_lt_DD=$lt_DD +fi + +rm -f conftest.i conftest2.i conftest.out +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 +$as_echo "$ac_cv_path_lt_DD" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 +$as_echo_n "checking how to truncate binary pipes... " >&6; } +if ${lt_cv_truncate_bin+:} false; then : + $as_echo_n "(cached) " >&6 +else + printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 +$as_echo "$lt_cv_truncate_bin" >&6; } + + + + + + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '#line '$LINENO' "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if ${lt_cv_cc_needs_belf+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. +set dummy ${ac_tool_prefix}mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$MANIFEST_TOOL"; then + ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL +if test -n "$MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 +$as_echo "$MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_MANIFEST_TOOL"; then + ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL + # Extract the first word of "mt", so it can be a program name with args. +set dummy mt; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_MANIFEST_TOOL"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL +if test -n "$ac_ct_MANIFEST_TOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 +$as_echo "$ac_ct_MANIFEST_TOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_MANIFEST_TOOL" = x; then + MANIFEST_TOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL + fi +else + MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" +fi + +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 +$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } +if ${lt_cv_path_mainfest_tool+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&5 + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 +$as_echo "$lt_cv_path_mainfest_tool" >&6; } +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi + + + + + + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DSYMUTIL=$ac_ct_DSYMUTIL + fi +else + DSYMUTIL="$ac_cv_prog_DSYMUTIL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + NMEDIT=$ac_ct_NMEDIT + fi +else + NMEDIT="$ac_cv_prog_NMEDIT" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_LIPO+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_LIPO" = x; then + LIPO=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LIPO=$ac_ct_LIPO + fi +else + LIPO="$ac_cv_prog_LIPO" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" +fi + + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 + fi +else + OTOOL64="$ac_cv_prog_OTOOL64" +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if ${lt_cv_apple_cc_single_mod+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&5 + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if ${lt_cv_ld_exported_symbols_list+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes +else + lt_cv_ld_exported_symbols_list=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 +$as_echo_n "checking for -force_load linker flag... " >&6; } +if ${lt_cv_ld_force_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 + echo "$AR cru libconftest.a conftest.o" >&5 + $AR cru libconftest.a conftest.o 2>&5 + echo "$RANLIB libconftest.a" >&5 + $RANLIB libconftest.a 2>&5 + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&5 + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&5 + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 +$as_echo "$lt_cv_ld_force_load" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[012][,.]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF + +fi + +done + + + + +func_stripname_cnf () +{ + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; + esac +} # func_stripname_cnf + + + + + +# Set options + + + + enable_dlopen=no + + + enable_win32_dll=no + + + + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_static=yes +fi + + + + + + + + + + +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + pic_mode=default +fi + + + + + + + + + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac +else + enable_fast_install=yes +fi + + + + + + + + + shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[5-9]*,yes) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 +$as_echo_n "checking which variant of shared library versioning to provide... " >&6; } + +# Check whether --with-aix-soname was given. +if test "${with_aix_soname+set}" = set; then : + withval=$with_aix_soname; case $withval in + aix|svr4|both) + ;; + *) + as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname +else + if ${lt_cv_with_aix_soname+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_with_aix_soname=aix +fi + + with_aix_soname=$lt_cv_with_aix_soname +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 +$as_echo "$with_aix_soname" >&6; } + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + + + + + + + + + + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +test -z "$LN_S" && LN_S="ln -s" + + + + + + + + + + + + + + +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if ${lt_cv_objdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir + + + + + +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF + + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/${ac_tool_prefix}file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + + + +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if ${lt_cv_path_MAGIC_CMD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/file"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac +fi + +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + else + MAGIC_CMD=: + fi +fi + + fi + ;; +esac + +# Use C for the default configuration in the libtool script + +lt_save_CC=$CC +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +objext=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + +if test -n "$compiler"; then + +lt_prog_compiler_no_builtin_flag= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; + *) + lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } + +if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : +fi + +fi + + + + + + + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= + + + if test yes = "$GCC"; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + lt_prog_compiler_pic='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; + + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi + ;; + + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + lt_prog_compiler_wl='-Xlinker ' + if test -n "$lt_prog_compiler_pic"; then + lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + nagfor*) + # NAG Fortran compiler + lt_prog_compiler_wl='-Wl,-Wl,,' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + *Sun\ F* | *Sun*Fortran*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Intel*\ [CF]*Compiler*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + *Portland\ Group*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + + rdos*) + lt_prog_compiler_static='-non_shared' + ;; + + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; + + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; + + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; + + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; + + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic=$lt_prog_compiler_pic +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 +$as_echo "$lt_cv_prog_compiler_pic" >&6; } +lt_prog_compiler_pic=$lt_cv_prog_compiler_pic + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if ${lt_cv_prog_compiler_pic_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works"; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi + +fi + + + + + + + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works"; then + : +else + lt_prog_compiler_static= +fi + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs=no + ;; + esac + + ld_shlibs=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; + *\ \(GNU\ Binutils\)\ [3-9]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + export_dynamic_flag_spec='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + export_dynamic_flag_spec='$wl--export-all-symbols' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; + + haiku*) + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs=yes + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + export_dynamic_flag_spec='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; + + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + + if test no = "$ld_shlibs"; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct=no + hardcode_direct_absolute=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath_+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath_"; then + lt_cv_aix_libpath_=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath_ +fi + + hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' $wl-bernotok' + allow_undefined_flag=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + fi + archive_cmds_need_lc=yes + archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; + + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + always_export_symbols=yes + file_list_spec='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, )='true' + enable_shared_with_static_runtimes=yes + exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + old_postinstall_cmds='chmod 644 $oldlib' + postlink_cmds='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + enable_shared_with_static_runtimes=yes + ;; + esac + ;; + + darwin* | rhapsody*) + + + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec='' + fi + link_all_deplibs=yes + allow_undefined_flag=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + + else + ld_shlibs=no + fi + + ;; + + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + if test yes = "$GCC"; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 +$as_echo_n "checking if $CC understands -b... " >&6; } +if ${lt_cv_prog_compiler__b+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler__b=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -b" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler__b=yes + fi + else + lt_cv_prog_compiler__b=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 +$as_echo "$lt_cv_prog_compiler__b" >&6; } + +if test yes = "$lt_cv_prog_compiler__b"; then + archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' +else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' +fi + + ;; + esac + fi + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec='$wl+b $wl$libdir' + hardcode_libdir_separator=: + + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 +$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } +if ${lt_cv_irix_exported_symbol+:} false; then : + $as_echo_n "(cached) " >&6 +else + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo (void) { return 0; } +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_irix_exported_symbol=yes +else + lt_cv_irix_exported_symbol=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 +$as_echo "$lt_cv_irix_exported_symbol" >&6; } + if test yes = "$lt_cv_irix_exported_symbol"; then + archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + link_all_deplibs=no + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + ld_shlibs=yes + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + export_dynamic_flag_spec='$wl-E' + else + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='$wl-rpath,$libdir' + fi + else + ld_shlibs=no + fi + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + shrext_cmds=.dll + archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes=yes + ;; + + osf3*) + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + hardcode_libdir_separator=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + allow_undefined_flag=' $wl-expect_unresolved $wl\*' + archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; + + solaris*) + no_undefined_flag=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + sysv4) + case $host_vendor in + sni) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' + reload_cmds='$CC -r -o $output$reload_objs' + hardcode_direct=no + ;; + motorola) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + hardcode_shlibpath_var=no + ;; + + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='$wl-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='$wl-z,text' + allow_undefined_flag='$wl-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='$wl-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='$wl-Blargedynsym' + ;; + esac + fi + fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test no = "$ld_shlibs" && can_build_shared=no + +with_gnu_ld=$with_gnu_ld + + + + + + + + + + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc=no + else + lt_cv_archive_cmds_need_lc=yes + fi + allow_undefined_flag=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc" >&6; } + archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([A-Za-z]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test yes = "$hardcode_automatic"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && + test no != "$hardcode_minus_L"; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } + +if test relink = "$hardcode_action" || + test yes = "$inherit_rpath"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + +fi + + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = xyes; then : + lt_cv_dlopen=shl_load +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if ${ac_cv_lib_dld_shl_load+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = xyes; then : + lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = xyes; then : + lt_cv_dlopen=dlopen +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if ${ac_cv_lib_dl_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if ${ac_cv_lib_svld_dlopen+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = xyes; then : + lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if ${ac_cv_lib_dld_dld_link+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = xyes; then : + lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld +fi + + +fi + + +fi + + +fi + + +fi + + +fi + + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if ${lt_cv_dlopen_self_static+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test yes = "$cross_compiling"; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi + + + + + + + + + + + + + + + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + ;; + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + ;; + esac +fi + + + + + + + + + + + + + # Report what library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[4-9]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } + + + + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +CC=$lt_save_CC + + if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if ${ac_cv_prog_CXXCPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +else + _lt_caught_CXX_error=yes +fi + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +reload_flag_CXX=$reload_flag +reload_cmds_CXX=$reload_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* + + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* + + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + compiler_CXX=$CC + func_cc_basename $compiler +cc_basename=$func_cc_basename_result + + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + + +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes +else + with_gnu_ld=no +fi + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if ${lt_cv_path_LD+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if ${lt_cv_prog_gnu_ld+:} false; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld + + + + + + + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + hardcode_direct_CXX=no + hardcode_direct_absolute_CXX=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + export_dynamic_flag_spec_CXX='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + no_undefined_flag_CXX='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + if ${lt_cv_aix_libpath__CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + + lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }' + lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test -z "$lt_cv_aix_libpath__CXX"; then + lt_cv_aix_libpath__CXX=/usr/lib:/lib + fi + +fi + + aix_libpath=$lt_cv_aix_libpath__CXX +fi + + hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' $wl-bernotok' + allow_undefined_flag_CXX=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + fi + archive_cmds_need_lc_CXX=yes + archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec_CXX=' ' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=yes + file_list_spec_CXX='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' + enable_shared_with_static_runtimes_CXX=yes + # Don't use ranlib + old_postinstall_cmds_CXX='chmod 644 $oldlib' + postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + export_dynamic_flag_spec_CXX='$wl--export-all-symbols' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + + + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + if test yes = "$lt_cv_ld_force_load"; then + whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + + else + whole_archive_flag_spec_CXX='' + fi + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + if test yes != "$lt_cv_apple_cc_single_mod"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi + + else + ld_shlibs_CXX=no + fi + + ;; + + os2*) + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_minus_L_CXX=yes + allow_undefined_flag_CXX=unsupported + shrext_cmds=.dll + archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + enable_shared_with_static_runtimes_CXX=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; + + freebsd-elf*) + archive_cmds_need_lc_CXX=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; + + haiku*) + archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + link_all_deplibs_CXX=yes + ;; + + hpux9*) + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='$wl-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' + hardcode_libdir_separator_CXX=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5].* | *pgcpp\ [1-5].*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + export_dynamic_flag_spec_CXX='$wl--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='$wl-E' + whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + ld_shlibs_CXX=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac + + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + no_undefined_flag_CXX=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='$wl-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='$wl-z,text' + allow_undefined_flag_CXX='$wl-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ + '"$old_archive_cmds_CXX" + reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ + '"$reload_cmds_CXX" + ;; + *) + archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test no = "$ld_shlibs_CXX" && can_build_shared=no + + GCC_CXX=$GXX + LD_CXX=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= + +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF + + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX=$prev$p + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX=$prev$p + else + postdeps_CXX="${postdeps_CXX} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX=$p + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX=$p + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; +esac + + +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= + + + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + lt_prog_compiler_pic_CXX='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + case $host_os in + os2*) + lt_prog_compiler_static_CXX='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic_CXX='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + lt_prog_compiler_static_CXX= + ;; + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic_CXX=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + *) + lt_prog_compiler_pic_CXX='-fPIC' + ;; + esac + else + case $host_os in + aix[4-9]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' + ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi + +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; +esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } +if ${lt_cv_prog_compiler_pic_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } +lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi + +fi + + + + + +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } + +if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then + : +else + lt_prog_compiler_static_CXX= +fi + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } + + + + +hard_links=nottested +if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test no = "$hard_links"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + link_all_deplibs_CXX=no + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test no = "$ld_shlibs_CXX" && can_build_shared=no + +with_gnu_ld_CXX=$with_gnu_ld + + + + + + +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc_CXX" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc_CXX=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $archive_cmds_CXX in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } +if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + lt_cv_archive_cmds_need_lc_CXX=no + else + lt_cv_archive_cmds_need_lc_CXX=yes + fi + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 +$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } + archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX + ;; + esac + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + + + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[4-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[01] | aix4.[01].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a(lib.so.V)' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[45]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' + + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[23].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[01]* | freebsdelf3.[01]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ + freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[3-9]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + hardcode_libdir_flag_spec_CXX='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + if ${lt_cv_shlibpath_overrides_runpath+:} false; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + lt_cv_shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + +fi + + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test yes = "$hardcode_automatic_CXX"; then + + # We can hardcode non-existent directories. + if test no != "$hardcode_direct_CXX" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && + test no != "$hardcode_minus_L_CXX"; then + # Linking always hardcodes the temporary library directory. + hardcode_action_CXX=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action_CXX=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action_CXX=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } + +if test relink = "$hardcode_action_CXX" || + test yes = "$inherit_rpath_CXX"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi + + + + + + + + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + ac_config_commands="$ac_config_commands libtool" + + + + +# Only expand once: + + + +# Checks for libraries. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 +$as_echo_n "checking for main in -lm... " >&6; } +if ${ac_cv_lib_m_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_m_main=yes +else + ac_cv_lib_m_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5 +$as_echo "$ac_cv_lib_m_main" >&6; } +if test "x$ac_cv_lib_m_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + +# FIXME: Replace `main' with a function in `-lrt': +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lrt" >&5 +$as_echo_n "checking for main in -lrt... " >&6; } +if ${ac_cv_lib_rt_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_main=yes +else + ac_cv_lib_rt_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_main" >&5 +$as_echo "$ac_cv_lib_rt_main" >&6; } +if test "x$ac_cv_lib_rt_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRT 1 +_ACEOF + + LIBS="-lrt $LIBS" + +fi + + +# Checks for header files. +for ac_header in float.h stdlib.h string.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" +if test "x$ac_cv_type_size_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + + +# Checks for library functions. +ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" +if test "x$ac_cv_type_pid_t" = xyes; then : + +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +for ac_header in vfork.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" +if test "x$ac_cv_header_vfork_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_VFORK_H 1 +_ACEOF + +fi + +done + +for ac_func in fork vfork +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 +$as_echo_n "checking for working fork... " >&6; } +if ${ac_cv_func_fork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_fork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_fork_works=yes +else + ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 +$as_echo "$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 +$as_echo_n "checking for working vfork... " >&6; } +if ${ac_cv_func_vfork_works+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + ac_cv_func_vfork_works=cross +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_func_vfork_works=yes +else + ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 +$as_echo "$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h + +else + +$as_echo "#define vfork fork" >>confdefs.h + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h + +fi + +for ac_func in clock_gettime +do : + ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" +if test "x$ac_cv_func_clock_gettime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCK_GETTIME 1 +_ACEOF + +fi +done + + +use_double=no +# Check whether --enable-double-precision was given. +if test "${enable_double_precision+set}" = set; then : + enableval=$enable_double_precision; use_double=$enableval +fi + +if test x$use_double = xno +then + CCD_PRECISION=CCD_SINGLE +else + CCD_PRECISION=CCD_DOUBLE +fi + + +ac_config_files="$ac_config_files Makefile src/Makefile src/ccd/precision.h src/testsuites/Makefile src/testsuites/cu/Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by libccd $as_me 1.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +libccd config.status 1.0 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' +macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' +macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' +enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' +pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' +shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' +SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' +ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' +PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' +host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' +host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' +host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' +build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' +build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' +build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' +SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' +Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' +GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' +EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' +FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' +LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' +NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' +LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' +ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' +exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' +lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' +lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' +lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' +reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' +file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' +want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' +DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' +sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' +AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' +archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' +STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' +RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' +lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' +CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' +compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' +GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' +lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' +nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' +lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' +lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' +objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' +need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' +MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' +LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' +OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' +libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' +module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' +postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' +need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' +version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' +runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' +libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' +soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' +install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' +finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' +configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' +configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' +old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' +striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' +predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' +predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' +postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' +reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' +reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in SHELL \ +ECHO \ +PATH_SEPARATOR \ +SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +file_magic_glob \ +want_nocaseglob \ +DLLTOOL \ +sharedlib_from_linklib_cmd \ +AR \ +AR_FLAGS \ +archiver_list_spec \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_import \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +lt_cv_nm_interface \ +nm_file_list_spec \ +lt_cv_truncate_bin \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_pic \ +lt_prog_compiler_wl \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +MANIFEST_TOOL \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_separator \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +install_override_mode \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +reload_flag_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_separator_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postlink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +configure_time_dlsearch_path \ +configure_time_lt_sys_library_path \ +reload_cmds_CXX \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX \ +postlink_cmds_CXX; do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +ac_aux_dir='$ac_aux_dir' + +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + + + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile' + + + + + + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "src/ccd/precision.h") CONFIG_FILES="$CONFIG_FILES src/ccd/precision.h" ;; + "src/testsuites/Makefile") CONFIG_FILES="$CONFIG_FILES src/testsuites/Makefile" ;; + "src/testsuites/cu/Makefile") CONFIG_FILES="$CONFIG_FILES src/testsuites/cu/Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named 'Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running 'make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "$am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done + done +} + ;; + "libtool":C) + + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +# The names of the tagged configurations supported by this script. +available_tags='CXX ' + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG + +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared + +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision + +# Whether or not to build static libraries. +build_old_libs=$enable_static + +# What type of objects to build. +pic_mode=$pic_mode + +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install + +# Shared archive member basename,for filename based shared library versioning on AIX. +shared_archive_member_spec=$shared_archive_member_spec + +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL + +# An echo program that protects backslashes. +ECHO=$lt_ECHO + +# The PATH separator for the build system. +PATH_SEPARATOR=$lt_PATH_SEPARATOR + +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os + +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os + +# A sed program that does not truncate output. +SED=$lt_SED + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" + +# A grep program that handles long lines. +GREP=$lt_GREP + +# An ERE matcher. +EGREP=$lt_EGREP + +# A literal string matcher. +FGREP=$lt_FGREP + +# A BSD- or MS-compatible name lister. +NM=$lt_NM + +# Whether we need soft or hard links. +LN_S=$lt_LN_S + +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len + +# Object file suffix (normally "o"). +objext=$ac_objext + +# Executable file suffix (normally ""). +exeext=$exeext + +# whether the shell understands "unset". +lt_unset=$lt_unset + +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL + +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP + +# convert \$build file names to \$host format. +to_host_file_cmd=$lt_cv_to_host_file_cmd + +# convert \$build files to toolchain format. +to_tool_file_cmd=$lt_cv_to_tool_file_cmd + +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP + +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method + +# Command to use when deplibs_check_method = "file_magic". +file_magic_cmd=$lt_file_magic_cmd + +# How to find potential files when deplibs_check_method = "file_magic". +file_magic_glob=$lt_file_magic_glob + +# Find potential files using nocaseglob when deplibs_check_method = "file_magic". +want_nocaseglob=$lt_want_nocaseglob + +# DLL creation program. +DLLTOOL=$lt_DLLTOOL + +# Command to associate shared and link libraries. +sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd + +# The archiver. +AR=$lt_AR + +# Flags to create an archive. +AR_FLAGS=$lt_AR_FLAGS + +# How to feed a file listing to the archiver. +archiver_list_spec=$lt_archiver_list_spec + +# A symbol stripping program. +STRIP=$lt_STRIP + +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds + +# Whether to use a lock for old archive extraction. +lock_old_archive_extraction=$lock_old_archive_extraction + +# A C compiler. +LTCC=$lt_CC + +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + +# Transform the output of nm into a list of symbols to manually relocate. +global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import + +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix + +# The name lister interface. +nm_interface=$lt_lt_cv_nm_interface + +# Specify filename containing input files for \$NM. +nm_file_list_spec=$lt_nm_file_list_spec + +# The root where to search for dependent libraries,and where our libraries should be installed. +lt_sysroot=$lt_sysroot + +# Command to truncate a binary pipe. +lt_truncate_bin=$lt_lt_cv_truncate_bin + +# The name of the directory that contains temporary libtool files. +objdir=$objdir + +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD + +# Must we lock files when doing compilation? +need_locks=$lt_need_locks + +# Manifest tool. +MANIFEST_TOOL=$lt_MANIFEST_TOOL + +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL + +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT + +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO + +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL + +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 + +# Old archive suffix (normally "a"). +libext=$libext + +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds + +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds + +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink + +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix + +# Do we need a version for libraries? +need_version=$need_version + +# Library versioning type. +version_type=$version_type + +# Shared library runtime path variable. +runpath_var=$runpath_var + +# Shared library path variable. +shlibpath_var=$shlibpath_var + +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath + +# Format of library name prefix. +libname_spec=$lt_libname_spec + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec + +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec + +# Permission mode override for installation of shared libraries. +install_override_mode=$lt_install_override_mode + +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds + +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds + +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds + +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval + +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs + +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + +# Detected run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path + +# Explicit LT_SYS_LIBRARY_PATH set during ./configure time. +configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path + +# Whether dlopen is supported. +dlopen_support=$enable_dlopen + +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self + +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static + +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib + + +# The linker used to build libraries. +LD=$lt_LD + +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds + +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path + +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x$2 in + x) + ;; + *:) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" + ;; + x:*) + eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" + ;; + *) + eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" + ;; + esac +} + + +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in $*""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} + + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + +ltmain=$ac_aux_dir/ltmain.sh + + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" + + + cat <<_LT_EOF >> "$ofile" + +# ### BEGIN LIBTOOL TAG CONFIG: CXX + +# The linker used to build libraries. +LD=$lt_LD_CXX + +# How to create reloadable object files. +reload_flag=$lt_reload_flag_CXX +reload_cmds=$lt_reload_cmds_CXX + +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX + +# A language specific compiler. +CC=$lt_compiler_CXX + +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX + +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX + +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX + +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX + +# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \$shlibpath_var if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX + +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX + +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX + +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX + +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX + +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX + +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX + +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX + +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX + +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Commands necessary for finishing linking programs. +postlink_cmds=$lt_postlink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF + + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/thirdparty/ode-0.16.5/libccd/configure.ac b/thirdparty/ode-0.16.5/libccd/configure.ac new file mode 100644 index 0000000..5a12836 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/configure.ac @@ -0,0 +1,50 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +#AC_PREREQ([2.65]) +AC_INIT([libccd], [1.0], [danfis@danfis.cz]) +AC_CONFIG_SRCDIR([src/ccd.c]) +AC_CONFIG_HEADERS([src/config.h]) +AM_INIT_AUTOMAKE(foreign) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC +AC_PROG_INSTALL +AC_DISABLE_SHARED +LT_INIT + +# Checks for libraries. +AC_CHECK_LIB([m], [main]) +# FIXME: Replace `main' with a function in `-lrt': +AC_CHECK_LIB([rt], [main]) + +# Checks for header files. +AC_CHECK_HEADERS([float.h stdlib.h string.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_FORK +AC_CHECK_FUNCS([clock_gettime]) + +use_double=no +AC_ARG_ENABLE(double-precision, + AS_HELP_STRING([--enable-double-precision], + [enable double precision computations instead of single precision]), + [use_double=$enableval]) +if test x$use_double = xno +then + CCD_PRECISION=CCD_SINGLE +else + CCD_PRECISION=CCD_DOUBLE +fi +AC_SUBST(CCD_PRECISION) + +AC_CONFIG_FILES([Makefile + src/Makefile + src/ccd/precision.h + src/testsuites/Makefile + src/testsuites/cu/Makefile]) +AC_OUTPUT diff --git a/thirdparty/ode-0.16.5/libccd/src/Makefile.am b/thirdparty/ode-0.16.5/libccd/src/Makefile.am new file mode 100644 index 0000000..e8655ba --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/Makefile.am @@ -0,0 +1,22 @@ +SUBDIRS = testsuites + +AM_CPPFLAGS = -I$(top_srcdir)/src/custom +AM_CFLAGS = -std=c99 + +noinst_LTLIBRARIES = libccd.la + +libccd_la_SOURCES = alloc.c ccd/alloc.h \ + ccd/compiler.h \ + ccd/dbg.h \ + ccd.c ccd/ccd.h \ + ccd/list.h \ + polytope.c ccd/polytope.h \ + ccd/quat.h custom/ccdcustom/quat.h \ + ccd/simplex.h \ + support.c ccd/support.h \ + vec3.c ccd/vec3.h custom/ccdcustom/vec3.h \ + mpr.c + +nodist_libccd_la_SOURCES = ccd/precision.h + +EXTRA_DIST = ccd/precision.h.in diff --git a/thirdparty/ode-0.16.5/libccd/src/Makefile.in b/thirdparty/ode-0.16.5/libccd/src/Makefile.in new file mode 100644 index 0000000..bab323f --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/Makefile.in @@ -0,0 +1,744 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libccd_la_LIBADD = +am_libccd_la_OBJECTS = alloc.lo ccd.lo polytope.lo support.lo vec3.lo \ + mpr.lo +nodist_libccd_la_OBJECTS = +libccd_la_OBJECTS = $(am_libccd_la_OBJECTS) \ + $(nodist_libccd_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libccd_la_SOURCES) $(nodist_libccd_la_SOURCES) +DIST_SOURCES = $(libccd_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(top_srcdir)/../depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_PRECISION = @CCD_PRECISION@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = testsuites +AM_CPPFLAGS = -I$(top_srcdir)/src/custom +AM_CFLAGS = -std=c99 +noinst_LTLIBRARIES = libccd.la +libccd_la_SOURCES = alloc.c ccd/alloc.h \ + ccd/compiler.h \ + ccd/dbg.h \ + ccd.c ccd/ccd.h \ + ccd/list.h \ + polytope.c ccd/polytope.h \ + ccd/quat.h custom/ccdcustom/quat.h \ + ccd/simplex.h \ + support.c ccd/support.h \ + vec3.c ccd/vec3.h custom/ccdcustom/vec3.h \ + mpr.c + +nodist_libccd_la_SOURCES = ccd/precision.h +EXTRA_DIST = ccd/precision.h.in +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libccd.la: $(libccd_la_OBJECTS) $(libccd_la_DEPENDENCIES) $(EXTRA_libccd_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libccd_la_OBJECTS) $(libccd_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ccd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polytope.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vec3.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) all install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/libccd/src/alloc.c b/thirdparty/ode-0.16.5/libccd/src/alloc.c new file mode 100644 index 0000000..d3fb213 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/alloc.c @@ -0,0 +1,38 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +void *ccdRealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (ret == NULL && size != 0){ + fprintf(stderr, "Fatal error: Allocation of memory failed!\n"); + fflush(stderr); + exit(-1); + } + + return ret; +} + + diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd.c b/thirdparty/ode-0.16.5/libccd/src/ccd.c new file mode 100644 index 0000000..636db01 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd.c @@ -0,0 +1,955 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +/** Performs GJK algorithm. Returns 0 if intersection was found and simplex + * is filled with resulting polytope. */ +static int __ccdGJK(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *simplex); + +/** Performs GJK+EPA algorithm. Returns 0 if intersection was found and + * pt is filled with resulting polytope and nearest with pointer to + * nearest element (vertex, edge, face) of polytope to origin. */ +static int __ccdGJKEPA(const void *obj1, const void *obj2, + const ccd_t *ccd, + ccd_pt_t *pt, ccd_pt_el_t **nearest); + + +/** Returns true if simplex contains origin. + * This function also alteres simplex and dir according to further + * processing of GJK algorithm. */ +static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir); +static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir); +static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir); +static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir); + +/** d = a x b x c */ +_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b, + const ccd_vec3_t *c, ccd_vec3_t *d); + + +/** Transforms simplex to polytope. It is assumed that simplex has 4 + * vertices. */ +static int simplexToPolytope4(const void *obj1, const void *obj2, + const ccd_t *ccd, + ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest); + +/** Transforms simplex to polytope, three vertices required */ +static int simplexToPolytope3(const void *obj1, const void *obj2, + const ccd_t *ccd, + const ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest); + +/** Transforms simplex to polytope, two vertices required */ +static int simplexToPolytope2(const void *obj1, const void *obj2, + const ccd_t *ccd, + const ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest); + +/** Expands polytope using new vertex v. */ +static void expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el, + const ccd_support_t *newv); + +/** Finds next support point (and stores it in out argument). + * Returns 0 on success, -1 otherwise */ +static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd, + const ccd_pt_el_t *el, + ccd_support_t *out); + + + +void ccdFirstDirDefault(const void *o1, const void *o2, ccd_vec3_t *dir) +{ + ccdVec3Set(dir, CCD_ONE, CCD_ZERO, CCD_ZERO); +} + +int ccdGJKIntersect(const void *obj1, const void *obj2, const ccd_t *ccd) +{ + ccd_simplex_t simplex; + return __ccdGJK(obj1, obj2, ccd, &simplex) == 0; +} + +int ccdGJKSeparate(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_vec3_t *sep) +{ + ccd_pt_t polytope; + ccd_pt_el_t *nearest; + int ret; + + ccdPtInit(&polytope); + + ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest); + + // set separation vector + if (nearest) + ccdVec3Copy(sep, &nearest->witness); + + ccdPtDestroy(&polytope); + + return ret; +} + + +static int penEPAPosCmp(const void *a, const void *b) +{ + ccd_pt_vertex_t *v1, *v2; + v1 = *(ccd_pt_vertex_t **)a; + v2 = *(ccd_pt_vertex_t **)b; + + if (ccdEq(v1->dist, v2->dist)){ + return 0; + }else if (v1->dist < v2->dist){ + return -1; + }else{ + return 1; + } +} + +static void penEPAPos(const ccd_pt_t *pt, const ccd_pt_el_t *nearest, + ccd_vec3_t *pos) +{ + ccd_pt_vertex_t *v; + ccd_pt_vertex_t **vs; + size_t i, len; + ccd_real_t scale; + + // compute median + len = 0; + ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){ + len++; + } + + vs = CCD_ALLOC_ARR(ccd_pt_vertex_t *, len); + i = 0; + ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){ + vs[i++] = v; + } + + qsort(vs, len, sizeof(ccd_pt_vertex_t *), penEPAPosCmp); + + ccdVec3Set(pos, CCD_ZERO, CCD_ZERO, CCD_ZERO); + scale = CCD_ZERO; + if (len % 2 == 1) + len++; + + for (i = 0; i < len / 2; i++){ + ccdVec3Add(pos, &vs[i]->v.v1); + ccdVec3Add(pos, &vs[i]->v.v2); + scale += CCD_REAL(2.); + } + ccdVec3Scale(pos, CCD_ONE / scale); + + free(vs); +} + +int ccdGJKPenetration(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) +{ + ccd_pt_t polytope; + ccd_pt_el_t *nearest; + int ret; + + ccdPtInit(&polytope); + + ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest); + + // set separation vector + if (ret == 0 && nearest){ + // store normalized direction vector + ccdVec3Copy(dir, &nearest->witness); + ret = ccdVec3SafeNormalize(dir); + + if (ret == 0) { + // compute depth of penetration + *depth = CCD_SQRT(nearest->dist); + // compute position + penEPAPos(&polytope, nearest, pos); + } + } + + ccdPtDestroy(&polytope); + + return ret; +} + + + + +static int __ccdGJK(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *simplex) +{ + unsigned long iterations; + ccd_vec3_t dir; // direction vector + ccd_support_t last; // last support point + int do_simplex_res; + + // initialize simplex struct + ccdSimplexInit(simplex); + + // get first direction + ccd->first_dir(obj1, obj2, &dir); + // get first support point + __ccdSupport(obj1, obj2, &dir, ccd, &last); + // and add this point to simplex as last one + ccdSimplexAdd(simplex, &last); + + // set up direction vector as (O - last) which is exactly -last + ccdVec3Copy(&dir, &last.v); + ccdVec3Scale(&dir, -CCD_ONE); + + // start iterations + for (iterations = 0UL; iterations < ccd->max_iterations; ++iterations) { + // obtain support point + __ccdSupport(obj1, obj2, &dir, ccd, &last); + + // check if farthest point in Minkowski difference in direction dir + // isn't somewhere before origin (the test on negative dot product) + // - because if it is, objects are not intersecting at all. + if (ccdVec3Dot(&last.v, &dir) < CCD_ZERO){ + return -1; // intersection not found + } + + // add last support vector to simplex + ccdSimplexAdd(simplex, &last); + + // if doSimplex returns 1 if objects intersect, -1 if objects don't + // intersect and 0 if algorithm should continue + do_simplex_res = doSimplex(simplex, &dir); + if (do_simplex_res == 1){ + return 0; // intersection found + }else if (do_simplex_res == -1){ + return -1; // intersection not found + } + + if (ccdIsZero(ccdVec3Len2(&dir))){ + return -1; // intersection not found + } + } + + // intersection wasn't found + return -1; +} + +static int __ccdGJKEPA(const void *obj1, const void *obj2, + const ccd_t *ccd, + ccd_pt_t *polytope, ccd_pt_el_t **nearest) +{ + ccd_simplex_t simplex; + ccd_support_t supp; // support point + int ret, size; + + *nearest = NULL; + + // run GJK and obtain terminal simplex + ret = __ccdGJK(obj1, obj2, ccd, &simplex); + if (ret != 0) + return -1; + + // transform simplex to polytope - simplex won't be used anymore + size = ccdSimplexSize(&simplex); + if (size == 4){ + if (simplexToPolytope4(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){ + return 0;// touch contact + } + }else if (size == 3){ + if (simplexToPolytope3(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){ + return 0; // touch contact + } + }else{ // size == 2 + if (simplexToPolytope2(obj1, obj2, ccd, &simplex, polytope, nearest) != 0){ + return 0; // touch contact + } + } + + while (1){ + // get triangle nearest to origin + *nearest = ccdPtNearest(polytope); + + // get next support point + if (nextSupport(obj1, obj2, ccd, *nearest, &supp) != 0) + break; + + // expand nearest triangle using new point - supp + expandPolytope(polytope, *nearest, &supp); + } + + return 0; +} + + + +static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir) +{ + const ccd_support_t *A, *B; + ccd_vec3_t AB, AO, tmp; + ccd_real_t dot; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other point + B = ccdSimplexPoint(simplex, 0); + // compute AB oriented segment + ccdVec3Sub2(&AB, &B->v, &A->v); + // compute AO vector + ccdVec3Copy(&AO, &A->v); + ccdVec3Scale(&AO, -CCD_ONE); + + // dot product AB . AO + dot = ccdVec3Dot(&AB, &AO); + + // check if origin doesn't lie on AB segment + ccdVec3Cross(&tmp, &AB, &AO); + if (ccdIsZero(ccdVec3Len2(&tmp)) && dot > CCD_ZERO){ + return 1; + } + + // check if origin is in area where AB segment is + if (ccdIsZero(dot) || dot < CCD_ZERO){ + // origin is in outside are of A + ccdSimplexSet(simplex, 0, A); + ccdSimplexSetSize(simplex, 1); + ccdVec3Copy(dir, &AO); + }else{ + // origin is in area where AB segment is + + // keep simplex untouched and set direction to + // AB x AO x AB + tripleCross(&AB, &AO, &AB, dir); + } + + return 0; +} + +static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir) +{ + const ccd_support_t *A, *B, *C; + ccd_vec3_t AO, AB, AC, ABC, tmp; + ccd_real_t dot, dist; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other points + B = ccdSimplexPoint(simplex, 1); + C = ccdSimplexPoint(simplex, 0); + + // check touching contact + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL); + if (ccdIsZero(dist)){ + return 1; + } + + // check if triangle is really triangle (has area > 0) + // if not simplex can't be expanded and thus no intersection is found + if (ccdVec3Eq(&A->v, &B->v) || ccdVec3Eq(&A->v, &C->v)){ + return -1; + } + + // compute AO vector + ccdVec3Copy(&AO, &A->v); + ccdVec3Scale(&AO, -CCD_ONE); + + // compute AB and AC segments and ABC vector (perpendicular to triangle) + ccdVec3Sub2(&AB, &B->v, &A->v); + ccdVec3Sub2(&AC, &C->v, &A->v); + ccdVec3Cross(&ABC, &AB, &AC); + + ccdVec3Cross(&tmp, &ABC, &AC); + dot = ccdVec3Dot(&tmp, &AO); + if (ccdIsZero(dot) || dot > CCD_ZERO){ + dot = ccdVec3Dot(&AC, &AO); + if (ccdIsZero(dot) || dot > CCD_ZERO){ + // C is already in place + ccdSimplexSet(simplex, 1, A); + ccdSimplexSetSize(simplex, 2); + tripleCross(&AC, &AO, &AC, dir); + }else{ +ccd_do_simplex3_45: + dot = ccdVec3Dot(&AB, &AO); + if (ccdIsZero(dot) || dot > CCD_ZERO){ + ccdSimplexSet(simplex, 0, B); + ccdSimplexSet(simplex, 1, A); + ccdSimplexSetSize(simplex, 2); + tripleCross(&AB, &AO, &AB, dir); + }else{ + ccdSimplexSet(simplex, 0, A); + ccdSimplexSetSize(simplex, 1); + ccdVec3Copy(dir, &AO); + } + } + }else{ + ccdVec3Cross(&tmp, &AB, &ABC); + dot = ccdVec3Dot(&tmp, &AO); + if (ccdIsZero(dot) || dot > CCD_ZERO){ + goto ccd_do_simplex3_45; + }else{ + dot = ccdVec3Dot(&ABC, &AO); + if (ccdIsZero(dot) || dot > CCD_ZERO){ + ccdVec3Copy(dir, &ABC); + }else{ + ccd_support_t Ctmp; + ccdSupportCopy(&Ctmp, C); + ccdSimplexSet(simplex, 0, B); + ccdSimplexSet(simplex, 1, &Ctmp); + + ccdVec3Copy(dir, &ABC); + ccdVec3Scale(dir, -CCD_ONE); + } + } + } + + return 0; +} + +static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir) +{ + const ccd_support_t *A, *B, *C, *D; + ccd_vec3_t AO, AB, AC, AD, ABC, ACD, ADB; + int B_on_ACD, C_on_ADB, D_on_ABC; + int AB_O, AC_O, AD_O; + ccd_real_t dist; + + // get last added as A + A = ccdSimplexLast(simplex); + // get the other points + B = ccdSimplexPoint(simplex, 2); + C = ccdSimplexPoint(simplex, 1); + D = ccdSimplexPoint(simplex, 0); + + // check if tetrahedron is really tetrahedron (has volume > 0) + // if it is not simplex can't be expanded and thus no intersection is + // found + dist = ccdVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, NULL); + if (ccdIsZero(dist)){ + return -1; + } + + // check if origin lies on some of tetrahedron's face - if so objects + // intersect + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL); + if (ccdIsZero(dist)) + return 1; + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &C->v, &D->v, NULL); + if (ccdIsZero(dist)) + return 1; + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &D->v, NULL); + if (ccdIsZero(dist)) + return 1; + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &B->v, &C->v, &D->v, NULL); + if (ccdIsZero(dist)) + return 1; + + // compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors + ccdVec3Copy(&AO, &A->v); + ccdVec3Scale(&AO, -CCD_ONE); + ccdVec3Sub2(&AB, &B->v, &A->v); + ccdVec3Sub2(&AC, &C->v, &A->v); + ccdVec3Sub2(&AD, &D->v, &A->v); + ccdVec3Cross(&ABC, &AB, &AC); + ccdVec3Cross(&ACD, &AC, &AD); + ccdVec3Cross(&ADB, &AD, &AB); + + // side (positive or negative) of B, C, D relative to planes ACD, ADB + // and ABC respectively + B_on_ACD = ccdSign(ccdVec3Dot(&ACD, &AB)); + C_on_ADB = ccdSign(ccdVec3Dot(&ADB, &AC)); + D_on_ABC = ccdSign(ccdVec3Dot(&ABC, &AD)); + + // whether origin is on same side of ACD, ADB, ABC as B, C, D + // respectively + AB_O = ccdSign(ccdVec3Dot(&ACD, &AO)) == B_on_ACD; + AC_O = ccdSign(ccdVec3Dot(&ADB, &AO)) == C_on_ADB; + AD_O = ccdSign(ccdVec3Dot(&ABC, &AO)) == D_on_ABC; + + if (AB_O && AC_O && AD_O){ + // origin is in tetrahedron + return 1; + + // rearrange simplex to triangle and call doSimplex3() + }else if (!AB_O){ + // B is farthest from the origin among all of the tetrahedron's + // points, so remove it from the list and go on with the triangle + // case + + // D and C are in place + ccdSimplexSet(simplex, 2, A); + ccdSimplexSetSize(simplex, 3); + }else if (!AC_O){ + // C is farthest + ccdSimplexSet(simplex, 1, D); + ccdSimplexSet(simplex, 0, B); + ccdSimplexSet(simplex, 2, A); + ccdSimplexSetSize(simplex, 3); + }else{ // (!AD_O) + ccdSimplexSet(simplex, 0, C); + ccdSimplexSet(simplex, 1, B); + ccdSimplexSet(simplex, 2, A); + ccdSimplexSetSize(simplex, 3); + } + + return doSimplex3(simplex, dir); +} + +static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir) +{ + if (ccdSimplexSize(simplex) == 2){ + // simplex contains segment only one segment + return doSimplex2(simplex, dir); + }else if (ccdSimplexSize(simplex) == 3){ + // simplex contains triangle + return doSimplex3(simplex, dir); + }else{ // ccdSimplexSize(simplex) == 4 + // tetrahedron - this is the only shape which can encapsule origin + // so doSimplex4() also contains test on it + return doSimplex4(simplex, dir); + } +} + +_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b, + const ccd_vec3_t *c, ccd_vec3_t *d) +{ + ccd_vec3_t e; + ccdVec3Cross(&e, a, b); + ccdVec3Cross(d, &e, c); +} + + + +/** Transforms simplex to polytope. It is assumed that simplex has 4 + * vertices! */ +static int simplexToPolytope4(const void *obj1, const void *obj2, + const ccd_t *ccd, + ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest) +{ + const ccd_support_t *a, *b, *c, *d; + int use_polytope3; + ccd_real_t dist; + ccd_pt_vertex_t *v[4]; + ccd_pt_edge_t *e[6]; + size_t i; + + a = ccdSimplexPoint(simplex, 0); + b = ccdSimplexPoint(simplex, 1); + c = ccdSimplexPoint(simplex, 2); + d = ccdSimplexPoint(simplex, 3); + + // check if origin lies on some of tetrahedron's face - if so use + // simplexToPolytope3() + use_polytope3 = 0; + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &c->v, NULL); + if (ccdIsZero(dist)){ + use_polytope3 = 1; + } + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &c->v, &d->v, NULL); + if (ccdIsZero(dist)){ + use_polytope3 = 1; + ccdSimplexSet(simplex, 1, c); + ccdSimplexSet(simplex, 2, d); + } + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &d->v, NULL); + if (ccdIsZero(dist)){ + use_polytope3 = 1; + ccdSimplexSet(simplex, 2, d); + } + dist = ccdVec3PointTriDist2(ccd_vec3_origin, &b->v, &c->v, &d->v, NULL); + if (ccdIsZero(dist)){ + use_polytope3 = 1; + ccdSimplexSet(simplex, 0, b); + ccdSimplexSet(simplex, 1, c); + ccdSimplexSet(simplex, 2, d); + } + + if (use_polytope3){ + ccdSimplexSetSize(simplex, 3); + return simplexToPolytope3(obj1, obj2, ccd, simplex, pt, nearest); + } + + // no touching contact - simply create tetrahedron + for (i = 0; i < 4; i++){ + v[i] = ccdPtAddVertex(pt, ccdSimplexPoint(simplex, i)); + } + + e[0] = ccdPtAddEdge(pt, v[0], v[1]); + e[1] = ccdPtAddEdge(pt, v[1], v[2]); + e[2] = ccdPtAddEdge(pt, v[2], v[0]); + e[3] = ccdPtAddEdge(pt, v[3], v[0]); + e[4] = ccdPtAddEdge(pt, v[3], v[1]); + e[5] = ccdPtAddEdge(pt, v[3], v[2]); + + ccdPtAddFace(pt, e[0], e[1], e[2]); + ccdPtAddFace(pt, e[3], e[4], e[0]); + ccdPtAddFace(pt, e[4], e[5], e[1]); + ccdPtAddFace(pt, e[5], e[3], e[2]); + + return 0; +} + +/** Transforms simplex to polytope, three vertices required */ +static int simplexToPolytope3(const void *obj1, const void *obj2, + const ccd_t *ccd, + const ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest) +{ + const ccd_support_t *a, *b, *c; + ccd_support_t d, d2; + ccd_vec3_t ab, ac, dir; + ccd_pt_vertex_t *v[5]; + ccd_pt_edge_t *e[9]; + ccd_real_t dist, dist2; + + *nearest = NULL; + + a = ccdSimplexPoint(simplex, 0); + b = ccdSimplexPoint(simplex, 1); + c = ccdSimplexPoint(simplex, 2); + + // If only one triangle left from previous GJK run origin lies on this + // triangle. So it is necessary to expand triangle into two + // tetrahedrons connected with base (which is exactly abc triangle). + + // get next support point in direction of normal of triangle + ccdVec3Sub2(&ab, &b->v, &a->v); + ccdVec3Sub2(&ac, &c->v, &a->v); + ccdVec3Cross(&dir, &ab, &ac); + __ccdSupport(obj1, obj2, &dir, ccd, &d); + dist = ccdVec3PointTriDist2(&d.v, &a->v, &b->v, &c->v, NULL); + + // and second one take in opposite direction + ccdVec3Scale(&dir, -CCD_ONE); + __ccdSupport(obj1, obj2, &dir, ccd, &d2); + dist2 = ccdVec3PointTriDist2(&d2.v, &a->v, &b->v, &c->v, NULL); + + // check if face isn't already on edge of minkowski sum and thus we + // have touching contact + if (ccdIsZero(dist) || ccdIsZero(dist2)){ + v[0] = ccdPtAddVertex(pt, a); + v[1] = ccdPtAddVertex(pt, b); + v[2] = ccdPtAddVertex(pt, c); + e[0] = ccdPtAddEdge(pt, v[0], v[1]); + e[1] = ccdPtAddEdge(pt, v[1], v[2]); + e[2] = ccdPtAddEdge(pt, v[2], v[0]); + *nearest = (ccd_pt_el_t *)ccdPtAddFace(pt, e[0], e[1], e[2]); + + return -1; + } + + // form polyhedron + v[0] = ccdPtAddVertex(pt, a); + v[1] = ccdPtAddVertex(pt, b); + v[2] = ccdPtAddVertex(pt, c); + v[3] = ccdPtAddVertex(pt, &d); + v[4] = ccdPtAddVertex(pt, &d2); + + e[0] = ccdPtAddEdge(pt, v[0], v[1]); + e[1] = ccdPtAddEdge(pt, v[1], v[2]); + e[2] = ccdPtAddEdge(pt, v[2], v[0]); + + e[3] = ccdPtAddEdge(pt, v[3], v[0]); + e[4] = ccdPtAddEdge(pt, v[3], v[1]); + e[5] = ccdPtAddEdge(pt, v[3], v[2]); + + e[6] = ccdPtAddEdge(pt, v[4], v[0]); + e[7] = ccdPtAddEdge(pt, v[4], v[1]); + e[8] = ccdPtAddEdge(pt, v[4], v[2]); + + ccdPtAddFace(pt, e[3], e[4], e[0]); + ccdPtAddFace(pt, e[4], e[5], e[1]); + ccdPtAddFace(pt, e[5], e[3], e[2]); + + ccdPtAddFace(pt, e[6], e[7], e[0]); + ccdPtAddFace(pt, e[7], e[8], e[1]); + ccdPtAddFace(pt, e[8], e[6], e[2]); + + return 0; +} + +/** Transforms simplex to polytope, two vertices required */ +static int simplexToPolytope2(const void *obj1, const void *obj2, + const ccd_t *ccd, + const ccd_simplex_t *simplex, + ccd_pt_t *pt, ccd_pt_el_t **nearest) +{ + const ccd_support_t *a, *b; + ccd_vec3_t ab, ac, dir; + ccd_support_t supp[4]; + ccd_pt_vertex_t *v[6]; + ccd_pt_edge_t *e[12]; + size_t i; + int found; + + a = ccdSimplexPoint(simplex, 0); + b = ccdSimplexPoint(simplex, 1); + + // This situation is a bit tricky. If only one segment comes from + // previous run of GJK - it means that either this segment is on + // minkowski edge (and thus we have touch contact) or it isn't and + // therefore segment is somewhere *inside* minkowski sum and it *must* + // be possible to fully enclose this segment with polyhedron formed by + // at least 8 triangle faces. + + // get first support point (any) + found = 0; + for (i = 0; i < ccd_points_on_sphere_len; i++){ + __ccdSupport(obj1, obj2, &ccd_points_on_sphere[i], ccd, &supp[0]); + if (!ccdVec3Eq(&a->v, &supp[0].v) && !ccdVec3Eq(&b->v, &supp[0].v)){ + found = 1; + break; + } + } + if (!found) + goto simplexToPolytope2_touching_contact; + + // get second support point in opposite direction than supp[0] + ccdVec3Copy(&dir, &supp[0].v); + ccdVec3Scale(&dir, -CCD_ONE); + __ccdSupport(obj1, obj2, &dir, ccd, &supp[1]); + if (ccdVec3Eq(&a->v, &supp[1].v) || ccdVec3Eq(&b->v, &supp[1].v)) + goto simplexToPolytope2_touching_contact; + + // next will be in direction of normal of triangle a,supp[0],supp[1] + ccdVec3Sub2(&ab, &supp[0].v, &a->v); + ccdVec3Sub2(&ac, &supp[1].v, &a->v); + ccdVec3Cross(&dir, &ab, &ac); + __ccdSupport(obj1, obj2, &dir, ccd, &supp[2]); + if (ccdVec3Eq(&a->v, &supp[2].v) || ccdVec3Eq(&b->v, &supp[2].v)) + goto simplexToPolytope2_touching_contact; + + // and last one will be in opposite direction + ccdVec3Scale(&dir, -CCD_ONE); + __ccdSupport(obj1, obj2, &dir, ccd, &supp[3]); + if (ccdVec3Eq(&a->v, &supp[3].v) || ccdVec3Eq(&b->v, &supp[3].v)) + goto simplexToPolytope2_touching_contact; + + goto simplexToPolytope2_not_touching_contact; +simplexToPolytope2_touching_contact: + v[0] = ccdPtAddVertex(pt, a); + v[1] = ccdPtAddVertex(pt, b); + *nearest = (ccd_pt_el_t *)ccdPtAddEdge(pt, v[0], v[1]); + return -1; + +simplexToPolytope2_not_touching_contact: + // form polyhedron + v[0] = ccdPtAddVertex(pt, a); + v[1] = ccdPtAddVertex(pt, &supp[0]); + v[2] = ccdPtAddVertex(pt, b); + v[3] = ccdPtAddVertex(pt, &supp[1]); + v[4] = ccdPtAddVertex(pt, &supp[2]); + v[5] = ccdPtAddVertex(pt, &supp[3]); + + e[0] = ccdPtAddEdge(pt, v[0], v[1]); + e[1] = ccdPtAddEdge(pt, v[1], v[2]); + e[2] = ccdPtAddEdge(pt, v[2], v[3]); + e[3] = ccdPtAddEdge(pt, v[3], v[0]); + + e[4] = ccdPtAddEdge(pt, v[4], v[0]); + e[5] = ccdPtAddEdge(pt, v[4], v[1]); + e[6] = ccdPtAddEdge(pt, v[4], v[2]); + e[7] = ccdPtAddEdge(pt, v[4], v[3]); + + e[8] = ccdPtAddEdge(pt, v[5], v[0]); + e[9] = ccdPtAddEdge(pt, v[5], v[1]); + e[10] = ccdPtAddEdge(pt, v[5], v[2]); + e[11] = ccdPtAddEdge(pt, v[5], v[3]); + + ccdPtAddFace(pt, e[4], e[5], e[0]); + ccdPtAddFace(pt, e[5], e[6], e[1]); + ccdPtAddFace(pt, e[6], e[7], e[2]); + ccdPtAddFace(pt, e[7], e[4], e[3]); + + ccdPtAddFace(pt, e[8], e[9], e[0]); + ccdPtAddFace(pt, e[9], e[10], e[1]); + ccdPtAddFace(pt, e[10], e[11], e[2]); + ccdPtAddFace(pt, e[11], e[8], e[3]); + + return 0; +} + +/** Expands polytope's tri by new vertex v. Triangle tri is replaced by + * three triangles each with one vertex in v. */ +static void expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el, + const ccd_support_t *newv) +{ + ccd_pt_vertex_t *v[5]; + ccd_pt_edge_t *e[8] = {0}; + ccd_pt_face_t *f[2]; + + + // element can be either segment or triangle + if (el->type == CCD_PT_EDGE){ + // In this case, segment should be replaced by new point. + // Simpliest case is when segment stands alone and in this case + // this segment is replaced by two other segments both connected to + // newv. + // Segment can be also connected to max two faces and in that case + // each face must be replaced by two other faces. To do this + // correctly it is necessary to have correctly ordered edges and + // vertices which is exactly what is done in following code. + // + + ccdPtEdgeVertices((const ccd_pt_edge_t *)el, &v[0], &v[2]); + + ccdPtEdgeFaces((ccd_pt_edge_t *)el, &f[0], &f[1]); + + if (f[0]){ + ccdPtFaceEdges(f[0], &e[0], &e[1], &e[2]); + if (e[0] == (ccd_pt_edge_t *)el){ + e[0] = e[2]; + }else if (e[1] == (ccd_pt_edge_t *)el){ + e[1] = e[2]; + } + ccdPtEdgeVertices(e[0], &v[1], &v[3]); + if (v[1] != v[0] && v[3] != v[0]){ + e[2] = e[0]; + e[0] = e[1]; + e[1] = e[2]; + if (v[1] == v[2]) + v[1] = v[3]; + }else{ + if (v[1] == v[0]) + v[1] = v[3]; + } + + if (f[1]){ + ccdPtFaceEdges(f[1], &e[2], &e[3], &e[4]); + if (e[2] == (ccd_pt_edge_t *)el){ + e[2] = e[4]; + }else if (e[3] == (ccd_pt_edge_t *)el){ + e[3] = e[4]; + } + ccdPtEdgeVertices(e[2], &v[3], &v[4]); + if (v[3] != v[2] && v[4] != v[2]){ + e[4] = e[2]; + e[2] = e[3]; + e[3] = e[4]; + if (v[3] == v[0]) + v[3] = v[4]; + }else{ + if (v[3] == v[2]) + v[3] = v[4]; + } + } + + + v[4] = ccdPtAddVertex(pt, newv); + + ccdPtDelFace(pt, f[0]); + if (f[1]){ + ccdPtDelFace(pt, f[1]); + ccdPtDelEdge(pt, (ccd_pt_edge_t *)el); + } + + e[4] = ccdPtAddEdge(pt, v[4], v[2]); + e[5] = ccdPtAddEdge(pt, v[4], v[0]); + e[6] = ccdPtAddEdge(pt, v[4], v[1]); + if (f[1]) + e[7] = ccdPtAddEdge(pt, v[4], v[3]); + + ccdPtAddFace(pt, e[1], e[4], e[6]); + ccdPtAddFace(pt, e[0], e[6], e[5]); + if (f[1]){ + ccdPtAddFace(pt, e[3], e[5], e[7]); + ccdPtAddFace(pt, e[4], e[7], e[2]); + }else{ + ccdPtAddFace(pt, e[4], e[5], (ccd_pt_edge_t *)el); + } + } + }else{ // el->type == CCD_PT_FACE + // replace triangle by tetrahedron without base (base would be the + // triangle that will be removed) + + // get triplet of surrounding edges and vertices of triangle face + ccdPtFaceEdges((const ccd_pt_face_t *)el, &e[0], &e[1], &e[2]); + ccdPtEdgeVertices(e[0], &v[0], &v[1]); + ccdPtEdgeVertices(e[1], &v[2], &v[3]); + + // following code sorts edges to have e[0] between vertices 0-1, + // e[1] between 1-2 and e[2] between 2-0 + if (v[2] != v[1] && v[3] != v[1]){ + // swap e[1] and e[2] + e[3] = e[1]; + e[1] = e[2]; + e[2] = e[3]; + } + if (v[3] != v[0] && v[3] != v[1]) + v[2] = v[3]; + + // remove triangle face + ccdPtDelFace(pt, (ccd_pt_face_t *)el); + + // expand triangle to tetrahedron + v[3] = ccdPtAddVertex(pt, newv); + e[3] = ccdPtAddEdge(pt, v[3], v[0]); + e[4] = ccdPtAddEdge(pt, v[3], v[1]); + e[5] = ccdPtAddEdge(pt, v[3], v[2]); + + ccdPtAddFace(pt, e[3], e[4], e[0]); + ccdPtAddFace(pt, e[4], e[5], e[1]); + ccdPtAddFace(pt, e[5], e[3], e[2]); + } +} + +/** Finds next support point (and stores it in out argument). + * Returns 0 on success, -1 otherwise */ +static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd, + const ccd_pt_el_t *el, + ccd_support_t *out) +{ + ccd_vec3_t *a, *b, *c; + ccd_real_t dist; + + if (el->type == CCD_PT_VERTEX) + return -1; + + // touch contact + if (ccdIsZero(el->dist)) + return -1; + + __ccdSupport(obj1, obj2, &el->witness, ccd, out); + + if (el->type == CCD_PT_EDGE){ + // fetch end points of edge + ccdPtEdgeVec3((ccd_pt_edge_t *)el, &a, &b); + + // get distance from segment + dist = ccdVec3PointSegmentDist2(&out->v, a, b, NULL); + }else{ // el->type == CCD_PT_FACE + // fetch vertices of triangle face + ccdPtFaceVec3((ccd_pt_face_t *)el, &a, &b, &c); + + // check if new point can significantly expand polytope + dist = ccdVec3PointTriDist2(&out->v, a, b, c, NULL); + } + + if (dist < ccd->epa_tolerance) + return -1; + + return 0; +} diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/alloc.h b/thirdparty/ode-0.16.5/libccd/src/ccd/alloc.h new file mode 100644 index 0000000..7b92e3e --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/alloc.h @@ -0,0 +1,52 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_ALLOC_H__ +#define __CCD_ALLOC_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Functions and macros required for memory allocation. + */ + +/* Memory allocation: */ +#define __CCD_ALLOC_MEMORY(type, ptr_old, size) \ + (type *)ccdRealloc((void *)ptr_old, (size)) + +/** Allocate memory for one element of type. */ +#define CCD_ALLOC(type) \ + __CCD_ALLOC_MEMORY(type, NULL, sizeof(type)) + +/** Allocate memory for array of elements of type type. */ +#define CCD_ALLOC_ARR(type, num_elements) \ + __CCD_ALLOC_MEMORY(type, NULL, sizeof(type) * (num_elements)) + +#define CCD_REALLOC_ARR(ptr, type, num_elements) \ + __CCD_ALLOC_MEMORY(type, ptr, sizeof(type) * (num_elements)) + +void *ccdRealloc(void *ptr, size_t size); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_ALLOC_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/ccd.h b/thirdparty/ode-0.16.5/libccd/src/ccd/ccd.h new file mode 100644 index 0000000..075c5dd --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/ccd.h @@ -0,0 +1,148 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010,2011 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_H__ +#define __CCD_H__ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Type of *support* function that takes pointer to 3D object and direction + * and returns (via vec argument) furthest point from object in specified + * direction. + */ +typedef void (*ccd_support_fn)(const void *obj, const ccd_vec3_t *dir, + ccd_vec3_t *vec); + +/** + * Returns (via dir argument) first direction vector that will be used in + * initialization of algorithm. + */ +typedef void (*ccd_first_dir_fn)(const void *obj1, const void *obj2, + ccd_vec3_t *dir); + + +/** + * Returns (via center argument) geometric center (some point near center) + * of given object. + */ +typedef void (*ccd_center_fn)(const void *obj1, ccd_vec3_t *center); + +/** + * Main structure of CCD algorithm. + */ +struct _ccd_t { + ccd_first_dir_fn first_dir; /*!< Returns initial direction where first + !< support point will be searched*/ + ccd_support_fn support1; /*!< Function that returns support point of + !< first object*/ + ccd_support_fn support2; /*!< Function that returns support point of + !< second object*/ + + ccd_center_fn center1; /*!< Function that returns geometric center of + !< first object*/ + ccd_center_fn center2; /*!< Function that returns geometric center of + !< second object*/ + + unsigned long max_iterations; /*!< Maximal number of iterations*/ + ccd_real_t epa_tolerance; + ccd_real_t mpr_tolerance; /*!< Boundary tolerance for MPR algorithm*/ +}; +typedef struct _ccd_t ccd_t; + +/** + * Default first direction. + */ +void ccdFirstDirDefault(const void *o1, const void *o2, ccd_vec3_t *dir); + +#define CCD_INIT(ccd) \ + do { \ + (ccd)->first_dir = ccdFirstDirDefault; \ + (ccd)->support1 = NULL; \ + (ccd)->support2 = NULL; \ + (ccd)->center1 = NULL; \ + (ccd)->center2 = NULL; \ + \ + (ccd)->max_iterations = (unsigned long)-1; \ + (ccd)->epa_tolerance = CCD_REAL(0.0001); \ + (ccd)->mpr_tolerance = CCD_REAL(0.0001); \ + } while(0) + + +/** + * Returns true if two given objects interest. + */ +int ccdGJKIntersect(const void *obj1, const void *obj2, const ccd_t *ccd); + +/** + * This function computes separation vector of two objects. Separation + * vector is minimal translation of obj2 to get obj1 and obj2 separated + * (without intersection). + * Returns 0 if obj1 and obj2 intersect and sep is filled with translation + * vector. If obj1 and obj2 don't intersect -1 is returned. + */ +int ccdGJKSeparate(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_vec3_t *sep); + +/** + * Computes penetration of obj2 into obj1. + * Depth of penetration, direction and position is returned. It means that + * if obj2 is translated by distance depth in direction dir objects will + * have touching contact, pos should be position in global coordinates + * where force should take a place. + * + * CCD+EPA algorithm is used. + * + * Returns 0 if obj1 and obj2 intersect and depth, dir and pos are filled + * if given non-NULL pointers. + * If obj1 and obj2 don't intersect -1 is returned. + */ +int ccdGJKPenetration(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos); + + +/** + * Returns true if two given objects intersect - MPR algorithm is used. + */ +int ccdMPRIntersect(const void *obj1, const void *obj2, const ccd_t *ccd); + +/** + * Computes penetration of obj2 into obj1. + * Depth of penetration, direction and position is returned, i.e. if obj2 + * is translated by computed depth in resulting direction obj1 and obj2 + * would have touching contact. Position is a point in global coordinates + * where force should take place. + * + * Minkowski Portal Refinement algorithm is used (MPR, a.k.a. XenoCollide, + * see Game Programming Gem 7). + * + * Returns 0 if obj1 and obj2 intersect, otherwise -1 is returned. + */ +int ccdMPRPenetration(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/compiler.h b/thirdparty/ode-0.16.5/libccd/src/ccd/compiler.h new file mode 100644 index 0000000..380878f --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/compiler.h @@ -0,0 +1,61 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_COMPILER_H__ +#define __CCD_COMPILER_H__ + +#include + +#define ccd_offsetof(TYPE, MEMBER) offsetof(TYPE, MEMBER) + +#define ccd_container_of(ptr, type, member) \ + (type *)( (char *)ptr - ccd_offsetof(type, member)) + + +/** + * Marks inline function. + */ +#ifdef __GNUC__ +/*# define _ccd_inline static inline __attribute__((always_inline))*/ +# define _ccd_inline static inline +#else /* __GNUC__ */ +# define _ccd_inline static __inline +#endif /* __GNUC__ */ + + +/** + * __prefetch(x) - prefetches the cacheline at "x" for read + * __prefetchw(x) - prefetches the cacheline at "x" for write + */ +#ifdef __GNUC__ +# define _ccd_prefetch(x) __builtin_prefetch(x) +# define _ccd_prefetchw(x) __builtin_prefetch(x,1) +#else /* __GNUC__ */ +# define _ccd_prefetch(x) ((void)0) +# define _ccd_prefetchw(x) ((void)0) +#endif /* __GNUC__ */ + + +#ifdef __ICC +/* disable unused parameter warning */ +# pragma warning(disable:869) +/* disable annoying "operands are evaluated in unspecified order" warning */ +# pragma warning(disable:981) +#endif /* __ICC */ + +#endif /* __CCD_COMPILER_H__ */ + diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/dbg.h b/thirdparty/ode-0.16.5/libccd/src/ccd/dbg.h new file mode 100644 index 0000000..f4852c1 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/dbg.h @@ -0,0 +1,65 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_DBG_H__ +#define __CCD_DBG_H__ + +/** + * Some macros which can be used for printing debug info to stderr if macro + * NDEBUG not defined. + * + * DBG_PROLOGUE can be specified as string and this string will be + * prepended to output text + */ +#ifndef NDEBUG + +#include + +#ifndef DBG_PROLOGUE +# define DBG_PROLOGUE +#endif + +# define DBG(format, ...) do { \ + fprintf(stderr, DBG_PROLOGUE "%s :: " format "\n", __func__, ## __VA_ARGS__); \ + fflush(stderr); \ + } while (0) + +# define DBG2(str) do { \ + fprintf(stderr, DBG_PROLOGUE "%s :: " str "\n", __func__); \ + fflush(stderr); \ + } while (0) + +# define DBG_VEC3(vec, prefix) do {\ + fprintf(stderr, DBG_PROLOGUE "%s :: %s[%lf %lf %lf]\n", \ + __func__, prefix, ccdVec3X(vec), ccdVec3Y(vec), ccdVec3Z(vec)); \ + fflush(stderr); \ + } while (0) +/* +# define DBG_VEC3(vec, prefix) do {\ + fprintf(stderr, DBG_PROLOGUE "%s :: %s[%.20lf %.20lf %.20lf]\n", \ + __func__, prefix, ccdVec3X(vec), ccdVec3Y(vec), ccdVec3Z(vec)); \ + fflush(stderr); \ + } while (0) +*/ + +#else +# define DBG(format, ...) +# define DBG2(str) +# define DBG_VEC3(v, prefix) +#endif + +#endif /* __CCD_DBG_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/list.h b/thirdparty/ode-0.16.5/libccd/src/ccd/list.h new file mode 100644 index 0000000..995fec1 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/list.h @@ -0,0 +1,155 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_LIST_H__ +#define __CCD_LIST_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct _ccd_list_t { + struct _ccd_list_t *next, *prev; +}; +typedef struct _ccd_list_t ccd_list_t; + + + +/** + * Get the struct for this entry. + * @ptr: the &ccd_list_t pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define ccdListEntry(ptr, type, member) \ + ccd_container_of(ptr, type, member) + +/** + * Iterates over list. + */ +#define ccdListForEach(list, item) \ + for (item = (list)->next; \ + _ccd_prefetch((item)->next), item != (list); \ + item = (item)->next) + +/** + * Iterates over list safe against removal of list entry + */ +#define ccdListForEachSafe(list, item, tmp) \ + for (item = (list)->next, tmp = (item)->next; \ + item != (list); \ + item = tmp, tmp = (item)->next) + +/** + * Iterates over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define ccdListForEachEntry(head, pos, postype, member) \ + for (pos = ccdListEntry((head)->next, postype, member); \ + _ccd_prefetch(pos->member.next), &pos->member != (head); \ + pos = ccdListEntry(pos->member.next, postype, member)) + +/** + * Iterates over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define ccdListForEachEntrySafe(head, pos, postype, n, ntype, member) \ + for (pos = ccdListEntry((head)->next, postype, member), \ + n = ccdListEntry(pos->member.next, postype, member); \ + &pos->member != (head); \ + pos = n, n = ccdListEntry(n->member.next, ntype, member)) + + +/** + * Initialize list. + */ +_ccd_inline void ccdListInit(ccd_list_t *l); + +_ccd_inline ccd_list_t *ccdListNext(ccd_list_t *l); +_ccd_inline ccd_list_t *ccdListPrev(ccd_list_t *l); + +/** + * Returns true if list is empty. + */ +_ccd_inline int ccdListEmpty(const ccd_list_t *head); + +/** + * Appends item to end of the list l. + */ +_ccd_inline void ccdListAppend(ccd_list_t *l, ccd_list_t *item); + +/** + * Removes item from list. + */ +_ccd_inline void ccdListDel(ccd_list_t *item); + + + +/// +/// INLINES: +/// + +_ccd_inline void ccdListInit(ccd_list_t *l) +{ + l->next = l; + l->prev = l; +} + +_ccd_inline ccd_list_t *ccdListNext(ccd_list_t *l) +{ + return l->next; +} + +_ccd_inline ccd_list_t *ccdListPrev(ccd_list_t *l) +{ + return l->prev; +} + +_ccd_inline int ccdListEmpty(const ccd_list_t *head) +{ + return head->next == head; +} + +_ccd_inline void ccdListAppend(ccd_list_t *l, ccd_list_t *newccd) +{ + newccd->prev = l->prev; + newccd->next = l; + l->prev->next = newccd; + l->prev = newccd; +} + +_ccd_inline void ccdListDel(ccd_list_t *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->next = item; + item->prev = item; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_LIST_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/polytope.h b/thirdparty/ode-0.16.5/libccd/src/ccd/polytope.h new file mode 100644 index 0000000..bba527d --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/polytope.h @@ -0,0 +1,322 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_POLYTOPE_H__ +#define __CCD_POLYTOPE_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define CCD_PT_VERTEX 1 +#define CCD_PT_EDGE 2 +#define CCD_PT_FACE 3 + + +#define __CCD_PT_EL \ + int type; /*! type of element */ \ + ccd_real_t dist; /*! distance from origin */ \ + ccd_vec3_t witness; /*! witness point of projection of origin */ \ + ccd_list_t list; /*! list of elements of same type */ + +/** + * General polytope element. + * Could be vertex, edge or triangle. + */ +struct _ccd_pt_el_t { + __CCD_PT_EL +}; +typedef struct _ccd_pt_el_t ccd_pt_el_t; + +struct _ccd_pt_edge_t; +struct _ccd_pt_face_t; + +/** + * Polytope's vertex. + */ +struct _ccd_pt_vertex_t { + __CCD_PT_EL + + int id; + ccd_support_t v; + ccd_list_t edges; //!< List of edges +}; +typedef struct _ccd_pt_vertex_t ccd_pt_vertex_t; + +/** + * Polytope's edge. + */ +struct _ccd_pt_edge_t { + __CCD_PT_EL + + ccd_pt_vertex_t *vertex[2]; //!< Reference to vertices + struct _ccd_pt_face_t *faces[2]; //!< Reference to faces + + ccd_list_t vertex_list[2]; //!< List items in vertices' lists +}; +typedef struct _ccd_pt_edge_t ccd_pt_edge_t; + +/** + * Polytope's triangle faces. + */ +struct _ccd_pt_face_t { + __CCD_PT_EL + + ccd_pt_edge_t *edge[3]; //!< Reference to surrounding edges +}; +typedef struct _ccd_pt_face_t ccd_pt_face_t; + + +/** + * Struct containing polytope. + */ +struct _ccd_pt_t { + ccd_list_t vertices; //!< List of vertices + ccd_list_t edges; //!< List of edges + ccd_list_t faces; //!< List of faces + + ccd_pt_el_t *nearest; + ccd_real_t nearest_dist; + int nearest_type; +}; +typedef struct _ccd_pt_t ccd_pt_t; + + +void ccdPtInit(ccd_pt_t *pt); +void ccdPtDestroy(ccd_pt_t *pt); + +/** + * Returns vertices surrounding given triangle face. + */ +_ccd_inline void ccdPtFaceVec3(const ccd_pt_face_t *face, + ccd_vec3_t **a, + ccd_vec3_t **b, + ccd_vec3_t **c); +_ccd_inline void ccdPtFaceVertices(const ccd_pt_face_t *face, + ccd_pt_vertex_t **a, + ccd_pt_vertex_t **b, + ccd_pt_vertex_t **c); +_ccd_inline void ccdPtFaceEdges(const ccd_pt_face_t *f, + ccd_pt_edge_t **a, + ccd_pt_edge_t **b, + ccd_pt_edge_t **c); + +_ccd_inline void ccdPtEdgeVec3(const ccd_pt_edge_t *e, + ccd_vec3_t **a, + ccd_vec3_t **b); +_ccd_inline void ccdPtEdgeVertices(const ccd_pt_edge_t *e, + ccd_pt_vertex_t **a, + ccd_pt_vertex_t **b); +_ccd_inline void ccdPtEdgeFaces(const ccd_pt_edge_t *e, + ccd_pt_face_t **f1, + ccd_pt_face_t **f2); + + +/** + * Adds vertex to polytope and returns pointer to newly created vertex. + */ +ccd_pt_vertex_t *ccdPtAddVertex(ccd_pt_t *pt, const ccd_support_t *v); +_ccd_inline ccd_pt_vertex_t *ccdPtAddVertexCoords(ccd_pt_t *pt, + ccd_real_t x, ccd_real_t y, ccd_real_t z); + +/** + * Adds edge to polytope. + */ +ccd_pt_edge_t *ccdPtAddEdge(ccd_pt_t *pt, ccd_pt_vertex_t *v1, + ccd_pt_vertex_t *v2); + +/** + * Adds face to polytope. + */ +ccd_pt_face_t *ccdPtAddFace(ccd_pt_t *pt, ccd_pt_edge_t *e1, + ccd_pt_edge_t *e2, + ccd_pt_edge_t *e3); + +/** + * Deletes vertex from polytope. + * Returns 0 on success, -1 otherwise. + */ +_ccd_inline int ccdPtDelVertex(ccd_pt_t *pt, ccd_pt_vertex_t *); +_ccd_inline int ccdPtDelEdge(ccd_pt_t *pt, ccd_pt_edge_t *); +_ccd_inline int ccdPtDelFace(ccd_pt_t *pt, ccd_pt_face_t *); + + +/** + * Recompute distances from origin for all elements in pt. + */ +void ccdPtRecomputeDistances(ccd_pt_t *pt); + +/** + * Returns nearest element to origin. + */ +ccd_pt_el_t *ccdPtNearest(ccd_pt_t *pt); + + +void ccdPtDumpSVT(ccd_pt_t *pt, const char *fn); +void ccdPtDumpSVT2(ccd_pt_t *pt, FILE *); + + +/**** INLINES ****/ +_ccd_inline ccd_pt_vertex_t *ccdPtAddVertexCoords(ccd_pt_t *pt, + ccd_real_t x, ccd_real_t y, ccd_real_t z) +{ + ccd_support_t s; + ccdVec3Set(&s.v, x, y, z); + return ccdPtAddVertex(pt, &s); +} + +_ccd_inline int ccdPtDelVertex(ccd_pt_t *pt, ccd_pt_vertex_t *v) +{ + // test if any edge is connected to this vertex + if (!ccdListEmpty(&v->edges)) + return -1; + + // delete vertex from main list + ccdListDel(&v->list); + + if ((void *)pt->nearest == (void *)v){ + pt->nearest = NULL; + } + + free(v); + return 0; +} + +_ccd_inline int ccdPtDelEdge(ccd_pt_t *pt, ccd_pt_edge_t *e) +{ + // text if any face is connected to this edge (faces[] is always + // aligned to lower indices) + if (e->faces[0] != NULL) + return -1; + + // disconnect edge from lists of edges in vertex struct + ccdListDel(&e->vertex_list[0]); + ccdListDel(&e->vertex_list[1]); + + // disconnect edge from main list + ccdListDel(&e->list); + + if ((void *)pt->nearest == (void *)e){ + pt->nearest = NULL; + } + + free(e); + return 0; +} + +_ccd_inline int ccdPtDelFace(ccd_pt_t *pt, ccd_pt_face_t *f) +{ + ccd_pt_edge_t *e; + size_t i; + + // remove face from edges' reference lists + for (i = 0; i < 3; i++){ + e = f->edge[i]; + if (e->faces[0] == f){ + e->faces[0] = e->faces[1]; + } + e->faces[1] = NULL; + } + + // remove face from list of all faces + ccdListDel(&f->list); + + if ((void *)pt->nearest == (void *)f){ + pt->nearest = NULL; + } + + free(f); + return 0; +} + +_ccd_inline void ccdPtFaceVec3(const ccd_pt_face_t *face, + ccd_vec3_t **a, + ccd_vec3_t **b, + ccd_vec3_t **c) +{ + *a = &face->edge[0]->vertex[0]->v.v; + *b = &face->edge[0]->vertex[1]->v.v; + + if (face->edge[1]->vertex[0] != face->edge[0]->vertex[0] + && face->edge[1]->vertex[0] != face->edge[0]->vertex[1]){ + *c = &face->edge[1]->vertex[0]->v.v; + }else{ + *c = &face->edge[1]->vertex[1]->v.v; + } +} + +_ccd_inline void ccdPtFaceVertices(const ccd_pt_face_t *face, + ccd_pt_vertex_t **a, + ccd_pt_vertex_t **b, + ccd_pt_vertex_t **c) +{ + *a = face->edge[0]->vertex[0]; + *b = face->edge[0]->vertex[1]; + + if (face->edge[1]->vertex[0] != face->edge[0]->vertex[0] + && face->edge[1]->vertex[0] != face->edge[0]->vertex[1]){ + *c = face->edge[1]->vertex[0]; + }else{ + *c = face->edge[1]->vertex[1]; + } +} + +_ccd_inline void ccdPtFaceEdges(const ccd_pt_face_t *f, + ccd_pt_edge_t **a, + ccd_pt_edge_t **b, + ccd_pt_edge_t **c) +{ + *a = f->edge[0]; + *b = f->edge[1]; + *c = f->edge[2]; +} + +_ccd_inline void ccdPtEdgeVec3(const ccd_pt_edge_t *e, + ccd_vec3_t **a, + ccd_vec3_t **b) +{ + *a = &e->vertex[0]->v.v; + *b = &e->vertex[1]->v.v; +} + +_ccd_inline void ccdPtEdgeVertices(const ccd_pt_edge_t *e, + ccd_pt_vertex_t **a, + ccd_pt_vertex_t **b) +{ + *a = e->vertex[0]; + *b = e->vertex[1]; +} + +_ccd_inline void ccdPtEdgeFaces(const ccd_pt_edge_t *e, + ccd_pt_face_t **f1, + ccd_pt_face_t **f2) +{ + *f1 = e->faces[0]; + *f2 = e->faces[1]; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_POLYTOPE_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h b/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h new file mode 100644 index 0000000..06ecd03 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h @@ -0,0 +1,14 @@ +#ifndef __CCD_PRECISION_H__ +#define __CCD_PRECISION_H__ + +/* define either CCD_SINGLE or CCD_DOUBLE */ + +#if defined(CCD_IDESINGLE) +#define CCD_SINGLE +#elif defined(CCD_IDEDOUBLE) +#define CCD_DOUBLE +#else +#define CCD_UNDEFINEDPRECISION +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h.in b/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h.in new file mode 100644 index 0000000..4f98509 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/precision.h.in @@ -0,0 +1,14 @@ +#ifndef __CCD_PRECISION_H__ +#define __CCD_PRECISION_H__ + +/* define either CCD_SINGLE or CCD_DOUBLE */ + +#if defined(CCD_IDESINGLE) +#define CCD_SINGLE +#elif defined(CCD_IDEDOUBLE) +#define CCD_DOUBLE +#else +#define @CCD_PRECISION@ +#endif + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/quat.h b/thirdparty/ode-0.16.5/libccd/src/ccd/quat.h new file mode 100644 index 0000000..d167220 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/quat.h @@ -0,0 +1,231 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_QUAT_H__ +#define __CCD_QUAT_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct _ccd_quat_t { + ccd_real_t q[4]; //!< x, y, z, w +}; +typedef struct _ccd_quat_t ccd_quat_t; + +#define CCD_QUAT(name, x, y, z, w) \ + ccd_quat_t name = { {x, y, z, w} } + +_ccd_inline ccd_real_t ccdQuatLen2(const ccd_quat_t *q); +_ccd_inline ccd_real_t ccdQuatLen(const ccd_quat_t *q); + +_ccd_inline void ccdQuatSet(ccd_quat_t *q, ccd_real_t x, ccd_real_t y, ccd_real_t z, ccd_real_t w); +_ccd_inline void ccdQuatCopy(ccd_quat_t *dest, const ccd_quat_t *src); + +_ccd_inline int ccdQuatNormalize(ccd_quat_t *q); + +_ccd_inline void ccdQuatSetAngleAxis(ccd_quat_t *q, + ccd_real_t angle, const ccd_vec3_t *axis); + +_ccd_inline void ccdQuatScale(ccd_quat_t *q, ccd_real_t k); + +/** + * q = q * q2 + */ +_ccd_inline void ccdQuatMul(ccd_quat_t *q, const ccd_quat_t *q2); + +/** + * q = a * b + */ +_ccd_inline void ccdQuatMul2(ccd_quat_t *q, + const ccd_quat_t *a, const ccd_quat_t *b); + +/** + * Inverts quaternion. + * Returns 0 on success. + */ +_ccd_inline int ccdQuatInvert(ccd_quat_t *q); +_ccd_inline int ccdQuatInvert2(ccd_quat_t *dest, const ccd_quat_t *src); + + +/** + * Rotate vector v by quaternion q. + */ +_ccd_inline void ccdQuatRotVec(ccd_vec3_t *v, const ccd_quat_t *q); + + +/**** INLINES ****/ +_ccd_inline ccd_real_t ccdQuatLen2(const ccd_quat_t *q) +{ + ccd_real_t len; + + len = q->q[0] * q->q[0]; + len += q->q[1] * q->q[1]; + len += q->q[2] * q->q[2]; + len += q->q[3] * q->q[3]; + + return len; +} + +_ccd_inline ccd_real_t ccdQuatLen(const ccd_quat_t *q) +{ + return CCD_SQRT(ccdQuatLen2(q)); +} + +_ccd_inline void ccdQuatSet(ccd_quat_t *q, ccd_real_t x, ccd_real_t y, ccd_real_t z, ccd_real_t w) +{ + q->q[0] = x; + q->q[1] = y; + q->q[2] = z; + q->q[3] = w; +} + +_ccd_inline void ccdQuatCopy(ccd_quat_t *dest, const ccd_quat_t *src) +{ + *dest = *src; +} + + +_ccd_inline int ccdQuatNormalize(ccd_quat_t *q) +{ + ccd_real_t len = ccdQuatLen(q); + if (len < CCD_EPS) + return 0; + + ccdQuatScale(q, CCD_ONE / len); + return 1; +} + +_ccd_inline void ccdQuatSetAngleAxis(ccd_quat_t *q, + ccd_real_t angle, const ccd_vec3_t *axis) +{ + ccd_real_t a, x, y, z, n, s; + + a = angle/2; + x = ccdVec3X(axis); + y = ccdVec3Y(axis); + z = ccdVec3Z(axis); + n = CCD_SQRT(x*x + y*y + z*z); + + // axis==0? (treat this the same as angle==0 with an arbitrary axis) + if (n < CCD_EPS){ + q->q[0] = q->q[1] = q->q[2] = CCD_ZERO; + q->q[3] = CCD_ONE; + }else{ + s = sin(a)/n; + + q->q[3] = cos(a); + q->q[0] = x*s; + q->q[1] = y*s; + q->q[2] = z*s; + + ccdQuatNormalize(q); + } +} + + +_ccd_inline void ccdQuatScale(ccd_quat_t *q, ccd_real_t k) +{ + size_t i; + for (i = 0; i < 4; i++) + q->q[i] *= k; +} + +_ccd_inline void ccdQuatMul(ccd_quat_t *q, const ccd_quat_t *q2) +{ + ccd_quat_t a; + ccdQuatCopy(&a, q); + ccdQuatMul2(q, &a, q2); +} + +_ccd_inline void ccdQuatMul2(ccd_quat_t *q, + const ccd_quat_t *a, const ccd_quat_t *b) +{ + q->q[0] = a->q[3] * b->q[0] + + a->q[0] * b->q[3] + + a->q[1] * b->q[2] + - a->q[2] * b->q[1]; + q->q[1] = a->q[3] * b->q[1] + + a->q[1] * b->q[3] + - a->q[0] * b->q[2] + + a->q[2] * b->q[0]; + q->q[2] = a->q[3] * b->q[2] + + a->q[2] * b->q[3] + + a->q[0] * b->q[1] + - a->q[1] * b->q[0]; + q->q[3] = a->q[3] * b->q[3] + - a->q[0] * b->q[0] + - a->q[1] * b->q[1] + - a->q[2] * b->q[2]; +} + +_ccd_inline int ccdQuatInvert(ccd_quat_t *q) +{ + ccd_real_t len2 = ccdQuatLen2(q); + if (len2 < CCD_EPS) + return -1; + + len2 = CCD_ONE / len2; + + q->q[0] = -q->q[0] * len2; + q->q[1] = -q->q[1] * len2; + q->q[2] = -q->q[2] * len2; + q->q[3] = q->q[3] * len2; + + return 0; +} +_ccd_inline int ccdQuatInvert2(ccd_quat_t *dest, const ccd_quat_t *src) +{ + ccdQuatCopy(dest, src); + return ccdQuatInvert(dest); +} + +_ccd_inline void ccdQuatRotVec(ccd_vec3_t *v, const ccd_quat_t *q) +{ + // original version: 31 mul + 21 add + // optimized version: 18 mul + 12 add + // formula: v = v + 2 * cross(q.xyz, cross(q.xyz, v) + q.w * v) + ccd_real_t cross1_x, cross1_y, cross1_z, cross2_x, cross2_y, cross2_z; + ccd_real_t x, y, z, w; + ccd_real_t vx, vy, vz; + + vx = ccdVec3X(v); + vy = ccdVec3Y(v); + vz = ccdVec3Z(v); + + w = q->q[3]; + x = q->q[0]; + y = q->q[1]; + z = q->q[2]; + + cross1_x = y * vz - z * vy + w * vx; + cross1_y = z * vx - x * vz + w * vy; + cross1_z = x * vy - y * vx + w * vz; + cross2_x = y * cross1_z - z * cross1_y; + cross2_y = z * cross1_x - x * cross1_z; + cross2_z = x * cross1_y - y * cross1_x; + ccdVec3Set(v, vx + 2 * cross2_x, vy + 2 * cross2_y, vz + 2 * cross2_z); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_QUAT_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/simplex.h b/thirdparty/ode-0.16.5/libccd/src/ccd/simplex.h new file mode 100644 index 0000000..1d07e39 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/simplex.h @@ -0,0 +1,104 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_SIMPLEX_H__ +#define __CCD_SIMPLEX_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct _ccd_simplex_t { + ccd_support_t ps[4]; + int last; /*!< index of last added point*/ +}; +typedef struct _ccd_simplex_t ccd_simplex_t; + + +_ccd_inline void ccdSimplexInit(ccd_simplex_t *s); +_ccd_inline int ccdSimplexSize(const ccd_simplex_t *s); +_ccd_inline const ccd_support_t *ccdSimplexLast(const ccd_simplex_t *s); +_ccd_inline const ccd_support_t *ccdSimplexPoint(const ccd_simplex_t *s, int idx); +_ccd_inline ccd_support_t *ccdSimplexPointW(ccd_simplex_t *s, int idx); + +_ccd_inline void ccdSimplexAdd(ccd_simplex_t *s, const ccd_support_t *v); +_ccd_inline void ccdSimplexSet(ccd_simplex_t *s, size_t pos, const ccd_support_t *a); +_ccd_inline void ccdSimplexSetSize(ccd_simplex_t *s, int size); +_ccd_inline void ccdSimplexSwap(ccd_simplex_t *s, size_t pos1, size_t pos2); + + +/**** INLINES ****/ + +_ccd_inline void ccdSimplexInit(ccd_simplex_t *s) +{ + s->last = -1; +} + +_ccd_inline int ccdSimplexSize(const ccd_simplex_t *s) +{ + return s->last + 1; +} + +_ccd_inline const ccd_support_t *ccdSimplexLast(const ccd_simplex_t *s) +{ + return ccdSimplexPoint(s, s->last); +} + +_ccd_inline const ccd_support_t *ccdSimplexPoint(const ccd_simplex_t *s, int idx) +{ + /* here is no check on boundaries */ + return &s->ps[idx]; +} +_ccd_inline ccd_support_t *ccdSimplexPointW(ccd_simplex_t *s, int idx) +{ + return &s->ps[idx]; +} + +_ccd_inline void ccdSimplexAdd(ccd_simplex_t *s, const ccd_support_t *v) +{ + /* here is no check on boundaries in sake of speed */ + ++s->last; + ccdSupportCopy(s->ps + s->last, v); +} + +_ccd_inline void ccdSimplexSet(ccd_simplex_t *s, size_t pos, const ccd_support_t *a) +{ + ccdSupportCopy(s->ps + pos, a); +} + +_ccd_inline void ccdSimplexSetSize(ccd_simplex_t *s, int size) +{ + s->last = size - 1; +} + +_ccd_inline void ccdSimplexSwap(ccd_simplex_t *s, size_t pos1, size_t pos2) +{ + ccd_support_t supp; + + ccdSupportCopy(&supp, &s->ps[pos1]); + ccdSupportCopy(&s->ps[pos1], &s->ps[pos2]); + ccdSupportCopy(&s->ps[pos2], &supp); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_SIMPLEX_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/support.h b/thirdparty/ode-0.16.5/libccd/src/ccd/support.h new file mode 100644 index 0000000..0a21b3e --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/support.h @@ -0,0 +1,57 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_SUPPORT_H__ +#define __CCD_SUPPORT_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct _ccd_support_t { + ccd_vec3_t v; /*!< Support point in minkowski sum*/ + ccd_vec3_t v1; /*!< Support point in obj1*/ + ccd_vec3_t v2; /*!< Support point in obj2*/ +}; +typedef struct _ccd_support_t ccd_support_t; + +_ccd_inline void ccdSupportCopy(ccd_support_t *, const ccd_support_t *s); + +/** + * Computes support point of obj1 and obj2 in direction dir. + * Support point is returned via supp. + */ +void __ccdSupport(const void *obj1, const void *obj2, + const ccd_vec3_t *dir, const ccd_t *ccd, + ccd_support_t *supp); + + +/**** INLINES ****/ +_ccd_inline void ccdSupportCopy(ccd_support_t *d, const ccd_support_t *s) +{ + *d = *s; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_SUPPORT_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/ccd/vec3.h b/thirdparty/ode-0.16.5/libccd/src/ccd/vec3.h new file mode 100644 index 0000000..d9175ed --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/ccd/vec3.h @@ -0,0 +1,340 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010,2011 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_VEC3_H__ +#define __CCD_VEC3_H__ + +#include +#include +#include + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#ifndef CCD_SINGLE +# ifndef CCD_DOUBLE +# error You must define CCD_SINGLE or CCD_DOUBLE +# endif /* CCD_DOUBLE */ +#endif /* CCD_SINGLE */ + + +#if defined(_MSC_VER) && _MSC_VER < 1700 +/* Define fmin, fmax, fminf, fmaxf which are missing from MSVC (up to VS2005 at least) */ +static __inline double fmin(double x, double y) { return __min(x, y); } +static __inline double fmax(double x, double y) { return __max(x, y); } +static __inline float fminf(float x, float y) { return __min(x, y); } +static __inline float fmaxf(float x, float y) { return __max(x, y); } + +#endif /* #if defined(_MSC_VER) */ + + +#ifdef CCD_SINGLE +# ifdef CCD_DOUBLE +# error You can define either CCD_SINGLE or CCD_DOUBLE, not both! +# endif /* CCD_DOUBLE */ + +typedef float ccd_real_t; + +/*# define CCD_EPS 1E-6*/ +# define CCD_EPS FLT_EPSILON + +# define CCD_REAL_MAX FLT_MAX + +# define CCD_REAL(x) (x ## f) /*!< form a constant */ +# define CCD_SQRT(x) (sqrtf(x)) /*!< square root */ +# define CCD_FABS(x) (fabsf(x)) /*!< absolute value */ +# define CCD_FMAX(x, y) (fmaxf((x), (y))) /*!< maximum of two floats */ +# define CCD_FMIN(x, y) (fminf((x), (y))) /*!< minimum of two floats */ +#endif /* CCD_SINGLE */ + +#ifdef CCD_DOUBLE +typedef double ccd_real_t; + +/*# define CCD_EPS 1E-10*/ +# define CCD_EPS DBL_EPSILON + +# define CCD_REAL_MAX DBL_MAX + +# define CCD_REAL(x) (x) /*!< form a constant */ +# define CCD_SQRT(x) (sqrt(x)) /*!< square root */ +# define CCD_FABS(x) (fabs(x)) /*!< absolute value */ +# define CCD_FMAX(x, y) (fmax((x), (y))) /*!< maximum of two floats */ +# define CCD_FMIN(x, y) (fmin((x), (y))) /*!< minimum of two floats */ +#endif /* CCD_DOUBLE */ + +#define CCD_ONE CCD_REAL(1.) +#define CCD_ZERO CCD_REAL(0.) + +struct _ccd_vec3_t { + ccd_real_t v[3]; +}; +typedef struct _ccd_vec3_t ccd_vec3_t; + + +/** + * Holds origin (0,0,0) - this variable is meant to be read-only! + */ +extern ccd_vec3_t *ccd_vec3_origin; + +/** + * Array of points uniformly distributed on unit sphere. + */ +extern ccd_vec3_t *ccd_points_on_sphere; +extern size_t ccd_points_on_sphere_len; + +/** Returns sign of value. */ +_ccd_inline int ccdSign(ccd_real_t val); +/** Returns true if val is zero. **/ +_ccd_inline int ccdIsZero(ccd_real_t val); +/** Returns true if a and b equal. **/ +_ccd_inline int ccdEq(ccd_real_t a, ccd_real_t b); + + +#define CCD_VEC3_STATIC(x, y, z) \ + { { (x), (y), (z) } } + +#define CCD_VEC3(name, x, y, z) \ + ccd_vec3_t name = CCD_VEC3_STATIC((x), (y), (z)) + +_ccd_inline ccd_real_t ccdVec3X(const ccd_vec3_t *v); +_ccd_inline ccd_real_t ccdVec3Y(const ccd_vec3_t *v); +_ccd_inline ccd_real_t ccdVec3Z(const ccd_vec3_t *v); + +/** + * Returns true if a and b equal. + */ +_ccd_inline int ccdVec3Eq(const ccd_vec3_t *a, const ccd_vec3_t *b); + +/** + * Returns squared length of vector. + */ +_ccd_inline ccd_real_t ccdVec3Len2(const ccd_vec3_t *v); + +/** + * Returns distance between a and b. + */ +_ccd_inline ccd_real_t ccdVec3Dist2(const ccd_vec3_t *a, const ccd_vec3_t *b); + + +_ccd_inline void ccdVec3Set(ccd_vec3_t *v, ccd_real_t x, ccd_real_t y, ccd_real_t z); + +/** + * v = w + */ +_ccd_inline void ccdVec3Copy(ccd_vec3_t *v, const ccd_vec3_t *w); + +/** + * Subtracts coordinates of vector w from vector v. v = v - w + */ +_ccd_inline void ccdVec3Sub(ccd_vec3_t *v, const ccd_vec3_t *w); + +/** + * Adds coordinates of vector w to vector v. v = v + w + */ +_ccd_inline void ccdVec3Add(ccd_vec3_t *v, const ccd_vec3_t *w); + +/** + * d = v - w + */ +_ccd_inline void ccdVec3Sub2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w); + +/** + * d = d * k; + */ +_ccd_inline void ccdVec3Scale(ccd_vec3_t *d, ccd_real_t k); + + +/** + * Normalizes given vector to unit length. + */ +_ccd_inline void ccdVec3Normalize(ccd_vec3_t *d); + + +/** + * Dot product of two vectors. + */ +_ccd_inline ccd_real_t ccdVec3Dot(const ccd_vec3_t *a, const ccd_vec3_t *b); + +/** + * Cross product: d = a x b. + */ +_ccd_inline void ccdVec3Cross(ccd_vec3_t *d, const ccd_vec3_t *a, const ccd_vec3_t *b); + + +/** + * Returns distance2 of point P to segment ab. + * If witness is non-NULL it is filled with coordinates of point from which + * was computed distance to point P. + */ +ccd_real_t ccdVec3PointSegmentDist2(const ccd_vec3_t *P, + const ccd_vec3_t *a, const ccd_vec3_t *b, + ccd_vec3_t *witness); + +/** + * Returns distance2 of point P from triangle formed by triplet a, b, c. + * If witness vector is provided it is filled with coordinates of point + * from which was computed distance to point P. + */ +ccd_real_t ccdVec3PointTriDist2(const ccd_vec3_t *P, + const ccd_vec3_t *a, const ccd_vec3_t *b, + const ccd_vec3_t *c, + ccd_vec3_t *witness); + + +/**** INLINES ****/ +_ccd_inline int ccdSign(ccd_real_t val) +{ + if (ccdIsZero(val)){ + return 0; + }else if (val < CCD_ZERO){ + return -1; + } + return 1; +} + +_ccd_inline int ccdIsZero(ccd_real_t val) +{ + return CCD_FABS(val) < CCD_EPS; +} + +_ccd_inline int ccdEq(ccd_real_t _a, ccd_real_t _b) +{ + ccd_real_t ab; + ccd_real_t a, b; + + ab = CCD_FABS(_a - _b); + if (CCD_FABS(ab) < CCD_EPS) + return 1; + + a = CCD_FABS(_a); + b = CCD_FABS(_b); + if (b > a){ + return ab < CCD_EPS * b; + }else{ + return ab < CCD_EPS * a; + } +} + + +_ccd_inline ccd_real_t ccdVec3X(const ccd_vec3_t *v) +{ + return v->v[0]; +} + +_ccd_inline ccd_real_t ccdVec3Y(const ccd_vec3_t *v) +{ + return v->v[1]; +} + +_ccd_inline ccd_real_t ccdVec3Z(const ccd_vec3_t *v) +{ + return v->v[2]; +} + +_ccd_inline int ccdVec3Eq(const ccd_vec3_t *a, const ccd_vec3_t *b) +{ + return ccdEq(ccdVec3X(a), ccdVec3X(b)) + && ccdEq(ccdVec3Y(a), ccdVec3Y(b)) + && ccdEq(ccdVec3Z(a), ccdVec3Z(b)); +} + +_ccd_inline ccd_real_t ccdVec3Len2(const ccd_vec3_t *v) +{ + return ccdVec3Dot(v, v); +} + +_ccd_inline ccd_real_t ccdVec3Dist2(const ccd_vec3_t *a, const ccd_vec3_t *b) +{ + ccd_vec3_t ab; + ccdVec3Sub2(&ab, a, b); + return ccdVec3Len2(&ab); +} + +_ccd_inline void ccdVec3Set(ccd_vec3_t *v, ccd_real_t x, ccd_real_t y, ccd_real_t z) +{ + v->v[0] = x; + v->v[1] = y; + v->v[2] = z; +} + +_ccd_inline void ccdVec3Copy(ccd_vec3_t *v, const ccd_vec3_t *w) +{ + *v = *w; +} + +_ccd_inline void ccdVec3Sub(ccd_vec3_t *v, const ccd_vec3_t *w) +{ + v->v[0] -= w->v[0]; + v->v[1] -= w->v[1]; + v->v[2] -= w->v[2]; +} + +_ccd_inline void ccdVec3Sub2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w) +{ + d->v[0] = v->v[0] - w->v[0]; + d->v[1] = v->v[1] - w->v[1]; + d->v[2] = v->v[2] - w->v[2]; +} + +_ccd_inline void ccdVec3Add(ccd_vec3_t *v, const ccd_vec3_t *w) +{ + v->v[0] += w->v[0]; + v->v[1] += w->v[1]; + v->v[2] += w->v[2]; +} + +_ccd_inline void ccdVec3Scale(ccd_vec3_t *d, ccd_real_t k) +{ + d->v[0] *= k; + d->v[1] *= k; + d->v[2] *= k; +} + +_ccd_inline void ccdVec3Normalize(ccd_vec3_t *d) +{ + ccd_real_t k = CCD_ONE / CCD_SQRT(ccdVec3Len2(d)); + ccdVec3Scale(d, k); +} + +_ccd_inline ccd_real_t ccdVec3Dot(const ccd_vec3_t *a, const ccd_vec3_t *b) +{ + ccd_real_t dot; + + dot = a->v[0] * b->v[0]; + dot += a->v[1] * b->v[1]; + dot += a->v[2] * b->v[2]; + return dot; +} + +_ccd_inline void ccdVec3Cross(ccd_vec3_t *d, const ccd_vec3_t *a, const ccd_vec3_t *b) +{ + d->v[0] = (a->v[1] * b->v[2]) - (a->v[2] * b->v[1]); + d->v[1] = (a->v[2] * b->v[0]) - (a->v[0] * b->v[2]); + d->v[2] = (a->v[0] * b->v[1]) - (a->v[1] * b->v[0]); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_VEC3_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/config.h.in b/thirdparty/ode-0.16.5/libccd/src/config.h.in new file mode 100644 index 0000000..f928422 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/config.h.in @@ -0,0 +1,97 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_FLOAT_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `m' library (-lm). */ +#undef HAVE_LIBM + +/* Define to 1 if you have the `rt' library (-lrt). */ +#undef HAVE_LIBRT + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/quat.h b/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/quat.h new file mode 100644 index 0000000..157dd85 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/quat.h @@ -0,0 +1,69 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_CUSTOM_QUAT_H__ +#define __CCD_CUSTOM_QUAT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Rotate vector s by quaternion q and put result into d. + */ +_ccd_inline void ccdQuatRotVec2(ccd_vec3_t *d, const ccd_vec3_t *s, const ccd_quat_t *q); + + +_ccd_inline void ccdQuatRotVec2(ccd_vec3_t *d, const ccd_vec3_t *s, const ccd_quat_t *q) +{ +#ifndef dLIBCCD_USE_SYSTEM + // original version: 31 mul + 21 add + // optimized version: 18 mul + 12 add + // formula: d = s + 2 * cross(q.xyz, cross(q.xyz, v) + q.w * s) + ccd_real_t cross1_x, cross1_y, cross1_z, cross2_x, cross2_y, cross2_z; + ccd_real_t x, y, z, w; + ccd_real_t vx, vy, vz; + + vx = ccdVec3X(s); + vy = ccdVec3Y(s); + vz = ccdVec3Z(s); + + w = q->q[3]; + x = q->q[0]; + y = q->q[1]; + z = q->q[2]; + + cross1_x = y * vz - z * vy + w * vx; + cross1_y = z * vx - x * vz + w * vy; + cross1_z = x * vy - y * vx + w * vz; + cross2_x = y * cross1_z - z * cross1_y; + cross2_y = z * cross1_x - x * cross1_z; + cross2_z = x * cross1_y - y * cross1_x; + ccdVec3Set(d, vx + 2 * cross2_x, vy + 2 * cross2_y, vz + 2 * cross2_z); +#else + ccdVec3Copy(d, s); + ccdQuatRotVec(d, q); +#endif +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_QUAT_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/vec3.h b/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/vec3.h new file mode 100644 index 0000000..2ac2f22 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/custom/ccdcustom/vec3.h @@ -0,0 +1,114 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010,2011 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#ifndef __CCD_CUSTOM_VEC3_H__ +#define __CCD_CUSTOM_VEC3_H__ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#ifdef CCD_SINGLE +# define CCD_ATAN2(x, y) (atan2f((x), (y))) /*!< atan2 of two floats */ +#endif /* CCD_SINGLE */ + +#ifdef CCD_DOUBLE +# define CCD_ATAN2(x, y) (atan2((x), (y))) /*!< atan2 of two floats */ +#endif /* CCD_DOUBLE */ + +/** + * d = v + w + */ +_ccd_inline void ccdVec3Add2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w); + +/** + * d = s * k; + */ +_ccd_inline void ccdVec3CopyScaled(ccd_vec3_t *d, const ccd_vec3_t *s, ccd_real_t k); + +/** + * d = v + s * k; + */ +_ccd_inline void ccdVec3AddScaled(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *s, ccd_real_t k); + + +/** + * Normalizes given vector to unit length. + */ +_ccd_inline int ccdVec3SafeNormalize(ccd_vec3_t *d); + + +_ccd_inline void ccdVec3Add2(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *w) +{ +#ifndef dLIBCCD_USE_SYSTEM + d->v[0] = v->v[0] + w->v[0]; + d->v[1] = v->v[1] + w->v[1]; + d->v[2] = v->v[2] + w->v[2]; +#else + ccdVec3Copy(d, v); + ccdVec3Add(d, w); +#endif +} + +_ccd_inline void ccdVec3CopyScaled(ccd_vec3_t *d, const ccd_vec3_t *s, ccd_real_t k) +{ +#ifndef dLIBCCD_USE_SYSTEM + d->v[0] = s->v[0] * k; + d->v[1] = s->v[1] * k; + d->v[2] = s->v[2] * k; +#else + ccdVec3Copy(d, s); + ccdVec3Scale(d, k); +#endif +} + +_ccd_inline void ccdVec3AddScaled(ccd_vec3_t *d, const ccd_vec3_t *v, const ccd_vec3_t *s, ccd_real_t k) +{ +#ifndef dLIBCCD_USE_SYSTEM + d->v[0] = v->v[0] + s->v[0] * k; + d->v[1] = v->v[1] + s->v[1] * k; + d->v[2] = v->v[2] + s->v[2] * k; +#else + ccdVec3Copy(d, s); + ccdVec3Scale(d, k); + ccdVec3Add(d, v); +#endif +} + +_ccd_inline int ccdVec3SafeNormalize(ccd_vec3_t *d) +{ + int result = -1; + + ccd_real_t len = CCD_SQRT(ccdVec3Len2(d)); + if (len >= CCD_EPS) { + ccdVec3Scale(d, CCD_ONE / len); + result = 0; + } + + return result; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_CUSTOM_VEC3_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/mpr.c b/thirdparty/ode-0.16.5/libccd/src/mpr.c new file mode 100644 index 0000000..072a99b --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/mpr.c @@ -0,0 +1,572 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010,2011 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/** Finds origin (center) of Minkowski difference (actually it can be any + * interior point of Minkowski difference. */ +_ccd_inline void findOrigin(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_support_t *center); + +/** Discovers initial portal - that is tetrahedron that intersects with + * origin ray (ray from center of Minkowski diff to (0,0,0). + * + * Returns -1 if already recognized that origin is outside Minkowski + * portal. + * Returns 1 if origin lies on v1 of simplex (only v0 and v1 are present + * in simplex). + * Returns 2 if origin lies on v0-v1 segment. + * Returns 0 if portal was built. + */ +static int discoverPortal(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *portal); + + +/** Expands portal towards origin and determine if objects intersect. + * Already established portal must be given as argument. + * If intersection is found 0 is returned, -1 otherwise */ +static int refinePortal(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *portal); + +/** Finds penetration info by expanding provided portal. */ +static int findPenetr(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos); + +/** Finds penetration info if origin lies on portal's v1 */ +static void findPenetrTouch(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos); + +/** Find penetration info if origin lies on portal's segment v0-v1 */ +static int findPenetrSegment(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos); + +/** Finds position vector from fully established portal */ +static int findPos(const void *obj1, const void *obj2, const ccd_t *ccd, + const ccd_simplex_t *portal, ccd_vec3_t *pos); + +/** Extends portal with new support point. + * Portal must have face v1-v2-v3 arranged to face outside portal. */ +_ccd_inline void expandPortal(ccd_simplex_t *portal, + const ccd_support_t *v4); + +/** Fill dir with direction outside portal. Portal's v1-v2-v3 face must be + * arranged in correct order! */ +_ccd_inline int portalDir(const ccd_simplex_t *portal, ccd_vec3_t *dir); + +/** Returns true if portal encapsules origin (0,0,0), dir is direction of + * v1-v2-v3 face. */ +_ccd_inline int portalEncapsulesOrigin(const ccd_simplex_t *portal, + const ccd_vec3_t *dir); + +/** Returns true if portal with new point v4 would reach specified + * tolerance (i.e. returns true if portal can _not_ significantly expand + * within Minkowski difference). + * + * v4 is candidate for new point in portal, dir is direction in which v4 + * was obtained. */ +_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal, + const ccd_support_t *v4, + const ccd_vec3_t *dir, + const ccd_t *ccd); + +/** Returns true if portal expanded by new point v4 could possibly contain + * origin, dir is direction in which v4 was obtained. */ +_ccd_inline int portalCanEncapsuleOrigin(const ccd_simplex_t *portal, + const ccd_support_t *v4, + const ccd_vec3_t *dir); + + +int ccdMPRIntersect(const void *obj1, const void *obj2, const ccd_t *ccd) +{ + ccd_simplex_t portal; + int res; + + // Phase 1: Portal discovery - find portal that intersects with origin + // ray (ray from center of Minkowski diff to origin of coordinates) + res = discoverPortal(obj1, obj2, ccd, &portal); + if (res < 0) + return 0; + if (res > 0) + return 1; + + // Phase 2: Portal refinement + res = refinePortal(obj1, obj2, ccd, &portal); + return (res == 0 ? 1 : 0); +} + +int ccdMPRPenetration(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) +{ + ccd_simplex_t portal; + int res; + + // Phase 1: Portal discovery + res = discoverPortal(obj1, obj2, ccd, &portal); + if (res < 0){ + // Origin isn't inside portal - no collision. + return -1; + + }else if (res == 1){ + // Touching contact on portal's v1. + findPenetrTouch(obj1, obj2, ccd, &portal, depth, dir, pos); + + }else if (res == 2){ + // Origin lies on v0-v1 segment. + if (findPenetrSegment(obj1, obj2, ccd, &portal, depth, dir, pos) != 0) { + return -1; + } + + }else if (res == 0){ + // Phase 2: Portal refinement + res = refinePortal(obj1, obj2, ccd, &portal); + if (res < 0) { + return -1; + } + + // Phase 3. Penetration info + if (findPenetr(obj1, obj2, ccd, &portal, depth, dir, pos) != 0) { + return -1; + } + } + + return 0; +} + + + +_ccd_inline void findOrigin(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_support_t *center) +{ + ccd->center1(obj1, ¢er->v1); + ccd->center2(obj2, ¢er->v2); + ccdVec3Sub2(¢er->v, ¢er->v1, ¢er->v2); +} + +static int discoverPortal(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *portal) +{ + ccd_vec3_t dir, va, vb; + ccd_real_t dot; + int cont; + + // vertex 0 is center of portal + findOrigin(obj1, obj2, ccd, ccdSimplexPointW(portal, 0)); + ccdSimplexSetSize(portal, 1); + + if (ccdVec3Eq(&ccdSimplexPoint(portal, 0)->v, ccd_vec3_origin)){ + // Portal's center lies on origin (0,0,0) => we know that objects + // intersect but we would need to know penetration info. + // So move center little bit... + ccdVec3Set(&va, CCD_EPS * CCD_REAL(10.), CCD_ZERO, CCD_ZERO); + ccdVec3Add(&ccdSimplexPointW(portal, 0)->v, &va); + } + + + // vertex 1 = support in direction of origin + ccdVec3Copy(&dir, &ccdSimplexPoint(portal, 0)->v); + ccdVec3Scale(&dir, CCD_REAL(-1.)); + if (ccdVec3SafeNormalize(&dir) != 0) { + return -1; + } + __ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 1)); + ccdSimplexSetSize(portal, 2); + + // test if origin isn't outside of v1 + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, &dir); + if (ccdIsZero(dot) || dot < CCD_ZERO) + return -1; + + + // vertex 2 + ccdVec3Cross(&dir, &ccdSimplexPoint(portal, 0)->v, + &ccdSimplexPoint(portal, 1)->v); + if (ccdIsZero(ccdVec3Len2(&dir))){ + if (ccdVec3Eq(&ccdSimplexPoint(portal, 1)->v, ccd_vec3_origin)){ + // origin lies on v1 + return 1; + }else{ + // origin lies on v0-v1 segment + return 2; + } + } + + if (ccdVec3SafeNormalize(&dir) != 0) { + return -1; + } + __ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 2)); + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, &dir); + if (ccdIsZero(dot) || dot < CCD_ZERO) { + return -1; + } + + ccdSimplexSetSize(portal, 3); + + // vertex 3 direction + ccdVec3Sub2(&va, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 0)->v); + ccdVec3Sub2(&vb, &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 0)->v); + ccdVec3Cross(&dir, &va, &vb); + if (ccdVec3SafeNormalize(&dir) != 0) { + return -1; + } + + // it is better to form portal faces to be oriented "outside" origin + dot = ccdVec3Dot(&dir, &ccdSimplexPoint(portal, 0)->v); + if (dot > CCD_ZERO){ + ccdSimplexSwap(portal, 1, 2); + ccdVec3Scale(&dir, CCD_REAL(-1.)); + } + + while (ccdSimplexSize(portal) < 4){ + __ccdSupport(obj1, obj2, &dir, ccd, ccdSimplexPointW(portal, 3)); + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, &dir); + if (ccdIsZero(dot) || dot < CCD_ZERO) { + return -1; + } + + cont = 0; + + // test if origin is outside (v1, v0, v3) - set v2 as v3 and + // continue + ccdVec3Cross(&va, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 3)->v); + dot = ccdVec3Dot(&va, &ccdSimplexPoint(portal, 0)->v); + if (dot < CCD_ZERO && !ccdIsZero(dot)){ + ccdSimplexSet(portal, 2, ccdSimplexPoint(portal, 3)); + cont = 1; + } + + if (!cont){ + // test if origin is outside (v3, v0, v2) - set v1 as v3 and + // continue + ccdVec3Cross(&va, &ccdSimplexPoint(portal, 3)->v, + &ccdSimplexPoint(portal, 2)->v); + dot = ccdVec3Dot(&va, &ccdSimplexPoint(portal, 0)->v); + if (dot < CCD_ZERO && !ccdIsZero(dot)){ + ccdSimplexSet(portal, 1, ccdSimplexPoint(portal, 3)); + cont = 1; + } + } + + if (cont){ + ccdVec3Sub2(&va, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 0)->v); + ccdVec3Sub2(&vb, &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 0)->v); + ccdVec3Cross(&dir, &va, &vb); + if (ccdVec3SafeNormalize(&dir) != 0) { + return -1; + } + }else{ + ccdSimplexSetSize(portal, 4); + } + } + + return 0; +} + +static int refinePortal(const void *obj1, const void *obj2, + const ccd_t *ccd, ccd_simplex_t *portal) +{ + ccd_vec3_t dir; + ccd_support_t v4; + + while (1){ + // compute direction outside the portal (from v0 throught v1,v2,v3 + // face) + if (portalDir(portal, &dir) != 0) { + return -1; + } + + // test if origin is inside the portal + if (portalEncapsulesOrigin(portal, &dir)) + return 0; + + // get next support point + __ccdSupport(obj1, obj2, &dir, ccd, &v4); + + // test if v4 can expand portal to contain origin and if portal + // expanding doesn't reach given tolerance + if (!portalCanEncapsuleOrigin(portal, &v4, &dir) + || portalReachTolerance(portal, &v4, &dir, ccd)){ + return -1; + } + + // v1-v2-v3 triangle must be rearranged to face outside Minkowski + // difference (direction from v0). + expandPortal(portal, &v4); + } + + return -1; +} + + +static int findPenetr(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *pdir, ccd_vec3_t *pos) +{ + ccd_vec3_t dir; + ccd_support_t v4; + unsigned long iterations; + + iterations = 0UL; + while (1){ + // compute portal direction and obtain next support point + if (portalDir(portal, &dir) != 0) { + return -1; + } + + __ccdSupport(obj1, obj2, &dir, ccd, &v4); + + // reached tolerance -> find penetration info + if (portalReachTolerance(portal, &v4, &dir, ccd) + || iterations > ccd->max_iterations){ + *depth = ccdVec3PointTriDist2(ccd_vec3_origin, + &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 3)->v, + pdir); + *depth = CCD_SQRT(*depth); + if (ccdVec3SafeNormalize(pdir) != 0) { + return -1; + } + + // barycentric coordinates: + if (findPos(obj1, obj2, ccd, portal, pos) != 0) { + return -1; + } + + return 0; + } + + expandPortal(portal, &v4); + + iterations++; + } +} + +static void findPenetrTouch(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) +{ + // Touching contact on portal's v1 - so depth is zero and direction + // is unimportant and pos can be guessed + *depth = CCD_REAL(0.); + ccdVec3Copy(dir, ccd_vec3_origin); + + ccdVec3Copy(pos, &ccdSimplexPoint(portal, 1)->v1); + ccdVec3Add(pos, &ccdSimplexPoint(portal, 1)->v2); + ccdVec3Scale(pos, 0.5); +} + +static int findPenetrSegment(const void *obj1, const void *obj2, const ccd_t *ccd, + ccd_simplex_t *portal, + ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) +{ + /* + ccd_vec3_t vec; + ccd_real_t k; + */ + + // Origin lies on v0-v1 segment. + // Depth is distance to v1, direction also and position must be + // computed + + ccdVec3Copy(pos, &ccdSimplexPoint(portal, 1)->v1); + ccdVec3Add(pos, &ccdSimplexPoint(portal, 1)->v2); + ccdVec3Scale(pos, CCD_REAL(0.5)); + + /* + ccdVec3Sub2(&vec, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 0)->v); + k = CCD_SQRT(ccdVec3Len2(&ccdSimplexPoint(portal, 0)->v)); + k /= CCD_SQRT(ccdVec3Len2(&vec)); + ccdVec3Scale(&vec, -k); + ccdVec3Add(pos, &vec); + */ + + ccdVec3Copy(dir, &ccdSimplexPoint(portal, 1)->v); + *depth = CCD_SQRT(ccdVec3Len2(dir)); + if (ccdVec3SafeNormalize(dir) != 0) { + return -1; + } + return 0; +} + + +static int findPos(const void *obj1, const void *obj2, const ccd_t *ccd, + const ccd_simplex_t *portal, ccd_vec3_t *pos) +{ + ccd_vec3_t dir; + size_t i; + ccd_real_t b[4], sum, inv; + ccd_vec3_t vec, p1, p2; + + if (portalDir(portal, &dir) != 0) { + return -1; + } + + // use barycentric coordinates of tetrahedron to find origin + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 2)->v); + b[0] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 3)->v); + + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 3)->v, + &ccdSimplexPoint(portal, 2)->v); + b[1] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 0)->v); + + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 0)->v, + &ccdSimplexPoint(portal, 1)->v); + b[2] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 3)->v); + + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 1)->v); + b[3] = ccdVec3Dot(&vec, &ccdSimplexPoint(portal, 0)->v); + + sum = b[0] + b[1] + b[2] + b[3]; + + if (ccdIsZero(sum) || sum < CCD_ZERO){ + b[0] = CCD_REAL(0.); + + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 3)->v); + b[1] = ccdVec3Dot(&vec, &dir); + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 3)->v, + &ccdSimplexPoint(portal, 1)->v); + b[2] = ccdVec3Dot(&vec, &dir); + ccdVec3Cross(&vec, &ccdSimplexPoint(portal, 1)->v, + &ccdSimplexPoint(portal, 2)->v); + b[3] = ccdVec3Dot(&vec, &dir); + + sum = b[1] + b[2] + b[3]; + } + + inv = CCD_REAL(1.) / sum; + + ccdVec3Copy(&p1, ccd_vec3_origin); + ccdVec3Copy(&p2, ccd_vec3_origin); + for (i = 0; i < 4; i++){ + ccdVec3Copy(&vec, &ccdSimplexPoint(portal, i)->v1); + ccdVec3Scale(&vec, b[i]); + ccdVec3Add(&p1, &vec); + + ccdVec3Copy(&vec, &ccdSimplexPoint(portal, i)->v2); + ccdVec3Scale(&vec, b[i]); + ccdVec3Add(&p2, &vec); + } + ccdVec3Scale(&p1, inv); + ccdVec3Scale(&p2, inv); + + ccdVec3Copy(pos, &p1); + ccdVec3Add(pos, &p2); + ccdVec3Scale(pos, 0.5); + return 0; +} + +_ccd_inline void expandPortal(ccd_simplex_t *portal, + const ccd_support_t *v4) +{ + ccd_real_t dot; + ccd_vec3_t v4v0; + + ccdVec3Cross(&v4v0, &v4->v, &ccdSimplexPoint(portal, 0)->v); + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, &v4v0); + if (dot > CCD_ZERO){ + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, &v4v0); + if (dot > CCD_ZERO){ + ccdSimplexSet(portal, 1, v4); + }else{ + ccdSimplexSet(portal, 3, v4); + } + }else{ + dot = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, &v4v0); + if (dot > CCD_ZERO){ + ccdSimplexSet(portal, 2, v4); + }else{ + ccdSimplexSet(portal, 1, v4); + } + } +} + +_ccd_inline int portalDir(const ccd_simplex_t *portal, ccd_vec3_t *dir) +{ + ccd_vec3_t v2v1, v3v1; + + ccdVec3Sub2(&v2v1, &ccdSimplexPoint(portal, 2)->v, + &ccdSimplexPoint(portal, 1)->v); + ccdVec3Sub2(&v3v1, &ccdSimplexPoint(portal, 3)->v, + &ccdSimplexPoint(portal, 1)->v); + ccdVec3Cross(dir, &v2v1, &v3v1); + if (ccdVec3SafeNormalize(dir) != 0) { + return -1; + } + return 0; +} + +_ccd_inline int portalEncapsulesOrigin(const ccd_simplex_t *portal, + const ccd_vec3_t *dir) +{ + ccd_real_t dot; + dot = ccdVec3Dot(dir, &ccdSimplexPoint(portal, 1)->v); + return ccdIsZero(dot) || dot > CCD_ZERO; +} + +_ccd_inline int portalReachTolerance(const ccd_simplex_t *portal, + const ccd_support_t *v4, + const ccd_vec3_t *dir, + const ccd_t *ccd) +{ + ccd_real_t dv1, dv2, dv3, dv4; + ccd_real_t dot1, dot2, dot3; + + // find the smallest dot product of dir and {v1-v4, v2-v4, v3-v4} + + dv1 = ccdVec3Dot(&ccdSimplexPoint(portal, 1)->v, dir); + dv2 = ccdVec3Dot(&ccdSimplexPoint(portal, 2)->v, dir); + dv3 = ccdVec3Dot(&ccdSimplexPoint(portal, 3)->v, dir); + dv4 = ccdVec3Dot(&v4->v, dir); + + dot1 = dv4 - dv1; + dot2 = dv4 - dv2; + dot3 = dv4 - dv3; + + dot1 = CCD_FMIN(dot1, dot2); + dot1 = CCD_FMIN(dot1, dot3); + + return ccdEq(dot1, ccd->mpr_tolerance) || dot1 < ccd->mpr_tolerance; +} + +_ccd_inline int portalCanEncapsuleOrigin(const ccd_simplex_t *portal, + const ccd_support_t *v4, + const ccd_vec3_t *dir) +{ + ccd_real_t dot; + dot = ccdVec3Dot(&v4->v, dir); + return ccdIsZero(dot) || dot > CCD_ZERO; +} diff --git a/thirdparty/ode-0.16.5/libccd/src/polytope.c b/thirdparty/ode-0.16.5/libccd/src/polytope.c new file mode 100644 index 0000000..c340b8c --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/polytope.c @@ -0,0 +1,287 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +_ccd_inline void _ccdPtNearestUpdate(ccd_pt_t *pt, ccd_pt_el_t *el) +{ + if (ccdEq(pt->nearest_dist, el->dist)){ + if (el->type < pt->nearest_type){ + pt->nearest = el; + pt->nearest_dist = el->dist; + pt->nearest_type = el->type; + } + }else if (el->dist < pt->nearest_dist){ + pt->nearest = el; + pt->nearest_dist = el->dist; + pt->nearest_type = el->type; + } +} + +static void _ccdPtNearestRenew(ccd_pt_t *pt) +{ + ccd_pt_vertex_t *v; + ccd_pt_edge_t *e; + ccd_pt_face_t *f; + + pt->nearest_dist = CCD_REAL_MAX; + pt->nearest_type = 3; + pt->nearest = NULL; + + ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){ + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)v); + } + + ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){ + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)e); + } + + ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){ + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)f); + } +} + + + +void ccdPtInit(ccd_pt_t *pt) +{ + ccdListInit(&pt->vertices); + ccdListInit(&pt->edges); + ccdListInit(&pt->faces); + + pt->nearest = NULL; + pt->nearest_dist = CCD_REAL_MAX; + pt->nearest_type = 3; +} + +void ccdPtDestroy(ccd_pt_t *pt) +{ + ccd_pt_face_t *f, *f2; + ccd_pt_edge_t *e, *e2; + ccd_pt_vertex_t *v, *v2; + + // first delete all faces + ccdListForEachEntrySafe(&pt->faces, f, ccd_pt_face_t, f2, ccd_pt_face_t, list){ + ccdPtDelFace(pt, f); + } + + // delete all edges + ccdListForEachEntrySafe(&pt->edges, e, ccd_pt_edge_t, e2, ccd_pt_edge_t, list){ + ccdPtDelEdge(pt, e); + } + + // delete all vertices + ccdListForEachEntrySafe(&pt->vertices, v, ccd_pt_vertex_t, v2, ccd_pt_vertex_t, list){ + ccdPtDelVertex(pt, v); + } +} + + +ccd_pt_vertex_t *ccdPtAddVertex(ccd_pt_t *pt, const ccd_support_t *v) +{ + ccd_pt_vertex_t *vert; + + vert = CCD_ALLOC(ccd_pt_vertex_t); + vert->type = CCD_PT_VERTEX; + ccdSupportCopy(&vert->v, v); + + vert->dist = ccdVec3Len2(&vert->v.v); + ccdVec3Copy(&vert->witness, &vert->v.v); + + ccdListInit(&vert->edges); + + // add vertex to list + ccdListAppend(&pt->vertices, &vert->list); + + // update position in .nearest array + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)vert); + + return vert; +} + +ccd_pt_edge_t *ccdPtAddEdge(ccd_pt_t *pt, ccd_pt_vertex_t *v1, + ccd_pt_vertex_t *v2) +{ + const ccd_vec3_t *a, *b; + ccd_pt_edge_t *edge; + + edge = CCD_ALLOC(ccd_pt_edge_t); + edge->type = CCD_PT_EDGE; + edge->vertex[0] = v1; + edge->vertex[1] = v2; + edge->faces[0] = edge->faces[1] = NULL; + + a = &edge->vertex[0]->v.v; + b = &edge->vertex[1]->v.v; + edge->dist = ccdVec3PointSegmentDist2(ccd_vec3_origin, a, b, &edge->witness); + + ccdListAppend(&edge->vertex[0]->edges, &edge->vertex_list[0]); + ccdListAppend(&edge->vertex[1]->edges, &edge->vertex_list[1]); + + ccdListAppend(&pt->edges, &edge->list); + + // update position in .nearest array + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)edge); + + return edge; +} + +ccd_pt_face_t *ccdPtAddFace(ccd_pt_t *pt, ccd_pt_edge_t *e1, + ccd_pt_edge_t *e2, + ccd_pt_edge_t *e3) +{ + const ccd_vec3_t *a, *b, *c; + ccd_pt_face_t *face; + ccd_pt_edge_t *e; + size_t i; + + face = CCD_ALLOC(ccd_pt_face_t); + face->type = CCD_PT_FACE; + face->edge[0] = e1; + face->edge[1] = e2; + face->edge[2] = e3; + + // obtain triplet of vertices + a = &face->edge[0]->vertex[0]->v.v; + b = &face->edge[0]->vertex[1]->v.v; + e = face->edge[1]; + if (e->vertex[0] != face->edge[0]->vertex[0] + && e->vertex[0] != face->edge[0]->vertex[1]){ + c = &e->vertex[0]->v.v; + }else{ + c = &e->vertex[1]->v.v; + } + face->dist = ccdVec3PointTriDist2(ccd_vec3_origin, a, b, c, &face->witness); + + + for (i = 0; i < 3; i++){ + if (face->edge[i]->faces[0] == NULL){ + face->edge[i]->faces[0] = face; + }else{ + face->edge[i]->faces[1] = face; + } + } + + ccdListAppend(&pt->faces, &face->list); + + // update position in .nearest array + _ccdPtNearestUpdate(pt, (ccd_pt_el_t *)face); + + return face; +} + + +void ccdPtRecomputeDistances(ccd_pt_t *pt) +{ + ccd_pt_vertex_t *v; + ccd_pt_edge_t *e; + ccd_pt_face_t *f; + const ccd_vec3_t *a, *b, *c; + ccd_real_t dist; + + ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){ + dist = ccdVec3Len2(&v->v.v); + v->dist = dist; + ccdVec3Copy(&v->witness, &v->v.v); + } + + ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){ + a = &e->vertex[0]->v.v; + b = &e->vertex[1]->v.v; + dist = ccdVec3PointSegmentDist2(ccd_vec3_origin, a, b, &e->witness); + e->dist = dist; + } + + ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){ + // obtain triplet of vertices + a = &f->edge[0]->vertex[0]->v.v; + b = &f->edge[0]->vertex[1]->v.v; + e = f->edge[1]; + if (e->vertex[0] != f->edge[0]->vertex[0] + && e->vertex[0] != f->edge[0]->vertex[1]){ + c = &e->vertex[0]->v.v; + }else{ + c = &e->vertex[1]->v.v; + } + + dist = ccdVec3PointTriDist2(ccd_vec3_origin, a, b, c, &f->witness); + f->dist = dist; + } +} + +ccd_pt_el_t *ccdPtNearest(ccd_pt_t *pt) +{ + if (!pt->nearest){ + _ccdPtNearestRenew(pt); + } + return pt->nearest; +} + + +void ccdPtDumpSVT(ccd_pt_t *pt, const char *fn) +{ + FILE *fout; + + fout = fopen(fn, "a"); + if (fout == NULL) + return; + + ccdPtDumpSVT2(pt, fout); + + fclose(fout); +} + +void ccdPtDumpSVT2(ccd_pt_t *pt, FILE *fout) +{ + ccd_pt_vertex_t *v, *a, *b, *c; + ccd_pt_edge_t *e; + ccd_pt_face_t *f; + size_t i; + + fprintf(fout, "-----\n"); + + fprintf(fout, "Points:\n"); + i = 0; + ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){ + v->id = i++; + fprintf(fout, "%lf %lf %lf\n", + ccdVec3X(&v->v.v), ccdVec3Y(&v->v.v), ccdVec3Z(&v->v.v)); + } + + fprintf(fout, "Edges:\n"); + ccdListForEachEntry(&pt->edges, e, ccd_pt_edge_t, list){ + fprintf(fout, "%d %d\n", e->vertex[0]->id, e->vertex[1]->id); + } + + fprintf(fout, "Faces:\n"); + ccdListForEachEntry(&pt->faces, f, ccd_pt_face_t, list){ + a = f->edge[0]->vertex[0]; + b = f->edge[0]->vertex[1]; + c = f->edge[1]->vertex[0]; + if (c == a || c == b){ + c = f->edge[1]->vertex[1]; + } + fprintf(fout, "%d %d %d\n", a->id, b->id, c->id); + } +} diff --git a/thirdparty/ode-0.16.5/libccd/src/support.c b/thirdparty/ode-0.16.5/libccd/src/support.c new file mode 100644 index 0000000..5ce3586 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/support.c @@ -0,0 +1,39 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +void __ccdSupport(const void *obj1, const void *obj2, + const ccd_vec3_t *_dir, const ccd_t *ccd, + ccd_support_t *supp) +{ + ccd_vec3_t dir; + + ccdVec3Copy(&dir, _dir); + + ccd->support1(obj1, &dir, &supp->v1); + + ccdVec3Scale(&dir, -CCD_ONE); + ccd->support2(obj2, &dir, &supp->v2); + + ccdVec3Sub2(&supp->v, &supp->v1, &supp->v2); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.am b/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.am new file mode 100644 index 0000000..733fed3 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.am @@ -0,0 +1,28 @@ +SUBDIRS = cu + +AM_CPPFLAGS = -I $(srcdir)/.. -I $(builddir)/.. -I $(srcdir)/cu + +LDADD = $(builddir)/cu/libcu.la $(builddir)/../libccd.la + + +check_PROGRAMS = test bench bench2 + +test_SOURCES = main.c \ + common.c common.h \ + support.c support.h \ + vec3.c vec3.h \ + polytope.c polytope.h \ + boxbox.c boxbox.h \ + spheresphere.c spheresphere.h \ + cylcyl.c cylcyl.h \ + boxcyl.c boxcyl.h \ + mpr_boxbox.c mpr_boxbox.h \ + mpr_cylcyl.c mpr_cylcyl.h \ + mpr_boxcyl.c mpr_boxcyl.h + +bench_SOURCES = bench.c \ + support.c support.h + +bench2_SOURCES = bench2.c \ + support.c support.h + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.in b/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.in new file mode 100644 index 0000000..40ff0ea --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/Makefile.in @@ -0,0 +1,753 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +check_PROGRAMS = test$(EXEEXT) bench$(EXEEXT) bench2$(EXEEXT) +subdir = src/testsuites +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am_bench_OBJECTS = bench.$(OBJEXT) support.$(OBJEXT) +bench_OBJECTS = $(am_bench_OBJECTS) +bench_LDADD = $(LDADD) +bench_DEPENDENCIES = $(builddir)/cu/libcu.la $(builddir)/../libccd.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_bench2_OBJECTS = bench2.$(OBJEXT) support.$(OBJEXT) +bench2_OBJECTS = $(am_bench2_OBJECTS) +bench2_LDADD = $(LDADD) +bench2_DEPENDENCIES = $(builddir)/cu/libcu.la $(builddir)/../libccd.la +am_test_OBJECTS = main.$(OBJEXT) common.$(OBJEXT) support.$(OBJEXT) \ + vec3.$(OBJEXT) polytope.$(OBJEXT) boxbox.$(OBJEXT) \ + spheresphere.$(OBJEXT) cylcyl.$(OBJEXT) boxcyl.$(OBJEXT) \ + mpr_boxbox.$(OBJEXT) mpr_cylcyl.$(OBJEXT) mpr_boxcyl.$(OBJEXT) +test_OBJECTS = $(am_test_OBJECTS) +test_LDADD = $(LDADD) +test_DEPENDENCIES = $(builddir)/cu/libcu.la $(builddir)/../libccd.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(bench_SOURCES) $(bench2_SOURCES) $(test_SOURCES) +DIST_SOURCES = $(bench_SOURCES) $(bench2_SOURCES) $(test_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_PRECISION = @CCD_PRECISION@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = cu +AM_CPPFLAGS = -I $(srcdir)/.. -I $(builddir)/.. -I $(srcdir)/cu +LDADD = $(builddir)/cu/libcu.la $(builddir)/../libccd.la +test_SOURCES = main.c \ + common.c common.h \ + support.c support.h \ + vec3.c vec3.h \ + polytope.c polytope.h \ + boxbox.c boxbox.h \ + spheresphere.c spheresphere.h \ + cylcyl.c cylcyl.h \ + boxcyl.c boxcyl.h \ + mpr_boxbox.c mpr_boxbox.h \ + mpr_cylcyl.c mpr_cylcyl.h \ + mpr_boxcyl.c mpr_boxcyl.h + +bench_SOURCES = bench.c \ + support.c support.h + +bench2_SOURCES = bench2.c \ + support.c support.h + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/testsuites/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/testsuites/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +bench$(EXEEXT): $(bench_OBJECTS) $(bench_DEPENDENCIES) $(EXTRA_bench_DEPENDENCIES) + @rm -f bench$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(bench_OBJECTS) $(bench_LDADD) $(LIBS) + +bench2$(EXEEXT): $(bench2_OBJECTS) $(bench2_DEPENDENCIES) $(EXTRA_bench2_DEPENDENCIES) + @rm -f bench2$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(bench2_OBJECTS) $(bench2_LDADD) $(LIBS) + +test$(EXEEXT): $(test_OBJECTS) $(test_DEPENDENCIES) $(EXTRA_test_DEPENDENCIES) + @rm -f test$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_OBJECTS) $(test_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bench.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bench2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boxbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/boxcyl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cylcyl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpr_boxbox.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpr_boxcyl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpr_cylcyl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/polytope.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spheresphere.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/support.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vec3.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/bench.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/bench.c new file mode 100644 index 0000000..779a8ac --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/bench.c @@ -0,0 +1,257 @@ +#define CU_ENABLE_TIMER +#include +#include +#include +#include +#include "support.h" + +TEST_SUITES { + TEST_SUITES_CLOSURE +}; + +static int bench_num = 1; +static size_t cycles = 10000; + +static void runBench(const void *o1, const void *o2, const ccd_t *ccd) +{ + ccd_real_t depth; + ccd_vec3_t dir, pos; + int res; + size_t i; + const struct timespec *timer; + + cuTimerStart(); + for (i = 0; i < cycles; i++){ + res = ccdGJKPenetration(o1, o2, ccd, &depth, &dir, &pos); + } + timer = cuTimerStop(); + fprintf(stdout, "%02d: %ld %ld\n", bench_num, + (long)timer->tv_sec, (long)timer->tv_nsec); + fflush(stdout); + + bench_num++; +} + +static void boxbox(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + ccd_vec3_t axis; + ccd_quat_t rot; + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + bench_num = 1; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0., 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 1., 1.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.2; box2.y = 0.5; box2.z = 1.; + box2.x = box2.y = box2.z = 1.; + + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 0., 0.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -1.3, 0., 0.); + + ccdVec3Set(&box2.pos, 0., 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + + fprintf(stdout, "\n----\n\n"); +} + +void cylcyl(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_CYL(cyl1); + CCD_CYL(cyl2); + ccd_vec3_t axis; + + cyl1.radius = 0.35; + cyl1.height = 0.5; + cyl2.radius = 0.5; + cyl2.height = 1.; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&cyl1.pos, 0.3, 0.1, 0.1); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0., 0., 0.); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, -0.2, 0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0.567, 1.2, 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, -4.567, 1.2, 0.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + fprintf(stdout, "\n----\n\n"); +} + +void boxcyl(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + ccd_vec3_t axis; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + fprintf(stdout, "\n----\n\n"); +} + +int main(int argc, char *argv[]) +{ + if (argc > 1){ + cycles = atol(argv[1]); + } + + fprintf(stdout, "Cycles: %zu\n", cycles); + fprintf(stdout, "\n"); + + boxbox(); + cylcyl(); + boxcyl(); + + return 0; +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/bench2.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/bench2.c new file mode 100644 index 0000000..8fb29df --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/bench2.c @@ -0,0 +1,263 @@ +#define CU_ENABLE_TIMER +#include +#include +#include +#include +#include "support.h" + +TEST_SUITES { + TEST_SUITES_CLOSURE +}; + +static int bench_num = 1; +static size_t cycles = 10000; + +static void runBench(const void *o1, const void *o2, const ccd_t *ccd) +{ + ccd_real_t depth; + ccd_vec3_t dir, pos; + int res; + size_t i; + const struct timespec *timer; + + cuTimerStart(); + for (i = 0; i < cycles; i++){ + res = ccdMPRPenetration(o1, o2, ccd, &depth, &dir, &pos); + } + timer = cuTimerStop(); + fprintf(stdout, "%02d: %ld %ld\n", bench_num, + (long)timer->tv_sec, (long)timer->tv_nsec); + fflush(stdout); + + bench_num++; +} + +static void boxbox(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + ccd_vec3_t axis; + ccd_quat_t rot; + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + bench_num = 1; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0., 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 1., 1.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.2; box2.y = 0.5; box2.z = 1.; + box2.x = box2.y = box2.z = 1.; + + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 0., 0.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -1.3, 0., 0.); + + ccdVec3Set(&box2.pos, 0., 0., 0.); + runBench(&box1, &box2, &ccd); + runBench(&box2, &box1, &ccd); + + + fprintf(stdout, "\n----\n\n"); +} + +void cylcyl(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_CYL(cyl1); + CCD_CYL(cyl2); + ccd_vec3_t axis; + + cyl1.radius = 0.35; + cyl1.height = 0.5; + cyl2.radius = 0.5; + cyl2.height = 1.; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&cyl1.pos, 0.3, 0.1, 0.1); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0., 0., 0.); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, -0.2, 0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, 0.567, 1.2, 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + ccdVec3Set(&axis, -4.567, 1.2, 0.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + runBench(&cyl1, &cyl2, &ccd); + runBench(&cyl2, &cyl1, &ccd); + + fprintf(stdout, "\n----\n\n"); +} + +void boxcyl(void) +{ + fprintf(stdout, "%s:\n", __func__); + + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + ccd_vec3_t axis; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + runBench(&box, &cyl, &ccd); + runBench(&cyl, &box, &ccd); + + fprintf(stdout, "\n----\n\n"); +} + +int main(int argc, char *argv[]) +{ + if (argc > 1){ + cycles = atol(argv[1]); + } + + fprintf(stdout, "Cycles: %zu\n", cycles); + fprintf(stdout, "\n"); + + boxbox(); + cylcyl(); + boxcyl(); + + return 0; +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.c new file mode 100644 index 0000000..3dfc965 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.c @@ -0,0 +1,467 @@ +#include +#include + +#include +#include "support.h" +#include +#include +#include "common.h" + + +TEST(boxboxSetUp) +{ +} + +TEST(boxboxTearDown) +{ +} + +TEST(boxboxAlignedX) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + //ccd.max_iterations = 20; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } + + + box1.x = 0.1; + box1.y = 0.2; + box1.z = 0.1; + box2.x = 0.2; + box2.y = 0.1; + box2.z = 0.2; + + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.01; + } + + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., -0.1, 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } +} + +TEST(boxboxAlignedY) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, 0., -5., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[1] += 0.1; + } +} + +TEST(boxboxAlignedZ) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, 0., 0., -5.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[2] += 0.1; + } +} + + +TEST(boxboxRot) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t axis; + ccd_real_t angle; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., 0.5, 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i < 33 || i > 67){ + assertFalse(res); + }else if (i != 33 && i != 67){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } + + box1.x = 1; + box1.y = 1; + box1.z = 1; + box2.x = 1; + box2.y = 1; + box2.z = 1; + + ccdVec3Set(&box1.pos, -1.01, 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + + ccdVec3Set(&axis, 0., 1., 0.); + angle = 0.; + for (i = 0; i < 30; i++){ + res = ccdGJKIntersect(&box1, &box2, &ccd); + + if (i != 0 && i != 10 && i != 20){ + assertTrue(res); + }else{ + assertFalse(res); + } + + angle += M_PI / 20.; + ccdQuatSetAngleAxis(&box1.quat, angle, &axis); + } + +} + + + +static void pConf(ccd_box_t *box1, ccd_box_t *box2, const ccd_vec3_t *v) +{ + fprintf(stdout, "# box1.pos: [%lf %lf %lf]\n", + ccdVec3X(&box1->pos), ccdVec3Y(&box1->pos), ccdVec3Z(&box1->pos)); + fprintf(stdout, "# box1->quat: [%lf %lf %lf %lf]\n", + box1->quat.q[0], box1->quat.q[1], box1->quat.q[2], box1->quat.q[3]); + fprintf(stdout, "# box2->pos: [%lf %lf %lf]\n", + ccdVec3X(&box2->pos), ccdVec3Y(&box2->pos), ccdVec3Z(&box2->pos)); + fprintf(stdout, "# box2->quat: [%lf %lf %lf %lf]\n", + box2->quat.q[0], box2->quat.q[1], box2->quat.q[2], box2->quat.q[3]); + fprintf(stdout, "# sep: [%lf %lf %lf]\n", + ccdVec3X(v), ccdVec3Y(v), ccdVec3Z(v)); + fprintf(stdout, "#\n"); +} + +TEST(boxboxSeparate) +{ + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t sep, expsep, expsep2, axis; + + fprintf(stderr, "\n\n\n---- boxboxSeparate ----\n\n\n"); + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.2); + res = ccdGJKIntersect(&box1, &box2, &ccd); + assertTrue(res); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0.25, 0., 0.); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + ccdVec3Scale(&sep, -1.); + ccdVec3Add(&box1.pos, &sep); + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., 0.); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., -0.25); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0., 0., 0.); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., 1.); + ccdVec3Set(&expsep2, 0., 0., -1.); + assertTrue(ccdVec3Eq(&sep, &expsep) || ccdVec3Eq(&sep, &expsep2)); + + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + pConf(&box1, &box2, &sep); + + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + pConf(&box1, &box2, &sep); +} + + +#define TOSVT() \ + svtObjPen(&box1, &box2, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&box2.pos, &dir); \ + svtObjPen(&box1, &box2, stdout, "Pen 1", depth, &dir, &pos) + +TEST(boxboxPenetration) +{ + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t axis; + ccd_quat_t rot; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + fprintf(stderr, "\n\n\n---- boxboxPenetration ----\n\n\n"); + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&box2.pos, 0.1, 0., 0.); + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); <<< + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0.1, 0., 0.1); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&box2.pos, 0.1, 0., 0.); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 1., 1.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 7"); + //TOSVT(); <<< + + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.2; box2.y = 0.5; box2.z = 1.; + box2.x = box2.y = box2.z = 1.; + + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 0., 0.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -1.3, 0., 0.); + + ccdVec3Set(&box2.pos, 0., 0., 0.); + + res = ccdGJKPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 8"); + //TOSVT(); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.h new file mode 100644 index 0000000..8127c7c --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxbox.h @@ -0,0 +1,32 @@ +#ifndef BOX_BOX +#define BOX_BOX + +#include + +TEST(boxboxSetUp); +TEST(boxboxTearDown); + +TEST(boxboxAlignedX); +TEST(boxboxAlignedY); +TEST(boxboxAlignedZ); + +TEST(boxboxRot); + +TEST(boxboxSeparate); +TEST(boxboxPenetration); + +TEST_SUITE(TSBoxBox) { + TEST_ADD(boxboxSetUp), + + TEST_ADD(boxboxAlignedX), + TEST_ADD(boxboxAlignedY), + TEST_ADD(boxboxAlignedZ), + TEST_ADD(boxboxRot), + TEST_ADD(boxboxSeparate), + TEST_ADD(boxboxPenetration), + + TEST_ADD(boxboxTearDown), + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.c new file mode 100644 index 0000000..4a556cb --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.c @@ -0,0 +1,162 @@ +#include +#include "common.h" +#include +#include "support.h" + + +#define TOSVT() \ + svtObjPen(&box, &cyl, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&cyl.pos, &dir); \ + svtObjPen(&box, &cyl, stdout, "Pen 1", depth, &dir, &pos) + + +TEST(boxcylIntersect) +{ + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + int res; + ccd_vec3_t axis; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&cyl.pos, 0.1, 0., 0.); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + res = ccdGJKIntersect(&box, &cyl, &ccd); + assertTrue(res); +} + + +TEST(boxcylPenEPA) +{ + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + int res; + ccd_vec3_t axis; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&cyl.pos, 0.1, 0., 0.); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); <<< + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 7"); + //TOSVT(); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + res = ccdGJKPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 8"); + //TOSVT(); +} + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.h new file mode 100644 index 0000000..3d348d9 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/boxcyl.h @@ -0,0 +1,16 @@ +#ifndef TEST_BOXCYL_H +#define TEST_BOXCYL_H + +#include + +TEST(boxcylIntersect); +TEST(boxcylPenEPA); + +TEST_SUITE(TSBoxCyl){ + TEST_ADD(boxcylIntersect), + TEST_ADD(boxcylPenEPA), + + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/common.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/common.c new file mode 100644 index 0000000..eca7776 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/common.c @@ -0,0 +1,174 @@ +#include "common.h" +#include +#include +#include "support.h" + +static void svtCyl(ccd_cyl_t *c, FILE *out, const char *color, const char *name) +{ + ccd_vec3_t v[32]; + ccd_quat_t rot; + ccd_vec3_t axis, vpos, vpos2; + ccd_real_t angle, x, y; + unsigned int i; + + ccdVec3Set(&axis, 0., 0., 1.); + ccdVec3Set(&vpos, 0., c->radius, 0.); + angle = 0.; + for (i = 0; i < 16; i++){ + angle = (ccd_real_t)i * (2. * M_PI / 16.); + + ccdQuatSetAngleAxis(&rot, angle, &axis); + ccdVec3Copy(&vpos2, &vpos); + ccdQuatRotVec(&vpos2, &rot); + x = ccdVec3X(&vpos2); + y = ccdVec3Y(&vpos2); + + ccdVec3Set(&v[i], x, y, c->height / 2.); + ccdVec3Set(&v[i + 16], x, y, -c->height / 2.); + } + + for (i = 0; i < 32; i++){ + ccdQuatRotVec(&v[i], &c->quat); + ccdVec3Add(&v[i], &c->pos); + } + + fprintf(out, "-----\n"); + if (name) + fprintf(out, "Name: %s\n", name); + + fprintf(out, "Face color: %s\n", color); + fprintf(out, "Edge color: %s\n", color); + fprintf(out, "Point color: %s\n", color); + fprintf(out, "Points:\n"); + for (i = 0; i < 32; i++){ + fprintf(out, "%lf %lf %lf\n", ccdVec3X(&v[i]), ccdVec3Y(&v[i]), ccdVec3Z(&v[i])); + } + + fprintf(out, "Edges:\n"); + fprintf(out, "0 16\n"); + fprintf(out, "0 31\n"); + for (i = 1; i < 16; i++){ + fprintf(out, "0 %d\n", i); + fprintf(out, "16 %d\n", i + 16); + if (i != 0){ + fprintf(out, "%d %d\n", i - 1, i); + fprintf(out, "%d %d\n", i + 16 - 1, i + 16); + } + + fprintf(out, "%d %d\n", i, i + 16); + fprintf(out, "%d %d\n", i, i + 16 - 1); + } + + fprintf(out, "Faces:\n"); + for (i = 2; i < 16; i++){ + fprintf(out, "0 %d %d\n", i, i -1); + fprintf(out, "16 %d %d\n", i + 16, i + 16 -1); + + } + fprintf(out, "0 16 31\n"); + fprintf(out, "0 31 15\n"); + for (i = 1; i < 16; i++){ + fprintf(out, "%d %d %d\n", i, i + 16, i + 16 - 1); + fprintf(out, "%d %d %d\n", i, i + 16 - 1, i - 1); + } + fprintf(out, "-----\n"); +} + +static void svtBox(ccd_box_t *b, FILE *out, const char *color, const char *name) +{ + ccd_vec3_t v[8]; + size_t i; + + ccdVec3Set(&v[0], b->x * 0.5, b->y * 0.5, b->z * 0.5); + ccdVec3Set(&v[1], b->x * 0.5, b->y * -0.5, b->z * 0.5); + ccdVec3Set(&v[2], b->x * 0.5, b->y * 0.5, b->z * -0.5); + ccdVec3Set(&v[3], b->x * 0.5, b->y * -0.5, b->z * -0.5); + ccdVec3Set(&v[4], b->x * -0.5, b->y * 0.5, b->z * 0.5); + ccdVec3Set(&v[5], b->x * -0.5, b->y * -0.5, b->z * 0.5); + ccdVec3Set(&v[6], b->x * -0.5, b->y * 0.5, b->z * -0.5); + ccdVec3Set(&v[7], b->x * -0.5, b->y * -0.5, b->z * -0.5); + + for (i = 0; i < 8; i++){ + ccdQuatRotVec(&v[i], &b->quat); + ccdVec3Add(&v[i], &b->pos); + } + + fprintf(out, "-----\n"); + if (name) + fprintf(out, "Name: %s\n", name); + fprintf(out, "Face color: %s\n", color); + fprintf(out, "Edge color: %s\n", color); + fprintf(out, "Point color: %s\n", color); + fprintf(out, "Points:\n"); + for (i = 0; i < 8; i++){ + fprintf(out, "%lf %lf %lf\n", ccdVec3X(&v[i]), ccdVec3Y(&v[i]), ccdVec3Z(&v[i])); + } + + fprintf(out, "Edges:\n"); + fprintf(out, "0 1\n 0 2\n2 3\n3 1\n1 2\n6 2\n1 7\n1 5\n"); + fprintf(out, "5 0\n0 4\n4 2\n6 4\n6 5\n5 7\n6 7\n7 2\n7 3\n4 5\n"); + + fprintf(out, "Faces:\n"); + fprintf(out, "0 2 1\n1 2 3\n6 2 4\n4 2 0\n4 0 5\n5 0 1\n"); + fprintf(out, "5 1 7\n7 1 3\n6 4 5\n6 5 7\n2 6 7\n2 7 3\n"); + fprintf(out, "-----\n"); +} + + +void svtObj(void *_o, FILE *out, const char *color, const char *name) +{ + ccd_obj_t *o = (ccd_obj_t *)_o; + + if (o->type == CCD_OBJ_CYL){ + svtCyl((ccd_cyl_t *)o, out, color, name); + }else if (o->type == CCD_OBJ_BOX){ + svtBox((ccd_box_t *)o, out, color, name); + } +} + +void svtObjPen(void *o1, void *o2, + FILE *out, const char *name, + ccd_real_t depth, const ccd_vec3_t *dir, const ccd_vec3_t *pos) +{ + ccd_vec3_t sep; + char oname[500]; + + ccdVec3Copy(&sep, dir); + ccdVec3Scale(&sep, depth); + ccdVec3Add(&sep, pos); + + fprintf(out, "------\n"); + if (name) + fprintf(out, "Name: %s\n", name); + fprintf(out, "Point color: 0.1 0.1 0.9\n"); + fprintf(out, "Points:\n%lf %lf %lf\n", ccdVec3X(pos), ccdVec3Y(pos), ccdVec3Z(pos)); + fprintf(out, "------\n"); + fprintf(out, "Point color: 0.1 0.9 0.9\n"); + fprintf(out, "Edge color: 0.1 0.9 0.9\n"); + fprintf(out, "Points:\n%lf %lf %lf\n", ccdVec3X(pos), ccdVec3Y(pos), ccdVec3Z(pos)); + fprintf(out, "%lf %lf %lf\n", ccdVec3X(&sep), ccdVec3Y(&sep), ccdVec3Z(&sep)); + fprintf(out, "Edges: 0 1\n"); + + oname[0] = 0x0; + if (name) + sprintf(oname, "%s o1", name); + svtObj(o1, out, "0.9 0.1 0.1", oname); + + oname[0] = 0x0; + if (name) + sprintf(oname, "%s o1", name); + svtObj(o2, out, "0.1 0.9 0.1", oname); +} + + +void recPen(ccd_real_t depth, const ccd_vec3_t *dir, const ccd_vec3_t *pos, + FILE *out, const char *note) +{ + if (!note) + note = ""; + + fprintf(out, "# %s: depth: %lf\n", note, depth); + fprintf(out, "# %s: dir: [%lf %lf %lf]\n", note, ccdVec3X(dir), ccdVec3Y(dir), ccdVec3Z(dir)); + fprintf(out, "# %s: pos: [%lf %lf %lf]\n", note, ccdVec3X(pos), ccdVec3Y(pos), ccdVec3Z(pos)); + fprintf(out, "#\n"); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/common.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/common.h new file mode 100644 index 0000000..a4de4c2 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/common.h @@ -0,0 +1,14 @@ +#ifndef TEST_COMMON +#define TEST_COMMON + +#include +#include + +void svtObj(void *o, FILE *out, const char *color, const char *name); +void svtObjPen(void *o1, void *o2, + FILE *out, const char *name, + ccd_real_t depth, const ccd_vec3_t *dir, const ccd_vec3_t *pos); +void recPen(ccd_real_t depth, const ccd_vec3_t *dir, const ccd_vec3_t *pos, + FILE *out, const char *note); + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING.LESSER b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING.LESSER new file mode 100644 index 0000000..fc8a5de --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.am b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.am new file mode 100644 index 0000000..66d6daf --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.am @@ -0,0 +1,6 @@ +AM_CPPFLAGS = -DCU_ENABLE_TIMER + +check_LTLIBRARIES = libcu.la + +libcu_la_SOURCES = cu.c cu.h + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.in b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.in new file mode 100644 index 0000000..6c6a3a0 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/Makefile.in @@ -0,0 +1,587 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/testsuites/cu +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +libcu_la_LIBADD = +am_libcu_la_OBJECTS = cu.lo +libcu_la_OBJECTS = $(am_libcu_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libcu_la_SOURCES) +DIST_SOURCES = $(libcu_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/../depcomp \ + COPYING COPYING.LESSER +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_PRECISION = @CCD_PRECISION@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -DCU_ENABLE_TIMER +check_LTLIBRARIES = libcu.la +libcu_la_SOURCES = cu.c cu.h +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/testsuites/cu/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/testsuites/cu/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkLTLIBRARIES: + -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES) + @list='$(check_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libcu.la: $(libcu_la_OBJECTS) $(libcu_la_DEPENDENCIES) $(EXTRA_libcu_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libcu_la_OBJECTS) $(libcu_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cu.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkLTLIBRARIES clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-checkLTLIBRARIES clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.c new file mode 100644 index 0000000..61d0bdb --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.c @@ -0,0 +1,387 @@ +/*** + * CU - C unit testing framework + * --------------------------------- + * Copyright (c)2007,2008,2009 Daniel Fiser + * + * + * This file is part of CU. + * + * CU is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * CU 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "cu.h" + +/** Declared here, because I didn't find header file where it is declared */ +char *strsignal(int sig); + +const char *cu_current_test; +const char *cu_current_test_suite; +int cu_success_test_suites = 0; +int cu_fail_test_suites = 0; +int cu_success_tests = 0; +int cu_fail_tests = 0; +int cu_success_checks = 0; +int cu_fail_checks = 0; + +char cu_out_prefix[CU_OUT_PREFIX_LENGTH+1] = ""; + + +/* globally used file descriptor for reading/writing messages */ +int fd; + +/* indicate if test has failed */ +int test_failed; + +/* codes of messages */ +#define CHECK_FAILED '0' +#define CHECK_SUCCEED '1' +#define TEST_FAILED '2' +#define TEST_SUCCEED '3' +#define TEST_SUITE_FAILED '4' +#define TEST_SUITE_SUCCEED '5' +#define END '6' +#define TEST_NAME '7' + +/* predefined messages */ +#define MSG_CHECK_SUCCEED write(fd, "1\n", 2) +#define MSG_TEST_FAILED write(fd, "2\n", 2) +#define MSG_TEST_SUCCEED write(fd, "3\n", 2) +#define MSG_TEST_SUITE_FAILED write(fd, "4\n", 2) +#define MSG_TEST_SUITE_SUCCEED write(fd, "5\n", 2) +#define MSG_END write(fd, "6\n", 2) + +/* length of buffers */ +#define BUF_LEN 1000 +#define MSGBUF_LEN 300 + + +static void redirect_out_err(const char *testName); +static void close_out_err(void); +static void run_test_suite(const char *ts_name, cu_test_suite_t *ts); +static void receive_messages(void); + +static void cu_run_fork(const char *ts_name, cu_test_suite_t *test_suite); +static void cu_print_results(void); + +void cu_run(int argc, char *argv[]) +{ + cu_test_suites_t *tss; + int i; + char found = 0; + + if (argc > 1){ + for (i=1; i < argc; i++){ + tss = cu_test_suites; + while (tss->name != NULL && tss->test_suite != NULL){ + if (strcmp(argv[i], tss->name) == 0){ + found = 1; + cu_run_fork(tss->name, tss->test_suite); + break; + } + tss++; + } + + if (tss->name == NULL || tss->test_suite == NULL){ + fprintf(stderr, "ERROR: Could not find test suite '%s'\n", argv[i]); + } + } + + if (found == 1) + cu_print_results(); + + }else{ + tss = cu_test_suites; + while (tss->name != NULL && tss->test_suite != NULL){ + cu_run_fork(tss->name, tss->test_suite); + tss++; + } + cu_print_results(); + } + + +} + +static void cu_run_fork(const char *ts_name, cu_test_suite_t *ts) +{ + int pipefd[2]; + int pid; + int status; + + if (pipe(pipefd) == -1){ + perror("Pipe error"); + exit(-1); + } + + fprintf(stdout, " -> %s [IN PROGESS]\n", ts_name); + fflush(stdout); + + pid = fork(); + if (pid < 0){ + perror("Fork error"); + exit(-1); + } + + if (pid == 0){ + /* close read end of pipe */ + close(pipefd[0]); + + fd = pipefd[1]; + + /* run testsuite, messages go to fd */ + run_test_suite(ts_name, ts); + + MSG_END; + close(fd); + + /* stop process where running testsuite */ + exit(0); + }else{ + /* close write end of pipe */ + close(pipefd[1]); + + fd = pipefd[0]; + + /* receive and interpret all messages */ + receive_messages(); + + /* wait for children */ + wait(&status); + if (!WIFEXITED(status)){ /* if child process ends up abnormaly */ + if (WIFSIGNALED(status)){ + fprintf(stdout, "Test suite was terminated by signal %d (%s).\n", + WTERMSIG(status), strsignal(WTERMSIG(status))); + }else{ + fprintf(stdout, "Test suite terminated abnormaly!\n"); + } + + /* mark this test suite as failed, because it was terminated + * prematurely */ + cu_fail_test_suites++; + } + + close(fd); + + fprintf(stdout, " -> %s [DONE]\n\n", ts_name); + fflush(stdout); + } + +} + +static void run_test_suite(const char *ts_name, cu_test_suite_t *ts) +{ + int test_suite_failed = 0; + char buffer[MSGBUF_LEN]; + int len; + + /* set up current test suite name for later messaging... */ + cu_current_test_suite = ts_name; + + /* redirect stdout and stderr */ + redirect_out_err(cu_current_test_suite); + + while (ts->name != NULL && ts->func != NULL){ + test_failed = 0; + + /* set up name of test for later messaging */ + cu_current_test = ts->name; + + /* send message what test is currently running */ + len = snprintf(buffer, MSGBUF_LEN, "%c --> Running %s...\n", + TEST_NAME, cu_current_test); + write(fd, buffer, len); + + /* run test */ + (*(ts->func))(); + + if (test_failed){ + MSG_TEST_FAILED; + test_suite_failed = 1; + }else{ + MSG_TEST_SUCCEED; + } + + ts++; /* next test in test suite */ + } + + if (test_suite_failed){ + MSG_TEST_SUITE_FAILED; + }else{ + MSG_TEST_SUITE_SUCCEED; + } + + /* close redirected stdout and stderr */ + close_out_err(); +} + + +static void receive_messages(void) +{ + char buf[BUF_LEN]; /* buffer */ + int buf_len; /* how many chars stored in buf */ + char bufout[MSGBUF_LEN]; /* buffer which can be printed out */ + int bufout_len; + int state = 0; /* 0 - waiting for code, 1 - copy msg to stdout */ + int i; + int end = 0; /* end of messages? */ + + bufout_len = 0; + while((buf_len = read(fd, buf, BUF_LEN)) > 0 && !end){ + for (i=0; i < buf_len; i++){ + + /* Prepare message for printing out */ + if (state == 1 || state == 2){ + if (bufout_len < MSGBUF_LEN) + bufout[bufout_len++] = buf[i]; + } + + /* reset state on '\n' in msg */ + if (buf[i] == '\n'){ + /* copy messages out */ + if (state == 1) + write(1, bufout, bufout_len); + if (state == 2) + write(2, bufout, bufout_len); + + state = 0; + bufout_len = 0; + continue; + } + + if (state == 0){ + if (buf[i] == CHECK_FAILED){ + cu_fail_checks++; + state = 2; + }else if (buf[i] == TEST_NAME){ + state = 1; + }else if (buf[i] == CHECK_SUCCEED){ + cu_success_checks++; + }else if (buf[i] == TEST_FAILED){ + cu_fail_tests++; + }else if (buf[i] == TEST_SUCCEED){ + cu_success_tests++; + }else if (buf[i] == TEST_SUITE_FAILED){ + cu_fail_test_suites++; + }else if (buf[i] == TEST_SUITE_SUCCEED){ + cu_success_test_suites++; + }else if (buf[i] == END){ + end = 1; + break; + } + } + } + } +} + +void cu_success_assertation(void) +{ + MSG_CHECK_SUCCEED; +} + +void cu_fail_assertation(const char *file, int line, const char *msg) +{ + char buf[MSGBUF_LEN]; + int len; + + len = snprintf(buf, MSGBUF_LEN, "%c%s:%d (%s::%s) :: %s\n", + CHECK_FAILED, + file, line, cu_current_test_suite, cu_current_test, msg); + write(fd, buf, len); + + /* enable test_failed flag */ + test_failed = 1; +} + +static void cu_print_results(void) +{ + fprintf(stdout, "\n"); + fprintf(stdout, "==================================================\n"); + fprintf(stdout, "| | failed | succeed | total |\n"); + fprintf(stdout, "|------------------------------------------------|\n"); + fprintf(stdout, "| assertations: | %6d | %7d | %5d |\n", + cu_fail_checks, cu_success_checks, + cu_success_checks+cu_fail_checks); + fprintf(stdout, "| tests: | %6d | %7d | %5d |\n", + cu_fail_tests, cu_success_tests, + cu_success_tests+cu_fail_tests); + fprintf(stdout, "| tests suites: | %6d | %7d | %5d |\n", + cu_fail_test_suites, cu_success_test_suites, + cu_success_test_suites+cu_fail_test_suites); + fprintf(stdout, "==================================================\n"); +} + +void cu_set_out_prefix(const char *str) +{ + strncpy(cu_out_prefix, str, CU_OUT_PREFIX_LENGTH); +} + +static void redirect_out_err(const char *test_name) +{ + char buf[100]; + + snprintf(buf, 99, "%stmp.%s.out", cu_out_prefix, test_name); + if (freopen(buf, "w", stdout) == NULL){ + perror("Redirecting of stdout failed"); + exit(-1); + } + + snprintf(buf, 99, "%stmp.%s.err", cu_out_prefix, test_name); + if (freopen(buf, "w", stderr) == NULL){ + perror("Redirecting of stderr failed"); + exit(-1); + } +} + +static void close_out_err(void) +{ + fclose(stdout); + fclose(stderr); +} + + +#ifdef CU_ENABLE_TIMER +/* global variables for timer functions */ +struct timespec __cu_timer; +static struct timespec __cu_timer_start, __cu_timer_stop; + +const struct timespec *cuTimer(void) +{ + return &__cu_timer; +} + +void cuTimerStart(void) +{ + clock_gettime(CLOCK_MONOTONIC, &__cu_timer_start); +} + +const struct timespec *cuTimerStop(void) +{ + clock_gettime(CLOCK_MONOTONIC, &__cu_timer_stop); + + /* store into t difference between time_start and time_end */ + if (__cu_timer_stop.tv_nsec > __cu_timer_start.tv_nsec){ + __cu_timer.tv_nsec = __cu_timer_stop.tv_nsec - __cu_timer_start.tv_nsec; + __cu_timer.tv_sec = __cu_timer_stop.tv_sec - __cu_timer_start.tv_sec; + }else{ + __cu_timer.tv_nsec = __cu_timer_stop.tv_nsec + 1000000000L - __cu_timer_start.tv_nsec; + __cu_timer.tv_sec = __cu_timer_stop.tv_sec - 1 - __cu_timer_start.tv_sec; + } + + return &__cu_timer; +} +#endif /* CU_ENABLE_TIMER */ diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.h new file mode 100644 index 0000000..77af63f --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cu/cu.h @@ -0,0 +1,164 @@ +/*** + * CU - C unit testing framework + * --------------------------------- + * Copyright (c)2007,2008,2009 Daniel Fiser + * + * + * This file is part of CU. + * + * CU is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or (at your option) any later version. + * + * CU 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef _CU_H_ +#define _CU_H_ + +#ifdef CU_ENABLE_TIMER +# include +#endif /* CU_ENABLE_TIMER */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/***** PUBLIC API *****/ +/** + * Define test + */ +#define TEST(name) \ + void name(void) + +/** + * Define testsuite + */ +#define TEST_SUITE(name) \ + cu_test_suite_t test_suite_##name[] = +/** + * Must be on the end of list of tests. + */ +#define TEST_SUITE_CLOSURE \ + { NULL, NULL } + +#define TEST_SUITES \ + cu_test_suites_t cu_test_suites[] = +#define TEST_SUITES_CLOSURE \ + { NULL, NULL } +#define TEST_SUITE_ADD(name) \ + { #name, test_suite_##name } + +/** + * Add test to testsuite + */ +#define TEST_ADD(name) \ + { #name, name } + +#define CU_RUN(argc, argv) \ + cu_run(argc, argv) + +/** + * Set prefix for files printed out. Must contain trailing /. + */ +#define CU_SET_OUT_PREFIX(str) \ + cu_set_out_prefix(str) + +/** + * Assertations + * Assertations with suffix 'M' (e.g. assertTrueM) are variations of macros + * where it is possible to specify error message. + */ +#define assertTrueM(a, message) \ + if (a){ \ + cu_success_assertation(); \ + }else{ \ + cu_fail_assertation(__FILE__, __LINE__, message); \ + } +#define assertTrue(a) \ + assertTrueM((a), #a " is not true") + +#define assertFalseM(a, message) \ + assertTrueM(!(a), message) +#define assertFalse(a) \ + assertFalseM((a), #a " is not false") + +#define assertEqualsM(a,b,message) \ + assertTrueM((a) == (b), message) +#define assertEquals(a,b) \ + assertEqualsM((a), (b), #a " not equals " #b) + +#define assertNotEqualsM(a,b,message) \ + assertTrueM((a) != (b), message) +#define assertNotEquals(a,b) \ + assertNotEqualsM((a), (b), #a " equals " #b) +/***** PUBLIC API END *****/ + + +#include + +#define CU_MAX_NAME_LENGTH 30 + +typedef void (*cu_test_func_t)(void); +typedef struct _cu_test_suite_t { + const char *name; + cu_test_func_t func; +} cu_test_suite_t; +typedef struct _cu_test_suites_t { + const char *name; + cu_test_suite_t *test_suite; +} cu_test_suites_t; + +extern cu_test_suites_t cu_test_suites[]; + +extern const char *cu_current_test; +extern const char *cu_current_test_suite; + +extern int cu_success_test_suites; +extern int cu_fail_test_suites; +extern int cu_success_tests; +extern int cu_fail_tests; +extern int cu_success_checks; +extern int cu_fail_checks; + +#define CU_OUT_PREFIX_LENGTH 30 +extern char cu_out_prefix[CU_OUT_PREFIX_LENGTH+1]; + +void cu_run(int argc, char *argv[]); +void cu_success_assertation(void); +void cu_fail_assertation(const char *file, int line, const char *msg); +void cu_set_out_prefix(const char *str); + +/** Timer **/ +#ifdef CU_ENABLE_TIMER +extern struct timespec __cu_timer; + +/** + * Returns value of timer. (as timespec struct) + */ +const struct timespec *cuTimer(void); + +/** + * Starts timer. + */ +void cuTimerStart(void); + +/** + * Stops timer and record elapsed time from last call of cuTimerStart(). + * Returns current value of timer. + */ +const struct timespec *cuTimerStop(void); +#endif /* CU_ENABLE_TIMER */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.c new file mode 100644 index 0000000..6cd2124 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include "support.h" +#include "common.h" + + +TEST(cylcylSetUp) +{ +} + +TEST(cylcylTearDown) +{ +} + + +TEST(cylcylAlignedX) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, -5., 0., 0.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&c1, &c2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[0] += 0.1; + } +} + +TEST(cylcylAlignedY) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, 0., -5., 0.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&c1, &c2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[1] += 0.1; + } +} + +TEST(cylcylAlignedZ) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, 0., 0., -5.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&c1, &c2, &ccd); + + if (i < 43 || i > 57){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[2] += 0.1; + } +} + +#define TOSVT() \ + svtObjPen(&cyl1, &cyl2, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&cyl2.pos, &dir); \ + svtObjPen(&cyl1, &cyl2, stdout, "Pen 1", depth, &dir, &pos) + +TEST(cylcylPenetrationEPA) +{ + ccd_t ccd; + CCD_CYL(cyl1); + CCD_CYL(cyl2); + int res; + ccd_vec3_t axis; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + fprintf(stderr, "\n\n\n---- cylcylPenetration ----\n\n\n"); + + cyl1.radius = 0.35; + cyl1.height = 0.5; + cyl2.radius = 0.5; + cyl2.height = 1.; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&cyl2.pos, 0., 0., 0.3); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + ccdVec3Set(&cyl1.pos, 0.3, 0.1, 0.1); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); <<< + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0., 0., 0.); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, -0.2, 0.7, 0.2); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + ccdVec3Set(&axis, 0.567, 1.2, 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + ccdVec3Set(&axis, -4.567, 1.2, 0.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + res = ccdGJKPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.h new file mode 100644 index 0000000..8cbbe07 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/cylcyl.h @@ -0,0 +1,29 @@ +#ifndef CYL_CYL +#define CYL_CYL + +#include + +TEST(cylcylSetUp); +TEST(cylcylTearDown); + +TEST(cylcylAlignedX); +TEST(cylcylAlignedY); +TEST(cylcylAlignedZ); + +TEST(cylcylPenetrationEPA); + +TEST_SUITE(TSCylCyl) { + TEST_ADD(cylcylSetUp), + + TEST_ADD(cylcylAlignedX), + TEST_ADD(cylcylAlignedY), + TEST_ADD(cylcylAlignedZ), + + TEST_ADD(cylcylPenetrationEPA), + + TEST_ADD(cylcylTearDown), + TEST_SUITE_CLOSURE +}; + +#endif + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/main.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/main.c new file mode 100644 index 0000000..a4585b0 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/main.c @@ -0,0 +1,32 @@ +#include "vec3.h" +#include "polytope.h" +#include "boxbox.h" +#include "spheresphere.h" +#include "cylcyl.h" +#include "boxcyl.h" + +#include "mpr_boxbox.h" +#include "mpr_cylcyl.h" +#include "mpr_boxcyl.h" + +TEST_SUITES { + TEST_SUITE_ADD(TSVec3), + TEST_SUITE_ADD(TSPt), + TEST_SUITE_ADD(TSBoxBox), + TEST_SUITE_ADD(TSSphereSphere), + TEST_SUITE_ADD(TSCylCyl), + TEST_SUITE_ADD(TSBoxCyl), + + TEST_SUITE_ADD(TSMPRBoxBox), + TEST_SUITE_ADD(TSMPRCylCyl), + TEST_SUITE_ADD(TSMPRBoxCyl), + + TEST_SUITES_CLOSURE +}; +int main(int argc, char *argv[]) +{ + CU_SET_OUT_PREFIX("regressions/"); + CU_RUN(argc, argv); + + return 0; +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.c new file mode 100644 index 0000000..2342850 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.c @@ -0,0 +1,500 @@ +#include +#include + +#include +#include "support.h" +#include +#include +#include "common.h" + + +TEST(mprBoxboxAlignedX) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } + + box1.x = 0.1; + box1.y = 0.2; + box1.z = 0.1; + box2.x = 0.2; + box2.y = 0.1; + box2.z = 0.2; + + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.01; + } + + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., -0.1, 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } +} + +TEST(mprBoxboxAlignedY) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, 0., -5., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[1] += 0.1; + } +} + +TEST(mprBoxboxAlignedZ) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, 0., 0., -5.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i < 35 || i > 65){ + assertFalse(res); + }else if (i != 35 && i != 65){ + assertTrue(res); + } + + box1.pos.v[2] += 0.1; + } +} + + +TEST(mprBoxboxRot) +{ + size_t i; + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t axis; + ccd_real_t angle; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + box1.x = 1; + box1.y = 2; + box1.z = 1; + box2.x = 2; + box2.y = 1; + box2.z = 2; + + ccdVec3Set(&box1.pos, -5., 0.5, 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i < 33 || i > 67){ + assertFalse(res); + }else if (i != 33 && i != 67){ + assertTrue(res); + } + + box1.pos.v[0] += 0.1; + } + + box1.x = 1; + box1.y = 1; + box1.z = 1; + box2.x = 1; + box2.y = 1; + box2.z = 1; + + ccdVec3Set(&box1.pos, -1.01, 0., 0.); + ccdVec3Set(&box2.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + + ccdVec3Set(&axis, 0., 1., 0.); + angle = 0.; + for (i = 0; i < 30; i++){ + res = ccdMPRIntersect(&box1, &box2, &ccd); + + if (i != 0 && i != 10 && i != 20){ + assertTrue(res); + }else{ + assertFalse(res); + } + + angle += M_PI / 20.; + ccdQuatSetAngleAxis(&box1.quat, angle, &axis); + } + +} + + + +static void pConf(ccd_box_t *box1, ccd_box_t *box2, const ccd_vec3_t *v) +{ + fprintf(stdout, "# box1.pos: [%lf %lf %lf]\n", + ccdVec3X(&box1->pos), ccdVec3Y(&box1->pos), ccdVec3Z(&box1->pos)); + fprintf(stdout, "# box1->quat: [%lf %lf %lf %lf]\n", + box1->quat.q[0], box1->quat.q[1], box1->quat.q[2], box1->quat.q[3]); + fprintf(stdout, "# box2->pos: [%lf %lf %lf]\n", + ccdVec3X(&box2->pos), ccdVec3Y(&box2->pos), ccdVec3Z(&box2->pos)); + fprintf(stdout, "# box2->quat: [%lf %lf %lf %lf]\n", + box2->quat.q[0], box2->quat.q[1], box2->quat.q[2], box2->quat.q[3]); + fprintf(stdout, "# sep: [%lf %lf %lf]\n", + ccdVec3X(v), ccdVec3Y(v), ccdVec3Z(v)); + fprintf(stdout, "#\n"); +} + +TEST(mprBoxboxSeparate) +{ + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t sep, expsep, expsep2, axis; + + fprintf(stderr, "\n\n\n---- boxboxSeparate ----\n\n\n"); + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.2); + res = ccdMPRIntersect(&box1, &box2, &ccd); + assertTrue(res); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0.25, 0., 0.); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + ccdVec3Scale(&sep, -1.); + ccdVec3Add(&box1.pos, &sep); + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., 0.); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., -0.25); + assertTrue(ccdVec3Eq(&sep, &expsep)); + + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0., 0., 0.); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + ccdVec3Set(&expsep, 0., 0., 1.); + ccdVec3Set(&expsep2, 0., 0., -1.); + assertTrue(ccdVec3Eq(&sep, &expsep) || ccdVec3Eq(&sep, &expsep2)); + + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + pConf(&box1, &box2, &sep); + + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdGJKSeparate(&box1, &box2, &ccd, &sep); + assertTrue(res == 0); + pConf(&box1, &box2, &sep); +} + + +#define TOSVT() \ + svtObjPen(&box1, &box2, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&box2.pos, &dir); \ + svtObjPen(&box1, &box2, stdout, "Pen 1", depth, &dir, &pos) + +TEST(mprBoxboxPenetration) +{ + ccd_t ccd; + CCD_BOX(box1); + CCD_BOX(box2); + int res; + ccd_vec3_t axis; + ccd_quat_t rot; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + fprintf(stderr, "\n\n\n---- boxboxPenetration ----\n\n\n"); + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; + box2.y = 1.; + box2.z = 1.5; + + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + /* + ccdVec3Set(&box2.pos, 0., 0., 0.); + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + TOSVT(); + */ + + ccdVec3Set(&box2.pos, 0.1, 0., 0.); + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + + ccdVec3Set(&box1.pos, -0.3, 0.5, 1.); + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, 0.1, 0., 0.1); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0., 0.); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.5, 0.); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = box2.y = box2.z = 1.; + ccdVec3Set(&box2.pos, 0.1, 0., 0.); + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 1., 1.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -0.5, 0.1, 0.4); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 7"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.2; box2.y = 0.5; box2.z = 1.; + box2.x = box2.y = box2.z = 1.; + + ccdVec3Set(&axis, 0., 0., 1.); + ccdQuatSetAngleAxis(&box1.quat, M_PI / 4., &axis); + ccdVec3Set(&axis, 1., 0., 0.); + ccdQuatSetAngleAxis(&rot, M_PI / 4., &axis); + ccdQuatMul(&box1.quat, &rot); + ccdVec3Set(&box1.pos, -1.3, 0., 0.); + + ccdVec3Set(&box2.pos, 0., 0., 0.); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 8"); + //TOSVT(); + + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; box2.y = 0.5; box2.z = .5; + ccdVec3Set(&box1.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdVec3Set(&box2.pos, 0., 0.73, 0.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 9"); + //TOSVT(); + + box1.x = box1.y = box1.z = 1.; + box2.x = 0.5; box2.y = 0.5; box2.z = .5; + ccdVec3Set(&box1.pos, 0., 0., 0.); + ccdQuatSet(&box1.quat, 0., 0., 0., 1.); + ccdVec3Set(&box2.pos, 0.3, 0.738, 0.); + ccdQuatSet(&box2.quat, 0., 0., 0., 1.); + + res = ccdMPRPenetration(&box1, &box2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 10"); + //TOSVT(); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.h new file mode 100644 index 0000000..e6922aa --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxbox.h @@ -0,0 +1,26 @@ +#ifndef MPR_BOX_BOX +#define MPR_BOX_BOX + +#include + +TEST(mprBoxboxAlignedX); +TEST(mprBoxboxAlignedY); +TEST(mprBoxboxAlignedZ); + +TEST(mprBoxboxRot); + +TEST(mprBoxboxSeparate); +TEST(mprBoxboxPenetration); + +TEST_SUITE(TSMPRBoxBox) { + TEST_ADD(mprBoxboxAlignedX), + TEST_ADD(mprBoxboxAlignedY), + TEST_ADD(mprBoxboxAlignedZ), + TEST_ADD(mprBoxboxRot), + //TEST_ADD(mprBoxboxSeparate), + TEST_ADD(mprBoxboxPenetration), + + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.c new file mode 100644 index 0000000..7a1b7fa --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.c @@ -0,0 +1,165 @@ +#include +#include "common.h" +#include +#include "support.h" + +#define TOSVT() \ + svtObjPen(&box, &cyl, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&cyl.pos, &dir); \ + svtObjPen(&box, &cyl, stdout, "Pen 1", depth, &dir, &pos) + +TEST(mprBoxcylIntersect) +{ + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + int res; + ccd_vec3_t axis; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + ccdVec3Set(&cyl.pos, 0.1, 0., 0.); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + res = ccdMPRIntersect(&box, &cyl, &ccd); + assertTrue(res); +} + + + +TEST(mprBoxcylPen) +{ + ccd_t ccd; + CCD_BOX(box); + CCD_CYL(cyl); + int res; + ccd_vec3_t axis; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + box.x = 0.5; + box.y = 1.; + box.z = 1.5; + cyl.radius = 0.4; + cyl.height = 0.7; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + ccdVec3Set(&cyl.pos, 0.1, 0., 0.); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + ccdVec3Set(&cyl.pos, .6, 0., 0.); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + ccdVec3Set(&axis, 0., 1., 0.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl.pos, .6, 0.6, 0.5); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + ccdVec3Set(&axis, 0.67, 1.1, 0.12); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .6, 0., 0.5); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 7"); + //TOSVT(); + + ccdVec3Set(&axis, -0.1, 2.2, -1.); + ccdQuatSetAngleAxis(&cyl.quat, M_PI / 5., &axis); + ccdVec3Set(&cyl.pos, .6, 0., 0.5); + ccdVec3Set(&axis, 1., 1., 0.); + ccdQuatSetAngleAxis(&box.quat, -M_PI / 4., &axis); + ccdVec3Set(&box.pos, .9, 0.8, 0.5); + res = ccdMPRPenetration(&box, &cyl, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 8"); + //TOSVT(); +} + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.h new file mode 100644 index 0000000..86f14e6 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_boxcyl.h @@ -0,0 +1,16 @@ +#ifndef MPR_TEST_BOXCYL_H +#define MPR_TEST_BOXCYL_H + +#include + +TEST(mprBoxcylIntersect); +TEST(mprBoxcylPen); + +TEST_SUITE(TSMPRBoxCyl){ + TEST_ADD(mprBoxcylIntersect), + TEST_ADD(mprBoxcylPen), + + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.c new file mode 100644 index 0000000..ec0a3bc --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include "support.h" +#include "common.h" + + +TEST(mprCylcylAlignedX) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, -5., 0., 0.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&c1, &c2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[0] += 0.1; + } +} + +TEST(mprCylcylAlignedY) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, 0., -5., 0.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&c1, &c2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[1] += 0.1; + } +} + +TEST(mprCylcylAlignedZ) +{ + ccd_t ccd; + CCD_CYL(c1); + CCD_CYL(c2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + c1.radius = 0.35; + c1.height = 0.5; + c2.radius = 0.5; + c2.height = 1.; + + ccdVec3Set(&c1.pos, 0., 0., -5.); + for (i = 0; i < 100; i++){ + res = ccdMPRIntersect(&c1, &c2, &ccd); + + if (i < 43 || i > 57){ + assertFalse(res); + }else{ + assertTrue(res); + } + + c1.pos.v[2] += 0.1; + } +} + +#define TOSVT() \ + svtObjPen(&cyl1, &cyl2, stdout, "Pen 1", depth, &dir, &pos); \ + ccdVec3Scale(&dir, depth); \ + ccdVec3Add(&cyl2.pos, &dir); \ + svtObjPen(&cyl1, &cyl2, stdout, "Pen 1", depth, &dir, &pos) + +TEST(mprCylcylPenetration) +{ + ccd_t ccd; + CCD_CYL(cyl1); + CCD_CYL(cyl2); + int res; + ccd_vec3_t axis; + ccd_real_t depth; + ccd_vec3_t dir, pos; + + fprintf(stderr, "\n\n\n---- mprCylcylPenetration ----\n\n\n"); + + cyl1.radius = 0.35; + cyl1.height = 0.5; + cyl2.radius = 0.5; + cyl2.height = 1.; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + ccd.center1 = ccdObjCenter; + ccd.center2 = ccdObjCenter; + + ccdVec3Set(&cyl2.pos, 0., 0., 0.3); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 1"); + //TOSVT(); + + ccdVec3Set(&cyl1.pos, 0.3, 0.1, 0.1); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 2"); + //TOSVT(); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0., 0., 0.); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 3"); + //TOSVT(); + + ccdVec3Set(&axis, 0., 1., 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, -0.2, 0.7, 0.2); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 4"); + //TOSVT(); + + ccdVec3Set(&axis, 0.567, 1.2, 1.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 4., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 5"); + //TOSVT(); + + ccdVec3Set(&axis, -4.567, 1.2, 0.); + ccdQuatSetAngleAxis(&cyl2.quat, M_PI / 3., &axis); + ccdVec3Set(&cyl2.pos, 0.6, -0.7, 0.2); + res = ccdMPRPenetration(&cyl1, &cyl2, &ccd, &depth, &dir, &pos); + assertTrue(res == 0); + recPen(depth, &dir, &pos, stdout, "Pen 6"); + //TOSVT(); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.h new file mode 100644 index 0000000..2d2162b --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/mpr_cylcyl.h @@ -0,0 +1,23 @@ +#ifndef MPR_CYL_CYL +#define MPR_CYL_CYL + +#include + +TEST(mprCylcylAlignedX); +TEST(mprCylcylAlignedY); +TEST(mprCylcylAlignedZ); + +TEST(mprCylcylPenetration); + +TEST_SUITE(TSMPRCylCyl) { + TEST_ADD(mprCylcylAlignedX), + TEST_ADD(mprCylcylAlignedY), + TEST_ADD(mprCylcylAlignedZ), + + TEST_ADD(mprCylcylPenetration), + + TEST_SUITE_CLOSURE +}; + +#endif + diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.c new file mode 100644 index 0000000..65686fb --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.c @@ -0,0 +1,398 @@ +//#undef NDEBUG +#include +#include +#include + +TEST(ptSetUp) +{ +} + +TEST(ptTearDown) +{ +} + + +TEST(ptCreate1) +{ + ccd_pt_t pt; + ccd_pt_vertex_t *v[3]; + ccd_pt_edge_t *e[3]; + ccd_pt_face_t *f; + ccd_vec3_t u; + int res; + size_t i; + + DBG2("------"); + + ccdPtInit(&pt); + ccdPtDestroy(&pt); + + + ccdPtInit(&pt); + + ccdVec3Set(&u, -1., -1., 0.); + v[0] = ccdPtAddVertexCoords(&pt, -1., -1., 0.); + assertTrue(ccdVec3Eq(&u, &v[0]->v.v)); + + ccdVec3Set(&u, 1., 0., 0.); + v[1] = ccdPtAddVertexCoords(&pt, 1., 0., 0.); + assertTrue(ccdVec3Eq(&u, &v[1]->v.v)); + + ccdVec3Set(&u, 0., 0., 1.); + v[2] = ccdPtAddVertexCoords(&pt, 0., 0., 1.); + assertTrue(ccdVec3Eq(&u, &v[2]->v.v)); + + for (i = 0; i < 3; i++){ + assertTrue(ccdEq(v[i]->dist, ccdVec3Len2(&v[i]->v.v))); + } + + + e[0] = ccdPtAddEdge(&pt, v[0], v[1]); + e[1] = ccdPtAddEdge(&pt, v[1], v[2]); + e[2] = ccdPtAddEdge(&pt, v[2], v[0]); + for (i = 0; i < 3; i++){ + DBG("e[%d]->dist: %lf", i, e[i]->dist); + DBG_VEC3(&e[i]->witness, " ->witness: "); + } + + f = ccdPtAddFace(&pt, e[0], e[1], e[2]); + DBG("f->dist: %lf", f->dist); + DBG_VEC3(&f->witness, " ->witness: "); + + for (i = 0; i < 3; i++){ + res = ccdPtDelVertex(&pt, v[i]); + assertFalse(res == 0); + res = ccdPtDelEdge(&pt, e[i]); + assertFalse(res == 0); + } + + ccdPtDelFace(&pt, f); + for (i = 0; i < 3; i++){ + res = ccdPtDelVertex(&pt, v[i]); + assertFalse(res == 0); + } + for (i = 0; i < 3; i++){ + res = ccdPtDelEdge(&pt, e[i]); + assertTrue(res == 0); + } + for (i = 0; i < 3; i++){ + res = ccdPtDelVertex(&pt, v[i]); + assertTrue(res == 0); + } + + v[0] = ccdPtAddVertexCoords(&pt, -1., -1., 0.); + v[1] = ccdPtAddVertexCoords(&pt, 1., 0., 0.); + v[2] = ccdPtAddVertexCoords(&pt, 0., 0., 1.); + + e[0] = ccdPtAddEdge(&pt, v[0], v[1]); + e[1] = ccdPtAddEdge(&pt, v[1], v[2]); + e[2] = ccdPtAddEdge(&pt, v[2], v[0]); + + f = ccdPtAddFace(&pt, e[0], e[1], e[2]); + + ccdPtDestroy(&pt); +} + +TEST(ptCreate2) +{ + ccd_pt_t pt; + ccd_pt_vertex_t *v[4]; + ccd_pt_edge_t *e[6]; + ccd_pt_face_t *f[4]; + ccd_vec3_t u; + int res; + unsigned int i; + + DBG2("------"); + + ccdPtInit(&pt); + + ccdVec3Set(&u, -1., -1., 0.); + v[0] = ccdPtAddVertexCoords(&pt, -1., -1., 0.); + assertTrue(ccdVec3Eq(&u, &v[0]->v.v)); + + ccdVec3Set(&u, 1., 0., 0.); + v[1] = ccdPtAddVertexCoords(&pt, 1., 0., 0.); + assertTrue(ccdVec3Eq(&u, &v[1]->v.v)); + + ccdVec3Set(&u, 0., 0., 1.); + v[2] = ccdPtAddVertexCoords(&pt, 0., 0., 1.); + assertTrue(ccdVec3Eq(&u, &v[2]->v.v)); + + ccdVec3Set(&u, 0., 1., 0.); + v[3] = ccdPtAddVertexCoords(&pt, 0., 1., 0.); + assertTrue(ccdVec3Eq(&u, &v[3]->v.v)); + + for (i = 0; i < 4; i++){ + assertTrue(ccdEq(v[i]->dist, ccdVec3Len2(&v[i]->v.v))); + } + for (i = 0; i < 4; i++){ + DBG("v[%d]->dist: %lf", i, v[i]->dist); + DBG_VEC3(&v[i]->witness, " ->witness: "); + } + + e[0] = ccdPtAddEdge(&pt, v[0], v[1]); + e[1] = ccdPtAddEdge(&pt, v[1], v[2]); + e[2] = ccdPtAddEdge(&pt, v[2], v[0]); + e[3] = ccdPtAddEdge(&pt, v[3], v[0]); + e[4] = ccdPtAddEdge(&pt, v[3], v[1]); + e[5] = ccdPtAddEdge(&pt, v[3], v[2]); + for (i = 0; i < 6; i++){ + DBG("e[%d]->dist: %lf", i, e[i]->dist); + DBG_VEC3(&e[i]->witness, " ->witness: "); + } + + f[0] = ccdPtAddFace(&pt, e[0], e[1], e[2]); + f[1] = ccdPtAddFace(&pt, e[3], e[4], e[0]); + f[2] = ccdPtAddFace(&pt, e[4], e[5], e[1]); + f[3] = ccdPtAddFace(&pt, e[5], e[3], e[2]); + for (i = 0; i < 4; i++){ + DBG("f[%d]->dist: %lf", i, f[i]->dist); + DBG_VEC3(&f[i]->witness, " ->witness: "); + } + + for (i = 0; i < 4; i++){ + res = ccdPtDelVertex(&pt, v[i]); + assertFalse(res == 0); + } + for (i = 0; i < 6; i++){ + res = ccdPtDelEdge(&pt, e[i]); + assertFalse(res == 0); + } + + res = ccdPtDelFace(&pt, f[0]); + for (i = 0; i < 6; i++){ + res = ccdPtDelEdge(&pt, e[i]); + assertFalse(res == 0); + } + + res = ccdPtDelFace(&pt, f[1]); + assertTrue(ccdPtDelEdge(&pt, e[0]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[1]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[2]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[3]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[4]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[5]) == 0); + for (i = 0; i < 4; i++){ + res = ccdPtDelVertex(&pt, v[i]); + assertFalse(res == 0); + } + + res = ccdPtDelFace(&pt, f[2]); + assertTrue(ccdPtDelEdge(&pt, e[1]) == 0); + assertTrue(ccdPtDelEdge(&pt, e[4]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[2]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[3]) == 0); + assertFalse(ccdPtDelEdge(&pt, e[5]) == 0); + + assertTrue(ccdPtDelVertex(&pt, v[1]) == 0); + assertFalse(ccdPtDelVertex(&pt, v[0]) == 0); + assertFalse(ccdPtDelVertex(&pt, v[2]) == 0); + assertFalse(ccdPtDelVertex(&pt, v[3]) == 0); + + res = ccdPtDelFace(&pt, f[3]); + assertTrue(ccdPtDelEdge(&pt, e[2]) == 0); + assertTrue(ccdPtDelEdge(&pt, e[3]) == 0); + assertTrue(ccdPtDelEdge(&pt, e[5]) == 0); + + assertTrue(ccdPtDelVertex(&pt, v[0]) == 0); + assertTrue(ccdPtDelVertex(&pt, v[2]) == 0); + assertTrue(ccdPtDelVertex(&pt, v[3]) == 0); + + + v[0] = ccdPtAddVertexCoords(&pt, -1., -1., 0.); + v[1] = ccdPtAddVertexCoords(&pt, 1., 0., 0.); + v[2] = ccdPtAddVertexCoords(&pt, 0., 0., 1.); + v[3] = ccdPtAddVertexCoords(&pt, 0., 1., 0.); + + e[0] = ccdPtAddEdge(&pt, v[0], v[1]); + e[1] = ccdPtAddEdge(&pt, v[1], v[2]); + e[2] = ccdPtAddEdge(&pt, v[2], v[0]); + e[3] = ccdPtAddEdge(&pt, v[3], v[0]); + e[4] = ccdPtAddEdge(&pt, v[3], v[1]); + e[5] = ccdPtAddEdge(&pt, v[3], v[2]); + + f[0] = ccdPtAddFace(&pt, e[0], e[1], e[2]); + f[1] = ccdPtAddFace(&pt, e[3], e[4], e[0]); + f[2] = ccdPtAddFace(&pt, e[4], e[5], e[1]); + f[3] = ccdPtAddFace(&pt, e[5], e[3], e[2]); + + ccdPtDestroy(&pt); +} + +TEST(ptNearest) +{ + ccd_pt_t pt; + ccd_pt_vertex_t *v[4]; + ccd_pt_edge_t *e[6]; + ccd_pt_face_t *f[4]; + ccd_pt_el_t *nearest; + + DBG2("------"); + + ccdPtInit(&pt); + + v[0] = ccdPtAddVertexCoords(&pt, -1., -1., 0.); + v[1] = ccdPtAddVertexCoords(&pt, 1., 0., 0.); + v[2] = ccdPtAddVertexCoords(&pt, 0., 0., 1.); + v[3] = ccdPtAddVertexCoords(&pt, 0., 1., 0.); + + e[0] = ccdPtAddEdge(&pt, v[0], v[1]); + e[1] = ccdPtAddEdge(&pt, v[1], v[2]); + e[2] = ccdPtAddEdge(&pt, v[2], v[0]); + e[3] = ccdPtAddEdge(&pt, v[3], v[0]); + e[4] = ccdPtAddEdge(&pt, v[3], v[1]); + e[5] = ccdPtAddEdge(&pt, v[3], v[2]); + + f[0] = ccdPtAddFace(&pt, e[0], e[1], e[2]); + f[1] = ccdPtAddFace(&pt, e[3], e[4], e[0]); + f[2] = ccdPtAddFace(&pt, e[4], e[5], e[1]); + f[3] = ccdPtAddFace(&pt, e[5], e[3], e[2]); + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_FACE); + assertEquals(nearest, (ccd_pt_el_t *)f[1]); + assertTrue(ccdPtDelFace(&pt, f[1]) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_FACE); + assertTrue(nearest == (ccd_pt_el_t *)f[0] + || nearest == (ccd_pt_el_t *)f[3]); + assertTrue(ccdPtDelFace(&pt, (ccd_pt_face_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_FACE); + assertTrue(nearest == (ccd_pt_el_t *)f[0] + || nearest == (ccd_pt_el_t *)f[3]); + assertTrue(ccdPtDelFace(&pt, (ccd_pt_face_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[0] + || nearest == (ccd_pt_el_t *)e[3]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[0] + || nearest == (ccd_pt_el_t *)e[3]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_FACE); + assertEquals(nearest, (ccd_pt_el_t *)f[2]); + assertTrue(ccdPtDelFace(&pt, f[2]) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[1] + || nearest == (ccd_pt_el_t *)e[4] + || nearest == (ccd_pt_el_t *)e[5]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[1] + || nearest == (ccd_pt_el_t *)e[4] + || nearest == (ccd_pt_el_t *)e[5]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[1] + || nearest == (ccd_pt_el_t *)e[4] + || nearest == (ccd_pt_el_t *)e[5]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_EDGE); + assertTrue(nearest == (ccd_pt_el_t *)e[2]); + assertTrue(ccdPtDelEdge(&pt, (ccd_pt_edge_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_VERTEX); + assertTrue(nearest == (ccd_pt_el_t *)v[1] + || nearest == (ccd_pt_el_t *)v[2] + || nearest == (ccd_pt_el_t *)v[3]); + assertTrue(ccdPtDelVertex(&pt, (ccd_pt_vertex_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_VERTEX); + assertTrue(nearest == (ccd_pt_el_t *)v[1] + || nearest == (ccd_pt_el_t *)v[2] + || nearest == (ccd_pt_el_t *)v[3]); + assertTrue(ccdPtDelVertex(&pt, (ccd_pt_vertex_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_VERTEX); + assertTrue(nearest == (ccd_pt_el_t *)v[1] + || nearest == (ccd_pt_el_t *)v[2] + || nearest == (ccd_pt_el_t *)v[3]); + assertTrue(ccdPtDelVertex(&pt, (ccd_pt_vertex_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + //DBG("nearest->type: %d", nearest->type); + //DBG(" ->dist: %lf", nearest->dist); + //DBG_VEC3(&nearest->witness, " ->witness: "); + assertEquals(nearest->type, CCD_PT_VERTEX); + assertTrue(nearest == (ccd_pt_el_t *)v[0]); + assertTrue(ccdPtDelVertex(&pt, (ccd_pt_vertex_t *)nearest) == 0); + + + nearest = ccdPtNearest(&pt); + assertTrue(nearest == NULL); + + ccdPtDestroy(&pt); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.h new file mode 100644 index 0000000..cf31546 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/polytope.h @@ -0,0 +1,24 @@ +#ifndef TEST_POLYTOPE_H +#define TEST_POLYTOPE_H + +#include + +TEST(ptSetUp); +TEST(ptTearDown); + +TEST(ptCreate1); +TEST(ptCreate2); +TEST(ptNearest); + +TEST_SUITE(TSPt) { + TEST_ADD(ptSetUp), + + TEST_ADD(ptCreate1), + TEST_ADD(ptCreate2), + TEST_ADD(ptNearest), + + TEST_ADD(ptTearDown), + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.c new file mode 100644 index 0000000..36628f6 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.c @@ -0,0 +1,99 @@ +#include +#include +#include "support.h" +#include + +TEST(spheresphereSetUp) +{ +} + +TEST(spheresphereTearDown) +{ +} + +TEST(spheresphereAlignedX) +{ + ccd_t ccd; + CCD_SPHERE(s1); + CCD_SPHERE(s2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + s1.radius = 0.35; + s2.radius = .5; + + ccdVec3Set(&s1.pos, -5., 0., 0.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&s1, &s2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + s1.pos.v[0] += 0.1; + } +} + +TEST(spheresphereAlignedY) +{ + ccd_t ccd; + CCD_SPHERE(s1); + CCD_SPHERE(s2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + s1.radius = 0.35; + s2.radius = .5; + + ccdVec3Set(&s1.pos, 0., -5., 0.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&s1, &s2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + s1.pos.v[1] += 0.1; + } +} + +TEST(spheresphereAlignedZ) +{ + ccd_t ccd; + CCD_SPHERE(s1); + CCD_SPHERE(s2); + size_t i; + int res; + + CCD_INIT(&ccd); + ccd.support1 = ccdSupport; + ccd.support2 = ccdSupport; + + s1.radius = 0.35; + s2.radius = .5; + + ccdVec3Set(&s1.pos, 0., 0., -5.); + for (i = 0; i < 100; i++){ + res = ccdGJKIntersect(&s1, &s2, &ccd); + + if (i < 42 || i > 58){ + assertFalse(res); + }else{ + assertTrue(res); + } + + s1.pos.v[2] += 0.1; + } +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.h new file mode 100644 index 0000000..b032215 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/spheresphere.h @@ -0,0 +1,24 @@ +#ifndef SPHERE_SPHERE +#define SPHERE_SPHERE + +#include + +TEST(spheresphereSetUp); +TEST(spheresphereTearDown); + +TEST(spheresphereAlignedX); +TEST(spheresphereAlignedY); +TEST(spheresphereAlignedZ); + +TEST_SUITE(TSSphereSphere) { + TEST_ADD(spheresphereSetUp), + + TEST_ADD(spheresphereAlignedX), + TEST_ADD(spheresphereAlignedY), + TEST_ADD(spheresphereAlignedZ), + + TEST_ADD(spheresphereTearDown), + TEST_SUITE_CLOSURE +}; + +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/support.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/support.c new file mode 100644 index 0000000..5f2b4c7 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/support.c @@ -0,0 +1,85 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include +#include +#include "support.h" + +void ccdSupport(const void *_obj, const ccd_vec3_t *_dir, + ccd_vec3_t *v) +{ + // Support function is made according to Gino van den Bergen's paper + // A Fast and Robust CCD Implementation for Collision Detection of + // Convex Objects + + ccd_obj_t *obj = (ccd_obj_t *)_obj; + ccd_vec3_t dir; + ccd_quat_t qinv; + + ccdVec3Copy(&dir, _dir); + ccdQuatInvert2(&qinv, &obj->quat); + + ccdQuatRotVec(&dir, &qinv); + + if (obj->type == CCD_OBJ_BOX){ + ccd_box_t *box = (ccd_box_t *)obj; + ccdVec3Set(v, ccdSign(ccdVec3X(&dir)) * box->x * CCD_REAL(0.5), + ccdSign(ccdVec3Y(&dir)) * box->y * CCD_REAL(0.5), + ccdSign(ccdVec3Z(&dir)) * box->z * CCD_REAL(0.5)); + }else if (obj->type == CCD_OBJ_SPHERE){ + ccd_sphere_t *sphere = (ccd_sphere_t *)obj; + ccd_real_t len; + + len = ccdVec3Len2(&dir); + if (len - CCD_EPS > CCD_ZERO){ + ccdVec3Copy(v, &dir); + ccdVec3Scale(v, sphere->radius / CCD_SQRT(len)); + }else{ + ccdVec3Set(v, CCD_ZERO, CCD_ZERO, CCD_ZERO); + } + }else if (obj->type == CCD_OBJ_CYL){ + ccd_cyl_t *cyl = (ccd_cyl_t *)obj; + ccd_real_t zdist, rad; + + zdist = dir.v[0] * dir.v[0] + dir.v[1] * dir.v[1]; + zdist = CCD_SQRT(zdist); + if (ccdIsZero(zdist)){ + ccdVec3Set(v, CCD_ZERO, CCD_ZERO, + ccdSign(ccdVec3Z(&dir)) * cyl->height * CCD_REAL(0.5)); + }else{ + rad = cyl->radius / zdist; + + ccdVec3Set(v, rad * ccdVec3X(&dir), + rad * ccdVec3Y(&dir), + ccdSign(ccdVec3Z(&dir)) * cyl->height * CCD_REAL(0.5)); + } + } + + // transform support vertex + ccdQuatRotVec(v, &obj->quat); + ccdVec3Add(v, &obj->pos); +} + +void ccdObjCenter(const void *_obj, ccd_vec3_t *center) +{ + ccd_obj_t *obj = (ccd_obj_t *)_obj; + + ccdVec3Set(center, CCD_ZERO, CCD_ZERO, CCD_ZERO); + // rotation is not needed + ccdVec3Add(center, &obj->pos); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/support.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/support.h new file mode 100644 index 0000000..b0effb2 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/support.h @@ -0,0 +1,102 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +/*** + * Some support() functions for some convex shapes. + */ + +#ifndef __CCD_SUPPORT_H__ +#define __CCD_SUPPORT_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define CCD_OBJ_BOX 1 +#define CCD_OBJ_SPHERE 2 +#define CCD_OBJ_CYL 3 + +#define __CCD_OBJ__ \ + int type; \ + ccd_vec3_t pos; \ + ccd_quat_t quat; + +struct _ccd_obj_t { + __CCD_OBJ__ +}; +typedef struct _ccd_obj_t ccd_obj_t; + +struct _ccd_box_t { + __CCD_OBJ__ + ccd_real_t x, y, z; //!< Lengths of box's edges +}; +typedef struct _ccd_box_t ccd_box_t; + +struct _ccd_sphere_t { + __CCD_OBJ__ + ccd_real_t radius; +}; +typedef struct _ccd_sphere_t ccd_sphere_t; + +struct _ccd_cyl_t { + __CCD_OBJ__ + ccd_real_t radius; + ccd_real_t height; +}; +typedef struct _ccd_cyl_t ccd_cyl_t; + + +#define CCD_BOX(name) \ + ccd_box_t name = { .type = CCD_OBJ_BOX, \ + .pos = { .v = { 0., 0., 0. } }, \ + .quat = { .q = { 0., 0., 0., 1. } }, \ + .x = 0., \ + .y = 0., \ + .z = 0. } + +#define CCD_SPHERE(name) \ + ccd_sphere_t name = { .type = CCD_OBJ_SPHERE, \ + .pos = { .v = { 0., 0., 0. } }, \ + .quat = { .q = { 0., 0., 0., 1. } }, \ + .radius = 0. } + +#define CCD_CYL(name) \ + ccd_cyl_t name = { .type = CCD_OBJ_CYL, \ + .pos = { .v = { 0., 0., 0. } }, \ + .quat = { .q = { 0., 0., 0., 1. } }, \ + .radius = 0., \ + .height = 0. } + +/** + * Returns supporting vertex via v. + * Supporting vertex is farthest vertex from object in direction dir. + */ +void ccdSupport(const void *obj, const ccd_vec3_t *dir, + ccd_vec3_t *v); + +/** + * Returns center of object. + */ +void ccdObjCenter(const void *obj, ccd_vec3_t *center); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* __CCD_SUPPORT_H__ */ diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.c b/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.c new file mode 100644 index 0000000..33387a7 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.c @@ -0,0 +1,273 @@ +#include +#include +#include + +TEST(vec3SetUp) +{ +} + +TEST(vec3TearDown) +{ +} + + +TEST(vec3PointSegmentDist) +{ + ccd_vec3_t P, a, b, w, ew; + ccd_real_t dist; + + ccdVec3Set(&a, 0., 0., 0.); + ccdVec3Set(&b, 1., 0., 0.); + + // extreme w == a + ccdVec3Set(&P, -1., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 1.)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -0.5, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.5 * 0.5)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -0.1, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, .1 * .1)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, 0., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -1., 1., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 2.)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -0.5, 0.5, 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.5)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -0.1, -1., 2.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 5.01)); + assertTrue(ccdVec3Eq(&w, &a)); + + + // extreme w == b + ccdVec3Set(&P, 2., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 1.)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 1.5, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.5 * 0.5)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 1.1, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, .1 * .1)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 1., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 2., 1., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 2.)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 1.5, 0.5, 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.5)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 1.1, -1., 2.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 5.01)); + assertTrue(ccdVec3Eq(&w, &b)); + + // inside segment + ccdVec3Set(&P, .5, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &P)); + + ccdVec3Set(&P, .9, 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &P)); + + ccdVec3Set(&P, .5, 1., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 1.)); + ccdVec3Set(&ew, 0.5, 0., 0.); + assertTrue(ccdVec3Eq(&w, &ew)); + + ccdVec3Set(&P, .5, 1., 1.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 2.)); + ccdVec3Set(&ew, 0.5, 0., 0.); + assertTrue(ccdVec3Eq(&w, &ew)); + + + + ccdVec3Set(&a, -.5, 2., 1.); + ccdVec3Set(&b, 1., 1.5, 0.5); + + // extreme w == a + ccdVec3Set(&P, -10., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 9.5 * 9.5 + 2. * 2. + 1.)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, -10., 9.2, 3.4); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 9.5 * 9.5 + 7.2 * 7.2 + 2.4 * 2.4)); + assertTrue(ccdVec3Eq(&w, &a)); + + // extreme w == b + ccdVec3Set(&P, 10., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 9. * 9. + 1.5 * 1.5 + 0.5 * 0.5)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, 10., 9.2, 3.4); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 9. * 9. + 7.7 * 7.7 + 2.9 * 2.9)); + assertTrue(ccdVec3Eq(&w, &b)); + + // inside ab + ccdVec3Set(&a, -.1, 1., 1.); + ccdVec3Set(&b, 1., 1., 1.); + ccdVec3Set(&P, 0., 0., 0.); + dist = ccdVec3PointSegmentDist2(&P, &a, &b, &w); + assertTrue(ccdEq(dist, 2.)); + ccdVec3Set(&ew, 0., 1., 1.); + assertTrue(ccdVec3Eq(&w, &ew)); +} + + +TEST(vec3PointTriDist) +{ + ccd_vec3_t P, a, b, c, w, P0; + ccd_real_t dist; + + ccdVec3Set(&a, -1., 0., 0.); + ccdVec3Set(&b, 0., 1., 1.); + ccdVec3Set(&c, -1., 0., 1.); + + ccdVec3Set(&P, -1., 0., 0.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &a)); + + ccdVec3Set(&P, 0., 1., 1.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &b)); + + ccdVec3Set(&P, -1., 0., 1.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &c)); + + ccdVec3Set(&P, 0., 0., 0.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, NULL); + assertTrue(ccdEq(dist, 2./3.)); + + + // region 4 + ccdVec3Set(&P, -2., 0., 0.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &a))); + assertTrue(ccdVec3Eq(&w, &a)); + ccdVec3Set(&P, -2., 0.2, -1.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &a))); + assertTrue(ccdVec3Eq(&w, &a)); + + // region 2 + ccdVec3Set(&P, -1.3, 0., 1.2); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &c))); + assertTrue(ccdVec3Eq(&w, &c)); + ccdVec3Set(&P, -1.2, 0.2, 1.1); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &c))); + assertTrue(ccdVec3Eq(&w, &c)); + + // region 6 + ccdVec3Set(&P, 0.3, 1., 1.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &b))); + assertTrue(ccdVec3Eq(&w, &b)); + ccdVec3Set(&P, .1, 1., 1.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, ccdVec3Dist2(&P, &b))); + assertTrue(ccdVec3Eq(&w, &b)); + + // region 1 + ccdVec3Set(&P, 0., 1., 2.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 1.)); + assertTrue(ccdVec3Eq(&w, &b)); + ccdVec3Set(&P, -1., 0., 2.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 1.)); + assertTrue(ccdVec3Eq(&w, &c)); + ccdVec3Set(&P, -0.5, 0.5, 2.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 1.)); + ccdVec3Set(&P0, -0.5, 0.5, 1.); + assertTrue(ccdVec3Eq(&w, &P0)); + + // region 3 + ccdVec3Set(&P, -2., -1., 0.7); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 2.)); + ccdVec3Set(&P0, -1., 0., 0.7); + assertTrue(ccdVec3Eq(&w, &P0)); + + // region 5 + ccdVec3Set(&P, 0., 0., 0.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 2./3.)); + ccdVec3Set(&P0, -2./3., 1./3., 1./3.); + assertTrue(ccdVec3Eq(&w, &P0)); + + // region 0 + ccdVec3Set(&P, -0.5, 0.5, 0.5); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &P)); + ccdVec3Set(&P, -0.5, 0.5, 0.7); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &P)); + ccdVec3Set(&P, -0.5, 0.5, 0.9); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.)); + assertTrue(ccdVec3Eq(&w, &P)); + + ccdVec3Set(&P, 0., 0., 0.5); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.5)); + ccdVec3Set(&P0, -.5, .5, .5); + assertTrue(ccdVec3Eq(&w, &P0)); + + ccdVec3Set(&a, -1., 0., 0.); + ccdVec3Set(&b, 0., 1., -1.); + ccdVec3Set(&c, 0., 1., 1.); + ccdVec3Set(&P, 0., 0., 0.); + dist = ccdVec3PointTriDist2(&P, &a, &b, &c, &w); + assertTrue(ccdEq(dist, 0.5)); + ccdVec3Set(&P0, -.5, .5, 0.); + assertTrue(ccdVec3Eq(&w, &P0)); + //fprintf(stderr, "dist: %lf\n", dist); +} diff --git a/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.h b/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.h new file mode 100644 index 0000000..2055947 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/testsuites/vec3.h @@ -0,0 +1,20 @@ +#ifndef TEST_VEC3_H +#define TEST_VEC3_H + +#include + +TEST(vec3SetUp); +TEST(vec3TearDown); +TEST(vec3PointSegmentDist); +TEST(vec3PointTriDist); + +TEST_SUITE(TSVec3) { + TEST_ADD(vec3SetUp), + + TEST_ADD(vec3PointSegmentDist), + TEST_ADD(vec3PointTriDist), + + TEST_ADD(vec3TearDown), + TEST_SUITE_CLOSURE +}; +#endif diff --git a/thirdparty/ode-0.16.5/libccd/src/vec3.c b/thirdparty/ode-0.16.5/libccd/src/vec3.c new file mode 100644 index 0000000..f1a0804 --- /dev/null +++ b/thirdparty/ode-0.16.5/libccd/src/vec3.c @@ -0,0 +1,215 @@ +/*** + * libccd + * --------------------------------- + * Copyright (c)2010 Daniel Fiser + * + * + * This file is part of libccd. + * + * Distributed under the OSI-approved BSD License (the "License"); + * see accompanying file BDS-LICENSE for details or see + * . + * + * This software is distributed WITHOUT ANY WARRANTY; without even the + * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the License for more information. + */ + +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +static CCD_VEC3(__ccd_vec3_origin, CCD_ZERO, CCD_ZERO, CCD_ZERO); +ccd_vec3_t *ccd_vec3_origin = &__ccd_vec3_origin; + +static ccd_vec3_t points_on_sphere[] = { + CCD_VEC3_STATIC(CCD_REAL( 0.000000), CCD_REAL(-0.000000), CCD_REAL(-1.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.723608), CCD_REAL(-0.525725), CCD_REAL(-0.447219)), + CCD_VEC3_STATIC(CCD_REAL(-0.276388), CCD_REAL(-0.850649), CCD_REAL(-0.447219)), + CCD_VEC3_STATIC(CCD_REAL(-0.894426), CCD_REAL(-0.000000), CCD_REAL(-0.447216)), + CCD_VEC3_STATIC(CCD_REAL(-0.276388), CCD_REAL( 0.850649), CCD_REAL(-0.447220)), + CCD_VEC3_STATIC(CCD_REAL( 0.723608), CCD_REAL( 0.525725), CCD_REAL(-0.447219)), + CCD_VEC3_STATIC(CCD_REAL( 0.276388), CCD_REAL(-0.850649), CCD_REAL( 0.447220)), + CCD_VEC3_STATIC(CCD_REAL(-0.723608), CCD_REAL(-0.525725), CCD_REAL( 0.447219)), + CCD_VEC3_STATIC(CCD_REAL(-0.723608), CCD_REAL( 0.525725), CCD_REAL( 0.447219)), + CCD_VEC3_STATIC(CCD_REAL( 0.276388), CCD_REAL( 0.850649), CCD_REAL( 0.447219)), + CCD_VEC3_STATIC(CCD_REAL( 0.894426), CCD_REAL( 0.000000), CCD_REAL( 0.447216)), + CCD_VEC3_STATIC(CCD_REAL(-0.000000), CCD_REAL( 0.000000), CCD_REAL( 1.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.425323), CCD_REAL(-0.309011), CCD_REAL(-0.850654)), + CCD_VEC3_STATIC(CCD_REAL(-0.162456), CCD_REAL(-0.499995), CCD_REAL(-0.850654)), + CCD_VEC3_STATIC(CCD_REAL( 0.262869), CCD_REAL(-0.809012), CCD_REAL(-0.525738)), + CCD_VEC3_STATIC(CCD_REAL( 0.425323), CCD_REAL( 0.309011), CCD_REAL(-0.850654)), + CCD_VEC3_STATIC(CCD_REAL( 0.850648), CCD_REAL(-0.000000), CCD_REAL(-0.525736)), + CCD_VEC3_STATIC(CCD_REAL(-0.525730), CCD_REAL(-0.000000), CCD_REAL(-0.850652)), + CCD_VEC3_STATIC(CCD_REAL(-0.688190), CCD_REAL(-0.499997), CCD_REAL(-0.525736)), + CCD_VEC3_STATIC(CCD_REAL(-0.162456), CCD_REAL( 0.499995), CCD_REAL(-0.850654)), + CCD_VEC3_STATIC(CCD_REAL(-0.688190), CCD_REAL( 0.499997), CCD_REAL(-0.525736)), + CCD_VEC3_STATIC(CCD_REAL( 0.262869), CCD_REAL( 0.809012), CCD_REAL(-0.525738)), + CCD_VEC3_STATIC(CCD_REAL( 0.951058), CCD_REAL( 0.309013), CCD_REAL( 0.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.951058), CCD_REAL(-0.309013), CCD_REAL( 0.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.587786), CCD_REAL(-0.809017), CCD_REAL( 0.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.000000), CCD_REAL(-1.000000), CCD_REAL( 0.000000)), + CCD_VEC3_STATIC(CCD_REAL(-0.587786), CCD_REAL(-0.809017), CCD_REAL( 0.000000)), + CCD_VEC3_STATIC(CCD_REAL(-0.951058), CCD_REAL(-0.309013), CCD_REAL(-0.000000)), + CCD_VEC3_STATIC(CCD_REAL(-0.951058), CCD_REAL( 0.309013), CCD_REAL(-0.000000)), + CCD_VEC3_STATIC(CCD_REAL(-0.587786), CCD_REAL( 0.809017), CCD_REAL(-0.000000)), + CCD_VEC3_STATIC(CCD_REAL(-0.000000), CCD_REAL( 1.000000), CCD_REAL(-0.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.587786), CCD_REAL( 0.809017), CCD_REAL(-0.000000)), + CCD_VEC3_STATIC(CCD_REAL( 0.688190), CCD_REAL(-0.499997), CCD_REAL( 0.525736)), + CCD_VEC3_STATIC(CCD_REAL(-0.262869), CCD_REAL(-0.809012), CCD_REAL( 0.525738)), + CCD_VEC3_STATIC(CCD_REAL(-0.850648), CCD_REAL( 0.000000), CCD_REAL( 0.525736)), + CCD_VEC3_STATIC(CCD_REAL(-0.262869), CCD_REAL( 0.809012), CCD_REAL( 0.525738)), + CCD_VEC3_STATIC(CCD_REAL( 0.688190), CCD_REAL( 0.499997), CCD_REAL( 0.525736)), + CCD_VEC3_STATIC(CCD_REAL( 0.525730), CCD_REAL( 0.000000), CCD_REAL( 0.850652)), + CCD_VEC3_STATIC(CCD_REAL( 0.162456), CCD_REAL(-0.499995), CCD_REAL( 0.850654)), + CCD_VEC3_STATIC(CCD_REAL(-0.425323), CCD_REAL(-0.309011), CCD_REAL( 0.850654)), + CCD_VEC3_STATIC(CCD_REAL(-0.425323), CCD_REAL( 0.309011), CCD_REAL( 0.850654)), + CCD_VEC3_STATIC(CCD_REAL( 0.162456), CCD_REAL( 0.499995), CCD_REAL( 0.850654)) +}; +ccd_vec3_t *ccd_points_on_sphere = points_on_sphere; +size_t ccd_points_on_sphere_len = sizeof(points_on_sphere) / sizeof(ccd_vec3_t); + + +_ccd_inline ccd_real_t __ccdVec3PointSegmentDist2(const ccd_vec3_t *P, + const ccd_vec3_t *x0, + const ccd_vec3_t *b, + ccd_vec3_t *witness) +{ + // The computation comes from solving equation of segment: + // S(t) = x0 + t.d + // where - x0 is initial point of segment + // - d is direction of segment from x0 (|d| > 0) + // - t belongs to <0, 1> interval + // + // Than, distance from a segment to some point P can be expressed: + // D(t) = |x0 + t.d - P|^2 + // which is distance from any point on segment. Minimization + // of this function brings distance from P to segment. + // Minimization of D(t) leads to simple quadratic equation that's + // solving is straightforward. + // + // Bonus of this method is witness point for free. + + ccd_real_t dist, t; + ccd_vec3_t d, a; + + // direction of segment + ccdVec3Sub2(&d, b, x0); + + // precompute vector from P to x0 + ccdVec3Sub2(&a, x0, P); + + t = -CCD_REAL(1.) * ccdVec3Dot(&a, &d); + t /= ccdVec3Len2(&d); + + if (t < CCD_ZERO || ccdIsZero(t)){ + dist = ccdVec3Dist2(x0, P); + if (witness) + ccdVec3Copy(witness, x0); + }else if (t > CCD_ONE || ccdEq(t, CCD_ONE)){ + dist = ccdVec3Dist2(b, P); + if (witness) + ccdVec3Copy(witness, b); + }else{ + if (witness){ + ccdVec3Copy(witness, &d); + ccdVec3Scale(witness, t); + ccdVec3Add(witness, x0); + dist = ccdVec3Dist2(witness, P); + }else{ + // recycling variables + ccdVec3Scale(&d, t); + ccdVec3Add(&d, &a); + dist = ccdVec3Len2(&d); + } + } + + return dist; +} + +ccd_real_t ccdVec3PointSegmentDist2(const ccd_vec3_t *P, + const ccd_vec3_t *x0, const ccd_vec3_t *b, + ccd_vec3_t *witness) +{ + return __ccdVec3PointSegmentDist2(P, x0, b, witness); +} + +ccd_real_t ccdVec3PointTriDist2(const ccd_vec3_t *P, + const ccd_vec3_t *x0, const ccd_vec3_t *B, + const ccd_vec3_t *C, + ccd_vec3_t *witness) +{ + // Computation comes from analytic expression for triangle (x0, B, C) + // T(s, t) = x0 + s.d1 + t.d2, where d1 = B - x0 and d2 = C - x0 and + // Then equation for distance is: + // D(s, t) = | T(s, t) - P |^2 + // This leads to minimization of quadratic function of two variables. + // The solution from is taken only if s is between 0 and 1, t is + // between 0 and 1 and t + s < 1, otherwise distance from segment is + // computed. + + ccd_vec3_t d1, d2, a; + ccd_real_t u, v, w, p, q, r; + ccd_real_t s, t, dist, dist2; + ccd_vec3_t witness2; + + ccdVec3Sub2(&d1, B, x0); + ccdVec3Sub2(&d2, C, x0); + ccdVec3Sub2(&a, x0, P); + + u = ccdVec3Dot(&a, &a); + v = ccdVec3Dot(&d1, &d1); + w = ccdVec3Dot(&d2, &d2); + p = ccdVec3Dot(&a, &d1); + q = ccdVec3Dot(&a, &d2); + r = ccdVec3Dot(&d1, &d2); + + s = (q * r - w * p) / (w * v - r * r); + t = (-s * r - q) / w; + + if ((ccdIsZero(s) || s > CCD_ZERO) + && (ccdEq(s, CCD_ONE) || s < CCD_ONE) + && (ccdIsZero(t) || t > CCD_ZERO) + && (ccdEq(t, CCD_ONE) || t < CCD_ONE) + && (ccdEq(t + s, CCD_ONE) || t + s < CCD_ONE)){ + + if (witness){ + ccdVec3Scale(&d1, s); + ccdVec3Scale(&d2, t); + ccdVec3Copy(witness, x0); + ccdVec3Add(witness, &d1); + ccdVec3Add(witness, &d2); + + dist = ccdVec3Dist2(witness, P); + }else{ + dist = s * s * v; + dist += t * t * w; + dist += CCD_REAL(2.) * s * t * r; + dist += CCD_REAL(2.) * s * p; + dist += CCD_REAL(2.) * t * q; + dist += u; + } + }else{ + dist = __ccdVec3PointSegmentDist2(P, x0, B, witness); + + dist2 = __ccdVec3PointSegmentDist2(P, x0, C, &witness2); + if (dist2 < dist){ + dist = dist2; + if (witness) + ccdVec3Copy(witness, &witness2); + } + + dist2 = __ccdVec3PointSegmentDist2(P, B, C, &witness2); + if (dist2 < dist){ + dist = dist2; + if (witness) + ccdVec3Copy(witness, &witness2); + } + } + + return dist; +} diff --git a/thirdparty/ode-0.16.5/ltmain.sh b/thirdparty/ode-0.16.5/ltmain.sh new file mode 100644 index 0000000..147d758 --- /dev/null +++ b/thirdparty/ode-0.16.5/ltmain.sh @@ -0,0 +1,11156 @@ +#! /bin/sh +## DO NOT EDIT - This file generated from ./build-aux/ltmain.in +## by inline-source v2014-01-03.01 + +# libtool (GNU libtool) 2.4.6 +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +PROGRAM=libtool +PACKAGE=libtool +VERSION="2.4.6 Debian-2.4.6-0.1" +package_revision=2.4.6 + + +## ------ ## +## Usage. ## +## ------ ## + +# Run './libtool --help' for help with using this script from the +# command line. + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# After configure completes, it has a better idea of some of the +# shell tools we need than the defaults used by the functions shared +# with bootstrap, so set those here where they can still be over- +# ridden by the user, but otherwise take precedence. + +: ${AUTOCONF="autoconf"} +: ${AUTOMAKE="automake"} + + +## -------------------------- ## +## Source external libraries. ## +## -------------------------- ## + +# Much of our low-level functionality needs to be sourced from external +# libraries, which are installed to $pkgauxdir. + +# Set a version string for this script. +scriptversion=2015-01-20.17; # UTC + +# General shell script boiler plate, and helper functions. +# Written by Gary V. Vaughan, 2004 + +# Copyright (C) 2004-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Evaluate this file near the top of your script to gain access to +# the functions and variables defined here: +# +# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh +# +# If you need to override any of the default environment variable +# settings, do that before evaluating this file. + + +## -------------------- ## +## Shell normalisation. ## +## -------------------- ## + +# Some shells need a little help to be as Bourne compatible as possible. +# Before doing anything else, make sure all that help has been provided! + +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac +fi + +# NLS nuisances: We save the old values in case they are required later. +_G_user_locale= +_G_safe_locale= +for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test set = \"\${$_G_var+set}\"; then + save_$_G_var=\$$_G_var + $_G_var=C + export $_G_var + _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" + _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" + fi" +done + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Make sure IFS has a sensible default +sp=' ' +nl=' +' +IFS="$sp $nl" + +# There are apparently some retarded systems that use ';' as a PATH separator! +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + + +## ------------------------- ## +## Locate command utilities. ## +## ------------------------- ## + + +# func_executable_p FILE +# ---------------------- +# Check that FILE is an executable regular file. +func_executable_p () +{ + test -f "$1" && test -x "$1" +} + + +# func_path_progs PROGS_LIST CHECK_FUNC [PATH] +# -------------------------------------------- +# Search for either a program that responds to --version with output +# containing "GNU", or else returned by CHECK_FUNC otherwise, by +# trying all the directories in PATH with each of the elements of +# PROGS_LIST. +# +# CHECK_FUNC should accept the path to a candidate program, and +# set $func_check_prog_result if it truncates its output less than +# $_G_path_prog_max characters. +func_path_progs () +{ + _G_progs_list=$1 + _G_check_func=$2 + _G_PATH=${3-"$PATH"} + + _G_path_prog_max=0 + _G_path_prog_found=false + _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} + for _G_dir in $_G_PATH; do + IFS=$_G_save_IFS + test -z "$_G_dir" && _G_dir=. + for _G_prog_name in $_G_progs_list; do + for _exeext in '' .EXE; do + _G_path_prog=$_G_dir/$_G_prog_name$_exeext + func_executable_p "$_G_path_prog" || continue + case `"$_G_path_prog" --version 2>&1` in + *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; + *) $_G_check_func $_G_path_prog + func_path_progs_result=$func_check_prog_result + ;; + esac + $_G_path_prog_found && break 3 + done + done + done + IFS=$_G_save_IFS + test -z "$func_path_progs_result" && { + echo "no acceptable sed could be found in \$PATH" >&2 + exit 1 + } +} + + +# We want to be able to use the functions in this file before configure +# has figured out where the best binaries are kept, which means we have +# to search for them ourselves - except when the results are already set +# where we skip the searches. + +# Unless the user overrides by setting SED, search the path for either GNU +# sed, or the sed that truncates its output the least. +test -z "$SED" && { + _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for _G_i in 1 2 3 4 5 6 7; do + _G_sed_script=$_G_sed_script$nl$_G_sed_script + done + echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed + _G_sed_script= + + func_check_prog_sed () + { + _G_path_prog=$1 + + _G_count=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo '' >> conftest.nl + "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + rm -f conftest.sed + SED=$func_path_progs_result +} + + +# Unless the user overrides by setting GREP, search the path for either GNU +# grep, or the grep that truncates its output the least. +test -z "$GREP" && { + func_check_prog_grep () + { + _G_path_prog=$1 + + _G_count=0 + _G_path_prog_max=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo 'GREP' >> conftest.nl + "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + GREP=$func_path_progs_result +} + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase variable names are used for environment variables. These +# variables can be overridden by the user before calling a script that +# uses them if a suitable command of that name is not already available +# in the command search PATH. + +: ${CP="cp -f"} +: ${ECHO="printf %s\n"} +: ${EGREP="$GREP -E"} +: ${FGREP="$GREP -F"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} + + +## -------------------- ## +## Useful sed snippets. ## +## -------------------- ## + +sed_dirname='s|/[^/]*$||' +sed_basename='s|^.*/||' + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Same as above, but do not quote variable references. +sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' + +# Sed substitution that converts a w32 file name or path +# that contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-'\' parameter expansions in output of sed_double_quote_subst that +# were '\'-ed in input to the same. If an odd number of '\' preceded a +# '$' in input to sed_double_quote_subst, that '$' was protected from +# expansion. Since each input '\' is now two '\'s, look for any number +# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. +_G_bs='\\' +_G_bs2='\\\\' +_G_bs4='\\\\\\\\' +_G_dollar='\$' +sed_double_backslash="\ + s/$_G_bs4/&\\ +/g + s/^$_G_bs2$_G_dollar/$_G_bs&/ + s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g + s/\n//g" + + +## ----------------- ## +## Global variables. ## +## ----------------- ## + +# Except for the global variables explicitly listed below, the following +# functions in the '^func_' namespace, and the '^require_' namespace +# variables initialised in the 'Resource management' section, sourcing +# this file will not pollute your global namespace with anything +# else. There's no portable way to scope variables in Bourne shell +# though, so actually running these functions will sometimes place +# results into a variable named after the function, and often use +# temporary variables in the '^_G_' namespace. If you are careful to +# avoid using those namespaces casually in your sourcing script, things +# should continue to work as you expect. And, of course, you can freely +# overwrite any of the functions or variables defined here before +# calling anything to customize them. + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +# Allow overriding, eg assuming that you follow the convention of +# putting '$debug_cmd' at the start of all your functions, you can get +# bash to show function call trace with: +# +# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name +debug_cmd=${debug_cmd-":"} +exit_cmd=: + +# By convention, finish your script with: +# +# exit $exit_status +# +# so that you can set exit_status to non-zero if you want to indicate +# something went wrong during execution without actually bailing out at +# the point of failure. +exit_status=$EXIT_SUCCESS + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath=$0 + +# The name of this program. +progname=`$ECHO "$progpath" |$SED "$sed_basename"` + +# Make sure we have an absolute progpath for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` + progdir=`cd "$progdir" && pwd` + progpath=$progdir/$progname + ;; + *) + _G_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS=$_G_IFS + test -x "$progdir/$progname" && break + done + IFS=$_G_IFS + test -n "$progdir" || progdir=`pwd` + progpath=$progdir/$progname + ;; +esac + + +## ----------------- ## +## Standard options. ## +## ----------------- ## + +# The following options affect the operation of the functions defined +# below, and should be set appropriately depending on run-time para- +# meters passed on the command line. + +opt_dry_run=false +opt_quiet=false +opt_verbose=false + +# Categories 'all' and 'none' are always available. Append any others +# you will pass as the first argument to func_warning from your own +# code. +warning_categories= + +# By default, display warnings according to 'opt_warning_types'. Set +# 'warning_func' to ':' to elide all warnings, or func_fatal_error to +# treat the next displayed warning as a fatal error. +warning_func=func_warn_and_continue + +# Set to 'all' to display all warnings, 'none' to suppress all +# warnings, or a space delimited list of some subset of +# 'warning_categories' to display only the listed warnings. +opt_warning_types=all + + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Call them using their associated +# 'require_*' variable to ensure that they are executed, at most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_term_colors +# ------------------- +# Allow display of bold text on terminals that support it. +require_term_colors=func_require_term_colors +func_require_term_colors () +{ + $debug_cmd + + test -t 1 && { + # COLORTERM and USE_ANSI_COLORS environment variables take + # precedence, because most terminfo databases neglect to describe + # whether color sequences are supported. + test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} + + if test 1 = "$USE_ANSI_COLORS"; then + # Standard ANSI escape sequences + tc_reset='' + tc_bold=''; tc_standout='' + tc_red=''; tc_green='' + tc_blue=''; tc_cyan='' + else + # Otherwise trust the terminfo database after all. + test -n "`tput sgr0 2>/dev/null`" && { + tc_reset=`tput sgr0` + test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` + tc_standout=$tc_bold + test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` + test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` + test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` + test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` + test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` + } + fi + } + + require_term_colors=: +} + + +## ----------------- ## +## Function library. ## +## ----------------- ## + +# This section contains a variety of useful functions to call in your +# scripts. Take note of the portable wrappers for features provided by +# some modern shells, which will fall back to slower equivalents on +# less featureful shells. + + +# func_append VAR VALUE +# --------------------- +# Append VALUE onto the existing contents of VAR. + + # We should try to minimise forks, especially on Windows where they are + # unreasonably slow, so skip the feature probes when bash or zsh are + # being used: + if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then + : ${_G_HAVE_ARITH_OP="yes"} + : ${_G_HAVE_XSI_OPS="yes"} + # The += operator was introduced in bash 3.1 + case $BASH_VERSION in + [12].* | 3.0 | 3.0*) ;; + *) + : ${_G_HAVE_PLUSEQ_OP="yes"} + ;; + esac + fi + + # _G_HAVE_PLUSEQ_OP + # Can be empty, in which case the shell is probed, "yes" if += is + # useable or anything else if it does not work. + test -z "$_G_HAVE_PLUSEQ_OP" \ + && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ + && _G_HAVE_PLUSEQ_OP=yes + +if test yes = "$_G_HAVE_PLUSEQ_OP" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_append () + { + $debug_cmd + + eval "$1+=\$2" + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_append () + { + $debug_cmd + + eval "$1=\$$1\$2" + } +fi + + +# func_append_quoted VAR VALUE +# ---------------------------- +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +if test yes = "$_G_HAVE_PLUSEQ_OP"; then + eval 'func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" + }' +else + func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" + } +fi + + +# func_append_uniq VAR VALUE +# -------------------------- +# Append unique VALUE onto the existing contents of VAR, assuming +# entries are delimited by the first character of VALUE. For example: +# +# func_append_uniq options " --another-option option-argument" +# +# will only append to $options if " --another-option option-argument " +# is not already present somewhere in $options already (note spaces at +# each end implied by leading space in second argument). +func_append_uniq () +{ + $debug_cmd + + eval _G_current_value='`$ECHO $'$1'`' + _G_delim=`expr "$2" : '\(.\)'` + + case $_G_delim$_G_current_value$_G_delim in + *"$2$_G_delim"*) ;; + *) func_append "$@" ;; + esac +} + + +# func_arith TERM... +# ------------------ +# Set func_arith_result to the result of evaluating TERMs. + test -z "$_G_HAVE_ARITH_OP" \ + && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ + && _G_HAVE_ARITH_OP=yes + +if test yes = "$_G_HAVE_ARITH_OP"; then + eval 'func_arith () + { + $debug_cmd + + func_arith_result=$(( $* )) + }' +else + func_arith () + { + $debug_cmd + + func_arith_result=`expr "$@"` + } +fi + + +# func_basename FILE +# ------------------ +# Set func_basename_result to FILE with everything up to and including +# the last / stripped. +if test yes = "$_G_HAVE_XSI_OPS"; then + # If this shell supports suffix pattern removal, then use it to avoid + # forking. Hide the definitions single quotes in case the shell chokes + # on unsupported syntax... + _b='func_basename_result=${1##*/}' + _d='case $1 in + */*) func_dirname_result=${1%/*}$2 ;; + * ) func_dirname_result=$3 ;; + esac' + +else + # ...otherwise fall back to using sed. + _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' + _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` + if test "X$func_dirname_result" = "X$1"; then + func_dirname_result=$3 + else + func_append func_dirname_result "$2" + fi' +fi + +eval 'func_basename () +{ + $debug_cmd + + '"$_b"' +}' + + +# func_dirname FILE APPEND NONDIR_REPLACEMENT +# ------------------------------------------- +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +eval 'func_dirname () +{ + $debug_cmd + + '"$_d"' +}' + + +# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT +# -------------------------------------------------------- +# Perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# For efficiency, we do not delegate to the functions above but instead +# duplicate the functionality here. +eval 'func_dirname_and_basename () +{ + $debug_cmd + + '"$_b"' + '"$_d"' +}' + + +# func_echo ARG... +# ---------------- +# Echo program name prefixed message. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_echo_all ARG... +# -------------------- +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + + +# func_echo_infix_1 INFIX ARG... +# ------------------------------ +# Echo program name, followed by INFIX on the first line, with any +# additional lines not showing INFIX. +func_echo_infix_1 () +{ + $debug_cmd + + $require_term_colors + + _G_infix=$1; shift + _G_indent=$_G_infix + _G_prefix="$progname: $_G_infix: " + _G_message=$* + + # Strip color escape sequences before counting printable length + for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" + do + test -n "$_G_tc" && { + _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` + _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` + } + done + _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes + + func_echo_infix_1_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_infix_1_IFS + $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 + _G_prefix=$_G_indent + done + IFS=$func_echo_infix_1_IFS +} + + +# func_error ARG... +# ----------------- +# Echo program name prefixed message to standard error. +func_error () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 +} + + +# func_fatal_error ARG... +# ----------------------- +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + $debug_cmd + + func_error "$*" + exit $EXIT_FAILURE +} + + +# func_grep EXPRESSION FILENAME +# ----------------------------- +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $debug_cmd + + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_len STRING +# --------------- +# Set func_len_result to the length of STRING. STRING may not +# start with a hyphen. + test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` + } +fi + + +# func_mkdir_p DIRECTORY-PATH +# --------------------------- +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + $debug_cmd + + _G_directory_path=$1 + _G_dir_list= + + if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then + + # Protect directory names starting with '-' + case $_G_directory_path in + -*) _G_directory_path=./$_G_directory_path ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$_G_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + _G_dir_list=$_G_directory_path:$_G_dir_list + + # If the last portion added has no slash in it, the list is done + case $_G_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` + done + _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` + + func_mkdir_p_IFS=$IFS; IFS=: + for _G_dir in $_G_dir_list; do + IFS=$func_mkdir_p_IFS + # mkdir can fail with a 'File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$_G_dir" 2>/dev/null || : + done + IFS=$func_mkdir_p_IFS + + # Bail out if we (or some other process) failed to create a directory. + test -d "$_G_directory_path" || \ + func_fatal_error "Failed to create '$1'" + fi +} + + +# func_mktempdir [BASENAME] +# ------------------------- +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, BASENAME is the basename for that directory. +func_mktempdir () +{ + $debug_cmd + + _G_template=${TMPDIR-/tmp}/${1-$progname} + + if test : = "$opt_dry_run"; then + # Return a directory name, but don't create it in dry-run mode + _G_tmpdir=$_G_template-$$ + else + + # If mktemp works, use that first and foremost + _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` + + if test ! -d "$_G_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + _G_tmpdir=$_G_template-${RANDOM-0}$$ + + func_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$_G_tmpdir" + umask $func_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$_G_tmpdir" || \ + func_fatal_error "cannot create temporary directory '$_G_tmpdir'" + fi + + $ECHO "$_G_tmpdir" +} + + +# func_normal_abspath PATH +# ------------------------ +# Remove doubled-up and trailing slashes, "." path components, +# and cancel out any ".." path components in PATH after making +# it an absolute path. +func_normal_abspath () +{ + $debug_cmd + + # These SED scripts presuppose an absolute path with a trailing slash. + _G_pathcar='s|^/\([^/]*\).*$|\1|' + _G_pathcdr='s|^/[^/]*||' + _G_removedotparts=':dotsl + s|/\./|/|g + t dotsl + s|/\.$|/|' + _G_collapseslashes='s|/\{1,\}|/|g' + _G_finalslash='s|/*$|/|' + + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in + "") + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return + ;; + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. + ;; + *) + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath + ;; + esac + + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` + while :; do + # Processed it all yet? + if test / = "$func_normal_abspath_tpath"; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result"; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result +} + + +# func_notquiet ARG... +# -------------------- +# Echo program name prefixed message only when not in quiet mode. +func_notquiet () +{ + $debug_cmd + + $opt_quiet || func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + + +# func_relative_path SRCDIR DSTDIR +# -------------------------------- +# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. +func_relative_path () +{ + $debug_cmd + + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=$func_dirname_result + if test -z "$func_relative_path_tlibdir"; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test -n "$func_stripname_result"; then + func_append func_relative_path_result "/$func_stripname_result" + fi + + # Normalisation. If bindir is libdir, return '.' else relative path. + if test -n "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + fi + + test -n "$func_relative_path_result" || func_relative_path_result=. + + : +} + + +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. +func_quote_for_eval () +{ + $debug_cmd + + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in + *[\\\`\"\$]*) + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi + + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" + ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; + esac + + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done +} + + +# func_quote_for_expand ARG +# ------------------------- +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + $debug_cmd + + case $1 in + *[\\\`\"]*) + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; + *) + _G_arg=$1 ;; + esac + + case $_G_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_arg=\"$_G_arg\" + ;; + esac + + func_quote_for_expand_result=$_G_arg +} + + +# func_stripname PREFIX SUFFIX NAME +# --------------------------------- +# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_stripname () + { + $debug_cmd + + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary variable first. + func_stripname_result=$3 + func_stripname_result=${func_stripname_result#"$1"} + func_stripname_result=${func_stripname_result%"$2"} + }' +else + func_stripname () + { + $debug_cmd + + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; + esac + } +fi + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" + + $opt_dry_run || { + eval "$_G_cmd" + _G_status=$? + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_show_eval_locale CMD [FAIL_EXP] +# ------------------------------------ +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + $debug_cmd + + _G_cmd=$1 + _G_fail_exp=${2-':'} + + $opt_quiet || { + func_quote_for_expand "$_G_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + $opt_dry_run || { + eval "$_G_user_locale + $_G_cmd" + _G_status=$? + eval "$_G_safe_locale" + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" + fi + } +} + + +# func_tr_sh +# ---------- +# Turn $1 into a string suitable for a shell variable name. +# Result is stored in $func_tr_sh_result. All characters +# not in the set a-zA-Z0-9_ are replaced with '_'. Further, +# if $1 begins with a digit, a '_' is prepended as well. +func_tr_sh () +{ + $debug_cmd + + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac +} + + +# func_verbose ARG... +# ------------------- +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $debug_cmd + + $opt_verbose && func_echo "$*" + + : +} + + +# func_warn_and_continue ARG... +# ----------------------------- +# Echo program name prefixed warning message to standard error. +func_warn_and_continue () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 +} + + +# func_warning CATEGORY ARG... +# ---------------------------- +# Echo program name prefixed warning message to standard error. Warning +# messages can be filtered according to CATEGORY, where this function +# elides messages where CATEGORY is not listed in the global variable +# 'opt_warning_types'. +func_warning () +{ + $debug_cmd + + # CATEGORY must be in the warning_categories list! + case " $warning_categories " in + *" $1 "*) ;; + *) func_internal_error "invalid warning category '$1'" ;; + esac + + _G_category=$1 + shift + + case " $opt_warning_types " in + *" $_G_category "*) $warning_func ${1+"$@"} ;; + esac +} + + +# func_sort_ver VER1 VER2 +# ----------------------- +# 'sort -V' is not generally available. +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +func_sort_ver () +{ + $debug_cmd + + printf '%s\n%s\n' "$1" "$2" \ + | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n +} + +# func_lt_ver PREV CURR +# --------------------- +# Return true if PREV and CURR are in the correct order according to +# func_sort_ver, otherwise false. Use it like this: +# +# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." +func_lt_ver () +{ + $debug_cmd + + test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Set a version string for this script. +scriptversion=2014-01-07.03; # UTC + +# A portable, pluggable option parser for Bourne shell. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# This file is a library for parsing options in your shell scripts along +# with assorted other useful supporting features that you can make use +# of too. +# +# For the simplest scripts you might need only: +# +# #!/bin/sh +# . relative/path/to/funclib.sh +# . relative/path/to/options-parser +# scriptversion=1.0 +# func_options ${1+"$@"} +# eval set dummy "$func_options_result"; shift +# ...rest of your script... +# +# In order for the '--version' option to work, you will need to have a +# suitably formatted comment like the one at the top of this file +# starting with '# Written by ' and ending with '# warranty; '. +# +# For '-h' and '--help' to work, you will also need a one line +# description of your script's purpose in a comment directly above the +# '# Written by ' line, like the one at the top of this file. +# +# The default options also support '--debug', which will turn on shell +# execution tracing (see the comment above debug_cmd below for another +# use), and '--verbose' and the func_verbose function to allow your script +# to display verbose messages only when your user has specified +# '--verbose'. +# +# After sourcing this file, you can plug processing for additional +# options by amending the variables from the 'Configuration' section +# below, and following the instructions in the 'Option parsing' +# section further down. + +## -------------- ## +## Configuration. ## +## -------------- ## + +# You should override these variables in your script after sourcing this +# file so that they reflect the customisations you have added to the +# option parser. + +# The usage line for option parsing errors and the start of '-h' and +# '--help' output messages. You can embed shell variables for delayed +# expansion at the time the message is displayed, but you will need to +# quote other shell meta-characters carefully to prevent them being +# expanded when the contents are evaled. +usage='$progpath [OPTION]...' + +# Short help message in response to '-h' and '--help'. Add to this or +# override it after sourcing this library to reflect the full set of +# options your script accepts. +usage_message="\ + --debug enable verbose shell tracing + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -v, --verbose verbosely report processing + --version print version information and exit + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=" +Warning categories include: + 'all' show all warnings + 'none' turn off all the warnings + 'error' warnings are treated as fatal errors" + +# Help message printed before fatal option parsing errors. +fatal_help="Try '\$progname --help' for more information." + + + +## ------------------------- ## +## Hook function management. ## +## ------------------------- ## + +# This section contains functions for adding, removing, and running hooks +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. + +# func_hookable FUNC_NAME +# ----------------------- +# Declare that FUNC_NAME will run hooks added with +# 'func_add_hook FUNC_NAME ...'. +func_hookable () +{ + $debug_cmd + + func_append hookable_fns " $1" +} + + +# func_add_hook FUNC_NAME HOOK_FUNC +# --------------------------------- +# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must +# first have been declared "hookable" by a call to 'func_hookable'. +func_add_hook () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not accept hook functions." ;; + esac + + eval func_append ${1}_hooks '" $2"' +} + + +# func_remove_hook FUNC_NAME HOOK_FUNC +# ------------------------------------ +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +func_remove_hook () +{ + $debug_cmd + + eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' +} + + +# func_run_hooks FUNC_NAME [ARG]... +# --------------------------------- +# Run all hook functions registered to FUNC_NAME. +# It is assumed that the list of hook functions contains nothing more +# than a whitespace-delimited list of legal shell function names, and +# no effort is wasted trying to catch shell meta-characters or preserve +# whitespace. +func_run_hooks () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; + esac + + eval _G_hook_fns=\$$1_hooks; shift + + for _G_hook in $_G_hook_fns; do + eval $_G_hook '"$@"' + + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift + done + + func_quote_for_eval ${1+"$@"} + func_run_hooks_result=$func_quote_for_eval_result +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# In order to add your own option parsing hooks, you must accept the +# full positional parameter list in your hook function, remove any +# options that you action, and then pass back the remaining unprocessed +# options in '_result', escaped suitably for +# 'eval'. Like this: +# +# my_options_prep () +# { +# $debug_cmd +# +# # Extend the existing usage message. +# usage_message=$usage_message' +# -s, --silent don'\''t print informational messages +# ' +# +# func_quote_for_eval ${1+"$@"} +# my_options_prep_result=$func_quote_for_eval_result +# } +# func_add_hook func_options_prep my_options_prep +# +# +# my_silent_option () +# { +# $debug_cmd +# +# # Note that for efficiency, we parse as many options as we can +# # recognise in a loop before passing the remainder back to the +# # caller on the first unrecognised argument we encounter. +# while test $# -gt 0; do +# opt=$1; shift +# case $opt in +# --silent|-s) opt_silent=: ;; +# # Separate non-argument short options: +# -s*) func_split_short_opt "$_G_opt" +# set dummy "$func_split_short_opt_name" \ +# "-$func_split_short_opt_arg" ${1+"$@"} +# shift +# ;; +# *) set dummy "$_G_opt" "$*"; shift; break ;; +# esac +# done +# +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result +# } +# func_add_hook func_parse_options my_silent_option +# +# +# my_option_validation () +# { +# $debug_cmd +# +# $opt_silent && $opt_verbose && func_fatal_help "\ +# '--silent' and '--verbose' options are mutually exclusive." +# +# func_quote_for_eval ${1+"$@"} +# my_option_validation_result=$func_quote_for_eval_result +# } +# func_add_hook func_validate_options my_option_validation +# +# You'll alse need to manually amend $usage_message to reflect the extra +# options you parse. It's preferable to append if you can, so that +# multiple option parsing hooks can be added safely. + + +# func_options [ARG]... +# --------------------- +# All the functions called inside func_options are hookable. See the +# individual implementations for details. +func_hookable func_options +func_options () +{ + $debug_cmd + + func_options_prep ${1+"$@"} + eval func_parse_options \ + ${func_options_prep_result+"$func_options_prep_result"} + eval func_validate_options \ + ${func_parse_options_result+"$func_parse_options_result"} + + eval func_run_hooks func_options \ + ${func_validate_options_result+"$func_validate_options_result"} + + # save modified positional parameters for caller + func_options_result=$func_run_hooks_result +} + + +# func_options_prep [ARG]... +# -------------------------- +# All initialisations required before starting the option parse loop. +# Note that when calling hook functions, we pass through the list of +# positional parameters. If a hook function modifies that list, and +# needs to propogate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning. +func_hookable func_options_prep +func_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_verbose=false + opt_warning_types= + + func_run_hooks func_options_prep ${1+"$@"} + + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result +} + + +# func_parse_options [ARG]... +# --------------------------- +# The main option parsing loop. +func_hookable func_parse_options +func_parse_options () +{ + $debug_cmd + + func_parse_options_result= + + # this just eases exit handling + while test $# -gt 0; do + # Defer to hook functions for initial option parsing, so they + # get priority in the event of reusing an option name. + func_run_hooks func_parse_options ${1+"$@"} + + # Adjust func_parse_options positional parameters to match + eval set dummy "$func_run_hooks_result"; shift + + # Break out of the loop if we already parsed every option. + test $# -gt 0 || break + + _G_opt=$1 + shift + case $_G_opt in + --debug|-x) debug_cmd='set -x' + func_echo "enabling shell trace mode" + $debug_cmd + ;; + + --no-warnings|--no-warning|--no-warn) + set dummy --warnings none ${1+"$@"} + shift + ;; + + --warnings|--warning|-W) + test $# = 0 && func_missing_arg $_G_opt && break + case " $warning_categories $1" in + *" $1 "*) + # trailing space prevents matching last $1 above + func_append_uniq opt_warning_types " $1" + ;; + *all) + opt_warning_types=$warning_categories + ;; + *none) + opt_warning_types=none + warning_func=: + ;; + *error) + opt_warning_types=$warning_categories + warning_func=func_fatal_error + ;; + *) + func_fatal_error \ + "unsupported warning category: '$1'" + ;; + esac + shift + ;; + + --verbose|-v) opt_verbose=: ;; + --version) func_version ;; + -\?|-h) func_usage ;; + --help) func_help ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + # Separate optargs to short options: + -W*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-v*|-x*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) break ;; + -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result +} + + +# func_validate_options [ARG]... +# ------------------------------ +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +func_hookable func_validate_options +func_validate_options () +{ + $debug_cmd + + # Display all warnings if -W was not given. + test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" + + func_run_hooks func_validate_options ${1+"$@"} + + # Bail if the options were screwed! + $exit_cmd $EXIT_FAILURE + + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result +} + + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of the +# hookable option parser framework in ascii-betical order. + + +# func_fatal_help ARG... +# ---------------------- +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + eval \$ECHO \""$fatal_help"\" + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + + +# func_help +# --------- +# Echo long help message to standard output and exit. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message" + exit 0 +} + + +# func_missing_arg ARGNAME +# ------------------------ +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + $debug_cmd + + func_error "Missing argument for '$1'." + exit_cmd=exit +} + + +# func_split_equals STRING +# ------------------------ +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. +test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=${1%%=*} + func_split_equals_rhs=${1#*=} + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` + func_split_equals_rhs= + test "x$func_split_equals_lhs" = "x$1" \ + || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` + } +fi #func_split_equals + + +# func_split_short_opt SHORTOPT +# ----------------------------- +# Set func_split_short_opt_name and func_split_short_opt_arg shell +# variables after splitting SHORTOPT after the 2nd character. +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` + func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` + } +fi #func_split_short_opt + + +# func_usage +# ---------- +# Echo short help message to standard output and exit. +func_usage () +{ + $debug_cmd + + func_usage_message + $ECHO "Run '$progname --help |${PAGER-more}' for full usage" + exit 0 +} + + +# func_usage_message +# ------------------ +# Echo short help message to standard output. +func_usage_message () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + echo + $SED -n 's|^# || + /^Written by/{ + x;p;x + } + h + /^Written by/q' < "$progpath" + echo + eval \$ECHO \""$usage_message"\" +} + + +# func_version +# ------------ +# Echo version message to standard output and exit. +func_version () +{ + $debug_cmd + + printf '%s\n' "$progname $scriptversion" + $SED -n ' + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more + } + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p + } + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" + + exit $? +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: + +# Set a version string. +scriptversion='(GNU libtool) 2.4.6' + + +# func_echo ARG... +# ---------------- +# Libtool also displays the current mode in messages, so override +# funclib.sh func_echo with this custom definition. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_warning ARG... +# ------------------- +# Libtool warnings are not categorized, so override funclib.sh +# func_warning with this simpler definition. +func_warning () +{ + $debug_cmd + + $warning_func ${1+"$@"} +} + + +## ---------------- ## +## Options parsing. ## +## ---------------- ## + +# Hook in the functions to make sure our own options are parsed during +# the option parsing loop. + +usage='$progpath [OPTION]... [MODE-ARG]...' + +# Short help message in response to '-h'. +usage_message="Options: + --config show all configuration variables + --debug enable verbose shell tracing + -n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --mode=MODE use operation mode MODE + --no-warnings equivalent to '-Wnone' + --preserve-dup-deps don't remove duplicate dependency libraries + --quiet, --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + -v, --verbose print more informational messages than default + --version print version information + -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] + -h, --help, --help-all print short, long, or detailed help message +" + +# Additional text appended to 'usage_message' in response to '--help'. +func_help () +{ + $debug_cmd + + func_usage_message + $ECHO "$long_help_message + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. When passed as first option, +'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. +Try '$progname --help --mode=MODE' for a more detailed description of MODE. + +When reporting a bug, please describe a test case to reproduce it and +include the following information: + + host-triplet: $host + shell: $SHELL + compiler: $LTCC + compiler flags: $LTCFLAGS + linker: $LD (gnu? $with_gnu_ld) + version: $progname (GNU libtool) 2.4.6 + automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` + autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` + +Report bugs to . +GNU libtool home page: . +General help using GNU software: ." + exit 0 +} + + +# func_lo2o OBJECT-NAME +# --------------------- +# Transform OBJECT-NAME from a '.lo' suffix to the platform specific +# object suffix. + +lo2o=s/\\.lo\$/.$objext/ +o2lo=s/\\.$objext\$/.lo/ + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_lo2o () + { + case $1 in + *.lo) func_lo2o_result=${1%.lo}.$objext ;; + * ) func_lo2o_result=$1 ;; + esac + }' + + # func_xform LIBOBJ-OR-SOURCE + # --------------------------- + # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) + # suffix to a '.lo' libtool-object suffix. + eval 'func_xform () + { + func_xform_result=${1%.*}.lo + }' +else + # ...otherwise fall back to using sed. + func_lo2o () + { + func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` + } + + func_xform () + { + func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` + } +fi + + +# func_fatal_configuration ARG... +# ------------------------------- +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func__fatal_error ${1+"$@"} \ + "See the $PACKAGE documentation for more information." \ + "Fatal configuration error." +} + + +# func_config +# ----------- +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" + + # Now print the configurations for the tags. + for tagname in $taglist; do + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" + done + + exit $? +} + + +# func_features +# ------------- +# Display the features supported by this script. +func_features () +{ + echo "host: $host" + if test yes = "$build_libtool_libs"; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test yes = "$build_old_libs"; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + + exit $? +} + + +# func_enable_tag TAGNAME +# ----------------------- +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname=$1 + + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf=/$re_begincf/,/$re_endcf/p + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac + + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + + +# func_check_version_match +# ------------------------ +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +# libtool_options_prep [ARG]... +# ----------------------------- +# Preparation for options parsed by libtool. +libtool_options_prep () +{ + $debug_mode + + # Option defaults: + opt_config=false + opt_dlopen= + opt_dry_run=false + opt_help=false + opt_mode= + opt_preserve_dup_deps=false + opt_quiet=false + + nonopt= + preserve_args= + + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; + esac + + # Pass back the list of options. + func_quote_for_eval ${1+"$@"} + libtool_options_prep_result=$func_quote_for_eval_result +} +func_add_hook func_options_prep libtool_options_prep + + +# libtool_parse_options [ARG]... +# --------------------------------- +# Provide handling for libtool specific options. +libtool_parse_options () +{ + $debug_cmd + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_opt=$1 + shift + case $_G_opt in + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + + --config) func_config ;; + + --dlopen|-dlopen) + opt_dlopen="${opt_dlopen+$opt_dlopen +}$1" + shift + ;; + + --preserve-dup-deps) + opt_preserve_dup_deps=: ;; + + --features) func_features ;; + + --finish) set dummy --mode finish ${1+"$@"}; shift ;; + + --help) opt_help=: ;; + + --help-all) opt_help=': help-all' ;; + + --mode) test $# = 0 && func_missing_arg $_G_opt && break + opt_mode=$1 + case $1 in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $_G_opt" + exit_cmd=exit + break + ;; + esac + shift + ;; + + --no-silent|--no-quiet) + opt_quiet=false + func_append preserve_args " $_G_opt" + ;; + + --no-warnings|--no-warning|--no-warn) + opt_warning=false + func_append preserve_args " $_G_opt" + ;; + + --no-verbose) + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --silent|--quiet) + opt_quiet=: + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --tag) test $# = 0 && func_missing_arg $_G_opt && break + opt_tag=$1 + func_append preserve_args " $_G_opt $1" + func_enable_tag "$1" + shift + ;; + + --verbose|-v) opt_quiet=false + opt_verbose=: + func_append preserve_args " $_G_opt" + ;; + + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;; + esac + done + + + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + libtool_parse_options_result=$func_quote_for_eval_result +} +func_add_hook func_parse_options libtool_parse_options + + + +# libtool_validate_options [ARG]... +# --------------------------------- +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +libtool_validate_options () +{ + # save first non-option argument + if test 0 -lt $#; then + nonopt=$1 + shift + fi + + # preserve --debug + test : = "$debug_cmd" || func_append preserve_args " --debug" + + case $host in + # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 + # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 + *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + test yes != "$build_libtool_libs" \ + && test yes != "$build_old_libs" \ + && func_fatal_configuration "not configured to build any kind of library" + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test execute != "$opt_mode"; then + func_error "unrecognized option '-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help=$help + help="Try '$progname --help --mode=$opt_mode' for more information." + } + + # Pass back the unparsed argument list + func_quote_for_eval ${1+"$@"} + libtool_validate_options_result=$func_quote_for_eval_result +} +func_add_hook func_validate_options libtool_validate_options + + +# Process options as early as possible so that --help and --version +# can return quickly. +func_options ${1+"$@"} +eval set dummy "$func_options_result"; shift + + + +## ----------- ## +## Main. ## +## ----------- ## + +magic='%%%MAGIC variable%%%' +magic_exe='%%%MAGIC EXE variable%%%' + +# Global variables. +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# func_generated_by_libtool +# True iff stdin has been generated by Libtool. This function is only +# a basic sanity check; it will hardly flush out determined imposters. +func_generated_by_libtool_p () +{ + $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool '.la' library or '.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if 'file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case $lalib_p_line in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test yes = "$lalib_p" +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + test -f "$1" && + $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $debug_cmd + + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# 'FILE.' does not work on cygwin managed mounts. +func_source () +{ + $debug_cmd + + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_resolve_sysroot PATH +# Replace a leading = in PATH with a sysroot. Store the result into +# func_resolve_sysroot_result +func_resolve_sysroot () +{ + func_resolve_sysroot_result=$1 + case $func_resolve_sysroot_result in + =*) + func_stripname '=' '' "$func_resolve_sysroot_result" + func_resolve_sysroot_result=$lt_sysroot$func_stripname_result + ;; + esac +} + +# func_replace_sysroot PATH +# If PATH begins with the sysroot, replace it with = and +# store the result into func_replace_sysroot_result. +func_replace_sysroot () +{ + case $lt_sysroot:$1 in + ?*:"$lt_sysroot"*) + func_stripname "$lt_sysroot" '' "$1" + func_replace_sysroot_result='='$func_stripname_result + ;; + *) + # Including no sysroot. + func_replace_sysroot_result=$1 + ;; + esac +} + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $debug_cmd + + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_append_quoted CC_quoted "$arg" + done + CC_expanded=`func_echo_all $CC` + CC_quoted_expanded=`func_echo_all $CC_quoted` + case "$@ " in + " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \ + " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with '--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=$1 + if test yes = "$build_libtool_libs"; then + write_lobj=\'$2\' + else + write_lobj=none + fi + + if test yes = "$build_old_libs"; then + write_oldobj=\'$3\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T </dev/null` + if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then + func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | + $SED -e "$sed_naive_backslashify"` + else + func_convert_core_file_wine_to_w32_result= + fi + fi +} +# end: func_convert_core_file_wine_to_w32 + + +# func_convert_core_path_wine_to_w32 ARG +# Helper function used by path conversion functions when $build is *nix, and +# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly +# configured wine environment available, with the winepath program in $build's +# $PATH. Assumes ARG has no leading or trailing path separator characters. +# +# ARG is path to be converted from $build format to win32. +# Result is available in $func_convert_core_path_wine_to_w32_result. +# Unconvertible file (directory) names in ARG are skipped; if no directory names +# are convertible, then the result may be empty. +func_convert_core_path_wine_to_w32 () +{ + $debug_cmd + + # unfortunately, winepath doesn't convert paths, only file names + func_convert_core_path_wine_to_w32_result= + if test -n "$1"; then + oldIFS=$IFS + IFS=: + for func_convert_core_path_wine_to_w32_f in $1; do + IFS=$oldIFS + func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" + if test -n "$func_convert_core_file_wine_to_w32_result"; then + if test -z "$func_convert_core_path_wine_to_w32_result"; then + func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result + else + func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" + fi + fi + done + IFS=$oldIFS + fi +} +# end: func_convert_core_path_wine_to_w32 + + +# func_cygpath ARGS... +# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when +# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2) +# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or +# (2), returns the Cygwin file name or path in func_cygpath_result (input +# file name or path is assumed to be in w32 format, as previously converted +# from $build's *nix or MSYS format). In case (3), returns the w32 file name +# or path in func_cygpath_result (input file name or path is assumed to be in +# Cygwin format). Returns an empty string on error. +# +# ARGS are passed to cygpath, with the last one being the file name or path to +# be converted. +# +# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH +# environment variable; do not put it in $PATH. +func_cygpath () +{ + $debug_cmd + + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then + func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` + if test "$?" -ne 0; then + # on failure, ensure result is empty + func_cygpath_result= + fi + else + func_cygpath_result= + func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" + fi +} +#end: func_cygpath + + +# func_convert_core_msys_to_w32 ARG +# Convert file name or path ARG from MSYS format to w32 format. Return +# result in func_convert_core_msys_to_w32_result. +func_convert_core_msys_to_w32 () +{ + $debug_cmd + + # awkward: cmd appends spaces to result + func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | + $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` +} +#end: func_convert_core_msys_to_w32 + + +# func_convert_file_check ARG1 ARG2 +# Verify that ARG1 (a file name in $build format) was converted to $host +# format in ARG2. Otherwise, emit an error message, but continue (resetting +# func_to_host_file_result to ARG1). +func_convert_file_check () +{ + $debug_cmd + + if test -z "$2" && test -n "$1"; then + func_error "Could not determine host file name corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_file_result=$1 + fi +} +# end func_convert_file_check + + +# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH +# Verify that FROM_PATH (a path in $build format) was converted to $host +# format in TO_PATH. Otherwise, emit an error message, but continue, resetting +# func_to_host_file_result to a simplistic fallback value (see below). +func_convert_path_check () +{ + $debug_cmd + + if test -z "$4" && test -n "$3"; then + func_error "Could not determine the host path corresponding to" + func_error " '$3'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This is a deliberately simplistic "conversion" and + # should not be "improved". See libtool.info. + if test "x$1" != "x$2"; then + lt_replace_pathsep_chars="s|$1|$2|g" + func_to_host_path_result=`echo "$3" | + $SED -e "$lt_replace_pathsep_chars"` + else + func_to_host_path_result=$3 + fi + fi +} +# end func_convert_path_check + + +# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG +# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT +# and appending REPL if ORIG matches BACKPAT. +func_convert_path_front_back_pathsep () +{ + $debug_cmd + + case $4 in + $1 ) func_to_host_path_result=$3$func_to_host_path_result + ;; + esac + case $4 in + $2 ) func_append func_to_host_path_result "$3" + ;; + esac +} +# end func_convert_path_front_back_pathsep + + +################################################## +# $build to $host FILE NAME CONVERSION FUNCTIONS # +################################################## +# invoked via '$to_host_file_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# Result will be available in $func_to_host_file_result. + + +# func_to_host_file ARG +# Converts the file name ARG from $build format to $host format. Return result +# in func_to_host_file_result. +func_to_host_file () +{ + $debug_cmd + + $to_host_file_cmd "$1" +} +# end func_to_host_file + + +# func_to_tool_file ARG LAZY +# converts the file name ARG from $build format to toolchain format. Return +# result in func_to_tool_file_result. If the conversion in use is listed +# in (the comma separated) LAZY, no conversion takes place. +func_to_tool_file () +{ + $debug_cmd + + case ,$2, in + *,"$to_tool_file_cmd",*) + func_to_tool_file_result=$1 + ;; + *) + $to_tool_file_cmd "$1" + func_to_tool_file_result=$func_to_host_file_result + ;; + esac +} +# end func_to_tool_file + + +# func_convert_file_noop ARG +# Copy ARG to func_to_host_file_result. +func_convert_file_noop () +{ + func_to_host_file_result=$1 +} +# end func_convert_file_noop + + +# func_convert_file_msys_to_w32 ARG +# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_file_result. +func_convert_file_msys_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_to_host_file_result=$func_convert_core_msys_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_w32 + + +# func_convert_file_cygwin_to_w32 ARG +# Convert file name ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_file_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # because $build is cygwin, we call "the" cygpath in $PATH; no need to use + # LT_CYGPATH in this case. + func_to_host_file_result=`cygpath -m "$1"` + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_cygwin_to_w32 + + +# func_convert_file_nix_to_w32 ARG +# Convert file name ARG from *nix to w32 format. Requires a wine environment +# and a working winepath. Returns result in func_to_host_file_result. +func_convert_file_nix_to_w32 () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_file_wine_to_w32 "$1" + func_to_host_file_result=$func_convert_core_file_wine_to_w32_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_w32 + + +# func_convert_file_msys_to_cygwin ARG +# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_file_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + func_convert_core_msys_to_w32 "$1" + func_cygpath -u "$func_convert_core_msys_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_msys_to_cygwin + + +# func_convert_file_nix_to_cygwin ARG +# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed +# in a wine environment, working winepath, and LT_CYGPATH set. Returns result +# in func_to_host_file_result. +func_convert_file_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_file_result=$1 + if test -n "$1"; then + # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. + func_convert_core_file_wine_to_w32 "$1" + func_cygpath -u "$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result=$func_cygpath_result + fi + func_convert_file_check "$1" "$func_to_host_file_result" +} +# end func_convert_file_nix_to_cygwin + + +############################################# +# $build to $host PATH CONVERSION FUNCTIONS # +############################################# +# invoked via '$to_host_path_cmd ARG' +# +# In each case, ARG is the path to be converted from $build to $host format. +# The result will be available in $func_to_host_path_result. +# +# Path separators are also converted from $build format to $host format. If +# ARG begins or ends with a path separator character, it is preserved (but +# converted to $host format) on output. +# +# All path conversion functions are named using the following convention: +# file name conversion function : func_convert_file_X_to_Y () +# path conversion function : func_convert_path_X_to_Y () +# where, for any given $build/$host combination the 'X_to_Y' value is the +# same. If conversion functions are added for new $build/$host combinations, +# the two new functions must follow this pattern, or func_init_to_host_path_cmd +# will break. + + +# func_init_to_host_path_cmd +# Ensures that function "pointer" variable $to_host_path_cmd is set to the +# appropriate value, based on the value of $to_host_file_cmd. +to_host_path_cmd= +func_init_to_host_path_cmd () +{ + $debug_cmd + + if test -z "$to_host_path_cmd"; then + func_stripname 'func_convert_file_' '' "$to_host_file_cmd" + to_host_path_cmd=func_convert_path_$func_stripname_result + fi +} + + +# func_to_host_path ARG +# Converts the path ARG from $build format to $host format. Return result +# in func_to_host_path_result. +func_to_host_path () +{ + $debug_cmd + + func_init_to_host_path_cmd + $to_host_path_cmd "$1" +} +# end func_to_host_path + + +# func_convert_path_noop ARG +# Copy ARG to func_to_host_path_result. +func_convert_path_noop () +{ + func_to_host_path_result=$1 +} +# end func_convert_path_noop + + +# func_convert_path_msys_to_w32 ARG +# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic +# conversion to w32 is not available inside the cwrapper. Returns result in +# func_to_host_path_result. +func_convert_path_msys_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from ARG. MSYS + # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; + # and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_msys_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_msys_to_w32 + + +# func_convert_path_cygwin_to_w32 ARG +# Convert path ARG from Cygwin to w32 format. Returns result in +# func_to_host_file_result. +func_convert_path_cygwin_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"` + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_cygwin_to_w32 + + +# func_convert_path_nix_to_w32 ARG +# Convert path ARG from *nix to w32 format. Requires a wine environment and +# a working winepath. Returns result in func_to_host_file_result. +func_convert_path_nix_to_w32 () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_to_host_path_result=$func_convert_core_path_wine_to_w32_result + func_convert_path_check : ";" \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" + fi +} +# end func_convert_path_nix_to_w32 + + +# func_convert_path_msys_to_cygwin ARG +# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set. +# Returns result in func_to_host_file_result. +func_convert_path_msys_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # See func_convert_path_msys_to_w32: + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_msys_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_msys_to_cygwin + + +# func_convert_path_nix_to_cygwin ARG +# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a +# a wine environment, working winepath, and LT_CYGPATH set. Returns result in +# func_to_host_file_result. +func_convert_path_nix_to_cygwin () +{ + $debug_cmd + + func_to_host_path_result=$1 + if test -n "$1"; then + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_stripname : : "$1" + func_to_host_path_tmp1=$func_stripname_result + func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" + func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result=$func_cygpath_result + func_convert_path_check : : \ + "$func_to_host_path_tmp1" "$func_to_host_path_result" + func_convert_path_front_back_pathsep ":*" "*:" : "$1" + fi +} +# end func_convert_path_nix_to_cygwin + + +# func_dll_def_p FILE +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with _LT_DLL_DEF_P in libtool.m4 +func_dll_def_p () +{ + $debug_cmd + + func_dll_def_p_tmp=`$SED -n \ + -e 's/^[ ]*//' \ + -e '/^\(;.*\)*$/d' \ + -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ + -e q \ + "$1"` + test DEF = "$func_dll_def_p_tmp" +} + + +# func_mode_compile arg... +func_mode_compile () +{ + $debug_cmd + + # Get the compilation command and the source file. + base_compile= + srcfile=$nonopt # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + pie_flag= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg=$arg + arg_mode=normal + ;; + + target ) + libobj=$arg + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + test -n "$libobj" && \ + func_fatal_error "you cannot specify '-o' more than once" + arg_mode=target + continue + ;; + + -pie | -fpie | -fPIE) + func_append pie_flag " $arg" + continue + ;; + + -shared | -static | -prefer-pic | -prefer-non-pic) + func_append later " $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + lastarg= + save_ifs=$IFS; IFS=, + for arg in $args; do + IFS=$save_ifs + func_append_quoted lastarg "$arg" + done + IFS=$save_ifs + func_stripname ' ' '' "$lastarg" + lastarg=$func_stripname_result + + # Add the arguments to base_compile. + func_append base_compile " $lastarg" + continue + ;; + + *) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg=$srcfile + srcfile=$arg + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + func_append_quoted base_compile "$lastarg" + done # for arg + + case $arg_mode in + arg) + func_fatal_error "you must specify an argument for -Xcompile" + ;; + target) + func_fatal_error "you must specify a target with '-o'" + ;; + *) + # Get the name of the library object. + test -z "$libobj" && { + func_basename "$srcfile" + libobj=$func_basename_result + } + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + case $libobj in + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) + func_xform "$libobj" + libobj=$func_xform_result + ;; + esac + + case $libobj in + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; + *) + func_fatal_error "cannot determine name of library object from '$libobj'" + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -shared) + test yes = "$build_libtool_libs" \ + || func_fatal_configuration "cannot build a shared library" + build_old_libs=no + continue + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name '$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname=$func_basename_result + xdir=$func_dirname_result + lobj=$xdir$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test yes = "$build_old_libs"; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test no = "$compiler_c_o"; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext + lockfile=$output_obj.lock + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test yes = "$need_locks"; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test warn = "$need_locks"; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + func_append removelist " $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + func_append removelist " $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + func_to_tool_file "$srcfile" func_convert_file_msys_to_w32 + srcfile=$func_to_tool_file_result + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test yes = "$build_libtool_libs"; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test no != "$pic_mode"; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + func_append command " -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test yes = "$suppress_opt"; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test yes = "$build_old_libs"; then + if test yes != "$pic_mode"; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test yes = "$compiler_c_o"; then + func_append command " -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + func_append command "$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test warn = "$need_locks" && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support '-c' and '-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test no != "$need_locks"; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { + test compile = "$opt_mode" && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $opt_mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to build PIC objects only + -prefer-non-pic try to build non-PIC objects only + -shared do not build a '.o' file suitable for static linking + -static only build a '.o' file suitable for static linking + -Wc,FLAG pass FLAG directly to the compiler + +COMPILE-COMMAND is a command to be used in creating a 'standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix '.c' with the +library object suffix, '.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to '-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the '--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the 'install' or 'cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -bindir BINDIR specify path to binaries directory (for systems where + libraries must be found in the PATH setting at runtime) + -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE use a list of object files found in FILE to specify objects + -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + -Wc,FLAG + -Xcompiler FLAG pass linker-specific FLAG directly to the compiler + -Wl,FLAG + -Xlinker FLAG pass linker-specific FLAG directly to the linker + -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) + +All other options (arguments beginning with '-') are ignored. + +Every other argument is treated as a filename. Files ending in '.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in '.la', then a libtool library is created, +only library objects ('.lo' files) may be specified, and '-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created +using 'ar' and 'ranlib', or on Windows using 'lib'. + +If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode '$opt_mode'" + ;; + esac + + echo + $ECHO "Try '$progname --help' for more information about other modes." +} + +# Now that we've collected a possible --mode arg, show help if necessary +if $opt_help; then + if test : = "$opt_help"; then + func_mode_help + else + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + func_mode_help + done + } | $SED -n '1p; 2,$s/^Usage:/ or: /p' + { + func_help noexit + for opt_mode in compile link execute install finish uninstall clean; do + echo + func_mode_help + done + } | + $SED '1d + /^When reporting/,/^Report/{ + H + d + } + $x + /information about other modes/d + /more detailed .*MODE/d + s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/' + fi + exit $? +fi + + +# func_mode_execute arg... +func_mode_execute () +{ + $debug_cmd + + # The first argument is the command name. + cmd=$nonopt + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $opt_dlopen; do + test -f "$file" \ + || func_fatal_help "'$file' is not a file" + + dir= + case $file in + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "'$file' was not linked with '-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir=$func_dirname_result + + if test -f "$dir/$objdir/$dlname"; then + func_append dir "/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir=$func_dirname_result + ;; + + *) + func_warning "'-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir=$absdir + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic=$magic + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -* | *.la | *.lo ) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file=$progdir/$program + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file=$progdir/$program + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_append_quoted args "$file" + done + + if $opt_dry_run; then + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + else + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd=\$cmd$args + fi +} + +test execute = "$opt_mode" && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $debug_cmd + + libs= + libdirs= + admincmds= + + for opt in "$nonopt" ${1+"$@"} + do + if test -d "$opt"; then + func_append libdirs " $opt" + + elif test -f "$opt"; then + if func_lalib_unsafe_p "$opt"; then + func_append libs " $opt" + else + func_warning "'$opt' is not a valid libtool archive" + fi + + else + func_fatal_error "invalid argument '$opt'" + fi + done + + if test -n "$libs"; then + if test -n "$lt_sysroot"; then + sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"` + sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;" + else + sysroot_cmd= + fi + + # Remove sysroot references + if $opt_dry_run; then + for lib in $libs; do + echo "removing references to $lt_sysroot and '=' prefixes from $lib" + done + else + tmpdir=`func_mktempdir` + for lib in $libs; do + $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + > $tmpdir/tmp-la + mv -f $tmpdir/tmp-la $lib + done + ${RM}r "$tmpdir" + fi + fi + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || func_append admincmds " + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_quiet && exit $EXIT_SUCCESS + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + echo "----------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + echo + echo "If you ever happen to want to link against installed libraries" + echo "in a given directory, LIBDIR, you must either use libtool, and" + echo "specify the full pathname of the library, or use the '-LLIBDIR'" + echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the '$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the '$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the '$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" + fi + echo + + echo "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + echo "more information, such as the ld(1), crle(1) and ld.so(8) manual" + echo "pages." + ;; + *) + echo "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + echo "----------------------------------------------------------------------" + fi + exit $EXIT_SUCCESS +} + +test finish = "$opt_mode" && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $debug_cmd + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || + # Allow the use of GNU shtool's install command. + case $nonopt in *shtool*) :;; *) false;; esac + then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + func_append install_prog "$func_quote_for_eval_result" + install_shared_prog=$install_prog + case " $install_prog " in + *[\\\ /]cp\ *) install_cp=: ;; + *) install_cp=false ;; + esac + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=false + stripme= + no_mode=: + for arg + do + arg2= + if test -n "$dest"; then + func_append files " $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=: ;; + -f) + if $install_cp; then :; else + prev=$arg + fi + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + if test X-m = "X$prev" && test -n "$install_override_mode"; then + arg2=$install_override_mode + no_mode=false + fi + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + func_append install_prog " $func_quote_for_eval_result" + if test -n "$arg2"; then + func_quote_for_eval "$arg2" + fi + func_append install_shared_prog " $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the '$prev' option requires an argument" + + if test -n "$install_override_mode" && $no_mode; then + if $install_cp; then :; else + func_quote_for_eval "$install_override_mode" + func_append install_shared_prog " -m $func_quote_for_eval_result" + fi + fi + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=: + if $isdir; then + destdir=$dest + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir=$func_dirname_result + destname=$func_basename_result + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "'$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "'$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + func_append staticlibs " $file" + ;; + + *.la) + func_resolve_sysroot "$file" + file=$func_resolve_sysroot_result + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "'$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) func_append current_libdirs " $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) func_append future_libdirs " $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir=$func_dirname_result + func_append dir "$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking '$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname=$1 + shift + + srcname=$realname + test -n "$relink_command" && srcname=${realname}T + + # Install the shared library and build the symlinks. + func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme=$stripme + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme= + ;; + esac + ;; + os2*) + case $realname in + *_dll.a) + tstripme= + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try 'ln -sf' first, because the 'ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib=$destdir/$realname + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name=$func_basename_result + instname=$dir/${name}i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && func_append staticlibs " $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest=$destfile + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to '$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test yes = "$build_old_libs"; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile=$destdir/$destname + else + func_basename "$file" + destfile=$func_basename_result + destfile=$destdir/$destfile + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext= + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=.exe + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script '$wrapper'" + + finalize=: + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "'$lib' has not been installed in '$libdir'" + finalize=false + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test no = "$fast_install" && test -n "$relink_command"; then + $opt_dry_run || { + if $finalize; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file=$func_basename_result + outputname=$tmpdir/$file + # Replace the output file specification. + relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_quiet || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink '$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file=$outputname + else + func_warning "cannot relink '$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name=$func_basename_result + + # Set up the ranlib parameters. + oldlib=$destdir/$name + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run '$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test install = "$opt_mode" && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $debug_cmd + + my_outputname=$1 + my_originator=$2 + my_pic_p=${3-false} + my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms=${my_outputname}S.c + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist=$output_objdir/$my_outputname.nm + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#pragma GCC diagnostic ignored \"-Wstrict-prototypes\" +#endif + +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT_DLSYM_CONST +#else +# define LT_DLSYM_CONST const +#endif + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* External symbol declarations for the compiler. */\ +" + + if test yes = "$dlself"; then + func_verbose "generating symbol list for '$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_to_tool_file "$progfile" func_convert_file_msys_to_w32 + func_verbose "extracting global C symbols from '$func_to_tool_file_result'" + $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols=$output_objdir/$outputname.exp + $opt_dry_run || { + $RM $export_symbols + eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from '$dlprefile'" + func_basename "$dlprefile" + name=$func_basename_result + case $host in + *cygwin* | *mingw* | *cegcc* ) + # if an import library, we need to obtain dlname + if func_win32_import_lib_p "$dlprefile"; then + func_tr_sh "$dlprefile" + eval "curr_lafile=\$libfile_$func_tr_sh_result" + dlprefile_dlbasename= + if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then + # Use subshell, to avoid clobbering current variable values + dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` + if test -n "$dlprefile_dlname"; then + func_basename "$dlprefile_dlname" + dlprefile_dlbasename=$func_basename_result + else + # no lafile. user explicitly requested -dlpreopen . + $sharedlib_from_linklib_cmd "$dlprefile" + dlprefile_dlbasename=$sharedlib_from_linklib_result + fi + fi + $opt_dry_run || { + if test -n "$dlprefile_dlbasename"; then + eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' + else + func_warning "Could not compute DLL name from $name" + eval '$ECHO ": $name " >> "$nlist"' + fi + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe | + $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'" + } + else # not an import lib + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + fi + ;; + *) + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32 + eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + ;; + esac + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + echo '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + func_show_eval '$RM "${nlist}I"' + if test -n "$global_symbol_to_import"; then + eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +extern LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[];\ +" + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ +static void lt_syminit(void) +{ + LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; + for (; symbol->name; ++symbol) + {" + $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" + echo >> "$output_objdir/$my_dlsyms" "\ + } +}" + fi + echo >> "$output_objdir/$my_dlsyms" "\ +LT_DLSYM_CONST lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{ {\"$my_originator\", (void *) 0}," + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ + {\"@INIT@\", (void *) <_syminit}," + fi + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + echo >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + $my_pic_p && pic_flag_for_symtable=" $pic_flag" + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) func_append symtab_cflags " $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' + + # Transform the symbol file into the correct name. + symfileobj=$output_objdir/${my_outputname}S.$objext + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for '$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"` + finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"` + fi +} + +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +# Despite the name, also deal with 64 bit binaries. +func_win32_libid () +{ + $debug_cmd + + win32_libid_type=unknown + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then + case $nm_interface in + "MS dumpbin") + if func_cygming_ms_implib_p "$1" || + func_cygming_gnu_implib_p "$1" + then + win32_nmres=import + else + win32_nmres= + fi + ;; + *) + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' + 1,100{ + / I /{ + s|.*|import| + p + q + } + }'` + ;; + esac + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + +# func_cygming_dll_for_implib ARG +# +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib () +{ + $debug_cmd + + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` +} + +# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs +# +# The is the core of a fallback implementation of a +# platform-specific function to extract the name of the +# DLL associated with the specified import library LIBNAME. +# +# SECTION_NAME is either .idata$6 or .idata$7, depending +# on the platform and compiler that created the implib. +# +# Echos the name of the DLL associated with the +# specified import library. +func_cygming_dll_for_implib_fallback_core () +{ + $debug_cmd + + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` + $OBJDUMP -s --section "$1" "$2" 2>/dev/null | + $SED '/^Contents of section '"$match_literal"':/{ + # Place marker at beginning of archive member dllname section + s/.*/====MARK====/ + p + d + } + # These lines can sometimes be longer than 43 characters, but + # are always uninteresting + /:[ ]*file format pe[i]\{,1\}-/d + /^In archive [^:]*:/d + # Ensure marker is printed + /^====MARK====/p + # Remove all lines with less than 43 characters + /^.\{43\}/!d + # From remaining lines, remove first 43 characters + s/^.\{43\}//' | + $SED -n ' + # Join marker and all lines until next marker into a single line + /^====MARK====/ b para + H + $ b para + b + :para + x + s/\n//g + # Remove the marker + s/^====MARK====// + # Remove trailing dots and whitespace + s/[\. \t]*$// + # Print + /./p' | + # we now have a list, one entry per line, of the stringified + # contents of the appropriate section of all members of the + # archive that possess that section. Heuristic: eliminate + # all those that have a first or second character that is + # a '.' (that is, objdump's representation of an unprintable + # character.) This should work for all archives with less than + # 0x302f exports -- but will fail for DLLs whose name actually + # begins with a literal '.' or a single character followed by + # a '.'. + # + # Of those that remain, print the first one. + $SED -e '/^\./d;/^.\./d;q' +} + +# func_cygming_dll_for_implib_fallback ARG +# Platform-specific function to extract the +# name of the DLL associated with the specified +# import library ARG. +# +# This fallback implementation is for use when $DLLTOOL +# does not support the --identify-strict option. +# Invoked by eval'ing the libtool variable +# $sharedlib_from_linklib_cmd +# Result is available in the variable +# $sharedlib_from_linklib_result +func_cygming_dll_for_implib_fallback () +{ + $debug_cmd + + if func_cygming_gnu_implib_p "$1"; then + # binutils import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` + elif func_cygming_ms_implib_p "$1"; then + # ms-generated import library + sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` + else + # unknown + sharedlib_from_linklib_result= + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $debug_cmd + + f_ex_an_ar_dir=$1; shift + f_ex_an_ar_oldlib=$1 + if test yes = "$lock_old_archive_extraction"; then + lockfile=$f_ex_an_ar_oldlib.lock + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + fi + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ + 'stat=$?; rm -f "$lockfile"; exit $stat' + if test yes = "$lock_old_archive_extraction"; then + $opt_dry_run || rm -f "$lockfile" + fi + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $debug_cmd + + my_gentop=$1; shift + my_oldlibs=${1+"$@"} + my_oldobjs= + my_xlib= + my_xabs= + my_xdir= + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib=$func_basename_result + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir=$my_gentop/$my_xlib_u + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + func_basename "$darwin_archive" + darwin_base_archive=$func_basename_result + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches; do + func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" + $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" + cd "unfat-$$/$darwin_base_archive-$darwin_arch" + func_extract_an_archive "`pwd`" "$darwin_base_archive" + cd "$darwin_curdir" + $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` + done + + func_extract_archives_result=$my_oldobjs +} + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory where it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=${1-no} + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + file=\"\$0\"" + + qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"` + $ECHO "\ + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$1 +_LTECHO_EOF' +} + ECHO=\"$qECHO\" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ that is used only on +# windows platforms, and (c) all begin with the string "--lt-" +# (application programs are unlikely to have options that match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's $0 value, followed by "$@". +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=\$0 + shift + for lt_opt + do + case \"\$lt_opt\" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\` + test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=. + lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\` + cat \"\$lt_dump_D/\$lt_dump_F\" + exit 0 + ;; + --lt-*) + \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n \"\$lt_option_debug\"; then + echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" + lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + if test -n \"\$lt_option_debug\"; then + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 + func_lt_dump_args \${1+\"\$@\"} 1>&2 + fi + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from \$@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac + func_exec_program_core \${1+\"\$@\"} +} + + # Parse options + func_parse_lt_options \"\$0\" \${1+\"\$@\"} + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test yes = "$fast_install"; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + \$ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # fixup the dll searchpath if we need to. + # + # Fix the DLL searchpath if we need to. Do this before prepending + # to shlibpath, because on Windows, both are PATH and uninstalled + # libraries must come first. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + # Export our shlibpath_var if we have one. + if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\` + + export $shlibpath_var +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + func_exec_program \${1+\"\$@\"} + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} + + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +#else +# include +# include +# ifdef __CYGWIN__ +# include +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + +/* declarations of non-ANSI functions */ +#if defined __MINGW32__ +# ifdef __STRICT_ANSI__ +int _putenv (const char *); +# endif +#elif defined __CYGWIN__ +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +/* #elif defined other_platform || defined ... */ +#endif + +/* portability defines, excluding path handling macros */ +#if defined _MSC_VER +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +# define S_IXUSR _S_IEXEC +#elif defined __MINGW32__ +# define setmode _setmode +# define stat _stat +# define chmod _chmod +# define getcwd _getcwd +# define putenv _putenv +#elif defined __CYGWIN__ +# define HAVE_SETENV +# define FOPEN_WB "wb" +/* #elif defined other platforms ... */ +#endif + +#if defined PATH_MAX +# define LT_PATHMAX PATH_MAX +#elif defined MAXPATHLEN +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +/* path handling portability macros */ +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ + defined __OS2__ +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free (stale); stale = 0; } \ +} while (0) + +#if defined LT_DEBUGWRAPPER +static int lt_debug = 1; +#else +static int lt_debug = 0; +#endif + +const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */ + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_debugprintf (const char *file, int line, const char *fmt, ...); +void lt_fatal (const char *file, int line, const char *message, ...); +static const char *nonnull (const char *s); +static const char *nonempty (const char *s); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); +char **prepare_spawn (char **argv); +void lt_dump_script (FILE *f); +EOF + + cat <= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n", + nonempty (path)); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + size_t tmp_len; + char *concat_name; + + lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n", + nonempty (wrapper)); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined HAVE_DOS_BASED_FILE_SYSTEM + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined HAVE_DOS_BASED_FILE_SYSTEM + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = (size_t) (q - p); + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal (__FILE__, __LINE__, "getcwd failed: %s", + nonnull (strerror (errno))); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + lt_debugprintf (__FILE__, __LINE__, + "checking path component for symlinks: %s\n", + tmp_pathspec); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + lt_fatal (__FILE__, __LINE__, + "error accessing file \"%s\": %s", + tmp_pathspec, nonnull (strerror (errno))); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal (__FILE__, __LINE__, + "could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (STREQ (str, pat)) + *str = '\0'; + } + return str; +} + +void +lt_debugprintf (const char *file, int line, const char *fmt, ...) +{ + va_list args; + if (lt_debug) + { + (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line); + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); + } +} + +static void +lt_error_core (int exit_status, const char *file, + int line, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *file, int line, const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap); + va_end (ap); +} + +static const char * +nonnull (const char *s) +{ + return s ? s : "(null)"; +} + +static const char * +nonempty (const char *s) +{ + return (s && !*s) ? "(empty)" : nonnull (s); +} + +void +lt_setenv (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_setenv) setting '%s' to '%s'\n", + nonnull (name), nonnull (value)); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + size_t len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + size_t orig_value_len = strlen (orig_value); + size_t add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + size_t len = strlen (new_value); + while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[--len] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + lt_debugprintf (__FILE__, __LINE__, + "(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + nonnull (name), nonnull (value)); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +EOF + case $host_os in + mingw*) + cat <<"EOF" + +/* Prepares an argument vector before calling spawn(). + Note that spawn() does not by itself call the command interpreter + (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") : + ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&v); + v.dwPlatformId == VER_PLATFORM_WIN32_NT; + }) ? "cmd.exe" : "command.com"). + Instead it simply concatenates the arguments, separated by ' ', and calls + CreateProcess(). We must quote the arguments since Win32 CreateProcess() + interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a + special way: + - Space and tab are interpreted as delimiters. They are not treated as + delimiters if they are surrounded by double quotes: "...". + - Unescaped double quotes are removed from the input. Their only effect is + that within double quotes, space and tab are treated like normal + characters. + - Backslashes not followed by double quotes are not special. + - But 2*n+1 backslashes followed by a double quote become + n backslashes followed by a double quote (n >= 0): + \" -> " + \\\" -> \" + \\\\\" -> \\" + */ +#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" +char ** +prepare_spawn (char **argv) +{ + size_t argc; + char **new_argv; + size_t i; + + /* Count number of arguments. */ + for (argc = 0; argv[argc] != NULL; argc++) + ; + + /* Allocate new argument vector. */ + new_argv = XMALLOC (char *, argc + 1); + + /* Put quoted arguments into the new argument vector. */ + for (i = 0; i < argc; i++) + { + const char *string = argv[i]; + + if (string[0] == '\0') + new_argv[i] = xstrdup ("\"\""); + else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL) + { + int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL); + size_t length; + unsigned int backslashes; + const char *s; + char *quoted_string; + char *p; + + length = 0; + backslashes = 0; + if (quote_around) + length++; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + length += backslashes + 1; + length++; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + length += backslashes + 1; + + quoted_string = XMALLOC (char, length + 1); + + p = quoted_string; + backslashes = 0; + if (quote_around) + *p++ = '"'; + for (s = string; *s != '\0'; s++) + { + char c = *s; + if (c == '"') + { + unsigned int j; + for (j = backslashes + 1; j > 0; j--) + *p++ = '\\'; + } + *p++ = c; + if (c == '\\') + backslashes++; + else + backslashes = 0; + } + if (quote_around) + { + unsigned int j; + for (j = backslashes; j > 0; j--) + *p++ = '\\'; + *p++ = '"'; + } + *p = '\0'; + + new_argv[i] = quoted_string; + } + else + new_argv[i] = (char *) string; + } + new_argv[argc] = NULL; + + return new_argv; +} +EOF + ;; + esac + + cat <<"EOF" +void lt_dump_script (FILE* f) +{ +EOF + func_emit_wrapper yes | + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' + cat <<"EOF" +} +EOF +} +# end: func_emit_cwrapperexe_src + +# func_win32_import_lib_p ARG +# True if ARG is an import lib, as indicated by $file_magic_cmd +func_win32_import_lib_p () +{ + $debug_cmd + + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in + *import*) : ;; + *) false ;; + esac +} + +# func_suncc_cstd_abi +# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! +# Several compiler flags select an ABI that is incompatible with the +# Cstd library. Avoid specifying it if any are in CXXFLAGS. +func_suncc_cstd_abi () +{ + $debug_cmd + + case " $compile_command " in + *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) + suncc_use_cstd_abi=no + ;; + *) + suncc_use_cstd_abi=yes + ;; + esac +} + +# func_mode_link arg... +func_mode_link () +{ + $debug_cmd + + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # what system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll that has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + bindir= + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + os2dllname= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=false + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module=$wl-single_module + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test yes != "$build_libtool_libs" \ + && func_fatal_configuration "cannot build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg=$1 + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + bindir) + bindir=$arg + prev= + continue + ;; + dlfiles|dlprefiles) + $preload || { + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=: + } + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test no = "$dlself"; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test dlprefiles = "$prev"; then + dlself=yes + elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test dlfiles = "$prev"; then + func_append dlfiles " $arg" + else + func_append dlprefiles " $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols=$arg + test -f "$arg" \ + || func_fatal_error "symbol file '$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex=$arg + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) func_append deplibs " $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir=$arg + prev= + continue + ;; + mllvm) + # Clang does not use LLVM to link, so we can simply discard any + # '-mllvm $arg' options when doing the link step. + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# func_append moreargs " $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + if test none != "$pic_object"; then + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + fi + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file '$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + os2dllname) + os2dllname=$arg + prev= + continue + ;; + precious_regex) + precious_files_regex=$arg + prev= + continue + ;; + release) + release=-$arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test rpath = "$prev"; then + case "$rpath " in + *" $arg "*) ;; + *) func_append rpath " $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) func_append xrpath " $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds=$arg + prev= + continue + ;; + weak) + func_append weak_libs " $arg" + prev= + continue + ;; + xcclinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + func_append compiler_flags " $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + func_append linker_flags " $qarg" + func_append compiler_flags " $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg=$arg + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "'-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -bindir) + prev=bindir + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test X-export-symbols = "X$arg"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname "-L" '' "$arg" + if test -z "$func_stripname_result"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between '-L' and '$1'" + else + func_fatal_error "need path for '-L' option" + fi + fi + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of '$dir'" + dir=$absdir + ;; + esac + case "$deplibs " in + *" -L$dir "* | *" $arg "*) + # Will only happen for absolute or sysroot arguments + ;; + *) + # Preserve sysroot, but never include relative directories + case $dir in + [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;; + *) func_append deplibs " -L$dir" ;; + esac + func_append lib_search_path " $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) func_append dllsearchpath ":$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test X-lc = "X$arg" || test X-lm = "X$arg"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test X-lc = "X$arg" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc due to us having libc/libc_r. + test X-lc = "X$arg" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + func_append deplibs " System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test X-lc = "X$arg" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test X-lc = "X$arg" && continue + ;; + esac + elif test X-lc_r = "X$arg"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + func_append deplibs " $arg" + continue + ;; + + -mllvm) + prev=mllvm + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot|--sysroot) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + func_append compiler_flags " $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) func_append new_inherited_linker_flags " $arg" ;; + esac + continue + ;; + + -multi_module) + single_module=$wl-multi_module + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "'-no-install' is ignored for $host" + func_warning "assuming '-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -os2dllname) + prev=os2dllname + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + =*) + func_stripname '=' '' "$dir" + dir=$lt_sysroot$func_stripname_result + ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $func_quote_for_eval_result" + func_append compiler_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs=$IFS; IFS=, + for flag in $args; do + IFS=$save_ifs + func_quote_for_eval "$flag" + func_append arg " $wl$func_quote_for_eval_result" + func_append compiler_flags " $wl$func_quote_for_eval_result" + func_append linker_flags " $func_quote_for_eval_result" + done + IFS=$save_ifs + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + # Flags to be passed through unchanged, with rationale: + # -64, -mips[0-9] enable 64-bit mode for the SGI compiler + # -r[0-9][0-9]* specify processor for the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler + # +DA*, +DD* enable 64-bit mode for the HP compiler + # -q* compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* architecture-specific flags for GCC + # -F/path path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # -fstack-protector* stack protector flags for GCC + # @file GCC response files + # -tp=* Portland pgcc target processor selection + # --sysroot=* for sysroot support + # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files + # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang/GCC memory and address sanitizer + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*|-fsanitize=*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + func_append compile_command " $arg" + func_append finalize_command " $arg" + func_append compiler_flags " $arg" + continue + ;; + + -Z*) + if test os2 = "`expr $host : '.*\(os2\)'`"; then + # OS/2 uses -Zxxx to specify OS/2-specific options + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case $arg in + -Zlinker | -Zstack) + prev=xcompiler + ;; + esac + continue + else + # Otherwise treat like 'Some other compiler flag' below + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + fi + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + + *.$objext) + # A standard object. + func_append objs " $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + test none = "$pic_object" || { + # Prepend the subdirectory the object is found in. + pic_object=$xdir$pic_object + + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then + func_append dlfiles " $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test dlprefiles = "$prev"; then + # Preload the old-style object. + func_append dlprefiles " $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg=$pic_object + } + + # Non-PIC object. + if test none != "$non_pic_object"; then + # Prepend the subdirectory the object is found in. + non_pic_object=$xdir$non_pic_object + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object=$pic_object + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir=$func_dirname_result + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "'$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + func_append deplibs " $arg" + func_append old_deplibs " $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + func_resolve_sysroot "$arg" + if test dlfiles = "$prev"; then + # This library was specified with -dlopen. + func_append dlfiles " $func_resolve_sysroot_result" + prev= + elif test dlprefiles = "$prev"; then + # The library was specified with -dlpreopen. + func_append dlprefiles " $func_resolve_sysroot_result" + prev= + else + func_append deplibs " $func_resolve_sysroot_result" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the '$prevarg' option requires an argument" + + if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname=$func_basename_result + libobjs_save=$libobjs + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + # Definition is injected by LT_CONFIG during libtool generation. + func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" + + func_dirname "$output" "/" "" + output_objdir=$func_dirname_result$objdir + func_to_tool_file "$output_objdir/" + tool_output_objdir=$func_to_tool_file_result + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_preserve_dup_deps; then + case "$libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append libs " $deplib" + done + + if test lib = "$linkmode"; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;; + esac + func_append pre_post_deps " $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=false + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test lib,link = "$linkmode,$pass"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs=$tmp_deplibs + fi + + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass"; then + libs=$deplibs + deplibs= + fi + if test prog = "$linkmode"; then + case $pass in + dlopen) libs=$dlfiles ;; + dlpreopen) libs=$dlprefiles ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test lib,dlpreopen = "$linkmode,$pass"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + func_resolve_sysroot "$lib" + case $lib in + *.la) func_source "$func_resolve_sysroot_result" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + func_basename "$deplib" + deplib_base=$func_basename_result + case " $weak_libs " in + *" $deplib_base "*) ;; + *) func_append deplibs " $deplib" ;; + esac + done + done + libs=$dlprefiles + fi + if test dlopen = "$pass"; then + # Collect dlpreopened libraries + save_deplibs=$deplibs + deplibs= + fi + + for deplib in $libs; do + lib= + found=false + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append compiler_flags " $deplib" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test lib != "$linkmode" && test prog != "$linkmode"; then + func_warning "'-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test lib = "$linkmode"; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib=$searchdir/lib$name$search_ext + if test -f "$lib"; then + if test .la = "$search_ext"; then + found=: + else + found=false + fi + break 2 + fi + done + done + if $found; then + # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll=$l + done + if test "X$ll" = "X$old_library"; then # only static version available + found=false + func_dirname "$lib" "" "." + ladir=$func_dirname_result + lib=$ladir/$old_library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + else + # deplib doesn't seem to be a libtool library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + ;; # -l + *.ltframework) + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test lib = "$linkmode"; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) func_append new_inherited_linker_flags " $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test conv = "$pass" && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + prog) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + if test scan = "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + *) + func_warning "'-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test link = "$pass"; then + func_stripname '-R' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + dir=$func_resolve_sysroot_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) func_append xrpath " $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) + func_resolve_sysroot "$deplib" + lib=$func_resolve_sysroot_result + ;; + *.$libext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=false + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=: + fi + ;; + pass_all) + valid_a_lib=: + ;; + esac + if $valid_a_lib; then + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + else + echo + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because the file extensions .$libext of this argument makes me believe" + echo "*** that it is just a static archive that I should not use here." + fi + ;; + esac + continue + ;; + prog) + if test link != "$pass"; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test conv = "$pass"; then + deplibs="$deplib $deplibs" + elif test prog = "$linkmode"; then + if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + func_append newdlprefiles " $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + func_append newdlfiles " $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=: + continue + ;; + esac # case $deplib + + $found || test -f "$lib" \ + || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "'$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir=$func_dirname_result + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass" || + { test prog != "$linkmode" && test lib != "$linkmode"; }; then + test -n "$dlopen" && func_append dlfiles " $dlopen" + test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" + fi + + if test conv = "$pass"; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + # It is a libtool convenience library, so add in its objects. + func_append convenience " $ladir/$objdir/$old_library" + func_append old_convenience " $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done + elif test prog != "$linkmode" && test lib != "$linkmode"; then + func_fatal_error "'$lib' is not a convenience library" + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + if test -n "$old_library" && + { test yes = "$prefer_static_libs" || + test built,no = "$prefer_static_libs,$installed"; }; then + linklib=$old_library + else + for l in $old_library $library_names; do + linklib=$l + done + fi + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for '$lib'" + fi + + # This library was specified with -dlopen. + if test dlopen = "$pass"; then + test -z "$libdir" \ + && func_fatal_error "cannot -dlopen a convenience library: '$lib'" + if test -z "$dlname" || + test yes != "$dlopen_support" || + test no = "$build_libtool_libs" + then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + func_append dlprefiles " $lib $dependency_libs" + else + func_append newdlfiles " $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of '$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir=$ladir + fi + ;; + esac + func_basename "$lib" + laname=$func_basename_result + + # Find the relevant object directory and library name. + if test yes = "$installed"; then + if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library '$lib' was moved." + dir=$ladir + absdir=$abs_ladir + libdir=$abs_ladir + else + dir=$lt_sysroot$libdir + absdir=$lt_sysroot$libdir + fi + test yes = "$hardcode_automatic" && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir=$ladir + absdir=$abs_ladir + # Remove this search path later + func_append notinst_path " $abs_ladir" + else + dir=$ladir/$objdir + absdir=$abs_ladir/$objdir + # Remove this search path later + func_append notinst_path " $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test dlpreopen = "$pass"; then + if test -z "$libdir" && test prog = "$linkmode"; then + func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" + fi + case $host in + # special handling for platforms with PE-DLLs. + *cygwin* | *mingw* | *cegcc* ) + # Linker will automatically link against shared library if both + # static and shared are present. Therefore, ensure we extract + # symbols from the import library if a shared library is present + # (otherwise, the dlopen module name will be incorrect). We do + # this by putting the import library name into $newdlprefiles. + # We recover the dlopen module name by 'saving' the la file + # name in a special purpose variable, and (later) extracting the + # dlname from the la file. + if test -n "$dlname"; then + func_tr_sh "$dir/$linklib" + eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname" + func_append newdlprefiles " $dir/$linklib" + else + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + fi + ;; + * ) + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + func_append newdlprefiles " $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + func_append dlpreconveniencelibs " $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + func_append newdlprefiles " $dir/$dlname" + else + func_append newdlprefiles " $dir/$linklib" + fi + ;; + esac + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test lib = "$linkmode"; then + deplibs="$dir/$old_library $deplibs" + elif test prog,link = "$linkmode,$pass"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test prog = "$linkmode" && test link != "$pass"; then + func_append newlib_search_path " $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=false + if test no != "$link_all_deplibs" || test -z "$library_names" || + test no = "$build_libtool_libs"; then + linkalldeplibs=: + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result" + func_append newlib_search_path " $func_resolve_sysroot_result" + ;; + esac + # Need to link against all dependency_libs? + if $linkalldeplibs; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $deplib "*) func_append specialdeplibs " $deplib" ;; + esac + fi + func_append tmp_libs " $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test prog,link = "$linkmode,$pass"; then + if test -n "$library_names" && + { { test no = "$prefer_static_libs" || + test built,yes = "$prefer_static_libs,$installed"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then + # Make sure the rpath contains only unique directories. + case $temp_rpath: in + *"$absdir:"*) ;; + *) func_append temp_rpath "$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if $alldeplibs && + { test pass_all = "$deplibs_check_method" || + { test yes = "$build_libtool_libs" && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test built = "$use_static_libs" && test yes = "$installed"; then + use_static_libs=no + fi + if test -n "$library_names" && + { test no = "$use_static_libs" || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc* | *os2*) + # No point in relinking DLLs because paths are not encoded + func_append notinst_deplibs " $lib" + need_relink=no + ;; + *) + if test no = "$installed"; then + func_append notinst_deplibs " $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule= + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule=$dlpremoduletest + break + fi + done + if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then + echo + if test prog = "$linkmode"; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test lib = "$linkmode" && + test yes = "$hardcode_into_libs"; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) func_append compile_rpath " $absdir" ;; + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname=$1 + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname=$dlname + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc* | *os2*) + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + esac + eval soname=\"$soname_spec\" + else + soname=$realname + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot=$soname + func_basename "$soroot" + soname=$func_basename_result + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from '$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for '$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test prog = "$linkmode" || test relink != "$opt_mode"; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test no = "$hardcode_direct"; then + add=$dir/$linklib + case $host in + *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; + *-*-sysv4*uw2*) add_dir=-L$dir ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir=-L$dir ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we cannot + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library"; then + echo + echo "*** And there doesn't seem to be a static archive available" + echo "*** The link will probably fail, sorry" + else + add=$dir/$old_library + fi + elif test -n "$old_library"; then + add=$dir/$old_library + fi + fi + esac + elif test no = "$hardcode_minus_L"; then + case $host in + *-*-sunos*) add_shlibpath=$dir ;; + esac + add_dir=-L$dir + add=-l$name + elif test no = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + relink) + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$dir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$absdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test yes != "$lib_linked"; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) func_append compile_shlibpath "$add_shlibpath:" ;; + esac + fi + if test prog = "$linkmode"; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test yes != "$hardcode_direct" && + test yes != "$hardcode_minus_L" && + test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + fi + fi + fi + + if test prog = "$linkmode" || test relink = "$opt_mode"; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$libdir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$libdir + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) func_append finalize_shlibpath "$libdir:" ;; + esac + add=-l$name + elif test yes = "$hardcode_automatic"; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib"; then + add=$inst_prefix_dir$libdir/$linklib + else + add=$libdir/$linklib + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir=-L$libdir + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + func_append add_dir " -L$inst_prefix_dir$libdir" + ;; + esac + fi + add=-l$name + fi + + if test prog = "$linkmode"; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test prog = "$linkmode"; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test unsupported != "$hardcode_direct"; then + test -n "$old_library" && linklib=$old_library + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test yes = "$build_libtool_libs"; then + # Not a shared library + if test pass_all != "$deplibs_check_method"; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + echo + $ECHO "*** Warning: This system cannot link to static lib archive $lib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have." + if test yes = "$module"; then + echo "*** But as you try to build a module library, libtool will still create " + echo "*** a static module, that should work as long as the dlopening application" + echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test lib = "$linkmode"; then + if test -n "$dependency_libs" && + { test yes != "$hardcode_into_libs" || + test yes = "$build_old_libs" || + test yes = "$link_static"; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) func_append xrpath " $temp_xrpath";; + esac;; + *) func_append temp_deplibs " $libdir";; + esac + done + dependency_libs=$temp_deplibs + fi + + func_append newlib_search_path " $absdir" + # Link against this library + test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + func_resolve_sysroot "$func_stripname_result";; + *) func_resolve_sysroot "$deplib" ;; + esac + if $opt_preserve_dup_deps; then + case "$tmp_libs " in + *" $func_resolve_sysroot_result "*) + func_append specialdeplibs " $func_resolve_sysroot_result" ;; + esac + fi + func_append tmp_libs " $func_resolve_sysroot_result" + done + + if test no != "$link_all_deplibs"; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + path= + case $deplib in + -L*) path=$deplib ;; + *.la) + func_resolve_sysroot "$deplib" + deplib=$func_resolve_sysroot_result + func_dirname "$deplib" "" "." + dir=$func_dirname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of '$dir'" + absdir=$dir + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names"; then + for tmp in $deplibrary_names; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl"; then + depdepl=$absdir/$objdir/$depdepl + darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" + func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" + path= + fi + fi + ;; + *) + path=-L$absdir/$objdir + ;; + esac + else + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "'$deplib' seems to be moved" + + path=-L$absdir + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test link = "$pass"; then + if test prog = "$linkmode"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs=$newdependency_libs + if test dlpreopen = "$pass"; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test dlopen != "$pass"; then + test conv = "$pass" || { + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) func_append lib_search_path " $dir" ;; + esac + done + newlib_search_path= + } + + if test prog,link = "$linkmode,$pass"; then + vars="compile_deplibs finalize_deplibs" + else + vars=deplibs + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) func_append tmp_libs " $deplib" ;; + esac + ;; + *) func_append tmp_libs " $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + + # Add Sun CC postdeps if required: + test CXX = "$tagname" && { + case $host_os in + linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C++ 5.9 + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + + solaris*) + func_cc_basename "$CC" + case $func_cc_basename_result in + CC* | sunCC*) + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + esac + } + + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i= + ;; + esac + if test -n "$i"; then + func_append tmp_libs " $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test prog = "$linkmode"; then + dlfiles=$newdlfiles + fi + if test prog = "$linkmode" || test lib = "$linkmode"; then + dlprefiles=$newdlprefiles + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "'-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "'-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs=$output + func_append objs "$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form 'libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test no = "$module" \ + && func_fatal_help "libtool library '$output' must begin with 'lib'" + + if test no != "$need_lib_prefix"; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test pass_all != "$deplibs_check_method"; then + func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" + else + echo + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + func_append libobjs " $objs" + fi + fi + + test no = "$dlself" \ + || func_warning "'-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test 1 -lt "$#" \ + && func_warning "ignoring multiple '-rpath's for a libtool library" + + install_libdir=$1 + + oldlibs= + if test -z "$rpath"; then + if test yes = "$build_libtool_libs"; then + # Building a libtool convenience library. + # Some compilers have problems with a '.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "'-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "'-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs=$IFS; IFS=: + set dummy $vinfo 0 0 0 + shift + IFS=$save_ifs + + test -n "$7" && \ + func_fatal_help "too many parameters to '-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major=$1 + number_minor=$2 + number_revision=$3 + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # that has an extra 1 added just for fun + # + case $version_type in + # correct linux to gnu/linux during the next big refactor + darwin|freebsd-elf|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_revision + ;; + freebsd-aout|qnx|sunos) + current=$number_major + revision=$number_minor + age=0 + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age=$number_minor + revision=$number_minor + lt_irix_increment=no + ;; + *) + func_fatal_configuration "$modename: unknown library version type '$version_type'" + ;; + esac + ;; + no) + current=$1 + revision=$2 + age=$3 + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT '$current' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION '$revision' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE '$age' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE '$age' is greater than the current interface number '$current'" + func_fatal_error "'$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + # On Darwin other compilers + case $CC in + nagfor*) + verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + ;; + *) + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + esac + ;; + + freebsd-aout) + major=.$current + versuffix=.$current.$revision + ;; + + freebsd-elf) + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + irix | nonstopux) + if test no = "$lt_irix_increment"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring=$verstring_prefix$major.$revision + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test 0 -ne "$loop"; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring_prefix$major.$iface:$verstring + done + + # Before this point, $major must not contain '.'. + major=.$major + versuffix=$major.$revision + ;; + + linux) # correct to gnu/linux during the next big refactor + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=.$current.$age.$revision + verstring=$current.$age.$revision + + # Add in all the interfaces that we are compatible with. + loop=$age + while test 0 -ne "$loop"; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring=$verstring:$iface.0 + done + + # Make executables depend on our current version. + func_append verstring ":$current.0" + ;; + + qnx) + major=.$current + versuffix=.$current + ;; + + sco) + major=.$current + versuffix=.$current + ;; + + sunos) + major=.$current + versuffix=.$current.$revision + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 file systems. + func_arith $current - $age + major=$func_arith_result + versuffix=-$major + ;; + + *) + func_fatal_configuration "unknown library version type '$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring=0.0 + ;; + esac + if test no = "$need_version"; then + versuffix= + else + versuffix=.0.0 + fi + fi + + # Remove version info from name if versioning should be avoided + if test yes,no = "$avoid_version,$need_version"; then + major= + versuffix= + verstring= + fi + + # Check to see if the archive will have undefined symbols. + if test yes = "$allow_undefined"; then + if test unsupported = "$allow_undefined_flag"; then + if test yes = "$build_old_libs"; then + func_warning "undefined symbols not allowed in $host shared libraries; building static only" + build_libtool_libs=no + else + func_fatal_error "can't build $host shared library unless -no-undefined is specified" + fi + fi + else + # Don't allow undefined symbols. + allow_undefined_flag=$no_undefined_flag + fi + + fi + + func_generate_dlsyms "$libname" "$libname" : + func_append libobjs " $symfileobj" + test " " = "$libobjs" && libobjs= + + if test relink != "$opt_mode"; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) + if test -n "$precious_files_regex"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + func_append removelist " $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then + func_append oldlibs " $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"` + # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"` + # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + func_replace_sysroot "$libdir" + func_append temp_xrpath " -R$func_replace_sysroot_result" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles=$dlfiles + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) func_append dlfiles " $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles=$dlprefiles + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) func_append dlprefiles " $lib" ;; + esac + done + + if test yes = "$build_libtool_libs"; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + func_append deplibs " System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test yes = "$build_libtool_need_lc"; then + func_append deplibs " -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release= + versuffix= + major= + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + $nocaseglob + else + potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` + fi + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib=$potent_lib + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | $SED 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; + *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + case " $predeps $postdeps " in + *" $a_deplib "*) + func_append newdeplibs " $a_deplib" + a_deplib= + ;; + esac + fi + if test -n "$a_deplib"; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib=$potent_lib # see symlink-check above in file_magic test + if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + func_append newdeplibs " $a_deplib" + a_deplib= + break 2 + fi + done + done + fi + if test -n "$a_deplib"; then + droppeddeps=yes + echo + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + echo "*** I have the capability to make that library automatically link in when" + echo "*** you link to this library. But I can only do this if you have a" + echo "*** shared version of the library, which you do not appear to have" + echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib"; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + func_append newdeplibs " $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs= + tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + for i in $predeps $postdeps; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` + done + fi + case $tmp_deplibs in + *[!\ \ ]*) + echo + if test none = "$deplibs_check_method"; then + echo "*** Warning: inter-library dependencies are not supported in this platform." + else + echo "*** Warning: inter-library dependencies are not known to be supported." + fi + echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + ;; + esac + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + if test yes = "$droppeddeps"; then + if test yes = "$module"; then + echo + echo "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + echo "*** a static module, that should work as long as the dlopening" + echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + echo + echo "*** However, this would only work if libtool was able to extract symbol" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" + echo "*** not find such a program. So, this module is probably useless." + echo "*** 'nm' from GNU binutils and a full rebuild may help." + fi + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + echo "*** The inter-library dependencies that have been dropped here will be" + echo "*** automatically added whenever a program is linked with this library" + echo "*** or is declared to -dlopen it." + + if test no = "$allow_undefined"; then + echo + echo "*** Since this library must not contain undefined symbols," + echo "*** because either the platform does not support them or" + echo "*** it was explicitly requested with -no-undefined," + echo "*** libtool will only create a static version of it." + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + deplibs=$new_libs + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test yes = "$build_libtool_libs"; then + # Remove $wl instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test yes = "$hardcode_into_libs"; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath=$finalize_rpath + test relink = "$opt_mode" || rpath=$compile_rpath$rpath + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + func_replace_sysroot "$libdir" + libdir=$func_replace_sysroot_result + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append dep_rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath=$finalize_shlibpath + test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname=$1 + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname=$realname + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib=$output_objdir/$realname + linknames= + for link + do + func_append linknames " $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols=$output_objdir/$libname.uexp + func_append delfiles " $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + func_dll_def_p "$export_symbols" || { + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols=$export_symbols + export_symbols= + always_export_symbols=yes + } + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs=$IFS; IFS='~' + for cmd1 in $cmds; do + IFS=$save_ifs + # Take the normal branch if the nm_file_list_spec branch + # doesn't work or if tool conversion is not needed. + case $nm_file_list_spec~$to_tool_file_cmd in + *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*) + try_normal_branch=yes + eval cmd=\"$cmd1\" + func_len " $cmd" + len=$func_len_result + ;; + *) + try_normal_branch=no + ;; + esac + if test yes = "$try_normal_branch" \ + && { test "$len" -lt "$max_cmd_len" \ + || test "$max_cmd_len" -le -1; } + then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + elif test -n "$nm_file_list_spec"; then + func_basename "$output" + output_la=$func_basename_result + save_libobjs=$libobjs + save_output=$output + output=$output_objdir/$output_la.nm + func_to_tool_file "$output" + libobjs=$nm_file_list_spec$func_to_tool_file_result + func_append delfiles " $output" + func_verbose "creating $NM input file list: $output" + for obj in $save_libobjs; do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > "$output" + eval cmd=\"$cmd1\" + func_show_eval "$cmd" 'exit $?' + output=$save_output + libobjs=$save_libobjs + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS=$save_ifs + if test -n "$export_symbols_regex" && test : != "$skipped_export"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test : != "$skipped_export" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + func_append tmp_deplibs " $test_deplib" + ;; + esac + done + deplibs=$tmp_deplibs + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test yes = "$compiler_needs_object" && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + func_append linker_flags " $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test : != "$skipped_export" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + func_basename "$output" + output_la=$func_basename_result + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then + output=$output_objdir/$output_la.lnkscript + func_verbose "creating GNU ld script: $output" + echo 'INPUT (' > $output + for obj in $save_libobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + echo ')' >> $output + func_append delfiles " $output" + func_to_tool_file "$output" + output=$func_to_tool_file_result + elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then + output=$output_objdir/$output_la.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test yes = "$compiler_needs_object"; then + firstobj="$1 " + shift + fi + for obj + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" >> $output + done + func_append delfiles " $output" + func_to_tool_file "$output" + output=$firstobj\"$file_list_spec$func_to_tool_file_result\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-$k.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test -z "$objlist" || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test 1 -eq "$k"; then + # The first file doesn't have a previous command to add. + reload_objs=$objlist + eval concat_cmds=\"$reload_cmds\" + else + # All subsequent reloadable object files will link in + # the last one created. + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-$k.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-$k.$objext + objlist=" $obj" + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + reload_objs="$objlist $last_robj" + eval concat_cmds=\"\$concat_cmds$reload_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + func_append delfiles " $output" + + else + output= + fi + + ${skipped_export-false} && { + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + } + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs=$IFS; IFS='~' + for cmd in $concat_cmds; do + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + ${skipped_export-false} && { + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols + $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands, which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + func_append delfiles " $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + } + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test yes = "$module" && test -n "$module_cmds"; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append libobjs " $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs=$IFS; IFS='~' + for cmd in $cmds; do + IFS=$sp$nl + eval cmd=\"$cmd\" + IFS=$save_ifs + $opt_quiet || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS=$save_ifs + + # Restore the uninstalled library and exit + if test relink = "$opt_mode"; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test yes = "$module" || test yes = "$export_dynamic"; then + # On all known operating systems, these are identical. + dlname=$soname + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "'-l' and '-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "'-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "'-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "'-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object '$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj=$output + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # if reload_cmds runs $LD directly, get rid of -Wl from + # whole_archive_flag_spec and hope we can get by with turning comma + # into space. + case $reload_cmds in + *\$LD[\ \$]*) wl= ;; + esac + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags + else + gentop=$output_objdir/${obj}x + func_append generated " $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # If we're not building shared, we need to use non_pic_objs + test yes = "$build_libtool_libs" || libobjs=$non_pic_objects + + # Create the old-style object. + reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs + + output=$obj + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + test yes = "$build_libtool_libs" || { + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + } + + if test -n "$pic_flag" || test default != "$pic_mode"; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output=$libobj + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "'-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "'-release' is ignored for programs" + + $preload \ + && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ + && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test CXX = "$tagname"; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + func_append compile_command " $wl-bind_at_load" + func_append finalize_command " $wl-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + func_append new_libs " -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) func_append new_libs " $deplib" ;; + esac + ;; + *) func_append new_libs " $deplib" ;; + esac + done + compile_deplibs=$new_libs + + + func_append compile_command " $compile_deplibs" + func_append finalize_command " $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) func_append finalize_rpath " $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) func_append perm_rpath " $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) func_append dllsearchpath ":$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) func_append dllsearchpath ":$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath=$rpath + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs=$libdir + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + func_append hardcode_libdirs "$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + func_append rpath " $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) func_append finalize_perm_rpath " $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir=$hardcode_libdirs + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath=$rpath + + if test -n "$libobjs" && test yes = "$build_old_libs"; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" false + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=: + case $host in + *cegcc* | *mingw32ce*) + # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. + wrappers_required=false + ;; + *cygwin* | *mingw* ) + test yes = "$build_libtool_libs" || wrappers_required=false + ;; + *) + if test no = "$need_relink" || test yes != "$build_libtool_libs"; then + wrappers_required=false + fi + ;; + esac + $wrappers_required || { + # Replace the output file specification. + compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + link_command=$compile_command$compile_rpath + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.$objext"; then + func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' + fi + + exit $exit_status + } + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + func_append rpath "$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + func_append rpath "$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test yes = "$no_install"; then + # We don't need to create a wrapper script. + link_command=$compile_var$compile_command$compile_rpath + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + exit $EXIT_SUCCESS + fi + + case $hardcode_action,$fast_install in + relink,*) + # Fast installation is not supported + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "'$output' will be relinked during installation" + ;; + *,yes) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + ;; + *,no) + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + ;; + *,needless) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command= + ;; + esac + + # Replace the output file specification. + link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + if test -n "$postlink_cmds"; then + func_to_tool_file "$output_objdir/$outputname" + postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'` + func_execute_cmds "$postlink_cmds" 'exit $?' + fi + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource=$output_path/$objdir/lt-$output_name.c + cwrapper=$output_path/$output_name.exe + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host"; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + case $build_libtool_libs in + convenience) + oldobjs="$libobjs_save $symfileobj" + addlibs=$convenience + build_libtool_libs=no + ;; + module) + oldobjs=$libobjs_save + addlibs=$old_convenience + build_libtool_libs=no + ;; + *) + oldobjs="$old_deplibs $non_pic_objects" + $preload && test -f "$symfileobj" \ + && func_append oldobjs " $symfileobj" + addlibs=$old_convenience + ;; + esac + + if test -n "$addlibs"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $addlibs + func_append oldobjs " $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + + func_extract_archives $gentop $dlprefiles + func_append oldobjs " $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + echo "copying selected object files to avoid basename conflicts..." + gentop=$output_objdir/${outputname}x + func_append generated " $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase=$func_basename_result + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + func_append oldobjs " $gentop/$newobj" + ;; + *) func_append oldobjs " $obj" ;; + esac + done + fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + elif test -n "$archiver_list_spec"; then + func_verbose "using command file archive linking..." + for obj in $oldobjs + do + func_to_tool_file "$obj" + $ECHO "$func_to_tool_file_result" + done > $output_objdir/$libname.libcmd + func_to_tool_file "$output_objdir/$libname.libcmd" + oldobjs=" $archiver_list_spec$func_to_tool_file_result" + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj"; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test -z "$oldobjs"; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test yes = "$build_old_libs" && old_library=$libname.$libext + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` + if test yes = "$hardcode_automatic"; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test yes = "$installed"; then + if test -z "$install_libdir"; then + break + fi + output=$output_objdir/${outputname}i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name=$func_basename_result + func_resolve_sysroot "$deplib" + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` + test -z "$libdir" && \ + func_fatal_error "'$deplib' is not a valid libtool archive" + func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" + ;; + -L*) + func_stripname -L '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -L$func_replace_sysroot_result" + ;; + -R*) + func_stripname -R '' "$deplib" + func_replace_sysroot "$func_stripname_result" + func_append newdependency_libs " -R$func_replace_sysroot_result" + ;; + *) func_append newdependency_libs " $deplib" ;; + esac + done + dependency_libs=$newdependency_libs + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" + ;; + *) func_append newdlfiles " $lib" ;; + esac + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "'$lib' is not a valid libtool archive" + func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" + ;; + esac + done + dlprefiles=$newdlprefiles + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlfiles " $abs" + done + dlfiles=$newdlfiles + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; + *) abs=`pwd`"/$lib" ;; + esac + func_append newdlprefiles " $abs" + done + dlprefiles=$newdlprefiles + fi + $RM $output + # place dlname in correct position for cygwin + # In fact, it would be nice if we could use this code for all target + # systems that can't hard-code library paths into their executables + # and that have no shared library path variable independent of PATH, + # but it turns out we can't easily determine that from inspecting + # libtool variables, so we have to hard-code the OSs to which it + # applies here; at the moment, that means platforms that use the PE + # object format with DLL files. See the long comment at the top of + # tests/bindir.at for full details. + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) + # If a -bindir argument was supplied, place the dll there. + if test -n "$bindir"; then + func_relative_path "$install_libdir" "$bindir" + tdlname=$func_relative_path_result/$dlname + else + # Otherwise fall back on heuristic. + tdlname=../bin/$dlname + fi + ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test no,yes = "$installed,$need_relink"; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +if test link = "$opt_mode" || test relink = "$opt_mode"; then + func_mode_link ${1+"$@"} +fi + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $debug_cmd + + RM=$nonopt + files= + rmforce=false + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic=$magic + + for arg + do + case $arg in + -f) func_append RM " $arg"; rmforce=: ;; + -*) func_append RM " $arg" ;; + *) func_append files " $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + for file in $files; do + func_dirname "$file" "" "." + dir=$func_dirname_result + if test . = "$dir"; then + odir=$objdir + else + odir=$dir/$objdir + fi + func_basename "$file" + name=$func_basename_result + test uninstall = "$opt_mode" && odir=$dir + + # Remember odir for removal later, being careful to avoid duplicates + if test clean = "$opt_mode"; then + case " $rmdirs " in + *" $odir "*) ;; + *) func_append rmdirs " $odir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif $rmforce; then + continue + fi + + rmfiles=$file + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + func_append rmfiles " $odir/$n" + done + test -n "$old_library" && func_append rmfiles " $odir/$old_library" + + case $opt_mode in + clean) + case " $library_names " in + *" $dlname "*) ;; + *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;; + esac + test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && test none != "$pic_object"; then + func_append rmfiles " $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && test none != "$non_pic_object"; then + func_append rmfiles " $dir/$non_pic_object" + fi + fi + ;; + + *) + if test clean = "$opt_mode"; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + func_append rmfiles " $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + func_append rmfiles " $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + func_append rmfiles " $odir/$name $odir/${name}S.$objext" + if test yes = "$fast_install" && test -n "$relink_command"; then + func_append rmfiles " $odir/lt-$name" + fi + if test "X$noexename" != "X$name"; then + func_append rmfiles " $odir/lt-$noexename.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + + # Try to remove the $objdir's in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then + func_mode_uninstall ${1+"$@"} +fi + +test -z "$opt_mode" && { + help=$generic_help + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode '$opt_mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# where we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/thirdparty/ode-0.16.5/m4/libtool.m4 b/thirdparty/ode-0.16.5/m4/libtool.m4 new file mode 100644 index 0000000..10ab284 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/libtool.m4 @@ -0,0 +1,8388 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +]) + +# serial 58 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK +AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS=$ltmain + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + +# _LT_CC_BASENAME(CC) +# ------------------- +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. +m4_defun([_LT_CC_BASENAME], +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl + +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options that allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a '.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld=$lt_cv_prog_gnu_ld + +old_CC=$CC +old_CFLAGS=$CFLAGS + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PREPARE_SED_QUOTE_VARS +# -------------------------- +# Define a few sed substitution that help us do robust quoting. +m4_defun([_LT_PREPARE_SED_QUOTE_VARS], +[# Backslashify metacharacters that are still active within +# double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' +]) + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from 'configure', and 'config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain=$ac_aux_dir/ltmain.sh +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the 'libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags='_LT_TAGS'dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +\$[]1 +_LTECHO_EOF' +} + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +_LT_OUTPUT_LIBTOOL_INIT +]) + +# _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) +# ------------------------------------ +# Generate a child script FILE with all initialization necessary to +# reuse the environment learned by the parent script, and make the +# file executable. If COMMENT is supplied, it is inserted after the +# '#!' sequence but before initialization text begins. After this +# macro, additional text can be appended to FILE to form the body of +# the child script. The macro ends with non-zero status if the +# file could not be fully written (such as if the disk is full). +m4_ifdef([AS_INIT_GENERATED], +[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], +[m4_defun([_LT_GENERATED_FILE_INIT], +[m4_require([AS_PREPARE])]dnl +[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl +[lt_write_fail=0 +cat >$1 <<_ASEOF || lt_write_fail=1 +#! $SHELL +# Generated by $as_me. +$2 +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$1 <<\_ASEOF || lt_write_fail=1 +AS_SHELL_SANITIZE +_AS_PREPARE +exec AS_MESSAGE_FD>&1 +_ASEOF +test 0 = "$lt_write_fail" && chmod +x $1[]dnl +m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +_LT_GENERATED_FILE_INIT(["$CONFIG_LT"], +[# Run this file to recreate a libtool stub with the current configuration.]) + +cat >>"$CONFIG_LT" <<\_LTEOF +lt_cl_silent=false +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +'$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2011 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test 0 != $[#] +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try '$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try '$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +lt_cl_success=: +test yes = "$silent" && + lt_config_lt_args="$lt_config_lt_args --quiet" +exec AS_MESSAGE_LOG_FD>/dev/null +$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false +exec AS_MESSAGE_LOG_FD>>config.log +$lt_cl_success || AS_EXIT(1) +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options that allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}"; then + setopt NO_GLOB_SUBST + fi + + cfgfile=${ofile}T + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# Generated automatically by $as_me ($PACKAGE) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + +_LT_COPYING +_LT_LIBTOOL_TAGS + +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test set != "${COLLECT_NAMES+set}"; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '$q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) +dnl AC_DEFUN([AC_LIBTOOL_RC], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "$LT_MULTI_MODULE"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS=$save_LDFLAGS + ]) + + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], + [lt_cv_ld_force_load=no + cat > conftest.c << _LT_EOF +int forced_loaded() { return 2;} +_LT_EOF + echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD + echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD + $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD + cat > conftest.c << _LT_EOF +int main() { return 0;} +_LT_EOF + echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err + _lt_result=$? + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then + lt_cv_ld_force_load=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -f conftest.err libconftest.a conftest conftest.c + rm -rf conftest.dSYM + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test yes = "$lt_cv_apple_cc_single_mod"; then + _lt_dar_single_mod='$single_module' + fi + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' + fi + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined + case $cc_basename in + ifort*|nagfor*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test yes = "$_lt_dar_can_shared"; then + output_verbose_link_cmd=func_echo_all + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" + m4_if([$1], [CXX], +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX([TAGNAME]) +# ---------------------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +# Store the results from the different compilers for each TAGNAME. +# Allow to override them for all tags through lt_cv_aix_libpath. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +if test set = "${lt_cv_aix_libpath+set}"; then + aix_libpath=$lt_cv_aix_libpath +else + AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], + [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ + lt_aix_libpath_sed='[ + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\([^ ]*\) *$/\1/ + p + } + }]' + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + # Check for a 64-bit object if we didn't find anything. + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` + fi],[]) + if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib + fi + ]) + aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) +fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[m4_divert_text([M4SH-INIT], [$1 +])])# _LT_SHELL_INIT + + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Find how we can fake an echo command that does not interpret backslash. +# In particular, with Autoconf 2.60 or later we add some code to the start +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO +ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + +AC_MSG_CHECKING([how to print strings]) +# Test print first, because it will be a builtin if present. +if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ + test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='print -r --' +elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then + ECHO='printf %s\n' +else + # Use this function as a fallback that always works. + func_fallback_echo () + { + eval 'cat <<_LTECHO_EOF +$[]1 +_LTECHO_EOF' + } + ECHO='func_fallback_echo' +fi + +# func_echo_all arg... +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + +case $ECHO in + printf*) AC_MSG_RESULT([printf]) ;; + print*) AC_MSG_RESULT([print -r]) ;; + *) AC_MSG_RESULT([cat]) ;; +esac + +m4_ifdef([_AS_DETECT_SUGGESTED], +[_AS_DETECT_SUGGESTED([ + test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( + ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO + ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO + PATH=/empty FPATH=/empty; export PATH FPATH + test "X`printf %s $ECHO`" = "X$ECHO" \ + || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) + +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_WITH_SYSROOT +# ---------------- +AC_DEFUN([_LT_WITH_SYSROOT], +[AC_MSG_CHECKING([for sysroot]) +AC_ARG_WITH([sysroot], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], +[], [with_sysroot=no]) + +dnl lt_sysroot will always be passed unquoted. We quote it here +dnl in case the user passed a directory name. +lt_sysroot= +case $with_sysroot in #( + yes) + if test yes = "$GCC"; then + lt_sysroot=`$CC --print-sysroot 2>/dev/null` + fi + ;; #( + /*) + lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` + ;; #( + no|'') + ;; #( + *) + AC_MSG_RESULT([$with_sysroot]) + AC_MSG_ERROR([The sysroot must be an absolute path.]) + ;; +esac + + AC_MSG_RESULT([${lt_sysroot:-no}]) +_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl +[dependent libraries, and where our libraries should be installed.])]) + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test no = "$enable_libtool_lock" || enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE=32 + ;; + *ELF-64*) + HPUX_IA64_MODE=64 + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test yes = "$lt_cv_prog_gnu_ld"; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac + ;; + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test yes != "$lt_cv_cc_needs_belf"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS=$SAVE_CFLAGS + fi + ;; +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks=$enable_libtool_lock +])# _LT_ENABLE_LOCK + + +# _LT_PROG_AR +# ----------- +m4_defun([_LT_PROG_AR], +[AC_CHECK_TOOLS(AR, [ar], false) +: ${AR=ar} +: ${AR_FLAGS=cru} +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) + +AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], + [lt_cv_ar_at_file=no + AC_COMPILE_IFELSE([AC_LANG_PROGRAM], + [echo conftest.$ac_objext > conftest.lst + lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([lt_ar_try]) + if test 0 -eq "$ac_status"; then + # Ensure the archiver fails upon bogus file names. + rm -f conftest.$ac_objext libconftest.a + AC_TRY_EVAL([lt_ar_try]) + if test 0 -ne "$ac_status"; then + lt_cv_ar_at_file=@ + fi + fi + rm -f conftest.* libconftest.a + ]) + ]) + +if test no = "$lt_cv_ar_at_file"; then + archiver_list_spec= +else + archiver_list_spec=$lt_cv_ar_at_file +fi +_LT_DECL([], [archiver_list_spec], [1], + [How to feed a file listing to the archiver]) +])# _LT_PROG_AR + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[_LT_PROG_AR + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" +fi + +case $host_os in + darwin*) + lock_old_archive_extraction=yes ;; + *) + lock_old_archive_extraction=no ;; +esac +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +_LT_DECL([], [lock_old_archive_extraction], [0], + [Whether to use a lock for old archive extraction]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test yes = "[$]$2"; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS=$save_LDFLAGS +]) + +if test yes = "[$]$2"; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring=ABCD + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + mint*) + # On MiNT this can take a long time and run out of memory. + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ + = "X$teststring$teststring"; } >/dev/null 2>&1 && + test 17 != "$i" # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n "$lt_cv_sys_max_cmd_len"; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes = "$cross_compiling"; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line $LINENO "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +/* When -fvisibility=hidden is used, assume the code has been annotated + correspondingly for the symbols needed. */ +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +int fnord () __attribute__((visibility("default"))); +#endif + +int fnord () { return 42; } +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else + { + if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + else puts (dlerror ()); + } + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test yes != "$enable_dlopen"; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen=load_add_on + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen=LoadLibrary + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen=shl_load], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen=dlopen], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test no = "$lt_cv_dlopen"; then + enable_dlopen=no + else + enable_dlopen=yes + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS=$LDFLAGS + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS=$LIBS + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test yes = "$lt_cv_dlopen_self"; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then + + # We can hardcode non-existent directories. + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then + # Fast installation is not supported + enable_fast_install=no +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP"; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test yes = "$GCC"; then + case $host_os in + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; + esac + case $host_os in + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` + case $lt_search_path_spec in + *\;*) + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` + ;; + *) + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` + ;; + esac + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary... + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo = "/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + # AWK program above erroneously prepends '/' to C:/dos/paths + # for these hosts. + case $host_os in + mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; + esac + sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=.so +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + +case $host_os in +aix3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='$libname$release$shared_ext$major' + ;; + +aix[[4-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test ia64 = "$host_cpu"; then + # AIX 5 supports IA64 + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='$libname$shared_ext' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + + case $GCC,$cc_basename in + yes,*) + # gcc + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + ;; + esac + dynamic_linker='Win32 ld.exe' + ;; + + *,cl*) + # Native MSVC + libname_spec='$name' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' + + case $build_os in + mingw*) + sys_lib_search_path_spec= + lt_save_ifs=$IFS + IFS=';' + for lt_path in $LIB + do + IFS=$lt_save_ifs + # Let DOS variable expansion print the short 8.3 style file name. + lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` + sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" + done + IFS=$lt_save_ifs + # Convert to MSYS style. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` + ;; + cygwin*) + # Convert to unix form, then to dos form, then back to unix form + # but this time dos style (no spaces!) so that the unix form looks + # like /cygdrive/c/PROGRA~1:/cygdr... + sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` + sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` + sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + ;; + *) + sys_lib_search_path_spec=$LIB + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # FIXME: find the short name or the path components, as spaces are + # common. (e.g. "Program Files" -> "PROGRA~1") + ;; + esac + + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + dynamic_linker='Win32 link.exe' + ;; + + *) + # Assume MSVC wrapper + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' + dynamic_linker='Win32 ld.exe' + ;; + esac + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[23]].*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2.*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +haiku*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + dynamic_linker="$host_os runtime_loader" + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LIBRARY_PATH + shlibpath_overrides_runpath=no + sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 + fi + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555, ... + postinstall_cmds='chmod 555 $lib' + # or fails outright, so override atomically: + install_override_mode=555 + ;; + +interix[[3-9]]*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +linux*android*) + version_type=none # Android doesn't support versioned libraries. + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + + # Some binutils ld are patched to set DT_RUNPATH + AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], + [lt_cv_shlibpath_overrides_runpath=no + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [lt_cv_shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + ]) + shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd* | bitrig*) + version_type=sunos + sys_lib_dlsearch_path_spec=/usr/lib + need_lib_prefix=no + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +os2*) + libname_spec='$name' + version_type=windows + shrext_cmds=.dll + need_version=no + need_lib_prefix=no + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test yes = "$with_gnu_ld"; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=sco + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test yes = "$with_gnu_ld"; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test no = "$dynamic_linker" && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test yes = "$GCC"; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec +fi + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec +fi + +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [install_override_mode], [1], + [Permission mode override for installation of shared libraries]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program that can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD=$lt_cv_path_MAGIC_CMD + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD + ;; +esac]) +MAGIC_CMD=$lt_cv_path_MAGIC_CMD +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program that can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PROG_ECHO_BACKSLASH])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test no = "$withval" || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test yes = "$GCC"; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return, which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD=$ac_prog + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test yes = "$with_gnu_ld"; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD=$ac_dir/$ac_prog + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + +# _LT_CHECK_MAGIC_METHOD +# ---------------------- +# how to check for library dependencies +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_MAGIC_METHOD], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +AC_CACHE_CHECK([how to recognize dependent libraries], +lt_cv_deplibs_check_method, +[lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# 'unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. + +case $host_os in +aix[[4-9]]*) + lt_cv_deplibs_check_method=pass_all + ;; + +beos*) + lt_cv_deplibs_check_method=pass_all + ;; + +bsdi[[45]]*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; + +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; + +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + # Keep this pattern in sync with the one in func_win32_libid. + lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc*) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +haiku*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) + +file_magic_glob= +want_nocaseglob=no +if test "$build" = "$host"; then + case $host_os in + mingw* | pw32*) + if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then + want_nocaseglob=yes + else + file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` + fi + ;; + esac +fi + +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method = "file_magic"]) +_LT_DECL([], [file_magic_glob], [1], + [How to find potential files when deplibs_check_method = "file_magic"]) +_LT_DECL([], [want_nocaseglob], [1], + [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM=$NM +else + lt_nm_to_check=${ac_tool_prefix}nm + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS=$lt_save_ifs + test -z "$ac_dir" && ac_dir=. + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break 2 + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break 2 + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS=$lt_save_ifs + done + : ${lt_cv_path_NM=no} +fi]) +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$DUMPBIN"; then : + # Let the user override the test. + else + AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in + *COFF*) + DUMPBIN="$DUMPBIN -symbols -headers" + ;; + *) + DUMPBIN=: + ;; + esac + fi + AC_SUBST([DUMPBIN]) + if test : != "$DUMPBIN"; then + NM=$DUMPBIN + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + +# _LT_CHECK_SHAREDLIB_FROM_LINKLIB +# -------------------------------- +# how to determine the name of the shared library +# associated with a specific link library. +# -- PORTME fill in with the dynamic library characteristics +m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], +[m4_require([_LT_DECL_EGREP]) +m4_require([_LT_DECL_OBJDUMP]) +m4_require([_LT_DECL_DLLTOOL]) +AC_CACHE_CHECK([how to associate runtime and link libraries], +lt_cv_sharedlib_from_linklib_cmd, +[lt_cv_sharedlib_from_linklib_cmd='unknown' + +case $host_os in +cygwin* | mingw* | pw32* | cegcc*) + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL + case `$DLLTOOL --help 2>&1` in + *--identify-strict*) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib + ;; + *) + lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback + ;; + esac + ;; +*) + # fallback: assume linklib IS sharedlib + lt_cv_sharedlib_from_linklib_cmd=$ECHO + ;; +esac +]) +sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd +test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO + +_LT_DECL([], [sharedlib_from_linklib_cmd], [1], + [Command to associate shared and link libraries]) +])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB + + +# _LT_PATH_MANIFEST_TOOL +# ---------------------- +# locate the manifest tool +m4_defun([_LT_PATH_MANIFEST_TOOL], +[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) +test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt +AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], + [lt_cv_path_mainfest_tool=no + echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD + $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out + cat conftest.err >&AS_MESSAGE_LOG_FD + if $GREP 'Manifest Tool' conftest.out > /dev/null; then + lt_cv_path_mainfest_tool=yes + fi + rm -f conftest*]) +if test yes != "$lt_cv_path_mainfest_tool"; then + MANIFEST_TOOL=: +fi +_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl +])# _LT_PATH_MANIFEST_TOOL + + +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM=-lm) + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test yes = "$GCC"; then + case $cc_basename in + nvcc*) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; + *) + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; + esac + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test ia64 = "$host_cpu"; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime + relocations are performed -- see ld's documentation on pseudo-relocs. */ +# define LT@&t@_DLSYM_CONST +#elif defined __osf__ +/* This system does not cope well with relocations in const data. */ +# define LT@&t@_DLSYM_CONST +#else +# define LT@&t@_DLSYM_CONST const +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +LT@&t@_DLSYM_CONST struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_globsym_save_LIBS=$LIBS + lt_globsym_save_CFLAGS=$CFLAGS + LIBS=conftstm.$ac_objext + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then + pipe_works=yes + fi + LIBS=$lt_globsym_save_LIBS + CFLAGS=$lt_globsym_save_CFLAGS + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test yes = "$pipe_works"; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +# Response file support. +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + nm_file_list_spec='@' +elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then + nm_file_list_spec='@' +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) +_LT_DECL([], [nm_file_list_spec], [1], + [Specify filename containing input files for $NM]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64, which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) + # IBM XL 8.0, 9.0 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test yes = "$GCC"; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + haiku*) + # PIC is the default for Haiku. + # The "-static" flag exists, but is broken. + _LT_TAGVAR(lt_prog_compiler_static, $1)= + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + + case $cc_basename in + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test ia64 = "$host_cpu"; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + # old Intel for x86_64, which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl* | bgxl* | bgf* | mpixl*) + # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms that do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac + +AC_CACHE_CHECK([for $compiler option to produce PIC], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds + ;; + cygwin* | mingw* | cegcc*) + case $cc_basename in + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + ;; + esac + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test yes != "$GCC"; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd* | bitrig*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu | gnu*) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + + # On some targets, GNU ld is compatible enough with the native linker + # that we're better off using the native interface for both. + lt_use_gnu_ld_interface=no + if test yes = "$with_gnu_ld"; then + case $host_os in + aix*) + # The AIX port of GNU ld has always aspired to compatibility + # with the native linker. However, as the warning in the GNU ld + # block says, versions before 2.19.5* couldn't really create working + # shared libraries, regardless of the interface used. + case `$LD -v 2>&1` in + *\ \(GNU\ Binutils\)\ 2.19.5*) ;; + *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; + *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + ;; + *) + lt_use_gnu_ld_interface=yes + ;; + esac + fi + + if test yes = "$lt_use_gnu_ld_interface"; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='$wl' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test ia64 != "$host_cpu"; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.19, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to install binutils +*** 2.20 or above, or modify your PATH so that a non-GNU linker is found. +*** You will then need to restart the configuration process. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test linux-dietlibc = "$host_os"; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test no = "$tmp_diet" + then + tmp_addflag=' $pic_flag' + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95* | pgfortran*) + # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; + xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + nvcc*) # Cuda Compiler Driver 2.2 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; + xlf* | bgf* | bgxlf* | mpixlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then + aix_use_runtimelinking=yes + break + fi + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GCC"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + case $cc_basename in + cl*) + # Native MSVC + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # Assume MSVC wrapper + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + esac + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2.*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + + hpux10*) + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test yes,no = "$GCC,$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + m4_if($1, [], [ + # Older versions of the 11.00 compiler do not understand -b yet + # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) + _LT_LINKER_OPTION([if $CC understands -b], + _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + ;; + esac + fi + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + # This should be the same for all languages, so no per-tag cache variable. + AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], + [lt_cv_irix_exported_symbol], + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" + AC_LINK_IFELSE( + [AC_LANG_SOURCE( + [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], + [C++], [[int foo (void) { return 0; }]], + [Fortran 77], [[ + subroutine foo + end]], + [Fortran], [[ + subroutine foo + end]])])], + [lt_cv_irix_exported_symbol=yes], + [lt_cv_irix_exported_symbol=no]) + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + osf3*) + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. GCC discards it without '$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test sequent = "$host_vendor"; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test sni = "$host_vendor"; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test yes,yes = "$GCC,$enable_shared"; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_CACHE_CHECK([whether -lc should be explicitly linked in], + [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), + [$RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + ]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting $shlibpath_var if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [postlink_cmds], [2], + [Commands necessary for finishing linking programs]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC=$CC +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report what library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC=$lt_save_CC +])# _LT_LANG_C_CONFIG + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_PATH_MANIFEST_TOOL])dnl +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_caught_CXX_error"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_CFLAGS=$CFLAGS + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + CFLAGS=$CXXFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test yes = "$GXX"; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test yes = "$GXX"; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='$wl' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test ia64 = "$host_cpu"; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag= + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac + + if test yes = "$GXX"; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`$CC -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' + fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' + else + # not using gcc + if test ia64 = "$host_cpu"; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' + else + shared_flag='$wl-bM:SRE' + fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag + else + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX([$1]) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then + # We only use this code for GNU lds that support --whole-archive. + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + else + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + case $GXX,$cc_basename in + ,cl* | no,cl*) + # Native MSVC + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='@' + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=.dll + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' + # The linker will not automatically build a static lib if we build a DLL. + # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + # Don't use ranlib + _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' + _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' + ;; + *) + # g++ + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd2.*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + haiku*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 6 and above use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + ;; + xl* | mpixl* | bgxl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd* | bitrig*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' + fi + output_verbose_link_cmd=func_echo_all + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + ;; + *) + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC* | sunCC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands '-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='func_echo_all' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + else + # g++ 2.7 appears to require '-G' NOT '-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We CANNOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ + '"$_LT_TAGVAR(old_archive_cmds, $1)" + _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ + '"$_LT_TAGVAR(reload_cmds, $1)" + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no + + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test yes != "$_lt_caught_CXX_error" + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_FUNC_STRIPNAME_CNF +# ---------------------- +# func_stripname_cnf prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# +# This function is identical to the (non-XSI) version of func_stripname, +# except this one can be used by m4 code that may be executed by configure, +# rather than the libtool script. +m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl +AC_REQUIRE([_LT_DECL_SED]) +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) +func_stripname_cnf () +{ + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; + esac +} # func_stripname_cnf +])# _LT_FUNC_STRIPNAME_CNF + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF +]) + +_lt_libdeps_save_CFLAGS=$CFLAGS +case "$CC $CFLAGS " in #( +*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; +*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; +esac + +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $prev$p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test x-L = "$p" || + test x-R = "$p"; then + prev=$p + continue + fi + + # Expand the sysroot to ease extracting the directories later. + if test -z "$prev"; then + case $p in + -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; + -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; + -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; + esac + fi + case $p in + =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; + esac + if test no = "$pre_test_object_deps_done"; then + case $prev in + -L | -R) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)=$prev$p + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" + fi + fi + prev= + ;; + + *.lto.$objext) ;; # Ignore GCC LTO objects + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test no = "$pre_test_object_deps_done"; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)=$p + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)=$p + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext +CFLAGS=$_lt_libdeps_save_CFLAGS + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_LANG_PUSH(Fortran 77) +if test -z "$F77" || test no = "$F77"; then + _lt_disable_F77=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_F77"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${F77-"f77"} + CFLAGS=$FFLAGS + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_LANG_PUSH(Fortran) + +if test -z "$FC" || test no = "$FC"; then + _lt_disable_FC=yes +fi + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test yes != "$_lt_disable_FC"; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_GCC=$GCC + lt_save_CFLAGS=$CFLAGS + CC=${FC-"f95"} + CFLAGS=$FCFLAGS + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test no = "$can_build_shared" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test yes = "$enable_shared" && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test yes = "$enable_shared" || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_FC" + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +CFLAGS=$GCJFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code=$lt_simple_compile_test_code + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +CFLAGS= +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + +# _LT_DECL_DLLTOOL +# ---------------- +# Ensure DLLTOOL variable is set. +m4_defun([_LT_DECL_DLLTOOL], +[AC_CHECK_TOOL(DLLTOOL, dlltool, false) +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program]) +AC_SUBST([DLLTOOL]) +]) + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f "$lt_ac_sed" && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test 10 -lt "$lt_ac_count" && break + lt_ac_count=`expr $lt_ac_count + 1` + if test "$lt_ac_count" -gt "$lt_ac_max"; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PATH_CONVERSION_FUNCTIONS +# ----------------------------- +# Determine what file name conversion functions should be used by +# func_to_host_file (and, implicitly, by func_to_host_path). These are needed +# for certain cross-compile configurations and native mingw. +m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +AC_MSG_CHECKING([how to convert $build file names to $host format]) +AC_CACHE_VAL(lt_cv_to_host_file_cmd, +[case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 + ;; + esac + ;; + *-*-cygwin* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin + ;; + *-*-cygwin* ) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; + * ) # otherwise, assume *nix + lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin + ;; + esac + ;; + * ) # unhandled hosts (and "normal" native builds) + lt_cv_to_host_file_cmd=func_convert_file_noop + ;; +esac +]) +to_host_file_cmd=$lt_cv_to_host_file_cmd +AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) +_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], + [0], [convert $build file names to $host format])dnl + +AC_MSG_CHECKING([how to convert $build file names to toolchain format]) +AC_CACHE_VAL(lt_cv_to_tool_file_cmd, +[#assume ordinary cross tools, or native build. +lt_cv_to_tool_file_cmd=func_convert_file_noop +case $host in + *-*-mingw* ) + case $build in + *-*-mingw* ) # actually msys + lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 + ;; + esac + ;; +esac +]) +to_tool_file_cmd=$lt_cv_to_tool_file_cmd +AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) +_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], + [0], [convert $build files to toolchain format])dnl +])# _LT_PATH_CONVERSION_FUNCTIONS diff --git a/thirdparty/ode-0.16.5/m4/ltoptions.m4 b/thirdparty/ode-0.16.5/m4/ltoptions.m4 new file mode 100644 index 0000000..94b0829 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/ltoptions.m4 @@ -0,0 +1,437 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 8 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option '$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [1], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for pkg in $enableval; do + IFS=$lt_save_ifs + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the 'disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' +# LT_INIT options. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the 'pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/thirdparty/ode-0.16.5/m4/ltsugar.m4 b/thirdparty/ode-0.16.5/m4/ltsugar.m4 new file mode 100644 index 0000000..48bc934 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/ltsugar.m4 @@ -0,0 +1,124 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59, which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/thirdparty/ode-0.16.5/m4/ltversion.m4 b/thirdparty/ode-0.16.5/m4/ltversion.m4 new file mode 100644 index 0000000..fa04b52 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# @configure_input@ + +# serial 4179 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.4.6' +macro_revision='2.4.6' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/thirdparty/ode-0.16.5/m4/lt~obsolete.m4 b/thirdparty/ode-0.16.5/m4/lt~obsolete.m4 new file mode 100644 index 0000000..c6b26f8 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/lt~obsolete.m4 @@ -0,0 +1,99 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 5 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) +m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) +m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) +m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) +m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) +m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) +m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) diff --git a/thirdparty/ode-0.16.5/m4/pkg.m4 b/thirdparty/ode-0.16.5/m4/pkg.m4 new file mode 100644 index 0000000..73973f7 --- /dev/null +++ b/thirdparty/ode-0.16.5/m4/pkg.m4 @@ -0,0 +1,157 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 1 (pkg-config-0.24) +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +# only at the first occurence in configure.ac, so if the first place +# it's called might be skipped (such as if it is within an "if", you +# have to call PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT]) + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .]) + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])# PKG_CHECK_MODULES diff --git a/thirdparty/ode-0.16.5/missing b/thirdparty/ode-0.16.5/missing new file mode 100644 index 0000000..f62bbae --- /dev/null +++ b/thirdparty/ode-0.16.5/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program 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 General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/thirdparty/ode-0.16.5/ode-config.cmake.in b/thirdparty/ode-0.16.5/ode-config.cmake.in new file mode 100644 index 0000000..210b558 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode-config.cmake.in @@ -0,0 +1,13 @@ +set(ODE_VERSION "@VERSION@") +set(ODE_VERSION_MAJOR "@VERSION_MAJOR@") +set(ODE_VERSION_MINOR "@VERSION_MINOR@") +set(ODE_VERSION_PATCH "@VERSION_PATCH@") + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/ode-export.cmake") + +set(ODE_DEFINITIONS "") +set(ODE_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include") +set(ODE_LIBRARY_DIRS "${PACKAGE_PREFIX_DIR}/lib") +set(ODE_LIBRARIES "ODE::ODE") diff --git a/thirdparty/ode-0.16.5/ode-config.in b/thirdparty/ode-0.16.5/ode-config.in new file mode 100644 index 0000000..78fa45e --- /dev/null +++ b/thirdparty/ode-0.16.5/ode-config.in @@ -0,0 +1,53 @@ +#!/bin/sh + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +exec_prefix_set=no + +usage="\ +Usage: ode-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--cflags] [--libs]" + +if test $# -eq 0; then + echo "${usage}" 1>&2 + exit 1 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + if test $exec_prefix_set = no ; then + exec_prefix=$optarg + fi + ;; + --prefix) + echo $prefix + ;; + --exec-prefix=*) + exec_prefix=$optarg + exec_prefix_set=yes + ;; + --exec-prefix) + echo $exec_prefix + ;; + --version) + echo @ODE_VERSION@ + ;; + --cflags) + echo -I@includedir@ + ;; + --libs) + echo -L@libdir@ -lode + ;; + *) + echo "${usage}" 1>&2 + exit 1 + ;; + esac + shift +done diff --git a/thirdparty/ode-0.16.5/ode.pc.in b/thirdparty/ode-0.16.5/ode.pc.in new file mode 100644 index 0000000..9fa106a --- /dev/null +++ b/thirdparty/ode-0.16.5/ode.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +precision=@ODE_PRECISION@ + +Name: ode +Description: Open Dynamics Engine +Version: @ODE_VERSION@ +Libs: -L${libdir} -lode +Libs.private: -lstdc++ -lm +Cflags: -I${includedir} diff --git a/thirdparty/ode-0.16.5/ode/Makefile.am b/thirdparty/ode-0.16.5/ode/Makefile.am new file mode 100644 index 0000000..656e7a4 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/Makefile.am @@ -0,0 +1,6 @@ +SUBDIRS = src doc +if ENABLE_DEMOS + SUBDIRS += demo +endif + +#EXTRA_DIST = doc diff --git a/thirdparty/ode-0.16.5/ode/Makefile.in b/thirdparty/ode-0.16.5/ode/Makefile.in new file mode 100644 index 0000000..eae84a5 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/Makefile.in @@ -0,0 +1,643 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@ENABLE_DEMOS_TRUE@am__append_1 = demo +subdir = ode +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = src doc demo +am__DIST_COMMON = $(srcdir)/Makefile.in README TODO +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = src doc $(am__append_1) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ode/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign ode/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +#EXTRA_DIST = doc + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/ode/README b/thirdparty/ode-0.16.5/ode/README new file mode 100644 index 0000000..dd4596f --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/README @@ -0,0 +1,158 @@ +Dynamics Library. +================= + +CONVENTIONS +----------- + +matrix storage +-------------- + +matrix operations like factorization are expensive, so we must store the data +in a way that is most useful to the matrix code. we want the ability to update +the dynamics library without recompiling applications, e.g. so users can take +advantage of new floating point hardware. so we must settle on a single +format. because of the prevalence of 4-way SIMD, the format is this: store +the matrix by rows or columns, and each column is rounded up to a multiple of +4 elements. the extra "padding" elements at the end of each row/column are set +to 0. this is called the "standard format". to indicate if the data is stored +by rows or columns, we will say "standard row format" or "standard column +format". hopefully this decision will remain good in the future, as more and +more processors have 4-way SIMD, and 3D graphics always needs fast 4x4 +matrices. + +exception: matrices that have only one column or row (vectors), are always +stored as consecutive elements in standard row format, i.e. there is no +interior padding, only padding at the end. + +thus: all 3x1 floating point vectors are stored as 4x1 vectors: (x,x,x,0). +also: all 6x1 spatial velocities and accelerations are split into 3x1 position + and angular components, which are stored as contiguous 4x1 vectors. + +ALL matrices are stored by in standard row format. + + +arguments +--------- + +3x1 vector arguments to set() functions are supplied as x,y,z. +3x1 vector result arguments to get() function are pointers to arrays. +larger vectors are always supplied and returned as pointers. +all coordinates are in the global frame except where otherwise specified. +output-only arguments are usually supplied at the end. + + +memory allocation +----------------- + +with many C/C++ libraries memory allocation is a difficult problem to solve. +who allocates the memory? who frees it? must objects go on the heap or can +they go on the stack or in static storage? to provide the maximum flexibility, +the dynamics and collision libraries do not do their own memory allocation. +you must pass in pointers to externally allocated chunks of the right sizes. +the body, joint and colllision object structures are all exported, so you +can make instances of those structure and pass pointers to them. + +there are helper functions which allocate objects out of areans, in case you +need loots of dynamic creation and deletion. + +BUT!!! this ties us down to the body/joint/collision representation. + +a better approach is to supply custom memory allocation functions +(e.g. dlAlloc() etc). + + +C versus C++ ... ? +------------------ + +everything should be C linkable, and there should be C header files for +everything. but we want to develop in C++. so do this: + * all comments are "//". automatically convert to /**/ for distribution. + * structures derived from other structures --> automatically convert? + + +WORLDS +------ + +might want better terminology here. + +the dynamics world (DWorld) is a list of systems. each system corresponds to +one or more bodies, or perhaps some other kinds of physical object. +each system corresponds to one or more objects in the collision world +(there does not have to be a one-to-one correspondence between bodies and +collision objects). + +systems are simulated separately, perhaps using completely different +techniques. we must do something special when systems collide. +systems collide when collision objects belonging to system A touch +collision objects belonging to system B. + +for each collision point, the system must provide matrix equation data +that is used to compute collision forces. once those forces are computed, +the system must incorporate the forces into its timestep. +PROBLEM: what if we intertwine the LCP problems of the two systems - then +this simple approach wont work. + +the dynamics world contains two kinds of objects: bodies and joints. +joints connect two bodies together. + +the world contains one of more partitions. each partition is a collection of +bodies and joints such that each body is attached (through one or more joints) +to every other body. + +Joints +------ + +a joint can be connected to one or two bodies. +if the joint is only connected to one body, joint.node[1].body == 0. +joint.node[0].body is always valid. + + +Linkage +------- + +this library will always be statically linked with the app, for these reasons: + * collision space is selected at compile time, it adds data to the geom + objects. + + +Optimization +------------ + +doubles must be aligned on 8 byte boundaries! + + +MinGW on Windows issues +----------------------- + +* the .rc file for drawstuff needs a different include, try winresrc.h. + +* it seems we can't have both main() and WinMain() without the entry point + defaulting to main() and having resource loading problems. this screws up + what i was trying to do in the drawstuff library. perhaps main2() ? + +* remember to compile resources to COFF format RES files. + + + +Collision +--------- + +to plug in your own collision handling, replace (some of?) these functions +with your own. collision should be a separate library that you can link in +or not. your own library can call components in this collision library, e.g. +if you want polymorphic spaces instead of a single statically called space. + +creating an object will automatically register the appropriate +class (if necessary). how can we ensure that the minimum amount of code is +linked in? e.g. only one space handler, and sphere-sphere and sphere-box and +box-box collision code (if spheres and boxes instanced). + +the user creates a collision space, and for each dynamics object that is +created a collision object is inserted into the space. the collision +object's pos and R pointers are set to the corresponding dynamics +variables. + +there should be utility functions which create the dynamics and collision +objects at the same time, e.g. dMakeSphere(). + +collision objects and dynamics objects keep pointers to each other. diff --git a/thirdparty/ode-0.16.5/ode/TODO b/thirdparty/ode-0.16.5/ode/TODO new file mode 100644 index 0000000..cdf71dc --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/TODO @@ -0,0 +1,698 @@ + +@@@'s + + +TODO for COLLISION +------------------ + +box-box collision: adjust generated face-face contact points by depth/2 to +be more fair. + +what happens when a GeomTransform's encapsulated object is manipulated, +e.g. position changed. should this be disallowed? should a GeomTransform +behave like a space and propagate dirtyness upwards? + +make sure that when we are using a large space for static environmental geoms, +that there is not excessive AABB computation when geoms are added/removed from +the space. the space AABB is pretty much guaranteed to cover everything, so +there's no need to compute/test the AABB in this case. + +hash space: implement collide2() efficiently instead of the current +simple-space-like brute-force approach. + +hash space: incremental scheme, so we dont have to rebuild the data structures +for geoms that don't move. + +disabled geoms (remove from all collision considerations) ... isn't this the +same as just taking it out of its enclosing group/space? + +integrate: + dRay + triangle collider - get latest tri collider code from erwin + erwin's quadtree space + +tests: + all aspects of collision API + + dGeomSetBody(0) maintains body-geom linked list properly. + + simple space: instantiate lots of non-moving geoms (i.e. environmental + geoms and make sure that we're still able to collide efficiently. + make sure AABB computation is efficient, or can be made efficient + through proper use of the API. + + test C interface support for making new classes. + make sure the dxGeom::aabbTest() function behaves as advertised. + + testing for contact point consistency: test for things that + would cause the dynamics to fail or become unstable + + test for: small adjustment in geom position causes a big jump in the + contact point set (bad for dynamics). + + test for: if contact constraints observed then it's impossible + (or hard) to move the objects so that the penetration is + increased. relax this when only a subset of the contact points are + returned. + + test for consistency, e.g. the boundary of geoms X and Y can + be defined by intersecting with a point, so test the intersection of X + and Y by comparing with the point tests. + + check that contact points are in the collision volume + + all existing space tests, and more. + +demos: + test_buggy: make a terrain out of non-moving geoms. use heirarchical + groups to get efficient collision, even with the simple space. + +go though the new collision docs and make sure the behavior that is described +there is actually implemented. + +multi-resolution hash table: + the current implementation rebuilds a new hash table each time + collide() is called. we don't keep any state between calls. this is + wasteful if there are unmoving objects in the space. + + make sure we prevent multiple collision callbacks for the same pair + + better virtual address function. + + the collision search can perhaps be optimized - as we search + chains we can come across other candidate intersections at + other levels, perhaps we should do the intersection check + straight away? --> save on list searching time only, which is + not too significant. + +collision docs: + optimization guide: whenever a single geom changes in a simple space, + the space AABB has to be recomputed by examining EVERY geom. + document this, or find a better behavior. + + + +TODO BEFORE NEXT RELEASE +------------------------ + +g++ needed for compiling tests using gcc 3.2 ? what is the problem? + +add joint feedback info from lambda, so that we can get motor forces etc. +need a way to map constraint indexes to what they mean. + +track down and fix the occasional popping/jumping problem in test_boxstack, +especially when boxes are piled on top of each other. find out if this is +caused by a configuration singularity or whether there is a bug in LCP. +i need to add some kind of diagnostic tool to help resolve these kinds of +problems. + +fixup ground plane jitter and shadow jumping in drawstuff. + +the inertias/COMs don't appear to be totally correct for the boxstack demo. +fix up, and add a mode that shows the effective mass box (for a given density). + +Improve box-box collision, especially for face-face contact (3 contact points). +Improve cylinder-box collision (2 contact points). + +windows DLL building and unix shared libs. libtool? +also MSVC project files. + +dBodyGetPointVel() + +contrib directory - all stuff in ~/3/ode + +functions to allow systems to be copied/cloned + dBodyTransplant (b, world) + dTransplantIsland (b, world) + dBodyCopy (bdest, bsrc) + dJointCopy (jdest, jsrc) -- what about body connections? + dCloneBody() + dCloneJoint() + dCloseBodyAndJointList() + dCloneIsland() + +this collision rule: + // no contacts if both geoms on the same body, and the body is not 0 + if (g1->body == g2->body && g1->body) return 0; +needs to be replaced. sometimes we want no collision when both bodies are 0, +but this wont work for geomgroup-to-environment. avoid stupid stuff like + dGeomSetBody (geom_group, (dBodyID) 1); +this also causes "failed-to-report" errors in the space test. + +Expose type-specific collision functions? + +Automatic code optimization process. + +joint limit spongyness: interacts with powered joints badly, because when the +limit is reached full power is applied. fix or doc. + +various hinge2 functions may not function correctly if axis1 and axis2 are not +perpendicular. in particular the getAngle() and getAngleRate() functions +probably will give bogus answers. + +slow step function will not respect the joint getinfo2 functions calling +addTorque() because it reads the force/torque accumulators before the +getinfo2 functions are called. + +spaces need multiple lists of objects that can never overlap. objects in these +lists are never tested against each other. + +deleting a body a joint is attached to should adjust the joint to only have +one body attached. currently the connected joints have *both* their body +attachments removed. BUT, dont do this if the dJOINT_TWOBODIES flag is set +on the joint. + +document error, mem and math functions. + +Web pages + credits section + projects using ODE + +update C++ interface? use SWIG? + +collision exclusion groups - exclude if obj1.n == obj2.n ? + +make sure the amotor joint can be used with just one body. at the moment it +only allows two-body attachments. + +implement dJointGetAMotorAngleRate() + +erwin says: Should the GeomGroup have a cleanupmode as the GeomTransform has? + +erwin says: http://q12.org/pipermail/ode/2002-January/000766.html + and http://q12.org/pipermail/ode/2001-December/000753.html + +rename duplicate filenames (object.h?) - some environments can't handle this. + +naming inconsistency: dCreateSphere() should be dSphereCreate() (etc...) to +match the rest of the API. + + +TODO +---- + +joint allocation in joint groups. allocation size should be rounded up using +dEFFICIENT_SIZE, to properly align all the data members. + +all dAlloc() allocations should be aligned using dEFFICIENT_SIZE() ??? + +automatic body & joint disabling / enabling. + +sometimes getting LCP infinite loops. + +function to get the entire island of bodies/joints + +joints: + hinge2 joint - implement trail, i.e. non-convergent steering and wheel + axes. + + erp individually settable for each joint? + + more joints: + angular3 (constrian full angle not position) + fixed path 1 (point must follow fixed path, etc etc) + - other fixed path joints. + linear a (point in 1 body fixed to plane of other) + linear b (point in 1 body fixed to line on other) + linear c (line in 1 body fixed to plane on other) + linear d (line in 1 body fixed to line on other) - like + prismatic but orientation along line can change + Relative-Path-Relative-Oriention Joint (set all dofs of 2 + bodies relative to each other) + spring (with natural length) + universal (2 kinds) + various angular relationships + + when attaching joints to static env, provision to move attachment + point (e.g. give it a linear/angular velocity). this can be used + instead of a FPFO joint on a body in many cases. + also do this with contacts to static env, to allow for contacts to + *moving* objects in the static env. + + interpretation of erp: is it (1) the error reduction per timestep, + (2) or a time constant independent of timestep?? if it's (2) then + perhaps this should be universal - this is already the meaning for + the suspension. + + hinge2 suspension: + suspension limits + suspension limit restitution and spongyness?? + +use autoconf? set paths in makefile? + +no-arg init functions, for andy + +explore: do joint parameters need to be set for the joint to be setup +correctly, or should set some proper body-dependent params when it is +attached? this is only really an issue for joints that have no parameters to +set, such as the fixed joint. + +dAlloc() should take an arena parameters which is stored in dWorld. + +debugging mode should use dASSERT2 that prints a descriptive error message +on error, not just the file:line or function. use dASSERT for internal +consistency checking. + +when vectors and matrices are initialized, we must ensure that the padding +elements are set to 0. this is going to be a problem everywhere! + +don't use 3-vectors anywhere. use SIMD friendly 4-vectors. + +make sure all data in body/joint etc objects is aligned well for single +precision SIMD (i.e. all vectors start on a 16 byte boundary). + +think about more complicated uses of collision, e.g. a single geom representing +an articulated structure. + +bodyGroup? (like joint group but for bodies). systemGroup? + +check the overhead of resizing Array<>s as elements are pushed on to them. + +replace alloca() with dPushFrame(), dPopFrame(), and dAlloca() ? allow for +the possibility of allocating in non-stack memory ? + +make sure that we can set mass parameters with non-zero center of mass. +if this is done after the body position is set, the position is adjusted. +if this is done before the body position is set, what do we do when the +pos is set? does the pos always refer to the center of mass from the user's +point of view? + +consider splitting solver into functions, which can be optimized separately. +might make things go faster. + +faster code for islands with a single body? faster code for dynamically +symmetric bodies? + +rotation.cpp functions that set matrices should also set padding elements. + +lcp solver must return (L,d) and some other information, so we can re-solve +for other right hand sides later on, but using the same complimentarity +solution so there are no integrator discontinuities. + +dSetZero() - make fast inline functions for fixed n e.g. (1-4). + +need proper `sticky' friction, i.e. compensation for numerical slip. + +on windows, make sure gcc-compiles libs can be linked with VC++ apps. need +to make sure some C++ runtime bits are present? + +kill all references to dArray<> (in geom.cpp). + +need testing code to test all joints with body-to-static-env + +copy stack.cpp, memory.cpp stuff to reuse + +dFactorLDLT() is not so efficient for matrix sizes < block size, e.g. +redundant calls, zero loads, adds etc + +contacts: cheaper friction: viscous friction? one step delay friction force. + +in geom.cpp, for objects that are never meant to collide, dCollide() will +always try to find the collider functions, which wastes a bit of time. + +geom.cpp:dCollideG() - handle special case of colliding 2 groups more +efficiently. + +timer reporting function: + void timerReport (void (*printFunction)(char *, ...)); + +disabled bodies stored in a separate list, so they are never traversed at all, +for speed when there are many disabled bodies. + + +MAYBE +----- + +new implementation for joint groups that is not so system dependent. +maybe individual contacts are reusable? in this case contact information +should be settable in the contact joints. max_size arg is really annoying. + +consider making anchor,axis, (everything) into a joint parameter and setting +them with a consistent interface. also consider overload the joint functions +so they are not distinguished by joint type?? + +collision memory optimizations? + +collision: support for persistent contact information? + +multiply reference tri list data so that it can be cloned + if the tri-list geoms could support rot/pos + transformations then we could have several tri-lists pointing to the + same vertex information. + +height fields + +pre-converted collision data -- Creating a hash space and associated +opcode tree structures may take significant amounts of time for a +large world with many 10s of thousands of triangles. Any chance of +pre-building that off-line and passing a memory block pointer to the +collision system? + +putting objects in multiple spaces -- If it was possible to add +objects to more than one space, you could do collision queries other +than 1vsN and NvsN. That flexibility might be useful when you want to +only collide against a subset of the space. For example, a camera +system may want to collide some rays with occlusion walls but the +occlusion walls may also need to be in the game-level space to bounce +against. + + +ALWAYS +------ + +make sure functions check their arguments in debug mode (e.g. using dASSERT). +make sure joint/geom functions check for the specific object type. + +vectors alloca()ed on the stack must have the correct alignment, use ALLOCA16. + +library should have no global constructors, as it might be used with C linkage. + +use `const' in function arguments. blah. + + + +DON'T BOTHER +------------ + +warning if user tries to set mass params with nonzero center of mass. + + + +DONE +---- + +check: when contact attached with (body1,0) and (0,body1), check that polarity +on depth and error info is okay for the two cases. + +set a better convention for which is the 1st and 2nd body in a joint, because +sometimes we get things swapped (because of the way the joint nodes are used). + +hinge and prismatic, attachment to static environment. + +turn macros into C++ inline functions? what about C users? + +remove `space' argument to geom creation functions? make user add it? +or just remove it from dCreateGeom() ? <-- did this one. + +test_chain should be in C, not C++. but first must remove global constructors. + +add more functionality to C++ interface - dMass, dSpace, dGeom + +there should be functions to delete groups of bodies/joints in one go - this +will be more efficient than deleting them one at a time, because less +partitioning tests will be needed. + +should we expose body and joint object structures so that the user can +explicitly allocate them locally, or e.g. on the stack? makes allocating +temporary contact constraints easier. NO --> helps data hiding and therefore +library binary compatability. + +joints: + hinge & slider - DONE + measure angle, rate - DONE + power - DONE + joint limits - DONE + mixed powered+limited joints, powering away from limit - DONE + + hinge2 - DONE + steering angle and rate measurement - DONE + steering limits - DONE + steering motor - DONE + wheel motor - DONE + wheel angle rate measurement - DONE + + optional hinge2 suspension: - DONE + alignment of B&S part to given axis - DONE + global framework for giving epsilon and gamma - DONE + + toss away r-motor, make power & stuff specific to joint - DONE + it's just easier that way + + joint code reuse: - DONE + use standard functions to set velocity (c), limits (lo,hi), + spongyness (epsilon) etc, this prevents these functions from + proliferating + + implicit spring framework - actually allow joints to return a value `k' + such that J*vnew = c + k*f, where f = force needed to achieve + vnew - DONE + + contact slip - DONE + contact erp & cfm parameters (not just "softness") - DONE + + hinge2: when we lock back wheels along the steering axis, there is no + error correction if they get out of alignment - DONE, just use high + and low limits. + + joint limit spongyness: erp and cfm for joint set from world (global) + values when joint created. - DONE + + joint limit restitution - DONE + +check inertia transformations, e.g. by applying steering torque to a thin +wheel --> actually, i made test_I + +more comprehensive random number comparisons between slow and fast methods. + - random PD inertia (not just diagonal). + - random velocity + - random joint error (make joints then move bodies a bit) + +check that J*vnew=c (slow step already does this, but it doesn't equal zero +for some reason! - actually, when LCP constraint limits are reached, it wont!) + +tons of things in lcp.cpp (@@@), especially speed optimizations. also, we +wanted to do index block switching and index block updates to take advantage +of the outer product trick ... but this is not worth the effort i think. + +lcp.cpp: if lo=hi=0, check operation. can we switch from NL <-> NH without +going through C? --> done. + +andy says: still having trouble with those resource files.. +drawstuff.res doesn't seem to build or be found under cygwin gcc. + +DOC how bodies and geoms associated then resolved in contact callback ... not +really necessary. + +fix the "memory leak" in geom.cpp + +library should have no global constructors, as it might be used with C linkage. + --> as long as test_chain1 works, there are none. + +DOC cfm, the derivation and what it means. + --> partially done, could be better + +joint "get type" function + +andy says: in ode/src/error.cpp _snprintf() and _vsnprintf() are missing +in testode: finite and isnan are missing. copysign is missing + russ: okay here's the problem: i have Makefile.platform files for + VC++, MinGW, but not Cygwin. Cygwin uses the unix-like functions + for everything, but the VC++/MinGW configs assumes the MS C-runtime + functions. this is easy to fix, except i need to install Cygwin + which is a pain to do over MinGW. argh. + +build on linux - assumptions made about location of X11 lib, opengl etc. + +implement: dBodyAddForceAtPos,dBodyAddRelForceAtPos,dBodyAddRelForceAtRelPos, + dBodyGetPointPos,dBodyGetPointVel,dBodyGetPointRelVel + +dJointAttach(), allow both bodies to be 0 to put the joint into limbo. + +space near-callback should be given potentially intersecting objects 100 at a +time instead of 1 at a time, to save on calling costs ... which are trivial, +so we don't bother to do this. + +doccer: @func{} also refs second etc function in function *list*. + +make sure joints can return 0 from GetInfo1, i.e. no constraints or "inactive" +joint, and the step functions will handle it. + +when attaching contact with (0,body), instead of setting the reverse flag +on the joint and checking it in getInfo2(), we should just reverse the normal +straight away ... ? + --> trouble is, dJointAttach() knows nothing about what kind of joint + it is attaching. + +hinge2 needs to be attached to two bodies for it to work, make sure this is +always the case. --> assertion added in dJointAttach(). + +if two joints connect to the same two bodies, check that the fast solver +works! -> it should. + +functions to get all the joints/bodies a body/joint is connected to. + +If I don't have the GCC libraries installed, HUGE_VALF is undefined. + +fix capped cylinder - capped cylinder collision so that two contacts can +be generated. + +transformation geometry object. + +joint groups should also be destroyed by destroying the world --> naaahhh. + +DONT DO THIS: body/joint creators with world = 0 --> not inserted into any +world. allow bodies/joints to be detached from a world (this is what happens +to grouped joints when a world is destroyed). + can bodies and joints be linked together when not attached to world?? + what happens when we have an island of b/j, some of which are not in + world? soln: dont keep lists of b/j in the world, just infer it from + the islands? + +body & joint disabling / enabling + +start a change log. + +collision flags - 0xffff mask. + +dBodyGetFiniteRotationMode() / ...Axis() + +dBodyAddForceAtRelPos() + +ball & socket joint limits and motors. + +auto-build env on windows: 3 compilers, debug/release, short/double = +12 combinations --> auto logs. + +handle infinities better: HUGE_VALF is not commanly defined, it seems. +get rid of the __USE_ISOC9X macro in common.h +perhaps just use a "big" number instead of the actual IEEE infinity, it's +more portable anyway. + --> new config system + +dCloseODE() - tidy up *all* allocated memory, esp in geom.cpp. used to keep +leak detectors happy. + +extra API to get lambda and J'*lambda from last timestep. + +better stack implementation that is not so system dependent. but how will +we do dynamic page allocation? do we even need to? + + +all collision files will now be collision_*, not geom_* + +check exported global symbols - no C++ mangling. + +rename dSphere etc to dxSphere etc. + +C interface support for making new classes. + +make sure DLL-ized stuff preserved ... but class numbers should no longer be +exported. + +point geom ( = sphere of radius 0 ) + +geoms stored in doubly linked lists in space (fast removal). + +bodies need to keep geoms pointers and call dGeomMoved() in dBodySetPosition() +etc and world step. PROBLEM: links dynamics and collision together too much, +makes it hard to extract ODE collision ... unless we say: dGeomMoved() and +dGeomID must be supplied by the new collision library! + +dCollide() should take spaces as arguments - it should call dSpaceCollide2() +with its own callback that puts all found contacts in the array, stopping +when there is no more space left in the array. + +dxSpace::getGeom() - the geom numbers will change as geoms are dirtied - find +some other numbering scheme, or document this behavior. + +the 'placeable' property - objects that should not ever be attached to bodies +should flag an error when setBody etc are called. + +dGeomSetBody(0) - DOC: the position and orientation of the body will be +preserved. in this case the geom should NOT be dirtied (dGeomMoved() should +not be called). + +DOC: dGeomGetBodyNext() as part of dynamics/collision interface + +groups/spaces are subclasses of geom. + +groups/spaces can contain other groups/spaces. geom can be owned by a +group/space. collision handling: + geom-geom : standard collision function + geom-group : special space code + group-group : n^2 tests (or n space tests) - hard to optimize because + of disjoint space representations. + group internal : normal space internal-collision code + +groups/spaces can be told that some objects never move, i.e. that the objects +are locked. should we lock the whole space? + locking: the AABB for the object is not recalculated + +groups/spaces can be told that the internal contents self-intersect or not. +actually an old ODE group is the equivalent of an old ODE simple space. + - just call dCollide() or not. + +the group doesn't get passed to the space callback anymore ... only the +intersecting geoms get passed? maybe the callback can initiate the extra +intersection tests itself? (because we want programmable flexibility to +determine what gets intersected and what doesn't) + - NO + +infrastructure to indicate when an object has moved (and thus its AABB needs +to be recalculated) + +space enumeration functions. make sure that there are no additions or deletions +while enumeration is taking place. + - documented the behavior, didn't disallow it + +cache the AABB in the dxGeom? (for non-moving objects) - perhaps keep a +pointer to separately allocated space? ... no + +DOC: dGeomGetClass() is a first-class geom function, not in the "User +defined classes" section. it returns a constant that can be checked +against dSphereClass etc. + +remove dxGeom dependence on dBodyID? ... not yet + +dBase -> dxBase + +allow a geom to be inserted into multiple spaces? need this to optimize some +kinds of tests ... no + +update docs. + +make CHECK_NOT_LOCKED an assert. + +DOC: "Calling these functions on a non-placeable geom results in a +runtime error." ...in the debug build only? + +non-placeable geoms should not allocate dxPosR. perhaps pass a dGeom +constructor arg that says 'placeable' or not - this also sets the +GEOM_PLACEABLE flag. + +GeomTransform: + final_pos and final_R valid if no GEOM_AABB_BAD flag!!! + fix up this code, esp use of ComputeTX(). + +Space incompatibilities: no dSpaceDestroy(), dGeomDestroy() does not +take a dSpaceID ... dSpaceDestroy() added. + +GeomGroup incompatibilities: + dCollide() used to take a GeomGroup and would return all the contact + points for all the intersecting objects. now you have to call + dSpaceCollide2() and get a callback for each one. + need to provide old behavior. + +simple space optimization: we should keep the precomputed AABB for the +non-moving geoms around, so that when the other geoms move we can just +compute the AABBs for those geoms and then combine it with the non-moving AABB. + --> too hard! + +collision build options: old and new + +tidyups for collision: + * rationalize what stuff goes in what source files, and file names + * minimize set of header files that all collision* sources use - after + all changes. + * update ode-cpp stuff (C++ interface header files). + +porting guide: + ODE list email + + dGeomGetSpaceAABB() deleted + + dGeomGetClass (geom_group); used to return a unique type for + GeomGroups, but now it returns dSimpleSpaceID. + +tidyups: update DLL declarations. + diff --git a/thirdparty/ode-0.16.5/ode/demo/Makefile.am b/thirdparty/ode-0.16.5/ode/demo/Makefile.am new file mode 100644 index 0000000..5d02b87 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/Makefile.am @@ -0,0 +1,75 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -DDRAWSTUFF_TEXTURE_PATH="\"$(abs_top_srcdir)/drawstuff/textures\"" + +if X11 +AM_LDFLAGS = $(X_PRE_LIBS) $(X_LIBS) $(X_EXTRA_LIBS) +endif + +# On Windows, GL_LIBS must go after libdrawstuff.la. +LDADD = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la @GL_LIBS@ + +noinst_HEADERS = basket_geom.h bunny_geom.h convex_bunny_geom.h convex_prism.h \ + icosahedron_geom.h halton235_geom.h texturepath.h world_geom3.h + +AM_DEFAULT_SOURCE_EXT = .cpp + +noinst_PROGRAMS = \ + demo_boxstack \ + demo_buggy \ + demo_cards \ + demo_chain1 \ + demo_chain2 \ + demo_collision \ + demo_convex \ + demo_crash \ + demo_cylvssphere \ + demo_dball \ + demo_dhinge \ + demo_transmission \ + demo_feedback \ + demo_friction \ + demo_gyroscopic \ + demo_gyro2 \ + demo_heightfield \ + demo_hinge \ + demo_I \ + demo_jointPR \ + demo_joints \ + demo_jointPU \ + demo_kinematic \ + demo_motion \ + demo_motor \ + demo_ode \ + demo_piston \ + demo_plane2d \ + demo_rfriction \ + demo_slider \ + demo_space \ + demo_space_stress \ + demo_step \ + demo_tracks + +demo_chain1_SOURCES = demo_chain1.c +demo_chain1_LDADD = $(LDADD) -lstdc++ + + +if TRIMESH +noinst_PROGRAMS += \ + demo_basket \ + demo_cyl \ + demo_moving_trimesh \ + demo_moving_convex \ + demo_trimesh + +AM_CPPFLAGS += -DdTRIMESH_ENABLED +endif + + + +if WIN32 +resources.o: $(top_srcdir)/drawstuff/src/resources.rc $(top_srcdir)/drawstuff/src/resource.h + @WINDRES@ $(top_srcdir)/drawstuff/src/resources.rc -o resources.o +LDADD += resources.o +endif diff --git a/thirdparty/ode-0.16.5/ode/demo/Makefile.in b/thirdparty/ode-0.16.5/ode/demo/Makefile.in new file mode 100644 index 0000000..368a5d1 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/Makefile.in @@ -0,0 +1,1133 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = demo_boxstack$(EXEEXT) demo_buggy$(EXEEXT) \ + demo_cards$(EXEEXT) demo_chain1$(EXEEXT) demo_chain2$(EXEEXT) \ + demo_collision$(EXEEXT) demo_convex$(EXEEXT) \ + demo_crash$(EXEEXT) demo_cylvssphere$(EXEEXT) \ + demo_dball$(EXEEXT) demo_dhinge$(EXEEXT) \ + demo_transmission$(EXEEXT) demo_feedback$(EXEEXT) \ + demo_friction$(EXEEXT) demo_gyroscopic$(EXEEXT) \ + demo_gyro2$(EXEEXT) demo_heightfield$(EXEEXT) \ + demo_hinge$(EXEEXT) demo_I$(EXEEXT) demo_jointPR$(EXEEXT) \ + demo_joints$(EXEEXT) demo_jointPU$(EXEEXT) \ + demo_kinematic$(EXEEXT) demo_motion$(EXEEXT) \ + demo_motor$(EXEEXT) demo_ode$(EXEEXT) demo_piston$(EXEEXT) \ + demo_plane2d$(EXEEXT) demo_rfriction$(EXEEXT) \ + demo_slider$(EXEEXT) demo_space$(EXEEXT) \ + demo_space_stress$(EXEEXT) demo_step$(EXEEXT) \ + demo_tracks$(EXEEXT) $(am__EXEEXT_1) +@TRIMESH_TRUE@am__append_1 = \ +@TRIMESH_TRUE@ demo_basket \ +@TRIMESH_TRUE@ demo_cyl \ +@TRIMESH_TRUE@ demo_moving_trimesh \ +@TRIMESH_TRUE@ demo_moving_convex \ +@TRIMESH_TRUE@ demo_trimesh + +@TRIMESH_TRUE@am__append_2 = -DdTRIMESH_ENABLED +@WIN32_TRUE@am__append_3 = resources.o +subdir = ode/demo +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/ode/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@TRIMESH_TRUE@am__EXEEXT_1 = demo_basket$(EXEEXT) demo_cyl$(EXEEXT) \ +@TRIMESH_TRUE@ demo_moving_trimesh$(EXEEXT) \ +@TRIMESH_TRUE@ demo_moving_convex$(EXEEXT) \ +@TRIMESH_TRUE@ demo_trimesh$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +demo_I_SOURCES = demo_I.cpp +demo_I_OBJECTS = demo_I.$(OBJEXT) +demo_I_LDADD = $(LDADD) +demo_I_DEPENDENCIES = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +demo_basket_SOURCES = demo_basket.cpp +demo_basket_OBJECTS = demo_basket.$(OBJEXT) +demo_basket_LDADD = $(LDADD) +demo_basket_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_boxstack_SOURCES = demo_boxstack.cpp +demo_boxstack_OBJECTS = demo_boxstack.$(OBJEXT) +demo_boxstack_LDADD = $(LDADD) +demo_boxstack_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_buggy_SOURCES = demo_buggy.cpp +demo_buggy_OBJECTS = demo_buggy.$(OBJEXT) +demo_buggy_LDADD = $(LDADD) +demo_buggy_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_cards_SOURCES = demo_cards.cpp +demo_cards_OBJECTS = demo_cards.$(OBJEXT) +demo_cards_LDADD = $(LDADD) +demo_cards_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +am_demo_chain1_OBJECTS = demo_chain1.$(OBJEXT) +demo_chain1_OBJECTS = $(am_demo_chain1_OBJECTS) +am__DEPENDENCIES_1 = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_chain1_DEPENDENCIES = $(am__DEPENDENCIES_1) +demo_chain2_SOURCES = demo_chain2.cpp +demo_chain2_OBJECTS = demo_chain2.$(OBJEXT) +demo_chain2_LDADD = $(LDADD) +demo_chain2_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_collision_SOURCES = demo_collision.cpp +demo_collision_OBJECTS = demo_collision.$(OBJEXT) +demo_collision_LDADD = $(LDADD) +demo_collision_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_convex_SOURCES = demo_convex.cpp +demo_convex_OBJECTS = demo_convex.$(OBJEXT) +demo_convex_LDADD = $(LDADD) +demo_convex_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_crash_SOURCES = demo_crash.cpp +demo_crash_OBJECTS = demo_crash.$(OBJEXT) +demo_crash_LDADD = $(LDADD) +demo_crash_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_cyl_SOURCES = demo_cyl.cpp +demo_cyl_OBJECTS = demo_cyl.$(OBJEXT) +demo_cyl_LDADD = $(LDADD) +demo_cyl_DEPENDENCIES = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_cylvssphere_SOURCES = demo_cylvssphere.cpp +demo_cylvssphere_OBJECTS = demo_cylvssphere.$(OBJEXT) +demo_cylvssphere_LDADD = $(LDADD) +demo_cylvssphere_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_dball_SOURCES = demo_dball.cpp +demo_dball_OBJECTS = demo_dball.$(OBJEXT) +demo_dball_LDADD = $(LDADD) +demo_dball_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_dhinge_SOURCES = demo_dhinge.cpp +demo_dhinge_OBJECTS = demo_dhinge.$(OBJEXT) +demo_dhinge_LDADD = $(LDADD) +demo_dhinge_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_feedback_SOURCES = demo_feedback.cpp +demo_feedback_OBJECTS = demo_feedback.$(OBJEXT) +demo_feedback_LDADD = $(LDADD) +demo_feedback_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_friction_SOURCES = demo_friction.cpp +demo_friction_OBJECTS = demo_friction.$(OBJEXT) +demo_friction_LDADD = $(LDADD) +demo_friction_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_gyro2_SOURCES = demo_gyro2.cpp +demo_gyro2_OBJECTS = demo_gyro2.$(OBJEXT) +demo_gyro2_LDADD = $(LDADD) +demo_gyro2_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_gyroscopic_SOURCES = demo_gyroscopic.cpp +demo_gyroscopic_OBJECTS = demo_gyroscopic.$(OBJEXT) +demo_gyroscopic_LDADD = $(LDADD) +demo_gyroscopic_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_heightfield_SOURCES = demo_heightfield.cpp +demo_heightfield_OBJECTS = demo_heightfield.$(OBJEXT) +demo_heightfield_LDADD = $(LDADD) +demo_heightfield_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_hinge_SOURCES = demo_hinge.cpp +demo_hinge_OBJECTS = demo_hinge.$(OBJEXT) +demo_hinge_LDADD = $(LDADD) +demo_hinge_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_jointPR_SOURCES = demo_jointPR.cpp +demo_jointPR_OBJECTS = demo_jointPR.$(OBJEXT) +demo_jointPR_LDADD = $(LDADD) +demo_jointPR_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_jointPU_SOURCES = demo_jointPU.cpp +demo_jointPU_OBJECTS = demo_jointPU.$(OBJEXT) +demo_jointPU_LDADD = $(LDADD) +demo_jointPU_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_joints_SOURCES = demo_joints.cpp +demo_joints_OBJECTS = demo_joints.$(OBJEXT) +demo_joints_LDADD = $(LDADD) +demo_joints_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_kinematic_SOURCES = demo_kinematic.cpp +demo_kinematic_OBJECTS = demo_kinematic.$(OBJEXT) +demo_kinematic_LDADD = $(LDADD) +demo_kinematic_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_motion_SOURCES = demo_motion.cpp +demo_motion_OBJECTS = demo_motion.$(OBJEXT) +demo_motion_LDADD = $(LDADD) +demo_motion_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_motor_SOURCES = demo_motor.cpp +demo_motor_OBJECTS = demo_motor.$(OBJEXT) +demo_motor_LDADD = $(LDADD) +demo_motor_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_moving_convex_SOURCES = demo_moving_convex.cpp +demo_moving_convex_OBJECTS = demo_moving_convex.$(OBJEXT) +demo_moving_convex_LDADD = $(LDADD) +demo_moving_convex_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_moving_trimesh_SOURCES = demo_moving_trimesh.cpp +demo_moving_trimesh_OBJECTS = demo_moving_trimesh.$(OBJEXT) +demo_moving_trimesh_LDADD = $(LDADD) +demo_moving_trimesh_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_ode_SOURCES = demo_ode.cpp +demo_ode_OBJECTS = demo_ode.$(OBJEXT) +demo_ode_LDADD = $(LDADD) +demo_ode_DEPENDENCIES = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_piston_SOURCES = demo_piston.cpp +demo_piston_OBJECTS = demo_piston.$(OBJEXT) +demo_piston_LDADD = $(LDADD) +demo_piston_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_plane2d_SOURCES = demo_plane2d.cpp +demo_plane2d_OBJECTS = demo_plane2d.$(OBJEXT) +demo_plane2d_LDADD = $(LDADD) +demo_plane2d_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_rfriction_SOURCES = demo_rfriction.cpp +demo_rfriction_OBJECTS = demo_rfriction.$(OBJEXT) +demo_rfriction_LDADD = $(LDADD) +demo_rfriction_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_slider_SOURCES = demo_slider.cpp +demo_slider_OBJECTS = demo_slider.$(OBJEXT) +demo_slider_LDADD = $(LDADD) +demo_slider_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_space_SOURCES = demo_space.cpp +demo_space_OBJECTS = demo_space.$(OBJEXT) +demo_space_LDADD = $(LDADD) +demo_space_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_space_stress_SOURCES = demo_space_stress.cpp +demo_space_stress_OBJECTS = demo_space_stress.$(OBJEXT) +demo_space_stress_LDADD = $(LDADD) +demo_space_stress_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_step_SOURCES = demo_step.cpp +demo_step_OBJECTS = demo_step.$(OBJEXT) +demo_step_LDADD = $(LDADD) +demo_step_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_tracks_SOURCES = demo_tracks.cpp +demo_tracks_OBJECTS = demo_tracks.$(OBJEXT) +demo_tracks_LDADD = $(LDADD) +demo_tracks_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_transmission_SOURCES = demo_transmission.cpp +demo_transmission_OBJECTS = demo_transmission.$(OBJEXT) +demo_transmission_LDADD = $(LDADD) +demo_transmission_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +demo_trimesh_SOURCES = demo_trimesh.cpp +demo_trimesh_OBJECTS = demo_trimesh.$(OBJEXT) +demo_trimesh_LDADD = $(LDADD) +demo_trimesh_DEPENDENCIES = \ + $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la $(am__append_3) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/ode/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = demo_I.cpp demo_basket.cpp demo_boxstack.cpp demo_buggy.cpp \ + demo_cards.cpp $(demo_chain1_SOURCES) demo_chain2.cpp \ + demo_collision.cpp demo_convex.cpp demo_crash.cpp demo_cyl.cpp \ + demo_cylvssphere.cpp demo_dball.cpp demo_dhinge.cpp \ + demo_feedback.cpp demo_friction.cpp demo_gyro2.cpp \ + demo_gyroscopic.cpp demo_heightfield.cpp demo_hinge.cpp \ + demo_jointPR.cpp demo_jointPU.cpp demo_joints.cpp \ + demo_kinematic.cpp demo_motion.cpp demo_motor.cpp \ + demo_moving_convex.cpp demo_moving_trimesh.cpp demo_ode.cpp \ + demo_piston.cpp demo_plane2d.cpp demo_rfriction.cpp \ + demo_slider.cpp demo_space.cpp demo_space_stress.cpp \ + demo_step.cpp demo_tracks.cpp demo_transmission.cpp \ + demo_trimesh.cpp +DIST_SOURCES = demo_I.cpp demo_basket.cpp demo_boxstack.cpp \ + demo_buggy.cpp demo_cards.cpp $(demo_chain1_SOURCES) \ + demo_chain2.cpp demo_collision.cpp demo_convex.cpp \ + demo_crash.cpp demo_cyl.cpp demo_cylvssphere.cpp \ + demo_dball.cpp demo_dhinge.cpp demo_feedback.cpp \ + demo_friction.cpp demo_gyro2.cpp demo_gyroscopic.cpp \ + demo_heightfield.cpp demo_hinge.cpp demo_jointPR.cpp \ + demo_jointPU.cpp demo_joints.cpp demo_kinematic.cpp \ + demo_motion.cpp demo_motor.cpp demo_moving_convex.cpp \ + demo_moving_trimesh.cpp demo_ode.cpp demo_piston.cpp \ + demo_plane2d.cpp demo_rfriction.cpp demo_slider.cpp \ + demo_space.cpp demo_space_stress.cpp demo_step.cpp \ + demo_tracks.cpp demo_transmission.cpp demo_trimesh.cpp +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CCD_CFLAGS = @CCD_CFLAGS@ +CCD_LIBS = @CCD_LIBS@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOXYGEN = @DOXYGEN@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXTRA_LIBTOOL_LDFLAGS = @EXTRA_LIBTOOL_LDFLAGS@ +FGREP = @FGREP@ +GL_LIBS = @GL_LIBS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSTDCXX = @LIBSTDCXX@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ODE_PRECISION = @ODE_PRECISION@ +ODE_VERSION = @ODE_VERSION@ +ODE_VERSION_INFO = @ODE_VERSION_INFO@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WINDRES = @WINDRES@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_WINDRES = @ac_ct_WINDRES@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include \ + -DDRAWSTUFF_TEXTURE_PATH="\"$(abs_top_srcdir)/drawstuff/textures\"" \ + $(am__append_2) +@X11_TRUE@AM_LDFLAGS = $(X_PRE_LIBS) $(X_LIBS) $(X_EXTRA_LIBS) + +# On Windows, GL_LIBS must go after libdrawstuff.la. +LDADD = $(top_builddir)/drawstuff/src/libdrawstuff.la \ + $(top_builddir)/ode/src/libode.la @GL_LIBS@ $(am__append_3) +noinst_HEADERS = basket_geom.h bunny_geom.h convex_bunny_geom.h convex_prism.h \ + icosahedron_geom.h halton235_geom.h texturepath.h world_geom3.h + +AM_DEFAULT_SOURCE_EXT = .cpp +demo_chain1_SOURCES = demo_chain1.c +demo_chain1_LDADD = $(LDADD) -lstdc++ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign ode/demo/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign ode/demo/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +demo_I$(EXEEXT): $(demo_I_OBJECTS) $(demo_I_DEPENDENCIES) $(EXTRA_demo_I_DEPENDENCIES) + @rm -f demo_I$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_I_OBJECTS) $(demo_I_LDADD) $(LIBS) + +demo_basket$(EXEEXT): $(demo_basket_OBJECTS) $(demo_basket_DEPENDENCIES) $(EXTRA_demo_basket_DEPENDENCIES) + @rm -f demo_basket$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_basket_OBJECTS) $(demo_basket_LDADD) $(LIBS) + +demo_boxstack$(EXEEXT): $(demo_boxstack_OBJECTS) $(demo_boxstack_DEPENDENCIES) $(EXTRA_demo_boxstack_DEPENDENCIES) + @rm -f demo_boxstack$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_boxstack_OBJECTS) $(demo_boxstack_LDADD) $(LIBS) + +demo_buggy$(EXEEXT): $(demo_buggy_OBJECTS) $(demo_buggy_DEPENDENCIES) $(EXTRA_demo_buggy_DEPENDENCIES) + @rm -f demo_buggy$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_buggy_OBJECTS) $(demo_buggy_LDADD) $(LIBS) + +demo_cards$(EXEEXT): $(demo_cards_OBJECTS) $(demo_cards_DEPENDENCIES) $(EXTRA_demo_cards_DEPENDENCIES) + @rm -f demo_cards$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_cards_OBJECTS) $(demo_cards_LDADD) $(LIBS) + +demo_chain1$(EXEEXT): $(demo_chain1_OBJECTS) $(demo_chain1_DEPENDENCIES) $(EXTRA_demo_chain1_DEPENDENCIES) + @rm -f demo_chain1$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(demo_chain1_OBJECTS) $(demo_chain1_LDADD) $(LIBS) + +demo_chain2$(EXEEXT): $(demo_chain2_OBJECTS) $(demo_chain2_DEPENDENCIES) $(EXTRA_demo_chain2_DEPENDENCIES) + @rm -f demo_chain2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_chain2_OBJECTS) $(demo_chain2_LDADD) $(LIBS) + +demo_collision$(EXEEXT): $(demo_collision_OBJECTS) $(demo_collision_DEPENDENCIES) $(EXTRA_demo_collision_DEPENDENCIES) + @rm -f demo_collision$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_collision_OBJECTS) $(demo_collision_LDADD) $(LIBS) + +demo_convex$(EXEEXT): $(demo_convex_OBJECTS) $(demo_convex_DEPENDENCIES) $(EXTRA_demo_convex_DEPENDENCIES) + @rm -f demo_convex$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_convex_OBJECTS) $(demo_convex_LDADD) $(LIBS) + +demo_crash$(EXEEXT): $(demo_crash_OBJECTS) $(demo_crash_DEPENDENCIES) $(EXTRA_demo_crash_DEPENDENCIES) + @rm -f demo_crash$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_crash_OBJECTS) $(demo_crash_LDADD) $(LIBS) + +demo_cyl$(EXEEXT): $(demo_cyl_OBJECTS) $(demo_cyl_DEPENDENCIES) $(EXTRA_demo_cyl_DEPENDENCIES) + @rm -f demo_cyl$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_cyl_OBJECTS) $(demo_cyl_LDADD) $(LIBS) + +demo_cylvssphere$(EXEEXT): $(demo_cylvssphere_OBJECTS) $(demo_cylvssphere_DEPENDENCIES) $(EXTRA_demo_cylvssphere_DEPENDENCIES) + @rm -f demo_cylvssphere$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_cylvssphere_OBJECTS) $(demo_cylvssphere_LDADD) $(LIBS) + +demo_dball$(EXEEXT): $(demo_dball_OBJECTS) $(demo_dball_DEPENDENCIES) $(EXTRA_demo_dball_DEPENDENCIES) + @rm -f demo_dball$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_dball_OBJECTS) $(demo_dball_LDADD) $(LIBS) + +demo_dhinge$(EXEEXT): $(demo_dhinge_OBJECTS) $(demo_dhinge_DEPENDENCIES) $(EXTRA_demo_dhinge_DEPENDENCIES) + @rm -f demo_dhinge$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_dhinge_OBJECTS) $(demo_dhinge_LDADD) $(LIBS) + +demo_feedback$(EXEEXT): $(demo_feedback_OBJECTS) $(demo_feedback_DEPENDENCIES) $(EXTRA_demo_feedback_DEPENDENCIES) + @rm -f demo_feedback$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_feedback_OBJECTS) $(demo_feedback_LDADD) $(LIBS) + +demo_friction$(EXEEXT): $(demo_friction_OBJECTS) $(demo_friction_DEPENDENCIES) $(EXTRA_demo_friction_DEPENDENCIES) + @rm -f demo_friction$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_friction_OBJECTS) $(demo_friction_LDADD) $(LIBS) + +demo_gyro2$(EXEEXT): $(demo_gyro2_OBJECTS) $(demo_gyro2_DEPENDENCIES) $(EXTRA_demo_gyro2_DEPENDENCIES) + @rm -f demo_gyro2$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_gyro2_OBJECTS) $(demo_gyro2_LDADD) $(LIBS) + +demo_gyroscopic$(EXEEXT): $(demo_gyroscopic_OBJECTS) $(demo_gyroscopic_DEPENDENCIES) $(EXTRA_demo_gyroscopic_DEPENDENCIES) + @rm -f demo_gyroscopic$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_gyroscopic_OBJECTS) $(demo_gyroscopic_LDADD) $(LIBS) + +demo_heightfield$(EXEEXT): $(demo_heightfield_OBJECTS) $(demo_heightfield_DEPENDENCIES) $(EXTRA_demo_heightfield_DEPENDENCIES) + @rm -f demo_heightfield$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_heightfield_OBJECTS) $(demo_heightfield_LDADD) $(LIBS) + +demo_hinge$(EXEEXT): $(demo_hinge_OBJECTS) $(demo_hinge_DEPENDENCIES) $(EXTRA_demo_hinge_DEPENDENCIES) + @rm -f demo_hinge$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_hinge_OBJECTS) $(demo_hinge_LDADD) $(LIBS) + +demo_jointPR$(EXEEXT): $(demo_jointPR_OBJECTS) $(demo_jointPR_DEPENDENCIES) $(EXTRA_demo_jointPR_DEPENDENCIES) + @rm -f demo_jointPR$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_jointPR_OBJECTS) $(demo_jointPR_LDADD) $(LIBS) + +demo_jointPU$(EXEEXT): $(demo_jointPU_OBJECTS) $(demo_jointPU_DEPENDENCIES) $(EXTRA_demo_jointPU_DEPENDENCIES) + @rm -f demo_jointPU$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_jointPU_OBJECTS) $(demo_jointPU_LDADD) $(LIBS) + +demo_joints$(EXEEXT): $(demo_joints_OBJECTS) $(demo_joints_DEPENDENCIES) $(EXTRA_demo_joints_DEPENDENCIES) + @rm -f demo_joints$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_joints_OBJECTS) $(demo_joints_LDADD) $(LIBS) + +demo_kinematic$(EXEEXT): $(demo_kinematic_OBJECTS) $(demo_kinematic_DEPENDENCIES) $(EXTRA_demo_kinematic_DEPENDENCIES) + @rm -f demo_kinematic$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_kinematic_OBJECTS) $(demo_kinematic_LDADD) $(LIBS) + +demo_motion$(EXEEXT): $(demo_motion_OBJECTS) $(demo_motion_DEPENDENCIES) $(EXTRA_demo_motion_DEPENDENCIES) + @rm -f demo_motion$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_motion_OBJECTS) $(demo_motion_LDADD) $(LIBS) + +demo_motor$(EXEEXT): $(demo_motor_OBJECTS) $(demo_motor_DEPENDENCIES) $(EXTRA_demo_motor_DEPENDENCIES) + @rm -f demo_motor$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_motor_OBJECTS) $(demo_motor_LDADD) $(LIBS) + +demo_moving_convex$(EXEEXT): $(demo_moving_convex_OBJECTS) $(demo_moving_convex_DEPENDENCIES) $(EXTRA_demo_moving_convex_DEPENDENCIES) + @rm -f demo_moving_convex$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_moving_convex_OBJECTS) $(demo_moving_convex_LDADD) $(LIBS) + +demo_moving_trimesh$(EXEEXT): $(demo_moving_trimesh_OBJECTS) $(demo_moving_trimesh_DEPENDENCIES) $(EXTRA_demo_moving_trimesh_DEPENDENCIES) + @rm -f demo_moving_trimesh$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_moving_trimesh_OBJECTS) $(demo_moving_trimesh_LDADD) $(LIBS) + +demo_ode$(EXEEXT): $(demo_ode_OBJECTS) $(demo_ode_DEPENDENCIES) $(EXTRA_demo_ode_DEPENDENCIES) + @rm -f demo_ode$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_ode_OBJECTS) $(demo_ode_LDADD) $(LIBS) + +demo_piston$(EXEEXT): $(demo_piston_OBJECTS) $(demo_piston_DEPENDENCIES) $(EXTRA_demo_piston_DEPENDENCIES) + @rm -f demo_piston$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_piston_OBJECTS) $(demo_piston_LDADD) $(LIBS) + +demo_plane2d$(EXEEXT): $(demo_plane2d_OBJECTS) $(demo_plane2d_DEPENDENCIES) $(EXTRA_demo_plane2d_DEPENDENCIES) + @rm -f demo_plane2d$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_plane2d_OBJECTS) $(demo_plane2d_LDADD) $(LIBS) + +demo_rfriction$(EXEEXT): $(demo_rfriction_OBJECTS) $(demo_rfriction_DEPENDENCIES) $(EXTRA_demo_rfriction_DEPENDENCIES) + @rm -f demo_rfriction$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_rfriction_OBJECTS) $(demo_rfriction_LDADD) $(LIBS) + +demo_slider$(EXEEXT): $(demo_slider_OBJECTS) $(demo_slider_DEPENDENCIES) $(EXTRA_demo_slider_DEPENDENCIES) + @rm -f demo_slider$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_slider_OBJECTS) $(demo_slider_LDADD) $(LIBS) + +demo_space$(EXEEXT): $(demo_space_OBJECTS) $(demo_space_DEPENDENCIES) $(EXTRA_demo_space_DEPENDENCIES) + @rm -f demo_space$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_space_OBJECTS) $(demo_space_LDADD) $(LIBS) + +demo_space_stress$(EXEEXT): $(demo_space_stress_OBJECTS) $(demo_space_stress_DEPENDENCIES) $(EXTRA_demo_space_stress_DEPENDENCIES) + @rm -f demo_space_stress$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_space_stress_OBJECTS) $(demo_space_stress_LDADD) $(LIBS) + +demo_step$(EXEEXT): $(demo_step_OBJECTS) $(demo_step_DEPENDENCIES) $(EXTRA_demo_step_DEPENDENCIES) + @rm -f demo_step$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_step_OBJECTS) $(demo_step_LDADD) $(LIBS) + +demo_tracks$(EXEEXT): $(demo_tracks_OBJECTS) $(demo_tracks_DEPENDENCIES) $(EXTRA_demo_tracks_DEPENDENCIES) + @rm -f demo_tracks$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_tracks_OBJECTS) $(demo_tracks_LDADD) $(LIBS) + +demo_transmission$(EXEEXT): $(demo_transmission_OBJECTS) $(demo_transmission_DEPENDENCIES) $(EXTRA_demo_transmission_DEPENDENCIES) + @rm -f demo_transmission$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_transmission_OBJECTS) $(demo_transmission_LDADD) $(LIBS) + +demo_trimesh$(EXEEXT): $(demo_trimesh_OBJECTS) $(demo_trimesh_DEPENDENCIES) $(EXTRA_demo_trimesh_DEPENDENCIES) + @rm -f demo_trimesh$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(demo_trimesh_OBJECTS) $(demo_trimesh_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_I.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_basket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_boxstack.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_buggy.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_cards.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_chain1.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_chain2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_collision.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_convex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_crash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_cyl.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_cylvssphere.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_dball.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_dhinge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_feedback.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_friction.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_gyro2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_gyroscopic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_heightfield.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_hinge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_jointPR.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_jointPU.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_joints.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_kinematic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_motion.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_motor.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_moving_convex.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_moving_trimesh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_ode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_piston.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_plane2d.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_rfriction.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_slider.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_space.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_space_stress.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_step.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_tracks.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_transmission.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/demo_trimesh.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(HEADERS) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +@WIN32_TRUE@resources.o: $(top_srcdir)/drawstuff/src/resources.rc $(top_srcdir)/drawstuff/src/resource.h +@WIN32_TRUE@ @WINDRES@ $(top_srcdir)/drawstuff/src/resources.rc -o resources.o + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/thirdparty/ode-0.16.5/ode/demo/basket_geom.h b/thirdparty/ode-0.16.5/ode/demo/basket_geom.h new file mode 100644 index 0000000..ec88327 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/basket_geom.h @@ -0,0 +1,599 @@ + +static float world_normals[] = { + 0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,-0,0,1,0,0,1,0,0,1,0,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,-0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,-0.948064f,0.318080f,0,-0.989482f,0.144655f,0,-0.983494f,0.180939f,0,-0.983494f,0.180939f,0,-0.908999f,0.416798f,0,-0.948064f,0.318080f,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-1,0,-0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-0.132460f,0.991188f,0,0.264920f,0.964270f,0,0.132460f,0.991188f,0,0.132460f,0.991188f,0,-0.264920f,0.964270f,0,-0.132460f,0.991188f,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-0.687592f,-0.726097f,-0,-0.881727f,-0.471761f,0,-0.687592f,-0.726097f,-0,-0.881727f,-0.471761f,0,-0.881727f,-0.471761f,0,-0.687592f,-0.726097f,-0,0.687592f,-0.726097f,0,0.928375f,-0.371644f,0,0.824321f,-0.566123f,0,0.687592f,-0.726097f,0,0.824321f,-0.566123f,0,0.687592f,-0.726097f,0,-0.881727f,-0.471761f,0,-0.985594f,-0.169128f,0,-0.985594f,-0.169128f,0,-0.985594f,-0.169128f,0,-0.881727f,-0.471761f,0,-0.881727f,-0.471761f,0,0.928375f,-0.371644f,0,0.985594f,-0.169128f,0,0.985594f,-0.169128f,0,0.928375f,-0.371644f,0,0.985594f,-0.169128f,0,0.824321f,-0.566123f,0,-0.870167f,0.492758f,0,-0.870167f,0.492758f,0,-0.870167f,0.492758f,0,-0.870167f,0.492758f,0,-0.870167f,0.492758f,0,-0.870167f,0.492758f,0,0.870167f,0.492758f,0,0.870167f,0.492758f,0,0.870167f,0.492758f,0,0.870167f,0.492758f,0,0.870167f,0.492758f,0,0.870167f,0.492758f,-0,-0.390313f,0.920682f,0,-0.132460f,0.991188f,0,-0.264920f,0.964270f,0,-0.264920f,0.964270f,0,-0.390313f,0.920682f,0,-0.390313f,0.920682f,0,0.390313f,0.920682f,0,0.132460f,0.991188f,0,0.264920f,0.964270f,0,0.390313f,0.920682f,0,0.264920f,0.964270f,0,0.390313f,0.920682f,-0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0.985594f,0.169128f,0,0.824321f,0.566123f,0,0.928375f,0.371644f,0,0.928375f,0.371644f,0,0.985594f,0.169128f,0,0.985594f,0.169128f,0,0.824321f,0.566123f,0,0.687592f,0.726097f,0,0.687592f,0.726097f,0,0.687592f,0.726097f,0,0.928375f,0.371644f,0,0.824321f,0.566123f,0,0,1,0,-0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0.687592f,0.726097f,0,-0.687592f,0.726097f,0,-0.881727f,0.471761f,0,-0.881727f,0.471761f,0,-0.881727f,0.471761f,0,-0.687592f,0.726097f,0,-0.881727f,0.471761f,0,-0.985594f,0.169128f,0,-0.985594f,0.169128f,0,-0.985594f,0.169128f,0,-0.881727f,0.471761f,0,-0.881727f,0.471761f,0,-0.870166f,-0.492758f,0,-0.870166f,-0.492758f,0,-0.870166f,-0.492758f,0,-0.870166f,-0.492758f,0,-0.870166f,-0.492758f,0,-0.870166f,-0.492758f,0,-0.390314f,-0.920682f,0,-0.132460f,-0.991188f,0,-0.264921f,-0.964270f,0,-0.264921f,-0.964270f,0,-0.390314f,-0.920682f,0,-0.390314f,-0.920682f,0,-0.132460f,-0.991188f,0,0.264921f,-0.964270f,0,0.132460f,-0.991188f,0,0.132460f,-0.991188f,0,-0.264921f,-0.964270f,0,-0.132460f,-0.991188f,0,0.264921f,-0.964270f,0,0.390314f,-0.920682f,0,0.390314f,-0.920682f,0,0.390314f,-0.920682f,0,0.132460f,-0.991188f,0,0.264921f,-0.964270f,0,0.870166f,-0.492758f,0,0.870166f,-0.492758f,0,0.870166f,-0.492758f,0,0.870166f,-0.492758f,0,0.870166f,-0.492758f,0,0.870166f,-0.492758f,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0.527606f,0.849489f,0,-0.793893f,0.608057f,0,-0.715135f,0.698986f,0,-0.715135f,0.698986f,0,-0.418249f,0.908332f,0,-0.527606f,0.849489f,0,-0.075284f,0.997162f,0,-0.253577f,0.967315f,0,-0.202069f,0.979371f,0,-0.202069f,0.979371f,0,-0.075284f,0.997162f,0,-0.075284f,0.997162f,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.160137f,0.987095f,0,0.049305f,0.998784f,0,0.049305f,0.998784f,0,0.049305f,0.998784f,0,0.221401f,0.975183f,0,0.160137f,0.987095f,0,0.696124f,0.717921f,0,0.696124f,0.717921f,0,0.433340f,0.901230f,0,0.433340f,0.901230f,0,0.433340f,0.901230f,0,0.696124f,0.717921f,0,0.696124f,0.717921f,0,0.696124f,0.717921f,0,0.838308f,0.545197f,0,0.696124f,0.717921f,0,0.872167f,0.489208f,0,0.838308f,0.545197f,0,-0.994126f,0.108225f,0,-0.983494f,0.180939f,0,-0.989482f,0.144655f,0,-0.994126f,0.108225f,0,-0.989482f,0.144655f,0,-0.994126f,0.108225f,0,-0.948064f,0.318080f,0,-0.908999f,0.416798f,0,-0.793893f,0.608057f,0,-0.908999f,0.416798f,0,-0.715135f,0.698986f,0,-0.793893f,0.608057f,0,-0.527606f,0.849489f,0,-0.418249f,0.908332f,0,-0.253577f,0.967315f,0,-0.418249f,0.908332f,0,-0.202069f,0.979371f,0,-0.253577f,0.967315f,0,-0.075284f,0.997162f,0,-0.075284f,0.997162f,0,0,1,0,-0.075284f,0.997162f,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0.049305f,0.998784f,0,0,1,0,0.049305f,0.998784f,0,0.049305f,0.998784f,0,0.160137f,0.987095f,0,0.221401f,0.975183f,0,0.433340f,0.901230f,0,0.221401f,0.975183f,0,0.433340f,0.901230f,0,0.433340f,0.901230f,0,0.902172f,0.431376f,0,0.838308f,0.545197f,0,0.872167f,0.489208f,0,0.872167f,0.489208f,0,0.902172f,0.431376f,0,0.902172f,0.431376f, +}; + +static float world_vertices[] = { + -4,-4,-0.100000f, + 4,-4,-0.100000f, + 4,-4,0.100000f, + -4,-4,0.100000f, + 4,0,0.100000f, + 4,4,-0.100000f, + 4,4,0.100000f, + -4,4,-0.100000f, + 0.066000f,-2.060000f,2, + 0.066000f,-1.940000f,2, + -0.066000f,-2.060000f,2, + -0.066000f,-1.940000f,2, + -4,4,0.100000f, + -4,0,0.100000f, + 0.360000f,3.244444f,1.466974f, + 0.360000f,3.422222f,2.266974f, + -0.360000f,3.422222f,2.266974f, + -0.360000f,3.244444f,1.466974f, + 0.066000f,-2.060000f,0.100000f, + -0.066000f,-2.060000f,0.100000f, + 0.066000f,-1.940000f,0.100000f, + -0.066000f,-1.940000f,0.100000f, + 0.066000f,-1.940000f,1.950000f, + -0.052853f,-1.506390f,2, + 0.052853f,-1.506390f,2, + 0.052853f,-1.506390f,1.950000f, + -0.052853f,-1.506390f,1.950000f, + -0.066000f,-1.940000f,1.950000f, + -0.066000f,-1.840000f,1.950000f, + 0.066000f,-1.840000f,1.950000f, + -0.066000f,-1.840000f,2, + 0.066000f,-1.840000f,2, + -0.171600f,-1.740000f,2, + -0.171600f,-1.740000f,1.950000f, + 0.171600f,-1.740000f,1.950000f, + 0.171600f,-1.740000f,2, + -0.188760f,-1.640000f,2, + -0.188760f,-1.640000f,1.950000f, + 0.188760f,-1.640000f,1.950000f, + 0.188760f,-1.640000f,2, + -0.132132f,-1.540000f,2, + -0.132132f,-1.540000f,1.950000f, + 0.132132f,-1.540000f,1.950000f, + 0.132132f,-1.540000f,2, + 0.173397f,-1.642679f,1.950000f, + 0.121808f,-1.551577f,1.950000f, + 0.157950f,-1.732697f,1.950000f, + 0.060149f,-1.825311f,1.950000f, + -0.060149f,-1.825311f,1.950000f, + -0.157950f,-1.732697f,1.950000f, + -0.173397f,-1.642679f,1.950000f, + -0.121808f,-1.551577f,1.950000f, + -0.049868f,-1.521079f,1.950000f, + 0.049868f,-1.521079f,1.950000f, + -0.173397f,-1.642679f,2, + -0.121808f,-1.551577f,2, + -0.157950f,-1.732697f,2, + -0.060149f,-1.825311f,2, + 0.060149f,-1.825311f,2, + 0.157950f,-1.732697f,2, + 0.173397f,-1.642679f,2, + 0.121808f,-1.551577f,2, + 0.049868f,-1.521079f,2, + -0.049868f,-1.521079f,2, + -0.360000f,3.600000f,0.100000f, + 0.360000f,3.600000f,0.100000f, + -0.360000f,0.400000f,0.100000f, + 0.360000f,0.400000f,0.100000f, + 0.360000f,2.888889f,1.023752f, + 0.360000f,3.066667f,1.166974f, + -0.360000f,3.066667f,1.166974f, + -0.360000f,2.888889f,1.023752f, + 0.360000f,2.533333f,0.939976f, + 0.360000f,2.711111f,0.966974f, + -0.360000f,2.711111f,0.966974f, + -0.360000f,2.533333f,0.939976f, + -0.360000f,2.177778f,0.939976f, + 0.360000f,2.177778f,0.939976f, + 0.360000f,2.355556f,0.939976f, + -0.360000f,2.355556f,0.939976f, + -0.360000f,1.822222f,0.939976f, + 0.360000f,1.822222f,0.939976f, + 0.360000f,2,0.939976f, + -0.360000f,2,0.939976f, + -0.360000f,1.466667f,0.939976f, + 0.360000f,1.466667f,0.939976f, + 0.360000f,1.644444f,0.939976f, + -0.360000f,1.644444f,0.939976f, + 0.360000f,1.111111f,0.957571f, + 0.360000f,1.288889f,0.939976f, + -0.360000f,1.288889f,0.939976f, + -0.360000f,1.111111f,0.957571f, + -0.360000f,0.755556f,1.134246f, + 0.360000f,0.755556f,1.134246f, + 0.360000f,0.933333f,1.009739f, + -0.360000f,0.933333f,1.009739f, + 0.360000f,0.577778f,1.372130f, + -0.360000f,0.577778f,1.372130f, + -0.360000f,3.600000f,3.900000f, + 0.360000f,3.600000f,3.900000f, + 0.360000f,0.400000f,1.743932f, + -0.360000f,0.400000f,1.743932f, +}; + +static dTriIndex world_indices[] = { + 0, + 1, + 2, + 0, + 2, + 3, + 4, + 1, + 5, + 4, + 5, + 6, + 4, + 2, + 1, + 0, + 7, + 5, + 0, + 5, + 1, + 8, + 9, + 10, + 9, + 11, + 10, + 12, + 6, + 5, + 5, + 7, + 12, + 3, + 13, + 0, + 13, + 12, + 7, + 13, + 7, + 0, + 14, + 15, + 16, + 16, + 17, + 14, + 2, + 18, + 19, + 19, + 3, + 2, + 4, + 20, + 2, + 20, + 18, + 2, + 21, + 20, + 4, + 4, + 13, + 21, + 19, + 21, + 13, + 13, + 3, + 19, + 8, + 10, + 19, + 19, + 18, + 8, + 22, + 9, + 8, + 8, + 18, + 22, + 18, + 20, + 22, + 23, + 24, + 25, + 25, + 26, + 23, + 19, + 10, + 27, + 19, + 27, + 21, + 10, + 11, + 27, + 21, + 27, + 22, + 21, + 22, + 20, + 27, + 28, + 22, + 28, + 29, + 22, + 11, + 30, + 28, + 28, + 27, + 11, + 9, + 31, + 11, + 31, + 30, + 11, + 22, + 29, + 31, + 22, + 31, + 9, + 30, + 32, + 28, + 32, + 33, + 28, + 29, + 34, + 35, + 29, + 35, + 31, + 32, + 36, + 37, + 37, + 33, + 32, + 34, + 38, + 39, + 34, + 39, + 35, + 36, + 40, + 41, + 41, + 37, + 36, + 38, + 42, + 43, + 38, + 43, + 39, + 40, + 23, + 26, + 26, + 41, + 40, + 42, + 25, + 24, + 42, + 24, + 43, + 38, + 44, + 45, + 45, + 42, + 38, + 34, + 46, + 44, + 34, + 44, + 38, + 34, + 29, + 47, + 34, + 47, + 46, + 28, + 48, + 29, + 48, + 47, + 29, + 33, + 49, + 48, + 33, + 48, + 28, + 50, + 49, + 33, + 33, + 37, + 50, + 51, + 50, + 37, + 37, + 41, + 51, + 26, + 52, + 51, + 26, + 51, + 41, + 53, + 52, + 26, + 26, + 25, + 53, + 25, + 42, + 45, + 25, + 45, + 53, + 36, + 54, + 55, + 55, + 40, + 36, + 32, + 56, + 54, + 54, + 36, + 32, + 30, + 57, + 32, + 57, + 56, + 32, + 31, + 58, + 30, + 58, + 57, + 30, + 35, + 59, + 58, + 35, + 58, + 31, + 60, + 59, + 35, + 35, + 39, + 60, + 61, + 60, + 39, + 39, + 43, + 61, + 24, + 62, + 61, + 24, + 61, + 43, + 63, + 62, + 24, + 24, + 23, + 63, + 55, + 63, + 23, + 23, + 40, + 55, + 54, + 56, + 49, + 49, + 50, + 54, + 56, + 57, + 48, + 48, + 49, + 56, + 57, + 58, + 47, + 47, + 48, + 57, + 47, + 58, + 59, + 59, + 46, + 47, + 59, + 60, + 44, + 44, + 46, + 59, + 60, + 61, + 45, + 45, + 44, + 60, + 61, + 62, + 53, + 53, + 45, + 61, + 62, + 63, + 52, + 52, + 53, + 62, + 63, + 55, + 51, + 51, + 52, + 63, + 55, + 54, + 50, + 50, + 51, + 55, + 64, + 65, + 6, + 6, + 12, + 64, + 66, + 64, + 12, + 12, + 13, + 66, + 4, + 67, + 66, + 66, + 13, + 4, + 6, + 65, + 4, + 65, + 67, + 4, + 68, + 69, + 70, + 70, + 71, + 68, + 72, + 73, + 74, + 74, + 75, + 72, + 76, + 77, + 78, + 78, + 79, + 76, + 80, + 81, + 82, + 82, + 83, + 80, + 84, + 85, + 86, + 86, + 87, + 84, + 88, + 89, + 90, + 90, + 91, + 88, + 92, + 93, + 94, + 94, + 95, + 92, + 93, + 92, + 96, + 92, + 97, + 96, + 98, + 16, + 15, + 98, + 15, + 99, + 14, + 17, + 69, + 17, + 70, + 69, + 68, + 71, + 73, + 71, + 74, + 73, + 72, + 75, + 79, + 72, + 79, + 78, + 77, + 76, + 83, + 77, + 83, + 82, + 81, + 80, + 87, + 81, + 87, + 86, + 85, + 84, + 90, + 85, + 90, + 89, + 88, + 91, + 94, + 91, + 95, + 94, + 100, + 96, + 97, + 97, + 101, + 100, +}; + diff --git a/thirdparty/ode-0.16.5/ode/demo/bunny_geom.h b/thirdparty/ode-0.16.5/ode/demo/bunny_geom.h new file mode 100644 index 0000000..78f8eb0 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/bunny_geom.h @@ -0,0 +1,1366 @@ +// Bunny mesh ripped from Opcode +const int VertexCount = 453; +const int IndexCount = 902 * 3; + + +float Vertices[VertexCount * 3] = { + -0.334392f, 0.133007f, 0.062259f, + -0.350189f, 0.150354f, -0.147769f, + -0.234201f, 0.343811f, -0.174307f, + -0.200259f, 0.285207f, 0.093749f, + 0.003520f, 0.475208f, -0.159365f, + 0.001856f, 0.419203f, 0.098582f, + -0.252802f, 0.093666f, 0.237538f, + -0.162901f, 0.237984f, 0.206905f, + 0.000865f, 0.318141f, 0.235370f, + -0.414624f, 0.164083f, -0.278254f, + -0.262213f, 0.357334f, -0.293246f, + 0.004628f, 0.482694f, -0.338626f, + -0.402162f, 0.133528f, -0.443247f, + -0.243781f, 0.324275f, -0.436763f, + 0.005293f, 0.437592f, -0.458332f, + -0.339884f, -0.041150f, -0.668211f, + -0.248382f, 0.255825f, -0.627493f, + 0.006261f, 0.376103f, -0.631506f, + -0.216201f, -0.126776f, -0.886936f, + -0.171075f, 0.011544f, -0.881386f, + -0.181074f, 0.098223f, -0.814779f, + -0.119891f, 0.218786f, -0.760153f, + -0.078895f, 0.276780f, -0.739281f, + 0.006801f, 0.310959f, -0.735661f, + -0.168842f, 0.102387f, -0.920381f, + -0.104072f, 0.177278f, -0.952530f, + -0.129704f, 0.211848f, -0.836678f, + -0.099875f, 0.310931f, -0.799381f, + 0.007237f, 0.361687f, -0.794439f, + -0.077913f, 0.258753f, -0.921640f, + 0.007957f, 0.282241f, -0.931680f, + -0.252222f, -0.550401f, -0.557810f, + -0.267633f, -0.603419f, -0.655209f, + -0.446838f, -0.118517f, -0.466159f, + -0.459488f, -0.093017f, -0.311341f, + -0.370645f, -0.100108f, -0.159454f, + -0.371984f, -0.091991f, -0.011044f, + -0.328945f, -0.098269f, 0.088659f, + -0.282452f, -0.018862f, 0.311501f, + -0.352403f, -0.131341f, 0.144902f, + -0.364126f, -0.200299f, 0.202388f, + -0.283965f, -0.231869f, 0.023668f, + -0.298943f, -0.155218f, 0.369716f, + -0.293787f, -0.121856f, 0.419097f, + -0.290163f, -0.290797f, 0.107824f, + -0.264165f, -0.272849f, 0.036347f, + -0.228567f, -0.372573f, 0.290309f, + -0.190431f, -0.286997f, 0.421917f, + -0.191039f, -0.240973f, 0.507118f, + -0.287272f, -0.276431f, -0.065444f, + -0.295675f, -0.280818f, -0.174200f, + -0.399537f, -0.313131f, -0.376167f, + -0.392666f, -0.488581f, -0.427494f, + -0.331669f, -0.570185f, -0.466054f, + -0.282290f, -0.618140f, -0.589220f, + -0.374238f, -0.594882f, -0.323298f, + -0.381071f, -0.629723f, -0.350777f, + -0.382112f, -0.624060f, -0.221577f, + -0.272701f, -0.566522f, 0.259157f, + -0.256702f, -0.663406f, 0.286079f, + -0.280948f, -0.428359f, 0.055790f, + -0.184974f, -0.508894f, 0.326265f, + -0.279971f, -0.526918f, 0.395319f, + -0.282599f, -0.663393f, 0.412411f, + -0.188329f, -0.475093f, 0.417954f, + -0.263384f, -0.663396f, 0.466604f, + -0.209063f, -0.663393f, 0.509344f, + -0.002044f, -0.319624f, 0.553078f, + -0.001266f, -0.371260f, 0.413296f, + -0.219753f, -0.339762f, -0.040921f, + -0.256986f, -0.282511f, -0.006349f, + -0.271706f, -0.260881f, 0.001764f, + -0.091191f, -0.419184f, -0.045912f, + -0.114944f, -0.429752f, -0.124739f, + -0.113970f, -0.382987f, -0.188540f, + -0.243012f, -0.464942f, -0.242850f, + -0.314815f, -0.505402f, -0.324768f, + 0.002774f, -0.437526f, -0.262766f, + -0.072625f, -0.417748f, -0.221440f, + -0.160112f, -0.476932f, -0.293450f, + 0.003859f, -0.453425f, -0.443916f, + -0.120363f, -0.581567f, -0.438689f, + -0.091499f, -0.584191f, -0.294511f, + -0.116469f, -0.599861f, -0.188308f, + -0.208032f, -0.513640f, -0.134649f, + -0.235749f, -0.610017f, -0.040939f, + -0.344916f, -0.622487f, -0.085380f, + -0.336401f, -0.531864f, -0.212298f, + 0.001961f, -0.459550f, -0.135547f, + -0.058296f, -0.430536f, -0.043440f, + 0.001378f, -0.449511f, -0.037762f, + -0.130135f, -0.510222f, 0.079144f, + 0.000142f, -0.477549f, 0.157064f, + -0.114284f, -0.453206f, 0.304397f, + -0.000592f, -0.443558f, 0.285401f, + -0.056215f, -0.663402f, 0.326073f, + -0.026248f, -0.568010f, 0.273318f, + -0.049261f, -0.531064f, 0.389854f, + -0.127096f, -0.663398f, 0.479316f, + -0.058384f, -0.663401f, 0.372891f, + -0.303961f, 0.054199f, 0.625921f, + -0.268594f, 0.193403f, 0.502766f, + -0.277159f, 0.126123f, 0.443289f, + -0.287605f, -0.005722f, 0.531844f, + -0.231396f, -0.121289f, 0.587387f, + -0.253475f, -0.081797f, 0.756541f, + -0.195164f, -0.137969f, 0.728011f, + -0.167673f, -0.156573f, 0.609388f, + -0.145917f, -0.169029f, 0.697600f, + -0.077776f, -0.214247f, 0.622586f, + -0.076873f, -0.214971f, 0.696301f, + -0.002341f, -0.233135f, 0.622859f, + -0.002730f, -0.213526f, 0.691267f, + -0.003136f, -0.192628f, 0.762731f, + -0.056136f, -0.201222f, 0.763806f, + -0.114589f, -0.166192f, 0.770723f, + -0.155145f, -0.129632f, 0.791738f, + -0.183611f, -0.058705f, 0.847012f, + -0.165562f, 0.001980f, 0.833386f, + -0.220084f, 0.019914f, 0.768935f, + -0.255730f, 0.090306f, 0.670782f, + -0.255594f, 0.113833f, 0.663389f, + -0.226380f, 0.212655f, 0.617740f, + -0.003367f, -0.195342f, 0.799680f, + -0.029743f, -0.210508f, 0.827180f, + -0.003818f, -0.194783f, 0.873636f, + -0.004116f, -0.157907f, 0.931268f, + -0.031280f, -0.184555f, 0.889476f, + -0.059885f, -0.184448f, 0.841330f, + -0.135333f, -0.164332f, 0.878200f, + -0.085574f, -0.170948f, 0.925547f, + -0.163833f, -0.094170f, 0.897114f, + -0.138444f, -0.104250f, 0.945975f, + -0.083497f, -0.084934f, 0.979607f, + -0.004433f, -0.146642f, 0.985872f, + -0.150715f, 0.032650f, 0.884111f, + -0.135892f, -0.035520f, 0.945455f, + -0.070612f, 0.036849f, 0.975733f, + -0.004458f, -0.042526f, 1.015670f, + -0.004249f, 0.046042f, 1.003240f, + -0.086969f, 0.133224f, 0.947633f, + -0.003873f, 0.161605f, 0.970499f, + -0.125544f, 0.140012f, 0.917678f, + -0.125651f, 0.250246f, 0.857602f, + -0.003127f, 0.284070f, 0.878870f, + -0.159174f, 0.125726f, 0.888878f, + -0.183807f, 0.196970f, 0.844480f, + -0.159890f, 0.291736f, 0.732480f, + -0.199495f, 0.207230f, 0.779864f, + -0.206182f, 0.164608f, 0.693257f, + -0.186315f, 0.160689f, 0.817193f, + -0.192827f, 0.166706f, 0.782271f, + -0.175112f, 0.110008f, 0.860621f, + -0.161022f, 0.057420f, 0.855111f, + -0.172319f, 0.036155f, 0.816189f, + -0.190318f, 0.064083f, 0.760605f, + -0.195072f, 0.129179f, 0.731104f, + -0.203126f, 0.410287f, 0.680536f, + -0.216677f, 0.309274f, 0.642272f, + -0.241515f, 0.311485f, 0.587832f, + -0.002209f, 0.366663f, 0.749413f, + -0.088230f, 0.396265f, 0.678635f, + -0.170147f, 0.109517f, 0.840784f, + -0.160521f, 0.067766f, 0.830650f, + -0.181546f, 0.139805f, 0.812146f, + -0.180495f, 0.148568f, 0.776087f, + -0.180255f, 0.129125f, 0.744192f, + -0.186298f, 0.078308f, 0.769352f, + -0.167622f, 0.060539f, 0.806675f, + -0.189876f, 0.102760f, 0.802582f, + -0.108340f, 0.455446f, 0.657174f, + -0.241585f, 0.527592f, 0.669296f, + -0.265676f, 0.513366f, 0.634594f, + -0.203073f, 0.478550f, 0.581526f, + -0.266772f, 0.642330f, 0.602061f, + -0.216961f, 0.564846f, 0.535435f, + -0.202210f, 0.525495f, 0.475944f, + -0.193888f, 0.467925f, 0.520606f, + -0.265837f, 0.757267f, 0.500933f, + -0.240306f, 0.653440f, 0.463215f, + -0.309239f, 0.776868f, 0.304726f, + -0.271009f, 0.683094f, 0.382018f, + -0.312111f, 0.671099f, 0.286687f, + -0.268791f, 0.624342f, 0.377231f, + -0.302457f, 0.533996f, 0.360289f, + -0.263656f, 0.529310f, 0.412564f, + -0.282311f, 0.415167f, 0.447666f, + -0.239201f, 0.442096f, 0.495604f, + -0.220043f, 0.569026f, 0.445877f, + -0.001263f, 0.395631f, 0.602029f, + -0.057345f, 0.442535f, 0.572224f, + -0.088927f, 0.506333f, 0.529106f, + -0.125738f, 0.535076f, 0.612913f, + -0.126251f, 0.577170f, 0.483159f, + -0.149594f, 0.611520f, 0.557731f, + -0.163188f, 0.660791f, 0.491080f, + -0.172482f, 0.663387f, 0.415416f, + -0.160464f, 0.591710f, 0.370659f, + -0.156445f, 0.536396f, 0.378302f, + -0.136496f, 0.444358f, 0.425226f, + -0.095564f, 0.373768f, 0.473659f, + -0.104146f, 0.315912f, 0.498104f, + -0.000496f, 0.384194f, 0.473817f, + -0.000183f, 0.297770f, 0.401486f, + -0.129042f, 0.270145f, 0.434495f, + 0.000100f, 0.272963f, 0.349138f, + -0.113060f, 0.236984f, 0.385554f, + 0.007260f, 0.016311f, -0.883396f, + 0.007865f, 0.122104f, -0.956137f, + -0.032842f, 0.115282f, -0.953252f, + -0.089115f, 0.108449f, -0.950317f, + -0.047440f, 0.014729f, -0.882756f, + -0.104458f, 0.013137f, -0.882070f, + -0.086439f, -0.584866f, -0.608343f, + -0.115026f, -0.662605f, -0.436732f, + -0.071683f, -0.665372f, -0.606385f, + -0.257884f, -0.665381f, -0.658052f, + -0.272542f, -0.665381f, -0.592063f, + -0.371322f, -0.665382f, -0.353620f, + -0.372362f, -0.665381f, -0.224420f, + -0.335166f, -0.665380f, -0.078623f, + -0.225999f, -0.665375f, -0.038981f, + -0.106719f, -0.665374f, -0.186351f, + -0.081749f, -0.665372f, -0.292554f, + 0.006943f, -0.091505f, -0.858354f, + 0.006117f, -0.280985f, -0.769967f, + 0.004495f, -0.502360f, -0.559799f, + -0.198638f, -0.302135f, -0.845816f, + -0.237395f, -0.542544f, -0.587188f, + -0.270001f, -0.279489f, -0.669861f, + -0.134547f, -0.119852f, -0.959004f, + -0.052088f, -0.122463f, -0.944549f, + -0.124463f, -0.293508f, -0.899566f, + -0.047616f, -0.289643f, -0.879292f, + -0.168595f, -0.529132f, -0.654931f, + -0.099793f, -0.515719f, -0.645873f, + -0.186168f, -0.605282f, -0.724690f, + -0.112970f, -0.583097f, -0.707469f, + -0.108152f, -0.665375f, -0.700408f, + -0.183019f, -0.665378f, -0.717630f, + -0.349529f, -0.334459f, -0.511985f, + -0.141182f, -0.437705f, -0.798194f, + -0.212670f, -0.448725f, -0.737447f, + -0.261111f, -0.414945f, -0.613835f, + -0.077364f, -0.431480f, -0.778113f, + 0.005174f, -0.425277f, -0.651592f, + 0.089236f, -0.431732f, -0.777093f, + 0.271006f, -0.415749f, -0.610577f, + 0.223981f, -0.449384f, -0.734774f, + 0.153275f, -0.438150f, -0.796391f, + 0.358414f, -0.335529f, -0.507649f, + 0.193434f, -0.665946f, -0.715325f, + 0.118363f, -0.665717f, -0.699021f, + 0.123515f, -0.583454f, -0.706020f, + 0.196851f, -0.605860f, -0.722345f, + 0.109788f, -0.516035f, -0.644590f, + 0.178656f, -0.529656f, -0.652804f, + 0.061157f, -0.289807f, -0.878626f, + 0.138234f, -0.293905f, -0.897958f, + 0.066933f, -0.122643f, -0.943820f, + 0.149571f, -0.120281f, -0.957264f, + 0.280989f, -0.280321f, -0.666487f, + 0.246581f, -0.543275f, -0.584224f, + 0.211720f, -0.302754f, -0.843303f, + 0.086966f, -0.665627f, -0.291520f, + 0.110634f, -0.665702f, -0.185021f, + 0.228099f, -0.666061f, -0.036201f, + 0.337743f, -0.666396f, -0.074503f, + 0.376722f, -0.666513f, -0.219833f, + 0.377265f, -0.666513f, -0.349036f, + 0.281411f, -0.666217f, -0.588670f, + 0.267564f, -0.666174f, -0.654834f, + 0.080745f, -0.665602f, -0.605452f, + 0.122016f, -0.662963f, -0.435280f, + 0.095767f, -0.585141f, -0.607228f, + 0.118944f, 0.012799f, -0.880702f, + 0.061944f, 0.014564f, -0.882086f, + 0.104725f, 0.108156f, -0.949130f, + 0.048513f, 0.115159f, -0.952753f, + 0.112696f, 0.236643f, 0.386937f, + 0.128177f, 0.269757f, 0.436071f, + 0.102643f, 0.315600f, 0.499370f, + 0.094535f, 0.373481f, 0.474824f, + 0.136270f, 0.443946f, 0.426895f, + 0.157071f, 0.535923f, 0.380222f, + 0.161350f, 0.591224f, 0.372630f, + 0.173035f, 0.662865f, 0.417531f, + 0.162808f, 0.660299f, 0.493077f, + 0.148250f, 0.611070f, 0.559555f, + 0.125719f, 0.576790f, 0.484702f, + 0.123489f, 0.534699f, 0.614440f, + 0.087621f, 0.506066f, 0.530188f, + 0.055321f, 0.442365f, 0.572915f, + 0.219936f, 0.568361f, 0.448571f, + 0.238099f, 0.441375f, 0.498528f, + 0.281711f, 0.414315f, 0.451121f, + 0.263833f, 0.528513f, 0.415794f, + 0.303284f, 0.533081f, 0.363998f, + 0.269687f, 0.623528f, 0.380528f, + 0.314255f, 0.670153f, 0.290524f, + 0.272023f, 0.682273f, 0.385343f, + 0.311480f, 0.775931f, 0.308527f, + 0.240239f, 0.652714f, 0.466159f, + 0.265619f, 0.756464f, 0.504187f, + 0.192562f, 0.467341f, 0.522972f, + 0.201605f, 0.524885f, 0.478417f, + 0.215743f, 0.564193f, 0.538084f, + 0.264969f, 0.641527f, 0.605317f, + 0.201031f, 0.477940f, 0.584002f, + 0.263086f, 0.512567f, 0.637832f, + 0.238615f, 0.526867f, 0.672237f, + 0.105309f, 0.455123f, 0.658482f, + 0.183993f, 0.102195f, 0.804872f, + 0.161563f, 0.060042f, 0.808692f, + 0.180748f, 0.077754f, 0.771600f, + 0.175168f, 0.128588f, 0.746368f, + 0.175075f, 0.148030f, 0.778264f, + 0.175658f, 0.139265f, 0.814333f, + 0.154191f, 0.067291f, 0.832578f, + 0.163818f, 0.109013f, 0.842830f, + 0.084760f, 0.396004f, 0.679695f, + 0.238888f, 0.310760f, 0.590775f, + 0.213380f, 0.308625f, 0.644905f, + 0.199666f, 0.409678f, 0.683003f, + 0.190143f, 0.128597f, 0.733463f, + 0.184833f, 0.063516f, 0.762902f, + 0.166070f, 0.035644f, 0.818261f, + 0.154361f, 0.056943f, 0.857042f, + 0.168542f, 0.109489f, 0.862725f, + 0.187387f, 0.166131f, 0.784599f, + 0.180428f, 0.160135f, 0.819438f, + 0.201823f, 0.163991f, 0.695756f, + 0.194206f, 0.206635f, 0.782275f, + 0.155438f, 0.291260f, 0.734412f, + 0.177696f, 0.196424f, 0.846693f, + 0.152305f, 0.125256f, 0.890786f, + 0.119546f, 0.249876f, 0.859104f, + 0.118369f, 0.139643f, 0.919173f, + 0.079410f, 0.132973f, 0.948652f, + 0.062419f, 0.036648f, 0.976547f, + 0.127847f, -0.035919f, 0.947070f, + 0.143624f, 0.032206f, 0.885913f, + 0.074888f, -0.085173f, 0.980577f, + 0.130184f, -0.104656f, 0.947620f, + 0.156201f, -0.094653f, 0.899074f, + 0.077366f, -0.171194f, 0.926545f, + 0.127722f, -0.164729f, 0.879810f, + 0.052670f, -0.184618f, 0.842019f, + 0.023477f, -0.184638f, 0.889811f, + 0.022626f, -0.210587f, 0.827500f, + 0.223089f, 0.211976f, 0.620493f, + 0.251444f, 0.113067f, 0.666494f, + 0.251419f, 0.089540f, 0.673887f, + 0.214360f, 0.019258f, 0.771595f, + 0.158999f, 0.001490f, 0.835374f, + 0.176696f, -0.059249f, 0.849218f, + 0.148696f, -0.130091f, 0.793599f, + 0.108290f, -0.166528f, 0.772088f, + 0.049820f, -0.201382f, 0.764454f, + 0.071341f, -0.215195f, 0.697209f, + 0.073148f, -0.214475f, 0.623510f, + 0.140502f, -0.169461f, 0.699354f, + 0.163374f, -0.157073f, 0.611416f, + 0.189466f, -0.138550f, 0.730366f, + 0.247593f, -0.082554f, 0.759610f, + 0.227468f, -0.121982f, 0.590197f, + 0.284702f, -0.006586f, 0.535347f, + 0.275741f, 0.125287f, 0.446676f, + 0.266650f, 0.192594f, 0.506044f, + 0.300086f, 0.053287f, 0.629620f, + 0.055450f, -0.663935f, 0.375065f, + 0.122854f, -0.664138f, 0.482323f, + 0.046520f, -0.531571f, 0.391918f, + 0.024824f, -0.568450f, 0.275106f, + 0.053855f, -0.663931f, 0.328224f, + 0.112829f, -0.453549f, 0.305788f, + 0.131265f, -0.510617f, 0.080746f, + 0.061174f, -0.430716f, -0.042710f, + 0.341019f, -0.532887f, -0.208150f, + 0.347705f, -0.623533f, -0.081139f, + 0.238040f, -0.610732f, -0.038037f, + 0.211764f, -0.514274f, -0.132078f, + 0.120605f, -0.600219f, -0.186856f, + 0.096985f, -0.584476f, -0.293357f, + 0.127621f, -0.581941f, -0.437170f, + 0.165902f, -0.477425f, -0.291453f, + 0.077720f, -0.417975f, -0.220519f, + 0.320892f, -0.506363f, -0.320874f, + 0.248214f, -0.465684f, -0.239842f, + 0.118764f, -0.383338f, -0.187114f, + 0.118816f, -0.430106f, -0.123307f, + 0.094131f, -0.419464f, -0.044777f, + 0.274526f, -0.261706f, 0.005110f, + 0.259842f, -0.283292f, -0.003185f, + 0.222861f, -0.340431f, -0.038210f, + 0.204445f, -0.664380f, 0.513353f, + 0.259286f, -0.664547f, 0.471281f, + 0.185402f, -0.476020f, 0.421718f, + 0.279163f, -0.664604f, 0.417328f, + 0.277157f, -0.528122f, 0.400208f, + 0.183069f, -0.509812f, 0.329995f, + 0.282599f, -0.429210f, 0.059242f, + 0.254816f, -0.664541f, 0.290687f, + 0.271436f, -0.567707f, 0.263966f, + 0.386561f, -0.625221f, -0.216870f, + 0.387086f, -0.630883f, -0.346073f, + 0.380021f, -0.596021f, -0.318679f, + 0.291269f, -0.619007f, -0.585707f, + 0.339280f, -0.571198f, -0.461946f, + 0.400045f, -0.489778f, -0.422640f, + 0.406817f, -0.314349f, -0.371230f, + 0.300588f, -0.281718f, -0.170549f, + 0.290866f, -0.277304f, -0.061905f, + 0.187735f, -0.241545f, 0.509437f, + 0.188032f, -0.287569f, 0.424234f, + 0.227520f, -0.373262f, 0.293102f, + 0.266526f, -0.273650f, 0.039597f, + 0.291592f, -0.291676f, 0.111386f, + 0.291914f, -0.122741f, 0.422683f, + 0.297574f, -0.156119f, 0.373368f, + 0.286603f, -0.232731f, 0.027162f, + 0.364663f, -0.201399f, 0.206850f, + 0.353855f, -0.132408f, 0.149228f, + 0.282208f, -0.019715f, 0.314960f, + 0.331187f, -0.099266f, 0.092701f, + 0.375463f, -0.093120f, -0.006467f, + 0.375917f, -0.101236f, -0.154882f, + 0.466635f, -0.094416f, -0.305669f, + 0.455805f, -0.119881f, -0.460632f, + 0.277465f, -0.604242f, -0.651871f, + 0.261022f, -0.551176f, -0.554667f, + 0.093627f, 0.258494f, -0.920589f, + 0.114248f, 0.310608f, -0.798070f, + 0.144232f, 0.211434f, -0.835001f, + 0.119916f, 0.176940f, -0.951159f, + 0.184061f, 0.101854f, -0.918220f, + 0.092431f, 0.276521f, -0.738231f, + 0.133504f, 0.218403f, -0.758602f, + 0.194987f, 0.097655f, -0.812476f, + 0.185542f, 0.011005f, -0.879202f, + 0.230315f, -0.127450f, -0.884202f, + 0.260471f, 0.255056f, -0.624378f, + 0.351567f, -0.042194f, -0.663976f, + 0.253742f, 0.323524f, -0.433716f, + 0.411612f, 0.132299f, -0.438264f, + 0.270513f, 0.356530f, -0.289984f, + 0.422146f, 0.162819f, -0.273130f, + 0.164724f, 0.237490f, 0.208912f, + 0.253806f, 0.092900f, 0.240640f, + 0.203608f, 0.284597f, 0.096223f, + 0.241006f, 0.343093f, -0.171396f, + 0.356076f, 0.149288f, -0.143443f, + 0.337656f, 0.131992f, 0.066374f +}; + +dTriIndex Indices[IndexCount / 3][3] = { + {126,134,133}, + {342,138,134}, + {133,134,138}, + {126,342,134}, + {312,316,317}, + {169,163,162}, + {312,317,319}, + {312,319,318}, + {169,162,164}, + {169,168,163}, + {312,314,315}, + {169,164,165}, + {169,167,168}, + {312,315,316}, + {312,313,314}, + {169,165,166}, + {169,166,167}, + {312,318,313}, + {308,304,305}, + {308,305,306}, + {179,181,188}, + {177,173,175}, + {177,175,176}, + {302,293,300}, + {322,294,304}, + {188,176,175}, + {188,175,179}, + {158,177,187}, + {305,293,302}, + {305,302,306}, + {322,304,308}, + {188,181,183}, + {158,173,177}, + {293,298,300}, + {304,294,296}, + {304,296,305}, + {185,176,188}, + {185,188,183}, + {187,177,176}, + {187,176,185}, + {305,296,298}, + {305,298,293}, + {436,432, 28}, + {436, 28, 23}, + {434,278,431}, + { 30,208,209}, + { 30,209, 29}, + { 19, 20, 24}, + {208,207,211}, + {208,211,209}, + { 19,210,212}, + {433,434,431}, + {433,431,432}, + {433,432,436}, + {436,437,433}, + {277,275,276}, + {277,276,278}, + {209,210, 25}, + { 21, 26, 24}, + { 21, 24, 20}, + { 25, 26, 27}, + { 25, 27, 29}, + {435,439,277}, + {439,275,277}, + {432,431, 30}, + {432, 30, 28}, + {433,437,438}, + {433,438,435}, + {434,277,278}, + { 24, 25,210}, + { 24, 26, 25}, + { 29, 27, 28}, + { 29, 28, 30}, + { 19, 24,210}, + {208, 30,431}, + {208,431,278}, + {435,434,433}, + {435,277,434}, + { 25, 29,209}, + { 27, 22, 23}, + { 27, 23, 28}, + { 26, 22, 27}, + { 26, 21, 22}, + {212,210,209}, + {212,209,211}, + {207,208,278}, + {207,278,276}, + {439,435,438}, + { 12, 9, 10}, + { 12, 10, 13}, + { 2, 3, 5}, + { 2, 5, 4}, + { 16, 13, 14}, + { 16, 14, 17}, + { 22, 21, 16}, + { 13, 10, 11}, + { 13, 11, 14}, + { 1, 0, 3}, + { 1, 3, 2}, + { 15, 12, 16}, + { 19, 18, 15}, + { 19, 15, 16}, + { 19, 16, 20}, + { 9, 1, 2}, + { 9, 2, 10}, + { 3, 7, 8}, + { 3, 8, 5}, + { 16, 17, 23}, + { 16, 23, 22}, + { 21, 20, 16}, + { 10, 2, 4}, + { 10, 4, 11}, + { 0, 6, 7}, + { 0, 7, 3}, + { 12, 13, 16}, + {451,446,445}, + {451,445,450}, + {442,440,439}, + {442,439,438}, + {442,438,441}, + {421,420,422}, + {412,411,426}, + {412,426,425}, + {408,405,407}, + {413, 67, 68}, + {413, 68,414}, + {391,390,412}, + { 80,384,386}, + {404,406,378}, + {390,391,377}, + {390,377, 88}, + {400,415,375}, + {398,396,395}, + {398,395,371}, + {398,371,370}, + {112,359,358}, + {112,358,113}, + {351,352,369}, + {125,349,348}, + {345,343,342}, + {342,340,339}, + {341,335,337}, + {328,341,327}, + {331,323,333}, + {331,322,323}, + {327,318,319}, + {327,319,328}, + {315,314,324}, + {302,300,301}, + {302,301,303}, + {320,311,292}, + {285,284,289}, + {310,307,288}, + {310,288,290}, + {321,350,281}, + {321,281,282}, + {423,448,367}, + {272,273,384}, + {272,384,274}, + {264,265,382}, + {264,382,383}, + {440,442,261}, + {440,261,263}, + {252,253,254}, + {252,254,251}, + {262,256,249}, + {262,249,248}, + {228,243,242}, + {228, 31,243}, + {213,215,238}, + {213,238,237}, + { 19,212,230}, + {224,225,233}, + {224,233,231}, + {217,218, 56}, + {217, 56, 54}, + {217,216,239}, + {217,239,238}, + {217,238,215}, + {218,217,215}, + {218,215,214}, + { 6,102,206}, + {186,199,200}, + {197,182,180}, + {170,171,157}, + {201,200,189}, + {170,190,191}, + {170,191,192}, + {175,174,178}, + {175,178,179}, + {168,167,155}, + {122,149,158}, + {122,158,159}, + {135,153,154}, + {135,154,118}, + {143,140,141}, + {143,141,144}, + {132,133,136}, + {130,126,133}, + {124,125,127}, + {122,101,100}, + {122,100,121}, + {110,108,107}, + {110,107,109}, + { 98, 99, 97}, + { 98, 97, 64}, + { 98, 64, 66}, + { 87, 55, 57}, + { 83, 82, 79}, + { 83, 79, 84}, + { 78, 74, 50}, + { 49, 71, 41}, + { 49, 41, 37}, + { 49, 37, 36}, + { 58, 44, 60}, + { 60, 59, 58}, + { 51, 34, 33}, + { 39, 40, 42}, + { 39, 42, 38}, + {243,240, 33}, + {243, 33,229}, + { 39, 38, 6}, + { 44, 46, 40}, + { 55, 56, 57}, + { 64, 62, 65}, + { 64, 65, 66}, + { 41, 71, 45}, + { 75, 50, 51}, + { 81, 79, 82}, + { 77, 88, 73}, + { 93, 92, 94}, + { 68, 47, 46}, + { 96, 97, 99}, + { 96, 99, 95}, + {110,109,111}, + {111,112,110}, + {114,113,123}, + {114,123,124}, + {132,131,129}, + {133,137,136}, + {135,142,145}, + {145,152,135}, + {149,147,157}, + {157,158,149}, + {164,150,151}, + {153,163,168}, + {153,168,154}, + {185,183,182}, + {185,182,184}, + {161,189,190}, + {200,199,191}, + {200,191,190}, + {180,178,195}, + {180,195,196}, + {102,101,204}, + {102,204,206}, + { 43, 48,104}, + { 43,104,103}, + {216,217, 54}, + {216, 54, 32}, + {207,224,231}, + {230,212,211}, + {230,211,231}, + {227,232,241}, + {227,241,242}, + {235,234,241}, + {235,241,244}, + {430,248,247}, + {272,274,253}, + {272,253,252}, + {439,260,275}, + {225,224,259}, + {225,259,257}, + {269,270,407}, + {269,407,405}, + {270,269,273}, + {270,273,272}, + {273,269,268}, + {273,268,267}, + {273,267,266}, + {273,266,265}, + {273,265,264}, + {448,279,367}, + {281,350,368}, + {285,286,301}, + {290,323,310}, + {290,311,323}, + {282,281,189}, + {292,311,290}, + {292,290,291}, + {307,306,302}, + {307,302,303}, + {316,315,324}, + {316,324,329}, + {331,351,350}, + {330,334,335}, + {330,335,328}, + {341,337,338}, + {344,355,354}, + {346,345,348}, + {346,348,347}, + {364,369,352}, + {364,352,353}, + {365,363,361}, + {365,361,362}, + {376,401,402}, + {373,372,397}, + {373,397,400}, + {376, 92,377}, + {381,378,387}, + {381,387,385}, + {386, 77, 80}, + {390,389,412}, + {416,417,401}, + {403,417,415}, + {408,429,430}, + {419,423,418}, + {427,428,444}, + {427,444,446}, + {437,436,441}, + {450,445, 11}, + {450, 11, 4}, + {447,449, 5}, + {447, 5, 8}, + {441,438,437}, + {425,426,451}, + {425,451,452}, + {417,421,415}, + {408,407,429}, + {399,403,400}, + {399,400,397}, + {394,393,416}, + {389,411,412}, + {386,383,385}, + {408,387,378}, + {408,378,406}, + {377,391,376}, + { 94,375,415}, + {372,373,374}, + {372,374,370}, + {359,111,360}, + {359,112,111}, + {113,358,349}, + {113,349,123}, + {346,343,345}, + {343,340,342}, + {338,336,144}, + {338,144,141}, + {327,341,354}, + {327,354,326}, + {331,350,321}, + {331,321,322}, + {314,313,326}, + {314,326,325}, + {300,298,299}, + {300,299,301}, + {288,287,289}, + {189,292,282}, + {287,288,303}, + {284,285,297}, + {368,280,281}, + {448,447,279}, + {274,226,255}, + {267,268,404}, + {267,404,379}, + {429,262,430}, + {439,440,260}, + {257,258,249}, + {257,249,246}, + {430,262,248}, + {234,228,242}, + {234,242,241}, + {237,238,239}, + {237,239,236}, + { 15, 18,227}, + { 15,227,229}, + {222,223, 82}, + {222, 82, 83}, + {214,215,213}, + {214,213, 81}, + { 38,102, 6}, + {122,159,200}, + {122,200,201}, + {174,171,192}, + {174,192,194}, + {197,193,198}, + {190,170,161}, + {181,179,178}, + {181,178,180}, + {166,156,155}, + {163,153,152}, + {163,152,162}, + {120,156,149}, + {120,149,121}, + {152,153,135}, + {140,143,142}, + {135,131,132}, + {135,132,136}, + {130,129,128}, + {130,128,127}, + {100,105,119}, + {100,119,120}, + {106,104,107}, + {106,107,108}, + { 91, 95, 59}, + { 93, 94, 68}, + { 91, 89, 92}, + { 76, 53, 55}, + { 76, 55, 87}, + { 81, 78, 79}, + { 74, 73, 49}, + { 69, 60, 45}, + { 58, 62, 64}, + { 58, 64, 61}, + { 53, 31, 32}, + { 32, 54, 53}, + { 42, 43, 38}, + { 35, 36, 0}, + { 35, 0, 1}, + { 34, 35, 1}, + { 34, 1, 9}, + { 44, 40, 41}, + { 44, 41, 45}, + { 33,240, 51}, + { 63, 62, 58}, + { 63, 58, 59}, + { 45, 71, 70}, + { 76, 75, 51}, + { 76, 51, 52}, + { 86, 85, 84}, + { 86, 84, 87}, + { 89, 72, 73}, + { 89, 73, 88}, + { 91, 92, 96}, + { 91, 96, 95}, + { 72, 91, 60}, + { 72, 60, 69}, + {104,106,105}, + {119,105,117}, + {119,117,118}, + {124,127,128}, + {117,116,129}, + {117,129,131}, + {118,117,131}, + {135,140,142}, + {146,150,152}, + {146,152,145}, + {149,122,121}, + {166,165,151}, + {166,151,156}, + {158,172,173}, + {161,160,189}, + {199,198,193}, + {199,193,191}, + {204,201,202}, + {178,174,194}, + {200,159,186}, + {109, 48, 67}, + { 48,107,104}, + {216, 32,236}, + {216,236,239}, + {223,214, 81}, + {223, 81, 82}, + { 33, 12, 15}, + { 32,228,234}, + { 32,234,236}, + {240, 31, 52}, + {256,255,246}, + {256,246,249}, + {258,263,248}, + {258,248,249}, + {275,260,259}, + {275,259,276}, + {207,276,259}, + {270,271,429}, + {270,429,407}, + {413,418,366}, + {413,366,365}, + {368,367,279}, + {368,279,280}, + {303,301,286}, + {303,286,287}, + {283,282,292}, + {283,292,291}, + {320,292,189}, + {298,296,297}, + {298,297,299}, + {318,327,326}, + {318,326,313}, + {329,330,317}, + {336,333,320}, + {326,354,353}, + {334,332,333}, + {334,333,336}, + {342,339,139}, + {342,139,138}, + {345,342,126}, + {347,357,356}, + {369,368,351}, + {363,356,357}, + {363,357,361}, + {366,367,368}, + {366,368,369}, + {375,373,400}, + { 92, 90,377}, + {409,387,408}, + {386,385,387}, + {386,387,388}, + {412,394,391}, + {396,398,399}, + {408,406,405}, + {415,421,419}, + {415,419,414}, + {425,452,448}, + {425,448,424}, + {444,441,443}, + {448,452,449}, + {448,449,447}, + {446,444,443}, + {446,443,445}, + {250,247,261}, + {250,261,428}, + {421,422,423}, + {421,423,419}, + {427,410,250}, + {417,403,401}, + {403,402,401}, + {420,392,412}, + {420,412,425}, + {420,425,424}, + {386,411,389}, + {383,382,381}, + {383,381,385}, + {378,379,404}, + {372,371,395}, + {372,395,397}, + {371,372,370}, + {361,359,360}, + {361,360,362}, + {368,350,351}, + {349,347,348}, + {356,355,344}, + {356,344,346}, + {344,341,340}, + {344,340,343}, + {338,337,336}, + {328,335,341}, + {324,352,351}, + {324,351,331}, + {320,144,336}, + {314,325,324}, + {322,308,309}, + {310,309,307}, + {287,286,289}, + {203,280,279}, + {203,279,205}, + {297,295,283}, + {297,283,284}, + {447,205,279}, + {274,384, 80}, + {274, 80,226}, + {266,267,379}, + {266,379,380}, + {225,257,246}, + {225,246,245}, + {256,254,253}, + {256,253,255}, + {430,247,250}, + {226,235,244}, + {226,244,245}, + {232,233,244}, + {232,244,241}, + {230, 18, 19}, + { 32, 31,228}, + {219,220, 86}, + {219, 86, 57}, + {226,213,235}, + {206, 7, 6}, + {122,201,101}, + {201,204,101}, + {180,196,197}, + {170,192,171}, + {200,190,189}, + {194,193,195}, + {183,181,180}, + {183,180,182}, + {155,154,168}, + {149,156,151}, + {149,151,148}, + {155,156,120}, + {145,142,143}, + {145,143,146}, + {136,137,140}, + {133,132,130}, + {128,129,116}, + {100,120,121}, + {110,112,113}, + {110,113,114}, + { 66, 65, 63}, + { 66, 63, 99}, + { 66, 99, 98}, + { 96, 46, 61}, + { 89, 88, 90}, + { 86, 87, 57}, + { 80, 78, 81}, + { 72, 69, 49}, + { 67, 48, 47}, + { 67, 47, 68}, + { 56, 55, 53}, + { 50, 49, 36}, + { 50, 36, 35}, + { 40, 39, 41}, + {242,243,229}, + {242,229,227}, + { 6, 37, 39}, + { 42, 47, 48}, + { 42, 48, 43}, + { 61, 46, 44}, + { 45, 70, 69}, + { 69, 70, 71}, + { 69, 71, 49}, + { 74, 78, 77}, + { 83, 84, 85}, + { 73, 74, 77}, + { 93, 96, 92}, + { 68, 46, 93}, + { 95, 99, 63}, + { 95, 63, 59}, + {115,108,110}, + {115,110,114}, + {125,126,127}, + {129,130,132}, + {137,133,138}, + {137,138,139}, + {148,146,143}, + {148,143,147}, + {119,118,154}, + {161,147,143}, + {165,164,151}, + {158,157,171}, + {158,171,172}, + {159,158,187}, + {159,187,186}, + {194,192,191}, + {194,191,193}, + {189,202,201}, + {182,197,184}, + {205, 8, 7}, + { 48,109,107}, + {218,219, 57}, + {218, 57, 56}, + {207,231,211}, + {232,230,231}, + {232,231,233}, + { 53, 52, 31}, + {388,411,386}, + {409,430,250}, + {262,429,254}, + {262,254,256}, + {442,444,428}, + {273,264,383}, + {273,383,384}, + {429,271,251}, + {429,251,254}, + {413,365,362}, + { 67,413,360}, + {282,283,295}, + {285,301,299}, + {202,281,280}, + {284,283,291}, + {284,291,289}, + {320,189,160}, + {308,306,307}, + {307,309,308}, + {319,317,330}, + {319,330,328}, + {353,352,324}, + {332,331,333}, + {340,341,338}, + {354,341,344}, + {349,358,357}, + {349,357,347}, + {364,355,356}, + {364,356,363}, + {364,365,366}, + {364,366,369}, + {374,376,402}, + {375, 92,373}, + { 77,389,390}, + {382,380,381}, + {389, 77,386}, + {393,394,412}, + {393,412,392}, + {401,394,416}, + {415,400,403}, + {411,410,427}, + {411,427,426}, + {422,420,424}, + {247,248,263}, + {247,263,261}, + {445,443, 14}, + {445, 14, 11}, + {449,450, 4}, + {449, 4, 5}, + {443,441, 17}, + {443, 17, 14}, + {436, 23, 17}, + {436, 17,441}, + {424,448,422}, + {448,423,422}, + {414,419,418}, + {414,418,413}, + {406,404,405}, + {399,397,395}, + {399,395,396}, + {420,416,392}, + {388,410,411}, + {386,384,383}, + {390, 88, 77}, + {375, 94, 92}, + {415,414, 68}, + {415, 68, 94}, + {370,374,402}, + {370,402,398}, + {361,357,358}, + {361,358,359}, + {125,348,126}, + {346,344,343}, + {340,338,339}, + {337,335,334}, + {337,334,336}, + {325,353,324}, + {324,331,332}, + {324,332,329}, + {323,322,309}, + {323,309,310}, + {294,295,297}, + {294,297,296}, + {289,286,285}, + {202,280,203}, + {288,307,303}, + {282,295,321}, + { 67,360,111}, + {418,423,367}, + {418,367,366}, + {272,252,251}, + {272,251,271}, + {272,271,270}, + {255,253,274}, + {265,266,380}, + {265,380,382}, + {442,428,261}, + {440,263,258}, + {440,258,260}, + {409,250,410}, + {255,226,245}, + {255,245,246}, + { 31,240,243}, + {236,234,235}, + {236,235,237}, + {233,225,245}, + {233,245,244}, + {220,221, 85}, + {220, 85, 86}, + { 81,213,226}, + { 81,226, 80}, + { 7,206,205}, + {186,184,198}, + {186,198,199}, + {204,203,205}, + {204,205,206}, + {195,193,196}, + {171,174,172}, + {173,174,175}, + {173,172,174}, + {155,167,166}, + {160,161,143}, + {160,143,144}, + {119,154,155}, + {148,151,150}, + {148,150,146}, + {140,137,139}, + {140,139,141}, + {127,126,130}, + {114,124,128}, + {114,128,115}, + {117,105,106}, + {117,106,116}, + {104,105,100}, + {104,100,103}, + { 59, 60, 91}, + { 97, 96, 61}, + { 97, 61, 64}, + { 91, 72, 89}, + { 87, 84, 79}, + { 87, 79, 76}, + { 78, 80, 77}, + { 49, 50, 74}, + { 60, 44, 45}, + { 61, 44, 58}, + { 51, 50, 35}, + { 51, 35, 34}, + { 39, 37, 41}, + { 33, 34, 9}, + { 33, 9, 12}, + { 0, 36, 37}, + { 0, 37, 6}, + { 40, 46, 47}, + { 40, 47, 42}, + { 53, 54, 56}, + { 65, 62, 63}, + { 72, 49, 73}, + { 79, 78, 75}, + { 79, 75, 76}, + { 52, 53, 76}, + { 92, 89, 90}, + { 96, 93, 46}, + {102,103,100}, + {102,100,101}, + {116,106,108}, + {116,108,115}, + {123,125,124}, + {116,115,128}, + {118,131,135}, + {140,135,136}, + {148,147,149}, + {120,119,155}, + {164,162,152}, + {164,152,150}, + {157,147,161}, + {157,161,170}, + {186,187,185}, + {186,185,184}, + {193,197,196}, + {202,203,204}, + {194,195,178}, + {198,184,197}, + { 67,111,109}, + { 38, 43,103}, + { 38,103,102}, + {214,223,222}, + {214,222,221}, + {214,221,220}, + {214,220,219}, + {214,219,218}, + {213,237,235}, + {221,222, 83}, + {221, 83, 85}, + { 15,229, 33}, + {227, 18,230}, + {227,230,232}, + { 52, 51,240}, + { 75, 78, 50}, + {408,430,409}, + {260,258,257}, + {260,257,259}, + {224,207,259}, + {268,269,405}, + {268,405,404}, + {413,362,360}, + {447, 8,205}, + {299,297,285}, + {189,281,202}, + {290,288,289}, + {290,289,291}, + {322,321,295}, + {322,295,294}, + {333,323,311}, + {333,311,320}, + {317,316,329}, + {320,160,144}, + {353,325,326}, + {329,332,334}, + {329,334,330}, + {339,338,141}, + {339,141,139}, + {348,345,126}, + {347,356,346}, + {123,349,125}, + {364,353,354}, + {364,354,355}, + {365,364,363}, + {376,391,394}, + {376,394,401}, + { 92,376,374}, + { 92,374,373}, + {377, 90, 88}, + {380,379,378}, + {380,378,381}, + {388,387,409}, + {388,409,410}, + {416,393,392}, + {399,398,402}, + {399,402,403}, + {250,428,427}, + {421,417,416}, + {421,416,420}, + {426,427,446}, + {426,446,451}, + {444,442,441}, + {452,451,450}, + {452,450,449} +}; + diff --git a/thirdparty/ode-0.16.5/ode/demo/convex_bunny_geom.h b/thirdparty/ode-0.16.5/ode/demo/convex_bunny_geom.h new file mode 100644 index 0000000..09fe6de --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/convex_bunny_geom.h @@ -0,0 +1,468 @@ +const unsigned int convexBunnyPlaneCount = 176; +dReal convexBunnyPlanes[] = +{ + 0.986167, -0.0612533, -0.154021, 0.399481, + 0.982735, -0.0691036, -0.171628, 0.409884, + -0.984387, -0.0582774, -0.166089, 0.403079, + 0.985044, -0.172279, 0.00302105, 0.437531, + 0.976915, -0.184361, 0.107929, 0.465334, + 0.951478, -0.281619, 0.124019, 0.475223, + 0.798136, -0.502214, 0.332805, 0.535555, + 0.949728, -0.211128, -0.231176, 0.528156, + -0.000894561, -0.995066, -0.0992088, 0.80299, + -0.000896015, -0.995066, -0.0992131, 0.802991, + -0.0035709, -0.935822, 0.352454, 0.618504, + -0.291551, -0.828515, 0.478081, 0.681579, + -0.978715, -0.181408, 0.0959605, 0.468907, + -0.985523, -0.169302, -0.00904739, 0.441129, + -0.953768, -0.278749, 0.11236, 0.478704, + 0.372745, -0.827499, 0.419888, 0.630174, + 0.976911, -0.18316, 0.109991, 0.466086, + 0.817827, -0.387738, 0.425227, 0.569153, + -0.978732, -0.180211, 0.0980152, 0.469656, + 0.662794, -0.0277654, 0.748287, 0.803459, + 0.00359857, -0.660581, -0.750746, 0.877267, + 0.00359952, -0.660579, -0.750748, 0.877266, + -0.947456, -0.208272, -0.242797, 0.531628, + -0.980764, -0.0661375, -0.18365, 0.413468, + -0.940835, -0.0698657, -0.331585, 0.494827, + 0.983751, 0.0529367, -0.171556, 0.403533, + 0.981839, 0.0996302, -0.16145, 0.410144, + 0.977938, 0.0857834, -0.190468, 0.411823, + 0.959636, 0.106068, -0.260476, 0.44898, + 0.85334, -0.0495414, -0.518996, 0.60489, + -0.803667, -0.499793, 0.322995, 0.538478, + -0.689742, -0.615484, 0.381361, 0.574754, + -0.380353, -0.826364, 0.415277, 0.63155, + -0.39985, -0.817283, 0.414933, 0.630682, + -0.380309, -0.826285, 0.415473, 0.631677, + -0.981411, 0.0559147, -0.183592, 0.407121, + -0.979526, 0.101914, -0.173615, 0.413635, + -0.975381, 0.0881975, -0.202119, 0.415257, + 0.988445, 0.140182, 0.0576755, 0.485174, + 0.876515, 0.0408992, 0.479634, 0.620093, + 0.848907, -0.0996824, 0.519057, 0.631268, + 0.895754, -0.195088, 0.399457, 0.563346, + 0.861448, -0.256989, 0.438023, 0.574909, + 0.775672, -0.447481, 0.445076, 0.586422, + 0.683157, -0.617557, 0.389768, 0.572247, + 0.391755, -0.818722, 0.419789, 0.629264, + 0.28317, -0.829384, 0.481599, 0.680529, + 0.3727, -0.827422, 0.420079, 0.630298, + -0.824143, -0.385255, 0.415171, 0.57215, + -0.782413, -0.445128, 0.435536, 0.589267, + 0.00152876, 0.999994, -0.00308275, 0.665646, + 0.00242466, 0.999989, -0.0038994, 0.665879, + -0.979892, 0.121321, -0.158406, 0.420287, + 0.767537, -0.190214, -0.612132, 0.695479, + 0.372649, -0.42747, -0.823652, 0.869878, + 0.537245, -0.335515, -0.77382, 0.82472, + 0.0263648, -0.598975, -0.800334, 0.873623, + 0.00393345, -0.60959, -0.792707, 0.869865, + -0.0183706, -0.598907, -0.800608, 0.873704, + 0.00875728, 0.676014, -0.736836, 0.825597, + 0.852333, -0.0355955, -0.521786, 0.607886, + 0.392036, 0.534934, -0.748434, 0.818042, + 0.847696, -0.122973, -0.516033, 0.615814, + 0.884763, -0.0760716, -0.45979, 0.565893, + 0.9446, -0.0727144, -0.320069, 0.491401, + 0.904971, -0.0675211, -0.420081, 0.541673, + -0.899959, -0.0647928, -0.431134, 0.544968, + -0.955972, 0.108494, -0.272667, 0.452769, + -0.363823, -0.426358, -0.828161, 0.871222, + -0.528689, -0.333936, -0.780368, 0.826685, + 0.982068, 0.118277, -0.146811, 0.416542, + 0.98951, 0.144455, 0.00164104, 0.468616, + 0.50797, -0.0708041, 0.85846, 0.883126, + 0.748614, -0.431275, 0.503565, 0.634026, + 0.214863, -0.405791, 0.888351, 0.94048, + -0.901162, -0.192379, 0.388455, 0.566627, + -0.867521, -0.254376, 0.427435, 0.578065, + -0.226957, -0.405135, 0.885639, 0.941284, + -0.756029, -0.429007, 0.494341, 0.636765, + -0.00629553, -0.188362, 0.982079, 0.968197, + 0.0165684, 0.999846, -0.00581961, 0.670373, + 0.00313267, 0.999987, -0.00405124, 0.666103, + 0.545069, 0.472158, -0.692796, 0.780052, + 0.932011, 0.148856, -0.330451, 0.498417, + 0.844043, 0.227673, -0.485547, 0.599903, + -0.019033, 0.999801, -0.00590978, 0.672252, + -0.959662, 0.239262, -0.147656, 0.488538, + 0.00151234, 0.999999, -0.000399466, 0.665855, + -0.988649, 0.143168, 0.0455675, 0.488784, + -0.989015, 0.147444, -0.01048, 0.472227, + -0.972439, 0.232727, -0.01415, 0.518344, + 0.587681, -0.160147, -0.793085, 0.823999, + 0.640479, -0.269179, -0.719256, 0.778142, + 0.541109, -0.332896, -0.772257, 0.823226, + 0.546185, -0.14771, -0.824538, 0.84854, + 0.528519, -0.026044, -0.848522, 0.873136, + 0.447231, -0.0756684, -0.891212, 0.903953, + 0.490619, -0.0795123, -0.867739, 0.884255, + 0.279393, 0.264257, -0.923097, 0.950045, + 0.374653, 0.39486, -0.83888, 0.886593, + 0.00050174, 0.999994, -0.00331563, 0.665976, + -0.0103777, 0.999934, -0.00487936, 0.669494, + -0.927571, 0.150741, -0.341889, 0.501807, + -0.267268, 0.265084, -0.926444, 0.951043, + -0.845984, -0.0330207, -0.532184, 0.610986, + -0.518165, -0.0244575, -0.854931, 0.875047, + -0.83753, 0.228953, -0.496108, 0.603116, + -0.535666, 0.472122, -0.700116, 0.78259, + -0.381083, 0.534649, -0.754272, 0.820328, + -0.363157, 0.395975, -0.843398, 0.88794, + -0.326829, -0.256634, -0.909572, 0.922946, + 0.394875, -0.128601, -0.90969, 0.920236, + 0.337169, -0.257642, -0.905504, 0.921733, + 0.398433, -0.193767, -0.896496, 0.910015, + -0.536477, -0.146072, -0.831177, 0.850523, + -0.436512, -0.0743406, -0.896622, 0.905564, + -0.480187, -0.0780522, -0.873687, 0.886029, + -0.384093, -0.127432, -0.914458, 0.921656, + -0.388009, -0.192572, -0.901313, 0.911451, + 0.977045, 0.15796, 0.14294, 0.521934, + 0.930035, 0.231515, 0.28537, 0.600301, + -0.855499, -0.0971121, 0.508616, 0.634376, + -0.875419, 0.136849, 0.463589, 0.62897, + -0.882196, 0.0435387, 0.468864, 0.623303, + 0.0204398, -0.0238739, 0.999506, 0.958419, + -0.0062197, -0.0777937, 0.99695, 0.962769, + 0.907123, 0.250746, 0.338015, 0.623205, + 0.902358, 0.173321, 0.394601, 0.607696, + 0.870085, 0.134211, 0.474278, 0.625782, + 0.0015108, 0.999999, 6.34978e-06, 0.665945, + 0.00150567, 0.999999, 0.000568537, 0.666143, + 0.00150738, 0.999999, 0.000565012, 0.666141, + -0.963954, 0.266039, -0.00405118, 0.539571, + 0.0015136, 0.999999, -0.000393102, 0.665856, + 0.00151117, 0.999999, 4.32104e-06, 0.665944, + 0.0272711, 0.999604, -0.00696439, 0.673709, + 0.962047, 0.236383, -0.136341, 0.484917, + 0.973236, 0.229796, -0.00223071, 0.514797, + 0.964728, 0.263133, 0.00776342, 0.536054, + -0.906596, 0.176056, 0.383521, 0.610999, + -0.978237, 0.160918, 0.130987, 0.525513, + -0.910434, 0.253486, 0.326887, 0.626522, + -0.932759, 0.234321, 0.27396, 0.603697, + 0.800621, -0.0921333, -0.592045, 0.665278, + 0.679569, -0.15358, -0.717356, 0.765121, + 0.684928, -0.199829, -0.700672, 0.756959, + -0.532604, -0.33128, -0.778837, 0.82519, + -0.578395, -0.158384, -0.800234, 0.826134, + -0.671209, -0.151537, -0.725613, 0.767576, + -0.00530287, 0.323551, 0.946196, 0.94547, + -0.719766, 0.229227, 0.655281, 0.774388, + -0.604194, 0.29171, 0.741522, 0.841564, + -0.544989, 0.302925, 0.781808, 0.866398, + -0.518662, -0.0692529, 0.85217, 0.884999, + -0.671987, -0.0257512, 0.740115, 0.805898, + -0.00613626, -0.00823685, 0.999947, 0.957141, + -0.032747, -0.0237908, 0.99918, 0.958516, + -0.00530611, 0.323546, 0.946198, 0.945471, + -0.545061, 0.302657, 0.781861, 0.866377, + -0.639902, 0.23832, 0.730568, 0.823722, + 0.00149706, 0.999997, 0.00193434, 0.667038, + 0.00149731, 0.999997, 0.00193252, 0.667037, + -0.0048341, 0.44008, 0.897946, 0.936327, + -0.00483143, 0.440078, 0.897947, 0.936327, + -0.632454, -0.267241, -0.727039, 0.780455, + -0.67691, -0.197765, -0.709, 0.759436, + -0.841667, -0.120432, -0.526396, 0.618913, + -0.760706, -0.187792, -0.621337, 0.698143, + -0.87929, -0.0734062, -0.470597, 0.569116, + -0.847068, -0.0469762, -0.529405, 0.607991, + -0.793572, -0.0897399, -0.601823, 0.668201, + 0.536355, 0.301024, 0.788485, 0.864403, + 0.536284, 0.301291, 0.788431, 0.864424, + 0.595945, 0.289897, 0.748872, 0.839373, + 0.631626, 0.236397, 0.738353, 0.8214, + 0.712378, 0.227061, 0.664049, 0.771772, +}; +const unsigned int convexBunnyPointCount = 105; +dReal convexBunnyPoints[] = +{ + -0.459488, -0.093017, -0.311341, + 0.466635, -0.094416, -0.305669, + -0.309239, 0.776868, 0.304726, + -0.004458, -0.042526, 1.01567, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 0.007957, 0.282241, -0.93168, + 0.204445, -0.66438, 0.513353, + -0.303961, 0.054199, 0.625921, + 0.265619, 0.756464, 0.504187, + -0.402162, 0.133528, -0.443247, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.266772, 0.64233, 0.602061, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 0.411612, 0.132299, -0.438264, + 0.31148, 0.775931, 0.308527, + 0.300086, 0.053287, 0.62962, + -0.414624, 0.164083, -0.278254, + -0.248382, 0.255825, -0.627493, + -0.216201, -0.126776, -0.886936, + 0.267564, -0.666174, -0.654834, + -0.135892, -0.03552, 0.945455, + -0.265837, 0.757267, 0.500933, + -0.003873, 0.161605, 0.970499, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.282599, -0.663393, 0.412411, + 0.007237, 0.361687, -0.794439, + 0.093627, 0.258494, -0.920589, + 0.422146, 0.162819, -0.27313, + 0.279163, -0.664604, 0.417328, + 0.263086, 0.512567, 0.637832, + -0.099875, 0.310931, -0.799381, + -0.446838, -0.118517, -0.466159, + -0.168842, 0.102387, -0.920381, + 0.455805, -0.119881, -0.460632, + 0.337743, -0.666396, -0.074503, + -0.134547, -0.119852, -0.959004, + -0.183807, 0.19697, 0.84448, + 0.264969, 0.641527, 0.605317, + -0.209063, -0.663393, 0.509344, + -0.364126, -0.200299, 0.202388, + -0.253475, -0.081797, 0.756541, + 0.260471, 0.255056, -0.624378, + 0.114248, 0.310608, -0.79807, + 0.364663, -0.201399, 0.20685, + 0.127847, -0.035919, 0.94707, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.381071, -0.629723, -0.350777, + -0.339884, -0.04115, -0.668211, + -0.077913, 0.258753, -0.92164, + 0.184061, 0.101854, -0.91822, + -0.335166, -0.66538, -0.078623, + 0.386561, -0.625221, -0.21687, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.241585, 0.527592, 0.669296, + -0.086969, 0.133224, 0.947633, + -0.003127, 0.28407, 0.87887, + -0.004433, -0.146642, 0.985872, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.138444, -0.10425, 0.945975, + -0.265676, 0.513366, 0.634594, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 0.247593, -0.082554, 0.75961, + 0.07941, 0.132973, 0.948652, + 0.238615, 0.526867, 0.672237, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.382112, -0.62406, -0.221577, + -0.104072, 0.177278, -0.95253, + 0.351567, -0.042194, -0.663976, + 0.138234, -0.293905, -0.897958, + 0.119916, 0.17694, -0.951159, + -0.371322, -0.665382, -0.35362, + -0.263384, -0.663396, 0.466604, + 0.376722, -0.666513, -0.219833, + 0.387086, -0.630883, -0.346073, + -0.125544, 0.140012, 0.917678, + -0.070612, 0.036849, 0.975733, + -0.083497, -0.084934, 0.979607, + 0.259286, -0.664547, 0.471281, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 0.074888, -0.085173, 0.980577, + 0.152305, 0.125256, 0.890786, + 0.130184, -0.104656, 0.94762, + -0.004249, 0.046042, 1.00324, + 0.062419, 0.036648, 0.976547, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.392666, -0.488581, -0.427494, + 0.230315, -0.12745, -0.884202, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + 0.193434, -0.665946, -0.715325, + 0.007865, 0.122104, -0.956137, + 8.40779e-45, 3.00321e-39, 2.8026e-44, + -0.257884, -0.665381, -0.658052, + 0.377265, -0.666513, -0.349036, + -0.372362, -0.665381, -0.22442, + 0.400045, -0.489778, -0.42264, + -0.159174, 0.125726, 0.888878, + 0.118369, 0.139643, 0.919173, + -0.124463, -0.293508, -0.899566, + 0.21172, -0.302754, -0.843303, + 0.149571, -0.120281, -0.957264, + -0.183019, -0.665378, -0.71763, + 0.177696, 0.196424, 0.846693, + -0.198638, -0.302135, -0.845816, +}; +unsigned int convexBunnyPolygons[] = +{ + 3, 7, 2, 0, + 3, 2, 7, 11, + 3, 1, 15, 16, + 3, 0, 2, 17, + 3, 17, 9, 0, + 3, 2, 9, 17, + 3, 18, 9, 2, + 3, 2, 11, 22, + 3, 22, 15, 2, + 3, 8, 15, 22, + 3, 2, 15, 26, + 3, 5, 26, 27, + 3, 1, 14, 28, + 3, 28, 15, 1, + 3, 14, 15, 28, + 3, 2, 26, 31, + 3, 0, 9, 32, + 3, 9, 18, 33, + 3, 34, 14, 1, + 3, 19, 33, 36, + 3, 8, 22, 38, + 3, 38, 22, 11, + 3, 38, 15, 8, + 3, 38, 16, 15, + 3, 38, 30, 16, + 3, 40, 7, 0, + 3, 0, 25, 40, + 3, 40, 25, 7, + 3, 7, 25, 41, + 3, 21, 37, 41, + 3, 42, 15, 14, + 3, 42, 27, 15, + 3, 43, 26, 15, + 3, 15, 27, 43, + 3, 43, 27, 26, + 3, 1, 16, 44, + 3, 44, 29, 1, + 3, 16, 29, 44, + 3, 0, 32, 47, + 3, 19, 32, 48, + 3, 48, 33, 19, + 3, 48, 32, 9, + 3, 9, 33, 48, + 3, 49, 33, 18, + 3, 49, 18, 2, + 3, 2, 31, 49, + 3, 49, 26, 5, + 3, 49, 31, 26, + 3, 50, 42, 14, + 3, 27, 42, 50, + 3, 51, 35, 6, + 3, 6, 39, 51, + 3, 1, 29, 52, + 3, 11, 37, 54, + 3, 55, 23, 11, + 3, 11, 54, 55, + 3, 11, 23, 56, + 3, 56, 38, 11, + 3, 23, 38, 56, + 3, 57, 39, 6, + 3, 21, 41, 59, + 3, 39, 57, 59, + 3, 60, 37, 11, + 3, 60, 41, 37, + 3, 60, 11, 7, + 3, 7, 41, 60, + 3, 16, 30, 62, + 3, 62, 29, 16, + 3, 63, 38, 23, + 3, 38, 63, 64, + 3, 67, 25, 0, + 3, 0, 47, 67, + 3, 68, 36, 33, + 3, 33, 49, 68, + 3, 68, 49, 5, + 3, 14, 34, 69, + 3, 69, 50, 14, + 3, 5, 27, 71, + 3, 27, 50, 71, + 3, 71, 68, 5, + 3, 25, 51, 73, + 3, 73, 51, 39, + 3, 39, 59, 73, + 3, 73, 41, 25, + 3, 73, 59, 41, + 3, 29, 35, 74, + 3, 74, 52, 29, + 3, 35, 51, 74, + 3, 75, 34, 1, + 3, 1, 52, 75, + 3, 52, 74, 75, + 3, 21, 55, 76, + 3, 76, 54, 37, + 3, 76, 55, 54, + 3, 77, 55, 21, + 3, 21, 59, 78, + 3, 3, 77, 78, + 3, 78, 77, 21, + 3, 78, 57, 3, + 3, 78, 59, 57, + 3, 6, 35, 79, + 3, 79, 35, 29, + 3, 29, 62, 79, + 3, 3, 57, 81, + 3, 83, 62, 45, + 3, 45, 81, 83, + 3, 83, 79, 62, + 3, 6, 79, 83, + 3, 83, 57, 6, + 3, 83, 81, 57, + 3, 84, 63, 23, + 3, 84, 77, 3, + 3, 23, 55, 84, + 3, 55, 77, 84, + 3, 45, 63, 85, + 3, 3, 81, 85, + 3, 85, 81, 45, + 3, 85, 84, 3, + 3, 63, 84, 85, + 3, 87, 47, 32, + 3, 87, 72, 47, + 3, 50, 69, 88, + 3, 88, 34, 20, + 3, 88, 69, 34, + 3, 36, 68, 91, + 3, 68, 71, 91, + 3, 72, 87, 93, + 3, 93, 87, 32, + 3, 93, 32, 19, + 3, 94, 74, 72, + 3, 94, 93, 20, + 3, 72, 93, 94, + 3, 94, 75, 74, + 3, 95, 74, 51, + 3, 72, 74, 95, + 3, 95, 51, 25, + 3, 25, 67, 95, + 3, 95, 67, 47, + 3, 47, 72, 95, + 3, 20, 34, 96, + 3, 34, 75, 96, + 3, 96, 94, 20, + 3, 75, 94, 96, + 3, 97, 37, 21, + 3, 21, 76, 97, + 3, 97, 76, 37, + 3, 98, 64, 63, + 3, 98, 63, 45, + 3, 45, 82, 98, + 3, 36, 70, 99, + 3, 100, 88, 20, + 3, 20, 90, 100, + 3, 100, 90, 70, + 3, 101, 71, 50, + 3, 50, 88, 101, + 3, 36, 91, 101, + 3, 101, 91, 71, + 3, 101, 70, 36, + 3, 101, 100, 70, + 3, 88, 100, 101, + 3, 102, 90, 20, + 3, 20, 93, 102, + 3, 70, 90, 102, + 3, 102, 99, 70, + 3, 64, 98, 103, + 3, 103, 98, 82, + 3, 30, 38, 103, + 3, 38, 64, 103, + 3, 103, 62, 30, + 3, 45, 62, 103, + 3, 103, 82, 45, + 3, 36, 99, 104, + 3, 99, 102, 104, + 3, 104, 102, 93, + 3, 19, 36, 104, + 3, 104, 93, 19, +}; diff --git a/thirdparty/ode-0.16.5/ode/demo/convex_prism.h b/thirdparty/ode-0.16.5/ode/demo/convex_prism.h new file mode 100644 index 0000000..1dedfdb --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/convex_prism.h @@ -0,0 +1,28 @@ +unsigned int prism_pointcount = 8; +unsigned int prism_planecount = 6; +dReal prism_points[24]={ + 10.0, 1.0,-1.0, + 10.0,-1.0,-1.0, +-10.0,-1.0,-1.0, +-10.0, 1.0,-1.0, + 10.0, 1.0, 1.0, + 10.0,-1.0, 1.0, +-10.0,-1.0, 1.0, +-10.0, 1.0, 1.0 +}; +unsigned int prism_polygons[]={ +4,0,1,2,3, +4,4,7,6,5, +4,0,4,5,1, +4,1,5,6,2, +4,2,6,7,3, +4,4,0,3,7, +}; +dReal prism_planes[]={ +0.0,0.0,-1.0,1.0, +0.0,0.0,1.0,1.0, +1.0,0.0,0.0,10.0, +0.0,-1.0,0.0,1.0, +-1.0,0.0,-0.0,10.0, +0.0,1.0,0.0,1.0, +}; diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_I.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_I.cpp new file mode 100644 index 0000000..156a4ad --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_I.cpp @@ -0,0 +1,253 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +test that the rotational physics is correct. + +an "anchor body" has a number of other randomly positioned bodies +("particles") attached to it by ball-and-socket joints, giving it some +random effective inertia tensor. the effective inertia matrix is calculated, +and then this inertia is assigned to another "test" body. a random torque is +applied to both bodies and the difference in angular velocity and orientation +is observed after a number of iterations. + +typical errors for each test cycle are about 1e-5 ... 1e-4. + +*/ + + +#include +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define NUM 10 // number of particles +#define SIDE 0.1 // visual size of the particles + + +// dynamics objects an globals + +static dWorldID world=0; +static dBodyID anchor_body,particle[NUM],test_body; +static dJointID particle_joint[NUM]; +static dReal torque[3]; +static int iteration; + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {1.5572f,-1.8886f,1.5700f}; + static float hpr[3] = {118.5000f,-17.0000f,0.0000f}; + dsSetViewpoint (xyz,hpr); +} + + +// compute the mass parameters of a particle set. q = particle positions, +// pm = particle masses + +#define _I(i,j) I[(i)*4+(j)] + +void computeMassParams (dMass *m, dReal q[NUM][3], dReal pm[NUM]) +{ + int i,j; + dMassSetZero (m); + for (i=0; imass += pm[i]; + for (j=0; j<3; j++) m->c[j] += pm[i]*q[i][j]; + m->_I(0,0) += pm[i]*(q[i][1]*q[i][1] + q[i][2]*q[i][2]); + m->_I(1,1) += pm[i]*(q[i][0]*q[i][0] + q[i][2]*q[i][2]); + m->_I(2,2) += pm[i]*(q[i][0]*q[i][0] + q[i][1]*q[i][1]); + m->_I(0,1) -= pm[i]*(q[i][0]*q[i][1]); + m->_I(0,2) -= pm[i]*(q[i][0]*q[i][2]); + m->_I(1,2) -= pm[i]*(q[i][1]*q[i][2]); + } + for (j=0; j<3; j++) m->c[j] /= m->mass; + m->_I(1,0) = m->_I(0,1); + m->_I(2,0) = m->_I(0,2); + m->_I(2,1) = m->_I(1,2); +} + + +void reset_test() +{ + int i; + dMass m,anchor_m; + dReal q[NUM][3], pm[NUM]; // particle positions and masses + dReal pos1[3] = {1,0,1}; // point of reference (POR) + dReal pos2[3] = {-1,0,1}; // point of reference (POR) + + // make random particle positions (relative to POR) and masses + for (i=0; i= 100) { + // measure the difference between the anchor and test bodies + const dReal *w1 = dBodyGetAngularVel (anchor_body); + const dReal *w2 = dBodyGetAngularVel (test_body); + const dReal *q1 = dBodyGetQuaternion (anchor_body); + const dReal *q2 = dBodyGetQuaternion (test_body); + dReal maxdiff = dMaxDifference (w1,w2,1,3); + printf ("w-error = %.4e (%.2f,%.2f,%.2f) and (%.2f,%.2f,%.2f)\n", + maxdiff,w1[0],w1[1],w1[2],w2[0],w2[1],w2[2]); + maxdiff = dMaxDifference (q1,q2,1,4); + printf ("q-error = %.4e\n",maxdiff); + reset_test(); + } + } + + dReal sides[3] = {SIDE,SIDE,SIDE}; + dReal sides2[3] = {6*SIDE,6*SIDE,6*SIDE}; + dReal sides3[3] = {3*SIDE,3*SIDE,3*SIDE}; + dsSetColor (1,1,1); + dsDrawBox (dBodyGetPosition(anchor_body), dBodyGetRotation(anchor_body), + sides3); + dsSetColor (1,0,0); + dsDrawBox (dBodyGetPosition(test_body), dBodyGetRotation(test_body), sides2); + dsSetColor (1,1,0); + for (int i=0; i +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include "texturepath.h" +#include "basket_geom.h" // this is our world mesh + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// some constants + +#define RADIUS 0.14 + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; + +static dBodyID sphbody; +static dGeomID sphgeom; + +static dJointGroupID contactgroup; +static dGeomID world_mesh; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + assert(o1); + assert(o2); + + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + fprintf(stderr,"testing space %p %p\n", (void*)o1, (void*)o2); + // colliding a space with something + dSpaceCollide2(o1,o2,data,&nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + return; + } + +// fprintf(stderr,"testing geoms %p %p\n", o1, o2); + + const int N = 32; + dContact contact[N]; + int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact)); + if (n > 0) + { + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +#include "icosahedron_geom.h" + + +//<---- Convex Object +dReal planes[]= // planes for a cube, these should coincide with the face array + { + 1.0f ,0.0f ,0.0f ,0.25f, + 0.0f ,1.0f ,0.0f ,0.25f, + 0.0f ,0.0f ,1.0f ,0.25f, + -1.0f,0.0f ,0.0f ,0.25f, + 0.0f ,-1.0f,0.0f ,0.25f, + 0.0f ,0.0f ,-1.0f,0.25f + /* + 1.0f ,0.0f ,0.0f ,2.0f, + 0.0f ,1.0f ,0.0f ,1.0f, + 0.0f ,0.0f ,1.0f ,1.0f, + 0.0f ,0.0f ,-1.0f,1.0f, + 0.0f ,-1.0f,0.0f ,1.0f, + -1.0f,0.0f ,0.0f ,0.0f + */ + }; +const unsigned int planecount=6; + +dReal points[]= // points for a cube + { + 0.25f,0.25f,0.25f, // point 0 + -0.25f,0.25f,0.25f, // point 1 + + 0.25f,-0.25f,0.25f, // point 2 + -0.25f,-0.25f,0.25f,// point 3 + + 0.25f,0.25f,-0.25f, // point 4 + -0.25f,0.25f,-0.25f,// point 5 + + 0.25f,-0.25f,-0.25f,// point 6 + -0.25f,-0.25f,-0.25f,// point 7 + }; +const unsigned int pointcount=8; +unsigned int polygons[] = //Polygons for a cube (6 squares) + { + 4,0,2,6,4, // positive X + 4,1,0,4,5, // positive Y + 4,0,1,3,2, // positive Z + 4,3,1,5,7, // negative X + 4,2,3,7,6, // negative Y + 4,5,4,6,7, // negative Z + }; +//----> Convex Object + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawConvex dsDrawConvexD +#endif + + +// some constants + +#define NUM 100 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 8 // maximum number of contact points per body +#define MAX_FEEDBACKNUM 20 +#define GRAVITY REAL(0.5) + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? +static int write_world = 0; +static int show_body = 0; + +struct MyFeedback { + dJointFeedback fb; + bool first; +}; +static int doFeedback=0; +static MyFeedback feedbacks[MAX_FEEDBACKNUM]; +static int fbnum=0; + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + if (b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact)) + return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command(int cmd) +{ + dsizeint i; + int j,k; + dReal sides[3]; + dMass m; + bool setBody = false; + + cmd = locase(cmd); + if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'x' || cmd == 'y' || cmd == 'v') { + if (num < NUM) { + // new object to be created + i = num; + num++; + } else { + // recycle existing object + i = nextobj++; + nextobj %= num; // wrap-around if needed + + // destroy the body and geoms for slot i + dBodyDestroy (obj[i].body); + obj[i].body = 0; + + for (k=0; k < GPB; k++) + if (obj[i].geom[k]) { + dGeomDestroy(obj[i].geom[k]); + obj[i].geom[k] = 0; + } + } + + obj[i].body = dBodyCreate(world); + + for (k=0; k<3; k++) + sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) { + dBodySetPosition(obj[i].body, + dRandReal()*2-1,dRandReal()*2-1,dRandReal()+2); + dRFromAxisAndAngle(R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } else { + // higher than highest body position + dReal maxheight = 0; + for (k=0; k maxheight) + maxheight = pos[2]; + } + dBodySetPosition(obj[i].body, 0,0,maxheight+1); + dRSetIdentity(R); + //dRFromAxisAndAngle (R,0,0,1,/*dRandReal()*10.0-5.0*/0); + } + + dBodySetRotation(obj[i].body,R); + + if (cmd == 'b') { + + dMassSetBox(&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox(space,sides[0],sides[1],sides[2]); + + } else if (cmd == 'c') { + + sides[0] *= 0.5; + dMassSetCapsule(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule (space,sides[0],sides[1]); + + } else if (cmd == 'v') { + + dMassSetBox(&m,DENSITY,0.25,0.25,0.25); +#if 0 + obj[i].geom[0] = dCreateConvex(space, + planes, + planecount, + points, + pointcount, + polygons); +#else + obj[i].geom[0] = dCreateConvex(space, + Sphere_planes, + Sphere_planecount, + Sphere_points, + Sphere_pointcount, + Sphere_polygons); +#endif + + } else if (cmd == 'y') { + + dMassSetCylinder(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder(space,sides[0],sides[1]); + + } else if (cmd == 's') { + + sides[0] *= 0.5; + dMassSetSphere (&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere (space,sides[0]); + + } else if (cmd == 'x') { + + setBody = true; + // start accumulating masses for the composite geometries + dMass m2; + dMassSetZero (&m); + + dReal dpos[GPB][3]; // delta-positions for composite geometries + dMatrix3 drot[GPB]; + + // set random delta positions + for (j=0; j= num) + selected = 0; + if (selected == -1) + selected = 0; + + } else if (cmd == 'd' && selected >= 0 && selected < num) { + + dBodyDisable(obj[selected].body); + + } else if (cmd == 'e' && selected >= 0 && selected < num) { + + dBodyEnable(obj[selected].body); + + } else if (cmd == 'a') { + + show_aabb = !show_aabb; + + } else if (cmd == 't') { + + show_contacts = !show_contacts; + + } else if (cmd == 'r') { + + random_pos = !random_pos; + } else if (cmd == '1') { + + write_world = 1; + + } else if (cmd == 'p'&& selected >= 0) { + + const dReal* pos = dGeomGetPosition(obj[selected].geom[0]); + const dReal* rot = dGeomGetRotation(obj[selected].geom[0]); + printf("POSITION:\n\t[%f,%f,%f]\n\n",pos[0],pos[1],pos[2]); + printf("ROTATION:\n\t[%f,%f,%f,%f]\n\t[%f,%f,%f,%f]\n\t[%f,%f,%f,%f]\n\n", + rot[0],rot[1],rot[2],rot[3], + rot[4],rot[5],rot[6],rot[7], + rot[8],rot[9],rot[10],rot[11]); + + } else if (cmd == 'f' && selected >= 0 && selected < num) { + + if (dBodyIsEnabled(obj[selected].body)) + doFeedback = 1; + + } +} + + +// draw a geom + +void drawGeom(dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + int i; + + if (!g) + return; + if (!pos) + pos = dGeomGetPosition(g); + if (!R) + R = dGeomGetRotation(g); + + int type = dGeomGetClass(g); + if (type == dBoxClass) { + + dVector3 sides; + dGeomBoxGetLengths (g,sides); + dsDrawBox(pos,R,sides); + + } else if (type == dSphereClass) { + + dsDrawSphere(pos,R,dGeomSphereGetRadius(g)); + + } else if (type == dCapsuleClass) { + + dReal radius,length; + dGeomCapsuleGetParams(g,&radius,&length); + dsDrawCapsule(pos,R,length,radius); + + } else if (type == dConvexClass) { + +#if 0 + dsDrawConvex(pos,R,planes, + planecount, + points, + pointcount, + polygons); +#else + dsDrawConvex(pos,R, + Sphere_planes, + Sphere_planecount, + Sphere_points, + Sphere_pointcount, + Sphere_polygons); +#endif + + } else if (type == dCylinderClass) { + + dReal radius,length; + dGeomCylinderGetParams(g,&radius,&length); + dsDrawCylinder(pos,R,length,radius); + + } + + if (show_body) { + dBodyID body = dGeomGetBody(g); + if (body) { + const dReal *bodypos = dBodyGetPosition(body); + const dReal *bodyr = dBodyGetRotation(body); + dReal bodySides[3] = { 0.1, 0.1, 0.1 }; + dsSetColorAlpha(0,1,0,1); + dsDrawBox(bodypos,bodyr,bodySides); + } + } + + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB(g,aabb); + dVector3 bbpos; + for (i=0; i<3; i++) + bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (i=0; i<3; i++) + bbsides[i] = aabb[i*2+1] - aabb[i*2]; + dMatrix3 RI; + dRSetIdentity (RI); + dsSetColorAlpha(1,0,0,0.5); + dsDrawBox(bbpos,RI,bbsides); + } +} + + +// simulation loop + +static void simLoop(int pause) +{ + dSpaceCollide(space, 0, &nearCallback); + + if (!pause) + dWorldQuickStep(world, 0.02); + + if (write_world) { + FILE *f = fopen("state.dif","wt"); + if (f) { + dWorldExportDIF(world,f,"X"); + fclose (f); + } + write_world = 0; + } + + + if (doFeedback) { + if (fbnum>MAX_FEEDBACKNUM) + printf("joint feedback buffer overflow!\n"); + else { + dVector3 sum = {0, 0, 0}; + printf("\n"); + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define LENGTH 0.7 // chassis length +#define WIDTH 0.5 // chassis width +#define HEIGHT 0.2 // chassis height +#define RADIUS 0.18 // wheel radius +#define STARTZ 0.5 // starting height of chassis +#define CMASS 1 // chassis mass +#define WMASS 0.2 // wheel mass + +static const dVector3 yunit = { 0, 1, 0 }, zunit = { 0, 0, 1 }; + + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; +static dBodyID body[4]; +static dJointID joint[3]; // joint[0] is the front wheel +static dJointGroupID contactgroup; +static dGeomID ground; +static dSpaceID car_space; +static dGeomID box[1]; +static dGeomID sphere[3]; +static dGeomID ground_box; + + +// things that the user controls + +static dReal speed=0,steer=0; // user commands + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,n; + + // only collide things with the ground + int g1 = (o1 == ground || o1 == ground_box); + int g2 = (o2 == ground || o2 == ground_box); + if (!(g1 ^ g2)) return; + + const int N = 10; + dContact contact[N]; + n = dCollide (o1,o2,N,&contact[0].geom,sizeof(dContact)); + if (n > 0) { + for (i=0; i 0.1) v = 0.1; + if (v < -0.1) v = -0.1; + v *= 10.0; + dJointSetHinge2Param (joint[0],dParamVel,v); + dJointSetHinge2Param (joint[0],dParamFMax,0.2); + dJointSetHinge2Param (joint[0],dParamLoStop,-0.75); + dJointSetHinge2Param (joint[0],dParamHiStop,0.75); + dJointSetHinge2Param (joint[0],dParamFudgeFactor,0.1); + + dSpaceCollide (space,0,&nearCallback); + dWorldStep (world,0.05); + + // remove all contact joints + dJointGroupEmpty (contactgroup); + + } + + dsSetColor (0,1,1); + dsSetTexture (DS_WOOD); + dReal sides[3] = {LENGTH,WIDTH,HEIGHT}; + dsDrawBox (dBodyGetPosition(body[0]),dBodyGetRotation(body[0]),sides); + dsSetColor (1,1,1); + for (i=1; i<=3; i++) dsDrawCylinder (dBodyGetPosition(body[i]), + dBodyGetRotation(body[i]),0.02f,RADIUS); + + dVector3 ss; + dGeomBoxGetLengths (ground_box,ss); + dsDrawBox (dGeomGetPosition(ground_box),dGeomGetRotation(ground_box),ss); + +} + + +int main (int argc, char **argv) +{ + int i; + dMass m; + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // create world + dInitODE2(0); + world = dWorldCreate(); + space = dHashSpaceCreate (0); + contactgroup = dJointGroupCreate (0); + dWorldSetGravity (world,0,0,-0.5); + ground = dCreatePlane (space,0,0,1,0); + + // chassis body + body[0] = dBodyCreate (world); + dBodySetPosition (body[0],0,0,STARTZ); + dMassSetBox (&m,1,LENGTH,WIDTH,HEIGHT); + dMassAdjust (&m,CMASS); + dBodySetMass (body[0],&m); + box[0] = dCreateBox (0,LENGTH,WIDTH,HEIGHT); + dGeomSetBody (box[0],body[0]); + + // wheel bodies + for (i=1; i<=3; i++) { + body[i] = dBodyCreate (world); + dQuaternion q; + dQFromAxisAndAngle (q,1,0,0,M_PI*0.5); + dBodySetQuaternion (body[i],q); + dMassSetSphere (&m,1,RADIUS); + dMassAdjust (&m,WMASS); + dBodySetMass (body[i],&m); + sphere[i-1] = dCreateSphere (0,RADIUS); + dGeomSetBody (sphere[i-1],body[i]); + } + dBodySetPosition (body[1],0.5*LENGTH,0,STARTZ-HEIGHT*0.5); + dBodySetPosition (body[2],-0.5*LENGTH, WIDTH*0.5,STARTZ-HEIGHT*0.5); + dBodySetPosition (body[3],-0.5*LENGTH,-WIDTH*0.5,STARTZ-HEIGHT*0.5); + + // front and back wheel hinges + for (i=0; i<3; i++) { + joint[i] = dJointCreateHinge2 (world,0); + dJointAttach (joint[i],body[0],body[i+1]); + const dReal *a = dBodyGetPosition (body[i+1]); + dJointSetHinge2Anchor (joint[i],a[0],a[1],a[2]); + dJointSetHinge2Axes (joint[i], zunit, yunit); + } + + // set joint suspension + for (i=0; i<3; i++) { + dJointSetHinge2Param (joint[i],dParamSuspensionERP,0.4); + dJointSetHinge2Param (joint[i],dParamSuspensionCFM,0.8); + } + + // lock back wheels along the steering axis + for (i=1; i<3; i++) { + // set stops to make sure wheels always stay in alignment + dJointSetHinge2Param (joint[i],dParamLoStop,0); + dJointSetHinge2Param (joint[i],dParamHiStop,0); + // the following alternative method is no good as the wheels may get out + // of alignment: + // dJointSetHinge2Param (joint[i],dParamVel,0); + // dJointSetHinge2Param (joint[i],dParamFMax,dInfinity); + } + + // create car space and add it to the top level space + car_space = dSimpleSpaceCreate (space); + dSpaceSetCleanup (car_space,0); + dSpaceAdd (car_space,box[0]); + dSpaceAdd (car_space,sphere[0]); + dSpaceAdd (car_space,sphere[1]); + dSpaceAdd (car_space,sphere[2]); + + // environment + ground_box = dCreateBox (space,2,1.5,1); + dMatrix3 R; + dRFromAxisAndAngle (R,0,1,0,-0.15); + dGeomSetPosition (ground_box,2,0,-0.34); + dGeomSetRotation (ground_box,R); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dGeomDestroy (box[0]); + dGeomDestroy (sphere[0]); + dGeomDestroy (sphere[1]); + dGeomDestroy (sphere[2]); + dJointGroupDestroy (contactgroup); + dSpaceDestroy (space); + dWorldDestroy (world); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_cards.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_cards.cpp new file mode 100644 index 0000000..17284ba --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_cards.cpp @@ -0,0 +1,237 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + +static int levels = 5; +static int ncards = 0; + +static dSpaceID space; +static dWorldID world; +static dJointGroupID contactgroup; + +struct Card { + dBodyID body; + dGeomID geom; + static const dReal sides[3]; + + Card() + { + body = dBodyCreate(world); + geom = dCreateBox(space, sides[0], sides[1], sides[2]); + dGeomSetBody(geom, body); + dGeomSetData(geom, this); + dMass mass; + mass.setBox(1, sides[0], sides[1], sides[2]); + dBodySetMass(body, &mass); + } + + ~Card() + { + dBodyDestroy(body); + dGeomDestroy(geom); + } + + void draw() const + { + dsDrawBox(dBodyGetPosition(body), + dBodyGetRotation(body), sides); + } +}; +static const dReal cwidth=.5, cthikness=.02, clength=1; +const dReal Card::sides[3] = { cwidth, cthikness, clength }; + + +std::vector cards; + +int getncards(int levels) +{ + return (3*levels*levels + levels) / 2; +} + +void place_cards() +{ + ncards = getncards(levels); + // destroy removed cards (if any) + int oldcards = cards.size(); + for (int i=ncards; ibody, + 0, + -n*hstep + hstep*i, + height + ); + if (i%2) + dBodySetRotation(cards[c]->body, left); + else + dBodySetRotation(cards[c]->body, right); + } + + if (n==1) // top of the house + break; + + // horizontal cards + for (int i=0; ibody, + 0, + -(n-1 - (clength-hstep)/2)*hstep + 2*hstep*i, + height + vstep/2); + dBodySetRotation(cards[c]->body, hrot); + } + } + +} + + +void start() +{ + puts("Controls:"); + puts(" SPACE - reposition cards"); + puts(" - - one less level"); + puts(" = - one more level"); +} + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + const int MAX_CONTACTS = 8; + dContact contact[MAX_CONTACTS]; + + int numc = dCollide (o1, o2, MAX_CONTACTS, + &contact[0].geom, + sizeof(dContact)); + + for (int i=0; idraw(); + } + +} + +void command(int c) +{ + switch (c) { + case '=': + levels++; + place_cards(); + break; + case '-': + levels--; + if (levels <= 0) + levels++; + place_cards(); + break; + case ' ': + place_cards(); + break; + } +} + +int main(int argc, char **argv) +{ + dInitODE(); + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + + world = dWorldCreate(); + dWorldSetGravity(world, 0, 0, -0.5); + dWorldSetQuickStepNumIterations(world, 50); // <-- increase for more stability + + space = dSimpleSpaceCreate(0); + contactgroup = dJointGroupCreate(0); + dGeomID ground = dCreatePlane(space, 0, 0, 1, 0); + + place_cards(); + + // run simulation + dsSimulationLoop (argc, argv, 640, 480, &fn); + + levels = 0; + place_cards(); + + dJointGroupDestroy(contactgroup); + dWorldDestroy(world); + dGeomDestroy(ground); + dSpaceDestroy(space); + + dCloseODE(); +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_chain1.c b/thirdparty/ode-0.16.5/ode/demo/demo_chain1.c new file mode 100644 index 0000000..a6d7b38 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_chain1.c @@ -0,0 +1,171 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* exercise the C interface */ + +#include +#include "ode/ode.h" +#include "drawstuff/drawstuff.h" +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) /* for VC++, no precision loss complaints */ +#endif + +/* select correct drawing functions */ + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +/* some constants */ + +#define NUM 10 /* number of boxes */ +#define SIDE (0.2) /* side length of a box */ +#define MASS (1.0) /* mass of a box */ +#define RADIUS (0.1732f) /* sphere radius */ + + +/* dynamics and collision objects */ + +static dWorldID world; +static dSpaceID space; +static dBodyID body[NUM]; +static dJointID joint[NUM-1]; +static dJointGroupID contactgroup; +static dGeomID sphere[NUM]; + + +/* this is called by dSpaceCollide when two objects in space are + * potentially colliding. + */ + +static void nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + /* exit without doing anything if the two bodies are connected by a joint */ + dBodyID b1,b2; + dContact contact; + (void)data; + + b1 = dGeomGetBody(o1); + b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnected (b1,b2)) return; + + contact.surface.mode = 0; + contact.surface.mu = 0.1; + contact.surface.mu2 = 0; + if (dCollide (o1,o2,1,&contact.geom,sizeof(dContactGeom))) { + dJointID c = dJointCreateContact (world,contactgroup,&contact); + dJointAttach (c,b1,b2); + } +} + + +/* start simulation - set viewpoint */ + +static void start() +{ + static float xyz[3] = {2.1640f,-1.3079f,1.7600f}; + static float hpr[3] = {125.5000f,-17.0000f,0.0000f}; + + dAllocateODEDataForThread(dAllocateMaskAll); + dsSetViewpoint (xyz,hpr); +} + + +/* simulation loop */ + +static void simLoop (int pause) +{ + int i; + if (!pause) { + static double angle = 0; + angle += 0.05; + dBodyAddForce (body[NUM-1],0,0,1.5*(sin(angle)+1.0)); + + dSpaceCollide (space,0,&nearCallback); + dWorldStep (world,0.05); + + /* remove all contact joints */ + dJointGroupEmpty (contactgroup); + } + + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define NUM 10 // number of boxes +#define SIDE (0.2) // side length of a box +#define MASS (1.0) // mass of a box +#define RADIUS (0.1732f) // sphere radius + +//using namespace ode; + +// dynamics and collision objects + +static dWorld world; +static dSimpleSpace space (0); +static dBody body[NUM]; +static dBallJoint joint[NUM-1]; +static dJointGroup contactgroup; +static dBox box[NUM]; + + +// this is called by space.collide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnected (b1,b2)) return; + + // @@@ it's still more convenient to use the C interface here. + + dContact contact; + contact.surface.mode = 0; + contact.surface.mu = dInfinity; + if (dCollide (o1,o2,1,&contact.geom,sizeof(dContactGeom))) { + dJointID c = dJointCreateContact (world.id(),contactgroup.id(),&contact); + dJointAttach (c,b1,b2); + } +} + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {2.1640f,-1.3079f,1.7600f}; + static float hpr[3] = {125.5000f,-17.0000f,0.0000f}; + dsSetViewpoint (xyz,hpr); +} + + +// simulation loop + +static void simLoop (int pause) +{ + if (!pause) { + static double angle = 0; + angle += 0.05; + body[NUM-1].addForce (0,0,1.5*(sin(angle)+1.0)); + + space.collide (0,&nearCallback); + world.step (0.05); + + // remove all contact joints + contactgroup.empty(); + } + + dReal sides[3] = {SIDE,SIDE,SIDE}; + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawSphere dsDrawSphereD +#define dsDrawBox dsDrawBoxD +#define dsDrawLine dsDrawLineD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawCylinder dsDrawCylinderD +#endif + +//**************************************************************************** +// test infrastructure, including constants and macros + +#define TEST_REPS1 1000 // run each test this many times (first batch) +#define TEST_REPS2 10000 // run each test this many times (second batch) +const dReal tol = 1e-8; // tolerance used for numerical checks +#define MAX_TESTS 1000 // maximum number of test slots +#define Z_OFFSET 2 // z offset for drawing (to get above ground) + +//using namespace ode; + +// test function. returns 1 if the test passed or 0 if it failed +typedef int test_function_t(); + +struct TestSlot { + int number; // number of test + const char *name; // name of test + int failcount; + test_function_t *test_fn; + int last_failed_line; +}; +TestSlot testslot[MAX_TESTS]; + + +// globals used by the test functions +int graphical_test=0; // show graphical results of this test, 0=none +int current_test; // currently execiting test +int draw_all_objects_called; + + +#define MAKE_TEST(number,function) \ + if (testslot[number].name) dDebug (0,"test number already used"); \ + if (number <= 0 || number >= MAX_TESTS) dDebug (0,"bad test number"); \ + testslot[number].name = # function; \ + testslot[number].test_fn = function; + +#define FAILED() { if (graphical_test==0) { \ + testslot[current_test].last_failed_line=__LINE__; return 0; } } +#define PASSED() { return 1; } + +//**************************************************************************** +// globals + +/* int dBoxBox (const dVector3 p1, const dMatrix3 R1, + const dVector3 side1, const dVector3 p2, + const dMatrix3 R2, const dVector3 side2, + dVector3 normal, dReal *depth, int *code, + int maxc, dContactGeom *contact, int skip); */ + +void dLineClosestApproach (const dVector3 pa, const dVector3 ua, + const dVector3 pb, const dVector3 ub, + dReal *alpha, dReal *beta); + +//**************************************************************************** +// draw all objects in a space, and draw all the collision contact points + +void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,j,n; + const int N = 100; + dContactGeom contact[N]; + + if (dGeomGetClass (o2) == dRayClass) { + n = dCollide (o2,o1,N,&contact[0],sizeof(dContactGeom)); + } + else { + n = dCollide (o1,o2,N,&contact[0],sizeof(dContactGeom)); + } + if (n > 0) { + dMatrix3 RI; + dRSetIdentity (RI); + const dReal ss[3] = {0.01,0.01,0.01}; + for (i=0; i tol) FAILED(); + + // ********** test point on surface has depth 0 + + for (j=0; j<3; j++) q[j] = dRandReal()-0.5; + dNormalize3 (q); + for (j=0; j<3; j++) q[j] = q[j]*r + p[j]; + if (dFabs(dGeomSpherePointDepth (sphere,q[0],q[1],q[2])) > tol) FAILED(); + + // ********** test point at random depth + + d = (dRandReal()*2-1) * r; + for (j=0; j<3; j++) q[j] = dRandReal()-0.5; + dNormalize3 (q); + for (j=0; j<3; j++) q[j] = q[j]*(r-d) + p[j]; + if (dFabs(dGeomSpherePointDepth (sphere,q[0],q[1],q[2])-d) > tol) FAILED(); + + PASSED(); +} + + +int test_box_point_depth() +{ + int i,j; + dVector3 s,p,q,q2; // s = box sides + dMatrix3 R; + dReal ss,d; // ss = smallest side + + dSimpleSpace space(0); + dGeomID box = dCreateBox (0,1,1,1); + dSpaceAdd (space,box); + + // ********** make a random box + + for (j=0; j<3; j++) s[j] = dRandReal() + 0.1; + dGeomBoxSetLengths (box,s[0],s[1],s[2]); + dMakeRandomVector (p,3,1.0); + dGeomSetPosition (box,p[0],p[1],p[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (box,R); + + // ********** test center point has depth of smallest side + + ss = 1e9; + for (j=0; j<3; j++) if (s[j] < ss) ss = s[j]; + if (dFabs(dGeomBoxPointDepth (box,p[0],p[1],p[2]) - 0.5*ss) > tol) + FAILED(); + + // ********** test point on surface has depth 0 + + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j]; + i = dRandInt (3); + if (dRandReal() > 0.5) q[i] = 0.5*s[i]; else q[i] = -0.5*s[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + if (dFabs(dGeomBoxPointDepth (box,q2[0],q2[1],q2[2])) > tol) FAILED(); + + // ********** test points outside box have -ve depth + + for (j=0; j<3; j++) { + q[j] = 0.5*s[j] + dRandReal() + 0.01; + if (dRandReal() > 0.5) q[j] = -q[j]; + } + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + if (dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) >= 0) FAILED(); + + // ********** test points inside box have +ve depth + + for (j=0; j<3; j++) q[j] = s[j] * 0.99 * (dRandReal()-0.5); + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + if (dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) <= 0) FAILED(); + + // ********** test random depth of point aligned along axis (up to ss deep) + + i = dRandInt (3); + for (j=0; j<3; j++) q[j] = 0; + d = (dRandReal()*(ss*0.5+1)-1); + q[i] = s[i]*0.5 - d; + if (dRandReal() > 0.5) q[i] = -q[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + if (dFabs(dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) - d) >= tol) FAILED(); + + PASSED(); +} + + +int test_ccylinder_point_depth() +{ + int j; + dVector3 p,a; + dMatrix3 R; + dReal r,l,beta,x,y,d; + + dSimpleSpace space(0); + dGeomID ccyl = dCreateCapsule (0,1,1); + dSpaceAdd (space,ccyl); + + // ********** make a random ccyl + + r = dRandReal()*0.5 + 0.01; + l = dRandReal()*1 + 0.01; + dGeomCapsuleSetParams (ccyl,r,l); + dMakeRandomVector (p,3,1.0); + dGeomSetPosition (ccyl,p[0],p[1],p[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ccyl,R); + + // ********** test point on axis has depth of 'radius' + + beta = dRandReal()-0.5; + for (j=0; j<3; j++) a[j] = p[j] + l*beta*R[j*4+2]; + if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - r) >= tol) + FAILED(); + + // ********** test point on surface (excluding caps) has depth 0 + + beta = dRandReal()*2*M_PI; + x = r*sin(beta); + y = r*cos(beta); + beta = dRandReal()-0.5; + for (j=0; j<3; j++) a[j] = p[j] + x*R[j*4+0] + y*R[j*4+1] + l*beta*R[j*4+2]; + if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2])) >= tol) FAILED(); + + // ********** test point on surface of caps has depth 0 + + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + dNormalize3 (a); + if (dCalcVectorDot3_14(a,R+2) > 0) { + for (j=0; j<3; j++) a[j] = p[j] + a[j]*r + l*0.5*R[j*4+2]; + } + else { + for (j=0; j<3; j++) a[j] = p[j] + a[j]*r - l*0.5*R[j*4+2]; + } + if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2])) >= tol) FAILED(); + + // ********** test point inside ccyl has positive depth + + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + dNormalize3 (a); + beta = dRandReal()-0.5; + for (j=0; j<3; j++) a[j] = p[j] + a[j]*r*0.99 + l*beta*R[j*4+2]; + if (dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) < 0) FAILED(); + + // ********** test point depth (1) + + d = (dRandReal()*2-1) * r; + beta = dRandReal()*2*M_PI; + x = (r-d)*sin(beta); + y = (r-d)*cos(beta); + beta = dRandReal()-0.5; + for (j=0; j<3; j++) a[j] = p[j] + x*R[j*4+0] + y*R[j*4+1] + l*beta*R[j*4+2]; + if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - d) >= tol) + FAILED(); + + // ********** test point depth (2) + + d = (dRandReal()*2-1) * r; + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + dNormalize3 (a); + if (dCalcVectorDot3_14(a,R+2) > 0) { + for (j=0; j<3; j++) a[j] = p[j] + a[j]*(r-d) + l*0.5*R[j*4+2]; + } + else { + for (j=0; j<3; j++) a[j] = p[j] + a[j]*(r-d) - l*0.5*R[j*4+2]; + } + if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - d) >= tol) + FAILED(); + + PASSED(); +} + + +int test_plane_point_depth() +{ + int j; + dVector3 n,p,q,a,b; // n = plane normal + dReal d; + + dSimpleSpace space(0); + dGeomID plane = dCreatePlane (0,0,0,1,0); + dSpaceAdd (space,plane); + + // ********** make a random plane + + for (j=0; j<3; j++) n[j] = dRandReal() - 0.5; + dNormalize3 (n); + d = dRandReal() - 0.5; + dGeomPlaneSetParams (plane,n[0],n[1],n[2],d); + dPlaneSpace (n,p,q); + + // ********** test point on plane has depth 0 + + a[0] = dRandReal() - 0.5; + a[1] = dRandReal() - 0.5; + a[2] = 0; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2])) >= tol) FAILED(); + + // ********** test arbitrary depth point + + a[0] = dRandReal() - 0.5; + a[1] = dRandReal() - 0.5; + a[2] = dRandReal() - 0.5; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2]) + a[2]) >= tol) + FAILED(); + + // ********** test depth-1 point + + a[0] = dRandReal() - 0.5; + a[1] = dRandReal() - 0.5; + a[2] = -1; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2]) - 1) >= tol) FAILED(); + + PASSED(); +} + +//**************************************************************************** +// ray tests + +int test_ray_and_sphere() +{ + int j; + dContactGeom contact; + dVector3 p,q,q2,n,v1; + dMatrix3 R; + dReal r,k; + + dSimpleSpace space(0); + dGeomID ray = dCreateRay (0,0); + dGeomID sphere = dCreateSphere (0,1); + dSpaceAdd (space,ray); + dSpaceAdd (space,sphere); + + // ********** make a random sphere of radius r at position p + + r = dRandReal()+0.1; + dGeomSphereSetRadius (sphere,r); + dMakeRandomVector (p,3,1.0); + dGeomSetPosition (sphere,p[0],p[1],p[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (sphere,R); + + // ********** test zero length ray just inside sphere + + dGeomRaySetLength (ray,0); + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + for (j=0; j<3; j++) q[j] = 0.99*r * q[j] + p[j]; + dGeomSetPosition (ray,q[0],q[1],q[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ray,R); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test zero length ray just outside that sphere + + dGeomRaySetLength (ray,0); + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j]; + dGeomSetPosition (ray,q[0],q[1],q[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ray,R); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray totally contained inside the sphere + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + k = dRandReal(); + for (j=0; j<3; j++) q[j] = k*r*0.99 * q[j] + p[j]; + dMakeRandomVector (q2,3,1.0); + dNormalize3 (q2); + k = dRandReal(); + for (j=0; j<3; j++) q2[j] = k*r*0.99 * q2[j] + p[j]; + for (j=0; j<3; j++) n[j] = q2[j] - q[j]; + dNormalize3 (n); + dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,dCalcPointsDistance3(q,q2)); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray totally outside the sphere + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + do { + dMakeRandomVector (n,3,1.0); + dNormalize3 (n); + } + while (dCalcVectorDot3(n,q) < 0); // make sure normal goes away from sphere + for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j]; + dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,100); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray from outside to just above surface + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + for (j=0; j<3; j++) n[j] = -q[j]; + for (j=0; j<3; j++) q2[j] = 2*r * q[j] + p[j]; + dGeomRaySet (ray,q2[0],q2[1],q2[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,0.99*r); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray from outside to just below surface + + dGeomRaySetLength (ray,1.01*r); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + for (j=0; j<3; j++) q2[j] = r * q[j] + p[j]; + if (dCalcPointsDistance3 (contact.pos,q2) > tol) FAILED(); + + // ********** test contact point distance for random rays + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + k = dRandReal()+0.5; + for (j=0; j<3; j++) q[j] = k*r * q[j] + p[j]; + dMakeRandomVector (n,3,1.0); + dNormalize3 (n); + dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,100); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom))) { + k = dCalcPointsDistance3 (contact.pos,dGeomGetPosition(sphere)); + if (dFabs(k - r) > tol) FAILED(); + // also check normal signs + if (dCalcVectorDot3 (n,contact.normal) > 0) FAILED(); + // also check depth of contact point + if (dFabs (dGeomSpherePointDepth + (sphere,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + + draw_all_objects (space); + } + + // ********** test tangential grazing - miss + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + dPlaneSpace (q,n,v1); + for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j]; + for (j=0; j<3; j++) q[j] -= n[j]; + dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,2); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test tangential grazing - hit + + dMakeRandomVector (q,3,1.0); + dNormalize3 (q); + dPlaneSpace (q,n,v1); + for (j=0; j<3; j++) q[j] = 0.99*r * q[j] + p[j]; + for (j=0; j<3; j++) q[j] -= n[j]; + dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,2); + if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + + PASSED(); +} + + +int test_ray_and_box() +{ + int i,j; + dContactGeom contact; + dVector3 s,p,q,n,q2,q3,q4; // s = box sides + dMatrix3 R; + dReal k; + + dSimpleSpace space(0); + dGeomID ray = dCreateRay (0,0); + dGeomID box = dCreateBox (0,1,1,1); + dSpaceAdd (space,ray); + dSpaceAdd (space,box); + + // ********** make a random box + + for (j=0; j<3; j++) s[j] = dRandReal() + 0.1; + dGeomBoxSetLengths (box,s[0],s[1],s[2]); + dMakeRandomVector (p,3,1.0); + dGeomSetPosition (box,p[0],p[1],p[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (box,R); + + // ********** test zero length ray just inside box + + dGeomRaySetLength (ray,0); + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j]; + i = dRandInt (3); + if (dRandReal() > 0.5) q[i] = 0.99*0.5*s[i]; else q[i] = -0.99*0.5*s[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + dGeomSetPosition (ray,q2[0],q2[1],q2[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ray,R); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test zero length ray just outside box + + dGeomRaySetLength (ray,0); + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j]; + i = dRandInt (3); + if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + dGeomSetPosition (ray,q2[0],q2[1],q2[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ray,R); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray totally contained inside the box + + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*0.99*s[j]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + for (j=0; j<3; j++) q3[j] = (dRandReal()-0.5)*0.99*s[j]; + dMultiply0 (q4,dGeomGetRotation(box),q3,3,3,1); + for (j=0; j<3; j++) q4[j] += p[j]; + for (j=0; j<3; j++) n[j] = q4[j] - q2[j]; + dNormalize3 (n); + dGeomRaySet (ray,q2[0],q2[1],q2[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,dCalcPointsDistance3(q2,q4)); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray totally outside the box + + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j]; + i = dRandInt (3); + if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q3[j] = q2[j] + p[j]; + dNormalize3 (q2); + dGeomRaySet (ray,q3[0],q3[1],q3[2],q2[0],q2[1],q2[2]); + dGeomRaySetLength (ray,10); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray from outside to just above surface + + for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j]; + i = dRandInt (3); + if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q3[j] = 2*q2[j] + p[j]; + k = dSqrt(q2[0]*q2[0] + q2[1]*q2[1] + q2[2]*q2[2]); + for (j=0; j<3; j++) q2[j] = -q2[j]; + dGeomRaySet (ray,q3[0],q3[1],q3[2],q2[0],q2[1],q2[2]); + dGeomRaySetLength (ray,k*0.99); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray from outside to just below surface + + dGeomRaySetLength (ray,k*1.01); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + + // ********** test contact point position for random rays + + for (j=0; j<3; j++) q[j] = dRandReal()*s[j]; + dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1); + for (j=0; j<3; j++) q2[j] += p[j]; + for (j=0; j<3; j++) q3[j] = dRandReal()-0.5; + dNormalize3 (q3); + dGeomRaySet (ray,q2[0],q2[1],q2[2],q3[0],q3[1],q3[2]); + dGeomRaySetLength (ray,10); + if (dCollide (ray,box,1,&contact,sizeof(dContactGeom))) { + // check depth of contact point + if (dFabs (dGeomBoxPointDepth + (box,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + // check position of contact point + for (j=0; j<3; j++) contact.pos[j] -= p[j]; + dMultiply1 (q,dGeomGetRotation(box),contact.pos,3,3,1); + if ( dFabs(dFabs (q[0]) - 0.5*s[0]) > tol && + dFabs(dFabs (q[1]) - 0.5*s[1]) > tol && + dFabs(dFabs (q[2]) - 0.5*s[2]) > tol) { + FAILED(); + } + // also check normal signs + if (dCalcVectorDot3 (q3,contact.normal) > 0) FAILED(); + + draw_all_objects (space); + } + + PASSED(); +} + + +int test_ray_and_ccylinder() +{ + int j; + dContactGeom contact; + dVector3 p,a,b,n; + dMatrix3 R; + dReal r,l,k,x,y; + + dSimpleSpace space(0); + dGeomID ray = dCreateRay (0,0); + dGeomID ccyl = dCreateCapsule (0,1,1); + dSpaceAdd (space,ray); + dSpaceAdd (space,ccyl); + + // ********** make a random capped cylinder + + r = dRandReal()*0.5 + 0.01; + l = dRandReal()*1 + 0.01; + dGeomCapsuleSetParams (ccyl,r,l); + dMakeRandomVector (p,3,1.0); + dGeomSetPosition (ccyl,p[0],p[1],p[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ccyl,R); + + // ********** test ray completely within ccyl + + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + dNormalize3 (a); + k = (dRandReal()-0.5)*l; + for (j=0; j<3; j++) a[j] = p[j] + r*0.99*a[j] + k*0.99*R[j*4+2]; + for (j=0; j<3; j++) b[j] = dRandReal()-0.5; + dNormalize3 (b); + k = (dRandReal()-0.5)*l; + for (j=0; j<3; j++) b[j] = p[j] + r*0.99*b[j] + k*0.99*R[j*4+2]; + dGeomRaySetLength (ray,dCalcPointsDistance3(a,b)); + for (j=0; j<3; j++) b[j] -= a[j]; + dNormalize3 (b); + dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]); + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray outside ccyl that just misses (between caps) + + k = dRandReal()*2*M_PI; + x = sin(k); + y = cos(k); + for (j=0; j<3; j++) a[j] = x*R[j*4+0] + y*R[j*4+1]; + k = (dRandReal()-0.5)*l; + for (j=0; j<3; j++) b[j] = -a[j]*r*2 + k*R[j*4+2] + p[j]; + dGeomRaySet (ray,b[0],b[1],b[2],a[0],a[1],a[2]); + dGeomRaySetLength (ray,r*0.99); + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray outside ccyl that just hits (between caps) + + dGeomRaySetLength (ray,r*1.01); + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + // check depth of contact point + if (dFabs (dGeomCapsulePointDepth + (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + + // ********** test ray outside ccyl that just misses (caps) + + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + dNormalize3 (a); + if (dCalcVectorDot3_14(a,R+2) < 0) { + for (j=0; j<3; j++) b[j] = p[j] - a[j]*2*r + l*0.5*R[j*4+2]; + } + else { + for (j=0; j<3; j++) b[j] = p[j] - a[j]*2*r - l*0.5*R[j*4+2]; + } + dGeomRaySet (ray,b[0],b[1],b[2],a[0],a[1],a[2]); + dGeomRaySetLength (ray,r*0.99); + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray outside ccyl that just hits (caps) + + dGeomRaySetLength (ray,r*1.01); + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + // check depth of contact point + if (dFabs (dGeomCapsulePointDepth + (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + + // ********** test random rays + + for (j=0; j<3; j++) a[j] = dRandReal()-0.5; + for (j=0; j<3; j++) n[j] = dRandReal()-0.5; + dNormalize3 (n); + dGeomRaySet (ray,a[0],a[1],a[2],n[0],n[1],n[2]); + dGeomRaySetLength (ray,10); + + if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom))) { + // check depth of contact point + if (dFabs (dGeomCapsulePointDepth + (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + + // check normal signs + if (dCalcVectorDot3 (n,contact.normal) > 0) FAILED(); + + draw_all_objects (space); + } + + PASSED(); +} + +/* + Test rays within the cylinder + -completely inside + -exiting through side + -exiting through cap + -exiting through corner + Test rays outside the cylinder +*/ +int test_ray_and_cylinder() +{ + dVector3 a,b; + + dSimpleSpace space(0); + dGeomID ray = dCreateRay(space,4); + + // The first thing that happens is the ray is + // rotated into cylinder coordinates. We'll trust that's + // done right. The major axis is in the z-dir. + + + // Random tests + /*b[0]=4*dRandReal()-2; + b[1]=4*dRandReal()-2; + b[2]=4*dRandReal()-2; + a[0]=2*dRandReal()-1; + a[1]=2*dRandReal()-1; + a[2]=2*dRandReal()-1;*/ + + // Inside out + b[0]=dRandReal()-0.5; + b[1]=dRandReal()-0.5; + b[2]=dRandReal()-0.5; + a[0]=2*dRandReal()-1; + a[1]=2*dRandReal()-1; + a[2]=2*dRandReal()-1; + + // Outside in + /*b[0]=4*dRandReal()-2; + b[1]=4*dRandReal()-2; + b[2]=4*dRandReal()-2; + a[0]=-b[0]; + a[1]=-b[1]; + a[2]=-b[2];*/ + + + dGeomRaySet (ray,b[0],b[1],b[2],a[0],a[1],a[2]); + // This is just for visual inspection right now. + //if (dCollide (ray,cyl,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + + draw_all_objects (space); + + PASSED(); +} + + +int test_ray_and_plane() +{ + int j; + dContactGeom contact; + dVector3 n,p,q,a,b,g,h; // n,d = plane parameters + dMatrix3 R; + dReal d; + + dSimpleSpace space(0); + dGeomID ray = dCreateRay (0,0); + dGeomID plane = dCreatePlane (0,0,0,1,0); + dSpaceAdd (space,ray); + dSpaceAdd (space,plane); + + // ********** make a random plane + + for (j=0; j<3; j++) n[j] = dRandReal() - 0.5; + dNormalize3 (n); + d = dRandReal() - 0.5; + dGeomPlaneSetParams (plane,n[0],n[1],n[2],d); + dPlaneSpace (n,p,q); + + // ********** test finite length ray below plane + + dGeomRaySetLength (ray,0.09); + a[0] = dRandReal()-0.5; + a[1] = dRandReal()-0.5; + a[2] = -dRandReal()*0.5 - 0.1; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + dGeomSetPosition (ray,b[0],b[1],b[2]); + dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1, + dRandReal()*2-1,dRandReal()*10-5); + dGeomSetRotation (ray,R); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray above plane + + a[0] = dRandReal()-0.5; + a[1] = dRandReal()-0.5; + a[2] = dRandReal()*0.5 + 0.01; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + g[0] = dRandReal()-0.5; + g[1] = dRandReal()-0.5; + g[2] = dRandReal() + 0.01; + for (j=0; j<3; j++) h[j] = g[0]*p[j] + g[1]*q[j] + g[2]*n[j]; + dNormalize3 (h); + dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]); + dGeomRaySetLength (ray,10); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test finite length ray that intersects plane + + a[0] = dRandReal()-0.5; + a[1] = dRandReal()-0.5; + a[2] = dRandReal()-0.5; + for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j]; + g[0] = dRandReal()-0.5; + g[1] = dRandReal()-0.5; + g[2] = dRandReal()-0.5; + for (j=0; j<3; j++) h[j] = g[0]*p[j] + g[1]*q[j] + g[2]*n[j]; + dNormalize3 (h); + dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]); + dGeomRaySetLength (ray,10); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom))) { + // test that contact is on plane surface + if (dFabs (dCalcVectorDot3(contact.pos,n) - d) > tol) FAILED(); + // also check normal signs + if (dCalcVectorDot3 (h,contact.normal) > 0) FAILED(); + // also check contact point depth + if (dFabs (dGeomPlanePointDepth + (plane,contact.pos[0],contact.pos[1],contact.pos[2])) > tol) + FAILED(); + + draw_all_objects (space); + } + + // ********** test ray that just misses + + for (j=0; j<3; j++) b[j] = (1+d)*n[j]; + for (j=0; j<3; j++) h[j] = -n[j]; + dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]); + dGeomRaySetLength (ray,0.99); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED(); + + // ********** test ray that just hits + + dGeomRaySetLength (ray,1.01); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + + // ********** test polarity with typical ground plane + + dGeomPlaneSetParams (plane,0,0,1,0); + for (j=0; j<3; j++) a[j] = 0.1; + for (j=0; j<3; j++) b[j] = 0; + a[2] = 1; + b[2] = -1; + dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]); + dGeomRaySetLength (ray,2); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + if (dFabs (contact.depth - 1) > tol) FAILED(); + a[2] = -1; + b[2] = 1; + dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]); + if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED(); + if (dFabs (contact.depth - 1) > tol) FAILED(); + + PASSED(); +} + +//**************************************************************************** +// a really inefficient, but hopefully correct implementation of +// dBoxTouchesBox(), that does 144 edge-face tests. + +// return 1 if edge v1 -> v2 hits the rectangle described by p1,p2,p3 + +static int edgeIntersectsRect (dVector3 v1, dVector3 v2, + dVector3 p1, dVector3 p2, dVector3 p3) +{ + int k; + dVector3 u1, u2, n, tmp; + + for (k=0; k < 3; k++) u1[k] = p3[k] - p1[k]; + for (k=0; k < 3; k++) u2[k] = p2[k] - p1[k]; + + dReal d1 = dSqrt(dCalcVectorDot3(u1, u1)); + dReal d2 = dSqrt(dCalcVectorDot3(u2, u2)); + dNormalize3(u1); + dNormalize3(u2); + + dReal error; +#ifdef dSINGLE + const dReal uEpsilon = 1e-5, pEpsilon = 1e-6, tmpEpsilon = 1.5e-4; +#else + const dReal uEpsilon = 1e-6, pEpsilon = 1e-8, tmpEpsilon = 1e-6; +#endif + + error = dFabs(dCalcVectorDot3(u1, u2)); + if (error > uEpsilon) dDebug(0, "bad u1/u2"); + + dCalcVectorCross3(n, u1, u2); + + for (k=0; k < 3; k++) tmp[k] = v2[k] - v1[k]; + + dReal d = -dCalcVectorDot3(n, p1); + + error = dFabs(dCalcVectorDot3(n, p1) + d); + if (error > pEpsilon) dDebug(0, "bad n wrt p1"); + + error = dFabs(dCalcVectorDot3(n, p2) + d); + if (error > pEpsilon) dDebug(0, "bad n wrt p2"); + + error = dFabs(dCalcVectorDot3(n, p3) + d); + if (error > pEpsilon) dDebug(0, "bad n wrt p3"); + + dReal alpha = -(d + dCalcVectorDot3(n, v1)) / dCalcVectorDot3(n, tmp); + for (k=0; k < 3; k++) tmp[k] = v1[k] + alpha * (v2[k] - v1[k]); + + error = dFabs(dCalcVectorDot3(n, tmp) + d); + if (error > tmpEpsilon) dDebug(0, "bad tmp"); + + if (alpha < 0) return 0; + if (alpha > 1) return 0; + + for (k=0; k < 3; k++) tmp[k] -= p1[k]; + dReal a1 = dCalcVectorDot3(u1, tmp); + dReal a2 = dCalcVectorDot3(u2, tmp); + if (a1 < 0 || a2 < 0 || a1 > d1 || a2 > d2) return 0; + + return 1; +} + + +// return 1 if box 1 is completely inside box 2 + +static int box1inside2 (const dVector3 p1, const dMatrix3 R1, + const dVector3 side1, const dVector3 p2, + const dMatrix3 R2, const dVector3 side2) +{ + for (int i=-1; i<=1; i+=2) { + for (int j=-1; j<=1; j+=2) { + for (int k=-1; k<=1; k+=2) { + dVector3 v,vv; + v[0] = i*0.5*side1[0]; + v[1] = j*0.5*side1[1]; + v[2] = k*0.5*side1[2]; + dMultiply0_331 (vv,R1,v); + vv[0] += p1[0] - p2[0]; + vv[1] += p1[1] - p2[1]; + vv[2] += p1[2] - p2[2]; + for (int axis=0; axis < 3; axis++) { + dReal z = dCalcVectorDot3_14(vv,R2+axis); + if (z < (-side2[axis]*0.5) || z > (side2[axis]*0.5)) return 0; + } + } + } + } + return 1; +} + + +// test if any edge from box 1 hits a face from box 2 + +static int testBoxesTouch2 (const dVector3 p1, const dMatrix3 R1, + const dVector3 side1, const dVector3 p2, + const dMatrix3 R2, const dVector3 side2) +{ + int j,k,j1,j2; + + // for 6 faces from box 2 + for (int fd=0; fd<3; fd++) { // direction for face + + for (int fo=0; fo<2; fo++) { // offset of face + // get four points on the face. first get 2 indexes that are not fd + int k1=0,k2=0; + if (fd==0) { k1 = 1; k2 = 2; } + if (fd==1) { k1 = 0; k2 = 2; } + if (fd==2) { k1 = 0; k2 = 1; } + dVector3 fp[4],tmp; + k=0; + for (j1=-1; j1<=1; j1+=2) { + for (j2=-1; j2<=1; j2+=2) { + fp[k][k1] = j1; + fp[k][k2] = j2; + fp[k][fd] = fo*2-1; + k++; + } + } + for (j=0; j<4; j++) { + for (k=0; k<3; k++) fp[j][k] *= 0.5*side2[k]; + dMultiply0_331 (tmp,R2,fp[j]); + for (k=0; k<3; k++) fp[j][k] = tmp[k] + p2[k]; + } + + // for 8 vertices + dReal v1[3]; + for (v1[0]=-1; v1[0] <= 1; v1[0] += 2) { + for (v1[1]=-1; v1[1] <= 1; v1[1] += 2) { + for (v1[2]=-1; v1[2] <= 1; v1[2] += 2) { + // for all possible +ve leading edges from those vertices + for (int ei=0; ei < 3; ei ++) { + if (v1[ei] < 0) { + // get vertex1 -> vertex2 = an edge from box 1 + dVector3 vv1,vv2; + for (k=0; k<3; k++) vv1[k] = v1[k] * 0.5*side1[k]; + for (k=0; k<3; k++) vv2[k] = (v1[k] + (k==ei)*2)*0.5*side1[k]; + dVector3 vertex1,vertex2; + dMultiply0_331 (vertex1,R1,vv1); + dMultiply0_331 (vertex2,R1,vv2); + for (k=0; k<3; k++) vertex1[k] += p1[k]; + for (k=0; k<3; k++) vertex2[k] += p1[k]; + + // see if vertex1 -> vertex2 interesects face + if (edgeIntersectsRect (vertex1,vertex2,fp[0],fp[1],fp[2])) + return 1; + } + } + } + } + } + } + } + + if (box1inside2 (p1,R1,side1,p2,R2,side2)) return 1; + if (box1inside2 (p2,R2,side2,p1,R1,side1)) return 1; + + return 0; +} + +//**************************************************************************** +// dBoxTouchesBox() test + +int test_dBoxTouchesBox() +{ + int k,bt1,bt2; + dVector3 p1,p2,side1,side2; + dMatrix3 R1,R2; + + dSimpleSpace space(0); + dGeomID box1 = dCreateBox (0,1,1,1); + dSpaceAdd (space,box1); + dGeomID box2 = dCreateBox (0,1,1,1); + dSpaceAdd (space,box2); + + dMakeRandomVector (p1,3,0.5); + dMakeRandomVector (p2,3,0.5); + for (k=0; k<3; k++) side1[k] = dRandReal() + 0.01; + for (k=0; k<3; k++) side2[k] = dRandReal() + 0.01; + dRFromAxisAndAngle (R1,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + dRFromAxisAndAngle (R2,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + + dGeomBoxSetLengths (box1,side1[0],side1[1],side1[2]); + dGeomBoxSetLengths (box2,side2[0],side2[1],side2[2]); + dGeomSetPosition (box1,p1[0],p1[1],p1[2]); + dGeomSetRotation (box1,R1); + dGeomSetPosition (box2,p2[0],p2[1],p2[2]); + dGeomSetRotation (box2,R2); + draw_all_objects (space); + + int t1 = testBoxesTouch2 (p1,R1,side1,p2,R2,side2); + int t2 = testBoxesTouch2 (p2,R2,side2,p1,R1,side1); + bt1 = t1 || t2; + bt2 = dBoxTouchesBox (p1,R1,side1,p2,R2,side2); + + if (bt1 != bt2) FAILED(); + + /* + // some more debugging info if necessary + if (bt1 && bt2) printf ("agree - boxes touch\n"); + if (!bt1 && !bt2) printf ("agree - boxes don't touch\n"); + if (bt1 && !bt2) printf ("disagree - boxes touch but dBoxTouchesBox " + "says no\n"); + if (!bt1 && bt2) printf ("disagree - boxes don't touch but dBoxTouchesBox " + "says yes\n"); + */ + + PASSED(); +} + +//**************************************************************************** +// test box-box collision + +int test_dBoxBox() +{ + int k,bt; + dVector3 p1,p2,side1,side2,normal,normal2; + dMatrix3 R1,R2; + dReal depth,depth2; + int code; + dContactGeom contact[48]; + + dSimpleSpace space(0); + dGeomID box1 = dCreateBox (0,1,1,1); + dSpaceAdd (space,box1); + dGeomID box2 = dCreateBox (0,1,1,1); + dSpaceAdd (space,box2); + + dMakeRandomVector (p1,3,0.5); + dMakeRandomVector (p2,3,0.5); + for (k=0; k<3; k++) side1[k] = dRandReal() + 0.01; + for (k=0; k<3; k++) side2[k] = dRandReal() + 0.01; + + dRFromAxisAndAngle (R1,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + dRFromAxisAndAngle (R2,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + + // dRSetIdentity (R1); // we can also try this + // dRSetIdentity (R2); + + dGeomBoxSetLengths (box1,side1[0],side1[1],side1[2]); + dGeomBoxSetLengths (box2,side2[0],side2[1],side2[2]); + dGeomSetPosition (box1,p1[0],p1[1],p1[2]); + dGeomSetRotation (box1,R1); + dGeomSetPosition (box2,p2[0],p2[1],p2[2]); + dGeomSetRotation (box2,R2); + + code = 0; + depth = 0; + bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal,&depth,&code,8,contact, + sizeof(dContactGeom)); + if (bt==1) { + p2[0] += normal[0] * 0.96 * depth; + p2[1] += normal[1] * 0.96 * depth; + p2[2] += normal[2] * 0.96 * depth; + bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal2,&depth2,&code,8,contact, + sizeof(dContactGeom)); + + /* + dGeomSetPosition (box2,p2[0],p2[1],p2[2]); + draw_all_objects (space); + */ + + if (bt != 1) { + FAILED(); + dGeomSetPosition (box2,p2[0],p2[1],p2[2]); + draw_all_objects (space); + } + + p2[0] += normal[0] * 0.08 * depth; + p2[1] += normal[1] * 0.08 * depth; + p2[2] += normal[2] * 0.08 * depth; + bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal2,&depth2,&code,8,contact, + sizeof(dContactGeom)); + if (bt != 0) FAILED(); + + // dGeomSetPosition (box2,p2[0],p2[1],p2[2]); + // draw_all_objects (space); + } + + // printf ("code=%2d depth=%.4f ",code,depth); + + PASSED(); +} + +//**************************************************************************** +// graphics + +int space_pressed = 0; + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {2.4807,-1.8023,2.7600}; + static float hpr[3] = {141.5000,-18.5000,0.0000}; + dsSetViewpoint (xyz,hpr); +} + + +// called when a key pressed + +static void command (int cmd) +{ + if (cmd == ' ') space_pressed = 1; +} + + +// simulation loop + +static void simLoop (int) +{ + do { + draw_all_objects_called = 0; + unsigned long seed = dRandGetSeed(); + testslot[graphical_test].test_fn(); + if (draw_all_objects_called) { + if (space_pressed) space_pressed = 0; else dRandSetSeed (seed); + } + } + while (!draw_all_objects_called); +} + +//**************************************************************************** +// do all the tests + +void do_tests (int argc, char **argv) +{ + int i,j; + + // process command line arguments + if (argc >= 2) { + graphical_test = atoi (argv[1]); + } + + if (graphical_test) { + // do one test gaphically and interactively + + if (graphical_test < 1 || graphical_test >= MAX_TESTS || + !testslot[graphical_test].name) { + dError (0,"invalid test number"); + } + + printf ("performing test: %s\n",testslot[graphical_test].name); + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dsSetSphereQuality (3); + dsSetCapsuleQuality (8); + dsSimulationLoop (argc,argv,1280,900,&fn); + } + else { + // do all tests noninteractively + + for (i=0; ifailcount = 0; + int total_reps=0; + for (int batch=0; batch<2; batch++) { + int reps = (batch==0) ? TEST_REPS1 : TEST_REPS2; + total_reps += reps; + printf ("testing batch %d (%d reps)...\n",batch+1,reps); + + // run tests + for (j=0; jnumber; + if (ts[i]->test_fn() != 1) ts[i]->failcount++; + } + } + + // check for failures + int total_fail_count=0; + for (i=0; ifailcount; + if (total_fail_count) break; + } + + // print results + for (i=0; inumber,ts[i]->name); + if (ts[i]->failcount) { + printf ("FAILED (%.2f%%) at line %d\n", + double(ts[i]->failcount)/double(total_reps)*100.0, + ts[i]->last_failed_line); + } + else { + printf ("ok\n"); + } + } + } +} + +//**************************************************************************** + +int main (int argc, char **argv) +{ + // setup all tests + + memset (testslot,0,sizeof(testslot)); + dInitODE2(0); + + MAKE_TEST(1,test_sphere_point_depth); + MAKE_TEST(2,test_box_point_depth); + MAKE_TEST(3,test_ccylinder_point_depth); + MAKE_TEST(4,test_plane_point_depth); + + MAKE_TEST(10,test_ray_and_sphere); + MAKE_TEST(11,test_ray_and_box); + MAKE_TEST(12,test_ray_and_ccylinder); + MAKE_TEST(13,test_ray_and_plane); + MAKE_TEST(14,test_ray_and_cylinder); + + MAKE_TEST(100,test_dBoxTouchesBox); + MAKE_TEST(101,test_dBoxBox); + + do_tests (argc,argv); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_convex.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_convex.cpp new file mode 100644 index 0000000..eea5c6e --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_convex.cpp @@ -0,0 +1,307 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// Convex demo. +// Serves as a test for the convex geometry. +// By Bram Stolk. + +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include "texturepath.h" + +#include "halton235_geom.h" + +#ifdef dDOUBLE +# define dsDrawConvex dsDrawConvexD +# define dsDrawLine dsDrawLineD +#endif + + +#ifdef _MSC_VER +# pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + + +// Height at which we drop the composite block. +const dReal H=4.20; + +static dWorldID world; +static dSpaceID space; + +static dBodyID mbody; + +static dBodyID hbody[ halton_numc ]; +static dGeomID hgeom[ halton_numc ]; + +static dJointGroupID contactgroup; + +static bool drawpos=false; +static bool solidkernel=false; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback(void *data, dGeomID o1, dGeomID o2) +{ + assert(o1); + assert(o2); + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + // colliding a space with something + dSpaceCollide2(o1,o2,data,&nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + return; + } + + const int N = 32; + dContact contact[N]; + int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact)); + if (n > 0) + { + for (int i=0; i 8 ? 8 : nrofsteps; + + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define LENGTH 3.5 // chassis length +#define WIDTH 2.5 // chassis width +#define HEIGHT 1.0 // chassis height +#define RADIUS 0.5 // wheel radius +#define STARTZ 1.0 // starting height of chassis +#define CMASS 1 // chassis mass +#define WMASS 1 // wheel mass +#define COMOFFSET -5 // center of mass offset +#define WALLMASS 1 // wall box mass +#define BALLMASS 1 // ball mass +#define FMAX 25 // car engine fmax +#define ROWS 1 // rows of cars +#define COLS 1 // columns of cars +#define ITERS 20 // number of iterations +#define WBOXSIZE 1.0 // size of wall boxes +#define WALLWIDTH 12 // width of wall +#define WALLHEIGHT 10 // height of wall +#define DISABLE_THRESHOLD 0.008 // maximum velocity (squared) a body can have and be disabled +#define DISABLE_STEPS 10 // number of steps a box has to have been disable-able before it will be disabled +#define CANNON_X -10 // x position of cannon +#define CANNON_Y 5 // y position of cannon +#define CANNON_BALL_MASS 10 // mass of the cannon ball +#define CANNON_BALL_RADIUS 0.5 + +static const dVector3 xunit = { 1, 0, 0 }, yunit = { 0, 1, 0 }, zpunit = { 0, 0, 1 }, zmunit = { 0, 0, -1 }; + +//#define BOX +#define CARS +#define WALL +//#define BALLS +//#define BALLSTACK +//#define ONEBALL +//#define CENTIPEDE +#define CANNON + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; +static dThreadingImplementationID threading; +static dThreadingThreadPoolID pool; +static dBodyID body[10000]; +static int bodies; +static dJointID joint[100000]; +static int joints; +static dJointGroupID contactgroup; +static dGeomID ground; +static dGeomID box[10000]; +static int boxes; +static dGeomID sphere[10000]; +static int spheres; +static dGeomID wall_boxes[10000]; +static dBodyID wall_bodies[10000]; +static dGeomID cannon_ball_geom; +static dBodyID cannon_ball_body; +static int wb_stepsdis[10000]; +static int wb; +static bool doFast; +static dBodyID b; +static dMass m; + + +// things that the user controls + +static dReal turn = 0, speed = 0; // user commands +static dReal cannon_angle=0,cannon_elevation=-1.2; + + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,n; + + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnected(b1, b2)) + return; + + const int N = 4; + dContact contact[N]; + n = dCollide (o1,o2,N,&contact[0].geom,sizeof(dContact)); + if (n > 0) { + for (i=0; i -20; x-=RADIUS*2) + { + body[bodies] = dBodyCreate (world); + dBodySetPosition(body[bodies], x, y, STARTZ); + dMassSetSphere(&m, 1, RADIUS); + dMassAdjust(&m, WMASS); + dBodySetMass(body[bodies], &m); + sphere[spheres] = dCreateSphere (space, RADIUS); + dGeomSetBody (sphere[spheres++], body[bodies]); + + joint[joints] = dJointCreateHinge2 (world,0); + if (x == -17) + dJointAttach (joint[joints],b,body[bodies]); + else + dJointAttach (joint[joints],body[bodies-2],body[bodies]); + const dReal *a = dBodyGetPosition (body[bodies++]); + dJointSetHinge2Anchor (joint[joints],a[0],a[1],a[2]); + dJointSetHinge2Axes (joint[joints], zpunit, xunit); + dJointSetHinge2Param (joint[joints],dParamSuspensionERP,1.0); + dJointSetHinge2Param (joint[joints],dParamSuspensionCFM,1e-5); + dJointSetHinge2Param (joint[joints],dParamLoStop,0); + dJointSetHinge2Param (joint[joints],dParamHiStop,0); + dJointSetHinge2Param (joint[joints],dParamVel2,-10.0); + dJointSetHinge2Param (joint[joints++],dParamFMax2,FMAX); + + body[bodies] = dBodyCreate (world); + dBodySetPosition(body[bodies], -30 - x, y, STARTZ); + dMassSetSphere(&m, 1, RADIUS); + dMassAdjust(&m, WMASS); + dBodySetMass(body[bodies], &m); + sphere[spheres] = dCreateSphere (space, RADIUS); + dGeomSetBody (sphere[spheres++], body[bodies]); + + joint[joints] = dJointCreateHinge2 (world,0); + if (x == -17) + dJointAttach (joint[joints],b,body[bodies]); + else + dJointAttach (joint[joints],body[bodies-2],body[bodies]); + const dReal *b = dBodyGetPosition (body[bodies++]); + dJointSetHinge2Anchor (joint[joints],b[0],b[1],b[2]); + dJointSetHinge2Axes (joint[joints], zpunit, xunit); + dJointSetHinge2Param (joint[joints],dParamSuspensionERP,1.0); + dJointSetHinge2Param (joint[joints],dParamSuspensionCFM,1e-5); + dJointSetHinge2Param (joint[joints],dParamLoStop,0); + dJointSetHinge2Param (joint[joints],dParamHiStop,0); + dJointSetHinge2Param (joint[joints],dParamVel2,10.0); + dJointSetHinge2Param (joint[joints++],dParamFMax2,FMAX); + } + if (lastb) + { + dJointID j = dJointCreateFixed(world,0); + dJointAttach (j, b, lastb); + dJointSetFixed(j); + } + lastb = b; + } +#endif +#ifdef BOX + body[bodies] = dBodyCreate (world); + dBodySetPosition (body[bodies],0,0,HEIGHT/2); + dMassSetBox (&m,1,LENGTH,WIDTH,HEIGHT); + dMassAdjust (&m, 1); + dBodySetMass (body[bodies],&m); + box[boxes] = dCreateBox (space,LENGTH,WIDTH,HEIGHT); + dGeomSetBody (box[boxes++],body[bodies++]); +#endif +#ifdef CANNON + cannon_ball_body = dBodyCreate (world); + cannon_ball_geom = dCreateSphere (space,CANNON_BALL_RADIUS); + dMassSetSphereTotal (&m,CANNON_BALL_MASS,CANNON_BALL_RADIUS); + dBodySetMass (cannon_ball_body,&m); + dGeomSetBody (cannon_ball_geom,cannon_ball_body); + dBodySetPosition (cannon_ball_body,CANNON_X,CANNON_Y,CANNON_BALL_RADIUS); +#endif +} + +// called when a key pressed + +static void command (int cmd) +{ + switch (cmd) { + case 'a': case 'A': + speed += 0.3; + break; + case 'z': case 'Z': + speed -= 0.3; + break; + case ',': + turn += 0.1; + if (turn > 0.3) + turn = 0.3; + break; + case '.': + turn -= 0.1; + if (turn < -0.3) + turn = -0.3; + break; + case ' ': + speed = 0; + turn = 0; + break; + case 'f': case 'F': + doFast = !doFast; + break; + case 'r': case 'R': + shutdownSimulation(); + setupSimulation(); + break; + case '[': + cannon_angle += 0.1; + break; + case ']': + cannon_angle -= 0.1; + break; + case '1': + cannon_elevation += 0.1; + break; + case '2': + cannon_elevation -= 0.1; + break; + case 'x': case 'X': { + dMatrix3 R2,R3,R4; + dRFromAxisAndAngle (R2,0,0,1,cannon_angle); + dRFromAxisAndAngle (R3,0,1,0,cannon_elevation); + dMultiply0 (R4,R2,R3,3,3,3); + dReal cpos[3] = {CANNON_X,CANNON_Y,1}; + for (int i=0; i<3; i++) cpos[i] += 3*R4[i*4+2]; + dBodySetPosition (cannon_ball_body,cpos[0],cpos[1],cpos[2]); + dReal force = 10; + dBodySetLinearVel (cannon_ball_body,force*R4[2],force*R4[6],force*R4[10]); + dBodySetAngularVel (cannon_ball_body,0,0,0); + break; + } + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + int i, j; + + dsSetTexture (DS_WOOD); + + if (!pause) { +#ifdef BOX + dBodyAddForce(body[bodies-1],lspeed,0,0); +#endif + for (j = 0; j < joints; j++) + { + dReal curturn = dJointGetHinge2Angle1 (joint[j]); + //dMessage (0,"curturn %e, turn %e, vel %e", curturn, turn, (turn-curturn)*1.0); + dJointSetHinge2Param(joint[j],dParamVel,(turn-curturn)*1.0); + dJointSetHinge2Param(joint[j],dParamFMax,dInfinity); + dJointSetHinge2Param(joint[j],dParamVel2,speed); + dJointSetHinge2Param(joint[j],dParamFMax2,FMAX); + dBodyEnable(dJointGetBody(joint[j],0)); + dBodyEnable(dJointGetBody(joint[j],1)); + } + if (doFast) + { + dSpaceCollide (space,0,&nearCallback); + dWorldQuickStep (world,0.05); + dJointGroupEmpty (contactgroup); + } + else + { + dSpaceCollide (space,0,&nearCallback); + dWorldStep (world,0.05); + dJointGroupEmpty (contactgroup); + } + + for (i = 0; i < wb; i++) + { + b = dGeomGetBody(wall_boxes[i]); + if (dBodyIsEnabled(b)) + { + bool disable = true; + const dReal *lvel = dBodyGetLinearVel(b); + dReal lspeed = lvel[0]*lvel[0]+lvel[1]*lvel[1]+lvel[2]*lvel[2]; + if (lspeed > DISABLE_THRESHOLD) + disable = false; + const dReal *avel = dBodyGetAngularVel(b); + dReal aspeed = avel[0]*avel[0]+avel[1]*avel[1]+avel[2]*avel[2]; + if (aspeed > DISABLE_THRESHOLD) + disable = false; + + if (disable) + wb_stepsdis[i]++; + else + wb_stepsdis[i] = 0; + + if (wb_stepsdis[i] > DISABLE_STEPS) + { + dBodyDisable(b); + dsSetColor(0.5,0.5,1); + } + else + dsSetColor(1,1,1); + + } + else + dsSetColor(0.4,0.4,0.4); + dVector3 ss; + dGeomBoxGetLengths (wall_boxes[i], ss); + dsDrawBox(dGeomGetPosition(wall_boxes[i]), dGeomGetRotation(wall_boxes[i]), ss); + } + } + else + { + for (i = 0; i < wb; i++) + { + b = dGeomGetBody(wall_boxes[i]); + if (dBodyIsEnabled(b)) + dsSetColor(1,1,1); + else + dsSetColor(0.4,0.4,0.4); + dVector3 ss; + dGeomBoxGetLengths (wall_boxes[i], ss); + dsDrawBox(dGeomGetPosition(wall_boxes[i]), dGeomGetRotation(wall_boxes[i]), ss); + } + } + + dsSetColor (0,1,1); + dReal sides[3] = {LENGTH,WIDTH,HEIGHT}; + for (i = 0; i < boxes; i++) + dsDrawBox (dGeomGetPosition(box[i]),dGeomGetRotation(box[i]),sides); + dsSetColor (1,1,1); + for (i=0; i< spheres; i++) dsDrawSphere (dGeomGetPosition(sphere[i]), + dGeomGetRotation(sphere[i]),RADIUS); + + // draw the cannon + dsSetColor (1,1,0); + dMatrix3 R2,R3,R4; + dRFromAxisAndAngle (R2,0,0,1,cannon_angle); + dRFromAxisAndAngle (R3,0,1,0,cannon_elevation); + dMultiply0 (R4,R2,R3,3,3,3); + dReal cpos[3] = {CANNON_X,CANNON_Y,1}; + dReal csides[3] = {2,2,2}; + dsDrawBox (cpos,R2,csides); + for (i=0; i<3; i++) cpos[i] += 1.5*R4[i*4+2]; + dsDrawCylinder (cpos,R4,3,0.5); + + // draw the cannon ball + dsDrawSphere (dBodyGetPosition(cannon_ball_body),dBodyGetRotation(cannon_ball_body), + CANNON_BALL_RADIUS); +} + +int main (int argc, char **argv) +{ + doFast = true; + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dInitODE2(0); + + bodies = 0; + joints = 0; + boxes = 0; + spheres = 0; + + setupSimulation(); + + dThreadingImplementationID threading = dThreadingAllocateMultiThreadedImplementation(); + dThreadingThreadPoolID pool = dThreadingAllocateThreadPool(8, 0, dAllocateFlagBasicData, NULL); + dThreadingThreadPoolServeMultiThreadedImplementation(pool, threading); + // dWorldSetStepIslandsProcessingMaxThreadCount(world, 1); + dWorldSetStepThreadingImplementation(world, dThreadingImplementationGetFunctions(threading), threading); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dThreadingImplementationShutdownProcessing(threading); + dThreadingFreeThreadPool(pool); + dWorldSetStepThreadingImplementation(world, NULL, NULL); + dThreadingFreeImplementation(threading); + + shutdownSimulation(); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_cyl.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_cyl.cpp new file mode 100644 index 0000000..368a0c1 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_cyl.cpp @@ -0,0 +1,321 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// Test for non-capped cylinder, by Bram Stolk +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include "texturepath.h" + +#include "world_geom3.h" // this is our world mesh + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + + +#define BOX +#define CYL + +// some constants + +#define RADIUS 0.22 // wheel radius +#define WMASS 0.2 // wheel mass +#define WHEELW 0.2 // wheel width +#define BOXSZ 0.4 // box size +//#define CYL_GEOM_OFFSET // rotate cylinder using geom offset + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; +#ifdef BOX +static dBodyID boxbody; +static dGeomID boxgeom; +#endif +#ifdef CYL +static dBodyID cylbody; +static dGeomID cylgeom; +#endif +static dJointGroupID contactgroup; +static dGeomID world_mesh; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + assert(o1); + assert(o2); + + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + fprintf(stderr,"testing space %p %p\n", (void*)o1, (void*)o2); + // colliding a space with something + dSpaceCollide2(o1,o2,data,&nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + return; + } + +// fprintf(stderr,"testing geoms %p %p\n", o1, o2); + + const int N = 32; + dContact contact[N]; + int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact)); + if (n > 0) + { + for (int i=0; i +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; + +static dBodyID cylbody; +static dGeomID cylgeom; + +static dBodyID sphbody; +static dGeomID sphgeom; + +static dJointGroupID contactgroup; + +static bool show_contacts = true; + +#define CYLRADIUS 0.6 +#define CYLLENGTH 2.0 +#define SPHERERADIUS 0.5 + + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawLine dsDrawLineD +#endif + + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + assert(o1); + assert(o2); + + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + fprintf(stderr,"testing space %p %p\n", (void*)o1, (void*)o2); + // colliding a space with something + dSpaceCollide2(o1,o2,data,&nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + return; + } + + const int N = 32; + dContact contact[N]; + int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact)); + if (n > 0) + { + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawSphere dsDrawSphereD +#define dsDrawBox dsDrawBoxD +#define dsDrawLine dsDrawLineD +#endif + + +dWorldID world; +dSpaceID space; +dBodyID body1; +dBodyID body2; +dJointID joint1, joint2; + +void start() +{ + world = dWorldCreate(); + dWorldSetGravity (world,0,0,-9.8); + + dWorldSetDamping(world, 1e-4, 1e-5); +// dWorldSetERP(world, 1); + + space = dSimpleSpaceCreate (0); + + body1 = dBodyCreate(world); + body2 = dBodyCreate(world); + + dBodySetPosition(body1, 0, 0, 3); + dBodySetPosition(body2, 0, 0, 1); + + + dGeomID g; + dMass mass; + + g = dCreateBox(space, 0.2, 0.2, 1); + dGeomSetBody(g, body1); + dMassSetBox(&mass, 1, 0.2, 0.2, 1); + dBodySetMass(body1, &mass); + + g = dCreateBox(space, 0.2, 0.2, 1); + dGeomSetBody(g, body2); + dMassSetBox(&mass, 1, 0.2, 0.2, 1); + dBodySetMass(body2, &mass); + + joint1 = dJointCreateDBall(world, 0); + dJointAttach(joint1, body1, 0); + dJointSetDBallAnchor1(joint1, 0, 0, 3.5); + dJointSetDBallAnchor2(joint1, 0, 0, 4.5); + + joint2 = dJointCreateDBall(world, 0); + dJointAttach(joint2, body1, body2); + dJointSetDBallAnchor1(joint2, 0, 0, 2.5); + dJointSetDBallAnchor2(joint2, 0, 0, 1.5); + + + // initial camera position + static float xyz[3] = {3.8966, -2.0614, 4.0300}; + static float hpr[3] = {153.5, -16.5, 0}; + dsSetViewpoint (xyz,hpr); +} + +void stop() +{ + dSpaceDestroy(space); + + dWorldDestroy(world); +} + + +void drawGeom(dGeomID g) +{ + int gclass = dGeomGetClass(g); + const dReal *pos = dGeomGetPosition(g); + const dReal *rot = dGeomGetRotation(g); + + switch (gclass) { + case dSphereClass: + dsSetColorAlpha(0, 0.75, 0.5, 1); + dsSetTexture (DS_CHECKERED); + dsDrawSphere(pos, rot, dGeomSphereGetRadius(g)); + break; + case dBoxClass: + { + dVector3 lengths; + dsSetColorAlpha(1, 1, 0, 1); + dsSetTexture (DS_WOOD); + dGeomBoxGetLengths(g, lengths); + dsDrawBox(pos, rot, lengths); + break; + } + + default: + {} + } +} + +void simLoop(int pause) +{ + if (!pause) { + + static dReal t = 0; + + const dReal step = 0.005; + const unsigned nsteps = 4; + + for (unsigned i=0; i +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawSphere dsDrawSphereD +#define dsDrawBox dsDrawBoxD +#define dsDrawLine dsDrawLineD +#endif + + +dWorldID world; +dSpaceID space; +dBodyID body1; +dBodyID body2; +dJointID joint1, joint2; +bool applyForce = false; + +void start() +{ + world = dWorldCreate(); + dWorldSetGravity (world,0,0,-9.8); + + dWorldSetDamping(world, 1e-4, 1e-5); +// dWorldSetERP(world, 1); + + space = dSimpleSpaceCreate (0); + + body1 = dBodyCreate(world); + body2 = dBodyCreate(world); + + dBodySetPosition(body1, 0, 0, 3); + dBodySetPosition(body2, 0, 0, 1); + + + dGeomID g; + dMass mass; + + g = dCreateBox(space, 0.2, 0.2, 1); + dGeomSetBody(g, body1); + dMassSetBox(&mass, 1, 0.2, 0.2, 1); + dBodySetMass(body1, &mass); + + g = dCreateBox(space, 0.2, 0.2, 1); + dGeomSetBody(g, body2); + dMassSetBox(&mass, 1, 0.2, 0.2, 1); + dBodySetMass(body2, &mass); + +#if 1 + joint1 = dJointCreateDHinge(world, 0); + dJointAttach(joint1, body1, 0); + dJointSetDHingeAxis(joint1, 0, 1, 0); + dJointSetDHingeAnchor1(joint1, 0, 0, 3.5); + dJointSetDHingeAnchor2(joint1, 0, 0, 4.5); +#endif + +#if 1 + joint2 = dJointCreateDHinge(world, 0); + dJointAttach(joint2, body1, body2); + dJointSetDHingeAxis(joint2, 1, 0, 0); + dJointSetDHingeAnchor1(joint2, 0, 0, 2.5); + dJointSetDHingeAnchor2(joint2, 0, 0, 1.5); +#else + joint2 = dJointCreateDBall(world, 0); + dJointAttach(joint2, body1, body2); + dJointSetDBallAnchor1(joint2, 0, 0, 2.5); + dJointSetDBallAnchor2(joint2, 0, 0, 1.5); +#endif + + //dBodyAddForce(body1, 20, 0, 0); + + + // initial camera position + static float xyz[3] = {3.8966, -2.0614, 4.0300}; + static float hpr[3] = {153.5, -16.5, 0}; + dsSetViewpoint (xyz,hpr); +} + +void stop() +{ + dSpaceDestroy(space); + + dWorldDestroy(world); +} + + +void drawGeom(dGeomID g) +{ + int gclass = dGeomGetClass(g); + const dReal *pos = dGeomGetPosition(g); + const dReal *rot = dGeomGetRotation(g); + + switch (gclass) { + case dBoxClass: + { + dVector3 lengths; + if (applyForce) + dsSetColor(1, .5, 0); + else + dsSetColor(1, 1, 0); + dsSetTexture (DS_WOOD); + dGeomBoxGetLengths(g, lengths); + dsDrawBox(pos, rot, lengths); + break; + } + + default: + {} + } +} + + +void simLoop(int pause) +{ + if (!pause) { + + static dReal t = 0; + + const dReal step = 0.005; + const unsigned nsteps = 2; + + for (unsigned i=0; i 2.; + + if (applyForce) { + dReal f = 0.3 * sin(t*1.2); + dBodyAddForceAtRelPos(body1, + f, 0, 0, + 0, 0, -0.5); // at the lower end + + dReal g = 0.3 * sin(t*0.7); + dBodyAddForceAtRelPos(body2, + 0, g, 0, + 0, 0, -0.5); // at the lower end + } + + t += step; + if (t > 20.) + t = 0.; + + dWorldQuickStep(world, step); + } + } + + // now we draw everything + unsigned ngeoms = dSpaceGetNumGeoms(space); + for (unsigned i=0; i +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include "texturepath.h" + + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawCylinder dsDrawCylinderD +#endif + + +// dynamics and collision objects (chassis, 3 wheels, environment) + +static dWorldID world; +static dSpaceID space; + +static const int STACKCNT=10; // nr of weights on bridge +static const int SEGMCNT=16; // nr of segments in bridge +static const float SEGMDIM[3] = { 0.9, 4, 0.1 }; + +static dGeomID groundgeom; +static dBodyID segbodies[SEGMCNT]; +static dGeomID seggeoms[SEGMCNT]; +static dBodyID stackbodies[STACKCNT]; +static dGeomID stackgeoms[STACKCNT]; +static dJointID hinges[SEGMCNT-1]; +static dJointID sliders[2]; +static dJointFeedback jfeedbacks[SEGMCNT-1]; +static dReal colours[SEGMCNT]; +static int stress[SEGMCNT-1]; + +static dJointGroupID contactgroup; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *data, dGeomID o1, dGeomID o2) +{ + assert(o1); + assert(o2); + + if (dGeomIsSpace(o1) || dGeomIsSpace(o2)) + { + fprintf(stderr,"testing space %p %p\n", (void*)o1, (void*)o2); + // colliding a space with something + dSpaceCollide2(o1,o2,data,&nearCallback); + // Note we do not want to test intersections within a space, + // only between spaces. + return; + } + + const int N = 32; + dContact contact[N]; + int n = dCollide (o1,o2,N,&(contact[0].geom),sizeof(dContact)); + if (n > 0) + { + for (int i=0; i forcelimit || l1 > forcelimit) + stress[i]++; + else + stress[i]=0; + if (stress[i]>4) + { + // Low-pass filter the noisy feedback data. + // Only after 4 consecutive timesteps with excessive load, snap. + fprintf(stderr,"SNAP! (that was the sound of joint %d breaking)\n", i); + dJointAttach (hinges[i], 0, 0); + } + } + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + int i; + + double simstep = 0.002; // 2ms simulation steps + double dt = dsElapsedTime(); + int nrofsteps = (int) ceilf(dt/simstep); + for (i=0; i1.0) v=1.0; + if (v<0.5) + { + r=2*v; + g=1.0; + } + else + { + r=1.0; + g=2*(1.0-v); + } + dsSetColor (r,g,b); + drawGeom(seggeoms[i]); + } + dsSetColor (1,1,1); + for (i=0; i MU * body_mass * GRAVITY + (j+1)*FORCE > MU * (i+1)*MASS * GRAVITY + (j+1) > (i+1) * (MU*MASS*GRAVITY/FORCE) + (j+1) > (i+1) * k + +this should be independent of the number of contact points, as N contact +points will each have 1/N'th the normal force but the pushing force will +have to overcome N contacts. the constants are chosen so that k=1. +thus you should see a triangle made of half the bodies in the array start to +slide. + +*/ + + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define LENGTH 0.2 // box length & width +#define HEIGHT 0.05 // box height +#define MASS 0.2 // mass of box[i][j] = (i+1) * MASS +#define FORCE 0.05 // force applied to box[i][j] = (j+1) * FORCE +#define MU 0.5 // the global mu to use +#define GRAVITY 0.5 // the global gravity to use +#define N1 10 // number of different forces to try +#define N2 10 // number of different masses to try + + +// dynamics and collision objects + +static dWorldID world; +static dSpaceID space; +static dBodyID body[N1][N2]; +static dJointGroupID contactgroup; +static dGeomID ground; +static dGeomID box[N1][N2]; + + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + + // only collide things with the ground + int g1 = (o1 == ground); + int g2 = (o2 == ground); + if (!(g1 ^ g2)) return; + + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + dContact contact[3]; // up to 3 contacts per box + for (i=0; i<3; i++) { + contact[i].surface.mode = dContactSoftCFM | dContactApprox1; + contact[i].surface.mu = MU; + contact[i].surface.soft_cfm = 0.01; + } + if (int numc = dCollide (o1,o2,3,&contact[0].geom,sizeof(dContact))) { + for (i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + +// dynamics and collision objects +static dWorldID world = 0; + +static const dReal dt = 1/REAL(60.0); // 60 fps +// Water density if units are meters and kg +static const dReal density = 1000; + +// A long skinny thing +static dVector3 sides = {2,.5,.25}; +// Initial angular velocity +static dVector3 omega = {5,1,2}; +static dVector3 torque = {0,10,0}; +static dBodyID noGyroBody; +static dBodyID expGyroBody; +static dBodyID impGyroBody; + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {0,-4.0f,3.0f}; + static float hpr[3] = {90.0000,-15.0000,0.0000}; + dsSetViewpoint (xyz,hpr); + printf ("Press:\n" + "\t'a' to apply a torque\n" + "\t'r' to reset simulation.\n"); +} + +/** + Delete the bodies, etc. +*/ +static void clear() +{ + if (world) dWorldDestroy (world); + world = 0; +} + + + +/** + Cleanup if necessary and rebuild the + world. +*/ +static void reset() +{ + clear(); + + // create world + world = dWorldCreate(); + + // Calculate mass for a box; + dMass boxMass; + dMassSetBox(&boxMass,density,sides[0],sides[1],sides[2]); + + noGyroBody = dBodyCreate(world);// Conservation of ang-velocity + expGyroBody = dBodyCreate(world);// Explicit conservation of ang-momentum + impGyroBody = dBodyCreate(world);// Implicit conservation of ang-momentum + + dBodySetMass( noGyroBody , &boxMass ); + dBodySetMass( expGyroBody, &boxMass ); + dBodySetMass( impGyroBody, &boxMass ); + + // Try to avoid collisions. + dReal sep = dCalcVectorLength3(sides); + dBodySetPosition( noGyroBody , -sep, 0, sep); + dBodySetPosition( expGyroBody, 0, 0, sep); + dBodySetPosition( impGyroBody, sep, 0, sep); + + // Set the initial angular velocity + dBodySetAngularVel( noGyroBody , omega[0], omega[1], omega[2]); + dBodySetAngularVel( expGyroBody, omega[0], omega[1], omega[2]); + dBodySetAngularVel( impGyroBody, omega[0], omega[1], omega[2]); + + dBodySetGyroscopicMode( noGyroBody, 0); + // We compute this ourselves using the math + // that was in the old stepper. + dBodySetGyroscopicMode(expGyroBody, 0); + // Keep things from crashing by limiting + // the angular speed of the explicit body. + // Note that this isn't necessary for + // the other two bodies. + dBodySetMaxAngularSpeed( expGyroBody, 40 ); +} + +static void command (int cmd) +{ + switch (cmd) { + case 'a': case 'A': + dBodyAddTorque( noGyroBody, torque[0], torque[1], torque[2]); + dBodyAddTorque(expGyroBody, torque[0], torque[1], torque[2]); + dBodyAddTorque(impGyroBody, torque[0], torque[1], torque[2]); + break; + case 'r': case 'R': + reset(); + break; + } + +} + +/** + This is the explicit computation of + gyroscopic forces. +*/ +static void expStep(dBodyID body) +{ + // Explicit computation + dMatrix3 I,tmp; + dMass m; + dBodyGetMass(body,&m); + const dReal* R = dBodyGetRotation(body); + // compute inertia tensor in global frame + dMultiply2_333 (tmp,m.I,R); + dMultiply0_333 (I,R,tmp); + // compute explicit rotational force + // we treat 'tmp'like a vector, but that's okay. + const dReal* w = dBodyGetAngularVel(body); + dMultiply0_331 (tmp,I,w); + dVector3 tau; + dCalcVectorCross3(tau,tmp,w); + dBodyAddTorque(body,tau[0],tau[1],tau[2]); +} + + +// simulation loop +static void simLoop (int pause) +{ + if (!pause) { + expStep(expGyroBody); + dWorldStep (world,dt); + } + + dsSetTexture (DS_WOOD); + dsSetColor(1,0,0); + dsDrawBox(dBodyGetPosition(noGyroBody ),dBodyGetRotation(noGyroBody ),sides); + dsSetColor(1,1,0); + dsDrawBox(dBodyGetPosition(expGyroBody),dBodyGetRotation(expGyroBody),sides); + dsSetColor(0,1,0); + dsDrawBox(dBodyGetPosition(impGyroBody),dBodyGetRotation(impGyroBody),sides); +} + + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dInitODE2(0); + reset(); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + clear(); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_gyroscopic.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_gyroscopic.cpp new file mode 100644 index 0000000..5b6a532 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_gyroscopic.cpp @@ -0,0 +1,258 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawConvex dsDrawConvexD +#endif + +bool write_world = false; +bool show_contacts = false; +dWorld * world; +dBody *top1, *top2; +dSpace *space; +dJointGroup contactgroup; + +const dReal pinradius = 0.05f; +const dReal pinlength = 1.5f; +const dReal topradius = 1.0f; +const dReal toplength = 0.25f; +const dReal topmass = 1.0f; + +#define MAX_CONTACTS 4 + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + // for drawing the contact points + dMatrix3 RI; + dRSetIdentity (RI); + const dReal ss[3] = {0.02,0.02,0.02}; + + int i; + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + dContact contact[MAX_CONTACTS]; + int numc = dCollide (o1,o2,MAX_CONTACTS,&contact[0].geom, + sizeof(dContact)); + + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed +static void reset(); +static void tilt(); + +static void command (int cmd) +{ + cmd = locase (cmd); + if (cmd == ' ') + { + reset(); + } + else if (cmd == 'a') { + tilt(); + } + else if (cmd == 't') { + show_contacts = !show_contacts; + } + else if (cmd == '1') { + write_world = true; + } +} + +// simulation loop + +static void simLoop (int pause) +{ + dsSetColor (0,0,2); + space->collide(0,&nearCallback); + if (!pause) + //world->quickStep(0.02); + world->step(0.02); + + if (write_world) { + FILE *f = fopen ("state.dif","wt"); + if (f) { + dWorldExportDIF (*world,f,"X"); + fclose (f); + } + write_world = false; + } + + // remove all contact joints + dJointGroupEmpty (contactgroup); + + dsSetTexture (DS_WOOD); + + dsSetColor (1,0.5f,0); + dsDrawCylinder(top1->getPosition(), + top1->getRotation(), + toplength, topradius); + dsDrawCapsule(top1->getPosition(), + top1->getRotation(), + pinlength, pinradius); + + dsSetColor (0.5f,1,0); + dsDrawCylinder(top2->getPosition(), + top2->getRotation(), + toplength, topradius); + dsDrawCapsule(top2->getPosition(), + top2->getRotation(), + pinlength, pinradius); + +} + + +static void reset() +{ + dMatrix3 R; + dRSetIdentity(R); + + top1->setRotation(R); + top2->setRotation(R); + + top1->setPosition(0.8f, -2, 2); + top2->setPosition(0.8f, 2, 2); + + top1->setAngularVel(1,0,7); + top2->setAngularVel(1,0,7); + + top1->setLinearVel(0,0.2f,0); + top2->setLinearVel(0,0.2f,0); +} + +static void tilt() +{ + top1->addTorque(0, 10, 0); + top2->addTorque(0, 10, 0); +} + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + + // create world + dInitODE(); + world = new dWorld(); + world->setGravity(0,0,-0.5f); + world->setCFM(1e-5f); + world->setLinearDamping(0.00001f); + world->setAngularDamping(0.0001f); + + space = new dSimpleSpace(0); + + dPlane *floor = new dPlane(*space, 0,0,1,0); + + top1 = new dBody(*world); + top2 = new dBody(*world); + + dMass m; + m.setCylinderTotal(1, 3, topradius, toplength); + top1->setMass(m); + top2->setMass(m); + + dGeom *g1, *g2, *pin1, *pin2; + g1 = new dCylinder(*space, topradius, toplength); + g1->setBody(*top1); + g2 = new dCylinder(*space, topradius, toplength); + g2->setBody(*top2); + + pin1 = new dCapsule(*space, pinradius, pinlength); + pin1->setBody(*top1); + pin2 = new dCapsule(*space, pinradius, pinlength); + pin2->setBody(*top2); + + top2->setGyroscopicMode(false); + + reset(); + + // run simulation + dsSimulationLoop (argc,argv,512,384,&fn); + + delete g1; + delete g2; + delete pin1; + delete pin2; + delete floor; + contactgroup.empty(); + delete top1; + delete top2; + delete space; + delete world; + dCloseODE(); +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_heightfield.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_heightfield.cpp new file mode 100644 index 0000000..d68cb6a --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_heightfield.cpp @@ -0,0 +1,714 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include "texturepath.h" +#include "bunny_geom.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + + +#define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians + +int g_allow_trimesh; + +// Our heightfield geom +dGeomID gheight; + + + +// Heightfield dimensions + +#define HFIELD_WSTEP 15 // Vertex count along edge >= 2 +#define HFIELD_DSTEP 31 + +#define HFIELD_WIDTH REAL( 4.0 ) +#define HFIELD_DEPTH REAL( 8.0 ) + +#define HFIELD_WSAMP ( HFIELD_WIDTH / ( HFIELD_WSTEP-1 ) ) +#define HFIELD_DSAMP ( HFIELD_DEPTH / ( HFIELD_DSTEP-1 ) ) + + + +//<---- Convex Object +dReal planes[]= // planes for a cube + { + 1.0f ,0.0f ,0.0f ,0.25f, + 0.0f ,1.0f ,0.0f ,0.25f, + 0.0f ,0.0f ,1.0f ,0.25f, + 0.0f ,0.0f ,-1.0f,0.25f, + 0.0f ,-1.0f,0.0f ,0.25f, + -1.0f,0.0f ,0.0f ,0.25f + /* + 1.0f ,0.0f ,0.0f ,2.0f, + 0.0f ,1.0f ,0.0f ,1.0f, + 0.0f ,0.0f ,1.0f ,1.0f, + 0.0f ,0.0f ,-1.0f,1.0f, + 0.0f ,-1.0f,0.0f ,1.0f, + -1.0f,0.0f ,0.0f ,0.0f + */ + }; +const unsigned int planecount=6; + +dReal points[]= // points for a cube + { + 0.25f,0.25f,0.25f, // point 0 + -0.25f,0.25f,0.25f, // point 1 + + 0.25f,-0.25f,0.25f, // point 2 + -0.25f,-0.25f,0.25f,// point 3 + + 0.25f,0.25f,-0.25f, // point 4 + -0.25f,0.25f,-0.25f,// point 5 + + 0.25f,-0.25f,-0.25f,// point 6 + -0.25f,-0.25f,-0.25f,// point 7 + }; +const unsigned int pointcount=8; +unsigned int polygons[] = //Polygons for a cube (6 squares) + { + 4,0,2,6,4, // positive X + 4,1,0,4,5, // positive Y + 4,0,1,3,2, // positive Z + 4,3,1,5,7, // negative X + 4,2,3,7,6, // negative Y + 4,5,4,6,7, // negative Z + }; +//----> Convex Object + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawConvex dsDrawConvexD +#define dsDrawTriangle dsDrawTriangleD +#endif + + +// some constants + +#define NUM 100 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 64 // maximum number of contact points per body + + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body + + // Trimesh only - double buffered matrices for 'last transform' setup + dReal matrix_dblbuff[ 16 * 2 ]; + int last_matrix_index; +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? +static int write_world = 0; + + + + +//============================ + +dGeomID TriMesh1; +dGeomID TriMesh2; +//static dTriMeshDataID TriData1, TriData2; // reusable static trimesh data + +//============================ + + +dReal heightfield_callback( void*, int x, int z ) +{ + dReal fx = ( ((dReal)x) - ( HFIELD_WSTEP-1 )/2 ) / (dReal)( HFIELD_WSTEP-1 ); + dReal fz = ( ((dReal)z) - ( HFIELD_DSTEP-1 )/2 ) / (dReal)( HFIELD_DSTEP-1 ); + + // Create an interesting 'hump' shape + dReal h = REAL( 1.0 ) + ( REAL( -16.0 ) * ( fx*fx*fx + fz*fz*fz ) ); + + return h; +} + + + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnectedExcluding(b1,b2,dJointTypeContact)) + return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command(int cmd) +{ + dsizeint i; + int j,k; + dReal sides[3]; + dMass m; + bool setBody = false; + + cmd = locase (cmd); + + + // + // Geom Creation + // + + if ( cmd == 'b' || cmd == 's' || cmd == 'c' || ( cmd == 'm' && g_allow_trimesh ) || + cmd == 'x' || cmd == 'y' || cmd == 'v' ) { + + if ( num < NUM ) { + i = num; + num++; + } else { + i = nextobj++; + nextobj %= num; + + // destroy the body and geoms for slot i + dBodyDestroy(obj[i].body); + obj[i].body = 0; + + for (k=0; k < GPB; k++) + if (obj[i].geom[k]) { + dGeomDestroy(obj[i].geom[k]); + obj[i].geom[k] = 0; + } + } + + obj[i].body = dBodyCreate(world); + for (k=0; k<3; k++) + sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) { + dBodySetPosition(obj[i].body, + (dRandReal()-0.5)*HFIELD_WIDTH*0.75, + (dRandReal()-0.5)*HFIELD_DEPTH*0.75, + dRandReal() + 2 ); + dRFromAxisAndAngle(R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } else { + dReal maxheight = 0; + for (k=0; k maxheight) + maxheight = pos[2]; + } + dBodySetPosition(obj[i].body, 0,maxheight+1,0); + dRFromAxisAndAngle(R,0,0,1,dRandReal()*10.0-5.0); + } + + dBodySetRotation(obj[i].body,R); + + if (cmd == 'b') { + + dMassSetBox(&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox(space,sides[0],sides[1],sides[2]); + + } else if (cmd == 'c') { + + sides[0] *= 0.5; + dMassSetCapsule(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule(space,sides[0],sides[1]); + + } else if (cmd == 'v') { + + dMassSetBox (&m,DENSITY,0.25,0.25,0.25); + obj[i].geom[0] = dCreateConvex(space, + planes, + planecount, + points, + pointcount, + polygons); + + } else if (cmd == 'y') { + + dMassSetCylinder(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder(space,sides[0],sides[1]); + + } else if (cmd == 's') { + + sides[0] *= 0.5; + dMassSetSphere(&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere(space,sides[0]); + + } else if (cmd == 'm' && g_allow_trimesh) { + + dTriMeshDataID new_tmdata = dGeomTriMeshDataCreate(); + dGeomTriMeshDataBuildSingle(new_tmdata, &Vertices[0], 3 * sizeof(float), VertexCount, + &Indices[0], IndexCount, 3 * sizeof(dTriIndex)); + dGeomTriMeshDataPreprocess2(new_tmdata, (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES), NULL); + + obj[i].geom[0] = dCreateTriMesh(space, new_tmdata, 0, 0, 0); + + dMassSetTrimesh( &m, DENSITY, obj[i].geom[0] ); + printf("mass at %f %f %f\n", m.c[0], m.c[1], m.c[2]); + dGeomSetPosition(obj[i].geom[0], -m.c[0], -m.c[1], -m.c[2]); + dMassTranslate(&m, -m.c[0], -m.c[1], -m.c[2]); + + } else if (cmd == 'x') { + + setBody = 1; + // start accumulating masses for the composite geometries + dMass m2; + dMassSetZero (&m); + + dReal dpos[GPB][3]; // delta-positions for composite geometries + dMatrix3 drot[GPB]; + + // set random delta positions + for (j=0; j= num) + selected = 0; + if (selected < -1) + selected = 0; + + } else if (cmd == 'd' && selected >= 0 && selected < num) { + + dBodyDisable(obj[selected].body); + + } else if (cmd == 'e' && selected >= 0 && selected < num) { + + dBodyEnable(obj[selected].body); + + } else if (cmd == 'a') { + + show_aabb = !show_aabb; + + } else if (cmd == 't') { + + show_contacts = !show_contacts; + + } else if (cmd == 'r') { + + random_pos = !random_pos; + + } else if (cmd == '1') { + + write_world = 1; + + } +} + + +// draw a geom + +void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + if (!g) + return; + if (!pos) + pos = dGeomGetPosition(g); + if (!R) + R = dGeomGetRotation(g); + + int type = dGeomGetClass(g); + if (type == dBoxClass) { + + dVector3 sides; + dGeomBoxGetLengths(g,sides); + dsDrawBox(pos,R,sides); + + } else if (type == dSphereClass) { + + dsDrawSphere(pos,R,dGeomSphereGetRadius(g)); + + } else if (type == dCapsuleClass) { + + dReal radius,length; + dGeomCapsuleGetParams(g,&radius,&length); + dsDrawCapsule(pos,R,length,radius); + + } else if (type == dConvexClass) { + + //dVector3 sides={0.50,0.50,0.50}; + dsDrawConvex(pos,R,planes, + planecount, + points, + pointcount, + polygons); + + } else if (type == dCylinderClass) { + + dReal radius,length; + dGeomCylinderGetParams(g,&radius,&length); + dsDrawCylinder(pos,R,length,radius); + + } else if (type == dTriMeshClass) { + + dTriIndex* Indices = (dTriIndex*)::Indices; + + // assume all trimeshes are drawn as bunnies + for (int ii = 0; ii < IndexCount / 3; ii++) { + const dReal v[9] = { // explicit conversion from float to dReal + Vertices[Indices[ii * 3 + 0] * 3 + 0], + Vertices[Indices[ii * 3 + 0] * 3 + 1], + Vertices[Indices[ii * 3 + 0] * 3 + 2], + Vertices[Indices[ii * 3 + 1] * 3 + 0], + Vertices[Indices[ii * 3 + 1] * 3 + 1], + Vertices[Indices[ii * 3 + 1] * 3 + 2], + Vertices[Indices[ii * 3 + 2] * 3 + 0], + Vertices[Indices[ii * 3 + 2] * 3 + 1], + Vertices[Indices[ii * 3 + 2] * 3 + 2] + }; + dsDrawTriangle(pos, R, &v[0], &v[3], &v[6], 1); + } + + } else if (type == dHeightfieldClass) { + + // Set ox and oz to zero for DHEIGHTFIELD_CORNER_ORIGIN mode. + int ox = (int) ( -HFIELD_WIDTH/2 ); + int oz = (int) ( -HFIELD_DEPTH/2 ); + + // for ( int tx = -1; tx < 2; ++tx ) + // for ( int tz = -1; tz < 2; ++tz ) + dsSetColorAlpha (0.5,1,0.5,0.5); + dsSetTexture( DS_WOOD ); + + for ( int i = 0; i < HFIELD_WSTEP - 1; ++i ) + for ( int j = 0; j < HFIELD_DSTEP - 1; ++j ) { + dReal a[3], b[3], c[3], d[3]; + + a[ 0 ] = ox + ( i ) * HFIELD_WSAMP; + a[ 1 ] = heightfield_callback( NULL, i, j ); + a[ 2 ] = oz + ( j ) * HFIELD_DSAMP; + + b[ 0 ] = ox + ( i + 1 ) * HFIELD_WSAMP; + b[ 1 ] = heightfield_callback( NULL, i + 1, j ); + b[ 2 ] = oz + ( j ) * HFIELD_DSAMP; + + c[ 0 ] = ox + ( i ) * HFIELD_WSAMP; + c[ 1 ] = heightfield_callback( NULL, i, j + 1 ); + c[ 2 ] = oz + ( j + 1 ) * HFIELD_DSAMP; + + d[ 0 ] = ox + ( i + 1 ) * HFIELD_WSAMP; + d[ 1 ] = heightfield_callback( NULL, i + 1, j + 1 ); + d[ 2 ] = oz + ( j + 1 ) * HFIELD_DSAMP; + + dsDrawTriangle( pos, R, a, c, b, 1 ); + dsDrawTriangle( pos, R, b, c, d, 1 ); + } + + } + + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB(g,aabb); + dVector3 bbpos; + for (int i=0; i<3; i++) + bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (int i=0; i<3; i++) + bbsides[i] = aabb[i*2+1] - aabb[i*2]; + dMatrix3 RI; + dRSetIdentity(RI); + dsSetColorAlpha(1,0,0,0.5); + dsDrawBox(bbpos,RI,bbsides); + } + +} + +// simulation loop + +static void simLoop (int pause) +{ + int i,j; + + dSpaceCollide(space,0,&nearCallback); + + if (!pause) + dWorldQuickStep(world,0.05); + + + if (write_world) { + FILE *f = fopen ("state.dif","wt"); + if (f) { + dWorldExportDIF(world,f,"X"); + fclose (f); + } + write_world = 0; + } + + // remove all contact joints + dJointGroupEmpty(contactgroup); + + + + // + // Draw Heightfield + // + + drawGeom(gheight, 0, 0, 0); + + + + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + + +// some constants +#define SIDE (0.5f) // side length of a box +#define MASS (1.0) // mass of a box + + +// dynamics and collision objects +static dWorldID world; +static dBodyID body[2]; +static dJointID hinge; + + +// state set by keyboard commands +static int occasional_error = 0; + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {1.0382f,-1.0811f,1.4700f}; + static float hpr[3] = {135.0000f,-19.5000f,0.0000f}; + dsSetViewpoint (xyz,hpr); + printf ("Press 'e' to start/stop occasional error.\n"); +} + + +// called when a key pressed + +static void command (int cmd) +{ + if (cmd == 'e' || cmd == 'E') { + occasional_error ^= 1; + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + const dReal kd = -0.3; // angular damping constant + if (!pause) { + // add an oscillating torque to body 0, and also damp its rotational motion + static dReal a=0; + const dReal *w = dBodyGetAngularVel (body[0]); + dBodyAddTorque (body[0],kd*w[0],kd*w[1]+0.1*cos(a),kd*w[2]+0.1*sin(a)); + dWorldStep (world,0.05); + a += 0.01; + + // occasionally re-orient one of the bodies to create a deliberate error. + if (occasional_error) { + static int count = 0; + if ((count % 20)==0) { + // randomly adjust orientation of body[0] + const dReal *R1; + dMatrix3 R2,R3; + R1 = dBodyGetRotation (body[0]); + dRFromAxisAndAngle (R2,dRandReal()-0.5,dRandReal()-0.5, + dRandReal()-0.5,dRandReal()-0.5); + dMultiply0 (R3,R1,R2,3,3,3); + dBodySetRotation (body[0],R3); + + // randomly adjust position of body[0] + const dReal *pos = dBodyGetPosition (body[0]); + dBodySetPosition (body[0], + pos[0]+0.2*(dRandReal()-0.5), + pos[1]+0.2*(dRandReal()-0.5), + pos[2]+0.2*(dRandReal()-0.5)); + } + count++; + } + } + + dReal sides1[3] = {SIDE,SIDE,SIDE}; + dReal sides2[3] = {SIDE,SIDE,SIDE*0.8f}; + dsSetTexture (DS_WOOD); + dsSetColor (1,1,0); + dsDrawBox (dBodyGetPosition(body[0]),dBodyGetRotation(body[0]),sides1); + dsSetColor (0,1,1); + dsDrawBox (dBodyGetPosition(body[1]),dBodyGetRotation(body[1]),sides2); +} + + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // create world + dInitODE2(0); + world = dWorldCreate(); + + dMass m; + dMassSetBox (&m,1,SIDE,SIDE,SIDE); + dMassAdjust (&m,MASS); + + dQuaternion q; + dQFromAxisAndAngle (q,1,1,0,0.25*M_PI); + + body[0] = dBodyCreate (world); + dBodySetMass (body[0],&m); + dBodySetPosition (body[0],0.5*SIDE,0.5*SIDE,1); + dBodySetQuaternion (body[0],q); + + body[1] = dBodyCreate (world); + dBodySetMass (body[1],&m); + dBodySetPosition (body[1],-0.5*SIDE,-0.5*SIDE,1); + dBodySetQuaternion (body[1],q); + + hinge = dJointCreateHinge (world,0); + dJointAttach (hinge,body[0],body[1]); + dJointSetHingeAnchor (hinge,0,0,1); + dJointSetHingeAxis (hinge,1,-1,1.41421356); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dWorldDestroy (world); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_jointPR.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_jointPR.cpp new file mode 100644 index 0000000..b760af1 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_jointPR.cpp @@ -0,0 +1,434 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +This file try to demonstrate how the PR joint is working. + +The axisP is draw in red and the axisR is in green + +*/ + + +#include +#include +#include +#include +#include "texturepath.h" + + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + + +// physics parameters +#define BOX1_LENGTH 2 // Size along the X axis +#define BOX1_WIDTH 1 // Size along the Y axis +#define BOX1_HEIGHT 0.4 // Size along the Z axis (up) since gravity is (0,0,-10) +#define BOX2_LENGTH 0.2 +#define BOX2_WIDTH 0.1 +#define BOX2_HEIGHT 0.4 +#define Mass1 10 +#define Mass2 0.1 + + +#define PRISMATIC_ONLY 1 +#define ROTOIDE_ONLY 2 +int flag = 0; + + +//camera view +static float xyz[3] = {2.0f,-3.5f,2.0000f}; +static float hpr[3] = {90.000f,-25.5000f,0.0000f}; +//world,space,body & geom +static dWorldID world; +static dSpaceID space; +static dSpaceID box1_space; +static dBodyID box1_body[1]; +static dBodyID box2_body[1]; +static dJointID joint[1]; +static dJointGroupID contactgroup; +static dGeomID ground; +static dGeomID box1[1]; +static dGeomID box2[1]; + + +//collision detection +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,n; + + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return; + const int N = 10; + dContact contact[N]; + n = dCollide (o1,o2,N,&contact[0].geom,sizeof(dContact)); + if (n > 0) + { + for (i=0; i= 2 ) + { + for (int i=1; i < argc; ++i) + { + if ( 0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i]) ) + Help(argv); + + if (!flag && (0 == strcmp("-p", argv[i]) ||0 == strcmp("--prismatic-only", argv[i])) ) + flag = PRISMATIC_ONLY; + + if (!flag && (0 == strcmp("-r", argv[i]) || 0 == strcmp("--rotoide-only", argv[i])) ) + flag = ROTOIDE_ONLY; + + if (0 == strcmp("-t", argv[i]) || 0 == strcmp("--texture-path", argv[i])) + { + int j = i+1; + if ( j >= argc || // Check if we have enough arguments + argv[j][0] == '\0' || // We should have a path here + argv[j][0] == '-' ) // We should have a path not a command line + Help(argv); + else + fn.path_to_textures = argv[++i]; // Increase i since we use this argument + } + } + } + + dInitODE2(0); + + // create world + world = dWorldCreate(); + space = dHashSpaceCreate (0); + contactgroup = dJointGroupCreate (0); + dWorldSetGravity (world,0,0,-10); + ground = dCreatePlane (space,0,0,1,0); + + //create two boxes + dMass m; + box1_body[0] = dBodyCreate (world); + dMassSetBox (&m,1,BOX1_LENGTH,BOX1_WIDTH,BOX1_HEIGHT); + dMassAdjust (&m,Mass1); + dBodySetMass (box1_body[0],&m); + box1[0] = dCreateBox (0,BOX1_LENGTH,BOX1_WIDTH,BOX1_HEIGHT); + dGeomSetBody (box1[0],box1_body[0]); + + box2_body[0] = dBodyCreate (world); + dMassSetBox (&m,10,BOX2_LENGTH,BOX2_WIDTH,BOX2_HEIGHT); + dMassAdjust (&m,Mass2); + dBodySetMass (box2_body[0],&m); + box2[0] = dCreateBox (0,BOX2_LENGTH,BOX2_WIDTH,BOX2_HEIGHT); + dGeomSetBody (box2[0],box2_body[0]); + + //set the initial positions of body1 and body2 + dMatrix3 R; + dRSetIdentity(R); + dBodySetPosition (box1_body[0],0,0,BOX1_HEIGHT/2.0); + dBodySetRotation (box1_body[0], R); + + dBodySetPosition (box2_body[0], + 2.1, + 0.0, + BOX2_HEIGHT/2.0); + dBodySetRotation (box2_body[0], R); + + + //set PR joint + joint[0] = dJointCreatePR(world,0); + dJointAttach (joint[0],box1_body[0],box2_body[0]); + switch (flag) + { + case PRISMATIC_ONLY: + dJointSetPRAnchor (joint[0], + 2.1, + 0.0, + BOX2_HEIGHT/2.0); + dJointSetPRParam (joint[0],dParamLoStop, -0.5); + dJointSetPRParam (joint[0],dParamHiStop, 1.5); + break; + + case ROTOIDE_ONLY: + dJointSetPRAnchor (joint[0], + 0.0, + 0.0, + BOX2_HEIGHT/2.0); + dJointSetPRParam (joint[0],dParamLoStop, 0.0); + dJointSetPRParam (joint[0],dParamHiStop, 0.0); + break; + + default: + dJointSetPRAnchor (joint[0], + 1.1, + 0.0, + BOX2_HEIGHT/2.0); + dJointSetPRParam (joint[0],dParamLoStop, -0.5); + dJointSetPRParam (joint[0],dParamHiStop, 1.5); + break; + } + + dJointSetPRAxis1(joint[0],1,0,0); + dJointSetPRAxis2(joint[0],0,0,1); +// We position the 2 body +// The position of the rotoide joint is on the second body so it can rotate on itself +// and move along the X axis. +// With this anchor +// - A force in X will move only the body 2 inside the low and hi limit +// of the prismatic +// - A force in Y will make the 2 bodies to rotate around on the plane + + box1_space = dSimpleSpaceCreate (space); + dSpaceSetCleanup (box1_space,0); + dSpaceAdd(box1_space,box1[0]); + + // run simulation + dsSimulationLoop (argc,argv,400,300,&fn); + dJointGroupDestroy (contactgroup); + dSpaceDestroy (space); + dWorldDestroy (world); + dCloseODE(); + return 0; +} + diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_jointPU.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_jointPU.cpp new file mode 100644 index 0000000..470612c --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_jointPU.cpp @@ -0,0 +1,736 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + This program demonstrates how the PU joint works. + A PU joint is a combination of a Universal joint and a Slider joint. + It is a universal joint with a slider between the anchor point and + body 1. + + + The upper yellow body is fixed to the world + The lower yellow body is attached to the upper body by a PU joint + The green object is one aprt of the slider. + The purple object is the second part of the slider. + The red object represent the axis1 of the universal part. + The blue object represent the axis2 of the universal part. + The gray object represent the anchor2 of the PU joint. +*/ + + +#include +#include +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + +enum IDX_CYL_DIM +{ + RADIUS, + LENGTH, + NUM_CYL_DIM +}; + + +const dVector3 boxDim = {1,1,1}; +const dVector3 extDim = {0.2,0.2,1.2}; +const dVector3 ancDim = {0.2,0.2,0.5}; +const dReal axDim[NUM_CYL_DIM] = {0.1,1.0}; + + +int type = dJointTypePU; + + +const dReal VEL_INC = 0.01; // Velocity increment + +// physics parameters +const dReal PI = 3.14159265358979323846264338327950288419716939937510; + + +const dReal INT_EXT_RATIO = 0.8; + +#define X 0 +#define Y 1 +#define Z 2 + +enum INDEX +{ + W = 0, + D, + EXT, + INT, + AXIS1, + AXIS2, + ANCHOR, + GROUND, + NUM_PARTS, + ALL = NUM_PARTS, + // INDEX for catBits + JOINT, + LAST_INDEX_CNT +}; + +const int catBits[LAST_INDEX_CNT] = + { + 0x0001, ///< W Cylinder category + 0x0002, ///< D Cylinder category + 0x0004, ///< EXT sliderr category + 0x0008, ///< INT slider category + 0x0010, ///< AXIS1 universal category + 0x0020, ///< AXIS2 universal category + 0x0040, ///< ANCHOR category + 0x0080, ///< Ground category + ~0L, ///< All categories + 0x0004 | 0x0008 | 0x0010 | 0x0020 ///< JOINT category + }; + +#define Mass1 10 +#define Mass2 8 + + +//camera view +static float xyz[3] = {6.0f,0.0f,6.0000f}; +static float hpr[3] = {-180.000f,-25.5000f,0.0000f}; + + +//world,space,body & geom +static dWorldID world; +static dSpaceID space; +static dJointGroupID contactgroup; +static dBodyID body[NUM_PARTS]; +static dGeomID geom[NUM_PARTS]; + +static dJoint *joint; + + + +const dReal BOX_SIDES[3] = {1.0,1.0,1.0}; +const dReal OBS_SIDES[3] = {0.4,0.4,0.4}; +const dReal RECT_SIDES[3] = {0.3, 0.1, 0.2}; + + +//collision detection +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,n; + + const int N = 10; + dContact contact[N]; + n = dCollide (o1,o2,N,&contact[0].geom,sizeof (dContact) ); + if (n > 0) { + for (i=0; igetParam (dParamVel3) - VEL_INC; + joint->setParam (dParamVel3, vel); + joint->setParam (dParamFMax3, 2); + std::cout<<"Velocity = "<' : { + dReal vel = joint->getParam (dParamVel3) + VEL_INC; + joint->setParam (dParamVel3, vel); + joint->setParam (dParamFMax3, 2); + std::cout<<"Velocity = "<getParam (dParamFMax) ) { + aLimit = dInfinity; + lLimit = dInfinity; + fmax = 0; + } + else { + aLimit = 0.25*PI; + lLimit = 0.5*axDim[LENGTH]; + fmax = 0.02; + } + + joint->setParam (dParamFMax1, fmax); + joint->setParam (dParamFMax2, fmax); + joint->setParam (dParamFMax3, fmax); + + switch (joint->getType() ) { + case dJointTypePR : { + dPRJoint *pr = reinterpret_cast (joint); + pr->setParam (dParamLoStop, -lLimit); + pr->setParam (dParamHiStop, -lLimit); + pr->setParam (dParamLoStop2, aLimit); + pr->setParam (dParamHiStop2, -aLimit); + } + break; + case dJointTypePU : { + dPUJoint *pu = reinterpret_cast (joint); + pu->setParam (dParamLoStop1, -aLimit); + pu->setParam (dParamHiStop1, aLimit); + pu->setParam (dParamLoStop2, -aLimit); + pu->setParam (dParamHiStop2, aLimit); + pu->setParam (dParamLoStop3, -lLimit); + pu->setParam (dParamHiStop3, lLimit); + } + break; + default: {} // keep the compiler happy + } + } + + break; + + case 'g': case 'G' : { + dVector3 g; + dWorldGetGravity(world, g); + if ( g[2]< -0.1 ) + dWorldSetGravity(world, 0, 0, 0); + else + dWorldSetGravity(world, 0, 0, -0.5); + + } + break; + + case 'p' :case 'P' : { + switch (joint->getType() ) { + case dJointTypeSlider : { + dSliderJoint *sj = reinterpret_cast (joint); + std::cout<<"Position ="<getPosition() <<"\n"; + } + break; + case dJointTypePU : { + dPUJoint *pu = reinterpret_cast (joint); + std::cout<<"Position ="<getPosition() <<"\n"; + std::cout<<"Position Rate="<getPositionRate() <<"\n"; + std::cout<<"Angle1 ="<getAngle1() <<"\n"; + std::cout<<"Angle1 Rate="<getAngle1Rate() <<"\n"; + std::cout<<"Angle2 ="<getAngle2() <<"\n"; + std::cout<<"Angle2 Rate="<getAngle2Rate() <<"\n"; + } + break; + default: {} // keep the compiler happy + } + } + break; + } +} + +static void drawBox (dGeomID id, int R, int G, int B) +{ + if (!id) + return; + + const dReal *pos = dGeomGetPosition (id); + const dReal *rot = dGeomGetRotation (id); + dsSetColor (R,G,B); + + dVector3 l; + dGeomBoxGetLengths (id, l); + dsDrawBox (pos, rot, l); +} + + +// simulation loop +static void simLoop (int pause) +{ + static bool todo = false; + if ( todo ) { // DEBUG + static int cnt = 0; + ++cnt; + + if (cnt == 5) + command ( 'q' ); + if (cnt == 10) + dsStop(); + } + + + + + if (!pause) { + double simstep = 0.01; // 10ms simulation steps + double dt = dsElapsedTime(); + + int nrofsteps = (int) ceilf (dt/simstep); + if (!nrofsteps) + nrofsteps = 1; + + for (int i=0; i (joint); + ang1 = pu->getAngle1(); + ang2 = pu->getAngle2(); + pu->getAxis1 (axisR1); + pu->getAxis2 (axisR2); + pu->getAxisP (axisP); + + dJointGetPUAnchor (pu->id(), anchorPos); + } + else if ( dJointTypePR == type ) { + dPRJoint *pr = dynamic_cast (joint); + pr->getAxis1 (axisP); + pr->getAxis2 (axisR1); + + dJointGetPRAnchor (pr->id(), anchorPos); + } + + + // Draw the axisR + if ( geom[INT] ) { + dsSetColor (1,0,1); + dVector3 l; + dGeomBoxGetLengths (geom[INT], l); + + const dReal *rotBox = dGeomGetRotation (geom[W]); + + dVector3 pos; + for (int i=0; i<3; ++i) + pos[i] = anchorPos[i] - 0.5*extDim[Z]*axisP[i]; + dsDrawBox (pos, rotBox, l); + } + + dsSetTexture (DS_CHECKERED); + if ( geom[AXIS1] ) { + dQuaternion q, qAng; + dQFromAxisAndAngle (qAng,axisR1[X], axisR1[Y], axisR1[Z], ang1); + dGeomGetQuaternion (geom[AXIS1], q); + + dQuaternion qq; + dQMultiply1 (qq, qAng, q); + dMatrix3 R; + dQtoR (qq,R); + + + dGeomCylinderGetParams (geom[AXIS1], &radius, &length); + dsSetColor (1,0,0); + dsDrawCylinder (anchorPos, R, length, radius); + } + + if ( dJointTypePU == type && geom[AXIS2] ) { + //dPUJoint *pu = dynamic_cast (joint); + + dQuaternion q, qAng, qq, qq1; + dGeomGetQuaternion (geom[AXIS2], q); + + dQFromAxisAndAngle (qAng, 0, 1, 0, ang2); + dQMultiply1 (qq, qAng, q); + + + dQFromAxisAndAngle (qAng,axisR1[X], axisR1[Y], axisR1[Z], ang1); + + dQMultiply1 (qq1, qAng, qq); + + + dMatrix3 R; + dQtoR (qq1,R); + + + dGeomCylinderGetParams (geom[AXIS2], &radius, &length); + dsSetColor (0,0,1); + dsDrawCylinder (anchorPos, R, length, radius); + } + + dsSetTexture (DS_WOOD); + + // Draw the anchor + if ( geom[ANCHOR] ) { + dsSetColor (1,1,1); + dVector3 l; + dGeomBoxGetLengths (geom[ANCHOR], l); + + const dReal *rotBox = dGeomGetRotation (geom[D]); + const dReal *posBox = dGeomGetPosition (geom[D]); + + dVector3 e; + for (int i=0; i<3; ++i) + e[i] = posBox[i] - anchorPos[i]; + dNormalize3 (e); + + dVector3 pos; + for (int i=0; i<3; ++i) + pos[i] = anchorPos[i] + 0.5 * l[Z]*e[i]; + dsDrawBox (pos, rotBox, l); + } + + drawBox (geom[D], 1,1,0); + } +} + + +void Help (char **argv) +{ + printf ("%s ", argv[0]); + printf (" -h | --help : print this help\n"); + printf (" -p | --PRJoint : Use a PR joint instead of PU joint\n"); + printf (" -t | --texture-path path : Path to the texture.\n"); + printf (" Default = %s\n", DRAWSTUFF_TEXTURE_PATH); + printf ("--------------------------------------------------\n"); + printf ("Hit any key to continue:"); + getchar(); + + exit (0); +} + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + if (argc >= 2 ) { + for (int i=1; i < argc; ++i) { + if ( 0 == strcmp ("-h", argv[i]) || 0 == strcmp ("--help", argv[i]) ) + Help (argv); + + if ( 0 == strcmp ("-p", argv[i]) || 0 == strcmp ("--PRJoint", argv[i]) ) + type = dJointTypePR; + + if (0 == strcmp ("-t", argv[i]) || 0 == strcmp ("--texture-path", argv[i]) ) { + int j = i+1; + if ( j >= argc || // Check if we have enough arguments + argv[j][0] == '\0' || // We should have a path here + argv[j][0] == '-' ) // We should have a path not a command line + Help (argv); + else + fn.path_to_textures = argv[++i]; // Increase i since we use this argument + } + } + } + + dInitODE2(0); + + world = dWorldCreate(); + dWorldSetERP (world, 0.8); + + space = dSimpleSpaceCreate (0); + contactgroup = dJointGroupCreate (0); + geom[GROUND] = dCreatePlane (space, 0,0,1,0); + dGeomSetCategoryBits (geom[GROUND], catBits[GROUND]); + dGeomSetCollideBits (geom[GROUND], catBits[ALL]); + + dMass m; + + // Create the body attached to the World + body[W] = dBodyCreate (world); + // Main axis of cylinder is along X=1 + m.setBox (1, boxDim[X], boxDim[Y], boxDim[Z]); + m.adjust (Mass1); + geom[W] = dCreateBox (space, boxDim[X], boxDim[Y], boxDim[Z]); + dGeomSetBody (geom[W], body[W]); + dGeomSetCategoryBits (geom[W], catBits[W]); + dGeomSetCollideBits (geom[W], catBits[ALL] & (~catBits[W]) & (~catBits[JOINT]) ); + dBodySetMass (body[W], &m); + + + + + + // Create the dandling body + body[D] = dBodyCreate (world); + // Main axis of capsule is along X=1 + m.setBox (1, boxDim[X], boxDim[Y], boxDim[Z]); + m.adjust (Mass1); + geom[D] = dCreateBox (space, boxDim[X], boxDim[Y], boxDim[Z]); + dGeomSetBody (geom[D], body[D]); + dGeomSetCategoryBits (geom[D], catBits[D]); + dGeomSetCollideBits (geom[D], catBits[ALL] & (~catBits[D]) & (~catBits[JOINT]) ); + dBodySetMass (body[D], &m); + + + // Create the external part of the slider joint + geom[EXT] = dCreateBox (0, extDim[X], extDim[Y], extDim[Z]); + dGeomSetCategoryBits (geom[EXT], catBits[EXT]); + dGeomSetCollideBits (geom[EXT], + catBits[ALL] & (~catBits[JOINT]) & (~catBits[W]) & (~catBits[D]) ); + + // Create the internal part of the slider joint + geom[INT] = dCreateBox (0, INT_EXT_RATIO*extDim[X], + INT_EXT_RATIO*extDim[Y], + INT_EXT_RATIO*extDim[Z]); + dGeomSetCategoryBits (geom[INT], catBits[INT]); + dGeomSetCollideBits (geom[INT], + catBits[ALL] & (~catBits[JOINT]) & (~catBits[W]) & (~catBits[D]) ); + + + dMatrix3 R; + // Create the first axis of the universal joi9nt + //Rotation of 90deg around y + geom[AXIS1] = dCreateCylinder(0, axDim[RADIUS], axDim[LENGTH]); + dRFromAxisAndAngle(R, 0,1,0, 0.5*PI); + dGeomSetRotation(geom[AXIS1], R); + dGeomSetCategoryBits(geom[AXIS1], catBits[AXIS1]); + dGeomSetCollideBits(geom[AXIS1], + catBits[ALL] & ~catBits[JOINT] & ~catBits[W] & ~catBits[D]); + + + // Create the second axis of the universal joint + geom[AXIS2] = dCreateCylinder(0, axDim[RADIUS], axDim[LENGTH]); + //Rotation of 90deg around y + dRFromAxisAndAngle(R, 1,0,0, 0.5*PI); + dGeomSetRotation(geom[AXIS2], R); + dGeomSetCategoryBits(geom[AXIS2], catBits[AXIS2]); + dGeomSetCollideBits(geom[AXIS2], + catBits[ALL] & ~catBits[JOINT] & ~catBits[W] & ~catBits[D]); + + // Create the anchor + geom[ANCHOR] = dCreateBox (0, ancDim[X], ancDim[Y], ancDim[Z]); + dGeomSetCategoryBits(geom[ANCHOR], catBits[ANCHOR]); + dGeomSetCollideBits(geom[ANCHOR], + catBits[ALL] & (~catBits[JOINT]) & (~catBits[W]) & (~catBits[D]) ); + + + + if (body[W]) { + dBodySetPosition(body[W], 0, 0, 5); + } + + + if (geom[EXT]) { + dGeomSetPosition(geom[EXT], 0,0,3.8); + } + if (geom[INT]) { + dGeomSetPosition(geom[INT], 0,0,2.6); + } + if (geom[AXIS1]) { + dGeomSetPosition(geom[AXIS1], 0,0,2.5); + } + if (geom[AXIS2]) { + dGeomSetPosition(geom[AXIS2], 0,0,2.5); + } + + if (geom[ANCHOR]) { + dGeomSetPosition(geom[ANCHOR], 0,0,2.25); + } + + if (body[D]) { + dBodySetPosition(body[D], 0,0,1.5); + } + + + + // Attache the upper box to the world + dJointID fixed = dJointCreateFixed (world,0); + dJointAttach (fixed , NULL, body[W]); + dJointSetFixed (fixed ); + + if (type == dJointTypePR) { + dPRJoint *pr = new dPRJoint (world, 0); + pr->attach (body[W], body[D]); + pr->setAxis1 (0, 0, -1); + pr->setAxis2 (1, 0, 0); + joint = pr; + + dJointSetPRAnchor (pr->id(), 0, 0, 2.5); + } + else { + dPUJoint *pu = new dPUJoint (world, 0); + pu->attach (body[W], body[D]); + pu->setAxis1 (1, 0, 0); + pu->setAxis2 (0, 1, 0); + pu->setAxisP (0, 0, -1); + joint = pu; + + dJointSetPUAnchor (pu->id(), 0, 0, 2.5); + } + + + // run simulation + dsSimulationLoop (argc,argv,400,300,&fn); + + delete joint; + dJointGroupDestroy (contactgroup); + dSpaceDestroy (space); + dWorldDestroy (world); + dCloseODE(); + return 0; +} + diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_joints.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_joints.cpp new file mode 100644 index 0000000..d545369 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_joints.cpp @@ -0,0 +1,1090 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +perform tests on all the joint types. +this should be done using the double precision version of the library. + +usage: + test_joints [-nXXX] [-g] [-i] [-e] [path_to_textures] + +if a test number is given then that specific test is performed, otherwise +all the tests are performed. the tests are numbered `xxyy', where xx +corresponds to the joint type and yy is the sub-test number. not every +number maps to an actual test. + +flags: + i: the test is interactive. + g: turn off graphical display (can't use this with `i'). + e: turn on occasional error perturbations + n: performe test XXX +some tests compute and display error values. these values are scaled so +<1 is good and >1 is bad. other tests just show graphical results which +you must verify visually. + +*/ + +#include +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + + +// some constants +#define NUM_JOINTS 10 // number of joints to test (the `xx' value) +#define SIDE (0.5f) // side length of a box - don't change this +#define MASS (1.0) // mass of a box +#define STEPSIZE 0.05 + +static const dVector3 xunit = { 1, 0, 0 }, zunit = { 0, 0, 1 }; + + +// dynamics objects +static dWorldID world; +static dBodyID body[2]; +static dJointID joint; + + +// data from the command line arguments +static int cmd_test_num = -1; +static int cmd_interactive = 0; +static int cmd_graphics = 1; +static char *cmd_path_to_textures = NULL; +static int cmd_occasional_error = 0; // perturb occasionally + + +// info about the current test +struct TestInfo; +static int test_num = 0; // number of the current test +static int iteration = 0; +static int max_iterations = 0; +static dReal max_error = 0; + +//**************************************************************************** +// utility stuff + +static dReal length (dVector3 a) +{ + return dSqrt (a[0]*a[0] + a[1]*a[1] + a[2]*a[2]); +} + + +// get the max difference between a 3x3 matrix and the identity + +dReal cmpIdentity (const dMatrix3 A) +{ + dMatrix3 I; + dSetZero (I,12); + I[0] = 1; + I[5] = 1; + I[10] = 1; + return dMaxDifference (A,I,3,3); +} + +//**************************************************************************** +// test world construction and utilities + +void constructWorldForTest (dReal gravity, int bodycount, + /* body 1 pos */ dReal pos1x, dReal pos1y, dReal pos1z, + /* body 2 pos */ dReal pos2x, dReal pos2y, dReal pos2z, + /* body 1 rotation axis */ dReal ax1x, dReal ax1y, dReal ax1z, + /* body 1 rotation axis */ dReal ax2x, dReal ax2y, dReal ax2z, + /* rotation angles */ dReal a1, dReal a2) +{ + // create world + world = dWorldCreate(); + dWorldSetERP (world,0.2); + dWorldSetCFM (world,1e-6); + dWorldSetGravity (world,0,0,gravity); + + dMass m; + dMassSetBox (&m,1,SIDE,SIDE,SIDE); + dMassAdjust (&m,MASS); + + body[0] = dBodyCreate (world); + dBodySetMass (body[0],&m); + dBodySetPosition (body[0], pos1x, pos1y, pos1z); + dQuaternion q; + dQFromAxisAndAngle (q,ax1x,ax1y,ax1z,a1); + dBodySetQuaternion (body[0],q); + + if (bodycount==2) { + body[1] = dBodyCreate (world); + dBodySetMass (body[1],&m); + dBodySetPosition (body[1], pos2x, pos2y, pos2z); + dQFromAxisAndAngle (q,ax2x,ax2y,ax2z,a2); + dBodySetQuaternion (body[1],q); + } + else body[1] = 0; +} + + +// add an oscillating torque to body 0 + +void addOscillatingTorque (dReal tscale) +{ + static dReal a=0; + dBodyAddTorque (body[0],tscale*cos(2*a),tscale*cos(2.7183*a), + tscale*cos(1.5708*a)); + a += 0.01; +} + + +void addOscillatingTorqueAbout(dReal tscale, dReal x, dReal y, dReal z) +{ + static dReal a=0; + dBodyAddTorque (body[0], tscale*cos(a) * x, tscale*cos(a) * y, + tscale * cos(a) * z); + a += 0.02; +} + + +// damp the rotational motion of body 0 a bit + +void dampRotationalMotion (dReal kd) +{ + const dReal *w = dBodyGetAngularVel (body[0]); + dBodyAddTorque (body[0],-kd*w[0],-kd*w[1],-kd*w[2]); +} + + +// add a spring force to keep the bodies together, otherwise they may fly +// apart with some joints. + +void addSpringForce (dReal ks) +{ + const dReal *p1 = dBodyGetPosition (body[0]); + const dReal *p2 = dBodyGetPosition (body[1]); + dBodyAddForce (body[0],ks*(p2[0]-p1[0]),ks*(p2[1]-p1[1]),ks*(p2[2]-p1[2])); + dBodyAddForce (body[1],ks*(p1[0]-p2[0]),ks*(p1[1]-p2[1]),ks*(p1[2]-p2[2])); +} + + +// add an oscillating Force to body 0 + +void addOscillatingForce (dReal fscale) +{ + static dReal a=0; + dBodyAddForce (body[0],fscale*cos(2*a),fscale*cos(2.7183*a), + fscale*cos(1.5708*a)); + a += 0.01; +} + +//**************************************************************************** +// stuff specific to the tests +// +// 0xx : fixed +// 1xx : ball and socket +// 2xx : hinge +// 3xx : slider +// 4xx : hinge 2 +// 5xx : contact +// 6xx : amotor +// 7xx : universal joint +// 8xx : PR joint (Prismatic and Rotoide) + +// setup for the given test. return 0 if there is no such test + +int setupTest (int n) +{ + switch (n) { + + // ********** fixed joint + + case 0: { // 2 body + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,1,0, 1,1,0, + 0.25*M_PI,0.25*M_PI); + joint = dJointCreateFixed (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetFixed (joint); + return 1; + } + + case 1: { // 1 body to static env + constructWorldForTest (0,1, + 0.5*SIDE,0.5*SIDE,1, 0,0,0, + 1,0,0, 1,0,0, + 0,0); + joint = dJointCreateFixed (world,0); + dJointAttach (joint,body[0],0); + dJointSetFixed (joint); + return 1; + } + + case 2: { // 2 body with relative rotation + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,1,0, 1,1,0, + 0.25*M_PI,-0.25*M_PI); + joint = dJointCreateFixed (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetFixed (joint); + return 1; + } + + case 3: { // 1 body to static env with relative rotation + constructWorldForTest (0,1, + 0.5*SIDE,0.5*SIDE,1, 0,0,0, + 1,0,0, 1,0,0, + 0.25*M_PI,0); + joint = dJointCreateFixed (world,0); + dJointAttach (joint,body[0],0); + dJointSetFixed (joint); + return 1; + } + + // ********** hinge joint + + case 200: // 2 body + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,1,0, 1,1,0, 0.25*M_PI,0.25*M_PI); + joint = dJointCreateHinge (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHingeAnchor (joint,0,0,1); + dJointSetHingeAxis (joint,1,-1,1.41421356); + return 1; + + case 220: // hinge angle polarity test + case 221: // hinge angle rate test + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateHinge (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHingeAnchor (joint,0,0,1); + dJointSetHingeAxis (joint,0,0,1); + max_iterations = 50; + return 1; + + case 230: // hinge motor rate (and polarity) test + case 231: // ...with stops + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateHinge (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHingeAnchor (joint,0,0,1); + dJointSetHingeAxis (joint,0,0,1); + dJointSetHingeParam (joint,dParamFMax,1); + if (n==231) { + dJointSetHingeParam (joint,dParamLoStop,-0.5); + dJointSetHingeParam (joint,dParamHiStop,0.5); + } + return 1; + + case 250: // limit bounce test (gravity down) + case 251: { // ...gravity up + constructWorldForTest ((n==251) ? 0.1 : -0.1, 2, + 0.5*SIDE,0,1+0.5*SIDE, -0.5*SIDE,0,1-0.5*SIDE, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateHinge (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHingeAnchor (joint,0,0,1); + dJointSetHingeAxis (joint,0,1,0); + dJointSetHingeParam (joint,dParamLoStop,-0.9); + dJointSetHingeParam (joint,dParamHiStop,0.7854); + dJointSetHingeParam (joint,dParamBounce,0.5); + // anchor 2nd body with a fixed joint + dJointID j = dJointCreateFixed (world,0); + dJointAttach (j,body[1],0); + dJointSetFixed (j); + return 1; + } + + // ********** slider + + case 300: // 2 body + constructWorldForTest (0,2, + 0,0,1, 0.2,0.2,1.2, + 0,0,1, -1,1,0, 0,0.25*M_PI); + joint = dJointCreateSlider (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetSliderAxis (joint,1,1,1); + return 1; + + case 320: // slider angle polarity test + case 321: // slider angle rate test + constructWorldForTest (0,2, + 0,0,1, 0,0,1.2, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateSlider (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetSliderAxis (joint,0,0,1); + max_iterations = 50; + return 1; + + case 330: // slider motor rate (and polarity) test + case 331: // ...with stops + constructWorldForTest (0, 2, + 0,0,1, 0,0,1.2, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateSlider (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetSliderAxis (joint,0,0,1); + dJointSetSliderParam (joint,dParamFMax,100); + if (n==331) { + dJointSetSliderParam (joint,dParamLoStop,-0.4); + dJointSetSliderParam (joint,dParamHiStop,0.4); + } + return 1; + + case 350: // limit bounce tests + case 351: { + constructWorldForTest ((n==351) ? 0.1 : -0.1, 2, + 0,0,1, 0,0,1.2, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateSlider (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetSliderAxis (joint,0,0,1); + dJointSetSliderParam (joint,dParamLoStop,-0.5); + dJointSetSliderParam (joint,dParamHiStop,0.5); + dJointSetSliderParam (joint,dParamBounce,0.5); + // anchor 2nd body with a fixed joint + dJointID j = dJointCreateFixed (world,0); + dJointAttach (j,body[1],0); + dJointSetFixed (j); + return 1; + } + + // ********** hinge-2 joint + + case 420: // hinge-2 steering angle polarity test + case 421: // hinge-2 steering angle rate test + constructWorldForTest (0,2, + 0.5*SIDE,0,1, -0.5*SIDE,0,1, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateHinge2 (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHinge2Anchor (joint,-0.5*SIDE,0,1); + dJointSetHinge2Axes (joint, zunit, xunit); + max_iterations = 50; + return 1; + + case 430: // hinge 2 steering motor rate (+polarity) test + case 431: // ...with stops + case 432: // hinge 2 wheel motor rate (+polarity) test + constructWorldForTest (0,2, + 0.5*SIDE,0,1, -0.5*SIDE,0,1, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateHinge2 (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetHinge2Anchor (joint,-0.5*SIDE,0,1); + dJointSetHinge2Axes (joint, zunit, xunit); + dJointSetHinge2Param (joint,dParamFMax,1); + dJointSetHinge2Param (joint,dParamFMax2,1); + if (n==431) { + dJointSetHinge2Param (joint,dParamLoStop,-0.5); + dJointSetHinge2Param (joint,dParamHiStop,0.5); + } + return 1; + + // ********** angular motor joint + + case 600: // test euler angle calculations + constructWorldForTest (0,2, + -SIDE*0.5,0,1, SIDE*0.5,0,1, + 0,0,1, 0,0,1, 0,0); + joint = dJointCreateAMotor (world,0); + dJointAttach (joint,body[0],body[1]); + + dJointSetAMotorNumAxes (joint,3); + dJointSetAMotorAxis (joint,0,1, 0,0,1); + dJointSetAMotorAxis (joint,2,2, 1,0,0); + dJointSetAMotorMode (joint,dAMotorEuler); + max_iterations = 200; + return 1; + + // ********** universal joint + + case 700: // 2 body + case 701: + case 702: + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,1,0, 1,1,0, 0.25*M_PI,0.25*M_PI); + joint = dJointCreateUniversal (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetUniversalAnchor (joint,0,0,1); + dJointSetUniversalAxis1 (joint, 1, -1, 1.41421356); + dJointSetUniversalAxis2 (joint, 1, -1, -1.41421356); + return 1; + + case 720: // universal transmit torque test + case 721: + case 722: + case 730: // universal torque about axis 1 + case 731: + case 732: + case 740: // universal torque about axis 2 + case 741: + case 742: + constructWorldForTest (0,2, + 0.5*SIDE,0.5*SIDE,1, -0.5*SIDE,-0.5*SIDE,1, + 1,0,0, 1,0,0, 0,0); + joint = dJointCreateUniversal (world,0); + dJointAttach (joint,body[0],body[1]); + dJointSetUniversalAnchor (joint,0,0,1); + dJointSetUniversalAxis1 (joint,0,0,1); + dJointSetUniversalAxis2 (joint, 1, -1,0); + max_iterations = 100; + return 1; + + // Joint PR (Prismatic and Rotoide) + case 800: // 2 body + case 801: // 2 bodies with spring force and prismatic fixed + case 802: // 2 bodies with torque on body1 and prismatic fixed + constructWorldForTest (0, 2, + -1.0, 0.0, 1.0, + 1.0, 0.0, 1.0, + 1,0,0, 1,0,0, + 0, 0); + joint = dJointCreatePR (world, 0); + dJointAttach (joint, body[0], body[1]); + dJointSetPRAnchor (joint,-0.5, 0.0, 1.0); + dJointSetPRAxis1 (joint, 0, 1, 0); + dJointSetPRAxis2 (joint, 1, 0, 0); + dJointSetPRParam (joint,dParamLoStop,-0.5); + dJointSetPRParam (joint,dParamHiStop,0.5); + dJointSetPRParam (joint,dParamLoStop2,0); + dJointSetPRParam (joint,dParamHiStop2,0); + return 1; + case 803: // 2 bodies with spring force and prismatic NOT fixed + case 804: // 2 bodies with torque force and prismatic NOT fixed + case 805: // 2 bodies with force only on first body + constructWorldForTest (0, 2, + -1.0, 0.0, 1.0, + 1.0, 0.0, 1.0, + 1,0,0, 1,0,0, + 0, 0); + joint = dJointCreatePR (world, 0); + dJointAttach (joint, body[0], body[1]); + dJointSetPRAnchor (joint,-0.5, 0.0, 1.0); + dJointSetPRAxis1 (joint, 0, 1, 0); + dJointSetPRAxis2 (joint, 1, 0, 0); + dJointSetPRParam (joint,dParamLoStop,-0.5); + dJointSetPRParam (joint,dParamHiStop,0.5); + dJointSetPRParam (joint,dParamLoStop2,-0.5); + dJointSetPRParam (joint,dParamHiStop2,0.5); + return 1; + } + return 0; +} + + +// do stuff specific to this test each iteration. you can check some +// invariants for the test -- the return value is some scaled error measurement +// that must be less than 1. +// return a dInfinity if error is not measured for this n. + +dReal doStuffAndGetError (int n) +{ + switch (n) { + + // ********** fixed joint + + case 0: { // 2 body + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + // check the orientations are the same + const dReal *R1 = dBodyGetRotation (body[0]); + const dReal *R2 = dBodyGetRotation (body[1]); + dReal err1 = dMaxDifference (R1,R2,3,3); + // check the body offset is correct + dVector3 p,pp; + const dReal *p1 = dBodyGetPosition (body[0]); + const dReal *p2 = dBodyGetPosition (body[1]); + for (int i=0; i<3; i++) p[i] = p2[i] - p1[i]; + dMultiply1_331 (pp,R1,p); + pp[0] += 0.5; + pp[1] += 0.5; + return (err1 + length (pp)) * 300; + } + + case 1: { // 1 body to static env + addOscillatingTorque (0.1); + + // check the orientation is the identity + dReal err1 = cmpIdentity (dBodyGetRotation (body[0])); + + // check the body offset is correct + dVector3 p; + const dReal *p1 = dBodyGetPosition (body[0]); + for (int i=0; i<3; i++) p[i] = p1[i]; + p[0] -= 0.25; + p[1] -= 0.25; + p[2] -= 1; + return (err1 + length (p)) * 1e6; + } + + case 2: { // 2 body + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + // check the body offset is correct + // Should really check body rotation too. Oh well. + const dReal *R1 = dBodyGetRotation (body[0]); + dVector3 p,pp; + const dReal *p1 = dBodyGetPosition (body[0]); + const dReal *p2 = dBodyGetPosition (body[1]); + for (int i=0; i<3; i++) p[i] = p2[i] - p1[i]; + dMultiply1_331 (pp,R1,p); + pp[0] += 0.5; + pp[1] += 0.5; + return length(pp) * 300; + } + + case 3: { // 1 body to static env with relative rotation + addOscillatingTorque (0.1); + + // check the body offset is correct + dVector3 p; + const dReal *p1 = dBodyGetPosition (body[0]); + for (int i=0; i<3; i++) p[i] = p1[i]; + p[0] -= 0.25; + p[1] -= 0.25; + p[2] -= 1; + return length (p) * 1e6; + } + + + // ********** hinge joint + + case 200: // 2 body + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + return dInfinity; + + case 220: // hinge angle polarity test + dBodyAddTorque (body[0],0,0,0.01); + dBodyAddTorque (body[1],0,0,-0.01); + if (iteration == 40) { + dReal a = dJointGetHingeAngle (joint); + if (a > 0.5 && a < 1) return 0; else return 10; + } + return 0; + + case 221: { // hinge angle rate test + static dReal last_angle = 0; + dBodyAddTorque (body[0],0,0,0.01); + dBodyAddTorque (body[1],0,0,-0.01); + dReal a = dJointGetHingeAngle (joint); + dReal r = dJointGetHingeAngleRate (joint); + dReal er = (a-last_angle)/STEPSIZE; // estimated rate + last_angle = a; + return fabs(r-er) * 4e4; + } + + case 230: // hinge motor rate (and polarity) test + case 231: { // ...with stops + static dReal a = 0; + dReal r = dJointGetHingeAngleRate (joint); + dReal err = fabs (cos(a) - r); + if (a==0) err = 0; + a += 0.03; + dJointSetHingeParam (joint,dParamVel,cos(a)); + if (n==231) return dInfinity; + return err * 1e6; + } + + // ********** slider joint + + case 300: // 2 body + addOscillatingTorque (0.05); + dampRotationalMotion (0.1); + addSpringForce (0.5); + return dInfinity; + + case 320: // slider angle polarity test + dBodyAddForce (body[0],0,0,0.1); + dBodyAddForce (body[1],0,0,-0.1); + if (iteration == 40) { + dReal a = dJointGetSliderPosition (joint); + if (a > 0.2 && a < 0.5) + return 0; + else + return 10; // Failed + } + return 0; + + case 321: { // slider angle rate test + static dReal last_pos = 0; + dBodyAddForce (body[0],0,0,0.1); + dBodyAddForce (body[1],0,0,-0.1); + dReal p = dJointGetSliderPosition (joint); + dReal r = dJointGetSliderPositionRate (joint); + dReal er = (p-last_pos)/STEPSIZE; // estimated rate (almost exact) + last_pos = p; + return fabs(r-er) * 1e9; + } + + case 330: // slider motor rate (and polarity) test + case 331: { // ...with stops + static dReal a = 0; + dReal r = dJointGetSliderPositionRate (joint); + dReal err = fabs (0.7*cos(a) - r); + if (a < 0.04) err = 0; + a += 0.03; + dJointSetSliderParam (joint,dParamVel,0.7*cos(a)); + if (n==331) return dInfinity; + return err * 1e6; + } + + // ********** hinge-2 joint + + case 420: // hinge-2 steering angle polarity test + dBodyAddTorque (body[0],0,0,0.01); + dBodyAddTorque (body[1],0,0,-0.01); + if (iteration == 40) { + dReal a = dJointGetHinge2Angle1 (joint); + if (a > 0.5 && a < 0.6) return 0; else return 10; + } + return 0; + + case 421: { // hinge-2 steering angle rate test + static dReal last_angle = 0; + dBodyAddTorque (body[0],0,0,0.01); + dBodyAddTorque (body[1],0,0,-0.01); + dReal a = dJointGetHinge2Angle1 (joint); + dReal r = dJointGetHinge2Angle1Rate (joint); + dReal er = (a-last_angle)/STEPSIZE; // estimated rate + last_angle = a; + return fabs(r-er)*2e4; + } + + case 430: // hinge 2 steering motor rate (+polarity) test + case 431: { // ...with stops + static dReal a = 0; + dReal r = dJointGetHinge2Angle1Rate (joint); + dReal err = fabs (cos(a) - r); + if (a==0) err = 0; + a += 0.03; + dJointSetHinge2Param (joint,dParamVel,cos(a)); + if (n==431) return dInfinity; + return err * 1e6; + } + + case 432: { // hinge 2 wheel motor rate (+polarity) test + static dReal a = 0; + dReal r = dJointGetHinge2Angle2Rate (joint); + dReal err = fabs (cos(a) - r); + if (a==0) err = 0; + a += 0.03; + dJointSetHinge2Param (joint,dParamVel2,cos(a)); + return err * 1e6; + } + + // ********** angular motor joint + + case 600: { // test euler angle calculations + // desired euler angles from last iteration + static dReal a1,a2,a3; + + // find actual euler angles + dReal aa1 = dJointGetAMotorAngle (joint,0); + dReal aa2 = dJointGetAMotorAngle (joint,1); + dReal aa3 = dJointGetAMotorAngle (joint,2); + // printf ("actual = %.4f %.4f %.4f\n\n",aa1,aa2,aa3); + + dReal err = dInfinity; + if (iteration > 0) { + err = dFabs(aa1-a1) + dFabs(aa2-a2) + dFabs(aa3-a3); + err *= 1e10; + } + + // get random base rotation for both bodies + dMatrix3 Rbase; + dRFromAxisAndAngle (Rbase, 3*(dRandReal()-0.5), 3*(dRandReal()-0.5), + 3*(dRandReal()-0.5), 3*(dRandReal()-0.5)); + dBodySetRotation (body[0],Rbase); + + // rotate body 2 by random euler angles w.r.t. body 1 + a1 = 3.14 * 2 * (dRandReal()-0.5); + a2 = 1.57 * 2 * (dRandReal()-0.5); + a3 = 3.14 * 2 * (dRandReal()-0.5); + dMatrix3 R1,R2,R3,Rtmp1,Rtmp2; + dRFromAxisAndAngle (R1,0,0,1,-a1); + dRFromAxisAndAngle (R2,0,1,0,a2); + dRFromAxisAndAngle (R3,1,0,0,-a3); + dMultiply0 (Rtmp1,R2,R3,3,3,3); + dMultiply0 (Rtmp2,R1,Rtmp1,3,3,3); + dMultiply0 (Rtmp1,Rbase,Rtmp2,3,3,3); + dBodySetRotation (body[1],Rtmp1); + // printf ("desired = %.4f %.4f %.4f\n",a1,a2,a3); + + return err; + } + + // ********** universal joint + + case 700: { // 2 body: joint constraint + dVector3 ax1, ax2; + + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + dJointGetUniversalAxis1(joint, ax1); + dJointGetUniversalAxis2(joint, ax2); + return fabs(10*dCalcVectorDot3(ax1, ax2)); + } + + case 701: { // 2 body: angle 1 rate + static dReal last_angle = 0; + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle1(joint); + dReal r = dJointGetUniversalAngle1Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + // I'm not sure why the error is so large here. + return fabs(r - er) * 1e1; + } + + case 702: { // 2 body: angle 2 rate + static dReal last_angle = 0; + addOscillatingTorque (0.1); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle2(joint); + dReal r = dJointGetUniversalAngle2Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + // I'm not sure why the error is so large here. + return fabs(r - er) * 1e1; + } + + case 720: { // universal transmit torque test: constraint error + dVector3 ax1, ax2; + addOscillatingTorqueAbout (0.1, 1, 1, 0); + dampRotationalMotion (0.1); + dJointGetUniversalAxis1(joint, ax1); + dJointGetUniversalAxis2(joint, ax2); + return fabs(10*dCalcVectorDot3(ax1, ax2)); + } + + case 721: { // universal transmit torque test: angle1 rate + static dReal last_angle = 0; + addOscillatingTorqueAbout (0.1, 1, 1, 0); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle1(joint); + dReal r = dJointGetUniversalAngle1Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 1e10; + } + + case 722: { // universal transmit torque test: angle2 rate + static dReal last_angle = 0; + addOscillatingTorqueAbout (0.1, 1, 1, 0); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle2(joint); + dReal r = dJointGetUniversalAngle2Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 1e10; + } + + case 730:{ + dVector3 ax1, ax2; + dJointGetUniversalAxis1(joint, ax1); + dJointGetUniversalAxis2(joint, ax2); + addOscillatingTorqueAbout (0.1, ax1[0], ax1[1], ax1[2]); + dampRotationalMotion (0.1); + return fabs(10*dCalcVectorDot3(ax1, ax2)); + } + + case 731:{ + dVector3 ax1; + static dReal last_angle = 0; + dJointGetUniversalAxis1(joint, ax1); + addOscillatingTorqueAbout (0.1, ax1[0], ax1[1], ax1[2]); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle1(joint); + dReal r = dJointGetUniversalAngle1Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 2e3; + } + + case 732:{ + dVector3 ax1; + static dReal last_angle = 0; + dJointGetUniversalAxis1(joint, ax1); + addOscillatingTorqueAbout (0.1, ax1[0], ax1[1], ax1[2]); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle2(joint); + dReal r = dJointGetUniversalAngle2Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 1e10; + } + + case 740:{ + dVector3 ax1, ax2; + dJointGetUniversalAxis1(joint, ax1); + dJointGetUniversalAxis2(joint, ax2); + addOscillatingTorqueAbout (0.1, ax2[0], ax2[1], ax2[2]); + dampRotationalMotion (0.1); + return fabs(10*dCalcVectorDot3(ax1, ax2)); + } + + case 741:{ + dVector3 ax2; + static dReal last_angle = 0; + dJointGetUniversalAxis2(joint, ax2); + addOscillatingTorqueAbout (0.1, ax2[0], ax2[1], ax2[2]); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle1(joint); + dReal r = dJointGetUniversalAngle1Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 1e10; + } + + case 742:{ + dVector3 ax2; + static dReal last_angle = 0; + dJointGetUniversalAxis2(joint, ax2); + addOscillatingTorqueAbout (0.1, ax2[0], ax2[1], ax2[2]); + dampRotationalMotion (0.1); + dReal a = dJointGetUniversalAngle2(joint); + dReal r = dJointGetUniversalAngle2Rate(joint); + dReal diff = a - last_angle; + if (diff > M_PI) diff -= 2*M_PI; + if (diff < -M_PI) diff += 2*M_PI; + dReal er = diff / STEPSIZE; // estimated rate + last_angle = a; + return fabs(r - er) * 1e4; + } + + // ********** slider joint + case 801: + case 803: + addSpringForce (0.25); + return dInfinity; + + case 802: + case 804: { + static dReal a = 0; + dBodyAddTorque (body[0], 0, 0.01*cos(1.5708*a), 0); + a += 0.01; + return dInfinity; + } + + case 805: + addOscillatingForce (0.1); + return dInfinity; + } + + + return dInfinity; +} + +//**************************************************************************** +// simulation stuff common to all the tests + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {1.0382f,-1.0811f,1.4700f}; + static float hpr[3] = {135.0000f,-19.5000f,0.0000f}; + dsSetViewpoint (xyz,hpr); +} + + +// simulation loop + +static void simLoop (int pause) +{ + // stop after a given number of iterations, as long as we are not in + // interactive mode + if (cmd_graphics && !cmd_interactive && + (iteration >= max_iterations)) { + dsStop(); + return; + } + iteration++; + + if (!pause) { + // do stuff for this test and check to see if the joint is behaving well + dReal error = doStuffAndGetError (test_num); + if (error > max_error) max_error = error; + if (cmd_interactive && error < dInfinity) { + printf ("scaled error = %.4e\n",error); + } + + // take a step + dWorldStep (world,STEPSIZE); + + // occasionally re-orient the first body to create a deliberate error. + if (cmd_occasional_error) { + static int count = 0; + if ((count % 20)==0) { + // randomly adjust orientation of body[0] + const dReal *R1; + dMatrix3 R2,R3; + R1 = dBodyGetRotation (body[0]); + dRFromAxisAndAngle (R2,dRandReal()-0.5,dRandReal()-0.5, + dRandReal()-0.5,dRandReal()-0.5); + dMultiply0 (R3,R1,R2,3,3,3); + dBodySetRotation (body[0],R3); + + // randomly adjust position of body[0] + const dReal *pos = dBodyGetPosition (body[0]); + dBodySetPosition (body[0], + pos[0]+0.2*(dRandReal()-0.5), + pos[1]+0.2*(dRandReal()-0.5), + pos[2]+0.2*(dRandReal()-0.5)); + } + count++; + } + } + + if (cmd_graphics) { + dReal sides1[3] = {SIDE,SIDE,SIDE}; + dReal sides2[3] = {SIDE*0.99f,SIDE*0.99f,SIDE*0.99f}; + dsSetTexture (DS_WOOD); + dsSetColor (1,1,0); + dsDrawBox (dBodyGetPosition(body[0]),dBodyGetRotation(body[0]),sides1); + if (body[1]) { + dsSetColor (0,1,1); + dsDrawBox (dBodyGetPosition(body[1]),dBodyGetRotation(body[1]),sides2); + } + } +} + +//**************************************************************************** +// conduct a specific test, and report the results + +void doTest (int argc, char **argv, int n, int fatal_if_bad_n) +{ + test_num = n; + iteration = 0; + max_iterations = 300; + max_error = 0; + + if (! setupTest (n)) { + if (fatal_if_bad_n) dError (0,"bad test number"); + return; + } + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = 0; + fn.stop = 0; + if (cmd_path_to_textures) + fn.path_to_textures = cmd_path_to_textures; + else + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // run simulation + if (cmd_graphics) { + dsSimulationLoop (argc,argv,352,288,&fn); + } + else { + for (int i=0; i < max_iterations; i++) simLoop (0); + } + dWorldDestroy (world); + body[0] = 0; + body[1] = 0; + joint = 0; + + // print results + printf ("test %d: ",n); + if (max_error == dInfinity) printf ("error not computed\n"); + else { + printf ("max scaled error = %.4e",max_error); + if (max_error < 1) printf (" - passed\n"); + else printf (" - FAILED\n"); + } +} + +//**************************************************************************** +// main + +int main (int argc, char **argv) +{ + int i; + dInitODE2(0); + + // process the command line args. anything that starts with `-' is assumed + // to be a drawstuff argument. + for (i=1; i +#include +#include +#include +#include +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawCylinder dsDrawCylinderD +#endif + + +using namespace std; + +dWorld *world; +dSpace *space; +dPlane *ground; +dBody *kbody; +dBox *kbox; +dJointGroup joints; +dCylinder *kpole; +dBody *matraca; +dBox *matraca_geom; +dHingeJoint *hinge; + +struct Box { + dBody body; + dBox geom; + Box() : + body(*world), + geom(*space, 0.2, 0.2, 0.2) + { + dMass mass; + mass.setBox(10, 0.2, 0.2, 0.2); + body.setMass(mass); + geom.setData(this); + geom.setBody(body); + } + void draw() const + { + dVector3 lengths; + geom.getLengths(lengths); + dsSetTexture(DS_WOOD); + dsSetColor(0,1,0); + dsDrawBox(geom.getPosition(), geom.getRotation(), lengths); + } +}; + +set boxes; +set to_remove; + +void dropBox() +{ + Box *box = new Box(); + + dReal px = (rand() / float(RAND_MAX)) * 2 - 1; + dReal py = (rand() / float(RAND_MAX)) * 2 - 1; + dReal pz = 2.5; + box->body.setPosition(px, py, pz); + + boxes.insert(box); +} + +void queueRemoval(dGeomID g) +{ + Box *b = (Box*)dGeomGetData(g); + to_remove.insert(b); +} + +void removeQueued() +{ + while (!to_remove.empty()) { + Box *b = *to_remove.begin(); + to_remove.erase(b); + boxes.erase(b); + delete b; + } +} + + +void nearCallback(void *, dGeomID g1, dGeomID g2) +{ + if (g1 == ground->id()) { + queueRemoval(g2); + return; + } + if (g2 == ground->id()) { + queueRemoval(g1); + return; + } + + dBodyID b1 = dGeomGetBody(g1); + dBodyID b2 = dGeomGetBody(g2); + + if (b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact)) + return; + + const int MAX_CONTACTS = 10; + dContact contact[MAX_CONTACTS]; + int n = dCollide(g1, g2, MAX_CONTACTS, &contact[0].geom, sizeof(dContact)); + for (int i=0; i 2*M_PI) + t = 0; + dVector3 next_pos = { dCos(t), dSin(t), REAL(0.5)}; + dVector3 vel; + // vel = (next_pos - cur_pos) / timestep + dSubtractVectors3(vel, next_pos, kbody->getPosition()); + dScaleVector3(vel, 1/timestep); + kbody->setLinearVel(vel); + // end of hard-coded animation + + space->collide(0, nearCallback); + removeQueued(); + + world->quickStep(timestep); + joints.clear(); + } + + dVector3 lengths; + + // the moving platform + kbox->getLengths(lengths); + dsSetTexture(DS_WOOD); + dsSetColor(.3, .3, 1); + dsDrawBox(kbox->getPosition(), kbox->getRotation(), lengths); + dReal length, radius; + kpole->getParams(&radius, &length); + dsSetTexture(DS_CHECKERED); + dsSetColor(1, 1, 0); + dsDrawCylinder(kpole->getPosition(), kpole->getRotation(), length, radius); + + // the matraca + matraca_geom->getLengths(lengths); + dsSetColor(1,0,0); + dsSetTexture(DS_WOOD); + dsDrawBox(matraca_geom->getPosition(), matraca_geom->getRotation(), lengths); + + // and the boxes + for_each(boxes.begin(), boxes.end(), mem_fun(&Box::draw)); +} + +void command(int c) +{ + switch (c) { + case ' ': + dropBox(); + break; + } +} + +int main(int argc, char **argv) +{ + dInitODE(); + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = 0; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + cout << endl << "*** Press SPACE to drop boxes **" << endl; + + space = new dSimpleSpace(); + ground = new dPlane(*space, 0, 0, 1, 0); + + world = new dWorld; + world->setGravity(0, 0, -.5); + + kbody = new dBody(*world); + kbody->setKinematic(); + const dReal kx = 1, ky = 0, kz = .5; + kbody->setPosition(kx, ky, kz); + kbox = new dBox(*space, 3, 3, .5); + kbox->setBody(*kbody); + kpole = new dCylinder(*space, .125, 1.5); + kpole->setBody(*kbody); + dGeomSetOffsetPosition(kpole->id(), 0, 0, 0.8); + + matraca = new dBody(*world); + matraca->setPosition(kx+0, ky+1, kz+1); + matraca_geom = new dBox(*space, 0.5, 2, 0.75); + matraca_geom->setBody(*matraca); + dMass mass; + mass.setBox(1, 0.5, 2, 0.75); + matraca->setMass(mass); + + hinge = new dHingeJoint(*world); + hinge->attach(*kbody, *matraca); + hinge->setAnchor(kx, ky, kz+1); + hinge->setAxis(0, 0, 1); + + dsSimulationLoop (argc, argv, 640, 480, &fn); + + dCloseODE(); +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_motion.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_motion.cpp new file mode 100644 index 0000000..a83887d --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_motion.cpp @@ -0,0 +1,527 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + This demo shows how to use dContactMotionN in a lifting platform. +*/ +//#include // for usleep() +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawConvex dsDrawConvexD +#endif + + +// some constants + +#define NUM 100 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 8 // maximum number of contact points per body +#define USE_GEOM_OFFSET 1 + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? +static int write_world = 0; +static int show_body = 0; + +static dGeomID platform, ground; + +dVector3 platpos = {0, 0, 0}; +int mov_type = 2; +dReal mov_time = 0; + + +const dReal mov1_speed = 0.2; + +dVector3 mov2_vel = { 0.2, 0.1, 0.25}; + + + + +/**************************************************************** + * Movement 1: move platform up, reset every 80 units of time. * + * This is the simplest case * + ****************************************************************/ +static void moveplat_1(dReal stepsize) +{ + mov_time += stepsize; + if (mov_time > 80) + mov_time = 0; + + platpos[0] = platpos[1] = 0; + // the platform moves up (Z) at constant speed: mov1_speed + platpos[2] = mov1_speed * mov_time; +} + +// Generate contact info for movement 1 +static void contactplat_1(dContact &contact) +{ + contact.surface.mode |= dContactMotionN; + contact.surface.motionN = mov1_speed; +} + + + +/**************************************************************** + * Movement 2: move platform along direction mov2_vel, reset * + * every 80 units of time. * + * This is the most general case: the geom moves along * + * an arbitrary direction. * + ****************************************************************/ +static void moveplat_2(dReal stepsize) +{ + mov_time += stepsize; + if (mov_time > 80) + mov_time = 0; + + // the platform moves at constant speed: mov2_speed + platpos[0] = mov2_vel[0] * mov_time; + platpos[1] = mov2_vel[1] * mov_time; + platpos[2] = mov2_vel[2] * mov_time; +} + +// Generate contact info for movement 1 +static void contactplat_2(dContact &contact) +{ + /* + For arbitrary contact directions we need to project the moving + geom's velocity against the contact normal and fdir1, fdir2 + (obtained with dPlaneSpace()). Assuming moving geom=g2 + (so the contact joint is in the moving geom's reference frame): + motion1 = dCalcVectorDot3(fdir1, vel); + motion2 = dCalcVectorDot3(fdir2, vel); + motionN = dCalcVectorDot3(normal, vel); + + For geom=g1 just negate motionN and motion2. fdir1 is an arbitrary + vector, so there's no need to negate motion1. + + */ + contact.surface.mode |= + dContactMotionN | // velocity along normal + dContactMotion1 | dContactMotion2 | // and along the contact plane + dContactFDir1; // don't forget to set the direction 1 + + + // This is a convenience function: given a vector, it finds other 2 perpendicular vectors + dVector3 motiondir1, motiondir2; + dPlaneSpace(contact.geom.normal, motiondir1, motiondir2); + for (int i=0; i<3; ++i) + contact.fdir1[i] = motiondir1[i]; + + + dReal inv = 1; + if (contact.geom.g1 == platform) + inv = -1; + + contact.surface.motion1 = dCalcVectorDot3(mov2_vel, motiondir1); + contact.surface.motion2 = inv * dCalcVectorDot3(mov2_vel, motiondir2); + contact.surface.motionN = inv * dCalcVectorDot3(mov2_vel, contact.geom.normal); + +} + + + + + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + dMatrix3 RI; + static const dReal ss[3] = {0.02,0.02,0.02}; + + dContact contact[MAX_CONTACTS]; + int numc = dCollide (o1, o2, MAX_CONTACTS, + &contact[0].geom, sizeof(dContact)); + + if (numc) + dRSetIdentity(RI); + + bool isplatform = (o1 == platform) || (o2 == platform); + + for (int i=0; i< numc; i++) { + contact[i].surface.mode = dContactBounce; + contact[i].surface.mu = 1; + contact[i].surface.bounce = 0.25; + contact[i].surface.bounce_vel = 0.01; + + if (isplatform) { + switch (mov_type) { + case 1: + contactplat_1(contact[i]); + break; + case 2: + contactplat_2(contact[i]); + break; + } + } + + dJointID c = dJointCreateContact (world,contactgroup,contact+i); + dJointAttach (c, dGeomGetBody(o1), dGeomGetBody(o2)); + if (show_contacts) + dsDrawBox (contact[i].geom.pos, RI, ss); + } +} + + +// start simulation - set viewpoint + +static float xyz[3] = {2.1106f,-1.3007,2.f}; +static float hpr[3] = {150.f,-13.5000f,0.0000f}; + +static void start() +{ + //dAllocateODEDataForThread(dAllocateMaskAll); + dsSetViewpoint (xyz,hpr); + printf ("To drop another object, press:\n"); + printf (" b for box.\n"); + printf (" s for sphere.\n"); + printf (" c for capsule.\n"); + printf (" y for cylinder.\n"); + printf ("Press m to change the movement type\n"); + printf ("Press space to reset the platform\n"); + printf ("To toggle showing the geom AABBs, press a.\n"); + printf ("To toggle showing the contact points, press t.\n"); + printf ("To toggle dropping from random position/orientation, press r.\n"); + printf ("To save the current state to 'state.dif', press 1.\n"); +} + + +char locase (char c) +{ + if (c >= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command (int cmd) +{ + dsizeint i; + int k; + dReal sides[3]; + dMass m; + int setBody; + + cmd = locase (cmd); + if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'y') + { + setBody = 0; + if (num < NUM) { + i = num; + num++; + } + else { + i = nextobj; + nextobj++; + if (nextobj >= num) nextobj = 0; + + // destroy the body and geoms for slot i + if (obj[i].body) { + dBodyDestroy (obj[i].body); + } + for (k=0; k < GPB; k++) { + if (obj[i].geom[k]) { + dGeomDestroy (obj[i].geom[k]); + } + } + memset (&obj[i],0,sizeof(obj[i])); + } + + obj[i].body = dBodyCreate (world); + for (k=0; k<3; k++) sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) + { + dBodySetPosition (obj[i].body, + dRandReal()*2-1 + platpos[0], + dRandReal()*2-1 + platpos[1], + dRandReal()+2 + platpos[2]); + dRFromAxisAndAngle (R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } + else + { + dBodySetPosition (obj[i].body, + platpos[0], + platpos[1], + platpos[2]+2); + dRSetIdentity (R); + } + dBodySetRotation (obj[i].body,R); + dBodySetData (obj[i].body,(void*) i); + + if (cmd == 'b') { + dMassSetBox (&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox (space,sides[0],sides[1],sides[2]); + } + else if (cmd == 'c') { + sides[0] *= 0.5; + dMassSetCapsule (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule (space,sides[0],sides[1]); + } + else if (cmd == 'y') { + dMassSetCylinder (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder (space,sides[0],sides[1]); + } + else if (cmd == 's') { + sides[0] *= 0.5; + dMassSetSphere (&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere (space,sides[0]); + } + + if (!setBody) + for (k=0; k < GPB; k++) { + if (obj[i].geom[k]) { + dGeomSetBody (obj[i].geom[k],obj[i].body); + } + } + + dBodySetMass (obj[i].body,&m); + } + else if (cmd == 'a') { + show_aabb ^= 1; + } + else if (cmd == 't') { + show_contacts ^= 1; + } + else if (cmd == 'r') { + random_pos ^= 1; + } + else if (cmd == '1') { + write_world = 1; + } + else if (cmd == ' ') { + mov_time = 0; + } + else if (cmd == 'm') { + mov_type = mov_type==1 ? 2 : 1; + mov_time = 0; + } +} + + +// draw a geom + +void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + int i; + + if (!g) return; + if (!pos) pos = dGeomGetPosition (g); + if (!R) R = dGeomGetRotation (g); + + int type = dGeomGetClass (g); + if (type == dBoxClass) { + dVector3 sides; + dGeomBoxGetLengths (g,sides); + dsDrawBox (pos,R,sides); + } + else if (type == dSphereClass) { + dsDrawSphere (pos,R,dGeomSphereGetRadius (g)); + } + else if (type == dCapsuleClass) { + dReal radius,length; + dGeomCapsuleGetParams (g,&radius,&length); + dsDrawCapsule (pos,R,length,radius); + } + else if (type == dCylinderClass) { + dReal radius,length; + dGeomCylinderGetParams (g,&radius,&length); + dsDrawCylinder (pos,R,length,radius); + } + + if (show_body) { + dBodyID body = dGeomGetBody(g); + if (body) { + const dReal *bodypos = dBodyGetPosition (body); + const dReal *bodyr = dBodyGetRotation (body); + dReal bodySides[3] = { 0.1, 0.1, 0.1 }; + dsSetColorAlpha(0,1,0,1); + dsDrawBox(bodypos,bodyr,bodySides); + } + } + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB (g,aabb); + dVector3 bbpos; + for (i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (i=0; i<3; i++) bbsides[i] = aabb[i*2+1] - aabb[i*2]; + dMatrix3 RI; + dRSetIdentity (RI); + dsSetColorAlpha (1,0,0,0.5); + dsDrawBox (bbpos,RI,bbsides); + } +} + + +// simulation loop + +static void updatecam() +{ + xyz[0] = platpos[0] + 3.3; + xyz[1] = platpos[1] - 1.8; + xyz[2] = platpos[2] + 2; + dsSetViewpoint (xyz, hpr); +} + +static void simLoop (int pause) +{ + const dReal stepsize = 0.02; + + dsSetColor (0,0,2); + dSpaceCollide (space,0,&nearCallback); + if (!pause) { + + if (mov_type == 1) + moveplat_1(stepsize); + else + moveplat_2(stepsize); + + dGeomSetPosition(platform, platpos[0], platpos[1], platpos[2]); + updatecam(); + dWorldQuickStep (world,stepsize); + //dWorldStep (world,stepsize); + } + + if (write_world) { + FILE *f = fopen ("state.dif","wt"); + if (f) { + dWorldExportDIF (world,f,"X"); + fclose (f); + } + write_world = 0; + } + + // remove all contact joints + dJointGroupEmpty (contactgroup); + + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + + +// some constants +#define SIDE (0.5f) // side length of a box +#define MASS (1.0) // mass of a box + + +// dynamics and collision objects +static dWorldID world; +static dBodyID body[2]; +static dGeomID geom[2]; +static dJointID lmotor[2]; +static dJointID amotor[2]; +static dSpaceID space; +static dJointGroupID contactgroup; + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {1.0382f,-1.0811f,1.4700f}; + static float hpr[3] = {135.0000f,-19.5000f,0.0000f}; + dsSetViewpoint (xyz,hpr); + printf ("Press 'q,a,z' to control one axis of lmotor connectiong two bodies. (q is +,a is 0, z is -)\n"); + printf ("Press 'w,e,r' to control one axis of lmotor connectiong first body with world. (w is +,e is 0, r is -)\n"); +} + + +// called when a key pressed + +static void command (int cmd) +{ + if (cmd == 'q' || cmd == 'Q') { + dJointSetLMotorParam(lmotor[0],dParamVel,0); + dJointSetLMotorParam(lmotor[0],dParamVel2,0); + dJointSetLMotorParam(lmotor[0],dParamVel3,0.1); + } else if (cmd == 'a' || cmd == 'A') { + dJointSetLMotorParam(lmotor[0],dParamVel,0); + dJointSetLMotorParam(lmotor[0],dParamVel2,0); + dJointSetLMotorParam(lmotor[0],dParamVel3,0); + } else if (cmd == 'z' || cmd == 'Z') { + dJointSetLMotorParam(lmotor[0],dParamVel,0); + dJointSetLMotorParam(lmotor[0],dParamVel2,0); + dJointSetLMotorParam(lmotor[0],dParamVel3,-0.1); + } else if (cmd == 'w' || cmd == 'W') { + dJointSetLMotorParam(lmotor[1],dParamVel,0.1); + dJointSetLMotorParam(lmotor[1],dParamVel2,0); + dJointSetLMotorParam(lmotor[1],dParamVel3,0); + } else if (cmd == 'e' || cmd == 'E') { + dJointSetLMotorParam(lmotor[1],dParamVel,0); + dJointSetLMotorParam(lmotor[1],dParamVel2,0); + dJointSetLMotorParam(lmotor[1],dParamVel3,0); + } else if (cmd == 'r' || cmd == 'R') { + dJointSetLMotorParam(lmotor[1],dParamVel,-0.1); + dJointSetLMotorParam(lmotor[1],dParamVel2,0); + dJointSetLMotorParam(lmotor[1],dParamVel3,0); + } + +} + + + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + dContact contact; + contact.surface.mode = 0; + contact.surface.mu = dInfinity; + if (dCollide (o1,o2,1,&contact.geom,sizeof(dContactGeom))) { + dJointID c = dJointCreateContact (world,contactgroup,&contact); + dJointAttach (c,b1,b2); + } +} + +// simulation loop + +static void simLoop (int pause) +{ + if (!pause) { + dSpaceCollide(space,0,&nearCallback); + dWorldQuickStep (world,0.05); + dJointGroupEmpty(contactgroup); + } + + dReal sides1[3]; + dGeomBoxGetLengths(geom[0], sides1); + dReal sides2[3]; + dGeomBoxGetLengths(geom[1], sides2); + dsSetTexture (DS_WOOD); + dsSetColor (1,1,0); + dsDrawBox (dBodyGetPosition(body[0]),dBodyGetRotation(body[0]),sides1); + dsSetColor (0,1,1); + dsDrawBox (dBodyGetPosition(body[1]),dBodyGetRotation(body[1]),sides2); +} + + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // create world + dInitODE2(0); + contactgroup = dJointGroupCreate(0); + world = dWorldCreate(); + space = dSimpleSpaceCreate(0); + dMass m; + dMassSetBox (&m,1,SIDE,SIDE,SIDE); + dMassAdjust (&m,MASS); + + body[0] = dBodyCreate (world); + dBodySetMass (body[0],&m); + dBodySetPosition (body[0],0,0,1); + geom[0] = dCreateBox(space,SIDE,SIDE,SIDE); + body[1] = dBodyCreate (world); + dBodySetMass (body[1],&m); + dBodySetPosition (body[1],0,0,2); + geom[1] = dCreateBox(space,SIDE,SIDE,SIDE); + + dGeomSetBody(geom[0],body[0]); + dGeomSetBody(geom[1],body[1]); + + lmotor[0] = dJointCreateLMotor (world,0); + dJointAttach (lmotor[0],body[0],body[1]); + lmotor[1] = dJointCreateLMotor (world,0); + dJointAttach (lmotor[1],body[0],0); + amotor[0] = dJointCreateAMotor(world,0); + dJointAttach(amotor[0], body[0],body[1]); + amotor[1] = dJointCreateAMotor(world,0); + dJointAttach(amotor[1], body[0], 0); + + for (int i=0; i<2; i++) { + dJointSetAMotorNumAxes(amotor[i], 3); + dJointSetAMotorAxis(amotor[i],0,1,1,0,0); + dJointSetAMotorAxis(amotor[i],1,1,0,1,0); + dJointSetAMotorAxis(amotor[i],2,1,0,0,1); + dJointSetAMotorParam(amotor[i],dParamFMax,0.00001); + dJointSetAMotorParam(amotor[i],dParamFMax2,0.00001); + dJointSetAMotorParam(amotor[i],dParamFMax3,0.00001); + + dJointSetAMotorParam(amotor[i],dParamVel,0); + dJointSetAMotorParam(amotor[i],dParamVel2,0); + dJointSetAMotorParam(amotor[i],dParamVel3,0); + + dJointSetLMotorNumAxes(lmotor[i],3); + dJointSetLMotorAxis(lmotor[i],0,1,1,0,0); + dJointSetLMotorAxis(lmotor[i],1,1,0,1,0); + dJointSetLMotorAxis(lmotor[i],2,1,0,0,1); + + dJointSetLMotorParam(lmotor[i],dParamFMax,0.0001); + dJointSetLMotorParam(lmotor[i],dParamFMax2,0.0001); + dJointSetLMotorParam(lmotor[i],dParamFMax3,0.0001); + } + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dJointGroupDestroy(contactgroup); + dSpaceDestroy (space); + dWorldDestroy (world); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_moving_convex.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_moving_convex.cpp new file mode 100644 index 0000000..2f03900 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_moving_convex.cpp @@ -0,0 +1,415 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include +#include +#include "texturepath.h" +#include "bunny_geom.h" +#include "convex_bunny_geom.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawLine dsDrawLineD +#define dsDrawTriangle dsDrawTriangleD +#define dsDrawConvex dsDrawConvexD +#endif + + +// some constants + +#define NUM 200 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 64 // maximum number of contact points per body + + +// dynamics and collision objects + +struct MyObject +{ + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? + +typedef dReal dVector3R[3]; + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback( void *, dGeomID o1, dGeomID o2 ) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody( o1 ); + dBodyID b2 = dGeomGetBody( o2 ); + if ( b1 && b2 && dAreConnectedExcluding( b1,b2,dJointTypeContact ) ) return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for ( i=0; i= 'A' && c <= 'Z' ) return c - ( 'a'-'A' ); + else return c; +} + + +// called when a key pressed +static void command( int cmd ) +{ + int i,k; + dReal sides[3]; + dMass m; + + cmd = locase( cmd ); + if ( cmd == 'v' || cmd == 'b' || cmd == 'c' || cmd == 's' || cmd == 'y') + { + if ( num < NUM ) + { + i = num; + num++; + } + else + { + i = nextobj; + nextobj++; + if ( nextobj >= num ) nextobj = 0; + + // destroy the body and geoms for slot i + dBodyDestroy( obj[i].body ); + for ( k=0; k < GPB; k++ ) + { + if ( obj[i].geom[k] ) dGeomDestroy( obj[i].geom[k] ); + } + memset( &obj[i],0,sizeof( obj[i] ) ); + } + + obj[i].body = dBodyCreate( world ); + for ( k=0; k<3; k++ ) sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if ( random_pos ) + { + dBodySetPosition( obj[i].body, + dRandReal()*2-1,dRandReal()*2-1,dRandReal()+3 ); + dRFromAxisAndAngle( R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0 ); + } + else + { + dReal maxheight = 0; + for ( k=0; k maxheight ) maxheight = pos[2]; + } + dBodySetPosition( obj[i].body, 0,0,maxheight+1 ); + dRFromAxisAndAngle( R,0,0,1,dRandReal()*10.0-5.0 ); + } + dBodySetRotation( obj[i].body,R ); + dBodySetData( obj[i].body,( void* )( dsizeint )i ); + + if ( cmd == 'b' ) + { + dMassSetBox( &m,DENSITY,sides[0],sides[1],sides[2] ); + obj[i].geom[0] = dCreateBox( space,sides[0],sides[1],sides[2] ); + } + else if ( cmd == 'c' ) + { + sides[0] *= 0.5; + dMassSetCapsule( &m,DENSITY,3,sides[0],sides[1] ); + obj[i].geom[0] = dCreateCapsule( space,sides[0],sides[1] ); + } + else if (cmd == 'y') { + dMassSetCylinder (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder (space,sides[0],sides[1]); + } + else if ( cmd == 's' ) + { + sides[0] *= 0.5; + dMassSetSphere( &m,DENSITY,sides[0] ); + obj[i].geom[0] = dCreateSphere( space,sides[0] ); + } + else if ( cmd == 'v' ) + { + obj[i].geom[0] = dCreateConvex( space, + convexBunnyPlanes, + convexBunnyPlaneCount, + convexBunnyPoints, + convexBunnyPointCount, + convexBunnyPolygons ); + + /// Use equivalent TriMesh to set mass + dTriMeshDataID new_tmdata = dGeomTriMeshDataCreate(); + dGeomTriMeshDataBuildSingle( new_tmdata, &Vertices[0], 3 * sizeof( float ), VertexCount, + ( dTriIndex* )&Indices[0], IndexCount, 3 * sizeof( dTriIndex ) ); + dGeomTriMeshDataPreprocess2( new_tmdata, (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES), NULL ); + + dGeomID triMesh = dCreateTriMesh( 0, new_tmdata, 0, 0, 0 ); + + dMassSetTrimesh( &m, DENSITY, triMesh ); + + dGeomDestroy( triMesh ); + dGeomTriMeshDataDestroy( new_tmdata ); + + printf( "mass at %f %f %f\n", m.c[0], m.c[1], m.c[2] ); + dGeomSetPosition( obj[i].geom[0], -m.c[0], -m.c[1], -m.c[2] ); + dMassTranslate( &m, -m.c[0], -m.c[1], -m.c[2] ); + } + + for ( k=0; k < GPB; k++ ) + { + if ( obj[i].geom[k] ) dGeomSetBody( obj[i].geom[k],obj[i].body ); + } + + dBodySetMass( obj[i].body,&m ); + } + + if ( cmd == ' ' ) + { + selected++; + if ( selected >= num ) selected = 0; + if ( selected < 0 ) selected = 0; + } + else if ( cmd == 'd' && selected >= 0 && selected < num ) + { + dBodyDisable( obj[selected].body ); + } + else if ( cmd == 'e' && selected >= 0 && selected < num ) + { + dBodyEnable( obj[selected].body ); + } + else if ( cmd == 'a' ) + { + show_aabb ^= 1; + } + else if ( cmd == 't' ) + { + show_contacts ^= 1; + } + else if ( cmd == 'r' ) + { + random_pos ^= 1; + } +} + +// draw a geom +void drawGeom( dGeomID g, const dReal *pos, const dReal *R, int show_aabb ) +{ + if ( !g ) return; + if ( !pos ) pos = dGeomGetPosition( g ); + if ( !R ) R = dGeomGetRotation( g ); + + int type = dGeomGetClass( g ); + if ( type == dBoxClass ) + { + dVector3 sides; + dGeomBoxGetLengths( g,sides ); + dsDrawBox( pos,R,sides ); + } + else if ( type == dSphereClass ) + { + dsDrawSphere( pos,R,dGeomSphereGetRadius( g ) ); + } + else if (type == dCylinderClass) { + dReal radius,length; + dGeomCylinderGetParams (g,&radius,&length); + dsDrawCylinder (pos,R,length,radius); + } + else if ( type == dCapsuleClass ) + { + dReal radius,length; + dGeomCapsuleGetParams( g,&radius,&length ); + dsDrawCapsule( pos,R,length,radius ); + } + else if ( type == dConvexClass ) + { + dsDrawConvex( pos,R, + convexBunnyPlanes, + convexBunnyPlaneCount, + convexBunnyPoints, + convexBunnyPointCount, + convexBunnyPolygons ); + } + + if ( show_aabb ) + { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB( g,aabb ); + dVector3 bbpos; + for ( int i=0; i<3; i++ ) bbpos[i] = 0.5*( aabb[i*2] + aabb[i*2+1] ); + dVector3 bbsides; + for ( int j=0; j<3; j++ ) bbsides[j] = aabb[j*2+1] - aabb[j*2]; + dMatrix3 RI; + dRSetIdentity( RI ); + dsSetColorAlpha( 1,0,0,0.5 ); + dsDrawBox( bbpos,RI,bbsides ); + } +} + +// simulation loop + +static void simLoop( int pause ) +{ + dsSetColor( 0,0,2 ); + dSpaceCollide( space,0,&nearCallback ); + + if ( !pause ) dWorldQuickStep( world,0.05 ); + + for ( int j = 0; j < dSpaceGetNumGeoms( space ); j++ ) + { + dSpaceGetGeom( space, j ); + } + + // remove all contact joints + dJointGroupEmpty( contactgroup ); + + dsSetColor( 1,1,0 ); + dsSetTexture( DS_WOOD ); + for ( int i=0; i +#include +#include "texturepath.h" +#include "bunny_geom.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +//<---- Convex Object +static const dReal planes[] = // planes for a cube +{ + 1.0f ,0.0f ,0.0f ,0.25f, + 0.0f ,1.0f ,0.0f ,0.25f, + 0.0f ,0.0f ,1.0f ,0.25f, + 0.0f ,0.0f ,-1.0f,0.25f, + 0.0f ,-1.0f,0.0f ,0.25f, + -1.0f,0.0f ,0.0f ,0.25f + /* + 1.0f ,0.0f ,0.0f ,2.0f, + 0.0f ,1.0f ,0.0f ,1.0f, + 0.0f ,0.0f ,1.0f ,1.0f, + 0.0f ,0.0f ,-1.0f,1.0f, + 0.0f ,-1.0f,0.0f ,1.0f, + -1.0f,0.0f ,0.0f ,0.0f + */ +}; +static const unsigned int planecount=6; + +static const dReal points[] = // points for a cube +{ + 0.25f,0.25f,0.25f, + -0.25f,0.25f,0.25f, + + 0.25f,-0.25f,0.25f, + -0.25f,-0.25f,0.25f, + + 0.25f,0.25f,-0.25f, + -0.25f,0.25f,-0.25f, + + 0.25f,-0.25f,-0.25f, + -0.25f,-0.25f,-0.25f, +}; +static const unsigned int pointcount=8; + +static const unsigned int polygons[] = //Polygons for a cube (6 squares) + { + 4,0,2,6,4, // positive X + 4,1,0,4,5, // positive Y + 4,0,1,3,2, // positive Z + 4,3,1,5,7, // negative X + 4,2,3,7,6, // negative Y + 4,5,4,6,7, // negative Z + }; +//----> Convex Object + +int tmTriangles[] = +{ + 0,2,6, + 0,6,4, + 1,0,4, + 1,4,5, + 0,1,3, + 0,3,2, + 3,1,5, + 3,5,7, + 2,3,7, + 2,7,6, + 5,4,6, + 5,6,7 +}; + +float tmVertices[] = +{ + 0.25f,0.25f,0.25f, // point 0 + -0.25f,0.25f,0.25f, // point 1 + + 0.25f,-0.25f,0.25f, // point 2 + -0.25f,-0.25f,0.25f,// point 3 + + 0.25f,0.25f,-0.25f, // point 4 + -0.25f,0.25f,-0.25f,// point 5 + + 0.25f,-0.25f,-0.25f,// point 6 + -0.25f,-0.25f,-0.25f,// point 7 +}; + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawLine dsDrawLineD +#define dsDrawTriangle dsDrawTriangleD +#define dsDrawConvex dsDrawConvexD +#endif + + +// some constants + +#define NUM 200 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 64 // maximum number of contact points per body + + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body + + // Trimesh only - double buffered matrices for 'last transform' setup + dReal matrix_dblbuff[ 16 * 2 ]; + int last_matrix_index; +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? + +typedef dReal dVector3R[3]; + +dGeomID TriMesh1; +dGeomID TriMesh2; +static dTriMeshDataID TriData1, TriData2; // reusable static trimesh data + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command (int cmd) +{ + int i,j,k; + dReal sides[3]; + dMass m; + bool setBody = false; + + cmd = locase (cmd); + if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'x' || cmd == 'm' || cmd == 'y' || cmd == 'v') { + if (num < NUM) { + i = num; + num++; + } + else { + i = nextobj; + nextobj++; + if (nextobj >= num) nextobj = 0; + + // destroy the body and geoms for slot i + dBodyDestroy (obj[i].body); + for (k=0; k < GPB; k++) { + if (obj[i].geom[k]) dGeomDestroy (obj[i].geom[k]); + } + memset (&obj[i],0,sizeof(obj[i])); + } + + obj[i].body = dBodyCreate (world); + for (k=0; k<3; k++) sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) { + dBodySetPosition (obj[i].body, + dRandReal()*2-1,dRandReal()*2-1,dRandReal()+3); + dRFromAxisAndAngle (R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } + else { + dReal maxheight = 0; + for (k=0; k maxheight) maxheight = pos[2]; + } + dBodySetPosition (obj[i].body, 0,0,maxheight+1); + dRFromAxisAndAngle (R,0,0,1,dRandReal()*10.0-5.0); + } + dBodySetRotation (obj[i].body,R); + dBodySetData (obj[i].body,(void*)(dsizeint)i); + + if (cmd == 'b') { + dMassSetBox (&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox (space,sides[0],sides[1],sides[2]); + } + else if (cmd == 'c') { + sides[0] *= 0.5; + dMassSetCapsule (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule (space,sides[0],sides[1]); + } else if (cmd == 'v') { + + dMassSetBox (&m,DENSITY,0.25,0.25,0.25); + obj[i].geom[0] = dCreateConvex(space, + planes, + planecount, + points, + pointcount, + polygons); + } + else if (cmd == 'y') { + sides[1] *= 0.5; + dMassSetCylinder (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder (space,sides[0],sides[1]); + } + else if (cmd == 's') { + sides[0] *= 0.5; + dMassSetSphere (&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere (space,sides[0]); + } + else if (cmd == 'm') { + dTriMeshDataID new_tmdata = dGeomTriMeshDataCreate(); + dGeomTriMeshDataBuildSingle(new_tmdata, &Vertices[0], 3 * sizeof(float), VertexCount, + (dTriIndex*)&Indices[0], IndexCount, 3 * sizeof(dTriIndex)); + dGeomTriMeshDataPreprocess2(new_tmdata, (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES), NULL); + + + obj[i].geom[0] = dCreateTriMesh(space, new_tmdata, 0, 0, 0); + + // remember the mesh's dTriMeshDataID on its userdata for convenience. + dGeomSetData(obj[i].geom[0], new_tmdata); + + dMassSetTrimesh( &m, DENSITY, obj[i].geom[0] ); + printf("mass at %f %f %f\n", m.c[0], m.c[1], m.c[2]); + dGeomSetPosition(obj[i].geom[0], -m.c[0], -m.c[1], -m.c[2]); + dMassTranslate(&m, -m.c[0], -m.c[1], -m.c[2]); + } + else if (cmd == 'x') { + + setBody = true; + // start accumulating masses for the composite geometries + dMass m2; + dMassSetZero (&m); + + dReal dpos[GPB][3]; // delta-positions for composite geometries + dMatrix3 drot[GPB]; + + // set random delta positions + for (j=0; j= num) selected = 0; + if (selected < 0) selected = 0; + } + else if (cmd == 'd' && selected >= 0 && selected < num) { + dBodyDisable (obj[selected].body); + } + else if (cmd == 'e' && selected >= 0 && selected < num) { + dBodyEnable (obj[selected].body); + } + else if (cmd == 'a') { + show_aabb ^= 1; + } + else if (cmd == 't') { + show_contacts ^= 1; + } + else if (cmd == 'r') { + random_pos ^= 1; + } +} + + +// draw a geom + +void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + if (!g) return; + if (!pos) pos = dGeomGetPosition (g); + if (!R) R = dGeomGetRotation (g); + + int type = dGeomGetClass (g); + if (type == dBoxClass) { + dVector3 sides; + dGeomBoxGetLengths (g,sides); + dsDrawBox (pos,R,sides); + } + else if (type == dSphereClass) { + dsDrawSphere (pos,R,dGeomSphereGetRadius (g)); + } + else if (type == dCapsuleClass) { + dReal radius,length; + dGeomCapsuleGetParams (g,&radius,&length); + dsDrawCapsule (pos,R,length,radius); + } + else if (type == dCylinderClass) { + dReal radius,length; + dGeomCylinderGetParams (g,&radius,&length); + dsDrawCylinder (pos,R,length,radius); + } else if (type == dConvexClass) { + //dVector3 sides={0.50,0.50,0.50}; + dsDrawConvex(pos,R,planes, + planecount, + points, + pointcount, + polygons); + } + + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB (g,aabb); + dVector3 bbpos; + for (int i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (int j=0; j<3; j++) bbsides[j] = aabb[j*2+1] - aabb[j*2]; + dMatrix3 RI; + dRSetIdentity (RI); + dsSetColorAlpha (1,0,0,0.5); + dsDrawBox (bbpos,RI,bbsides); + } +} + + +// set previous transformation matrix for trimesh +void setCurrentTransform(dGeomID geom) +{ + const dReal* Pos = dGeomGetPosition(geom); + const dReal* Rot = dGeomGetRotation(geom); + + const dReal Transform[16] = + { + Rot[0], Rot[4], Rot[8], 0, + Rot[1], Rot[5], Rot[9], 0, + Rot[2], Rot[6], Rot[10], 0, + Pos[0], Pos[1], Pos[2], 1 + }; + + dGeomTriMeshSetLastTransform( geom, *(dMatrix4*)(&Transform) ); + +} + + +// simulation loop + +static void simLoop (int pause) +{ + dsSetColor (0,0,2); + dSpaceCollide (space,0,&nearCallback); + + +#if 1 + // What is this for??? - Bram + if (!pause) + { + for (int i=0; i +#include + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +//**************************************************************************** +// matrix sizes + +#define dALIGN_SIZE(buf_size, alignment) (((buf_size) + (alignment - 1)) & (int)(~(alignment - 1))) // Casting the mask to int ensures sign-extension to larger integer sizes +#define dALIGN_PTR(buf_ptr, alignment) ((void *)(((duintptr)(buf_ptr) + ((alignment) - 1)) & (int)(~(alignment - 1)))) // Casting the mask to int ensures sign-extension to larger integer sizes + +#define MSIZE 21 +#define MSIZE4 dALIGN_SIZE(MSIZE, 4) // MSIZE rounded up to 4 + + + +//**************************************************************************** +// matrix accessors + +#define _A(i,j) A[(i)*4+(j)] +#define _I(i,j) I[(i)*4+(j)] +#define _R(i,j) R[(i)*4+(j)] + +//**************************************************************************** +// tolerances + +#ifdef dDOUBLE +const double tol = 1e-10; +#endif + +#ifdef dSINGLE +const double tol = 1e-5; +#endif + +//**************************************************************************** +// misc messages and error handling + +#ifdef __GNUC__ +#define HEADER printf ("%s()\n", __FUNCTION__); +#else +#define HEADER printf ("%s:%d\n",__FILE__,__LINE__); +#endif + +static jmp_buf jump_buffer; + + +void myMessageFunction (int num, const char *msg, va_list ap) +{ + printf ("(Message %d: ",num); + vprintf (msg,ap); + printf (")"); + dSetMessageHandler (0); + longjmp (jump_buffer,1); +} + + +#define TRAP_MESSAGE(do,ifnomsg,ifmsg) \ + dSetMessageHandler (&myMessageFunction); \ + if (setjmp (jump_buffer)) { \ + dSetMessageHandler (0); \ + ifmsg ; \ + } \ + else { \ + dSetMessageHandler (&myMessageFunction); \ + do ; \ + ifnomsg ; \ + } \ + dSetMessageHandler (0); + +//**************************************************************************** +// utility stuff + +// compare two numbers, within a threshhold, return 1 if approx equal + +int cmp (dReal a, dReal b) +{ + return (fabs(a-b) < tol); +} + +//**************************************************************************** +// matrix utility stuff + +// compare a 3x3 matrix with the identity + +int cmpIdentityMat3 (dMatrix3 A) +{ + return + (cmp(_A(0,0),1.0) && cmp(_A(0,1),0.0) && cmp(_A(0,2),0.0) && + cmp(_A(1,0),0.0) && cmp(_A(1,1),1.0) && cmp(_A(1,2),0.0) && + cmp(_A(2,0),0.0) && cmp(_A(2,1),0.0) && cmp(_A(2,2),1.0)); +} + + +// transpose a 3x3 matrix in-line + +void transpose3x3 (dMatrix3 A) +{ + dReal tmp; + tmp=A[4]; A[4]=A[1]; A[1]=tmp; + tmp=A[8]; A[8]=A[2]; A[2]=tmp; + tmp=A[9]; A[9]=A[6]; A[6]=tmp; +} + +//**************************************************************************** +// test miscellaneous math functions + +void testRandomNumberGenerator() +{ + HEADER; + if (dTestRand()) printf ("\tpassed\n"); + else printf ("\tFAILED\n"); +} + + +void testInfinity() +{ + HEADER; + if (1e10 < dInfinity && -1e10 > -dInfinity && -dInfinity < dInfinity) + printf ("\tpassed\n"); + else printf ("\tFAILED\n"); +} + + +void testPad() +{ + HEADER; + char s[100]; + s[0]=0; + for (int i=0; i<=16; i++) sprintf (s+strlen(s),"%d ",dPAD(i)); + printf ("\t%s\n", strcmp(s,"0 1 4 4 4 8 8 8 8 12 12 12 12 16 16 16 16 ") ? + "FAILED" : "passed"); +} + + +void testCrossProduct() +{ + HEADER; + + dVector3 a1,a2,b,c; + dMatrix3 B; + dMakeRandomVector (b,3,1.0); + dMakeRandomVector (c,3,1.0); + + dCalcVectorCross3(a1,b,c); + + dSetZero (B,12); + dSetCrossMatrixPlus(B,b,4); + dMultiply0 (a2,B,c,3,3,1); + + dReal diff = dMaxDifference(a1,a2,3,1); + printf ("\t%s\n", diff > tol ? "FAILED" : "passed"); +} + + +void testSetZero() +{ + HEADER; + dReal a[100]; + dMakeRandomVector (a,100,1.0); + dSetZero (a,100); + for (int i=0; i<100; i++) if (a[i] != 0.0) { + printf ("\tFAILED\n"); + return; + } + printf ("\tpassed\n"); +} + + +void testNormalize3() +{ + HEADER; + int i,j,bad=0; + dVector3 n1,n2; + for (i=0; i<1000; i++) { + dMakeRandomVector (n1,3,1.0); + for (j=0; j<3; j++) n2[j]=n1[j]; + dNormalize3 (n2); + if (dFabs(dCalcVectorDot3(n2,n2) - 1.0) > tol) bad |= 1; + if (dFabs(n2[0]/n1[0] - n2[1]/n1[1]) > tol) bad |= 2; + if (dFabs(n2[0]/n1[0] - n2[2]/n1[2]) > tol) bad |= 4; + if (dFabs(n2[1]/n1[1] - n2[2]/n1[2]) > tol) bad |= 8; + if (dFabs(dCalcVectorDot3(n2,n1) - dSqrt(dCalcVectorDot3(n1,n1))) > tol) bad |= 16; + if (bad) { + printf ("\tFAILED (code=%x)\n",bad); + return; + } + } + printf ("\tpassed\n"); +} + + +/* +void testReorthonormalize() +{ +HEADER; +dMatrix3 R,I; +dMakeRandomMatrix (R,3,3,1.0); +for (int i=0; i<30; i++) dReorthonormalize (R); +dMultiply2 (I,R,R,3,3,3); +printf ("\t%s\n",cmpIdentityMat3 (I) ? "passed" : "FAILED"); +} +*/ + + +void testPlaneSpace() +{ + HEADER; + dVector3 n,p,q; + int bad = 0; + for (int i=0; i<1000; i++) { + dMakeRandomVector (n,3,1.0); + dNormalize3 (n); + dPlaneSpace (n,p,q); + if (fabs(dCalcVectorDot3(n,p)) > tol) bad = 1; + if (fabs(dCalcVectorDot3(n,q)) > tol) bad = 1; + if (fabs(dCalcVectorDot3(p,q)) > tol) bad = 1; + if (fabs(dCalcVectorDot3(p,p)-1) > tol) bad = 1; + if (fabs(dCalcVectorDot3(q,q)-1) > tol) bad = 1; + } + printf ("\t%s\n", bad ? "FAILED" : "passed"); +} + +//**************************************************************************** +// test matrix functions + +void testMatrixMultiply() +{ + // A is 2x3, B is 3x4, B2 is B except stored columnwise, C is 2x4 + dReal A[8],B[12],A2[12],B2[16],C[8]; + int i; + + HEADER; + dSetZero (A,8); + for (i=0; i<3; i++) A[i] = i+2; + for (i=0; i<3; i++) A[i+4] = i+3+2; + for (i=0; i<12; i++) B[i] = i+8; + dSetZero (A2,12); + for (i=0; i<6; i++) A2[i+2*(i/2)] = A[i+i/3]; + dSetZero (B2,16); + for (i=0; i<12; i++) B2[i+i/3] = B[i]; + + dMultiply0 (C,A,B,2,3,4); + if (C[0] != 116 || C[1] != 125 || C[2] != 134 || C[3] != 143 || + C[4] != 224 || C[5] != 242 || C[6] != 260 || C[7] != 278) + printf ("\tFAILED (1)\n"); else printf ("\tpassed (1)\n"); + + dMultiply1 (C,A2,B,2,3,4); + if (C[0] != 160 || C[1] != 172 || C[2] != 184 || C[3] != 196 || + C[4] != 196 || C[5] != 211 || C[6] != 226 || C[7] != 241) + printf ("\tFAILED (2)\n"); else printf ("\tpassed (2)\n"); + + dMultiply2 (C,A,B2,2,3,4); + if (C[0] != 83 || C[1] != 110 || C[2] != 137 || C[3] != 164 || + C[4] != 164 || C[5] != 218 || C[6] != 272 || C[7] != 326) + printf ("\tFAILED (3)\n"); else printf ("\tpassed (3)\n"); +} + + +void testSmallMatrixMultiply() +{ + dMatrix3 A,B,C,A2; + dVector3 a,a2,x; + + HEADER; + dMakeRandomMatrix (A,3,3,1.0); + dMakeRandomMatrix (B,3,3,1.0); + dMakeRandomMatrix (C,3,3,1.0); + dMakeRandomMatrix (x,3,1,1.0); + + // dMultiply0_331() + dMultiply0_331 (a,B,x); + dMultiply0 (a2,B,x,3,3,1); + printf ("\t%s (1)\n",(dMaxDifference (a,a2,3,1) > tol) ? "FAILED" : + "passed"); + + // dMultiply1_331() + dMultiply1_331 (a,B,x); + dMultiply1 (a2,B,x,3,3,1); + printf ("\t%s (2)\n",(dMaxDifference (a,a2,3,1) > tol) ? "FAILED" : + "passed"); + + // dMultiply0_133 + dMultiply0_133 (a,x,B); + dMultiply0 (a2,x,B,1,3,3); + printf ("\t%s (3)\n",(dMaxDifference (a,a2,1,3) > tol) ? "FAILED" : + "passed"); + + // dMultiply0_333() + dMultiply0_333 (A,B,C); + dMultiply0 (A2,B,C,3,3,3); + printf ("\t%s (4)\n",(dMaxDifference (A,A2,3,3) > tol) ? "FAILED" : + "passed"); + + // dMultiply1_333() + dMultiply1_333 (A,B,C); + dMultiply1 (A2,B,C,3,3,3); + printf ("\t%s (5)\n",(dMaxDifference (A,A2,3,3) > tol) ? "FAILED" : + "passed"); + + // dMultiply2_333() + dMultiply2_333 (A,B,C); + dMultiply2 (A2,B,C,3,3,3); + printf ("\t%s (6)\n",(dMaxDifference (A,A2,3,3) > tol) ? "FAILED" : + "passed"); +} + + +void testCholeskyFactorization() +{ + dsizeint matrixSize = sizeof(dReal) * MSIZE4 * MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *B = (dReal *)dAlloc(matrixSize), *C = (dReal *)dAlloc(matrixSize), diff; + + HEADER; + dMakeRandomMatrix (A,MSIZE,MSIZE,1.0); + dMultiply2 (B,A,A,MSIZE,MSIZE,MSIZE); + memcpy (A,B,MSIZE4*MSIZE*sizeof(dReal)); + if (dFactorCholesky (B,MSIZE)) printf ("\tpassed (1)\n"); + else printf ("\tFAILED (1)\n"); + dClearUpperTriangle (B,MSIZE); + dMultiply2 (C,B,B,MSIZE,MSIZE,MSIZE); + diff = dMaxDifference(A,C,MSIZE,MSIZE); + printf ("\tmaximum difference = %.6e - %s (2)\n",diff, + diff > tol ? "FAILED" : "passed"); + + dFree(C, matrixSize); + dFree(B, matrixSize); + dFree(A, matrixSize); +} + + +void testCholeskySolve() +{ + dsizeint matrixSize = sizeof(dReal) * MSIZE4 * MSIZE, vectorSize = sizeof(dReal) * MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize); + dReal *b = (dReal *)dAlloc(vectorSize), *x = (dReal *)dAlloc(vectorSize), *btest = (dReal *)dAlloc(vectorSize), diff; + + HEADER; + + // get A,L = PD matrix + dMakeRandomMatrix (A,MSIZE,MSIZE,1.0); + dMultiply2 (L,A,A,MSIZE,MSIZE,MSIZE); + memcpy (A,L,MSIZE4*MSIZE*sizeof(dReal)); + + // get b,x = right hand side + dMakeRandomMatrix (b,MSIZE,1,1.0); + memcpy (x,b,MSIZE*sizeof(dReal)); + + // factor L + if (dFactorCholesky (L,MSIZE)) printf ("\tpassed (1)\n"); + else printf ("\tFAILED (1)\n"); + dClearUpperTriangle (L,MSIZE); + + // solve A*x = b + dSolveCholesky (L,x,MSIZE); + + // compute A*x and compare it with b + dMultiply2 (btest,A,x,MSIZE,MSIZE,1); + diff = dMaxDifference(b,btest,MSIZE,1); + printf ("\tmaximum difference = %.6e - %s (2)\n",diff, + diff > tol ? "FAILED" : "passed"); + + dFree(btest, vectorSize); + dFree(x, vectorSize); + dFree(b, vectorSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + + +void testInvertPDMatrix() +{ + int i,j,ok; + dsizeint matrixSize = sizeof(dReal) * MSIZE4 * MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *Ainv = (dReal *)dAlloc(matrixSize), *I = (dReal *)dAlloc(matrixSize); + + HEADER; + + dMakeRandomMatrix (A,MSIZE,MSIZE,1.0); + dMultiply2 (Ainv,A,A,MSIZE,MSIZE,MSIZE); + memcpy (A,Ainv,MSIZE4*MSIZE*sizeof(dReal)); + dSetZero (Ainv,MSIZE4*MSIZE); + + if (dInvertPDMatrix (A,Ainv,MSIZE)) + printf ("\tpassed (1)\n"); else printf ("\tFAILED (1)\n"); + dMultiply0 (I,A,Ainv,MSIZE,MSIZE,MSIZE); + + // compare with identity + ok = 1; + for (i=0; i tol ? "FAILED" : "passed"); + + dFree(d, vectorSize); + dFree(ATEST, matrixSize); + dFree(DL, matrixSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + + +void testCoopLDLTFactorization() +{ + int i,j; + + const dsizeint COOP_MSIZE = MSIZE * 51, COOP_MSIZE4 = dALIGN_SIZE(COOP_MSIZE, 4); + + dsizeint matrixSize = sizeof(dReal) * COOP_MSIZE4 * COOP_MSIZE, vectorSize = sizeof(dReal) * COOP_MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize), *DL = (dReal *)dAlloc(matrixSize), + *ATEST = (dReal *)dAlloc(matrixSize), *d = (dReal *)dAlloc(vectorSize), diff; + + const unsigned threadCountMaximum = 8; + dThreadingImplementationID threading = dThreadingAllocateMultiThreadedImplementation(); + dCooperativeID cooperative = dCooperativeCreate(dThreadingImplementationGetFunctions(threading), threading); + dThreadingThreadPoolID pool = dThreadingAllocateThreadPool(threadCountMaximum, 0, dAllocateFlagBasicData, NULL); + dThreadingThreadPoolServeMultiThreadedImplementation(pool, threading); + + dResourceRequirementsID requirements = dResourceRequirementsCreate(cooperative); + dEstimateCooperativelyFactorLDLTResourceRequirements(requirements, threadCountMaximum, COOP_MSIZE); + dResourceContainerID resources = dResourceContainerAcquire(requirements); + + HEADER; + + for (int pass = 0; pass != 4; ++pass) + { + dTimerStart ("Factoring LDLT"); + + const unsigned allowedThreads = 4; + const unsigned PASS_MSIZE = COOP_MSIZE - pass, PASS_MSIZE4 = dALIGN_SIZE(PASS_MSIZE, 4); + + dTimerNow ("Preparing data"); + dMakeRandomMatrix (L, PASS_MSIZE, PASS_MSIZE, 1.0); + dMultiply2 (A, L, L, PASS_MSIZE, PASS_MSIZE, PASS_MSIZE); + memcpy (L, A, sizeof(dReal) * PASS_MSIZE4 * PASS_MSIZE); + + dTimerNow ("Factoring multi threaded"); + dCooperativelyFactorLDLT (resources, allowedThreads, L, d, PASS_MSIZE, PASS_MSIZE4); + + dTimerNow ("Verifying"); + dClearUpperTriangle (L, PASS_MSIZE); + for (i = 0; i < PASS_MSIZE; i++) L[i * PASS_MSIZE4 + i] = 1.0; + + dSetZero (DL, PASS_MSIZE4 * PASS_MSIZE); + for (i = 0; i < PASS_MSIZE; i++) { + for (j = 0; j < PASS_MSIZE; j++) DL[i * PASS_MSIZE4 + j] = L[i * PASS_MSIZE4 + j] / d[j]; + } + + dMultiply2 (ATEST, L, DL, PASS_MSIZE, PASS_MSIZE, PASS_MSIZE); + diff = dMaxDifference(A, ATEST, PASS_MSIZE, PASS_MSIZE); + printf ("\tN=%u: maximum difference = %.6e - %s\n", PASS_MSIZE, diff, diff > 1e2 * tol ? "FAILED" : "passed"); + + dTimerEnd(); + dTimerReport(stdout, 0); + } + + dResourceContainerDestroy(resources); + dResourceRequirementsDestroy(requirements); + + dThreadingImplementationShutdownProcessing(threading); + dThreadingFreeThreadPool(pool); + dCooperativeDestroy(cooperative); + dThreadingFreeImplementation(threading); + + dFree(d, vectorSize); + dFree(ATEST, matrixSize); + dFree(DL, matrixSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + + +void testSolveLDLT() +{ + dsizeint matrixSize = sizeof(dReal) * MSIZE4 * MSIZE, vectorSize = sizeof(dReal) * MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize), + *d = (dReal *)dAlloc(vectorSize), *x = (dReal *)dAlloc(vectorSize), + *b = (dReal *)dAlloc(vectorSize), *btest = (dReal *)dAlloc(vectorSize), diff; + + HEADER; + + dMakeRandomMatrix (A,MSIZE,MSIZE,1.0); + dMultiply2 (L,A,A,MSIZE,MSIZE,MSIZE); + memcpy (A,L,MSIZE4*MSIZE*sizeof(dReal)); + + dMakeRandomMatrix (b,MSIZE,1,1.0); + memcpy (x,b,MSIZE*sizeof(dReal)); + + dFactorLDLT (L,d,MSIZE,MSIZE4); + dSolveLDLT (L,d,x,MSIZE,MSIZE4); + + dMultiply2 (btest,A,x,MSIZE,MSIZE,1); + diff = dMaxDifference(b,btest,MSIZE,1); + printf ("\tmaximum difference = %.6e - %s\n",diff, + diff > tol ? "FAILED" : "passed"); + + dFree(btest, vectorSize); + dFree(b, vectorSize); + dFree(x, vectorSize); + dFree(d, vectorSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + +void testCoopSolveLDLT() +{ + const dsizeint COOP_MSIZE = MSIZE * 51, COOP_MSIZE4 = dALIGN_SIZE(COOP_MSIZE, 4); + + dsizeint matrixSize = sizeof(dReal) * COOP_MSIZE4 * COOP_MSIZE, vectorSize = sizeof(dReal) * COOP_MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize), + *d = (dReal *)dAlloc(vectorSize), *x = (dReal *)dAlloc(vectorSize), + *b = (dReal *)dAlloc(vectorSize), *btest = (dReal *)dAlloc(vectorSize), diff; + + const unsigned threadCountMaximum = 8; + dThreadingImplementationID threading = dThreadingAllocateMultiThreadedImplementation(); + dCooperativeID cooperative = dCooperativeCreate(dThreadingImplementationGetFunctions(threading), threading); + dThreadingThreadPoolID pool = dThreadingAllocateThreadPool(threadCountMaximum, 0, dAllocateFlagBasicData, NULL); + dThreadingThreadPoolServeMultiThreadedImplementation(pool, threading); + + dResourceRequirementsID requirements = dResourceRequirementsCreate(cooperative); + dEstimateCooperativelySolveLDLTResourceRequirements(requirements, threadCountMaximum, COOP_MSIZE); + dResourceContainerID resources = dResourceContainerAcquire(requirements); + + HEADER; + + for (int pass = 0; pass != 4; ++pass) + { + dTimerStart ("Solving LDLT"); + + const unsigned allowedThreads = 4; + const unsigned PASS_MSIZE = COOP_MSIZE - pass, PASS_MSIZE4 = dALIGN_SIZE(PASS_MSIZE, 4); + + dTimerNow ("Preparing data"); + dMakeRandomMatrix (b, PASS_MSIZE, 1, 1.0); + + dMakeRandomMatrix (L, PASS_MSIZE, PASS_MSIZE, 1.0); + dMultiply2 (A, L, L, PASS_MSIZE, PASS_MSIZE, PASS_MSIZE); + + memcpy (x, b, PASS_MSIZE * sizeof(dReal)); + memcpy (L, A, sizeof(dReal) * PASS_MSIZE4 * PASS_MSIZE); + + dTimerNow ("Factoring"); + dFactorLDLT (L, d, PASS_MSIZE, PASS_MSIZE4); + + dTimerNow ("Solving multi-threaded"); + dCooperativelySolveLDLT(resources, allowedThreads, L, d, x, PASS_MSIZE, PASS_MSIZE4); + + dTimerNow ("Verifying solution"); + dMultiply2 (btest, A, x, PASS_MSIZE, PASS_MSIZE, 1); + diff = dMaxDifference(b, btest, PASS_MSIZE, 1); + printf ("\tN=%u: maximum difference = %.6e - %s\n", PASS_MSIZE, diff, diff > 1e2 * tol ? "FAILED" : "passed"); + + dTimerEnd(); + dTimerReport(stdout, 0); + } + + dResourceContainerDestroy(resources); + dResourceRequirementsDestroy(requirements); + + dThreadingImplementationShutdownProcessing(threading); + dThreadingFreeThreadPool(pool); + dCooperativeDestroy(cooperative); + dThreadingFreeImplementation(threading); + + dFree(btest, vectorSize); + dFree(b, vectorSize); + dFree(x, vectorSize); + dFree(d, vectorSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + + +void testLDLTAddTL() +{ + int i,j; + dsizeint matrixSize = sizeof(dReal) * MSIZE4 * MSIZE, vectorSize = sizeof(dReal) * MSIZE; + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize), + *DL = (dReal *)dAlloc(matrixSize), *ATEST = (dReal *)dAlloc(matrixSize), + *d = (dReal *)dAlloc(vectorSize), *a = (dReal *)dAlloc(vectorSize), diff; + + HEADER; + + dMakeRandomMatrix (A,MSIZE,MSIZE,1.0); + dMultiply2 (L,A,A,MSIZE,MSIZE,MSIZE); + memcpy (A,L,MSIZE4*MSIZE*sizeof(dReal)); + dFactorLDLT (L,d,MSIZE,MSIZE4); + + // delete first row and column of factorization + for (i=0; i tol ? "FAILED" : "passed"); + + dFree(a, vectorSize); + dFree(d, vectorSize); + dFree(ATEST, matrixSize); + dFree(DL, matrixSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + + +void testLDLTRemove() +{ + int i,j,r; + dsizeint intVectorSize = sizeof(int) * MSIZE, matrixSize = sizeof(dReal) * MSIZE4 * MSIZE, vectorSize = sizeof(dReal) * MSIZE; + int *p = (int *)dAlloc(intVectorSize); + dReal *A = (dReal *)dAlloc(matrixSize), *L = (dReal *)dAlloc(matrixSize), + *L2 = (dReal *)dAlloc(matrixSize), *DL2 = (dReal *)dAlloc(matrixSize), + *Atest1 = (dReal *)dAlloc(matrixSize), *Atest2 = (dReal *)dAlloc(matrixSize), + *d = (dReal *)dAlloc(vectorSize), *d2 = (dReal *)dAlloc(vectorSize), diff, maxdiff; + + HEADER; + + // make array of A row pointers + dReal *Arows[MSIZE]; + for (i=0; i= r) ii--; + if (jj >= r) jj--; + if (A[i*MSIZE4+j] != Atest1[ii*MSIZE4+jj]) bad = 1; + } + } + } + if (bad) printf ("\trow/col removal FAILED for row %d\n",r); + + // zero out last row/column of Atest1 + for (i=0; i tol ? "FAILED" : "passed"); + + dFree(d2, vectorSize); + dFree(d, vectorSize); + dFree(Atest2, matrixSize); + dFree(Atest1, matrixSize); + dFree(DL2, matrixSize); + dFree(L2, matrixSize); + dFree(L, matrixSize); + dFree(A, matrixSize); +} + +//**************************************************************************** +// test mass stuff + +#define NUMP 10 // number of particles + + +void printMassParams (dMass *m) +{ + printf ("mass = %.4f\n",m->mass); + printf ("com = (%.4f,%.4f,%.4f)\n",m->c[0],m->c[1],m->c[2]); + printf ("I = [ %10.4f %10.4f %10.4f ]\n" + " [ %10.4f %10.4f %10.4f ]\n" + " [ %10.4f %10.4f %10.4f ]\n", + m->_I(0,0),m->_I(0,1),m->_I(0,2), + m->_I(1,0),m->_I(1,1),m->_I(1,2), + m->_I(2,0),m->_I(2,1),m->_I(2,2)); +} + + +void compareMassParams (dMass *m1, dMass *m2, const char *msg) +{ + int i,j,ok = 1; + if (!(cmp(m1->mass,m2->mass) && cmp(m1->c[0],m2->c[0]) && + cmp(m1->c[1],m2->c[1]) && cmp(m1->c[2],m2->c[2]))) + ok = 0; + for (i=0; i<3; i++) for (j=0; j<3; j++) + if (cmp (m1->_I(i,j),m2->_I(i,j))==0) ok = 0; + if (ok) printf ("\tpassed (%s)\n",msg); else printf ("\tFAILED (%s)\n",msg); +} + + +// compute the mass parameters of a particle set + +void computeMassParams (dMass *m, dReal q[NUMP][3], dReal pm[NUMP]) +{ + int i,j; + dMassSetZero (m); + for (i=0; imass += pm[i]; + for (j=0; j<3; j++) m->c[j] += pm[i]*q[i][j]; + m->_I(0,0) += pm[i]*(q[i][1]*q[i][1] + q[i][2]*q[i][2]); + m->_I(1,1) += pm[i]*(q[i][0]*q[i][0] + q[i][2]*q[i][2]); + m->_I(2,2) += pm[i]*(q[i][0]*q[i][0] + q[i][1]*q[i][1]); + m->_I(0,1) -= pm[i]*(q[i][0]*q[i][1]); + m->_I(0,2) -= pm[i]*(q[i][0]*q[i][2]); + m->_I(1,2) -= pm[i]*(q[i][1]*q[i][2]); + } + for (j=0; j<3; j++) m->c[j] /= m->mass; + m->_I(1,0) = m->_I(0,1); + m->_I(2,0) = m->_I(0,2); + m->_I(2,1) = m->_I(1,2); +} + + +void testMassFunctions() +{ + dMass m; + int i,j; + dReal q[NUMP][3]; // particle positions + dReal pm[NUMP]; // particle masses + dMass m1,m2; + dMatrix3 R; + + HEADER; + + printf ("\t"); + dMassSetZero (&m); + TRAP_MESSAGE (dMassSetParameters (&m,10, 0,0,0, 1,2,3, 4,5,6), + printf (" FAILED (1)\n"), printf (" passed (1)\n")); + + printf ("\t"); + dMassSetZero (&m); + TRAP_MESSAGE (dMassSetParameters (&m,10, 0.1,0.2,0.15, 3,5,14, 3.1,3.2,4), + printf ("passed (2)\n") , printf (" FAILED (2)\n")); + if (m.mass==10 && m.c[0]==REAL(0.1) && m.c[1]==REAL(0.2) && + m.c[2]==REAL(0.15) && m._I(0,0)==3 && m._I(1,1)==5 && m._I(2,2)==14 && + m._I(0,1)==REAL(3.1) && m._I(0,2)==REAL(3.2) && m._I(1,2)==4 && + m._I(1,0)==REAL(3.1) && m._I(2,0)==REAL(3.2) && m._I(2,1)==4) + printf ("\tpassed (3)\n"); else printf ("\tFAILED (3)\n"); + + dMassSetZero (&m); + dMassSetSphere (&m,1.4, 0.86); + if (cmp(m.mass,3.73002719949386) && m.c[0]==0 && m.c[1]==0 && m.c[2]==0 && + cmp(m._I(0,0),1.10349124669826) && + cmp(m._I(1,1),1.10349124669826) && + cmp(m._I(2,2),1.10349124669826) && + m._I(0,1)==0 && m._I(0,2)==0 && m._I(1,2)==0 && + m._I(1,0)==0 && m._I(2,0)==0 && m._I(2,1)==0) + printf ("\tpassed (4)\n"); else printf ("\tFAILED (4)\n"); + + dMassSetZero (&m); + dMassSetCapsule (&m,1.3,1,0.76,1.53); + if (cmp(m.mass,5.99961928996029) && m.c[0]==0 && m.c[1]==0 && m.c[2]==0 && + cmp(m._I(0,0),1.59461986077384) && + cmp(m._I(1,1),4.21878433864904) && + cmp(m._I(2,2),4.21878433864904) && + m._I(0,1)==0 && m._I(0,2)==0 && m._I(1,2)==0 && + m._I(1,0)==0 && m._I(2,0)==0 && m._I(2,1)==0) + printf ("\tpassed (5)\n"); else printf ("\tFAILED (5)\n"); + + dMassSetZero (&m); + dMassSetBox (&m,0.27,3,4,5); + if (cmp(m.mass,16.2) && m.c[0]==0 && m.c[1]==0 && m.c[2]==0 && + cmp(m._I(0,0),55.35) && cmp(m._I(1,1),45.9) && cmp(m._I(2,2),33.75) && + m._I(0,1)==0 && m._I(0,2)==0 && m._I(1,2)==0 && + m._I(1,0)==0 && m._I(2,0)==0 && m._I(2,1)==0) + printf ("\tpassed (6)\n"); else printf ("\tFAILED (6)\n"); + + // test dMassAdjust? + + // make random particles and compute the mass, COM and inertia, then + // translate and repeat. + for (i=0; i Q -> R works + dReal maxdiff=0; + for (i=0; i<100; i++) { + makeRandomRotation (R); + dRtoQ (R,q); + dQtoR (q,R2); + dReal diff = dMaxDifference (R,R2,3,3); + if (diff > maxdiff) maxdiff = diff; + } + printf ("\tmaximum difference = %e - %s (3)\n",maxdiff, + (maxdiff > tol) ? "FAILED" : "passed"); +} + + +void testQuaternionMultiply() +{ + HEADER; + dMatrix3 RA,RB,RC,Rtest; + dQuaternion qa,qb,qc; + dReal diff,maxdiff=0; + + for (int i=0; i<100; i++) { + makeRandomRotation (RB); + makeRandomRotation (RC); + dRtoQ (RB,qb); + dRtoQ (RC,qc); + + dMultiply0 (RA,RB,RC,3,3,3); + dQMultiply0 (qa,qb,qc); + dQtoR (qa,Rtest); + diff = dMaxDifference (Rtest,RA,3,3); + if (diff > maxdiff) maxdiff = diff; + + dMultiply1 (RA,RB,RC,3,3,3); + dQMultiply1 (qa,qb,qc); + dQtoR (qa,Rtest); + diff = dMaxDifference (Rtest,RA,3,3); + if (diff > maxdiff) maxdiff = diff; + + dMultiply2 (RA,RB,RC,3,3,3); + dQMultiply2 (qa,qb,qc); + dQtoR (qa,Rtest); + diff = dMaxDifference (Rtest,RA,3,3); + if (diff > maxdiff) maxdiff = diff; + + dMultiply0 (RA,RC,RB,3,3,3); + transpose3x3 (RA); + dQMultiply3 (qa,qb,qc); + dQtoR (qa,Rtest); + diff = dMaxDifference (Rtest,RA,3,3); + if (diff > maxdiff) maxdiff = diff; + } + printf ("\tmaximum difference = %e - %s\n",maxdiff, + (maxdiff > tol) ? "FAILED" : "passed"); +} + + +void testRotationFunctions() +{ + dMatrix3 R1; + HEADER; + + printf ("\tdRSetIdentity - "); + dMakeRandomMatrix (R1,3,3,1.0); + dRSetIdentity (R1); + if (cmpIdentityMat3(R1)) printf ("passed\n"); else printf ("FAILED\n"); + + printf ("\tdRFromAxisAndAngle - "); + + printf ("\n"); + printf ("\tdRFromEulerAngles - "); + + printf ("\n"); + printf ("\tdRFrom2Axes - "); + + printf ("\n"); +} + +//**************************************************************************** + +#include + +template +class simplevector +{ +private: + int n; + int max; + T* data; + +public: + simplevector() { initialize(); } + ~simplevector() { finalize(); } + T& operator[](int i) { assert(i>=0 && i=0 && i mat; + int afterfirst,index; + +public: + dMatrixComparison(); + ~dMatrixComparison(); + + dReal nextMatrix (dReal *A, int n, int m, int lower_tri, const char *name, ...); + // add a new n*m matrix A to the sequence. the name of the matrix is given + // by the printf-style arguments (name,...). if this is the first sequence + // then this object will simply record the matrices and return 0. + // if this the second or subsequent sequence then this object will compare + // the matrices with the first sequence, and report any differences. + // the matrix error will be returned. if `lower_tri' is 1 then only the + // lower triangle of the matrix (including the diagonal) will be compared + // (the matrix must be square). + + void end(); + // end a sequence. + + void reset(); + // restarts the object, so the next sequence will be the first sequence. + + void dump(); + // print out info about all the matrices in the sequence +}; + +struct dMatrixComparison::dMatInfo { + int n,m; // size of matrix + char name[128]; // name of the matrix + dReal *data; // matrix data + int size; // size of `data' +}; + + + +dMatrixComparison::dMatrixComparison() +{ + afterfirst = 0; + index = 0; +} + + +dMatrixComparison::~dMatrixComparison() +{ + reset(); +} + + +dReal dMatrixComparison::nextMatrix (dReal *A, int n, int m, int lower_tri, + const char *name, ...) +{ + if (A==0 || n < 1 || m < 1 || name==0) dDebug (0,"bad args to nextMatrix"); + int num = n*dPAD(m); + + if (afterfirst==0) { + dMatInfo *mi = (dMatInfo*) dAlloc (sizeof(dMatInfo)); + mi->n = n; + mi->m = m; + mi->size = num * sizeof(dReal); + mi->data = (dReal*) dAlloc (mi->size); + memcpy (mi->data,A,mi->size); + + va_list ap; + va_start (ap,name); + vsprintf (mi->name,name,ap); + va_end (ap); + if (strlen(mi->name) >= sizeof (mi->name)) dDebug (0,"name too long"); + + mat.push_back(mi); + return 0; + } + else { + if (lower_tri && n != m) + dDebug (0,"dMatrixComparison, lower triangular matrix must be square"); + if (index >= mat.size()) dDebug (0,"dMatrixComparison, too many matrices"); + dMatInfo *mp = mat[index]; + index++; + + dMatInfo mi; + va_list ap; + va_start (ap,name); + vsprintf (mi.name,name,ap); + va_end (ap); + if (strlen(mi.name) >= sizeof (mi.name)) dDebug (0,"name too long"); + + if (strcmp(mp->name,mi.name) != 0) + dDebug (0,"dMatrixComparison, name mismatch (\"%s\" and \"%s\")", + mp->name,mi.name); + if (mp->n != n || mp->m != m) + dDebug (0,"dMatrixComparison, size mismatch (%dx%d and %dx%d)", + mp->n,mp->m,n,m); + + dReal maxdiff; + if (lower_tri) { + maxdiff = dMaxDifferenceLowerTriangle (A,mp->data,n); + } + else { + maxdiff = dMaxDifference (A,mp->data,n,m); + } + if (maxdiff > tol) + dDebug (0,"dMatrixComparison, matrix error (size=%dx%d, name=\"%s\", " + "error=%.4e)",n,m,mi.name,maxdiff); + return maxdiff; + } +} + + +void dMatrixComparison::end() +{ + if (mat.size() <= 0) dDebug (0,"no matrices in sequence"); + afterfirst = 1; + index = 0; +} + + +void dMatrixComparison::reset() +{ + for (int i=0; idata,mat[i]->size); + dFree (mat[i],sizeof(dMatInfo)); + } + mat.clear(); + afterfirst = 0; + index = 0; +} + + +void dMatrixComparison::dump() +{ + for (int i=0; iname,mat[i]->n,mat[i]->m); +} + +//**************************************************************************** +// unit test + +#include + +// static jmp_buf jump_buffer; + +static void myDebug (int /*num*/, const char* /*msg*/, va_list /*ap*/) +{ + // printf ("(Error %d: ",num); + // vprintf (msg,ap); + // printf (")\n"); + longjmp (jump_buffer,1); +} + + +extern "C" void dTestMatrixComparison() +{ + volatile int i; + printf ("dTestMatrixComparison()\n"); + dMessageFunction *orig_debug = dGetDebugHandler(); + + dMatrixComparison mc; + dReal A[50*50]; + + // make first sequence + unsigned long seed = dRandGetSeed(); + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + + //mc.dump(); + + // test identical sequence + dSetDebugHandler (&myDebug); + dRandSetSeed (seed); + if (setjmp (jump_buffer)) { + printf ("\tFAILED (1)\n"); + } + else { + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + printf ("\tpassed (1)\n"); + } + dSetDebugHandler (orig_debug); + + // test broken sequences (with matrix error) + dRandSetSeed (seed); + volatile int passcount = 0; + for (i=1; i<49; i++) { + if (setjmp (jump_buffer)) { + passcount++; + } + else { + dSetDebugHandler (&myDebug); + dMakeRandomMatrix (A,i,i+1,1.0); + A[(i-1)*dPAD(i+1)+i] += REAL(0.01); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + dSetDebugHandler (orig_debug); + } + } + mc.end(); + printf ("\t%s (2)\n",(passcount == 48) ? "passed" : "FAILED"); + + // test broken sequences (with name error) + dRandSetSeed (seed); + passcount = 0; + for (i=1; i<49; i++) { + if (setjmp (jump_buffer)) { + passcount++; + } + else { + dSetDebugHandler (&myDebug); + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"B%d",i); + dSetDebugHandler (orig_debug); + } + } + mc.end(); + printf ("\t%s (3)\n",(passcount == 48) ? "passed" : "FAILED"); + + // test identical sequence again + dSetDebugHandler (&myDebug); + dRandSetSeed (seed); + if (setjmp (jump_buffer)) { + printf ("\tFAILED (4)\n"); + } + else { + for (i=1; i<49; i++) { + dMakeRandomMatrix (A,i,i+1,1.0); + mc.nextMatrix (A,i,i+1,0,"A%d",i); + } + mc.end(); + printf ("\tpassed (4)\n"); + } + dSetDebugHandler (orig_debug); +} + +//**************************************************************************** + +// internal unit tests +extern "C" void dTestDataStructures(); +extern "C" void dTestMatrixComparison(); +extern "C" int dTestSolveLCP(); + + +int main() +{ + dInitODE(); + testRandomNumberGenerator(); + testInfinity(); + testPad(); + testCrossProduct(); + testSetZero(); + testNormalize3(); + //testReorthonormalize(); ... not anymore + testPlaneSpace(); + testMatrixMultiply(); + testSmallMatrixMultiply(); + testCholeskyFactorization(); + testCholeskySolve(); + testInvertPDMatrix(); + testIsPositiveDefinite(); + testFastLDLTFactorization(); + testCoopLDLTFactorization(); + testSolveLDLT(); + testCoopSolveLDLT(); + testLDLTAddTL(); + testLDLTRemove(); + testMassFunctions(); + testRtoQandQtoR(); + testQuaternionMultiply(); + testRotationFunctions(); + dTestMatrixComparison(); + dTestSolveLCP(); + // dTestDataStructures(); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_piston.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_piston.cpp new file mode 100644 index 0000000..8a0453a --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_piston.cpp @@ -0,0 +1,813 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + * Created by: Remi Ricard * + * (remi.ricard@simlog.com or papaDoc@videotron.ca) * + * Creation date: 2007/05/04 * + *************************************************************************/ + +/* + This program demonstrates how the Piston joint works. + + A Piston joint enables the sliding of a body with respect to another body + and the 2 bodies are free to rotate about the sliding axis. + + - The yellow body is fixed to the world. + - The yellow body and the blue body are attached by a Piston joint with + the axis along the x direction. + - The purple object is a geometry obstacle. + - The red line is the representation of the prismatic axis + - The orange line is the representation of the rotoide axis + - The light blue ball is the anchor position + + N.B. Many command options are available type -h to print them. +*/ + +#include +#include +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawSphere dsDrawSphereD +#endif + + +const dReal VEL_INC = 0.01; // Velocity increment + +// physics parameters +const dReal PI = 3.14159265358979323846264338327950288419716939937510; +const dReal BODY1_LENGTH = 1.5; // Size along the X axis + +const dReal RADIUS = 0.2; +const dReal AXIS_RADIUS = 0.01; + + +#define X 0 +#define Y 1 +#define Z 2 + +enum INDEX +{ + BODY1 = 0, + BODY2, + RECT, + BOX, + OBS, + GROUND, + NUM_PARTS, + ALL = NUM_PARTS +}; + +const int catBits[NUM_PARTS+1] = +{ + 0x0001, ///< Ext Cylinder category + 0x0002, ///< Int Cylinder category + 0x0004, ///< Int_Rect Cylinder category + 0x0008, ///< Box category + 0x0010, ///< Obstacle category + 0x0020, ///< Ground category + ~0L ///< All categories +}; + +#define Mass1 10 +#define Mass2 8 + + +//camera view +static float xyz[3] = {2.0f,-3.5f,2.0000f}; +static float hpr[3] = {90.000f,-25.5000f,0.0000f}; + + +//world,space,body & geom +static dWorldID world; +static dSpaceID space; +static dJointGroupID contactgroup; +static dBodyID body[NUM_PARTS]; +static dGeomID geom[NUM_PARTS]; + +// Default Positions and anchor of the 2 bodies +dVector3 pos1; +dVector3 pos2; +dVector3 anchor; + +static dJoint *joint; + + +const dReal BODY2_SIDES[3] = {0.4, 0.4, 0.4}; +const dReal OBS_SIDES[3] = {1,1,1}; +const dReal RECT_SIDES[3] = {0.3, 0.1, 0.2}; + + +int type = dJointTypePiston; + +//#pragma message("tc to be changed to 0") + +int tc = 0; // The test case choice; + + +//collision detection +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i,n; + + dBodyID b1 = dGeomGetBody (o1); + dBodyID b2 = dGeomGetBody (o2); + if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact) ) return; + const int N = 10; + dContact contact[N]; + n = dCollide (o1,o2,N,&contact[0].geom,sizeof (dContact) ); + if (n > 0) + { + for (i=0; iattach (body[BODY1], body[BODY2]); + if (joint->getType() == dJointTypePiston) + dJointSetPistonAnchor(joint->id(), anchor[X], anchor[Y], anchor[Z]); + } + +} + + +// function to update camera position at each step. +void update() +{ +// static FILE *file = fopen("x:/sim/src/libode/tstsrcSF/export.dat", "w"); + +// static int cnt = 0; +// char str[24]; +// sprintf(str, "%06d",cnt++); + +// dWorldExportDIF(world, file, str); +} + + +// called when a key pressed +static void command (int cmd) +{ + switch (cmd) + { + case 'h' : + case 'H' : + case '?' : + printKeyBoardShortCut(); + break; + + // Force + case 'q' : + case 'Q' : + dBodyAddForce (body[BODY1],4,0,0); + break; + case 'w' : + case 'W' : + dBodyAddForce (body[BODY1],-4,0,0); + break; + + case 'a' : + case 'A' : + dBodyAddForce (body[BODY1],0,40,0); + break; + case 's' : + case 'S' : + dBodyAddForce (body[BODY1],0,-40,0); + break; + + case 'z' : + case 'Z' : + dBodyAddForce (body[BODY1],0,0,4); + break; + case 'x' : + case 'X' : + dBodyAddForce (body[BODY1],0,0,-4); + break; + + // Torque + case 'e': + case 'E': + dBodyAddTorque (body[BODY1],0.1,0,0); + break; + case 'r': + case 'R': + dBodyAddTorque (body[BODY1],-0.1,0,0); + break; + + case 'd': + case 'D': + dBodyAddTorque (body[BODY1],0, 0.1,0); + break; + case 'f': + case 'F': + dBodyAddTorque (body[BODY1],0,-0.1,0); + break; + + case 'c': + case 'C': + dBodyAddTorque (body[BODY1],0.1,0,0); + break; + case 'v': + case 'V': + dBodyAddTorque (body[BODY1],-0.1,0,0); + break; + + case 't': + case 'T': + if (joint->getType() == dJointTypePiston) + dJointAddPistonForce (joint->id(),1); + else + dJointAddSliderForce (joint->id(),1); + break; + case 'y': + case 'Y': + if (joint->getType() == dJointTypePiston) + dJointAddPistonForce (joint->id(),-1); + else + dJointAddSliderForce (joint->id(),-1); + break; + + + case '8' : + dJointAttach(joint->id(), body[0], 0); + break; + case '9' : + dJointAttach(joint->id(), 0, body[0]); + break; + + case 'i': + case 'I' : + joint->setParam (dParamLoStop, 0); + joint->setParam (dParamHiStop, 0); + break; + + case 'o': + case 'O' : + joint->setParam (dParamLoStop2, 0); + joint->setParam (dParamHiStop2, 0); + break; + + case 'k': + case 'K': + joint->setParam (dParamLoStop2, -45.0*3.14159267/180.0); + joint->setParam (dParamHiStop2, 45.0*3.14159267/180.0); + break; + case 'l': + case 'L': + joint->setParam (dParamLoStop2, -dInfinity); + joint->setParam (dParamHiStop2, dInfinity); + break; + + // Velocity of joint + case ',': + case '<' : + { + dReal vel = joint->getParam (dParamVel) - VEL_INC; + joint->setParam (dParamVel, vel); + std::cout<<"Velocity = "<' : + { + dReal vel = joint->getParam (dParamVel) + VEL_INC; + joint->setParam (dParamVel, vel); + std::cout<<"Velocity = "<getType() ) + { + case dJointTypeSlider : + { + dSliderJoint *sj = reinterpret_cast (joint); + std::cout<<"Position ="<getPosition() <<"\n"; + } + break; + case dJointTypePiston : + { + dPistonJoint *rj = reinterpret_cast (joint); + std::cout<<"Position ="<getPosition() <<"\n"; + } + break; + default: + {} // keep the compiler happy + } + } + break; + + case '+' : + (++tc) %= 4; + setPositionBodies (tc); + break; + case '-' : + (--tc) %= 4; + setPositionBodies (tc); + break; + + + } +} + +static void drawBox (dGeomID id, int R, int G, int B) +{ + if (!id) + return; + + const dReal *pos = dGeomGetPosition (id); + const dReal *rot = dGeomGetRotation (id); + dsSetColor (R,G,B); + + dVector3 l; + dGeomBoxGetLengths (id, l); + dsDrawBox (pos, rot, l); +} + + +// simulation loop +static void simLoop (int pause) +{ + const dReal *rot; + dVector3 ax; + dReal l=0; + + switch (joint->getType() ) + { + case dJointTypeSlider : + ( (dSliderJoint *) joint)->getAxis (ax); + l = ( (dSliderJoint *) joint)->getPosition(); + break; + case dJointTypePiston : + ( (dPistonJoint *) joint)->getAxis (ax); + l = ( (dPistonJoint *) joint)->getPosition(); + break; + default: + {} // keep the compiler happy + } + + + if (!pause) + { + double simstep = 0.01; // 1ms simulation steps + double dt = dsElapsedTime(); + + int nrofsteps = (int) ceilf (dt/simstep); + if (!nrofsteps) + nrofsteps = 1; + + for (int i=0; igetType() == dJointTypePiston ) + { + dVector3 anchor; + dJointGetPistonAnchor(joint->id(), anchor); + + // Draw the rotoide axis + rot = dGeomGetRotation (geom[BODY2]); + dsSetColor (1,0.5,0); + dsDrawCylinder (anchor, rot, 4, AXIS_RADIUS); + + + dsSetColor (0,1,1); + rot = dGeomGetRotation (geom[BODY1]); + dsDrawSphere (anchor, rot, 1.5*RADIUS); + } + + } +} + + +void Help (char **argv) +{ + printf ("%s ", argv[0]); + printf (" -h | --help : print this help\n"); + printf (" -s | --slider : Set the joint as a slider\n"); + printf (" -p | --piston : Set the joint as a Piston. (Default joint)\n"); + printf (" -1 | --offset1 : Create an offset between the 2 bodies\n"); + printf (" Offset one of the body by z=-0.5 and keep the anchor\n"); + printf (" point in the middle of the fixed body\n"); + printf (" -2 | --offset2 : Create an offset between the 2 bodies\n"); + printf (" Offset one of the body by z=-0.5 and set the anchor\n"); + printf (" point in the middle of the movable body\n"); + printf (" -3 | --offset3 : Create an offset between the 2 bodies\n"); + printf (" Offset one of the body by z=-0.5 and set the anchor\n"); + printf (" point in the middle of the 2 bodies\n"); + printf (" -t | --texture-path path : Path to the texture.\n"); + printf (" Default = %s\n", DRAWSTUFF_TEXTURE_PATH); + printf (" -n | --notFixed : In free space with no gravity mode"); + printf ("-notex : Don't use texture\n"); + printf ("-noshadow : No shadow\n"); + printf ("-noshadows : No shadows\n"); + printf ("-pause : Initial pause\n"); + printf ("--------------------------------------------------\n"); + printf ("Hit any key to continue:"); + getchar(); + + exit (0); +} + +int main (int argc, char **argv) +{ + dInitODE2(0); + bool fixed = true; + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dVector3 offset; + dSetZero (offset, 4); + + // Default test case + + if (argc >= 2 ) + { + for (int i=1; i < argc; ++i) + { + //static int tata = 0; + + if (1) + { + if ( 0 == strcmp ("-h", argv[i]) || 0 == strcmp ("--help", argv[i]) ) + Help (argv); + + if ( 0 == strcmp ("-s", argv[i]) || 0 == strcmp ("--slider", argv[i]) ) + type = dJointTypeSlider; + + if ( 0 == strcmp ("-t", argv[i]) || 0 == strcmp ("--texture-path", argv[i]) ) + { + int j = i+1; + if ( j >= argc || // Check if we have enough arguments + argv[j][0] == '\0' || // We should have a path here + argv[j][0] == '-' ) // We should have a path not a command line + Help (argv); + else + fn.path_to_textures = argv[++i]; // Increase i since we use this argument + } + } + + + if ( 0 == strcmp ("-1", argv[i]) || 0 == strcmp ("--offset1", argv[i]) ) + tc = 1; + + if ( 0 == strcmp ("-2", argv[i]) || 0 == strcmp ("--offset2", argv[i]) ) + tc = 2; + + if ( 0 == strcmp ("-3", argv[i]) || 0 == strcmp ("--offset3", argv[i]) ) + tc = 3; + + if (0 == strcmp ("-n", argv[i]) || 0 == strcmp ("--notFixed", argv[i]) ) + fixed = false; + } + } + + world = dWorldCreate(); + dWorldSetERP (world, 0.8); + + space = dSimpleSpaceCreate (0); + contactgroup = dJointGroupCreate (0); + geom[GROUND] = dCreatePlane (space, 0,0,1,0); + dGeomSetCategoryBits (geom[GROUND], catBits[GROUND]); + dGeomSetCollideBits (geom[GROUND], catBits[ALL]); + + dMass m; + dMatrix3 R; + + + // Create the Obstacle + geom[OBS] = dCreateBox (space, OBS_SIDES[0], OBS_SIDES[1], OBS_SIDES[2]); + dGeomSetCategoryBits (geom[OBS], catBits[OBS]); + dGeomSetCollideBits (geom[OBS], catBits[ALL]); + //Rotation of 45deg around y + dRFromAxisAndAngle (R, 1,1,0, -0.25*PI); + dGeomSetRotation (geom[OBS], R); + dGeomSetPosition (geom[OBS], 1.95, -0.2, 0.5); + + + //Rotation of 90deg around y + // Will orient the Z axis along X + dRFromAxisAndAngle (R, 0,1,0, -0.5*PI); + + + // Create Body2 (Wiil be attached to the world) + body[BODY2] = dBodyCreate (world); + // Main axis of cylinder is along X=1 + dMassSetBox (&m, 1, BODY2_SIDES[0], BODY2_SIDES[1], BODY2_SIDES[2]); + dMassAdjust (&m, Mass1); + geom[BODY2] = dCreateBox (space, BODY2_SIDES[0], BODY2_SIDES[1], BODY2_SIDES[2]); + dGeomSetBody (geom[BODY2], body[BODY2]); + dGeomSetOffsetRotation (geom[BODY2], R); + dGeomSetCategoryBits (geom[BODY2], catBits[BODY2]); + dGeomSetCollideBits (geom[BODY2], catBits[ALL] & (~catBits[BODY1]) ); + dBodySetMass (body[BODY2], &m); + + + // Create Body 1 (Slider on the prismatic axis) + body[BODY1] = dBodyCreate (world); + // Main axis of capsule is along X=1 + dMassSetCapsule (&m, 1, 1, RADIUS, BODY1_LENGTH); + dMassAdjust (&m, Mass1); + geom[BODY1] = dCreateCapsule (space, RADIUS, BODY1_LENGTH); + dGeomSetBody (geom[BODY1], body[BODY1]); + dGeomSetOffsetRotation (geom[BODY1], R); + dGeomSetCategoryBits (geom[BODY1], catBits[BODY1]); + dGeomSetCollideBits (geom[BODY1], catBits[ALL] & ~catBits[BODY2] & ~catBits[RECT]); + + dMass mRect; + dMassSetBox (&mRect, 1, RECT_SIDES[0], RECT_SIDES[1], RECT_SIDES[2]); + dMassAdd (&m, &mRect); + // TODO: translate m? + geom[RECT] = dCreateBox (space, RECT_SIDES[0], RECT_SIDES[1], RECT_SIDES[2]); + dGeomSetBody (geom[RECT], body[BODY1]); + dGeomSetOffsetPosition (geom[RECT], + (BODY1_LENGTH-RECT_SIDES[0]) /2.0, + 0.0, + -RADIUS -RECT_SIDES[2]/2.0); + dGeomSetCategoryBits (geom[RECT], catBits[RECT]); + dGeomSetCollideBits (geom[RECT], catBits[ALL] & (~catBits[BODY1]) ); + + dBodySetMass (body[BODY1], &m); + + + + setPositionBodies (tc); + + + if ( fixed ) + { + // Attache external cylinder to the world + dJointID fixed = dJointCreateFixed (world,0); + dJointAttach (fixed , NULL, body[BODY2]); + dJointSetFixed (fixed ); + dWorldSetGravity (world,0,0,-0.8); + } + else + { + dWorldSetGravity (world,0,0,0); + } + + + + + // The static is here only to help debugging + switch (type) + { + case dJointTypeSlider : + { + dSliderJoint *sj = new dSliderJoint (world, 0); + sj->attach (body[BODY1], body[BODY2]); + sj->setAxis (1, 0, 0); + joint = sj; + } + break; + + case dJointTypePiston : // fall through default + default: + { + dPistonJoint *pj = new dPistonJoint (world, 0); + pj->attach (body[BODY1], body[BODY2]); + pj->setAxis (1, 0, 0); + + dJointSetPistonAnchor(pj->id(), anchor[X], anchor[Y], anchor[Z]); + + joint = pj; + } + break; + }; + + + // run simulation + dsSimulationLoop (argc,argv,400,300,&fn); + + delete joint; + dJointGroupDestroy (contactgroup); + dSpaceDestroy (space); + dWorldDestroy (world); + dCloseODE(); + return 0; +} + + + + diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_plane2d.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_plane2d.cpp new file mode 100644 index 0000000..559f9ae --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_plane2d.cpp @@ -0,0 +1,304 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// Test my Plane2D constraint. +// Uses ode-0.35 collision API. + +# include +# include +# include +# include +# include +#include "texturepath.h" + + +# define drand48() ((double) (((double) rand()) / ((double) RAND_MAX))) + +# define N_BODIES 40 +# define STAGE_SIZE 8.0 // in m + +# define TIME_STEP 0.01 +# define K_SPRING 10.0 +# define K_DAMP 10.0 + +//using namespace ode; + +struct GlobalVars +{ + dWorld dyn_world; + dBody dyn_bodies[N_BODIES]; + dReal bodies_sides[N_BODIES][3]; + + dSpaceID coll_space_id; + dJointID plane2d_joint_ids[N_BODIES]; + dJointGroup coll_contacts; +}; + +static GlobalVars *g_globals_ptr = NULL; + + + +static void cb_start () +/*************************/ +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = { 0.5f*STAGE_SIZE, 0.5f*STAGE_SIZE, 0.65f*STAGE_SIZE}; + static float hpr[3] = { 90.0f, -90.0f, 0 }; + + dsSetViewpoint (xyz, hpr); +} + + + +static void cb_near_collision (void *, dGeomID o1, dGeomID o2) +/********************************************************************/ +{ + dBodyID b1 = dGeomGetBody (o1); + dBodyID b2 = dGeomGetBody (o2); + dContact contact; + + + // exit without doing anything if the two bodies are static + if (b1 == 0 && b2 == 0) + return; + + // exit without doing anything if the two bodies are connected by a joint + if (b1 && b2 && dAreConnected (b1, b2)) + { + /* MTRAP; */ + return; + } + + contact.surface.mode = 0; + contact.surface.mu = 0; // frictionless + + if (dCollide (o1, o2, 1, &contact.geom, sizeof (dContactGeom))) + { + dJointID c = dJointCreateContact (g_globals_ptr->dyn_world.id(), + g_globals_ptr->coll_contacts.id (), &contact); + dJointAttach (c, b1, b2); + } +} + + +static void track_to_pos (dBody &body, dJointID joint_id, + dReal target_x, dReal target_y) +/************************************************************************/ +{ + dReal curr_x = body.getPosition()[0]; + dReal curr_y = body.getPosition()[1]; + + dJointSetPlane2DXParam (joint_id, dParamVel, 1 * (target_x - curr_x)); + dJointSetPlane2DYParam (joint_id, dParamVel, 1 * (target_y - curr_y)); +} + + + +static void cb_sim_step (int pause) +/*************************************/ +{ + if (! pause) + { + static dReal angle = 0; + + angle += REAL( 0.01 ); + + track_to_pos (g_globals_ptr->dyn_bodies[0], g_globals_ptr->plane2d_joint_ids[0], + dReal( STAGE_SIZE/2 + STAGE_SIZE/2.0 * cos (angle) ), + dReal( STAGE_SIZE/2 + STAGE_SIZE/2.0 * sin (angle) )); + + /* double f0 = 0.001; */ + /* for (int b = 0; b < N_BODIES; b ++) */ + /* { */ + /* double p = 1 + b / (double) N_BODIES; */ + /* double q = 2 - b / (double) N_BODIES; */ + /* g_globals_ptr->dyn_bodies[b].addForce (f0 * cos (p*angle), f0 * sin (q*angle), 0); */ + /* } */ + /* g_globals_ptr->dyn_bodies[0].addTorque (0, 0, 0.1); */ + + const int n = 10; + for (int i = 0; i < n; i ++) + { + dSpaceCollide (g_globals_ptr->coll_space_id, 0, &cb_near_collision); + g_globals_ptr->dyn_world.step (dReal(TIME_STEP/n)); + g_globals_ptr->coll_contacts.empty (); + } + } + +# if 1 /* [ */ + { + // @@@ hack Plane2D constraint error reduction here: + for (int b = 0; b < N_BODIES; b ++) + { + const dReal *rot = dBodyGetAngularVel (g_globals_ptr->dyn_bodies[b].id()); + const dReal *quat_ptr; + dReal quat[4], + quat_len; + + + quat_ptr = dBodyGetQuaternion (g_globals_ptr->dyn_bodies[b].id()); + quat[0] = quat_ptr[0]; + quat[1] = 0; + quat[2] = 0; + quat[3] = quat_ptr[3]; + quat_len = sqrt (quat[0] * quat[0] + quat[3] * quat[3]); + quat[0] /= quat_len; + quat[3] /= quat_len; + dBodySetQuaternion (g_globals_ptr->dyn_bodies[b].id(), quat); + dBodySetAngularVel (g_globals_ptr->dyn_bodies[b].id(), 0, 0, rot[2]); + } + } +# endif /* ] */ + + +# if 0 /* [ */ + { + // @@@ friction + for (int b = 0; b < N_BODIES; b ++) + { + const dReal *vel = dBodyGetLinearVel (g_globals_ptr->dyn_bodies[b].id()), + *rot = dBodyGetAngularVel (g_globals_ptr->dyn_bodies[b].id()); + dReal s = 1.00; + dReal t = 0.99; + + dBodySetLinearVel (g_globals_ptr->dyn_bodies[b].id(), s*vel[0],s*vel[1],s*vel[2]); + dBodySetAngularVel (g_globals_ptr->dyn_bodies[b].id(),t*rot[0],t*rot[1],t*rot[2]); + } + } +# endif /* ] */ + + + { + // ode drawstuff + + dsSetTexture (DS_WOOD); + for (int b = 0; b < N_BODIES; b ++) + { + if (b == 0) + dsSetColor (1.0, 0.5, 1.0); + else + dsSetColor (0, 0.5, 1.0); +#ifdef dDOUBLE + dsDrawBoxD (g_globals_ptr->dyn_bodies[b].getPosition(), g_globals_ptr->dyn_bodies[b].getRotation(), g_globals_ptr->bodies_sides[b]); +#else + dsDrawBox (g_globals_ptr->dyn_bodies[b].getPosition(), g_globals_ptr->dyn_bodies[b].getRotation(), g_globals_ptr->bodies_sides[b]); +#endif + } + } +} + + + +extern int main +/******************/ +( + int argc, + char **argv +) +{ + int b; + dsFunctions drawstuff_functions; + + + dInitODE2(0); + + g_globals_ptr = new GlobalVars(); + + // dynamic world + + dReal cf_mixing;// = 1 / TIME_STEP * K_SPRING + K_DAMP; + dReal err_reduct;// = TIME_STEP * K_SPRING * cf_mixing; + err_reduct = REAL( 0.5 ); + cf_mixing = REAL( 0.001 ); + dWorldSetERP (g_globals_ptr->dyn_world.id (), err_reduct); + dWorldSetCFM (g_globals_ptr->dyn_world.id (), cf_mixing); + g_globals_ptr->dyn_world.setGravity (0, 0.0, -1.0); + + g_globals_ptr->coll_space_id = dSimpleSpaceCreate (0); + + // dynamic bodies + for (b = 0; b < N_BODIES; b ++) + { + int l = (int) (1 + sqrt ((double) N_BODIES)); + dReal x = dReal( (0.5 + (b / l)) / l * STAGE_SIZE ); + dReal y = dReal( (0.5 + (b % l)) / l * STAGE_SIZE ); + dReal z = REAL( 1.0 ) + REAL( 0.1 ) * (dReal)drand48(); + + g_globals_ptr->bodies_sides[b][0] = dReal( 5 * (0.2 + 0.7*drand48()) / sqrt((double)N_BODIES) ); + g_globals_ptr->bodies_sides[b][1] = dReal( 5 * (0.2 + 0.7*drand48()) / sqrt((double)N_BODIES) ); + g_globals_ptr->bodies_sides[b][2] = z; + + g_globals_ptr->dyn_bodies[b].create (g_globals_ptr->dyn_world); + g_globals_ptr->dyn_bodies[b].setPosition (x, y, z/2); + g_globals_ptr->dyn_bodies[b].setData ((void*) (dsizeint)b); + dBodySetLinearVel (g_globals_ptr->dyn_bodies[b].id (), + dReal( 3 * (drand48 () - 0.5) ), + dReal( 3 * (drand48 () - 0.5) ), 0); + + dMass m; + m.setBox (1, g_globals_ptr->bodies_sides[b][0],g_globals_ptr->bodies_sides[b][1],g_globals_ptr->bodies_sides[b][2]); + m.adjust (REAL(0.1) * g_globals_ptr->bodies_sides[b][0] * g_globals_ptr->bodies_sides[b][1]); + g_globals_ptr->dyn_bodies[b].setMass (&m); + + g_globals_ptr->plane2d_joint_ids[b] = dJointCreatePlane2D (g_globals_ptr->dyn_world.id (), 0); + dJointAttach (g_globals_ptr->plane2d_joint_ids[b], g_globals_ptr->dyn_bodies[b].id (), 0); + } + + dJointSetPlane2DXParam (g_globals_ptr->plane2d_joint_ids[0], dParamFMax, 10); + dJointSetPlane2DYParam (g_globals_ptr->plane2d_joint_ids[0], dParamFMax, 10); + + + // collision geoms and joints + dCreatePlane (g_globals_ptr->coll_space_id, 1, 0, 0, 0); + dCreatePlane (g_globals_ptr->coll_space_id, -1, 0, 0, -STAGE_SIZE); + dCreatePlane (g_globals_ptr->coll_space_id, 0, 1, 0, 0); + dCreatePlane (g_globals_ptr->coll_space_id, 0, -1, 0, -STAGE_SIZE); + + for (b = 0; b < N_BODIES; b ++) + { + dGeomID coll_box_id; + coll_box_id = dCreateBox (g_globals_ptr->coll_space_id, + g_globals_ptr->bodies_sides[b][0], g_globals_ptr->bodies_sides[b][1], g_globals_ptr->bodies_sides[b][2]); + dGeomSetBody (coll_box_id, g_globals_ptr->dyn_bodies[b].id ()); + } + + g_globals_ptr->coll_contacts.create (); + + { + // simulation loop (by drawstuff lib) + drawstuff_functions.version = DS_VERSION; + drawstuff_functions.start = &cb_start; + drawstuff_functions.step = &cb_sim_step; + drawstuff_functions.command = 0; + drawstuff_functions.stop = 0; + drawstuff_functions.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dsSimulationLoop (argc, argv, 352,288,&drawstuff_functions); + } + + delete g_globals_ptr; + g_globals_ptr = NULL; + + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_rfriction.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_rfriction.cpp new file mode 100644 index 0000000..61d1115 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_rfriction.cpp @@ -0,0 +1,258 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* +Angular friction demo: + +A bunch of ramps of different pitch. +A bunch of spheres with rolling friction. +*/ + + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants +#define GRAVITY 10 // the global gravity to use +#define RAMP_COUNT 8 + +static const dReal rampX = 6.0f; +static const dReal rampY = 0.5f; +static const dReal rampZ = 0.25f; +static const dReal sphereRadius = 0.25f; +static const dReal maxRamp = M_PI/4.0f; // Needs to be less than pi/2 +static const dReal rampInc = maxRamp/RAMP_COUNT; + +// dynamics and collision objects +static dWorldID world = 0; +static dSpaceID space = 0; +static dJointGroupID contactgroup = 0; +static dGeomID ground; + +static dReal mu = REAL(0.37); // the global mu to use +static dReal rho = REAL(0.1); // the global rho to use +static dReal omega = REAL(25.0); + +static dGeomID rampGeom[RAMP_COUNT]; +static dBodyID sphereBody[RAMP_COUNT]; +static dGeomID sphereGeom[RAMP_COUNT]; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + + if (b1==0 && b2==0) return; + + dContact contact[3]; + for (int ii=0; ii<3; ii++) { + contact[ii].surface.mode = dContactApprox1 | dContactRolling; + contact[ii].surface.mu = mu; + contact[ii].surface.rho = rho; + } + if (int numc = dCollide (o1,o2,3,&contact[0].geom,sizeof(dContact))) { + for (i=0; i1) rho=1; + break; + case 'n': case 'N': + mu-=0.02; + if (mu<0) mu=0; + break; + case 'm': case 'M': + mu+=0.02; + if (mu>1) mu=1; + break; + case 'r': case 'R': + reset(); + break; + case ']': + omega+=1; + break; + case '[': + omega-=1; + break; + } +} + +// simulation loop + +static void simLoop (int pause) +{ + if (!pause) { + dSpaceCollide (space,0,&nearCallback); + dWorldStep (world,0.017); // 60 fps + // remove all contact joints + dJointGroupEmpty (contactgroup); + } + + // Render ramps and spheres + dsSetTexture (DS_WOOD); + for (int ii=0;ii +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#endif + + +// some constants +#define SIDE (0.5f) // side length of a box +#define MASS (1.0) // mass of a box + + +// dynamics and collision objects +static dWorldID world; +static dBodyID body[2]; +static dJointID slider; + + +// state set by keyboard commands +static int occasional_error = 0; + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {1.0382f,-1.0811f,1.4700f}; + static float hpr[3] = {135.0000f,-19.5000f,0.0000f}; + dsSetViewpoint (xyz,hpr); + printf ("Press 'e' to start/stop occasional error.\n"); +} + + +// called when a key pressed + +static void command (int cmd) +{ + if (cmd == 'e' || cmd == 'E') { + occasional_error ^= 1; + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + const dReal kd = -0.3; // angular damping constant + const dReal ks = 0.5; // spring constant + if (!pause) { + // add an oscillating torque to body 0, and also damp its rotational motion + static dReal a=0; + const dReal *w = dBodyGetAngularVel (body[0]); + dBodyAddTorque (body[0],kd*w[0],kd*w[1]+0.1*cos(a),kd*w[2]+0.1*sin(a)); + a += 0.01; + + // add a spring force to keep the bodies together, otherwise they will + // fly apart along the slider axis. + const dReal *p1 = dBodyGetPosition (body[0]); + const dReal *p2 = dBodyGetPosition (body[1]); + dBodyAddForce (body[0],ks*(p2[0]-p1[0]),ks*(p2[1]-p1[1]), + ks*(p2[2]-p1[2])); + dBodyAddForce (body[1],ks*(p1[0]-p2[0]),ks*(p1[1]-p2[1]), + ks*(p1[2]-p2[2])); + + // occasionally re-orient one of the bodies to create a deliberate error. + if (occasional_error) { + static int count = 0; + if ((count % 20)==0) { + // randomly adjust orientation of body[0] + const dReal *R1; + dMatrix3 R2,R3; + R1 = dBodyGetRotation (body[0]); + dRFromAxisAndAngle (R2,dRandReal()-0.5,dRandReal()-0.5, + dRandReal()-0.5,dRandReal()-0.5); + dMultiply0 (R3,R1,R2,3,3,3); + dBodySetRotation (body[0],R3); + + // randomly adjust position of body[0] + const dReal *pos = dBodyGetPosition (body[0]); + dBodySetPosition (body[0], + pos[0]+0.2*(dRandReal()-0.5), + pos[1]+0.2*(dRandReal()-0.5), + pos[2]+0.2*(dRandReal()-0.5)); + } + count++; + } + + dWorldStep (world,0.05); + } + + dReal sides1[3] = {SIDE,SIDE,SIDE}; + dReal sides2[3] = {SIDE*0.8f,SIDE*0.8f,SIDE*2.0f}; + dsSetTexture (DS_WOOD); + dsSetColor (1,1,0); + dsDrawBox (dBodyGetPosition(body[0]),dBodyGetRotation(body[0]),sides1); + dsSetColor (0,1,1); + dsDrawBox (dBodyGetPosition(body[1]),dBodyGetRotation(body[1]),sides2); +} + + +int main (int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // create world + dInitODE2(0); + world = dWorldCreate(); + dMass m; + dMassSetBox (&m,1,SIDE,SIDE,SIDE); + dMassAdjust (&m,MASS); + + body[0] = dBodyCreate (world); + dBodySetMass (body[0],&m); + dBodySetPosition (body[0],0,0,1); + body[1] = dBodyCreate (world); + dBodySetMass (body[1],&m); + dQuaternion q; + dQFromAxisAndAngle (q,-1,1,0,0.25*M_PI); + dBodySetPosition (body[1],0.2,0.2,1.2); + dBodySetQuaternion (body[1],q); + + slider = dJointCreateSlider (world,0); + dJointAttach (slider,body[0],body[1]); + dJointSetSliderAxis (slider,1,1,1); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dWorldDestroy (world); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_space.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_space.cpp new file mode 100644 index 0000000..6f871f6 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_space.cpp @@ -0,0 +1,232 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +/* + +testing procedure: + * create a bunch of random boxes + * test for intersections directly, put results in n^2 array + * get space to report collisions: + - all correct collisions reported + - no pair reported more than once + - no incorrect collisions reported + +*/ + + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define NUM 20 // number of boxes to test + + +// collision objects and globals + +static dSpaceID space; +static dGeomID geom[NUM]; +static dReal bounds[NUM][6]; +static dsizeint good_matrix[NUM][NUM]; // correct collision matrix +static dsizeint test_matrix[NUM][NUM]; // testing collision matrix +static dsizeint hits[NUM]; // number of collisions a box has +static unsigned long seed=37; + + +static void init_test() +{ + int i,j; + const dReal scale = 0.5; + + // set random boxes + dRandSetSeed (seed); + for (i=0; i < NUM; i++) { + bounds[i][0] = dRandReal()*2-1; + bounds[i][1] = bounds[i][0] + dRandReal()*scale; + bounds[i][2] = dRandReal()*2-1; + bounds[i][3] = bounds[i][2] + dRandReal()*scale; + bounds[i][4] = dRandReal()*2; + bounds[i][5] = bounds[i][4] + dRandReal()*scale; + + if (geom[i]) dGeomDestroy (geom[i]); + geom[i] = dCreateBox (space, + bounds[i][1] - bounds[i][0], + bounds[i][3] - bounds[i][2], + bounds[i][5] - bounds[i][4]); + dGeomSetPosition (geom[i], + (bounds[i][0] + bounds[i][1])*0.5, + (bounds[i][2] + bounds[i][3])*0.5, + (bounds[i][4] + bounds[i][5])*0.5); + dGeomSetData (geom[i],(void*)(dsizeint)(i)); + } + + // compute all intersections and put the results in "good_matrix" + for (i=0; i < NUM; i++) { + for (j=0; j < NUM; j++) good_matrix[i][j] = 0; + } + for (i=0; i < NUM; i++) hits[i] = 0; + + for (i=0; i < NUM; i++) { + for (j=i+1; j < NUM; j++) { + dReal *bounds1 = &bounds[i][0]; + dReal *bounds2 = &bounds[j][0]; + if (bounds1[0] > bounds2[1] || + bounds1[1] < bounds2[0] || + bounds1[2] > bounds2[3] || + bounds1[3] < bounds2[2] || + bounds1[4] > bounds2[5] || + bounds1[5] < bounds2[4]) continue; + good_matrix[i][j] = 1; + good_matrix[j][i] = 1; + hits[i]++; + hits[j]++; + } + } +} + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + dsizeint i,j; + i = (dsizeint) dGeomGetData (o1); + j = (dsizeint) dGeomGetData (o2); + if (i==j) + printf ("collision (%d,%d) is between the same object\n",(int)i,(int)j); + if (!good_matrix[i][j] || !good_matrix[j][i]) + printf ("collision (%d,%d) is incorrect\n",(int)i,(int)j); + if (test_matrix[i][j] || test_matrix[j][i]) + printf ("collision (%d,%d) reported more than once\n",(int)i,(int)j); + test_matrix[i][j] = 1; + test_matrix[j][i] = 1; +} + + +// start simulation - set viewpoint + +static void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + static float xyz[3] = {2.1640f,-1.3079f,1.7600f}; + static float hpr[3] = {125.5000f,-17.0000f,0.0000f}; + dsSetViewpoint (xyz,hpr); +} + + +static void command (int cmd) +{ + if (cmd == ' ') { + seed++; + init_test(); + } +} + + +// simulation loop + +static void simLoop (int) +{ + int i,j; + + for (i=0; i < NUM; i++) { + for (j=0; j < NUM; j++) test_matrix[i][j] = 0; + } + dSpaceCollide (space,0,&nearCallback); + for (i=0; i < NUM; i++) { + for (j=i+1; j < NUM; j++) { + if (good_matrix[i][j] && !test_matrix[i][j]) { + printf ("failed to report collision (%d,%d) (seed=%ld)\n",i,j,seed); + } + } + } + + seed++; + init_test(); + + for (i=0; i 0) dsSetColor (1,0,0); + else dsSetColor (1,1,0); + dsDrawBox (pos,R,side); + } +} + + +int main (int argc, char **argv) +{ + int i; + + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = 0; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + dInitODE2(0); + + // test the simple space: + // space = dSimpleSpaceCreate(); + + // test the hash space: + // space = dHashSpaceCreate (0); + // dHashSpaceSetLevels (space,-10,10); + + // test the quadtree space + dVector3 Center = {0, 0, 0, 0}; + dVector3 Extents = {10, 0, 10, 0}; + space = dQuadTreeSpaceCreate(0, Center, Extents, 7); + + for (i=0; i < NUM; i++) geom[i] = 0; + init_test(); + + // run simulation + dsSimulationLoop (argc,argv,352,288,&fn); + + dSpaceDestroy (space); + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_space_stress.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_space_stress.cpp new file mode 100644 index 0000000..dcbd9d7 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_space_stress.cpp @@ -0,0 +1,449 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#include + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define NUM 10000 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 4 // maximum number of contact points per body +#define WORLD_SIZE 20 +#define WORLD_HEIGHT 20 + + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space = NULL; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? +static int draw_geom = 1; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command (int cmd) +{ + int i,j,k; + dReal sides[3]; + dMass m; + bool setBody = false; + + cmd = locase(cmd); + if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'x' || cmd == 'y') { + if (num < NUM) { + // new object to be created + i = num; + num++; + } else { + // recycle existing object + i = nextobj++; + nextobj %= num; // wrap-around if needed + + // destroy the body and geoms for slot i + dBodyDestroy (obj[i].body); + obj[i].body = 0; + + for (k=0; k < GPB; k++) + if (obj[i].geom[k]) { + dGeomDestroy(obj[i].geom[k]); + obj[i].geom[k] = 0; + } + } + + obj[i].body = dBodyCreate(world); + + for (k=0; k<3; k++) + sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) { + dBodySetPosition(obj[i].body, + dRandReal()*2-1,dRandReal()*2-1,dRandReal()+2); + dRFromAxisAndAngle(R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } else { + // higher than highest body position + dReal maxheight = 0; + for (k=0; k maxheight) + maxheight = pos[2]; + } + dBodySetPosition(obj[i].body, 0,0,maxheight+1); + dRSetIdentity(R); + //dRFromAxisAndAngle (R,0,0,1,/*dRandReal()*10.0-5.0*/0); + } + + dBodySetRotation(obj[i].body,R); + + if (cmd == 'b') { + + dMassSetBox(&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox(space,sides[0],sides[1],sides[2]); + + } else if (cmd == 'c') { + + sides[0] *= 0.5; + dMassSetCapsule(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule (space,sides[0],sides[1]); + + } else if (cmd == 'y') { + + dMassSetCylinder(&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder(space,sides[0],sides[1]); + + } else if (cmd == 's') { + + sides[0] *= 0.5; + dMassSetSphere (&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere (space,sides[0]); + + } else if (cmd == 'x') { + + setBody = true; + // start accumulating masses for the composite geometries + dMass m2; + dMassSetZero (&m); + + dReal dpos[GPB][3]; // delta-positions for composite geometries + dMatrix3 drot[GPB]; + + // set random delta positions + for (j=0; j= num) selected = 0; + if (selected < 0) selected = 0; + } + else if (cmd == 'd' && selected >= 0 && selected < num) { + dBodyDisable (obj[selected].body); + } + else if (cmd == 'e' && selected >= 0 && selected < num) { + dBodyEnable (obj[selected].body); + } + else if (cmd == 'a') { + show_aabb ^= 1; + } + else if (cmd == 't') { + show_contacts ^= 1; + } + else if (cmd == 'r') { + random_pos ^= 1; + } + else if (cmd == 'o') { + draw_geom ^= 1; + } +} + + +// draw a geom + +void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + if (!draw_geom){ + return; + } + + if (!g) return; + if (!pos) pos = dGeomGetPosition(g); + if (!R) R = dGeomGetRotation(g); + + int type = dGeomGetClass (g); + if (type == dBoxClass) { + dVector3 sides; + dGeomBoxGetLengths(g,sides); + dsDrawBox(pos,R,sides); + } + else if (type == dSphereClass) { + dsDrawSphere(pos,R,dGeomSphereGetRadius (g)); + } + else if (type == dCapsuleClass) { + dReal radius,length; + dGeomCapsuleGetParams(g,&radius,&length); + dsDrawCapsule (pos,R,length,radius); + } else if (type == dCylinderClass) { + dReal radius,length; + dGeomCylinderGetParams(g,&radius,&length); + dsDrawCylinder(pos,R,length,radius); + } + + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB(g,aabb); + dVector3 bbpos; + for (int i=0; i<3; i++) + bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (int j=0; j<3; j++) + bbsides[j] = aabb[j*2+1] - aabb[j*2]; + dMatrix3 RI; + dRSetIdentity(RI); + dsSetColorAlpha(1,0,0,0.5); + dsDrawBox(bbpos,RI,bbsides); + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + dsSetColor (0,0,2); + dSpaceCollide (space,0,&nearCallback); + //if (!pause) dWorldStep (world,0.05); + if (!pause) dWorldQuickStep (world,0.05); + //if (!pause) dWorldStepFast (world,0.05, 1); + + // remove all contact joints + dJointGroupEmpty (contactgroup); + + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (int i=0; i +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#endif + + +// some constants + +#define NUM 10 // number of bodies +#define NUMJ 9 // number of joints +#define SIDE (0.2) // side length of a box +#define MASS (1.0) // mass of a box +#define RADIUS (0.1732f) // sphere radius + + + +// dynamics and collision objects + +static dWorldID world=0; +static dBodyID body[NUM]; +static dJointID joint[NUMJ]; + + +// create the test system + +void createTest() +{ + int i,j; + if (world) dWorldDestroy (world); + + world = dWorldCreate(); + + // create random bodies + for (i=0; i +#include +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawSphere dsDrawSphereD +#define dsDrawBox dsDrawBoxD +#define dsDrawTriangle dsDrawTriangleD +#define dsDrawLine dsDrawLineD +#endif + + + +const dReal ball_radius = 0.4; +const dReal balls_sep = 2; // separation between the balls + +/* Choose one test case + */ +#define TEST_CASE 0 + +#if TEST_CASE == 0 +const dReal track_len = 10; +const dReal track_height = 1; +const dReal track_width = 0.1; +const dReal track_gauge = 1; +const dReal track_elevation = 2; +const dReal track_angle = 80 * M_PI/180.; +const dReal track_incl = 10 * M_PI/180.; +#elif TEST_CASE == 1 +const dReal track_len = 10; +const dReal track_height = 1; +const dReal track_width = 0.1; +const dReal track_gauge = 1.9*ball_radius; +const dReal track_elevation = 2; +const dReal track_angle = 0 * M_PI/180.; +const dReal track_incl = 10 * M_PI/180.; +#elif TEST_CASE == 2 +const dReal track_len = 10; +const dReal track_height = 1; +const dReal track_width = 0.1; +const dReal track_gauge = 1.9*ball_radius; +const dReal track_elevation = 2; +const dReal track_angle = 15 * M_PI/180.; +const dReal track_incl = 10 * M_PI/180.; +#elif TEST_CASE == 3 +const dReal track_len = 10; +const dReal track_height = .7; +const dReal track_width = 0.1; +const dReal track_gauge = track_height*1.1; +const dReal track_elevation = 2; +const dReal track_angle = 90 * M_PI/180.; +const dReal track_incl = 10 * M_PI/180.; +#else +#error "TEST_CAST to a valid value!" +#endif + + + +dWorldID world; +dSpaceID space; +dJointGroupID contact_group; +dGeomID ground; +dGeomID ball1_geom, ball2_geom; +dTriMeshDataID mesh_data; +dGeomID mesh_geom; + +dBodyID ball1_body, ball2_body; + +const unsigned n_box_verts = 8; +dVector3 box_verts[n_box_verts] = { + {-track_len/2, -track_width/2, track_height/2}, // 0 + { track_len/2, -track_width/2, track_height/2}, // 1 + { track_len/2, track_width/2, track_height/2}, // 2 + {-track_len/2, track_width/2, track_height/2}, // 3 + { track_len/2, -track_width/2, -track_height/2}, // 4 + {-track_len/2, -track_width/2, -track_height/2}, // 5 + {-track_len/2, track_width/2, -track_height/2}, // 6 + { track_len/2, track_width/2, -track_height/2} // 7 +}; + +const unsigned n_box_faces = 12; +dTriIndex box_faces[n_box_faces * 3] = { + 0, 1, 2, + 0, 2, 3, + 1, 4, 7, + 1, 7, 2, + 4, 5, 6, + 4, 6, 7, + 5, 0, 3, + 5, 3, 6, + 3, 2, 7, + 3, 7, 6, + 0, 5, 4, + 0, 4, 1 +}; + + +const unsigned n_track_verts = n_box_verts * 2; +const unsigned n_track_faces = n_box_faces * 2; + +dVector3 track_verts[n_track_verts]; +dTriIndex track_faces[n_track_faces * 3]; + + + +void resetBall(dBodyID b, unsigned idx) +{ + dBodySetPosition(b, + 0.5*track_len*cos(track_incl) // Z + - 0.5*track_height*sin(track_incl) + - ball_radius, // X + balls_sep*idx, // Y + track_elevation + ball_radius// Z + + 0.5*track_len*sin(track_incl) + + 0.5*track_height*cos(track_incl)); + dMatrix3 r = {1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0}; + dBodySetRotation(b, r); + dBodySetLinearVel(b, 0, 0, 0); + dBodySetAngularVel(b, 0, 0, 0); + +} + + +void resetSim() +{ + resetBall(ball1_body, 0); + resetBall(ball2_body, 1); +} + + +void start() +{ + dAllocateODEDataForThread(dAllocateMaskAll); + + world = dWorldCreate(); + dWorldSetGravity (world,0,0,-9.8); + + contact_group = dJointGroupCreate(0); + + space = dSimpleSpaceCreate (0); + + + // first, the ground plane + // it has to coincide with the plane we have in drawstuff + ground = dCreatePlane(space, 0, 0, 1, 0); + + + // now a ball + dMass m; + dMassSetSphere(&m, 0.1, ball_radius); + + ball1_geom = dCreateSphere(space, ball_radius); + ball1_body = dBodyCreate(world); + dGeomSetBody(ball1_geom, ball1_body); + dBodySetMass(ball1_body, &m); + + ball2_geom = dCreateSphere(space, ball_radius); + ball2_body = dBodyCreate(world); + dGeomSetBody(ball2_geom, ball2_body); + dBodySetMass(ball2_body, &m); + + + + + // tracks made out of boxes + dGeomID trk; + dMatrix3 r1, r2, r3; + dVector3 ro = {0, -(0.5*track_gauge + 0.5*track_width), track_elevation}; + dMatrix3 s1, s2, s3; + dVector3 so = {0, 0.5*track_gauge + 0.5*track_width, track_elevation}; + + dRFromAxisAndAngle(r1, 1, 0, 0, track_angle); + dRFromAxisAndAngle(r2, 0, 1, 0, -track_incl); + dMultiply0_333(r3, r2, r1); + + dRFromAxisAndAngle(s1, 1, 0, 0, -track_angle); + dRFromAxisAndAngle(s2, 0, 1, 0, -track_incl); + dMultiply0_333(s3, s2, s1); + + trk = dCreateBox(space, track_len, track_width, track_height); + dGeomSetPosition(trk, ro[0], ro[1] + balls_sep, ro[2]); + dGeomSetRotation(trk, r3); + + trk = dCreateBox(space, track_len, track_width, track_height); + dGeomSetPosition(trk, so[0], so[1] + balls_sep, so[2]); + dGeomSetRotation(trk, s3); + + + + + + // tracks made out of trimesh + for (unsigned i=0; i 0.99 // about 8 degrees of difference + && + dCalcPointsDistance3(contacts[i].geom.pos, contacts[j].geom.pos) < epsilon) { + // they are too close + closest_point = j; + //clog << "found close points: " << j << " and " << i << endl; + break; + } + } + + if (closest_point != i) { + // we discard one of the points + if (contacts[i].geom.depth > contacts[closest_point].geom.depth) + // the new point is deeper, copy it over closest_point + contacts[closest_point] = contacts[i]; + } else + contacts[new_n++] = contacts[i]; // the point is preserved + } + //clog << "reduced from " << n << " to " << new_n << endl; + n = new_n; + + for (int i=0; i +#include +#include "texturepath.h" + +#ifdef dDOUBLE +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawLine dsDrawLineD +#define dsDrawSphere dsDrawSphereD +#endif + +dReal theta = M_PI / 4; +dReal ratio = 1, speed = 5, rho_1 = 1, rho_2 = 1, backlash = 0.1; +int mode = 0; + +dWorldID world; +dSpaceID space; +dBodyID body1, body2; +dGeomID geom1, geom2; +dJointID hinge1, hinge2, transmission; +dJointFeedback feedback; + +void setup() { + dMatrix3 R; + + switch (mode) { + case 0: + // Parallel axes. + + dBodySetPosition(body1, 1, 0, 1); + dBodySetPosition(body2, -1, 0, 1); + + dRSetIdentity (R); + dBodySetRotation (body1, R); + dBodySetRotation (body2, R); + + dJointSetHingeAnchor(hinge2, -1, 0, 1); + dJointSetHingeAxis(hinge2, 0, 0, 1); + + dJointSetHingeAnchor(hinge1, 1, 0, 1); + dJointSetHingeAxis(hinge1, 0, 0, 1); + + dJointSetTransmissionMode(transmission, dTransmissionParallelAxes); + dJointSetTransmissionRatio(transmission, ratio); + dJointSetTransmissionAnchor1(transmission, 1, 0, 1); + dJointSetTransmissionAnchor2(transmission, -1, 0, 1); + dJointSetTransmissionAxis(transmission, 0, 0, 1); + + break; + case 1: + // Intersecting axes. + + dBodySetPosition(body1, 1, 0, 1); + dBodySetPosition(body2, -1, 0, 2); + + dRSetIdentity (R); + dBodySetRotation (body1, R); + + dRFromZAxis (R, cos(theta), 0, sin(theta)); + dBodySetRotation (body2, R); + + dJointSetHingeAnchor(hinge2, -1, 0, 2); + dJointSetHingeAxis(hinge2, cos(theta), 0, sin(theta)); + + dJointSetHingeAnchor(hinge1, 1, 0, 1); + dJointSetHingeAxis(hinge1, 0, 0, 1); + + dJointSetTransmissionMode(transmission, dTransmissionIntersectingAxes); + dJointSetTransmissionAnchor1(transmission, 1, 0, 1); + dJointSetTransmissionAnchor2(transmission, -1, 0, 2); + dJointSetTransmissionAxis1(transmission, 0, 0, -1); + dJointSetTransmissionAxis2(transmission, cos(theta), 0, sin(theta)); + + break; + case 2: + // Chain. + + dBodySetPosition(body1, 2, 0, 1); + dBodySetPosition(body2, -2, 0, 1); + + dRSetIdentity (R); + dBodySetRotation (body1, R); + dBodySetRotation (body2, R); + + dJointSetHingeAnchor(hinge2, -2, 0, 1); + dJointSetHingeAxis(hinge2, 0, 0, 1); + + dJointSetHingeAnchor(hinge1, 2, 0, 1); + dJointSetHingeAxis(hinge1, 0, 0, 1); + + dJointSetTransmissionMode(transmission, dTransmissionChainDrive); + dJointSetTransmissionAnchor1(transmission, 2, 0, 1); + dJointSetTransmissionAnchor2(transmission, -2, 0, 1); + dJointSetTransmissionRadius1(transmission, rho_1); + dJointSetTransmissionRadius2(transmission, rho_2); + dJointSetTransmissionAxis(transmission, 0, 0, 1); + + break; + } + + dJointSetTransmissionBacklash(transmission, backlash); + + dJointSetHingeParam(hinge2, dParamVel, speed); + dJointSetHingeParam(hinge2, dParamFMax, 50); + + dJointSetHingeParam(hinge1, dParamVel, 0); + dJointSetHingeParam(hinge1, dParamFMax, 2); + + dBodySetLinearVel(body1, 0, 0, 0); + dBodySetLinearVel(body2, 0, 0, 0); + dBodySetAngularVel(body1, 0, 0, 0); + dBodySetAngularVel(body2, 0, 0, 0); +} + +void start() +{ + dMass mass; + + world = dWorldCreate(); + dWorldSetGravity (world,0,0,-9.8); + + dWorldSetERP(world, 0.2); + + space = dSimpleSpaceCreate (0); + + body1 = dBodyCreate(world); + body2 = dBodyCreate(world); + + dBodySetFiniteRotationMode(body1, 1); + dBodySetFiniteRotationMode(body2, 1); + + geom1 = dCreateCylinder(space, 0.2, 0.5); + dGeomSetBody(geom1, body1); + dMassSetCylinder(&mass, 100, 3, 0.2, 0.5); + dBodySetMass(body1, &mass); + + geom2 = dCreateCylinder(space, 0.2, 0.5); + dGeomSetBody(geom2, body2); + dMassSetCylinder(&mass, 100, 3, 0.2, 0.5); + dBodySetMass(body2, &mass); + + hinge1 = dJointCreateHinge(world, 0); + dJointAttach(hinge1, body1, 0); + + hinge2 = dJointCreateHinge(world, 0); + dJointAttach(hinge2, body2, 0); + + transmission = dJointCreateTransmission(world, 0); + dJointAttach(transmission, body1, body2); + dJointSetFeedback(transmission, &feedback); + + setup(); + + // initial camera position + static float xyz[3] = {1.15,-2.78,4.1}; + static float hpr[3] = {105,-45.5,0}; + dsSetViewpoint (xyz,hpr); + + fprintf (stderr, + "The green wheel is driving the red one. To control it use the following:\n" + " '[' : decrease wheel ratio\n" + " ']' : increase wheel ratio\n" + " ',' : decrease driving wheel speed\n" + " '.' : increase driving wheel speed\n" + " '-' : decrease backlash\n" + " '=' : increase backlash\n" + " '1' : switch to parallel axes gears mode\n" + " '2' : switch to intersecting axes gears mode\n" + " '3' : switch to chain (or belt) mode\n" +); +} + +void stop() +{ + dSpaceDestroy(space); + + dWorldDestroy(world); +} + +void drawGeom(dGeomID g) +{ + int gclass = dGeomGetClass(g); + const dReal *pos = dGeomGetPosition(g); + const dReal *rot = dGeomGetRotation(g); + + switch (gclass) { + case dCylinderClass: + { + dReal length, radius; + + if (g == geom1) { + dsSetColorAlpha(1, 0, 0, 1); + } else { + dsSetColorAlpha(0, 1, 0, 1); + } + + dsSetTexture (DS_WOOD); + dGeomCylinderGetParams(g, &radius, &length); + dsDrawCylinder(pos, rot, length, radius); + break; + } + + default: + { + abort(); + } + } +} + +void simLoop(int pause) +{ + if (!pause) { + + const dReal step = 0.003; + const unsigned nsteps = 4; + + for (unsigned i=0; i 0.125) { + ratio *= 0.5; + + fprintf (stderr, "Gear ratio set to %.3f.\n", ratio); + } + break; + case dTransmissionIntersectingAxes: + if (theta > 0.1) { + theta -= 0.1; + + fprintf (stderr, "Gear angle set to %.3f deg.\n", + theta / M_PI * 180); + } + break; + case dTransmissionChainDrive: + if (rho_2 > 0.125) { + rho_2 /= 2; + + fprintf (stderr, "Sprocket ratio set to %.3f.\n", rho_2 / rho_1); + } + break; + } + + setup(); + } else if (cmd == ']') { + switch(mode) { + case dTransmissionParallelAxes: + if (ratio < 8) { + ratio *= 2; + + fprintf (stderr, "Gear ratio set to %.3f.\n", ratio); + } + break; + case dTransmissionIntersectingAxes: + if (theta < 0.9) { + theta += 0.1; + + fprintf (stderr, "Gear angle set to %.3f deg.\n", + theta / M_PI * 180); + } + break; + case dTransmissionChainDrive: + if (rho_2 < 2) { + rho_2 *= 2; + + fprintf (stderr, "Sprocket ratio set to %.3f.\n", rho_2 / rho_1); + } + break; + } + + setup(); + } else if (cmd == '.') { + speed += 5; + + fprintf (stderr, "Driving wheel speed set to %g rad/s.\n", speed); + + dJointSetHingeParam(hinge2, dParamVel, speed); + } else if (cmd == ',') { + speed -= 5; + + fprintf (stderr, "Driving wheel speed set to %g rad/s.\n", speed); + + dJointSetHingeParam(hinge2, dParamVel, speed); + } else if (cmd == '/') { + if (dJointGetHingeParam(hinge2, dParamFMax) > 0) { + dJointSetHingeParam(hinge2, dParamFMax, 0); + } else { + dJointSetHingeParam(hinge2, dParamFMax, 50); + } + + } else if (cmd == '-') { + backlash -= 0.1; + + fprintf (stderr, "Backlash set to %g m.\n", backlash); + + dJointSetTransmissionBacklash(transmission, backlash); + } else if (cmd == '=') { + backlash += 0.1; + + fprintf (stderr, "Backlash set to %g m.\n", backlash); + + dJointSetTransmissionBacklash(transmission, backlash); + } else if (cmd == '1') { + mode = dTransmissionParallelAxes; + setup(); + } else if (cmd == '2') { + mode = dTransmissionIntersectingAxes; + setup(); + } else if (cmd == '3') { + mode = dTransmissionChainDrive; + setup(); + } +} + +int main(int argc, char **argv) +{ + // setup pointers to drawstuff callback functions + dsFunctions fn; + fn.version = DS_VERSION; + fn.start = &start; + fn.step = &simLoop; + fn.command = &command; + fn.stop = stop; + fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH; + + // create world + dInitODE(); + + // run demo + dsSimulationLoop (argc, argv, 800, 600, &fn); + + dCloseODE(); + return 0; +} diff --git a/thirdparty/ode-0.16.5/ode/demo/demo_trimesh.cpp b/thirdparty/ode-0.16.5/ode/demo/demo_trimesh.cpp new file mode 100644 index 0000000..1c53334 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/demo_trimesh.cpp @@ -0,0 +1,605 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001-2003 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +// TriMesh test by Erwin de Vries + +#include +#include +#include "texturepath.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244 4305) // for VC++, no precision loss complaints +#endif + +//<---- Convex Object +static const dReal planes[] = // planes for a cube +{ + 1.0f ,0.0f ,0.0f ,0.25f, + 0.0f ,1.0f ,0.0f ,0.25f, + 0.0f ,0.0f ,1.0f ,0.25f, + 0.0f ,0.0f ,-1.0f,0.25f, + 0.0f ,-1.0f,0.0f ,0.25f, + -1.0f,0.0f ,0.0f ,0.25f + /* + 1.0f ,0.0f ,0.0f ,2.0f, + 0.0f ,1.0f ,0.0f ,1.0f, + 0.0f ,0.0f ,1.0f ,1.0f, + 0.0f ,0.0f ,-1.0f,1.0f, + 0.0f ,-1.0f,0.0f ,1.0f, + -1.0f,0.0f ,0.0f ,0.0f + */ +}; +static const unsigned int planecount=6; + +static const dReal points[] = // points for a cube +{ + 0.25f,0.25f,0.25f, + -0.25f,0.25f,0.25f, + + 0.25f,-0.25f,0.25f, + -0.25f,-0.25f,0.25f, + + 0.25f,0.25f,-0.25f, + -0.25f,0.25f,-0.25f, + + 0.25f,-0.25f,-0.25f, + -0.25f,-0.25f,-0.25f, +}; +static const unsigned int pointcount=8; + +static const unsigned int polygons[] = //Polygons for a cube (6 squares) + { + 4,0,2,6,4, // positive X + 4,1,0,4,5, // positive Y + 4,0,1,3,2, // positive Z + 4,3,1,5,7, // negative X + 4,2,3,7,6, // negative Y + 4,5,4,6,7, // negative Z + }; +//----> Convex Object + +// select correct drawing functions + +#ifdef dDOUBLE +#define dsDrawBox dsDrawBoxD +#define dsDrawSphere dsDrawSphereD +#define dsDrawCylinder dsDrawCylinderD +#define dsDrawCapsule dsDrawCapsuleD +#define dsDrawLine dsDrawLineD +#define dsDrawTriangle dsDrawTriangleD +#define dsDrawConvex dsDrawConvexD +#endif + + +// some constants + +#define NUM 200 // max number of objects +#define DENSITY (5.0) // density of all objects +#define GPB 3 // maximum number of geometries per body +#define MAX_CONTACTS 40 // maximum number of contact points per body + + +// dynamics and collision objects + +struct MyObject { + dBodyID body; // the body + dGeomID geom[GPB]; // geometries representing this body +}; + +static int num=0; // number of objects in simulation +static int nextobj=0; // next object to recycle if num==NUM +static dWorldID world; +static dSpaceID space; +static MyObject obj[NUM]; +static dJointGroupID contactgroup; +static int selected = -1; // selected object +static int show_aabb = 0; // show geom AABBs? +static int show_contacts = 0; // show contact points? +static int random_pos = 1; // drop objects from random position? + +#define VertexCount 5 +#define IndexCount 12 + +static dVector3 Size; +static float Vertices[VertexCount][3]; +static dTriIndex Indices[IndexCount]; + +static dGeomID TriMesh; +static dGeomID Ray; + + +// this is called by dSpaceCollide when two objects in space are +// potentially colliding. + +static void nearCallback (void *, dGeomID o1, dGeomID o2) +{ + int i; + // if (o1->body && o2->body) return; + + // exit without doing anything if the two bodies are connected by a joint + dBodyID b1 = dGeomGetBody(o1); + dBodyID b2 = dGeomGetBody(o2); + if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return; + + dContact contact[MAX_CONTACTS]; // up to MAX_CONTACTS contacts per box-box + for (i=0; i= 'A' && c <= 'Z') return c - ('a'-'A'); + else return c; +} + + +// called when a key pressed + +static void command (int cmd) +{ + int i,j,k; + dReal sides[3]; + dMass m; + bool setBody = false; + + cmd = locase (cmd); + if (cmd == 'b' || cmd == 's' || cmd == 'c' || cmd == 'x' || cmd == 'v' + /* || cmd == 'l' */) { + if (num < NUM) { + i = num; + num++; + } + else { + i = nextobj; + nextobj++; + if (nextobj >= num) nextobj = 0; + + // destroy the body and geoms for slot i + dBodyDestroy (obj[i].body); + for (k=0; k < GPB; k++) { + if (obj[i].geom[k]) dGeomDestroy (obj[i].geom[k]); + } + memset (&obj[i],0,sizeof(obj[i])); + } + + obj[i].body = dBodyCreate (world); + for (k=0; k<3; k++) sides[k] = dRandReal()*0.5+0.1; + + dMatrix3 R; + if (random_pos) { + dBodySetPosition (obj[i].body, + dRandReal()*2-1,dRandReal()*2-1,dRandReal()+1); + dRFromAxisAndAngle (R,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0, + dRandReal()*2.0-1.0,dRandReal()*10.0-5.0); + } + else { + dReal maxheight = 0; + for (k=0; k maxheight) maxheight = pos[2]; + } + dBodySetPosition (obj[i].body, 0,0,maxheight+1); + dRFromAxisAndAngle (R,0,0,1,dRandReal()*10.0-5.0); + } + dBodySetRotation (obj[i].body,R); + dBodySetData (obj[i].body,(void*)(dsizeint)i); + + if (cmd == 'b') { + dMassSetBox (&m,DENSITY,sides[0],sides[1],sides[2]); + obj[i].geom[0] = dCreateBox (space,sides[0],sides[1],sides[2]); + } + else if (cmd == 'c') { + sides[0] *= 0.5; + dMassSetCapsule (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCapsule (space,sides[0],sides[1]); + } +/* + // cylinder option not yet implemented + else if (cmd == 'l') { + sides[1] *= 0.5; + dMassSetCapsule (&m,DENSITY,3,sides[0],sides[1]); + obj[i].geom[0] = dCreateCylinder (space,sides[0],sides[1]); + } +*/ + else if (cmd == 's') { + sides[0] *= 0.5; + dMassSetSphere (&m,DENSITY,sides[0]); + obj[i].geom[0] = dCreateSphere (space,sides[0]); + } + else if (cmd == 'x') { + + setBody = true; + // start accumulating masses for the composite geometries + dMass m2; + dMassSetZero (&m); + + dReal dpos[GPB][3]; // delta-positions for composite geometries + dMatrix3 drot[GPB]; + + // set random delta positions + for (j=0; j= num) selected = 0; + if (selected < 0) selected = 0; + } + else if (cmd == 'd' && selected >= 0 && selected < num) { + dBodyDisable (obj[selected].body); + } + else if (cmd == 'e' && selected >= 0 && selected < num) { + dBodyEnable (obj[selected].body); + } + else if (cmd == 'a') { + show_aabb ^= 1; + } + else if (cmd == 't') { + show_contacts ^= 1; + } + else if (cmd == 'r') { + random_pos ^= 1; + } +} + + +// draw a geom + +void drawGeom (dGeomID g, const dReal *pos, const dReal *R, int show_aabb) +{ + if (!g) return; + if (!pos) pos = dGeomGetPosition (g); + if (!R) R = dGeomGetRotation (g); + + int type = dGeomGetClass (g); + if (type == dBoxClass) { + dVector3 sides; + dGeomBoxGetLengths (g,sides); + dsDrawBox (pos,R,sides); + } + else if (type == dSphereClass) { + dsDrawSphere (pos,R,dGeomSphereGetRadius (g)); + } + else if (type == dCapsuleClass) { + dReal radius,length; + dGeomCapsuleGetParams (g,&radius,&length); + dsDrawCapsule (pos,R,length,radius); + } else if (type == dConvexClass) { + //dVector3 sides={0.50,0.50,0.50}; + dsDrawConvex(pos,R,planes, + planecount, + points, + pointcount, + polygons); + } +/* + // cylinder option not yet implemented + else if (type == dCylinderClass) { + dReal radius,length; + dGeomCylinderGetParams (g,&radius,&length); + dsDrawCylinder (pos,R,length,radius); + } +*/ + + if (show_aabb) { + // draw the bounding box for this geom + dReal aabb[6]; + dGeomGetAABB (g,aabb); + dVector3 bbpos; + for (int i=0; i<3; i++) bbpos[i] = 0.5*(aabb[i*2] + aabb[i*2+1]); + dVector3 bbsides; + for (int j=0; j<3; j++) bbsides[j] = aabb[j*2+1] - aabb[j*2]; + dMatrix3 RI; + dRSetIdentity (RI); + dsSetColorAlpha (1,0,0,0.5); + dsDrawBox (bbpos,RI,bbsides); + } +} + + +// simulation loop + +static void simLoop (int pause) +{ + dsSetColor (0,0,2); + dSpaceCollide (space,0,&nearCallback); + if (!pause) dWorldStep (world,0.05); + //if (!pause) dWorldStepFast (world,0.05, 1); + + // remove all contact joints + dJointGroupEmpty (contactgroup); + + dsSetColor (1,1,0); + dsSetTexture (DS_WOOD); + for (int i=0; i +/* + This is a description of a convex icosahedron, to test + the convex collision detection. +*/ +unsigned int Sphere_pointcount = 42; +unsigned int Sphere_planecount = 80; +dReal Sphere_points[126]={ +0.000000,0.000000,-0.300000, +0.217080,-0.157716,-0.134164, +-0.082915,-0.255192,-0.134164, +-0.268327,0.000000,-0.134164, +-0.082915,0.255192,-0.134164, +0.217080,0.157716,-0.134164, +0.082915,-0.255192,0.134164, +-0.217080,-0.157716,0.134164, +-0.217080,0.157716,0.134164, +0.082915,0.255192,0.134164, +0.268327,0.000000,0.134164, +0.000000,0.000000,0.300000, +0.127597,-0.092703,-0.255196, +-0.048737,-0.149999,-0.255196, +0.078861,-0.242703,-0.157721, +0.127597,0.092703,-0.255196, +0.255194,0.000000,-0.157721, +-0.157719,0.000000,-0.255195, +-0.206457,-0.149999,-0.157721, +-0.048737,0.149999,-0.255196, +-0.206457,0.149999,-0.157721, +0.078861,0.242703,-0.157721, +0.285317,0.092704,0.000000, +0.285317,-0.092704,0.000000, +0.176336,-0.242705,0.000000, +0.000000,-0.300000,0.000000, +-0.176336,-0.242705,0.000000, +-0.285317,-0.092704,0.000000, +-0.285317,0.092704,0.000000, +-0.176336,0.242705,0.000000, +0.000000,0.300000,0.000000, +0.176336,0.242705,0.000000, +0.206457,-0.149999,0.157721, +-0.078861,-0.242703,0.157721, +-0.255194,0.000000,0.157721, +-0.078861,0.242703,0.157721, +0.206457,0.149999,0.157721, +0.157719,0.000000,0.255195, +0.048737,-0.149999,0.255196, +-0.127597,-0.092703,0.255196, +-0.127597,0.092703,0.255196, +0.048737,0.149999,0.255196 +}; +unsigned int Sphere_polygons[]={ +3,14,12,1, +3,12,14,13, +3,2,13,14, +3,13,0,12, +3,16,1,12, +3,12,15,16, +3,5,16,15, +3,12,0,15, +3,18,13,2, +3,13,18,17, +3,3,17,18, +3,17,0,13, +3,20,17,3, +3,17,20,19, +3,4,19,20, +3,19,0,17, +3,21,19,4, +3,19,21,15, +3,5,15,21, +3,15,0,19, +3,23,1,16, +3,16,22,23, +3,10,23,22, +3,22,16,5, +3,25,2,14, +3,14,24,25, +3,6,25,24, +3,24,14,1, +3,27,3,18, +3,18,26,27, +3,7,27,26, +3,26,18,2, +3,29,4,20, +3,20,28,29, +3,8,29,28, +3,28,20,3, +3,31,5,21, +3,21,30,31, +3,9,31,30, +3,30,21,4, +3,32,23,10, +3,23,32,24, +3,6,24,32, +3,24,1,23, +3,33,25,6, +3,25,33,26, +3,7,26,33, +3,26,2,25, +3,34,27,7, +3,27,34,28, +3,8,28,34, +3,28,3,27, +3,35,29,8, +3,29,35,30, +3,9,30,35, +3,30,4,29, +3,36,31,9, +3,31,36,22, +3,10,22,36, +3,22,5,31, +3,38,6,32, +3,32,37,38, +3,11,38,37, +3,37,32,10, +3,39,7,33, +3,33,38,39, +3,11,39,38, +3,38,33,6, +3,40,8,34, +3,34,39,40, +3,11,40,39, +3,39,34,7, +3,41,9,35, +3,35,40,41, +3,11,41,40, +3,40,35,8, +3,37,10,36, +3,36,41,37, +3,11,37,41, +3,41,36,9, +}; +dReal Sphere_planes[]={ +0.471317,-0.583121,-0.661687,0.283056, +0.187594,-0.577345,-0.794658,0.280252, +-0.038547,-0.748789,-0.661687,0.283056, +0.102381,-0.315090,-0.943523,0.283057, +0.700228,-0.268049,-0.661688,0.283056, +0.607060,0.000000,-0.794656,0.280252, +0.700228,0.268049,-0.661688,0.283056, +0.331305,0.000000,-0.943524,0.283057, +-0.408939,-0.628443,-0.661686,0.283056, +-0.491119,-0.356821,-0.794657,0.280252, +-0.724044,-0.194735,-0.661694,0.283057, +-0.268034,-0.194737,-0.943523,0.283057, +-0.724044,0.194735,-0.661694,0.283057, +-0.491119,0.356821,-0.794657,0.280252, +-0.408939,0.628443,-0.661686,0.283056, +-0.268034,0.194737,-0.943523,0.283057, +-0.038547,0.748789,-0.661687,0.283056, +0.187594,0.577345,-0.794658,0.280252, +0.471317,0.583121,-0.661687,0.283056, +0.102381,0.315090,-0.943523,0.283057, +0.904981,-0.268049,-0.330393,0.283056, +0.982246,0.000000,-0.187599,0.280252, +0.992077,0.000000,0.125631,0.283057, +0.904981,0.268049,-0.330393,0.283056, +0.024726,-0.943519,-0.330396,0.283056, +0.303531,-0.934171,-0.187598,0.280251, +0.306568,-0.943519,0.125651,0.283056, +0.534590,-0.777851,-0.330395,0.283056, +-0.889698,-0.315092,-0.330386,0.283056, +-0.794656,-0.577348,-0.187595,0.280251, +-0.802607,-0.583125,0.125648,0.283055, +-0.574584,-0.748793,-0.330397,0.283055, +-0.574584,0.748793,-0.330397,0.283055, +-0.794656,0.577348,-0.187595,0.280251, +-0.802607,0.583125,0.125648,0.283055, +-0.889698,0.315092,-0.330386,0.283056, +0.534590,0.777851,-0.330395,0.283056, +0.303531,0.934171,-0.187598,0.280251, +0.306568,0.943519,0.125651,0.283056, +0.024726,0.943519,-0.330396,0.283056, +0.889698,-0.315092,0.330386,0.283056, +0.794656,-0.577348,0.187595,0.280251, +0.574584,-0.748793,0.330397,0.283055, +0.802607,-0.583125,-0.125648,0.283055, +-0.024726,-0.943519,0.330396,0.283055, +-0.303531,-0.934171,0.187598,0.280251, +-0.534590,-0.777851,0.330395,0.283056, +-0.306568,-0.943519,-0.125651,0.283056, +-0.904981,-0.268049,0.330393,0.283056, +-0.982246,0.000000,0.187599,0.280252, +-0.904981,0.268049,0.330393,0.283056, +-0.992077,0.000000,-0.125631,0.283057, +-0.534590,0.777851,0.330395,0.283056, +-0.303531,0.934171,0.187598,0.280251, +-0.024726,0.943519,0.330396,0.283055, +-0.306568,0.943519,-0.125651,0.283056, +0.574584,0.748793,0.330397,0.283055, +0.794656,0.577348,0.187595,0.280251, +0.889698,0.315092,0.330386,0.283056, +0.802607,0.583125,-0.125648,0.283055, +0.408939,-0.628443,0.661686,0.283056, +0.491119,-0.356821,0.794657,0.280252, +0.268034,-0.194737,0.943523,0.283057, +0.724044,-0.194735,0.661694,0.283057, +-0.471317,-0.583121,0.661687,0.283056, +-0.187594,-0.577345,0.794658,0.280252, +-0.102381,-0.315090,0.943523,0.283057, +0.038547,-0.748789,0.661687,0.283056, +-0.700228,0.268049,0.661688,0.283056, +-0.607060,0.000000,0.794656,0.280252, +-0.331305,0.000000,0.943524,0.283057, +-0.700228,-0.268049,0.661688,0.283056, +0.038547,0.748789,0.661687,0.283056, +-0.187594,0.577345,0.794658,0.280252, +-0.102381,0.315090,0.943523,0.283057, +-0.471317,0.583121,0.661687,0.283056, +0.724044,0.194735,0.661694,0.283057, +0.491119,0.356821,0.794657,0.280252, +0.268034,0.194737,0.943523,0.283057, +0.408939,0.628443,0.661686,0.283056, +}; + diff --git a/thirdparty/ode-0.16.5/ode/demo/texturepath.h b/thirdparty/ode-0.16.5/ode/demo/texturepath.h new file mode 100644 index 0000000..5815138 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/texturepath.h @@ -0,0 +1,26 @@ +/************************************************************************* + * * + * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * + * All rights reserved. Email: russ@q12.org Web: www.q12.org * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of EITHER: * + * (1) The GNU Lesser General Public License as published by the Free * + * Software Foundation; either version 2.1 of the License, or (at * + * your option) any later version. The text of the GNU Lesser * + * General Public License is included with this library in the * + * file LICENSE.TXT. * + * (2) The BSD-style license that is included with this library in * + * the file LICENSE-BSD.TXT. * + * * + * 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 files * + * LICENSE.TXT and LICENSE-BSD.TXT for more details. * + * * + *************************************************************************/ + +#ifndef DRAWSTUFF_TEXTURE_PATH +#define DRAWSTUFF_TEXTURE_PATH "../../drawstuff/textures" +#endif + diff --git a/thirdparty/ode-0.16.5/ode/demo/world_geom3.h b/thirdparty/ode-0.16.5/ode/demo/world_geom3.h new file mode 100644 index 0000000..27cd69e --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/demo/world_geom3.h @@ -0,0 +1,9 @@ +// mesh for a world model, to be used with test_cyl.cpp + +static float world_vertices[] = {10.000000f,-10.000000f,1.000000f,-10.000000f,-10.000000f,1.000000f,-10.000000f,-10.000000f,-1.000000f,-10.000000f,-10.000000f,-1.000000f,10.000000f,-10.000000f,-1.000000f,10.000000f,-10.000000f,1.000000f,10.000000f,10.000000f,1.000000f,10.000000f,-10.000000f,1.000000f,10.000000f,-10.000000f,-1.000000f,10.000000f,-10.000000f,-1.000000f,10.000000f,10.000000f,-1.000000f,10.000000f,10.000000f,1.000000f,10.000000f,10.000000f,-1.000000f,10.000000f,-10.000000f,-1.000000f,-10.000000f,-10.000000f,-1.000000f,-10.000000f,-10.000000f,-1.000000f,-10.000000f,10.000000f,-1.000000f,10.000000f,10.000000f,-1.000000f,0.000000f,9.000000f,-0.000000f,0.000000f,-9.000000f,0.000000f,9.000000f,-9.000000f,0.000000f,0.000000f,9.000000f,-0.000000f,9.000000f,-9.000000f,0.000000f,9.000000f,9.000000f,-0.000000f,10.000000f,10.000000f,-1.000000f,-10.000000f,10.000000f,-1.000000f,-10.000000f,10.000000f,1.000000f,10.000000f,10.000000f,-1.000000f,-10.000000f,10.000000f,1.000000f,10.000000f,10.000000f,1.000000f,-10.000000f,-10.000000f,-1.000000f,-10.000000f,-10.000000f,1.000000f,-10.000000f,10.000000f,1.000000f,-10.000000f,10.000000f,1.000000f,-10.000000f,10.000000f,-1.000000f,-10.000000f,-10.000000f,-1.000000f,9.000000f,-9.000000f,1.000000f,-9.000000f,-9.000000f,1.000000f,10.000000f,-10.000000f,1.000000f,-9.000000f,-9.000000f,1.000000f,-10.000000f,-10.000000f,1.000000f,10.000000f,-10.000000f,1.000000f,9.000000f,9.000000f,1.000000f,9.000000f,-9.000000f,1.000000f,10.000000f,-10.000000f,1.000000f,10.000000f,-10.000000f,1.000000f,10.000000f,10.000000f,1.000000f,9.000000f,9.000000f,1.000000f,-9.000000f,9.000000f,1.000000f,9.000000f,9.000000f,1.000000f,10.000000f,10.000000f,1.000000f,10.000000f,10.000000f,1.000000f,-10.000000f,10.000000f,1.000000f,-9.000000f,9.000000f,1.000000f,-9.000000f,9.000000f,1.000000f,-10.000000f,10.000000f,1.000000f,-9.000000f,-9.000000f,1.000000f,-10.000000f,10.000000f,1.000000f,-10.000000f,-10.000000f,1.000000f,-9.000000f,-9.000000f,1.000000f,0.000000f,-9.000000f,0.000000f,-9.000000f,-9.000000f,0.000000f,-9.000000f,-9.000000f,1.000000f,0.000000f,-9.000000f,0.000000f,-9.000000f,-9.000000f,1.000000f,9.000000f,-9.000000f,1.000000f,0.000000f,-9.000000f,0.000000f,9.000000f,-9.000000f,1.000000f,9.000000f,-9.000000f,0.000000f,9.000000f,-9.000000f,0.000000f,9.000000f,-9.000000f,1.000000f,9.000000f,9.000000f,1.000000f,9.000000f,9.000000f,1.000000f,9.000000f,9.000000f,-0.000000f,9.000000f,-9.000000f,0.000000f,0.000000f,9.000000f,-0.000000f,9.000000f,9.000000f,-0.000000f,9.000000f,9.000000f,1.000000f,0.000000f,9.000000f,-0.000000f,9.000000f,9.000000f,1.000000f,-9.000000f,9.000000f,1.000000f,0.000000f,9.000000f,-0.000000f,-9.000000f,9.000000f,1.000000f,-9.000000f,9.000000f,-0.000000f,-9.000000f,9.000000f,1.000000f,-9.000000f,-9.000000f,1.000000f,-9.000000f,-9.000000f,0.000000f,-9.000000f,-9.000000f,0.000000f,-9.000000f,9.000000f,-0.000000f,-9.000000f,9.000000f,1.000000f,-2.997000f,-1.748874f,0.000000f,-2.997000f,-2.001000f,0.000000f,0.000000f,-9.000000f,0.000000f,-2.997000f,-1.748874f,0.000000f,0.000000f,-9.000000f,0.000000f,-2.997000f,1.748874f,-0.000000f,-2.997000f,-2.001000f,0.000000f,-2.997000f,-6.003000f,0.002697f,0.000000f,-9.000000f,0.000000f,0.000000f,9.000000f,-0.000000f,-2.997000f,2.001000f,-0.000000f,-2.997000f,1.748874f,-0.000000f,0.000000f,9.000000f,-0.000000f,-2.997000f,1.748874f,-0.000000f,0.000000f,-9.000000f,0.000000f,-2.997000f,2.001000f,-0.000000f,0.000000f,9.000000f,-0.000000f,-2.997000f,6.003000f,0.002697f,-6.003000f,6.003000f,0.002697f,-2.997000f,6.003000f,0.002697f,0.000000f,9.000000f,-0.000000f,0.000000f,9.000000f,-0.000000f,-9.000000f,9.000000f,-0.000000f,-6.003000f,6.003000f,0.002697f,-6.003000f,1.748874f,-0.000000f,-9.000000f,-9.000000f,0.000000f,-6.003000f,-1.748874f,0.000000f,-6.003000f,2.001000f,-0.000000f,-6.003000f,6.003000f,0.002697f,-9.000000f,9.000000f,-0.000000f,-9.000000f,9.000000f,-0.000000f,-6.003000f,1.748874f,-0.000000f,-6.003000f,2.001000f,-0.000000f,-9.000000f,9.000000f,-0.000000f,-9.000000f,-9.000000f,0.000000f,-6.003000f,1.748874f,-0.000000f,-9.000000f,-9.000000f,0.000000f,-6.003000f,-6.003000f,0.002697f,-6.003000f,-2.001000f,0.000000f,-9.000000f,-9.000000f,0.000000f,-6.003000f,-2.001000f,0.000000f,-6.003000f,-1.748874f,0.000000f,-6.003000f,-6.003000f,0.002697f,-9.000000f,-9.000000f,0.000000f,0.000000f,-9.000000f,0.000000f,-6.003000f,-6.003000f,0.002697f,0.000000f,-9.000000f,0.000000f,-2.997000f,-6.003000f,0.002697f,-2.997000f,1.748874f,1.237951f,-2.997000f,1.748874f,-0.000000f,-2.997000f,2.001000f,-0.000000f,-2.997000f,1.748874f,1.237951f,-2.997000f,2.001000f,-0.000000f,-2.997000f,2.001000f,1.515748f,-6.003000f,-2.001000f,1.515748f,-6.003000f,-6.003000f,0.002697f,-2.997000f,-6.003000f,0.002697f,-6.003000f,-2.001000f,1.515748f,-2.997000f,-6.003000f,0.002697f,-2.997000f,-2.001000f,1.515748f,-2.997000f,2.001000f,1.515748f,-2.997000f,6.003000f,0.002697f,-6.003000f,6.003000f,0.002697f,-6.003000f,6.003000f,0.002697f,-6.003000f,2.001000f,1.515748f,-2.997000f,2.001000f,1.515748f,-6.003000f,-2.001000f,0.000000f,-6.003000f,-6.003000f,0.002697f,-6.003000f,-2.001000f,1.515748f,-6.003000f,2.001000f,1.515748f,-6.003000f,6.003000f,0.002697f,-6.003000f,2.001000f,-0.000000f,-2.997000f,-2.001000f,1.515748f,-2.997000f,-6.003000f,0.002697f,-2.997000f,-2.001000f,0.000000f,-2.997000f,2.001000f,-0.000000f,-2.997000f,6.003000f,0.002697f,-2.997000f,2.001000f,1.515748f,-2.997000f,-2.001000f,1.515748f,-2.997000f,2.001000f,1.515748f,-6.003000f,2.001000f,1.515748f,-6.003000f,2.001000f,1.515748f,-6.003000f,-2.001000f,1.515748f,-2.997000f,-2.001000f,1.515748f,-2.997000f,-1.748874f,1.237951f,-2.997000f,1.748874f,1.237951f,-2.997000f,2.001000f,1.515748f,-2.997000f,-1.748874f,1.237951f,-2.997000f,2.001000f,1.515748f,-2.997000f,-2.001000f,1.515748f,-6.003000f,-1.748874f,1.237951f,-6.003000f,-1.748874f,0.000000f,-6.003000f,-2.001000f,0.000000f,-6.003000f,-1.748874f,1.237951f,-6.003000f,-2.001000f,0.000000f,-6.003000f,-2.001000f,1.515748f,-2.997000f,-2.001000f,1.515748f,-2.997000f,-2.001000f,0.000000f,-2.997000f,-1.748874f,1.237951f,-2.997000f,-2.001000f,0.000000f,-2.997000f,-1.748874f,0.000000f,-2.997000f,-1.748874f,1.237951f,-6.003000f,1.748874f,1.237951f,-6.003000f,2.001000f,1.515748f,-6.003000f,2.001000f,-0.000000f,-6.003000f,1.748874f,1.237951f,-6.003000f,2.001000f,-0.000000f,-6.003000f,1.748874f,-0.000000f,-6.003000f,1.748874f,1.237951f,-6.003000f,-1.748874f,1.237951f,-6.003000f,-2.001000f,1.515748f,-6.003000f,1.748874f,1.237951f,-6.003000f,-2.001000f,1.515748f,-6.003000f,2.001000f,1.515748f,-6.003000f,1.748874f,1.237951f,-6.003000f,1.748874f,-0.000000f,-2.997000f,1.748874f,1.237951f,-6.003000f,1.748874f,-0.000000f,-2.997000f,1.748874f,-0.000000f,-2.997000f,1.748874f,1.237951f,-6.003000f,1.748874f,-0.000000f,-6.003000f,-1.748874f,0.000000f,-2.997000f,-1.748874f,0.000000f,-6.003000f,1.748874f,-0.000000f,-2.997000f,-1.748874f,0.000000f,-2.997000f,1.748874f,-0.000000f,-6.003000f,-1.748874f,0.000000f,-6.003000f,-1.748874f,1.237951f,-2.997000f,-1.748874f,1.237951f,-2.997000f,-1.748874f,1.237951f,-2.997000f,-1.748874f,0.000000f,-6.003000f,-1.748874f,0.000000f,-6.003000f,-1.748874f,1.237951f,-6.003000f,1.748874f,1.237951f,-2.997000f,-1.748874f,1.237951f,-6.003000f,1.748874f,1.237951f,-2.997000f,1.748874f,1.237951f,-2.997000f,-1.748874f,1.237951f}; + +static float world_normals[] = {0.000000f,-1.000000f,0.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000225f,0.000161f,1.000000f,0.000225f,-0.000161f,1.000000f,0.000000f,0.000000f,1.000000f,0.000225f,0.000161f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,-0.000000f,0.000000f,1.000000f,0.000787f,0.000337f,1.000000f,0.000225f,-0.000161f,1.000000f,-0.000000f,0.000000f,1.000000f,0.000225f,-0.000161f,1.000000f,0.000000f,0.000000f,1.000000f,0.000787f,0.000337f,1.000000f,0.000400f,-0.179805f,0.983702f,0.000225f,-0.000161f,1.000000f,0.000225f,0.000161f,1.000000f,0.000787f,-0.000337f,1.000000f,0.000000f,0.000000f,1.000000f,0.000225f,0.000161f,1.000000f,0.000000f,0.000000f,1.000000f,0.000225f,-0.000161f,1.000000f,0.000787f,-0.000337f,1.000000f,0.000225f,0.000161f,1.000000f,0.000532f,0.119686f,0.992812f,-0.000320f,0.143927f,0.989588f,0.000532f,0.119686f,0.992812f,0.000225f,0.000161f,1.000000f,0.000225f,0.000161f,1.000000f,-0.000393f,0.000056f,1.000000f,-0.000320f,0.143927f,0.989588f,-0.000000f,0.000000f,1.000000f,-0.000315f,-0.000045f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000787f,-0.000337f,1.000000f,-0.000320f,0.143927f,0.989588f,-0.000393f,0.000056f,1.000000f,-0.000393f,0.000056f,1.000000f,-0.000000f,0.000000f,1.000000f,-0.000787f,-0.000337f,1.000000f,-0.000393f,0.000056f,1.000000f,-0.000315f,-0.000045f,1.000000f,-0.000000f,0.000000f,1.000000f,-0.000315f,-0.000045f,1.000000f,-0.000398f,-0.089784f,0.995961f,-0.000787f,0.000337f,1.000000f,-0.000315f,-0.000045f,1.000000f,-0.000787f,0.000337f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000398f,-0.089784f,0.995961f,-0.000315f,-0.000045f,1.000000f,0.000225f,-0.000161f,1.000000f,-0.000398f,-0.089784f,0.995961f,0.000225f,-0.000161f,1.000000f,0.000400f,-0.179805f,0.983702f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,0.000000f,-0.239222f,0.970965f,-0.000398f,-0.089784f,0.995961f,0.000400f,-0.179805f,0.983702f,0.000000f,-0.239222f,0.970965f,0.000400f,-0.179805f,0.983702f,0.000000f,-0.119611f,0.992821f,0.000000f,0.239222f,0.970965f,0.000532f,0.119686f,0.992812f,-0.000320f,0.143927f,0.989588f,-0.000320f,0.143927f,0.989588f,0.000000f,0.119611f,0.992821f,0.000000f,0.239222f,0.970965f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,0.000000f,-0.119611f,0.992821f,0.000000f,0.239222f,0.970965f,0.000000f,0.119611f,0.992821f,0.000000f,0.119611f,0.992821f,0.000000f,-0.239222f,0.970965f,0.000000f,-0.119611f,0.992821f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,-0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,-0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,-0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,1.000000f,0.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f,0.000000f,0.000000f,-1.000000f}; + + +static dTriIndex world_indices[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227}; + diff --git a/thirdparty/ode-0.16.5/ode/doc/Doxyfile.in b/thirdparty/ode-0.16.5/ode/doc/Doxyfile.in new file mode 100644 index 0000000..a72fb02 --- /dev/null +++ b/thirdparty/ode-0.16.5/ode/doc/Doxyfile.in @@ -0,0 +1,2331 @@ +# Doxyfile 1.8.5 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = ODE + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @ODE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = @top_srcdir@/web/ODElogo.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese- +# Traditional, Croatian, Czech, Danish, Dutch, English, Esperanto, Farsi, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en, +# Korean, Korean-en, Latvian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, +# Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized anymore. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = @top_srcdir@/include/ode @top_builddir@/include/ode + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.as \ + *.js + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = ODE_API + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = d + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 243 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 187 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 92 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = YES + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = NativeMML + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /
  17. 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¥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%0Ahw^WSxV||$zr5VObt-m5LxnDdwt|rezL}0 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 z%0D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 z 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 P00000NkvXXu0mjfA2P4f literal 0 HcmV?d00001 diff --git a/content/textures/ui/ui_msg_nopoints.png b/content/textures/ui/ui_msg_nopoints.png new file mode 100644 index 0000000000000000000000000000000000000000..365d5eb0318ad9d5f494108ac8b914ecfebb2c58 GIT binary patch literal 2453 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 +#include "filesystem/file.h" + +static const char* s_stdioOpeningMode[] = { "rb", "wb" }; +static int s_stdioSeekDir[] = { SEEK_SET, SEEK_CUR, SEEK_END }; + +File::File(const char* path, FileAccess access) +{ + m_filehandle = fopen(path, s_stdioOpeningMode[(int)access]); + assert(m_filehandle && "Unable to open file"); +} + +File::~File() +{ + if (m_filehandle) + { + fclose(m_filehandle); + m_filehandle = nullptr; + } +} + +void File::seek(SeekDir seekdir, long offset) +{ + fseek(m_filehandle, offset, s_stdioSeekDir[(int)seekdir]); +} + +long File::tell() +{ + return ftell(m_filehandle); +} + +bool File::eof() +{ + return feof(m_filehandle); +} + +size_t File::read(void* buffer, size_t size) +{ + return fread(buffer, 1, size, m_filehandle); +} + +size_t File::write(void const* buffer, size_t size) +{ + return fwrite(buffer, size, 1, m_filehandle); +} + +void File::readStringBuffer(char* buffer, size_t bufferSize) +{ + seek(SeekDir::End, 0); + size_t length = tell(); + seek(SeekDir::Begin, 0); + + assert(length <= bufferSize); + + read(buffer, length); + buffer[length] = '\0'; +} diff --git a/src/engine/filesystem/file.h b/src/engine/filesystem/file.h new file mode 100644 index 0000000..14b53b5 --- /dev/null +++ b/src/engine/filesystem/file.h @@ -0,0 +1,28 @@ +#ifndef FILE_H +#define FILE_H + +#include "filesystem/filecommon.h" + +class File +{ +public: + File(const char* path, FileAccess access); + ~File(); + + FILE* getHandle() { return m_filehandle; } + + void seek(SeekDir seekdir, long offset); + long tell(); + bool eof(); + + size_t read(void* buffer, size_t size); + size_t write(void const* buffer, size_t size); + + // helpers + void readStringBuffer(char* buffer, size_t bufferSize); + +private: + FILE* m_filehandle; +}; + +#endif diff --git a/src/engine/filesystem/filecommon.h b/src/engine/filesystem/filecommon.h new file mode 100644 index 0000000..12b680c --- /dev/null +++ b/src/engine/filesystem/filecommon.h @@ -0,0 +1,21 @@ +#ifndef FILECOMMON_H +#define FILECOMMON_H + +#include + +static const int kMaxPathLength = 260; + +enum class FileAccess +{ + Read, + Write +}; + +enum class SeekDir +{ + Begin, + Current, + End +}; + +#endif // !FILECOMMON_H diff --git a/src/engine/filesystem/filemanager.cpp b/src/engine/filesystem/filemanager.cpp new file mode 100644 index 0000000..e65991a --- /dev/null +++ b/src/engine/filesystem/filemanager.cpp @@ -0,0 +1,316 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "filesystem/stream.h" +#include "filesystem/filemanager.h" + +void RecursiveSearch(const std::string& path, std::vector& files); +void osResolvePath(char* path); + +FileManager* g_fileManager; + +FileManager::FileManager() +{ + static char currentDirectory[kMaxPathLength]; + GetCurrentDirectoryA(kMaxPathLength, currentDirectory); + strcat(currentDirectory, "/"); + + osResolvePath(currentDirectory); + + m_defaultPath = currentDirectory; +} + +FileManager::~FileManager() +{ + +} + +void FileManager::SetDefaultPath(const char* path) +{ + m_defaultPath = path; +} + +bool FileManager::FileExist(const char* filename) +{ +#if defined(WIN32) || defined(_WIN32) + char pathBuffer[kMaxPathLength]; + if (!strstr(filename, m_defaultPath)) + sprintf(pathBuffer, "%s%s", m_defaultPath, filename); + else + strcpy(pathBuffer, filename); + + osResolvePath(pathBuffer); + + DWORD dwAttrib = GetFileAttributes(pathBuffer); + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + FILE* file; + + if ((file = fopen(filename, "r")) != NULL) + { + fclose(file); + return true; +} + else + return false; +#endif +} + +File* FileManager::OpenFile(const char* path, FileAccess access) +{ + char pathBuffer[kMaxPathLength]; + if (!strstr(path, m_defaultPath)) + sprintf(pathBuffer, "%s%s", m_defaultPath, path); + else + strcpy(pathBuffer, path); + + osResolvePath(pathBuffer); + + return new File(pathBuffer, access); +} + +void FileManager::CloseFile(File*& file) +{ + if (file) + { + delete file; + file = nullptr; + } +} + +StreamBase* FileManager::OpenStream(const char* fname, FileAccess access) +{ + char pathBuffer[kMaxPathLength]; + if (!strstr(fname, m_defaultPath)) + sprintf(pathBuffer, "%s%s", m_defaultPath, fname); + else + strcpy(pathBuffer, fname); + + osResolvePath(pathBuffer); + + if (!g_fileManager->FileExist( pathBuffer )) + return nullptr; + + File* file = new File(pathBuffer, access); + return createFileStream(file); +} + +#if _WIN32 +#include +#include +#include +#include + +void GetFileListFromFolder(const char* path, const char* findingPattern, std::vector& filesList) +{ + struct _finddata_t c_file; + intptr_t hFile; + + char pathfind[kMaxPathLength]; + sprintf(pathfind, "%s/%s", path, findingPattern); + osResolvePath(pathfind); + + if ((hFile = _findfirst(pathfind, &c_file)) != -1L) { + do { + char buffer[kMaxPathLength]; + sprintf(buffer, "%s/%s", path, c_file.name); + filesList.push_back(buffer); + } while (_findnext(hFile, &c_file) == 0); + _findclose(hFile); + } +} + +void osResolvePath(char* path) +{ + assert(path); + + size_t length = strlen(path); + + for (int i = 0; i < (int)length; i++) { + if (path[i] == '/') + path[i] = '\\'; + } +} + +bool osDirectoryIsExist(const char* path) +{ + DWORD ftyp = GetFileAttributesA(path); + if (ftyp == INVALID_FILE_ATTRIBUTES) + return false; + + if (ftyp & FILE_ATTRIBUTE_DIRECTORY) + return true; + + return false; +} + +void osCreateDirectory(const char* path) +{ + CreateDirectoryA(path, NULL); +} + +void RecursiveSearch(const std::string& path, std::vector& files) +{ + struct _finddata_t c_file; + intptr_t hFile; + + std::string bufferPath; + bufferPath += path; + bufferPath += "/"; + bufferPath += "*.*"; + + if ((hFile = _findfirst(bufferPath.c_str(), &c_file)) != -1L) + { + do { + if (strcmp(c_file.name, ".") == 0 || + strcmp(c_file.name, "..") == 0 || + strcmp(c_file.name, ".git") == 0 || + strcmp(c_file.name, ".gitignore") == 0 || + strcmp(c_file.name, ".gitignore") == 0 || + strcmp(c_file.name, ".vs") == 0) + continue; + + if (c_file.attrib & _A_SUBDIR) + { + std::string nextPath; + nextPath += path; + nextPath += "/"; + nextPath += c_file.name; + + RecursiveSearch(nextPath, files); + } + else + { + std::string filepath; + filepath += path; + filepath += "/"; + filepath += c_file.name; + files.push_back(filepath); + } + + } while (_findnext(hFile, &c_file) == 0); + _findclose(hFile); + } +} + +#else +#include + +bool match(const char* pattern, const char* str, int p = 0, int c = 0) +{ + if (pattern[p] == '\0') + { + return str[c] == '\0'; + } + else if (pattern[p] == '*') + { + for (; str[c] != '\0'; c++) + { + if (match(pattern, str, p + 1, c)) + return true; + } + return match(pattern, str, p + 1, c); + } + else if (pattern[p] != '?' && pattern[p] != str[c]) + { + return false; + } + else + { + return match(pattern, str, p + 1, c + 1); + } +} + +void GetFileListFromFolder(const char* path, const char* findingPattern, std::vector& filesList) +{ + DIR* dir; + dirent* ent; + + if ((dir = opendir(path)) != NULL) + { + while ((ent = readdir(dir)) != NULL) + { + if (match(findingPattern, ent->d_name)) + { + char buffer[kMaxPathLength]; + snprintf(buffer, kMaxPathLength, "%s/%s", path, ent->d_name); + filesList.push_back(buffer); + } + } + + closedir(dir); + } +} +#endif + +namespace fs +{ + std::string getFileExtension(const std::string& filename) + { + size_t whereIsDot = filename.find_last_of('.'); + if (whereIsDot != std::string::npos) { + return filename.substr(whereIsDot); + } + + return std::string(); + } + + std::string getFilePath(const std::string& filename) + { + size_t lastindex = filename.find_last_of("/"); + if (lastindex == std::string::npos) { + lastindex = filename.find_last_of("\\"); + } + + if (lastindex != std::string::npos) { + + return filename.substr(0, lastindex); + } + + return std::string(); + } + + std::string getFileNameWithoutExtension(const std::string& filename) + { + size_t lastindex = filename.find_last_of("."); + if (lastindex != std::string::npos) { + return filename.substr(0, lastindex); + } + + return filename; + } + + std::string getFilenameWithoutPath(const std::string& filename) + { + size_t whereIsSlash = filename.find_last_of('/'); + if (whereIsSlash == std::string::npos) { + whereIsSlash = filename.find_last_of('\\'); + } + + if (whereIsSlash == std::string::npos) { + return filename; + } + + std::string string = filename.substr(whereIsSlash + 1); + return string; + } + + std::string getFilenameWithoutPathAndExtension(const std::string& filename) + { + size_t whereIsDot = filename.find_last_of('.'); + size_t whereIsSlash = filename.find_last_of('/'); + if (whereIsSlash == std::string::npos) { + whereIsSlash = filename.find_last_of('\\'); + } + + if (whereIsDot == std::string::npos && whereIsSlash == std::string::npos) { + return filename; + } + + std::string string = filename.substr(whereIsSlash + 1); + whereIsDot = string.find_last_of('.'); + string = string.substr(0, whereIsDot); + + return string; + } +} \ No newline at end of file diff --git a/src/engine/filesystem/filemanager.h b/src/engine/filesystem/filemanager.h new file mode 100644 index 0000000..1854b0a --- /dev/null +++ b/src/engine/filesystem/filemanager.h @@ -0,0 +1,51 @@ +#ifndef FILEMANAGER_H +#define FILEMANAGER_H + +#include +#include + +#include "filesystem/file.h" + +class StreamBase; + +class FileManager +{ +public: + FileManager(); + ~FileManager(); + + void SetDefaultPath(const char* path); + const char* GetDefaultPath() { return m_defaultPath; } + + bool FileExist(const char* filename); + + File* OpenFile(const char* path, FileAccess access); + void CloseFile(File*& file); + + StreamBase* OpenStream(const char* fname, FileAccess access); + +private: + const char* m_defaultPath; +}; + +extern FileManager* g_fileManager; + +void GetFileListFromFolder(const char* path, const char* findingPattern, std::vector& filesList); + +void osResolvePath(char* path); +bool osDirectoryIsExist(const char* path); +void osCreateDirectory(const char* path); + +// Filename helper + +namespace fs +{ + std::string getFileExtension(const std::string& filename); + std::string getFilePath(const std::string& filename); + std::string getFileNameWithoutExtension(const std::string& filename); + std::string getFilenameWithoutPath(const std::string& filename); + std::string getFilenameWithoutPathAndExtension(const std::string& filename); +}; + + +#endif // !FILEMANAGER_H diff --git a/src/engine/filesystem/stream.cpp b/src/engine/filesystem/stream.cpp new file mode 100644 index 0000000..82fd83b --- /dev/null +++ b/src/engine/filesystem/stream.cpp @@ -0,0 +1,61 @@ +#include +#include "filesystem/stream.h" +#include "filesystem/filemanager.h" + +class FileStream : public StreamBase +{ +public: + FileStream(File* file); + ~FileStream(); + + size_t readBuffer(void* buffer, size_t size); + size_t writeBuffer(void* buffer, size_t size); + void seek(SeekDir way, long offset); + size_t tell(); + bool eof(); + +private: + File* m_file; +}; + +FileStream::FileStream(File* file) +{ + assert(file && "Cannot open file stream with nullptr file handle."); + m_file = file; +} + +FileStream::~FileStream() +{ + if (m_file) + g_fileManager->CloseFile(m_file); +} + +size_t FileStream::readBuffer(void* buffer, size_t size) +{ + return m_file->read(buffer, size); +} + +size_t FileStream::writeBuffer(void* buffer, size_t size) +{ + return m_file->write(buffer, size); +} + +void FileStream::seek(SeekDir way, long offset) +{ + m_file->seek(way, offset); +} + +size_t FileStream::tell() +{ + return m_file->tell(); +} + +bool FileStream::eof() +{ + return m_file->eof();; +} + +StreamBase* createFileStream(File* file) +{ + return new FileStream(file); +} \ No newline at end of file diff --git a/src/engine/filesystem/stream.h b/src/engine/filesystem/stream.h new file mode 100644 index 0000000..dfa0b86 --- /dev/null +++ b/src/engine/filesystem/stream.h @@ -0,0 +1,21 @@ +#ifndef STREAM_H +#define STREAM_H + +#include "filesystem/filecommon.h" + +class StreamBase +{ +public: + virtual ~StreamBase() {} + + virtual size_t readBuffer(void* buffer, size_t size) = 0; + virtual size_t writeBuffer(void* buffer, size_t size) = 0; + virtual void seek(SeekDir way, long offset) = 0; + virtual size_t tell() = 0; + virtual bool eof() = 0; +}; + +class File; +StreamBase* createFileStream(File* file); + +#endif // !STREAM_H diff --git a/src/engine/input/input_system.cpp b/src/engine/input/input_system.cpp new file mode 100644 index 0000000..0b42632 --- /dev/null +++ b/src/engine/input/input_system.cpp @@ -0,0 +1,182 @@ +#include +#include +#include "utils/logger.h" +#include "utils/maths.h" +#include "input/input_system.h" + +const int kMaxKeyboardKeys = 460; +const int kMaxMouseButtons = mouseButton_LastNamed + 1; //5; +const float kKeyThreshold = 0.2f; + +IInputSystem* g_inputSystem = nullptr; + +class InputSystem : public IInputSystem +{ +public: + InputSystem(); + ~InputSystem(); + + void Init(); + void Shutdown(); + void Update(); + + size_t GetMaxScancode() const; + size_t GetMaxMouseButton() const; + + char TranslateScancode(scanCode_t scancode) const; + + void OnKeyDown(scanCode_t scancode); + void OnKeyUp(scanCode_t scancode); + void OnMouseKeyDown(mouseButton_t mousebutton); + void OnMouseKeyUp(mouseButton_t mousebutton); + void OnMouseMove(int32_t x, int32_t y); + void OnMouseWheel(int32_t x, int32_t y); + + bool IsKeyPressed(scanCode_t scancode) const; + bool IsMouseButtonPressed(mouseButton_t mousebutton) const; + + void GetMousePosition(int32_t* x, int32_t* y) const; + void GetMouseWheelPosition(int32_t* x, int32_t* y) const; + + const char* ActionToString(Action action) const; + void BindKey(Action action, scanCode_t scancode); + bool IsActionActive(Action action) const; + void UnBindKey(Action action); + void UnBindAll(); + +private: + bool m_keyboardKeys[kMaxKeyboardKeys]; + bool m_mouseButtons[kMaxMouseButtons]; + Point2 m_mousePosition; +}; + +InputSystem::InputSystem() +{ + memset( m_keyboardKeys, 0, sizeof( m_keyboardKeys ) ); + memset( m_mouseButtons, 0, sizeof( m_mouseButtons ) ); + memset( &m_mousePosition, 0, sizeof( m_mousePosition ) ); +} + +InputSystem::~InputSystem() +{ +} + +void InputSystem::Init() +{ + // Nothing to do +} + +void InputSystem::Shutdown() +{ + // Nothing to do +} + +void InputSystem::Update() +{ + // TODO: Reset all pressed keys ... +} + +size_t InputSystem::GetMaxScancode() const +{ + return kMaxKeyboardKeys; +} + +size_t InputSystem::GetMaxMouseButton() const +{ + return kMaxMouseButtons; +} + +char InputSystem::TranslateScancode(scanCode_t scancode) const +{ + assert(0 && "Not implemented"); + return 0; +} + +void InputSystem::OnKeyDown(scanCode_t scancode) +{ + assert(scancode <= kMaxKeyboardKeys); + m_keyboardKeys[scancode] = true; +} + +void InputSystem::OnKeyUp(scanCode_t scancode) +{ + assert(scancode <= kMaxKeyboardKeys); + m_keyboardKeys[scancode] = false; +} + +void InputSystem::OnMouseKeyDown(mouseButton_t mousebutton) +{ + assert(mousebutton <= kMaxMouseButtons); + m_mouseButtons[mousebutton] = true; +} + +void InputSystem::OnMouseKeyUp(mouseButton_t mousebutton) +{ + assert(mousebutton <= kMaxMouseButtons); + m_mouseButtons[mousebutton] = false; +} + +void InputSystem::OnMouseMove(int32_t x, int32_t y) +{ + m_mousePosition.x = (int)x; + m_mousePosition.y = (int)y; +} + +void InputSystem::OnMouseWheel(int32_t x, int32_t y) +{ +} + +bool InputSystem::IsKeyPressed(scanCode_t scancode) const +{ + assert(scancode <= KEY_MAX); + return m_keyboardKeys[scancode]; +} + +bool InputSystem::IsMouseButtonPressed(mouseButton_t mousebutton) const +{ + assert(mousebutton <= kMaxMouseButtons); + return m_mouseButtons[mousebutton]; +} + +void InputSystem::GetMousePosition(int32_t* x, int32_t* y) const +{ + if (x) *x = (int32_t)m_mousePosition.x; + if (y) *y = (int32_t)m_mousePosition.y; +} + +void InputSystem::GetMouseWheelPosition(int32_t* x, int32_t* y) const +{ +} + +const char* InputSystem::ActionToString(Action action) const +{ + return nullptr; +} + +void InputSystem::BindKey(Action action, scanCode_t scancode) +{ +} + +bool InputSystem::IsActionActive(Action action) const +{ + return false; +} + +void InputSystem::UnBindKey(Action action) +{ +} + +void InputSystem::UnBindAll() +{ +} + +IInputSystem* CreateInputSystem(void* wndHandle) +{ + return new InputSystem(); +} + +void DestroyInputSystem(IInputSystem* pInstance) +{ + InputSystem* pInput = (InputSystem*)pInstance; + delete pInput; +} diff --git a/src/engine/input/input_system.h b/src/engine/input/input_system.h new file mode 100644 index 0000000..e1928b4 --- /dev/null +++ b/src/engine/input/input_system.h @@ -0,0 +1,198 @@ +#ifndef INPUT_SYSTEM_H +#define INPUT_SYSTEM_H + +#include +#include + +enum KeyboardKeys +{ + KEY_UNKNOWN = 0, + KEY_SPACE = 32, + KEY_APOSTROPHE = 39, /* ' */ + KEY_COMMA = 44, /* , */ + KEY_MINUS = 45, /* - */ + KEY_PERIOD = 46, /* . */ + KEY_SLASH = 47, /* / */ + KEY_0 = 48, + KEY_1 = 49, + KEY_2 = 50, + KEY_3 = 51, + KEY_4 = 52, + KEY_5 = 53, + KEY_6 = 54, + KEY_7 = 55, + KEY_8 = 56, + KEY_9 = 57, + KEY_SEMICOLON = 59, /* ; */ + KEY_EQUAL = 61, /* = */ + KEY_A = 65, + KEY_B = 66, + KEY_C = 67, + KEY_D = 68, + KEY_E = 69, + KEY_F = 70, + KEY_G = 71, + KEY_H = 72, + KEY_I = 73, + KEY_J = 74, + KEY_K = 75, + KEY_L = 76, + KEY_M = 77, + KEY_N = 78, + KEY_O = 79, + KEY_P = 80, + KEY_Q = 81, + KEY_R = 82, + KEY_S = 83, + KEY_T = 84, + KEY_U = 85, + KEY_V = 86, + KEY_W = 87, + KEY_X = 88, + KEY_Y = 89, + KEY_Z = 90, + KEY_LEFT_BRACKET = 91, /* [ */ + KEY_BACKSLASH = 92, /* \ */ + KEY_RIGHT_BRACKET = 93, /* ] */ + KEY_GRAVE_ACCENT = 96, /* ` */ + KEY_WORLD_1 = 161, /* non-US #1 */ + KEY_WORLD_2 = 162, /* non-US #2 */ + KEY_ESCAPE = 256, + KEY_ENTER = 257, + KEY_TAB = 258, + KEY_BACKSPACE = 259, + KEY_INSERT = 260, + KEY_DELETE = 261, + KEY_RIGHT = 262, + KEY_LEFT = 263, + KEY_DOWN = 264, + KEY_UP = 265, + KEY_PAGE_UP = 266, + KEY_PAGE_DOWN = 267, + KEY_HOME = 268, + KEY_END = 269, + KEY_CAPS_LOCK = 280, + KEY_SCROLL_LOCK = 281, + KEY_NUM_LOCK = 282, + KEY_PRINT_SCREEN = 283, + KEY_PAUSE = 284, + KEY_F1 = 290, + KEY_F2 = 291, + KEY_F3 = 292, + KEY_F4 = 293, + KEY_F5 = 294, + KEY_F6 = 295, + KEY_F7 = 296, + KEY_F8 = 297, + KEY_F9 = 298, + KEY_F10 = 299, + KEY_F11 = 300, + KEY_F12 = 301, + KEY_F13 = 302, + KEY_F14 = 303, + KEY_F15 = 304, + KEY_F16 = 305, + KEY_F17 = 306, + KEY_F18 = 307, + KEY_F19 = 308, + KEY_F20 = 309, + KEY_F21 = 310, + KEY_F22 = 311, + KEY_F23 = 312, + KEY_F24 = 313, + KEY_F25 = 314, + KEY_KP_0 = 320, + KEY_KP_1 = 321, + KEY_KP_2 = 322, + KEY_KP_3 = 323, + KEY_KP_4 = 324, + KEY_KP_5 = 325, + KEY_KP_6 = 326, + KEY_KP_7 = 327, + KEY_KP_8 = 328, + KEY_KP_9 = 329, + KEY_KP_DECIMAL = 330, + KEY_KP_DIVIDE = 331, + KEY_KP_MULTIPLY = 332, + KEY_KP_SUBTRACT = 333, + KEY_KP_ADD = 334, + KEY_KP_ENTER = 335, + KEY_KP_EQUAL = 336, + KEY_LEFT_SHIFT = 340, + KEY_LEFT_CONTROL = 341, + KEY_LEFT_ALT = 342, + KEY_LEFT_SUPER = 343, + KEY_RIGHT_SHIFT = 344, + KEY_RIGHT_CONTROL = 345, + KEY_RIGHT_ALT = 346, + KEY_RIGHT_SUPER = 347, + KEY_MENU = 348, + + KEY_MAX +}; + +typedef KeyboardKeys scanCode_t; +typedef uint8_t mouseButton_t; + +enum mouseButton_ +{ + mouseButton_Left = 1, + mouseButton_Middle , + mouseButton_Right , + + mouseButton_LastNamed = mouseButton_Right +}; +#define GetNamelessMouseButtonIndex(buttonNum) mouseButton_t(mouseButton_LastNamed + buttonNum) + +enum Action_ +{ + Action_Forward, + Action_Backward, + Action_Left, + Action_Right, + Action_Fire, + Action_Use, + Action_Max +}; +typedef uint32_t Action; + +class IInputSystem +{ +public: + virtual ~IInputSystem() {} + + virtual void Init() = 0; + virtual void Shutdown() = 0; + virtual void Update() = 0; + + virtual size_t GetMaxScancode () const = 0; + virtual size_t GetMaxMouseButton() const = 0; + + virtual char TranslateScancode(scanCode_t scancode) const = 0; + + virtual void OnKeyDown (scanCode_t scancode) = 0; + virtual void OnKeyUp (scanCode_t scancode) = 0; + virtual void OnMouseKeyDown (mouseButton_t mousebutton) = 0; + virtual void OnMouseKeyUp (mouseButton_t mousebutton) = 0; + virtual void OnMouseMove (int32_t x, int32_t y) = 0; + virtual void OnMouseWheel (int32_t x, int32_t y) = 0; + + virtual bool IsKeyPressed (scanCode_t scancode) const = 0; + virtual bool IsMouseButtonPressed (mouseButton_t mousebutton) const = 0; + + virtual void GetMousePosition (int32_t* x, int32_t* y) const = 0; + virtual void GetMouseWheelPosition (int32_t* x, int32_t* y) const = 0; + + virtual const char* ActionToString (Action action) const = 0; + virtual void BindKey (Action action, scanCode_t scancode) = 0; + virtual bool IsActionActive (Action action) const = 0; + virtual void UnBindKey (Action action) = 0; + virtual void UnBindAll () = 0; +}; + +extern IInputSystem* g_inputSystem; + +IInputSystem* CreateInputSystem(void* wndHandle); +void DestroyInputSystem(IInputSystem* pInstance); + +#endif // !INPUT_SYSTEM_H diff --git a/src/engine/physics/physics_object.cpp b/src/engine/physics/physics_object.cpp new file mode 100644 index 0000000..5b85e73 --- /dev/null +++ b/src/engine/physics/physics_object.cpp @@ -0,0 +1,158 @@ +#include "utils/maths.h" +#include "physics_object.h" +#include "physics_world.h" +#include "physics_render.h" +#include "game/game_object.h" + +#include + +#include + +PhysicsObject::PhysicsObject() : + m_pCollisionShape(nullptr), m_pRigidBody(nullptr), m_pTriMesh(nullptr), + m_pEntity(nullptr), + m_Friction(0.5f), m_Gravity(0.0f), m_LinearVelocity(0.0f) +{ +} + +PhysicsObject::~PhysicsObject() +{ +} + +void PhysicsObject::CreateTriMesh(const Vec3* vertices, const int verticesCount, const uint32_t* indices, const int indicesCount) +{ + m_pRigidBody = dBodyCreate(g_PhysicsWorld->GetWorld()); + dBodySetPosition(m_pRigidBody, 0, 0, 0); + + dMass m; + dMassSetBox(&m, 1, 10.0f, 10.0f, 10.0f); + dBodySetMass(m_pRigidBody, &m); + + // create triangle mesh + m_pTriMesh = dGeomTriMeshDataCreate(); + + // collect data + std::vector odevertices; + int j = 0; + for (int i = 0; i < verticesCount; i++) + { + if (j > 2) + j = 0; + + if (j == 0) + odevertices.push_back(vertices[i].x); + if (j == 1) + odevertices.push_back(vertices[i].x); + if (j == 2) + odevertices.push_back(vertices[i].x); + + j++; + } + + dGeomTriMeshDataBuildSimple(m_pTriMesh, odevertices.data(), odevertices.size(), (const dTriIndex*)indices, indicesCount); + m_pCollisionShape = dCreateTriMesh(g_PhysicsWorld->GetSpace(), m_pTriMesh, NULL, NULL, NULL); + dGeomSetBody(m_pCollisionShape, m_pRigidBody); +} + +void PhysicsObject::CreatePlayer() +{ + m_pRigidBody = dBodyCreate(g_PhysicsWorld->GetWorld()); + dBodySetPosition(m_pRigidBody, 0, 0, 0); + dBodySetKinematic(m_pRigidBody); + dBodySetAutoDisableFlag(m_pRigidBody, 0); // disable body sleeping + + dMass m; + dMassSetBox(&m, 1, 10.0f, 10.0f, 10.0f); + dBodySetMass(m_pRigidBody, &m); + + m_pCollisionShape = dCreateBox(g_PhysicsWorld->GetSpace(), 0.5f, 0.5f, 0.5f); + dGeomSetBody(m_pCollisionShape, m_pRigidBody); +} + +void PhysicsObject::CreateSimpleBox() +{ + m_pRigidBody = dBodyCreate(g_PhysicsWorld->GetWorld()); + dBodySetPosition(m_pRigidBody, 0, 0, 0); + + dMass m; + dMassSetBox(&m, 1, 10.0f, 10.0f, 10.0f); + dBodySetMass(m_pRigidBody, &m); + + m_pCollisionShape = dCreateBox(g_PhysicsWorld->GetSpace(), 0.5f, 0.5f, 0.5f); + dGeomSetBody(m_pCollisionShape, m_pRigidBody); +} + +void PhysicsObject::CreateSimpleBox_Kinematic() +{ + m_pRigidBody = dBodyCreate(g_PhysicsWorld->GetWorld()); + dBodySetPosition(m_pRigidBody, 0, 0, 0); + dBodySetKinematic(m_pRigidBody); + + dMass m; + dMassSetBox(&m, 1, 10.0f, 10.0f, 10.0f); + dBodySetMass(m_pRigidBody, &m); + + m_pCollisionShape = dCreateBox(g_PhysicsWorld->GetSpace(), 0.5f, 0.5f, 0.5f); + dGeomSetBody(m_pCollisionShape, m_pRigidBody); +} + +void PhysicsObject::Destroy() +{ + g_PhysicsWorld->RemoveObject(this); + + dGeomTriMeshDataDestroy(m_pTriMesh); + dGeomDestroy(m_pCollisionShape); + dBodyDestroy(m_pRigidBody); +} + +void PhysicsObject::SyncWithEntity() +{ + if (!m_pEntity) + return; + + Vec3 position = m_pEntity->m_position; + dBodySetPosition(m_pRigidBody, position.x, position.y, position.z); +} + +void PhysicsObject::SyncEntity() +{ + if (!m_pEntity) + return; + + const dReal* pPos = dBodyGetPosition(m_pRigidBody); + m_pEntity->m_position.x = pPos[0]; + m_pEntity->m_position.y = pPos[1]; + m_pEntity->m_position.z = pPos[2]; +} + +float PhysicsObject::GetFriction() const +{ + return m_Friction; +} + +void PhysicsObject::SetFriction(float friction) +{ + m_Friction = friction; +} + +void PhysicsObject::SetLinearVelocity(const Vec3& vel) +{ + m_LinearVelocity = vel; + dBodySetLinearVel(m_pRigidBody, vel.x, vel.y, vel.z); +} + +Vec3 PhysicsObject::GetLinearVelocity() const +{ + const dReal* vel = dBodyGetLinearVel(m_pRigidBody); + return Vec3( vel[0], vel[1], vel[2] ); +} + +void PhysicsObject::SetGravity(const Vec3& gravityVector) +{ + m_Gravity = gravityVector; +} + +Vec3 PhysicsObject::GetGravity() +{ + return m_Gravity; +} diff --git a/src/engine/physics/physics_object.h b/src/engine/physics/physics_object.h new file mode 100644 index 0000000..8595db9 --- /dev/null +++ b/src/engine/physics/physics_object.h @@ -0,0 +1,54 @@ +#ifndef PHYSICS_OBJECT_H +#define PHYSICS_OBJECT_H + +#include "physics/physics_world.h" + +class GameObject; + +class PhysicsObject +{ +public: + PhysicsObject(); + ~PhysicsObject(); + + void CreateTriMesh(const Vec3* vertices, const int verticesCount, const uint32_t* indices, const int indicesCount); + void CreatePlayer(); + void CreateSimpleBox(); + void CreateSimpleBox_Kinematic(); + + void Destroy(); + + inline dGeomID GetShape() { return (m_pCollisionShape); } + inline dGeomID const GetShape() const { return (m_pCollisionShape); } + + inline dBodyID GetBody() { return (m_pRigidBody); } + inline dBodyID const GetBody() const { return (m_pRigidBody); } + + inline void SetEntity(GameObject* pEntity) { m_pEntity = pEntity; } + inline GameObject* GetEntity() { return (m_pEntity); } + inline GameObject const* GetEntity() const { return (m_pEntity); } + + void SyncWithEntity (); + void SyncEntity (); + + float GetFriction () const; + void SetFriction (float friction); + void SetLinearVelocity (const Vec3& vel); + Vec3 GetLinearVelocity () const; + + void SetGravity (const Vec3& gravityVector); + Vec3 GetGravity (); + +private: + GameObject* m_pEntity; + dTriMeshDataID m_pTriMesh; + dGeomID m_pCollisionShape; + dBodyID m_pRigidBody; + + float m_Friction; + Vec3 m_Gravity; + + Vec3 m_LinearVelocity; +}; + +#endif // !PHYSICS_OBJECT_H diff --git a/src/engine/physics/physics_render.cpp b/src/engine/physics/physics_render.cpp new file mode 100644 index 0000000..815d4d1 --- /dev/null +++ b/src/engine/physics/physics_render.cpp @@ -0,0 +1 @@ +#include "render/debugrender.h" diff --git a/src/engine/physics/physics_render.h b/src/engine/physics/physics_render.h new file mode 100644 index 0000000..6686397 --- /dev/null +++ b/src/engine/physics/physics_render.h @@ -0,0 +1,5 @@ +#ifndef PHYSICSDEBUGDRAW_H +#define PHYSICSDEBUGDRAW_H + + +#endif \ No newline at end of file diff --git a/src/engine/physics/physics_world.cpp b/src/engine/physics/physics_world.cpp new file mode 100644 index 0000000..e01b2a8 --- /dev/null +++ b/src/engine/physics/physics_world.cpp @@ -0,0 +1,171 @@ +#include +#include "utils/maths.h" +#include "render/debugrender.h" +#include "physics_world.h" +#include "physics_render.h" +#include "physics_object.h" +#include "game/game_object.h" + +#include + +static void odeNearCollisionCallback(void* _this, dGeomID id1, dGeomID id2) +{ + PhysicsWorld* physWorld = (PhysicsWorld*)_this; + return physWorld->NearCollisionCallback(id1, id2); +} + +PhysicsWorld* g_PhysicsWorld = nullptr; + +PhysicsWorld::PhysicsWorld() +{ + m_dynamicsWorld = nullptr; + m_space = nullptr; +} + +PhysicsWorld::~PhysicsWorld() +{ +} + +void PhysicsWorld::Create() +{ + m_dynamicsWorld = dWorldCreate(); + m_space = dHashSpaceCreate(0); +} + +void PhysicsWorld::Destroy() +{ + dSpaceDestroy(m_space); + dWorldDestroy(m_dynamicsWorld); +} + +void PhysicsWorld::Update(float deltaTime) +{ + dSpaceCollide(m_space, this, &odeNearCollisionCallback); + dWorldQuickStep(m_dynamicsWorld, 1); + + + //dWorldStep(m_dynamicsWorld, 0.05); + //dWorldQuickStep(m_dynamicsWorld, stepSize); +} + +void PhysicsWorld::DebugDraw() +{ + for (int i = 0; i < m_objects.size(); i++) + { + PhysicsObject* obj = m_objects[i]; + if (dGeomID geom = obj->GetShape()) + { + int type = dGeomGetClass(geom); + switch(type) + { + + //case dSphereClass: + //case dBoxClass: + //case dTriMeshClass: + //{ + // //float rad = dGeomSphereGetRadius(geom); + // //GLUquadric* q = gluNewQuadric(); + // //gluSphere(q, rad, 8, 8); break; + //} + case dBoxClass: + { + + } + break; + + case dTriMeshClass: + { + // not supported yet + } + break; + case dSphereClass: + { + float rad = dGeomSphereGetRadius(geom); + + glm::mat4 m = obj->GetEntity()->GetModelMatrix(); + m = glm::scale(m, glm::vec3(RadtoDeg(rad))); + + g_debugRender->DrawEllipse(m, Vec3(0.5f, 0.5f, 1.0f)); + } + break; + + default: + printf(" UNKNOWN OBJECT CLASS!\n"); + break; + } + } + } +} + +dWorldID PhysicsWorld::GetWorld() +{ + return m_dynamicsWorld; +} + +dSpaceID PhysicsWorld::GetSpace() +{ + return m_space; +} + +void PhysicsWorld::AddObject(PhysicsObject* object) +{ + std::vector::iterator it = std::find( + m_objects.begin(), + m_objects.end(), + object + ); + + assert(it == m_objects.end()); + + m_objects.push_back(object); +} + +void PhysicsWorld::RemoveObject(PhysicsObject* object) +{ + std::vector::iterator it = std::find( + m_objects.begin(), + m_objects.end(), + object + ); + + assert(it != m_objects.end()); + + m_objects.erase(it); +} + +void PhysicsWorld::SetGravity(const Vec3& gravityVector) +{ + dWorldSetGravity(m_dynamicsWorld, gravityVector.x, gravityVector.y, gravityVector.z); +} + +Vec3 PhysicsWorld::GetGravity() +{ + dVector3 g; + dWorldGetGravity(m_dynamicsWorld, g); + return Vec3(g[0], g[1], g[2]); +} + +void PhysicsWorld::NearCollisionCallback(dGeomID id1, dGeomID id2) +{ +} + +void PhysicsInit() +{ + dInitODE2(0); + + g_PhysicsWorld = new PhysicsWorld(); + g_PhysicsWorld->Create(); +} + +void PhysicsShutdown() +{ + if (g_PhysicsWorld) + { + g_PhysicsWorld->Destroy(); + + delete g_PhysicsWorld; + g_PhysicsWorld = nullptr; + } + + dCloseODE(); +} diff --git a/src/engine/physics/physics_world.h b/src/engine/physics/physics_world.h new file mode 100644 index 0000000..929c0e0 --- /dev/null +++ b/src/engine/physics/physics_world.h @@ -0,0 +1,46 @@ +#ifndef PHYSICS_WORLD_H +#define PHYSICS_WORLD_H + +#include + +#define dDOUBLE +#include + +class PhysicsObject; + +class PhysicsWorld +{ +public: + PhysicsWorld(); + ~PhysicsWorld(); + + void Create(); + void Destroy(); + + void Update(float deltaTime); + void DebugDraw(); + + dWorldID GetWorld(); + dSpaceID GetSpace(); + + void AddObject(PhysicsObject* object); + void RemoveObject(PhysicsObject* object); + + void SetGravity(const Vec3& gravityVector); + Vec3 GetGravity(); + + void NearCollisionCallback(dGeomID id1, dGeomID id2); + +private: + std::vector m_objects; + + dWorldID m_dynamicsWorld; + dSpaceID m_space; +}; + +extern PhysicsWorld* g_PhysicsWorld; + +void PhysicsInit(); +void PhysicsShutdown(); + +#endif // !PHYSICS_WORLD_H diff --git a/src/engine/render/color.h b/src/engine/render/color.h new file mode 100644 index 0000000..887c4f3 --- /dev/null +++ b/src/engine/render/color.h @@ -0,0 +1,52 @@ +#ifndef _COLOR_H_ +#define _COLOR_H_ +#pragma once + +//#define MAKE_ARGB(a,r,g,b) \ +// ((uint32_t)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) +//#define MAKE_RGBA(r,g,b,a) MAKE_ARGB(a,r,g,b) +//#define MAKE_XRGB(r,g,b) MAKE_ARGB(0xff,r,g,b) +// +//#define GET_A(argb) (((argb&0xff000000)>>24)&0xff) +//#define GET_R(argb) (((argb&0x00ff0000)>>16)&0xff) +//#define GET_G(argb) (((argb&0x0000ff00)>>8)&0xff) +//#define GET_B(argb) ((argb&0x000000ff)&0xff) +//#define GET_COLOR(argb) \ +// (Color({\ +// GET_A(argb),\ +// GET_R(argb),\ +// GET_G(argb),\ +// GET_B(argb)\ +// })) +// +//namespace BasicColor +//{ +// enum EBasicColor +// { +// eWhite = MAKE_XRGB(255, 255, 255), +// eBlack = MAKE_XRGB(0, 0, 0), +// eRed = MAKE_XRGB(255, 0, 0), +// eGreen = MAKE_XRGB(0, 255, 0), +// eBlue = MAKE_XRGB(0, 0, 255), +// eYellow = MAKE_XRGB(255, 255, 0), +// eCyan = MAKE_XRGB(0, 255, 255), +// eMagenta = MAKE_XRGB(255, 0, 255) +// }; +//}; + +struct Color +{ + union + { + struct + { + uint8_t a; + uint8_t r; + uint8_t g; + uint8_t b; + }; + uint32_t argb; + }; +}; + +#endif diff --git a/src/engine/render/debugrender.cpp b/src/engine/render/debugrender.cpp new file mode 100644 index 0000000..4823587 --- /dev/null +++ b/src/engine/render/debugrender.cpp @@ -0,0 +1,341 @@ +#include "render/debugrender.h" +#include "render/render_shared.h" +#include "render/render.h" +#include "render/renderdevice.h" +#include "render/vertexbuffer.h" +#include "render/shadersystem.h" + +#include "glad/glad.h" + +struct DebugVertex +{ + Vec3 position; + Vec3 color; +}; + +static InputLayoutDesc_t g_debugRenderLayout[] = +{ + { VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION }, + { VERTEXATTR_VEC3, SHADERSEMANTIC_COLOR } + +}; + +const int kEllipseVertexBufferSize = 114; +const int kEllipseIndexBufferSize = 672; + +void FillVertexBuffer( VertexBuffer* _pVertexBuffer, const Vec3& _Color ) +{ + static Vec3 previousColor = Vec3(0.0f, 0.0f, 0.0f); + + if (previousColor.x == _Color.x && + previousColor.y == _Color.y && + previousColor.z == _Color.z) + return; + + float vertices[] = + { + 0.0000f,0.0000f,1.0000f, 0.0000f,0.3827f,0.9239f, -0.1464f,0.3536f,0.9239f, + -0.2706f,0.2706f,0.9239f, -0.3536f,0.1464f,0.9239f, -0.3827f,0.0000f,0.9239f, + -0.3536f,-0.1464f,0.9239f, -0.2706f,-0.2706f,0.9239f, -0.1464f,-0.3536f,0.9239f, + 0.0000f,-0.3827f,0.9239f, 0.1464f,-0.3536f,0.9239f, 0.2706f,-0.2706f,0.9239f, + 0.3536f,-0.1464f,0.9239f, 0.3827f,0.0000f,0.9239f, 0.3536f,0.1464f,0.9239f, + 0.2706f,0.2706f,0.9239f, 0.1464f,0.3536f,0.9239f, 0.0000f,0.7071f,0.7071f, + -0.2706f,0.6533f,0.7071f, -0.5000f,0.5000f,0.7071f, -0.6533f,0.2706f,0.7071f, + -0.7071f,0.0000f,0.7071f, -0.6533f,-0.2706f,0.7071f, -0.5000f,-0.5000f,0.7071f, + -0.2706f,-0.6533f,0.7071f, 0.0000f,-0.7071f,0.7071f, 0.2706f,-0.6533f,0.7071f, + 0.5000f,-0.5000f,0.7071f, 0.6533f,-0.2706f,0.7071f, 0.7071f,0.0000f,0.7071f, + 0.6533f,0.2706f,0.7071f, 0.5000f,0.5000f,0.7071f, 0.2706f,0.6533f,0.7071f, + 0.0000f,0.9239f,0.3827f, -0.3536f,0.8536f,0.3827f, -0.6533f,0.6533f,0.3827f, + -0.8536f,0.3536f,0.3827f, -0.9239f,0.0000f,0.3827f, -0.8536f,-0.3536f,0.3827f, + -0.6533f,-0.6533f,0.3827f, -0.3536f,-0.8536f,0.3827f, 0.0000f,-0.9239f,0.3827f, + 0.3536f,-0.8536f,0.3827f, 0.6533f,-0.6533f,0.3827f, 0.8536f,-0.3536f,0.3827f, + 0.9239f,0.0000f,0.3827f, 0.8536f,0.3536f,0.3827f, 0.6533f,0.6533f,0.3827f, + 0.3536f,0.8536f,0.3827f, 0.0000f,1.0000f,0.0000f, -0.3827f,0.9239f,0.0000f, + -0.7071f,0.7071f,0.0000f, -0.9239f,0.3827f,0.0000f, -1.0000f,0.0000f,0.0000f, + -0.9239f,-0.3827f,0.0000f, -0.7071f,-0.7071f,0.0000f, -0.3827f,-0.9239f,0.0000f, + 0.0000f,-1.0000f,0.0000f, 0.3827f,-0.9239f,0.0000f, 0.7071f,-0.7071f,0.0000f, + 0.9239f,-0.3827f,0.0000f, 1.0000f,0.0000f,0.0000f, 0.9239f,0.3827f,0.0000f, + 0.7071f,0.7071f,0.0000f, 0.3827f,0.9239f,0.0000f, 0.0000f,0.9239f,-0.3827f, + -0.3536f,0.8536f,-0.3827f, -0.6533f,0.6533f,-0.3827f, -0.8536f,0.3536f,-0.3827f, + -0.9239f,0.0000f,-0.3827f, -0.8536f,-0.3536f,-0.3827f, -0.6533f,-0.6533f,-0.3827f, + -0.3536f,-0.8536f,-0.3827f, 0.0000f,-0.9239f,-0.3827f, 0.3536f,-0.8536f,-0.3827f, + 0.6533f,-0.6533f,-0.3827f, 0.8536f,-0.3536f,-0.3827f, 0.9239f,0.0000f,-0.3827f, + 0.8536f,0.3536f,-0.3827f, 0.6533f,0.6533f,-0.3827f, 0.3536f,0.8536f,-0.3827f, + 0.0000f,0.7071f,-0.7071f, -0.2706f,0.6533f,-0.7071f, -0.5000f,0.5000f,-0.7071f, + -0.6533f,0.2706f,-0.7071f, -0.7071f,0.0000f,-0.7071f, -0.6533f,-0.2706f,-0.7071f, + -0.5000f,-0.5000f,-0.7071f, -0.2706f,-0.6533f,-0.7071f, 0.0000f,-0.7071f,-0.7071f, + 0.2706f,-0.6533f,-0.7071f, 0.5000f,-0.5000f,-0.7071f, 0.6533f,-0.2706f,-0.7071f, + 0.7071f,0.0000f,-0.7071f, 0.6533f,0.2706f,-0.7071f, 0.5000f,0.5000f,-0.7071f, + 0.2706f,0.6533f,-0.7071f, 0.0000f,0.3827f,-0.9239f, -0.1464f,0.3536f,-0.9239f, + -0.2706f,0.2706f,-0.9239f, -0.3536f,0.1464f,-0.9239f, -0.3827f,0.0000f,-0.9239f, + -0.3536f,-0.1464f,-0.9239f, -0.2706f,-0.2706f,-0.9239f, -0.1464f,-0.3536f,-0.9239f, + 0.0000f,-0.3827f,-0.9239f, 0.1464f,-0.3536f,-0.9239f, 0.2706f,-0.2706f,-0.9239f, + 0.3536f,-0.1464f,-0.9239f, 0.3827f,0.0000f,-0.9239f, 0.3536f,0.1464f,-0.9239f, + 0.2706f,0.2706f,-0.9239f, 0.1464f,0.3536f,-0.9239f, 0.0000f,0.0000f,-1.0000f + }; + + const int vcnt = sizeof(vertices) / (sizeof(float) * 3); + DebugVertex verts[vcnt]; + for (int i = 0; i < vcnt; i++) { + int k = i * 3; + + verts[i].position.x = vertices[k]; + verts[i].position.y = vertices[k + 1]; + verts[i].position.z = vertices[k + 2]; + verts[i].color = _Color; + } + + DebugVertex* pAGPMemory = ( DebugVertex* )_pVertexBuffer->MapBuffer( BufferAccess::WRITE_ONLY ); + memcpy( pAGPMemory, verts, sizeof( verts ) ); + _pVertexBuffer->UnmapBuffer(); + + previousColor = _Color; +} + +IndexBuffer* CreateEllipseIndexBuffer() +{ + uint16_t faces[224 * 3] = + { + 0,1,2, 0,2,3, 0,3,4, 0,4,5, 0,5,6, 0,6,7, 0,7,8, 0,8,9, 0,9,10, + 0,10,11, 0,11,12, 0,12,13, 0,13,14, 0,14,15, 0,15,16, 0,16,1, 1,17,18, 1,18,2, + 2,18,19, 2,19,3, 3,19,20, 3,20,4, 4,20,21, 4,21,5, 5,21,22, 5,22,6, 6,22,23, + 6,23,7, 7,23,24, 7,24,8, 8,24,25, 8,25,9, 9,25,26, 9,26,10, 10,26,27, 10,27,11, + 11,27,28, 11,28,12, 12,28,29, 12,29,13, 13,29,30, 13,30,14, 14,30,31, 14,31,15, 15,31,32, + 15,32,16, 16,32,17, 16,17,1, 17,33,34, 17,34,18, 18,34,35, 18,35,19, 19,35,36, 19,36,20, + 20,36,37, 20,37,21, 21,37,38, 21,38,22, 22,38,39, 22,39,23, 23,39,40, 23,40,24, 24,40,41, + 24,41,25, 25,41,42, 25,42,26, 26,42,43, 26,43,27, 27,43,44, 27,44,28, 28,44,45, 28,45,29, + 29,45,46, 29,46,30, 30,46,47, 30,47,31, 31,47,48, 31,48,32, 32,48,33, 32,33,17, 33,49,50, + 33,50,34, 34,50,51, 34,51,35, 35,51,52, 35,52,36, 36,52,53, 36,53,37, 37,53,54, 37,54,38, + 38,54,55, 38,55,39, 39,55,56, 39,56,40, 40,56,57, 40,57,41, 41,57,58, 41,58,42, 42,58,59, + 42,59,43, 43,59,60, 43,60,44, 44,60,61, 44,61,45, 45,61,62, 45,62,46, 46,62,63, 46,63,47, + 47,63,64, 47,64,48, 48,64,49, 48,49,33, 49,65,66, 49,66,50, 50,66,67, 50,67,51, 51,67,68, + 51,68,52, 52,68,69, 52,69,53, 53,69,70, 53,70,54, 54,70,71, 54,71,55, 55,71,72, 55,72,56, + 56,72,73, 56,73,57, 57,73,74, 57,74,58, 58,74,75, 58,75,59, 59,75,76, 59,76,60, 60,76,77, + 60,77,61, 61,77,78, 61,78,62, 62,78,79, 62,79,63, 63,79,80, 63,80,64, 64,80,65, 64,65,49, + 65,81,82, 65,82,66, 66,82,83, 66,83,67, 67,83,84, 67,84,68, 68,84,85, 68,85,69, 69,85,86, + 69,86,70, 70,86,87, 70,87,71, 71,87,88, 71,88,72, 72,88,89, 72,89,73, 73,89,90, 73,90,74, + 74,90,91, 74,91,75, 75,91,92, 75,92,76, 76,92,93, 76,93,77, 77,93,94, 77,94,78, 78,94,95, + 78,95,79, 79,95,96, 79,96,80, 80,96,81, 80,81,65, 81,97,98, 81,98,82, 82,98,99, 82,99,83, + 83,99,100, 83,100,84, 84,100,101, 84,101,85, 85,101,102, 85,102,86, 86,102,103, 86,103,87, 87,103,104, + 87,104,88, 88,104,105, 88,105,89, 89,105,106, 89,106,90, 90,106,107, 90,107,91, 91,107,108, 91,108,92, + 92,108,109, 92,109,93, 93,109,110, 93,110,94, 94,110,111, 94,111,95, 95,111,112, 95,112,96, 96,112,97, + 96,97,81, 113,98,97, 113,99,98, 113,100,99, 113,101,100, 113,102,101, 113,103,102, 113,104,103, 113,105,104, + 113,106,105, 113,107,106, 113,108,107, 113,109,108, 113,110,109, 113,111,110, 113,112,111, 113,97,112 + }; + + return g_renderDevice->CreateIndexBuffer(faces, sizeof(faces)); +} + +bool g_drawDebug = false; +DebugRender* g_debugRender; + +DebugRender::DebugRender() +{ + m_verticesBuffer = nullptr; + m_ellipseVertexBuffer = nullptr; + m_ellipseIndexBuffer = nullptr; + m_shader = nullptr; +} + +DebugRender::~DebugRender() +{ + m_verticesBuffer = nullptr; + m_shader = nullptr; +} + +void DebugRender::Initialize() +{ + float points[12]; + m_verticesBuffer = g_renderDevice->CreateVertexBuffer(points, sizeof(points), true); + + m_ellipseVertexBuffer = g_renderDevice->CreateVertexBuffer(nullptr, kEllipseVertexBufferSize * sizeof(DebugVertex), true); + m_ellipseIndexBuffer = CreateEllipseIndexBuffer(); + + m_shader = g_shaderSystem->CreateShader("debug_draw", + "content/shaders/debug_draw.vs", + "content/shaders/debug_draw.ps", + g_debugRenderLayout, + sizeof(g_debugRenderLayout) / sizeof(g_debugRenderLayout[0])); + + m_shader->m_stride = sizeof(DebugVertex); + + glGenVertexArrays(1, &m_vao); +} + +void DebugRender::Shutdown() +{ + glDeleteVertexArrays(1, &m_vao); + + if (m_ellipseIndexBuffer) { + delete m_ellipseIndexBuffer; + m_ellipseIndexBuffer = nullptr; + } + + if (m_ellipseVertexBuffer) { + delete m_ellipseVertexBuffer; + m_ellipseVertexBuffer = nullptr; + } + + if (m_verticesBuffer) { + delete m_verticesBuffer; + m_verticesBuffer = nullptr; + } +} + +void DebugRender::DrawLine(const Vec3& from, const Vec3& to, const Vec3& color) +{ + if (!g_drawDebug) + return; + + Line line; + line.from = from; + line.color0 = color; + line.to = to; + line.color1 = color; + m_lines.push_back(line); +} + +void DebugRender::DrawBoundingBox(const BoundingBox& box, const glm::vec3& color) +{ + glm::vec3 from = glm::vec3(box.min.x, box.min.y, box.min.z); + glm::vec3 to = glm::vec3(box.max.x, box.max.y, box.max.z); + + glm::vec3 halfExtents = (to - from) * 0.5f; + glm::vec3 center = (to + from) * 0.5f; + int i, j; + + glm::vec3 edgecoord(1.f, 1.f, 1.f), pa, pb; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 3; j++) + { + pa = glm::vec3(edgecoord[0] * halfExtents[0], edgecoord[1] * halfExtents[1], + edgecoord[2] * halfExtents[2]); + pa += center; + + int othercoord = j % 3; + edgecoord[othercoord] *= -1.f; + pb = glm::vec3(edgecoord[0] * halfExtents[0], edgecoord[1] * halfExtents[1], + edgecoord[2] * halfExtents[2]); + pb += center; + + DrawLine(Vec3(pa.x, pa.y, pa.z), Vec3(pb.x, pb.y, pb.z) , Vec3(color.x, color.y, color.z) ); + } + + edgecoord = glm::vec3(-1.f, -1.f, -1.f); + if (i < 3) + edgecoord[i] *= -1.f; + } +} + +void DebugRender::DrawEllipse(const glm::mat4& trans, const Vec3& color) +{ + EllipseDrawCmd drawCmd = {}; + drawCmd.trans = trans; + drawCmd.color = color; + m_ellipseDrawCmd.push_back(drawCmd); +} + +void DebugRender::RenderFrame() +{ + if (!g_drawDebug) + return; + + BeginDraw(); + + // draw lines + DrawLinesInternal(); + + // and clear them + m_lines.clear(); + + // Draw cmds + DrawCmds(); + + // and clear them + m_ellipseDrawCmd.clear(); + + EndDraw(); +} + +void DebugRender::DrawAxis(const Vec3& vec) +{ + const float length = 0.2f; + DrawLine(vec, Vec3(vec.x + length, vec.y, vec.z), Vec3(1.0f, 0.0, 0.0f)); + DrawLine(vec, Vec3(vec.x, vec.y + length, vec.z), Vec3(0.0f, 0.0f, 1.0f)); + DrawLine(vec, Vec3(vec.x, vec.y, vec.z + length), Vec3(0.0f, 1.0f, 0.0f)); +} + +void DebugRender::BeginDraw() +{ + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (int*)&m_savedVao); + + glBindVertexArray(m_vao); +} + +void DebugRender::EndDraw() +{ + glBindVertexArray(m_savedVao); +} + +void DebugRender::DrawLinesInternal() +{ + if (m_lines.empty()) + return; + + g_renderDevice->SetDepthTest(true); + g_renderDevice->SetDepthWrite(true); + + g_renderDevice->SetVerticesBuffer(m_verticesBuffer); + + m_verticesBuffer->UpdateBuffer(m_lines.data(), m_lines.size() * sizeof(Line)); + + // Bind our shader + g_shaderSystem->SetShader(m_shader); + + // #TODO: Fix stupid bug, when we get very far from wireframe and lines can start cliping + View view = g_renderView; + view.proj[2][3] -= 0.0001f; + + glm::mat4 mv = glm::identity(); + mv = view.proj * view.view; + + g_shaderSystem->SetUniformMatrix(m_shader, UNIFORM_MVP_MATRIX, &mv[0]); + + // draw stuff + g_renderDevice->DrawArrays(PT_LINES, 0, m_lines.size() * 2); +} + +void DebugRender::DrawCmds() +{ + if (m_ellipseDrawCmd.empty()) + return; + + g_renderDevice->SetDepthTest(true); + g_renderDevice->SetDepthWrite(true); + + g_renderDevice->SetVerticesBuffer(m_ellipseVertexBuffer); + g_renderDevice->SetIndicesBuffer(m_ellipseIndexBuffer); + + // Bind our shader + g_shaderSystem->SetShader(m_shader); + + for (int i = 0; i < m_ellipseDrawCmd.size(); i++) + { + const EllipseDrawCmd& cmd = m_ellipseDrawCmd[i]; + + FillVertexBuffer(m_ellipseVertexBuffer, cmd.color); + + // #TODO: Fix stupid bug, when we get very far from wireframe and lines can start cliping + View view = g_renderView; + view.proj[2][3] -= 0.0001f; + + glm::mat4 mv = glm::identity(); + mv = view.proj * view.view * cmd.trans; + + g_shaderSystem->SetUniformMatrix(m_shader, UNIFORM_MVP_MATRIX, &mv[0]); + + // draw stuff + g_renderDevice->DrawElements( PT_LINES, 672, true ); + } +} diff --git a/src/engine/render/debugrender.h b/src/engine/render/debugrender.h new file mode 100644 index 0000000..829aa1f --- /dev/null +++ b/src/engine/render/debugrender.h @@ -0,0 +1,68 @@ +#ifndef DEBUGRENDER_H +#define DEBUGRENDER_H + +#include +#include + +class VertexBuffer; +class IndexBuffer; +class Shader; + +class DebugRender +{ +public: + DebugRender(); + ~DebugRender(); + + void Initialize(); + void Shutdown(); + + void DrawAxis(const Vec3& vec); + void DrawLine(const Vec3& from, const Vec3& to, const Vec3& color); + void DrawBoundingBox(const BoundingBox& box, const glm::vec3& color); + void DrawEllipse(const glm::mat4& trans, const Vec3& color); + + void RenderFrame(); + +private: + void BeginDraw(); + void EndDraw(); + + void DrawLinesInternal(); + void DrawCmds(); + +private: + // Primitives + struct Line + { + Vec3 from; + Vec3 color0; + + Vec3 to; + Vec3 color1; + }; + + struct EllipseDrawCmd + { + glm::mat4 trans; + Vec3 color; + }; + + std::vector m_lines; + std::vector m_ellipseDrawCmd; + +private: + VertexBuffer* m_verticesBuffer; + VertexBuffer* m_ellipseVertexBuffer; + IndexBuffer* m_ellipseIndexBuffer; + Shader* m_shader; + + unsigned int m_vao; + unsigned int m_savedVao; +}; + +extern DebugRender* g_debugRender; + +extern bool g_drawDebug; + +#endif // !DEBUGRENDER_H diff --git a/src/engine/render/font.cpp b/src/engine/render/font.cpp new file mode 100644 index 0000000..28984b3 --- /dev/null +++ b/src/engine/render/font.cpp @@ -0,0 +1,76 @@ +#include "utils/logger.h" +#include "render/font.h" +#include "render/shader.h" +#include "render/vertexbuffer.h" +#include "render/indexbuffer.h" +#include "render/renderdevice.h" +#include "render/shadersystem.h" +#include "render/ui.h" +#include "render/texture2d.h" +#include "render/texturesmanager.h" + +#include + +struct FontGlobals { + VertexBuffer* vb; + IndexBuffer* ib; + Shader* shader; + + UIVertex* vertices = nullptr; + uint16_t* indices = nullptr; + uint16_t count = 0; + uint16_t position = 0; + uint16_t Indexposition = 0; + uint16_t currentIdx = 0; +} g_font; + +void fontInit() +{ + // Create vertex and index buffer + + // same as ui + g_font.vb = g_renderDevice->CreateVertexBuffer( nullptr, MAX_UI_VERTICES, true ); + + // Create shader + + InputLayoutDesc_t inputLayout[] = + { + { VERTEXATTR_VEC2, SHADERSEMANTIC_POSITION }, + { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }, + { VERTEXATTR_VEC4, SHADERSEMANTIC_COLOR }, + }; + + g_font.shader = g_shaderSystem->CreateShader("ui_base", "content/shaders/ui_base.vs", "content/shaders/ui_base.ps", inputLayout, sizeof(inputLayout) / sizeof(inputLayout[0])); + g_font.shader->m_stride = sizeof( UIVertex ); +} + +void fontShutdown() +{ + delete g_font.ib; + delete g_font.vb; +} + +inline unsigned char toByteColor(const Vec4& color) +{ + +} + +void fontDrawString(const char* text, float x, float y, float w, float h, const Vec4& color) +{ + // lock buffers + + UIVertex* vertices = (UIVertex*)g_font.vb->MapBuffer(BufferAccess::WRITE_ONLY); + + //int num_quads = stb_easy_font_print(x, y, (char*)text, , buffer, sizeof(buffer)); + + //static char buffer[99999]; // ~500 chars + //int num_quads; + + //num_quads = stb_easy_font_print(x, y, (char*)text, NULL, buffer, sizeof(buffer)); + + //glColor3f(r, g, b); + //glEnableClientState(GL_VERTEX_ARRAY); + //glVertexPointer(2, GL_FLOAT, 16, buffer); + //glDrawArrays(GL_QUADS, 0, num_quads * 4); + //glDisableClientState(GL_VERTEX_ARRAY); +} diff --git a/src/engine/render/font.h b/src/engine/render/font.h new file mode 100644 index 0000000..cefcb59 --- /dev/null +++ b/src/engine/render/font.h @@ -0,0 +1,10 @@ +#ifndef FONT_H +#define FONT_H + +#include + +void fontInit(); +void fontShutdown(); +void fontDrawString(const char* text, float x, float y, float w, float h, const Vec4& color); + +#endif // !FONT_H diff --git a/src/engine/render/gl_shared.cpp b/src/engine/render/gl_shared.cpp new file mode 100644 index 0000000..3df13ec --- /dev/null +++ b/src/engine/render/gl_shared.cpp @@ -0,0 +1,44 @@ +#include "utils/logger.h" +#include "render/gl_shared.h" + +const char *GL_ErrorString( int err ) +{ + switch( err ) + { + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "UNKNOWN ERROR"; + } +} + +void GL_CheckError() +{ + GLenum err; + if ( (err = glGetError()) != GL_NO_ERROR ) + Msg( "OpenGL Error: %s", GL_ErrorString( err ) );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target)); +} + +void GL_CheckErrorEx( const char* filename, int line ) +{ + GLenum err; + if ( (err = glGetError()) != GL_NO_ERROR ) + Msg( "OpenGL Error: %s at %s:%i", GL_ErrorString( err ), filename, line );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target)); +} + +void GL_CheckErrorFunction(const char* expression, const char* filename, int line) +{ + GLenum err; + if ( (err = glGetError()) != GL_NO_ERROR ) + Msg( "OpenGL Error: %s (%s) at %s:%i", expression, GL_ErrorString( err ), filename, line );// Msg("OpenGL Error: %s [%s]\n", GL_ErrorString(err), GL_TargetToString(tex->target)); +} \ No newline at end of file diff --git a/src/engine/render/gl_shared.h b/src/engine/render/gl_shared.h new file mode 100644 index 0000000..0890a70 --- /dev/null +++ b/src/engine/render/gl_shared.h @@ -0,0 +1,17 @@ +#ifndef GL_SHARED_H +#define GL_SHARED_H + +#include + +void GL_CheckError(); +void GL_CheckErrorEx(const char* filename, int line); +void GL_CheckErrorFunction(const char* expression, const char* filename, int line); + +#define GL_CHECK_ERROR() \ + GL_CheckErrorEx(__FILE__, __LINE__) + +#define GL_CHECK_FUNC_ERROR(expr) \ + expr; \ + GL_CheckErrorFunction(#expr, __FILE__, __LINE__) + +#endif // !GL_SHARED_H diff --git a/src/engine/render/indexbuffer.cpp b/src/engine/render/indexbuffer.cpp new file mode 100644 index 0000000..9ea763b --- /dev/null +++ b/src/engine/render/indexbuffer.cpp @@ -0,0 +1,56 @@ +#include "render/indexbuffer.h" +#include "render/gl_shared.h" + +IndexBuffer::IndexBuffer(void* data, size_t size, bool isStream /*= false*/) +{ + glGenBuffers(1, &m_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, isStream ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + //if (isStream) { + // Logger::msg("created dynamic index stream ..."); + //} +} + +IndexBuffer::~IndexBuffer() +{ + glDeleteBuffers(1, &m_buffer); +} + +void IndexBuffer::Bind() +{ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer); +} + +void* IndexBuffer::MapBuffer(BufferAccess access) +{ + GLenum accessGl = 0; + + switch (access) + { + case BufferAccess::READ_ONLY: + accessGl = GL_READ_ONLY; + break; + case BufferAccess::WRITE_ONLY: + accessGl = GL_WRITE_ONLY; + break; + case BufferAccess::READ_WRITE: + accessGl = GL_READ_WRITE; + break; + } + + Bind(); + + void* ptr = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, accessGl); + + //check_for_opengl_error(); + + return ptr; +} + +void IndexBuffer::UnmapBuffer() +{ + Bind(); + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); +} diff --git a/src/engine/render/indexbuffer.h b/src/engine/render/indexbuffer.h new file mode 100644 index 0000000..bdaf4e6 --- /dev/null +++ b/src/engine/render/indexbuffer.h @@ -0,0 +1,26 @@ +#ifndef INDEXOBJECT_H +#define INDEXOBJECT_H + +#include +#include "render/render_shared.h" + +class RenderDevice; + +class IndexBuffer +{ + friend class RenderDevice; +public: + ~IndexBuffer(); + + void* MapBuffer(BufferAccess access); + void UnmapBuffer(); + + void Bind(); + +private: + IndexBuffer(void* data, size_t size, bool isStream = false); + + uint32_t m_buffer; +}; + +#endif // !INDEXOBJECT_H diff --git a/src/engine/render/modelmanager.cpp b/src/engine/render/modelmanager.cpp new file mode 100644 index 0000000..8f41800 --- /dev/null +++ b/src/engine/render/modelmanager.cpp @@ -0,0 +1,373 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ModelManager* g_modelManager = nullptr; + +static InputLayoutDesc_t g_staticVertexLayout[] = { + { VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION }, + { VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL }, + { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }, +}; + +static InputLayoutDesc_t g_skinnedVertexLayout[] = { + { VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION }, + { VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL }, + { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD } +}; + +Shader* g_litShader = nullptr; + +ModelManager::ModelManager() +{ +} + +ModelManager::~ModelManager() +{ +} + +VertexBuffer* ModelManager::CreateVertexBuffer( void* data, int size ) +{ + // create vertex buffer + VertexBuffer* vb = g_renderDevice->CreateVertexBuffer( data, size ); + + return vb; +} + +IndexBuffer* ModelManager::CreateIndexBuffer( void* data, int size ) +{ + // create index buffer + IndexBuffer* ib = g_renderDevice->CreateIndexBuffer( data, size ); + + return ib; +} + +Model* ModelManager::LoadModel(const char* filename) +{ + auto it = std::find_if(m_models.begin(), m_models.end(), [=](const ModelEntry& entry) { return strcmp(entry.filename, filename) == 0; }); + if (it != m_models.end()) + { + return it->model; + } + + if (!g_fileManager->FileExist(filename)) + { + Logger::logPrint("ModelManager::LoadModel: File '%s' not exist.", filename); + return nullptr; + } + + Model* model = new Model(); + + if (strstr(filename, ".obj")) + { + model->LoadObj(filename); + } + else + { + Logger::logPrint("ModelManager::LoadModel(%s): Unknowed file format '%s'", + filename, fs::getFileExtension(filename).c_str()); + + return nullptr; + } + + ModelEntry entry = {}; + strcpy(entry.filename, filename); + entry.model = model; + + m_models.push_back(entry); + + Logger::logPrint("ModelManager::LoadModel: Loaded '%s'", filename); + + return model; +} + +Model::Model() +{ + m_data.vb = nullptr; + m_data.ib = nullptr; + m_AlbedoTexture = nullptr; + + m_boundingBox.min = glm::vec3(0.0f); + m_boundingBox.max = glm::vec3(0.0f); +} + +Model::~Model() +{ + m_AlbedoTexture = nullptr; + + if (m_data.vb) + { + delete m_data.vb; + m_data.vb = nullptr; + } +} + +void Model::LoadObj( const char* filename ) +{ + Msg( "Loading OBJ file %s...", filename ); + + std::vector vertexIndices, uvIndices, normalIndices; + std::vector temp_vertices; + std::vector temp_uvs; + std::vector temp_normals; + + std::vector out_vertices; + std::vector out_uvs; + std::vector out_normals; + + FILE* file = fopen(filename, "r"); + if (file == NULL) { + Msg("Model::LoadObj: Impossible to open the file !"); + return; + } + + while (1) { + + char lineHeader[128]; + // read the first word of the line + int res = fscanf(file, "%s", lineHeader); + if (res == EOF) + break; // EOF = End Of File. Quit the loop. + + // else : parse lineHeader + + if (strcmp(lineHeader, "v") == 0) { + glm::vec3 vertex; + fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z); + temp_vertices.push_back(vertex); + } + else if (strcmp(lineHeader, "vt") == 0) { + glm::vec2 uv; + fscanf(file, "%f %f\n", &uv.x, &uv.y); + uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders. + temp_uvs.push_back(uv); + } + else if (strcmp(lineHeader, "vn") == 0) { + glm::vec3 normal; + fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z); + temp_normals.push_back(normal); + } + else if (strcmp(lineHeader, "f") == 0) { + std::string vertex1, vertex2, vertex3; + unsigned int vertexIndex[3], uvIndex[3], normalIndex[3]; + int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]); + if (matches != 9) { + Msg("Model::LoadObj: File can't be read by our simple parser :-( Try exporting with other options"); + fclose(file); + return; + } + vertexIndices.push_back(vertexIndex[0]); + vertexIndices.push_back(vertexIndex[1]); + vertexIndices.push_back(vertexIndex[2]); + uvIndices.push_back(uvIndex[0]); + uvIndices.push_back(uvIndex[1]); + uvIndices.push_back(uvIndex[2]); + normalIndices.push_back(normalIndex[0]); + normalIndices.push_back(normalIndex[1]); + normalIndices.push_back(normalIndex[2]); + } + else { + // Probably a comment, eat up the rest of the line + char stupidBuffer[1000]; + fgets(stupidBuffer, 1000, file); + } + + } + + // For each vertex of each triangle + for (unsigned int i = 0; i < vertexIndices.size(); i++) { + + // Get the indices of its attributes + unsigned int vertexIndex = vertexIndices[i]; + unsigned int uvIndex = uvIndices[i]; + unsigned int normalIndex = normalIndices[i]; + + // Get the attributes thanks to the index + glm::vec3 vertex = temp_vertices[vertexIndex - 1]; + glm::vec2 uv = temp_uvs[uvIndex - 1]; + glm::vec3 normal = temp_normals[normalIndex - 1]; + + // Put the attributes in buffers + out_vertices.push_back(vertex); + out_uvs.push_back(uv); + out_normals.push_back(normal); + + } + + fclose(file); + + // Combine in to the one array + std::vector vertices; + for (unsigned int i = 0; i < vertexIndices.size(); i++) + { + // Get the indices of its attributes + unsigned int vertexIndex = vertexIndices[i]; + unsigned int uvIndex = uvIndices[i]; + unsigned int normalIndex = normalIndices[i]; + + // Get the attributes thanks to the index + glm::vec3 vertex = temp_vertices[vertexIndex - 1]; + glm::vec2 uv = temp_uvs[uvIndex - 1]; + glm::vec3 normal = temp_normals[normalIndex - 1]; + + StaticMeshVertex vtx = {}; + vtx.position =vertex; + vtx.normal = normal; + vtx.texcoord = uv; + vertices.push_back(vtx); + + m_boundingBox.min = glm::min(m_boundingBox.min, vertex); + m_boundingBox.max = glm::max(m_boundingBox.max, vertex); + } + + m_data.vb = g_modelManager->CreateVertexBuffer(vertices.data(), (int)sizeof(StaticMeshVertex) * (int)vertices.size()); + m_data.vbcount = vertices.size(); + + std::string mtlfilename = fs::getFileNameWithoutExtension(filename); + mtlfilename += ".mtl"; + LoadMtl(mtlfilename.c_str()); +} + +void Model::LoadMtl( const char* filename ) +{ + Msg("Loading MTL file %s...", filename); + + FILE* file = fopen(filename, "r"); + if (file == NULL) { + Msg("Model::LoadObj: Impossible to open the file !"); + return; + } + + while (1) { + + char lineHeader[128]; + // read the first word of the line + int res = fscanf(file, "%s", lineHeader); + if (res == EOF) + break; // EOF = End Of File. Quit the loop. + + if (strcmp(lineHeader, "map_Kd") == 0) { + char stupidBuffer[1000]; + fgets(stupidBuffer, 1000, file); + + const char* textureFilename = stupidBuffer + 1; + m_AlbedoTexture = g_texturesManager->LoadTexture2D(textureFilename, true); + } + + //if (strcmp(lineHeader, "v") == 0) { + // glm::vec3 vertex; + // fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z); + // temp_vertices.push_back(vertex); + //} + //else { + // // Probably a comment, eat up the rest of the line + // char stupidBuffer[1000]; + // fgets(stupidBuffer, 1000, file); + //} + + } + + fclose(file); +} + +void Model::LoadIQM( const char* filename ) +{ +} + +void Model::Draw( const glm::mat4& model, bool isTransparent /*= false*/ ) +{ + if (!g_litShader) + { + g_litShader = g_shaderSystem->CreateShader("lit_generic", "content/shaders/lit_generic.vs", "content/shaders/lit_generic.ps", + g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0])); + + g_litShader->m_stride = sizeof(StaticMeshVertex); + } + + g_renderDevice->SetDepthTest(true); + g_renderDevice->SetDepthWrite(true); + + if (isTransparent) + { + // Enable blending + g_renderDevice->SetBlending(true); + g_renderDevice->SetBlendingFunction(BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA); + + glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, .5f); + g_shaderSystem->SetUniformFloat4( g_litShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr( color ) ); + } + else + { + g_renderDevice->SetBlending(false); + + glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, 1.f); + g_shaderSystem->SetUniformFloat4( g_litShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr( color ) ); + } + + g_renderDevice->SetVerticesBuffer(m_data.vb); + + g_shaderSystem->SetShader(g_litShader); + g_shaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MODEL_MATRIX, &model[0] ); + + //static float test = 0.0f; + //test += g_systemTimer->GetDelta() * 6.0f; + + //glm::mat4 model = glm::mat4(1.0f); + + //int32_t x = 0, y = 0; + //g_inputSystem->GetMousePosition(&x, &y); + // + //glm::mat4 model = glm::mat4(1.0f); + //model = glm::rotate(model, test, glm::vec3(0.0f, 1.0f, 0.0f)); + // + //float realY = (float)g_renderView.height - (float)y - 1; + // + //glm::vec4 viewport = glm::vec4(0.0f, 0.0f, (float)g_renderView.width, (float)g_renderView.height); + //glm::vec3 pos = glm::unProject( + // glm::vec3((float)x, realY, 0.0f), + // model, + // g_renderView.proj, + // viewport); + // + //pos *= 50.0f; + // + //model = glm::translate(model, pos); + + glm::mat4 mvp = glm::identity(); + mvp = g_renderView.proj * g_renderView.view * model; + g_shaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MVP_MATRIX, &mvp[0]); + + g_texturesManager->SetTexture( 0, m_AlbedoTexture ); + g_shaderSystem->SetUniformSampler(g_litShader, SAMPLER_ALBEDO, 0); + + g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount); +} + +void ReleaseModelData(ModelData_t& data) +{ + if (data.ib) + { + delete data.ib; + data.ib = nullptr; + } + + if (data.vb) + { + delete data.vb; + data.vb = nullptr; + } +} diff --git a/src/engine/render/modelmanager.h b/src/engine/render/modelmanager.h new file mode 100644 index 0000000..3e867d3 --- /dev/null +++ b/src/engine/render/modelmanager.h @@ -0,0 +1,75 @@ +#ifndef MODELMANAGER_H +#define MODELMANAGER_H + +#include + +#include + +class VertexBuffer; +class IndexBuffer; +class Texture2D; + +struct StaticMeshVertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 texcoord; +}; + +struct ModelData_t +{ + VertexBuffer* vb; + IndexBuffer* ib; + uint32_t vbcount; + uint32_t ibcount; +}; + +void ReleaseModelData(ModelData_t& data); + +class Model +{ +public: + Model(); + ~Model(); + + void LoadObj( const char* filename ); + void LoadMtl( const char* filename ); + + void LoadIQM( const char* filename ); + + void Draw( const glm::mat4& model, bool isTransparent = false ); + + BoundingBox GetBoundingBox() { return m_boundingBox; } + +private: + ModelData_t m_data; + BoundingBox m_boundingBox; + Texture2D* m_AlbedoTexture; +}; + +class ModelManager +{ +public: + ModelManager(); + ~ModelManager(); + + // Buffer creation + VertexBuffer* CreateVertexBuffer( void* data, int size ); + IndexBuffer* CreateIndexBuffer( void* data, int size ); + + Model* LoadModel(const char* filename); + +private: + struct ModelEntry + { + char filename[260]; + Model* model; + }; + + std::vector< ModelEntry > m_models; + +}; + +extern ModelManager* g_modelManager; + +#endif // !MODELMANAGER_H diff --git a/src/engine/render/render.cpp b/src/engine/render/render.cpp new file mode 100644 index 0000000..bbcc2c5 --- /dev/null +++ b/src/engine/render/render.cpp @@ -0,0 +1,396 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#pragma comment(lib, "OpenGL32.lib") + +View g_renderView; +Render* g_render = NULL; +HDC g_hDC = NULL; +HGLRC g_openglContext = NULL; + +GLuint g_vao = 0; + +void CreateOpenGL( HWND hWnd ) +{ + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags + PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. + 32, // Colordepth of the framebuffer. + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 24, // Number of bits for the depthbuffer + 8, // Number of bits for the stencilbuffer + 0, // Number of Aux buffers in the framebuffer. + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + g_hDC = GetDC( hWnd ); + int pixelFormat = ChoosePixelFormat( g_hDC, &pfd ); + SetPixelFormat( g_hDC, pixelFormat, &pfd ); + + HGLRC tempContext = wglCreateContext( g_hDC ); + wglMakeCurrent( g_hDC, tempContext ); + + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); + assert(wglCreateContextAttribsARB); + + int attribs[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 2, + WGL_CONTEXT_FLAGS_ARB, 0, + 0 + }; + + g_openglContext = wglCreateContextAttribsARB( g_hDC, 0, attribs ); + wglMakeCurrent( NULL, NULL ); + wglDeleteContext( tempContext ); + wglMakeCurrent( g_hDC, g_openglContext ); +} + +Render::Render() : m_window(nullptr) +{ +} + +Render::~Render() +{ +} + +void Render::Init() +{ + Msg("Initializing renderer ..."); + + m_window = WND_CreateWindow(0, 0, 1280, 720, "Game"); + + CreateOpenGL( (HWND)m_window ); + + gladLoadGL(); + + int width = 0, height = 0; + WND_GetWindowSize(m_window, &width, &height); + + g_renderView.width = width; + g_renderView.height = height; + + // Create render device + g_renderDevice = new RenderDevice(); + + // Create texture manager + g_texturesManager = new TexturesManager(); + g_texturesManager->Init(); + + // Create shader system + g_shaderSystem = new ShaderSystem(); + g_shaderSystem->Init(); + + // Create model manager + g_modelManager = new ModelManager(); + + // Create debug render + g_debugRender = new DebugRender(); + g_debugRender->Initialize(); + + g_drawDebug = true; + + // Create UI System + uiInit(); + + // Create global vertex array + glGenVertexArrays(1, &g_vao); + glBindVertexArray(g_vao); +} + +void Render::Shutdown() +{ + glBindVertexArray(0); + glDeleteVertexArrays(1, &g_vao); + + uiShutdown(); + + g_debugRender->Shutdown(); + delete g_debugRender; + g_debugRender = nullptr; + + delete g_modelManager; + g_modelManager = nullptr; + + g_shaderSystem->Shutdown(); + delete g_shaderSystem; + g_shaderSystem = nullptr; + + g_texturesManager->Shutdown(); + delete g_texturesManager; + g_texturesManager = nullptr; + + delete g_renderDevice; + g_renderDevice = nullptr; +} + +void Render::BeginFrame() +{ + BuildRenderView(); + + int width = 0, height = 0; + WND_GetWindowSize(m_window, &width, &height); + + glViewport( 0, 0, width, height ); + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glClearColor( 0.5f, 0.5f, 0.5f, 1.0f ); + + g_renderDevice->SetBackfaceCull(true); +} + +void Render::EndFrame() +{ + // Flush debug render + g_debugRender->RenderFrame(); + + SwapBuffers( g_hDC ); +} + +void Render::DrawFrame() +{ + +} + +void* Render::GetRenderWindow() +{ + return m_window; +} + +void Render::BuildRenderView() +{ + if (g_renderView.fov <= 0.0f) + { + Msg("Render::BuildRenderView: FOV is zero!"); + g_renderView.fov = 0.1f;//70.0f; + } + + int width = 0, height = 0; + WND_GetWindowSize(m_window, &width, &height); + + if (width < 1) width = 1; + if (height < 1) height = 1; + + float aspect = (float)width / (float)height; + g_renderView.proj = glm::perspectiveFov(glm::radians(g_renderView.fov), (float)width, (float)height, 0.1f, 1000.0f); + + ShowCursor(FALSE); +} + +// Windowing + +static int s_should_wrap_mouse = FALSE; +static bool s_wndclass_registered = FALSE; +static const char* s_wndclass_name = "DumbWindowClass"; + +int g_mousex = 0; +int g_mousey = 0; +RECT g_rect = { 0, 0, 0, 0 }; + +// Should be implemented by library user. +void WND_Callback_Keyboard(void* window, KeyboardKeys key, int nativeKey, int action) +{ + if (action) + g_inputSystem->OnKeyDown(key); + else + g_inputSystem->OnKeyUp(key); +} + +mouseButton_t GetMouseButton(UINT mouseButton) +{ + switch (mouseButton) + { + case MK_LBUTTON: + return mouseButton_Left; + case MK_MBUTTON: + return mouseButton_Middle; + case MK_RBUTTON: + return mouseButton_Right; + default: + break; + } + + // #TODO: nameless buttons ??? + return GetNamelessMouseButtonIndex(1); +} + +LRESULT CALLBACK WND_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + + case WM_KEYDOWN: + WND_Callback_Keyboard(hwnd, GetKeyFromWin32((Win32Keys::Keys)wParam), (int)wParam, PRESSED); + break; + + case WM_KEYUP: + WND_Callback_Keyboard(hwnd, GetKeyFromWin32((Win32Keys::Keys)wParam), (int)wParam, UNPRESSED); + break; + + case WM_LBUTTONDOWN: + g_inputSystem->OnMouseKeyDown(mouseButton_Left); + break; + case WM_RBUTTONDOWN: + g_inputSystem->OnMouseKeyDown(mouseButton_Right); + break; + + + case WM_LBUTTONUP: + g_inputSystem->OnMouseKeyUp(mouseButton_Left); + break; + case WM_RBUTTONUP: + g_inputSystem->OnMouseKeyUp(mouseButton_Right); + break; + + case WM_MOUSEHOVER: + ShowCursor(FALSE); + break; + + case WM_MOUSELEAVE: + ShowCursor(TRUE); + break; + + case WM_MOUSEMOVE: + { + if (s_should_wrap_mouse) + { + GetClientRect(hwnd, &g_rect); // Get window dimension + + g_rect.right -= 12; // right offset + g_rect.left += 12; // left offset + + g_mousex = LOWORD(lParam); // Get mouse X pos + g_mousey = HIWORD(lParam); // Get mouse Y pos + + if (g_mousex >= g_rect.right) + { + SetCursorPos(g_rect.left, g_mousey); + } + else if (g_mousex <= g_rect.left) + { + SetCursorPos(g_rect.right, g_mousey); + } + g_inputSystem->OnMouseMove(g_mousex, g_mousey); + } + else + { + g_mousex = LOWORD(lParam); // Get mouse X pos + g_mousey = HIWORD(lParam); // Get mouse Y pos + g_inputSystem->OnMouseMove(g_mousex, g_mousey); + } + } + break; + default: + break; + } + + return DefWindowProcA(hwnd, uMsg, wParam, lParam); +} + + +void* WND_CreateWindow(int x, int y, int width, int height, const char* title) +{ + if (!s_wndclass_registered) + { + WNDCLASSA wc; + memset(&wc, 0, sizeof(wc)); + // wc.hIcon = LoadIconA(GetModuleHandleA(0), MAKEINTRESOURCEA(IDI_ICON1)); + wc.lpfnWndProc = WND_WindowProc; + wc.hInstance = GetModuleHandleA(0); + wc.lpszClassName = s_wndclass_name; + RegisterClassA(&wc); + + s_wndclass_registered = TRUE; + } + + // Window Style + DWORD dwWindowStyle = WS_OVERLAPPEDWINDOW ^ (WS_THICKFRAME | WS_MAXIMIZEBOX); + + // Rect + RECT rc = { 0, 0, width, height }; + AdjustWindowRect(&rc, dwWindowStyle, FALSE); + + HWND hWnd = CreateWindowA(s_wndclass_name, title, dwWindowStyle, + x, y, (rc.right - rc.left), (rc.bottom - rc.top), + NULL, NULL, GetModuleHandleA(0), NULL); + + assert(hWnd != INVALID_HANDLE_VALUE); + + ShowWindow(hWnd, SW_SHOW); + UpdateWindow(hWnd); + + return hWnd; +} + +void WND_DestroyWindow(void* window) +{ + DestroyWindow((HWND)window); +} + +void WND_GetWindowSize(void* window, int* x, int* y) +{ + RECT rc; + memset(&rc, 0, sizeof(rc)); + + GetClientRect((HWND)window, &rc); + + *x = (rc.right - rc.left); + *y = (rc.bottom - rc.top); +} + +bool WND_GetWindowGrab(void* window) +{ + return GetFocus() == (HWND)window; +} + +void HELPER_GetWindowPos(HWND hWnd, int* x, int* y) +{ + RECT rect = { NULL }; + if (GetWindowRect(hWnd, &rect)) + { + *x = rect.left; + *y = rect.top; + } +} + +void WND_SetWindowSize(void* window, int x, int y) +{ + int posX = 0, posY = 0; + HELPER_GetWindowPos((HWND)window, &posX, &posY); + MoveWindow((HWND)window, posX, posY, x, y, TRUE); + UpdateWindow((HWND)window); +} + +void WND_SetRelativeMouseMode(int value) +{ + s_should_wrap_mouse = value; +} diff --git a/src/engine/render/render.h b/src/engine/render/render.h new file mode 100644 index 0000000..76c9458 --- /dev/null +++ b/src/engine/render/render.h @@ -0,0 +1,45 @@ +#ifndef RENDER_H +#define RENDER_H + +// Render windowing management + +enum KEY_ACTION +{ + UNPRESSED, + PRESSED, + HOLDING +}; + +void* WND_CreateWindow(int x, int y, int width, int height, const char* title); +void WND_DestroyWindow(void* window); +void WND_GetWindowSize(void* window, int* x, int* y); +bool WND_GetWindowGrab(void* window); +void WND_SetWindowSize(void* window, int x, int y); +void WND_SetRelativeMouseMode(int value); + +class Render +{ +public: + Render(); + ~Render(); + + void Init(); + void Shutdown(); + + void BeginFrame(); + void EndFrame(); + + void DrawFrame(); + + void* GetRenderWindow(); + +private: + void BuildRenderView(); + +private: + void* m_window; +}; + +extern Render* g_render; + +#endif // !RENDER_H diff --git a/src/engine/render/render_shared.h b/src/engine/render/render_shared.h new file mode 100644 index 0000000..dd8ebb3 --- /dev/null +++ b/src/engine/render/render_shared.h @@ -0,0 +1,84 @@ +// Shared header for render stuff +#ifndef RENDER_SHARED_H +#define RENDER_SHARED_H + +#include + +#include "utils/maths.h" +#include "render/color.h" + +enum class BufferAccess +{ + READ_ONLY, + WRITE_ONLY, + READ_WRITE +}; + +// texture format +enum PixelFormat +{ + PF_UNKNOWN, + PF_R8G8B8, + PF_R8G8B8A8, + PF_R8G8B8F, + PF_R8G8B8A8F, + + // Depth formats + PF_DEPTH32F +}; + +// pixel format convertion +uint32_t getGLPF(PixelFormat pf); +uint32_t getGLInternalPF(PixelFormat pf); +uint32_t getGLTypePF(PixelFormat pf); + +// Blending functions +enum BlendFactor +{ + BF_ZERO, + BF_ONE, + BF_SRC_COLOR, + BF_ONE_MINUS_SRC_COLOR, + BF_DST_COLOR, + BF_ONE_MINUS_DST_COLOR, + BF_SRC_ALPHA, + BF_ONE_MINUS_SRC_ALPHA, + BF_DST_ALPHA, + BF_ONE_MINUS_DST_ALPHA, + BF_CONSTANT_COLOR, + BF_ONE_MINUS_CONSTANT_COLOR, + BF_CONSTANT_ALPHA, + BF_ONE_MINUS_CONSTANT_ALPHA +}; + +// Blending factor convertion +uint32_t GetGLBlendFactor(BlendFactor factor); + +// Base structure for render view (view and projection matrices, viewport settings) +struct View +{ + glm::mat4 view; + glm::mat4 proj; + + int x, y; + int width, height; + + float fov; +}; + +// Global instance of render view +extern View g_renderView; + +// Stretched picture vertex +struct StretchedVertex +{ + Vec3 position; + Vec2 texcoord; +}; + +#define MAX_STRETCH_VX 12 * sizeof(StretchedVertex) + +// debugging stuff +void check_for_opengl_error(); + +#endif \ No newline at end of file diff --git a/src/engine/render/renderdevice.cpp b/src/engine/render/renderdevice.cpp new file mode 100644 index 0000000..a4df9ca --- /dev/null +++ b/src/engine/render/renderdevice.cpp @@ -0,0 +1,202 @@ +#include +#include + +// OpenGL +#include "render/gl_shared.h" + +// VERTEX AND INDEX OBJECTS +#include "render/vertexbuffer.h" +#include "render/indexbuffer.h" + +#include "render/rendertarget.h" +#include "render/renderdevice.h" + +RenderDevice* g_renderDevice = nullptr; + +RenderDevice::RenderDevice() +{ + m_activeVB = nullptr; + m_activeIB = nullptr; + m_blending = false; + m_activeReadRT = nullptr; + m_activeWriteRT = nullptr; + m_backfaceCull = false; +} + +RenderDevice::~RenderDevice() +{ +} + +VertexBuffer* RenderDevice::CreateVertexBuffer(void* data, size_t size, bool isStream) +{ + return new VertexBuffer(data, size, isStream); +} + +IndexBuffer* RenderDevice::CreateIndexBuffer(void* data, size_t size, bool isStream) +{ + return new IndexBuffer(data, size, isStream); +} + +void RenderDevice::SetVerticesBuffer(VertexBuffer* buffer) +{ + if (buffer) { + if (m_activeVB != buffer) { + m_activeVB = buffer; + m_activeVB->Bind(); + } + } else { // unbind buffer + glBindBuffer(GL_ARRAY_BUFFER, 0); + } +} + +void RenderDevice::SetIndicesBuffer(IndexBuffer* buffer) +{ + if (buffer) { + if (m_activeIB != buffer) { + m_activeIB = buffer; + m_activeIB->Bind(); + } + } else { // unbind buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +} + +void RenderDevice::SetDepthTest(bool enable) +{ + enable ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST); +} + +void RenderDevice::SetDepthWrite(bool enable) +{ + glDepthMask(enable ? GL_TRUE : GL_FALSE); +} + +//void RenderDevice::setVertexFormat(VertexFormat* format) +//{ +// assert(format); +// +// if (format->count() == 0) { +// Core::error("RenderDevice::setVertexFormat: failed to set empty vertex format"); +// } +// +// size_t appliedOffset = 0; +// for (VertexAttribute* it = format->begin(); it != format->end(); ++it) { +// +// GLenum data_type = get_gl_vertex_attribute_type(it->m_type); +// GLuint data_size = GLuint(get_vertex_attribute_size(it->m_type)); +// +// if (appliedOffset > 0) +// glVertexAttribPointer( +// GLuint(it->m_offset), +// GLint(it->m_size), +// data_type, +// GL_FALSE, +// GLsizei(format->size() * data_size), +// (void*)(appliedOffset * sizeof(float)) +// ); +// else +// glVertexAttribPointer( +// GLuint(it->m_offset), +// GLint(it->m_size), +// data_type, +// GL_FALSE, +// GLsizei(format->size() * data_size), +// (void*)0 +// ); +// +// glEnableVertexAttribArray(GLuint(it->m_offset)); +// +// appliedOffset += it->m_size; +// } +//} + +void RenderDevice::SetBlending(bool value) +{ + if (m_blending != value) { + m_blending = value; + + value ? glEnable(GL_BLEND) : glDisable(GL_BLEND); + } +} + +void RenderDevice::SetBlendingFunction(BlendFactor srcFactor, BlendFactor destFactor) +{ + // Switch state if one of two blending factors was changed + if (srcFactor != m_srcBlendFactor || destFactor != m_destBlendFactor) + { + m_srcBlendFactor = srcFactor; + m_destBlendFactor = destFactor; + + // push to gl state + glBlendFunc(GetGLBlendFactor(srcFactor), GetGLBlendFactor(destFactor)); + } +} + +void RenderDevice::SetReadRenderTarget(RenderTarget* renderTarget) +{ + if (renderTarget) { + if (m_activeReadRT != renderTarget) { + m_activeReadRT = renderTarget; + glBindFramebuffer(GL_READ_FRAMEBUFFER, renderTarget->m_framebuffer); + } + } + else { // set default rt + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + } +} + +void RenderDevice::SetWriteRenderTarget(RenderTarget* renderTarget) +{ + if (renderTarget) { + if (m_activeWriteRT != renderTarget) { + m_activeWriteRT = renderTarget; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, renderTarget->m_framebuffer); + } + } + else { // set default rt + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } +} + +void RenderDevice::SetBackfaceCull(bool value) +{ + if (m_backfaceCull != value) { + m_backfaceCull = value; + + value ? glEnable(GL_CULL_FACE) : glDisable(GL_CULL_FACE); + } +} + +void RenderDevice::SetViewport(int x, int y, int w, int h) +{ + glViewport(x, y, w, h); +} + +void RenderDevice::BlitRenderTarget(int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, TextureSurfaceType surfaceType) +{ + GLbitfield mask = 0; + if (surfaceType & TST_COLOR) + mask |= GL_COLOR_BUFFER_BIT; + if (surfaceType & TST_DEPTH) + mask |= GL_DEPTH_BUFFER_BIT; + if (surfaceType & TST_STENCIL) + mask |= GL_STENCIL_BUFFER_BIT; + + glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, mask, GL_LINEAR); +} + +static GLenum g_glPrimitiveMode[PT_TRIANGLES + 1] = { + GL_POINTS, + GL_LINES, + GL_TRIANGLES +}; + +void RenderDevice::DrawArrays(PrimitiveType primType, uint32_t startOf, size_t verticesCount) +{ + glDrawArrays(g_glPrimitiveMode[primType], startOf, GLsizei(verticesCount)); +} + +void RenderDevice::DrawElements(PrimitiveType primType, size_t elementsCount, bool is16bitIndices) +{ + glDrawElements(g_glPrimitiveMode[primType], GLsizei(elementsCount), is16bitIndices ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, NULL); +} diff --git a/src/engine/render/renderdevice.h b/src/engine/render/renderdevice.h new file mode 100644 index 0000000..d16b616 --- /dev/null +++ b/src/engine/render/renderdevice.h @@ -0,0 +1,78 @@ +#ifndef RENDERDEVICE_H +#define RENDERDEVICE_H + +#include +#include "render/render_shared.h" + +class VertexBuffer; +class IndexBuffer; +class VertexFormat; +class RenderTarget; + +enum TextureSurfaceType +{ + TST_COLOR = 1 << 0, + TST_DEPTH = 1 << 1, + TST_STENCIL = 1 << 2, +}; + +enum PrimitiveType +{ + PT_POINTS, + PT_LINES, + PT_TRIANGLES +}; + +class RenderDevice +{ +public: + RenderDevice(); + ~RenderDevice(); + + VertexBuffer* CreateVertexBuffer(void* data, size_t size, bool isStream = false); + IndexBuffer* CreateIndexBuffer(void* data, size_t size, bool isStream = false); + + void SetVerticesBuffer(VertexBuffer* buffer); + void SetIndicesBuffer(IndexBuffer* buffer); + + void SetDepthTest(bool enable); + void SetDepthWrite(bool enable); + + //void setVertexFormat(VertexFormat* format); + + void SetBlending(bool value); + void SetBlendingFunction(BlendFactor srcFactor, BlendFactor destFactor); + + void SetReadRenderTarget(RenderTarget* renderTarget); + void SetWriteRenderTarget(RenderTarget* renderTarget); + + void SetBackfaceCull(bool value); + + void SetViewport(int x, int y, int w, int h); + + // glBlitFramebuffer + void BlitRenderTarget(int srcX, int srcY, int srcWidth, int srcHeight, + int destX, int destY, int destWidth, int destHeight, + TextureSurfaceType surfaceType); + + // drawing + void DrawArrays(PrimitiveType primType, uint32_t startOf, size_t verticesCount); + void DrawElements(PrimitiveType primType, size_t elementsCount, bool is16bitIndices); + +private: + VertexBuffer* m_activeVB; + IndexBuffer* m_activeIB; + + RenderTarget* m_activeReadRT; + RenderTarget* m_activeWriteRT; + + bool m_blending; + BlendFactor m_srcBlendFactor; + BlendFactor m_destBlendFactor; + + bool m_backfaceCull; +}; + +extern RenderDevice* g_renderDevice; + +#endif // !RENDERDEVICE_H diff --git a/src/engine/render/rendertarget.cpp b/src/engine/render/rendertarget.cpp new file mode 100644 index 0000000..9f77b85 --- /dev/null +++ b/src/engine/render/rendertarget.cpp @@ -0,0 +1,63 @@ +#include +#include "render/rendertarget.h" + +#include "render/texturesmanager.h" +#include "render/texture2d.h" + +#include "glad/glad.h" + +void RenderTarget::setDefaultFramebuffer() +{ + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +RenderTarget::RenderTarget() +{ + m_framebuffer = -1; +} + +RenderTarget::~RenderTarget() +{ + m_framebuffer = -1; +} + +void RenderTarget::create(const char* name /*= nullptr*/) +{ + assert(g_texturesManager); + + //////////////////////// + // Create FBO + + // generate frame buffer + glGenFramebuffers(1, &m_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); +} + +void RenderTarget::destroy() +{ + GLint drawFboId; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &drawFboId); + + if (drawFboId == (GLint)m_framebuffer) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &m_framebuffer); +} + +void RenderTarget::finialize() +{ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + assert(0 && "RenderTarget::finialize: failed to finialize framebuffer. Framebuffer is not complete"); + } +} + +void RenderTarget::attachColorTexture(int slot, Texture2D* texture) +{ + assert(texture && "Failed to assing nullptr texture"); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + slot, GL_TEXTURE_2D, texture->GetHandle(), 0); +} + +void RenderTarget::attachDepthTexture(Texture2D* texture) +{ + assert(texture && "Failed to assing nullptr texture"); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture->GetHandle(), 0); +} diff --git a/src/engine/render/rendertarget.h b/src/engine/render/rendertarget.h new file mode 100644 index 0000000..302dcaf --- /dev/null +++ b/src/engine/render/rendertarget.h @@ -0,0 +1,33 @@ +#ifndef RENDERTARGET_H +#define RENDERTARGET_H + +#include + +class Texture2D; +class RenderDevice; + +class RenderTarget +{ + friend class RenderDevice; +public: + // #TODO: Little hack + static void setDefaultFramebuffer(); + +public: + RenderTarget(); + ~RenderTarget(); + + void create(const char* name = nullptr); + void destroy(); + + void finialize(); + + void attachColorTexture(int slot, Texture2D* texture); + void attachDepthTexture(Texture2D* texture); + +private: + uint32_t m_framebuffer; + +}; + +#endif \ No newline at end of file diff --git a/src/engine/render/shader.cpp b/src/engine/render/shader.cpp new file mode 100644 index 0000000..bd314aa --- /dev/null +++ b/src/engine/render/shader.cpp @@ -0,0 +1,92 @@ +#include +#include "utils/logger.h" +#include "render/shader.h" +#include "filesystem/filemanager.h" +#include "filesystem/stream.h" + +GLuint CreateShader(GLenum shaderType, const char* filename) +{ + StreamBase* stream = g_fileManager->OpenStream( filename, FileAccess::Read ); + if ( !stream ) + { + Msg( "CreateShader: failed to open file %s", filename ); + assert( 0 ); + } + + stream->seek( SeekDir::End, 0 ); + size_t length = stream->tell(); + stream->seek( SeekDir::Begin, 0 ); + + std::string content; + content.resize( length + 1 ); + stream->readBuffer( (void*)content.data(), length ); + + content[length] = '\0'; + + const char* contentCStr = content.c_str(); + + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &contentCStr, NULL); + glCompileShader(shader); + + int success; + char infoLog[512]; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (!success) + { + glGetShaderInfoLog(shader, 512, NULL, infoLog); + Msg("Failed to compile shader %s\n%s", filename, infoLog); + } + + Msg("created shader from file %s", filename); + return shader; +} + +Shader::Shader() : + m_name(nullptr), + m_stride(0), + m_layout_count(0) +{ + +} + +Shader::~Shader() +{ + if (m_name) + free( (void*)m_name ); + + Destroy(); +} + +void Shader::Create(const char* name, const char* vsfilepath, const char* psfilepath) +{ + //m_name = strdup( name ); + + GLuint vertexShader = CreateShader(GL_VERTEX_SHADER, vsfilepath); + GLuint fragmentShader = CreateShader(GL_FRAGMENT_SHADER, psfilepath); + + m_program = glCreateProgram(); + glAttachShader(m_program, vertexShader); + glAttachShader(m_program, fragmentShader); + AllocateAttributes(); + glLinkProgram(m_program); + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + + int success; + char infoLog[512]; + glGetProgramiv(m_program, GL_LINK_STATUS, &success); + + if (!success) + { + glGetProgramInfoLog(m_program, 512, NULL, infoLog); + Msg("Failed to link program %s", infoLog); + } +} + +void Shader::Destroy() +{ + glDeleteProgram(m_program); +} diff --git a/src/engine/render/shader.h b/src/engine/render/shader.h new file mode 100644 index 0000000..7c9b060 --- /dev/null +++ b/src/engine/render/shader.h @@ -0,0 +1,98 @@ +#ifndef SHADER_H +#define SHADER_H + +#include +#include + +const int SHADERUNIFORM_MAX_COUNT = 16; +const int INPUT_LAYOUT_MAX_COUNT = 8; + +enum VertexAttribute_t { + VERTEXATTR_VEC2, + VERTEXATTR_VEC3, + VERTEXATTR_VEC4, + + VERTEXATTR_MAX +}; + +enum ShaderSemantic_t { + SHADERSEMANTIC_POSITION, + SHADERSEMANTIC_COLOR, + SHADERSEMANTIC_TEXCOORD, + SHADERSEMANTIC_TEXCOORD0, + SHADERSEMANTIC_TEXCOORD1, + SHADERSEMANTIC_NORMAL, + SHADERSEMANTIC_TANGENT, + SHADERSEMANTIC_BITANGENT, + + SHADERSEMANTIC_MAX +}; + +enum ShaderUniformType_t { + SHADERUNIFORM_FLOAT, + SHADERUNIFORM_VEC2, + SHADERUNIFORM_VEC3, + SHADERUNIFORM_VEC4, + SHADERUNIFORM_MAT4, + + SHADERUNIFORM_MAX +}; + +struct InputLayoutDesc_t { + VertexAttribute_t attribute; + ShaderSemantic_t semantic; +}; + +struct ShaderUniformDesc_t { + ShaderUniformType_t type; + const char* name; + size_t size; +}; + +class Shader +{ +public: + Shader(); + ~Shader(); + + void Create(const char* name, const char* vsfilepath, const char* psfilepath); + void Destroy(); + + void AllocateAttributes(); + +public: + // TEMP SOLUTION + ShaderUniformDesc_t m_uniform_desc[SHADERUNIFORM_MAX_COUNT]; + size_t m_uniform_count; + + InputLayoutDesc_t m_layouts[INPUT_LAYOUT_MAX_COUNT]; + size_t m_layout_count; + + GLuint m_glLayouts[INPUT_LAYOUT_MAX_COUNT]; + + // #TODO: REMOVE PLEASE + int m_stride; + + const char* m_name; + + GLuint m_program; +}; + +inline int GetVertexAttributeSize( VertexAttribute_t attrib ) +{ + switch ( attrib ) + { + case VERTEXATTR_VEC2: + return 2; + case VERTEXATTR_VEC3: + return 3; + case VERTEXATTR_VEC4: + return 4; + default: + break; + } + + return 0; +} + +#endif // !SHADER_H diff --git a/src/engine/render/shadersystem.cpp b/src/engine/render/shadersystem.cpp new file mode 100644 index 0000000..154c75e --- /dev/null +++ b/src/engine/render/shadersystem.cpp @@ -0,0 +1,190 @@ +#include +#include +#include "utils/logger.h" +#include "render/shadersystem.h" +#include "render/shader.h" +#include + +static const char* g_uniformNameTable[UNIFORM_MAX] = +{ + "u_modelMatrix", + "u_viewMatrix", + "u_projectionMatrix", + "u_modelViewProjection", + "u_customColor", + "u_sunDirection", + "u_sunColor", + "u_sunAmbientColor", +}; + +static const char* g_samplersNameTable[SAMPLER_MAX] = +{ + "u_albedoTexture", + "u_normalTexture", + "u_lightmapTexture", +}; + +ShaderSystem* g_shaderSystem = nullptr; + +ShaderSystem::ShaderSystem() +{ +} + +ShaderSystem::~ShaderSystem() +{ +} + +void ShaderSystem::Init() +{ + Msg("Initializing Shader System ..."); +} + +void ShaderSystem::Shutdown() +{ + for (int i = 0; i < m_shaders.size(); i++) + { + ShaderData& shaderData = m_shaders[i]; + + if (shaderData.shader) + { + delete shaderData.shader; + shaderData.shader = nullptr; + } + } + + m_shaders.clear(); +} + +Shader* ShaderSystem::CreateShader(const char* name, const char* vsfilepath, const char* psfilepath, InputLayoutDesc_t* inputLayout /*= nullptr*/, int inputLayoutCount/* = 0*/) +{ + auto it = std::find_if(m_shaders.begin(), m_shaders.end(), [=](const ShaderData& shaderData) { return shaderData.shadername == name; }); + if ( it != m_shaders.end() ) + { + return it->shader; + } + + Shader* pShader = new Shader(); + + if ( inputLayout && inputLayoutCount > 0 ) + { + memcpy( pShader->m_layouts, inputLayout, inputLayoutCount * sizeof( InputLayoutDesc_t ) ); + pShader->m_layout_count = inputLayoutCount; + } + + pShader->Create( name, vsfilepath, psfilepath ); + + ShaderData shaderData = { name, pShader }; + m_shaders.push_back(shaderData); + + return pShader; +} + +size_t g_vertex_attribs_sizetable[VERTEXATTR_MAX] = +{ + 2, // VERTEXATTR_VEC2 + 3, // VERTEXATTR_VEC3 + 4, // VERTEXATTR_VEC4 +}; + +size_t g_vertex_attribs_realsizetable[VERTEXATTR_MAX] = +{ + 8, // VERTEXATTR_VEC2 + 12, // VERTEXATTR_VEC3 + 16, // VERTEXATTR_VEC4 +}; + +void ApplyVertexFormat(const Shader* shader, size_t stride) +{ + size_t appliedOffset = 0; + for (int i = 0; i < shader->m_layout_count; i++) + { + const InputLayoutDesc_t* layoutEntry = &shader->m_layouts[i]; + + glEnableVertexAttribArray(GLuint(i)); + + glVertexAttribPointer(GLuint(i), GLint(g_vertex_attribs_sizetable[layoutEntry->attribute]), + GL_FLOAT, GL_FALSE, static_cast(stride), + (appliedOffset > 0) ? (void*)(appliedOffset * sizeof(float)) : (void*)0); + + appliedOffset += g_vertex_attribs_sizetable[layoutEntry->attribute]; + } +} + +void ShaderSystem::SetShader(const Shader* shader) +{ + assert( shader ); + glUseProgram( shader->m_program ); + + // apply input layout + + //for ( int i = 0; i < shader->m_layout_count; i++ ) + //{ + // const InputLayoutDesc_t& layout = shader->m_layouts[ i ]; + // + // //glEnableVertexAttribArray( shader->m_glLayouts[ i ] ); + // //glVertexAttribPointer( shader->m_glLayouts[ i ], GetVertexAttributeSize( layout.attribute ), GL_FLOAT, GL_FALSE, shader->m_stride, NULL ); + + // glEnableVertexAttribArray( i ); + // glVertexAttribPointer( i, GetVertexAttributeSize( layout.attribute ), GL_FLOAT, GL_FALSE, shader->m_stride, NULL ); + //} + + ApplyVertexFormat( shader, shader->m_stride ); + //for ( int i = 0; i < shader->m_layout_count; i++ ) + //{ + // const InputLayoutDesc_t& layout = shader->m_layouts[ i ]; + // + // glEnableVertexAttribArray( shader->m_glLayouts[ i ] ); + // glVertexAttribPointer( shader->m_glLayouts[ i ], GetVertexAttributeSize( layout.attribute ), GL_FLOAT, GL_FALSE, shader->m_stride, NULL ); + //} + +} + +void ShaderSystem::SetUniformSampler(const Shader* shader, ShaderSamplers_t sampler, int index) +{ + glGetError(); + + GLint uniformLocation = glGetUniformLocation(shader->m_program, g_samplersNameTable[sampler]); + glUniform1i(uniformLocation, static_cast(index)); +} + +void ShaderSystem::SetUniformFloat4(const Shader* shader, ShaderUniform_t uniform, const void* data) +{ + GLint location = glGetUniformLocation( shader->m_program, g_uniformNameTable[ uniform ] ); + glUniform4fv( location, 1, ( const GLfloat* )data ); +} + +void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data ) +{ + GLint location = glGetUniformLocation( shader->m_program, g_uniformNameTable[ uniform ] ); + glUniformMatrix4fv( location, 1, GL_FALSE, ( const GLfloat* )data ); +} + +// #TODO: Move out + +static const char* s_attributeNameTable[SHADERSEMANTIC_MAX] = +{ + "a_position",//SHADERSEMANTIC_POSITION, + "a_color", //SHADERSEMANTIC_COLOR + "a_texcoord",//SHADERSEMANTIC_TEXCOORD, + "a_texcoord0", //SHADERSEMANTIC_TEXCOORD0, + "a_texcoord1",//SHADERSEMANTIC_TEXCOORD1, + "a_normal",//SHADERSEMANTIC_NORMAL, + "a_tangent",//SHADERSEMANTIC_TANGENT, + "a_bitangent"//SHADERSEMANTIC_BITANGENT, +}; + +void Shader::AllocateAttributes() +{ + // Allocate input layout + for ( int i = 0; i < m_layout_count; i++ ) + { + const InputLayoutDesc_t& layout = m_layouts[ i ]; + // Msg( "Shader::AllocateAttributes: Bind %i as %s", i, s_attributeNameTable[ layout.semantic ] ); + glBindAttribLocation( m_program, i, s_attributeNameTable[ layout.semantic ] ); + + GL_CHECK_ERROR(); + + // m_glLayouts[ i ] = glGetAttribLocation( m_program, s_attributeNameTable[i] ); + // assert( m_glLayouts[ i ] ); + } +} diff --git a/src/engine/render/shadersystem.h b/src/engine/render/shadersystem.h new file mode 100644 index 0000000..b43286f --- /dev/null +++ b/src/engine/render/shadersystem.h @@ -0,0 +1,61 @@ +#ifndef SHADERSYSTEM_H +#define SHADERSYSTEM_H + +#include +#include + +#include "render/shader.h" + +enum ShaderUniform_t +{ + UNIFORM_MODEL_MATRIX, + UNIFORM_VIEW_MATRIX, + UNIFORM_PROJ_MATRIX, + UNIFORM_MVP_MATRIX, + UNIFORM_CUSTOM_COLOR, + UNIFORM_SUN_DIRECTION, + UNIFORM_SUN_COLOR, + UNIFORM_SUN_AMBIENT, + + UNIFORM_MAX, +}; + +enum ShaderSamplers_t +{ + SAMPLER_ALBEDO, + SAMPLER_NORMAL, + SAMPLER_LIGHTMAP, + + SAMPLER_MAX +}; + +class ShaderSystem +{ +public: + ShaderSystem(); + ~ShaderSystem(); + + void Init(); + void Shutdown(); + + Shader* CreateShader(const char* name, const char* vsfilepath, const char* psfilepath, InputLayoutDesc_t* inputLayout = nullptr, int inputLayoutCount = 0); + + void SetShader(const Shader* shader); + + void SetUniformSampler( const Shader* shader, ShaderSamplers_t sampler, int index ); + void SetUniformFloat4( const Shader* shader, ShaderUniform_t uniform, const void* data ); + void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data ); + +private: + struct ShaderData + { + std::string shadername; + Shader* shader; + }; + + std::vector m_shaders; +}; + +extern ShaderSystem* g_shaderSystem; + +#endif // !SHADERSYSTEM_H diff --git a/src/engine/render/texture2d.cpp b/src/engine/render/texture2d.cpp new file mode 100644 index 0000000..5b81624 --- /dev/null +++ b/src/engine/render/texture2d.cpp @@ -0,0 +1,308 @@ +#include +#include "render/texture2d.h" +#include "render/texturesmanager.h" +#include "render/gl_shared.h" + +GLint getGlWrap(TextureWrap wrap); +GLint getGlTexFilter(TextureFilter filter); + +Texture2D* Texture2D::Create() +{ + return new Texture2D; +} + +Texture2D::Texture2D() +{ + m_pf = PF_UNKNOWN; + m_width = 0; + m_height = 0; + m_channels = 0; + m_handle = -1; +} + +Texture2D::~Texture2D() +{ + m_pf = PF_UNKNOWN; + m_width = 0; + m_height = 0; + m_channels = 0; + m_handle = -1; +} + +void Texture2D::CreateBlackTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + uint8_t* data = new uint8_t[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 0; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateWhiteTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + uint8_t* data = new uint8_t[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 255; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateGrayTexture(int width, int height, int channels) +{ + size_t textureSize = width * height * channels; + uint8_t* data = new uint8_t[textureSize]; + assert(data); + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = 255 / 2; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateTexture_Generator(int width, int height, int channels, int color) +{ + size_t textureSize = width * height * channels; + uint8_t* data = new uint8_t[textureSize]; + assert(data); + + m_textureFileName = "$generator_texture$"; + + for (int i = 0; i < (int)textureSize; i++) { + data[i] = color; + } + + CreateFromExistedData(data, width, height, channels); + + delete[] data; +} + +void Texture2D::CreateFromExistedData(void* data, int width, int height, int channels) +{ + //assert(data); + + m_width = width; + m_height = height; + m_channels = channels; + + glGenTextures(1, &m_handle); + glBindTexture(GL_TEXTURE_2D, m_handle); + glTexImage2D(GL_TEXTURE_2D, 0, (channels == 3) ? GL_RGB : GL_RGBA, width, height, 0, (channels == 3) ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture2D::CreateRaw(void* data, int width, int height, PixelFormat pf) +{ + m_width = width; + m_height = height; + m_channels = (pf == PF_R8G8B8) ? 3 : 4; + m_pf = pf; + + glGenTextures(1, &m_handle); + glBindTexture(GL_TEXTURE_2D, m_handle); + glTexImage2D(GL_TEXTURE_2D, 0, getGLInternalPF(pf), width, height, 0, getGLInternalPF(pf), GL_UNSIGNED_BYTE, data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); +} + +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +void Texture2D::GenerateMipmaps() +{ + glBindTexture(GL_TEXTURE_2D, m_handle); + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + //if (g_texAnisoFilter.getValueB()) { + // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, g_texAnisoLevel.getValueI()); + //} + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture2D::Bind() +{ + glBindTexture(GL_TEXTURE_2D, m_handle); +} + +void Texture2D::setWrapS(TextureWrap wrap) +{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getGlWrap(wrap)); +} + +void Texture2D::setWrapT(TextureWrap wrap) +{ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getGlWrap(wrap)); +} + +void Texture2D::setMin(TextureFilter filter) +{ + GLint param = 0; + param = getGlTexFilter(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param); +} + +void Texture2D::setMag(TextureFilter filter) +{ + GLint param = 0; + param = getGlTexFilter(filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param); +} + +GLint getGlWrap(TextureWrap wrap) +{ + GLint param = 0; + + if (wrap == TextureWrap::Repeat) + param = GL_REPEAT; + else if (wrap == TextureWrap::MirroredRepeat) + param = GL_MIRRORED_REPEAT; + else if (wrap == TextureWrap::ClampToEdge) + param = GL_CLAMP_TO_EDGE; + else if (wrap == TextureWrap::ClampToBorder) + param = GL_CLAMP_TO_BORDER; + + return param; +} + +GLint getGlTexFilter(TextureFilter filter) +{ + GLint param = 0; + + if (filter == TextureFilter::Linear) + param = GL_LINEAR; + else if (filter == TextureFilter::Nearest) + param = GL_NEAREST; + else if (filter == TextureFilter::LinearMipmapLinear) + param = GL_LINEAR_MIPMAP_LINEAR; + else if (filter == TextureFilter::LinearMipmapNearest) + param = GL_LINEAR_MIPMAP_NEAREST; + else if (filter == TextureFilter::NearestMipmapLinear) + param = GL_NEAREST_MIPMAP_LINEAR; + else if (filter == TextureFilter::NearestMipmapNearest) + param = GL_NEAREST_MIPMAP_NEAREST; + + return param; +} + +uint32_t getGLPF(PixelFormat pf) +{ + return 0; + //return uint32_t(); +} + +// Kirill: Remove to render_main.cpp or something else +uint32_t getGLInternalPF(PixelFormat pf) +{ + switch (pf) + { + case PF_UNKNOWN: + return 0; + + case PF_R8G8B8: + case PF_R8G8B8F: + return GL_RGB; + + case PF_R8G8B8A8: + case PF_R8G8B8A8F: + return GL_RGBA; + } + + return 0; +} + +uint32_t getGLTypePF(PixelFormat pf) +{ + switch (pf) + { + case PF_UNKNOWN: + return 0; + + case PF_R8G8B8: + case PF_R8G8B8A8: + return GL_UNSIGNED_BYTE; + + case PF_R8G8B8F: + case PF_R8G8B8A8F: + return GL_FLOAT; + } + + return 0; +} + +uint32_t GetGLBlendFactor(BlendFactor factor) +{ + switch (factor) + { + case BF_ZERO: + return GL_ZERO; + break; + case BF_ONE: + return GL_ONE; + break; + case BF_SRC_COLOR: + return GL_SRC_COLOR; + break; + case BF_ONE_MINUS_SRC_COLOR: + return GL_ONE_MINUS_SRC_COLOR; + break; + case BF_DST_COLOR: + return GL_DST_COLOR; + break; + case BF_ONE_MINUS_DST_COLOR: + return GL_ONE_MINUS_DST_COLOR; + break; + case BF_SRC_ALPHA: + return GL_SRC_ALPHA; + break; + case BF_ONE_MINUS_SRC_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + break; + case BF_DST_ALPHA: + return GL_DST_ALPHA; + break; + case BF_ONE_MINUS_DST_ALPHA: + return GL_ONE_MINUS_DST_ALPHA; + break; + case BF_CONSTANT_COLOR: + return GL_CONSTANT_COLOR; + break; + case BF_ONE_MINUS_CONSTANT_COLOR: + return GL_ONE_MINUS_CONSTANT_COLOR; + break; + case BF_CONSTANT_ALPHA: + return GL_CONSTANT_ALPHA; + break; + case BF_ONE_MINUS_CONSTANT_ALPHA: + return GL_ONE_MINUS_CONSTANT_ALPHA; + break; + default: + break; + } + + return 0; +} diff --git a/src/engine/render/texture2d.h b/src/engine/render/texture2d.h new file mode 100644 index 0000000..f65bc1b --- /dev/null +++ b/src/engine/render/texture2d.h @@ -0,0 +1,67 @@ +#ifndef TEXTURE2D_H +#define TEXTURE2D_H + +#include + +#include "render/render_shared.h" + +enum class TextureWrap +{ + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder +}; + +enum class TextureFilter +{ + Nearest, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear +}; + +class TexturesManager; + +class Texture2D +{ + friend class TexturesManager; +public: + static Texture2D* Create(); + +public: + Texture2D(); + ~Texture2D(); + + void CreateBlackTexture(int width, int height, int channels); + void CreateWhiteTexture(int width, int height, int channels); + void CreateGrayTexture(int width, int height, int channels); + void CreateTexture_Generator(int width, int height, int channels, int color); + void CreateFromExistedData(void* data, int width, int height, int channels); + void CreateFromFile(const char* filename); + + void CreateRaw(void* data, int width, int height, PixelFormat pf); + + void GenerateMipmaps(); + + void Bind(); + + void setWrapS(TextureWrap wrap); + void setWrapT(TextureWrap wrap); + void setMin(TextureFilter filter); + void setMag(TextureFilter filter); + + uint32_t GetHandle() { return m_handle; } + +private: + std::string m_textureFileName; + PixelFormat m_pf; + int m_width; + int m_height; + int m_channels; + uint32_t m_handle; +}; + +#endif // !TEXTURE2D_H diff --git a/src/engine/render/texturesmanager.cpp b/src/engine/render/texturesmanager.cpp new file mode 100644 index 0000000..9539c04 --- /dev/null +++ b/src/engine/render/texturesmanager.cpp @@ -0,0 +1,194 @@ +#include "utils/logger.h" +#include "filesystem/filemanager.h" +#include "render/texture2d.h" +#include "render/texturesmanager.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include + +// STB Image loading code +void Texture2D::CreateFromFile(const char* filename) +{ + int width, height, channels; + m_textureFileName = filename; + + File* file = g_fileManager->OpenFile(filename, FileAccess::Read); + + file->seek(SeekDir::End, 0); + size_t imageSize = file->tell(); + file->seek(SeekDir::Begin, 0); + + uint8_t* fileData = (uint8_t*)malloc(imageSize); + file->read(fileData, imageSize); + + g_fileManager->CloseFile(file); + + uint8_t* imageData = stbi_load_from_memory(fileData, int(imageSize), &width, &height, &channels, 0); + + if (imageData == NULL) { + free(fileData); + Msg("Texture loading error: %s (%s)", filename, stbi_failure_reason()); + assert(imageData); + } + + CreateFromExistedData(imageData, width, height, channels); + m_textureFileName = filename; + + free(fileData); + + stbi_image_free(imageData); +} + +static const char* g_texFileExtensions[] = { ".png", ".jpeg", ".jpg", ".tga", ".bmp" }; +const int kTexFileExtensionsSize = sizeof(g_texFileExtensions) / sizeof(g_texFileExtensions[0]); + +TexturesManager* g_texturesManager = nullptr; + +TexturesManager::TexturesManager() +{ + m_notex = nullptr; +} + +TexturesManager::~TexturesManager() +{ + +} + +void TexturesManager::Init() +{ + stbi_set_flip_vertically_on_load(true); + + m_notex = LoadTexture2D("content/textures/system/notex.png", true); + if (!m_notex) { + Logger::error("TexturesManager::Init: Failed to initialize system texture! 'system/notex.png' is not exist."); + } +} + +void TexturesManager::Shutdown() +{ + if (!m_textures.empty()) { + Msg("--- unfreed textures ---"); + + for (std::vector::iterator it = m_textures.begin(); it != m_textures.end(); ++it) { + Msg("%s", (*it)->m_textureFileName.c_str()); + delete* it; + *it = nullptr; + } + + m_textures.clear(); + } +} + +void TexturesManager::SetTexture(int slot, Texture2D* texture) +{ + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, texture ? texture->GetHandle() : 0); +} + +Texture2D* TexturesManager::CreateManual2D(const char* name, uint32_t width, uint32_t height, PixelFormat format, bool useAsRenderTarget) +{ + for (std::vector::iterator it = m_textures.begin(); it != m_textures.end(); ++it) { + if ((*it)->m_textureFileName == name) { + Logger::error("TexturesManager::CreateManual2D: texture %s is already created!", name); + } + } + + // allocate + Texture2D* texture = Texture2D::Create(); + texture->CreateRaw(nullptr, width, height, format); + texture->m_textureFileName = name; + + if (useAsRenderTarget) + Msg("Created rt texture [%s]", name); + + return texture; +} + +bool IsSupportedExtension(const char* filename) +{ + std::string ext = fs::getFileExtension(filename); + + for (int i = 0; i < kTexFileExtensionsSize; i++) { + if (ext == g_texFileExtensions[i]) { + return true; + } + } + + return false; +} + +Texture2D* TexturesManager::LoadTexture2D(const char* texturename, bool useMipmaps /*= false*/) +{ + int texturesNbr = m_textures.size(); + for (int i = 0; i < texturesNbr; i++) { + if (m_textures[i]->m_textureFileName == texturename) + return m_textures[i]; + } + + if (strcmp(texturename, "$white$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$white$"; + tex->CreateWhiteTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$black$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$black$"; + tex->CreateBlackTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$gray$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->m_textureFileName = "$gray$"; + tex->CreateGrayTexture(16, 16, 3); + m_textures.push_back(tex); + return tex; + } + if (strcmp(texturename, "$gray_console$") == 0) { + Texture2D* tex = Texture2D::Create(); + tex->CreateTexture_Generator(16, 16, 3, 32); + m_textures.push_back(tex); + return tex; + } + + if (strlen(texturename) <= 0) { + return m_notex; + } + + std::string texnamebuf; + + // find texture from disk + for (int i = 0; i < kTexFileExtensionsSize; i++) + { + std::string textureFilename = fs::getFileNameWithoutExtension(texturename); + textureFilename += g_texFileExtensions[i]; + + if (g_fileManager->FileExist(textureFilename.c_str())) + { + texnamebuf = textureFilename; + break; + } + } + + if (!texnamebuf.empty()) { + Texture2D* texture = Texture2D::Create(); + texture->CreateFromFile(texnamebuf.c_str()); + + if (useMipmaps) + texture->GenerateMipmaps(); + + Msg("loaded %s", fs::getFilenameWithoutPathAndExtension(texturename).c_str()); + m_textures.push_back(texture); + return texture; + } + else if (texnamebuf.empty() && m_notex) { + Msg("not found %s", fs::getFilenameWithoutPathAndExtension(texturename).c_str()); + return m_notex; + } + + return nullptr; +} diff --git a/src/engine/render/texturesmanager.h b/src/engine/render/texturesmanager.h new file mode 100644 index 0000000..be49018 --- /dev/null +++ b/src/engine/render/texturesmanager.h @@ -0,0 +1,39 @@ +#ifndef TEXTURESMANAGER_H +#define TEXTURESMANAGER_H + +#include +#include + +#include "render/render_shared.h" + +class Texture2D; + +class TexturesManager +{ +public: + TexturesManager(); + ~TexturesManager(); + + void Init(); + void Shutdown(); + + void SetTexture(int slot, Texture2D* texture); + + Texture2D* CreateManual2D( + const char* name, + uint32_t width, + uint32_t height, + PixelFormat format, + bool useAsRenderTarget = false); + + Texture2D* LoadTexture2D(const char* texturename, bool useMipmaps = false); + +private: + std::vector m_textures; + + Texture2D* m_notex; +}; + +extern TexturesManager* g_texturesManager; + +#endif // !TEXTURESMANAGER_H diff --git a/src/engine/render/ui.cpp b/src/engine/render/ui.cpp new file mode 100644 index 0000000..5891881 --- /dev/null +++ b/src/engine/render/ui.cpp @@ -0,0 +1,460 @@ +#include +#include +#include +#include "utils/logger.h" +#include "render/ui.h" +#include "render/vertexbuffer.h" +#include "render/indexbuffer.h" +#include "render/renderdevice.h" +#include "render/shadersystem.h" +#include "render/texturesmanager.h" +#include "render/texture2d.h" + +struct DrawInfo +{ + uint16_t vxcount; + uint16_t idxcount; +}; + +struct UIGlobals { + VertexBuffer* vb; + IndexBuffer* ib; + Shader* shader; + Texture2D* activetexture = nullptr; + Texture2D* defaultTexture = nullptr; + + UIVertex* vertices = nullptr; + uint16_t* indices = nullptr; + DrawInfo drawInfo[MAX_UI_VERTICES]; + uint16_t count = 0; + uint16_t position = 0; + uint16_t Indexposition = 0; + uint16_t currentIdx = 0; +} g_ui; + + +void uiInit() +{ + ////////////////////////////////////////////////////////////////////////// + // Buffer and context state + + // Buffer creation + g_ui.vb = g_renderDevice->CreateVertexBuffer(nullptr, MAX_UI_VERTICES, true); + g_ui.ib = g_renderDevice->CreateIndexBuffer(nullptr, MAX_UI_INDICES, true); + + // Create shader + + InputLayoutDesc_t inputLayout[] = + { + { VERTEXATTR_VEC2, SHADERSEMANTIC_POSITION }, + { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }, + { VERTEXATTR_VEC4, SHADERSEMANTIC_COLOR }, + }; + + g_ui.shader = g_shaderSystem->CreateShader("ui", "content/shaders/ui_base.vs", "content/shaders/ui_tex.ps", inputLayout, sizeof(inputLayout) / sizeof(inputLayout[0])); + g_ui.shader->m_stride = sizeof( UIVertex ); + + g_ui.defaultTexture = g_texturesManager->LoadTexture2D("$white$"); +} + +void uiShutdown() +{ + delete g_ui.ib; + delete g_ui.vb; +} + +void uiDumpBuffers() +{ + Msg( "--- UI Vertex Buffer ---" ); + + g_ui.vertices = (UIVertex*)g_ui.vb->MapBuffer(BufferAccess::WRITE_ONLY); + assert(g_ui.vertices); + + for ( int i = 0; i < g_ui.position; i++ ) + { + Msg( "%i: POSITION = %.2f %.2f", i, g_ui.vertices[ i ].position.x, g_ui.vertices[ i ].position.y ); + Msg( "%i: TEXCOORD = %.2f %.2f", i, g_ui.vertices[ i ].uv.x, g_ui.vertices[ i ].uv.y ); + Msg( "%i: COLOR = %.2f %.2f %.2f %.2f", i, + g_ui.vertices[ i ].color.x, + g_ui.vertices[ i ].color.y, + g_ui.vertices[ i ].color.z, + g_ui.vertices[ i ].color.w); + } + + Msg( "------------------------" ); + + g_ui.vb->UnmapBuffer(); +} + +void uiBeginRender() +{ + g_ui.vertices = (UIVertex*)g_ui.vb->MapBuffer(BufferAccess::WRITE_ONLY); + assert(g_ui.vertices); + + g_ui.indices = (uint16_t*)g_ui.ib->MapBuffer(BufferAccess::WRITE_ONLY); + assert(g_ui.indices); +} + +void uiDrawQuad(const Vec2& a, const Vec2& b, const Vec2& c, const Vec2& d, const Vec4& color) +{ + DrawInfo& drawInfo = g_ui.drawInfo[g_ui.count]; + drawInfo.vxcount = 4; + drawInfo.idxcount = 6; + + g_ui.indices[g_ui.Indexposition + 0] = g_ui.currentIdx; + g_ui.indices[g_ui.Indexposition + 1] = g_ui.currentIdx + 1; + g_ui.indices[g_ui.Indexposition + 2] = g_ui.currentIdx + 2; + g_ui.indices[g_ui.Indexposition + 3] = g_ui.currentIdx; + g_ui.indices[g_ui.Indexposition + 4] = g_ui.currentIdx + 2; + g_ui.indices[g_ui.Indexposition + 5] = g_ui.currentIdx + 3; + g_ui.vertices[g_ui.position + 0].position = a; g_ui.vertices[g_ui.position + 0].color = color; + g_ui.vertices[g_ui.position + 1].position = b; g_ui.vertices[g_ui.position + 1].color = color; + g_ui.vertices[g_ui.position + 2].position = c; g_ui.vertices[g_ui.position + 2].color = color; + g_ui.vertices[g_ui.position + 3].position = d; g_ui.vertices[g_ui.position + 3].color = color; + + // texcoord + g_ui.vertices[g_ui.position + 0].uv = Vec2{ 0.0f, 1.0f }; + g_ui.vertices[g_ui.position + 1].uv = Vec2{ 1.0f, 1.0f }; + g_ui.vertices[g_ui.position + 2].uv = Vec2{ 1.0f, 0.0f }; + g_ui.vertices[g_ui.position + 3].uv = Vec2{ 0.0f, 0.0f }; + + g_ui.currentIdx += 4; + g_ui.Indexposition += 6; + g_ui.position += 4; + g_ui.count++; +} + +static inline float Rsqrt(float x) { return 1.0f / sqrtf(x); } +#define NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = Rsqrt(d2); VX *= inv_len; VY *= inv_len; } } (void)0 + +void uiDrawLines(const Vec2* drawPoints, const size_t pointsCount, bool closed, const Vec4& color) +{ + float thickness = 2.1f; + + const int count = closed ? pointsCount : pointsCount - 1; + const int vtxCount = (count) * 4; + const int idxCount = (count) * 6; + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1 + 1) == pointsCount ? 0 : i1 + 1; + const Vec2& p1 = drawPoints[i1]; + const Vec2& p2 = drawPoints[i2]; + + float dx = p2.x - p1.x; + float dy = p2.y - p1.y; + NORMALIZE2F_OVER_ZERO(dx, dy); + dx *= (thickness * 0.5f); + dy *= (thickness * 0.5f); + + g_ui.vertices[g_ui.position + 0].position.x = p1.x + dy; g_ui.vertices[g_ui.position + 0].position.y = p1.y - dx; g_ui.vertices[g_ui.position + 0].color = color; + g_ui.vertices[g_ui.position + 1].position.x = p2.x + dy; g_ui.vertices[g_ui.position + 1].position.y = p2.y - dx; g_ui.vertices[g_ui.position + 1].color = color; + g_ui.vertices[g_ui.position + 2].position.x = p2.x - dy; g_ui.vertices[g_ui.position + 2].position.y = p2.y + dx; g_ui.vertices[g_ui.position + 2].color = color; + g_ui.vertices[g_ui.position + 3].position.x = p1.x - dy; g_ui.vertices[g_ui.position + 3].position.y = p1.y + dx; g_ui.vertices[g_ui.position + 3].color = color; + + g_ui.indices[g_ui.Indexposition + 0] = g_ui.currentIdx; + g_ui.indices[g_ui.Indexposition + 1] = g_ui.currentIdx + 1; + g_ui.indices[g_ui.Indexposition + 2] = g_ui.currentIdx + 2; + g_ui.indices[g_ui.Indexposition + 3] = g_ui.currentIdx; + g_ui.indices[g_ui.Indexposition + 4] = g_ui.currentIdx + 2; + g_ui.indices[g_ui.Indexposition + 5] = g_ui.currentIdx + 3; + g_ui.currentIdx += 4; + g_ui.Indexposition += 6; + g_ui.position += 4; + + DrawInfo& drawInfo = g_ui.drawInfo[g_ui.count]; + drawInfo.vxcount = 4; + drawInfo.idxcount = 6; + g_ui.count++; + } +} + +void uiDrawRect(const Vec2& position, const Vec2& size, const Vec4& color) +{ + Vec2 quad[4]; + quad[0] = { position.x, position.y }; + quad[1] = { position.x + size.x, position.y }; + quad[2] = { position.x + size.x, position.y + size.y }; + quad[3] = { position.x, position.y + size.y }; + uiDrawQuad(quad[0], quad[1], quad[2], quad[3], color); +} + +void uiSetTexture(Texture2D* texture) +{ + g_ui.activetexture = texture ? texture : g_ui.defaultTexture; +} + +void uiSetTextureByName(const char* filename) +{ + uiSetTexture( g_texturesManager->LoadTexture2D( filename ) ); +} + +void uiEndRender() +{ + g_ui.indices = nullptr; + g_ui.vertices = nullptr; + + g_ui.ib->UnmapBuffer(); + g_ui.vb->UnmapBuffer(); + + int x = 0, y = 0, w = g_renderView.width, h = g_renderView.height; + g_renderDevice->SetViewport(x, y, w, h); + + float L = x; + float R = x + w; + float T = y; + float B = y + h; +#if defined(GL_CLIP_ORIGIN) + if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left +#endif + const float orthoProjection[4][4] = + { + { 2.0f / (R - L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f / (T - B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R + L) / (L - R), (T + B) / (B - T), 0.0f, 1.0f }, + }; + + if ( g_ui.activetexture == nullptr ) + g_ui.activetexture = g_ui.defaultTexture; + + g_texturesManager->SetTexture( 0, g_ui.activetexture ); + g_ui.activetexture->setMin(TextureFilter::Linear); + g_ui.activetexture->setMag(TextureFilter::Linear); + g_ui.activetexture->setWrapS(TextureWrap::ClampToEdge); + g_ui.activetexture->setWrapT(TextureWrap::ClampToEdge); + + g_shaderSystem->SetShader( g_ui.shader ); + g_shaderSystem->SetUniformMatrix( g_ui.shader, UNIFORM_PROJ_MATRIX, orthoProjection ); + + g_renderDevice->SetVerticesBuffer( g_ui.vb ); + g_renderDevice->SetIndicesBuffer( g_ui.ib ); + + // Enable blending + g_renderDevice->SetBlending( true ); + g_renderDevice->SetBlendingFunction( BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA ); + + // Disable depth + g_renderDevice->SetDepthTest( false ); + g_renderDevice->SetBackfaceCull( false ); + + g_renderDevice->DrawElements( PT_TRIANGLES, g_ui.Indexposition, true ); + + // Uncomment for UI VB debugging + //uiDumpBuffers(); + + g_ui.currentIdx = 0; + g_ui.Indexposition = 0; + g_ui.position = 0; + g_ui.count = 0; +} + +typedef struct Character { + int codePoint, x, y, width, height, originX, originY; +} Character; + +typedef struct Font { + const char* name; + int size, bold, italic, width, height, characterCount; + Character* characters; +} Font; + +static Character characters_Courier_New[] = { + {' ', 36, 61, 3, 3, 1, 1}, + {'!', 498, 0, 10, 20, -1, 16}, + {'"', 423, 43, 14, 13, 1, 16}, + {'#', 160, 0, 16, 22, 2, 17}, + {'$', 47, 0, 16, 23, 2, 17}, + {'%', 357, 0, 16, 20, 2, 16}, + {'&', 202, 24, 15, 19, 1, 15}, + {'\'', 448, 43, 10, 13, -1, 16}, + {'(', 111, 0, 11, 23, -2, 16}, + {')', 122, 0, 11, 23, 1, 16}, + {'*', 357, 43, 16, 15, 2, 16}, + {'+', 392, 24, 18, 18, 3, 15}, + {',', 437, 43, 11, 13, -1, 6}, + {'-', 20, 61, 16, 9, 2, 10}, + {'.', 474, 43, 10, 10, -1, 6}, + {'/', 0, 0, 16, 24, 2, 18}, + {'0', 373, 0, 16, 20, 2, 16}, + {'1', 106, 24, 16, 19, 2, 16}, + {'2', 122, 24, 16, 19, 2, 16}, + {'3', 389, 0, 16, 20, 2, 16}, + {'4', 138, 24, 16, 19, 2, 16}, + {'5', 405, 0, 16, 20, 2, 16}, + {'6', 453, 0, 15, 20, 1, 16}, + {'7', 154, 24, 16, 19, 2, 16}, + {'8', 421, 0, 16, 20, 2, 16}, + {'9', 468, 0, 15, 20, 1, 16}, + {':', 292, 43, 10, 16, -1, 12}, + {';', 120, 43, 12, 18, 0, 12}, + {'<', 297, 24, 19, 18, 4, 15}, + {'=', 405, 43, 18, 13, 3, 12}, + {'>', 316, 24, 19, 18, 3, 15}, + {'?', 483, 0, 15, 20, 1, 16}, + {'@', 232, 0, 15, 21, 2, 16}, + {'A', 217, 24, 20, 18, 4, 15}, + {'B', 410, 24, 18, 18, 3, 15}, + {'C', 285, 0, 18, 20, 3, 16}, + {'D', 428, 24, 18, 18, 3, 15}, + {'E', 446, 24, 18, 18, 3, 15}, + {'F', 54, 43, 17, 18, 2, 15}, + {'G', 303, 0, 18, 20, 3, 16}, + {'H', 464, 24, 18, 18, 3, 15}, + {'I', 88, 43, 16, 18, 2, 15}, + {'J', 0, 24, 18, 19, 2, 15}, + {'K', 335, 24, 19, 18, 3, 15}, + {'L', 482, 24, 18, 18, 3, 15}, + {'M', 237, 24, 20, 18, 4, 15}, + {'N', 354, 24, 19, 18, 4, 15}, + {'O', 321, 0, 18, 20, 3, 16}, + {'P', 71, 43, 17, 18, 2, 15}, + {'Q', 142, 0, 18, 22, 3, 16}, + {'R', 373, 24, 19, 18, 3, 15}, + {'S', 437, 0, 16, 20, 2, 16}, + {'T', 0, 43, 18, 18, 3, 15}, + {'U', 18, 24, 18, 19, 3, 15}, + {'V', 257, 24, 20, 18, 4, 15}, + {'W', 277, 24, 20, 18, 4, 15}, + {'X', 18, 43, 18, 18, 3, 15}, + {'Y', 36, 43, 18, 18, 3, 15}, + {'Z', 104, 43, 16, 18, 2, 15}, + {'[', 63, 0, 12, 23, -1, 16}, + {'\\', 16, 0, 16, 24, 2, 18}, + {']', 75, 0, 12, 23, 1, 16}, + {'^', 389, 43, 16, 14, 2, 17}, + {'_', 0, 61, 20, 9, 4, -1}, + {'`', 484, 43, 10, 10, -1, 17}, + {'a', 132, 43, 18, 17, 3, 13}, + {'b', 247, 0, 19, 20, 4, 16}, + {'c', 186, 43, 17, 17, 2, 13}, + {'d', 266, 0, 19, 20, 3, 16}, + {'e', 150, 43, 18, 17, 3, 13}, + {'f', 72, 24, 17, 19, 2, 16}, + {'g', 214, 0, 18, 21, 3, 13}, + {'h', 36, 24, 18, 19, 3, 16}, + {'i', 170, 24, 16, 19, 2, 16}, + {'j', 32, 0, 15, 24, 2, 16}, + {'k', 54, 24, 18, 19, 3, 16}, + {'l', 186, 24, 16, 19, 2, 16}, + {'m', 219, 43, 20, 16, 4, 13}, + {'n', 239, 43, 18, 16, 3, 13}, + {'o', 168, 43, 18, 17, 3, 13}, + {'p', 176, 0, 19, 21, 4, 13}, + {'q', 195, 0, 19, 21, 3, 13}, + {'r', 275, 43, 17, 16, 2, 13}, + {'s', 203, 43, 16, 17, 2, 13}, + {'t', 89, 24, 17, 19, 2, 15}, + {'u', 257, 43, 18, 16, 3, 12}, + {'v', 302, 43, 19, 15, 3, 12}, + {'w', 321, 43, 18, 15, 3, 12}, + {'x', 339, 43, 18, 15, 3, 12}, + {'y', 339, 0, 18, 20, 3, 12}, + {'z', 373, 43, 16, 15, 2, 12}, + {'{', 87, 0, 12, 23, 0, 16}, + {'|', 133, 0, 9, 23, -2, 16}, + {'}', 99, 0, 12, 23, 0, 16}, + {'~', 458, 43, 16, 11, 2, 11}, +}; + +static Font font_Courier_New = { "Courier New", 20, 1, 0, 512, 128, 95, characters_Courier_New }; + +static Character characters_Arial[] = { + {' ', 10, 61, 3, 3, 1, 1}, + {'!', 205, 34, 5, 15, 0, 14}, + {'"', 202, 49, 8, 7, 1, 14}, + {'#', 187, 19, 12, 15, 1, 14}, + {'$', 71, 0, 12, 18, 1, 15}, + {'%', 162, 0, 16, 15, 0, 14}, + {'&', 208, 0, 14, 15, 1, 14}, + {'\'', 210, 49, 5, 7, 1, 14}, + {'(', 35, 0, 7, 19, 0, 14}, + {')', 42, 0, 7, 19, 0, 14}, + {'*', 193, 49, 9, 7, 1, 14}, + {'+', 161, 49, 11, 10, 0, 11}, + {',', 215, 49, 5, 7, 0, 3}, + {'-', 246, 49, 8, 4, 1, 6}, + {'.', 0, 61, 5, 4, 0, 3}, + {'/', 177, 34, 7, 15, 1, 14}, + {'0', 199, 19, 12, 15, 1, 14}, + {'1', 184, 34, 7, 15, -1, 14}, + {'2', 84, 34, 11, 15, 1, 14}, + {'3', 211, 19, 12, 15, 1, 14}, + {'4', 223, 19, 12, 15, 1, 14}, + {'5', 235, 19, 12, 15, 1, 14}, + {'6', 0, 34, 12, 15, 1, 14}, + {'7', 12, 34, 12, 15, 1, 14}, + {'8', 24, 34, 12, 15, 1, 14}, + {'9', 36, 34, 12, 15, 1, 14}, + {':', 156, 49, 5, 11, 0, 10}, + {';', 223, 34, 5, 14, 0, 10}, + {'<', 134, 49, 11, 11, 0, 12}, + {'=', 182, 49, 11, 8, 0, 10}, + {'>', 145, 49, 11, 11, 0, 12}, + {'?', 95, 34, 11, 15, 1, 14}, + {'@', 0, 0, 19, 19, 0, 14}, + {'A', 222, 0, 14, 15, 1, 14}, + {'B', 48, 34, 12, 15, 0, 14}, + {'C', 236, 0, 14, 15, 0, 14}, + {'D', 70, 19, 13, 15, 0, 14}, + {'E', 60, 34, 12, 15, 0, 14}, + {'F', 72, 34, 12, 15, 0, 14}, + {'G', 0, 19, 14, 15, 0, 14}, + {'H', 83, 19, 13, 15, 0, 14}, + {'I', 210, 34, 5, 15, 0, 14}, + {'J', 139, 34, 10, 15, 1, 14}, + {'K', 96, 19, 13, 15, 0, 14}, + {'L', 106, 34, 11, 15, 0, 14}, + {'M', 178, 0, 15, 15, 0, 14}, + {'N', 109, 19, 13, 15, 0, 14}, + {'O', 193, 0, 15, 15, 0, 14}, + {'P', 122, 19, 13, 15, 0, 14}, + {'Q', 83, 0, 16, 16, 1, 14}, + {'R', 14, 19, 14, 15, 0, 14}, + {'S', 135, 19, 13, 15, 1, 14}, + {'T', 148, 19, 13, 15, 1, 14}, + {'U', 161, 19, 13, 15, 0, 14}, + {'V', 28, 19, 14, 15, 1, 14}, + {'W', 143, 0, 19, 15, 1, 14}, + {'X', 42, 19, 14, 15, 1, 14}, + {'Y', 56, 19, 14, 15, 1, 14}, + {'Z', 174, 19, 13, 15, 1, 14}, + {'[', 49, 0, 6, 19, 0, 14}, + {'\\', 191, 34, 7, 15, 1, 14}, + {']', 55, 0, 6, 19, 1, 14}, + {'^', 172, 49, 10, 9, 1, 14}, + {'_', 232, 49, 14, 4, 2, -1}, + {'`', 5, 61, 5, 4, 0, 14}, + {'a', 15, 49, 12, 12, 1, 11}, + {'b', 117, 34, 11, 15, 0, 14}, + {'c', 51, 49, 11, 12, 1, 11}, + {'d', 128, 34, 11, 15, 1, 14}, + {'e', 27, 49, 12, 12, 1, 11}, + {'f', 169, 34, 8, 15, 1, 14}, + {'g', 99, 0, 11, 16, 1, 11}, + {'h', 149, 34, 10, 15, 0, 14}, + {'i', 215, 34, 4, 15, 0, 14}, + {'j', 61, 0, 6, 19, 2, 14}, + {'k', 159, 34, 10, 15, 0, 14}, + {'l', 219, 34, 4, 15, 0, 14}, + {'m', 228, 34, 15, 12, 0, 11}, + {'n', 106, 49, 10, 12, 0, 11}, + {'o', 39, 49, 12, 12, 1, 11}, + {'p', 110, 0, 11, 16, 0, 11}, + {'q', 121, 0, 11, 16, 1, 11}, + {'r', 126, 49, 8, 12, 0, 11}, + {'s', 62, 49, 11, 12, 1, 11}, + {'t', 198, 34, 7, 15, 1, 14}, + {'u', 116, 49, 10, 12, 0, 11}, + {'v', 73, 49, 11, 12, 1, 11}, + {'w', 0, 49, 15, 12, 1, 11}, + {'x', 84, 49, 11, 12, 1, 11}, + {'y', 132, 0, 11, 16, 1, 11}, + {'z', 95, 49, 11, 12, 1, 11}, + {'{', 19, 0, 8, 19, 1, 14}, + {'|', 67, 0, 4, 19, 0, 14}, + {'}', 27, 0, 8, 19, 1, 14}, + {'~', 220, 49, 12, 5, 1, 9}, +}; + +static Font font_Arial = { "Arial", 18, 0, 0, 256, 128, 95, characters_Arial }; diff --git a/src/engine/render/ui.h b/src/engine/render/ui.h new file mode 100644 index 0000000..84adcd9 --- /dev/null +++ b/src/engine/render/ui.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#define MAX_UI_VERTICES 2048 * 4 +#define MAX_UI_INDICES 2048 * 4 + +class Texture2D; + +struct UIVertex +{ + Vec2 position; + Vec2 uv; + Vec4 color; +}; + +void uiInit(); +void uiShutdown(); + +void uiBeginRender(); + +void uiDrawQuad(const Vec2& a, const Vec2& b, const Vec2& c, const Vec2& d, const Vec4& color); +void uiDrawLines(const Vec2* drawPoints, const size_t pointsCount, bool closed, const Vec4& color); +void uiDrawRect(const Vec2& position, const Vec2& size, const Vec4& color); + +void uiSetTexture(Texture2D* texture); +void uiSetTextureByName(const char* filename); + +void uiEndRender(); \ No newline at end of file diff --git a/src/engine/render/vertexbuffer.cpp b/src/engine/render/vertexbuffer.cpp new file mode 100644 index 0000000..d9291ba --- /dev/null +++ b/src/engine/render/vertexbuffer.cpp @@ -0,0 +1,62 @@ +#include "render/vertexbuffer.h" +#include "render/gl_shared.h" + +VertexBuffer::VertexBuffer(void* data, size_t size, bool isStream /*= false*/) +{ + glGenBuffers(1, &m_buffer); + glBindBuffer(GL_ARRAY_BUFFER, m_buffer); + glBufferData(GL_ARRAY_BUFFER, size, data, isStream ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + //if (isStream) { + // Logger::msg("created dynamic vertex stream ..."); + //} +} + +VertexBuffer::~VertexBuffer() +{ + glDeleteBuffers(1, &m_buffer); +} + +void VertexBuffer::Bind() +{ + glBindBuffer(GL_ARRAY_BUFFER, m_buffer); +} + +void* VertexBuffer::MapBuffer(BufferAccess access) +{ + GLenum accessGl = 0; + + switch (access) + { + case BufferAccess::READ_ONLY: + accessGl = GL_READ_ONLY; + break; + case BufferAccess::WRITE_ONLY: + accessGl = GL_WRITE_ONLY; + break; + case BufferAccess::READ_WRITE: + accessGl = GL_READ_WRITE; + break; + } + + Bind(); + + void* ptr = glMapBuffer(GL_ARRAY_BUFFER, accessGl); + + //check_for_opengl_error(); + + return ptr; +} + +void VertexBuffer::UnmapBuffer() +{ + Bind(); + glUnmapBuffer(GL_ARRAY_BUFFER); +} + +void VertexBuffer::UpdateBuffer(void* data, size_t size) +{ + glBufferData(GL_ARRAY_BUFFER, size, data, GL_DYNAMIC_DRAW); + GL_CHECK_ERROR(); +} diff --git a/src/engine/render/vertexbuffer.h b/src/engine/render/vertexbuffer.h new file mode 100644 index 0000000..c793518 --- /dev/null +++ b/src/engine/render/vertexbuffer.h @@ -0,0 +1,28 @@ +#ifndef VERTEXOBJECT_H +#define VERTEXOBJECT_H + +#include +#include "render/render_shared.h" + +class RenderDevice; + +class VertexBuffer +{ + friend class RenderDevice; +public: + ~VertexBuffer(); + + void Bind(); + + void* MapBuffer(BufferAccess access); + void UnmapBuffer(); + + void UpdateBuffer(void* data, size_t size); + +private: + VertexBuffer(void* data, size_t size, bool isStream = false); + + uint32_t m_buffer; +}; + +#endif // !VERTEXOBJECT_H diff --git a/src/engine/sound/sound_system.cpp b/src/engine/sound/sound_system.cpp new file mode 100644 index 0000000..c0bdb4f --- /dev/null +++ b/src/engine/sound/sound_system.cpp @@ -0,0 +1,3 @@ +#include "sound_system.h" + +ISoundSystem* g_soundSystem = nullptr; diff --git a/src/engine/sound/sound_system.h b/src/engine/sound/sound_system.h new file mode 100644 index 0000000..e991022 --- /dev/null +++ b/src/engine/sound/sound_system.h @@ -0,0 +1,53 @@ +#ifndef SOUND_SYSTEM_H +#define SOUND_SYSTEM_H +#pragma once + +class ISound +{ +public: + virtual ~ISound () { } + + virtual void play () = 0; + virtual void Stop () = 0; + virtual void Pause () = 0; + + virtual bool isPlaying () const = 0; + virtual bool isPaused () const = 0; + + virtual bool IsLooped () const = 0; + virtual void SetLooped (bool bLooped) = 0; + + virtual float GetVolume () const = 0; + virtual void SetVolume (float fVolume) = 0; + + virtual void OnFrame (float delta) = 0; +}; + +typedef unsigned int SoundDeviceID; + +class ISoundSystem +{ +public: + virtual ~ISoundSystem () { } + + virtual void selectDevice (SoundDeviceID deviceId) = 0; + virtual SoundDeviceID selectedDevice () const = 0; + + virtual ISound* CreateSound (const char* sSoundFile) = 0; + virtual void DestroySound (ISound*& pSound) = 0; + + virtual void Update (float delta) = 0; + + virtual void StopAllSounds () = 0; + virtual void PauseAllSounds () = 0; + virtual void ReleaseAllSounds() = 0; + + virtual float GetMasterVolume () const = 0; + virtual void SetMasterVolume (float fVolume) = 0; +}; + +ISoundSystem* CreateSoundSystem_DirectSound(void* wndHandle); + +extern ISoundSystem* g_soundSystem; + +#endif diff --git a/src/engine/sound/sound_system_null.cpp b/src/engine/sound/sound_system_null.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/engine/sound/soundsys_dsound.cpp b/src/engine/sound/soundsys_dsound.cpp new file mode 100644 index 0000000..f55210d --- /dev/null +++ b/src/engine/sound/soundsys_dsound.cpp @@ -0,0 +1,378 @@ +#include +#include "utils/logger.h" +#include "filesystem/stream.h" +#include "filesystem/filemanager.h" +#include "sound/sound_system.h" + +#undef Msg +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#pragma comment(lib, "dsound.lib") + +IDirectSound8* g_DirectSound = nullptr; +IDirectSoundBuffer* g_PrimaryBuffer = nullptr; + +///////////////////////////////////////////////////////////////////////////////////////// +// Sound implementation + +class Sound_DSound_Impl : public ISound +{ +public: + Sound_DSound_Impl(const char* sSoundFile); + ~Sound_DSound_Impl(); + + void play() override; + void Stop() override; + void Pause() override; + + bool isPlaying() const override; + bool isPaused() const override; + + bool IsLooped() const override; + void SetLooped(bool bLooped) override; + + float GetVolume() const override; + void SetVolume(float fVolume) override; + + void OnFrame(float delta) override; + +private: + IDirectSoundBuffer8* m_pBuffer; + bool m_IsLopped; +}; + +Sound_DSound_Impl::Sound_DSound_Impl(const char* sSoundFile) : m_pBuffer(nullptr), m_IsLopped(false) +{ + if (!strstr(sSoundFile, ".wav")) + Logger::error("Failed to load sound file '%s'. Only wav supported", sSoundFile); + + StreamBase* pStream = g_fileManager->OpenStream( sSoundFile, FileAccess::Read ); + assert( pStream ); + + pStream->seek(SeekDir::End, 0); + size_t length = pStream->tell(); + pStream->seek(SeekDir::Begin, 0); + + uint8_t* filedata = (uint8_t*)malloc(length); + pStream->readBuffer(filedata, length); + + delete pStream; + pStream = nullptr; + + DSBUFFERDESC bufferDesc = {}; + + + //g_DirectSound->CreateSoundBuffer( as ) + +} + +Sound_DSound_Impl::~Sound_DSound_Impl() +{ + +} + +void Sound_DSound_Impl::play() +{ + m_pBuffer->Play(0, 0, m_IsLopped ? DSBPLAY_LOOPING : 0); +} + +void Sound_DSound_Impl::Stop() +{ + m_pBuffer->Stop(); +} + +void Sound_DSound_Impl::Pause() +{ + +} + +bool Sound_DSound_Impl::isPlaying() const +{ + return false; +} + +bool Sound_DSound_Impl::isPaused() const +{ + return false; +} + +bool Sound_DSound_Impl::IsLooped() const +{ + return m_IsLopped; +} + +void Sound_DSound_Impl::SetLooped(bool bLooped) +{ + m_IsLopped = bLooped; +} + +float Sound_DSound_Impl::GetVolume() const +{ + return 1.0f; +} + +void Sound_DSound_Impl::SetVolume(float fVolume) +{ + +} + +void Sound_DSound_Impl::OnFrame(float delta) +{ + +} + +//bool cSoundBuffer::SetVolume(short percent) +//{ +// long Volume; +// if (m_buffer == NULL) +// return false; +// +// if (!percent) +// Volume = DSBVOLUME_MIN; +// else +// Volume = -20 * (100 - (percent % 101)); +// +// if (FAILED(m_buffer->SetVolume(Volume))) +// return false; return true; +//} +// +//bool cSoundBuffer::SetPan(signed long level) +//{ +// signed long Pan; +// +// if (m_buffer == NULL) +// return false; +// +// if (level < 0) +// Pan = DSBPAN_LEFT / 100 * ((-level) % 101); +// else +// Pan = DSBPAN_RIGHT / 100 * (level % 101); +// +// if (FAILED(m_buffer->SetPan(Pan))) +// return false; +// +// return true; +//} + +///////////////////////////////////////////////////////////////////////////////////////// +// Sound System implementation + +class SoundSystem_DSound : public ISoundSystem +{ +public: + SoundSystem_DSound(HWND hWndHandle); + ~SoundSystem_DSound(); + + void InitPrimaryBuffer(); + + void selectDevice(SoundDeviceID deviceId) override; + SoundDeviceID selectedDevice() const override; + + ISound* CreateSound(const char* sSoundFile) override; + void DestroySound(ISound*& pSound) override; + + void Update(float delta) override; + + void StopAllSounds() override; + void PauseAllSounds() override; + void ReleaseAllSounds() override; + + float GetMasterVolume() const override; + void SetMasterVolume(float fVolume) override; + +private: + std::vector< Sound_DSound_Impl* > m_Sounds; + float m_MasterVolume; +}; + +SoundSystem_DSound::SoundSystem_DSound(HWND hWndHandle) : + m_MasterVolume(0.0f) +{ + Logger::logPrint("Starting SOUND system ..."); + + HRESULT hr = DirectSoundCreate8(NULL, &g_DirectSound, NULL); + if (FAILED(hr)) + Logger::error("Failed to initialize DirectSound"); + + // Set cooperative level to the engine window + hr = g_DirectSound->SetCooperativeLevel(hWndHandle, DSSCL_PRIORITY); + if (FAILED(hr)) + Logger::error("g_DirectSound->SetCooperativeLevel(...) Failed!"); + + InitPrimaryBuffer(); + + Logger::logPrint("SoundSystem_DSound: Created device successfully"); +} + +SoundSystem_DSound::~SoundSystem_DSound() +{ + ReleaseAllSounds(); + + if (g_PrimaryBuffer) + { + g_PrimaryBuffer->Release(); + g_PrimaryBuffer = nullptr; + } + + if (g_DirectSound) + { + g_DirectSound->Release(); + g_DirectSound = nullptr; + } +} + +void SoundSystem_DSound::InitPrimaryBuffer() +{ + // Create primary buffer. + DSBUFFERDESC dsbd; + memset(&dsbd, 0, sizeof(DSBUFFERDESC)); + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER; + dsbd.dwBufferBytes = 0; + + HRESULT hr = g_DirectSound->CreateSoundBuffer(&dsbd, &g_PrimaryBuffer, NULL); + if (FAILED(hr)) + Logger::error("SoundSystem_DSound::InitPrimaryBuffer: Failed to create primary buffer"); + + const int bitCount = 16; + const int channelCount = 2; + const int sampleRate22k = 22050; + const int sampleRate44k = 44100; + + // calculate buffer format + + WAVEFORMATEX waveFormat; + memset(&waveFormat, 0, sizeof(waveFormat)); + waveFormat.cbSize = sizeof(WAVEFORMATEX); + waveFormat.wFormatTag = WAVE_FORMAT_PCM; + waveFormat.nChannels = channelCount; + waveFormat.nSamplesPerSec = sampleRate44k; + waveFormat.nBlockAlign = channelCount * bitCount / 8; + waveFormat.nAvgBytesPerSec = sampleRate44k * waveFormat.nBlockAlign; + waveFormat.wBitsPerSample = sampleRate44k; + + // For safety only :) + g_PrimaryBuffer->SetFormat(&waveFormat); + g_PrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING); + + Logger::logPrint("SoundSystem_DSound::InitPrimaryBuffer: Created primary buffer with %i sample rate", waveFormat.nSamplesPerSec); +} + +void SoundSystem_DSound::selectDevice(SoundDeviceID deviceId) +{ + // Stub method +} + +SoundDeviceID SoundSystem_DSound::selectedDevice() const +{ + // Stub method + return 0; +} + +// SOUND Creation + +ISound* SoundSystem_DSound::CreateSound(const char* sSoundFile) +{ + Sound_DSound_Impl* pSoundImpl = new Sound_DSound_Impl(sSoundFile); + m_Sounds.push_back(pSoundImpl); + return pSoundImpl; +} + +void SoundSystem_DSound::DestroySound(ISound*& pSound) +{ + // find sound in the cache + + std::vector< Sound_DSound_Impl* >::iterator it = std::find(m_Sounds.begin(), m_Sounds.end(), pSound); + if (it != m_Sounds.end()) + { + m_Sounds.erase( it ); + } + + // delete sound + delete pSound; + pSound = nullptr; +} + +LONG dsu_CalcVolume(const float Volume) +{ + LONG Result = 0; + if (Volume == 0) + Result = -10000; + else + Result = -round(1000 * log(1 / Volume)); + return Result; +} + +void SoundSystem_DSound::Update(float delta) +{ + if (!g_PrimaryBuffer) + Logger::error("SoundSystem_DSound::Update: Canno't update sound system without primary buffer."); + + g_PrimaryBuffer->SetVolume(dsu_CalcVolume(m_MasterVolume)); + + int soundsNbr = m_Sounds.size(); + for (int i = 0; i < soundsNbr; i++) + { + // Sanity check + if (m_Sounds[i]) + m_Sounds[i]->OnFrame( delta ); + } +} + +void SoundSystem_DSound::StopAllSounds() +{ + int soundsNbr = m_Sounds.size(); + for (int i = 0; i < soundsNbr; i++) + { + // Sanity check + if (m_Sounds[i]) + m_Sounds[i]->Stop(); + } +} + +void SoundSystem_DSound::PauseAllSounds() +{ + int soundsNbr = m_Sounds.size(); + for (int i = 0; i < soundsNbr; i++) + { + // Sanity check + if (m_Sounds[i]) + m_Sounds[i]->Pause(); + } +} + +void SoundSystem_DSound::ReleaseAllSounds() +{ + int soundsNbr = m_Sounds.size(); + for (int i = 0; i < soundsNbr; i++) + { + if (m_Sounds[i]) + { + delete m_Sounds[i]; + m_Sounds[i] = nullptr; + } + } + + m_Sounds.clear(); + + Logger::logPrint("SoundSystem_DSound::ReleaseAllSounds: Cleared %i sounds", soundsNbr); +} + +float SoundSystem_DSound::GetMasterVolume() const +{ + return m_MasterVolume; +} + +void SoundSystem_DSound::SetMasterVolume(float fVolume) +{ + m_MasterVolume = fVolume; +} + +// Sound System Factory +ISoundSystem* CreateSoundSystem_DirectSound(void* wndHandle) +{ + return new SoundSystem_DSound( (HWND)wndHandle ); +} diff --git a/src/engine/utils/logger.cpp b/src/engine/utils/logger.cpp new file mode 100644 index 0000000..f11e203 --- /dev/null +++ b/src/engine/utils/logger.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/logger.h" + +namespace +{ + static FILE* s_logFileHandle = nullptr; + static DWORD s_logOpenTime = 0; + + void logWriteMsg(const char* msg) + { + DWORD dwCurrentTime = GetTickCount() - s_logOpenTime; + + static char buffer[1024]; + int len = sprintf(buffer, "%02lu:%02lu.%02lu: %s", + dwCurrentTime / 60000, dwCurrentTime / 1000 % 60, dwCurrentTime % 1000 / 10, msg); + + if (s_logFileHandle) + { + fwrite(buffer, sizeof(char), len, s_logFileHandle); + fflush(s_logFileHandle); + } + } +} + +void Logger::init() +{ + s_logFileHandle = fopen("output.txt", "w"); + assert(s_logFileHandle); + + struct tm newtime; + __time32_t aclock; + + char buffer[32]; + _time32(&aclock); + _localtime32_s(&newtime, &aclock); + errno_t errNum = asctime_s(buffer, 32, &newtime); + + char timeBuffer[1024]; + int len = sprintf(timeBuffer, "Log started: %s", buffer); + + fwrite(timeBuffer, sizeof(char), len, s_logFileHandle); + + s_logOpenTime = GetTickCount(); +} + +void Logger::shutdown() +{ + if (s_logFileHandle) + { + fclose(s_logFileHandle); + s_logFileHandle = nullptr; + } +} + +void Logger::logPrint(const char* msg, ...) +{ + static char buffer[2048 * 2]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, sizeof(buffer), msg, args); + va_end(args); + + strcat(buffer, "\n"); + + OutputDebugStringA(buffer); + + // write to console + fwrite(buffer, sizeof(char), strlen(buffer), stdout); + + logWriteMsg(buffer); +} + +void Logger::error(const char* msg, ...) +{ + static char buffer[2048 * 2]; + va_list args; + va_start(args, msg); + vsnprintf(buffer, sizeof(buffer), msg, args); + va_end(args); + + strcat(buffer, "\n"); + + OutputDebugStringA(buffer); + + // write to console + fwrite(buffer, sizeof(char), strlen(buffer), stdout); + + logWriteMsg(buffer); + + MessageBoxA(NULL, buffer, "Engine Error", MB_OK | MB_ICONERROR); + + if (IsDebuggerPresent()) + DebugBreak(); + //else + exit(-1); +} diff --git a/src/engine/utils/logger.h b/src/engine/utils/logger.h new file mode 100644 index 0000000..c06fcdf --- /dev/null +++ b/src/engine/utils/logger.h @@ -0,0 +1,16 @@ +#ifndef LOGGER_H +#define LOGGER_H + +class Logger +{ +public: + static void init(); + static void shutdown(); + static void logPrint(const char* msg, ...); + static void error(const char* msg, ...); +}; + +#define Msg Logger::logPrint +#define LogPrint Logger::logPrint + +#endif // !LOGGER_H diff --git a/src/engine/utils/maths.cpp b/src/engine/utils/maths.cpp new file mode 100644 index 0000000..efd988c --- /dev/null +++ b/src/engine/utils/maths.cpp @@ -0,0 +1,61 @@ +#include + +Vec3 g_zeroVector = Vec3(0.0f, 0.0f, 0.0f); +Vec3 g_identityVector = Vec3(1.0f, 1.0f, 1.0f); +Matrix g_identityMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + +BoundingBox g_identityBoundingBox = { { -0.1f, -0.1f, -0.1f }, { 0.1f, 0.1f, 0.1f } }; + +void TransformBoundingBox(BoundingBox& bbox, const glm::mat4& model) +{ +#if 1 + // transform to center/extents box representation + glm::vec3 center = (bbox.max + bbox.min) * glm::vec3(0.5); + glm::vec3 extents = bbox.max - center; + + // transform center + glm::vec3 t_center = glm::vec3(model * glm::vec4(center, 1.0)); + + // transform extents (take maximum) + glm::mat3 abs_mat = glm::mat3(abs(glm::vec3(model[0])), abs(glm::vec3(model[1])), abs(glm::vec3(model[2]))); + glm::vec3 t_extents = abs_mat * extents; + + // transform to min/max box representation + glm::vec3 tmin = t_center - t_extents; + glm::vec3 tmax = t_center + t_extents; + + bbox.min = tmin; + bbox.max = tmax; +#else + + glm::mat3 M(model); + + // Split the transform into a translation vector (T) and a 3x3 rotation (M). + BoundingBox B = {}; // zero-volume AABB at T + for (int i = 0, j = 0; i < 3, j < 3; i++, j++) + { + float a = M[i][j] * bbox.min[j]; + float b = M[i][j] * bbox.max[j]; + B.min[i] += a < b ? a : b; + B.max[i] += a < b ? b : a; + } + + bbox.min = B.min; + bbox.max = B.max; +#endif +} + +void TransformOBB(OBB& obb, const BoundingBox& bbox, const glm::mat4& model) +{ + // transform to center/extents box representation + glm::vec3 center = (bbox.max + bbox.min) * glm::vec3(0.5); + glm::vec3 extents = bbox.max - center; + + // transform center + glm::vec3 t_center = glm::vec3(model * glm::vec4(center, 1.0)); + + +} diff --git a/src/engine/utils/maths.h b/src/engine/utils/maths.h new file mode 100644 index 0000000..6b28376 --- /dev/null +++ b/src/engine/utils/maths.h @@ -0,0 +1,339 @@ +// maths.h - Simple mathematics library with aggressive inlining. +#ifndef MATHS_H +#define MATHS_H + +#include +#include + +// Define M_PI +#ifndef M_PI +#define M_PI 3.14159265358979323846 // pi +#endif // !M_PI + +#define GLM_ENABLE_EXPERIMENTAL +#include +#include +#include +#include +#include +#include + +#pragma warning( disable : 4244 ) + +struct Point2 +{ + int x; + int y; +}; + +struct Vec2 +{ + float x; + float y; + + Vec2() : x(0.0f), y(0.0f) {} + Vec2(float _val) : x(_val), y(_val) {} + Vec2(float _x, float _y) : x(_x), y(_y) {} + + // Vec2 with Vec2 operations + + inline Vec2 operator+(const Vec2& rhs) const { return Vec2(x + rhs.x, y + rhs.y); } + inline Vec2 operator-(const Vec2& rhs) const { return Vec2(x - rhs.x, y - rhs.y); } + inline Vec2 operator*(const Vec2& rhs) const { return Vec2(x * rhs.x, y * rhs.y); } + inline Vec2 operator/(const Vec2& rhs) const { return Vec2(x / rhs.x, y / rhs.y); } + + inline Vec2& operator+=(const Vec2& rhs) { *this = *this + rhs; return *this; } + inline Vec2& operator*=(const Vec2& rhs) { *this = *this * rhs; return *this; } + inline Vec2& operator-=(const Vec2& rhs) { *this = *this - rhs; return *this; } + inline Vec2& operator/=(const Vec2& rhs) { *this = *this / rhs; return *this; } + + // Vec2 with Scalar operations + + inline Vec2 operator+(const float s) const { return Vec2(x + s, y + s); } + inline Vec2 operator-(const float s) const { return Vec2(x - s, y - s); } + inline Vec2 operator*(const float s) const { return Vec2(x * s, y * s); } + inline Vec2 operator/(const float s) const { return Vec2(x / s, y / s); } + + inline Vec2& operator+=(const float s) { *this = *this + s; return *this; } + inline Vec2& operator*=(const float s) { *this = *this * s; return *this; } + inline Vec2& operator-=(const float s) { *this = *this - s; return *this; } + inline Vec2& operator/=(const float s) { *this = *this / s; return *this; } +}; + +struct Vec3 +{ + float x; + float y; + float z; + + Vec3() : x(0.0f), y(0.0f), z(0.0f) {} + Vec3(float _val) : x(_val), y(_val), z(_val) {} + Vec3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + + // Vec3 with Vec2 operations + + inline Vec3 operator+(const Vec2& rhs) const { return Vec3(x + rhs.x, y + rhs.y, z); } + inline Vec3 operator-(const Vec2& rhs) const { return Vec3(x - rhs.x, y - rhs.y, z); } + inline Vec3 operator*(const Vec2& rhs) const { return Vec3(x * rhs.x, y * rhs.y, z); } + inline Vec3 operator/(const Vec2& rhs) const { return Vec3(x / rhs.x, y / rhs.y, z); } + + inline Vec3& operator+=(const Vec2& rhs) { *this = *this + rhs; return *this; } + inline Vec3& operator*=(const Vec2& rhs) { *this = *this * rhs; return *this; } + inline Vec3& operator-=(const Vec2& rhs) { *this = *this - rhs; return *this; } + inline Vec3& operator/=(const Vec2& rhs) { *this = *this / rhs; return *this; } + + // Vec3 with Vec3 operations + + inline Vec3 operator+(const Vec3& rhs) const { return Vec3(x + rhs.x, y + rhs.y, z + rhs.z); } + inline Vec3 operator-(const Vec3& rhs) const { return Vec3(x - rhs.x, y - rhs.y, z - rhs.z); } + inline Vec3 operator*(const Vec3& rhs) const { return Vec3(x * rhs.x, y * rhs.y, z * rhs.z); } + inline Vec3 operator/(const Vec3& rhs) const { return Vec3(x / rhs.x, y / rhs.y, z / rhs.z); } + + inline Vec3& operator+=(const Vec3& rhs) { *this = *this + rhs; return *this; } + inline Vec3& operator*=(const Vec3& rhs) { *this = *this * rhs; return *this; } + inline Vec3& operator-=(const Vec3& rhs) { *this = *this - rhs; return *this; } + inline Vec3& operator/=(const Vec3& rhs) { *this = *this / rhs; return *this; } + + // Vec3 with Scalar operations + + inline Vec3 operator+(const float s) const { return Vec3(x + s, y + s, z + s); } + inline Vec3 operator-(const float s) const { return Vec3(x - s, y - s, z - s); } + inline Vec3 operator*(const float s) const { return Vec3(x * s, y * s, z * s); } + inline Vec3 operator/(const float s) const { return Vec3(x / s, y / s, z / s); } + + inline Vec3& operator+=(const float s) { *this = *this + s; return *this; } + inline Vec3& operator*=(const float s) { *this = *this * s; return *this; } + inline Vec3& operator-=(const float s) { *this = *this - s; return *this; } + inline Vec3& operator/=(const float s) { *this = *this / s; return *this; } + + // Vec3 with GLM Vec3 operations (please delete someday :) + + inline Vec3 operator+(const glm::vec3& rhs) const { return Vec3(x + rhs.x, y + rhs.y, z + rhs.z); } + inline Vec3 operator-(const glm::vec3& rhs) const { return Vec3(x - rhs.x, y - rhs.y, z - rhs.z); } + inline Vec3 operator*(const glm::vec3& rhs) const { return Vec3(x * rhs.x, y * rhs.y, z * rhs.z); } + inline Vec3 operator/(const glm::vec3& rhs) const { return Vec3(x / rhs.x, y / rhs.y, z / rhs.z); } + + inline Vec3& operator+=(const glm::vec3& rhs) { *this = *this + rhs; return *this; } + inline Vec3& operator*=(const glm::vec3& rhs) { *this = *this * rhs; return *this; } + inline Vec3& operator-=(const glm::vec3& rhs) { *this = *this - rhs; return *this; } + inline Vec3& operator/=(const glm::vec3& rhs) { *this = *this / rhs; return *this; } +}; + +extern Vec3 g_zeroVector; +extern Vec3 g_identityVector; + +struct Vec4 +{ + float x; + float y; + float z; + float w; +}; + +inline Vec3 operator+(const float s, const Vec3& v) +{ + return Vec3(s + v.x, s + v.y, s + v.z); +} + +inline Vec3 operator-(const float s, const Vec3& v) +{ + return Vec3(s - v.x, s - v.y, s - v.z); +} + +inline Vec3 operator*(const float s, const Vec3& v) +{ + return Vec3(s * v.x, s * v.y, s * v.z); +} + +inline Vec3 operator/(const float s, const Vec3& v) +{ + return Vec3(s / v.x, s / v.y, s / v.z); +} + +inline float lerp(float f, float a, float b) +{ + return a * (1.0 - f) + (b * f); +} + +inline Vec3 lerp(float f, const Vec3& a, const Vec3& b) +{ + return a * (1.0 - f) + (b * f); +} + +inline bool GetBoxIntersection2D(const Vec2& boxAmins, const Vec2& boxAmaxs, + const Vec2& boxBmins, const Vec2& boxBmaxs) +{ + float xMin1 = boxAmins.x; + float yMin1 = boxAmins.y; + + float xMax1 = boxAmaxs.x; + float yMax1 = boxAmaxs.y; + + float xMin2 = boxBmins.x; + float yMin2 = boxBmins.y; + + float xMax2 = boxBmaxs.x; + float yMax2 = boxBmaxs.y; + + if (xMin1 < xMax2 || xMax1 > xMin2) + return false; + if (yMin1 < yMax2 || yMax1 > yMin2) + return false; + + return true; +} + +inline float DegtoRad(float _deg) +{ + return _deg * M_PI / 180.0f; +} + +inline float RadtoDeg(float _rad) +{ + return _rad / M_PI * 180.0f; +} + +inline Vec3 sub(const Vec3& _a, const Vec3& _b) +{ + return Vec3(_a.x - _b.x, _a.y - _b.y, _a.z - _b.z); +} + +inline Vec3 sub(const Vec3 _a, float _b) +{ + return Vec3(_a.x - _b, _a.y - _b, _a.z - _b); +} + +inline Vec3 mul(const Vec3& _a, const Vec3& _b) +{ + return Vec3(_a.x * _b.x, _a.y * _b.y, _a.z * _b.z); +} + +inline Vec3 mul(const Vec3& _a, float _b) +{ + return Vec3(_a.x * _b, _a.y * _b, _a.z * _b); +} + +inline float dot(const Vec3& _a, const Vec3& _b) +{ + return _a.x * _b.x + _a.y * _b.y + _a.z * _b.z; +} + +inline float length(const Vec3& _a) +{ + return sqrt(dot(_a, _a)); +} + +inline Vec3 normalize(const Vec3& _a) +{ + const float invLen = 1.0f / length(_a); + const Vec3 result = mul(_a, invLen); + return result; +} + +inline Vec3 cross(const Vec3& _a, const Vec3& _b) +{ + return Vec3(_a.y * _b.z - _a.z * _b.y, _a.z * _b.x - _a.x * _b.z, _a.x * _b.y - _a.y * _b.x); +} + +// Matrix class +struct Matrix +{ + union { + struct { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + float _41, _42, _43, _44; + }; + struct { + Vec3 i; float _14_; + Vec3 j; float _24_; + Vec3 k; float _34_; + Vec3 c; float _44_; + }; + float m[4][4]; + }; + + float* data() const { return (float*)this; } + float* operator()()const{ return (float*)this; } +}; + +extern Matrix g_identityMatrix; + +inline Matrix MakePerspective(float fovy, float aspect, float znear, float zfar) +{ + Matrix M = {}; + + float f = 1.f / tanf(fovy / 2.f), + A = (zfar + znear) / (znear - zfar), + B = (2.f * zfar * znear) / (znear - zfar); + + M._11 = f / aspect; M._12 = 0.f; M._13 = 0.f; M._14 = 0.f; + M._21 = 0.f; M._22 = f; M._23 = 0.f; M._24 = 0.f; + M._31 = 0.f; M._32 = 0.f; M._33 = A; M._34 = B; + M._41 = 0.f; M._42 = 0.f; M._43 = -1.f; M._44 = 0.f; + + return M; +} + +struct BoundingBox +{ + glm::vec3 min; + glm::vec3 max; +}; + +struct OBB +{ + BoundingBox bbox; + glm::mat4 rot; +}; + +extern BoundingBox g_identityBoundingBox; + +void TransformBoundingBox(BoundingBox& bbox, const glm::mat4& model); +void TransformOBB(OBB& obb, const BoundingBox& bbox, const glm::mat4& model); + +inline bool GetBBoxIntersection3D(const BoundingBox& boxA, const BoundingBox& boxB) +{ + return ((boxA.max.x >= boxB.min.x) && (boxA.min.x <= boxB.max.x) && + (boxA.max.z >= boxB.min.z) && (boxA.min.z <= boxB.max.z) && + (boxA.max.y >= boxB.min.y) && (boxA.min.y <= boxB.max.y)); +} + +inline bool RayBoxIntersection(const Vec3& ray_pos, const Vec3& ray_dir, BoundingBox& box, float& tmin) +{ + float lo = ray_dir.x * (box.min.x - ray_pos.x); + float hi = ray_dir.x * (box.max.x - ray_pos.x); + + float tmax; + tmin = fminf(lo, hi); + tmax = fmaxf(lo, hi); + + float lo1 = ray_dir.y * (box.min.y - ray_pos.y); + float hi1 = ray_dir.y * (box.max.y - ray_pos.y); + + tmin = fmaxf(tmin, fminf(lo1, hi1)); + tmax = fminf(tmax, fmaxf(lo1, hi1)); + + float lo2 = ray_dir.z * (box.min.z - ray_pos.z); + float hi2 = ray_dir.z * (box.max.z - ray_pos.z); + + tmin = fmaxf(tmin, fminf(lo2, hi2)); + tmax = fminf(tmax, fmaxf(lo2, hi2)); + + return (tmin <= tmax) && (tmax > 0.f); +} + +// GLM Stuff + +inline glm::vec3 ToGlm(const Vec3& v) +{ + return glm::vec3(v.x, v.y, v.z); +} + +inline Vec3 ToEngine(const glm::vec3& v) +{ + return Vec3(v.x, v.y, v.z); +} + +#endif // !MATHS_H diff --git a/src/engine/utils/objectmanager.cpp b/src/engine/utils/objectmanager.cpp new file mode 100644 index 0000000..a74c20c --- /dev/null +++ b/src/engine/utils/objectmanager.cpp @@ -0,0 +1,183 @@ +#include + +#include + +#include "utils/objectmanager.h" +#include "utils/logger.h" + +ObjectManager g_ObjectManager; + +ObjectManager::ObjectManager() +{ +} + +ObjectManager::~ObjectManager() +{ +} + +void ObjectManager::Register( const RuntimeClass* _pRuntimeClass, IObjectConstructor* _pObjectConstructor ) +{ + // Find already registered object. + + std::vector::iterator it = std::find_if( + m_objectRegistry.begin(), + m_objectRegistry.end(), + [=]( const ObjectRegistryEntry& objectEntry ) + { + return objectEntry.pRuntimeClass == _pRuntimeClass; + } + ); + + assert( it == m_objectRegistry.end() ); + + ObjectRegistryEntry entry = {}; + entry.pRuntimeClass = _pRuntimeClass; + entry.pObjectConstructor = _pObjectConstructor; + + m_objectRegistry.push_back( entry ); + + Msg( "ObjectManager: Registered object %s", _pRuntimeClass->m_classname ); +} + +IObjectBase* ObjectManager::Create( const RuntimeClass* _pRuntimeClass ) +{ + // Find already registered object. + std::vector::iterator it = std::find_if( + m_objectRegistry.begin(), + m_objectRegistry.end(), + [=]( const ObjectRegistryEntry& objectEntry ) + { + return objectEntry.pRuntimeClass == _pRuntimeClass; + } + ); + + assert( it != m_objectRegistry.end() ); + + IObjectConstructor* pObjectConstructor = it->pObjectConstructor; + assert( pObjectConstructor ); + + IObjectBase* pObject = pObjectConstructor->Create(); + assert( pObject ); + + // Add reference + //pObject->AddRef(); + + return pObject; +} + +IObjectBase* ObjectManager::CreateByName( const char* _pClassName ) +{ + // Find already registered object. + std::vector::iterator it = std::find_if( + m_objectRegistry.begin(), + m_objectRegistry.end(), + [=]( const ObjectRegistryEntry& objectEntry ) + { + return strcmp( objectEntry.pRuntimeClass->Name(), _pClassName ) == 0; + } + ); + + assert( it != m_objectRegistry.end() ); + + IObjectConstructor* pObjectConstructor = it->pObjectConstructor; + assert( pObjectConstructor ); + + IObjectBase* pObject = pObjectConstructor->Create(); + assert( pObject ); + + // Add reference + pObject->AddRef(); + + return pObject; +} + +///////////////////////////////////////////////////////////////////// +// Object System Test + +class TestObject1 : public IObjectBase +{ + DECLARE_RTTI_ROOT( TestObject1 ); +public: + TestObject1(); + virtual ~TestObject1(); + + virtual void Foo(); +}; + +IMPLEMENT_RTTI_ROOT( TestObject1 ); + +TestObject1::TestObject1() +{ + Msg( "TestObject1: ctor called" ); +} + +TestObject1::~TestObject1() +{ + Msg( "TestObject1: dtor called" ); +} + +void TestObject1::Foo() +{ + Msg( "TestObject1::Foo() called" ); +} + +class TestObject2 : public TestObject1 +{ + DECLARE_RTTI(TestObject2, TestObject1); +public: + TestObject2(); + ~TestObject2(); + + void Foo() override; +}; + +IMPLEMENT_RTTI(TestObject2, TestObject1); + +TestObject2::TestObject2() +{ + Msg("TestObject2: ctor called"); +} + +TestObject2::~TestObject2() +{ + Msg("TestObject2: dtor called"); +} + +void TestObject2::Foo() +{ + Msg("TestObject2::Foo() called"); +} + +void TestTPtr() +{ + TPtr< TestObject1 > testObjectPtr = CreateObject(); + testObjectPtr->Foo(); + + TPtr< TestObject2 > testObject2 = CreateObject(); + testObject2->Foo(); +} + +void ObjectSystemTest() +{ + RegisterObject< TestObject1 >(); + RegisterObject< TestObject2 >(); + + Msg( "Creating object" ); + + IObjectBase* pObject = g_ObjectManager.Create( RUNTIME_CLASS( TestObject1 ) ); + pObject->AddRef(); + + if ( TestObject1* pTestObject = DynamicCast< TestObject1 >( pObject ) ) + { + Msg( "DynamicCast for TestObject1 was successful" ); + + pTestObject->Foo(); + } + + Msg( "Releasing object" ); + + pObject->Release(); + + TestTPtr(); + +} diff --git a/src/engine/utils/objectmanager.h b/src/engine/utils/objectmanager.h new file mode 100644 index 0000000..80b2b1f --- /dev/null +++ b/src/engine/utils/objectmanager.h @@ -0,0 +1,348 @@ +#ifndef objectmanagerH_included +#define objectmanagerH_included + +#include +#include + +#include +#include + +// #TODO: Make IRefCount or templated class for AddRef\Release and Ref Counting implementation + +// \brief Base interface for all objects. +class IObjectBase : public RefCount +{ +public: + // Virtual destructor + virtual ~IObjectBase() {} + + // Get RuntimeClass. + virtual const RuntimeClass* GetRuntimeClass() const = 0; +}; + +// \brief Dynamic Cast +template < typename T > +T* DynamicCast( IObjectBase* _pObject ) +{ + const RuntimeClass* pRuntimeClass = RUNTIME_CLASS( T ); + + if ( _pObject->GetRuntimeClass()->IsKindOf( pRuntimeClass ) ) + return static_cast< T* >( _pObject ); + + return nullptr; +} + +template +class _NoAddRefReleaseOnTPtr : public T +{ +private: + virtual uint64_t AddRef() = 0; + virtual uint64_t Release() = 0; +}; + +// TPtrBase provides the basis for all other smart pointers +// The other smartpointers add their own constructors and operators +template +class TPtrBase +{ +protected: + TPtrBase() + { + p = NULL; + } + + TPtrBase(T* lp) + { + p = lp; + if (p != NULL) + p->AddRef(); + } + +public: + typedef T _PtrClass; + + ~TPtrBase() + { + if (p) + p->Release(); + } + + operator T* () const + { + return p; + } + + T& operator*() const + { + assert(p != NULL); + return *p; + } + + //The assert on operator& usually indicates a bug. If this is really + //what is needed, however, take the address of the p member explicitly. + T** operator&() + { + assert(p == NULL); + return &p; + } + + _NoAddRefReleaseOnTPtr* operator->() const + { + assert(p != NULL); + return (_NoAddRefReleaseOnTPtr*)p; + } + + bool operator!() const + { + return (p == NULL); + } + + bool operator<(T* pT) const + { + return p < pT; + } + + bool operator!=(T* pT) const + { + return !operator==(pT); + } + + bool operator==(T* pT) const + { + return p == pT; + } + + // Release the interface and set to NULL + void Release() + { + T* pTemp = p; + if (pTemp) + { + p = NULL; + pTemp->Release(); + } + } + + // Compare two objects for equivalence + bool IsEqualObject(IObjectBase* pOther) + { + if (p == NULL && pOther == NULL) + return true; // They are both NULL objects + + if (p == NULL || pOther == NULL) + return false; // One is NULL the other is not + + return p->GetRuntimeClass()->IsKindOf( pOther->GetRuntimeClass() ); + + /* CComPtr punk1; + CComPtr punk2; + p->QueryInterface(__uuidof(IUnknown), (void**)&punk1); + pOther->QueryInterface(__uuidof(IUnknown), (void**)&punk2); + return punk1 == punk2;*/ + } + + // Attach to an existing interface (does not AddRef) + void Attach(T* p2) throw() + { + if (p) + p->Release(); + + p = p2; + } + + // Detach the interface (does not Release) + T* Detach() throw() + { + T* pt = p; + p = NULL; + return pt; + } + + bool CopyTo(T** ppT) + { + assert(ppT != NULL); + if (ppT == NULL) + return false; + + *ppT = p; + if (p) + p->AddRef(); + + return true; + } + + //template + //bool QueryInterface(__deref_out_opt Q** pp) const throw() + //{ + // assert(pp != NULL); + // + // const RuntimeClass* pRuntimeClass = RUNTIME_CLASS( Q ); + // if ( pRuntimeClass->IsKindOf( pRuntimeClass ) ) + // return (Q*)p; + + // return p->QueryInterface(__uuidof(Q), (void**)pp); + //} + + T* p; +}; + +inline IObjectBase* TPtrAssign(IObjectBase** pp, IObjectBase* lp) +{ + if (pp == NULL) + return NULL; + + if (lp != NULL) + lp->AddRef(); + + if (*pp) + (*pp)->Release(); + + *pp = lp; + return lp; +} + +template +class TPtr : public TPtrBase +{ +public: + TPtr() + { + } + + TPtr(T* lp) : TPtrBase(lp) + { + } + + TPtr(__in const TPtr& lp) : TPtrBase(lp.p) + { + } + + T* operator=(T* lp) + { + if (*this != lp) + { + return static_cast(TPtrAssign((IObjectBase**)&p, lp)); + } + + return *this; + } + + //template + //T* operator=(__in const TPtr& lp) + //{ + // if (!IsEqualObject(lp)) + // { + // return static_cast(AtlComQIPtrAssign((IUnknown**)&p, lp, __uuidof(T))); + // } + // return *this; + //} + + T* operator=(__in const TPtr& lp) + { + if (*this != lp) + { + return static_cast(TPtrAssign((IObjectBase**)&p, lp)); + } + return *this; + } +}; + +// \brief Dynamic Cast +template < typename T > +TPtr< T >* DynamicCast( TPtr< IObjectBase >& _pObject ) +{ + const RuntimeClass* pRuntimeClass = RUNTIME_CLASS( T ); + + if ( _pObject->GetRuntimeClass()->IsKindOf( pRuntimeClass ) ) + return TPtr< T >( static_cast< T* >( _pObject ) ); + + return nullptr; +} + +// \brief Interface for ObjectConstructor +class IObjectConstructor +{ +public: + virtual ~IObjectConstructor() {} + + // Create object + virtual IObjectBase* Create() = 0; +}; + +// \brief A helper class for creating object from inplace new. +template +class ObjectConstructor : public IObjectConstructor +{ +public: + // Singleton + static ObjectConstructor* GetInstance(); + +public: + IObjectBase* Create() override; +}; + +template +inline ObjectConstructor* ObjectConstructor::GetInstance() +{ + static ObjectConstructor s_Instance; + return &s_Instance; +} + +template +inline IObjectBase* ObjectConstructor::Create() +{ + return new T(); +} + +// \brief The Object Manager. Store all Runtime Classes and create refcount objects +class ObjectManager +{ +public: + ObjectManager(); + ~ObjectManager(); + + // Register IObjectBase to the object manager + void Register( const RuntimeClass* _pRuntimeClass, IObjectConstructor* _pObjectConstructor ); + + // Create object + IObjectBase* Create( const RuntimeClass* _pRuntimeClass ); + IObjectBase* CreateByName( const char* _pClassName ); + + // Templated version of Create + template < typename T > + T* Create(); + +private: + struct ObjectRegistryEntry + { + const RuntimeClass* pRuntimeClass; + IObjectConstructor* pObjectConstructor; + }; + + std::vector m_objectRegistry; +}; + +template< typename T > +inline T* ObjectManager::Create() +{ + return Create(RUNTIME_CLASS(T)); +} + +extern ObjectManager g_ObjectManager; + +// Helper function for registering object +template < typename T > +void RegisterObject() +{ + g_ObjectManager.Register( RUNTIME_CLASS( T ), ObjectConstructor< T >::GetInstance() ); +} + +// Helper function for creating object +template < typename T > +TPtr< T > CreateObject() +{ + IObjectBase* pObject = g_ObjectManager.Create( RUNTIME_CLASS( T ) ); + return TPtr< T >( ( T* )pObject ); +} + +#endif // !objectmanagerH_included diff --git a/src/engine/utils/refcount.h b/src/engine/utils/refcount.h new file mode 100644 index 0000000..7e4eba5 --- /dev/null +++ b/src/engine/utils/refcount.h @@ -0,0 +1,45 @@ +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include +#include +#include + +class RefCount +{ +public: + // Virtual destructor + virtual ~RefCount() {} + + // Add reference + uint64_t AddRef(); + + // Release (remove reference and destroy object at zero reference's) + uint64_t Release(); + +private: + // Ref counting + uint64_t m_RefCount = 0; +}; + +inline uint64_t RefCount::AddRef() +{ + ++m_RefCount; + return m_RefCount; +} + +inline uint64_t RefCount::Release() +{ + assert(m_RefCount > 0); + --m_RefCount; + + if (m_RefCount == 0) + { + delete this; + return 0; + } + + return m_RefCount; +} + +#endif diff --git a/src/engine/utils/rtti.cpp b/src/engine/utils/rtti.cpp new file mode 100644 index 0000000..01faca7 --- /dev/null +++ b/src/engine/utils/rtti.cpp @@ -0,0 +1,23 @@ +#include "rtti.h" + +RuntimeClass::RuntimeClass(const char* classname) : m_classname(classname), m_baseRuntimeClass(nullptr) +{ +} + +RuntimeClass::RuntimeClass(const char* classname, const RuntimeClass* baseTypeInfo) : m_classname(classname), m_baseRuntimeClass(baseTypeInfo) +{ +} + +bool RuntimeClass::IsKindOf(const RuntimeClass* typeInfo) const +{ + for (const RuntimeClass* it = this; it != nullptr; it = it->m_baseRuntimeClass) + if (it == typeInfo) + return true; + + return false; +} + +const char* RuntimeClass::Name() const +{ + return m_classname; +} diff --git a/src/engine/utils/rtti.h b/src/engine/utils/rtti.h new file mode 100644 index 0000000..5280e29 --- /dev/null +++ b/src/engine/utils/rtti.h @@ -0,0 +1,43 @@ +#ifndef RTTI_H +#define RTTI_H + +struct RuntimeClass +{ + RuntimeClass(const char* classname); + RuntimeClass(const char* classname, const RuntimeClass* baseTypeInfo); + + bool IsKindOf(const RuntimeClass* typeInfo) const; + + const char* Name() const; + + const RuntimeClass* m_baseRuntimeClass; + const char* m_classname; +}; + +#define DECLARE_RTTI_ROOT(typeName) \ + public: \ + static const RuntimeClass s_runtimeClass; \ + static const RuntimeClass* GetStaticRuntimeClass() { return &s_runtimeClass; } \ + virtual const RuntimeClass* GetRuntimeClass() const { return GetStaticRuntimeClass(); } + +#define DECLARE_RTTI(typeName, baseTypeName) \ + public: \ + static const RuntimeClass s_runtimeClass; \ + static const RuntimeClass* GetStaticRuntimeClass() { return &s_runtimeClass; } \ + virtual const RuntimeClass* GetRuntimeClass() const { return GetStaticRuntimeClass(); } + +#define IMPLEMENT_RTTI_ROOT(typeName) \ + const RuntimeClass typeName::s_runtimeClass(#typeName, nullptr) + +#define IMPLEMENT_RTTI(typeName, baseTypeName) \ + const RuntimeClass typeName::s_runtimeClass(#typeName, baseTypeName::GetStaticRuntimeClass()) + +template +const RuntimeClass* GetRuntimeClass() +{ + return T::GetStaticRuntimeClass(); +} + +#define RUNTIME_CLASS(typeName) (typeName::GetStaticRuntimeClass()) + +#endif //!RTTI_H \ No newline at end of file diff --git a/src/engine/utils/string.cpp b/src/engine/utils/string.cpp new file mode 100644 index 0000000..cd729db --- /dev/null +++ b/src/engine/utils/string.cpp @@ -0,0 +1,14 @@ +#include +#include "utils/string.h" + +const char* va(const char* fmt, ...) +{ + va_list args; + static char buffer[4096]; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + + return buffer; +} diff --git a/src/engine/utils/string.h b/src/engine/utils/string.h new file mode 100644 index 0000000..159b4af --- /dev/null +++ b/src/engine/utils/string.h @@ -0,0 +1,10 @@ +#ifndef UTILS_STRING_H +#define UTILS_STRING_H + +#include + +typedef std::string String; + +const char* va(const char* fmt, ...); + +#endif // !UTILS_STRING_H diff --git a/src/engine/utils/timer.cpp b/src/engine/utils/timer.cpp new file mode 100644 index 0000000..bd7b897 --- /dev/null +++ b/src/engine/utils/timer.cpp @@ -0,0 +1,65 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include "timer.h" + +#if defined(WIN32) || defined(_WIN32) +class SystemTimerWin32 : public SystemTimer +{ +public: + void Init(); + void Shutdown(); + + void Update(); + + float GetTime(); + float GetDelta(); + +private: + LARGE_INTEGER m_frequency; + LARGE_INTEGER m_startTime; + LARGE_INTEGER m_endTime; + + float m_floatFrequency; + float m_deltaTime; +}; + +static SystemTimerWin32 s_system_timer_win32; +SystemTimer* g_systemTimer = (SystemTimer*)&s_system_timer_win32; + +void SystemTimerWin32::Init() +{ + QueryPerformanceFrequency(&m_frequency); + m_floatFrequency = (float)m_frequency.QuadPart; + + QueryPerformanceCounter(&m_startTime); + m_startTime = m_endTime; +} + +void SystemTimerWin32::Shutdown() +{ +} + +void SystemTimerWin32::Update() +{ + QueryPerformanceCounter(&m_startTime); + m_deltaTime = static_cast(m_startTime.QuadPart - m_endTime.QuadPart) / m_frequency.QuadPart; + + if (m_deltaTime > 10.0f) + m_deltaTime = 0.0f; + + m_endTime = m_startTime; +} + +float SystemTimerWin32::GetTime() +{ + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + return static_cast(time.QuadPart / m_frequency.QuadPart); +} + +float SystemTimerWin32::GetDelta() +{ + return m_deltaTime; +} +#endif // WIN32 diff --git a/src/engine/utils/timer.h b/src/engine/utils/timer.h new file mode 100644 index 0000000..1405350 --- /dev/null +++ b/src/engine/utils/timer.h @@ -0,0 +1,26 @@ +#ifndef TIMER_H +#define TIMER_H + +//! Base class for system timer +class SystemTimer +{ +public: + //! Initialization + virtual void Init() = 0; + + //! Shutdown + virtual void Shutdown() = 0; + + //! Update timer + virtual void Update() = 0; + + //! Get time from application start + virtual float GetTime() = 0; + + //! Get delta + virtual float GetDelta() = 0; +}; + +extern SystemTimer* g_systemTimer; + +#endif // !TIMER_H diff --git a/src/engine/utils/win32_keys.h b/src/engine/utils/win32_keys.h new file mode 100644 index 0000000..b874878 --- /dev/null +++ b/src/engine/utils/win32_keys.h @@ -0,0 +1,359 @@ +#ifndef WIN32_KEYS_H +#define WIN32_KEYS_H + +#include "input/input_system.h" + +namespace Win32Keys +{ + enum Keys + { + Backspace = 0x08, + Tab = 0x09, + Clear = 0x0C, + Enter = 0x0D, + Shift = 0x10, + Control = 0x11, + Alt = 0x12, + Pause = 0x13, + CapsLock = 0x14, + Escape = 0x1B, + Space = 0x20, + PageUp = 0x21, + PageDown = 0x22, + End = 0x23, + Home = 0x24, + Left = 0x25, + Up = 0x26, + Right = 0x27, + Down = 0x28, + Select = 0x29, + Print = 0x2A, + Execute = 0x2B, + PrintScreen = 0x2C, + Insert = 0x2D, + Delete = 0x2E, + Help = 0x2F, + Zero = 0x30, + One = 0x31, + Two = 0x32, + Three = 0x33, + Four = 0x34, + Five = 0x35, + Six = 0x36, + Seven = 0x37, + Eight = 0x38, + Nine = 0x39, + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5A, + LeftWindowsKey = 0x5B, + RightWindowsKey = 0x5C, + ApplicationsKey = 0x5D, + Sleep = 0x5F, + NumPad0 = 0x60, + NumPad1 = 0x61, + NumPad2 = 0x62, + NumPad3 = 0x63, + NumPad4 = 0x64, + NumPad5 = 0x65, + NumPad6 = 0x66, + NumPad7 = 0x67, + NumPad8 = 0x68, + NumPad9 = 0x69, + Multiply = 0x6A, + Add = 0x6B, + Seperator = 0x6C, + Subtract = 0x6D, + Decimal = 0x6E, + Divide = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + F13 = 0x7C, + F14 = 0x7D, + F15 = 0x7E, + F16 = 0x7F, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + Numlock = 0x90, + ScrollLock = 0x91, + LeftShift = 0xA0, + RightShift = 0xA1, + LeftControl = 0xA2, + RightContol = 0xA3, + LeftMenu = 0xA4, + RightMenu = 0xA5, + BrowserBack = 0xA6, + BrowserForward = 0xA7, + BrowserRefresh = 0xA8, + BrowserStop = 0xA9, + BrowserSearch = 0xAA, + BrowserFavorites = 0xAB, + BrowserHome = 0xAC, + VolumeMute = 0xAD, + VolumeDown = 0xAE, + VolumeUp = 0xAF, + NextTrack = 0xB0, + PreviousTrack = 0xB1, + StopMedia = 0xB2, + PlayPause = 0xB3, + LaunchMail = 0xB4, + SelectMedia = 0xB5, + LaunchApp1 = 0xB6, + LaunchApp2 = 0xB7, + OEM1 = 0xBA, + OEMPlus = 0xB8, + OEMComma = 0xBC, + OEMMinus = 0xBD, + OEMPeriod = 0xBE, + OEM2 = 0xBF, + OEM3 = 0xC0, + OEM4 = 0xDB, + OEM5 = 0xDC, + OEM6 = 0xDD, + OEM7 = 0xDE, + OEM8 = 0xDF, + OEM102 = 0xE2, + Process = 0xE5, + Packet = 0xE7, + Attn = 0xF6, + CrSel = 0xF7, + ExSel = 0xF8, + EraseEOF = 0xF9, + Play = 0xFA, + Zoom = 0xFB, + PA1 = 0xFD, + OEMClear = 0xFE + }; +} + +inline KeyboardKeys GetKeyFromWin32(Win32Keys::Keys key) +{ + switch (key) + { + case Win32Keys::Backspace: return KeyboardKeys::KEY_BACKSPACE; + case Win32Keys::Tab: return KeyboardKeys::KEY_TAB; + case Win32Keys::Clear: return KeyboardKeys::KEY_WORLD_1; + case Win32Keys::Enter: return KeyboardKeys::KEY_ENTER; + case Win32Keys::Shift: return KeyboardKeys::KEY_LEFT_SHIFT; // TODO + case Win32Keys::Control: return KeyboardKeys::KEY_LEFT_CONTROL; // TODO + case Win32Keys::Alt: return KeyboardKeys::KEY_LEFT_ALT; // TODO + case Win32Keys::Pause: return KeyboardKeys::KEY_PAUSE; + case Win32Keys::CapsLock: return KeyboardKeys::KEY_CAPS_LOCK; + case Win32Keys::Escape: return KeyboardKeys::KEY_ESCAPE; + case Win32Keys::Space: return KeyboardKeys::KEY_SPACE; + case Win32Keys::PageUp: return KeyboardKeys::KEY_PAGE_UP; + case Win32Keys::PageDown: return KeyboardKeys::KEY_PAGE_DOWN; + case Win32Keys::End: return KeyboardKeys::KEY_END; + case Win32Keys::Home: return KeyboardKeys::KEY_HOME; + case Win32Keys::Left: return KeyboardKeys::KEY_LEFT; + case Win32Keys::Up: return KeyboardKeys::KEY_UP; + case Win32Keys::Right: return KeyboardKeys::KEY_RIGHT; + case Win32Keys::Down: return KeyboardKeys::KEY_DOWN; + case Win32Keys::Select: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::Print: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::Execute: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::PrintScreen: return KeyboardKeys::KEY_PRINT_SCREEN; + case Win32Keys::Insert: return KeyboardKeys::KEY_INSERT; + case Win32Keys::Delete: return KeyboardKeys::KEY_DELETE; + case Win32Keys::Help: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::Zero: return KeyboardKeys::KEY_0; + case Win32Keys::One: return KeyboardKeys::KEY_1; + case Win32Keys::Two: return KeyboardKeys::KEY_2; + case Win32Keys::Three: return KeyboardKeys::KEY_3; + case Win32Keys::Four: return KeyboardKeys::KEY_4; + case Win32Keys::Five: return KeyboardKeys::KEY_5; + case Win32Keys::Six: return KeyboardKeys::KEY_6; + case Win32Keys::Seven: return KeyboardKeys::KEY_7; + case Win32Keys::Eight: return KeyboardKeys::KEY_8; + case Win32Keys::Nine: return KeyboardKeys::KEY_9; + case Win32Keys::A: return KeyboardKeys::KEY_A; + case Win32Keys::B: return KeyboardKeys::KEY_B; + case Win32Keys::C: return KeyboardKeys::KEY_C; + case Win32Keys::D: return KeyboardKeys::KEY_D; + case Win32Keys::E: return KeyboardKeys::KEY_E; + case Win32Keys::F: return KeyboardKeys::KEY_F; + case Win32Keys::G: return KeyboardKeys::KEY_G; + case Win32Keys::H: return KeyboardKeys::KEY_H; + case Win32Keys::I: return KeyboardKeys::KEY_I; + case Win32Keys::J: return KeyboardKeys::KEY_J; + case Win32Keys::K: return KeyboardKeys::KEY_K; + case Win32Keys::L: return KeyboardKeys::KEY_L; + case Win32Keys::M: return KeyboardKeys::KEY_M; + case Win32Keys::N: return KeyboardKeys::KEY_N; + case Win32Keys::O: return KeyboardKeys::KEY_O; + case Win32Keys::P: return KeyboardKeys::KEY_P; + case Win32Keys::Q: return KeyboardKeys::KEY_Q; + case Win32Keys::R: return KeyboardKeys::KEY_R; + case Win32Keys::S: return KeyboardKeys::KEY_S; + case Win32Keys::T: return KeyboardKeys::KEY_T; + case Win32Keys::U: return KeyboardKeys::KEY_U; + case Win32Keys::V: return KeyboardKeys::KEY_V; + case Win32Keys::W: return KeyboardKeys::KEY_W; + case Win32Keys::X: return KeyboardKeys::KEY_X; + case Win32Keys::Y: return KeyboardKeys::KEY_Y; + case Win32Keys::Z: return KeyboardKeys::KEY_Z; + case Win32Keys::LeftWindowsKey: return KeyboardKeys::KEY_LEFT_SUPER; + case Win32Keys::RightWindowsKey:return KeyboardKeys::KEY_RIGHT_SUPER; + case Win32Keys::ApplicationsKey:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::Sleep: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::NumPad0: return KeyboardKeys::KEY_KP_0; + case Win32Keys::NumPad1: return KeyboardKeys::KEY_KP_1; + case Win32Keys::NumPad2: return KeyboardKeys::KEY_KP_2; + case Win32Keys::NumPad3: return KeyboardKeys::KEY_KP_3; + case Win32Keys::NumPad4: return KeyboardKeys::KEY_KP_4; + case Win32Keys::NumPad5: return KeyboardKeys::KEY_KP_5; + case Win32Keys::NumPad6: return KeyboardKeys::KEY_KP_6; + case Win32Keys::NumPad7: return KeyboardKeys::KEY_KP_7; + case Win32Keys::NumPad8: return KeyboardKeys::KEY_KP_8; + case Win32Keys::NumPad9: return KeyboardKeys::KEY_KP_9; + case Win32Keys::Multiply: return KeyboardKeys::KEY_KP_MULTIPLY; + case Win32Keys::Add: return KeyboardKeys::KEY_KP_ADD; + case Win32Keys::Seperator: return KeyboardKeys::KEY_KP_ENTER; + case Win32Keys::Subtract: return KeyboardKeys::KEY_KP_SUBTRACT; + case Win32Keys::Decimal: return KeyboardKeys::KEY_KP_DECIMAL; + case Win32Keys::Divide: return KeyboardKeys::KEY_KP_DIVIDE; + case Win32Keys::F1: return KeyboardKeys::KEY_F1; + case Win32Keys::F2: return KeyboardKeys::KEY_F2; + case Win32Keys::F3: return KeyboardKeys::KEY_F3; + case Win32Keys::F4: return KeyboardKeys::KEY_F4; + case Win32Keys::F5: return KeyboardKeys::KEY_F5; + case Win32Keys::F6: return KeyboardKeys::KEY_F6; + case Win32Keys::F7: return KeyboardKeys::KEY_F7; + case Win32Keys::F8: return KeyboardKeys::KEY_F8; + case Win32Keys::F9: return KeyboardKeys::KEY_F9; + case Win32Keys::F10: return KeyboardKeys::KEY_F10; + case Win32Keys::F11: return KeyboardKeys::KEY_F11; + case Win32Keys::F12: return KeyboardKeys::KEY_F12; + case Win32Keys::F13: return KeyboardKeys::KEY_F13; + case Win32Keys::F14: return KeyboardKeys::KEY_F14; + case Win32Keys::F15: return KeyboardKeys::KEY_F15; + case Win32Keys::F16: return KeyboardKeys::KEY_F16; + case Win32Keys::F17: return KeyboardKeys::KEY_F17; + case Win32Keys::F18: return KeyboardKeys::KEY_F18; + case Win32Keys::F19: return KeyboardKeys::KEY_F19; + case Win32Keys::F20: return KeyboardKeys::KEY_F20; + case Win32Keys::F21: return KeyboardKeys::KEY_F21; + case Win32Keys::F22: return KeyboardKeys::KEY_F22; + case Win32Keys::F23: return KeyboardKeys::KEY_F23; + case Win32Keys::F24: return KeyboardKeys::KEY_F24; + case Win32Keys::Numlock: return KeyboardKeys::KEY_NUM_LOCK; + case Win32Keys::ScrollLock: return KeyboardKeys::KEY_SCROLL_LOCK; + case Win32Keys::LeftShift: return KeyboardKeys::KEY_LEFT_SHIFT; + case Win32Keys::RightShift: return KeyboardKeys::KEY_RIGHT_SHIFT; + case Win32Keys::LeftControl: return KeyboardKeys::KEY_LEFT_CONTROL; + case Win32Keys::RightContol: return KeyboardKeys::KEY_RIGHT_CONTROL; + case Win32Keys::LeftMenu: return KeyboardKeys::KEY_MENU; + case Win32Keys::RightMenu: return KeyboardKeys::KEY_MENU; + case Win32Keys::BrowserBack: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserForward: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserRefresh: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserStop: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserSearch: return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserFavorites:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::BrowserHome:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::VolumeMute:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::VolumeDown:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::VolumeUp:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::NextTrack:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::PreviousTrack:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::StopMedia:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::PlayPause:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::LaunchMail:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::SelectMedia:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::LaunchApp1:return KeyboardKeys::KEY_UNKNOWN; // TODO + case Win32Keys::LaunchApp2: + break; + case Win32Keys::OEM1: + break; + case Win32Keys::OEMPlus: + break; + case Win32Keys::OEMComma: + break; + case Win32Keys::OEMMinus: + break; + case Win32Keys::OEMPeriod: + break; + case Win32Keys::OEM2: + break; + case Win32Keys::OEM3: + break; + case Win32Keys::OEM4: + break; + case Win32Keys::OEM5: + break; + case Win32Keys::OEM6: + break; + case Win32Keys::OEM7: + break; + case Win32Keys::OEM8: + break; + case Win32Keys::OEM102: + break; + case Win32Keys::Process: + break; + case Win32Keys::Packet: + break; + case Win32Keys::Attn: + break; + case Win32Keys::CrSel: + break; + case Win32Keys::ExSel: + break; + case Win32Keys::EraseEOF: + break; + case Win32Keys::Play: + break; + case Win32Keys::Zoom: + break; + case Win32Keys::PA1: + break; + case Win32Keys::OEMClear: + break; + default: + break; + } + + return KeyboardKeys::KEY_UNKNOWN; +} + + +#endif // !WIN32_KEYS_H diff --git a/src/game/camera.cpp b/src/game/camera.cpp new file mode 100644 index 0000000..9b675b3 --- /dev/null +++ b/src/game/camera.cpp @@ -0,0 +1,96 @@ +#include +#include + +Camera g_camera; + +struct CameraGlobals { + int mouseX = 0; + int mouseY = 0; + int prevMouseX = 0; + int prevMouseY = 0; + int deltaMouseX = 0; + int deltaMouseY = 0; +} g_cameraGlobals; + +void cameraUpdateMouseLook( int x, int y ) +{ + CameraGlobals& camg = g_cameraGlobals; + camg.mouseX = x; + camg.mouseY = y; + + camg.deltaMouseX = camg.mouseX - camg.prevMouseX; + camg.deltaMouseY = camg.mouseY - camg.prevMouseY; + + camg.prevMouseX = camg.mouseX; + camg.prevMouseY = camg.mouseY; + + g_camera.yaw += (float)camg.deltaMouseX * 0.2f; + g_camera.pitch -= (float)camg.deltaMouseY * 0.2f; + + //if (g_camera.yaw > 180.0f) + // g_camera.yaw -= 180.0f; + //else if (g_camera.yaw < -180.0f) + // g_camera.yaw += 180.0f; + + if (g_camera.pitch > 89.0f) + g_camera.pitch = 89.0f; + if (g_camera.pitch < -89.0f) + g_camera.pitch = -89.0f; +} + +void cameraUpdateLook(const Vec3& target, float dist) +{ + float yaw = (float)g_camera.yaw; + float pitch = (float)g_camera.pitch; + + //yaw *= 0.2f; + //pitch *= 0.2f; + + glm::vec3 direction = glm::vec3(0.0f); + direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction.y = sin(glm::radians(pitch)); + direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + + g_camera.look = glm::vec3(target.x, target.y, target.z); + + g_camera.eye = g_camera.look - direction * dist; + + //g_camera.eye = glm::vec3(eye.x, eye.y, eye.z); +} + +void cameraUpdateLook_FPS(const Vec3& pos) +{ + float yaw = (float)g_camera.yaw; + float pitch = (float)g_camera.pitch; + + glm::vec3 direction = glm::vec3(0.0f); + direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction.y = sin(glm::radians(pitch)); + direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + + g_camera.eye = ToGlm(pos); + g_camera.look = direction; +} + +glm::vec3 cameraGetDirection() +{ + float yaw = (float)g_camera.yaw; + float pitch = (float)g_camera.pitch; + + glm::vec3 direction = glm::vec3(0.0f); + direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction.y = sin(glm::radians(pitch)); + direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + return direction; +} + +Vec3 cameraGetDir() +{ + return Vec3(ToEngine(cameraGetDirection())); +} + +void cameraPushToRender() +{ + static glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + g_renderView.view = glm::lookAt(g_camera.eye, g_camera.eye + g_camera.look, up); +} diff --git a/src/game/camera.h b/src/game/camera.h new file mode 100644 index 0000000..cda39b1 --- /dev/null +++ b/src/game/camera.h @@ -0,0 +1,24 @@ +#ifndef cameraH_included +#define cameraH_included + +#include + +struct Camera +{ + glm::vec3 eye = glm::vec3(0.0f); + glm::vec3 look = glm::vec3(0.0f); + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + float yaw = 0.0f; + float pitch = 0.0f; +}; + +extern Camera g_camera; + +void cameraUpdateMouseLook(int x, int y); +void cameraUpdateLook(const Vec3& target, float dist); +void cameraUpdateLook_FPS(const Vec3& pos); +glm::vec3 cameraGetDirection(); +Vec3 cameraGetDir(); +void cameraPushToRender(); + +#endif // !cameraH_included diff --git a/src/game/game_app.cpp b/src/game/game_app.cpp new file mode 100644 index 0000000..31717f8 --- /dev/null +++ b/src/game/game_app.cpp @@ -0,0 +1,378 @@ +#include "utils/logger.h" +#include "utils/maths.h" +#include "utils/timer.h" +#include "filesystem/filemanager.h" +#include "game/game_app.h" +#include "game/game_object_factory.h" +#include "game/level.h" +#include "game/player.h" +#include "game/main_menu.h" +#include "sound/sound_system.h" +#include "render/render.h" +#include "render/modelmanager.h" +#include "render/render_shared.h" +#include "render/ui.h" +#include "physics/physics_world.h" + +GameApp* g_gameApp = nullptr; +GameState g_gameState = GAME_STATE_MAIN_MENU; + +#define REGISTER_OBJECT( className ) \ + g_objectFactory->Register( RUNTIME_CLASS( className ), &className::_Construct ) + +void RegisterObjects() +{ + REGISTER_OBJECT( GameObject ); + REGISTER_OBJECT( Player ); + //g_objectFactory->Register( RUNTIME_CLASS( GameObject ), &GameObject::_Construct ); + //g_objectFactory->Register( RUNTIME_CLASS( Player ), &Player::_Construct ); +} + +GameApp::GameApp() : + m_bExitRequested(false) +{ +} + +GameApp::~GameApp() +{ +} + +void GameApp::Init() +{ + g_systemTimer->Init(); + + Logger::init(); + + g_fileManager = new FileManager(); + + RegisterObjects(); + + g_inputSystem = CreateInputSystem(nullptr); + + g_render = new Render(); + g_render->Init(); + + g_soundSystem = CreateSoundSystem_DirectSound(g_render->GetRenderWindow()); + g_soundSystem->SetMasterVolume(1.0f); + + glm::vec3 eye = glm::vec3(1.0f); + glm::vec3 look = glm::vec3(0.0f); + glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + g_renderView.view = glm::lookAt(eye, look, up); + g_renderView.fov = 70.0f; + + for (int i = 0; i < __argc; i++) + { + if ( __argv[i] && strcmp( __argv[i], "-skipMenu" ) == 0 ) + g_gameState = GAME_STATE_RUNNING; + } + + PhysicsInit(); + + g_level = new Level(); + g_level->LoadText("content/levels/playertest.txt"); + + //g_level = new Level(); + //g_level->LoadText("content/levels/test.txt"); +} + +void GameApp::Shutdown() +{ + PhysicsShutdown(); + + delete g_soundSystem; + g_soundSystem = nullptr; + + g_render->Shutdown(); + + delete g_render; + g_render = nullptr; + + DestroyInputSystem(g_inputSystem); + g_inputSystem = nullptr; + + delete g_fileManager; + g_fileManager = nullptr; + + Logger::shutdown(); + + g_systemTimer->Shutdown(); +} + +const float kDuration = 2.2f; + +struct NoScoreGlobals +{ + float time = 0.0f; + float x = 0.0f; + float y = 0.0f; +} g_msg_noscore; + +void UpdateNoScoreAnimation() +{ + const Vec2 size = { 256.0f, 128.0f }; + + float alpha = lerp(g_msg_noscore.time, 0.0f, 1.0f); + + uiBeginRender(); + uiSetTextureByName("content/textures/ui/ui_msg_nopoints.png"); + uiDrawRect(Vec2{ g_msg_noscore.x,g_msg_noscore.y }, size, Vec4{ 1.0f, 1.0f, 1.0f, alpha }); + uiEndRender(); + + const float speed = 170.0f; + g_msg_noscore.y += speed * g_systemTimer->GetDelta(); + g_msg_noscore.time += g_systemTimer->GetDelta(); +} + +void Reset() +{ + g_msg_noscore.x = 0.0f; + g_msg_noscore.y = 0.0f; + g_msg_noscore.time = 0.0f; +} + +const float kFadeDuration = 2.0f; + +struct Fade +{ + float time = 0.0f; +} g_fade; + +bool showFade = true; +bool g_hideCursor = false; + +void GameApp::UpdateFrame() +{ + // #TODO: Refactor loop + // in progress ... + + // Update the timer + g_systemTimer->Update(); + + float deltaTime = g_systemTimer->GetDelta(); + + // Update the Input System + g_inputSystem->Update(); + + // Update sound system + g_soundSystem->Update( deltaTime ); + + // Request exit on Q button + if (g_inputSystem->IsKeyPressed(KEY_Q)) + RequestExit(); + + //if (g_inputSystem->IsKeyPressed(KEY_ESCAPE) && g_gameState == GAME_STATE_RUNNING) + // g_gameState = GAME_STATE_MAIN_MENU; + //else if (g_inputSystem->IsKeyPressed(KEY_ESCAPE) && g_gameState == GAME_STATE_MAIN_MENU) + // g_gameState = GAME_STATE_RUNNING; + + // Update level if we in running state +// if ( g_level && g_gameState == GAME_STATE_RUNNING ) + g_level->Update(deltaTime); + + // Begin of frame + g_render->BeginFrame(); + + // Draw main menu + //if ( g_gameState == GAME_STATE_MAIN_MENU ) + //{ + // // Draw BG + // uiBeginRender(); + // uiSetTextureByName("content/textures/ui/ui_main_bg.png"); + // uiDrawRect(Vec2{ 0.0f, 0.0f }, Vec2{ (float)g_renderView.width,(float)g_renderView.height }, Vec4{1.f, 1.f,1.f,1.f}); + // uiEndRender(); + + // // Draw new game button + // { + // // Check for selection + // bool selected = buttonIsSelected(&g_startGameButton); + // if (selected && g_inputSystem->IsMouseButtonPressed(mouseButton_Left)) + // { + // Msg("GameApp: NEW GAME !!!"); + // + // if (g_level) + // delete g_level; + + // g_level = new Level(); + // g_level->LoadText("content/levels/playertest.txt"); + + // g_gameState = GAME_STATE_RUNNING; + // g_hideCursor = true; + // } + + // float color = lerp((float)selected, 0.8f, 1.0f); + + // uiBeginRender(); + // uiSetTextureByName("content/textures/ui/ui_btn_newgame.png"); + // uiDrawRect(g_startGameButton.pos, g_startGameButton.size, Vec4{ color,color,color,1.f }); + // uiEndRender(); + // } + + // // Draw quit button + // + // { + // // Check for selection + // bool selected = buttonIsSelected(&g_quitButton); + // if (selected && g_inputSystem->IsMouseButtonPressed(mouseButton_Left)) + // { + // RequestExit(); + // Msg("GameApp: Requested exit"); + // } + + // float color = lerp((float)selected, 0.8f, 1.0f); + + // uiBeginRender(); + // uiSetTextureByName("content/textures/ui/ui_btn_quit.png"); + // uiDrawRect(g_quitButton.pos, g_quitButton.size, Vec4{ color,color,color,1.f }); + // uiEndRender(); + // } + //} + //else if (g_gameState == GAME_STATE_RUNNING) + { + // Render level + + if (g_level) + g_level->Render(); + } + + // Draw cursor + if (!g_hideCursor) + { + int32_t x = 0, y = 0; + g_inputSystem->GetMousePosition(&x, &y); + + uiBeginRender(); + uiSetTextureByName("content/textures/ui/ui_cursor.png"); + uiDrawRect(Vec2{ (float)x,(float)y }, Vec2{ 16.0f,35.0f }, Vec4{ 1.f, 1.f,1.f,1.f }); + uiEndRender(); + } + + // End of frame + g_render->EndFrame(); +#if 0 + //glm::vec3 eye = glm::vec3(5.0f); + //glm::vec3 look = glm::vec3(0.0f); + //glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); + //g_renderView.view = glm::lookAt(eye, look, up); + + g_systemTimer->Update(); + +// g_camera.look = glm::vec3(0.0f, 0.0f, 10.0f); + + const float cameraSpeed = 4.0f; + + glm::vec3 dir = glm::normalize( g_camera.look ); +// dir.y = 0.0f; + + if (g_inputSystem->IsKeyPressed(KEY_W)) + g_camera.eye += cameraSpeed * dir * g_systemTimer->GetDelta(); + if (g_inputSystem->IsKeyPressed(KEY_S)) + g_camera.eye -= cameraSpeed * dir * g_systemTimer->GetDelta(); + if (g_inputSystem->IsKeyPressed(KEY_A)) + g_camera.eye -= cameraSpeed * g_systemTimer->GetDelta() * glm::normalize(glm::cross(dir, glm::vec3(0.0f, 1.0f, 0.0f))); + if (g_inputSystem->IsKeyPressed(KEY_D)) + g_camera.eye += cameraSpeed * g_systemTimer->GetDelta() * glm::normalize(glm::cross(dir, glm::vec3(0.0f, 1.0f, 0.0f))); + + + if (g_inputSystem->IsMouseButtonPressed(mouseButton_Right)) + { + int32_t x = 0, y = 0; + g_inputSystem->GetMousePosition(&x, &y); + + float yaw = (float)x; + float pitch = (float)-y; + + yaw *= 0.2f; + pitch *= 0.2f; + + glm::vec3 direction = glm::vec3(0.0f); + direction.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); + direction.y = sin(glm::radians(pitch)); + direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); + + g_camera.look = direction; + } + + //Msg("%f %f %f", g_camera.eye.x, g_camera.eye.y, g_camera.eye.z); + + g_renderView.view = glm::lookAt(g_camera.eye, g_camera.eye + g_camera.look, glm::vec3(0.0f, 1.0f, 0.0f)); + + if (g_inputSystem->IsKeyPressed(KEY_Q)) + RequestExit(); + + if (g_inputSystem->IsKeyPressed(KEY_E)) + g_renderView.fov -= 0.5f; + else if (g_inputSystem->IsKeyPressed(KEY_R)) + g_renderView.fov += 0.5f; + + g_render->BeginFrame(); + +// if (g_model) +// g_model->Draw(g_identityMatrix); + + g_level->Render(); + + Vec2 buttonSize = { 100.0f,100.0f }; + + int32_t x = 0, y = 0; + g_inputSystem->GetMousePosition(&x, &y); + + Vec2 pos = { 100.0f, 100.0f }; + bool inFocus = GetBoxIntersection2D(Vec2{ (float)x + 15.0f, (float)y + 35.0f }, Vec2{ (float)x,(float)y }, + Vec2{ pos.x + buttonSize.x, pos.y + buttonSize.y }, pos); + + static bool updateAni = false; + static float updateAniTime = 0.0f; + + //stopRotate = inFocus && g_inputSystem->IsMouseButtonPressed(mouseButton_Left); + + if (inFocus && g_inputSystem->IsMouseButtonPressed(mouseButton_Left)) + { + updateAniTime = 0.0f; + Reset(); + updateAni = true; + } + + // Draw button + + float color = lerp((float)inFocus, 0.8f, 1.0f); + + uiBeginRender(); + uiSetTextureByName("content/textures/ui/ui_new_unit.png"); + uiDrawRect(pos, buttonSize, Vec4{ color, color, color, 1.f }); + uiEndRender(); + + if ( updateAni && updateAniTime <= kDuration ) + { + UpdateNoScoreAnimation(); + updateAniTime += g_systemTimer->GetDelta(); + } + else if (updateAni && updateAniTime >= kDuration) + { + updateAniTime = 0.0f; + updateAni = false; + g_msg_noscore.x = 0.0f; + g_msg_noscore.y = 0.0f; + g_msg_noscore.time = 0.0f; + } + + if (showFade && g_fade.time <= kFadeDuration) + { + uiBeginRender(); + uiSetTexture(nullptr); + uiDrawRect(Vec2{ 0.f, 0.f }, Vec2{ (float)g_renderView.width, (float)g_renderView.height }, Vec4{ 0.0f, 0.0f, 0.0f, lerp(g_fade.time, 1.0f, 0.0f) }); + uiEndRender(); + + g_fade.time += g_systemTimer->GetDelta(); + } + + // draw cursor + + uiBeginRender(); + uiSetTextureByName("content/textures/ui/ui_cursor.png"); + uiDrawRect(Vec2{ (float)x,(float)y }, Vec2{ 16.0f,35.0f }, Vec4{ 1.f, 1.f,1.f,1.f }); + uiEndRender(); + + g_render->EndFrame(); +#endif +} diff --git a/src/game/game_app.h b/src/game/game_app.h new file mode 100644 index 0000000..71eccc9 --- /dev/null +++ b/src/game/game_app.h @@ -0,0 +1,32 @@ +#ifndef GAME_APP_H +#define GAME_APP_H + +#include "input/input_system.h" + +enum GameState +{ + GAME_STATE_RUNNING, + GAME_STATE_MAIN_MENU, +}; + +class GameApp +{ +public: + GameApp(); + ~GameApp(); + + void Init(); + void Shutdown(); + + void UpdateFrame(); + + inline void RequestExit() { m_bExitRequested = true; } + inline bool IsExitRequested() const { return (m_bExitRequested); } + +private: + bool m_bExitRequested; +}; + +extern GameApp* g_gameApp; + +#endif // !GAME_APP_H diff --git a/src/game/game_object.cpp b/src/game/game_object.cpp new file mode 100644 index 0000000..5fd1540 --- /dev/null +++ b/src/game/game_object.cpp @@ -0,0 +1,72 @@ +#include "utils/logger.h" +#include "render/modelmanager.h" + +#include "game_object.h" + +#include + +IMPLEMENT_RTTI_ROOT(GameObject); + +GameObject::GameObject() : + m_position(0.0f), m_rotation(0.0f), m_scale(1.0f), + m_model(nullptr) +{ + m_boundingBox = g_identityBoundingBox; +} + +GameObject::~GameObject() +{ +} + +bool GameObject::IsKindOf(const RuntimeClass* _typeInfo) const +{ + for (const RuntimeClass* typeInfo = GetRuntimeClass(); typeInfo != nullptr; typeInfo = typeInfo->m_baseRuntimeClass) + if (typeInfo == _typeInfo) + return true; + + return false; +} + +GameObject* GameObject::_Construct() +{ + return new GameObject(); +} + +void GameObject::LoadModel(const char* filename) +{ + m_model = g_modelManager->LoadModel(filename); + if (!m_model) + { + Msg("Failed to load %s, no physic or animation", filename); + return; + } + + // get model bounds + m_boundingBox = m_model->GetBoundingBox(); +} + +glm::mat4 GameObject::GetModelMatrix() +{ + glm::mat4 model = glm::mat4(1.0f); + model = glm::translate(model, glm::vec3(m_position.x, m_position.y, m_position.z)); + + glm::vec3 radiansRot = glm::radians(glm::vec3(m_rotation.x, m_rotation.y, m_rotation.z)); + + // rotation + glm::mat4 rotationMatrix = glm::yawPitchRoll(radiansRot.y, radiansRot.x, radiansRot.z); + model *= rotationMatrix; + + return model; +} + +void GameObject::GetTransformedBBox( BoundingBox* boundinBox ) +{ + // local copy of BBox + BoundingBox transformedBBox = m_boundingBox; + + // rotate + TransformBoundingBox(transformedBBox, GetModelMatrix()); + + assert(boundinBox); + *boundinBox = transformedBBox; +} diff --git a/src/game/game_object.h b/src/game/game_object.h new file mode 100644 index 0000000..373835e --- /dev/null +++ b/src/game/game_object.h @@ -0,0 +1,75 @@ +#ifndef GAME_OBJECT_H +#define GAME_OBJECT_H + +#include "utils/rtti.h" +#include "utils/maths.h" +#include "utils/string.h" + +#include "physics/physics_object.h" + +class Model; + +enum GameObjectComponents +{ + GAME_OBJECT_COMPONENTS_NONE, + GAME_OBJECT_COMPONENTS_VISUAL, + GAME_OBJECT_COMPONENTS_PHYSICS, + GAME_OBJECT_COMPONENTS_COLLIDER, + GAME_OBJECT_COMPONENTS_TRIGGER, + GAME_OBJECT_COMPONENTS_GHOST, + GAME_OBJECT_COMPONENTS_SOUND, + GAME_OBJECT_COMPONENTS_PARTICLESYSTEM, + + GAME_OBJECT_COMPONENTS_MAX, +}; + +class GameObject +{ + DECLARE_RTTI_ROOT(GameObject); +public: + GameObject(); + ~GameObject(); + + bool IsKindOf(const RuntimeClass* _typeInfo) const; + + template + bool IsKindOf(); + + static GameObject* _Construct(); + + virtual void LoadModel(const char* filename); + + glm::mat4 GetModelMatrix(); + + void GetTransformedBBox( BoundingBox* boundinBox ); + + inline PhysicsObject* CreatePhysicsObject() + { + PhysicsObject* pPhysicsObject = new PhysicsObject(); + m_pPhysicsObject = pPhysicsObject; + return pPhysicsObject; + } + +public: + String m_tag; + String m_name; + + Vec3 m_position; + Vec3 m_rotation; + Vec3 m_scale; + + BoundingBox m_boundingBox; + + Model* m_model; + + PhysicsObject* m_pPhysicsObject; + +}; + +template +inline bool GameObject::IsKindOf() +{ + return IsKindOf( RUNTIME_CLASS(T) ); +} + +#endif // !GAME_OBJECT_H diff --git a/src/game/game_object_factory.cpp b/src/game/game_object_factory.cpp new file mode 100644 index 0000000..5aec8e3 --- /dev/null +++ b/src/game/game_object_factory.cpp @@ -0,0 +1,34 @@ +#include +#include + +#include "game_object_factory.h" + +static GameObjectFactory s_objectFactory; +GameObjectFactory* g_objectFactory = (GameObjectFactory*)&s_objectFactory; + +void GameObjectFactory::Register(const RuntimeClass* _pRuntimeClass, createGameObject_t _createObjectFunc) +{ + std::map::iterator it = m_factories.find(_pRuntimeClass); + assert(it == m_factories.end()); + + m_factories.emplace(_pRuntimeClass, _createObjectFunc); + +#ifdef _DEBUG + Msg("Registered object %s", _pRuntimeClass->Name()); +#endif // _DEBUG +} + +GameObject* GameObjectFactory::Create(const char* classname) +{ + std::map::iterator it = std::find_if( + m_factories.begin(), + m_factories.end(), + [=](const std::pair& p) + { + return strcmp(p.first->Name(), classname) == 0; + } + ); // m_factories.find(classname); + assert(it != m_factories.end()); + + return it->second(); +} diff --git a/src/game/game_object_factory.h b/src/game/game_object_factory.h new file mode 100644 index 0000000..105c5c9 --- /dev/null +++ b/src/game/game_object_factory.h @@ -0,0 +1,32 @@ +#ifndef GAME_OBJECT_FACTORY_H +#define GAME_OBJECT_FACTORY_H + +#include + +#include "game/game_object.h" + +typedef GameObject* (*createGameObject_t)(); + +class GameObjectFactory +{ +public: + void Register(const RuntimeClass* _pRuntimeClass, createGameObject_t _createObjectFunc); + + template + void Register(); + + GameObject* Create(const char* classname); + +private: + std::map m_factories; +}; + +template +inline void GameObjectFactory::Register() +{ + Register(GetRuntimeClass(), T::_Construct); +} + +extern GameObjectFactory* g_objectFactory; + +#endif // !GAME_OBJECT_FACTORY_H diff --git a/src/game/level.cpp b/src/game/level.cpp new file mode 100644 index 0000000..3734a10 --- /dev/null +++ b/src/game/level.cpp @@ -0,0 +1,392 @@ +#include "utils/logger.h" +#include "filesystem/file.h" +#include "filesystem/stream.h" +#include "filesystem/filemanager.h" +#include "render/modelmanager.h" +#include "render/debugrender.h" +#include "game/game_object.h" +#include "game/game_object_factory.h" +#include "game/level.h" + +Level* g_level = nullptr; + +Level::Level() : + m_busy(false), /*m_physics_world(nullptr),*/ + m_level_name(nullptr), + m_needToDestroyEnt(false) +{ + //m_physics_world = mem_new(); + //m_physics_world->Create(); +} + +Level::~Level() +{ + for (auto it : m_entities) + { + delete it; + } + + m_entities.clear(); + + //if (m_physics_world) + //{ + // m_physics_world->Destroy(); + // mem_delete(m_physics_world); + // m_physics_world = nullptr; + //} + + if (m_level_name) + { + free((void*)m_level_name); + m_level_name = nullptr; + } +} + +bool Level::IsLevelExist(const char* levelname) +{ + char levelpath[512]; + sprintf(levelpath, "content/levels/%s/", levelname); + size_t const pathend = strlen(levelpath); + strcpy_s(levelpath + pathend, sizeof(levelpath) - pathend, "level.lmf"); + + if (!g_fileManager->FileExist(levelpath)) + { + Msg("Level::IsLevelExist: not found level mesh %s", levelname); + return false; + } + + return true; +} + +void Level::Load(const char* levelname) +{ + Msg("loading level %s", levelname); + + m_level_name = strdup(levelname); + + StreamBase* reader = nullptr; + char levelpath[512]; + sprintf(levelpath, "content/levels/%s/", levelname); + size_t const pathend = strlen(levelpath); + + strcpy_s(levelpath + pathend, sizeof(levelpath) - pathend, "level.lmf"); + reader = g_fileManager->OpenStream(levelpath, FileAccess::Read); + //LoadLMF(reader); +// g_FileSystem->CloseFile(reader); + delete reader; + + //strcpy(levelpath + pathend, "level.xml"); + //SaveXML(levelpath); + + //reader = g_FileSystem->OpenRead(levelpath); + //LoadXML(reader); + //g_FileSystem->CloseFile(reader); + + // strcpy(levelpath + pathend, "level.somefile"); + // reader = g_FileSystem->openRead(levelpath); + // loadSomeFile(reader); + // etc. +} + +template +void RemoveNextLine(char(&buffer)[buffer_capacity]) +{ + for (int i = 0; i < buffer_capacity; i++) + { + if (buffer[i] == '\n') + buffer[i] = '\0'; + } +} + +void Level::LoadText(const char* filename) +{ + Msg("loading level %s", filename); + + char fn[kMaxPathLength]; + snprintf(fn, sizeof(fn), "%s/%s", g_fileManager->GetDefaultPath(), filename); + osResolvePath(fn); + FILE* file = fopen(fn, "r"); + if (!file) + { + Msg("can't load level %s file not exist", filename); + return; + } + + bool beginEntity = false; + bool endEntity = false; + char entitynameBuffer[256]; + char classnameBuffer[256]; + char visualnameBuffer[256]; + char tagnameBuffer[256]; + Vec3 pos = {0.0f}; + Vec3 rot = {0.0f}; + entitynameBuffer[0] = '\0'; + classnameBuffer[0] = '\0'; + visualnameBuffer[0] = '\0'; + tagnameBuffer[0] = '\0'; + + while (true) + { + if (endEntity) { + GameObject* gameObject = CreateEntityByClassName(classnameBuffer); + gameObject->m_position = pos; + gameObject->m_rotation = rot; + gameObject->m_tag = tagnameBuffer; + gameObject->LoadModel(visualnameBuffer); + endEntity = false; + + memset(entitynameBuffer, 0, sizeof(entitynameBuffer)); + entitynameBuffer[0] = '\0'; + memset(classnameBuffer, 0, sizeof(classnameBuffer)); + classnameBuffer[0] = '\0'; + memset(visualnameBuffer, 0, sizeof(visualnameBuffer)); + visualnameBuffer[0] = '\0'; + memset(tagnameBuffer, 0, sizeof(tagnameBuffer)); + tagnameBuffer[0] = '\0'; + } + + char lineHeader[128]; + + // read the first word of the line + int res = fscanf(file, "%s", lineHeader); + if (res == EOF) + break; // EOF = End Of File. Quit the loop. + + if (lineHeader[0] == '#') + { + // Probably a comment, eat up the rest of the line + char stupidBuffer[1000]; + fgets(stupidBuffer, 1000, file); + continue; + } + + if (!beginEntity) + { + if (strcmp(lineHeader, "BeginEntity") == 0) + { + fscanf(file, "%s", entitynameBuffer); + //fgets(entitynameBuffer, sizeof(entitynameBuffer), file); + RemoveNextLine(entitynameBuffer); + beginEntity = true; + } + } + else + { + if (strcmp(lineHeader, "ClassName") == 0) { + fscanf(file, "%s", classnameBuffer); + //fgets(classnameBuffer, sizeof(classnameBuffer), file); + RemoveNextLine(classnameBuffer); + } else if (strcmp(lineHeader, "Visual") == 0) { + fscanf(file, "%s", visualnameBuffer); + //fgets(visualnameBuffer, sizeof(visualnameBuffer), file); + RemoveNextLine(visualnameBuffer); + } else if (strcmp(lineHeader, "Position") == 0) { + fscanf(file, "%f %f %f\n", &pos.x, &pos.y, &pos.z); + } else if (strcmp(lineHeader, "Rotation") == 0) { + fscanf(file, "%f %f %f\n", &rot.x, &rot.y, &rot.z); + } else if (strcmp(lineHeader, "EndEntity") == 0) { + beginEntity = false; + endEntity = true; + }else if (strcmp(lineHeader, "Tag") == 0) { + fscanf(file, "%s", tagnameBuffer); + RemoveNextLine(tagnameBuffer); + } + } + } + + fclose(file); +} + +GameObject* Level::CreateEntity() +{ + if (m_busy) + { +#ifdef _DEBUG + Logger::error("Level::CreateEntity: level is currently busy"); +#endif + return (NULL); + } + + GameObject* entity = GameObject::_Construct(); + m_entities.push_back(entity); + return entity; +} + +GameObject* Level::CreateEntityByRC(const RuntimeClass* _pRuntimeClass) +{ + return CreateEntityByClassName(_pRuntimeClass->Name()); +} + +GameObject* Level::CreateEntityByClassName(const char* classname) +{ + if (m_busy) + { +#ifdef _DEBUG + Logger::error("Level::CreateEntityByClassName: level is currently busy"); +#endif + return (NULL); + } + + + GameObject* entity = g_objectFactory->Create(classname); + if (!entity) + { + Logger::error("Level::CreateEntityByClassName: Entity with classname '%s' is unregistered", classname); + return (NULL); + } + + m_entities.push_back(entity); + return entity; +} + +void Level::AddEntity(GameObject* entity) +{ + if (m_busy) + { + Logger::error("Level::AddEntity: level is currently busy"); + } + if (!entity) + { + Logger::error("Level::AddEntity: failed to add NULL entity"); + } + + m_entities.push_back(entity); +} + +void Level::DestroyEntity(GameObject* entity) +{ + if (!entity) return; + + std::vector::iterator it = std::find( + m_entities.begin(), + m_entities.end(), + entity + ); + if (it != m_entities.end()) + { + if (m_busy) + { + m_entitiesToDestroy.push_back(*it); + m_needToDestroyEnt = true; + } + else + { + delete *it; + m_entities.erase(it); + } + } +} + +#include "game/player.h" + +void Level::Update(float fDeltaTime) +{ + if (m_needToDestroyEnt) + { + DestroyEntities(); + m_needToDestroyEnt = false; + } + + m_busy = true; + + g_PhysicsWorld->Update(fDeltaTime); + + //if (m_physics_world) + // m_physics_world->Update(fDeltaTime); + + for (auto it : m_entities) + { + if ( it->IsKindOf() ) + { + Player* player = ( Player* )it; + player->Update( fDeltaTime ); + } + } + + m_busy = false; +} + +void Level::Render() +{ + for (int i = 0; i < m_entities.size(); i++) + { + GameObject* ent = m_entities[i]; + if (ent->m_model) + ent->m_model->Draw(ent->GetModelMatrix()); + + static bool showBounds = true; + if (showBounds) + { + BoundingBox bbox; + ent->GetTransformedBBox(&bbox); + g_debugRender->DrawBoundingBox(bbox, glm::vec3(1.0f)); + + // if (strcmp(ent->GetRuntimeClass()->Name(), "Player")) + // g_debugRender->DrawEllipse(ent->GetModelMatrix(), Vec3(0.5f, 0.5f, 1.0f)); + } + } +} + +GameObject* Level::GetObjectByTag(const String& tag) +{ + std::vector::iterator it = std::find_if( + m_entities.begin(), + m_entities.end(), + [=](const GameObject* O) + { + return tag == O->m_tag; + } + ); + + if (it != m_entities.end()) + return *it; + + return nullptr; +} + +const char* Level::GetLevelName() +{ + return m_level_name; +} + +void Level::DestroyEntities() +{ + // Remove entities from pool + m_entities.erase(std::remove_if(m_entities.begin(), m_entities.end(), + [=](GameObject* pE) + { + if (FindEntityToDestroy(pE)) + { + return (true); + } + return (false); + } + ), + m_entities.end() + ); + + // Delete entities + for (int i = 0; i < m_entitiesToDestroy.size(); i++) + { + if (m_entitiesToDestroy[i]) + delete m_entitiesToDestroy[i]; + } + + m_entitiesToDestroy.clear(); + + // Ok +} + +bool Level::FindEntityToDestroy(GameObject* O) +{ + std::vector::iterator it = std::find( + m_entitiesToDestroy.begin(), + m_entitiesToDestroy.end(), + O + ); + + if (it != m_entitiesToDestroy.end()) + return true; + + return false; +} diff --git a/src/game/level.h b/src/game/level.h new file mode 100644 index 0000000..961f4c3 --- /dev/null +++ b/src/game/level.h @@ -0,0 +1,50 @@ +#ifndef LEVEL_H +#define LEVEL_H + +#include + +class GameObject; + +class Level +{ +public: + static bool IsLevelExist(const char* levelname); + +public: + Level(); + ~Level(); + + void Load(const char* levelname); + void LoadText(const char* filename); + + GameObject* CreateEntity(); + GameObject* CreateEntityByRC(const RuntimeClass* _pRuntimeClass); + GameObject* CreateEntityByClassName(const char* classname); + void AddEntity(GameObject* entity); + void DestroyEntity(GameObject* entity); + + void Update(float fDeltaTime); + void Render(); + + GameObject* GetObjectByTag(const String& tag); + + std::vector& GetEntities() { return m_entities; } + //PhysicsWorld* GetPhysicsWorld(); + const char* GetLevelName(); + +private: + void DestroyEntities(); + bool FindEntityToDestroy(GameObject* O); + +private: + bool m_busy; + bool m_needToDestroyEnt; + std::vector m_entities; + std::vector m_entitiesToDestroy; + //PhysicsWorld* m_physics_world; + const char* m_level_name; +}; + +extern Level* g_level; + +#endif // !LEVEL_H diff --git a/src/game/lua_impl.c b/src/game/lua_impl.c new file mode 100644 index 0000000..29b81a1 --- /dev/null +++ b/src/game/lua_impl.c @@ -0,0 +1,2 @@ +#define LUA_IMPL +#include "minilua.h" \ No newline at end of file diff --git a/src/game/main_menu.cpp b/src/game/main_menu.cpp new file mode 100644 index 0000000..518bf83 --- /dev/null +++ b/src/game/main_menu.cpp @@ -0,0 +1,44 @@ +#include "game/main_menu.h" +#include "input/input_system.h" +#include "render/ui.h" + +MainMenu* g_pMainMenu = nullptr; + +MainMenu::MainMenu() +{ + //// HD Resolution - 1280x720 + //Button g_startGameButton = { Vec2{ (1280.0f / 2.f) - 128.f , (720.f / 2.f)}, Vec2{256.f, 128.f} }; + //Button g_quitButton = { Vec2{ (1280.0f / 2.f) - 128.f , (720.f / 2.f) + 128.f + 6.0f}, Vec2{256.f, 128.f} }; + + m_startGameButton.pos = Vec2{ (1280.0f / 2.f) - 128.f , (720.f / 2.f) }; + m_startGameButton.size = Vec2{ 256.f, 128.f }; + + m_quitButton = { Vec2{ (1280.0f / 2.f) - 128.f , (720.f / 2.f) + 128.f + 6.0f}, Vec2{256.f, 128.f} }; +} + +MainMenu::~MainMenu() +{ +} + +void MainMenu::Update() +{ +} + +void MainMenu::Render() +{ + //Vec4 color = { 1.0f, 0.0f, 0.0f, 1.0f }; + + +} + +bool buttonIsSelected(const Button* button) +{ + int32_t x = 0, y = 0; + g_inputSystem->GetMousePosition(&x, &y); + + return GetBoxIntersection2D( + Vec2{ (float)x + 16.0f, (float)y + 35.0f }, + Vec2{ (float)x,(float)y }, + Vec2{ button->pos.x + button->size.x, button->pos.y + button->size.y }, + button->pos); +} \ No newline at end of file diff --git a/src/game/main_menu.h b/src/game/main_menu.h new file mode 100644 index 0000000..84beb2c --- /dev/null +++ b/src/game/main_menu.h @@ -0,0 +1,31 @@ +#ifndef MAIN_MENU_H +#define MAIN_MENU_H + +#include + +struct Button +{ + Vec2 pos; + Vec2 size; +}; + +bool buttonIsSelected(const Button* button); + +class MainMenu +{ +public: + MainMenu(); + ~MainMenu(); + + void Update(); + void Render(); + +private: + Button m_startGameButton; + Button m_quitButton; + +}; + +extern MainMenu* g_pMainMenu; + +#endif // !MAIN_MENU_H diff --git a/src/game/phys_test.cpp b/src/game/phys_test.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/game/phys_test.h b/src/game/phys_test.h new file mode 100644 index 0000000..1a20e5c --- /dev/null +++ b/src/game/phys_test.h @@ -0,0 +1,6 @@ +#ifndef PHYS_TEST_H +#define PHYS_TEST_H + + + +#endif // !PHYS_TEST_H diff --git a/src/game/player.cpp b/src/game/player.cpp new file mode 100644 index 0000000..da3d809 --- /dev/null +++ b/src/game/player.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "input/input_system.h" + +#include "render/render_shared.h" +#include "render/modelmanager.h" +#include "render/debugrender.h" + +#include "game/player.h" +#include "game/camera.h" +#include "game/level.h" + +IMPLEMENT_RTTI(Player, GameObject); + +// Object factory +GameObject* Player::_Construct() +{ + return new Player(); +} + +Player::Player() +{ + Msg(" --- Creating Player ---"); + + //m_weapon = (Ship_Weapon*)g_level->CreateEntityByRC( RUNTIME_CLASS( Ship_Weapon ) ); + //m_weapon->LoadModel("content/test/ship_weapon.obj"); +} + +Player::~Player() +{ +} + +void Player::Update(float dt) +{ + // Update camera + + int32_t x = 0, y = 0; + g_inputSystem->GetMousePosition(&x, &y); + + cameraUpdateMouseLook((int)x, (int)y); + + const float kCameraOffset = 1.2f; + const float kBackSpeed = 1.2f; + float kPlayerSpeed = 2.0f; + if (g_inputSystem->IsKeyPressed(KEY_LEFT_SHIFT)) + kPlayerSpeed = 9.0f; + + // Update player itself + + Vec3 dir = normalize(cameraGetDir()); +// dir.y = 0.0f; + + if (g_inputSystem->IsKeyPressed(KEY_W)) + m_position += kPlayerSpeed * dt * dir; + if (g_inputSystem->IsKeyPressed(KEY_S)) + m_position -= kPlayerSpeed * dt * dir; + if (g_inputSystem->IsKeyPressed(KEY_A)) + m_position -= kPlayerSpeed * dt * cross(normalize(dir), Vec3(0.0f, 1.0f, 0.0f)); + if (g_inputSystem->IsKeyPressed(KEY_D)) + m_position += kPlayerSpeed * dt * cross(normalize(dir), Vec3(0.0f, 1.0f, 0.0f)); + + if (g_inputSystem->IsKeyPressed(KEY_P)) + Msg("\nPlayer Position:\nVec3(%.2f, %.2f, %.2f)\nPosition %.2f %.2f %.2f\n", m_position.x, m_position.y, m_position.z, + m_position.x, m_position.y, m_position.z); + + Vec3 cameraPos = m_position; + cameraPos.y += 0.4f; + + cameraUpdateLook_FPS(cameraPos); + cameraPushToRender(); + + g_debugRender->DrawEllipse(this->GetModelMatrix(), Vec3(0.4f, 0.4f, 1.0f)); +} diff --git a/src/game/player.h b/src/game/player.h new file mode 100644 index 0000000..a7c2187 --- /dev/null +++ b/src/game/player.h @@ -0,0 +1,30 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include "game/game_object.h" + +class Ship_Weapon; + +class Player : public GameObject +{ + DECLARE_RTTI( Player, GameObject ); + +public: + // Object factory + static GameObject* _Construct(); + +public: + Player(); + ~Player(); + + void Update( float dt ); + + +private: + Ship_Weapon* m_weapon; + + Vec3 m_linearVelocity; + +}; + +#endif // !PLAYER_H diff --git a/src/game/script_system.cpp b/src/game/script_system.cpp new file mode 100644 index 0000000..649ed8d --- /dev/null +++ b/src/game/script_system.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include "script_system.h" +#include "engine/utils/logger.h" + +static int Engine_Msg(lua_State* L) +{ + const char* msg = lua_tostring(L, 1); + if (msg == nullptr) + { + return ScriptSystem::PushError(L, "engine.msg: missing string argument"); + } + + Msg("[LUA]: %s", msg); + return 1; +} + +static int Engine_LoadScript(lua_State* L) +{ + const char* filename = lua_tostring(L, 1); + g_scriptSystem.DoFile(filename); + return 1; +} + +static int Engine_StartThread(lua_State* L) +{ + const char* threadname = lua_tostring(L, 1); + const char* functionname = lua_tostring(L, 2); + + g_ScriptThreadManager.CreateThread(threadname, functionname); + + return 1; +} + +static const struct luaL_Reg g_engineFunctions[] = +{ + { "msg", Engine_Msg }, + { "load_script", Engine_LoadScript }, + { "start_thread", Engine_StartThread }, + { 0, 0 } +}; + +ScriptSystem g_scriptSystem; + +ScriptSystem::ScriptSystem() : + m_State(nullptr) +{ +} + +ScriptSystem::~ScriptSystem() +{ +} + +static int EngineLib_require(lua_State* L) +{ + luaL_newlib(L, g_engineFunctions); + return 1; +} + +void ScriptSystem::Init() +{ + Msg("Script system initializing ..."); + + m_State = luaL_newstate(); + luaL_openlibs(m_State); + + // register engine lib + luaL_requiref(m_State, "engine", EngineLib_require, 1); + lua_pop(m_State, 1); +} + +void ScriptSystem::Shutdown() +{ + if (m_State) + { + lua_close(m_State); + m_State = nullptr; + } +} + +lua_State* ScriptSystem::GetState() +{ + return m_State; +} + +void ScriptSystem::DoFile(const char* filename) +{ + lua_State* L = m_State; + + int r = luaL_dofile(L, filename); + if (r != LUA_OK) + { + Msg("Lua Error: %s", lua_tostring(L, lua_gettop(L))); + lua_pop(L, lua_gettop(L)); + } +} + +void ScriptSystem::DoString(const char* functionName) +{ + lua_State* L = m_State; + + int r = luaL_dostring(L, functionName); + if (r != LUA_OK) + { + Msg("Lua Error: %s", lua_tostring(L, lua_gettop(L))); + lua_pop(L, lua_gettop(L)); + } +} + +int ScriptSystem::PushError(lua_State* L, const char* errorMsg) +{ + lua_pushstring(L, errorMsg); + return lua_error(L); +} + +void ScriptSystem::RegisterGlobalInt(const char* name, int value) +{ + lua_pushnumber(m_State, value); + lua_setglobal(m_State, name); +} + +ScriptThreadManager g_ScriptThreadManager; + +ScriptThreadManager::ScriptThreadManager() +{ +} + +ScriptThreadManager::~ScriptThreadManager() +{ +} + +lua_State* ScriptThreadManager::CreateThread(const char* name, const char* functionName) +{ + assert(name); + assert(functionName); + assert(g_scriptSystem.GetState() && "Couldn't create script thread while script system is not initialized"); + + Msg("Start script thread %s with function %s", name, functionName); + + lua_State* L = g_scriptSystem.GetState(); + + ScriptThreadInfo info = {}; + info.threadname = strdup(name); // DMan to Kirill : unfreed memory + info.state = lua_newthread(L); + m_Threads.push_back(info); + +#if 0 + // get function from base state + int functionId = 0; + + // push globals + lua_pushglobaltable(L); + + // push nil to stack + lua_pushnil(L); + + while (lua_next(L, -2) != 0) + { + const char* variableName = lua_tostring(L, -2); + Msg("%s", variableName); + if (strcmp(variableName, functionName) == 0) + { + if (!lua_isfunction(L, -2)) + { + FATAL("Lua: %s is not a function!", functionName); + } + else + { + // execute it from thread + + } + } + + lua_pop(L, 1); + } + + // pop globals + lua_pop(L, 1); +#else + char funcBuf[256]; + sprintf(funcBuf, "%s()", functionName); + luaL_dostring(info.state, funcBuf); +#endif + + //lua_resume(info.state, 0, 0, 0); + lua_resetthread(info.state); + + return info.state; +} diff --git a/src/game/script_system.h b/src/game/script_system.h new file mode 100644 index 0000000..9dfdfbc --- /dev/null +++ b/src/game/script_system.h @@ -0,0 +1,54 @@ +#ifndef SCRIPT_SYSTEM_H +#define SCRIPT_SYSTEM_H + +extern "C" +{ +#include "minilua.h" +} + +class ScriptSystem +{ +public: + ScriptSystem(); + ~ScriptSystem(); + + void Init(); + void Shutdown(); + + lua_State* GetState(); + + void DoFile(const char* filename); + void DoString(const char* functionName); + + static int PushError(lua_State* L, const char* errorMsg); + + void RegisterGlobalInt(const char* name, int value); + +private: + lua_State* m_State; +}; + +extern ScriptSystem g_scriptSystem; + +struct ScriptThreadInfo +{ + const char* threadname; + lua_State* state; +}; + +// Thread manager +class ScriptThreadManager +{ +public: + ScriptThreadManager(); + ~ScriptThreadManager(); + + lua_State* CreateThread(const char* name, const char* functionName); + +private: + std::vector m_Threads; +}; + +extern ScriptThreadManager g_ScriptThreadManager; + +#endif // !SCRIPT_SYSTEM_H diff --git a/src/main/main.cpp b/src/main/main.cpp new file mode 100644 index 0000000..eb83c1a --- /dev/null +++ b/src/main/main.cpp @@ -0,0 +1,45 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include "game/game_app.h" + +void StartUp() +{ + g_gameApp = new GameApp(); + g_gameApp->Init(); +} + +void Shutdown() +{ + g_gameApp->Shutdown(); + delete g_gameApp; + g_gameApp = nullptr; +} + +void Frame() +{ + MSG msg = {}; + while (msg.message != WM_QUIT && !g_gameApp->IsExitRequested()) + { + if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + else + { + g_gameApp->UpdateFrame(); + } + } +} + +int main(int argc, char* argv[]) +{ + StartUp(); + + Frame(); + + Shutdown(); + + return 0; +} \ No newline at end of file diff --git a/thirdparty/GL/glcorearb.h b/thirdparty/GL/glcorearb.h new file mode 100644 index 0000000..2d95c38 --- /dev/null +++ b/thirdparty/GL/glcorearb.h @@ -0,0 +1,5997 @@ +#ifndef __gl_glcorearb_h_ +#define __gl_glcorearb_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT +** +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** https://github.com/KhronosGroup/OpenGL-Registry +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +/* glcorearb.h is for use with OpenGL core profile implementations. +** It should should be placed in the same directory as gl.h and +** included as . +** +** glcorearb.h includes only APIs in the latest OpenGL core profile +** implementation together with APIs in newer ARB extensions which +** can be supported by the core profile. It does not, and never will +** include functionality removed from the core profile, such as +** fixed-function vertex and fragment processing. +** +** Do not #include both and either of or +** in the same source file. +*/ + +/* Generated C header for: + * API: gl + * Profile: core + * Versions considered: .* + * Versions emitted: .* + * Default extensions included: glcore + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 +typedef void GLvoid; +typedef unsigned int GLenum; +#include +typedef khronos_float_t GLfloat; +typedef int GLint; +typedef int GLsizei; +typedef unsigned int GLbitfield; +typedef double GLdouble; +typedef unsigned int GLuint; +typedef unsigned char GLboolean; +typedef khronos_uint8_t GLubyte; +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); +typedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); +typedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size); +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum buf); +typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); +typedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); +typedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); +typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLFINISHPROC) (void); +typedef void (APIENTRYP PFNGLFLUSHPROC) (void); +typedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); +typedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode); +typedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); +typedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum src); +typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *data); +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *data); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); +typedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *data); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullFace (GLenum mode); +GLAPI void APIENTRY glFrontFace (GLenum mode); +GLAPI void APIENTRY glHint (GLenum target, GLenum mode); +GLAPI void APIENTRY glLineWidth (GLfloat width); +GLAPI void APIENTRY glPointSize (GLfloat size); +GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); +GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glDrawBuffer (GLenum buf); +GLAPI void APIENTRY glClear (GLbitfield mask); +GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glClearStencil (GLint s); +GLAPI void APIENTRY glClearDepth (GLdouble depth); +GLAPI void APIENTRY glStencilMask (GLuint mask); +GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GLAPI void APIENTRY glDepthMask (GLboolean flag); +GLAPI void APIENTRY glDisable (GLenum cap); +GLAPI void APIENTRY glEnable (GLenum cap); +GLAPI void APIENTRY glFinish (void); +GLAPI void APIENTRY glFlush (void); +GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GLAPI void APIENTRY glLogicOp (GLenum opcode); +GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GLAPI void APIENTRY glDepthFunc (GLenum func); +GLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); +GLAPI void APIENTRY glReadBuffer (GLenum src); +GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *data); +GLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *data); +GLAPI GLenum APIENTRY glGetError (void); +GLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *data); +GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data); +GLAPI const GLubyte *APIENTRY glGetString (GLenum name); +GLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); +GLAPI void APIENTRY glDepthRange (GLdouble n, GLdouble f); +GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_0 */ + +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 +typedef khronos_float_t GLclampf; +typedef double GLclampd; +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DOUBLE 0x140A +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_VERTEX_ARRAY 0x8074 +typedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glGetPointerv (GLenum pname, void **params); +GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTexture (GLuint texture); +#endif +#endif /* GL_VERSION_1_1 */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +typedef khronos_ssize_t GLsizeiptr; +typedef khronos_intptr_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +typedef khronos_int16_t GLshort; +typedef khronos_int8_t GLbyte; +typedef khronos_uint16_t GLushort; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef khronos_uint16_t GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +typedef khronos_uint64_t GLuint64; +typedef khronos_int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); +typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); +typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); +GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); +GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); +GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); +GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); +GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); +GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); +GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); +GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); +GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); +GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); +GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); +GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); +GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); +GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); +GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); +GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_VERSION_4_6 +#define GL_VERSION_4_6 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED +typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_VERSION_4_6 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_2_compatibility +#define GL_ARB_ES3_2_compatibility 1 +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 +typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveBoundingBoxARB (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#endif +#endif /* GL_ARB_ES3_2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef khronos_uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_shader_interlock +#define GL_ARB_fragment_shader_interlock 1 +#endif /* GL_ARB_fragment_shader_interlock */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gl_spirv +#define GL_ARB_gl_spirv 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SPIR_V_BINARY_ARB 0x9552 +typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpecializeShaderARB (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#endif +#endif /* GL_ARB_gl_spirv */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_gpu_shader_int64 +#define GL_ARB_gpu_shader_int64 1 +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 +typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64ARB (GLint location, GLint64 x); +GLAPI void APIENTRY glUniform2i64ARB (GLint location, GLint64 x, GLint64 y); +GLAPI void APIENTRY glUniform3i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z); +GLAPI void APIENTRY glUniform4i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +GLAPI void APIENTRY glUniform1i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform2i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform3i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform4i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform1ui64ARB (GLint location, GLuint64 x); +GLAPI void APIENTRY glUniform2ui64ARB (GLint location, GLuint64 x, GLuint64 y); +GLAPI void APIENTRY glUniform3ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +GLAPI void APIENTRY glUniform4ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +GLAPI void APIENTRY glUniform1ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform2ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform3ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform4ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glGetUniformi64vARB (GLuint program, GLint location, GLint64 *params); +GLAPI void APIENTRY glGetUniformui64vARB (GLuint program, GLint location, GLuint64 *params); +GLAPI void APIENTRY glGetnUniformi64vARB (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glGetnUniformui64vARB (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); +GLAPI void APIENTRY glProgramUniform1i64ARB (GLuint program, GLint location, GLint64 x); +GLAPI void APIENTRY glProgramUniform2i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y); +GLAPI void APIENTRY glProgramUniform3i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +GLAPI void APIENTRY glProgramUniform4i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +GLAPI void APIENTRY glProgramUniform1i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform2i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform3i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform4i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform1ui64ARB (GLuint program, GLint location, GLuint64 x); +GLAPI void APIENTRY glProgramUniform2ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y); +GLAPI void APIENTRY glProgramUniform3ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +GLAPI void APIENTRY glProgramUniform4ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +GLAPI void APIENTRY glProgramUniform1ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform2ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform3ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform4ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#endif +#endif /* GL_ARB_gpu_shader_int64 */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMaxShaderCompilerThreadsARB (GLuint count); +#endif +#endif /* GL_ARB_parallel_shader_compile */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_polygon_offset_clamp +#define GL_ARB_polygon_offset_clamp 1 +#endif /* GL_ARB_polygon_offset_clamp */ + +#ifndef GL_ARB_post_depth_coverage +#define GL_ARB_post_depth_coverage 1 +#endif /* GL_ARB_post_depth_coverage */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_locations +#define GL_ARB_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSampleLocationsfvARB (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvARB (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glEvaluateDepthValuesARB (void); +#endif +#endif /* GL_ARB_sample_locations */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counter_ops +#define GL_ARB_shader_atomic_counter_ops 1 +#endif /* GL_ARB_shader_atomic_counter_ops */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_ballot +#define GL_ARB_shader_ballot 1 +#endif /* GL_ARB_shader_ballot */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_clock +#define GL_ARB_shader_clock 1 +#endif /* GL_ARB_shader_clock */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +#endif /* GL_ARB_shader_viewport_layer_array */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_sparse_texture2 +#define GL_ARB_sparse_texture2 1 +#endif /* GL_ARB_sparse_texture2 */ + +#ifndef GL_ARB_sparse_texture_clamp +#define GL_ARB_sparse_texture_clamp 1 +#endif /* GL_ARB_sparse_texture_clamp */ + +#ifndef GL_ARB_spirv_extensions +#define GL_ARB_spirv_extensions 1 +#endif /* GL_ARB_spirv_extensions */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_filter_anisotropic +#define GL_ARB_texture_filter_anisotropic 1 +#endif /* GL_ARB_texture_filter_anisotropic */ + +#ifndef GL_ARB_texture_filter_minmax +#define GL_ARB_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#endif /* GL_ARB_texture_filter_minmax */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangeArraydvNV (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexeddNV (GLuint index, GLdouble n, GLdouble f); +#endif +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendBarrierKHR (void); +#endif +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count); +#endif +#endif /* GL_KHR_parallel_shader_compile */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_shader_subgroup +#define GL_KHR_shader_subgroup 1 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#endif /* GL_KHR_shader_subgroup */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_KHR_texture_compression_astc_sliced_3d +#define GL_KHR_texture_compression_astc_sliced_3d 1 +#endif /* GL_KHR_texture_compression_astc_sliced_3d */ + +#ifndef GL_AMD_framebuffer_multisample_advanced +#define GL_AMD_framebuffer_multisample_advanced 1 +#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 +#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 +#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 +#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 +#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 +#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_AMD_framebuffer_multisample_advanced */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_EXT_EGL_image_storage +#define GL_EXT_EGL_image_storage 1 +typedef void *GLeglImageOES; +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list); +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list); +GLAPI void APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#endif +#endif /* GL_EXT_EGL_image_storage */ + +#ifndef GL_EXT_EGL_sync +#define GL_EXT_EGL_sync 1 +#endif /* GL_EXT_EGL_sync */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); +GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); +GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_multiview_tessellation_geometry_shader +#define GL_EXT_multiview_tessellation_geometry_shader 1 +#endif /* GL_EXT_multiview_tessellation_geometry_shader */ + +#ifndef GL_EXT_multiview_texture_multisample +#define GL_EXT_multiview_texture_multisample 1 +#endif /* GL_EXT_multiview_texture_multisample */ + +#ifndef GL_EXT_multiview_timer_query +#define GL_EXT_multiview_timer_query 1 +#endif /* GL_EXT_multiview_timer_query */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRasterSamplesEXT (GLuint samples, GLboolean fixedsamplelocations); +#endif +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_shader_framebuffer_fetch +#define GL_EXT_shader_framebuffer_fetch 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#endif /* GL_EXT_shader_framebuffer_fetch */ + +#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 +typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferFetchBarrierEXT (void); +#endif +#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_sRGB_R8 +#define GL_EXT_texture_sRGB_R8 1 +#define GL_SR8_EXT 0x8FBD +#endif /* GL_EXT_texture_sRGB_R8 */ + +#ifndef GL_EXT_texture_sRGB_RG8 +#define GL_EXT_texture_sRGB_RG8 1 +#define GL_SRG8_EXT 0x8FBE +#endif /* GL_EXT_texture_sRGB_RG8 */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shadow_lod +#define GL_EXT_texture_shadow_lod 1 +#endif /* GL_EXT_texture_shadow_lod */ + +#ifndef GL_EXT_texture_storage +#define GL_EXT_texture_storage 1 +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_ALPHA8_EXT 0x803C +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGB32F_EXT 0x8815 +#define GL_ALPHA32F_EXT 0x8816 +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_ALPHA16F_EXT 0x881C +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGB10_EXT 0x8052 +#define GL_BGRA8_EXT 0x93A1 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#define GL_R32F_EXT 0x822E +#define GL_RG32F_EXT 0x8230 +#define GL_R16F_EXT 0x822D +#define GL_RG16F_EXT 0x822F +typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_EXT_texture_storage */ + +#ifndef GL_EXT_window_rectangles +#define GL_EXT_window_rectangles 1 +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 +typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowRectanglesEXT (GLenum mode, GLsizei count, const GLint *box); +#endif +#endif /* GL_EXT_window_rectangles */ + +#ifndef GL_INTEL_blackhole_render +#define GL_INTEL_blackhole_render 1 +#define GL_BLACKHOLE_RENDER_INTEL 0x83FC +#endif /* GL_INTEL_blackhole_render */ + +#ifndef GL_INTEL_conservative_rasterization +#define GL_INTEL_conservative_rasterization 1 +#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE +#endif /* GL_INTEL_conservative_rasterization */ + +#ifndef GL_INTEL_framebuffer_CMAA +#define GL_INTEL_framebuffer_CMAA 1 +typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyFramebufferAttachmentCMAAINTEL (void); +#endif +#endif /* GL_INTEL_framebuffer_CMAA */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); +GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); +GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); +GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); +GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); +GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESA_framebuffer_flip_x +#define GL_MESA_framebuffer_flip_x 1 +#define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC +#endif /* GL_MESA_framebuffer_flip_x */ + +#ifndef GL_MESA_framebuffer_flip_y +#define GL_MESA_framebuffer_flip_y 1 +#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_MESA_framebuffer_flip_y */ + +#ifndef GL_MESA_framebuffer_swap_xy +#define GL_MESA_framebuffer_swap_xy 1 +#define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD +#endif /* GL_MESA_framebuffer_swap_xy */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessCountNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessCountNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); +GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_minmax_factor +#define GL_NV_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_NV_blend_minmax_factor */ + +#ifndef GL_NV_clip_space_w_scaling +#define GL_NV_clip_space_w_scaling 1 +#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C +#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D +#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E +typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glViewportPositionWScaleNV (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#endif +#endif /* GL_NV_clip_space_w_scaling */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 +typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); +typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); +typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); +typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); +typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); +typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); +typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); +typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); +typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCreateStatesNV (GLsizei n, GLuint *states); +GLAPI void APIENTRY glDeleteStatesNV (GLsizei n, const GLuint *states); +GLAPI GLboolean APIENTRY glIsStateNV (GLuint state); +GLAPI void APIENTRY glStateCaptureNV (GLuint state, GLenum mode); +GLAPI GLuint APIENTRY glGetCommandHeaderNV (GLenum tokenID, GLuint size); +GLAPI GLushort APIENTRY glGetStageIndexNV (GLenum shadertype); +GLAPI void APIENTRY glDrawCommandsNV (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsAddressNV (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesNV (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesAddressNV (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCreateCommandListsNV (GLsizei n, GLuint *lists); +GLAPI void APIENTRY glDeleteCommandListsNV (GLsizei n, const GLuint *lists); +GLAPI GLboolean APIENTRY glIsCommandListNV (GLuint list); +GLAPI void APIENTRY glListDrawCommandsStatesClientNV (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCommandListSegmentsNV (GLuint list, GLuint segments); +GLAPI void APIENTRY glCompileCommandListNV (GLuint list); +GLAPI void APIENTRY glCallCommandListNV (GLuint list); +#endif +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_shader_derivatives +#define GL_NV_compute_shader_derivatives 1 +#endif /* GL_NV_compute_shader_derivatives */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 +typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybits); +#endif +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_conservative_raster_dilate +#define GL_NV_conservative_raster_dilate 1 +#define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 +#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A +#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConservativeRasterParameterfNV (GLenum pname, GLfloat value); +#endif +#endif /* GL_NV_conservative_raster_dilate */ + +#ifndef GL_NV_conservative_raster_pre_snap +#define GL_NV_conservative_raster_pre_snap 1 +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 +#endif /* GL_NV_conservative_raster_pre_snap */ + +#ifndef GL_NV_conservative_raster_pre_snap_triangles +#define GL_NV_conservative_raster_pre_snap_triangles 1 +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConservativeRasterParameteriNV (GLenum pname, GLint param); +#endif +#endif /* GL_NV_conservative_raster_pre_snap_triangles */ + +#ifndef GL_NV_conservative_raster_underestimation +#define GL_NV_conservative_raster_underestimation 1 +#endif /* GL_NV_conservative_raster_underestimation */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_draw_vulkan_image +#define GL_NV_draw_vulkan_image 1 +typedef void (APIENTRY *GLVULKANPROCNV)(void); +typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name); +typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawVkImageNV (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +GLAPI GLVULKANPROCNV APIENTRY glGetVkProcAddrNV (const GLchar *name); +GLAPI void APIENTRY glWaitVkSemaphoreNV (GLuint64 vkSemaphore); +GLAPI void APIENTRY glSignalVkSemaphoreNV (GLuint64 vkSemaphore); +GLAPI void APIENTRY glSignalVkFenceNV (GLuint64 vkFence); +#endif +#endif /* GL_NV_draw_vulkan_image */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE +typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentCoverageColorNV (GLuint color); +#endif +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_shader_barycentric +#define GL_NV_fragment_shader_barycentric 1 +#endif /* GL_NV_fragment_shader_barycentric */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); +typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCoverageModulationTableNV (GLsizei n, const GLfloat *v); +GLAPI void APIENTRY glGetCoverageModulationTableNV (GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glCoverageModulationNV (GLenum components); +#endif +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +typedef khronos_int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 +typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#endif +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_memory_attachment +#define GL_NV_memory_attachment 1 +#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 +#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 +#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 +#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 +#define GL_MEMORY_ATTACHABLE_NV 0x95A8 +#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 +#define GL_DETACHED_TEXTURES_NV 0x95AA +#define GL_DETACHED_BUFFERS_NV 0x95AB +#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC +#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD +typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); +typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname); +typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); +GLAPI void APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname); +GLAPI void APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_NV_memory_attachment */ + +#ifndef GL_NV_memory_object_sparse +#define GL_NV_memory_object_sparse 1 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentMemNV (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +GLAPI void APIENTRY glTexPageCommitmentMemNV (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentMemNV (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +GLAPI void APIENTRY glTexturePageCommitmentMemNV (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#endif +#endif /* GL_NV_memory_object_sparse */ + +#ifndef GL_NV_mesh_shader +#define GL_NV_mesh_shader 1 +#define GL_MESH_SHADER_NV 0x9559 +#define GL_TASK_SHADER_NV 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 +#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 +#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 +#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 +#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A +#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D +#define GL_MAX_MESH_VIEWS_NV 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 +#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B +#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C +#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E +#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F +#define GL_MESH_VERTICES_OUT_NV 0x9579 +#define GL_MESH_PRIMITIVES_OUT_NV 0x957A +#define GL_MESH_OUTPUT_TYPE_NV 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 +#define GL_MESH_SHADER_BIT_NV 0x00000040 +#define GL_TASK_SHADER_BIT_NV 0x00000080 +#define GL_MESH_SUBROUTINE_NV 0x957C +#define GL_TASK_SUBROUTINE_NV 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F +typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count); +typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshTasksNV (GLuint first, GLuint count); +GLAPI void APIENTRY glDrawMeshTasksIndirectNV (GLintptr indirect); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectNV (GLintptr indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountNV (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_NV_mesh_shader */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +GLAPI void APIENTRY glMatrixLoad3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoad3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); +GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_primitive_shading_rate +#define GL_NV_primitive_shading_rate 1 +#define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1 +#define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2 +#endif /* GL_NV_primitive_shading_rate */ + +#ifndef GL_NV_representative_fragment_test +#define GL_NV_representative_fragment_test 1 +#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F +#endif /* GL_NV_representative_fragment_test */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSampleLocationsfvNV (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvNV (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glResolveDepthValuesNV (void); +#endif +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_scissor_exclusive +#define GL_NV_scissor_exclusive 1 +#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 +#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v); +#endif +#endif /* GL_NV_scissor_exclusive */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_float64 +#define GL_NV_shader_atomic_float64 1 +#endif /* GL_NV_shader_atomic_float64 */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_subgroup_partitioned +#define GL_NV_shader_subgroup_partitioned 1 +#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 +#endif /* GL_NV_shader_subgroup_partitioned */ + +#ifndef GL_NV_shader_texture_footprint +#define GL_NV_shader_texture_footprint 1 +#endif /* GL_NV_shader_texture_footprint */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_shading_rate_image +#define GL_NV_shading_rate_image 1 +#define GL_SHADING_RATE_IMAGE_NV 0x9563 +#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 +#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 +#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 +#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A +#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B +#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C +#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D +#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E +#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F +#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B +#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C +#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D +#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E +#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F +#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE +#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF +#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 +typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate); +typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindShadingRateImageNV (GLuint texture); +GLAPI void APIENTRY glGetShadingRateImagePaletteNV (GLuint viewport, GLuint entry, GLenum *rate); +GLAPI void APIENTRY glGetShadingRateSampleLocationivNV (GLenum rate, GLuint samples, GLuint index, GLint *location); +GLAPI void APIENTRY glShadingRateImageBarrierNV (GLboolean synchronize); +GLAPI void APIENTRY glShadingRateImagePaletteNV (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); +GLAPI void APIENTRY glShadingRateSampleOrderNV (GLenum order); +GLAPI void APIENTRY glShadingRateSampleOrderCustomNV (GLenum rate, GLuint samples, const GLint *locations); +#endif +#endif /* GL_NV_shading_rate_image */ + +#ifndef GL_NV_stereo_view_rendering +#define GL_NV_stereo_view_rendering 1 +#endif /* GL_NV_stereo_view_rendering */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_rectangle_compressed +#define GL_NV_texture_rectangle_compressed 1 +#endif /* GL_NV_texture_rectangle_compressed */ + +#ifndef GL_NV_uniform_buffer_std430_layout +#define GL_NV_uniform_buffer_std430_layout 1 +#endif /* GL_NV_uniform_buffer_std430_layout */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_NV_viewport_swizzle +#define GL_NV_viewport_swizzle 1 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 +#define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 +#define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 +#define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A +#define GL_VIEWPORT_SWIZZLE_W_NV 0x935B +typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glViewportSwizzleNV (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#endif +#endif /* GL_NV_viewport_swizzle */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferTextureMultiviewOVR (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +GLAPI void APIENTRY glNamedFramebufferTextureMultiviewOVR (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/GL/glext.h b/thirdparty/GL/glext.h new file mode 100644 index 0000000..bd86320 --- /dev/null +++ b/thirdparty/GL/glext.h @@ -0,0 +1,12914 @@ +#ifndef __gl_glext_h_ +#define __gl_glext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT +** +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** https://github.com/KhronosGroup/OpenGL-Registry +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +#define GL_GLEXT_VERSION 20240327 + +#include + +/* Generated C header for: + * API: gl + * Profile: compatibility + * Versions considered: .* + * Versions emitted: 1\.[2-9]|[234]\.[0-9] + * Default extensions included: gl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFogCoordf (GLfloat coord); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); +GLAPI void APIENTRY glFogCoordd (GLdouble coord); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2iv (const GLint *v); +GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); +GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3iv (const GLint *v); +GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +typedef khronos_ssize_t GLsizeiptr; +typedef khronos_intptr_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef khronos_uint16_t GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +typedef khronos_uint64_t GLuint64; +typedef khronos_int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_DISPLAY_LIST 0x82E7 +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_MINMAX 0x802E +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); +typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); +typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNMAPDVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); +GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); +GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); +GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); +GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); +GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); +GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); +GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); +GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); +GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); +GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); +GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); +GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); +GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); +GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); +GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); +GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnMapdv (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfv (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapiv (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfv (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuiv (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusv (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStipple (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTable (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilter (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilter (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_VERSION_4_6 +#define GL_VERSION_4_6 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 +#define GL_SPIR_V_BINARY 0x9552 +#define GL_PARAMETER_BUFFER 0x80EE +#define GL_PARAMETER_BUFFER_BINDING 0x80EF +#define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 +#define GL_VERTICES_SUBMITTED 0x82EE +#define GL_PRIMITIVES_SUBMITTED 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 +#define GL_POLYGON_OFFSET_CLAMP 0x8E1B +#define GL_SPIR_V_EXTENSIONS 0x9553 +#define GL_NUM_SPIR_V_EXTENSIONS 0x9554 +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED +typedef void (APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpecializeShader (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +GLAPI void APIENTRY glMultiDrawArraysIndirectCount (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCount (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glPolygonOffsetClamp (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_VERSION_4_6 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_2_compatibility +#define GL_ARB_ES3_2_compatibility 1 +#define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE +#define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 +#define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 +typedef void (APIENTRYP PFNGLPRIMITIVEBOUNDINGBOXARBPROC) (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveBoundingBoxARB (GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); +#endif +#endif /* GL_ARB_ES3_2_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef khronos_uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_fragment_shader_interlock +#define GL_ARB_fragment_shader_interlock 1 +#endif /* GL_ARB_fragment_shader_interlock */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gl_spirv +#define GL_ARB_gl_spirv 1 +#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 +#define GL_SPIR_V_BINARY_ARB 0x9552 +typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpecializeShaderARB (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue); +#endif +#endif /* GL_ARB_gl_spirv */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_gpu_shader_int64 +#define GL_ARB_gpu_shader_int64 1 +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 +typedef void (APIENTRYP PFNGLUNIFORM1I64ARBPROC) (GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2I64ARBPROC) (GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4I64ARBPROC) (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VARBPROC) (GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64ARBPROC) (GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64ARBPROC) (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VARBPROC) (GLuint program, GLint location, GLint64 *params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLuint64 *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUI64VARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64ARBPROC) (GLuint program, GLint location, GLint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64ARBPROC) (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64ARBPROC) (GLuint program, GLint location, GLuint64 x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64ARBPROC) (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64ARB (GLint location, GLint64 x); +GLAPI void APIENTRY glUniform2i64ARB (GLint location, GLint64 x, GLint64 y); +GLAPI void APIENTRY glUniform3i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z); +GLAPI void APIENTRY glUniform4i64ARB (GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +GLAPI void APIENTRY glUniform1i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform2i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform3i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform4i64vARB (GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glUniform1ui64ARB (GLint location, GLuint64 x); +GLAPI void APIENTRY glUniform2ui64ARB (GLint location, GLuint64 x, GLuint64 y); +GLAPI void APIENTRY glUniform3ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +GLAPI void APIENTRY glUniform4ui64ARB (GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +GLAPI void APIENTRY glUniform1ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform2ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform3ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glUniform4ui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glGetUniformi64vARB (GLuint program, GLint location, GLint64 *params); +GLAPI void APIENTRY glGetUniformui64vARB (GLuint program, GLint location, GLuint64 *params); +GLAPI void APIENTRY glGetnUniformi64vARB (GLuint program, GLint location, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glGetnUniformui64vARB (GLuint program, GLint location, GLsizei bufSize, GLuint64 *params); +GLAPI void APIENTRY glProgramUniform1i64ARB (GLuint program, GLint location, GLint64 x); +GLAPI void APIENTRY glProgramUniform2i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y); +GLAPI void APIENTRY glProgramUniform3i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); +GLAPI void APIENTRY glProgramUniform4i64ARB (GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); +GLAPI void APIENTRY glProgramUniform1i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform2i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform3i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform4i64vARB (GLuint program, GLint location, GLsizei count, const GLint64 *value); +GLAPI void APIENTRY glProgramUniform1ui64ARB (GLuint program, GLint location, GLuint64 x); +GLAPI void APIENTRY glProgramUniform2ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y); +GLAPI void APIENTRY glProgramUniform3ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); +GLAPI void APIENTRY glProgramUniform4ui64ARB (GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); +GLAPI void APIENTRY glProgramUniform1ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform2ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform3ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniform4ui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *value); +#endif +#endif /* GL_ARB_gpu_shader_int64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +typedef khronos_uint16_t GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogram (GLenum target); +GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, const void *indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_VIEW_CLASS_EAC_R11 0x9383 +#define GL_VIEW_CLASS_EAC_RG11 0x9384 +#define GL_VIEW_CLASS_ETC2_RGB 0x9385 +#define GL_VIEW_CLASS_ETC2_RGBA 0x9386 +#define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 +#define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 +#define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 +#define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A +#define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B +#define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C +#define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D +#define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E +#define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F +#define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 +#define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 +#define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 +#define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 +#define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 +#define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); +#endif +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); +GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQueryARB (GLenum target); +GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_parallel_shader_compile +#define GL_ARB_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 +#define GL_COMPLETION_STATUS_ARB 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSARBPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMaxShaderCompilerThreadsARB (GLuint count); +#endif +#endif /* GL_ARB_parallel_shader_compile */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_polygon_offset_clamp +#define GL_ARB_polygon_offset_clamp 1 +#endif /* GL_ARB_polygon_offset_clamp */ + +#ifndef GL_ARB_post_depth_coverage +#define GL_ARB_post_depth_coverage 1 +#endif /* GL_ARB_post_depth_coverage */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_locations +#define GL_ARB_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 +#define GL_SAMPLE_LOCATION_ARB 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLEVALUATEDEPTHVALUESARBPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSampleLocationsfvARB (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvARB (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glEvaluateDepthValuesARB (void); +#endif +#endif /* GL_ARB_sample_locations */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counter_ops +#define GL_ARB_shader_atomic_counter_ops 1 +#endif /* GL_ARB_shader_atomic_counter_ops */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_ballot +#define GL_ARB_shader_ballot 1 +#endif /* GL_ARB_shader_ballot */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_clock +#define GL_ARB_shader_clock 1 +#endif /* GL_ARB_shader_clock */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shader_viewport_layer_array +#define GL_ARB_shader_viewport_layer_array 1 +#endif /* GL_ARB_shader_viewport_layer_array */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_sparse_texture2 +#define GL_ARB_sparse_texture2 1 +#endif /* GL_ARB_sparse_texture2 */ + +#ifndef GL_ARB_sparse_texture_clamp +#define GL_ARB_sparse_texture_clamp 1 +#endif /* GL_ARB_sparse_texture_clamp */ + +#ifndef GL_ARB_spirv_extensions +#define GL_ARB_spirv_extensions 1 +#endif /* GL_ARB_spirv_extensions */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); +#endif +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_filter_anisotropic +#define GL_ARB_texture_filter_anisotropic 1 +#endif /* GL_ARB_texture_filter_anisotropic */ + +#ifndef GL_ARB_texture_filter_minmax +#define GL_ARB_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 +#define GL_WEIGHTED_AVERAGE_ARB 0x9367 +#endif /* GL_ARB_texture_filter_minmax */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); +GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); +GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); +GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); +GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); +GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); +GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); +GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); +GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef khronos_ssize_t GLsizeiptrARB; +typedef khronos_intptr_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); +GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); +#endif +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYDVNVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDDNVPROC) (GLuint index, GLdouble n, GLdouble f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangeArraydvNV (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexeddNV (GLuint index, GLdouble n, GLdouble f); +#endif +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif +#endif /* GL_ARB_window_pos */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendBarrierKHR (void); +#endif +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +typedef void (APIENTRYP PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) (GLuint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMaxShaderCompilerThreadsKHR (GLuint count); +#endif +#endif /* GL_KHR_parallel_shader_compile */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_shader_subgroup +#define GL_KHR_shader_subgroup 1 +#define GL_SUBGROUP_SIZE_KHR 0x9532 +#define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 +#define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 +#define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 +#define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 +#define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 +#define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 +#define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 +#define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 +#define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 +#define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 +#define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 +#endif /* GL_KHR_shader_subgroup */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_KHR_texture_compression_astc_sliced_3d +#define GL_KHR_texture_compression_astc_sliced_3d 1 +#endif /* GL_KHR_texture_compression_astc_sliced_3d */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z, GLbyte w); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); +GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); +GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); +GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); +GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex2bOES (GLbyte x, GLbyte y); +GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y, GLbyte z); +GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z, GLbyte w); +GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +typedef khronos_int32_t GLfixed; +#define GL_FIXED_OES 0x140C +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); +GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); +GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); +GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); +GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidthxOES (GLfixed width); +GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glPointSizexOES (GLfixed size); +GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); +GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); +GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); +GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); +GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); +GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); +GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); +GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); +GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); +GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glIndexxOES (GLfixed component); +GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); +GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); +GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); +GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); +GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glPassThroughxOES (GLfixed token); +GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); +GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); +GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); +GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); +GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); +GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); +GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glVertex2xOES (GLfixed x); +GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); +typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); +GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); +GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); +GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif +#endif /* GL_OES_single_precision */ + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufSize, GLenum *categories, GLenum *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufSize, GLenum *categories, GLenum *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_framebuffer_multisample_advanced +#define GL_AMD_framebuffer_multisample_advanced 1 +#define GL_RENDERBUFFER_STORAGE_SAMPLES_AMD 0x91B2 +#define GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD 0x91B3 +#define GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD 0x91B4 +#define GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD 0x91B5 +#define GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B6 +#define GL_SUPPORTED_MULTISAMPLE_MODES_AMD 0x91B7 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEADVANCEDAMDPROC) (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleAdvancedAMD (GLenum target, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleAdvancedAMD (GLuint renderbuffer, GLsizei samples, GLsizei storageSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_AMD_framebuffer_multisample_advanced */ + +#ifndef GL_AMD_framebuffer_sample_positions +#define GL_AMD_framebuffer_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +#define GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD 0x91AE +#define GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD 0x91AF +#define GL_ALL_PIXELS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC) (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC) (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSamplePositionsfvAMD (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values); +GLAPI void APIENTRY glNamedFramebufferSamplePositionsfvAMD (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values); +GLAPI void APIENTRY glGetFramebufferParameterfvAMD (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +GLAPI void APIENTRY glGetNamedFramebufferParameterfvAMD (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values); +#endif +#endif /* GL_AMD_framebuffer_sample_positions */ + +#ifndef GL_AMD_gcn_shader +#define GL_AMD_gcn_shader 1 +#endif /* GL_AMD_gcn_shader */ + +#ifndef GL_AMD_gpu_shader_half_float +#define GL_AMD_gpu_shader_half_float 1 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD +#endif /* GL_AMD_gpu_shader_half_float */ + +#ifndef GL_AMD_gpu_shader_int16 +#define GL_AMD_gpu_shader_int16 1 +#endif /* GL_AMD_gpu_shader_int16 */ + +#ifndef GL_AMD_gpu_shader_int64 +#define GL_AMD_gpu_shader_int64 1 +typedef khronos_int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_AMD_gpu_shader_int64 */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 +typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); +#endif +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#endif +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); +GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); +GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); +#endif +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 +typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); +#endif +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_ballot +#define GL_AMD_shader_ballot 1 +#endif /* GL_AMD_shader_ballot */ + +#ifndef GL_AMD_shader_explicit_vertex_parameter +#define GL_AMD_shader_explicit_vertex_parameter 1 +#endif /* GL_AMD_shader_explicit_vertex_parameter */ + +#ifndef GL_AMD_shader_gpu_shader_half_float_fetch +#define GL_AMD_shader_gpu_shader_half_float_fetch 1 +#endif /* GL_AMD_shader_gpu_shader_half_float_fetch */ + +#ifndef GL_AMD_shader_image_load_store_lod +#define GL_AMD_shader_image_load_store_lod 1 +#endif /* GL_AMD_shader_image_load_store_lod */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_gather_bias_lod +#define GL_AMD_texture_gather_bias_lod 1 +#endif /* GL_AMD_texture_gather_bias_lod */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_transform_feedback4 +#define GL_AMD_transform_feedback4 1 +#define GL_STREAM_RASTERIZATION_AMD 0x91A0 +#endif /* GL_AMD_transform_feedback4 */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); +GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); +GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); +GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); +GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_EGL_image_storage +#define GL_EXT_EGL_image_storage 1 +typedef void *GLeglImageOES; +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC) (GLenum target, GLeglImageOES image, const GLint* attrib_list); +typedef void (APIENTRYP PFNGLEGLIMAGETARGETTEXTURESTORAGEEXTPROC) (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEGLImageTargetTexStorageEXT (GLenum target, GLeglImageOES image, const GLint* attrib_list); +GLAPI void APIENTRY glEGLImageTargetTextureStorageEXT (GLuint texture, GLeglImageOES image, const GLint* attrib_list); +#endif +#endif /* GL_EXT_EGL_image_storage */ + +#ifndef GL_EXT_EGL_sync +#define GL_EXT_EGL_sync 1 +#endif /* GL_EXT_EGL_sync */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#endif +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); +GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); +GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); +GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); +GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); +GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#endif +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_external_buffer +#define GL_EXT_external_buffer 1 +typedef void *GLeglClientBufferEXT; +typedef void (APIENTRYP PFNGLBUFFERSTORAGEEXTERNALEXTPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTERNALEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorageExternalEXT (GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +GLAPI void APIENTRY glNamedBufferStorageExternalEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags); +#endif +#endif /* GL_EXT_external_buffer */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_blit_layers +#define GL_EXT_framebuffer_blit_layers 1 +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERLAYERSEXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERLAYEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint srcLayer, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstLayer, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferLayersEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glBlitFramebufferLayerEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint srcLayer, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstLayer, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit_layers */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogramEXT (GLenum target); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); +GLAPI void APIENTRY glTextureLightEXT (GLenum pname); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_memory_object +#define GL_EXT_memory_object 1 +#define GL_TEXTURE_TILING_EXT 0x9580 +#define GL_DEDICATED_MEMORY_OBJECT_EXT 0x9581 +#define GL_PROTECTED_MEMORY_OBJECT_EXT 0x959B +#define GL_NUM_TILING_TYPES_EXT 0x9582 +#define GL_TILING_TYPES_EXT 0x9583 +#define GL_OPTIMAL_TILING_EXT 0x9584 +#define GL_LINEAR_TILING_EXT 0x9585 +#define GL_NUM_DEVICE_UUIDS_EXT 0x9596 +#define GL_DEVICE_UUID_EXT 0x9597 +#define GL_DRIVER_UUID_EXT 0x9598 +#define GL_UUID_SIZE_EXT 16 +typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEVEXTPROC) (GLenum pname, GLubyte *data); +typedef void (APIENTRYP PFNGLGETUNSIGNEDBYTEI_VEXTPROC) (GLenum target, GLuint index, GLubyte *data); +typedef void (APIENTRYP PFNGLDELETEMEMORYOBJECTSEXTPROC) (GLsizei n, const GLuint *memoryObjects); +typedef GLboolean (APIENTRYP PFNGLISMEMORYOBJECTEXTPROC) (GLuint memoryObject); +typedef void (APIENTRYP PFNGLCREATEMEMORYOBJECTSEXTPROC) (GLsizei n, GLuint *memoryObjects); +typedef void (APIENTRYP PFNGLMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMEMORYOBJECTPARAMETERIVEXTPROC) (GLuint memoryObject, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM2DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM3DMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLBUFFERSTORAGEMEMEXTPROC) (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM2DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM3DMULTISAMPLEEXTPROC) (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEMEMEXTPROC) (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXSTORAGEMEM1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTURESTORAGEMEM1DEXTPROC) (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUnsignedBytevEXT (GLenum pname, GLubyte *data); +GLAPI void APIENTRY glGetUnsignedBytei_vEXT (GLenum target, GLuint index, GLubyte *data); +GLAPI void APIENTRY glDeleteMemoryObjectsEXT (GLsizei n, const GLuint *memoryObjects); +GLAPI GLboolean APIENTRY glIsMemoryObjectEXT (GLuint memoryObject); +GLAPI void APIENTRY glCreateMemoryObjectsEXT (GLsizei n, GLuint *memoryObjects); +GLAPI void APIENTRY glMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMemoryObjectParameterivEXT (GLuint memoryObject, GLenum pname, GLint *params); +GLAPI void APIENTRY glTexStorageMem2DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTexStorageMem2DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTexStorageMem3DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTexStorageMem3DMultisampleEXT (GLenum target, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glBufferStorageMemEXT (GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureStorageMem2DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureStorageMem2DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureStorageMem3DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureStorageMem3DMultisampleEXT (GLuint texture, GLsizei samples, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glNamedBufferStorageMemEXT (GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTexStorageMem1DEXT (GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureStorageMem1DEXT (GLuint texture, GLsizei levels, GLenum internalFormat, GLsizei width, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_EXT_memory_object */ + +#ifndef GL_EXT_memory_object_fd +#define GL_EXT_memory_object_fd 1 +#define GL_HANDLE_TYPE_OPAQUE_FD_EXT 0x9586 +typedef void (APIENTRYP PFNGLIMPORTMEMORYFDEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImportMemoryFdEXT (GLuint memory, GLuint64 size, GLenum handleType, GLint fd); +#endif +#endif /* GL_EXT_memory_object_fd */ + +#ifndef GL_EXT_memory_object_win32 +#define GL_EXT_memory_object_win32 1 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_EXT 0x9587 +#define GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT 0x9588 +#define GL_DEVICE_LUID_EXT 0x9599 +#define GL_DEVICE_NODE_MASK_EXT 0x959A +#define GL_LUID_SIZE_EXT 8 +#define GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT 0x9589 +#define GL_HANDLE_TYPE_D3D12_RESOURCE_EXT 0x958A +#define GL_HANDLE_TYPE_D3D11_IMAGE_EXT 0x958B +#define GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT 0x958C +typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32HANDLEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, void *handle); +typedef void (APIENTRYP PFNGLIMPORTMEMORYWIN32NAMEEXTPROC) (GLuint memory, GLuint64 size, GLenum handleType, const void *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImportMemoryWin32HandleEXT (GLuint memory, GLuint64 size, GLenum handleType, void *handle); +GLAPI void APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GLenum handleType, const void *name); +#endif +#endif /* GL_EXT_memory_object_win32 */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#endif +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_multiview_tessellation_geometry_shader +#define GL_EXT_multiview_tessellation_geometry_shader 1 +#endif /* GL_EXT_multiview_tessellation_geometry_shader */ + +#ifndef GL_EXT_multiview_texture_multisample +#define GL_EXT_multiview_texture_multisample 1 +#endif /* GL_EXT_multiview_texture_multisample */ + +#ifndef GL_EXT_multiview_timer_query +#define GL_EXT_multiview_timer_query 1 +#endif /* GL_EXT_multiview_timer_query */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRasterSamplesEXT (GLuint samples, GLboolean fixedsamplelocations); +#endif +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_semaphore +#define GL_EXT_semaphore 1 +#define GL_LAYOUT_GENERAL_EXT 0x958D +#define GL_LAYOUT_COLOR_ATTACHMENT_EXT 0x958E +#define GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT 0x958F +#define GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT 0x9590 +#define GL_LAYOUT_SHADER_READ_ONLY_EXT 0x9591 +#define GL_LAYOUT_TRANSFER_SRC_EXT 0x9592 +#define GL_LAYOUT_TRANSFER_DST_EXT 0x9593 +#define GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT 0x9530 +#define GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT 0x9531 +typedef void (APIENTRYP PFNGLGENSEMAPHORESEXTPROC) (GLsizei n, GLuint *semaphores); +typedef void (APIENTRYP PFNGLDELETESEMAPHORESEXTPROC) (GLsizei n, const GLuint *semaphores); +typedef GLboolean (APIENTRYP PFNGLISSEMAPHOREEXTPROC) (GLuint semaphore); +typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, const GLuint64 *params); +typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERUI64VEXTPROC) (GLuint semaphore, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLWAITSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts); +typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREEXTPROC) (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenSemaphoresEXT (GLsizei n, GLuint *semaphores); +GLAPI void APIENTRY glDeleteSemaphoresEXT (GLsizei n, const GLuint *semaphores); +GLAPI GLboolean APIENTRY glIsSemaphoreEXT (GLuint semaphore); +GLAPI void APIENTRY glSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, const GLuint64 *params); +GLAPI void APIENTRY glGetSemaphoreParameterui64vEXT (GLuint semaphore, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glWaitSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *srcLayouts); +GLAPI void APIENTRY glSignalSemaphoreEXT (GLuint semaphore, GLuint numBufferBarriers, const GLuint *buffers, GLuint numTextureBarriers, const GLuint *textures, const GLenum *dstLayouts); +#endif +#endif /* GL_EXT_semaphore */ + +#ifndef GL_EXT_semaphore_fd +#define GL_EXT_semaphore_fd 1 +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREFDEXTPROC) (GLuint semaphore, GLenum handleType, GLint fd); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImportSemaphoreFdEXT (GLuint semaphore, GLenum handleType, GLint fd); +#endif +#endif /* GL_EXT_semaphore_fd */ + +#ifndef GL_EXT_semaphore_win32 +#define GL_EXT_semaphore_win32 1 +#define GL_HANDLE_TYPE_D3D12_FENCE_EXT 0x9594 +#define GL_D3D12_FENCE_VALUE_EXT 0x9595 +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32HANDLEEXTPROC) (GLuint semaphore, GLenum handleType, void *handle); +typedef void (APIENTRYP PFNGLIMPORTSEMAPHOREWIN32NAMEEXTPROC) (GLuint semaphore, GLenum handleType, const void *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImportSemaphoreWin32HandleEXT (GLuint semaphore, GLenum handleType, void *handle); +GLAPI void APIENTRY glImportSemaphoreWin32NameEXT (GLuint semaphore, GLenum handleType, const void *name); +#endif +#endif /* GL_EXT_semaphore_win32 */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_framebuffer_fetch +#define GL_EXT_shader_framebuffer_fetch 1 +#define GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT 0x8A52 +#endif /* GL_EXT_shader_framebuffer_fetch */ + +#ifndef GL_EXT_shader_framebuffer_fetch_non_coherent +#define GL_EXT_shader_framebuffer_fetch_non_coherent 1 +typedef void (APIENTRYP PFNGLFRAMEBUFFERFETCHBARRIEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferFetchBarrierEXT (void); +#endif +#endif /* GL_EXT_shader_framebuffer_fetch_non_coherent */ + +#ifndef GL_EXT_shader_image_load_formatted +#define GL_EXT_shader_image_load_formatted 1 +#endif /* GL_EXT_shader_image_load_formatted */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shader_samples_identical +#define GL_EXT_shader_samples_identical 1 +#endif /* GL_EXT_shader_samples_identical */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_sparse_texture2 +#define GL_EXT_sparse_texture2 1 +#endif /* GL_EXT_sparse_texture2 */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#define GL_TEXTURE_REDUCTION_MODE_EXT 0x9366 +#define GL_WEIGHTED_AVERAGE_EXT 0x9367 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_R8 +#define GL_EXT_texture_sRGB_R8 1 +#define GL_SR8_EXT 0x8FBD +#endif /* GL_EXT_texture_sRGB_R8 */ + +#ifndef GL_EXT_texture_sRGB_RG8 +#define GL_EXT_texture_sRGB_RG8 1 +#define GL_SRG8_EXT 0x8FBE +#endif /* GL_EXT_texture_sRGB_RG8 */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shadow_lod +#define GL_EXT_texture_shadow_lod 1 +#endif /* GL_EXT_texture_shadow_lod */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_storage +#define GL_EXT_texture_storage 1 +#define GL_TEXTURE_IMMUTABLE_FORMAT_EXT 0x912F +#define GL_RGBA32F_EXT 0x8814 +#define GL_RGB32F_EXT 0x8815 +#define GL_ALPHA32F_EXT 0x8816 +#define GL_LUMINANCE32F_EXT 0x8818 +#define GL_LUMINANCE_ALPHA32F_EXT 0x8819 +#define GL_RGBA16F_EXT 0x881A +#define GL_RGB16F_EXT 0x881B +#define GL_ALPHA16F_EXT 0x881C +#define GL_LUMINANCE16F_EXT 0x881E +#define GL_LUMINANCE_ALPHA16F_EXT 0x881F +#define GL_BGRA8_EXT 0x93A1 +#define GL_R8_EXT 0x8229 +#define GL_RG8_EXT 0x822B +#define GL_R32F_EXT 0x822E +#define GL_RG32F_EXT 0x8230 +#define GL_R16F_EXT 0x822D +#define GL_RG16F_EXT 0x822F +typedef void (APIENTRYP PFNGLTEXSTORAGE1DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DEXTPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorage1DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3DEXT (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_EXT_texture_storage */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackEXT (void); +GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint i); +GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); +GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); +GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#endif +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +#endif +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); +GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); +GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); +GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); +GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); +GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); +GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); +GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); +GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); +GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); +GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_win32_keyed_mutex +#define GL_EXT_win32_keyed_mutex 1 +typedef GLboolean (APIENTRYP PFNGLACQUIREKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key, GLuint timeout); +typedef GLboolean (APIENTRYP PFNGLRELEASEKEYEDMUTEXWIN32EXTPROC) (GLuint memory, GLuint64 key); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAcquireKeyedMutexWin32EXT (GLuint memory, GLuint64 key, GLuint timeout); +GLAPI GLboolean APIENTRY glReleaseKeyedMutexWin32EXT (GLuint memory, GLuint64 key); +#endif +#endif /* GL_EXT_win32_keyed_mutex */ + +#ifndef GL_EXT_window_rectangles +#define GL_EXT_window_rectangles 1 +#define GL_INCLUSIVE_EXT 0x8F10 +#define GL_EXCLUSIVE_EXT 0x8F11 +#define GL_WINDOW_RECTANGLE_EXT 0x8F12 +#define GL_WINDOW_RECTANGLE_MODE_EXT 0x8F13 +#define GL_MAX_WINDOW_RECTANGLES_EXT 0x8F14 +#define GL_NUM_WINDOW_RECTANGLES_EXT 0x8F15 +typedef void (APIENTRYP PFNGLWINDOWRECTANGLESEXTPROC) (GLenum mode, GLsizei count, const GLint *box); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowRectanglesEXT (GLenum mode, GLsizei count, const GLint *box); +#endif +#endif /* GL_EXT_window_rectangles */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 +typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); +#endif +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#endif +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#endif +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_blackhole_render +#define GL_INTEL_blackhole_render 1 +#define GL_BLACKHOLE_RENDER_INTEL 0x83FC +#endif /* GL_INTEL_blackhole_render */ + +#ifndef GL_INTEL_conservative_rasterization +#define GL_INTEL_conservative_rasterization 1 +#define GL_CONSERVATIVE_RASTERIZATION_INTEL 0x83FE +#endif /* GL_INTEL_conservative_rasterization */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_framebuffer_CMAA +#define GL_INTEL_framebuffer_CMAA 1 +typedef void (APIENTRYP PFNGLAPPLYFRAMEBUFFERATTACHMENTCMAAINTELPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyFramebufferAttachmentCMAAINTEL (void); +#endif +#endif /* GL_INTEL_framebuffer_CMAA */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); +typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); +GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); +GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#endif +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); +GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); +#endif +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); +GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); +GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); +GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, void *data, GLuint *bytesWritten); +GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); +GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_framebuffer_flip_x +#define GL_MESA_framebuffer_flip_x 1 +#define GL_FRAMEBUFFER_FLIP_X_MESA 0x8BBC +#endif /* GL_MESA_framebuffer_flip_x */ + +#ifndef GL_MESA_framebuffer_flip_y +#define GL_MESA_framebuffer_flip_y 1 +#define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIMESAPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVMESAPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferParameteriMESA (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameterivMESA (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_MESA_framebuffer_flip_y */ + +#ifndef GL_MESA_framebuffer_swap_xy +#define GL_MESA_framebuffer_swap_xy 1 +#define GL_FRAMEBUFFER_SWAP_XY_MESA 0x8BBD +#endif /* GL_MESA_framebuffer_swap_xy */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_program_binary_formats +#define GL_MESA_program_binary_formats 1 +#define GL_PROGRAM_BINARY_FORMAT_MESA 0x875F +#endif /* GL_MESA_program_binary_formats */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_shader_integer_functions +#define GL_MESA_shader_integer_functions 1 +#endif /* GL_MESA_shader_integer_functions */ + +#ifndef GL_MESA_tile_raster_order +#define GL_MESA_tile_raster_order 1 +#define GL_TILE_RASTER_ORDER_FIXED_MESA 0x8BB8 +#define GL_TILE_RASTER_ORDER_INCREASING_X_MESA 0x8BB9 +#define GL_TILE_RASTER_ORDER_INCREASING_Y_MESA 0x8BBA +#endif /* GL_MESA_tile_raster_order */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_blend_equation_advanced_multi_draw_buffers +#define GL_NVX_blend_equation_advanced_multi_draw_buffers 1 +#endif /* GL_NVX_blend_equation_advanced_multi_draw_buffers */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); +GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NVX_gpu_memory_info +#define GL_NVX_gpu_memory_info 1 +#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif /* GL_NVX_gpu_memory_info */ + +#ifndef GL_NVX_gpu_multicast2 +#define GL_NVX_gpu_multicast2 1 +#define GL_UPLOAD_GPU_MASK_NVX 0x954A +typedef void (APIENTRYP PFNGLUPLOADGPUMASKNVXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTICASTVIEWPORTPOSITIONWSCALENVXPROC) (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); +typedef void (APIENTRYP PFNGLMULTICASTSCISSORARRAYVNVXPROC) (GLuint gpu, GLuint first, GLsizei count, const GLint *v); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYBUFFERSUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +typedef GLuint (APIENTRYP PFNGLASYNCCOPYIMAGESUBDATANVXPROC) (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUploadGpuMaskNVX (GLbitfield mask); +GLAPI void APIENTRY glMulticastViewportArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glMulticastViewportPositionWScaleNVX (GLuint gpu, GLuint index, GLfloat xcoeff, GLfloat ycoeff); +GLAPI void APIENTRY glMulticastScissorArrayvNVX (GLuint gpu, GLuint first, GLsizei count, const GLint *v); +GLAPI GLuint APIENTRY glAsyncCopyBufferSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *fenceValueArray, GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +GLAPI GLuint APIENTRY glAsyncCopyImageSubDataNVX (GLsizei waitSemaphoreCount, const GLuint *waitSemaphoreArray, const GLuint64 *waitValueArray, GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth, GLsizei signalSemaphoreCount, const GLuint *signalSemaphoreArray, const GLuint64 *signalValueArray); +#endif +#endif /* GL_NVX_gpu_multicast2 */ + +#ifndef GL_NVX_linked_gpu_multicast +#define GL_NVX_linked_gpu_multicast 1 +#define GL_LGPU_SEPARATE_STORAGE_BIT_NVX 0x0800 +#define GL_MAX_LGPU_GPUS_NVX 0x92BA +typedef void (APIENTRYP PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC) (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLLGPUINTERLOCKNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLGPUNamedBufferSubDataNVX (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glLGPUCopyImageSubDataNVX (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glLGPUInterlockNVX (void); +#endif +#endif /* GL_NVX_linked_gpu_multicast */ + +#ifndef GL_NVX_progress_fence +#define GL_NVX_progress_fence 1 +typedef GLuint (APIENTRYP PFNGLCREATEPROGRESSFENCENVXPROC) (void); +typedef void (APIENTRYP PFNGLSIGNALSEMAPHOREUI64NVXPROC) (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +typedef void (APIENTRYP PFNGLWAITSEMAPHOREUI64NVXPROC) (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +typedef void (APIENTRYP PFNGLCLIENTWAITSEMAPHOREUI64NVXPROC) (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glCreateProgressFenceNVX (void); +GLAPI void APIENTRY glSignalSemaphoreui64NVX (GLuint signalGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +GLAPI void APIENTRY glWaitSemaphoreui64NVX (GLuint waitGpu, GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +GLAPI void APIENTRY glClientWaitSemaphoreui64NVX (GLsizei fenceObjectCount, const GLuint *semaphoreArray, const GLuint64 *fenceValueArray); +#endif +#endif /* GL_NVX_progress_fence */ + +#ifndef GL_NV_alpha_to_coverage_dither_control +#define GL_NV_alpha_to_coverage_dither_control 1 +#define GL_ALPHA_TO_COVERAGE_DITHER_DEFAULT_NV 0x934D +#define GL_ALPHA_TO_COVERAGE_DITHER_ENABLE_NV 0x934E +#define GL_ALPHA_TO_COVERAGE_DITHER_DISABLE_NV 0x934F +#define GL_ALPHA_TO_COVERAGE_DITHER_MODE_NV 0x92BF +typedef void (APIENTRYP PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAlphaToCoverageDitherControlNV (GLenum mode); +#endif +#endif /* GL_NV_alpha_to_coverage_dither_control */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessCountNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessCountNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); +GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_minmax_factor +#define GL_NV_blend_minmax_factor 1 +#endif /* GL_NV_blend_minmax_factor */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_clip_space_w_scaling +#define GL_NV_clip_space_w_scaling 1 +#define GL_VIEWPORT_POSITION_W_SCALE_NV 0x937C +#define GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV 0x937D +#define GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV 0x937E +typedef void (APIENTRYP PFNGLVIEWPORTPOSITIONWSCALENVPROC) (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glViewportPositionWScaleNV (GLuint index, GLfloat xcoeff, GLfloat ycoeff); +#endif +#endif /* GL_NV_clip_space_w_scaling */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 +typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); +typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); +typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); +typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); +typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); +typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); +typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); +typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); +typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCreateStatesNV (GLsizei n, GLuint *states); +GLAPI void APIENTRY glDeleteStatesNV (GLsizei n, const GLuint *states); +GLAPI GLboolean APIENTRY glIsStateNV (GLuint state); +GLAPI void APIENTRY glStateCaptureNV (GLuint state, GLenum mode); +GLAPI GLuint APIENTRY glGetCommandHeaderNV (GLenum tokenID, GLuint size); +GLAPI GLushort APIENTRY glGetStageIndexNV (GLenum shadertype); +GLAPI void APIENTRY glDrawCommandsNV (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsAddressNV (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesNV (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesAddressNV (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCreateCommandListsNV (GLsizei n, GLuint *lists); +GLAPI void APIENTRY glDeleteCommandListsNV (GLsizei n, const GLuint *lists); +GLAPI GLboolean APIENTRY glIsCommandListNV (GLuint list); +GLAPI void APIENTRY glListDrawCommandsStatesClientNV (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCommandListSegmentsNV (GLuint list, GLuint segments); +GLAPI void APIENTRY glCompileCommandListNV (GLuint list); +GLAPI void APIENTRY glCallCommandListNV (GLuint list); +#endif +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_compute_shader_derivatives +#define GL_NV_compute_shader_derivatives 1 +#endif /* GL_NV_compute_shader_derivatives */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 +typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybits); +#endif +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_conservative_raster_dilate +#define GL_NV_conservative_raster_dilate 1 +#define GL_CONSERVATIVE_RASTER_DILATE_NV 0x9379 +#define GL_CONSERVATIVE_RASTER_DILATE_RANGE_NV 0x937A +#define GL_CONSERVATIVE_RASTER_DILATE_GRANULARITY_NV 0x937B +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERFNVPROC) (GLenum pname, GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConservativeRasterParameterfNV (GLenum pname, GLfloat value); +#endif +#endif /* GL_NV_conservative_raster_dilate */ + +#ifndef GL_NV_conservative_raster_pre_snap +#define GL_NV_conservative_raster_pre_snap 1 +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV 0x9550 +#endif /* GL_NV_conservative_raster_pre_snap */ + +#ifndef GL_NV_conservative_raster_pre_snap_triangles +#define GL_NV_conservative_raster_pre_snap_triangles 1 +#define GL_CONSERVATIVE_RASTER_MODE_NV 0x954D +#define GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV 0x954E +#define GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV 0x954F +typedef void (APIENTRYP PFNGLCONSERVATIVERASTERPARAMETERINVPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConservativeRasterParameteriNV (GLenum pname, GLint param); +#endif +#endif /* GL_NV_conservative_raster_pre_snap_triangles */ + +#ifndef GL_NV_conservative_raster_underestimation +#define GL_NV_conservative_raster_underestimation 1 +#endif /* GL_NV_conservative_raster_underestimation */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_draw_vulkan_image +#define GL_NV_draw_vulkan_image 1 +typedef void (APIENTRY *GLVULKANPROCNV)(void); +typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name); +typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore); +typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawVkImageNV (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +GLAPI GLVULKANPROCNV APIENTRY glGetVkProcAddrNV (const GLchar *name); +GLAPI void APIENTRY glWaitVkSemaphoreNV (GLuint64 vkSemaphore); +GLAPI void APIENTRY glSignalVkSemaphoreNV (GLuint64 vkSemaphore); +GLAPI void APIENTRY glSignalVkFenceNV (GLuint64 vkFence); +#endif +#endif /* GL_NV_draw_vulkan_image */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); +GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); +GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GLAPI void APIENTRY glFinishFenceNV (GLuint fence); +GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +#endif /* GL_NV_fence */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE +typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentCoverageColorNV (GLuint color); +#endif +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_fragment_shader_barycentric +#define GL_NV_fragment_shader_barycentric 1 +#endif /* GL_NV_fragment_shader_barycentric */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); +typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCoverageModulationTableNV (GLsizei n, const GLfloat *v); +GLAPI void APIENTRY glGetCoverageModulationTableNV (GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glCoverageModulationNV (GLenum components); +#endif +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_multicast +#define GL_NV_gpu_multicast 1 +#define GL_PER_GPU_STORAGE_BIT_NV 0x0800 +#define GL_MULTICAST_GPUS_NV 0x92BA +#define GL_RENDER_GPU_MASK_NV 0x9558 +#define GL_PER_GPU_STORAGE_NV 0x9548 +#define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549 +typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTICASTBARRIERNVPROC) (void); +typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC) (GLuint signalGpu, GLbitfield waitGpuMask); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderGpuMaskNV (GLbitfield mask); +GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glMulticastFramebufferSampleLocationsfvNV (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glMulticastBarrierNV (void); +GLAPI void APIENTRY glMulticastWaitSyncNV (GLuint signalGpu, GLbitfield waitGpuMask); +GLAPI void APIENTRY glMulticastGetQueryObjectivNV (GLuint gpu, GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glMulticastGetQueryObjectuivNV (GLuint gpu, GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMulticastGetQueryObjecti64vNV (GLuint gpu, GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glMulticastGetQueryObjectui64vNV (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_NV_gpu_multicast */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); +#endif +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 +typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei count, GLint *params); +#endif +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_memory_attachment +#define GL_NV_memory_attachment 1 +#define GL_ATTACHED_MEMORY_OBJECT_NV 0x95A4 +#define GL_ATTACHED_MEMORY_OFFSET_NV 0x95A5 +#define GL_MEMORY_ATTACHABLE_ALIGNMENT_NV 0x95A6 +#define GL_MEMORY_ATTACHABLE_SIZE_NV 0x95A7 +#define GL_MEMORY_ATTACHABLE_NV 0x95A8 +#define GL_DETACHED_MEMORY_INCARNATION_NV 0x95A9 +#define GL_DETACHED_TEXTURES_NV 0x95AA +#define GL_DETACHED_BUFFERS_NV 0x95AB +#define GL_MAX_DETACHED_TEXTURES_NV 0x95AC +#define GL_MAX_DETACHED_BUFFERS_NV 0x95AD +typedef void (APIENTRYP PFNGLGETMEMORYOBJECTDETACHEDRESOURCESUIVNVPROC) (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); +typedef void (APIENTRYP PFNGLRESETMEMORYOBJECTPARAMETERNVPROC) (GLuint memory, GLenum pname); +typedef void (APIENTRYP PFNGLTEXATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLBUFFERATTACHMEMORYNVPROC) (GLenum target, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLTEXTUREATTACHMEMORYNVPROC) (GLuint texture, GLuint memory, GLuint64 offset); +typedef void (APIENTRYP PFNGLNAMEDBUFFERATTACHMEMORYNVPROC) (GLuint buffer, GLuint memory, GLuint64 offset); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMemoryObjectDetachedResourcesuivNV (GLuint memory, GLenum pname, GLint first, GLsizei count, GLuint *params); +GLAPI void APIENTRY glResetMemoryObjectParameterNV (GLuint memory, GLenum pname); +GLAPI void APIENTRY glTexAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glBufferAttachMemoryNV (GLenum target, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glTextureAttachMemoryNV (GLuint texture, GLuint memory, GLuint64 offset); +GLAPI void APIENTRY glNamedBufferAttachMemoryNV (GLuint buffer, GLuint memory, GLuint64 offset); +#endif +#endif /* GL_NV_memory_attachment */ + +#ifndef GL_NV_memory_object_sparse +#define GL_NV_memory_object_sparse 1 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTMEMNVPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTMEMNVPROC) (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTMEMNVPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTMEMNVPROC) (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentMemNV (GLenum target, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +GLAPI void APIENTRY glTexPageCommitmentMemNV (GLenum target, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentMemNV (GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit); +GLAPI void APIENTRY glTexturePageCommitmentMemNV (GLuint texture, GLint layer, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLuint memory, GLuint64 offset, GLboolean commit); +#endif +#endif /* GL_NV_memory_object_sparse */ + +#ifndef GL_NV_mesh_shader +#define GL_NV_mesh_shader 1 +#define GL_MESH_SHADER_NV 0x9559 +#define GL_TASK_SHADER_NV 0x955A +#define GL_MAX_MESH_UNIFORM_BLOCKS_NV 0x8E60 +#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_NV 0x8E61 +#define GL_MAX_MESH_IMAGE_UNIFORMS_NV 0x8E62 +#define GL_MAX_MESH_UNIFORM_COMPONENTS_NV 0x8E63 +#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_NV 0x8E64 +#define GL_MAX_MESH_ATOMIC_COUNTERS_NV 0x8E65 +#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_NV 0x8E66 +#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_NV 0x8E67 +#define GL_MAX_TASK_UNIFORM_BLOCKS_NV 0x8E68 +#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_NV 0x8E69 +#define GL_MAX_TASK_IMAGE_UNIFORMS_NV 0x8E6A +#define GL_MAX_TASK_UNIFORM_COMPONENTS_NV 0x8E6B +#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_NV 0x8E6C +#define GL_MAX_TASK_ATOMIC_COUNTERS_NV 0x8E6D +#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_NV 0x8E6E +#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_NV 0x8E6F +#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_NV 0x95A2 +#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_NV 0x95A3 +#define GL_MAX_MESH_TOTAL_MEMORY_SIZE_NV 0x9536 +#define GL_MAX_TASK_TOTAL_MEMORY_SIZE_NV 0x9537 +#define GL_MAX_MESH_OUTPUT_VERTICES_NV 0x9538 +#define GL_MAX_MESH_OUTPUT_PRIMITIVES_NV 0x9539 +#define GL_MAX_TASK_OUTPUT_COUNT_NV 0x953A +#define GL_MAX_DRAW_MESH_TASKS_COUNT_NV 0x953D +#define GL_MAX_MESH_VIEWS_NV 0x9557 +#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_NV 0x92DF +#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_NV 0x9543 +#define GL_MAX_MESH_WORK_GROUP_SIZE_NV 0x953B +#define GL_MAX_TASK_WORK_GROUP_SIZE_NV 0x953C +#define GL_MESH_WORK_GROUP_SIZE_NV 0x953E +#define GL_TASK_WORK_GROUP_SIZE_NV 0x953F +#define GL_MESH_VERTICES_OUT_NV 0x9579 +#define GL_MESH_PRIMITIVES_OUT_NV 0x957A +#define GL_MESH_OUTPUT_TYPE_NV 0x957B +#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_NV 0x959C +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_NV 0x959D +#define GL_REFERENCED_BY_MESH_SHADER_NV 0x95A0 +#define GL_REFERENCED_BY_TASK_SHADER_NV 0x95A1 +#define GL_MESH_SHADER_BIT_NV 0x00000040 +#define GL_TASK_SHADER_BIT_NV 0x00000080 +#define GL_MESH_SUBROUTINE_NV 0x957C +#define GL_TASK_SUBROUTINE_NV 0x957D +#define GL_MESH_SUBROUTINE_UNIFORM_NV 0x957E +#define GL_TASK_SUBROUTINE_UNIFORM_NV 0x957F +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_NV 0x959E +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_NV 0x959F +typedef void (APIENTRYP PFNGLDRAWMESHTASKSNVPROC) (GLuint first, GLuint count); +typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTNVPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTNVPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshTasksNV (GLuint first, GLuint count); +GLAPI void APIENTRY glDrawMeshTasksIndirectNV (GLintptr indirect); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectNV (GLintptr indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountNV (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_NV_mesh_shader */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#endif +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +GLAPI void APIENTRY glMatrixLoad3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoad3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint *baseAndCount); +GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei count, GLsizei *length, GLfloat *params); +GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); +GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_primitive_shading_rate +#define GL_NV_primitive_shading_rate 1 +#define GL_SHADING_RATE_IMAGE_PER_PRIMITIVE_NV 0x95B1 +#define GL_SHADING_RATE_IMAGE_PALETTE_COUNT_NV 0x95B2 +#endif /* GL_NV_primitive_shading_rate */ + +#ifndef GL_NV_query_resource +#define GL_NV_query_resource 1 +#define GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 0x9540 +#define GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV 0x9542 +#define GL_QUERY_RESOURCE_SYS_RESERVED_NV 0x9544 +#define GL_QUERY_RESOURCE_TEXTURE_NV 0x9545 +#define GL_QUERY_RESOURCE_RENDERBUFFER_NV 0x9546 +#define GL_QUERY_RESOURCE_BUFFEROBJECT_NV 0x9547 +typedef GLint (APIENTRYP PFNGLQUERYRESOURCENVPROC) (GLenum queryType, GLint tagId, GLuint count, GLint *buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glQueryResourceNV (GLenum queryType, GLint tagId, GLuint count, GLint *buffer); +#endif +#endif /* GL_NV_query_resource */ + +#ifndef GL_NV_query_resource_tag +#define GL_NV_query_resource_tag 1 +typedef void (APIENTRYP PFNGLGENQUERYRESOURCETAGNVPROC) (GLsizei n, GLint *tagIds); +typedef void (APIENTRYP PFNGLDELETEQUERYRESOURCETAGNVPROC) (GLsizei n, const GLint *tagIds); +typedef void (APIENTRYP PFNGLQUERYRESOURCETAGNVPROC) (GLint tagId, const GLchar *tagString); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueryResourceTagNV (GLsizei n, GLint *tagIds); +GLAPI void APIENTRY glDeleteQueryResourceTagNV (GLsizei n, const GLint *tagIds); +GLAPI void APIENTRY glQueryResourceTagNV (GLint tagId, const GLchar *tagString); +#endif +#endif /* GL_NV_query_resource_tag */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_representative_fragment_test +#define GL_NV_representative_fragment_test 1 +#define GL_REPRESENTATIVE_FRAGMENT_TEST_NV 0x937F +#endif /* GL_NV_representative_fragment_test */ + +#ifndef GL_NV_robustness_video_memory_purge +#define GL_NV_robustness_video_memory_purge 1 +#define GL_PURGED_CONTEXT_RESET_NV 0x92BB +#endif /* GL_NV_robustness_video_memory_purge */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSampleLocationsfvNV (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvNV (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glResolveDepthValuesNV (void); +#endif +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_scissor_exclusive +#define GL_NV_scissor_exclusive 1 +#define GL_SCISSOR_TEST_EXCLUSIVE_NV 0x9555 +#define GL_SCISSOR_BOX_EXCLUSIVE_NV 0x9556 +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVENVPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSOREXCLUSIVEARRAYVNVPROC) (GLuint first, GLsizei count, const GLint *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glScissorExclusiveNV (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorExclusiveArrayvNV (GLuint first, GLsizei count, const GLint *v); +#endif +#endif /* GL_NV_scissor_exclusive */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_float64 +#define GL_NV_shader_atomic_float64 1 +#endif /* GL_NV_shader_atomic_float64 */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_shader_subgroup_partitioned +#define GL_NV_shader_subgroup_partitioned 1 +#define GL_SUBGROUP_FEATURE_PARTITIONED_BIT_NV 0x00000100 +#endif /* GL_NV_shader_subgroup_partitioned */ + +#ifndef GL_NV_shader_texture_footprint +#define GL_NV_shader_texture_footprint 1 +#endif /* GL_NV_shader_texture_footprint */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_shading_rate_image +#define GL_NV_shading_rate_image 1 +#define GL_SHADING_RATE_IMAGE_NV 0x9563 +#define GL_SHADING_RATE_NO_INVOCATIONS_NV 0x9564 +#define GL_SHADING_RATE_1_INVOCATION_PER_PIXEL_NV 0x9565 +#define GL_SHADING_RATE_1_INVOCATION_PER_1X2_PIXELS_NV 0x9566 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X1_PIXELS_NV 0x9567 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X2_PIXELS_NV 0x9568 +#define GL_SHADING_RATE_1_INVOCATION_PER_2X4_PIXELS_NV 0x9569 +#define GL_SHADING_RATE_1_INVOCATION_PER_4X2_PIXELS_NV 0x956A +#define GL_SHADING_RATE_1_INVOCATION_PER_4X4_PIXELS_NV 0x956B +#define GL_SHADING_RATE_2_INVOCATIONS_PER_PIXEL_NV 0x956C +#define GL_SHADING_RATE_4_INVOCATIONS_PER_PIXEL_NV 0x956D +#define GL_SHADING_RATE_8_INVOCATIONS_PER_PIXEL_NV 0x956E +#define GL_SHADING_RATE_16_INVOCATIONS_PER_PIXEL_NV 0x956F +#define GL_SHADING_RATE_IMAGE_BINDING_NV 0x955B +#define GL_SHADING_RATE_IMAGE_TEXEL_WIDTH_NV 0x955C +#define GL_SHADING_RATE_IMAGE_TEXEL_HEIGHT_NV 0x955D +#define GL_SHADING_RATE_IMAGE_PALETTE_SIZE_NV 0x955E +#define GL_MAX_COARSE_FRAGMENT_SAMPLES_NV 0x955F +#define GL_SHADING_RATE_SAMPLE_ORDER_DEFAULT_NV 0x95AE +#define GL_SHADING_RATE_SAMPLE_ORDER_PIXEL_MAJOR_NV 0x95AF +#define GL_SHADING_RATE_SAMPLE_ORDER_SAMPLE_MAJOR_NV 0x95B0 +typedef void (APIENTRYP PFNGLBINDSHADINGRATEIMAGENVPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLGETSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint entry, GLenum *rate); +typedef void (APIENTRYP PFNGLGETSHADINGRATESAMPLELOCATIONIVNVPROC) (GLenum rate, GLuint samples, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEBARRIERNVPROC) (GLboolean synchronize); +typedef void (APIENTRYP PFNGLSHADINGRATEIMAGEPALETTENVPROC) (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERNVPROC) (GLenum order); +typedef void (APIENTRYP PFNGLSHADINGRATESAMPLEORDERCUSTOMNVPROC) (GLenum rate, GLuint samples, const GLint *locations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindShadingRateImageNV (GLuint texture); +GLAPI void APIENTRY glGetShadingRateImagePaletteNV (GLuint viewport, GLuint entry, GLenum *rate); +GLAPI void APIENTRY glGetShadingRateSampleLocationivNV (GLenum rate, GLuint samples, GLuint index, GLint *location); +GLAPI void APIENTRY glShadingRateImageBarrierNV (GLboolean synchronize); +GLAPI void APIENTRY glShadingRateImagePaletteNV (GLuint viewport, GLuint first, GLsizei count, const GLenum *rates); +GLAPI void APIENTRY glShadingRateSampleOrderNV (GLenum order); +GLAPI void APIENTRY glShadingRateSampleOrderCustomNV (GLenum rate, GLuint samples, const GLint *locations); +#endif +#endif /* GL_NV_shading_rate_image */ + +#ifndef GL_NV_stereo_view_rendering +#define GL_NV_stereo_view_rendering 1 +#endif /* GL_NV_stereo_view_rendering */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_rectangle_compressed +#define GL_NV_texture_rectangle_compressed 1 +#endif /* GL_NV_texture_rectangle_compressed */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_timeline_semaphore +#define GL_NV_timeline_semaphore 1 +#define GL_TIMELINE_SEMAPHORE_VALUE_NV 0x9595 +#define GL_SEMAPHORE_TYPE_NV 0x95B3 +#define GL_SEMAPHORE_TYPE_BINARY_NV 0x95B4 +#define GL_SEMAPHORE_TYPE_TIMELINE_NV 0x95B5 +#define GL_MAX_TIMELINE_SEMAPHORE_VALUE_DIFFERENCE_NV 0x95B6 +typedef void (APIENTRYP PFNGLCREATESEMAPHORESNVPROC) (GLsizei n, GLuint *semaphores); +typedef void (APIENTRYP PFNGLSEMAPHOREPARAMETERIVNVPROC) (GLuint semaphore, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETSEMAPHOREPARAMETERIVNVPROC) (GLuint semaphore, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCreateSemaphoresNV (GLsizei n, GLuint *semaphores); +GLAPI void APIENTRY glSemaphoreParameterivNV (GLuint semaphore, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetSemaphoreParameterivNV (GLuint semaphore, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_timeline_semaphore */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLsizei count, const GLint *attribs, GLenum bufferMode); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); +GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedbackNV (void); +GLAPI void APIENTRY glResumeTransformFeedbackNV (void); +GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_uniform_buffer_std430_layout +#define GL_NV_uniform_buffer_std430_layout 1 +#endif /* GL_NV_uniform_buffer_std430_layout */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); +GLAPI void APIENTRY glVDPAUFiniNV (void); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei count, GLsizei *length, GLint *values); +GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); +GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vdpau_interop2 +#define GL_NV_vdpau_interop2 1 +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACEWITHPICTURESTRUCTURENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceWithPictureStructureNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames, GLboolean isFrameStructure); +#endif +#endif /* GL_NV_vdpau_interop2 */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); +#endif +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); +GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); +GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); +GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif +#endif /* GL_NV_video_capture */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_NV_viewport_swizzle +#define GL_NV_viewport_swizzle 1 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV 0x9350 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV 0x9351 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV 0x9352 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV 0x9353 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV 0x9354 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV 0x9355 +#define GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV 0x9356 +#define GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV 0x9357 +#define GL_VIEWPORT_SWIZZLE_X_NV 0x9358 +#define GL_VIEWPORT_SWIZZLE_Y_NV 0x9359 +#define GL_VIEWPORT_SWIZZLE_Z_NV 0x935A +#define GL_VIEWPORT_SWIZZLE_W_NV 0x935B +typedef void (APIENTRYP PFNGLVIEWPORTSWIZZLENVPROC) (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glViewportSwizzleNV (GLuint index, GLenum swizzlex, GLenum swizzley, GLenum swizzlez, GLenum swizzlew); +#endif +#endif /* GL_NV_viewport_swizzle */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +#define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferTextureMultiviewOVR (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +GLAPI void APIENTRY glNamedFramebufferTextureMultiviewOVR (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); +GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); +#endif +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); +GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); +GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842E +#define GL_UNPACK_RESAMPLE_SGIX 0x842F +#define GL_RESAMPLE_REPLICATE_SGIX 0x8433 +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x8434 +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); +#endif +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/GL/glxext.h b/thirdparty/GL/glxext.h new file mode 100644 index 0000000..a024605 --- /dev/null +++ b/thirdparty/GL/glxext.h @@ -0,0 +1,954 @@ +#ifndef __glx_glxext_h_ +#define __glx_glxext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT +** +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** https://github.com/KhronosGroup/OpenGL-Registry +*/ + +#define GLX_GLXEXT_VERSION 20231018 + +/* Generated C header for: + * API: glx + * Versions considered: .* + * Versions emitted: 1\.[3-9] + * Default extensions included: glx + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GLX_VERSION_1_3 +#define GLX_VERSION_1_3 1 +typedef XID GLXContextID; +typedef struct __GLXFBConfigRec *GLXFBConfig; +typedef XID GLXWindow; +typedef XID GLXPbuffer; +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_PIXMAP_BIT 0x00000002 +#define GLX_PBUFFER_BIT 0x00000004 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_COLOR_INDEX_BIT 0x00000002 +#define GLX_PBUFFER_CLOBBER_MASK 0x08000000 +#define GLX_FRONT_LEFT_BUFFER_BIT 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT 0x00000002 +#define GLX_BACK_LEFT_BUFFER_BIT 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT 0x00000008 +#define GLX_AUX_BUFFERS_BIT 0x00000010 +#define GLX_DEPTH_BUFFER_BIT 0x00000020 +#define GLX_STENCIL_BUFFER_BIT 0x00000040 +#define GLX_ACCUM_BUFFER_BIT 0x00000080 +#define GLX_CONFIG_CAVEAT 0x20 +#define GLX_X_VISUAL_TYPE 0x22 +#define GLX_TRANSPARENT_TYPE 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE 0x24 +#define GLX_TRANSPARENT_RED_VALUE 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE 0x28 +#define GLX_DONT_CARE 0xFFFFFFFF +#define GLX_NONE 0x8000 +#define GLX_SLOW_CONFIG 0x8001 +#define GLX_TRUE_COLOR 0x8002 +#define GLX_DIRECT_COLOR 0x8003 +#define GLX_PSEUDO_COLOR 0x8004 +#define GLX_STATIC_COLOR 0x8005 +#define GLX_GRAY_SCALE 0x8006 +#define GLX_STATIC_GRAY 0x8007 +#define GLX_TRANSPARENT_RGB 0x8008 +#define GLX_TRANSPARENT_INDEX 0x8009 +#define GLX_VISUAL_ID 0x800B +#define GLX_SCREEN 0x800C +#define GLX_NON_CONFORMANT_CONFIG 0x800D +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_X_RENDERABLE 0x8012 +#define GLX_FBCONFIG_ID 0x8013 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_COLOR_INDEX_TYPE 0x8015 +#define GLX_MAX_PBUFFER_WIDTH 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT 0x8017 +#define GLX_MAX_PBUFFER_PIXELS 0x8018 +#define GLX_PRESERVED_CONTENTS 0x801B +#define GLX_LARGEST_PBUFFER 0x801C +#define GLX_WIDTH 0x801D +#define GLX_HEIGHT 0x801E +#define GLX_EVENT_MASK 0x801F +#define GLX_DAMAGED 0x8020 +#define GLX_SAVED 0x8021 +#define GLX_WINDOW 0x8022 +#define GLX_PBUFFER 0x8023 +#define GLX_PBUFFER_HEIGHT 0x8040 +#define GLX_PBUFFER_WIDTH 0x8041 +typedef GLXFBConfig *( *PFNGLXGETFBCONFIGSPROC) (Display *dpy, int screen, int *nelements); +typedef GLXFBConfig *( *PFNGLXCHOOSEFBCONFIGPROC) (Display *dpy, int screen, const int *attrib_list, int *nelements); +typedef int ( *PFNGLXGETFBCONFIGATTRIBPROC) (Display *dpy, GLXFBConfig config, int attribute, int *value); +typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGPROC) (Display *dpy, GLXFBConfig config); +typedef GLXWindow ( *PFNGLXCREATEWINDOWPROC) (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list); +typedef void ( *PFNGLXDESTROYWINDOWPROC) (Display *dpy, GLXWindow win); +typedef GLXPixmap ( *PFNGLXCREATEPIXMAPPROC) (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); +typedef void ( *PFNGLXDESTROYPIXMAPPROC) (Display *dpy, GLXPixmap pixmap); +typedef GLXPbuffer ( *PFNGLXCREATEPBUFFERPROC) (Display *dpy, GLXFBConfig config, const int *attrib_list); +typedef void ( *PFNGLXDESTROYPBUFFERPROC) (Display *dpy, GLXPbuffer pbuf); +typedef void ( *PFNGLXQUERYDRAWABLEPROC) (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); +typedef GLXContext ( *PFNGLXCREATENEWCONTEXTPROC) (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); +typedef Bool ( *PFNGLXMAKECONTEXTCURRENTPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLEPROC) (void); +typedef int ( *PFNGLXQUERYCONTEXTPROC) (Display *dpy, GLXContext ctx, int attribute, int *value); +typedef void ( *PFNGLXSELECTEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long event_mask); +typedef void ( *PFNGLXGETSELECTEDEVENTPROC) (Display *dpy, GLXDrawable draw, unsigned long *event_mask); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXFBConfig *glXGetFBConfigs (Display *dpy, int screen, int *nelements); +GLXFBConfig *glXChooseFBConfig (Display *dpy, int screen, const int *attrib_list, int *nelements); +int glXGetFBConfigAttrib (Display *dpy, GLXFBConfig config, int attribute, int *value); +XVisualInfo *glXGetVisualFromFBConfig (Display *dpy, GLXFBConfig config); +GLXWindow glXCreateWindow (Display *dpy, GLXFBConfig config, Window win, const int *attrib_list); +void glXDestroyWindow (Display *dpy, GLXWindow win); +GLXPixmap glXCreatePixmap (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list); +void glXDestroyPixmap (Display *dpy, GLXPixmap pixmap); +GLXPbuffer glXCreatePbuffer (Display *dpy, GLXFBConfig config, const int *attrib_list); +void glXDestroyPbuffer (Display *dpy, GLXPbuffer pbuf); +void glXQueryDrawable (Display *dpy, GLXDrawable draw, int attribute, unsigned int *value); +GLXContext glXCreateNewContext (Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct); +Bool glXMakeContextCurrent (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +GLXDrawable glXGetCurrentReadDrawable (void); +int glXQueryContext (Display *dpy, GLXContext ctx, int attribute, int *value); +void glXSelectEvent (Display *dpy, GLXDrawable draw, unsigned long event_mask); +void glXGetSelectedEvent (Display *dpy, GLXDrawable draw, unsigned long *event_mask); +#endif +#endif /* GLX_VERSION_1_3 */ + +#ifndef GLX_VERSION_1_4 +#define GLX_VERSION_1_4 1 +typedef void ( *__GLXextFuncPtr)(void); +#define GLX_SAMPLE_BUFFERS 100000 +#define GLX_SAMPLES 100001 +typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSPROC) (const GLubyte *procName); +#ifdef GLX_GLXEXT_PROTOTYPES +__GLXextFuncPtr glXGetProcAddress (const GLubyte *procName); +#endif +#endif /* GLX_VERSION_1_4 */ + +#ifndef GLX_ARB_context_flush_control +#define GLX_ARB_context_flush_control 1 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* GLX_ARB_context_flush_control */ + +#ifndef GLX_ARB_create_context +#define GLX_ARB_create_context 1 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +typedef GLXContext ( *PFNGLXCREATECONTEXTATTRIBSARBPROC) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXContext glXCreateContextAttribsARB (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#endif +#endif /* GLX_ARB_create_context */ + +#ifndef GLX_ARB_create_context_no_error +#define GLX_ARB_create_context_no_error 1 +#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* GLX_ARB_create_context_no_error */ + +#ifndef GLX_ARB_create_context_profile +#define GLX_ARB_create_context_profile 1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#endif /* GLX_ARB_create_context_profile */ + +#ifndef GLX_ARB_create_context_robustness +#define GLX_ARB_create_context_robustness 1 +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* GLX_ARB_create_context_robustness */ + +#ifndef GLX_ARB_fbconfig_float +#define GLX_ARB_fbconfig_float 1 +#define GLX_RGBA_FLOAT_TYPE_ARB 0x20B9 +#define GLX_RGBA_FLOAT_BIT_ARB 0x00000004 +#endif /* GLX_ARB_fbconfig_float */ + +#ifndef GLX_ARB_framebuffer_sRGB +#define GLX_ARB_framebuffer_sRGB 1 +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 +#endif /* GLX_ARB_framebuffer_sRGB */ + +#ifndef GLX_ARB_get_proc_address +#define GLX_ARB_get_proc_address 1 +typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName); +#ifdef GLX_GLXEXT_PROTOTYPES +__GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); +#endif +#endif /* GLX_ARB_get_proc_address */ + +#ifndef GLX_ARB_multisample +#define GLX_ARB_multisample 1 +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif /* GLX_ARB_multisample */ + +#ifndef GLX_ARB_robustness_application_isolation +#define GLX_ARB_robustness_application_isolation 1 +#define GLX_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* GLX_ARB_robustness_application_isolation */ + +#ifndef GLX_ARB_robustness_share_group_isolation +#define GLX_ARB_robustness_share_group_isolation 1 +#endif /* GLX_ARB_robustness_share_group_isolation */ + +#ifndef GLX_ARB_vertex_buffer_object +#define GLX_ARB_vertex_buffer_object 1 +#define GLX_CONTEXT_ALLOW_BUFFER_BYTE_ORDER_MISMATCH_ARB 0x2095 +#endif /* GLX_ARB_vertex_buffer_object */ + +#ifndef GLX_3DFX_multisample +#define GLX_3DFX_multisample 1 +#define GLX_SAMPLE_BUFFERS_3DFX 0x8050 +#define GLX_SAMPLES_3DFX 0x8051 +#endif /* GLX_3DFX_multisample */ + +#ifndef GLX_AMD_gpu_association +#define GLX_AMD_gpu_association 1 +#define GLX_GPU_VENDOR_AMD 0x1F00 +#define GLX_GPU_RENDERER_STRING_AMD 0x1F01 +#define GLX_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define GLX_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define GLX_GPU_RAM_AMD 0x21A3 +#define GLX_GPU_CLOCK_AMD 0x21A4 +#define GLX_GPU_NUM_PIPES_AMD 0x21A5 +#define GLX_GPU_NUM_SIMD_AMD 0x21A6 +#define GLX_GPU_NUM_RB_AMD 0x21A7 +#define GLX_GPU_NUM_SPI_AMD 0x21A8 +typedef unsigned int ( *PFNGLXGETGPUIDSAMDPROC) (unsigned int maxCount, unsigned int *ids); +typedef int ( *PFNGLXGETGPUINFOAMDPROC) (unsigned int id, int property, GLenum dataType, unsigned int size, void *data); +typedef unsigned int ( *PFNGLXGETCONTEXTGPUIDAMDPROC) (GLXContext ctx); +typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC) (unsigned int id, GLXContext share_list); +typedef GLXContext ( *PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (unsigned int id, GLXContext share_context, const int *attribList); +typedef Bool ( *PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC) (GLXContext ctx); +typedef Bool ( *PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (GLXContext ctx); +typedef GLXContext ( *PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); +typedef void ( *PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC) (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GLX_GLXEXT_PROTOTYPES +unsigned int glXGetGPUIDsAMD (unsigned int maxCount, unsigned int *ids); +int glXGetGPUInfoAMD (unsigned int id, int property, GLenum dataType, unsigned int size, void *data); +unsigned int glXGetContextGPUIDAMD (GLXContext ctx); +GLXContext glXCreateAssociatedContextAMD (unsigned int id, GLXContext share_list); +GLXContext glXCreateAssociatedContextAttribsAMD (unsigned int id, GLXContext share_context, const int *attribList); +Bool glXDeleteAssociatedContextAMD (GLXContext ctx); +Bool glXMakeAssociatedContextCurrentAMD (GLXContext ctx); +GLXContext glXGetCurrentAssociatedContextAMD (void); +void glXBlitContextFramebufferAMD (GLXContext dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GLX_AMD_gpu_association */ + +#ifndef GLX_EXT_buffer_age +#define GLX_EXT_buffer_age 1 +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif /* GLX_EXT_buffer_age */ + +#ifndef GLX_EXT_context_priority +#define GLX_EXT_context_priority 1 +#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 +#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 +#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 +#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 +#endif /* GLX_EXT_context_priority */ + +#ifndef GLX_EXT_create_context_es2_profile +#define GLX_EXT_create_context_es2_profile 1 +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* GLX_EXT_create_context_es2_profile */ + +#ifndef GLX_EXT_create_context_es_profile +#define GLX_EXT_create_context_es_profile 1 +#define GLX_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* GLX_EXT_create_context_es_profile */ + +#ifndef GLX_EXT_fbconfig_packed_float +#define GLX_EXT_fbconfig_packed_float 1 +#define GLX_RGBA_UNSIGNED_FLOAT_TYPE_EXT 0x20B1 +#define GLX_RGBA_UNSIGNED_FLOAT_BIT_EXT 0x00000008 +#endif /* GLX_EXT_fbconfig_packed_float */ + +#ifndef GLX_EXT_framebuffer_sRGB +#define GLX_EXT_framebuffer_sRGB 1 +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20B2 +#endif /* GLX_EXT_framebuffer_sRGB */ + +#ifndef GLX_EXT_get_drawable_type +#define GLX_EXT_get_drawable_type 1 +#endif /* GLX_EXT_get_drawable_type */ + +#ifndef GLX_EXT_import_context +#define GLX_EXT_import_context 1 +#define GLX_SHARE_CONTEXT_EXT 0x800A +#define GLX_VISUAL_ID_EXT 0x800B +#define GLX_SCREEN_EXT 0x800C +typedef Display *( *PFNGLXGETCURRENTDISPLAYEXTPROC) (void); +typedef int ( *PFNGLXQUERYCONTEXTINFOEXTPROC) (Display *dpy, GLXContext context, int attribute, int *value); +typedef GLXContextID ( *PFNGLXGETCONTEXTIDEXTPROC) (const GLXContext context); +typedef GLXContext ( *PFNGLXIMPORTCONTEXTEXTPROC) (Display *dpy, GLXContextID contextID); +typedef void ( *PFNGLXFREECONTEXTEXTPROC) (Display *dpy, GLXContext context); +#ifdef GLX_GLXEXT_PROTOTYPES +Display *glXGetCurrentDisplayEXT (void); +int glXQueryContextInfoEXT (Display *dpy, GLXContext context, int attribute, int *value); +GLXContextID glXGetContextIDEXT (const GLXContext context); +GLXContext glXImportContextEXT (Display *dpy, GLXContextID contextID); +void glXFreeContextEXT (Display *dpy, GLXContext context); +#endif +#endif /* GLX_EXT_import_context */ + +#ifndef GLX_EXT_libglvnd +#define GLX_EXT_libglvnd 1 +#define GLX_VENDOR_NAMES_EXT 0x20F6 +#endif /* GLX_EXT_libglvnd */ + +#ifndef GLX_EXT_no_config_context +#define GLX_EXT_no_config_context 1 +#endif /* GLX_EXT_no_config_context */ + +#ifndef GLX_EXT_stereo_tree +#define GLX_EXT_stereo_tree 1 +typedef struct { + int type; + unsigned long serial; + Bool send_event; + Display *display; + int extension; + int evtype; + GLXDrawable window; + Bool stereo_tree; +} GLXStereoNotifyEventEXT; +#define GLX_STEREO_TREE_EXT 0x20F5 +#define GLX_STEREO_NOTIFY_MASK_EXT 0x00000001 +#define GLX_STEREO_NOTIFY_EXT 0x00000000 +#endif /* GLX_EXT_stereo_tree */ + +#ifndef GLX_EXT_swap_control +#define GLX_EXT_swap_control 1 +#define GLX_SWAP_INTERVAL_EXT 0x20F1 +#define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 +typedef void ( *PFNGLXSWAPINTERVALEXTPROC) (Display *dpy, GLXDrawable drawable, int interval); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXSwapIntervalEXT (Display *dpy, GLXDrawable drawable, int interval); +#endif +#endif /* GLX_EXT_swap_control */ + +#ifndef GLX_EXT_swap_control_tear +#define GLX_EXT_swap_control_tear 1 +#define GLX_LATE_SWAPS_TEAR_EXT 0x20F3 +#endif /* GLX_EXT_swap_control_tear */ + +#ifndef GLX_EXT_texture_from_pixmap +#define GLX_EXT_texture_from_pixmap 1 +#define GLX_TEXTURE_1D_BIT_EXT 0x00000001 +#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 +#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004 +#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0 +#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 +#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2 +#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 +#define GLX_Y_INVERTED_EXT 0x20D4 +#define GLX_TEXTURE_FORMAT_EXT 0x20D5 +#define GLX_TEXTURE_TARGET_EXT 0x20D6 +#define GLX_MIPMAP_TEXTURE_EXT 0x20D7 +#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8 +#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9 +#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA +#define GLX_TEXTURE_1D_EXT 0x20DB +#define GLX_TEXTURE_2D_EXT 0x20DC +#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD +#define GLX_FRONT_LEFT_EXT 0x20DE +#define GLX_FRONT_RIGHT_EXT 0x20DF +#define GLX_BACK_LEFT_EXT 0x20E0 +#define GLX_BACK_RIGHT_EXT 0x20E1 +#define GLX_FRONT_EXT 0x20DE +#define GLX_BACK_EXT 0x20E0 +#define GLX_AUX0_EXT 0x20E2 +#define GLX_AUX1_EXT 0x20E3 +#define GLX_AUX2_EXT 0x20E4 +#define GLX_AUX3_EXT 0x20E5 +#define GLX_AUX4_EXT 0x20E6 +#define GLX_AUX5_EXT 0x20E7 +#define GLX_AUX6_EXT 0x20E8 +#define GLX_AUX7_EXT 0x20E9 +#define GLX_AUX8_EXT 0x20EA +#define GLX_AUX9_EXT 0x20EB +typedef void ( *PFNGLXBINDTEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); +typedef void ( *PFNGLXRELEASETEXIMAGEEXTPROC) (Display *dpy, GLXDrawable drawable, int buffer); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXBindTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer, const int *attrib_list); +void glXReleaseTexImageEXT (Display *dpy, GLXDrawable drawable, int buffer); +#endif +#endif /* GLX_EXT_texture_from_pixmap */ + +#ifndef GLX_EXT_visual_info +#define GLX_EXT_visual_info 1 +#define GLX_X_VISUAL_TYPE_EXT 0x22 +#define GLX_TRANSPARENT_TYPE_EXT 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE_EXT 0x24 +#define GLX_TRANSPARENT_RED_VALUE_EXT 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE_EXT 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE_EXT 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE_EXT 0x28 +#define GLX_NONE_EXT 0x8000 +#define GLX_TRUE_COLOR_EXT 0x8002 +#define GLX_DIRECT_COLOR_EXT 0x8003 +#define GLX_PSEUDO_COLOR_EXT 0x8004 +#define GLX_STATIC_COLOR_EXT 0x8005 +#define GLX_GRAY_SCALE_EXT 0x8006 +#define GLX_STATIC_GRAY_EXT 0x8007 +#define GLX_TRANSPARENT_RGB_EXT 0x8008 +#define GLX_TRANSPARENT_INDEX_EXT 0x8009 +#endif /* GLX_EXT_visual_info */ + +#ifndef GLX_EXT_visual_rating +#define GLX_EXT_visual_rating 1 +#define GLX_VISUAL_CAVEAT_EXT 0x20 +#define GLX_SLOW_VISUAL_EXT 0x8001 +#define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D +#endif /* GLX_EXT_visual_rating */ + +#ifndef GLX_INTEL_swap_event +#define GLX_INTEL_swap_event 1 +#define GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK 0x04000000 +#define GLX_EXCHANGE_COMPLETE_INTEL 0x8180 +#define GLX_COPY_COMPLETE_INTEL 0x8181 +#define GLX_FLIP_COMPLETE_INTEL 0x8182 +#endif /* GLX_INTEL_swap_event */ + +#ifndef GLX_MESA_agp_offset +#define GLX_MESA_agp_offset 1 +typedef unsigned int ( *PFNGLXGETAGPOFFSETMESAPROC) (const void *pointer); +#ifdef GLX_GLXEXT_PROTOTYPES +unsigned int glXGetAGPOffsetMESA (const void *pointer); +#endif +#endif /* GLX_MESA_agp_offset */ + +#ifndef GLX_MESA_copy_sub_buffer +#define GLX_MESA_copy_sub_buffer 1 +typedef void ( *PFNGLXCOPYSUBBUFFERMESAPROC) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXCopySubBufferMESA (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +#endif +#endif /* GLX_MESA_copy_sub_buffer */ + +#ifndef GLX_MESA_pixmap_colormap +#define GLX_MESA_pixmap_colormap 1 +typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPMESAPROC) (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXPixmap glXCreateGLXPixmapMESA (Display *dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap); +#endif +#endif /* GLX_MESA_pixmap_colormap */ + +#ifndef GLX_MESA_query_renderer +#define GLX_MESA_query_renderer 1 +#define GLX_RENDERER_VENDOR_ID_MESA 0x8183 +#define GLX_RENDERER_DEVICE_ID_MESA 0x8184 +#define GLX_RENDERER_VERSION_MESA 0x8185 +#define GLX_RENDERER_ACCELERATED_MESA 0x8186 +#define GLX_RENDERER_VIDEO_MEMORY_MESA 0x8187 +#define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA 0x8188 +#define GLX_RENDERER_PREFERRED_PROFILE_MESA 0x8189 +#define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA 0x818A +#define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B +#define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA 0x818C +#define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA 0x818D +typedef Bool ( *PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) (int attribute, unsigned int *value); +typedef const char *( *PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC) (int attribute); +typedef Bool ( *PFNGLXQUERYRENDERERINTEGERMESAPROC) (Display *dpy, int screen, int renderer, int attribute, unsigned int *value); +typedef const char *( *PFNGLXQUERYRENDERERSTRINGMESAPROC) (Display *dpy, int screen, int renderer, int attribute); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXQueryCurrentRendererIntegerMESA (int attribute, unsigned int *value); +const char *glXQueryCurrentRendererStringMESA (int attribute); +Bool glXQueryRendererIntegerMESA (Display *dpy, int screen, int renderer, int attribute, unsigned int *value); +const char *glXQueryRendererStringMESA (Display *dpy, int screen, int renderer, int attribute); +#endif +#endif /* GLX_MESA_query_renderer */ + +#ifndef GLX_MESA_release_buffers +#define GLX_MESA_release_buffers 1 +typedef Bool ( *PFNGLXRELEASEBUFFERSMESAPROC) (Display *dpy, GLXDrawable drawable); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXReleaseBuffersMESA (Display *dpy, GLXDrawable drawable); +#endif +#endif /* GLX_MESA_release_buffers */ + +#ifndef GLX_MESA_set_3dfx_mode +#define GLX_MESA_set_3dfx_mode 1 +#define GLX_3DFX_WINDOW_MODE_MESA 0x1 +#define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2 +typedef GLboolean ( *PFNGLXSET3DFXMODEMESAPROC) (GLint mode); +#ifdef GLX_GLXEXT_PROTOTYPES +GLboolean glXSet3DfxModeMESA (GLint mode); +#endif +#endif /* GLX_MESA_set_3dfx_mode */ + +#ifndef GLX_MESA_swap_control +#define GLX_MESA_swap_control 1 +typedef int ( *PFNGLXGETSWAPINTERVALMESAPROC) (void); +typedef int ( *PFNGLXSWAPINTERVALMESAPROC) (unsigned int interval); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXGetSwapIntervalMESA (void); +int glXSwapIntervalMESA (unsigned int interval); +#endif +#endif /* GLX_MESA_swap_control */ + +#ifndef GLX_NV_copy_buffer +#define GLX_NV_copy_buffer 1 +typedef void ( *PFNGLXCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void ( *PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC) (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +void glXNamedCopyBufferSubDataNV (Display *dpy, GLXContext readCtx, GLXContext writeCtx, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif +#endif /* GLX_NV_copy_buffer */ + +#ifndef GLX_NV_copy_image +#define GLX_NV_copy_image 1 +typedef void ( *PFNGLXCOPYIMAGESUBDATANVPROC) (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXCopyImageSubDataNV (Display *dpy, GLXContext srcCtx, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLXContext dstCtx, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GLX_NV_copy_image */ + +#ifndef GLX_NV_delay_before_swap +#define GLX_NV_delay_before_swap 1 +typedef Bool ( *PFNGLXDELAYBEFORESWAPNVPROC) (Display *dpy, GLXDrawable drawable, GLfloat seconds); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXDelayBeforeSwapNV (Display *dpy, GLXDrawable drawable, GLfloat seconds); +#endif +#endif /* GLX_NV_delay_before_swap */ + +#ifndef GLX_NV_float_buffer +#define GLX_NV_float_buffer 1 +#define GLX_FLOAT_COMPONENTS_NV 0x20B0 +#endif /* GLX_NV_float_buffer */ + +#ifndef GLX_NV_multigpu_context +#define GLX_NV_multigpu_context 1 +#define GLX_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define GLX_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define GLX_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define GLX_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* GLX_NV_multigpu_context */ + +#ifndef GLX_NV_multisample_coverage +#define GLX_NV_multisample_coverage 1 +#define GLX_COVERAGE_SAMPLES_NV 100001 +#define GLX_COLOR_SAMPLES_NV 0x20B3 +#endif /* GLX_NV_multisample_coverage */ + +#ifndef GLX_NV_present_video +#define GLX_NV_present_video 1 +#define GLX_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef unsigned int *( *PFNGLXENUMERATEVIDEODEVICESNVPROC) (Display *dpy, int screen, int *nelements); +typedef int ( *PFNGLXBINDVIDEODEVICENVPROC) (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); +#ifdef GLX_GLXEXT_PROTOTYPES +unsigned int *glXEnumerateVideoDevicesNV (Display *dpy, int screen, int *nelements); +int glXBindVideoDeviceNV (Display *dpy, unsigned int video_slot, unsigned int video_device, const int *attrib_list); +#endif +#endif /* GLX_NV_present_video */ + +#ifndef GLX_NV_robustness_video_memory_purge +#define GLX_NV_robustness_video_memory_purge 1 +#define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 +#endif /* GLX_NV_robustness_video_memory_purge */ + +#ifndef GLX_NV_swap_group +#define GLX_NV_swap_group 1 +typedef Bool ( *PFNGLXJOINSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint group); +typedef Bool ( *PFNGLXBINDSWAPBARRIERNVPROC) (Display *dpy, GLuint group, GLuint barrier); +typedef Bool ( *PFNGLXQUERYSWAPGROUPNVPROC) (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); +typedef Bool ( *PFNGLXQUERYMAXSWAPGROUPSNVPROC) (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); +typedef Bool ( *PFNGLXQUERYFRAMECOUNTNVPROC) (Display *dpy, int screen, GLuint *count); +typedef Bool ( *PFNGLXRESETFRAMECOUNTNVPROC) (Display *dpy, int screen); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXJoinSwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint group); +Bool glXBindSwapBarrierNV (Display *dpy, GLuint group, GLuint barrier); +Bool glXQuerySwapGroupNV (Display *dpy, GLXDrawable drawable, GLuint *group, GLuint *barrier); +Bool glXQueryMaxSwapGroupsNV (Display *dpy, int screen, GLuint *maxGroups, GLuint *maxBarriers); +Bool glXQueryFrameCountNV (Display *dpy, int screen, GLuint *count); +Bool glXResetFrameCountNV (Display *dpy, int screen); +#endif +#endif /* GLX_NV_swap_group */ + +#ifndef GLX_NV_video_capture +#define GLX_NV_video_capture 1 +typedef XID GLXVideoCaptureDeviceNV; +#define GLX_DEVICE_ID_NV 0x20CD +#define GLX_UNIQUE_ID_NV 0x20CE +#define GLX_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef int ( *PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); +typedef GLXVideoCaptureDeviceNV *( *PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) (Display *dpy, int screen, int *nelements); +typedef void ( *PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device); +typedef int ( *PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); +typedef void ( *PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) (Display *dpy, GLXVideoCaptureDeviceNV device); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXBindVideoCaptureDeviceNV (Display *dpy, unsigned int video_capture_slot, GLXVideoCaptureDeviceNV device); +GLXVideoCaptureDeviceNV *glXEnumerateVideoCaptureDevicesNV (Display *dpy, int screen, int *nelements); +void glXLockVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device); +int glXQueryVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device, int attribute, int *value); +void glXReleaseVideoCaptureDeviceNV (Display *dpy, GLXVideoCaptureDeviceNV device); +#endif +#endif /* GLX_NV_video_capture */ + +#ifndef GLX_NV_video_out +#define GLX_NV_video_out 1 +typedef unsigned int GLXVideoDeviceNV; +#define GLX_VIDEO_OUT_COLOR_NV 0x20C3 +#define GLX_VIDEO_OUT_ALPHA_NV 0x20C4 +#define GLX_VIDEO_OUT_DEPTH_NV 0x20C5 +#define GLX_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define GLX_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define GLX_VIDEO_OUT_FRAME_NV 0x20C8 +#define GLX_VIDEO_OUT_FIELD_1_NV 0x20C9 +#define GLX_VIDEO_OUT_FIELD_2_NV 0x20CA +#define GLX_VIDEO_OUT_STACKED_FIELDS_1_2_NV 0x20CB +#define GLX_VIDEO_OUT_STACKED_FIELDS_2_1_NV 0x20CC +typedef int ( *PFNGLXGETVIDEODEVICENVPROC) (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); +typedef int ( *PFNGLXRELEASEVIDEODEVICENVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); +typedef int ( *PFNGLXBINDVIDEOIMAGENVPROC) (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); +typedef int ( *PFNGLXRELEASEVIDEOIMAGENVPROC) (Display *dpy, GLXPbuffer pbuf); +typedef int ( *PFNGLXSENDPBUFFERTOVIDEONVPROC) (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); +typedef int ( *PFNGLXGETVIDEOINFONVPROC) (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXGetVideoDeviceNV (Display *dpy, int screen, int numVideoDevices, GLXVideoDeviceNV *pVideoDevice); +int glXReleaseVideoDeviceNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice); +int glXBindVideoImageNV (Display *dpy, GLXVideoDeviceNV VideoDevice, GLXPbuffer pbuf, int iVideoBuffer); +int glXReleaseVideoImageNV (Display *dpy, GLXPbuffer pbuf); +int glXSendPbufferToVideoNV (Display *dpy, GLXPbuffer pbuf, int iBufferType, unsigned long *pulCounterPbuffer, GLboolean bBlock); +int glXGetVideoInfoNV (Display *dpy, int screen, GLXVideoDeviceNV VideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#endif +#endif /* GLX_NV_video_out */ + +#ifndef GLX_OML_swap_method +#define GLX_OML_swap_method 1 +#define GLX_SWAP_METHOD_OML 0x8060 +#define GLX_SWAP_EXCHANGE_OML 0x8061 +#define GLX_SWAP_COPY_OML 0x8062 +#define GLX_SWAP_UNDEFINED_OML 0x8063 +#endif /* GLX_OML_swap_method */ + +#ifndef GLX_OML_sync_control +#define GLX_OML_sync_control 1 +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GLX_OML_sync_control extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif +typedef Bool ( *PFNGLXGETSYNCVALUESOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); +typedef Bool ( *PFNGLXGETMSCRATEOMLPROC) (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); +typedef int64_t ( *PFNGLXSWAPBUFFERSMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); +typedef Bool ( *PFNGLXWAITFORMSCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); +typedef Bool ( *PFNGLXWAITFORSBCOMLPROC) (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXGetSyncValuesOML (Display *dpy, GLXDrawable drawable, int64_t *ust, int64_t *msc, int64_t *sbc); +Bool glXGetMscRateOML (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator); +int64_t glXSwapBuffersMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder); +Bool glXWaitForMscOML (Display *dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t *ust, int64_t *msc, int64_t *sbc); +Bool glXWaitForSbcOML (Display *dpy, GLXDrawable drawable, int64_t target_sbc, int64_t *ust, int64_t *msc, int64_t *sbc); +#endif +#endif /* GLX_OML_sync_control */ + +#ifndef GLX_SGIS_blended_overlay +#define GLX_SGIS_blended_overlay 1 +#define GLX_BLENDED_RGBA_SGIS 0x8025 +#endif /* GLX_SGIS_blended_overlay */ + +#ifndef GLX_SGIS_multisample +#define GLX_SGIS_multisample 1 +#define GLX_SAMPLE_BUFFERS_SGIS 100000 +#define GLX_SAMPLES_SGIS 100001 +#endif /* GLX_SGIS_multisample */ + +#ifndef GLX_SGIS_shared_multisample +#define GLX_SGIS_shared_multisample 1 +#define GLX_MULTISAMPLE_SUB_RECT_WIDTH_SGIS 0x8026 +#define GLX_MULTISAMPLE_SUB_RECT_HEIGHT_SGIS 0x8027 +#endif /* GLX_SGIS_shared_multisample */ + +#ifndef GLX_SGIX_dmbuffer +#define GLX_SGIX_dmbuffer 1 +typedef XID GLXPbufferSGIX; +#ifdef _DM_BUFFER_H_ +#define GLX_DIGITAL_MEDIA_PBUFFER_SGIX 0x8024 +typedef Bool ( *PFNGLXASSOCIATEDMPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXAssociateDMPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuffer, DMparams *params, DMbuffer dmbuffer); +#endif +#endif /* _DM_BUFFER_H_ */ +#endif /* GLX_SGIX_dmbuffer */ + +#ifndef GLX_SGIX_fbconfig +#define GLX_SGIX_fbconfig 1 +typedef struct __GLXFBConfigRec *GLXFBConfigSGIX; +#define GLX_WINDOW_BIT_SGIX 0x00000001 +#define GLX_PIXMAP_BIT_SGIX 0x00000002 +#define GLX_RGBA_BIT_SGIX 0x00000001 +#define GLX_COLOR_INDEX_BIT_SGIX 0x00000002 +#define GLX_DRAWABLE_TYPE_SGIX 0x8010 +#define GLX_RENDER_TYPE_SGIX 0x8011 +#define GLX_X_RENDERABLE_SGIX 0x8012 +#define GLX_FBCONFIG_ID_SGIX 0x8013 +#define GLX_RGBA_TYPE_SGIX 0x8014 +#define GLX_COLOR_INDEX_TYPE_SGIX 0x8015 +typedef int ( *PFNGLXGETFBCONFIGATTRIBSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); +typedef GLXFBConfigSGIX *( *PFNGLXCHOOSEFBCONFIGSGIXPROC) (Display *dpy, int screen, int *attrib_list, int *nelements); +typedef GLXPixmap ( *PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); +typedef GLXContext ( *PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); +typedef XVisualInfo *( *PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) (Display *dpy, GLXFBConfigSGIX config); +typedef GLXFBConfigSGIX ( *PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) (Display *dpy, XVisualInfo *vis); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXGetFBConfigAttribSGIX (Display *dpy, GLXFBConfigSGIX config, int attribute, int *value); +GLXFBConfigSGIX *glXChooseFBConfigSGIX (Display *dpy, int screen, int *attrib_list, int *nelements); +GLXPixmap glXCreateGLXPixmapWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, Pixmap pixmap); +GLXContext glXCreateContextWithConfigSGIX (Display *dpy, GLXFBConfigSGIX config, int render_type, GLXContext share_list, Bool direct); +XVisualInfo *glXGetVisualFromFBConfigSGIX (Display *dpy, GLXFBConfigSGIX config); +GLXFBConfigSGIX glXGetFBConfigFromVisualSGIX (Display *dpy, XVisualInfo *vis); +#endif +#endif /* GLX_SGIX_fbconfig */ + +#ifndef GLX_SGIX_hyperpipe +#define GLX_SGIX_hyperpipe 1 +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int networkId; +} GLXHyperpipeNetworkSGIX; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int channel; + unsigned int participationType; + int timeSlice; +} GLXHyperpipeConfigSGIX; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int srcXOrigin, srcYOrigin, srcWidth, srcHeight; + int destXOrigin, destYOrigin, destWidth, destHeight; +} GLXPipeRect; +typedef struct { + char pipeName[80]; /* Should be [GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX] */ + int XOrigin, YOrigin, maxHeight, maxWidth; +} GLXPipeRectLimits; +#define GLX_HYPERPIPE_PIPE_NAME_LENGTH_SGIX 80 +#define GLX_BAD_HYPERPIPE_CONFIG_SGIX 91 +#define GLX_BAD_HYPERPIPE_SGIX 92 +#define GLX_HYPERPIPE_DISPLAY_PIPE_SGIX 0x00000001 +#define GLX_HYPERPIPE_RENDER_PIPE_SGIX 0x00000002 +#define GLX_PIPE_RECT_SGIX 0x00000001 +#define GLX_PIPE_RECT_LIMITS_SGIX 0x00000002 +#define GLX_HYPERPIPE_STEREO_SGIX 0x00000003 +#define GLX_HYPERPIPE_PIXEL_AVERAGE_SGIX 0x00000004 +#define GLX_HYPERPIPE_ID_SGIX 0x8030 +typedef GLXHyperpipeNetworkSGIX *( *PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) (Display *dpy, int *npipes); +typedef int ( *PFNGLXHYPERPIPECONFIGSGIXPROC) (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); +typedef GLXHyperpipeConfigSGIX *( *PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId, int *npipes); +typedef int ( *PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) (Display *dpy, int hpId); +typedef int ( *PFNGLXBINDHYPERPIPESGIXPROC) (Display *dpy, int hpId); +typedef int ( *PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); +typedef int ( *PFNGLXHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *attribList); +typedef int ( *PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXHyperpipeNetworkSGIX *glXQueryHyperpipeNetworkSGIX (Display *dpy, int *npipes); +int glXHyperpipeConfigSGIX (Display *dpy, int networkId, int npipes, GLXHyperpipeConfigSGIX *cfg, int *hpId); +GLXHyperpipeConfigSGIX *glXQueryHyperpipeConfigSGIX (Display *dpy, int hpId, int *npipes); +int glXDestroyHyperpipeConfigSGIX (Display *dpy, int hpId); +int glXBindHyperpipeSGIX (Display *dpy, int hpId); +int glXQueryHyperpipeBestAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList, void *returnAttribList); +int glXHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *attribList); +int glXQueryHyperpipeAttribSGIX (Display *dpy, int timeSlice, int attrib, int size, void *returnAttribList); +#endif +#endif /* GLX_SGIX_hyperpipe */ + +#ifndef GLX_SGIX_pbuffer +#define GLX_SGIX_pbuffer 1 +#define GLX_PBUFFER_BIT_SGIX 0x00000004 +#define GLX_BUFFER_CLOBBER_MASK_SGIX 0x08000000 +#define GLX_FRONT_LEFT_BUFFER_BIT_SGIX 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT_SGIX 0x00000002 +#define GLX_BACK_LEFT_BUFFER_BIT_SGIX 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT_SGIX 0x00000008 +#define GLX_AUX_BUFFERS_BIT_SGIX 0x00000010 +#define GLX_DEPTH_BUFFER_BIT_SGIX 0x00000020 +#define GLX_STENCIL_BUFFER_BIT_SGIX 0x00000040 +#define GLX_ACCUM_BUFFER_BIT_SGIX 0x00000080 +#define GLX_SAMPLE_BUFFERS_BIT_SGIX 0x00000100 +#define GLX_MAX_PBUFFER_WIDTH_SGIX 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT_SGIX 0x8017 +#define GLX_MAX_PBUFFER_PIXELS_SGIX 0x8018 +#define GLX_OPTIMAL_PBUFFER_WIDTH_SGIX 0x8019 +#define GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX 0x801A +#define GLX_PRESERVED_CONTENTS_SGIX 0x801B +#define GLX_LARGEST_PBUFFER_SGIX 0x801C +#define GLX_WIDTH_SGIX 0x801D +#define GLX_HEIGHT_SGIX 0x801E +#define GLX_EVENT_MASK_SGIX 0x801F +#define GLX_DAMAGED_SGIX 0x8020 +#define GLX_SAVED_SGIX 0x8021 +#define GLX_WINDOW_SGIX 0x8022 +#define GLX_PBUFFER_SGIX 0x8023 +typedef GLXPbufferSGIX ( *PFNGLXCREATEGLXPBUFFERSGIXPROC) (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); +typedef void ( *PFNGLXDESTROYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf); +typedef void ( *PFNGLXQUERYGLXPBUFFERSGIXPROC) (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); +typedef void ( *PFNGLXSELECTEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long mask); +typedef void ( *PFNGLXGETSELECTEDEVENTSGIXPROC) (Display *dpy, GLXDrawable drawable, unsigned long *mask); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXPbufferSGIX glXCreateGLXPbufferSGIX (Display *dpy, GLXFBConfigSGIX config, unsigned int width, unsigned int height, int *attrib_list); +void glXDestroyGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf); +void glXQueryGLXPbufferSGIX (Display *dpy, GLXPbufferSGIX pbuf, int attribute, unsigned int *value); +void glXSelectEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long mask); +void glXGetSelectedEventSGIX (Display *dpy, GLXDrawable drawable, unsigned long *mask); +#endif +#endif /* GLX_SGIX_pbuffer */ + +#ifndef GLX_SGIX_swap_barrier +#define GLX_SGIX_swap_barrier 1 +typedef void ( *PFNGLXBINDSWAPBARRIERSGIXPROC) (Display *dpy, GLXDrawable drawable, int barrier); +typedef Bool ( *PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) (Display *dpy, int screen, int *max); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXBindSwapBarrierSGIX (Display *dpy, GLXDrawable drawable, int barrier); +Bool glXQueryMaxSwapBarriersSGIX (Display *dpy, int screen, int *max); +#endif +#endif /* GLX_SGIX_swap_barrier */ + +#ifndef GLX_SGIX_swap_group +#define GLX_SGIX_swap_group 1 +typedef void ( *PFNGLXJOINSWAPGROUPSGIXPROC) (Display *dpy, GLXDrawable drawable, GLXDrawable member); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXJoinSwapGroupSGIX (Display *dpy, GLXDrawable drawable, GLXDrawable member); +#endif +#endif /* GLX_SGIX_swap_group */ + +#ifndef GLX_SGIX_video_resize +#define GLX_SGIX_video_resize 1 +#define GLX_SYNC_FRAME_SGIX 0x00000000 +#define GLX_SYNC_SWAP_SGIX 0x00000001 +typedef int ( *PFNGLXBINDCHANNELTOWINDOWSGIXPROC) (Display *display, int screen, int channel, Window window); +typedef int ( *PFNGLXCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int x, int y, int w, int h); +typedef int ( *PFNGLXQUERYCHANNELRECTSGIXPROC) (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); +typedef int ( *PFNGLXQUERYCHANNELDELTASSGIXPROC) (Display *display, int screen, int channel, int *x, int *y, int *w, int *h); +typedef int ( *PFNGLXCHANNELRECTSYNCSGIXPROC) (Display *display, int screen, int channel, GLenum synctype); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXBindChannelToWindowSGIX (Display *display, int screen, int channel, Window window); +int glXChannelRectSGIX (Display *display, int screen, int channel, int x, int y, int w, int h); +int glXQueryChannelRectSGIX (Display *display, int screen, int channel, int *dx, int *dy, int *dw, int *dh); +int glXQueryChannelDeltasSGIX (Display *display, int screen, int channel, int *x, int *y, int *w, int *h); +int glXChannelRectSyncSGIX (Display *display, int screen, int channel, GLenum synctype); +#endif +#endif /* GLX_SGIX_video_resize */ + +#ifndef GLX_SGIX_video_source +#define GLX_SGIX_video_source 1 +typedef XID GLXVideoSourceSGIX; +#ifdef _VL_H +typedef GLXVideoSourceSGIX ( *PFNGLXCREATEGLXVIDEOSOURCESGIXPROC) (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); +typedef void ( *PFNGLXDESTROYGLXVIDEOSOURCESGIXPROC) (Display *dpy, GLXVideoSourceSGIX glxvideosource); +#ifdef GLX_GLXEXT_PROTOTYPES +GLXVideoSourceSGIX glXCreateGLXVideoSourceSGIX (Display *display, int screen, VLServer server, VLPath path, int nodeClass, VLNode drainNode); +void glXDestroyGLXVideoSourceSGIX (Display *dpy, GLXVideoSourceSGIX glxvideosource); +#endif +#endif /* _VL_H */ +#endif /* GLX_SGIX_video_source */ + +#ifndef GLX_SGIX_visual_select_group +#define GLX_SGIX_visual_select_group 1 +#define GLX_VISUAL_SELECT_GROUP_SGIX 0x8028 +#endif /* GLX_SGIX_visual_select_group */ + +#ifndef GLX_SGI_cushion +#define GLX_SGI_cushion 1 +typedef void ( *PFNGLXCUSHIONSGIPROC) (Display *dpy, Window window, float cushion); +#ifdef GLX_GLXEXT_PROTOTYPES +void glXCushionSGI (Display *dpy, Window window, float cushion); +#endif +#endif /* GLX_SGI_cushion */ + +#ifndef GLX_SGI_make_current_read +#define GLX_SGI_make_current_read 1 +typedef Bool ( *PFNGLXMAKECURRENTREADSGIPROC) (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +typedef GLXDrawable ( *PFNGLXGETCURRENTREADDRAWABLESGIPROC) (void); +#ifdef GLX_GLXEXT_PROTOTYPES +Bool glXMakeCurrentReadSGI (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx); +GLXDrawable glXGetCurrentReadDrawableSGI (void); +#endif +#endif /* GLX_SGI_make_current_read */ + +#ifndef GLX_SGI_swap_control +#define GLX_SGI_swap_control 1 +typedef int ( *PFNGLXSWAPINTERVALSGIPROC) (int interval); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXSwapIntervalSGI (int interval); +#endif +#endif /* GLX_SGI_swap_control */ + +#ifndef GLX_SGI_video_sync +#define GLX_SGI_video_sync 1 +typedef int ( *PFNGLXGETVIDEOSYNCSGIPROC) (unsigned int *count); +typedef int ( *PFNGLXWAITVIDEOSYNCSGIPROC) (int divisor, int remainder, unsigned int *count); +#ifdef GLX_GLXEXT_PROTOTYPES +int glXGetVideoSyncSGI (unsigned int *count); +int glXWaitVideoSyncSGI (int divisor, int remainder, unsigned int *count); +#endif +#endif /* GLX_SGI_video_sync */ + +#ifndef GLX_SUN_get_transparent_index +#define GLX_SUN_get_transparent_index 1 +typedef Status ( *PFNGLXGETTRANSPARENTINDEXSUNPROC) (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex); +#ifdef GLX_GLXEXT_PROTOTYPES +Status glXGetTransparentIndexSUN (Display *dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex); +#endif +#endif /* GLX_SUN_get_transparent_index */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/GL/wglext.h b/thirdparty/GL/wglext.h new file mode 100644 index 0000000..453c195 --- /dev/null +++ b/thirdparty/GL/wglext.h @@ -0,0 +1,845 @@ +#ifndef __wgl_wglext_h_ +#define __wgl_wglext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright 2013-2020 The Khronos Group Inc. +** SPDX-License-Identifier: MIT +** +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** https://github.com/KhronosGroup/OpenGL-Registry +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#define WGL_WGLEXT_VERSION 20231018 + +/* Generated C header for: + * API: wgl + * Versions considered: .* + * Versions emitted: _nomatch_^ + * Default extensions included: wgl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef WGL_ARB_buffer_region +#define WGL_ARB_buffer_region 1 +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 +typedef HANDLE (WINAPI * PFNWGLCREATEBUFFERREGIONARBPROC) (HDC hDC, int iLayerPlane, UINT uType); +typedef VOID (WINAPI * PFNWGLDELETEBUFFERREGIONARBPROC) (HANDLE hRegion); +typedef BOOL (WINAPI * PFNWGLSAVEBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height); +typedef BOOL (WINAPI * PFNWGLRESTOREBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#ifdef WGL_WGLEXT_PROTOTYPES +HANDLE WINAPI wglCreateBufferRegionARB (HDC hDC, int iLayerPlane, UINT uType); +VOID WINAPI wglDeleteBufferRegionARB (HANDLE hRegion); +BOOL WINAPI wglSaveBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height); +BOOL WINAPI wglRestoreBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#endif +#endif /* WGL_ARB_buffer_region */ + +#ifndef WGL_ARB_context_flush_control +#define WGL_ARB_context_flush_control 1 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* WGL_ARB_context_flush_control */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList); +#ifdef WGL_WGLEXT_PROTOTYPES +HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList); +#endif +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_no_error +#define WGL_ARB_create_context_no_error 1 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 +#endif /* WGL_ARB_create_context_no_error */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_ARB_create_context_robustness +#define WGL_ARB_create_context_robustness 1 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* WGL_ARB_create_context_robustness */ + +#ifndef WGL_ARB_extensions_string +#define WGL_ARB_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringARB (HDC hdc); +#endif +#endif /* WGL_ARB_extensions_string */ + +#ifndef WGL_ARB_framebuffer_sRGB +#define WGL_ARB_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#endif /* WGL_ARB_framebuffer_sRGB */ + +#ifndef WGL_ARB_make_current_read +#define WGL_ARB_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTARBPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCARBPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentARB (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCARB (void); +#endif +#endif /* WGL_ARB_make_current_read */ + +#ifndef WGL_ARB_multisample +#define WGL_ARB_multisample 1 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif /* WGL_ARB_multisample */ + +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFERARB WINAPI wglCreatePbufferARB (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCARB (HPBUFFERARB hPbuffer); +int WINAPI wglReleasePbufferDCARB (HPBUFFERARB hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferARB (HPBUFFERARB hPbuffer); +BOOL WINAPI wglQueryPbufferARB (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_ARB_pbuffer */ + +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatARB (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_pixel_format_float +#define WGL_ARB_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif /* WGL_ARB_pixel_format_float */ + +#ifndef WGL_ARB_render_texture +#define WGL_ARB_render_texture 1 +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 +typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int *piAttribList); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglReleaseTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglSetPbufferAttribARB (HPBUFFERARB hPbuffer, const int *piAttribList); +#endif +#endif /* WGL_ARB_render_texture */ + +#ifndef WGL_ARB_robustness_application_isolation +#define WGL_ARB_robustness_application_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_application_isolation */ + +#ifndef WGL_ARB_robustness_share_group_isolation +#define WGL_ARB_robustness_share_group_isolation 1 +#endif /* WGL_ARB_robustness_share_group_isolation */ + +#ifndef WGL_3DFX_multisample +#define WGL_3DFX_multisample 1 +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif /* WGL_3DFX_multisample */ + +#ifndef WGL_3DL_stereo_control +#define WGL_3DL_stereo_control 1 +#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055 +#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056 +#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057 +#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058 +typedef BOOL (WINAPI * PFNWGLSETSTEREOEMITTERSTATE3DLPROC) (HDC hDC, UINT uState); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSetStereoEmitterState3DL (HDC hDC, UINT uState); +#endif +#endif /* WGL_3DL_stereo_control */ + +#ifndef WGL_AMD_gpu_association +#define WGL_AMD_gpu_association 1 +#define WGL_GPU_VENDOR_AMD 0x1F00 +#define WGL_GPU_RENDERER_STRING_AMD 0x1F01 +#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define WGL_GPU_RAM_AMD 0x21A3 +#define WGL_GPU_CLOCK_AMD 0x21A4 +#define WGL_GPU_NUM_PIPES_AMD 0x21A5 +#define WGL_GPU_NUM_SIMD_AMD 0x21A6 +#define WGL_GPU_NUM_RB_AMD 0x21A7 +#define WGL_GPU_NUM_SPI_AMD 0x21A8 +typedef UINT (WINAPI * PFNWGLGETGPUIDSAMDPROC) (UINT maxCount, UINT *ids); +typedef INT (WINAPI * PFNWGLGETGPUINFOAMDPROC) (UINT id, INT property, GLenum dataType, UINT size, void *data); +typedef UINT (WINAPI * PFNWGLGETCONTEXTGPUIDAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) (UINT id); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (UINT id, HGLRC hShareContext, const int *attribList); +typedef BOOL (WINAPI * PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) (HGLRC hglrc); +typedef BOOL (WINAPI * PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); +typedef VOID (WINAPI * PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef WGL_WGLEXT_PROTOTYPES +UINT WINAPI wglGetGPUIDsAMD (UINT maxCount, UINT *ids); +INT WINAPI wglGetGPUInfoAMD (UINT id, INT property, GLenum dataType, UINT size, void *data); +UINT WINAPI wglGetContextGPUIDAMD (HGLRC hglrc); +HGLRC WINAPI wglCreateAssociatedContextAMD (UINT id); +HGLRC WINAPI wglCreateAssociatedContextAttribsAMD (UINT id, HGLRC hShareContext, const int *attribList); +BOOL WINAPI wglDeleteAssociatedContextAMD (HGLRC hglrc); +BOOL WINAPI wglMakeAssociatedContextCurrentAMD (HGLRC hglrc); +HGLRC WINAPI wglGetCurrentAssociatedContextAMD (void); +VOID WINAPI wglBlitContextFramebufferAMD (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* WGL_AMD_gpu_association */ + +#ifndef WGL_ATI_pixel_format_float +#define WGL_ATI_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#endif /* WGL_ATI_pixel_format_float */ + +#ifndef WGL_ATI_render_texture_rectangle +#define WGL_ATI_render_texture_rectangle 1 +#define WGL_TEXTURE_RECTANGLE_ATI 0x21A5 +#endif /* WGL_ATI_render_texture_rectangle */ + +#ifndef WGL_EXT_colorspace +#define WGL_EXT_colorspace 1 +#define WGL_COLORSPACE_EXT 0x309D +#define WGL_COLORSPACE_SRGB_EXT 0x3089 +#define WGL_COLORSPACE_LINEAR_EXT 0x308A +#endif /* WGL_EXT_colorspace */ + +#ifndef WGL_EXT_create_context_es2_profile +#define WGL_EXT_create_context_es2_profile 1 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es2_profile */ + +#ifndef WGL_EXT_create_context_es_profile +#define WGL_EXT_create_context_es_profile 1 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es_profile */ + +#ifndef WGL_EXT_depth_float +#define WGL_EXT_depth_float 1 +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif /* WGL_EXT_depth_float */ + +#ifndef WGL_EXT_display_color_table +#define WGL_EXT_display_color_table 1 +typedef GLboolean (WINAPI * PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef GLboolean (WINAPI * PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) (const GLushort *table, GLuint length); +typedef GLboolean (WINAPI * PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef VOID (WINAPI * PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) (GLushort id); +#ifdef WGL_WGLEXT_PROTOTYPES +GLboolean WINAPI wglCreateDisplayColorTableEXT (GLushort id); +GLboolean WINAPI wglLoadDisplayColorTableEXT (const GLushort *table, GLuint length); +GLboolean WINAPI wglBindDisplayColorTableEXT (GLushort id); +VOID WINAPI wglDestroyDisplayColorTableEXT (GLushort id); +#endif +#endif /* WGL_EXT_display_color_table */ + +#ifndef WGL_EXT_extensions_string +#define WGL_EXT_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringEXT (void); +#endif +#endif /* WGL_EXT_extensions_string */ + +#ifndef WGL_EXT_framebuffer_sRGB +#define WGL_EXT_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9 +#endif /* WGL_EXT_framebuffer_sRGB */ + +#ifndef WGL_EXT_make_current_read +#define WGL_EXT_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTEXTPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentEXT (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCEXT (void); +#endif +#endif /* WGL_EXT_make_current_read */ + +#ifndef WGL_EXT_multisample +#define WGL_EXT_multisample 1 +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif /* WGL_EXT_multisample */ + +#ifndef WGL_EXT_pbuffer +#define WGL_EXT_pbuffer 1 +DECLARE_HANDLE(HPBUFFEREXT); +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 +typedef HPBUFFEREXT (WINAPI * PFNWGLCREATEPBUFFEREXTPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFEREXT WINAPI wglCreatePbufferEXT (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCEXT (HPBUFFEREXT hPbuffer); +int WINAPI wglReleasePbufferDCEXT (HPBUFFEREXT hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferEXT (HPBUFFEREXT hPbuffer); +BOOL WINAPI wglQueryPbufferEXT (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_EXT_pbuffer */ + +#ifndef WGL_EXT_pixel_format +#define WGL_EXT_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATEXTPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatEXT (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_EXT_pixel_format */ + +#ifndef WGL_EXT_pixel_format_packed_float +#define WGL_EXT_pixel_format_packed_float 1 +#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8 +#endif /* WGL_EXT_pixel_format_packed_float */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); +typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSwapIntervalEXT (int interval); +int WINAPI wglGetSwapIntervalEXT (void); +#endif +#endif /* WGL_EXT_swap_control */ + +#ifndef WGL_EXT_swap_control_tear +#define WGL_EXT_swap_control_tear 1 +#endif /* WGL_EXT_swap_control_tear */ + +#ifndef WGL_I3D_digital_video_control +#define WGL_I3D_digital_video_control 1 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 +typedef BOOL (WINAPI * PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetDigitalVideoParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetDigitalVideoParametersI3D (HDC hDC, int iAttribute, const int *piValue); +#endif +#endif /* WGL_I3D_digital_video_control */ + +#ifndef WGL_I3D_gamma +#define WGL_I3D_gamma 1 +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetGammaTableParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetGammaTableParametersI3D (HDC hDC, int iAttribute, const int *piValue); +BOOL WINAPI wglGetGammaTableI3D (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +BOOL WINAPI wglSetGammaTableI3D (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#endif +#endif /* WGL_I3D_gamma */ + +#ifndef WGL_I3D_genlock +#define WGL_I3D_genlock 1 +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C +typedef BOOL (WINAPI * PFNWGLENABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLDISABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLISENABLEDGENLOCKI3DPROC) (HDC hDC, BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEI3DPROC) (HDC hDC, UINT uSource); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEI3DPROC) (HDC hDC, UINT *uSource); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT uEdge); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT *uEdge); +typedef BOOL (WINAPI * PFNWGLGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT uRate); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT *uRate); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT uDelay); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT *uDelay); +typedef BOOL (WINAPI * PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableGenlockI3D (HDC hDC); +BOOL WINAPI wglDisableGenlockI3D (HDC hDC); +BOOL WINAPI wglIsEnabledGenlockI3D (HDC hDC, BOOL *pFlag); +BOOL WINAPI wglGenlockSourceI3D (HDC hDC, UINT uSource); +BOOL WINAPI wglGetGenlockSourceI3D (HDC hDC, UINT *uSource); +BOOL WINAPI wglGenlockSourceEdgeI3D (HDC hDC, UINT uEdge); +BOOL WINAPI wglGetGenlockSourceEdgeI3D (HDC hDC, UINT *uEdge); +BOOL WINAPI wglGenlockSampleRateI3D (HDC hDC, UINT uRate); +BOOL WINAPI wglGetGenlockSampleRateI3D (HDC hDC, UINT *uRate); +BOOL WINAPI wglGenlockSourceDelayI3D (HDC hDC, UINT uDelay); +BOOL WINAPI wglGetGenlockSourceDelayI3D (HDC hDC, UINT *uDelay); +BOOL WINAPI wglQueryGenlockMaxSourceDelayI3D (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#endif +#endif /* WGL_I3D_genlock */ + +#ifndef WGL_I3D_image_buffer +#define WGL_I3D_image_buffer 1 +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 +typedef LPVOID (WINAPI * PFNWGLCREATEIMAGEBUFFERI3DPROC) (HDC hDC, DWORD dwSize, UINT uFlags); +typedef BOOL (WINAPI * PFNWGLDESTROYIMAGEBUFFERI3DPROC) (HDC hDC, LPVOID pAddress); +typedef BOOL (WINAPI * PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +typedef BOOL (WINAPI * PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const LPVOID *pAddress, UINT count); +#ifdef WGL_WGLEXT_PROTOTYPES +LPVOID WINAPI wglCreateImageBufferI3D (HDC hDC, DWORD dwSize, UINT uFlags); +BOOL WINAPI wglDestroyImageBufferI3D (HDC hDC, LPVOID pAddress); +BOOL WINAPI wglAssociateImageBufferEventsI3D (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +BOOL WINAPI wglReleaseImageBufferEventsI3D (HDC hDC, const LPVOID *pAddress, UINT count); +#endif +#endif /* WGL_I3D_image_buffer */ + +#ifndef WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_lock 1 +typedef BOOL (WINAPI * PFNWGLENABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLDISABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLISENABLEDFRAMELOCKI3DPROC) (BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMELOCKMASTERI3DPROC) (BOOL *pFlag); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableFrameLockI3D (void); +BOOL WINAPI wglDisableFrameLockI3D (void); +BOOL WINAPI wglIsEnabledFrameLockI3D (BOOL *pFlag); +BOOL WINAPI wglQueryFrameLockMasterI3D (BOOL *pFlag); +#endif +#endif /* WGL_I3D_swap_frame_lock */ + +#ifndef WGL_I3D_swap_frame_usage +#define WGL_I3D_swap_frame_usage 1 +typedef BOOL (WINAPI * PFNWGLGETFRAMEUSAGEI3DPROC) (float *pUsage); +typedef BOOL (WINAPI * PFNWGLBEGINFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLENDFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMETRACKINGI3DPROC) (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetFrameUsageI3D (float *pUsage); +BOOL WINAPI wglBeginFrameTrackingI3D (void); +BOOL WINAPI wglEndFrameTrackingI3D (void); +BOOL WINAPI wglQueryFrameTrackingI3D (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#endif +#endif /* WGL_I3D_swap_frame_usage */ + +#ifndef WGL_NV_DX_interop +#define WGL_NV_DX_interop 1 +#define WGL_ACCESS_READ_ONLY_NV 0x00000000 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 +typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLENVPROC) (void *dxObject, HANDLE shareHandle); +typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICENVPROC) (void *dxDevice); +typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICENVPROC) (HANDLE hDevice); +typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTNVPROC) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECTNVPROC) (HANDLE hDevice, HANDLE hObject); +typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSNVPROC) (HANDLE hObject, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDXSetResourceShareHandleNV (void *dxObject, HANDLE shareHandle); +HANDLE WINAPI wglDXOpenDeviceNV (void *dxDevice); +BOOL WINAPI wglDXCloseDeviceNV (HANDLE hDevice); +HANDLE WINAPI wglDXRegisterObjectNV (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +BOOL WINAPI wglDXUnregisterObjectNV (HANDLE hDevice, HANDLE hObject); +BOOL WINAPI wglDXObjectAccessNV (HANDLE hObject, GLenum access); +BOOL WINAPI wglDXLockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +BOOL WINAPI wglDXUnlockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +#endif +#endif /* WGL_NV_DX_interop */ + +#ifndef WGL_NV_DX_interop2 +#define WGL_NV_DX_interop2 1 +#endif /* WGL_NV_DX_interop2 */ + +#ifndef WGL_NV_copy_image +#define WGL_NV_copy_image 1 +typedef BOOL (WINAPI * PFNWGLCOPYIMAGESUBDATANVPROC) (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglCopyImageSubDataNV (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* WGL_NV_copy_image */ + +#ifndef WGL_NV_delay_before_swap +#define WGL_NV_delay_before_swap 1 +typedef BOOL (WINAPI * PFNWGLDELAYBEFORESWAPNVPROC) (HDC hDC, GLfloat seconds); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDelayBeforeSwapNV (HDC hDC, GLfloat seconds); +#endif +#endif /* WGL_NV_delay_before_swap */ + +#ifndef WGL_NV_float_buffer +#define WGL_NV_float_buffer 1 +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif /* WGL_NV_float_buffer */ + +#ifndef WGL_NV_gpu_affinity +#define WGL_NV_gpu_affinity 1 +DECLARE_HANDLE(HGPUNV); +struct _GPU_DEVICE { + DWORD cb; + CHAR DeviceName[32]; + CHAR DeviceString[128]; + DWORD Flags; + RECT rcVirtualScreen; +}; +typedef struct _GPU_DEVICE *PGPU_DEVICE; +#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0 +#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1 +typedef BOOL (WINAPI * PFNWGLENUMGPUSNVPROC) (UINT iGpuIndex, HGPUNV *phGpu); +typedef BOOL (WINAPI * PFNWGLENUMGPUDEVICESNVPROC) (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +typedef HDC (WINAPI * PFNWGLCREATEAFFINITYDCNVPROC) (const HGPUNV *phGpuList); +typedef BOOL (WINAPI * PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +typedef BOOL (WINAPI * PFNWGLDELETEDCNVPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnumGpusNV (UINT iGpuIndex, HGPUNV *phGpu); +BOOL WINAPI wglEnumGpuDevicesNV (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +HDC WINAPI wglCreateAffinityDCNV (const HGPUNV *phGpuList); +BOOL WINAPI wglEnumGpusFromAffinityDCNV (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +BOOL WINAPI wglDeleteDCNV (HDC hdc); +#endif +#endif /* WGL_NV_gpu_affinity */ + +#ifndef WGL_NV_multigpu_context +#define WGL_NV_multigpu_context 1 +#define WGL_CONTEXT_MULTIGPU_ATTRIB_NV 0x20AA +#define WGL_CONTEXT_MULTIGPU_ATTRIB_SINGLE_NV 0x20AB +#define WGL_CONTEXT_MULTIGPU_ATTRIB_AFR_NV 0x20AC +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTICAST_NV 0x20AD +#define WGL_CONTEXT_MULTIGPU_ATTRIB_MULTI_DISPLAY_MULTICAST_NV 0x20AE +#endif /* WGL_NV_multigpu_context */ + +#ifndef WGL_NV_multisample_coverage +#define WGL_NV_multisample_coverage 1 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_COLOR_SAMPLES_NV 0x20B9 +#endif /* WGL_NV_multisample_coverage */ + +#ifndef WGL_NV_present_video +#define WGL_NV_present_video 1 +DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); +#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef int (WINAPI * PFNWGLENUMERATEVIDEODEVICESNVPROC) (HDC hDc, HVIDEOOUTPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLBINDVIDEODEVICENVPROC) (HDC hDc, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +typedef BOOL (WINAPI * PFNWGLQUERYCURRENTCONTEXTNVPROC) (int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +int WINAPI wglEnumerateVideoDevicesNV (HDC hDc, HVIDEOOUTPUTDEVICENV *phDeviceList); +BOOL WINAPI wglBindVideoDeviceNV (HDC hDc, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +BOOL WINAPI wglQueryCurrentContextNV (int iAttribute, int *piValue); +#endif +#endif /* WGL_NV_present_video */ + +#ifndef WGL_NV_render_depth_texture +#define WGL_NV_render_depth_texture 1 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif /* WGL_NV_render_depth_texture */ + +#ifndef WGL_NV_render_texture_rectangle +#define WGL_NV_render_texture_rectangle 1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif /* WGL_NV_render_texture_rectangle */ + +#ifndef WGL_NV_swap_group +#define WGL_NV_swap_group 1 +typedef BOOL (WINAPI * PFNWGLJOINSWAPGROUPNVPROC) (HDC hDC, GLuint group); +typedef BOOL (WINAPI * PFNWGLBINDSWAPBARRIERNVPROC) (GLuint group, GLuint barrier); +typedef BOOL (WINAPI * PFNWGLQUERYSWAPGROUPNVPROC) (HDC hDC, GLuint *group, GLuint *barrier); +typedef BOOL (WINAPI * PFNWGLQUERYMAXSWAPGROUPSNVPROC) (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMECOUNTNVPROC) (HDC hDC, GLuint *count); +typedef BOOL (WINAPI * PFNWGLRESETFRAMECOUNTNVPROC) (HDC hDC); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglJoinSwapGroupNV (HDC hDC, GLuint group); +BOOL WINAPI wglBindSwapBarrierNV (GLuint group, GLuint barrier); +BOOL WINAPI wglQuerySwapGroupNV (HDC hDC, GLuint *group, GLuint *barrier); +BOOL WINAPI wglQueryMaxSwapGroupsNV (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +BOOL WINAPI wglQueryFrameCountNV (HDC hDC, GLuint *count); +BOOL WINAPI wglResetFrameCountNV (HDC hDC); +#endif +#endif /* WGL_NV_swap_group */ + +#ifndef WGL_NV_vertex_array_range +#define WGL_NV_vertex_array_range 1 +typedef void *(WINAPI * PFNWGLALLOCATEMEMORYNVPROC) (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +typedef void (WINAPI * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#ifdef WGL_WGLEXT_PROTOTYPES +void *WINAPI wglAllocateMemoryNV (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +void WINAPI wglFreeMemoryNV (void *pointer); +#endif +#endif /* WGL_NV_vertex_array_range */ + +#ifndef WGL_NV_video_capture +#define WGL_NV_video_capture 1 +DECLARE_HANDLE(HVIDEOINPUTDEVICENV); +#define WGL_UNIQUE_ID_NV 0x20CE +#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef BOOL (WINAPI * PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +typedef UINT (WINAPI * PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +typedef BOOL (WINAPI * PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindVideoCaptureDeviceNV (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +UINT WINAPI wglEnumerateVideoCaptureDevicesNV (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +BOOL WINAPI wglLockVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +BOOL WINAPI wglQueryVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +BOOL WINAPI wglReleaseVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#endif +#endif /* WGL_NV_video_capture */ + +#ifndef WGL_NV_video_output +#define WGL_NV_video_output 1 +DECLARE_HANDLE(HPVIDEODEV); +#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0 +#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1 +#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2 +#define WGL_VIDEO_OUT_COLOR_NV 0x20C3 +#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4 +#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5 +#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define WGL_VIDEO_OUT_FRAME 0x20C8 +#define WGL_VIDEO_OUT_FIELD_1 0x20C9 +#define WGL_VIDEO_OUT_FIELD_2 0x20CA +#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB +#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC +typedef BOOL (WINAPI * PFNWGLGETVIDEODEVICENVPROC) (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEODEVICENVPROC) (HPVIDEODEV hVideoDevice); +typedef BOOL (WINAPI * PFNWGLBINDVIDEOIMAGENVPROC) (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOIMAGENVPROC) (HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLSENDPBUFFERTOVIDEONVPROC) (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +typedef BOOL (WINAPI * PFNWGLGETVIDEOINFONVPROC) (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetVideoDeviceNV (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +BOOL WINAPI wglReleaseVideoDeviceNV (HPVIDEODEV hVideoDevice); +BOOL WINAPI wglBindVideoImageNV (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglReleaseVideoImageNV (HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglSendPbufferToVideoNV (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +BOOL WINAPI wglGetVideoInfoNV (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#endif +#endif /* WGL_NV_video_output */ + +#ifndef WGL_OML_sync_control +#define WGL_OML_sync_control 1 +typedef BOOL (WINAPI * PFNWGLGETSYNCVALUESOMLPROC) (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLGETMSCRATEOMLPROC) (HDC hdc, INT32 *numerator, INT32 *denominator); +typedef INT64 (WINAPI * PFNWGLSWAPBUFFERSMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef INT64 (WINAPI * PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) (HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef BOOL (WINAPI * PFNWGLWAITFORMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLWAITFORSBCOMLPROC) (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetSyncValuesOML (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglGetMscRateOML (HDC hdc, INT32 *numerator, INT32 *denominator); +INT64 WINAPI wglSwapBuffersMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +INT64 WINAPI wglSwapLayerBuffersMscOML (HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +BOOL WINAPI wglWaitForMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglWaitForSbcOML (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#endif +#endif /* WGL_OML_sync_control */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/KHR/khrplatform.h b/thirdparty/KHR/khrplatform.h new file mode 100644 index 0000000..0164644 --- /dev/null +++ b/thirdparty/KHR/khrplatform.h @@ -0,0 +1,311 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/thirdparty/glad/include/KHR/khrplatform.h b/thirdparty/glad/include/KHR/khrplatform.h new file mode 100644 index 0000000..0164644 --- /dev/null +++ b/thirdparty/glad/include/KHR/khrplatform.h @@ -0,0 +1,311 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/thirdparty/glad/include/glad/glad.h b/thirdparty/glad/include/glad/glad.h new file mode 100644 index 0000000..784862c --- /dev/null +++ b/thirdparty/glad/include/glad/glad.h @@ -0,0 +1,3437 @@ +/* + + OpenGL loader generated by glad 0.1.36 on Sun Apr 7 23:56:48 2024. + + Language/Generator: C/C++ + Specification: gl + APIs: gl=3.1 + Profile: compatibility + Extensions: + GL_KHR_debug + Loader: True + Local files: False + Omit khrplatform: False + Reproducible: False + + Commandline: + --profile="compatibility" --api="gl=3.1" --generator="c" --spec="gl" --extensions="GL_KHR_debug" + Online: + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.1&extensions=GL_KHR_debug +*/ + + +#ifndef __glad_h_ +#define __glad_h_ + +#ifdef __gl_h_ +#error OpenGL header already included, remove this include, glad already provides it +#endif +#define __gl_h_ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define APIENTRY __stdcall +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +#ifndef GLAPIENTRY +#define GLAPIENTRY APIENTRY +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct gladGLversionStruct { + int major; + int minor; +}; + +typedef void* (* GLADloadproc)(const char *name); + +#ifndef GLAPI +# if defined(GLAD_GLAPI_EXPORT) +# if defined(_WIN32) || defined(__CYGWIN__) +# if defined(GLAD_GLAPI_EXPORT_BUILD) +# if defined(__GNUC__) +# define GLAPI __attribute__ ((dllexport)) extern +# else +# define GLAPI __declspec(dllexport) extern +# endif +# else +# if defined(__GNUC__) +# define GLAPI __attribute__ ((dllimport)) extern +# else +# define GLAPI __declspec(dllimport) extern +# endif +# endif +# elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD) +# define GLAPI __attribute__ ((visibility ("default"))) extern +# else +# define GLAPI extern +# endif +# else +# define GLAPI extern +# endif +#endif + +GLAPI struct gladGLversionStruct GLVersion; + +GLAPI int gladLoadGL(void); + +GLAPI int gladLoadGLLoader(GLADloadproc); + +#include +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef khronos_int8_t GLbyte; +typedef khronos_uint8_t GLubyte; +typedef khronos_int16_t GLshort; +typedef khronos_uint16_t GLushort; +typedef int GLint; +typedef unsigned int GLuint; +typedef khronos_int32_t GLclampx; +typedef int GLsizei; +typedef khronos_float_t GLfloat; +typedef khronos_float_t GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void *GLeglClientBufferEXT; +typedef void *GLeglImageOES; +typedef char GLchar; +typedef char GLcharARB; +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef khronos_uint16_t GLhalf; +typedef khronos_uint16_t GLhalfARB; +typedef khronos_int32_t GLfixed; +typedef khronos_intptr_t GLintptr; +typedef khronos_intptr_t GLintptrARB; +typedef khronos_ssize_t GLsizeiptr; +typedef khronos_ssize_t GLsizeiptrARB; +typedef khronos_int64_t GLint64; +typedef khronos_int64_t GLint64EXT; +typedef khronos_uint64_t GLuint64; +typedef khronos_uint64_t GLuint64EXT; +typedef struct __GLsync *GLsync; +struct _cl_context; +struct _cl_event; +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +typedef unsigned short GLhalfNV; +typedef GLintptr GLvdpauSurfaceNV; +typedef void (APIENTRY *GLVULKANPROCNV)(void); +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +#define GL_TEXTURE 0x1702 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +#define GL_CURRENT_BIT 0x00000001 +#define GL_POINT_BIT 0x00000002 +#define GL_LINE_BIT 0x00000004 +#define GL_POLYGON_BIT 0x00000008 +#define GL_POLYGON_STIPPLE_BIT 0x00000010 +#define GL_PIXEL_MODE_BIT 0x00000020 +#define GL_LIGHTING_BIT 0x00000040 +#define GL_FOG_BIT 0x00000080 +#define GL_ACCUM_BUFFER_BIT 0x00000200 +#define GL_VIEWPORT_BIT 0x00000800 +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_ENABLE_BIT 0x00002000 +#define GL_HINT_BIT 0x00008000 +#define GL_EVAL_BIT 0x00010000 +#define GL_LIST_BIT 0x00020000 +#define GL_TEXTURE_BIT 0x00040000 +#define GL_SCISSOR_BIT 0x00080000 +#define GL_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_QUAD_STRIP 0x0008 +#define GL_POLYGON 0x0009 +#define GL_ACCUM 0x0100 +#define GL_LOAD 0x0101 +#define GL_RETURN 0x0102 +#define GL_MULT 0x0103 +#define GL_ADD 0x0104 +#define GL_AUX0 0x0409 +#define GL_AUX1 0x040A +#define GL_AUX2 0x040B +#define GL_AUX3 0x040C +#define GL_2D 0x0600 +#define GL_3D 0x0601 +#define GL_3D_COLOR 0x0602 +#define GL_3D_COLOR_TEXTURE 0x0603 +#define GL_4D_COLOR_TEXTURE 0x0604 +#define GL_PASS_THROUGH_TOKEN 0x0700 +#define GL_POINT_TOKEN 0x0701 +#define GL_LINE_TOKEN 0x0702 +#define GL_POLYGON_TOKEN 0x0703 +#define GL_BITMAP_TOKEN 0x0704 +#define GL_DRAW_PIXEL_TOKEN 0x0705 +#define GL_COPY_PIXEL_TOKEN 0x0706 +#define GL_LINE_RESET_TOKEN 0x0707 +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 +#define GL_COEFF 0x0A00 +#define GL_ORDER 0x0A01 +#define GL_DOMAIN 0x0A02 +#define GL_PIXEL_MAP_I_TO_I 0x0C70 +#define GL_PIXEL_MAP_S_TO_S 0x0C71 +#define GL_PIXEL_MAP_I_TO_R 0x0C72 +#define GL_PIXEL_MAP_I_TO_G 0x0C73 +#define GL_PIXEL_MAP_I_TO_B 0x0C74 +#define GL_PIXEL_MAP_I_TO_A 0x0C75 +#define GL_PIXEL_MAP_R_TO_R 0x0C76 +#define GL_PIXEL_MAP_G_TO_G 0x0C77 +#define GL_PIXEL_MAP_B_TO_B 0x0C78 +#define GL_PIXEL_MAP_A_TO_A 0x0C79 +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_CURRENT_RASTER_COLOR 0x0B04 +#define GL_CURRENT_RASTER_INDEX 0x0B05 +#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +#define GL_POINT_SMOOTH 0x0B10 +#define GL_LINE_STIPPLE 0x0B24 +#define GL_LINE_STIPPLE_PATTERN 0x0B25 +#define GL_LINE_STIPPLE_REPEAT 0x0B26 +#define GL_LIST_MODE 0x0B30 +#define GL_MAX_LIST_NESTING 0x0B31 +#define GL_LIST_BASE 0x0B32 +#define GL_LIST_INDEX 0x0B33 +#define GL_POLYGON_STIPPLE 0x0B42 +#define GL_EDGE_FLAG 0x0B43 +#define GL_LIGHTING 0x0B50 +#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_SHADE_MODEL 0x0B54 +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_PARAMETER 0x0B56 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_FOG 0x0B60 +#define GL_FOG_INDEX 0x0B61 +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_COLOR 0x0B66 +#define GL_ACCUM_CLEAR_VALUE 0x0B80 +#define GL_MATRIX_MODE 0x0BA0 +#define GL_NORMALIZE 0x0BA1 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +#define GL_ALPHA_TEST 0x0BC0 +#define GL_ALPHA_TEST_FUNC 0x0BC1 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_LOGIC_OP 0x0BF1 +#define GL_AUX_BUFFERS 0x0C00 +#define GL_INDEX_CLEAR_VALUE 0x0C20 +#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_INDEX_MODE 0x0C30 +#define GL_RGBA_MODE 0x0C31 +#define GL_RENDER_MODE 0x0C40 +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_FOG_HINT 0x0C54 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_Q 0x0C63 +#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 +#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 +#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 +#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 +#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 +#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 +#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 +#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 +#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 +#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 +#define GL_MAP_COLOR 0x0D10 +#define GL_MAP_STENCIL 0x0D11 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_INDEX_OFFSET 0x0D13 +#define GL_RED_SCALE 0x0D14 +#define GL_RED_BIAS 0x0D15 +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 +#define GL_GREEN_SCALE 0x0D18 +#define GL_GREEN_BIAS 0x0D19 +#define GL_BLUE_SCALE 0x0D1A +#define GL_BLUE_BIAS 0x0D1B +#define GL_ALPHA_SCALE 0x0D1C +#define GL_ALPHA_BIAS 0x0D1D +#define GL_DEPTH_SCALE 0x0D1E +#define GL_DEPTH_BIAS 0x0D1F +#define GL_MAX_EVAL_ORDER 0x0D30 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_PIXEL_MAP_TABLE 0x0D34 +#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_NAME_STACK_DEPTH 0x0D37 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_INDEX_BITS 0x0D51 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_ALPHA_BITS 0x0D55 +#define GL_DEPTH_BITS 0x0D56 +#define GL_STENCIL_BITS 0x0D57 +#define GL_ACCUM_RED_BITS 0x0D58 +#define GL_ACCUM_GREEN_BITS 0x0D59 +#define GL_ACCUM_BLUE_BITS 0x0D5A +#define GL_ACCUM_ALPHA_BITS 0x0D5B +#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_AUTO_NORMAL 0x0D80 +#define GL_MAP1_COLOR_4 0x0D90 +#define GL_MAP1_INDEX 0x0D91 +#define GL_MAP1_NORMAL 0x0D92 +#define GL_MAP1_TEXTURE_COORD_1 0x0D93 +#define GL_MAP1_TEXTURE_COORD_2 0x0D94 +#define GL_MAP1_TEXTURE_COORD_3 0x0D95 +#define GL_MAP1_TEXTURE_COORD_4 0x0D96 +#define GL_MAP1_VERTEX_3 0x0D97 +#define GL_MAP1_VERTEX_4 0x0D98 +#define GL_MAP2_COLOR_4 0x0DB0 +#define GL_MAP2_INDEX 0x0DB1 +#define GL_MAP2_NORMAL 0x0DB2 +#define GL_MAP2_TEXTURE_COORD_1 0x0DB3 +#define GL_MAP2_TEXTURE_COORD_2 0x0DB4 +#define GL_MAP2_TEXTURE_COORD_3 0x0DB5 +#define GL_MAP2_TEXTURE_COORD_4 0x0DB6 +#define GL_MAP2_VERTEX_3 0x0DB7 +#define GL_MAP2_VERTEX_4 0x0DB8 +#define GL_MAP1_GRID_DOMAIN 0x0DD0 +#define GL_MAP1_GRID_SEGMENTS 0x0DD1 +#define GL_MAP2_GRID_DOMAIN 0x0DD2 +#define GL_MAP2_GRID_SEGMENTS 0x0DD3 +#define GL_TEXTURE_COMPONENTS 0x1003 +#define GL_TEXTURE_BORDER 0x1005 +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 +#define GL_COMPILE 0x1300 +#define GL_COMPILE_AND_EXECUTE 0x1301 +#define GL_2_BYTES 0x1407 +#define GL_3_BYTES 0x1408 +#define GL_4_BYTES 0x1409 +#define GL_EMISSION 0x1600 +#define GL_SHININESS 0x1601 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_COLOR_INDEXES 0x1603 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_COLOR_INDEX 0x1900 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_BITMAP 0x1A00 +#define GL_RENDER 0x1C00 +#define GL_FEEDBACK 0x1C01 +#define GL_SELECT 0x1C02 +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 +#define GL_S 0x2000 +#define GL_T 0x2001 +#define GL_R 0x2002 +#define GL_Q 0x2003 +#define GL_MODULATE 0x2100 +#define GL_DECAL 0x2101 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_ENV 0x2300 +#define GL_EYE_LINEAR 0x2400 +#define GL_OBJECT_LINEAR 0x2401 +#define GL_SPHERE_MAP 0x2402 +#define GL_TEXTURE_GEN_MODE 0x2500 +#define GL_OBJECT_PLANE 0x2501 +#define GL_EYE_PLANE 0x2502 +#define GL_CLAMP 0x2900 +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_DOUBLE 0x140A +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001 +#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 +#define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_INDEX_ARRAY_POINTER 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 +#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 +#define GL_SELECTION_BUFFER_POINTER 0x0DF3 +#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B +#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 +#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 +#define GL_SELECTION_BUFFER_SIZE 0x0DF4 +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_EDGE_FLAG_ARRAY 0x8079 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_INDEX_ARRAY_TYPE 0x8085 +#define GL_INDEX_ARRAY_STRIDE 0x8086 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +#define GL_TEXTURE_PRIORITY 0x8066 +#define GL_TEXTURE_RESIDENT 0x8067 +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_V2F 0x2A20 +#define GL_V3F 0x2A21 +#define GL_C4UB_V2F 0x2A22 +#define GL_C4UB_V3F 0x2A23 +#define GL_C3F_V3F 0x2A24 +#define GL_N3F_V3F 0x2A25 +#define GL_C4F_N3F_V3F 0x2A26 +#define GL_T2F_V3F 0x2A27 +#define GL_T4F_V4F 0x2A28 +#define GL_T2F_C4UB_V3F 0x2A29 +#define GL_T2F_C3F_V3F 0x2A2A +#define GL_T2F_N3F_V3F 0x2A2B +#define GL_T2F_C4F_N3F_V3F 0x2A2C +#define GL_T4F_C4F_N3F_V4F 0x2A2D +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_FUNC_SUBTRACT 0x800A +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFF +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 +GLAPI int GLAD_GL_VERSION_1_0; +typedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum mode); +GLAPI PFNGLCULLFACEPROC glad_glCullFace; +#define glCullFace glad_glCullFace +typedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum mode); +GLAPI PFNGLFRONTFACEPROC glad_glFrontFace; +#define glFrontFace glad_glFrontFace +typedef void (APIENTRYP PFNGLHINTPROC)(GLenum target, GLenum mode); +GLAPI PFNGLHINTPROC glad_glHint; +#define glHint glad_glHint +typedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat width); +GLAPI PFNGLLINEWIDTHPROC glad_glLineWidth; +#define glLineWidth glad_glLineWidth +typedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat size); +GLAPI PFNGLPOINTSIZEPROC glad_glPointSize; +#define glPointSize glad_glPointSize +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); +GLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode; +#define glPolygonMode glad_glPolygonMode +typedef void (APIENTRYP PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI PFNGLSCISSORPROC glad_glScissor; +#define glScissor glad_glScissor +typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); +GLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf; +#define glTexParameterf glad_glTexParameterf +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat *params); +GLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; +#define glTexParameterfv glad_glTexParameterfv +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); +GLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri; +#define glTexParameteri glad_glTexParameteri +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint *params); +GLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; +#define glTexParameteriv glad_glTexParameteriv +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D; +#define glTexImage1D glad_glTexImage1D +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D; +#define glTexImage2D glad_glTexImage2D +typedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum buf); +GLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer; +#define glDrawBuffer glad_glDrawBuffer +typedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield mask); +GLAPI PFNGLCLEARPROC glad_glClear; +#define glClear glad_glClear +typedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI PFNGLCLEARCOLORPROC glad_glClearColor; +#define glClearColor glad_glClearColor +typedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint s); +GLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil; +#define glClearStencil glad_glClearStencil +typedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble depth); +GLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth; +#define glClearDepth glad_glClearDepth +typedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint mask); +GLAPI PFNGLSTENCILMASKPROC glad_glStencilMask; +#define glStencilMask glad_glStencilMask +typedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GLAPI PFNGLCOLORMASKPROC glad_glColorMask; +#define glColorMask glad_glColorMask +typedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean flag); +GLAPI PFNGLDEPTHMASKPROC glad_glDepthMask; +#define glDepthMask glad_glDepthMask +typedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum cap); +GLAPI PFNGLDISABLEPROC glad_glDisable; +#define glDisable glad_glDisable +typedef void (APIENTRYP PFNGLENABLEPROC)(GLenum cap); +GLAPI PFNGLENABLEPROC glad_glEnable; +#define glEnable glad_glEnable +typedef void (APIENTRYP PFNGLFINISHPROC)(void); +GLAPI PFNGLFINISHPROC glad_glFinish; +#define glFinish glad_glFinish +typedef void (APIENTRYP PFNGLFLUSHPROC)(void); +GLAPI PFNGLFLUSHPROC glad_glFlush; +#define glFlush glad_glFlush +typedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); +GLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc; +#define glBlendFunc glad_glBlendFunc +typedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum opcode); +GLAPI PFNGLLOGICOPPROC glad_glLogicOp; +#define glLogicOp glad_glLogicOp +typedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); +GLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc; +#define glStencilFunc glad_glStencilFunc +typedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); +GLAPI PFNGLSTENCILOPPROC glad_glStencilOp; +#define glStencilOp glad_glStencilOp +typedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum func); +GLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc; +#define glDepthFunc glad_glDepthFunc +typedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); +GLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref; +#define glPixelStoref glad_glPixelStoref +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); +GLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei; +#define glPixelStorei glad_glPixelStorei +typedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum src); +GLAPI PFNGLREADBUFFERPROC glad_glReadBuffer; +#define glReadBuffer glad_glReadBuffer +typedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); +GLAPI PFNGLREADPIXELSPROC glad_glReadPixels; +#define glReadPixels glad_glReadPixels +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean *data); +GLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv; +#define glGetBooleanv glad_glGetBooleanv +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble *data); +GLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev; +#define glGetDoublev glad_glGetDoublev +typedef GLenum (APIENTRYP PFNGLGETERRORPROC)(void); +GLAPI PFNGLGETERRORPROC glad_glGetError; +#define glGetError glad_glGetError +typedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum pname, GLfloat *data); +GLAPI PFNGLGETFLOATVPROC glad_glGetFloatv; +#define glGetFloatv glad_glGetFloatv +typedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum pname, GLint *data); +GLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv; +#define glGetIntegerv glad_glGetIntegerv +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC)(GLenum name); +GLAPI PFNGLGETSTRINGPROC glad_glGetString; +#define glGetString glad_glGetString +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage; +#define glGetTexImage glad_glGetTexImage +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat *params); +GLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; +#define glGetTexParameterfv glad_glGetTexParameterfv +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; +#define glGetTexParameteriv glad_glGetTexParameteriv +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; +#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; +#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap); +GLAPI PFNGLISENABLEDPROC glad_glIsEnabled; +#define glIsEnabled glad_glIsEnabled +typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); +GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange; +#define glDepthRange glad_glDepthRange +typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI PFNGLVIEWPORTPROC glad_glViewport; +#define glViewport glad_glViewport +typedef void (APIENTRYP PFNGLNEWLISTPROC)(GLuint list, GLenum mode); +GLAPI PFNGLNEWLISTPROC glad_glNewList; +#define glNewList glad_glNewList +typedef void (APIENTRYP PFNGLENDLISTPROC)(void); +GLAPI PFNGLENDLISTPROC glad_glEndList; +#define glEndList glad_glEndList +typedef void (APIENTRYP PFNGLCALLLISTPROC)(GLuint list); +GLAPI PFNGLCALLLISTPROC glad_glCallList; +#define glCallList glad_glCallList +typedef void (APIENTRYP PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void *lists); +GLAPI PFNGLCALLLISTSPROC glad_glCallLists; +#define glCallLists glad_glCallLists +typedef void (APIENTRYP PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); +GLAPI PFNGLDELETELISTSPROC glad_glDeleteLists; +#define glDeleteLists glad_glDeleteLists +typedef GLuint (APIENTRYP PFNGLGENLISTSPROC)(GLsizei range); +GLAPI PFNGLGENLISTSPROC glad_glGenLists; +#define glGenLists glad_glGenLists +typedef void (APIENTRYP PFNGLLISTBASEPROC)(GLuint base); +GLAPI PFNGLLISTBASEPROC glad_glListBase; +#define glListBase glad_glListBase +typedef void (APIENTRYP PFNGLBEGINPROC)(GLenum mode); +GLAPI PFNGLBEGINPROC glad_glBegin; +#define glBegin glad_glBegin +typedef void (APIENTRYP PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap); +GLAPI PFNGLBITMAPPROC glad_glBitmap; +#define glBitmap glad_glBitmap +typedef void (APIENTRYP PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +GLAPI PFNGLCOLOR3BPROC glad_glColor3b; +#define glColor3b glad_glColor3b +typedef void (APIENTRYP PFNGLCOLOR3BVPROC)(const GLbyte *v); +GLAPI PFNGLCOLOR3BVPROC glad_glColor3bv; +#define glColor3bv glad_glColor3bv +typedef void (APIENTRYP PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +GLAPI PFNGLCOLOR3DPROC glad_glColor3d; +#define glColor3d glad_glColor3d +typedef void (APIENTRYP PFNGLCOLOR3DVPROC)(const GLdouble *v); +GLAPI PFNGLCOLOR3DVPROC glad_glColor3dv; +#define glColor3dv glad_glColor3dv +typedef void (APIENTRYP PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +GLAPI PFNGLCOLOR3FPROC glad_glColor3f; +#define glColor3f glad_glColor3f +typedef void (APIENTRYP PFNGLCOLOR3FVPROC)(const GLfloat *v); +GLAPI PFNGLCOLOR3FVPROC glad_glColor3fv; +#define glColor3fv glad_glColor3fv +typedef void (APIENTRYP PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); +GLAPI PFNGLCOLOR3IPROC glad_glColor3i; +#define glColor3i glad_glColor3i +typedef void (APIENTRYP PFNGLCOLOR3IVPROC)(const GLint *v); +GLAPI PFNGLCOLOR3IVPROC glad_glColor3iv; +#define glColor3iv glad_glColor3iv +typedef void (APIENTRYP PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +GLAPI PFNGLCOLOR3SPROC glad_glColor3s; +#define glColor3s glad_glColor3s +typedef void (APIENTRYP PFNGLCOLOR3SVPROC)(const GLshort *v); +GLAPI PFNGLCOLOR3SVPROC glad_glColor3sv; +#define glColor3sv glad_glColor3sv +typedef void (APIENTRYP PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +GLAPI PFNGLCOLOR3UBPROC glad_glColor3ub; +#define glColor3ub glad_glColor3ub +typedef void (APIENTRYP PFNGLCOLOR3UBVPROC)(const GLubyte *v); +GLAPI PFNGLCOLOR3UBVPROC glad_glColor3ubv; +#define glColor3ubv glad_glColor3ubv +typedef void (APIENTRYP PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +GLAPI PFNGLCOLOR3UIPROC glad_glColor3ui; +#define glColor3ui glad_glColor3ui +typedef void (APIENTRYP PFNGLCOLOR3UIVPROC)(const GLuint *v); +GLAPI PFNGLCOLOR3UIVPROC glad_glColor3uiv; +#define glColor3uiv glad_glColor3uiv +typedef void (APIENTRYP PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +GLAPI PFNGLCOLOR3USPROC glad_glColor3us; +#define glColor3us glad_glColor3us +typedef void (APIENTRYP PFNGLCOLOR3USVPROC)(const GLushort *v); +GLAPI PFNGLCOLOR3USVPROC glad_glColor3usv; +#define glColor3usv glad_glColor3usv +typedef void (APIENTRYP PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); +GLAPI PFNGLCOLOR4BPROC glad_glColor4b; +#define glColor4b glad_glColor4b +typedef void (APIENTRYP PFNGLCOLOR4BVPROC)(const GLbyte *v); +GLAPI PFNGLCOLOR4BVPROC glad_glColor4bv; +#define glColor4bv glad_glColor4bv +typedef void (APIENTRYP PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); +GLAPI PFNGLCOLOR4DPROC glad_glColor4d; +#define glColor4d glad_glColor4d +typedef void (APIENTRYP PFNGLCOLOR4DVPROC)(const GLdouble *v); +GLAPI PFNGLCOLOR4DVPROC glad_glColor4dv; +#define glColor4dv glad_glColor4dv +typedef void (APIENTRYP PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI PFNGLCOLOR4FPROC glad_glColor4f; +#define glColor4f glad_glColor4f +typedef void (APIENTRYP PFNGLCOLOR4FVPROC)(const GLfloat *v); +GLAPI PFNGLCOLOR4FVPROC glad_glColor4fv; +#define glColor4fv glad_glColor4fv +typedef void (APIENTRYP PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); +GLAPI PFNGLCOLOR4IPROC glad_glColor4i; +#define glColor4i glad_glColor4i +typedef void (APIENTRYP PFNGLCOLOR4IVPROC)(const GLint *v); +GLAPI PFNGLCOLOR4IVPROC glad_glColor4iv; +#define glColor4iv glad_glColor4iv +typedef void (APIENTRYP PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); +GLAPI PFNGLCOLOR4SPROC glad_glColor4s; +#define glColor4s glad_glColor4s +typedef void (APIENTRYP PFNGLCOLOR4SVPROC)(const GLshort *v); +GLAPI PFNGLCOLOR4SVPROC glad_glColor4sv; +#define glColor4sv glad_glColor4sv +typedef void (APIENTRYP PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); +GLAPI PFNGLCOLOR4UBPROC glad_glColor4ub; +#define glColor4ub glad_glColor4ub +typedef void (APIENTRYP PFNGLCOLOR4UBVPROC)(const GLubyte *v); +GLAPI PFNGLCOLOR4UBVPROC glad_glColor4ubv; +#define glColor4ubv glad_glColor4ubv +typedef void (APIENTRYP PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); +GLAPI PFNGLCOLOR4UIPROC glad_glColor4ui; +#define glColor4ui glad_glColor4ui +typedef void (APIENTRYP PFNGLCOLOR4UIVPROC)(const GLuint *v); +GLAPI PFNGLCOLOR4UIVPROC glad_glColor4uiv; +#define glColor4uiv glad_glColor4uiv +typedef void (APIENTRYP PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); +GLAPI PFNGLCOLOR4USPROC glad_glColor4us; +#define glColor4us glad_glColor4us +typedef void (APIENTRYP PFNGLCOLOR4USVPROC)(const GLushort *v); +GLAPI PFNGLCOLOR4USVPROC glad_glColor4usv; +#define glColor4usv glad_glColor4usv +typedef void (APIENTRYP PFNGLEDGEFLAGPROC)(GLboolean flag); +GLAPI PFNGLEDGEFLAGPROC glad_glEdgeFlag; +#define glEdgeFlag glad_glEdgeFlag +typedef void (APIENTRYP PFNGLEDGEFLAGVPROC)(const GLboolean *flag); +GLAPI PFNGLEDGEFLAGVPROC glad_glEdgeFlagv; +#define glEdgeFlagv glad_glEdgeFlagv +typedef void (APIENTRYP PFNGLENDPROC)(void); +GLAPI PFNGLENDPROC glad_glEnd; +#define glEnd glad_glEnd +typedef void (APIENTRYP PFNGLINDEXDPROC)(GLdouble c); +GLAPI PFNGLINDEXDPROC glad_glIndexd; +#define glIndexd glad_glIndexd +typedef void (APIENTRYP PFNGLINDEXDVPROC)(const GLdouble *c); +GLAPI PFNGLINDEXDVPROC glad_glIndexdv; +#define glIndexdv glad_glIndexdv +typedef void (APIENTRYP PFNGLINDEXFPROC)(GLfloat c); +GLAPI PFNGLINDEXFPROC glad_glIndexf; +#define glIndexf glad_glIndexf +typedef void (APIENTRYP PFNGLINDEXFVPROC)(const GLfloat *c); +GLAPI PFNGLINDEXFVPROC glad_glIndexfv; +#define glIndexfv glad_glIndexfv +typedef void (APIENTRYP PFNGLINDEXIPROC)(GLint c); +GLAPI PFNGLINDEXIPROC glad_glIndexi; +#define glIndexi glad_glIndexi +typedef void (APIENTRYP PFNGLINDEXIVPROC)(const GLint *c); +GLAPI PFNGLINDEXIVPROC glad_glIndexiv; +#define glIndexiv glad_glIndexiv +typedef void (APIENTRYP PFNGLINDEXSPROC)(GLshort c); +GLAPI PFNGLINDEXSPROC glad_glIndexs; +#define glIndexs glad_glIndexs +typedef void (APIENTRYP PFNGLINDEXSVPROC)(const GLshort *c); +GLAPI PFNGLINDEXSVPROC glad_glIndexsv; +#define glIndexsv glad_glIndexsv +typedef void (APIENTRYP PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI PFNGLNORMAL3BPROC glad_glNormal3b; +#define glNormal3b glad_glNormal3b +typedef void (APIENTRYP PFNGLNORMAL3BVPROC)(const GLbyte *v); +GLAPI PFNGLNORMAL3BVPROC glad_glNormal3bv; +#define glNormal3bv glad_glNormal3bv +typedef void (APIENTRYP PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI PFNGLNORMAL3DPROC glad_glNormal3d; +#define glNormal3d glad_glNormal3d +typedef void (APIENTRYP PFNGLNORMAL3DVPROC)(const GLdouble *v); +GLAPI PFNGLNORMAL3DVPROC glad_glNormal3dv; +#define glNormal3dv glad_glNormal3dv +typedef void (APIENTRYP PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI PFNGLNORMAL3FPROC glad_glNormal3f; +#define glNormal3f glad_glNormal3f +typedef void (APIENTRYP PFNGLNORMAL3FVPROC)(const GLfloat *v); +GLAPI PFNGLNORMAL3FVPROC glad_glNormal3fv; +#define glNormal3fv glad_glNormal3fv +typedef void (APIENTRYP PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); +GLAPI PFNGLNORMAL3IPROC glad_glNormal3i; +#define glNormal3i glad_glNormal3i +typedef void (APIENTRYP PFNGLNORMAL3IVPROC)(const GLint *v); +GLAPI PFNGLNORMAL3IVPROC glad_glNormal3iv; +#define glNormal3iv glad_glNormal3iv +typedef void (APIENTRYP PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); +GLAPI PFNGLNORMAL3SPROC glad_glNormal3s; +#define glNormal3s glad_glNormal3s +typedef void (APIENTRYP PFNGLNORMAL3SVPROC)(const GLshort *v); +GLAPI PFNGLNORMAL3SVPROC glad_glNormal3sv; +#define glNormal3sv glad_glNormal3sv +typedef void (APIENTRYP PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); +GLAPI PFNGLRASTERPOS2DPROC glad_glRasterPos2d; +#define glRasterPos2d glad_glRasterPos2d +typedef void (APIENTRYP PFNGLRASTERPOS2DVPROC)(const GLdouble *v); +GLAPI PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv; +#define glRasterPos2dv glad_glRasterPos2dv +typedef void (APIENTRYP PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); +GLAPI PFNGLRASTERPOS2FPROC glad_glRasterPos2f; +#define glRasterPos2f glad_glRasterPos2f +typedef void (APIENTRYP PFNGLRASTERPOS2FVPROC)(const GLfloat *v); +GLAPI PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv; +#define glRasterPos2fv glad_glRasterPos2fv +typedef void (APIENTRYP PFNGLRASTERPOS2IPROC)(GLint x, GLint y); +GLAPI PFNGLRASTERPOS2IPROC glad_glRasterPos2i; +#define glRasterPos2i glad_glRasterPos2i +typedef void (APIENTRYP PFNGLRASTERPOS2IVPROC)(const GLint *v); +GLAPI PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv; +#define glRasterPos2iv glad_glRasterPos2iv +typedef void (APIENTRYP PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); +GLAPI PFNGLRASTERPOS2SPROC glad_glRasterPos2s; +#define glRasterPos2s glad_glRasterPos2s +typedef void (APIENTRYP PFNGLRASTERPOS2SVPROC)(const GLshort *v); +GLAPI PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv; +#define glRasterPos2sv glad_glRasterPos2sv +typedef void (APIENTRYP PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLRASTERPOS3DPROC glad_glRasterPos3d; +#define glRasterPos3d glad_glRasterPos3d +typedef void (APIENTRYP PFNGLRASTERPOS3DVPROC)(const GLdouble *v); +GLAPI PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv; +#define glRasterPos3dv glad_glRasterPos3dv +typedef void (APIENTRYP PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLRASTERPOS3FPROC glad_glRasterPos3f; +#define glRasterPos3f glad_glRasterPos3f +typedef void (APIENTRYP PFNGLRASTERPOS3FVPROC)(const GLfloat *v); +GLAPI PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv; +#define glRasterPos3fv glad_glRasterPos3fv +typedef void (APIENTRYP PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); +GLAPI PFNGLRASTERPOS3IPROC glad_glRasterPos3i; +#define glRasterPos3i glad_glRasterPos3i +typedef void (APIENTRYP PFNGLRASTERPOS3IVPROC)(const GLint *v); +GLAPI PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv; +#define glRasterPos3iv glad_glRasterPos3iv +typedef void (APIENTRYP PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); +GLAPI PFNGLRASTERPOS3SPROC glad_glRasterPos3s; +#define glRasterPos3s glad_glRasterPos3s +typedef void (APIENTRYP PFNGLRASTERPOS3SVPROC)(const GLshort *v); +GLAPI PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv; +#define glRasterPos3sv glad_glRasterPos3sv +typedef void (APIENTRYP PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI PFNGLRASTERPOS4DPROC glad_glRasterPos4d; +#define glRasterPos4d glad_glRasterPos4d +typedef void (APIENTRYP PFNGLRASTERPOS4DVPROC)(const GLdouble *v); +GLAPI PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv; +#define glRasterPos4dv glad_glRasterPos4dv +typedef void (APIENTRYP PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI PFNGLRASTERPOS4FPROC glad_glRasterPos4f; +#define glRasterPos4f glad_glRasterPos4f +typedef void (APIENTRYP PFNGLRASTERPOS4FVPROC)(const GLfloat *v); +GLAPI PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv; +#define glRasterPos4fv glad_glRasterPos4fv +typedef void (APIENTRYP PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); +GLAPI PFNGLRASTERPOS4IPROC glad_glRasterPos4i; +#define glRasterPos4i glad_glRasterPos4i +typedef void (APIENTRYP PFNGLRASTERPOS4IVPROC)(const GLint *v); +GLAPI PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv; +#define glRasterPos4iv glad_glRasterPos4iv +typedef void (APIENTRYP PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI PFNGLRASTERPOS4SPROC glad_glRasterPos4s; +#define glRasterPos4s glad_glRasterPos4s +typedef void (APIENTRYP PFNGLRASTERPOS4SVPROC)(const GLshort *v); +GLAPI PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv; +#define glRasterPos4sv glad_glRasterPos4sv +typedef void (APIENTRYP PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); +GLAPI PFNGLRECTDPROC glad_glRectd; +#define glRectd glad_glRectd +typedef void (APIENTRYP PFNGLRECTDVPROC)(const GLdouble *v1, const GLdouble *v2); +GLAPI PFNGLRECTDVPROC glad_glRectdv; +#define glRectdv glad_glRectdv +typedef void (APIENTRYP PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); +GLAPI PFNGLRECTFPROC glad_glRectf; +#define glRectf glad_glRectf +typedef void (APIENTRYP PFNGLRECTFVPROC)(const GLfloat *v1, const GLfloat *v2); +GLAPI PFNGLRECTFVPROC glad_glRectfv; +#define glRectfv glad_glRectfv +typedef void (APIENTRYP PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); +GLAPI PFNGLRECTIPROC glad_glRecti; +#define glRecti glad_glRecti +typedef void (APIENTRYP PFNGLRECTIVPROC)(const GLint *v1, const GLint *v2); +GLAPI PFNGLRECTIVPROC glad_glRectiv; +#define glRectiv glad_glRectiv +typedef void (APIENTRYP PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); +GLAPI PFNGLRECTSPROC glad_glRects; +#define glRects glad_glRects +typedef void (APIENTRYP PFNGLRECTSVPROC)(const GLshort *v1, const GLshort *v2); +GLAPI PFNGLRECTSVPROC glad_glRectsv; +#define glRectsv glad_glRectsv +typedef void (APIENTRYP PFNGLTEXCOORD1DPROC)(GLdouble s); +GLAPI PFNGLTEXCOORD1DPROC glad_glTexCoord1d; +#define glTexCoord1d glad_glTexCoord1d +typedef void (APIENTRYP PFNGLTEXCOORD1DVPROC)(const GLdouble *v); +GLAPI PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv; +#define glTexCoord1dv glad_glTexCoord1dv +typedef void (APIENTRYP PFNGLTEXCOORD1FPROC)(GLfloat s); +GLAPI PFNGLTEXCOORD1FPROC glad_glTexCoord1f; +#define glTexCoord1f glad_glTexCoord1f +typedef void (APIENTRYP PFNGLTEXCOORD1FVPROC)(const GLfloat *v); +GLAPI PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv; +#define glTexCoord1fv glad_glTexCoord1fv +typedef void (APIENTRYP PFNGLTEXCOORD1IPROC)(GLint s); +GLAPI PFNGLTEXCOORD1IPROC glad_glTexCoord1i; +#define glTexCoord1i glad_glTexCoord1i +typedef void (APIENTRYP PFNGLTEXCOORD1IVPROC)(const GLint *v); +GLAPI PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv; +#define glTexCoord1iv glad_glTexCoord1iv +typedef void (APIENTRYP PFNGLTEXCOORD1SPROC)(GLshort s); +GLAPI PFNGLTEXCOORD1SPROC glad_glTexCoord1s; +#define glTexCoord1s glad_glTexCoord1s +typedef void (APIENTRYP PFNGLTEXCOORD1SVPROC)(const GLshort *v); +GLAPI PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv; +#define glTexCoord1sv glad_glTexCoord1sv +typedef void (APIENTRYP PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); +GLAPI PFNGLTEXCOORD2DPROC glad_glTexCoord2d; +#define glTexCoord2d glad_glTexCoord2d +typedef void (APIENTRYP PFNGLTEXCOORD2DVPROC)(const GLdouble *v); +GLAPI PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv; +#define glTexCoord2dv glad_glTexCoord2dv +typedef void (APIENTRYP PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); +GLAPI PFNGLTEXCOORD2FPROC glad_glTexCoord2f; +#define glTexCoord2f glad_glTexCoord2f +typedef void (APIENTRYP PFNGLTEXCOORD2FVPROC)(const GLfloat *v); +GLAPI PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv; +#define glTexCoord2fv glad_glTexCoord2fv +typedef void (APIENTRYP PFNGLTEXCOORD2IPROC)(GLint s, GLint t); +GLAPI PFNGLTEXCOORD2IPROC glad_glTexCoord2i; +#define glTexCoord2i glad_glTexCoord2i +typedef void (APIENTRYP PFNGLTEXCOORD2IVPROC)(const GLint *v); +GLAPI PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv; +#define glTexCoord2iv glad_glTexCoord2iv +typedef void (APIENTRYP PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); +GLAPI PFNGLTEXCOORD2SPROC glad_glTexCoord2s; +#define glTexCoord2s glad_glTexCoord2s +typedef void (APIENTRYP PFNGLTEXCOORD2SVPROC)(const GLshort *v); +GLAPI PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv; +#define glTexCoord2sv glad_glTexCoord2sv +typedef void (APIENTRYP PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); +GLAPI PFNGLTEXCOORD3DPROC glad_glTexCoord3d; +#define glTexCoord3d glad_glTexCoord3d +typedef void (APIENTRYP PFNGLTEXCOORD3DVPROC)(const GLdouble *v); +GLAPI PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv; +#define glTexCoord3dv glad_glTexCoord3dv +typedef void (APIENTRYP PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); +GLAPI PFNGLTEXCOORD3FPROC glad_glTexCoord3f; +#define glTexCoord3f glad_glTexCoord3f +typedef void (APIENTRYP PFNGLTEXCOORD3FVPROC)(const GLfloat *v); +GLAPI PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv; +#define glTexCoord3fv glad_glTexCoord3fv +typedef void (APIENTRYP PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); +GLAPI PFNGLTEXCOORD3IPROC glad_glTexCoord3i; +#define glTexCoord3i glad_glTexCoord3i +typedef void (APIENTRYP PFNGLTEXCOORD3IVPROC)(const GLint *v); +GLAPI PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv; +#define glTexCoord3iv glad_glTexCoord3iv +typedef void (APIENTRYP PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); +GLAPI PFNGLTEXCOORD3SPROC glad_glTexCoord3s; +#define glTexCoord3s glad_glTexCoord3s +typedef void (APIENTRYP PFNGLTEXCOORD3SVPROC)(const GLshort *v); +GLAPI PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv; +#define glTexCoord3sv glad_glTexCoord3sv +typedef void (APIENTRYP PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI PFNGLTEXCOORD4DPROC glad_glTexCoord4d; +#define glTexCoord4d glad_glTexCoord4d +typedef void (APIENTRYP PFNGLTEXCOORD4DVPROC)(const GLdouble *v); +GLAPI PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv; +#define glTexCoord4dv glad_glTexCoord4dv +typedef void (APIENTRYP PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI PFNGLTEXCOORD4FPROC glad_glTexCoord4f; +#define glTexCoord4f glad_glTexCoord4f +typedef void (APIENTRYP PFNGLTEXCOORD4FVPROC)(const GLfloat *v); +GLAPI PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv; +#define glTexCoord4fv glad_glTexCoord4fv +typedef void (APIENTRYP PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); +GLAPI PFNGLTEXCOORD4IPROC glad_glTexCoord4i; +#define glTexCoord4i glad_glTexCoord4i +typedef void (APIENTRYP PFNGLTEXCOORD4IVPROC)(const GLint *v); +GLAPI PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv; +#define glTexCoord4iv glad_glTexCoord4iv +typedef void (APIENTRYP PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI PFNGLTEXCOORD4SPROC glad_glTexCoord4s; +#define glTexCoord4s glad_glTexCoord4s +typedef void (APIENTRYP PFNGLTEXCOORD4SVPROC)(const GLshort *v); +GLAPI PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv; +#define glTexCoord4sv glad_glTexCoord4sv +typedef void (APIENTRYP PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); +GLAPI PFNGLVERTEX2DPROC glad_glVertex2d; +#define glVertex2d glad_glVertex2d +typedef void (APIENTRYP PFNGLVERTEX2DVPROC)(const GLdouble *v); +GLAPI PFNGLVERTEX2DVPROC glad_glVertex2dv; +#define glVertex2dv glad_glVertex2dv +typedef void (APIENTRYP PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); +GLAPI PFNGLVERTEX2FPROC glad_glVertex2f; +#define glVertex2f glad_glVertex2f +typedef void (APIENTRYP PFNGLVERTEX2FVPROC)(const GLfloat *v); +GLAPI PFNGLVERTEX2FVPROC glad_glVertex2fv; +#define glVertex2fv glad_glVertex2fv +typedef void (APIENTRYP PFNGLVERTEX2IPROC)(GLint x, GLint y); +GLAPI PFNGLVERTEX2IPROC glad_glVertex2i; +#define glVertex2i glad_glVertex2i +typedef void (APIENTRYP PFNGLVERTEX2IVPROC)(const GLint *v); +GLAPI PFNGLVERTEX2IVPROC glad_glVertex2iv; +#define glVertex2iv glad_glVertex2iv +typedef void (APIENTRYP PFNGLVERTEX2SPROC)(GLshort x, GLshort y); +GLAPI PFNGLVERTEX2SPROC glad_glVertex2s; +#define glVertex2s glad_glVertex2s +typedef void (APIENTRYP PFNGLVERTEX2SVPROC)(const GLshort *v); +GLAPI PFNGLVERTEX2SVPROC glad_glVertex2sv; +#define glVertex2sv glad_glVertex2sv +typedef void (APIENTRYP PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLVERTEX3DPROC glad_glVertex3d; +#define glVertex3d glad_glVertex3d +typedef void (APIENTRYP PFNGLVERTEX3DVPROC)(const GLdouble *v); +GLAPI PFNGLVERTEX3DVPROC glad_glVertex3dv; +#define glVertex3dv glad_glVertex3dv +typedef void (APIENTRYP PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLVERTEX3FPROC glad_glVertex3f; +#define glVertex3f glad_glVertex3f +typedef void (APIENTRYP PFNGLVERTEX3FVPROC)(const GLfloat *v); +GLAPI PFNGLVERTEX3FVPROC glad_glVertex3fv; +#define glVertex3fv glad_glVertex3fv +typedef void (APIENTRYP PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); +GLAPI PFNGLVERTEX3IPROC glad_glVertex3i; +#define glVertex3i glad_glVertex3i +typedef void (APIENTRYP PFNGLVERTEX3IVPROC)(const GLint *v); +GLAPI PFNGLVERTEX3IVPROC glad_glVertex3iv; +#define glVertex3iv glad_glVertex3iv +typedef void (APIENTRYP PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); +GLAPI PFNGLVERTEX3SPROC glad_glVertex3s; +#define glVertex3s glad_glVertex3s +typedef void (APIENTRYP PFNGLVERTEX3SVPROC)(const GLshort *v); +GLAPI PFNGLVERTEX3SVPROC glad_glVertex3sv; +#define glVertex3sv glad_glVertex3sv +typedef void (APIENTRYP PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI PFNGLVERTEX4DPROC glad_glVertex4d; +#define glVertex4d glad_glVertex4d +typedef void (APIENTRYP PFNGLVERTEX4DVPROC)(const GLdouble *v); +GLAPI PFNGLVERTEX4DVPROC glad_glVertex4dv; +#define glVertex4dv glad_glVertex4dv +typedef void (APIENTRYP PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI PFNGLVERTEX4FPROC glad_glVertex4f; +#define glVertex4f glad_glVertex4f +typedef void (APIENTRYP PFNGLVERTEX4FVPROC)(const GLfloat *v); +GLAPI PFNGLVERTEX4FVPROC glad_glVertex4fv; +#define glVertex4fv glad_glVertex4fv +typedef void (APIENTRYP PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); +GLAPI PFNGLVERTEX4IPROC glad_glVertex4i; +#define glVertex4i glad_glVertex4i +typedef void (APIENTRYP PFNGLVERTEX4IVPROC)(const GLint *v); +GLAPI PFNGLVERTEX4IVPROC glad_glVertex4iv; +#define glVertex4iv glad_glVertex4iv +typedef void (APIENTRYP PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI PFNGLVERTEX4SPROC glad_glVertex4s; +#define glVertex4s glad_glVertex4s +typedef void (APIENTRYP PFNGLVERTEX4SVPROC)(const GLshort *v); +GLAPI PFNGLVERTEX4SVPROC glad_glVertex4sv; +#define glVertex4sv glad_glVertex4sv +typedef void (APIENTRYP PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble *equation); +GLAPI PFNGLCLIPPLANEPROC glad_glClipPlane; +#define glClipPlane glad_glClipPlane +typedef void (APIENTRYP PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); +GLAPI PFNGLCOLORMATERIALPROC glad_glColorMaterial; +#define glColorMaterial glad_glColorMaterial +typedef void (APIENTRYP PFNGLFOGFPROC)(GLenum pname, GLfloat param); +GLAPI PFNGLFOGFPROC glad_glFogf; +#define glFogf glad_glFogf +typedef void (APIENTRYP PFNGLFOGFVPROC)(GLenum pname, const GLfloat *params); +GLAPI PFNGLFOGFVPROC glad_glFogfv; +#define glFogfv glad_glFogfv +typedef void (APIENTRYP PFNGLFOGIPROC)(GLenum pname, GLint param); +GLAPI PFNGLFOGIPROC glad_glFogi; +#define glFogi glad_glFogi +typedef void (APIENTRYP PFNGLFOGIVPROC)(GLenum pname, const GLint *params); +GLAPI PFNGLFOGIVPROC glad_glFogiv; +#define glFogiv glad_glFogiv +typedef void (APIENTRYP PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); +GLAPI PFNGLLIGHTFPROC glad_glLightf; +#define glLightf glad_glLightf +typedef void (APIENTRYP PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat *params); +GLAPI PFNGLLIGHTFVPROC glad_glLightfv; +#define glLightfv glad_glLightfv +typedef void (APIENTRYP PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); +GLAPI PFNGLLIGHTIPROC glad_glLighti; +#define glLighti glad_glLighti +typedef void (APIENTRYP PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint *params); +GLAPI PFNGLLIGHTIVPROC glad_glLightiv; +#define glLightiv glad_glLightiv +typedef void (APIENTRYP PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); +GLAPI PFNGLLIGHTMODELFPROC glad_glLightModelf; +#define glLightModelf glad_glLightModelf +typedef void (APIENTRYP PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat *params); +GLAPI PFNGLLIGHTMODELFVPROC glad_glLightModelfv; +#define glLightModelfv glad_glLightModelfv +typedef void (APIENTRYP PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); +GLAPI PFNGLLIGHTMODELIPROC glad_glLightModeli; +#define glLightModeli glad_glLightModeli +typedef void (APIENTRYP PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint *params); +GLAPI PFNGLLIGHTMODELIVPROC glad_glLightModeliv; +#define glLightModeliv glad_glLightModeliv +typedef void (APIENTRYP PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); +GLAPI PFNGLLINESTIPPLEPROC glad_glLineStipple; +#define glLineStipple glad_glLineStipple +typedef void (APIENTRYP PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); +GLAPI PFNGLMATERIALFPROC glad_glMaterialf; +#define glMaterialf glad_glMaterialf +typedef void (APIENTRYP PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat *params); +GLAPI PFNGLMATERIALFVPROC glad_glMaterialfv; +#define glMaterialfv glad_glMaterialfv +typedef void (APIENTRYP PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); +GLAPI PFNGLMATERIALIPROC glad_glMateriali; +#define glMateriali glad_glMateriali +typedef void (APIENTRYP PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint *params); +GLAPI PFNGLMATERIALIVPROC glad_glMaterialiv; +#define glMaterialiv glad_glMaterialiv +typedef void (APIENTRYP PFNGLPOLYGONSTIPPLEPROC)(const GLubyte *mask); +GLAPI PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple; +#define glPolygonStipple glad_glPolygonStipple +typedef void (APIENTRYP PFNGLSHADEMODELPROC)(GLenum mode); +GLAPI PFNGLSHADEMODELPROC glad_glShadeModel; +#define glShadeModel glad_glShadeModel +typedef void (APIENTRYP PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); +GLAPI PFNGLTEXENVFPROC glad_glTexEnvf; +#define glTexEnvf glad_glTexEnvf +typedef void (APIENTRYP PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat *params); +GLAPI PFNGLTEXENVFVPROC glad_glTexEnvfv; +#define glTexEnvfv glad_glTexEnvfv +typedef void (APIENTRYP PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); +GLAPI PFNGLTEXENVIPROC glad_glTexEnvi; +#define glTexEnvi glad_glTexEnvi +typedef void (APIENTRYP PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint *params); +GLAPI PFNGLTEXENVIVPROC glad_glTexEnviv; +#define glTexEnviv glad_glTexEnviv +typedef void (APIENTRYP PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); +GLAPI PFNGLTEXGENDPROC glad_glTexGend; +#define glTexGend glad_glTexGend +typedef void (APIENTRYP PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble *params); +GLAPI PFNGLTEXGENDVPROC glad_glTexGendv; +#define glTexGendv glad_glTexGendv +typedef void (APIENTRYP PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); +GLAPI PFNGLTEXGENFPROC glad_glTexGenf; +#define glTexGenf glad_glTexGenf +typedef void (APIENTRYP PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat *params); +GLAPI PFNGLTEXGENFVPROC glad_glTexGenfv; +#define glTexGenfv glad_glTexGenfv +typedef void (APIENTRYP PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); +GLAPI PFNGLTEXGENIPROC glad_glTexGeni; +#define glTexGeni glad_glTexGeni +typedef void (APIENTRYP PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint *params); +GLAPI PFNGLTEXGENIVPROC glad_glTexGeniv; +#define glTexGeniv glad_glTexGeniv +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat *buffer); +GLAPI PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer; +#define glFeedbackBuffer glad_glFeedbackBuffer +typedef void (APIENTRYP PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint *buffer); +GLAPI PFNGLSELECTBUFFERPROC glad_glSelectBuffer; +#define glSelectBuffer glad_glSelectBuffer +typedef GLint (APIENTRYP PFNGLRENDERMODEPROC)(GLenum mode); +GLAPI PFNGLRENDERMODEPROC glad_glRenderMode; +#define glRenderMode glad_glRenderMode +typedef void (APIENTRYP PFNGLINITNAMESPROC)(void); +GLAPI PFNGLINITNAMESPROC glad_glInitNames; +#define glInitNames glad_glInitNames +typedef void (APIENTRYP PFNGLLOADNAMEPROC)(GLuint name); +GLAPI PFNGLLOADNAMEPROC glad_glLoadName; +#define glLoadName glad_glLoadName +typedef void (APIENTRYP PFNGLPASSTHROUGHPROC)(GLfloat token); +GLAPI PFNGLPASSTHROUGHPROC glad_glPassThrough; +#define glPassThrough glad_glPassThrough +typedef void (APIENTRYP PFNGLPOPNAMEPROC)(void); +GLAPI PFNGLPOPNAMEPROC glad_glPopName; +#define glPopName glad_glPopName +typedef void (APIENTRYP PFNGLPUSHNAMEPROC)(GLuint name); +GLAPI PFNGLPUSHNAMEPROC glad_glPushName; +#define glPushName glad_glPushName +typedef void (APIENTRYP PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI PFNGLCLEARACCUMPROC glad_glClearAccum; +#define glClearAccum glad_glClearAccum +typedef void (APIENTRYP PFNGLCLEARINDEXPROC)(GLfloat c); +GLAPI PFNGLCLEARINDEXPROC glad_glClearIndex; +#define glClearIndex glad_glClearIndex +typedef void (APIENTRYP PFNGLINDEXMASKPROC)(GLuint mask); +GLAPI PFNGLINDEXMASKPROC glad_glIndexMask; +#define glIndexMask glad_glIndexMask +typedef void (APIENTRYP PFNGLACCUMPROC)(GLenum op, GLfloat value); +GLAPI PFNGLACCUMPROC glad_glAccum; +#define glAccum glad_glAccum +typedef void (APIENTRYP PFNGLPOPATTRIBPROC)(void); +GLAPI PFNGLPOPATTRIBPROC glad_glPopAttrib; +#define glPopAttrib glad_glPopAttrib +typedef void (APIENTRYP PFNGLPUSHATTRIBPROC)(GLbitfield mask); +GLAPI PFNGLPUSHATTRIBPROC glad_glPushAttrib; +#define glPushAttrib glad_glPushAttrib +typedef void (APIENTRYP PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI PFNGLMAP1DPROC glad_glMap1d; +#define glMap1d glad_glMap1d +typedef void (APIENTRYP PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI PFNGLMAP1FPROC glad_glMap1f; +#define glMap1f glad_glMap1f +typedef void (APIENTRYP PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI PFNGLMAP2DPROC glad_glMap2d; +#define glMap2d glad_glMap2d +typedef void (APIENTRYP PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +GLAPI PFNGLMAP2FPROC glad_glMap2f; +#define glMap2f glad_glMap2f +typedef void (APIENTRYP PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); +GLAPI PFNGLMAPGRID1DPROC glad_glMapGrid1d; +#define glMapGrid1d glad_glMapGrid1d +typedef void (APIENTRYP PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); +GLAPI PFNGLMAPGRID1FPROC glad_glMapGrid1f; +#define glMapGrid1f glad_glMapGrid1f +typedef void (APIENTRYP PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); +GLAPI PFNGLMAPGRID2DPROC glad_glMapGrid2d; +#define glMapGrid2d glad_glMapGrid2d +typedef void (APIENTRYP PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); +GLAPI PFNGLMAPGRID2FPROC glad_glMapGrid2f; +#define glMapGrid2f glad_glMapGrid2f +typedef void (APIENTRYP PFNGLEVALCOORD1DPROC)(GLdouble u); +GLAPI PFNGLEVALCOORD1DPROC glad_glEvalCoord1d; +#define glEvalCoord1d glad_glEvalCoord1d +typedef void (APIENTRYP PFNGLEVALCOORD1DVPROC)(const GLdouble *u); +GLAPI PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv; +#define glEvalCoord1dv glad_glEvalCoord1dv +typedef void (APIENTRYP PFNGLEVALCOORD1FPROC)(GLfloat u); +GLAPI PFNGLEVALCOORD1FPROC glad_glEvalCoord1f; +#define glEvalCoord1f glad_glEvalCoord1f +typedef void (APIENTRYP PFNGLEVALCOORD1FVPROC)(const GLfloat *u); +GLAPI PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv; +#define glEvalCoord1fv glad_glEvalCoord1fv +typedef void (APIENTRYP PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); +GLAPI PFNGLEVALCOORD2DPROC glad_glEvalCoord2d; +#define glEvalCoord2d glad_glEvalCoord2d +typedef void (APIENTRYP PFNGLEVALCOORD2DVPROC)(const GLdouble *u); +GLAPI PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv; +#define glEvalCoord2dv glad_glEvalCoord2dv +typedef void (APIENTRYP PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); +GLAPI PFNGLEVALCOORD2FPROC glad_glEvalCoord2f; +#define glEvalCoord2f glad_glEvalCoord2f +typedef void (APIENTRYP PFNGLEVALCOORD2FVPROC)(const GLfloat *u); +GLAPI PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv; +#define glEvalCoord2fv glad_glEvalCoord2fv +typedef void (APIENTRYP PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); +GLAPI PFNGLEVALMESH1PROC glad_glEvalMesh1; +#define glEvalMesh1 glad_glEvalMesh1 +typedef void (APIENTRYP PFNGLEVALPOINT1PROC)(GLint i); +GLAPI PFNGLEVALPOINT1PROC glad_glEvalPoint1; +#define glEvalPoint1 glad_glEvalPoint1 +typedef void (APIENTRYP PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); +GLAPI PFNGLEVALMESH2PROC glad_glEvalMesh2; +#define glEvalMesh2 glad_glEvalMesh2 +typedef void (APIENTRYP PFNGLEVALPOINT2PROC)(GLint i, GLint j); +GLAPI PFNGLEVALPOINT2PROC glad_glEvalPoint2; +#define glEvalPoint2 glad_glEvalPoint2 +typedef void (APIENTRYP PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); +GLAPI PFNGLALPHAFUNCPROC glad_glAlphaFunc; +#define glAlphaFunc glad_glAlphaFunc +typedef void (APIENTRYP PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); +GLAPI PFNGLPIXELZOOMPROC glad_glPixelZoom; +#define glPixelZoom glad_glPixelZoom +typedef void (APIENTRYP PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); +GLAPI PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf; +#define glPixelTransferf glad_glPixelTransferf +typedef void (APIENTRYP PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); +GLAPI PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi; +#define glPixelTransferi glad_glPixelTransferi +typedef void (APIENTRYP PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat *values); +GLAPI PFNGLPIXELMAPFVPROC glad_glPixelMapfv; +#define glPixelMapfv glad_glPixelMapfv +typedef void (APIENTRYP PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint *values); +GLAPI PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv; +#define glPixelMapuiv glad_glPixelMapuiv +typedef void (APIENTRYP PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort *values); +GLAPI PFNGLPIXELMAPUSVPROC glad_glPixelMapusv; +#define glPixelMapusv glad_glPixelMapusv +typedef void (APIENTRYP PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); +GLAPI PFNGLCOPYPIXELSPROC glad_glCopyPixels; +#define glCopyPixels glad_glCopyPixels +typedef void (APIENTRYP PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLDRAWPIXELSPROC glad_glDrawPixels; +#define glDrawPixels glad_glDrawPixels +typedef void (APIENTRYP PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble *equation); +GLAPI PFNGLGETCLIPPLANEPROC glad_glGetClipPlane; +#define glGetClipPlane glad_glGetClipPlane +typedef void (APIENTRYP PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat *params); +GLAPI PFNGLGETLIGHTFVPROC glad_glGetLightfv; +#define glGetLightfv glad_glGetLightfv +typedef void (APIENTRYP PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint *params); +GLAPI PFNGLGETLIGHTIVPROC glad_glGetLightiv; +#define glGetLightiv glad_glGetLightiv +typedef void (APIENTRYP PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble *v); +GLAPI PFNGLGETMAPDVPROC glad_glGetMapdv; +#define glGetMapdv glad_glGetMapdv +typedef void (APIENTRYP PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat *v); +GLAPI PFNGLGETMAPFVPROC glad_glGetMapfv; +#define glGetMapfv glad_glGetMapfv +typedef void (APIENTRYP PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint *v); +GLAPI PFNGLGETMAPIVPROC glad_glGetMapiv; +#define glGetMapiv glad_glGetMapiv +typedef void (APIENTRYP PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat *params); +GLAPI PFNGLGETMATERIALFVPROC glad_glGetMaterialfv; +#define glGetMaterialfv glad_glGetMaterialfv +typedef void (APIENTRYP PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint *params); +GLAPI PFNGLGETMATERIALIVPROC glad_glGetMaterialiv; +#define glGetMaterialiv glad_glGetMaterialiv +typedef void (APIENTRYP PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat *values); +GLAPI PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv; +#define glGetPixelMapfv glad_glGetPixelMapfv +typedef void (APIENTRYP PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint *values); +GLAPI PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv; +#define glGetPixelMapuiv glad_glGetPixelMapuiv +typedef void (APIENTRYP PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort *values); +GLAPI PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv; +#define glGetPixelMapusv glad_glGetPixelMapusv +typedef void (APIENTRYP PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte *mask); +GLAPI PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple; +#define glGetPolygonStipple glad_glGetPolygonStipple +typedef void (APIENTRYP PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat *params); +GLAPI PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv; +#define glGetTexEnvfv glad_glGetTexEnvfv +typedef void (APIENTRYP PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETTEXENVIVPROC glad_glGetTexEnviv; +#define glGetTexEnviv glad_glGetTexEnviv +typedef void (APIENTRYP PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble *params); +GLAPI PFNGLGETTEXGENDVPROC glad_glGetTexGendv; +#define glGetTexGendv glad_glGetTexGendv +typedef void (APIENTRYP PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat *params); +GLAPI PFNGLGETTEXGENFVPROC glad_glGetTexGenfv; +#define glGetTexGenfv glad_glGetTexGenfv +typedef void (APIENTRYP PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint *params); +GLAPI PFNGLGETTEXGENIVPROC glad_glGetTexGeniv; +#define glGetTexGeniv glad_glGetTexGeniv +typedef GLboolean (APIENTRYP PFNGLISLISTPROC)(GLuint list); +GLAPI PFNGLISLISTPROC glad_glIsList; +#define glIsList glad_glIsList +typedef void (APIENTRYP PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI PFNGLFRUSTUMPROC glad_glFrustum; +#define glFrustum glad_glFrustum +typedef void (APIENTRYP PFNGLLOADIDENTITYPROC)(void); +GLAPI PFNGLLOADIDENTITYPROC glad_glLoadIdentity; +#define glLoadIdentity glad_glLoadIdentity +typedef void (APIENTRYP PFNGLLOADMATRIXFPROC)(const GLfloat *m); +GLAPI PFNGLLOADMATRIXFPROC glad_glLoadMatrixf; +#define glLoadMatrixf glad_glLoadMatrixf +typedef void (APIENTRYP PFNGLLOADMATRIXDPROC)(const GLdouble *m); +GLAPI PFNGLLOADMATRIXDPROC glad_glLoadMatrixd; +#define glLoadMatrixd glad_glLoadMatrixd +typedef void (APIENTRYP PFNGLMATRIXMODEPROC)(GLenum mode); +GLAPI PFNGLMATRIXMODEPROC glad_glMatrixMode; +#define glMatrixMode glad_glMatrixMode +typedef void (APIENTRYP PFNGLMULTMATRIXFPROC)(const GLfloat *m); +GLAPI PFNGLMULTMATRIXFPROC glad_glMultMatrixf; +#define glMultMatrixf glad_glMultMatrixf +typedef void (APIENTRYP PFNGLMULTMATRIXDPROC)(const GLdouble *m); +GLAPI PFNGLMULTMATRIXDPROC glad_glMultMatrixd; +#define glMultMatrixd glad_glMultMatrixd +typedef void (APIENTRYP PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI PFNGLORTHOPROC glad_glOrtho; +#define glOrtho glad_glOrtho +typedef void (APIENTRYP PFNGLPOPMATRIXPROC)(void); +GLAPI PFNGLPOPMATRIXPROC glad_glPopMatrix; +#define glPopMatrix glad_glPopMatrix +typedef void (APIENTRYP PFNGLPUSHMATRIXPROC)(void); +GLAPI PFNGLPUSHMATRIXPROC glad_glPushMatrix; +#define glPushMatrix glad_glPushMatrix +typedef void (APIENTRYP PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLROTATEDPROC glad_glRotated; +#define glRotated glad_glRotated +typedef void (APIENTRYP PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLROTATEFPROC glad_glRotatef; +#define glRotatef glad_glRotatef +typedef void (APIENTRYP PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLSCALEDPROC glad_glScaled; +#define glScaled glad_glScaled +typedef void (APIENTRYP PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLSCALEFPROC glad_glScalef; +#define glScalef glad_glScalef +typedef void (APIENTRYP PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLTRANSLATEDPROC glad_glTranslated; +#define glTranslated glad_glTranslated +typedef void (APIENTRYP PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLTRANSLATEFPROC glad_glTranslatef; +#define glTranslatef glad_glTranslatef +#endif +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 +GLAPI int GLAD_GL_VERSION_1_1; +typedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); +GLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays; +#define glDrawArrays glad_glDrawArrays +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices); +GLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements; +#define glDrawElements glad_glDrawElements +typedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum pname, void **params); +GLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv; +#define glGetPointerv glad_glGetPointerv +typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); +GLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; +#define glPolygonOffset glad_glPolygonOffset +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; +#define glCopyTexImage1D glad_glCopyTexImage1D +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; +#define glCopyTexImage2D glad_glCopyTexImage2D +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; +#define glCopyTexSubImage1D glad_glCopyTexSubImage1D +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; +#define glCopyTexSubImage2D glad_glCopyTexSubImage2D +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; +#define glTexSubImage1D glad_glTexSubImage1D +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; +#define glTexSubImage2D glad_glTexSubImage2D +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); +GLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture; +#define glBindTexture glad_glBindTexture +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint *textures); +GLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures; +#define glDeleteTextures glad_glDeleteTextures +typedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei n, GLuint *textures); +GLAPI PFNGLGENTEXTURESPROC glad_glGenTextures; +#define glGenTextures glad_glGenTextures +typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint texture); +GLAPI PFNGLISTEXTUREPROC glad_glIsTexture; +#define glIsTexture glad_glIsTexture +typedef void (APIENTRYP PFNGLARRAYELEMENTPROC)(GLint i); +GLAPI PFNGLARRAYELEMENTPROC glad_glArrayElement; +#define glArrayElement glad_glArrayElement +typedef void (APIENTRYP PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLCOLORPOINTERPROC glad_glColorPointer; +#define glColorPointer glad_glColorPointer +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEPROC)(GLenum array); +GLAPI PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState; +#define glDisableClientState glad_glDisableClientState +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void *pointer); +GLAPI PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer; +#define glEdgeFlagPointer glad_glEdgeFlagPointer +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEPROC)(GLenum array); +GLAPI PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState; +#define glEnableClientState glad_glEnableClientState +typedef void (APIENTRYP PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLINDEXPOINTERPROC glad_glIndexPointer; +#define glIndexPointer glad_glIndexPointer +typedef void (APIENTRYP PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void *pointer); +GLAPI PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays; +#define glInterleavedArrays glad_glInterleavedArrays +typedef void (APIENTRYP PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLNORMALPOINTERPROC glad_glNormalPointer; +#define glNormalPointer glad_glNormalPointer +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer; +#define glTexCoordPointer glad_glTexCoordPointer +typedef void (APIENTRYP PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLVERTEXPOINTERPROC glad_glVertexPointer; +#define glVertexPointer glad_glVertexPointer +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident; +#define glAreTexturesResident glad_glAreTexturesResident +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint *textures, const GLfloat *priorities); +GLAPI PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures; +#define glPrioritizeTextures glad_glPrioritizeTextures +typedef void (APIENTRYP PFNGLINDEXUBPROC)(GLubyte c); +GLAPI PFNGLINDEXUBPROC glad_glIndexub; +#define glIndexub glad_glIndexub +typedef void (APIENTRYP PFNGLINDEXUBVPROC)(const GLubyte *c); +GLAPI PFNGLINDEXUBVPROC glad_glIndexubv; +#define glIndexubv glad_glIndexubv +typedef void (APIENTRYP PFNGLPOPCLIENTATTRIBPROC)(void); +GLAPI PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib; +#define glPopClientAttrib glad_glPopClientAttrib +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); +GLAPI PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib; +#define glPushClientAttrib glad_glPushClientAttrib +#endif +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +GLAPI int GLAD_GL_VERSION_1_2; +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; +#define glDrawRangeElements glad_glDrawRangeElements +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D; +#define glTexImage3D glad_glTexImage3D +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; +#define glTexSubImage3D glad_glTexSubImage3D +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; +#define glCopyTexSubImage3D glad_glCopyTexSubImage3D +#endif +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +GLAPI int GLAD_GL_VERSION_1_3; +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum texture); +GLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture; +#define glActiveTexture glad_glActiveTexture +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); +GLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; +#define glSampleCoverage glad_glSampleCoverage +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; +#define glCompressedTexImage3D glad_glCompressedTexImage3D +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; +#define glCompressedTexImage2D glad_glCompressedTexImage2D +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; +#define glCompressedTexImage1D glad_glCompressedTexImage1D +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; +#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; +#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; +#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void *img); +GLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; +#define glGetCompressedTexImage glad_glGetCompressedTexImage +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); +GLAPI PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture; +#define glClientActiveTexture glad_glClientActiveTexture +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); +GLAPI PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d; +#define glMultiTexCoord1d glad_glMultiTexCoord1d +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble *v); +GLAPI PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv; +#define glMultiTexCoord1dv glad_glMultiTexCoord1dv +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); +GLAPI PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f; +#define glMultiTexCoord1f glad_glMultiTexCoord1f +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat *v); +GLAPI PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv; +#define glMultiTexCoord1fv glad_glMultiTexCoord1fv +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); +GLAPI PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i; +#define glMultiTexCoord1i glad_glMultiTexCoord1i +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint *v); +GLAPI PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv; +#define glMultiTexCoord1iv glad_glMultiTexCoord1iv +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); +GLAPI PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s; +#define glMultiTexCoord1s glad_glMultiTexCoord1s +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort *v); +GLAPI PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv; +#define glMultiTexCoord1sv glad_glMultiTexCoord1sv +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); +GLAPI PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d; +#define glMultiTexCoord2d glad_glMultiTexCoord2d +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble *v); +GLAPI PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv; +#define glMultiTexCoord2dv glad_glMultiTexCoord2dv +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); +GLAPI PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f; +#define glMultiTexCoord2f glad_glMultiTexCoord2f +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat *v); +GLAPI PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv; +#define glMultiTexCoord2fv glad_glMultiTexCoord2fv +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); +GLAPI PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i; +#define glMultiTexCoord2i glad_glMultiTexCoord2i +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint *v); +GLAPI PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv; +#define glMultiTexCoord2iv glad_glMultiTexCoord2iv +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); +GLAPI PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s; +#define glMultiTexCoord2s glad_glMultiTexCoord2s +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort *v); +GLAPI PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv; +#define glMultiTexCoord2sv glad_glMultiTexCoord2sv +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d; +#define glMultiTexCoord3d glad_glMultiTexCoord3d +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble *v); +GLAPI PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv; +#define glMultiTexCoord3dv glad_glMultiTexCoord3dv +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f; +#define glMultiTexCoord3f glad_glMultiTexCoord3f +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat *v); +GLAPI PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv; +#define glMultiTexCoord3fv glad_glMultiTexCoord3fv +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); +GLAPI PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i; +#define glMultiTexCoord3i glad_glMultiTexCoord3i +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint *v); +GLAPI PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv; +#define glMultiTexCoord3iv glad_glMultiTexCoord3iv +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s; +#define glMultiTexCoord3s glad_glMultiTexCoord3s +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort *v); +GLAPI PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv; +#define glMultiTexCoord3sv glad_glMultiTexCoord3sv +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d; +#define glMultiTexCoord4d glad_glMultiTexCoord4d +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble *v); +GLAPI PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv; +#define glMultiTexCoord4dv glad_glMultiTexCoord4dv +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f; +#define glMultiTexCoord4f glad_glMultiTexCoord4f +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat *v); +GLAPI PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv; +#define glMultiTexCoord4fv glad_glMultiTexCoord4fv +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i; +#define glMultiTexCoord4i glad_glMultiTexCoord4i +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint *v); +GLAPI PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv; +#define glMultiTexCoord4iv glad_glMultiTexCoord4iv +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s; +#define glMultiTexCoord4s glad_glMultiTexCoord4s +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort *v); +GLAPI PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv; +#define glMultiTexCoord4sv glad_glMultiTexCoord4sv +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat *m); +GLAPI PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf; +#define glLoadTransposeMatrixf glad_glLoadTransposeMatrixf +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble *m); +GLAPI PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd; +#define glLoadTransposeMatrixd glad_glLoadTransposeMatrixd +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat *m); +GLAPI PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf; +#define glMultTransposeMatrixf glad_glMultTransposeMatrixf +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble *m); +GLAPI PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd; +#define glMultTransposeMatrixd glad_glMultTransposeMatrixd +#endif +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +GLAPI int GLAD_GL_VERSION_1_4; +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; +#define glBlendFuncSeparate glad_glBlendFuncSeparate +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; +#define glMultiDrawArrays glad_glMultiDrawArrays +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; +#define glMultiDrawElements glad_glMultiDrawElements +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); +GLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; +#define glPointParameterf glad_glPointParameterf +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat *params); +GLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; +#define glPointParameterfv glad_glPointParameterfv +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); +GLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; +#define glPointParameteri glad_glPointParameteri +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint *params); +GLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; +#define glPointParameteriv glad_glPointParameteriv +typedef void (APIENTRYP PFNGLFOGCOORDFPROC)(GLfloat coord); +GLAPI PFNGLFOGCOORDFPROC glad_glFogCoordf; +#define glFogCoordf glad_glFogCoordf +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC)(const GLfloat *coord); +GLAPI PFNGLFOGCOORDFVPROC glad_glFogCoordfv; +#define glFogCoordfv glad_glFogCoordfv +typedef void (APIENTRYP PFNGLFOGCOORDDPROC)(GLdouble coord); +GLAPI PFNGLFOGCOORDDPROC glad_glFogCoordd; +#define glFogCoordd glad_glFogCoordd +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC)(const GLdouble *coord); +GLAPI PFNGLFOGCOORDDVPROC glad_glFogCoorddv; +#define glFogCoorddv glad_glFogCoorddv +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer; +#define glFogCoordPointer glad_glFogCoordPointer +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); +GLAPI PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b; +#define glSecondaryColor3b glad_glSecondaryColor3b +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte *v); +GLAPI PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv; +#define glSecondaryColor3bv glad_glSecondaryColor3bv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); +GLAPI PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d; +#define glSecondaryColor3d glad_glSecondaryColor3d +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble *v); +GLAPI PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv; +#define glSecondaryColor3dv glad_glSecondaryColor3dv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); +GLAPI PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f; +#define glSecondaryColor3f glad_glSecondaryColor3f +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat *v); +GLAPI PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv; +#define glSecondaryColor3fv glad_glSecondaryColor3fv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); +GLAPI PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i; +#define glSecondaryColor3i glad_glSecondaryColor3i +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC)(const GLint *v); +GLAPI PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv; +#define glSecondaryColor3iv glad_glSecondaryColor3iv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); +GLAPI PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s; +#define glSecondaryColor3s glad_glSecondaryColor3s +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC)(const GLshort *v); +GLAPI PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv; +#define glSecondaryColor3sv glad_glSecondaryColor3sv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); +GLAPI PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub; +#define glSecondaryColor3ub glad_glSecondaryColor3ub +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte *v); +GLAPI PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv; +#define glSecondaryColor3ubv glad_glSecondaryColor3ubv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); +GLAPI PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui; +#define glSecondaryColor3ui glad_glSecondaryColor3ui +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint *v); +GLAPI PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv; +#define glSecondaryColor3uiv glad_glSecondaryColor3uiv +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); +GLAPI PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us; +#define glSecondaryColor3us glad_glSecondaryColor3us +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC)(const GLushort *v); +GLAPI PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv; +#define glSecondaryColor3usv glad_glSecondaryColor3usv +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer; +#define glSecondaryColorPointer glad_glSecondaryColorPointer +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); +GLAPI PFNGLWINDOWPOS2DPROC glad_glWindowPos2d; +#define glWindowPos2d glad_glWindowPos2d +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC)(const GLdouble *v); +GLAPI PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv; +#define glWindowPos2dv glad_glWindowPos2dv +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); +GLAPI PFNGLWINDOWPOS2FPROC glad_glWindowPos2f; +#define glWindowPos2f glad_glWindowPos2f +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC)(const GLfloat *v); +GLAPI PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv; +#define glWindowPos2fv glad_glWindowPos2fv +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); +GLAPI PFNGLWINDOWPOS2IPROC glad_glWindowPos2i; +#define glWindowPos2i glad_glWindowPos2i +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC)(const GLint *v); +GLAPI PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv; +#define glWindowPos2iv glad_glWindowPos2iv +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); +GLAPI PFNGLWINDOWPOS2SPROC glad_glWindowPos2s; +#define glWindowPos2s glad_glWindowPos2s +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC)(const GLshort *v); +GLAPI PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv; +#define glWindowPos2sv glad_glWindowPos2sv +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLWINDOWPOS3DPROC glad_glWindowPos3d; +#define glWindowPos3d glad_glWindowPos3d +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC)(const GLdouble *v); +GLAPI PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv; +#define glWindowPos3dv glad_glWindowPos3dv +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLWINDOWPOS3FPROC glad_glWindowPos3f; +#define glWindowPos3f glad_glWindowPos3f +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC)(const GLfloat *v); +GLAPI PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv; +#define glWindowPos3fv glad_glWindowPos3fv +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); +GLAPI PFNGLWINDOWPOS3IPROC glad_glWindowPos3i; +#define glWindowPos3i glad_glWindowPos3i +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC)(const GLint *v); +GLAPI PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv; +#define glWindowPos3iv glad_glWindowPos3iv +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); +GLAPI PFNGLWINDOWPOS3SPROC glad_glWindowPos3s; +#define glWindowPos3s glad_glWindowPos3s +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC)(const GLshort *v); +GLAPI PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv; +#define glWindowPos3sv glad_glWindowPos3sv +typedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI PFNGLBLENDCOLORPROC glad_glBlendColor; +#define glBlendColor glad_glBlendColor +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum mode); +GLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation; +#define glBlendEquation glad_glBlendEquation +#endif +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +GLAPI int GLAD_GL_VERSION_1_5; +typedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei n, GLuint *ids); +GLAPI PFNGLGENQUERIESPROC glad_glGenQueries; +#define glGenQueries glad_glGenQueries +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint *ids); +GLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries; +#define glDeleteQueries glad_glDeleteQueries +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint id); +GLAPI PFNGLISQUERYPROC glad_glIsQuery; +#define glIsQuery glad_glIsQuery +typedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); +GLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery; +#define glBeginQuery glad_glBeginQuery +typedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum target); +GLAPI PFNGLENDQUERYPROC glad_glEndQuery; +#define glEndQuery glad_glEndQuery +typedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv; +#define glGetQueryiv glad_glGetQueryiv +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint *params); +GLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; +#define glGetQueryObjectiv glad_glGetQueryObjectiv +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint *params); +GLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; +#define glGetQueryObjectuiv glad_glGetQueryObjectuiv +typedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); +GLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer; +#define glBindBuffer glad_glBindBuffer +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers); +GLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; +#define glDeleteBuffers glad_glDeleteBuffers +typedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers); +GLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers; +#define glGenBuffers glad_glGenBuffers +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint buffer); +GLAPI PFNGLISBUFFERPROC glad_glIsBuffer; +#define glIsBuffer glad_glIsBuffer +typedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI PFNGLBUFFERDATAPROC glad_glBufferData; +#define glBufferData glad_glBufferData +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; +#define glBufferSubData glad_glBufferSubData +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; +#define glGetBufferSubData glad_glGetBufferSubData +typedef void * (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); +GLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer; +#define glMapBuffer glad_glMapBuffer +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum target); +GLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; +#define glUnmapBuffer glad_glUnmapBuffer +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; +#define glGetBufferParameteriv glad_glGetBufferParameteriv +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void **params); +GLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; +#define glGetBufferPointerv glad_glGetBufferPointerv +#endif +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +GLAPI int GLAD_GL_VERSION_2_0; +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); +GLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; +#define glBlendEquationSeparate glad_glBlendEquationSeparate +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum *bufs); +GLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; +#define glDrawBuffers glad_glDrawBuffers +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; +#define glStencilOpSeparate glad_glStencilOpSeparate +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; +#define glStencilFuncSeparate glad_glStencilFuncSeparate +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); +GLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; +#define glStencilMaskSeparate glad_glStencilMaskSeparate +typedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); +GLAPI PFNGLATTACHSHADERPROC glad_glAttachShader; +#define glAttachShader glad_glAttachShader +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar *name); +GLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; +#define glBindAttribLocation glad_glBindAttribLocation +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint shader); +GLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader; +#define glCompileShader glad_glCompileShader +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)(void); +GLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram; +#define glCreateProgram glad_glCreateProgram +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum type); +GLAPI PFNGLCREATESHADERPROC glad_glCreateShader; +#define glCreateShader glad_glCreateShader +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint program); +GLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; +#define glDeleteProgram glad_glDeleteProgram +typedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint shader); +GLAPI PFNGLDELETESHADERPROC glad_glDeleteShader; +#define glDeleteShader glad_glDeleteShader +typedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); +GLAPI PFNGLDETACHSHADERPROC glad_glDetachShader; +#define glDetachShader glad_glDetachShader +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); +GLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; +#define glDisableVertexAttribArray glad_glDisableVertexAttribArray +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); +GLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; +#define glEnableVertexAttribArray glad_glEnableVertexAttribArray +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; +#define glGetActiveAttrib glad_glGetActiveAttrib +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; +#define glGetActiveUniform glad_glGetActiveUniform +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; +#define glGetAttachedShaders glad_glGetAttachedShaders +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar *name); +GLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; +#define glGetAttribLocation glad_glGetAttribLocation +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint *params); +GLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; +#define glGetProgramiv glad_glGetProgramiv +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; +#define glGetProgramInfoLog glad_glGetProgramInfoLog +typedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint *params); +GLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv; +#define glGetShaderiv glad_glGetShaderiv +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; +#define glGetShaderInfoLog glad_glGetShaderInfoLog +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; +#define glGetShaderSource glad_glGetShaderSource +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar *name); +GLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; +#define glGetUniformLocation glad_glGetUniformLocation +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat *params); +GLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; +#define glGetUniformfv glad_glGetUniformfv +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint *params); +GLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; +#define glGetUniformiv glad_glGetUniformiv +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble *params); +GLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; +#define glGetVertexAttribdv glad_glGetVertexAttribdv +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat *params); +GLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; +#define glGetVertexAttribfv glad_glGetVertexAttribfv +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint *params); +GLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; +#define glGetVertexAttribiv glad_glGetVertexAttribiv +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void **pointer); +GLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; +#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint program); +GLAPI PFNGLISPROGRAMPROC glad_glIsProgram; +#define glIsProgram glad_glIsProgram +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint shader); +GLAPI PFNGLISSHADERPROC glad_glIsShader; +#define glIsShader glad_glIsShader +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint program); +GLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram; +#define glLinkProgram glad_glLinkProgram +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource; +#define glShaderSource glad_glShaderSource +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint program); +GLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram; +#define glUseProgram glad_glUseProgram +typedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); +GLAPI PFNGLUNIFORM1FPROC glad_glUniform1f; +#define glUniform1f glad_glUniform1f +typedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); +GLAPI PFNGLUNIFORM2FPROC glad_glUniform2f; +#define glUniform2f glad_glUniform2f +typedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI PFNGLUNIFORM3FPROC glad_glUniform3f; +#define glUniform3f glad_glUniform3f +typedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI PFNGLUNIFORM4FPROC glad_glUniform4f; +#define glUniform4f glad_glUniform4f +typedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint location, GLint v0); +GLAPI PFNGLUNIFORM1IPROC glad_glUniform1i; +#define glUniform1i glad_glUniform1i +typedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); +GLAPI PFNGLUNIFORM2IPROC glad_glUniform2i; +#define glUniform2i glad_glUniform2i +typedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); +GLAPI PFNGLUNIFORM3IPROC glad_glUniform3i; +#define glUniform3i glad_glUniform3i +typedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI PFNGLUNIFORM4IPROC glad_glUniform4i; +#define glUniform4i glad_glUniform4i +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv; +#define glUniform1fv glad_glUniform1fv +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv; +#define glUniform2fv glad_glUniform2fv +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv; +#define glUniform3fv glad_glUniform3fv +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat *value); +GLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv; +#define glUniform4fv glad_glUniform4fv +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv; +#define glUniform1iv glad_glUniform1iv +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv; +#define glUniform2iv glad_glUniform2iv +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv; +#define glUniform3iv glad_glUniform3iv +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint *value); +GLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv; +#define glUniform4iv glad_glUniform4iv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; +#define glUniformMatrix2fv glad_glUniformMatrix2fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; +#define glUniformMatrix3fv glad_glUniformMatrix3fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; +#define glUniformMatrix4fv glad_glUniformMatrix4fv +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint program); +GLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; +#define glValidateProgram glad_glValidateProgram +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); +GLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; +#define glVertexAttrib1d glad_glVertexAttrib1d +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble *v); +GLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; +#define glVertexAttrib1dv glad_glVertexAttrib1dv +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); +GLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; +#define glVertexAttrib1f glad_glVertexAttrib1f +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat *v); +GLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; +#define glVertexAttrib1fv glad_glVertexAttrib1fv +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); +GLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; +#define glVertexAttrib1s glad_glVertexAttrib1s +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; +#define glVertexAttrib1sv glad_glVertexAttrib1sv +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); +GLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; +#define glVertexAttrib2d glad_glVertexAttrib2d +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble *v); +GLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; +#define glVertexAttrib2dv glad_glVertexAttrib2dv +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); +GLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; +#define glVertexAttrib2f glad_glVertexAttrib2f +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat *v); +GLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; +#define glVertexAttrib2fv glad_glVertexAttrib2fv +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); +GLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; +#define glVertexAttrib2s glad_glVertexAttrib2s +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; +#define glVertexAttrib2sv glad_glVertexAttrib2sv +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; +#define glVertexAttrib3d glad_glVertexAttrib3d +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble *v); +GLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; +#define glVertexAttrib3dv glad_glVertexAttrib3dv +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; +#define glVertexAttrib3f glad_glVertexAttrib3f +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat *v); +GLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; +#define glVertexAttrib3fv glad_glVertexAttrib3fv +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; +#define glVertexAttrib3s glad_glVertexAttrib3s +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; +#define glVertexAttrib3sv glad_glVertexAttrib3sv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte *v); +GLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; +#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; +#define glVertexAttrib4Niv glad_glVertexAttrib4Niv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; +#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; +#define glVertexAttrib4Nub glad_glVertexAttrib4Nub +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte *v); +GLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; +#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; +#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort *v); +GLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; +#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte *v); +GLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; +#define glVertexAttrib4bv glad_glVertexAttrib4bv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; +#define glVertexAttrib4d glad_glVertexAttrib4d +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble *v); +GLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; +#define glVertexAttrib4dv glad_glVertexAttrib4dv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; +#define glVertexAttrib4f glad_glVertexAttrib4f +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat *v); +GLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; +#define glVertexAttrib4fv glad_glVertexAttrib4fv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; +#define glVertexAttrib4iv glad_glVertexAttrib4iv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; +#define glVertexAttrib4s glad_glVertexAttrib4s +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; +#define glVertexAttrib4sv glad_glVertexAttrib4sv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte *v); +GLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; +#define glVertexAttrib4ubv glad_glVertexAttrib4ubv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; +#define glVertexAttrib4uiv glad_glVertexAttrib4uiv +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort *v); +GLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; +#define glVertexAttrib4usv glad_glVertexAttrib4usv +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; +#define glVertexAttribPointer glad_glVertexAttribPointer +#endif +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +GLAPI int GLAD_GL_VERSION_2_1; +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; +#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; +#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; +#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; +#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; +#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; +#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv +#endif +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +GLAPI int GLAD_GL_VERSION_3_0; +typedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI PFNGLCOLORMASKIPROC glad_glColorMaski; +#define glColorMaski glad_glColorMaski +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean *data); +GLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; +#define glGetBooleani_v glad_glGetBooleani_v +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint *data); +GLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; +#define glGetIntegeri_v glad_glGetIntegeri_v +typedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum target, GLuint index); +GLAPI PFNGLENABLEIPROC glad_glEnablei; +#define glEnablei glad_glEnablei +typedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum target, GLuint index); +GLAPI PFNGLDISABLEIPROC glad_glDisablei; +#define glDisablei glad_glDisablei +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum target, GLuint index); +GLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi; +#define glIsEnabledi glad_glIsEnabledi +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); +GLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; +#define glBeginTransformFeedback glad_glBeginTransformFeedback +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)(void); +GLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; +#define glEndTransformFeedback glad_glEndTransformFeedback +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; +#define glBindBufferRange glad_glBindBufferRange +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); +GLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; +#define glBindBufferBase glad_glBindBufferBase +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; +#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; +#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); +GLAPI PFNGLCLAMPCOLORPROC glad_glClampColor; +#define glClampColor glad_glClampColor +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); +GLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; +#define glBeginConditionalRender glad_glBeginConditionalRender +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)(void); +GLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; +#define glEndConditionalRender glad_glEndConditionalRender +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; +#define glVertexAttribIPointer glad_glVertexAttribIPointer +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint *params); +GLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; +#define glGetVertexAttribIiv glad_glGetVertexAttribIiv +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint *params); +GLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; +#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); +GLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; +#define glVertexAttribI1i glad_glVertexAttribI1i +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); +GLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; +#define glVertexAttribI2i glad_glVertexAttribI2i +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); +GLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; +#define glVertexAttribI3i glad_glVertexAttribI3i +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; +#define glVertexAttribI4i glad_glVertexAttribI4i +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); +GLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; +#define glVertexAttribI1ui glad_glVertexAttribI1ui +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); +GLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; +#define glVertexAttribI2ui glad_glVertexAttribI2ui +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; +#define glVertexAttribI3ui glad_glVertexAttribI3ui +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; +#define glVertexAttribI4ui glad_glVertexAttribI4ui +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; +#define glVertexAttribI1iv glad_glVertexAttribI1iv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; +#define glVertexAttribI2iv glad_glVertexAttribI2iv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; +#define glVertexAttribI3iv glad_glVertexAttribI3iv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint *v); +GLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; +#define glVertexAttribI4iv glad_glVertexAttribI4iv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; +#define glVertexAttribI1uiv glad_glVertexAttribI1uiv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; +#define glVertexAttribI2uiv glad_glVertexAttribI2uiv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; +#define glVertexAttribI3uiv glad_glVertexAttribI3uiv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint *v); +GLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; +#define glVertexAttribI4uiv glad_glVertexAttribI4uiv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte *v); +GLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; +#define glVertexAttribI4bv glad_glVertexAttribI4bv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort *v); +GLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; +#define glVertexAttribI4sv glad_glVertexAttribI4sv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte *v); +GLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; +#define glVertexAttribI4ubv glad_glVertexAttribI4ubv +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort *v); +GLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; +#define glVertexAttribI4usv glad_glVertexAttribI4usv +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint *params); +GLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; +#define glGetUniformuiv glad_glGetUniformuiv +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar *name); +GLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; +#define glBindFragDataLocation glad_glBindFragDataLocation +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar *name); +GLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; +#define glGetFragDataLocation glad_glGetFragDataLocation +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); +GLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui; +#define glUniform1ui glad_glUniform1ui +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); +GLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui; +#define glUniform2ui glad_glUniform2ui +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui; +#define glUniform3ui glad_glUniform3ui +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui; +#define glUniform4ui glad_glUniform4ui +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint *value); +GLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; +#define glUniform1uiv glad_glUniform1uiv +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint *value); +GLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; +#define glUniform2uiv glad_glUniform2uiv +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint *value); +GLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; +#define glUniform3uiv glad_glUniform3uiv +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint *value); +GLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; +#define glUniform4uiv glad_glUniform4uiv +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint *params); +GLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; +#define glTexParameterIiv glad_glTexParameterIiv +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint *params); +GLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; +#define glTexParameterIuiv glad_glTexParameterIuiv +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; +#define glGetTexParameterIiv glad_glGetTexParameterIiv +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint *params); +GLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; +#define glGetTexParameterIuiv glad_glGetTexParameterIuiv +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; +#define glClearBufferiv glad_glClearBufferiv +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; +#define glClearBufferuiv glad_glClearBufferuiv +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; +#define glClearBufferfv glad_glClearBufferfv +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; +#define glClearBufferfi glad_glClearBufferfi +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); +GLAPI PFNGLGETSTRINGIPROC glad_glGetStringi; +#define glGetStringi glad_glGetStringi +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); +GLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; +#define glIsRenderbuffer glad_glIsRenderbuffer +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); +GLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; +#define glBindRenderbuffer glad_glBindRenderbuffer +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint *renderbuffers); +GLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; +#define glDeleteRenderbuffers glad_glDeleteRenderbuffers +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint *renderbuffers); +GLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; +#define glGenRenderbuffers glad_glGenRenderbuffers +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; +#define glRenderbufferStorage glad_glRenderbufferStorage +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint *params); +GLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; +#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); +GLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; +#define glIsFramebuffer glad_glIsFramebuffer +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); +GLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; +#define glBindFramebuffer glad_glBindFramebuffer +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint *framebuffers); +GLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; +#define glDeleteFramebuffers glad_glDeleteFramebuffers +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint *framebuffers); +GLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; +#define glGenFramebuffers glad_glGenFramebuffers +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); +GLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; +#define glCheckFramebufferStatus glad_glCheckFramebufferStatus +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; +#define glFramebufferTexture1D glad_glFramebufferTexture1D +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; +#define glFramebufferTexture2D glad_glFramebufferTexture2D +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; +#define glFramebufferTexture3D glad_glFramebufferTexture3D +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; +#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; +#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum target); +GLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; +#define glGenerateMipmap glad_glGenerateMipmap +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; +#define glBlitFramebuffer glad_glBlitFramebuffer +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; +#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; +#define glFramebufferTextureLayer glad_glFramebufferTextureLayer +typedef void * (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; +#define glMapBufferRange glad_glMapBufferRange +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; +#define glFlushMappedBufferRange glad_glFlushMappedBufferRange +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint array); +GLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; +#define glBindVertexArray glad_glBindVertexArray +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint *arrays); +GLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; +#define glDeleteVertexArrays glad_glDeleteVertexArrays +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint *arrays); +GLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; +#define glGenVertexArrays glad_glGenVertexArrays +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint array); +GLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; +#define glIsVertexArray glad_glIsVertexArray +#endif +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +GLAPI int GLAD_GL_VERSION_3_1; +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; +#define glDrawArraysInstanced glad_glDrawArraysInstanced +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; +#define glDrawElementsInstanced glad_glDrawElementsInstanced +typedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); +GLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer; +#define glTexBuffer glad_glTexBuffer +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); +GLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; +#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; +#define glCopyBufferSubData glad_glCopyBufferSubData +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; +#define glGetUniformIndices glad_glGetUniformIndices +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; +#define glGetActiveUniformsiv glad_glGetActiveUniformsiv +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; +#define glGetActiveUniformName glad_glGetActiveUniformName +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar *uniformBlockName); +GLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; +#define glGetUniformBlockIndex glad_glGetUniformBlockIndex +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; +#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; +#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +GLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; +#define glUniformBlockBinding glad_glUniformBlockBinding +#endif +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245 +#define GL_DEBUG_SOURCE_API_KHR 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A +#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B +#define GL_DEBUG_TYPE_ERROR_KHR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250 +#define GL_DEBUG_TYPE_OTHER_KHR 0x8251 +#define GL_DEBUG_TYPE_MARKER_KHR 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D +#define GL_BUFFER_KHR 0x82E0 +#define GL_SHADER_KHR 0x82E1 +#define GL_PROGRAM_KHR 0x82E2 +#define GL_VERTEX_ARRAY_KHR 0x8074 +#define GL_QUERY_KHR 0x82E3 +#define GL_PROGRAM_PIPELINE_KHR 0x82E4 +#define GL_SAMPLER_KHR 0x82E6 +#define GL_MAX_LABEL_LENGTH_KHR 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147 +#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148 +#define GL_DEBUG_OUTPUT_KHR 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002 +#define GL_STACK_OVERFLOW_KHR 0x0503 +#define GL_STACK_UNDERFLOW_KHR 0x0504 +#define GL_DISPLAY_LIST 0x82E7 +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +GLAPI int GLAD_GL_KHR_debug; +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; +#define glDebugMessageControl glad_glDebugMessageControl +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; +#define glDebugMessageInsert glad_glDebugMessageInsert +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; +#define glDebugMessageCallback glad_glDebugMessageCallback +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; +#define glGetDebugMessageLog glad_glGetDebugMessageLog +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; +#define glPushDebugGroup glad_glPushDebugGroup +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; +#define glPopDebugGroup glad_glPopDebugGroup +typedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel; +#define glObjectLabel glad_glObjectLabel +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; +#define glGetObjectLabel glad_glGetObjectLabel +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; +#define glObjectPtrLabel glad_glObjectPtrLabel +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; +#define glGetObjectPtrLabel glad_glGetObjectPtrLabel +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR; +#define glDebugMessageControlKHR glad_glDebugMessageControlKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR; +#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR callback, const void *userParam); +GLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR; +#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR; +#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR; +#define glPushDebugGroupKHR glad_glPushDebugGroupKHR +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)(void); +GLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR; +#define glPopDebugGroupKHR glad_glPopDebugGroupKHR +typedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR; +#define glObjectLabelKHR glad_glObjectLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR; +#define glGetObjectLabelKHR glad_glGetObjectLabelKHR +typedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei length, const GLchar *label); +GLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR; +#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR; +#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR +typedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum pname, void **params); +GLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR; +#define glGetPointervKHR glad_glGetPointervKHR +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/glad/src/glad.c b/thirdparty/glad/src/glad.c new file mode 100644 index 0000000..dd63e1b --- /dev/null +++ b/thirdparty/glad/src/glad.c @@ -0,0 +1,1722 @@ +/* + + OpenGL loader generated by glad 0.1.36 on Sun Apr 7 23:56:48 2024. + + Language/Generator: C/C++ + Specification: gl + APIs: gl=3.1 + Profile: compatibility + Extensions: + GL_KHR_debug + Loader: True + Local files: False + Omit khrplatform: False + Reproducible: False + + Commandline: + --profile="compatibility" --api="gl=3.1" --generator="c" --spec="gl" --extensions="GL_KHR_debug" + Online: + https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.1&extensions=GL_KHR_debug +*/ + +#include +#include +#include +#include + +static void* get_proc(const char *namez); + +#if defined(_WIN32) || defined(__CYGWIN__) +#ifndef _WINDOWS_ +#undef APIENTRY +#endif +#include +static HMODULE libGL; + +typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*); +static PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; + +#ifdef _MSC_VER +#ifdef __has_include + #if __has_include() + #define HAVE_WINAPIFAMILY 1 + #endif +#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ + #define HAVE_WINAPIFAMILY 1 +#endif +#endif + +#ifdef HAVE_WINAPIFAMILY + #include + #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) + #define IS_UWP 1 + #endif +#endif + +static +int open_gl(void) { +#ifndef IS_UWP + libGL = LoadLibraryW(L"opengl32.dll"); + if(libGL != NULL) { + void (* tmp)(void); + tmp = (void(*)(void)) GetProcAddress(libGL, "wglGetProcAddress"); + gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE) tmp; + return gladGetProcAddressPtr != NULL; + } +#endif + + return 0; +} + +static +void close_gl(void) { + if(libGL != NULL) { + FreeLibrary((HMODULE) libGL); + libGL = NULL; + } +} +#else +#include +static void* libGL; + +#if !defined(__APPLE__) && !defined(__HAIKU__) +typedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*); +static PFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr; +#endif + +static +int open_gl(void) { +#ifdef __APPLE__ + static const char *NAMES[] = { + "../Frameworks/OpenGL.framework/OpenGL", + "/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/OpenGL", + "/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL" + }; +#else + static const char *NAMES[] = {"libGL.so.1", "libGL.so"}; +#endif + + unsigned int index = 0; + for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) { + libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL); + + if(libGL != NULL) { +#if defined(__APPLE__) || defined(__HAIKU__) + return 1; +#else + gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL, + "glXGetProcAddressARB"); + return gladGetProcAddressPtr != NULL; +#endif + } + } + + return 0; +} + +static +void close_gl(void) { + if(libGL != NULL) { + dlclose(libGL); + libGL = NULL; + } +} +#endif + +static +void* get_proc(const char *namez) { + void* result = NULL; + if(libGL == NULL) return NULL; + +#if !defined(__APPLE__) && !defined(__HAIKU__) + if(gladGetProcAddressPtr != NULL) { + result = gladGetProcAddressPtr(namez); + } +#endif + if(result == NULL) { +#if defined(_WIN32) || defined(__CYGWIN__) + result = (void*)GetProcAddress((HMODULE) libGL, namez); +#else + result = dlsym(libGL, namez); +#endif + } + + return result; +} + +int gladLoadGL(void) { + int status = 0; + + if(open_gl()) { + status = gladLoadGLLoader(&get_proc); + close_gl(); + } + + return status; +} + +struct gladGLversionStruct GLVersion = { 0, 0 }; + +#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) +#define _GLAD_IS_SOME_NEW_VERSION 1 +#endif + +static int max_loaded_major; +static int max_loaded_minor; + +static const char *exts = NULL; +static int num_exts_i = 0; +static char **exts_i = NULL; + +static int get_exts(void) { +#ifdef _GLAD_IS_SOME_NEW_VERSION + if(max_loaded_major < 3) { +#endif + exts = (const char *)glGetString(GL_EXTENSIONS); +#ifdef _GLAD_IS_SOME_NEW_VERSION + } else { + unsigned int index; + + num_exts_i = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts_i); + if (num_exts_i > 0) { + exts_i = (char **)malloc((size_t)num_exts_i * (sizeof *exts_i)); + } + + if (exts_i == NULL) { + return 0; + } + + for(index = 0; index < (unsigned)num_exts_i; index++) { + const char *gl_str_tmp = (const char*)glGetStringi(GL_EXTENSIONS, index); + size_t len = strlen(gl_str_tmp); + + char *local_str = (char*)malloc((len+1) * sizeof(char)); + if(local_str != NULL) { + memcpy(local_str, gl_str_tmp, (len+1) * sizeof(char)); + } + exts_i[index] = local_str; + } + } +#endif + return 1; +} + +static void free_exts(void) { + if (exts_i != NULL) { + int index; + for(index = 0; index < num_exts_i; index++) { + free((char *)exts_i[index]); + } + free((void *)exts_i); + exts_i = NULL; + } +} + +static int has_ext(const char *ext) { +#ifdef _GLAD_IS_SOME_NEW_VERSION + if(max_loaded_major < 3) { +#endif + const char *extensions; + const char *loc; + const char *terminator; + extensions = exts; + if(extensions == NULL || ext == NULL) { + return 0; + } + + while(1) { + loc = strstr(extensions, ext); + if(loc == NULL) { + return 0; + } + + terminator = loc + strlen(ext); + if((loc == extensions || *(loc - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return 1; + } + extensions = terminator; + } +#ifdef _GLAD_IS_SOME_NEW_VERSION + } else { + int index; + if(exts_i == NULL) return 0; + for(index = 0; index < num_exts_i; index++) { + const char *e = exts_i[index]; + + if(exts_i[index] != NULL && strcmp(e, ext) == 0) { + return 1; + } + } + } +#endif + + return 0; +} +int GLAD_GL_VERSION_1_0 = 0; +int GLAD_GL_VERSION_1_1 = 0; +int GLAD_GL_VERSION_1_2 = 0; +int GLAD_GL_VERSION_1_3 = 0; +int GLAD_GL_VERSION_1_4 = 0; +int GLAD_GL_VERSION_1_5 = 0; +int GLAD_GL_VERSION_2_0 = 0; +int GLAD_GL_VERSION_2_1 = 0; +int GLAD_GL_VERSION_3_0 = 0; +int GLAD_GL_VERSION_3_1 = 0; +PFNGLACCUMPROC glad_glAccum = NULL; +PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; +PFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL; +PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL; +PFNGLARRAYELEMENTPROC glad_glArrayElement = NULL; +PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; +PFNGLBEGINPROC glad_glBegin = NULL; +PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; +PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; +PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; +PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; +PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; +PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; +PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; +PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; +PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; +PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; +PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; +PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; +PFNGLBITMAPPROC glad_glBitmap = NULL; +PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; +PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; +PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; +PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; +PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; +PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; +PFNGLBUFFERDATAPROC glad_glBufferData = NULL; +PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; +PFNGLCALLLISTPROC glad_glCallList = NULL; +PFNGLCALLLISTSPROC glad_glCallLists = NULL; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; +PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; +PFNGLCLEARPROC glad_glClear = NULL; +PFNGLCLEARACCUMPROC glad_glClearAccum = NULL; +PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; +PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; +PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; +PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; +PFNGLCLEARCOLORPROC glad_glClearColor = NULL; +PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; +PFNGLCLEARINDEXPROC glad_glClearIndex = NULL; +PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; +PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL; +PFNGLCLIPPLANEPROC glad_glClipPlane = NULL; +PFNGLCOLOR3BPROC glad_glColor3b = NULL; +PFNGLCOLOR3BVPROC glad_glColor3bv = NULL; +PFNGLCOLOR3DPROC glad_glColor3d = NULL; +PFNGLCOLOR3DVPROC glad_glColor3dv = NULL; +PFNGLCOLOR3FPROC glad_glColor3f = NULL; +PFNGLCOLOR3FVPROC glad_glColor3fv = NULL; +PFNGLCOLOR3IPROC glad_glColor3i = NULL; +PFNGLCOLOR3IVPROC glad_glColor3iv = NULL; +PFNGLCOLOR3SPROC glad_glColor3s = NULL; +PFNGLCOLOR3SVPROC glad_glColor3sv = NULL; +PFNGLCOLOR3UBPROC glad_glColor3ub = NULL; +PFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL; +PFNGLCOLOR3UIPROC glad_glColor3ui = NULL; +PFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL; +PFNGLCOLOR3USPROC glad_glColor3us = NULL; +PFNGLCOLOR3USVPROC glad_glColor3usv = NULL; +PFNGLCOLOR4BPROC glad_glColor4b = NULL; +PFNGLCOLOR4BVPROC glad_glColor4bv = NULL; +PFNGLCOLOR4DPROC glad_glColor4d = NULL; +PFNGLCOLOR4DVPROC glad_glColor4dv = NULL; +PFNGLCOLOR4FPROC glad_glColor4f = NULL; +PFNGLCOLOR4FVPROC glad_glColor4fv = NULL; +PFNGLCOLOR4IPROC glad_glColor4i = NULL; +PFNGLCOLOR4IVPROC glad_glColor4iv = NULL; +PFNGLCOLOR4SPROC glad_glColor4s = NULL; +PFNGLCOLOR4SVPROC glad_glColor4sv = NULL; +PFNGLCOLOR4UBPROC glad_glColor4ub = NULL; +PFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL; +PFNGLCOLOR4UIPROC glad_glColor4ui = NULL; +PFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL; +PFNGLCOLOR4USPROC glad_glColor4us = NULL; +PFNGLCOLOR4USVPROC glad_glColor4usv = NULL; +PFNGLCOLORMASKPROC glad_glColorMask = NULL; +PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; +PFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL; +PFNGLCOLORPOINTERPROC glad_glColorPointer = NULL; +PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; +PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; +PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; +PFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL; +PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; +PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; +PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; +PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; +PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; +PFNGLCREATESHADERPROC glad_glCreateShader = NULL; +PFNGLCULLFACEPROC glad_glCullFace = NULL; +PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; +PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; +PFNGLDELETELISTSPROC glad_glDeleteLists = NULL; +PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; +PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; +PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; +PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; +PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; +PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; +PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; +PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; +PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; +PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; +PFNGLDISABLEPROC glad_glDisable = NULL; +PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; +PFNGLDISABLEIPROC glad_glDisablei = NULL; +PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; +PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; +PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; +PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; +PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; +PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; +PFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL; +PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; +PFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL; +PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL; +PFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL; +PFNGLENABLEPROC glad_glEnable = NULL; +PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL; +PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; +PFNGLENABLEIPROC glad_glEnablei = NULL; +PFNGLENDPROC glad_glEnd = NULL; +PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; +PFNGLENDLISTPROC glad_glEndList = NULL; +PFNGLENDQUERYPROC glad_glEndQuery = NULL; +PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; +PFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL; +PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL; +PFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL; +PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL; +PFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL; +PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL; +PFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL; +PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL; +PFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL; +PFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL; +PFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL; +PFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL; +PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL; +PFNGLFINISHPROC glad_glFinish = NULL; +PFNGLFLUSHPROC glad_glFlush = NULL; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; +PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL; +PFNGLFOGCOORDDPROC glad_glFogCoordd = NULL; +PFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL; +PFNGLFOGCOORDFPROC glad_glFogCoordf = NULL; +PFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL; +PFNGLFOGFPROC glad_glFogf = NULL; +PFNGLFOGFVPROC glad_glFogfv = NULL; +PFNGLFOGIPROC glad_glFogi = NULL; +PFNGLFOGIVPROC glad_glFogiv = NULL; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; +PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; +PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; +PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; +PFNGLFRONTFACEPROC glad_glFrontFace = NULL; +PFNGLFRUSTUMPROC glad_glFrustum = NULL; +PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; +PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; +PFNGLGENLISTSPROC glad_glGenLists = NULL; +PFNGLGENQUERIESPROC glad_glGenQueries = NULL; +PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; +PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; +PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; +PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; +PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; +PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; +PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; +PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; +PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; +PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; +PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; +PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; +PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; +PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; +PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; +PFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL; +PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; +PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; +PFNGLGETERRORPROC glad_glGetError = NULL; +PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; +PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; +PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; +PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; +PFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL; +PFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL; +PFNGLGETMAPDVPROC glad_glGetMapdv = NULL; +PFNGLGETMAPFVPROC glad_glGetMapfv = NULL; +PFNGLGETMAPIVPROC glad_glGetMapiv = NULL; +PFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL; +PFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL; +PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL; +PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL; +PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL; +PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; +PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL; +PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; +PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; +PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; +PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; +PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; +PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; +PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; +PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; +PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; +PFNGLGETSTRINGPROC glad_glGetString = NULL; +PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; +PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL; +PFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL; +PFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL; +PFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL; +PFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL; +PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; +PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; +PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; +PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; +PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; +PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; +PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; +PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; +PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; +PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; +PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; +PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; +PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; +PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; +PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; +PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; +PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; +PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; +PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; +PFNGLHINTPROC glad_glHint = NULL; +PFNGLINDEXMASKPROC glad_glIndexMask = NULL; +PFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL; +PFNGLINDEXDPROC glad_glIndexd = NULL; +PFNGLINDEXDVPROC glad_glIndexdv = NULL; +PFNGLINDEXFPROC glad_glIndexf = NULL; +PFNGLINDEXFVPROC glad_glIndexfv = NULL; +PFNGLINDEXIPROC glad_glIndexi = NULL; +PFNGLINDEXIVPROC glad_glIndexiv = NULL; +PFNGLINDEXSPROC glad_glIndexs = NULL; +PFNGLINDEXSVPROC glad_glIndexsv = NULL; +PFNGLINDEXUBPROC glad_glIndexub = NULL; +PFNGLINDEXUBVPROC glad_glIndexubv = NULL; +PFNGLINITNAMESPROC glad_glInitNames = NULL; +PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL; +PFNGLISBUFFERPROC glad_glIsBuffer = NULL; +PFNGLISENABLEDPROC glad_glIsEnabled = NULL; +PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; +PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; +PFNGLISLISTPROC glad_glIsList = NULL; +PFNGLISPROGRAMPROC glad_glIsProgram = NULL; +PFNGLISQUERYPROC glad_glIsQuery = NULL; +PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; +PFNGLISSHADERPROC glad_glIsShader = NULL; +PFNGLISTEXTUREPROC glad_glIsTexture = NULL; +PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; +PFNGLLIGHTMODELFPROC glad_glLightModelf = NULL; +PFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL; +PFNGLLIGHTMODELIPROC glad_glLightModeli = NULL; +PFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL; +PFNGLLIGHTFPROC glad_glLightf = NULL; +PFNGLLIGHTFVPROC glad_glLightfv = NULL; +PFNGLLIGHTIPROC glad_glLighti = NULL; +PFNGLLIGHTIVPROC glad_glLightiv = NULL; +PFNGLLINESTIPPLEPROC glad_glLineStipple = NULL; +PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; +PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; +PFNGLLISTBASEPROC glad_glListBase = NULL; +PFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL; +PFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL; +PFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL; +PFNGLLOADNAMEPROC glad_glLoadName = NULL; +PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL; +PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL; +PFNGLLOGICOPPROC glad_glLogicOp = NULL; +PFNGLMAP1DPROC glad_glMap1d = NULL; +PFNGLMAP1FPROC glad_glMap1f = NULL; +PFNGLMAP2DPROC glad_glMap2d = NULL; +PFNGLMAP2FPROC glad_glMap2f = NULL; +PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; +PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; +PFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL; +PFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL; +PFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL; +PFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL; +PFNGLMATERIALFPROC glad_glMaterialf = NULL; +PFNGLMATERIALFVPROC glad_glMaterialfv = NULL; +PFNGLMATERIALIPROC glad_glMateriali = NULL; +PFNGLMATERIALIVPROC glad_glMaterialiv = NULL; +PFNGLMATRIXMODEPROC glad_glMatrixMode = NULL; +PFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL; +PFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL; +PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL; +PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL; +PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; +PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; +PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL; +PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL; +PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL; +PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL; +PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL; +PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL; +PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL; +PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL; +PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL; +PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL; +PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL; +PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL; +PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL; +PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL; +PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL; +PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL; +PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL; +PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL; +PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL; +PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL; +PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL; +PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL; +PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL; +PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL; +PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL; +PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL; +PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL; +PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL; +PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL; +PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL; +PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL; +PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL; +PFNGLNEWLISTPROC glad_glNewList = NULL; +PFNGLNORMAL3BPROC glad_glNormal3b = NULL; +PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL; +PFNGLNORMAL3DPROC glad_glNormal3d = NULL; +PFNGLNORMAL3DVPROC glad_glNormal3dv = NULL; +PFNGLNORMAL3FPROC glad_glNormal3f = NULL; +PFNGLNORMAL3FVPROC glad_glNormal3fv = NULL; +PFNGLNORMAL3IPROC glad_glNormal3i = NULL; +PFNGLNORMAL3IVPROC glad_glNormal3iv = NULL; +PFNGLNORMAL3SPROC glad_glNormal3s = NULL; +PFNGLNORMAL3SVPROC glad_glNormal3sv = NULL; +PFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL; +PFNGLORTHOPROC glad_glOrtho = NULL; +PFNGLPASSTHROUGHPROC glad_glPassThrough = NULL; +PFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL; +PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL; +PFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL; +PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; +PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; +PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL; +PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL; +PFNGLPIXELZOOMPROC glad_glPixelZoom = NULL; +PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; +PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; +PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; +PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; +PFNGLPOINTSIZEPROC glad_glPointSize = NULL; +PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; +PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; +PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL; +PFNGLPOPATTRIBPROC glad_glPopAttrib = NULL; +PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL; +PFNGLPOPMATRIXPROC glad_glPopMatrix = NULL; +PFNGLPOPNAMEPROC glad_glPopName = NULL; +PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; +PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL; +PFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL; +PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL; +PFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL; +PFNGLPUSHNAMEPROC glad_glPushName = NULL; +PFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL; +PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL; +PFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL; +PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL; +PFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL; +PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL; +PFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL; +PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL; +PFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL; +PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL; +PFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL; +PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL; +PFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL; +PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL; +PFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL; +PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL; +PFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL; +PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL; +PFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL; +PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL; +PFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL; +PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL; +PFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL; +PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL; +PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; +PFNGLREADPIXELSPROC glad_glReadPixels = NULL; +PFNGLRECTDPROC glad_glRectd = NULL; +PFNGLRECTDVPROC glad_glRectdv = NULL; +PFNGLRECTFPROC glad_glRectf = NULL; +PFNGLRECTFVPROC glad_glRectfv = NULL; +PFNGLRECTIPROC glad_glRecti = NULL; +PFNGLRECTIVPROC glad_glRectiv = NULL; +PFNGLRECTSPROC glad_glRects = NULL; +PFNGLRECTSVPROC glad_glRectsv = NULL; +PFNGLRENDERMODEPROC glad_glRenderMode = NULL; +PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; +PFNGLROTATEDPROC glad_glRotated = NULL; +PFNGLROTATEFPROC glad_glRotatef = NULL; +PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; +PFNGLSCALEDPROC glad_glScaled = NULL; +PFNGLSCALEFPROC glad_glScalef = NULL; +PFNGLSCISSORPROC glad_glScissor = NULL; +PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL; +PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL; +PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL; +PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL; +PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL; +PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL; +PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL; +PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL; +PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL; +PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL; +PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL; +PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL; +PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL; +PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL; +PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL; +PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL; +PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL; +PFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL; +PFNGLSHADEMODELPROC glad_glShadeModel = NULL; +PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; +PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; +PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; +PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; +PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; +PFNGLSTENCILOPPROC glad_glStencilOp = NULL; +PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; +PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; +PFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL; +PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL; +PFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL; +PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL; +PFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL; +PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL; +PFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL; +PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL; +PFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL; +PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL; +PFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL; +PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL; +PFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL; +PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL; +PFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL; +PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL; +PFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL; +PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL; +PFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL; +PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL; +PFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL; +PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL; +PFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL; +PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL; +PFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL; +PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL; +PFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL; +PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL; +PFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL; +PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL; +PFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL; +PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL; +PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL; +PFNGLTEXENVFPROC glad_glTexEnvf = NULL; +PFNGLTEXENVFVPROC glad_glTexEnvfv = NULL; +PFNGLTEXENVIPROC glad_glTexEnvi = NULL; +PFNGLTEXENVIVPROC glad_glTexEnviv = NULL; +PFNGLTEXGENDPROC glad_glTexGend = NULL; +PFNGLTEXGENDVPROC glad_glTexGendv = NULL; +PFNGLTEXGENFPROC glad_glTexGenf = NULL; +PFNGLTEXGENFVPROC glad_glTexGenfv = NULL; +PFNGLTEXGENIPROC glad_glTexGeni = NULL; +PFNGLTEXGENIVPROC glad_glTexGeniv = NULL; +PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; +PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; +PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; +PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; +PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; +PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; +PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; +PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; +PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; +PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; +PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; +PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; +PFNGLTRANSLATEDPROC glad_glTranslated = NULL; +PFNGLTRANSLATEFPROC glad_glTranslatef = NULL; +PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; +PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; +PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; +PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; +PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; +PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; +PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; +PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; +PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; +PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; +PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; +PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; +PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; +PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; +PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; +PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; +PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; +PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; +PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; +PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; +PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; +PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; +PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; +PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; +PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; +PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; +PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; +PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; +PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; +PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; +PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; +PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; +PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; +PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; +PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; +PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; +PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; +PFNGLVERTEX2DPROC glad_glVertex2d = NULL; +PFNGLVERTEX2DVPROC glad_glVertex2dv = NULL; +PFNGLVERTEX2FPROC glad_glVertex2f = NULL; +PFNGLVERTEX2FVPROC glad_glVertex2fv = NULL; +PFNGLVERTEX2IPROC glad_glVertex2i = NULL; +PFNGLVERTEX2IVPROC glad_glVertex2iv = NULL; +PFNGLVERTEX2SPROC glad_glVertex2s = NULL; +PFNGLVERTEX2SVPROC glad_glVertex2sv = NULL; +PFNGLVERTEX3DPROC glad_glVertex3d = NULL; +PFNGLVERTEX3DVPROC glad_glVertex3dv = NULL; +PFNGLVERTEX3FPROC glad_glVertex3f = NULL; +PFNGLVERTEX3FVPROC glad_glVertex3fv = NULL; +PFNGLVERTEX3IPROC glad_glVertex3i = NULL; +PFNGLVERTEX3IVPROC glad_glVertex3iv = NULL; +PFNGLVERTEX3SPROC glad_glVertex3s = NULL; +PFNGLVERTEX3SVPROC glad_glVertex3sv = NULL; +PFNGLVERTEX4DPROC glad_glVertex4d = NULL; +PFNGLVERTEX4DVPROC glad_glVertex4dv = NULL; +PFNGLVERTEX4FPROC glad_glVertex4f = NULL; +PFNGLVERTEX4FVPROC glad_glVertex4fv = NULL; +PFNGLVERTEX4IPROC glad_glVertex4i = NULL; +PFNGLVERTEX4IVPROC glad_glVertex4iv = NULL; +PFNGLVERTEX4SPROC glad_glVertex4s = NULL; +PFNGLVERTEX4SVPROC glad_glVertex4sv = NULL; +PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; +PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; +PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; +PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; +PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; +PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; +PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; +PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; +PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; +PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; +PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; +PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; +PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; +PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; +PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; +PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; +PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; +PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; +PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; +PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; +PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; +PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; +PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; +PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; +PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; +PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; +PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; +PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; +PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; +PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; +PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; +PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; +PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; +PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; +PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; +PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; +PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; +PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; +PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; +PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; +PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; +PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; +PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; +PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; +PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; +PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; +PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; +PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; +PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; +PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; +PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; +PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; +PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; +PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; +PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; +PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; +PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; +PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; +PFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL; +PFNGLVIEWPORTPROC glad_glViewport = NULL; +PFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL; +PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL; +PFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL; +PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL; +PFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL; +PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL; +PFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL; +PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL; +PFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL; +PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL; +PFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL; +PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL; +PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; +PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; +PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; +PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; +int GLAD_GL_KHR_debug = 0; +PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; +PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; +PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; +PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; +PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; +PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; +PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; +PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; +PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; +PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; +PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR = NULL; +PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR = NULL; +PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR = NULL; +PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR = NULL; +PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR = NULL; +PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR = NULL; +PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR = NULL; +PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR = NULL; +PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR = NULL; +PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR = NULL; +PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR = NULL; +static void load_GL_VERSION_1_0(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_0) return; + glad_glCullFace = (PFNGLCULLFACEPROC)load("glCullFace"); + glad_glFrontFace = (PFNGLFRONTFACEPROC)load("glFrontFace"); + glad_glHint = (PFNGLHINTPROC)load("glHint"); + glad_glLineWidth = (PFNGLLINEWIDTHPROC)load("glLineWidth"); + glad_glPointSize = (PFNGLPOINTSIZEPROC)load("glPointSize"); + glad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load("glPolygonMode"); + glad_glScissor = (PFNGLSCISSORPROC)load("glScissor"); + glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load("glTexParameterf"); + glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load("glTexParameterfv"); + glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load("glTexParameteri"); + glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load("glTexParameteriv"); + glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load("glTexImage1D"); + glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load("glTexImage2D"); + glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load("glDrawBuffer"); + glad_glClear = (PFNGLCLEARPROC)load("glClear"); + glad_glClearColor = (PFNGLCLEARCOLORPROC)load("glClearColor"); + glad_glClearStencil = (PFNGLCLEARSTENCILPROC)load("glClearStencil"); + glad_glClearDepth = (PFNGLCLEARDEPTHPROC)load("glClearDepth"); + glad_glStencilMask = (PFNGLSTENCILMASKPROC)load("glStencilMask"); + glad_glColorMask = (PFNGLCOLORMASKPROC)load("glColorMask"); + glad_glDepthMask = (PFNGLDEPTHMASKPROC)load("glDepthMask"); + glad_glDisable = (PFNGLDISABLEPROC)load("glDisable"); + glad_glEnable = (PFNGLENABLEPROC)load("glEnable"); + glad_glFinish = (PFNGLFINISHPROC)load("glFinish"); + glad_glFlush = (PFNGLFLUSHPROC)load("glFlush"); + glad_glBlendFunc = (PFNGLBLENDFUNCPROC)load("glBlendFunc"); + glad_glLogicOp = (PFNGLLOGICOPPROC)load("glLogicOp"); + glad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load("glStencilFunc"); + glad_glStencilOp = (PFNGLSTENCILOPPROC)load("glStencilOp"); + glad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load("glDepthFunc"); + glad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load("glPixelStoref"); + glad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load("glPixelStorei"); + glad_glReadBuffer = (PFNGLREADBUFFERPROC)load("glReadBuffer"); + glad_glReadPixels = (PFNGLREADPIXELSPROC)load("glReadPixels"); + glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load("glGetBooleanv"); + glad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load("glGetDoublev"); + glad_glGetError = (PFNGLGETERRORPROC)load("glGetError"); + glad_glGetFloatv = (PFNGLGETFLOATVPROC)load("glGetFloatv"); + glad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load("glGetIntegerv"); + glad_glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); + glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load("glGetTexImage"); + glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load("glGetTexParameterfv"); + glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load("glGetTexParameteriv"); + glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load("glGetTexLevelParameterfv"); + glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load("glGetTexLevelParameteriv"); + glad_glIsEnabled = (PFNGLISENABLEDPROC)load("glIsEnabled"); + glad_glDepthRange = (PFNGLDEPTHRANGEPROC)load("glDepthRange"); + glad_glViewport = (PFNGLVIEWPORTPROC)load("glViewport"); + glad_glNewList = (PFNGLNEWLISTPROC)load("glNewList"); + glad_glEndList = (PFNGLENDLISTPROC)load("glEndList"); + glad_glCallList = (PFNGLCALLLISTPROC)load("glCallList"); + glad_glCallLists = (PFNGLCALLLISTSPROC)load("glCallLists"); + glad_glDeleteLists = (PFNGLDELETELISTSPROC)load("glDeleteLists"); + glad_glGenLists = (PFNGLGENLISTSPROC)load("glGenLists"); + glad_glListBase = (PFNGLLISTBASEPROC)load("glListBase"); + glad_glBegin = (PFNGLBEGINPROC)load("glBegin"); + glad_glBitmap = (PFNGLBITMAPPROC)load("glBitmap"); + glad_glColor3b = (PFNGLCOLOR3BPROC)load("glColor3b"); + glad_glColor3bv = (PFNGLCOLOR3BVPROC)load("glColor3bv"); + glad_glColor3d = (PFNGLCOLOR3DPROC)load("glColor3d"); + glad_glColor3dv = (PFNGLCOLOR3DVPROC)load("glColor3dv"); + glad_glColor3f = (PFNGLCOLOR3FPROC)load("glColor3f"); + glad_glColor3fv = (PFNGLCOLOR3FVPROC)load("glColor3fv"); + glad_glColor3i = (PFNGLCOLOR3IPROC)load("glColor3i"); + glad_glColor3iv = (PFNGLCOLOR3IVPROC)load("glColor3iv"); + glad_glColor3s = (PFNGLCOLOR3SPROC)load("glColor3s"); + glad_glColor3sv = (PFNGLCOLOR3SVPROC)load("glColor3sv"); + glad_glColor3ub = (PFNGLCOLOR3UBPROC)load("glColor3ub"); + glad_glColor3ubv = (PFNGLCOLOR3UBVPROC)load("glColor3ubv"); + glad_glColor3ui = (PFNGLCOLOR3UIPROC)load("glColor3ui"); + glad_glColor3uiv = (PFNGLCOLOR3UIVPROC)load("glColor3uiv"); + glad_glColor3us = (PFNGLCOLOR3USPROC)load("glColor3us"); + glad_glColor3usv = (PFNGLCOLOR3USVPROC)load("glColor3usv"); + glad_glColor4b = (PFNGLCOLOR4BPROC)load("glColor4b"); + glad_glColor4bv = (PFNGLCOLOR4BVPROC)load("glColor4bv"); + glad_glColor4d = (PFNGLCOLOR4DPROC)load("glColor4d"); + glad_glColor4dv = (PFNGLCOLOR4DVPROC)load("glColor4dv"); + glad_glColor4f = (PFNGLCOLOR4FPROC)load("glColor4f"); + glad_glColor4fv = (PFNGLCOLOR4FVPROC)load("glColor4fv"); + glad_glColor4i = (PFNGLCOLOR4IPROC)load("glColor4i"); + glad_glColor4iv = (PFNGLCOLOR4IVPROC)load("glColor4iv"); + glad_glColor4s = (PFNGLCOLOR4SPROC)load("glColor4s"); + glad_glColor4sv = (PFNGLCOLOR4SVPROC)load("glColor4sv"); + glad_glColor4ub = (PFNGLCOLOR4UBPROC)load("glColor4ub"); + glad_glColor4ubv = (PFNGLCOLOR4UBVPROC)load("glColor4ubv"); + glad_glColor4ui = (PFNGLCOLOR4UIPROC)load("glColor4ui"); + glad_glColor4uiv = (PFNGLCOLOR4UIVPROC)load("glColor4uiv"); + glad_glColor4us = (PFNGLCOLOR4USPROC)load("glColor4us"); + glad_glColor4usv = (PFNGLCOLOR4USVPROC)load("glColor4usv"); + glad_glEdgeFlag = (PFNGLEDGEFLAGPROC)load("glEdgeFlag"); + glad_glEdgeFlagv = (PFNGLEDGEFLAGVPROC)load("glEdgeFlagv"); + glad_glEnd = (PFNGLENDPROC)load("glEnd"); + glad_glIndexd = (PFNGLINDEXDPROC)load("glIndexd"); + glad_glIndexdv = (PFNGLINDEXDVPROC)load("glIndexdv"); + glad_glIndexf = (PFNGLINDEXFPROC)load("glIndexf"); + glad_glIndexfv = (PFNGLINDEXFVPROC)load("glIndexfv"); + glad_glIndexi = (PFNGLINDEXIPROC)load("glIndexi"); + glad_glIndexiv = (PFNGLINDEXIVPROC)load("glIndexiv"); + glad_glIndexs = (PFNGLINDEXSPROC)load("glIndexs"); + glad_glIndexsv = (PFNGLINDEXSVPROC)load("glIndexsv"); + glad_glNormal3b = (PFNGLNORMAL3BPROC)load("glNormal3b"); + glad_glNormal3bv = (PFNGLNORMAL3BVPROC)load("glNormal3bv"); + glad_glNormal3d = (PFNGLNORMAL3DPROC)load("glNormal3d"); + glad_glNormal3dv = (PFNGLNORMAL3DVPROC)load("glNormal3dv"); + glad_glNormal3f = (PFNGLNORMAL3FPROC)load("glNormal3f"); + glad_glNormal3fv = (PFNGLNORMAL3FVPROC)load("glNormal3fv"); + glad_glNormal3i = (PFNGLNORMAL3IPROC)load("glNormal3i"); + glad_glNormal3iv = (PFNGLNORMAL3IVPROC)load("glNormal3iv"); + glad_glNormal3s = (PFNGLNORMAL3SPROC)load("glNormal3s"); + glad_glNormal3sv = (PFNGLNORMAL3SVPROC)load("glNormal3sv"); + glad_glRasterPos2d = (PFNGLRASTERPOS2DPROC)load("glRasterPos2d"); + glad_glRasterPos2dv = (PFNGLRASTERPOS2DVPROC)load("glRasterPos2dv"); + glad_glRasterPos2f = (PFNGLRASTERPOS2FPROC)load("glRasterPos2f"); + glad_glRasterPos2fv = (PFNGLRASTERPOS2FVPROC)load("glRasterPos2fv"); + glad_glRasterPos2i = (PFNGLRASTERPOS2IPROC)load("glRasterPos2i"); + glad_glRasterPos2iv = (PFNGLRASTERPOS2IVPROC)load("glRasterPos2iv"); + glad_glRasterPos2s = (PFNGLRASTERPOS2SPROC)load("glRasterPos2s"); + glad_glRasterPos2sv = (PFNGLRASTERPOS2SVPROC)load("glRasterPos2sv"); + glad_glRasterPos3d = (PFNGLRASTERPOS3DPROC)load("glRasterPos3d"); + glad_glRasterPos3dv = (PFNGLRASTERPOS3DVPROC)load("glRasterPos3dv"); + glad_glRasterPos3f = (PFNGLRASTERPOS3FPROC)load("glRasterPos3f"); + glad_glRasterPos3fv = (PFNGLRASTERPOS3FVPROC)load("glRasterPos3fv"); + glad_glRasterPos3i = (PFNGLRASTERPOS3IPROC)load("glRasterPos3i"); + glad_glRasterPos3iv = (PFNGLRASTERPOS3IVPROC)load("glRasterPos3iv"); + glad_glRasterPos3s = (PFNGLRASTERPOS3SPROC)load("glRasterPos3s"); + glad_glRasterPos3sv = (PFNGLRASTERPOS3SVPROC)load("glRasterPos3sv"); + glad_glRasterPos4d = (PFNGLRASTERPOS4DPROC)load("glRasterPos4d"); + glad_glRasterPos4dv = (PFNGLRASTERPOS4DVPROC)load("glRasterPos4dv"); + glad_glRasterPos4f = (PFNGLRASTERPOS4FPROC)load("glRasterPos4f"); + glad_glRasterPos4fv = (PFNGLRASTERPOS4FVPROC)load("glRasterPos4fv"); + glad_glRasterPos4i = (PFNGLRASTERPOS4IPROC)load("glRasterPos4i"); + glad_glRasterPos4iv = (PFNGLRASTERPOS4IVPROC)load("glRasterPos4iv"); + glad_glRasterPos4s = (PFNGLRASTERPOS4SPROC)load("glRasterPos4s"); + glad_glRasterPos4sv = (PFNGLRASTERPOS4SVPROC)load("glRasterPos4sv"); + glad_glRectd = (PFNGLRECTDPROC)load("glRectd"); + glad_glRectdv = (PFNGLRECTDVPROC)load("glRectdv"); + glad_glRectf = (PFNGLRECTFPROC)load("glRectf"); + glad_glRectfv = (PFNGLRECTFVPROC)load("glRectfv"); + glad_glRecti = (PFNGLRECTIPROC)load("glRecti"); + glad_glRectiv = (PFNGLRECTIVPROC)load("glRectiv"); + glad_glRects = (PFNGLRECTSPROC)load("glRects"); + glad_glRectsv = (PFNGLRECTSVPROC)load("glRectsv"); + glad_glTexCoord1d = (PFNGLTEXCOORD1DPROC)load("glTexCoord1d"); + glad_glTexCoord1dv = (PFNGLTEXCOORD1DVPROC)load("glTexCoord1dv"); + glad_glTexCoord1f = (PFNGLTEXCOORD1FPROC)load("glTexCoord1f"); + glad_glTexCoord1fv = (PFNGLTEXCOORD1FVPROC)load("glTexCoord1fv"); + glad_glTexCoord1i = (PFNGLTEXCOORD1IPROC)load("glTexCoord1i"); + glad_glTexCoord1iv = (PFNGLTEXCOORD1IVPROC)load("glTexCoord1iv"); + glad_glTexCoord1s = (PFNGLTEXCOORD1SPROC)load("glTexCoord1s"); + glad_glTexCoord1sv = (PFNGLTEXCOORD1SVPROC)load("glTexCoord1sv"); + glad_glTexCoord2d = (PFNGLTEXCOORD2DPROC)load("glTexCoord2d"); + glad_glTexCoord2dv = (PFNGLTEXCOORD2DVPROC)load("glTexCoord2dv"); + glad_glTexCoord2f = (PFNGLTEXCOORD2FPROC)load("glTexCoord2f"); + glad_glTexCoord2fv = (PFNGLTEXCOORD2FVPROC)load("glTexCoord2fv"); + glad_glTexCoord2i = (PFNGLTEXCOORD2IPROC)load("glTexCoord2i"); + glad_glTexCoord2iv = (PFNGLTEXCOORD2IVPROC)load("glTexCoord2iv"); + glad_glTexCoord2s = (PFNGLTEXCOORD2SPROC)load("glTexCoord2s"); + glad_glTexCoord2sv = (PFNGLTEXCOORD2SVPROC)load("glTexCoord2sv"); + glad_glTexCoord3d = (PFNGLTEXCOORD3DPROC)load("glTexCoord3d"); + glad_glTexCoord3dv = (PFNGLTEXCOORD3DVPROC)load("glTexCoord3dv"); + glad_glTexCoord3f = (PFNGLTEXCOORD3FPROC)load("glTexCoord3f"); + glad_glTexCoord3fv = (PFNGLTEXCOORD3FVPROC)load("glTexCoord3fv"); + glad_glTexCoord3i = (PFNGLTEXCOORD3IPROC)load("glTexCoord3i"); + glad_glTexCoord3iv = (PFNGLTEXCOORD3IVPROC)load("glTexCoord3iv"); + glad_glTexCoord3s = (PFNGLTEXCOORD3SPROC)load("glTexCoord3s"); + glad_glTexCoord3sv = (PFNGLTEXCOORD3SVPROC)load("glTexCoord3sv"); + glad_glTexCoord4d = (PFNGLTEXCOORD4DPROC)load("glTexCoord4d"); + glad_glTexCoord4dv = (PFNGLTEXCOORD4DVPROC)load("glTexCoord4dv"); + glad_glTexCoord4f = (PFNGLTEXCOORD4FPROC)load("glTexCoord4f"); + glad_glTexCoord4fv = (PFNGLTEXCOORD4FVPROC)load("glTexCoord4fv"); + glad_glTexCoord4i = (PFNGLTEXCOORD4IPROC)load("glTexCoord4i"); + glad_glTexCoord4iv = (PFNGLTEXCOORD4IVPROC)load("glTexCoord4iv"); + glad_glTexCoord4s = (PFNGLTEXCOORD4SPROC)load("glTexCoord4s"); + glad_glTexCoord4sv = (PFNGLTEXCOORD4SVPROC)load("glTexCoord4sv"); + glad_glVertex2d = (PFNGLVERTEX2DPROC)load("glVertex2d"); + glad_glVertex2dv = (PFNGLVERTEX2DVPROC)load("glVertex2dv"); + glad_glVertex2f = (PFNGLVERTEX2FPROC)load("glVertex2f"); + glad_glVertex2fv = (PFNGLVERTEX2FVPROC)load("glVertex2fv"); + glad_glVertex2i = (PFNGLVERTEX2IPROC)load("glVertex2i"); + glad_glVertex2iv = (PFNGLVERTEX2IVPROC)load("glVertex2iv"); + glad_glVertex2s = (PFNGLVERTEX2SPROC)load("glVertex2s"); + glad_glVertex2sv = (PFNGLVERTEX2SVPROC)load("glVertex2sv"); + glad_glVertex3d = (PFNGLVERTEX3DPROC)load("glVertex3d"); + glad_glVertex3dv = (PFNGLVERTEX3DVPROC)load("glVertex3dv"); + glad_glVertex3f = (PFNGLVERTEX3FPROC)load("glVertex3f"); + glad_glVertex3fv = (PFNGLVERTEX3FVPROC)load("glVertex3fv"); + glad_glVertex3i = (PFNGLVERTEX3IPROC)load("glVertex3i"); + glad_glVertex3iv = (PFNGLVERTEX3IVPROC)load("glVertex3iv"); + glad_glVertex3s = (PFNGLVERTEX3SPROC)load("glVertex3s"); + glad_glVertex3sv = (PFNGLVERTEX3SVPROC)load("glVertex3sv"); + glad_glVertex4d = (PFNGLVERTEX4DPROC)load("glVertex4d"); + glad_glVertex4dv = (PFNGLVERTEX4DVPROC)load("glVertex4dv"); + glad_glVertex4f = (PFNGLVERTEX4FPROC)load("glVertex4f"); + glad_glVertex4fv = (PFNGLVERTEX4FVPROC)load("glVertex4fv"); + glad_glVertex4i = (PFNGLVERTEX4IPROC)load("glVertex4i"); + glad_glVertex4iv = (PFNGLVERTEX4IVPROC)load("glVertex4iv"); + glad_glVertex4s = (PFNGLVERTEX4SPROC)load("glVertex4s"); + glad_glVertex4sv = (PFNGLVERTEX4SVPROC)load("glVertex4sv"); + glad_glClipPlane = (PFNGLCLIPPLANEPROC)load("glClipPlane"); + glad_glColorMaterial = (PFNGLCOLORMATERIALPROC)load("glColorMaterial"); + glad_glFogf = (PFNGLFOGFPROC)load("glFogf"); + glad_glFogfv = (PFNGLFOGFVPROC)load("glFogfv"); + glad_glFogi = (PFNGLFOGIPROC)load("glFogi"); + glad_glFogiv = (PFNGLFOGIVPROC)load("glFogiv"); + glad_glLightf = (PFNGLLIGHTFPROC)load("glLightf"); + glad_glLightfv = (PFNGLLIGHTFVPROC)load("glLightfv"); + glad_glLighti = (PFNGLLIGHTIPROC)load("glLighti"); + glad_glLightiv = (PFNGLLIGHTIVPROC)load("glLightiv"); + glad_glLightModelf = (PFNGLLIGHTMODELFPROC)load("glLightModelf"); + glad_glLightModelfv = (PFNGLLIGHTMODELFVPROC)load("glLightModelfv"); + glad_glLightModeli = (PFNGLLIGHTMODELIPROC)load("glLightModeli"); + glad_glLightModeliv = (PFNGLLIGHTMODELIVPROC)load("glLightModeliv"); + glad_glLineStipple = (PFNGLLINESTIPPLEPROC)load("glLineStipple"); + glad_glMaterialf = (PFNGLMATERIALFPROC)load("glMaterialf"); + glad_glMaterialfv = (PFNGLMATERIALFVPROC)load("glMaterialfv"); + glad_glMateriali = (PFNGLMATERIALIPROC)load("glMateriali"); + glad_glMaterialiv = (PFNGLMATERIALIVPROC)load("glMaterialiv"); + glad_glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC)load("glPolygonStipple"); + glad_glShadeModel = (PFNGLSHADEMODELPROC)load("glShadeModel"); + glad_glTexEnvf = (PFNGLTEXENVFPROC)load("glTexEnvf"); + glad_glTexEnvfv = (PFNGLTEXENVFVPROC)load("glTexEnvfv"); + glad_glTexEnvi = (PFNGLTEXENVIPROC)load("glTexEnvi"); + glad_glTexEnviv = (PFNGLTEXENVIVPROC)load("glTexEnviv"); + glad_glTexGend = (PFNGLTEXGENDPROC)load("glTexGend"); + glad_glTexGendv = (PFNGLTEXGENDVPROC)load("glTexGendv"); + glad_glTexGenf = (PFNGLTEXGENFPROC)load("glTexGenf"); + glad_glTexGenfv = (PFNGLTEXGENFVPROC)load("glTexGenfv"); + glad_glTexGeni = (PFNGLTEXGENIPROC)load("glTexGeni"); + glad_glTexGeniv = (PFNGLTEXGENIVPROC)load("glTexGeniv"); + glad_glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC)load("glFeedbackBuffer"); + glad_glSelectBuffer = (PFNGLSELECTBUFFERPROC)load("glSelectBuffer"); + glad_glRenderMode = (PFNGLRENDERMODEPROC)load("glRenderMode"); + glad_glInitNames = (PFNGLINITNAMESPROC)load("glInitNames"); + glad_glLoadName = (PFNGLLOADNAMEPROC)load("glLoadName"); + glad_glPassThrough = (PFNGLPASSTHROUGHPROC)load("glPassThrough"); + glad_glPopName = (PFNGLPOPNAMEPROC)load("glPopName"); + glad_glPushName = (PFNGLPUSHNAMEPROC)load("glPushName"); + glad_glClearAccum = (PFNGLCLEARACCUMPROC)load("glClearAccum"); + glad_glClearIndex = (PFNGLCLEARINDEXPROC)load("glClearIndex"); + glad_glIndexMask = (PFNGLINDEXMASKPROC)load("glIndexMask"); + glad_glAccum = (PFNGLACCUMPROC)load("glAccum"); + glad_glPopAttrib = (PFNGLPOPATTRIBPROC)load("glPopAttrib"); + glad_glPushAttrib = (PFNGLPUSHATTRIBPROC)load("glPushAttrib"); + glad_glMap1d = (PFNGLMAP1DPROC)load("glMap1d"); + glad_glMap1f = (PFNGLMAP1FPROC)load("glMap1f"); + glad_glMap2d = (PFNGLMAP2DPROC)load("glMap2d"); + glad_glMap2f = (PFNGLMAP2FPROC)load("glMap2f"); + glad_glMapGrid1d = (PFNGLMAPGRID1DPROC)load("glMapGrid1d"); + glad_glMapGrid1f = (PFNGLMAPGRID1FPROC)load("glMapGrid1f"); + glad_glMapGrid2d = (PFNGLMAPGRID2DPROC)load("glMapGrid2d"); + glad_glMapGrid2f = (PFNGLMAPGRID2FPROC)load("glMapGrid2f"); + glad_glEvalCoord1d = (PFNGLEVALCOORD1DPROC)load("glEvalCoord1d"); + glad_glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC)load("glEvalCoord1dv"); + glad_glEvalCoord1f = (PFNGLEVALCOORD1FPROC)load("glEvalCoord1f"); + glad_glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC)load("glEvalCoord1fv"); + glad_glEvalCoord2d = (PFNGLEVALCOORD2DPROC)load("glEvalCoord2d"); + glad_glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC)load("glEvalCoord2dv"); + glad_glEvalCoord2f = (PFNGLEVALCOORD2FPROC)load("glEvalCoord2f"); + glad_glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC)load("glEvalCoord2fv"); + glad_glEvalMesh1 = (PFNGLEVALMESH1PROC)load("glEvalMesh1"); + glad_glEvalPoint1 = (PFNGLEVALPOINT1PROC)load("glEvalPoint1"); + glad_glEvalMesh2 = (PFNGLEVALMESH2PROC)load("glEvalMesh2"); + glad_glEvalPoint2 = (PFNGLEVALPOINT2PROC)load("glEvalPoint2"); + glad_glAlphaFunc = (PFNGLALPHAFUNCPROC)load("glAlphaFunc"); + glad_glPixelZoom = (PFNGLPIXELZOOMPROC)load("glPixelZoom"); + glad_glPixelTransferf = (PFNGLPIXELTRANSFERFPROC)load("glPixelTransferf"); + glad_glPixelTransferi = (PFNGLPIXELTRANSFERIPROC)load("glPixelTransferi"); + glad_glPixelMapfv = (PFNGLPIXELMAPFVPROC)load("glPixelMapfv"); + glad_glPixelMapuiv = (PFNGLPIXELMAPUIVPROC)load("glPixelMapuiv"); + glad_glPixelMapusv = (PFNGLPIXELMAPUSVPROC)load("glPixelMapusv"); + glad_glCopyPixels = (PFNGLCOPYPIXELSPROC)load("glCopyPixels"); + glad_glDrawPixels = (PFNGLDRAWPIXELSPROC)load("glDrawPixels"); + glad_glGetClipPlane = (PFNGLGETCLIPPLANEPROC)load("glGetClipPlane"); + glad_glGetLightfv = (PFNGLGETLIGHTFVPROC)load("glGetLightfv"); + glad_glGetLightiv = (PFNGLGETLIGHTIVPROC)load("glGetLightiv"); + glad_glGetMapdv = (PFNGLGETMAPDVPROC)load("glGetMapdv"); + glad_glGetMapfv = (PFNGLGETMAPFVPROC)load("glGetMapfv"); + glad_glGetMapiv = (PFNGLGETMAPIVPROC)load("glGetMapiv"); + glad_glGetMaterialfv = (PFNGLGETMATERIALFVPROC)load("glGetMaterialfv"); + glad_glGetMaterialiv = (PFNGLGETMATERIALIVPROC)load("glGetMaterialiv"); + glad_glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC)load("glGetPixelMapfv"); + glad_glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC)load("glGetPixelMapuiv"); + glad_glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC)load("glGetPixelMapusv"); + glad_glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC)load("glGetPolygonStipple"); + glad_glGetTexEnvfv = (PFNGLGETTEXENVFVPROC)load("glGetTexEnvfv"); + glad_glGetTexEnviv = (PFNGLGETTEXENVIVPROC)load("glGetTexEnviv"); + glad_glGetTexGendv = (PFNGLGETTEXGENDVPROC)load("glGetTexGendv"); + glad_glGetTexGenfv = (PFNGLGETTEXGENFVPROC)load("glGetTexGenfv"); + glad_glGetTexGeniv = (PFNGLGETTEXGENIVPROC)load("glGetTexGeniv"); + glad_glIsList = (PFNGLISLISTPROC)load("glIsList"); + glad_glFrustum = (PFNGLFRUSTUMPROC)load("glFrustum"); + glad_glLoadIdentity = (PFNGLLOADIDENTITYPROC)load("glLoadIdentity"); + glad_glLoadMatrixf = (PFNGLLOADMATRIXFPROC)load("glLoadMatrixf"); + glad_glLoadMatrixd = (PFNGLLOADMATRIXDPROC)load("glLoadMatrixd"); + glad_glMatrixMode = (PFNGLMATRIXMODEPROC)load("glMatrixMode"); + glad_glMultMatrixf = (PFNGLMULTMATRIXFPROC)load("glMultMatrixf"); + glad_glMultMatrixd = (PFNGLMULTMATRIXDPROC)load("glMultMatrixd"); + glad_glOrtho = (PFNGLORTHOPROC)load("glOrtho"); + glad_glPopMatrix = (PFNGLPOPMATRIXPROC)load("glPopMatrix"); + glad_glPushMatrix = (PFNGLPUSHMATRIXPROC)load("glPushMatrix"); + glad_glRotated = (PFNGLROTATEDPROC)load("glRotated"); + glad_glRotatef = (PFNGLROTATEFPROC)load("glRotatef"); + glad_glScaled = (PFNGLSCALEDPROC)load("glScaled"); + glad_glScalef = (PFNGLSCALEFPROC)load("glScalef"); + glad_glTranslated = (PFNGLTRANSLATEDPROC)load("glTranslated"); + glad_glTranslatef = (PFNGLTRANSLATEFPROC)load("glTranslatef"); +} +static void load_GL_VERSION_1_1(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_1) return; + glad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load("glDrawArrays"); + glad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load("glDrawElements"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); + glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load("glPolygonOffset"); + glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load("glCopyTexImage1D"); + glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load("glCopyTexImage2D"); + glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load("glCopyTexSubImage1D"); + glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load("glCopyTexSubImage2D"); + glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load("glTexSubImage1D"); + glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load("glTexSubImage2D"); + glad_glBindTexture = (PFNGLBINDTEXTUREPROC)load("glBindTexture"); + glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load("glDeleteTextures"); + glad_glGenTextures = (PFNGLGENTEXTURESPROC)load("glGenTextures"); + glad_glIsTexture = (PFNGLISTEXTUREPROC)load("glIsTexture"); + glad_glArrayElement = (PFNGLARRAYELEMENTPROC)load("glArrayElement"); + glad_glColorPointer = (PFNGLCOLORPOINTERPROC)load("glColorPointer"); + glad_glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC)load("glDisableClientState"); + glad_glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC)load("glEdgeFlagPointer"); + glad_glEnableClientState = (PFNGLENABLECLIENTSTATEPROC)load("glEnableClientState"); + glad_glIndexPointer = (PFNGLINDEXPOINTERPROC)load("glIndexPointer"); + glad_glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC)load("glInterleavedArrays"); + glad_glNormalPointer = (PFNGLNORMALPOINTERPROC)load("glNormalPointer"); + glad_glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC)load("glTexCoordPointer"); + glad_glVertexPointer = (PFNGLVERTEXPOINTERPROC)load("glVertexPointer"); + glad_glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC)load("glAreTexturesResident"); + glad_glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC)load("glPrioritizeTextures"); + glad_glIndexub = (PFNGLINDEXUBPROC)load("glIndexub"); + glad_glIndexubv = (PFNGLINDEXUBVPROC)load("glIndexubv"); + glad_glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC)load("glPopClientAttrib"); + glad_glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC)load("glPushClientAttrib"); +} +static void load_GL_VERSION_1_2(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_2) return; + glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load("glDrawRangeElements"); + glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load("glTexImage3D"); + glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load("glTexSubImage3D"); + glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load("glCopyTexSubImage3D"); +} +static void load_GL_VERSION_1_3(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_3) return; + glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load("glActiveTexture"); + glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load("glSampleCoverage"); + glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load("glCompressedTexImage3D"); + glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load("glCompressedTexImage2D"); + glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load("glCompressedTexImage1D"); + glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load("glCompressedTexSubImage3D"); + glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load("glCompressedTexSubImage2D"); + glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load("glCompressedTexSubImage1D"); + glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load("glGetCompressedTexImage"); + glad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC)load("glClientActiveTexture"); + glad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC)load("glMultiTexCoord1d"); + glad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC)load("glMultiTexCoord1dv"); + glad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC)load("glMultiTexCoord1f"); + glad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC)load("glMultiTexCoord1fv"); + glad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC)load("glMultiTexCoord1i"); + glad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC)load("glMultiTexCoord1iv"); + glad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC)load("glMultiTexCoord1s"); + glad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC)load("glMultiTexCoord1sv"); + glad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC)load("glMultiTexCoord2d"); + glad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC)load("glMultiTexCoord2dv"); + glad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC)load("glMultiTexCoord2f"); + glad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)load("glMultiTexCoord2fv"); + glad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC)load("glMultiTexCoord2i"); + glad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC)load("glMultiTexCoord2iv"); + glad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC)load("glMultiTexCoord2s"); + glad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC)load("glMultiTexCoord2sv"); + glad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC)load("glMultiTexCoord3d"); + glad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC)load("glMultiTexCoord3dv"); + glad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC)load("glMultiTexCoord3f"); + glad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC)load("glMultiTexCoord3fv"); + glad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC)load("glMultiTexCoord3i"); + glad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC)load("glMultiTexCoord3iv"); + glad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC)load("glMultiTexCoord3s"); + glad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC)load("glMultiTexCoord3sv"); + glad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC)load("glMultiTexCoord4d"); + glad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC)load("glMultiTexCoord4dv"); + glad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC)load("glMultiTexCoord4f"); + glad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC)load("glMultiTexCoord4fv"); + glad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC)load("glMultiTexCoord4i"); + glad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC)load("glMultiTexCoord4iv"); + glad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC)load("glMultiTexCoord4s"); + glad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC)load("glMultiTexCoord4sv"); + glad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC)load("glLoadTransposeMatrixf"); + glad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC)load("glLoadTransposeMatrixd"); + glad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC)load("glMultTransposeMatrixf"); + glad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC)load("glMultTransposeMatrixd"); +} +static void load_GL_VERSION_1_4(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_4) return; + glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load("glBlendFuncSeparate"); + glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load("glMultiDrawArrays"); + glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load("glMultiDrawElements"); + glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load("glPointParameterf"); + glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load("glPointParameterfv"); + glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load("glPointParameteri"); + glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load("glPointParameteriv"); + glad_glFogCoordf = (PFNGLFOGCOORDFPROC)load("glFogCoordf"); + glad_glFogCoordfv = (PFNGLFOGCOORDFVPROC)load("glFogCoordfv"); + glad_glFogCoordd = (PFNGLFOGCOORDDPROC)load("glFogCoordd"); + glad_glFogCoorddv = (PFNGLFOGCOORDDVPROC)load("glFogCoorddv"); + glad_glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC)load("glFogCoordPointer"); + glad_glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC)load("glSecondaryColor3b"); + glad_glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC)load("glSecondaryColor3bv"); + glad_glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC)load("glSecondaryColor3d"); + glad_glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC)load("glSecondaryColor3dv"); + glad_glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC)load("glSecondaryColor3f"); + glad_glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC)load("glSecondaryColor3fv"); + glad_glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC)load("glSecondaryColor3i"); + glad_glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC)load("glSecondaryColor3iv"); + glad_glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC)load("glSecondaryColor3s"); + glad_glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC)load("glSecondaryColor3sv"); + glad_glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC)load("glSecondaryColor3ub"); + glad_glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC)load("glSecondaryColor3ubv"); + glad_glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC)load("glSecondaryColor3ui"); + glad_glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC)load("glSecondaryColor3uiv"); + glad_glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC)load("glSecondaryColor3us"); + glad_glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC)load("glSecondaryColor3usv"); + glad_glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC)load("glSecondaryColorPointer"); + glad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC)load("glWindowPos2d"); + glad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC)load("glWindowPos2dv"); + glad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC)load("glWindowPos2f"); + glad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC)load("glWindowPos2fv"); + glad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC)load("glWindowPos2i"); + glad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC)load("glWindowPos2iv"); + glad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC)load("glWindowPos2s"); + glad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC)load("glWindowPos2sv"); + glad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC)load("glWindowPos3d"); + glad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC)load("glWindowPos3dv"); + glad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC)load("glWindowPos3f"); + glad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC)load("glWindowPos3fv"); + glad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC)load("glWindowPos3i"); + glad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC)load("glWindowPos3iv"); + glad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC)load("glWindowPos3s"); + glad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC)load("glWindowPos3sv"); + glad_glBlendColor = (PFNGLBLENDCOLORPROC)load("glBlendColor"); + glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load("glBlendEquation"); +} +static void load_GL_VERSION_1_5(GLADloadproc load) { + if(!GLAD_GL_VERSION_1_5) return; + glad_glGenQueries = (PFNGLGENQUERIESPROC)load("glGenQueries"); + glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load("glDeleteQueries"); + glad_glIsQuery = (PFNGLISQUERYPROC)load("glIsQuery"); + glad_glBeginQuery = (PFNGLBEGINQUERYPROC)load("glBeginQuery"); + glad_glEndQuery = (PFNGLENDQUERYPROC)load("glEndQuery"); + glad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load("glGetQueryiv"); + glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load("glGetQueryObjectiv"); + glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load("glGetQueryObjectuiv"); + glad_glBindBuffer = (PFNGLBINDBUFFERPROC)load("glBindBuffer"); + glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load("glDeleteBuffers"); + glad_glGenBuffers = (PFNGLGENBUFFERSPROC)load("glGenBuffers"); + glad_glIsBuffer = (PFNGLISBUFFERPROC)load("glIsBuffer"); + glad_glBufferData = (PFNGLBUFFERDATAPROC)load("glBufferData"); + glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load("glBufferSubData"); + glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load("glGetBufferSubData"); + glad_glMapBuffer = (PFNGLMAPBUFFERPROC)load("glMapBuffer"); + glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load("glUnmapBuffer"); + glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load("glGetBufferParameteriv"); + glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load("glGetBufferPointerv"); +} +static void load_GL_VERSION_2_0(GLADloadproc load) { + if(!GLAD_GL_VERSION_2_0) return; + glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load("glBlendEquationSeparate"); + glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load("glDrawBuffers"); + glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load("glStencilOpSeparate"); + glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load("glStencilFuncSeparate"); + glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load("glStencilMaskSeparate"); + glad_glAttachShader = (PFNGLATTACHSHADERPROC)load("glAttachShader"); + glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load("glBindAttribLocation"); + glad_glCompileShader = (PFNGLCOMPILESHADERPROC)load("glCompileShader"); + glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load("glCreateProgram"); + glad_glCreateShader = (PFNGLCREATESHADERPROC)load("glCreateShader"); + glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load("glDeleteProgram"); + glad_glDeleteShader = (PFNGLDELETESHADERPROC)load("glDeleteShader"); + glad_glDetachShader = (PFNGLDETACHSHADERPROC)load("glDetachShader"); + glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load("glDisableVertexAttribArray"); + glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load("glEnableVertexAttribArray"); + glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load("glGetActiveAttrib"); + glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load("glGetActiveUniform"); + glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load("glGetAttachedShaders"); + glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load("glGetAttribLocation"); + glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load("glGetProgramiv"); + glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load("glGetProgramInfoLog"); + glad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load("glGetShaderiv"); + glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load("glGetShaderInfoLog"); + glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load("glGetShaderSource"); + glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load("glGetUniformLocation"); + glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load("glGetUniformfv"); + glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load("glGetUniformiv"); + glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load("glGetVertexAttribdv"); + glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load("glGetVertexAttribfv"); + glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load("glGetVertexAttribiv"); + glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load("glGetVertexAttribPointerv"); + glad_glIsProgram = (PFNGLISPROGRAMPROC)load("glIsProgram"); + glad_glIsShader = (PFNGLISSHADERPROC)load("glIsShader"); + glad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load("glLinkProgram"); + glad_glShaderSource = (PFNGLSHADERSOURCEPROC)load("glShaderSource"); + glad_glUseProgram = (PFNGLUSEPROGRAMPROC)load("glUseProgram"); + glad_glUniform1f = (PFNGLUNIFORM1FPROC)load("glUniform1f"); + glad_glUniform2f = (PFNGLUNIFORM2FPROC)load("glUniform2f"); + glad_glUniform3f = (PFNGLUNIFORM3FPROC)load("glUniform3f"); + glad_glUniform4f = (PFNGLUNIFORM4FPROC)load("glUniform4f"); + glad_glUniform1i = (PFNGLUNIFORM1IPROC)load("glUniform1i"); + glad_glUniform2i = (PFNGLUNIFORM2IPROC)load("glUniform2i"); + glad_glUniform3i = (PFNGLUNIFORM3IPROC)load("glUniform3i"); + glad_glUniform4i = (PFNGLUNIFORM4IPROC)load("glUniform4i"); + glad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load("glUniform1fv"); + glad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load("glUniform2fv"); + glad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load("glUniform3fv"); + glad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load("glUniform4fv"); + glad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load("glUniform1iv"); + glad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load("glUniform2iv"); + glad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load("glUniform3iv"); + glad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load("glUniform4iv"); + glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load("glUniformMatrix2fv"); + glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load("glUniformMatrix3fv"); + glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load("glUniformMatrix4fv"); + glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load("glValidateProgram"); + glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load("glVertexAttrib1d"); + glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load("glVertexAttrib1dv"); + glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load("glVertexAttrib1f"); + glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load("glVertexAttrib1fv"); + glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load("glVertexAttrib1s"); + glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load("glVertexAttrib1sv"); + glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load("glVertexAttrib2d"); + glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load("glVertexAttrib2dv"); + glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load("glVertexAttrib2f"); + glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load("glVertexAttrib2fv"); + glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load("glVertexAttrib2s"); + glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load("glVertexAttrib2sv"); + glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load("glVertexAttrib3d"); + glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load("glVertexAttrib3dv"); + glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load("glVertexAttrib3f"); + glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load("glVertexAttrib3fv"); + glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load("glVertexAttrib3s"); + glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load("glVertexAttrib3sv"); + glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load("glVertexAttrib4Nbv"); + glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load("glVertexAttrib4Niv"); + glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load("glVertexAttrib4Nsv"); + glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load("glVertexAttrib4Nub"); + glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load("glVertexAttrib4Nubv"); + glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load("glVertexAttrib4Nuiv"); + glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load("glVertexAttrib4Nusv"); + glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load("glVertexAttrib4bv"); + glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load("glVertexAttrib4d"); + glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load("glVertexAttrib4dv"); + glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load("glVertexAttrib4f"); + glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load("glVertexAttrib4fv"); + glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load("glVertexAttrib4iv"); + glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load("glVertexAttrib4s"); + glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load("glVertexAttrib4sv"); + glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load("glVertexAttrib4ubv"); + glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load("glVertexAttrib4uiv"); + glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load("glVertexAttrib4usv"); + glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load("glVertexAttribPointer"); +} +static void load_GL_VERSION_2_1(GLADloadproc load) { + if(!GLAD_GL_VERSION_2_1) return; + glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load("glUniformMatrix2x3fv"); + glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load("glUniformMatrix3x2fv"); + glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load("glUniformMatrix2x4fv"); + glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load("glUniformMatrix4x2fv"); + glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load("glUniformMatrix3x4fv"); + glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load("glUniformMatrix4x3fv"); +} +static void load_GL_VERSION_3_0(GLADloadproc load) { + if(!GLAD_GL_VERSION_3_0) return; + glad_glColorMaski = (PFNGLCOLORMASKIPROC)load("glColorMaski"); + glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load("glGetBooleani_v"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); + glad_glEnablei = (PFNGLENABLEIPROC)load("glEnablei"); + glad_glDisablei = (PFNGLDISABLEIPROC)load("glDisablei"); + glad_glIsEnabledi = (PFNGLISENABLEDIPROC)load("glIsEnabledi"); + glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load("glBeginTransformFeedback"); + glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load("glEndTransformFeedback"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); + glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load("glTransformFeedbackVaryings"); + glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load("glGetTransformFeedbackVarying"); + glad_glClampColor = (PFNGLCLAMPCOLORPROC)load("glClampColor"); + glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load("glBeginConditionalRender"); + glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load("glEndConditionalRender"); + glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load("glVertexAttribIPointer"); + glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load("glGetVertexAttribIiv"); + glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load("glGetVertexAttribIuiv"); + glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load("glVertexAttribI1i"); + glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load("glVertexAttribI2i"); + glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load("glVertexAttribI3i"); + glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load("glVertexAttribI4i"); + glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load("glVertexAttribI1ui"); + glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load("glVertexAttribI2ui"); + glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load("glVertexAttribI3ui"); + glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load("glVertexAttribI4ui"); + glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load("glVertexAttribI1iv"); + glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load("glVertexAttribI2iv"); + glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load("glVertexAttribI3iv"); + glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load("glVertexAttribI4iv"); + glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load("glVertexAttribI1uiv"); + glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load("glVertexAttribI2uiv"); + glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load("glVertexAttribI3uiv"); + glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load("glVertexAttribI4uiv"); + glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load("glVertexAttribI4bv"); + glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load("glVertexAttribI4sv"); + glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load("glVertexAttribI4ubv"); + glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load("glVertexAttribI4usv"); + glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load("glGetUniformuiv"); + glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load("glBindFragDataLocation"); + glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load("glGetFragDataLocation"); + glad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load("glUniform1ui"); + glad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load("glUniform2ui"); + glad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load("glUniform3ui"); + glad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load("glUniform4ui"); + glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load("glUniform1uiv"); + glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load("glUniform2uiv"); + glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load("glUniform3uiv"); + glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load("glUniform4uiv"); + glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load("glTexParameterIiv"); + glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load("glTexParameterIuiv"); + glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load("glGetTexParameterIiv"); + glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load("glGetTexParameterIuiv"); + glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load("glClearBufferiv"); + glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load("glClearBufferuiv"); + glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load("glClearBufferfv"); + glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load("glClearBufferfi"); + glad_glGetStringi = (PFNGLGETSTRINGIPROC)load("glGetStringi"); + glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load("glIsRenderbuffer"); + glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load("glBindRenderbuffer"); + glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load("glDeleteRenderbuffers"); + glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load("glGenRenderbuffers"); + glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load("glRenderbufferStorage"); + glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load("glGetRenderbufferParameteriv"); + glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load("glIsFramebuffer"); + glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load("glBindFramebuffer"); + glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load("glDeleteFramebuffers"); + glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load("glGenFramebuffers"); + glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load("glCheckFramebufferStatus"); + glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load("glFramebufferTexture1D"); + glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load("glFramebufferTexture2D"); + glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load("glFramebufferTexture3D"); + glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load("glFramebufferRenderbuffer"); + glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load("glGetFramebufferAttachmentParameteriv"); + glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load("glGenerateMipmap"); + glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load("glBlitFramebuffer"); + glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load("glRenderbufferStorageMultisample"); + glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load("glFramebufferTextureLayer"); + glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load("glMapBufferRange"); + glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load("glFlushMappedBufferRange"); + glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load("glBindVertexArray"); + glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load("glDeleteVertexArrays"); + glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load("glGenVertexArrays"); + glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load("glIsVertexArray"); +} +static void load_GL_VERSION_3_1(GLADloadproc load) { + if(!GLAD_GL_VERSION_3_1) return; + glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load("glDrawArraysInstanced"); + glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load("glDrawElementsInstanced"); + glad_glTexBuffer = (PFNGLTEXBUFFERPROC)load("glTexBuffer"); + glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load("glPrimitiveRestartIndex"); + glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load("glCopyBufferSubData"); + glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load("glGetUniformIndices"); + glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load("glGetActiveUniformsiv"); + glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load("glGetActiveUniformName"); + glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load("glGetUniformBlockIndex"); + glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load("glGetActiveUniformBlockiv"); + glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load("glGetActiveUniformBlockName"); + glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load("glUniformBlockBinding"); + glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load("glBindBufferRange"); + glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load("glBindBufferBase"); + glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load("glGetIntegeri_v"); +} +static void load_GL_KHR_debug(GLADloadproc load) { + if(!GLAD_GL_KHR_debug) return; + glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load("glDebugMessageControl"); + glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load("glDebugMessageInsert"); + glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load("glDebugMessageCallback"); + glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load("glGetDebugMessageLog"); + glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load("glPushDebugGroup"); + glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load("glPopDebugGroup"); + glad_glObjectLabel = (PFNGLOBJECTLABELPROC)load("glObjectLabel"); + glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load("glGetObjectLabel"); + glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load("glObjectPtrLabel"); + glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load("glGetObjectPtrLabel"); + glad_glGetPointerv = (PFNGLGETPOINTERVPROC)load("glGetPointerv"); + glad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load("glDebugMessageControlKHR"); + glad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load("glDebugMessageInsertKHR"); + glad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load("glDebugMessageCallbackKHR"); + glad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load("glGetDebugMessageLogKHR"); + glad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load("glPushDebugGroupKHR"); + glad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load("glPopDebugGroupKHR"); + glad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load("glObjectLabelKHR"); + glad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load("glGetObjectLabelKHR"); + glad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load("glObjectPtrLabelKHR"); + glad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load("glGetObjectPtrLabelKHR"); + glad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load("glGetPointervKHR"); +} +static int find_extensionsGL(void) { + if (!get_exts()) return 0; + GLAD_GL_KHR_debug = has_ext("GL_KHR_debug"); + free_exts(); + return 1; +} + +static void find_coreGL(void) { + + /* Thank you @elmindreda + * https://github.com/elmindreda/greg/blob/master/templates/greg.c.in#L176 + * https://github.com/glfw/glfw/blob/master/src/context.c#L36 + */ + int i, major, minor; + + const char* version; + const char* prefixes[] = { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + NULL + }; + + version = (const char*) glGetString(GL_VERSION); + if (!version) return; + + for (i = 0; prefixes[i]; i++) { + const size_t length = strlen(prefixes[i]); + if (strncmp(version, prefixes[i], length) == 0) { + version += length; + break; + } + } + +/* PR #18 */ +#ifdef _MSC_VER + sscanf_s(version, "%d.%d", &major, &minor); +#else + sscanf(version, "%d.%d", &major, &minor); +#endif + + GLVersion.major = major; GLVersion.minor = minor; + max_loaded_major = major; max_loaded_minor = minor; + GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; + GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; + GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; + GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; + GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; + GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; + GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; + GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; + GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; + GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; + if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 1)) { + max_loaded_major = 3; + max_loaded_minor = 1; + } +} + +int gladLoadGLLoader(GLADloadproc load) { + GLVersion.major = 0; GLVersion.minor = 0; + glGetString = (PFNGLGETSTRINGPROC)load("glGetString"); + if(glGetString == NULL) return 0; + if(glGetString(GL_VERSION) == NULL) return 0; + find_coreGL(); + load_GL_VERSION_1_0(load); + load_GL_VERSION_1_1(load); + load_GL_VERSION_1_2(load); + load_GL_VERSION_1_3(load); + load_GL_VERSION_1_4(load); + load_GL_VERSION_1_5(load); + load_GL_VERSION_2_0(load); + load_GL_VERSION_2_1(load); + load_GL_VERSION_3_0(load); + load_GL_VERSION_3_1(load); + + if (!find_extensionsGL()) return 0; + load_GL_KHR_debug(load); + return GLVersion.major != 0 || GLVersion.minor != 0; +} + diff --git a/thirdparty/glm/glm/CMakeLists.txt b/thirdparty/glm/glm/CMakeLists.txt new file mode 100644 index 0000000..178d23a --- /dev/null +++ b/thirdparty/glm/glm/CMakeLists.txt @@ -0,0 +1,69 @@ +file(GLOB ROOT_SOURCE *.cpp) +file(GLOB ROOT_INLINE *.inl) +file(GLOB ROOT_HEADER *.hpp) +file(GLOB ROOT_TEXT ../*.txt) +file(GLOB ROOT_MD ../*.md) +file(GLOB ROOT_NAT ../util/glm.natvis) + +file(GLOB_RECURSE CORE_SOURCE ./detail/*.cpp) +file(GLOB_RECURSE CORE_INLINE ./detail/*.inl) +file(GLOB_RECURSE CORE_HEADER ./detail/*.hpp) + +file(GLOB_RECURSE EXT_SOURCE ./ext/*.cpp) +file(GLOB_RECURSE EXT_INLINE ./ext/*.inl) +file(GLOB_RECURSE EXT_HEADER ./ext/*.hpp) + +file(GLOB_RECURSE GTC_SOURCE ./gtc/*.cpp) +file(GLOB_RECURSE GTC_INLINE ./gtc/*.inl) +file(GLOB_RECURSE GTC_HEADER ./gtc/*.hpp) + +file(GLOB_RECURSE GTX_SOURCE ./gtx/*.cpp) +file(GLOB_RECURSE GTX_INLINE ./gtx/*.inl) +file(GLOB_RECURSE GTX_HEADER ./gtx/*.hpp) + +file(GLOB_RECURSE SIMD_SOURCE ./simd/*.cpp) +file(GLOB_RECURSE SIMD_INLINE ./simd/*.inl) +file(GLOB_RECURSE SIMD_HEADER ./simd/*.h) + +source_group("Text Files" FILES ${ROOT_TEXT} ${ROOT_MD}) +source_group("Core Files" FILES ${CORE_SOURCE}) +source_group("Core Files" FILES ${CORE_INLINE}) +source_group("Core Files" FILES ${CORE_HEADER}) +source_group("EXT Files" FILES ${EXT_SOURCE}) +source_group("EXT Files" FILES ${EXT_INLINE}) +source_group("EXT Files" FILES ${EXT_HEADER}) +source_group("GTC Files" FILES ${GTC_SOURCE}) +source_group("GTC Files" FILES ${GTC_INLINE}) +source_group("GTC Files" FILES ${GTC_HEADER}) +source_group("GTX Files" FILES ${GTX_SOURCE}) +source_group("GTX Files" FILES ${GTX_INLINE}) +source_group("GTX Files" FILES ${GTX_HEADER}) +source_group("SIMD Files" FILES ${SIMD_SOURCE}) +source_group("SIMD Files" FILES ${SIMD_INLINE}) +source_group("SIMD Files" FILES ${SIMD_HEADER}) + +add_library(glm-header-only INTERFACE) +add_library(glm::glm-header-only ALIAS glm-header-only) + +target_include_directories(glm-header-only INTERFACE + "$" + "$" +) + +if (GLM_BUILD_LIBRARY) + add_library(glm + ${ROOT_TEXT} ${ROOT_MD} ${ROOT_NAT} + ${ROOT_SOURCE} ${ROOT_INLINE} ${ROOT_HEADER} + ${CORE_SOURCE} ${CORE_INLINE} ${CORE_HEADER} + ${EXT_SOURCE} ${EXT_INLINE} ${EXT_HEADER} + ${GTC_SOURCE} ${GTC_INLINE} ${GTC_HEADER} + ${GTX_SOURCE} ${GTX_INLINE} ${GTX_HEADER} + ${SIMD_SOURCE} ${SIMD_INLINE} ${SIMD_HEADER} + ) + add_library(glm::glm ALIAS glm) + target_link_libraries(glm PUBLIC glm-header-only) +else() + add_library(glm INTERFACE) + add_library(glm::glm ALIAS glm) + target_link_libraries(glm INTERFACE glm-header-only) +endif() diff --git a/thirdparty/glm/glm/common.hpp b/thirdparty/glm/glm/common.hpp new file mode 100644 index 0000000..b59657d --- /dev/null +++ b/thirdparty/glm/glm/common.hpp @@ -0,0 +1,539 @@ +/// @ref core +/// @file glm/common.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.3 Common Functions +/// +/// @defgroup core_func_common Common functions +/// @ingroup core +/// +/// Provides GLSL common functions +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/qualifier.hpp" +#include "detail/_fixes.hpp" + +namespace glm +{ + /// @addtogroup core_func_common + /// @{ + + /// Returns x if x >= 0; otherwise, it returns -x. + /// + /// @tparam genType floating-point or signed integer; scalar or vector types. + /// + /// @see GLSL abs man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType abs(genType x); + + /// Returns x if x >= 0; otherwise, it returns -x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL abs man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec abs(vec const& x); + + /// Returns 1.0 if x > 0, 0.0 if x == 0, or -1.0 if x < 0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sign man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec sign(vec const& x); + + /// Returns a value equal to the nearest integer that is less then or equal to x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floor man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floor(vec const& x); + + /// Returns a value equal to the nearest integer to x + /// whose absolute value is not larger than the absolute value of x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL trunc man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec trunc(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// This includes the possibility that round(x) returns the + /// same value as roundEven(x) for all values of x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL round man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec round(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// A fractional part of 0.5 will round toward the nearest even + /// integer. (Both 3.5 and 4.5 for x will return 4.0.) + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL roundEven man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + /// @see New round to even technique + template + GLM_FUNC_DECL vec roundEven(vec const& x); + + /// Returns a value equal to the nearest integer + /// that is greater than or equal to x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL ceil man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec ceil(vec const& x); + + /// Return x - floor(x). + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL fract man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType fract(genType x); + + /// Return x - floor(x). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL fract man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec fract(vec const& x); + + template + GLM_FUNC_DECL genType mod(genType x, genType y); + + template + GLM_FUNC_DECL vec mod(vec const& x, T y); + + /// Modulus. Returns x - y * floor(x / y) + /// for each component in x using the floating point value y. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types, include glm/gtc/integer for integer scalar types support + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL mod man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec mod(vec const& x, vec const& y); + + /// Returns the fractional part of x and sets i to the integer + /// part (as a whole number floating point value). Both the + /// return value and the output parameter will have the same + /// sign as x. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL modf man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType modf(genType x, genType& i); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType min(genType x, genType y); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& x, T y); + + /// Returns y if y < x; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL min man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& x, vec const& y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType max(genType x, genType y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, T y); + + /// Returns y if x < y; otherwise, it returns x. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL max man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, vec const& y); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam genType Floating-point or integer; scalar or vector types. + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR genType clamp(genType x, genType minVal, genType maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec clamp(vec const& x, T minVal, T maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x + /// using the floating-point values minVal and maxVal. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL clamp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec clamp(vec const& x, vec const& minVal, vec const& maxVal); + + /// If genTypeU is a floating scalar or vector: + /// Returns x * (1.0 - a) + y * a, i.e., the linear blend of + /// x and y using the floating-point value a. + /// The value for a is not restricted to the range [0, 1]. + /// + /// If genTypeU is a boolean scalar or vector: + /// Selects which vector each returned component comes + /// from. For a component of 'a' that is false, the + /// corresponding component of 'x' is returned. For a + /// component of 'a' that is true, the corresponding + /// component of 'y' is returned. Components of 'x' and 'y' that + /// are not selected are allowed to be invalid floating point + /// values and will have no effect on the results. Thus, this + /// provides different functionality than + /// genType mix(genType x, genType y, genType(a)) + /// where a is a Boolean vector. + /// + /// @see GLSL mix man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + /// + /// @param[in] x Value to interpolate. + /// @param[in] y Value to interpolate. + /// @param[in] a Interpolant. + /// + /// @tparam genTypeT Floating point scalar or vector. + /// @tparam genTypeU Floating point or boolean scalar or vector. It can't be a vector if it is the length of genTypeT. + /// + /// @code + /// #include + /// ... + /// float a; + /// bool b; + /// glm::dvec3 e; + /// glm::dvec3 f; + /// glm::vec4 g; + /// glm::vec4 h; + /// ... + /// glm::vec4 r = glm::mix(g, h, a); // Interpolate with a floating-point scalar two vectors. + /// glm::vec4 s = glm::mix(g, h, b); // Returns g or h; + /// glm::dvec3 t = glm::mix(e, f, a); // Types of the third parameter is not required to match with the first and the second. + /// glm::vec4 u = glm::mix(g, h, r); // Interpolations can be perform per component with a vector for the last parameter. + /// @endcode + template + GLM_FUNC_DECL GLM_CONSTEXPR genTypeT mix(genTypeT x, genTypeT y, genTypeU a); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec mix(vec const& x, vec const& y, vec const& a); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec mix(vec const& x, vec const& y, U a); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0 for each component of a genType. + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType step(genType edge, genType x); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec step(T edge, vec const& x); + + /// Returns 0.0 if x < edge, otherwise it returns 1.0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL step man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec step(vec const& edge, vec const& x); + + /// Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and + /// performs smooth Hermite interpolation between 0 and 1 + /// when edge0 < x < edge1. This is useful in cases where + /// you would want a threshold function with a smooth + /// transition. This is equivalent to: + /// genType t; + /// t = clamp ((x - edge0) / (edge1 - edge0), 0, 1); + /// return t * t * (3 - 2 * t); + /// Results are undefined if edge0 >= edge1. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL smoothstep man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType smoothstep(genType edge0, genType edge1, genType x); + + template + GLM_FUNC_DECL vec smoothstep(T edge0, T edge1, vec const& x); + + template + GLM_FUNC_DECL vec smoothstep(vec const& edge0, vec const& edge1, vec const& x); + + /// Returns true if x holds a NaN (not a number) + /// representation in the underlying implementation's set of + /// floating point representations. Returns false otherwise, + /// including for implementations with no NaN + /// representations. + /// + /// /!\ When using compiler fast math, this function may fail. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL isnan man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec isnan(vec const& x); + + /// Returns true if x holds a positive infinity or negative + /// infinity representation in the underlying implementation's + /// set of floating point representations. Returns false + /// otherwise, including for implementations with no infinity + /// representations. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL isinf man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec isinf(vec const& x); + + /// Returns a signed integer value representing + /// the encoding of a floating-point value. The floating-point + /// value's bit-level representation is preserved. + /// + /// @see GLSL floatBitsToInt man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL int floatBitsToInt(float v); + + /// Returns a signed integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floatBitsToInt man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floatBitsToInt(vec const& v); + + /// Returns a unsigned integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @see GLSL floatBitsToUint man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL uint floatBitsToUint(float v); + + /// Returns a unsigned integer value representing + /// the encoding of a floating-point value. The floatingpoint + /// value's bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL floatBitsToUint man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec floatBitsToUint(vec const& v); + + /// Returns a floating-point value corresponding to a signed + /// integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @see GLSL intBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL float intBitsToFloat(int v); + + /// Returns a floating-point value corresponding to a signed + /// integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL intBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec intBitsToFloat(vec const& v); + + /// Returns a floating-point value corresponding to a + /// unsigned integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @see GLSL uintBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + GLM_FUNC_DECL float uintBitsToFloat(uint v); + + /// Returns a floating-point value corresponding to a + /// unsigned integer encoding of a floating-point value. + /// If an inf or NaN is passed in, it will not signal, and the + /// resulting floating point value is unspecified. Otherwise, + /// the bit-level representation is preserved. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL uintBitsToFloat man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL vec uintBitsToFloat(vec const& v); + + /// Computes and returns a * b + c. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL fma man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType fma(genType const& a, genType const& b, genType const& c); + + /// Splits x into a floating-point significand in the range + /// [0.5, 1.0) and an integral exponent of two, such that: + /// x = significand * exp(2, exponent) + /// + /// The significand is returned by the function and the + /// exponent is returned in the parameter exp. For a + /// floating-point value of zero, the significant and exponent + /// are both zero. For a floating-point value that is an + /// infinity or is not a number, the results are undefined. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL frexp man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType frexp(genType x, int& exp); + + template + GLM_FUNC_DECL vec frexp(vec const& v, vec& exp); + + /// Builds a floating-point number from x and the + /// corresponding integral exponent of two in exp, returning: + /// significand * exp(2, exponent) + /// + /// If this product is too large to be represented in the + /// floating-point type, the result is undefined. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL ldexp man page; + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL genType ldexp(genType const& x, int const& exp); + + template + GLM_FUNC_DECL vec ldexp(vec const& v, vec const& exp); + + /// @} +}//namespace glm + +#include "detail/func_common.inl" + diff --git a/thirdparty/glm/glm/copying.txt b/thirdparty/glm/glm/copying.txt new file mode 100644 index 0000000..779c32f --- /dev/null +++ b/thirdparty/glm/glm/copying.txt @@ -0,0 +1,54 @@ +================================================================================ +OpenGL Mathematics (GLM) +-------------------------------------------------------------------------------- +GLM is licensed under The Happy Bunny License or MIT License + +================================================================================ +The Happy Bunny License (Modified MIT License) +-------------------------------------------------------------------------------- +Copyright (c) 2005 - G-Truc Creation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +Restrictions: + By making use of the Software for military purposes, you choose to make a + Bunny unhappy. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +================================================================================ +The MIT License +-------------------------------------------------------------------------------- +Copyright (c) 2005 - G-Truc Creation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/thirdparty/glm/glm/detail/_features.hpp b/thirdparty/glm/glm/detail/_features.hpp new file mode 100644 index 0000000..b0cbe9f --- /dev/null +++ b/thirdparty/glm/glm/detail/_features.hpp @@ -0,0 +1,394 @@ +#pragma once + +// #define GLM_CXX98_EXCEPTIONS +// #define GLM_CXX98_RTTI + +// #define GLM_CXX11_RVALUE_REFERENCES +// Rvalue references - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html + +// GLM_CXX11_TRAILING_RETURN +// Rvalue references for *this - GCC not supported +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2439.htm + +// GLM_CXX11_NONSTATIC_MEMBER_INIT +// Initialization of class objects by rvalues - GCC any +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1610.html + +// GLM_CXX11_NONSTATIC_MEMBER_INIT +// Non-static data member initializers - GCC 4.7 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2756.htm + +// #define GLM_CXX11_VARIADIC_TEMPLATE +// Variadic templates - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf + +// +// Extending variadic template template parameters - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf + +// #define GLM_CXX11_GENERALIZED_INITIALIZERS +// Initializer lists - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm + +// #define GLM_CXX11_STATIC_ASSERT +// Static assertions - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1720.html + +// #define GLM_CXX11_AUTO_TYPE +// auto-typed variables - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1984.pdf + +// #define GLM_CXX11_AUTO_TYPE +// Multi-declarator auto - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1737.pdf + +// #define GLM_CXX11_AUTO_TYPE +// Removal of auto as a storage-class specifier - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2546.htm + +// #define GLM_CXX11_AUTO_TYPE +// New function declarator syntax - GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2541.htm + +// #define GLM_CXX11_LAMBDAS +// New wording for C++0x lambdas - GCC 4.5 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2927.pdf + +// #define GLM_CXX11_DECLTYPE +// Declared type of an expression - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2343.pdf + +// +// Right angle brackets - GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1757.html + +// +// Default template arguments for function templates DR226 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#226 + +// +// Solving the SFINAE problem for expressions DR339 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html + +// #define GLM_CXX11_ALIAS_TEMPLATE +// Template aliases N2258 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf + +// +// Extern templates N1987 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1987.htm + +// #define GLM_CXX11_NULLPTR +// Null pointer constant N2431 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf + +// #define GLM_CXX11_STRONG_ENUMS +// Strongly-typed enums N2347 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf + +// +// Forward declarations for enums N2764 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2764.pdf + +// +// Generalized attributes N2761 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2761.pdf + +// +// Generalized constant expressions N2235 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf + +// +// Alignment support N2341 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf + +// #define GLM_CXX11_DELEGATING_CONSTRUCTORS +// Delegating constructors N1986 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1986.pdf + +// +// Inheriting constructors N2540 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2540.htm + +// #define GLM_CXX11_EXPLICIT_CONVERSIONS +// Explicit conversion operators N2437 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf + +// +// New character types N2249 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2249.html + +// +// Unicode string literals N2442 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm + +// +// Raw string literals N2442 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2442.htm + +// +// Universal character name literals N2170 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2170.html + +// #define GLM_CXX11_USER_LITERALS +// User-defined literals N2765 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2765.pdf + +// +// Standard Layout Types N2342 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2342.htm + +// #define GLM_CXX11_DEFAULTED_FUNCTIONS +// #define GLM_CXX11_DELETED_FUNCTIONS +// Defaulted and deleted functions N2346 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm + +// +// Extended friend declarations N1791 GCC 4.7 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1791.pdf + +// +// Extending sizeof N2253 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2253.html + +// #define GLM_CXX11_INLINE_NAMESPACES +// Inline namespaces N2535 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2535.htm + +// #define GLM_CXX11_UNRESTRICTED_UNIONS +// Unrestricted unions N2544 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf + +// #define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS +// Local and unnamed types as template arguments N2657 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm + +// #define GLM_CXX11_RANGE_FOR +// Range-based for N2930 GCC 4.6 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2930.html + +// #define GLM_CXX11_OVERRIDE_CONTROL +// Explicit virtual overrides N2928 N3206 N3272 GCC 4.7 +// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2928.htm +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3206.htm +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3272.htm + +// +// Minimal support for garbage collection and reachability-based leak detection N2670 No +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2670.htm + +// #define GLM_CXX11_NOEXCEPT +// Allowing move constructors to throw [noexcept] N3050 GCC 4.6 (core language only) +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3050.html + +// +// Defining move special member functions N3053 GCC 4.6 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3053.html + +// +// Sequence points N2239 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html + +// +// Atomic operations N2427 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2239.html + +// +// Strong Compare and Exchange N2748 GCC 4.5 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html + +// +// Bidirectional Fences N2752 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2752.htm + +// +// Memory model N2429 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm + +// +// Data-dependency ordering: atomics and memory model N2664 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2664.htm + +// +// Propagating exceptions N2179 GCC 4.4 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html + +// +// Abandoning a process and at_quick_exit N2440 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2440.htm + +// +// Allow atomics use in signal handlers N2547 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2547.htm + +// +// Thread-local storage N2659 GCC 4.8 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2659.htm + +// +// Dynamic initialization and destruction with concurrency N2660 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2660.htm + +// +// __func__ predefined identifier N2340 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2340.htm + +// +// C99 preprocessor N1653 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm + +// +// long long N1811 GCC 4.3 +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1811.pdf + +// +// Extended integral types N1988 Yes +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1988.pdf + +#if(GLM_COMPILER & GLM_COMPILER_GCC) + +# define GLM_CXX11_STATIC_ASSERT + +#elif(GLM_COMPILER & GLM_COMPILER_CLANG) +# if(__has_feature(cxx_exceptions)) +# define GLM_CXX98_EXCEPTIONS +# endif + +# if(__has_feature(cxx_rtti)) +# define GLM_CXX98_RTTI +# endif + +# if(__has_feature(cxx_access_control_sfinae)) +# define GLM_CXX11_ACCESS_CONTROL_SFINAE +# endif + +# if(__has_feature(cxx_alias_templates)) +# define GLM_CXX11_ALIAS_TEMPLATE +# endif + +# if(__has_feature(cxx_alignas)) +# define GLM_CXX11_ALIGNAS +# endif + +# if(__has_feature(cxx_attributes)) +# define GLM_CXX11_ATTRIBUTES +# endif + +# if(__has_feature(cxx_constexpr)) +# define GLM_CXX11_CONSTEXPR +# endif + +# if(__has_feature(cxx_decltype)) +# define GLM_CXX11_DECLTYPE +# endif + +# if(__has_feature(cxx_default_function_template_args)) +# define GLM_CXX11_DEFAULT_FUNCTION_TEMPLATE_ARGS +# endif + +# if(__has_feature(cxx_defaulted_functions)) +# define GLM_CXX11_DEFAULTED_FUNCTIONS +# endif + +# if(__has_feature(cxx_delegating_constructors)) +# define GLM_CXX11_DELEGATING_CONSTRUCTORS +# endif + +# if(__has_feature(cxx_deleted_functions)) +# define GLM_CXX11_DELETED_FUNCTIONS +# endif + +# if(__has_feature(cxx_explicit_conversions)) +# define GLM_CXX11_EXPLICIT_CONVERSIONS +# endif + +# if(__has_feature(cxx_generalized_initializers)) +# define GLM_CXX11_GENERALIZED_INITIALIZERS +# endif + +# if(__has_feature(cxx_implicit_moves)) +# define GLM_CXX11_IMPLICIT_MOVES +# endif + +# if(__has_feature(cxx_inheriting_constructors)) +# define GLM_CXX11_INHERITING_CONSTRUCTORS +# endif + +# if(__has_feature(cxx_inline_namespaces)) +# define GLM_CXX11_INLINE_NAMESPACES +# endif + +# if(__has_feature(cxx_lambdas)) +# define GLM_CXX11_LAMBDAS +# endif + +# if(__has_feature(cxx_local_type_template_args)) +# define GLM_CXX11_LOCAL_TYPE_TEMPLATE_ARGS +# endif + +# if(__has_feature(cxx_noexcept)) +# define GLM_CXX11_NOEXCEPT +# endif + +# if(__has_feature(cxx_nonstatic_member_init)) +# define GLM_CXX11_NONSTATIC_MEMBER_INIT +# endif + +# if(__has_feature(cxx_nullptr)) +# define GLM_CXX11_NULLPTR +# endif + +# if(__has_feature(cxx_override_control)) +# define GLM_CXX11_OVERRIDE_CONTROL +# endif + +# if(__has_feature(cxx_reference_qualified_functions)) +# define GLM_CXX11_REFERENCE_QUALIFIED_FUNCTIONS +# endif + +# if(__has_feature(cxx_range_for)) +# define GLM_CXX11_RANGE_FOR +# endif + +# if(__has_feature(cxx_raw_string_literals)) +# define GLM_CXX11_RAW_STRING_LITERALS +# endif + +# if(__has_feature(cxx_rvalue_references)) +# define GLM_CXX11_RVALUE_REFERENCES +# endif + +# if(__has_feature(cxx_static_assert)) +# define GLM_CXX11_STATIC_ASSERT +# endif + +# if(__has_feature(cxx_auto_type)) +# define GLM_CXX11_AUTO_TYPE +# endif + +# if(__has_feature(cxx_strong_enums)) +# define GLM_CXX11_STRONG_ENUMS +# endif + +# if(__has_feature(cxx_trailing_return)) +# define GLM_CXX11_TRAILING_RETURN +# endif + +# if(__has_feature(cxx_unicode_literals)) +# define GLM_CXX11_UNICODE_LITERALS +# endif + +# if(__has_feature(cxx_unrestricted_unions)) +# define GLM_CXX11_UNRESTRICTED_UNIONS +# endif + +# if(__has_feature(cxx_user_literals)) +# define GLM_CXX11_USER_LITERALS +# endif + +# if(__has_feature(cxx_variadic_templates)) +# define GLM_CXX11_VARIADIC_TEMPLATES +# endif + +#endif//(GLM_COMPILER & GLM_COMPILER_CLANG) diff --git a/thirdparty/glm/glm/detail/_fixes.hpp b/thirdparty/glm/glm/detail/_fixes.hpp new file mode 100644 index 0000000..a503c7c --- /dev/null +++ b/thirdparty/glm/glm/detail/_fixes.hpp @@ -0,0 +1,27 @@ +#include + +//! Workaround for compatibility with other libraries +#ifdef max +#undef max +#endif + +//! Workaround for compatibility with other libraries +#ifdef min +#undef min +#endif + +//! Workaround for Android +#ifdef isnan +#undef isnan +#endif + +//! Workaround for Android +#ifdef isinf +#undef isinf +#endif + +//! Workaround for Chrone Native Client +#ifdef log2 +#undef log2 +#endif + diff --git a/thirdparty/glm/glm/detail/_noise.hpp b/thirdparty/glm/glm/detail/_noise.hpp new file mode 100644 index 0000000..5a874a0 --- /dev/null +++ b/thirdparty/glm/glm/detail/_noise.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include "../common.hpp" + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER T mod289(T const& x) + { + return x - floor(x * (static_cast(1.0) / static_cast(289.0))) * static_cast(289.0); + } + + template + GLM_FUNC_QUALIFIER T permute(T const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> permute(vec<2, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> permute(vec<3, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> permute(vec<4, T, Q> const& x) + { + return mod289(((x * static_cast(34)) + static_cast(1)) * x); + } + + template + GLM_FUNC_QUALIFIER T taylorInvSqrt(T const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> taylorInvSqrt(vec<2, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> taylorInvSqrt(vec<3, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> taylorInvSqrt(vec<4, T, Q> const& r) + { + return static_cast(1.79284291400159) - static_cast(0.85373472095314) * r; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> fade(vec<2, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> fade(vec<3, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> fade(vec<4, T, Q> const& t) + { + return (t * t * t) * (t * (t * static_cast(6) - static_cast(15)) + static_cast(10)); + } +}//namespace detail +}//namespace glm + diff --git a/thirdparty/glm/glm/detail/_swizzle.hpp b/thirdparty/glm/glm/detail/_swizzle.hpp new file mode 100644 index 0000000..e1f57cc --- /dev/null +++ b/thirdparty/glm/glm/detail/_swizzle.hpp @@ -0,0 +1,804 @@ +#pragma once + +namespace glm{ +namespace detail +{ + // Internal class for implementing swizzle operators + template + struct _swizzle_base0 + { + protected: + GLM_FUNC_QUALIFIER T& elem(int i){ return (reinterpret_cast(_buffer))[i]; } + GLM_FUNC_QUALIFIER T const& elem(int i) const{ return (reinterpret_cast(_buffer))[i]; } + + // Use an opaque buffer to *ensure* the compiler doesn't call a constructor. + // The size 1 buffer is assumed to aligned to the actual members so that the + // elem() + char _buffer[1]; + }; + + template + struct _swizzle_base1 : public _swizzle_base0 + { + }; + + template + struct _swizzle_base1<2, T, Q, E0,E1,-1,-2, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<2, T, Q> operator ()() const { return vec<2, T, Q>(this->elem(E0), this->elem(E1)); } + }; + + template + struct _swizzle_base1<3, T, Q, E0,E1,E2,-1, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<3, T, Q> operator ()() const { return vec<3, T, Q>(this->elem(E0), this->elem(E1), this->elem(E2)); } + }; + + template + struct _swizzle_base1<4, T, Q, E0,E1,E2,E3, Aligned> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, T, Q> operator ()() const { return vec<4, T, Q>(this->elem(E0), this->elem(E1), this->elem(E2), this->elem(E3)); } + }; + + // Internal class for implementing swizzle operators + /* + Template parameters: + + T = type of scalar values (e.g. float, double) + N = number of components in the vector (e.g. 3) + E0...3 = what index the n-th element of this swizzle refers to in the unswizzled vec + + DUPLICATE_ELEMENTS = 1 if there is a repeated element, 0 otherwise (used to specialize swizzles + containing duplicate elements so that they cannot be used as r-values). + */ + template + struct _swizzle_base2 : public _swizzle_base1::value> + { + struct op_equal + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e = t; } + }; + + struct op_minus + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e -= t; } + }; + + struct op_plus + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e += t; } + }; + + struct op_mul + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e *= t; } + }; + + struct op_div + { + GLM_FUNC_QUALIFIER void operator() (T& e, T& t) const{ e /= t; } + }; + + public: + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (const T& t) + { + for (int i = 0; i < N; ++i) + (*this)[i] = t; + return *this; + } + + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (vec const& that) + { + _apply_op(that, op_equal()); + return *this; + } + + GLM_FUNC_QUALIFIER void operator -= (vec const& that) + { + _apply_op(that, op_minus()); + } + + GLM_FUNC_QUALIFIER void operator += (vec const& that) + { + _apply_op(that, op_plus()); + } + + GLM_FUNC_QUALIFIER void operator *= (vec const& that) + { + _apply_op(that, op_mul()); + } + + GLM_FUNC_QUALIFIER void operator /= (vec const& that) + { + _apply_op(that, op_div()); + } + + GLM_FUNC_QUALIFIER T& operator[](int i) + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + GLM_FUNC_QUALIFIER T operator[](int i) const + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + + protected: + template + GLM_FUNC_QUALIFIER void _apply_op(vec const& that, const U& op) + { + // Make a copy of the data in this == &that. + // The copier should optimize out the copy in cases where the function is + // properly inlined and the copy is not necessary. + T t[N]; + for (int i = 0; i < N; ++i) + t[i] = that[i]; + for (int i = 0; i < N; ++i) + op( (*this)[i], t[i] ); + } + }; + + // Specialization for swizzles containing duplicate elements. These cannot be modified. + template + struct _swizzle_base2 : public _swizzle_base1::value> + { + struct Stub {}; + + GLM_FUNC_QUALIFIER _swizzle_base2& operator= (Stub const&) { return *this; } + + GLM_FUNC_QUALIFIER T operator[] (int i) const + { + const int offset_dst[4] = { E0, E1, E2, E3 }; + return this->elem(offset_dst[i]); + } + }; + + template + struct _swizzle : public _swizzle_base2 + { + typedef _swizzle_base2 base_type; + + using base_type::operator=; + + GLM_FUNC_QUALIFIER operator vec () const { return (*this)(); } + }; + +// +// To prevent the C++ syntax from getting entirely overwhelming, define some alias macros +// +#define GLM_SWIZZLE_TEMPLATE1 template +#define GLM_SWIZZLE_TEMPLATE2 template +#define GLM_SWIZZLE_TYPE1 _swizzle +#define GLM_SWIZZLE_TYPE2 _swizzle + +// +// Wrapper for a binary operator (e.g. u.yy + v.zy) +// +#define GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b) \ + { \ + return a() OPERAND b(); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const vec& b) \ + { \ + return a() OPERAND b; \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const vec& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return a OPERAND b(); \ + } + +// +// Wrapper for a operand between a swizzle and a binary (e.g. 1.0f - u.xyz) +// +#define GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(OPERAND) \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const GLM_SWIZZLE_TYPE1& a, const T& b) \ + { \ + return a() OPERAND b; \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER vec operator OPERAND ( const T& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return a OPERAND b(); \ + } + +// +// Macro for wrapping a function taking one argument (e.g. abs()) +// +#define GLM_SWIZZLE_FUNCTION_1_ARGS(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a) \ + { \ + return FUNCTION(a()); \ + } + +// +// Macro for wrapping a function taking two vector arguments (e.g. dot()). +// +#define GLM_SWIZZLE_FUNCTION_2_ARGS(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b) \ + { \ + return FUNCTION(a(), b()); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return FUNCTION(a(), b()); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const typename V& b) \ + { \ + return FUNCTION(a(), b); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const V& a, const GLM_SWIZZLE_TYPE1& b) \ + { \ + return FUNCTION(a, b()); \ + } + +// +// Macro for wrapping a function take 2 vec arguments followed by a scalar (e.g. mix()). +// +#define GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(RETURN_TYPE,FUNCTION) \ + GLM_SWIZZLE_TEMPLATE2 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE2& b, const T& c) \ + { \ + return FUNCTION(a(), b(), c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const GLM_SWIZZLE_TYPE1& b, const T& c) \ + { \ + return FUNCTION(a(), b(), c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const GLM_SWIZZLE_TYPE1& a, const typename S0::vec_type& b, const T& c)\ + { \ + return FUNCTION(a(), b, c); \ + } \ + GLM_SWIZZLE_TEMPLATE1 \ + GLM_FUNC_QUALIFIER typename GLM_SWIZZLE_TYPE1::RETURN_TYPE FUNCTION(const typename V& a, const GLM_SWIZZLE_TYPE1& b, const T& c) \ + { \ + return FUNCTION(a, b(), c); \ + } + +}//namespace detail +}//namespace glm + +namespace glm +{ + namespace detail + { + GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(-) + GLM_SWIZZLE_SCALAR_BINARY_OPERATOR_IMPLEMENTATION(*) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(+) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(-) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(*) + GLM_SWIZZLE_VECTOR_BINARY_OPERATOR_IMPLEMENTATION(/) + } + + // + // Swizzles are distinct types from the unswizzled type. The below macros will + // provide template specializations for the swizzle types for the given functions + // so that the compiler does not have any ambiguity to choosing how to handle + // the function. + // + // The alternative is to use the operator()() when calling the function in order + // to explicitly convert the swizzled type to the unswizzled type. + // + + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, abs); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acos); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, acosh); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, all); + //GLM_SWIZZLE_FUNCTION_1_ARGS(vec_type, any); + + //GLM_SWIZZLE_FUNCTION_2_ARGS(value_type, dot); + //GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, cross); + //GLM_SWIZZLE_FUNCTION_2_ARGS(vec_type, step); + //GLM_SWIZZLE_FUNCTION_2_ARGS_SCALAR(vec_type, mix); +} + +#define GLM_SWIZZLE2_2_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<2, T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2, T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2, T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2, T, Q, 1,1,-1,-2> E1 ## E1; }; + +#define GLM_SWIZZLE2_3_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<3,T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3,T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3,T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; + +#define GLM_SWIZZLE2_4_MEMBERS(T, Q, E0,E1) \ + struct { detail::_swizzle<4,T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; + +#define GLM_SWIZZLE3_2_MEMBERS(T, Q, E0,E1,E2) \ + struct { detail::_swizzle<2,T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 0,2,-1,-2> E0 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 1,1,-1,-2> E1 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 1,2,-1,-2> E1 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 2,0,-1,-2> E2 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 2,1,-1,-2> E2 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 2,2,-1,-2> E2 ## E2; }; + +#define GLM_SWIZZLE3_3_MEMBERS(T, Q ,E0,E1,E2) \ + struct { detail::_swizzle<3, T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,2,-1> E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,2,-1> E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,0,-1> E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,1,-1> E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,2,-1> E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,2,-1> E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,2,-1> E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,0,-1> E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,1,-1> E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,2,-1> E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,0,-1> E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,1,-1> E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,2,-1> E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,0,-1> E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,1,-1> E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,2,-1> E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,0,-1> E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,1,-1> E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,2,-1> E2 ## E2 ## E2; }; + +#define GLM_SWIZZLE3_4_MEMBERS(T, Q, E0,E1,E2) \ + struct { detail::_swizzle<4,T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4,T, Q, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; + +#define GLM_SWIZZLE4_2_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<2,T, Q, 0,0,-1,-2> E0 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 0,1,-1,-2> E0 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 0,2,-1,-2> E0 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 0,3,-1,-2> E0 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 1,0,-1,-2> E1 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 1,1,-1,-2> E1 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 1,2,-1,-2> E1 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 1,3,-1,-2> E1 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 2,0,-1,-2> E2 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 2,1,-1,-2> E2 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 2,2,-1,-2> E2 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 2,3,-1,-2> E2 ## E3; }; \ + struct { detail::_swizzle<2,T, Q, 3,0,-1,-2> E3 ## E0; }; \ + struct { detail::_swizzle<2,T, Q, 3,1,-1,-2> E3 ## E1; }; \ + struct { detail::_swizzle<2,T, Q, 3,2,-1,-2> E3 ## E2; }; \ + struct { detail::_swizzle<2,T, Q, 3,3,-1,-2> E3 ## E3; }; + +#define GLM_SWIZZLE4_3_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<3, T, Q, 0,0,0,-1> E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,1,-1> E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,2,-1> E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,0,3,-1> E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,0,-1> E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,1,-1> E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,2,-1> E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,1,3,-1> E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,0,-1> E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,1,-1> E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,2,-1> E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,2,3,-1> E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,0,-1> E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,1,-1> E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,2,-1> E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 0,3,3,-1> E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,0,-1> E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,1,-1> E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,2,-1> E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,0,3,-1> E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,0,-1> E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,1,-1> E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,2,-1> E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,1,3,-1> E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,0,-1> E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,1,-1> E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,2,-1> E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,2,3,-1> E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,0,-1> E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,1,-1> E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,2,-1> E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 1,3,3,-1> E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,0,-1> E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,1,-1> E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,2,-1> E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,0,3,-1> E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,0,-1> E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,1,-1> E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,2,-1> E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,1,3,-1> E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,0,-1> E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,1,-1> E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,2,-1> E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,2,3,-1> E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,0,-1> E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,1,-1> E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,2,-1> E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 2,3,3,-1> E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,0,-1> E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,1,-1> E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,2,-1> E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,0,3,-1> E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,0,-1> E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,1,-1> E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,2,-1> E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,1,3,-1> E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,0,-1> E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,1,-1> E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,2,-1> E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,2,3,-1> E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,0,-1> E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,1,-1> E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,2,-1> E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<3, T, Q, 3,3,3,-1> E3 ## E3 ## E3; }; + +#define GLM_SWIZZLE4_4_MEMBERS(T, Q, E0,E1,E2,E3) \ + struct { detail::_swizzle<4, T, Q, 0,0,0,0> E0 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,1> E0 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,2> E0 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,0,3> E0 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,0> E0 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,1> E0 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,2> E0 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,1,3> E0 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,0> E0 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,1> E0 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,2> E0 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,2,3> E0 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,0> E0 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,1> E0 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,2> E0 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,0,3,3> E0 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,0> E0 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,1> E0 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,2> E0 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,0,3> E0 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,0> E0 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,1> E0 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,2> E0 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,1,3> E0 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,0> E0 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,1> E0 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,2> E0 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,2,3> E0 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,0> E0 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,1> E0 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,2> E0 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,1,3,3> E0 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,0> E0 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,1> E0 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,2> E0 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,0,3> E0 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,0> E0 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,1> E0 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,2> E0 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,1,3> E0 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,0> E0 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,1> E0 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,2> E0 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,2,3> E0 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,0> E0 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,1> E0 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,2> E0 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,2,3,3> E0 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,0> E0 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,1> E0 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,2> E0 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,0,3> E0 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,0> E0 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,1> E0 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,2> E0 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,1,3> E0 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,0> E0 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,1> E0 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,2> E0 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,2,3> E0 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,0> E0 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,1> E0 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,2> E0 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 0,3,3,3> E0 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,0> E1 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,1> E1 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,2> E1 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,0,3> E1 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,0> E1 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,1> E1 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,2> E1 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,1,3> E1 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,0> E1 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,1> E1 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,2> E1 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,2,3> E1 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,0> E1 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,1> E1 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,2> E1 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,0,3,3> E1 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,0> E1 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,1> E1 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,2> E1 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,0,3> E1 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,0> E1 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,1> E1 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,2> E1 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,1,3> E1 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,0> E1 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,1> E1 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,2> E1 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,2,3> E1 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,0> E1 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,1> E1 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,2> E1 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,1,3,3> E1 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,0> E1 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,1> E1 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,2> E1 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,0,3> E1 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,0> E1 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,1> E1 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,2> E1 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,1,3> E1 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,0> E1 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,1> E1 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,2> E1 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,2,3> E1 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,0> E1 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,1> E1 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,2> E1 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,2,3,3> E1 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,0> E1 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,1> E1 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,2> E1 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,0,3> E1 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,0> E1 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,1> E1 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,2> E1 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,1,3> E1 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,0> E1 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,1> E1 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,2> E1 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,2,3> E1 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,0> E1 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,1> E1 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,2> E1 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 1,3,3,3> E1 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,0> E2 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,1> E2 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,2> E2 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,0,3> E2 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,0> E2 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,1> E2 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,2> E2 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,1,3> E2 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,0> E2 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,1> E2 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,2> E2 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,2,3> E2 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,0> E2 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,1> E2 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,2> E2 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,0,3,3> E2 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,0> E2 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,1> E2 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,2> E2 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,0,3> E2 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,0> E2 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,1> E2 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,2> E2 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,1,3> E2 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,0> E2 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,1> E2 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,2> E2 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,2,3> E2 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,0> E2 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,1> E2 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,2> E2 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,1,3,3> E2 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,0> E2 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,1> E2 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,2> E2 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,0,3> E2 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,0> E2 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,1> E2 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,2> E2 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,1,3> E2 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,0> E2 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,1> E2 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,2> E2 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,2,3> E2 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,0> E2 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,1> E2 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,2> E2 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,2,3,3> E2 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,0> E2 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,1> E2 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,2> E2 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,0,3> E2 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,0> E2 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,1> E2 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,2> E2 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,1,3> E2 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,0> E2 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,1> E2 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,2> E2 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,2,3> E2 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,0> E2 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,1> E2 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,2> E2 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 2,3,3,3> E2 ## E3 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,0> E3 ## E0 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,1> E3 ## E0 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,2> E3 ## E0 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,0,3> E3 ## E0 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,0> E3 ## E0 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,1> E3 ## E0 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,2> E3 ## E0 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,1,3> E3 ## E0 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,0> E3 ## E0 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,1> E3 ## E0 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,2> E3 ## E0 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,2,3> E3 ## E0 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,0> E3 ## E0 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,1> E3 ## E0 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,2> E3 ## E0 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,0,3,3> E3 ## E0 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,0> E3 ## E1 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,1> E3 ## E1 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,2> E3 ## E1 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,0,3> E3 ## E1 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,0> E3 ## E1 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,1> E3 ## E1 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,2> E3 ## E1 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,1,3> E3 ## E1 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,0> E3 ## E1 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,1> E3 ## E1 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,2> E3 ## E1 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,2,3> E3 ## E1 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,0> E3 ## E1 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,1> E3 ## E1 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,2> E3 ## E1 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,1,3,3> E3 ## E1 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,0> E3 ## E2 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,1> E3 ## E2 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,2> E3 ## E2 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,0,3> E3 ## E2 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,0> E3 ## E2 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,1> E3 ## E2 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,2> E3 ## E2 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,1,3> E3 ## E2 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,0> E3 ## E2 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,1> E3 ## E2 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,2> E3 ## E2 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,2,3> E3 ## E2 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,0> E3 ## E2 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,1> E3 ## E2 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,2> E3 ## E2 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,2,3,3> E3 ## E2 ## E3 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,0> E3 ## E3 ## E0 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,1> E3 ## E3 ## E0 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,2> E3 ## E3 ## E0 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,0,3> E3 ## E3 ## E0 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,0> E3 ## E3 ## E1 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,1> E3 ## E3 ## E1 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,2> E3 ## E3 ## E1 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,1,3> E3 ## E3 ## E1 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,0> E3 ## E3 ## E2 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,1> E3 ## E3 ## E2 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,2> E3 ## E3 ## E2 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,2,3> E3 ## E3 ## E2 ## E3; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,0> E3 ## E3 ## E3 ## E0; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,1> E3 ## E3 ## E3 ## E1; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,2> E3 ## E3 ## E3 ## E2; }; \ + struct { detail::_swizzle<4, T, Q, 3,3,3,3> E3 ## E3 ## E3 ## E3; }; diff --git a/thirdparty/glm/glm/detail/_swizzle_func.hpp b/thirdparty/glm/glm/detail/_swizzle_func.hpp new file mode 100644 index 0000000..a264ae9 --- /dev/null +++ b/thirdparty/glm/glm/detail/_swizzle_func.hpp @@ -0,0 +1,682 @@ +#pragma once + +#define GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, CONST, A, B) \ + GLM_FUNC_QUALIFIER vec<2, T, Q> A ## B() CONST \ + { \ + return vec<2, T, Q>(this->A, this->B); \ + } + +#define GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, CONST, A, B, C) \ + GLM_FUNC_QUALIFIER vec<3, T, Q> A ## B ## C() CONST \ + { \ + return vec<3, T, Q>(this->A, this->B, this->C); \ + } + +#define GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, CONST, A, B, C, D) \ + GLM_FUNC_QUALIFIER vec<4, T, Q> A ## B ## C ## D() CONST \ + { \ + return vec<4, T, Q>(this->A, this->B, this->C, this->D); \ + } + +#define GLM_SWIZZLE_GEN_VEC2_ENTRY_DEF(T, P, L, CONST, A, B) \ + template \ + GLM_FUNC_QUALIFIER vec vec::A ## B() CONST \ + { \ + return vec<2, T, Q>(this->A, this->B); \ + } + +#define GLM_SWIZZLE_GEN_VEC3_ENTRY_DEF(T, P, L, CONST, A, B, C) \ + template \ + GLM_FUNC_QUALIFIER vec<3, T, Q> vec::A ## B ## C() CONST \ + { \ + return vec<3, T, Q>(this->A, this->B, this->C); \ + } + +#define GLM_SWIZZLE_GEN_VEC4_ENTRY_DEF(T, P, L, CONST, A, B, C, D) \ + template \ + GLM_FUNC_QUALIFIER vec<4, T, Q> vec::A ## B ## C ## D() CONST \ + { \ + return vec<4, T, Q>(this->A, this->B, this->C, this->D); \ + } + +#define GLM_MUTABLE + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, 2, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, 2, GLM_MUTABLE, B, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC2(T, P) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, x, y) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, r, g) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC2_SWIZZLE(T, P, s, t) + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, B) + +#define GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, GLM_MUTABLE, C, B, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_REF3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC3_SWIZZLE(T, P, A, B, C) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC3(T, P) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, x, y, z) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, r, g, b) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC3_COMP(T, P, s, t, p) + +#define GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, A, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, B, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, GLM_MUTABLE, D, C) + +#define GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , A, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , B, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , C, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, , D, C, B) + +#define GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , B, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , C, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, , D, B, C, A) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_REF4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) + +#define GLM_SWIZZLE_GEN_REF_FROM_VEC4(T, P) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, x, y, z, w) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, r, g, b, a) \ + GLM_SWIZZLE_GEN_REF_FROM_VEC4_COMP(T, P, s, t, p, q) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC2_SWIZZLE(T, P, A, B) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC2_SWIZZLE(T, P, A, B) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC2(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, x, y) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, r, g) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, P, s, t) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, C) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, C) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, C) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC3_SWIZZLE(T, P, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC3_SWIZZLE(T, P, A, B, C) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC3(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, x, y, z) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, r, g, b) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, P, s, t, p) + +#define GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, A, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, B, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, C, D) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, A) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, B) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, C) \ + GLM_SWIZZLE_GEN_VEC2_ENTRY(T, P, const, D, D) + +#define GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, A, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, B, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, C, D, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, A, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, B, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, C, D) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, A) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, B) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, C) \ + GLM_SWIZZLE_GEN_VEC3_ENTRY(T, P, const, D, D, D) + +#define GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, A, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, B, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, C, D, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, A, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, B, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, C, D, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, A, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, B, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, C, D) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, A) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, B) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, C) \ + GLM_SWIZZLE_GEN_VEC4_ENTRY(T, P, const, D, D, D, D) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC2_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC3_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) \ + GLM_SWIZZLE_GEN_VEC4_FROM_VEC4_SWIZZLE(T, P, A, B, C, D) + +#define GLM_SWIZZLE_GEN_VEC_FROM_VEC4(T, P) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, x, y, z, w) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, r, g, b, a) \ + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, P, s, t, p, q) + diff --git a/thirdparty/glm/glm/detail/_vectorize.hpp b/thirdparty/glm/glm/detail/_vectorize.hpp new file mode 100644 index 0000000..1fcaec3 --- /dev/null +++ b/thirdparty/glm/glm/detail/_vectorize.hpp @@ -0,0 +1,162 @@ +#pragma once + +namespace glm{ +namespace detail +{ + template class vec, length_t L, typename R, typename T, qualifier Q> + struct functor1{}; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<1, R, Q> call(R (*Func) (T x), vec<1, T, Q> const& v) + { + return vec<1, R, Q>(Func(v.x)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<2, R, Q> call(R (*Func) (T x), vec<2, T, Q> const& v) + { + return vec<2, R, Q>(Func(v.x), Func(v.y)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<3, R, Q> call(R (*Func) (T x), vec<3, T, Q> const& v) + { + return vec<3, R, Q>(Func(v.x), Func(v.y), Func(v.z)); + } + }; + + template class vec, typename R, typename T, qualifier Q> + struct functor1 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, R, Q> call(R (*Func) (T x), vec<4, T, Q> const& v) + { + return vec<4, R, Q>(Func(v.x), Func(v.y), Func(v.z), Func(v.w)); + } + }; + + template class vec, length_t L, typename T, qualifier Q> + struct functor2{}; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<1, T, Q> call(T (*Func) (T x, T y), vec<1, T, Q> const& a, vec<1, T, Q> const& b) + { + return vec<1, T, Q>(Func(a.x, b.x)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<2, T, Q> call(T (*Func) (T x, T y), vec<2, T, Q> const& a, vec<2, T, Q> const& b) + { + return vec<2, T, Q>(Func(a.x, b.x), Func(a.y, b.y)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<3, T, Q> call(T (*Func) (T x, T y), vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + return vec<3, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2 + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(T (*Func) (T x, T y), vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z), Func(a.w, b.w)); + } + }; + + template class vec, length_t L, typename T, qualifier Q> + struct functor2_vec_sca{}; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<1, T, Q> call(T (*Func) (T x, T y), vec<1, T, Q> const& a, T b) + { + return vec<1, T, Q>(Func(a.x, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<2, T, Q> call(T (*Func) (T x, T y), vec<2, T, Q> const& a, T b) + { + return vec<2, T, Q>(Func(a.x, b), Func(a.y, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<3, T, Q> call(T (*Func) (T x, T y), vec<3, T, Q> const& a, T b) + { + return vec<3, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b)); + } + }; + + template class vec, typename T, qualifier Q> + struct functor2_vec_sca + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(T (*Func) (T x, T y), vec<4, T, Q> const& a, T b) + { + return vec<4, T, Q>(Func(a.x, b), Func(a.y, b), Func(a.z, b), Func(a.w, b)); + } + }; + + template + struct functor2_vec_int {}; + + template + struct functor2_vec_int<1, T, Q> + { + GLM_FUNC_QUALIFIER static vec<1, int, Q> call(int (*Func) (T x, int y), vec<1, T, Q> const& a, vec<1, int, Q> const& b) + { + return vec<1, int, Q>(Func(a.x, b.x)); + } + }; + + template + struct functor2_vec_int<2, T, Q> + { + GLM_FUNC_QUALIFIER static vec<2, int, Q> call(int (*Func) (T x, int y), vec<2, T, Q> const& a, vec<2, int, Q> const& b) + { + return vec<2, int, Q>(Func(a.x, b.x), Func(a.y, b.y)); + } + }; + + template + struct functor2_vec_int<3, T, Q> + { + GLM_FUNC_QUALIFIER static vec<3, int, Q> call(int (*Func) (T x, int y), vec<3, T, Q> const& a, vec<3, int, Q> const& b) + { + return vec<3, int, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z)); + } + }; + + template + struct functor2_vec_int<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(int (*Func) (T x, int y), vec<4, T, Q> const& a, vec<4, int, Q> const& b) + { + return vec<4, int, Q>(Func(a.x, b.x), Func(a.y, b.y), Func(a.z, b.z), Func(a.w, b.w)); + } + }; +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/compute_common.hpp b/thirdparty/glm/glm/detail/compute_common.hpp new file mode 100644 index 0000000..83362bc --- /dev/null +++ b/thirdparty/glm/glm/detail/compute_common.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "setup.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_abs + {}; + + template + struct compute_abs + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genFIType call(genFIType x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_signed, + "'abs' only accept floating-point and integer scalar or vector inputs"); + + return x >= genFIType(0) ? x : -x; + // TODO, perf comp with: *(((int *) &x) + 1) &= 0x7fffffff; + } + }; + +#if (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) + template<> + struct compute_abs + { + GLM_FUNC_QUALIFIER static float call(float x) + { + return fabsf(x); + } + }; +#endif + + template + struct compute_abs + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genFIType call(genFIType x) + { + GLM_STATIC_ASSERT( + (!std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'abs' only accept floating-point and integer scalar or vector inputs"); + return x; + } + }; +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/compute_vector_decl.hpp b/thirdparty/glm/glm/detail/compute_vector_decl.hpp new file mode 100644 index 0000000..00d7de5 --- /dev/null +++ b/thirdparty/glm/glm/detail/compute_vector_decl.hpp @@ -0,0 +1,190 @@ + +#pragma once +#include +#include "_vectorize.hpp" + +namespace glm { + namespace detail + { + template + struct compute_vec_add {}; + + template + struct compute_vec_sub {}; + + template + struct compute_vec_mul {}; + + template + struct compute_vec_div {}; + + template + struct compute_vec_mod {}; + + template + struct compute_splat {}; + + template + struct compute_vec_and {}; + + template + struct compute_vec_or {}; + + template + struct compute_vec_xor {}; + + template + struct compute_vec_shift_left {}; + + template + struct compute_vec_shift_right {}; + + template + struct compute_vec_equal {}; + + template + struct compute_vec_nequal {}; + + template + struct compute_vec_bitwise_not {}; + + template + struct compute_vec_add + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::plus(), a, b); + } + }; + + template + struct compute_vec_sub + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::minus(), a, b); + } + }; + + template + struct compute_vec_mul + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::multiplies(), a, b); + } + }; + + template + struct compute_vec_div + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::divides(), a, b); + } + }; + + template + struct compute_vec_mod + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::modulus(), a, b); + } + }; + + template + struct compute_vec_and + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] &= static_cast(b[i]); + return v; + } + }; + + template + struct compute_vec_or + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] |= static_cast(b[i]); + return v; + } + }; + + template + struct compute_vec_xor + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] ^= static_cast(b[i]); + return v; + } + }; + + template + struct compute_vec_shift_left + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] <<= static_cast(b[i]); + return v; + } + }; + + template + struct compute_vec_shift_right + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a, vec const& b) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] >>= static_cast(b[i]); + return v; + } + }; + + template + struct compute_vec_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec const& v1, vec const& v2) + { + bool b = true; + for (length_t i = 0; i < L; ++i) + b = b && detail::compute_equal::is_iec559>::call(v1.x, v2.x); + return b; + } + }; + + template + struct compute_vec_nequal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return !compute_vec_equal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + }; + + template + struct compute_vec_bitwise_not + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& a) + { + vec v(a); + for (length_t i = 0; i < L; ++i) + v[i] = ~v[i]; + return v; + } + }; + + } +} diff --git a/thirdparty/glm/glm/detail/compute_vector_relational.hpp b/thirdparty/glm/glm/detail/compute_vector_relational.hpp new file mode 100644 index 0000000..167b634 --- /dev/null +++ b/thirdparty/glm/glm/detail/compute_vector_relational.hpp @@ -0,0 +1,30 @@ +#pragma once + +//#include "compute_common.hpp" +#include "setup.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(T a, T b) + { + return a == b; + } + }; +/* + template + struct compute_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(T a, T b) + { + return detail::compute_abs::is_signed>::call(b - a) <= static_cast(0); + //return std::memcmp(&a, &b, sizeof(T)) == 0; + } + }; +*/ +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/func_common.inl b/thirdparty/glm/glm/detail/func_common.inl new file mode 100644 index 0000000..f8584c9 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_common.inl @@ -0,0 +1,792 @@ +/// @ref core +/// @file glm/detail/func_common.inl + +#include "../vector_relational.hpp" +#include "compute_common.hpp" +#include "type_vec1.hpp" +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include "_vectorize.hpp" +#include + +namespace glm +{ + // min + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType min(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return (y < x) ? y : x; + } + + // max + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType max(genType x, genType y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + + return (x < y) ? y : x; + } + + // abs + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR int abs(int x) + { + int const y = x >> (sizeof(int) * 8 - 1); + return (x ^ y) - y; + } + + // round +# if GLM_HAS_CXX11_STL + using ::std::round; +# else + template + GLM_FUNC_QUALIFIER genType round(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'round' only accept floating-point inputs"); + + return x < static_cast(0) ? static_cast(int(x - static_cast(0.5))) : static_cast(int(x + static_cast(0.5))); + } +# endif + + // trunc +# if GLM_HAS_CXX11_STL + using ::std::trunc; +# else + template + GLM_FUNC_QUALIFIER genType trunc(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'trunc' only accept floating-point inputs"); + + return x < static_cast(0) ? -std::floor(-x) : std::floor(x); + } +# endif + +}//namespace glm + +namespace glm{ +namespace detail +{ + template + struct compute_abs_vector + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x) + { + return detail::functor1::call(abs, x); + } + }; + + template + struct compute_mix_vector + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x, vec const& y, vec const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return vec(vec(x) * (static_cast(1) - a) + vec(y) * a); + } + }; + + template + struct compute_mix_vector + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x, vec const& y, vec const& a) + { + vec Result(0); + for(length_t i = 0; i < x.length(); ++i) + Result[i] = a[i] ? y[i] : x[i]; + return Result; + } + }; + + template + struct compute_mix_scalar + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x, vec const& y, U const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return vec(vec(x) * (static_cast(1) - a) + vec(y) * a); + } + }; + + template + struct compute_mix_scalar + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x, vec const& y, bool const& a) + { + return a ? y : x; + } + }; + + template + struct compute_mix + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(T const& x, T const& y, U const& a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'mix' only accept floating-point inputs for the interpolator a"); + + return static_cast(static_cast(x) * (static_cast(1) - a) + static_cast(y) * a); + } + }; + + template + struct compute_mix + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(T const& x, T const& y, bool const& a) + { + return a ? y : x; + } + }; + + template + struct compute_sign + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x) + { + return vec(glm::lessThan(vec(0), x)) - vec(glm::lessThan(x, vec(0))); + } + }; + +# if GLM_ARCH == GLM_ARCH_X86 + template + struct compute_sign + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec call(vec const& x) + { + T const Shift(static_cast(sizeof(T) * 8 - 1)); + vec const y(vec::type, Q>(-x) >> typename detail::make_unsigned::type(Shift)); + + return (x >> Shift) | y; + } + }; +# endif + + template + struct compute_floor + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::floor, x); + } + }; + + template + struct compute_ceil + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::ceil, x); + } + }; + + template + struct compute_fract + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return x - floor(x); + } + }; + + template + struct compute_trunc + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(trunc, x); + } + }; + + template + struct compute_round + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(round, x); + } + }; + + template + struct compute_mod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'mod' only accept floating-point inputs. Include for integer inputs."); + return a - b * floor(a / b); + } + }; + + template + struct compute_min_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y) + { + return detail::functor2::call(min, x, y); + } + }; + + template + struct compute_max_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& y) + { + return detail::functor2::call(max, x, y); + } + }; + + template + struct compute_clamp_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, vec const& minVal, vec const& maxVal) + { + return min(max(x, minVal), maxVal); + } + }; + + template + struct compute_step_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& edge, vec const& x) + { + return mix(vec(1), vec(0), glm::lessThan(x, edge)); + } + }; + + template + struct compute_smoothstep_vector + { + GLM_FUNC_QUALIFIER static vec call(vec const& edge0, vec const& edge1, vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'smoothstep' only accept floating-point inputs"); + vec const tmp(clamp((x - edge0) / (edge1 - edge0), static_cast(0), static_cast(1))); + return tmp * tmp * (static_cast(3) - static_cast(2) * tmp); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genFIType abs(genFIType x) + { + return detail::compute_abs::is_signed>::call(x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec abs(vec const& x) + { + return detail::compute_abs_vector::value>::call(x); + } + + // sign + // fast and works for any type + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genFIType sign(genFIType x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || (std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'sign' only accept signed inputs"); + + return detail::compute_sign<1, genFIType, defaultp, + std::numeric_limits::is_iec559, detail::is_aligned::value>::call(vec<1, genFIType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec sign(vec const& x) + { + GLM_STATIC_ASSERT( + std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || (std::numeric_limits::is_signed && std::numeric_limits::is_integer), + "'sign' only accept signed inputs"); + + return detail::compute_sign::is_iec559, detail::is_aligned::value>::call(x); + } + + // floor + using ::std::floor; + template + GLM_FUNC_QUALIFIER vec floor(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'floor' only accept floating-point inputs."); + return detail::compute_floor::value>::call(x); + } + + template + GLM_FUNC_QUALIFIER vec trunc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'trunc' only accept floating-point inputs"); + return detail::compute_trunc::value>::call(x); + } + + template + GLM_FUNC_QUALIFIER vec round(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'round' only accept floating-point inputs"); + return detail::compute_round::value>::call(x); + } + +/* + // roundEven + template + GLM_FUNC_QUALIFIER genType roundEven(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'roundEven' only accept floating-point inputs"); + + return genType(int(x + genType(int(x) % 2))); + } +*/ + + // roundEven + template + GLM_FUNC_QUALIFIER genType roundEven(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'roundEven' only accept floating-point inputs"); + + int Integer = static_cast(x); + genType IntegerPart = static_cast(Integer); + genType FractionalPart = fract(x); + + if(FractionalPart > static_cast(0.5) || FractionalPart < static_cast(0.5)) + { + return round(x); + } + else if((Integer % 2) == 0) + { + return IntegerPart; + } + else if(x <= static_cast(0)) // Work around... + { + return IntegerPart - static_cast(1); + } + else + { + return IntegerPart + static_cast(1); + } + //else // Bug on MinGW 4.5.2 + //{ + // return mix(IntegerPart + genType(-1), IntegerPart + genType(1), x <= genType(0)); + //} + } + + template + GLM_FUNC_QUALIFIER vec roundEven(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'roundEven' only accept floating-point inputs"); + return detail::functor1::call(roundEven, x); + } + + // ceil + using ::std::ceil; + template + GLM_FUNC_QUALIFIER vec ceil(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'ceil' only accept floating-point inputs"); + return detail::compute_ceil::value>::call(x); + } + + // fract + template + GLM_FUNC_QUALIFIER genType fract(genType x) + { + return fract(vec<1, genType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec fract(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fract' only accept floating-point inputs"); + return detail::compute_fract::value>::call(x); + } + + // mod + template + GLM_FUNC_QUALIFIER genType mod(genType x, genType y) + { +# if (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) + // Another Cuda compiler bug https://github.com/g-truc/glm/issues/530 + vec<1, genType, defaultp> Result(mod(vec<1, genType, defaultp>(x), y)); + return Result.x; +# else + return mod(vec<1, genType, defaultp>(x), y).x; +# endif + } + + template + GLM_FUNC_QUALIFIER vec mod(vec const& x, T y) + { + return detail::compute_mod::value>::call(x, vec(y)); + } + + template + GLM_FUNC_QUALIFIER vec mod(vec const& x, vec const& y) + { + return detail::compute_mod::value>::call(x, y); + } + + // modf + template + GLM_FUNC_QUALIFIER genType modf(genType x, genType & i) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'modf' only accept floating-point inputs"); + return std::modf(x, &i); + } + + template + GLM_FUNC_QUALIFIER vec<1, T, Q> modf(vec<1, T, Q> const& x, vec<1, T, Q> & i) + { + return vec<1, T, Q>( + modf(x.x, i.x)); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> modf(vec<2, T, Q> const& x, vec<2, T, Q> & i) + { + return vec<2, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> modf(vec<3, T, Q> const& x, vec<3, T, Q> & i) + { + return vec<3, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y), + modf(x.z, i.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> modf(vec<4, T, Q> const& x, vec<4, T, Q> & i) + { + return vec<4, T, Q>( + modf(x.x, i.x), + modf(x.y, i.y), + modf(x.z, i.z), + modf(x.w, i.w)); + } + + //// Only valid if (INT_MIN <= x-y <= INT_MAX) + //// min(x,y) + //r = y + ((x - y) & ((x - y) >> (sizeof(int) * + //CHAR_BIT - 1))); + //// max(x,y) + //r = x - ((x - y) & ((x - y) >> (sizeof(int) * + //CHAR_BIT - 1))); + + // min + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return detail::compute_min_vector::value>::call(a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& a, vec const& b) + { + return detail::compute_min_vector::value>::call(a, b); + } + + // max + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + return detail::compute_max_vector::value>::call(a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& a, vec const& b) + { + return detail::compute_max_vector::value>::call(a, b); + } + + // clamp + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType clamp(genType x, genType minVal, genType maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return min(max(x, minVal), maxVal); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec clamp(vec const& x, T minVal, T maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return detail::compute_clamp_vector::value>::call(x, vec(minVal), vec(maxVal)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec clamp(vec const& x, vec const& minVal, vec const& maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || std::numeric_limits::is_integer, "'clamp' only accept floating-point or integer inputs"); + return detail::compute_clamp_vector::value>::call(x, minVal, maxVal); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genTypeT mix(genTypeT x, genTypeT y, genTypeU a) + { + return detail::compute_mix::call(x, y, a); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec mix(vec const& x, vec const& y, U a) + { + return detail::compute_mix_scalar::value>::call(x, y, a); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec mix(vec const& x, vec const& y, vec const& a) + { + return detail::compute_mix_vector::value>::call(x, y, a); + } + + // step + template + GLM_FUNC_QUALIFIER genType step(genType edge, genType x) + { + return mix(static_cast(1), static_cast(0), x < edge); + } + + template + GLM_FUNC_QUALIFIER vec step(T edge, vec const& x) + { + return detail::compute_step_vector::value>::call(vec(edge), x); + } + + template + GLM_FUNC_QUALIFIER vec step(vec const& edge, vec const& x) + { + return detail::compute_step_vector::value>::call(edge, x); + } + + // smoothstep + template + GLM_FUNC_QUALIFIER genType smoothstep(genType edge0, genType edge1, genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'smoothstep' only accept floating-point inputs"); + + genType const tmp(clamp((x - edge0) / (edge1 - edge0), genType(0), genType(1))); + return tmp * tmp * (genType(3) - genType(2) * tmp); + } + + template + GLM_FUNC_QUALIFIER vec smoothstep(T edge0, T edge1, vec const& x) + { + return detail::compute_smoothstep_vector::value>::call(vec(edge0), vec(edge1), x); + } + + template + GLM_FUNC_QUALIFIER vec smoothstep(vec const& edge0, vec const& edge1, vec const& x) + { + return detail::compute_smoothstep_vector::value>::call(edge0, edge1, x); + } + +# if GLM_HAS_CXX11_STL + using std::isnan; +# else + template + GLM_FUNC_QUALIFIER bool isnan(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isnan' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::isnan(x); +# elif GLM_COMPILER & GLM_COMPILER_VC + return _isnan(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# if GLM_PLATFORM & GLM_PLATFORM_WINDOWS + return _isnan(x) != 0; +# else + return ::isnan(x) != 0; +# endif +# elif (GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG)) && (GLM_PLATFORM & GLM_PLATFORM_ANDROID) && __cplusplus < 201103L + return _isnan(x) != 0; +# elif (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) + return ::isnan(x) != 0; +# else + return std::isnan(x); +# endif + } +# endif + + template + GLM_FUNC_QUALIFIER vec isnan(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isnan' only accept floating-point inputs"); + + vec Result(0); + for (length_t l = 0; l < v.length(); ++l) + Result[l] = glm::isnan(v[l]); + return Result; + } + +# if GLM_HAS_CXX11_STL + using std::isinf; +# else + template + GLM_FUNC_QUALIFIER bool isinf(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isinf' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::isinf(x); +# elif GLM_COMPILER & (GLM_COMPILER_INTEL | GLM_COMPILER_VC) +# if(GLM_PLATFORM & GLM_PLATFORM_WINDOWS) + return _fpclass(x) == _FPCLASS_NINF || _fpclass(x) == _FPCLASS_PINF; +# else + return ::isinf(x); +# endif +# elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG) +# if(GLM_PLATFORM & GLM_PLATFORM_ANDROID && __cplusplus < 201103L) + return _isinf(x) != 0; +# else + return std::isinf(x); +# endif +# elif (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) + // http://developer.download.nvidia.com/compute/cuda/4_2/rel/toolkit/docs/online/group__CUDA__MATH__DOUBLE_g13431dd2b40b51f9139cbb7f50c18fab.html#g13431dd2b40b51f9139cbb7f50c18fab + return ::isinf(double(x)) != 0; +# else + return std::isinf(x); +# endif + } +# endif + + template + GLM_FUNC_QUALIFIER vec isinf(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isinf' only accept floating-point inputs"); + + vec Result(0); + for (length_t l = 0; l < v.length(); ++l) + Result[l] = glm::isinf(v[l]); + return Result; + } + + GLM_FUNC_QUALIFIER int floatBitsToInt(float v) + { + union + { + float in; + int out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec floatBitsToInt(vec const& v) + { + return detail::functor1::call(floatBitsToInt, v); + } + + GLM_FUNC_QUALIFIER uint floatBitsToUint(float v) + { + union + { + float in; + uint out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec floatBitsToUint(vec const& v) + { + return detail::functor1::call(floatBitsToUint, v); + } + + GLM_FUNC_QUALIFIER float intBitsToFloat(int v) + { + union + { + int in; + float out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec intBitsToFloat(vec const& v) + { + return detail::functor1::call(intBitsToFloat, v); + } + + GLM_FUNC_QUALIFIER float uintBitsToFloat(uint v) + { + union + { + uint in; + float out; + } u; + + u.in = v; + + return u.out; + } + + template + GLM_FUNC_QUALIFIER vec uintBitsToFloat(vec const& v) + { + return reinterpret_cast&>(const_cast&>(v)); + } + +# if GLM_HAS_CXX11_STL + using std::fma; +# else + template + GLM_FUNC_QUALIFIER genType fma(genType const& a, genType const& b, genType const& c) + { + return a * b + c; + } +# endif + + template + GLM_FUNC_QUALIFIER genType frexp(genType x, int& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'frexp' only accept floating-point inputs"); + + return std::frexp(x, &exp); + } + + template + GLM_FUNC_QUALIFIER vec frexp(vec const& v, vec& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'frexp' only accept floating-point inputs"); + + vec Result(0); + for (length_t l = 0; l < v.length(); ++l) + Result[l] = std::frexp(v[l], &exp[l]); + return Result; + } + + template + GLM_FUNC_QUALIFIER genType ldexp(genType const& x, int const& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'ldexp' only accept floating-point inputs"); + + return std::ldexp(x, exp); + } + + template + GLM_FUNC_QUALIFIER vec ldexp(vec const& v, vec const& exp) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'ldexp' only accept floating-point inputs"); + + vec Result(0); + for (length_t l = 0; l < v.length(); ++l) + Result[l] = std::ldexp(v[l], exp[l]); + return Result; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_common_simd.inl" +#endif diff --git a/thirdparty/glm/glm/detail/func_common_simd.inl b/thirdparty/glm/glm/detail/func_common_simd.inl new file mode 100644 index 0000000..ce0032d --- /dev/null +++ b/thirdparty/glm/glm/detail/func_common_simd.inl @@ -0,0 +1,231 @@ +/// @ref core +/// @file glm/detail/func_common_simd.inl + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#include "../simd/common.h" + +#include + +namespace glm{ +namespace detail +{ + template + struct compute_abs_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_abs(v.data); + return result; + } + }; + + template + struct compute_abs_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v) + { + vec<4, int, Q> result; + result.data = glm_ivec4_abs(v.data); + return result; + } + }; + + template + struct compute_floor<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_floor(v.data); + return result; + } + }; + + template + struct compute_ceil<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_ceil(v.data); + return result; + } + }; + + template + struct compute_fract<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_fract(v.data); + return result; + } + }; + + template + struct compute_round<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> result; + result.data = glm_vec4_round(v.data); + return result; + } + }; + + template + struct compute_mod<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& y) + { + vec<4, float, Q> result; + result.data = glm_vec4_mod(x.data, y.data); + return result; + } + }; + + template + struct compute_min_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + vec<4, float, Q> result; + result.data = _mm_min_ps(v1.data, v2.data); + return result; + } + }; + + template + struct compute_min_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + vec<4, int, Q> result; + result.data = _mm_min_epi32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_min_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + vec<4, uint, Q> result; + result.data = _mm_min_epu32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + vec<4, float, Q> result; + result.data = _mm_max_ps(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + vec<4, int, Q> result; + result.data = _mm_max_epi32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_max_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + vec<4, uint, Q> result; + result.data = _mm_max_epu32(v1.data, v2.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& minVal, vec<4, float, Q> const& maxVal) + { + vec<4, float, Q> result; + result.data = _mm_min_ps(_mm_max_ps(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, int, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& x, vec<4, int, Q> const& minVal, vec<4, int, Q> const& maxVal) + { + vec<4, int, Q> result; + result.data = _mm_min_epi32(_mm_max_epi32(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_clamp_vector<4, uint, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& x, vec<4, uint, Q> const& minVal, vec<4, uint, Q> const& maxVal) + { + vec<4, uint, Q> result; + result.data = _mm_min_epu32(_mm_max_epu32(x.data, minVal.data), maxVal.data); + return result; + } + }; + + template + struct compute_mix_vector<4, float, bool, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& x, vec<4, float, Q> const& y, vec<4, bool, Q> const& a) + { + __m128i const Load = _mm_set_epi32(-static_cast(a.w), -static_cast(a.z), -static_cast(a.y), -static_cast(a.x)); + __m128 const Mask = _mm_castsi128_ps(Load); + + vec<4, float, Q> Result; +# if 0 && GLM_ARCH & GLM_ARCH_AVX + Result.data = _mm_blendv_ps(x.data, y.data, Mask); +# else + Result.data = _mm_or_ps(_mm_and_ps(Mask, y.data), _mm_andnot_ps(Mask, x.data)); +# endif + return Result; + } + }; +/* FIXME + template + struct compute_step_vector + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& edge, vec<4, float, Q> const& x) + { + vec<4, float, Q> Result; + result.data = glm_vec4_step(edge.data, x.data); + return result; + } + }; +*/ + template + struct compute_smoothstep_vector<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& edge0, vec<4, float, Q> const& edge1, vec<4, float, Q> const& x) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_smoothstep(edge0.data, edge1.data, x.data); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/detail/func_exponential.inl b/thirdparty/glm/glm/detail/func_exponential.inl new file mode 100644 index 0000000..2efcdc6 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_exponential.inl @@ -0,0 +1,152 @@ +/// @ref core +/// @file glm/detail/func_exponential.inl + +#include "../vector_relational.hpp" +#include "_vectorize.hpp" +#include +#include +#include + +namespace glm{ +namespace detail +{ +# if GLM_HAS_CXX11_STL + using std::log2; +# else + template + genType log2(genType Value) + { + return std::log(Value) * static_cast(1.4426950408889634073599246810019); + } +# endif + + template + struct compute_log2 + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'log2' only accept floating-point inputs. Include for integer inputs."); + + return detail::functor1::call(log2, v); + } + }; + + template + struct compute_sqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(std::sqrt, x); + } + }; + + template + struct compute_inversesqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return static_cast(1) / sqrt(x); + } + }; + + template + struct compute_inversesqrt + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + vec tmp(x); + vec xhalf(tmp * 0.5f); + vec* p = reinterpret_cast*>(const_cast*>(&x)); + vec i = vec(0x5f375a86) - (*p >> vec(1)); + vec* ptmp = reinterpret_cast*>(&i); + tmp = *ptmp; + tmp = tmp * (1.5f - xhalf * tmp * tmp); + return tmp; + } + }; +}//namespace detail + + // pow + using std::pow; + template + GLM_FUNC_QUALIFIER vec pow(vec const& base, vec const& exponent) + { + return detail::functor2::call(pow, base, exponent); + } + + // exp + using std::exp; + template + GLM_FUNC_QUALIFIER vec exp(vec const& x) + { + return detail::functor1::call(exp, x); + } + + // log + using std::log; + template + GLM_FUNC_QUALIFIER vec log(vec const& x) + { + return detail::functor1::call(log, x); + } + +# if GLM_HAS_CXX11_STL + using std::exp2; +# else + //exp2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType exp2(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'exp2' only accept floating-point inputs"); + + return std::exp(static_cast(0.69314718055994530941723212145818) * x); + } +# endif + + template + GLM_FUNC_QUALIFIER vec exp2(vec const& x) + { + return detail::functor1::call(exp2, x); + } + + // log2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType log2(genType x) + { + return log2(vec<1, genType>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec log2(vec const& x) + { + return detail::compute_log2::is_iec559, detail::is_aligned::value>::call(x); + } + + // sqrt + using std::sqrt; + template + GLM_FUNC_QUALIFIER vec sqrt(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'sqrt' only accept floating-point inputs"); + return detail::compute_sqrt::value>::call(x); + } + + // inversesqrt + template + GLM_FUNC_QUALIFIER genType inversesqrt(genType x) + { + return static_cast(1) / sqrt(x); + } + + template + GLM_FUNC_QUALIFIER vec inversesqrt(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'inversesqrt' only accept floating-point inputs"); + return detail::compute_inversesqrt::value>::call(x); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_exponential_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/func_exponential_simd.inl b/thirdparty/glm/glm/detail/func_exponential_simd.inl new file mode 100644 index 0000000..fb78951 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_exponential_simd.inl @@ -0,0 +1,37 @@ +/// @ref core +/// @file glm/detail/func_exponential_simd.inl + +#include "../simd/exponential.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_sqrt<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> Result; + Result.data = _mm_sqrt_ps(v.data); + return Result; + } + }; + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + struct compute_sqrt<4, float, aligned_lowp, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, aligned_lowp> call(vec<4, float, aligned_lowp> const& v) + { + vec<4, float, aligned_lowp> Result; + Result.data = glm_vec4_sqrt_lowp(v.data); + return Result; + } + }; +# endif +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/detail/func_geometric.inl b/thirdparty/glm/glm/detail/func_geometric.inl new file mode 100644 index 0000000..404c990 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_geometric.inl @@ -0,0 +1,243 @@ +#include "../exponential.hpp" +#include "../common.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_length + { + GLM_FUNC_QUALIFIER static T call(vec const& v) + { + return sqrt(dot(v, v)); + } + }; + + template + struct compute_distance + { + GLM_FUNC_QUALIFIER static T call(vec const& p0, vec const& p1) + { + return length(p1 - p0); + } + }; + + template + struct compute_dot{}; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(vec<1, T, Q> const& a, vec<1, T, Q> const& b) + { + return a.x * b.x; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(vec<2, T, Q> const& a, vec<2, T, Q> const& b) + { + vec<2, T, Q> tmp(a * b); + return tmp.x + tmp.y; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + vec<3, T, Q> tmp(a * b); + return tmp.x + tmp.y + tmp.z; + } + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> tmp(a * b); + return (tmp.x + tmp.y) + (tmp.z + tmp.w); + } + }; + + template + struct compute_cross + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<3, T, Q> call(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'cross' accepts only floating-point inputs"); + + return vec<3, T, Q>( + x.y * y.z - y.y * x.z, + x.z * y.x - y.z * x.x, + x.x * y.y - y.x * x.y); + } + }; + + template + struct compute_normalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return v * inversesqrt(dot(v, v)); + } + }; + + template + struct compute_faceforward + { + GLM_FUNC_QUALIFIER static vec call(vec const& N, vec const& I, vec const& Nref) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return dot(Nref, I) < static_cast(0) ? N : -N; + } + }; + + template + struct compute_reflect + { + GLM_FUNC_QUALIFIER static vec call(vec const& I, vec const& N) + { + return I - N * dot(N, I) * static_cast(2); + } + }; + + template + struct compute_refract + { + GLM_FUNC_QUALIFIER static vec call(vec const& I, vec const& N, T eta) + { + T const dotValue(dot(N, I)); + T const k(static_cast(1) - eta * eta * (static_cast(1) - dotValue * dotValue)); + vec const Result = + (k >= static_cast(0)) ? (eta * I - (eta * dotValue + std::sqrt(k)) * N) : vec(0); + return Result; + } + }; +}//namespace detail + + // length + template + GLM_FUNC_QUALIFIER genType length(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length' accepts only floating-point inputs"); + + return abs(x); + } + + template + GLM_FUNC_QUALIFIER T length(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'length' accepts only floating-point inputs"); + + return detail::compute_length::value>::call(v); + } + + // distance + template + GLM_FUNC_QUALIFIER genType distance(genType const& p0, genType const& p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'distance' accepts only floating-point inputs"); + + return length(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T distance(vec const& p0, vec const& p1) + { + return detail::compute_distance::value>::call(p0, p1); + } + + // dot + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T dot(T x, T y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'dot' accepts only floating-point inputs"); + return x * y; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T dot(vec const& x, vec const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'dot' accepts only floating-point inputs"); + return detail::compute_dot, T, detail::is_aligned::value>::call(x, y); + } + + // cross + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + return detail::compute_cross::value>::call(x, y); + } +/* + // normalize + template + GLM_FUNC_QUALIFIER genType normalize(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return x < genType(0) ? genType(-1) : genType(1); + } +*/ + template + GLM_FUNC_QUALIFIER vec normalize(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'normalize' accepts only floating-point inputs"); + + return detail::compute_normalize::value>::call(x); + } + + // faceforward + template + GLM_FUNC_QUALIFIER genType faceforward(genType const& N, genType const& I, genType const& Nref) + { + return dot(Nref, I) < static_cast(0) ? N : -N; + } + + template + GLM_FUNC_QUALIFIER vec faceforward(vec const& N, vec const& I, vec const& Nref) + { + return detail::compute_faceforward::value>::call(N, I, Nref); + } + + // reflect + template + GLM_FUNC_QUALIFIER genType reflect(genType const& I, genType const& N) + { + return I - N * dot(N, I) * genType(2); + } + + template + GLM_FUNC_QUALIFIER vec reflect(vec const& I, vec const& N) + { + return detail::compute_reflect::value>::call(I, N); + } + + // refract + template + GLM_FUNC_QUALIFIER genType refract(genType const& I, genType const& N, genType eta) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'refract' accepts only floating-point inputs"); + genType const dotValue(dot(N, I)); + genType const k(static_cast(1) - eta * eta * (static_cast(1) - dotValue * dotValue)); + return (eta * I - (eta * dotValue + sqrt(k)) * N) * static_cast(k >= static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER vec refract(vec const& I, vec const& N, T eta) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'refract' accepts only floating-point inputs"); + return detail::compute_refract::value>::call(I, N, eta); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_geometric_simd.inl" +#endif diff --git a/thirdparty/glm/glm/detail/func_geometric_simd.inl b/thirdparty/glm/glm/detail/func_geometric_simd.inl new file mode 100644 index 0000000..2076dae --- /dev/null +++ b/thirdparty/glm/glm/detail/func_geometric_simd.inl @@ -0,0 +1,163 @@ +/// @ref core +/// @file glm/detail/func_geometric_simd.inl + +#include "../simd/geometric.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_length<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& v) + { + return _mm_cvtss_f32(glm_vec4_length(v.data)); + } + }; + + template + struct compute_distance<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& p0, vec<4, float, Q> const& p1) + { + return _mm_cvtss_f32(glm_vec4_distance(p0.data, p1.data)); + } + }; + + template + struct compute_dot, float, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& x, vec<4, float, Q> const& y) + { + return _mm_cvtss_f32(glm_vec1_dot(x.data, y.data)); + } + }; + + template + struct compute_cross + { + GLM_FUNC_QUALIFIER static vec<3, float, Q> call(vec<3, float, Q> const& a, vec<3, float, Q> const& b) + { + __m128 const set0 = _mm_set_ps(0.0f, a.z, a.y, a.x); + __m128 const set1 = _mm_set_ps(0.0f, b.z, b.y, b.x); + __m128 const xpd0 = glm_vec4_cross(set0, set1); + + vec<4, float, Q> Result; + Result.data = xpd0; + return vec<3, float, Q>(Result); + } + }; + + template + struct compute_normalize<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_normalize(v.data); + return Result; + } + }; + + template + struct compute_faceforward<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& N, vec<4, float, Q> const& I, vec<4, float, Q> const& Nref) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_faceforward(N.data, I.data, Nref.data); + return Result; + } + }; + + template + struct compute_reflect<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& I, vec<4, float, Q> const& N) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_reflect(I.data, N.data); + return Result; + } + }; + + template + struct compute_refract<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& I, vec<4, float, Q> const& N, float eta) + { + vec<4, float, Q> Result; + Result.data = glm_vec4_refract(I.data, N.data, _mm_set1_ps(eta)); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#elif GLM_ARCH & GLM_ARCH_NEON_BIT +namespace glm{ +namespace detail +{ + template + struct compute_length<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& v) + { + return sqrt(compute_dot, float, true>::call(v, v)); + } + }; + + template + struct compute_distance<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& p0, vec<4, float, Q> const& p1) + { + return compute_length<4, float, Q, true>::call(p1 - p0); + } + }; + + + template + struct compute_dot, float, true> + { + GLM_FUNC_QUALIFIER static float call(vec<4, float, Q> const& x, vec<4, float, Q> const& y) + { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + float32x4_t v = vmulq_f32(x.data, y.data); + return vaddvq_f32(v); +#else // Armv7a with Neon + float32x4_t p = vmulq_f32(x.data, y.data); + float32x2_t v = vpadd_f32(vget_low_f32(p), vget_high_f32(p)); + v = vpadd_f32(v, v); + return vget_lane_f32(v, 0); +#endif + } + }; + + template + struct compute_normalize<4, float, Q, true> + { + GLM_FUNC_QUALIFIER static vec<4, float, Q> call(vec<4, float, Q> const& v) + { + float32x4_t p = vmulq_f32(v.data, v.data); +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + p = vpaddq_f32(p, p); + p = vpaddq_f32(p, p); +#else + float32x2_t t = vpadd_f32(vget_low_f32(p), vget_high_f32(p)); + t = vpadd_f32(t, t); + p = vcombine_f32(t, t); +#endif + + float32x4_t vd = vrsqrteq_f32(p); + vec<4, float, Q> Result; + Result.data = vmulq_f32(v.data, vd); + return Result; + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/detail/func_integer.inl b/thirdparty/glm/glm/detail/func_integer.inl new file mode 100644 index 0000000..67177a0 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_integer.inl @@ -0,0 +1,392 @@ +/// @ref core + +#include "_vectorize.hpp" +#if(GLM_ARCH & GLM_ARCH_X86 && GLM_COMPILER & GLM_COMPILER_VC) +# include +# pragma intrinsic(_BitScanReverse) +#endif//(GLM_ARCH & GLM_ARCH_X86 && GLM_COMPILER & GLM_COMPILER_VC) +#include + +#if !GLM_HAS_EXTENDED_INTEGER_TYPE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wlong-long" +# endif +# if (GLM_COMPILER & GLM_COMPILER_CLANG) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wc++11-long-long" +# endif +#endif + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER T mask(T Bits) + { + return Bits >= static_cast(sizeof(T) * 8) ? ~static_cast(0) : (static_cast(1) << Bits) - static_cast(1); + } + + template + struct compute_bitfieldReverseStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T, T) + { + return v; + } + }; + + template + struct compute_bitfieldReverseStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Mask, T Shift) + { + return (v & Mask) << Shift | (v & (~Mask)) >> Shift; + } + }; + + template + struct compute_bitfieldBitCountStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T, T) + { + return v; + } + }; + + template + struct compute_bitfieldBitCountStep + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Mask, T Shift) + { + return (v & Mask) + ((v >> Shift) & Mask); + } + }; + + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + if(Value == 0) + return -1; + + return glm::bitCount(~Value & (Value - static_cast(1))); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanForward(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + }; + +# if !((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_MODEL == GLM_MODEL_32)) + template + struct compute_findLSB + { + GLM_FUNC_QUALIFIER static int call(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanForward64(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + }; +# endif +# endif//GLM_HAS_BITSCAN_WINDOWS + + template + struct compute_findMSB_step_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, T Shift) + { + return x | (x >> Shift); + } + }; + + template + struct compute_findMSB_step_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x, T) + { + return x; + } + }; + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + vec x(v); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 1)); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 2)); + x = compute_findMSB_step_vec= 8>::call(x, static_cast( 4)); + x = compute_findMSB_step_vec= 16>::call(x, static_cast( 8)); + x = compute_findMSB_step_vec= 32>::call(x, static_cast(16)); + x = compute_findMSB_step_vec= 64>::call(x, static_cast(32)); + return vec(sizeof(T) * 8 - 1) - glm::bitCount(~x); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + GLM_FUNC_QUALIFIER int compute_findMSB_32(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanReverse(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(compute_findMSB_32, x); + } + }; + +# if !((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_MODEL == GLM_MODEL_32)) + template + GLM_FUNC_QUALIFIER int compute_findMSB_64(genIUType Value) + { + unsigned long Result(0); + unsigned char IsNotNull = _BitScanReverse64(&Result, *reinterpret_cast(&Value)); + return IsNotNull ? int(Result) : -1; + } + + template + struct compute_findMSB_vec + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + return detail::functor1::call(compute_findMSB_64, x); + } + }; +# endif +# endif//GLM_HAS_BITSCAN_WINDOWS +}//namespace detail + + // uaddCarry + GLM_FUNC_QUALIFIER uint uaddCarry(uint const& x, uint const& y, uint & Carry) + { + detail::uint64 const Value64(static_cast(x) + static_cast(y)); + detail::uint64 const Max32((static_cast(1) << static_cast(32)) - static_cast(1)); + Carry = Value64 > Max32 ? 1u : 0u; + return static_cast(Value64 % (Max32 + static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER vec uaddCarry(vec const& x, vec const& y, vec& Carry) + { + vec Value64(vec(x) + vec(y)); + vec Max32((static_cast(1) << static_cast(32)) - static_cast(1)); + Carry = mix(vec(0), vec(1), greaterThan(Value64, Max32)); + return vec(Value64 % (Max32 + static_cast(1))); + } + + // usubBorrow + GLM_FUNC_QUALIFIER uint usubBorrow(uint const& x, uint const& y, uint & Borrow) + { + Borrow = x >= y ? static_cast(0) : static_cast(1); + if(y >= x) + return y - x; + else + return static_cast((static_cast(1) << static_cast(32)) + (static_cast(y) - static_cast(x))); + } + + template + GLM_FUNC_QUALIFIER vec usubBorrow(vec const& x, vec const& y, vec& Borrow) + { + Borrow = mix(vec(1), vec(0), greaterThanEqual(x, y)); + vec const YgeX(y - x); + vec const XgeY(vec((static_cast(1) << static_cast(32)) + (vec(y) - vec(x)))); + return mix(XgeY, YgeX, greaterThanEqual(y, x)); + } + + // umulExtended + GLM_FUNC_QUALIFIER void umulExtended(uint const& x, uint const& y, uint & msb, uint & lsb) + { + detail::uint64 Value64 = static_cast(x) * static_cast(y); + msb = static_cast(Value64 >> static_cast(32)); + lsb = static_cast(Value64); + } + + template + GLM_FUNC_QUALIFIER void umulExtended(vec const& x, vec const& y, vec& msb, vec& lsb) + { + vec Value64(vec(x) * vec(y)); + msb = vec(Value64 >> static_cast(32)); + lsb = vec(Value64); + } + + // imulExtended + GLM_FUNC_QUALIFIER void imulExtended(int x, int y, int& msb, int& lsb) + { + detail::int64 Value64 = static_cast(x) * static_cast(y); + msb = static_cast(Value64 >> static_cast(32)); + lsb = static_cast(Value64); + } + + template + GLM_FUNC_QUALIFIER void imulExtended(vec const& x, vec const& y, vec& msb, vec& lsb) + { + vec Value64(vec(x) * vec(y)); + lsb = vec(Value64 & static_cast(0xFFFFFFFF)); + msb = vec((Value64 >> static_cast(32)) & static_cast(0xFFFFFFFF)); + } + + // bitfieldExtract + template + GLM_FUNC_QUALIFIER genIUType bitfieldExtract(genIUType Value, int Offset, int Bits) + { + return bitfieldExtract(vec<1, genIUType>(Value), Offset, Bits).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldExtract(vec const& Value, int Offset, int Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldExtract' only accept integer inputs"); + + return (Value >> static_cast(Offset)) & static_cast(detail::mask(Bits)); + } + + // bitfieldInsert + template + GLM_FUNC_QUALIFIER genIUType bitfieldInsert(genIUType const& Base, genIUType const& Insert, int Offset, int Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldInsert' only accept integer values"); + + return bitfieldInsert(vec<1, genIUType>(Base), vec<1, genIUType>(Insert), Offset, Bits).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldInsert(vec const& Base, vec const& Insert, int Offset, int Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldInsert' only accept integer values"); + + T const Mask = detail::mask(static_cast(Bits)) << Offset; + return (Base & ~Mask) | ((Insert << static_cast(Offset)) & Mask); + } + +#if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4309) +#endif + + // bitfieldReverse + template + GLM_FUNC_QUALIFIER genIUType bitfieldReverse(genIUType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldReverse' only accept integer values"); + + return bitfieldReverse(glm::vec<1, genIUType, glm::defaultp>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec bitfieldReverse(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldReverse' only accept integer values"); + + vec x(v); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 2>::call(x, static_cast(0x5555555555555555ull), static_cast( 1)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 4>::call(x, static_cast(0x3333333333333333ull), static_cast( 2)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 8>::call(x, static_cast(0x0F0F0F0F0F0F0F0Full), static_cast( 4)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 16>::call(x, static_cast(0x00FF00FF00FF00FFull), static_cast( 8)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 32>::call(x, static_cast(0x0000FFFF0000FFFFull), static_cast(16)); + x = detail::compute_bitfieldReverseStep::value, sizeof(T) * 8>= 64>::call(x, static_cast(0x00000000FFFFFFFFull), static_cast(32)); + return x; + } + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif + + // bitCount + template + GLM_FUNC_QUALIFIER int bitCount(genIUType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitCount' only accept integer values"); + + return bitCount(glm::vec<1, genIUType, glm::defaultp>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec bitCount(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitCount' only accept integer values"); + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4310) //cast truncates constant value +# endif + + vec::type, Q> x(v); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 2>::call(x, typename detail::make_unsigned::type(0x5555555555555555ull), typename detail::make_unsigned::type( 1)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 4>::call(x, typename detail::make_unsigned::type(0x3333333333333333ull), typename detail::make_unsigned::type( 2)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 8>::call(x, typename detail::make_unsigned::type(0x0F0F0F0F0F0F0F0Full), typename detail::make_unsigned::type( 4)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 16>::call(x, typename detail::make_unsigned::type(0x00FF00FF00FF00FFull), typename detail::make_unsigned::type( 8)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 32>::call(x, typename detail::make_unsigned::type(0x0000FFFF0000FFFFull), typename detail::make_unsigned::type(16)); + x = detail::compute_bitfieldBitCountStep::type, Q, detail::is_aligned::value, sizeof(T) * 8>= 64>::call(x, typename detail::make_unsigned::type(0x00000000FFFFFFFFull), typename detail::make_unsigned::type(32)); + return vec(x); + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif + } + + // findLSB + template + GLM_FUNC_QUALIFIER int findLSB(genIUType Value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findLSB' only accept integer values"); + + return detail::compute_findLSB::call(Value); + } + + template + GLM_FUNC_QUALIFIER vec findLSB(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findLSB' only accept integer values"); + + return detail::functor1::call(findLSB, x); + } + + // findMSB + template + GLM_FUNC_QUALIFIER int findMSB(genIUType v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findMSB' only accept integer values"); + + return findMSB(vec<1, genIUType>(v)).x; + } + + template + GLM_FUNC_QUALIFIER vec findMSB(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findMSB' only accept integer values"); + + return detail::compute_findMSB_vec(sizeof(T) * 8)>::call(v); + } +}//namespace glm + +#if !GLM_HAS_EXTENDED_INTEGER_TYPE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# endif +# if (GLM_COMPILER & GLM_COMPILER_CLANG) +# pragma clang diagnostic pop +# endif +#endif + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_integer_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/func_integer_simd.inl b/thirdparty/glm/glm/detail/func_integer_simd.inl new file mode 100644 index 0000000..5600c84 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_integer_simd.inl @@ -0,0 +1,65 @@ +#include "../simd/integer.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_bitfieldReverseStep<4, uint, Q, true, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v, uint Mask, uint Shift) + { + __m128i const set0 = v.data; + + __m128i const set1 = _mm_set1_epi32(static_cast(Mask)); + __m128i const and1 = _mm_and_si128(set0, set1); + __m128i const sft1 = _mm_slli_epi32(and1, static_cast(Shift)); + + __m128i const set2 = _mm_andnot_si128(set0, _mm_set1_epi32(-1)); + __m128i const and2 = _mm_and_si128(set0, set2); + __m128i const sft2 = _mm_srai_epi32(and2, static_cast(Shift)); + + __m128i const or0 = _mm_or_si128(sft1, sft2); + + return or0; + } + }; + + template + struct compute_bitfieldBitCountStep<4, uint, Q, true, true> + { + GLM_FUNC_QUALIFIER static vec<4, uint, Q> call(vec<4, uint, Q> const& v, uint Mask, uint Shift) + { + __m128i const set0 = v.data; + + __m128i const set1 = _mm_set1_epi32(static_cast(Mask)); + __m128i const and0 = _mm_and_si128(set0, set1); + __m128i const sft0 = _mm_slli_epi32(set0, static_cast(Shift)); + __m128i const and1 = _mm_and_si128(sft0, set1); + __m128i const add0 = _mm_add_epi32(and0, and1); + + return add0; + } + }; +}//namespace detail + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template<> + GLM_FUNC_QUALIFIER int bitCount(uint x) + { + return _mm_popcnt_u32(x); + } + +# if(GLM_MODEL == GLM_MODEL_64) + template<> + GLM_FUNC_QUALIFIER int bitCount(detail::uint64 x) + { + return static_cast(_mm_popcnt_u64(x)); + } +# endif//GLM_MODEL +# endif//GLM_ARCH + +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/detail/func_matrix.inl b/thirdparty/glm/glm/detail/func_matrix.inl new file mode 100644 index 0000000..081761f --- /dev/null +++ b/thirdparty/glm/glm/detail/func_matrix.inl @@ -0,0 +1,443 @@ +#include "../geometric.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct compute_matrixCompMult + { + GLM_FUNC_QUALIFIER static mat call(mat const& x, mat const& y) + { + mat Result(1); + for(length_t i = 0; i < Result.length(); ++i) + Result[i] = x[i] * y[i]; + return Result; + } + }; + + template + struct compute_matrixCompMult_type { + GLM_FUNC_QUALIFIER static mat call(mat const& x, mat const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, + "'matrixCompMult' only accept floating-point inputs, include to discard this restriction."); + return detail::compute_matrixCompMult::value>::call(x, y); + } + }; + + template + struct compute_outerProduct { + GLM_FUNC_QUALIFIER static typename detail::outerProduct_trait::type call(vec const& c, vec const& r) + { + typename detail::outerProduct_trait::type m(0); + for(length_t i = 0; i < m.length(); ++i) + m[i] = c * r[i]; + return m; + } + }; + + template + struct compute_outerProduct_type { + GLM_FUNC_QUALIFIER static typename detail::outerProduct_trait::type call(vec const& c, vec const& r) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, + "'outerProduct' only accept floating-point inputs, include to discard this restriction."); + + return detail::compute_outerProduct::call(c, r); + } + }; + + template + struct compute_transpose{}; + + template + struct compute_transpose<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m) + { + mat<2, 2, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + return Result; + } + }; + + template + struct compute_transpose<2, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 2, T, Q> call(mat<2, 3, T, Q> const& m) + { + mat<3,2, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + return Result; + } + }; + + template + struct compute_transpose<2, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 2, T, Q> call(mat<2, 4, T, Q> const& m) + { + mat<4, 2, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + return Result; + } + }; + + template + struct compute_transpose<3, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 3, T, Q> call(mat<3, 2, T, Q> const& m) + { + mat<2, 3, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + return Result; + } + }; + + template + struct compute_transpose<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m) + { + mat<3, 3, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + return Result; + } + }; + + template + struct compute_transpose<3, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 3, T, Q> call(mat<3, 4, T, Q> const& m) + { + mat<4, 3, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + return Result; + } + }; + + template + struct compute_transpose<4, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 4, T, Q> call(mat<4, 2, T, Q> const& m) + { + mat<2, 4, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + return Result; + } + }; + + template + struct compute_transpose<4, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 4, T, Q> call(mat<4, 3, T, Q> const& m) + { + mat<3, 4, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + return Result; + } + }; + + template + struct compute_transpose<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m) + { + mat<4, 4, T, Q> Result(1); + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + Result[3][3] = m[3][3]; + return Result; + } + }; + + template + struct compute_transpose_type { + GLM_FUNC_QUALIFIER static mat call(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, + "'transpose' only accept floating-point inputs, include to discard this restriction."); + return detail::compute_transpose::value>::call(m); + } + }; + + template + struct compute_determinant{}; + + template + struct compute_determinant<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<2, 2, T, Q> const& m) + { + return m[0][0] * m[1][1] - m[1][0] * m[0][1]; + } + }; + + template + struct compute_determinant<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<3, 3, T, Q> const& m) + { + return + + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) + - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) + + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]); + } + }; + + template + struct compute_determinant<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static T call(mat<4, 4, T, Q> const& m) + { + T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + vec<4, T, Q> DetCof( + + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + return + m[0][0] * DetCof[0] + m[0][1] * DetCof[1] + + m[0][2] * DetCof[2] + m[0][3] * DetCof[3]; + } + }; + + template + struct compute_determinant_type{ + + GLM_FUNC_QUALIFIER static T call(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, + "'determinant' only accept floating-point inputs, include to discard this restriction."); + return detail::compute_determinant::value>::call(m); + } + }; + + template + struct compute_inverse{}; + + template + struct compute_inverse<2, 2, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<2, 2, T, Q> call(mat<2, 2, T, Q> const& m) + { + T OneOverDeterminant = static_cast(1) / ( + + m[0][0] * m[1][1] + - m[1][0] * m[0][1]); + + mat<2, 2, T, Q> Inverse( + + m[1][1] * OneOverDeterminant, + - m[0][1] * OneOverDeterminant, + - m[1][0] * OneOverDeterminant, + + m[0][0] * OneOverDeterminant); + + return Inverse; + } + }; + + template + struct compute_inverse<3, 3, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<3, 3, T, Q> call(mat<3, 3, T, Q> const& m) + { + T OneOverDeterminant = static_cast(1) / ( + + m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) + - m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) + + m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])); + + mat<3, 3, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDeterminant; + Inverse[1][0] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDeterminant; + Inverse[2][0] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDeterminant; + Inverse[0][1] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDeterminant; + Inverse[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDeterminant; + Inverse[2][1] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDeterminant; + Inverse[0][2] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDeterminant; + Inverse[1][2] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDeterminant; + Inverse[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDeterminant; + + return Inverse; + } + }; + + template + struct compute_inverse<4, 4, T, Q, Aligned> + { + GLM_FUNC_QUALIFIER static mat<4, 4, T, Q> call(mat<4, 4, T, Q> const& m) + { + T Coef00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T Coef02 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + T Coef03 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + T Coef04 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T Coef06 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + T Coef07 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + T Coef08 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T Coef10 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + T Coef11 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + T Coef12 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T Coef14 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + T Coef15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + T Coef16 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T Coef18 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + T Coef19 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + T Coef20 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + T Coef22 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + T Coef23 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + vec<4, T, Q> Fac0(Coef00, Coef00, Coef02, Coef03); + vec<4, T, Q> Fac1(Coef04, Coef04, Coef06, Coef07); + vec<4, T, Q> Fac2(Coef08, Coef08, Coef10, Coef11); + vec<4, T, Q> Fac3(Coef12, Coef12, Coef14, Coef15); + vec<4, T, Q> Fac4(Coef16, Coef16, Coef18, Coef19); + vec<4, T, Q> Fac5(Coef20, Coef20, Coef22, Coef23); + + vec<4, T, Q> Vec0(m[1][0], m[0][0], m[0][0], m[0][0]); + vec<4, T, Q> Vec1(m[1][1], m[0][1], m[0][1], m[0][1]); + vec<4, T, Q> Vec2(m[1][2], m[0][2], m[0][2], m[0][2]); + vec<4, T, Q> Vec3(m[1][3], m[0][3], m[0][3], m[0][3]); + + vec<4, T, Q> Inv0(Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2); + vec<4, T, Q> Inv1(Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4); + vec<4, T, Q> Inv2(Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5); + vec<4, T, Q> Inv3(Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5); + + vec<4, T, Q> SignA(+1, -1, +1, -1); + vec<4, T, Q> SignB(-1, +1, -1, +1); + mat<4, 4, T, Q> Inverse(Inv0 * SignA, Inv1 * SignB, Inv2 * SignA, Inv3 * SignB); + + vec<4, T, Q> Row0(Inverse[0][0], Inverse[1][0], Inverse[2][0], Inverse[3][0]); + + vec<4, T, Q> Dot0(m[0] * Row0); + T Dot1 = (Dot0.x + Dot0.y) + (Dot0.z + Dot0.w); + + T OneOverDeterminant = static_cast(1) / Dot1; + + return Inverse * OneOverDeterminant; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER mat matrixCompMult(mat const& x, mat const& y) + { + return detail::compute_matrixCompMult_type::is_iec559, detail::is_aligned::value>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER typename detail::outerProduct_trait::type outerProduct(vec const& c, vec const& r) + { + return detail::compute_outerProduct_type::is_iec559>::call(c, r); + } + + template + GLM_FUNC_QUALIFIER typename mat::transpose_type transpose(mat const& m) + { + return detail::compute_transpose_type::is_iec559, detail::is_aligned::value>::call(m); + } + + template + GLM_FUNC_QUALIFIER T determinant(mat const& m) + { + return detail::compute_determinant_type::is_iec559, detail::is_aligned::value>::call(m); + } + + template + GLM_FUNC_QUALIFIER mat inverse(mat const& m) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_GENTYPE, "'inverse' only accept floating-point inputs"); + return detail::compute_inverse::value>::call(m); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_matrix_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/func_matrix_simd.inl b/thirdparty/glm/glm/detail/func_matrix_simd.inl new file mode 100644 index 0000000..b9bb461 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_matrix_simd.inl @@ -0,0 +1,252 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#include "type_mat4x4.hpp" +#include "../geometric.hpp" +#include "../simd/matrix.h" +#include + +namespace glm{ +namespace detail +{ +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template + struct compute_matrixCompMult<4, 4, float, Q, true> + { + GLM_STATIC_ASSERT(detail::is_aligned::value, "Specialization requires aligned"); + + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& x, mat<4, 4, float, Q> const& y) + { + mat<4, 4, float, Q> Result; + glm_mat4_matrixCompMult( + &x[0].data, + &y[0].data, + &Result[0].data); + return Result; + } + }; +# endif + + template + struct compute_transpose<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& m) + { + mat<4, 4, float, Q> Result; + glm_mat4_transpose(&m[0].data, &Result[0].data); + return Result; + } + }; + + template + struct compute_determinant<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static float call(mat<4, 4, float, Q> const& m) + { + return _mm_cvtss_f32(glm_mat4_determinant(&m[0].data)); + } + }; + + template + struct compute_inverse<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& m) + { + mat<4, 4, float, Q> Result; + glm_mat4_inverse(&m[0].data, &Result[0].data); + return Result; + } + }; +}//namespace detail + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_lowp> outerProduct<4, 4, float, aligned_lowp>(vec<4, float, aligned_lowp> const& c, vec<4, float, aligned_lowp> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_lowp> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } + + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_mediump> outerProduct<4, 4, float, aligned_mediump>(vec<4, float, aligned_mediump> const& c, vec<4, float, aligned_mediump> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_mediump> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } + + template<> + GLM_FUNC_QUALIFIER mat<4, 4, float, aligned_highp> outerProduct<4, 4, float, aligned_highp>(vec<4, float, aligned_highp> const& c, vec<4, float, aligned_highp> const& r) + { + __m128 NativeResult[4]; + glm_mat4_outerProduct(c.data, r.data, NativeResult); + mat<4, 4, float, aligned_highp> Result; + std::memcpy(&Result[0], &NativeResult[0], sizeof(Result)); + return Result; + } +# endif +}//namespace glm + +#elif GLM_ARCH & GLM_ARCH_NEON_BIT + +namespace glm { +#if GLM_LANG & GLM_LANG_CXX11_FLAG + template + GLM_FUNC_QUALIFIER + typename std::enable_if::value, mat<4, 4, float, Q>>::type + operator*(mat<4, 4, float, Q> const & m1, mat<4, 4, float, Q> const & m2) + { + auto MulRow = [&](int l) { + float32x4_t const SrcA = m2[l].data; + + float32x4_t r = neon::mul_lane(m1[0].data, SrcA, 0); + r = neon::madd_lane(r, m1[1].data, SrcA, 1); + r = neon::madd_lane(r, m1[2].data, SrcA, 2); + r = neon::madd_lane(r, m1[3].data, SrcA, 3); + + return r; + }; + + mat<4, 4, float, aligned_highp> Result; + Result[0].data = MulRow(0); + Result[1].data = MulRow(1); + Result[2].data = MulRow(2); + Result[3].data = MulRow(3); + + return Result; + } +#endif // CXX11 + +namespace detail +{ + template + struct compute_inverse<4, 4, float, Q, true> + { + GLM_FUNC_QUALIFIER static mat<4, 4, float, Q> call(mat<4, 4, float, Q> const& m) + { + float32x4_t const& m0 = m[0].data; + float32x4_t const& m1 = m[1].data; + float32x4_t const& m2 = m[2].data; + float32x4_t const& m3 = m[3].data; + + // m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + float32x4_t Fac0; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 2), neon::dup_lane(m1, 2)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 3), 3, m2, 3); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 2), 3, m2, 2); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 3), neon::dup_lane(m1, 3)); + Fac0 = w0 * w1 - w2 * w3; + } + + // m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + float32x4_t Fac1; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 1), neon::dup_lane(m1, 1)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 3), 3, m2, 3); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 1), 3, m2, 1); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 3), neon::dup_lane(m1, 3)); + Fac1 = w0 * w1 - w2 * w3; + } + + // m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + float32x4_t Fac2; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 1), neon::dup_lane(m1, 1)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 2), 3, m2, 2); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 1), 3, m2, 1); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 2), neon::dup_lane(m1, 2)); + Fac2 = w0 * w1 - w2 * w3; + } + + // m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + float32x4_t Fac3; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 0), neon::dup_lane(m1, 0)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 3), 3, m2, 3); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 0), 3, m2, 0); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 3), neon::dup_lane(m1, 3)); + Fac3 = w0 * w1 - w2 * w3; + } + + // m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + float32x4_t Fac4; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 0), neon::dup_lane(m1, 0)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 2), 3, m2, 2); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 0), 3, m2, 0); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 2), neon::dup_lane(m1, 2)); + Fac4 = w0 * w1 - w2 * w3; + } + + // m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + float32x4_t Fac5; + { + float32x4_t w0 = vcombine_f32(neon::dup_lane(m2, 0), neon::dup_lane(m1, 0)); + float32x4_t w1 = neon::copy_lane(neon::dupq_lane(m3, 1), 3, m2, 1); + float32x4_t w2 = neon::copy_lane(neon::dupq_lane(m3, 0), 3, m2, 0); + float32x4_t w3 = vcombine_f32(neon::dup_lane(m2, 1), neon::dup_lane(m1, 1)); + Fac5 = w0 * w1 - w2 * w3; + } + + float32x4_t Vec0 = neon::copy_lane(neon::dupq_lane(m0, 0), 0, m1, 0); // (m[1][0], m[0][0], m[0][0], m[0][0]); + float32x4_t Vec1 = neon::copy_lane(neon::dupq_lane(m0, 1), 0, m1, 1); // (m[1][1], m[0][1], m[0][1], m[0][1]); + float32x4_t Vec2 = neon::copy_lane(neon::dupq_lane(m0, 2), 0, m1, 2); // (m[1][2], m[0][2], m[0][2], m[0][2]); + float32x4_t Vec3 = neon::copy_lane(neon::dupq_lane(m0, 3), 0, m1, 3); // (m[1][3], m[0][3], m[0][3], m[0][3]); + + float32x4_t Inv0 = Vec1 * Fac0 - Vec2 * Fac1 + Vec3 * Fac2; + float32x4_t Inv1 = Vec0 * Fac0 - Vec2 * Fac3 + Vec3 * Fac4; + float32x4_t Inv2 = Vec0 * Fac1 - Vec1 * Fac3 + Vec3 * Fac5; + float32x4_t Inv3 = Vec0 * Fac2 - Vec1 * Fac4 + Vec2 * Fac5; + + float32x4_t r0 = float32x4_t{-1, +1, -1, +1} * Inv0; + float32x4_t r1 = float32x4_t{+1, -1, +1, -1} * Inv1; + float32x4_t r2 = float32x4_t{-1, +1, -1, +1} * Inv2; + float32x4_t r3 = float32x4_t{+1, -1, +1, -1} * Inv3; + + float32x4_t det = neon::mul_lane(r0, m0, 0); + det = neon::madd_lane(det, r1, m0, 1); + det = neon::madd_lane(det, r2, m0, 2); + det = neon::madd_lane(det, r3, m0, 3); + + float32x4_t rdet = vdupq_n_f32(1 / vgetq_lane_f32(det, 0)); + + mat<4, 4, float, Q> r; + r[0].data = vmulq_f32(r0, rdet); + r[1].data = vmulq_f32(r1, rdet); + r[2].data = vmulq_f32(r2, rdet); + r[3].data = vmulq_f32(r3, rdet); + return r; + } + }; +}//namespace detail +}//namespace glm +#endif diff --git a/thirdparty/glm/glm/detail/func_packing.inl b/thirdparty/glm/glm/detail/func_packing.inl new file mode 100644 index 0000000..234b093 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_packing.inl @@ -0,0 +1,189 @@ +/// @ref core +/// @file glm/detail/func_packing.inl + +#include "../common.hpp" +#include "type_half.hpp" + +namespace glm +{ + GLM_FUNC_QUALIFIER uint packUnorm2x16(vec2 const& v) + { + union + { + unsigned short in[2]; + uint out; + } u; + + vec<2, unsigned short, defaultp> result(round(clamp(v, 0.0f, 1.0f) * 65535.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x16(uint p) + { + union + { + uint in; + unsigned short out[2]; + } u; + + u.in = p; + + return vec2(u.out[0], u.out[1]) * 1.5259021896696421759365224689097e-5f; + } + + GLM_FUNC_QUALIFIER uint packSnorm2x16(vec2 const& v) + { + union + { + signed short in[2]; + uint out; + } u; + + vec<2, short, defaultp> result(round(clamp(v, -1.0f, 1.0f) * 32767.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackSnorm2x16(uint p) + { + union + { + uint in; + signed short out[2]; + } u; + + u.in = p; + + return clamp(vec2(u.out[0], u.out[1]) * 3.0518509475997192297128208258309e-5f, -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint packUnorm4x8(vec4 const& v) + { + union + { + unsigned char in[4]; + uint out; + } u; + + vec<4, unsigned char, defaultp> result(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + u.in[2] = result[2]; + u.in[3] = result[3]; + + return u.out; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x8(uint p) + { + union + { + uint in; + unsigned char out[4]; + } u; + + u.in = p; + + return vec4(u.out[0], u.out[1], u.out[2], u.out[3]) * 0.0039215686274509803921568627451f; + } + + GLM_FUNC_QUALIFIER uint packSnorm4x8(vec4 const& v) + { + union + { + signed char in[4]; + uint out; + } u; + + vec<4, signed char, defaultp> result(round(clamp(v, -1.0f, 1.0f) * 127.0f)); + + u.in[0] = result[0]; + u.in[1] = result[1]; + u.in[2] = result[2]; + u.in[3] = result[3]; + + return u.out; + } + + GLM_FUNC_QUALIFIER glm::vec4 unpackSnorm4x8(uint p) + { + union + { + uint in; + signed char out[4]; + } u; + + u.in = p; + + return clamp(vec4(u.out[0], u.out[1], u.out[2], u.out[3]) * 0.0078740157480315f, -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER double packDouble2x32(uvec2 const& v) + { + union + { + uint in[2]; + double out; + } u; + + u.in[0] = v[0]; + u.in[1] = v[1]; + + return u.out; + } + + GLM_FUNC_QUALIFIER uvec2 unpackDouble2x32(double v) + { + union + { + double in; + uint out[2]; + } u; + + u.in = v; + + return uvec2(u.out[0], u.out[1]); + } + + GLM_FUNC_QUALIFIER uint packHalf2x16(vec2 const& v) + { + union + { + signed short in[2]; + uint out; + } u; + + u.in[0] = detail::toFloat16(v.x); + u.in[1] = detail::toFloat16(v.y); + + return u.out; + } + + GLM_FUNC_QUALIFIER vec2 unpackHalf2x16(uint v) + { + union + { + uint in; + signed short out[2]; + } u; + + u.in = v; + + return vec2( + detail::toFloat32(u.out[0]), + detail::toFloat32(u.out[1])); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_packing_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/func_packing_simd.inl b/thirdparty/glm/glm/detail/func_packing_simd.inl new file mode 100644 index 0000000..fd0fe8b --- /dev/null +++ b/thirdparty/glm/glm/detail/func_packing_simd.inl @@ -0,0 +1,6 @@ +namespace glm{ +namespace detail +{ + +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/func_trigonometric.inl b/thirdparty/glm/glm/detail/func_trigonometric.inl new file mode 100644 index 0000000..9e6d9cf --- /dev/null +++ b/thirdparty/glm/glm/detail/func_trigonometric.inl @@ -0,0 +1,197 @@ +#include "_vectorize.hpp" +#include +#include + +namespace glm +{ + // radians + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType radians(genType degrees) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'radians' only accept floating-point input"); + + return degrees * static_cast(0.01745329251994329576923690768489); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec radians(vec const& v) + { + return detail::functor1::call(radians, v); + } + + // degrees + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType degrees(genType radians) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'degrees' only accept floating-point input"); + + return radians * static_cast(57.295779513082320876798154814105); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec degrees(vec const& v) + { + return detail::functor1::call(degrees, v); + } + + // sin + using ::std::sin; + + template + GLM_FUNC_QUALIFIER vec sin(vec const& v) + { + return detail::functor1::call(sin, v); + } + + // cos + using std::cos; + + template + GLM_FUNC_QUALIFIER vec cos(vec const& v) + { + return detail::functor1::call(cos, v); + } + + // tan + using std::tan; + + template + GLM_FUNC_QUALIFIER vec tan(vec const& v) + { + return detail::functor1::call(tan, v); + } + + // asin + using std::asin; + + template + GLM_FUNC_QUALIFIER vec asin(vec const& v) + { + return detail::functor1::call(asin, v); + } + + // acos + using std::acos; + + template + GLM_FUNC_QUALIFIER vec acos(vec const& v) + { + return detail::functor1::call(acos, v); + } + + // atan + template + GLM_FUNC_QUALIFIER genType atan(genType y, genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'atan' only accept floating-point input"); + + return ::std::atan2(y, x); + } + + template + GLM_FUNC_QUALIFIER vec atan(vec const& y, vec const& x) + { + return detail::functor2::call(::std::atan2, y, x); + } + + using std::atan; + + template + GLM_FUNC_QUALIFIER vec atan(vec const& v) + { + return detail::functor1::call(atan, v); + } + + // sinh + using std::sinh; + + template + GLM_FUNC_QUALIFIER vec sinh(vec const& v) + { + return detail::functor1::call(sinh, v); + } + + // cosh + using std::cosh; + + template + GLM_FUNC_QUALIFIER vec cosh(vec const& v) + { + return detail::functor1::call(cosh, v); + } + + // tanh + using std::tanh; + + template + GLM_FUNC_QUALIFIER vec tanh(vec const& v) + { + return detail::functor1::call(tanh, v); + } + + // asinh +# if GLM_HAS_CXX11_STL + using std::asinh; +# else + template + GLM_FUNC_QUALIFIER genType asinh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'asinh' only accept floating-point input"); + + return (x < static_cast(0) ? static_cast(-1) : (x > static_cast(0) ? static_cast(1) : static_cast(0))) * log(std::abs(x) + sqrt(static_cast(1) + x * x)); + } +# endif + + template + GLM_FUNC_QUALIFIER vec asinh(vec const& v) + { + return detail::functor1::call(asinh, v); + } + + // acosh +# if GLM_HAS_CXX11_STL + using std::acosh; +# else + template + GLM_FUNC_QUALIFIER genType acosh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acosh' only accept floating-point input"); + + if(x < static_cast(1)) + return static_cast(0); + return log(x + sqrt(x * x - static_cast(1))); + } +# endif + + template + GLM_FUNC_QUALIFIER vec acosh(vec const& v) + { + return detail::functor1::call(acosh, v); + } + + // atanh +# if GLM_HAS_CXX11_STL + using std::atanh; +# else + template + GLM_FUNC_QUALIFIER genType atanh(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'atanh' only accept floating-point input"); + + if(std::abs(x) >= static_cast(1)) + return 0; + return static_cast(0.5) * log((static_cast(1) + x) / (static_cast(1) - x)); + } +# endif + + template + GLM_FUNC_QUALIFIER vec atanh(vec const& v) + { + return detail::functor1::call(atanh, v); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_trigonometric_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/func_trigonometric_simd.inl b/thirdparty/glm/glm/detail/func_trigonometric_simd.inl new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/glm/glm/detail/func_vector_relational.inl b/thirdparty/glm/glm/detail/func_vector_relational.inl new file mode 100644 index 0000000..80c9e87 --- /dev/null +++ b/thirdparty/glm/glm/detail/func_vector_relational.inl @@ -0,0 +1,87 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec lessThan(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] < y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec lessThanEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] <= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec greaterThan(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] > y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec greaterThanEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] >= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] == y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = x[i] != y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool any(vec const& v) + { + bool Result = false; + for(length_t i = 0; i < L; ++i) + Result = Result || v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool all(vec const& v) + { + bool Result = true; + for(length_t i = 0; i < L; ++i) + Result = Result && v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec not_(vec const& v) + { + vec Result(true); + for(length_t i = 0; i < L; ++i) + Result[i] = !v[i]; + return Result; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "func_vector_relational_simd.inl" +#endif diff --git a/thirdparty/glm/glm/detail/func_vector_relational_simd.inl b/thirdparty/glm/glm/detail/func_vector_relational_simd.inl new file mode 100644 index 0000000..fd0fe8b --- /dev/null +++ b/thirdparty/glm/glm/detail/func_vector_relational_simd.inl @@ -0,0 +1,6 @@ +namespace glm{ +namespace detail +{ + +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/glm.cpp b/thirdparty/glm/glm/detail/glm.cpp new file mode 100644 index 0000000..e0755bd --- /dev/null +++ b/thirdparty/glm/glm/detail/glm.cpp @@ -0,0 +1,263 @@ +/// @ref core +/// @file glm/glm.cpp + +#ifndef GLM_ENABLE_EXPERIMENTAL +#define GLM_ENABLE_EXPERIMENTAL +#endif +#include +#include +#include +#include +#include +#include + +namespace glm +{ +// tvec1 type explicit instantiation +template struct vec<1, uint8, lowp>; +template struct vec<1, uint16, lowp>; +template struct vec<1, uint32, lowp>; +template struct vec<1, uint64, lowp>; +template struct vec<1, int8, lowp>; +template struct vec<1, int16, lowp>; +template struct vec<1, int32, lowp>; +template struct vec<1, int64, lowp>; +template struct vec<1, float32, lowp>; +template struct vec<1, float64, lowp>; + +template struct vec<1, uint8, mediump>; +template struct vec<1, uint16, mediump>; +template struct vec<1, uint32, mediump>; +template struct vec<1, uint64, mediump>; +template struct vec<1, int8, mediump>; +template struct vec<1, int16, mediump>; +template struct vec<1, int32, mediump>; +template struct vec<1, int64, mediump>; +template struct vec<1, float32, mediump>; +template struct vec<1, float64, mediump>; + +template struct vec<1, uint8, highp>; +template struct vec<1, uint16, highp>; +template struct vec<1, uint32, highp>; +template struct vec<1, uint64, highp>; +template struct vec<1, int8, highp>; +template struct vec<1, int16, highp>; +template struct vec<1, int32, highp>; +template struct vec<1, int64, highp>; +template struct vec<1, float32, highp>; +template struct vec<1, float64, highp>; + +// tvec2 type explicit instantiation +template struct vec<2, uint8, lowp>; +template struct vec<2, uint16, lowp>; +template struct vec<2, uint32, lowp>; +template struct vec<2, uint64, lowp>; +template struct vec<2, int8, lowp>; +template struct vec<2, int16, lowp>; +template struct vec<2, int32, lowp>; +template struct vec<2, int64, lowp>; +template struct vec<2, float32, lowp>; +template struct vec<2, float64, lowp>; + +template struct vec<2, uint8, mediump>; +template struct vec<2, uint16, mediump>; +template struct vec<2, uint32, mediump>; +template struct vec<2, uint64, mediump>; +template struct vec<2, int8, mediump>; +template struct vec<2, int16, mediump>; +template struct vec<2, int32, mediump>; +template struct vec<2, int64, mediump>; +template struct vec<2, float32, mediump>; +template struct vec<2, float64, mediump>; + +template struct vec<2, uint8, highp>; +template struct vec<2, uint16, highp>; +template struct vec<2, uint32, highp>; +template struct vec<2, uint64, highp>; +template struct vec<2, int8, highp>; +template struct vec<2, int16, highp>; +template struct vec<2, int32, highp>; +template struct vec<2, int64, highp>; +template struct vec<2, float32, highp>; +template struct vec<2, float64, highp>; + +// tvec3 type explicit instantiation +template struct vec<3, uint8, lowp>; +template struct vec<3, uint16, lowp>; +template struct vec<3, uint32, lowp>; +template struct vec<3, uint64, lowp>; +template struct vec<3, int8, lowp>; +template struct vec<3, int16, lowp>; +template struct vec<3, int32, lowp>; +template struct vec<3, int64, lowp>; +template struct vec<3, float32, lowp>; +template struct vec<3, float64, lowp>; + +template struct vec<3, uint8, mediump>; +template struct vec<3, uint16, mediump>; +template struct vec<3, uint32, mediump>; +template struct vec<3, uint64, mediump>; +template struct vec<3, int8, mediump>; +template struct vec<3, int16, mediump>; +template struct vec<3, int32, mediump>; +template struct vec<3, int64, mediump>; +template struct vec<3, float32, mediump>; +template struct vec<3, float64, mediump>; + +template struct vec<3, uint8, highp>; +template struct vec<3, uint16, highp>; +template struct vec<3, uint32, highp>; +template struct vec<3, uint64, highp>; +template struct vec<3, int8, highp>; +template struct vec<3, int16, highp>; +template struct vec<3, int32, highp>; +template struct vec<3, int64, highp>; +template struct vec<3, float32, highp>; +template struct vec<3, float64, highp>; + +// tvec4 type explicit instantiation +template struct vec<4, uint8, lowp>; +template struct vec<4, uint16, lowp>; +template struct vec<4, uint32, lowp>; +template struct vec<4, uint64, lowp>; +template struct vec<4, int8, lowp>; +template struct vec<4, int16, lowp>; +template struct vec<4, int32, lowp>; +template struct vec<4, int64, lowp>; +template struct vec<4, float32, lowp>; +template struct vec<4, float64, lowp>; + +template struct vec<4, uint8, mediump>; +template struct vec<4, uint16, mediump>; +template struct vec<4, uint32, mediump>; +template struct vec<4, uint64, mediump>; +template struct vec<4, int8, mediump>; +template struct vec<4, int16, mediump>; +template struct vec<4, int32, mediump>; +template struct vec<4, int64, mediump>; +template struct vec<4, float32, mediump>; +template struct vec<4, float64, mediump>; + +template struct vec<4, uint8, highp>; +template struct vec<4, uint16, highp>; +template struct vec<4, uint32, highp>; +template struct vec<4, uint64, highp>; +template struct vec<4, int8, highp>; +template struct vec<4, int16, highp>; +template struct vec<4, int32, highp>; +template struct vec<4, int64, highp>; +template struct vec<4, float32, highp>; +template struct vec<4, float64, highp>; + +// tmat2x2 type explicit instantiation +template struct mat<2, 2, float32, lowp>; +template struct mat<2, 2, float64, lowp>; + +template struct mat<2, 2, float32, mediump>; +template struct mat<2, 2, float64, mediump>; + +template struct mat<2, 2, float32, highp>; +template struct mat<2, 2, float64, highp>; + +// tmat2x3 type explicit instantiation +template struct mat<2, 3, float32, lowp>; +template struct mat<2, 3, float64, lowp>; + +template struct mat<2, 3, float32, mediump>; +template struct mat<2, 3, float64, mediump>; + +template struct mat<2, 3, float32, highp>; +template struct mat<2, 3, float64, highp>; + +// tmat2x4 type explicit instantiation +template struct mat<2, 4, float32, lowp>; +template struct mat<2, 4, float64, lowp>; + +template struct mat<2, 4, float32, mediump>; +template struct mat<2, 4, float64, mediump>; + +template struct mat<2, 4, float32, highp>; +template struct mat<2, 4, float64, highp>; + +// tmat3x2 type explicit instantiation +template struct mat<3, 2, float32, lowp>; +template struct mat<3, 2, float64, lowp>; + +template struct mat<3, 2, float32, mediump>; +template struct mat<3, 2, float64, mediump>; + +template struct mat<3, 2, float32, highp>; +template struct mat<3, 2, float64, highp>; + +// tmat3x3 type explicit instantiation +template struct mat<3, 3, float32, lowp>; +template struct mat<3, 3, float64, lowp>; + +template struct mat<3, 3, float32, mediump>; +template struct mat<3, 3, float64, mediump>; + +template struct mat<3, 3, float32, highp>; +template struct mat<3, 3, float64, highp>; + +// tmat3x4 type explicit instantiation +template struct mat<3, 4, float32, lowp>; +template struct mat<3, 4, float64, lowp>; + +template struct mat<3, 4, float32, mediump>; +template struct mat<3, 4, float64, mediump>; + +template struct mat<3, 4, float32, highp>; +template struct mat<3, 4, float64, highp>; + +// tmat4x2 type explicit instantiation +template struct mat<4, 2, float32, lowp>; +template struct mat<4, 2, float64, lowp>; + +template struct mat<4, 2, float32, mediump>; +template struct mat<4, 2, float64, mediump>; + +template struct mat<4, 2, float32, highp>; +template struct mat<4, 2, float64, highp>; + +// tmat4x3 type explicit instantiation +template struct mat<4, 3, float32, lowp>; +template struct mat<4, 3, float64, lowp>; + +template struct mat<4, 3, float32, mediump>; +template struct mat<4, 3, float64, mediump>; + +template struct mat<4, 3, float32, highp>; +template struct mat<4, 3, float64, highp>; + +// tmat4x4 type explicit instantiation +template struct mat<4, 4, float32, lowp>; +template struct mat<4, 4, float64, lowp>; + +template struct mat<4, 4, float32, mediump>; +template struct mat<4, 4, float64, mediump>; + +template struct mat<4, 4, float32, highp>; +template struct mat<4, 4, float64, highp>; + +// tquat type explicit instantiation +template struct qua; +template struct qua; + +template struct qua; +template struct qua; + +template struct qua; +template struct qua; + +//tdualquat type explicit instantiation +template struct tdualquat; +template struct tdualquat; + +template struct tdualquat; +template struct tdualquat; + +template struct tdualquat; +template struct tdualquat; + +}//namespace glm + diff --git a/thirdparty/glm/glm/detail/qualifier.hpp b/thirdparty/glm/glm/detail/qualifier.hpp new file mode 100644 index 0000000..a6c96cc --- /dev/null +++ b/thirdparty/glm/glm/detail/qualifier.hpp @@ -0,0 +1,229 @@ +#pragma once + +#include "setup.hpp" + +namespace glm +{ + /// Qualify GLM types in term of alignment (packed, aligned) and precision in term of ULPs (lowp, mediump, highp) + enum qualifier + { + packed_highp, ///< Typed data is tightly packed in memory and operations are executed with high precision in term of ULPs + packed_mediump, ///< Typed data is tightly packed in memory and operations are executed with medium precision in term of ULPs for higher performance + packed_lowp, ///< Typed data is tightly packed in memory and operations are executed with low precision in term of ULPs to maximize performance + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + aligned_highp, ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs + aligned_mediump, ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs for higher performance + aligned_lowp, // ///< Typed data is aligned in memory allowing SIMD optimizations and operations are executed with high precision in term of ULPs to maximize performance + aligned = aligned_highp, ///< By default aligned qualifier is also high precision +# endif + + highp = packed_highp, ///< By default highp qualifier is also packed + mediump = packed_mediump, ///< By default mediump qualifier is also packed + lowp = packed_lowp, ///< By default lowp qualifier is also packed + packed = packed_highp, ///< By default packed qualifier is also high precision + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE && defined(GLM_FORCE_DEFAULT_ALIGNED_GENTYPES) + defaultp = aligned_highp +# else + defaultp = highp +# endif + }; + + typedef qualifier precision; + + template struct vec; + template struct mat; + template struct qua; + +# if GLM_HAS_TEMPLATE_ALIASES + template using tvec1 = vec<1, T, Q>; + template using tvec2 = vec<2, T, Q>; + template using tvec3 = vec<3, T, Q>; + template using tvec4 = vec<4, T, Q>; + template using tmat2x2 = mat<2, 2, T, Q>; + template using tmat2x3 = mat<2, 3, T, Q>; + template using tmat2x4 = mat<2, 4, T, Q>; + template using tmat3x2 = mat<3, 2, T, Q>; + template using tmat3x3 = mat<3, 3, T, Q>; + template using tmat3x4 = mat<3, 4, T, Q>; + template using tmat4x2 = mat<4, 2, T, Q>; + template using tmat4x3 = mat<4, 3, T, Q>; + template using tmat4x4 = mat<4, 4, T, Q>; + template using tquat = qua; +# endif + +namespace detail +{ + template + struct is_aligned + { + static const bool value = false; + }; + +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + template<> + struct is_aligned + { + static const bool value = true; + }; + + template<> + struct is_aligned + { + static const bool value = true; + }; + + template<> + struct is_aligned + { + static const bool value = true; + }; +# endif + + template + struct storage + { + typedef struct type { + T data[L]; + } type; + }; + +# if GLM_HAS_ALIGNOF + template + struct storage + { + typedef struct alignas(L * sizeof(T)) type { + T data[L]; + } type; + }; + + template + struct storage<3, T, true> + { + typedef struct alignas(4 * sizeof(T)) type { + T data[4]; + } type; + }; +# endif + +# if GLM_ARCH & GLM_ARCH_SSE2_BIT + template<> + struct storage<4, float, true> + { + typedef glm_f32vec4 type; + }; + + template<> + struct storage<4, int, true> + { + typedef glm_i32vec4 type; + }; + + template<> + struct storage<4, unsigned int, true> + { + typedef glm_u32vec4 type; + }; + + template<> + struct storage<2, double, true> + { + typedef glm_f64vec2 type; + }; + + template<> + struct storage<2, detail::int64, true> + { + typedef glm_i64vec2 type; + }; + + template<> + struct storage<2, detail::uint64, true> + { + typedef glm_u64vec2 type; + }; +# endif +# if (GLM_ARCH & GLM_ARCH_AVX_BIT) + template<> + struct storage<4, double, true> + { + typedef glm_f64vec4 type; + }; +# endif + +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) + template<> + struct storage<4, detail::int64, true> + { + typedef glm_i64vec4 type; + }; + + template<> + struct storage<4, detail::uint64, true> + { + typedef glm_u64vec4 type; + }; +# endif + +# if GLM_ARCH & GLM_ARCH_NEON_BIT + template<> + struct storage<4, float, true> + { + typedef glm_f32vec4 type; + }; + + template<> + struct storage<4, int, true> + { + typedef glm_i32vec4 type; + }; + + template<> + struct storage<4, unsigned int, true> + { + typedef glm_u32vec4 type; + }; +# endif + + enum genTypeEnum + { + GENTYPE_VEC, + GENTYPE_MAT, + GENTYPE_QUAT + }; + + template + struct genTypeTrait + {}; + + template + struct genTypeTrait > + { + static const genTypeEnum GENTYPE = GENTYPE_MAT; + }; + + template + struct init_gentype + { + }; + + template + struct init_gentype + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genType identity() + { + return genType(1, 0, 0, 0); + } + }; + + template + struct init_gentype + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static genType identity() + { + return genType(1); + } + }; +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/setup.hpp b/thirdparty/glm/glm/detail/setup.hpp new file mode 100644 index 0000000..1664c89 --- /dev/null +++ b/thirdparty/glm/glm/detail/setup.hpp @@ -0,0 +1,1188 @@ +#ifndef GLM_SETUP_INCLUDED + +#include +#include + +#define GLM_VERSION_MAJOR 1 +#define GLM_VERSION_MINOR 0 +#define GLM_VERSION_PATCH 0 +#define GLM_VERSION_REVISION 0 // Deprecated +#define GLM_VERSION 1000 // Deprecated +#define GLM_VERSION_MESSAGE "GLM: version 1.0.0" + +#define GLM_MAKE_API_VERSION(variant, major, minor, patch) \ + ((((uint32_t)(variant)) << 29U) | (((uint32_t)(major)) << 22U) | (((uint32_t)(minor)) << 12U) | ((uint32_t)(patch))) + +#define GLM_VERSION_COMPLETE GLM_MAKE_API_VERSION(0, GLM_VERSION_MAJOR, GLM_VERSION_MINOR, GLM_VERSION_PATCH) + +#define GLM_SETUP_INCLUDED GLM_VERSION + +#define GLM_GET_VERSION_VARIANT(version) ((uint32_t)(version) >> 29U) +#define GLM_GET_VERSION_MAJOR(version) (((uint32_t)(version) >> 22U) & 0x7FU) +#define GLM_GET_VERSION_MINOR(version) (((uint32_t)(version) >> 12U) & 0x3FFU) +#define GLM_GET_VERSION_PATCH(version) ((uint32_t)(version) & 0xFFFU) + +/////////////////////////////////////////////////////////////////////////////////// +// Active states + +#define GLM_DISABLE 0 +#define GLM_ENABLE 1 + +/////////////////////////////////////////////////////////////////////////////////// +// Messages + +#if defined(GLM_FORCE_MESSAGES) +# define GLM_MESSAGES GLM_ENABLE +#else +# define GLM_MESSAGES GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Detect the platform + +#include "../simd/platform.h" + +/////////////////////////////////////////////////////////////////////////////////// +// Build model + +#if defined(_M_ARM64) || defined(__LP64__) || defined(_M_X64) || defined(__ppc64__) || defined(__x86_64__) +# define GLM_MODEL GLM_MODEL_64 +#elif defined(__i386__) || defined(__ppc__) || defined(__ILP32__) || defined(_M_ARM) +# define GLM_MODEL GLM_MODEL_32 +#else +# define GLM_MODEL GLM_MODEL_32 +#endif// + +#if !defined(GLM_MODEL) && GLM_COMPILER != 0 +# error "GLM_MODEL undefined, your compiler may not be supported by GLM. Add #define GLM_MODEL 0 to ignore this message." +#endif//GLM_MODEL + +/////////////////////////////////////////////////////////////////////////////////// +// C++ Version + +// User defines: GLM_FORCE_CXX98, GLM_FORCE_CXX03, GLM_FORCE_CXX11, GLM_FORCE_CXX14, GLM_FORCE_CXX17, GLM_FORCE_CXX2A + +#define GLM_LANG_CXX98_FLAG (1 << 1) +#define GLM_LANG_CXX03_FLAG (1 << 2) +#define GLM_LANG_CXX0X_FLAG (1 << 3) +#define GLM_LANG_CXX11_FLAG (1 << 4) +#define GLM_LANG_CXX14_FLAG (1 << 5) +#define GLM_LANG_CXX17_FLAG (1 << 6) +#define GLM_LANG_CXX20_FLAG (1 << 7) +#define GLM_LANG_CXXMS_FLAG (1 << 8) +#define GLM_LANG_CXXGNU_FLAG (1 << 9) + +#define GLM_LANG_CXX98 GLM_LANG_CXX98_FLAG +#define GLM_LANG_CXX03 (GLM_LANG_CXX98 | GLM_LANG_CXX03_FLAG) +#define GLM_LANG_CXX0X (GLM_LANG_CXX03 | GLM_LANG_CXX0X_FLAG) +#define GLM_LANG_CXX11 (GLM_LANG_CXX0X | GLM_LANG_CXX11_FLAG) +#define GLM_LANG_CXX14 (GLM_LANG_CXX11 | GLM_LANG_CXX14_FLAG) +#define GLM_LANG_CXX17 (GLM_LANG_CXX14 | GLM_LANG_CXX17_FLAG) +#define GLM_LANG_CXX20 (GLM_LANG_CXX17 | GLM_LANG_CXX20_FLAG) +#define GLM_LANG_CXXMS GLM_LANG_CXXMS_FLAG +#define GLM_LANG_CXXGNU GLM_LANG_CXXGNU_FLAG + +#if (defined(_MSC_EXTENSIONS)) +# define GLM_LANG_EXT GLM_LANG_CXXMS_FLAG +#elif ((GLM_COMPILER & (GLM_COMPILER_CLANG | GLM_COMPILER_GCC)) && (GLM_ARCH & GLM_ARCH_SIMD_BIT)) +# define GLM_LANG_EXT GLM_LANG_CXXMS_FLAG +#else +# define GLM_LANG_EXT 0 +#endif + +#if (defined(GLM_FORCE_CXX_UNKNOWN)) +# define GLM_LANG 0 +#elif defined(GLM_FORCE_CXX20) +# define GLM_LANG (GLM_LANG_CXX20 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX17) +# define GLM_LANG (GLM_LANG_CXX17 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX14) +# define GLM_LANG (GLM_LANG_CXX14 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX11) +# define GLM_LANG (GLM_LANG_CXX11 | GLM_LANG_EXT) +# define GLM_LANG_STL11_FORCED +#elif defined(GLM_FORCE_CXX03) +# define GLM_LANG (GLM_LANG_CXX03 | GLM_LANG_EXT) +#elif defined(GLM_FORCE_CXX98) +# define GLM_LANG (GLM_LANG_CXX98 | GLM_LANG_EXT) +#else +# if GLM_COMPILER & GLM_COMPILER_VC && defined(_MSVC_LANG) +# if GLM_COMPILER >= GLM_COMPILER_VC15_7 +# define GLM_LANG_PLATFORM _MSVC_LANG +# elif GLM_COMPILER >= GLM_COMPILER_VC15 +# if _MSVC_LANG > 201402L +# define GLM_LANG_PLATFORM 201402L +# else +# define GLM_LANG_PLATFORM _MSVC_LANG +# endif +# else +# define GLM_LANG_PLATFORM 0 +# endif +# else +# define GLM_LANG_PLATFORM 0 +# endif + +# if __cplusplus > 201703L || GLM_LANG_PLATFORM > 201703L +# define GLM_LANG (GLM_LANG_CXX20 | GLM_LANG_EXT) +# elif __cplusplus == 201703L || GLM_LANG_PLATFORM == 201703L +# define GLM_LANG (GLM_LANG_CXX17 | GLM_LANG_EXT) +# elif __cplusplus == 201402L || __cplusplus == 201406L || __cplusplus == 201500L || GLM_LANG_PLATFORM == 201402L +# define GLM_LANG (GLM_LANG_CXX14 | GLM_LANG_EXT) +# elif __cplusplus == 201103L || GLM_LANG_PLATFORM == 201103L +# define GLM_LANG (GLM_LANG_CXX11 | GLM_LANG_EXT) +# elif defined(__INTEL_CXX11_MODE__) || defined(_MSC_VER) || defined(__GXX_EXPERIMENTAL_CXX0X__) +# define GLM_LANG (GLM_LANG_CXX0X | GLM_LANG_EXT) +# elif __cplusplus == 199711L +# define GLM_LANG (GLM_LANG_CXX98 | GLM_LANG_EXT) +# else +# define GLM_LANG (0 | GLM_LANG_EXT) +# endif +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Has of C++ features + +// http://clang.llvm.org/cxx_status.html +// http://gcc.gnu.org/projects/cxx0x.html +// http://msdn.microsoft.com/en-us/library/vstudio/hh567368(v=vs.120).aspx + +// Android has multiple STLs but C++11 STL detection doesn't always work #284 #564 +#if GLM_PLATFORM == GLM_PLATFORM_ANDROID && !defined(GLM_LANG_STL11_FORCED) +# define GLM_HAS_CXX11_STL 0 +#elif (GLM_COMPILER & GLM_COMPILER_CUDA_RTC) == GLM_COMPILER_CUDA_RTC +# define GLM_HAS_CXX11_STL 0 +#elif (GLM_COMPILER & GLM_COMPILER_HIP) +# define GLM_HAS_CXX11_STL 0 +#elif GLM_COMPILER & GLM_COMPILER_CLANG +# if (defined(_LIBCPP_VERSION) || (GLM_LANG & GLM_LANG_CXX11_FLAG) || defined(GLM_LANG_STL11_FORCED)) +# define GLM_HAS_CXX11_STL 1 +# else +# define GLM_HAS_CXX11_STL 0 +# endif +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_CXX11_STL 1 +#else +# define GLM_HAS_CXX11_STL ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_GCC) && (GLM_COMPILER >= GLM_COMPILER_GCC48)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_PLATFORM != GLM_PLATFORM_WINDOWS) && (GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL15)))) +#endif + +// N1720 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_STATIC_ASSERT __has_feature(cxx_static_assert) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_STATIC_ASSERT 1 +#else +# define GLM_HAS_STATIC_ASSERT ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N1988 +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_EXTENDED_INTEGER_TYPE 1 +#else +# define GLM_HAS_EXTENDED_INTEGER_TYPE (\ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_COMPILER & GLM_COMPILER_CLANG)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP))) +#endif + +// N2672 Initializer lists http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htm +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_INITIALIZER_LISTS __has_feature(cxx_generalized_initializers) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_INITIALIZER_LISTS 1 +#else +# define GLM_HAS_INITIALIZER_LISTS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)) || \ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL14)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2544 Unrestricted unions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_UNRESTRICTED_UNIONS __has_feature(cxx_unrestricted_unions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_UNRESTRICTED_UNIONS 1 +#else +# define GLM_HAS_UNRESTRICTED_UNIONS (GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + (GLM_COMPILER & GLM_COMPILER_VC) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP))) +#endif + +// N2346 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_DEFAULTED_FUNCTIONS __has_feature(cxx_defaulted_functions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_DEFAULTED_FUNCTIONS 1 +#else +# define GLM_HAS_DEFAULTED_FUNCTIONS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + (GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP))) +#endif + +// N2118 +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_RVALUE_REFERENCES __has_feature(cxx_rvalue_references) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_RVALUE_REFERENCES 1 +#else +# define GLM_HAS_RVALUE_REFERENCES ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2437 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2437.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS __has_feature(cxx_explicit_conversions) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS 1 +#else +# define GLM_HAS_EXPLICIT_CONVERSION_OPERATORS ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL14)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2258 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2258.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_TEMPLATE_ALIASES __has_feature(cxx_alias_templates) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_TEMPLATE_ALIASES 1 +#else +# define GLM_HAS_TEMPLATE_ALIASES ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2930 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2930.html +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_RANGE_FOR __has_feature(cxx_range_for) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_RANGE_FOR 1 +#else +# define GLM_HAS_RANGE_FOR ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2341 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2341.pdf +#if GLM_COMPILER & GLM_COMPILER_CLANG +# define GLM_HAS_ALIGNOF __has_feature(cxx_alignas) +#elif GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_ALIGNOF 1 +#else +# define GLM_HAS_ALIGNOF ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL15)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC14)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// N2235 Generalized Constant Expressions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf +// N3652 Extended Constant Expressions http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html +#if (GLM_ARCH & GLM_ARCH_SIMD_BIT) // Compiler SIMD intrinsics don't support constexpr... +# define GLM_HAS_CONSTEXPR 0 +#elif (GLM_COMPILER & GLM_COMPILER_CLANG) +# define GLM_HAS_CONSTEXPR __has_feature(cxx_relaxed_constexpr) +#elif (GLM_LANG & GLM_LANG_CXX14_FLAG) +# define GLM_HAS_CONSTEXPR 1 +#else +# define GLM_HAS_CONSTEXPR ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && GLM_HAS_INITIALIZER_LISTS && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_COMPILER >= GLM_COMPILER_INTEL17)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)))) +#endif + +#if GLM_HAS_CONSTEXPR +# define GLM_CONSTEXPR constexpr +#else +# define GLM_CONSTEXPR +#endif + +// +#if GLM_HAS_CONSTEXPR +# if (GLM_COMPILER & GLM_COMPILER_CLANG) +# if __has_feature(cxx_if_constexpr) +# define GLM_HAS_IF_CONSTEXPR 1 +# else +# define GLM_HAS_IF_CONSTEXPR 0 +# endif +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) +# define GLM_HAS_IF_CONSTEXPR 1 +# else +# define GLM_HAS_IF_CONSTEXPR 0 +# endif +#else +# define GLM_HAS_IF_CONSTEXPR 0 +#endif + +#if GLM_HAS_IF_CONSTEXPR +# define GLM_IF_CONSTEXPR if constexpr +#else +# define GLM_IF_CONSTEXPR if +#endif + +// [nodiscard] +#if GLM_LANG & GLM_LANG_CXX17_FLAG +# define GLM_NODISCARD [[nodiscard]] +#else +# define GLM_NODISCARD +#endif + +// +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_ASSIGNABLE 1 +#else +# define GLM_HAS_ASSIGNABLE ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC15)) || \ + ((GLM_COMPILER & GLM_COMPILER_GCC) && (GLM_COMPILER >= GLM_COMPILER_GCC49)))) +#endif + +// +#define GLM_HAS_TRIVIAL_QUERIES 0 + +// +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_MAKE_SIGNED 1 +#else +# define GLM_HAS_MAKE_SIGNED ((GLM_LANG & GLM_LANG_CXX0X_FLAG) && (\ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC12)) || \ + ((GLM_COMPILER & GLM_COMPILER_CUDA)) || \ + ((GLM_COMPILER & GLM_COMPILER_HIP)))) +#endif + +// +#if defined(GLM_FORCE_INTRINSICS) +# define GLM_HAS_BITSCAN_WINDOWS ((GLM_PLATFORM & GLM_PLATFORM_WINDOWS) && (\ + ((GLM_COMPILER & GLM_COMPILER_INTEL)) || \ + ((GLM_COMPILER & GLM_COMPILER_VC) && (GLM_COMPILER >= GLM_COMPILER_VC14) && (GLM_ARCH & GLM_ARCH_X86_BIT)))) +#else +# define GLM_HAS_BITSCAN_WINDOWS 0 +#endif + +#if GLM_LANG & GLM_LANG_CXX11_FLAG +# define GLM_HAS_NOEXCEPT 1 +#else +# define GLM_HAS_NOEXCEPT 0 +#endif + +#if GLM_HAS_NOEXCEPT +# define GLM_NOEXCEPT noexcept +#else +# define GLM_NOEXCEPT +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// OpenMP +#ifdef _OPENMP +# if GLM_COMPILER & GLM_COMPILER_GCC +# if GLM_COMPILER >= GLM_COMPILER_GCC61 +# define GLM_HAS_OPENMP 45 +# elif GLM_COMPILER >= GLM_COMPILER_GCC49 +# define GLM_HAS_OPENMP 40 +# elif GLM_COMPILER >= GLM_COMPILER_GCC47 +# define GLM_HAS_OPENMP 31 +# else +# define GLM_HAS_OPENMP 0 +# endif +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# if GLM_COMPILER >= GLM_COMPILER_CLANG38 +# define GLM_HAS_OPENMP 31 +# else +# define GLM_HAS_OPENMP 0 +# endif +# elif GLM_COMPILER & GLM_COMPILER_VC +# define GLM_HAS_OPENMP 20 +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# if GLM_COMPILER >= GLM_COMPILER_INTEL16 +# define GLM_HAS_OPENMP 40 +# else +# define GLM_HAS_OPENMP 0 +# endif +# else +# define GLM_HAS_OPENMP 0 +# endif +#else +# define GLM_HAS_OPENMP 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// nullptr + +#if GLM_LANG & GLM_LANG_CXX0X_FLAG +# define GLM_CONFIG_NULLPTR GLM_ENABLE +#else +# define GLM_CONFIG_NULLPTR GLM_DISABLE +#endif + +#if GLM_CONFIG_NULLPTR == GLM_ENABLE +# define GLM_NULLPTR nullptr +#else +# define GLM_NULLPTR 0 +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Static assert + +#if GLM_HAS_STATIC_ASSERT +# define GLM_STATIC_ASSERT(x, message) static_assert(x, message) +#elif GLM_COMPILER & GLM_COMPILER_VC +# define GLM_STATIC_ASSERT(x, message) typedef char __CASSERT__##__LINE__[(x) ? 1 : -1] +#else +# define GLM_STATIC_ASSERT(x, message) assert(x) +#endif//GLM_LANG + +/////////////////////////////////////////////////////////////////////////////////// +// Qualifiers + +// User defines: GLM_CUDA_FORCE_DEVICE_FUNC, GLM_CUDA_FORCE_HOST_FUNC + +#if (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) +# if defined(GLM_CUDA_FORCE_DEVICE_FUNC) && defined(GLM_CUDA_FORCE_HOST_FUNC) +# error "GLM error: GLM_CUDA_FORCE_DEVICE_FUNC and GLM_CUDA_FORCE_HOST_FUNC should not be defined at the same time, GLM by default generates both device and host code for CUDA compiler." +# endif//defined(GLM_CUDA_FORCE_DEVICE_FUNC) && defined(GLM_CUDA_FORCE_HOST_FUNC) + +# if defined(GLM_CUDA_FORCE_DEVICE_FUNC) +# define GLM_CUDA_FUNC_DEF __device__ +# define GLM_CUDA_FUNC_DECL __device__ +# elif defined(GLM_CUDA_FORCE_HOST_FUNC) +# define GLM_CUDA_FUNC_DEF __host__ +# define GLM_CUDA_FUNC_DECL __host__ +# else +# define GLM_CUDA_FUNC_DEF __device__ __host__ +# define GLM_CUDA_FUNC_DECL __device__ __host__ +# endif//defined(GLM_CUDA_FORCE_XXXX_FUNC) +#else +# define GLM_CUDA_FUNC_DEF +# define GLM_CUDA_FUNC_DECL +#endif + +#if defined(GLM_FORCE_INLINE) +# if GLM_COMPILER & GLM_COMPILER_VC +# define GLM_INLINE __forceinline +# define GLM_NEVER_INLINE __declspec(noinline) +# elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG) +# define GLM_INLINE inline __attribute__((__always_inline__)) +# define GLM_NEVER_INLINE __attribute__((__noinline__)) +# elif (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) +# define GLM_INLINE __forceinline__ +# define GLM_NEVER_INLINE __noinline__ +# else +# define GLM_INLINE inline +# define GLM_NEVER_INLINE +# endif//GLM_COMPILER +#else +# define GLM_INLINE inline +# define GLM_NEVER_INLINE +#endif//defined(GLM_FORCE_INLINE) + +#define GLM_CTOR_DECL GLM_CUDA_FUNC_DECL GLM_CONSTEXPR +#define GLM_FUNC_DISCARD_DECL GLM_CUDA_FUNC_DECL +#define GLM_FUNC_DECL GLM_NODISCARD GLM_CUDA_FUNC_DECL +#define GLM_FUNC_QUALIFIER GLM_CUDA_FUNC_DEF GLM_INLINE + +// Do not use CUDA function qualifiers on CUDA compiler when functions are made default +#if GLM_HAS_DEFAULTED_FUNCTIONS +# define GLM_DEFAULTED_FUNC_DECL +# define GLM_DEFAULTED_FUNC_QUALIFIER GLM_INLINE +#else +# define GLM_DEFAULTED_FUNC_DECL GLM_FUNC_DISCARD_DECL +# define GLM_DEFAULTED_FUNC_QUALIFIER GLM_FUNC_QUALIFIER +#endif//GLM_HAS_DEFAULTED_FUNCTIONS +#if !defined(GLM_FORCE_CTOR_INIT) +# define GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CUDA_FUNC_DECL +# define GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_DEFAULTED_FUNC_QUALIFIER +#else +# define GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_FUNC_DISCARD_DECL +# define GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_FUNC_QUALIFIER +#endif//GLM_FORCE_CTOR_INIT + +/////////////////////////////////////////////////////////////////////////////////// +// Swizzle operators + +// User defines: GLM_FORCE_SWIZZLE + +#define GLM_SWIZZLE_DISABLED 0 +#define GLM_SWIZZLE_OPERATOR 1 +#define GLM_SWIZZLE_FUNCTION 2 + +#if defined(GLM_SWIZZLE) +# pragma message("GLM: GLM_SWIZZLE is deprecated, use GLM_FORCE_SWIZZLE instead.") +# define GLM_FORCE_SWIZZLE +#endif + +#if defined(GLM_FORCE_SWIZZLE) && (GLM_LANG & GLM_LANG_CXXMS_FLAG) && !defined(GLM_FORCE_XYZW_ONLY) +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_OPERATOR +#elif defined(GLM_FORCE_SWIZZLE) +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_FUNCTION +#else +# define GLM_CONFIG_SWIZZLE GLM_SWIZZLE_DISABLED +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Allows using not basic types as genType + +// #define GLM_FORCE_UNRESTRICTED_GENTYPE + +#ifdef GLM_FORCE_UNRESTRICTED_GENTYPE +# define GLM_CONFIG_UNRESTRICTED_GENTYPE GLM_ENABLE +#else +# define GLM_CONFIG_UNRESTRICTED_GENTYPE GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Allows using any scaler as float + +// #define GLM_FORCE_UNRESTRICTED_FLOAT + +#ifdef GLM_FORCE_UNRESTRICTED_FLOAT +# define GLM_CONFIG_UNRESTRICTED_FLOAT GLM_ENABLE +#else +# define GLM_CONFIG_UNRESTRICTED_FLOAT GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Clip control, define GLM_FORCE_DEPTH_ZERO_TO_ONE before including GLM +// to use a clip space between 0 to 1. +// Coordinate system, define GLM_FORCE_LEFT_HANDED before including GLM +// to use left handed coordinate system by default. + +#define GLM_CLIP_CONTROL_ZO_BIT (1 << 0) // ZERO_TO_ONE +#define GLM_CLIP_CONTROL_NO_BIT (1 << 1) // NEGATIVE_ONE_TO_ONE +#define GLM_CLIP_CONTROL_LH_BIT (1 << 2) // LEFT_HANDED, For DirectX, Metal, Vulkan +#define GLM_CLIP_CONTROL_RH_BIT (1 << 3) // RIGHT_HANDED, For OpenGL, default in GLM + +#define GLM_CLIP_CONTROL_LH_ZO (GLM_CLIP_CONTROL_LH_BIT | GLM_CLIP_CONTROL_ZO_BIT) +#define GLM_CLIP_CONTROL_LH_NO (GLM_CLIP_CONTROL_LH_BIT | GLM_CLIP_CONTROL_NO_BIT) +#define GLM_CLIP_CONTROL_RH_ZO (GLM_CLIP_CONTROL_RH_BIT | GLM_CLIP_CONTROL_ZO_BIT) +#define GLM_CLIP_CONTROL_RH_NO (GLM_CLIP_CONTROL_RH_BIT | GLM_CLIP_CONTROL_NO_BIT) + +#ifdef GLM_FORCE_DEPTH_ZERO_TO_ONE +# ifdef GLM_FORCE_LEFT_HANDED +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_LH_ZO +# else +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_RH_ZO +# endif +#else +# ifdef GLM_FORCE_LEFT_HANDED +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_LH_NO +# else +# define GLM_CONFIG_CLIP_CONTROL GLM_CLIP_CONTROL_RH_NO +# endif +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Qualifiers + +#if (GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS)) +# define GLM_DEPRECATED __declspec(deprecated) +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef __declspec(align(alignment)) type name +#elif GLM_COMPILER & (GLM_COMPILER_GCC | GLM_COMPILER_CLANG | GLM_COMPILER_INTEL) +# define GLM_DEPRECATED __attribute__((__deprecated__)) +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name __attribute__((aligned(alignment))) +#elif (GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP) +# define GLM_DEPRECATED +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name __align__(x) +#else +# define GLM_DEPRECATED +# define GLM_ALIGNED_TYPEDEF(type, name, alignment) typedef type name +#endif + +/////////////////////////////////////////////////////////////////////////////////// + +#ifdef GLM_FORCE_EXPLICIT_CTOR +# define GLM_EXPLICIT explicit +#else +# define GLM_EXPLICIT +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Length type: all length functions returns a length_t type. +// When GLM_FORCE_SIZE_T_LENGTH is defined, length_t is a typedef of size_t otherwise +// length_t is a typedef of int like GLSL defines it. + +#define GLM_LENGTH_INT 1 +#define GLM_LENGTH_SIZE_T 2 + +#ifdef GLM_FORCE_SIZE_T_LENGTH +# define GLM_CONFIG_LENGTH_TYPE GLM_LENGTH_SIZE_T +# define GLM_ASSERT_LENGTH(l, max) (assert ((l) < (max))) +#else +# define GLM_CONFIG_LENGTH_TYPE GLM_LENGTH_INT +# define GLM_ASSERT_LENGTH(l, max) (assert ((l) >= 0 && (l) < (max))) +#endif + +namespace glm +{ + using std::size_t; +# if GLM_CONFIG_LENGTH_TYPE == GLM_LENGTH_SIZE_T + typedef size_t length_t; +# else + typedef int length_t; +# endif +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// constexpr + +#if GLM_HAS_CONSTEXPR +# define GLM_CONFIG_CONSTEXP GLM_ENABLE + + namespace glm + { + template + constexpr std::size_t countof(T const (&)[N]) + { + return N; + } + }//namespace glm +# define GLM_COUNTOF(arr) glm::countof(arr) +#elif defined(_MSC_VER) +# define GLM_CONFIG_CONSTEXP GLM_DISABLE + +# define GLM_COUNTOF(arr) _countof(arr) +#else +# define GLM_CONFIG_CONSTEXP GLM_DISABLE + +# define GLM_COUNTOF(arr) sizeof(arr) / sizeof(arr[0]) +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// uint + +namespace glm{ +namespace detail +{ + template + struct is_int + { + enum test {value = 0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + typedef unsigned int uint; +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// 64-bit int + +#if GLM_HAS_EXTENDED_INTEGER_TYPE +# include +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::uint64_t uint64; + typedef std::int64_t int64; +# elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) // C99 detected, 64 bit types available + typedef uint64_t uint64; + typedef int64_t int64; +# elif GLM_COMPILER & GLM_COMPILER_VC + typedef unsigned __int64 uint64; + typedef signed __int64 int64; +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic ignored "-Wlong-long" + __extension__ typedef unsigned long long uint64; + __extension__ typedef signed long long int64; +# elif (GLM_COMPILER & GLM_COMPILER_CLANG) +# pragma clang diagnostic ignored "-Wc++11-long-long" + typedef unsigned long long uint64; + typedef signed long long int64; +# else//unknown compiler + typedef unsigned long long uint64; + typedef signed long long int64; +# endif +}//namespace detail +}//namespace glm + +/////////////////////////////////////////////////////////////////////////////////// +// make_unsigned + +#if GLM_HAS_MAKE_SIGNED +# include + +namespace glm{ +namespace detail +{ + using std::make_unsigned; +}//namespace detail +}//namespace glm + +#else + +namespace glm{ +namespace detail +{ + template + struct make_unsigned + {}; + + template<> + struct make_unsigned + { + typedef unsigned char type; + }; + + template<> + struct make_unsigned + { + typedef unsigned char type; + }; + + template<> + struct make_unsigned + { + typedef unsigned short type; + }; + + template<> + struct make_unsigned + { + typedef unsigned int type; + }; + + template<> + struct make_unsigned + { + typedef unsigned long type; + }; + + template<> + struct make_unsigned + { + typedef uint64 type; + }; + + template<> + struct make_unsigned + { + typedef unsigned char type; + }; + + template<> + struct make_unsigned + { + typedef unsigned short type; + }; + + template<> + struct make_unsigned + { + typedef unsigned int type; + }; + + template<> + struct make_unsigned + { + typedef unsigned long type; + }; + + template<> + struct make_unsigned + { + typedef uint64 type; + }; +}//namespace detail +}//namespace glm +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Only use x, y, z, w as vector type components + +#ifdef GLM_FORCE_XYZW_ONLY +# define GLM_CONFIG_XYZW_ONLY GLM_ENABLE +#else +# define GLM_CONFIG_XYZW_ONLY GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of defaulted initialized types + +#define GLM_CTOR_INIT_DISABLE 0 +#define GLM_CTOR_INITIALIZER_LIST 1 +#define GLM_CTOR_INITIALISATION 2 + +#if defined(GLM_FORCE_CTOR_INIT) && GLM_HAS_INITIALIZER_LISTS +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INITIALIZER_LIST +#elif defined(GLM_FORCE_CTOR_INIT) && !GLM_HAS_INITIALIZER_LISTS +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INITIALISATION +#else +# define GLM_CONFIG_CTOR_INIT GLM_CTOR_INIT_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Use SIMD instruction sets + +#if GLM_HAS_ALIGNOF && (GLM_LANG & GLM_LANG_CXXMS_FLAG) && (GLM_ARCH & GLM_ARCH_SIMD_BIT) +# define GLM_CONFIG_SIMD GLM_ENABLE +#else +# define GLM_CONFIG_SIMD GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of defaulted function + +#if GLM_HAS_DEFAULTED_FUNCTIONS +# define GLM_CONFIG_DEFAULTED_FUNCTIONS GLM_ENABLE +# define GLM_DEFAULT = default +#else +# define GLM_CONFIG_DEFAULTED_FUNCTIONS GLM_DISABLE +# define GLM_DEFAULT +#endif + +#if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INIT_DISABLE && GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_ENABLE +# define GLM_CONFIG_DEFAULTED_DEFAULT_CTOR GLM_ENABLE +# define GLM_DEFAULT_CTOR GLM_DEFAULT +#else +# define GLM_CONFIG_DEFAULTED_DEFAULT_CTOR GLM_DISABLE +# define GLM_DEFAULT_CTOR +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of aligned gentypes + +#ifdef GLM_FORCE_ALIGNED // Legacy define +# define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +#endif + +#ifdef GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +# define GLM_FORCE_ALIGNED_GENTYPES +#endif + +#if GLM_HAS_ALIGNOF && (GLM_LANG & GLM_LANG_CXXMS_FLAG) && (defined(GLM_FORCE_ALIGNED_GENTYPES) || (GLM_CONFIG_SIMD == GLM_ENABLE)) +# define GLM_CONFIG_ALIGNED_GENTYPES GLM_ENABLE +#else +# define GLM_CONFIG_ALIGNED_GENTYPES GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Configure the use of anonymous structure as implementation detail + +#if ((GLM_CONFIG_SIMD == GLM_ENABLE) || (GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR) || (GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE)) +# define GLM_CONFIG_ANONYMOUS_STRUCT GLM_ENABLE +#else +# define GLM_CONFIG_ANONYMOUS_STRUCT GLM_DISABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Silent warnings + +#ifdef GLM_FORCE_WARNINGS +# define GLM_SILENT_WARNINGS GLM_DISABLE +#else +# define GLM_SILENT_WARNINGS GLM_ENABLE +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Precision + +#define GLM_HIGHP 1 +#define GLM_MEDIUMP 2 +#define GLM_LOWP 3 + +#if defined(GLM_FORCE_PRECISION_HIGHP_BOOL) || defined(GLM_PRECISION_HIGHP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_BOOL) || defined(GLM_PRECISION_MEDIUMP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_BOOL) || defined(GLM_PRECISION_LOWP_BOOL) +# define GLM_CONFIG_PRECISION_BOOL GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_BOOL GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_INT) || defined(GLM_PRECISION_HIGHP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_INT) || defined(GLM_PRECISION_MEDIUMP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_INT) || defined(GLM_PRECISION_LOWP_INT) +# define GLM_CONFIG_PRECISION_INT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_INT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_UINT) || defined(GLM_PRECISION_HIGHP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_UINT) || defined(GLM_PRECISION_MEDIUMP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_UINT) || defined(GLM_PRECISION_LOWP_UINT) +# define GLM_CONFIG_PRECISION_UINT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_UINT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_FLOAT) || defined(GLM_PRECISION_HIGHP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_FLOAT) || defined(GLM_PRECISION_MEDIUMP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_FLOAT) || defined(GLM_PRECISION_LOWP_FLOAT) +# define GLM_CONFIG_PRECISION_FLOAT GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_FLOAT GLM_HIGHP +#endif + +#if defined(GLM_FORCE_PRECISION_HIGHP_DOUBLE) || defined(GLM_PRECISION_HIGHP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_HIGHP +#elif defined(GLM_FORCE_PRECISION_MEDIUMP_DOUBLE) || defined(GLM_PRECISION_MEDIUMP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_MEDIUMP +#elif defined(GLM_FORCE_PRECISION_LOWP_DOUBLE) || defined(GLM_PRECISION_LOWP_DOUBLE) +# define GLM_CONFIG_PRECISION_DOUBLE GLM_LOWP +#else +# define GLM_CONFIG_PRECISION_DOUBLE GLM_HIGHP +#endif + +/////////////////////////////////////////////////////////////////////////////////// +// Check inclusions of different versions of GLM + +#elif ((GLM_SETUP_INCLUDED != GLM_VERSION) && !defined(GLM_FORCE_IGNORE_VERSION)) +# error "GLM error: A different version of GLM is already included. Define GLM_FORCE_IGNORE_VERSION before including GLM headers to ignore this error." +#elif GLM_SETUP_INCLUDED == GLM_VERSION + +/////////////////////////////////////////////////////////////////////////////////// +// Messages + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_MESSAGE_DISPLAYED) +# define GLM_MESSAGE_DISPLAYED +# define GLM_STR_HELPER(x) #x +# define GLM_STR(x) GLM_STR_HELPER(x) + + // Report GLM version +# pragma message (GLM_STR(GLM_VERSION_MESSAGE)) + + // Report C++ language +# if (GLM_LANG & GLM_LANG_CXX20_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 20 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX20_FLAG) +# pragma message("GLM: C++ 2A") +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 17 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX17_FLAG) +# pragma message("GLM: C++ 17") +# elif (GLM_LANG & GLM_LANG_CXX14_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 14 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX14_FLAG) +# pragma message("GLM: C++ 14") +# elif (GLM_LANG & GLM_LANG_CXX11_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 11 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX11_FLAG) +# pragma message("GLM: C++ 11") +# elif (GLM_LANG & GLM_LANG_CXX0X_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 0x with extensions") +# elif (GLM_LANG & GLM_LANG_CXX0X_FLAG) +# pragma message("GLM: C++ 0x") +# elif (GLM_LANG & GLM_LANG_CXX03_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 03 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX03_FLAG) +# pragma message("GLM: C++ 03") +# elif (GLM_LANG & GLM_LANG_CXX98_FLAG) && (GLM_LANG & GLM_LANG_EXT) +# pragma message("GLM: C++ 98 with extensions") +# elif (GLM_LANG & GLM_LANG_CXX98_FLAG) +# pragma message("GLM: C++ 98") +# else +# pragma message("GLM: C++ language undetected") +# endif//GLM_LANG + + // Report compiler detection +# if GLM_COMPILER & GLM_COMPILER_CUDA +# pragma message("GLM: CUDA compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_HIP +# pragma message("GLM: HIP compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma message("GLM: Visual C++ compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma message("GLM: Clang compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_INTEL +# pragma message("GLM: Intel Compiler detected") +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma message("GLM: GCC compiler detected") +# else +# pragma message("GLM: Compiler not detected") +# endif + + // Report build target +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with AVX2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_AVX2_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with AVX2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_AVX_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with AVX instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_AVX_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with AVX instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE42_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE4.2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE42_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE4.2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE41_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE4.1 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE41_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE4.1 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSSE3_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSSE3 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSSE3_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSSE3 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE3_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE3 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE3_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE3 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_SSE2_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits with SSE2 instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_SSE2_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits with SSE2 instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_X86_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: x86 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_X86_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: x86 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_NEON_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: ARM 64 bits with Neon instruction set build target") +# elif (GLM_ARCH & GLM_ARCH_NEON_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: ARM 32 bits with Neon instruction set build target") + +# elif (GLM_ARCH & GLM_ARCH_ARM_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: ARM 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_ARM_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: ARM 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_MIPS_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: MIPS 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_MIPS_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: MIPS 32 bits build target") + +# elif (GLM_ARCH & GLM_ARCH_PPC_BIT) && (GLM_MODEL == GLM_MODEL_64) +# pragma message("GLM: PowerPC 64 bits build target") +# elif (GLM_ARCH & GLM_ARCH_PPC_BIT) && (GLM_MODEL == GLM_MODEL_32) +# pragma message("GLM: PowerPC 32 bits build target") +# else +# pragma message("GLM: Unknown build target") +# endif//GLM_ARCH + + // Report platform name +# if(GLM_PLATFORM & GLM_PLATFORM_QNXNTO) +# pragma message("GLM: QNX platform detected") +//# elif(GLM_PLATFORM & GLM_PLATFORM_IOS) +//# pragma message("GLM: iOS platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_APPLE) +# pragma message("GLM: Apple platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_WINCE) +# pragma message("GLM: WinCE platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_WINDOWS) +# pragma message("GLM: Windows platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_CHROME_NACL) +# pragma message("GLM: Native Client detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) +# pragma message("GLM: Android platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_LINUX) +# pragma message("GLM: Linux platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_UNIX) +# pragma message("GLM: UNIX platform detected") +# elif(GLM_PLATFORM & GLM_PLATFORM_UNKNOWN) +# pragma message("GLM: platform unknown") +# else +# pragma message("GLM: platform not detected") +# endif + + // Report whether only xyzw component are used +# if defined GLM_FORCE_XYZW_ONLY +# pragma message("GLM: GLM_FORCE_XYZW_ONLY is defined. Only x, y, z and w component are available in vector type. This define disables swizzle operators and SIMD instruction sets.") +# endif + + // Report swizzle operator support +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# pragma message("GLM: GLM_FORCE_SWIZZLE is defined, swizzling operators enabled.") +# elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# pragma message("GLM: GLM_FORCE_SWIZZLE is defined, swizzling functions enabled. Enable compiler C++ language extensions to enable swizzle operators.") +# else +# pragma message("GLM: GLM_FORCE_SWIZZLE is undefined. swizzling functions or operators are disabled.") +# endif + + // Report .length() type +# if GLM_CONFIG_LENGTH_TYPE == GLM_LENGTH_SIZE_T +# pragma message("GLM: GLM_FORCE_SIZE_T_LENGTH is defined. .length() returns a glm::length_t, a typedef of std::size_t.") +# else +# pragma message("GLM: GLM_FORCE_SIZE_T_LENGTH is undefined. .length() returns a glm::length_t, a typedef of int following GLSL.") +# endif + +# if GLM_CONFIG_UNRESTRICTED_GENTYPE == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_UNRESTRICTED_GENTYPE is defined. Removes GLSL restrictions on valid function genTypes.") +# else +# pragma message("GLM: GLM_FORCE_UNRESTRICTED_GENTYPE is undefined. Follows strictly GLSL on valid function genTypes.") +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_SILENT_WARNINGS is defined. Ignores C++ warnings from using C++ language extensions.") +# else +# pragma message("GLM: GLM_FORCE_SILENT_WARNINGS is undefined. Shows C++ warnings from using C++ language extensions.") +# endif + +# ifdef GLM_FORCE_SINGLE_ONLY +# pragma message("GLM: GLM_FORCE_SINGLE_ONLY is defined. Using only single precision floating-point types.") +# endif + +# if defined(GLM_FORCE_ALIGNED_GENTYPES) && (GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE) +# undef GLM_FORCE_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_ALIGNED_GENTYPES is defined, allowing aligned types. This prevents the use of C++ constexpr.") +# elif defined(GLM_FORCE_ALIGNED_GENTYPES) && (GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE) +# undef GLM_FORCE_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_ALIGNED_GENTYPES is defined but is disabled. It requires C++11 and language extensions.") +# endif + +# if defined(GLM_FORCE_DEFAULT_ALIGNED_GENTYPES) +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE +# undef GLM_FORCE_DEFAULT_ALIGNED_GENTYPES +# pragma message("GLM: GLM_FORCE_DEFAULT_ALIGNED_GENTYPES is defined but is disabled. It requires C++11 and language extensions.") +# elif GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# pragma message("GLM: GLM_FORCE_DEFAULT_ALIGNED_GENTYPES is defined. All gentypes (e.g. vec3) will be aligned and padded by default.") +# endif +# endif + +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT +# pragma message("GLM: GLM_FORCE_DEPTH_ZERO_TO_ONE is defined. Using zero to one depth clip space.") +# else +# pragma message("GLM: GLM_FORCE_DEPTH_ZERO_TO_ONE is undefined. Using negative one to one depth clip space.") +# endif + +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT +# pragma message("GLM: GLM_FORCE_LEFT_HANDED is defined. Using left handed coordinate system.") +# else +# pragma message("GLM: GLM_FORCE_LEFT_HANDED is undefined. Using right handed coordinate system.") +# endif +#endif//GLM_MESSAGES + +#endif//GLM_SETUP_INCLUDED diff --git a/thirdparty/glm/glm/detail/type_float.hpp b/thirdparty/glm/glm/detail/type_float.hpp new file mode 100644 index 0000000..c8037eb --- /dev/null +++ b/thirdparty/glm/glm/detail/type_float.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "setup.hpp" + +#if GLM_COMPILER == GLM_COMPILER_VC12 +# pragma warning(push) +# pragma warning(disable: 4512) // assignment operator could not be generated +#endif + +namespace glm{ +namespace detail +{ + template + union float_t + {}; + + // https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + template <> + union float_t + { + typedef int int_type; + typedef float float_type; + + GLM_CONSTEXPR float_t(float_type Num = 0.0f) : f(Num) {} + + GLM_CONSTEXPR float_t& operator=(float_t const& x) + { + f = x.f; + return *this; + } + + // Portable extraction of components. + GLM_CONSTEXPR bool negative() const { return i < 0; } + GLM_CONSTEXPR int_type mantissa() const { return i & ((1 << 23) - 1); } + GLM_CONSTEXPR int_type exponent() const { return (i >> 23) & ((1 << 8) - 1); } + + int_type i; + float_type f; + }; + + template <> + union float_t + { + typedef detail::int64 int_type; + typedef double float_type; + + GLM_CONSTEXPR float_t(float_type Num = static_cast(0)) : f(Num) {} + + GLM_CONSTEXPR float_t& operator=(float_t const& x) + { + f = x.f; + return *this; + } + + // Portable extraction of components. + GLM_CONSTEXPR bool negative() const { return i < 0; } + GLM_CONSTEXPR int_type mantissa() const { return i & ((int_type(1) << 52) - 1); } + GLM_CONSTEXPR int_type exponent() const { return (i >> 52) & ((int_type(1) << 11) - 1); } + + int_type i; + float_type f; + }; +}//namespace detail +}//namespace glm + +#if GLM_COMPILER == GLM_COMPILER_VC12 +# pragma warning(pop) +#endif diff --git a/thirdparty/glm/glm/detail/type_half.hpp b/thirdparty/glm/glm/detail/type_half.hpp new file mode 100644 index 0000000..40b8bec --- /dev/null +++ b/thirdparty/glm/glm/detail/type_half.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "setup.hpp" + +namespace glm{ +namespace detail +{ + typedef short hdata; + + GLM_FUNC_DECL float toFloat32(hdata value); + GLM_FUNC_DECL hdata toFloat16(float const& value); + +}//namespace detail +}//namespace glm + +#include "type_half.inl" diff --git a/thirdparty/glm/glm/detail/type_half.inl b/thirdparty/glm/glm/detail/type_half.inl new file mode 100644 index 0000000..5d239cf --- /dev/null +++ b/thirdparty/glm/glm/detail/type_half.inl @@ -0,0 +1,241 @@ +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER float overflow() + { + volatile float f = 1e10; + + for(int i = 0; i < 10; ++i) + f = f * f; // this will overflow before the for loop terminates + return f; + } + + union uif32 + { + GLM_FUNC_QUALIFIER uif32() : + i(0) + {} + + GLM_FUNC_QUALIFIER uif32(float f_) : + f(f_) + {} + + GLM_FUNC_QUALIFIER uif32(unsigned int i_) : + i(i_) + {} + + float f; + unsigned int i; + }; + + GLM_FUNC_QUALIFIER float toFloat32(hdata value) + { + int s = (value >> 15) & 0x00000001; + int e = (value >> 10) & 0x0000001f; + int m = value & 0x000003ff; + + if(e == 0) + { + if(m == 0) + { + // + // Plus or minus zero + // + + detail::uif32 result; + result.i = static_cast(s << 31); + return result.f; + } + else + { + // + // Denormalized number -- renormalize it + // + + while(!(m & 0x00000400)) + { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } + else if(e == 31) + { + if(m == 0) + { + // + // Positive or negative infinity + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000); + return result.f; + } + else + { + // + // Nan -- preserve sign and significand bits + // + + uif32 result; + result.i = static_cast((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + // + // Normalized number + // + + e = e + (127 - 15); + m = m << 13; + + // + // Assemble s, e and m. + // + + uif32 Result; + Result.i = static_cast((s << 31) | (e << 23) | m); + return Result.f; + } + + GLM_FUNC_QUALIFIER hdata toFloat16(float const& f) + { + uif32 Entry; + Entry.f = f; + int i = static_cast(Entry.i); + + // + // Our floating point number, f, is represented by the bit + // pattern in integer i. Disassemble that bit pattern into + // the sign, s, the exponent, e, and the significand, m. + // Shift s into the position where it will go in the + // resulting half number. + // Adjust e, accounting for the different exponent bias + // of float and half (127 versus 15). + // + + int s = (i >> 16) & 0x00008000; + int e = ((i >> 23) & 0x000000ff) - (127 - 15); + int m = i & 0x007fffff; + + // + // Now reassemble s, e and m into a half: + // + + if(e <= 0) + { + if(e < -10) + { + // + // E is less than -10. The absolute value of f is + // less than half_MIN (f may be a small normalized + // float, a denormalized float or a zero). + // + // We convert f to a half zero. + // + + return hdata(s); + } + + // + // E is between -10 and 0. F is a normalized float, + // whose magnitude is less than __half_NRM_MIN. + // + // We convert f to a denormalized half. + // + + m = (m | 0x00800000) >> (1 - e); + + // + // Round to nearest, round "0.5" up. + // + // Rounding may cause the significand to overflow and make + // our number normalized. Because of the way a half's bits + // are laid out, we don't have to treat this case separately; + // the code below will handle it correctly. + // + + if(m & 0x00001000) + m += 0x00002000; + + // + // Assemble the half from s, e (zero) and m. + // + + return hdata(s | (m >> 13)); + } + else if(e == 0xff - (127 - 15)) + { + if(m == 0) + { + // + // F is an infinity; convert f to a half + // infinity with the same sign as f. + // + + return hdata(s | 0x7c00); + } + else + { + // + // F is a NAN; we produce a half NAN that preserves + // the sign bit and the 10 leftmost bits of the + // significand of f, with one exception: If the 10 + // leftmost bits are all zero, the NAN would turn + // into an infinity, so we have to set at least one + // bit in the significand. + // + + m >>= 13; + + return hdata(s | 0x7c00 | m | (m == 0)); + } + } + else + { + // + // E is greater than zero. F is a normalized float. + // We try to convert f to a normalized half. + // + + // + // Round to nearest, round "0.5" up + // + + if(m & 0x00001000) + { + m += 0x00002000; + + if(m & 0x00800000) + { + m = 0; // overflow in significand, + e += 1; // adjust exponent + } + } + + // + // Handle exponent overflow + // + + if (e > 30) + { + overflow(); // Cause a hardware floating point overflow; + + return hdata(s | 0x7c00); + // if this returns, the half becomes an + } // infinity with the same sign as f. + + // + // Assemble the half from s, e and m. + // + + return hdata(s | (e << 10) | (m >> 13)); + } + } + +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat2x2.hpp b/thirdparty/glm/glm/detail/type_mat2x2.hpp new file mode 100644 index 0000000..82e9f66 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x2.hpp @@ -0,0 +1,177 @@ +/// @ref core +/// @file glm/detail/type_mat2x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 2, T, Q> type; + typedef mat<2, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<2, 2, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T const& x1, T const& y1, + T const& x2, T const& y2); + GLM_CTOR_DECL mat( + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template + GLM_CTOR_DECL mat( + U const& x1, V const& y1, + M const& x2, N const& y2); + + template + GLM_CTOR_DECL mat( + vec<2, U, Q> const& v1, + vec<2, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator+=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator-=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator*=(mat<2, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator/=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator/=(mat<2, 2, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator++ (); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 2, T, Q> & operator-- (); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type operator*(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 2, T, Q>::row_type operator*(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2); +} //namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x2.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat2x2.inl b/thirdparty/glm/glm/detail/type_mat2x2.inl new file mode 100644 index 0000000..afb2b31 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x2.inl @@ -0,0 +1,536 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(T scalar) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(scalar, 0), col_type(0, scalar)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(scalar, 0); + this->value[1] = col_type(0, scalar); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat + ( + T const& x0, T const& y0, + T const& x1, T const& y1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{v0, v1} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; +# endif + } + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat + ( + X1 const& x1, Y1 const& y1, + X2 const& x2, Y2 const& y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(static_cast(x1), value_type(y1)), col_type(static_cast(x2), value_type(y2)) } +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(static_cast(x1), value_type(y1)); + this->value[1] = col_type(static_cast(x2), value_type(y2)); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- mat2x2 matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type const& mat<2, 2, T, Q>::operator[](typename mat<2, 2, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator=(mat<2, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(U scalar) + { + this->value[0] += scalar; + this->value[1] += scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator+=(mat<2, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(U scalar) + { + this->value[0] -= scalar; + this->value[1] -= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator-=(mat<2, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(U scalar) + { + this->value[0] *= scalar; + this->value[1] *= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator*=(mat<2, 2, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(U scalar) + { + this->value[0] /= scalar; + this->value[1] /= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator/=(mat<2, 2, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q>& mat<2, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> mat<2, 2, T, Q>::operator++(int) + { + mat<2, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> mat<2, 2, T, Q>::operator--(int) + { + mat<2, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator+(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator+(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator-(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + scalar - m[0], + scalar - m[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator-(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator*(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type operator* + ( + mat<2, 2, T, Q> const& m, + typename mat<2, 2, T, Q>::row_type const& v + ) + { + return vec<2, T, Q>( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::row_type operator* + ( + typename mat<2, 2, T, Q>::col_type const& v, + mat<2, 2, T, Q> const& m + ) + { + return vec<2, T, Q>( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<2, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m, T scalar) + { + return mat<2, 2, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator/(T scalar, mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::col_type operator/(mat<2, 2, T, Q> const& m, typename mat<2, 2, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 2, T, Q>::row_type operator/(typename mat<2, 2, T, Q>::col_type const& v, mat<2, 2, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator/(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + mat<2, 2, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<2, 2, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat2x3.hpp b/thirdparty/glm/glm/detail/type_mat2x3.hpp new file mode 100644 index 0000000..b65d1c5 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x3.hpp @@ -0,0 +1,159 @@ +/// @ref core +/// @file glm/detail/type_mat2x3.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 3, T, Q> type; + typedef mat<3, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<2, 3, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, T z0, + T x1, T y1, T z1); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1); + + // -- Conversions -- + + template + GLM_CTOR_DECL mat( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2); + + template + GLM_CTOR_DECL mat( + vec<3, U, Q> const& v1, + vec<3, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator+=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator-=(mat<2, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator++ (); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 3, T, Q> & operator-- (); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 3, T, Q>::col_type operator*(mat<2, 3, T, Q> const& m, typename mat<2, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 3, T, Q>::row_type operator*(typename mat<2, 3, T, Q>::col_type const& v, mat<2, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x3.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat2x3.inl b/thirdparty/glm/glm/detail/type_mat2x3.inl new file mode 100644 index 0000000..345ac8a --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x3.inl @@ -0,0 +1,510 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m.value[0]; + this->value[1] = m.value[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(T scalar) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(scalar, 0, 0), col_type(0, scalar, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(scalar, 0, 0); + this->value[1] = col_type(0, scalar, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat + ( + T x0, T y0, T z0, + T x1, T y1, T z1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1), col_type(x2, y2, z2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1); + this->value[1] = col_type(x2, y2, z2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 3, T, Q>::col_type & mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 3, T, Q>::col_type const& mat<2, 3, T, Q>::operator[](typename mat<2, 3, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator=(mat<2, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator+=(mat<2, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator-=(mat<2, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q>& mat<2, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> & mat<2, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> mat<2, 3, T, Q>::operator++(int) + { + mat<2, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> mat<2, 3, T, Q>::operator--(int) + { + mat<2, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator+(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator-(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator*(T scalar, mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 3, T, Q>::col_type operator* + ( + mat<2, 3, T, Q> const& m, + typename mat<2, 3, T, Q>::row_type const& v) + { + return typename mat<2, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y, + m[0][2] * v.x + m[1][2] * v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 3, T, Q>::row_type operator* + ( + typename mat<2, 3, T, Q>::col_type const& v, + mat<2, 3, T, Q> const& m) + { + return typename mat<2, 3, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + T SrcA00 = m1[0][0]; + T SrcA01 = m1[0][1]; + T SrcA02 = m1[0][2]; + T SrcA10 = m1[1][0]; + T SrcA11 = m1[1][1]; + T SrcA12 = m1[1][2]; + + T SrcB00 = m2[0][0]; + T SrcB01 = m2[0][1]; + T SrcB10 = m2[1][0]; + T SrcB11 = m2[1][1]; + T SrcB20 = m2[2][0]; + T SrcB21 = m2[2][1]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<2, 3, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator/(mat<2, 3, T, Q> const& m, T scalar) + { + return mat<2, 3, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator/(T scalar, mat<2, 3, T, Q> const& m) + { + return mat<2, 3, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<2, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat2x4.hpp b/thirdparty/glm/glm/detail/type_mat2x4.hpp new file mode 100644 index 0000000..7ca43e5 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x4.hpp @@ -0,0 +1,161 @@ +/// @ref core +/// @file glm/detail/type_mat2x4.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<2, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<2, T, Q> row_type; + typedef mat<2, 4, T, Q> type; + typedef mat<4, 2, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[2]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 2; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<2, 4, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_CTOR_DECL mat( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2); + + template + GLM_CTOR_DECL mat( + vec<4, U, Q> const& v1, + vec<4, V, Q> const& v2); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator+=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator-=(mat<2, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator++ (); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<2, 4, T, Q> & operator-- (); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat2x4.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat2x4.inl b/thirdparty/glm/glm/detail/type_mat2x4.inl new file mode 100644 index 0000000..1c182c9 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat2x4.inl @@ -0,0 +1,520 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat + ( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0, w0), col_type(x1, y1, z1, w1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(col_type const& v0, col_type const& v1) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1, w1); + this->value[1] = col_type(x2, y2, z2, w2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 4, T, Q>::col_type & mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 4, T, Q>::col_type const& mat<2, 4, T, Q>::operator[](typename mat<2, 4, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator=(mat<2, 4, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator+=(mat<2, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator-=(mat<2, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> & mat<2, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q>& mat<2, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> mat<2, 4, T, Q>::operator++(int) + { + mat<2, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> mat<2, 4, T, Q>::operator--(int) + { + mat<2, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + -m[0], + -m[1]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] + scalar, + m[1] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator+(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] - scalar, + m[1] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator-(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator*(T scalar, mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + m[0] * scalar, + m[1] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 4, T, Q>::col_type operator*(mat<2, 4, T, Q> const& m, typename mat<2, 4, T, Q>::row_type const& v) + { + return typename mat<2, 4, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y, + m[0][1] * v.x + m[1][1] * v.y, + m[0][2] * v.x + m[1][2] * v.y, + m[0][3] * v.x + m[1][3] * v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<2, 4, T, Q>::row_type operator*(typename mat<2, 4, T, Q>::col_type const& v, mat<2, 4, T, Q> const& m) + { + return typename mat<2, 4, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + T SrcA00 = m1[0][0]; + T SrcA01 = m1[0][1]; + T SrcA02 = m1[0][2]; + T SrcA03 = m1[0][3]; + T SrcA10 = m1[1][0]; + T SrcA11 = m1[1][1]; + T SrcA12 = m1[1][2]; + T SrcA13 = m1[1][3]; + + T SrcB00 = m2[0][0]; + T SrcB01 = m2[0][1]; + T SrcB10 = m2[1][0]; + T SrcB11 = m2[1][1]; + T SrcB20 = m2[2][0]; + T SrcB21 = m2[2][1]; + T SrcB30 = m2[3][0]; + T SrcB31 = m2[3][1]; + + mat<4, 4, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01; + Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11; + Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21; + Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21; + Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31; + Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31; + Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31; + Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<2, 2, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<2, 4, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator/(mat<2, 4, T, Q> const& m, T scalar) + { + return mat<2, 4, T, Q>( + m[0] / scalar, + m[1] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator/(T scalar, mat<2, 4, T, Q> const& m) + { + return mat<2, 4, T, Q>( + scalar / m[0], + scalar / m[1]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<2, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat3x2.hpp b/thirdparty/glm/glm/detail/type_mat3x2.hpp new file mode 100644 index 0000000..0249bef --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x2.hpp @@ -0,0 +1,167 @@ +/// @ref core +/// @file glm/detail/type_mat3x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 2, T, Q> type; + typedef mat<2, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<3, 2, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, + T x1, T y1, + T x2, T y2); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_CTOR_DECL mat( + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3); + + template + GLM_CTOR_DECL mat( + vec<2, V1, Q> const& v1, + vec<2, V2, Q> const& v2, + vec<2, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator+=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator-=(mat<3, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator++ (); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 2, T, Q> & operator-- (); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2); + +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x2.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat3x2.inl b/thirdparty/glm/glm/detail/type_mat3x2.inl new file mode 100644 index 0000000..cd9f46c --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x2.inl @@ -0,0 +1,532 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1), col_type(0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); + this->value[2] = col_type(0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0), col_type(0, s), col_type(0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0); + this->value[1] = col_type(0, s); + this->value[2] = col_type(0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat + ( + T x0, T y0, + T x1, T y1, + T x2, T y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat + ( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 2, T, Q>::col_type & mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 2, T, Q>::col_type const& mat<3, 2, T, Q>::operator[](typename mat<3, 2, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator=(mat<3, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator+=(mat<3, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator-=(mat<3, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> & mat<3, 2, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q>& mat<3, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> mat<3, 2, T, Q>::operator++(int) + { + mat<3, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> mat<3, 2, T, Q>::operator--(int) + { + mat<3, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator+(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator-(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator*(T scalar, mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 2, T, Q>::col_type operator*(mat<3, 2, T, Q> const& m, typename mat<3, 2, T, Q>::row_type const& v) + { + return typename mat<3, 2, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 2, T, Q>::row_type operator*(typename mat<3, 2, T, Q>::col_type const& v, mat<3, 2, T, Q> const& m) + { + return typename mat<3, 2, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1], + v.x * m[2][0] + v.y * m[2][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + const T SrcA00 = m1[0][0]; + const T SrcA01 = m1[0][1]; + const T SrcA10 = m1[1][0]; + const T SrcA11 = m1[1][1]; + const T SrcA20 = m1[2][0]; + const T SrcA21 = m1[2][1]; + + const T SrcB00 = m2[0][0]; + const T SrcB01 = m2[0][1]; + const T SrcB02 = m2[0][2]; + const T SrcB10 = m2[1][0]; + const T SrcB11 = m2[1][1]; + const T SrcB12 = m2[1][2]; + + mat<2, 2, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<3, 2, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator/(mat<3, 2, T, Q> const& m, T scalar) + { + return mat<3, 2, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator/(T scalar, mat<3, 2, T, Q> const& m) + { + return mat<3, 2, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<3, 2, T, Q> const& m1, mat<3, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat3x3.hpp b/thirdparty/glm/glm/detail/type_mat3x3.hpp new file mode 100644 index 0000000..e4cbbdc --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x3.hpp @@ -0,0 +1,184 @@ +/// @ref core +/// @file glm/detail/type_mat3x3.hpp + +#pragma once + +#include "type_vec3.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 3, T, Q> type; + typedef mat<3, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<3, 3, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, T z0, + T x1, T y1, T z1, + T x2, T y2, T z2); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_CTOR_DECL mat( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2, + X3 x3, Y3 y3, Z3 z3); + + template + GLM_CTOR_DECL mat( + vec<3, V1, Q> const& v1, + vec<3, V2, Q> const& v2, + vec<3, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator+=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator-=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator*=(mat<3, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator/=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator/=(mat<3, 3, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 3, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x3.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat3x3.inl b/thirdparty/glm/glm/detail/type_mat3x3.inl new file mode 100644 index 0000000..a9b633a --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x3.inl @@ -0,0 +1,601 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0), col_type(0, 0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); + this->value[2] = col_type(0, 0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0); + this->value[1] = col_type(0, s, 0); + this->value[2] = col_type(0, 0, s); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat + ( + T x0, T y0, T z0, + T x1, T y1, T z1, + T x2, T y2, T z2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat + ( + X1 x1, Y1 y1, Z1 z1, + X2 x2, Y2 y2, Z2 z2, + X3 x3, Y3 y3, Z3 z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1); + this->value[1] = col_type(x2, y2, z2); + this->value[2] = col_type(x3, y3, z3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type & mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type const& mat<3, 3, T, Q>::operator[](typename mat<3, 3, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator=(mat<3, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator+=(mat<3, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator-=(mat<3, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator*=(mat<3, 3, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator/=(mat<3, 3, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> & mat<3, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> mat<3, 3, T, Q>::operator++(int) + { + mat<3, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> mat<3, 3, T, Q>::operator--(int) + { + mat<3, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator+(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator+(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator-(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + scalar - m[0], + scalar - m[1], + scalar - m[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator-(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator*(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type operator*(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v) + { + return typename mat<3, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::row_type operator*(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m) + { + return typename mat<3, 3, T, Q>::row_type( + m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z, + m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z, + m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA02 = m1[0][2]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA12 = m1[1][2]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA22 = m1[2][2]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB20 = m2[2][0]; + T const SrcB21 = m2[2][1]; + T const SrcB22 = m2[2][2]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<3, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m, T scalar) + { + return mat<3, 3, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator/(T scalar, mat<3, 3, T, Q> const& m) + { + return mat<3, 3, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::col_type operator/(mat<3, 3, T, Q> const& m, typename mat<3, 3, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 3, T, Q>::row_type operator/(typename mat<3, 3, T, Q>::col_type const& v, mat<3, 3, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator/(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + mat<3, 3, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<3, 3, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat3x4.hpp b/thirdparty/glm/glm/detail/type_mat3x4.hpp new file mode 100644 index 0000000..f9913d2 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x4.hpp @@ -0,0 +1,166 @@ +/// @ref core +/// @file glm/detail/type_mat3x4.hpp + +#pragma once + +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<3, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<3, T, Q> row_type; + typedef mat<3, 4, T, Q> type; + typedef mat<4, 3, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[3]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 3; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<3, 4, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1, + T x2, T y2, T z2, T w2); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3> + GLM_CTOR_DECL mat( + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2, + X3 x3, Y3 y3, Z3 z3, W3 w3); + + template + GLM_CTOR_DECL mat( + vec<4, V1, Q> const& v1, + vec<4, V2, Q> const& v2, + vec<4, V3, Q> const& v3); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator+=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator-=(mat<3, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<3, 4, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 4, T, Q>::col_type operator*(mat<3, 4, T, Q> const& m, typename mat<3, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<3, 4, T, Q>::row_type operator*(typename mat<3, 4, T, Q>::col_type const& v, mat<3, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat3x4.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat3x4.inl b/thirdparty/glm/glm/detail/type_mat3x4.inl new file mode 100644 index 0000000..209e9d9 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat3x4.inl @@ -0,0 +1,578 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0), col_type(0, 0, 1, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); + this->value[2] = col_type(0, 0, s, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat + ( + T x0, T y0, T z0, T w0, + T x1, T y1, T z1, T w1, + T x2, T y2, T z2, T w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, typename Z0, typename W0, + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat + ( + X0 x0, Y0 y0, Z0 z0, W0 w0, + X1 x1, Y1 y1, Z1 z1, W1 w1, + X2 x2, Y2 y2, Z2 z2, W2 w2 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(vec<4, V1, Q> const& v0, vec<4, V2, Q> const& v1, vec<4, V3, Q> const& v2) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 4, T, Q>::col_type & mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 4, T, Q>::col_type const& mat<3, 4, T, Q>::operator[](typename mat<3, 4, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator=(mat<3, 4, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator+=(mat<3, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator-=(mat<3, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> & mat<3, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q>& mat<3, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> mat<3, 4, T, Q>::operator++(int) + { + mat<3, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> mat<3, 4, T, Q>::operator--(int) + { + mat<3, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + -m[0], + -m[1], + -m[2]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator+(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator-(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator*(T scalar, mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 4, T, Q>::col_type operator* + ( + mat<3, 4, T, Q> const& m, + typename mat<3, 4, T, Q>::row_type const& v + ) + { + return typename mat<3, 4, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z, + m[0][3] * v.x + m[1][3] * v.y + m[2][3] * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<3, 4, T, Q>::row_type operator* + ( + typename mat<3, 4, T, Q>::col_type const& v, + mat<3, 4, T, Q> const& m + ) + { + return typename mat<3, 4, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2] + v.w * m[0][3], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2] + v.w * m[1][3], + v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2] + v.w * m[2][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + const T SrcA00 = m1[0][0]; + const T SrcA01 = m1[0][1]; + const T SrcA02 = m1[0][2]; + const T SrcA03 = m1[0][3]; + const T SrcA10 = m1[1][0]; + const T SrcA11 = m1[1][1]; + const T SrcA12 = m1[1][2]; + const T SrcA13 = m1[1][3]; + const T SrcA20 = m1[2][0]; + const T SrcA21 = m1[2][1]; + const T SrcA22 = m1[2][2]; + const T SrcA23 = m1[2][3]; + + const T SrcB00 = m2[0][0]; + const T SrcB01 = m2[0][1]; + const T SrcB02 = m2[0][2]; + const T SrcB10 = m2[1][0]; + const T SrcB11 = m2[1][1]; + const T SrcB12 = m2[1][2]; + const T SrcB20 = m2[2][0]; + const T SrcB21 = m2[2][1]; + const T SrcB22 = m2[2][2]; + const T SrcB30 = m2[3][0]; + const T SrcB31 = m2[3][1]; + const T SrcB32 = m2[3][2]; + + mat<4, 4, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02; + Result[0][3] = SrcA03 * SrcB00 + SrcA13 * SrcB01 + SrcA23 * SrcB02; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12; + Result[1][3] = SrcA03 * SrcB10 + SrcA13 * SrcB11 + SrcA23 * SrcB12; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22; + Result[2][3] = SrcA03 * SrcB20 + SrcA13 * SrcB21 + SrcA23 * SrcB22; + Result[3][0] = SrcA00 * SrcB30 + SrcA10 * SrcB31 + SrcA20 * SrcB32; + Result[3][1] = SrcA01 * SrcB30 + SrcA11 * SrcB31 + SrcA21 * SrcB32; + Result[3][2] = SrcA02 * SrcB30 + SrcA12 * SrcB31 + SrcA22 * SrcB32; + Result[3][3] = SrcA03 * SrcB30 + SrcA13 * SrcB31 + SrcA23 * SrcB32; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<2, 3, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<3, 4, T, Q> const& m1, mat<3, 3, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator/(mat<3, 4, T, Q> const& m, T scalar) + { + return mat<3, 4, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator/(T scalar, mat<3, 4, T, Q> const& m) + { + return mat<3, 4, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<3, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat4x2.hpp b/thirdparty/glm/glm/detail/type_mat4x2.hpp new file mode 100644 index 0000000..7057d4c --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x2.hpp @@ -0,0 +1,171 @@ +/// @ref core +/// @file glm/detail/type_mat4x2.hpp + +#pragma once + +#include "type_vec2.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 2, T, Q> + { + typedef vec<2, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 2, T, Q> type; + typedef mat<2, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 4; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<4, 2, T, P> const& m); + + GLM_CTOR_DECL mat(T scalar); + GLM_CTOR_DECL mat( + T x0, T y0, + T x1, T y1, + T x2, T y2, + T x3, T y3); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_CTOR_DECL mat( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3); + + template + GLM_CTOR_DECL mat( + vec<2, V1, Q> const& v1, + vec<2, V2, Q> const& v2, + vec<2, V3, Q> const& v3, + vec<2, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL mat(mat<4, 2, U, P> const& m); + + GLM_CTOR_DECL mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL mat(mat<4, 3, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 4, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator+=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator-=(mat<4, 2, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator++ (); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 2, T, Q> & operator-- (); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x2.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat4x2.inl b/thirdparty/glm/glm/detail/type_mat4x2.inl new file mode 100644 index 0000000..2b9b617 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x2.inl @@ -0,0 +1,574 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0), col_type(0, 1), col_type(0, 0), col_type(0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0); + this->value[1] = col_type(0, 1); + this->value[2] = col_type(0, 0); + this->value[3] = col_type(0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 2, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0), col_type(0, s), col_type(0, 0), col_type(0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0); + this->value[1] = col_type(0, s); + this->value[2] = col_type(0, 0); + this->value[3] = col_type(0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat + ( + T x0, T y0, + T x1, T y1, + T x2, T y2, + T x3, T y3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); + this->value[3] = col_type(x3, y3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, + typename X1, typename Y1, + typename X2, typename Y2, + typename X3, typename Y3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat + ( + X0 x0, Y0 y0, + X1 x1, Y1 y1, + X2 x2, Y2 y2, + X3 x3, Y3 y3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0), col_type(x1, y1), col_type(x2, y2), col_type(x3, y3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0); + this->value[1] = col_type(x1, y1); + this->value[2] = col_type(x2, y2); + this->value[3] = col_type(x3, y3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(vec<2, V0, Q> const& v0, vec<2, V1, Q> const& v1, vec<2, V2, Q> const& v2, vec<2, V3, Q> const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v0); + this->value[1] = col_type(v1); + this->value[2] = col_type(v2); + this->value[3] = col_type(v3); +# endif + } + + // -- Conversion -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 2, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 2, T, Q>::col_type & mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 2, T, Q>::col_type const& mat<4, 2, T, Q>::operator[](typename mat<4, 2, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q>& mat<4, 2, T, Q>::operator=(mat<4, 2, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator+=(mat<4, 2, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator-=(mat<4, 2, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> & mat<4, 2, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> mat<4, 2, T, Q>::operator++(int) + { + mat<4, 2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> mat<4, 2, T, Q>::operator--(int) + { + mat<4, 2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar, + m[3] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator+(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar, + m[3] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator-(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator*(T scalar, mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 2, T, Q>::col_type operator*(mat<4, 2, T, Q> const& m, typename mat<4, 2, T, Q>::row_type const& v) + { + return typename mat<4, 2, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 2, T, Q>::row_type operator*(typename mat<4, 2, T, Q>::col_type const& v, mat<4, 2, T, Q> const& m) + { + return typename mat<4, 2, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1], + v.x * m[1][0] + v.y * m[1][1], + v.x * m[2][0] + v.y * m[2][1], + v.x * m[3][0] + v.y * m[3][1]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA30 = m1[3][0]; + T const SrcA31 = m1[3][1]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB03 = m2[0][3]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB13 = m2[1][3]; + + mat<2, 2, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator*(mat<4, 2, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 2, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator/(mat<4, 2, T, Q> const& m, T scalar) + { + return mat<4, 2, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar, + m[3] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 2, T, Q> operator/(T scalar, mat<4, 2, T, Q> const& m) + { + return mat<4, 2, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2], + scalar / m[3]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<4, 2, T, Q> const& m1, mat<4, 2, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat4x3.hpp b/thirdparty/glm/glm/detail/type_mat4x3.hpp new file mode 100644 index 0000000..52a38d8 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x3.hpp @@ -0,0 +1,171 @@ +/// @ref core +/// @file glm/detail/type_mat4x3.hpp + +#pragma once + +#include "type_vec3.hpp" +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 3, T, Q> + { + typedef vec<3, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 3, T, Q> type; + typedef mat<3, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length() { return 4; } + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<4, 3, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T s); + GLM_CTOR_DECL mat( + T const& x0, T const& y0, T const& z0, + T const& x1, T const& y1, T const& z1, + T const& x2, T const& y2, T const& z2, + T const& x3, T const& y3, T const& z3); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3, + typename X4, typename Y4, typename Z4> + GLM_CTOR_DECL mat( + X1 const& x1, Y1 const& y1, Z1 const& z1, + X2 const& x2, Y2 const& y2, Z2 const& z2, + X3 const& x3, Y3 const& y3, Z3 const& z3, + X4 const& x4, Y4 const& y4, Z4 const& z4); + + template + GLM_CTOR_DECL mat( + vec<3, V1, Q> const& v1, + vec<3, V2, Q> const& v2, + vec<3, V3, Q> const& v3, + vec<3, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL mat(mat<4, 3, U, P> const& m); + + GLM_CTOR_DECL mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL mat(mat<4, 4, T, Q> const& x); + GLM_CTOR_DECL mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL mat(mat<3, 4, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator+=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator-=(mat<4, 3, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q> & operator/=(U s); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q>& operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 3, T, Q>& operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator*(T scalar, mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 3, T, Q>::col_type operator*(mat<4, 3, T, Q> const& m, typename mat<4, 3, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 3, T, Q>::row_type operator*(typename mat<4, 3, T, Q>::col_type const& v, mat<4, 3, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 3, T, Q> operator/(T scalar, mat<4, 3, T, Q> const& m); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x3.inl" +#endif //GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_mat4x3.inl b/thirdparty/glm/glm/detail/type_mat4x3.inl new file mode 100644 index 0000000..8430bc0 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x3.inl @@ -0,0 +1,598 @@ +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0), col_type(0, 1, 0), col_type(0, 0, 1), col_type(0, 0, 0)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0); + this->value[1] = col_type(0, 1, 0); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0, 0, 0); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 3, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0), col_type(0, s, 0), col_type(0, 0, s), col_type(0, 0, 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0); + this->value[1] = col_type(0, s, 0); + this->value[2] = col_type(0, 0, s); + this->value[3] = col_type(0, 0, 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat + ( + T const& x0, T const& y0, T const& z0, + T const& x1, T const& y1, T const& z1, + T const& x2, T const& y2, T const& z2, + T const& x3, T const& y3, T const& z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); + this->value[3] = col_type(x3, y3, z3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + // -- Conversion constructors -- + + template + template< + typename X0, typename Y0, typename Z0, + typename X1, typename Y1, typename Z1, + typename X2, typename Y2, typename Z2, + typename X3, typename Y3, typename Z3> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat + ( + X0 const& x0, Y0 const& y0, Z0 const& z0, + X1 const& x1, Y1 const& y1, Z1 const& z1, + X2 const& x2, Y2 const& y2, Z2 const& z2, + X3 const& x3, Y3 const& y3, Z3 const& z3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x0, y0, z0), col_type(x1, y1, z1), col_type(x2, y2, z2), col_type(x3, y3, z3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0); + this->value[1] = col_type(x1, y1, z1); + this->value[2] = col_type(x2, y2, z2); + this->value[3] = col_type(x3, y3, z3); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(vec<3, V1, Q> const& v1, vec<3, V2, Q> const& v2, vec<3, V3, Q> const& v3, vec<3, V4, Q> const& v4) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3), col_type(v4)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); + this->value[3] = col_type(v4); +# endif + } + + // -- Matrix conversions -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 3, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(0, 0, 1); + this->value[3] = col_type(0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 1), col_type(m[3], 0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 1); + this->value[3] = col_type(m[3], 0); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(0); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 3, T, Q>::col_type & mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 3, T, Q>::col_type const& mat<4, 3, T, Q>::operator[](typename mat<4, 3, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary updatable operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q>& mat<4, 3, T, Q>::operator=(mat<4, 3, U, Q> const& m) + { + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator+=(mat<4, 3, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator-=(mat<4, 3, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> & mat<4, 3, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> mat<4, 3, T, Q>::operator++(int) + { + mat<4, 3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> mat<4, 3, T, Q>::operator--(int) + { + mat<4, 3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m, T scalar) + { + return mat<4, 3, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar, + m[3] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator+(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m, T scalar) + { + return mat<4, 3, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar, + m[3] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator-(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m, T scalar) + { + return mat<4, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator*(T scalar, mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 3, T, Q>::col_type operator* + ( + mat<4, 3, T, Q> const& m, + typename mat<4, 3, T, Q>::row_type const& v) + { + return typename mat<4, 3, T, Q>::col_type( + m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w, + m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w, + m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * v.w); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 3, T, Q>::row_type operator* + ( + typename mat<4, 3, T, Q>::col_type const& v, + mat<4, 3, T, Q> const& m) + { + return typename mat<4, 3, T, Q>::row_type( + v.x * m[0][0] + v.y * m[0][1] + v.z * m[0][2], + v.x * m[1][0] + v.y * m[1][1] + v.z * m[1][2], + v.x * m[2][0] + v.y * m[2][1] + v.z * m[2][2], + v.x * m[3][0] + v.y * m[3][1] + v.z * m[3][2]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + T const SrcA00 = m1[0][0]; + T const SrcA01 = m1[0][1]; + T const SrcA02 = m1[0][2]; + T const SrcA10 = m1[1][0]; + T const SrcA11 = m1[1][1]; + T const SrcA12 = m1[1][2]; + T const SrcA20 = m1[2][0]; + T const SrcA21 = m1[2][1]; + T const SrcA22 = m1[2][2]; + T const SrcA30 = m1[3][0]; + T const SrcA31 = m1[3][1]; + T const SrcA32 = m1[3][2]; + + T const SrcB00 = m2[0][0]; + T const SrcB01 = m2[0][1]; + T const SrcB02 = m2[0][2]; + T const SrcB03 = m2[0][3]; + T const SrcB10 = m2[1][0]; + T const SrcB11 = m2[1][1]; + T const SrcB12 = m2[1][2]; + T const SrcB13 = m2[1][3]; + T const SrcB20 = m2[2][0]; + T const SrcB21 = m2[2][1]; + T const SrcB22 = m2[2][2]; + T const SrcB23 = m2[2][3]; + + mat<3, 3, T, Q> Result; + Result[0][0] = SrcA00 * SrcB00 + SrcA10 * SrcB01 + SrcA20 * SrcB02 + SrcA30 * SrcB03; + Result[0][1] = SrcA01 * SrcB00 + SrcA11 * SrcB01 + SrcA21 * SrcB02 + SrcA31 * SrcB03; + Result[0][2] = SrcA02 * SrcB00 + SrcA12 * SrcB01 + SrcA22 * SrcB02 + SrcA32 * SrcB03; + Result[1][0] = SrcA00 * SrcB10 + SrcA10 * SrcB11 + SrcA20 * SrcB12 + SrcA30 * SrcB13; + Result[1][1] = SrcA01 * SrcB10 + SrcA11 * SrcB11 + SrcA21 * SrcB12 + SrcA31 * SrcB13; + Result[1][2] = SrcA02 * SrcB10 + SrcA12 * SrcB11 + SrcA22 * SrcB12 + SrcA32 * SrcB13; + Result[2][0] = SrcA00 * SrcB20 + SrcA10 * SrcB21 + SrcA20 * SrcB22 + SrcA30 * SrcB23; + Result[2][1] = SrcA01 * SrcB20 + SrcA11 * SrcB21 + SrcA21 * SrcB22 + SrcA31 * SrcB23; + Result[2][2] = SrcA02 * SrcB20 + SrcA12 * SrcB21 + SrcA22 * SrcB22 + SrcA32 * SrcB23; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator*(mat<4, 3, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 3, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3], + m1[0][0] * m2[3][0] + m1[1][0] * m2[3][1] + m1[2][0] * m2[3][2] + m1[3][0] * m2[3][3], + m1[0][1] * m2[3][0] + m1[1][1] * m2[3][1] + m1[2][1] * m2[3][2] + m1[3][1] * m2[3][3], + m1[0][2] * m2[3][0] + m1[1][2] * m2[3][1] + m1[2][2] * m2[3][2] + m1[3][2] * m2[3][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator/(mat<4, 3, T, Q> const& m, T scalar) + { + return mat<4, 3, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar, + m[3] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 3, T, Q> operator/(T scalar, mat<4, 3, T, Q> const& m) + { + return mat<4, 3, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2], + scalar / m[3]); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<4, 3, T, Q> const& m1, mat<4, 3, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +} //namespace glm diff --git a/thirdparty/glm/glm/detail/type_mat4x4.hpp b/thirdparty/glm/glm/detail/type_mat4x4.hpp new file mode 100644 index 0000000..ad7597b --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x4.hpp @@ -0,0 +1,189 @@ +/// @ref core +/// @file glm/detail/type_mat4x4.hpp + +#pragma once + +#include "type_vec4.hpp" +#include +#include + +namespace glm +{ + template + struct mat<4, 4, T, Q> + { + typedef vec<4, T, Q> col_type; + typedef vec<4, T, Q> row_type; + typedef mat<4, 4, T, Q> type; + typedef mat<4, 4, T, Q> transpose_type; + typedef T value_type; + + private: + col_type value[4]; + + public: + // -- Accesses -- + + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL GLM_CONSTEXPR col_type & operator[](length_type i) GLM_NOEXCEPT; + GLM_FUNC_DECL GLM_CONSTEXPR col_type const& operator[](length_type i) const GLM_NOEXCEPT; + + // -- Constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR mat() GLM_DEFAULT_CTOR; + template + GLM_CTOR_DECL mat(mat<4, 4, T, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(T s); + GLM_CTOR_DECL mat( + T const& x0, T const& y0, T const& z0, T const& w0, + T const& x1, T const& y1, T const& z1, T const& w1, + T const& x2, T const& y2, T const& z2, T const& w2, + T const& x3, T const& y3, T const& z3, T const& w3); + GLM_CTOR_DECL mat( + col_type const& v0, + col_type const& v1, + col_type const& v2, + col_type const& v3); + + // -- Conversions -- + + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3, + typename X4, typename Y4, typename Z4, typename W4> + GLM_CTOR_DECL mat( + X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1, + X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2, + X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3, + X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4); + + template + GLM_CTOR_DECL mat( + vec<4, V1, Q> const& v1, + vec<4, V2, Q> const& v2, + vec<4, V3, Q> const& v3, + vec<4, V4, Q> const& v4); + + // -- Matrix conversions -- + + template + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 4, U, P> const& m); + + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 3, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<2, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 2, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<3, 4, T, Q> const& x); + GLM_CTOR_DECL GLM_EXPLICIT mat(mat<4, 3, T, Q> const& x); + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator+=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator+=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator-=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator-=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator*=(mat<4, 4, U, Q> const& m); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator/=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator/=(mat<4, 4, U, Q> const& m); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR mat<4, 4, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator--(int); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator+(T scalar, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator-(T scalar, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator*(T scalar, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type operator*(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 4, T, Q>::row_type operator*(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator/(T scalar, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_mat4x4.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_mat4x4.inl b/thirdparty/glm/glm/detail/type_mat4x4.inl new file mode 100644 index 0000000..116731d --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x4.inl @@ -0,0 +1,706 @@ +#include "../matrix.hpp" + +namespace glm +{ + // -- Constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat() +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALIZER_LIST + : value{col_type(1, 0, 0, 0), col_type(0, 1, 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if GLM_CONFIG_CTOR_INIT == GLM_CTOR_INITIALISATION + this->value[0] = col_type(1, 0, 0, 0); + this->value[1] = col_type(0, 1, 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 4, T, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(T s) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(s, 0, 0, 0), col_type(0, s, 0, 0), col_type(0, 0, s, 0), col_type(0, 0, 0, s)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(s, 0, 0, 0); + this->value[1] = col_type(0, s, 0, 0); + this->value[2] = col_type(0, 0, s, 0); + this->value[3] = col_type(0, 0, 0, s); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat + ( + T const& x0, T const& y0, T const& z0, T const& w0, + T const& x1, T const& y1, T const& z1, T const& w1, + T const& x2, T const& y2, T const& z2, T const& w2, + T const& x3, T const& y3, T const& z3, T const& w3 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{ + col_type(x0, y0, z0, w0), + col_type(x1, y1, z1, w1), + col_type(x2, y2, z2, w2), + col_type(x3, y3, z3, w3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x0, y0, z0, w0); + this->value[1] = col_type(x1, y1, z1, w1); + this->value[2] = col_type(x2, y2, z2, w2); + this->value[3] = col_type(x3, y3, z3, w3); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(col_type const& v0, col_type const& v1, col_type const& v2, col_type const& v3) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v0), col_type(v1), col_type(v2), col_type(v3)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = v0; + this->value[1] = v1; + this->value[2] = v2; + this->value[3] = v3; +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 4, U, P> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(m[3])} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0]); + this->value[1] = col_type(m[1]); + this->value[2] = col_type(m[2]); + this->value[3] = col_type(m[3]); +# endif + } + + // -- Conversions -- + + template + template< + typename X1, typename Y1, typename Z1, typename W1, + typename X2, typename Y2, typename Z2, typename W2, + typename X3, typename Y3, typename Z3, typename W3, + typename X4, typename Y4, typename Z4, typename W4> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat + ( + X1 const& x1, Y1 const& y1, Z1 const& z1, W1 const& w1, + X2 const& x2, Y2 const& y2, Z2 const& z2, W2 const& w2, + X3 const& x3, Y3 const& y3, Z3 const& z3, W3 const& w3, + X4 const& x4, Y4 const& y4, Z4 const& z4, W4 const& w4 + ) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(x1, y1, z1, w1), col_type(x2, y2, z2, w2), col_type(x3, y3, z3, w3), col_type(x4, y4, z4, w4)} +# endif + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 5th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 6th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 7th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 8th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 9th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 10th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 11th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 12th parameter type invalid."); + + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 13th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 14th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 15th parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 16th parameter type invalid."); + +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(x1, y1, z1, w1); + this->value[1] = col_type(x2, y2, z2, w2); + this->value[2] = col_type(x3, y3, z3, w3); + this->value[3] = col_type(x4, y4, z4, w4); +# endif + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(vec<4, V1, Q> const& v1, vec<4, V2, Q> const& v2, vec<4, V3, Q> const& v3, vec<4, V4, Q> const& v4) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(v1), col_type(v2), col_type(v3), col_type(v4)} +# endif + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 1st parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 2nd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 3rd parameter type invalid."); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer || GLM_CONFIG_UNRESTRICTED_GENTYPE, "*mat4x4 constructor only takes float and integer types, 4th parameter type invalid."); + +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(v1); + this->value[1] = col_type(v2); + this->value[2] = col_type(v3); + this->value[3] = col_type(v4); +# endif + } + + // -- Matrix conversions -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(m[2], 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(m[2], 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<2, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 2, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0, 0), col_type(m[1], 0, 0), col_type(0, 0, 1, 0), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0, 0); + this->value[1] = col_type(m[1], 0, 0); + this->value[2] = col_type(0, 0, 1, 0); + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<3, 4, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0]), col_type(m[1]), col_type(m[2]), col_type(0, 0, 0, 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = col_type(0, 0, 0, 1); +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>::mat(mat<4, 3, T, Q> const& m) +# if GLM_HAS_INITIALIZER_LISTS + : value{col_type(m[0], 0), col_type(m[1], 0), col_type(m[2], 0), col_type(m[3], 1)} +# endif + { +# if !GLM_HAS_INITIALIZER_LISTS + this->value[0] = col_type(m[0], 0); + this->value[1] = col_type(m[1], 0); + this->value[2] = col_type(m[2], 0); + this->value[3] = col_type(m[3], 1); +# endif + } + + // -- Accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type & mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i) GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type const& mat<4, 4, T, Q>::operator[](typename mat<4, 4, T, Q>::length_type i) const GLM_NOEXCEPT + { + GLM_ASSERT_LENGTH(i, this->length()); + return this->value[i]; + } + + // -- Unary arithmetic operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator=(mat<4, 4, U, Q> const& m) + { + //memcpy could be faster + //memcpy(&this->value, &m.value, 16 * sizeof(valType)); + this->value[0] = m[0]; + this->value[1] = m[1]; + this->value[2] = m[2]; + this->value[3] = m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(U s) + { + this->value[0] += s; + this->value[1] += s; + this->value[2] += s; + this->value[3] += s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q>& mat<4, 4, T, Q>::operator+=(mat<4, 4, U, Q> const& m) + { + this->value[0] += m[0]; + this->value[1] += m[1]; + this->value[2] += m[2]; + this->value[3] += m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator-=(U s) + { + this->value[0] -= s; + this->value[1] -= s; + this->value[2] -= s; + this->value[3] -= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator-=(mat<4, 4, U, Q> const& m) + { + this->value[0] -= m[0]; + this->value[1] -= m[1]; + this->value[2] -= m[2]; + this->value[3] -= m[3]; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator*=(U s) + { + this->value[0] *= s; + this->value[1] *= s; + this->value[2] *= s; + this->value[3] *= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator*=(mat<4, 4, U, Q> const& m) + { + return (*this = *this * m); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator/=(U s) + { + this->value[0] /= s; + this->value[1] /= s; + this->value[2] /= s; + this->value[3] /= s; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator/=(mat<4, 4, U, Q> const& m) + { + return *this *= inverse(m); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator++() + { + ++this->value[0]; + ++this->value[1]; + ++this->value[2]; + ++this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> & mat<4, 4, T, Q>::operator--() + { + --this->value[0]; + --this->value[1]; + --this->value[2]; + --this->value[3]; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> mat<4, 4, T, Q>::operator++(int) + { + mat<4, 4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> mat<4, 4, T, Q>::operator--(int) + { + mat<4, 4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m) + { + return m; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + -m[0], + -m[1], + -m[2], + -m[3]); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m, T scalar) + { + return mat<4, 4, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar, + m[3] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator+(T scalar, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0] + scalar, + m[1] + scalar, + m[2] + scalar, + m[3] + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator+(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 4, T, Q>( + m1[0] + m2[0], + m1[1] + m2[1], + m1[2] + m2[2], + m1[3] + m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m, T scalar) + { + return mat<4, 4, T, Q>( + m[0] - scalar, + m[1] - scalar, + m[2] - scalar, + m[3] - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator-(T scalar, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + scalar - m[0], + scalar - m[1], + scalar - m[2], + scalar - m[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator-(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return mat<4, 4, T, Q>( + m1[0] - m2[0], + m1[1] - m2[1], + m1[2] - m2[2], + m1[3] - m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m, T scalar) + { + return mat<4, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator*(T scalar, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0] * scalar, + m[1] * scalar, + m[2] * scalar, + m[3] * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type operator* + ( + mat<4, 4, T, Q> const& m, + typename mat<4, 4, T, Q>::row_type const& v + ) + { +/* + __m128 v0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 v1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 v2 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v3 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(m[0].data, v0); + __m128 m1 = _mm_mul_ps(m[1].data, v1); + __m128 a0 = _mm_add_ps(m0, m1); + + __m128 m2 = _mm_mul_ps(m[2].data, v2); + __m128 m3 = _mm_mul_ps(m[3].data, v3); + __m128 a1 = _mm_add_ps(m2, m3); + + __m128 a2 = _mm_add_ps(a0, a1); + + return typename mat<4, 4, T, Q>::col_type(a2); +*/ + + typename mat<4, 4, T, Q>::col_type const Mov0(v[0]); + typename mat<4, 4, T, Q>::col_type const Mov1(v[1]); + typename mat<4, 4, T, Q>::col_type const Mul0 = m[0] * Mov0; + typename mat<4, 4, T, Q>::col_type const Mul1 = m[1] * Mov1; + typename mat<4, 4, T, Q>::col_type const Add0 = Mul0 + Mul1; + typename mat<4, 4, T, Q>::col_type const Mov2(v[2]); + typename mat<4, 4, T, Q>::col_type const Mov3(v[3]); + typename mat<4, 4, T, Q>::col_type const Mul2 = m[2] * Mov2; + typename mat<4, 4, T, Q>::col_type const Mul3 = m[3] * Mov3; + typename mat<4, 4, T, Q>::col_type const Add1 = Mul2 + Mul3; + typename mat<4, 4, T, Q>::col_type const Add2 = Add0 + Add1; + return Add2; + +/* + return typename mat<4, 4, T, Q>::col_type( + m[0][0] * v[0] + m[1][0] * v[1] + m[2][0] * v[2] + m[3][0] * v[3], + m[0][1] * v[0] + m[1][1] * v[1] + m[2][1] * v[2] + m[3][1] * v[3], + m[0][2] * v[0] + m[1][2] * v[1] + m[2][2] * v[2] + m[3][2] * v[3], + m[0][3] * v[0] + m[1][3] * v[1] + m[2][3] * v[2] + m[3][3] * v[3]); +*/ + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::row_type operator* + ( + typename mat<4, 4, T, Q>::col_type const& v, + mat<4, 4, T, Q> const& m + ) + { + return typename mat<4, 4, T, Q>::row_type( + m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2] + m[0][3] * v[3], + m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2] + m[1][3] * v[3], + m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2] + m[2][3] * v[3], + m[3][0] * v[0] + m[3][1] * v[1] + m[3][2] * v[2] + m[3][3] * v[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<2, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<2, 4, T, Q> const& m2) + { + return mat<2, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<3, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<3, 4, T, Q> const& m2) + { + return mat<3, 4, T, Q>( + m1[0][0] * m2[0][0] + m1[1][0] * m2[0][1] + m1[2][0] * m2[0][2] + m1[3][0] * m2[0][3], + m1[0][1] * m2[0][0] + m1[1][1] * m2[0][1] + m1[2][1] * m2[0][2] + m1[3][1] * m2[0][3], + m1[0][2] * m2[0][0] + m1[1][2] * m2[0][1] + m1[2][2] * m2[0][2] + m1[3][2] * m2[0][3], + m1[0][3] * m2[0][0] + m1[1][3] * m2[0][1] + m1[2][3] * m2[0][2] + m1[3][3] * m2[0][3], + m1[0][0] * m2[1][0] + m1[1][0] * m2[1][1] + m1[2][0] * m2[1][2] + m1[3][0] * m2[1][3], + m1[0][1] * m2[1][0] + m1[1][1] * m2[1][1] + m1[2][1] * m2[1][2] + m1[3][1] * m2[1][3], + m1[0][2] * m2[1][0] + m1[1][2] * m2[1][1] + m1[2][2] * m2[1][2] + m1[3][2] * m2[1][3], + m1[0][3] * m2[1][0] + m1[1][3] * m2[1][1] + m1[2][3] * m2[1][2] + m1[3][3] * m2[1][3], + m1[0][0] * m2[2][0] + m1[1][0] * m2[2][1] + m1[2][0] * m2[2][2] + m1[3][0] * m2[2][3], + m1[0][1] * m2[2][0] + m1[1][1] * m2[2][1] + m1[2][1] * m2[2][2] + m1[3][1] * m2[2][3], + m1[0][2] * m2[2][0] + m1[1][2] * m2[2][1] + m1[2][2] * m2[2][2] + m1[3][2] * m2[2][3], + m1[0][3] * m2[2][0] + m1[1][3] * m2[2][1] + m1[2][3] * m2[2][2] + m1[3][3] * m2[2][3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator*(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + typename mat<4, 4, T, Q>::col_type const SrcA0 = m1[0]; + typename mat<4, 4, T, Q>::col_type const SrcA1 = m1[1]; + typename mat<4, 4, T, Q>::col_type const SrcA2 = m1[2]; + typename mat<4, 4, T, Q>::col_type const SrcA3 = m1[3]; + + typename mat<4, 4, T, Q>::col_type const SrcB0 = m2[0]; + typename mat<4, 4, T, Q>::col_type const SrcB1 = m2[1]; + typename mat<4, 4, T, Q>::col_type const SrcB2 = m2[2]; + typename mat<4, 4, T, Q>::col_type const SrcB3 = m2[3]; + + mat<4, 4, T, Q> Result; + Result[0] = SrcA0 * SrcB0[0] + SrcA1 * SrcB0[1] + SrcA2 * SrcB0[2] + SrcA3 * SrcB0[3]; + Result[1] = SrcA0 * SrcB1[0] + SrcA1 * SrcB1[1] + SrcA2 * SrcB1[2] + SrcA3 * SrcB1[3]; + Result[2] = SrcA0 * SrcB2[0] + SrcA1 * SrcB2[1] + SrcA2 * SrcB2[2] + SrcA3 * SrcB2[3]; + Result[3] = SrcA0 * SrcB3[0] + SrcA1 * SrcB3[1] + SrcA2 * SrcB3[2] + SrcA3 * SrcB3[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m, T scalar) + { + return mat<4, 4, T, Q>( + m[0] / scalar, + m[1] / scalar, + m[2] / scalar, + m[3] / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator/(T scalar, mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + scalar / m[0], + scalar / m[1], + scalar / m[2], + scalar / m[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::col_type operator/(mat<4, 4, T, Q> const& m, typename mat<4, 4, T, Q>::row_type const& v) + { + return inverse(m) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR typename mat<4, 4, T, Q>::row_type operator/(typename mat<4, 4, T, Q>::col_type const& v, mat<4, 4, T, Q> const& m) + { + return v * inverse(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> operator/(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + mat<4, 4, T, Q> m1_copy(m1); + return m1_copy /= m2; + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return (m1[0] == m2[0]) && (m1[1] == m2[1]) && (m1[2] == m2[2]) && (m1[3] == m2[3]); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2) + { + return (m1[0] != m2[0]) || (m1[1] != m2[1]) || (m1[2] != m2[2]) || (m1[3] != m2[3]); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_mat4x4_simd.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_mat4x4_simd.inl b/thirdparty/glm/glm/detail/type_mat4x4_simd.inl new file mode 100644 index 0000000..fb3a16f --- /dev/null +++ b/thirdparty/glm/glm/detail/type_mat4x4_simd.inl @@ -0,0 +1,6 @@ +/// @ref core + +namespace glm +{ + +}//namespace glm diff --git a/thirdparty/glm/glm/detail/type_quat.hpp b/thirdparty/glm/glm/detail/type_quat.hpp new file mode 100644 index 0000000..1b41e15 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_quat.hpp @@ -0,0 +1,193 @@ +/// @ref core +/// @file glm/detail/type_quat.hpp + +#pragma once + +// Dependency: +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat4x4.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" +#include "../ext/vector_relational.hpp" +#include "../ext/quaternion_relational.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/matrix_transform.hpp" + +namespace glm +{ +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + + template + struct qua + { + // -- Implementation detail -- + + typedef qua type; + typedef T value_type; + + // -- Data -- + +# if GLM_LANG & GLM_LANG_CXXMS_FLAG + union + { +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + struct { T w, x, y, z; }; +# else + struct { T x, y, z, w; }; +# endif + + typename detail::storage<4, T, detail::is_aligned::value>::type data; + }; +# else +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + T w, x, y, z; +# else + T x, y, z, w; +# endif +# endif + + // -- Component accesses -- + + typedef length_t length_type; + + /// Return the count of components of a quaternion + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR qua() GLM_DEFAULT_CTOR; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR qua(qua const& q) GLM_DEFAULT; + template + GLM_CTOR_DECL qua(qua const& q); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL qua(T s, vec<3, T, Q> const& v); + +# ifdef GLM_FORCE_QUAT_DATA_XYZW + GLM_CTOR_DECL qua(T x, T y, T z, T w); +# else + GLM_CTOR_DECL qua(T w, T x, T y, T z); +# endif + + GLM_FUNC_DECL static GLM_CONSTEXPR qua wxyz(T w, T x, T y, T z); + + // -- Conversion constructors -- + + template + GLM_CTOR_DECL GLM_EXPLICIT qua(qua const& q); + + /// Explicit conversion operators +# if GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + GLM_FUNC_DECL explicit operator mat<3, 3, T, Q>() const; + GLM_FUNC_DECL explicit operator mat<4, 4, T, Q>() const; +# endif + + /// Create a quaternion from two normalized axis + /// + /// @param u A first normalized axis + /// @param v A second normalized axis + /// @see gtc_quaternion + /// @see http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors + GLM_FUNC_DISCARD_DECL qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v); + + /// Build a quaternion from euler angles (pitch, yaw, roll), in radians. + GLM_CTOR_DECL GLM_EXPLICIT qua(vec<3, T, Q> const& eulerAngles); + GLM_CTOR_DECL GLM_EXPLICIT qua(mat<3, 3, T, Q> const& q); + GLM_CTOR_DECL GLM_EXPLICIT qua(mat<4, 4, T, Q> const& q); + + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR qua& operator=(qua const& q) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator=(qua const& q); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator+=(qua const& q); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator-=(qua const& q); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator*=(qua const& q); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator*=(U s); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR qua& operator/=(U s); + }; + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator+(qua const& q); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator-(qua const& q); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator+(qua const& q, qua const& p); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator-(qua const& q, qua const& p); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator*(qua const& q, qua const& p); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(qua const& q, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua const& q); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(qua const& q, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua const& q); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator*(qua const& q, T const& s); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator*(T const& s, qua const& q); + + template + GLM_FUNC_DECL GLM_CONSTEXPR qua operator/(qua const& q, T const& s); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(qua const& q1, qua const& q2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(qua const& q1, qua const& q2); +} //namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_quat.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_quat.inl b/thirdparty/glm/glm/detail/type_quat.inl new file mode 100644 index 0000000..6a8f987 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_quat.inl @@ -0,0 +1,424 @@ +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include "../ext/quaternion_common.hpp" +#include "../ext/quaternion_geometric.hpp" +#include + +namespace glm{ +namespace detail +{ + template + struct genTypeTrait > + { + static const genTypeEnum GENTYPE = GENTYPE_QUAT; + }; + + template + struct compute_dot, T, Aligned> + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(qua const& a, qua const& b) + { + vec<4, T, Q> tmp(a.w * b.w, a.x * b.x, a.y * b.y, a.z * b.z); + return (tmp.x + tmp.y) + (tmp.z + tmp.w); + } + }; + + template + struct compute_quat_add + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static qua call(qua const& q, qua const& p) + { + return qua::wxyz(q.w + p.w, q.x + p.x, q.y + p.y, q.z + p.z); + } + }; + + template + struct compute_quat_sub + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static qua call(qua const& q, qua const& p) + { + return qua::wxyz(q.w - p.w, q.x - p.x, q.y - p.y, q.z - p.z); + } + }; + + template + struct compute_quat_mul_scalar + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static qua call(qua const& q, T s) + { + return qua::wxyz(q.w * s, q.x * s, q.y * s, q.z * s); + } + }; + + template + struct compute_quat_div_scalar + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static qua call(qua const& q, T s) + { + return qua::wxyz(q.w / s, q.x / s, q.y / s, q.z / s); + } + }; + + template + struct compute_quat_mul_vec4 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(qua const& q, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(q * vec<3, T, Q>(v), v.w); + } + }; +}//namespace detail + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & qua::operator[](typename qua::length_type i) + { + GLM_ASSERT_LENGTH(i, this->length()); +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + return (&w)[i]; +# else + return (&x)[i]; +# endif + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& qua::operator[](typename qua::length_type i) const + { + GLM_ASSERT_LENGTH(i, this->length()); +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + return (&w)[i]; +# else + return (&x)[i]; +# endif + } + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR qua::qua() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(1), x(0), y(0), z(0) +# else + : x(0), y(0), z(0), w(1) +# endif +# endif + {} +# endif + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(q.w), x(q.x), y(q.y), z(q.z) +# else + : x(q.x), y(q.y), z(q.z), w(q.w) +# endif + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(q.w), x(q.x), y(q.y), z(q.z) +# else + : x(q.x), y(q.y), z(q.z), w(q.w) +# endif + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(T s, vec<3, T, Q> const& v) +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(s), x(v.x), y(v.y), z(v.z) +# else + : x(v.x), y(v.y), z(v.z), w(s) +# endif + {} + + template +# ifdef GLM_FORCE_QUAT_DATA_XYZW + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(T _x, T _y, T _z, T _w) +# else + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(T _w, T _x, T _y, T _z) +# endif +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(_w), x(_x), y(_y), z(_z) +# else + : x(_x), y(_y), z(_z), w(_w) +# endif + {} + + template + GLM_CONSTEXPR qua qua::wxyz(T w, T x, T y, T z) { +# ifdef GLM_FORCE_QUAT_DATA_XYZW + return qua(x, y, z, w); +# else + return qua(w, x, y, z); +# endif + } + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(qua const& q) +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + : w(static_cast(q.w)), x(static_cast(q.x)), y(static_cast(q.y)), z(static_cast(q.z)) +# else + : x(static_cast(q.x)), y(static_cast(q.y)), z(static_cast(q.z)), w(static_cast(q.w)) +# endif + {} + + //template + //GLM_FUNC_QUALIFIER qua::qua + //( + // valType const& pitch, + // valType const& yaw, + // valType const& roll + //) + //{ + // vec<3, valType> eulerAngle(pitch * valType(0.5), yaw * valType(0.5), roll * valType(0.5)); + // vec<3, valType> c = glm::cos(eulerAngle * valType(0.5)); + // vec<3, valType> s = glm::sin(eulerAngle * valType(0.5)); + // + // this->w = c.x * c.y * c.z + s.x * s.y * s.z; + // this->x = s.x * c.y * c.z - c.x * s.y * s.z; + // this->y = c.x * s.y * c.z + s.x * c.y * s.z; + // this->z = c.x * c.y * s.z - s.x * s.y * c.z; + //} + + template + GLM_FUNC_QUALIFIER qua::qua(vec<3, T, Q> const& u, vec<3, T, Q> const& v) + { + T norm_u_norm_v = sqrt(dot(u, u) * dot(v, v)); + T real_part = norm_u_norm_v + dot(u, v); + vec<3, T, Q> t; + + if(real_part < static_cast(1.e-6f) * norm_u_norm_v) + { + // If u and v are exactly opposite, rotate 180 degrees + // around an arbitrary orthogonal axis. Axis normalisation + // can happen later, when we normalise the quaternion. + real_part = static_cast(0); + t = abs(u.x) > abs(u.z) ? vec<3, T, Q>(-u.y, u.x, static_cast(0)) : vec<3, T, Q>(static_cast(0), -u.z, u.y); + } + else + { + // Otherwise, build quaternion the standard way. + t = cross(u, v); + } + + *this = normalize(qua::wxyz(real_part, t.x, t.y, t.z)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(vec<3, T, Q> const& eulerAngle) + { + vec<3, T, Q> c = glm::cos(eulerAngle * T(0.5)); + vec<3, T, Q> s = glm::sin(eulerAngle * T(0.5)); + + this->w = c.x * c.y * c.z + s.x * s.y * s.z; + this->x = s.x * c.y * c.z - c.x * s.y * s.z; + this->y = c.x * s.y * c.z + s.x * c.y * s.z; + this->z = c.x * c.y * s.z - s.x * s.y * c.z; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(mat<3, 3, T, Q> const& m) + { + *this = quat_cast(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua::qua(mat<4, 4, T, Q> const& m) + { + *this = quat_cast(m); + } + +# if GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + template + GLM_FUNC_QUALIFIER qua::operator mat<3, 3, T, Q>() const + { + return mat3_cast(*this); + } + + template + GLM_FUNC_QUALIFIER qua::operator mat<4, 4, T, Q>() const + { + return mat4_cast(*this); + } +# endif//GLM_HAS_EXPLICIT_CONVERSION_OPERATORS + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator=(qua const& q) + { + this->w = q.w; + this->x = q.x; + this->y = q.y; + this->z = q.z; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator=(qua const& q) + { + this->w = static_cast(q.w); + this->x = static_cast(q.x); + this->y = static_cast(q.y); + this->z = static_cast(q.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator+=(qua const& q) + { + return (*this = detail::compute_quat_add::value>::call(*this, qua(q))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator-=(qua const& q) + { + return (*this = detail::compute_quat_sub::value>::call(*this, qua(q))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator*=(qua const& r) + { + qua const p(*this); + qua const q(r); + + this->w = p.w * q.w - p.x * q.x - p.y * q.y - p.z * q.z; + this->x = p.w * q.x + p.x * q.w + p.y * q.z - p.z * q.y; + this->y = p.w * q.y + p.y * q.w + p.z * q.x - p.x * q.z; + this->z = p.w * q.z + p.z * q.w + p.x * q.y - p.y * q.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator*=(U s) + { + return (*this = detail::compute_quat_mul_scalar::value>::call(*this, static_cast(s))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua & qua::operator/=(U s) + { + return (*this = detail::compute_quat_div_scalar::value>::call(*this, static_cast(s))); + } + + // -- Unary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator+(qua const& q) + { + return q; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator-(qua const& q) + { + return qua::wxyz(-q.w, -q.x, -q.y, -q.z); + } + + // -- Binary operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator+(qua const& q, qua const& p) + { + return qua(q) += p; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator-(qua const& q, qua const& p) + { + return qua(q) -= p; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator*(qua const& q, qua const& p) + { + return qua(q) *= p; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(qua const& q, vec<3, T, Q> const& v) + { + vec<3, T, Q> const QuatVector(q.x, q.y, q.z); + vec<3, T, Q> const uv(glm::cross(QuatVector, v)); + vec<3, T, Q> const uuv(glm::cross(QuatVector, uv)); + + return v + ((uv * q.w) + uuv) * static_cast(2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, qua const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(qua const& q, vec<4, T, Q> const& v) + { + return detail::compute_quat_mul_vec4::value>::call(q, v); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, qua const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator*(qua const& q, T const& s) + { + return qua::wxyz( + q.w * s, q.x * s, q.y * s, q.z * s); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator*(T const& s, qua const& q) + { + return q * s; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua operator/(qua const& q, T const& s) + { + return qua::wxyz( + q.w / s, q.x / s, q.y / s, q.z / s); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(qua const& q1, qua const& q2) + { + return q1.x == q2.x && q1.y == q2.y && q1.z == q2.z && q1.w == q2.w; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(qua const& q1, qua const& q2) + { + return q1.x != q2.x || q1.y != q2.y || q1.z != q2.z || q1.w != q2.w; + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_quat_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/detail/type_quat_simd.inl b/thirdparty/glm/glm/detail/type_quat_simd.inl new file mode 100644 index 0000000..fa6da19 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_quat_simd.inl @@ -0,0 +1,208 @@ +/// @ref core + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ +/* + template + struct compute_quat_mul + { + static qua call(qua const& q1, qua const& q2) + { + // SSE2 STATS: 11 shuffle, 8 mul, 8 add + // SSE4 STATS: 3 shuffle, 4 mul, 4 dpps + + __m128 const mul0 = _mm_mul_ps(q1.data, _mm_shuffle_ps(q2.data, q2.data, _MM_SHUFFLE(0, 1, 2, 3))); + __m128 const mul1 = _mm_mul_ps(q1.data, _mm_shuffle_ps(q2.data, q2.data, _MM_SHUFFLE(1, 0, 3, 2))); + __m128 const mul2 = _mm_mul_ps(q1.data, _mm_shuffle_ps(q2.data, q2.data, _MM_SHUFFLE(2, 3, 0, 1))); + __m128 const mul3 = _mm_mul_ps(q1.data, q2.data); + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + __m128 const add0 = _mm_dp_ps(mul0, _mm_set_ps(1.0f, -1.0f, 1.0f, 1.0f), 0xff); + __m128 const add1 = _mm_dp_ps(mul1, _mm_set_ps(1.0f, 1.0f, 1.0f, -1.0f), 0xff); + __m128 const add2 = _mm_dp_ps(mul2, _mm_set_ps(1.0f, 1.0f, -1.0f, 1.0f), 0xff); + __m128 const add3 = _mm_dp_ps(mul3, _mm_set_ps(1.0f, -1.0f, -1.0f, -1.0f), 0xff); +# else + __m128 const mul4 = _mm_mul_ps(mul0, _mm_set_ps(1.0f, -1.0f, 1.0f, 1.0f)); + __m128 const add0 = _mm_add_ps(mul0, _mm_movehl_ps(mul4, mul4)); + __m128 const add4 = _mm_add_ss(add0, _mm_shuffle_ps(add0, add0, 1)); + + __m128 const mul5 = _mm_mul_ps(mul1, _mm_set_ps(1.0f, 1.0f, 1.0f, -1.0f)); + __m128 const add1 = _mm_add_ps(mul1, _mm_movehl_ps(mul5, mul5)); + __m128 const add5 = _mm_add_ss(add1, _mm_shuffle_ps(add1, add1, 1)); + + __m128 const mul6 = _mm_mul_ps(mul2, _mm_set_ps(1.0f, 1.0f, -1.0f, 1.0f)); + __m128 const add2 = _mm_add_ps(mul6, _mm_movehl_ps(mul6, mul6)); + __m128 const add6 = _mm_add_ss(add2, _mm_shuffle_ps(add2, add2, 1)); + + __m128 const mul7 = _mm_mul_ps(mul3, _mm_set_ps(1.0f, -1.0f, -1.0f, -1.0f)); + __m128 const add3 = _mm_add_ps(mul3, _mm_movehl_ps(mul7, mul7)); + __m128 const add7 = _mm_add_ss(add3, _mm_shuffle_ps(add3, add3, 1)); + #endif + + // This SIMD code is a politically correct way of doing this, but in every test I've tried it has been slower than + // the final code below. I'll keep this here for reference - maybe somebody else can do something better... + // + //__m128 xxyy = _mm_shuffle_ps(add4, add5, _MM_SHUFFLE(0, 0, 0, 0)); + //__m128 zzww = _mm_shuffle_ps(add6, add7, _MM_SHUFFLE(0, 0, 0, 0)); + // + //return _mm_shuffle_ps(xxyy, zzww, _MM_SHUFFLE(2, 0, 2, 0)); + + qua Result; + _mm_store_ss(&Result.x, add4); + _mm_store_ss(&Result.y, add5); + _mm_store_ss(&Result.z, add6); + _mm_store_ss(&Result.w, add7); + return Result; + } + }; +*/ + + template + struct compute_quat_add + { + static qua call(qua const& q, qua const& p) + { + qua Result; + Result.data = _mm_add_ps(q.data, p.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_add + { + static qua call(qua const& a, qua const& b) + { + qua Result; + Result.data = _mm256_add_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_quat_sub + { + static qua call(qua const& q, qua const& p) + { + qua Result; + Result.data = _mm_sub_ps(q.data, p.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_sub + { + static qua call(qua const& a, qua const& b) + { + qua Result; + Result.data = _mm256_sub_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_quat_mul_scalar + { + static qua call(qua const& q, float s) + { + vec<4, float, Q> Result; + Result.data = _mm_mul_ps(q.data, _mm_set_ps1(s)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_mul_scalar + { + static qua call(qua const& q, double s) + { + qua Result; + Result.data = _mm256_mul_pd(q.data, _mm_set_ps1(s)); + return Result; + } + }; +# endif + + template + struct compute_quat_div_scalar + { + static qua call(qua const& q, float s) + { + vec<4, float, Q> Result; + Result.data = _mm_div_ps(q.data, _mm_set_ps1(s)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_quat_div_scalar + { + static qua call(qua const& q, double s) + { + qua Result; + Result.data = _mm256_div_pd(q.data, _mm_set_ps1(s)); + return Result; + } + }; +# endif + + template + struct compute_quat_mul_vec4 + { + static vec<4, float, Q> call(qua const& q, vec<4, float, Q> const& v) + { +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + __m128 const q_wwww = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 const q_swp0 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(0, 1, 3, 2)); + __m128 const q_swp1 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(0, 2, 1, 3)); + __m128 const v_swp0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 const v_swp1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 1, 0, 2)); + + __m128 uv = _mm_sub_ps(_mm_mul_ps(q_swp0, v_swp1), _mm_mul_ps(q_swp1, v_swp0)); + __m128 uv_swp0 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 uv_swp1 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 uuv = _mm_sub_ps(_mm_mul_ps(q_swp0, uv_swp1), _mm_mul_ps(q_swp1, uv_swp0)); + + __m128 const two = _mm_set1_ps(2.0f); + uv = _mm_mul_ps(uv, _mm_mul_ps(q_wwww, two)); + uuv = _mm_mul_ps(uuv, two); + + vec<4, float, Q> Result; + Result.data = _mm_add_ps(v.data, _mm_add_ps(uv, uuv)); + return Result; +# else + __m128 const q_wwww = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 const q_swp0 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 const q_swp1 = _mm_shuffle_ps(q.data, q.data, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 const v_swp0 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 const v_swp1 = _mm_shuffle_ps(v.data, v.data, _MM_SHUFFLE(3, 1, 0, 2)); + + __m128 uv = _mm_sub_ps(_mm_mul_ps(q_swp0, v_swp1), _mm_mul_ps(q_swp1, v_swp0)); + __m128 uv_swp0 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 0, 2, 1)); + __m128 uv_swp1 = _mm_shuffle_ps(uv, uv, _MM_SHUFFLE(3, 1, 0, 2)); + __m128 uuv = _mm_sub_ps(_mm_mul_ps(q_swp0, uv_swp1), _mm_mul_ps(q_swp1, uv_swp0)); + + __m128 const two = _mm_set1_ps(2.0f); + uv = _mm_mul_ps(uv, _mm_mul_ps(q_wwww, two)); + uuv = _mm_mul_ps(uuv, two); + + vec<4, float, Q> Result; + Result.data = _mm_add_ps(v.data, _mm_add_ps(uv, uuv)); + return Result; +# endif + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/detail/type_vec1.hpp b/thirdparty/glm/glm/detail/type_vec1.hpp new file mode 100644 index 0000000..0cc7b5d --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec1.hpp @@ -0,0 +1,308 @@ +/// @ref core +/// @file glm/detail/type_vec1.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<1, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<1, T, Q> type; + typedef vec<1, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x; +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + T x; + T r; + T s; + + typename detail::storage<1, T, detail::is_aligned::value>::type data; +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + _GLM_SWIZZLE1_2_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_2_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_2_MEMBERS(T, Q, s) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_3_MEMBERS(T, Q, s) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, x) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, r) + _GLM_SWIZZLE1_4_MEMBERS(T, Q, s) +# endif +*/ + }; +# else + union {T x, r, s;}; +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC1(T, Q) +# endif +*/ +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 1;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR vec() GLM_DEFAULT_CTOR; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_CTOR_DECL vec(vec<1, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL explicit vec(T scalar); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<2, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<3, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<1, U, P> const& v); + + // -- Swizzle constructors -- +/* +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec(detail::_swizzle<1, T, Q, E0, -1,-2,-3> const& that) + { + *this = that(); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +*/ + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> & operator=(vec const& v) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator/=(vec<1, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<1, T, Q> & operator>>=(vec<1, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, T, Q> operator~(vec<1, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec1.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_vec1.inl b/thirdparty/glm/glm/detail/type_vec1.inl new file mode 100644 index 0000000..18411e7 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec1.inl @@ -0,0 +1,553 @@ +/// @ref core + +#include "./compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0) +# endif + {} +# endif + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, T, Q> const& v) + : x(v.x) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, T, P> const& v) + : x(v.x) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(T scalar) + : x(scalar) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<2, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type) + { + return x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<1, T, Q>::operator[](typename vec<1, T, Q>::length_type) const + { + return x; + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator=(vec<1, T, Q> const& v) + { + this->x = v.x; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator=(vec<1, U, Q> const& v) + { + this->x = static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator/=(U scalar) + { + this->x /= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator++() + { + ++this->x; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator--() + { + --this->x; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> vec<1, T, Q>::operator++(int) + { + vec<1, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> vec<1, T, Q>::operator--(int) + { + vec<1, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator%=(U scalar) + { + this->x %= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator&=(U scalar) + { + this->x &= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator|=(U scalar) + { + this->x |= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator^=(U scalar) + { + this->x ^= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator<<=(U scalar) + { + this->x <<= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> & vec<1, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + return *this; + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + -v.x); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar + v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator+(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x + v2.x); + } + + //operator- + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar - v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator-(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x - v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar * v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator*(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x * v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar / v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator/(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x / v2.x); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar % v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator%(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x % v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar & v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator&(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x & v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar | v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator|(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x | v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + v.x ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + scalar ^ v.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator^(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + v1.x ^ v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + static_cast(v.x << scalar)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + static_cast(scalar << v.x)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator<<(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + static_cast(v1.x << v2.x)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v, T scalar) + { + return vec<1, T, Q>( + static_cast(v.x >> scalar)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(T scalar, vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + static_cast(scalar >> v.x)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator>>(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<1, T, Q>( + static_cast(v1.x >> v2.x)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, T, Q> operator~(vec<1, T, Q> const& v) + { + return vec<1, T, Q>( + ~v.x); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return detail::compute_equal::is_iec559>::call(v1.x, v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<1, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, bool, Q> operator&&(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2) + { + return vec<1, bool, Q>(v1.x && v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<1, bool, Q> operator||(vec<1, bool, Q> const& v1, vec<1, bool, Q> const& v2) + { + return vec<1, bool, Q>(v1.x || v2.x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/detail/type_vec2.hpp b/thirdparty/glm/glm/detail/type_vec2.hpp new file mode 100644 index 0000000..2ddfb43 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec2.hpp @@ -0,0 +1,402 @@ +/// @ref core +/// @file glm/detail/type_vec2.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<2, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<2, T, Q> type; + typedef vec<2, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y; +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC2_COMP(T, Q, x, y) +# endif//GLM_CONFIG_SWIZZLE +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct{ T x, y; }; + struct{ T r, g; }; + struct{ T s, t; }; + + typename detail::storage<2, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE2_2_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_2_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_2_MEMBERS(T, Q, s, t) + GLM_SWIZZLE2_3_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_3_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_3_MEMBERS(T, Q, s, t) + GLM_SWIZZLE2_4_MEMBERS(T, Q, x, y) + GLM_SWIZZLE2_4_MEMBERS(T, Q, r, g) + GLM_SWIZZLE2_4_MEMBERS(T, Q, s, t) +# endif + }; +# else + union {T x, r, s;}; + union {T y, g, t;}; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC2(T, Q) +# endif//GLM_CONFIG_SWIZZLE +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 2;} + + GLM_FUNC_DECL GLM_CONSTEXPR T& operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR vec() GLM_DEFAULT_CTOR; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_CTOR_DECL vec(vec<2, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL explicit vec(T scalar); + GLM_CTOR_DECL vec(T x, T y); + + // -- Conversion constructors -- + + template + GLM_CTOR_DECL explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A x, B y); + template + GLM_CTOR_DECL vec(vec<1, A, Q> const& x, B y); + template + GLM_CTOR_DECL vec(A x, vec<1, B, Q> const& y); + template + GLM_CTOR_DECL vec(vec<1, A, Q> const& x, vec<1, B, Q> const& y); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<3, U, P> const& v); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<2, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DISCARD_DECL vec(detail::_swizzle<2, T, Q, E0, E1,-1,-2> const& that) + { + *this = that(); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> & operator=(vec const& v) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator+=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator-=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator*=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator/=(vec<2, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator%=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator&=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator|=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator^=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator<<=(vec<2, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<2, T, Q> & operator>>=(vec<2, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, T, Q> operator~(vec<2, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec2.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_vec2.inl b/thirdparty/glm/glm/detail/type_vec2.inl new file mode 100644 index 0000000..e840899 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec2.inl @@ -0,0 +1,915 @@ +/// @ref core + +#include "./compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0) +# endif + {} +# endif + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, T, Q> const& v) + : x(v.x), y(v.y) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, T, P> const& v) + : x(v.x), y(v.y) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(T scalar) + : x(scalar), y(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(T _x, T _y) + : x(_x), y(_y) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(A _x, B _y) + : x(static_cast(_x)) + , y(static_cast(_y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, A, Q> const& _x, B _y) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(A _x, vec<1, B, Q> const& _y) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<1, A, Q> const& _x, vec<1, B, Q> const& _y) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<2, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i) + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<2, T, Q>::operator[](typename vec<2, T, Q>::length_type i) const + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator=(vec<2, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator=(vec<2, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + this->y += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator+=(vec<2, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + this->y -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator-=(vec<2, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + this->y *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator*=(vec<2, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(U scalar) + { + this->x /= static_cast(scalar); + this->y /= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator/=(vec<2, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.y); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator++() + { + ++this->x; + ++this->y; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator--() + { + --this->x; + --this->y; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> vec<2, T, Q>::operator++(int) + { + vec<2, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> vec<2, T, Q>::operator--(int) + { + vec<2, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(U scalar) + { + this->x %= static_cast(scalar); + this->y %= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= static_cast(v.x); + this->y %= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator%=(vec<2, U, Q> const& v) + { + this->x %= static_cast(v.x); + this->y %= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(U scalar) + { + this->x &= static_cast(scalar); + this->y &= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= static_cast(v.x); + this->y &= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator&=(vec<2, U, Q> const& v) + { + this->x &= static_cast(v.x); + this->y &= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(U scalar) + { + this->x |= static_cast(scalar); + this->y |= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= static_cast(v.x); + this->y |= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator|=(vec<2, U, Q> const& v) + { + this->x |= static_cast(v.x); + this->y |= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(U scalar) + { + this->x ^= static_cast(scalar); + this->y ^= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= static_cast(v.x); + this->y ^= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator^=(vec<2, U, Q> const& v) + { + this->x ^= static_cast(v.x); + this->y ^= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(U scalar) + { + this->x <<= static_cast(scalar); + this->y <<= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator<<=(vec<2, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.y); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + this->y >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> & vec<2, T, Q>::operator>>=(vec<2, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.y); + return *this; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + -v.x, + -v.y); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x + scalar, + v.y + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.y + v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar + v.x, + scalar + v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.x + v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator+(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x + v2.x, + v1.y + v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x - scalar, + v.y - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.y - v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar - v.x, + scalar - v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.x - v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator-(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x - v2.x, + v1.y - v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x * scalar, + v.y * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.y * v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar * v.x, + scalar * v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.x * v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator*(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x * v2.x, + v1.y * v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x / scalar, + v.y / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.y / v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar / v.x, + scalar / v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.x / v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator/(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x / v2.x, + v1.y / v2.y); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x % scalar, + v.y % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.y % v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar % v.x, + scalar % v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.x % v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator%(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x % v2.x, + v1.y % v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x & scalar, + v.y & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.y & v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar & v.x, + scalar & v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.x & v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator&(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x & v2.x, + v1.y & v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x | scalar, + v.y | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.y | v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar | v.x, + scalar | v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.x | v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator|(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x | v2.x, + v1.y | v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x ^ scalar, + v.y ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar ^ v.x, + scalar ^ v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.x ^ v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator^(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x << scalar, + v.y << scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.y << v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar << v.x, + scalar << v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.x << v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator<<(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x << v2.x, + v1.y << v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v, T scalar) + { + return vec<2, T, Q>( + v.x >> scalar, + v.y >> scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.y >> v2.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(T scalar, vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + scalar >> v.x, + scalar >> v.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<1, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.x >> v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator>>(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return vec<2, T, Q>( + v1.x >> v2.x, + v1.y >> v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, T, Q> operator~(vec<2, T, Q> const& v) + { + return vec<2, T, Q>( + ~v.x, + ~v.y); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<2, T, Q> const& v1, vec<2, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, bool, Q> operator&&(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2) + { + return vec<2, bool, Q>(v1.x && v2.x, v1.y && v2.y); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<2, bool, Q> operator||(vec<2, bool, Q> const& v1, vec<2, bool, Q> const& v2) + { + return vec<2, bool, Q>(v1.x || v2.x, v1.y || v2.y); + } +}//namespace glm diff --git a/thirdparty/glm/glm/detail/type_vec3.hpp b/thirdparty/glm/glm/detail/type_vec3.hpp new file mode 100644 index 0000000..4bf8395 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec3.hpp @@ -0,0 +1,436 @@ +/// @ref core +/// @file glm/detail/type_vec3.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<3, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<3, T, Q> type; + typedef vec<3, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# pragma clang diagnostic ignored "-Wpadded" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# pragma warning(disable: 4324) // structure was padded due to alignment specifier +# endif +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y, z; +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC3_COMP(T, Q, x, y, z) +# endif//GLM_CONFIG_SWIZZLE +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct{ T x, y, z; }; + struct{ T r, g, b; }; + struct{ T s, t, p; }; + + typename detail::storage<3, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE3_2_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_2_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_2_MEMBERS(T, Q, s, t, p) + GLM_SWIZZLE3_3_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_3_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_3_MEMBERS(T, Q, s, t, p) + GLM_SWIZZLE3_4_MEMBERS(T, Q, x, y, z) + GLM_SWIZZLE3_4_MEMBERS(T, Q, r, g, b) + GLM_SWIZZLE3_4_MEMBERS(T, Q, s, t, p) +# endif + }; +# else + union { T x, r, s; }; + union { T y, g, t; }; + union { T z, b, p; }; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC3(T, Q) +# endif//GLM_CONFIG_SWIZZLE +# endif//GLM_LANG + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + /// Return the count of components of the vector + typedef length_t length_type; + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 3;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR vec() GLM_DEFAULT_CTOR; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec(vec const& v) GLM_DEFAULT; + template + GLM_CTOR_DECL vec(vec<3, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL explicit vec(T scalar); + GLM_CTOR_DECL vec(T a, T b, T c); + + // -- Conversion scalar constructors -- + + template + GLM_CTOR_DECL explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(X x, Y y, Z z); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, Z _z); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, Z _z); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z); + template + GLM_CTOR_DECL vec(X _x, Y _y, vec<1, Z, Q> const& _z); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, B _z); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, vec<2, B, P> const& _yz); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<4, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<3, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec(detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& that) + { + *this = that(); + } + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& scalar) + { + *this = vec(v(), scalar); + } + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec(T const& scalar, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v) + { + *this = vec(scalar, v()); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q>& operator=(vec<3, T, Q> const& v) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator+=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator-=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator*=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator/=(vec<3, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator%=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator&=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator|=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator^=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator<<=(vec<3, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<3, T, Q> & operator>>=(vec<3, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<1, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> operator~(vec<3, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec3.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_vec3.inl b/thirdparty/glm/glm/detail/type_vec3.inl new file mode 100644 index 0000000..4e44047 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec3.inl @@ -0,0 +1,1070 @@ +/// @ref core + +#include "compute_vector_relational.hpp" + +namespace glm +{ + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0), z(0) +# endif + {} +# endif + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, T, Q> const& v) + : x(v.x), y(v.y), z(v.z) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, T, P> const& v) + : x(v.x), y(v.y), z(v.z) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(T scalar) + : x(scalar), y(scalar), z(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(T _x, T _y, T _z) + : x(_x), y(_y), z(_z) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + , z(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, Y _y, Z _z) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<2, A, P> const& _xy, B _z) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(A _x, vec<2, B, P> const& _yz) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<3, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T & vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i) + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<3, T, Q>::operator[](typename vec<3, T, Q>::length_type i) const + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>& vec<3, T, Q>::operator=(vec<3, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + this->z = v.z; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q>& vec<3, T, Q>::operator=(vec<3, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + this->z = static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(U scalar) + { + this->x += static_cast(scalar); + this->y += static_cast(scalar); + this->z += static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<1, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.x); + this->z += static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator+=(vec<3, U, Q> const& v) + { + this->x += static_cast(v.x); + this->y += static_cast(v.y); + this->z += static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(U scalar) + { + this->x -= static_cast(scalar); + this->y -= static_cast(scalar); + this->z -= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(vec<1, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.x); + this->z -= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator-=(vec<3, U, Q> const& v) + { + this->x -= static_cast(v.x); + this->y -= static_cast(v.y); + this->z -= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(U scalar) + { + this->x *= static_cast(scalar); + this->y *= static_cast(scalar); + this->z *= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(vec<1, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.x); + this->z *= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator*=(vec<3, U, Q> const& v) + { + this->x *= static_cast(v.x); + this->y *= static_cast(v.y); + this->z *= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(U v) + { + this->x /= static_cast(v); + this->y /= static_cast(v); + this->z /= static_cast(v); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(vec<1, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.x); + this->z /= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator/=(vec<3, U, Q> const& v) + { + this->x /= static_cast(v.x); + this->y /= static_cast(v.y); + this->z /= static_cast(v.z); + return *this; + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator++() + { + ++this->x; + ++this->y; + ++this->z; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator--() + { + --this->x; + --this->y; + --this->z; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> vec<3, T, Q>::operator++(int) + { + vec<3, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> vec<3, T, Q>::operator--(int) + { + vec<3, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(U scalar) + { + this->x %= scalar; + this->y %= scalar; + this->z %= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(vec<1, U, Q> const& v) + { + this->x %= v.x; + this->y %= v.x; + this->z %= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator%=(vec<3, U, Q> const& v) + { + this->x %= v.x; + this->y %= v.y; + this->z %= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(U scalar) + { + this->x &= scalar; + this->y &= scalar; + this->z &= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(vec<1, U, Q> const& v) + { + this->x &= v.x; + this->y &= v.x; + this->z &= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator&=(vec<3, U, Q> const& v) + { + this->x &= v.x; + this->y &= v.y; + this->z &= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(U scalar) + { + this->x |= scalar; + this->y |= scalar; + this->z |= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(vec<1, U, Q> const& v) + { + this->x |= v.x; + this->y |= v.x; + this->z |= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator|=(vec<3, U, Q> const& v) + { + this->x |= v.x; + this->y |= v.y; + this->z |= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(U scalar) + { + this->x ^= scalar; + this->y ^= scalar; + this->z ^= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(vec<1, U, Q> const& v) + { + this->x ^= v.x; + this->y ^= v.x; + this->z ^= v.x; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator^=(vec<3, U, Q> const& v) + { + this->x ^= v.x; + this->y ^= v.y; + this->z ^= v.z; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(U scalar) + { + this->x <<= scalar; + this->y <<= scalar; + this->z <<= scalar; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.x); + this->z <<= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator<<=(vec<3, U, Q> const& v) + { + this->x <<= static_cast(v.x); + this->y <<= static_cast(v.y); + this->z <<= static_cast(v.z); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(U scalar) + { + this->x >>= static_cast(scalar); + this->y >>= static_cast(scalar); + this->z >>= static_cast(scalar); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.x); + this->z >>= static_cast(v.x); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> & vec<3, T, Q>::operator>>=(vec<3, U, Q> const& v) + { + this->x >>= static_cast(v.x); + this->y >>= static_cast(v.y); + this->z >>= static_cast(v.z); + return *this; + } + + // -- Unary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + -v.x, + -v.y, + -v.z); + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x + scalar, + v.y + scalar, + v.z + scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x + scalar.x, + v.y + scalar.x, + v.z + scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar + v.x, + scalar + v.y, + scalar + v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x + v.x, + scalar.x + v.y, + scalar.x + v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator+(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x + v2.x, + v1.y + v2.y, + v1.z + v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x - scalar, + v.y - scalar, + v.z - scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x - scalar.x, + v.y - scalar.x, + v.z - scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar - v.x, + scalar - v.y, + scalar - v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x - v.x, + scalar.x - v.y, + scalar.x - v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator-(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x - v2.x, + v1.y - v2.y, + v1.z - v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x * scalar, + v.y * scalar, + v.z * scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x * scalar.x, + v.y * scalar.x, + v.z * scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar * v.x, + scalar * v.y, + scalar * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x * v.x, + scalar.x * v.y, + scalar.x * v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator*(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x * v2.x, + v1.y * v2.y, + v1.z * v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x / scalar, + v.y / scalar, + v.z / scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x / scalar.x, + v.y / scalar.x, + v.z / scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar / v.x, + scalar / v.y, + scalar / v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x / v.x, + scalar.x / v.y, + scalar.x / v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator/(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x / v2.x, + v1.y / v2.y, + v1.z / v2.z); + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x % scalar, + v.y % scalar, + v.z % scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x % scalar.x, + v.y % scalar.x, + v.z % scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar % v.x, + scalar % v.y, + scalar % v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x % v.x, + scalar.x % v.y, + scalar.x % v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator%(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x % v2.x, + v1.y % v2.y, + v1.z % v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x & scalar, + v.y & scalar, + v.z & scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x & scalar.x, + v.y & scalar.x, + v.z & scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar & v.x, + scalar & v.y, + scalar & v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x & v.x, + scalar.x & v.y, + scalar.x & v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator&(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x & v2.x, + v1.y & v2.y, + v1.z & v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x | scalar, + v.y | scalar, + v.z | scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x | scalar.x, + v.y | scalar.x, + v.z | scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar | v.x, + scalar | v.y, + scalar | v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x | v.x, + scalar.x | v.y, + scalar.x | v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator|(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x | v2.x, + v1.y | v2.y, + v1.z | v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x ^ scalar, + v.y ^ scalar, + v.z ^ scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x ^ scalar.x, + v.y ^ scalar.x, + v.z ^ scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar ^ v.x, + scalar ^ v.y, + scalar ^ v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x ^ v.x, + scalar.x ^ v.y, + scalar.x ^ v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator^(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x ^ v2.x, + v1.y ^ v2.y, + v1.z ^ v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x << scalar, + v.y << scalar, + v.z << scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x << scalar.x, + v.y << scalar.x, + v.z << scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar << v.x, + scalar << v.y, + scalar << v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x << v.x, + scalar.x << v.y, + scalar.x << v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator<<(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x << v2.x, + v1.y << v2.y, + v1.z << v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, T scalar) + { + return vec<3, T, Q>( + v.x >> scalar, + v.y >> scalar, + v.z >> scalar); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<3, T, Q>( + v.x >> scalar.x, + v.y >> scalar.x, + v.z >> scalar.x); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(T scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar >> v.x, + scalar >> v.y, + scalar >> v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + scalar.x >> v.x, + scalar.x >> v.y, + scalar.x >> v.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator>>(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return vec<3, T, Q>( + v1.x >> v2.x, + v1.y >> v2.y, + v1.z >> v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> operator~(vec<3, T, Q> const& v) + { + return vec<3, T, Q>( + ~v.x, + ~v.y, + ~v.z); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y) && + detail::compute_equal::is_iec559>::call(v1.z, v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<3, T, Q> const& v1, vec<3, T, Q> const& v2) + { + return !(v1 == v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, bool, Q> operator&&(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2) + { + return vec<3, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, bool, Q> operator||(vec<3, bool, Q> const& v1, vec<3, bool, Q> const& v2) + { + return vec<3, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z); + } +}//namespace glm diff --git a/thirdparty/glm/glm/detail/type_vec4.hpp b/thirdparty/glm/glm/detail/type_vec4.hpp new file mode 100644 index 0000000..15f122f --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec4.hpp @@ -0,0 +1,508 @@ +/// @ref core +/// @file glm/detail/type_vec4.hpp + +#pragma once + +#include "qualifier.hpp" +#if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR +# include "_swizzle.hpp" +#elif GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION +# include "_swizzle_func.hpp" +#endif +#include + +namespace glm +{ + template + struct vec<4, T, Q> + { + // -- Implementation detail -- + + typedef T value_type; + typedef vec<4, T, Q> type; + typedef vec<4, bool, Q> bool_type; + + // -- Data -- + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-anonymous-struct" +# pragma clang diagnostic ignored "-Wnested-anon-types" +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union +# endif +# endif + +# if GLM_CONFIG_XYZW_ONLY + T x, y, z, w; +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC4_COMP(T, Q, x, y, z, w) +# endif//GLM_CONFIG_SWIZZLE +# elif GLM_CONFIG_ANONYMOUS_STRUCT == GLM_ENABLE + union + { + struct { T x, y, z, w; }; + struct { T r, g, b, a; }; + struct { T s, t, p, q; }; + + typename detail::storage<4, T, detail::is_aligned::value>::type data; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + GLM_SWIZZLE4_2_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_2_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_2_MEMBERS(T, Q, s, t, p, q) + GLM_SWIZZLE4_3_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_3_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_3_MEMBERS(T, Q, s, t, p, q) + GLM_SWIZZLE4_4_MEMBERS(T, Q, x, y, z, w) + GLM_SWIZZLE4_4_MEMBERS(T, Q, r, g, b, a) + GLM_SWIZZLE4_4_MEMBERS(T, Q, s, t, p, q) +# endif + }; +# else + union { T x, r, s; }; + union { T y, g, t; }; + union { T z, b, p; }; + union { T w, a, q; }; + +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_FUNCTION + GLM_SWIZZLE_GEN_VEC_FROM_VEC4(T, Q) +# endif +# endif + +# if GLM_SILENT_WARNINGS == GLM_ENABLE +# if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_GCC +# pragma GCC diagnostic pop +# elif GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif +# endif + + // -- Component accesses -- + + typedef length_t length_type; + + /// Return the count of components of the vector + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 4;} + + GLM_FUNC_DECL GLM_CONSTEXPR T & operator[](length_type i); + GLM_FUNC_DECL GLM_CONSTEXPR T const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_DEFAULT_CTOR_DECL GLM_CONSTEXPR vec() GLM_DEFAULT_CTOR; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec(vec<4, T, Q> const& v) GLM_DEFAULT; + template + GLM_CTOR_DECL vec(vec<4, T, P> const& v); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL explicit vec(T scalar); + GLM_CTOR_DECL vec(T x, T y, T z, T w); + + // -- Conversion scalar constructors -- + + template + GLM_CTOR_DECL explicit vec(vec<1, U, P> const& v); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(X _x, Y _y, Z _z, W _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w); + template + GLM_CTOR_DECL vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + template + GLM_CTOR_DECL vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w); + + // -- Conversion vector constructors -- + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, B _z, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, vec<2, B, P> const& _yz, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, B _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<3, A, P> const& _xyz, B _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(A _x, vec<3, B, P> const& _yzw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw); + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw); + + /// Explicit conversions (From section 5.4.1 Conversion and scalar constructors of GLSL 1.30.08 specification) + template + GLM_CTOR_DECL GLM_EXPLICIT vec(vec<4, U, P> const& v); + + // -- Swizzle constructors -- +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + GLM_FUNC_DISCARD_DECL vec(detail::_swizzle<4, T, Q, E0, E1, E2, E3> const& that) + { + *this = that(); + } + + template + GLM_FUNC_DISCARD_DECL vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, detail::_swizzle<2, T, Q, F0, F1, -1, -2> const& u) + { + *this = vec<4, T, Q>(v(), u()); + } + + template + GLM_FUNC_DISCARD_DECL vec(T const& x, T const& y, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v) + { + *this = vec<4, T, Q>(x, y, v()); + } + + template + GLM_FUNC_DISCARD_DECL vec(T const& x, detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& w) + { + *this = vec<4, T, Q>(x, v(), w); + } + + template + GLM_FUNC_DISCARD_DECL vec(detail::_swizzle<2, T, Q, E0, E1, -1, -2> const& v, T const& z, T const& w) + { + *this = vec<4, T, Q>(v(), z, w); + } + + template + GLM_FUNC_DISCARD_DECL vec(detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& v, T const& w) + { + *this = vec<4, T, Q>(v(), w); + } + + template + GLM_FUNC_DISCARD_DECL vec(T const& x, detail::_swizzle<3, T, Q, E0, E1, E2, -1> const& v) + { + *this = vec<4, T, Q>(x, v()); + } +# endif//GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q>& operator=(vec<4, T, Q> const& v) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator=(vec<4, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator+=(vec<4, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator-=(vec<4, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator*=(vec<4, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(U scalar); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(vec<1, U, Q> const& v); + template + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q>& operator/=(vec<4, U, Q> const& v); + + // -- Increment and decrement operators -- + + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q> & operator++(); + GLM_FUNC_DISCARD_DECL GLM_CONSTEXPR vec<4, T, Q> & operator--(); + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator++(int); + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator--(int); + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator%=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator&=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator|=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator^=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator<<=(vec<4, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(U scalar); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(vec<1, U, Q> const& v); + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> & operator>>=(vec<4, U, Q> const& v); + }; + + // -- Unary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v); + + // -- Binary operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, T, Q> operator~(vec<4, T, Q> const& v); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2); + + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2); +}//namespace glm + +#ifndef GLM_EXTERNAL_TEMPLATE +#include "type_vec4.inl" +#endif//GLM_EXTERNAL_TEMPLATE diff --git a/thirdparty/glm/glm/detail/type_vec4.inl b/thirdparty/glm/glm/detail/type_vec4.inl new file mode 100644 index 0000000..d48fae7 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec4.inl @@ -0,0 +1,1142 @@ +/// @ref core + +#include "compute_vector_relational.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_vec4_add + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); + } + }; + + template + struct compute_vec4_sub + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); + } + }; + + template + struct compute_vec4_mul + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); + } + }; + + template + struct compute_vec4_div + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); + } + }; + + template + struct compute_vec4_mod + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x % b.x, a.y % b.y, a.z % b.z, a.w % b.w); + } + }; + + template + struct compute_vec4_and + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x & b.x, a.y & b.y, a.z & b.z, a.w & b.w); + } + }; + + template + struct compute_vec4_or + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x | b.x, a.y | b.y, a.z | b.z, a.w | b.w); + } + }; + + template + struct compute_vec4_xor + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x ^ b.x, a.y ^ b.y, a.z ^ b.z, a.w ^ b.w); + } + }; + + template + struct compute_vec4_shift_left + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x << b.x, a.y << b.y, a.z << b.z, a.w << b.w); + } + }; + + template + struct compute_vec4_shift_right + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + return vec<4, T, Q>(a.x >> b.x, a.y >> b.y, a.z >> b.z, a.w >> b.w); + } + }; + + template + struct compute_vec4_equal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return + detail::compute_equal::is_iec559>::call(v1.x, v2.x) && + detail::compute_equal::is_iec559>::call(v1.y, v2.y) && + detail::compute_equal::is_iec559>::call(v1.z, v2.z) && + detail::compute_equal::is_iec559>::call(v1.w, v2.w); + } + }; + + template + struct compute_vec4_nequal + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static bool call(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return !compute_vec4_equal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + }; + + template + struct compute_vec4_bitwise_not + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + return vec<4, T, Q>(~v.x, ~v.y, ~v.z, ~v.w); + } + }; +}//namespace detail + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_DEFAULT_CTOR == GLM_DISABLE + template + GLM_DEFAULTED_DEFAULT_CTOR_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec() +# if GLM_CONFIG_CTOR_INIT != GLM_CTOR_INIT_DISABLE + : x(0), y(0), z(0), w(0) +# endif + {} +# endif + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, T, Q> const& v) + : x(v.x), y(v.y), z(v.z), w(v.w) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, T, P> const& v) + : x(v.x), y(v.y), z(v.z), w(v.w) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(T scalar) + : x(scalar), y(scalar), z(scalar), w(scalar) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(T _x, T _y, T _z, T _w) + : x(_x), y(_y), z(_z), w(_w) + {} + + // -- Conversion scalar constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.x)) + , z(static_cast(v.x)) + , w(static_cast(v.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, Z _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, W _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, Z _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, Y _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(X _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, X, Q> const& _x, vec<1, Y, Q> const& _y, vec<1, Z, Q> const& _z, vec<1, W, Q> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + // -- Conversion vector constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, C _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, C _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, B _z, vec<1, C, P> const& _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<1, B, P> const& _z, vec<1, C, P> const& _w) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_z.x)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, C _w) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, C _w) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w) + : x(static_cast(_x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<2, B, P> const& _yz, vec<1, C, P> const& _w) + : x(static_cast(_x.x)) + , y(static_cast(_yz.x)) + , z(static_cast(_yz.y)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, B _y, vec<2, C, P> const& _zw) + : x(static_cast(_x)) + , y(static_cast(_y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, B _y, vec<2, C, P> const& _zw) + : x(static_cast(_x.x)) + , y(static_cast(_y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw) + : x(static_cast(_x)) + , y(static_cast(_y.x)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<1, B, P> const& _y, vec<2, C, P> const& _zw) + : x(static_cast(_x.x)) + , y(static_cast(_y.x)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, B _w) + : x(static_cast(_xyz.x)) + , y(static_cast(_xyz.y)) + , z(static_cast(_xyz.z)) + , w(static_cast(_w)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<3, A, P> const& _xyz, vec<1, B, P> const& _w) + : x(static_cast(_xyz.x)) + , y(static_cast(_xyz.y)) + , z(static_cast(_xyz.z)) + , w(static_cast(_w.x)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(A _x, vec<3, B, P> const& _yzw) + : x(static_cast(_x)) + , y(static_cast(_yzw.x)) + , z(static_cast(_yzw.y)) + , w(static_cast(_yzw.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<1, A, P> const& _x, vec<3, B, P> const& _yzw) + : x(static_cast(_x.x)) + , y(static_cast(_yzw.x)) + , z(static_cast(_yzw.y)) + , w(static_cast(_yzw.z)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<2, A, P> const& _xy, vec<2, B, P> const& _zw) + : x(static_cast(_xy.x)) + , y(static_cast(_xy.y)) + , z(static_cast(_zw.x)) + , w(static_cast(_zw.y)) + {} + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>::vec(vec<4, U, P> const& v) + : x(static_cast(v.x)) + , y(static_cast(v.y)) + , z(static_cast(v.z)) + , w(static_cast(v.w)) + {} + + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i) + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T const& vec<4, T, Q>::operator[](typename vec<4, T, Q>::length_type i) const + { + GLM_ASSERT_LENGTH(i, this->length()); + switch(i) + { + default: + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + } + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>& vec<4, T, Q>::operator=(vec<4, T, Q> const& v) + { + this->x = v.x; + this->y = v.y; + this->z = v.z; + this->w = v.w; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q>& vec<4, T, Q>::operator=(vec<4, U, Q> const& v) + { + this->x = static_cast(v.x); + this->y = static_cast(v.y); + this->z = static_cast(v.z); + this->w = static_cast(v.w); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(U scalar) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator+=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_add::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(U scalar) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator-=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_sub::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(U scalar) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator*=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_mul::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(U scalar) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(v.x))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator/=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_div::value>::call(*this, vec<4, T, Q>(v))); + } + + // -- Increment and decrement operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator++() + { + ++this->x; + ++this->y; + ++this->z; + ++this->w; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator--() + { + --this->x; + --this->y; + --this->z; + --this->w; + return *this; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> vec<4, T, Q>::operator++(int) + { + vec<4, T, Q> Result(*this); + ++*this; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> vec<4, T, Q>::operator--(int) + { + vec<4, T, Q> Result(*this); + --*this; + return Result; + } + + // -- Unary bit operators -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(U scalar) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator%=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_mod::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(U scalar) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator&=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_and::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(U scalar) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator|=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_or::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(U scalar) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator^=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_xor::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(U scalar) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator<<=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_left::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(U scalar) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(scalar))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(vec<1, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> & vec<4, T, Q>::operator>>=(vec<4, U, Q> const& v) + { + return (*this = detail::compute_vec4_shift_right::value, sizeof(T) * 8, detail::is_aligned::value>::call(*this, vec<4, T, Q>(v))); + } + + // -- Unary constant operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v) + { + return vec<4, T, Q>(0) -= v; + } + + // -- Binary arithmetic operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) += scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) += v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(v) += scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v2) += v1; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator+(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) += v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) -= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) -= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator-(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) -= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) *= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) *= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(v) *= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v2) *= v1; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator*(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) *= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) /= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) /= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) /= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) /= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator/(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) /= v2; + } + + // -- Binary bit operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) %= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) %= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) %= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<1, T, Q> const& scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar.x) %= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator%(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) %= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) &= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v, vec<1, T, Q> const& scalar) + { + return vec<4, T, Q>(v) &= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) &= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) &= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator&(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) &= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) |= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) |= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) |= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) |= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator|(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) |= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) ^= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) ^= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) ^= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) ^= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator^(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) ^= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) <<= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) <<= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) <<= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) <<= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator<<(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) <<= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v, T scalar) + { + return vec<4, T, Q>(v) >>= scalar; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<1, T, Q> const& v2) + { + return vec<4, T, Q>(v1) >>= v2.x; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(T scalar, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(scalar) >>= v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<1, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1.x) >>= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator>>(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return vec<4, T, Q>(v1) >>= v2; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, T, Q> operator~(vec<4, T, Q> const& v) + { + return detail::compute_vec4_bitwise_not::value, sizeof(T) * 8, detail::is_aligned::value>::call(v); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator==(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return detail::compute_vec4_equal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool operator!=(vec<4, T, Q> const& v1, vec<4, T, Q> const& v2) + { + return detail::compute_vec4_nequal::value, sizeof(T) * 8, detail::is_aligned::value>::call(v1, v2); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> operator&&(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2) + { + return vec<4, bool, Q>(v1.x && v2.x, v1.y && v2.y, v1.z && v2.z, v1.w && v2.w); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> operator||(vec<4, bool, Q> const& v1, vec<4, bool, Q> const& v2) + { + return vec<4, bool, Q>(v1.x || v2.x, v1.y || v2.y, v1.z || v2.z, v1.w || v2.w); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "type_vec4_simd.inl" +#endif diff --git a/thirdparty/glm/glm/detail/type_vec4_simd.inl b/thirdparty/glm/glm/detail/type_vec4_simd.inl new file mode 100644 index 0000000..816ef45 --- /dev/null +++ b/thirdparty/glm/glm/detail/type_vec4_simd.inl @@ -0,0 +1,788 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm { + namespace detail + { +# if GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + template + struct _swizzle_base1<4, float, Q, E0, E1, E2, E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, float, Q> operator ()() const + { + __m128 data = *reinterpret_cast<__m128 const*>(&this->_buffer); + + vec<4, float, Q> Result; +# if GLM_ARCH & GLM_ARCH_AVX_BIT + Result.data = _mm_permute_ps(data, _MM_SHUFFLE(E3, E2, E1, E0)); +# else + Result.data = _mm_shuffle_ps(data, data, _MM_SHUFFLE(E3, E2, E1, E0)); +# endif + return Result; + } + }; + + template + struct _swizzle_base1<4, int, Q, E0, E1, E2, E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, int, Q> operator ()() const + { + __m128i data = *reinterpret_cast<__m128i const*>(&this->_buffer); + + vec<4, int, Q> Result; + Result.data = _mm_shuffle_epi32(data, _MM_SHUFFLE(E3, E2, E1, E0)); + return Result; + } + }; + + template + struct _swizzle_base1<4, uint, Q, E0, E1, E2, E3, true> : public _swizzle_base0 + { + GLM_FUNC_QUALIFIER vec<4, uint, Q> operator ()() const + { + __m128i data = *reinterpret_cast<__m128i const*>(&this->_buffer); + + vec<4, uint, Q> Result; + Result.data = _mm_shuffle_epi32(data, _MM_SHUFFLE(E3, E2, E1, E0)); + return Result; + } + }; +# endif// GLM_CONFIG_SWIZZLE == GLM_SWIZZLE_OPERATOR + + template + struct compute_vec4_add + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_add_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_add + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_add_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_sub + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_sub_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_sub + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_sub_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_mul + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_mul_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_mul + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_mul_pd(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_div + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = _mm_div_ps(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template + struct compute_vec4_div + { + static vec<4, double, Q> call(vec<4, double, Q> const& a, vec<4, double, Q> const& b) + { + vec<4, double, Q> Result; + Result.data = _mm256_div_pd(a.data, b.data); + return Result; + } + }; +# endif + + template<> + struct compute_vec4_div + { + static vec<4, float, aligned_lowp> call(vec<4, float, aligned_lowp> const& a, vec<4, float, aligned_lowp> const& b) + { + vec<4, float, aligned_lowp> Result; + Result.data = _mm_mul_ps(a.data, _mm_rcp_ps(b.data)); + return Result; + } + }; + + template + struct compute_vec4_and + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_and_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_and + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_and_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_or + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_or_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_or + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_or_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_xor + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_xor_si128(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_xor + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_xor_si256(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_shift_left + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_sll_epi32(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_shift_left + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_sll_epi64(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_shift_right + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm_srl_epi32(a.data, b.data); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_shift_right + { + static vec<4, T, Q> call(vec<4, T, Q> const& a, vec<4, T, Q> const& b) + { + vec<4, T, Q> Result; + Result.data = _mm256_srl_epi64(a.data, b.data); + return Result; + } + }; +# endif + + template + struct compute_vec4_bitwise_not + { + static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + vec<4, T, Q> Result; + Result.data = _mm_xor_si128(v.data, _mm_set1_epi32(-1)); + return Result; + } + }; + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template + struct compute_vec4_bitwise_not + { + static vec<4, T, Q> call(vec<4, T, Q> const& v) + { + vec<4, T, Q> Result; + Result.data = _mm256_xor_si256(v.data, _mm_set1_epi32(-1)); + return Result; + } + }; +# endif + + template + struct compute_vec4_equal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + return _mm_movemask_ps(_mm_cmpneq_ps(v1.data, v2.data)) == 0; + } + }; + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + template + struct compute_vec4_equal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + //return _mm_movemask_epi8(_mm_cmpeq_epi32(v1.data, v2.data)) != 0; + __m128i neq = _mm_xor_si128(v1.data, v2.data); + return _mm_test_all_zeros(neq, neq) == 0; + } + }; +# endif + + template + struct compute_vec4_nequal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + return _mm_movemask_ps(_mm_cmpneq_ps(v1.data, v2.data)) != 0; + } + }; + +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + template + struct compute_vec4_nequal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + //return _mm_movemask_epi8(_mm_cmpneq_epi32(v1.data, v2.data)) != 0; + __m128i neq = _mm_xor_si128(v1.data, v2.data); + return _mm_test_all_zeros(neq, neq) != 0; + } + }; +# endif + }//namespace detail + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(float _s) : + data(_mm_set1_ps(_s)) + {} + +# if GLM_ARCH & GLM_ARCH_AVX_BIT + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_lowp>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_mediump>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, double, aligned_highp>::vec(double _s) : + data(_mm256_set1_pd(_s)) + {} +# endif + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_lowp>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_mediump>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_highp>::vec(int _s) : + data(_mm_set1_epi32(_s)) + {} + +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_lowp>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_mediump>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, detail::int64, aligned_highp>::vec(detail::int64 _s) : + data(_mm256_set1_epi64x(_s)) + {} +# endif + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(float _x, float _y, float _z, float _w) : + data(_mm_set_ps(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_lowp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_mediump>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_highp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_set_epi32(_w, _z, _y, _x)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(int _x, int _y, int _z, int _w) : + data(_mm_cvtepi32_ps(_mm_set_epi32(_w, _z, _y, _x))) + {} +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + +#if GLM_ARCH & GLM_ARCH_NEON_BIT +namespace glm { + namespace detail { + + template + struct compute_vec4_add + { + static + vec<4, float, Q> + call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = vaddq_f32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_add + { + static + vec<4, uint, Q> + call(vec<4, uint, Q> const& a, vec<4, uint, Q> const& b) + { + vec<4, uint, Q> Result; + Result.data = vaddq_u32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_add + { + static + vec<4, int, Q> + call(vec<4, int, Q> const& a, vec<4, int, Q> const& b) + { + vec<4, int, Q> Result; + Result.data = vaddq_s32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_sub + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = vsubq_f32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_sub + { + static vec<4, uint, Q> call(vec<4, uint, Q> const& a, vec<4, uint, Q> const& b) + { + vec<4, uint, Q> Result; + Result.data = vsubq_u32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_sub + { + static vec<4, int, Q> call(vec<4, int, Q> const& a, vec<4, int, Q> const& b) + { + vec<4, int, Q> Result; + Result.data = vsubq_s32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_mul + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; + Result.data = vmulq_f32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_mul + { + static vec<4, uint, Q> call(vec<4, uint, Q> const& a, vec<4, uint, Q> const& b) + { + vec<4, uint, Q> Result; + Result.data = vmulq_u32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_mul + { + static vec<4, int, Q> call(vec<4, int, Q> const& a, vec<4, int, Q> const& b) + { + vec<4, int, Q> Result; + Result.data = vmulq_s32(a.data, b.data); + return Result; + } + }; + + template + struct compute_vec4_div + { + static vec<4, float, Q> call(vec<4, float, Q> const& a, vec<4, float, Q> const& b) + { + vec<4, float, Q> Result; +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + Result.data = vdivq_f32(a.data, b.data); +#else + /* Arm assembler reference: + * + * The Newton-Raphson iteration: x[n+1] = x[n] * (2 - d * x[n]) + * converges to (1/d) if x0 is the result of VRECPE applied to d. + * + * Note: The precision usually improves with two interactions, but more than two iterations are not helpful. */ + float32x4_t x = vrecpeq_f32(b.data); + x = vmulq_f32(vrecpsq_f32(b.data, x), x); + x = vmulq_f32(vrecpsq_f32(b.data, x), x); + Result.data = vmulq_f32(a.data, x); +#endif + return Result; + } + }; + + template + struct compute_vec4_equal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + uint32x4_t cmp = vceqq_f32(v1.data, v2.data); +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + cmp = vpminq_u32(cmp, cmp); + cmp = vpminq_u32(cmp, cmp); + uint32_t r = cmp[0]; +#else + uint32x2_t cmpx2 = vpmin_u32(vget_low_u32(cmp), vget_high_u32(cmp)); + cmpx2 = vpmin_u32(cmpx2, cmpx2); + uint32_t r = cmpx2[0]; +#endif + return r == ~0u; + } + }; + + template + struct compute_vec4_equal + { + static bool call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + uint32x4_t cmp = vceqq_u32(v1.data, v2.data); +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + cmp = vpminq_u32(cmp, cmp); + cmp = vpminq_u32(cmp, cmp); + uint32_t r = cmp[0]; +#else + uint32x2_t cmpx2 = vpmin_u32(vget_low_u32(cmp), vget_high_u32(cmp)); + cmpx2 = vpmin_u32(cmpx2, cmpx2); + uint32_t r = cmpx2[0]; +#endif + return r == ~0u; + } + }; + + template + struct compute_vec4_equal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + uint32x4_t cmp = vceqq_s32(v1.data, v2.data); +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + cmp = vpminq_u32(cmp, cmp); + cmp = vpminq_u32(cmp, cmp); + uint32_t r = cmp[0]; +#else + uint32x2_t cmpx2 = vpmin_u32(vget_low_u32(cmp), vget_high_u32(cmp)); + cmpx2 = vpmin_u32(cmpx2, cmpx2); + uint32_t r = cmpx2[0]; +#endif + return r == ~0u; + } + }; + + template + struct compute_vec4_nequal + { + static bool call(vec<4, float, Q> const& v1, vec<4, float, Q> const& v2) + { + return !compute_vec4_equal::call(v1, v2); + } + }; + + template + struct compute_vec4_nequal + { + static bool call(vec<4, uint, Q> const& v1, vec<4, uint, Q> const& v2) + { + return !compute_vec4_equal::call(v1, v2); + } + }; + + template + struct compute_vec4_nequal + { + static bool call(vec<4, int, Q> const& v1, vec<4, int, Q> const& v2) + { + return !compute_vec4_equal::call(v1, v2); + } + }; + + }//namespace detail + +#if !GLM_CONFIG_XYZW_ONLY + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(float _s) : + data(vdupq_n_f32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(float _s) : + data(vdupq_n_f32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(float _s) : + data(vdupq_n_f32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_lowp>::vec(int _s) : + data(vdupq_n_s32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_mediump>::vec(int _s) : + data(vdupq_n_s32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, int, aligned_highp>::vec(int _s) : + data(vdupq_n_s32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, uint, aligned_lowp>::vec(uint _s) : + data(vdupq_n_u32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, uint, aligned_mediump>::vec(uint _s) : + data(vdupq_n_u32(_s)) + {} + + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, uint, aligned_highp>::vec(uint _s) : + data(vdupq_n_u32(_s)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(const vec<4, float, aligned_highp>& rhs) : + data(rhs.data) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(const vec<4, int, aligned_highp>& rhs) : + data(vcvtq_f32_s32(rhs.data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(const vec<4, uint, aligned_highp>& rhs) : + data(vcvtq_f32_u32(rhs.data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(int _x, int _y, int _z, int _w) : + data(vcvtq_f32_s32(vec<4, int, aligned_lowp>(_x, _y, _z, _w).data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(int _x, int _y, int _z, int _w) : + data(vcvtq_f32_s32(vec<4, int, aligned_mediump>(_x, _y, _z, _w).data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(int _x, int _y, int _z, int _w) : + data(vcvtq_f32_s32(vec<4, int, aligned_highp>(_x, _y, _z, _w).data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_lowp>::vec(uint _x, uint _y, uint _z, uint _w) : + data(vcvtq_f32_u32(vec<4, uint, aligned_lowp>(_x, _y, _z, _w).data)) + {} + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_mediump>::vec(uint _x, uint _y, uint _z, uint _w) : + data(vcvtq_f32_u32(vec<4, uint, aligned_mediump>(_x, _y, _z, _w).data)) + {} + + + template<> + template<> + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, float, aligned_highp>::vec(uint _x, uint _y, uint _z, uint _w) : + data(vcvtq_f32_u32(vec<4, uint, aligned_highp>(_x, _y, _z, _w).data)) + {} + +#endif +}//namespace glm + +#endif diff --git a/thirdparty/glm/glm/exponential.hpp b/thirdparty/glm/glm/exponential.hpp new file mode 100644 index 0000000..1614f76 --- /dev/null +++ b/thirdparty/glm/glm/exponential.hpp @@ -0,0 +1,110 @@ +/// @ref core +/// @file glm/exponential.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions +/// +/// @defgroup core_func_exponential Exponential functions +/// @ingroup core +/// +/// Provides GLSL exponential functions +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/type_vec1.hpp" +#include "detail/type_vec2.hpp" +#include "detail/type_vec3.hpp" +#include "detail/type_vec4.hpp" +#include + +namespace glm +{ + /// @addtogroup core_func_exponential + /// @{ + + /// Returns 'base' raised to the power 'exponent'. + /// + /// @param base Floating point value. pow function is defined for input values of 'base' defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @param exponent Floating point value representing the 'exponent'. + /// + /// @see GLSL pow man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec pow(vec const& base, vec const& exponent); + + /// Returns the natural exponentiation of v, i.e., e^v. + /// + /// @param v exp function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL exp man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec exp(vec const& v); + + /// Returns the natural logarithm of v, i.e., + /// returns the value y which satisfies the equation x = e^y. + /// Results are undefined if v <= 0. + /// + /// @param v log function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL log man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec log(vec const& v); + + /// Returns 2 raised to the v power. + /// + /// @param v exp2 function is defined for input values of v defined in the range (inf-, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL exp2 man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec exp2(vec const& v); + + /// Returns the base 2 log of x, i.e., returns the value y, + /// which satisfies the equation x = 2 ^ y. + /// + /// @param v log2 function is defined for input values of v defined in the range (0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL log2 man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec log2(vec const& v); + + /// Returns the positive square root of v. + /// + /// @param v sqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL sqrt man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec sqrt(vec const& v); + + /// Returns the reciprocal of the positive square root of v. + /// + /// @param v inversesqrt function is defined for input values of v defined in the range [0, inf+) in the limit of the type qualifier. + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL inversesqrt man page + /// @see GLSL 4.20.8 specification, section 8.2 Exponential Functions + template + GLM_FUNC_DECL vec inversesqrt(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_exponential.inl" diff --git a/thirdparty/glm/glm/ext.hpp b/thirdparty/glm/glm/ext.hpp new file mode 100644 index 0000000..f9ac369 --- /dev/null +++ b/thirdparty/glm/glm/ext.hpp @@ -0,0 +1,267 @@ +/// @file glm/ext.hpp +/// +/// @ref core (Dependence) + +#include "detail/setup.hpp" + +#pragma once + +#include "glm.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_MESSAGE_EXT_INCLUDED_DISPLAYED) +# define GLM_MESSAGE_EXT_INCLUDED_DISPLAYED +# pragma message("GLM: All extensions included (not recommended)") +#endif//GLM_MESSAGES + +#include "./ext/matrix_clip_space.hpp" +#include "./ext/matrix_common.hpp" + +#include "./ext/matrix_double2x2.hpp" +#include "./ext/matrix_double2x2_precision.hpp" +#include "./ext/matrix_double2x3.hpp" +#include "./ext/matrix_double2x3_precision.hpp" +#include "./ext/matrix_double2x4.hpp" +#include "./ext/matrix_double2x4_precision.hpp" +#include "./ext/matrix_double3x2.hpp" +#include "./ext/matrix_double3x2_precision.hpp" +#include "./ext/matrix_double3x3.hpp" +#include "./ext/matrix_double3x3_precision.hpp" +#include "./ext/matrix_double3x4.hpp" +#include "./ext/matrix_double3x4_precision.hpp" +#include "./ext/matrix_double4x2.hpp" +#include "./ext/matrix_double4x2_precision.hpp" +#include "./ext/matrix_double4x3.hpp" +#include "./ext/matrix_double4x3_precision.hpp" +#include "./ext/matrix_double4x4.hpp" +#include "./ext/matrix_double4x4_precision.hpp" + +#include "./ext/matrix_float2x2.hpp" +#include "./ext/matrix_float2x2_precision.hpp" +#include "./ext/matrix_float2x3.hpp" +#include "./ext/matrix_float2x3_precision.hpp" +#include "./ext/matrix_float2x4.hpp" +#include "./ext/matrix_float2x4_precision.hpp" +#include "./ext/matrix_float3x2.hpp" +#include "./ext/matrix_float3x2_precision.hpp" +#include "./ext/matrix_float3x3.hpp" +#include "./ext/matrix_float3x3_precision.hpp" +#include "./ext/matrix_float3x4.hpp" +#include "./ext/matrix_float3x4_precision.hpp" +#include "./ext/matrix_float4x2.hpp" +#include "./ext/matrix_float4x2_precision.hpp" +#include "./ext/matrix_float4x3.hpp" +#include "./ext/matrix_float4x3_precision.hpp" +#include "./ext/matrix_float4x4.hpp" +#include "./ext/matrix_float4x4_precision.hpp" + +#include "./ext/matrix_int2x2.hpp" +#include "./ext/matrix_int2x2_sized.hpp" +#include "./ext/matrix_int2x3.hpp" +#include "./ext/matrix_int2x3_sized.hpp" +#include "./ext/matrix_int2x4.hpp" +#include "./ext/matrix_int2x4_sized.hpp" +#include "./ext/matrix_int3x2.hpp" +#include "./ext/matrix_int3x2_sized.hpp" +#include "./ext/matrix_int3x3.hpp" +#include "./ext/matrix_int3x3_sized.hpp" +#include "./ext/matrix_int3x4.hpp" +#include "./ext/matrix_int3x4_sized.hpp" +#include "./ext/matrix_int4x2.hpp" +#include "./ext/matrix_int4x2_sized.hpp" +#include "./ext/matrix_int4x3.hpp" +#include "./ext/matrix_int4x3_sized.hpp" +#include "./ext/matrix_int4x4.hpp" +#include "./ext/matrix_int4x4_sized.hpp" + +#include "./ext/matrix_uint2x2.hpp" +#include "./ext/matrix_uint2x2_sized.hpp" +#include "./ext/matrix_uint2x3.hpp" +#include "./ext/matrix_uint2x3_sized.hpp" +#include "./ext/matrix_uint2x4.hpp" +#include "./ext/matrix_uint2x4_sized.hpp" +#include "./ext/matrix_uint3x2.hpp" +#include "./ext/matrix_uint3x2_sized.hpp" +#include "./ext/matrix_uint3x3.hpp" +#include "./ext/matrix_uint3x3_sized.hpp" +#include "./ext/matrix_uint3x4.hpp" +#include "./ext/matrix_uint3x4_sized.hpp" +#include "./ext/matrix_uint4x2.hpp" +#include "./ext/matrix_uint4x2_sized.hpp" +#include "./ext/matrix_uint4x3.hpp" +#include "./ext/matrix_uint4x3_sized.hpp" +#include "./ext/matrix_uint4x4.hpp" +#include "./ext/matrix_uint4x4_sized.hpp" + +#include "./ext/matrix_projection.hpp" +#include "./ext/matrix_relational.hpp" +#include "./ext/matrix_transform.hpp" + +#include "./ext/quaternion_common.hpp" +#include "./ext/quaternion_double.hpp" +#include "./ext/quaternion_double_precision.hpp" +#include "./ext/quaternion_float.hpp" +#include "./ext/quaternion_float_precision.hpp" +#include "./ext/quaternion_exponential.hpp" +#include "./ext/quaternion_geometric.hpp" +#include "./ext/quaternion_relational.hpp" +#include "./ext/quaternion_transform.hpp" +#include "./ext/quaternion_trigonometric.hpp" + +#include "./ext/scalar_common.hpp" +#include "./ext/scalar_constants.hpp" +#include "./ext/scalar_integer.hpp" +#include "./ext/scalar_packing.hpp" +#include "./ext/scalar_reciprocal.hpp" +#include "./ext/scalar_relational.hpp" +#include "./ext/scalar_ulp.hpp" + +#include "./ext/scalar_int_sized.hpp" +#include "./ext/scalar_uint_sized.hpp" + +#include "./ext/vector_common.hpp" +#include "./ext/vector_integer.hpp" +#include "./ext/vector_packing.hpp" +#include "./ext/vector_reciprocal.hpp" +#include "./ext/vector_relational.hpp" +#include "./ext/vector_ulp.hpp" + +#include "./ext/vector_bool1.hpp" +#include "./ext/vector_bool1_precision.hpp" +#include "./ext/vector_bool2.hpp" +#include "./ext/vector_bool2_precision.hpp" +#include "./ext/vector_bool3.hpp" +#include "./ext/vector_bool3_precision.hpp" +#include "./ext/vector_bool4.hpp" +#include "./ext/vector_bool4_precision.hpp" + +#include "./ext/vector_double1.hpp" +#include "./ext/vector_double1_precision.hpp" +#include "./ext/vector_double2.hpp" +#include "./ext/vector_double2_precision.hpp" +#include "./ext/vector_double3.hpp" +#include "./ext/vector_double3_precision.hpp" +#include "./ext/vector_double4.hpp" +#include "./ext/vector_double4_precision.hpp" + +#include "./ext/vector_float1.hpp" +#include "./ext/vector_float1_precision.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float2_precision.hpp" +#include "./ext/vector_float3.hpp" +#include "./ext/vector_float3_precision.hpp" +#include "./ext/vector_float4.hpp" +#include "./ext/vector_float4_precision.hpp" + +#include "./ext/vector_int1.hpp" +#include "./ext/vector_int1_sized.hpp" +#include "./ext/vector_int2.hpp" +#include "./ext/vector_int2_sized.hpp" +#include "./ext/vector_int3.hpp" +#include "./ext/vector_int3_sized.hpp" +#include "./ext/vector_int4.hpp" +#include "./ext/vector_int4_sized.hpp" + +#include "./ext/vector_uint1.hpp" +#include "./ext/vector_uint1_sized.hpp" +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_uint2_sized.hpp" +#include "./ext/vector_uint3.hpp" +#include "./ext/vector_uint3_sized.hpp" +#include "./ext/vector_uint4.hpp" +#include "./ext/vector_uint4_sized.hpp" + +#include "./gtc/bitfield.hpp" +#include "./gtc/color_space.hpp" +#include "./gtc/constants.hpp" +#include "./gtc/epsilon.hpp" +#include "./gtc/integer.hpp" +#include "./gtc/matrix_access.hpp" +#include "./gtc/matrix_integer.hpp" +#include "./gtc/matrix_inverse.hpp" +#include "./gtc/matrix_transform.hpp" +#include "./gtc/noise.hpp" +#include "./gtc/packing.hpp" +#include "./gtc/quaternion.hpp" +#include "./gtc/random.hpp" +#include "./gtc/reciprocal.hpp" +#include "./gtc/round.hpp" +#include "./gtc/type_precision.hpp" +#include "./gtc/type_ptr.hpp" +#include "./gtc/ulp.hpp" +#include "./gtc/vec1.hpp" +#if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE +# include "./gtc/type_aligned.hpp" +#endif + +#ifdef GLM_ENABLE_EXPERIMENTAL +#include "./gtx/associated_min_max.hpp" +#include "./gtx/bit.hpp" +#include "./gtx/closest_point.hpp" +#include "./gtx/color_encoding.hpp" +#include "./gtx/color_space.hpp" +#include "./gtx/color_space_YCoCg.hpp" +#include "./gtx/common.hpp" +#include "./gtx/compatibility.hpp" +#include "./gtx/component_wise.hpp" +#include "./gtx/dual_quaternion.hpp" +#include "./gtx/easing.hpp" +#include "./gtx/euler_angles.hpp" +#include "./gtx/extend.hpp" +#include "./gtx/extended_min_max.hpp" +#include "./gtx/fast_exponential.hpp" +#include "./gtx/fast_square_root.hpp" +#include "./gtx/fast_trigonometry.hpp" +#include "./gtx/functions.hpp" +#include "./gtx/gradient_paint.hpp" +#include "./gtx/handed_coordinate_space.hpp" + +#if __cplusplus >= 201103L +#include "./gtx/hash.hpp" +#endif + +#include "./gtx/integer.hpp" +#include "./gtx/intersect.hpp" +#include "./gtx/io.hpp" +#include "./gtx/log_base.hpp" +#include "./gtx/matrix_cross_product.hpp" +#include "./gtx/matrix_decompose.hpp" +#include "./gtx/matrix_factorisation.hpp" +#include "./gtx/matrix_interpolation.hpp" +#include "./gtx/matrix_major_storage.hpp" +#include "./gtx/matrix_operation.hpp" +#include "./gtx/matrix_query.hpp" +#include "./gtx/mixed_product.hpp" +#include "./gtx/norm.hpp" +#include "./gtx/normal.hpp" +#include "./gtx/normalize_dot.hpp" +#include "./gtx/number_precision.hpp" +#include "./gtx/optimum_pow.hpp" +#include "./gtx/orthonormalize.hpp" +#include "./gtx/pca.hpp" +#include "./gtx/perpendicular.hpp" +#include "./gtx/polar_coordinates.hpp" +#include "./gtx/projection.hpp" +#include "./gtx/quaternion.hpp" +#include "./gtx/raw_data.hpp" +#include "./gtx/rotate_normalized_axis.hpp" +#include "./gtx/rotate_vector.hpp" +#include "./gtx/spline.hpp" +#include "./gtx/std_based_type.hpp" +#if !((GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP)) +# include "./gtx/string_cast.hpp" +#endif +#include "./gtx/transform.hpp" +#include "./gtx/transform2.hpp" +#include "./gtx/vec_swizzle.hpp" +#include "./gtx/vector_angle.hpp" +#include "./gtx/vector_query.hpp" +#include "./gtx/wrap.hpp" + +#if GLM_HAS_TEMPLATE_ALIASES +# include "./gtx/scalar_multiplication.hpp" +#endif + +#if GLM_HAS_RANGE_FOR +# include "./gtx/range.hpp" +#endif +#endif//GLM_ENABLE_EXPERIMENTAL diff --git a/thirdparty/glm/glm/ext/_matrix_vectorize.hpp b/thirdparty/glm/glm/ext/_matrix_vectorize.hpp new file mode 100644 index 0000000..0d08117 --- /dev/null +++ b/thirdparty/glm/glm/ext/_matrix_vectorize.hpp @@ -0,0 +1,128 @@ +#pragma once + +namespace glm { + + namespace detail { + + template class mat, length_t C, length_t R, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<2, 2, T, Q> call(Ret (*Func)(T x), mat<2, 2, T, Q> const &x) { + return mat<2, 2, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), + Func(x[1][0]), Func(x[1][1]) + ); + } + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<2, 3, T, Q> call(Ret (*Func)(T x), mat<2, 3, T, Q> const &x) { + return mat<2, 3, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<2, 4, T, Q> call(Ret (*Func)(T x), mat<2, 4, T, Q> const &x) { + return mat<2, 4, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), Func(x[0][3]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]), Func(x[1][3]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<3, 2, T, Q> call(Ret (*Func)(T x), mat<3, 2, T, Q> const &x) { + return mat<3, 2, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), + Func(x[1][0]), Func(x[1][1]), + Func(x[2][0]), Func(x[2][1]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<3, 3, T, Q> call(Ret (*Func)(T x), mat<3, 3, T, Q> const &x) { + return mat<3, 3, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]), + Func(x[2][0]), Func(x[2][1]), Func(x[2][2]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<3, 4, T, Q> call(Ret (*Func)(T x), mat<3, 4, T, Q> const &x) { + return mat<3, 4, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), Func(x[0][3]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]), Func(x[1][3]), + Func(x[2][0]), Func(x[2][1]), Func(x[2][2]), Func(x[2][3]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<4, 2, T, Q> call(Ret (*Func)(T x), mat<4, 2, T, Q> const &x) { + return mat<4, 2, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), + Func(x[1][0]), Func(x[1][1]), + Func(x[2][0]), Func(x[2][1]), + Func(x[3][0]), Func(x[3][1]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<4, 3, T, Q> call(Ret (*Func)(T x), mat<4, 3, T, Q> const &x) { + return mat<4, 3, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]), + Func(x[2][0]), Func(x[2][1]), Func(x[2][2]), + Func(x[3][0]), Func(x[3][1]), Func(x[3][2]) + ); + } + + }; + + template class mat, typename Ret, typename T, qualifier Q> + struct matrix_functor_1 { + + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat<4, 4, T, Q> call(Ret (*Func)(T x), mat<4, 4, T, Q> const &x) { + return mat<4, 4, Ret, Q>( + Func(x[0][0]), Func(x[0][1]), Func(x[0][2]), Func(x[0][3]), + Func(x[1][0]), Func(x[1][1]), Func(x[1][2]), Func(x[1][3]), + Func(x[2][0]), Func(x[2][1]), Func(x[2][2]), Func(x[2][3]), + Func(x[3][0]), Func(x[3][1]), Func(x[3][2]), Func(x[3][3]) + ); + } + + }; + + } + +}// namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_clip_space.hpp b/thirdparty/glm/glm/ext/matrix_clip_space.hpp new file mode 100644 index 0000000..43579b8 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_clip_space.hpp @@ -0,0 +1,522 @@ +/// @ref ext_matrix_clip_space +/// @file glm/ext/matrix_clip_space.hpp +/// +/// @defgroup ext_matrix_clip_space GLM_EXT_matrix_clip_space +/// @ingroup ext +/// +/// Defines functions that generate clip space transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_transform +/// @see ext_matrix_projection + +#pragma once + +// Dependencies +#include "../ext/scalar_constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_clip_space extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_clip_space + /// @{ + + /// Creates a matrix for projecting two-dimensional coordinates onto the screen. + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top, T const& zNear, T const& zFar) + /// @see gluOrtho2D man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> ortho( + T left, T right, T bottom, T top); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH_ZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH_NO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH_ZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH_NO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoZO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoNO( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using left-handed coordinates. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoLH( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using right-handed coordinates. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> orthoRH( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a matrix for an orthographic parallel viewing volume, using the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @tparam T A floating-point scalar type + /// + /// @see - glm::ortho(T const& left, T const& right, T const& bottom, T const& top) + /// @see glOrtho man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> ortho( + T left, T right, T bottom, T top, T zNear, T zFar); + + /// Creates a left-handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH_ZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a left-handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH_NO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right-handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH_ZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right-handed frustum matrix. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH_NO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumZO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumNO( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a left-handed frustum matrix. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumLH( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a right-handed frustum matrix. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustumRH( + T left, T right, T bottom, T top, T near, T far); + + /// Creates a frustum matrix with default handedness, using the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @tparam T A floating-point scalar type + /// @see glFrustum man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> frustum( + T left, T right, T bottom, T top, T near, T far); + + + /// Creates a matrix for a right-handed, symmetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH_ZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a right-handed, symmetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH_NO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left-handed, symmetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH_ZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left-handed, symmetric perspective-view frustum. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH_NO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symmetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveZO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symmetric perspective-view frustum using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveNO( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a right-handed, symmetric perspective-view frustum. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveRH( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a left-handed, symmetric perspective-view frustum. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveLH( + T fovy, T aspect, T near, T far); + + /// Creates a matrix for a symmetric perspective-view frustum based on the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param fovy Specifies the field of view angle in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + /// @see gluPerspective man page + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspective( + T fovy, T aspect, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH_ZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using right-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH_NO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH_ZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH_NO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovZO( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view using left-handed coordinates if GLM_FORCE_LEFT_HANDED if defined or right-handed coordinates otherwise. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovNO( + T fov, T width, T height, T near, T far); + + /// Builds a right-handed perspective projection matrix based on a field of view. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovRH( + T fov, T width, T height, T near, T far); + + /// Builds a left-handed perspective projection matrix based on a field of view. + /// If GLM_FORCE_DEPTH_ZERO_TO_ONE is defined, the near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// Otherwise, the near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFovLH( + T fov, T width, T height, T near, T far); + + /// Builds a perspective projection matrix based on a field of view and the default handedness and default near and far clip planes definition. + /// To change default handedness use GLM_FORCE_LEFT_HANDED. To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param fov Expressed in radians. + /// @param width Width of the viewport + /// @param height Height of the viewport + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param far Specifies the distance from the viewer to the far clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> perspectiveFov( + T fov, T width, T height, T near, T far); + + /// Creates a matrix for a left-handed, symmetric perspective-view frustum with far plane at infinite. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspectiveLH( + T fovy, T aspect, T near); + + /// Creates a matrix for a right-handed, symmetric perspective-view frustum with far plane at infinite. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspectiveRH( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite with default handedness. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> infinitePerspective( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> tweakedInfinitePerspective( + T fovy, T aspect, T near); + + /// Creates a matrix for a symmetric perspective-view frustum with far plane at infinite for graphics hardware that doesn't support depth clamping. + /// + /// @param fovy Specifies the field of view angle, in degrees, in the y direction. Expressed in radians. + /// @param aspect Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). + /// @param near Specifies the distance from the viewer to the near clipping plane (always positive). + /// @param ep Epsilon + /// + /// @tparam T A floating-point scalar type + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> tweakedInfinitePerspective( + T fovy, T aspect, T near, T ep); + + /// @} +}//namespace glm + +#include "matrix_clip_space.inl" diff --git a/thirdparty/glm/glm/ext/matrix_clip_space.inl b/thirdparty/glm/glm/ext/matrix_clip_space.inl new file mode 100644 index 0000000..27fb6a1 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_clip_space.inl @@ -0,0 +1,595 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top) + { + mat<4, 4, T, defaultp> Result(static_cast(1)); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(1); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = static_cast(1) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - zNear / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_NO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = static_cast(2) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - (zFar + zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_ZO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(1) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - zNear / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH_NO(T left, T right, T bottom, T top, T zNear, T zFar) + { + mat<4, 4, T, defaultp> Result(1); + Result[0][0] = static_cast(2) / (right - left); + Result[1][1] = static_cast(2) / (top - bottom); + Result[2][2] = - static_cast(2) / (zFar - zNear); + Result[3][0] = - (right + left) / (right - left); + Result[3][1] = - (top + bottom) / (top - bottom); + Result[3][2] = - (zFar + zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoZO(T left, T right, T bottom, T top, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); +# else + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoNO(T left, T right, T bottom, T top, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return orthoLH_NO(left, right, bottom, top, zNear, zFar); +# else + return orthoRH_NO(left, right, bottom, top, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH(T left, T right, T bottom, T top, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); +# else + return orthoLH_NO(left, right, bottom, top, zNear, zFar); +# endif + + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoRH(T left, T right, T bottom, T top, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); +# else + return orthoRH_NO(left, right, bottom, top, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> ortho(T left, T right, T bottom, T top, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO + return orthoLH_ZO(left, right, bottom, top, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO + return orthoLH_NO(left, right, bottom, top, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO + return orthoRH_ZO(left, right, bottom, top, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO + return orthoRH_NO(left, right, bottom, top, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = -(right + left) / (right - left); + Result[2][1] = -(top + bottom) / (top - bottom); + Result[2][2] = farVal / (farVal - nearVal); + Result[2][3] = static_cast(1); + Result[3][2] = -(farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH_NO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = -(right + left) / (right - left); + Result[2][1] = -(top + bottom) / (top - bottom); + Result[2][2] = (farVal + nearVal) / (farVal - nearVal); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH_ZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = farVal / (nearVal - farVal); + Result[2][3] = static_cast(-1); + Result[3][2] = -(farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH_NO(T left, T right, T bottom, T top, T nearVal, T farVal) + { + mat<4, 4, T, defaultp> Result(0); + Result[0][0] = (static_cast(2) * nearVal) / (right - left); + Result[1][1] = (static_cast(2) * nearVal) / (top - bottom); + Result[2][0] = (right + left) / (right - left); + Result[2][1] = (top + bottom) / (top - bottom); + Result[2][2] = - (farVal + nearVal) / (farVal - nearVal); + Result[2][3] = static_cast(-1); + Result[3][2] = - (static_cast(2) * farVal * nearVal) / (farVal - nearVal); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumZO(T left, T right, T bottom, T top, T nearVal, T farVal) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); +# else + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumNO(T left, T right, T bottom, T top, T nearVal, T farVal) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); +# else + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumLH(T left, T right, T bottom, T top, T nearVal, T farVal) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); +# else + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustumRH(T left, T right, T bottom, T top, T nearVal, T farVal) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); +# else + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> frustum(T left, T right, T bottom, T top, T nearVal, T farVal) + { +# if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO + return frustumLH_ZO(left, right, bottom, top, nearVal, farVal); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO + return frustumLH_NO(left, right, bottom, top, nearVal, farVal); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO + return frustumRH_ZO(left, right, bottom, top, nearVal, farVal); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO + return frustumRH_NO(left, right, bottom, top, nearVal, farVal); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH_ZO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = zFar / (zNear - zFar); + Result[2][3] = - static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH_NO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = - (zFar + zNear) / (zFar - zNear); + Result[2][3] = - static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH_ZO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = zFar / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar) + { + assert(abs(aspect - std::numeric_limits::epsilon()) > static_cast(0)); + + T const tanHalfFovy = tan(fovy / static_cast(2)); + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = static_cast(1) / (aspect * tanHalfFovy); + Result[1][1] = static_cast(1) / (tanHalfFovy); + Result[2][2] = (zFar + zNear) / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveZO(T fovy, T aspect, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); +# else + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveNO(T fovy, T aspect, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return perspectiveLH_NO(fovy, aspect, zNear, zFar); +# else + return perspectiveRH_NO(fovy, aspect, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH(T fovy, T aspect, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); +# else + return perspectiveLH_NO(fovy, aspect, zNear, zFar); +# endif + + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveRH(T fovy, T aspect, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); +# else + return perspectiveRH_NO(fovy, aspect, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspective(T fovy, T aspect, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO + return perspectiveLH_ZO(fovy, aspect, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO + return perspectiveLH_NO(fovy, aspect, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO + return perspectiveRH_ZO(fovy, aspect, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO + return perspectiveRH_NO(fovy, aspect, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH_ZO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = zFar / (zNear - zFar); + Result[2][3] = - static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH_NO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = - (zFar + zNear) / (zFar - zNear); + Result[2][3] = - static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH_ZO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = zFar / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = -(zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH_NO(T fov, T width, T height, T zNear, T zFar) + { + assert(width > static_cast(0)); + assert(height > static_cast(0)); + assert(fov > static_cast(0)); + + T const rad = fov; + T const h = glm::cos(static_cast(0.5) * rad) / glm::sin(static_cast(0.5) * rad); + T const w = h * height / width; ///todo max(width , Height) / min(width , Height)? + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = w; + Result[1][1] = h; + Result[2][2] = (zFar + zNear) / (zFar - zNear); + Result[2][3] = static_cast(1); + Result[3][2] = - (static_cast(2) * zFar * zNear) / (zFar - zNear); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovZO(T fov, T width, T height, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); +# else + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovNO(T fov, T width, T height, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); +# else + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovLH(T fov, T width, T height, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); +# else + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFovRH(T fov, T width, T height, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); +# else + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveFov(T fov, T width, T height, T zNear, T zFar) + { +# if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO + return perspectiveFovLH_ZO(fov, width, height, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO + return perspectiveFovLH_NO(fov, width, height, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO + return perspectiveFovRH_ZO(fov, width, height, zNear, zFar); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO + return perspectiveFovRH_NO(fov, width, height, zNear, zFar); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveRH_NO(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = - static_cast(1); + Result[2][3] = - static_cast(1); + Result[3][2] = - static_cast(2) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveRH_ZO(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = - static_cast(1); + Result[2][3] = - static_cast(1); + Result[3][2] = - zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveLH_NO(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(T(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = static_cast(1); + Result[2][3] = static_cast(1); + Result[3][2] = - static_cast(2) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspectiveLH_ZO(T fovy, T aspect, T zNear) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(T(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = static_cast(1); + Result[2][3] = static_cast(1); + Result[3][2] = - zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> infinitePerspective(T fovy, T aspect, T zNear) + { +# if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO + return infinitePerspectiveLH_ZO(fovy, aspect, zNear); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO + return infinitePerspectiveLH_NO(fovy, aspect, zNear); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_ZO + return infinitePerspectiveRH_ZO(fovy, aspect, zNear); +# elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_RH_NO + return infinitePerspectiveRH_NO(fovy, aspect, zNear); +# endif + } + + // Infinite projection matrix: http://www.terathon.com/gdc07_lengyel.pdf + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear, T ep) + { + T const range = tan(fovy / static_cast(2)) * zNear; + T const left = -range * aspect; + T const right = range * aspect; + T const bottom = -range; + T const top = range; + + mat<4, 4, T, defaultp> Result(static_cast(0)); + Result[0][0] = (static_cast(2) * zNear) / (right - left); + Result[1][1] = (static_cast(2) * zNear) / (top - bottom); + Result[2][2] = ep - static_cast(1); + Result[2][3] = static_cast(-1); + Result[3][2] = (ep - static_cast(2)) * zNear; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> tweakedInfinitePerspective(T fovy, T aspect, T zNear) + { + return tweakedInfinitePerspective(fovy, aspect, zNear, epsilon()); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_common.hpp b/thirdparty/glm/glm/ext/matrix_common.hpp new file mode 100644 index 0000000..6bb3d06 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_common.hpp @@ -0,0 +1,39 @@ +/// @ref ext_matrix_common +/// @file glm/ext/matrix_common.hpp +/// +/// @defgroup ext_matrix_common GLM_EXT_matrix_common +/// @ingroup ext +/// +/// Defines functions for common matrix operations. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_common + +#pragma once + +#include "../detail/qualifier.hpp" +#include "../detail/_fixes.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_common + /// @{ + + template + GLM_FUNC_DECL mat mix(mat const& x, mat const& y, mat const& a); + + template + GLM_FUNC_DECL mat mix(mat const& x, mat const& y, U a); + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat abs(mat const& x); + + /// @} +}//namespace glm + +#include "matrix_common.inl" diff --git a/thirdparty/glm/glm/ext/matrix_common.inl b/thirdparty/glm/glm/ext/matrix_common.inl new file mode 100644 index 0000000..1be4222 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_common.inl @@ -0,0 +1,34 @@ +#include "../matrix.hpp" + +#include "_matrix_vectorize.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat mix(mat const& x, mat const& y, U a) + { + return mat(x) * (static_cast(1) - a) + mat(y) * a; + } + + template + GLM_FUNC_QUALIFIER mat mix(mat const& x, mat const& y, mat const& a) + { + return matrixCompMult(mat(x), static_cast(1) - a) + matrixCompMult(mat(y), a); + } + + template + struct compute_abs_matrix + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static mat call(mat const& x) + { + return detail::matrix_functor_1::call(abs, x); + } + }; + + template + GLM_FUNC_DECL GLM_CONSTEXPR mat abs(mat const& x) + { + return compute_abs_matrix::value>::call(x); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x2.hpp b/thirdparty/glm/glm/ext/matrix_double2x2.hpp new file mode 100644 index 0000000..94dca54 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x2.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double2x2.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, double, defaultp> dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, double, defaultp> dmat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_double2x2_precision.hpp new file mode 100644 index 0000000..9e2c174 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x2_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, lowp> lowp_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, mediump> mediump_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, highp> highp_dmat2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, lowp> lowp_dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, mediump> mediump_dmat2x2; + + /// 2 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, double, highp> highp_dmat2x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x3.hpp b/thirdparty/glm/glm/ext/matrix_double2x3.hpp new file mode 100644 index 0000000..bfef87a --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double2x3.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 3, double, defaultp> dmat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_double2x3_precision.hpp new file mode 100644 index 0000000..098fb60 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double2x3_precision.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, lowp> lowp_dmat2x3; + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, mediump> mediump_dmat2x3; + + /// 2 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, double, highp> highp_dmat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x4.hpp b/thirdparty/glm/glm/ext/matrix_double2x4.hpp new file mode 100644 index 0000000..499284b --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double2x4.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 4, double, defaultp> dmat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double2x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_double2x4_precision.hpp new file mode 100644 index 0000000..9b61ebc --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double2x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double2x4_precision.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, lowp> lowp_dmat2x4; + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, mediump> mediump_dmat2x4; + + /// 2 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, double, highp> highp_dmat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x2.hpp b/thirdparty/glm/glm/ext/matrix_double3x2.hpp new file mode 100644 index 0000000..dd23f36 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double3x2.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 2, double, defaultp> dmat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_double3x2_precision.hpp new file mode 100644 index 0000000..068d9e9 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double3x2_precision.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, lowp> lowp_dmat3x2; + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, mediump> mediump_dmat3x2; + + /// 3 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, double, highp> highp_dmat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x3.hpp b/thirdparty/glm/glm/ext/matrix_double3x3.hpp new file mode 100644 index 0000000..53572b7 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x3.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double3x3.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, double, defaultp> dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, double, defaultp> dmat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_double3x3_precision.hpp new file mode 100644 index 0000000..8691e78 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x3_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double3x3_precision.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, lowp> lowp_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, mediump> mediump_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, highp> highp_dmat3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, lowp> lowp_dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, mediump> mediump_dmat3x3; + + /// 3 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, double, highp> highp_dmat3x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x4.hpp b/thirdparty/glm/glm/ext/matrix_double3x4.hpp new file mode 100644 index 0000000..c572d63 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double3x4.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 4, double, defaultp> dmat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double3x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_double3x4_precision.hpp new file mode 100644 index 0000000..f040217 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double3x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double3x4_precision.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, lowp> lowp_dmat3x4; + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, mediump> mediump_dmat3x4; + + /// 3 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, double, highp> highp_dmat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x2.hpp b/thirdparty/glm/glm/ext/matrix_double4x2.hpp new file mode 100644 index 0000000..9b229f4 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double4x2.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 2 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 2, double, defaultp> dmat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_double4x2_precision.hpp new file mode 100644 index 0000000..6ad18ba --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double4x2_precision.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, lowp> lowp_dmat4x2; + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, mediump> mediump_dmat4x2; + + /// 4 columns of 2 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, double, highp> highp_dmat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x3.hpp b/thirdparty/glm/glm/ext/matrix_double4x3.hpp new file mode 100644 index 0000000..dca4cf9 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_double4x3.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 3 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 3, double, defaultp> dmat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_double4x3_precision.hpp new file mode 100644 index 0000000..f7371de --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_double4x3_precision.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, lowp> lowp_dmat4x3; + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, mediump> mediump_dmat4x3; + + /// 4 columns of 3 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, double, highp> highp_dmat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x4.hpp b/thirdparty/glm/glm/ext/matrix_double4x4.hpp new file mode 100644 index 0000000..81e1bf6 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x4.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_double4x4.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, double, defaultp> dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, double, defaultp> dmat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_double4x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_double4x4_precision.hpp new file mode 100644 index 0000000..4c36a84 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_double4x4_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_double4x4_precision.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, lowp> lowp_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, mediump> mediump_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, highp> highp_dmat4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, lowp> lowp_dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, mediump> mediump_dmat4x4; + + /// 4 columns of 4 components matrix of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, double, highp> highp_dmat4x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x2.hpp b/thirdparty/glm/glm/ext/matrix_float2x2.hpp new file mode 100644 index 0000000..53df921 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x2.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, float, defaultp> mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 2, float, defaultp> mat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_float2x2_precision.hpp new file mode 100644 index 0000000..898b6db --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x2_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, lowp> lowp_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, mediump> mediump_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, highp> highp_mat2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, lowp> lowp_mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, mediump> mediump_mat2x2; + + /// 2 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 2, float, highp> highp_mat2x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x3.hpp b/thirdparty/glm/glm/ext/matrix_float2x3.hpp new file mode 100644 index 0000000..6f68822 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float2x3.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 3, float, defaultp> mat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_float2x3_precision.hpp new file mode 100644 index 0000000..50c1032 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x3_precision.hpp + +#pragma once +#include "../detail/type_mat2x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, lowp> lowp_mat2x3; + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, mediump> mediump_mat2x3; + + /// 2 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 3, float, highp> highp_mat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x4.hpp b/thirdparty/glm/glm/ext/matrix_float2x4.hpp new file mode 100644 index 0000000..30f30de --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float2x4.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 2 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<2, 4, float, defaultp> mat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float2x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_float2x4_precision.hpp new file mode 100644 index 0000000..079d638 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float2x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x4_precision.hpp + +#pragma once +#include "../detail/type_mat2x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, lowp> lowp_mat2x4; + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, mediump> mediump_mat2x4; + + /// 2 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<2, 4, float, highp> highp_mat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x2.hpp b/thirdparty/glm/glm/ext/matrix_float3x2.hpp new file mode 100644 index 0000000..280d0a3 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float3x2.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 2, float, defaultp> mat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_float3x2_precision.hpp new file mode 100644 index 0000000..8572c2a --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float3x2_precision.hpp + +#pragma once +#include "../detail/type_mat3x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, lowp> lowp_mat3x2; + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, mediump> mediump_mat3x2; + + /// 3 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 2, float, highp> highp_mat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x3.hpp b/thirdparty/glm/glm/ext/matrix_float3x3.hpp new file mode 100644 index 0000000..177d809 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x3.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float3x3.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, float, defaultp> mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 3, float, defaultp> mat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_float3x3_precision.hpp new file mode 100644 index 0000000..8a900c1 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x3_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float3x3_precision.hpp + +#pragma once +#include "../detail/type_mat3x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, lowp> lowp_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, mediump> mediump_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, highp> highp_mat3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, lowp> lowp_mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, mediump> mediump_mat3x3; + + /// 3 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 3, float, highp> highp_mat3x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x4.hpp b/thirdparty/glm/glm/ext/matrix_float3x4.hpp new file mode 100644 index 0000000..64b8459 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float3x4.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 3 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<3, 4, float, defaultp> mat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float3x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_float3x4_precision.hpp new file mode 100644 index 0000000..bc36bf1 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float3x4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float3x4_precision.hpp + +#pragma once +#include "../detail/type_mat3x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, lowp> lowp_mat3x4; + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, mediump> mediump_mat3x4; + + /// 3 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<3, 4, float, highp> highp_mat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x2.hpp b/thirdparty/glm/glm/ext/matrix_float4x2.hpp new file mode 100644 index 0000000..1ed5227 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float4x2.hpp + +#pragma once +#include "../detail/type_mat4x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 2 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 2, float, defaultp> mat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x2_precision.hpp b/thirdparty/glm/glm/ext/matrix_float4x2_precision.hpp new file mode 100644 index 0000000..88fd069 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float2x2_precision.hpp + +#pragma once +#include "../detail/type_mat2x2.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, lowp> lowp_mat4x2; + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, mediump> mediump_mat4x2; + + /// 4 columns of 2 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 2, float, highp> highp_mat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x3.hpp b/thirdparty/glm/glm/ext/matrix_float4x3.hpp new file mode 100644 index 0000000..5dbe765 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/matrix_float4x3.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix + /// @{ + + /// 4 columns of 3 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 3, float, defaultp> mat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x3_precision.hpp b/thirdparty/glm/glm/ext/matrix_float4x3_precision.hpp new file mode 100644 index 0000000..846ed4f --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/matrix_float4x3_precision.hpp + +#pragma once +#include "../detail/type_mat4x3.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, lowp> lowp_mat4x3; + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, mediump> mediump_mat4x3; + + /// 4 columns of 3 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 3, float, highp> highp_mat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x4.hpp b/thirdparty/glm/glm/ext/matrix_float4x4.hpp new file mode 100644 index 0000000..5ba111d --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x4.hpp @@ -0,0 +1,23 @@ +/// @ref core +/// @file glm/ext/matrix_float4x4.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @ingroup core_matrix + /// @{ + + /// 4 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, float, defaultp> mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + typedef mat<4, 4, float, defaultp> mat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_float4x4_precision.hpp b/thirdparty/glm/glm/ext/matrix_float4x4_precision.hpp new file mode 100644 index 0000000..597149b --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_float4x4_precision.hpp @@ -0,0 +1,49 @@ +/// @ref core +/// @file glm/ext/matrix_float4x4_precision.hpp + +#pragma once +#include "../detail/type_mat4x4.hpp" + +namespace glm +{ + /// @addtogroup core_matrix_precision + /// @{ + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, lowp> lowp_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, mediump> mediump_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, highp> highp_mat4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, lowp> lowp_mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, mediump> mediump_mat4x4; + + /// 4 columns of 4 components matrix of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see GLSL 4.20.8 specification, section 4.1.6 Matrices + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef mat<4, 4, float, highp> highp_mat4x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x2.hpp b/thirdparty/glm/glm/ext/matrix_int2x2.hpp new file mode 100644 index 0000000..c6aa068 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x2.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_int2x2 +/// @file glm/ext/matrix_int2x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x2 GLM_EXT_matrix_int2x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x2 + /// @{ + + /// Signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2 + typedef mat<2, 2, int, defaultp> imat2x2; + + /// Signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2 + typedef mat<2, 2, int, defaultp> imat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_int2x2_sized.hpp new file mode 100644 index 0000000..70c0c21 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x2_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_int2x2_sized +/// @file glm/ext/matrix_int2x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x2_sized GLM_EXT_matrix_int2x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x2_sized + /// @{ + + /// 8 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int8, defaultp> i8mat2x2; + + /// 16 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int16, defaultp> i16mat2x2; + + /// 32 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int32, defaultp> i32mat2x2; + + /// 64 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int64, defaultp> i64mat2x2; + + + /// 8 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int8, defaultp> i8mat2; + + /// 16 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int16, defaultp> i16mat2; + + /// 32 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int32, defaultp> i32mat2; + + /// 64 bit signed integer 2x2 matrix. + /// + /// @see ext_matrix_int2x2_sized + typedef mat<2, 2, int64, defaultp> i64mat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x3.hpp b/thirdparty/glm/glm/ext/matrix_int2x3.hpp new file mode 100644 index 0000000..aee415c --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x3.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int2x3 +/// @file glm/ext/matrix_int2x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x3 GLM_EXT_matrix_int2x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x3 + /// @{ + + /// Signed integer 2x3 matrix. + /// + /// @see ext_matrix_int2x3 + typedef mat<2, 3, int, defaultp> imat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_int2x3_sized.hpp new file mode 100644 index 0000000..b5526fe --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int2x3_sized +/// @file glm/ext/matrix_int2x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x3_sized GLM_EXT_matrix_int2x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x3.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x3_sized + /// @{ + + /// 8 bit signed integer 2x3 matrix. + /// + /// @see ext_matrix_int2x3_sized + typedef mat<2, 3, int8, defaultp> i8mat2x3; + + /// 16 bit signed integer 2x3 matrix. + /// + /// @see ext_matrix_int2x3_sized + typedef mat<2, 3, int16, defaultp> i16mat2x3; + + /// 32 bit signed integer 2x3 matrix. + /// + /// @see ext_matrix_int2x3_sized + typedef mat<2, 3, int32, defaultp> i32mat2x3; + + /// 64 bit signed integer 2x3 matrix. + /// + /// @see ext_matrix_int2x3_sized + typedef mat<2, 3, int64, defaultp> i64mat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x4.hpp b/thirdparty/glm/glm/ext/matrix_int2x4.hpp new file mode 100644 index 0000000..4f36331 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x4.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int2x4 +/// @file glm/ext/matrix_int2x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x4 GLM_EXT_matrix_int2x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x4 + /// @{ + + /// Signed integer 2x4 matrix. + /// + /// @see ext_matrix_int2x4 + typedef mat<2, 4, int, defaultp> imat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int2x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_int2x4_sized.hpp new file mode 100644 index 0000000..a66a5e7 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int2x4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int2x4_sized +/// @file glm/ext/matrix_int2x4_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int2x4_sized GLM_EXT_matrix_int2x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x4.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int2x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int2x4_sized + /// @{ + + /// 8 bit signed integer 2x4 matrix. + /// + /// @see ext_matrix_int2x4_sized + typedef mat<2, 4, int8, defaultp> i8mat2x4; + + /// 16 bit signed integer 2x4 matrix. + /// + /// @see ext_matrix_int2x4_sized + typedef mat<2, 4, int16, defaultp> i16mat2x4; + + /// 32 bit signed integer 2x4 matrix. + /// + /// @see ext_matrix_int2x4_sized + typedef mat<2, 4, int32, defaultp> i32mat2x4; + + /// 64 bit signed integer 2x4 matrix. + /// + /// @see ext_matrix_int2x4_sized + typedef mat<2, 4, int64, defaultp> i64mat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x2.hpp b/thirdparty/glm/glm/ext/matrix_int3x2.hpp new file mode 100644 index 0000000..3bd563b --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x2.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int3x2 +/// @file glm/ext/matrix_int3x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x2 GLM_EXT_matrix_int3x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x2 + /// @{ + + /// Signed integer 3x2 matrix. + /// + /// @see ext_matrix_int3x2 + typedef mat<3, 2, int, defaultp> imat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_int3x2_sized.hpp new file mode 100644 index 0000000..7e34c52 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int3x2_sized +/// @file glm/ext/matrix_int3x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x2_sized GLM_EXT_matrix_int3x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x2.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x2_sized + /// @{ + + /// 8 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_int3x2_sized + typedef mat<3, 2, int8, defaultp> i8mat3x2; + + /// 16 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_int3x2_sized + typedef mat<3, 2, int16, defaultp> i16mat3x2; + + /// 32 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_int3x2_sized + typedef mat<3, 2, int32, defaultp> i32mat3x2; + + /// 64 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_int3x2_sized + typedef mat<3, 2, int64, defaultp> i64mat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x3.hpp b/thirdparty/glm/glm/ext/matrix_int3x3.hpp new file mode 100644 index 0000000..287488d --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x3.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_int3x3 +/// @file glm/ext/matrix_int3x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x3 GLM_EXT_matrix_int3x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x3 + /// @{ + + /// Signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3 + typedef mat<3, 3, int, defaultp> imat3x3; + + /// Signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3 + typedef mat<3, 3, int, defaultp> imat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_int3x3_sized.hpp new file mode 100644 index 0000000..577e305 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x3_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_int3x3_sized +/// @file glm/ext/matrix_int3x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x3_sized GLM_EXT_matrix_int3x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x3_sized + /// @{ + + /// 8 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int8, defaultp> i8mat3x3; + + /// 16 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int16, defaultp> i16mat3x3; + + /// 32 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int32, defaultp> i32mat3x3; + + /// 64 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int64, defaultp> i64mat3x3; + + + /// 8 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int8, defaultp> i8mat3; + + /// 16 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int16, defaultp> i16mat3; + + /// 32 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int32, defaultp> i32mat3; + + /// 64 bit signed integer 3x3 matrix. + /// + /// @see ext_matrix_int3x3_sized + typedef mat<3, 3, int64, defaultp> i64mat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x4.hpp b/thirdparty/glm/glm/ext/matrix_int3x4.hpp new file mode 100644 index 0000000..08e534d --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x4.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int3x4 +/// @file glm/ext/matrix_int3x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x4 GLM_EXT_matrix_int3x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x4 + /// @{ + + /// Signed integer 3x4 matrix. + /// + /// @see ext_matrix_int3x4 + typedef mat<3, 4, int, defaultp> imat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int3x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_int3x4_sized.hpp new file mode 100644 index 0000000..692c48c --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int3x4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int3x4_sized +/// @file glm/ext/matrix_int3x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int3x4_sized GLM_EXT_matrix_int3x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x4.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int3x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int3x4_sized + /// @{ + + /// 8 bit signed integer 3x4 matrix. + /// + /// @see ext_matrix_int3x4_sized + typedef mat<3, 4, int8, defaultp> i8mat3x4; + + /// 16 bit signed integer 3x4 matrix. + /// + /// @see ext_matrix_int3x4_sized + typedef mat<3, 4, int16, defaultp> i16mat3x4; + + /// 32 bit signed integer 3x4 matrix. + /// + /// @see ext_matrix_int3x4_sized + typedef mat<3, 4, int32, defaultp> i32mat3x4; + + /// 64 bit signed integer 3x4 matrix. + /// + /// @see ext_matrix_int3x4_sized + typedef mat<3, 4, int64, defaultp> i64mat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x2.hpp b/thirdparty/glm/glm/ext/matrix_int4x2.hpp new file mode 100644 index 0000000..f756ef2 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x2.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int4x2 +/// @file glm/ext/matrix_int4x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x2 GLM_EXT_matrix_int4x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x2 + /// @{ + + /// Signed integer 4x2 matrix. + /// + /// @see ext_matrix_int4x2 + typedef mat<4, 2, int, defaultp> imat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_int4x2_sized.hpp new file mode 100644 index 0000000..63a99d6 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int4x2_sized +/// @file glm/ext/matrix_int4x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x2_sized GLM_EXT_matrix_int4x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x2.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x2_sized + /// @{ + + /// 8 bit signed integer 4x2 matrix. + /// + /// @see ext_matrix_int4x2_sized + typedef mat<4, 2, int8, defaultp> i8mat4x2; + + /// 16 bit signed integer 4x2 matrix. + /// + /// @see ext_matrix_int4x2_sized + typedef mat<4, 2, int16, defaultp> i16mat4x2; + + /// 32 bit signed integer 4x2 matrix. + /// + /// @see ext_matrix_int4x2_sized + typedef mat<4, 2, int32, defaultp> i32mat4x2; + + /// 64 bit signed integer 4x2 matrix. + /// + /// @see ext_matrix_int4x2_sized + typedef mat<4, 2, int64, defaultp> i64mat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x3.hpp b/thirdparty/glm/glm/ext/matrix_int4x3.hpp new file mode 100644 index 0000000..d5d97a7 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x3.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_int4x3 +/// @file glm/ext/matrix_int4x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x3 GLM_EXT_matrix_int4x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x3 + /// @{ + + /// Signed integer 4x3 matrix. + /// + /// @see ext_matrix_int4x3 + typedef mat<4, 3, int, defaultp> imat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_int4x3_sized.hpp new file mode 100644 index 0000000..55078fa --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_int4x3_sized +/// @file glm/ext/matrix_int4x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x3_sized GLM_EXT_matrix_int4x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x3.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x3_sized + /// @{ + + /// 8 bit signed integer 4x3 matrix. + /// + /// @see ext_matrix_int4x3_sized + typedef mat<4, 3, int8, defaultp> i8mat4x3; + + /// 16 bit signed integer 4x3 matrix. + /// + /// @see ext_matrix_int4x3_sized + typedef mat<4, 3, int16, defaultp> i16mat4x3; + + /// 32 bit signed integer 4x3 matrix. + /// + /// @see ext_matrix_int4x3_sized + typedef mat<4, 3, int32, defaultp> i32mat4x3; + + /// 64 bit signed integer 4x3 matrix. + /// + /// @see ext_matrix_int4x3_sized + typedef mat<4, 3, int64, defaultp> i64mat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x4.hpp b/thirdparty/glm/glm/ext/matrix_int4x4.hpp new file mode 100644 index 0000000..e17cff1 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x4.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_int4x4 +/// @file glm/ext/matrix_int4x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x4 GLM_EXT_matrix_int4x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x4 + /// @{ + + /// Signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4 + typedef mat<4, 4, int, defaultp> imat4x4; + + /// Signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4 + typedef mat<4, 4, int, defaultp> imat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_int4x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_int4x4_sized.hpp new file mode 100644 index 0000000..4a11203 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_int4x4_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_int4x4_sized +/// @file glm/ext/matrix_int4x4_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_int4x4_sized GLM_EXT_matrix_int4x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x4.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_int4x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_int4x4_sized + /// @{ + + /// 8 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int8, defaultp> i8mat4x4; + + /// 16 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int16, defaultp> i16mat4x4; + + /// 32 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int32, defaultp> i32mat4x4; + + /// 64 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int64, defaultp> i64mat4x4; + + + /// 8 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int8, defaultp> i8mat4; + + /// 16 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int16, defaultp> i16mat4; + + /// 32 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int32, defaultp> i32mat4; + + /// 64 bit signed integer 4x4 matrix. + /// + /// @see ext_matrix_int4x4_sized + typedef mat<4, 4, int64, defaultp> i64mat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_integer.hpp b/thirdparty/glm/glm/ext/matrix_integer.hpp new file mode 100644 index 0000000..7d7dfc5 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_integer.hpp @@ -0,0 +1,91 @@ +/// @ref ext_matrix_integer +/// @file glm/ext/matrix_integer.hpp +/// +/// @defgroup ext_matrix_integer GLM_EXT_matrix_integer +/// @ingroup ext +/// +/// Defines functions that generate common transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_projection +/// @see ext_matrix_clip_space + +#pragma once + +// Dependencies +#include "../gtc/constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_integer + /// @{ + + /// Multiply matrix x by matrix y component-wise, i.e., + /// result[i][j] is the scalar product of x[i][j] and y[i][j]. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL matrixCompMult man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL mat matrixCompMult(mat const& x, mat const& y); + + /// Treats the first parameter c as a column vector + /// and the second parameter r as a row vector + /// and does a linear algebraic matrix multiply c * r. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL outerProduct man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename detail::outerProduct_trait::type outerProduct(vec const& c, vec const& r); + + /// Returns the transposed matrix of x + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL transpose man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename mat::transpose_type transpose(mat const& x); + + /// Return the determinant of a squared matrix. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL determinant man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL T determinant(mat const& m); + + /// @} +}//namespace glm + +#include "matrix_integer.inl" diff --git a/thirdparty/glm/glm/ext/matrix_integer.inl b/thirdparty/glm/glm/ext/matrix_integer.inl new file mode 100644 index 0000000..8b377ce --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_integer.inl @@ -0,0 +1,38 @@ +namespace glm{ +namespace detail +{ + template + struct compute_matrixCompMult_type { + GLM_FUNC_QUALIFIER static mat call(mat const& x, mat const& y) + { + return detail::compute_matrixCompMult::value>::call(x, y); + } + }; + + template + struct compute_outerProduct_type { + GLM_FUNC_QUALIFIER static typename detail::outerProduct_trait::type call(vec const& c, vec const& r) + { + return detail::compute_outerProduct::call(c, r); + } + }; + + template + struct compute_transpose_type + { + GLM_FUNC_QUALIFIER static mat call(mat const& m) + { + return detail::compute_transpose::value>::call(m); + } + }; + + template + struct compute_determinant_type{ + + GLM_FUNC_QUALIFIER static T call(mat const& m) + { + return detail::compute_determinant::value>::call(m); + } + }; +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_projection.hpp b/thirdparty/glm/glm/ext/matrix_projection.hpp new file mode 100644 index 0000000..51fd01b --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_projection.hpp @@ -0,0 +1,149 @@ +/// @ref ext_matrix_projection +/// @file glm/ext/matrix_projection.hpp +/// +/// @defgroup ext_matrix_projection GLM_EXT_matrix_projection +/// @ingroup ext +/// +/// Functions that generate common projection transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_transform +/// @see ext_matrix_clip_space + +#pragma once + +// Dependencies +#include "../gtc/constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_projection extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_projection + /// @{ + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> projectZO( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> projectNO( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified object coordinates (obj.x, obj.y, obj.z) into window coordinates using default near and far clip planes definition. + /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param obj Specify the object coordinates. + /// @param model Specifies the current modelview matrix + /// @param proj Specifies the current projection matrix + /// @param viewport Specifies the current viewport + /// @return Return the computed window coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluProject man page + template + GLM_FUNC_DECL vec<3, T, Q> project( + vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of 0 and +1 respectively. (Direct3D clip volume definition) + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProjectZO( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates. + /// The near and far clip planes correspond to z normalized device coordinates of -1 and +1 respectively. (OpenGL clip volume definition) + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProjectNO( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Map the specified window coordinates (win.x, win.y, win.z) into object coordinates using default near and far clip planes definition. + /// To change default near and far clip planes definition use GLM_FORCE_DEPTH_ZERO_TO_ONE. + /// + /// @param win Specify the window coordinates to be mapped. + /// @param model Specifies the modelview matrix + /// @param proj Specifies the projection matrix + /// @param viewport Specifies the viewport + /// @return Returns the computed object coordinates. + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluUnProject man page + template + GLM_FUNC_DECL vec<3, T, Q> unProject( + vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport); + + /// Define a picking region + /// + /// @param center Specify the center of a picking region in window coordinates. + /// @param delta Specify the width and height, respectively, of the picking region in window coordinates. + /// @param viewport Rendering viewport + /// @tparam T Native type used for the computation. Currently supported: half (not recommended), float or double. + /// @tparam U Currently supported: Floating-point types and integer types. + /// + /// @see gluPickMatrix man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> pickMatrix( + vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport); + + /// @} +}//namespace glm + +#include "matrix_projection.inl" diff --git a/thirdparty/glm/glm/ext/matrix_projection.inl b/thirdparty/glm/glm/ext/matrix_projection.inl new file mode 100644 index 0000000..2f2c196 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_projection.inl @@ -0,0 +1,106 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> projectZO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast(1)); + tmp = model * tmp; + tmp = proj * tmp; + + tmp /= tmp.w; + tmp.x = tmp.x * static_cast(0.5) + static_cast(0.5); + tmp.y = tmp.y * static_cast(0.5) + static_cast(0.5); + + tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]); + tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]); + + return vec<3, T, Q>(tmp); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> projectNO(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + vec<4, T, Q> tmp = vec<4, T, Q>(obj, static_cast(1)); + tmp = model * tmp; + tmp = proj * tmp; + + tmp /= tmp.w; + tmp = tmp * static_cast(0.5) + static_cast(0.5); + tmp[0] = tmp[0] * T(viewport[2]) + T(viewport[0]); + tmp[1] = tmp[1] * T(viewport[3]) + T(viewport[1]); + + return vec<3, T, Q>(tmp); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> project(vec<3, T, Q> const& obj, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return projectZO(obj, model, proj, viewport); +# else + return projectNO(obj, model, proj, viewport); +# endif + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProjectZO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + mat<4, 4, T, Q> Inverse = inverse(proj * model); + + vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1)); + tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]); + tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]); + tmp.x = tmp.x * static_cast(2) - static_cast(1); + tmp.y = tmp.y * static_cast(2) - static_cast(1); + + vec<4, T, Q> obj = Inverse * tmp; + obj /= obj.w; + + return vec<3, T, Q>(obj); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProjectNO(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { + mat<4, 4, T, Q> Inverse = inverse(proj * model); + + vec<4, T, Q> tmp = vec<4, T, Q>(win, T(1)); + tmp.x = (tmp.x - T(viewport[0])) / T(viewport[2]); + tmp.y = (tmp.y - T(viewport[1])) / T(viewport[3]); + tmp = tmp * static_cast(2) - static_cast(1); + + vec<4, T, Q> obj = Inverse * tmp; + obj /= obj.w; + + return vec<3, T, Q>(obj); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unProject(vec<3, T, Q> const& win, mat<4, 4, T, Q> const& model, mat<4, 4, T, Q> const& proj, vec<4, U, Q> const& viewport) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_ZO_BIT + return unProjectZO(win, model, proj, viewport); +# else + return unProjectNO(win, model, proj, viewport); +# endif + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> pickMatrix(vec<2, T, Q> const& center, vec<2, T, Q> const& delta, vec<4, U, Q> const& viewport) + { + assert(delta.x > static_cast(0) && delta.y > static_cast(0)); + mat<4, 4, T, Q> Result(static_cast(1)); + + if(!(delta.x > static_cast(0) && delta.y > static_cast(0))) + return Result; // Error + + vec<3, T, Q> Temp( + (static_cast(viewport[2]) - static_cast(2) * (center.x - static_cast(viewport[0]))) / delta.x, + (static_cast(viewport[3]) - static_cast(2) * (center.y - static_cast(viewport[1]))) / delta.y, + static_cast(0)); + + // Translate and scale the picked region to the entire window + Result = translate(Result, Temp); + return scale(Result, vec<3, T, Q>(static_cast(viewport[2]) / delta.x, static_cast(viewport[3]) / delta.y, static_cast(1))); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_relational.hpp b/thirdparty/glm/glm/ext/matrix_relational.hpp new file mode 100644 index 0000000..20023ad --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_relational.hpp @@ -0,0 +1,132 @@ +/// @ref ext_matrix_relational +/// @file glm/ext/matrix_relational.hpp +/// +/// @defgroup ext_matrix_relational GLM_EXT_matrix_relational +/// @ingroup ext +/// +/// Exposes comparison functions for matrix types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_relational +/// @see ext_scalar_relational +/// @see ext_quaternion_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_relational + /// @{ + + /// Perform a component-wise equal-to comparison of two matrices. + /// Return a boolean vector which components value is True if this expression is satisfied per column of the matrices. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y); + + /// Perform a component-wise not-equal-to comparison of two matrices. + /// Return a boolean vector which components value is True if this expression is satisfied per column of the matrices. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, vec const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, vec const& epsilon); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(mat const& x, mat const& y, vec const& ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number of columns of the matrix + /// @tparam R Integer between 1 and 4 included that qualify the number of rows of the matrix + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(mat const& x, mat const& y, vec const& ULPs); + + /// @} +}//namespace glm + +#include "matrix_relational.inl" diff --git a/thirdparty/glm/glm/ext/matrix_relational.inl b/thirdparty/glm/glm/ext/matrix_relational.inl new file mode 100644 index 0000000..9cd42b7 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_relational.inl @@ -0,0 +1,88 @@ +/// @ref ext_vector_relational +/// @file glm/ext/vector_relational.inl + +// Dependency: +#include "../ext/vector_relational.hpp" +#include "../common.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = all(equal(a[i], b[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, T Epsilon) + { + return equal(a, b, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, vec const& Epsilon) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = all(equal(a[i], b[i], Epsilon[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = any(notEqual(a[i], b[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, T Epsilon) + { + return notEqual(a, b, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, vec const& Epsilon) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = any(notEqual(a[i], b[i], Epsilon[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, int MaxULPs) + { + return equal(a, b, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(mat const& a, mat const& b, vec const& MaxULPs) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = all(equal(a[i], b[i], MaxULPs[i])); + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, int MaxULPs) + { + return notEqual(a, b, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(mat const& a, mat const& b, vec const& MaxULPs) + { + vec Result(true); + for(length_t i = 0; i < C; ++i) + Result[i] = any(notEqual(a[i], b[i], MaxULPs[i])); + return Result; + } + +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_transform.hpp b/thirdparty/glm/glm/ext/matrix_transform.hpp new file mode 100644 index 0000000..52695b8 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_transform.hpp @@ -0,0 +1,171 @@ +/// @ref ext_matrix_transform +/// @file glm/ext/matrix_transform.hpp +/// +/// @defgroup ext_matrix_transform GLM_EXT_matrix_transform +/// @ingroup ext +/// +/// Defines functions that generate common transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. +/// +/// Include to use the features of this extension. +/// +/// @see ext_matrix_projection +/// @see ext_matrix_clip_space + +#pragma once + +// Dependencies +#include "../gtc/constants.hpp" +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_transform + /// @{ + + /// Builds an identity matrix. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType identity(); + + /// Builds a translation 4 * 4 matrix created from a vector of 3 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a translation vector. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @code + /// #include + /// #include + /// ... + /// glm::mat4 m = glm::translate(glm::mat4(1.0f), glm::vec3(1.0f)); + /// // m[0][0] == 1.0f, m[0][1] == 0.0f, m[0][2] == 0.0f, m[0][3] == 0.0f + /// // m[1][0] == 0.0f, m[1][1] == 1.0f, m[1][2] == 0.0f, m[1][3] == 0.0f + /// // m[2][0] == 0.0f, m[2][1] == 0.0f, m[2][2] == 1.0f, m[2][3] == 0.0f + /// // m[3][0] == 1.0f, m[3][1] == 1.0f, m[3][2] == 1.0f, m[3][3] == 1.0f + /// @endcode + /// + /// @see - translate(mat<4, 4, T, Q> const& m, T x, T y, T z) + /// @see - translate(vec<3, T, Q> const& v) + /// @see glTranslate man page + template + GLM_FUNC_DECL GLM_CONSTEXPR mat<4, 4, T, Q> translate( + mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v); + + /// Builds a rotation 4 * 4 matrix created from an axis vector and an angle. + /// + /// @param m Input matrix multiplied by this rotation matrix. + /// @param angle Rotation angle expressed in radians. + /// @param axis Rotation axis, recommended to be normalized. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - rotate(mat<4, 4, T, Q> const& m, T angle, T x, T y, T z) + /// @see - rotate(T angle, vec<3, T, Q> const& v) + /// @see glRotate man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotate( + mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& axis); + + /// Builds a scale 4 * 4 matrix created from 3 scalars. + /// + /// @param m Input matrix multiplied by this scale matrix. + /// @param v Ratio of scaling for each axis. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - scale(mat<4, 4, T, Q> const& m, T x, T y, T z) + /// @see - scale(vec<3, T, Q> const& v) + /// @see glScale man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> scale( + mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v); + + /// Builds a scale 4 * 4 matrix created from point referent 3 shearers. + /// + /// @param m Input matrix multiplied by this shear matrix. + /// @param p Point of shearing as reference. + /// @param l_x Ratio of matrix.x projection in YZ plane relative to the y-axis/z-axis. + /// @param l_y Ratio of matrix.y projection in XZ plane relative to the x-axis/z-axis. + /// @param l_z Ratio of matrix.z projection in XY plane relative to the x-axis/y-axis. + /// + /// as example: + /// [1 , l_xy, l_xz, -(l_xy+l_xz) * p_x] [x] T + /// [x`, y`, z`, w`] = [x`, y`, z`, w`] * [l_yx, 1 , l_yz, -(l_yx+l_yz) * p_y] [y] + /// [l_zx, l_zy, 1 , -(l_zx+l_zy) * p_z] [z] + /// [0 , 0 , 0 , 1 ] [w] + /// + /// @tparam T A floating-point shear type + /// @tparam Q A value from qualifier enum + /// + /// @see - shear(mat<4, 4, T, Q> const& m, T x, T y, T z) + /// @see - shear(vec<3, T, Q> const& p) + /// @see - shear(vec<2, T, Q> const& l_x) + /// @see - shear(vec<2, T, Q> const& l_y) + /// @see - shear(vec<2, T, Q> const& l_z) + /// @see no resource... + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shear( + mat<4, 4, T, Q> const &m, vec<3, T, Q> const& p, vec<2, T, Q> const &l_x, vec<2, T, Q> const &l_y, vec<2, T, Q> const &l_z); + + /// Build a right handed look at view matrix. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAtRH( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// Build a left handed look at view matrix. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAtLH( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// Build a look at view matrix based on the default handedness. + /// + /// @param eye Position of the camera + /// @param center Position where the camera is looking at + /// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1) + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) frustum(T const& left, T const& right, T const& bottom, T const& top, T const& nearVal, T const& farVal) + /// @see gluLookAt man page + template + GLM_FUNC_DECL mat<4, 4, T, Q> lookAt( + vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up); + + /// @} +}//namespace glm + +#include "matrix_transform.inl" diff --git a/thirdparty/glm/glm/ext/matrix_transform.inl b/thirdparty/glm/glm/ext/matrix_transform.inl new file mode 100644 index 0000000..029ef0f --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_transform.inl @@ -0,0 +1,207 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType identity() + { + return detail::init_gentype::GENTYPE>::identity(); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR mat<4, 4, T, Q> translate(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result(m); + Result[3] = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + vec<3, T, Q> axis(normalize(v)); + vec<3, T, Q> temp((T(1) - c) * axis); + + mat<4, 4, T, Q> Rotate; + Rotate[0][0] = c + temp[0] * axis[0]; + Rotate[0][1] = temp[0] * axis[1] + s * axis[2]; + Rotate[0][2] = temp[0] * axis[2] - s * axis[1]; + + Rotate[1][0] = temp[1] * axis[0] - s * axis[2]; + Rotate[1][1] = c + temp[1] * axis[1]; + Rotate[1][2] = temp[1] * axis[2] + s * axis[0]; + + Rotate[2][0] = temp[2] * axis[0] + s * axis[1]; + Rotate[2][1] = temp[2] * axis[1] - s * axis[0]; + Rotate[2][2] = c + temp[2] * axis[2]; + + mat<4, 4, T, Q> Result; + Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate_slow(mat<4, 4, T, Q> const& m, T angle, vec<3, T, Q> const& v) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + mat<4, 4, T, Q> Result; + + vec<3, T, Q> axis = normalize(v); + + Result[0][0] = c + (static_cast(1) - c) * axis.x * axis.x; + Result[0][1] = (static_cast(1) - c) * axis.x * axis.y + s * axis.z; + Result[0][2] = (static_cast(1) - c) * axis.x * axis.z - s * axis.y; + Result[0][3] = static_cast(0); + + Result[1][0] = (static_cast(1) - c) * axis.y * axis.x - s * axis.z; + Result[1][1] = c + (static_cast(1) - c) * axis.y * axis.y; + Result[1][2] = (static_cast(1) - c) * axis.y * axis.z + s * axis.x; + Result[1][3] = static_cast(0); + + Result[2][0] = (static_cast(1) - c) * axis.z * axis.x + s * axis.y; + Result[2][1] = (static_cast(1) - c) * axis.z * axis.y - s * axis.x; + Result[2][2] = c + (static_cast(1) - c) * axis.z * axis.z; + Result[2][3] = static_cast(0); + + Result[3] = vec<4, T, Q>(0, 0, 0, 1); + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result; + Result[0] = m[0] * v[0]; + Result[1] = m[1] * v[1]; + Result[2] = m[2] * v[2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale_slow(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& v) + { + mat<4, 4, T, Q> Result(T(1)); + Result[0][0] = v.x; + Result[1][1] = v.y; + Result[2][2] = v.z; + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shear(mat<4, 4, T, Q> const &m, vec<3, T, Q> const& p, vec<2, T, Q> const &l_x, vec<2, T, Q> const &l_y, vec<2, T, Q> const &l_z) + { + T const lambda_xy = l_x[0]; + T const lambda_xz = l_x[1]; + T const lambda_yx = l_y[0]; + T const lambda_yz = l_y[1]; + T const lambda_zx = l_z[0]; + T const lambda_zy = l_z[1]; + + vec<3, T, Q> point_lambda = vec<3, T, Q>( + (lambda_xy + lambda_xz), (lambda_yx + lambda_yz), (lambda_zx + lambda_zy) + ); + + mat<4, 4, T, Q> Shear = mat<4, 4, T, Q>( + 1 , lambda_yx , lambda_zx , 0, + lambda_xy , 1 , lambda_zy , 0, + lambda_xz , lambda_yz , 1 , 0, + -point_lambda[0] * p[0], -point_lambda[1] * p[1], -point_lambda[2] * p[2], 1 + ); + + mat<4, 4, T, Q> Result; + Result[0] = m[0] * Shear[0][0] + m[1] * Shear[0][1] + m[2] * Shear[0][2] + m[3] * Shear[0][3]; + Result[1] = m[0] * Shear[1][0] + m[1] * Shear[1][1] + m[2] * Shear[1][2] + m[3] * Shear[1][3]; + Result[2] = m[0] * Shear[2][0] + m[1] * Shear[2][1] + m[2] * Shear[2][2] + m[3] * Shear[2][3]; + Result[3] = m[0] * Shear[3][0] + m[1] * Shear[3][1] + m[2] * Shear[3][2] + m[3] * Shear[3][3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shear_slow(mat<4, 4, T, Q> const &m, vec<3, T, Q> const& p, vec<2, T, Q> const &l_x, vec<2, T, Q> const &l_y, vec<2, T, Q> const &l_z) + { + T const lambda_xy = static_cast(l_x[0]); + T const lambda_xz = static_cast(l_x[1]); + T const lambda_yx = static_cast(l_y[0]); + T const lambda_yz = static_cast(l_y[1]); + T const lambda_zx = static_cast(l_z[0]); + T const lambda_zy = static_cast(l_z[1]); + + vec<3, T, Q> point_lambda = vec<3, T, Q>( + static_cast(lambda_xy + lambda_xz), + static_cast(lambda_yx + lambda_yz), + static_cast(lambda_zx + lambda_zy) + ); + + mat<4, 4, T, Q> Shear = mat<4, 4, T, Q>( + 1 , lambda_yx , lambda_zx , 0, + lambda_xy , 1 , lambda_zy , 0, + lambda_xz , lambda_yz , 1 , 0, + -point_lambda[0] * p[0], -point_lambda[1] * p[1], -point_lambda[2] * p[2], 1 + ); + return m * Shear; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtRH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { + vec<3, T, Q> const f(normalize(center - eye)); + vec<3, T, Q> const s(normalize(cross(f, up))); + vec<3, T, Q> const u(cross(s, f)); + + mat<4, 4, T, Q> Result(1); + Result[0][0] = s.x; + Result[1][0] = s.y; + Result[2][0] = s.z; + Result[0][1] = u.x; + Result[1][1] = u.y; + Result[2][1] = u.z; + Result[0][2] =-f.x; + Result[1][2] =-f.y; + Result[2][2] =-f.z; + Result[3][0] =-dot(s, eye); + Result[3][1] =-dot(u, eye); + Result[3][2] = dot(f, eye); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAtLH(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { + vec<3, T, Q> const f(normalize(center - eye)); + vec<3, T, Q> const s(normalize(cross(up, f))); + vec<3, T, Q> const u(cross(f, s)); + + mat<4, 4, T, Q> Result(1); + Result[0][0] = s.x; + Result[1][0] = s.y; + Result[2][0] = s.z; + Result[0][1] = u.x; + Result[1][1] = u.y; + Result[2][1] = u.z; + Result[0][2] = f.x; + Result[1][2] = f.y; + Result[2][2] = f.z; + Result[3][0] = -dot(s, eye); + Result[3][1] = -dot(u, eye); + Result[3][2] = -dot(f, eye); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> lookAt(vec<3, T, Q> const& eye, vec<3, T, Q> const& center, vec<3, T, Q> const& up) + { +# if (GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT) + return lookAtLH(eye, center, up); +# else + return lookAtRH(eye, center, up); +# endif + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x2.hpp b/thirdparty/glm/glm/ext/matrix_uint2x2.hpp new file mode 100644 index 0000000..034771a --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x2.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_uint2x2 +/// @file glm/ext/matrix_uint2x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x2 GLM_EXT_matrix_uint2x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x2 + /// @{ + + /// Unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2 + typedef mat<2, 2, uint, defaultp> umat2x2; + + /// Unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2 + typedef mat<2, 2, uint, defaultp> umat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint2x2_sized.hpp new file mode 100644 index 0000000..4555324 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x2_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_uint2x2_sized +/// @file glm/ext/matrix_uint2x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x2_sized GLM_EXT_matrix_uint2x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x2_sized + /// @{ + + /// 8 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint8, defaultp> u8mat2x2; + + /// 16 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint16, defaultp> u16mat2x2; + + /// 32 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint32, defaultp> u32mat2x2; + + /// 64 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint64, defaultp> u64mat2x2; + + + /// 8 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint8, defaultp> u8mat2; + + /// 16 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint16, defaultp> u16mat2; + + /// 32 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint32, defaultp> u32mat2; + + /// 64 bit unsigned integer 2x2 matrix. + /// + /// @see ext_matrix_uint2x2_sized + typedef mat<2, 2, uint64, defaultp> u64mat2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x3.hpp b/thirdparty/glm/glm/ext/matrix_uint2x3.hpp new file mode 100644 index 0000000..f496c53 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x3.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint2x3 +/// @file glm/ext/matrix_uint2x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x3 GLM_EXT_matrix_uint2x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x3 + /// @{ + + /// Unsigned integer 2x3 matrix. + /// + /// @see ext_matrix_uint2x3 + typedef mat<2, 3, uint, defaultp> umat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint2x3_sized.hpp new file mode 100644 index 0000000..db7939c --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint2x3_sized +/// @file glm/ext/matrix_uint2x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x3_sized GLM_EXT_matrix_uint2x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x3.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x3_sized + /// @{ + + /// 8 bit unsigned integer 2x3 matrix. + /// + /// @see ext_matrix_uint2x3_sized + typedef mat<2, 3, uint8, defaultp> u8mat2x3; + + /// 16 bit unsigned integer 2x3 matrix. + /// + /// @see ext_matrix_uint2x3_sized + typedef mat<2, 3, uint16, defaultp> u16mat2x3; + + /// 32 bit unsigned integer 2x3 matrix. + /// + /// @see ext_matrix_uint2x3_sized + typedef mat<2, 3, uint32, defaultp> u32mat2x3; + + /// 64 bit unsigned integer 2x3 matrix. + /// + /// @see ext_matrix_uint2x3_sized + typedef mat<2, 3, uint64, defaultp> u64mat2x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x4.hpp b/thirdparty/glm/glm/ext/matrix_uint2x4.hpp new file mode 100644 index 0000000..0f99350 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x4.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint2x4 +/// @file glm/ext/matrix_uint2x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x4 GLM_EXT_matrix_int2x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x4 + /// @{ + + /// Unsigned integer 2x4 matrix. + /// + /// @see ext_matrix_uint2x4 + typedef mat<2, 4, uint, defaultp> umat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint2x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint2x4_sized.hpp new file mode 100644 index 0000000..5c55547 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint2x4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint2x4_sized +/// @file glm/ext/matrix_uint2x4_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint2x4_sized GLM_EXT_matrix_uint2x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x4.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint2x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint2x4_sized + /// @{ + + /// 8 bit unsigned integer 2x4 matrix. + /// + /// @see ext_matrix_uint2x4_sized + typedef mat<2, 4, uint8, defaultp> u8mat2x4; + + /// 16 bit unsigned integer 2x4 matrix. + /// + /// @see ext_matrix_uint2x4_sized + typedef mat<2, 4, uint16, defaultp> u16mat2x4; + + /// 32 bit unsigned integer 2x4 matrix. + /// + /// @see ext_matrix_uint2x4_sized + typedef mat<2, 4, uint32, defaultp> u32mat2x4; + + /// 64 bit unsigned integer 2x4 matrix. + /// + /// @see ext_matrix_uint2x4_sized + typedef mat<2, 4, uint64, defaultp> u64mat2x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x2.hpp b/thirdparty/glm/glm/ext/matrix_uint3x2.hpp new file mode 100644 index 0000000..55a9bed --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x2.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint3x2 +/// @file glm/ext/matrix_uint3x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x2 GLM_EXT_matrix_uint3x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x2 + /// @{ + + /// Unsigned integer 3x2 matrix. + /// + /// @see ext_matrix_uint3x2 + typedef mat<3, 2, uint, defaultp> umat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint3x2_sized.hpp new file mode 100644 index 0000000..c81af8f --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint3x2_sized +/// @file glm/ext/matrix_uint3x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x2_sized GLM_EXT_matrix_uint3x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x2.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x2_sized + /// @{ + + /// 8 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_uint3x2_sized + typedef mat<3, 2, uint8, defaultp> u8mat3x2; + + /// 16 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_uint3x2_sized + typedef mat<3, 2, uint16, defaultp> u16mat3x2; + + /// 32 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_uint3x2_sized + typedef mat<3, 2, uint32, defaultp> u32mat3x2; + + /// 64 bit signed integer 3x2 matrix. + /// + /// @see ext_matrix_uint3x2_sized + typedef mat<3, 2, uint64, defaultp> u64mat3x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x3.hpp b/thirdparty/glm/glm/ext/matrix_uint3x3.hpp new file mode 100644 index 0000000..1004c0d --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x3.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_uint3x3 +/// @file glm/ext/matrix_uint3x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x3 GLM_EXT_matrix_uint3x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x3 + /// @{ + + /// Unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3 + typedef mat<3, 3, uint, defaultp> umat3x3; + + /// Unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3 + typedef mat<3, 3, uint, defaultp> umat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint3x3_sized.hpp new file mode 100644 index 0000000..41a8be7 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x3_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_uint3x3_sized +/// @file glm/ext/matrix_uint3x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x3_sized GLM_EXT_matrix_uint3x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x3_sized + /// @{ + + /// 8 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint8, defaultp> u8mat3x3; + + /// 16 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint16, defaultp> u16mat3x3; + + /// 32 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint32, defaultp> u32mat3x3; + + /// 64 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint64, defaultp> u64mat3x3; + + + /// 8 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint8, defaultp> u8mat3; + + /// 16 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint16, defaultp> u16mat3; + + /// 32 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint32, defaultp> u32mat3; + + /// 64 bit unsigned integer 3x3 matrix. + /// + /// @see ext_matrix_uint3x3_sized + typedef mat<3, 3, uint64, defaultp> u64mat3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x4.hpp b/thirdparty/glm/glm/ext/matrix_uint3x4.hpp new file mode 100644 index 0000000..c6dd78c --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x4.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint3x4 +/// @file glm/ext/matrix_uint3x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x4 GLM_EXT_matrix_uint3x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x4 + /// @{ + + /// Signed integer 3x4 matrix. + /// + /// @see ext_matrix_uint3x4 + typedef mat<3, 4, uint, defaultp> umat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint3x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint3x4_sized.hpp new file mode 100644 index 0000000..2ce28ad --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint3x4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint3x4_sized +/// @file glm/ext/matrix_uint3x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint3x4_sized GLM_EXT_matrix_uint3x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat3x4.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint3x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint3x4_sized + /// @{ + + /// 8 bit unsigned integer 3x4 matrix. + /// + /// @see ext_matrix_uint3x4_sized + typedef mat<3, 4, uint8, defaultp> u8mat3x4; + + /// 16 bit unsigned integer 3x4 matrix. + /// + /// @see ext_matrix_uint3x4_sized + typedef mat<3, 4, uint16, defaultp> u16mat3x4; + + /// 32 bit unsigned integer 3x4 matrix. + /// + /// @see ext_matrix_uint3x4_sized + typedef mat<3, 4, uint32, defaultp> u32mat3x4; + + /// 64 bit unsigned integer 3x4 matrix. + /// + /// @see ext_matrix_uint3x4_sized + typedef mat<3, 4, uint64, defaultp> u64mat3x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x2.hpp b/thirdparty/glm/glm/ext/matrix_uint4x2.hpp new file mode 100644 index 0000000..0446f57 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x2.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint4x2 +/// @file glm/ext/matrix_uint4x2.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x2 GLM_EXT_matrix_uint4x2 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x2.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x2 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x2 + /// @{ + + /// Unsigned integer 4x2 matrix. + /// + /// @see ext_matrix_uint4x2 + typedef mat<4, 2, uint, defaultp> umat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x2_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint4x2_sized.hpp new file mode 100644 index 0000000..57a66bf --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint4x2_sized +/// @file glm/ext/matrix_uint4x2_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x2_sized GLM_EXT_matrix_uint4x2_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x2.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x2_sized + /// @{ + + /// 8 bit unsigned integer 4x2 matrix. + /// + /// @see ext_matrix_uint4x2_sized + typedef mat<4, 2, uint8, defaultp> u8mat4x2; + + /// 16 bit unsigned integer 4x2 matrix. + /// + /// @see ext_matrix_uint4x2_sized + typedef mat<4, 2, uint16, defaultp> u16mat4x2; + + /// 32 bit unsigned integer 4x2 matrix. + /// + /// @see ext_matrix_uint4x2_sized + typedef mat<4, 2, uint32, defaultp> u32mat4x2; + + /// 64 bit unsigned integer 4x2 matrix. + /// + /// @see ext_matrix_uint4x2_sized + typedef mat<4, 2, uint64, defaultp> u64mat4x2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x3.hpp b/thirdparty/glm/glm/ext/matrix_uint4x3.hpp new file mode 100644 index 0000000..54c24e4 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x3.hpp @@ -0,0 +1,33 @@ +/// @ref ext_matrix_uint4x3 +/// @file glm/ext/matrix_uint4x3.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x3 GLM_EXT_matrix_uint4x3 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x3.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x3 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x3 + /// @{ + + /// Unsigned integer 4x3 matrix. + /// + /// @see ext_matrix_uint4x3 + typedef mat<4, 3, uint, defaultp> umat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x3_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint4x3_sized.hpp new file mode 100644 index 0000000..2e61124 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_matrix_uint4x3_sized +/// @file glm/ext/matrix_uint4x3_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x3_sized GLM_EXT_matrix_uint4x3_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x3.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x3_sized + /// @{ + + /// 8 bit unsigned integer 4x3 matrix. + /// + /// @see ext_matrix_uint4x3_sized + typedef mat<4, 3, uint8, defaultp> u8mat4x3; + + /// 16 bit unsigned integer 4x3 matrix. + /// + /// @see ext_matrix_uint4x3_sized + typedef mat<4, 3, uint16, defaultp> u16mat4x3; + + /// 32 bit unsigned integer 4x3 matrix. + /// + /// @see ext_matrix_uint4x3_sized + typedef mat<4, 3, uint32, defaultp> u32mat4x3; + + /// 64 bit unsigned integer 4x3 matrix. + /// + /// @see ext_matrix_uint4x3_sized + typedef mat<4, 3, uint64, defaultp> u64mat4x3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x4.hpp b/thirdparty/glm/glm/ext/matrix_uint4x4.hpp new file mode 100644 index 0000000..5cc8455 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x4.hpp @@ -0,0 +1,38 @@ +/// @ref ext_matrix_uint4x4 +/// @file glm/ext/matrix_uint4x4.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x4 GLM_EXT_matrix_uint4x4 +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x4 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x4 + /// @{ + + /// Unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4 + typedef mat<4, 4, uint, defaultp> umat4x4; + + /// Unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4 + typedef mat<4, 4, uint, defaultp> umat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/matrix_uint4x4_sized.hpp b/thirdparty/glm/glm/ext/matrix_uint4x4_sized.hpp new file mode 100644 index 0000000..bb10bd2 --- /dev/null +++ b/thirdparty/glm/glm/ext/matrix_uint4x4_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_matrix_uint4x4_sized +/// @file glm/ext/matrix_uint4x4_sized.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_matrix_uint4x4_sized GLM_EXT_matrix_uint4x4_sized +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat4x4.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_matrix_uint4x4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_matrix_uint4x4_sized + /// @{ + + /// 8 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint8, defaultp> u8mat4x4; + + /// 16 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint16, defaultp> u16mat4x4; + + /// 32 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint32, defaultp> u32mat4x4; + + /// 64 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint64, defaultp> u64mat4x4; + + + /// 8 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint8, defaultp> u8mat4; + + /// 16 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint16, defaultp> u16mat4; + + /// 32 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint32, defaultp> u32mat4; + + /// 64 bit unsigned integer 4x4 matrix. + /// + /// @see ext_matrix_uint4x4_sized + typedef mat<4, 4, uint64, defaultp> u64mat4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/quaternion_common.hpp b/thirdparty/glm/glm/ext/quaternion_common.hpp new file mode 100644 index 0000000..f738692 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_common.hpp @@ -0,0 +1,135 @@ +/// @ref ext_quaternion_common +/// @file glm/ext/quaternion_common.hpp +/// +/// @defgroup ext_quaternion_common GLM_EXT_quaternion_common +/// @ingroup ext +/// +/// Provides common functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_common +/// @see ext_vector_common +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_trigonometric +/// @see ext_quaternion_transform + +#pragma once + +// Dependency: +#include "../ext/scalar_constants.hpp" +#include "../ext/quaternion_geometric.hpp" +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_common + /// @{ + + /// Spherical linear interpolation of two quaternions. + /// The interpolation is oriented and the rotation is performed at constant speed. + /// For short path spherical linear interpolation, use the slerp function. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + /// + /// @see - slerp(qua const& x, qua const& y, T const& a) + template + GLM_FUNC_DECL qua mix(qua const& x, qua const& y, T a); + + /// Linear interpolation of two quaternions. + /// The interpolation is oriented. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined in the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR qua lerp(qua const& x, qua const& y, T a); + + /// Spherical linear interpolation of two quaternions. + /// The interpolation always take the short path and the rotation is performed at constant speed. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua slerp(qua const& x, qua const& y, T a); + + /// Spherical linear interpolation of two quaternions with multiple spins over rotation axis. + /// The interpolation always take the short path when the spin count is positive and long path + /// when count is negative. Rotation is performed at constant speed. + /// + /// @param x A quaternion + /// @param y A quaternion + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// @param k Additional spin count. If Value is negative interpolation will be on "long" path. + /// + /// @tparam T A floating-point scalar type + /// @tparam S An integer scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua slerp(qua const& x, qua const& y, T a, S k); + + /// Returns the q conjugate. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR qua conjugate(qua const& q); + + /// Returns the q inverse. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR qua inverse(qua const& q); + + /// Returns true if x holds a NaN (not a number) + /// representation in the underlying implementation's set of + /// floating point representations. Returns false otherwise, + /// including for implementations with no NaN + /// representations. + /// + /// /!\ When using compiler fast math, this function may fail. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> isnan(qua const& x); + + /// Returns true if x holds a positive infinity or negative + /// infinity representation in the underlying implementation's + /// set of floating point representations. Returns false + /// otherwise, including for implementations with no infinity + /// representations. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> isinf(qua const& x); + + /// @} +} //namespace glm + +#include "quaternion_common.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_common.inl b/thirdparty/glm/glm/ext/quaternion_common.inl new file mode 100644 index 0000000..ad171f9 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_common.inl @@ -0,0 +1,144 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua mix(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'mix' only accept floating-point inputs"); + + T const cosTheta = dot(x, y); + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if(cosTheta > static_cast(1) - epsilon()) + { + // Linear interpolation + return qua::wxyz( + mix(x.w, y.w, a), + mix(x.x, y.x, a), + mix(x.y, y.y, a), + mix(x.z, y.z, a)); + } + else + { + // Essential Mathematics, page 467 + T angle = acos(cosTheta); + return (sin((static_cast(1) - a) * angle) * x + sin(a * angle) * y) / sin(angle); + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua lerp(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'lerp' only accept floating-point inputs"); + + // Lerp is only defined in [0, 1] + assert(a >= static_cast(0)); + assert(a <= static_cast(1)); + + return x * (static_cast(1) - a) + (y * a); + } + + template + GLM_FUNC_QUALIFIER qua slerp(qua const& x, qua const& y, T a) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'slerp' only accept floating-point inputs"); + + qua z = y; + + T cosTheta = dot(x, y); + + // If cosTheta < 0, the interpolation will take the long way around the sphere. + // To fix this, one quat must be negated. + if(cosTheta < static_cast(0)) + { + z = -y; + cosTheta = -cosTheta; + } + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if(cosTheta > static_cast(1) - epsilon()) + { + // Linear interpolation + return qua::wxyz( + mix(x.w, z.w, a), + mix(x.x, z.x, a), + mix(x.y, z.y, a), + mix(x.z, z.z, a)); + } + else + { + // Essential Mathematics, page 467 + T angle = acos(cosTheta); + return (sin((static_cast(1) - a) * angle) * x + sin(a * angle) * z) / sin(angle); + } + } + + template + GLM_FUNC_QUALIFIER qua slerp(qua const& x, qua const& y, T a, S k) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'slerp' only accept floating-point inputs"); + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'slerp' only accept integer for spin count"); + + qua z = y; + + T cosTheta = dot(x, y); + + // If cosTheta < 0, the interpolation will take the long way around the sphere. + // To fix this, one quat must be negated. + if (cosTheta < static_cast(0)) + { + z = -y; + cosTheta = -cosTheta; + } + + // Perform a linear interpolation when cosTheta is close to 1 to avoid side effect of sin(angle) becoming a zero denominator + if (cosTheta > static_cast(1) - epsilon()) + { + // Linear interpolation + return qua::wxyz( + mix(x.w, z.w, a), + mix(x.x, z.x, a), + mix(x.y, z.y, a), + mix(x.z, z.z, a)); + } + else + { + // Graphics Gems III, page 96 + T angle = acos(cosTheta); + T phi = angle + static_cast(k) * glm::pi(); + return (sin(angle - a * phi)* x + sin(a * phi) * z) / sin(angle); + } + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua conjugate(qua const& q) + { + return qua::wxyz(q.w, -q.x, -q.y, -q.z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua inverse(qua const& q) + { + return conjugate(q) / dot(q, q); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isnan(qua const& q) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isnan' only accept floating-point inputs"); + + return vec<4, bool, Q>(isnan(q.x), isnan(q.y), isnan(q.z), isnan(q.w)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isinf(qua const& q) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isinf' only accept floating-point inputs"); + + return vec<4, bool, Q>(isinf(q.x), isinf(q.y), isinf(q.z), isinf(q.w)); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "quaternion_common_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/ext/quaternion_common_simd.inl b/thirdparty/glm/glm/ext/quaternion_common_simd.inl new file mode 100644 index 0000000..ddfc8a4 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_common_simd.inl @@ -0,0 +1,18 @@ +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +namespace glm{ +namespace detail +{ + template + struct compute_dot, float, true> + { + static GLM_FUNC_QUALIFIER float call(qua const& x, qua const& y) + { + return _mm_cvtss_f32(glm_vec1_dot(x.data, y.data)); + } + }; +}//namespace detail +}//namespace glm + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + diff --git a/thirdparty/glm/glm/ext/quaternion_double.hpp b/thirdparty/glm/glm/ext/quaternion_double.hpp new file mode 100644 index 0000000..63b24de --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_double.hpp @@ -0,0 +1,39 @@ +/// @ref ext_quaternion_double +/// @file glm/ext/quaternion_double.hpp +/// +/// @defgroup ext_quaternion_double GLM_EXT_quaternion_double +/// @ingroup ext +/// +/// Exposes double-precision floating point quaternion type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double_precision +/// @see ext_quaternion_common +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_double extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_double + /// @{ + + /// Quaternion of double-precision floating-point numbers. + typedef qua dquat; + + /// @} +} //namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_double_precision.hpp b/thirdparty/glm/glm/ext/quaternion_double_precision.hpp new file mode 100644 index 0000000..8aa24a1 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_double_precision.hpp @@ -0,0 +1,42 @@ +/// @ref ext_quaternion_double_precision +/// @file glm/ext/quaternion_double_precision.hpp +/// +/// @defgroup ext_quaternion_double_precision GLM_EXT_quaternion_double_precision +/// @ingroup ext +/// +/// Exposes double-precision floating point quaternion type with various precision in term of ULPs. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_double_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_double_precision + /// @{ + + /// Quaternion of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua lowp_dquat; + + /// Quaternion of medium double-qualifier floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua mediump_dquat; + + /// Quaternion of high double-qualifier floating-point numbers using high precision arithmetic in term of ULPs. + /// + /// @see ext_quaternion_double_precision + typedef qua highp_dquat; + + /// @} +} //namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_exponential.hpp b/thirdparty/glm/glm/ext/quaternion_exponential.hpp new file mode 100644 index 0000000..affe297 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_exponential.hpp @@ -0,0 +1,63 @@ +/// @ref ext_quaternion_exponential +/// @file glm/ext/quaternion_exponential.hpp +/// +/// @defgroup ext_quaternion_exponential GLM_EXT_quaternion_exponential +/// @ingroup ext +/// +/// Provides exponential functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see core_exponential +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../geometric.hpp" +#include "../ext/scalar_constants.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_exponential extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_transform + /// @{ + + /// Returns a exponential of a quaternion. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua exp(qua const& q); + + /// Returns a logarithm of a quaternion + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua log(qua const& q); + + /// Returns a quaternion raised to a power. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua pow(qua const& q, T y); + + /// Returns the square root of a quaternion + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua sqrt(qua const& q); + + /// @} +} //namespace glm + +#include "quaternion_exponential.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_exponential.inl b/thirdparty/glm/glm/ext/quaternion_exponential.inl new file mode 100644 index 0000000..8a9d774 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_exponential.inl @@ -0,0 +1,89 @@ +#include "scalar_constants.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua exp(qua const& q) + { + vec<3, T, Q> u(q.x, q.y, q.z); + T const Angle = glm::length(u); + if (Angle < epsilon()) + return qua(); + + vec<3, T, Q> const v(u / Angle); + return qua(cos(Angle), sin(Angle) * v); + } + + template + GLM_FUNC_QUALIFIER qua log(qua const& q) + { + vec<3, T, Q> u(q.x, q.y, q.z); + T Vec3Len = length(u); + + if (Vec3Len < epsilon()) + { + if(q.w > static_cast(0)) + return qua::wxyz(log(q.w), static_cast(0), static_cast(0), static_cast(0)); + else if(q.w < static_cast(0)) + return qua::wxyz(log(-q.w), pi(), static_cast(0), static_cast(0)); + else + return qua::wxyz(std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + else + { + T t = atan(Vec3Len, T(q.w)) / Vec3Len; + T QuatLen2 = Vec3Len * Vec3Len + q.w * q.w; + return qua::wxyz(static_cast(0.5) * log(QuatLen2), t * q.x, t * q.y, t * q.z); + } + } + + template + GLM_FUNC_QUALIFIER qua pow(qua const& x, T y) + { + //Raising to the power of 0 should yield 1 + //Needed to prevent a division by 0 error later on + if(y > -epsilon() && y < epsilon()) + return qua::wxyz(1,0,0,0); + + //To deal with non-unit quaternions + T magnitude = sqrt(x.x * x.x + x.y * x.y + x.z * x.z + x.w *x.w); + + T Angle; + if(abs(x.w / magnitude) > cos_one_over_two()) + { + //Scalar component is close to 1; using it to recover angle would lose precision + //Instead, we use the non-scalar components since sin() is accurate around 0 + + //Prevent a division by 0 error later on + T VectorMagnitude = x.x * x.x + x.y * x.y + x.z * x.z; + //Despite the compiler might say, we actually want to compare + //VectorMagnitude to 0. here; we could use denorm_int() compiling a + //project with unsafe maths optimizations might make the comparison + //always false, even when VectorMagnitude is 0. + if (VectorMagnitude < std::numeric_limits::min()) { + //Equivalent to raising a real number to a power + return qua::wxyz(pow(x.w, y), 0, 0, 0); + } + + Angle = asin(sqrt(VectorMagnitude) / magnitude); + } + else + { + //Scalar component is small, shouldn't cause loss of precision + Angle = acos(x.w / magnitude); + } + + T NewAngle = Angle * y; + T Div = sin(NewAngle) / sin(Angle); + T Mag = pow(magnitude, y - static_cast(1)); + return qua::wxyz(cos(NewAngle) * magnitude * Mag, x.x * Div * Mag, x.y * Div * Mag, x.z * Div * Mag); + } + + template + GLM_FUNC_QUALIFIER qua sqrt(qua const& x) + { + return pow(x, static_cast(0.5)); + } +}//namespace glm + + diff --git a/thirdparty/glm/glm/ext/quaternion_float.hpp b/thirdparty/glm/glm/ext/quaternion_float.hpp new file mode 100644 index 0000000..ca42a60 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_float.hpp @@ -0,0 +1,39 @@ +/// @ref ext_quaternion_float +/// @file glm/ext/quaternion_float.hpp +/// +/// @defgroup ext_quaternion_float GLM_EXT_quaternion_float +/// @ingroup ext +/// +/// Exposes single-precision floating point quaternion type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_double +/// @see ext_quaternion_float_precision +/// @see ext_quaternion_common +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_float extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_float + /// @{ + + /// Quaternion of single-precision floating-point numbers. + typedef qua quat; + + /// @} +} //namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_float_precision.hpp b/thirdparty/glm/glm/ext/quaternion_float_precision.hpp new file mode 100644 index 0000000..f9e4f5c --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_float_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_quaternion_float_precision +/// @file glm/ext/quaternion_float_precision.hpp +/// +/// @defgroup ext_quaternion_float_precision GLM_EXT_quaternion_float_precision +/// @ingroup ext +/// +/// Exposes single-precision floating point quaternion type with various precision in term of ULPs. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependency: +#include "../detail/type_quat.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_float_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_float_precision + /// @{ + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua lowp_quat; + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua mediump_quat; + + /// Quaternion of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef qua highp_quat; + + /// @} +} //namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_geometric.hpp b/thirdparty/glm/glm/ext/quaternion_geometric.hpp new file mode 100644 index 0000000..6a2403f --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_geometric.hpp @@ -0,0 +1,70 @@ +/// @ref ext_quaternion_geometric +/// @file glm/ext/quaternion_geometric.hpp +/// +/// @defgroup ext_quaternion_geometric GLM_EXT_quaternion_geometric +/// @ingroup ext +/// +/// Provides geometric functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see core_func_geometric +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "../ext/vector_relational.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_geometric extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_geometric + /// @{ + + /// Returns the norm of a quaternions + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL T length(qua const& q); + + /// Returns the normalized quaternion. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL qua normalize(qua const& q); + + /// Returns dot product of q1 and q2, i.e., q1[0] * q2[0] + q1[1] * q2[1] + ... + /// + /// @tparam T Floating-point scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_DECL GLM_CONSTEXPR T dot(qua const& x, qua const& y); + + /// Compute a cross product. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_geometric + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua cross(qua const& q1, qua const& q2); + + /// @} +} //namespace glm + +#include "quaternion_geometric.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_geometric.inl b/thirdparty/glm/glm/ext/quaternion_geometric.inl new file mode 100644 index 0000000..88dc4d6 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_geometric.inl @@ -0,0 +1,36 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T dot(qua const& x, qua const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'dot' accepts only floating-point inputs"); + return detail::compute_dot, T, detail::is_aligned::value>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER T length(qua const& q) + { + return glm::sqrt(dot(q, q)); + } + + template + GLM_FUNC_QUALIFIER qua normalize(qua const& q) + { + T len = length(q); + if(len <= static_cast(0)) // Problem + return qua::wxyz(static_cast(1), static_cast(0), static_cast(0), static_cast(0)); + T oneOverLen = static_cast(1) / len; + return qua::wxyz(q.w * oneOverLen, q.x * oneOverLen, q.y * oneOverLen, q.z * oneOverLen); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua cross(qua const& q1, qua const& q2) + { + return qua::wxyz( + q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z, + q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y, + q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z, + q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_relational.hpp b/thirdparty/glm/glm/ext/quaternion_relational.hpp new file mode 100644 index 0000000..7aa121d --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_relational.hpp @@ -0,0 +1,62 @@ +/// @ref ext_quaternion_relational +/// @file glm/ext/quaternion_relational.hpp +/// +/// @defgroup ext_quaternion_relational GLM_EXT_quaternion_relational +/// @ingroup ext +/// +/// Exposes comparison functions for quaternion types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_vector_relational +/// @see ext_matrix_relational +/// @see ext_quaternion_float +/// @see ext_quaternion_double + +#pragma once + +// Dependency: +#include "../vector_relational.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_relational + /// @{ + + /// Returns the component-wise comparison of result x == y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> equal(qua const& x, qua const& y); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> equal(qua const& x, qua const& y, T epsilon); + + /// Returns the component-wise comparison of result x != y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> notEqual(qua const& x, qua const& y); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL vec<4, bool, Q> notEqual(qua const& x, qua const& y, T epsilon); + + /// @} +} //namespace glm + +#include "quaternion_relational.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_relational.inl b/thirdparty/glm/glm/ext/quaternion_relational.inl new file mode 100644 index 0000000..b1713e9 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_relational.inl @@ -0,0 +1,35 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> equal(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] == y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> equal(qua const& x, qua const& y, T epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return lessThan(abs(v), vec<4, T, Q>(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> notEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result; + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] != y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> notEqual(qua const& x, qua const& y, T epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon)); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_transform.hpp b/thirdparty/glm/glm/ext/quaternion_transform.hpp new file mode 100644 index 0000000..a9cc5c2 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_transform.hpp @@ -0,0 +1,47 @@ +/// @ref ext_quaternion_transform +/// @file glm/ext/quaternion_transform.hpp +/// +/// @defgroup ext_quaternion_transform GLM_EXT_quaternion_transform +/// @ingroup ext +/// +/// Provides transformation functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_trigonometric + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../trigonometric.hpp" +#include "../geometric.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_transform + /// @{ + + /// Rotates a quaternion from a vector of 3 components axis and an angle. + /// + /// @param q Source orientation + /// @param angle Angle expressed in radians. + /// @param axis Axis of the rotation + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL qua rotate(qua const& q, T const& angle, vec<3, T, Q> const& axis); + /// @} +} //namespace glm + +#include "quaternion_transform.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_transform.inl b/thirdparty/glm/glm/ext/quaternion_transform.inl new file mode 100644 index 0000000..7e773fb --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_transform.inl @@ -0,0 +1,24 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER qua rotate(qua const& q, T const& angle, vec<3, T, Q> const& v) + { + vec<3, T, Q> Tmp = v; + + // Axis of rotation must be normalised + T len = glm::length(Tmp); + if(abs(len - static_cast(1)) > static_cast(0.001)) + { + T oneOverLen = static_cast(1) / len; + Tmp.x *= oneOverLen; + Tmp.y *= oneOverLen; + Tmp.z *= oneOverLen; + } + + T const AngleRad(angle); + T const Sin = sin(AngleRad * static_cast(0.5)); + + return q * qua::wxyz(cos(AngleRad * static_cast(0.5)), Tmp.x * Sin, Tmp.y * Sin, Tmp.z * Sin); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/ext/quaternion_trigonometric.hpp b/thirdparty/glm/glm/ext/quaternion_trigonometric.hpp new file mode 100644 index 0000000..574a704 --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_trigonometric.hpp @@ -0,0 +1,65 @@ +/// @ref ext_quaternion_trigonometric +/// @file glm/ext/quaternion_trigonometric.hpp +/// +/// @defgroup ext_quaternion_trigonometric GLM_EXT_quaternion_trigonometric +/// @ingroup ext +/// +/// Provides trigonometric functions for quaternion types +/// +/// Include to use the features of this extension. +/// +/// @see ext_quaternion_float +/// @see ext_quaternion_double +/// @see ext_quaternion_exponential +/// @see ext_quaternion_geometric +/// @see ext_quaternion_relational +/// @see ext_quaternion_transform + +#pragma once + +// Dependency: +#include "../trigonometric.hpp" +#include "../exponential.hpp" +#include "scalar_constants.hpp" +#include "vector_relational.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_quaternion_trigonometric extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_quaternion_trigonometric + /// @{ + + /// Returns the quaternion rotation angle. + /// + /// @param x A normalized quaternion. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL T angle(qua const& x); + + /// Returns the q rotation axis. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL vec<3, T, Q> axis(qua const& x); + + /// Build a quaternion from an angle and a normalized axis. + /// + /// @param angle Angle expressed in radians. + /// @param axis Axis of the quaternion, must be normalized. + /// + /// @tparam T A floating-point scalar type + /// @tparam Q A value from qualifier enum + template + GLM_FUNC_DECL qua angleAxis(T const& angle, vec<3, T, Q> const& axis); + + /// @} +} //namespace glm + +#include "quaternion_trigonometric.inl" diff --git a/thirdparty/glm/glm/ext/quaternion_trigonometric.inl b/thirdparty/glm/glm/ext/quaternion_trigonometric.inl new file mode 100644 index 0000000..896449a --- /dev/null +++ b/thirdparty/glm/glm/ext/quaternion_trigonometric.inl @@ -0,0 +1,37 @@ +#include "scalar_constants.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T angle(qua const& x) + { + if (abs(x.w) > cos_one_over_two()) + { + T const a = asin(sqrt(x.x * x.x + x.y * x.y + x.z * x.z)) * static_cast(2); + if(x.w < static_cast(0)) + return pi() * static_cast(2) - a; + return a; + } + + return acos(x.w) * static_cast(2); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> axis(qua const& x) + { + T const tmp1 = static_cast(1) - x.w * x.w; + if(tmp1 <= static_cast(0)) + return vec<3, T, Q>(0, 0, 1); + T const tmp2 = static_cast(1) / sqrt(tmp1); + return vec<3, T, Q>(x.x * tmp2, x.y * tmp2, x.z * tmp2); + } + + template + GLM_FUNC_QUALIFIER qua angleAxis(T const& angle, vec<3, T, Q> const& v) + { + T const a(angle); + T const s = glm::sin(a * static_cast(0.5)); + + return qua(glm::cos(a * static_cast(0.5)), v * s); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_common.hpp b/thirdparty/glm/glm/ext/scalar_common.hpp new file mode 100644 index 0000000..df04b6b --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_common.hpp @@ -0,0 +1,181 @@ +/// @ref ext_scalar_common +/// @file glm/ext/scalar_common.hpp +/// +/// @defgroup ext_scalar_common GLM_EXT_scalar_common +/// @ingroup ext +/// +/// Exposes min and max functions for 3 to 4 scalar parameters. +/// +/// Include to use the features of this extension. +/// +/// @see core_func_common +/// @see ext_vector_common + +#pragma once + +// Dependency: +#include "../common.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_common + /// @{ + + /// Returns the minimum component-wise values of 3 inputs + /// + /// @tparam T A floating-point scalar type. + /// + /// @see ext_scalar_common + template + GLM_FUNC_DECL T min(T a, T b, T c); + + /// Returns the minimum component-wise values of 4 inputs + /// + /// @tparam T A floating-point scalar type. + /// + /// @see ext_scalar_common + template + GLM_FUNC_DECL T min(T a, T b, T c, T d); + + /// Returns the maximum component-wise values of 3 inputs + /// + /// @tparam T A floating-point scalar type. + /// + /// @see ext_scalar_common + template + GLM_FUNC_DECL T max(T a, T b, T c); + + /// Returns the maximum component-wise values of 4 inputs + /// + /// @tparam T A floating-point scalar type. + /// + /// @see ext_scalar_common + template + GLM_FUNC_DECL T max(T a, T b, T c, T d); + + /// Returns the minimum component-wise values of 2 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmin(T a, T b); + + /// Returns the minimum component-wise values of 3 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmin(T a, T b, T c); + + /// Returns the minimum component-wise values of 4 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmin documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmin(T a, T b, T c, T d); + + /// Returns the maximum component-wise values of 2 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmax(T a, T b); + + /// Returns the maximum component-wise values of 3 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmax(T a, T b, T C); + + /// Returns the maximum component-wise values of 4 inputs. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam T A floating-point scalar type. + /// + /// @see std::fmax documentation + /// @see ext_scalar_common + template + GLM_FUNC_DECL T fmax(T a, T b, T C, T D); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam genType Floating-point scalar types. + /// + /// @see ext_scalar_common + template + GLM_FUNC_DECL genType fclamp(genType x, genType minVal, genType maxVal); + + /// Simulate GL_CLAMP OpenGL wrap mode + /// + /// @tparam genType Floating-point scalar types. + /// + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL genType clamp(genType const& Texcoord); + + /// Simulate GL_REPEAT OpenGL wrap mode + /// + /// @tparam genType Floating-point scalar types. + /// + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL genType repeat(genType const& Texcoord); + + /// Simulate GL_MIRRORED_REPEAT OpenGL wrap mode + /// + /// @tparam genType Floating-point scalar types. + /// + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL genType mirrorClamp(genType const& Texcoord); + + /// Simulate GL_MIRROR_REPEAT OpenGL wrap mode + /// + /// @tparam genType Floating-point scalar types. + /// + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL genType mirrorRepeat(genType const& Texcoord); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam genType floating point scalar types. + /// + /// @see GLSL round man page + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL int iround(genType const& x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam genType floating point scalar types. + /// + /// @see GLSL round man page + /// @see ext_scalar_common extension. + template + GLM_FUNC_DECL uint uround(genType const& x); + + /// @} +}//namespace glm + +#include "scalar_common.inl" diff --git a/thirdparty/glm/glm/ext/scalar_common.inl b/thirdparty/glm/glm/ext/scalar_common.inl new file mode 100644 index 0000000..3d09fef --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_common.inl @@ -0,0 +1,170 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER T min(T a, T b, T c) + { + return glm::min(glm::min(a, b), c); + } + + template + GLM_FUNC_QUALIFIER T min(T a, T b, T c, T d) + { + return glm::min(glm::min(a, b), glm::min(c, d)); + } + + template + GLM_FUNC_QUALIFIER T max(T a, T b, T c) + { + return glm::max(glm::max(a, b), c); + } + + template + GLM_FUNC_QUALIFIER T max(T a, T b, T c, T d) + { + return glm::max(glm::max(a, b), glm::max(c, d)); + } + +# if GLM_HAS_CXX11_STL + using std::fmin; +# else + template + GLM_FUNC_QUALIFIER T fmin(T a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return b; + return min(a, b); + } +# endif + + template + GLM_FUNC_QUALIFIER T fmin(T a, T b, T c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return fmin(b, c); + if (isnan(b)) + return fmin(a, c); + if (isnan(c)) + return min(a, b); + return min(a, b, c); + } + + template + GLM_FUNC_QUALIFIER T fmin(T a, T b, T c, T d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmin' only accept floating-point input"); + + if (isnan(a)) + return fmin(b, c, d); + if (isnan(b)) + return min(a, fmin(c, d)); + if (isnan(c)) + return fmin(min(a, b), d); + if (isnan(d)) + return min(a, b, c); + return min(a, b, c, d); + } + + +# if GLM_HAS_CXX11_STL + using std::fmax; +# else + template + GLM_FUNC_QUALIFIER T fmax(T a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return b; + return max(a, b); + } +# endif + + template + GLM_FUNC_QUALIFIER T fmax(T a, T b, T c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return fmax(b, c); + if (isnan(b)) + return fmax(a, c); + if (isnan(c)) + return max(a, b); + return max(a, b, c); + } + + template + GLM_FUNC_QUALIFIER T fmax(T a, T b, T c, T d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fmax' only accept floating-point input"); + + if (isnan(a)) + return fmax(b, c, d); + if (isnan(b)) + return max(a, fmax(c, d)); + if (isnan(c)) + return fmax(max(a, b), d); + if (isnan(d)) + return max(a, b, c); + return max(a, b, c, d); + } + + // fclamp + template + GLM_FUNC_QUALIFIER genType fclamp(genType x, genType minVal, genType maxVal) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fclamp' only accept floating-point or integer inputs"); + return fmin(fmax(x, minVal), maxVal); + } + + template + GLM_FUNC_QUALIFIER genType clamp(genType const& Texcoord) + { + return glm::clamp(Texcoord, static_cast(0), static_cast(1)); + } + + template + GLM_FUNC_QUALIFIER genType repeat(genType const& Texcoord) + { + return glm::fract(Texcoord); + } + + template + GLM_FUNC_QUALIFIER genType mirrorClamp(genType const& Texcoord) + { + return glm::fract(glm::abs(Texcoord)); + } + + template + GLM_FUNC_QUALIFIER genType mirrorRepeat(genType const& Texcoord) + { + genType const Abs = glm::abs(Texcoord); + genType const Clamp = glm::mod(glm::floor(Abs), static_cast(2)); + genType const Floor = glm::floor(Abs); + genType const Rest = Abs - Floor; + genType const Mirror = Clamp + Rest; + return mix(Rest, static_cast(1) - Rest, Mirror >= static_cast(1)); + } + + template + GLM_FUNC_QUALIFIER int iround(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'iround' only accept floating-point inputs"); + assert(static_cast(0.0) <= x); + + return static_cast(x + static_cast(0.5)); + } + + template + GLM_FUNC_QUALIFIER uint uround(genType const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'uround' only accept floating-point inputs"); + assert(static_cast(0.0) <= x); + + return static_cast(x + static_cast(0.5)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_constants.hpp b/thirdparty/glm/glm/ext/scalar_constants.hpp new file mode 100644 index 0000000..74e210d --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_constants.hpp @@ -0,0 +1,40 @@ +/// @ref ext_scalar_constants +/// @file glm/ext/scalar_constants.hpp +/// +/// @defgroup ext_scalar_constants GLM_EXT_scalar_constants +/// @ingroup ext +/// +/// Provides a list of constants and precomputed useful values. +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_constants extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_constants + /// @{ + + /// Return the epsilon constant for floating point types. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType epsilon(); + + /// Return the pi constant for floating point types. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType pi(); + + /// Return the value of cos(1 / 2) for floating point types. + template + GLM_FUNC_DECL GLM_CONSTEXPR genType cos_one_over_two(); + + /// @} +} //namespace glm + +#include "scalar_constants.inl" diff --git a/thirdparty/glm/glm/ext/scalar_constants.inl b/thirdparty/glm/glm/ext/scalar_constants.inl new file mode 100644 index 0000000..b928e51 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_constants.inl @@ -0,0 +1,24 @@ +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType epsilon() + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'epsilon' only accepts floating-point inputs"); + return std::numeric_limits::epsilon(); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType pi() + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'pi' only accepts floating-point inputs"); + return static_cast(3.14159265358979323846264338327950288); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType cos_one_over_two() + { + return genType(0.877582561890372716130286068203503191); + } +} //namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_int_sized.hpp b/thirdparty/glm/glm/ext/scalar_int_sized.hpp new file mode 100644 index 0000000..8e9c511 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_int_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_scalar_int_sized +/// @file glm/ext/scalar_int_sized.hpp +/// +/// @defgroup ext_scalar_int_sized GLM_EXT_scalar_int_sized +/// @ingroup ext +/// +/// Exposes sized signed integer scalar types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized + +#pragma once + +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_int_sized extension included") +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::int8_t int8; + typedef std::int16_t int16; + typedef std::int32_t int32; +# else + typedef signed char int8; + typedef signed short int16; + typedef signed int int32; +#endif// + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + + /// @addtogroup ext_scalar_int_sized + /// @{ + + /// 8 bit signed integer type. + typedef detail::int8 int8; + + /// 16 bit signed integer type. + typedef detail::int16 int16; + + /// 32 bit signed integer type. + typedef detail::int32 int32; + + /// 64 bit signed integer type. + typedef detail::int64 int64; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_integer.hpp b/thirdparty/glm/glm/ext/scalar_integer.hpp new file mode 100644 index 0000000..a2ca8a2 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_integer.hpp @@ -0,0 +1,92 @@ +/// @ref ext_scalar_integer +/// @file glm/ext/scalar_integer.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_scalar_integer GLM_EXT_scalar_integer +/// @ingroup ext +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "../detail/type_float.hpp" +#include "../vector_relational.hpp" +#include "../common.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_integer + /// @{ + + /// Return true if the value is a power of two number. + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL bool isPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL genIUType nextPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL genIUType prevPowerOfTwo(genIUType v); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL bool isMultiple(genIUType v, genIUType Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam genIUType Integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL genIUType nextMultiple(genIUType v, genIUType Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam genIUType Integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL genIUType prevMultiple(genIUType v, genIUType Multiple); + + /// Returns the bit number of the Nth significant bit set to + /// 1 in the binary representation of value. + /// If value bitcount is less than the Nth significant bit, -1 will be returned. + /// + /// @tparam genIUType Signed or unsigned integer scalar types. + /// + /// @see ext_scalar_integer + template + GLM_FUNC_DECL int findNSB(genIUType x, int significantBitCount); + + /// @} +} //namespace glm + +#include "scalar_integer.inl" diff --git a/thirdparty/glm/glm/ext/scalar_integer.inl b/thirdparty/glm/glm/ext/scalar_integer.inl new file mode 100644 index 0000000..d416197 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_integer.inl @@ -0,0 +1,243 @@ +#include "../integer.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_ceilShift + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T) + { + return v; + } + }; + + template + struct compute_ceilShift + { + GLM_FUNC_QUALIFIER static vec call(vec const& v, T Shift) + { + return v | (v >> Shift); + } + }; + + template + struct compute_ceilPowerOfTwo + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + GLM_STATIC_ASSERT(!std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'ceilPowerOfTwo' only accept integer scalar or vector inputs"); + + vec const Sign(sign(x)); + + vec v(abs(x)); + + v = v - static_cast(1); + v = v | (v >> static_cast(1)); + v = v | (v >> static_cast(2)); + v = v | (v >> static_cast(4)); + v = compute_ceilShift= 2>::call(v, 8); + v = compute_ceilShift= 4>::call(v, 16); + v = compute_ceilShift= 8>::call(v, 32); + return (v + static_cast(1)) * Sign; + } + }; + + template + struct compute_ceilPowerOfTwo + { + GLM_FUNC_QUALIFIER static vec call(vec const& x) + { + GLM_STATIC_ASSERT(!std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'ceilPowerOfTwo' only accept integer scalar or vector inputs"); + + vec v(x); + + v = v - static_cast(1); + v = v | (v >> static_cast(1)); + v = v | (v >> static_cast(2)); + v = v | (v >> static_cast(4)); + v = compute_ceilShift= 2>::call(v, 8); + v = compute_ceilShift= 4>::call(v, 16); + v = compute_ceilShift= 8>::call(v, 32); + return v + static_cast(1); + } + }; + + template + struct compute_ceilMultiple{}; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source > genType(0)) + return Source + (Multiple - std::fmod(Source, Multiple)); + else + return Source + std::fmod(-Source, Multiple); + } + }; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + genType Tmp = Source - genType(1); + return Tmp + (Multiple - (Tmp % Multiple)); + } + }; + + template<> + struct compute_ceilMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + assert(Multiple > genType(0)); + if(Source > genType(0)) + { + genType Tmp = Source - genType(1); + return Tmp + (Multiple - (Tmp % Multiple)); + } + else + return Source + (-Source % Multiple); + } + }; + + template + struct compute_floorMultiple{}; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - std::fmod(Source, Multiple); + else + return Source - std::fmod(Source, Multiple) - Multiple; + } + }; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; + + template<> + struct compute_floorMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if(Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER bool isPowerOfTwo(genIUType Value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'isPowerOfTwo' only accept integer inputs"); + + genIUType const Result = glm::abs(Value); + return !(Result & (Result - 1)); + } + + template + GLM_FUNC_QUALIFIER genIUType nextPowerOfTwo(genIUType value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'nextPowerOfTwo' only accept integer inputs"); + + return detail::compute_ceilPowerOfTwo<1, genIUType, defaultp, std::numeric_limits::is_signed>::call(vec<1, genIUType, defaultp>(value)).x; + } + + template + GLM_FUNC_QUALIFIER genIUType prevPowerOfTwo(genIUType value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'prevPowerOfTwo' only accept integer inputs"); + + return isPowerOfTwo(value) ? value : static_cast(static_cast(1) << static_cast(findMSB(value))); + } + + template + GLM_FUNC_QUALIFIER bool isMultiple(genIUType Value, genIUType Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'isMultiple' only accept integer inputs"); + + return isMultiple(vec<1, genIUType>(Value), vec<1, genIUType>(Multiple)).x; + } + + template + GLM_FUNC_QUALIFIER genIUType nextMultiple(genIUType Source, genIUType Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'nextMultiple' only accept integer inputs"); + + return detail::compute_ceilMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER genIUType prevMultiple(genIUType Source, genIUType Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'prevMultiple' only accept integer inputs"); + + return detail::compute_floorMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER int findNSB(genIUType x, int significantBitCount) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findNSB' only accept integer inputs"); + + if(bitCount(x) < significantBitCount) + return -1; + + genIUType const One = static_cast(1); + int bitPos = 0; + + genIUType key = x; + int nBitCount = significantBitCount; + int Step = sizeof(x) * 8 / 2; + while (key > One) + { + genIUType Mask = static_cast((One << Step) - One); + genIUType currentKey = key & Mask; + int currentBitCount = bitCount(currentKey); + if (nBitCount > currentBitCount) + { + nBitCount -= currentBitCount; + bitPos += Step; + key >>= static_cast(Step); + } + else + { + key = key & Mask; + } + + Step >>= 1; + } + + return static_cast(bitPos); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_packing.hpp b/thirdparty/glm/glm/ext/scalar_packing.hpp new file mode 100644 index 0000000..18b85b7 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_packing.hpp @@ -0,0 +1,32 @@ +/// @ref ext_scalar_packing +/// @file glm/ext/scalar_packing.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_scalar_packing GLM_EXT_scalar_packing +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// This extension provides a set of function to convert scalar values to packed +/// formats. + +#pragma once + +// Dependency: +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_packing extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_packing + /// @{ + + + /// @} +}// namespace glm + +#include "scalar_packing.inl" diff --git a/thirdparty/glm/glm/ext/scalar_packing.inl b/thirdparty/glm/glm/ext/scalar_packing.inl new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/glm/glm/ext/scalar_reciprocal.hpp b/thirdparty/glm/glm/ext/scalar_reciprocal.hpp new file mode 100644 index 0000000..1c7b81d --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_reciprocal.hpp @@ -0,0 +1,135 @@ +/// @ref ext_scalar_reciprocal +/// @file glm/ext/scalar_reciprocal.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_scalar_reciprocal GLM_EXT_scalar_reciprocal +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Define secant, cosecant and cotangent functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_reciprocal extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_reciprocal + /// @{ + + /// Secant function. + /// hypotenuse / adjacent or 1 / cos(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType sec(genType angle); + + /// Cosecant function. + /// hypotenuse / opposite or 1 / sin(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType csc(genType angle); + + /// Cotangent function. + /// adjacent / opposite or 1 / tan(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType cot(genType angle); + + /// Inverse secant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType asec(genType x); + + /// Inverse cosecant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType acsc(genType x); + + /// Inverse cotangent function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType acot(genType x); + + /// Secant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType sech(genType angle); + + /// Cosecant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType csch(genType angle); + + /// Cotangent hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType coth(genType angle); + + /// Inverse secant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType asech(genType x); + + /// Inverse cosecant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType acsch(genType x); + + /// Inverse cotangent hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_scalar_reciprocal + template + GLM_FUNC_DECL genType acoth(genType x); + + /// @} +}//namespace glm + +#include "scalar_reciprocal.inl" diff --git a/thirdparty/glm/glm/ext/scalar_reciprocal.inl b/thirdparty/glm/glm/ext/scalar_reciprocal.inl new file mode 100644 index 0000000..0cd5f87 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_reciprocal.inl @@ -0,0 +1,107 @@ +/// @ref ext_scalar_reciprocal + +#include "../trigonometric.hpp" +#include + +namespace glm +{ + // sec + template + GLM_FUNC_QUALIFIER genType sec(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'sec' only accept floating-point values"); + return genType(1) / glm::cos(angle); + } + + // csc + template + GLM_FUNC_QUALIFIER genType csc(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'csc' only accept floating-point values"); + return genType(1) / glm::sin(angle); + } + + // cot + template + GLM_FUNC_QUALIFIER genType cot(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'cot' only accept floating-point values"); + + genType const pi_over_2 = genType(3.1415926535897932384626433832795 / 2.0); + return glm::tan(pi_over_2 - angle); + } + + // asec + template + GLM_FUNC_QUALIFIER genType asec(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'asec' only accept floating-point values"); + return acos(genType(1) / x); + } + + // acsc + template + GLM_FUNC_QUALIFIER genType acsc(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acsc' only accept floating-point values"); + return asin(genType(1) / x); + } + + // acot + template + GLM_FUNC_QUALIFIER genType acot(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acot' only accept floating-point values"); + + genType const pi_over_2 = genType(3.1415926535897932384626433832795 / 2.0); + return pi_over_2 - atan(x); + } + + // sech + template + GLM_FUNC_QUALIFIER genType sech(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'sech' only accept floating-point values"); + return genType(1) / glm::cosh(angle); + } + + // csch + template + GLM_FUNC_QUALIFIER genType csch(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'csch' only accept floating-point values"); + return genType(1) / glm::sinh(angle); + } + + // coth + template + GLM_FUNC_QUALIFIER genType coth(genType angle) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'coth' only accept floating-point values"); + return glm::cosh(angle) / glm::sinh(angle); + } + + // asech + template + GLM_FUNC_QUALIFIER genType asech(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'asech' only accept floating-point values"); + return acosh(genType(1) / x); + } + + // acsch + template + GLM_FUNC_QUALIFIER genType acsch(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acsch' only accept floating-point values"); + return asinh(genType(1) / x); + } + + // acoth + template + GLM_FUNC_QUALIFIER genType acoth(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acoth' only accept floating-point values"); + return atanh(genType(1) / x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_relational.hpp b/thirdparty/glm/glm/ext/scalar_relational.hpp new file mode 100644 index 0000000..e84df17 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_relational.hpp @@ -0,0 +1,68 @@ +/// @ref ext_scalar_relational +/// @file glm/ext/scalar_relational.hpp +/// +/// @defgroup ext_scalar_relational GLM_EXT_scalar_relational +/// @ingroup ext +/// +/// Exposes comparison functions for scalar types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_vector_relational +/// @see ext_matrix_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_relational + /// @{ + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int ULPs); + + /// Returns the component-wise comparison between two scalars in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @param x First operand. + /// @param y Second operand. + /// @param ULPs Maximum difference in ULPs between the two operators to consider them not equal. + /// + /// @tparam genType Floating-point or integer scalar types + template + GLM_FUNC_DECL GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs); + + /// @} +}//namespace glm + +#include "scalar_relational.inl" diff --git a/thirdparty/glm/glm/ext/scalar_relational.inl b/thirdparty/glm/glm/ext/scalar_relational.inl new file mode 100644 index 0000000..c85583e --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_relational.inl @@ -0,0 +1,40 @@ +#include "../common.hpp" +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/type_float.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, genType const& epsilon) + { + return abs(x - y) <= epsilon; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, genType const& epsilon) + { + return abs(x - y) > epsilon; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool equal(genType const& x, genType const& y, int MaxULPs) + { + detail::float_t const a(x); + detail::float_t const b(y); + + // Different signs means they do not match. + if(a.negative() != b.negative()) + return false; + + // Find the difference in ULPs. + typename detail::float_t::int_type const DiffULPs = abs(a.i - b.i); + return DiffULPs <= MaxULPs; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR bool notEqual(genType const& x, genType const& y, int ULPs) + { + return !equal(x, y, ULPs); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_uint_sized.hpp b/thirdparty/glm/glm/ext/scalar_uint_sized.hpp new file mode 100644 index 0000000..fd5267f --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_uint_sized.hpp @@ -0,0 +1,70 @@ +/// @ref ext_scalar_uint_sized +/// @file glm/ext/scalar_uint_sized.hpp +/// +/// @defgroup ext_scalar_uint_sized GLM_EXT_scalar_uint_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer scalar types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized + +#pragma once + +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_uint_sized extension included") +#endif + +namespace glm{ +namespace detail +{ +# if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::uint8_t uint8; + typedef std::uint16_t uint16; + typedef std::uint32_t uint32; +# else + typedef unsigned char uint8; + typedef unsigned short uint16; + typedef unsigned int uint32; +#endif + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; + + template<> + struct is_int + { + enum test {value = ~0}; + }; +}//namespace detail + + + /// @addtogroup ext_scalar_uint_sized + /// @{ + + /// 8 bit unsigned integer type. + typedef detail::uint8 uint8; + + /// 16 bit unsigned integer type. + typedef detail::uint16 uint16; + + /// 32 bit unsigned integer type. + typedef detail::uint32 uint32; + + /// 64 bit unsigned integer type. + typedef detail::uint64 uint64; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/scalar_ulp.hpp b/thirdparty/glm/glm/ext/scalar_ulp.hpp new file mode 100644 index 0000000..6344d95 --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_ulp.hpp @@ -0,0 +1,77 @@ +/// @ref ext_scalar_ulp +/// @file glm/ext/scalar_ulp.hpp +/// +/// @defgroup ext_scalar_ulp GLM_EXT_scalar_ulp +/// @ingroup ext +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_ulp +/// @see ext_scalar_relational + +#pragma once + +// Dependencies +#include "../ext/scalar_int_sized.hpp" +#include "../common.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_scalar_ulp extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_scalar_ulp + /// @{ + + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType nextFloat(genType x); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType prevFloat(genType x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType nextFloat(genType x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL genType prevFloat(genType x, int ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @see ext_scalar_ulp + GLM_FUNC_DECL int floatDistance(float x, float y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @see ext_scalar_ulp + GLM_FUNC_DECL int64 floatDistance(double x, double y); + + /// @} +}//namespace glm + +#include "scalar_ulp.inl" diff --git a/thirdparty/glm/glm/ext/scalar_ulp.inl b/thirdparty/glm/glm/ext/scalar_ulp.inl new file mode 100644 index 0000000..716528d --- /dev/null +++ b/thirdparty/glm/glm/ext/scalar_ulp.inl @@ -0,0 +1,291 @@ +/// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +/// +/// Developed at SunPro, a Sun Microsystems, Inc. business. +/// Permission to use, copy, modify, and distribute this +/// software is freely granted, provided that this notice +/// is preserved. + +#include "../detail/type_float.hpp" +#include "../ext/scalar_constants.hpp" +#include +#include + +#if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4127) +# pragma warning(disable : 4365) // '=': signed/unsigned mismatch +#elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wpadded" +#endif + +typedef union +{ + float value; + /* FIXME: Assumes 32 bit int. */ + unsigned int word; +} ieee_float_shape_type; + +typedef union +{ + double value; + struct + { + int lsw; + int msw; + } parts; +} ieee_double_shape_type; + +#define GLM_EXTRACT_WORDS(ix0,ix1,d) \ + do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +#define GLM_GET_FLOAT_WORD(i,d) \ + do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = static_cast(gf_u.word); \ + } while (0) + +#define GLM_SET_FLOAT_WORD(d,i) \ + do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = static_cast(i); \ + (d) = sf_u.value; \ + } while (0) + +#define GLM_INSERT_WORDS(d,ix0,ix1) \ + do { \ + ieee_double_shape_type iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER float nextafterf(float x, float y) + { + volatile float t; + int hx, hy, ix, iy; + + GLM_GET_FLOAT_WORD(hx, x); + GLM_GET_FLOAT_WORD(hy, y); + ix = hx & 0x7fffffff; // |x| + iy = hy & 0x7fffffff; // |y| + + if((ix > 0x7f800000) || // x is nan + (iy > 0x7f800000)) // y is nan + return x + y; + if(abs(y - x) <= epsilon()) + return y; // x=y, return y + if(ix == 0) + { // x == 0 + GLM_SET_FLOAT_WORD(x, (hy & 0x80000000) | 1);// return +-minsubnormal + t = x * x; + if(abs(t - x) <= epsilon()) + return t; + else + return x; // raise underflow flag + } + if(hx >= 0) + { // x > 0 + if(hx > hy) // x > y, x -= ulp + hx -= 1; + else // x < y, x += ulp + hx += 1; + } + else + { // x < 0 + if(hy >= 0 || hx > hy) // x < y, x -= ulp + hx -= 1; + else // x > y, x += ulp + hx += 1; + } + hy = hx & 0x7f800000; + if(hy >= 0x7f800000) + return x + x; // overflow + if(hy < 0x00800000) // underflow + { + t = x * x; + if(abs(t - x) > epsilon()) + { // raise underflow flag + GLM_SET_FLOAT_WORD(y, hx); + return y; + } + } + GLM_SET_FLOAT_WORD(x, hx); + return x; + } + + GLM_FUNC_QUALIFIER double nextafter(double x, double y) + { + volatile double t; + int hx, hy, ix, iy; + unsigned int lx, ly; + + GLM_EXTRACT_WORDS(hx, lx, x); + GLM_EXTRACT_WORDS(hy, ly, y); + ix = hx & 0x7fffffff; // |x| + iy = hy & 0x7fffffff; // |y| + + if(((ix >= 0x7ff00000) && ((ix - 0x7ff00000) | lx) != 0) || // x is nan + ((iy >= 0x7ff00000) && ((iy - 0x7ff00000) | ly) != 0)) // y is nan + return x + y; + if(abs(y - x) <= epsilon()) + return y; // x=y, return y + if((ix | lx) == 0) + { // x == 0 + GLM_INSERT_WORDS(x, hy & 0x80000000, 1); // return +-minsubnormal + t = x * x; + if(abs(t - x) <= epsilon()) + return t; + else + return x; // raise underflow flag + } + if(hx >= 0) { // x > 0 + if(hx > hy || ((hx == hy) && (lx > ly))) { // x > y, x -= ulp + if(lx == 0) hx -= 1; + lx -= 1; + } + else { // x < y, x += ulp + lx += 1; + if(lx == 0) hx += 1; + } + } + else { // x < 0 + if(hy >= 0 || hx > hy || ((hx == hy) && (lx > ly))){// x < y, x -= ulp + if(lx == 0) hx -= 1; + lx -= 1; + } + else { // x > y, x += ulp + lx += 1; + if(lx == 0) hx += 1; + } + } + hy = hx & 0x7ff00000; + if(hy >= 0x7ff00000) + return x + x; // overflow + if(hy < 0x00100000) + { // underflow + t = x * x; + if(abs(t - x) > epsilon()) + { // raise underflow flag + GLM_INSERT_WORDS(y, hx, lx); + return y; + } + } + GLM_INSERT_WORDS(x, hx, lx); + return x; + } +}//namespace detail +}//namespace glm + +#if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +#elif GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + +namespace glm +{ + template<> + GLM_FUNC_QUALIFIER float nextFloat(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MAX); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MAX); +# else + return nextafterf(x, FLT_MAX); +# endif + } + + template<> + GLM_FUNC_QUALIFIER double nextFloat(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafter(x, std::numeric_limits::max()); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MAX); +# else + return nextafter(x, DBL_MAX); +# endif + } + + template + GLM_FUNC_QUALIFIER T nextFloat(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'next_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for(int i = 0; i < ULPs; ++i) + temp = nextFloat(temp); + return temp; + } + + GLM_FUNC_QUALIFIER float prevFloat(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MIN); +# else + return nextafterf(x, FLT_MIN); +# endif + } + + GLM_FUNC_QUALIFIER double prevFloat(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return _nextafter(x, DBL_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MIN); +# else + return nextafter(x, DBL_MIN); +# endif + } + + template + GLM_FUNC_QUALIFIER T prevFloat(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'prev_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for(int i = 0; i < ULPs; ++i) + temp = prevFloat(temp); + return temp; + } + + GLM_FUNC_QUALIFIER int floatDistance(float x, float y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } + + GLM_FUNC_QUALIFIER int64 floatDistance(double x, double y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool1.hpp b/thirdparty/glm/glm/ext/vector_bool1.hpp new file mode 100644 index 0000000..002c320 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool1.hpp @@ -0,0 +1,30 @@ +/// @ref ext_vector_bool1 +/// @file glm/ext/vector_bool1.hpp +/// +/// @defgroup ext_vector_bool1 GLM_EXT_vector_bool1 +/// @ingroup ext +/// +/// Exposes bvec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_bool1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_bool1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_bool1 + /// @{ + + /// 1 components vector of boolean. + typedef vec<1, bool, defaultp> bvec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool1_precision.hpp b/thirdparty/glm/glm/ext/vector_bool1_precision.hpp new file mode 100644 index 0000000..e62d3cf --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool1_precision.hpp @@ -0,0 +1,34 @@ +/// @ref ext_vector_bool1_precision +/// @file glm/ext/vector_bool1_precision.hpp +/// +/// @defgroup ext_vector_bool1_precision GLM_EXT_vector_bool1_precision +/// @ingroup ext +/// +/// Exposes highp_bvec1, mediump_bvec1 and lowp_bvec1 types. +/// +/// Include to use the features of this extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_bool1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_bool1_precision + /// @{ + + /// 1 component vector of bool values. + typedef vec<1, bool, highp> highp_bvec1; + + /// 1 component vector of bool values. + typedef vec<1, bool, mediump> mediump_bvec1; + + /// 1 component vector of bool values. + typedef vec<1, bool, lowp> lowp_bvec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool2.hpp b/thirdparty/glm/glm/ext/vector_bool2.hpp new file mode 100644 index 0000000..52288b7 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, bool, defaultp> bvec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool2_precision.hpp b/thirdparty/glm/glm/ext/vector_bool2_precision.hpp new file mode 100644 index 0000000..4370933 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, highp> highp_bvec2; + + /// 2 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, mediump> mediump_bvec2; + + /// 2 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, bool, lowp> lowp_bvec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool3.hpp b/thirdparty/glm/glm/ext/vector_bool3.hpp new file mode 100644 index 0000000..90a0b7e --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, bool, defaultp> bvec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool3_precision.hpp b/thirdparty/glm/glm/ext/vector_bool3_precision.hpp new file mode 100644 index 0000000..89cd2d3 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, highp> highp_bvec3; + + /// 3 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, mediump> mediump_bvec3; + + /// 3 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, bool, lowp> lowp_bvec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool4.hpp b/thirdparty/glm/glm/ext/vector_bool4.hpp new file mode 100644 index 0000000..18aa71b --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_bool4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of boolean. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, bool, defaultp> bvec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_bool4_precision.hpp b/thirdparty/glm/glm/ext/vector_bool4_precision.hpp new file mode 100644 index 0000000..79786e5 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_bool4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_bool4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, highp> highp_bvec4; + + /// 4 components vector of medium qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, mediump> mediump_bvec4; + + /// 4 components vector of low qualifier bool numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, bool, lowp> lowp_bvec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_common.hpp b/thirdparty/glm/glm/ext/vector_common.hpp new file mode 100644 index 0000000..c0a2858 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_common.hpp @@ -0,0 +1,228 @@ +/// @ref ext_vector_common +/// @file glm/ext/vector_common.hpp +/// +/// @defgroup ext_vector_common GLM_EXT_vector_common +/// @ingroup ext +/// +/// Exposes min and max functions for 3 to 4 vector parameters. +/// +/// Include to use the features of this extension. +/// +/// @see core_common +/// @see ext_scalar_common + +#pragma once + +// Dependency: +#include "../ext/scalar_common.hpp" +#include "../common.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_common extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_common + /// @{ + + /// Return the minimum component-wise values of 3 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& a, vec const& b, vec const& c); + + /// Return the minimum component-wise values of 4 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec min(vec const& a, vec const& b, vec const& c, vec const& d); + + /// Return the maximum component-wise values of 3 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z); + + /// Return the maximum component-wise values of 4 inputs + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec max( vec const& x, vec const& y, vec const& z, vec const& w); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& x, T y); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& x, vec const& y); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& a, vec const& b, vec const& c); + + /// Returns y if y < x; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmin documentation + template + GLM_FUNC_DECL vec fmin(vec const& a, vec const& b, vec const& c, vec const& d); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, T b); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b, vec const& c); + + /// Returns y if x < y; otherwise, it returns x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see std::fmax documentation + template + GLM_FUNC_DECL vec fmax(vec const& a, vec const& b, vec const& c, vec const& d); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common + template + GLM_FUNC_DECL vec fclamp(vec const& x, T minVal, T maxVal); + + /// Returns min(max(x, minVal), maxVal) for each component in x. If one of the two arguments is NaN, the value of the other argument is returned. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common + template + GLM_FUNC_DECL vec fclamp(vec const& x, vec const& minVal, vec const& maxVal); + + /// Simulate GL_CLAMP OpenGL wrap mode + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec clamp(vec const& Texcoord); + + /// Simulate GL_REPEAT OpenGL wrap mode + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec repeat(vec const& Texcoord); + + /// Simulate GL_MIRRORED_REPEAT OpenGL wrap mode + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec mirrorClamp(vec const& Texcoord); + + /// Simulate GL_MIRROR_REPEAT OpenGL wrap mode + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec mirrorRepeat(vec const& Texcoord); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam T floating point scalar types. + /// + /// @see GLSL round man page + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec iround(vec const& x); + + /// Returns a value equal to the nearest integer to x. + /// The fraction 0.5 will round in a direction chosen by the + /// implementation, presumably the direction that is fastest. + /// + /// @param x The values of the argument must be greater or equal to zero. + /// @tparam T floating point scalar types. + /// + /// @see GLSL round man page + /// @see ext_vector_common extension. + template + GLM_FUNC_DECL vec uround(vec const& x); + + /// @} +}//namespace glm + +#include "vector_common.inl" diff --git a/thirdparty/glm/glm/ext/vector_common.inl b/thirdparty/glm/glm/ext/vector_common.inl new file mode 100644 index 0000000..67817fc --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_common.inl @@ -0,0 +1,147 @@ +#include "../detail/_vectorize.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& x, vec const& y, vec const& z) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return glm::min(glm::min(x, y), z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec min(vec const& x, vec const& y, vec const& z, vec const& w) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'min' only accept floating-point or integer inputs"); + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + return glm::max(glm::max(x, y), z); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec max(vec const& x, vec const& y, vec const& z, vec const& w) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || std::numeric_limits::is_integer, "'max' only accept floating-point or integer inputs"); + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return detail::functor2::call(fmin, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return detail::functor2::call(fmin, a, b); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b, vec const& c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return fmin(fmin(a, b), c); + } + + template + GLM_FUNC_QUALIFIER vec fmin(vec const& a, vec const& b, vec const& c, vec const& d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmin' only accept floating-point inputs"); + return fmin(fmin(a, b), fmin(c, d)); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, T b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return detail::functor2::call(fmax, a, vec(b)); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return detail::functor2::call(fmax, a, b); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b, vec const& c) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return fmax(fmax(a, b), c); + } + + template + GLM_FUNC_QUALIFIER vec fmax(vec const& a, vec const& b, vec const& c, vec const& d) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'fmax' only accept floating-point inputs"); + return fmax(fmax(a, b), fmax(c, d)); + } + + template + GLM_FUNC_QUALIFIER vec fclamp(vec const& x, T minVal, T maxVal) + { + return fmin(fmax(x, vec(minVal)), vec(maxVal)); + } + + template + GLM_FUNC_QUALIFIER vec fclamp(vec const& x, vec const& minVal, vec const& maxVal) + { + return fmin(fmax(x, minVal), maxVal); + } + + template + GLM_FUNC_QUALIFIER vec clamp(vec const& Texcoord) + { + return glm::clamp(Texcoord, vec(0), vec(1)); + } + + template + GLM_FUNC_QUALIFIER vec repeat(vec const& Texcoord) + { + return glm::fract(Texcoord); + } + + template + GLM_FUNC_QUALIFIER vec mirrorClamp(vec const& Texcoord) + { + return glm::fract(glm::abs(Texcoord)); + } + + template + GLM_FUNC_QUALIFIER vec mirrorRepeat(vec const& Texcoord) + { + vec const Abs = glm::abs(Texcoord); + vec const Clamp = glm::mod(glm::floor(Abs), vec(2)); + vec const Floor = glm::floor(Abs); + vec const Rest = Abs - Floor; + vec const Mirror = Clamp + Rest; + return mix(Rest, vec(1) - Rest, glm::greaterThanEqual(Mirror, vec(1))); + } + + template + GLM_FUNC_QUALIFIER vec iround(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'iround' only accept floating-point inputs"); + assert(all(lessThanEqual(vec(0), x))); + + return vec(x + static_cast(0.5)); + } + + template + GLM_FUNC_QUALIFIER vec uround(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'uround' only accept floating-point inputs"); + assert(all(lessThanEqual(vec(0), x))); + + return vec(x + static_cast(0.5)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double1.hpp b/thirdparty/glm/glm/ext/vector_double1.hpp new file mode 100644 index 0000000..3882667 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double1.hpp @@ -0,0 +1,31 @@ +/// @ref ext_vector_double1 +/// @file glm/ext/vector_double1.hpp +/// +/// @defgroup ext_vector_double1 GLM_EXT_vector_double1 +/// @ingroup ext +/// +/// Exposes double-precision floating point vector type with one component. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_double1_precision extension. +/// @see ext_vector_float1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_double1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_double1 + /// @{ + + /// 1 components vector of double-precision floating-point numbers. + typedef vec<1, double, defaultp> dvec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double1_precision.hpp b/thirdparty/glm/glm/ext/vector_double1_precision.hpp new file mode 100644 index 0000000..1d47195 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double1_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_vector_double1_precision +/// @file glm/ext/vector_double1_precision.hpp +/// +/// @defgroup ext_vector_double1_precision GLM_EXT_vector_double1_precision +/// @ingroup ext +/// +/// Exposes highp_dvec1, mediump_dvec1 and lowp_dvec1 types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_double1 + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_double1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_double1_precision + /// @{ + + /// 1 component vector of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, highp> highp_dvec1; + + /// 1 component vector of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, mediump> mediump_dvec1; + + /// 1 component vector of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, lowp> lowp_dvec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double2.hpp b/thirdparty/glm/glm/ext/vector_double2.hpp new file mode 100644 index 0000000..60e3577 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, double, defaultp> dvec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double2_precision.hpp b/thirdparty/glm/glm/ext/vector_double2_precision.hpp new file mode 100644 index 0000000..fa53940 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_double2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, highp> highp_dvec2; + + /// 2 components vector of medium double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, mediump> mediump_dvec2; + + /// 2 components vector of low double-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, double, lowp> lowp_dvec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double3.hpp b/thirdparty/glm/glm/ext/vector_double3.hpp new file mode 100644 index 0000000..6dfe4c6 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, double, defaultp> dvec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double3_precision.hpp b/thirdparty/glm/glm/ext/vector_double3_precision.hpp new file mode 100644 index 0000000..a8cfa37 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double3_precision.hpp @@ -0,0 +1,34 @@ +/// @ref core +/// @file glm/ext/vector_double3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, highp> highp_dvec3; + + /// 3 components vector of medium double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, mediump> mediump_dvec3; + + /// 3 components vector of low double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, double, lowp> lowp_dvec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double4.hpp b/thirdparty/glm/glm/ext/vector_double4.hpp new file mode 100644 index 0000000..87f225f --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_double4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of double-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, double, defaultp> dvec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_double4_precision.hpp b/thirdparty/glm/glm/ext/vector_double4_precision.hpp new file mode 100644 index 0000000..09cafa1 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_double4_precision.hpp @@ -0,0 +1,35 @@ +/// @ref core +/// @file glm/ext/vector_double4_precision.hpp + +#pragma once +#include "../detail/setup.hpp" +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, highp> highp_dvec4; + + /// 4 components vector of medium double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, mediump> mediump_dvec4; + + /// 4 components vector of low double-qualifier floating-point numbers. + /// There is no guarantee on the actual qualifier. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, double, lowp> lowp_dvec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float1.hpp b/thirdparty/glm/glm/ext/vector_float1.hpp new file mode 100644 index 0000000..28acc2c --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float1.hpp @@ -0,0 +1,31 @@ +/// @ref ext_vector_float1 +/// @file glm/ext/vector_float1.hpp +/// +/// @defgroup ext_vector_float1 GLM_EXT_vector_float1 +/// @ingroup ext +/// +/// Exposes single-precision floating point vector type with one component. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_float1_precision extension. +/// @see ext_vector_double1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_float1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_float1 + /// @{ + + /// 1 components vector of single-precision floating-point numbers. + typedef vec<1, float, defaultp> vec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float1_precision.hpp b/thirdparty/glm/glm/ext/vector_float1_precision.hpp new file mode 100644 index 0000000..6e8dad8 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float1_precision.hpp @@ -0,0 +1,36 @@ +/// @ref ext_vector_float1_precision +/// @file glm/ext/vector_float1_precision.hpp +/// +/// @defgroup ext_vector_float1_precision GLM_EXT_vector_float1_precision +/// @ingroup ext +/// +/// Exposes highp_vec1, mediump_vec1 and lowp_vec1 types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_float1 extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_float1_precision extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_float1_precision + /// @{ + + /// 1 component vector of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, highp> highp_vec1; + + /// 1 component vector of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, mediump> mediump_vec1; + + /// 1 component vector of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, lowp> lowp_vec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float2.hpp b/thirdparty/glm/glm/ext/vector_float2.hpp new file mode 100644 index 0000000..d31545d --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, float, defaultp> vec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float2_precision.hpp b/thirdparty/glm/glm/ext/vector_float2_precision.hpp new file mode 100644 index 0000000..23c0820 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float2_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float2_precision.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 2 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, highp> highp_vec2; + + /// 2 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, mediump> mediump_vec2; + + /// 2 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<2, float, lowp> lowp_vec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float3.hpp b/thirdparty/glm/glm/ext/vector_float3.hpp new file mode 100644 index 0000000..cd79a62 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, float, defaultp> vec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float3_precision.hpp b/thirdparty/glm/glm/ext/vector_float3_precision.hpp new file mode 100644 index 0000000..be640b5 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float3_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float3_precision.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 3 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, highp> highp_vec3; + + /// 3 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, mediump> mediump_vec3; + + /// 3 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<3, float, lowp> lowp_vec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float4.hpp b/thirdparty/glm/glm/ext/vector_float4.hpp new file mode 100644 index 0000000..d84adcc --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_float4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of single-precision floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, float, defaultp> vec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_float4_precision.hpp b/thirdparty/glm/glm/ext/vector_float4_precision.hpp new file mode 100644 index 0000000..aede838 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_float4_precision.hpp @@ -0,0 +1,31 @@ +/// @ref core +/// @file glm/ext/vector_float4_precision.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector_precision + /// @{ + + /// 4 components vector of high single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, highp> highp_vec4; + + /// 4 components vector of medium single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, mediump> mediump_vec4; + + /// 4 components vector of low single-qualifier floating-point numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + /// @see GLSL 4.20.8 specification, section 4.7.2 Precision Qualifier + typedef vec<4, float, lowp> lowp_vec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int1.hpp b/thirdparty/glm/glm/ext/vector_int1.hpp new file mode 100644 index 0000000..dc86038 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int1.hpp @@ -0,0 +1,32 @@ +/// @ref ext_vector_int1 +/// @file glm/ext/vector_int1.hpp +/// +/// @defgroup ext_vector_int1 GLM_EXT_vector_int1 +/// @ingroup ext +/// +/// Exposes ivec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_uint1 extension. +/// @see ext_vector_int1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int1 + /// @{ + + /// 1 component vector of signed integer numbers. + typedef vec<1, int, defaultp> ivec1; + + /// @} +}//namespace glm + diff --git a/thirdparty/glm/glm/ext/vector_int1_sized.hpp b/thirdparty/glm/glm/ext/vector_int1_sized.hpp new file mode 100644 index 0000000..de0d4cf --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int1_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_int1_sized +/// @file glm/ext/vector_int1_sized.hpp +/// +/// @defgroup ext_vector_int1_sized GLM_EXT_vector_int1_sized +/// @ingroup ext +/// +/// Exposes sized signed integer vector types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized +/// @see ext_vector_uint1_sized + +#pragma once + +#include "../ext/vector_int1.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int1_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int1_sized + /// @{ + + /// 8 bit signed integer vector of 1 component type. + /// + /// @see ext_vector_int1_sized + typedef vec<1, int8, defaultp> i8vec1; + + /// 16 bit signed integer vector of 1 component type. + /// + /// @see ext_vector_int1_sized + typedef vec<1, int16, defaultp> i16vec1; + + /// 32 bit signed integer vector of 1 component type. + /// + /// @see ext_vector_int1_sized + typedef vec<1, int32, defaultp> i32vec1; + + /// 64 bit signed integer vector of 1 component type. + /// + /// @see ext_vector_int1_sized + typedef vec<1, int64, defaultp> i64vec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int2.hpp b/thirdparty/glm/glm/ext/vector_int2.hpp new file mode 100644 index 0000000..aef803e --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, int, defaultp> ivec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int2_sized.hpp b/thirdparty/glm/glm/ext/vector_int2_sized.hpp new file mode 100644 index 0000000..1fd57ee --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_int2_sized +/// @file glm/ext/vector_int2_sized.hpp +/// +/// @defgroup ext_vector_int2_sized GLM_EXT_vector_int2_sized +/// @ingroup ext +/// +/// Exposes sized signed integer vector of 2 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized +/// @see ext_vector_uint2_sized + +#pragma once + +#include "../ext/vector_int2.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int2_sized + /// @{ + + /// 8 bit signed integer vector of 2 components type. + /// + /// @see ext_vector_int2_sized + typedef vec<2, int8, defaultp> i8vec2; + + /// 16 bit signed integer vector of 2 components type. + /// + /// @see ext_vector_int2_sized + typedef vec<2, int16, defaultp> i16vec2; + + /// 32 bit signed integer vector of 2 components type. + /// + /// @see ext_vector_int2_sized + typedef vec<2, int32, defaultp> i32vec2; + + /// 64 bit signed integer vector of 2 components type. + /// + /// @see ext_vector_int2_sized + typedef vec<2, int64, defaultp> i64vec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int3.hpp b/thirdparty/glm/glm/ext/vector_int3.hpp new file mode 100644 index 0000000..4767e61 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, int, defaultp> ivec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int3_sized.hpp b/thirdparty/glm/glm/ext/vector_int3_sized.hpp new file mode 100644 index 0000000..085a3fe --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_int3_sized +/// @file glm/ext/vector_int3_sized.hpp +/// +/// @defgroup ext_vector_int3_sized GLM_EXT_vector_int3_sized +/// @ingroup ext +/// +/// Exposes sized signed integer vector of 3 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized +/// @see ext_vector_uint3_sized + +#pragma once + +#include "../ext/vector_int3.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int3_sized + /// @{ + + /// 8 bit signed integer vector of 3 components type. + /// + /// @see ext_vector_int3_sized + typedef vec<3, int8, defaultp> i8vec3; + + /// 16 bit signed integer vector of 3 components type. + /// + /// @see ext_vector_int3_sized + typedef vec<3, int16, defaultp> i16vec3; + + /// 32 bit signed integer vector of 3 components type. + /// + /// @see ext_vector_int3_sized + typedef vec<3, int32, defaultp> i32vec3; + + /// 64 bit signed integer vector of 3 components type. + /// + /// @see ext_vector_int3_sized + typedef vec<3, int64, defaultp> i64vec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int4.hpp b/thirdparty/glm/glm/ext/vector_int4.hpp new file mode 100644 index 0000000..bb23adf --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_int4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of signed integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, int, defaultp> ivec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_int4_sized.hpp b/thirdparty/glm/glm/ext/vector_int4_sized.hpp new file mode 100644 index 0000000..c63d465 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_int4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_int4_sized +/// @file glm/ext/vector_int4_sized.hpp +/// +/// @defgroup ext_vector_int4_sized GLM_EXT_vector_int4_sized +/// @ingroup ext +/// +/// Exposes sized signed integer vector of 4 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_int_sized +/// @see ext_vector_uint4_sized + +#pragma once + +#include "../ext/vector_int4.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_int4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_int4_sized + /// @{ + + /// 8 bit signed integer vector of 4 components type. + /// + /// @see ext_vector_int4_sized + typedef vec<4, int8, defaultp> i8vec4; + + /// 16 bit signed integer vector of 4 components type. + /// + /// @see ext_vector_int4_sized + typedef vec<4, int16, defaultp> i16vec4; + + /// 32 bit signed integer vector of 4 components type. + /// + /// @see ext_vector_int4_sized + typedef vec<4, int32, defaultp> i32vec4; + + /// 64 bit signed integer vector of 4 components type. + /// + /// @see ext_vector_int4_sized + typedef vec<4, int64, defaultp> i64vec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_integer.hpp b/thirdparty/glm/glm/ext/vector_integer.hpp new file mode 100644 index 0000000..1304dd8 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_integer.hpp @@ -0,0 +1,149 @@ +/// @ref ext_vector_integer +/// @file glm/ext/vector_integer.hpp +/// +/// @see core (dependence) +/// @see ext_vector_integer (dependence) +/// +/// @defgroup ext_vector_integer GLM_EXT_vector_integer +/// @ingroup ext +/// +/// Include to use the features of this extension. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "../vector_relational.hpp" +#include "../common.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_integer + /// @{ + + /// Return true if the value is a power of two number. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec isPowerOfTwo(vec const& v); + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec nextPowerOfTwo(vec const& v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec prevPowerOfTwo(vec const& v); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec isMultiple(vec const& v, T Multiple); + + /// Return true if the 'Value' is a multiple of 'Multiple'. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec isMultiple(vec const& v, vec const& Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec nextMultiple(vec const& v, T Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec nextMultiple(vec const& v, vec const& Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec prevMultiple(vec const& v, T Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed or unsigned integer scalar types. + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec prevMultiple(vec const& v, vec const& Multiple); + + /// Returns the bit number of the Nth significant bit set to + /// 1 in the binary representation of value. + /// If value bitcount is less than the Nth significant bit, -1 will be returned. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see ext_vector_integer + template + GLM_FUNC_DECL vec findNSB(vec const& Source, vec SignificantBitCount); + + /// @} +} //namespace glm + +#include "vector_integer.inl" diff --git a/thirdparty/glm/glm/ext/vector_integer.inl b/thirdparty/glm/glm/ext/vector_integer.inl new file mode 100644 index 0000000..cefb132 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_integer.inl @@ -0,0 +1,85 @@ +#include "scalar_integer.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec isPowerOfTwo(vec const& Value) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'isPowerOfTwo' only accept integer inputs"); + + vec const Result(abs(Value)); + return equal(Result & (Result - vec(1)), vec(0)); + } + + template + GLM_FUNC_QUALIFIER vec nextPowerOfTwo(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'nextPowerOfTwo' only accept integer inputs"); + + return detail::compute_ceilPowerOfTwo::is_signed>::call(v); + } + + template + GLM_FUNC_QUALIFIER vec prevPowerOfTwo(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'prevPowerOfTwo' only accept integer inputs"); + + return detail::functor1::call(prevPowerOfTwo, v); + } + + template + GLM_FUNC_QUALIFIER vec isMultiple(vec const& Value, T Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'isMultiple' only accept integer inputs"); + + return equal(Value % Multiple, vec(0)); + } + + template + GLM_FUNC_QUALIFIER vec isMultiple(vec const& Value, vec const& Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'isMultiple' only accept integer inputs"); + + return equal(Value % Multiple, vec(0)); + } + + template + GLM_FUNC_QUALIFIER vec nextMultiple(vec const& Source, T Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'nextMultiple' only accept integer inputs"); + + return detail::functor2::call(nextMultiple, Source, vec(Multiple)); + } + + template + GLM_FUNC_QUALIFIER vec nextMultiple(vec const& Source, vec const& Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'nextMultiple' only accept integer inputs"); + + return detail::functor2::call(nextMultiple, Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec prevMultiple(vec const& Source, T Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'prevMultiple' only accept integer inputs"); + + return detail::functor2::call(prevMultiple, Source, vec(Multiple)); + } + + template + GLM_FUNC_QUALIFIER vec prevMultiple(vec const& Source, vec const& Multiple) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'prevMultiple' only accept integer inputs"); + + return detail::functor2::call(prevMultiple, Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec findNSB(vec const& Source, vec SignificantBitCount) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'findNSB' only accept integer inputs"); + + return detail::functor2_vec_int::call(findNSB, Source, SignificantBitCount); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_packing.hpp b/thirdparty/glm/glm/ext/vector_packing.hpp new file mode 100644 index 0000000..76e5d0c --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_packing.hpp @@ -0,0 +1,32 @@ +/// @ref ext_vector_packing +/// @file glm/ext/vector_packing.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_vector_packing GLM_EXT_vector_packing +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// This extension provides a set of function to convert vectors to packed +/// formats. + +#pragma once + +// Dependency: +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_packing extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_packing + /// @{ + + + /// @} +}// namespace glm + +#include "vector_packing.inl" diff --git a/thirdparty/glm/glm/ext/vector_packing.inl b/thirdparty/glm/glm/ext/vector_packing.inl new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/glm/glm/ext/vector_reciprocal.hpp b/thirdparty/glm/glm/ext/vector_reciprocal.hpp new file mode 100644 index 0000000..84d6766 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_reciprocal.hpp @@ -0,0 +1,135 @@ +/// @ref ext_vector_reciprocal +/// @file glm/ext/vector_reciprocal.hpp +/// +/// @see core (dependence) +/// +/// @defgroup ext_vector_reciprocal GLM_EXT_vector_reciprocal +/// @ingroup ext +/// +/// Include to use the features of this extension. +/// +/// Define secant, cosecant and cotangent functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_reciprocal extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_reciprocal + /// @{ + + /// Secant function. + /// hypotenuse / adjacent or 1 / cos(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType sec(genType angle); + + /// Cosecant function. + /// hypotenuse / opposite or 1 / sin(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType csc(genType angle); + + /// Cotangent function. + /// adjacent / opposite or 1 / tan(x) + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType cot(genType angle); + + /// Inverse secant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType asec(genType x); + + /// Inverse cosecant function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType acsc(genType x); + + /// Inverse cotangent function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType acot(genType x); + + /// Secant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType sech(genType angle); + + /// Cosecant hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType csch(genType angle); + + /// Cotangent hyperbolic function. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType coth(genType angle); + + /// Inverse secant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType asech(genType x); + + /// Inverse cosecant hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType acsch(genType x); + + /// Inverse cotangent hyperbolic function. + /// + /// @return Return an angle expressed in radians. + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see ext_vector_reciprocal + template + GLM_FUNC_DECL genType acoth(genType x); + + /// @} +}//namespace glm + +#include "vector_reciprocal.inl" diff --git a/thirdparty/glm/glm/ext/vector_reciprocal.inl b/thirdparty/glm/glm/ext/vector_reciprocal.inl new file mode 100644 index 0000000..b85102a --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_reciprocal.inl @@ -0,0 +1,105 @@ +/// @ref ext_vector_reciprocal + +#include "../trigonometric.hpp" +#include + +namespace glm +{ + // sec + template + GLM_FUNC_QUALIFIER vec sec(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'sec' only accept floating-point inputs"); + return static_cast(1) / detail::functor1::call(cos, x); + } + + // csc + template + GLM_FUNC_QUALIFIER vec csc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'csc' only accept floating-point inputs"); + return static_cast(1) / detail::functor1::call(sin, x); + } + + // cot + template + GLM_FUNC_QUALIFIER vec cot(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'cot' only accept floating-point inputs"); + T const pi_over_2 = static_cast(3.1415926535897932384626433832795 / 2.0); + return detail::functor1::call(tan, pi_over_2 - x); + } + + // asec + template + GLM_FUNC_QUALIFIER vec asec(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'asec' only accept floating-point inputs"); + return detail::functor1::call(acos, static_cast(1) / x); + } + + // acsc + template + GLM_FUNC_QUALIFIER vec acsc(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acsc' only accept floating-point inputs"); + return detail::functor1::call(asin, static_cast(1) / x); + } + + // acot + template + GLM_FUNC_QUALIFIER vec acot(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acot' only accept floating-point inputs"); + T const pi_over_2 = static_cast(3.1415926535897932384626433832795 / 2.0); + return pi_over_2 - detail::functor1::call(atan, x); + } + + // sech + template + GLM_FUNC_QUALIFIER vec sech(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'sech' only accept floating-point inputs"); + return static_cast(1) / detail::functor1::call(cosh, x); + } + + // csch + template + GLM_FUNC_QUALIFIER vec csch(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'csch' only accept floating-point inputs"); + return static_cast(1) / detail::functor1::call(sinh, x); + } + + // coth + template + GLM_FUNC_QUALIFIER vec coth(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'coth' only accept floating-point inputs"); + return glm::cosh(x) / glm::sinh(x); + } + + // asech + template + GLM_FUNC_QUALIFIER vec asech(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'asech' only accept floating-point inputs"); + return detail::functor1::call(acosh, static_cast(1) / x); + } + + // acsch + template + GLM_FUNC_QUALIFIER vec acsch(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acsch' only accept floating-point inputs"); + return detail::functor1::call(asinh, static_cast(1) / x); + } + + // acoth + template + GLM_FUNC_QUALIFIER vec acoth(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'acoth' only accept floating-point inputs"); + return detail::functor1::call(atanh, static_cast(1) / x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_relational.hpp b/thirdparty/glm/glm/ext/vector_relational.hpp new file mode 100644 index 0000000..1c2367d --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_relational.hpp @@ -0,0 +1,107 @@ +/// @ref ext_vector_relational +/// @file glm/ext/vector_relational.hpp +/// +/// @see core (dependence) +/// @see ext_scalar_integer (dependence) +/// +/// @defgroup ext_vector_relational GLM_EXT_vector_relational +/// @ingroup ext +/// +/// Exposes comparison functions for vector types that take a user defined epsilon values. +/// +/// Include to use the features of this extension. +/// +/// @see core_vector_relational +/// @see ext_scalar_relational +/// @see ext_matrix_relational + +#pragma once + +// Dependencies +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_relational + /// @{ + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, T epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& epsilon); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, int ULPs); + + /// Returns the component-wise comparison between two vectors in term of ULPs. + /// True if this expression is not satisfied. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& ULPs); + + /// @} +}//namespace glm + +#include "vector_relational.inl" diff --git a/thirdparty/glm/glm/ext/vector_relational.inl b/thirdparty/glm/glm/ext/vector_relational.inl new file mode 100644 index 0000000..7a39ab5 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_relational.inl @@ -0,0 +1,75 @@ +#include "../vector_relational.hpp" +#include "../common.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/type_float.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, T Epsilon) + { + return equal(x, y, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& Epsilon) + { + return lessThanEqual(abs(x - y), Epsilon); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, T Epsilon) + { + return notEqual(x, y, vec(Epsilon)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& Epsilon) + { + return greaterThan(abs(x - y), Epsilon); + } + + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, int MaxULPs) + { + return equal(x, y, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec equal(vec const& x, vec const& y, vec const& MaxULPs) + { + vec Result(false); + for(length_t i = 0; i < L; ++i) + { + detail::float_t const a(x[i]); + detail::float_t const b(y[i]); + + // Different signs means they do not match. + if(a.negative() != b.negative()) + { + // Check for equality to make sure +0==-0 + Result[i] = a.mantissa() == b.mantissa() && a.exponent() == b.exponent(); + } + else + { + // Find the difference in ULPs. + typename detail::float_t::int_type const DiffULPs = abs(a.i - b.i); + Result[i] = DiffULPs <= MaxULPs[i]; + } + } + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, int MaxULPs) + { + return notEqual(x, y, vec(MaxULPs)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y, vec const& MaxULPs) + { + return not_(equal(x, y, MaxULPs)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint1.hpp b/thirdparty/glm/glm/ext/vector_uint1.hpp new file mode 100644 index 0000000..eb8a704 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint1.hpp @@ -0,0 +1,32 @@ +/// @ref ext_vector_uint1 +/// @file glm/ext/vector_uint1.hpp +/// +/// @defgroup ext_vector_uint1 GLM_EXT_vector_uint1 +/// @ingroup ext +/// +/// Exposes uvec1 vector type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_vector_int1 extension. +/// @see ext_vector_uint1_precision extension. + +#pragma once + +#include "../detail/type_vec1.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint1 extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint1 + /// @{ + + /// 1 component vector of unsigned integer numbers. + typedef vec<1, unsigned int, defaultp> uvec1; + + /// @} +}//namespace glm + diff --git a/thirdparty/glm/glm/ext/vector_uint1_sized.hpp b/thirdparty/glm/glm/ext/vector_uint1_sized.hpp new file mode 100644 index 0000000..2a938bb --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint1_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_uint1_sized +/// @file glm/ext/vector_uint1_sized.hpp +/// +/// @defgroup ext_vector_uint1_sized GLM_EXT_vector_uint1_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer vector types. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized +/// @see ext_vector_int1_sized + +#pragma once + +#include "../ext/vector_uint1.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint1_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint1_sized + /// @{ + + /// 8 bit unsigned integer vector of 1 component type. + /// + /// @see ext_vector_uint1_sized + typedef vec<1, uint8, defaultp> u8vec1; + + /// 16 bit unsigned integer vector of 1 component type. + /// + /// @see ext_vector_uint1_sized + typedef vec<1, uint16, defaultp> u16vec1; + + /// 32 bit unsigned integer vector of 1 component type. + /// + /// @see ext_vector_uint1_sized + typedef vec<1, uint32, defaultp> u32vec1; + + /// 64 bit unsigned integer vector of 1 component type. + /// + /// @see ext_vector_uint1_sized + typedef vec<1, uint64, defaultp> u64vec1; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint2.hpp b/thirdparty/glm/glm/ext/vector_uint2.hpp new file mode 100644 index 0000000..03c00f5 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint2.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint2.hpp + +#pragma once +#include "../detail/type_vec2.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 2 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<2, unsigned int, defaultp> uvec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint2_sized.hpp b/thirdparty/glm/glm/ext/vector_uint2_sized.hpp new file mode 100644 index 0000000..620fdc6 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint2_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_uint2_sized +/// @file glm/ext/vector_uint2_sized.hpp +/// +/// @defgroup ext_vector_uint2_sized GLM_EXT_vector_uint2_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer vector of 2 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized +/// @see ext_vector_int2_sized + +#pragma once + +#include "../ext/vector_uint2.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint2_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint2_sized + /// @{ + + /// 8 bit unsigned integer vector of 2 components type. + /// + /// @see ext_vector_uint2_sized + typedef vec<2, uint8, defaultp> u8vec2; + + /// 16 bit unsigned integer vector of 2 components type. + /// + /// @see ext_vector_uint2_sized + typedef vec<2, uint16, defaultp> u16vec2; + + /// 32 bit unsigned integer vector of 2 components type. + /// + /// @see ext_vector_uint2_sized + typedef vec<2, uint32, defaultp> u32vec2; + + /// 64 bit unsigned integer vector of 2 components type. + /// + /// @see ext_vector_uint2_sized + typedef vec<2, uint64, defaultp> u64vec2; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint3.hpp b/thirdparty/glm/glm/ext/vector_uint3.hpp new file mode 100644 index 0000000..f5b41c4 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint3.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint3.hpp + +#pragma once +#include "../detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 3 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<3, unsigned int, defaultp> uvec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint3_sized.hpp b/thirdparty/glm/glm/ext/vector_uint3_sized.hpp new file mode 100644 index 0000000..6f96b98 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint3_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_uint3_sized +/// @file glm/ext/vector_uint3_sized.hpp +/// +/// @defgroup ext_vector_uint3_sized GLM_EXT_vector_uint3_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer vector of 3 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized +/// @see ext_vector_int3_sized + +#pragma once + +#include "../ext/vector_uint3.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint3_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint3_sized + /// @{ + + /// 8 bit unsigned integer vector of 3 components type. + /// + /// @see ext_vector_uint3_sized + typedef vec<3, uint8, defaultp> u8vec3; + + /// 16 bit unsigned integer vector of 3 components type. + /// + /// @see ext_vector_uint3_sized + typedef vec<3, uint16, defaultp> u16vec3; + + /// 32 bit unsigned integer vector of 3 components type. + /// + /// @see ext_vector_uint3_sized + typedef vec<3, uint32, defaultp> u32vec3; + + /// 64 bit unsigned integer vector of 3 components type. + /// + /// @see ext_vector_uint3_sized + typedef vec<3, uint64, defaultp> u64vec3; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint4.hpp b/thirdparty/glm/glm/ext/vector_uint4.hpp new file mode 100644 index 0000000..32ced58 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint4.hpp @@ -0,0 +1,18 @@ +/// @ref core +/// @file glm/ext/vector_uint4.hpp + +#pragma once +#include "../detail/type_vec4.hpp" + +namespace glm +{ + /// @addtogroup core_vector + /// @{ + + /// 4 components vector of unsigned integer numbers. + /// + /// @see GLSL 4.20.8 specification, section 4.1.5 Vectors + typedef vec<4, unsigned int, defaultp> uvec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_uint4_sized.hpp b/thirdparty/glm/glm/ext/vector_uint4_sized.hpp new file mode 100644 index 0000000..da992ea --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_uint4_sized.hpp @@ -0,0 +1,49 @@ +/// @ref ext_vector_uint4_sized +/// @file glm/ext/vector_uint4_sized.hpp +/// +/// @defgroup ext_vector_uint4_sized GLM_EXT_vector_uint4_sized +/// @ingroup ext +/// +/// Exposes sized unsigned integer vector of 4 components type. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_uint_sized +/// @see ext_vector_int4_sized + +#pragma once + +#include "../ext/vector_uint4.hpp" +#include "../ext/scalar_uint_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_uint4_sized extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_uint4_sized + /// @{ + + /// 8 bit unsigned integer vector of 4 components type. + /// + /// @see ext_vector_uint4_sized + typedef vec<4, uint8, defaultp> u8vec4; + + /// 16 bit unsigned integer vector of 4 components type. + /// + /// @see ext_vector_uint4_sized + typedef vec<4, uint16, defaultp> u16vec4; + + /// 32 bit unsigned integer vector of 4 components type. + /// + /// @see ext_vector_uint4_sized + typedef vec<4, uint32, defaultp> u32vec4; + + /// 64 bit unsigned integer vector of 4 components type. + /// + /// @see ext_vector_uint4_sized + typedef vec<4, uint64, defaultp> u64vec4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/ext/vector_ulp.hpp b/thirdparty/glm/glm/ext/vector_ulp.hpp new file mode 100644 index 0000000..7c539bb --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_ulp.hpp @@ -0,0 +1,112 @@ +/// @ref ext_vector_ulp +/// @file glm/ext/vector_ulp.hpp +/// +/// @defgroup ext_vector_ulp GLM_EXT_vector_ulp +/// @ingroup ext +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. +/// +/// Include to use the features of this extension. +/// +/// @see ext_scalar_ulp +/// @see ext_scalar_relational +/// @see ext_vector_relational + +#pragma once + +// Dependencies +#include "../ext/scalar_ulp.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_EXT_vector_ulp extension included") +#endif + +namespace glm +{ + /// @addtogroup ext_vector_ulp + /// @{ + + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec nextFloat(vec const& x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec nextFloat(vec const& x, int ULPs); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec nextFloat(vec const& x, vec const& ULPs); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prevFloat(vec const& x); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prevFloat(vec const& x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec prevFloat(vec const& x, vec const& ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec floatDistance(vec const& x, vec const& y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see ext_scalar_ulp + template + GLM_FUNC_DECL vec floatDistance(vec const& x, vec const& y); + + /// @} +}//namespace glm + +#include "vector_ulp.inl" diff --git a/thirdparty/glm/glm/ext/vector_ulp.inl b/thirdparty/glm/glm/ext/vector_ulp.inl new file mode 100644 index 0000000..d3c7648 --- /dev/null +++ b/thirdparty/glm/glm/ext/vector_ulp.inl @@ -0,0 +1,74 @@ +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec nextFloat(vec const& x) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = nextFloat(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec nextFloat(vec const& x, int ULPs) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = nextFloat(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec nextFloat(vec const& x, vec const& ULPs) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = nextFloat(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prevFloat(vec const& x) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prevFloat(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prevFloat(vec const& x, int ULPs) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prevFloat(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prevFloat(vec const& x, vec const& ULPs) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prevFloat(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec floatDistance(vec const& x, vec const& y) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = floatDistance(x[i], y[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec floatDistance(vec const& x, vec const& y) + { + vec Result(0); + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = floatDistance(x[i], y[i]); + return Result; + } +}//namespace glm diff --git a/thirdparty/glm/glm/fwd.hpp b/thirdparty/glm/glm/fwd.hpp new file mode 100644 index 0000000..9c2e5ea --- /dev/null +++ b/thirdparty/glm/glm/fwd.hpp @@ -0,0 +1,1233 @@ +#pragma once + +#include "detail/qualifier.hpp" + +namespace glm +{ +#if GLM_HAS_EXTENDED_INTEGER_TYPE + typedef std::int8_t int8; + typedef std::int16_t int16; + typedef std::int32_t int32; + typedef std::int64_t int64; + + typedef std::uint8_t uint8; + typedef std::uint16_t uint16; + typedef std::uint32_t uint32; + typedef std::uint64_t uint64; +#else + typedef signed char int8; + typedef signed short int16; + typedef signed int int32; + typedef detail::int64 int64; + + typedef unsigned char uint8; + typedef unsigned short uint16; + typedef unsigned int uint32; + typedef detail::uint64 uint64; +#endif + + // Scalar int + + typedef int8 lowp_i8; + typedef int8 mediump_i8; + typedef int8 highp_i8; + typedef int8 i8; + + typedef int8 lowp_int8; + typedef int8 mediump_int8; + typedef int8 highp_int8; + + typedef int8 lowp_int8_t; + typedef int8 mediump_int8_t; + typedef int8 highp_int8_t; + typedef int8 int8_t; + + typedef int16 lowp_i16; + typedef int16 mediump_i16; + typedef int16 highp_i16; + typedef int16 i16; + + typedef int16 lowp_int16; + typedef int16 mediump_int16; + typedef int16 highp_int16; + + typedef int16 lowp_int16_t; + typedef int16 mediump_int16_t; + typedef int16 highp_int16_t; + typedef int16 int16_t; + + typedef int32 lowp_i32; + typedef int32 mediump_i32; + typedef int32 highp_i32; + typedef int32 i32; + + typedef int32 lowp_int32; + typedef int32 mediump_int32; + typedef int32 highp_int32; + + typedef int32 lowp_int32_t; + typedef int32 mediump_int32_t; + typedef int32 highp_int32_t; + typedef int32 int32_t; + + typedef int64 lowp_i64; + typedef int64 mediump_i64; + typedef int64 highp_i64; + typedef int64 i64; + + typedef int64 lowp_int64; + typedef int64 mediump_int64; + typedef int64 highp_int64; + + typedef int64 lowp_int64_t; + typedef int64 mediump_int64_t; + typedef int64 highp_int64_t; + typedef int64 int64_t; + + // Scalar uint + + typedef unsigned int uint; + + typedef uint8 lowp_u8; + typedef uint8 mediump_u8; + typedef uint8 highp_u8; + typedef uint8 u8; + + typedef uint8 lowp_uint8; + typedef uint8 mediump_uint8; + typedef uint8 highp_uint8; + + typedef uint8 lowp_uint8_t; + typedef uint8 mediump_uint8_t; + typedef uint8 highp_uint8_t; + typedef uint8 uint8_t; + + typedef uint16 lowp_u16; + typedef uint16 mediump_u16; + typedef uint16 highp_u16; + typedef uint16 u16; + + typedef uint16 lowp_uint16; + typedef uint16 mediump_uint16; + typedef uint16 highp_uint16; + + typedef uint16 lowp_uint16_t; + typedef uint16 mediump_uint16_t; + typedef uint16 highp_uint16_t; + typedef uint16 uint16_t; + + typedef uint32 lowp_u32; + typedef uint32 mediump_u32; + typedef uint32 highp_u32; + typedef uint32 u32; + + typedef uint32 lowp_uint32; + typedef uint32 mediump_uint32; + typedef uint32 highp_uint32; + + typedef uint32 lowp_uint32_t; + typedef uint32 mediump_uint32_t; + typedef uint32 highp_uint32_t; + typedef uint32 uint32_t; + + typedef uint64 lowp_u64; + typedef uint64 mediump_u64; + typedef uint64 highp_u64; + typedef uint64 u64; + + typedef uint64 lowp_uint64; + typedef uint64 mediump_uint64; + typedef uint64 highp_uint64; + + typedef uint64 lowp_uint64_t; + typedef uint64 mediump_uint64_t; + typedef uint64 highp_uint64_t; + typedef uint64 uint64_t; + + // Scalar float + + typedef float lowp_f32; + typedef float mediump_f32; + typedef float highp_f32; + typedef float f32; + + typedef float lowp_float32; + typedef float mediump_float32; + typedef float highp_float32; + typedef float float32; + + typedef float lowp_float32_t; + typedef float mediump_float32_t; + typedef float highp_float32_t; + typedef float float32_t; + + + typedef double lowp_f64; + typedef double mediump_f64; + typedef double highp_f64; + typedef double f64; + + typedef double lowp_float64; + typedef double mediump_float64; + typedef double highp_float64; + typedef double float64; + + typedef double lowp_float64_t; + typedef double mediump_float64_t; + typedef double highp_float64_t; + typedef double float64_t; + + // Vector bool + + typedef vec<1, bool, lowp> lowp_bvec1; + typedef vec<2, bool, lowp> lowp_bvec2; + typedef vec<3, bool, lowp> lowp_bvec3; + typedef vec<4, bool, lowp> lowp_bvec4; + + typedef vec<1, bool, mediump> mediump_bvec1; + typedef vec<2, bool, mediump> mediump_bvec2; + typedef vec<3, bool, mediump> mediump_bvec3; + typedef vec<4, bool, mediump> mediump_bvec4; + + typedef vec<1, bool, highp> highp_bvec1; + typedef vec<2, bool, highp> highp_bvec2; + typedef vec<3, bool, highp> highp_bvec3; + typedef vec<4, bool, highp> highp_bvec4; + + typedef vec<1, bool, defaultp> bvec1; + typedef vec<2, bool, defaultp> bvec2; + typedef vec<3, bool, defaultp> bvec3; + typedef vec<4, bool, defaultp> bvec4; + + // Vector int + + typedef vec<1, int, lowp> lowp_ivec1; + typedef vec<2, int, lowp> lowp_ivec2; + typedef vec<3, int, lowp> lowp_ivec3; + typedef vec<4, int, lowp> lowp_ivec4; + + typedef vec<1, int, mediump> mediump_ivec1; + typedef vec<2, int, mediump> mediump_ivec2; + typedef vec<3, int, mediump> mediump_ivec3; + typedef vec<4, int, mediump> mediump_ivec4; + + typedef vec<1, int, highp> highp_ivec1; + typedef vec<2, int, highp> highp_ivec2; + typedef vec<3, int, highp> highp_ivec3; + typedef vec<4, int, highp> highp_ivec4; + + typedef vec<1, int, defaultp> ivec1; + typedef vec<2, int, defaultp> ivec2; + typedef vec<3, int, defaultp> ivec3; + typedef vec<4, int, defaultp> ivec4; + + typedef vec<1, i8, lowp> lowp_i8vec1; + typedef vec<2, i8, lowp> lowp_i8vec2; + typedef vec<3, i8, lowp> lowp_i8vec3; + typedef vec<4, i8, lowp> lowp_i8vec4; + + typedef vec<1, i8, mediump> mediump_i8vec1; + typedef vec<2, i8, mediump> mediump_i8vec2; + typedef vec<3, i8, mediump> mediump_i8vec3; + typedef vec<4, i8, mediump> mediump_i8vec4; + + typedef vec<1, i8, highp> highp_i8vec1; + typedef vec<2, i8, highp> highp_i8vec2; + typedef vec<3, i8, highp> highp_i8vec3; + typedef vec<4, i8, highp> highp_i8vec4; + + typedef vec<1, i8, defaultp> i8vec1; + typedef vec<2, i8, defaultp> i8vec2; + typedef vec<3, i8, defaultp> i8vec3; + typedef vec<4, i8, defaultp> i8vec4; + + typedef vec<1, i16, lowp> lowp_i16vec1; + typedef vec<2, i16, lowp> lowp_i16vec2; + typedef vec<3, i16, lowp> lowp_i16vec3; + typedef vec<4, i16, lowp> lowp_i16vec4; + + typedef vec<1, i16, mediump> mediump_i16vec1; + typedef vec<2, i16, mediump> mediump_i16vec2; + typedef vec<3, i16, mediump> mediump_i16vec3; + typedef vec<4, i16, mediump> mediump_i16vec4; + + typedef vec<1, i16, highp> highp_i16vec1; + typedef vec<2, i16, highp> highp_i16vec2; + typedef vec<3, i16, highp> highp_i16vec3; + typedef vec<4, i16, highp> highp_i16vec4; + + typedef vec<1, i16, defaultp> i16vec1; + typedef vec<2, i16, defaultp> i16vec2; + typedef vec<3, i16, defaultp> i16vec3; + typedef vec<4, i16, defaultp> i16vec4; + + typedef vec<1, i32, lowp> lowp_i32vec1; + typedef vec<2, i32, lowp> lowp_i32vec2; + typedef vec<3, i32, lowp> lowp_i32vec3; + typedef vec<4, i32, lowp> lowp_i32vec4; + + typedef vec<1, i32, mediump> mediump_i32vec1; + typedef vec<2, i32, mediump> mediump_i32vec2; + typedef vec<3, i32, mediump> mediump_i32vec3; + typedef vec<4, i32, mediump> mediump_i32vec4; + + typedef vec<1, i32, highp> highp_i32vec1; + typedef vec<2, i32, highp> highp_i32vec2; + typedef vec<3, i32, highp> highp_i32vec3; + typedef vec<4, i32, highp> highp_i32vec4; + + typedef vec<1, i32, defaultp> i32vec1; + typedef vec<2, i32, defaultp> i32vec2; + typedef vec<3, i32, defaultp> i32vec3; + typedef vec<4, i32, defaultp> i32vec4; + + typedef vec<1, i64, lowp> lowp_i64vec1; + typedef vec<2, i64, lowp> lowp_i64vec2; + typedef vec<3, i64, lowp> lowp_i64vec3; + typedef vec<4, i64, lowp> lowp_i64vec4; + + typedef vec<1, i64, mediump> mediump_i64vec1; + typedef vec<2, i64, mediump> mediump_i64vec2; + typedef vec<3, i64, mediump> mediump_i64vec3; + typedef vec<4, i64, mediump> mediump_i64vec4; + + typedef vec<1, i64, highp> highp_i64vec1; + typedef vec<2, i64, highp> highp_i64vec2; + typedef vec<3, i64, highp> highp_i64vec3; + typedef vec<4, i64, highp> highp_i64vec4; + + typedef vec<1, i64, defaultp> i64vec1; + typedef vec<2, i64, defaultp> i64vec2; + typedef vec<3, i64, defaultp> i64vec3; + typedef vec<4, i64, defaultp> i64vec4; + + // Vector uint + + typedef vec<1, uint, lowp> lowp_uvec1; + typedef vec<2, uint, lowp> lowp_uvec2; + typedef vec<3, uint, lowp> lowp_uvec3; + typedef vec<4, uint, lowp> lowp_uvec4; + + typedef vec<1, uint, mediump> mediump_uvec1; + typedef vec<2, uint, mediump> mediump_uvec2; + typedef vec<3, uint, mediump> mediump_uvec3; + typedef vec<4, uint, mediump> mediump_uvec4; + + typedef vec<1, uint, highp> highp_uvec1; + typedef vec<2, uint, highp> highp_uvec2; + typedef vec<3, uint, highp> highp_uvec3; + typedef vec<4, uint, highp> highp_uvec4; + + typedef vec<1, uint, defaultp> uvec1; + typedef vec<2, uint, defaultp> uvec2; + typedef vec<3, uint, defaultp> uvec3; + typedef vec<4, uint, defaultp> uvec4; + + typedef vec<1, u8, lowp> lowp_u8vec1; + typedef vec<2, u8, lowp> lowp_u8vec2; + typedef vec<3, u8, lowp> lowp_u8vec3; + typedef vec<4, u8, lowp> lowp_u8vec4; + + typedef vec<1, u8, mediump> mediump_u8vec1; + typedef vec<2, u8, mediump> mediump_u8vec2; + typedef vec<3, u8, mediump> mediump_u8vec3; + typedef vec<4, u8, mediump> mediump_u8vec4; + + typedef vec<1, u8, highp> highp_u8vec1; + typedef vec<2, u8, highp> highp_u8vec2; + typedef vec<3, u8, highp> highp_u8vec3; + typedef vec<4, u8, highp> highp_u8vec4; + + typedef vec<1, u8, defaultp> u8vec1; + typedef vec<2, u8, defaultp> u8vec2; + typedef vec<3, u8, defaultp> u8vec3; + typedef vec<4, u8, defaultp> u8vec4; + + typedef vec<1, u16, lowp> lowp_u16vec1; + typedef vec<2, u16, lowp> lowp_u16vec2; + typedef vec<3, u16, lowp> lowp_u16vec3; + typedef vec<4, u16, lowp> lowp_u16vec4; + + typedef vec<1, u16, mediump> mediump_u16vec1; + typedef vec<2, u16, mediump> mediump_u16vec2; + typedef vec<3, u16, mediump> mediump_u16vec3; + typedef vec<4, u16, mediump> mediump_u16vec4; + + typedef vec<1, u16, highp> highp_u16vec1; + typedef vec<2, u16, highp> highp_u16vec2; + typedef vec<3, u16, highp> highp_u16vec3; + typedef vec<4, u16, highp> highp_u16vec4; + + typedef vec<1, u16, defaultp> u16vec1; + typedef vec<2, u16, defaultp> u16vec2; + typedef vec<3, u16, defaultp> u16vec3; + typedef vec<4, u16, defaultp> u16vec4; + + typedef vec<1, u32, lowp> lowp_u32vec1; + typedef vec<2, u32, lowp> lowp_u32vec2; + typedef vec<3, u32, lowp> lowp_u32vec3; + typedef vec<4, u32, lowp> lowp_u32vec4; + + typedef vec<1, u32, mediump> mediump_u32vec1; + typedef vec<2, u32, mediump> mediump_u32vec2; + typedef vec<3, u32, mediump> mediump_u32vec3; + typedef vec<4, u32, mediump> mediump_u32vec4; + + typedef vec<1, u32, highp> highp_u32vec1; + typedef vec<2, u32, highp> highp_u32vec2; + typedef vec<3, u32, highp> highp_u32vec3; + typedef vec<4, u32, highp> highp_u32vec4; + + typedef vec<1, u32, defaultp> u32vec1; + typedef vec<2, u32, defaultp> u32vec2; + typedef vec<3, u32, defaultp> u32vec3; + typedef vec<4, u32, defaultp> u32vec4; + + typedef vec<1, u64, lowp> lowp_u64vec1; + typedef vec<2, u64, lowp> lowp_u64vec2; + typedef vec<3, u64, lowp> lowp_u64vec3; + typedef vec<4, u64, lowp> lowp_u64vec4; + + typedef vec<1, u64, mediump> mediump_u64vec1; + typedef vec<2, u64, mediump> mediump_u64vec2; + typedef vec<3, u64, mediump> mediump_u64vec3; + typedef vec<4, u64, mediump> mediump_u64vec4; + + typedef vec<1, u64, highp> highp_u64vec1; + typedef vec<2, u64, highp> highp_u64vec2; + typedef vec<3, u64, highp> highp_u64vec3; + typedef vec<4, u64, highp> highp_u64vec4; + + typedef vec<1, u64, defaultp> u64vec1; + typedef vec<2, u64, defaultp> u64vec2; + typedef vec<3, u64, defaultp> u64vec3; + typedef vec<4, u64, defaultp> u64vec4; + + // Vector float + + typedef vec<1, float, lowp> lowp_vec1; + typedef vec<2, float, lowp> lowp_vec2; + typedef vec<3, float, lowp> lowp_vec3; + typedef vec<4, float, lowp> lowp_vec4; + + typedef vec<1, float, mediump> mediump_vec1; + typedef vec<2, float, mediump> mediump_vec2; + typedef vec<3, float, mediump> mediump_vec3; + typedef vec<4, float, mediump> mediump_vec4; + + typedef vec<1, float, highp> highp_vec1; + typedef vec<2, float, highp> highp_vec2; + typedef vec<3, float, highp> highp_vec3; + typedef vec<4, float, highp> highp_vec4; + + typedef vec<1, float, defaultp> vec1; + typedef vec<2, float, defaultp> vec2; + typedef vec<3, float, defaultp> vec3; + typedef vec<4, float, defaultp> vec4; + + typedef vec<1, float, lowp> lowp_fvec1; + typedef vec<2, float, lowp> lowp_fvec2; + typedef vec<3, float, lowp> lowp_fvec3; + typedef vec<4, float, lowp> lowp_fvec4; + + typedef vec<1, float, mediump> mediump_fvec1; + typedef vec<2, float, mediump> mediump_fvec2; + typedef vec<3, float, mediump> mediump_fvec3; + typedef vec<4, float, mediump> mediump_fvec4; + + typedef vec<1, float, highp> highp_fvec1; + typedef vec<2, float, highp> highp_fvec2; + typedef vec<3, float, highp> highp_fvec3; + typedef vec<4, float, highp> highp_fvec4; + + typedef vec<1, f32, defaultp> fvec1; + typedef vec<2, f32, defaultp> fvec2; + typedef vec<3, f32, defaultp> fvec3; + typedef vec<4, f32, defaultp> fvec4; + + typedef vec<1, f32, lowp> lowp_f32vec1; + typedef vec<2, f32, lowp> lowp_f32vec2; + typedef vec<3, f32, lowp> lowp_f32vec3; + typedef vec<4, f32, lowp> lowp_f32vec4; + + typedef vec<1, f32, mediump> mediump_f32vec1; + typedef vec<2, f32, mediump> mediump_f32vec2; + typedef vec<3, f32, mediump> mediump_f32vec3; + typedef vec<4, f32, mediump> mediump_f32vec4; + + typedef vec<1, f32, highp> highp_f32vec1; + typedef vec<2, f32, highp> highp_f32vec2; + typedef vec<3, f32, highp> highp_f32vec3; + typedef vec<4, f32, highp> highp_f32vec4; + + typedef vec<1, f32, defaultp> f32vec1; + typedef vec<2, f32, defaultp> f32vec2; + typedef vec<3, f32, defaultp> f32vec3; + typedef vec<4, f32, defaultp> f32vec4; + + typedef vec<1, f64, lowp> lowp_dvec1; + typedef vec<2, f64, lowp> lowp_dvec2; + typedef vec<3, f64, lowp> lowp_dvec3; + typedef vec<4, f64, lowp> lowp_dvec4; + + typedef vec<1, f64, mediump> mediump_dvec1; + typedef vec<2, f64, mediump> mediump_dvec2; + typedef vec<3, f64, mediump> mediump_dvec3; + typedef vec<4, f64, mediump> mediump_dvec4; + + typedef vec<1, f64, highp> highp_dvec1; + typedef vec<2, f64, highp> highp_dvec2; + typedef vec<3, f64, highp> highp_dvec3; + typedef vec<4, f64, highp> highp_dvec4; + + typedef vec<1, f64, defaultp> dvec1; + typedef vec<2, f64, defaultp> dvec2; + typedef vec<3, f64, defaultp> dvec3; + typedef vec<4, f64, defaultp> dvec4; + + typedef vec<1, f64, lowp> lowp_f64vec1; + typedef vec<2, f64, lowp> lowp_f64vec2; + typedef vec<3, f64, lowp> lowp_f64vec3; + typedef vec<4, f64, lowp> lowp_f64vec4; + + typedef vec<1, f64, mediump> mediump_f64vec1; + typedef vec<2, f64, mediump> mediump_f64vec2; + typedef vec<3, f64, mediump> mediump_f64vec3; + typedef vec<4, f64, mediump> mediump_f64vec4; + + typedef vec<1, f64, highp> highp_f64vec1; + typedef vec<2, f64, highp> highp_f64vec2; + typedef vec<3, f64, highp> highp_f64vec3; + typedef vec<4, f64, highp> highp_f64vec4; + + typedef vec<1, f64, defaultp> f64vec1; + typedef vec<2, f64, defaultp> f64vec2; + typedef vec<3, f64, defaultp> f64vec3; + typedef vec<4, f64, defaultp> f64vec4; + + // Matrix NxN + + typedef mat<2, 2, f32, lowp> lowp_mat2; + typedef mat<3, 3, f32, lowp> lowp_mat3; + typedef mat<4, 4, f32, lowp> lowp_mat4; + + typedef mat<2, 2, f32, mediump> mediump_mat2; + typedef mat<3, 3, f32, mediump> mediump_mat3; + typedef mat<4, 4, f32, mediump> mediump_mat4; + + typedef mat<2, 2, f32, highp> highp_mat2; + typedef mat<3, 3, f32, highp> highp_mat3; + typedef mat<4, 4, f32, highp> highp_mat4; + + typedef mat<2, 2, f32, defaultp> mat2; + typedef mat<3, 3, f32, defaultp> mat3; + typedef mat<4, 4, f32, defaultp> mat4; + + typedef mat<2, 2, f32, lowp> lowp_fmat2; + typedef mat<3, 3, f32, lowp> lowp_fmat3; + typedef mat<4, 4, f32, lowp> lowp_fmat4; + + typedef mat<2, 2, f32, mediump> mediump_fmat2; + typedef mat<3, 3, f32, mediump> mediump_fmat3; + typedef mat<4, 4, f32, mediump> mediump_fmat4; + + typedef mat<2, 2, f32, highp> highp_fmat2; + typedef mat<3, 3, f32, highp> highp_fmat3; + typedef mat<4, 4, f32, highp> highp_fmat4; + + typedef mat<2, 2, f32, defaultp> fmat2; + typedef mat<3, 3, f32, defaultp> fmat3; + typedef mat<4, 4, f32, defaultp> fmat4; + + typedef mat<2, 2, f32, lowp> lowp_f32mat2; + typedef mat<3, 3, f32, lowp> lowp_f32mat3; + typedef mat<4, 4, f32, lowp> lowp_f32mat4; + + typedef mat<2, 2, f32, mediump> mediump_f32mat2; + typedef mat<3, 3, f32, mediump> mediump_f32mat3; + typedef mat<4, 4, f32, mediump> mediump_f32mat4; + + typedef mat<2, 2, f32, highp> highp_f32mat2; + typedef mat<3, 3, f32, highp> highp_f32mat3; + typedef mat<4, 4, f32, highp> highp_f32mat4; + + typedef mat<2, 2, f32, defaultp> f32mat2; + typedef mat<3, 3, f32, defaultp> f32mat3; + typedef mat<4, 4, f32, defaultp> f32mat4; + + typedef mat<2, 2, f64, lowp> lowp_dmat2; + typedef mat<3, 3, f64, lowp> lowp_dmat3; + typedef mat<4, 4, f64, lowp> lowp_dmat4; + + typedef mat<2, 2, f64, mediump> mediump_dmat2; + typedef mat<3, 3, f64, mediump> mediump_dmat3; + typedef mat<4, 4, f64, mediump> mediump_dmat4; + + typedef mat<2, 2, f64, highp> highp_dmat2; + typedef mat<3, 3, f64, highp> highp_dmat3; + typedef mat<4, 4, f64, highp> highp_dmat4; + + typedef mat<2, 2, f64, defaultp> dmat2; + typedef mat<3, 3, f64, defaultp> dmat3; + typedef mat<4, 4, f64, defaultp> dmat4; + + typedef mat<2, 2, f64, lowp> lowp_f64mat2; + typedef mat<3, 3, f64, lowp> lowp_f64mat3; + typedef mat<4, 4, f64, lowp> lowp_f64mat4; + + typedef mat<2, 2, f64, mediump> mediump_f64mat2; + typedef mat<3, 3, f64, mediump> mediump_f64mat3; + typedef mat<4, 4, f64, mediump> mediump_f64mat4; + + typedef mat<2, 2, f64, highp> highp_f64mat2; + typedef mat<3, 3, f64, highp> highp_f64mat3; + typedef mat<4, 4, f64, highp> highp_f64mat4; + + typedef mat<2, 2, f64, defaultp> f64mat2; + typedef mat<3, 3, f64, defaultp> f64mat3; + typedef mat<4, 4, f64, defaultp> f64mat4; + + // Matrix MxN + + typedef mat<2, 2, f32, lowp> lowp_mat2x2; + typedef mat<2, 3, f32, lowp> lowp_mat2x3; + typedef mat<2, 4, f32, lowp> lowp_mat2x4; + typedef mat<3, 2, f32, lowp> lowp_mat3x2; + typedef mat<3, 3, f32, lowp> lowp_mat3x3; + typedef mat<3, 4, f32, lowp> lowp_mat3x4; + typedef mat<4, 2, f32, lowp> lowp_mat4x2; + typedef mat<4, 3, f32, lowp> lowp_mat4x3; + typedef mat<4, 4, f32, lowp> lowp_mat4x4; + + typedef mat<2, 2, f32, mediump> mediump_mat2x2; + typedef mat<2, 3, f32, mediump> mediump_mat2x3; + typedef mat<2, 4, f32, mediump> mediump_mat2x4; + typedef mat<3, 2, f32, mediump> mediump_mat3x2; + typedef mat<3, 3, f32, mediump> mediump_mat3x3; + typedef mat<3, 4, f32, mediump> mediump_mat3x4; + typedef mat<4, 2, f32, mediump> mediump_mat4x2; + typedef mat<4, 3, f32, mediump> mediump_mat4x3; + typedef mat<4, 4, f32, mediump> mediump_mat4x4; + + typedef mat<2, 2, f32, highp> highp_mat2x2; + typedef mat<2, 3, f32, highp> highp_mat2x3; + typedef mat<2, 4, f32, highp> highp_mat2x4; + typedef mat<3, 2, f32, highp> highp_mat3x2; + typedef mat<3, 3, f32, highp> highp_mat3x3; + typedef mat<3, 4, f32, highp> highp_mat3x4; + typedef mat<4, 2, f32, highp> highp_mat4x2; + typedef mat<4, 3, f32, highp> highp_mat4x3; + typedef mat<4, 4, f32, highp> highp_mat4x4; + + typedef mat<2, 2, f32, defaultp> mat2x2; + typedef mat<2, 3, f32, defaultp> mat2x3; + typedef mat<2, 4, f32, defaultp> mat2x4; + typedef mat<3, 2, f32, defaultp> mat3x2; + typedef mat<3, 3, f32, defaultp> mat3x3; + typedef mat<3, 4, f32, defaultp> mat3x4; + typedef mat<4, 2, f32, defaultp> mat4x2; + typedef mat<4, 3, f32, defaultp> mat4x3; + typedef mat<4, 4, f32, defaultp> mat4x4; + + typedef mat<2, 2, f32, lowp> lowp_fmat2x2; + typedef mat<2, 3, f32, lowp> lowp_fmat2x3; + typedef mat<2, 4, f32, lowp> lowp_fmat2x4; + typedef mat<3, 2, f32, lowp> lowp_fmat3x2; + typedef mat<3, 3, f32, lowp> lowp_fmat3x3; + typedef mat<3, 4, f32, lowp> lowp_fmat3x4; + typedef mat<4, 2, f32, lowp> lowp_fmat4x2; + typedef mat<4, 3, f32, lowp> lowp_fmat4x3; + typedef mat<4, 4, f32, lowp> lowp_fmat4x4; + + typedef mat<2, 2, f32, mediump> mediump_fmat2x2; + typedef mat<2, 3, f32, mediump> mediump_fmat2x3; + typedef mat<2, 4, f32, mediump> mediump_fmat2x4; + typedef mat<3, 2, f32, mediump> mediump_fmat3x2; + typedef mat<3, 3, f32, mediump> mediump_fmat3x3; + typedef mat<3, 4, f32, mediump> mediump_fmat3x4; + typedef mat<4, 2, f32, mediump> mediump_fmat4x2; + typedef mat<4, 3, f32, mediump> mediump_fmat4x3; + typedef mat<4, 4, f32, mediump> mediump_fmat4x4; + + typedef mat<2, 2, f32, highp> highp_fmat2x2; + typedef mat<2, 3, f32, highp> highp_fmat2x3; + typedef mat<2, 4, f32, highp> highp_fmat2x4; + typedef mat<3, 2, f32, highp> highp_fmat3x2; + typedef mat<3, 3, f32, highp> highp_fmat3x3; + typedef mat<3, 4, f32, highp> highp_fmat3x4; + typedef mat<4, 2, f32, highp> highp_fmat4x2; + typedef mat<4, 3, f32, highp> highp_fmat4x3; + typedef mat<4, 4, f32, highp> highp_fmat4x4; + + typedef mat<2, 2, f32, defaultp> fmat2x2; + typedef mat<2, 3, f32, defaultp> fmat2x3; + typedef mat<2, 4, f32, defaultp> fmat2x4; + typedef mat<3, 2, f32, defaultp> fmat3x2; + typedef mat<3, 3, f32, defaultp> fmat3x3; + typedef mat<3, 4, f32, defaultp> fmat3x4; + typedef mat<4, 2, f32, defaultp> fmat4x2; + typedef mat<4, 3, f32, defaultp> fmat4x3; + typedef mat<4, 4, f32, defaultp> fmat4x4; + + typedef mat<2, 2, f32, lowp> lowp_f32mat2x2; + typedef mat<2, 3, f32, lowp> lowp_f32mat2x3; + typedef mat<2, 4, f32, lowp> lowp_f32mat2x4; + typedef mat<3, 2, f32, lowp> lowp_f32mat3x2; + typedef mat<3, 3, f32, lowp> lowp_f32mat3x3; + typedef mat<3, 4, f32, lowp> lowp_f32mat3x4; + typedef mat<4, 2, f32, lowp> lowp_f32mat4x2; + typedef mat<4, 3, f32, lowp> lowp_f32mat4x3; + typedef mat<4, 4, f32, lowp> lowp_f32mat4x4; + + typedef mat<2, 2, f32, mediump> mediump_f32mat2x2; + typedef mat<2, 3, f32, mediump> mediump_f32mat2x3; + typedef mat<2, 4, f32, mediump> mediump_f32mat2x4; + typedef mat<3, 2, f32, mediump> mediump_f32mat3x2; + typedef mat<3, 3, f32, mediump> mediump_f32mat3x3; + typedef mat<3, 4, f32, mediump> mediump_f32mat3x4; + typedef mat<4, 2, f32, mediump> mediump_f32mat4x2; + typedef mat<4, 3, f32, mediump> mediump_f32mat4x3; + typedef mat<4, 4, f32, mediump> mediump_f32mat4x4; + + typedef mat<2, 2, f32, highp> highp_f32mat2x2; + typedef mat<2, 3, f32, highp> highp_f32mat2x3; + typedef mat<2, 4, f32, highp> highp_f32mat2x4; + typedef mat<3, 2, f32, highp> highp_f32mat3x2; + typedef mat<3, 3, f32, highp> highp_f32mat3x3; + typedef mat<3, 4, f32, highp> highp_f32mat3x4; + typedef mat<4, 2, f32, highp> highp_f32mat4x2; + typedef mat<4, 3, f32, highp> highp_f32mat4x3; + typedef mat<4, 4, f32, highp> highp_f32mat4x4; + + typedef mat<2, 2, f32, defaultp> f32mat2x2; + typedef mat<2, 3, f32, defaultp> f32mat2x3; + typedef mat<2, 4, f32, defaultp> f32mat2x4; + typedef mat<3, 2, f32, defaultp> f32mat3x2; + typedef mat<3, 3, f32, defaultp> f32mat3x3; + typedef mat<3, 4, f32, defaultp> f32mat3x4; + typedef mat<4, 2, f32, defaultp> f32mat4x2; + typedef mat<4, 3, f32, defaultp> f32mat4x3; + typedef mat<4, 4, f32, defaultp> f32mat4x4; + + typedef mat<2, 2, double, lowp> lowp_dmat2x2; + typedef mat<2, 3, double, lowp> lowp_dmat2x3; + typedef mat<2, 4, double, lowp> lowp_dmat2x4; + typedef mat<3, 2, double, lowp> lowp_dmat3x2; + typedef mat<3, 3, double, lowp> lowp_dmat3x3; + typedef mat<3, 4, double, lowp> lowp_dmat3x4; + typedef mat<4, 2, double, lowp> lowp_dmat4x2; + typedef mat<4, 3, double, lowp> lowp_dmat4x3; + typedef mat<4, 4, double, lowp> lowp_dmat4x4; + + typedef mat<2, 2, double, mediump> mediump_dmat2x2; + typedef mat<2, 3, double, mediump> mediump_dmat2x3; + typedef mat<2, 4, double, mediump> mediump_dmat2x4; + typedef mat<3, 2, double, mediump> mediump_dmat3x2; + typedef mat<3, 3, double, mediump> mediump_dmat3x3; + typedef mat<3, 4, double, mediump> mediump_dmat3x4; + typedef mat<4, 2, double, mediump> mediump_dmat4x2; + typedef mat<4, 3, double, mediump> mediump_dmat4x3; + typedef mat<4, 4, double, mediump> mediump_dmat4x4; + + typedef mat<2, 2, double, highp> highp_dmat2x2; + typedef mat<2, 3, double, highp> highp_dmat2x3; + typedef mat<2, 4, double, highp> highp_dmat2x4; + typedef mat<3, 2, double, highp> highp_dmat3x2; + typedef mat<3, 3, double, highp> highp_dmat3x3; + typedef mat<3, 4, double, highp> highp_dmat3x4; + typedef mat<4, 2, double, highp> highp_dmat4x2; + typedef mat<4, 3, double, highp> highp_dmat4x3; + typedef mat<4, 4, double, highp> highp_dmat4x4; + + typedef mat<2, 2, double, defaultp> dmat2x2; + typedef mat<2, 3, double, defaultp> dmat2x3; + typedef mat<2, 4, double, defaultp> dmat2x4; + typedef mat<3, 2, double, defaultp> dmat3x2; + typedef mat<3, 3, double, defaultp> dmat3x3; + typedef mat<3, 4, double, defaultp> dmat3x4; + typedef mat<4, 2, double, defaultp> dmat4x2; + typedef mat<4, 3, double, defaultp> dmat4x3; + typedef mat<4, 4, double, defaultp> dmat4x4; + + typedef mat<2, 2, f64, lowp> lowp_f64mat2x2; + typedef mat<2, 3, f64, lowp> lowp_f64mat2x3; + typedef mat<2, 4, f64, lowp> lowp_f64mat2x4; + typedef mat<3, 2, f64, lowp> lowp_f64mat3x2; + typedef mat<3, 3, f64, lowp> lowp_f64mat3x3; + typedef mat<3, 4, f64, lowp> lowp_f64mat3x4; + typedef mat<4, 2, f64, lowp> lowp_f64mat4x2; + typedef mat<4, 3, f64, lowp> lowp_f64mat4x3; + typedef mat<4, 4, f64, lowp> lowp_f64mat4x4; + + typedef mat<2, 2, f64, mediump> mediump_f64mat2x2; + typedef mat<2, 3, f64, mediump> mediump_f64mat2x3; + typedef mat<2, 4, f64, mediump> mediump_f64mat2x4; + typedef mat<3, 2, f64, mediump> mediump_f64mat3x2; + typedef mat<3, 3, f64, mediump> mediump_f64mat3x3; + typedef mat<3, 4, f64, mediump> mediump_f64mat3x4; + typedef mat<4, 2, f64, mediump> mediump_f64mat4x2; + typedef mat<4, 3, f64, mediump> mediump_f64mat4x3; + typedef mat<4, 4, f64, mediump> mediump_f64mat4x4; + + typedef mat<2, 2, f64, highp> highp_f64mat2x2; + typedef mat<2, 3, f64, highp> highp_f64mat2x3; + typedef mat<2, 4, f64, highp> highp_f64mat2x4; + typedef mat<3, 2, f64, highp> highp_f64mat3x2; + typedef mat<3, 3, f64, highp> highp_f64mat3x3; + typedef mat<3, 4, f64, highp> highp_f64mat3x4; + typedef mat<4, 2, f64, highp> highp_f64mat4x2; + typedef mat<4, 3, f64, highp> highp_f64mat4x3; + typedef mat<4, 4, f64, highp> highp_f64mat4x4; + + typedef mat<2, 2, f64, defaultp> f64mat2x2; + typedef mat<2, 3, f64, defaultp> f64mat2x3; + typedef mat<2, 4, f64, defaultp> f64mat2x4; + typedef mat<3, 2, f64, defaultp> f64mat3x2; + typedef mat<3, 3, f64, defaultp> f64mat3x3; + typedef mat<3, 4, f64, defaultp> f64mat3x4; + typedef mat<4, 2, f64, defaultp> f64mat4x2; + typedef mat<4, 3, f64, defaultp> f64mat4x3; + typedef mat<4, 4, f64, defaultp> f64mat4x4; + + // Signed integer matrix MxN + + typedef mat<2, 2, int, lowp> lowp_imat2x2; + typedef mat<2, 3, int, lowp> lowp_imat2x3; + typedef mat<2, 4, int, lowp> lowp_imat2x4; + typedef mat<3, 2, int, lowp> lowp_imat3x2; + typedef mat<3, 3, int, lowp> lowp_imat3x3; + typedef mat<3, 4, int, lowp> lowp_imat3x4; + typedef mat<4, 2, int, lowp> lowp_imat4x2; + typedef mat<4, 3, int, lowp> lowp_imat4x3; + typedef mat<4, 4, int, lowp> lowp_imat4x4; + + typedef mat<2, 2, int, mediump> mediump_imat2x2; + typedef mat<2, 3, int, mediump> mediump_imat2x3; + typedef mat<2, 4, int, mediump> mediump_imat2x4; + typedef mat<3, 2, int, mediump> mediump_imat3x2; + typedef mat<3, 3, int, mediump> mediump_imat3x3; + typedef mat<3, 4, int, mediump> mediump_imat3x4; + typedef mat<4, 2, int, mediump> mediump_imat4x2; + typedef mat<4, 3, int, mediump> mediump_imat4x3; + typedef mat<4, 4, int, mediump> mediump_imat4x4; + + typedef mat<2, 2, int, highp> highp_imat2x2; + typedef mat<2, 3, int, highp> highp_imat2x3; + typedef mat<2, 4, int, highp> highp_imat2x4; + typedef mat<3, 2, int, highp> highp_imat3x2; + typedef mat<3, 3, int, highp> highp_imat3x3; + typedef mat<3, 4, int, highp> highp_imat3x4; + typedef mat<4, 2, int, highp> highp_imat4x2; + typedef mat<4, 3, int, highp> highp_imat4x3; + typedef mat<4, 4, int, highp> highp_imat4x4; + + typedef mat<2, 2, int, defaultp> imat2x2; + typedef mat<2, 3, int, defaultp> imat2x3; + typedef mat<2, 4, int, defaultp> imat2x4; + typedef mat<3, 2, int, defaultp> imat3x2; + typedef mat<3, 3, int, defaultp> imat3x3; + typedef mat<3, 4, int, defaultp> imat3x4; + typedef mat<4, 2, int, defaultp> imat4x2; + typedef mat<4, 3, int, defaultp> imat4x3; + typedef mat<4, 4, int, defaultp> imat4x4; + + + typedef mat<2, 2, int8, lowp> lowp_i8mat2x2; + typedef mat<2, 3, int8, lowp> lowp_i8mat2x3; + typedef mat<2, 4, int8, lowp> lowp_i8mat2x4; + typedef mat<3, 2, int8, lowp> lowp_i8mat3x2; + typedef mat<3, 3, int8, lowp> lowp_i8mat3x3; + typedef mat<3, 4, int8, lowp> lowp_i8mat3x4; + typedef mat<4, 2, int8, lowp> lowp_i8mat4x2; + typedef mat<4, 3, int8, lowp> lowp_i8mat4x3; + typedef mat<4, 4, int8, lowp> lowp_i8mat4x4; + + typedef mat<2, 2, int8, mediump> mediump_i8mat2x2; + typedef mat<2, 3, int8, mediump> mediump_i8mat2x3; + typedef mat<2, 4, int8, mediump> mediump_i8mat2x4; + typedef mat<3, 2, int8, mediump> mediump_i8mat3x2; + typedef mat<3, 3, int8, mediump> mediump_i8mat3x3; + typedef mat<3, 4, int8, mediump> mediump_i8mat3x4; + typedef mat<4, 2, int8, mediump> mediump_i8mat4x2; + typedef mat<4, 3, int8, mediump> mediump_i8mat4x3; + typedef mat<4, 4, int8, mediump> mediump_i8mat4x4; + + typedef mat<2, 2, int8, highp> highp_i8mat2x2; + typedef mat<2, 3, int8, highp> highp_i8mat2x3; + typedef mat<2, 4, int8, highp> highp_i8mat2x4; + typedef mat<3, 2, int8, highp> highp_i8mat3x2; + typedef mat<3, 3, int8, highp> highp_i8mat3x3; + typedef mat<3, 4, int8, highp> highp_i8mat3x4; + typedef mat<4, 2, int8, highp> highp_i8mat4x2; + typedef mat<4, 3, int8, highp> highp_i8mat4x3; + typedef mat<4, 4, int8, highp> highp_i8mat4x4; + + typedef mat<2, 2, int8, defaultp> i8mat2x2; + typedef mat<2, 3, int8, defaultp> i8mat2x3; + typedef mat<2, 4, int8, defaultp> i8mat2x4; + typedef mat<3, 2, int8, defaultp> i8mat3x2; + typedef mat<3, 3, int8, defaultp> i8mat3x3; + typedef mat<3, 4, int8, defaultp> i8mat3x4; + typedef mat<4, 2, int8, defaultp> i8mat4x2; + typedef mat<4, 3, int8, defaultp> i8mat4x3; + typedef mat<4, 4, int8, defaultp> i8mat4x4; + + + typedef mat<2, 2, int16, lowp> lowp_i16mat2x2; + typedef mat<2, 3, int16, lowp> lowp_i16mat2x3; + typedef mat<2, 4, int16, lowp> lowp_i16mat2x4; + typedef mat<3, 2, int16, lowp> lowp_i16mat3x2; + typedef mat<3, 3, int16, lowp> lowp_i16mat3x3; + typedef mat<3, 4, int16, lowp> lowp_i16mat3x4; + typedef mat<4, 2, int16, lowp> lowp_i16mat4x2; + typedef mat<4, 3, int16, lowp> lowp_i16mat4x3; + typedef mat<4, 4, int16, lowp> lowp_i16mat4x4; + + typedef mat<2, 2, int16, mediump> mediump_i16mat2x2; + typedef mat<2, 3, int16, mediump> mediump_i16mat2x3; + typedef mat<2, 4, int16, mediump> mediump_i16mat2x4; + typedef mat<3, 2, int16, mediump> mediump_i16mat3x2; + typedef mat<3, 3, int16, mediump> mediump_i16mat3x3; + typedef mat<3, 4, int16, mediump> mediump_i16mat3x4; + typedef mat<4, 2, int16, mediump> mediump_i16mat4x2; + typedef mat<4, 3, int16, mediump> mediump_i16mat4x3; + typedef mat<4, 4, int16, mediump> mediump_i16mat4x4; + + typedef mat<2, 2, int16, highp> highp_i16mat2x2; + typedef mat<2, 3, int16, highp> highp_i16mat2x3; + typedef mat<2, 4, int16, highp> highp_i16mat2x4; + typedef mat<3, 2, int16, highp> highp_i16mat3x2; + typedef mat<3, 3, int16, highp> highp_i16mat3x3; + typedef mat<3, 4, int16, highp> highp_i16mat3x4; + typedef mat<4, 2, int16, highp> highp_i16mat4x2; + typedef mat<4, 3, int16, highp> highp_i16mat4x3; + typedef mat<4, 4, int16, highp> highp_i16mat4x4; + + typedef mat<2, 2, int16, defaultp> i16mat2x2; + typedef mat<2, 3, int16, defaultp> i16mat2x3; + typedef mat<2, 4, int16, defaultp> i16mat2x4; + typedef mat<3, 2, int16, defaultp> i16mat3x2; + typedef mat<3, 3, int16, defaultp> i16mat3x3; + typedef mat<3, 4, int16, defaultp> i16mat3x4; + typedef mat<4, 2, int16, defaultp> i16mat4x2; + typedef mat<4, 3, int16, defaultp> i16mat4x3; + typedef mat<4, 4, int16, defaultp> i16mat4x4; + + + typedef mat<2, 2, int32, lowp> lowp_i32mat2x2; + typedef mat<2, 3, int32, lowp> lowp_i32mat2x3; + typedef mat<2, 4, int32, lowp> lowp_i32mat2x4; + typedef mat<3, 2, int32, lowp> lowp_i32mat3x2; + typedef mat<3, 3, int32, lowp> lowp_i32mat3x3; + typedef mat<3, 4, int32, lowp> lowp_i32mat3x4; + typedef mat<4, 2, int32, lowp> lowp_i32mat4x2; + typedef mat<4, 3, int32, lowp> lowp_i32mat4x3; + typedef mat<4, 4, int32, lowp> lowp_i32mat4x4; + + typedef mat<2, 2, int32, mediump> mediump_i32mat2x2; + typedef mat<2, 3, int32, mediump> mediump_i32mat2x3; + typedef mat<2, 4, int32, mediump> mediump_i32mat2x4; + typedef mat<3, 2, int32, mediump> mediump_i32mat3x2; + typedef mat<3, 3, int32, mediump> mediump_i32mat3x3; + typedef mat<3, 4, int32, mediump> mediump_i32mat3x4; + typedef mat<4, 2, int32, mediump> mediump_i32mat4x2; + typedef mat<4, 3, int32, mediump> mediump_i32mat4x3; + typedef mat<4, 4, int32, mediump> mediump_i32mat4x4; + + typedef mat<2, 2, int32, highp> highp_i32mat2x2; + typedef mat<2, 3, int32, highp> highp_i32mat2x3; + typedef mat<2, 4, int32, highp> highp_i32mat2x4; + typedef mat<3, 2, int32, highp> highp_i32mat3x2; + typedef mat<3, 3, int32, highp> highp_i32mat3x3; + typedef mat<3, 4, int32, highp> highp_i32mat3x4; + typedef mat<4, 2, int32, highp> highp_i32mat4x2; + typedef mat<4, 3, int32, highp> highp_i32mat4x3; + typedef mat<4, 4, int32, highp> highp_i32mat4x4; + + typedef mat<2, 2, int32, defaultp> i32mat2x2; + typedef mat<2, 3, int32, defaultp> i32mat2x3; + typedef mat<2, 4, int32, defaultp> i32mat2x4; + typedef mat<3, 2, int32, defaultp> i32mat3x2; + typedef mat<3, 3, int32, defaultp> i32mat3x3; + typedef mat<3, 4, int32, defaultp> i32mat3x4; + typedef mat<4, 2, int32, defaultp> i32mat4x2; + typedef mat<4, 3, int32, defaultp> i32mat4x3; + typedef mat<4, 4, int32, defaultp> i32mat4x4; + + + typedef mat<2, 2, int64, lowp> lowp_i64mat2x2; + typedef mat<2, 3, int64, lowp> lowp_i64mat2x3; + typedef mat<2, 4, int64, lowp> lowp_i64mat2x4; + typedef mat<3, 2, int64, lowp> lowp_i64mat3x2; + typedef mat<3, 3, int64, lowp> lowp_i64mat3x3; + typedef mat<3, 4, int64, lowp> lowp_i64mat3x4; + typedef mat<4, 2, int64, lowp> lowp_i64mat4x2; + typedef mat<4, 3, int64, lowp> lowp_i64mat4x3; + typedef mat<4, 4, int64, lowp> lowp_i64mat4x4; + + typedef mat<2, 2, int64, mediump> mediump_i64mat2x2; + typedef mat<2, 3, int64, mediump> mediump_i64mat2x3; + typedef mat<2, 4, int64, mediump> mediump_i64mat2x4; + typedef mat<3, 2, int64, mediump> mediump_i64mat3x2; + typedef mat<3, 3, int64, mediump> mediump_i64mat3x3; + typedef mat<3, 4, int64, mediump> mediump_i64mat3x4; + typedef mat<4, 2, int64, mediump> mediump_i64mat4x2; + typedef mat<4, 3, int64, mediump> mediump_i64mat4x3; + typedef mat<4, 4, int64, mediump> mediump_i64mat4x4; + + typedef mat<2, 2, int64, highp> highp_i64mat2x2; + typedef mat<2, 3, int64, highp> highp_i64mat2x3; + typedef mat<2, 4, int64, highp> highp_i64mat2x4; + typedef mat<3, 2, int64, highp> highp_i64mat3x2; + typedef mat<3, 3, int64, highp> highp_i64mat3x3; + typedef mat<3, 4, int64, highp> highp_i64mat3x4; + typedef mat<4, 2, int64, highp> highp_i64mat4x2; + typedef mat<4, 3, int64, highp> highp_i64mat4x3; + typedef mat<4, 4, int64, highp> highp_i64mat4x4; + + typedef mat<2, 2, int64, defaultp> i64mat2x2; + typedef mat<2, 3, int64, defaultp> i64mat2x3; + typedef mat<2, 4, int64, defaultp> i64mat2x4; + typedef mat<3, 2, int64, defaultp> i64mat3x2; + typedef mat<3, 3, int64, defaultp> i64mat3x3; + typedef mat<3, 4, int64, defaultp> i64mat3x4; + typedef mat<4, 2, int64, defaultp> i64mat4x2; + typedef mat<4, 3, int64, defaultp> i64mat4x3; + typedef mat<4, 4, int64, defaultp> i64mat4x4; + + + // Unsigned integer matrix MxN + + typedef mat<2, 2, uint, lowp> lowp_umat2x2; + typedef mat<2, 3, uint, lowp> lowp_umat2x3; + typedef mat<2, 4, uint, lowp> lowp_umat2x4; + typedef mat<3, 2, uint, lowp> lowp_umat3x2; + typedef mat<3, 3, uint, lowp> lowp_umat3x3; + typedef mat<3, 4, uint, lowp> lowp_umat3x4; + typedef mat<4, 2, uint, lowp> lowp_umat4x2; + typedef mat<4, 3, uint, lowp> lowp_umat4x3; + typedef mat<4, 4, uint, lowp> lowp_umat4x4; + + typedef mat<2, 2, uint, mediump> mediump_umat2x2; + typedef mat<2, 3, uint, mediump> mediump_umat2x3; + typedef mat<2, 4, uint, mediump> mediump_umat2x4; + typedef mat<3, 2, uint, mediump> mediump_umat3x2; + typedef mat<3, 3, uint, mediump> mediump_umat3x3; + typedef mat<3, 4, uint, mediump> mediump_umat3x4; + typedef mat<4, 2, uint, mediump> mediump_umat4x2; + typedef mat<4, 3, uint, mediump> mediump_umat4x3; + typedef mat<4, 4, uint, mediump> mediump_umat4x4; + + typedef mat<2, 2, uint, highp> highp_umat2x2; + typedef mat<2, 3, uint, highp> highp_umat2x3; + typedef mat<2, 4, uint, highp> highp_umat2x4; + typedef mat<3, 2, uint, highp> highp_umat3x2; + typedef mat<3, 3, uint, highp> highp_umat3x3; + typedef mat<3, 4, uint, highp> highp_umat3x4; + typedef mat<4, 2, uint, highp> highp_umat4x2; + typedef mat<4, 3, uint, highp> highp_umat4x3; + typedef mat<4, 4, uint, highp> highp_umat4x4; + + typedef mat<2, 2, uint, defaultp> umat2x2; + typedef mat<2, 3, uint, defaultp> umat2x3; + typedef mat<2, 4, uint, defaultp> umat2x4; + typedef mat<3, 2, uint, defaultp> umat3x2; + typedef mat<3, 3, uint, defaultp> umat3x3; + typedef mat<3, 4, uint, defaultp> umat3x4; + typedef mat<4, 2, uint, defaultp> umat4x2; + typedef mat<4, 3, uint, defaultp> umat4x3; + typedef mat<4, 4, uint, defaultp> umat4x4; + + + typedef mat<2, 2, uint8, lowp> lowp_u8mat2x2; + typedef mat<2, 3, uint8, lowp> lowp_u8mat2x3; + typedef mat<2, 4, uint8, lowp> lowp_u8mat2x4; + typedef mat<3, 2, uint8, lowp> lowp_u8mat3x2; + typedef mat<3, 3, uint8, lowp> lowp_u8mat3x3; + typedef mat<3, 4, uint8, lowp> lowp_u8mat3x4; + typedef mat<4, 2, uint8, lowp> lowp_u8mat4x2; + typedef mat<4, 3, uint8, lowp> lowp_u8mat4x3; + typedef mat<4, 4, uint8, lowp> lowp_u8mat4x4; + + typedef mat<2, 2, uint8, mediump> mediump_u8mat2x2; + typedef mat<2, 3, uint8, mediump> mediump_u8mat2x3; + typedef mat<2, 4, uint8, mediump> mediump_u8mat2x4; + typedef mat<3, 2, uint8, mediump> mediump_u8mat3x2; + typedef mat<3, 3, uint8, mediump> mediump_u8mat3x3; + typedef mat<3, 4, uint8, mediump> mediump_u8mat3x4; + typedef mat<4, 2, uint8, mediump> mediump_u8mat4x2; + typedef mat<4, 3, uint8, mediump> mediump_u8mat4x3; + typedef mat<4, 4, uint8, mediump> mediump_u8mat4x4; + + typedef mat<2, 2, uint8, highp> highp_u8mat2x2; + typedef mat<2, 3, uint8, highp> highp_u8mat2x3; + typedef mat<2, 4, uint8, highp> highp_u8mat2x4; + typedef mat<3, 2, uint8, highp> highp_u8mat3x2; + typedef mat<3, 3, uint8, highp> highp_u8mat3x3; + typedef mat<3, 4, uint8, highp> highp_u8mat3x4; + typedef mat<4, 2, uint8, highp> highp_u8mat4x2; + typedef mat<4, 3, uint8, highp> highp_u8mat4x3; + typedef mat<4, 4, uint8, highp> highp_u8mat4x4; + + typedef mat<2, 2, uint8, defaultp> u8mat2x2; + typedef mat<2, 3, uint8, defaultp> u8mat2x3; + typedef mat<2, 4, uint8, defaultp> u8mat2x4; + typedef mat<3, 2, uint8, defaultp> u8mat3x2; + typedef mat<3, 3, uint8, defaultp> u8mat3x3; + typedef mat<3, 4, uint8, defaultp> u8mat3x4; + typedef mat<4, 2, uint8, defaultp> u8mat4x2; + typedef mat<4, 3, uint8, defaultp> u8mat4x3; + typedef mat<4, 4, uint8, defaultp> u8mat4x4; + + + typedef mat<2, 2, uint16, lowp> lowp_u16mat2x2; + typedef mat<2, 3, uint16, lowp> lowp_u16mat2x3; + typedef mat<2, 4, uint16, lowp> lowp_u16mat2x4; + typedef mat<3, 2, uint16, lowp> lowp_u16mat3x2; + typedef mat<3, 3, uint16, lowp> lowp_u16mat3x3; + typedef mat<3, 4, uint16, lowp> lowp_u16mat3x4; + typedef mat<4, 2, uint16, lowp> lowp_u16mat4x2; + typedef mat<4, 3, uint16, lowp> lowp_u16mat4x3; + typedef mat<4, 4, uint16, lowp> lowp_u16mat4x4; + + typedef mat<2, 2, uint16, mediump> mediump_u16mat2x2; + typedef mat<2, 3, uint16, mediump> mediump_u16mat2x3; + typedef mat<2, 4, uint16, mediump> mediump_u16mat2x4; + typedef mat<3, 2, uint16, mediump> mediump_u16mat3x2; + typedef mat<3, 3, uint16, mediump> mediump_u16mat3x3; + typedef mat<3, 4, uint16, mediump> mediump_u16mat3x4; + typedef mat<4, 2, uint16, mediump> mediump_u16mat4x2; + typedef mat<4, 3, uint16, mediump> mediump_u16mat4x3; + typedef mat<4, 4, uint16, mediump> mediump_u16mat4x4; + + typedef mat<2, 2, uint16, highp> highp_u16mat2x2; + typedef mat<2, 3, uint16, highp> highp_u16mat2x3; + typedef mat<2, 4, uint16, highp> highp_u16mat2x4; + typedef mat<3, 2, uint16, highp> highp_u16mat3x2; + typedef mat<3, 3, uint16, highp> highp_u16mat3x3; + typedef mat<3, 4, uint16, highp> highp_u16mat3x4; + typedef mat<4, 2, uint16, highp> highp_u16mat4x2; + typedef mat<4, 3, uint16, highp> highp_u16mat4x3; + typedef mat<4, 4, uint16, highp> highp_u16mat4x4; + + typedef mat<2, 2, uint16, defaultp> u16mat2x2; + typedef mat<2, 3, uint16, defaultp> u16mat2x3; + typedef mat<2, 4, uint16, defaultp> u16mat2x4; + typedef mat<3, 2, uint16, defaultp> u16mat3x2; + typedef mat<3, 3, uint16, defaultp> u16mat3x3; + typedef mat<3, 4, uint16, defaultp> u16mat3x4; + typedef mat<4, 2, uint16, defaultp> u16mat4x2; + typedef mat<4, 3, uint16, defaultp> u16mat4x3; + typedef mat<4, 4, uint16, defaultp> u16mat4x4; + + + typedef mat<2, 2, uint32, lowp> lowp_u32mat2x2; + typedef mat<2, 3, uint32, lowp> lowp_u32mat2x3; + typedef mat<2, 4, uint32, lowp> lowp_u32mat2x4; + typedef mat<3, 2, uint32, lowp> lowp_u32mat3x2; + typedef mat<3, 3, uint32, lowp> lowp_u32mat3x3; + typedef mat<3, 4, uint32, lowp> lowp_u32mat3x4; + typedef mat<4, 2, uint32, lowp> lowp_u32mat4x2; + typedef mat<4, 3, uint32, lowp> lowp_u32mat4x3; + typedef mat<4, 4, uint32, lowp> lowp_u32mat4x4; + + typedef mat<2, 2, uint32, mediump> mediump_u32mat2x2; + typedef mat<2, 3, uint32, mediump> mediump_u32mat2x3; + typedef mat<2, 4, uint32, mediump> mediump_u32mat2x4; + typedef mat<3, 2, uint32, mediump> mediump_u32mat3x2; + typedef mat<3, 3, uint32, mediump> mediump_u32mat3x3; + typedef mat<3, 4, uint32, mediump> mediump_u32mat3x4; + typedef mat<4, 2, uint32, mediump> mediump_u32mat4x2; + typedef mat<4, 3, uint32, mediump> mediump_u32mat4x3; + typedef mat<4, 4, uint32, mediump> mediump_u32mat4x4; + + typedef mat<2, 2, uint32, highp> highp_u32mat2x2; + typedef mat<2, 3, uint32, highp> highp_u32mat2x3; + typedef mat<2, 4, uint32, highp> highp_u32mat2x4; + typedef mat<3, 2, uint32, highp> highp_u32mat3x2; + typedef mat<3, 3, uint32, highp> highp_u32mat3x3; + typedef mat<3, 4, uint32, highp> highp_u32mat3x4; + typedef mat<4, 2, uint32, highp> highp_u32mat4x2; + typedef mat<4, 3, uint32, highp> highp_u32mat4x3; + typedef mat<4, 4, uint32, highp> highp_u32mat4x4; + + typedef mat<2, 2, uint32, defaultp> u32mat2x2; + typedef mat<2, 3, uint32, defaultp> u32mat2x3; + typedef mat<2, 4, uint32, defaultp> u32mat2x4; + typedef mat<3, 2, uint32, defaultp> u32mat3x2; + typedef mat<3, 3, uint32, defaultp> u32mat3x3; + typedef mat<3, 4, uint32, defaultp> u32mat3x4; + typedef mat<4, 2, uint32, defaultp> u32mat4x2; + typedef mat<4, 3, uint32, defaultp> u32mat4x3; + typedef mat<4, 4, uint32, defaultp> u32mat4x4; + + + typedef mat<2, 2, uint64, lowp> lowp_u64mat2x2; + typedef mat<2, 3, uint64, lowp> lowp_u64mat2x3; + typedef mat<2, 4, uint64, lowp> lowp_u64mat2x4; + typedef mat<3, 2, uint64, lowp> lowp_u64mat3x2; + typedef mat<3, 3, uint64, lowp> lowp_u64mat3x3; + typedef mat<3, 4, uint64, lowp> lowp_u64mat3x4; + typedef mat<4, 2, uint64, lowp> lowp_u64mat4x2; + typedef mat<4, 3, uint64, lowp> lowp_u64mat4x3; + typedef mat<4, 4, uint64, lowp> lowp_u64mat4x4; + + typedef mat<2, 2, uint64, mediump> mediump_u64mat2x2; + typedef mat<2, 3, uint64, mediump> mediump_u64mat2x3; + typedef mat<2, 4, uint64, mediump> mediump_u64mat2x4; + typedef mat<3, 2, uint64, mediump> mediump_u64mat3x2; + typedef mat<3, 3, uint64, mediump> mediump_u64mat3x3; + typedef mat<3, 4, uint64, mediump> mediump_u64mat3x4; + typedef mat<4, 2, uint64, mediump> mediump_u64mat4x2; + typedef mat<4, 3, uint64, mediump> mediump_u64mat4x3; + typedef mat<4, 4, uint64, mediump> mediump_u64mat4x4; + + typedef mat<2, 2, uint64, highp> highp_u64mat2x2; + typedef mat<2, 3, uint64, highp> highp_u64mat2x3; + typedef mat<2, 4, uint64, highp> highp_u64mat2x4; + typedef mat<3, 2, uint64, highp> highp_u64mat3x2; + typedef mat<3, 3, uint64, highp> highp_u64mat3x3; + typedef mat<3, 4, uint64, highp> highp_u64mat3x4; + typedef mat<4, 2, uint64, highp> highp_u64mat4x2; + typedef mat<4, 3, uint64, highp> highp_u64mat4x3; + typedef mat<4, 4, uint64, highp> highp_u64mat4x4; + + typedef mat<2, 2, uint64, defaultp> u64mat2x2; + typedef mat<2, 3, uint64, defaultp> u64mat2x3; + typedef mat<2, 4, uint64, defaultp> u64mat2x4; + typedef mat<3, 2, uint64, defaultp> u64mat3x2; + typedef mat<3, 3, uint64, defaultp> u64mat3x3; + typedef mat<3, 4, uint64, defaultp> u64mat3x4; + typedef mat<4, 2, uint64, defaultp> u64mat4x2; + typedef mat<4, 3, uint64, defaultp> u64mat4x3; + typedef mat<4, 4, uint64, defaultp> u64mat4x4; + + // Quaternion + + typedef qua lowp_quat; + typedef qua mediump_quat; + typedef qua highp_quat; + typedef qua quat; + + typedef qua lowp_fquat; + typedef qua mediump_fquat; + typedef qua highp_fquat; + typedef qua fquat; + + typedef qua lowp_f32quat; + typedef qua mediump_f32quat; + typedef qua highp_f32quat; + typedef qua f32quat; + + typedef qua lowp_dquat; + typedef qua mediump_dquat; + typedef qua highp_dquat; + typedef qua dquat; + + typedef qua lowp_f64quat; + typedef qua mediump_f64quat; + typedef qua highp_f64quat; + typedef qua f64quat; +}//namespace glm + + diff --git a/thirdparty/glm/glm/geometric.hpp b/thirdparty/glm/glm/geometric.hpp new file mode 100644 index 0000000..ac857e6 --- /dev/null +++ b/thirdparty/glm/glm/geometric.hpp @@ -0,0 +1,116 @@ +/// @ref core +/// @file glm/geometric.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions +/// +/// @defgroup core_func_geometric Geometric functions +/// @ingroup core +/// +/// These operate on vectors as vectors, not component-wise. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/type_vec3.hpp" + +namespace glm +{ + /// @addtogroup core_func_geometric + /// @{ + + /// Returns the length of x, i.e., sqrt(x * x). + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL length man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL T length(vec const& x); + + /// Returns the distance between p0 and p1, i.e., length(p0 - p1). + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL distance man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL T distance(vec const& p0, vec const& p1); + + /// Returns the dot product of x and y, i.e., result = x * y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL dot man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR T dot(vec const& x, vec const& y); + + /// Returns the cross product of x and y. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL cross man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> cross(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + /// Returns a vector in the same direction as x but with length of 1. + /// According to issue 10 GLSL 1.10 specification, if length(x) == 0 then result is undefined and generate an error. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL normalize man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec normalize(vec const& x); + + /// If dot(Nref, I) < 0.0, return N, otherwise, return -N. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL faceforward man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec faceforward( + vec const& N, + vec const& I, + vec const& Nref); + + /// For the incident vector I and surface orientation N, + /// returns the reflection direction : result = I - 2.0 * dot(N, I) * N. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL reflect man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec reflect( + vec const& I, + vec const& N); + + /// For the incident vector I and surface normal N, + /// and the ratio of indices of refraction eta, + /// return the refraction vector. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Floating-point scalar types. + /// + /// @see GLSL refract man page + /// @see GLSL 4.20.8 specification, section 8.5 Geometric Functions + template + GLM_FUNC_DECL vec refract( + vec const& I, + vec const& N, + T eta); + + /// @} +}//namespace glm + +#include "detail/func_geometric.inl" diff --git a/thirdparty/glm/glm/glm.cppm b/thirdparty/glm/glm/glm.cppm new file mode 100644 index 0000000..85e946e --- /dev/null +++ b/thirdparty/glm/glm/glm.cppm @@ -0,0 +1,2675 @@ +module; + +// #define GLM_GTC_INLINE_NAMESPACE to inline glm::gtc into glm +// #define GLM_EXT_INLINE_NAMESPACE to inline glm::ext into glm +// #define GLM_GTX_INLINE_NAMESPACE to inline glm::gtx into glm + +#include +#include + +export module glm; + +export namespace glm { + // Base types + using glm::qualifier; + using glm::precision; + using glm::vec; + using glm::mat; + using glm::qua; +# if GLM_HAS_TEMPLATE_ALIASES + using glm::tvec1; + using glm::tvec2; + using glm::tvec3; + using glm::tvec4; + using glm::tmat2x2; + using glm::tmat2x3; + using glm::tmat2x4; + using glm::tmat3x2; + using glm::tmat3x3; + using glm::tmat3x4; + using glm::tmat4x2; + using glm::tmat4x3; + using glm::tmat4x4; + using glm::tquat; +# endif + + using glm::int8; + using glm::int16; + using glm::int32; + using glm::int64; + using glm::uint8; + using glm::uint16; + using glm::uint32; + using glm::uint64; + using glm::lowp_i8; + using glm::mediump_i8; + using glm::highp_i8; + using glm::i8; + using glm::lowp_int8; + using glm::mediump_int8; + using glm::highp_int8; + using glm::lowp_int8_t; + using glm::mediump_int8_t; + using glm::highp_int8_t; + using glm::int8_t; + using glm::lowp_i16; + using glm::mediump_i16; + using glm::highp_i16; + using glm::i16; + using glm::lowp_int16; + using glm::mediump_int16; + using glm::highp_int16; + using glm::lowp_int16_t; + using glm::mediump_int16_t; + using glm::highp_int16_t; + using glm::int16_t; + using glm::lowp_i32; + using glm::mediump_i32; + using glm::highp_i32; + using glm::i32; + using glm::lowp_int32; + using glm::mediump_int32; + using glm::highp_int32; + using glm::lowp_int32_t; + using glm::mediump_int32_t; + using glm::highp_int32_t; + using glm::int32_t; + using glm::lowp_i64; + using glm::mediump_i64; + using glm::highp_i64; + using glm::i64; + using glm::lowp_int64; + using glm::mediump_int64; + using glm::highp_int64; + using glm::lowp_int64_t; + using glm::mediump_int64_t; + using glm::highp_int64_t; + using glm::int64_t; + using glm::uint; + using glm::lowp_u8; + using glm::mediump_u8; + using glm::highp_u8; + using glm::u8; + using glm::lowp_uint8; + using glm::mediump_uint8; + using glm::highp_uint8; + using glm::lowp_uint8_t; + using glm::mediump_uint8_t; + using glm::highp_uint8_t; + using glm::uint8_t; + using glm::lowp_u16; + using glm::mediump_u16; + using glm::highp_u16; + using glm::u16; + using glm::lowp_uint16; + using glm::mediump_uint16; + using glm::highp_uint16; + using glm::lowp_uint16_t; + using glm::mediump_uint16_t; + using glm::highp_uint16_t; + using glm::uint16_t; + using glm::lowp_u32; + using glm::mediump_u32; + using glm::highp_u32; + using glm::u32; + using glm::lowp_uint32; + using glm::mediump_uint32; + using glm::highp_uint32; + using glm::lowp_uint32_t; + using glm::mediump_uint32_t; + using glm::highp_uint32_t; + using glm::uint32_t; + using glm::lowp_u64; + using glm::mediump_u64; + using glm::highp_u64; + using glm::u64; + using glm::lowp_uint64; + using glm::mediump_uint64; + using glm::highp_uint64; + using glm::lowp_uint64_t; + using glm::mediump_uint64_t; + using glm::highp_uint64_t; + using glm::uint64_t; + using glm::lowp_f32; + using glm::mediump_f32; + using glm::highp_f32; + using glm::f32; + using glm::lowp_float32; + using glm::mediump_float32; + using glm::highp_float32; + using glm::float32; + using glm::lowp_float32_t; + using glm::mediump_float32_t; + using glm::highp_float32_t; + using glm::float32_t; + using glm::lowp_f64; + using glm::mediump_f64; + using glm::highp_f64; + using glm::f64; + using glm::lowp_float64; + using glm::mediump_float64; + using glm::highp_float64; + using glm::float64; + using glm::lowp_float64_t; + using glm::mediump_float64_t; + using glm::highp_float64_t; + using glm::float64_t; + using glm::lowp_bvec1; + using glm::lowp_bvec2; + using glm::lowp_bvec3; + using glm::lowp_bvec4; + using glm::mediump_bvec1; + using glm::mediump_bvec2; + using glm::mediump_bvec3; + using glm::mediump_bvec4; + using glm::highp_bvec1; + using glm::highp_bvec2; + using glm::highp_bvec3; + using glm::highp_bvec4; + using glm::bvec1; + using glm::bvec2; + using glm::bvec3; + using glm::bvec4; + using glm::lowp_ivec1; + using glm::lowp_ivec2; + using glm::lowp_ivec3; + using glm::lowp_ivec4; + using glm::mediump_ivec1; + using glm::mediump_ivec2; + using glm::mediump_ivec3; + using glm::mediump_ivec4; + using glm::highp_ivec1; + using glm::highp_ivec2; + using glm::highp_ivec3; + using glm::highp_ivec4; + using glm::ivec1; + using glm::ivec2; + using glm::ivec3; + using glm::ivec4; + using glm::lowp_i8vec1; + using glm::lowp_i8vec2; + using glm::lowp_i8vec3; + using glm::lowp_i8vec4; + using glm::mediump_i8vec1; + using glm::mediump_i8vec2; + using glm::mediump_i8vec3; + using glm::mediump_i8vec4; + using glm::highp_i8vec1; + using glm::highp_i8vec2; + using glm::highp_i8vec3; + using glm::highp_i8vec4; + using glm::i8vec1; + using glm::i8vec2; + using glm::i8vec3; + using glm::i8vec4; + using glm::lowp_i16vec1; + using glm::lowp_i16vec2; + using glm::lowp_i16vec3; + using glm::lowp_i16vec4; + using glm::mediump_i16vec1; + using glm::mediump_i16vec2; + using glm::mediump_i16vec3; + using glm::mediump_i16vec4; + using glm::highp_i16vec1; + using glm::highp_i16vec2; + using glm::highp_i16vec3; + using glm::highp_i16vec4; + using glm::i16vec1; + using glm::i16vec2; + using glm::i16vec3; + using glm::i16vec4; + using glm::lowp_i32vec1; + using glm::lowp_i32vec2; + using glm::lowp_i32vec3; + using glm::lowp_i32vec4; + using glm::mediump_i32vec1; + using glm::mediump_i32vec2; + using glm::mediump_i32vec3; + using glm::mediump_i32vec4; + using glm::highp_i32vec1; + using glm::highp_i32vec2; + using glm::highp_i32vec3; + using glm::highp_i32vec4; + using glm::i32vec1; + using glm::i32vec2; + using glm::i32vec3; + using glm::i32vec4; + using glm::lowp_i64vec1; + using glm::lowp_i64vec2; + using glm::lowp_i64vec3; + using glm::lowp_i64vec4; + using glm::mediump_i64vec1; + using glm::mediump_i64vec2; + using glm::mediump_i64vec3; + using glm::mediump_i64vec4; + using glm::highp_i64vec1; + using glm::highp_i64vec2; + using glm::highp_i64vec3; + using glm::highp_i64vec4; + using glm::i64vec1; + using glm::i64vec2; + using glm::i64vec3; + using glm::i64vec4; + using glm::lowp_uvec1; + using glm::lowp_uvec2; + using glm::lowp_uvec3; + using glm::lowp_uvec4; + using glm::mediump_uvec1; + using glm::mediump_uvec2; + using glm::mediump_uvec3; + using glm::mediump_uvec4; + using glm::highp_uvec1; + using glm::highp_uvec2; + using glm::highp_uvec3; + using glm::highp_uvec4; + using glm::uvec1; + using glm::uvec2; + using glm::uvec3; + using glm::uvec4; + using glm::lowp_u8vec1; + using glm::lowp_u8vec2; + using glm::lowp_u8vec3; + using glm::lowp_u8vec4; + using glm::mediump_u8vec1; + using glm::mediump_u8vec2; + using glm::mediump_u8vec3; + using glm::mediump_u8vec4; + using glm::highp_u8vec1; + using glm::highp_u8vec2; + using glm::highp_u8vec3; + using glm::highp_u8vec4; + using glm::u8vec1; + using glm::u8vec2; + using glm::u8vec3; + using glm::u8vec4; + using glm::lowp_u16vec1; + using glm::lowp_u16vec2; + using glm::lowp_u16vec3; + using glm::lowp_u16vec4; + using glm::mediump_u16vec1; + using glm::mediump_u16vec2; + using glm::mediump_u16vec3; + using glm::mediump_u16vec4; + using glm::highp_u16vec1; + using glm::highp_u16vec2; + using glm::highp_u16vec3; + using glm::highp_u16vec4; + using glm::u16vec1; + using glm::u16vec2; + using glm::u16vec3; + using glm::u16vec4; + using glm::lowp_u32vec1; + using glm::lowp_u32vec2; + using glm::lowp_u32vec3; + using glm::lowp_u32vec4; + using glm::mediump_u32vec1; + using glm::mediump_u32vec2; + using glm::mediump_u32vec3; + using glm::mediump_u32vec4; + using glm::highp_u32vec1; + using glm::highp_u32vec2; + using glm::highp_u32vec3; + using glm::highp_u32vec4; + using glm::u32vec1; + using glm::u32vec2; + using glm::u32vec3; + using glm::u32vec4; + using glm::lowp_u64vec1; + using glm::lowp_u64vec2; + using glm::lowp_u64vec3; + using glm::lowp_u64vec4; + using glm::mediump_u64vec1; + using glm::mediump_u64vec2; + using glm::mediump_u64vec3; + using glm::mediump_u64vec4; + using glm::highp_u64vec1; + using glm::highp_u64vec2; + using glm::highp_u64vec3; + using glm::highp_u64vec4; + using glm::u64vec1; + using glm::u64vec2; + using glm::u64vec3; + using glm::u64vec4; + using glm::lowp_vec1; + using glm::lowp_vec2; + using glm::lowp_vec3; + using glm::lowp_vec4; + using glm::mediump_vec1; + using glm::mediump_vec2; + using glm::mediump_vec3; + using glm::mediump_vec4; + using glm::highp_vec1; + using glm::highp_vec2; + using glm::highp_vec3; + using glm::highp_vec4; + using glm::vec1; + using glm::vec2; + using glm::vec3; + using glm::vec4; + using glm::lowp_fvec1; + using glm::lowp_fvec2; + using glm::lowp_fvec3; + using glm::lowp_fvec4; + using glm::mediump_fvec1; + using glm::mediump_fvec2; + using glm::mediump_fvec3; + using glm::mediump_fvec4; + using glm::highp_fvec1; + using glm::highp_fvec2; + using glm::highp_fvec3; + using glm::highp_fvec4; + using glm::fvec1; + using glm::fvec2; + using glm::fvec3; + using glm::fvec4; + using glm::lowp_f32vec1; + using glm::lowp_f32vec2; + using glm::lowp_f32vec3; + using glm::lowp_f32vec4; + using glm::mediump_f32vec1; + using glm::mediump_f32vec2; + using glm::mediump_f32vec3; + using glm::mediump_f32vec4; + using glm::highp_f32vec1; + using glm::highp_f32vec2; + using glm::highp_f32vec3; + using glm::highp_f32vec4; + using glm::f32vec1; + using glm::f32vec2; + using glm::f32vec3; + using glm::f32vec4; + using glm::lowp_dvec1; + using glm::lowp_dvec2; + using glm::lowp_dvec3; + using glm::lowp_dvec4; + using glm::mediump_dvec1; + using glm::mediump_dvec2; + using glm::mediump_dvec3; + using glm::mediump_dvec4; + using glm::highp_dvec1; + using glm::highp_dvec2; + using glm::highp_dvec3; + using glm::highp_dvec4; + using glm::dvec1; + using glm::dvec2; + using glm::dvec3; + using glm::dvec4; + using glm::lowp_f64vec1; + using glm::lowp_f64vec2; + using glm::lowp_f64vec3; + using glm::lowp_f64vec4; + using glm::mediump_f64vec1; + using glm::mediump_f64vec2; + using glm::mediump_f64vec3; + using glm::mediump_f64vec4; + using glm::highp_f64vec1; + using glm::highp_f64vec2; + using glm::highp_f64vec3; + using glm::highp_f64vec4; + using glm::f64vec1; + using glm::f64vec2; + using glm::f64vec3; + using glm::f64vec4; + using glm::lowp_mat2; + using glm::lowp_mat3; + using glm::lowp_mat4; + using glm::mediump_mat2; + using glm::mediump_mat3; + using glm::mediump_mat4; + using glm::highp_mat2; + using glm::highp_mat3; + using glm::highp_mat4; + using glm::mat2; + using glm::mat3; + using glm::mat4; + using glm::lowp_fmat2; + using glm::lowp_fmat3; + using glm::lowp_fmat4; + using glm::mediump_fmat2; + using glm::mediump_fmat3; + using glm::mediump_fmat4; + using glm::highp_fmat2; + using glm::highp_fmat3; + using glm::highp_fmat4; + using glm::fmat2; + using glm::fmat3; + using glm::fmat4; + using glm::lowp_f32mat2; + using glm::lowp_f32mat3; + using glm::lowp_f32mat4; + using glm::mediump_f32mat2; + using glm::mediump_f32mat3; + using glm::mediump_f32mat4; + using glm::highp_f32mat2; + using glm::highp_f32mat3; + using glm::highp_f32mat4; + using glm::f32mat2; + using glm::f32mat3; + using glm::f32mat4; + using glm::lowp_dmat2; + using glm::lowp_dmat3; + using glm::lowp_dmat4; + using glm::mediump_dmat2; + using glm::mediump_dmat3; + using glm::mediump_dmat4; + using glm::highp_dmat2; + using glm::highp_dmat3; + using glm::highp_dmat4; + using glm::dmat2; + using glm::dmat3; + using glm::dmat4; + using glm::lowp_f64mat2; + using glm::lowp_f64mat3; + using glm::lowp_f64mat4; + using glm::mediump_f64mat2; + using glm::mediump_f64mat3; + using glm::mediump_f64mat4; + using glm::highp_f64mat2; + using glm::highp_f64mat3; + using glm::highp_f64mat4; + using glm::f64mat2; + using glm::f64mat3; + using glm::f64mat4; + using glm::lowp_mat2x2; + using glm::lowp_mat2x3; + using glm::lowp_mat2x4; + using glm::lowp_mat3x2; + using glm::lowp_mat3x3; + using glm::lowp_mat3x4; + using glm::lowp_mat4x2; + using glm::lowp_mat4x3; + using glm::lowp_mat4x4; + using glm::mediump_mat2x2; + using glm::mediump_mat2x3; + using glm::mediump_mat2x4; + using glm::mediump_mat3x2; + using glm::mediump_mat3x3; + using glm::mediump_mat3x4; + using glm::mediump_mat4x2; + using glm::mediump_mat4x3; + using glm::mediump_mat4x4; + using glm::highp_mat2x2; + using glm::highp_mat2x3; + using glm::highp_mat2x4; + using glm::highp_mat3x2; + using glm::highp_mat3x3; + using glm::highp_mat3x4; + using glm::highp_mat4x2; + using glm::highp_mat4x3; + using glm::highp_mat4x4; + using glm::mat2x2; + using glm::mat2x3; + using glm::mat2x4; + using glm::mat3x2; + using glm::mat3x3; + using glm::mat3x4; + using glm::mat4x2; + using glm::mat4x3; + using glm::mat4x4; + using glm::lowp_fmat2x2; + using glm::lowp_fmat2x3; + using glm::lowp_fmat2x4; + using glm::lowp_fmat3x2; + using glm::lowp_fmat3x3; + using glm::lowp_fmat3x4; + using glm::lowp_fmat4x2; + using glm::lowp_fmat4x3; + using glm::lowp_fmat4x4; + using glm::mediump_fmat2x2; + using glm::mediump_fmat2x3; + using glm::mediump_fmat2x4; + using glm::mediump_fmat3x2; + using glm::mediump_fmat3x3; + using glm::mediump_fmat3x4; + using glm::mediump_fmat4x2; + using glm::mediump_fmat4x3; + using glm::mediump_fmat4x4; + using glm::highp_fmat2x2; + using glm::highp_fmat2x3; + using glm::highp_fmat2x4; + using glm::highp_fmat3x2; + using glm::highp_fmat3x3; + using glm::highp_fmat3x4; + using glm::highp_fmat4x2; + using glm::highp_fmat4x3; + using glm::highp_fmat4x4; + using glm::fmat2x2; + using glm::fmat2x3; + using glm::fmat2x4; + using glm::fmat3x2; + using glm::fmat3x3; + using glm::fmat3x4; + using glm::fmat4x2; + using glm::fmat4x3; + using glm::fmat4x4; + using glm::lowp_f32mat2x2; + using glm::lowp_f32mat2x3; + using glm::lowp_f32mat2x4; + using glm::lowp_f32mat3x2; + using glm::lowp_f32mat3x3; + using glm::lowp_f32mat3x4; + using glm::lowp_f32mat4x2; + using glm::lowp_f32mat4x3; + using glm::lowp_f32mat4x4; + + using glm::mediump_f32mat2x2; + using glm::mediump_f32mat2x3; + using glm::mediump_f32mat2x4; + using glm::mediump_f32mat3x2; + using glm::mediump_f32mat3x3; + using glm::mediump_f32mat3x4; + using glm::mediump_f32mat4x2; + using glm::mediump_f32mat4x3; + using glm::mediump_f32mat4x4; + using glm::highp_f32mat2x2; + using glm::highp_f32mat2x3; + using glm::highp_f32mat2x4; + using glm::highp_f32mat3x2; + using glm::highp_f32mat3x3; + using glm::highp_f32mat3x4; + using glm::highp_f32mat4x2; + using glm::highp_f32mat4x3; + using glm::highp_f32mat4x4; + using glm::f32mat2x2; + using glm::f32mat2x3; + using glm::f32mat2x4; + using glm::f32mat3x2; + using glm::f32mat3x3; + using glm::f32mat3x4; + using glm::f32mat4x2; + using glm::f32mat4x3; + using glm::f32mat4x4; + using glm::lowp_dmat2x2; + using glm::lowp_dmat2x3; + using glm::lowp_dmat2x4; + using glm::lowp_dmat3x2; + using glm::lowp_dmat3x3; + using glm::lowp_dmat3x4; + using glm::lowp_dmat4x2; + using glm::lowp_dmat4x3; + using glm::lowp_dmat4x4; + using glm::mediump_dmat2x2; + using glm::mediump_dmat2x3; + using glm::mediump_dmat2x4; + using glm::mediump_dmat3x2; + using glm::mediump_dmat3x3; + using glm::mediump_dmat3x4; + using glm::mediump_dmat4x2; + using glm::mediump_dmat4x3; + using glm::mediump_dmat4x4; + using glm::highp_dmat2x2; + using glm::highp_dmat2x3; + using glm::highp_dmat2x4; + using glm::highp_dmat3x2; + using glm::highp_dmat3x3; + using glm::highp_dmat3x4; + using glm::highp_dmat4x2; + using glm::highp_dmat4x3; + using glm::highp_dmat4x4; + using glm::dmat2x2; + using glm::dmat2x3; + using glm::dmat2x4; + using glm::dmat3x2; + using glm::dmat3x3; + using glm::dmat3x4; + using glm::dmat4x2; + using glm::dmat4x3; + using glm::dmat4x4; + using glm::lowp_f64mat2x2; + using glm::lowp_f64mat2x3; + using glm::lowp_f64mat2x4; + using glm::lowp_f64mat3x2; + using glm::lowp_f64mat3x3; + using glm::lowp_f64mat3x4; + using glm::lowp_f64mat4x2; + using glm::lowp_f64mat4x3; + using glm::lowp_f64mat4x4; + using glm::mediump_f64mat2x2; + using glm::mediump_f64mat2x3; + using glm::mediump_f64mat2x4; + using glm::mediump_f64mat3x2; + using glm::mediump_f64mat3x3; + using glm::mediump_f64mat3x4; + using glm::mediump_f64mat4x2; + using glm::mediump_f64mat4x3; + using glm::mediump_f64mat4x4; + using glm::highp_f64mat2x2; + using glm::highp_f64mat2x3; + using glm::highp_f64mat2x4; + using glm::highp_f64mat3x2; + using glm::highp_f64mat3x3; + using glm::highp_f64mat3x4; + using glm::highp_f64mat4x2; + using glm::highp_f64mat4x3; + using glm::highp_f64mat4x4; + using glm::f64mat2x2; + using glm::f64mat2x3; + using glm::f64mat2x4; + using glm::f64mat3x2; + using glm::f64mat3x3; + using glm::f64mat3x4; + using glm::f64mat4x2; + using glm::f64mat4x3; + using glm::f64mat4x4; + using glm::lowp_imat2x2; + using glm::lowp_imat2x3; + using glm::lowp_imat2x4; + using glm::lowp_imat3x2; + using glm::lowp_imat3x3; + using glm::lowp_imat3x4; + using glm::lowp_imat4x2; + using glm::lowp_imat4x3; + using glm::lowp_imat4x4; + using glm::mediump_imat2x2; + using glm::mediump_imat2x3; + using glm::mediump_imat2x4; + using glm::mediump_imat3x2; + using glm::mediump_imat3x3; + using glm::mediump_imat3x4; + using glm::mediump_imat4x2; + using glm::mediump_imat4x3; + using glm::mediump_imat4x4; + using glm::highp_imat2x2; + using glm::highp_imat2x3; + using glm::highp_imat2x4; + using glm::highp_imat3x2; + using glm::highp_imat3x3; + using glm::highp_imat3x4; + using glm::highp_imat4x2; + using glm::highp_imat4x3; + using glm::highp_imat4x4; + using glm::imat2x2; + using glm::imat2x3; + using glm::imat2x4; + using glm::imat3x2; + using glm::imat3x3; + using glm::imat3x4; + using glm::imat4x2; + using glm::imat4x3; + using glm::imat4x4; + using glm::lowp_i8mat2x2; + using glm::lowp_i8mat2x3; + using glm::lowp_i8mat2x4; + using glm::lowp_i8mat3x2; + using glm::lowp_i8mat3x3; + using glm::lowp_i8mat3x4; + using glm::lowp_i8mat4x2; + using glm::lowp_i8mat4x3; + using glm::lowp_i8mat4x4; + using glm::mediump_i8mat2x2; + using glm::mediump_i8mat2x3; + using glm::mediump_i8mat2x4; + using glm::mediump_i8mat3x2; + using glm::mediump_i8mat3x3; + using glm::mediump_i8mat3x4; + using glm::mediump_i8mat4x2; + using glm::mediump_i8mat4x3; + using glm::mediump_i8mat4x4; + using glm::highp_i8mat2x2; + using glm::highp_i8mat2x3; + using glm::highp_i8mat2x4; + using glm::highp_i8mat3x2; + using glm::highp_i8mat3x3; + using glm::highp_i8mat3x4; + using glm::highp_i8mat4x2; + using glm::highp_i8mat4x3; + using glm::highp_i8mat4x4; + using glm::i8mat2x2; + using glm::i8mat2x3; + using glm::i8mat2x4; + using glm::i8mat3x2; + using glm::i8mat3x3; + using glm::i8mat3x4; + using glm::i8mat4x2; + using glm::i8mat4x3; + using glm::i8mat4x4; + using glm::lowp_i16mat2x2; + using glm::lowp_i16mat2x3; + using glm::lowp_i16mat2x4; + using glm::lowp_i16mat3x2; + using glm::lowp_i16mat3x3; + using glm::lowp_i16mat3x4; + using glm::lowp_i16mat4x2; + using glm::lowp_i16mat4x3; + using glm::lowp_i16mat4x4; + using glm::mediump_i16mat2x2; + using glm::mediump_i16mat2x3; + using glm::mediump_i16mat2x4; + using glm::mediump_i16mat3x2; + using glm::mediump_i16mat3x3; + using glm::mediump_i16mat3x4; + using glm::mediump_i16mat4x2; + using glm::mediump_i16mat4x3; + using glm::mediump_i16mat4x4; + using glm::highp_i16mat2x2; + using glm::highp_i16mat2x3; + using glm::highp_i16mat2x4; + using glm::highp_i16mat3x2; + using glm::highp_i16mat3x3; + using glm::highp_i16mat3x4; + using glm::highp_i16mat4x2; + using glm::highp_i16mat4x3; + using glm::highp_i16mat4x4; + using glm::i16mat2x2; + using glm::i16mat2x3; + using glm::i16mat2x4; + using glm::i16mat3x2; + using glm::i16mat3x3; + using glm::i16mat3x4; + using glm::i16mat4x2; + using glm::i16mat4x3; + using glm::i16mat4x4; + using glm::lowp_i32mat2x2; + using glm::lowp_i32mat2x3; + using glm::lowp_i32mat2x4; + using glm::lowp_i32mat3x2; + using glm::lowp_i32mat3x3; + using glm::lowp_i32mat3x4; + using glm::lowp_i32mat4x2; + using glm::lowp_i32mat4x3; + using glm::lowp_i32mat4x4; + using glm::mediump_i32mat2x2; + using glm::mediump_i32mat2x3; + using glm::mediump_i32mat2x4; + using glm::mediump_i32mat3x2; + using glm::mediump_i32mat3x3; + using glm::mediump_i32mat3x4; + using glm::mediump_i32mat4x2; + using glm::mediump_i32mat4x3; + using glm::mediump_i32mat4x4; + using glm::highp_i32mat2x2; + using glm::highp_i32mat2x3; + using glm::highp_i32mat2x4; + using glm::highp_i32mat3x2; + using glm::highp_i32mat3x3; + using glm::highp_i32mat3x4; + using glm::highp_i32mat4x2; + using glm::highp_i32mat4x3; + using glm::highp_i32mat4x4; + using glm::i32mat2x2; + using glm::i32mat2x3; + using glm::i32mat2x4; + using glm::i32mat3x2; + using glm::i32mat3x3; + using glm::i32mat3x4; + using glm::i32mat4x2; + using glm::i32mat4x3; + using glm::i32mat4x4; + using glm::lowp_i64mat2x2; + using glm::lowp_i64mat2x3; + using glm::lowp_i64mat2x4; + using glm::lowp_i64mat3x2; + using glm::lowp_i64mat3x3; + using glm::lowp_i64mat3x4; + using glm::lowp_i64mat4x2; + using glm::lowp_i64mat4x3; + using glm::lowp_i64mat4x4; + using glm::mediump_i64mat2x2; + using glm::mediump_i64mat2x3; + using glm::mediump_i64mat2x4; + using glm::mediump_i64mat3x2; + using glm::mediump_i64mat3x3; + using glm::mediump_i64mat3x4; + using glm::mediump_i64mat4x2; + using glm::mediump_i64mat4x3; + using glm::mediump_i64mat4x4; + using glm::highp_i64mat2x2; + using glm::highp_i64mat2x3; + using glm::highp_i64mat2x4; + using glm::highp_i64mat3x2; + using glm::highp_i64mat3x3; + using glm::highp_i64mat3x4; + using glm::highp_i64mat4x2; + using glm::highp_i64mat4x3; + using glm::highp_i64mat4x4; + using glm::i64mat2x2; + using glm::i64mat2x3; + using glm::i64mat2x4; + using glm::i64mat3x2; + using glm::i64mat3x3; + using glm::i64mat3x4; + using glm::i64mat4x2; + using glm::i64mat4x3; + using glm::i64mat4x4; + using glm::lowp_umat2x2; + using glm::lowp_umat2x3; + using glm::lowp_umat2x4; + using glm::lowp_umat3x2; + using glm::lowp_umat3x3; + using glm::lowp_umat3x4; + using glm::lowp_umat4x2; + using glm::lowp_umat4x3; + using glm::lowp_umat4x4; + using glm::mediump_umat2x2; + using glm::mediump_umat2x3; + using glm::mediump_umat2x4; + using glm::mediump_umat3x2; + using glm::mediump_umat3x3; + using glm::mediump_umat3x4; + using glm::mediump_umat4x2; + using glm::mediump_umat4x3; + using glm::mediump_umat4x4; + using glm::highp_umat2x2; + using glm::highp_umat2x3; + using glm::highp_umat2x4; + using glm::highp_umat3x2; + using glm::highp_umat3x3; + using glm::highp_umat3x4; + using glm::highp_umat4x2; + using glm::highp_umat4x3; + using glm::highp_umat4x4; + using glm::umat2x2; + using glm::umat2x3; + using glm::umat2x4; + using glm::umat3x2; + using glm::umat3x3; + using glm::umat3x4; + using glm::umat4x2; + using glm::umat4x3; + using glm::umat4x4; + using glm::lowp_u8mat2x2; + using glm::lowp_u8mat2x3; + using glm::lowp_u8mat2x4; + using glm::lowp_u8mat3x2; + using glm::lowp_u8mat3x3; + using glm::lowp_u8mat3x4; + using glm::lowp_u8mat4x2; + using glm::lowp_u8mat4x3; + using glm::lowp_u8mat4x4; + using glm::mediump_u8mat2x2; + using glm::mediump_u8mat2x3; + using glm::mediump_u8mat2x4; + using glm::mediump_u8mat3x2; + using glm::mediump_u8mat3x3; + using glm::mediump_u8mat3x4; + using glm::mediump_u8mat4x2; + using glm::mediump_u8mat4x3; + using glm::mediump_u8mat4x4; + using glm::highp_u8mat2x2; + using glm::highp_u8mat2x3; + using glm::highp_u8mat2x4; + using glm::highp_u8mat3x2; + using glm::highp_u8mat3x3; + using glm::highp_u8mat3x4; + using glm::highp_u8mat4x2; + using glm::highp_u8mat4x3; + using glm::highp_u8mat4x4; + using glm::u8mat2x2; + using glm::u8mat2x3; + using glm::u8mat2x4; + using glm::u8mat3x2; + using glm::u8mat3x3; + using glm::u8mat3x4; + using glm::u8mat4x2; + using glm::u8mat4x3; + using glm::u8mat4x4; + using glm::lowp_u16mat2x2; + using glm::lowp_u16mat2x3; + using glm::lowp_u16mat2x4; + using glm::lowp_u16mat3x2; + using glm::lowp_u16mat3x3; + using glm::lowp_u16mat3x4; + using glm::lowp_u16mat4x2; + using glm::lowp_u16mat4x3; + using glm::lowp_u16mat4x4; + using glm::mediump_u16mat2x2; + using glm::mediump_u16mat2x3; + using glm::mediump_u16mat2x4; + using glm::mediump_u16mat3x2; + using glm::mediump_u16mat3x3; + using glm::mediump_u16mat3x4; + using glm::mediump_u16mat4x2; + using glm::mediump_u16mat4x3; + using glm::mediump_u16mat4x4; + using glm::highp_u16mat2x2; + using glm::highp_u16mat2x3; + using glm::highp_u16mat2x4; + using glm::highp_u16mat3x2; + using glm::highp_u16mat3x3; + using glm::highp_u16mat3x4; + using glm::highp_u16mat4x2; + using glm::highp_u16mat4x3; + using glm::highp_u16mat4x4; + using glm::u16mat2x2; + using glm::u16mat2x3; + using glm::u16mat2x4; + using glm::u16mat3x2; + using glm::u16mat3x3; + using glm::u16mat3x4; + using glm::u16mat4x2; + using glm::u16mat4x3; + using glm::u16mat4x4; + using glm::lowp_u32mat2x2; + using glm::lowp_u32mat2x3; + using glm::lowp_u32mat2x4; + using glm::lowp_u32mat3x2; + using glm::lowp_u32mat3x3; + using glm::lowp_u32mat3x4; + using glm::lowp_u32mat4x2; + using glm::lowp_u32mat4x3; + using glm::lowp_u32mat4x4; + using glm::mediump_u32mat2x2; + using glm::mediump_u32mat2x3; + using glm::mediump_u32mat2x4; + using glm::mediump_u32mat3x2; + using glm::mediump_u32mat3x3; + using glm::mediump_u32mat3x4; + using glm::mediump_u32mat4x2; + using glm::mediump_u32mat4x3; + using glm::mediump_u32mat4x4; + using glm::highp_u32mat2x2; + using glm::highp_u32mat2x3; + using glm::highp_u32mat2x4; + using glm::highp_u32mat3x2; + using glm::highp_u32mat3x3; + using glm::highp_u32mat3x4; + using glm::highp_u32mat4x2; + using glm::highp_u32mat4x3; + using glm::highp_u32mat4x4; + using glm::u32mat2x2; + using glm::u32mat2x3; + using glm::u32mat2x4; + using glm::u32mat3x2; + using glm::u32mat3x3; + using glm::u32mat3x4; + using glm::u32mat4x2; + using glm::u32mat4x3; + using glm::u32mat4x4; + using glm::lowp_u64mat2x2; + using glm::lowp_u64mat2x3; + using glm::lowp_u64mat2x4; + using glm::lowp_u64mat3x2; + using glm::lowp_u64mat3x3; + using glm::lowp_u64mat3x4; + using glm::lowp_u64mat4x2; + using glm::lowp_u64mat4x3; + using glm::lowp_u64mat4x4; + using glm::mediump_u64mat2x2; + using glm::mediump_u64mat2x3; + using glm::mediump_u64mat2x4; + using glm::mediump_u64mat3x2; + using glm::mediump_u64mat3x3; + using glm::mediump_u64mat3x4; + using glm::mediump_u64mat4x2; + using glm::mediump_u64mat4x3; + using glm::mediump_u64mat4x4; + using glm::highp_u64mat2x2; + using glm::highp_u64mat2x3; + using glm::highp_u64mat2x4; + using glm::highp_u64mat3x2; + using glm::highp_u64mat3x3; + using glm::highp_u64mat3x4; + using glm::highp_u64mat4x2; + using glm::highp_u64mat4x3; + using glm::highp_u64mat4x4; + using glm::u64mat2x2; + using glm::u64mat2x3; + using glm::u64mat2x4; + using glm::u64mat3x2; + using glm::u64mat3x3; + using glm::u64mat3x4; + using glm::u64mat4x2; + using glm::u64mat4x3; + using glm::u64mat4x4; + using glm::lowp_quat; + using glm::mediump_quat; + using glm::highp_quat; + using glm::quat; + using glm::lowp_fquat; + using glm::mediump_fquat; + using glm::highp_fquat; + using glm::fquat; + using glm::lowp_f32quat; + using glm::mediump_f32quat; + using glm::highp_f32quat; + using glm::f32quat; + using glm::lowp_dquat; + using glm::mediump_dquat; + using glm::highp_dquat; + using glm::dquat; + using glm::lowp_f64quat; + using glm::mediump_f64quat; + using glm::highp_f64quat; + using glm::f64quat; + + // Operators + using glm::operator+; + using glm::operator-; + using glm::operator*; + using glm::operator/; + using glm::operator%; + using glm::operator^; + using glm::operator&; + using glm::operator|; + using glm::operator~; + using glm::operator<<; + using glm::operator>>; + using glm::operator==; + using glm::operator!=; + using glm::operator&&; + using glm::operator||; + + // Core functions + using glm::abs; + using glm::acos; + using glm::acosh; + using glm::all; + using glm::any; + using glm::asin; + using glm::asinh; + using glm::atan; + using glm::atanh; + using glm::bitCount; + using glm::bitfieldExtract; + using glm::bitfieldInsert; + using glm::bitfieldReverse; + using glm::ceil; + using glm::clamp; + using glm::cos; + using glm::cosh; + using glm::cross; + using glm::degrees; + using glm::determinant; + using glm::distance; + using glm::dot; + using glm::equal; + using glm::exp; + using glm::exp2; + using glm::faceforward; + using glm::findLSB; + using glm::findMSB; + using glm::floatBitsToInt; + using glm::floatBitsToUint; + using glm::floor; + using glm::fma; + using glm::fract; + using glm::frexp; + using glm::greaterThan; + using glm::greaterThanEqual; + using glm::imulExtended; + using glm::intBitsToFloat; + using glm::inverse; + using glm::inversesqrt; + using glm::isinf; + using glm::isnan; + using glm::ldexp; + using glm::length; + using glm::lessThan; + using glm::lessThanEqual; + using glm::log; + using glm::log2; + using glm::matrixCompMult; + using glm::max; + using glm::min; + using glm::mix; + using glm::mod; + using glm::modf; + using glm::normalize; + using glm::notEqual; + using glm::not_; + using glm::outerProduct; + using glm::packDouble2x32; + using glm::packHalf2x16; + using glm::packSnorm2x16; + using glm::packSnorm4x8; + using glm::packUnorm2x16; + using glm::packUnorm4x8; + using glm::pow; + using glm::radians; + using glm::reflect; + using glm::refract; + using glm::round; + using glm::roundEven; + using glm::sign; + using glm::sin; + using glm::sinh; + using glm::smoothstep; + using glm::sqrt; + using glm::step; + using glm::tan; + using glm::tanh; + using glm::transpose; + using glm::trunc; + using glm::uaddCarry; + using glm::uintBitsToFloat; + using glm::umulExtended; + using glm::unpackDouble2x32; + using glm::unpackHalf2x16; + using glm::unpackSnorm2x16; + using glm::unpackSnorm4x8; + using glm::unpackUnorm2x16; + using glm::unpackUnorm4x8; + using glm::usubBorrow; + +# ifdef GLM_GTC_INLINE_NAMESPACE + inline +# endif + namespace gtc { +# if GLM_CONFIG_ALIGNED_GENTYPES == GLM_ENABLE + using glm::aligned_highp_vec1; + using glm::aligned_mediump_vec1; + using glm::aligned_lowp_vec1; + using glm::aligned_highp_dvec1; + using glm::aligned_mediump_dvec1; + using glm::aligned_lowp_dvec1; + using glm::aligned_highp_ivec1; + using glm::aligned_mediump_ivec1; + using glm::aligned_lowp_ivec1; + using glm::aligned_highp_uvec1; + using glm::aligned_mediump_uvec1; + using glm::aligned_lowp_uvec1; + using glm::aligned_highp_bvec1; + using glm::aligned_mediump_bvec1; + using glm::aligned_lowp_bvec1; + using glm::packed_highp_vec1; + using glm::packed_mediump_vec1; + using glm::packed_lowp_vec1; + using glm::packed_highp_dvec1; + using glm::packed_mediump_dvec1; + using glm::packed_lowp_dvec1; + using glm::packed_highp_ivec1; + using glm::packed_mediump_ivec1; + using glm::packed_lowp_ivec1; + using glm::packed_highp_uvec1; + using glm::packed_mediump_uvec1; + using glm::packed_lowp_uvec1; + using glm::packed_highp_bvec1; + using glm::packed_mediump_bvec1; + using glm::packed_lowp_bvec1; + using glm::aligned_highp_vec2; + using glm::aligned_mediump_vec2; + using glm::aligned_lowp_vec2; + using glm::aligned_highp_dvec2; + using glm::aligned_mediump_dvec2; + using glm::aligned_lowp_dvec2; + using glm::aligned_highp_ivec2; + using glm::aligned_mediump_ivec2; + using glm::aligned_lowp_ivec2; + using glm::aligned_highp_uvec2; + using glm::aligned_mediump_uvec2; + using glm::aligned_lowp_uvec2; + using glm::aligned_highp_bvec2; + using glm::aligned_mediump_bvec2; + using glm::aligned_lowp_bvec2; + using glm::packed_highp_vec2; + using glm::packed_mediump_vec2; + using glm::packed_lowp_vec2; + using glm::packed_highp_dvec2; + using glm::packed_mediump_dvec2; + using glm::packed_lowp_dvec2; + using glm::packed_highp_ivec2; + using glm::packed_mediump_ivec2; + using glm::packed_lowp_ivec2; + using glm::packed_highp_uvec2; + using glm::packed_mediump_uvec2; + using glm::packed_lowp_uvec2; + using glm::packed_highp_bvec2; + using glm::packed_mediump_bvec2; + using glm::packed_lowp_bvec2; + using glm::aligned_highp_vec3; + using glm::aligned_mediump_vec3; + using glm::aligned_lowp_vec3; + using glm::aligned_highp_dvec3; + using glm::aligned_mediump_dvec3; + using glm::aligned_lowp_dvec3; + using glm::aligned_highp_ivec3; + using glm::aligned_mediump_ivec3; + using glm::aligned_lowp_ivec3; + using glm::aligned_highp_uvec3; + using glm::aligned_mediump_uvec3; + using glm::aligned_lowp_uvec3; + using glm::aligned_highp_bvec3; + using glm::aligned_mediump_bvec3; + using glm::aligned_lowp_bvec3; + using glm::packed_highp_vec3; + using glm::packed_mediump_vec3; + using glm::packed_lowp_vec3; + using glm::packed_highp_dvec3; + using glm::packed_mediump_dvec3; + using glm::packed_lowp_dvec3; + using glm::packed_highp_ivec3; + using glm::packed_mediump_ivec3; + using glm::packed_lowp_ivec3; + using glm::packed_highp_uvec3; + using glm::packed_mediump_uvec3; + using glm::packed_lowp_uvec3; + using glm::packed_highp_bvec3; + using glm::packed_mediump_bvec3; + using glm::packed_lowp_bvec3; + using glm::aligned_highp_vec4; + using glm::aligned_mediump_vec4; + using glm::aligned_lowp_vec4; + using glm::aligned_highp_dvec4; + using glm::aligned_mediump_dvec4; + using glm::aligned_lowp_dvec4; + using glm::aligned_highp_ivec4; + using glm::aligned_mediump_ivec4; + using glm::aligned_lowp_ivec4; + using glm::aligned_highp_uvec4; + using glm::aligned_mediump_uvec4; + using glm::aligned_lowp_uvec4; + using glm::aligned_highp_bvec4; + using glm::aligned_mediump_bvec4; + using glm::aligned_lowp_bvec4; + using glm::packed_highp_vec4; + using glm::packed_mediump_vec4; + using glm::packed_lowp_vec4; + using glm::packed_highp_dvec4; + using glm::packed_mediump_dvec4; + using glm::packed_lowp_dvec4; + using glm::packed_highp_ivec4; + using glm::packed_mediump_ivec4; + using glm::packed_lowp_ivec4; + using glm::packed_highp_uvec4; + using glm::packed_mediump_uvec4; + using glm::packed_lowp_uvec4; + using glm::packed_highp_bvec4; + using glm::packed_mediump_bvec4; + using glm::packed_lowp_bvec4; + using glm::aligned_highp_mat2; + using glm::aligned_mediump_mat2; + using glm::aligned_lowp_mat2; + using glm::aligned_highp_dmat2; + using glm::aligned_mediump_dmat2; + using glm::aligned_lowp_dmat2; + using glm::packed_highp_mat2; + using glm::packed_mediump_mat2; + using glm::packed_lowp_mat2; + using glm::packed_highp_dmat2; + using glm::packed_mediump_dmat2; + using glm::packed_lowp_dmat2; + using glm::aligned_highp_mat3; + using glm::aligned_mediump_mat3; + using glm::aligned_lowp_mat3; + using glm::aligned_highp_dmat3; + using glm::aligned_mediump_dmat3; + using glm::aligned_lowp_dmat3; + using glm::packed_highp_mat3; + using glm::packed_mediump_mat3; + using glm::packed_lowp_mat3; + using glm::packed_highp_dmat3; + using glm::packed_mediump_dmat3; + using glm::packed_lowp_dmat3; + using glm::aligned_highp_mat4; + using glm::aligned_mediump_mat4; + using glm::aligned_lowp_mat4; + using glm::aligned_highp_dmat4; + using glm::aligned_mediump_dmat4; + using glm::aligned_lowp_dmat4; + using glm::packed_highp_mat4; + using glm::packed_mediump_mat4; + using glm::packed_lowp_mat4; + using glm::packed_highp_dmat4; + using glm::packed_mediump_dmat4; + using glm::packed_lowp_dmat4; + using glm::aligned_highp_mat2x2; + using glm::aligned_mediump_mat2x2; + using glm::aligned_lowp_mat2x2; + using glm::aligned_highp_dmat2x2; + using glm::aligned_mediump_dmat2x2; + using glm::aligned_lowp_dmat2x2; + using glm::packed_highp_mat2x2; + using glm::packed_mediump_mat2x2; + using glm::packed_lowp_mat2x2; + using glm::packed_highp_dmat2x2; + using glm::packed_mediump_dmat2x2; + using glm::packed_lowp_dmat2x2; + using glm::aligned_highp_mat2x3; + using glm::aligned_mediump_mat2x3; + using glm::aligned_lowp_mat2x3; + using glm::aligned_highp_dmat2x3; + using glm::aligned_mediump_dmat2x3; + using glm::aligned_lowp_dmat2x3; + using glm::packed_highp_mat2x3; + using glm::packed_mediump_mat2x3; + using glm::packed_lowp_mat2x3; + using glm::packed_highp_dmat2x3; + using glm::packed_mediump_dmat2x3; + using glm::packed_lowp_dmat2x3; + using glm::aligned_highp_mat2x4; + using glm::aligned_mediump_mat2x4; + using glm::aligned_lowp_mat2x4; + using glm::aligned_highp_dmat2x4; + using glm::aligned_mediump_dmat2x4; + using glm::aligned_lowp_dmat2x4; + using glm::packed_highp_mat2x4; + using glm::packed_mediump_mat2x4; + using glm::packed_lowp_mat2x4; + using glm::packed_highp_dmat2x4; + using glm::packed_mediump_dmat2x4; + using glm::packed_lowp_dmat2x4; + using glm::aligned_highp_mat3x2; + using glm::aligned_mediump_mat3x2; + using glm::aligned_lowp_mat3x2; + using glm::aligned_highp_dmat3x2; + using glm::aligned_mediump_dmat3x2; + using glm::aligned_lowp_dmat3x2; + using glm::packed_highp_mat3x2; + using glm::packed_mediump_mat3x2; + using glm::packed_lowp_mat3x2; + using glm::packed_highp_dmat3x2; + using glm::packed_mediump_dmat3x2; + using glm::packed_lowp_dmat3x2; + using glm::aligned_highp_mat3x3; + using glm::aligned_mediump_mat3x3; + using glm::aligned_lowp_mat3x3; + using glm::aligned_highp_dmat3x3; + using glm::aligned_mediump_dmat3x3; + using glm::aligned_lowp_dmat3x3; + using glm::packed_highp_mat3x3; + using glm::packed_mediump_mat3x3; + using glm::packed_lowp_mat3x3; + using glm::packed_highp_dmat3x3; + using glm::packed_mediump_dmat3x3; + using glm::packed_lowp_dmat3x3; + using glm::aligned_highp_mat3x4; + using glm::aligned_mediump_mat3x4; + using glm::aligned_lowp_mat3x4; + using glm::aligned_highp_dmat3x4; + using glm::aligned_mediump_dmat3x4; + using glm::aligned_lowp_dmat3x4; + using glm::packed_highp_mat3x4; + using glm::packed_mediump_mat3x4; + using glm::packed_lowp_mat3x4; + using glm::packed_highp_dmat3x4; + using glm::packed_mediump_dmat3x4; + using glm::packed_lowp_dmat3x4; + using glm::aligned_highp_mat4x2; + using glm::aligned_mediump_mat4x2; + using glm::aligned_lowp_mat4x2; + using glm::aligned_highp_dmat4x2; + using glm::aligned_mediump_dmat4x2; + using glm::aligned_lowp_dmat4x2; + using glm::packed_highp_mat4x2; + using glm::packed_mediump_mat4x2; + using glm::packed_lowp_mat4x2; + using glm::packed_highp_dmat4x2; + using glm::packed_mediump_dmat4x2; + using glm::packed_lowp_dmat4x2; + using glm::aligned_highp_mat4x3; + using glm::aligned_mediump_mat4x3; + using glm::aligned_lowp_mat4x3; + using glm::aligned_highp_dmat4x3; + using glm::aligned_mediump_dmat4x3; + using glm::aligned_lowp_dmat4x3; + using glm::packed_highp_mat4x3; + using glm::packed_mediump_mat4x3; + using glm::packed_lowp_mat4x3; + using glm::packed_highp_dmat4x3; + using glm::packed_mediump_dmat4x3; + using glm::packed_lowp_dmat4x3; + using glm::aligned_highp_mat4x4; + using glm::aligned_mediump_mat4x4; + using glm::aligned_lowp_mat4x4; + using glm::aligned_highp_dmat4x4; + using glm::aligned_mediump_dmat4x4; + using glm::aligned_lowp_dmat4x4; + using glm::packed_highp_mat4x4; + using glm::packed_mediump_mat4x4; + using glm::packed_lowp_mat4x4; + using glm::packed_highp_dmat4x4; + using glm::packed_mediump_dmat4x4; + using glm::packed_lowp_dmat4x4; +# if(defined(GLM_PRECISION_LOWP_FLOAT)) + using glm::aligned_vec1; + using glm::aligned_vec2; + using glm::aligned_vec3; + using glm::aligned_vec4; + using glm::packed_vec1; + using glm::packed_vec2; + using glm::packed_vec3; + using glm::packed_vec4; + using glm::aligned_mat2; + using glm::aligned_mat3; + using glm::aligned_mat4; + using glm::packed_mat2; + using glm::packed_mat3; + using glm::packed_mat4; + using glm::aligned_mat2x2; + using glm::aligned_mat2x3; + using glm::aligned_mat2x4; + using glm::aligned_mat3x2; + using glm::aligned_mat3x3; + using glm::aligned_mat3x4; + using glm::aligned_mat4x2; + using glm::aligned_mat4x3; + using glm::aligned_mat4x4; + using glm::packed_mat2x2; + using glm::packed_mat2x3; + using glm::packed_mat2x4; + using glm::packed_mat3x2; + using glm::packed_mat3x3; + using glm::packed_mat3x4; + using glm::packed_mat4x2; + using glm::packed_mat4x3; + using glm::packed_mat4x4; +# elif(defined(GLM_PRECISION_MEDIUMP_FLOAT)) + using glm::aligned_vec1; + using glm::aligned_vec2; + using glm::aligned_vec3; + using glm::aligned_vec4; + using glm::packed_vec1; + using glm::packed_vec2; + using glm::packed_vec3; + using glm::packed_vec4; + using glm::aligned_mat2; + using glm::aligned_mat3; + using glm::aligned_mat4; + using glm::packed_mat2; + using glm::packed_mat3; + using glm::packed_mat4; + using glm::aligned_mat2x2; + using glm::aligned_mat2x3; + using glm::aligned_mat2x4; + using glm::aligned_mat3x2; + using glm::aligned_mat3x3; + using glm::aligned_mat3x4; + using glm::aligned_mat4x2; + using glm::aligned_mat4x3; + using glm::aligned_mat4x4; + using glm::packed_mat2x2; + using glm::packed_mat2x3; + using glm::packed_mat2x4; + using glm::packed_mat3x2; + using glm::packed_mat3x3; + using glm::packed_mat3x4; + using glm::packed_mat4x2; + using glm::packed_mat4x3; + using glm::packed_mat4x4; +# else //defined(GLM_PRECISION_HIGHP_FLOAT) + using glm::aligned_vec1; + using glm::aligned_vec2; + using glm::aligned_vec3; + using glm::aligned_vec4; + using glm::packed_vec1; + using glm::packed_vec2; + using glm::packed_vec3; + using glm::packed_vec4; + using glm::aligned_mat2; + using glm::aligned_mat3; + using glm::aligned_mat4; + using glm::packed_mat2; + using glm::packed_mat3; + using glm::packed_mat4; + using glm::aligned_mat2x2; + using glm::aligned_mat2x3; + using glm::aligned_mat2x4; + using glm::aligned_mat3x2; + using glm::aligned_mat3x3; + using glm::aligned_mat3x4; + using glm::aligned_mat4x2; + using glm::aligned_mat4x3; + using glm::aligned_mat4x4; + using glm::packed_mat2x2; + using glm::packed_mat2x3; + using glm::packed_mat2x4; + using glm::packed_mat3x2; + using glm::packed_mat3x3; + using glm::packed_mat3x4; + using glm::packed_mat4x2; + using glm::packed_mat4x3; + using glm::packed_mat4x4; +# endif//GLM_PRECISION +# if(defined(GLM_PRECISION_LOWP_DOUBLE)) + using glm::aligned_dvec1; + using glm::aligned_dvec2; + using glm::aligned_dvec3; + using glm::aligned_dvec4; + using glm::packed_dvec1; + using glm::packed_dvec2; + using glm::packed_dvec3; + using glm::packed_dvec4; + using glm::aligned_dmat2; + using glm::aligned_dmat3; + using glm::aligned_dmat4; + using glm::packed_dmat2; + using glm::packed_dmat3; + using glm::packed_dmat4; + using glm::aligned_dmat2x2; + using glm::aligned_dmat2x3; + using glm::aligned_dmat2x4; + using glm::aligned_dmat3x2; + using glm::aligned_dmat3x3; + using glm::aligned_dmat3x4; + using glm::aligned_dmat4x2; + using glm::aligned_dmat4x3; + using glm::aligned_dmat4x4; + using glm::packed_dmat2x2; + using glm::packed_dmat2x3; + using glm::packed_dmat2x4; + using glm::packed_dmat3x2; + using glm::packed_dmat3x3; + using glm::packed_dmat3x4; + using glm::packed_dmat4x2; + using glm::packed_dmat4x3; + using glm::packed_dmat4x4; +# elif(defined(GLM_PRECISION_MEDIUMP_DOUBLE)) + using glm::aligned_dvec1; + using glm::aligned_dvec2; + using glm::aligned_dvec3; + using glm::aligned_dvec4; + using glm::packed_dvec1; + using glm::packed_dvec2; + using glm::packed_dvec3; + using glm::packed_dvec4; + using glm::aligned_dmat2; + using glm::aligned_dmat3; + using glm::aligned_dmat4; + using glm::packed_dmat2; + using glm::packed_dmat3; + using glm::packed_dmat4; + using glm::aligned_dmat2x2; + using glm::aligned_dmat2x3; + using glm::aligned_dmat2x4; + using glm::aligned_dmat3x2; + using glm::aligned_dmat3x3; + using glm::aligned_dmat3x4; + using glm::aligned_dmat4x2; + using glm::aligned_dmat4x3; + using glm::aligned_dmat4x4; + using glm::packed_dmat2x2; + using glm::packed_dmat2x3; + using glm::packed_dmat2x4; + using glm::packed_dmat3x2; + using glm::packed_dmat3x3; + using glm::packed_dmat3x4; + using glm::packed_dmat4x2; + using glm::packed_dmat4x3; + using glm::packed_dmat4x4; +# else //defined(GLM_PRECISION_HIGHP_DOUBLE) + using glm::aligned_dvec1; + using glm::aligned_dvec2; + using glm::aligned_dvec3; + using glm::aligned_dvec4; + using glm::packed_dvec1; + using glm::packed_dvec2; + using glm::packed_dvec3; + using glm::packed_dvec4; + using glm::aligned_dmat2; + using glm::aligned_dmat3; + using glm::aligned_dmat4; + using glm::packed_dmat2; + using glm::packed_dmat3; + using glm::packed_dmat4; + using glm::aligned_dmat2x2; + using glm::aligned_dmat2x3; + using glm::aligned_dmat2x4; + using glm::aligned_dmat3x2; + using glm::aligned_dmat3x3; + using glm::aligned_dmat3x4; + using glm::aligned_dmat4x2; + using glm::aligned_dmat4x3; + using glm::aligned_dmat4x4; + using glm::packed_dmat2x2; + using glm::packed_dmat2x3; + using glm::packed_dmat2x4; + using glm::packed_dmat3x2; + using glm::packed_dmat3x3; + using glm::packed_dmat3x4; + using glm::packed_dmat4x2; + using glm::packed_dmat4x3; + using glm::packed_dmat4x4; +# endif//GLM_PRECISION +# if(defined(GLM_PRECISION_LOWP_INT)) + using glm::aligned_ivec1; + using glm::aligned_ivec2; + using glm::aligned_ivec3; + using glm::aligned_ivec4; +# elif(defined(GLM_PRECISION_MEDIUMP_INT)) + using glm::aligned_ivec1; + using glm::aligned_ivec2; + using glm::aligned_ivec3; + using glm::aligned_ivec4; +# else //defined(GLM_PRECISION_HIGHP_INT) + using glm::aligned_ivec1; + using glm::aligned_ivec2; + using glm::aligned_ivec3; + using glm::aligned_ivec4; + using glm::packed_ivec1; + using glm::packed_ivec2; + using glm::packed_ivec3; + using glm::packed_ivec4; +# endif//GLM_PRECISION +# if(defined(GLM_PRECISION_LOWP_UINT)) + using glm::aligned_uvec1; + using glm::aligned_uvec2; + using glm::aligned_uvec3; + using glm::aligned_uvec4; +# elif(defined(GLM_PRECISION_MEDIUMP_UINT)) + using glm::aligned_uvec1; + using glm::aligned_uvec2; + using glm::aligned_uvec3; + using glm::aligned_uvec4; +# else //defined(GLM_PRECISION_HIGHP_UINT) + using glm::aligned_uvec1; + using glm::aligned_uvec2; + using glm::aligned_uvec3; + using glm::aligned_uvec4; + using glm::packed_uvec1; + using glm::packed_uvec2; + using glm::packed_uvec3; + using glm::packed_uvec4; +# endif//GLM_PRECISION +# if(defined(GLM_PRECISION_LOWP_BOOL)) + using glm::aligned_bvec1; + using glm::aligned_bvec2; + using glm::aligned_bvec3; + using glm::aligned_bvec4; +# elif(defined(GLM_PRECISION_MEDIUMP_BOOL)) + using glm::aligned_bvec1; + using glm::aligned_bvec2; + using glm::aligned_bvec3; + using glm::aligned_bvec4; +# else //defined(GLM_PRECISION_HIGHP_BOOL) + using glm::aligned_bvec1; + using glm::aligned_bvec2; + using glm::aligned_bvec3; + using glm::aligned_bvec4; + using glm::packed_bvec1; + using glm::packed_bvec2; + using glm::packed_bvec3; + using glm::packed_bvec4; +# endif//GLM_PRECISION +# endif + + + using glm::abs; + using glm::acos; + using glm::acosh; + using glm::acot; + using glm::acoth; + using glm::acsc; + using glm::acsch; + using glm::affineInverse; + using glm::all; + using glm::angle; + using glm::angleAxis; + using glm::any; + using glm::asec; + using glm::asech; + using glm::asin; + using glm::asinh; + using glm::atan; + using glm::atanh; + using glm::axis; + using glm::ballRand; + using glm::bitCount; + using glm::bitfieldDeinterleave; + using glm::bitfieldExtract; + using glm::bitfieldFillOne; + using glm::bitfieldFillZero; + using glm::bitfieldInsert; + using glm::bitfieldInterleave; + using glm::bitfieldReverse; + using glm::bitfieldRotateLeft; + using glm::bitfieldRotateRight; + using glm::ceil; + using glm::ceilMultiple; + using glm::ceilPowerOfTwo; + using glm::circularRand; + using glm::clamp; + using glm::column; + using glm::conjugate; + using glm::convertLinearToSRGB; + using glm::convertSRGBToLinear; + using glm::cos; + using glm::cos_one_over_two; + using glm::cosh; + using glm::cot; + using glm::coth; + using glm::cross; + using glm::csc; + using glm::csch; + using glm::degrees; + using glm::determinant; + using glm::diskRand; + using glm::distance; + using glm::dot; + using glm::e; + using glm::epsilon; + using glm::epsilonEqual; + using glm::epsilonNotEqual; + using glm::equal; + using glm::euler; + using glm::eulerAngles; + using glm::exp; + using glm::exp2; + using glm::faceforward; + using glm::fclamp; + using glm::findLSB; + using glm::findMSB; + using glm::floatBitsToInt; + using glm::floatBitsToUint; + using glm::float_distance; + using glm::floor; + using glm::floorMultiple; + using glm::floorPowerOfTwo; + using glm::fma; + using glm::fmax; + using glm::fmin; + using glm::four_over_pi; + using glm::fract; + using glm::frexp; + using glm::frustum; + using glm::frustumLH; + using glm::frustumLH_NO; + using glm::frustumLH_ZO; + using glm::frustumNO; + using glm::frustumRH; + using glm::frustumRH_NO; + using glm::frustumRH_ZO; + using glm::frustumZO; + using glm::gaussRand; + using glm::golden_ratio; + using glm::greaterThan; + using glm::greaterThanEqual; + using glm::half_pi; + using glm::identity; + using glm::imulExtended; + using glm::infinitePerspective; + using glm::infinitePerspectiveLH; + using glm::infinitePerspectiveRH; + using glm::intBitsToFloat; + using glm::inverse; + using glm::inverseTranspose; + using glm::inversesqrt; + using glm::iround; + using glm::isinf; + using glm::isnan; + using glm::ldexp; + using glm::length; + using glm::lerp; + using glm::lessThan; + using glm::lessThanEqual; + using glm::linearRand; + using glm::ln_ln_two; + using glm::ln_ten; + using glm::ln_two; + using glm::log; + using glm::log2; + using glm::lookAt; + using glm::lookAtLH; + using glm::lookAtRH; + using glm::make_mat2; + using glm::make_mat2x2; + using glm::make_mat2x3; + using glm::make_mat2x4; + using glm::make_mat3; + using glm::make_mat3x2; + using glm::make_mat3x3; + using glm::make_mat3x4; + using glm::make_mat4; + using glm::make_mat4x2; + using glm::make_mat4x3; + using glm::make_mat4x4; + using glm::make_quat; + using glm::make_vec1; + using glm::make_vec2; + using glm::make_vec3; + using glm::make_vec4; + using glm::mask; + using glm::mat3_cast; + using glm::mat4_cast; + using glm::matrixCompMult; + using glm::max; + using glm::min; + using glm::mirrorClamp; + using glm::mirrorRepeat; + using glm::mix; + using glm::mod; + using glm::modf; + using glm::next_float; + using glm::normalize; + using glm::notEqual; + using glm::not_; + using glm::one; + using glm::one_over_pi; + using glm::one_over_root_two; + using glm::one_over_two_pi; + using glm::ortho; + using glm::orthoLH; + using glm::orthoLH_NO; + using glm::orthoLH_ZO; + using glm::orthoNO; + using glm::orthoRH; + using glm::orthoRH_NO; + using glm::orthoRH_ZO; + using glm::orthoZO; + using glm::outerProduct; + using glm::packF2x11_1x10; + using glm::packF3x9_E1x5; + using glm::packHalf; + using glm::packHalf1x16; + using glm::packHalf4x16; + using glm::packI3x10_1x2; + using glm::packInt2x16; + using glm::packInt2x32; + using glm::packInt2x8; + using glm::packInt4x16; + using glm::packInt4x8; + using glm::packRGBM; + using glm::packSnorm; + using glm::packSnorm1x16; + using glm::packSnorm1x8; + using glm::packSnorm2x8; + using glm::packSnorm3x10_1x2; + using glm::packSnorm4x16; + using glm::packU3x10_1x2; + using glm::packUint2x16; + using glm::packUint2x32; + using glm::packUint2x8; + using glm::packUint4x16; + using glm::packUint4x8; + using glm::packUnorm; + using glm::packUnorm1x16; + using glm::packUnorm1x5_1x6_1x5; + using glm::packUnorm1x8; + using glm::packUnorm2x3_1x2; + using glm::packUnorm2x4; + using glm::packUnorm2x8; + using glm::packUnorm3x10_1x2; + using glm::packUnorm3x5_1x1; + using glm::packUnorm4x16; + using glm::packUnorm4x4; + using glm::perlin; + using glm::perspective; + using glm::perspectiveFov; + using glm::perspectiveFovLH; + using glm::perspectiveFovLH_NO; + using glm::perspectiveFovLH_ZO; + using glm::perspectiveFovNO; + using glm::perspectiveFovRH; + using glm::perspectiveFovRH_NO; + using glm::perspectiveFovRH_ZO; + using glm::perspectiveFovZO; + using glm::perspectiveLH; + using glm::perspectiveLH_NO; + using glm::perspectiveLH_ZO; + using glm::perspectiveNO; + using glm::perspectiveRH; + using glm::perspectiveRH_NO; + using glm::perspectiveRH_ZO; + using glm::perspectiveZO; + using glm::pi; + using glm::pickMatrix; + using glm::pitch; + using glm::pow; + using glm::prev_float; + using glm::project; + using glm::projectNO; + using glm::projectZO; + using glm::quarter_pi; + using glm::quatLookAt; + using glm::quatLookAtLH; + using glm::quatLookAtRH; + using glm::quat_cast; + using glm::radians; + using glm::reflect; + using glm::refract; + using glm::repeat; + using glm::roll; + using glm::root_five; + using glm::root_half_pi; + using glm::root_ln_four; + using glm::root_pi; + using glm::root_three; + using glm::root_two; + using glm::root_two_pi; + using glm::rotate; + using glm::round; + using glm::roundEven; + using glm::roundMultiple; + using glm::roundPowerOfTwo; + using glm::row; + using glm::scale; + using glm::sec; + using glm::sech; + using glm::sign; + using glm::simplex; + using glm::sin; + using glm::sinh; + using glm::slerp; + using glm::smoothstep; + using glm::sphericalRand; + using glm::sqrt; + using glm::step; + using glm::tan; + using glm::tanh; + using glm::third; + using glm::three_over_two_pi; + using glm::translate; + using glm::transpose; + using glm::trunc; + using glm::tweakedInfinitePerspective; + using glm::two_over_pi; + using glm::two_over_root_pi; + using glm::two_pi; + using glm::two_thirds; + using glm::uaddCarry; + using glm::uintBitsToFloat; + using glm::umulExtended; + using glm::unProject; + using glm::unProjectNO; + using glm::unProjectZO; + using glm::unpackF2x11_1x10; + using glm::unpackF3x9_E1x5; + using glm::unpackHalf; + using glm::unpackHalf1x16; + using glm::unpackHalf4x16; + using glm::unpackI3x10_1x2; + using glm::unpackInt2x16; + using glm::unpackInt2x32; + using glm::unpackInt2x8; + using glm::unpackInt4x16; + using glm::unpackInt4x8; + using glm::unpackRGBM; + using glm::unpackSnorm; + using glm::unpackSnorm1x16; + using glm::unpackSnorm1x8; + using glm::unpackSnorm2x8; + using glm::unpackSnorm3x10_1x2; + using glm::unpackSnorm4x16; + using glm::unpackU3x10_1x2; + using glm::unpackUint2x16; + using glm::unpackUint2x32; + using glm::unpackUint2x8; + using glm::unpackUint4x16; + using glm::unpackUint4x8; + using glm::unpackUnorm; + using glm::unpackUnorm1x16; + using glm::unpackUnorm1x5_1x6_1x5; + using glm::unpackUnorm1x8; + using glm::unpackUnorm2x3_1x2; + using glm::unpackUnorm2x4; + using glm::unpackUnorm2x8; + using glm::unpackUnorm3x10_1x2; + using glm::unpackUnorm3x5_1x1; + using glm::unpackUnorm4x16; + using glm::unpackUnorm4x4; + using glm::uround; + using glm::usubBorrow; + using glm::value_ptr; + using glm::yaw; + using glm::zero; + } + +# ifdef GLM_EXT_INLINE_NAMESPACE + inline +# endif + namespace ext { + using glm::abs; + using glm::acos; + using glm::acosh; + using glm::acot; + using glm::acoth; + using glm::acsc; + using glm::acsch; + using glm::all; + using glm::angle; + using glm::angleAxis; + using glm::any; + using glm::asec; + using glm::asech; + using glm::asin; + using glm::asinh; + using glm::atan; + using glm::atanh; + using glm::axis; + using glm::ceil; + using glm::clamp; + using glm::conjugate; + using glm::cos; + using glm::cos_one_over_two; + using glm::cosh; + using glm::cot; + using glm::coth; + using glm::cross; + using glm::csc; + using glm::csch; + using glm::degrees; + using glm::determinant; + using glm::distance; + using glm::dot; + using glm::e; + using glm::epsilon; + using glm::equal; + using glm::euler; + using glm::exp; + using glm::exp2; + using glm::faceforward; + using glm::fclamp; + using glm::findNSB; + using glm::floatBitsToInt; + using glm::floatBitsToUint; + using glm::floatDistance; + using glm::floor; + using glm::fma; + using glm::fmax; + using glm::fmin; + using glm::four_over_pi; + using glm::fract; + using glm::frexp; + using glm::frustum; + using glm::frustumLH; + using glm::frustumLH_NO; + using glm::frustumLH_ZO; + using glm::frustumNO; + using glm::frustumRH; + using glm::frustumRH_NO; + using glm::frustumRH_ZO; + using glm::frustumZO; + using glm::golden_ratio; + using glm::greaterThan; + using glm::greaterThanEqual; + using glm::half_pi; + using glm::identity; + using glm::infinitePerspective; + using glm::infinitePerspectiveLH; + using glm::infinitePerspectiveRH; + using glm::intBitsToFloat; + using glm::inverse; + using glm::inversesqrt; + using glm::iround; + using glm::isMultiple; + using glm::isPowerOfTwo; + using glm::isinf; + using glm::isnan; + using glm::ldexp; + using glm::length; + using glm::lerp; + using glm::lessThan; + using glm::lessThanEqual; + using glm::ln_ln_two; + using glm::ln_ten; + using glm::ln_two; + using glm::log; + using glm::log2; + using glm::lookAt; + using glm::lookAtLH; + using glm::lookAtRH; + using glm::matrixCompMult; + using glm::max; + using glm::min; + using glm::mirrorClamp; + using glm::mirrorRepeat; + using glm::mix; + using glm::mod; + using glm::modf; + using glm::nextFloat; + using glm::nextMultiple; + using glm::nextPowerOfTwo; + using glm::normalize; + using glm::notEqual; + using glm::not_; + using glm::one; + using glm::one_over_pi; + using glm::one_over_root_two; + using glm::one_over_two_pi; + using glm::ortho; + using glm::orthoLH; + using glm::orthoLH_NO; + using glm::orthoLH_ZO; + using glm::orthoNO; + using glm::orthoRH; + using glm::orthoRH_NO; + using glm::orthoRH_ZO; + using glm::orthoZO; + using glm::outerProduct; + using glm::perspective; + using glm::perspectiveFov; + using glm::perspectiveFovLH; + using glm::perspectiveFovLH_NO; + using glm::perspectiveFovLH_ZO; + using glm::perspectiveFovNO; + using glm::perspectiveFovRH; + using glm::perspectiveFovRH_NO; + using glm::perspectiveFovRH_ZO; + using glm::perspectiveFovZO; + using glm::perspectiveLH; + using glm::perspectiveLH_NO; + using glm::perspectiveLH_ZO; + using glm::perspectiveNO; + using glm::perspectiveRH; + using glm::perspectiveRH_NO; + using glm::perspectiveRH_ZO; + using glm::perspectiveZO; + using glm::pi; + using glm::pickMatrix; + using glm::pow; + using glm::prevFloat; + using glm::prevMultiple; + using glm::prevPowerOfTwo; + using glm::project; + using glm::projectNO; + using glm::projectZO; + using glm::quarter_pi; + using glm::radians; + using glm::reflect; + using glm::refract; + using glm::repeat; + using glm::root_five; + using glm::root_half_pi; + using glm::root_ln_four; + using glm::root_pi; + using glm::root_three; + using glm::root_two; + using glm::root_two_pi; + using glm::rotate; + using glm::round; + using glm::roundEven; + using glm::scale; + using glm::sec; + using glm::sech; + using glm::sign; + using glm::sin; + using glm::sinh; + using glm::slerp; + using glm::smoothstep; + using glm::sqrt; + using glm::step; + using glm::tan; + using glm::tanh; + using glm::third; + using glm::three_over_two_pi; + using glm::translate; + using glm::transpose; + using glm::trunc; + using glm::tweakedInfinitePerspective; + using glm::two_over_pi; + using glm::two_over_root_pi; + using glm::two_pi; + using glm::two_thirds; + using glm::uintBitsToFloat; + using glm::unProject; + using glm::unProjectNO; + using glm::unProjectZO; + using glm::uround; + using glm::zero; + } + +# ifdef GLM_ENABLE_EXPERIMENTAL +# ifdef GLM_GTX_INLINE_NAMESPACE + inline +# endif + namespace gtx { + using glm::io::order_type; + using glm::io::format_punct; + using glm::io::basic_state_saver; + using glm::io::basic_format_saver; + using glm::io::precision; + using glm::io::width; + using glm::io::delimeter; + using glm::io::order; + using glm::io::get_facet; + using glm::io::formatted; + using glm::io::unformatted; + using glm::io::operator<<; + using glm::operator<<; + using glm::tdualquat; + +# if !((GLM_COMPILER & GLM_COMPILER_CUDA) || (GLM_COMPILER & GLM_COMPILER_HIP)) + using glm::to_string; +# endif +# if GLM_HAS_TEMPLATE_ALIASES + using glm::operator*; + using glm::operator/; +# endif +# if GLM_HAS_RANGE_FOR + using glm::components; + using glm::begin; + using glm::end; +# endif + + using glm::abs; + using glm::acos; + using glm::acosh; + using glm::adjugate; + using glm::all; + using glm::angle; + using glm::angleAxis; + using glm::any; + using glm::areCollinear; + using glm::areOrthogonal; + using glm::areOrthonormal; + using glm::asin; + using glm::asinh; + using glm::associatedMax; + using glm::associatedMin; + using glm::atan; + using glm::atanh; + using glm::axis; + using glm::axisAngle; + using glm::axisAngleMatrix; + using glm::backEaseIn; + using glm::backEaseInOut; + using glm::backEaseOut; + using glm::bitCount; + using glm::bitfieldDeinterleave; + using glm::bitfieldExtract; + using glm::bitfieldFillOne; + using glm::bitfieldFillZero; + using glm::bitfieldInsert; + using glm::bitfieldInterleave; + using glm::bitfieldReverse; + using glm::bitfieldRotateLeft; + using glm::bitfieldRotateRight; + using glm::bounceEaseIn; + using glm::bounceEaseInOut; + using glm::bounceEaseOut; + using glm::catmullRom; + using glm::ceil; + using glm::circularEaseIn; + using glm::circularEaseInOut; + using glm::circularEaseOut; + using glm::clamp; + using glm::closeBounded; + using glm::closestPointOnLine; + using glm::colMajor2; + using glm::colMajor3; + using glm::colMajor4; + using glm::compAdd; + using glm::compMax; + using glm::compMin; + using glm::compMul; + using glm::compNormalize; + using glm::compScale; + using glm::computeCovarianceMatrix; + using glm::conjugate; + using glm::convertD65XYZToD50XYZ; + using glm::convertD65XYZToLinearSRGB; + using glm::convertLinearSRGBToD50XYZ; + using glm::convertLinearSRGBToD65XYZ; + using glm::cos; + using glm::cos_one_over_two; + using glm::cosh; + using glm::cross; + using glm::cubic; + using glm::cubicEaseIn; + using glm::cubicEaseInOut; + using glm::cubicEaseOut; + using glm::decompose; + using glm::degrees; + using glm::derivedEulerAngleX; + using glm::derivedEulerAngleY; + using glm::derivedEulerAngleZ; + using glm::determinant; + using glm::diagonal2x2; + using glm::diagonal2x3; + using glm::diagonal2x4; + using glm::diagonal3x2; + using glm::diagonal3x3; + using glm::diagonal3x4; + using glm::diagonal4x2; + using glm::diagonal4x3; + using glm::diagonal4x4; + using glm::distance; + using glm::distance2; + using glm::dot; + using glm::dual_quat_identity; + using glm::dualquat_cast; + using glm::e; + using glm::elasticEaseIn; + using glm::elasticEaseInOut; + using glm::elasticEaseOut; + using glm::epsilon; + using glm::epsilonEqual; + using glm::epsilonNotEqual; + using glm::equal; + using glm::euclidean; + using glm::euler; + using glm::eulerAngleX; + using glm::eulerAngleXY; + using glm::eulerAngleXYX; + using glm::eulerAngleXYZ; + using glm::eulerAngleXZ; + using glm::eulerAngleXZX; + using glm::eulerAngleXZY; + using glm::eulerAngleY; + using glm::eulerAngleYX; + using glm::eulerAngleYXY; + using glm::eulerAngleYXZ; + using glm::eulerAngleYZ; + using glm::eulerAngleYZX; + using glm::eulerAngleYZY; + using glm::eulerAngleZ; + using glm::eulerAngleZX; + using glm::eulerAngleZXY; + using glm::eulerAngleZXZ; + using glm::eulerAngleZY; + using glm::eulerAngleZYX; + using glm::eulerAngleZYZ; + using glm::eulerAngles; + using glm::exp; + using glm::exp2; + using glm::exponentialEaseIn; + using glm::exponentialEaseInOut; + using glm::exponentialEaseOut; + using glm::extend; + using glm::extractEulerAngleXYX; + using glm::extractEulerAngleXYZ; + using glm::extractEulerAngleXZX; + using glm::extractEulerAngleXZY; + using glm::extractEulerAngleYXY; + using glm::extractEulerAngleYXZ; + using glm::extractEulerAngleYZX; + using glm::extractEulerAngleYZY; + using glm::extractEulerAngleZXY; + using glm::extractEulerAngleZXZ; + using glm::extractEulerAngleZYX; + using glm::extractEulerAngleZYZ; + using glm::extractMatrixRotation; + using glm::extractRealComponent; + using glm::faceforward; + using glm::factorial; + using glm::fastAcos; + using glm::fastAsin; + using glm::fastAtan; + using glm::fastCos; + using glm::fastDistance; + using glm::fastExp; + using glm::fastExp2; + using glm::fastInverseSqrt; + using glm::fastLength; + using glm::fastLog; + using glm::fastLog2; + using glm::fastMix; + using glm::fastNormalize; + using glm::fastNormalizeDot; + using glm::fastPow; + using glm::fastSin; + using glm::fastSqrt; + using glm::fastTan; + using glm::fclamp; + using glm::findLSB; + using glm::findMSB; + using glm::fliplr; + using glm::flipud; + using glm::floatBitsToInt; + using glm::floatBitsToUint; + using glm::floor; + using glm::floor_log2; + using glm::fma; + using glm::fmax; + using glm::fmin; + using glm::fmod; + using glm::four_over_pi; + using glm::fract; + using glm::frexp; + using glm::frustum; + using glm::frustumLH; + using glm::frustumLH_NO; + using glm::frustumLH_ZO; + using glm::frustumNO; + using glm::frustumRH; + using glm::frustumRH_NO; + using glm::frustumRH_ZO; + using glm::frustumZO; + using glm::gauss; + using glm::golden_ratio; + using glm::greaterThan; + using glm::greaterThanEqual; + using glm::half_pi; + using glm::hermite; + using glm::highestBitValue; + using glm::hsvColor; + using glm::identity; + using glm::imulExtended; + using glm::infinitePerspective; + using glm::infinitePerspectiveLH; + using glm::infinitePerspectiveRH; + using glm::intBitsToFloat; + using glm::intermediate; + using glm::interpolate; + using glm::intersectLineSphere; + using glm::intersectLineTriangle; + using glm::intersectRayPlane; + using glm::intersectRaySphere; + using glm::intersectRayTriangle; + using glm::inverse; + using glm::inversesqrt; + using glm::iround; + using glm::isCompNull; + using glm::isIdentity; + using glm::isNormalized; + using glm::isNull; + using glm::isOrthogonal; + using glm::isdenormal; + using glm::isfinite; + using glm::isinf; + using glm::isnan; + using glm::l1Norm; + using glm::l2Norm; + using glm::lMaxNorm; + using glm::ldexp; + using glm::leftHanded; + using glm::length; + using glm::length2; + using glm::lerp; + using glm::lessThan; + using glm::lessThanEqual; + using glm::linearGradient; + using glm::linearInterpolation; + using glm::ln_ln_two; + using glm::ln_ten; + using glm::ln_two; + using glm::log; + using glm::log2; + using glm::lookAt; + using glm::lookAtLH; + using glm::lookAtRH; + using glm::lowestBitValue; + using glm::luminosity; + using glm::lxNorm; + using glm::make_mat2; + using glm::make_mat2x2; + using glm::make_mat2x3; + using glm::make_mat2x4; + using glm::make_mat3; + using glm::make_mat3x2; + using glm::make_mat3x3; + using glm::make_mat3x4; + using glm::make_mat4; + using glm::make_mat4x2; + using glm::make_mat4x3; + using glm::make_mat4x4; + using glm::make_quat; + using glm::make_vec1; + using glm::make_vec2; + using glm::make_vec3; + using glm::make_vec4; + using glm::mask; + using glm::mat2x4_cast; + using glm::mat3_cast; + using glm::mat3x4_cast; + using glm::mat4_cast; + using glm::matrixCompMult; + using glm::matrixCross3; + using glm::matrixCross4; + using glm::max; + using glm::min; + using glm::mirrorClamp; + using glm::mirrorRepeat; + using glm::mix; + using glm::mixedProduct; + using glm::mod; + using glm::modf; + using glm::nlz; + using glm::normalize; + using glm::normalizeDot; + using glm::notEqual; + using glm::not_; + using glm::YCoCg2rgb; + using glm::YCoCgR2rgb; + using glm::one; + using glm::one_over_pi; + using glm::one_over_root_two; + using glm::one_over_two_pi; + using glm::openBounded; + using glm::orientate2; + using glm::orientate3; + using glm::orientate4; + using glm::orientation; + using glm::orientedAngle; + using glm::ortho; + using glm::orthoLH; + using glm::orthoLH_NO; + using glm::orthoLH_ZO; + using glm::orthoNO; + using glm::orthoRH; + using glm::orthoRH_NO; + using glm::orthoRH_ZO; + using glm::orthoZO; + using glm::orthonormalize; + using glm::outerProduct; + using glm::packDouble2x32; + using glm::packHalf2x16; + using glm::packSnorm2x16; + using glm::packSnorm4x8; + using glm::packUnorm2x16; + using glm::packUnorm4x8; + using glm::perp; + using glm::perspective; + using glm::perspectiveFov; + using glm::perspectiveFovLH; + using glm::perspectiveFovLH_NO; + using glm::perspectiveFovLH_ZO; + using glm::perspectiveFovNO; + using glm::perspectiveFovRH; + using glm::perspectiveFovRH_NO; + using glm::perspectiveFovRH_ZO; + using glm::perspectiveFovZO; + using glm::perspectiveLH; + using glm::perspectiveLH_NO; + using glm::perspectiveLH_ZO; + using glm::perspectiveNO; + using glm::perspectiveRH; + using glm::perspectiveRH_NO; + using glm::perspectiveRH_ZO; + using glm::perspectiveZO; + using glm::pi; + using glm::pickMatrix; + using glm::pitch; + using glm::polar; + using glm::pow; + using glm::pow2; + using glm::pow3; + using glm::pow4; + using glm::powerOfTwoAbove; + using glm::powerOfTwoBelow; + using glm::powerOfTwoNearest; + using glm::proj; + using glm::proj2D; + using glm::proj3D; + using glm::project; + using glm::projectNO; + using glm::projectZO; + using glm::qr_decompose; + using glm::quadraticEaseIn; + using glm::quadraticEaseInOut; + using glm::quadraticEaseOut; + using glm::quarter_pi; + using glm::quarticEaseIn; + using glm::quarticEaseInOut; + using glm::quarticEaseOut; + using glm::quatLookAt; + using glm::quatLookAtLH; + using glm::quatLookAtRH; + using glm::quat_cast; + using glm::quat_identity; + using glm::quinticEaseIn; + using glm::quinticEaseInOut; + using glm::quinticEaseOut; + using glm::radialGradient; + using glm::radians; + using glm::recompose; + using glm::reflect; + using glm::refract; + using glm::repeat; + using glm::rgb2YCoCg; + using glm::rgb2YCoCgR; + using glm::rgbColor; + using glm::rightHanded; + using glm::roll; + using glm::root_five; + using glm::root_half_pi; + using glm::root_ln_four; + using glm::root_pi; + using glm::root_three; + using glm::root_two; + using glm::root_two_pi; + using glm::rotate; + using glm::rotateNormalizedAxis; + using glm::rotateX; + using glm::rotateY; + using glm::rotateZ; + using glm::rotation; + using glm::round; + using glm::roundEven; + using glm::rowMajor2; + using glm::rowMajor3; + using glm::rowMajor4; + using glm::rq_decompose; + using glm::saturation; + using glm::scale; + using glm::scaleBias; + using glm::shearX2D; + using glm::shearX3D; + using glm::shearY2D; + using glm::shearY3D; + using glm::shearZ3D; + using glm::shortMix; + using glm::sign; + using glm::sin; + using glm::sineEaseIn; + using glm::sineEaseInOut; + using glm::sineEaseOut; + using glm::sinh; + using glm::slerp; + using glm::smoothstep; + using glm::sortEigenvalues; + using glm::sqrt; + using glm::squad; + using glm::step; + using glm::tan; + using glm::tanh; + using glm::third; + using glm::three_over_two_pi; + using glm::translate; + using glm::transpose; + using glm::triangleNormal; + using glm::trunc; + using glm::tweakedInfinitePerspective; + using glm::two_over_pi; + using glm::two_over_root_pi; + using glm::two_pi; + using glm::two_thirds; + using glm::uaddCarry; + using glm::uintBitsToFloat; + using glm::umulExtended; + using glm::unProject; + using glm::unProjectNO; + using glm::unProjectZO; + using glm::unpackDouble2x32; + using glm::unpackHalf2x16; + using glm::unpackSnorm2x16; + using glm::unpackSnorm4x8; + using glm::unpackUnorm2x16; + using glm::unpackUnorm4x8; + using glm::uround; + using glm::usubBorrow; + using glm::value_ptr; + using glm::wrapAngle; + using glm::wxyz; + using glm::yaw; + using glm::yawPitchRoll; + using glm::zero; + } +# endif +} + +#if defined(_MSC_VER) // Workaround +// Partial template specialization doesn't need to be exported explicitly, but this may not work otherwise on MSVC. +export namespace std { + using std::hash; // See GLM_GTX_hash +} +#endif diff --git a/thirdparty/glm/glm/glm.hpp b/thirdparty/glm/glm/glm.hpp new file mode 100644 index 0000000..8b37545 --- /dev/null +++ b/thirdparty/glm/glm/glm.hpp @@ -0,0 +1,137 @@ +/// @ref core +/// @file glm/glm.hpp +/// +/// @mainpage OpenGL Mathematics (GLM) +/// - Website: glm.g-truc.net +/// - GLM API documentation +/// - GLM Manual +/// +/// @defgroup core Core features +/// +/// @brief Features that implement in C++ the GLSL specification as closely as possible. +/// +/// The GLM core consists of C++ types that mirror GLSL types and +/// C++ functions that mirror the GLSL functions. +/// +/// The best documentation for GLM Core is the current GLSL specification, +/// version 4.2 +/// (pdf file). +/// +/// GLM core functionalities require to be included to be used. +/// +/// +/// @defgroup core_vector Vector types +/// +/// Vector types of two to four components with an exhaustive set of operators. +/// +/// @ingroup core +/// +/// +/// @defgroup core_vector_precision Vector types with precision qualifiers +/// +/// @brief Vector types with precision qualifiers which may result in various precision in term of ULPs +/// +/// GLSL allows defining qualifiers for particular variables. +/// With OpenGL's GLSL, these qualifiers have no effect; they are there for compatibility, +/// with OpenGL ES's GLSL, these qualifiers do have an effect. +/// +/// C++ has no language equivalent to qualifier qualifiers. So GLM provides the next-best thing: +/// a number of typedefs that use a particular qualifier. +/// +/// None of these types make any guarantees about the actual qualifier used. +/// +/// @ingroup core +/// +/// +/// @defgroup core_matrix Matrix types +/// +/// Matrix types of with C columns and R rows where C and R are values between 2 to 4 included. +/// These types have exhaustive sets of operators. +/// +/// @ingroup core +/// +/// +/// @defgroup core_matrix_precision Matrix types with precision qualifiers +/// +/// @brief Matrix types with precision qualifiers which may result in various precision in term of ULPs +/// +/// GLSL allows defining qualifiers for particular variables. +/// With OpenGL's GLSL, these qualifiers have no effect; they are there for compatibility, +/// with OpenGL ES's GLSL, these qualifiers do have an effect. +/// +/// C++ has no language equivalent to qualifier qualifiers. So GLM provides the next-best thing: +/// a number of typedefs that use a particular qualifier. +/// +/// None of these types make any guarantees about the actual qualifier used. +/// +/// @ingroup core +/// +/// +/// @defgroup ext Stable extensions +/// +/// @brief Additional features not specified by GLSL specification. +/// +/// EXT extensions are fully tested and documented. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions at once by +/// including . Otherwise, each extension needs to be included a specific file. +/// +/// +/// @defgroup gtc Recommended extensions +/// +/// @brief Additional features not specified by GLSL specification. +/// +/// GTC extensions aim to be stable with tests and documentation. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions at once by +/// including . Otherwise, each extension needs to be included a specific file. +/// +/// +/// @defgroup gtx Experimental extensions +/// +/// @brief Experimental features not specified by GLSL specification. +/// +/// Experimental extensions are useful functions and types, but the development of +/// their API and functionality is not necessarily stable. They can change +/// substantially between versions. Backwards compatibility is not much of an issue +/// for them. +/// +/// Even if it's highly unrecommended, it's possible to include all the extensions +/// at once by including . Otherwise, each extension needs to be +/// included a specific file. +/// + +#include "detail/_fixes.hpp" + +#include "detail/setup.hpp" + +#pragma once + +#include +#include +#include +#include +#include +#include "fwd.hpp" + +#include "vec2.hpp" +#include "vec3.hpp" +#include "vec4.hpp" +#include "mat2x2.hpp" +#include "mat2x3.hpp" +#include "mat2x4.hpp" +#include "mat3x2.hpp" +#include "mat3x3.hpp" +#include "mat3x4.hpp" +#include "mat4x2.hpp" +#include "mat4x3.hpp" +#include "mat4x4.hpp" + +#include "trigonometric.hpp" +#include "exponential.hpp" +#include "common.hpp" +#include "packing.hpp" +#include "geometric.hpp" +#include "matrix.hpp" +#include "vector_relational.hpp" +#include "integer.hpp" diff --git a/thirdparty/glm/glm/gtc/bitfield.hpp b/thirdparty/glm/glm/gtc/bitfield.hpp new file mode 100644 index 0000000..084fbe7 --- /dev/null +++ b/thirdparty/glm/glm/gtc/bitfield.hpp @@ -0,0 +1,266 @@ +/// @ref gtc_bitfield +/// @file glm/gtc/bitfield.hpp +/// +/// @see core (dependence) +/// @see gtc_bitfield (dependence) +/// +/// @defgroup gtc_bitfield GLM_GTC_bitfield +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#include "../detail/setup.hpp" + +#pragma once + +// Dependencies +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "type_precision.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_bitfield extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_bitfield + /// @{ + + /// Build a mask of 'count' bits + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType mask(genIUType Bits); + + /// Build a mask of 'count' bits + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec mask(vec const& v); + + /// Rotate all bits to the right. All the bits dropped in the right side are inserted back on the left side. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldRotateRight(genIUType In, int Shift); + + /// Rotate all bits to the right. All the bits dropped in the right side are inserted back on the left side. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldRotateRight(vec const& In, int Shift); + + /// Rotate all bits to the left. All the bits dropped in the left side are inserted back on the right side. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldRotateLeft(genIUType In, int Shift); + + /// Rotate all bits to the left. All the bits dropped in the left side are inserted back on the right side. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldRotateLeft(vec const& In, int Shift); + + /// Set to 1 a range of bits. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldFillOne(genIUType Value, int FirstBit, int BitCount); + + /// Set to 1 a range of bits. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldFillOne(vec const& Value, int FirstBit, int BitCount); + + /// Set to 0 a range of bits. + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL genIUType bitfieldFillZero(genIUType Value, int FirstBit, int BitCount); + + /// Set to 0 a range of bits. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Signed and unsigned integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_bitfield + template + GLM_FUNC_DECL vec bitfieldFillZero(vec const& Value, int FirstBit, int BitCount); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int16 bitfieldInterleave(int8 x, int8 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint16 bitfieldInterleave(uint8 x, uint8 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint16 bitfieldInterleave(u8vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u8vec2 bitfieldDeinterleave(glm::uint16 x); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int16 x, int16 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint16 x, uint16 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(u16vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u16vec2 bitfieldDeinterleave(glm::uint32 x); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int32 x, int32 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of x followed by the first bit of y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint32 x, uint32 y); + + /// Interleaves the bits of x and y. + /// The first bit is the first bit of v.x followed by the first bit of v.y. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(u32vec2 const& v); + + /// Deinterleaves the bits of x. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL glm::u32vec2 bitfieldDeinterleave(glm::uint64 x); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int8 x, int8 y, int8 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int16 x, int16 y, int16 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int32 x, int32 y, int32 z); + + /// Interleaves the bits of x, y and z. + /// The first bit is the first bit of x followed by the first bit of y and the first bit of z. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint32 x, uint32 y, uint32 z); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int32 bitfieldInterleave(int8 x, int8 y, int8 z, int8 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z, uint8 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL int64 bitfieldInterleave(int16 x, int16 y, int16 z, int16 w); + + /// Interleaves the bits of x, y, z and w. + /// The first bit is the first bit of x followed by the first bit of y, the first bit of z and finally the first bit of w. + /// The other bits are interleaved following the previous sequence. + /// + /// @see gtc_bitfield + GLM_FUNC_DECL uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z, uint16 w); + + /// @} +} //namespace glm + +#include "bitfield.inl" diff --git a/thirdparty/glm/glm/gtc/bitfield.inl b/thirdparty/glm/glm/gtc/bitfield.inl new file mode 100644 index 0000000..e7a0b4f --- /dev/null +++ b/thirdparty/glm/glm/gtc/bitfield.inl @@ -0,0 +1,635 @@ +/// @ref gtc_bitfield + +#include "../simd/integer.h" + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y); + + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y, PARAM z); + + template + GLM_FUNC_DECL RET bitfieldInterleave(PARAM x, PARAM y, PARAM z, PARAM w); + + template<> + GLM_FUNC_QUALIFIER glm::uint16 bitfieldInterleave(glm::uint8 x, glm::uint8 y) + { + glm::uint16 REG1(x); + glm::uint16 REG2(y); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x3333); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x3333); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x5555); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x5555); + + return REG1 | static_cast(REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint16 x, glm::uint16 y) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x00FF00FF); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x00FF00FF); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F0F0F); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F0F0F); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x33333333); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x33333333); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x55555555); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x55555555); + + return REG1 | (REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint32 x, glm::uint32 y) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x0000FFFF0000FFFFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x0000FFFF0000FFFFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x00FF00FF00FF00FFull); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x00FF00FF00FF00FFull); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x0F0F0F0F0F0F0F0Full); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x0F0F0F0F0F0F0F0Full); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x3333333333333333ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x3333333333333333ull); + + REG1 = ((REG1 << 1) | REG1) & static_cast(0x5555555555555555ull); + REG2 = ((REG2 << 1) | REG2) & static_cast(0x5555555555555555ull); + + return REG1 | (REG2 << 1); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint8 x, glm::uint8 y, glm::uint8 z) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + glm::uint32 REG3(z); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0xFF0000FFu); + REG2 = ((REG2 << 16) | REG2) & static_cast(0xFF0000FFu); + REG3 = ((REG3 << 16) | REG3) & static_cast(0xFF0000FFu); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0x0F00F00Fu); + REG2 = ((REG2 << 8) | REG2) & static_cast(0x0F00F00Fu); + REG3 = ((REG3 << 8) | REG3) & static_cast(0x0F00F00Fu); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0xC30C30C3u); + REG2 = ((REG2 << 4) | REG2) & static_cast(0xC30C30C3u); + REG3 = ((REG3 << 4) | REG3) & static_cast(0xC30C30C3u); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x49249249u); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x49249249u); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x49249249u); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint16 x, glm::uint16 y, glm::uint16 z) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + + REG1 = ((REG1 << 32) | REG1) & static_cast(0xFFFF00000000FFFFull); + REG2 = ((REG2 << 32) | REG2) & static_cast(0xFFFF00000000FFFFull); + REG3 = ((REG3 << 32) | REG3) & static_cast(0xFFFF00000000FFFFull); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x00FF0000FF0000FFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x00FF0000FF0000FFull); + REG3 = ((REG3 << 16) | REG3) & static_cast(0x00FF0000FF0000FFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0xF00F00F00F00F00Full); + REG2 = ((REG2 << 8) | REG2) & static_cast(0xF00F00F00F00F00Full); + REG3 = ((REG3 << 8) | REG3) & static_cast(0xF00F00F00F00F00Full); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x30C30C30C30C30C3ull); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x30C30C30C30C30C3ull); + REG3 = ((REG3 << 4) | REG3) & static_cast(0x30C30C30C30C30C3ull); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x9249249249249249ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x9249249249249249ull); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x9249249249249249ull); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint32 x, glm::uint32 y, glm::uint32 z) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + + REG1 = ((REG1 << 32) | REG1) & static_cast(0xFFFF00000000FFFFull); + REG2 = ((REG2 << 32) | REG2) & static_cast(0xFFFF00000000FFFFull); + REG3 = ((REG3 << 32) | REG3) & static_cast(0xFFFF00000000FFFFull); + + REG1 = ((REG1 << 16) | REG1) & static_cast(0x00FF0000FF0000FFull); + REG2 = ((REG2 << 16) | REG2) & static_cast(0x00FF0000FF0000FFull); + REG3 = ((REG3 << 16) | REG3) & static_cast(0x00FF0000FF0000FFull); + + REG1 = ((REG1 << 8) | REG1) & static_cast(0xF00F00F00F00F00Full); + REG2 = ((REG2 << 8) | REG2) & static_cast(0xF00F00F00F00F00Full); + REG3 = ((REG3 << 8) | REG3) & static_cast(0xF00F00F00F00F00Full); + + REG1 = ((REG1 << 4) | REG1) & static_cast(0x30C30C30C30C30C3ull); + REG2 = ((REG2 << 4) | REG2) & static_cast(0x30C30C30C30C30C3ull); + REG3 = ((REG3 << 4) | REG3) & static_cast(0x30C30C30C30C30C3ull); + + REG1 = ((REG1 << 2) | REG1) & static_cast(0x9249249249249249ull); + REG2 = ((REG2 << 2) | REG2) & static_cast(0x9249249249249249ull); + REG3 = ((REG3 << 2) | REG3) & static_cast(0x9249249249249249ull); + + return REG1 | (REG2 << 1) | (REG3 << 2); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(glm::uint8 x, glm::uint8 y, glm::uint8 z, glm::uint8 w) + { + glm::uint32 REG1(x); + glm::uint32 REG2(y); + glm::uint32 REG3(z); + glm::uint32 REG4(w); + + REG1 = ((REG1 << 12) | REG1) & static_cast(0x000F000Fu); + REG2 = ((REG2 << 12) | REG2) & static_cast(0x000F000Fu); + REG3 = ((REG3 << 12) | REG3) & static_cast(0x000F000Fu); + REG4 = ((REG4 << 12) | REG4) & static_cast(0x000F000Fu); + + REG1 = ((REG1 << 6) | REG1) & static_cast(0x03030303u); + REG2 = ((REG2 << 6) | REG2) & static_cast(0x03030303u); + REG3 = ((REG3 << 6) | REG3) & static_cast(0x03030303u); + REG4 = ((REG4 << 6) | REG4) & static_cast(0x03030303u); + + REG1 = ((REG1 << 3) | REG1) & static_cast(0x11111111u); + REG2 = ((REG2 << 3) | REG2) & static_cast(0x11111111u); + REG3 = ((REG3 << 3) | REG3) & static_cast(0x11111111u); + REG4 = ((REG4 << 3) | REG4) & static_cast(0x11111111u); + + return REG1 | (REG2 << 1) | (REG3 << 2) | (REG4 << 3); + } + + template<> + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(glm::uint16 x, glm::uint16 y, glm::uint16 z, glm::uint16 w) + { + glm::uint64 REG1(x); + glm::uint64 REG2(y); + glm::uint64 REG3(z); + glm::uint64 REG4(w); + + REG1 = ((REG1 << 24) | REG1) & static_cast(0x000000FF000000FFull); + REG2 = ((REG2 << 24) | REG2) & static_cast(0x000000FF000000FFull); + REG3 = ((REG3 << 24) | REG3) & static_cast(0x000000FF000000FFull); + REG4 = ((REG4 << 24) | REG4) & static_cast(0x000000FF000000FFull); + + REG1 = ((REG1 << 12) | REG1) & static_cast(0x000F000F000F000Full); + REG2 = ((REG2 << 12) | REG2) & static_cast(0x000F000F000F000Full); + REG3 = ((REG3 << 12) | REG3) & static_cast(0x000F000F000F000Full); + REG4 = ((REG4 << 12) | REG4) & static_cast(0x000F000F000F000Full); + + REG1 = ((REG1 << 6) | REG1) & static_cast(0x0303030303030303ull); + REG2 = ((REG2 << 6) | REG2) & static_cast(0x0303030303030303ull); + REG3 = ((REG3 << 6) | REG3) & static_cast(0x0303030303030303ull); + REG4 = ((REG4 << 6) | REG4) & static_cast(0x0303030303030303ull); + + REG1 = ((REG1 << 3) | REG1) & static_cast(0x1111111111111111ull); + REG2 = ((REG2 << 3) | REG2) & static_cast(0x1111111111111111ull); + REG3 = ((REG3 << 3) | REG3) & static_cast(0x1111111111111111ull); + REG4 = ((REG4 << 3) | REG4) & static_cast(0x1111111111111111ull); + + return REG1 | (REG2 << 1) | (REG3 << 2) | (REG4 << 3); + } +}//namespace detail + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +#endif + + template + GLM_FUNC_QUALIFIER genIUType mask(genIUType Bits) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'mask' accepts only integer values"); + + return Bits >= sizeof(genIUType) * 8 ? ~static_cast(0) : (static_cast(1) << Bits) - static_cast(1); + } + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + + template + GLM_FUNC_QUALIFIER vec mask(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'mask' accepts only integer values"); + + return detail::functor1::call(mask, v); + } + + template + GLM_FUNC_QUALIFIER genIType bitfieldRotateRight(genIType In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateRight' accepts only integer values"); + + int const BitSize = static_cast(sizeof(genIType) * 8); + return (In << static_cast(Shift)) | (In >> static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldRotateRight(vec const& In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateRight' accepts only integer values"); + + int const BitSize = static_cast(sizeof(T) * 8); + return (In << static_cast(Shift)) | (In >> static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER genIType bitfieldRotateLeft(genIType In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateLeft' accepts only integer values"); + + int const BitSize = static_cast(sizeof(genIType) * 8); + return (In >> static_cast(Shift)) | (In << static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldRotateLeft(vec const& In, int Shift) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "'bitfieldRotateLeft' accepts only integer values"); + + int const BitSize = static_cast(sizeof(T) * 8); + return (In >> static_cast(Shift)) | (In << static_cast(BitSize - Shift)); + } + + template + GLM_FUNC_QUALIFIER genIUType bitfieldFillOne(genIUType Value, int FirstBit, int BitCount) + { + return Value | static_cast(mask(BitCount) << FirstBit); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldFillOne(vec const& Value, int FirstBit, int BitCount) + { + return Value | static_cast(mask(BitCount) << FirstBit); + } + + template + GLM_FUNC_QUALIFIER genIUType bitfieldFillZero(genIUType Value, int FirstBit, int BitCount) + { + return Value & static_cast(~(mask(BitCount) << FirstBit)); + } + + template + GLM_FUNC_QUALIFIER vec bitfieldFillZero(vec const& Value, int FirstBit, int BitCount) + { + return Value & static_cast(~(mask(BitCount) << FirstBit)); + } + + GLM_FUNC_QUALIFIER int16 bitfieldInterleave(int8 x, int8 y) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y; + + union sign16 + { + int16 i; + uint16 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint16 bitfieldInterleave(uint8 x, uint8 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER uint16 bitfieldInterleave(u8vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER u8vec2 bitfieldDeinterleave(glm::uint16 x) + { + uint16 REG1(x); + uint16 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x5555); + REG2 = REG2 & static_cast(0x5555); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x3333); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x3333); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0xFFFF); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0xFFFF); + + return glm::u8vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int16 x, int16 y) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint16 x, uint16 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER glm::uint32 bitfieldInterleave(u16vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER glm::u16vec2 bitfieldDeinterleave(glm::uint32 x) + { + glm::uint32 REG1(x); + glm::uint32 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x55555555); + REG2 = REG2 & static_cast(0x55555555); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x33333333); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x33333333); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F0F0F); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F0F0F); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF00FF); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF00FF); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0x0000FFFF); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0x0000FFFF); + + return glm::u16vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int32 x, int32 y) + { + union sign32 + { + int32 i; + uint32 u; + } sign_x, sign_y; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + result.u = bitfieldInterleave(sign_x.u, sign_y.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint32 x, uint32 y) + { + return detail::bitfieldInterleave(x, y); + } + + GLM_FUNC_QUALIFIER glm::uint64 bitfieldInterleave(u32vec2 const& v) + { + return detail::bitfieldInterleave(v.x, v.y); + } + + GLM_FUNC_QUALIFIER glm::u32vec2 bitfieldDeinterleave(glm::uint64 x) + { + glm::uint64 REG1(x); + glm::uint64 REG2(x >>= 1); + + REG1 = REG1 & static_cast(0x5555555555555555ull); + REG2 = REG2 & static_cast(0x5555555555555555ull); + + REG1 = ((REG1 >> 1) | REG1) & static_cast(0x3333333333333333ull); + REG2 = ((REG2 >> 1) | REG2) & static_cast(0x3333333333333333ull); + + REG1 = ((REG1 >> 2) | REG1) & static_cast(0x0F0F0F0F0F0F0F0Full); + REG2 = ((REG2 >> 2) | REG2) & static_cast(0x0F0F0F0F0F0F0F0Full); + + REG1 = ((REG1 >> 4) | REG1) & static_cast(0x00FF00FF00FF00FFull); + REG2 = ((REG2 >> 4) | REG2) & static_cast(0x00FF00FF00FF00FFull); + + REG1 = ((REG1 >> 8) | REG1) & static_cast(0x0000FFFF0000FFFFull); + REG2 = ((REG2 >> 8) | REG2) & static_cast(0x0000FFFF0000FFFFull); + + REG1 = ((REG1 >> 16) | REG1) & static_cast(0x00000000FFFFFFFFull); + REG2 = ((REG2 >> 16) | REG2) & static_cast(0x00000000FFFFFFFFull); + + return glm::u32vec2(REG1, REG2); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int8 x, int8 y, int8 z) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y, sign_z; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(u8vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int16 x, int16 y, int16 z) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y, sign_z; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u16vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int32 x, int32 y, int32 z) + { + union sign16 + { + int32 i; + uint32 u; + } sign_x, sign_y, sign_z; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint32 x, uint32 y, uint32 z) + { + return detail::bitfieldInterleave(x, y, z); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u32vec3 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z); + } + + GLM_FUNC_QUALIFIER int32 bitfieldInterleave(int8 x, int8 y, int8 z, int8 w) + { + union sign8 + { + int8 i; + uint8 u; + } sign_x, sign_y, sign_z, sign_w; + + union sign32 + { + int32 i; + uint32 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + sign_w.i = w; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u, sign_w.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(uint8 x, uint8 y, uint8 z, uint8 w) + { + return detail::bitfieldInterleave(x, y, z, w); + } + + GLM_FUNC_QUALIFIER uint32 bitfieldInterleave(u8vec4 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z, v.w); + } + + GLM_FUNC_QUALIFIER int64 bitfieldInterleave(int16 x, int16 y, int16 z, int16 w) + { + union sign16 + { + int16 i; + uint16 u; + } sign_x, sign_y, sign_z, sign_w; + + union sign64 + { + int64 i; + uint64 u; + } result; + + sign_x.i = x; + sign_y.i = y; + sign_z.i = z; + sign_w.i = w; + result.u = bitfieldInterleave(sign_x.u, sign_y.u, sign_z.u, sign_w.u); + + return result.i; + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(uint16 x, uint16 y, uint16 z, uint16 w) + { + return detail::bitfieldInterleave(x, y, z, w); + } + + GLM_FUNC_QUALIFIER uint64 bitfieldInterleave(u16vec4 const& v) + { + return detail::bitfieldInterleave(v.x, v.y, v.z, v.w); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/color_space.hpp b/thirdparty/glm/glm/gtc/color_space.hpp new file mode 100644 index 0000000..cffd9f0 --- /dev/null +++ b/thirdparty/glm/glm/gtc/color_space.hpp @@ -0,0 +1,56 @@ +/// @ref gtc_color_space +/// @file glm/gtc/color_space.hpp +/// +/// @see core (dependence) +/// @see gtc_color_space (dependence) +/// +/// @defgroup gtc_color_space GLM_GTC_color_space +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../exponential.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_color_space extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_color_space + /// @{ + + /// Convert a linear color to sRGB color using a standard gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertLinearToSRGB(vec const& ColorLinear); + + /// Convert a linear color to sRGB color using a custom gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertLinearToSRGB(vec const& ColorLinear, T Gamma); + + /// Convert a sRGB color to linear color using a standard gamma correction. + /// IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertSRGBToLinear(vec const& ColorSRGB); + + /// Convert a sRGB color to linear color using a custom gamma correction. + // IEC 61966-2-1:1999 / Rec. 709 specification https://www.w3.org/Graphics/Color/srgb + template + GLM_FUNC_DECL vec convertSRGBToLinear(vec const& ColorSRGB, T Gamma); + + /// @} +} //namespace glm + +#include "color_space.inl" diff --git a/thirdparty/glm/glm/gtc/color_space.inl b/thirdparty/glm/glm/gtc/color_space.inl new file mode 100644 index 0000000..2a90004 --- /dev/null +++ b/thirdparty/glm/glm/gtc/color_space.inl @@ -0,0 +1,84 @@ +/// @ref gtc_color_space + +namespace glm{ +namespace detail +{ + template + struct compute_rgbToSrgb + { + GLM_FUNC_QUALIFIER static vec call(vec const& ColorRGB, T GammaCorrection) + { + vec const ClampedColor(clamp(ColorRGB, static_cast(0), static_cast(1))); + + return mix( + pow(ClampedColor, vec(GammaCorrection)) * static_cast(1.055) - static_cast(0.055), + ClampedColor * static_cast(12.92), + lessThan(ClampedColor, vec(static_cast(0.0031308)))); + } + }; + + template + struct compute_rgbToSrgb<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(vec<4, T, Q> const& ColorRGB, T GammaCorrection) + { + return vec<4, T, Q>(compute_rgbToSrgb<3, T, Q>::call(vec<3, T, Q>(ColorRGB), GammaCorrection), ColorRGB.w); + } + }; + + template + struct compute_srgbToRgb + { + GLM_FUNC_QUALIFIER static vec call(vec const& ColorSRGB, T Gamma) + { + return mix( + pow((ColorSRGB + static_cast(0.055)) * static_cast(0.94786729857819905213270142180095), vec(Gamma)), + ColorSRGB * static_cast(0.07739938080495356037151702786378), + lessThanEqual(ColorSRGB, vec(static_cast(0.04045)))); + } + }; + + template + struct compute_srgbToRgb<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, T, Q> call(vec<4, T, Q> const& ColorSRGB, T Gamma) + { + return vec<4, T, Q>(compute_srgbToRgb<3, T, Q>::call(vec<3, T, Q>(ColorSRGB), Gamma), ColorSRGB.w); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER vec convertLinearToSRGB(vec const& ColorLinear) + { + return detail::compute_rgbToSrgb::call(ColorLinear, static_cast(0.41666)); + } + + // Based on Ian Taylor http://chilliant.blogspot.fr/2012/08/srgb-approximations-for-hlsl.html + template<> + GLM_FUNC_QUALIFIER vec<3, float, lowp> convertLinearToSRGB(vec<3, float, lowp> const& ColorLinear) + { + vec<3, float, lowp> S1 = sqrt(ColorLinear); + vec<3, float, lowp> S2 = sqrt(S1); + vec<3, float, lowp> S3 = sqrt(S2); + return 0.662002687f * S1 + 0.684122060f * S2 - 0.323583601f * S3 - 0.0225411470f * ColorLinear; + } + + template + GLM_FUNC_QUALIFIER vec convertLinearToSRGB(vec const& ColorLinear, T Gamma) + { + return detail::compute_rgbToSrgb::call(ColorLinear, static_cast(1) / Gamma); + } + + template + GLM_FUNC_QUALIFIER vec convertSRGBToLinear(vec const& ColorSRGB) + { + return detail::compute_srgbToRgb::call(ColorSRGB, static_cast(2.4)); + } + + template + GLM_FUNC_QUALIFIER vec convertSRGBToLinear(vec const& ColorSRGB, T Gamma) + { + return detail::compute_srgbToRgb::call(ColorSRGB, Gamma); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/constants.hpp b/thirdparty/glm/glm/gtc/constants.hpp new file mode 100644 index 0000000..6a1f37d --- /dev/null +++ b/thirdparty/glm/glm/gtc/constants.hpp @@ -0,0 +1,170 @@ +/// @ref gtc_constants +/// @file glm/gtc/constants.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_constants GLM_GTC_constants +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Provide a list of constants and precomputed useful values. + +#pragma once + +// Dependencies +#include "../ext/scalar_constants.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_constants extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_constants + /// @{ + + /// Return 0. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType zero(); + + /// Return 1. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one(); + + /// Return pi * 2. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_pi(); + + /// Return unit-circle circumference, or pi * 2. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType tau(); + + /// Return square root of pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_pi(); + + /// Return pi / 2. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType half_pi(); + + /// Return pi / 2 * 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType three_over_two_pi(); + + /// Return pi / 4. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType quarter_pi(); + + /// Return 1 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_pi(); + + /// Return 1 / (pi * 2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_two_pi(); + + /// Return 2 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_over_pi(); + + /// Return 4 / pi. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType four_over_pi(); + + /// Return 2 / sqrt(pi). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_over_root_pi(); + + /// Return 1 / sqrt(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType one_over_root_two(); + + /// Return sqrt(pi / 2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_half_pi(); + + /// Return sqrt(2 * pi). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_two_pi(); + + /// Return sqrt(ln(4)). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_ln_four(); + + /// Return e constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType e(); + + /// Return Euler's constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType euler(); + + /// Return sqrt(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_two(); + + /// Return sqrt(3). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_three(); + + /// Return sqrt(5). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType root_five(); + + /// Return ln(2). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_two(); + + /// Return ln(10). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_ten(); + + /// Return ln(ln(2)). + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType ln_ln_two(); + + /// Return 1 / 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType third(); + + /// Return 2 / 3. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType two_thirds(); + + /// Return the golden ratio constant. + /// @see gtc_constants + template + GLM_FUNC_DECL GLM_CONSTEXPR genType golden_ratio(); + + /// @} +} //namespace glm + +#include "constants.inl" diff --git a/thirdparty/glm/glm/gtc/constants.inl b/thirdparty/glm/glm/gtc/constants.inl new file mode 100644 index 0000000..e9d3776 --- /dev/null +++ b/thirdparty/glm/glm/gtc/constants.inl @@ -0,0 +1,173 @@ +/// @ref gtc_constants + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType zero() + { + return genType(0); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one() + { + return genType(1); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_pi() + { + return genType(6.28318530717958647692528676655900576); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType tau() + { + return two_pi(); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_pi() + { + return genType(1.772453850905516027); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType half_pi() + { + return genType(1.57079632679489661923132169163975144); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType three_over_two_pi() + { + return genType(4.71238898038468985769396507491925432); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType quarter_pi() + { + return genType(0.785398163397448309615660845819875721); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_pi() + { + return genType(0.318309886183790671537767526745028724); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_two_pi() + { + return genType(0.159154943091895335768883763372514362); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_over_pi() + { + return genType(0.636619772367581343075535053490057448); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType four_over_pi() + { + return genType(1.273239544735162686151070106980114898); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_over_root_pi() + { + return genType(1.12837916709551257389615890312154517); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType one_over_root_two() + { + return genType(0.707106781186547524400844362104849039); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_half_pi() + { + return genType(1.253314137315500251); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_two_pi() + { + return genType(2.506628274631000502); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_ln_four() + { + return genType(1.17741002251547469); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType e() + { + return genType(2.71828182845904523536); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType euler() + { + return genType(0.577215664901532860606); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_two() + { + return genType(1.41421356237309504880168872420969808); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_three() + { + return genType(1.73205080756887729352744634150587236); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType root_five() + { + return genType(2.23606797749978969640917366873127623); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_two() + { + return genType(0.693147180559945309417232121458176568); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_ten() + { + return genType(2.30258509299404568401799145468436421); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType ln_ln_two() + { + return genType(-0.3665129205816643); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType third() + { + return genType(0.3333333333333333333333333333333333333333); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType two_thirds() + { + return genType(0.666666666666666666666666666666666666667); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR genType golden_ratio() + { + return genType(1.61803398874989484820458683436563811); + } + +} //namespace glm diff --git a/thirdparty/glm/glm/gtc/epsilon.hpp b/thirdparty/glm/glm/gtc/epsilon.hpp new file mode 100644 index 0000000..640439b --- /dev/null +++ b/thirdparty/glm/glm/gtc/epsilon.hpp @@ -0,0 +1,60 @@ +/// @ref gtc_epsilon +/// @file glm/gtc/epsilon.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_epsilon GLM_GTC_epsilon +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Comparison functions for a user defined epsilon values. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_epsilon extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_epsilon + /// @{ + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL vec epsilonEqual(vec const& x, vec const& y, T const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL bool epsilonEqual(genType const& x, genType const& y, genType const& epsilon); + + /// Returns the component-wise comparison of |x - y| < epsilon. + /// True if this expression is not satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL vec epsilonNotEqual(vec const& x, vec const& y, T const& epsilon); + + /// Returns the component-wise comparison of |x - y| >= epsilon. + /// True if this expression is not satisfied. + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL bool epsilonNotEqual(genType const& x, genType const& y, genType const& epsilon); + + /// @} +}//namespace glm + +#include "epsilon.inl" diff --git a/thirdparty/glm/glm/gtc/epsilon.inl b/thirdparty/glm/glm/gtc/epsilon.inl new file mode 100644 index 0000000..508b9f8 --- /dev/null +++ b/thirdparty/glm/glm/gtc/epsilon.inl @@ -0,0 +1,80 @@ +/// @ref gtc_epsilon + +// Dependency: +#include "../vector_relational.hpp" +#include "../common.hpp" + +namespace glm +{ + template<> + GLM_FUNC_QUALIFIER bool epsilonEqual + ( + float const& x, + float const& y, + float const& epsilon + ) + { + return abs(x - y) < epsilon; + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonEqual + ( + double const& x, + double const& y, + double const& epsilon + ) + { + return abs(x - y) < epsilon; + } + + template + GLM_FUNC_QUALIFIER vec epsilonEqual(vec const& x, vec const& y, T const& epsilon) + { + return lessThan(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec epsilonEqual(vec const& x, vec const& y, vec const& epsilon) + { + return lessThan(abs(x - y), vec(epsilon)); + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonNotEqual(float const& x, float const& y, float const& epsilon) + { + return abs(x - y) >= epsilon; + } + + template<> + GLM_FUNC_QUALIFIER bool epsilonNotEqual(double const& x, double const& y, double const& epsilon) + { + return abs(x - y) >= epsilon; + } + + template + GLM_FUNC_QUALIFIER vec epsilonNotEqual(vec const& x, vec const& y, T const& epsilon) + { + return greaterThanEqual(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec epsilonNotEqual(vec const& x, vec const& y, vec const& epsilon) + { + return greaterThanEqual(abs(x - y), vec(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> epsilonEqual(qua const& x, qua const& y, T const& epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return lessThan(abs(v), vec<4, T, Q>(epsilon)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> epsilonNotEqual(qua const& x, qua const& y, T const& epsilon) + { + vec<4, T, Q> v(x.x - y.x, x.y - y.y, x.z - y.z, x.w - y.w); + return greaterThanEqual(abs(v), vec<4, T, Q>(epsilon)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/integer.hpp b/thirdparty/glm/glm/gtc/integer.hpp new file mode 100644 index 0000000..cff08dc --- /dev/null +++ b/thirdparty/glm/glm/gtc/integer.hpp @@ -0,0 +1,43 @@ +/// @ref gtc_integer +/// @file glm/gtc/integer.hpp +/// +/// @see core (dependence) +/// @see gtc_integer (dependence) +/// +/// @defgroup gtc_integer GLM_GTC_integer +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../common.hpp" +#include "../integer.hpp" +#include "../exponential.hpp" +#include "../ext/scalar_common.hpp" +#include "../ext/vector_common.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_integer + /// @{ + + /// Returns the log2 of x for integer values. Useful to compute mipmap count from the texture size. + /// @see gtc_integer + template + GLM_FUNC_DECL vec log2(vec const& v); + + /// @} +} //namespace glm + +#include "integer.inl" diff --git a/thirdparty/glm/glm/gtc/integer.inl b/thirdparty/glm/glm/gtc/integer.inl new file mode 100644 index 0000000..5f66dfe --- /dev/null +++ b/thirdparty/glm/glm/gtc/integer.inl @@ -0,0 +1,33 @@ +/// @ref gtc_integer + +namespace glm{ +namespace detail +{ + template + struct compute_log2 + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + //Equivalent to return findMSB(vec); but save one function call in ASM with VC + //return findMSB(vec); + return vec(detail::compute_findMSB_vec::call(v)); + } + }; + +# if GLM_HAS_BITSCAN_WINDOWS + template + struct compute_log2<4, int, Q, false, Aligned> + { + GLM_FUNC_QUALIFIER static vec<4, int, Q> call(vec<4, int, Q> const& v) + { + vec<4, int, Q> Result; + _BitScanReverse(reinterpret_cast(&Result.x), v.x); + _BitScanReverse(reinterpret_cast(&Result.y), v.y); + _BitScanReverse(reinterpret_cast(&Result.z), v.z); + _BitScanReverse(reinterpret_cast(&Result.w), v.w); + return Result; + } + }; +# endif//GLM_HAS_BITSCAN_WINDOWS +}//namespace detail +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/matrix_access.hpp b/thirdparty/glm/glm/gtc/matrix_access.hpp new file mode 100644 index 0000000..4935ba7 --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_access.hpp @@ -0,0 +1,60 @@ +/// @ref gtc_matrix_access +/// @file glm/gtc/matrix_access.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_access GLM_GTC_matrix_access +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines functions to access rows or columns of a matrix easily. + +#pragma once + +// Dependency: +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_access extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_access + /// @{ + + /// Get a specific row of a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL typename genType::row_type row( + genType const& m, + length_t index); + + /// Set a specific row to a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL genType row( + genType const& m, + length_t index, + typename genType::row_type const& x); + + /// Get a specific column of a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL typename genType::col_type column( + genType const& m, + length_t index); + + /// Set a specific column to a matrix. + /// @see gtc_matrix_access + template + GLM_FUNC_DECL genType column( + genType const& m, + length_t index, + typename genType::col_type const& x); + + /// @} +}//namespace glm + +#include "matrix_access.inl" diff --git a/thirdparty/glm/glm/gtc/matrix_access.inl b/thirdparty/glm/glm/gtc/matrix_access.inl new file mode 100644 index 0000000..09fcc10 --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_access.inl @@ -0,0 +1,62 @@ +/// @ref gtc_matrix_access + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType row + ( + genType const& m, + length_t index, + typename genType::row_type const& x + ) + { + assert(index >= 0 && index < m[0].length()); + + genType Result = m; + for(length_t i = 0; i < m.length(); ++i) + Result[i][index] = x[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER typename genType::row_type row + ( + genType const& m, + length_t index + ) + { + assert(index >= 0 && index < m[0].length()); + + typename genType::row_type Result(0); + for(length_t i = 0; i < m.length(); ++i) + Result[i] = m[i][index]; + return Result; + } + + template + GLM_FUNC_QUALIFIER genType column + ( + genType const& m, + length_t index, + typename genType::col_type const& x + ) + { + assert(index >= 0 && index < m.length()); + + genType Result = m; + Result[index] = x; + return Result; + } + + template + GLM_FUNC_QUALIFIER typename genType::col_type column + ( + genType const& m, + length_t index + ) + { + assert(index >= 0 && index < m.length()); + + return m[index]; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/matrix_integer.hpp b/thirdparty/glm/glm/gtc/matrix_integer.hpp new file mode 100644 index 0000000..d7ebdc7 --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_integer.hpp @@ -0,0 +1,433 @@ +/// @ref gtc_matrix_integer +/// @file glm/gtc/matrix_integer.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_integer GLM_GTC_matrix_integer +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines a number of matrices with integer types. + +#pragma once + +// Dependency: +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_integer + /// @{ + + /// High-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, highp> highp_imat2; + + /// High-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, highp> highp_imat3; + + /// High-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, highp> highp_imat4; + + /// High-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, highp> highp_imat2x2; + + /// High-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, highp> highp_imat2x3; + + /// High-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, highp> highp_imat2x4; + + /// High-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, highp> highp_imat3x2; + + /// High-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, highp> highp_imat3x3; + + /// High-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, highp> highp_imat3x4; + + /// High-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, highp> highp_imat4x2; + + /// High-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, highp> highp_imat4x3; + + /// High-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, highp> highp_imat4x4; + + + /// Medium-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, mediump> mediump_imat2; + + /// Medium-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, mediump> mediump_imat3; + + /// Medium-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, mediump> mediump_imat4; + + + /// Medium-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, mediump> mediump_imat2x2; + + /// Medium-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, mediump> mediump_imat2x3; + + /// Medium-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, mediump> mediump_imat2x4; + + /// Medium-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, mediump> mediump_imat3x2; + + /// Medium-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, mediump> mediump_imat3x3; + + /// Medium-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, mediump> mediump_imat3x4; + + /// Medium-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, mediump> mediump_imat4x2; + + /// Medium-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, mediump> mediump_imat4x3; + + /// Medium-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, mediump> mediump_imat4x4; + + + /// Low-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, lowp> lowp_imat2; + + /// Low-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, lowp> lowp_imat3; + + /// Low-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, lowp> lowp_imat4; + + + /// Low-qualifier signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, lowp> lowp_imat2x2; + + /// Low-qualifier signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, lowp> lowp_imat2x3; + + /// Low-qualifier signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, lowp> lowp_imat2x4; + + /// Low-qualifier signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, lowp> lowp_imat3x2; + + /// Low-qualifier signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, lowp> lowp_imat3x3; + + /// Low-qualifier signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, lowp> lowp_imat3x4; + + /// Low-qualifier signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, lowp> lowp_imat4x2; + + /// Low-qualifier signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, lowp> lowp_imat4x3; + + /// Low-qualifier signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, lowp> lowp_imat4x4; + + + /// High-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, highp> highp_umat2; + + /// High-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, highp> highp_umat3; + + /// High-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, highp> highp_umat4; + + /// High-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, highp> highp_umat2x2; + + /// High-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, highp> highp_umat2x3; + + /// High-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, highp> highp_umat2x4; + + /// High-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, highp> highp_umat3x2; + + /// High-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, highp> highp_umat3x3; + + /// High-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, highp> highp_umat3x4; + + /// High-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, highp> highp_umat4x2; + + /// High-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, highp> highp_umat4x3; + + /// High-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, highp> highp_umat4x4; + + + /// Medium-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, mediump> mediump_umat2; + + /// Medium-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, mediump> mediump_umat3; + + /// Medium-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, mediump> mediump_umat4; + + + /// Medium-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, mediump> mediump_umat2x2; + + /// Medium-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, mediump> mediump_umat2x3; + + /// Medium-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, mediump> mediump_umat2x4; + + /// Medium-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, mediump> mediump_umat3x2; + + /// Medium-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, mediump> mediump_umat3x3; + + /// Medium-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, mediump> mediump_umat3x4; + + /// Medium-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, mediump> mediump_umat4x2; + + /// Medium-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, mediump> mediump_umat4x3; + + /// Medium-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, mediump> mediump_umat4x4; + + + /// Low-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, lowp> lowp_umat2; + + /// Low-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, lowp> lowp_umat3; + + /// Low-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, lowp> lowp_umat4; + + + /// Low-qualifier unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, lowp> lowp_umat2x2; + + /// Low-qualifier unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, lowp> lowp_umat2x3; + + /// Low-qualifier unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, lowp> lowp_umat2x4; + + /// Low-qualifier unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, lowp> lowp_umat3x2; + + /// Low-qualifier unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, lowp> lowp_umat3x3; + + /// Low-qualifier unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, lowp> lowp_umat3x4; + + /// Low-qualifier unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, lowp> lowp_umat4x2; + + /// Low-qualifier unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, lowp> lowp_umat4x3; + + /// Low-qualifier unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, lowp> lowp_umat4x4; + + + + /// Signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, defaultp> imat2; + + /// Signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, defaultp> imat3; + + /// Signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, defaultp> imat4; + + /// Signed integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, int, defaultp> imat2x2; + + /// Signed integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, int, defaultp> imat2x3; + + /// Signed integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, int, defaultp> imat2x4; + + /// Signed integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, int, defaultp> imat3x2; + + /// Signed integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, int, defaultp> imat3x3; + + /// Signed integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, int, defaultp> imat3x4; + + /// Signed integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, int, defaultp> imat4x2; + + /// Signed integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, int, defaultp> imat4x3; + + /// Signed integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, int, defaultp> imat4x4; + + + + /// Unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, defaultp> umat2; + + /// Unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, defaultp> umat3; + + /// Unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, defaultp> umat4; + + /// Unsigned integer 2x2 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 2, uint, defaultp> umat2x2; + + /// Unsigned integer 2x3 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 3, uint, defaultp> umat2x3; + + /// Unsigned integer 2x4 matrix. + /// @see gtc_matrix_integer + typedef mat<2, 4, uint, defaultp> umat2x4; + + /// Unsigned integer 3x2 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 2, uint, defaultp> umat3x2; + + /// Unsigned integer 3x3 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 3, uint, defaultp> umat3x3; + + /// Unsigned integer 3x4 matrix. + /// @see gtc_matrix_integer + typedef mat<3, 4, uint, defaultp> umat3x4; + + /// Unsigned integer 4x2 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 2, uint, defaultp> umat4x2; + + /// Unsigned integer 4x3 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 3, uint, defaultp> umat4x3; + + /// Unsigned integer 4x4 matrix. + /// @see gtc_matrix_integer + typedef mat<4, 4, uint, defaultp> umat4x4; + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/matrix_inverse.hpp b/thirdparty/glm/glm/gtc/matrix_inverse.hpp new file mode 100644 index 0000000..75d53f2 --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_inverse.hpp @@ -0,0 +1,50 @@ +/// @ref gtc_matrix_inverse +/// @file glm/gtc/matrix_inverse.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_matrix_inverse GLM_GTC_matrix_inverse +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines additional matrix inverting functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../matrix.hpp" +#include "../mat2x2.hpp" +#include "../mat3x3.hpp" +#include "../mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_inverse extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_matrix_inverse + /// @{ + + /// Fast matrix inverse for affine matrix. + /// + /// @param m Input matrix to invert. + /// @tparam genType Squared floating-point matrix: half, float or double. Inverse of matrix based of half-qualifier floating point value is highly inaccurate. + /// @see gtc_matrix_inverse + template + GLM_FUNC_DECL genType affineInverse(genType const& m); + + /// Compute the inverse transpose of a matrix. + /// + /// @param m Input matrix to invert transpose. + /// @tparam genType Squared floating-point matrix: half, float or double. Inverse of matrix based of half-qualifier floating point value is highly inaccurate. + /// @see gtc_matrix_inverse + template + GLM_FUNC_DECL genType inverseTranspose(genType const& m); + + /// @} +}//namespace glm + +#include "matrix_inverse.inl" diff --git a/thirdparty/glm/glm/gtc/matrix_inverse.inl b/thirdparty/glm/glm/gtc/matrix_inverse.inl new file mode 100644 index 0000000..c004b9e --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_inverse.inl @@ -0,0 +1,118 @@ +/// @ref gtc_matrix_inverse + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> affineInverse(mat<3, 3, T, Q> const& m) + { + mat<2, 2, T, Q> const Inv(inverse(mat<2, 2, T, Q>(m))); + + return mat<3, 3, T, Q>( + vec<3, T, Q>(Inv[0], static_cast(0)), + vec<3, T, Q>(Inv[1], static_cast(0)), + vec<3, T, Q>(-Inv * vec<2, T, Q>(m[2]), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> affineInverse(mat<4, 4, T, Q> const& m) + { + mat<3, 3, T, Q> const Inv(inverse(mat<3, 3, T, Q>(m))); + + return mat<4, 4, T, Q>( + vec<4, T, Q>(Inv[0], static_cast(0)), + vec<4, T, Q>(Inv[1], static_cast(0)), + vec<4, T, Q>(Inv[2], static_cast(0)), + vec<4, T, Q>(-Inv * vec<3, T, Q>(m[3]), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> inverseTranspose(mat<2, 2, T, Q> const& m) + { + T Determinant = m[0][0] * m[1][1] - m[1][0] * m[0][1]; + + mat<2, 2, T, Q> Inverse( + + m[1][1] / Determinant, + - m[0][1] / Determinant, + - m[1][0] / Determinant, + + m[0][0] / Determinant); + + return Inverse; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> inverseTranspose(mat<3, 3, T, Q> const& m) + { + T Determinant = + + m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) + - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]); + + mat<3, 3, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * m[2][2] - m[2][1] * m[1][2]); + Inverse[0][1] = - (m[1][0] * m[2][2] - m[2][0] * m[1][2]); + Inverse[0][2] = + (m[1][0] * m[2][1] - m[2][0] * m[1][1]); + Inverse[1][0] = - (m[0][1] * m[2][2] - m[2][1] * m[0][2]); + Inverse[1][1] = + (m[0][0] * m[2][2] - m[2][0] * m[0][2]); + Inverse[1][2] = - (m[0][0] * m[2][1] - m[2][0] * m[0][1]); + Inverse[2][0] = + (m[0][1] * m[1][2] - m[1][1] * m[0][2]); + Inverse[2][1] = - (m[0][0] * m[1][2] - m[1][0] * m[0][2]); + Inverse[2][2] = + (m[0][0] * m[1][1] - m[1][0] * m[0][1]); + Inverse /= Determinant; + + return Inverse; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> inverseTranspose(mat<4, 4, T, Q> const& m) + { + T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + T SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + T SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + T SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + T SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + T SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + T SubFactor11 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + T SubFactor12 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + T SubFactor13 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + T SubFactor14 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + T SubFactor15 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + T SubFactor16 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + T SubFactor17 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + mat<4, 4, T, Q> Inverse; + Inverse[0][0] = + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02); + Inverse[0][1] = - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04); + Inverse[0][2] = + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05); + Inverse[0][3] = - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05); + + Inverse[1][0] = - (m[0][1] * SubFactor00 - m[0][2] * SubFactor01 + m[0][3] * SubFactor02); + Inverse[1][1] = + (m[0][0] * SubFactor00 - m[0][2] * SubFactor03 + m[0][3] * SubFactor04); + Inverse[1][2] = - (m[0][0] * SubFactor01 - m[0][1] * SubFactor03 + m[0][3] * SubFactor05); + Inverse[1][3] = + (m[0][0] * SubFactor02 - m[0][1] * SubFactor04 + m[0][2] * SubFactor05); + + Inverse[2][0] = + (m[0][1] * SubFactor06 - m[0][2] * SubFactor07 + m[0][3] * SubFactor08); + Inverse[2][1] = - (m[0][0] * SubFactor06 - m[0][2] * SubFactor09 + m[0][3] * SubFactor10); + Inverse[2][2] = + (m[0][0] * SubFactor07 - m[0][1] * SubFactor09 + m[0][3] * SubFactor11); + Inverse[2][3] = - (m[0][0] * SubFactor08 - m[0][1] * SubFactor10 + m[0][2] * SubFactor11); + + Inverse[3][0] = - (m[0][1] * SubFactor12 - m[0][2] * SubFactor13 + m[0][3] * SubFactor14); + Inverse[3][1] = + (m[0][0] * SubFactor12 - m[0][2] * SubFactor15 + m[0][3] * SubFactor16); + Inverse[3][2] = - (m[0][0] * SubFactor13 - m[0][1] * SubFactor15 + m[0][3] * SubFactor17); + Inverse[3][3] = + (m[0][0] * SubFactor14 - m[0][1] * SubFactor16 + m[0][2] * SubFactor17); + + T Determinant = + + m[0][0] * Inverse[0][0] + + m[0][1] * Inverse[0][1] + + m[0][2] * Inverse[0][2] + + m[0][3] * Inverse[0][3]; + + Inverse /= Determinant; + + return Inverse; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/matrix_transform.hpp b/thirdparty/glm/glm/gtc/matrix_transform.hpp new file mode 100644 index 0000000..612418f --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_transform.hpp @@ -0,0 +1,36 @@ +/// @ref gtc_matrix_transform +/// @file glm/gtc/matrix_transform.hpp +/// +/// @see core (dependence) +/// @see gtx_transform +/// @see gtx_transform2 +/// +/// @defgroup gtc_matrix_transform GLM_GTC_matrix_transform +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines functions that generate common transformation matrices. +/// +/// The matrices generated by this extension use standard OpenGL fixed-function +/// conventions. For example, the lookAt function generates a transform from world +/// space into the specific eye space that the projective matrix functions +/// (perspective, ortho, etc) are designed to expect. The OpenGL compatibility +/// specifications defines the particular layout of this eye space. + +#pragma once + +// Dependencies +#include "../mat4x4.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../ext/matrix_projection.hpp" +#include "../ext/matrix_clip_space.hpp" +#include "../ext/matrix_transform.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_matrix_transform extension included") +#endif + +#include "matrix_transform.inl" diff --git a/thirdparty/glm/glm/gtc/matrix_transform.inl b/thirdparty/glm/glm/gtc/matrix_transform.inl new file mode 100644 index 0000000..15b46bc --- /dev/null +++ b/thirdparty/glm/glm/gtc/matrix_transform.inl @@ -0,0 +1,3 @@ +#include "../geometric.hpp" +#include "../trigonometric.hpp" +#include "../matrix.hpp" diff --git a/thirdparty/glm/glm/gtc/noise.hpp b/thirdparty/glm/glm/gtc/noise.hpp new file mode 100644 index 0000000..ab1772e --- /dev/null +++ b/thirdparty/glm/glm/gtc/noise.hpp @@ -0,0 +1,61 @@ +/// @ref gtc_noise +/// @file glm/gtc/noise.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_noise GLM_GTC_noise +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines 2D, 3D and 4D procedural noise functions +/// Based on the work of Stefan Gustavson and Ashima Arts on "webgl-noise": +/// https://github.com/ashima/webgl-noise +/// Following Stefan Gustavson's paper "Simplex noise demystified": +/// http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_noise.hpp" +#include "../geometric.hpp" +#include "../common.hpp" +#include "../vector_relational.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_noise extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_noise + /// @{ + + /// Classic perlin noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T perlin( + vec const& p); + + /// Periodic perlin noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T perlin( + vec const& p, + vec const& rep); + + /// Simplex noise. + /// @see gtc_noise + template + GLM_FUNC_DECL T simplex( + vec const& p); + + /// @} +}//namespace glm + +#include "noise.inl" diff --git a/thirdparty/glm/glm/gtc/noise.inl b/thirdparty/glm/glm/gtc/noise.inl new file mode 100644 index 0000000..a1cf399 --- /dev/null +++ b/thirdparty/glm/glm/gtc/noise.inl @@ -0,0 +1,807 @@ +/// @ref gtc_noise +/// +// Based on the work of Stefan Gustavson and Ashima Arts on "webgl-noise": +// https://github.com/ashima/webgl-noise +// Following Stefan Gustavson's paper "Simplex noise demystified": +// http://www.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER vec<4, T, Q> grad4(T const& j, vec<4, T, Q> const& ip) + { + vec<3, T, Q> pXYZ = floor(fract(vec<3, T, Q>(j) * vec<3, T, Q>(ip)) * T(7)) * ip[2] - T(1); + T pW = static_cast(1.5) - dot(abs(pXYZ), vec<3, T, Q>(1)); + vec<4, T, Q> s = vec<4, T, Q>(lessThan(vec<4, T, Q>(pXYZ, pW), vec<4, T, Q>(0.0))); + pXYZ = pXYZ + (vec<3, T, Q>(s) * T(2) - T(1)) * s.w; + return vec<4, T, Q>(pXYZ, pW); + } +}//namespace detail + + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<2, T, Q> const& Position) + { + vec<4, T, Q> Pi = glm::floor(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) + vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + vec<4, T, Q> Pf = glm::fract(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) - vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, vec<4, T, Q>(289)); // To avoid truncation effects in permutation + vec<4, T, Q> ix(Pi.x, Pi.z, Pi.x, Pi.z); + vec<4, T, Q> iy(Pi.y, Pi.y, Pi.w, Pi.w); + vec<4, T, Q> fx(Pf.x, Pf.z, Pf.x, Pf.z); + vec<4, T, Q> fy(Pf.y, Pf.y, Pf.w, Pf.w); + + vec<4, T, Q> i = detail::permute(detail::permute(ix) + iy); + + vec<4, T, Q> gx = static_cast(2) * glm::fract(i / T(41)) - T(1); + vec<4, T, Q> gy = glm::abs(gx) - T(0.5); + vec<4, T, Q> tx = glm::floor(gx + T(0.5)); + gx = gx - tx; + + vec<2, T, Q> g00(gx.x, gy.x); + vec<2, T, Q> g10(gx.y, gy.y); + vec<2, T, Q> g01(gx.z, gy.z); + vec<2, T, Q> g11(gx.w, gy.w); + + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + + T n00 = dot(g00, vec<2, T, Q>(fx.x, fy.x)); + T n10 = dot(g10, vec<2, T, Q>(fx.y, fy.y)); + T n01 = dot(g01, vec<2, T, Q>(fx.z, fy.z)); + T n11 = dot(g11, vec<2, T, Q>(fx.w, fy.w)); + + vec<2, T, Q> fade_xy = detail::fade(vec<2, T, Q>(Pf.x, Pf.y)); + vec<2, T, Q> n_x = mix(vec<2, T, Q>(n00, n01), vec<2, T, Q>(n10, n11), fade_xy.x); + T n_xy = mix(n_x.x, n_x.y, fade_xy.y); + return T(2.3) * n_xy; + } + + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& Position) + { + vec<3, T, Q> Pi0 = floor(Position); // Integer part for indexing + vec<3, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = detail::mod289(Pi0); + Pi1 = detail::mod289(Pi1); + vec<3, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(vec<2, T, Q>(Pi0.y), vec<2, T, Q>(Pi1.y)); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 * T(1.0 / 7.0); + vec<4, T, Q> gy0 = fract(floor(gx0) * T(1.0 / 7.0)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0.0)); + gx0 -= sz0 * (step(T(0), gx0) - T(0.5)); + gy0 -= sz0 * (step(T(0), gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 * T(1.0 / 7.0); + vec<4, T, Q> gy1 = fract(floor(gx1) * T(1.0 / 7.0)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(0.0)); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = detail::fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix(vec<2, T, Q>(n_z.x, n_z.y), vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + /* + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& P) + { + vec<3, T, Q> Pi0 = floor(P); // Integer part for indexing + vec<3, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = mod(Pi0, T(289)); + Pi1 = mod(Pi1, T(289)); + vec<3, T, Q> Pf0 = fract(P); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = permute(permute(ix) + iy); + vec<4, T, Q> ixy0 = permute(ixy + iz0); + vec<4, T, Q> ixy1 = permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 / T(7); + vec<4, T, Q> gy0 = fract(floor(gx0) / T(7)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0.0)); + gx0 -= sz0 * (step(0.0, gx0) - T(0.5)); + gy0 -= sz0 * (step(0.0, gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 / T(7); + vec<4, T, Q> gy1 = fract(floor(gx1) / T(7)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(0.0)); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix( + vec<2, T, Q>(n_z.x, n_z.y), + vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + */ + // Classic Perlin noise + template + GLM_FUNC_QUALIFIER T perlin(vec<4, T, Q> const& Position) + { + vec<4, T, Q> Pi0 = floor(Position); // Integer part for indexing + vec<4, T, Q> Pi1 = Pi0 + T(1); // Integer part + 1 + Pi0 = mod(Pi0, vec<4, T, Q>(289)); + Pi1 = mod(Pi1, vec<4, T, Q>(289)); + vec<4, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<4, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + vec<4, T, Q> iw0(Pi0.w); + vec<4, T, Q> iw1(Pi1.w); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + vec<4, T, Q> ixy00 = detail::permute(ixy0 + iw0); + vec<4, T, Q> ixy01 = detail::permute(ixy0 + iw1); + vec<4, T, Q> ixy10 = detail::permute(ixy1 + iw0); + vec<4, T, Q> ixy11 = detail::permute(ixy1 + iw1); + + vec<4, T, Q> gx00 = ixy00 / T(7); + vec<4, T, Q> gy00 = floor(gx00) / T(7); + vec<4, T, Q> gz00 = floor(gy00) / T(6); + gx00 = fract(gx00) - T(0.5); + gy00 = fract(gy00) - T(0.5); + gz00 = fract(gz00) - T(0.5); + vec<4, T, Q> gw00 = vec<4, T, Q>(0.75) - abs(gx00) - abs(gy00) - abs(gz00); + vec<4, T, Q> sw00 = step(gw00, vec<4, T, Q>(0.0)); + gx00 -= sw00 * (step(T(0), gx00) - T(0.5)); + gy00 -= sw00 * (step(T(0), gy00) - T(0.5)); + + vec<4, T, Q> gx01 = ixy01 / T(7); + vec<4, T, Q> gy01 = floor(gx01) / T(7); + vec<4, T, Q> gz01 = floor(gy01) / T(6); + gx01 = fract(gx01) - T(0.5); + gy01 = fract(gy01) - T(0.5); + gz01 = fract(gz01) - T(0.5); + vec<4, T, Q> gw01 = vec<4, T, Q>(0.75) - abs(gx01) - abs(gy01) - abs(gz01); + vec<4, T, Q> sw01 = step(gw01, vec<4, T, Q>(0.0)); + gx01 -= sw01 * (step(T(0), gx01) - T(0.5)); + gy01 -= sw01 * (step(T(0), gy01) - T(0.5)); + + vec<4, T, Q> gx10 = ixy10 / T(7); + vec<4, T, Q> gy10 = floor(gx10) / T(7); + vec<4, T, Q> gz10 = floor(gy10) / T(6); + gx10 = fract(gx10) - T(0.5); + gy10 = fract(gy10) - T(0.5); + gz10 = fract(gz10) - T(0.5); + vec<4, T, Q> gw10 = vec<4, T, Q>(0.75) - abs(gx10) - abs(gy10) - abs(gz10); + vec<4, T, Q> sw10 = step(gw10, vec<4, T, Q>(0)); + gx10 -= sw10 * (step(T(0), gx10) - T(0.5)); + gy10 -= sw10 * (step(T(0), gy10) - T(0.5)); + + vec<4, T, Q> gx11 = ixy11 / T(7); + vec<4, T, Q> gy11 = floor(gx11) / T(7); + vec<4, T, Q> gz11 = floor(gy11) / T(6); + gx11 = fract(gx11) - T(0.5); + gy11 = fract(gy11) - T(0.5); + gz11 = fract(gz11) - T(0.5); + vec<4, T, Q> gw11 = vec<4, T, Q>(0.75) - abs(gx11) - abs(gy11) - abs(gz11); + vec<4, T, Q> sw11 = step(gw11, vec<4, T, Q>(0.0)); + gx11 -= sw11 * (step(T(0), gx11) - T(0.5)); + gy11 -= sw11 * (step(T(0), gy11) - T(0.5)); + + vec<4, T, Q> g0000(gx00.x, gy00.x, gz00.x, gw00.x); + vec<4, T, Q> g1000(gx00.y, gy00.y, gz00.y, gw00.y); + vec<4, T, Q> g0100(gx00.z, gy00.z, gz00.z, gw00.z); + vec<4, T, Q> g1100(gx00.w, gy00.w, gz00.w, gw00.w); + vec<4, T, Q> g0010(gx10.x, gy10.x, gz10.x, gw10.x); + vec<4, T, Q> g1010(gx10.y, gy10.y, gz10.y, gw10.y); + vec<4, T, Q> g0110(gx10.z, gy10.z, gz10.z, gw10.z); + vec<4, T, Q> g1110(gx10.w, gy10.w, gz10.w, gw10.w); + vec<4, T, Q> g0001(gx01.x, gy01.x, gz01.x, gw01.x); + vec<4, T, Q> g1001(gx01.y, gy01.y, gz01.y, gw01.y); + vec<4, T, Q> g0101(gx01.z, gy01.z, gz01.z, gw01.z); + vec<4, T, Q> g1101(gx01.w, gy01.w, gz01.w, gw01.w); + vec<4, T, Q> g0011(gx11.x, gy11.x, gz11.x, gw11.x); + vec<4, T, Q> g1011(gx11.y, gy11.y, gz11.y, gw11.y); + vec<4, T, Q> g0111(gx11.z, gy11.z, gz11.z, gw11.z); + vec<4, T, Q> g1111(gx11.w, gy11.w, gz11.w, gw11.w); + + vec<4, T, Q> norm00 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100))); + g0000 *= norm00.x; + g0100 *= norm00.y; + g1000 *= norm00.z; + g1100 *= norm00.w; + + vec<4, T, Q> norm01 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101))); + g0001 *= norm01.x; + g0101 *= norm01.y; + g1001 *= norm01.z; + g1101 *= norm01.w; + + vec<4, T, Q> norm10 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110))); + g0010 *= norm10.x; + g0110 *= norm10.y; + g1010 *= norm10.z; + g1110 *= norm10.w; + + vec<4, T, Q> norm11 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111))); + g0011 *= norm11.x; + g0111 *= norm11.y; + g1011 *= norm11.z; + g1111 *= norm11.w; + + T n0000 = dot(g0000, Pf0); + T n1000 = dot(g1000, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf0.w)); + T n0100 = dot(g0100, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf0.w)); + T n1100 = dot(g1100, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf0.w)); + T n0010 = dot(g0010, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf0.w)); + T n1010 = dot(g1010, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf0.w)); + T n0110 = dot(g0110, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf0.w)); + T n1110 = dot(g1110, vec<4, T, Q>(Pf1.x, Pf1.y, Pf1.z, Pf0.w)); + T n0001 = dot(g0001, vec<4, T, Q>(Pf0.x, Pf0.y, Pf0.z, Pf1.w)); + T n1001 = dot(g1001, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf1.w)); + T n0101 = dot(g0101, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf1.w)); + T n1101 = dot(g1101, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf1.w)); + T n0011 = dot(g0011, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf1.w)); + T n1011 = dot(g1011, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf1.w)); + T n0111 = dot(g0111, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf1.w)); + T n1111 = dot(g1111, Pf1); + + vec<4, T, Q> fade_xyzw = detail::fade(Pf0); + vec<4, T, Q> n_0w = mix(vec<4, T, Q>(n0000, n1000, n0100, n1100), vec<4, T, Q>(n0001, n1001, n0101, n1101), fade_xyzw.w); + vec<4, T, Q> n_1w = mix(vec<4, T, Q>(n0010, n1010, n0110, n1110), vec<4, T, Q>(n0011, n1011, n0111, n1111), fade_xyzw.w); + vec<4, T, Q> n_zw = mix(n_0w, n_1w, fade_xyzw.z); + vec<2, T, Q> n_yzw = mix(vec<2, T, Q>(n_zw.x, n_zw.y), vec<2, T, Q>(n_zw.z, n_zw.w), fade_xyzw.y); + T n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x); + return T(2.2) * n_xyzw; + } + + // Classic Perlin noise, periodic variant + template + GLM_FUNC_QUALIFIER T perlin(vec<2, T, Q> const& Position, vec<2, T, Q> const& rep) + { + vec<4, T, Q> Pi = floor(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) + vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + vec<4, T, Q> Pf = fract(vec<4, T, Q>(Position.x, Position.y, Position.x, Position.y)) - vec<4, T, Q>(0.0, 0.0, 1.0, 1.0); + Pi = mod(Pi, vec<4, T, Q>(rep.x, rep.y, rep.x, rep.y)); // To create noise with explicit period + Pi = mod(Pi, vec<4, T, Q>(289)); // To avoid truncation effects in permutation + vec<4, T, Q> ix(Pi.x, Pi.z, Pi.x, Pi.z); + vec<4, T, Q> iy(Pi.y, Pi.y, Pi.w, Pi.w); + vec<4, T, Q> fx(Pf.x, Pf.z, Pf.x, Pf.z); + vec<4, T, Q> fy(Pf.y, Pf.y, Pf.w, Pf.w); + + vec<4, T, Q> i = detail::permute(detail::permute(ix) + iy); + + vec<4, T, Q> gx = static_cast(2) * fract(i / T(41)) - T(1); + vec<4, T, Q> gy = abs(gx) - T(0.5); + vec<4, T, Q> tx = floor(gx + T(0.5)); + gx = gx - tx; + + vec<2, T, Q> g00(gx.x, gy.x); + vec<2, T, Q> g10(gx.y, gy.y); + vec<2, T, Q> g01(gx.z, gy.z); + vec<2, T, Q> g11(gx.w, gy.w); + + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11))); + g00 *= norm.x; + g01 *= norm.y; + g10 *= norm.z; + g11 *= norm.w; + + T n00 = dot(g00, vec<2, T, Q>(fx.x, fy.x)); + T n10 = dot(g10, vec<2, T, Q>(fx.y, fy.y)); + T n01 = dot(g01, vec<2, T, Q>(fx.z, fy.z)); + T n11 = dot(g11, vec<2, T, Q>(fx.w, fy.w)); + + vec<2, T, Q> fade_xy = detail::fade(vec<2, T, Q>(Pf.x, Pf.y)); + vec<2, T, Q> n_x = mix(vec<2, T, Q>(n00, n01), vec<2, T, Q>(n10, n11), fade_xy.x); + T n_xy = mix(n_x.x, n_x.y, fade_xy.y); + return T(2.3) * n_xy; + } + + // Classic Perlin noise, periodic variant + template + GLM_FUNC_QUALIFIER T perlin(vec<3, T, Q> const& Position, vec<3, T, Q> const& rep) + { + vec<3, T, Q> Pi0 = mod(floor(Position), rep); // Integer part, modulo period + vec<3, T, Q> Pi1 = mod(Pi0 + vec<3, T, Q>(T(1)), rep); // Integer part + 1, mod period + Pi0 = mod(Pi0, vec<3, T, Q>(289)); + Pi1 = mod(Pi1, vec<3, T, Q>(289)); + vec<3, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<3, T, Q> Pf1 = Pf0 - vec<3, T, Q>(T(1)); // Fractional part - 1.0 + vec<4, T, Q> ix = vec<4, T, Q>(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + + vec<4, T, Q> gx0 = ixy0 / T(7); + vec<4, T, Q> gy0 = fract(floor(gx0) / T(7)) - T(0.5); + gx0 = fract(gx0); + vec<4, T, Q> gz0 = vec<4, T, Q>(0.5) - abs(gx0) - abs(gy0); + vec<4, T, Q> sz0 = step(gz0, vec<4, T, Q>(0)); + gx0 -= sz0 * (step(T(0), gx0) - T(0.5)); + gy0 -= sz0 * (step(T(0), gy0) - T(0.5)); + + vec<4, T, Q> gx1 = ixy1 / T(7); + vec<4, T, Q> gy1 = fract(floor(gx1) / T(7)) - T(0.5); + gx1 = fract(gx1); + vec<4, T, Q> gz1 = vec<4, T, Q>(0.5) - abs(gx1) - abs(gy1); + vec<4, T, Q> sz1 = step(gz1, vec<4, T, Q>(T(0))); + gx1 -= sz1 * (step(T(0), gx1) - T(0.5)); + gy1 -= sz1 * (step(T(0), gy1) - T(0.5)); + + vec<3, T, Q> g000 = vec<3, T, Q>(gx0.x, gy0.x, gz0.x); + vec<3, T, Q> g100 = vec<3, T, Q>(gx0.y, gy0.y, gz0.y); + vec<3, T, Q> g010 = vec<3, T, Q>(gx0.z, gy0.z, gz0.z); + vec<3, T, Q> g110 = vec<3, T, Q>(gx0.w, gy0.w, gz0.w); + vec<3, T, Q> g001 = vec<3, T, Q>(gx1.x, gy1.x, gz1.x); + vec<3, T, Q> g101 = vec<3, T, Q>(gx1.y, gy1.y, gz1.y); + vec<3, T, Q> g011 = vec<3, T, Q>(gx1.z, gy1.z, gz1.z); + vec<3, T, Q> g111 = vec<3, T, Q>(gx1.w, gy1.w, gz1.w); + + vec<4, T, Q> norm0 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); + g000 *= norm0.x; + g010 *= norm0.y; + g100 *= norm0.z; + g110 *= norm0.w; + vec<4, T, Q> norm1 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); + g001 *= norm1.x; + g011 *= norm1.y; + g101 *= norm1.z; + g111 *= norm1.w; + + T n000 = dot(g000, Pf0); + T n100 = dot(g100, vec<3, T, Q>(Pf1.x, Pf0.y, Pf0.z)); + T n010 = dot(g010, vec<3, T, Q>(Pf0.x, Pf1.y, Pf0.z)); + T n110 = dot(g110, vec<3, T, Q>(Pf1.x, Pf1.y, Pf0.z)); + T n001 = dot(g001, vec<3, T, Q>(Pf0.x, Pf0.y, Pf1.z)); + T n101 = dot(g101, vec<3, T, Q>(Pf1.x, Pf0.y, Pf1.z)); + T n011 = dot(g011, vec<3, T, Q>(Pf0.x, Pf1.y, Pf1.z)); + T n111 = dot(g111, Pf1); + + vec<3, T, Q> fade_xyz = detail::fade(Pf0); + vec<4, T, Q> n_z = mix(vec<4, T, Q>(n000, n100, n010, n110), vec<4, T, Q>(n001, n101, n011, n111), fade_xyz.z); + vec<2, T, Q> n_yz = mix(vec<2, T, Q>(n_z.x, n_z.y), vec<2, T, Q>(n_z.z, n_z.w), fade_xyz.y); + T n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); + return T(2.2) * n_xyz; + } + + // Classic Perlin noise, periodic version + template + GLM_FUNC_QUALIFIER T perlin(vec<4, T, Q> const& Position, vec<4, T, Q> const& rep) + { + vec<4, T, Q> Pi0 = mod(floor(Position), rep); // Integer part modulo rep + vec<4, T, Q> Pi1 = mod(Pi0 + T(1), rep); // Integer part + 1 mod rep + vec<4, T, Q> Pf0 = fract(Position); // Fractional part for interpolation + vec<4, T, Q> Pf1 = Pf0 - T(1); // Fractional part - 1.0 + vec<4, T, Q> ix = vec<4, T, Q>(Pi0.x, Pi1.x, Pi0.x, Pi1.x); + vec<4, T, Q> iy = vec<4, T, Q>(Pi0.y, Pi0.y, Pi1.y, Pi1.y); + vec<4, T, Q> iz0(Pi0.z); + vec<4, T, Q> iz1(Pi1.z); + vec<4, T, Q> iw0(Pi0.w); + vec<4, T, Q> iw1(Pi1.w); + + vec<4, T, Q> ixy = detail::permute(detail::permute(ix) + iy); + vec<4, T, Q> ixy0 = detail::permute(ixy + iz0); + vec<4, T, Q> ixy1 = detail::permute(ixy + iz1); + vec<4, T, Q> ixy00 = detail::permute(ixy0 + iw0); + vec<4, T, Q> ixy01 = detail::permute(ixy0 + iw1); + vec<4, T, Q> ixy10 = detail::permute(ixy1 + iw0); + vec<4, T, Q> ixy11 = detail::permute(ixy1 + iw1); + + vec<4, T, Q> gx00 = ixy00 / T(7); + vec<4, T, Q> gy00 = floor(gx00) / T(7); + vec<4, T, Q> gz00 = floor(gy00) / T(6); + gx00 = fract(gx00) - T(0.5); + gy00 = fract(gy00) - T(0.5); + gz00 = fract(gz00) - T(0.5); + vec<4, T, Q> gw00 = vec<4, T, Q>(0.75) - abs(gx00) - abs(gy00) - abs(gz00); + vec<4, T, Q> sw00 = step(gw00, vec<4, T, Q>(0)); + gx00 -= sw00 * (step(T(0), gx00) - T(0.5)); + gy00 -= sw00 * (step(T(0), gy00) - T(0.5)); + + vec<4, T, Q> gx01 = ixy01 / T(7); + vec<4, T, Q> gy01 = floor(gx01) / T(7); + vec<4, T, Q> gz01 = floor(gy01) / T(6); + gx01 = fract(gx01) - T(0.5); + gy01 = fract(gy01) - T(0.5); + gz01 = fract(gz01) - T(0.5); + vec<4, T, Q> gw01 = vec<4, T, Q>(0.75) - abs(gx01) - abs(gy01) - abs(gz01); + vec<4, T, Q> sw01 = step(gw01, vec<4, T, Q>(0.0)); + gx01 -= sw01 * (step(T(0), gx01) - T(0.5)); + gy01 -= sw01 * (step(T(0), gy01) - T(0.5)); + + vec<4, T, Q> gx10 = ixy10 / T(7); + vec<4, T, Q> gy10 = floor(gx10) / T(7); + vec<4, T, Q> gz10 = floor(gy10) / T(6); + gx10 = fract(gx10) - T(0.5); + gy10 = fract(gy10) - T(0.5); + gz10 = fract(gz10) - T(0.5); + vec<4, T, Q> gw10 = vec<4, T, Q>(0.75) - abs(gx10) - abs(gy10) - abs(gz10); + vec<4, T, Q> sw10 = step(gw10, vec<4, T, Q>(0.0)); + gx10 -= sw10 * (step(T(0), gx10) - T(0.5)); + gy10 -= sw10 * (step(T(0), gy10) - T(0.5)); + + vec<4, T, Q> gx11 = ixy11 / T(7); + vec<4, T, Q> gy11 = floor(gx11) / T(7); + vec<4, T, Q> gz11 = floor(gy11) / T(6); + gx11 = fract(gx11) - T(0.5); + gy11 = fract(gy11) - T(0.5); + gz11 = fract(gz11) - T(0.5); + vec<4, T, Q> gw11 = vec<4, T, Q>(0.75) - abs(gx11) - abs(gy11) - abs(gz11); + vec<4, T, Q> sw11 = step(gw11, vec<4, T, Q>(T(0))); + gx11 -= sw11 * (step(T(0), gx11) - T(0.5)); + gy11 -= sw11 * (step(T(0), gy11) - T(0.5)); + + vec<4, T, Q> g0000(gx00.x, gy00.x, gz00.x, gw00.x); + vec<4, T, Q> g1000(gx00.y, gy00.y, gz00.y, gw00.y); + vec<4, T, Q> g0100(gx00.z, gy00.z, gz00.z, gw00.z); + vec<4, T, Q> g1100(gx00.w, gy00.w, gz00.w, gw00.w); + vec<4, T, Q> g0010(gx10.x, gy10.x, gz10.x, gw10.x); + vec<4, T, Q> g1010(gx10.y, gy10.y, gz10.y, gw10.y); + vec<4, T, Q> g0110(gx10.z, gy10.z, gz10.z, gw10.z); + vec<4, T, Q> g1110(gx10.w, gy10.w, gz10.w, gw10.w); + vec<4, T, Q> g0001(gx01.x, gy01.x, gz01.x, gw01.x); + vec<4, T, Q> g1001(gx01.y, gy01.y, gz01.y, gw01.y); + vec<4, T, Q> g0101(gx01.z, gy01.z, gz01.z, gw01.z); + vec<4, T, Q> g1101(gx01.w, gy01.w, gz01.w, gw01.w); + vec<4, T, Q> g0011(gx11.x, gy11.x, gz11.x, gw11.x); + vec<4, T, Q> g1011(gx11.y, gy11.y, gz11.y, gw11.y); + vec<4, T, Q> g0111(gx11.z, gy11.z, gz11.z, gw11.z); + vec<4, T, Q> g1111(gx11.w, gy11.w, gz11.w, gw11.w); + + vec<4, T, Q> norm00 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100))); + g0000 *= norm00.x; + g0100 *= norm00.y; + g1000 *= norm00.z; + g1100 *= norm00.w; + + vec<4, T, Q> norm01 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101))); + g0001 *= norm01.x; + g0101 *= norm01.y; + g1001 *= norm01.z; + g1101 *= norm01.w; + + vec<4, T, Q> norm10 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110))); + g0010 *= norm10.x; + g0110 *= norm10.y; + g1010 *= norm10.z; + g1110 *= norm10.w; + + vec<4, T, Q> norm11 = detail::taylorInvSqrt(vec<4, T, Q>(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111))); + g0011 *= norm11.x; + g0111 *= norm11.y; + g1011 *= norm11.z; + g1111 *= norm11.w; + + T n0000 = dot(g0000, Pf0); + T n1000 = dot(g1000, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf0.w)); + T n0100 = dot(g0100, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf0.w)); + T n1100 = dot(g1100, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf0.w)); + T n0010 = dot(g0010, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf0.w)); + T n1010 = dot(g1010, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf0.w)); + T n0110 = dot(g0110, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf0.w)); + T n1110 = dot(g1110, vec<4, T, Q>(Pf1.x, Pf1.y, Pf1.z, Pf0.w)); + T n0001 = dot(g0001, vec<4, T, Q>(Pf0.x, Pf0.y, Pf0.z, Pf1.w)); + T n1001 = dot(g1001, vec<4, T, Q>(Pf1.x, Pf0.y, Pf0.z, Pf1.w)); + T n0101 = dot(g0101, vec<4, T, Q>(Pf0.x, Pf1.y, Pf0.z, Pf1.w)); + T n1101 = dot(g1101, vec<4, T, Q>(Pf1.x, Pf1.y, Pf0.z, Pf1.w)); + T n0011 = dot(g0011, vec<4, T, Q>(Pf0.x, Pf0.y, Pf1.z, Pf1.w)); + T n1011 = dot(g1011, vec<4, T, Q>(Pf1.x, Pf0.y, Pf1.z, Pf1.w)); + T n0111 = dot(g0111, vec<4, T, Q>(Pf0.x, Pf1.y, Pf1.z, Pf1.w)); + T n1111 = dot(g1111, Pf1); + + vec<4, T, Q> fade_xyzw = detail::fade(Pf0); + vec<4, T, Q> n_0w = mix(vec<4, T, Q>(n0000, n1000, n0100, n1100), vec<4, T, Q>(n0001, n1001, n0101, n1101), fade_xyzw.w); + vec<4, T, Q> n_1w = mix(vec<4, T, Q>(n0010, n1010, n0110, n1110), vec<4, T, Q>(n0011, n1011, n0111, n1111), fade_xyzw.w); + vec<4, T, Q> n_zw = mix(n_0w, n_1w, fade_xyzw.z); + vec<2, T, Q> n_yzw = mix(vec<2, T, Q>(n_zw.x, n_zw.y), vec<2, T, Q>(n_zw.z, n_zw.w), fade_xyzw.y); + T n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x); + return T(2.2) * n_xyzw; + } + + template + GLM_FUNC_QUALIFIER T simplex(glm::vec<2, T, Q> const& v) + { + vec<4, T, Q> const C = vec<4, T, Q>( + T( 0.211324865405187), // (3.0 - sqrt(3.0)) / 6.0 + T( 0.366025403784439), // 0.5 * (sqrt(3.0) - 1.0) + T(-0.577350269189626), // -1.0 + 2.0 * C.x + T( 0.024390243902439)); // 1.0 / 41.0 + + // First corner + vec<2, T, Q> i = floor(v + dot(v, vec<2, T, Q>(C[1]))); + vec<2, T, Q> x0 = v - i + dot(i, vec<2, T, Q>(C[0])); + + // Other corners + //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0 + //i1.y = 1.0 - i1.x; + vec<2, T, Q> i1 = (x0.x > x0.y) ? vec<2, T, Q>(1, 0) : vec<2, T, Q>(0, 1); + // x0 = x0 - 0.0 + 0.0 * C.xx ; + // x1 = x0 - i1 + 1.0 * C.xx ; + // x2 = x0 - 1.0 + 2.0 * C.xx ; + vec<4, T, Q> x12 = vec<4, T, Q>(x0.x, x0.y, x0.x, x0.y) + vec<4, T, Q>(C.x, C.x, C.z, C.z); + x12 = vec<4, T, Q>(vec<2, T, Q>(x12) - i1, x12.z, x12.w); + + // Permutations + i = mod(i, vec<2, T, Q>(289)); // Avoid truncation effects in permutation + vec<3, T, Q> p = detail::permute( + detail::permute(i.y + vec<3, T, Q>(T(0), i1.y, T(1))) + + i.x + vec<3, T, Q>(T(0), i1.x, T(1))); + + vec<3, T, Q> m = max(vec<3, T, Q>(0.5) - vec<3, T, Q>( + dot(x0, x0), + dot(vec<2, T, Q>(x12.x, x12.y), vec<2, T, Q>(x12.x, x12.y)), + dot(vec<2, T, Q>(x12.z, x12.w), vec<2, T, Q>(x12.z, x12.w))), vec<3, T, Q>(0)); + m = m * m ; + m = m * m ; + + // Gradients: 41 points uniformly over a line, mapped onto a diamond. + // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) + + vec<3, T, Q> x = static_cast(2) * fract(p * C.w) - T(1); + vec<3, T, Q> h = abs(x) - T(0.5); + vec<3, T, Q> ox = floor(x + T(0.5)); + vec<3, T, Q> a0 = x - ox; + + // Normalise gradients implicitly by scaling m + // Inlined for speed: m *= taylorInvSqrt( a0*a0 + h*h ); + m *= static_cast(1.79284291400159) - T(0.85373472095314) * (a0 * a0 + h * h); + + // Compute final noise value at P + vec<3, T, Q> g; + g.x = a0.x * x0.x + h.x * x0.y; + //g.yz = a0.yz * x12.xz + h.yz * x12.yw; + g.y = a0.y * x12.x + h.y * x12.y; + g.z = a0.z * x12.z + h.z * x12.w; + return T(130) * dot(m, g); + } + + template + GLM_FUNC_QUALIFIER T simplex(vec<3, T, Q> const& v) + { + vec<2, T, Q> const C(1.0 / 6.0, 1.0 / 3.0); + vec<4, T, Q> const D(0.0, 0.5, 1.0, 2.0); + + // First corner + vec<3, T, Q> i(floor(v + dot(v, vec<3, T, Q>(C.y)))); + vec<3, T, Q> x0(v - i + dot(i, vec<3, T, Q>(C.x))); + + // Other corners + vec<3, T, Q> g(step(vec<3, T, Q>(x0.y, x0.z, x0.x), x0)); + vec<3, T, Q> l(T(1) - g); + vec<3, T, Q> i1(min(g, vec<3, T, Q>(l.z, l.x, l.y))); + vec<3, T, Q> i2(max(g, vec<3, T, Q>(l.z, l.x, l.y))); + + // x0 = x0 - 0.0 + 0.0 * C.xxx; + // x1 = x0 - i1 + 1.0 * C.xxx; + // x2 = x0 - i2 + 2.0 * C.xxx; + // x3 = x0 - 1.0 + 3.0 * C.xxx; + vec<3, T, Q> x1(x0 - i1 + C.x); + vec<3, T, Q> x2(x0 - i2 + C.y); // 2.0*C.x = 1/3 = C.y + vec<3, T, Q> x3(x0 - D.y); // -1.0+3.0*C.x = -0.5 = -D.y + + // Permutations + i = detail::mod289(i); + vec<4, T, Q> p(detail::permute(detail::permute(detail::permute( + i.z + vec<4, T, Q>(T(0), i1.z, i2.z, T(1))) + + i.y + vec<4, T, Q>(T(0), i1.y, i2.y, T(1))) + + i.x + vec<4, T, Q>(T(0), i1.x, i2.x, T(1)))); + + // Gradients: 7x7 points over a square, mapped onto an octahedron. + // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) + T n_ = static_cast(0.142857142857); // 1.0/7.0 + vec<3, T, Q> ns(n_ * vec<3, T, Q>(D.w, D.y, D.z) - vec<3, T, Q>(D.x, D.z, D.x)); + + vec<4, T, Q> j(p - T(49) * floor(p * ns.z * ns.z)); // mod(p,7*7) + + vec<4, T, Q> x_(floor(j * ns.z)); + vec<4, T, Q> y_(floor(j - T(7) * x_)); // mod(j,N) + + vec<4, T, Q> x(x_ * ns.x + ns.y); + vec<4, T, Q> y(y_ * ns.x + ns.y); + vec<4, T, Q> h(T(1) - abs(x) - abs(y)); + + vec<4, T, Q> b0(x.x, x.y, y.x, y.y); + vec<4, T, Q> b1(x.z, x.w, y.z, y.w); + + // vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; + // vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; + vec<4, T, Q> s0(floor(b0) * T(2) + T(1)); + vec<4, T, Q> s1(floor(b1) * T(2) + T(1)); + vec<4, T, Q> sh(-step(h, vec<4, T, Q>(0.0))); + + vec<4, T, Q> a0 = vec<4, T, Q>(b0.x, b0.z, b0.y, b0.w) + vec<4, T, Q>(s0.x, s0.z, s0.y, s0.w) * vec<4, T, Q>(sh.x, sh.x, sh.y, sh.y); + vec<4, T, Q> a1 = vec<4, T, Q>(b1.x, b1.z, b1.y, b1.w) + vec<4, T, Q>(s1.x, s1.z, s1.y, s1.w) * vec<4, T, Q>(sh.z, sh.z, sh.w, sh.w); + + vec<3, T, Q> p0(a0.x, a0.y, h.x); + vec<3, T, Q> p1(a0.z, a0.w, h.y); + vec<3, T, Q> p2(a1.x, a1.y, h.z); + vec<3, T, Q> p3(a1.z, a1.w, h.w); + + // Normalise gradients + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + + // Mix final noise value + vec<4, T, Q> m = max(T(0.6) - vec<4, T, Q>(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), vec<4, T, Q>(0)); + m = m * m; + return T(42) * dot(m * m, vec<4, T, Q>(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); + } + + template + GLM_FUNC_QUALIFIER T simplex(vec<4, T, Q> const& v) + { + vec<4, T, Q> const C( + 0.138196601125011, // (5 - sqrt(5))/20 G4 + 0.276393202250021, // 2 * G4 + 0.414589803375032, // 3 * G4 + -0.447213595499958); // -1 + 4 * G4 + + // (sqrt(5) - 1)/4 = F4, used once below + T const F4 = static_cast(0.309016994374947451); + + // First corner + vec<4, T, Q> i = floor(v + dot(v, vec<4, T, Q>(F4))); + vec<4, T, Q> x0 = v - i + dot(i, vec<4, T, Q>(C.x)); + + // Other corners + + // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) + vec<4, T, Q> i0; + vec<3, T, Q> isX = step(vec<3, T, Q>(x0.y, x0.z, x0.w), vec<3, T, Q>(x0.x)); + vec<3, T, Q> isYZ = step(vec<3, T, Q>(x0.z, x0.w, x0.w), vec<3, T, Q>(x0.y, x0.y, x0.z)); + // i0.x = dot(isX, vec3(1.0)); + //i0.x = isX.x + isX.y + isX.z; + //i0.yzw = static_cast(1) - isX; + i0 = vec<4, T, Q>(isX.x + isX.y + isX.z, T(1) - isX); + // i0.y += dot(isYZ.xy, vec2(1.0)); + i0.y += isYZ.x + isYZ.y; + //i0.zw += 1.0 - vec<2, T, Q>(isYZ.x, isYZ.y); + i0.z += static_cast(1) - isYZ.x; + i0.w += static_cast(1) - isYZ.y; + i0.z += isYZ.z; + i0.w += static_cast(1) - isYZ.z; + + // i0 now contains the unique values 0,1,2,3 in each channel + vec<4, T, Q> i3 = clamp(i0, T(0), T(1)); + vec<4, T, Q> i2 = clamp(i0 - T(1), T(0), T(1)); + vec<4, T, Q> i1 = clamp(i0 - T(2), T(0), T(1)); + + // x0 = x0 - 0.0 + 0.0 * C.xxxx + // x1 = x0 - i1 + 0.0 * C.xxxx + // x2 = x0 - i2 + 0.0 * C.xxxx + // x3 = x0 - i3 + 0.0 * C.xxxx + // x4 = x0 - 1.0 + 4.0 * C.xxxx + vec<4, T, Q> x1 = x0 - i1 + C.x; + vec<4, T, Q> x2 = x0 - i2 + C.y; + vec<4, T, Q> x3 = x0 - i3 + C.z; + vec<4, T, Q> x4 = x0 + C.w; + + // Permutations + i = mod(i, vec<4, T, Q>(289)); + T j0 = detail::permute(detail::permute(detail::permute(detail::permute(i.w) + i.z) + i.y) + i.x); + vec<4, T, Q> j1 = detail::permute(detail::permute(detail::permute(detail::permute( + i.w + vec<4, T, Q>(i1.w, i2.w, i3.w, T(1))) + + i.z + vec<4, T, Q>(i1.z, i2.z, i3.z, T(1))) + + i.y + vec<4, T, Q>(i1.y, i2.y, i3.y, T(1))) + + i.x + vec<4, T, Q>(i1.x, i2.x, i3.x, T(1))); + + // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope + // 7*7*6 = 294, which is close to the ring size 17*17 = 289. + vec<4, T, Q> ip = vec<4, T, Q>(T(1) / T(294), T(1) / T(49), T(1) / T(7), T(0)); + + vec<4, T, Q> p0 = detail::grad4(j0, ip); + vec<4, T, Q> p1 = detail::grad4(j1.x, ip); + vec<4, T, Q> p2 = detail::grad4(j1.y, ip); + vec<4, T, Q> p3 = detail::grad4(j1.z, ip); + vec<4, T, Q> p4 = detail::grad4(j1.w, ip); + + // Normalise gradients + vec<4, T, Q> norm = detail::taylorInvSqrt(vec<4, T, Q>(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + p4 *= detail::taylorInvSqrt(dot(p4, p4)); + + // Mix contributions from the five corners + vec<3, T, Q> m0 = max(T(0.6) - vec<3, T, Q>(dot(x0, x0), dot(x1, x1), dot(x2, x2)), vec<3, T, Q>(0)); + vec<2, T, Q> m1 = max(T(0.6) - vec<2, T, Q>(dot(x3, x3), dot(x4, x4) ), vec<2, T, Q>(0)); + m0 = m0 * m0; + m1 = m1 * m1; + return T(49) * + (dot(m0 * m0, vec<3, T, Q>(dot(p0, x0), dot(p1, x1), dot(p2, x2))) + + dot(m1 * m1, vec<2, T, Q>(dot(p3, x3), dot(p4, x4)))); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/packing.hpp b/thirdparty/glm/glm/gtc/packing.hpp new file mode 100644 index 0000000..8e416b3 --- /dev/null +++ b/thirdparty/glm/glm/gtc/packing.hpp @@ -0,0 +1,728 @@ +/// @ref gtc_packing +/// @file glm/gtc/packing.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_packing GLM_GTC_packing +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// This extension provides a set of function to convert vertors to packed +/// formats. + +#pragma once + +// Dependency: +#include "type_precision.hpp" +#include "../ext/vector_packing.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_packing extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_packing + /// @{ + + /// First, converts the normalized floating-point value v into a 8-bit integer value. + /// Then, the results are packed into the returned 8-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm1x8: round(clamp(c, 0, +1) * 255.0) + /// + /// @see gtc_packing + /// @see uint16 packUnorm2x8(vec2 const& v) + /// @see uint32 packUnorm4x8(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint8 packUnorm1x8(float v); + + /// Convert a single 8-bit integer to a normalized floating-point value. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x8(uint16 p) + /// @see vec4 unpackUnorm4x8(uint32 p) + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackUnorm1x8(uint8 p); + + /// First, converts each component of the normalized floating-point value v into 8-bit integer values. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm2x8: round(clamp(c, 0, +1) * 255.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint8 packUnorm1x8(float const& v) + /// @see uint32 packUnorm4x8(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packUnorm2x8(vec2 const& v); + + /// First, unpacks a single 16-bit unsigned integer p into a pair of 8-bit unsigned integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackUnorm1x8(uint8 v) + /// @see vec4 unpackUnorm4x8(uint32 p) + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackUnorm2x8(uint16 p); + + /// First, converts the normalized floating-point value v into 8-bit integer value. + /// Then, the results are packed into the returned 8-bit unsigned integer. + /// + /// The conversion to fixed point is done as follows: + /// packSnorm1x8: round(clamp(s, -1, +1) * 127.0) + /// + /// @see gtc_packing + /// @see uint16 packSnorm2x8(vec2 const& v) + /// @see uint32 packSnorm4x8(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint8 packSnorm1x8(float s); + + /// First, unpacks a single 8-bit unsigned integer p into a single 8-bit signed integers. + /// Then, the value is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm1x8: clamp(f / 127.0, -1, +1) + /// + /// @see gtc_packing + /// @see vec2 unpackSnorm2x8(uint16 p) + /// @see vec4 unpackSnorm4x8(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackSnorm1x8(uint8 p); + + /// First, converts each component of the normalized floating-point value v into 8-bit integer values. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x8: round(clamp(c, -1, +1) * 127.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint8 packSnorm1x8(float const& v) + /// @see uint32 packSnorm4x8(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packSnorm2x8(vec2 const& v); + + /// First, unpacks a single 16-bit unsigned integer p into a pair of 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm2x8: clamp(f / 127.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackSnorm1x8(uint8 p) + /// @see vec4 unpackSnorm4x8(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackSnorm2x8(uint16 p); + + /// First, converts the normalized floating-point value v into a 16-bit integer value. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm1x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// @see gtc_packing + /// @see uint16 packSnorm1x16(float const& v) + /// @see uint64 packSnorm4x16(vec4 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packUnorm1x16(float v); + + /// First, unpacks a single 16-bit unsigned integer p into a of 16-bit unsigned integers. + /// Then, the value is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm1x16: f / 65535.0 + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x16(uint32 p) + /// @see vec4 unpackUnorm4x16(uint64 p) + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackUnorm1x16(uint16 p); + + /// First, converts each component of the normalized floating-point value v into 16-bit integer values. + /// Then, the results are packed into the returned 64-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm4x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint16 packUnorm1x16(float const& v) + /// @see uint32 packUnorm2x16(vec2 const& v) + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packUnorm4x16(vec4 const& v); + + /// First, unpacks a single 64-bit unsigned integer p into four 16-bit unsigned integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnormx4x16: f / 65535.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackUnorm1x16(uint16 p) + /// @see vec2 unpackUnorm2x16(uint32 p) + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackUnorm4x16(uint64 p); + + /// First, converts the normalized floating-point value v into 16-bit integer value. + /// Then, the results are packed into the returned 16-bit unsigned integer. + /// + /// The conversion to fixed point is done as follows: + /// packSnorm1x8: round(clamp(s, -1, +1) * 32767.0) + /// + /// @see gtc_packing + /// @see uint32 packSnorm2x16(vec2 const& v) + /// @see uint64 packSnorm4x16(vec4 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packSnorm1x16(float v); + + /// First, unpacks a single 16-bit unsigned integer p into a single 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned scalar. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm1x16: clamp(f / 32767.0, -1, +1) + /// + /// @see gtc_packing + /// @see vec2 unpackSnorm2x16(uint32 p) + /// @see vec4 unpackSnorm4x16(uint64 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackSnorm1x16(uint16 p); + + /// First, converts each component of the normalized floating-point value v into 16-bit integer values. + /// Then, the results are packed into the returned 64-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x8: round(clamp(c, -1, +1) * 32767.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see gtc_packing + /// @see uint16 packSnorm1x16(float const& v) + /// @see uint32 packSnorm2x16(vec2 const& v) + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packSnorm4x16(vec4 const& v); + + /// First, unpacks a single 64-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm4x16: clamp(f / 32767.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see float unpackSnorm1x16(uint16 p) + /// @see vec2 unpackSnorm2x16(uint32 p) + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackSnorm4x16(uint64 p); + + /// Returns an unsigned integer obtained by converting the components of a floating-point scalar + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing this 16-bit value into a 16-bit unsigned integer. + /// + /// @see gtc_packing + /// @see uint32 packHalf2x16(vec2 const& v) + /// @see uint64 packHalf4x16(vec4 const& v) + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint16 packHalf1x16(float v); + + /// Returns a floating-point scalar with components obtained by unpacking a 16-bit unsigned integer into a 16-bit value, + /// interpreted as a 16-bit floating-point number according to the OpenGL Specification, + /// and converting it to 32-bit floating-point values. + /// + /// @see gtc_packing + /// @see vec2 unpackHalf2x16(uint32 const& v) + /// @see vec4 unpackHalf4x16(uint64 const& v) + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL float unpackHalf1x16(uint16 v); + + /// Returns an unsigned integer obtained by converting the components of a four-component floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing these four 16-bit values into a 64-bit unsigned integer. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see uint16 packHalf1x16(float const& v) + /// @see uint32 packHalf2x16(vec2 const& v) + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint64 packHalf4x16(vec4 const& v); + + /// Returns a four-component floating-point vector with components obtained by unpacking a 64-bit unsigned integer into four 16-bit values, + /// interpreting those values as 16-bit floating-point numbers according to the OpenGL Specification, + /// and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see float unpackHalf1x16(uint16 const& v) + /// @see vec2 unpackHalf2x16(uint32 const& v) + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackHalf4x16(uint64 p); + + /// Returns an unsigned integer obtained by converting the components of a four-component signed integer vector + /// to the 10-10-10-2-bit signed integer representation found in the OpenGL Specification, + /// and then packing these four values into a 32-bit unsigned integer. + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see uint32 packI3x10_1x2(uvec4 const& v) + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see ivec4 unpackI3x10_1x2(uint32 const& p) + GLM_FUNC_DECL uint32 packI3x10_1x2(ivec4 const& v); + + /// Unpacks a single 32-bit unsigned integer p into three 10-bit and one 2-bit signed integers. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p); + /// @see uvec4 unpackI3x10_1x2(uint32 const& p); + GLM_FUNC_DECL ivec4 unpackI3x10_1x2(uint32 p); + + /// Returns an unsigned integer obtained by converting the components of a four-component unsigned integer vector + /// to the 10-10-10-2-bit unsigned integer representation found in the OpenGL Specification, + /// and then packing these four values into a 32-bit unsigned integer. + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see uint32 packI3x10_1x2(ivec4 const& v) + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see ivec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL uint32 packU3x10_1x2(uvec4 const& v); + + /// Unpacks a single 32-bit unsigned integer p into three 10-bit and one 2-bit unsigned integers. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p); + /// @see uvec4 unpackI3x10_1x2(uint32 const& p); + GLM_FUNC_DECL uvec4 unpackU3x10_1x2(uint32 p); + + /// First, converts the first three components of the normalized floating-point value v into 10-bit signed integer values. + /// Then, converts the forth component of the normalized floating-point value v into 2-bit signed integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm3x10_1x2(xyz): round(clamp(c, -1, +1) * 511.0) + /// packSnorm3x10_1x2(w): round(clamp(c, -1, +1) * 1.0) + /// + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see vec4 unpackSnorm3x10_1x2(uint32 const& p) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see uint32 packI3x10_1x2(ivec4 const& v) + GLM_FUNC_DECL uint32 packSnorm3x10_1x2(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm3x10_1x2(xyz): clamp(f / 511.0, -1, +1) + /// unpackSnorm3x10_1x2(w): clamp(f / 511.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see vec4 unpackUnorm3x10_1x2(uint32 const& p)) + /// @see uvec4 unpackI3x10_1x2(uint32 const& p) + /// @see uvec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL vec4 unpackSnorm3x10_1x2(uint32 p); + + /// First, converts the first three components of the normalized floating-point value v into 10-bit unsigned integer values. + /// Then, converts the forth component of the normalized floating-point value v into 2-bit signed uninteger values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm3x10_1x2(xyz): round(clamp(c, 0, +1) * 1023.0) + /// packUnorm3x10_1x2(w): round(clamp(c, 0, +1) * 3.0) + /// + /// The first vector component specifies the 10 least-significant bits of the result; + /// the forth component specifies the 2 most-significant bits. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm3x10_1x2(uint32 const& p) + /// @see uint32 packUnorm3x10_1x2(vec4 const& v) + /// @see uint32 packU3x10_1x2(uvec4 const& v) + /// @see uint32 packI3x10_1x2(ivec4 const& v) + GLM_FUNC_DECL uint32 packUnorm3x10_1x2(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into four 16-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm3x10_1x2(xyz): clamp(f / 1023.0, 0, +1) + /// unpackSnorm3x10_1x2(w): clamp(f / 3.0, 0, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packSnorm3x10_1x2(vec4 const& v) + /// @see vec4 unpackInorm3x10_1x2(uint32 const& p)) + /// @see uvec4 unpackI3x10_1x2(uint32 const& p) + /// @see uvec4 unpackU3x10_1x2(uint32 const& p) + GLM_FUNC_DECL vec4 unpackUnorm3x10_1x2(uint32 p); + + /// First, converts the first two components of the normalized floating-point value v into 11-bit signless floating-point values. + /// Then, converts the third component of the normalized floating-point value v into a 10-bit signless floating-point value. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The first vector component specifies the 11 least-significant bits of the result; + /// the last component specifies the 10 most-significant bits. + /// + /// @see gtc_packing + /// @see vec3 unpackF2x11_1x10(uint32 const& p) + GLM_FUNC_DECL uint32 packF2x11_1x10(vec3 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into two 11-bit signless floating-point values and one 10-bit signless floating-point value . + /// Then, each component is converted to a normalized floating-point value to generate the returned three-component vector. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see gtc_packing + /// @see uint32 packF2x11_1x10(vec3 const& v) + GLM_FUNC_DECL vec3 unpackF2x11_1x10(uint32 p); + + + /// First, converts the first two components of the normalized floating-point value v into 11-bit signless floating-point values. + /// Then, converts the third component of the normalized floating-point value v into a 10-bit signless floating-point value. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The first vector component specifies the 11 least-significant bits of the result; + /// the last component specifies the 10 most-significant bits. + /// + /// packF3x9_E1x5 allows encoding into RGBE / RGB9E5 format + /// + /// @see gtc_packing + /// @see vec3 unpackF3x9_E1x5(uint32 const& p) + GLM_FUNC_DECL uint32 packF3x9_E1x5(vec3 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into two 11-bit signless floating-point values and one 10-bit signless floating-point value . + /// Then, each component is converted to a normalized floating-point value to generate the returned three-component vector. + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// unpackF3x9_E1x5 allows decoding RGBE / RGB9E5 data + /// + /// @see gtc_packing + /// @see uint32 packF3x9_E1x5(vec3 const& v) + GLM_FUNC_DECL vec3 unpackF3x9_E1x5(uint32 p); + + /// Returns an unsigned integer vector obtained by converting the components of a floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& p) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec<4, T, Q> packRGBM(vec<3, T, Q> const& rgb); + + /// Returns a floating-point vector with components obtained by reinterpreting an integer vector as 16-bit floating-point numbers and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see vec<4, T, Q> packRGBM(vec<3, float, Q> const& v) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& rgbm); + + /// Returns an unsigned integer vector obtained by converting the components of a floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the forth component specifies the 16 most-significant bits. + /// + /// @see gtc_packing + /// @see vec unpackHalf(vec const& p) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec packHalf(vec const& v); + + /// Returns a floating-point vector with components obtained by reinterpreting an integer vector as 16-bit floating-point numbers and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the forth component is obtained from the 16 most-significant bits of v. + /// + /// @see gtc_packing + /// @see vec packHalf(vec const& v) + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + template + GLM_FUNC_DECL vec unpackHalf(vec const& p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec unpackUnorm(vec const& p); + template + GLM_FUNC_DECL vec packUnorm(vec const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see vec packUnorm(vec const& v) + template + GLM_FUNC_DECL vec unpackUnorm(vec const& v); + + /// Convert each component of the normalized floating-point vector into signed integer values. + /// + /// @see gtc_packing + /// @see vec unpackSnorm(vec const& p); + template + GLM_FUNC_DECL vec packSnorm(vec const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see vec packSnorm(vec const& v) + template + GLM_FUNC_DECL vec unpackSnorm(vec const& v); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec2 unpackUnorm2x4(uint8 p) + GLM_FUNC_DECL uint8 packUnorm2x4(vec2 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint8 packUnorm2x4(vec2 const& v) + GLM_FUNC_DECL vec2 unpackUnorm2x4(uint8 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm4x4(uint16 p) + GLM_FUNC_DECL uint16 packUnorm4x4(vec4 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm4x4(vec4 const& v) + GLM_FUNC_DECL vec4 unpackUnorm4x4(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec3 unpackUnorm1x5_1x6_1x5(uint16 p) + GLM_FUNC_DECL uint16 packUnorm1x5_1x6_1x5(vec3 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm1x5_1x6_1x5(vec3 const& v) + GLM_FUNC_DECL vec3 unpackUnorm1x5_1x6_1x5(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec4 unpackUnorm3x5_1x1(uint16 p) + GLM_FUNC_DECL uint16 packUnorm3x5_1x1(vec4 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint16 packUnorm3x5_1x1(vec4 const& v) + GLM_FUNC_DECL vec4 unpackUnorm3x5_1x1(uint16 p); + + /// Convert each component of the normalized floating-point vector into unsigned integer values. + /// + /// @see gtc_packing + /// @see vec3 unpackUnorm2x3_1x2(uint8 p) + GLM_FUNC_DECL uint8 packUnorm2x3_1x2(vec3 const& v); + + /// Convert a packed integer to a normalized floating-point vector. + /// + /// @see gtc_packing + /// @see uint8 packUnorm2x3_1x2(vec3 const& v) + GLM_FUNC_DECL vec3 unpackUnorm2x3_1x2(uint8 p); + + + + /// Convert each component from an integer vector into a packed integer. + /// + /// @see gtc_packing + /// @see i8vec2 unpackInt2x8(int16 p) + GLM_FUNC_DECL int16 packInt2x8(i8vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int16 packInt2x8(i8vec2 const& v) + GLM_FUNC_DECL i8vec2 unpackInt2x8(int16 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u8vec2 unpackInt2x8(uint16 p) + GLM_FUNC_DECL uint16 packUint2x8(u8vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint16 packInt2x8(u8vec2 const& v) + GLM_FUNC_DECL u8vec2 unpackUint2x8(uint16 p); + + /// Convert each component from an integer vector into a packed integer. + /// + /// @see gtc_packing + /// @see i8vec4 unpackInt4x8(int32 p) + GLM_FUNC_DECL int32 packInt4x8(i8vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int32 packInt2x8(i8vec4 const& v) + GLM_FUNC_DECL i8vec4 unpackInt4x8(int32 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u8vec4 unpackUint4x8(uint32 p) + GLM_FUNC_DECL uint32 packUint4x8(u8vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint32 packUint4x8(u8vec2 const& v) + GLM_FUNC_DECL u8vec4 unpackUint4x8(uint32 p); + + /// Convert each component from an integer vector into a packed integer. + /// + /// @see gtc_packing + /// @see i16vec2 unpackInt2x16(int p) + GLM_FUNC_DECL int packInt2x16(i16vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packInt2x16(i16vec2 const& v) + GLM_FUNC_DECL i16vec2 unpackInt2x16(int p); + + /// Convert each component from an integer vector into a packed integer. + /// + /// @see gtc_packing + /// @see i16vec4 unpackInt4x16(int64 p) + GLM_FUNC_DECL int64 packInt4x16(i16vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int64 packInt4x16(i16vec4 const& v) + GLM_FUNC_DECL i16vec4 unpackInt4x16(int64 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u16vec2 unpackUint2x16(uint p) + GLM_FUNC_DECL uint packUint2x16(u16vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint packUint2x16(u16vec2 const& v) + GLM_FUNC_DECL u16vec2 unpackUint2x16(uint p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u16vec4 unpackUint4x16(uint64 p) + GLM_FUNC_DECL uint64 packUint4x16(u16vec4 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see uint64 packUint4x16(u16vec4 const& v) + GLM_FUNC_DECL u16vec4 unpackUint4x16(uint64 p); + + /// Convert each component from an integer vector into a packed integer. + /// + /// @see gtc_packing + /// @see i32vec2 unpackInt2x32(int p) + GLM_FUNC_DECL int64 packInt2x32(i32vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packInt2x16(i32vec2 const& v) + GLM_FUNC_DECL i32vec2 unpackInt2x32(int64 p); + + /// Convert each component from an integer vector into a packed unsigned integer. + /// + /// @see gtc_packing + /// @see u32vec2 unpackUint2x32(int p) + GLM_FUNC_DECL uint64 packUint2x32(u32vec2 const& v); + + /// Convert a packed integer into an integer vector. + /// + /// @see gtc_packing + /// @see int packUint2x16(u32vec2 const& v) + GLM_FUNC_DECL u32vec2 unpackUint2x32(uint64 p); + + /// @} +}// namespace glm + +#include "packing.inl" diff --git a/thirdparty/glm/glm/gtc/packing.inl b/thirdparty/glm/glm/gtc/packing.inl new file mode 100644 index 0000000..1792760 --- /dev/null +++ b/thirdparty/glm/glm/gtc/packing.inl @@ -0,0 +1,951 @@ +/// @ref gtc_packing + +#include "../ext/scalar_relational.hpp" +#include "../ext/vector_relational.hpp" +#include "../common.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../detail/type_half.hpp" +#include +#include + +namespace glm{ +namespace detail +{ + GLM_FUNC_QUALIFIER glm::uint16 float2half(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((f >> 16) & 0x8000) | // sign + ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) | // exponential + ((f >> 13) & 0x03ff); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 float2packed11(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x000007c0 => 00000000 00000000 00000111 11000000 + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((f & 0x7f800000) - 0x38000000) >> 17) & 0x07c0) | // exponential + ((f >> 17) & 0x003f); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 packed11ToFloat(glm::uint32 p) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x000007c0 => 00000000 00000000 00000111 11000000 + // 0x00007c00 => 00000000 00000000 01111100 00000000 + // 0x000003ff => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((p & 0x07c0) << 17) + 0x38000000) & 0x7f800000) | // exponential + ((p & 0x003f) << 17); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 float2packed10(glm::uint32 f) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x0000001F => 00000000 00000000 00000000 00011111 + // 0x0000003F => 00000000 00000000 00000000 00111111 + // 0x000003E0 => 00000000 00000000 00000011 11100000 + // 0x000007C0 => 00000000 00000000 00000111 11000000 + // 0x00007C00 => 00000000 00000000 01111100 00000000 + // 0x000003FF => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((f & 0x7f800000) - 0x38000000) >> 18) & 0x03E0) | // exponential + ((f >> 18) & 0x001f); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint32 packed10ToFloat(glm::uint32 p) + { + // 10 bits => EE EEEFFFFF + // 11 bits => EEE EEFFFFFF + // Half bits => SEEEEEFF FFFFFFFF + // Float bits => SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF + + // 0x0000001F => 00000000 00000000 00000000 00011111 + // 0x0000003F => 00000000 00000000 00000000 00111111 + // 0x000003E0 => 00000000 00000000 00000011 11100000 + // 0x000007C0 => 00000000 00000000 00000111 11000000 + // 0x00007C00 => 00000000 00000000 01111100 00000000 + // 0x000003FF => 00000000 00000000 00000011 11111111 + // 0x38000000 => 00111000 00000000 00000000 00000000 + // 0x7f800000 => 01111111 10000000 00000000 00000000 + // 0x00008000 => 00000000 00000000 10000000 00000000 + return + ((((p & 0x03E0) << 18) + 0x38000000) & 0x7f800000) | // exponential + ((p & 0x001f) << 18); // Mantissa + } + + GLM_FUNC_QUALIFIER glm::uint half2float(glm::uint h) + { + return ((h & 0x8000) << 16) | ((( h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); + } + + GLM_FUNC_QUALIFIER glm::uint floatTo11bit(float x) + { + if(x == 0.0f) + return 0u; + else if(glm::isnan(x)) + return ~0u; + else if(glm::isinf(x)) + return 0x1Fu << 6u; + + uint Pack = 0u; + memcpy(&Pack, &x, sizeof(Pack)); + return float2packed11(Pack); + } + + GLM_FUNC_QUALIFIER float packed11bitToFloat(glm::uint x) + { + if(x == 0) + return 0.0f; + else if(x == ((1 << 11) - 1)) + return ~0;//NaN + else if(x == (0x1f << 6)) + return ~0;//Inf + + uint Result = packed11ToFloat(x); + + float Temp = 0; + memcpy(&Temp, &Result, sizeof(Temp)); + return Temp; + } + + GLM_FUNC_QUALIFIER glm::uint floatTo10bit(float x) + { + if(x == 0.0f) + return 0u; + else if(glm::isnan(x)) + return ~0u; + else if(glm::isinf(x)) + return 0x1Fu << 5u; + + uint Pack = 0; + memcpy(&Pack, &x, sizeof(Pack)); + return float2packed10(Pack); + } + + GLM_FUNC_QUALIFIER float packed10bitToFloat(glm::uint x) + { + if(x == 0) + return 0.0f; + else if(x == ((1 << 10) - 1)) + return ~0;//NaN + else if(x == (0x1f << 5)) + return ~0;//Inf + + uint Result = packed10ToFloat(x); + + float Temp = 0; + memcpy(&Temp, &Result, sizeof(Temp)); + return Temp; + } + +// GLM_FUNC_QUALIFIER glm::uint f11_f11_f10(float x, float y, float z) +// { +// return ((floatTo11bit(x) & ((1 << 11) - 1)) << 0) | ((floatTo11bit(y) & ((1 << 11) - 1)) << 11) | ((floatTo10bit(z) & ((1 << 10) - 1)) << 22); +// } + +#if GLM_SILENT_WARNINGS == GLM_ENABLE +# if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# endif +#endif + + union u3u3u2 + { + struct Data + { + uint x : 3; + uint y : 3; + uint z : 2; + } data; + uint8 pack; + }; + + union u4u4 + { + struct Data + { + uint x : 4; + uint y : 4; + } data; + uint8 pack; + }; + + union u4u4u4u4 + { + struct Data + { + uint x : 4; + uint y : 4; + uint z : 4; + uint w : 4; + } data; + uint16 pack; + }; + + union u5u6u5 + { + struct Data + { + uint x : 5; + uint y : 6; + uint z : 5; + } data; + uint16 pack; + }; + + union u5u5u5u1 + { + struct Data + { + uint x : 5; + uint y : 5; + uint z : 5; + uint w : 1; + } data; + uint16 pack; + }; + +#if GLM_SILENT_WARNINGS == GLM_ENABLE +# if defined(__clang__) +# pragma clang diagnostic pop +# endif +#endif + + union u10u10u10u2 + { + struct Data + { + uint x : 10; + uint y : 10; + uint z : 10; + uint w : 2; + } data; + uint32 pack; + }; + + union i10i10i10i2 + { + struct Data + { + int x : 10; + int y : 10; + int z : 10; + int w : 2; + } data; + uint32 pack; + }; + + union u9u9u9e5 + { + struct Data + { + uint x : 9; + uint y : 9; + uint z : 9; + uint w : 5; + } data; + uint32 pack; + }; + + template + struct compute_half + {}; + + template + struct compute_half<1, Q> + { + GLM_FUNC_QUALIFIER static vec<1, uint16, Q> pack(vec<1, float, Q> const& v) + { + int16 const Unpack(detail::toFloat16(v.x)); + u16vec1 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<1, float, Q> unpack(vec<1, uint16, Q> const& v) + { + i16vec1 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<1, float, Q>(detail::toFloat32(v.x)); + } + }; + + template + struct compute_half<2, Q> + { + GLM_FUNC_QUALIFIER static vec<2, uint16, Q> pack(vec<2, float, Q> const& v) + { + vec<2, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y)); + u16vec2 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<2, float, Q> unpack(vec<2, uint16, Q> const& v) + { + i16vec2 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<2, float, Q>(detail::toFloat32(v.x), detail::toFloat32(v.y)); + } + }; + + template + struct compute_half<3, Q> + { + GLM_FUNC_QUALIFIER static vec<3, uint16, Q> pack(vec<3, float, Q> const& v) + { + vec<3, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y), detail::toFloat16(v.z)); + u16vec3 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<3, float, Q> unpack(vec<3, uint16, Q> const& v) + { + i16vec3 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<3, float, Q>(detail::toFloat32(v.x), detail::toFloat32(v.y), detail::toFloat32(v.z)); + } + }; + + template + struct compute_half<4, Q> + { + GLM_FUNC_QUALIFIER static vec<4, uint16, Q> pack(vec<4, float, Q> const& v) + { + vec<4, int16, Q> const Unpack(detail::toFloat16(v.x), detail::toFloat16(v.y), detail::toFloat16(v.z), detail::toFloat16(v.w)); + u16vec4 Packed; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER static vec<4, float, Q> unpack(vec<4, uint16, Q> const& v) + { + i16vec4 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec<4, float, Q>(detail::toFloat32(Unpack.x), detail::toFloat32(Unpack.y), detail::toFloat32(Unpack.z), detail::toFloat32(Unpack.w)); + } + }; +}//namespace detail + + GLM_FUNC_QUALIFIER uint8 packUnorm1x8(float v) + { + return static_cast(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + } + + GLM_FUNC_QUALIFIER float unpackUnorm1x8(uint8 p) + { + float const Unpack(p); + return Unpack * static_cast(0.0039215686274509803921568627451); // 1 / 255 + } + + GLM_FUNC_QUALIFIER uint16 packUnorm2x8(vec2 const& v) + { + u8vec2 const Topack(round(clamp(v, 0.0f, 1.0f) * 255.0f)); + + uint16 Unpack = 0; + memcpy(&Unpack, &Topack, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x8(uint16 p) + { + u8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return vec2(Unpack) * float(0.0039215686274509803921568627451); // 1 / 255 + } + + GLM_FUNC_QUALIFIER uint8 packSnorm1x8(float v) + { + int8 const Topack(static_cast(round(clamp(v ,-1.0f, 1.0f) * 127.0f))); + uint8 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackSnorm1x8(uint8 p) + { + int8 Unpack = 0; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + static_cast(Unpack) * 0.00787401574803149606299212598425f, // 1.0f / 127.0f + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packSnorm2x8(vec2 const& v) + { + i8vec2 const Topack(round(clamp(v, -1.0f, 1.0f) * 127.0f)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec2 unpackSnorm2x8(uint16 p) + { + i8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + vec2(Unpack) * 0.00787401574803149606299212598425f, // 1.0f / 127.0f + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packUnorm1x16(float s) + { + return static_cast(round(clamp(s, 0.0f, 1.0f) * 65535.0f)); + } + + GLM_FUNC_QUALIFIER float unpackUnorm1x16(uint16 p) + { + float const Unpack(p); + return Unpack * 1.5259021896696421759365224689097e-5f; // 1.0 / 65535.0 + } + + GLM_FUNC_QUALIFIER uint64 packUnorm4x16(vec4 const& v) + { + u16vec4 const Topack(round(clamp(v , 0.0f, 1.0f) * 65535.0f)); + uint64 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x16(uint64 p) + { + u16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return vec4(Unpack) * 1.5259021896696421759365224689097e-5f; // 1.0 / 65535.0 + } + + GLM_FUNC_QUALIFIER uint16 packSnorm1x16(float v) + { + int16 const Topack = static_cast(round(clamp(v ,-1.0f, 1.0f) * 32767.0f)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackSnorm1x16(uint16 p) + { + int16 Unpack = 0; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + static_cast(Unpack) * 3.0518509475997192297128208258309e-5f, //1.0f / 32767.0f, + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint64 packSnorm4x16(vec4 const& v) + { + i16vec4 const Topack(round(clamp(v ,-1.0f, 1.0f) * 32767.0f)); + uint64 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER vec4 unpackSnorm4x16(uint64 p) + { + i16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return clamp( + vec4(Unpack) * 3.0518509475997192297128208258309e-5f, //1.0f / 32767.0f, + -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint16 packHalf1x16(float v) + { + int16 const Topack(detail::toFloat16(v)); + uint16 Packed = 0; + memcpy(&Packed, &Topack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER float unpackHalf1x16(uint16 v) + { + int16 Unpack = 0; + memcpy(&Unpack, &v, sizeof(Unpack)); + return detail::toFloat32(Unpack); + } + + GLM_FUNC_QUALIFIER uint64 packHalf4x16(glm::vec4 const& v) + { + i16vec4 const Unpack( + detail::toFloat16(v.x), + detail::toFloat16(v.y), + detail::toFloat16(v.z), + detail::toFloat16(v.w)); + uint64 Packed = 0; + memcpy(&Packed, &Unpack, sizeof(Packed)); + return Packed; + } + + GLM_FUNC_QUALIFIER glm::vec4 unpackHalf4x16(uint64 v) + { + i16vec4 Unpack; + memcpy(&Unpack, &v, sizeof(Unpack)); + return vec4( + detail::toFloat32(Unpack.x), + detail::toFloat32(Unpack.y), + detail::toFloat32(Unpack.z), + detail::toFloat32(Unpack.w)); + } + + GLM_FUNC_QUALIFIER uint32 packI3x10_1x2(ivec4 const& v) + { + detail::i10i10i10i2 Result; + Result.data.x = v.x; + Result.data.y = v.y; + Result.data.z = v.z; + Result.data.w = v.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER ivec4 unpackI3x10_1x2(uint32 v) + { + detail::i10i10i10i2 Unpack; + Unpack.pack = v; + return ivec4( + Unpack.data.x, + Unpack.data.y, + Unpack.data.z, + Unpack.data.w); + } + + GLM_FUNC_QUALIFIER uint32 packU3x10_1x2(uvec4 const& v) + { + detail::u10u10u10u2 Result; + Result.data.x = v.x; + Result.data.y = v.y; + Result.data.z = v.z; + Result.data.w = v.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER uvec4 unpackU3x10_1x2(uint32 v) + { + detail::u10u10u10u2 Unpack; + Unpack.pack = v; + return uvec4( + Unpack.data.x, + Unpack.data.y, + Unpack.data.z, + Unpack.data.w); + } + + GLM_FUNC_QUALIFIER uint32 packSnorm3x10_1x2(vec4 const& v) + { + ivec4 const Pack(round(clamp(v,-1.0f, 1.0f) * vec4(511.f, 511.f, 511.f, 1.f))); + + detail::i10i10i10i2 Result; + Result.data.x = Pack.x; + Result.data.y = Pack.y; + Result.data.z = Pack.z; + Result.data.w = Pack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackSnorm3x10_1x2(uint32 v) + { + detail::i10i10i10i2 Unpack; + Unpack.pack = v; + + vec4 const Result(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w); + + return clamp(Result * vec4(1.f / 511.f, 1.f / 511.f, 1.f / 511.f, 1.f), -1.0f, 1.0f); + } + + GLM_FUNC_QUALIFIER uint32 packUnorm3x10_1x2(vec4 const& v) + { + uvec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec4(1023.f, 1023.f, 1023.f, 3.f))); + + detail::u10u10u10u2 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm3x10_1x2(uint32 v) + { + vec4 const ScaleFactors(1.0f / 1023.f, 1.0f / 1023.f, 1.0f / 1023.f, 1.0f / 3.f); + + detail::u10u10u10u2 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactors; + } + + GLM_FUNC_QUALIFIER uint32 packF2x11_1x10(vec3 const& v) + { + return + ((detail::floatTo11bit(v.x) & ((1 << 11) - 1)) << 0) | + ((detail::floatTo11bit(v.y) & ((1 << 11) - 1)) << 11) | + ((detail::floatTo10bit(v.z) & ((1 << 10) - 1)) << 22); + } + + GLM_FUNC_QUALIFIER vec3 unpackF2x11_1x10(uint32 v) + { + return vec3( + detail::packed11bitToFloat(v >> 0), + detail::packed11bitToFloat(v >> 11), + detail::packed10bitToFloat(v >> 22)); + } + + GLM_FUNC_QUALIFIER uint32 packF3x9_E1x5(vec3 const& v) + { + float const SharedExpMax = (pow(2.0f, 9.0f - 1.0f) / pow(2.0f, 9.0f)) * pow(2.0f, 31.f - 15.f); + vec3 const Color = clamp(v, 0.0f, SharedExpMax); + float const MaxColor = max(Color.x, max(Color.y, Color.z)); + + float const ExpSharedP = max(-15.f - 1.f, floor(log2(MaxColor))) + 1.0f + 15.f; + float const MaxShared = floor(MaxColor / pow(2.0f, (ExpSharedP - 15.f - 9.f)) + 0.5f); + float const ExpShared = equal(MaxShared, pow(2.0f, 9.0f), epsilon()) ? ExpSharedP + 1.0f : ExpSharedP; + + uvec3 const ColorComp(floor(Color / pow(2.f, (ExpShared - 15.f - 9.f)) + 0.5f)); + + detail::u9u9u9e5 Unpack; + Unpack.data.x = ColorComp.x; + Unpack.data.y = ColorComp.y; + Unpack.data.z = ColorComp.z; + Unpack.data.w = uint(ExpShared); + return Unpack.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackF3x9_E1x5(uint32 v) + { + detail::u9u9u9e5 Unpack; + Unpack.pack = v; + + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * pow(2.0f, static_cast(Unpack.data.w) - 15.f - 9.f); + } + + // Based on Brian Karis http://graphicrants.blogspot.fr/2009/04/rgbm-color-encoding.html + template + GLM_FUNC_QUALIFIER vec<4, T, Q> packRGBM(vec<3, T, Q> const& rgb) + { + vec<3, T, Q> const Color(rgb * static_cast(1.0 / 6.0)); + T Alpha = clamp(max(max(Color.x, Color.y), max(Color.z, static_cast(1e-6))), static_cast(0), static_cast(1)); + Alpha = ceil(Alpha * static_cast(255.0)) / static_cast(255.0); + return vec<4, T, Q>(Color / Alpha, Alpha); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> unpackRGBM(vec<4, T, Q> const& rgbm) + { + return vec<3, T, Q>(rgbm.x, rgbm.y, rgbm.z) * rgbm.w * static_cast(6); + } + + template + GLM_FUNC_QUALIFIER vec packHalf(vec const& v) + { + return detail::compute_half::pack(v); + } + + template + GLM_FUNC_QUALIFIER vec unpackHalf(vec const& v) + { + return detail::compute_half::unpack(v); + } + + template + GLM_FUNC_QUALIFIER vec packUnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(round(clamp(v, static_cast(0), static_cast(1)) * static_cast(std::numeric_limits::max()))); + } + + template + GLM_FUNC_QUALIFIER vec unpackUnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(v) * (static_cast(1) / static_cast(std::numeric_limits::max())); + } + + template + GLM_FUNC_QUALIFIER vec packSnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return vec(round(clamp(v , static_cast(-1), static_cast(1)) * static_cast(std::numeric_limits::max()))); + } + + template + GLM_FUNC_QUALIFIER vec unpackSnorm(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_integer, "uintType must be an integer type"); + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "floatType must be a floating point type"); + + return clamp(vec(v) * (static_cast(1) / static_cast(std::numeric_limits::max())), static_cast(-1), static_cast(1)); + } + + GLM_FUNC_QUALIFIER uint8 packUnorm2x4(vec2 const& v) + { + u32vec2 const Unpack(round(clamp(v, 0.0f, 1.0f) * 15.0f)); + detail::u4u4 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec2 unpackUnorm2x4(uint8 v) + { + float const ScaleFactor(1.f / 15.f); + detail::u4u4 Unpack; + Unpack.pack = v; + return vec2(Unpack.data.x, Unpack.data.y) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm4x4(vec4 const& v) + { + u32vec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * 15.0f)); + detail::u4u4u4u4 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm4x4(uint16 v) + { + float const ScaleFactor(1.f / 15.f); + detail::u4u4u4u4 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm1x5_1x6_1x5(vec3 const& v) + { + u32vec3 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec3(31.f, 63.f, 31.f))); + detail::u5u6u5 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackUnorm1x5_1x6_1x5(uint16 v) + { + vec3 const ScaleFactor(1.f / 31.f, 1.f / 63.f, 1.f / 31.f); + detail::u5u6u5 Unpack; + Unpack.pack = v; + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint16 packUnorm3x5_1x1(vec4 const& v) + { + u32vec4 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec4(31.f, 31.f, 31.f, 1.f))); + detail::u5u5u5u1 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + Result.data.w = Unpack.w; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec4 unpackUnorm3x5_1x1(uint16 v) + { + vec4 const ScaleFactor(1.f / 31.f, 1.f / 31.f, 1.f / 31.f, 1.f); + detail::u5u5u5u1 Unpack; + Unpack.pack = v; + return vec4(Unpack.data.x, Unpack.data.y, Unpack.data.z, Unpack.data.w) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER uint8 packUnorm2x3_1x2(vec3 const& v) + { + u32vec3 const Unpack(round(clamp(v, 0.0f, 1.0f) * vec3(7.f, 7.f, 3.f))); + detail::u3u3u2 Result; + Result.data.x = Unpack.x; + Result.data.y = Unpack.y; + Result.data.z = Unpack.z; + return Result.pack; + } + + GLM_FUNC_QUALIFIER vec3 unpackUnorm2x3_1x2(uint8 v) + { + vec3 const ScaleFactor(1.f / 7.f, 1.f / 7.f, 1.f / 3.f); + detail::u3u3u2 Unpack; + Unpack.pack = v; + return vec3(Unpack.data.x, Unpack.data.y, Unpack.data.z) * ScaleFactor; + } + + GLM_FUNC_QUALIFIER int16 packInt2x8(i8vec2 const& v) + { + int16 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i8vec2 unpackInt2x8(int16 p) + { + i8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint16 packUint2x8(u8vec2 const& v) + { + uint16 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u8vec2 unpackUint2x8(uint16 p) + { + u8vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int32 packInt4x8(i8vec4 const& v) + { + int32 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i8vec4 unpackInt4x8(int32 p) + { + i8vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint32 packUint4x8(u8vec4 const& v) + { + uint32 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u8vec4 unpackUint4x8(uint32 p) + { + u8vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int packInt2x16(i16vec2 const& v) + { + int Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i16vec2 unpackInt2x16(int p) + { + i16vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int64 packInt4x16(i16vec4 const& v) + { + int64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i16vec4 unpackInt4x16(int64 p) + { + i16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint packUint2x16(u16vec2 const& v) + { + uint Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u16vec2 unpackUint2x16(uint p) + { + u16vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint64 packUint4x16(u16vec4 const& v) + { + uint64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u16vec4 unpackUint4x16(uint64 p) + { + u16vec4 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER int64 packInt2x32(i32vec2 const& v) + { + int64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER i32vec2 unpackInt2x32(int64 p) + { + i32vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } + + GLM_FUNC_QUALIFIER uint64 packUint2x32(u32vec2 const& v) + { + uint64 Pack = 0; + memcpy(&Pack, &v, sizeof(Pack)); + return Pack; + } + + GLM_FUNC_QUALIFIER u32vec2 unpackUint2x32(uint64 p) + { + u32vec2 Unpack; + memcpy(&Unpack, &p, sizeof(Unpack)); + return Unpack; + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtc/quaternion.hpp b/thirdparty/glm/glm/gtc/quaternion.hpp new file mode 100644 index 0000000..314449e --- /dev/null +++ b/thirdparty/glm/glm/gtc/quaternion.hpp @@ -0,0 +1,173 @@ +/// @ref gtc_quaternion +/// @file glm/gtc/quaternion.hpp +/// +/// @see core (dependence) +/// @see gtc_constants (dependence) +/// +/// @defgroup gtc_quaternion GLM_GTC_quaternion +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines a templated quaternion type and several quaternion operations. + +#pragma once + +// Dependency: +#include "../gtc/constants.hpp" +#include "../gtc/matrix_transform.hpp" +#include "../ext/vector_relational.hpp" +#include "../ext/quaternion_common.hpp" +#include "../ext/quaternion_float.hpp" +#include "../ext/quaternion_float_precision.hpp" +#include "../ext/quaternion_double.hpp" +#include "../ext/quaternion_double_precision.hpp" +#include "../ext/quaternion_relational.hpp" +#include "../ext/quaternion_geometric.hpp" +#include "../ext/quaternion_trigonometric.hpp" +#include "../ext/quaternion_transform.hpp" +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat4x4.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_quaternion extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_quaternion + /// @{ + + /// Returns euler angles, pitch as x, yaw as y, roll as z. + /// The result is expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> eulerAngles(qua const& x); + + /// Returns roll value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T roll(qua const& x); + + /// Returns pitch value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T pitch(qua const& x); + + /// Returns yaw value of euler angles expressed in radians. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL T yaw(qua const& x); + + /// Converts a quaternion to a 3 * 3 matrix. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL mat<3, 3, T, Q> mat3_cast(qua const& x); + + /// Converts a quaternion to a 4 * 4 matrix. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL mat<4, 4, T, Q> mat4_cast(qua const& x); + + /// Converts a pure rotation 3 * 3 matrix to a quaternion. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL qua quat_cast(mat<3, 3, T, Q> const& x); + + /// Converts a pure rotation 4 * 4 matrix to a quaternion. + /// + /// @tparam T Floating-point scalar types. + /// + /// @see gtc_quaternion + template + GLM_FUNC_DECL qua quat_cast(mat<4, 4, T, Q> const& x); + + /// Returns the component-wise comparison result of x < y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> lessThan(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x <= y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> lessThanEqual(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x > y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> greaterThan(qua const& x, qua const& y); + + /// Returns the component-wise comparison of result x >= y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_quaternion_relational + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<4, bool, Q> greaterThanEqual(qua const& x, qua const& y); + + /// Build a look at quaternion based on the default handedness. + /// + /// @param direction Desired forward direction. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAt( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + + /// Build a right-handed look at quaternion. + /// + /// @param direction Desired forward direction onto which the -z-axis gets mapped. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAtRH( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + + /// Build a left-handed look at quaternion. + /// + /// @param direction Desired forward direction onto which the +z-axis gets mapped. Needs to be normalized. + /// @param up Up vector, how the camera is oriented. Typically (0, 1, 0). + template + GLM_FUNC_DECL qua quatLookAtLH( + vec<3, T, Q> const& direction, + vec<3, T, Q> const& up); + /// @} +} //namespace glm + +#include "quaternion.inl" diff --git a/thirdparty/glm/glm/gtc/quaternion.inl b/thirdparty/glm/glm/gtc/quaternion.inl new file mode 100644 index 0000000..ea159f2 --- /dev/null +++ b/thirdparty/glm/glm/gtc/quaternion.inl @@ -0,0 +1,208 @@ +#include "../trigonometric.hpp" +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "epsilon.hpp" +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> eulerAngles(qua const& x) + { + return vec<3, T, Q>(pitch(x), yaw(x), roll(x)); + } + + template + GLM_FUNC_QUALIFIER T roll(qua const& q) + { + T const y = static_cast(2) * (q.x * q.y + q.w * q.z); + T const x = q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z; + + if(all(equal(vec<2, T, Q>(x, y), vec<2, T, Q>(0), epsilon()))) //avoid atan2(0,0) - handle singularity - Matiis + return static_cast(0); + + return static_cast(atan(y, x)); + } + + template + GLM_FUNC_QUALIFIER T pitch(qua const& q) + { + //return T(atan(T(2) * (q.y * q.z + q.w * q.x), q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z)); + T const y = static_cast(2) * (q.y * q.z + q.w * q.x); + T const x = q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z; + + if(all(equal(vec<2, T, Q>(x, y), vec<2, T, Q>(0), epsilon()))) //avoid atan2(0,0) - handle singularity - Matiis + return static_cast(static_cast(2) * atan(q.x, q.w)); + + return static_cast(atan(y, x)); + } + + template + GLM_FUNC_QUALIFIER T yaw(qua const& q) + { + return asin(clamp(static_cast(-2) * (q.x * q.z - q.w * q.y), static_cast(-1), static_cast(1))); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> mat3_cast(qua const& q) + { + mat<3, 3, T, Q> Result(T(1)); + T qxx(q.x * q.x); + T qyy(q.y * q.y); + T qzz(q.z * q.z); + T qxz(q.x * q.z); + T qxy(q.x * q.y); + T qyz(q.y * q.z); + T qwx(q.w * q.x); + T qwy(q.w * q.y); + T qwz(q.w * q.z); + + Result[0][0] = T(1) - T(2) * (qyy + qzz); + Result[0][1] = T(2) * (qxy + qwz); + Result[0][2] = T(2) * (qxz - qwy); + + Result[1][0] = T(2) * (qxy - qwz); + Result[1][1] = T(1) - T(2) * (qxx + qzz); + Result[1][2] = T(2) * (qyz + qwx); + + Result[2][0] = T(2) * (qxz + qwy); + Result[2][1] = T(2) * (qyz - qwx); + Result[2][2] = T(1) - T(2) * (qxx + qyy); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> mat4_cast(qua const& q) + { + return mat<4, 4, T, Q>(mat3_cast(q)); + } + + template + GLM_FUNC_QUALIFIER qua quat_cast(mat<3, 3, T, Q> const& m) + { + T fourXSquaredMinus1 = m[0][0] - m[1][1] - m[2][2]; + T fourYSquaredMinus1 = m[1][1] - m[0][0] - m[2][2]; + T fourZSquaredMinus1 = m[2][2] - m[0][0] - m[1][1]; + T fourWSquaredMinus1 = m[0][0] + m[1][1] + m[2][2]; + + int biggestIndex = 0; + T fourBiggestSquaredMinus1 = fourWSquaredMinus1; + if(fourXSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourXSquaredMinus1; + biggestIndex = 1; + } + if(fourYSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourYSquaredMinus1; + biggestIndex = 2; + } + if(fourZSquaredMinus1 > fourBiggestSquaredMinus1) + { + fourBiggestSquaredMinus1 = fourZSquaredMinus1; + biggestIndex = 3; + } + + T biggestVal = sqrt(fourBiggestSquaredMinus1 + static_cast(1)) * static_cast(0.5); + T mult = static_cast(0.25) / biggestVal; + + switch(biggestIndex) + { + case 0: + return qua::wxyz(biggestVal, (m[1][2] - m[2][1]) * mult, (m[2][0] - m[0][2]) * mult, (m[0][1] - m[1][0]) * mult); + case 1: + return qua::wxyz((m[1][2] - m[2][1]) * mult, biggestVal, (m[0][1] + m[1][0]) * mult, (m[2][0] + m[0][2]) * mult); + case 2: + return qua::wxyz((m[2][0] - m[0][2]) * mult, (m[0][1] + m[1][0]) * mult, biggestVal, (m[1][2] + m[2][1]) * mult); + case 3: + return qua::wxyz((m[0][1] - m[1][0]) * mult, (m[2][0] + m[0][2]) * mult, (m[1][2] + m[2][1]) * mult, biggestVal); + default: // Silence a -Wswitch-default warning in GCC. Should never actually get here. Assert is just for sanity. + assert(false); + return qua::wxyz(1, 0, 0, 0); + } + } + + template + GLM_FUNC_QUALIFIER qua quat_cast(mat<4, 4, T, Q> const& m4) + { + return quat_cast(mat<3, 3, T, Q>(m4)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> lessThan(qua const& x, qua const& y) + { + vec<4, bool, Q> Result(false, false, false, false); + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] < y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> lessThanEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result(false, false, false, false); + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] <= y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> greaterThan(qua const& x, qua const& y) + { + vec<4, bool, Q> Result(false, false, false, false); + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] > y[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<4, bool, Q> greaterThanEqual(qua const& x, qua const& y) + { + vec<4, bool, Q> Result(false, false, false, false); + for(length_t i = 0; i < x.length(); ++i) + Result[i] = x[i] >= y[i]; + return Result; + } + + + template + GLM_FUNC_QUALIFIER qua quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { +# if GLM_CONFIG_CLIP_CONTROL & GLM_CLIP_CONTROL_LH_BIT + return quatLookAtLH(direction, up); +# else + return quatLookAtRH(direction, up); +# endif + } + + template + GLM_FUNC_QUALIFIER qua quatLookAtRH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { + mat<3, 3, T, Q> Result; + + Result[2] = -direction; + vec<3, T, Q> const& Right = cross(up, Result[2]); + Result[0] = Right * inversesqrt(max(static_cast(0.00001), dot(Right, Right))); + Result[1] = cross(Result[2], Result[0]); + + return quat_cast(Result); + } + + template + GLM_FUNC_QUALIFIER qua quatLookAtLH(vec<3, T, Q> const& direction, vec<3, T, Q> const& up) + { + mat<3, 3, T, Q> Result; + + Result[2] = direction; + vec<3, T, Q> const& Right = cross(up, Result[2]); + Result[0] = Right * inversesqrt(max(static_cast(0.00001), dot(Right, Right))); + Result[1] = cross(Result[2], Result[0]); + + return quat_cast(Result); + } +}//namespace glm + +#if GLM_CONFIG_SIMD == GLM_ENABLE +# include "quaternion_simd.inl" +#endif + diff --git a/thirdparty/glm/glm/gtc/quaternion_simd.inl b/thirdparty/glm/glm/gtc/quaternion_simd.inl new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/glm/glm/gtc/random.hpp b/thirdparty/glm/glm/gtc/random.hpp new file mode 100644 index 0000000..c6485bf --- /dev/null +++ b/thirdparty/glm/glm/gtc/random.hpp @@ -0,0 +1,82 @@ +/// @ref gtc_random +/// @file glm/gtc/random.hpp +/// +/// @see core (dependence) +/// @see gtx_random (extended) +/// +/// @defgroup gtc_random GLM_GTC_random +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Generate random number from various distribution methods. + +#pragma once + +// Dependency: +#include "../ext/scalar_int_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/qualifier.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_random extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_random + /// @{ + + /// Generate random numbers in the interval [Min, Max], according a linear distribution + /// + /// @param Min Minimum value included in the sampling + /// @param Max Maximum value included in the sampling + /// @tparam genType Value type. Currently supported: float or double scalars. + /// @see gtc_random + template + GLM_FUNC_DECL genType linearRand(genType Min, genType Max); + + /// Generate random numbers in the interval [Min, Max], according a linear distribution + /// + /// @param Min Minimum value included in the sampling + /// @param Max Maximum value included in the sampling + /// @tparam T Value type. Currently supported: float or double. + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec linearRand(vec const& Min, vec const& Max); + + /// Generate random numbers in the interval [Min, Max], according a gaussian distribution + /// + /// @see gtc_random + template + GLM_FUNC_DECL genType gaussRand(genType Mean, genType Deviation); + + /// Generate a random 2D vector which coordinates are regularly distributed on a circle of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<2, T, defaultp> circularRand(T Radius); + + /// Generate a random 3D vector which coordinates are regularly distributed on a sphere of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<3, T, defaultp> sphericalRand(T Radius); + + /// Generate a random 2D vector which coordinates are regularly distributed within the area of a disk of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<2, T, defaultp> diskRand(T Radius); + + /// Generate a random 3D vector which coordinates are regularly distributed within the volume of a ball of a given radius + /// + /// @see gtc_random + template + GLM_FUNC_DECL vec<3, T, defaultp> ballRand(T Radius); + + /// @} +}//namespace glm + +#include "random.inl" diff --git a/thirdparty/glm/glm/gtc/random.inl b/thirdparty/glm/glm/gtc/random.inl new file mode 100644 index 0000000..249ec9f --- /dev/null +++ b/thirdparty/glm/glm/gtc/random.inl @@ -0,0 +1,303 @@ +#include "../geometric.hpp" +#include "../exponential.hpp" +#include "../trigonometric.hpp" +#include "../detail/type_vec1.hpp" +#include +#include +#include +#include + +namespace glm{ +namespace detail +{ + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call(); + }; + + template + struct compute_rand<1, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<1, uint8, P> call() + { + return vec<1, uint8, P>( + static_cast(std::rand() % std::numeric_limits::max())); + } + }; + + template + struct compute_rand<2, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<2, uint8, P> call() + { + return vec<2, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand<3, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<3, uint8, P> call() + { + return vec<3, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand<4, uint8, P> + { + GLM_FUNC_QUALIFIER static vec<4, uint8, P> call() + { + return vec<4, uint8, P>( + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max(), + std::rand() % std::numeric_limits::max()); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(8)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(16)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_rand + { + GLM_FUNC_QUALIFIER static vec call() + { + return + (vec(compute_rand::call()) << static_cast(32)) | + (vec(compute_rand::call()) << static_cast(0)); + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max); + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (vec(compute_rand::call() % vec(Max + static_cast(1) - Min))) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return (compute_rand::call() % (Max + static_cast(1) - Min)) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; + + template + struct compute_linearRand + { + GLM_FUNC_QUALIFIER static vec call(vec const& Min, vec const& Max) + { + return vec(compute_rand::call()) / static_cast(std::numeric_limits::max()) * (Max - Min) + Min; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER genType linearRand(genType Min, genType Max) + { + return detail::compute_linearRand<1, genType, highp>::call( + vec<1, genType, highp>(Min), + vec<1, genType, highp>(Max)).x; + } + + template + GLM_FUNC_QUALIFIER vec linearRand(vec const& Min, vec const& Max) + { + return detail::compute_linearRand::call(Min, Max); + } + + template + GLM_FUNC_QUALIFIER genType gaussRand(genType Mean, genType Deviation) + { + genType w, x1, x2; + + do + { + x1 = linearRand(genType(-1), genType(1)); + x2 = linearRand(genType(-1), genType(1)); + + w = x1 * x1 + x2 * x2; + } while(w > genType(1)); + + return static_cast(x2 * Deviation * Deviation * sqrt((genType(-2) * log(w)) / w) + Mean); + } + + template + GLM_FUNC_QUALIFIER vec gaussRand(vec const& Mean, vec const& Deviation) + { + return detail::functor2::call(gaussRand, Mean, Deviation); + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> diskRand(T Radius) + { + assert(Radius > static_cast(0)); + + vec<2, T, defaultp> Result(T(0)); + T LenRadius(T(0)); + + do + { + Result = linearRand( + vec<2, T, defaultp>(-Radius), + vec<2, T, defaultp>(Radius)); + LenRadius = length(Result); + } + while(LenRadius > Radius); + + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> ballRand(T Radius) + { + assert(Radius > static_cast(0)); + + vec<3, T, defaultp> Result(T(0)); + T LenRadius(T(0)); + + do + { + Result = linearRand( + vec<3, T, defaultp>(-Radius), + vec<3, T, defaultp>(Radius)); + LenRadius = length(Result); + } + while(LenRadius > Radius); + + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> circularRand(T Radius) + { + assert(Radius > static_cast(0)); + + T a = linearRand(T(0), static_cast(6.283185307179586476925286766559)); + return vec<2, T, defaultp>(glm::cos(a), glm::sin(a)) * Radius; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> sphericalRand(T Radius) + { + assert(Radius > static_cast(0)); + + T theta = linearRand(T(0), T(6.283185307179586476925286766559f)); + T phi = std::acos(linearRand(T(-1.0f), T(1.0f))); + + T x = std::sin(phi) * std::cos(theta); + T y = std::sin(phi) * std::sin(theta); + T z = std::cos(phi); + + return vec<3, T, defaultp>(x, y, z) * Radius; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/reciprocal.hpp b/thirdparty/glm/glm/gtc/reciprocal.hpp new file mode 100644 index 0000000..4d0fc91 --- /dev/null +++ b/thirdparty/glm/glm/gtc/reciprocal.hpp @@ -0,0 +1,24 @@ +/// @ref gtc_reciprocal +/// @file glm/gtc/reciprocal.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_reciprocal GLM_GTC_reciprocal +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Define secant, cosecant and cotangent functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_reciprocal extension included") +#endif + +#include "../ext/scalar_reciprocal.hpp" +#include "../ext/vector_reciprocal.hpp" + diff --git a/thirdparty/glm/glm/gtc/round.hpp b/thirdparty/glm/glm/gtc/round.hpp new file mode 100644 index 0000000..56edbbc --- /dev/null +++ b/thirdparty/glm/glm/gtc/round.hpp @@ -0,0 +1,160 @@ +/// @ref gtc_round +/// @file glm/gtc/round.hpp +/// +/// @see core (dependence) +/// @see gtc_round (dependence) +/// +/// @defgroup gtc_round GLM_GTC_round +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Rounding value to specific boundings + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "../vector_relational.hpp" +#include "../common.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_round extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_round + /// @{ + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType ceilPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just higher the input value, + /// round up to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec ceilPowerOfTwo(vec const& v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType floorPowerOfTwo(genIUType v); + + /// Return the power of two number which value is just lower the input value, + /// round down to a power of two. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec floorPowerOfTwo(vec const& v); + + /// Return the power of two number which value is the closet to the input value. + /// + /// @see gtc_round + template + GLM_FUNC_DECL genIUType roundPowerOfTwo(genIUType v); + + /// Return the power of two number which value is the closet to the input value. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec roundPowerOfTwo(vec const& v); + + /// Higher multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType ceilMultiple(genType v, genType Multiple); + + /// Higher multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec ceilMultiple(vec const& v, vec const& Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType floorMultiple(genType v, genType Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec floorMultiple(vec const& v, vec const& Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam genType Floating-point or integer scalar or vector types. + /// + /// @param v Source value to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL genType roundMultiple(genType v, genType Multiple); + + /// Lower multiple number of Source. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @param v Source values to which is applied the function + /// @param Multiple Must be a null or positive value + /// + /// @see gtc_round + template + GLM_FUNC_DECL vec roundMultiple(vec const& v, vec const& Multiple); + + /// @} +} //namespace glm + +#include "round.inl" diff --git a/thirdparty/glm/glm/gtc/round.inl b/thirdparty/glm/glm/gtc/round.inl new file mode 100644 index 0000000..48411e4 --- /dev/null +++ b/thirdparty/glm/glm/gtc/round.inl @@ -0,0 +1,155 @@ +/// @ref gtc_round + +#include "../integer.hpp" +#include "../ext/vector_integer.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_roundMultiple {}; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if (Source >= genType(0)) + return Source - std::fmod(Source, Multiple); + else + { + genType Tmp = Source + genType(1); + return Tmp - std::fmod(Tmp, Multiple) - Multiple; + } + } + }; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if (Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; + + template<> + struct compute_roundMultiple + { + template + GLM_FUNC_QUALIFIER static genType call(genType Source, genType Multiple) + { + if (Source >= genType(0)) + return Source - Source % Multiple; + else + { + genType Tmp = Source + genType(1); + return Tmp - Tmp % Multiple - Multiple; + } + } + }; +}//namespace detail + + ////////////////// + // ceilPowerOfTwo + + template + GLM_FUNC_QUALIFIER genType ceilPowerOfTwo(genType value) + { + return detail::compute_ceilPowerOfTwo<1, genType, defaultp, std::numeric_limits::is_signed>::call(vec<1, genType, defaultp>(value)).x; + } + + template + GLM_FUNC_QUALIFIER vec ceilPowerOfTwo(vec const& v) + { + return detail::compute_ceilPowerOfTwo::is_signed>::call(v); + } + + /////////////////// + // floorPowerOfTwo + + template + GLM_FUNC_QUALIFIER genType floorPowerOfTwo(genType value) + { + return isPowerOfTwo(value) ? value : static_cast(1) << findMSB(value); + } + + template + GLM_FUNC_QUALIFIER vec floorPowerOfTwo(vec const& v) + { + return detail::functor1::call(floorPowerOfTwo, v); + } + + /////////////////// + // roundPowerOfTwo + + template + GLM_FUNC_QUALIFIER genIUType roundPowerOfTwo(genIUType value) + { + if(isPowerOfTwo(value)) + return value; + + genIUType const prev = static_cast(1) << findMSB(value); + genIUType const next = prev << static_cast(1); + return (next - value) < (value - prev) ? next : prev; + } + + template + GLM_FUNC_QUALIFIER vec roundPowerOfTwo(vec const& v) + { + return detail::functor1::call(roundPowerOfTwo, v); + } + + ////////////////////// + // ceilMultiple + + template + GLM_FUNC_QUALIFIER genType ceilMultiple(genType Source, genType Multiple) + { + return detail::compute_ceilMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec ceilMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(ceilMultiple, Source, Multiple); + } + + ////////////////////// + // floorMultiple + + template + GLM_FUNC_QUALIFIER genType floorMultiple(genType Source, genType Multiple) + { + return detail::compute_floorMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec floorMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(floorMultiple, Source, Multiple); + } + + ////////////////////// + // roundMultiple + + template + GLM_FUNC_QUALIFIER genType roundMultiple(genType Source, genType Multiple) + { + return detail::compute_roundMultiple::is_iec559, std::numeric_limits::is_signed>::call(Source, Multiple); + } + + template + GLM_FUNC_QUALIFIER vec roundMultiple(vec const& Source, vec const& Multiple) + { + return detail::functor2::call(roundMultiple, Source, Multiple); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/type_aligned.hpp b/thirdparty/glm/glm/gtc/type_aligned.hpp new file mode 100644 index 0000000..5403abf --- /dev/null +++ b/thirdparty/glm/glm/gtc/type_aligned.hpp @@ -0,0 +1,1315 @@ +/// @ref gtc_type_aligned +/// @file glm/gtc/type_aligned.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_type_aligned GLM_GTC_type_aligned +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Aligned types allowing SIMD optimizations of vectors and matrices types + +#pragma once + +#if (GLM_CONFIG_ALIGNED_GENTYPES == GLM_DISABLE) +# error "GLM: Aligned gentypes require to enable C++ language extensions. Define GLM_FORCE_ALIGNED_GENTYPES before including GLM headers to use aligned types." +#endif + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_aligned extension included") +#endif + +#include "../mat4x4.hpp" +#include "../mat4x3.hpp" +#include "../mat4x2.hpp" +#include "../mat3x4.hpp" +#include "../mat3x3.hpp" +#include "../mat3x2.hpp" +#include "../mat2x4.hpp" +#include "../mat2x3.hpp" +#include "../mat2x2.hpp" +#include "../gtc/vec1.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" + +namespace glm +{ + /// @addtogroup gtc_type_aligned + /// @{ + + // -- *vec1 -- + + /// 1 component vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_highp> aligned_highp_vec1; + + /// 1 component vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_mediump> aligned_mediump_vec1; + + /// 1 component vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, aligned_lowp> aligned_lowp_vec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_highp> aligned_highp_dvec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_mediump> aligned_mediump_dvec1; + + /// 1 component vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, aligned_lowp> aligned_lowp_dvec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_highp> aligned_highp_ivec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_mediump> aligned_mediump_ivec1; + + /// 1 component vector aligned in memory of signed integer numbers. + typedef vec<1, int, aligned_lowp> aligned_lowp_ivec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_highp> aligned_highp_uvec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_mediump> aligned_mediump_uvec1; + + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef vec<1, uint, aligned_lowp> aligned_lowp_uvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_highp> aligned_highp_bvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_mediump> aligned_mediump_bvec1; + + /// 1 component vector aligned in memory of bool values. + typedef vec<1, bool, aligned_lowp> aligned_lowp_bvec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, float, packed_highp> packed_highp_vec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, float, packed_mediump> packed_mediump_vec1; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, float, packed_lowp> packed_lowp_vec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<1, double, packed_highp> packed_highp_dvec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<1, double, packed_mediump> packed_mediump_dvec1; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<1, double, packed_lowp> packed_lowp_dvec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_highp> packed_highp_ivec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_mediump> packed_mediump_ivec1; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef vec<1, int, packed_lowp> packed_lowp_ivec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_highp> packed_highp_uvec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_mediump> packed_mediump_uvec1; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef vec<1, uint, packed_lowp> packed_lowp_uvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_highp> packed_highp_bvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_mediump> packed_mediump_bvec1; + + /// 1 component vector tightly packed in memory of bool values. + typedef vec<1, bool, packed_lowp> packed_lowp_bvec1; + + // -- *vec2 -- + + /// 2 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_highp> aligned_highp_vec2; + + /// 2 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_mediump> aligned_mediump_vec2; + + /// 2 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, float, aligned_lowp> aligned_lowp_vec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_highp> aligned_highp_dvec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_mediump> aligned_mediump_dvec2; + + /// 2 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, double, aligned_lowp> aligned_lowp_dvec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_highp> aligned_highp_ivec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_mediump> aligned_mediump_ivec2; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef vec<2, int, aligned_lowp> aligned_lowp_ivec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_highp> aligned_highp_uvec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_mediump> aligned_mediump_uvec2; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef vec<2, uint, aligned_lowp> aligned_lowp_uvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_highp> aligned_highp_bvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_mediump> aligned_mediump_bvec2; + + /// 2 components vector aligned in memory of bool values. + typedef vec<2, bool, aligned_lowp> aligned_lowp_bvec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, float, packed_highp> packed_highp_vec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, float, packed_mediump> packed_mediump_vec2; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, float, packed_lowp> packed_lowp_vec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<2, double, packed_highp> packed_highp_dvec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<2, double, packed_mediump> packed_mediump_dvec2; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<2, double, packed_lowp> packed_lowp_dvec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_highp> packed_highp_ivec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_mediump> packed_mediump_ivec2; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef vec<2, int, packed_lowp> packed_lowp_ivec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_highp> packed_highp_uvec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_mediump> packed_mediump_uvec2; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<2, uint, packed_lowp> packed_lowp_uvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_highp> packed_highp_bvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_mediump> packed_mediump_bvec2; + + /// 2 components vector tightly packed in memory of bool values. + typedef vec<2, bool, packed_lowp> packed_lowp_bvec2; + + // -- *vec3 -- + + /// 3 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_highp> aligned_highp_vec3; + + /// 3 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_mediump> aligned_mediump_vec3; + + /// 3 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, float, aligned_lowp> aligned_lowp_vec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_highp> aligned_highp_dvec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_mediump> aligned_mediump_dvec3; + + /// 3 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, double, aligned_lowp> aligned_lowp_dvec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_highp> aligned_highp_ivec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_mediump> aligned_mediump_ivec3; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef vec<3, int, aligned_lowp> aligned_lowp_ivec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_highp> aligned_highp_uvec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_mediump> aligned_mediump_uvec3; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef vec<3, uint, aligned_lowp> aligned_lowp_uvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_highp> aligned_highp_bvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_mediump> aligned_mediump_bvec3; + + /// 3 components vector aligned in memory of bool values. + typedef vec<3, bool, aligned_lowp> aligned_lowp_bvec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, float, packed_highp> packed_highp_vec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, float, packed_mediump> packed_mediump_vec3; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, float, packed_lowp> packed_lowp_vec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<3, double, packed_highp> packed_highp_dvec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<3, double, packed_mediump> packed_mediump_dvec3; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<3, double, packed_lowp> packed_lowp_dvec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_highp> packed_highp_ivec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_mediump> packed_mediump_ivec3; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef vec<3, int, packed_lowp> packed_lowp_ivec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_highp> packed_highp_uvec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_mediump> packed_mediump_uvec3; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<3, uint, packed_lowp> packed_lowp_uvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_highp> packed_highp_bvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_mediump> packed_mediump_bvec3; + + /// 3 components vector tightly packed in memory of bool values. + typedef vec<3, bool, packed_lowp> packed_lowp_bvec3; + + // -- *vec4 -- + + /// 4 components vector aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_highp> aligned_highp_vec4; + + /// 4 components vector aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_mediump> aligned_mediump_vec4; + + /// 4 components vector aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, float, aligned_lowp> aligned_lowp_vec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_highp> aligned_highp_dvec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_mediump> aligned_mediump_dvec4; + + /// 4 components vector aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, double, aligned_lowp> aligned_lowp_dvec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_highp> aligned_highp_ivec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_mediump> aligned_mediump_ivec4; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef vec<4, int, aligned_lowp> aligned_lowp_ivec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_highp> aligned_highp_uvec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_mediump> aligned_mediump_uvec4; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef vec<4, uint, aligned_lowp> aligned_lowp_uvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_highp> aligned_highp_bvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_mediump> aligned_mediump_bvec4; + + /// 4 components vector aligned in memory of bool values. + typedef vec<4, bool, aligned_lowp> aligned_lowp_bvec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, float, packed_highp> packed_highp_vec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, float, packed_mediump> packed_mediump_vec4; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, float, packed_lowp> packed_lowp_vec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef vec<4, double, packed_highp> packed_highp_dvec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef vec<4, double, packed_mediump> packed_mediump_dvec4; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef vec<4, double, packed_lowp> packed_lowp_dvec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_highp> packed_highp_ivec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_mediump> packed_mediump_ivec4; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef vec<4, int, packed_lowp> packed_lowp_ivec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_highp> packed_highp_uvec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_mediump> packed_mediump_uvec4; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef vec<4, uint, packed_lowp> packed_lowp_uvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_highp> packed_highp_bvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_mediump> packed_mediump_bvec4; + + /// 4 components vector tightly packed in memory of bool values. + typedef vec<4, bool, packed_lowp> packed_lowp_bvec4; + + // -- *mat2 -- + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_highp> aligned_highp_mat2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_mediump> aligned_mediump_mat2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_lowp> aligned_lowp_mat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_highp> aligned_highp_dmat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_mediump> aligned_mediump_dmat2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_lowp> aligned_lowp_dmat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_highp> packed_highp_mat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_mediump> packed_mediump_mat2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_lowp> packed_lowp_mat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_highp> packed_highp_dmat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_mediump> packed_mediump_dmat2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_lowp> packed_lowp_dmat2; + + // -- *mat3 -- + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_highp> aligned_highp_mat3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_mediump> aligned_mediump_mat3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_lowp> aligned_lowp_mat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_highp> aligned_highp_dmat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_mediump> aligned_mediump_dmat3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_lowp> aligned_lowp_dmat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_highp> packed_highp_mat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_mediump> packed_mediump_mat3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_lowp> packed_lowp_mat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_highp> packed_highp_dmat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_mediump> packed_mediump_dmat3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_lowp> packed_lowp_dmat3; + + // -- *mat4 -- + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_highp> aligned_highp_mat4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_mediump> aligned_mediump_mat4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_lowp> aligned_lowp_mat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_highp> aligned_highp_dmat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_mediump> aligned_mediump_dmat4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_lowp> aligned_lowp_dmat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_highp> packed_highp_mat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_mediump> packed_mediump_mat4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_lowp> packed_lowp_mat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_highp> packed_highp_dmat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_mediump> packed_mediump_dmat4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_lowp> packed_lowp_dmat4; + + // -- *mat2x2 -- + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_highp> aligned_highp_mat2x2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_mediump> aligned_mediump_mat2x2; + + /// 2 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, aligned_lowp> aligned_lowp_mat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_highp> aligned_highp_dmat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_mediump> aligned_mediump_dmat2x2; + + /// 2 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, aligned_lowp> aligned_lowp_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_highp> packed_highp_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_mediump> packed_mediump_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, float, packed_lowp> packed_lowp_mat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_highp> packed_highp_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_mediump> packed_mediump_dmat2x2; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 2, double, packed_lowp> packed_lowp_dmat2x2; + + // -- *mat2x3 -- + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_highp> aligned_highp_mat2x3; + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_mediump> aligned_mediump_mat2x3; + + /// 2 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, float, aligned_lowp> aligned_lowp_mat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_highp> aligned_highp_dmat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_mediump> aligned_mediump_dmat2x3; + + /// 2 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, double, aligned_lowp> aligned_lowp_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_highp> packed_highp_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_mediump> packed_mediump_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, float, packed_lowp> packed_lowp_mat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_highp> packed_highp_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_mediump> packed_mediump_dmat2x3; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 3, double, packed_lowp> packed_lowp_dmat2x3; + + // -- *mat2x4 -- + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_highp> aligned_highp_mat2x4; + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_mediump> aligned_mediump_mat2x4; + + /// 2 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, float, aligned_lowp> aligned_lowp_mat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_highp> aligned_highp_dmat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_mediump> aligned_mediump_dmat2x4; + + /// 2 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, double, aligned_lowp> aligned_lowp_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_highp> packed_highp_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_mediump> packed_mediump_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, float, packed_lowp> packed_lowp_mat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_highp> packed_highp_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_mediump> packed_mediump_dmat2x4; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<2, 4, double, packed_lowp> packed_lowp_dmat2x4; + + // -- *mat3x2 -- + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_highp> aligned_highp_mat3x2; + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_mediump> aligned_mediump_mat3x2; + + /// 3 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, float, aligned_lowp> aligned_lowp_mat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_highp> aligned_highp_dmat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_mediump> aligned_mediump_dmat3x2; + + /// 3 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, double, aligned_lowp> aligned_lowp_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_highp> packed_highp_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_mediump> packed_mediump_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, float, packed_lowp> packed_lowp_mat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_highp> packed_highp_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_mediump> packed_mediump_dmat3x2; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 2, double, packed_lowp> packed_lowp_dmat3x2; + + // -- *mat3x3 -- + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_highp> aligned_highp_mat3x3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_mediump> aligned_mediump_mat3x3; + + /// 3 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, aligned_lowp> aligned_lowp_mat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_highp> aligned_highp_dmat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_mediump> aligned_mediump_dmat3x3; + + /// 3 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, aligned_lowp> aligned_lowp_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_highp> packed_highp_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_mediump> packed_mediump_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, float, packed_lowp> packed_lowp_mat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_highp> packed_highp_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_mediump> packed_mediump_dmat3x3; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 3, double, packed_lowp> packed_lowp_dmat3x3; + + // -- *mat3x4 -- + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_highp> aligned_highp_mat3x4; + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_mediump> aligned_mediump_mat3x4; + + /// 3 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, float, aligned_lowp> aligned_lowp_mat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_highp> aligned_highp_dmat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_mediump> aligned_mediump_dmat3x4; + + /// 3 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, double, aligned_lowp> aligned_lowp_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_highp> packed_highp_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_mediump> packed_mediump_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, float, packed_lowp> packed_lowp_mat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_highp> packed_highp_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_mediump> packed_mediump_dmat3x4; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<3, 4, double, packed_lowp> packed_lowp_dmat3x4; + + // -- *mat4x2 -- + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_highp> aligned_highp_mat4x2; + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_mediump> aligned_mediump_mat4x2; + + /// 4 by 2 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, float, aligned_lowp> aligned_lowp_mat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_highp> aligned_highp_dmat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_mediump> aligned_mediump_dmat4x2; + + /// 4 by 2 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, double, aligned_lowp> aligned_lowp_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_highp> packed_highp_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_mediump> packed_mediump_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, float, packed_lowp> packed_lowp_mat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_highp> packed_highp_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_mediump> packed_mediump_dmat4x2; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 2, double, packed_lowp> packed_lowp_dmat4x2; + + // -- *mat4x3 -- + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_highp> aligned_highp_mat4x3; + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_mediump> aligned_mediump_mat4x3; + + /// 4 by 3 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, float, aligned_lowp> aligned_lowp_mat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_highp> aligned_highp_dmat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_mediump> aligned_mediump_dmat4x3; + + /// 4 by 3 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, double, aligned_lowp> aligned_lowp_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_highp> packed_highp_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_mediump> packed_mediump_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, float, packed_lowp> packed_lowp_mat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_highp> packed_highp_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_mediump> packed_mediump_dmat4x3; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 3, double, packed_lowp> packed_lowp_dmat4x3; + + // -- *mat4x4 -- + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_highp> aligned_highp_mat4x4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_mediump> aligned_mediump_mat4x4; + + /// 4 by 4 matrix aligned in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, aligned_lowp> aligned_lowp_mat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_highp> aligned_highp_dmat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_mediump> aligned_mediump_dmat4x4; + + /// 4 by 4 matrix aligned in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, aligned_lowp> aligned_lowp_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_highp> packed_highp_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_mediump> packed_mediump_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, float, packed_lowp> packed_lowp_mat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using high precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_highp> packed_highp_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using medium precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_mediump> packed_mediump_dmat4x4; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers using low precision arithmetic in term of ULPs. + typedef mat<4, 4, double, packed_lowp> packed_lowp_dmat4x4; + + // -- default -- + +#if(defined(GLM_PRECISION_LOWP_FLOAT)) + typedef aligned_lowp_vec1 aligned_vec1; + typedef aligned_lowp_vec2 aligned_vec2; + typedef aligned_lowp_vec3 aligned_vec3; + typedef aligned_lowp_vec4 aligned_vec4; + typedef packed_lowp_vec1 packed_vec1; + typedef packed_lowp_vec2 packed_vec2; + typedef packed_lowp_vec3 packed_vec3; + typedef packed_lowp_vec4 packed_vec4; + + typedef aligned_lowp_mat2 aligned_mat2; + typedef aligned_lowp_mat3 aligned_mat3; + typedef aligned_lowp_mat4 aligned_mat4; + typedef packed_lowp_mat2 packed_mat2; + typedef packed_lowp_mat3 packed_mat3; + typedef packed_lowp_mat4 packed_mat4; + + typedef aligned_lowp_mat2x2 aligned_mat2x2; + typedef aligned_lowp_mat2x3 aligned_mat2x3; + typedef aligned_lowp_mat2x4 aligned_mat2x4; + typedef aligned_lowp_mat3x2 aligned_mat3x2; + typedef aligned_lowp_mat3x3 aligned_mat3x3; + typedef aligned_lowp_mat3x4 aligned_mat3x4; + typedef aligned_lowp_mat4x2 aligned_mat4x2; + typedef aligned_lowp_mat4x3 aligned_mat4x3; + typedef aligned_lowp_mat4x4 aligned_mat4x4; + typedef packed_lowp_mat2x2 packed_mat2x2; + typedef packed_lowp_mat2x3 packed_mat2x3; + typedef packed_lowp_mat2x4 packed_mat2x4; + typedef packed_lowp_mat3x2 packed_mat3x2; + typedef packed_lowp_mat3x3 packed_mat3x3; + typedef packed_lowp_mat3x4 packed_mat3x4; + typedef packed_lowp_mat4x2 packed_mat4x2; + typedef packed_lowp_mat4x3 packed_mat4x3; + typedef packed_lowp_mat4x4 packed_mat4x4; +#elif(defined(GLM_PRECISION_MEDIUMP_FLOAT)) + typedef aligned_mediump_vec1 aligned_vec1; + typedef aligned_mediump_vec2 aligned_vec2; + typedef aligned_mediump_vec3 aligned_vec3; + typedef aligned_mediump_vec4 aligned_vec4; + typedef packed_mediump_vec1 packed_vec1; + typedef packed_mediump_vec2 packed_vec2; + typedef packed_mediump_vec3 packed_vec3; + typedef packed_mediump_vec4 packed_vec4; + + typedef aligned_mediump_mat2 aligned_mat2; + typedef aligned_mediump_mat3 aligned_mat3; + typedef aligned_mediump_mat4 aligned_mat4; + typedef packed_mediump_mat2 packed_mat2; + typedef packed_mediump_mat3 packed_mat3; + typedef packed_mediump_mat4 packed_mat4; + + typedef aligned_mediump_mat2x2 aligned_mat2x2; + typedef aligned_mediump_mat2x3 aligned_mat2x3; + typedef aligned_mediump_mat2x4 aligned_mat2x4; + typedef aligned_mediump_mat3x2 aligned_mat3x2; + typedef aligned_mediump_mat3x3 aligned_mat3x3; + typedef aligned_mediump_mat3x4 aligned_mat3x4; + typedef aligned_mediump_mat4x2 aligned_mat4x2; + typedef aligned_mediump_mat4x3 aligned_mat4x3; + typedef aligned_mediump_mat4x4 aligned_mat4x4; + typedef packed_mediump_mat2x2 packed_mat2x2; + typedef packed_mediump_mat2x3 packed_mat2x3; + typedef packed_mediump_mat2x4 packed_mat2x4; + typedef packed_mediump_mat3x2 packed_mat3x2; + typedef packed_mediump_mat3x3 packed_mat3x3; + typedef packed_mediump_mat3x4 packed_mat3x4; + typedef packed_mediump_mat4x2 packed_mat4x2; + typedef packed_mediump_mat4x3 packed_mat4x3; + typedef packed_mediump_mat4x4 packed_mat4x4; +#else //defined(GLM_PRECISION_HIGHP_FLOAT) + /// 1 component vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec1 aligned_vec1; + + /// 2 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec2 aligned_vec2; + + /// 3 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec3 aligned_vec3; + + /// 4 components vector aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_vec4 aligned_vec4; + + /// 1 component vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec1 packed_vec1; + + /// 2 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec2 packed_vec2; + + /// 3 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec3 packed_vec3; + + /// 4 components vector tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_vec4 packed_vec4; + + /// 2 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2 aligned_mat2; + + /// 3 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3 aligned_mat3; + + /// 4 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4 aligned_mat4; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2 packed_mat2; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3 packed_mat3; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4 packed_mat4; + + /// 2 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x2 aligned_mat2x2; + + /// 2 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x3 aligned_mat2x3; + + /// 2 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat2x4 aligned_mat2x4; + + /// 3 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x2 aligned_mat3x2; + + /// 3 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x3 aligned_mat3x3; + + /// 3 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat3x4 aligned_mat3x4; + + /// 4 by 2 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x2 aligned_mat4x2; + + /// 4 by 3 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x3 aligned_mat4x3; + + /// 4 by 4 matrix tightly aligned in memory of single-precision floating-point numbers. + typedef aligned_highp_mat4x4 aligned_mat4x4; + + /// 2 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x2 packed_mat2x2; + + /// 2 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x3 packed_mat2x3; + + /// 2 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat2x4 packed_mat2x4; + + /// 3 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x2 packed_mat3x2; + + /// 3 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x3 packed_mat3x3; + + /// 3 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat3x4 packed_mat3x4; + + /// 4 by 2 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x2 packed_mat4x2; + + /// 4 by 3 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x3 packed_mat4x3; + + /// 4 by 4 matrix tightly packed in memory of single-precision floating-point numbers. + typedef packed_highp_mat4x4 packed_mat4x4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef aligned_lowp_dvec1 aligned_dvec1; + typedef aligned_lowp_dvec2 aligned_dvec2; + typedef aligned_lowp_dvec3 aligned_dvec3; + typedef aligned_lowp_dvec4 aligned_dvec4; + typedef packed_lowp_dvec1 packed_dvec1; + typedef packed_lowp_dvec2 packed_dvec2; + typedef packed_lowp_dvec3 packed_dvec3; + typedef packed_lowp_dvec4 packed_dvec4; + + typedef aligned_lowp_dmat2 aligned_dmat2; + typedef aligned_lowp_dmat3 aligned_dmat3; + typedef aligned_lowp_dmat4 aligned_dmat4; + typedef packed_lowp_dmat2 packed_dmat2; + typedef packed_lowp_dmat3 packed_dmat3; + typedef packed_lowp_dmat4 packed_dmat4; + + typedef aligned_lowp_dmat2x2 aligned_dmat2x2; + typedef aligned_lowp_dmat2x3 aligned_dmat2x3; + typedef aligned_lowp_dmat2x4 aligned_dmat2x4; + typedef aligned_lowp_dmat3x2 aligned_dmat3x2; + typedef aligned_lowp_dmat3x3 aligned_dmat3x3; + typedef aligned_lowp_dmat3x4 aligned_dmat3x4; + typedef aligned_lowp_dmat4x2 aligned_dmat4x2; + typedef aligned_lowp_dmat4x3 aligned_dmat4x3; + typedef aligned_lowp_dmat4x4 aligned_dmat4x4; + typedef packed_lowp_dmat2x2 packed_dmat2x2; + typedef packed_lowp_dmat2x3 packed_dmat2x3; + typedef packed_lowp_dmat2x4 packed_dmat2x4; + typedef packed_lowp_dmat3x2 packed_dmat3x2; + typedef packed_lowp_dmat3x3 packed_dmat3x3; + typedef packed_lowp_dmat3x4 packed_dmat3x4; + typedef packed_lowp_dmat4x2 packed_dmat4x2; + typedef packed_lowp_dmat4x3 packed_dmat4x3; + typedef packed_lowp_dmat4x4 packed_dmat4x4; +#elif(defined(GLM_PRECISION_MEDIUMP_DOUBLE)) + typedef aligned_mediump_dvec1 aligned_dvec1; + typedef aligned_mediump_dvec2 aligned_dvec2; + typedef aligned_mediump_dvec3 aligned_dvec3; + typedef aligned_mediump_dvec4 aligned_dvec4; + typedef packed_mediump_dvec1 packed_dvec1; + typedef packed_mediump_dvec2 packed_dvec2; + typedef packed_mediump_dvec3 packed_dvec3; + typedef packed_mediump_dvec4 packed_dvec4; + + typedef aligned_mediump_dmat2 aligned_dmat2; + typedef aligned_mediump_dmat3 aligned_dmat3; + typedef aligned_mediump_dmat4 aligned_dmat4; + typedef packed_mediump_dmat2 packed_dmat2; + typedef packed_mediump_dmat3 packed_dmat3; + typedef packed_mediump_dmat4 packed_dmat4; + + typedef aligned_mediump_dmat2x2 aligned_dmat2x2; + typedef aligned_mediump_dmat2x3 aligned_dmat2x3; + typedef aligned_mediump_dmat2x4 aligned_dmat2x4; + typedef aligned_mediump_dmat3x2 aligned_dmat3x2; + typedef aligned_mediump_dmat3x3 aligned_dmat3x3; + typedef aligned_mediump_dmat3x4 aligned_dmat3x4; + typedef aligned_mediump_dmat4x2 aligned_dmat4x2; + typedef aligned_mediump_dmat4x3 aligned_dmat4x3; + typedef aligned_mediump_dmat4x4 aligned_dmat4x4; + typedef packed_mediump_dmat2x2 packed_dmat2x2; + typedef packed_mediump_dmat2x3 packed_dmat2x3; + typedef packed_mediump_dmat2x4 packed_dmat2x4; + typedef packed_mediump_dmat3x2 packed_dmat3x2; + typedef packed_mediump_dmat3x3 packed_dmat3x3; + typedef packed_mediump_dmat3x4 packed_dmat3x4; + typedef packed_mediump_dmat4x2 packed_dmat4x2; + typedef packed_mediump_dmat4x3 packed_dmat4x3; + typedef packed_mediump_dmat4x4 packed_dmat4x4; +#else //defined(GLM_PRECISION_HIGHP_DOUBLE) + /// 1 component vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec1 aligned_dvec1; + + /// 2 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec2 aligned_dvec2; + + /// 3 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec3 aligned_dvec3; + + /// 4 components vector aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dvec4 aligned_dvec4; + + /// 1 component vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec1 packed_dvec1; + + /// 2 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec2 packed_dvec2; + + /// 3 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec3 packed_dvec3; + + /// 4 components vector tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dvec4 packed_dvec4; + + /// 2 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2 aligned_dmat2; + + /// 3 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3 aligned_dmat3; + + /// 4 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4 aligned_dmat4; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2 packed_dmat2; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3 packed_dmat3; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4 packed_dmat4; + + /// 2 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x2 aligned_dmat2x2; + + /// 2 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x3 aligned_dmat2x3; + + /// 2 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat2x4 aligned_dmat2x4; + + /// 3 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x2 aligned_dmat3x2; + + /// 3 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x3 aligned_dmat3x3; + + /// 3 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat3x4 aligned_dmat3x4; + + /// 4 by 2 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x2 aligned_dmat4x2; + + /// 4 by 3 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x3 aligned_dmat4x3; + + /// 4 by 4 matrix tightly aligned in memory of double-precision floating-point numbers. + typedef aligned_highp_dmat4x4 aligned_dmat4x4; + + /// 2 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x2 packed_dmat2x2; + + /// 2 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x3 packed_dmat2x3; + + /// 2 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat2x4 packed_dmat2x4; + + /// 3 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x2 packed_dmat3x2; + + /// 3 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x3 packed_dmat3x3; + + /// 3 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat3x4 packed_dmat3x4; + + /// 4 by 2 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x2 packed_dmat4x2; + + /// 4 by 3 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x3 packed_dmat4x3; + + /// 4 by 4 matrix tightly packed in memory of double-precision floating-point numbers. + typedef packed_highp_dmat4x4 packed_dmat4x4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_INT)) + typedef aligned_lowp_ivec1 aligned_ivec1; + typedef aligned_lowp_ivec2 aligned_ivec2; + typedef aligned_lowp_ivec3 aligned_ivec3; + typedef aligned_lowp_ivec4 aligned_ivec4; +#elif(defined(GLM_PRECISION_MEDIUMP_INT)) + typedef aligned_mediump_ivec1 aligned_ivec1; + typedef aligned_mediump_ivec2 aligned_ivec2; + typedef aligned_mediump_ivec3 aligned_ivec3; + typedef aligned_mediump_ivec4 aligned_ivec4; +#else //defined(GLM_PRECISION_HIGHP_INT) + /// 1 component vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec1 aligned_ivec1; + + /// 2 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec2 aligned_ivec2; + + /// 3 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec3 aligned_ivec3; + + /// 4 components vector aligned in memory of signed integer numbers. + typedef aligned_highp_ivec4 aligned_ivec4; + + /// 1 component vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec1 packed_ivec1; + + /// 2 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec2 packed_ivec2; + + /// 3 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec3 packed_ivec3; + + /// 4 components vector tightly packed in memory of signed integer numbers. + typedef packed_highp_ivec4 packed_ivec4; +#endif//GLM_PRECISION + + // -- Unsigned integer definition -- + +#if(defined(GLM_PRECISION_LOWP_UINT)) + typedef aligned_lowp_uvec1 aligned_uvec1; + typedef aligned_lowp_uvec2 aligned_uvec2; + typedef aligned_lowp_uvec3 aligned_uvec3; + typedef aligned_lowp_uvec4 aligned_uvec4; +#elif(defined(GLM_PRECISION_MEDIUMP_UINT)) + typedef aligned_mediump_uvec1 aligned_uvec1; + typedef aligned_mediump_uvec2 aligned_uvec2; + typedef aligned_mediump_uvec3 aligned_uvec3; + typedef aligned_mediump_uvec4 aligned_uvec4; +#else //defined(GLM_PRECISION_HIGHP_UINT) + /// 1 component vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec1 aligned_uvec1; + + /// 2 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec2 aligned_uvec2; + + /// 3 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec3 aligned_uvec3; + + /// 4 components vector aligned in memory of unsigned integer numbers. + typedef aligned_highp_uvec4 aligned_uvec4; + + /// 1 component vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec1 packed_uvec1; + + /// 2 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec2 packed_uvec2; + + /// 3 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec3 packed_uvec3; + + /// 4 components vector tightly packed in memory of unsigned integer numbers. + typedef packed_highp_uvec4 packed_uvec4; +#endif//GLM_PRECISION + +#if(defined(GLM_PRECISION_LOWP_BOOL)) + typedef aligned_lowp_bvec1 aligned_bvec1; + typedef aligned_lowp_bvec2 aligned_bvec2; + typedef aligned_lowp_bvec3 aligned_bvec3; + typedef aligned_lowp_bvec4 aligned_bvec4; +#elif(defined(GLM_PRECISION_MEDIUMP_BOOL)) + typedef aligned_mediump_bvec1 aligned_bvec1; + typedef aligned_mediump_bvec2 aligned_bvec2; + typedef aligned_mediump_bvec3 aligned_bvec3; + typedef aligned_mediump_bvec4 aligned_bvec4; +#else //defined(GLM_PRECISION_HIGHP_BOOL) + /// 1 component vector aligned in memory of bool values. + typedef aligned_highp_bvec1 aligned_bvec1; + + /// 2 components vector aligned in memory of bool values. + typedef aligned_highp_bvec2 aligned_bvec2; + + /// 3 components vector aligned in memory of bool values. + typedef aligned_highp_bvec3 aligned_bvec3; + + /// 4 components vector aligned in memory of bool values. + typedef aligned_highp_bvec4 aligned_bvec4; + + /// 1 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec1 packed_bvec1; + + /// 2 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec2 packed_bvec2; + + /// 3 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec3 packed_bvec3; + + /// 4 components vector tightly packed in memory of bool values. + typedef packed_highp_bvec4 packed_bvec4; +#endif//GLM_PRECISION + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/gtc/type_precision.hpp b/thirdparty/glm/glm/gtc/type_precision.hpp new file mode 100644 index 0000000..775e2f4 --- /dev/null +++ b/thirdparty/glm/glm/gtc/type_precision.hpp @@ -0,0 +1,2094 @@ +/// @ref gtc_type_precision +/// @file glm/gtc/type_precision.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_type_precision GLM_GTC_type_precision +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Defines specific C++-based qualifier types. + +#pragma once + +// Dependency: +#include "../gtc/quaternion.hpp" +#include "../gtc/vec1.hpp" +#include "../ext/vector_int1_sized.hpp" +#include "../ext/vector_int2_sized.hpp" +#include "../ext/vector_int3_sized.hpp" +#include "../ext/vector_int4_sized.hpp" +#include "../ext/scalar_int_sized.hpp" +#include "../ext/vector_uint1_sized.hpp" +#include "../ext/vector_uint2_sized.hpp" +#include "../ext/vector_uint3_sized.hpp" +#include "../ext/vector_uint4_sized.hpp" +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/type_vec2.hpp" +#include "../detail/type_vec3.hpp" +#include "../detail/type_vec4.hpp" +#include "../detail/type_mat2x2.hpp" +#include "../detail/type_mat2x3.hpp" +#include "../detail/type_mat2x4.hpp" +#include "../detail/type_mat3x2.hpp" +#include "../detail/type_mat3x3.hpp" +#include "../detail/type_mat3x4.hpp" +#include "../detail/type_mat4x2.hpp" +#include "../detail/type_mat4x3.hpp" +#include "../detail/type_mat4x4.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_precision extension included") +#endif + +namespace glm +{ + /////////////////////////// + // Signed int vector types + + /// @addtogroup gtc_type_precision + /// @{ + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_int8; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_int16; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_int32; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_int64; + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_int8_t; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_int16_t; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_int32_t; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_int64_t; + + /// Low qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 lowp_i8; + + /// Low qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 lowp_i16; + + /// Low qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 lowp_i32; + + /// Low qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 lowp_i64; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_int8; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_int16; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_int32; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_int64; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_int8_t; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_int16_t; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_int32_t; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_int64_t; + + /// Medium qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 mediump_i8; + + /// Medium qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 mediump_i16; + + /// Medium qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 mediump_i32; + + /// Medium qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 mediump_i64; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_int8; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_int16; + + /// High qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_int32; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_int64; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_int8_t; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_int16_t; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_int32_t; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_int64_t; + + /// High qualifier 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 highp_i8; + + /// High qualifier 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 highp_i16; + + /// High qualifier 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 highp_i32; + + /// High qualifier 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 highp_i64; + + +#if GLM_HAS_EXTENDED_INTEGER_TYPE + using std::int8_t; + using std::int16_t; + using std::int32_t; + using std::int64_t; +#else + /// 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 int8_t; + + /// 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 int16_t; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 int32_t; + + /// 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 int64_t; +#endif + + /// 8 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int8 i8; + + /// 16 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int16 i16; + + /// 32 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int32 i32; + + /// 64 bit signed integer type. + /// @see gtc_type_precision + typedef detail::int64 i64; + + ///////////////////////////// + // Unsigned int vector types + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_uint8; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_uint16; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_uint32; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_uint64; + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_uint8_t; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_uint16_t; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_uint32_t; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_uint64_t; + + /// Low qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 lowp_u8; + + /// Low qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 lowp_u16; + + /// Low qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 lowp_u32; + + /// Low qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 lowp_u64; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_uint8; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_uint16; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_uint32; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_uint64; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_uint8_t; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_uint16_t; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_uint32_t; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_uint64_t; + + /// Medium qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 mediump_u8; + + /// Medium qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 mediump_u16; + + /// Medium qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 mediump_u32; + + /// Medium qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 mediump_u64; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_uint8; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_uint16; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_uint32; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_uint64; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_uint8_t; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_uint16_t; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_uint32_t; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_uint64_t; + + /// High qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 highp_u8; + + /// High qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 highp_u16; + + /// High qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 highp_u32; + + /// High qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 highp_u64; + +#if GLM_HAS_EXTENDED_INTEGER_TYPE + using std::uint8_t; + using std::uint16_t; + using std::uint32_t; + using std::uint64_t; +#else + /// Default qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 uint8_t; + + /// Default qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 uint16_t; + + /// Default qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 uint32_t; + + /// Default qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 uint64_t; +#endif + + /// Default qualifier 8 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint8 u8; + + /// Default qualifier 16 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint16 u16; + + /// Default qualifier 32 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint32 u32; + + /// Default qualifier 64 bit unsigned integer type. + /// @see gtc_type_precision + typedef detail::uint64 u64; + + + + + + ////////////////////// + // Float vector types + + /// Single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float float32; + + /// Double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef double float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_float32_t; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_float64_t; + + /// Low 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 lowp_f32; + + /// Low 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 lowp_f64; + + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_float32; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_float64; + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_float32_t; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_float64_t; + + /// Medium 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 mediump_f32; + + /// Medium 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 mediump_f64; + + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_float32; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_float64; + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_float32_t; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_float64_t; + + /// High 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 highp_f32; + + /// High 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 highp_f64; + + +#if(defined(GLM_PRECISION_LOWP_FLOAT)) + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_float32_t float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_float64_t float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_f32 f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef lowp_f64 f64; + +#elif(defined(GLM_PRECISION_MEDIUMP_FLOAT)) + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float32 float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float64 float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float32 f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef mediump_float64 f64; + +#else//(defined(GLM_PRECISION_HIGHP_FLOAT)) + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float32_t float32_t; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float64_t float64_t; + + /// Default 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float32_t f32; + + /// Default 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef highp_float64_t f64; +#endif + + + /// Low single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, lowp> lowp_fvec1; + + /// Low single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, lowp> lowp_fvec2; + + /// Low single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, lowp> lowp_fvec3; + + /// Low single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, lowp> lowp_fvec4; + + + /// Medium single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, mediump> mediump_fvec1; + + /// Medium Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, mediump> mediump_fvec2; + + /// Medium Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, mediump> mediump_fvec3; + + /// Medium Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, mediump> mediump_fvec4; + + + /// High single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, highp> highp_fvec1; + + /// High Single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, float, highp> highp_fvec2; + + /// High Single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, float, highp> highp_fvec3; + + /// High Single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, float, highp> highp_fvec4; + + + /// Low single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, lowp> lowp_f32vec1; + + /// Low single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, f32, lowp> lowp_f32vec2; + + /// Low single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, f32, lowp> lowp_f32vec3; + + /// Low single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, f32, lowp> lowp_f32vec4; + + /// Medium single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, mediump> mediump_f32vec1; + + /// Medium single-qualifier floating-point vector of 2 components. + /// @see core_precision + typedef vec<2, f32, mediump> mediump_f32vec2; + + /// Medium single-qualifier floating-point vector of 3 components. + /// @see core_precision + typedef vec<3, f32, mediump> mediump_f32vec3; + + /// Medium single-qualifier floating-point vector of 4 components. + /// @see core_precision + typedef vec<4, f32, mediump> mediump_f32vec4; + + /// High single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, highp> highp_f32vec1; + + /// High single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f32, highp> highp_f32vec2; + + /// High single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f32, highp> highp_f32vec3; + + /// High single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f32, highp> highp_f32vec4; + + + /// Low double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, lowp> lowp_f64vec1; + + /// Low double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, lowp> lowp_f64vec2; + + /// Low double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, lowp> lowp_f64vec3; + + /// Low double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, lowp> lowp_f64vec4; + + /// Medium double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, mediump> mediump_f64vec1; + + /// Medium double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, mediump> mediump_f64vec2; + + /// Medium double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, mediump> mediump_f64vec3; + + /// Medium double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, mediump> mediump_f64vec4; + + /// High double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, highp> highp_f64vec1; + + /// High double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, highp> highp_f64vec2; + + /// High double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, highp> highp_f64vec3; + + /// High double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, highp> highp_f64vec4; + + + + ////////////////////// + // Float matrix types + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_f32 lowp_fmat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, lowp> lowp_fmat2x2; + + /// Low single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, lowp> lowp_fmat2x3; + + /// Low single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, lowp> lowp_fmat2x4; + + /// Low single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, lowp> lowp_fmat3x2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, lowp> lowp_fmat3x3; + + /// Low single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, lowp> lowp_fmat3x4; + + /// Low single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, lowp> lowp_fmat4x2; + + /// Low single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, lowp> lowp_fmat4x3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, lowp> lowp_fmat4x4; + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_fmat1x1 lowp_fmat1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_fmat2x2 lowp_fmat2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_fmat3x3 lowp_fmat3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_fmat4x4 lowp_fmat4; + + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_f32 mediump_fmat1x1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, mediump> mediump_fmat2x2; + + /// Medium single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, mediump> mediump_fmat2x3; + + /// Medium single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, mediump> mediump_fmat2x4; + + /// Medium single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, mediump> mediump_fmat3x2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, mediump> mediump_fmat3x3; + + /// Medium single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, mediump> mediump_fmat3x4; + + /// Medium single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, mediump> mediump_fmat4x2; + + /// Medium single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, mediump> mediump_fmat4x3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, mediump> mediump_fmat4x4; + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_fmat1x1 mediump_fmat1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_fmat2x2 mediump_fmat2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_fmat3x3 mediump_fmat3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_fmat4x4 mediump_fmat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_f32 highp_fmat1x1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, highp> highp_fmat2x2; + + /// High single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, highp> highp_fmat2x3; + + /// High single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, highp> highp_fmat2x4; + + /// High single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, highp> highp_fmat3x2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, highp> highp_fmat3x3; + + /// High single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, highp> highp_fmat3x4; + + /// High single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, highp> highp_fmat4x2; + + /// High single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, highp> highp_fmat4x3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, highp> highp_fmat4x4; + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_fmat1x1 highp_fmat1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_fmat2x2 highp_fmat2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_fmat3x3 highp_fmat3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_fmat4x4 highp_fmat4; + + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 lowp_f32mat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, lowp> lowp_f32mat2x2; + + /// Low single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, lowp> lowp_f32mat2x3; + + /// Low single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, lowp> lowp_f32mat2x4; + + /// Low single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, lowp> lowp_f32mat3x2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, lowp> lowp_f32mat3x3; + + /// Low single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, lowp> lowp_f32mat3x4; + + /// Low single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, lowp> lowp_f32mat4x2; + + /// Low single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, lowp> lowp_f32mat4x3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, lowp> lowp_f32mat4x4; + + /// Low single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 lowp_f32mat1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat2x2 lowp_f32mat2; + + /// Low single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat3x3 lowp_f32mat3; + + /// Low single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_f32mat4x4 lowp_f32mat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 mediump_f32mat1x1; + + /// Low single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, mediump> mediump_f32mat2x2; + + /// Medium single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, mediump> mediump_f32mat2x3; + + /// Medium single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, mediump> mediump_f32mat2x4; + + /// Medium single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, mediump> mediump_f32mat3x2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, mediump> mediump_f32mat3x3; + + /// Medium single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, mediump> mediump_f32mat3x4; + + /// Medium single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, mediump> mediump_f32mat4x2; + + /// Medium single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, mediump> mediump_f32mat4x3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, mediump> mediump_f32mat4x4; + + /// Medium single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// Medium single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat2x2 mediump_f32mat2; + + /// Medium single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat3x3 mediump_f32mat3; + + /// Medium single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_f32mat4x4 mediump_f32mat4; + + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 highp_f32mat1x1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, highp> highp_f32mat2x2; + + /// High single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, highp> highp_f32mat2x3; + + /// High single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, highp> highp_f32mat2x4; + + /// High single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, highp> highp_f32mat3x2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, highp> highp_f32mat3x3; + + /// High single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, highp> highp_f32mat3x4; + + /// High single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, highp> highp_f32mat4x2; + + /// High single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, highp> highp_f32mat4x3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, highp> highp_f32mat4x4; + + /// High single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// High single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_f32mat2x2 highp_f32mat2; + + /// High single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_f32mat3x3 highp_f32mat3; + + /// High single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_f32mat4x4 highp_f32mat4; + + + /// Low double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 lowp_f64mat1x1; + + /// Low double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, lowp> lowp_f64mat2x2; + + /// Low double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, lowp> lowp_f64mat2x3; + + /// Low double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, lowp> lowp_f64mat2x4; + + /// Low double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, lowp> lowp_f64mat3x2; + + /// Low double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, lowp> lowp_f64mat3x3; + + /// Low double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, lowp> lowp_f64mat3x4; + + /// Low double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, lowp> lowp_f64mat4x2; + + /// Low double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, lowp> lowp_f64mat4x3; + + /// Low double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, lowp> lowp_f64mat4x4; + + /// Low double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef lowp_f64mat1x1 lowp_f64mat1; + + /// Low double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat2x2 lowp_f64mat2; + + /// Low double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat3x3 lowp_f64mat3; + + /// Low double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef lowp_f64mat4x4 lowp_f64mat4; + + + /// Medium double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 Highp_f64mat1x1; + + /// Medium double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, mediump> mediump_f64mat2x2; + + /// Medium double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, mediump> mediump_f64mat2x3; + + /// Medium double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, mediump> mediump_f64mat2x4; + + /// Medium double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, mediump> mediump_f64mat3x2; + + /// Medium double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, mediump> mediump_f64mat3x3; + + /// Medium double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, mediump> mediump_f64mat3x4; + + /// Medium double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, mediump> mediump_f64mat4x2; + + /// Medium double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, mediump> mediump_f64mat4x3; + + /// Medium double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, mediump> mediump_f64mat4x4; + + /// Medium double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef mediump_f64mat1x1 mediump_f64mat1; + + /// Medium double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat2x2 mediump_f64mat2; + + /// Medium double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat3x3 mediump_f64mat3; + + /// Medium double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mediump_f64mat4x4 mediump_f64mat4; + + /// High double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 highp_f64mat1x1; + + /// High double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, highp> highp_f64mat2x2; + + /// High double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, highp> highp_f64mat2x3; + + /// High double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, highp> highp_f64mat2x4; + + /// High double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, highp> highp_f64mat3x2; + + /// High double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, highp> highp_f64mat3x3; + + /// High double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, highp> highp_f64mat3x4; + + /// High double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, highp> highp_f64mat4x2; + + /// High double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, highp> highp_f64mat4x3; + + /// High double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, highp> highp_f64mat4x4; + + /// High double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef highp_f64mat1x1 highp_f64mat1; + + /// High double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef highp_f64mat2x2 highp_f64mat2; + + /// High double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef highp_f64mat3x3 highp_f64mat3; + + /// High double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef highp_f64mat4x4 highp_f64mat4; + + + ///////////////////////////// + // Signed int vector types + + /// Low qualifier signed integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, int, lowp> lowp_ivec1; + + /// Low qualifier signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, int, lowp> lowp_ivec2; + + /// Low qualifier signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, int, lowp> lowp_ivec3; + + /// Low qualifier signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, int, lowp> lowp_ivec4; + + + /// Medium qualifier signed integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, int, mediump> mediump_ivec1; + + /// Medium qualifier signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, int, mediump> mediump_ivec2; + + /// Medium qualifier signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, int, mediump> mediump_ivec3; + + /// Medium qualifier signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, int, mediump> mediump_ivec4; + + + /// High qualifier signed integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, int, highp> highp_ivec1; + + /// High qualifier signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, int, highp> highp_ivec2; + + /// High qualifier signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, int, highp> highp_ivec3; + + /// High qualifier signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, int, highp> highp_ivec4; + + + /// Low qualifier 8 bit signed integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, i8, lowp> lowp_i8vec1; + + /// Low qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, lowp> lowp_i8vec2; + + /// Low qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, lowp> lowp_i8vec3; + + /// Low qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, lowp> lowp_i8vec4; + + + /// Medium qualifier 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, mediump> mediump_i8vec1; + + /// Medium qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, mediump> mediump_i8vec2; + + /// Medium qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, mediump> mediump_i8vec3; + + /// Medium qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, mediump> mediump_i8vec4; + + + /// High qualifier 8 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i8, highp> highp_i8vec1; + + /// High qualifier 8 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i8, highp> highp_i8vec2; + + /// High qualifier 8 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i8, highp> highp_i8vec3; + + /// High qualifier 8 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i8, highp> highp_i8vec4; + + + /// Low qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, lowp> lowp_i16vec1; + + /// Low qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, lowp> lowp_i16vec2; + + /// Low qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, lowp> lowp_i16vec3; + + /// Low qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, lowp> lowp_i16vec4; + + + /// Medium qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, mediump> mediump_i16vec1; + + /// Medium qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, mediump> mediump_i16vec2; + + /// Medium qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, mediump> mediump_i16vec3; + + /// Medium qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, mediump> mediump_i16vec4; + + + /// High qualifier 16 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i16, highp> highp_i16vec1; + + /// High qualifier 16 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i16, highp> highp_i16vec2; + + /// High qualifier 16 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i16, highp> highp_i16vec3; + + /// High qualifier 16 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i16, highp> highp_i16vec4; + + + /// Low qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, lowp> lowp_i32vec1; + + /// Low qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, lowp> lowp_i32vec2; + + /// Low qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, lowp> lowp_i32vec3; + + /// Low qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, lowp> lowp_i32vec4; + + + /// Medium qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, mediump> mediump_i32vec1; + + /// Medium qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, mediump> mediump_i32vec2; + + /// Medium qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, mediump> mediump_i32vec3; + + /// Medium qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, mediump> mediump_i32vec4; + + + /// High qualifier 32 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i32, highp> highp_i32vec1; + + /// High qualifier 32 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i32, highp> highp_i32vec2; + + /// High qualifier 32 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i32, highp> highp_i32vec3; + + /// High qualifier 32 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i32, highp> highp_i32vec4; + + + /// Low qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, lowp> lowp_i64vec1; + + /// Low qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, lowp> lowp_i64vec2; + + /// Low qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, lowp> lowp_i64vec3; + + /// Low qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, lowp> lowp_i64vec4; + + + /// Medium qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, mediump> mediump_i64vec1; + + /// Medium qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, mediump> mediump_i64vec2; + + /// Medium qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, mediump> mediump_i64vec3; + + /// Medium qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, mediump> mediump_i64vec4; + + + /// High qualifier 64 bit signed integer scalar type. + /// @see gtc_type_precision + typedef vec<1, i64, highp> highp_i64vec1; + + /// High qualifier 64 bit signed integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, i64, highp> highp_i64vec2; + + /// High qualifier 64 bit signed integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, i64, highp> highp_i64vec3; + + /// High qualifier 64 bit signed integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, i64, highp> highp_i64vec4; + + + ///////////////////////////// + // Unsigned int vector types + + /// Low qualifier unsigned integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, uint, lowp> lowp_uvec1; + + /// Low qualifier unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, uint, lowp> lowp_uvec2; + + /// Low qualifier unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, uint, lowp> lowp_uvec3; + + /// Low qualifier unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, uint, lowp> lowp_uvec4; + + + /// Medium qualifier unsigned integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, uint, mediump> mediump_uvec1; + + /// Medium qualifier unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, uint, mediump> mediump_uvec2; + + /// Medium qualifier unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, uint, mediump> mediump_uvec3; + + /// Medium qualifier unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, uint, mediump> mediump_uvec4; + + + /// High qualifier unsigned integer vector of 1 component type. + /// @see gtc_type_precision + typedef vec<1, uint, highp> highp_uvec1; + + /// High qualifier unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, uint, highp> highp_uvec2; + + /// High qualifier unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, uint, highp> highp_uvec3; + + /// High qualifier unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, uint, highp> highp_uvec4; + + + /// Low qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, lowp> lowp_u8vec1; + + /// Low qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, lowp> lowp_u8vec2; + + /// Low qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, lowp> lowp_u8vec3; + + /// Low qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, lowp> lowp_u8vec4; + + + /// Medium qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, mediump> mediump_u8vec1; + + /// Medium qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, mediump> mediump_u8vec2; + + /// Medium qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, mediump> mediump_u8vec3; + + /// Medium qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, mediump> mediump_u8vec4; + + + /// High qualifier 8 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u8, highp> highp_u8vec1; + + /// High qualifier 8 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u8, highp> highp_u8vec2; + + /// High qualifier 8 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u8, highp> highp_u8vec3; + + /// High qualifier 8 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u8, highp> highp_u8vec4; + + + /// Low qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, lowp> lowp_u16vec1; + + /// Low qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, lowp> lowp_u16vec2; + + /// Low qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, lowp> lowp_u16vec3; + + /// Low qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, lowp> lowp_u16vec4; + + + /// Medium qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, mediump> mediump_u16vec1; + + /// Medium qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, mediump> mediump_u16vec2; + + /// Medium qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, mediump> mediump_u16vec3; + + /// Medium qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, mediump> mediump_u16vec4; + + + /// High qualifier 16 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u16, highp> highp_u16vec1; + + /// High qualifier 16 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u16, highp> highp_u16vec2; + + /// High qualifier 16 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u16, highp> highp_u16vec3; + + /// High qualifier 16 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u16, highp> highp_u16vec4; + + + /// Low qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, lowp> lowp_u32vec1; + + /// Low qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, lowp> lowp_u32vec2; + + /// Low qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, lowp> lowp_u32vec3; + + /// Low qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, lowp> lowp_u32vec4; + + + /// Medium qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, mediump> mediump_u32vec1; + + /// Medium qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, mediump> mediump_u32vec2; + + /// Medium qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, mediump> mediump_u32vec3; + + /// Medium qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, mediump> mediump_u32vec4; + + + /// High qualifier 32 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u32, highp> highp_u32vec1; + + /// High qualifier 32 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u32, highp> highp_u32vec2; + + /// High qualifier 32 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u32, highp> highp_u32vec3; + + /// High qualifier 32 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u32, highp> highp_u32vec4; + + + /// Low qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, lowp> lowp_u64vec1; + + /// Low qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, lowp> lowp_u64vec2; + + /// Low qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, lowp> lowp_u64vec3; + + /// Low qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, lowp> lowp_u64vec4; + + + /// Medium qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, mediump> mediump_u64vec1; + + /// Medium qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, mediump> mediump_u64vec2; + + /// Medium qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, mediump> mediump_u64vec3; + + /// Medium qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, mediump> mediump_u64vec4; + + + /// High qualifier 64 bit unsigned integer scalar type. + /// @see gtc_type_precision + typedef vec<1, u64, highp> highp_u64vec1; + + /// High qualifier 64 bit unsigned integer vector of 2 components type. + /// @see gtc_type_precision + typedef vec<2, u64, highp> highp_u64vec2; + + /// High qualifier 64 bit unsigned integer vector of 3 components type. + /// @see gtc_type_precision + typedef vec<3, u64, highp> highp_u64vec3; + + /// High qualifier 64 bit unsigned integer vector of 4 components type. + /// @see gtc_type_precision + typedef vec<4, u64, highp> highp_u64vec4; + + + ////////////////////// + // Float vector types + + /// 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 float32_t; + + /// 32 bit single-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float32 f32; + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 float64_t; + + /// 64 bit double-qualifier floating-point scalar. + /// @see gtc_type_precision + typedef float64 f64; +# endif//GLM_FORCE_SINGLE_ONLY + + /// Single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, float, defaultp> fvec1; + + /// Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, float, defaultp> fvec2; + + /// Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, float, defaultp> fvec3; + + /// Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, float, defaultp> fvec4; + + + /// Single-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f32, defaultp> f32vec1; + + /// Single-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f32, defaultp> f32vec2; + + /// Single-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f32, defaultp> f32vec3; + + /// Single-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f32, defaultp> f32vec4; + +# ifndef GLM_FORCE_SINGLE_ONLY + /// Double-qualifier floating-point vector of 1 component. + /// @see gtc_type_precision + typedef vec<1, f64, defaultp> f64vec1; + + /// Double-qualifier floating-point vector of 2 components. + /// @see gtc_type_precision + typedef vec<2, f64, defaultp> f64vec2; + + /// Double-qualifier floating-point vector of 3 components. + /// @see gtc_type_precision + typedef vec<3, f64, defaultp> f64vec3; + + /// Double-qualifier floating-point vector of 4 components. + /// @see gtc_type_precision + typedef vec<4, f64, defaultp> f64vec4; +# endif//GLM_FORCE_SINGLE_ONLY + + + ////////////////////// + // Float matrix types + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 fmat1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> fmat2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> fmat3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> fmat4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 fmat1x1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> fmat2x2; + + /// Single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, defaultp> fmat2x3; + + /// Single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, defaultp> fmat2x4; + + /// Single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, defaultp> fmat3x2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> fmat3x3; + + /// Single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, defaultp> fmat3x4; + + /// Single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, defaultp> fmat4x2; + + /// Single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, defaultp> fmat4x3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> fmat4x4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f32mat1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> f32mat2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> f32mat3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> f32mat4; + + + /// Single-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f32 f32mat1x1; + + /// Single-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f32, defaultp> f32mat2x2; + + /// Single-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f32, defaultp> f32mat2x3; + + /// Single-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f32, defaultp> f32mat2x4; + + /// Single-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f32, defaultp> f32mat3x2; + + /// Single-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f32, defaultp> f32mat3x3; + + /// Single-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f32, defaultp> f32mat3x4; + + /// Single-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f32, defaultp> f32mat4x2; + + /// Single-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f32, defaultp> f32mat4x3; + + /// Single-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f32, defaultp> f32mat4x4; + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef detail::tmat1x1 f64mat1; + + /// Double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, defaultp> f64mat2; + + /// Double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, defaultp> f64mat3; + + /// Double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, defaultp> f64mat4; + + + /// Double-qualifier floating-point 1x1 matrix. + /// @see gtc_type_precision + //typedef f64 f64mat1x1; + + /// Double-qualifier floating-point 2x2 matrix. + /// @see gtc_type_precision + typedef mat<2, 2, f64, defaultp> f64mat2x2; + + /// Double-qualifier floating-point 2x3 matrix. + /// @see gtc_type_precision + typedef mat<2, 3, f64, defaultp> f64mat2x3; + + /// Double-qualifier floating-point 2x4 matrix. + /// @see gtc_type_precision + typedef mat<2, 4, f64, defaultp> f64mat2x4; + + /// Double-qualifier floating-point 3x2 matrix. + /// @see gtc_type_precision + typedef mat<3, 2, f64, defaultp> f64mat3x2; + + /// Double-qualifier floating-point 3x3 matrix. + /// @see gtc_type_precision + typedef mat<3, 3, f64, defaultp> f64mat3x3; + + /// Double-qualifier floating-point 3x4 matrix. + /// @see gtc_type_precision + typedef mat<3, 4, f64, defaultp> f64mat3x4; + + /// Double-qualifier floating-point 4x2 matrix. + /// @see gtc_type_precision + typedef mat<4, 2, f64, defaultp> f64mat4x2; + + /// Double-qualifier floating-point 4x3 matrix. + /// @see gtc_type_precision + typedef mat<4, 3, f64, defaultp> f64mat4x3; + + /// Double-qualifier floating-point 4x4 matrix. + /// @see gtc_type_precision + typedef mat<4, 4, f64, defaultp> f64mat4x4; + +# endif//GLM_FORCE_SINGLE_ONLY + + ////////////////////////// + // Quaternion types + + /// Single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua f32quat; + + /// Low single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua lowp_f32quat; + + /// Low double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua lowp_f64quat; + + /// Medium single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua mediump_f32quat; + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Medium double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua mediump_f64quat; + + /// High single-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua highp_f32quat; + + /// High double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua highp_f64quat; + + /// Double-qualifier floating-point quaternion. + /// @see gtc_type_precision + typedef qua f64quat; + +# endif//GLM_FORCE_SINGLE_ONLY + + /// @} +}//namespace glm + +#include "type_precision.inl" diff --git a/thirdparty/glm/glm/gtc/type_precision.inl b/thirdparty/glm/glm/gtc/type_precision.inl new file mode 100644 index 0000000..ae80912 --- /dev/null +++ b/thirdparty/glm/glm/gtc/type_precision.inl @@ -0,0 +1,6 @@ +/// @ref gtc_precision + +namespace glm +{ + +} diff --git a/thirdparty/glm/glm/gtc/type_ptr.hpp b/thirdparty/glm/glm/gtc/type_ptr.hpp new file mode 100644 index 0000000..d7e625a --- /dev/null +++ b/thirdparty/glm/glm/gtc/type_ptr.hpp @@ -0,0 +1,230 @@ +/// @ref gtc_type_ptr +/// @file glm/gtc/type_ptr.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtc_type_ptr GLM_GTC_type_ptr +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Handles the interaction between pointers and vector, matrix types. +/// +/// This extension defines an overloaded function, glm::value_ptr. It returns +/// a pointer to the memory layout of the object. Matrix types store their values +/// in column-major order. +/// +/// This is useful for uploading data to matrices or copying data to buffer objects. +/// +/// Example: +/// @code +/// #include +/// #include +/// +/// glm::vec3 aVector(3); +/// glm::mat4 someMatrix(1.0); +/// +/// glUniform3fv(uniformLoc, 1, glm::value_ptr(aVector)); +/// glUniformMatrix4fv(uniformMatrixLoc, 1, GL_FALSE, glm::value_ptr(someMatrix)); +/// @endcode +/// +/// need to be included to use the features of this extension. + +#pragma once + +// Dependency: +#include "../gtc/quaternion.hpp" +#include "../gtc/vec1.hpp" +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" +#include + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_type_ptr extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_type_ptr + /// @{ + + /// Return the constant address to the data of the input parameter. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL typename genType::value_type const * value_ptr(genType const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<1, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<2, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<3, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<4, T, Q> const& v); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<2, T, defaultp> make_vec2(T const * const ptr); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<3, T, defaultp> make_vec3(T const * const ptr); + + /// Build a vector from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL vec<4, T, defaultp> make_vec4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> make_mat2x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 3, T, defaultp> make_mat2x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 4, T, defaultp> make_mat2x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 2, T, defaultp> make_mat3x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> make_mat3x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 4, T, defaultp> make_mat3x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 2, T, defaultp> make_mat4x2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 3, T, defaultp> make_mat4x3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> make_mat4x4(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> make_mat2(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> make_mat3(T const * const ptr); + + /// Build a matrix from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> make_mat4(T const * const ptr); + + /// Build a quaternion from a pointer. + /// @see gtc_type_ptr + template + GLM_FUNC_DECL qua make_quat(T const * const ptr); + + /// @} +}//namespace glm + +#include "type_ptr.inl" diff --git a/thirdparty/glm/glm/gtc/type_ptr.inl b/thirdparty/glm/glm/gtc/type_ptr.inl new file mode 100644 index 0000000..26b20b5 --- /dev/null +++ b/thirdparty/glm/glm/gtc/type_ptr.inl @@ -0,0 +1,386 @@ +/// @ref gtc_type_ptr + +#include + +namespace glm +{ + /// @addtogroup gtc_type_ptr + /// @{ + + template + GLM_FUNC_QUALIFIER T const* value_ptr(vec<2, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<2, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const * value_ptr(vec<3, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<3, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(vec<4, T, Q> const& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(vec<4, T, Q>& v) + { + return &(v.x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<4, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<2, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<2, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 2, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<4, 2, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<3, 4, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(mat<3, 4, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const* value_ptr(mat<4, 3, T, Q> const& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T * value_ptr(mat<4, 3, T, Q>& m) + { + return &(m[0].x); + } + + template + GLM_FUNC_QUALIFIER T const * value_ptr(qua const& q) + { + return &(q[0]); + } + + template + GLM_FUNC_QUALIFIER T* value_ptr(qua& q) + { + return &(q[0]); + } + + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<1, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<2, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<3, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<1, T, Q> make_vec1(vec<4, T, Q> const& v) + { + return vec<1, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<1, T, Q> const& v) + { + return vec<2, T, Q>(v.x, static_cast(0)); + } + + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<2, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<3, T, Q> const& v) + { + return vec<2, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<2, T, Q> make_vec2(vec<4, T, Q> const& v) + { + return vec<2, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<1, T, Q> const& v) + { + return vec<3, T, Q>(v.x, static_cast(0), static_cast(0)); + } + + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<2, T, Q> const& v) + { + return vec<3, T, Q>(v.x, v.y, static_cast(0)); + } + + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<3, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_DECL vec<3, T, Q> make_vec3(vec<4, T, Q> const& v) + { + return vec<3, T, Q>(v); + } + + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<1, T, Q> const& v) + { + return vec<4, T, Q>(v.x, static_cast(0), static_cast(0), static_cast(1)); + } + + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<2, T, Q> const& v) + { + return vec<4, T, Q>(v.x, v.y, static_cast(0), static_cast(1)); + } + + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<3, T, Q> const& v) + { + return vec<4, T, Q>(v.x, v.y, v.z, static_cast(1)); + } + + template + GLM_FUNC_DECL vec<4, T, Q> make_vec4(vec<4, T, Q> const& v) + { + return v; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, defaultp> make_vec2(T const *const ptr) + { + vec<2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, defaultp> make_vec3(T const *const ptr) + { + vec<3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, defaultp> make_vec4(T const *const ptr) + { + vec<4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(vec<4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> make_mat2x2(T const *const ptr) + { + mat<2, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, defaultp> make_mat2x3(T const *const ptr) + { + mat<2, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, defaultp> make_mat2x4(T const *const ptr) + { + mat<2, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<2, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, defaultp> make_mat3x2(T const *const ptr) + { + mat<3, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> make_mat3x3(T const *const ptr) + { + mat<3, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, defaultp> make_mat3x4(T const *const ptr) + { + mat<3, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<3, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, defaultp> make_mat4x2(T const *const ptr) + { + mat<4, 2, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 2, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, defaultp> make_mat4x3(T const *const ptr) + { + mat<4, 3, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 3, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> make_mat4x4(T const *const ptr) + { + mat<4, 4, T, defaultp> Result; + memcpy(value_ptr(Result), ptr, sizeof(mat<4, 4, T, defaultp>)); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> make_mat2(T const *const ptr) + { + return make_mat2x2(ptr); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> make_mat3(T const *const ptr) + { + return make_mat3x3(ptr); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> make_mat4(T const *const ptr) + { + return make_mat4x4(ptr); + } + + template + GLM_FUNC_QUALIFIER qua make_quat(T const *const ptr) + { + qua Result; + memcpy(value_ptr(Result), ptr, sizeof(qua)); + return Result; + } + + /// @} +}//namespace glm + diff --git a/thirdparty/glm/glm/gtc/ulp.hpp b/thirdparty/glm/glm/gtc/ulp.hpp new file mode 100644 index 0000000..7b918f0 --- /dev/null +++ b/thirdparty/glm/glm/gtc/ulp.hpp @@ -0,0 +1,155 @@ +/// @ref gtc_ulp +/// @file glm/gtc/ulp.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_ulp GLM_GTC_ulp +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Allow the measurement of the accuracy of a function against a reference +/// implementation. This extension works on floating-point data and provide results +/// in ULP. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/_vectorize.hpp" +#include "../ext/scalar_int_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_ulp extension included") +#endif + +namespace glm +{ + /// @addtogroup gtc_ulp + /// @{ + + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL genType next_float(genType x); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL genType prev_float(genType x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL genType next_float(genType x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam genType A floating-point scalar type. + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL genType prev_float(genType x, int ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @see gtc_ulp + GLM_FUNC_DECL int float_distance(float x, float y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @see gtc_ulp + GLM_FUNC_DECL int64 float_distance(double x, double y); + + /// Return the next ULP value(s) after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x, int ULPs); + + /// Return the value(s) ULP distance after the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec next_float(vec const& x, vec const& ULPs); + + /// Return the previous ULP value(s) before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x, int ULPs); + + /// Return the value(s) ULP distance before the input value(s). + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec prev_float(vec const& x, vec const& ULPs); + + /// Return the distance in the number of ULP between 2 single-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec float_distance(vec const& x, vec const& y); + + /// Return the distance in the number of ULP between 2 double-precision floating-point scalars. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam Q Value from qualifier enum + /// + /// @see gtc_ulp + template + GLM_FUNC_DECL vec float_distance(vec const& x, vec const& y); + + /// @} +}//namespace glm + +#include "ulp.inl" diff --git a/thirdparty/glm/glm/gtc/ulp.inl b/thirdparty/glm/glm/gtc/ulp.inl new file mode 100644 index 0000000..836c84b --- /dev/null +++ b/thirdparty/glm/glm/gtc/ulp.inl @@ -0,0 +1,173 @@ +/// @ref gtc_ulp + +#include "../ext/scalar_ulp.hpp" + +namespace glm +{ + template<> + GLM_FUNC_QUALIFIER float next_float(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MAX); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MAX); +# else + return nextafterf(x, FLT_MAX); +# endif + } + + template<> + GLM_FUNC_QUALIFIER double next_float(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::max()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafter(x, std::numeric_limits::max()); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MAX); +# else + return nextafter(x, DBL_MAX); +# endif + } + + template + GLM_FUNC_QUALIFIER T next_float(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'next_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for (int i = 0; i < ULPs; ++i) + temp = next_float(temp); + return temp; + } + + GLM_FUNC_QUALIFIER float prev_float(float x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return detail::nextafterf(x, FLT_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafterf(x, FLT_MIN); +# else + return nextafterf(x, FLT_MIN); +# endif + } + + GLM_FUNC_QUALIFIER double prev_float(double x) + { +# if GLM_HAS_CXX11_STL + return std::nextafter(x, std::numeric_limits::min()); +# elif((GLM_COMPILER & GLM_COMPILER_VC) || ((GLM_COMPILER & GLM_COMPILER_INTEL) && (GLM_PLATFORM & GLM_PLATFORM_WINDOWS))) + return _nextafter(x, DBL_MIN); +# elif(GLM_PLATFORM & GLM_PLATFORM_ANDROID) + return __builtin_nextafter(x, DBL_MIN); +# else + return nextafter(x, DBL_MIN); +# endif + } + + template + GLM_FUNC_QUALIFIER T prev_float(T x, int ULPs) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'prev_float' only accept floating-point input"); + assert(ULPs >= 0); + + T temp = x; + for (int i = 0; i < ULPs; ++i) + temp = prev_float(temp); + return temp; + } + + GLM_FUNC_QUALIFIER int float_distance(float x, float y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } + + GLM_FUNC_QUALIFIER int64 float_distance(double x, double y) + { + detail::float_t const a(x); + detail::float_t const b(y); + + return abs(a.i - b.i); + } + + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x, int ULPs) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec next_float(vec const& x, vec const& ULPs) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = next_float(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x, int ULPs) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i], ULPs); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec prev_float(vec const& x, vec const& ULPs) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = prev_float(x[i], ULPs[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec float_distance(vec const& x, vec const& y) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = float_distance(x[i], y[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER vec float_distance(vec const& x, vec const& y) + { + vec Result; + for (length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = float_distance(x[i], y[i]); + return Result; + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtc/vec1.hpp b/thirdparty/glm/glm/gtc/vec1.hpp new file mode 100644 index 0000000..63697a2 --- /dev/null +++ b/thirdparty/glm/glm/gtc/vec1.hpp @@ -0,0 +1,30 @@ +/// @ref gtc_vec1 +/// @file glm/gtc/vec1.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtc_vec1 GLM_GTC_vec1 +/// @ingroup gtc +/// +/// Include to use the features of this extension. +/// +/// Add vec1, ivec1, uvec1 and bvec1 types. + +#pragma once + +// Dependency: +#include "../ext/vector_bool1.hpp" +#include "../ext/vector_bool1_precision.hpp" +#include "../ext/vector_float1.hpp" +#include "../ext/vector_float1_precision.hpp" +#include "../ext/vector_double1.hpp" +#include "../ext/vector_double1_precision.hpp" +#include "../ext/vector_int1.hpp" +#include "../ext/vector_int1_sized.hpp" +#include "../ext/vector_uint1.hpp" +#include "../ext/vector_uint1_sized.hpp" + +#if GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_vec1 extension included") +#endif + diff --git a/thirdparty/glm/glm/gtx/associated_min_max.hpp b/thirdparty/glm/glm/gtx/associated_min_max.hpp new file mode 100644 index 0000000..435230d --- /dev/null +++ b/thirdparty/glm/glm/gtx/associated_min_max.hpp @@ -0,0 +1,205 @@ +/// @ref gtx_associated_min_max +/// @file glm/gtx/associated_min_max.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_associated_min_max GLM_GTX_associated_min_max +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Min and max functions that return associated values not the compared ones. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_associated_min_max is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_associated_min_max extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_associated_min_max + /// @{ + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin(T x, U a, T y, U b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + T x, const vec& a, + T y, const vec& b); + + /// Minimum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, U a, + vec const& y, U b); + + /// Minimum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin( + T x, U a, + T y, U b, + T z, U c); + + /// Minimum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMin( + T x, U a, + T y, U b, + T z, U c, + T w, U d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d); + + /// Minimum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMin( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax(T x, U a, T y, U b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b); + + /// Maximum comparison between 2 variables and returns 2 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax( + T x, U a, + T y, U b, + T z, U c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c); + + /// Maximum comparison between 3 variables and returns 3 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL U associatedMax( + T x, U a, + T y, U b, + T z, U c, + T w, U d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d); + + /// Maximum comparison between 4 variables and returns 4 associated variable values + /// @see gtx_associated_min_max + template + GLM_FUNC_DECL vec associatedMax( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d); + + /// @} +} //namespace glm + +#include "associated_min_max.inl" diff --git a/thirdparty/glm/glm/gtx/associated_min_max.inl b/thirdparty/glm/glm/gtx/associated_min_max.inl new file mode 100644 index 0000000..f09f5bb --- /dev/null +++ b/thirdparty/glm/glm/gtx/associated_min_max.inl @@ -0,0 +1,354 @@ +/// @ref gtx_associated_min_max + +namespace glm{ + +// Min comparison between 2 variables +template +GLM_FUNC_QUALIFIER U associatedMin(T x, U a, T y, U b) +{ + return x < y ? a : b; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? a[i] : b[i]; + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + T x, const vec& a, + T y, const vec& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x < y ? a[i] : b[i]; + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, U a, + vec const& y, U b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? a : b; + return Result; +} + +// Min comparison between 3 variables +template +GLM_FUNC_QUALIFIER U associatedMin +( + T x, U a, + T y, U b, + T z, U c +) +{ + U Result = x < y ? (x < z ? a : c) : (y < z ? b : c); + return Result; +} + +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] < y[i] ? (x[i] < z[i] ? a[i] : c[i]) : (y[i] < z[i] ? b[i] : c[i]); + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER U associatedMin +( + T x, U a, + T y, U b, + T z, U c, + T w, U d +) +{ + T Test1 = min(x, y); + T Test2 = min(z, w); + U Result1 = x < y ? a : b; + U Result2 = z < w ? c : d; + U Result = Test1 < Test2 ? Result1 : Result2; + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = min(x[i], y[i]); + T Test2 = min(z[i], w[i]); + U Result1 = x[i] < y[i] ? a[i] : b[i]; + U Result2 = z[i] < w[i] ? c[i] : d[i]; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d +) +{ + T Test1 = min(x, y); + T Test2 = min(z, w); + + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + U Result1 = x < y ? a[i] : b[i]; + U Result2 = z < w ? c[i] : d[i]; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Min comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMin +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = min(x[i], y[i]); + T Test2 = min(z[i], w[i]); + U Result1 = x[i] < y[i] ? a : b; + U Result2 = z[i] < w[i] ? c : d; + Result[i] = Test1 < Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER U associatedMax(T x, U a, T y, U b) +{ + return x > y ? a : b; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? a[i] : b[i]; + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x > y ? a[i] : b[i]; + return Result; +} + +// Max comparison between 2 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? a : b; + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER U associatedMax +( + T x, U a, + T y, U b, + T z, U c +) +{ + U Result = x > y ? (x > z ? a : c) : (y > z ? b : c); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? (x[i] > z[i] ? a[i] : c[i]) : (y[i] > z[i] ? b[i] : c[i]); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x > y ? (x > z ? a[i] : c[i]) : (y > z ? b[i] : c[i]); + return Result; +} + +// Max comparison between 3 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + Result[i] = x[i] > y[i] ? (x[i] > z[i] ? a : c) : (y[i] > z[i] ? b : c); + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER U associatedMax +( + T x, U a, + T y, U b, + T z, U c, + T w, U d +) +{ + T Test1 = max(x, y); + T Test2 = max(z, w); + U Result1 = x > y ? a : b; + U Result2 = z > w ? c : d; + U Result = Test1 > Test2 ? Result1 : Result2; + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, vec const& a, + vec const& y, vec const& b, + vec const& z, vec const& c, + vec const& w, vec const& d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = max(x[i], y[i]); + T Test2 = max(z[i], w[i]); + U Result1 = x[i] > y[i] ? a[i] : b[i]; + U Result2 = z[i] > w[i] ? c[i] : d[i]; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + T x, vec const& a, + T y, vec const& b, + T z, vec const& c, + T w, vec const& d +) +{ + T Test1 = max(x, y); + T Test2 = max(z, w); + + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + U Result1 = x > y ? a[i] : b[i]; + U Result2 = z > w ? c[i] : d[i]; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} + +// Max comparison between 4 variables +template +GLM_FUNC_QUALIFIER vec associatedMax +( + vec const& x, U a, + vec const& y, U b, + vec const& z, U c, + vec const& w, U d +) +{ + vec Result; + for(length_t i = 0, n = Result.length(); i < n; ++i) + { + T Test1 = max(x[i], y[i]); + T Test2 = max(z[i], w[i]); + U Result1 = x[i] > y[i] ? a : b; + U Result2 = z[i] > w[i] ? c : d; + Result[i] = Test1 > Test2 ? Result1 : Result2; + } + return Result; +} +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/bit.hpp b/thirdparty/glm/glm/gtx/bit.hpp new file mode 100644 index 0000000..2f6b3f6 --- /dev/null +++ b/thirdparty/glm/glm/gtx/bit.hpp @@ -0,0 +1,96 @@ +/// @ref gtx_bit +/// @file glm/gtx/bit.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_bit GLM_GTX_bit +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../gtc/bitfield.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_bit is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_bit extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_bit + /// @{ + + /// @see gtx_bit + template + GLM_FUNC_DECL genIUType highestBitValue(genIUType Value); + + /// @see gtx_bit + template + GLM_FUNC_DECL genIUType lowestBitValue(genIUType Value); + + /// Find the highest bit set to 1 in a integer variable and return its value. + /// + /// @see gtx_bit + template + GLM_FUNC_DECL vec highestBitValue(vec const& value); + + /// Return the power of two number which value is just higher the input value. + /// Deprecated, use ceilPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoAbove(genIUType Value); + + /// Return the power of two number which value is just higher the input value. + /// Deprecated, use ceilPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoAbove(vec const& value); + + /// Return the power of two number which value is just lower the input value. + /// Deprecated, use floorPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoBelow(genIUType Value); + + /// Return the power of two number which value is just lower the input value. + /// Deprecated, use floorPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoBelow(vec const& value); + + /// Return the power of two number which value is the closet to the input value. + /// Deprecated, use roundPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL genIUType powerOfTwoNearest(genIUType Value); + + /// Return the power of two number which value is the closet to the input value. + /// Deprecated, use roundPowerOfTwo from GTC_round instead + /// + /// @see gtc_round + /// @see gtx_bit + template + GLM_DEPRECATED GLM_FUNC_DECL vec powerOfTwoNearest(vec const& value); + + /// @} +} //namespace glm + + +#include "bit.inl" + diff --git a/thirdparty/glm/glm/gtx/bit.inl b/thirdparty/glm/glm/gtx/bit.inl new file mode 100644 index 0000000..621b626 --- /dev/null +++ b/thirdparty/glm/glm/gtx/bit.inl @@ -0,0 +1,92 @@ +/// @ref gtx_bit + +namespace glm +{ + /////////////////// + // highestBitValue + + template + GLM_FUNC_QUALIFIER genIUType highestBitValue(genIUType Value) + { + genIUType tmp = Value; + genIUType result = genIUType(0); + while(tmp) + { + result = (tmp & (~tmp + 1)); // grab lowest bit + tmp &= ~result; // clear lowest bit + } + return result; + } + + template + GLM_FUNC_QUALIFIER vec highestBitValue(vec const& v) + { + return detail::functor1::call(highestBitValue, v); + } + + /////////////////// + // lowestBitValue + + template + GLM_FUNC_QUALIFIER genIUType lowestBitValue(genIUType Value) + { + return (Value & (~Value + 1)); + } + + template + GLM_FUNC_QUALIFIER vec lowestBitValue(vec const& v) + { + return detail::functor1::call(lowestBitValue, v); + } + + /////////////////// + // powerOfTwoAbove + + template + GLM_FUNC_QUALIFIER genType powerOfTwoAbove(genType value) + { + return isPowerOfTwo(value) ? value : highestBitValue(value) << 1; + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoAbove(vec const& v) + { + return detail::functor1::call(powerOfTwoAbove, v); + } + + /////////////////// + // powerOfTwoBelow + + template + GLM_FUNC_QUALIFIER genType powerOfTwoBelow(genType value) + { + return isPowerOfTwo(value) ? value : highestBitValue(value); + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoBelow(vec const& v) + { + return detail::functor1::call(powerOfTwoBelow, v); + } + + ///////////////////// + // powerOfTwoNearest + + template + GLM_FUNC_QUALIFIER genType powerOfTwoNearest(genType value) + { + if(isPowerOfTwo(value)) + return value; + + genType const prev = highestBitValue(value); + genType const next = prev << 1; + return (next - value) < (value - prev) ? next : prev; + } + + template + GLM_FUNC_QUALIFIER vec powerOfTwoNearest(vec const& v) + { + return detail::functor1::call(powerOfTwoNearest, v); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/closest_point.hpp b/thirdparty/glm/glm/gtx/closest_point.hpp new file mode 100644 index 0000000..a248e4b --- /dev/null +++ b/thirdparty/glm/glm/gtx/closest_point.hpp @@ -0,0 +1,47 @@ +/// @ref gtx_closest_point +/// @file glm/gtx/closest_point.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_closest_point GLM_GTX_closest_point +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Find the point on a straight line which is the closet of a point. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_closest_point is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_closest_point extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_closest_point + /// @{ + + /// Find the point on a straight line which is the closet of a point. + /// @see gtx_closest_point + template + GLM_FUNC_DECL vec<3, T, Q> closestPointOnLine( + vec<3, T, Q> const& point, + vec<3, T, Q> const& a, + vec<3, T, Q> const& b); + + /// 2d lines work as well + template + GLM_FUNC_DECL vec<2, T, Q> closestPointOnLine( + vec<2, T, Q> const& point, + vec<2, T, Q> const& a, + vec<2, T, Q> const& b); + + /// @} +}// namespace glm + +#include "closest_point.inl" diff --git a/thirdparty/glm/glm/gtx/closest_point.inl b/thirdparty/glm/glm/gtx/closest_point.inl new file mode 100644 index 0000000..0a39b04 --- /dev/null +++ b/thirdparty/glm/glm/gtx/closest_point.inl @@ -0,0 +1,45 @@ +/// @ref gtx_closest_point + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> closestPointOnLine + ( + vec<3, T, Q> const& point, + vec<3, T, Q> const& a, + vec<3, T, Q> const& b + ) + { + T LineLength = distance(a, b); + vec<3, T, Q> Vector = point - a; + vec<3, T, Q> LineDirection = (b - a) / LineLength; + + // Project Vector to LineDirection to get the distance of point from a + T Distance = dot(Vector, LineDirection); + + if(Distance <= T(0)) return a; + if(Distance >= LineLength) return b; + return a + LineDirection * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> closestPointOnLine + ( + vec<2, T, Q> const& point, + vec<2, T, Q> const& a, + vec<2, T, Q> const& b + ) + { + T LineLength = distance(a, b); + vec<2, T, Q> Vector = point - a; + vec<2, T, Q> LineDirection = (b - a) / LineLength; + + // Project Vector to LineDirection to get the distance of point from a + T Distance = dot(Vector, LineDirection); + + if(Distance <= T(0)) return a; + if(Distance >= LineLength) return b; + return a + LineDirection * Distance; + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/color_encoding.hpp b/thirdparty/glm/glm/gtx/color_encoding.hpp new file mode 100644 index 0000000..4769e0a --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_encoding.hpp @@ -0,0 +1,52 @@ +/// @ref gtx_color_encoding +/// @file glm/gtx/color_encoding.hpp +/// +/// @see core (dependence) +/// @see gtx_color_encoding (dependence) +/// +/// @defgroup gtx_color_encoding GLM_GTX_color_encoding +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../vec3.hpp" +#include + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTC_color_encoding is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTC_color_encoding extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_color_encoding + /// @{ + + /// Convert a linear sRGB color to D65 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertLinearSRGBToD65XYZ(vec<3, T, Q> const& ColorLinearSRGB); + + /// Convert a linear sRGB color to D50 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertLinearSRGBToD50XYZ(vec<3, T, Q> const& ColorLinearSRGB); + + /// Convert a D65 YUV color to linear sRGB. + template + GLM_FUNC_DECL vec<3, T, Q> convertD65XYZToLinearSRGB(vec<3, T, Q> const& ColorD65XYZ); + + /// Convert a D65 YUV color to D50 YUV. + template + GLM_FUNC_DECL vec<3, T, Q> convertD65XYZToD50XYZ(vec<3, T, Q> const& ColorD65XYZ); + + /// @} +} //namespace glm + +#include "color_encoding.inl" diff --git a/thirdparty/glm/glm/gtx/color_encoding.inl b/thirdparty/glm/glm/gtx/color_encoding.inl new file mode 100644 index 0000000..e50fa3e --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_encoding.inl @@ -0,0 +1,45 @@ +/// @ref gtx_color_encoding + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertLinearSRGBToD65XYZ(vec<3, T, Q> const& ColorLinearSRGB) + { + vec<3, T, Q> const M(0.490f, 0.17697f, 0.2f); + vec<3, T, Q> const N(0.31f, 0.8124f, 0.01063f); + vec<3, T, Q> const O(0.490f, 0.01f, 0.99f); + + return (M * ColorLinearSRGB + N * ColorLinearSRGB + O * ColorLinearSRGB) * static_cast(5.650675255693055f); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertLinearSRGBToD50XYZ(vec<3, T, Q> const& ColorLinearSRGB) + { + vec<3, T, Q> const M(0.436030342570117f, 0.222438466210245f, 0.013897440074263f); + vec<3, T, Q> const N(0.385101860087134f, 0.716942745571917f, 0.097076381494207f); + vec<3, T, Q> const O(0.143067806654203f, 0.060618777416563f, 0.713926257896652f); + + return M * ColorLinearSRGB + N * ColorLinearSRGB + O * ColorLinearSRGB; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertD65XYZToLinearSRGB(vec<3, T, Q> const& ColorD65XYZ) + { + vec<3, T, Q> const M(0.41847f, -0.091169f, 0.0009209f); + vec<3, T, Q> const N(-0.15866f, 0.25243f, 0.015708f); + vec<3, T, Q> const O(0.0009209f, -0.0025498f, 0.1786f); + + return M * ColorD65XYZ + N * ColorD65XYZ + O * ColorD65XYZ; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> convertD65XYZToD50XYZ(vec<3, T, Q> const& ColorD65XYZ) + { + vec<3, T, Q> const M(+1.047844353856414f, +0.029549007606644f, -0.009250984365223f); + vec<3, T, Q> const N(+0.022898981050086f, +0.990508028941971f, +0.015072338237051f); + vec<3, T, Q> const O(-0.050206647741605f, -0.017074711360960f, +0.751717835079977f); + + return M * ColorD65XYZ + N * ColorD65XYZ + O * ColorD65XYZ; + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/color_space.hpp b/thirdparty/glm/glm/gtx/color_space.hpp new file mode 100644 index 0000000..c39a1f4 --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_space.hpp @@ -0,0 +1,70 @@ +/// @ref gtx_color_space +/// @file glm/gtx/color_space.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_color_space GLM_GTX_color_space +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Related to RGB to HSV conversions and operations. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_color_space is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_color_space extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_color_space + /// @{ + + /// Converts a color from HSV color space to its color in RGB color space. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> rgbColor( + vec<3, T, Q> const& hsvValue); + + /// Converts a color from RGB color space to its color in HSV color space. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> hsvColor( + vec<3, T, Q> const& rgbValue); + + /// Build a saturation matrix. + /// @see gtx_color_space + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> saturation( + T const s); + + /// Modify the saturation of a color. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<3, T, Q> saturation( + T const s, + vec<3, T, Q> const& color); + + /// Modify the saturation of a color. + /// @see gtx_color_space + template + GLM_FUNC_DECL vec<4, T, Q> saturation( + T const s, + vec<4, T, Q> const& color); + + /// Compute color luminosity associating ratios (0.33, 0.59, 0.11) to RGB canals. + /// @see gtx_color_space + template + GLM_FUNC_DECL T luminosity( + vec<3, T, Q> const& color); + + /// @} +}//namespace glm + +#include "color_space.inl" diff --git a/thirdparty/glm/glm/gtx/color_space.inl b/thirdparty/glm/glm/gtx/color_space.inl new file mode 100644 index 0000000..b3183b9 --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_space.inl @@ -0,0 +1,144 @@ +/// @ref gtx_color_space + +#include +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgbColor(const vec<3, T, Q>& hsvColor) + { + vec<3, T, Q> hsv = hsvColor; + vec<3, T, Q> rgbColor; + + if(equal(hsv.y, static_cast(0), epsilon())) + // achromatic (grey) + rgbColor = vec<3, T, Q>(hsv.z); + else + { + T sector = floor(hsv.x * (T(1) / T(60))); + T frac = (hsv.x * (T(1) / T(60))) - sector; + // factorial part of h + T o = hsv.z * (T(1) - hsv.y); + T p = hsv.z * (T(1) - hsv.y * frac); + T q = hsv.z * (T(1) - hsv.y * (T(1) - frac)); + + switch(int(sector)) + { + default: + case 0: + rgbColor.r = hsv.z; + rgbColor.g = q; + rgbColor.b = o; + break; + case 1: + rgbColor.r = p; + rgbColor.g = hsv.z; + rgbColor.b = o; + break; + case 2: + rgbColor.r = o; + rgbColor.g = hsv.z; + rgbColor.b = q; + break; + case 3: + rgbColor.r = o; + rgbColor.g = p; + rgbColor.b = hsv.z; + break; + case 4: + rgbColor.r = q; + rgbColor.g = o; + rgbColor.b = hsv.z; + break; + case 5: + rgbColor.r = hsv.z; + rgbColor.g = o; + rgbColor.b = p; + break; + } + } + + return rgbColor; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> hsvColor(const vec<3, T, Q>& rgbColor) + { + vec<3, T, Q> hsv = rgbColor; + T Min = min(min(rgbColor.r, rgbColor.g), rgbColor.b); + T Max = max(max(rgbColor.r, rgbColor.g), rgbColor.b); + T Delta = Max - Min; + + hsv.z = Max; + + if(!equal(Max, static_cast(0), epsilon())) + { + hsv.y = Delta / hsv.z; + T h = static_cast(0); + + if(equal(rgbColor.r, Max, epsilon())) + // between yellow & magenta + h = static_cast(0) + T(60) * (rgbColor.g - rgbColor.b) / Delta; + else if(equal(rgbColor.g, Max, epsilon())) + // between cyan & yellow + h = static_cast(120) + T(60) * (rgbColor.b - rgbColor.r) / Delta; + else + // between magenta & cyan + h = static_cast(240) + T(60) * (rgbColor.r - rgbColor.g) / Delta; + + if(h < T(0)) + hsv.x = h + T(360); + else + hsv.x = h; + } + else + { + // If r = g = b = 0 then s = 0, h is undefined + hsv.y = static_cast(0); + hsv.x = static_cast(0); + } + + return hsv; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> saturation(T const s) + { + vec<3, T, defaultp> rgbw = vec<3, T, defaultp>(T(0.2126), T(0.7152), T(0.0722)); + + vec<3, T, defaultp> const col((T(1) - s) * rgbw); + + mat<4, 4, T, defaultp> result(T(1)); + result[0][0] = col.x + s; + result[0][1] = col.x; + result[0][2] = col.x; + result[1][0] = col.y; + result[1][1] = col.y + s; + result[1][2] = col.y; + result[2][0] = col.z; + result[2][1] = col.z; + result[2][2] = col.z + s; + + return result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> saturation(const T s, const vec<3, T, Q>& color) + { + return vec<3, T, Q>(saturation(s) * vec<4, T, Q>(color, T(0))); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> saturation(const T s, const vec<4, T, Q>& color) + { + return saturation(s) * color; + } + + template + GLM_FUNC_QUALIFIER T luminosity(const vec<3, T, Q>& color) + { + const vec<3, T, Q> tmp = vec<3, T, Q>(0.33, 0.59, 0.11); + return dot(color, tmp); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/color_space_YCoCg.hpp b/thirdparty/glm/glm/gtx/color_space_YCoCg.hpp new file mode 100644 index 0000000..a418037 --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_space_YCoCg.hpp @@ -0,0 +1,58 @@ +/// @ref gtx_color_space_YCoCg +/// @file glm/gtx/color_space_YCoCg.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_color_space_YCoCg GLM_GTX_color_space_YCoCg +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// RGB to YCoCg conversions and operations + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_color_space_YCoCg is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_color_space_YCoCg extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_color_space_YCoCg + /// @{ + + /// Convert a color from RGB color space to YCoCg color space. + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> rgb2YCoCg( + vec<3, T, Q> const& rgbColor); + + /// Convert a color from YCoCg color space to RGB color space. + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> YCoCg2rgb( + vec<3, T, Q> const& YCoCgColor); + + /// Convert a color from RGB color space to YCoCgR color space. + /// @see "YCoCg-R: A Color Space with RGB Reversibility and Low Dynamic Range" + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> rgb2YCoCgR( + vec<3, T, Q> const& rgbColor); + + /// Convert a color from YCoCgR color space to RGB color space. + /// @see "YCoCg-R: A Color Space with RGB Reversibility and Low Dynamic Range" + /// @see gtx_color_space_YCoCg + template + GLM_FUNC_DECL vec<3, T, Q> YCoCgR2rgb( + vec<3, T, Q> const& YCoCgColor); + + /// @} +}//namespace glm + +#include "color_space_YCoCg.inl" diff --git a/thirdparty/glm/glm/gtx/color_space_YCoCg.inl b/thirdparty/glm/glm/gtx/color_space_YCoCg.inl new file mode 100644 index 0000000..83ba857 --- /dev/null +++ b/thirdparty/glm/glm/gtx/color_space_YCoCg.inl @@ -0,0 +1,107 @@ +/// @ref gtx_color_space_YCoCg + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCg + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.x/*Y */ = rgbColor.r / T(4) + rgbColor.g / T(2) + rgbColor.b / T(4); + result.y/*Co*/ = rgbColor.r / T(2) + rgbColor.g * T(0) - rgbColor.b / T(2); + result.z/*Cg*/ = - rgbColor.r / T(4) + rgbColor.g / T(2) - rgbColor.b / T(4); + return result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCg2rgb + ( + vec<3, T, Q> const& YCoCgColor + ) + { + vec<3, T, Q> result; + result.r = YCoCgColor.x + YCoCgColor.y - YCoCgColor.z; + result.g = YCoCgColor.x + YCoCgColor.z; + result.b = YCoCgColor.x - YCoCgColor.y - YCoCgColor.z; + return result; + } + + template + class compute_YCoCgR { + public: + static GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.x/*Y */ = rgbColor.g * static_cast(0.5) + (rgbColor.r + rgbColor.b) * static_cast(0.25); + result.y/*Co*/ = rgbColor.r - rgbColor.b; + result.z/*Cg*/ = rgbColor.g - (rgbColor.r + rgbColor.b) * static_cast(0.5); + return result; + } + + static GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + vec<3, T, Q> result; + T tmp = YCoCgRColor.x - (YCoCgRColor.z * static_cast(0.5)); + result.g = YCoCgRColor.z + tmp; + result.b = tmp - (YCoCgRColor.y * static_cast(0.5)); + result.r = result.b + YCoCgRColor.y; + return result; + } + }; + + template + class compute_YCoCgR { + public: + static GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + vec<3, T, Q> result; + result.y/*Co*/ = rgbColor.r - rgbColor.b; + T tmp = rgbColor.b + (result.y >> 1); + result.z/*Cg*/ = rgbColor.g - tmp; + result.x/*Y */ = tmp + (result.z >> 1); + return result; + } + + static GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + vec<3, T, Q> result; + T tmp = YCoCgRColor.x - (YCoCgRColor.z >> 1); + result.g = YCoCgRColor.z + tmp; + result.b = tmp - (YCoCgRColor.y >> 1); + result.r = result.b + YCoCgRColor.y; + return result; + } + }; + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rgb2YCoCgR + ( + vec<3, T, Q> const& rgbColor + ) + { + return compute_YCoCgR::is_integer>::rgb2YCoCgR(rgbColor); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> YCoCgR2rgb + ( + vec<3, T, Q> const& YCoCgRColor + ) + { + return compute_YCoCgR::is_integer>::YCoCgR2rgb(YCoCgRColor); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/common.hpp b/thirdparty/glm/glm/gtx/common.hpp new file mode 100644 index 0000000..283f947 --- /dev/null +++ b/thirdparty/glm/glm/gtx/common.hpp @@ -0,0 +1,74 @@ +/// @ref gtx_common +/// @file glm/gtx/common.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_common GLM_GTX_common +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Provide functions to increase the compatibility with Cg and HLSL languages + +#pragma once + +// Dependencies: +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../gtc/vec1.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_common is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_common extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_common + /// @{ + + /// Returns true if x is a denormalized number + /// Numbers whose absolute value is too small to be represented in the normal format are represented in an alternate, denormalized format. + /// This format is less precise but can represent values closer to zero. + /// + /// @tparam genType Floating-point scalar or vector types. + /// + /// @see GLSL isnan man page + /// @see GLSL 4.20.8 specification, section 8.3 Common Functions + template + GLM_FUNC_DECL typename genType::bool_type isdenormal(genType const& x); + + /// Similar to 'mod' but with a different rounding and integer support. + /// Returns 'x - y * trunc(x/y)' instead of 'x - y * floor(x/y)' + /// + /// @see GLSL mod vs HLSL fmod + /// @see GLSL mod man page + template + GLM_FUNC_DECL vec fmod(vec const& v); + + /// Returns whether vector components values are within an interval. A open interval excludes its endpoints, and is denoted with square brackets. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_relational + template + GLM_FUNC_DECL vec openBounded(vec const& Value, vec const& Min, vec const& Max); + + /// Returns whether vector components values are within an interval. A closed interval includes its endpoints, and is denoted with square brackets. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or integer scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see ext_vector_relational + template + GLM_FUNC_DECL vec closeBounded(vec const& Value, vec const& Min, vec const& Max); + + /// @} +}//namespace glm + +#include "common.inl" diff --git a/thirdparty/glm/glm/gtx/common.inl b/thirdparty/glm/glm/gtx/common.inl new file mode 100644 index 0000000..4575b20 --- /dev/null +++ b/thirdparty/glm/glm/gtx/common.inl @@ -0,0 +1,125 @@ +/// @ref gtx_common + +#include +#include "../gtc/epsilon.hpp" +#include "../gtc/constants.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_fmod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + return detail::functor2::call(std::fmod, a, b); + } + }; + + template + struct compute_fmod + { + GLM_FUNC_QUALIFIER static vec call(vec const& a, vec const& b) + { + return a % b; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER bool isdenormal(T const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isdenormal' only accept floating-point inputs"); + +# if GLM_HAS_CXX11_STL + return std::fpclassify(x) == FP_SUBNORMAL; +# else + return epsilonNotEqual(x, static_cast(0), epsilon()) && std::fabs(x) < std::numeric_limits::min(); +# endif + } + + template + GLM_FUNC_QUALIFIER typename vec<1, T, Q>::bool_type isdenormal + ( + vec<1, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isdenormal' only accept floating-point inputs"); + + return typename vec<1, T, Q>::bool_type( + isdenormal(x.x)); + } + + template + GLM_FUNC_QUALIFIER typename vec<2, T, Q>::bool_type isdenormal + ( + vec<2, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isdenormal' only accept floating-point inputs"); + + return typename vec<2, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y)); + } + + template + GLM_FUNC_QUALIFIER typename vec<3, T, Q>::bool_type isdenormal + ( + vec<3, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isdenormal' only accept floating-point inputs"); + + return typename vec<3, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y), + isdenormal(x.z)); + } + + template + GLM_FUNC_QUALIFIER typename vec<4, T, Q>::bool_type isdenormal + ( + vec<4, T, Q> const& x + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'isdenormal' only accept floating-point inputs"); + + return typename vec<4, T, Q>::bool_type( + isdenormal(x.x), + isdenormal(x.y), + isdenormal(x.z), + isdenormal(x.w)); + } + + // fmod + template + GLM_FUNC_QUALIFIER genType fmod(genType x, genType y) + { + return fmod(vec<1, genType>(x), y).x; + } + + template + GLM_FUNC_QUALIFIER vec fmod(vec const& x, T y) + { + return detail::compute_fmod::is_iec559>::call(x, vec(y)); + } + + template + GLM_FUNC_QUALIFIER vec fmod(vec const& x, vec const& y) + { + return detail::compute_fmod::is_iec559>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER vec openBounded(vec const& Value, vec const& Min, vec const& Max) + { + return greaterThan(Value, Min) && lessThan(Value, Max); + } + + template + GLM_FUNC_QUALIFIER vec closeBounded(vec const& Value, vec const& Min, vec const& Max) + { + return greaterThanEqual(Value, Min) && lessThanEqual(Value, Max); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/compatibility.hpp b/thirdparty/glm/glm/gtx/compatibility.hpp new file mode 100644 index 0000000..463f86f --- /dev/null +++ b/thirdparty/glm/glm/gtx/compatibility.hpp @@ -0,0 +1,131 @@ +/// @ref gtx_compatibility +/// @file glm/gtx/compatibility.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_compatibility GLM_GTX_compatibility +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Provide functions to increase the compatibility with Cg and HLSL languages + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/quaternion.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_compatibility is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_compatibility extension included") +#endif + +#if GLM_COMPILER & GLM_COMPILER_VC +# include +#elif GLM_COMPILER & GLM_COMPILER_GCC +# include +# if(GLM_PLATFORM & GLM_PLATFORM_ANDROID) +# undef isfinite +# endif +#endif//GLM_COMPILER + +namespace glm +{ + /// @addtogroup gtx_compatibility + /// @{ + + template GLM_FUNC_QUALIFIER T lerp(T x, T y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> lerp(const vec<2, T, Q>& x, const vec<2, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER vec<3, T, Q> lerp(const vec<3, T, Q>& x, const vec<3, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> lerp(const vec<4, T, Q>& x, const vec<4, T, Q>& y, T a){return mix(x, y, a);} //!< \brief Returns x * (1.0 - a) + y * a, i.e., the linear blend of x and y using the floating-point value a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> lerp(const vec<2, T, Q>& x, const vec<2, T, Q>& y, const vec<2, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> lerp(const vec<3, T, Q>& x, const vec<3, T, Q>& y, const vec<3, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> lerp(const vec<4, T, Q>& x, const vec<4, T, Q>& y, const vec<4, T, Q>& a){return mix(x, y, a);} //!< \brief Returns the component-wise result of x * (1.0 - a) + y * a, i.e., the linear blend of x and y using vector a. The value for a is not restricted to the range [0, 1]. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER T saturate(T x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> saturate(const vec<2, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> saturate(const vec<3, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> saturate(const vec<4, T, Q>& x){return clamp(x, T(0), T(1));} //!< \brief Returns clamp(x, 0, 1) for each component in x. (From GLM_GTX_compatibility) + + template GLM_FUNC_QUALIFIER T atan2(T y, T x){return atan(y, x);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<2, T, Q> atan2(const vec<2, T, Q>& y, const vec<2, T, Q>& x){return atan(y, x);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<3, T, Q> atan2(const vec<3, T, Q>& y, const vec<3, T, Q>& x){return atan(y, x);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + template GLM_FUNC_QUALIFIER vec<4, T, Q> atan2(const vec<4, T, Q>& y, const vec<4, T, Q>& x){return atan(y, x);} //!< \brief Arc tangent. Returns an angle whose tangent is y/x. The signs of x and y are used to determine what quadrant the angle is in. The range of values returned by this function is [-PI, PI]. Results are undefined if x and y are both 0. (From GLM_GTX_compatibility) + + template GLM_FUNC_DECL bool isfinite(genType const& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<1, bool, Q> isfinite(const vec<1, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<2, bool, Q> isfinite(const vec<2, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<3, bool, Q> isfinite(const vec<3, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + template GLM_FUNC_DECL vec<4, bool, Q> isfinite(const vec<4, T, Q>& x); //!< \brief Test whether or not a scalar or each vector component is a finite value. (From GLM_GTX_compatibility) + + typedef bool bool1; //!< \brief boolean type with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, bool, highp> bool2; //!< \brief boolean type with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, bool, highp> bool3; //!< \brief boolean type with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, bool, highp> bool4; //!< \brief boolean type with 4 components. (From GLM_GTX_compatibility extension) + + typedef bool bool1x1; //!< \brief boolean matrix with 1 x 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, bool, highp> bool2x2; //!< \brief boolean matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, bool, highp> bool2x3; //!< \brief boolean matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, bool, highp> bool2x4; //!< \brief boolean matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, bool, highp> bool3x2; //!< \brief boolean matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, bool, highp> bool3x3; //!< \brief boolean matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, bool, highp> bool3x4; //!< \brief boolean matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, bool, highp> bool4x2; //!< \brief boolean matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, bool, highp> bool4x3; //!< \brief boolean matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, bool, highp> bool4x4; //!< \brief boolean matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef int int1; //!< \brief integer vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, int, highp> int2; //!< \brief integer vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, int, highp> int3; //!< \brief integer vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, int, highp> int4; //!< \brief integer vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef int int1x1; //!< \brief integer matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, int, highp> int2x2; //!< \brief integer matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, int, highp> int2x3; //!< \brief integer matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, int, highp> int2x4; //!< \brief integer matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, int, highp> int3x2; //!< \brief integer matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, int, highp> int3x3; //!< \brief integer matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, int, highp> int3x4; //!< \brief integer matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, int, highp> int4x2; //!< \brief integer matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, int, highp> int4x3; //!< \brief integer matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, int, highp> int4x4; //!< \brief integer matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef float float1; //!< \brief single-qualifier floating-point vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, float, highp> float2; //!< \brief single-qualifier floating-point vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, float, highp> float3; //!< \brief single-qualifier floating-point vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, float, highp> float4; //!< \brief single-qualifier floating-point vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef float float1x1; //!< \brief single-qualifier floating-point matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, float, highp> float2x2; //!< \brief single-qualifier floating-point matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, float, highp> float2x3; //!< \brief single-qualifier floating-point matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, float, highp> float2x4; //!< \brief single-qualifier floating-point matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, float, highp> float3x2; //!< \brief single-qualifier floating-point matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, float, highp> float3x3; //!< \brief single-qualifier floating-point matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, float, highp> float3x4; //!< \brief single-qualifier floating-point matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, float, highp> float4x2; //!< \brief single-qualifier floating-point matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, float, highp> float4x3; //!< \brief single-qualifier floating-point matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, float, highp> float4x4; //!< \brief single-qualifier floating-point matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + typedef double double1; //!< \brief double-qualifier floating-point vector with 1 component. (From GLM_GTX_compatibility extension) + typedef vec<2, double, highp> double2; //!< \brief double-qualifier floating-point vector with 2 components. (From GLM_GTX_compatibility extension) + typedef vec<3, double, highp> double3; //!< \brief double-qualifier floating-point vector with 3 components. (From GLM_GTX_compatibility extension) + typedef vec<4, double, highp> double4; //!< \brief double-qualifier floating-point vector with 4 components. (From GLM_GTX_compatibility extension) + + typedef double double1x1; //!< \brief double-qualifier floating-point matrix with 1 component. (From GLM_GTX_compatibility extension) + typedef mat<2, 2, double, highp> double2x2; //!< \brief double-qualifier floating-point matrix with 2 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 3, double, highp> double2x3; //!< \brief double-qualifier floating-point matrix with 2 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<2, 4, double, highp> double2x4; //!< \brief double-qualifier floating-point matrix with 2 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 2, double, highp> double3x2; //!< \brief double-qualifier floating-point matrix with 3 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 3, double, highp> double3x3; //!< \brief double-qualifier floating-point matrix with 3 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<3, 4, double, highp> double3x4; //!< \brief double-qualifier floating-point matrix with 3 x 4 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 2, double, highp> double4x2; //!< \brief double-qualifier floating-point matrix with 4 x 2 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 3, double, highp> double4x3; //!< \brief double-qualifier floating-point matrix with 4 x 3 components. (From GLM_GTX_compatibility extension) + typedef mat<4, 4, double, highp> double4x4; //!< \brief double-qualifier floating-point matrix with 4 x 4 components. (From GLM_GTX_compatibility extension) + + /// @} +}//namespace glm + +#include "compatibility.inl" diff --git a/thirdparty/glm/glm/gtx/compatibility.inl b/thirdparty/glm/glm/gtx/compatibility.inl new file mode 100644 index 0000000..1d49496 --- /dev/null +++ b/thirdparty/glm/glm/gtx/compatibility.inl @@ -0,0 +1,62 @@ +#include + +namespace glm +{ + // isfinite + template + GLM_FUNC_QUALIFIER bool isfinite( + genType const& x) + { +# if GLM_HAS_CXX11_STL + return std::isfinite(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_VC + return _finite(x) != 0; +# elif GLM_COMPILER & GLM_COMPILER_GCC && GLM_PLATFORM & GLM_PLATFORM_ANDROID + return _isfinite(x) != 0; +# else + if (std::numeric_limits::is_integer || std::denorm_absent == std::numeric_limits::has_denorm) + return std::numeric_limits::min() <= x && std::numeric_limits::max() >= x; + else + return -std::numeric_limits::max() <= x && std::numeric_limits::max() >= x; +# endif + } + + template + GLM_FUNC_QUALIFIER vec<1, bool, Q> isfinite( + vec<1, T, Q> const& x) + { + return vec<1, bool, Q>( + isfinite(x.x)); + } + + template + GLM_FUNC_QUALIFIER vec<2, bool, Q> isfinite( + vec<2, T, Q> const& x) + { + return vec<2, bool, Q>( + isfinite(x.x), + isfinite(x.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, bool, Q> isfinite( + vec<3, T, Q> const& x) + { + return vec<3, bool, Q>( + isfinite(x.x), + isfinite(x.y), + isfinite(x.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isfinite( + vec<4, T, Q> const& x) + { + return vec<4, bool, Q>( + isfinite(x.x), + isfinite(x.y), + isfinite(x.z), + isfinite(x.w)); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/component_wise.hpp b/thirdparty/glm/glm/gtx/component_wise.hpp new file mode 100644 index 0000000..b1caaa2 --- /dev/null +++ b/thirdparty/glm/glm/gtx/component_wise.hpp @@ -0,0 +1,77 @@ +/// @ref gtx_component_wise +/// @file glm/gtx/component_wise.hpp +/// @date 2007-05-21 / 2011-06-07 +/// @author Christophe Riccio +/// +/// @see core (dependence) +/// +/// @defgroup gtx_component_wise GLM_GTX_component_wise +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Operations between components of a type + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_component_wise is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_component_wise extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_component_wise + /// @{ + + /// Convert an integer vector to a normalized float vector. + /// If the parameter value type is already a floating qualifier type, the value is passed through. + /// @see gtx_component_wise + template + GLM_FUNC_DECL vec compNormalize(vec const& v); + + /// Convert a normalized float vector to an integer vector. + /// If the parameter value type is already a floating qualifier type, the value is passed through. + /// @see gtx_component_wise + template + GLM_FUNC_DECL vec compScale(vec const& v); + + /// Add all vector components together. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compAdd(genType const& v); + + /// Multiply all vector components together. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMul(genType const& v); + + /// Find the minimum value between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMin(genType const& v); + + /// Find the maximum value between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type compMax(genType const& v); + + /// Find the minimum float between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type fcompMin(genType const& v); + + /// Find the maximum float between single vector components. + /// @see gtx_component_wise + template + GLM_FUNC_DECL typename genType::value_type fcompMax(genType const& v); + + /// @} +}//namespace glm + +#include "component_wise.inl" diff --git a/thirdparty/glm/glm/gtx/component_wise.inl b/thirdparty/glm/glm/gtx/component_wise.inl new file mode 100644 index 0000000..f8217b2 --- /dev/null +++ b/thirdparty/glm/glm/gtx/component_wise.inl @@ -0,0 +1,147 @@ +/// @ref gtx_component_wise + +#include "../ext/scalar_common.hpp" +#include +#include + +namespace glm{ +namespace detail +{ + template + struct compute_compNormalize + {}; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + floatType const Min = static_cast(std::numeric_limits::min()); + floatType const Max = static_cast(std::numeric_limits::max()); + return (vec(v) - Min) / (Max - Min) * static_cast(2) - static_cast(1); + } + }; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return vec(v) / static_cast(std::numeric_limits::max()); + } + }; + + template + struct compute_compNormalize + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return v; + } + }; + + template + struct compute_compScale + {}; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + floatType const Max = static_cast(std::numeric_limits::max()) + static_cast(0.5); + vec const Scaled(v * Max); + vec const Result(Scaled - static_cast(0.5)); + return Result; + } + }; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return vec(vec(v) * static_cast(std::numeric_limits::max())); + } + }; + + template + struct compute_compScale + { + GLM_FUNC_QUALIFIER static vec call(vec const& v) + { + return v; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER vec compNormalize(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'compNormalize' accepts only floating-point types for 'floatType' template parameter"); + + return detail::compute_compNormalize::is_integer, std::numeric_limits::is_signed>::call(v); + } + + template + GLM_FUNC_QUALIFIER vec compScale(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'compScale' accepts only floating-point types for 'floatType' template parameter"); + + return detail::compute_compScale::is_integer, std::numeric_limits::is_signed>::call(v); + } + + template + GLM_FUNC_QUALIFIER T compAdd(vec const& v) + { + T Result(0); + for(length_t i = 0, n = v.length(); i < n; ++i) + Result += v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMul(vec const& v) + { + T Result(1); + for(length_t i = 0, n = v.length(); i < n; ++i) + Result *= v[i]; + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMin(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = min(Result, v[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER T compMax(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = max(Result, v[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER T fcompMin(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = fmin(Result, v[i]); + return Result; + } + + template + GLM_FUNC_QUALIFIER T fcompMax(vec const& v) + { + T Result(v[0]); + for(length_t i = 1, n = v.length(); i < n; ++i) + Result = fmax(Result, v[i]); + return Result; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/dual_quaternion.hpp b/thirdparty/glm/glm/gtx/dual_quaternion.hpp new file mode 100644 index 0000000..04a6070 --- /dev/null +++ b/thirdparty/glm/glm/gtx/dual_quaternion.hpp @@ -0,0 +1,272 @@ +/// @ref gtx_dual_quaternion +/// @file glm/gtx/dual_quaternion.hpp +/// @author Maksim Vorobiev (msomeone@gmail.com) +/// +/// @see core (dependence) +/// @see gtc_constants (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_dual_quaternion GLM_GTX_dual_quaternion +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines a templated dual-quaternion type and several dual-quaternion operations. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/quaternion.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_dual_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_dual_quaternion extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_dual_quaternion + /// @{ + + template + struct tdualquat + { + // -- Implementation detail -- + + typedef T value_type; + typedef qua part_type; + + // -- Data -- + + qua real, dual; + + // -- Component accesses -- + + typedef length_t length_type; + /// Return the count of components of a dual quaternion + GLM_FUNC_DECL static GLM_CONSTEXPR length_type length(){return 2;} + + GLM_FUNC_DECL part_type & operator[](length_type i); + GLM_FUNC_DECL part_type const& operator[](length_type i) const; + + // -- Implicit basic constructors -- + + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR tdualquat() GLM_DEFAULT; + GLM_DEFAULTED_FUNC_DECL GLM_CONSTEXPR tdualquat(tdualquat const& d) GLM_DEFAULT; + template + GLM_CTOR_DECL tdualquat(tdualquat const& d); + + // -- Explicit basic constructors -- + + GLM_CTOR_DECL tdualquat(qua const& real); + GLM_CTOR_DECL tdualquat(qua const& orientation, vec<3, T, Q> const& translation); + GLM_CTOR_DECL tdualquat(qua const& real, qua const& dual); + + // -- Conversion constructors -- + + template + GLM_CTOR_DECL GLM_EXPLICIT tdualquat(tdualquat const& q); + + GLM_CTOR_DECL GLM_EXPLICIT tdualquat(mat<2, 4, T, Q> const& holder_mat); + GLM_CTOR_DECL GLM_EXPLICIT tdualquat(mat<3, 4, T, Q> const& aug_mat); + + // -- Unary arithmetic operators -- + + GLM_DEFAULTED_FUNC_DECL tdualquat & operator=(tdualquat const& m) GLM_DEFAULT; + + template + GLM_FUNC_DISCARD_DECL tdualquat & operator=(tdualquat const& m); + template + GLM_FUNC_DISCARD_DECL tdualquat & operator*=(U s); + template + GLM_FUNC_DISCARD_DECL tdualquat & operator/=(U s); + }; + + // -- Unary bit operators -- + + template + GLM_FUNC_DECL tdualquat operator+(tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator-(tdualquat const& q); + + // -- Binary operators -- + + template + GLM_FUNC_DECL tdualquat operator+(tdualquat const& q, tdualquat const& p); + + template + GLM_FUNC_DECL tdualquat operator*(tdualquat const& q, tdualquat const& p); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(tdualquat const& q, vec<3, T, Q> const& v); + + template + GLM_FUNC_DECL vec<3, T, Q> operator*(vec<3, T, Q> const& v, tdualquat const& q); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(tdualquat const& q, vec<4, T, Q> const& v); + + template + GLM_FUNC_DECL vec<4, T, Q> operator*(vec<4, T, Q> const& v, tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator*(tdualquat const& q, T const& s); + + template + GLM_FUNC_DECL tdualquat operator*(T const& s, tdualquat const& q); + + template + GLM_FUNC_DECL tdualquat operator/(tdualquat const& q, T const& s); + + // -- Boolean operators -- + + template + GLM_FUNC_DECL bool operator==(tdualquat const& q1, tdualquat const& q2); + + template + GLM_FUNC_DECL bool operator!=(tdualquat const& q1, tdualquat const& q2); + + /// Creates an identity dual quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dual_quat_identity(); + + /// Returns the normalized quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat normalize(tdualquat const& q); + + /// Returns the linear interpolation of two dual quaternion. + /// + /// @see gtc_dual_quaternion + template + GLM_FUNC_DECL tdualquat lerp(tdualquat const& x, tdualquat const& y, T const& a); + + /// Returns the q inverse. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat inverse(tdualquat const& q); + + /// Converts a quaternion to a 2 * 4 matrix. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL mat<2, 4, T, Q> mat2x4_cast(tdualquat const& x); + + /// Converts a quaternion to a 3 * 4 matrix. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL mat<3, 4, T, Q> mat3x4_cast(tdualquat const& x); + + /// Converts a 2 * 4 matrix (matrix which holds real and dual parts) to a quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dualquat_cast(mat<2, 4, T, Q> const& x); + + /// Converts a 3 * 4 matrix (augmented matrix rotation + translation) to a quaternion. + /// + /// @see gtx_dual_quaternion + template + GLM_FUNC_DECL tdualquat dualquat_cast(mat<3, 4, T, Q> const& x); + + + /// Dual-quaternion of low single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_dualquat; + + /// Dual-quaternion of medium single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_dualquat; + + /// Dual-quaternion of high single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_dualquat; + + + /// Dual-quaternion of low single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_fdualquat; + + /// Dual-quaternion of medium single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_fdualquat; + + /// Dual-quaternion of high single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_fdualquat; + + + /// Dual-quaternion of low double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat lowp_ddualquat; + + /// Dual-quaternion of medium double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat mediump_ddualquat; + + /// Dual-quaternion of high double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef tdualquat highp_ddualquat; + + +#if(!defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + /// Dual-quaternion of floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_fdualquat dualquat; + + /// Dual-quaternion of single-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_fdualquat fdualquat; +#elif(defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + typedef highp_fdualquat dualquat; + typedef highp_fdualquat fdualquat; +#elif(!defined(GLM_PRECISION_HIGHP_FLOAT) && defined(GLM_PRECISION_MEDIUMP_FLOAT) && !defined(GLM_PRECISION_LOWP_FLOAT)) + typedef mediump_fdualquat dualquat; + typedef mediump_fdualquat fdualquat; +#elif(!defined(GLM_PRECISION_HIGHP_FLOAT) && !defined(GLM_PRECISION_MEDIUMP_FLOAT) && defined(GLM_PRECISION_LOWP_FLOAT)) + typedef lowp_fdualquat dualquat; + typedef lowp_fdualquat fdualquat; +#else +# error "GLM error: multiple default precision requested for single-precision floating-point types" +#endif + + +#if(!defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + /// Dual-quaternion of default double-qualifier floating-point numbers. + /// + /// @see gtx_dual_quaternion + typedef highp_ddualquat ddualquat; +#elif(defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef highp_ddualquat ddualquat; +#elif(!defined(GLM_PRECISION_HIGHP_DOUBLE) && defined(GLM_PRECISION_MEDIUMP_DOUBLE) && !defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef mediump_ddualquat ddualquat; +#elif(!defined(GLM_PRECISION_HIGHP_DOUBLE) && !defined(GLM_PRECISION_MEDIUMP_DOUBLE) && defined(GLM_PRECISION_LOWP_DOUBLE)) + typedef lowp_ddualquat ddualquat; +#else +# error "GLM error: Multiple default precision requested for double-precision floating-point types" +#endif + + /// @} +} //namespace glm + +#include "dual_quaternion.inl" diff --git a/thirdparty/glm/glm/gtx/dual_quaternion.inl b/thirdparty/glm/glm/gtx/dual_quaternion.inl new file mode 100644 index 0000000..3a04160 --- /dev/null +++ b/thirdparty/glm/glm/gtx/dual_quaternion.inl @@ -0,0 +1,352 @@ +/// @ref gtx_dual_quaternion + +#include "../geometric.hpp" +#include + +namespace glm +{ + // -- Component accesses -- + + template + GLM_FUNC_QUALIFIER typename tdualquat::part_type & tdualquat::operator[](typename tdualquat::length_type i) + { + assert(i >= 0 && i < this->length()); + return (&real)[i]; + } + + template + GLM_FUNC_QUALIFIER typename tdualquat::part_type const& tdualquat::operator[](typename tdualquat::length_type i) const + { + assert(i >= 0 && i < this->length()); + return (&real)[i]; + } + + // -- Implicit basic constructors -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat() +# if GLM_CONFIG_DEFAULTED_FUNCTIONS != GLM_DISABLE + : real(qua()) + , dual(qua::wxyz(0, 0, 0, 0)) +# endif + {} + + template + GLM_DEFAULTED_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& d) + : real(d.real) + , dual(d.dual) + {} +# endif + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& d) + : real(d.real) + , dual(d.dual) + {} + + // -- Explicit basic constructors -- + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& r) + : real(r), dual(qua::wxyz(0, 0, 0, 0)) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& q, vec<3, T, Q> const& p) + : real(q), dual(qua::wxyz( + T(-0.5) * ( p.x*q.x + p.y*q.y + p.z*q.z), + T(+0.5) * ( p.x*q.w + p.y*q.z - p.z*q.y), + T(+0.5) * (-p.x*q.z + p.y*q.w + p.z*q.x), + T(+0.5) * ( p.x*q.y - p.y*q.x + p.z*q.w))) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(qua const& r, qua const& d) + : real(r), dual(d) + {} + + // -- Conversion constructors -- + + template + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(tdualquat const& q) + : real(q.real) + , dual(q.dual) + {} + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(mat<2, 4, T, Q> const& m) + { + *this = dualquat_cast(m); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR tdualquat::tdualquat(mat<3, 4, T, Q> const& m) + { + *this = dualquat_cast(m); + } + + // -- Unary arithmetic operators -- + +# if GLM_CONFIG_DEFAULTED_FUNCTIONS == GLM_DISABLE + template + GLM_DEFAULTED_FUNC_QUALIFIER tdualquat & tdualquat::operator=(tdualquat const& q) + { + this->real = q.real; + this->dual = q.dual; + return *this; + } +# endif + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator=(tdualquat const& q) + { + this->real = q.real; + this->dual = q.dual; + return *this; + } + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator*=(U s) + { + this->real *= static_cast(s); + this->dual *= static_cast(s); + return *this; + } + + template + template + GLM_FUNC_QUALIFIER tdualquat & tdualquat::operator/=(U s) + { + this->real /= static_cast(s); + this->dual /= static_cast(s); + return *this; + } + + // -- Unary bit operators -- + + template + GLM_FUNC_QUALIFIER tdualquat operator+(tdualquat const& q) + { + return q; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator-(tdualquat const& q) + { + return tdualquat(-q.real, -q.dual); + } + + // -- Binary operators -- + + template + GLM_FUNC_QUALIFIER tdualquat operator+(tdualquat const& q, tdualquat const& p) + { + return tdualquat(q.real + p.real,q.dual + p.dual); + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(tdualquat const& p, tdualquat const& o) + { + return tdualquat(p.real * o.real,p.real * o.dual + p.dual * o.real); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(tdualquat const& q, vec<3, T, Q> const& v) + { + vec<3, T, Q> const real_v3(q.real.x,q.real.y,q.real.z); + vec<3, T, Q> const dual_v3(q.dual.x,q.dual.y,q.dual.z); + return (cross(real_v3, cross(real_v3,v) + v * q.real.w + dual_v3) + dual_v3 * q.real.w - real_v3 * q.dual.w) * T(2) + v; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> operator*(vec<3, T, Q> const& v, tdualquat const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(tdualquat const& q, vec<4, T, Q> const& v) + { + return vec<4, T, Q>(q * vec<3, T, Q>(v), v.w); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> operator*(vec<4, T, Q> const& v, tdualquat const& q) + { + return glm::inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(tdualquat const& q, T const& s) + { + return tdualquat(q.real * s, q.dual * s); + } + + template + GLM_FUNC_QUALIFIER tdualquat operator*(T const& s, tdualquat const& q) + { + return q * s; + } + + template + GLM_FUNC_QUALIFIER tdualquat operator/(tdualquat const& q, T const& s) + { + return tdualquat(q.real / s, q.dual / s); + } + + // -- Boolean operators -- + + template + GLM_FUNC_QUALIFIER bool operator==(tdualquat const& q1, tdualquat const& q2) + { + return (q1.real == q2.real) && (q1.dual == q2.dual); + } + + template + GLM_FUNC_QUALIFIER bool operator!=(tdualquat const& q1, tdualquat const& q2) + { + return (q1.real != q2.real) || (q1.dual != q2.dual); + } + + // -- Operations -- + + template + GLM_FUNC_QUALIFIER tdualquat dual_quat_identity() + { + return tdualquat( + qua::wxyz(static_cast(1), static_cast(0), static_cast(0), static_cast(0)), + qua::wxyz(static_cast(0), static_cast(0), static_cast(0), static_cast(0))); + } + + template + GLM_FUNC_QUALIFIER tdualquat normalize(tdualquat const& q) + { + return q / length(q.real); + } + + template + GLM_FUNC_QUALIFIER tdualquat lerp(tdualquat const& x, tdualquat const& y, T const& a) + { + // Dual Quaternion Linear blend aka DLB: + // Lerp is only defined in [0, 1] + assert(a >= static_cast(0)); + assert(a <= static_cast(1)); + T const k = dot(x.real,y.real) < static_cast(0) ? -a : a; + T const one(1); + return tdualquat(x * (one - a) + y * k); + } + + template + GLM_FUNC_QUALIFIER tdualquat inverse(tdualquat const& q) + { + const glm::qua real = conjugate(q.real); + const glm::qua dual = conjugate(q.dual); + return tdualquat(real, dual + (real * (-2.0f * dot(real,dual)))); + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> mat2x4_cast(tdualquat const& x) + { + return mat<2, 4, T, Q>( x[0].x, x[0].y, x[0].z, x[0].w, x[1].x, x[1].y, x[1].z, x[1].w ); + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> mat3x4_cast(tdualquat const& x) + { + qua r = x.real / length2(x.real); + + qua const rr(r.w * x.real.w, r.x * x.real.x, r.y * x.real.y, r.z * x.real.z); + r *= static_cast(2); + + T const xy = r.x * x.real.y; + T const xz = r.x * x.real.z; + T const yz = r.y * x.real.z; + T const wx = r.w * x.real.x; + T const wy = r.w * x.real.y; + T const wz = r.w * x.real.z; + + vec<4, T, Q> const a( + rr.w + rr.x - rr.y - rr.z, + xy - wz, + xz + wy, + -(x.dual.w * r.x - x.dual.x * r.w + x.dual.y * r.z - x.dual.z * r.y)); + + vec<4, T, Q> const b( + xy + wz, + rr.w + rr.y - rr.x - rr.z, + yz - wx, + -(x.dual.w * r.y - x.dual.x * r.z - x.dual.y * r.w + x.dual.z * r.x)); + + vec<4, T, Q> const c( + xz - wy, + yz + wx, + rr.w + rr.z - rr.x - rr.y, + -(x.dual.w * r.z + x.dual.x * r.y - x.dual.y * r.x - x.dual.z * r.w)); + + return mat<3, 4, T, Q>(a, b, c); + } + + template + GLM_FUNC_QUALIFIER tdualquat dualquat_cast(mat<2, 4, T, Q> const& x) + { + return tdualquat( + qua::wxyz( x[0].w, x[0].x, x[0].y, x[0].z ), + qua::wxyz( x[1].w, x[1].x, x[1].y, x[1].z )); + } + + template + GLM_FUNC_QUALIFIER tdualquat dualquat_cast(mat<3, 4, T, Q> const& x) + { + qua real; + + T const trace = x[0].x + x[1].y + x[2].z; + if(trace > static_cast(0)) + { + T const r = sqrt(T(1) + trace); + T const invr = static_cast(0.5) / r; + real.w = static_cast(0.5) * r; + real.x = (x[2].y - x[1].z) * invr; + real.y = (x[0].z - x[2].x) * invr; + real.z = (x[1].x - x[0].y) * invr; + } + else if(x[0].x > x[1].y && x[0].x > x[2].z) + { + T const r = sqrt(T(1) + x[0].x - x[1].y - x[2].z); + T const invr = static_cast(0.5) / r; + real.x = static_cast(0.5)*r; + real.y = (x[1].x + x[0].y) * invr; + real.z = (x[0].z + x[2].x) * invr; + real.w = (x[2].y - x[1].z) * invr; + } + else if(x[1].y > x[2].z) + { + T const r = sqrt(T(1) + x[1].y - x[0].x - x[2].z); + T const invr = static_cast(0.5) / r; + real.x = (x[1].x + x[0].y) * invr; + real.y = static_cast(0.5) * r; + real.z = (x[2].y + x[1].z) * invr; + real.w = (x[0].z - x[2].x) * invr; + } + else + { + T const r = sqrt(T(1) + x[2].z - x[0].x - x[1].y); + T const invr = static_cast(0.5) / r; + real.x = (x[0].z + x[2].x) * invr; + real.y = (x[2].y + x[1].z) * invr; + real.z = static_cast(0.5) * r; + real.w = (x[1].x - x[0].y) * invr; + } + + qua dual; + dual.x = static_cast(0.5) * ( x[0].w * real.w + x[1].w * real.z - x[2].w * real.y); + dual.y = static_cast(0.5) * (-x[0].w * real.z + x[1].w * real.w + x[2].w * real.x); + dual.z = static_cast(0.5) * ( x[0].w * real.y - x[1].w * real.x + x[2].w * real.w); + dual.w = -static_cast(0.5) * ( x[0].w * real.x + x[1].w * real.y + x[2].w * real.z); + return tdualquat(real, dual); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/easing.hpp b/thirdparty/glm/glm/gtx/easing.hpp new file mode 100644 index 0000000..50ed903 --- /dev/null +++ b/thirdparty/glm/glm/gtx/easing.hpp @@ -0,0 +1,217 @@ +/// @ref gtx_easing +/// @file glm/gtx/easing.hpp +/// @author Robert Chisholm +/// +/// @see core (dependence) +/// +/// @defgroup gtx_easing GLM_GTX_easing +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Easing functions for animations and transitions +/// All functions take a parameter x in the range [0.0,1.0] +/// +/// Based on the AHEasing project of Warren Moore (https://github.com/warrenm/AHEasing) + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../detail/qualifier.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_easing is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_easing extension included") +#endif + +namespace glm{ + /// @addtogroup gtx_easing + /// @{ + + /// Modelled after the line y = x + /// @see gtx_easing + template + GLM_FUNC_DECL genType linearInterpolation(genType const & a); + + /// Modelled after the parabola y = x^2 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseIn(genType const & a); + + /// Modelled after the parabola y = -x^2 + 2x + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseOut(genType const & a); + + /// Modelled after the piecewise quadratic + /// y = (1/2)((2x)^2) ; [0, 0.5) + /// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quadraticEaseInOut(genType const & a); + + /// Modelled after the cubic y = x^3 + template + GLM_FUNC_DECL genType cubicEaseIn(genType const & a); + + /// Modelled after the cubic y = (x - 1)^3 + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType cubicEaseOut(genType const & a); + + /// Modelled after the piecewise cubic + /// y = (1/2)((2x)^3) ; [0, 0.5) + /// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType cubicEaseInOut(genType const & a); + + /// Modelled after the quartic x^4 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseIn(genType const & a); + + /// Modelled after the quartic y = 1 - (x - 1)^4 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseOut(genType const & a); + + /// Modelled after the piecewise quartic + /// y = (1/2)((2x)^4) ; [0, 0.5) + /// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quarticEaseInOut(genType const & a); + + /// Modelled after the quintic y = x^5 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseIn(genType const & a); + + /// Modelled after the quintic y = (x - 1)^5 + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseOut(genType const & a); + + /// Modelled after the piecewise quintic + /// y = (1/2)((2x)^5) ; [0, 0.5) + /// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType quinticEaseInOut(genType const & a); + + /// Modelled after quarter-cycle of sine wave + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseIn(genType const & a); + + /// Modelled after quarter-cycle of sine wave (different phase) + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseOut(genType const & a); + + /// Modelled after half sine wave + /// @see gtx_easing + template + GLM_FUNC_DECL genType sineEaseInOut(genType const & a); + + /// Modelled after shifted quadrant IV of unit circle + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseIn(genType const & a); + + /// Modelled after shifted quadrant II of unit circle + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseOut(genType const & a); + + /// Modelled after the piecewise circular function + /// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5) + /// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType circularEaseInOut(genType const & a); + + /// Modelled after the exponential function y = 2^(10(x - 1)) + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseIn(genType const & a); + + /// Modelled after the exponential function y = -2^(-10x) + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseOut(genType const & a); + + /// Modelled after the piecewise exponential + /// y = (1/2)2^(10(2x - 1)) ; [0,0.5) + /// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType exponentialEaseInOut(genType const & a); + + /// Modelled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1)) + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseIn(genType const & a); + + /// Modelled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1 + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseOut(genType const & a); + + /// Modelled after the piecewise exponentially-damped sine wave: + /// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5) + /// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1] + /// @see gtx_easing + template + GLM_FUNC_DECL genType elasticEaseInOut(genType const & a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseIn(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseOut(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseInOut(genType const& a); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseIn(genType const& a, genType const& o); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseOut(genType const& a, genType const& o); + + /// @param a parameter + /// @param o Optional overshoot modifier + /// @see gtx_easing + template + GLM_FUNC_DECL genType backEaseInOut(genType const& a, genType const& o); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseIn(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseOut(genType const& a); + + /// @see gtx_easing + template + GLM_FUNC_DECL genType bounceEaseInOut(genType const& a); + + /// @} +}//namespace glm + +#include "easing.inl" diff --git a/thirdparty/glm/glm/gtx/easing.inl b/thirdparty/glm/glm/gtx/easing.inl new file mode 100644 index 0000000..b599c30 --- /dev/null +++ b/thirdparty/glm/glm/gtx/easing.inl @@ -0,0 +1,436 @@ +/// @ref gtx_easing + +#include + +namespace glm{ + + template + GLM_FUNC_QUALIFIER genType linearInterpolation(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a; + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a; + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return -(a * (a - static_cast(2))); + } + + template + GLM_FUNC_QUALIFIER genType quadraticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(2) * a * a; + } + else + { + return (-static_cast(2) * a * a) + (4 * a) - one(); + } + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = a - one(); + return f * f * f + one(); + } + + template + GLM_FUNC_QUALIFIER genType cubicEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if (a < static_cast(0.5)) + { + return static_cast(4) * a * a * a; + } + else + { + genType const f = ((static_cast(2) * a) - static_cast(2)); + return static_cast(0.5) * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = (a - one()); + return f * f * f * (one() - a) + one(); + } + + template + GLM_FUNC_QUALIFIER genType quarticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(8) * a * a * a * a; + } + else + { + genType const f = (a - one()); + return -static_cast(8) * f * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return a * a * a * a * a; + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType const f = (a - one()); + return f * f * f * f * f + one(); + } + + template + GLM_FUNC_QUALIFIER genType quinticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(16) * a * a * a * a * a; + } + else + { + genType const f = ((static_cast(2) * a) - static_cast(2)); + return static_cast(0.5) * f * f * f * f * f + one(); + } + } + + template + GLM_FUNC_QUALIFIER genType sineEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sin((a - one()) * half_pi()) + one(); + } + + template + GLM_FUNC_QUALIFIER genType sineEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sin(a * half_pi()); + } + + template + GLM_FUNC_QUALIFIER genType sineEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return static_cast(0.5) * (one() - cos(a * pi())); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return one() - sqrt(one() - (a * a)); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return sqrt((static_cast(2) - a) * a); + } + + template + GLM_FUNC_QUALIFIER genType circularEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(0.5) * (one() - std::sqrt(one() - static_cast(4) * (a * a))); + } + else + { + return static_cast(0.5) * (std::sqrt(-((static_cast(2) * a) - static_cast(3)) * ((static_cast(2) * a) - one())) + one()); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a <= zero()) + return a; + else + { + genType const Complementary = a - one(); + genType const Two = static_cast(2); + + return glm::pow(Two, Complementary * static_cast(10)); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a >= one()) + return a; + else + { + return one() - glm::pow(static_cast(2), -static_cast(10) * a); + } + } + + template + GLM_FUNC_QUALIFIER genType exponentialEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + return static_cast(0.5) * glm::pow(static_cast(2), (static_cast(20) * a) - static_cast(10)); + else + return -static_cast(0.5) * glm::pow(static_cast(2), (-static_cast(20) * a) + static_cast(10)) + one(); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return std::sin(static_cast(13) * half_pi() * a) * glm::pow(static_cast(2), static_cast(10) * (a - one())); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return std::sin(-static_cast(13) * half_pi() * (a + one())) * glm::pow(static_cast(2), -static_cast(10) * a) + one(); + } + + template + GLM_FUNC_QUALIFIER genType elasticEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + return static_cast(0.5) * std::sin(static_cast(13) * half_pi() * (static_cast(2) * a)) * glm::pow(static_cast(2), static_cast(10) * ((static_cast(2) * a) - one())); + else + return static_cast(0.5) * (std::sin(-static_cast(13) * half_pi() * ((static_cast(2) * a - one()) + one())) * glm::pow(static_cast(2), -static_cast(10) * (static_cast(2) * a - one())) + static_cast(2)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseIn(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType z = ((o + one()) * a) - o; + return (a * a * z); + } + + template + GLM_FUNC_QUALIFIER genType backEaseOut(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType n = a - one(); + genType z = ((o + one()) * n) + o; + return (n * n * z) + one(); + } + + template + GLM_FUNC_QUALIFIER genType backEaseInOut(genType const& a, genType const& o) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + genType s = o * static_cast(1.525); + genType x = static_cast(0.5); + genType n = a / static_cast(0.5); + + if (n < static_cast(1)) + { + genType z = ((s + static_cast(1)) * n) - s; + genType m = n * n * z; + return x * m; + } + else + { + n -= static_cast(2); + genType z = ((s + static_cast(1)) * n) + s; + genType m = (n*n*z) + static_cast(2); + return x * m; + } + } + + template + GLM_FUNC_QUALIFIER genType backEaseIn(genType const& a) + { + return backEaseIn(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseOut(genType const& a) + { + return backEaseOut(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType backEaseInOut(genType const& a) + { + return backEaseInOut(a, static_cast(1.70158)); + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(4.0 / 11.0)) + { + return (static_cast(121) * a * a) / static_cast(16); + } + else if(a < static_cast(8.0 / 11.0)) + { + return (static_cast(363.0 / 40.0) * a * a) - (static_cast(99.0 / 10.0) * a) + static_cast(17.0 / 5.0); + } + else if(a < static_cast(9.0 / 10.0)) + { + return (static_cast(4356.0 / 361.0) * a * a) - (static_cast(35442.0 / 1805.0) * a) + static_cast(16061.0 / 1805.0); + } + else + { + return (static_cast(54.0 / 5.0) * a * a) - (static_cast(513.0 / 25.0) * a) + static_cast(268.0 / 25.0); + } + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseIn(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + return one() - bounceEaseOut(one() - a); + } + + template + GLM_FUNC_QUALIFIER genType bounceEaseInOut(genType const& a) + { + // Only defined in [0, 1] + assert(a >= zero()); + assert(a <= one()); + + if(a < static_cast(0.5)) + { + return static_cast(0.5) * (one() - bounceEaseOut(one() - a * static_cast(2))); + } + else + { + return static_cast(0.5) * bounceEaseOut(a * static_cast(2) - one()) + static_cast(0.5); + } + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/euler_angles.hpp b/thirdparty/glm/glm/gtx/euler_angles.hpp new file mode 100644 index 0000000..5d67d8e --- /dev/null +++ b/thirdparty/glm/glm/gtx/euler_angles.hpp @@ -0,0 +1,333 @@ +/// @ref gtx_euler_angles +/// @file glm/gtx/euler_angles.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_euler_angles GLM_GTX_euler_angles +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build matrices from Euler angles. +/// +/// Extraction of Euler angles from rotation matrix. +/// Based on the original paper 2014 Mike Day - Extracting Euler Angles from a Rotation Matrix. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_euler_angles is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_euler_angles extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_euler_angles + /// @{ + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle X. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleX( + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle Y. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleY( + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from an euler angle Z. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZ( + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about X-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleX( + T const & angleX, T const & angularVelocityX); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about Y-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleY( + T const & angleY, T const & angularVelocityY); + + /// Creates a 3D 4 * 4 homogeneous derived matrix from the rotation matrix about Z-axis. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> derivedEulerAngleZ( + T const & angleZ, T const & angularVelocityZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXY( + T const& angleX, + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYX( + T const& angleY, + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZ( + T const& angleX, + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZX( + T const& angle, + T const& angleX); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZ( + T const& angleY, + T const& angleZ); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZY( + T const& angleZ, + T const& angleY); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXYZ( + T const& t1, + T const& t2, + T const& t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYXZ( + T const& yaw, + T const& pitch, + T const& roll); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXYX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYXY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZYZ( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZXZ( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (X * Z * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleXZY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * Z * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleYZX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * Y * X). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZYX( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Z * X * Y). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> eulerAngleZXY( + T const & t1, + T const & t2, + T const & t3); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, defaultp> yawPitchRoll( + T const& yaw, + T const& pitch, + T const& roll); + + /// Creates a 2D 2 * 2 rotation matrix from an euler angle. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<2, 2, T, defaultp> orientate2(T const& angle); + + /// Creates a 2D 4 * 4 homogeneous rotation matrix from an euler angle. + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<3, 3, T, defaultp> orientate3(T const& angle); + + /// Creates a 3D 3 * 3 rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<3, 3, T, Q> orientate3(vec<3, T, Q> const& angles); + + /// Creates a 3D 4 * 4 homogeneous rotation matrix from euler angles (Y * X * Z). + /// @see gtx_euler_angles + template + GLM_FUNC_DECL mat<4, 4, T, Q> orientate4(vec<3, T, Q> const& angles); + + /// Extracts the (X * Y * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleXYZ(mat<4, 4, T, defaultp> const& M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * X * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleYXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Z * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleXZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Y * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleXYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * X * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleYXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * Z * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleYZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * Y * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleZYZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * X * Z) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleZXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (X * Z * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleXZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Y * Z * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleYZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * Y * X) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleZYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// Extracts the (Z * X * Y) Euler angles from the rotation matrix M + /// @see gtx_euler_angles + template + GLM_FUNC_DISCARD_DECL void extractEulerAngleZXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3); + + /// @} +}//namespace glm + +#include "euler_angles.inl" diff --git a/thirdparty/glm/glm/gtx/euler_angles.inl b/thirdparty/glm/glm/gtx/euler_angles.inl new file mode 100644 index 0000000..85f22b9 --- /dev/null +++ b/thirdparty/glm/glm/gtx/euler_angles.inl @@ -0,0 +1,899 @@ +/// @ref gtx_euler_angles + +#include "compatibility.hpp" // glm::atan2 + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleX + ( + T const& angleX + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + + return mat<4, 4, T, defaultp>( + T(1), T(0), T(0), T(0), + T(0), cosX, sinX, T(0), + T(0),-sinX, cosX, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleY + ( + T const& angleY + ) + { + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, T(0), -sinY, T(0), + T(0), T(1), T(0), T(0), + sinY, T(0), cosY, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZ + ( + T const& angleZ + ) + { + T cosZ = glm::cos(angleZ); + T sinZ = glm::sin(angleZ); + + return mat<4, 4, T, defaultp>( + cosZ, sinZ, T(0), T(0), + -sinZ, cosZ, T(0), T(0), + T(0), T(0), T(1), T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleX + ( + T const & angleX, + T const & angularVelocityX + ) + { + T cosX = glm::cos(angleX) * angularVelocityX; + T sinX = glm::sin(angleX) * angularVelocityX; + + return mat<4, 4, T, defaultp>( + T(0), T(0), T(0), T(0), + T(0),-sinX, cosX, T(0), + T(0),-cosX,-sinX, T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleY + ( + T const & angleY, + T const & angularVelocityY + ) + { + T cosY = glm::cos(angleY) * angularVelocityY; + T sinY = glm::sin(angleY) * angularVelocityY; + + return mat<4, 4, T, defaultp>( + -sinY, T(0), -cosY, T(0), + T(0), T(0), T(0), T(0), + cosY, T(0), -sinY, T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> derivedEulerAngleZ + ( + T const & angleZ, + T const & angularVelocityZ + ) + { + T cosZ = glm::cos(angleZ) * angularVelocityZ; + T sinZ = glm::sin(angleZ) * angularVelocityZ; + + return mat<4, 4, T, defaultp>( + -sinZ, cosZ, T(0), T(0), + -cosZ, -sinZ, T(0), T(0), + T(0), T(0), T(0), T(0), + T(0), T(0), T(0), T(0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXY + ( + T const& angleX, + T const& angleY + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, -sinX * -sinY, cosX * -sinY, T(0), + T(0), cosX, sinX, T(0), + sinY, -sinX * cosY, cosX * cosY, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYX + ( + T const& angleY, + T const& angleX + ) + { + T cosX = glm::cos(angleX); + T sinX = glm::sin(angleX); + T cosY = glm::cos(angleY); + T sinY = glm::sin(angleY); + + return mat<4, 4, T, defaultp>( + cosY, 0, -sinY, T(0), + sinY * sinX, cosX, cosY * sinX, T(0), + sinY * cosX, -sinX, cosY * cosX, T(0), + T(0), T(0), T(0), T(1)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZ + ( + T const& angleX, + T const& angleZ + ) + { + return eulerAngleX(angleX) * eulerAngleZ(angleZ); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZX + ( + T const& angleZ, + T const& angleX + ) + { + return eulerAngleZ(angleZ) * eulerAngleX(angleX); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZ + ( + T const& angleY, + T const& angleZ + ) + { + return eulerAngleY(angleY) * eulerAngleZ(angleZ); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZY + ( + T const& angleZ, + T const& angleY + ) + { + return eulerAngleZ(angleZ) * eulerAngleY(angleY); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXYZ + ( + T const& t1, + T const& t2, + T const& t3 + ) + { + T c1 = glm::cos(-t1); + T c2 = glm::cos(-t2); + T c3 = glm::cos(-t3); + T s1 = glm::sin(-t1); + T s2 = glm::sin(-t2); + T s3 = glm::sin(-t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2 * c3; + Result[0][1] =-c1 * s3 + s1 * s2 * c3; + Result[0][2] = s1 * s3 + c1 * s2 * c3; + Result[0][3] = static_cast(0); + Result[1][0] = c2 * s3; + Result[1][1] = c1 * c3 + s1 * s2 * s3; + Result[1][2] =-s1 * c3 + c1 * s2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] =-s2; + Result[2][1] = s1 * c2; + Result[2][2] = c1 * c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYXZ + ( + T const& yaw, + T const& pitch, + T const& roll + ) + { + T tmp_ch = glm::cos(yaw); + T tmp_sh = glm::sin(yaw); + T tmp_cp = glm::cos(pitch); + T tmp_sp = glm::sin(pitch); + T tmp_cb = glm::cos(roll); + T tmp_sb = glm::sin(roll); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = tmp_ch * tmp_cb + tmp_sh * tmp_sp * tmp_sb; + Result[0][1] = tmp_sb * tmp_cp; + Result[0][2] = -tmp_sh * tmp_cb + tmp_ch * tmp_sp * tmp_sb; + Result[0][3] = static_cast(0); + Result[1][0] = -tmp_ch * tmp_sb + tmp_sh * tmp_sp * tmp_cb; + Result[1][1] = tmp_cb * tmp_cp; + Result[1][2] = tmp_sb * tmp_sh + tmp_ch * tmp_sp * tmp_cb; + Result[1][3] = static_cast(0); + Result[2][0] = tmp_sh * tmp_cp; + Result[2][1] = -tmp_sp; + Result[2][2] = tmp_ch * tmp_cp; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2; + Result[0][1] = c1 * s2; + Result[0][2] = s1 * s2; + Result[0][3] = static_cast(0); + Result[1][0] =-c3 * s2; + Result[1][1] = c1 * c2 * c3 - s1 * s3; + Result[1][2] = c1 * s3 + c2 * c3 * s1; + Result[1][3] = static_cast(0); + Result[2][0] = s2 * s3; + Result[2][1] =-c3 * s1 - c1 * c2 * s3; + Result[2][2] = c1 * c3 - c2 * s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXYX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2; + Result[0][1] = s1 * s2; + Result[0][2] =-c1 * s2; + Result[0][3] = static_cast(0); + Result[1][0] = s2 * s3; + Result[1][1] = c1 * c3 - c2 * s1 * s3; + Result[1][2] = c3 * s1 + c1 * c2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s2; + Result[2][1] =-c1 * s3 - c2 * c3 * s1; + Result[2][2] = c1 * c2 * c3 - s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYXY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - c2 * s1 * s3; + Result[0][1] = s2* s3; + Result[0][2] =-c3 * s1 - c1 * c2 * s3; + Result[0][3] = static_cast(0); + Result[1][0] = s1 * s2; + Result[1][1] = c2; + Result[1][2] = c1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s3 + c2 * c3 * s1; + Result[2][1] =-c3 * s2; + Result[2][2] = c1 * c2 * c3 - s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2 * c3 - s1 * s3; + Result[0][1] = c3 * s2; + Result[0][2] =-c1 * s3 - c2 * c3 * s1; + Result[0][3] = static_cast(0); + Result[1][0] =-c1 * s2; + Result[1][1] = c2; + Result[1][2] = s1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s1 + c1 * c2 * s3; + Result[2][1] = s2 * s3; + Result[2][2] = c1 * c3 - c2 * s1 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZYZ + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2 * c3 - s1 * s3; + Result[0][1] = c1 * s3 + c2 * c3 * s1; + Result[0][2] =-c3 * s2; + Result[0][3] = static_cast(0); + Result[1][0] =-c3 * s1 - c1 * c2 * s3; + Result[1][1] = c1 * c3 - c2 * s1 * s3; + Result[1][2] = s2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s2; + Result[2][1] = s1 * s2; + Result[2][2] = c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZXZ + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - c2 * s1 * s3; + Result[0][1] = c3 * s1 + c1 * c2 * s3; + Result[0][2] = s2 *s3; + Result[0][3] = static_cast(0); + Result[1][0] =-c1 * s3 - c2 * c3 * s1; + Result[1][1] = c1 * c2 * c3 - s1 * s3; + Result[1][2] = c3 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = s1 * s2; + Result[2][1] =-c1 * s2; + Result[2][2] = c2; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleXZY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c2 * c3; + Result[0][1] = s1 * s3 + c1 * c3 * s2; + Result[0][2] = c3 * s1 * s2 - c1 * s3; + Result[0][3] = static_cast(0); + Result[1][0] =-s2; + Result[1][1] = c1 * c2; + Result[1][2] = c2 * s1; + Result[1][3] = static_cast(0); + Result[2][0] = c2 * s3; + Result[2][1] = c1 * s2 * s3 - c3 * s1; + Result[2][2] = c1 * c3 + s1 * s2 *s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleYZX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2; + Result[0][1] = s2; + Result[0][2] =-c2 * s1; + Result[0][3] = static_cast(0); + Result[1][0] = s1 * s3 - c1 * c3 * s2; + Result[1][1] = c2 * c3; + Result[1][2] = c1 * s3 + c3 * s1 * s2; + Result[1][3] = static_cast(0); + Result[2][0] = c3 * s1 + c1 * s2 * s3; + Result[2][1] =-c2 * s3; + Result[2][2] = c1 * c3 - s1 * s2 * s3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZYX + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c2; + Result[0][1] = c2 * s1; + Result[0][2] =-s2; + Result[0][3] = static_cast(0); + Result[1][0] = c1 * s2 * s3 - c3 * s1; + Result[1][1] = c1 * c3 + s1 * s2 * s3; + Result[1][2] = c2 * s3; + Result[1][3] = static_cast(0); + Result[2][0] = s1 * s3 + c1 * c3 * s2; + Result[2][1] = c3 * s1 * s2 - c1 * s3; + Result[2][2] = c2 * c3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> eulerAngleZXY + ( + T const & t1, + T const & t2, + T const & t3 + ) + { + T c1 = glm::cos(t1); + T s1 = glm::sin(t1); + T c2 = glm::cos(t2); + T s2 = glm::sin(t2); + T c3 = glm::cos(t3); + T s3 = glm::sin(t3); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = c1 * c3 - s1 * s2 * s3; + Result[0][1] = c3 * s1 + c1 * s2 * s3; + Result[0][2] =-c2 * s3; + Result[0][3] = static_cast(0); + Result[1][0] =-c2 * s1; + Result[1][1] = c1 * c2; + Result[1][2] = s2; + Result[1][3] = static_cast(0); + Result[2][0] = c1 * s3 + c3 * s1 * s2; + Result[2][1] = s1 * s3 - c1 * c3 * s2; + Result[2][2] = c2 * c3; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> yawPitchRoll + ( + T const& yaw, + T const& pitch, + T const& roll + ) + { + T tmp_ch = glm::cos(yaw); + T tmp_sh = glm::sin(yaw); + T tmp_cp = glm::cos(pitch); + T tmp_sp = glm::sin(pitch); + T tmp_cb = glm::cos(roll); + T tmp_sb = glm::sin(roll); + + mat<4, 4, T, defaultp> Result; + Result[0][0] = tmp_ch * tmp_cb + tmp_sh * tmp_sp * tmp_sb; + Result[0][1] = tmp_sb * tmp_cp; + Result[0][2] = -tmp_sh * tmp_cb + tmp_ch * tmp_sp * tmp_sb; + Result[0][3] = static_cast(0); + Result[1][0] = -tmp_ch * tmp_sb + tmp_sh * tmp_sp * tmp_cb; + Result[1][1] = tmp_cb * tmp_cp; + Result[1][2] = tmp_sb * tmp_sh + tmp_ch * tmp_sp * tmp_cb; + Result[1][3] = static_cast(0); + Result[2][0] = tmp_sh * tmp_cp; + Result[2][1] = -tmp_sp; + Result[2][2] = tmp_ch * tmp_cp; + Result[2][3] = static_cast(0); + Result[3][0] = static_cast(0); + Result[3][1] = static_cast(0); + Result[3][2] = static_cast(0); + Result[3][3] = static_cast(1); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, defaultp> orientate2 + ( + T const& angle + ) + { + T c = glm::cos(angle); + T s = glm::sin(angle); + + mat<2, 2, T, defaultp> Result; + Result[0][0] = c; + Result[0][1] = s; + Result[1][0] = -s; + Result[1][1] = c; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, defaultp> orientate3 + ( + T const& angle + ) + { + T c = glm::cos(angle); + T s = glm::sin(angle); + + mat<3, 3, T, defaultp> Result; + Result[0][0] = c; + Result[0][1] = s; + Result[0][2] = T(0.0); + Result[1][0] = -s; + Result[1][1] = c; + Result[1][2] = T(0.0); + Result[2][0] = T(0.0); + Result[2][1] = T(0.0); + Result[2][2] = T(1.0); + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> orientate3 + ( + vec<3, T, Q> const& angles + ) + { + return mat<3, 3, T, Q>(yawPitchRoll(angles.z, angles.x, angles.y)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> orientate4 + ( + vec<3, T, Q> const& angles + ) + { + return yawPitchRoll(angles.z, angles.x, angles.y); + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXYZ(mat<4, 4, T, defaultp> const& M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][1], M[2][2]); + T C2 = glm::sqrt(M[0][0]*M[0][0] + M[1][0]*M[1][0]); + T T2 = glm::atan2(-M[2][0], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[0][2] - C1*M[0][1], C1*M[1][1] - S1*M[1][2 ]); + t1 = -T1; + t2 = -T2; + t3 = -T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][0], M[2][2]); + T C2 = glm::sqrt(M[0][1]*M[0][1] + M[1][1]*M[1][1]); + T T2 = glm::atan2(-M[2][1], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[1][2] - C1*M[1][0], C1*M[0][0] - S1*M[0][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][2], M[0][1]); + T S2 = glm::sqrt(M[1][0]*M[1][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(S2, M[0][0]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[1][2] - S1*M[1][1], C1*M[2][2] - S1*M[2][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][1], -M[0][2]); + T S2 = glm::sqrt(M[1][0]*M[1][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(S2, M[0][0]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-C1*M[2][1] - S1*M[2][2], C1*M[1][1] + S1*M[1][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][0], M[1][2]); + T S2 = glm::sqrt(M[0][1]*M[0][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(S2, M[1][1]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[2][0] - S1*M[2][2], C1*M[0][0] - S1*M[0][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][2], -M[1][0]); + T S2 = glm::sqrt(M[0][1]*M[0][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(S2, M[1][1]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-S1*M[0][0] - C1*M[0][2], S1*M[2][0] + C1*M[2][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZYZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][1], M[2][0]); + T S2 = glm::sqrt(M[0][2]*M[0][2] + M[1][2]*M[1][2]); + T T2 = glm::atan2(S2, M[2][2]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[0][1] - S1*M[0][0], C1*M[1][1] - S1*M[1][0]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZXZ(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[2][0], -M[2][1]); + T S2 = glm::sqrt(M[0][2]*M[0][2] + M[1][2]*M[1][2]); + T T2 = glm::atan2(S2, M[2][2]); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(-C1*M[1][0] - S1*M[1][1], C1*M[0][0] + S1*M[0][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleXZY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[1][2], M[1][1]); + T C2 = glm::sqrt(M[0][0]*M[0][0] + M[2][0]*M[2][0]); + T T2 = glm::atan2(-M[1][0], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[0][1] - C1*M[0][2], C1*M[2][2] - S1*M[2][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleYZX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(-M[0][2], M[0][0]); + T C2 = glm::sqrt(M[1][1]*M[1][1] + M[2][1]*M[2][1]); + T T2 = glm::atan2(M[0][1], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[1][0] + C1*M[1][2], S1*M[2][0] + C1*M[2][2]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZYX(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(M[0][1], M[0][0]); + T C2 = glm::sqrt(M[1][2]*M[1][2] + M[2][2]*M[2][2]); + T T2 = glm::atan2(-M[0][2], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(S1*M[2][0] - C1*M[2][1], C1*M[1][1] - S1*M[1][0]); + t1 = T1; + t2 = T2; + t3 = T3; + } + + template + GLM_FUNC_QUALIFIER void extractEulerAngleZXY(mat<4, 4, T, defaultp> const & M, + T & t1, + T & t2, + T & t3) + { + T T1 = glm::atan2(-M[1][0], M[1][1]); + T C2 = glm::sqrt(M[0][2]*M[0][2] + M[2][2]*M[2][2]); + T T2 = glm::atan2(M[1][2], C2); + T S1 = glm::sin(T1); + T C1 = glm::cos(T1); + T T3 = glm::atan2(C1*M[2][0] + S1*M[2][1], C1*M[0][0] + S1*M[0][1]); + t1 = T1; + t2 = T2; + t3 = T3; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/extend.hpp b/thirdparty/glm/glm/gtx/extend.hpp new file mode 100644 index 0000000..46bf5e7 --- /dev/null +++ b/thirdparty/glm/glm/gtx/extend.hpp @@ -0,0 +1,40 @@ +/// @ref gtx_extend +/// @file glm/gtx/extend.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_extend GLM_GTX_extend +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extend a position from a source to a position at a defined length. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_extend is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_extend extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_extend + /// @{ + + /// Extends of Length the Origin position using the (Source - Origin) direction. + /// @see gtx_extend + template + GLM_FUNC_DECL genType extend( + genType const& Origin, + genType const& Source, + typename genType::value_type const Length); + + /// @} +}//namespace glm + +#include "extend.inl" diff --git a/thirdparty/glm/glm/gtx/extend.inl b/thirdparty/glm/glm/gtx/extend.inl new file mode 100644 index 0000000..32128eb --- /dev/null +++ b/thirdparty/glm/glm/gtx/extend.inl @@ -0,0 +1,48 @@ +/// @ref gtx_extend + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType extend + ( + genType const& Origin, + genType const& Source, + genType const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> extend + ( + vec<2, T, Q> const& Origin, + vec<2, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> extend + ( + vec<3, T, Q> const& Origin, + vec<3, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> extend + ( + vec<4, T, Q> const& Origin, + vec<4, T, Q> const& Source, + T const& Distance + ) + { + return Origin + (Source - Origin) * Distance; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/extended_min_max.hpp b/thirdparty/glm/glm/gtx/extended_min_max.hpp new file mode 100644 index 0000000..e1b722f --- /dev/null +++ b/thirdparty/glm/glm/gtx/extended_min_max.hpp @@ -0,0 +1,135 @@ +/// @ref gtx_extended_min_max +/// @file glm/gtx/extended_min_max.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_extended_min_max GLM_GTX_extended_min_max +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Min and max functions for 3 to 4 parameters. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../ext/vector_common.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_extended_min_max is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_extended_min_max extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_extended_min_max + /// @{ + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T min( + T const& x, + T const& y, + T const& z); + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + typename C::T const& y, + typename C::T const& z); + + /// Return the minimum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + C const& y, + C const& z); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T min( + T const& x, + T const& y, + T const& z, + T const& w); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w); + + /// Return the minimum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C min( + C const& x, + C const& y, + C const& z, + C const& w); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T max( + T const& x, + T const& y, + T const& z); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + typename C::T const& y, + typename C::T const& z); + + /// Return the maximum component-wise values of 3 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + C const& y, + C const& z); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template + GLM_FUNC_DECL T max( + T const& x, + T const& y, + T const& z, + T const& w); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w); + + /// Return the maximum component-wise values of 4 inputs + /// @see gtx_extented_min_max + template class C> + GLM_FUNC_DECL C max( + C const& x, + C const& y, + C const& z, + C const& w); + + /// @} +}//namespace glm + +#include "extended_min_max.inl" diff --git a/thirdparty/glm/glm/gtx/extended_min_max.inl b/thirdparty/glm/glm/gtx/extended_min_max.inl new file mode 100644 index 0000000..de5998f --- /dev/null +++ b/thirdparty/glm/glm/gtx/extended_min_max.inl @@ -0,0 +1,138 @@ +/// @ref gtx_extended_min_max + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T min( + T const& x, + T const& y, + T const& z) + { + return glm::min(glm::min(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + typename C::T const& y, + typename C::T const& z + ) + { + return glm::min(glm::min(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + C const& y, + C const& z + ) + { + return glm::min(glm::min(x, y), z); + } + + template + GLM_FUNC_QUALIFIER T min + ( + T const& x, + T const& y, + T const& z, + T const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C min + ( + C const& x, + C const& y, + C const& z, + C const& w + ) + { + return glm::min(glm::min(x, y), glm::min(z, w)); + } + + template + GLM_FUNC_QUALIFIER T max( + T const& x, + T const& y, + T const& z) + { + return glm::max(glm::max(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + typename C::T const& y, + typename C::T const& z + ) + { + return glm::max(glm::max(x, y), z); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + C const& y, + C const& z + ) + { + return glm::max(glm::max(x, y), z); + } + + template + GLM_FUNC_QUALIFIER T max + ( + T const& x, + T const& y, + T const& z, + T const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + typename C::T const& y, + typename C::T const& z, + typename C::T const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } + + template class C> + GLM_FUNC_QUALIFIER C max + ( + C const& x, + C const& y, + C const& z, + C const& w + ) + { + return glm::max(glm::max(x, y), glm::max(z, w)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/exterior_product.hpp b/thirdparty/glm/glm/gtx/exterior_product.hpp new file mode 100644 index 0000000..1979acc --- /dev/null +++ b/thirdparty/glm/glm/gtx/exterior_product.hpp @@ -0,0 +1,43 @@ +/// @ref gtx_exterior_product +/// @file glm/gtx/exterior_product.hpp +/// +/// @see core (dependence) +/// @see gtx_exterior_product (dependence) +/// +/// @defgroup gtx_exterior_product GLM_GTX_exterior_product +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// @brief Allow to perform bit operations on integer values + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_exterior_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_exterior_product extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_exterior_product + /// @{ + + /// Returns the cross product of x and y. + /// + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see Exterior product + template + GLM_FUNC_DECL GLM_CONSTEXPR T cross(vec<2, T, Q> const& v, vec<2, T, Q> const& u); + + /// @} +} //namespace glm + +#include "exterior_product.inl" diff --git a/thirdparty/glm/glm/gtx/exterior_product.inl b/thirdparty/glm/glm/gtx/exterior_product.inl new file mode 100644 index 0000000..690085d --- /dev/null +++ b/thirdparty/glm/glm/gtx/exterior_product.inl @@ -0,0 +1,26 @@ +/// @ref gtx_exterior_product + +#include + +namespace glm { +namespace detail +{ + template + struct compute_cross_vec2 + { + GLM_FUNC_QUALIFIER GLM_CONSTEXPR static T call(vec<2, T, Q> const& v, vec<2, T, Q> const& u) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'cross' accepts only floating-point inputs"); + + return v.x * u.y - u.x * v.y; + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T cross(vec<2, T, Q> const& x, vec<2, T, Q> const& y) + { + return detail::compute_cross_vec2::value>::call(x, y); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtx/fast_exponential.hpp b/thirdparty/glm/glm/gtx/fast_exponential.hpp new file mode 100644 index 0000000..9fae325 --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_exponential.hpp @@ -0,0 +1,93 @@ +/// @ref gtx_fast_exponential +/// @file glm/gtx/fast_exponential.hpp +/// +/// @see core (dependence) +/// @see gtx_half_float (dependence) +/// +/// @defgroup gtx_fast_exponential GLM_GTX_fast_exponential +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of exponential based functions. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_fast_exponential is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_fast_exponential extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_exponential + /// @{ + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL genType fastPow(genType x, genType y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastPow(vec const& x, vec const& y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL genTypeT fastPow(genTypeT x, genTypeU y); + + /// Faster than the common pow function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastPow(vec const& x); + + /// Faster than the common exp function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastExp(T x); + + /// Faster than the common exp function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastExp(vec const& x); + + /// Faster than the common log function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastLog(T x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastLog(vec const& x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastExp2(T x); + + /// Faster than the common exp2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastExp2(vec const& x); + + /// Faster than the common log2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL T fastLog2(T x); + + /// Faster than the common log2 function but less accurate. + /// @see gtx_fast_exponential + template + GLM_FUNC_DECL vec fastLog2(vec const& x); + + /// @} +}//namespace glm + +#include "fast_exponential.inl" diff --git a/thirdparty/glm/glm/gtx/fast_exponential.inl b/thirdparty/glm/glm/gtx/fast_exponential.inl new file mode 100644 index 0000000..5b11742 --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_exponential.inl @@ -0,0 +1,136 @@ +/// @ref gtx_fast_exponential + +namespace glm +{ + // fastPow: + template + GLM_FUNC_QUALIFIER genType fastPow(genType x, genType y) + { + return exp(y * log(x)); + } + + template + GLM_FUNC_QUALIFIER vec fastPow(vec const& x, vec const& y) + { + return exp(y * log(x)); + } + + template + GLM_FUNC_QUALIFIER T fastPow(T x, int y) + { + T f = static_cast(1); + for(int i = 0; i < y; ++i) + f *= x; + return f; + } + + template + GLM_FUNC_QUALIFIER vec fastPow(vec const& x, vec const& y) + { + vec Result; + for(length_t i = 0, n = x.length(); i < n; ++i) + Result[i] = fastPow(x[i], y[i]); + return Result; + } + + // fastExp + // Note: This function provides accurate results only for value between -1 and 1, else avoid it. + template + GLM_FUNC_QUALIFIER T fastExp(T x) + { + // This has a better looking and same performance in release mode than the following code. However, in debug mode it's slower. + // return 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25 * (1.0f + x * 0.2f)))); + T x2 = x * x; + T x3 = x2 * x; + T x4 = x3 * x; + T x5 = x4 * x; + return T(1) + x + (x2 * T(0.5)) + (x3 * T(0.1666666667)) + (x4 * T(0.041666667)) + (x5 * T(0.008333333333)); + } + /* // Try to handle all values of float... but often shower than std::exp, glm::floor and the loop kill the performance + GLM_FUNC_QUALIFIER float fastExp(float x) + { + const float e = 2.718281828f; + const float IntegerPart = floor(x); + const float FloatPart = x - IntegerPart; + float z = 1.f; + + for(int i = 0; i < int(IntegerPart); ++i) + z *= e; + + const float x2 = FloatPart * FloatPart; + const float x3 = x2 * FloatPart; + const float x4 = x3 * FloatPart; + const float x5 = x4 * FloatPart; + return z * (1.0f + FloatPart + (x2 * 0.5f) + (x3 * 0.1666666667f) + (x4 * 0.041666667f) + (x5 * 0.008333333333f)); + } + + // Increase accuracy on number bigger that 1 and smaller than -1 but it's not enough for high and negative numbers + GLM_FUNC_QUALIFIER float fastExp(float x) + { + // This has a better looking and same performance in release mode than the following code. However, in debug mode it's slower. + // return 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25 * (1.0f + x * 0.2f)))); + float x2 = x * x; + float x3 = x2 * x; + float x4 = x3 * x; + float x5 = x4 * x; + float x6 = x5 * x; + float x7 = x6 * x; + float x8 = x7 * x; + return 1.0f + x + (x2 * 0.5f) + (x3 * 0.1666666667f) + (x4 * 0.041666667f) + (x5 * 0.008333333333f)+ (x6 * 0.00138888888888f) + (x7 * 0.000198412698f) + (x8 * 0.0000248015873f);; + } + */ + + template + GLM_FUNC_QUALIFIER vec fastExp(vec const& x) + { + return detail::functor1::call(fastExp, x); + } + + // fastLog + template + GLM_FUNC_QUALIFIER genType fastLog(genType x) + { + return std::log(x); + } + + /* Slower than the VC7.1 function... + GLM_FUNC_QUALIFIER float fastLog(float x) + { + float y1 = (x - 1.0f) / (x + 1.0f); + float y2 = y1 * y1; + return 2.0f * y1 * (1.0f + y2 * (0.3333333333f + y2 * (0.2f + y2 * 0.1428571429f))); + } + */ + + template + GLM_FUNC_QUALIFIER vec fastLog(vec const& x) + { + return detail::functor1::call(fastLog, x); + } + + //fastExp2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType fastExp2(genType x) + { + return fastExp(static_cast(0.69314718055994530941723212145818) * x); + } + + template + GLM_FUNC_QUALIFIER vec fastExp2(vec const& x) + { + return detail::functor1::call(fastExp2, x); + } + + // fastLog2, ln2 = 0.69314718055994530941723212145818f + template + GLM_FUNC_QUALIFIER genType fastLog2(genType x) + { + return fastLog(x) / static_cast(0.69314718055994530941723212145818); + } + + template + GLM_FUNC_QUALIFIER vec fastLog2(vec const& x) + { + return detail::functor1::call(fastLog2, x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/fast_square_root.hpp b/thirdparty/glm/glm/gtx/fast_square_root.hpp new file mode 100644 index 0000000..80729db --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_square_root.hpp @@ -0,0 +1,96 @@ +/// @ref gtx_fast_square_root +/// @file glm/gtx/fast_square_root.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_fast_square_root GLM_GTX_fast_square_root +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of square root based functions. +/// - Sqrt optimisation based on Newton's method, +/// www.gamedev.net/community/forums/topic.asp?topic id=139956 + +#pragma once + +// Dependency: +#include "../common.hpp" +#include "../exponential.hpp" +#include "../geometric.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_fast_square_root is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_fast_square_root extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_square_root + /// @{ + + /// Faster than the common sqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastSqrt(genType x); + + /// Faster than the common sqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL vec fastSqrt(vec const& x); + + /// Faster than the common inversesqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastInverseSqrt(genType x); + + /// Faster than the common inversesqrt function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL vec fastInverseSqrt(vec const& x); + + /// Faster than the common length function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastLength(genType x); + + /// Faster than the common length function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL T fastLength(vec const& x); + + /// Faster than the common distance function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastDistance(genType x, genType y); + + /// Faster than the common distance function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL T fastDistance(vec const& x, vec const& y); + + /// Faster than the common normalize function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL genType fastNormalize(genType x); + + /// Faster than the common normalize function but less accurate. + /// + /// @see gtx_fast_square_root extension. + template + GLM_FUNC_DECL vec fastNormalize(vec const& x); + + /// @} +}// namespace glm + +#include "fast_square_root.inl" diff --git a/thirdparty/glm/glm/gtx/fast_square_root.inl b/thirdparty/glm/glm/gtx/fast_square_root.inl new file mode 100644 index 0000000..60fdb7a --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_square_root.inl @@ -0,0 +1,75 @@ +/// @ref gtx_fast_square_root + +namespace glm +{ + // fastSqrt + template + GLM_FUNC_QUALIFIER genType fastSqrt(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fastSqrt' only accept floating-point input"); + + return genType(1) / fastInverseSqrt(x); + } + + template + GLM_FUNC_QUALIFIER vec fastSqrt(vec const& x) + { + return detail::functor1::call(fastSqrt, x); + } + + // fastInversesqrt + template + GLM_FUNC_QUALIFIER genType fastInverseSqrt(genType x) + { + return detail::compute_inversesqrt<1, genType, lowp, detail::is_aligned::value>::call(vec<1, genType, lowp>(x)).x; + } + + template + GLM_FUNC_QUALIFIER vec fastInverseSqrt(vec const& x) + { + return detail::compute_inversesqrt::value>::call(x); + } + + // fastLength + template + GLM_FUNC_QUALIFIER genType fastLength(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fastLength' only accept floating-point inputs"); + + return abs(x); + } + + template + GLM_FUNC_QUALIFIER T fastLength(vec const& x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'fastLength' only accept floating-point inputs"); + + return fastSqrt(dot(x, x)); + } + + // fastDistance + template + GLM_FUNC_QUALIFIER genType fastDistance(genType x, genType y) + { + return fastLength(y - x); + } + + template + GLM_FUNC_QUALIFIER T fastDistance(vec const& x, vec const& y) + { + return fastLength(y - x); + } + + // fastNormalize + template + GLM_FUNC_QUALIFIER genType fastNormalize(genType x) + { + return x > genType(0) ? genType(1) : -genType(1); + } + + template + GLM_FUNC_QUALIFIER vec fastNormalize(vec const& x) + { + return x * fastInverseSqrt(dot(x, x)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/fast_trigonometry.hpp b/thirdparty/glm/glm/gtx/fast_trigonometry.hpp new file mode 100644 index 0000000..93acab5 --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_trigonometry.hpp @@ -0,0 +1,77 @@ +/// @ref gtx_fast_trigonometry +/// @file glm/gtx/fast_trigonometry.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_fast_trigonometry GLM_GTX_fast_trigonometry +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Fast but less accurate implementations of trigonometric functions. + +#pragma once + +// Dependency: +#include "../gtc/constants.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_fast_trigonometry is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_fast_trigonometry extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_fast_trigonometry + /// @{ + + /// Wrap an angle to [0 2pi[ + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T wrapAngle(T angle); + + /// Faster than the common sin function but less accurate. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastSin(T angle); + + /// Faster than the common cos function but less accurate. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastCos(T angle); + + /// Faster than the common tan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastTan(T angle); + + /// Faster than the common asin function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAsin(T angle); + + /// Faster than the common acos function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAcos(T angle); + + /// Faster than the common atan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAtan(T y, T x); + + /// Faster than the common atan function but less accurate. + /// Defined between -2pi and 2pi. + /// From GLM_GTX_fast_trigonometry extension. + template + GLM_FUNC_DECL T fastAtan(T angle); + + /// @} +}//namespace glm + +#include "fast_trigonometry.inl" diff --git a/thirdparty/glm/glm/gtx/fast_trigonometry.inl b/thirdparty/glm/glm/gtx/fast_trigonometry.inl new file mode 100644 index 0000000..1a710cb --- /dev/null +++ b/thirdparty/glm/glm/gtx/fast_trigonometry.inl @@ -0,0 +1,142 @@ +/// @ref gtx_fast_trigonometry + +namespace glm{ +namespace detail +{ + template + GLM_FUNC_QUALIFIER vec taylorCos(vec const& x) + { + return static_cast(1) + - (x * x) * (1.f / 2.f) + + ((x * x) * (x * x)) * (1.f / 24.f) + - (((x * x) * (x * x)) * (x * x)) * (1.f / 720.f) + + (((x * x) * (x * x)) * ((x * x) * (x * x))) * (1.f / 40320.f); + } + + template + GLM_FUNC_QUALIFIER T cos_52s(T x) + { + T const xx(x * x); + return (T(0.9999932946) + xx * (T(-0.4999124376) + xx * (T(0.0414877472) + xx * T(-0.0012712095)))); + } + + template + GLM_FUNC_QUALIFIER vec cos_52s(vec const& x) + { + return detail::functor1::call(cos_52s, x); + } +}//namespace detail + + // wrapAngle + template + GLM_FUNC_QUALIFIER T wrapAngle(T angle) + { + return abs(mod(angle, two_pi())); + } + + template + GLM_FUNC_QUALIFIER vec wrapAngle(vec const& x) + { + return detail::functor1::call(wrapAngle, x); + } + + // cos + template + GLM_FUNC_QUALIFIER T fastCos(T x) + { + T const angle(wrapAngle(x)); + + if(angle < half_pi()) + return detail::cos_52s(angle); + if(angle < pi()) + return -detail::cos_52s(pi() - angle); + if(angle < (T(3) * half_pi())) + return -detail::cos_52s(angle - pi()); + + return detail::cos_52s(two_pi() - angle); + } + + template + GLM_FUNC_QUALIFIER vec fastCos(vec const& x) + { + return detail::functor1::call(fastCos, x); + } + + // sin + template + GLM_FUNC_QUALIFIER T fastSin(T x) + { + return fastCos(half_pi() - x); + } + + template + GLM_FUNC_QUALIFIER vec fastSin(vec const& x) + { + return detail::functor1::call(fastSin, x); + } + + // tan + template + GLM_FUNC_QUALIFIER T fastTan(T x) + { + return x + (x * x * x * T(0.3333333333)) + (x * x * x * x * x * T(0.1333333333333)) + (x * x * x * x * x * x * x * T(0.0539682539)); + } + + template + GLM_FUNC_QUALIFIER vec fastTan(vec const& x) + { + return detail::functor1::call(fastTan, x); + } + + // asin + template + GLM_FUNC_QUALIFIER T fastAsin(T x) + { + return x + (x * x * x * T(0.166666667)) + (x * x * x * x * x * T(0.075)) + (x * x * x * x * x * x * x * T(0.0446428571)) + (x * x * x * x * x * x * x * x * x * T(0.0303819444));// + (x * x * x * x * x * x * x * x * x * x * x * T(0.022372159)); + } + + template + GLM_FUNC_QUALIFIER vec fastAsin(vec const& x) + { + return detail::functor1::call(fastAsin, x); + } + + // acos + template + GLM_FUNC_QUALIFIER T fastAcos(T x) + { + return T(1.5707963267948966192313216916398) - fastAsin(x); //(PI / 2) + } + + template + GLM_FUNC_QUALIFIER vec fastAcos(vec const& x) + { + return detail::functor1::call(fastAcos, x); + } + + // atan + template + GLM_FUNC_QUALIFIER T fastAtan(T y, T x) + { + T sgn = sign(y) * sign(x); + return abs(fastAtan(y / x)) * sgn; + } + + template + GLM_FUNC_QUALIFIER vec fastAtan(vec const& y, vec const& x) + { + return detail::functor2::call(fastAtan, y, x); + } + + template + GLM_FUNC_QUALIFIER T fastAtan(T x) + { + return x - (x * x * x * T(0.333333333333)) + (x * x * x * x * x * T(0.2)) - (x * x * x * x * x * x * x * T(0.1428571429)) + (x * x * x * x * x * x * x * x * x * T(0.111111111111)) - (x * x * x * x * x * x * x * x * x * x * x * T(0.0909090909)); + } + + template + GLM_FUNC_QUALIFIER vec fastAtan(vec const& x) + { + return detail::functor1::call(fastAtan, x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/float_notmalize.inl b/thirdparty/glm/glm/gtx/float_notmalize.inl new file mode 100644 index 0000000..8cdbc5a --- /dev/null +++ b/thirdparty/glm/glm/gtx/float_notmalize.inl @@ -0,0 +1,13 @@ +/// @ref gtx_float_normalize + +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec floatNormalize(vec const& v) + { + return vec(v) / static_cast(std::numeric_limits::max()); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/functions.hpp b/thirdparty/glm/glm/gtx/functions.hpp new file mode 100644 index 0000000..df68a0d --- /dev/null +++ b/thirdparty/glm/glm/gtx/functions.hpp @@ -0,0 +1,54 @@ +/// @ref gtx_functions +/// @file glm/gtx/functions.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_functions GLM_GTX_functions +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// List of useful common functions. + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" +#include "../detail/qualifier.hpp" +#include "../detail/type_vec2.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_functions is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_functions extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_functions + /// @{ + + /// 1D gauss function + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL T gauss( + T x, + T ExpectedValue, + T StandardDeviation); + + /// 2D gauss function + /// + /// @see gtc_epsilon + template + GLM_FUNC_DECL T gauss( + vec<2, T, Q> const& Coord, + vec<2, T, Q> const& ExpectedValue, + vec<2, T, Q> const& StandardDeviation); + + /// @} +}//namespace glm + +#include "functions.inl" + diff --git a/thirdparty/glm/glm/gtx/functions.inl b/thirdparty/glm/glm/gtx/functions.inl new file mode 100644 index 0000000..29cbb20 --- /dev/null +++ b/thirdparty/glm/glm/gtx/functions.inl @@ -0,0 +1,30 @@ +/// @ref gtx_functions + +#include "../exponential.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T gauss + ( + T x, + T ExpectedValue, + T StandardDeviation + ) + { + return exp(-((x - ExpectedValue) * (x - ExpectedValue)) / (static_cast(2) * StandardDeviation * StandardDeviation)) / (StandardDeviation * sqrt(static_cast(6.28318530717958647692528676655900576))); + } + + template + GLM_FUNC_QUALIFIER T gauss + ( + vec<2, T, Q> const& Coord, + vec<2, T, Q> const& ExpectedValue, + vec<2, T, Q> const& StandardDeviation + ) + { + vec<2, T, Q> const Squared = ((Coord - ExpectedValue) * (Coord - ExpectedValue)) / (static_cast(2) * StandardDeviation * StandardDeviation); + return exp(-(Squared.x + Squared.y)); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtx/gradient_paint.hpp b/thirdparty/glm/glm/gtx/gradient_paint.hpp new file mode 100644 index 0000000..5656445 --- /dev/null +++ b/thirdparty/glm/glm/gtx/gradient_paint.hpp @@ -0,0 +1,51 @@ +/// @ref gtx_gradient_paint +/// @file glm/gtx/gradient_paint.hpp +/// +/// @see core (dependence) +/// @see gtx_optimum_pow (dependence) +/// +/// @defgroup gtx_gradient_paint GLM_GTX_gradient_paint +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions that return the color of procedural gradient for specific coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/optimum_pow.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_gradient_paint is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_gradient_paint extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_gradient_paint + /// @{ + + /// Return a color from a radial gradient. + /// @see - gtx_gradient_paint + template + GLM_FUNC_DECL T radialGradient( + vec<2, T, Q> const& Center, + T const& Radius, + vec<2, T, Q> const& Focal, + vec<2, T, Q> const& Position); + + /// Return a color from a linear gradient. + /// @see - gtx_gradient_paint + template + GLM_FUNC_DECL T linearGradient( + vec<2, T, Q> const& Point0, + vec<2, T, Q> const& Point1, + vec<2, T, Q> const& Position); + + /// @} +}// namespace glm + +#include "gradient_paint.inl" diff --git a/thirdparty/glm/glm/gtx/gradient_paint.inl b/thirdparty/glm/glm/gtx/gradient_paint.inl new file mode 100644 index 0000000..4c495e6 --- /dev/null +++ b/thirdparty/glm/glm/gtx/gradient_paint.inl @@ -0,0 +1,36 @@ +/// @ref gtx_gradient_paint + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T radialGradient + ( + vec<2, T, Q> const& Center, + T const& Radius, + vec<2, T, Q> const& Focal, + vec<2, T, Q> const& Position + ) + { + vec<2, T, Q> F = Focal - Center; + vec<2, T, Q> D = Position - Focal; + T Radius2 = pow2(Radius); + T Fx2 = pow2(F.x); + T Fy2 = pow2(F.y); + + T Numerator = (D.x * F.x + D.y * F.y) + sqrt(Radius2 * (pow2(D.x) + pow2(D.y)) - pow2(D.x * F.y - D.y * F.x)); + T Denominator = Radius2 - (Fx2 + Fy2); + return Numerator / Denominator; + } + + template + GLM_FUNC_QUALIFIER T linearGradient + ( + vec<2, T, Q> const& Point0, + vec<2, T, Q> const& Point1, + vec<2, T, Q> const& Position + ) + { + vec<2, T, Q> Dist = Point1 - Point0; + return (Dist.x * (Position.x - Point0.x) + Dist.y * (Position.y - Point0.y)) / glm::dot(Dist, Dist); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/handed_coordinate_space.hpp b/thirdparty/glm/glm/gtx/handed_coordinate_space.hpp new file mode 100644 index 0000000..7c2aada --- /dev/null +++ b/thirdparty/glm/glm/gtx/handed_coordinate_space.hpp @@ -0,0 +1,48 @@ +/// @ref gtx_handed_coordinate_space +/// @file glm/gtx/handed_coordinate_space.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_handed_coordinate_space GLM_GTX_handed_coordinate_space +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// To know if a set of three basis vectors defines a right or left-handed coordinate system. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_handed_coordinate_space is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_handed_coordinate_space extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_handed_coordinate_space + /// @{ + + //! Return if a trihedron right handed or not. + //! From GLM_GTX_handed_coordinate_space extension. + template + GLM_FUNC_DECL bool rightHanded( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal); + + //! Return if a trihedron left handed or not. + //! From GLM_GTX_handed_coordinate_space extension. + template + GLM_FUNC_DECL bool leftHanded( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal); + + /// @} +}// namespace glm + +#include "handed_coordinate_space.inl" diff --git a/thirdparty/glm/glm/gtx/handed_coordinate_space.inl b/thirdparty/glm/glm/gtx/handed_coordinate_space.inl new file mode 100644 index 0000000..e43c17b --- /dev/null +++ b/thirdparty/glm/glm/gtx/handed_coordinate_space.inl @@ -0,0 +1,26 @@ +/// @ref gtx_handed_coordinate_space + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool rightHanded + ( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal + ) + { + return dot(cross(normal, tangent), binormal) > T(0); + } + + template + GLM_FUNC_QUALIFIER bool leftHanded + ( + vec<3, T, Q> const& tangent, + vec<3, T, Q> const& binormal, + vec<3, T, Q> const& normal + ) + { + return dot(cross(normal, tangent), binormal) < T(0); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/hash.hpp b/thirdparty/glm/glm/gtx/hash.hpp new file mode 100644 index 0000000..ef89290 --- /dev/null +++ b/thirdparty/glm/glm/gtx/hash.hpp @@ -0,0 +1,146 @@ +/// @ref gtx_hash +/// @file glm/gtx/hash.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_hash GLM_GTX_hash +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add std::hash support for glm types + +#pragma once + +#if defined(GLM_FORCE_MESSAGES) && !defined(GLM_EXT_INCLUDED) +# ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_hash is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +# else +# pragma message("GLM: GLM_GTX_hash extension included") +# endif +#endif + +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../gtc/vec1.hpp" + +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" + +#include "../mat2x2.hpp" +#include "../mat2x3.hpp" +#include "../mat2x4.hpp" + +#include "../mat3x2.hpp" +#include "../mat3x3.hpp" +#include "../mat3x4.hpp" + +#include "../mat4x2.hpp" +#include "../mat4x3.hpp" +#include "../mat4x4.hpp" + +#if __cplusplus < 201103L +#pragma message("GLM_GTX_hash requires C++11 standard library support") +#endif + +#if GLM_LANG & GLM_LANG_CXX11 +#define GLM_GTX_hash 1 +#include + +namespace std +{ + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<1, T, Q> const& v) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<2, T, Q> const& v) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<3, T, Q> const& v) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::vec<4, T, Q> const& v) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::qua const& q) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::tdualquat const& q) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 2, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 3, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<2, 4, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 2, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 3, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<3, 4, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 2, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 3, T,Q> const& m) const GLM_NOEXCEPT; + }; + + template + struct hash > + { + GLM_FUNC_DECL size_t operator()(glm::mat<4, 4, T,Q> const& m) const GLM_NOEXCEPT; + }; +} // namespace std + +#include "hash.inl" + +#endif //GLM_LANG & GLM_LANG_CXX11 diff --git a/thirdparty/glm/glm/gtx/hash.inl b/thirdparty/glm/glm/gtx/hash.inl new file mode 100644 index 0000000..bcadfe5 --- /dev/null +++ b/thirdparty/glm/glm/gtx/hash.inl @@ -0,0 +1,175 @@ +/// @ref gtx_hash + +namespace glm { +namespace detail +{ + GLM_INLINE void hash_combine(size_t &seed, size_t hash) + { + hash += 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hash; + } +}} + +namespace std +{ + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::vec<1, T, Q> const& v) const GLM_NOEXCEPT + { + hash hasher; + return hasher(v.x); + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::vec<2, T, Q> const& v) const GLM_NOEXCEPT + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::vec<3, T, Q> const& v) const GLM_NOEXCEPT + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + glm::detail::hash_combine(seed, hasher(v.z)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::vec<4, T, Q> const& v) const GLM_NOEXCEPT + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(v.x)); + glm::detail::hash_combine(seed, hasher(v.y)); + glm::detail::hash_combine(seed, hasher(v.z)); + glm::detail::hash_combine(seed, hasher(v.w)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::qua const& q) const GLM_NOEXCEPT + { + size_t seed = 0; + hash hasher; + glm::detail::hash_combine(seed, hasher(q.x)); + glm::detail::hash_combine(seed, hasher(q.y)); + glm::detail::hash_combine(seed, hasher(q.z)); + glm::detail::hash_combine(seed, hasher(q.w)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::tdualquat const& q) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(q.real)); + glm::detail::hash_combine(seed, hasher(q.dual)); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<2, 2, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<2, 3, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<2, 4, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<3, 2, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<3, 3, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<3, 4, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<4, 2, T,Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<4, 3, T,Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } + + template + GLM_FUNC_QUALIFIER size_t hash >::operator()(glm::mat<4, 4, T, Q> const& m) const GLM_NOEXCEPT + { + size_t seed = 0; + hash > hasher; + glm::detail::hash_combine(seed, hasher(m[0])); + glm::detail::hash_combine(seed, hasher(m[1])); + glm::detail::hash_combine(seed, hasher(m[2])); + glm::detail::hash_combine(seed, hasher(m[3])); + return seed; + } +} diff --git a/thirdparty/glm/glm/gtx/integer.hpp b/thirdparty/glm/glm/gtx/integer.hpp new file mode 100644 index 0000000..2b16830 --- /dev/null +++ b/thirdparty/glm/glm/gtx/integer.hpp @@ -0,0 +1,74 @@ +/// @ref gtx_integer +/// @file glm/gtx/integer.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_integer GLM_GTX_integer +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add support for integer for core functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/integer.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_integer is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_integer extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_integer + /// @{ + + //! Returns x raised to the y power. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int pow(int x, uint y); + + //! Returns the positive square root of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int sqrt(int x); + + //! Returns the floor log2 of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL unsigned int floor_log2(unsigned int x); + + //! Modulus. Returns x - y * floor(x / y) for each component in x using the floating point value y. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL int mod(int x, int y); + + //! Return the factorial value of a number (!12 max, integer only) + //! From GLM_GTX_integer extension. + template + GLM_FUNC_DECL genType factorial(genType const& x); + + //! 32bit signed integer. + //! From GLM_GTX_integer extension. + typedef signed int sint; + + //! Returns x raised to the y power. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint pow(uint x, uint y); + + //! Returns the positive square root of x. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint sqrt(uint x); + + //! Modulus. Returns x - y * floor(x / y) for each component in x using the floating point value y. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint mod(uint x, uint y); + + //! Returns the number of leading zeros. + //! From GLM_GTX_integer extension. + GLM_FUNC_DECL uint nlz(uint x); + + /// @} +}//namespace glm + +#include "integer.inl" diff --git a/thirdparty/glm/glm/gtx/integer.inl b/thirdparty/glm/glm/gtx/integer.inl new file mode 100644 index 0000000..eb5df30 --- /dev/null +++ b/thirdparty/glm/glm/gtx/integer.inl @@ -0,0 +1,185 @@ +/// @ref gtx_integer + +namespace glm +{ + // pow + GLM_FUNC_QUALIFIER int pow(int x, uint y) + { + if(y == 0) + return x >= 0 ? 1 : -1; + + int result = x; + for(uint i = 1; i < y; ++i) + result *= x; + return result; + } + + // sqrt: From Christopher J. Musial, An integer square root, Graphics Gems, 1990, page 387 + GLM_FUNC_QUALIFIER int sqrt(int x) + { + if(x <= 1) return x; + + int NextTrial = x >> 1; + int CurrentAnswer; + + do + { + CurrentAnswer = NextTrial; + NextTrial = (NextTrial + x / NextTrial) >> 1; + } while(NextTrial < CurrentAnswer); + + return CurrentAnswer; + } + +// Henry Gordon Dietz: http://aggregate.org/MAGIC/ +namespace detail +{ + GLM_FUNC_QUALIFIER unsigned int ones32(unsigned int x) + { + /* 32-bit recursive reduction using SWAR... + but first step is mapping 2-bit values + into sum of 2 1-bit values in sneaky way + */ + x -= ((x >> 1) & 0x55555555); + x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); + x = (((x >> 4) + x) & 0x0f0f0f0f); + x += (x >> 8); + x += (x >> 16); + return(x & 0x0000003f); + } +}//namespace detail + + // Henry Gordon Dietz: http://aggregate.org/MAGIC/ +/* + GLM_FUNC_QUALIFIER unsigned int floor_log2(unsigned int x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return _detail::ones32(x) >> 1; + } +*/ + // mod + GLM_FUNC_QUALIFIER int mod(int x, int y) + { + return ((x % y) + y) % y; + } + + // factorial (!12 max, integer only) + template + GLM_FUNC_QUALIFIER genType factorial(genType const& x) + { + genType Temp = x; + genType Result; + for(Result = 1; Temp > 1; --Temp) + Result *= Temp; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> factorial( + vec<2, T, Q> const& x) + { + return vec<2, T, Q>( + factorial(x.x), + factorial(x.y)); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> factorial( + vec<3, T, Q> const& x) + { + return vec<3, T, Q>( + factorial(x.x), + factorial(x.y), + factorial(x.z)); + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> factorial( + vec<4, T, Q> const& x) + { + return vec<4, T, Q>( + factorial(x.x), + factorial(x.y), + factorial(x.z), + factorial(x.w)); + } + + GLM_FUNC_QUALIFIER uint pow(uint x, uint y) + { + if (y == 0) + return 1u; + + uint result = x; + for(uint i = 1; i < y; ++i) + result *= x; + return result; + } + + GLM_FUNC_QUALIFIER uint sqrt(uint x) + { + if(x <= 1) return x; + + uint NextTrial = x >> 1; + uint CurrentAnswer; + + do + { + CurrentAnswer = NextTrial; + NextTrial = (NextTrial + x / NextTrial) >> 1; + } while(NextTrial < CurrentAnswer); + + return CurrentAnswer; + } + + GLM_FUNC_QUALIFIER uint mod(uint x, uint y) + { + return x - y * (x / y); + } + +//#if(GLM_COMPILER & (GLM_COMPILER_VC | GLM_COMPILER_GCC)) + + GLM_FUNC_QUALIFIER unsigned int nlz(unsigned int x) + { + return 31u - static_cast(findMSB(x)); + } +/* +#else + + // Hackers Delight: http://www.hackersdelight.org/HDcode/nlz.c.txt + GLM_FUNC_QUALIFIER unsigned int nlz(unsigned int x) + { + int y, m, n; + + y = -int(x >> 16); // If left half of x is 0, + m = (y >> 16) & 16; // set n = 16. If left half + n = 16 - m; // is nonzero, set n = 0 and + x = x >> m; // shift x right 16. + // Now x is of the form 0000xxxx. + y = x - 0x100; // If positions 8-15 are 0, + m = (y >> 16) & 8; // add 8 to n and shift x left 8. + n = n + m; + x = x << m; + + y = x - 0x1000; // If positions 12-15 are 0, + m = (y >> 16) & 4; // add 4 to n and shift x left 4. + n = n + m; + x = x << m; + + y = x - 0x4000; // If positions 14-15 are 0, + m = (y >> 16) & 2; // add 2 to n and shift x left 2. + n = n + m; + x = x << m; + + y = x >> 14; // Set y = 0, 1, 2, or 3. + m = y & ~(y >> 1); // Set m = 0, 1, 2, or 2 resp. + return unsigned(n + 2 - m); + } + +#endif//(GLM_COMPILER) +*/ +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/intersect.hpp b/thirdparty/glm/glm/gtx/intersect.hpp new file mode 100644 index 0000000..c7aec6f --- /dev/null +++ b/thirdparty/glm/glm/gtx/intersect.hpp @@ -0,0 +1,90 @@ +/// @ref gtx_intersect +/// @file glm/gtx/intersect.hpp +/// +/// @see core (dependence) +/// @see gtx_closest_point (dependence) +/// +/// @defgroup gtx_intersect GLM_GTX_intersect +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add intersection functions + +#pragma once + +// Dependency: +#include +#include +#include "../glm.hpp" +#include "../geometric.hpp" +#include "../gtx/closest_point.hpp" +#include "../gtx/vector_query.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_closest_point is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_closest_point extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_intersect + /// @{ + + //! Compute the intersection of a ray and a plane. + //! Ray direction and plane normal must be unit length. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRayPlane( + genType const& orig, genType const& dir, + genType const& planeOrig, genType const& planeNormal, + typename genType::value_type & intersectionDistance); + + //! Compute the intersection of a ray and a triangle. + /// Based om Tomas Möller implementation http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/raytri/ + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRayTriangle( + vec<3, T, Q> const& orig, vec<3, T, Q> const& dir, + vec<3, T, Q> const& v0, vec<3, T, Q> const& v1, vec<3, T, Q> const& v2, + vec<2, T, Q>& baryPosition, T& distance); + + //! Compute the intersection of a line and a triangle. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectLineTriangle( + genType const& orig, genType const& dir, + genType const& vert0, genType const& vert1, genType const& vert2, + genType & position); + + //! Compute the intersection distance of a ray and a sphere. + //! The ray direction vector is unit length. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRaySphere( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, typename genType::value_type const sphereRadiusSquared, + typename genType::value_type & intersectionDistance); + + //! Compute the intersection of a ray and a sphere. + //! From GLM_GTX_intersect extension. + template + GLM_FUNC_DECL bool intersectRaySphere( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadius, + genType & intersectionPosition, genType & intersectionNormal); + + //! Compute the intersection of a line and a sphere. + //! From GLM_GTX_intersect extension + template + GLM_FUNC_DECL bool intersectLineSphere( + genType const& point0, genType const& point1, + genType const& sphereCenter, typename genType::value_type sphereRadius, + genType & intersectionPosition1, genType & intersectionNormal1, + genType & intersectionPosition2 = genType(), genType & intersectionNormal2 = genType()); + + /// @} +}//namespace glm + +#include "intersect.inl" diff --git a/thirdparty/glm/glm/gtx/intersect.inl b/thirdparty/glm/glm/gtx/intersect.inl new file mode 100644 index 0000000..925a903 --- /dev/null +++ b/thirdparty/glm/glm/gtx/intersect.inl @@ -0,0 +1,200 @@ +/// @ref gtx_intersect + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool intersectRayPlane + ( + genType const& orig, genType const& dir, + genType const& planeOrig, genType const& planeNormal, + typename genType::value_type & intersectionDistance + ) + { + typename genType::value_type d = glm::dot(dir, planeNormal); + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + + if(glm::abs(d) > Epsilon) // if dir and planeNormal are not perpendicular + { + typename genType::value_type const tmp_intersectionDistance = glm::dot(planeOrig - orig, planeNormal) / d; + if (tmp_intersectionDistance > static_cast(0)) { // allow only intersections + intersectionDistance = tmp_intersectionDistance; + return true; + } + } + + return false; + } + + template + GLM_FUNC_QUALIFIER bool intersectRayTriangle + ( + vec<3, T, Q> const& orig, vec<3, T, Q> const& dir, + vec<3, T, Q> const& vert0, vec<3, T, Q> const& vert1, vec<3, T, Q> const& vert2, + vec<2, T, Q>& baryPosition, T& distance + ) + { + // find vectors for two edges sharing vert0 + vec<3, T, Q> const edge1 = vert1 - vert0; + vec<3, T, Q> const edge2 = vert2 - vert0; + + // begin calculating determinant - also used to calculate U parameter + vec<3, T, Q> const p = glm::cross(dir, edge2); + + // if determinant is near zero, ray lies in plane of triangle + T const det = glm::dot(edge1, p); + + vec<3, T, Q> Perpendicular(0); + + if (det > static_cast(0)) + { + // calculate distance from vert0 to ray origin + vec<3, T, Q> const dist = orig - vert0; + + // calculate U parameter and test bounds + baryPosition.x = glm::dot(dist, p); + if(baryPosition.x < static_cast(0) || baryPosition.x > det) + return false; + + // prepare to test V parameter + Perpendicular = glm::cross(dist, edge1); + + // calculate V parameter and test bounds + baryPosition.y = glm::dot(dir, Perpendicular); + if((baryPosition.y < static_cast(0)) || ((baryPosition.x + baryPosition.y) > det)) + return false; + } + else if(det < static_cast(0)) + { + // calculate distance from vert0 to ray origin + vec<3, T, Q> const dist = orig - vert0; + + // calculate U parameter and test bounds + baryPosition.x = glm::dot(dist, p); + if((baryPosition.x > static_cast(0)) || (baryPosition.x < det)) + return false; + + // prepare to test V parameter + Perpendicular = glm::cross(dist, edge1); + + // calculate V parameter and test bounds + baryPosition.y = glm::dot(dir, Perpendicular); + if((baryPosition.y > static_cast(0)) || (baryPosition.x + baryPosition.y < det)) + return false; + } + else + return false; // ray is parallel to the plane of the triangle + + T inv_det = static_cast(1) / det; + + // calculate distance, ray intersects triangle + distance = glm::dot(edge2, Perpendicular) * inv_det; + baryPosition *= inv_det; + + return true; + } + + template + GLM_FUNC_QUALIFIER bool intersectLineTriangle + ( + genType const& orig, genType const& dir, + genType const& vert0, genType const& vert1, genType const& vert2, + genType & position + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + + genType edge1 = vert1 - vert0; + genType edge2 = vert2 - vert0; + + genType Perpendicular = cross(dir, edge2); + + typename genType::value_type det = dot(edge1, Perpendicular); + + if (det > -Epsilon && det < Epsilon) + return false; + typename genType::value_type inv_det = typename genType::value_type(1) / det; + + genType Tangent = orig - vert0; + + position.y = dot(Tangent, Perpendicular) * inv_det; + if (position.y < typename genType::value_type(0) || position.y > typename genType::value_type(1)) + return false; + + genType Cotangent = cross(Tangent, edge1); + + position.z = dot(dir, Cotangent) * inv_det; + if (position.z < typename genType::value_type(0) || position.y + position.z > typename genType::value_type(1)) + return false; + + position.x = dot(edge2, Cotangent) * inv_det; + + return true; + } + + template + GLM_FUNC_QUALIFIER bool intersectRaySphere + ( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadiusSquared, + typename genType::value_type & intersectionDistance + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + genType diff = sphereCenter - rayStarting; + typename genType::value_type t0 = dot(diff, rayNormalizedDirection); + typename genType::value_type dSquared = dot(diff, diff) - t0 * t0; + if( dSquared > sphereRadiusSquared ) + { + return false; + } + typename genType::value_type t1 = sqrt( sphereRadiusSquared - dSquared ); + intersectionDistance = t0 > t1 + Epsilon ? t0 - t1 : t0 + t1; + return intersectionDistance > Epsilon; + } + + template + GLM_FUNC_QUALIFIER bool intersectRaySphere + ( + genType const& rayStarting, genType const& rayNormalizedDirection, + genType const& sphereCenter, const typename genType::value_type sphereRadius, + genType & intersectionPosition, genType & intersectionNormal + ) + { + typename genType::value_type distance; + if( intersectRaySphere( rayStarting, rayNormalizedDirection, sphereCenter, sphereRadius * sphereRadius, distance ) ) + { + intersectionPosition = rayStarting + rayNormalizedDirection * distance; + intersectionNormal = (intersectionPosition - sphereCenter) / sphereRadius; + return true; + } + return false; + } + + template + GLM_FUNC_QUALIFIER bool intersectLineSphere + ( + genType const& point0, genType const& point1, + genType const& sphereCenter, typename genType::value_type sphereRadius, + genType & intersectionPoint1, genType & intersectionNormal1, + genType & intersectionPoint2, genType & intersectionNormal2 + ) + { + typename genType::value_type Epsilon = std::numeric_limits::epsilon(); + genType dir = normalize(point1 - point0); + genType diff = sphereCenter - point0; + typename genType::value_type t0 = dot(diff, dir); + typename genType::value_type dSquared = dot(diff, diff) - t0 * t0; + if( dSquared > sphereRadius * sphereRadius ) + { + return false; + } + typename genType::value_type t1 = sqrt( sphereRadius * sphereRadius - dSquared ); + if( t0 < t1 + Epsilon ) + t1 = -t1; + intersectionPoint1 = point0 + dir * (t0 - t1); + intersectionNormal1 = (intersectionPoint1 - sphereCenter) / sphereRadius; + intersectionPoint2 = point0 + dir * (t0 + t1); + intersectionNormal2 = (intersectionPoint2 - sphereCenter) / sphereRadius; + return true; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/io.hpp b/thirdparty/glm/glm/gtx/io.hpp new file mode 100644 index 0000000..5afc8cc --- /dev/null +++ b/thirdparty/glm/glm/gtx/io.hpp @@ -0,0 +1,210 @@ +/// @ref gtx_io +/// @file glm/gtx/io.hpp +/// @author Jan P Springer (regnirpsj@gmail.com) +/// +/// @see core (dependence) +/// @see gtc_matrix_access (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_io GLM_GTX_io +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// std::[w]ostream support for glm types +/// +/// std::[w]ostream support for glm types + qualifier/width/etc. manipulators +/// based on howard hinnant's std::chrono io proposal +/// [http://home.roadrunner.com/~hinnant/bloomington/chrono_io.html] + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/quaternion.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_io is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_io extension included") +#endif + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wshorten-64-to-32" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +#include // std::basic_ostream<> (fwd) +#include // std::locale, std::locale::facet, std::locale::id +#include // std::pair<> + +namespace glm +{ + /// @addtogroup gtx_io + /// @{ + + namespace io + { + enum order_type { column_major, row_major}; + + template + class format_punct : public std::locale::facet + { + typedef CTy char_type; + + public: + + static std::locale::id id; + + bool formatted; + unsigned precision; + unsigned width; + char_type separator; + char_type delim_left; + char_type delim_right; + char_type space; + char_type newline; + order_type order; + + GLM_FUNC_DISCARD_DECL explicit format_punct(size_t a = 0); + GLM_FUNC_DISCARD_DECL explicit format_punct(format_punct const&); + }; + + template > + class basic_state_saver { + + public: + + GLM_FUNC_DISCARD_DECL explicit basic_state_saver(std::basic_ios&); + GLM_FUNC_DISCARD_DECL ~basic_state_saver(); + + private: + + typedef ::std::basic_ios state_type; + typedef typename state_type::char_type char_type; + typedef ::std::ios_base::fmtflags flags_type; + typedef ::std::streamsize streamsize_type; + typedef ::std::locale const locale_type; + + state_type& state_; + flags_type flags_; + streamsize_type precision_; + streamsize_type width_; + char_type fill_; + locale_type locale_; + + GLM_FUNC_DECL basic_state_saver& operator=(basic_state_saver const&); + }; + + typedef basic_state_saver state_saver; + typedef basic_state_saver wstate_saver; + + template > + class basic_format_saver + { + public: + + GLM_FUNC_DISCARD_DECL explicit basic_format_saver(std::basic_ios&); + GLM_FUNC_DISCARD_DECL ~basic_format_saver(); + + private: + + basic_state_saver const bss_; + + GLM_FUNC_DECL basic_format_saver& operator=(basic_format_saver const&); + }; + + typedef basic_format_saver format_saver; + typedef basic_format_saver wformat_saver; + + struct precision + { + unsigned value; + + GLM_FUNC_DISCARD_DECL explicit precision(unsigned); + }; + + struct width + { + unsigned value; + + GLM_FUNC_DISCARD_DECL explicit width(unsigned); + }; + + template + struct delimeter + { + CTy value[3]; + + GLM_FUNC_DISCARD_DECL explicit delimeter(CTy /* left */, CTy /* right */, CTy /* separator */ = ','); + }; + + struct order + { + order_type value; + + GLM_FUNC_DISCARD_DECL explicit order(order_type); + }; + + // functions, inlined (inline) + + template + FTy const& get_facet(std::basic_ios&); + template + std::basic_ios& formatted(std::basic_ios&); + template + std::basic_ios& unformatted(std::basic_ios&); + + template + std::basic_ostream& operator<<(std::basic_ostream&, precision const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, width const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, delimeter const&); + template + std::basic_ostream& operator<<(std::basic_ostream&, order const&); + }//namespace io + + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, qua const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<1, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<2, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<3, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, vec<4, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 2, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 3, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<2, 4, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 2, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 3, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<3, 4, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 2, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 3, T, Q> const&); + template + GLM_FUNC_DISCARD_DECL std::basic_ostream& operator<<(std::basic_ostream&, mat<4, 4, T, Q> const&); + + template + GLM_FUNC_DISCARD_DECL std::basic_ostream & operator<<(std::basic_ostream &, + std::pair const, mat<4, 4, T, Q> const> const&); + + /// @} +}//namespace glm + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + +#include "io.inl" diff --git a/thirdparty/glm/glm/gtx/io.inl b/thirdparty/glm/glm/gtx/io.inl new file mode 100644 index 0000000..d4ef825 --- /dev/null +++ b/thirdparty/glm/glm/gtx/io.inl @@ -0,0 +1,452 @@ +/// @ref gtx_io +/// @author Jan P Springer (regnirpsj@gmail.com) + +#include // std::fixed, std::setfill<>, std::setprecision, std::right, std::setw +#include // std::basic_ostream<> +#include "../gtc/matrix_access.hpp" // glm::col, glm::row +#include "../gtx/type_trait.hpp" // glm::type<> + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wshorten-64-to-32" +# pragma clang diagnostic ignored "-Wglobal-constructors" +#endif + +namespace glm{ +namespace io +{ + template + GLM_FUNC_QUALIFIER format_punct::format_punct(size_t a) + : std::locale::facet(a) + , formatted(true) + , precision(3) + , width(1 + 4 + 1 + precision) + , separator(',') + , delim_left('[') + , delim_right(']') + , space(' ') + , newline('\n') + , order(column_major) + {} + + template + GLM_FUNC_QUALIFIER format_punct::format_punct(format_punct const& a) + : std::locale::facet(0) + , formatted(a.formatted) + , precision(a.precision) + , width(a.width) + , separator(a.separator) + , delim_left(a.delim_left) + , delim_right(a.delim_right) + , space(a.space) + , newline(a.newline) + , order(a.order) + {} + + template std::locale::id format_punct::id; + + template + GLM_FUNC_QUALIFIER basic_state_saver::basic_state_saver(std::basic_ios& a) + : state_(a) + , flags_(a.flags()) + , precision_(a.precision()) + , width_(a.width()) + , fill_(a.fill()) + , locale_(a.getloc()) + {} + + template + GLM_FUNC_QUALIFIER basic_state_saver::~basic_state_saver() + { + state_.imbue(locale_); + state_.fill(fill_); + state_.width(width_); + state_.precision(precision_); + state_.flags(flags_); + } + + template + GLM_FUNC_QUALIFIER basic_format_saver::basic_format_saver(std::basic_ios& a) + : bss_(a) + { + a.imbue(std::locale(a.getloc(), new format_punct(get_facet >(a)))); + } + + template + GLM_FUNC_QUALIFIER + basic_format_saver::~basic_format_saver() + {} + + GLM_FUNC_QUALIFIER precision::precision(unsigned a) + : value(a) + {} + + GLM_FUNC_QUALIFIER width::width(unsigned a) + : value(a) + {} + + template + GLM_FUNC_QUALIFIER delimeter::delimeter(CTy a, CTy b, CTy c) + : value() + { + value[0] = a; + value[1] = b; + value[2] = c; + } + + GLM_FUNC_QUALIFIER order::order(order_type a) + : value(a) + {} + + template + GLM_FUNC_QUALIFIER FTy const& get_facet(std::basic_ios& ios) + { + if(!std::has_facet(ios.getloc())) + ios.imbue(std::locale(ios.getloc(), new FTy)); + + return std::use_facet(ios.getloc()); + } + + template + GLM_FUNC_QUALIFIER std::basic_ios& formatted(std::basic_ios& ios) + { + const_cast&>(get_facet >(ios)).formatted = true; + return ios; + } + + template + GLM_FUNC_QUALIFIER std::basic_ios& unformatted(std::basic_ios& ios) + { + const_cast&>(get_facet >(ios)).formatted = false; + return ios; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, precision const& a) + { + const_cast&>(get_facet >(os)).precision = a.value; + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, width const& a) + { + const_cast&>(get_facet >(os)).width = a.value; + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, delimeter const& a) + { + format_punct & fmt(const_cast&>(get_facet >(os))); + + fmt.delim_left = a.value[0]; + fmt.delim_right = a.value[1]; + fmt.separator = a.value[2]; + + return os; + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, order const& a) + { + const_cast&>(get_facet >(os)).order = a.value; + return os; + } +} // namespace io + +namespace detail +{ + template + GLM_FUNC_QUALIFIER std::basic_ostream& + print_vector_on(std::basic_ostream& os, V const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + + length_t const& components(type::components); + + if(fmt.formatted) + { + io::basic_state_saver const bss(os); + + os << std::fixed << std::right << std::setprecision(static_cast(fmt.precision)) << std::setfill(fmt.space) << fmt.delim_left; + + for(length_t i(0); i < components; ++i) + { + os << std::setw(static_cast(fmt.width)) << a[i]; + if(components-1 != i) + os << fmt.separator; + } + + os << fmt.delim_right; + } + else + { + for(length_t i(0); i < components; ++i) + { + os << a[i]; + + if(components-1 != i) + os << fmt.space; + } + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, qua const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<1, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<2, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<3, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, vec<4, T, Q> const& a) + { + return detail::print_vector_on(os, a); + } + +namespace detail +{ + template class M, length_t C, length_t R, typename T, qualifier Q> + GLM_FUNC_QUALIFIER std::basic_ostream& print_matrix_on(std::basic_ostream& os, M const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + + length_t const& cols(type >::cols); + length_t const& rows(type >::rows); + + if(fmt.formatted) + { + os << fmt.newline << fmt.delim_left; + + switch(fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < rows; ++i) + { + if (0 != i) + os << fmt.space; + + os << row(a, i); + + if(rows-1 != i) + os << fmt.newline; + } + } + break; + + case io::row_major: + { + for(length_t i(0); i < cols; ++i) + { + if(0 != i) + os << fmt.space; + + os << column(a, i); + + if(cols-1 != i) + os << fmt.newline; + } + } + break; + } + + os << fmt.delim_right; + } + else + { + switch (fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < cols; ++i) + { + os << column(a, i); + + if(cols - 1 != i) + os << fmt.space; + } + } + break; + + case io::row_major: + { + for (length_t i(0); i < rows; ++i) + { + os << row(a, i); + + if (rows-1 != i) + os << fmt.space; + } + } + break; + } + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<2, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<3, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<(std::basic_ostream& os, mat<3, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<3, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 2, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 3, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + + template + GLM_FUNC_QUALIFIER std::basic_ostream & operator<<(std::basic_ostream& os, mat<4, 4, T, Q> const& a) + { + return detail::print_matrix_on(os, a); + } + +namespace detail +{ + template class M, length_t C, length_t R, typename T, qualifier Q> + GLM_FUNC_QUALIFIER std::basic_ostream& print_matrix_pair_on(std::basic_ostream& os, std::pair const, M const> const& a) + { + typename std::basic_ostream::sentry const cerberus(os); + + if(cerberus) + { + io::format_punct const& fmt(io::get_facet >(os)); + M const& ml(a.first); + M const& mr(a.second); + length_t const& cols(type >::cols); + length_t const& rows(type >::rows); + + if(fmt.formatted) + { + os << fmt.newline << fmt.delim_left; + + switch(fmt.order) + { + case io::column_major: + { + for(length_t i(0); i < rows; ++i) + { + if(0 != i) + os << fmt.space; + + os << row(ml, i) << ((rows-1 != i) ? fmt.space : fmt.delim_right) << fmt.space << ((0 != i) ? fmt.space : fmt.delim_left) << row(mr, i); + + if(rows-1 != i) + os << fmt.newline; + } + } + break; + case io::row_major: + { + for(length_t i(0); i < cols; ++i) + { + if(0 != i) + os << fmt.space; + + os << column(ml, i) << ((cols-1 != i) ? fmt.space : fmt.delim_right) << fmt.space << ((0 != i) ? fmt.space : fmt.delim_left) << column(mr, i); + + if(cols-1 != i) + os << fmt.newline; + } + } + break; + } + + os << fmt.delim_right; + } + else + { + os << ml << fmt.space << mr; + } + } + + return os; + } +}//namespace detail + + template + GLM_FUNC_QUALIFIER std::basic_ostream& operator<<( + std::basic_ostream & os, + std::pair const, + mat<4, 4, T, Q> const> const& a) + { + return detail::print_matrix_pair_on(os, a); + } +}//namespace glm + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + diff --git a/thirdparty/glm/glm/gtx/log_base.hpp b/thirdparty/glm/glm/gtx/log_base.hpp new file mode 100644 index 0000000..915c7a4 --- /dev/null +++ b/thirdparty/glm/glm/gtx/log_base.hpp @@ -0,0 +1,46 @@ +/// @ref gtx_log_base +/// @file glm/gtx/log_base.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_log_base GLM_GTX_log_base +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Logarithm for any base. base can be a vector or a scalar. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_log_base is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_log_base extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_log_base + /// @{ + + /// Logarithm for any base. + /// From GLM_GTX_log_base. + template + GLM_FUNC_DECL genType log( + genType const& x, + genType const& base); + + /// Logarithm for any base. + /// From GLM_GTX_log_base. + template + GLM_FUNC_DECL vec sign( + vec const& x, + vec const& base); + + /// @} +}//namespace glm + +#include "log_base.inl" diff --git a/thirdparty/glm/glm/gtx/log_base.inl b/thirdparty/glm/glm/gtx/log_base.inl new file mode 100644 index 0000000..4bbb8e8 --- /dev/null +++ b/thirdparty/glm/glm/gtx/log_base.inl @@ -0,0 +1,16 @@ +/// @ref gtx_log_base + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType log(genType const& x, genType const& base) + { + return glm::log(x) / glm::log(base); + } + + template + GLM_FUNC_QUALIFIER vec log(vec const& x, vec const& base) + { + return glm::log(x) / glm::log(base); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_cross_product.hpp b/thirdparty/glm/glm/gtx/matrix_cross_product.hpp new file mode 100644 index 0000000..882a1d7 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_cross_product.hpp @@ -0,0 +1,45 @@ +/// @ref gtx_matrix_cross_product +/// @file glm/gtx/matrix_cross_product.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_matrix_cross_product GLM_GTX_matrix_cross_product +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build cross product matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_cross_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_cross_product extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_cross_product + /// @{ + + //! Build a cross product matrix. + //! From GLM_GTX_matrix_cross_product extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> matrixCross3( + vec<3, T, Q> const& x); + + //! Build a cross product matrix. + //! From GLM_GTX_matrix_cross_product extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> matrixCross4( + vec<3, T, Q> const& x); + + /// @} +}//namespace glm + +#include "matrix_cross_product.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_cross_product.inl b/thirdparty/glm/glm/gtx/matrix_cross_product.inl new file mode 100644 index 0000000..3a15397 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_cross_product.inl @@ -0,0 +1,37 @@ +/// @ref gtx_matrix_cross_product + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> matrixCross3 + ( + vec<3, T, Q> const& x + ) + { + mat<3, 3, T, Q> Result(T(0)); + Result[0][1] = x.z; + Result[1][0] = -x.z; + Result[0][2] = -x.y; + Result[2][0] = x.y; + Result[1][2] = x.x; + Result[2][1] = -x.x; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> matrixCross4 + ( + vec<3, T, Q> const& x + ) + { + mat<4, 4, T, Q> Result(T(0)); + Result[0][1] = x.z; + Result[1][0] = -x.z; + Result[0][2] = -x.y; + Result[2][0] = x.y; + Result[1][2] = x.x; + Result[2][1] = -x.x; + return Result; + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_decompose.hpp b/thirdparty/glm/glm/gtx/matrix_decompose.hpp new file mode 100644 index 0000000..19ac8a8 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_decompose.hpp @@ -0,0 +1,50 @@ +/// @ref gtx_matrix_decompose +/// @file glm/gtx/matrix_decompose.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_decompose GLM_GTX_matrix_decompose +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Decomposes a model matrix to translations, rotation and scale components + +#pragma once + +// Dependencies +#include "../mat4x4.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../geometric.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtc/matrix_transform.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_decompose is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_decompose extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_decompose + /// @{ + + /// Decomposes a model matrix to translations, rotation and scale components + /// @see gtx_matrix_decompose + template + GLM_FUNC_DISCARD_DECL bool decompose( + mat<4, 4, T, Q> const& modelMatrix, + vec<3, T, Q> & scale, qua & orientation, vec<3, T, Q> & translation, vec<3, T, Q> & skew, vec<4, T, Q> & perspective); + + // Recomposes a model matrix from a previously-decomposed matrix + template + GLM_FUNC_DISCARD_DECL mat<4, 4, T, Q> recompose( + vec<3, T, Q> const& scale, qua const& orientation, vec<3, T, Q> const& translation, + vec<3, T, Q> const& skew, vec<4, T, Q> const& perspective); + + /// @} +}//namespace glm + +#include "matrix_decompose.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_decompose.inl b/thirdparty/glm/glm/gtx/matrix_decompose.inl new file mode 100644 index 0000000..1b587e2 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_decompose.inl @@ -0,0 +1,234 @@ +/// @ref gtx_matrix_decompose + +#include "../gtc/constants.hpp" +#include "../gtc/epsilon.hpp" +#include "../gtx/transform.hpp" + +namespace glm{ +namespace detail +{ + /// Make a linear combination of two vectors and return the result. + // result = (a * ascl) + (b * bscl) + template + GLM_FUNC_QUALIFIER vec<3, T, Q> combine( + vec<3, T, Q> const& a, + vec<3, T, Q> const& b, + T ascl, T bscl) + { + return (a * ascl) + (b * bscl); + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> scale(vec<3, T, Q> const& v, T desiredLength) + { + return v * desiredLength / length(v); + } +}//namespace detail + + // Matrix decompose + // http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp + // Decomposes the mode matrix to translations,rotation scale components + + template + GLM_FUNC_QUALIFIER bool decompose(mat<4, 4, T, Q> const& ModelMatrix, vec<3, T, Q> & Scale, qua & Orientation, vec<3, T, Q> & Translation, vec<3, T, Q> & Skew, vec<4, T, Q> & Perspective) + { + mat<4, 4, T, Q> LocalMatrix(ModelMatrix); + + // Normalize the matrix. + if(epsilonEqual(LocalMatrix[3][3], static_cast(0), epsilon())) + return false; + + for(length_t i = 0; i < 4; ++i) + for(length_t j = 0; j < 4; ++j) + LocalMatrix[i][j] /= LocalMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + mat<4, 4, T, Q> PerspectiveMatrix(LocalMatrix); + + for(length_t i = 0; i < 3; i++) + PerspectiveMatrix[i][3] = static_cast(0); + PerspectiveMatrix[3][3] = static_cast(1); + + /// TODO: Fixme! + if(epsilonEqual(determinant(PerspectiveMatrix), static_cast(0), epsilon())) + return false; + + // First, isolate perspective. This is the messiest. + if( + epsilonNotEqual(LocalMatrix[0][3], static_cast(0), epsilon()) || + epsilonNotEqual(LocalMatrix[1][3], static_cast(0), epsilon()) || + epsilonNotEqual(LocalMatrix[2][3], static_cast(0), epsilon())) + { + // rightHandSide is the right hand side of the equation. + vec<4, T, Q> RightHandSide; + RightHandSide[0] = LocalMatrix[0][3]; + RightHandSide[1] = LocalMatrix[1][3]; + RightHandSide[2] = LocalMatrix[2][3]; + RightHandSide[3] = LocalMatrix[3][3]; + + // Solve the equation by inverting PerspectiveMatrix and multiplying + // rightHandSide by the inverse. (This is the easiest way, not + // necessarily the best.) + mat<4, 4, T, Q> InversePerspectiveMatrix = glm::inverse(PerspectiveMatrix);// inverse(PerspectiveMatrix, inversePerspectiveMatrix); + mat<4, 4, T, Q> TransposedInversePerspectiveMatrix = glm::transpose(InversePerspectiveMatrix);// transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); + + Perspective = TransposedInversePerspectiveMatrix * RightHandSide; + // v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); + + // Clear the perspective partition + LocalMatrix[0][3] = LocalMatrix[1][3] = LocalMatrix[2][3] = static_cast(0); + LocalMatrix[3][3] = static_cast(1); + } + else + { + // No perspective. + Perspective = vec<4, T, Q>(0, 0, 0, 1); + } + + // Next take care of translation (easy). + Translation = vec<3, T, Q>(LocalMatrix[3]); + LocalMatrix[3] = vec<4, T, Q>(0, 0, 0, LocalMatrix[3].w); + + vec<3, T, Q> Row[3], Pdum3; + + // Now get scale and shear. + for(length_t i = 0; i < 3; ++i) + for(length_t j = 0; j < 3; ++j) + Row[i][j] = LocalMatrix[i][j]; + + // Compute X scale factor and normalize first row. + Scale.x = length(Row[0]);// v3Length(Row[0]); + + Row[0] = detail::scale(Row[0], static_cast(1)); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + Skew.z = dot(Row[0], Row[1]); + Row[1] = detail::combine(Row[1], Row[0], static_cast(1), -Skew.z); + + // Now, compute Y scale and normalize 2nd row. + Scale.y = length(Row[1]); + Row[1] = detail::scale(Row[1], static_cast(1)); + Skew.z /= Scale.y; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + Skew.y = glm::dot(Row[0], Row[2]); + Row[2] = detail::combine(Row[2], Row[0], static_cast(1), -Skew.y); + Skew.x = glm::dot(Row[1], Row[2]); + Row[2] = detail::combine(Row[2], Row[1], static_cast(1), -Skew.x); + + // Next, get Z scale and normalize 3rd row. + Scale.z = length(Row[2]); + Row[2] = detail::scale(Row[2], static_cast(1)); + Skew.y /= Scale.z; + Skew.x /= Scale.z; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + Pdum3 = cross(Row[1], Row[2]); // v3Cross(row[1], row[2], Pdum3); + if(dot(Row[0], Pdum3) < 0) + { + for(length_t i = 0; i < 3; i++) + { + Scale[i] *= static_cast(-1); + Row[i] *= static_cast(-1); + } + } + + // Now, get the rotations out, as described in the gem. + + // FIXME - Add the ability to return either quaternions (which are + // easier to recompose with) or Euler angles (rx, ry, rz), which + // are easier for authors to deal with. The latter will only be useful + // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I + // will leave the Euler angle code here for now. + + // ret.rotateY = asin(-Row[0][2]); + // if (cos(ret.rotateY) != 0) { + // ret.rotateX = atan2(Row[1][2], Row[2][2]); + // ret.rotateZ = atan2(Row[0][1], Row[0][0]); + // } else { + // ret.rotateX = atan2(-Row[2][0], Row[1][1]); + // ret.rotateZ = 0; + // } + + int i, j, k = 0; + T root, trace = Row[0].x + Row[1].y + Row[2].z; + if(trace > static_cast(0)) + { + root = sqrt(trace + static_cast(1.0)); + Orientation.w = static_cast(0.5) * root; + root = static_cast(0.5) / root; + Orientation.x = root * (Row[1].z - Row[2].y); + Orientation.y = root * (Row[2].x - Row[0].z); + Orientation.z = root * (Row[0].y - Row[1].x); + } // End if > 0 + else + { + static int Next[3] = {1, 2, 0}; + i = 0; + if(Row[1].y > Row[0].x) i = 1; + if(Row[2].z > Row[i][i]) i = 2; + j = Next[i]; + k = Next[j]; + +# ifdef GLM_FORCE_QUAT_DATA_WXYZ + int off = 1; +# else + int off = 0; +# endif + + root = sqrt(Row[i][i] - Row[j][j] - Row[k][k] + static_cast(1.0)); + + Orientation[i + off] = static_cast(0.5) * root; + root = static_cast(0.5) / root; + Orientation[j + off] = root * (Row[i][j] + Row[j][i]); + Orientation[k + off] = root * (Row[i][k] + Row[k][i]); + Orientation.w = root * (Row[j][k] - Row[k][j]); + } // End if <= 0 + + return true; + } + + // Recomposes a model matrix from a previously-decomposed matrix + // http://www.opensource.apple.com/source/WebCore/WebCore-514/platform/graphics/transforms/TransformationMatrix.cpp + // https://stackoverflow.com/a/75573092/1047040 + template + GLM_FUNC_DECL mat<4, 4, T, Q> recompose( + vec<3, T, Q> const& scale, qua const& orientation, vec<3, T, Q> const& translation, + vec<3, T, Q> const& skew, vec<4, T, Q> const& perspective) + { + glm::mat4 m = glm::mat4(1.f); + + m[0][3] = perspective.x; + m[1][3] = perspective.y; + m[2][3] = perspective.z; + m[3][3] = perspective.w; + + m *= glm::translate(translation); + m *= glm::mat4_cast(orientation); + + if (abs(skew.x) > static_cast(0)) { + glm::mat4 tmp(1.f); + tmp[2][1] = skew.x; + m *= tmp; + } + + if (abs(skew.y) > static_cast(0)) { + glm::mat4 tmp(1.f); + tmp[2][0] = skew.y; + m *= tmp; + } + + if (abs(skew.z) > static_cast(0)) { + glm::mat4 tmp(1.f); + tmp[1][0] = skew.z; + m *= tmp; + } + + m *= glm::scale(scale); + + return m; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_factorisation.hpp b/thirdparty/glm/glm/gtx/matrix_factorisation.hpp new file mode 100644 index 0000000..dc32847 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_factorisation.hpp @@ -0,0 +1,67 @@ +/// @ref gtx_matrix_factorisation +/// @file glm/gtx/matrix_factorisation.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_factorisation GLM_GTX_matrix_factorisation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions to factor matrices in various forms + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_factorisation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_factorisation extension included") +#endif + +/* +Suggestions: + - Move helper functions flipud and fliplr to another file: They may be helpful in more general circumstances. + - Implement other types of matrix factorisation, such as: QL and LQ, L(D)U, eigendecompositions, etc... +*/ + +namespace glm +{ + /// @addtogroup gtx_matrix_factorisation + /// @{ + + /// Flips the matrix rows up and down. + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL mat flipud(mat const& in); + + /// Flips the matrix columns right and left. + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DECL mat fliplr(mat const& in); + + /// Performs QR factorisation of a matrix. + /// Returns 2 matrices, q and r, such that the columns of q are orthonormal and span the same subspace than those of the input matrix, r is an upper triangular matrix, and q*r=in. + /// Given an n-by-m input matrix, q has dimensions min(n,m)-by-m, and r has dimensions n-by-min(n,m). + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DISCARD_DECL void qr_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& q, mat& r); + + /// Performs RQ factorisation of a matrix. + /// Returns 2 matrices, r and q, such that r is an upper triangular matrix, the rows of q are orthonormal and span the same subspace than those of the input matrix, and r*q=in. + /// Note that in the context of RQ factorisation, the diagonal is seen as starting in the lower-right corner of the matrix, instead of the usual upper-left. + /// Given an n-by-m input matrix, r has dimensions min(n,m)-by-m, and q has dimensions n-by-min(n,m). + /// + /// From GLM_GTX_matrix_factorisation extension. + template + GLM_FUNC_DISCARD_DECL void rq_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& r, mat& q); + + /// @} +} + +#include "matrix_factorisation.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_factorisation.inl b/thirdparty/glm/glm/gtx/matrix_factorisation.inl new file mode 100644 index 0000000..6f1683c --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_factorisation.inl @@ -0,0 +1,84 @@ +/// @ref gtx_matrix_factorisation + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat flipud(mat const& in) + { + mat tin = transpose(in); + tin = fliplr(tin); + mat out = transpose(tin); + + return out; + } + + template + GLM_FUNC_QUALIFIER mat fliplr(mat const& in) + { + mat out; + for (length_t i = 0; i < C; i++) + { + out[i] = in[(C - i) - 1]; + } + + return out; + } + + template + GLM_FUNC_QUALIFIER void qr_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& q, mat& r) + { + // Uses modified Gram-Schmidt method + // Source: https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process + // And https://en.wikipedia.org/wiki/QR_decomposition + + //For all the linearly independs columns of the input... + // (there can be no more linearly independents columns than there are rows.) + for (length_t i = 0; i < (C < R ? C : R); i++) + { + //Copy in Q the input's i-th column. + q[i] = in[i]; + + //j = [0,i[ + // Make that column orthogonal to all the previous ones by substracting to it the non-orthogonal projection of all the previous columns. + // Also: Fill the zero elements of R + for (length_t j = 0; j < i; j++) + { + q[i] -= dot(q[i], q[j])*q[j]; + r[j][i] = 0; + } + + //Now, Q i-th column is orthogonal to all the previous columns. Normalize it. + q[i] = normalize(q[i]); + + //j = [i,C[ + //Finally, compute the corresponding coefficients of R by computing the projection of the resulting column on the other columns of the input. + for (length_t j = i; j < C; j++) + { + r[j][i] = dot(in[j], q[i]); + } + } + } + + template + GLM_FUNC_QUALIFIER void rq_decompose(mat const& in, mat<(C < R ? C : R), R, T, Q>& r, mat& q) + { + // From https://en.wikipedia.org/wiki/QR_decomposition: + // The RQ decomposition transforms a matrix A into the product of an upper triangular matrix R (also known as right-triangular) and an orthogonal matrix Q. The only difference from QR decomposition is the order of these matrices. + // QR decomposition is Gram-Schmidt orthogonalization of columns of A, started from the first column. + // RQ decomposition is Gram-Schmidt orthogonalization of rows of A, started from the last row. + + mat tin = transpose(in); + tin = fliplr(tin); + + mat tr; + mat<(C < R ? C : R), C, T, Q> tq; + qr_decompose(tin, tq, tr); + + tr = fliplr(tr); + r = transpose(tr); + r = fliplr(r); + + tq = fliplr(tq); + q = transpose(tq); + } +} //namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_interpolation.hpp b/thirdparty/glm/glm/gtx/matrix_interpolation.hpp new file mode 100644 index 0000000..e2767c8 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_interpolation.hpp @@ -0,0 +1,58 @@ +/// @ref gtx_matrix_interpolation +/// @file glm/gtx/matrix_interpolation.hpp +/// @author Ghenadii Ursachi (the.asteroth@gmail.com) +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_interpolation GLM_GTX_matrix_interpolation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Allows to directly interpolate two matrices. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_interpolation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_interpolation extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_interpolation + /// @{ + + /// Get the axis and angle of the rotation from a matrix. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DISCARD_DECL void axisAngle( + mat<4, 4, T, Q> const& Mat, vec<3, T, Q> & Axis, T & Angle); + + /// Build a matrix from axis and angle. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> axisAngleMatrix( + vec<3, T, Q> const& Axis, T const Angle); + + /// Extracts the rotation part of a matrix. + /// From GLM_GTX_matrix_interpolation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> extractMatrixRotation( + mat<4, 4, T, Q> const& Mat); + + /// Build a interpolation of 4 * 4 matrixes. + /// From GLM_GTX_matrix_interpolation extension. + /// Warning! works only with rotation and/or translation matrixes, scale will generate unexpected results. + template + GLM_FUNC_DECL mat<4, 4, T, Q> interpolate( + mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2, T const Delta); + + /// @} +}//namespace glm + +#include "matrix_interpolation.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_interpolation.inl b/thirdparty/glm/glm/gtx/matrix_interpolation.inl new file mode 100644 index 0000000..f4ba3a6 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_interpolation.inl @@ -0,0 +1,146 @@ +/// @ref gtx_matrix_interpolation + +#include "../ext/scalar_constants.hpp" + +#include + +namespace glm +{ + template + GLM_FUNC_QUALIFIER void axisAngle(mat<4, 4, T, Q> const& m, vec<3, T, Q>& axis, T& angle) + { + T const epsilon = + std::numeric_limits::epsilon() * static_cast(1e2); + + bool const nearSymmetrical = + abs(m[1][0] - m[0][1]) < epsilon && + abs(m[2][0] - m[0][2]) < epsilon && + abs(m[2][1] - m[1][2]) < epsilon; + + if(nearSymmetrical) + { + bool const nearIdentity = + abs(m[1][0] + m[0][1]) < epsilon && + abs(m[2][0] + m[0][2]) < epsilon && + abs(m[2][1] + m[1][2]) < epsilon && + abs(m[0][0] + m[1][1] + m[2][2] - T(3.0)) < epsilon; + if (nearIdentity) + { + angle = static_cast(0.0); + axis = vec<3, T, Q>( + static_cast(1.0), static_cast(0.0), static_cast(0.0)); + return; + } + angle = pi(); + T xx = (m[0][0] + static_cast(1.0)) * static_cast(0.5); + T yy = (m[1][1] + static_cast(1.0)) * static_cast(0.5); + T zz = (m[2][2] + static_cast(1.0)) * static_cast(0.5); + T xy = (m[1][0] + m[0][1]) * static_cast(0.25); + T xz = (m[2][0] + m[0][2]) * static_cast(0.25); + T yz = (m[2][1] + m[1][2]) * static_cast(0.25); + if((xx > yy) && (xx > zz)) + { + if(xx < epsilon) + { + axis.x = static_cast(0.0); + axis.y = static_cast(0.7071); + axis.z = static_cast(0.7071); + } + else + { + axis.x = sqrt(xx); + axis.y = xy / axis.x; + axis.z = xz / axis.x; + } + } + else if (yy > zz) + { + if(yy < epsilon) + { + axis.x = static_cast(0.7071); + axis.y = static_cast(0.0); + axis.z = static_cast(0.7071); + } + else + { + axis.y = sqrt(yy); + axis.x = xy / axis.y; + axis.z = yz / axis.y; + } + } + else + { + if (zz < epsilon) + { + axis.x = static_cast(0.7071); + axis.y = static_cast(0.7071); + axis.z = static_cast(0.0); + } + else + { + axis.z = sqrt(zz); + axis.x = xz / axis.z; + axis.y = yz / axis.z; + } + } + return; + } + + T const angleCos = (m[0][0] + m[1][1] + m[2][2] - static_cast(1)) * static_cast(0.5); + if(angleCos >= static_cast(1.0)) + { + angle = static_cast(0.0); + } + else if (angleCos <= static_cast(-1.0)) + { + angle = pi(); + } + else + { + angle = acos(angleCos); + } + + axis = glm::normalize(glm::vec<3, T, Q>( + m[1][2] - m[2][1], m[2][0] - m[0][2], m[0][1] - m[1][0])); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> axisAngleMatrix(vec<3, T, Q> const& axis, T const angle) + { + T c = cos(angle); + T s = sin(angle); + T t = static_cast(1) - c; + vec<3, T, Q> n = normalize(axis); + + return mat<4, 4, T, Q>( + t * n.x * n.x + c, t * n.x * n.y + n.z * s, t * n.x * n.z - n.y * s, static_cast(0.0), + t * n.x * n.y - n.z * s, t * n.y * n.y + c, t * n.y * n.z + n.x * s, static_cast(0.0), + t * n.x * n.z + n.y * s, t * n.y * n.z - n.x * s, t * n.z * n.z + c, static_cast(0.0), + static_cast(0.0), static_cast(0.0), static_cast(0.0), static_cast(1.0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> extractMatrixRotation(mat<4, 4, T, Q> const& m) + { + return mat<4, 4, T, Q>( + m[0][0], m[0][1], m[0][2], static_cast(0.0), + m[1][0], m[1][1], m[1][2], static_cast(0.0), + m[2][0], m[2][1], m[2][2], static_cast(0.0), + static_cast(0.0), static_cast(0.0), static_cast(0.0), static_cast(1.0)); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> interpolate(mat<4, 4, T, Q> const& m1, mat<4, 4, T, Q> const& m2, T const delta) + { + mat<4, 4, T, Q> m1rot = extractMatrixRotation(m1); + mat<4, 4, T, Q> dltRotation = m2 * transpose(m1rot); + vec<3, T, Q> dltAxis; + T dltAngle; + axisAngle(dltRotation, dltAxis, dltAngle); + mat<4, 4, T, Q> out = axisAngleMatrix(dltAxis, dltAngle * delta) * m1rot; + out[3][0] = m1[3][0] + delta * (m2[3][0] - m1[3][0]); + out[3][1] = m1[3][1] + delta * (m2[3][1] - m1[3][1]); + out[3][2] = m1[3][2] + delta * (m2[3][2] - m1[3][2]); + return out; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_major_storage.hpp b/thirdparty/glm/glm/gtx/matrix_major_storage.hpp new file mode 100644 index 0000000..f518578 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_major_storage.hpp @@ -0,0 +1,117 @@ +/// @ref gtx_matrix_major_storage +/// @file glm/gtx/matrix_major_storage.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_matrix_major_storage GLM_GTX_matrix_major_storage +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build matrices with specific matrix order, row or column + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_major_storage is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_major_storage extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_major_storage + /// @{ + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> rowMajor2( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> rowMajor2( + mat<2, 2, T, Q> const& m); + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> rowMajor3( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> rowMajor3( + mat<3, 3, T, Q> const& m); + + //! Build a row major matrix from row vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> rowMajor4( + vec<4, T, Q> const& v1, + vec<4, T, Q> const& v2, + vec<4, T, Q> const& v3, + vec<4, T, Q> const& v4); + + //! Build a row major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> rowMajor4( + mat<4, 4, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> colMajor2( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> colMajor2( + mat<2, 2, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> colMajor3( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> colMajor3( + mat<3, 3, T, Q> const& m); + + //! Build a column major matrix from column vectors. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> colMajor4( + vec<4, T, Q> const& v1, + vec<4, T, Q> const& v2, + vec<4, T, Q> const& v3, + vec<4, T, Q> const& v4); + + //! Build a column major matrix from other matrix. + //! From GLM_GTX_matrix_major_storage extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> colMajor4( + mat<4, 4, T, Q> const& m); + + /// @} +}//namespace glm + +#include "matrix_major_storage.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_major_storage.inl b/thirdparty/glm/glm/gtx/matrix_major_storage.inl new file mode 100644 index 0000000..279dd34 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_major_storage.inl @@ -0,0 +1,166 @@ +/// @ref gtx_matrix_major_storage + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> rowMajor2 + ( + vec<2, T, Q> const& v1, + vec<2, T, Q> const& v2 + ) + { + mat<2, 2, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> rowMajor2( + const mat<2, 2, T, Q>& m) + { + mat<2, 2, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rowMajor3( + const vec<3, T, Q>& v1, + const vec<3, T, Q>& v2, + const vec<3, T, Q>& v3) + { + mat<3, 3, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[2][0] = v1.z; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + Result[2][1] = v2.z; + Result[0][2] = v3.x; + Result[1][2] = v3.y; + Result[2][2] = v3.z; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rowMajor3( + const mat<3, 3, T, Q>& m) + { + mat<3, 3, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rowMajor4( + const vec<4, T, Q>& v1, + const vec<4, T, Q>& v2, + const vec<4, T, Q>& v3, + const vec<4, T, Q>& v4) + { + mat<4, 4, T, Q> Result; + Result[0][0] = v1.x; + Result[1][0] = v1.y; + Result[2][0] = v1.z; + Result[3][0] = v1.w; + Result[0][1] = v2.x; + Result[1][1] = v2.y; + Result[2][1] = v2.z; + Result[3][1] = v2.w; + Result[0][2] = v3.x; + Result[1][2] = v3.y; + Result[2][2] = v3.z; + Result[3][2] = v3.w; + Result[0][3] = v4.x; + Result[1][3] = v4.y; + Result[2][3] = v4.z; + Result[3][3] = v4.w; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rowMajor4( + const mat<4, 4, T, Q>& m) + { + mat<4, 4, T, Q> Result; + Result[0][0] = m[0][0]; + Result[0][1] = m[1][0]; + Result[0][2] = m[2][0]; + Result[0][3] = m[3][0]; + Result[1][0] = m[0][1]; + Result[1][1] = m[1][1]; + Result[1][2] = m[2][1]; + Result[1][3] = m[3][1]; + Result[2][0] = m[0][2]; + Result[2][1] = m[1][2]; + Result[2][2] = m[2][2]; + Result[2][3] = m[3][2]; + Result[3][0] = m[0][3]; + Result[3][1] = m[1][3]; + Result[3][2] = m[2][3]; + Result[3][3] = m[3][3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> colMajor2( + const vec<2, T, Q>& v1, + const vec<2, T, Q>& v2) + { + return mat<2, 2, T, Q>(v1, v2); + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> colMajor2( + const mat<2, 2, T, Q>& m) + { + return mat<2, 2, T, Q>(m); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> colMajor3( + const vec<3, T, Q>& v1, + const vec<3, T, Q>& v2, + const vec<3, T, Q>& v3) + { + return mat<3, 3, T, Q>(v1, v2, v3); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> colMajor3( + const mat<3, 3, T, Q>& m) + { + return mat<3, 3, T, Q>(m); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> colMajor4( + const vec<4, T, Q>& v1, + const vec<4, T, Q>& v2, + const vec<4, T, Q>& v3, + const vec<4, T, Q>& v4) + { + return mat<4, 4, T, Q>(v1, v2, v3, v4); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> colMajor4( + const mat<4, 4, T, Q>& m) + { + return mat<4, 4, T, Q>(m); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_operation.hpp b/thirdparty/glm/glm/gtx/matrix_operation.hpp new file mode 100644 index 0000000..07ed8e8 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_operation.hpp @@ -0,0 +1,101 @@ +/// @ref gtx_matrix_operation +/// @file glm/gtx/matrix_operation.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_operation GLM_GTX_matrix_operation +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Build diagonal matrices from vectors. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_operation is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_operation extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_operation + /// @{ + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> diagonal2x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 3, T, Q> diagonal2x3( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 4, T, Q> diagonal2x4( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 2, T, Q> diagonal3x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> diagonal3x3( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 4, T, Q> diagonal3x4( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 2, T, Q> diagonal4x2( + vec<2, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 3, T, Q> diagonal4x3( + vec<3, T, Q> const& v); + + //! Build a diagonal matrix. + //! From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> diagonal4x4( + vec<4, T, Q> const& v); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<2, 2, T, Q> adjugate(mat<2, 2, T, Q> const& m); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> adjugate(mat<3, 3, T, Q> const& m); + + /// Build an adjugate matrix. + /// From GLM_GTX_matrix_operation extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> adjugate(mat<4, 4, T, Q> const& m); + + /// @} +}//namespace glm + +#include "matrix_operation.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_operation.inl b/thirdparty/glm/glm/gtx/matrix_operation.inl new file mode 100644 index 0000000..a4f4a85 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_operation.inl @@ -0,0 +1,176 @@ +/// @ref gtx_matrix_operation + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> diagonal2x2 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 3, T, Q> diagonal2x3 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 4, T, Q> diagonal2x4 + ( + vec<2, T, Q> const& v + ) + { + mat<2, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 2, T, Q> diagonal3x2 + ( + vec<2, T, Q> const& v + ) + { + mat<3, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> diagonal3x3 + ( + vec<3, T, Q> const& v + ) + { + mat<3, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 4, T, Q> diagonal3x4 + ( + vec<3, T, Q> const& v + ) + { + mat<3, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> diagonal4x4 + ( + vec<4, T, Q> const& v + ) + { + mat<4, 4, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + Result[3][3] = v[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 3, T, Q> diagonal4x3 + ( + vec<3, T, Q> const& v + ) + { + mat<4, 3, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + Result[2][2] = v[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 2, T, Q> diagonal4x2 + ( + vec<2, T, Q> const& v + ) + { + mat<4, 2, T, Q> Result(static_cast(1)); + Result[0][0] = v[0]; + Result[1][1] = v[1]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<2, 2, T, Q> adjugate(mat<2, 2, T, Q> const& m) + { + return mat<2, 2, T, Q>( + +m[1][1], -m[0][1], + -m[1][0], +m[0][0]); + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> adjugate(mat<3, 3, T, Q> const& m) + { + T const m00 = determinant(mat<2, 2, T, Q>(m[1][1], m[2][1], m[1][2], m[2][2])); + T const m01 = determinant(mat<2, 2, T, Q>(m[0][1], m[2][1], m[0][2], m[2][2])); + T const m02 = determinant(mat<2, 2, T, Q>(m[0][1], m[1][1], m[0][2], m[1][2])); + + T const m10 = determinant(mat<2, 2, T, Q>(m[1][0], m[2][0], m[1][2], m[2][2])); + T const m11 = determinant(mat<2, 2, T, Q>(m[0][0], m[2][0], m[0][2], m[2][2])); + T const m12 = determinant(mat<2, 2, T, Q>(m[0][0], m[1][0], m[0][2], m[1][2])); + + T const m20 = determinant(mat<2, 2, T, Q>(m[1][0], m[2][0], m[1][1], m[2][1])); + T const m21 = determinant(mat<2, 2, T, Q>(m[0][0], m[2][0], m[0][1], m[2][1])); + T const m22 = determinant(mat<2, 2, T, Q>(m[0][0], m[1][0], m[0][1], m[1][1])); + + return mat<3, 3, T, Q>( + +m00, -m01, +m02, + -m10, +m11, -m12, + +m20, -m21, +m22); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> adjugate(mat<4, 4, T, Q> const& m) + { + T const m00 = determinant(mat<3, 3, T, Q>(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3])); + T const m01 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3])); + T const m02 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3])); + T const m03 = determinant(mat<3, 3, T, Q>(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2])); + + T const m10 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3])); + T const m11 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3])); + T const m12 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3])); + T const m13 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2])); + + T const m20 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3])); + T const m21 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3])); + T const m22 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3])); + T const m23 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2])); + + T const m30 = determinant(mat<3, 3, T, Q>(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3])); + T const m31 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3])); + T const m32 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3])); + T const m33 = determinant(mat<3, 3, T, Q>(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2])); + + return mat<4, 4, T, Q>( + +m00, -m10, +m20, -m30, + -m01, +m11, -m21, +m31, + +m02, -m12, +m22, -m32, + -m03, +m13, -m23, +m33); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_query.hpp b/thirdparty/glm/glm/gtx/matrix_query.hpp new file mode 100644 index 0000000..de8c655 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_query.hpp @@ -0,0 +1,75 @@ +/// @ref gtx_matrix_query +/// @file glm/gtx/matrix_query.hpp +/// +/// @see core (dependence) +/// @see gtx_vector_query (dependence) +/// +/// @defgroup gtx_matrix_query GLM_GTX_matrix_query +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Query to evaluate matrix properties + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/vector_query.hpp" +#include + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_query is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_query extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_query + /// @{ + + /// Return whether a matrix a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<2, 2, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<3, 3, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a null matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNull(mat<4, 4, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is an identity matrix. + /// From GLM_GTX_matrix_query extension. + template class matType> + GLM_FUNC_DECL bool isIdentity(matType const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<2, 2, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<3, 3, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is a normalized matrix. + /// From GLM_GTX_matrix_query extension. + template + GLM_FUNC_DECL bool isNormalized(mat<4, 4, T, Q> const& m, T const& epsilon); + + /// Return whether a matrix is an orthonormalized matrix. + /// From GLM_GTX_matrix_query extension. + template class matType> + GLM_FUNC_DECL bool isOrthogonal(matType const& m, T const& epsilon); + + /// @} +}//namespace glm + +#include "matrix_query.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_query.inl b/thirdparty/glm/glm/gtx/matrix_query.inl new file mode 100644 index 0000000..dc3ec84 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_query.inl @@ -0,0 +1,119 @@ +/// @ref gtx_matrix_query + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool isNull(mat<2, 2, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNull(mat<3, 3, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNull(mat<4, 4, T, Q> const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length() ; ++i) + result = isNull(m[i], epsilon); + return result; + } + + template + GLM_FUNC_QUALIFIER bool isIdentity(mat const& m, T const& epsilon) + { + bool result = true; + for(length_t i = 0; result && i < m.length(); ++i) + { + for(length_t j = 0; result && j < glm::min(i, m[0].length()); ++j) + result = abs(m[i][j]) <= epsilon; + if(result && i < m[0].length()) + result = abs(m[i][i] - 1) <= epsilon; + for(length_t j = i + 1; result && j < m[0].length(); ++j) + result = abs(m[i][j]) <= epsilon; + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<2, 2, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<2, 2, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<3, 3, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<3, 3, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(mat<4, 4, T, Q> const& m, T const& epsilon) + { + bool result(true); + for(length_t i = 0; result && i < m.length(); ++i) + result = isNormalized(m[i], epsilon); + for(length_t i = 0; result && i < m.length(); ++i) + { + typename mat<4, 4, T, Q>::col_type v; + for(length_t j = 0; j < m.length(); ++j) + v[j] = m[j][i]; + result = isNormalized(v, epsilon); + } + return result; + } + + template + GLM_FUNC_QUALIFIER bool isOrthogonal(mat const& m, T const& epsilon) + { + bool result = true; + for(length_t i(0); result && i < m.length(); ++i) + { + result = isNormalized(m[i], epsilon); + for(length_t j(i + 1); result && j < m.length(); ++j) + result = abs(dot(m[i], m[j])) <= epsilon; + } + + if(result) + { + mat tmp = transpose(m); + for(length_t i(0); result && i < m.length(); ++i) + { + result = isNormalized(tmp[i], epsilon); + for(length_t j(i + 1); result && j < m.length(); ++j) + result = abs(dot(tmp[i], tmp[j])) <= epsilon; + } + } + return result; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/matrix_transform_2d.hpp b/thirdparty/glm/glm/gtx/matrix_transform_2d.hpp new file mode 100644 index 0000000..deb8da2 --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_transform_2d.hpp @@ -0,0 +1,79 @@ +/// @ref gtx_matrix_transform_2d +/// @file glm/gtx/matrix_transform_2d.hpp +/// @author Miguel Ángel Pérez Martínez +/// +/// @see core (dependence) +/// +/// @defgroup gtx_matrix_transform_2d GLM_GTX_matrix_transform_2d +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines functions that generate common 2d transformation matrices. + +#pragma once + +// Dependency: +#include "../mat3x3.hpp" +#include "../vec2.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_matrix_transform_2d is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_matrix_transform_2d extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_matrix_transform_2d + /// @{ + + /// Builds a translation 3 * 3 matrix created from a vector of 2 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a translation vector. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> translate( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v); + + /// Builds a rotation 3 * 3 matrix created from an angle. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param angle Rotation angle expressed in radians. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rotate( + mat<3, 3, T, Q> const& m, + T angle); + + /// Builds a scale 3 * 3 matrix created from a vector of 2 components. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param v Coordinates of a scale vector. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> scale( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v); + + /// Builds an horizontal (parallel to the x axis) shear 3 * 3 matrix. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param y Shear factor. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX( + mat<3, 3, T, Q> const& m, + T y); + + /// Builds a vertical (parallel to the y axis) shear 3 * 3 matrix. + /// + /// @param m Input matrix multiplied by this translation matrix. + /// @param x Shear factor. + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY( + mat<3, 3, T, Q> const& m, + T x); + + /// @} +}//namespace glm + +#include "matrix_transform_2d.inl" diff --git a/thirdparty/glm/glm/gtx/matrix_transform_2d.inl b/thirdparty/glm/glm/gtx/matrix_transform_2d.inl new file mode 100644 index 0000000..a68d24d --- /dev/null +++ b/thirdparty/glm/glm/gtx/matrix_transform_2d.inl @@ -0,0 +1,68 @@ +/// @ref gtx_matrix_transform_2d +/// @author Miguel Ángel Pérez Martínez + +#include "../trigonometric.hpp" + +namespace glm +{ + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> translate( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v) + { + mat<3, 3, T, Q> Result(m); + Result[2] = m[0] * v[0] + m[1] * v[1] + m[2]; + return Result; + } + + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> rotate( + mat<3, 3, T, Q> const& m, + T angle) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + mat<3, 3, T, Q> Result; + Result[0] = m[0] * c + m[1] * s; + Result[1] = m[0] * -s + m[1] * c; + Result[2] = m[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> scale( + mat<3, 3, T, Q> const& m, + vec<2, T, Q> const& v) + { + mat<3, 3, T, Q> Result; + Result[0] = m[0] * v[0]; + Result[1] = m[1] * v[1]; + Result[2] = m[2]; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX( + mat<3, 3, T, Q> const& m, + T y) + { + mat<3, 3, T, Q> Result(1); + Result[0][1] = y; + return m * Result; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY( + mat<3, 3, T, Q> const& m, + T x) + { + mat<3, 3, T, Q> Result(1); + Result[1][0] = x; + return m * Result; + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/mixed_product.hpp b/thirdparty/glm/glm/gtx/mixed_product.hpp new file mode 100644 index 0000000..a091274 --- /dev/null +++ b/thirdparty/glm/glm/gtx/mixed_product.hpp @@ -0,0 +1,39 @@ +/// @ref gtx_mixed_product +/// @file glm/gtx/mixed_product.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_mixed_product GLM_GTX_mixed_producte +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Mixed product of 3 vectors. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_mixed_product is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_mixed_product extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_mixed_product + /// @{ + + /// @brief Mixed product of 3 vectors (from GLM_GTX_mixed_product extension) + template + GLM_FUNC_DECL T mixedProduct( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3); + + /// @} +}// namespace glm + +#include "mixed_product.inl" diff --git a/thirdparty/glm/glm/gtx/mixed_product.inl b/thirdparty/glm/glm/gtx/mixed_product.inl new file mode 100644 index 0000000..e5cdbdb --- /dev/null +++ b/thirdparty/glm/glm/gtx/mixed_product.inl @@ -0,0 +1,15 @@ +/// @ref gtx_mixed_product + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T mixedProduct + ( + vec<3, T, Q> const& v1, + vec<3, T, Q> const& v2, + vec<3, T, Q> const& v3 + ) + { + return dot(cross(v1, v2), v3); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/norm.hpp b/thirdparty/glm/glm/gtx/norm.hpp new file mode 100644 index 0000000..d6724a8 --- /dev/null +++ b/thirdparty/glm/glm/gtx/norm.hpp @@ -0,0 +1,86 @@ +/// @ref gtx_norm +/// @file glm/gtx/norm.hpp +/// +/// @see core (dependence) +/// @see gtx_quaternion (dependence) +/// @see gtx_component_wise (dependence) +/// +/// @defgroup gtx_norm GLM_GTX_norm +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Various ways to compute vector norms. + +#pragma once + +// Dependency: +#include "../geometric.hpp" +#include "../gtx/quaternion.hpp" +#include "../gtx/component_wise.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_norm is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_norm extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_norm + /// @{ + + /// Returns the squared length of x. + /// From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T length2(vec const& x); + + /// Returns the squared distance between p0 and p1, i.e., length2(p0 - p1). + /// From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T distance2(vec const& p0, vec const& p1); + + //! Returns the L1 norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l1Norm(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + //! Returns the L1 norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l1Norm(vec<3, T, Q> const& v); + + //! Returns the L2 norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l2Norm(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + //! Returns the L2 norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T l2Norm(vec<3, T, Q> const& x); + + //! Returns the L norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lxNorm(vec<3, T, Q> const& x, vec<3, T, Q> const& y, unsigned int Depth); + + //! Returns the L norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lxNorm(vec<3, T, Q> const& x, unsigned int Depth); + + //! Returns the LMax norm between x and y. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lMaxNorm(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + //! Returns the LMax norm of v. + //! From GLM_GTX_norm extension. + template + GLM_FUNC_DECL T lMaxNorm(vec<3, T, Q> const& x); + + /// @} +}//namespace glm + +#include "norm.inl" diff --git a/thirdparty/glm/glm/gtx/norm.inl b/thirdparty/glm/glm/gtx/norm.inl new file mode 100644 index 0000000..4a9f796 --- /dev/null +++ b/thirdparty/glm/glm/gtx/norm.inl @@ -0,0 +1,95 @@ +/// @ref gtx_norm + +#include "../detail/qualifier.hpp" + +namespace glm{ +namespace detail +{ + template + struct compute_length2 + { + GLM_FUNC_QUALIFIER static T call(vec const& v) + { + return dot(v, v); + } + }; +}//namespace detail + + template + GLM_FUNC_QUALIFIER genType length2(genType x) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'length2' accepts only floating-point inputs"); + return x * x; + } + + template + GLM_FUNC_QUALIFIER T length2(vec const& v) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'length2' accepts only floating-point inputs"); + return detail::compute_length2::value>::call(v); + } + + template + GLM_FUNC_QUALIFIER T distance2(T p0, T p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'distance2' accepts only floating-point inputs"); + return length2(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T distance2(vec const& p0, vec const& p1) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'distance2' accepts only floating-point inputs"); + return length2(p1 - p0); + } + + template + GLM_FUNC_QUALIFIER T l1Norm(vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + return abs(b.x - a.x) + abs(b.y - a.y) + abs(b.z - a.z); + } + + template + GLM_FUNC_QUALIFIER T l1Norm(vec<3, T, Q> const& v) + { + return abs(v.x) + abs(v.y) + abs(v.z); + } + + template + GLM_FUNC_QUALIFIER T l2Norm(vec<3, T, Q> const& a, vec<3, T, Q> const& b + ) + { + return length(b - a); + } + + template + GLM_FUNC_QUALIFIER T l2Norm(vec<3, T, Q> const& v) + { + return length(v); + } + + template + GLM_FUNC_QUALIFIER T lxNorm(vec<3, T, Q> const& x, vec<3, T, Q> const& y, unsigned int Depth) + { + return pow(pow(abs(y.x - x.x), T(Depth)) + pow(abs(y.y - x.y), T(Depth)) + pow(abs(y.z - x.z), T(Depth)), T(1) / T(Depth)); + } + + template + GLM_FUNC_QUALIFIER T lxNorm(vec<3, T, Q> const& v, unsigned int Depth) + { + return pow(pow(abs(v.x), T(Depth)) + pow(abs(v.y), T(Depth)) + pow(abs(v.z), T(Depth)), T(1) / T(Depth)); + } + + template + GLM_FUNC_QUALIFIER T lMaxNorm(vec<3, T, Q> const& a, vec<3, T, Q> const& b) + { + return compMax(abs(b - a)); + } + + template + GLM_FUNC_QUALIFIER T lMaxNorm(vec<3, T, Q> const& v) + { + return compMax(abs(v)); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/normal.hpp b/thirdparty/glm/glm/gtx/normal.hpp new file mode 100644 index 0000000..8b3a4b5 --- /dev/null +++ b/thirdparty/glm/glm/gtx/normal.hpp @@ -0,0 +1,39 @@ +/// @ref gtx_normal +/// @file glm/gtx/normal.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_normal GLM_GTX_normal +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Compute the normal of a triangle. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_normal is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_normal extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_normal + /// @{ + + /// Computes triangle normal from triangle points. + /// + /// @see gtx_normal + template + GLM_FUNC_DECL vec<3, T, Q> triangleNormal(vec<3, T, Q> const& p1, vec<3, T, Q> const& p2, vec<3, T, Q> const& p3); + + /// @} +}//namespace glm + +#include "normal.inl" diff --git a/thirdparty/glm/glm/gtx/normal.inl b/thirdparty/glm/glm/gtx/normal.inl new file mode 100644 index 0000000..74f9fc9 --- /dev/null +++ b/thirdparty/glm/glm/gtx/normal.inl @@ -0,0 +1,15 @@ +/// @ref gtx_normal + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> triangleNormal + ( + vec<3, T, Q> const& p1, + vec<3, T, Q> const& p2, + vec<3, T, Q> const& p3 + ) + { + return normalize(cross(p1 - p2, p1 - p3)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/normalize_dot.hpp b/thirdparty/glm/glm/gtx/normalize_dot.hpp new file mode 100644 index 0000000..04a6b08 --- /dev/null +++ b/thirdparty/glm/glm/gtx/normalize_dot.hpp @@ -0,0 +1,47 @@ +/// @ref gtx_normalize_dot +/// @file glm/gtx/normalize_dot.hpp +/// +/// @see core (dependence) +/// @see gtx_fast_square_root (dependence) +/// +/// @defgroup gtx_normalize_dot GLM_GTX_normalize_dot +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Dot product of vectors that need to be normalize with a single square root. + +#pragma once + +// Dependency: +#include "../gtx/fast_square_root.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_normalize_dot is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_normalize_dot extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_normalize_dot + /// @{ + + /// Normalize parameters and returns the dot product of x and y. + /// It's faster that dot(normalize(x), normalize(y)). + /// + /// @see gtx_normalize_dot extension. + template + GLM_FUNC_DECL T normalizeDot(vec const& x, vec const& y); + + /// Normalize parameters and returns the dot product of x and y. + /// Faster that dot(fastNormalize(x), fastNormalize(y)). + /// + /// @see gtx_normalize_dot extension. + template + GLM_FUNC_DECL T fastNormalizeDot(vec const& x, vec const& y); + + /// @} +}//namespace glm + +#include "normalize_dot.inl" diff --git a/thirdparty/glm/glm/gtx/normalize_dot.inl b/thirdparty/glm/glm/gtx/normalize_dot.inl new file mode 100644 index 0000000..7bcd9a5 --- /dev/null +++ b/thirdparty/glm/glm/gtx/normalize_dot.inl @@ -0,0 +1,16 @@ +/// @ref gtx_normalize_dot + +namespace glm +{ + template + GLM_FUNC_QUALIFIER T normalizeDot(vec const& x, vec const& y) + { + return glm::dot(x, y) * glm::inversesqrt(glm::dot(x, x) * glm::dot(y, y)); + } + + template + GLM_FUNC_QUALIFIER T fastNormalizeDot(vec const& x, vec const& y) + { + return glm::dot(x, y) * glm::fastInverseSqrt(glm::dot(x, x) * glm::dot(y, y)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/number_precision.hpp b/thirdparty/glm/glm/gtx/number_precision.hpp new file mode 100644 index 0000000..5b9663e --- /dev/null +++ b/thirdparty/glm/glm/gtx/number_precision.hpp @@ -0,0 +1,44 @@ +/// @ref gtx_number_precision +/// @file glm/gtx/number_precision.hpp +/// +/// @see core (dependence) +/// @see gtc_type_precision (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_number_precision GLM_GTX_number_precision +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defined size types. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/type_precision.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_number_precision is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_number_precision extension included") +#endif + +namespace glm{ + ///////////////////////////// + // Unsigned int vector types + + /// @addtogroup gtx_number_precision + /// @{ + + ////////////////////// + // Float matrix types + + typedef f32 f32mat1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f32 f32mat1x1; //!< \brief Single-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f64 f64mat1; //!< \brief Double-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + typedef f64 f64mat1x1; //!< \brief Double-qualifier floating-point scalar. (from GLM_GTX_number_precision extension) + + /// @} +}//namespace glm + diff --git a/thirdparty/glm/glm/gtx/optimum_pow.hpp b/thirdparty/glm/glm/gtx/optimum_pow.hpp new file mode 100644 index 0000000..ac34e7e --- /dev/null +++ b/thirdparty/glm/glm/gtx/optimum_pow.hpp @@ -0,0 +1,50 @@ +/// @ref gtx_optimum_pow +/// @file glm/gtx/optimum_pow.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_optimum_pow GLM_GTX_optimum_pow +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Integer exponentiation of power functions. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_optimum_pow is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_optimum_pow extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_optimum_pow + /// @{ + + /// Returns x raised to the power of 2. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow2(genType const& x); + + /// Returns x raised to the power of 3. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow3(genType const& x); + + /// Returns x raised to the power of 4. + /// + /// @see gtx_optimum_pow + template + GLM_FUNC_DECL genType pow4(genType const& x); + + /// @} +}//namespace glm + +#include "optimum_pow.inl" diff --git a/thirdparty/glm/glm/gtx/optimum_pow.inl b/thirdparty/glm/glm/gtx/optimum_pow.inl new file mode 100644 index 0000000..a26c19c --- /dev/null +++ b/thirdparty/glm/glm/gtx/optimum_pow.inl @@ -0,0 +1,22 @@ +/// @ref gtx_optimum_pow + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType pow2(genType const& x) + { + return x * x; + } + + template + GLM_FUNC_QUALIFIER genType pow3(genType const& x) + { + return x * x * x; + } + + template + GLM_FUNC_QUALIFIER genType pow4(genType const& x) + { + return (x * x) * (x * x); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/orthonormalize.hpp b/thirdparty/glm/glm/gtx/orthonormalize.hpp new file mode 100644 index 0000000..801b755 --- /dev/null +++ b/thirdparty/glm/glm/gtx/orthonormalize.hpp @@ -0,0 +1,47 @@ +/// @ref gtx_orthonormalize +/// @file glm/gtx/orthonormalize.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_orthonormalize GLM_GTX_orthonormalize +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Orthonormalize matrices. + +#pragma once + +// Dependency: +#include "../vec3.hpp" +#include "../mat3x3.hpp" +#include "../geometric.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_orthonormalize is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_orthonormalize extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_orthonormalize + /// @{ + + /// Returns the orthonormalized matrix of m. + /// + /// @see gtx_orthonormalize + template + GLM_FUNC_DECL mat<3, 3, T, Q> orthonormalize(mat<3, 3, T, Q> const& m); + + /// Orthonormalizes x according y. + /// + /// @see gtx_orthonormalize + template + GLM_FUNC_DECL vec<3, T, Q> orthonormalize(vec<3, T, Q> const& x, vec<3, T, Q> const& y); + + /// @} +}//namespace glm + +#include "orthonormalize.inl" diff --git a/thirdparty/glm/glm/gtx/orthonormalize.inl b/thirdparty/glm/glm/gtx/orthonormalize.inl new file mode 100644 index 0000000..cb553ba --- /dev/null +++ b/thirdparty/glm/glm/gtx/orthonormalize.inl @@ -0,0 +1,29 @@ +/// @ref gtx_orthonormalize + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> orthonormalize(mat<3, 3, T, Q> const& m) + { + mat<3, 3, T, Q> r = m; + + r[0] = normalize(r[0]); + + T d0 = dot(r[0], r[1]); + r[1] -= r[0] * d0; + r[1] = normalize(r[1]); + + T d1 = dot(r[1], r[2]); + d0 = dot(r[0], r[2]); + r[2] -= r[0] * d0 + r[1] * d1; + r[2] = normalize(r[2]); + + return r; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> orthonormalize(vec<3, T, Q> const& x, vec<3, T, Q> const& y) + { + return normalize(x - y * dot(y, x)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/pca.hpp b/thirdparty/glm/glm/gtx/pca.hpp new file mode 100644 index 0000000..26f9aec --- /dev/null +++ b/thirdparty/glm/glm/gtx/pca.hpp @@ -0,0 +1,112 @@ +/// @ref gtx_pca +/// @file glm/gtx/pca.hpp +/// +/// @see core (dependence) +/// @see ext_scalar_relational (dependence) +/// +/// @defgroup gtx_pca GLM_GTX_pca +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Implements functions required for fundamental 'princple component analysis' in 2D, 3D, and 4D: +/// 1) Computing a covariance matrics from a list of _relative_ position vectors +/// 2) Compute the eigenvalues and eigenvectors of the covariance matrics +/// This is useful, e.g., to compute an object-aligned bounding box from vertices of an object. +/// https://en.wikipedia.org/wiki/Principal_component_analysis +/// +/// Example: +/// ``` +/// std::vector ptData; +/// // ... fill ptData with some point data, e.g. vertices +/// +/// glm::dvec3 center = computeCenter(ptData); +/// +/// glm::dmat3 covarMat = glm::computeCovarianceMatrix(ptData.data(), ptData.size(), center); +/// +/// glm::dvec3 evals; +/// glm::dmat3 evecs; +/// int evcnt = glm::findEigenvaluesSymReal(covarMat, evals, evecs); +/// +/// if(evcnt != 3) +/// // ... error handling +/// +/// glm::sortEigenvalues(evals, evecs); +/// +/// // ... now evecs[0] points in the direction (symmetric) of the largest spatial distribution within ptData +/// ``` + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../ext/scalar_relational.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_pca is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_pca extension included") +#endif + +namespace glm { + /// @addtogroup gtx_pca + /// @{ + + /// Compute a covariance matrix form an array of relative coordinates `v` (e.g., relative to the center of gravity of the object) + /// @param v Points to a memory holding `n` times vectors + /// @param n Number of points in v + template + GLM_INLINE mat computeCovarianceMatrix(vec const* v, size_t n); + + /// Compute a covariance matrix form an array of absolute coordinates `v` and a precomputed center of gravity `c` + /// @param v Points to a memory holding `n` times vectors + /// @param n Number of points in v + /// @param c Precomputed center of gravity + template + GLM_INLINE mat computeCovarianceMatrix(vec const* v, size_t n, vec const& c); + + /// Compute a covariance matrix form a pair of iterators `b` (begin) and `e` (end) of a container with relative coordinates (e.g., relative to the center of gravity of the object) + /// Dereferencing an iterator of type I must yield a `vec<D, T, Q%gt;` + template + GLM_FUNC_DECL mat computeCovarianceMatrix(I const& b, I const& e); + + /// Compute a covariance matrix form a pair of iterators `b` (begin) and `e` (end) of a container with absolute coordinates and a precomputed center of gravity `c` + /// Dereferencing an iterator of type I must yield a `vec<D, T, Q%gt;` + template + GLM_FUNC_DECL mat computeCovarianceMatrix(I const& b, I const& e, vec const& c); + + /// Assuming the provided covariance matrix `covarMat` is symmetric and real-valued, this function find the `D` Eigenvalues of the matrix, and also provides the corresponding Eigenvectors. + /// Note: the data in `outEigenvalues` and `outEigenvectors` are in matching order, i.e. `outEigenvector[i]` is the Eigenvector of the Eigenvalue `outEigenvalue[i]`. + /// This is a numeric implementation to find the Eigenvalues, using 'QL decomposition` (variant of QR decomposition: https://en.wikipedia.org/wiki/QR_decomposition). + /// + /// @param[in] covarMat A symmetric, real-valued covariance matrix, e.g. computed from computeCovarianceMatrix + /// @param[out] outEigenvalues Vector to receive the found eigenvalues + /// @param[out] outEigenvectors Matrix to receive the found eigenvectors corresponding to the found eigenvalues, as column vectors + /// @return The number of eigenvalues found, usually D if the precondition of the covariance matrix is met. + template + GLM_FUNC_DECL unsigned int findEigenvaluesSymReal + ( + mat const& covarMat, + vec& outEigenvalues, + mat& outEigenvectors + ); + + /// Sorts a group of Eigenvalues&Eigenvectors, for largest Eigenvalue to smallest Eigenvalue. + /// The data in `outEigenvalues` and `outEigenvectors` are assumed to be matching order, i.e. `outEigenvector[i]` is the Eigenvector of the Eigenvalue `outEigenvalue[i]`. + template + GLM_FUNC_DISCARD_DECL void sortEigenvalues(vec<2, T, Q>& eigenvalues, mat<2, 2, T, Q>& eigenvectors); + + /// Sorts a group of Eigenvalues&Eigenvectors, for largest Eigenvalue to smallest Eigenvalue. + /// The data in `outEigenvalues` and `outEigenvectors` are assumed to be matching order, i.e. `outEigenvector[i]` is the Eigenvector of the Eigenvalue `outEigenvalue[i]`. + template + GLM_FUNC_DISCARD_DECL void sortEigenvalues(vec<3, T, Q>& eigenvalues, mat<3, 3, T, Q>& eigenvectors); + + /// Sorts a group of Eigenvalues&Eigenvectors, for largest Eigenvalue to smallest Eigenvalue. + /// The data in `outEigenvalues` and `outEigenvectors` are assumed to be matching order, i.e. `outEigenvector[i]` is the Eigenvector of the Eigenvalue `outEigenvalue[i]`. + template + GLM_FUNC_DISCARD_DECL void sortEigenvalues(vec<4, T, Q>& eigenvalues, mat<4, 4, T, Q>& eigenvectors); + + /// @} +}//namespace glm + +#include "pca.inl" diff --git a/thirdparty/glm/glm/gtx/pca.inl b/thirdparty/glm/glm/gtx/pca.inl new file mode 100644 index 0000000..94cae94 --- /dev/null +++ b/thirdparty/glm/glm/gtx/pca.inl @@ -0,0 +1,343 @@ +/// @ref gtx_pca + +#ifndef GLM_HAS_CXX11_STL +#include +#else +#include +#endif + +namespace glm { + + + template + GLM_FUNC_QUALIFIER mat computeCovarianceMatrix(vec const* v, size_t n) + { + return computeCovarianceMatrix const*>(v, v + n); + } + + + template + GLM_FUNC_QUALIFIER mat computeCovarianceMatrix(vec const* v, size_t n, vec const& c) + { + return computeCovarianceMatrix const*>(v, v + n, c); + } + + + template + GLM_FUNC_QUALIFIER mat computeCovarianceMatrix(I const& b, I const& e) + { + glm::mat m(0); + + size_t cnt = 0; + for(I i = b; i != e; i++) + { + vec const& v = *i; + for(length_t x = 0; x < D; ++x) + for(length_t y = 0; y < D; ++y) + m[x][y] += static_cast(v[x] * v[y]); + cnt++; + } + if(cnt > 0) + m /= static_cast(cnt); + + return m; + } + + + template + GLM_FUNC_QUALIFIER mat computeCovarianceMatrix(I const& b, I const& e, vec const& c) + { + glm::mat m(0); + glm::vec v; + + size_t cnt = 0; + for(I i = b; i != e; i++) + { + v = *i - c; + for(length_t x = 0; x < D; ++x) + for(length_t y = 0; y < D; ++y) + m[x][y] += static_cast(v[x] * v[y]); + cnt++; + } + if(cnt > 0) + m /= static_cast(cnt); + + return m; + } + + namespace _internal_ + { + + template + GLM_FUNC_QUALIFIER static T transferSign(T const& v, T const& s) + { + return ((s) >= 0 ? glm::abs(v) : -glm::abs(v)); + } + + template + GLM_FUNC_QUALIFIER static T pythag(T const& a, T const& b) { + static const T epsilon = static_cast(0.0000001); + T absa = glm::abs(a); + T absb = glm::abs(b); + if(absa > absb) { + absb /= absa; + absb *= absb; + return absa * glm::sqrt(static_cast(1) + absb); + } + if(glm::equal(absb, 0, epsilon)) return static_cast(0); + absa /= absb; + absa *= absa; + return absb * glm::sqrt(static_cast(1) + absa); + } + + } + + template + GLM_FUNC_QUALIFIER unsigned int findEigenvaluesSymReal + ( + mat const& covarMat, + vec& outEigenvalues, + mat& outEigenvectors + ) + { + using _internal_::transferSign; + using _internal_::pythag; + + T a[D * D]; // matrix -- input and workspace for algorithm (will be changed inplace) + T d[D]; // diagonal elements + T e[D]; // off-diagonal elements + + for(length_t r = 0; r < D; r++) + for(length_t c = 0; c < D; c++) + a[(r) * D + (c)] = covarMat[c][r]; + + // 1. Householder reduction. + length_t l, k, j, i; + T scale, hh, h, g, f; + static const T epsilon = static_cast(0.0000001); + + for(i = D; i >= 2; i--) + { + l = i - 1; + h = scale = 0; + if(l > 1) + { + for(k = 1; k <= l; k++) + { + scale += glm::abs(a[(i - 1) * D + (k - 1)]); + } + if(glm::equal(scale, 0, epsilon)) + { + e[i - 1] = a[(i - 1) * D + (l - 1)]; + } + else + { + for(k = 1; k <= l; k++) + { + a[(i - 1) * D + (k - 1)] /= scale; + h += a[(i - 1) * D + (k - 1)] * a[(i - 1) * D + (k - 1)]; + } + f = a[(i - 1) * D + (l - 1)]; + g = ((f >= 0) ? -glm::sqrt(h) : glm::sqrt(h)); + e[i - 1] = scale * g; + h -= f * g; + a[(i - 1) * D + (l - 1)] = f - g; + f = 0; + for(j = 1; j <= l; j++) + { + a[(j - 1) * D + (i - 1)] = a[(i - 1) * D + (j - 1)] / h; + g = 0; + for(k = 1; k <= j; k++) + { + g += a[(j - 1) * D + (k - 1)] * a[(i - 1) * D + (k - 1)]; + } + for(k = j + 1; k <= l; k++) + { + g += a[(k - 1) * D + (j - 1)] * a[(i - 1) * D + (k - 1)]; + } + e[j - 1] = g / h; + f += e[j - 1] * a[(i - 1) * D + (j - 1)]; + } + hh = f / (h + h); + for(j = 1; j <= l; j++) + { + f = a[(i - 1) * D + (j - 1)]; + e[j - 1] = g = e[j - 1] - hh * f; + for(k = 1; k <= j; k++) + { + a[(j - 1) * D + (k - 1)] -= (f * e[k - 1] + g * a[(i - 1) * D + (k - 1)]); + } + } + } + } + else + { + e[i - 1] = a[(i - 1) * D + (l - 1)]; + } + d[i - 1] = h; + } + d[0] = 0; + e[0] = 0; + for(i = 1; i <= D; i++) + { + l = i - 1; + if(!glm::equal(d[i - 1], 0, epsilon)) + { + for(j = 1; j <= l; j++) + { + g = 0; + for(k = 1; k <= l; k++) + { + g += a[(i - 1) * D + (k - 1)] * a[(k - 1) * D + (j - 1)]; + } + for(k = 1; k <= l; k++) + { + a[(k - 1) * D + (j - 1)] -= g * a[(k - 1) * D + (i - 1)]; + } + } + } + d[i - 1] = a[(i - 1) * D + (i - 1)]; + a[(i - 1) * D + (i - 1)] = 1; + for(j = 1; j <= l; j++) + { + a[(j - 1) * D + (i - 1)] = a[(i - 1) * D + (j - 1)] = 0; + } + } + + // 2. Calculation of eigenvalues and eigenvectors (QL algorithm) + length_t m, iter; + T s, r, p, dd, c, b; + const length_t MAX_ITER = 30; + + for(i = 2; i <= D; i++) + { + e[i - 2] = e[i - 1]; + } + e[D - 1] = 0; + + for(l = 1; l <= D; l++) + { + iter = 0; + do + { + for(m = l; m <= D - 1; m++) + { + dd = glm::abs(d[m - 1]) + glm::abs(d[m - 1 + 1]); + if(glm::equal(glm::abs(e[m - 1]) + dd, dd, epsilon)) + break; + } + if(m != l) + { + if(iter++ == MAX_ITER) + { + return 0; // Too many iterations in FindEigenvalues + } + g = (d[l - 1 + 1] - d[l - 1]) / (2 * e[l - 1]); + r = pythag(g, 1); + g = d[m - 1] - d[l - 1] + e[l - 1] / (g + transferSign(r, g)); + s = c = 1; + p = 0; + for(i = m - 1; i >= l; i--) + { + f = s * e[i - 1]; + b = c * e[i - 1]; + e[i - 1 + 1] = r = pythag(f, g); + if(glm::equal(r, 0, epsilon)) + { + d[i - 1 + 1] -= p; + e[m - 1] = 0; + break; + } + s = f / r; + c = g / r; + g = d[i - 1 + 1] - p; + r = (d[i - 1] - g) * s + 2 * c * b; + d[i - 1 + 1] = g + (p = s * r); + g = c * r - b; + for(k = 1; k <= D; k++) + { + f = a[(k - 1) * D + (i - 1 + 1)]; + a[(k - 1) * D + (i - 1 + 1)] = s * a[(k - 1) * D + (i - 1)] + c * f; + a[(k - 1) * D + (i - 1)] = c * a[(k - 1) * D + (i - 1)] - s * f; + } + } + if(glm::equal(r, 0, epsilon) && (i >= l)) + continue; + d[l - 1] -= p; + e[l - 1] = g; + e[m - 1] = 0; + } + } while(m != l); + } + + // 3. output + for(i = 0; i < D; i++) + outEigenvalues[i] = d[i]; + for(i = 0; i < D; i++) + for(j = 0; j < D; j++) + outEigenvectors[i][j] = a[(j) * D + (i)]; + + return D; + } + + template + GLM_FUNC_QUALIFIER void sortEigenvalues(vec<2, T, Q>& eigenvalues, mat<2, 2, T, Q>& eigenvectors) + { + if (eigenvalues[0] < eigenvalues[1]) + { + std::swap(eigenvalues[0], eigenvalues[1]); + std::swap(eigenvectors[0], eigenvectors[1]); + } + } + + template + GLM_FUNC_QUALIFIER void sortEigenvalues(vec<3, T, Q>& eigenvalues, mat<3, 3, T, Q>& eigenvectors) + { + if (eigenvalues[0] < eigenvalues[1]) + { + std::swap(eigenvalues[0], eigenvalues[1]); + std::swap(eigenvectors[0], eigenvectors[1]); + } + if (eigenvalues[0] < eigenvalues[2]) + { + std::swap(eigenvalues[0], eigenvalues[2]); + std::swap(eigenvectors[0], eigenvectors[2]); + } + if (eigenvalues[1] < eigenvalues[2]) + { + std::swap(eigenvalues[1], eigenvalues[2]); + std::swap(eigenvectors[1], eigenvectors[2]); + } + } + + template + GLM_FUNC_QUALIFIER void sortEigenvalues(vec<4, T, Q>& eigenvalues, mat<4, 4, T, Q>& eigenvectors) + { + if (eigenvalues[0] < eigenvalues[2]) + { + std::swap(eigenvalues[0], eigenvalues[2]); + std::swap(eigenvectors[0], eigenvectors[2]); + } + if (eigenvalues[1] < eigenvalues[3]) + { + std::swap(eigenvalues[1], eigenvalues[3]); + std::swap(eigenvectors[1], eigenvectors[3]); + } + if (eigenvalues[0] < eigenvalues[1]) + { + std::swap(eigenvalues[0], eigenvalues[1]); + std::swap(eigenvectors[0], eigenvectors[1]); + } + if (eigenvalues[2] < eigenvalues[3]) + { + std::swap(eigenvalues[2], eigenvalues[3]); + std::swap(eigenvectors[2], eigenvectors[3]); + } + if (eigenvalues[1] < eigenvalues[2]) + { + std::swap(eigenvalues[1], eigenvalues[2]); + std::swap(eigenvectors[1], eigenvectors[2]); + } + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/perpendicular.hpp b/thirdparty/glm/glm/gtx/perpendicular.hpp new file mode 100644 index 0000000..4087ab0 --- /dev/null +++ b/thirdparty/glm/glm/gtx/perpendicular.hpp @@ -0,0 +1,39 @@ +/// @ref gtx_perpendicular +/// @file glm/gtx/perpendicular.hpp +/// +/// @see core (dependence) +/// @see gtx_projection (dependence) +/// +/// @defgroup gtx_perpendicular GLM_GTX_perpendicular +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Perpendicular of a vector from other one + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/projection.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_perpendicular is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_perpendicular extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_perpendicular + /// @{ + + //! Projects x a perpendicular axis of Normal. + //! From GLM_GTX_perpendicular extension. + template + GLM_FUNC_DECL genType perp(genType const& x, genType const& Normal); + + /// @} +}//namespace glm + +#include "perpendicular.inl" diff --git a/thirdparty/glm/glm/gtx/perpendicular.inl b/thirdparty/glm/glm/gtx/perpendicular.inl new file mode 100644 index 0000000..1e72f33 --- /dev/null +++ b/thirdparty/glm/glm/gtx/perpendicular.inl @@ -0,0 +1,10 @@ +/// @ref gtx_perpendicular + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType perp(genType const& x, genType const& Normal) + { + return x - proj(x, Normal); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/polar_coordinates.hpp b/thirdparty/glm/glm/gtx/polar_coordinates.hpp new file mode 100644 index 0000000..c27aacf --- /dev/null +++ b/thirdparty/glm/glm/gtx/polar_coordinates.hpp @@ -0,0 +1,46 @@ +/// @ref gtx_polar_coordinates +/// @file glm/gtx/polar_coordinates.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_polar_coordinates GLM_GTX_polar_coordinates +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Conversion from Euclidean space to polar space and revert. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_polar_coordinates is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_polar_coordinates extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_polar_coordinates + /// @{ + + /// Convert Euclidean to Polar coordinates, x is the latitude, y the longitude and z the xz distance. + /// + /// @see gtx_polar_coordinates + template + GLM_FUNC_DECL vec<3, T, Q> polar( + vec<3, T, Q> const& euclidean); + + /// Convert Polar to Euclidean coordinates. + /// + /// @see gtx_polar_coordinates + template + GLM_FUNC_DECL vec<3, T, Q> euclidean( + vec<2, T, Q> const& polar); + + /// @} +}//namespace glm + +#include "polar_coordinates.inl" diff --git a/thirdparty/glm/glm/gtx/polar_coordinates.inl b/thirdparty/glm/glm/gtx/polar_coordinates.inl new file mode 100644 index 0000000..371c8dd --- /dev/null +++ b/thirdparty/glm/glm/gtx/polar_coordinates.inl @@ -0,0 +1,36 @@ +/// @ref gtx_polar_coordinates + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> polar + ( + vec<3, T, Q> const& euclidean + ) + { + T const Length(length(euclidean)); + vec<3, T, Q> const tmp(euclidean / Length); + T const xz_dist(sqrt(tmp.x * tmp.x + tmp.z * tmp.z)); + + return vec<3, T, Q>( + asin(tmp.y), // latitude + atan(tmp.x, tmp.z), // longitude + xz_dist); // xz distance + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> euclidean + ( + vec<2, T, Q> const& polar + ) + { + T const latitude(polar.x); + T const longitude(polar.y); + + return vec<3, T, Q>( + cos(latitude) * sin(longitude), + sin(latitude), + cos(latitude) * cos(longitude)); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/projection.hpp b/thirdparty/glm/glm/gtx/projection.hpp new file mode 100644 index 0000000..a438f39 --- /dev/null +++ b/thirdparty/glm/glm/gtx/projection.hpp @@ -0,0 +1,41 @@ +/// @ref gtx_projection +/// @file glm/gtx/projection.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_projection GLM_GTX_projection +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Projection of a vector to other one + +#pragma once + +// Dependency: +#include "../geometric.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_projection is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_projection extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_projection + /// @{ + + /// Projects x on Normal. + /// + /// @param[in] x A vector to project + /// @param[in] Normal A normal that doesn't need to be of unit length. + /// + /// @see gtx_projection + template + GLM_FUNC_DECL genType proj(genType const& x, genType const& Normal); + + /// @} +}//namespace glm + +#include "projection.inl" diff --git a/thirdparty/glm/glm/gtx/projection.inl b/thirdparty/glm/glm/gtx/projection.inl new file mode 100644 index 0000000..f23f884 --- /dev/null +++ b/thirdparty/glm/glm/gtx/projection.inl @@ -0,0 +1,10 @@ +/// @ref gtx_projection + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType proj(genType const& x, genType const& Normal) + { + return glm::dot(x, Normal) / glm::dot(Normal, Normal) * Normal; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/quaternion.hpp b/thirdparty/glm/glm/gtx/quaternion.hpp new file mode 100644 index 0000000..f51c521 --- /dev/null +++ b/thirdparty/glm/glm/gtx/quaternion.hpp @@ -0,0 +1,172 @@ +/// @ref gtx_quaternion +/// @file glm/gtx/quaternion.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_quaternion GLM_GTX_quaternion +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extended quaternion types and functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/constants.hpp" +#include "../gtc/quaternion.hpp" +#include "../ext/quaternion_exponential.hpp" +#include "../gtx/norm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_quaternion is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_quaternion extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_quaternion + /// @{ + + /// Create an identity quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL GLM_CONSTEXPR qua quat_identity(); + + /// Compute a cross product between a quaternion and a vector. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> cross( + qua const& q, + vec<3, T, Q> const& v); + + //! Compute a cross product between a vector and a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL GLM_CONSTEXPR vec<3, T, Q> cross( + vec<3, T, Q> const& v, + qua const& q); + + //! Compute a point on a path according squad equation. + //! q1 and q2 are control points; s1 and s2 are intermediate control points. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua squad( + qua const& q1, + qua const& q2, + qua const& s1, + qua const& s2, + T const& h); + + //! Returns an intermediate control point for squad interpolation. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua intermediate( + qua const& prev, + qua const& curr, + qua const& next); + + //! Returns quarternion square root. + /// + /// @see gtx_quaternion + //template + //qua sqrt( + // qua const& q); + + //! Rotates a 3 components vector by a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<3, T, Q> rotate( + qua const& q, + vec<3, T, Q> const& v); + + /// Rotates a 4 components vector by a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL vec<4, T, Q> rotate( + qua const& q, + vec<4, T, Q> const& v); + + /// Extract the real component of a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL T extractRealComponent( + qua const& q); + + /// Converts a quaternion to a 3 * 3 matrix. + /// + /// @see gtx_quaternion + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> toMat3( + qua const& x){return mat3_cast(x);} + + /// Converts a quaternion to a 4 * 4 matrix. + /// + /// @see gtx_quaternion + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> toMat4( + qua const& x){return mat4_cast(x);} + + /// Converts a 3 * 3 matrix to a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_QUALIFIER qua toQuat( + mat<3, 3, T, Q> const& x){return quat_cast(x);} + + /// Converts a 4 * 4 matrix to a quaternion. + /// + /// @see gtx_quaternion + template + GLM_FUNC_QUALIFIER qua toQuat( + mat<4, 4, T, Q> const& x){return quat_cast(x);} + + /// Quaternion interpolation using the rotation short path. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua shortMix( + qua const& x, + qua const& y, + T const& a); + + /// Quaternion normalized linear interpolation. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua fastMix( + qua const& x, + qua const& y, + T const& a); + + /// Compute the rotation between two vectors. + /// @param orig vector, needs to be normalized + /// @param dest vector, needs to be normalized + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL qua rotation( + vec<3, T, Q> const& orig, + vec<3, T, Q> const& dest); + + /// Returns the squared length of x. + /// + /// @see gtx_quaternion + template + GLM_FUNC_DECL GLM_CONSTEXPR T length2(qua const& q); + + /// @} +}//namespace glm + +#include "quaternion.inl" diff --git a/thirdparty/glm/glm/gtx/quaternion.inl b/thirdparty/glm/glm/gtx/quaternion.inl new file mode 100644 index 0000000..5e18899 --- /dev/null +++ b/thirdparty/glm/glm/gtx/quaternion.inl @@ -0,0 +1,159 @@ +/// @ref gtx_quaternion + +#include +#include "../gtc/constants.hpp" + +namespace glm +{ + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR qua quat_identity() + { + return qua::wxyz(static_cast(1), static_cast(0), static_cast(0), static_cast(0)); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> cross(vec<3, T, Q> const& v, qua const& q) + { + return inverse(q) * v; + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR vec<3, T, Q> cross(qua const& q, vec<3, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER qua squad + ( + qua const& q1, + qua const& q2, + qua const& s1, + qua const& s2, + T const& h) + { + return mix(mix(q1, q2, h), mix(s1, s2, h), static_cast(2) * (static_cast(1) - h) * h); + } + + template + GLM_FUNC_QUALIFIER qua intermediate + ( + qua const& prev, + qua const& curr, + qua const& next + ) + { + qua invQuat = inverse(curr); + return exp((log(next * invQuat) + log(prev * invQuat)) / static_cast(-4)) * curr; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotate(qua const& q, vec<3, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotate(qua const& q, vec<4, T, Q> const& v) + { + return q * v; + } + + template + GLM_FUNC_QUALIFIER T extractRealComponent(qua const& q) + { + T w = static_cast(1) - q.x * q.x - q.y * q.y - q.z * q.z; + if(w < T(0)) + return T(0); + else + return -sqrt(w); + } + + template + GLM_FUNC_QUALIFIER GLM_CONSTEXPR T length2(qua const& q) + { + return q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; + } + + template + GLM_FUNC_QUALIFIER qua shortMix(qua const& x, qua const& y, T const& a) + { + if(a <= static_cast(0)) return x; + if(a >= static_cast(1)) return y; + + T fCos = dot(x, y); + qua y2(y); //BUG!!! qua y2; + if(fCos < static_cast(0)) + { + y2 = -y; + fCos = -fCos; + } + + //if(fCos > 1.0f) // problem + T k0, k1; + if(fCos > (static_cast(1) - epsilon())) + { + k0 = static_cast(1) - a; + k1 = static_cast(0) + a; //BUG!!! 1.0f + a; + } + else + { + T fSin = sqrt(T(1) - fCos * fCos); + T fAngle = atan(fSin, fCos); + T fOneOverSin = static_cast(1) / fSin; + k0 = sin((static_cast(1) - a) * fAngle) * fOneOverSin; + k1 = sin((static_cast(0) + a) * fAngle) * fOneOverSin; + } + + return qua::wxyz( + k0 * x.w + k1 * y2.w, + k0 * x.x + k1 * y2.x, + k0 * x.y + k1 * y2.y, + k0 * x.z + k1 * y2.z); + } + + template + GLM_FUNC_QUALIFIER qua fastMix(qua const& x, qua const& y, T const& a) + { + return glm::normalize(x * (static_cast(1) - a) + (y * a)); + } + + template + GLM_FUNC_QUALIFIER qua rotation(vec<3, T, Q> const& orig, vec<3, T, Q> const& dest) + { + T cosTheta = dot(orig, dest); + vec<3, T, Q> rotationAxis; + + if(cosTheta >= static_cast(1) - epsilon()) { + // orig and dest point in the same direction + return quat_identity(); + } + + if(cosTheta < static_cast(-1) + epsilon()) + { + // special case when vectors in opposite directions : + // there is no "ideal" rotation axis + // So guess one; any will do as long as it's perpendicular to start + // This implementation favors a rotation around the Up axis (Y), + // since it's often what you want to do. + rotationAxis = cross(vec<3, T, Q>(0, 0, 1), orig); + if(length2(rotationAxis) < epsilon()) // bad luck, they were parallel, try again! + rotationAxis = cross(vec<3, T, Q>(1, 0, 0), orig); + + rotationAxis = normalize(rotationAxis); + return angleAxis(pi(), rotationAxis); + } + + // Implementation from Stan Melax's Game Programming Gems 1 article + rotationAxis = cross(orig, dest); + + T s = sqrt((T(1) + cosTheta) * static_cast(2)); + T invs = static_cast(1) / s; + + return qua::wxyz( + s * static_cast(0.5f), + rotationAxis.x * invs, + rotationAxis.y * invs, + rotationAxis.z * invs); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/range.hpp b/thirdparty/glm/glm/gtx/range.hpp new file mode 100644 index 0000000..50c5e57 --- /dev/null +++ b/thirdparty/glm/glm/gtx/range.hpp @@ -0,0 +1,96 @@ +/// @ref gtx_range +/// @file glm/gtx/range.hpp +/// @author Joshua Moerman +/// +/// @defgroup gtx_range GLM_GTX_range +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines begin and end for vectors and matrices. Useful for range-based for loop. +/// The range is defined over the elements, not over columns or rows (e.g. mat4 has 16 elements). + +#pragma once + +// Dependencies +#include "../detail/setup.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_range is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_range extension included") +#endif + +#include "../gtc/type_ptr.hpp" +#include "../gtc/vec1.hpp" + +namespace glm +{ + /// @addtogroup gtx_range + /// @{ + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(push) +# pragma warning(disable : 4100) // unreferenced formal parameter +# endif + + template + inline length_t components(vec<1, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<2, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<3, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(vec<4, T, Q> const& v) + { + return v.length(); + } + + template + inline length_t components(genType const& m) + { + return m.length() * m[0].length(); + } + + template + inline typename genType::value_type const * begin(genType const& v) + { + return value_ptr(v); + } + + template + inline typename genType::value_type const * end(genType const& v) + { + return begin(v) + components(v); + } + + template + inline typename genType::value_type * begin(genType& v) + { + return value_ptr(v); + } + + template + inline typename genType::value_type * end(genType& v) + { + return begin(v) + components(v); + } + +# if GLM_COMPILER & GLM_COMPILER_VC +# pragma warning(pop) +# endif + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/raw_data.hpp b/thirdparty/glm/glm/gtx/raw_data.hpp new file mode 100644 index 0000000..3bc27b9 --- /dev/null +++ b/thirdparty/glm/glm/gtx/raw_data.hpp @@ -0,0 +1,49 @@ +/// @ref gtx_raw_data +/// @file glm/gtx/raw_data.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_raw_data GLM_GTX_raw_data +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Projection of a vector to other one + +#pragma once + +// Dependencies +#include "../ext/scalar_uint_sized.hpp" +#include "../detail/setup.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_raw_data is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_raw_data extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_raw_data + /// @{ + + //! Type for byte numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint8 byte; + + //! Type for word numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint16 word; + + //! Type for dword numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint32 dword; + + //! Type for qword numbers. + //! From GLM_GTX_raw_data extension. + typedef detail::uint64 qword; + + /// @} +}// namespace glm + +#include "raw_data.inl" diff --git a/thirdparty/glm/glm/gtx/raw_data.inl b/thirdparty/glm/glm/gtx/raw_data.inl new file mode 100644 index 0000000..c740317 --- /dev/null +++ b/thirdparty/glm/glm/gtx/raw_data.inl @@ -0,0 +1,2 @@ +/// @ref gtx_raw_data + diff --git a/thirdparty/glm/glm/gtx/rotate_normalized_axis.hpp b/thirdparty/glm/glm/gtx/rotate_normalized_axis.hpp new file mode 100644 index 0000000..02c3f5c --- /dev/null +++ b/thirdparty/glm/glm/gtx/rotate_normalized_axis.hpp @@ -0,0 +1,66 @@ +/// @ref gtx_rotate_normalized_axis +/// @file glm/gtx/rotate_normalized_axis.hpp +/// +/// @see core (dependence) +/// @see gtc_matrix_transform +/// @see gtc_quaternion +/// +/// @defgroup gtx_rotate_normalized_axis GLM_GTX_rotate_normalized_axis +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Quaternions and matrices rotations around normalized axis. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/epsilon.hpp" +#include "../gtc/quaternion.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_rotate_normalized_axis is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_rotate_normalized_axis extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_rotate_normalized_axis + /// @{ + + /// Builds a rotation 4 * 4 matrix created from a normalized axis and an angle. + /// + /// @param m Input matrix multiplied by this rotation matrix. + /// @param angle Rotation angle expressed in radians. + /// @param axis Rotation axis, must be normalized. + /// @tparam T Value type used to build the matrix. Currently supported: half (not recommended), float or double. + /// + /// @see gtx_rotate_normalized_axis + /// @see - rotate(T angle, T x, T y, T z) + /// @see - rotate(mat<4, 4, T, Q> const& m, T angle, T x, T y, T z) + /// @see - rotate(T angle, vec<3, T, Q> const& v) + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotateNormalizedAxis( + mat<4, 4, T, Q> const& m, + T const& angle, + vec<3, T, Q> const& axis); + + /// Rotates a quaternion from a vector of 3 components normalized axis and an angle. + /// + /// @param q Source orientation + /// @param angle Angle expressed in radians. + /// @param axis Normalized axis of the rotation, must be normalized. + /// + /// @see gtx_rotate_normalized_axis + template + GLM_FUNC_DECL qua rotateNormalizedAxis( + qua const& q, + T const& angle, + vec<3, T, Q> const& axis); + + /// @} +}//namespace glm + +#include "rotate_normalized_axis.inl" diff --git a/thirdparty/glm/glm/gtx/rotate_normalized_axis.inl b/thirdparty/glm/glm/gtx/rotate_normalized_axis.inl new file mode 100644 index 0000000..352a56c --- /dev/null +++ b/thirdparty/glm/glm/gtx/rotate_normalized_axis.inl @@ -0,0 +1,58 @@ +/// @ref gtx_rotate_normalized_axis + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotateNormalizedAxis + ( + mat<4, 4, T, Q> const& m, + T const& angle, + vec<3, T, Q> const& v + ) + { + T const a = angle; + T const c = cos(a); + T const s = sin(a); + + vec<3, T, Q> const axis(v); + + vec<3, T, Q> const temp((static_cast(1) - c) * axis); + + mat<4, 4, T, Q> Rotate; + Rotate[0][0] = c + temp[0] * axis[0]; + Rotate[0][1] = 0 + temp[0] * axis[1] + s * axis[2]; + Rotate[0][2] = 0 + temp[0] * axis[2] - s * axis[1]; + + Rotate[1][0] = 0 + temp[1] * axis[0] - s * axis[2]; + Rotate[1][1] = c + temp[1] * axis[1]; + Rotate[1][2] = 0 + temp[1] * axis[2] + s * axis[0]; + + Rotate[2][0] = 0 + temp[2] * axis[0] + s * axis[1]; + Rotate[2][1] = 0 + temp[2] * axis[1] - s * axis[0]; + Rotate[2][2] = c + temp[2] * axis[2]; + + mat<4, 4, T, Q> Result; + Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + Result[3] = m[3]; + return Result; + } + + template + GLM_FUNC_QUALIFIER qua rotateNormalizedAxis + ( + qua const& q, + T const& angle, + vec<3, T, Q> const& v + ) + { + vec<3, T, Q> const Tmp(v); + + T const AngleRad(angle); + T const Sin = sin(AngleRad * T(0.5)); + + return q * qua::wxyz(cos(AngleRad * static_cast(0.5)), Tmp.x * Sin, Tmp.y * Sin, Tmp.z * Sin); + //return gtc::quaternion::cross(q, tquat(cos(AngleRad * T(0.5)), Tmp.x * fSin, Tmp.y * fSin, Tmp.z * fSin)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/rotate_vector.hpp b/thirdparty/glm/glm/gtx/rotate_vector.hpp new file mode 100644 index 0000000..b7345bf --- /dev/null +++ b/thirdparty/glm/glm/gtx/rotate_vector.hpp @@ -0,0 +1,121 @@ +/// @ref gtx_rotate_vector +/// @file glm/gtx/rotate_vector.hpp +/// +/// @see core (dependence) +/// @see gtx_transform (dependence) +/// +/// @defgroup gtx_rotate_vector GLM_GTX_rotate_vector +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Function to directly rotate a vector + +#pragma once + +// Dependency: +#include "../gtx/transform.hpp" +#include "../gtc/epsilon.hpp" +#include "../ext/vector_relational.hpp" +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_rotate_vector is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_rotate_vector extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_rotate_vector + /// @{ + + /// Returns Spherical interpolation between two vectors + /// + /// @param x A first vector + /// @param y A second vector + /// @param a Interpolation factor. The interpolation is defined beyond the range [0, 1]. + /// + /// @see gtx_rotate_vector + template + GLM_FUNC_DECL vec<3, T, Q> slerp( + vec<3, T, Q> const& x, + vec<3, T, Q> const& y, + T const& a); + + //! Rotate a two dimensional vector. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<2, T, Q> rotate( + vec<2, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around an axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotate( + vec<3, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal); + + //! Rotate a four dimensional vector around an axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotate( + vec<4, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal); + + //! Rotate a three dimensional vector around the X axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateX( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around the Y axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateY( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a three dimensional vector around the Z axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<3, T, Q> rotateZ( + vec<3, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the X axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateX( + vec<4, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the Y axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateY( + vec<4, T, Q> const& v, + T const& angle); + + //! Rotate a four dimensional vector around the Z axis. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL vec<4, T, Q> rotateZ( + vec<4, T, Q> const& v, + T const& angle); + + //! Build a rotation matrix from a normal and a up vector. + //! From GLM_GTX_rotate_vector extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> orientation( + vec<3, T, Q> const& Normal, + vec<3, T, Q> const& Up); + + /// @} +}//namespace glm + +#include "rotate_vector.inl" diff --git a/thirdparty/glm/glm/gtx/rotate_vector.inl b/thirdparty/glm/glm/gtx/rotate_vector.inl new file mode 100644 index 0000000..f8136e7 --- /dev/null +++ b/thirdparty/glm/glm/gtx/rotate_vector.inl @@ -0,0 +1,187 @@ +/// @ref gtx_rotate_vector + +namespace glm +{ + template + GLM_FUNC_QUALIFIER vec<3, T, Q> slerp + ( + vec<3, T, Q> const& x, + vec<3, T, Q> const& y, + T const& a + ) + { + // get cosine of angle between vectors (-1 -> 1) + T CosAlpha = dot(x, y); + // get angle (0 -> pi) + T Alpha = acos(CosAlpha); + // get sine of angle between vectors (0 -> 1) + T SinAlpha = sin(Alpha); + // this breaks down when SinAlpha = 0, i.e. Alpha = 0 or pi + T t1 = sin((static_cast(1) - a) * Alpha) / SinAlpha; + T t2 = sin(a * Alpha) / SinAlpha; + + // interpolate src vectors + return x * t1 + y * t2; + } + + template + GLM_FUNC_QUALIFIER vec<2, T, Q> rotate + ( + vec<2, T, Q> const& v, + T const& angle + ) + { + vec<2, T, Q> Result; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotate + ( + vec<3, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal + ) + { + return mat<3, 3, T, Q>(glm::rotate(angle, normal)) * v; + } + /* + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateGTX( + const vec<3, T, Q>& x, + T angle, + const vec<3, T, Q>& normal) + { + const T Cos = cos(radians(angle)); + const T Sin = sin(radians(angle)); + return x * Cos + ((x * normal) * (T(1) - Cos)) * normal + cross(x, normal) * Sin; + } + */ + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotate + ( + vec<4, T, Q> const& v, + T const& angle, + vec<3, T, Q> const& normal + ) + { + return rotate(angle, normal) * v; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateX + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result(v); + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.y = v.y * Cos - v.z * Sin; + Result.z = v.y * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateY + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos + v.z * Sin; + Result.z = -v.x * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<3, T, Q> rotateZ + ( + vec<3, T, Q> const& v, + T const& angle + ) + { + vec<3, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateX + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.y = v.y * Cos - v.z * Sin; + Result.z = v.y * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateY + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos + v.z * Sin; + Result.z = -v.x * Sin + v.z * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER vec<4, T, Q> rotateZ + ( + vec<4, T, Q> const& v, + T const& angle + ) + { + vec<4, T, Q> Result = v; + T const Cos(cos(angle)); + T const Sin(sin(angle)); + + Result.x = v.x * Cos - v.y * Sin; + Result.y = v.x * Sin + v.y * Cos; + return Result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> orientation + ( + vec<3, T, Q> const& Normal, + vec<3, T, Q> const& Up + ) + { + if(all(equal(Normal, Up, epsilon()))) + return mat<4, 4, T, Q>(static_cast(1)); + + vec<3, T, Q> RotationAxis = cross(Up, Normal); + T Angle = acos(dot(Normal, Up)); + + return rotate(Angle, RotationAxis); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/scalar_multiplication.hpp b/thirdparty/glm/glm/gtx/scalar_multiplication.hpp new file mode 100644 index 0000000..97df000 --- /dev/null +++ b/thirdparty/glm/glm/gtx/scalar_multiplication.hpp @@ -0,0 +1,80 @@ +/// @ref gtx_scalar_multiplication +/// @file glm/gtx/scalar_multiplication.hpp +/// @author Joshua Moerman +/// +/// @defgroup gtx_scalar_multiplication GLM_GTX_scalar_multiplication +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Enables scalar multiplication for all types +/// +/// Since GLSL is very strict about types, the following (often used) combinations do not work: +/// double * vec4 +/// int * vec4 +/// vec4 / int +/// So we'll fix that! Of course "float * vec4" should remain the same (hence the enable_if magic) + +#pragma once + +#include "../detail/setup.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_scalar_multiplication is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_scalar_multiplication extension included") +#endif + +#include "../vec2.hpp" +#include "../vec3.hpp" +#include "../vec4.hpp" +#include "../mat2x2.hpp" +#include + +namespace glm +{ + /// @addtogroup gtx_scalar_multiplication + /// @{ + + template + using return_type_scalar_multiplication = typename std::enable_if< + !std::is_same::value // T may not be a float + && std::is_arithmetic::value, Vec // But it may be an int or double (no vec3 or mat3, ...) + >::type; + +#define GLM_IMPLEMENT_SCAL_MULT(Vec) \ + template \ + return_type_scalar_multiplication \ + operator*(T const& s, Vec rh){ \ + return rh *= static_cast(s); \ + } \ + \ + template \ + return_type_scalar_multiplication \ + operator*(Vec lh, T const& s){ \ + return lh *= static_cast(s); \ + } \ + \ + template \ + return_type_scalar_multiplication \ + operator/(Vec lh, T const& s){ \ + return lh *= 1.0f / static_cast(s); \ + } + +GLM_IMPLEMENT_SCAL_MULT(vec2) +GLM_IMPLEMENT_SCAL_MULT(vec3) +GLM_IMPLEMENT_SCAL_MULT(vec4) + +GLM_IMPLEMENT_SCAL_MULT(mat2) +GLM_IMPLEMENT_SCAL_MULT(mat2x3) +GLM_IMPLEMENT_SCAL_MULT(mat2x4) +GLM_IMPLEMENT_SCAL_MULT(mat3x2) +GLM_IMPLEMENT_SCAL_MULT(mat3) +GLM_IMPLEMENT_SCAL_MULT(mat3x4) +GLM_IMPLEMENT_SCAL_MULT(mat4x2) +GLM_IMPLEMENT_SCAL_MULT(mat4x3) +GLM_IMPLEMENT_SCAL_MULT(mat4) + +#undef GLM_IMPLEMENT_SCAL_MULT + /// @} +} // namespace glm diff --git a/thirdparty/glm/glm/gtx/scalar_relational.hpp b/thirdparty/glm/glm/gtx/scalar_relational.hpp new file mode 100644 index 0000000..e840932 --- /dev/null +++ b/thirdparty/glm/glm/gtx/scalar_relational.hpp @@ -0,0 +1,34 @@ +/// @ref gtx_scalar_relational +/// @file glm/gtx/scalar_relational.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_scalar_relational GLM_GTX_scalar_relational +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Extend a position from a source to a position at a defined length. + +#pragma once + +// Dependency: +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_scalar_relational is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_scalar_relational extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_scalar_relational + /// @{ + + + + /// @} +}//namespace glm + +#include "scalar_relational.inl" diff --git a/thirdparty/glm/glm/gtx/scalar_relational.inl b/thirdparty/glm/glm/gtx/scalar_relational.inl new file mode 100644 index 0000000..c2a121c --- /dev/null +++ b/thirdparty/glm/glm/gtx/scalar_relational.inl @@ -0,0 +1,88 @@ +/// @ref gtx_scalar_relational + +namespace glm +{ + template + GLM_FUNC_QUALIFIER bool lessThan + ( + T const& x, + T const& y + ) + { + return x < y; + } + + template + GLM_FUNC_QUALIFIER bool lessThanEqual + ( + T const& x, + T const& y + ) + { + return x <= y; + } + + template + GLM_FUNC_QUALIFIER bool greaterThan + ( + T const& x, + T const& y + ) + { + return x > y; + } + + template + GLM_FUNC_QUALIFIER bool greaterThanEqual + ( + T const& x, + T const& y + ) + { + return x >= y; + } + + template + GLM_FUNC_QUALIFIER bool equal + ( + T const& x, + T const& y + ) + { + return detail::compute_equal::is_iec559>::call(x, y); + } + + template + GLM_FUNC_QUALIFIER bool notEqual + ( + T const& x, + T const& y + ) + { + return !detail::compute_equal::is_iec559>::call(x, y); + } + + GLM_FUNC_QUALIFIER bool any + ( + bool const& x + ) + { + return x; + } + + GLM_FUNC_QUALIFIER bool all + ( + bool const& x + ) + { + return x; + } + + GLM_FUNC_QUALIFIER bool not_ + ( + bool const& x + ) + { + return !x; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/spline.hpp b/thirdparty/glm/glm/gtx/spline.hpp new file mode 100644 index 0000000..8df5584 --- /dev/null +++ b/thirdparty/glm/glm/gtx/spline.hpp @@ -0,0 +1,63 @@ +/// @ref gtx_spline +/// @file glm/gtx/spline.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_spline GLM_GTX_spline +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Spline functions + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/optimum_pow.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_spline is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_spline extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_spline + /// @{ + + /// Return a point from a catmull rom curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType catmullRom( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s); + + /// Return a point from a hermite curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType hermite( + genType const& v1, + genType const& t1, + genType const& v2, + genType const& t2, + typename genType::value_type const& s); + + /// Return a point from a cubic curve. + /// @see gtx_spline extension. + template + GLM_FUNC_DECL genType cubic( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s); + + /// @} +}//namespace glm + +#include "spline.inl" diff --git a/thirdparty/glm/glm/gtx/spline.inl b/thirdparty/glm/glm/gtx/spline.inl new file mode 100644 index 0000000..c3fd056 --- /dev/null +++ b/thirdparty/glm/glm/gtx/spline.inl @@ -0,0 +1,60 @@ +/// @ref gtx_spline + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType catmullRom + ( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s + ) + { + typename genType::value_type s2 = pow2(s); + typename genType::value_type s3 = pow3(s); + + typename genType::value_type f1 = -s3 + typename genType::value_type(2) * s2 - s; + typename genType::value_type f2 = typename genType::value_type(3) * s3 - typename genType::value_type(5) * s2 + typename genType::value_type(2); + typename genType::value_type f3 = typename genType::value_type(-3) * s3 + typename genType::value_type(4) * s2 + s; + typename genType::value_type f4 = s3 - s2; + + return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) / typename genType::value_type(2); + + } + + template + GLM_FUNC_QUALIFIER genType hermite + ( + genType const& v1, + genType const& t1, + genType const& v2, + genType const& t2, + typename genType::value_type const& s + ) + { + typename genType::value_type s2 = pow2(s); + typename genType::value_type s3 = pow3(s); + + typename genType::value_type f1 = typename genType::value_type(2) * s3 - typename genType::value_type(3) * s2 + typename genType::value_type(1); + typename genType::value_type f2 = typename genType::value_type(-2) * s3 + typename genType::value_type(3) * s2; + typename genType::value_type f3 = s3 - typename genType::value_type(2) * s2 + s; + typename genType::value_type f4 = s3 - s2; + + return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2; + } + + template + GLM_FUNC_QUALIFIER genType cubic + ( + genType const& v1, + genType const& v2, + genType const& v3, + genType const& v4, + typename genType::value_type const& s + ) + { + return ((v1 * s + v2) * s + v3) * s + v4; + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/std_based_type.hpp b/thirdparty/glm/glm/gtx/std_based_type.hpp new file mode 100644 index 0000000..864885d --- /dev/null +++ b/thirdparty/glm/glm/gtx/std_based_type.hpp @@ -0,0 +1,66 @@ +/// @ref gtx_std_based_type +/// @file glm/gtx/std_based_type.hpp +/// +/// @see core (dependence) +/// @see gtx_extented_min_max (dependence) +/// +/// @defgroup gtx_std_based_type GLM_GTX_std_based_type +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Adds vector types based on STL value types. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_std_based_type is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_std_based_type extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_std_based_type + /// @{ + + /// Vector type based of one std::size_t component. + /// @see GLM_GTX_std_based_type + typedef vec<1, std::size_t, defaultp> size1; + + /// Vector type based of two std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<2, std::size_t, defaultp> size2; + + /// Vector type based of three std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<3, std::size_t, defaultp> size3; + + /// Vector type based of four std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<4, std::size_t, defaultp> size4; + + /// Vector type based of one std::size_t component. + /// @see GLM_GTX_std_based_type + typedef vec<1, std::size_t, defaultp> size1_t; + + /// Vector type based of two std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<2, std::size_t, defaultp> size2_t; + + /// Vector type based of three std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<3, std::size_t, defaultp> size3_t; + + /// Vector type based of four std::size_t components. + /// @see GLM_GTX_std_based_type + typedef vec<4, std::size_t, defaultp> size4_t; + + /// @} +}//namespace glm + +#include "std_based_type.inl" diff --git a/thirdparty/glm/glm/gtx/std_based_type.inl b/thirdparty/glm/glm/gtx/std_based_type.inl new file mode 100644 index 0000000..9c34bdb --- /dev/null +++ b/thirdparty/glm/glm/gtx/std_based_type.inl @@ -0,0 +1,6 @@ +/// @ref gtx_std_based_type + +namespace glm +{ + +} diff --git a/thirdparty/glm/glm/gtx/string_cast.hpp b/thirdparty/glm/glm/gtx/string_cast.hpp new file mode 100644 index 0000000..2958edc --- /dev/null +++ b/thirdparty/glm/glm/gtx/string_cast.hpp @@ -0,0 +1,45 @@ +/// @ref gtx_string_cast +/// @file glm/gtx/string_cast.hpp +/// +/// @see core (dependence) +/// @see gtx_integer (dependence) +/// @see gtx_quaternion (dependence) +/// +/// @defgroup gtx_string_cast GLM_GTX_string_cast +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Setup strings for GLM type values + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/type_precision.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" +#include +#include +#include + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_string_cast is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_string_cast extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_string_cast + /// @{ + + /// Create a string from a GLM vector or matrix typed variable. + /// @see gtx_string_cast extension. + template + GLM_FUNC_DECL std::string to_string(genType const& x); + + /// @} +}//namespace glm + +#include "string_cast.inl" diff --git a/thirdparty/glm/glm/gtx/string_cast.inl b/thirdparty/glm/glm/gtx/string_cast.inl new file mode 100644 index 0000000..875f2be --- /dev/null +++ b/thirdparty/glm/glm/gtx/string_cast.inl @@ -0,0 +1,497 @@ +/// @ref gtx_string_cast + +#include +#include + +namespace glm{ +namespace detail +{ + template + struct cast + { + typedef T value_type; + }; + + template <> + struct cast + { + typedef double value_type; + }; + + GLM_FUNC_QUALIFIER std::string format(const char* message, ...) { + std::size_t const STRING_BUFFER(4096); + + assert(message != NULL); + assert(strlen(message) < STRING_BUFFER); + + char buffer[STRING_BUFFER]; + va_list list; + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + + va_start(list, message); + vsnprintf(buffer, STRING_BUFFER, message, list); + va_end(list); + +#if GLM_COMPILER & GLM_COMPILER_CLANG +# pragma clang diagnostic pop +#endif + + return buffer; + } + + static const char* LabelTrue = "true"; + static const char* LabelFalse = "false"; + + template + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%d";} + }; + + template + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%f";} + }; + +# if GLM_MODEL == GLM_MODEL_32 && GLM_COMPILER && GLM_COMPILER_VC + template<> + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%lld";} + }; + + template<> + struct literal + { + GLM_FUNC_QUALIFIER static char const * value() {return "%lld";} + }; +# endif//GLM_MODEL == GLM_MODEL_32 && GLM_COMPILER && GLM_COMPILER_VC + + template + struct prefix{}; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "d";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "b";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u8";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i8";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u16";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i16";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "u64";} + }; + + template<> + struct prefix + { + GLM_FUNC_QUALIFIER static char const * value() {return "i64";} + }; + + template + struct compute_to_string + {}; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<1, bool, Q> const& x) + { + return detail::format("bvec1(%s)", + x[0] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<2, bool, Q> const& x) + { + return detail::format("bvec2(%s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<3, bool, Q> const& x) + { + return detail::format("bvec3(%s, %s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse, + x[2] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<4, bool, Q> const& x) + { + return detail::format("bvec4(%s, %s, %s, %s)", + x[0] ? detail::LabelTrue : detail::LabelFalse, + x[1] ? detail::LabelTrue : detail::LabelFalse, + x[2] ? detail::LabelTrue : detail::LabelFalse, + x[3] ? detail::LabelTrue : detail::LabelFalse); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<1, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec1(%s)", + PrefixStr, + LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec2(%s, %s)", + PrefixStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec3(%s, %s, %s)", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1]), + static_cast::value_type>(x[2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(vec<4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%svec4(%s, %s, %s, %s)", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0]), + static_cast::value_type>(x[1]), + static_cast::value_type>(x[2]), + static_cast::value_type>(x[3])); + } + }; + + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x2((%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x3((%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<2, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat2x4((%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x2((%s, %s), (%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x3((%s, %s, %s), (%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<3, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat3x4((%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), static_cast::value_type>(x[2][3])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 2, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x2((%s, %s), (%s, %s), (%s, %s), (%s, %s))", + PrefixStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr, + LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 3, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x3((%s, %s, %s), (%s, %s, %s), (%s, %s, %s), (%s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1]), static_cast::value_type>(x[3][2])); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(mat<4, 4, T, Q> const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%smat4x4((%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s), (%s, %s, %s, %s))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x[0][0]), static_cast::value_type>(x[0][1]), static_cast::value_type>(x[0][2]), static_cast::value_type>(x[0][3]), + static_cast::value_type>(x[1][0]), static_cast::value_type>(x[1][1]), static_cast::value_type>(x[1][2]), static_cast::value_type>(x[1][3]), + static_cast::value_type>(x[2][0]), static_cast::value_type>(x[2][1]), static_cast::value_type>(x[2][2]), static_cast::value_type>(x[2][3]), + static_cast::value_type>(x[3][0]), static_cast::value_type>(x[3][1]), static_cast::value_type>(x[3][2]), static_cast::value_type>(x[3][3])); + } + }; + + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(qua const& q) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%squat(%s, {%s, %s, %s})", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(q.w), + static_cast::value_type>(q.x), + static_cast::value_type>(q.y), + static_cast::value_type>(q.z)); + } + }; + + template + struct compute_to_string > + { + GLM_FUNC_QUALIFIER static std::string call(tdualquat const& x) + { + char const * PrefixStr = prefix::value(); + char const * LiteralStr = literal::is_iec559>::value(); + std::string FormatStr(detail::format("%sdualquat((%s, {%s, %s, %s}), (%s, {%s, %s, %s}))", + PrefixStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr, + LiteralStr, LiteralStr, LiteralStr, LiteralStr)); + + return detail::format(FormatStr.c_str(), + static_cast::value_type>(x.real.w), + static_cast::value_type>(x.real.x), + static_cast::value_type>(x.real.y), + static_cast::value_type>(x.real.z), + static_cast::value_type>(x.dual.w), + static_cast::value_type>(x.dual.x), + static_cast::value_type>(x.dual.y), + static_cast::value_type>(x.dual.z)); + } + }; + +}//namespace detail + +template +GLM_FUNC_QUALIFIER std::string to_string(matType const& x) +{ + return detail::compute_to_string::call(x); +} + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/texture.hpp b/thirdparty/glm/glm/gtx/texture.hpp new file mode 100644 index 0000000..608c6ad --- /dev/null +++ b/thirdparty/glm/glm/gtx/texture.hpp @@ -0,0 +1,44 @@ +/// @ref gtx_texture +/// @file glm/gtx/texture.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_texture GLM_GTX_texture +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Wrapping mode of texture coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/integer.hpp" +#include "../gtx/component_wise.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_texture is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_texture extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_texture + /// @{ + + /// Compute the number of mipmaps levels necessary to create a mipmap complete texture + /// + /// @param Extent Extent of the texture base level mipmap + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point or signed integer scalar types + /// @tparam Q Value from qualifier enum + template + T levels(vec const& Extent); + + /// @} +}// namespace glm + +#include "texture.inl" + diff --git a/thirdparty/glm/glm/gtx/texture.inl b/thirdparty/glm/glm/gtx/texture.inl new file mode 100644 index 0000000..593c826 --- /dev/null +++ b/thirdparty/glm/glm/gtx/texture.inl @@ -0,0 +1,17 @@ +/// @ref gtx_texture + +namespace glm +{ + template + inline T levels(vec const& Extent) + { + return glm::log2(compMax(Extent)) + static_cast(1); + } + + template + inline T levels(T Extent) + { + return vec<1, T, defaultp>(Extent).x; + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtx/transform.hpp b/thirdparty/glm/glm/gtx/transform.hpp new file mode 100644 index 0000000..9707b50 --- /dev/null +++ b/thirdparty/glm/glm/gtx/transform.hpp @@ -0,0 +1,58 @@ +/// @ref gtx_transform +/// @file glm/gtx/transform.hpp +/// +/// @see core (dependence) +/// @see gtc_matrix_transform (dependence) +/// @see gtx_transform +/// @see gtx_transform2 +/// +/// @defgroup gtx_transform GLM_GTX_transform +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add transformation matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/matrix_transform.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_transform is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_transform extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_transform + /// @{ + + /// Transforms a matrix with a translation 4 * 4 matrix created from 3 scalars. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> translate( + vec<3, T, Q> const& v); + + /// Builds a rotation 4 * 4 matrix created from an axis of 3 scalars and an angle expressed in radians. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> rotate( + T angle, + vec<3, T, Q> const& v); + + /// Transforms a matrix with a scale 4 * 4 matrix created from a vector of 3 components. + /// @see gtc_matrix_transform + /// @see gtx_transform + template + GLM_FUNC_DECL mat<4, 4, T, Q> scale( + vec<3, T, Q> const& v); + + /// @} +}// namespace glm + +#include "transform.inl" diff --git a/thirdparty/glm/glm/gtx/transform.inl b/thirdparty/glm/glm/gtx/transform.inl new file mode 100644 index 0000000..48ee680 --- /dev/null +++ b/thirdparty/glm/glm/gtx/transform.inl @@ -0,0 +1,23 @@ +/// @ref gtx_transform + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> translate(vec<3, T, Q> const& v) + { + return translate(mat<4, 4, T, Q>(static_cast(1)), v); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> rotate(T angle, vec<3, T, Q> const& v) + { + return rotate(mat<4, 4, T, Q>(static_cast(1)), angle, v); + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scale(vec<3, T, Q> const& v) + { + return scale(mat<4, 4, T, Q>(static_cast(1)), v); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/transform2.hpp b/thirdparty/glm/glm/gtx/transform2.hpp new file mode 100644 index 0000000..9604a92 --- /dev/null +++ b/thirdparty/glm/glm/gtx/transform2.hpp @@ -0,0 +1,87 @@ +/// @ref gtx_transform2 +/// @file glm/gtx/transform2.hpp +/// +/// @see core (dependence) +/// @see gtx_transform (dependence) +/// +/// @defgroup gtx_transform2 GLM_GTX_transform2 +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Add extra transformation matrices + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtx/transform.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_transform2 is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_transform2 extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_transform2 + /// @{ + + //! Transforms a matrix with a shearing on X axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> shearX2D(mat<3, 3, T, Q> const& m, T y); + + //! Transforms a matrix with a shearing on Y axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> shearY2D(mat<3, 3, T, Q> const& m, T x); + + //! Transforms a matrix with a shearing on X axis + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearX3D(mat<4, 4, T, Q> const& m, T y, T z); + + //! Transforms a matrix with a shearing on Y axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearY3D(mat<4, 4, T, Q> const& m, T x, T z); + + //! Transforms a matrix with a shearing on Z axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> shearZ3D(mat<4, 4, T, Q> const& m, T x, T y); + + //template GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shear(const mat<4, 4, T, Q> & m, shearPlane, planePoint, angle) + // Identity + tan(angle) * cross(Normal, OnPlaneVector) 0 + // - dot(PointOnPlane, normal) * OnPlaneVector 1 + + // Reflect functions seem to don't work + //template mat<3, 3, T, Q> reflect2D(const mat<3, 3, T, Q> & m, const vec<3, T, Q>& normal){return reflect2DGTX(m, normal);} //!< \brief Build a reflection matrix (from GLM_GTX_transform2 extension) + //template mat<4, 4, T, Q> reflect3D(const mat<4, 4, T, Q> & m, const vec<3, T, Q>& normal){return reflect3DGTX(m, normal);} //!< \brief Build a reflection matrix (from GLM_GTX_transform2 extension) + + //! Build planar projection matrix along normal axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<3, 3, T, Q> proj2D(mat<3, 3, T, Q> const& m, vec<3, T, Q> const& normal); + + //! Build planar projection matrix along normal axis. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> proj3D(mat<4, 4, T, Q> const & m, vec<3, T, Q> const& normal); + + //! Build a scale bias matrix. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> scaleBias(T scale, T bias); + + //! Build a scale bias matrix. + //! From GLM_GTX_transform2 extension. + template + GLM_FUNC_DECL mat<4, 4, T, Q> scaleBias(mat<4, 4, T, Q> const& m, T scale, T bias); + + /// @} +}// namespace glm + +#include "transform2.inl" diff --git a/thirdparty/glm/glm/gtx/transform2.inl b/thirdparty/glm/glm/gtx/transform2.inl new file mode 100644 index 0000000..0118ab0 --- /dev/null +++ b/thirdparty/glm/glm/gtx/transform2.inl @@ -0,0 +1,125 @@ +/// @ref gtx_transform2 + +namespace glm +{ + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearX2D(mat<3, 3, T, Q> const& m, T s) + { + mat<3, 3, T, Q> r(1); + r[1][0] = s; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> shearY2D(mat<3, 3, T, Q> const& m, T s) + { + mat<3, 3, T, Q> r(1); + r[0][1] = s; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearX3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[0][1] = s; + r[0][2] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearY3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[1][0] = s; + r[1][2] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> shearZ3D(mat<4, 4, T, Q> const& m, T s, T t) + { + mat<4, 4, T, Q> r(1); + r[2][0] = s; + r[2][1] = t; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> reflect2D(mat<3, 3, T, Q> const& m, vec<3, T, Q> const& normal) + { + mat<3, 3, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - static_cast(2) * normal.x * normal.x; + r[0][1] = -static_cast(2) * normal.x * normal.y; + r[1][0] = -static_cast(2) * normal.x * normal.y; + r[1][1] = static_cast(1) - static_cast(2) * normal.y * normal.y; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> reflect3D(mat<4, 4, T, Q> const& m, vec<3, T, Q> const& normal) + { + mat<4, 4, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - static_cast(2) * normal.x * normal.x; + r[0][1] = -static_cast(2) * normal.x * normal.y; + r[0][2] = -static_cast(2) * normal.x * normal.z; + + r[1][0] = -static_cast(2) * normal.x * normal.y; + r[1][1] = static_cast(1) - static_cast(2) * normal.y * normal.y; + r[1][2] = -static_cast(2) * normal.y * normal.z; + + r[2][0] = -static_cast(2) * normal.x * normal.z; + r[2][1] = -static_cast(2) * normal.y * normal.z; + r[2][2] = static_cast(1) - static_cast(2) * normal.z * normal.z; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<3, 3, T, Q> proj2D( + const mat<3, 3, T, Q>& m, + const vec<3, T, Q>& normal) + { + mat<3, 3, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - normal.x * normal.x; + r[0][1] = - normal.x * normal.y; + r[1][0] = - normal.x * normal.y; + r[1][1] = static_cast(1) - normal.y * normal.y; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> proj3D( + const mat<4, 4, T, Q>& m, + const vec<3, T, Q>& normal) + { + mat<4, 4, T, Q> r(static_cast(1)); + r[0][0] = static_cast(1) - normal.x * normal.x; + r[0][1] = - normal.x * normal.y; + r[0][2] = - normal.x * normal.z; + r[1][0] = - normal.x * normal.y; + r[1][1] = static_cast(1) - normal.y * normal.y; + r[1][2] = - normal.y * normal.z; + r[2][0] = - normal.x * normal.z; + r[2][1] = - normal.y * normal.z; + r[2][2] = static_cast(1) - normal.z * normal.z; + return m * r; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scaleBias(T scale, T bias) + { + mat<4, 4, T, Q> result; + result[3] = vec<4, T, Q>(vec<3, T, Q>(bias), static_cast(1)); + result[0][0] = scale; + result[1][1] = scale; + result[2][2] = scale; + return result; + } + + template + GLM_FUNC_QUALIFIER mat<4, 4, T, Q> scaleBias(mat<4, 4, T, Q> const& m, T scale, T bias) + { + return m * scaleBias(scale, bias); + } +}//namespace glm + diff --git a/thirdparty/glm/glm/gtx/type_aligned.hpp b/thirdparty/glm/glm/gtx/type_aligned.hpp new file mode 100644 index 0000000..ec40935 --- /dev/null +++ b/thirdparty/glm/glm/gtx/type_aligned.hpp @@ -0,0 +1,980 @@ +/// @ref gtx_type_aligned +/// @file glm/gtx/type_aligned.hpp +/// +/// @see core (dependence) +/// @see gtc_quaternion (dependence) +/// +/// @defgroup gtx_type_aligned GLM_GTX_type_aligned +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines aligned types. + +#pragma once + +// Dependency: +#include "../gtc/type_precision.hpp" +#include "../gtc/quaternion.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# pragma message("GLM: GLM_GTX_type_aligned is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it.") +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_type_aligned extension included") +#endif + +namespace glm +{ + /////////////////////////// + // Signed int vector types + + /// @addtogroup gtx_type_aligned + /// @{ + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int8, aligned_lowp_int8, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int16, aligned_lowp_int16, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int32, aligned_lowp_int32, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int64, aligned_lowp_int64, 8); + + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int8_t, aligned_lowp_int8_t, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int16_t, aligned_lowp_int16_t, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int32_t, aligned_lowp_int32_t, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_int64_t, aligned_lowp_int64_t, 8); + + + /// Low qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i8, aligned_lowp_i8, 1); + + /// Low qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i16, aligned_lowp_i16, 2); + + /// Low qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i32, aligned_lowp_i32, 4); + + /// Low qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_i64, aligned_lowp_i64, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int8, aligned_mediump_int8, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int16, aligned_mediump_int16, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int32, aligned_mediump_int32, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int64, aligned_mediump_int64, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int8_t, aligned_mediump_int8_t, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int16_t, aligned_mediump_int16_t, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int32_t, aligned_mediump_int32_t, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_int64_t, aligned_mediump_int64_t, 8); + + + /// Medium qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i8, aligned_mediump_i8, 1); + + /// Medium qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i16, aligned_mediump_i16, 2); + + /// Medium qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i32, aligned_mediump_i32, 4); + + /// Medium qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_i64, aligned_mediump_i64, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int8, aligned_highp_int8, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int16, aligned_highp_int16, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int32, aligned_highp_int32, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int64, aligned_highp_int64, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int8_t, aligned_highp_int8_t, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int16_t, aligned_highp_int16_t, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int32_t, aligned_highp_int32_t, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_int64_t, aligned_highp_int64_t, 8); + + + /// High qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i8, aligned_highp_i8, 1); + + /// High qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i16, aligned_highp_i16, 2); + + /// High qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i32, aligned_highp_i32, 4); + + /// High qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_i64, aligned_highp_i64, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int8, aligned_int8, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int16, aligned_int16, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int32, aligned_int32, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int64, aligned_int64, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int8_t, aligned_int8_t, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int16_t, aligned_int16_t, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int32_t, aligned_int32_t, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(int64_t, aligned_int64_t, 8); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8, aligned_i8, 1); + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16, aligned_i16, 2); + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32, aligned_i32, 4); + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64, aligned_i64, 8); + + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec1, aligned_ivec1, 4); + + /// Default qualifier 32 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec2, aligned_ivec2, 8); + + /// Default qualifier 32 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec3, aligned_ivec3, 16); + + /// Default qualifier 32 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(ivec4, aligned_ivec4, 16); + + + /// Default qualifier 8 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec1, aligned_i8vec1, 1); + + /// Default qualifier 8 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec2, aligned_i8vec2, 2); + + /// Default qualifier 8 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec3, aligned_i8vec3, 4); + + /// Default qualifier 8 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i8vec4, aligned_i8vec4, 4); + + + /// Default qualifier 16 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec1, aligned_i16vec1, 2); + + /// Default qualifier 16 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec2, aligned_i16vec2, 4); + + /// Default qualifier 16 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec3, aligned_i16vec3, 8); + + /// Default qualifier 16 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i16vec4, aligned_i16vec4, 8); + + + /// Default qualifier 32 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec1, aligned_i32vec1, 4); + + /// Default qualifier 32 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec2, aligned_i32vec2, 8); + + /// Default qualifier 32 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec3, aligned_i32vec3, 16); + + /// Default qualifier 32 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i32vec4, aligned_i32vec4, 16); + + + /// Default qualifier 64 bit signed integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec1, aligned_i64vec1, 8); + + /// Default qualifier 64 bit signed integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec2, aligned_i64vec2, 16); + + /// Default qualifier 64 bit signed integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec3, aligned_i64vec3, 32); + + /// Default qualifier 64 bit signed integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(i64vec4, aligned_i64vec4, 32); + + + ///////////////////////////// + // Unsigned int vector types + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint8, aligned_lowp_uint8, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint16, aligned_lowp_uint16, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint32, aligned_lowp_uint32, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint64, aligned_lowp_uint64, 8); + + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint8_t, aligned_lowp_uint8_t, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint16_t, aligned_lowp_uint16_t, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint32_t, aligned_lowp_uint32_t, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_uint64_t, aligned_lowp_uint64_t, 8); + + + /// Low qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u8, aligned_lowp_u8, 1); + + /// Low qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u16, aligned_lowp_u16, 2); + + /// Low qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u32, aligned_lowp_u32, 4); + + /// Low qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(lowp_u64, aligned_lowp_u64, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint8, aligned_mediump_uint8, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint16, aligned_mediump_uint16, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint32, aligned_mediump_uint32, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint64, aligned_mediump_uint64, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint8_t, aligned_mediump_uint8_t, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint16_t, aligned_mediump_uint16_t, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint32_t, aligned_mediump_uint32_t, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_uint64_t, aligned_mediump_uint64_t, 8); + + + /// Medium qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u8, aligned_mediump_u8, 1); + + /// Medium qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u16, aligned_mediump_u16, 2); + + /// Medium qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u32, aligned_mediump_u32, 4); + + /// Medium qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mediump_u64, aligned_mediump_u64, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint8, aligned_highp_uint8, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint16, aligned_highp_uint16, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint32, aligned_highp_uint32, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint64, aligned_highp_uint64, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint8_t, aligned_highp_uint8_t, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint16_t, aligned_highp_uint16_t, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint32_t, aligned_highp_uint32_t, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_uint64_t, aligned_highp_uint64_t, 8); + + + /// High qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u8, aligned_highp_u8, 1); + + /// High qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u16, aligned_highp_u16, 2); + + /// High qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u32, aligned_highp_u32, 4); + + /// High qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(highp_u64, aligned_highp_u64, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint8, aligned_uint8, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint16, aligned_uint16, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint32, aligned_uint32, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint64, aligned_uint64, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint8_t, aligned_uint8_t, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint16_t, aligned_uint16_t, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint32_t, aligned_uint32_t, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uint64_t, aligned_uint64_t, 8); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8, aligned_u8, 1); + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16, aligned_u16, 2); + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32, aligned_u32, 4); + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64, aligned_u64, 8); + + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec1, aligned_uvec1, 4); + + /// Default qualifier 32 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec2, aligned_uvec2, 8); + + /// Default qualifier 32 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec3, aligned_uvec3, 16); + + /// Default qualifier 32 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(uvec4, aligned_uvec4, 16); + + + /// Default qualifier 8 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec1, aligned_u8vec1, 1); + + /// Default qualifier 8 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec2, aligned_u8vec2, 2); + + /// Default qualifier 8 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec3, aligned_u8vec3, 4); + + /// Default qualifier 8 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u8vec4, aligned_u8vec4, 4); + + + /// Default qualifier 16 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec1, aligned_u16vec1, 2); + + /// Default qualifier 16 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec2, aligned_u16vec2, 4); + + /// Default qualifier 16 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec3, aligned_u16vec3, 8); + + /// Default qualifier 16 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u16vec4, aligned_u16vec4, 8); + + + /// Default qualifier 32 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec1, aligned_u32vec1, 4); + + /// Default qualifier 32 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec2, aligned_u32vec2, 8); + + /// Default qualifier 32 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec3, aligned_u32vec3, 16); + + /// Default qualifier 32 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u32vec4, aligned_u32vec4, 16); + + + /// Default qualifier 64 bit unsigned integer aligned scalar type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec1, aligned_u64vec1, 8); + + /// Default qualifier 64 bit unsigned integer aligned vector of 2 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec2, aligned_u64vec2, 16); + + /// Default qualifier 64 bit unsigned integer aligned vector of 3 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec3, aligned_u64vec3, 32); + + /// Default qualifier 64 bit unsigned integer aligned vector of 4 components type. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(u64vec4, aligned_u64vec4, 32); + + + ////////////////////// + // Float vector types + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32, aligned_float32, 4); + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32_t, aligned_float32_t, 4); + + /// 32 bit single-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float32, aligned_f32, 4); + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64, aligned_float64, 8); + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64_t, aligned_float64_t, 8); + + /// 64 bit double-qualifier floating-point aligned scalar. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(float64, aligned_f64, 8); + +# endif//GLM_FORCE_SINGLE_ONLY + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec1, aligned_vec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec2, aligned_vec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec3, aligned_vec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(vec4, aligned_vec4, 16); + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec1, aligned_fvec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec2, aligned_fvec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec3, aligned_fvec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fvec4, aligned_fvec4, 16); + + + /// Single-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec1, aligned_f32vec1, 4); + + /// Single-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec2, aligned_f32vec2, 8); + + /// Single-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec3, aligned_f32vec3, 16); + + /// Single-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32vec4, aligned_f32vec4, 16); + + + /// Double-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec1, aligned_dvec1, 8); + + /// Double-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec2, aligned_dvec2, 16); + + /// Double-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec3, aligned_dvec3, 32); + + /// Double-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dvec4, aligned_dvec4, 32); + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned vector of 1 component. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec1, aligned_f64vec1, 8); + + /// Double-qualifier floating-point aligned vector of 2 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec2, aligned_f64vec2, 16); + + /// Double-qualifier floating-point aligned vector of 3 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec3, aligned_f64vec3, 32); + + /// Double-qualifier floating-point aligned vector of 4 components. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64vec4, aligned_f64vec4, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + ////////////////////// + // Float matrix types + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1 mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat2, aligned_mat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat3, aligned_mat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat4, aligned_mat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat2x2, aligned_mat2x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat3x3, aligned_mat3x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(mat4x4, aligned_mat4x4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 fmat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x2, aligned_fmat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x3, aligned_fmat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x4, aligned_fmat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f32 fmat1x1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x2, aligned_fmat2x2, 16); + + /// Single-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x3, aligned_fmat2x3, 16); + + /// Single-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat2x4, aligned_fmat2x4, 16); + + /// Single-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x2, aligned_fmat3x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x3, aligned_fmat3x3, 16); + + /// Single-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat3x4, aligned_fmat3x4, 16); + + /// Single-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x2, aligned_fmat4x2, 16); + + /// Single-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x3, aligned_fmat4x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(fmat4x4, aligned_fmat4x4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 f32mat1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x2, aligned_f32mat2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x3, aligned_f32mat3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x4, aligned_f32mat4, 16); + + + /// Single-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f32 f32mat1x1; + + /// Single-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x2, aligned_f32mat2x2, 16); + + /// Single-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x3, aligned_f32mat2x3, 16); + + /// Single-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat2x4, aligned_f32mat2x4, 16); + + /// Single-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x2, aligned_f32mat3x2, 16); + + /// Single-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x3, aligned_f32mat3x3, 16); + + /// Single-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat3x4, aligned_f32mat3x4, 16); + + /// Single-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x2, aligned_f32mat4x2, 16); + + /// Single-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x3, aligned_f32mat4x3, 16); + + /// Single-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32mat4x4, aligned_f32mat4x4, 16); + + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef detail::tmat1x1 f64mat1; + + /// Double-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x2, aligned_f64mat2, 32); + + /// Double-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x3, aligned_f64mat3, 32); + + /// Double-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x4, aligned_f64mat4, 32); + + + /// Double-qualifier floating-point aligned 1x1 matrix. + /// @see gtx_type_aligned + //typedef f64 f64mat1x1; + + /// Double-qualifier floating-point aligned 2x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x2, aligned_f64mat2x2, 32); + + /// Double-qualifier floating-point aligned 2x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x3, aligned_f64mat2x3, 32); + + /// Double-qualifier floating-point aligned 2x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat2x4, aligned_f64mat2x4, 32); + + /// Double-qualifier floating-point aligned 3x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x2, aligned_f64mat3x2, 32); + + /// Double-qualifier floating-point aligned 3x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x3, aligned_f64mat3x3, 32); + + /// Double-qualifier floating-point aligned 3x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat3x4, aligned_f64mat3x4, 32); + + /// Double-qualifier floating-point aligned 4x2 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x2, aligned_f64mat4x2, 32); + + /// Double-qualifier floating-point aligned 4x3 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x3, aligned_f64mat4x3, 32); + + /// Double-qualifier floating-point aligned 4x4 matrix. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64mat4x4, aligned_f64mat4x4, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + + ////////////////////////// + // Quaternion types + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(quat, aligned_quat, 16); + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(quat, aligned_fquat, 16); + + /// Double-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(dquat, aligned_dquat, 32); + + /// Single-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f32quat, aligned_f32quat, 16); + +# ifndef GLM_FORCE_SINGLE_ONLY + + /// Double-qualifier floating-point aligned quaternion. + /// @see gtx_type_aligned + GLM_ALIGNED_TYPEDEF(f64quat, aligned_f64quat, 32); + +# endif//GLM_FORCE_SINGLE_ONLY + + /// @} +}//namespace glm + +#include "type_aligned.inl" diff --git a/thirdparty/glm/glm/gtx/type_aligned.inl b/thirdparty/glm/glm/gtx/type_aligned.inl new file mode 100644 index 0000000..54c1b81 --- /dev/null +++ b/thirdparty/glm/glm/gtx/type_aligned.inl @@ -0,0 +1,6 @@ +/// @ref gtc_type_aligned + +namespace glm +{ + +} diff --git a/thirdparty/glm/glm/gtx/type_trait.hpp b/thirdparty/glm/glm/gtx/type_trait.hpp new file mode 100644 index 0000000..17ddbad --- /dev/null +++ b/thirdparty/glm/glm/gtx/type_trait.hpp @@ -0,0 +1,83 @@ +/// @ref gtx_type_trait +/// @file glm/gtx/type_trait.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_type_trait GLM_GTX_type_trait +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Defines traits for each type. + +#pragma once + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_type_trait is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_type_trait extension included") +#endif + +// Dependency: +#include "../detail/qualifier.hpp" +#include "../gtc/quaternion.hpp" +#include "../gtx/dual_quaternion.hpp" + +namespace glm +{ + /// @addtogroup gtx_type_trait + /// @{ + + template + struct type + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = false; + static length_t const components = 0; + static length_t const cols = 0; + static length_t const rows = 0; + }; + + template + struct type > + { + static bool const is_vec = true; + static bool const is_mat = false; + static bool const is_quat = false; + static length_t const components = L; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = true; + static bool const is_quat = false; + static length_t const components = C; + static length_t const cols = C; + static length_t const rows = R; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = true; + static length_t const components = 4; + }; + + template + struct type > + { + static bool const is_vec = false; + static bool const is_mat = false; + static bool const is_quat = true; + static length_t const components = 8; + }; + + /// @} +}//namespace glm + +#include "type_trait.inl" diff --git a/thirdparty/glm/glm/gtx/type_trait.inl b/thirdparty/glm/glm/gtx/type_trait.inl new file mode 100644 index 0000000..045de95 --- /dev/null +++ b/thirdparty/glm/glm/gtx/type_trait.inl @@ -0,0 +1,61 @@ +/// @ref gtx_type_trait + +namespace glm +{ + template + bool const type::is_vec; + template + bool const type::is_mat; + template + bool const type::is_quat; + template + length_t const type::components; + template + length_t const type::cols; + template + length_t const type::rows; + + // vec + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + + // mat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + template + length_t const type >::cols; + template + length_t const type >::rows; + + // tquat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; + + // tdualquat + template + bool const type >::is_vec; + template + bool const type >::is_mat; + template + bool const type >::is_quat; + template + length_t const type >::components; +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/vec_swizzle.hpp b/thirdparty/glm/glm/gtx/vec_swizzle.hpp new file mode 100644 index 0000000..2dafa64 --- /dev/null +++ b/thirdparty/glm/glm/gtx/vec_swizzle.hpp @@ -0,0 +1,2784 @@ +/// @ref gtx_vec_swizzle +/// @file glm/gtx/vec_swizzle.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_vec_swizzle GLM_GTX_vec_swizzle +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Functions to perform swizzle operation. + +#pragma once + +#include "../glm.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_vec_swizzle is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_vec_swizzle extension included") +#endif + +namespace glm { + /// @addtogroup gtx_vec_swizzle + /// @{ + + // xx + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xx(const glm::vec<1, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xx(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.x); + } + + // xy + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xy(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.y); + } + + // xz + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.z); + } + + // xw + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> xw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.x, v.w); + } + + // yx + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yx(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.x); + } + + // yy + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yy(const glm::vec<2, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.y); + } + + // yz + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.z); + } + + // yw + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> yw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.y, v.w); + } + + // zx + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zx(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.x); + } + + // zy + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zy(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.y); + } + + // zz + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zz(const glm::vec<3, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.z); + } + + // zw + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> zw(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.z, v.w); + } + + // wx + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> wx(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.x); + } + + // wy + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> wy(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.y); + } + + // wz + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> wz(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.z); + } + + // ww + template + GLM_FUNC_QUALIFIER glm::vec<2, T, Q> ww(const glm::vec<4, T, Q> &v) { + return glm::vec<2, T, Q>(v.w, v.w); + } + + // xxx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxx(const glm::vec<1, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.x); + } + + // xxy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.y); + } + + // xxz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.z); + } + + // xxw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.x, v.w); + } + + // xyx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.x); + } + + // xyy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.y); + } + + // xyz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.z); + } + + // xyw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.y, v.w); + } + + // xzx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.x); + } + + // xzy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.y); + } + + // xzz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.z); + } + + // xzw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.z, v.w); + } + + // xwx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.x); + } + + // xwy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.y); + } + + // xwz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.z); + } + + // xww + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> xww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.x, v.w, v.w); + } + + // yxx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.x); + } + + // yxy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.y); + } + + // yxz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.z); + } + + // yxw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.x, v.w); + } + + // yyx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyx(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.x); + } + + // yyy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyy(const glm::vec<2, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.y); + } + + // yyz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.z); + } + + // yyw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.y, v.w); + } + + // yzx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.x); + } + + // yzy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.y); + } + + // yzz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.z); + } + + // yzw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.z, v.w); + } + + // ywx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> ywx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.x); + } + + // ywy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> ywy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.y); + } + + // ywz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> ywz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.z); + } + + // yww + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> yww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.y, v.w, v.w); + } + + // zxx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.x); + } + + // zxy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.y); + } + + // zxz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.z); + } + + // zxw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.x, v.w); + } + + // zyx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.x); + } + + // zyy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.y); + } + + // zyz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.z); + } + + // zyw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.y, v.w); + } + + // zzx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzx(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.x); + } + + // zzy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzy(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.y); + } + + // zzz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzz(const glm::vec<3, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.z); + } + + // zzw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.z, v.w); + } + + // zwx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.x); + } + + // zwy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.y); + } + + // zwz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.z); + } + + // zww + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> zww(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.z, v.w, v.w); + } + + // wxx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wxx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.x); + } + + // wxy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wxy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.y); + } + + // wxz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wxz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.z); + } + + // wxw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wxw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.x, v.w); + } + + // wyx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wyx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.x); + } + + // wyy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wyy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.y); + } + + // wyz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wyz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.z); + } + + // wyw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wyw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.y, v.w); + } + + // wzx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wzx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.x); + } + + // wzy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wzy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.y); + } + + // wzz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wzz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.z); + } + + // wzw + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wzw(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.z, v.w); + } + + // wwx + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wwx(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.x); + } + + // wwy + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wwy(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.y); + } + + // wwz + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> wwz(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.z); + } + + // www + template + GLM_FUNC_QUALIFIER glm::vec<3, T, Q> www(const glm::vec<4, T, Q> &v) { + return glm::vec<3, T, Q>(v.w, v.w, v.w); + } + + // xxxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxx(const glm::vec<1, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.x); + } + + // xxxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.y); + } + + // xxxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.z); + } + + // xxxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.x, v.w); + } + + // xxyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.x); + } + + // xxyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.y); + } + + // xxyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.z); + } + + // xxyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.y, v.w); + } + + // xxzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.x); + } + + // xxzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.y); + } + + // xxzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.z); + } + + // xxzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.z, v.w); + } + + // xxwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.x); + } + + // xxwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.y); + } + + // xxwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.z); + } + + // xxww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.x, v.w, v.w); + } + + // xyxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.x); + } + + // xyxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.y); + } + + // xyxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.z); + } + + // xyxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.x, v.w); + } + + // xyyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.x); + } + + // xyyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.y); + } + + // xyyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.z); + } + + // xyyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.y, v.w); + } + + // xyzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.x); + } + + // xyzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.y); + } + + // xyzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.z); + } + + // xyzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.z, v.w); + } + + // xywx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.x); + } + + // xywy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.y); + } + + // xywz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.z); + } + + // xyww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.y, v.w, v.w); + } + + // xzxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.x); + } + + // xzxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.y); + } + + // xzxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.z); + } + + // xzxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.x, v.w); + } + + // xzyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.x); + } + + // xzyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.y); + } + + // xzyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.z); + } + + // xzyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.y, v.w); + } + + // xzzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.x); + } + + // xzzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.y); + } + + // xzzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.z); + } + + // xzzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.z, v.w); + } + + // xzwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.x); + } + + // xzwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.y); + } + + // xzwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.z); + } + + // xzww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.z, v.w, v.w); + } + + // xwxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.x); + } + + // xwxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.y); + } + + // xwxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.z); + } + + // xwxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.x, v.w); + } + + // xwyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.x); + } + + // xwyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.y); + } + + // xwyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.z); + } + + // xwyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.y, v.w); + } + + // xwzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.x); + } + + // xwzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.y); + } + + // xwzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.z); + } + + // xwzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.z, v.w); + } + + // xwwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.x); + } + + // xwwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.y); + } + + // xwwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.z); + } + + // xwww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> xwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.x, v.w, v.w, v.w); + } + + // yxxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.x); + } + + // yxxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.y); + } + + // yxxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.z); + } + + // yxxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.x, v.w); + } + + // yxyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.x); + } + + // yxyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.y); + } + + // yxyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.z); + } + + // yxyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.y, v.w); + } + + // yxzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.x); + } + + // yxzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.y); + } + + // yxzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.z); + } + + // yxzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.z, v.w); + } + + // yxwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.x); + } + + // yxwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.y); + } + + // yxwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.z); + } + + // yxww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.x, v.w, v.w); + } + + // yyxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.x); + } + + // yyxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.y); + } + + // yyxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.z); + } + + // yyxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.x, v.w); + } + + // yyyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyx(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.x); + } + + // yyyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyy(const glm::vec<2, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.y); + } + + // yyyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.z); + } + + // yyyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.y, v.w); + } + + // yyzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.x); + } + + // yyzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.y); + } + + // yyzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.z); + } + + // yyzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.z, v.w); + } + + // yywx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.x); + } + + // yywy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.y); + } + + // yywz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.z); + } + + // yyww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.y, v.w, v.w); + } + + // yzxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.x); + } + + // yzxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.y); + } + + // yzxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.z); + } + + // yzxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.x, v.w); + } + + // yzyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.x); + } + + // yzyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.y); + } + + // yzyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.z); + } + + // yzyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.y, v.w); + } + + // yzzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.x); + } + + // yzzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.y); + } + + // yzzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.z); + } + + // yzzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.z, v.w); + } + + // yzwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.x); + } + + // yzwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.y); + } + + // yzwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.z); + } + + // yzww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> yzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.z, v.w, v.w); + } + + // ywxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.x); + } + + // ywxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.y); + } + + // ywxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.z); + } + + // ywxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.x, v.w); + } + + // ywyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.x); + } + + // ywyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.y); + } + + // ywyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.z); + } + + // ywyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.y, v.w); + } + + // ywzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.x); + } + + // ywzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.y); + } + + // ywzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.z); + } + + // ywzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.z, v.w); + } + + // ywwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.x); + } + + // ywwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.y); + } + + // ywwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.z); + } + + // ywww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> ywww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.y, v.w, v.w, v.w); + } + + // zxxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.x); + } + + // zxxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.y); + } + + // zxxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.z); + } + + // zxxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.x, v.w); + } + + // zxyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.x); + } + + // zxyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.y); + } + + // zxyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.z); + } + + // zxyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.y, v.w); + } + + // zxzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.x); + } + + // zxzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.y); + } + + // zxzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.z); + } + + // zxzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.z, v.w); + } + + // zxwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.x); + } + + // zxwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.y); + } + + // zxwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.z); + } + + // zxww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.x, v.w, v.w); + } + + // zyxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.x); + } + + // zyxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.y); + } + + // zyxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.z); + } + + // zyxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.x, v.w); + } + + // zyyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.x); + } + + // zyyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.y); + } + + // zyyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.z); + } + + // zyyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.y, v.w); + } + + // zyzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.x); + } + + // zyzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.y); + } + + // zyzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.z); + } + + // zyzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.z, v.w); + } + + // zywx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.x); + } + + // zywy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.y); + } + + // zywz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.z); + } + + // zyww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.y, v.w, v.w); + } + + // zzxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.x); + } + + // zzxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.y); + } + + // zzxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.z); + } + + // zzxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.x, v.w); + } + + // zzyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.x); + } + + // zzyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.y); + } + + // zzyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.z); + } + + // zzyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.y, v.w); + } + + // zzzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzx(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.x); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.x); + } + + // zzzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzy(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.y); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.y); + } + + // zzzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzz(const glm::vec<3, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.z); + } + + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.z); + } + + // zzzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.z, v.w); + } + + // zzwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.x); + } + + // zzwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.y); + } + + // zzwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.z); + } + + // zzww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.z, v.w, v.w); + } + + // zwxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.x); + } + + // zwxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.y); + } + + // zwxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.z); + } + + // zwxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.x, v.w); + } + + // zwyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.x); + } + + // zwyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.y); + } + + // zwyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.z); + } + + // zwyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.y, v.w); + } + + // zwzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.x); + } + + // zwzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.y); + } + + // zwzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.z); + } + + // zwzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.z, v.w); + } + + // zwwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.x); + } + + // zwwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.y); + } + + // zwwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.z); + } + + // zwww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> zwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.z, v.w, v.w, v.w); + } + + // wxxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.x); + } + + // wxxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.y); + } + + // wxxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.z); + } + + // wxxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.x, v.w); + } + + // wxyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.x); + } + + // wxyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.y); + } + + // wxyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.z); + } + + // wxyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.y, v.w); + } + + // wxzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.x); + } + + // wxzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.y); + } + + // wxzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.z); + } + + // wxzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.z, v.w); + } + + // wxwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.x); + } + + // wxwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.y); + } + + // wxwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.z); + } + + // wxww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wxww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.x, v.w, v.w); + } + + // wyxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.x); + } + + // wyxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.y); + } + + // wyxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.z); + } + + // wyxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.x, v.w); + } + + // wyyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.x); + } + + // wyyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.y); + } + + // wyyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.z); + } + + // wyyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.y, v.w); + } + + // wyzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.x); + } + + // wyzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.y); + } + + // wyzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.z); + } + + // wyzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.z, v.w); + } + + // wywx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wywx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.x); + } + + // wywy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wywy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.y); + } + + // wywz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wywz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.z); + } + + // wyww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wyww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.y, v.w, v.w); + } + + // wzxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.x); + } + + // wzxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.y); + } + + // wzxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.z); + } + + // wzxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.x, v.w); + } + + // wzyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.x); + } + + // wzyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.y); + } + + // wzyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.z); + } + + // wzyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.y, v.w); + } + + // wzzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.x); + } + + // wzzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.y); + } + + // wzzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.z); + } + + // wzzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.z, v.w); + } + + // wzwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.x); + } + + // wzwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.y); + } + + // wzwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.z); + } + + // wzww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wzww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.z, v.w, v.w); + } + + // wwxx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwxx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.x); + } + + // wwxy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwxy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.y); + } + + // wwxz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwxz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.z); + } + + // wwxw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwxw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.x, v.w); + } + + // wwyx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwyx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.x); + } + + // wwyy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwyy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.y); + } + + // wwyz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwyz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.z); + } + + // wwyw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwyw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.y, v.w); + } + + // wwzx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwzx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.x); + } + + // wwzy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwzy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.y); + } + + // wwzz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwzz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.z); + } + + // wwzw + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwzw(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.z, v.w); + } + + // wwwx + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwwx(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.x); + } + + // wwwy + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwwy(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.y); + } + + // wwwz + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwwz(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.z); + } + + // wwww + template + GLM_FUNC_QUALIFIER glm::vec<4, T, Q> wwww(const glm::vec<4, T, Q> &v) { + return glm::vec<4, T, Q>(v.w, v.w, v.w, v.w); + } + + /// @} +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/vector_angle.hpp b/thirdparty/glm/glm/gtx/vector_angle.hpp new file mode 100644 index 0000000..9ff4127 --- /dev/null +++ b/thirdparty/glm/glm/gtx/vector_angle.hpp @@ -0,0 +1,55 @@ +/// @ref gtx_vector_angle +/// @file glm/gtx/vector_angle.hpp +/// +/// @see core (dependence) +/// @see gtx_quaternion (dependence) +/// @see gtx_epsilon (dependence) +/// +/// @defgroup gtx_vector_angle GLM_GTX_vector_angle +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Compute angle between vectors + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../gtc/epsilon.hpp" +#include "../gtx/quaternion.hpp" +#include "../gtx/rotate_vector.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_vector_angle is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_vector_angle extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_vector_angle + /// @{ + + //! Returns the absolute angle between two vectors. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T angle(vec const& x, vec const& y); + + //! Returns the oriented angle between two 2d vectors. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T orientedAngle(vec<2, T, Q> const& x, vec<2, T, Q> const& y); + + //! Returns the oriented angle between two 3d vectors based from a reference axis. + //! Parameters need to be normalized. + /// @see gtx_vector_angle extension. + template + GLM_FUNC_DECL T orientedAngle(vec<3, T, Q> const& x, vec<3, T, Q> const& y, vec<3, T, Q> const& ref); + + /// @} +}// namespace glm + +#include "vector_angle.inl" diff --git a/thirdparty/glm/glm/gtx/vector_angle.inl b/thirdparty/glm/glm/gtx/vector_angle.inl new file mode 100644 index 0000000..11e1a21 --- /dev/null +++ b/thirdparty/glm/glm/gtx/vector_angle.inl @@ -0,0 +1,45 @@ +/// @ref gtx_vector_angle + +namespace glm +{ + template + GLM_FUNC_QUALIFIER genType angle + ( + genType const& x, + genType const& y + ) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'angle' only accept floating-point inputs"); + return acos(clamp(dot(x, y), genType(-1), genType(1))); + } + + template + GLM_FUNC_QUALIFIER T angle(vec const& x, vec const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'angle' only accept floating-point inputs"); + return acos(clamp(dot(x, y), T(-1), T(1))); + } + + template + GLM_FUNC_QUALIFIER T orientedAngle(vec<2, T, Q> const& x, vec<2, T, Q> const& y) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'orientedAngle' only accept floating-point inputs"); + T const Angle(acos(clamp(dot(x, y), T(-1), T(1)))); + + T const partialCross = x.x * y.y - y.x * x.y; + + if (partialCross > T(0)) + return Angle; + else + return -Angle; + } + + template + GLM_FUNC_QUALIFIER T orientedAngle(vec<3, T, Q> const& x, vec<3, T, Q> const& y, vec<3, T, Q> const& ref) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559 || GLM_CONFIG_UNRESTRICTED_FLOAT, "'orientedAngle' only accept floating-point inputs"); + + T const Angle(acos(clamp(dot(x, y), T(-1), T(1)))); + return mix(Angle, -Angle, dot(ref, cross(x, y)) < T(0)); + } +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/vector_query.hpp b/thirdparty/glm/glm/gtx/vector_query.hpp new file mode 100644 index 0000000..ab52df0 --- /dev/null +++ b/thirdparty/glm/glm/gtx/vector_query.hpp @@ -0,0 +1,64 @@ +/// @ref gtx_vector_query +/// @file glm/gtx/vector_query.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_vector_query GLM_GTX_vector_query +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Query information of vector types + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include +#include + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_vector_query is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_vector_query extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_vector_query + /// @{ + + //! Check whether two vectors are collinears. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areCollinear(vec const& v0, vec const& v1, T const& epsilon); + + //! Check whether two vectors are orthogonals. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areOrthogonal(vec const& v0, vec const& v1, T const& epsilon); + + //! Check whether a vector is normalized. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool isNormalized(vec const& v, T const& epsilon); + + //! Check whether a vector is null. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool isNull(vec const& v, T const& epsilon); + + //! Check whether a each component of a vector is null. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL vec isCompNull(vec const& v, T const& epsilon); + + //! Check whether two vectors are orthonormal. + /// @see gtx_vector_query extensions. + template + GLM_FUNC_DECL bool areOrthonormal(vec const& v0, vec const& v1, T const& epsilon); + + /// @} +}// namespace glm + +#include "vector_query.inl" diff --git a/thirdparty/glm/glm/gtx/vector_query.inl b/thirdparty/glm/glm/gtx/vector_query.inl new file mode 100644 index 0000000..d1a5c9b --- /dev/null +++ b/thirdparty/glm/glm/gtx/vector_query.inl @@ -0,0 +1,154 @@ +/// @ref gtx_vector_query + +#include + +namespace glm{ +namespace detail +{ + template + struct compute_areCollinear{}; + + template + struct compute_areCollinear<2, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<2, T, Q> const& v0, vec<2, T, Q> const& v1, T const& epsilon) + { + return length(cross(vec<3, T, Q>(v0, static_cast(0)), vec<3, T, Q>(v1, static_cast(0)))) < epsilon; + } + }; + + template + struct compute_areCollinear<3, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<3, T, Q> const& v0, vec<3, T, Q> const& v1, T const& epsilon) + { + return length(cross(v0, v1)) < epsilon; + } + }; + + template + struct compute_areCollinear<4, T, Q> + { + GLM_FUNC_QUALIFIER static bool call(vec<4, T, Q> const& v0, vec<4, T, Q> const& v1, T const& epsilon) + { + return length(cross(vec<3, T, Q>(v0), vec<3, T, Q>(v1))) < epsilon; + } + }; + + template + struct compute_isCompNull{}; + + template + struct compute_isCompNull<2, T, Q> + { + GLM_FUNC_QUALIFIER static vec<2, bool, Q> call(vec<2, T, Q> const& v, T const& epsilon) + { + return vec<2, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon)); + } + }; + + template + struct compute_isCompNull<3, T, Q> + { + GLM_FUNC_QUALIFIER static vec<3, bool, Q> call(vec<3, T, Q> const& v, T const& epsilon) + { + return vec<3, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon), + (abs(v.z) < epsilon)); + } + }; + + template + struct compute_isCompNull<4, T, Q> + { + GLM_FUNC_QUALIFIER static vec<4, bool, Q> call(vec<4, T, Q> const& v, T const& epsilon) + { + return vec<4, bool, Q>( + (abs(v.x) < epsilon), + (abs(v.y) < epsilon), + (abs(v.z) < epsilon), + (abs(v.w) < epsilon)); + } + }; + +}//namespace detail + + template + GLM_FUNC_QUALIFIER bool areCollinear(vec const& v0, vec const& v1, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'areCollinear' only accept floating-point inputs"); + + return detail::compute_areCollinear::call(v0, v1, epsilon); + } + + template + GLM_FUNC_QUALIFIER bool areOrthogonal(vec const& v0, vec const& v1, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'areOrthogonal' only accept floating-point inputs"); + + return abs(dot(v0, v1)) <= max( + static_cast(1), + length(v0)) * max(static_cast(1), length(v1)) * epsilon; + } + + template + GLM_FUNC_QUALIFIER bool isNormalized(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isNormalized' only accept floating-point inputs"); + + return abs(length(v) - static_cast(1)) <= static_cast(2) * epsilon; + } + + template + GLM_FUNC_QUALIFIER bool isNull(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isNull' only accept floating-point inputs"); + + return length(v) <= epsilon; + } + + template + GLM_FUNC_QUALIFIER vec isCompNull(vec const& v, T const& epsilon) + { + GLM_STATIC_ASSERT(std::numeric_limits::is_iec559, "'isCompNull' only accept floating-point inputs"); + + return detail::compute_isCompNull::call(v, epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<2, bool, Q> isCompNull(vec<2, T, Q> const& v, T const& epsilon) + { + return vec<2, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<3, bool, Q> isCompNull(vec<3, T, Q> const& v, T const& epsilon) + { + return vec<3, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon, + abs(v.z) < epsilon); + } + + template + GLM_FUNC_QUALIFIER vec<4, bool, Q> isCompNull(vec<4, T, Q> const& v, T const& epsilon) + { + return vec<4, bool, Q>( + abs(v.x) < epsilon, + abs(v.y) < epsilon, + abs(v.z) < epsilon, + abs(v.w) < epsilon); + } + + template + GLM_FUNC_QUALIFIER bool areOrthonormal(vec const& v0, vec const& v1, T const& epsilon) + { + return isNormalized(v0, epsilon) && isNormalized(v1, epsilon) && (abs(dot(v0, v1)) <= epsilon); + } + +}//namespace glm diff --git a/thirdparty/glm/glm/gtx/wrap.hpp b/thirdparty/glm/glm/gtx/wrap.hpp new file mode 100644 index 0000000..b7ac5af --- /dev/null +++ b/thirdparty/glm/glm/gtx/wrap.hpp @@ -0,0 +1,35 @@ +/// @ref gtx_wrap +/// @file glm/gtx/wrap.hpp +/// +/// @see core (dependence) +/// +/// @defgroup gtx_wrap GLM_GTX_wrap +/// @ingroup gtx +/// +/// Include to use the features of this extension. +/// +/// Wrapping mode of texture coordinates. + +#pragma once + +// Dependency: +#include "../glm.hpp" +#include "../ext/scalar_common.hpp" +#include "../ext/vector_common.hpp" +#include "../gtc/vec1.hpp" + +#ifndef GLM_ENABLE_EXPERIMENTAL +# error "GLM: GLM_GTX_wrap is an experimental extension and may change in the future. Use #define GLM_ENABLE_EXPERIMENTAL before including it, if you really want to use it." +#elif GLM_MESSAGES == GLM_ENABLE && !defined(GLM_EXT_INCLUDED) +# pragma message("GLM: GLM_GTX_wrap extension included") +#endif + +namespace glm +{ + /// @addtogroup gtx_wrap + /// @{ + + /// @} +}// namespace glm + +#include "wrap.inl" diff --git a/thirdparty/glm/glm/gtx/wrap.inl b/thirdparty/glm/glm/gtx/wrap.inl new file mode 100644 index 0000000..4be3b4c --- /dev/null +++ b/thirdparty/glm/glm/gtx/wrap.inl @@ -0,0 +1,6 @@ +/// @ref gtx_wrap + +namespace glm +{ + +}//namespace glm diff --git a/thirdparty/glm/glm/integer.hpp b/thirdparty/glm/glm/integer.hpp new file mode 100644 index 0000000..36c67be --- /dev/null +++ b/thirdparty/glm/glm/integer.hpp @@ -0,0 +1,212 @@ +/// @ref core +/// @file glm/integer.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.8 Integer Functions +/// +/// @defgroup core_func_integer Integer functions +/// @ingroup core +/// +/// Provides GLSL functions on integer types +/// +/// These all operate component-wise. The description is per component. +/// The notation [a, b] means the set of bits from bit-number a through bit-number +/// b, inclusive. The lowest-order bit is bit 0. +/// +/// Include to use these core features. + +#pragma once + +#include "detail/qualifier.hpp" +#include "common.hpp" +#include "vector_relational.hpp" + +namespace glm +{ + /// @addtogroup core_func_integer + /// @{ + + /// Adds 32-bit unsigned integer x and y, returning the sum + /// modulo pow(2, 32). The value carry is set to 0 if the sum was + /// less than pow(2, 32), or to 1 otherwise. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL uaddCarry man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec uaddCarry( + vec const& x, + vec const& y, + vec & carry); + + /// Subtracts the 32-bit unsigned integer y from x, returning + /// the difference if non-negative, or pow(2, 32) plus the difference + /// otherwise. The value borrow is set to 0 if x >= y, or to 1 otherwise. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL usubBorrow man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec usubBorrow( + vec const& x, + vec const& y, + vec & borrow); + + /// Multiplies 32-bit integers x and y, producing a 64-bit + /// result. The 32 least-significant bits are returned in lsb. + /// The 32 most-significant bits are returned in msb. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL umulExtended man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DISCARD_DECL void umulExtended( + vec const& x, + vec const& y, + vec & msb, + vec & lsb); + + /// Multiplies 32-bit integers x and y, producing a 64-bit + /// result. The 32 least-significant bits are returned in lsb. + /// The 32 most-significant bits are returned in msb. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL imulExtended man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DISCARD_DECL void imulExtended( + vec const& x, + vec const& y, + vec & msb, + vec & lsb); + + /// Extracts bits [offset, offset + bits - 1] from value, + /// returning them in the least significant bits of the result. + /// For unsigned data types, the most significant bits of the + /// result will be set to zero. For signed data types, the + /// most significant bits will be set to the value of bit offset + base - 1. + /// + /// If bits is zero, the result will be zero. The result will be + /// undefined if offset or bits is negative, or if the sum of + /// offset and bits is greater than the number of bits used + /// to store the operand. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL bitfieldExtract man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldExtract( + vec const& Value, + int Offset, + int Bits); + + /// Returns the insertion the bits least-significant bits of insert into base. + /// + /// The result will have bits [offset, offset + bits - 1] taken + /// from bits [0, bits - 1] of insert, and all other bits taken + /// directly from the corresponding bits of base. If bits is + /// zero, the result will simply be base. The result will be + /// undefined if offset or bits is negative, or if the sum of + /// offset and bits is greater than the number of bits used to + /// store the operand. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitfieldInsert man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldInsert( + vec const& Base, + vec const& Insert, + int Offset, + int Bits); + + /// Returns the reversal of the bits of value. + /// The bit numbered n of the result will be taken from bit (bits - 1) - n of value, + /// where bits is the total number of bits used to represent value. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitfieldReverse man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitfieldReverse(vec const& v); + + /// Returns the number of bits set to 1 in the binary representation of value. + /// + /// @tparam genType Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitCount man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int bitCount(genType v); + + /// Returns the number of bits set to 1 in the binary representation of value. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar or vector types. + /// + /// @see GLSL bitCount man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec bitCount(vec const& v); + + /// Returns the bit number of the least significant bit set to + /// 1 in the binary representation of value. + /// If value is zero, -1 will be returned. + /// + /// @tparam genIUType Signed or unsigned integer scalar types. + /// + /// @see GLSL findLSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int findLSB(genIUType x); + + /// Returns the bit number of the least significant bit set to + /// 1 in the binary representation of value. + /// If value is zero, -1 will be returned. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL findLSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec findLSB(vec const& v); + + /// Returns the bit number of the most significant bit in the binary representation of value. + /// For positive integers, the result will be the bit number of the most significant bit set to 1. + /// For negative integers, the result will be the bit number of the most significant + /// bit set to 0. For a value of zero or negative one, -1 will be returned. + /// + /// @tparam genIUType Signed or unsigned integer scalar types. + /// + /// @see GLSL findMSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL int findMSB(genIUType x); + + /// Returns the bit number of the most significant bit in the binary representation of value. + /// For positive integers, the result will be the bit number of the most significant bit set to 1. + /// For negative integers, the result will be the bit number of the most significant + /// bit set to 0. For a value of zero or negative one, -1 will be returned. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T Signed or unsigned integer scalar types. + /// + /// @see GLSL findMSB man page + /// @see GLSL 4.20.8 specification, section 8.8 Integer Functions + template + GLM_FUNC_DECL vec findMSB(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_integer.inl" diff --git a/thirdparty/glm/glm/mat2x2.hpp b/thirdparty/glm/glm/mat2x2.hpp new file mode 100644 index 0000000..96bec96 --- /dev/null +++ b/thirdparty/glm/glm/mat2x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x2.hpp + +#pragma once +#include "./ext/matrix_double2x2.hpp" +#include "./ext/matrix_double2x2_precision.hpp" +#include "./ext/matrix_float2x2.hpp" +#include "./ext/matrix_float2x2_precision.hpp" + diff --git a/thirdparty/glm/glm/mat2x3.hpp b/thirdparty/glm/glm/mat2x3.hpp new file mode 100644 index 0000000..d68dc25 --- /dev/null +++ b/thirdparty/glm/glm/mat2x3.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x3.hpp + +#pragma once +#include "./ext/matrix_double2x3.hpp" +#include "./ext/matrix_double2x3_precision.hpp" +#include "./ext/matrix_float2x3.hpp" +#include "./ext/matrix_float2x3_precision.hpp" + diff --git a/thirdparty/glm/glm/mat2x4.hpp b/thirdparty/glm/glm/mat2x4.hpp new file mode 100644 index 0000000..b04b738 --- /dev/null +++ b/thirdparty/glm/glm/mat2x4.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat2x4.hpp + +#pragma once +#include "./ext/matrix_double2x4.hpp" +#include "./ext/matrix_double2x4_precision.hpp" +#include "./ext/matrix_float2x4.hpp" +#include "./ext/matrix_float2x4_precision.hpp" + diff --git a/thirdparty/glm/glm/mat3x2.hpp b/thirdparty/glm/glm/mat3x2.hpp new file mode 100644 index 0000000..c853153 --- /dev/null +++ b/thirdparty/glm/glm/mat3x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat3x2.hpp + +#pragma once +#include "./ext/matrix_double3x2.hpp" +#include "./ext/matrix_double3x2_precision.hpp" +#include "./ext/matrix_float3x2.hpp" +#include "./ext/matrix_float3x2_precision.hpp" + diff --git a/thirdparty/glm/glm/mat3x3.hpp b/thirdparty/glm/glm/mat3x3.hpp new file mode 100644 index 0000000..fd4fa31 --- /dev/null +++ b/thirdparty/glm/glm/mat3x3.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat3x3.hpp + +#pragma once +#include "./ext/matrix_double3x3.hpp" +#include "./ext/matrix_double3x3_precision.hpp" +#include "./ext/matrix_float3x3.hpp" +#include "./ext/matrix_float3x3_precision.hpp" diff --git a/thirdparty/glm/glm/mat3x4.hpp b/thirdparty/glm/glm/mat3x4.hpp new file mode 100644 index 0000000..6342bf5 --- /dev/null +++ b/thirdparty/glm/glm/mat3x4.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat3x4.hpp + +#pragma once +#include "./ext/matrix_double3x4.hpp" +#include "./ext/matrix_double3x4_precision.hpp" +#include "./ext/matrix_float3x4.hpp" +#include "./ext/matrix_float3x4_precision.hpp" diff --git a/thirdparty/glm/glm/mat4x2.hpp b/thirdparty/glm/glm/mat4x2.hpp new file mode 100644 index 0000000..e013e46 --- /dev/null +++ b/thirdparty/glm/glm/mat4x2.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat4x2.hpp + +#pragma once +#include "./ext/matrix_double4x2.hpp" +#include "./ext/matrix_double4x2_precision.hpp" +#include "./ext/matrix_float4x2.hpp" +#include "./ext/matrix_float4x2_precision.hpp" + diff --git a/thirdparty/glm/glm/mat4x3.hpp b/thirdparty/glm/glm/mat4x3.hpp new file mode 100644 index 0000000..205725a --- /dev/null +++ b/thirdparty/glm/glm/mat4x3.hpp @@ -0,0 +1,8 @@ +/// @ref core +/// @file glm/mat4x3.hpp + +#pragma once +#include "./ext/matrix_double4x3.hpp" +#include "./ext/matrix_double4x3_precision.hpp" +#include "./ext/matrix_float4x3.hpp" +#include "./ext/matrix_float4x3_precision.hpp" diff --git a/thirdparty/glm/glm/mat4x4.hpp b/thirdparty/glm/glm/mat4x4.hpp new file mode 100644 index 0000000..3515f7f --- /dev/null +++ b/thirdparty/glm/glm/mat4x4.hpp @@ -0,0 +1,9 @@ +/// @ref core +/// @file glm/mat4x4.hpp + +#pragma once +#include "./ext/matrix_double4x4.hpp" +#include "./ext/matrix_double4x4_precision.hpp" +#include "./ext/matrix_float4x4.hpp" +#include "./ext/matrix_float4x4_precision.hpp" + diff --git a/thirdparty/glm/glm/matrix.hpp b/thirdparty/glm/glm/matrix.hpp new file mode 100644 index 0000000..4584c92 --- /dev/null +++ b/thirdparty/glm/glm/matrix.hpp @@ -0,0 +1,161 @@ +/// @ref core +/// @file glm/matrix.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions +/// +/// @defgroup core_func_matrix Matrix functions +/// @ingroup core +/// +/// Provides GLSL matrix functions. +/// +/// Include to use these core features. + +#pragma once + +// Dependencies +#include "detail/qualifier.hpp" +#include "detail/setup.hpp" +#include "vec2.hpp" +#include "vec3.hpp" +#include "vec4.hpp" +#include "mat2x2.hpp" +#include "mat2x3.hpp" +#include "mat2x4.hpp" +#include "mat3x2.hpp" +#include "mat3x3.hpp" +#include "mat3x4.hpp" +#include "mat4x2.hpp" +#include "mat4x3.hpp" +#include "mat4x4.hpp" + +namespace glm { +namespace detail +{ + template + struct outerProduct_trait{}; + + template + struct outerProduct_trait<2, 2, T, Q> + { + typedef mat<2, 2, T, Q> type; + }; + + template + struct outerProduct_trait<2, 3, T, Q> + { + typedef mat<3, 2, T, Q> type; + }; + + template + struct outerProduct_trait<2, 4, T, Q> + { + typedef mat<4, 2, T, Q> type; + }; + + template + struct outerProduct_trait<3, 2, T, Q> + { + typedef mat<2, 3, T, Q> type; + }; + + template + struct outerProduct_trait<3, 3, T, Q> + { + typedef mat<3, 3, T, Q> type; + }; + + template + struct outerProduct_trait<3, 4, T, Q> + { + typedef mat<4, 3, T, Q> type; + }; + + template + struct outerProduct_trait<4, 2, T, Q> + { + typedef mat<2, 4, T, Q> type; + }; + + template + struct outerProduct_trait<4, 3, T, Q> + { + typedef mat<3, 4, T, Q> type; + }; + + template + struct outerProduct_trait<4, 4, T, Q> + { + typedef mat<4, 4, T, Q> type; + }; +}//namespace detail + + /// @addtogroup core_func_matrix + /// @{ + + /// Multiply matrix x by matrix y component-wise, i.e., + /// result[i][j] is the scalar product of x[i][j] and y[i][j]. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL matrixCompMult man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL mat matrixCompMult(mat const& x, mat const& y); + + /// Treats the first parameter c as a column vector + /// and the second parameter r as a row vector + /// and does a linear algebraic matrix multiply c * r. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL outerProduct man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename detail::outerProduct_trait::type outerProduct(vec const& c, vec const& r); + + /// Returns the transposed matrix of x + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL transpose man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL typename mat::transpose_type transpose(mat const& x); + + /// Return the determinant of a squared matrix. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL determinant man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL T determinant(mat const& m); + + /// Return the inverse of a squared matrix. + /// + /// @tparam C Integer between 1 and 4 included that qualify the number a column + /// @tparam R Integer between 1 and 4 included that qualify the number a row + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL inverse man page + /// @see GLSL 4.20.8 specification, section 8.6 Matrix Functions + template + GLM_FUNC_DECL mat inverse(mat const& m); + + /// @} +}//namespace glm + +#include "detail/func_matrix.inl" diff --git a/thirdparty/glm/glm/packing.hpp b/thirdparty/glm/glm/packing.hpp new file mode 100644 index 0000000..ca83ac1 --- /dev/null +++ b/thirdparty/glm/glm/packing.hpp @@ -0,0 +1,173 @@ +/// @ref core +/// @file glm/packing.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions +/// @see gtc_packing +/// +/// @defgroup core_func_packing Floating-Point Pack and Unpack Functions +/// @ingroup core +/// +/// Provides GLSL functions to pack and unpack half, single and double-precision floating point values into more compact integer types. +/// +/// These functions do not operate component-wise, rather as described in each case. +/// +/// Include to use these core features. + +#pragma once + +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float4.hpp" + +namespace glm +{ + /// @addtogroup core_func_packing + /// @{ + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm2x16: round(clamp(c, 0, +1) * 65535.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packUnorm2x16(vec2 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm2x16: round(clamp(v, -1, +1) * 32767.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packSnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packSnorm2x16(vec2 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packUnorm4x8: round(clamp(c, 0, +1) * 255.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packUnorm4x8(vec4 const& v); + + /// First, converts each component of the normalized floating-point value v into 8- or 16-bit integer values. + /// Then, the results are packed into the returned 32-bit unsigned integer. + /// + /// The conversion for component c of v to fixed point is done as follows: + /// packSnorm4x8: round(clamp(c, -1, +1) * 127.0) + /// + /// The first component of the vector will be written to the least significant bits of the output; + /// the last component will be written to the most significant bits. + /// + /// @see GLSL packSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packSnorm4x8(vec4 const& v); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm2x16: f / 65535.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackUnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackUnorm2x16(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm2x16: clamp(f / 32767.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackSnorm2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackSnorm2x16(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackUnorm4x8: f / 255.0 + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackUnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackUnorm4x8(uint p); + + /// First, unpacks a single 32-bit unsigned integer p into a pair of 16-bit unsigned integers, four 8-bit unsigned integers, or four 8-bit signed integers. + /// Then, each component is converted to a normalized floating-point value to generate the returned two- or four-component vector. + /// + /// The conversion for unpacked fixed-point value f to floating point is done as follows: + /// unpackSnorm4x8: clamp(f / 127.0, -1, +1) + /// + /// The first component of the returned vector will be extracted from the least significant bits of the input; + /// the last component will be extracted from the most significant bits. + /// + /// @see GLSL unpackSnorm4x8 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec4 unpackSnorm4x8(uint p); + + /// Returns a double-qualifier value obtained by packing the components of v into a 64-bit value. + /// If an IEEE 754 Inf or NaN is created, it will not signal, and the resulting floating point value is unspecified. + /// Otherwise, the bit- level representation of v is preserved. + /// The first vector component specifies the 32 least significant bits; + /// the second component specifies the 32 most significant bits. + /// + /// @see GLSL packDouble2x32 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL double packDouble2x32(uvec2 const& v); + + /// Returns a two-component unsigned integer vector representation of v. + /// The bit-level representation of v is preserved. + /// The first component of the vector contains the 32 least significant bits of the double; + /// the second component consists the 32 most significant bits. + /// + /// @see GLSL unpackDouble2x32 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uvec2 unpackDouble2x32(double v); + + /// Returns an unsigned integer obtained by converting the components of a two-component floating-point vector + /// to the 16-bit floating-point representation found in the OpenGL Specification, + /// and then packing these two 16- bit integers into a 32-bit unsigned integer. + /// The first vector component specifies the 16 least-significant bits of the result; + /// the second component specifies the 16 most-significant bits. + /// + /// @see GLSL packHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL uint packHalf2x16(vec2 const& v); + + /// Returns a two-component floating-point vector with components obtained by unpacking a 32-bit unsigned integer into a pair of 16-bit values, + /// interpreting those values as 16-bit floating-point numbers according to the OpenGL Specification, + /// and converting them to 32-bit floating-point values. + /// The first component of the vector is obtained from the 16 least-significant bits of v; + /// the second component is obtained from the 16 most-significant bits of v. + /// + /// @see GLSL unpackHalf2x16 man page + /// @see GLSL 4.20.8 specification, section 8.4 Floating-Point Pack and Unpack Functions + GLM_FUNC_DECL vec2 unpackHalf2x16(uint v); + + /// @} +}//namespace glm + +#include "detail/func_packing.inl" diff --git a/thirdparty/glm/glm/simd/common.h b/thirdparty/glm/glm/simd/common.h new file mode 100644 index 0000000..a580a1f --- /dev/null +++ b/thirdparty/glm/glm/simd/common.h @@ -0,0 +1,240 @@ +/// @ref simd +/// @file glm/simd/common.h + +#pragma once + +#include "platform.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_add(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_add_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_add(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_add_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_sub(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_sub_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_sub(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_sub_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_mul(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_mul_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_mul(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_mul_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_div(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_div_ps(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_div(glm_f32vec4 a, glm_f32vec4 b) +{ + return _mm_div_ss(a, b); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_div_lowp(glm_f32vec4 a, glm_f32vec4 b) +{ + return glm_vec4_mul(a, _mm_rcp_ps(b)); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_swizzle_xyzw(glm_f32vec4 a) +{ +# if GLM_ARCH & GLM_ARCH_AVX2_BIT + return _mm_permute_ps(a, _MM_SHUFFLE(3, 2, 1, 0)); +# else + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 1, 0)); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_fma(glm_f32vec4 a, glm_f32vec4 b, glm_f32vec4 c) +{ +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && !(GLM_COMPILER & GLM_COMPILER_CLANG) + return _mm_fmadd_ss(a, b, c); +# else + return _mm_add_ss(_mm_mul_ss(a, b), c); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_fma(glm_f32vec4 a, glm_f32vec4 b, glm_f32vec4 c) +{ +# if (GLM_ARCH & GLM_ARCH_AVX2_BIT) && !(GLM_COMPILER & GLM_COMPILER_CLANG) + return _mm_fmadd_ps(a, b, c); +# else + return glm_vec4_add(glm_vec4_mul(a, b), c); +# endif +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_abs(glm_f32vec4 x) +{ + return _mm_and_ps(x, _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF))); +} + +GLM_FUNC_QUALIFIER glm_ivec4 glm_ivec4_abs(glm_ivec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSSE3_BIT + return _mm_sign_epi32(x, x); +# else + glm_ivec4 const sgn0 = _mm_srai_epi32(x, 31); + glm_ivec4 const inv0 = _mm_xor_si128(x, sgn0); + glm_ivec4 const sub0 = _mm_sub_epi32(inv0, sgn0); + return sub0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_sign(glm_vec4 x) +{ + glm_vec4 const zro0 = _mm_setzero_ps(); + glm_vec4 const cmp0 = _mm_cmplt_ps(x, zro0); + glm_vec4 const cmp1 = _mm_cmpgt_ps(x, zro0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(-1.0f)); + glm_vec4 const and1 = _mm_and_ps(cmp1, _mm_set1_ps(1.0f)); + glm_vec4 const or0 = _mm_or_ps(and0, and1); + return or0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_round(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_round_ps(x, _MM_FROUND_TO_NEAREST_INT); +# else + glm_vec4 const sgn0 = _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000))); + glm_vec4 const and0 = _mm_and_ps(sgn0, x); + glm_vec4 const or0 = _mm_or_ps(and0, _mm_set_ps1(8388608.0f)); + glm_vec4 const add0 = glm_vec4_add(x, or0); + glm_vec4 const sub0 = glm_vec4_sub(add0, or0); + return sub0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_floor(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_floor_ps(x); +# else + glm_vec4 const rnd0 = glm_vec4_round(x); + glm_vec4 const cmp0 = _mm_cmplt_ps(x, rnd0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(1.0f)); + glm_vec4 const sub0 = glm_vec4_sub(rnd0, and0); + return sub0; +# endif +} + +/* trunc TODO +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_trunc(glm_vec4 x) +{ + return glm_vec4(); +} +*/ + +//roundEven +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_roundEven(glm_vec4 x) +{ + glm_vec4 const sgn0 = _mm_castsi128_ps(_mm_set1_epi32(int(0x80000000))); + glm_vec4 const and0 = _mm_and_ps(sgn0, x); + glm_vec4 const or0 = _mm_or_ps(and0, _mm_set_ps1(8388608.0f)); + glm_vec4 const add0 = glm_vec4_add(x, or0); + glm_vec4 const sub0 = glm_vec4_sub(add0, or0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_ceil(glm_vec4 x) +{ +# if GLM_ARCH & GLM_ARCH_SSE41_BIT + return _mm_ceil_ps(x); +# else + glm_vec4 const rnd0 = glm_vec4_round(x); + glm_vec4 const cmp0 = _mm_cmpgt_ps(x, rnd0); + glm_vec4 const and0 = _mm_and_ps(cmp0, _mm_set1_ps(1.0f)); + glm_vec4 const add0 = glm_vec4_add(rnd0, and0); + return add0; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_fract(glm_vec4 x) +{ + glm_vec4 const flr0 = glm_vec4_floor(x); + glm_vec4 const sub0 = glm_vec4_sub(x, flr0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_mod(glm_vec4 x, glm_vec4 y) +{ + glm_vec4 const div0 = glm_vec4_div(x, y); + glm_vec4 const flr0 = glm_vec4_floor(div0); + glm_vec4 const mul0 = glm_vec4_mul(y, flr0); + glm_vec4 const sub0 = glm_vec4_sub(x, mul0); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_clamp(glm_vec4 v, glm_vec4 minVal, glm_vec4 maxVal) +{ + glm_vec4 const min0 = _mm_min_ps(v, maxVal); + glm_vec4 const max0 = _mm_max_ps(min0, minVal); + return max0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_mix(glm_vec4 v1, glm_vec4 v2, glm_vec4 a) +{ + glm_vec4 const sub0 = glm_vec4_sub(_mm_set1_ps(1.0f), a); + glm_vec4 const mul0 = glm_vec4_mul(v1, sub0); + glm_vec4 const mad0 = glm_vec4_fma(v2, a, mul0); + return mad0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_step(glm_vec4 edge, glm_vec4 x) +{ + glm_vec4 const cmp = _mm_cmple_ps(x, edge); + return _mm_movemask_ps(cmp) == 0 ? _mm_set1_ps(1.0f) : _mm_setzero_ps(); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_smoothstep(glm_vec4 edge0, glm_vec4 edge1, glm_vec4 x) +{ + glm_vec4 const sub0 = glm_vec4_sub(x, edge0); + glm_vec4 const sub1 = glm_vec4_sub(edge1, edge0); + glm_vec4 const div0 = glm_vec4_div(sub0, sub1); + glm_vec4 const clp0 = glm_vec4_clamp(div0, _mm_setzero_ps(), _mm_set1_ps(1.0f)); + glm_vec4 const mul0 = glm_vec4_mul(_mm_set1_ps(2.0f), clp0); + glm_vec4 const sub2 = glm_vec4_sub(_mm_set1_ps(3.0f), mul0); + glm_vec4 const mul1 = glm_vec4_mul(clp0, clp0); + glm_vec4 const mul2 = glm_vec4_mul(mul1, sub2); + return mul2; +} + +// Agner Fog method +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_nan(glm_vec4 x) +{ + glm_ivec4 const t1 = _mm_castps_si128(x); // reinterpret as 32-bit integer + glm_ivec4 const t2 = _mm_sll_epi32(t1, _mm_cvtsi32_si128(1)); // shift out sign bit + glm_ivec4 const t3 = _mm_set1_epi32(int(0xFF000000)); // exponent mask + glm_ivec4 const t4 = _mm_and_si128(t2, t3); // exponent + glm_ivec4 const t5 = _mm_andnot_si128(t3, t2); // fraction + glm_ivec4 const Equal = _mm_cmpeq_epi32(t3, t4); + glm_ivec4 const Nequal = _mm_cmpeq_epi32(t5, _mm_setzero_si128()); + glm_ivec4 const And = _mm_and_si128(Equal, Nequal); + return _mm_castsi128_ps(And); // exponent = all 1s and fraction != 0 +} + +// Agner Fog method +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_inf(glm_vec4 x) +{ + glm_ivec4 const t1 = _mm_castps_si128(x); // reinterpret as 32-bit integer + glm_ivec4 const t2 = _mm_sll_epi32(t1, _mm_cvtsi32_si128(1)); // shift out sign bit + return _mm_castsi128_ps(_mm_cmpeq_epi32(t2, _mm_set1_epi32(int(0xFF000000)))); // exponent is all 1s, fraction is 0 +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/exponential.h b/thirdparty/glm/glm/simd/exponential.h new file mode 100644 index 0000000..bc351d0 --- /dev/null +++ b/thirdparty/glm/glm/simd/exponential.h @@ -0,0 +1,20 @@ +/// @ref simd +/// @file glm/simd/experimental.h + +#pragma once + +#include "platform.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec1_sqrt_lowp(glm_f32vec4 x) +{ + return _mm_mul_ss(_mm_rsqrt_ss(x), x); +} + +GLM_FUNC_QUALIFIER glm_f32vec4 glm_vec4_sqrt_lowp(glm_f32vec4 x) +{ + return _mm_mul_ps(_mm_rsqrt_ps(x), x); +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/geometric.h b/thirdparty/glm/glm/simd/geometric.h new file mode 100644 index 0000000..afbe590 --- /dev/null +++ b/thirdparty/glm/glm/simd/geometric.h @@ -0,0 +1,130 @@ +/// @ref simd +/// @file glm/simd/geometric.h + +#pragma once + +#include "common.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_DECL glm_vec4 glm_vec4_dot(glm_vec4 v1, glm_vec4 v2); +GLM_FUNC_DECL glm_vec4 glm_vec1_dot(glm_vec4 v1, glm_vec4 v2); + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_length(glm_vec4 x) +{ + glm_vec4 const dot0 = glm_vec4_dot(x, x); + glm_vec4 const sqt0 = _mm_sqrt_ps(dot0); + return sqt0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_distance(glm_vec4 p0, glm_vec4 p1) +{ + glm_vec4 const sub0 = _mm_sub_ps(p0, p1); + glm_vec4 const len0 = glm_vec4_length(sub0); + return len0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_dot(glm_vec4 v1, glm_vec4 v2) +{ +# if GLM_ARCH & GLM_ARCH_AVX_BIT + return _mm_dp_ps(v1, v2, 0xff); +# elif GLM_ARCH & GLM_ARCH_SSE3_BIT + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const hadd0 = _mm_hadd_ps(mul0, mul0); + glm_vec4 const hadd1 = _mm_hadd_ps(hadd0, hadd0); + return hadd1; +# else + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const swp0 = _mm_shuffle_ps(mul0, mul0, _MM_SHUFFLE(2, 3, 0, 1)); + glm_vec4 const add0 = _mm_add_ps(mul0, swp0); + glm_vec4 const swp1 = _mm_shuffle_ps(add0, add0, _MM_SHUFFLE(0, 1, 2, 3)); + glm_vec4 const add1 = _mm_add_ps(add0, swp1); + return add1; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec1_dot(glm_vec4 v1, glm_vec4 v2) +{ +# if GLM_ARCH & GLM_ARCH_AVX_BIT + return _mm_dp_ps(v1, v2, 0xff); +# elif GLM_ARCH & GLM_ARCH_SSE3_BIT + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const had0 = _mm_hadd_ps(mul0, mul0); + glm_vec4 const had1 = _mm_hadd_ps(had0, had0); + return had1; +# else + glm_vec4 const mul0 = _mm_mul_ps(v1, v2); + glm_vec4 const mov0 = _mm_movehl_ps(mul0, mul0); + glm_vec4 const add0 = _mm_add_ps(mov0, mul0); + glm_vec4 const swp1 = _mm_shuffle_ps(add0, add0, 1); + glm_vec4 const add1 = _mm_add_ss(add0, swp1); + return add1; +# endif +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_cross(glm_vec4 v1, glm_vec4 v2) +{ + glm_vec4 const swp0 = _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3, 0, 2, 1)); + glm_vec4 const swp1 = _mm_shuffle_ps(v1, v1, _MM_SHUFFLE(3, 1, 0, 2)); + glm_vec4 const swp2 = _mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3, 0, 2, 1)); + glm_vec4 const swp3 = _mm_shuffle_ps(v2, v2, _MM_SHUFFLE(3, 1, 0, 2)); + glm_vec4 const mul0 = _mm_mul_ps(swp0, swp3); + glm_vec4 const mul1 = _mm_mul_ps(swp1, swp2); + glm_vec4 const sub0 = _mm_sub_ps(mul0, mul1); + return sub0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_normalize(glm_vec4 v) +{ + glm_vec4 const dot0 = glm_vec4_dot(v, v); + glm_vec4 const isr0 = _mm_rsqrt_ps(dot0); + glm_vec4 const mul0 = _mm_mul_ps(v, isr0); + return mul0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_faceforward(glm_vec4 N, glm_vec4 I, glm_vec4 Nref) +{ + glm_vec4 const dot0 = glm_vec4_dot(Nref, I); + glm_vec4 const sgn0 = glm_vec4_sign(dot0); + glm_vec4 const mul0 = _mm_mul_ps(sgn0, _mm_set1_ps(-1.0f)); + glm_vec4 const mul1 = _mm_mul_ps(N, mul0); + return mul1; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_vec4_reflect(glm_vec4 I, glm_vec4 N) +{ + glm_vec4 const dot0 = glm_vec4_dot(N, I); + glm_vec4 const mul0 = _mm_mul_ps(N, dot0); + glm_vec4 const mul1 = _mm_mul_ps(mul0, _mm_set1_ps(2.0f)); + glm_vec4 const sub0 = _mm_sub_ps(I, mul1); + return sub0; +} + +GLM_FUNC_QUALIFIER __m128 glm_vec4_refract(glm_vec4 I, glm_vec4 N, glm_vec4 eta) +{ + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)); + // if (k < 0.0) + // R = genType(0.0); // or genDType(0.0) + // else + // R = eta * I - (eta * dot(N, I) + sqrt(k)) * N; + + glm_vec4 const dot0 = glm_vec4_dot(N, I); // dot(N, I) + glm_vec4 const mul0 = _mm_mul_ps(eta, eta); // eta * eta + glm_vec4 const mul1 = _mm_mul_ps(dot0, dot0); // dot(N, I) * dot(N, I) + glm_vec4 const sub1 = _mm_sub_ps(_mm_set1_ps(1.0f), mul1); // (1.0 - dot(N, I) * dot(N, I)) + glm_vec4 const mul2 = _mm_mul_ps(mul0, sub1); // eta * eta * (1.0 - dot(N, I) * dot(N, I)) + glm_vec4 const sub0 = _mm_sub_ps(_mm_set1_ps(1.0f), mul2); // 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + + if(_mm_movemask_ps(_mm_cmplt_ss(sub0, _mm_set1_ps(0.0f))) == 0) + return _mm_set1_ps(0.0f); + + glm_vec4 const sqt0 = _mm_sqrt_ps(sub0); + glm_vec4 const mad0 = glm_vec4_fma(eta, dot0, sqt0); + glm_vec4 const mul4 = _mm_mul_ps(mad0, N); + glm_vec4 const mul5 = _mm_mul_ps(eta, I); + glm_vec4 const sub2 = _mm_sub_ps(mul5, mul4); + + return sub2; +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/integer.h b/thirdparty/glm/glm/simd/integer.h new file mode 100644 index 0000000..9381418 --- /dev/null +++ b/thirdparty/glm/glm/simd/integer.h @@ -0,0 +1,115 @@ +/// @ref simd +/// @file glm/simd/integer.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER glm_uvec4 glm_i128_interleave(glm_uvec4 x) +{ + glm_uvec4 const Mask4 = _mm_set1_epi32(0x0000FFFF); + glm_uvec4 const Mask3 = _mm_set1_epi32(0x00FF00FF); + glm_uvec4 const Mask2 = _mm_set1_epi32(0x0F0F0F0F); + glm_uvec4 const Mask1 = _mm_set1_epi32(0x33333333); + glm_uvec4 const Mask0 = _mm_set1_epi32(0x55555555); + + glm_uvec4 Reg1; + glm_uvec4 Reg2; + + // REG1 = x; + // REG2 = y; + //Reg1 = _mm_unpacklo_epi64(x, y); + Reg1 = x; + + //REG1 = ((REG1 << 16) | REG1) & glm::uint64(0x0000FFFF0000FFFF); + //REG2 = ((REG2 << 16) | REG2) & glm::uint64(0x0000FFFF0000FFFF); + Reg2 = _mm_slli_si128(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask4); + + //REG1 = ((REG1 << 8) | REG1) & glm::uint64(0x00FF00FF00FF00FF); + //REG2 = ((REG2 << 8) | REG2) & glm::uint64(0x00FF00FF00FF00FF); + Reg2 = _mm_slli_si128(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask3); + + //REG1 = ((REG1 << 4) | REG1) & glm::uint64(0x0F0F0F0F0F0F0F0F); + //REG2 = ((REG2 << 4) | REG2) & glm::uint64(0x0F0F0F0F0F0F0F0F); + Reg2 = _mm_slli_epi32(Reg1, 4); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask2); + + //REG1 = ((REG1 << 2) | REG1) & glm::uint64(0x3333333333333333); + //REG2 = ((REG2 << 2) | REG2) & glm::uint64(0x3333333333333333); + Reg2 = _mm_slli_epi32(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask1); + + //REG1 = ((REG1 << 1) | REG1) & glm::uint64(0x5555555555555555); + //REG2 = ((REG2 << 1) | REG2) & glm::uint64(0x5555555555555555); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask0); + + //return REG1 | (REG2 << 1); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg2 = _mm_srli_si128(Reg2, 8); + Reg1 = _mm_or_si128(Reg1, Reg2); + + return Reg1; +} + +GLM_FUNC_QUALIFIER glm_uvec4 glm_i128_interleave2(glm_uvec4 x, glm_uvec4 y) +{ + glm_uvec4 const Mask4 = _mm_set1_epi32(0x0000FFFF); + glm_uvec4 const Mask3 = _mm_set1_epi32(0x00FF00FF); + glm_uvec4 const Mask2 = _mm_set1_epi32(0x0F0F0F0F); + glm_uvec4 const Mask1 = _mm_set1_epi32(0x33333333); + glm_uvec4 const Mask0 = _mm_set1_epi32(0x55555555); + + glm_uvec4 Reg1; + glm_uvec4 Reg2; + + // REG1 = x; + // REG2 = y; + Reg1 = _mm_unpacklo_epi64(x, y); + + //REG1 = ((REG1 << 16) | REG1) & glm::uint64(0x0000FFFF0000FFFF); + //REG2 = ((REG2 << 16) | REG2) & glm::uint64(0x0000FFFF0000FFFF); + Reg2 = _mm_slli_si128(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask4); + + //REG1 = ((REG1 << 8) | REG1) & glm::uint64(0x00FF00FF00FF00FF); + //REG2 = ((REG2 << 8) | REG2) & glm::uint64(0x00FF00FF00FF00FF); + Reg2 = _mm_slli_si128(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask3); + + //REG1 = ((REG1 << 4) | REG1) & glm::uint64(0x0F0F0F0F0F0F0F0F); + //REG2 = ((REG2 << 4) | REG2) & glm::uint64(0x0F0F0F0F0F0F0F0F); + Reg2 = _mm_slli_epi32(Reg1, 4); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask2); + + //REG1 = ((REG1 << 2) | REG1) & glm::uint64(0x3333333333333333); + //REG2 = ((REG2 << 2) | REG2) & glm::uint64(0x3333333333333333); + Reg2 = _mm_slli_epi32(Reg1, 2); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask1); + + //REG1 = ((REG1 << 1) | REG1) & glm::uint64(0x5555555555555555); + //REG2 = ((REG2 << 1) | REG2) & glm::uint64(0x5555555555555555); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg1 = _mm_or_si128(Reg2, Reg1); + Reg1 = _mm_and_si128(Reg1, Mask0); + + //return REG1 | (REG2 << 1); + Reg2 = _mm_slli_epi32(Reg1, 1); + Reg2 = _mm_srli_si128(Reg2, 8); + Reg1 = _mm_or_si128(Reg1, Reg2); + + return Reg1; +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/matrix.h b/thirdparty/glm/glm/simd/matrix.h new file mode 100644 index 0000000..b6c42ea --- /dev/null +++ b/thirdparty/glm/glm/simd/matrix.h @@ -0,0 +1,1028 @@ +/// @ref simd +/// @file glm/simd/matrix.h + +#pragma once + +#include "geometric.h" + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +GLM_FUNC_QUALIFIER void glm_mat4_matrixCompMult(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_mul_ps(in1[0], in2[0]); + out[1] = _mm_mul_ps(in1[1], in2[1]); + out[2] = _mm_mul_ps(in1[2], in2[2]); + out[3] = _mm_mul_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER void glm_mat4_add(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_add_ps(in1[0], in2[0]); + out[1] = _mm_add_ps(in1[1], in2[1]); + out[2] = _mm_add_ps(in1[2], in2[2]); + out[3] = _mm_add_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER void glm_mat4_sub(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + out[0] = _mm_sub_ps(in1[0], in2[0]); + out[1] = _mm_sub_ps(in1[1], in2[1]); + out[2] = _mm_sub_ps(in1[2], in2[2]); + out[3] = _mm_sub_ps(in1[3], in2[3]); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_mul_vec4(glm_vec4 const m[4], glm_vec4 v) +{ + __m128 v0 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 v1 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 v2 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 v3 = _mm_shuffle_ps(v, v, _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(m[0], v0); + __m128 m1 = _mm_mul_ps(m[1], v1); + __m128 m2 = _mm_mul_ps(m[2], v2); + __m128 m3 = _mm_mul_ps(m[3], v3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + return a2; +} + +GLM_FUNC_QUALIFIER __m128 glm_vec4_mul_mat4(glm_vec4 v, glm_vec4 const m[4]) +{ + __m128 i0 = m[0]; + __m128 i1 = m[1]; + __m128 i2 = m[2]; + __m128 i3 = m[3]; + + __m128 m0 = _mm_mul_ps(v, i0); + __m128 m1 = _mm_mul_ps(v, i1); + __m128 m2 = _mm_mul_ps(v, i2); + __m128 m3 = _mm_mul_ps(v, i3); + + __m128 u0 = _mm_unpacklo_ps(m0, m1); + __m128 u1 = _mm_unpackhi_ps(m0, m1); + __m128 a0 = _mm_add_ps(u0, u1); + + __m128 u2 = _mm_unpacklo_ps(m2, m3); + __m128 u3 = _mm_unpackhi_ps(m2, m3); + __m128 a1 = _mm_add_ps(u2, u3); + + __m128 f0 = _mm_movelh_ps(a0, a1); + __m128 f1 = _mm_movehl_ps(a1, a0); + __m128 f2 = _mm_add_ps(f0, f1); + + return f2; +} + +GLM_FUNC_QUALIFIER void glm_mat4_mul(glm_vec4 const in1[4], glm_vec4 const in2[4], glm_vec4 out[4]) +{ + { + __m128 e0 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[0], in2[0], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[0] = a2; + } + + { + __m128 e0 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[1], in2[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[1] = a2; + } + + { + __m128 e0 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[2], in2[2], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[2] = a2; + } + + { + //(__m128&)_mm_shuffle_epi32(__m128i&)in2[0], _MM_SHUFFLE(3, 3, 3, 3)) + __m128 e0 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 e1 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 e2 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 e3 = _mm_shuffle_ps(in2[3], in2[3], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 m0 = _mm_mul_ps(in1[0], e0); + __m128 m1 = _mm_mul_ps(in1[1], e1); + __m128 m2 = _mm_mul_ps(in1[2], e2); + __m128 m3 = _mm_mul_ps(in1[3], e3); + + __m128 a0 = _mm_add_ps(m0, m1); + __m128 a1 = _mm_add_ps(m2, m3); + __m128 a2 = _mm_add_ps(a0, a1); + + out[3] = a2; + } +} + +GLM_FUNC_QUALIFIER void glm_mat4_transpose(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 tmp0 = _mm_shuffle_ps(in[0], in[1], 0x44); + __m128 tmp2 = _mm_shuffle_ps(in[0], in[1], 0xEE); + __m128 tmp1 = _mm_shuffle_ps(in[2], in[3], 0x44); + __m128 tmp3 = _mm_shuffle_ps(in[2], in[3], 0xEE); + + out[0] = _mm_shuffle_ps(tmp0, tmp1, 0x88); + out[1] = _mm_shuffle_ps(tmp0, tmp1, 0xDD); + out[2] = _mm_shuffle_ps(tmp2, tmp3, 0x88); + out[3] = _mm_shuffle_ps(tmp2, tmp3, 0xDD); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant_highp(glm_vec4 const in[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + return Det0; +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant_lowp(glm_vec4 const m[4]) +{ + // _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128( + + //T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + //T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + //T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + //T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + //T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + //T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + // First 2 columns + __m128 Swp2A = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(0, 1, 1, 2))); + __m128 Swp3A = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(3, 2, 3, 3))); + __m128 MulA = _mm_mul_ps(Swp2A, Swp3A); + + // Second 2 columns + __m128 Swp2B = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(3, 2, 3, 3))); + __m128 Swp3B = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(0, 1, 1, 2))); + __m128 MulB = _mm_mul_ps(Swp2B, Swp3B); + + // Columns subtraction + __m128 SubE = _mm_sub_ps(MulA, MulB); + + // Last 2 rows + __m128 Swp2C = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[2]), _MM_SHUFFLE(0, 0, 1, 2))); + __m128 Swp3C = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[3]), _MM_SHUFFLE(1, 2, 0, 0))); + __m128 MulC = _mm_mul_ps(Swp2C, Swp3C); + __m128 SubF = _mm_sub_ps(_mm_movehl_ps(MulC, MulC), MulC); + + //vec<4, T, Q> DetCof( + // + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + // - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + // + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + // - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + __m128 SubFacA = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubE), _MM_SHUFFLE(2, 1, 0, 0))); + __m128 SwpFacA = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(0, 0, 0, 1))); + __m128 MulFacA = _mm_mul_ps(SwpFacA, SubFacA); + + __m128 SubTmpB = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(0, 0, 3, 1)); + __m128 SubFacB = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubTmpB), _MM_SHUFFLE(3, 1, 1, 0)));//SubF[0], SubE[3], SubE[3], SubE[1]; + __m128 SwpFacB = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(1, 1, 2, 2))); + __m128 MulFacB = _mm_mul_ps(SwpFacB, SubFacB); + + __m128 SubRes = _mm_sub_ps(MulFacA, MulFacB); + + __m128 SubTmpC = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(1, 0, 2, 2)); + __m128 SubFacC = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(SubTmpC), _MM_SHUFFLE(3, 3, 2, 0))); + __m128 SwpFacC = _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(m[1]), _MM_SHUFFLE(2, 3, 3, 3))); + __m128 MulFacC = _mm_mul_ps(SwpFacC, SubFacC); + + __m128 AddRes = _mm_add_ps(SubRes, MulFacC); + __m128 DetCof = _mm_mul_ps(AddRes, _mm_setr_ps( 1.0f,-1.0f, 1.0f,-1.0f)); + + //return m[0][0] * DetCof[0] + // + m[0][1] * DetCof[1] + // + m[0][2] * DetCof[2] + // + m[0][3] * DetCof[3]; + + return glm_vec4_dot(m[0], DetCof); +} + +GLM_FUNC_QUALIFIER glm_vec4 glm_mat4_determinant(glm_vec4 const m[4]) +{ + // _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(add) + + //T SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + //T SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + //T SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + //T SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + //T SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + //T SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + + // First 2 columns + __m128 Swp2A = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(0, 1, 1, 2)); + __m128 Swp3A = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(3, 2, 3, 3)); + __m128 MulA = _mm_mul_ps(Swp2A, Swp3A); + + // Second 2 columns + __m128 Swp2B = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(3, 2, 3, 3)); + __m128 Swp3B = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(0, 1, 1, 2)); + __m128 MulB = _mm_mul_ps(Swp2B, Swp3B); + + // Columns subtraction + __m128 SubE = _mm_sub_ps(MulA, MulB); + + // Last 2 rows + __m128 Swp2C = _mm_shuffle_ps(m[2], m[2], _MM_SHUFFLE(0, 0, 1, 2)); + __m128 Swp3C = _mm_shuffle_ps(m[3], m[3], _MM_SHUFFLE(1, 2, 0, 0)); + __m128 MulC = _mm_mul_ps(Swp2C, Swp3C); + __m128 SubF = _mm_sub_ps(_mm_movehl_ps(MulC, MulC), MulC); + + //vec<4, T, Q> DetCof( + // + (m[1][1] * SubFactor00 - m[1][2] * SubFactor01 + m[1][3] * SubFactor02), + // - (m[1][0] * SubFactor00 - m[1][2] * SubFactor03 + m[1][3] * SubFactor04), + // + (m[1][0] * SubFactor01 - m[1][1] * SubFactor03 + m[1][3] * SubFactor05), + // - (m[1][0] * SubFactor02 - m[1][1] * SubFactor04 + m[1][2] * SubFactor05)); + + __m128 SubFacA = _mm_shuffle_ps(SubE, SubE, _MM_SHUFFLE(2, 1, 0, 0)); + __m128 SwpFacA = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(0, 0, 0, 1)); + __m128 MulFacA = _mm_mul_ps(SwpFacA, SubFacA); + + __m128 SubTmpB = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(0, 0, 3, 1)); + __m128 SubFacB = _mm_shuffle_ps(SubTmpB, SubTmpB, _MM_SHUFFLE(3, 1, 1, 0));//SubF[0], SubE[3], SubE[3], SubE[1]; + __m128 SwpFacB = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(1, 1, 2, 2)); + __m128 MulFacB = _mm_mul_ps(SwpFacB, SubFacB); + + __m128 SubRes = _mm_sub_ps(MulFacA, MulFacB); + + __m128 SubTmpC = _mm_shuffle_ps(SubE, SubF, _MM_SHUFFLE(1, 0, 2, 2)); + __m128 SubFacC = _mm_shuffle_ps(SubTmpC, SubTmpC, _MM_SHUFFLE(3, 3, 2, 0)); + __m128 SwpFacC = _mm_shuffle_ps(m[1], m[1], _MM_SHUFFLE(2, 3, 3, 3)); + __m128 MulFacC = _mm_mul_ps(SwpFacC, SubFacC); + + __m128 AddRes = _mm_add_ps(SubRes, MulFacC); + __m128 DetCof = _mm_mul_ps(AddRes, _mm_setr_ps( 1.0f,-1.0f, 1.0f,-1.0f)); + + //return m[0][0] * DetCof[0] + // + m[0][1] * DetCof[1] + // + m[0][2] * DetCof[2] + // + m[0][3] * DetCof[3]; + + return glm_vec4_dot(m[0], DetCof); +} + +GLM_FUNC_QUALIFIER void glm_mat4_inverse(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + __m128 Rcp0 = _mm_div_ps(_mm_set1_ps(1.0f), Det0); + //__m128 Rcp0 = _mm_rcp_ps(Det0); + + // Inverse /= Determinant; + out[0] = _mm_mul_ps(Inv0, Rcp0); + out[1] = _mm_mul_ps(Inv1, Rcp0); + out[2] = _mm_mul_ps(Inv2, Rcp0); + out[3] = _mm_mul_ps(Inv3, Rcp0); +} + +GLM_FUNC_QUALIFIER void glm_mat4_inverse_lowp(glm_vec4 const in[4], glm_vec4 out[4]) +{ + __m128 Fac0; + { + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + // valType SubFactor06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + // valType SubFactor13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac0 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac1; + { + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + // valType SubFactor07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + // valType SubFactor14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac1 = _mm_sub_ps(Mul00, Mul01); + } + + + __m128 Fac2; + { + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + // valType SubFactor08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + // valType SubFactor15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac2 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac3; + { + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + // valType SubFactor09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + // valType SubFactor16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(3, 3, 3, 3)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac3 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac4; + { + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + // valType SubFactor10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + // valType SubFactor17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(2, 2, 2, 2)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac4 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 Fac5; + { + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + // valType SubFactor12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + // valType SubFactor18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + __m128 Swp0a = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Swp0b = _mm_shuffle_ps(in[3], in[2], _MM_SHUFFLE(0, 0, 0, 0)); + + __m128 Swp00 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); + __m128 Swp03 = _mm_shuffle_ps(in[2], in[1], _MM_SHUFFLE(1, 1, 1, 1)); + + __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); + __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); + Fac5 = _mm_sub_ps(Mul00, Mul01); + } + + __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); + __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); + + // m[1][0] + // m[0][0] + // m[0][0] + // m[0][0] + __m128 Temp0 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][1] + // m[0][1] + // m[0][1] + // m[0][1] + __m128 Temp1 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(1, 1, 1, 1)); + __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][2] + // m[0][2] + // m[0][2] + // m[0][2] + __m128 Temp2 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(2, 2, 2, 2)); + __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); + + // m[1][3] + // m[0][3] + // m[0][3] + // m[0][3] + __m128 Temp3 = _mm_shuffle_ps(in[1], in[0], _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); + + // col0 + // + (Vec1[0] * Fac0[0] - Vec2[0] * Fac1[0] + Vec3[0] * Fac2[0]), + // - (Vec1[1] * Fac0[1] - Vec2[1] * Fac1[1] + Vec3[1] * Fac2[1]), + // + (Vec1[2] * Fac0[2] - Vec2[2] * Fac1[2] + Vec3[2] * Fac2[2]), + // - (Vec1[3] * Fac0[3] - Vec2[3] * Fac1[3] + Vec3[3] * Fac2[3]), + __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); + __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); + __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); + __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); + __m128 Add00 = _mm_add_ps(Sub00, Mul02); + __m128 Inv0 = _mm_mul_ps(SignB, Add00); + + // col1 + // - (Vec0[0] * Fac0[0] - Vec2[0] * Fac3[0] + Vec3[0] * Fac4[0]), + // + (Vec0[0] * Fac0[1] - Vec2[1] * Fac3[1] + Vec3[1] * Fac4[1]), + // - (Vec0[0] * Fac0[2] - Vec2[2] * Fac3[2] + Vec3[2] * Fac4[2]), + // + (Vec0[0] * Fac0[3] - Vec2[3] * Fac3[3] + Vec3[3] * Fac4[3]), + __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); + __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); + __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); + __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); + __m128 Add01 = _mm_add_ps(Sub01, Mul05); + __m128 Inv1 = _mm_mul_ps(SignA, Add01); + + // col2 + // + (Vec0[0] * Fac1[0] - Vec1[0] * Fac3[0] + Vec3[0] * Fac5[0]), + // - (Vec0[0] * Fac1[1] - Vec1[1] * Fac3[1] + Vec3[1] * Fac5[1]), + // + (Vec0[0] * Fac1[2] - Vec1[2] * Fac3[2] + Vec3[2] * Fac5[2]), + // - (Vec0[0] * Fac1[3] - Vec1[3] * Fac3[3] + Vec3[3] * Fac5[3]), + __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); + __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); + __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); + __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); + __m128 Add02 = _mm_add_ps(Sub02, Mul08); + __m128 Inv2 = _mm_mul_ps(SignB, Add02); + + // col3 + // - (Vec1[0] * Fac2[0] - Vec1[0] * Fac4[0] + Vec2[0] * Fac5[0]), + // + (Vec1[0] * Fac2[1] - Vec1[1] * Fac4[1] + Vec2[1] * Fac5[1]), + // - (Vec1[0] * Fac2[2] - Vec1[2] * Fac4[2] + Vec2[2] * Fac5[2]), + // + (Vec1[0] * Fac2[3] - Vec1[3] * Fac4[3] + Vec2[3] * Fac5[3])); + __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); + __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); + __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); + __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); + __m128 Add03 = _mm_add_ps(Sub03, Mul11); + __m128 Inv3 = _mm_mul_ps(SignA, Add03); + + __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); + + // valType Determinant = m[0][0] * Inverse[0][0] + // + m[0][1] * Inverse[1][0] + // + m[0][2] * Inverse[2][0] + // + m[0][3] * Inverse[3][0]; + __m128 Det0 = glm_vec4_dot(in[0], Row2); + __m128 Rcp0 = _mm_rcp_ps(Det0); + //__m128 Rcp0 = _mm_div_ps(one, Det0); + // Inverse /= Determinant; + out[0] = _mm_mul_ps(Inv0, Rcp0); + out[1] = _mm_mul_ps(Inv1, Rcp0); + out[2] = _mm_mul_ps(Inv2, Rcp0); + out[3] = _mm_mul_ps(Inv3, Rcp0); +} +/* +GLM_FUNC_QUALIFIER void glm_mat4_rotate(__m128 const in[4], float Angle, float const v[3], __m128 out[4]) +{ + float a = glm::radians(Angle); + float c = cos(a); + float s = sin(a); + + glm::vec4 AxisA(v[0], v[1], v[2], float(0)); + __m128 AxisB = _mm_set_ps(AxisA.w, AxisA.z, AxisA.y, AxisA.x); + __m128 AxisC = detail::sse_nrm_ps(AxisB); + + __m128 Cos0 = _mm_set_ss(c); + __m128 CosA = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 Sin0 = _mm_set_ss(s); + __m128 SinA = _mm_shuffle_ps(Sin0, Sin0, _MM_SHUFFLE(0, 0, 0, 0)); + + // vec<3, T, Q> temp = (valType(1) - c) * axis; + __m128 Temp0 = _mm_sub_ps(one, CosA); + __m128 Temp1 = _mm_mul_ps(Temp0, AxisC); + + //Rotate[0][0] = c + temp[0] * axis[0]; + //Rotate[0][1] = 0 + temp[0] * axis[1] + s * axis[2]; + //Rotate[0][2] = 0 + temp[0] * axis[2] - s * axis[1]; + __m128 Axis0 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(0, 0, 0, 0)); + __m128 TmpA0 = _mm_mul_ps(Axis0, AxisC); + __m128 CosA0 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 1, 1, 0)); + __m128 TmpA1 = _mm_add_ps(CosA0, TmpA0); + __m128 SinA0 = SinA;//_mm_set_ps(0.0f, s, -s, 0.0f); + __m128 TmpA2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 1, 2, 3)); + __m128 TmpA3 = _mm_mul_ps(SinA0, TmpA2); + __m128 TmpA4 = _mm_add_ps(TmpA1, TmpA3); + + //Rotate[1][0] = 0 + temp[1] * axis[0] - s * axis[2]; + //Rotate[1][1] = c + temp[1] * axis[1]; + //Rotate[1][2] = 0 + temp[1] * axis[2] + s * axis[0]; + __m128 Axis1 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(1, 1, 1, 1)); + __m128 TmpB0 = _mm_mul_ps(Axis1, AxisC); + __m128 CosA1 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 1, 0, 1)); + __m128 TmpB1 = _mm_add_ps(CosA1, TmpB0); + __m128 SinB0 = SinA;//_mm_set_ps(-s, 0.0f, s, 0.0f); + __m128 TmpB2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 0, 3, 2)); + __m128 TmpB3 = _mm_mul_ps(SinA0, TmpB2); + __m128 TmpB4 = _mm_add_ps(TmpB1, TmpB3); + + //Rotate[2][0] = 0 + temp[2] * axis[0] + s * axis[1]; + //Rotate[2][1] = 0 + temp[2] * axis[1] - s * axis[0]; + //Rotate[2][2] = c + temp[2] * axis[2]; + __m128 Axis2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(2, 2, 2, 2)); + __m128 TmpC0 = _mm_mul_ps(Axis2, AxisC); + __m128 CosA2 = _mm_shuffle_ps(Cos0, Cos0, _MM_SHUFFLE(1, 0, 1, 1)); + __m128 TmpC1 = _mm_add_ps(CosA2, TmpC0); + __m128 SinC0 = SinA;//_mm_set_ps(s, -s, 0.0f, 0.0f); + __m128 TmpC2 = _mm_shuffle_ps(AxisC, AxisC, _MM_SHUFFLE(3, 3, 0, 1)); + __m128 TmpC3 = _mm_mul_ps(SinA0, TmpC2); + __m128 TmpC4 = _mm_add_ps(TmpC1, TmpC3); + + __m128 Result[4]; + Result[0] = TmpA4; + Result[1] = TmpB4; + Result[2] = TmpC4; + Result[3] = _mm_set_ps(1, 0, 0, 0); + + //mat<4, 4, valType> Result; + //Result[0] = m[0] * Rotate[0][0] + m[1] * Rotate[0][1] + m[2] * Rotate[0][2]; + //Result[1] = m[0] * Rotate[1][0] + m[1] * Rotate[1][1] + m[2] * Rotate[1][2]; + //Result[2] = m[0] * Rotate[2][0] + m[1] * Rotate[2][1] + m[2] * Rotate[2][2]; + //Result[3] = m[3]; + //return Result; + sse_mul_ps(in, Result, out); +} +*/ +GLM_FUNC_QUALIFIER void glm_mat4_outerProduct(__m128 const& c, __m128 const& r, __m128 out[4]) +{ + out[0] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(0, 0, 0, 0))); + out[1] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(1, 1, 1, 1))); + out[2] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(2, 2, 2, 2))); + out[3] = _mm_mul_ps(c, _mm_shuffle_ps(r, r, _MM_SHUFFLE(3, 3, 3, 3))); +} + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/neon.h b/thirdparty/glm/glm/simd/neon.h new file mode 100644 index 0000000..f85947f --- /dev/null +++ b/thirdparty/glm/glm/simd/neon.h @@ -0,0 +1,155 @@ +/// @ref simd_neon +/// @file glm/simd/neon.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_NEON_BIT +#include + +namespace glm { + namespace neon { + static inline float32x4_t dupq_lane(float32x4_t vsrc, int lane) { + switch(lane) { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + case 0: return vdupq_laneq_f32(vsrc, 0); + case 1: return vdupq_laneq_f32(vsrc, 1); + case 2: return vdupq_laneq_f32(vsrc, 2); + case 3: return vdupq_laneq_f32(vsrc, 3); +#else + case 0: return vdupq_n_f32(vgetq_lane_f32(vsrc, 0)); + case 1: return vdupq_n_f32(vgetq_lane_f32(vsrc, 1)); + case 2: return vdupq_n_f32(vgetq_lane_f32(vsrc, 2)); + case 3: return vdupq_n_f32(vgetq_lane_f32(vsrc, 3)); +#endif + } + assert(!"Unreachable code executed!"); + return vdupq_n_f32(0.0f); + } + + static inline float32x2_t dup_lane(float32x4_t vsrc, int lane) { + switch(lane) { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + case 0: return vdup_laneq_f32(vsrc, 0); + case 1: return vdup_laneq_f32(vsrc, 1); + case 2: return vdup_laneq_f32(vsrc, 2); + case 3: return vdup_laneq_f32(vsrc, 3); +#else + case 0: return vdup_n_f32(vgetq_lane_f32(vsrc, 0)); + case 1: return vdup_n_f32(vgetq_lane_f32(vsrc, 1)); + case 2: return vdup_n_f32(vgetq_lane_f32(vsrc, 2)); + case 3: return vdup_n_f32(vgetq_lane_f32(vsrc, 3)); +#endif + } + assert(!"Unreachable code executed!"); + return vdup_n_f32(0.0f); + } + + static inline float32x4_t copy_lane(float32x4_t vdst, int dlane, float32x4_t vsrc, int slane) { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + switch(dlane) { + case 0: + switch(slane) { + case 0: return vcopyq_laneq_f32(vdst, 0, vsrc, 0); + case 1: return vcopyq_laneq_f32(vdst, 0, vsrc, 1); + case 2: return vcopyq_laneq_f32(vdst, 0, vsrc, 2); + case 3: return vcopyq_laneq_f32(vdst, 0, vsrc, 3); + } + assert(!"Unreachable code executed!"); + case 1: + switch(slane) { + case 0: return vcopyq_laneq_f32(vdst, 1, vsrc, 0); + case 1: return vcopyq_laneq_f32(vdst, 1, vsrc, 1); + case 2: return vcopyq_laneq_f32(vdst, 1, vsrc, 2); + case 3: return vcopyq_laneq_f32(vdst, 1, vsrc, 3); + } + assert(!"Unreachable code executed!"); + case 2: + switch(slane) { + case 0: return vcopyq_laneq_f32(vdst, 2, vsrc, 0); + case 1: return vcopyq_laneq_f32(vdst, 2, vsrc, 1); + case 2: return vcopyq_laneq_f32(vdst, 2, vsrc, 2); + case 3: return vcopyq_laneq_f32(vdst, 2, vsrc, 3); + } + assert(!"Unreachable code executed!"); + case 3: + switch(slane) { + case 0: return vcopyq_laneq_f32(vdst, 3, vsrc, 0); + case 1: return vcopyq_laneq_f32(vdst, 3, vsrc, 1); + case 2: return vcopyq_laneq_f32(vdst, 3, vsrc, 2); + case 3: return vcopyq_laneq_f32(vdst, 3, vsrc, 3); + } + assert(!"Unreachable code executed!"); + } +#else + + float l; + switch(slane) { + case 0: l = vgetq_lane_f32(vsrc, 0); break; + case 1: l = vgetq_lane_f32(vsrc, 1); break; + case 2: l = vgetq_lane_f32(vsrc, 2); break; + case 3: l = vgetq_lane_f32(vsrc, 3); break; + default: + assert(!"Unreachable code executed!"); + } + switch(dlane) { + case 0: return vsetq_lane_f32(l, vdst, 0); + case 1: return vsetq_lane_f32(l, vdst, 1); + case 2: return vsetq_lane_f32(l, vdst, 2); + case 3: return vsetq_lane_f32(l, vdst, 3); + } +#endif + assert(!"Unreachable code executed!"); + return vdupq_n_f32(0.0f); + } + + static inline float32x4_t mul_lane(float32x4_t v, float32x4_t vlane, int lane) { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT + switch(lane) { + case 0: return vmulq_laneq_f32(v, vlane, 0); break; + case 1: return vmulq_laneq_f32(v, vlane, 1); break; + case 2: return vmulq_laneq_f32(v, vlane, 2); break; + case 3: return vmulq_laneq_f32(v, vlane, 3); break; + default: + assert(!"Unreachable code executed!"); + } + assert(!"Unreachable code executed!"); + return vdupq_n_f32(0.0f); +#else + return vmulq_f32(v, dupq_lane(vlane, lane)); +#endif + } + + static inline float32x4_t madd_lane(float32x4_t acc, float32x4_t v, float32x4_t vlane, int lane) { +#if GLM_ARCH & GLM_ARCH_ARMV8_BIT +#ifdef GLM_CONFIG_FORCE_FMA +# define FMADD_LANE(acc, x, y, L) do { asm volatile ("fmla %0.4s, %1.4s, %2.4s" : "+w"(acc) : "w"(x), "w"(dup_lane(y, L))); } while(0) +#else +# define FMADD_LANE(acc, x, y, L) do { acc = vmlaq_laneq_f32(acc, x, y, L); } while(0) +#endif + + switch(lane) { + case 0: + FMADD_LANE(acc, v, vlane, 0); + return acc; + case 1: + FMADD_LANE(acc, v, vlane, 1); + return acc; + case 2: + FMADD_LANE(acc, v, vlane, 2); + return acc; + case 3: + FMADD_LANE(acc, v, vlane, 3); + return acc; + default: + assert(!"Unreachable code executed!"); + } + assert(!"Unreachable code executed!"); + return vdupq_n_f32(0.0f); +# undef FMADD_LANE +#else + return vaddq_f32(acc, vmulq_f32(v, dupq_lane(vlane, lane))); +#endif + } + } //namespace neon +} // namespace glm +#endif // GLM_ARCH & GLM_ARCH_NEON_BIT diff --git a/thirdparty/glm/glm/simd/packing.h b/thirdparty/glm/glm/simd/packing.h new file mode 100644 index 0000000..609163e --- /dev/null +++ b/thirdparty/glm/glm/simd/packing.h @@ -0,0 +1,8 @@ +/// @ref simd +/// @file glm/simd/packing.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/simd/platform.h b/thirdparty/glm/glm/simd/platform.h new file mode 100644 index 0000000..a318b09 --- /dev/null +++ b/thirdparty/glm/glm/simd/platform.h @@ -0,0 +1,469 @@ +#pragma once + +/////////////////////////////////////////////////////////////////////////////////// +// Platform + +#define GLM_PLATFORM_UNKNOWN 0x00000000 +#define GLM_PLATFORM_WINDOWS 0x00010000 +#define GLM_PLATFORM_LINUX 0x00020000 +#define GLM_PLATFORM_APPLE 0x00040000 +//#define GLM_PLATFORM_IOS 0x00080000 +#define GLM_PLATFORM_ANDROID 0x00100000 +#define GLM_PLATFORM_CHROME_NACL 0x00200000 +#define GLM_PLATFORM_UNIX 0x00400000 +#define GLM_PLATFORM_QNXNTO 0x00800000 +#define GLM_PLATFORM_WINCE 0x01000000 +#define GLM_PLATFORM_CYGWIN 0x02000000 + +#ifdef GLM_FORCE_PLATFORM_UNKNOWN +# define GLM_PLATFORM GLM_PLATFORM_UNKNOWN +#elif defined(__CYGWIN__) +# define GLM_PLATFORM GLM_PLATFORM_CYGWIN +#elif defined(__QNXNTO__) +# define GLM_PLATFORM GLM_PLATFORM_QNXNTO +#elif defined(__APPLE__) +# define GLM_PLATFORM GLM_PLATFORM_APPLE +#elif defined(WINCE) +# define GLM_PLATFORM GLM_PLATFORM_WINCE +#elif defined(_WIN32) +# define GLM_PLATFORM GLM_PLATFORM_WINDOWS +#elif defined(__native_client__) +# define GLM_PLATFORM GLM_PLATFORM_CHROME_NACL +#elif defined(__ANDROID__) +# define GLM_PLATFORM GLM_PLATFORM_ANDROID +#elif defined(__linux) +# define GLM_PLATFORM GLM_PLATFORM_LINUX +#elif defined(__unix) +# define GLM_PLATFORM GLM_PLATFORM_UNIX +#else +# define GLM_PLATFORM GLM_PLATFORM_UNKNOWN +#endif// + +/////////////////////////////////////////////////////////////////////////////////// +// Compiler + +#define GLM_COMPILER_UNKNOWN 0x00000000 + +// Intel +#define GLM_COMPILER_INTEL 0x00100000 +#define GLM_COMPILER_INTEL14 0x00100040 +#define GLM_COMPILER_INTEL15 0x00100050 +#define GLM_COMPILER_INTEL16 0x00100060 +#define GLM_COMPILER_INTEL17 0x00100070 +#define GLM_COMPILER_INTEL18 0x00100080 +#define GLM_COMPILER_INTEL19 0x00100090 +#define GLM_COMPILER_INTEL21 0x001000A0 + +// Visual C++ defines +#define GLM_COMPILER_VC 0x01000000 +#define GLM_COMPILER_VC12 0x01000001 // Visual Studio 2013 +#define GLM_COMPILER_VC14 0x01000002 // Visual Studio 2015 +#define GLM_COMPILER_VC15 0x01000003 // Visual Studio 2017 +#define GLM_COMPILER_VC15_3 0x01000004 +#define GLM_COMPILER_VC15_5 0x01000005 +#define GLM_COMPILER_VC15_6 0x01000006 +#define GLM_COMPILER_VC15_7 0x01000007 +#define GLM_COMPILER_VC15_8 0x01000008 +#define GLM_COMPILER_VC15_9 0x01000009 +#define GLM_COMPILER_VC16 0x0100000A // Visual Studio 2019 +#define GLM_COMPILER_VC17 0x0100000B // Visual Studio 2022 + +// GCC defines +#define GLM_COMPILER_GCC 0x02000000 +#define GLM_COMPILER_GCC46 0x020000D0 +#define GLM_COMPILER_GCC47 0x020000E0 +#define GLM_COMPILER_GCC48 0x020000F0 +#define GLM_COMPILER_GCC49 0x02000100 +#define GLM_COMPILER_GCC5 0x02000200 +#define GLM_COMPILER_GCC6 0x02000300 +#define GLM_COMPILER_GCC61 0x02000800 +#define GLM_COMPILER_GCC7 0x02000400 +#define GLM_COMPILER_GCC8 0x02000500 +#define GLM_COMPILER_GCC9 0x02000600 +#define GLM_COMPILER_GCC10 0x02000700 +#define GLM_COMPILER_GCC11 0x02000800 +#define GLM_COMPILER_GCC12 0x02000900 +#define GLM_COMPILER_GCC13 0x02000A00 +#define GLM_COMPILER_GCC14 0x02000B00 + +// CUDA +#define GLM_COMPILER_CUDA 0x10000000 +#define GLM_COMPILER_CUDA75 0x10000001 +#define GLM_COMPILER_CUDA80 0x10000002 +#define GLM_COMPILER_CUDA90 0x10000004 +#define GLM_COMPILER_CUDA_RTC 0x10000100 + +// Clang +#define GLM_COMPILER_CLANG 0x20000000 +#define GLM_COMPILER_CLANG34 0x20000050 +#define GLM_COMPILER_CLANG35 0x20000060 +#define GLM_COMPILER_CLANG36 0x20000070 +#define GLM_COMPILER_CLANG37 0x20000080 +#define GLM_COMPILER_CLANG38 0x20000090 +#define GLM_COMPILER_CLANG39 0x200000A0 +#define GLM_COMPILER_CLANG4 0x200000B0 +#define GLM_COMPILER_CLANG5 0x200000C0 +#define GLM_COMPILER_CLANG6 0x200000D0 +#define GLM_COMPILER_CLANG7 0x200000E0 +#define GLM_COMPILER_CLANG8 0x200000F0 +#define GLM_COMPILER_CLANG9 0x20000100 +#define GLM_COMPILER_CLANG10 0x20000200 +#define GLM_COMPILER_CLANG11 0x20000300 +#define GLM_COMPILER_CLANG12 0x20000400 +#define GLM_COMPILER_CLANG13 0x20000500 +#define GLM_COMPILER_CLANG14 0x20000600 +#define GLM_COMPILER_CLANG15 0x20000700 +#define GLM_COMPILER_CLANG16 0x20000800 +#define GLM_COMPILER_CLANG17 0x20000900 +#define GLM_COMPILER_CLANG18 0x20000A00 +#define GLM_COMPILER_CLANG19 0x20000B00 + +// HIP +#define GLM_COMPILER_HIP 0x40000000 + +// Build model +#define GLM_MODEL_32 0x00000010 +#define GLM_MODEL_64 0x00000020 + +// Force generic C++ compiler +#ifdef GLM_FORCE_COMPILER_UNKNOWN +# define GLM_COMPILER GLM_COMPILER_UNKNOWN + +#elif defined(__INTEL_COMPILER) +# if __INTEL_COMPILER >= 2021 +# define GLM_COMPILER GLM_COMPILER_INTEL21 +# elif __INTEL_COMPILER >= 1900 +# define GLM_COMPILER GLM_COMPILER_INTEL19 +# elif __INTEL_COMPILER >= 1800 +# define GLM_COMPILER GLM_COMPILER_INTEL18 +# elif __INTEL_COMPILER >= 1700 +# define GLM_COMPILER GLM_COMPILER_INTEL17 +# elif __INTEL_COMPILER >= 1600 +# define GLM_COMPILER GLM_COMPILER_INTEL16 +# elif __INTEL_COMPILER >= 1500 +# define GLM_COMPILER GLM_COMPILER_INTEL15 +# elif __INTEL_COMPILER >= 1400 +# define GLM_COMPILER GLM_COMPILER_INTEL14 +# elif __INTEL_COMPILER < 1400 +# error "GLM requires ICC 2013 SP1 or newer" +# endif + +// CUDA +#elif defined(__CUDACC__) +# if !defined(CUDA_VERSION) && !defined(GLM_FORCE_CUDA) +# include // make sure version is defined since nvcc does not define it itself! +# endif +# if defined(__CUDACC_RTC__) +# define GLM_COMPILER GLM_COMPILER_CUDA_RTC +# elif CUDA_VERSION >= 8000 +# define GLM_COMPILER GLM_COMPILER_CUDA80 +# elif CUDA_VERSION >= 7500 +# define GLM_COMPILER GLM_COMPILER_CUDA75 +# elif CUDA_VERSION >= 7000 +# define GLM_COMPILER GLM_COMPILER_CUDA70 +# elif CUDA_VERSION < 7000 +# error "GLM requires CUDA 7.0 or higher" +# endif + +// HIP +#elif defined(__HIP__) +# define GLM_COMPILER GLM_COMPILER_HIP + +// Clang +#elif defined(__clang__) +# if defined(__apple_build_version__) +# if (__clang_major__ < 6) +# error "GLM requires Clang 3.4 / Apple Clang 6.0 or higher" +# elif __clang_major__ == 6 && __clang_minor__ == 0 +# define GLM_COMPILER GLM_COMPILER_CLANG35 +# elif __clang_major__ == 6 && __clang_minor__ >= 1 +# define GLM_COMPILER GLM_COMPILER_CLANG36 +# elif __clang_major__ >= 7 +# define GLM_COMPILER GLM_COMPILER_CLANG37 +# endif +# else +# if ((__clang_major__ == 3) && (__clang_minor__ < 4)) || (__clang_major__ < 3) +# error "GLM requires Clang 3.4 or higher" +# elif __clang_major__ == 3 && __clang_minor__ == 4 +# define GLM_COMPILER GLM_COMPILER_CLANG34 +# elif __clang_major__ == 3 && __clang_minor__ == 5 +# define GLM_COMPILER GLM_COMPILER_CLANG35 +# elif __clang_major__ == 3 && __clang_minor__ == 6 +# define GLM_COMPILER GLM_COMPILER_CLANG36 +# elif __clang_major__ == 3 && __clang_minor__ == 7 +# define GLM_COMPILER GLM_COMPILER_CLANG37 +# elif __clang_major__ == 3 && __clang_minor__ == 8 +# define GLM_COMPILER GLM_COMPILER_CLANG38 +# elif __clang_major__ == 3 && __clang_minor__ >= 9 +# define GLM_COMPILER GLM_COMPILER_CLANG39 +# elif __clang_major__ == 4 && __clang_minor__ == 0 +# define GLM_COMPILER GLM_COMPILER_CLANG4 +# elif __clang_major__ == 5 +# define GLM_COMPILER GLM_COMPILER_CLANG5 +# elif __clang_major__ == 6 +# define GLM_COMPILER GLM_COMPILER_CLANG6 +# elif __clang_major__ == 7 +# define GLM_COMPILER GLM_COMPILER_CLANG7 +# elif __clang_major__ == 8 +# define GLM_COMPILER GLM_COMPILER_CLANG8 +# elif __clang_major__ == 9 +# define GLM_COMPILER GLM_COMPILER_CLANG9 +# elif __clang_major__ == 10 +# define GLM_COMPILER GLM_COMPILER_CLANG10 +# elif __clang_major__ == 11 +# define GLM_COMPILER GLM_COMPILER_CLANG11 +# elif __clang_major__ == 12 +# define GLM_COMPILER GLM_COMPILER_CLANG12 +# elif __clang_major__ == 13 +# define GLM_COMPILER GLM_COMPILER_CLANG13 +# elif __clang_major__ == 14 +# define GLM_COMPILER GLM_COMPILER_CLANG14 +# elif __clang_major__ == 15 +# define GLM_COMPILER GLM_COMPILER_CLANG15 +# elif __clang_major__ == 16 +# define GLM_COMPILER GLM_COMPILER_CLANG16 +# elif __clang_major__ == 17 +# define GLM_COMPILER GLM_COMPILER_CLANG17 +# elif __clang_major__ == 18 +# define GLM_COMPILER GLM_COMPILER_CLANG18 +# elif __clang_major__ >= 19 +# define GLM_COMPILER GLM_COMPILER_CLANG19 +# endif +# endif + +// Visual C++ +#elif defined(_MSC_VER) +# if _MSC_VER >= 1930 +# define GLM_COMPILER GLM_COMPILER_VC17 +# elif _MSC_VER >= 1920 +# define GLM_COMPILER GLM_COMPILER_VC16 +# elif _MSC_VER >= 1916 +# define GLM_COMPILER GLM_COMPILER_VC15_9 +# elif _MSC_VER >= 1915 +# define GLM_COMPILER GLM_COMPILER_VC15_8 +# elif _MSC_VER >= 1914 +# define GLM_COMPILER GLM_COMPILER_VC15_7 +# elif _MSC_VER >= 1913 +# define GLM_COMPILER GLM_COMPILER_VC15_6 +# elif _MSC_VER >= 1912 +# define GLM_COMPILER GLM_COMPILER_VC15_5 +# elif _MSC_VER >= 1911 +# define GLM_COMPILER GLM_COMPILER_VC15_3 +# elif _MSC_VER >= 1910 +# define GLM_COMPILER GLM_COMPILER_VC15 +# elif _MSC_VER >= 1900 +# define GLM_COMPILER GLM_COMPILER_VC14 +# elif _MSC_VER >= 1800 +# define GLM_COMPILER GLM_COMPILER_VC12 +# elif _MSC_VER < 1800 +# error "GLM requires Visual C++ 12 - 2013 or higher" +# endif//_MSC_VER + +// G++ +#elif defined(__GNUC__) || defined(__MINGW32__) +# if __GNUC__ >= 14 +# define GLM_COMPILER GLM_COMPILER_GCC14 +# elif __GNUC__ >= 13 +# define GLM_COMPILER GLM_COMPILER_GCC13 +# elif __GNUC__ >= 12 +# define GLM_COMPILER GLM_COMPILER_GCC12 +# elif __GNUC__ >= 11 +# define GLM_COMPILER GLM_COMPILER_GCC11 +# elif __GNUC__ >= 10 +# define GLM_COMPILER GLM_COMPILER_GCC10 +# elif __GNUC__ >= 9 +# define GLM_COMPILER GLM_COMPILER_GCC9 +# elif __GNUC__ >= 8 +# define GLM_COMPILER GLM_COMPILER_GCC8 +# elif __GNUC__ >= 7 +# define GLM_COMPILER GLM_COMPILER_GCC7 +# elif __GNUC__ >= 6 +# define GLM_COMPILER GLM_COMPILER_GCC6 +# elif __GNUC__ >= 5 +# define GLM_COMPILER GLM_COMPILER_GCC5 +# elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9 +# define GLM_COMPILER GLM_COMPILER_GCC49 +# elif __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +# define GLM_COMPILER GLM_COMPILER_GCC48 +# elif __GNUC__ == 4 && __GNUC_MINOR__ >= 7 +# define GLM_COMPILER GLM_COMPILER_GCC47 +# elif __GNUC__ == 4 && __GNUC_MINOR__ >= 6 +# define GLM_COMPILER GLM_COMPILER_GCC46 +# elif ((__GNUC__ == 4) && (__GNUC_MINOR__ < 6)) || (__GNUC__ < 4) +# error "GLM requires GCC 4.6 or higher" +# endif + +#else +# define GLM_COMPILER GLM_COMPILER_UNKNOWN +#endif + +#ifndef GLM_COMPILER +# error "GLM_COMPILER undefined, your compiler may not be supported by GLM. Add #define GLM_COMPILER 0 to ignore this message." +#endif//GLM_COMPILER + +/////////////////////////////////////////////////////////////////////////////////// +// Instruction sets + +// User defines: GLM_FORCE_PURE GLM_FORCE_INTRINSICS GLM_FORCE_SSE2 GLM_FORCE_SSE3 GLM_FORCE_AVX GLM_FORCE_AVX2 GLM_FORCE_AVX2 + +#define GLM_ARCH_MIPS_BIT (0x10000000) +#define GLM_ARCH_PPC_BIT (0x20000000) +#define GLM_ARCH_ARM_BIT (0x40000000) +#define GLM_ARCH_ARMV8_BIT (0x01000000) +#define GLM_ARCH_X86_BIT (0x80000000) + +#define GLM_ARCH_SIMD_BIT (0x00001000) + +#define GLM_ARCH_NEON_BIT (0x00000001) +#define GLM_ARCH_SSE_BIT (0x00000002) +#define GLM_ARCH_SSE2_BIT (0x00000004) +#define GLM_ARCH_SSE3_BIT (0x00000008) +#define GLM_ARCH_SSSE3_BIT (0x00000010) +#define GLM_ARCH_SSE41_BIT (0x00000020) +#define GLM_ARCH_SSE42_BIT (0x00000040) +#define GLM_ARCH_AVX_BIT (0x00000080) +#define GLM_ARCH_AVX2_BIT (0x00000100) + +#define GLM_ARCH_UNKNOWN (0) +#define GLM_ARCH_X86 (GLM_ARCH_X86_BIT) +#define GLM_ARCH_SSE (GLM_ARCH_SSE_BIT | GLM_ARCH_SIMD_BIT | GLM_ARCH_X86) +#define GLM_ARCH_SSE2 (GLM_ARCH_SSE2_BIT | GLM_ARCH_SSE) +#define GLM_ARCH_SSE3 (GLM_ARCH_SSE3_BIT | GLM_ARCH_SSE2) +#define GLM_ARCH_SSSE3 (GLM_ARCH_SSSE3_BIT | GLM_ARCH_SSE3) +#define GLM_ARCH_SSE41 (GLM_ARCH_SSE41_BIT | GLM_ARCH_SSSE3) +#define GLM_ARCH_SSE42 (GLM_ARCH_SSE42_BIT | GLM_ARCH_SSE41) +#define GLM_ARCH_AVX (GLM_ARCH_AVX_BIT | GLM_ARCH_SSE42) +#define GLM_ARCH_AVX2 (GLM_ARCH_AVX2_BIT | GLM_ARCH_AVX) +#define GLM_ARCH_ARM (GLM_ARCH_ARM_BIT) +#define GLM_ARCH_ARMV8 (GLM_ARCH_NEON_BIT | GLM_ARCH_SIMD_BIT | GLM_ARCH_ARM | GLM_ARCH_ARMV8_BIT) +#define GLM_ARCH_NEON (GLM_ARCH_NEON_BIT | GLM_ARCH_SIMD_BIT | GLM_ARCH_ARM) +#define GLM_ARCH_MIPS (GLM_ARCH_MIPS_BIT) +#define GLM_ARCH_PPC (GLM_ARCH_PPC_BIT) + +#if defined(GLM_FORCE_ARCH_UNKNOWN) || defined(GLM_FORCE_PURE) +# define GLM_ARCH GLM_ARCH_UNKNOWN +#elif defined(GLM_FORCE_NEON) +# if __ARM_ARCH >= 8 +# define GLM_ARCH (GLM_ARCH_ARMV8) +# else +# define GLM_ARCH (GLM_ARCH_NEON) +# endif +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_AVX2) +# define GLM_ARCH (GLM_ARCH_AVX2) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_AVX) +# define GLM_ARCH (GLM_ARCH_AVX) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE42) +# define GLM_ARCH (GLM_ARCH_SSE42) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE41) +# define GLM_ARCH (GLM_ARCH_SSE41) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSSE3) +# define GLM_ARCH (GLM_ARCH_SSSE3) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE3) +# define GLM_ARCH (GLM_ARCH_SSE3) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE2) +# define GLM_ARCH (GLM_ARCH_SSE2) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_SSE) +# define GLM_ARCH (GLM_ARCH_SSE) +# define GLM_FORCE_INTRINSICS +#elif defined(GLM_FORCE_INTRINSICS) && !defined(GLM_FORCE_XYZW_ONLY) +# if defined(__AVX2__) +# define GLM_ARCH (GLM_ARCH_AVX2) +# elif defined(__AVX__) +# define GLM_ARCH (GLM_ARCH_AVX) +# elif defined(__SSE4_2__) +# define GLM_ARCH (GLM_ARCH_SSE42) +# elif defined(__SSE4_1__) +# define GLM_ARCH (GLM_ARCH_SSE41) +# elif defined(__SSSE3__) +# define GLM_ARCH (GLM_ARCH_SSSE3) +# elif defined(__SSE3__) +# define GLM_ARCH (GLM_ARCH_SSE3) +# elif defined(__SSE2__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_IX86_FP) +# define GLM_ARCH (GLM_ARCH_SSE2) +# elif defined(__i386__) +# define GLM_ARCH (GLM_ARCH_X86) +# elif defined(__ARM_ARCH) && (__ARM_ARCH >= 8) +# define GLM_ARCH (GLM_ARCH_ARMV8) +# elif defined(__ARM_NEON) +# define GLM_ARCH (GLM_ARCH_ARM | GLM_ARCH_NEON) +# elif defined(__arm__ ) || defined(_M_ARM) +# define GLM_ARCH (GLM_ARCH_ARM) +# elif defined(__mips__ ) +# define GLM_ARCH (GLM_ARCH_MIPS) +# elif defined(__powerpc__ ) || defined(_M_PPC) +# define GLM_ARCH (GLM_ARCH_PPC) +# else +# define GLM_ARCH (GLM_ARCH_UNKNOWN) +# endif +#else +# if defined(__x86_64__) || defined(_M_X64) || defined(_M_IX86) || defined(__i386__) +# define GLM_ARCH (GLM_ARCH_X86) +# elif defined(__arm__) || defined(_M_ARM) +# define GLM_ARCH (GLM_ARCH_ARM) +# elif defined(__powerpc__) || defined(_M_PPC) +# define GLM_ARCH (GLM_ARCH_PPC) +# elif defined(__mips__) +# define GLM_ARCH (GLM_ARCH_MIPS) +# else +# define GLM_ARCH (GLM_ARCH_UNKNOWN) +# endif +#endif + +#if GLM_ARCH & GLM_ARCH_AVX2_BIT +# include +#elif GLM_ARCH & GLM_ARCH_AVX_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE42_BIT +# if GLM_COMPILER & GLM_COMPILER_CLANG +# include +# endif +# include +#elif GLM_ARCH & GLM_ARCH_SSE41_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSSE3_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE3_BIT +# include +#elif GLM_ARCH & GLM_ARCH_SSE2_BIT +# include +#elif GLM_ARCH & GLM_ARCH_NEON_BIT +# include "neon.h" +#endif//GLM_ARCH + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + typedef __m128 glm_f32vec4; + typedef __m128i glm_i32vec4; + typedef __m128i glm_u32vec4; + typedef __m128d glm_f64vec2; + typedef __m128i glm_i64vec2; + typedef __m128i glm_u64vec2; + + typedef glm_f32vec4 glm_vec4; + typedef glm_i32vec4 glm_ivec4; + typedef glm_u32vec4 glm_uvec4; + typedef glm_f64vec2 glm_dvec2; +#endif + +#if GLM_ARCH & GLM_ARCH_AVX_BIT + typedef __m256d glm_f64vec4; + typedef glm_f64vec4 glm_dvec4; +#endif + +#if GLM_ARCH & GLM_ARCH_AVX2_BIT + typedef __m256i glm_i64vec4; + typedef __m256i glm_u64vec4; +#endif + +#if GLM_ARCH & GLM_ARCH_NEON_BIT + typedef float32x4_t glm_f32vec4; + typedef int32x4_t glm_i32vec4; + typedef uint32x4_t glm_u32vec4; +#endif diff --git a/thirdparty/glm/glm/simd/trigonometric.h b/thirdparty/glm/glm/simd/trigonometric.h new file mode 100644 index 0000000..739b796 --- /dev/null +++ b/thirdparty/glm/glm/simd/trigonometric.h @@ -0,0 +1,9 @@ +/// @ref simd +/// @file glm/simd/trigonometric.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT + diff --git a/thirdparty/glm/glm/simd/vector_relational.h b/thirdparty/glm/glm/simd/vector_relational.h new file mode 100644 index 0000000..f7385e9 --- /dev/null +++ b/thirdparty/glm/glm/simd/vector_relational.h @@ -0,0 +1,8 @@ +/// @ref simd +/// @file glm/simd/vector_relational.h + +#pragma once + +#if GLM_ARCH & GLM_ARCH_SSE2_BIT + +#endif//GLM_ARCH & GLM_ARCH_SSE2_BIT diff --git a/thirdparty/glm/glm/trigonometric.hpp b/thirdparty/glm/glm/trigonometric.hpp new file mode 100644 index 0000000..51d49c1 --- /dev/null +++ b/thirdparty/glm/glm/trigonometric.hpp @@ -0,0 +1,210 @@ +/// @ref core +/// @file glm/trigonometric.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions +/// +/// @defgroup core_func_trigonometric Angle and Trigonometry Functions +/// @ingroup core +/// +/// Function parameters specified as angle are assumed to be in units of radians. +/// In no case will any of these functions result in a divide by zero error. If +/// the divisor of a ratio is 0, then results will be undefined. +/// +/// These all operate component-wise. The description is per component. +/// +/// Include to use these core features. +/// +/// @see ext_vector_trigonometric + +#pragma once + +#include "detail/setup.hpp" +#include "detail/qualifier.hpp" + +namespace glm +{ + /// @addtogroup core_func_trigonometric + /// @{ + + /// Converts degrees to radians and returns the result. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL radians man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec radians(vec const& degrees); + + /// Converts radians to degrees and returns the result. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL degrees man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec degrees(vec const& radians); + + /// The standard trigonometric sine function. + /// The values returned by this function will range from [-1, 1]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sin man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec sin(vec const& angle); + + /// The standard trigonometric cosine function. + /// The values returned by this function will range from [-1, 1]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL cos man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec cos(vec const& angle); + + /// The standard trigonometric tangent function. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL tan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec tan(vec const& angle); + + /// Arc sine. Returns an angle whose sine is x. + /// The range of values returned by this function is [-PI/2, PI/2]. + /// Results are undefined if |x| > 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL asin man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec asin(vec const& x); + + /// Arc cosine. Returns an angle whose cosine is x. + /// The range of values returned by this function is [0, PI]. + /// Results are undefined if |x| > 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL acos man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec acos(vec const& x); + + /// Arc tangent. Returns an angle whose tangent is y/x. + /// The signs of x and y are used to determine what + /// quadrant the angle is in. The range of values returned + /// by this function is [-PI, PI]. Results are undefined + /// if x and y are both 0. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atan(vec const& y, vec const& x); + + /// Arc tangent. Returns an angle whose tangent is y_over_x. + /// The range of values returned by this function is [-PI/2, PI/2]. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atan man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atan(vec const& y_over_x); + + /// Returns the hyperbolic sine function, (exp(x) - exp(-x)) / 2 + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL sinh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec sinh(vec const& angle); + + /// Returns the hyperbolic cosine function, (exp(x) + exp(-x)) / 2 + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL cosh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec cosh(vec const& angle); + + /// Returns the hyperbolic tangent function, sinh(angle) / cosh(angle) + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL tanh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec tanh(vec const& angle); + + /// Arc hyperbolic sine; returns the inverse of sinh. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL asinh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec asinh(vec const& x); + + /// Arc hyperbolic cosine; returns the non-negative inverse + /// of cosh. Results are undefined if x < 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL acosh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec acosh(vec const& x); + + /// Arc hyperbolic tangent; returns the inverse of tanh. + /// Results are undefined if abs(x) >= 1. + /// + /// @tparam L Integer between 1 and 4 included that qualify the dimension of the vector + /// @tparam T Floating-point scalar types + /// @tparam Q Value from qualifier enum + /// + /// @see GLSL atanh man page + /// @see GLSL 4.20.8 specification, section 8.1 Angle and Trigonometry Functions + template + GLM_FUNC_DECL vec atanh(vec const& x); + + /// @} +}//namespace glm + +#include "detail/func_trigonometric.inl" diff --git a/thirdparty/glm/glm/vec2.hpp b/thirdparty/glm/glm/vec2.hpp new file mode 100644 index 0000000..cd4e070 --- /dev/null +++ b/thirdparty/glm/glm/vec2.hpp @@ -0,0 +1,14 @@ +/// @ref core +/// @file glm/vec2.hpp + +#pragma once +#include "./ext/vector_bool2.hpp" +#include "./ext/vector_bool2_precision.hpp" +#include "./ext/vector_float2.hpp" +#include "./ext/vector_float2_precision.hpp" +#include "./ext/vector_double2.hpp" +#include "./ext/vector_double2_precision.hpp" +#include "./ext/vector_int2.hpp" +#include "./ext/vector_int2_sized.hpp" +#include "./ext/vector_uint2.hpp" +#include "./ext/vector_uint2_sized.hpp" diff --git a/thirdparty/glm/glm/vec3.hpp b/thirdparty/glm/glm/vec3.hpp new file mode 100644 index 0000000..f5a927d --- /dev/null +++ b/thirdparty/glm/glm/vec3.hpp @@ -0,0 +1,14 @@ +/// @ref core +/// @file glm/vec3.hpp + +#pragma once +#include "./ext/vector_bool3.hpp" +#include "./ext/vector_bool3_precision.hpp" +#include "./ext/vector_float3.hpp" +#include "./ext/vector_float3_precision.hpp" +#include "./ext/vector_double3.hpp" +#include "./ext/vector_double3_precision.hpp" +#include "./ext/vector_int3.hpp" +#include "./ext/vector_int3_sized.hpp" +#include "./ext/vector_uint3.hpp" +#include "./ext/vector_uint3_sized.hpp" diff --git a/thirdparty/glm/glm/vec4.hpp b/thirdparty/glm/glm/vec4.hpp new file mode 100644 index 0000000..c6ea9f1 --- /dev/null +++ b/thirdparty/glm/glm/vec4.hpp @@ -0,0 +1,15 @@ +/// @ref core +/// @file glm/vec4.hpp + +#pragma once +#include "./ext/vector_bool4.hpp" +#include "./ext/vector_bool4_precision.hpp" +#include "./ext/vector_float4.hpp" +#include "./ext/vector_float4_precision.hpp" +#include "./ext/vector_double4.hpp" +#include "./ext/vector_double4_precision.hpp" +#include "./ext/vector_int4.hpp" +#include "./ext/vector_int4_sized.hpp" +#include "./ext/vector_uint4.hpp" +#include "./ext/vector_uint4_sized.hpp" + diff --git a/thirdparty/glm/glm/vector_relational.hpp b/thirdparty/glm/glm/vector_relational.hpp new file mode 100644 index 0000000..a0fe17e --- /dev/null +++ b/thirdparty/glm/glm/vector_relational.hpp @@ -0,0 +1,121 @@ +/// @ref core +/// @file glm/vector_relational.hpp +/// +/// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions +/// +/// @defgroup core_func_vector_relational Vector Relational Functions +/// @ingroup core +/// +/// Relational and equality operators (<, <=, >, >=, ==, !=) are defined to +/// operate on scalars and produce scalar Boolean results. For vector results, +/// use the following built-in functions. +/// +/// In all cases, the sizes of all the input and return vectors for any particular +/// call must match. +/// +/// Include to use these core features. +/// +/// @see ext_vector_relational + +#pragma once + +#include "detail/qualifier.hpp" +#include "detail/setup.hpp" + +namespace glm +{ + /// @addtogroup core_func_vector_relational + /// @{ + + /// Returns the component-wise comparison result of x < y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL lessThan man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec lessThan(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x <= y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL lessThanEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec lessThanEqual(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x > y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL greaterThan man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec greaterThan(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x >= y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point or integer scalar type. + /// + /// @see GLSL greaterThanEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec greaterThanEqual(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x == y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point, integer or bool scalar type. + /// + /// @see GLSL equal man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec equal(vec const& x, vec const& y); + + /// Returns the component-wise comparison of result x != y. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// @tparam T A floating-point, integer or bool scalar type. + /// + /// @see GLSL notEqual man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec notEqual(vec const& x, vec const& y); + + /// Returns true if any component of x is true. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL any man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR bool any(vec const& v); + + /// Returns true if all components of x are true. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL all man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR bool all(vec const& v); + + /// Returns the component-wise logical complement of x. + /// /!\ Because of language incompatibilities between C++ and GLSL, GLM defines the function not but not_ instead. + /// + /// @tparam L An integer between 1 and 4 included that qualify the dimension of the vector. + /// + /// @see GLSL not man page + /// @see GLSL 4.20.8 specification, section 8.7 Vector Relational Functions + template + GLM_FUNC_DECL GLM_CONSTEXPR vec not_(vec const& v); + + /// @} +}//namespace glm + +#include "detail/func_vector_relational.inl" diff --git a/thirdparty/miniaudio/.gitignore b/thirdparty/miniaudio/.gitignore new file mode 100644 index 0000000..a2ae2ff --- /dev/null +++ b/thirdparty/miniaudio/.gitignore @@ -0,0 +1,14 @@ +\#issues/ +_private/ +examples/build/vc6/ +examples/build/vc15/ +examples/build/bin/ +tests/_build/bin/ +tests/_build/res/output/ +tests/_build/tcc/ +tests/_build/vc6/ +tests/_build/vc15/ +tools/_build/ +*.vcxproj.user +.vs/ +.idea/ diff --git a/thirdparty/miniaudio/.gitmodules b/thirdparty/miniaudio/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/miniaudio/CHANGES.md b/thirdparty/miniaudio/CHANGES.md new file mode 100644 index 0000000..66dd8cc --- /dev/null +++ b/thirdparty/miniaudio/CHANGES.md @@ -0,0 +1,1110 @@ +v0.11.21 - 2023-11-15 +===================== +* Add new ma_device_notification_type_unlocked notification. This is used on Web and will be fired after the user has performed a gesture and thus unlocked the ability to play audio. +* Web: Fix an error where the buffer size is incorrectly calculated. +* Core Audio: Fix a -Wshadow warning. + + +v0.11.20 - 2023-11-10 +===================== +* Fix a compilation error with iOS. +* Fix an error when dynamically linking libraries when forcing the UWP build on desktop. + + +v0.11.19 - 2023-11-04 +===================== +* Fix a bug where `ma_decoder_init_file()` can incorrectly return successfully. +* Fix a crash when using a node with more than 2 outputs. +* Fix a bug where `ma_standard_sample_rate_11025` uses the incorrect rate. +* Fix a bug in `ma_noise` where only white noise would be generated even when specifying pink or Brownian. +* Fix an SSE related bug when converting from mono streams. +* Documentation fixes. +* Remove the use of some deprecated functions. +* Improvements to runtime linking on Apple platforms. +* Web / Emscripten: Audio will no longer attempt to unlock in response to the "touchstart" event. This addresses an issue with iOS and Safari. This results in a change of behavior if you were previously depending on starting audio when the user's finger first touches the screen. Audio will now only unlock when the user's finger is lifted. See this discussion for details: https://github.com/mackron/miniaudio/issues/759 +* Web / Emscripten: Fix an error when using a sample rate of 0 in the device config. + + +v0.11.18 - 2023-08-07 +===================== +* Fix some AIFF compatibility issues. +* Fix an error where the cursor of a Vorbis stream is incorrectly incremented. +* Add support for setting a callback on an `ma_engine` object that get's fired after it processes a chunk of audio. This allows applications to do things such as apply a post-processing effect or output the audio to a file. +* Add `ma_engine_get_volume()`. +* Add `ma_sound_get_time_in_milliseconds()`. +* Decouple `MA_API` and `MA_PRIVATE`. This relaxes applications from needing to define both of them if they're only wanting to redefine one. +* Decoding backends will now have their onInitFile/W and onInitMemory initialization routines used where appropriate if they're defined. +* Increase the accuracy of the linear resampler when setting the ratio with `ma_linear_resampler_set_rate_ratio()`. +* Fix erroneous output with the linear resampler when in/out rates are the same. +* AAudio: Fix an error where the buffer size is not configured correctly which sometimes results in excessively high latency. +* ALSA: Fix a possible error when stopping and restarting a device. +* PulseAudio: Minor changes to stream flags. +* Win32: Fix an error where `CoUninialize()` is being called when the corresponding `CoInitializeEx()` fails. +* Web / Emscripten: Add support for AudioWorklets. This is opt-in and can be enabled by defining `MA_ENABLE_AUDIO_WORKLETS`. You must compile with `-sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY` for this to work. Requires at least Emscripten v3.1.32. + + +v0.11.17 - 2023-05-27 +===================== +* Fix compilation errors with MA_USE_STDINT. +* Fix a possible runtime error with Windows 95/98. +* Fix a very minor linting warning in VS2022. +* Add support for AIFF/AIFC decoding. +* Add support for RIFX decoding. +* Work around some bad code generation by Clang. +* Amalgamations of dr_wav, dr_flac, dr_mp3 and c89atomic have been updated so that they're now fully namespaced. This allows each of these libraries to be able to be used alongside miniaudio without any conflicts. In addition, some duplicate code, such as sized type declarations, result codes, etc. has been removed. + + +v0.11.16 - 2023-05-15 +===================== +* Fix a memory leak with `ma_sound_init_copy()`. +* Improve performance of `ma_sound_init_*()` when using the `ASYNC | DECODE` flag combination. + + +v0.11.15 - 2023-04-30 +===================== +* Fix a bug where initialization of a duplex device fails on some backends. +* Fix a bug in ma_gainer where smoothing isn't applied correctly thus resulting in glitching. +* Add support for volume smoothing to sounds when changing the volume with `ma_sound_set_volume()`. To use this, you must configure it via the `volumeSmoothTimeInPCMFrames` member of ma_sound_config and use `ma_sound_init_ex()` to initialize your sound. Smoothing is disabled by default. +* WASAPI: Fix a possible buffer overrun when initializing a device. +* WASAPI: Make device initialization more robust by improving the handling of the querying of the internal data format. + + +v0.11.14 - 2023-03-29 +===================== +* Fix some pedantic warnings when compiling with GCC. +* Fix some crashes with the WAV decoder when loading an invalid file. +* Fix a channel mapping error with PipeWire which results in no audio being output. +* Add support for using `ma_pcm_rb` as a data source. +* Silence some C89 compatibility warnings with Clang. +* The `pBytesRead` parameter of the VFS onRead callback is now pre-initialized to zero. + + +v0.11.13 - 2023-03-23 +===================== +* Fix compilation errors with the C++ build. +* Fix compilation errors when WIN32_LEAN_AND_MEAN is defined. + + +v0.11.12 - 2023-03-19 +===================== +* Fix a bug with data source ranges which resulted in data being read from outside the range. +* Fix a crash due to a race condition in the resource manager. +* Fix a crash with some backends when rerouting the playback side of a duplex device. +* Fix some bugs with initialization of POSIX threads. +* Fix a bug where sounds are not resampled when `MA_SOUND_NO_PITCH` is used. +* Fix a bug where changing the range of a data source would result in no audio being read. +* Fix a bug where asynchronously loaded data sources via the resources manager would reset ranges and loop points. +* Fix some Wimplicit-fallthrough warnings. +* Add support for Windows 95/98. +* Add support for configuring the stack size of resource manager job threads. +* Add support for callback notifications when a sound reaches the end. +* Optimizations to the high level API. +* Remove the old runtime linking system for pthread. The `MA_USE_RUNTIME_LINKING_FOR_PTHREAD` option is no longer used. +* WASAPI: Fix a crash when starting a device while it's in the process of rerouting. +* Windows: Remove the Windows-specific default memcpy(), malloc(), etc. + + +v0.11.11 - 2022-11-04 +===================== +* Silence an unused variable warning. +* Remove references to ccall() from the Empscripten build. +* Improve Android detection. +* WASAPI: Some minor improvements to overrun recovery for capture and duplex modes. + + +v0.11.10 - 2022-10-20 +===================== +* Add support for setting the device notification callback when initializing an engine object. +* Add support for more than 2 outputs to splitter nodes. +* Fix a crash when initializing a channel converter. +* Fix a channel mapping error where weights are calculated incorrectly. +* Fix an unaligned access error. +* Fix logging with the C++ build. +* Fix some undefined behavior errors, including some memset()'s to null pointers of 0 bytes. +* Fix logging of device info for loopback devices. +* WASAPI: Fix an error where 32-bit formats are not properly detected. +* WASAPI: Fix a bug where the device is not drained when stopped. +* WASAPI: Fix an issue with loopback mode that results in waiting indefinitely and the callback never getting fired. +* WASAPI: Add support for the Avrt API to specify the audio thread's latency sensitivity requirements. Use the `deviceConfig.wasapi.usage` configuration option. +* PulseAudio: Pass the requested sample rate, if set, to PulseAudio so that it uses the requested sample rate internally rather than always using miniaudio's resampler. +* PulseAudio: Fix a rare null pointer dereference. +* ALSA: Fix a potential crash on older versions of Linux. +* Core Audio: Fix a very unlikely memory leak. +* Core Audio: Update a deprecated symbol. +* AAudio: Fix an error where the wrong tokens are being used for usage, content types and input preset hints. +* WebAudio: Do some cleanup of the internal global JavaScript object when the last context has been uninitialized. +* Win32: Fix an error when the channel mask reported by Windows is all zero. +* Various documentation fixes. +* Bring dr_wav, dr_flac and dr_mp3 up-to-date with latest versions. + + +v0.11.9 - 2022-04-20 +==================== +* Fix some bugs where looping doesn't work with the resource manager. +* Fix a crash when seeking a sound. +* Fix a subtle bug the results in a glitch when looping a decoder when resampling is being applied. +* Fix an issue where chaining streams would not result in a seamless transition. +* Add a new flag called MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH for use with resource managed data sources. This flag is used as a hint to the resource manager that the length of the data source is unknown and calling ma_data_source_get_length_in_pcm_frames() should be avoided. +* Add support for resetting a resampler. This is useful for resetting the internal timer and clearing the internal cache for when you want to seek the input sound source back to the start. +* Add support for clearing the cache from biquads and low-pass filters. + + +v0.11.8 - 2022-02-12 +==================== +* PulseAudio: Work around bugs in PipeWire: + - PipeWire is returning AUX channels for stereo streams instead of FL/FR. This workaround forces FL/FR for stereo streams. + - PipeWire will glitch when the buffer size is too small, but still well within reasonable limits. To work around this bug, the default buffer size on PulseAudio backends is now 25ms. You can override this in the device config. This bug does not exist with regular PulseAudio, but the new default buffer size will still apply because I'm not aware of a good way to detect if PipeWire is being used. If anybody has advice on how to detect this, I'm happy to listen! +* DirectSound: Increase the minimum period size from 20ms to 30ms. +* Return `MA_SUCCESS` from `ma_device_start()` and `ma_device_stopped()` if the device is already started or stopped respectively. +* Fix an incorrect assertion in the data converter. +* Fix a compilation error with ARM builds. + + +v0.11.7 - 2022-02-06 +==================== +* Fix an error when seeking to the end of a WAV file. +* Fix a memory leak with low-pass, high-pass and band-pass filters. +* Fix some bugs in the FLAC decoder. +* Fix a -Wundef warning + + +v0.11.6 - 2022-01-22 +==================== +* WASAPI: Fix a bug where the device is not stopped when an error occurrs when writing to a playback device. +* PulseAudio: Fix a rare crash due to a division by zero. +* The node graph can now be used as a node. This allows node graphs to be connected to other node graphs. +* Fix a crash with high-pass and band-pass filters. +* Fix an audio glitch when mixing engine nodes (ma_sound and ma_sound_group). +* Add some new helper APIs for cursor and length retrieval: + - ma_data_source_get_cursor_in_seconds() + - ma_data_source_get_length_in_seconds() + - ma_sound_get_cursor_in_seconds() + - ma_sound_get_length_in_seconds() + + +v0.11.5 - 2022-01-16 +==================== +* WASAPI: Fix a bug in duplex mode when the capture and playback devices have different native sample rates. +* AAudio: Add support for automatic stream routing. +* iOS: The interruption_began notification now automatically calls `ma_device_stop()`. This allows `ma_device_start()` to work as expected when called from interruption_ended. +* iOS: Fix a bug that results in a deadlock when stopping the device in response to the interruption_begain or interruption_ended notifications. +* Fix a bug with fixed sized callbacks that results in glitches in duplex mode. +* Fix a bug that results in a deadlock when starting a device. +* ma_engine_play_sound_ex() is now publicly visible. +* Add validation to ma_sound_set_pitch() to prevent negative pitches. +* Add validation to resamplers to prevent negative ratios. + + +--------------------------------------------------------------------------------------------------- + +v0.11.4 - 2022-01-12 + - AAudio: Add initial support for automatic stream routing. + - Add support for initializing an encoder from a VFS. + - Fix a bug when initializing an encoder from a file where the file handle does not get closed in + the event of an error. + +v0.11.3 - 2022-01-07 + - Add a new flag for nodes called MA_NODE_FLAG_SILENT_OUTPUT which tells miniaudio that the + output of the node should always be treated as silence. This gives miniaudio an optimization + opportunity by skipping mixing of those nodes. Useful for special nodes that need to have + their outputs wired up to the graph so they're processed, but don't want the output to + contribute to the final mix. + - Add support for fixed sized callbacks. With this change, the data callback will be fired with + a consistent frame count based on the periodSizeInFrames or periodSizeInMilliseconds config + variable (depending on which one is used). If the period size is not specified, the backend's + internal period size will be used. Under the hood this uses an intermediary buffer which + introduces a small inefficiency. To avoid this you can use the `noFixedSizedCallback` config + variable and set it to true. This will make the callback equivalent to the way it was before + this change and will avoid the intermediary buffer, but the data callback could get fired with + an inconsistent frame count which might cause problems where certain operations need to operate + on fixed sized chunks. + - Change the logging system to always process debug log messages. This is useful for allowing + debug and test builds of applications to output debug information that can later be passed on + for debugging in miniaudio. To filter out these messages, just filter against the log level + which will be MA_LOG_LEVEL_DEBUG. + - Change the wav decoder to pick the closest format to the source file by default if no preferred + format is specified. + - Fix a bug where ma_device_get_info() and ma_device_get_name() return an error. + - Fix a bug where ma_data_source_read_pcm_frames() can return MA_AT_END even when some data has + been read. MA_AT_END should only be returned when nothing has been read. + - PulseAudio: Fix some bugs where starting and stopping a device can result in a deadlock. + +v0.11.2 - 2021-12-31 + - Add a new device notification system to replace the stop callback. The stop callback is still + in place, but will be removed in version 0.12. New code should use the notificationCallback + member in the device config instead of stopCallback. + - Fix a bug where the stopped notification doesn't get fired. + - iOS: The IO buffer size is now configured based on the device's configured period size. + - WebAudio: Optimizations to some JavaScript code. + +v0.11.1 - 2021-12-27 + - Result codes are now declared as an enum rather than #defines. + - Channel positions (MA_CHANNEL_*) are now declared as an enum rather than #defines. + - Add ma_device_get_info() for retrieving device information from an initialized device. + - Add ma_device_get_name() for retrieving the name of an initialized device. + - Add support for setting the directional attenuation factor to sounds and groups. + - Fix a crash when passing in NULL for the pEngine parameter of ma_engine_init(). + - Fix a bug where the node graph will output silence if a node has zero input connections. + - Fix a bug in the engine where sounds in front of the listener are too loud. + - AAudio: Fix an incorrect assert. + - AAudio: Fix a bug that resulted in exclusive mode always resulting in initialization failure. + - AAudio: Fix a bug that resulted in a capture device incorrectly being detected as disconnected. + - OpenSL: Fix an error when initializing a device with a non-NULL device ID. + - OpenSL: Fix some bugs with device initialization. + +v0.11.0 - 2021-12-18 + - Add a node graph system for advanced mixing and effect processing. + - Add a resource manager for loading and streaming sounds. + - Add a high level engine API for sound management and mixing. This wraps around the node graph + and resource manager. + - Add support for custom resmplers. + - Add ma_decoder_get_data_format(). + - Add support for disabling denormals on the audio thread. + - Add a delay/echo effect called ma_delay. + - Add a stereo pan effect called ma_panner. + - Add a spataializer effect called ma_spatializer. + - Add support for amplification for device master volume. + - Remove dependency on MA_MAX_CHANNELS from filters and data conversion. + - Increase MA_MAX_CHANNELS from 32 to 254. + - API CHANGE: Changes have been made to the way custom data sources are made. See documentation + on how to implement custom data sources. + - API CHANGE: Remove ma_data_source_map() and ma_data_source_unmap() + - API CHANGE: Remove the `loop` parameter from ma_data_source_read_pcm_frames(). Use + ma_data_source_set_looping() to enable or disable looping. + - API CHANGE: Remove ma_channel_mix_mode_planar_blend. Use ma_channel_mix_mode_rectangular instead. + - API CHANGE: Remove MA_MIN_SAMPLE_RATE and MA_MAX_SAMPLE_RATE. Use ma_standard_sample_rate_min + and ma_standard_sample_rate_max instead. + - API CHANGE: Changes have been made to the ma_device_info structure. See documentation for + details of these changes. + - API CHANGE: Remove the `shareMode` parameter from ma_context_get_device_info(). + - API CHANGE: Rename noPreZeroedOutputBuffer to noPreSilencedOutputBuffer in the device config. + - API CHANGE: Remove pBufferOut parameter from ring buffer commit functions. + - API CHANGE: Remove ma_zero_pcm_frames(). Use ma_silence_pcm_frames() instead. + - API CHANGE: Change ma_clip_samples_f32() to take input and output buffers rather than working + exclusively in-place. + - API CHANGE: Remove ma_clip_pcm_frames_f32(). Use ma_clip_samples_f32() or ma_clip_pcm_frames() + instead. + - API CHANGE: Remove the onLog callback from the context config and replaced with a more + flexible system. See the documentation for how to use logging. + - API CHANGE: Remove MA_LOG_LEVEL_VERBOSE and add MA_LOG_LEVEL_DEBUG. Logs using the + MA_LOG_LEVEL_DEBUG logging level will only be output when miniaudio is compiled with the + MA_DEBUG_OUTPUT option. + - API CHANGE: MA_LOG_LEVEL has been removed. All log levels will be posted, except for + MA_LOG_LEVEL_DEBUG which will only be output when MA_DEBUG_OUTPUT is enabled. + - API CHANGE: Rename ma_resource_format to ma_encoding_format. + - API CHANGE: Remove all encoding-specific initialization routines for decoders. Use the + encodingFormat properties in the decoder config instead. + - API CHANGE: Change ma_decoder_get_length_in_pcm_frames() to return a result code and output the + number of frames read via an output parameter. + - API CHANGE: Allocation callbacks must now implement the onRealloc() callback. + - API CHANGE: Remove ma_get_standard_channel_map() and add ma_channel_map_init_standard(). + - API CHANGE: Rename ma_channel_map_valid() to ma_channel_map_is_valid(). + - API CHANGE: Rename ma_channel_map_equal() to ma_channel_map_is_equal(). + - API CHANGE: Rename ma_channel_map_blank() to ma_channel_map_is_blank(). + - API CHANGE: Remove the Speex resampler. Use a custom resampler instead. + - API CHANGE: Change the following resampler APIs to return a result code and output their result + via an output parameter: + - ma_linear_resampler_get_required_input_frame_count() + - ma_linear_resampler_get_expected_output_frame_count() + - ma_resampler_get_required_input_frame_count() + - ma_resampler_get_expected_output_frame_count() + - API CHANGE: Update relevant init/uninit functions to take a pointer to allocation callbacks. + - API CHANGE: Remove ma_scale_buffer_size() + - API CHANGE: Update ma_encoder_write_pcm_frames() to return a result code and output the number + of frames written via an output parameter. + - API CHANGE: Update ma_noise_read_pcm_frames() to return a result code and output the number of + frames read via an output parameter. + - API CHANGE: Update ma_waveform_read_pcm_frames() to return a result code and output the number + of frames read via an output parameter. + - API CHANGE: Remove The MA_STATE_* and add ma_device_state_* enums. + - API CHANGE: Rename ma_factor_to_gain_db() to ma_volume_linear_to_db(). + - API CHANGE: Rename ma_gain_db_to_factor() to ma_volume_db_to_linear(). + - API CHANGE: Rename ma_device_set_master_gain_db() to ma_device_set_master_volume_db(). + - API CHANGE: Rename ma_device_get_master_gain_db() to ma_device_get_master_volume_db() + +v0.10.43 - 2021-12-10 + - ALSA: Fix use of uninitialized variables. + - ALSA: Fix enumeration of devices that support both playback and capture. + - PulseAudio: Fix a possible division by zero. + - WebAudio: Fix errors in strict mode. + +v0.10.42 - 2021-08-22 + - Fix a possible deadlock when stopping devices. + +v0.10.41 - 2021-08-15 + - Core Audio: Fix some deadlock errors. + +v0.10.40 - 2021-07-23 + - Fix a bug when converting from stereo to mono. + - PulseAudio: Fix a glitch when pausing and resuming a device. + +v0.10.39 - 2021-07-20 + - Core Audio: Fix a deadlock when the default device is changed. + - Core Audio: Fix compilation errors on macOS and iOS. + - PulseAudio: Fix a bug where the stop callback is not fired when a device is unplugged. + - PulseAudio: Fix a null pointer dereference. + +v0.10.38 - 2021-07-14 + - Fix a linking error when MA_DEBUG_OUTPUT is not enabled. + - Fix an error where ma_log_postv() does not return a value. + - OpenSL: Fix a bug with setting of stream types and recording presets. + +0.10.37 - 2021-07-06 + - Fix a bug with log message formatting. + - Fix build when compiling with MA_NO_THREADING. + - Minor updates to channel mapping. + +0.10.36 - 2021-07-03 + - Add support for custom decoding backends. + - Fix some bugs with the Vorbis decoder. + - PulseAudio: Fix a bug with channel mapping. + - PulseAudio: Fix a bug where miniaudio does not fall back to a supported format when PulseAudio + defaults to a format not known to miniaudio. + - OpenSL: Fix a crash when initializing a capture device when a recording preset other than the + default is specified. + - Silence some warnings when compiling with MA_DEBUG_OUTPUT + - Improvements to logging. See the `ma_log` API for details. The logCallback variable used by + ma_context has been deprecated and will be replaced with the new system in version 0.11. + - Initialize an `ma_log` object with `ma_log_init()`. + - Register a callback with `ma_log_register_callback()`. + - In the context config, set `pLog` to your `ma_log` object and stop using `logCallback`. + - Prep work for some upcoming changes to data sources. These changes are still compatible with + existing code, however code will need to be updated in preparation for version 0.11 which will + be breaking. You should make these changes now for any custom data sources: + - Change your base data source object from `ma_data_source_callbacks` to `ma_data_source_base`. + - Call `ma_data_source_init()` for your base object in your custom data source's initialization + routine. This takes a config object which includes a pointer to a vtable which is now where + your custom callbacks are defined. + - Call `ma_data_source_uninit()` in your custom data source's uninitialization routine. This + doesn't currently do anything, but it placeholder in case some future uninitialization code + is required to be added at a later date. + +v0.10.35 - 2021-04-27 + - Fix the C++ build. + +v0.10.34 - 2021-04-26 + - WASAPI: Fix a bug where a result code is not getting checked at initialization time. + - WASAPI: Bug fixes for loopback mode. + - ALSA: Fix a possible deadlock when stopping devices. + - Mark devices as default on the null backend. + +v0.10.33 - 2021-04-04 + - Core Audio: Fix a memory leak. + - Core Audio: Fix a bug where the performance profile is not being used by playback devices. + - JACK: Fix loading of 64-bit JACK on Windows. + - Fix a calculation error and add a safety check to the following APIs to prevent a division by zero: + - ma_calculate_buffer_size_in_milliseconds_from_frames() + - ma_calculate_buffer_size_in_frames_from_milliseconds() + - Fix compilation errors relating to c89atomic. + - Update FLAC decoder. + +v0.10.32 - 2021-02-23 + - WASAPI: Fix a deadlock in exclusive mode. + - WASAPI: No longer return an error from ma_context_get_device_info() when an exclusive mode format + cannot be retrieved. + - WASAPI: Attempt to fix some bugs with device uninitialization. + - PulseAudio: Yet another refactor, this time to remove the dependency on `pa_threaded_mainloop`. + - Web Audio: Fix a bug on Chrome and any other browser using the same engine. + - Web Audio: Automatically start the device on some user input if the device has been started. This + is to work around Google's policy of not starting audio if no user input has yet been performed. + - Fix a bug where thread handles are not being freed. + - Fix some static analysis warnings in FLAC, WAV and MP3 decoders. + - Fix a warning due to referencing _MSC_VER when it is undefined. + - Update to latest version of c89atomic. + - Internal refactoring to migrate over to the new backend callback system for the following backends: + - PulseAudio + - ALSA + - Core Audio + - AAudio + - OpenSL|ES + - OSS + - audio(4) + - sndio + +v0.10.31 - 2021-01-17 + - Make some functions const correct. + - Update ma_data_source_read_pcm_frames() to initialize pFramesRead to 0 for safety. + - Add the MA_ATOMIC annotation for use with variables that should be used atomically and remove unnecessary volatile qualifiers. + - Add support for enabling only specific backends at compile time. This is the reverse of the pre-existing system. With the new + system, all backends are first disabled with `MA_ENABLE_ONLY_SPECIFIC_BACKENDS`, which is then followed with `MA_ENABLE_*`. The + old system where you disable backends with `MA_NO_*` still exists and is still the default. + +v0.10.30 - 2021-01-10 + - Fix a crash in ma_audio_buffer_read_pcm_frames(). + - Update spinlock APIs to take a volatile parameter as input. + - Silence some unused parameter warnings. + - Fix a warning on GCC when compiling as C++. + +v0.10.29 - 2020-12-26 + - Fix some subtle multi-threading bugs on non-x86 platforms. + - Fix a bug resulting in superfluous memory allocations when enumerating devices. + - Core Audio: Fix a compilation error when compiling for iOS. + +v0.10.28 - 2020-12-16 + - Fix a crash when initializing a POSIX thread. + - OpenSL|ES: Respect the MA_NO_RUNTIME_LINKING option. + +v0.10.27 - 2020-12-04 + - Add support for dynamically configuring some properties of `ma_noise` objects post-initialization. + - Add support for configuring the channel mixing mode in the device config. + - Fix a bug with simple channel mixing mode (drop or silence excess channels). + - Fix some bugs with trying to access uninitialized variables. + - Fix some errors with stopping devices for synchronous backends where the backend's stop callback would get fired twice. + - Fix a bug in the decoder due to using an uninitialized variable. + - Fix some data race errors. + +v0.10.26 - 2020-11-24 + - WASAPI: Fix a bug where the exclusive mode format may not be retrieved correctly due to accessing freed memory. + - Fix a bug with ma_waveform where glitching occurs after changing frequency. + - Fix compilation with OpenWatcom. + - Fix compilation with TCC. + - Fix compilation with Digital Mars. + - Fix compilation warnings. + - Remove bitfields from public structures to aid in binding maintenance. + +v0.10.25 - 2020-11-15 + - PulseAudio: Fix a bug where the stop callback isn't fired. + - WebAudio: Fix an error that occurs when Emscripten increases the size of it's heap. + - Custom Backends: Change the onContextInit and onDeviceInit callbacks to take a parameter which is a pointer to the config that was + passed into ma_context_init() and ma_device_init(). This replaces the deviceType parameter of onDeviceInit. + - Fix compilation warnings on older versions of GCC. + +v0.10.24 - 2020-11-10 + - Fix a bug where initialization of a backend can fail due to some bad state being set from a prior failed attempt at initializing a + lower priority backend. + +v0.10.23 - 2020-11-09 + - AAudio: Add support for configuring a playback stream's usage. + - Fix a compilation error when all built-in asynchronous backends are disabled at compile time. + - Fix compilation errors when compiling as C++. + +v0.10.22 - 2020-11-08 + - Add support for custom backends. + - Add support for detecting default devices during device enumeration and with `ma_context_get_device_info()`. + - Refactor to the PulseAudio backend. This simplifies the implementation and fixes a capture bug. + - ALSA: Fix a bug in `ma_context_get_device_info()` where the PCM handle is left open in the event of an error. + - Core Audio: Further improvements to sample rate selection. + - Core Audio: Fix some bugs with capture mode. + - OpenSL: Add support for configuring stream types and recording presets. + - AAudio: Add support for configuring content types and input presets. + - Fix bugs in `ma_decoder_init_file*()` where the file handle is not closed after a decoding error. + - Fix some compilation warnings on GCC and Clang relating to the Speex resampler. + - Fix a compilation error for the Linux build when the ALSA and JACK backends are both disabled. + - Fix a compilation error for the BSD build. + - Fix some compilation errors on older versions of GCC. + - Add documentation for `MA_NO_RUNTIME_LINKING`. + +v0.10.21 - 2020-10-30 + - Add ma_is_backend_enabled() and ma_get_enabled_backends() for retrieving enabled backends at run-time. + - WASAPI: Fix a copy and paste bug relating to loopback mode. + - Core Audio: Fix a bug when using multiple contexts. + - Core Audio: Fix a compilation warning. + - Core Audio: Improvements to sample rate selection. + - Core Audio: Improvements to format/channels/rate selection when requesting defaults. + - Core Audio: Add notes regarding the Apple notarization process. + - Fix some bugs due to null pointer dereferences. + +v0.10.20 - 2020-10-06 + - Fix build errors with UWP. + - Minor documentation updates. + +v0.10.19 - 2020-09-22 + - WASAPI: Return an error when exclusive mode is requested, but the native format is not supported by miniaudio. + - Fix a bug where ma_decoder_seek_to_pcm_frames() never returns MA_SUCCESS even though it was successful. + - Store the sample rate in the `ma_lpf` and `ma_hpf` structures. + +v0.10.18 - 2020-08-30 + - Fix build errors with VC6. + - Fix a bug in channel converter for s32 format. + - Change channel converter configs to use the default channel map instead of a blank channel map when no channel map is specified when initializing the + config. This fixes an issue where the optimized mono expansion path would never get used. + - Use a more appropriate default format for FLAC decoders. This will now use ma_format_s16 when the FLAC is encoded as 16-bit. + - Update FLAC decoder. + - Update links to point to the new repository location (https://github.com/mackron/miniaudio). + +v0.10.17 - 2020-08-28 + - Fix an error where the WAV codec is incorrectly excluded from the build depending on which compile time options are set. + - Fix a bug in ma_audio_buffer_read_pcm_frames() where it isn't returning the correct number of frames processed. + - Fix compilation error on Android. + - Core Audio: Fix a bug with full-duplex mode. + - Add ma_decoder_get_cursor_in_pcm_frames(). + - Update WAV codec. + +v0.10.16 - 2020-08-14 + - WASAPI: Fix a potential crash due to using an uninitialized variable. + - OpenSL: Enable runtime linking. + - OpenSL: Fix a multithreading bug when initializing and uninitializing multiple contexts at the same time. + - iOS: Improvements to device enumeration. + - Fix a crash in ma_data_source_read_pcm_frames() when the output frame count parameter is NULL. + - Fix a bug in ma_data_source_read_pcm_frames() where looping doesn't work. + - Fix some compilation warnings on Windows when both DirectSound and WinMM are disabled. + - Fix some compilation warnings when no decoders are enabled. + - Add ma_audio_buffer_get_available_frames(). + - Add ma_decoder_get_available_frames(). + - Add sample rate to ma_data_source_get_data_format(). + - Change volume APIs to take 64-bit frame counts. + - Updates to documentation. + +v0.10.15 - 2020-07-15 + - Fix a bug when converting bit-masked channel maps to miniaudio channel maps. This affects the WASAPI and OpenSL backends. + +v0.10.14 - 2020-07-14 + - Fix compilation errors on Android. + - Fix compilation errors with -march=armv6. + - Updates to the documentation. + +v0.10.13 - 2020-07-11 + - Fix some potential buffer overflow errors with channel maps when channel counts are greater than MA_MAX_CHANNELS. + - Fix compilation error on Emscripten. + - Silence some unused function warnings. + - Increase the default buffer size on the Web Audio backend. This fixes glitching issues on some browsers. + - Bring FLAC decoder up-to-date with dr_flac. + - Bring MP3 decoder up-to-date with dr_mp3. + +v0.10.12 - 2020-07-04 + - Fix compilation errors on the iOS build. + +v0.10.11 - 2020-06-28 + - Fix some bugs with device tracking on Core Audio. + - Updates to documentation. + +v0.10.10 - 2020-06-26 + - Add include guard for the implementation section. + - Mark ma_device_sink_info_callback() as static. + - Fix compilation errors with MA_NO_DECODING and MA_NO_ENCODING. + - Fix compilation errors with MA_NO_DEVICE_IO + +v0.10.9 - 2020-06-24 + - Amalgamation of dr_wav, dr_flac and dr_mp3. With this change, including the header section of these libraries before the implementation of miniaudio is no + longer required. Decoding of WAV, FLAC and MP3 should be supported seamlessly without any additional libraries. Decoders can be excluded from the build + with the following options: + - MA_NO_WAV + - MA_NO_FLAC + - MA_NO_MP3 + If you get errors about multiple definitions you need to either enable the options above, move the implementation of dr_wav, dr_flac and/or dr_mp3 to before + the implementation of miniaudio, or update dr_wav, dr_flac and/or dr_mp3. + - Changes to the internal atomics library. This has been replaced with c89atomic.h which is embedded within this file. + - Fix a bug when a decoding backend reports configurations outside the limits of miniaudio's decoder abstraction. + - Fix the UWP build. + - Fix the Core Audio build. + - Fix the -std=c89 build on GCC. + +v0.10.8 - 2020-06-22 + - Remove dependency on ma_context from mutexes. + - Change ma_data_source_read_pcm_frames() to return a result code and output the frames read as an output parameter. + - Change ma_data_source_seek_pcm_frames() to return a result code and output the frames seeked as an output parameter. + - Change ma_audio_buffer_unmap() to return MA_AT_END when the end has been reached. This should be considered successful. + - Change playback.pDeviceID and capture.pDeviceID to constant pointers in ma_device_config. + - Add support for initializing decoders from a virtual file system object. This is achieved via the ma_vfs API and allows the application to customize file + IO for the loading and reading of raw audio data. Passing in NULL for the VFS will use defaults. New APIs: + - ma_decoder_init_vfs() + - ma_decoder_init_vfs_wav() + - ma_decoder_init_vfs_flac() + - ma_decoder_init_vfs_mp3() + - ma_decoder_init_vfs_vorbis() + - ma_decoder_init_vfs_w() + - ma_decoder_init_vfs_wav_w() + - ma_decoder_init_vfs_flac_w() + - ma_decoder_init_vfs_mp3_w() + - ma_decoder_init_vfs_vorbis_w() + - Add support for memory mapping to ma_data_source. + - ma_data_source_map() + - ma_data_source_unmap() + - Add ma_offset_pcm_frames_ptr() and ma_offset_pcm_frames_const_ptr() which can be used for offsetting a pointer by a specified number of PCM frames. + - Add initial implementation of ma_yield() which is useful for spin locks which will be used in some upcoming work. + - Add documentation for log levels. + - The ma_event API has been made public in preparation for some uncoming work. + - Fix a bug in ma_decoder_seek_to_pcm_frame() where the internal sample rate is not being taken into account for determining the seek location. + - Fix some bugs with the linear resampler when dynamically changing the sample rate. + - Fix compilation errors with MA_NO_DEVICE_IO. + - Fix some warnings with GCC and -std=c89. + - Fix some formatting warnings with GCC and -Wall and -Wpedantic. + - Fix some warnings with VC6. + - Minor optimization to ma_copy_pcm_frames(). This is now a no-op when the input and output buffers are the same. + +v0.10.7 - 2020-05-25 + - Fix a compilation error in the C++ build. + - Silence a warning. + +v0.10.6 - 2020-05-24 + - Change ma_clip_samples_f32() and ma_clip_pcm_frames_f32() to take a 64-bit sample/frame count. + - Change ma_zero_pcm_frames() to clear to 128 for ma_format_u8. + - Add ma_silence_pcm_frames() which replaces ma_zero_pcm_frames(). ma_zero_pcm_frames() will be removed in version 0.11. + - Add support for u8, s24 and s32 formats to ma_channel_converter. + - Add compile-time and run-time version querying. + - MA_VERSION_MINOR + - MA_VERSION_MAJOR + - MA_VERSION_REVISION + - MA_VERSION_STRING + - ma_version() + - ma_version_string() + - Add ma_audio_buffer for reading raw audio data directly from memory. + - Fix a bug in shuffle mode in ma_channel_converter. + - Fix compilation errors in certain configurations for ALSA and PulseAudio. + - The data callback now initializes the output buffer to 128 when the playback sample format is ma_format_u8. + +v0.10.5 - 2020-05-05 + - Change ma_zero_pcm_frames() to take a 64-bit frame count. + - Add ma_copy_pcm_frames(). + - Add MA_NO_GENERATION build option to exclude the `ma_waveform` and `ma_noise` APIs from the build. + - Add support for formatted logging to the VC6 build. + - Fix a crash in the linear resampler when LPF order is 0. + - Fix compilation errors and warnings with older versions of Visual Studio. + - Minor documentation updates. + +v0.10.4 - 2020-04-12 + - Fix a data conversion bug when converting from the client format to the native device format. + +v0.10.3 - 2020-04-07 + - Bring up to date with breaking changes to dr_mp3. + - Remove MA_NO_STDIO. This was causing compilation errors and the maintenance cost versus practical benefit is no longer worthwhile. + - Fix a bug with data conversion where it was unnecessarily converting to s16 or f32 and then straight back to the original format. + - Fix compilation errors and warnings with Visual Studio 2005. + - ALSA: Disable ALSA's automatic data conversion by default and add configuration options to the device config: + - alsa.noAutoFormat + - alsa.noAutoChannels + - alsa.noAutoResample + - WASAPI: Add some overrun recovery for ma_device_type_capture devices. + +v0.10.2 - 2020-03-22 + - Decorate some APIs with MA_API which were missed in the previous version. + - Fix a bug in ma_linear_resampler_set_rate() and ma_linear_resampler_set_rate_ratio(). + +v0.10.1 - 2020-03-17 + - Add MA_API decoration. This can be customized by defining it before including miniaudio.h. + - Fix a bug where opening a file would return a success code when in fact it failed. + - Fix compilation errors with Visual Studio 6 and 2003. + - Fix warnings on macOS. + +v0.10.0 - 2020-03-07 + - API CHANGE: Refactor data conversion APIs + - ma_format_converter has been removed. Use ma_convert_pcm_frames_format() instead. + - ma_channel_router has been replaced with ma_channel_converter. + - ma_src has been replaced with ma_resampler + - ma_pcm_converter has been replaced with ma_data_converter + - API CHANGE: Add support for custom memory allocation callbacks. The following APIs have been updated to take an extra parameter for the allocation + callbacks: + - ma_malloc() + - ma_realloc() + - ma_free() + - ma_aligned_malloc() + - ma_aligned_free() + - ma_rb_init() / ma_rb_init_ex() + - ma_pcm_rb_init() / ma_pcm_rb_init_ex() + - API CHANGE: Simplify latency specification in device configurations. The bufferSizeInFrames and bufferSizeInMilliseconds parameters have been replaced with + periodSizeInFrames and periodSizeInMilliseconds respectively. The previous variables defined the size of the entire buffer, whereas the new ones define the + size of a period. The following APIs have been removed since they are no longer relevant: + - ma_get_default_buffer_size_in_milliseconds() + - ma_get_default_buffer_size_in_frames() + - API CHANGE: ma_device_set_stop_callback() has been removed. If you require a stop callback, you must now set it via the device config just like the data + callback. + - API CHANGE: The ma_sine_wave API has been replaced with ma_waveform. The following APIs have been removed: + - ma_sine_wave_init() + - ma_sine_wave_read_f32() + - ma_sine_wave_read_f32_ex() + - API CHANGE: ma_convert_frames() has been updated to take an extra parameter which is the size of the output buffer in PCM frames. Parameters have also been + reordered. + - API CHANGE: ma_convert_frames_ex() has been changed to take a pointer to a ma_data_converter_config object to specify the input and output formats to + convert between. + - API CHANGE: ma_calculate_frame_count_after_src() has been renamed to ma_calculate_frame_count_after_resampling(). + - Add support for the following filters: + - Biquad (ma_biquad) + - First order low-pass (ma_lpf1) + - Second order low-pass (ma_lpf2) + - Low-pass with configurable order (ma_lpf) + - First order high-pass (ma_hpf1) + - Second order high-pass (ma_hpf2) + - High-pass with configurable order (ma_hpf) + - Second order band-pass (ma_bpf2) + - Band-pass with configurable order (ma_bpf) + - Second order peaking EQ (ma_peak2) + - Second order notching (ma_notch2) + - Second order low shelf (ma_loshelf2) + - Second order high shelf (ma_hishelf2) + - Add waveform generation API (ma_waveform) with support for the following: + - Sine + - Square + - Triangle + - Sawtooth + - Add noise generation API (ma_noise) with support for the following: + - White + - Pink + - Brownian + - Add encoding API (ma_encoder). This only supports outputting to WAV files via dr_wav. + - Add ma_result_description() which is used to retrieve a human readable description of a given result code. + - Result codes have been changed. Binding maintainers will need to update their result code constants. + - More meaningful result codes are now returned when a file fails to open. + - Internal functions have all been made static where possible. + - Fix potential crash when ma_device object's are not aligned to MA_SIMD_ALIGNMENT. + - Fix a bug in ma_decoder_get_length_in_pcm_frames() where it was returning the length based on the internal sample rate rather than the output sample rate. + - Fix bugs in some backends where the device is not drained properly in ma_device_stop(). + - Improvements to documentation. + +v0.9.10 - 2020-01-15 + - Fix compilation errors due to #if/#endif mismatches. + - WASAPI: Fix a bug where automatic stream routing is being performed for devices that are initialized with an explicit device ID. + - iOS: Fix a crash on device uninitialization. + +v0.9.9 - 2020-01-09 + - Fix compilation errors with MinGW. + - Fix compilation errors when compiling on Apple platforms. + - WASAPI: Add support for disabling hardware offloading. + - WASAPI: Add support for disabling automatic stream routing. + - Core Audio: Fix bugs in the case where the internal device uses deinterleaved buffers. + - Core Audio: Add support for controlling the session category (AVAudioSessionCategory) and options (AVAudioSessionCategoryOptions). + - JACK: Fix bug where incorrect ports are connected. + +v0.9.8 - 2019-10-07 + - WASAPI: Fix a potential deadlock when starting a full-duplex device. + - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC. + - Core Audio: Fix bugs with automatic stream routing. + - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized + to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device + config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined. + - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is + configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in + the device config. + - Add support for master volume control for devices. + - Use ma_device_set_master_volume() to set the volume to a factor between 0 and 1, where 0 is silence and 1 is full volume. + - Use ma_device_set_master_volume_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume. + - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing. + +v0.9.7 - 2019-08-28 + - Add support for loopback mode (WASAPI only). + - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config. + - If you need to capture from a specific output device, set the capture device ID to that of a playback device. + - Fix a crash when an error is posted in ma_device_init(). + - Fix a compilation error when compiling for ARM architectures. + - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode. + - Fix memory leaks in the Core Audio backend. + - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends. + +v0.9.6 - 2019-08-04 + - Add support for loading decoders using a wchar_t string for file paths. + - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning + and return MA_INVALID_OPERATION. The same applies for ma_device_stop(). + - Try fixing an issue with PulseAudio taking a long time to start playback. + - Fix a bug in ma_convert_frames() and ma_convert_frames_ex(). + - Fix memory leaks in the WASAPI backend. + - Fix a compilation error with Visual Studio 2010. + +v0.9.5 - 2019-05-21 + - Add logging to ma_dlopen() and ma_dlsym(). + - Add ma_decoder_get_length_in_pcm_frames(). + - Fix a bug with capture on the OpenSL|ES backend. + - Fix a bug with the ALSA backend where a device would not restart after being stopped. + +v0.9.4 - 2019-05-06 + - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and + Microsoft compilers back to VC6. Other compilers should also work, but have not been tested. + +v0.9.3 - 2019-04-19 + - Fix compiler errors on GCC when compiling with -std=c99. + +v0.9.2 - 2019-04-08 + - Add support for per-context user data. + - Fix a potential bug with context configs. + - Fix some bugs with PulseAudio. + +v0.9.1 - 2019-03-17 + - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when + the device is running in passthrough mode (not doing any data conversion). + - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends. + - Fix error on the UWP build. + - Fix a build error on Apple platforms. + +v0.9 - 2019-03-06 + - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma". + - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly: + - The device type, device ID and user data pointer have moved from ma_device_init() to the config. + - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init(). + - ma_device_config_init() now takes only one parameter which is the device type. All other properties need + to be set on the returned object directly. + - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback" + and "stopCallback". + - The ID of the physical device is now split into two: one for the playback device and the other for the + capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID". + - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than + being separate for each. It now takes two pointers - one containing input data and the other output data. This + design in required for full-duplex. The return value is now void instead of the number of frames written. The + new callback looks like the following: + void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); + - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change, + ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The + new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The + "onLog" member of ma_context_config has been renamed to "logCallback". + - API CHANGE: Remove ma_device_get_buffer_size_in_bytes(). + - API CHANGE: Rename decoding APIs to "pcm_frames" convention. + - mal_decoder_read() -> ma_decoder_read_pcm_frames() + - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame() + - API CHANGE: Rename sine wave reading APIs to f32 convention. + - mal_sine_wave_read() -> ma_sine_wave_read_f32() + - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex() + - API CHANGE: Remove some deprecated APIs + - mal_device_set_recv_callback() + - mal_device_set_send_callback() + - mal_src_set_input_sample_rate() + - mal_src_set_output_sample_rate() + - API CHANGE: Add log level to the log callback. New signature: + - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message) + - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're + a binding mainainer you will need to update your result code constants. + - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you + will need to update. + - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to + ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*. + - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme. + - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation + is too inefficient right now. This will hopefully be improved at a later date. + - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable. + With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not + automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's + what they want. + - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and + ma_pcm_rb operates on PCM frames. + - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously + used for web support, will be removed in a future version. + - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts + with Android 8. OpenSL|ES is used as a fallback for older versions of Android. + - Remove OpenAL and SDL backends. + - Fix a possible deadlock when rapidly stopping the device after it has started. + - Update documentation. + - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution). + +v0.8.14 - 2018-12-16 + - Core Audio: Fix a bug where the device state is not set correctly after stopping. + - Add support for custom weights to the channel router. + - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav. + +v0.8.13 - 2018-12-04 + - Core Audio: Fix a bug with channel mapping. + - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight. + +v0.8.12 - 2018-11-27 + - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2". + - Fix a linking error with ALSA. + - Fix a bug on iOS where the device name is not set correctly. + +v0.8.11 - 2018-11-21 + - iOS bug fixes. + - Minor tweaks to PulseAudio. + +v0.8.10 - 2018-10-21 + - Core Audio: Fix a hang when uninitializing a device. + - Fix a bug where an incorrect value is returned from mal_device_stop(). + +v0.8.9 - 2018-09-28 + - Fix a bug with the SDL backend where device initialization fails. + +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, miniaudio 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. + - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop. + - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable. + - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable. + +v0.8.2 - 2018-07-07 + - Fix a bug on macOS with Core Audio where the internal callback is not called. + +v0.8.1 - 2018-07-06 + - Fix compilation errors and warnings. + +v0.8 - 2018-07-05 + - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old + way is still supported for now, but you should update as it may be removed in the future. + - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with + mal_context_get_devices(). An additional low-level device enumration API has been introduced called + mal_context_enumerate_devices() which uses a callback to report devices. + - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add + mal_get_bytes_per_frame(). + - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode. + - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive. + - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa. + - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES. + - API CHANGE: Change the default channel mapping to the standard Microsoft mapping. + - API CHANGE: Remove backend-specific result codes. + - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.) + - Add support for Core Audio (Apple). + - Add support for PulseAudio. + - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly + installed by default on many of the popular distros and offer's more seamless integration on + platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which + is extremely common), it's better to just use PulseAudio directly rather than going through the + "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to). + - Add support for JACK. + - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no + longer required to build miniaudio. + - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some + distributions of MinGW. + - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some + distributions of MinGW. + - Add support for dithering to format conversion. + - Add support for configuring the priority of the worker thread. + - Add a sine wave generator. + - Improve efficiency of sample rate conversion. + - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map(). + - Introduce the notion of default device configurations. A default config uses the same configuration + as the backend's internal device, and as such results in a pass-through data transmission pipeline. + - Add support for passing in NULL for the device config in mal_device_init(), which uses a default + config. This requires manually calling mal_device_set_send/recv_callback(). + - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.) + - Make mal_device_init_ex() more robust. + - Make some APIs more const-correct. + - Fix errors with SDL detection on Apple platforms. + - Fix errors with OpenAL detection. + - Fix some memory leaks. + - Fix a bug with opening decoders from memory. + - Early work on SSE2, AVX2 and NEON optimizations. + - Miscellaneous bug fixes. + - Documentation updates. + +v0.7 - 2018-02-25 + - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts. + - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files. + - Allow opening of devices without a context. + - In this case the context is created and managed internally by the device. + - Change the default channel mapping to the same as that used by FLAC. + - Fix build errors with macOS. + +v0.6c - 2018-02-12 + - Fix build errors with BSD/OSS. + +v0.6b - 2018-02-03 + - Fix some warnings when compiling with Visual C++. + +v0.6a - 2018-01-26 + - Fix errors with channel mixing when increasing the channel count. + - Improvements to the build system for the OpenAL backend. + - Documentation fixes. + +v0.6 - 2017-12-08 + - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll + need to update. + - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively. + - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent. + - Add support for SDL and Emscripten. + - Simplify the build system further for when development packages for various backends are not installed. + With this change, when the compiler supports __has_include, backends without the relevant development + packages installed will be ignored. This fixes the build for old versions of MinGW. + - Fixes to the Android build. + - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of + audio data to a different format. + - Improvements to f32 -> u8/s16/s24/s32 conversion routines. + - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend. + - Fixes and improvements for Raspberry Pi. + - Warning fixes. + +v0.5 - 2017-11-11 + - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for + configuring the context. The works in the same kind of way as the device config. The rationale for this + change is to give applications better control over context-level properties, add support for backend- + specific configurations, and support extensibility without breaking the API. + - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for + anything anymore. + - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications + can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config + variable. + - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If + this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now + honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above) + which is by design. + - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable. + - ALSA: Fix a bug with channel mapping which causes an assertion to fail. + - Fix errors with enumeration when pInfo is set to NULL. + - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill. + +v0.4 - 2017-11-05 + - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to + mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic + messages at the context level. Previously this was only available at the device level. + - API CHANGE: The device config passed to mal_device_init() is now const. + - Added support for OSS which enables support on BSD platforms. + - Added support for WinMM (waveOut/waveIn). + - Added support for UWP (Universal Windows Platform) applications. Currently C++ only. + - Added support for exclusive mode for selected backends. Currently supported on WASAPI. + - POSIX builds no longer require explicit linking to libpthread (-lpthread). + - ALSA: Explicit linking to libasound (-lasound) is no longer required. + - ALSA: Latency improvements. + - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config. + - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the + alsa.preferPlugHW config. + - WASAPI is now the highest priority backend on Windows platforms. + - Fixed an error with sample rate conversion which was causing crackling when capturing. + - Improved error handling. + - Improved compiler support. + - Miscellaneous bug fixes. + +v0.3 - 2017-06-19 + - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for + enumerating and creating devices. Now, applications must first create a context, and then use that to + enumerate and create devices. The reason for this change is to ensure device enumeration and creation is + tied to the same backend. In addition, some backends are better suited to this design. + - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard + to test and maintain, and just generally unreliable. + - Added helper APIs for initializing mal_device_config objects. + - Null Backend: Fixed a crash when recording. + - Fixed build for UWP. + - Added support for f32 formats to the OpenSL|ES backend. + - Added initial implementation of the WASAPI backend. + - Added initial implementation of the OpenAL backend. + - Added support for low quality linear sample rate conversion. + - Added early support for basic channel mapping. + +v0.2 - 2016-10-28 + - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this + change is to ensure the logging callback has access to the user data during initialization. + - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale: + 1) The number of parameters is just getting too much. + 2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a + chance there will be support added for backend-specific properties. + - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the + added maintenance cost. + - DirectSound: Increased the default buffer size for capture devices. + - Added initial implementation of the OpenSL|ES backend. + +v0.1 - 2016-10-21 + - Initial versioned release. \ No newline at end of file diff --git a/thirdparty/miniaudio/CONTRIBUTING.md b/thirdparty/miniaudio/CONTRIBUTING.md new file mode 100644 index 0000000..1c1666c --- /dev/null +++ b/thirdparty/miniaudio/CONTRIBUTING.md @@ -0,0 +1,88 @@ +Contributing to miniaudio +========================= +First of all, thanks for stopping by! This document will explain a few things to consider when contributing to +miniaudio. + + +Found a Bug? +------------ +If you've found a bug you can create a bug report [here on GitHub](https://github.com/mackron/miniaudio/issues). +The more information you can provide, the quicker I'll be able to get it fixed. Sample programs and files help a +lot, as does a detailed list of steps I can follow to reproduce the problem. + +You can also submit a pull request which, provided your fix is correct and well written, is the quickest way to +get the bug fixed. See the next section for guidance on pull requests. + + +Pull Requests +------------- +If you want to do actual development on miniaudio, pull requests are the best place to start. Just don't do any +significant work without talking to me first. If I don't like it, it won't be merged. You can find me via email, +[Discord](https://discord.gg/9vpqbjU) and [Twitter](https://twitter.com/mackron). + +Always base your pull request branch on the "dev" branch. The master branch contains the latest release, which +means your pull request may not be including the lastest in-development changes which may result in unnecessary +conflicts. + +I need to review your pull requests before merging. If your pull request is non-trivial, try to break it up into +logical bite sized commits to make it easier for review, but make sure every commit compiles. Obviously this is +not always easy to do in practice, but just keep it in mind. + +When it comes to coding style I'm fairly relaxed, but be professional and respect the existing coding style, +regardless of whether or not you like it. It's no big deal if something slips, but try to keep it in mind. Some +things in particular: + * C89. `/*...*/` style comments and variables declared at the top of the code block are the main thing. + * Spaces instead of tabs. 4 spaces per tab. + * Don't add a third party dependency. If you do this I'll immediately reject your pull request. + +I'm not going to outline specific coding styles - just look at the existing code and use common sense. + +If you want to submit a pull request for any of the dr_* libraries in the "extras" folder, please submit the pull +request to the [dr_libs repository](https://github.com/mackron/dr_libs). + + +Respect the Goals of the Project +-------------------------------- +When making a contribution, please respect the primary goals of the project. These are the points of difference +that make miniaudio unique and it's important they're maintained and respected. + + * miniaudio is *single file*. Do not split your work into multiple files thinking I'll be impressed with your + modular design - I won't, and your contribution will be immediately rejected. + * miniaudio has *no external dependencies*. You might think your helping by adding some cool new feature via + some external library. You're not helping, and your contribution will be immediately rejected. + * miniaudio is *public domain*. Don't add any code that's taken directly from licensed code. + + + +Licensing and Credits +--------------------- +miniaudio is dual licensed as a choice of public domain or MIT-0 (No Attribution), so you need to agree to release +your contributions as such. I also do not maintain a credit/contributions list. If you don't like this you should +not contribute to this project. + + +Predictable Questions +--------------------- +### "Would you consider splitting out [some section of code] into it's own file?" +No, the idea is to keep everything in one place. It would be nice in specific cases to split out specific sections +of the code, such as the resampler, for example. However, this will completely violate one of the major goals of the +project - to have a complete audio library contained within a single file. + +### "Would you consider adding support for CMake [or my favourite build system]?" +No, the whole point of having the code contained entirely within a single file and not have any external dependencies +is to make it easy to add to your source tree without needing any extra build system integration. There is no need to +incur the cost of maintaining build systems in miniaudio. + +### "Would you consider feature XYZ? It requires C11, but don't worry, all compilers support it." +One of the philosophies of miniaudio is that it should just work, and that includes compilation environment. There's +no real reason to not support older compilers. Newer versions of C will not add anything of any significance +that cannot already be done in C89. + +### "Will you consider adding a third license option such as [my favourite license]?" +No, the idea is to keep licensing simple. That's why miniaudio is public domain - to avoid as much license friction +as possible. However, some regions do not recognize public domain, so therefore an alternative license, MIT No +Attribution, is included as an added option. There is no need to make the licensing situation any more confusing. + +### "Is there a list of contributors? Will my name be added to any kind of list?" +No, there's no credit list as it just adds extra maintenance work and it's too easy to accidentally and unfairly +forget to add a contributor. A list of contributors can be retrieved from the Git log and also GitHub itself. diff --git a/thirdparty/miniaudio/LICENSE b/thirdparty/miniaudio/LICENSE new file mode 100644 index 0000000..0a393a0 --- /dev/null +++ b/thirdparty/miniaudio/LICENSE @@ -0,0 +1,47 @@ +This software is available as a choice of the following licenses. Choose +whichever you prefer. + +=============================================================================== +ALTERNATIVE 1 - Public Domain (www.unlicense.org) +=============================================================================== +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +=============================================================================== +ALTERNATIVE 2 - MIT No Attribution +=============================================================================== +Copyright 2023 David Reid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/thirdparty/miniaudio/README.md b/thirdparty/miniaudio/README.md new file mode 100644 index 0000000..93eeca2 --- /dev/null +++ b/thirdparty/miniaudio/README.md @@ -0,0 +1,222 @@ +

    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%0Au(lzn_kfB*pk1PBlyKwwe=8h}Yn=Q`ZXi*DTg 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

    9a|Cufc;W22bbcvl16MK*T}n6`r*$DU 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

n*tMe0x_Kow7@j3pUZ)h+yDb|#?XM-0{i+F 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 z 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 z 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